Data binding to SelectedItem in a WPF Treeview Data binding to SelectedItem in a WPF Treeview wpf wpf

Data binding to SelectedItem in a WPF Treeview


I realise this has already had an answer accepted, but I put this together to solve the problem. It uses a similar idea to Delta's solution, but without the need to subclass the TreeView:

public class BindableSelectedItemBehavior : Behavior<TreeView>{    #region SelectedItem Property    public object SelectedItem    {        get { return (object)GetValue(SelectedItemProperty); }        set { SetValue(SelectedItemProperty, value); }    }    public static readonly DependencyProperty SelectedItemProperty =        DependencyProperty.Register("SelectedItem", typeof(object), typeof(BindableSelectedItemBehavior), new UIPropertyMetadata(null, OnSelectedItemChanged));    private static void OnSelectedItemChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)    {        var item = e.NewValue as TreeViewItem;        if (item != null)        {            item.SetValue(TreeViewItem.IsSelectedProperty, true);        }    }    #endregion    protected override void OnAttached()    {        base.OnAttached();        this.AssociatedObject.SelectedItemChanged += OnTreeViewSelectedItemChanged;    }    protected override void OnDetaching()    {        base.OnDetaching();        if (this.AssociatedObject != null)        {            this.AssociatedObject.SelectedItemChanged -= OnTreeViewSelectedItemChanged;        }    }    private void OnTreeViewSelectedItemChanged(object sender, RoutedPropertyChangedEventArgs<object> e)    {        this.SelectedItem = e.NewValue;    }}

You can then use this in your XAML as:

<TreeView>    <e:Interaction.Behaviors>        <behaviours:BindableSelectedItemBehavior SelectedItem="{Binding SelectedItem, Mode=TwoWay}" />    </e:Interaction.Behaviors></TreeView>

Hopefully it will help someone!


This property exists : TreeView.SelectedItem

But it is readonly, so you cannot assign it through a binding, only retrieve it


Answer with attached properties and no external dependencies, should the need ever arise!

You can create an attached property that is bindable and has a getter and setter:

public class TreeViewHelper{    private static Dictionary<DependencyObject, TreeViewSelectedItemBehavior> behaviors = new Dictionary<DependencyObject, TreeViewSelectedItemBehavior>();    public static object GetSelectedItem(DependencyObject obj)    {        return (object)obj.GetValue(SelectedItemProperty);    }    public static void SetSelectedItem(DependencyObject obj, object value)    {        obj.SetValue(SelectedItemProperty, value);    }    // Using a DependencyProperty as the backing store for SelectedItem.  This enables animation, styling, binding, etc...    public static readonly DependencyProperty SelectedItemProperty =        DependencyProperty.RegisterAttached("SelectedItem", typeof(object), typeof(TreeViewHelper), new UIPropertyMetadata(null, SelectedItemChanged));    private static void SelectedItemChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)    {        if (!(obj is TreeView))            return;        if (!behaviors.ContainsKey(obj))            behaviors.Add(obj, new TreeViewSelectedItemBehavior(obj as TreeView));        TreeViewSelectedItemBehavior view = behaviors[obj];        view.ChangeSelectedItem(e.NewValue);    }    private class TreeViewSelectedItemBehavior    {        TreeView view;        public TreeViewSelectedItemBehavior(TreeView view)        {            this.view = view;            view.SelectedItemChanged += (sender, e) => SetSelectedItem(view, e.NewValue);        }        internal void ChangeSelectedItem(object p)        {            TreeViewItem item = (TreeViewItem)view.ItemContainerGenerator.ContainerFromItem(p);            item.IsSelected = true;        }    }}

Add the namespace declaration containing that class to your XAML and bind as follows (local is how I named the namespace declaration):

        <TreeView ItemsSource="{Binding Path=Root.Children}" local:TreeViewHelper.SelectedItem="{Binding Path=SelectedItem, Mode=TwoWay}">    </TreeView>

Now you can bind the selected item, and also set it in your view model to change it programmatically, should that requirement ever arise. This is, of course, assuming that you implement INotifyPropertyChanged on that particular property.