Displaying hierarchical data in the DataGrid



The attached project shows how to display data which is both tabular and hierarchical in nature in the Silverlight (or WPF) DataGrid. Employee list is an example of such data, where each employee is described using the same set of data column but there is also a reporting relationship between employees and their managers (also employees). The implementation uses DataGrid’s functionality to load and display individual rows and columns. It then adds some special UI and functionality to handle the TreeView-like functionality for expanding and collapsing hierarchical relationships between rows. The latter is accomplished through the following:


1.       Data is exposed to the UI through a set of presenters, which describe both the tabular and hierarchical nature of the data model (GridDef, RowDef and ColumnDef classes).


2.       The first column in the DataGrid is defined as DataGridTemplateColumn.


3.       The column template consists of three components, each databound to a property of the row definition (RowDef):


1.       A checkbox. Its IsChecked property is bound to the IsExpanded property of the row definition. Its margin property is bound to the row’s HierarchyLevel property (through a converter which converts sequential level to a Thickness structure).


2.       TextBlock or other element bound to the tabular valu of the first column (e.g. employee name).


4.       The checkbox in the above template is re-templatized so that instead of showing the usual rectangle, swish and other content, it only shows a triangle pointing down or to the side or nothing at all depending on whether the checkbox’s IsChecked property is true, false or null.


5.       The implementation of the IsExpanded property sets or resets row’s IsVisible property (a local property, not a UI property).


6.       The GridDef object (main presenter) has a Display method, which returns an IEnumerable. It iterates over the data and returns (though the yield statement) currently visible rows.


In a future blog I will suggest an implementation to handle sorting functionality.

TreeGridApp.zip

Comments (17)

  1. Hari says:

    Hi,

    I found this article very useful. Thank you. Did you find any solution for doing this with multiple roots?

  2. Anonymous says:

    Hari,

    Not sure if you’ve found a solution, but you should be able to simply replace these functions within the GridDef class.

           private void LoadData()

           {

               _columns = new List<ColumnDef>() {

                   new ColumnDef() { Title = "Col1" },

                   new ColumnDef() { Title = "Col2" }

               };

               XDocument doc = XDocument.Load("Data.xml");

               LoadData(_source, doc.Element("Rows").Elements("Row"), null);

               foreach (RowDef Row in _source)

               {

                   Row.IsVisible = true;

               }

           }

           public IEnumerable<RowDef> Display

           {

               get

               {

                   List<RowDef> lstDisplay = new List<RowDef>();

                   foreach (RowDef Row in _source)

                   {

                       lstDisplay.AddRange(IterateTree(Row));

                   }

                   return lstDisplay;

               }

           }

  3. Gopinath says:

    Hi,

         Really its very useful to me. Is there any way to Edit the cell and Update the value in xml file?

  4. kargo says:

    Hello,

             Is there any way to bind a multiple rows in the datagrid?

  5. Alvin says:

    Hi,guys

    you should try this to display multiple "roots"

    public IEnumerable<RowDef> Display

           {

               get

               {

                   //TODO: How to do this with multiple "roots"?    

                   return IterateTree();

                   //TODO:Only one "root"

                   //return IterateTree(_source[0]);                

               }

           }

           /// <summary>

           /// multiple "roots"

           /// </summary>

           /// <returns></returns>

           IEnumerable<RowDef> IterateTree()

           {

               foreach (RowDef parent in _source)

               {

                   yield return parent;

                   foreach (RowDef child in parent.Children)

                       foreach (RowDef r in IterateTree(child))

                           yield return r;

               }

           }

           IEnumerable<RowDef> IterateTree(RowDef parent)

           {

               if (!parent.IsVisible)

                   yield break;

               yield return parent;

               foreach (RowDef child in parent.Children)

                   foreach (RowDef r in IterateTree(child))

                       yield return r;

           }

  6. schwartzberg says:

    … the big problem (first noticeable when) when scrolling to a node (the node is not immediately in view), and then expanding it …and then the thing simply scrolls out of view, the list scrolls all the way to top with the first item of the list in focus.

    After expanding a node the user would probably like to see the expanded rows rather than the first row of the list.  

    This unintended behavior isn't noticeable when the list is small and all rows are anyway in view.

    Would you or someone have an idea of how to correct this SailingRock?

    Regards,

    Paul

  7. Shaumux says:

    Hi,

    This was really helpful.

    I still have a problem.

    This is loading  the file from the disk

    How can i pass an already loaded file via xelement or xdoc from another class?

    Thanks

    Shaumux

  8. dirt says:

    Alvin's multiple root addition IS PERFECT!

  9. ali says:

    I m getting system.xml.linq missing error Referece is already added but still not getting it.

  10. arun says:

    I have created the same as wpf (windows based). But when i click expand button i did get IsExpanded property change. So rowexpanding event not calling. Pls help

  11. Stefan says:

    IsChecked="{Binding IsExpanded,

                                                                 Mode=TwoWay,

                                                                 UpdateSourceTrigger=PropertyChanged}"

  12. Anup says:

    arun , Can i get the sample code for WPF Desktop base Grid. Its really helpful to me.

  13. Jimmy says:

    This is a very good example. Anyone can post link or sample code for WPF Desktop base tree grid. Its really appreciate.  

  14. Kim says:

    Is there any sample code for WPF TreeView for desktop version in MVVM Light .

  15. Michael says:

    I should learn to read the comments before I start fixing the problems on my own 🙂

    Wounderful control. Thank you, it's amazing.

  16. stelios says:

    hello,
    can you release a newer version that works with VS2015 ? the upgrade it’self did not work

    1. SailingRock says:

      Sorry, no – I have moved on to other things and don’t have the bandwidth to revisit the code. Perhaps someone else is interested.