WPF树视图中SelectedItem的数据绑定

如何检索在WPF-treeview中选择的项?我想在XAML中这样做,因为我想绑定它。

你可能认为它是SelectedItem,但显然不存在是只读的,因此不可用。

这就是我想做的:

<TreeView ItemsSource="{Binding Path=Model.Clusters}"
ItemTemplate="{StaticResource ClusterTemplate}"
SelectedItem="{Binding Path=Model.SelectedCluster}" />

我想将SelectedItem绑定到我的模型上的一个属性。

但这给了我一个错误:

“SelectedItem”属性是只读的,不能从标记中设置。

<强>编辑: 这就是我解决这个问题的方法

<TreeView
ItemsSource="{Binding Path=Model.Clusters}"
ItemTemplate="{StaticResource HoofdCLusterTemplate}"
SelectedItemChanged="TreeView_OnSelectedItemChanged" />

在我的xaml的代码背后文件:

private void TreeView_OnSelectedItemChanged(object sender, RoutedPropertyChangedEventArgs<object> e)
{
Model.SelectedCluster = (Cluster)e.NewValue;
}
192511 次浏览

此属性存在:树状视图。设置SelectedItem

但是它是只读的,所以你不能通过绑定来赋值它,只能检索它

你也可以使用TreeViewItem。IsSelected财产

我找到了一个解决办法。它可以移动混乱,这样MVVM就可以工作了。

首先添加这个类:

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));
}

并添加到你的xaml:

 <local:ExtendedTreeView ItemsSource="{Binding Items}" SelectedItem_="{Binding Item, Mode=TwoWay}">
.....
</local:ExtendedTreeView>

这可以以一种“更好”的方式完成,只使用绑定和GalaSoft MVVM Light库的EventToCommand。在您的VM中添加一个命令,当所选项目发生变化时将调用该命令,并初始化该命令以执行任何必要的操作。在这个例子中,我使用了一个RelayCommand,只设置SelectedCluster属性。

public class ViewModel
{
public ViewModel()
{
SelectedClusterChanged = new RelayCommand<Cluster>( c => SelectedCluster = c );
}


public RelayCommand<Cluster> SelectedClusterChanged { get; private set; }


public Cluster SelectedCluster { get; private set; }
}

然后在xaml中添加EventToCommand行为。使用混合非常简单。

<TreeView
x:Name="lstClusters"
ItemsSource="{Binding Path=Model.Clusters}"
ItemTemplate="{StaticResource HoofdCLusterTemplate}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectedItemChanged">
<GalaSoft_MvvmLight_Command:EventToCommand Command="{Binding SelectedClusterChanged}" CommandParameter="{Binding ElementName=lstClusters,Path=SelectedValue}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</TreeView>

我意识到这已经有了一个被接受的答案,但我把这些放在一起来解决问题。它使用了与Delta的解决方案类似的思想,但不需要子类化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;
}
}

然后你可以在你的XAML中使用它:

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

希望它能帮助到一些人!

我偶然发现了这一页,寻找与原作者相同的答案,并证明了总是有不止一种方法来做这件事,解决方案对我来说甚至比这里提供的答案更容易,所以我想我不妨添加到堆中。

绑定的动机是保持它的良好&MVVM。ViewModel的可能用法是有一个带有名称的属性,例如“CurrentThingy”,而在其他地方,其他东西上的DataContext被绑定到“CurrentThingy”。

而不是通过额外的步骤(例如:自定义行为,第三方控制)来支持从TreeView到我的模型的良好绑定,然后从其他东西到我的模型,我的解决方案是使用简单的元素绑定其他东西到TreeView。SelectedItem,而不是将其他东西绑定到ViewModel,从而跳过所需的额外工作。

XAML:

<TreeView x:Name="myTreeView" ItemsSource="{Binding MyThingyCollection}">
.... stuff
</TreeView>


<!-- then.. somewhere else where I want to see the currently selected TreeView item: -->


<local:MyThingyDetailsView
DataContext="{Binding ElementName=myTreeView, Path=SelectedItem}" />

当然,这对于读取当前选择的项目很好,但不能设置它,这是我所需要的。

我建议对Steve Greatrex提供的行为进行补充。他的行为并不反映来自源的更改,因为它可能不是TreeViewItems的集合。 所以这是一个在树中找到TreeViewItem的问题,它的数据上下文是来自源的selectedValue。 TreeView有一个名为“ItemsHost”的受保护属性,它包含TreeViewItem集合。我们可以通过反射获得它,并遍历树搜索所选项目。< / p >
private static void OnSelectedItemChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
var behavior = sender as BindableSelectedItemBehaviour;


if (behavior == null) return;


var tree = behavior.AssociatedObject;


if (tree == null) return;


if (e.NewValue == null)
foreach (var item in tree.Items.OfType<TreeViewItem>())
item.SetValue(TreeViewItem.IsSelectedProperty, false);


var treeViewItem = e.NewValue as TreeViewItem;
if (treeViewItem != null)
{
treeViewItem.SetValue(TreeViewItem.IsSelectedProperty, true);
}
else
{
var itemsHostProperty = tree.GetType().GetProperty("ItemsHost", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);


if (itemsHostProperty == null) return;


var itemsHost = itemsHostProperty.GetValue(tree, null) as Panel;


if (itemsHost == null) return;


foreach (var item in itemsHost.Children.OfType<TreeViewItem>())
if (WalkTreeViewItem(item, e.NewValue)) break;
}
}


public static bool WalkTreeViewItem(TreeViewItem treeViewItem, object selectedValue) {
if (treeViewItem.DataContext == selectedValue)
{
treeViewItem.SetValue(TreeViewItem.IsSelectedProperty, true);
treeViewItem.Focus();
return true;
}


foreach (var item in treeViewItem.Items.OfType<TreeViewItem>())
if (WalkTreeViewItem(item, selectedValue)) return true;


return false;
}

这种方式的行为适用于双向绑定。或者,也可以将ItemsHost获取移动到Behavior的OnAttached方法,从而节省每次绑定更新时使用反射的开销。

一切都很复杂……使用Caliburn Micro (http://caliburnmicro.codeplex.com/)

观点:

<TreeView Micro:Message.Attach="[Event SelectedItemChanged] = [Action SetSelectedItem($this.SelectedItem)]" />

视图模型:

public void SetSelectedItem(YourNodeViewModel item) {};

在学习了一天的互联网后,我找到了自己的解决方案,在正常的 WPF/ c#环境中创建正常的树视图后选择项目

private void BuildSortTree(int sel)
{
MergeSort.Items.Clear();
TreeViewItem itTemp = new TreeViewItem();
itTemp.Header = SortList[0];
MergeSort.Items.Add(itTemp);
TreeViewItem prev;
itTemp.IsExpanded = true;
if (0 == sel) itTemp.IsSelected= true;
prev = itTemp;
for(int i = 1; i<SortList.Count; i++)
{


TreeViewItem itTempNEW = new TreeViewItem();
itTempNEW.Header = SortList[i];
prev.Items.Add(itTempNEW);
itTempNEW.IsExpanded = true;
if (i == sel) itTempNEW.IsSelected = true;
prev = itTempNEW ;
}
}

还有一种方法可以在不使用Interaction.Behaviors的情况下创建XAML可绑定SelectedItem属性。

public static class BindableSelectedItemHelper
{
#region Properties


public static readonly DependencyProperty SelectedItemProperty = DependencyProperty.RegisterAttached("SelectedItem", typeof(object), typeof(BindableSelectedItemHelper),
new FrameworkPropertyMetadata(null, OnSelectedItemPropertyChanged));


public static readonly DependencyProperty AttachProperty = DependencyProperty.RegisterAttached("Attach", typeof(bool), typeof(BindableSelectedItemHelper), new PropertyMetadata(false, Attach));


private static readonly DependencyProperty IsUpdatingProperty = DependencyProperty.RegisterAttached("IsUpdating", typeof(bool), typeof(BindableSelectedItemHelper));


#endregion


#region Implementation


public static void SetAttach(DependencyObject dp, bool value)
{
dp.SetValue(AttachProperty, value);
}


public static bool GetAttach(DependencyObject dp)
{
return (bool)dp.GetValue(AttachProperty);
}


public static string GetSelectedItem(DependencyObject dp)
{
return (string)dp.GetValue(SelectedItemProperty);
}


public static void SetSelectedItem(DependencyObject dp, object value)
{
dp.SetValue(SelectedItemProperty, value);
}


private static bool GetIsUpdating(DependencyObject dp)
{
return (bool)dp.GetValue(IsUpdatingProperty);
}


private static void SetIsUpdating(DependencyObject dp, bool value)
{
dp.SetValue(IsUpdatingProperty, value);
}


private static void Attach(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
TreeListView treeListView = sender as TreeListView;
if (treeListView != null)
{
if ((bool)e.OldValue)
treeListView.SelectedItemChanged -= SelectedItemChanged;


if ((bool)e.NewValue)
treeListView.SelectedItemChanged += SelectedItemChanged;
}
}


private static void OnSelectedItemPropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
TreeListView treeListView = sender as TreeListView;
if (treeListView != null)
{
treeListView.SelectedItemChanged -= SelectedItemChanged;


if (!(bool)GetIsUpdating(treeListView))
{
foreach (TreeViewItem item in treeListView.Items)
{
if (item == e.NewValue)
{
item.IsSelected = true;
break;
}
else
item.IsSelected = false;
}
}


treeListView.SelectedItemChanged += SelectedItemChanged;
}
}


private static void SelectedItemChanged(object sender, RoutedEventArgs e)
{
TreeListView treeListView = sender as TreeListView;
if (treeListView != null)
{
SetIsUpdating(treeListView, true);
SetSelectedItem(treeListView, treeListView.SelectedItem);
SetIsUpdating(treeListView, false);
}
}
#endregion
}

然后你可以在你的XAML中使用它:

<TreeView  helper:BindableSelectedItemHelper.Attach="True"
helper:BindableSelectedItemHelper.SelectedItem="{Binding SelectedItem, Mode=TwoWay}">

我试过了这道题的所有解。没有人能完全解决我的问题。所以我认为最好使用这样的继承类与重定义属性SelectedItem。如果你从GUI中选择tree element并在代码中设置此属性值,它将完美地工作

public class TreeViewEx : TreeView
{
public TreeViewEx()
{
this.SelectedItemChanged += new RoutedPropertyChangedEventHandler<object>(TreeViewEx_SelectedItemChanged);
}


void TreeViewEx_SelectedItemChanged(object sender, RoutedPropertyChangedEventArgs<object> e)
{
this.SelectedItem = e.NewValue;
}


#region SelectedItem


/// <summary>
/// Gets or Sets the SelectedItem possible Value of the TreeViewItem object.
/// </summary>
public new object SelectedItem
{
get { return this.GetValue(TreeViewEx.SelectedItemProperty); }
set { this.SetValue(TreeViewEx.SelectedItemProperty, value); }
}


// Using a DependencyProperty as the backing store for MyProperty.  This enables animation, styling, binding, etc...
public new static readonly DependencyProperty SelectedItemProperty =
DependencyProperty.Register("SelectedItem", typeof(object), typeof(TreeViewEx),
new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, SelectedItemProperty_Changed));


static void SelectedItemProperty_Changed(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
{
TreeViewEx targetObject = dependencyObject as TreeViewEx;
if (targetObject != null)
{
TreeViewItem tvi = targetObject.FindItemNode(targetObject.SelectedItem) as TreeViewItem;
if (tvi != null)
tvi.IsSelected = true;
}
}
#endregion SelectedItem


public TreeViewItem FindItemNode(object item)
{
TreeViewItem node = null;
foreach (object data in this.Items)
{
node = this.ItemContainerGenerator.ContainerFromItem(data) as TreeViewItem;
if (node != null)
{
if (data == item)
break;
node = FindItemNodeInChildren(node, item);
if (node != null)
break;
}
}
return node;
}


protected TreeViewItem FindItemNodeInChildren(TreeViewItem parent, object item)
{
TreeViewItem node = null;
bool isExpanded = parent.IsExpanded;
if (!isExpanded) //Can't find child container unless the parent node is Expanded once
{
parent.IsExpanded = true;
parent.UpdateLayout();
}
foreach (object data in parent.Items)
{
node = parent.ItemContainerGenerator.ContainerFromItem(data) as TreeViewItem;
if (data == item && node != null)
break;
node = FindItemNodeInChildren(node, item);
if (node != null)
break;
}
if (node == null && parent.IsExpanded != isExpanded)
parent.IsExpanded = isExpanded;
if (node != null)
parent.IsExpanded = true;
return node;
}
}

也可以使用TreeView项的IsSelected属性来完成。我是这么做的,

public delegate void TreeviewItemSelectedHandler(TreeViewItem item);
public class TreeViewItem
{
public static event TreeviewItemSelectedHandler OnItemSelected = delegate { };
public bool IsSelected
{
get { return isSelected; }
set
{
isSelected = value;
if (value)
OnItemSelected(this);
}
}
}

然后在包含TreeView绑定到的数据的ViewModel中,只需订阅TreeViewItem类中的事件。

TreeViewItem.OnItemSelected += TreeViewItemSelected;

最后,在同一个ViewModel中实现这个处理器,

private void TreeViewItemSelected(TreeViewItem item)
{
//Do something
}

当然,绑定,

<Setter Property="IsSelected" Value="{Binding IsSelected}" />

WPF MVVM TreeView SelectedItem . MVVM TreeView SelectedItem

... 是一个更好的答案,但没有提到一种在ViewModel中获取/设置SelectedItem的方法。

  1. 添加一个IsSelected布尔属性到ItemViewModel,并在TreeViewItem的样式设置器中绑定到它。
  2. 在ViewModel中添加SelectedItem属性,作为TreeView的DataContext。这是上面解决方案中缺少的部分。
' ItemVM...
Public Property IsSelected As Boolean
Get
Return _func.SelectedNode Is Me
End Get
Set(value As Boolean)
If IsSelected  value Then
_func.SelectedNode = If(value, Me, Nothing)
End If
RaisePropertyChange()
End Set
End Property
' TreeVM...
Public Property SelectedItem As ItemVM
Get
Return _selectedItem
End Get
Set(value As ItemVM)
If _selectedItem Is value Then
Return
End If
Dim prev = _selectedItem
_selectedItem = value
If prev IsNot Nothing Then
prev.IsSelected = False
End If
If _selectedItem IsNot Nothing Then
_selectedItem.IsSelected = True
End If
End Set
End Property
<TreeView ItemsSource="{Binding Path=TreeVM}"
BorderBrush="Transparent">
<TreeView.ItemContainerStyle>
<Style TargetType="TreeViewItem">
<Setter Property="IsExpanded" Value="{Binding IsExpanded}"/>
<Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay}"/>
</Style>
</TreeView.ItemContainerStyle>
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Children}">
<TextBlock Text="{Binding Name}"/>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>

我的需求是基于PRISM-MVVM的解决方案,其中需要TreeView,绑定对象类型为Collection<>,因此需要HierarchicalDataTemplate。默认的BindableSelectedItemBehavior不能识别子TreeViewItem。让它在这种情况下起作用。

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 behavior = sender as BindableSelectedItemBehavior;
if (behavior == null) return;
var tree = behavior.AssociatedObject;
if (tree == null) return;
if (e.NewValue == null)
foreach (var item in tree.Items.OfType<TreeViewItem>())
item.SetValue(TreeViewItem.IsSelectedProperty, false);
var treeViewItem = e.NewValue as TreeViewItem;
if (treeViewItem != null)
treeViewItem.SetValue(TreeViewItem.IsSelectedProperty, true);
else
{
var itemsHostProperty = tree.GetType().GetProperty("ItemsHost", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
if (itemsHostProperty == null) return;
var itemsHost = itemsHostProperty.GetValue(tree, null) as Panel;
if (itemsHost == null) return;
foreach (var item in itemsHost.Children.OfType<TreeViewItem>())
{
if (WalkTreeViewItem(item, e.NewValue))
break;
}
}
}


public static bool WalkTreeViewItem(TreeViewItem treeViewItem, object selectedValue)
{
if (treeViewItem.DataContext == selectedValue)
{
treeViewItem.SetValue(TreeViewItem.IsSelectedProperty, true);
treeViewItem.Focus();
return true;
}
var itemsHostProperty = treeViewItem.GetType().GetProperty("ItemsHost", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
if (itemsHostProperty == null) return false;
var itemsHost = itemsHostProperty.GetValue(treeViewItem, null) as Panel;
if (itemsHost == null) return false;
foreach (var item in itemsHost.Children.OfType<TreeViewItem>())
{
if (WalkTreeViewItem(item, selectedValue))
break;
}
return false;
}
#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;
}
}

这样就可以遍历所有元素,而不考虑级别。

它的答案比OP期待的要多一点…但我希望它至少能帮助到一些人。

如果你想在SelectedItem发生变化时执行ICommand,你可以在事件上绑定一个命令,不再需要在ViewModel中使用属性SelectedItem

这样做:

1-添加System.Windows.Interactivity引用

xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"

2-将命令绑定到事件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>

(让我们都同意,TreeView在这个问题上显然是行不通的。绑定到SelectedItem是显而易见的。叹息)

我需要解决方案来正确地与TreeViewItem的IsSelected属性交互,所以这是我是如何做到的:

// the Type CustomThing needs to implement IsSelected with notification
// for this to work.
public class CustomTreeView : TreeView
{
public CustomThing SelectedCustomThing
{
get
{
return (CustomThing)GetValue(SelectedNode_Property);
}
set
{
SetValue(SelectedNode_Property, value);
if(value != null) value.IsSelected = true;
}
}


public static DependencyProperty SelectedNode_Property =
DependencyProperty.Register(
"SelectedCustomThing",
typeof(CustomThing),
typeof(CustomTreeView),
new FrameworkPropertyMetadata(
null,
FrameworkPropertyMetadataOptions.None,
SelectedNodeChanged));


public CustomTreeView(): base()
{
this.SelectedItemChanged += new RoutedPropertyChangedEventHandler<object>(SelectedItemChanged_CustomHandler);
}


void SelectedItemChanged_CustomHandler(object sender, RoutedPropertyChangedEventArgs<object> e)
{
SetValue(SelectedNode_Property, SelectedItem);
}


private static void SelectedNodeChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var treeView = d as CustomTreeView;
var newNode = e.NewValue as CustomThing;


treeView.SelectedCustomThing = (CustomThing)e.NewValue;
}
}

用这个XAML:

<local:CustonTreeView ItemsSource="{Binding TreeRoot}"
SelectedCustomThing="{Binding SelectedNode,Mode=TwoWay}">
<TreeView.ItemContainerStyle>
<Style TargetType="TreeViewItem">
<Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay}" />
</Style>
</TreeView.ItemContainerStyle>
</local:CustonTreeView>

我给你带来了我的解决方案,它具有以下特点:

  • 支持2种绑定方式

  • 自动更新TreeViewItemIsSelected属性(根据SelectedItem)

  • 没有TreeView子类

  • 绑定到ViewModel的项可以是任何类型(甚至是null)

1/在你的CS中粘贴以下代码:

public class BindableSelectedItem
{
public static readonly DependencyProperty SelectedItemProperty = DependencyProperty.RegisterAttached(
"SelectedItem", typeof(object), typeof(BindableSelectedItem), new PropertyMetadata(default(object), OnSelectedItemPropertyChangedCallback));


private static void OnSelectedItemPropertyChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var treeView = d as TreeView;
if (treeView != null)
{
BrowseTreeViewItems(treeView, tvi =>
{
tvi.IsSelected = tvi.DataContext == e.NewValue;
});
}
else
{
throw new Exception("Attached property supports only TreeView");
}
}


public static void SetSelectedItem(DependencyObject element, object value)
{
element.SetValue(SelectedItemProperty, value);
}


public static object GetSelectedItem(DependencyObject element)
{
return element.GetValue(SelectedItemProperty);
}


public static void BrowseTreeViewItems(TreeView treeView, Action<TreeViewItem> onBrowsedTreeViewItem)
{
var collectionsToVisit = new System.Collections.Generic.List<Tuple<ItemContainerGenerator, ItemCollection>> { new Tuple<ItemContainerGenerator, ItemCollection>(treeView.ItemContainerGenerator, treeView.Items) };
var collectionIndex = 0;
while (collectionIndex < collectionsToVisit.Count)
{
var itemContainerGenerator = collectionsToVisit[collectionIndex].Item1;
var itemCollection = collectionsToVisit[collectionIndex].Item2;
for (var i = 0; i < itemCollection.Count; i++)
{
var tvi = itemContainerGenerator.ContainerFromIndex(i) as TreeViewItem;
if (tvi == null)
{
continue;
}


if (tvi.ItemContainerGenerator.Status == System.Windows.Controls.Primitives.GeneratorStatus.ContainersGenerated)
{
collectionsToVisit.Add(new Tuple<ItemContainerGenerator, ItemCollection>(tvi.ItemContainerGenerator, tvi.Items));
}


onBrowsedTreeViewItem(tvi);
}


collectionIndex++;
}
}


}

2/在XAML文件中使用的例子

<TreeView myNS:BindableSelectedItem.SelectedItem="{Binding Path=SelectedItem, Mode=TwoWay}" />

我知道这个帖子已经10年了,但问题仍然存在....

最初的问题是“检索”所选项目。我还需要在我的视图模型中“获取”选定的项目(而不是设置它)。在这个线程中的所有答案中,“Wes”的答案是唯一一个以不同的方式解决问题的答案:如果你可以使用“Selected Item”作为数据绑定的目标,那么就使用它作为数据绑定的源。Wes对另一个视图属性做过,我将对viewmodel属性做

我们需要两样东西:

  • 在视图模型中创建一个依赖属性(在我的情况下,类型为'MyObject',因为我的树视图绑定到'MyObject'类型的对象)
  • 从Treeview绑定。SelectedItem属性在视图的构造函数中(是的,这是后面的代码,但很可能你也会init你的datacontext)

视图模型:

public static readonly DependencyProperty SelectedTreeViewItemProperty = DependencyProperty.Register("SelectedTreeViewItem", typeof(MyObject), typeof(MyViewModel), new PropertyMetadata(OnSelectedTreeViewItemChanged));


private static void OnSelectedTreeViewItemChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
(d as MyViewModel).OnSelectedTreeViewItemChanged(e);
}


private void OnSelectedTreeViewItemChanged(DependencyPropertyChangedEventArgs e)
{
//do your stuff here
}


public MyObject SelectedWorkOrderTreeViewItem
{
get { return (MyObject)GetValue(SelectedTreeViewItemProperty); }
set { SetValue(SelectedTreeViewItemProperty, value); }
}

构造函数:

Binding binding = new Binding("SelectedItem")
{
Source = treeView, //name of tree view in xaml
Mode = BindingMode.OneWay
};


BindingOperations.SetBinding(DataContext, MyViewModel.SelectedTreeViewItemProperty, binding);

我提出了这个解决方案(我认为这是最简单的,并且没有内存泄漏),它非常适合从视图的选定项更新ViewModel的选定项。

请注意,从ViewModel中更改所选项不会更新视图中的所选项。

public class TreeViewEx : TreeView
{
public static readonly DependencyProperty SelectedItemExProperty = DependencyProperty.Register("SelectedItemEx", typeof(object), typeof(TreeViewEx), new FrameworkPropertyMetadata(default(object))
{
BindsTwoWayByDefault = true // Required in order to avoid setting the "BindingMode" from the XAML
});


public object SelectedItemEx
{
get => GetValue(SelectedItemExProperty);
set => SetValue(SelectedItemExProperty, value);
}


protected override void OnSelectedItemChanged(RoutedPropertyChangedEventArgs<object> e)
{
SelectedItemEx = e.NewValue;
}
}

XAML使用

<l:TreeViewEx ItemsSource="{Binding Path=Items}" SelectedItemEx="{Binding Path=SelectedItem}" >

我意识到这已经有一段时间了,但FWIW我正在使用Telerik的RadTreeView, SelectedItem似乎工作得很好-要么问题已经修复,要么Telerik已经为我们工作了。

当你点击某个项目列表时,你会在“selected”中得到数据;财产。< br > < br > ViewModel < / >强:

public class ShellViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;


protected void NotifyPropertyChanged(String info)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
}


private ObservableCollection<Books> _books;


private List<Books> bookList = new List<Books>();


public ObservableCollection<Books> Books
{
get { return _books; }
set { _books = value; NotifyPropertyChanged("Books"); }
}


private Books books;


public Books Selected
{
get { return books; }
set { books = value; }
}




public ShellViewModel()
{
bookList = new List<Books>()
{
new Books{BookName = "Harry Poter",Price ="15$"},
new Books{BookName = "Harry Poter 2 ",Price ="14.95$"},
new Books{BookName = "Harry Poter 3",Price ="18.50$"},
new Books{BookName = "Harry Poter 4",Price ="32.90$"},
};


Books = new ObservableCollection<Books>(bookList);
}
}


public class Books
{
public string BookName { get; set; }
public string Price { get; set; }
}

XAML:

 <ListView x:Name="lst" Grid.Row="2" ItemsSource="{Binding Books}" SelectedItem="{Binding Selected}">
<ListView.View>
<GridView >
<GridViewColumn Header="Name" DisplayMemberBinding="{Binding BookName}"  />
<GridViewColumn Header="Price" Width="100" DisplayMemberBinding="{Binding Price}"/>
</GridView>
</ListView.View>
</ListView>