使用 MVVM 获取选定的 TreeViewItem [英] Get Selected TreeViewItem Using MVVM

查看:53
本文介绍了使用 MVVM 获取选定的 TreeViewItem的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

所以有人建议使用 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?"

推荐答案

修改TreeViewItemContainerStyle即可:

<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>

您必须在返回 ICommandTreeViewDataContext 上添加属性 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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆