none
Why is all TreeView samples about self-referencing entities? (hierarchicalDataTemplate)

    Question

  • Hi, I'm just wondering if I'm missing something here.

    I'm trying to dig into the concepts about building a hierarchical treeview based on my simple datamodell; Company->Area->Location. The three tables are referencing each other by foreign keys and I'm pulling the data in the tables through entity framework.

    So, now I want to somehow build my treeview using a hierarchicalDataTemplate, but I've understood that the hierarchicalDataTemplate doesnt support multiple object types - so it seems like I run into a limitation here and instead I need to create a POCO style entity in my web project to represent the hierarchical data (This means a poco entity that hold both my company, areas (collection) and locations (collection)... ?)

    So, the process of getting my data from the DomainService (RIA) and into the POCO entity is where I'm stuck - And in that regard all samples I can find is about self-referencing entities into a POCO... Why is that - I would think that most people actually work with a normalized datamodell with keys and that self-referencing entities is actually bad practice?

    Any tips or hint about how to get the data from different entities in a DomainService (RIA - LinqToEntities) and into a POCO entity is greatly appriciated! 

     

    Wednesday, January 27, 2010 5:20 AM

Answers

  • So your problem got me thinking and I started doing a bit of research.  What I found might help you out a bit.  The TreeView will actually take a hierarchy of different elements, they just need to have the same property names.  In your case, ItemTemplate is showing a TextBox bound to Name and a ItemsSource property bound to Chlidren, then as long as your entities supports a property called Name and has a collection (of any object) call Children it will work.

    So this is where I'm at.  I created 3 sample classes:

     

    1        public class Company
    2        {
    3            public string Name { get; set; }
    4            public List<Area> Areas { get; set; }
    5            public List<Area> Children { get { return Areas; } }
    6        }
    7    
    8        public class Area
    9        {
    10           public string AreaName { get; set; }
    11           public string Name { get { return AreaName; } }
    12           public List<Location> Locs { get; set; }
    13           public List<Location> Children { get { return Locs; } }
    14       }
    15   
    16       public class Location
    17       {
    18           public string Loc { get; set; }
    19           public string Name { get { return Loc; } }
    20       }
    21   
    

     

    As you can see, I created pass-thru properties in Area and Location that matches the "Name" and "Children" bindings I spoke to earlier.  Here is my TreeView XAML:

    1            <toolkit:TreeView x:Name="myTree">
    2                <toolkit:TreeView.ItemTemplate>
    3                    <base:HierarchicalDataTemplate x:Key="OrgTemplate" ItemsSource="{Binding Children}">
    4                        <StackPanel Orientation="Horizontal">
    5                            <TextBlock Text="{Binding Name}" />
    6                        </StackPanel>
    7                    </base:HierarchicalDataTemplate>
    8                </toolkit:TreeView.ItemTemplate>
    9            </toolkit:TreeView>
    10   
    11   
    

     As you can see, if I create a List<Company> then it's Children collection has Areas and it's Children collection has Locations.  I created some dummy data here:

    1                List<Company> data = new List<Company>() {
    2                                        new Company() {
    3                                            Name = "Comp1",
    4                                            Areas = new List<Area>() {
    5                                                new Area() {
    6                                                    AreaName = "Area1",
    7                                                    Locs = new List<Location>() {
    8                                                        new Location() { Loc = "Loc1" },
    9                                                        new Location() { Loc = "Loc2" }
    10                                                   }
    11                                               }
    12                                           }
    13                                       }
    14   
    

     If you bind that List<Company> to the ItemsSource of the treeview, you get a workable tree.  So now we have a TreeView binding to a hierarchy of different data.  Next step is to get it to work with your RIA data coming over.  That's the next step of the puzzle, but at least it might be a move in the right direction.

    Tony

    Wednesday, January 27, 2010 1:58 PM
  • I needed to do a double check on the next piece before I posted it.

    If you are using WCF RIA and want to mimic the "dummy" properties that I set up in my object classes, it's fairly straight forward.

    Created a partial class in your web project to extend the desired class.  You need to name it as a shared file.

    ex:   Area.shared.cs

    In the partial class definition, add your new properties that reference the desired properties:

     

    1       public partial class Area
    2        {
    3            public string Name{ get { return AreaName; } }
    4    
    5        }
    6    
    

     That will do ya.  Your SL client will have access to the new property and can bind to it.  Basically this will let you do heirarchical binding without having to process your data in code.

    Hope this helps.  Let me know if you have any issues with it.

    Tony

    Wednesday, January 27, 2010 2:15 PM
  • I'm trying to dig into the concepts about building a hierarchical treeview based on my simple datamodell; Company->Area->Location.
     

     

    Tony's example is very cool for unknown/infinite tree depths, like in a disk directory structure.

     
    I wanted to add this...

    No template named HierarchicalDataTemplate would be useful if you couldn't actually build a hierarchical template with it :)

    A  HierarchicalDataTemplate is a DataTemplate, but it also has an ItemsSource and an ItemTemplate, which are used to template the next level down.  So...

    Since you know the depth of your tree (three), you can use three nested DataTemplates - two Hierarchical and one regular -  something like this (borrowing and simplifying Tony's example classes):

     

        public class Company
    {
    public string CompanyName { get; set; }
    public List<Area> Areas { get; set; }
    }

    public class Area
    {
    public string AreaName { get; set; }
    public List<Location> Locs { get; set; }
    }

    public class Location
    {
    public string LocName { get; set; }
    }
      

     

     

    <UserControl x:Class="SilverlightTester.HierarchicalDataTemplateTestPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:common="clr-namespace:System.Windows;assembly=System.Windows.Controls"
    xmlns:swc="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls"
    >

    <Grid x:Name="LayoutRoot" Background="White" Width="500" Height="400" HorizontalAlignment="Center" VerticalAlignment="Center" >
    <swc:TreeView x:Name="treeview" >
    <swc:TreeView.ItemTemplate>
    <common:HierarchicalDataTemplate ItemsSource="{Binding Path=Areas}" >
    <common:HierarchicalDataTemplate.ItemTemplate>
    <common:HierarchicalDataTemplate ItemsSource="{Binding Path=Locs}" >
    <common:HierarchicalDataTemplate.ItemTemplate>
    <DataTemplate>
    <TextBlock Text="{Binding Path=LocName}" Foreground="Sienna" />
    </DataTemplate>
    </common:HierarchicalDataTemplate.ItemTemplate>
    <TextBlock Text="{Binding AreaName}" Foreground="Purple" />
    </common:HierarchicalDataTemplate>
    </common:HierarchicalDataTemplate.ItemTemplate>
    <TextBlock Text="{Binding CompanyName}" Foreground="Black" FontWeight="Bold" />
    </common:HierarchicalDataTemplate>
    </swc:TreeView.ItemTemplate>
    </swc:TreeView>
    </Grid>

    </UserControl>
      

    ...and a test method to make some test data - add this to the Company class and use its results to set ItemsSource on the TreeView:

     

        public static List<Company> GetCompanies()
    {
    return new List<Company>()
    {
    new Company()
    {
    CompanyName = "ACME",
    Areas = new List<Area>()
    {
    new Area()
    {
    AreaName = "ACME Area A",
    Locs = new List<Location>()
    {
    new Location() {LocName = "Location a1"},
    new Location(){LocName = "Location a2"},
    new Location(){LocName = "Location a3"}
    }
    },
    new Area()
    {
    AreaName = "ACME Area B",
    Locs = new List<Location>()
    {
    new Location() {LocName = "Location b1"},
    new Location(){LocName = "Location b2"},
    new Location(){LocName = "Location b3"}
    }
    }
    }
    },
    new Company()
    {
    CompanyName = "Microsoft",
    Areas = new List<Area>()
    {
    new Area()
    {
    AreaName = "Microsoft Area A",
    Locs = new List<Location>()
    {
    new Location() {LocName = "a1"},
    new Location(){LocName = "a2"},
    new Location(){LocName = "a3"}
    }
    },
    new Area()
    {
    AreaName = "Microsoft Area B",
    Locs = new List<Location>()
    {
    new Location() {LocName = "b1"},
    new Location(){LocName = "b2"},
    new Location(){LocName = "b3"}
    }
    }
    }
    }
    };
    }
     :

     

     

    Wednesday, January 27, 2010 7:40 PM

All replies

  • So your problem got me thinking and I started doing a bit of research.  What I found might help you out a bit.  The TreeView will actually take a hierarchy of different elements, they just need to have the same property names.  In your case, ItemTemplate is showing a TextBox bound to Name and a ItemsSource property bound to Chlidren, then as long as your entities supports a property called Name and has a collection (of any object) call Children it will work.

    So this is where I'm at.  I created 3 sample classes:

     

    1        public class Company
    2        {
    3            public string Name { get; set; }
    4            public List<Area> Areas { get; set; }
    5            public List<Area> Children { get { return Areas; } }
    6        }
    7    
    8        public class Area
    9        {
    10           public string AreaName { get; set; }
    11           public string Name { get { return AreaName; } }
    12           public List<Location> Locs { get; set; }
    13           public List<Location> Children { get { return Locs; } }
    14       }
    15   
    16       public class Location
    17       {
    18           public string Loc { get; set; }
    19           public string Name { get { return Loc; } }
    20       }
    21   
    

     

    As you can see, I created pass-thru properties in Area and Location that matches the "Name" and "Children" bindings I spoke to earlier.  Here is my TreeView XAML:

    1            <toolkit:TreeView x:Name="myTree">
    2                <toolkit:TreeView.ItemTemplate>
    3                    <base:HierarchicalDataTemplate x:Key="OrgTemplate" ItemsSource="{Binding Children}">
    4                        <StackPanel Orientation="Horizontal">
    5                            <TextBlock Text="{Binding Name}" />
    6                        </StackPanel>
    7                    </base:HierarchicalDataTemplate>
    8                </toolkit:TreeView.ItemTemplate>
    9            </toolkit:TreeView>
    10   
    11   
    

     As you can see, if I create a List<Company> then it's Children collection has Areas and it's Children collection has Locations.  I created some dummy data here:

    1                List<Company> data = new List<Company>() {
    2                                        new Company() {
    3                                            Name = "Comp1",
    4                                            Areas = new List<Area>() {
    5                                                new Area() {
    6                                                    AreaName = "Area1",
    7                                                    Locs = new List<Location>() {
    8                                                        new Location() { Loc = "Loc1" },
    9                                                        new Location() { Loc = "Loc2" }
    10                                                   }
    11                                               }
    12                                           }
    13                                       }
    14   
    

     If you bind that List<Company> to the ItemsSource of the treeview, you get a workable tree.  So now we have a TreeView binding to a hierarchy of different data.  Next step is to get it to work with your RIA data coming over.  That's the next step of the puzzle, but at least it might be a move in the right direction.

    Tony

    Wednesday, January 27, 2010 1:58 PM
  • I needed to do a double check on the next piece before I posted it.

    If you are using WCF RIA and want to mimic the "dummy" properties that I set up in my object classes, it's fairly straight forward.

    Created a partial class in your web project to extend the desired class.  You need to name it as a shared file.

    ex:   Area.shared.cs

    In the partial class definition, add your new properties that reference the desired properties:

     

    1       public partial class Area
    2        {
    3            public string Name{ get { return AreaName; } }
    4    
    5        }
    6    
    

     That will do ya.  Your SL client will have access to the new property and can bind to it.  Basically this will let you do heirarchical binding without having to process your data in code.

    Hope this helps.  Let me know if you have any issues with it.

    Tony

    Wednesday, January 27, 2010 2:15 PM
  • I'm trying to dig into the concepts about building a hierarchical treeview based on my simple datamodell; Company->Area->Location.
     

     

    Tony's example is very cool for unknown/infinite tree depths, like in a disk directory structure.

     
    I wanted to add this...

    No template named HierarchicalDataTemplate would be useful if you couldn't actually build a hierarchical template with it :)

    A  HierarchicalDataTemplate is a DataTemplate, but it also has an ItemsSource and an ItemTemplate, which are used to template the next level down.  So...

    Since you know the depth of your tree (three), you can use three nested DataTemplates - two Hierarchical and one regular -  something like this (borrowing and simplifying Tony's example classes):

     

        public class Company
    {
    public string CompanyName { get; set; }
    public List<Area> Areas { get; set; }
    }

    public class Area
    {
    public string AreaName { get; set; }
    public List<Location> Locs { get; set; }
    }

    public class Location
    {
    public string LocName { get; set; }
    }
      

     

     

    <UserControl x:Class="SilverlightTester.HierarchicalDataTemplateTestPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:common="clr-namespace:System.Windows;assembly=System.Windows.Controls"
    xmlns:swc="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls"
    >

    <Grid x:Name="LayoutRoot" Background="White" Width="500" Height="400" HorizontalAlignment="Center" VerticalAlignment="Center" >
    <swc:TreeView x:Name="treeview" >
    <swc:TreeView.ItemTemplate>
    <common:HierarchicalDataTemplate ItemsSource="{Binding Path=Areas}" >
    <common:HierarchicalDataTemplate.ItemTemplate>
    <common:HierarchicalDataTemplate ItemsSource="{Binding Path=Locs}" >
    <common:HierarchicalDataTemplate.ItemTemplate>
    <DataTemplate>
    <TextBlock Text="{Binding Path=LocName}" Foreground="Sienna" />
    </DataTemplate>
    </common:HierarchicalDataTemplate.ItemTemplate>
    <TextBlock Text="{Binding AreaName}" Foreground="Purple" />
    </common:HierarchicalDataTemplate>
    </common:HierarchicalDataTemplate.ItemTemplate>
    <TextBlock Text="{Binding CompanyName}" Foreground="Black" FontWeight="Bold" />
    </common:HierarchicalDataTemplate>
    </swc:TreeView.ItemTemplate>
    </swc:TreeView>
    </Grid>

    </UserControl>
      

    ...and a test method to make some test data - add this to the Company class and use its results to set ItemsSource on the TreeView:

     

        public static List<Company> GetCompanies()
    {
    return new List<Company>()
    {
    new Company()
    {
    CompanyName = "ACME",
    Areas = new List<Area>()
    {
    new Area()
    {
    AreaName = "ACME Area A",
    Locs = new List<Location>()
    {
    new Location() {LocName = "Location a1"},
    new Location(){LocName = "Location a2"},
    new Location(){LocName = "Location a3"}
    }
    },
    new Area()
    {
    AreaName = "ACME Area B",
    Locs = new List<Location>()
    {
    new Location() {LocName = "Location b1"},
    new Location(){LocName = "Location b2"},
    new Location(){LocName = "Location b3"}
    }
    }
    }
    },
    new Company()
    {
    CompanyName = "Microsoft",
    Areas = new List<Area>()
    {
    new Area()
    {
    AreaName = "Microsoft Area A",
    Locs = new List<Location>()
    {
    new Location() {LocName = "a1"},
    new Location(){LocName = "a2"},
    new Location(){LocName = "a3"}
    }
    },
    new Area()
    {
    AreaName = "Microsoft Area B",
    Locs = new List<Location>()
    {
    new Location() {LocName = "b1"},
    new Location(){LocName = "b2"},
    new Location(){LocName = "b3"}
    }
    }
    }
    }
    };
    }
     :

     

     

    Wednesday, January 27, 2010 7:40 PM
  • Thank you both Tony and msalsbery, I've marked all three replies as answer as they all highlight different parts of the issue I'm trying to solve! :)

    I would again thank you for the detailed code, and explanations, I will use some time today trying to implement my tree. Please keep watching this thread, I suspect that I will run into some more issues with it.. :) 

    Thursday, January 28, 2010 3:50 AM
  • As I expected, there's some new issues for me. First of all, I would like to tell you that I succeeded in implementing my tree using ideas from the structures that you guys provided for me :)

    In my RIA Domain Service I now have the following method which loads my Company->Areas->Locations into the classes on the client and from that I build my tree:

    Public Function LoadTreeNodes() As IQueryable(Of Company)
        Return Me.ObjectContext.Companies.Include("Areas.Locations").Where(Function(f) f.companyID = 1)
    End Function

    So, to the new issue:

    For some users that log into my system I want to just show the level of Company with my locations direct underneath Company. Like excluding the Area level in the tree. The location will of course still be tied to an Area in the database through foreign key, but I want to hide it in my tree.

    So, since it's just the area class that knows about the List of Locations I'm having trouble to see how to do this. So Tony and msalsbery, or anyone else, any idea/hints to how I can achieve this type of functionallity?  :)

    Thursday, January 28, 2010 10:59 AM
  • Hi tony,

    Thanks for the code.That is very useful. I have one question here, i am using same code with multilevel treeview and i want to apply different css for parent node and different css for parent-child node and different css for parent-child-child node. Because in html we are using only one textbox so please help me how i'll implement css for the same.

    Thanks

    Friday, April 27, 2012 6:10 AM
  • Hi,

    I got implimentation for the above question :

    Add :

     public SolidColorBrush FColor

            {   get

                {        

    return  new SolidColorBrush(System.Windows.Media.Colors.Black) ;

                }

            }

    And in xaml use :

    Foreground ="{ Binding FColor}"

    Wednesday, May 16, 2012 7:00 AM