如何检索在WPF-treeview中选择的项?我想在XAML中这样做,因为我想绑定它。
您可能认为它是SelectedItem,但显然它不存在是只读的,因此不可用。
这就是我想做的:
<TreeView ItemsSource="{Binding Path=Model.Clusters}"
ItemTemplate="{StaticResource ClusterTemplate}"
SelectedItem="{Binding Path=Model.SelectedCluster}" />
我想将SelectedItem绑定到我的Model上的一个属性。
但这给了我一个错误:
“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;
}
我找到了一个解决办法。它可以移动混乱,这样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>
我试过了这道题的所有解。没有人能完全解决我的问题。所以我认为最好使用这样的继承类与重定义属性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;
}
}
我给你带来了我的解决方案,它具有以下特点:
支持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}" />
走吧
... 是一个更好的答案,但没有提到一种在ViewModel中获取/设置SelectedItem的方法。
添加一个IsSelected布尔属性到ItemViewModel,并在TreeViewItem的样式设置器中绑定到它。
在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>
(让我们都同意,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>