This articles explores the problems of standard WPF TreeView controls and describes a better way to display hierarchical data using a custom TreeListView control.
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
IEnumerable GetChildren(object parent);
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 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.
Good day!
I don't know what's wrong. I create DependencyProperty and bind in xaml.
When the program starts for the first time, all work fine, but when i change bc.Budget, OnModelChanged not called. Maybe you know what's going on.
MainWindow.xaml
<tree:TreeList Name="_tree" Margin="0,39,0,0" Model="{Binding Path=Budget, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
MainWindow.xaml.cs
InitializeComponent();
bc = new BudgetViewModel(con, DateTime.Now.Year);
this.DataContext = bc;
TreeList.cs
get { return (ITreeModel)GetValue(ModelProperty); }
set { SetValue(ModelProperty, value); }
public static readonly DependencyProperty ModelProperty =
DependencyProperty.Register("Model", typeof(ITreeModel), typeof(TreeList), new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, OnModelChanged));
private static void OnModelChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
if (e.NewValue != null && e.NewValue is ITreeModel)
var tree = (obj as TreeList);
tree._root.Children.Clear();
tree.Rows.Clear();
tree.CreateChildrenNodes(tree._root);
Sign In·View Thread
Trigger method when Expand/Collapse signs of a node is clicked? 
Abbas Mousavi29-Jul-18 22:03
Abbas Mousavi29-Jul-18 22:03
Any idea how to trigger a method when the expand or collapse signs are pressed? The example provides a command button to do that.
Thanks
Abbas
modified 30-Jul-18 10:39am.
Sign In·View Thread
Is there a way to Bind the IsExpanded Property of the TreeNode to my Elements?
I found the answer for the IsSelected Property, but it's not 1:1 adaptable. Because "IsExpanded" is an unknown Property... :-/
Sign In·View Thread
Hi, I have problem with updateing text in GridViewColumn.
I have declared binding in XAML:
<GridViewColumn x:Name="Value_Column" Header="Value" Width="100" DisplayMemberBinding="{Binding Value}"/>
and I have list of objects referenced by this binding.
When I changing te Value property in binded structure object, nothing happens.
How can I change GridViewColumn text value in my ITreeModel element ?
Sign In·View Thread
Sorting capability -> Is there a way to add sorting capability to tree view columns? 
Member 797920026-Sep-14 7:42
Member 797920026-Sep-14 7:42
Is there a way to scroll CurrentItem into View (BringIntoView)?
I tried to get TreeListItem via TreeNode - added property to TreeNode class (reference to TreeListItem Container), but it is always = null.
Sign In·View Thread
I have been experimenting with this code, but have been unable to place data in the rows where the folder icon appears.
Is that possible?
Sign In·View Thread
Hello, im wondering if its possible to implement some kind of filtering algorithm based on textbox input by user?
nice job..you r the man
Sign In·View Thread
very impressive article.Well Done.
I would like to know how can I bind to selectedNode Name property. I tried it many ways but it did not work.
<TextBlock Text="{Binding ElementName=_tree Path=SelectedNode.Name}" />
is this not the correct way? can you please help me out with this?
Thanks.
Sign In·View Thread
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?
Sign In·View Thread
The DataModel element is returned by "Tag" property. You can use it in binding process:
<tree:TreeList.ItemContainerStyle>
<Style TargetType="{x:Type tree:TreeListItem}">
<Setter Property="IsSelected" Value="{Binding Tag.IsSelected, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
</Style>
</tree:TreeList.ItemContainerStyle>
Sign In·View Thread
Binding ItemsSource does nothing
Model can't be binded, is't not a dependency property
Doesn't follow WPF rules and break the code
Sign In·View Thread
Your control is really great. Iam using it in my application where i wanna display packages, classes and methods in a tree view along with coloumns for each, where i can display some metrics value for each item. I want to add a a mouse down event to the textblock and on dbclicking the textblock i should open the method or the file. My question is to hilight the methos i need to open the file where the method resides. How can i get the parent elements textblock textcontent?? Please help me out.
Sign In·View Thread
private void TreeViewSelectionChanged(object sender, SelectionChangedEventArgs e)
var tree = sender as TreeList;
var node = tree.SelectedNode;
var pnode = GetAnc(node, 2);
public TreeNode GetAnc(TreeNode node, int level)
for (var i = 0; i < level; i++)
if (node.Parent != null) node = node.Parent;
return node;
return null;
Sign In·View Thread
It seems that a memory leak happens somehow.
I used this nice control with large amount of rich data. So, as it happens really often - I have played with row expander by clicking it many times =) From click to click I realized the difference between two click delay whick have been becoming worse and worse.It just can be seen by watching process explorer that something wrong. I wanted to investigate this by using EQUTEK profiler and applied that to the SAMPLE application and my code. But nothing has resulted from my efforts.
P.S. This WPF control is *purely* nice =)
P.P.S. http://ib3.keep4u.ru/b/2012/09/14/50/503c7e04d3b497dd78dc6220fb690fd5.png[^]
Sign In·View Thread
I am curious if you intend to any form of run-time drag-drop within this tree, or between two instances of this tree.
thanks, Bill Woodruff
"One of the few good things about modern times: If you die horribly on television, you will not have died in vain. You will have entertained us." Kurt Vonnegut
Sign In·View Thread
Web02
2.8:2023-03-27:1