Click here to Skip to main content
11,580,580 members (74,265 online)
Click here to Skip to main content

WPF TreeListView Control

, 23 Aug 2012 Apache 394.3K 12.1K 170
Rate this:
Please Sign up or sign in to vote.
This article describes the usage of custom WPF TreeListView control comparing with basic TreeView

Introduction

This articles explores the problems of standard WPF TreeView controls and describes a better way to display hierarchical data using a custom TreeListView control.

Background of TreeView

Windows Forms TreeView has quite a limited functionality and provides no easy way to extend it. WPF TreeView seems like a major step forward, at first glance. But in real application the lack of features like multiselection or multicolumn view become apparent. Moreover, things that were quite easy to do in Windows Forms are now much more complex in WPF because there's no simple way to get the container item for your business object displayed in the TreeView. For example, if you need to expand the currently selected node you have to use ItemContainerGenerator as described here or use a special model just to support selection/expanding (see here).

Another weakness of the WPF TreeView is poor performance. It takes more the 10 seconds to expand a node containing 5000 subnodes! Even navigation becomes very slow — more than a second to just move a focus.

TreeListView

But there is an alternative way to display hierarchical data — use the ListView. The basic idea is the following:

  • For each tree node we create a row in the ListView.

  • The ListViewItem template contains a special control named RowExpander which allows you to expand/collapse a node and any number of additional controls to represent the data. Those controls are shifted to the right depending on the Node level.

  • When the TreeNode is expanded/collapsed we need to add/remove rows to/from the ListView

Using this approach we get all the benefits from ListView: multiselection, a possibility to display several columns, performance improvements comparing to TreeView, less memory usage because of virtualization support (it means that visual elements will be created only for the items currently displayed on the screen).

Model-View

WPF TreeView is supposed to be used with HierarchicalDataTemplate where you specify a property containing child items. It's quite simple, but requires you to provide such a property in your business objects, which means that you can't display the hierarchy of simple types, like String, in the TreeView. Additionally, I prefer to use another approach — use a special interface which describes hierarchical data:

public interface ITreeModel
{
    /// 
    /// Get list of children of the specified parent
    /// 
    IEnumerable GetChildren(object parent);

    /// 
    /// returns weather specified parent has any children or not.
    /// 
    bool HasChildren(object parent);
}

The HasChildren method is used to display/hide the expander control without calling GetChildren, which can be time expensive. Note that items are loaded on demand in TreeListView, which means the GetChildren method will be called only when the corresponding parent node expands.

Realization Details

We create subclasses of ListView and ListviewItem:

public class TreeList: ListView
{
    /// 
    /// Internal collection of rows representing visible nodes, actually displayed
    /// in the ListView
    /// 
    internal ObservableCollectionAdv Rows
    {
        get;
        private set;
    } 

    protected override DependencyObject GetContainerForItemOverride()
    {
        return new TreeListItem();
    }

    protected override bool IsItemItsOwnContainerOverride(object item)
    {
        return item is TreeListItem;
    }
}

When the Aaren node is collapsed/expanded we need to insert/remove all child nodes and notify ListView about that. As the system class ObservableCollection doesn't provide methods for that we need to create our own collection class:

public class ObservableCollectionAdv : ObservableCollection
{
    public void RemoveRange(int index, int count)
    {
        this.CheckReentrancy();
        var items = this.Items as List;
        items.RemoveRange(index, count);
        OnReset();
    }

    public void InsertRange(int index, IEnumerable collection)
    {
        this.CheckReentrancy();
        var items = this.Items as List;
        items.InsertRange(index, collection);
        OnReset();
    }
}

For every item created by the model we create a TreeNode class which will store node status (IsSelected, IsExpanded) and track changes in the model if it provides such information:

void ChildrenChanged(object sender, NotifyCollectionChangedEventArgs e)
{
    switch (e.Action)
    {
    case NotifyCollectionChangedAction.Add:
        if (e.NewItems != null)
        {
            int index = e.NewStartingIndex;
            int rowIndex = Tree.Rows.IndexOf(this);
            foreach (object obj in e.NewItems)
            {
                Tree.InsertNewNode(this, obj, rowIndex, index);
                index++;
            }
        }
        break;

    case NotifyCollectionChangedAction.Remove:
        if (Children.Count > e.OldStartingIndex)
            RemoveChildAt(e.OldStartingIndex);
        break;

    case NotifyCollectionChangedAction.Move:
    case NotifyCollectionChangedAction.Replace:
    case NotifyCollectionChangedAction.Reset:
        while (Children.Count > 0)
            RemoveChildAt(0);
        Tree.CreateChildrenNodes(this);
        break;
    }
    HasChildren = Children.Count > 0;
    OnPropertyChanged("IsExpandable");
}

Using the Code

The source code of the article contains two examples using TreeListView. One uses a classic TreeView style and the other displays how to interact with the TreeListView. The other shows how several columns can be used to display system registry.

Points of Interest

In the current implementation of the TreeListView you have to keep the XAML markup of the TreeListItem in the client library. Which means that you have to copy it to each project using the control. Normally this information should be stored in the same library as the control itself, but it just didn't work this way. If somebody finds the way how to achieve this, don't hesitate to share it.

License

This article, along with any associated source code and files, is licensed under The Apache License, Version 2.0

Share

About the Author

Andrey Gliznetsov
Software Developer
Russian Federation Russian Federation
No Biography provided

You may also be interested in...

Comments and Discussions

 
GeneralMy vote of 5 Pin
Maxim Gorki28-May-15 12:35
memberMaxim Gorki28-May-15 12:35 
QuestionBinding to IsExpanded Property Pin
Member 803563420-Feb-15 0:03
memberMember 803563420-Feb-15 0:03 
QuestionHow to modify value of GridViewColumn Pin
Rafał Niewiński23-Jan-15 9:47
memberRafał Niewiński23-Jan-15 9:47 
GeneralMy vote of 5 Pin
ivailo b18-Dec-14 12:14
memberivailo b18-Dec-14 12:14 
GeneralMy vote of 5 Pin
amitthk27-Oct-14 20:47
memberamitthk27-Oct-14 20:47 
QuestionSorting capability -> Is there a way to add sorting capability to tree view columns? Pin
Member 797920026-Sep-14 7:42
memberMember 797920026-Sep-14 7:42 
QuestionA way to scroll CurrentItem into View? Pin
v0id2416-Sep-14 6:29
memberv0id2416-Sep-14 6:29 
QuestionIs it possible to place data in the non-leaf rows? Pin
RamonFHerrera13-Jun-14 13:08
memberRamonFHerrera13-Jun-14 13:08 
QuestionFiltering Pin
looney_ops13-Mar-14 0:33
memberlooney_ops13-Mar-14 0:33 
AnswerRe: Filtering Pin
Member 797920026-Sep-14 7:43
memberMember 797920026-Sep-14 7:43 
Questionhow to Bind to SelectedNode Name property Pin
shakti saxena10-Mar-14 10:03
membershakti saxena10-Mar-14 10:03 
QuestionBind IsSelected and IsExpanded properties to viewmodel Pin
Georgios Petrou22-Apr-13 8:38
memberGeorgios Petrou22-Apr-13 8:38 
Following other people's comments regarding binding treemodel I was able to bind a viewmodel to the control. However, I am unable to bind IsSelected and IsExpanded properties to the viewModel. It seems these properties are affecting TreeNode class. Has anyone found any workaround on how to pass these properties to the viewModel?
AnswerRe: Bind IsSelected and IsExpanded properties to viewmodel Pin
zeno.deluca2-Dec-13 2:29
memberzeno.deluca2-Dec-13 2:29 
GeneralExcellent, very useful, my vote of 5! Pin
BGW20-Mar-13 23:38
memberBGW20-Mar-13 23:38 
GeneralMy vote of 1 Pin
tbayart5-Mar-13 22:52
membertbayart5-Mar-13 22:52 
QuestionRight click and double click Pin
Brad Yinger1-Feb-13 9:46
memberBrad Yinger1-Feb-13 9:46 
QuestionHow to get the parent of a selected node. Pin
gnirujan3-Dec-12 19:50
membergnirujan3-Dec-12 19:50 
AnswerRe: How to get the parent of a selected node. Pin
Vadim Tafrov7-Dec-12 13:04
memberVadim Tafrov7-Dec-12 13:04 
BugMEMORY LEAK question Pin
Tafroman13-Sep-12 11:38
memberTafroman13-Sep-12 11:38 
Question+5 very interesting, Andrey Pin
BillWoodruff23-Aug-12 19:52
memberBillWoodruff23-Aug-12 19:52 
Questioncontacting the author Pin
Barbara Evans22-Aug-12 5:24
memberBarbara Evans22-Aug-12 5:24 
QuestionI would like to talk to you about licensing your software Pin
Barbara Evans16-Aug-12 4:31
memberBarbara Evans16-Aug-12 4:31 
GeneralAlternative project: TreeViewEx Pin
Name taken31-Jul-12 4:33
memberName taken31-Jul-12 4:33 
GeneralRe: Alternative project: TreeViewEx Pin
BillWoodruff23-Aug-12 19:59
memberBillWoodruff23-Aug-12 19:59 
GeneralRe: Alternative project: TreeViewEx Pin
Name taken23-Aug-12 20:03
memberName taken23-Aug-12 20:03 
BugFocus issues with clicking the expander Pin
Name taken23-Jul-12 22:18
memberName taken23-Jul-12 22:18 
BugBug with inconsistent expanding of nodes Pin
Name taken23-Jul-12 4:41
memberName taken23-Jul-12 4:41 
QuestionTheming questions Pin
Name taken22-Jul-12 23:46
memberName taken22-Jul-12 23:46 
QuestionModel should be DependencyProperty to enable binding. Pin
jalalx19-Jun-12 11:15
memberjalalx19-Jun-12 11:15 
AnswerRe: Model should be DependencyProperty to enable binding. Pin
Michael Caron27-Jun-13 11:29
memberMichael Caron27-Jun-13 11:29 
GeneralRe: Model should be DependencyProperty to enable binding. Pin
Forrest Coward22-Dec-13 10:04
memberForrest Coward22-Dec-13 10:04 
GeneralMy vote of 5 Pin
inf_hacker16-May-12 3:29
memberinf_hacker16-May-12 3:29 
QuestionVery nice! Pin
GrantM18-Apr-12 9:40
memberGrantM18-Apr-12 9:40 
QuestionNIce :) Pin
Saroj Kumar Sahu12-Apr-12 20:26
memberSaroj Kumar Sahu12-Apr-12 20:26 
QuestionNice :) Pin
Member 866370412-Apr-12 20:09
memberMember 866370412-Apr-12 20:09 
AnswerRe: Nice :) Pin
GrantM19-Apr-12 10:12
memberGrantM19-Apr-12 10:12 
GeneralRe: Nice :) Pin
Member 866370419-Apr-12 19:23
memberMember 866370419-Apr-12 19:23 
QuestionMemory usage problem Pin
Member 866370427-Mar-12 2:05
memberMember 866370427-Mar-12 2:05 
QuestionBug fixes & little improvements Pin
chprogmer9-Dec-11 2:54
memberchprogmer9-Dec-11 2:54 
QuestionBug Fix Pin
Naidooy19-Jul-11 7:40
memberNaidooy19-Jul-11 7:40 
GeneralBinding IsSelected Pin
frank3312-May-11 0:15
memberfrank3312-May-11 0:15 
Generalmodification to use with DataGrid Pin
kantengri19-Feb-11 6:14
memberkantengri19-Feb-11 6:14 
GeneralRe: modification to use with DataGrid Pin
bravo220-Feb-11 0:15
memberbravo220-Feb-11 0:15 
Generaledition enabled + Re: modification to use with DataGrid Pin
chprogmer12-Jan-12 6:22
memberchprogmer12-Jan-12 6:22 
GeneralRe: edition enabled + Re: modification to use with DataGrid Pin
Tafroman21-Jun-12 2:47
memberTafroman21-Jun-12 2:47 
GeneralRe: edition enabled + Re: modification to use with DataGrid Pin
Michael Caron27-Jun-13 11:43
memberMichael Caron27-Jun-13 11:43 
GeneralRe: modification to use with DataGrid Pin
Member 866370421-Mar-12 19:07
memberMember 866370421-Mar-12 19:07 
AnswerHaving troubles getting the columns to auto resize? [modified] Pin
ub3rst4r8-Dec-10 14:45
memberub3rst4r8-Dec-10 14:45 
GeneralRe: Having troubles getting the columns to auto resize? [modified] Pin
idocs27-Sep-14 11:02
memberidocs27-Sep-14 11:02 
QuestionHow Use DataTrigger in this control to make it work like a Standard ListView? I find it not work. Pin
rgqancy24-Nov-10 16:26
memberrgqancy24-Nov-10 16:26 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Terms of Use | Mobile
Web03 | 2.8.150603.1 | Last Updated 23 Aug 2012
Article Copyright 2008 by Andrey Gliznetsov
Everything else Copyright © CodeProject, 1999-2015
Layout: fixed | fluid