TreeListView: Show Hierarchy Data with Details in Columns


This is the first session of this topic. I’ll introduce the minimum steps needed to build a TreeListView. The TreeListView is able to present hierarchy data with details in columns. See the following diagram:




The main Avalon elements we are going to cover in this session are TreeView, TreeViewItem, GridViewColumnCollection, GridViewRowPresenter, GridViewHeaderRowPresenter and Binding of course.


 


Step 1. Sub-class TreeView and TreeViewItem


The key in this step is to override proper methods to make sure correct container is generated.



public class TreeListView : TreeView


{


    protected override DependencyObject GetContainerForItemOverride(object item)


    {


        return new TreeListViewItem();


    }


    protected override bool IsItemItsOwnContainerOverride(object item)


    {


        return item is TreeListViewItem;


    }


    public GridViewColumnCollection Columns
    {
        get
        {
            if (_columns == null)
            {
                _columns = new GridViewColumnCollection();
            }


            return _columns;
        }
    }


    private GridViewColumnCollection _columns;


}


 


public class TreeListViewItem : TreeViewItem


{


    // copy the above two overrides here, they are same.


}


 


Step 2. Add a Level property to TreeListViewItem


Make the item be aware of the level it belongs to.



public class TreeListViewItem : TreeViewItem


{


    public int Level {


        get {


            if (_level == -1) {


                TreeListViewItem parent = ItemsControl.ItemsControlFromItemContainer(this) as TreeListViewItem;


                _level = (parent != null) ? parent.Level + 1 : 0;


            }


            return _level;


        }


}


}


 


Step 3. Write a data template for the 1st column


The keys in this step are:




  1. Bind Margin to item’s Level to mimic the indent;


  2. Write a converter to convert Level to proper indent space;


  3. Use ‘ancestor’ binding to find the TreeListViewItem;


  4. When there is no child in the item, trigger the expander’s (+ sign) visibility to Hidden but not Collapsed, so that the space is kept and all the item texts align vertically.


<DataTemplate x:Key=CellTemplate_Name>


  <DockPanel>


    <ToggleButton x:Name=Expander Style= ClickMode=Press


                  Margin={Binding Level,Converter={StaticResource LevelToIndentConverter},RelativeSource={RelativeSource AncestorType={x:Type l:TreeListViewItem}}}


                  IsChecked={Binding Path=IsExpanded,RelativeSource={RelativeSource AncestorType={x:Type l:TreeListViewItem}}}


                  />


    <TextBlock Text={Binding Name}/>


  </DockPanel>


  <DataTemplate.Triggers>


    <DataTrigger Binding={Binding Path=HasItems,RelativeSource={RelativeSource AncestorType={x:Type l:TreeListViewItem}}}


                 Value=False>


      <Setter TargetName=Expander


              Property=Visibility


              Value=Hidden/>


    </DataTrigger>


  </DataTemplate.Triggers>


</DataTemplate>


 


public class LevelToIndentConverter : IValueConverter


{


    public object Convert(object o, Type type, object parameter, CultureInfo culture)


    {


        return new Thickness((int)o * c_IndentSize, 0, 0, 0);


    }


    public object ConvertBack() {…}


   


}


 


Step 4. Write control template for TreeListViewItem


The keys in this step are:




  1. Instead of using ContentPresenter, we use GridViewRowPresenter to present the content in multiple columns


  2. Presenter finds content from item’s Header property


  3. Bind GridViewRowPresenter.Colums property to TreeListView.Columns property


<ControlTemplate TargetType={x:Type l:TreeListViewItem}>


  <StackPanel>


    <Border >


      <GridViewRowPresenter x:Name=PART_Header


                            Content={TemplateBinding Header}


                            Columns={Binding Path=Columns,RelativeSource={RelativeSource AncestorType={x:Type l:TreeListView}}}/>


    </Border>


    <ItemsPresenter x:Name=ItemsHost/>


  </StackPanel>


   


</ControlTemplate>


 


Step 5. Put it all together


The key in this step is: When setup the columns you want to show, the cell template for the 1st column is the one we just wrote that can do smart indent.



<Window >


  <Window.Resources>


    <Style x:Key=ExpandCollapseToggleStyle>


     


    <l:LevelToIndentConverter x:Key=LevelToIndentConverter/>


<DataTemplate x:Key=CellTemplate_Name>



    <Style TargetType={x:Type l:TreeListViewItem}>


    


    <Style TargetType={x:Type l:TreeListView}>


     


  </Window.Resources>


 


  <l:TreeListView>


    <l:TreeListView.Columns>


      <GridViewColumn Header=Name CellTemplate={StaticResource CellTemplate_Name}/>


      <GridViewColumn Header=IsAbstract DisplayMemberBinding={Binding IsAbstract}/>


      <GridViewColumn Header=Namespace DisplayMemberBinding={Binding Namespace}/>


    </l:TreeListView.Columns>


    <l:TreeListViewItem>


      <l:TreeListViewItem.Header>


        <x:Type TypeName=DependencyObject/>


      </l:TreeListViewItem.Header>


      <l:TreeListViewItem>


        <l:TreeListViewItem.Header>


          <x:Type TypeName=Visual/>


        </l:TreeListViewItem.Header>


      </l:TreeListViewItem>


     


    </l:TreeListViewItem>


   


  </l:TreeListView>


</Window>


We’re done!


I’ll introduce how to encapsulate the implementation details into a control in near future.

TreeListView.zip

Comments (17)

  1. As promised, the ATC team has posted another ListView sample. This sample is amazing because it doesn’t…

  2. stevenkhiem says:

    Dear all,

    Your gridview which you provide are impressive.

    Do you have a custom gridview supports edit data and show/hide columns.

    Thanks

    Steven

  3. ATC Avalon Team says:

    Good suggestion! We will post such a custom gridview in next two weeks. Could you provide more information about your scenario?

  4. alex_simkin says:

    Could you please also include in your future post the example of how to change the background of every other row in the list provided that only part of the list is displayed (i.e. vertical scrollbar is enabled on the ListView/GridView).

    Thank You

  5. stevenkhiem says:

    My scenario is gridview can host control such as textbox, comboxbox, check box …and support move between cells when I press Tab or edit data when I press F2.

    May Avalon Team provide custom gridview same as DataGridView of Window Form 2.0 ?

    Thanks

  6. ATC Avalon Team says:

    Hello, Alex_simkin

    you will see the sample by the end of March. Thanks!

  7. ATC Avalon Team says:

    Hello, Steven

    Your scenario is a typical DataGrid one, while the goal of ListView is to diplay items not to edit.

    We will give a sample of editing. However, you can’t suppose it works exactly as DataGrid. For instance, cell nevigation is not supported.

    DataGrid is not provided so far, but it is in planing.

    Thanks!

  8. alex_simkin says:

    Hello, Team

    Just want to clarify.

    Before March of what year? :)

    Alex

  9. ATC Avalon Team says:

    alex, it will be ready by the end of this March.

    Thanks!

  10. laxman_prabu says:

    Hi Team,

    Is there a possibility to bind the ‘columns’ of a grid view to a collection?

    A typical example could be a grid that could be bound dynamically to any table data (fields->columns, values->rows)?

    Thanks,

    Laxman

  11. ATC Avalon Team says:

    Sample updated.

    Add TreeListView.Columns property. So user can use multi TreeListView with different columns in the same app easily. This also avoid the problem of column sharing. Because in ListView (also TreeListView), GridViewColumn object shouldn’t be shared.

  12. Mathew says:

    Can you add sorting for the columns ?

    I tried that but the Parent-> child relation got screwed up.

    Thanks

    Mathew

  13. Melianos says:

    Hi Team !

    Great example, i was looking for a way to display hierarchical data by using a listview and i found this example, thx very much !!

    As im new to wpf, i have  a newbie question…

    i created my own tree, but the headers are not visible….can you tell me what i’ve down wrong ?

                                           <l:TreeListView>

    </l:TreeListView>

                                                   <l:TreeListViewItem Header="My tree">

                                                   </l:TreeListViewItem>

    etc …                                            

  14. Melianos says:

    Sorry i’v done wrong…and not down….

    and the code looks like

                                        <l:TreeListView>                                            <l:TreeListViewItem Header="My tree">                                              </l:TreeListViewItem>

    </l:TreeListView>

  15. jayantdotnet says:

    How can we show different images for each treelistnode? I tried a lot but no success..

  16. filipa.andrade says:

    Hi,

    I am experimenting with your TreeListView but I have some requisites that I dont know if are possible, like:

    • zebra background for the lines
    • editable cells in the columns part of the tree

    • a scrollbar

    • lines dividing the columns

    and I also need to figure out why the line separating the root element from the children doesnt appear!

    Is this possible starting with your sample?

    Thanks a lot for your help!