Data binding to SelectedItem in a WPF Treeview
How can I retrieve the item that is selected in a WPF-treeview? I want to do this in XAML, because I want to bind it.
You might think that it is SelectedItem
but apparently that does not exist is readonly and therefore unusable.
This is what I want to do:
<TreeView ItemsSource="{Binding Path=Model.Clusters}"
ItemTemplate="{StaticResource ClusterTemplate}"
SelectedItem="{Binding Path=Model.SelectedCluster}" />
I want to bind the SelectedItem
to a property on my Model.
But this gives me the error:
'SelectedItem' property is read-only and cannot be set from markup.
Edit: Ok, this is the way that I solved this:
<TreeView
ItemsSource="{Binding Path=Model.Clusters}"
ItemTemplate="{StaticResource HoofdCLusterTemplate}"
SelectedItemChanged="TreeView_OnSelectedItemChanged" />
and in the codebehindfile of my xaml:
private void TreeView_OnSelectedItemChanged(object sender, RoutedPropertyChangedEventArgs<object> e)
{
Model.SelectedCluster = (Cluster)e.NewValue;
}
Solution 1:
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!
Solution 2:
This property exists : TreeView.SelectedItem
But it is readonly, so you cannot assign it through a binding, only retrieve it
Solution 3:
Well, I found a solution. It moves the mess, so that MVVM works.
First add this class:
public class ExtendedTreeView : TreeView
{
public ExtendedTreeView()
: base()
{
this.SelectedItemChanged += new RoutedPropertyChangedEventHandler<object>(___ICH);
}
void ___ICH(object sender, RoutedPropertyChangedEventArgs<object> e)
{
if (SelectedItem != null)
{
SetValue(SelectedItem_Property, SelectedItem);
}
}
public object SelectedItem_
{
get { return (object)GetValue(SelectedItem_Property); }
set { SetValue(SelectedItem_Property, value); }
}
public static readonly DependencyProperty SelectedItem_Property = DependencyProperty.Register("SelectedItem_", typeof(object), typeof(ExtendedTreeView), new UIPropertyMetadata(null));
}
and add this to your xaml:
<local:ExtendedTreeView ItemsSource="{Binding Items}" SelectedItem_="{Binding Item, Mode=TwoWay}">
.....
</local:ExtendedTreeView>
Solution 4:
It answers a little more than the OP is expecting... But I hope it could help some one at least.
If you want to execute a ICommand
whenever the SelectedItem
changed, you can bind a command on an event and the use of a property SelectedItem
in the ViewModel
isn't needed anymore.
To do so:
1- Add reference to System.Windows.Interactivity
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
2- Bind the command to the event SelectedItemChanged
<TreeView x:Name="myTreeView" Margin="1"
ItemsSource="{Binding Directories}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectedItemChanged">
<i:InvokeCommandAction Command="{Binding SomeCommand}"
CommandParameter="
{Binding ElementName=myTreeView
,Path=SelectedItem}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
<TreeView.ItemTemplate>
<!-- ... -->
</TreeView.ItemTemplate>
</TreeView>