使用 MVVM 获取选定的 TreeViewItem [英] Get Selected TreeViewItem Using MVVM
问题描述
所以有人建议使用 WPF TreeView
,我想:是的,这似乎是正确的方法."现在,几个小时后,我简直不敢相信使用这个控件有多么困难.通过大量研究,我能够使 TreeView` 控件工作,但我根本找不到将所选项目放入视图模型的正确"方法.我不需要从代码中设置所选项目;我只需要我的视图模型来知道用户选择了哪个项目.
So someone suggested using a WPF TreeView
, and I thought: "Yeah, that seems like the right approach." Now, hours and hours later, I simply can't believe how difficult it has been to use this control. Through a bunch of research, I was able to get the TreeView` control working, but I simply cannot find the "proper" way to get the selected item to the view model. I do not need to set the selected item from code; I just need my view model to know which item the user selected.
到目前为止,我有这个 XAML,它本身并不是很直观.这一切都在 UserControl.Resources 标签中:
So far, I have this XAML, which isn't very intuitive on its own. This is all within the UserControl.Resources tag:
<CollectionViewSource x:Key="cvs" Source="{Binding ApplicationServers}">
<CollectionViewSource.GroupDescriptions>
<PropertyGroupDescription PropertyName="DeploymentEnvironment"/>
</CollectionViewSource.GroupDescriptions>
</CollectionViewSource>
<!-- Our leaf nodes (server names) -->
<DataTemplate x:Key="serverTemplate">
<TextBlock Text="{Binding Path=Name}"/>
</DataTemplate>
<!-- Note: The Items path refers to the items in the CollectionViewSource group (our servers).
The Name path refers to the group name. -->
<HierarchicalDataTemplate x:Key="categoryTemplate"
ItemsSource="{Binding Path=Items}"
ItemTemplate="{StaticResource serverTemplate}">
<TextBlock Text="{Binding Path=Name}" FontWeight="Bold"/>
</HierarchicalDataTemplate>
这是树状视图:
<TreeView DockPanel.Dock="Bottom" ItemsSource="{Binding Source={StaticResource cvs}, Path=Groups}"
ItemTemplate="{StaticResource categoryTemplate}">
<Style TargetType="TreeViewItem">
<Setter Property="IsSelected" Value="{Binding Path=IsSelected}"/>
</Style>
</TreeView>
这可以按环境(开发、质量检查、生产)正确显示服务器.但是,我在 SO 上找到了多种获取所选项目的方法,其中许多方法既复杂又困难.是否有一种简单方法可以将所选项目添加到我的视图模型中?
This correctly shows servers by environment (dev, QA, prod). However, I've found various ways on SO to get the selected item, and many are convoluted and difficult. Is there a simple way to get the selected item to my view model?
注意:TreeView` 上有一个SelectedItem
属性,但它是只读的.令我沮丧的是,只读就可以了;我不想通过代码改变它.但是我不能使用它,因为编译器抱怨它是只读的.
Note: There is a SelectedItem
property on the TreeView`, but it's read-only. What's frustrating to me is that read-only is just fine; I don't want to change it via code. But I can't use it because the compiler complains that it's read-only.
还有一个看似优雅的建议来做这样的事情:
There was also a seemingly elegant suggestion to do something like this:
<ContentPresenter Content="{Binding ElementName=treeView1, Path=SelectedItem}" />
我问了这个问题:你的视图模型如何获得这些信息?我知道 ContentPresenter
保存了选定的项目,但我们如何将它传递给视图模型?"但目前还没有答案.
And I asked this question: "How can your a view model get this information? I get that ContentPresenter
holds the selected item, but how do we get that over to the view model?" But there is no answer yet.
所以,我的总体问题是:是否有一种简单方法可以将所选项目添加到我的视图模型中?"
So, my overall question is: "Is there a simple way to get the selected item to my view model?"
推荐答案
修改TreeView
的ItemContainerStyle
即可:
<TreeView>
<TreeView.ItemContainerStyle>
<Style TargetType="TreeViewItem">
<Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay}"/>
</Style>
</TreeView.ItemContainerStyle>
</TreeView>
您的视图模型(树中每个项目的视图模型)必须公开一个布尔值 IsSelected
属性.
Your view-model (the view-model for each item in the tree) then has to expose a boolean IsSelected
property.
如果您希望能够控制特定 TreeViewItem
是否展开,您也可以为该属性使用 setter:
If you want to be able to control if a particular TreeViewItem
is expanded you can use a setter for that property too:
<Setter Property="IsExpanded" Value="{Binding IsExpanded, Mode=TwoWay}"/>
您的视图模型必须公开一个布尔值 IsExpanded
属性.
Your view-model then has to expose a boolean IsExpanded
property.
请注意,这些属性是双向的,因此如果用户选择树中的节点,则视图模型的 IsSelected
属性将设置为 true.另一方面,如果在视图模型上将 IsSelected
设置为 true,则将选择该视图模型树中的节点.和扩展一样.
Note that these properties work both ways so if the user selects a node in the tree the IsSelected
property of the view-model will be set to true. On the other hand if you set IsSelected
to true on a view-model the node in the tree for that view-model will be selected. And likewise with expanded.
如果树中的每一项都没有视图模型,那么你应该得到一个.没有视图模型意味着您将模型对象用作视图模型,但要使其工作,这些对象需要 IsSelected
属性.
If you don't have a view-model for each item in the tree, well, then you should get one. Not having a view-model means that you are using your model objects as view-models, but for this to work these objects require an IsSelected
property.
要在您的父视图模型(您绑定到 TreeView
并且具有子视图模型的集合)上公开 SelectedItem
属性,您可以实现像这样:
To expose an SelectedItem
property on your parent view-model (the one you bind to the TreeView
and that has a collection of child view-models) you can implement it like this:
public ChildViewModel SelectedItem {
get { return Items.FirstOrDefault(i => i.IsSelected); }
}
<小时>
如果您不想跟踪树上每个单独项目的选择,您仍然可以使用 TreeView
上的 SelectedItem
属性.但是,为了能够做到MVVM 风格",您需要使用混合行为(可作为各种 NuGet 包使用 - 搜索混合交互性").
If you don't want to track selection on each individual item on the tree you can still use the SelectedItem
property on the TreeView
. However, to be able to do it "MVVM style" you need to use a Blend behavior (available as various NuGet packages - search for "blend interactivity").
这里我添加了一个 EventTrigger
,它会在每次树中选定的项目改变时调用一个命令:
Here I have added an EventTrigger
that will invoke a command each time the selected item changes in the tree:
<TreeView x:Name="treeView">
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectedItemChanged">
<i:InvokeCommandAction
Command="{Binding SetSelectedItemCommand}"
CommandParameter="{Binding SelectedItem, ElementName=treeView}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</TreeView>
您必须在返回 ICommand
的 TreeView
的 DataContext
上添加属性 SetSelectedItemCommand
.当树视图的选定项更改时,将调用命令上的 Execute
方法,并将选定项作为参数.创建命令的最简单方法可能是使用 DelegateCommand
(谷歌它以获得实现,因为它不是 WPF 的一部分).
You will have to add a property SetSelectedItemCommand
on the DataContext
of the TreeView
returning an ICommand
. When the selected item of the tree view changes the Execute
method on the command is called with the selected item as the parameter. The easiest way to create a command is probably to use a DelegateCommand
(google it to get an implementation as it is not part of WPF).
在没有笨拙命令的情况下允许双向绑定的一个更好的选择是使用 BindableSelectedItemBehavior,由 Steve Greatrex 提供堆栈溢出.
A perhaps better alternative that allows two-way binding without the clunky command is to use BindableSelectedItemBehavior provided by Steve Greatrex here on Stack Overflow.
这篇关于使用 MVVM 获取选定的 TreeViewItem的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!