所以有人建议使用WPF TreeView
,我想:"是的,这似乎是正确的做法。"现在,几小时后,我简直无法相信使用这种控制有多么困难。通过一系列的研究,我能够让TreeView`控件工作,但我根本找不到"正确的"将所选项目添加到视图模型的方法。我不需要从代码中设置所选项目;我只需要我的视图模型就可以知道用户选择了哪个项目。
到目前为止,我有这个XAML,它本身并不直观。这都在UserControl.Resources标记内:
<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上找到了各种方法来获取所选项目,而且很多都很复杂和困难。是否有简单的方式将所选项目添加到我的视图模型中?
注意:TreeView上有一个SelectedItem
属性,但它是只读的。令我沮丧的是,只读是好的;我不想通过代码更改它。但是我无法使用它,因为编译器抱怨它是只读的。
还有一个看似优雅的建议是做这样的事情:
<ContentPresenter Content="{Binding ElementName=treeView1, Path=SelectedItem}" />
我问了这个问题:&#34;您的视图模型如何获取此信息?我得到ContentPresenter
保留所选项目,但我们如何将其转换为视图模型?&#34;但目前还没有答案。
所以,我的整体问题是:&#34;是否有简单的方式将所选项目提供给我的视图模型?&#34;
答案 0 :(得分:28)
要执行您想要的操作,您可以修改ItemContainerStyle
的{{1}}:
TreeView
您的视图模型(树中每个项目的视图模型)必须公开布尔<TreeView>
<TreeView.ItemContainerStyle>
<Style TargetType="TreeViewItem">
<Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay}"/>
</Style>
</TreeView.ItemContainerStyle>
</TreeView>
属性。
如果您希望能够控制特定IsSelected
是否已展开,您也可以使用该属性的设置器:
TreeViewItem
然后,您的视图模型必须公开布尔<Setter Property="IsExpanded" Value="{Binding IsExpanded, Mode=TwoWay}"/>
属性。
请注意,这些属性可以双向运行,因此,如果用户选择树中的节点,则视图模型的IsExpanded
属性将设置为true。另一方面,如果在视图模型上将IsSelected
设置为true,则将选择树视图模型中的节点。同样随着扩展。
如果你没有树中每个项目的视图模型,那么你应该得到一个。没有视图模型意味着您将模型对象用作视图模型,但为了使其工作,这些对象需要IsSelected
属性。
要在父视图模型(绑定到IsSelected
并具有子视图模型集合的那个)上公开SelectedItem
属性,您可以像这样实现它:
TreeView
如果您不想跟踪树上每个项目的选择,您仍然可以使用public ChildViewModel SelectedItem {
get { return Items.FirstOrDefault(i => i.IsSelected); }
}
上的SelectedItem
属性。但是,为了能够实现“MVVM样式”,您需要使用混合行为(可用作各种NuGet包 - 搜索“混合交互”)。
这里我添加了一个TreeView
,每当所选项在树中发生变化时,它将调用一个命令:
EventTrigger
您必须在返回<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>
的{{1}}的{{1}}处添加媒体资源SetSelectedItemCommand
。当树视图的选定项更改时,将使用所选项作为参数调用命令上的DataContext
方法。创建命令的最简单方法可能是使用TreeView
(谷歌来获取实现,因为它不是WPF的一部分)。
允许双向绑定而不使用笨重命令的更好的替代方法是在Stack Overflow上使用Steve Greatrex提供的BindableSelectedItemBehavior。
答案 1 :(得分:5)
我可能会使用SelectedItemChanged
事件在您的VM上设置相应的属性。
答案 2 :(得分:1)
根据Martin的回答,我做了一个简单的应用程序,展示了如何应用所提出的解决方案。
示例代码使用Cinch V2框架来支持MVVM,但可以轻松更改它以使用您偏好的框架。
对于那些感兴趣的人,在GitHub上here is the code
希望它有所帮助。
答案 3 :(得分:0)
派对迟到了,但对于现在遇到这种情况的人来说,我的解决方案是:
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectedItemChanged">
<i:InvokeCommandAction Command="{Binding SomeICommand}"
CommandParameter="{Binding ElementName=treeviewName, Path=SelectedItem}" />
</i:EventTrigger>
</i:Interaction.Triggers>
这将允许您使用标准的MVVM ICommand绑定来访问SelectedItem,而无需使用后面的代码或一些冗长的工作。
答案 4 :(得分:0)
晚了派对,但作为MVVMLight用户的替代选择:
整个实施过程非常快,而且效果很好。
此处为IsSelected属性(SourceItem是所选ViewModel项的Model部分):
Public Property IsSelected As Boolean
Get
Return _isSelected
End Get
Set(value As Boolean)
If Me.HasImages Then
_isSelected = value
OnPropertyChanged("IsSelected")
Messenger.Default.Send(Of SelectedImageFolderChangedMessage)(New SelectedImageFolderChangedMessage(Me, SourceItem, "SelectedImageFolder"))
Else
Me.IsExpanded = Not Me.IsExpanded
End If
End Set
End Property
,这里是VM主机代码:
Messenger.Default.Register(Of SelectedImageFolderChangedMessage)(Me, AddressOf NewSelectedImageFolder)
Private Sub NewSelectedImageFolder(msg As SelectedImageFolderChangedMessage)
If msg.PropertyName = "SelectedImageFolder" Then
Me.SelectedFolderItem = msg.NewValue
End If
End Sub