在 ContextMenu 中与 HierarchicalDataTemplate 作斗争
Struggling with HierarchicalDataTemplate in ContextMenu
我希望能够在我的视图模型中将 ContextMenu
的 ItemsSource
绑定到 Collection
,在 [= 中显示 Separator
s 14=],ItemsSource
必须是分层的(层次结构的每一层看起来都一样)。
在 中,我设法能够在数据绑定 ContextMenu
中显示菜单项和分隔符,但现在我很难使 ItemsSource
分层。
现在不知道怎么回事,能不能指教一下?
这是我的代码(简化为简短,但有效):
MenuItemViewModel.vb
Public Class MenuItemViewModel
Implements ICommand
Public Event CanExecuteChanged As EventHandler Implements ICommand.CanExecuteChanged
Public Property IsSeparator As Boolean
Public Property Caption As String
Private ReadOnly _subItems As List(Of MenuItemViewModel)
Public Sub New(createItems As Boolean, level As Byte)
_subItems = New List(Of MenuItemViewModel)
If createItems Then
_subItems.Add(New MenuItemViewModel(level < 4, level + 1) With {.Caption = "SubItem 1"})
_subItems.Add(New MenuItemViewModel(False, level + 1) With {.IsSeparator = True, .Caption = "SubSep 1"})
_subItems.Add(New MenuItemViewModel(level < 4, level + 1) With {.Caption = "SubItem 2"})
End If
End Sub
Public ReadOnly Property SubItems As List(Of MenuItemViewModel)
Get
Return _subItems
End Get
End Property
Public ReadOnly Property Command As ICommand
Get
Return Me
End Get
End Property
Public Sub Execute(ByVal parameter As Object) Implements ICommand.Execute
MessageBox.Show(Me.Caption)
End Sub
Public Function CanExecute(ByVal parameter As Object) As Boolean Implements ICommand.CanExecute
Return True
End Function
End Class
每个级别上每个菜单项的视图模型都有一个 Caption
显示在上下文菜单中,一个 IsSeparator
标志指示它是分隔符还是功能菜单项,一个 Command
作为功能菜单项时要绑定,当然还有 SubItems
collection 包含功能菜单项和分隔符到某个层次结构级别。
MainViewModel.vb
Public Class MainViewModel
Private ReadOnly _items As List(Of MenuItemViewModel)
Public Sub New()
_items = New List(Of MenuItemViewModel)
_items.Add(New MenuItemViewModel(True, 0) With {.Caption = "Item 1"})
_items.Add(New MenuItemViewModel(False, 0) With {.IsSeparator = True, .Caption = "Sep 1"})
_items.Add(New MenuItemViewModel(True, 0) With {.Caption = "Item 2"})
_items.Add(New MenuItemViewModel(True, 0) With {.Caption = "Item 3"})
_items.Add(New MenuItemViewModel(False, 0) With {.IsSeparator = True, .Caption = "Sep 2"})
_items.Add(New MenuItemViewModel(True, 0) With {.Caption = "Item 4"})
End Sub
Public ReadOnly Property Items As List(Of MenuItemViewModel)
Get
Return _items
End Get
End Property
End Class
主视图模型只有 Items
collection 包含功能菜单项和分隔符。
MainWindow.xaml
<Window x:Class="MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfApp3"
mc:Ignorable="d"
d:DataContext="{d:DesignInstance local:MainViewModel, IsDesignTimeCreatable=True}"
Title="MainWindow" Height="450" Width="800">
<Window.DataContext>
<local:MainViewModel />
</Window.DataContext>
<Window.Resources>
<ControlTemplate x:Key="mist" TargetType="{x:Type MenuItem}">
<Separator />
</ControlTemplate>
<ControlTemplate x:Key="mict" TargetType="{x:Type MenuItem}">
<MenuItem Header="{Binding Caption}" Command="{Binding Command}" ItemsSource="{Binding SubItems}" />
</ControlTemplate>
<Style x:Key="cmics" TargetType="{x:Type MenuItem}">
<Setter Property="Template" Value="{StaticResource mict}" />
<Style.Triggers>
<DataTrigger Binding="{Binding IsSeparator}" Value="True">
<Setter Property="Template" Value="{StaticResource mist}" />
</DataTrigger>
</Style.Triggers>
</Style>
</Window.Resources>
<Grid>
<TextBox HorizontalAlignment="Center" VerticalAlignment="Center" Text="Right click me">
<TextBox.ContextMenu>
<ContextMenu ItemsSource="{Binding Items}" ItemContainerStyle="{StaticResource cmics}">
<ContextMenu.ItemTemplate>
<HierarchicalDataTemplate DataType="{x:Type local:MenuItemViewModel}" ItemsSource="{Binding SubItems}" />
</ContextMenu.ItemTemplate>
</ContextMenu>
</TextBox.ContextMenu>
</TextBox>
</Grid>
</Window>
window资源包含两个ControlTemplate
s "mist"和"mict"以及一个Style
"cmics"在这两个ControlTemplate
s 取决于 IsSeparator
标志的值。只要 ItemsSource
不是分层的(参见 ),这就可以正常工作。
如果我的 Style
"cmics" 仅附加到 ContextMenu
的 ItemContainerStyle
(如我的示例代码中所示),那么它看起来像这样:
第一层有效,但其他层无效。将我的 Style
"cmics" 附加到 HierarchicalDataTemplate
的 ItemContainerStyle
时也不会改变。
如果我只将我的 Style
"cmics" 附加到 HierarchicalDataTemplate
那么它看起来像这样:
第一级不显示标题和分隔符,第二级有效,其他级别无效。
那么,我怎样才能说服 ContextMenu
使用我的 Style
"cmics" 作为每个层级的 ItemContainerStyle
?
我刚刚在 Xaml 部分对您的 (TextBox) 进行了一些更改。看看这个,
<TextBox HorizontalAlignment="Center" VerticalAlignment="Center" Text="Right click me">
<TextBox.Resources>
<HierarchicalDataTemplate DataType="{x:Type local:MenuItemViewModel}" ItemsSource="{Binding SubItems}">
<Button Content="{Binding Caption}" Command="{Binding Command}" Background="Red"/>
</HierarchicalDataTemplate>
</TextBox.Resources>
<TextBox.ContextMenu>
<ContextMenu ItemsSource="{Binding Items}" ItemContainerStyle="{StaticResource cmics}"/>
</TextBox.ContextMenu>
</TextBox>
基本上我已经删除了您的 ContexttMenu ItemTemplate 并在 TextBox.Resources 下添加了一个分层数据模板。
在数据模板中,我刚刚添加了一个单选按钮。您可以根据需要更改内容。
如果这能解决您的问题或需要任何其他帮助,请告诉我。
我找到了答案 。
我不得不为分隔符创建一个空的视图模型和一个从 ItemContainerTemplateSelector
派生到 return 的 class 属于类型的 DataTemplate
菜单项("MenuItemViewModel" 或 "SeparatorViewModel")。
链接的文章应该是不言自明的。
我希望能够在我的视图模型中将 ContextMenu
的 ItemsSource
绑定到 Collection
,在 [= 中显示 Separator
s 14=],ItemsSource
必须是分层的(层次结构的每一层看起来都一样)。
在 ContextMenu
中显示菜单项和分隔符,但现在我很难使 ItemsSource
分层。
现在不知道怎么回事,能不能指教一下?
这是我的代码(简化为简短,但有效):
MenuItemViewModel.vb
Public Class MenuItemViewModel
Implements ICommand
Public Event CanExecuteChanged As EventHandler Implements ICommand.CanExecuteChanged
Public Property IsSeparator As Boolean
Public Property Caption As String
Private ReadOnly _subItems As List(Of MenuItemViewModel)
Public Sub New(createItems As Boolean, level As Byte)
_subItems = New List(Of MenuItemViewModel)
If createItems Then
_subItems.Add(New MenuItemViewModel(level < 4, level + 1) With {.Caption = "SubItem 1"})
_subItems.Add(New MenuItemViewModel(False, level + 1) With {.IsSeparator = True, .Caption = "SubSep 1"})
_subItems.Add(New MenuItemViewModel(level < 4, level + 1) With {.Caption = "SubItem 2"})
End If
End Sub
Public ReadOnly Property SubItems As List(Of MenuItemViewModel)
Get
Return _subItems
End Get
End Property
Public ReadOnly Property Command As ICommand
Get
Return Me
End Get
End Property
Public Sub Execute(ByVal parameter As Object) Implements ICommand.Execute
MessageBox.Show(Me.Caption)
End Sub
Public Function CanExecute(ByVal parameter As Object) As Boolean Implements ICommand.CanExecute
Return True
End Function
End Class
每个级别上每个菜单项的视图模型都有一个 Caption
显示在上下文菜单中,一个 IsSeparator
标志指示它是分隔符还是功能菜单项,一个 Command
作为功能菜单项时要绑定,当然还有 SubItems
collection 包含功能菜单项和分隔符到某个层次结构级别。
MainViewModel.vb
Public Class MainViewModel
Private ReadOnly _items As List(Of MenuItemViewModel)
Public Sub New()
_items = New List(Of MenuItemViewModel)
_items.Add(New MenuItemViewModel(True, 0) With {.Caption = "Item 1"})
_items.Add(New MenuItemViewModel(False, 0) With {.IsSeparator = True, .Caption = "Sep 1"})
_items.Add(New MenuItemViewModel(True, 0) With {.Caption = "Item 2"})
_items.Add(New MenuItemViewModel(True, 0) With {.Caption = "Item 3"})
_items.Add(New MenuItemViewModel(False, 0) With {.IsSeparator = True, .Caption = "Sep 2"})
_items.Add(New MenuItemViewModel(True, 0) With {.Caption = "Item 4"})
End Sub
Public ReadOnly Property Items As List(Of MenuItemViewModel)
Get
Return _items
End Get
End Property
End Class
主视图模型只有 Items
collection 包含功能菜单项和分隔符。
MainWindow.xaml
<Window x:Class="MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfApp3"
mc:Ignorable="d"
d:DataContext="{d:DesignInstance local:MainViewModel, IsDesignTimeCreatable=True}"
Title="MainWindow" Height="450" Width="800">
<Window.DataContext>
<local:MainViewModel />
</Window.DataContext>
<Window.Resources>
<ControlTemplate x:Key="mist" TargetType="{x:Type MenuItem}">
<Separator />
</ControlTemplate>
<ControlTemplate x:Key="mict" TargetType="{x:Type MenuItem}">
<MenuItem Header="{Binding Caption}" Command="{Binding Command}" ItemsSource="{Binding SubItems}" />
</ControlTemplate>
<Style x:Key="cmics" TargetType="{x:Type MenuItem}">
<Setter Property="Template" Value="{StaticResource mict}" />
<Style.Triggers>
<DataTrigger Binding="{Binding IsSeparator}" Value="True">
<Setter Property="Template" Value="{StaticResource mist}" />
</DataTrigger>
</Style.Triggers>
</Style>
</Window.Resources>
<Grid>
<TextBox HorizontalAlignment="Center" VerticalAlignment="Center" Text="Right click me">
<TextBox.ContextMenu>
<ContextMenu ItemsSource="{Binding Items}" ItemContainerStyle="{StaticResource cmics}">
<ContextMenu.ItemTemplate>
<HierarchicalDataTemplate DataType="{x:Type local:MenuItemViewModel}" ItemsSource="{Binding SubItems}" />
</ContextMenu.ItemTemplate>
</ContextMenu>
</TextBox.ContextMenu>
</TextBox>
</Grid>
</Window>
window资源包含两个ControlTemplate
s "mist"和"mict"以及一个Style
"cmics"在这两个ControlTemplate
s 取决于 IsSeparator
标志的值。只要 ItemsSource
不是分层的(参见
如果我的 Style
"cmics" 仅附加到 ContextMenu
的 ItemContainerStyle
(如我的示例代码中所示),那么它看起来像这样:
第一层有效,但其他层无效。将我的 Style
"cmics" 附加到 HierarchicalDataTemplate
的 ItemContainerStyle
时也不会改变。
如果我只将我的 Style
"cmics" 附加到 HierarchicalDataTemplate
那么它看起来像这样:
第一级不显示标题和分隔符,第二级有效,其他级别无效。
那么,我怎样才能说服 ContextMenu
使用我的 Style
"cmics" 作为每个层级的 ItemContainerStyle
?
我刚刚在 Xaml 部分对您的 (TextBox) 进行了一些更改。看看这个,
<TextBox HorizontalAlignment="Center" VerticalAlignment="Center" Text="Right click me">
<TextBox.Resources>
<HierarchicalDataTemplate DataType="{x:Type local:MenuItemViewModel}" ItemsSource="{Binding SubItems}">
<Button Content="{Binding Caption}" Command="{Binding Command}" Background="Red"/>
</HierarchicalDataTemplate>
</TextBox.Resources>
<TextBox.ContextMenu>
<ContextMenu ItemsSource="{Binding Items}" ItemContainerStyle="{StaticResource cmics}"/>
</TextBox.ContextMenu>
</TextBox>
基本上我已经删除了您的 ContexttMenu ItemTemplate 并在 TextBox.Resources 下添加了一个分层数据模板。
在数据模板中,我刚刚添加了一个单选按钮。您可以根据需要更改内容。
如果这能解决您的问题或需要任何其他帮助,请告诉我。
我找到了答案
我不得不为分隔符创建一个空的视图模型和一个从 ItemContainerTemplateSelector
派生到 return 的 class 属于类型的 DataTemplate
菜单项("MenuItemViewModel" 或 "SeparatorViewModel")。
链接的文章应该是不言自明的。