Avatar of razza_b
razza_b
 asked on

xaml hierarchical data

Hi Experts

I have a treeview in SL4 that is being populated by a SP by using linq, but my hierarchical data in the xaml isnt displaying properly and just needed some help trying to get it to have the correct display. right now all data comes back but is displayed as..

  Category
> screename
> screename
> screename
  Category
> screename
> screename
> screename

but i need it as...
> Category
   screenname
   screenname
   screenname
> Category
   screenname
   screenname
   screenname

see screenshot of tree and DB data attached.

this is my xaml., i just need it tweaked to display the data explained above.
            <StackPanel x:Name="sp" Orientation="Vertical" >
                <StackPanel.Resources>
                   
                    <common:HierarchicalDataTemplate x:Key="CategoryTemplate">
                        <StackPanel>
                            <TextBlock FontStyle="Italic" Text="{Binding Category}" Margin="5,0"/>
                        </StackPanel>
                    </common:HierarchicalDataTemplate>
                   
                    <common:HierarchicalDataTemplate x:Key="ScreennameTemplate"  
                        ItemsSource="{Binding ScreenPath}"
                        ItemTemplate="{StaticResource CategoryTemplate}">
                        <TextBlock FontStyle="Italic" Text="{Binding Screenname}" Margin="5,0"/>
                    </common:HierarchicalDataTemplate>
                   
                </StackPanel.Resources>
               
                <controls:TreeView Width="190"  Height="598"  
                 ItemTemplate="{StaticResource ScreennameTemplate}" x:Name="treeView1" />
            </StackPanel>


Thanks


 SL-Treeview.docx SL-Treeview.docx
Microsoft Development.NET Programming

Avatar of undefined
Last Comment
razza_b

8/22/2022 - Mon
jagrut_patel

Try this. I have used hard-coded data.

Data related to categories and screens is organized into objects of following types:

    public class Category
    {
        public Category(string categoryName)
        {
            CategoryName = categoryName;
            Screens = new ObservableCollection<Screen>();
        }

        public string CategoryName { get; private set; }
        public ObservableCollection<Screen> Screens { get; private set; }
    }

    public class Screen
    {
        public Screen(string screenName)
        {
            ScreenName = screenName;
        }

        public string ScreenName { get; private set; }
    }

Open in new window


The hard-coded data goes as follows. For instance we can do this while page is being loaded.

            var generalUserTools = new Category("General User Tools")
            {
                Screens = 
                {
                    new Screen("Locator"),
                    new Screen("History"),
                    new Screen("Transaction Details"),
                    new Screen("Job/Lot Information")
                }
            };

            var reportFunctions = new Category("Report Functions")
            {
                Screens = 
                {
                    new Screen("WIP Reports"),
                    new Screen("Production - WIP Reports"),
                    new Screen("Quality - P-Charts")
                }
            };

            var applicationMenu = new Collection<Category>() { generalUserTools, reportFunctions};
            DataContext = new { ApplicationMenu = applicationMenu};

Open in new window


XAML looks like:

<controls:TreeView ItemsSource="{Binding ApplicationMenu}">
<!-- Template for Category -->
 <controls:TreeView.ItemTemplate>
  <common:HierarchicalDataTemplate ItemsSource="{Binding Screens}">
   <TextBlock Foreground="Red" Text="{Binding CategoryName}" />
    <!-- Template for Screen -->
    <common:HierarchicalDataTemplate.ItemTemplate>
     <DataTemplate>
      <TextBlock Text="{Binding ScreenName}" />
     </DataTemplate>
    </common:HierarchicalDataTemplate.ItemTemplate>
  </common:HierarchicalDataTemplate>
 </controls:TreeView.ItemTemplate>
</controls:TreeView>

Open in new window


Hope this helps!
razza_b

ASKER
Will this work for dynamic data that im bringing back from db?

Thanks.
jagrut_patel

I think it should.
Experts Exchange has (a) saved my job multiple times, (b) saved me hours, days, and even weeks of work, and often (c) makes me look like a superhero! This place is MAGIC!
Walt Forbes
razza_b

ASKER
Ok ill give it a try soon. Thanks
razza_b

ASKER
Hi

I trying to fit this code into what i have, this is the way i get my data..

C# Silverlight code that calls WCF...
private void btnLoadDynamicTree_Click(object sender, RoutedEventArgs e)
        {
            LoadDynamicTreeClient client = new LoadDynamicTreeClient();
            client.ListUserRolesCompleted += new EventHandler<ListUserRolesCompletedEventArgs>(client_ListUserRolesCompleted);
            client.ListUserRolesAsync();
        }

        void client_ListUserRolesCompleted(object sender, ListUserRolesCompletedEventArgs e)
        {
                    treeView1.ItemsSource = e.Result.ToList();
        }

WCF code...
public class LoadDynamicTree
    {
        [WebMethod]
        [OperationContract]
        public List<TreeMenu> ListUserRoles()
        {
            string user = "User1";
            TreeMenu tm = new TreeMenu();
            DataSet ds = new DataSet();
            var roles = new List<TreeMenu>();
            ds = tm.getDalUserAccess2(user);

            DataView dv = ds.Tables[0].DefaultView;

            if (dv != null)
                foreach (DataRowView dr in dv)
                {
                    var data = new TreeMenu
                    {
                        category = dr["Category"].ToString(),
                        screenname = dr["ScreenName"].ToString(),
                    };
                    roles.Add(data);
                }
            return roles.ToList();
        }
    }

TreeMenu class..
public class TreeMenu
    {
        private string _screenname;
        private string _category;

        public TreeMenu()
        {
        }

        public string screenname
        {
            get { return _screenname; }
            set { _screenname = value; }
        }
        public string category
        {
           get { return _category; }
          set { _category = value; }
        }
       
        //Private field for Database. Returns the default database found in the Web.config.
        Database _db = EnterpriseLibraryContainer.Current.GetInstance<Database>();

        public DataSet getDalUserAccess2(string groups)
        {
            DataSet ds;
            try
            {

                ds = _db.ExecuteDataSet("w4sp_getUserAccess_AA", new object[] { groups.Trim() });
                ds.DataSetName = "Menus";
                ds.Tables[0].TableName = "Menu";
                DataRelation relation = new DataRelation("ParentChild", ds.Tables["Menu"].Columns["ScreenID"], ds.Tables["Menu"].Columns["ParentID"], true);

                relation.Nested = true;
                ds.Relations.Add(relation);
                return ds;

            }
            catch (SqlException exSQL)
            {
                throw exSQL;
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

Thanks
razza_b

ASKER
any ideas how i can modify my code to be the way your code is set up?
Get an unlimited membership to EE for less than $4 a week.
Unlimited question asking, solutions, articles and more.
jagrut_patel

Your TreeMenu class flattens out the hierarchy. Data in List<TreeMenu> would look like:

Category                            ScreenName
----------------------------------------------
General User Tools            Locator
General User Tools            History
Report Functions               WIP Reports
Report Functions               Production - WIP Reports

So you just need to iterate this List and put data into Category and Screen view-model classes.
razza_b

ASKER
You mean if i did this...
List<TreeMenu> treeMenu = e.Result.ToList();

instead of ..
treeView1.ItemsSource = e.Result.ToList();

Then iterate that way and put into the 2 classes?
razza_b

ASKER
ok i have this now but my tree doesnt get populated at all, i dont know if its the hierarchical data or not....

            List<TreeMenu> treeMenu = e.Result.ToList();  

            foreach (WT4.LoadDynamicTree.TreeMenu tm in treeMenu)
            {
                UserRoleTreeData.Category category = new Classes.UserRoleTreeData.Category(tm.category);
                UserRoleTreeData.Screen screenname = new Classes.UserRoleTreeData.Screen(tm.screenname);
            }
This is the best money I have ever spent. I cannot not tell you how many times these folks have saved my bacon. I learn so much from the contributors.
rwheeler23
razza_b

ASKER
when im debugging in the Category and Screen classes i always see the name of the category and  screens but the count of screens is always null when entering Category class but once it gets to this line its always 0...
public ObservableCollection<Screen> Screens { get; private set; }
jagrut_patel

Certainly not.

UserRoleTreeData.Category must have collection of UserRoleTreeData.Screen in it.
See classes in my first post. Category class contains property of type ObservableCollection<Screen>.

UserRoleTreeData.Category class:
1. Category property
2. ObservableCollection<UserRoleTreeData.Screen> collection

UserRoleTreeData.Screen class:
1. Screen property

1. List<TreeMenu> treeMenu = e.Result.ToList();  
2. Create Collection<UserRoleTreeData.Category> collection, say categories
3. Add one entry for each 'distinct' category in categories collection. Set Category property appropriately.
4. Add instances of UserRoleTreeData.Screen correponding to this category into the observable collection defined in UserRoleTreeData.Category class.
5. Set DataContext = categories
6. Adjust XAML
razza_b

ASKER
ok got all this..
UserRoleTreeData.Category class:
1. Category property
2. ObservableCollection<UserRoleTreeData.Screen> collection

UserRoleTreeData.Screen class:
1. Screen property

1. List<TreeMenu> treeMenu = e.Result.ToList();  
2. Create Collection<UserRoleTreeData.Category> collection, say categories
-----------------------------------------------------------------------------------------------

Sorry my friend im a bit lost after this, not sure what im doing after this point.

List<TreeMenu> treeMenu = e.Result.ToList();
            ObservableCollection<UserRoleTreeData.Category> categories = new ObservableCollection<UserRoleTreeData.Category>();
           
            foreach (WT4.LoadDynamicTree.TreeMenu tm in treeMenu)
            {
               
            }
Get an unlimited membership to EE for less than $4 a week.
Unlimited question asking, solutions, articles and more.
razza_b

ASKER
i think im on the right path is this what your meaning...

           List<TreeMenu> treeMenu = e.Result.ToList();
           
            foreach (WT4.LoadDynamicTree.TreeMenu tm in treeMenu)
            {
                var categories = new UserRoleTreeData.Category(tm.category)
                {
                    Screens =
                    {
                        new UserRoleTreeData.Screen(tm.screenname),
                    }
                };
                var applicationMenu = new ObservableCollection<UserRoleTreeData.Category>{categories};
                DataContext = new {applicationMenu};
            }    

I can see the screen count now but when i drill down into Category class i dont see the count on this going up..

Screens = new ObservableCollection<UserRoleTreeData.Screen>();

razza_b

ASKER
tell a lie the screen count doesnt go by 1.
razza_b

ASKER
I cant see anything else what i have to do to this, is there something missing i dont know of?

this is what i have..

class UserRoleTreeData
    {
        public class Category
        {
            public Category(string categoryName)
            {
                CategoryName = categoryName;
                Screens = new ObservableCollection<UserRoleTreeData.Screen>();
            }
            public string CategoryName { get; private set; }
            public ObservableCollection<UserRoleTreeData.Screen> Screens { get; private set; }
        }

        public class Screen
        {
            public Screen(string screenName)
            {
                ScreenName = screenName;
            }
            public string ScreenName { get; private set; }
        }
    }

xaml.cs
            List<TreeMenu> treeMenu = e.Result.ToList();
           
            foreach (WT4.LoadDynamicTree.TreeMenu tm in treeMenu)
            {
                var categories = new UserRoleTreeData.Category(tm.category)
                {
                    Screens =
                    {
                        new UserRoleTreeData.Screen(tm.screenname),
                    }
                };

                var applicationMenu = new ObservableCollection<UserRoleTreeData.Category>
                {
                    categories
                };

                DataContext = new
                {
                    applicationMenu
                };
            }            


xaml..
<sdk:TreeView x:Name="treeView1" ItemsSource="{Binding applicationMenu}" Height="380" VerticalAlignment="Center" Margin="0" Background="#FFF6E9BB" HorizontalAlignment="Center" Width="200">
                    <!-- Template for Category -->
                    <sdk:TreeView.ItemTemplate>
                        <sdk:HierarchicalDataTemplate ItemsSource="{Binding Screens}">
                            <TextBlock Foreground="Black" FontWeight="Bold" Text="{Binding CategoryName}" />
                            <!-- Template for Screen -->
                            <sdk:HierarchicalDataTemplate.ItemTemplate>
                                <DataTemplate>
                                    <TextBlock Text="{Binding ScreenName}" />
                                </DataTemplate>
                            </sdk:HierarchicalDataTemplate.ItemTemplate>
                        </sdk:HierarchicalDataTemplate>
                    </sdk:TreeView.ItemTemplate>
                </sdk:TreeView>
Your help has saved me hundreds of hours of internet surfing.
fblack61
jagrut_patel

Your code is not correct. Following is the complete code you need to create DataContext for the tree-view

public class UserRoleTreeData
{
    public class Category
    {
        public Category(string categoryName)
        {
            CategoryName = categoryName;
            Screens = new ObservableCollection<Screen>();
        }

        public string CategoryName { get; private set; }
        public ObservableCollection<Screen> Screens { get; private set; }
    }

    public class Screen
    {
        public Screen(string screenName)
        {
            ScreenName = screenName;
        }

        public string ScreenName { get; private set; }
    }

    public class CategoryComparer : IEqualityComparer<Category>
    {
        public bool Equals(Category x, Category y)
        {
            return string.Equals(x.CategoryName, y.CategoryName, StringComparison.InvariantCultureIgnoreCase);
        }

        public int GetHashCode(Category obj)
        {
            return obj.CategoryName.GetHashCode();
        }
    }
}

Open in new window


xaml.cs

List<TreeMenu> treeMenu = e.Result.ToList();

List<UserRoleTreeData.Category> applicationMenu = (from c in treeMenu.AsQueryable()
            select new UserRoleTreeData.Category(c.category)).Distinct(new UserRoleTreeData.CategoryComparer()).ToList();

foreach (UserRoleTreeData.Category category in applicationMenu)
{
    var screens = (from s in treeMenu.AsQueryable()
                    where string.Equals(s.category, category.CategoryName, StringComparison.InvariantCultureIgnoreCase)
                    select new UserRoleTreeData.Screen(s.screenname)).ToList();
    foreach (UserRoleTreeData.Screen screen in screens)
        category.Screens.Add(screen);
}

DataContext = new { ApplicationMenu = applicationMenu };

Open in new window


In your XAML you wrongly mentioned,
ItemsSource="{Binding applicationMenu}"

It should be,
ItemsSource="{Binding ApplicationMenu}"

Would be happy to know if this helps!
razza_b

ASKER
Hi jagrut

The code looks great i didnt have that in mind whatsoever no wonder i was lost, i havnt used code like that, i take it thats using linq as well?

Any way i can see the all the categories with all their screens when debugging but still not displaying on tree.

razza_b

ASKER
I dont know if this may be a problem but if you look at the attached screenshot of db data where the menu header is in the category column, well when debugging i noticed that it gets this and first puts the categories as screens, then once all that is done then then it gets the screens for the categories.

so maybe the xaml is looking for the menu header as well and thats why its not binding?
Get an unlimited membership to EE for less than $4 a week.
Unlimited question asking, solutions, articles and more.
razza_b

ASKER
in fact i dont think this is a problem because its still see's it as a category and a screen.
ASKER CERTIFIED SOLUTION
jagrut_patel

Log in or sign up to see answer
Become an EE member today7-DAY FREE TRIAL
Members can start a 7-Day Free trial then enjoy unlimited access to the platform
Sign up - Free for 7 days
or
Learn why we charge membership fees
We get it - no one likes a content blocker. Take one extra minute and find out why we block content.
Not exactly the question you had in mind?
Sign up for an EE membership and get your own personalized solution. With an EE membership, you can ask unlimited troubleshooting, research, or opinion questions.
ask a question
razza_b

ASKER
jagrut thats fantastic, awsome piece of code dude it now display's, this is something ive been looking at for a while and now works.

I only have one small issue how could i remove the categories thet are screens within the menu header category?

see attachment..
dynamic-tree.docx
jagrut_patel

Thank you Razza! Glad to know that it helped.

Regarding your sub-question. You can either:
1. Skip retrieval of Menu Header Category from your database, or
2. In the LINQ query that fills "List<UserRoleTreeData.Level> applicationMenu" put a Where(e => e.category !== "Menu Header Category")

I will prefer (1)
I started with Experts Exchange in 2004 and it's been a mainstay of my professional computing life since. It helped me launch a career as a programmer / Oracle data analyst
William Peck
razza_b

ASKER
Hi jargut

Ive tried 2. but cant convert lambe to type bool because its not a delegate type.

List<UserRoleTreeData.Level> applicationMenu =
                    (from c in treeMenu.AsQueryable()
                        where (e => e.category !== "Menu Header")
                        select new UserRoleTreeData.Level(c.category)).Distinct(new UserRoleTreeData.LevelComparer()).ToList();
razza_b

ASKER
its ok i just used used..

where (c.category != "Menu Header")

and works :)
jagrut_patel

Good!
Get an unlimited membership to EE for less than $4 a week.
Unlimited question asking, solutions, articles and more.
razza_b

ASKER
thanks again!