Data-binding the SelectedItem property of the WPF TreeView

In a new WPF application I'm writing I needed to bind the "SelectedItem" property of the TreeView control to the "SelectedItem" property of its ViewModel; but, alas, the TreeView's "SelectedItem" property is read-only...

After some googling I found the Versatile TreeView by Philip Sumi which adds the property I wanted, but since I'm learning WPF and want a deeper understanding I decided to write my own (and use Philip's TreeView as reference).

In the code I noticed a lot of dependency properties, and I figured its time to learn what are dependency properties and how do they work; again, some googling brought me to this post which I think gives a very good explanation of dependency properties.

Now that I knew what I had to do, I started writing the "EnhancedTreeView" class (which is based on the TreeView class).

At first I thought about overriding the "SelectedItem" property but it didn't go very smooth so I added the "SelectedObject" property.

I added a callback to the dependency-property-changed event (see "SelectedObjectChangedCallback") and when the property was changed I needed to change the selected item; but, alas again!, the selected item is read-only...

Some more googling brought me to this blog post which uses the "ItemsContainerGenerator" to find the matching TreeViewItem for the object I want to select.

And here's the final code:

/// <summary>
/// This treeview class allows databinding the
/// "SelectedObject" property
/// </summary>
public class EnhancedTreeView : TreeView
#region SelectedObject

/// <summary>
/// The dependency property that allows use to bind the
/// "SelectedObject" property
/// </summary>
public static readonly DependencyProperty
SelectedObjectProperty =
new PropertyMetadata(SelectedObjectChangedCallback));

/// <summary>
/// Gets or sets the select object (a writable version of
/// the "SelectedItem" property)
/// </summary>
public object SelectedObject
get { return GetValue(SelectedObjectProperty); }
set { SetValue(SelectedObjectProperty, value); }

/// <summary>
/// This method is called whenever ever the selected
/// object is changed, and if it was changed from the
/// outside, this method will set the selected item.
/// </summary>
/// <param name="obj"></param>
/// <param name="eventArgs"></param>
private static void SelectedObjectChangedCallback
(DependencyObject obj,
DependencyPropertyChangedEventArgs eventArgs)
EnhancedTreeView treeView = (EnhancedTreeView)obj;
if (!ReferenceEquals(treeView.SelectedItem,
SelectItem(treeView, eventArgs.NewValue);


/// <summary>
/// Searches the given item in the parent (recursively)
/// and selects it, returns true if the item was found
/// and selected, false otherwise.
/// </summary>
/// <param name="parent"></param>
/// <param name="itemToSelect"></param>
/// <returns></returns>
private static bool SelectItem
(ItemsControl parent, object itemToSelect)
var childTreeNode =
as TreeViewItem;

// if the item to select is directly under "parent",
// just select it
if (childTreeNode != null)
return childTreeNode.IsSelected = true;

// if the item to select is not directly under "parent",
// search the child nodes of "parent"
if (parent.Items.Count > 0)
foreach (object childItem in parent.Items)
var childItemsControl =
as ItemsControl;

if (SelectItem(childItemsControl, itemToSelect))
return true;

// if the given item wasn't found here:
return false;

/// <summary>
/// When the selected item is updated from inside the tree,
/// this method will update the "SelectedObject" property.
/// </summary>
/// <param name="e"></param>
protected override void OnSelectedItemChanged
(RoutedPropertyChangedEventArgs<object> e)
this.SelectedObject = e.NewValue;

To use the EnhancedTreeView all you need is to bind the "SelectedObject" property:
SelectedObject="{Binding SelectedObject, Mode=TwoWay}"
ItemsSource="..." />
It's important to add "Mode=TwoWay" to the binding, otherwise it won't work.


Anonymous said…
Nice work. I needed something like this to complete a "search" feature. This worked great... as soon as I remembered to go back to the Xaml and add the Mode=TwoWay. In fact, it stuck out when I read the code, but I dismissed it until, through debugging, I noticed the One Way behavior.
Anonymous said…
> It's important to add "Mode=TwoWay" to the binding, otherwise it won't work.


new FrameworkPropertyMetadata(

Popular posts from this blog

Restart the Windows File Sharing Service to fix weird problems

WPF, ImageSource and Embedded Resources

SharpDevelop dark color scheme