Displaying hierarchical data in WPF and ASP.Net MVC

By Mirek on (tags: ASP.NET MVC, hierarchical data, Self referencing tables, WPF, categories: code)

Let assume we have following self referencing table of people

PersonTable

Each row represents the person and contains self unique identifier, name, last name and the Id of the parent or null if person has no parent.

Now we would like to display the list of persons as a hierarchical tree showing the relationships between each person.

Let create the Person model that will represent one person of our table.

 

   1: public class Person 
   2: { 
   3:     public int Id { get; set; } 
   4:   
   5:     public string Name { get; set; } 
   6:   
   7:     public string LastName { get; set; } 
   8:      
   9:     public Person Parent { get; set; } 
  10: }

 

We use Entity Framework Code First which automatically creates for us a key for a ParentParent_Id” and manages loading appropriate Parent object for each Person taken from the database.

In WPF we can show a hierarchical data using the TreeView control with associated HierarchicalDataTemplate. HierarchicalDataTemplate represents one node in the graphical hierarchy tree and as a ItemSource it wants the collection of subnode elements of the same type as the main TreeView.

So I we pass to ItemSource of the TreeView the collection of Persons

 

   1: ObservableCollection<Person> personList 

 

then the ItemSource of HierarchicalDataTemplate also has to point to the list of Person objects which will be treated as the child nodes of current node.

Lets create database context.

 

   1: public class PersonDbContext : DbContext 
   2: { 
   3:     [...] 
   4:     public PersonDbContext() 
   5:     { 
   6:         Database.DefaultConnectionFactory = new SqlCeConnectionFactory("System.Data.SqlServerCe.4.0"); 
   7:         Database.SetInitializer(new DropCreateDatabaseIfModelChanges<PersonDbContext>()); 
   8:     } 
   9:      
  10:     public DbSet<Person> People { get; set; } 
  11: }

 

In EF Code First this class is used as an entry point for all database operations. Using People property we can receive the collection of Person objects stored in our table Person. Now we have everything needed to store and load our People objects from database.

Now if we would like to bind a list of person to a TreeView we must face another problem. As you probably noticed our Person model does not contain a reference to its, let it be called, children. Thus as long as such information is needed we can not properly bind the collection of Person to TreeView hierarchy.

We can handle it in few ways. First approach has been proposed by Stefan Cruysberghs and can be found  here. It is based on Linq extension named AsHierarchy which  parses a collection of entities and creates a hierarchy of following nodes:

 

   1: public class HierarchyNode<T> where T : class 
   2: { 
   3:     public T Entity { get; set; } 
   4:     public IEnumerable<HierarchyNode<T>> ChildNodes { get; set; } 
   5:     public int Depth { get; set; } 
   6: } 

 

where Entity is regular object, in our case Person, ChildNodes contains a collection of all entities which as a parent has Entity object and Depth indicates the depth of node in hierarchy.

To construct a people hierarchy from collection of Person object we type following:

 

   1: var dbctx = new PersonDbContext(); 
   2: var hierarchy = dbctx.People.AsHierarchy(p => p.Id, p => p.Parent == null ? 0 : p.Parent.Id); 

 

where as a parameters for AsHierarchy we must provide two functions, first should return an Person identifier and second should return particular Person’s parent identifier. I have added a small modification for second parameter to prevent cases when Person does not have a parent.

Such constructed hierarchy we can now easily bind to the TreeView.

 

   1: <TreeView ItemsSource="{Binding hierarchy}"> 
   2:     <TreeView.ItemTemplate> 
   3:         <HierarchicalDataTemplate 
   4:            ItemsSource="{Binding ChildNodes}"> 
   5:             <StackPanel Orientation="Horizontal"> 
   6:                 <TextBlock Text="{Binding Path=Entity.Name}"></TextBlock> 
   7:                 <TextBlock Text=" "></TextBlock> 
   8:                 <TextBlock Text="{Binding Path=Entity.LastName}"></TextBlock> 
   9:             </StackPanel> 
  10:         </HierarchicalDataTemplate> 
  11:     </TreeView.ItemTemplate> 
  12: </TreeView>

 

As you can see the TreeView is binded to the main collection hierarchy generated by AsHierarchy method and each of  HierarchicalDataTemplate binds to the ChildNodes of HierarchyNode<Person> object.

Final hierarchy looks like following.

hierarchy1

On Stefan Cruysberghs page we can see how to customize hierarchy view to fit our needs.

Another approach for showing a hierarchical data taken from recursive self referencing table is to modify a model in the way it provides as a ready hierarchical data with roots and its children loaded in proper way.

Lets add a new property Children to out Person model.

 

   1: public class Person 
   2: { 
   3:     public int Id { get; set; } 
   4:   
   5:     public string Name { get; set; } 
   6:   
   7:     public string LastName { get; set; } 
   8:      
   9:     public Person Parent { get; set; } 
  10:   
  11:     public ICollection<Person> Children { get; set; } 
  12: } 

 

Now the magic of the Entity Framework Code First is that it by default automatically detects the collection Children as a end of bidirectional relation to Parent. Moreover it handles loading this collection and each Person object loaded from database will has this collection filled with appropriate objects. No changes to the database schema is made.

Now what we need is just to get the collection of first level root Persons. Then bind it to the tree and bind the Children to the HierarchicalDataTemplate.

 

   1: var dbctx = new PersonDbContext(); 
   2: var hierarchy = dbctx.People.Where(p => p.Parent == null); 

 

Now hierarchy contains a very high parents of the tree.

 

   1: <TreeView ItemsSource="{Binding hierarchy}"> 
   2:     <TreeView.ItemTemplate> 
   3:         <HierarchicalDataTemplate 
   4:            ItemsSource="{Binding Children}"> 
   5:             <StackPanel Orientation="Horizontal"> 
   6:               <TextBlock Text="{Binding Name}"/> 
   7:             <TextBlock Text=" "/> 
   8:             <TextBlock Text="{Binding LastName}"/> 
   9:             </StackPanel> 
  10:         </HierarchicalDataTemplate> 
  11:     </TreeView.ItemTemplate> 
  12: </TreeView> 

 

and the result looks like this.

hierarchy1

As we can see this approach gives the same result as the AsHierarchy extension and in my opinion is more simple and readable.

To represent a hierarchical data in ASP.NET MVC we will use most popular jQuery TreeView plugin. More information and the latest version of jQuery TreeView you can find here.
TreeView plugin usage is very simple. All we need is to put our hierarchy as an html list with unique id and apply a special CSS classes to its element.

 

   1: <ul id="hierarchy" class="filetree"> 
   2: <li><span class="folder">Folder 1</span> 
   3:   <ul> 
   4:     <li><span class="file">Item 1.1</span></li> 
   5:   </ul> 
   6: </li> 
   7: <li><span class="folder">Folder 2</span> 
   8:   <ul> 
   9:     <li><span class="folder">Subfolder 2.1</span> 
  10:       <ul id="folder21"> 
  11:         <li><span class="file">File 2.1.1</span></li> 
  12:         <li><span class="file">File 2.1.2</span></li> 
  13:       </ul> 
  14:     </li> 
  15:     <li><span class="file">File 2.2</span></li> 
  16:   </ul> 
  17: </li> 
  18: <li><span class="file">File 4</span></li> 
  19: </ul> 

 

Then on document loading we must perform an action on this list

 

   1: <script type="text/javascript"> 
   2:     $(document).ready(function () { 
   3:         $("#hierarchy").treeview(); 
   4:     }); 
   5: </script> 

 

and we get following hierarchy.

hierarchy_mvc

Drawing the list of elements can be a little bit problematic. But as always, also for that someone has found a solution. Matt Hidinger on his blog has published a solution ASP.NET MVC Recursive TreeView Helper which extend the HtmlHelper with TreeView method. Lets look at its definition.

 

   1: public static string TreeView<T>(this HtmlHelper html, string treeId, IEnumerable<T> rootItems, 
   2:    Func<T, IEnumerable<T>> childrenProperty, Func<T, string> itemContent

 

It takes a collection of hierarchy roots, reference to its children and a function that returns display data for each element.

Lets take a look how does it work in ASP.NET MVC 3.

Controller

 

   1: public class HierarchyController : Controller 
   2: { 
   3:     //Create database context 
   4:     PersonDbContext dbctx = new PersonDbContext(); 
   5:   
   6:     public ActionResult Index() 
   7:     { 
   8:         //retreive all root Person objects from db            
   9:         var rootPeople = from p in dbctx.People.ToList() 
  10:                          where p.Parent == null 
  11:                          select p;             
  12:         return View(rootPeople); 
  13:     }                 
  14: }

 

You can see one action which loads all Person objects from database (ToList() method) and then selects only those who does not has Parent which means the roots of the hierarchy.

Somewhere it the view we place following code

 

   1: @Html.Raw(Html.TreeView("hierarchy", Model, p => p.Children, p => p.Name)) 

 

and we get following result

hier3

with cool animating expanding.
Now we can customize the Html extension and use some cool features of jQuery TreeView. Also we can mix it with Stefan Cruysberghs Linq extension presented previously and create a Html helper extension method that allows a plain list of objects and automatically resolves the roots and children.