使用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>
这可以按环境正确显示服务器(dev,QA,prod).但是,我在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.
似乎也有这样的建议:
<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
:
To do what you want you can modify the ItemContainerStyle
of the TreeView
:
<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
,也可以对该属性使用设置器:
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.
要在父视图模型上公开一个SelectedItem
属性(绑定到TreeView
并具有子视图模型的集合),可以这样实现:
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风格",您需要使用Blend行为(可作为各种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>
您将必须在TreeView
的DataContext
上添加属性SetSelectedItemCommand
,并返回ICommand
.当树形视图的选定项目更改时,将以选定项目为参数调用命令上的Execute
方法.创建命令的最简单方法可能是使用DelegateCommand
(使用Google来获取实现,因为它不是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).
一种可能更好的替代方法,它允许在没有笨拙命令的情况下进行双向绑定,请使用由Steve Greatrex提供的 BindableSelectedItemBehavior 堆栈溢出.
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屋!