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

  • Anonymous
    March 02, 2006
    As promised, the ATC team has posted another ListView sample. This sample is amazing because it doesn’t...
  • Anonymous
    March 02, 2006
    Dear all,

    Your gridview which you provide are impressive.

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

    Thanks

    Steven
  • Anonymous
    March 02, 2006
    Good suggestion! We will post such a custom gridview in next two weeks. Could you provide more information about your scenario?
  • Anonymous
    March 03, 2006
    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
  • Anonymous
    March 08, 2006
    The comment has been removed
  • Anonymous
    March 08, 2006
    Hello, Alex_simkin

    you will see the sample by the end of March. Thanks!
  • Anonymous
    March 08, 2006
    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!
  • Anonymous
    March 13, 2006
    Hello, Team

    Just want to clarify.

    Before March of what year? :)

    Alex
  • Anonymous
    March 13, 2006
    alex, it will be ready by the end of this March.

    Thanks!
  • Anonymous
    August 03, 2006
    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
  • Anonymous
    September 24, 2006
    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.
  • Anonymous
    February 20, 2007
    The comment has been removed
  • Anonymous
    November 14, 2007
    PingBack from http://www.mindscape.co.nz/blog/?p=61
  • Anonymous
    February 15, 2008
    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 ...                                            
  • Anonymous
    February 15, 2008
    Sorry i'v done wrong...and not down....and the code looks like                                    <l:TreeListView>                                            <l:TreeListViewItem Header="My tree">                                              </l:TreeListViewItem></l:TreeListView>
  • Anonymous
    February 25, 2008
    How can we show different images for each treelistnode? I tried a lot but no success..
  • Anonymous
    February 28, 2008
    The comment has been removed