假设我有一个ListBox
,上面绑定了一些项目。
ListBox
本身有一些ContextMenuItem
,它们与所选项目无关(例如“添加”或“清除”或“刷新”)。
ListBox
中的每个项目都有ContextMenuItem
个依赖于所选项目的内容(例如“删除”,“编辑”或“显示详细信息”)。
当然,即使我右键单击ListBox
的某个项目,我也希望能够“添加”一个项目或“刷新”列表。
所以,这是我的问题。
是否可以通过某种方式将ContextMenuItem
的{{1}}添加到每个项目的ListBox
,以便用户在右键单击某个项目并ContextMenu
中只有较少的ContextMenuItem
个,以防他碰巧右键单击列表中不属于某项的部分?
如果必须(在设计时)将ListBox
的这些ContextMenuItem
手动添加到项目的ListBox
,如何绑定ContextMenu
的项目ContextMenuItem
的视图模型中的ListBox
到Command
的项?
第二部分之后的谷歌搜索仅带来了一些解决方案,包括以某种方式将列表的ListBox
放在项目的DataContext
中,然后绑定到Tag
s PlacementTarget
。但是误用Tag
似乎有点麻烦,我以为离开VB6时就误用了Tag
。
这里有一些代码:
MainViewModel.vb:
Tag
ItemViewModel.vb:
Imports System.Collections.ObjectModel
Imports System.ComponentModel
Public Class MainViewModel
Implements INotifyPropertyChanged
Public Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged
Private _items As ObservableCollection(Of ItemViewModel)
Private _refreshCommand As ICommand = New RelayCommand(AddressOf Me.Refresh)
Public Sub New()
_items = New ObservableCollection(Of ItemViewModel)
_items.Add(New ItemViewModel With {.Text = "Monica"})
_items.Add(New ItemViewModel With {.Text = "Ross"})
_items.Add(New ItemViewModel With {.Text = "Rachel"})
_items.Add(New ItemViewModel With {.Text = "Joey"})
_items.Add(New ItemViewModel With {.Text = "Phoebe"})
_items.Add(New ItemViewModel With {.Text = "Chandler"})
End Sub
Private Sub OnPropertyChanged(propertyName As String)
RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(propertyName))
End Sub
Public ReadOnly Property Items As ObservableCollection(Of ItemViewModel)
Get
Return _items
End Get
End Property
Public ReadOnly Property RefreshCommand As ICommand
Get
Return _refreshCommand
End Get
End Property
Private Sub Refresh()
MsgBox("List Refresh")
End Sub
End Class
MainWindow.xaml:
Imports System.ComponentModel
Public Class ItemViewModel
Implements INotifyPropertyChanged
Public Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged
Private _text As String
Private _showCommand As ICommand = New RelayCommand(AddressOf Me.Show)
Private Sub OnPropertyChanged(propertyName As String)
RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(propertyName))
End Sub
Public Property Text As String
Get
Return _text
End Get
Set(value As String)
_text = value
Me.OnPropertyChanged("Text")
End Set
End Property
Public ReadOnly Property ShowCommand As ICommand
Get
Return _showCommand
End Get
End Property
Private Sub Show()
MsgBox("Item Show")
End Sub
End Class
在这段代码中,我实际上将hacky <Window x:Class="MainWindow"
x:Name="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:_ContextMenuCommandTest"
mc:Ignorable="d"
d:DataContext="{d:DesignInstance Type=local:MainViewModel, IsDesignTimeCreatable=True}"
Title="MainWindow" Height="450" Width="800">
<Window.DataContext>
<local:MainViewModel />
</Window.DataContext>
<ListBox ItemsSource="{Binding Items}">
<ListBox.ContextMenu>
<ContextMenu>
<MenuItem x:Name="mnuListRefresh" Header="List Refresh" Command="{Binding RefreshCommand}" />
</ContextMenu>
</ListBox.ContextMenu>
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Text}" Tag="{Binding DataContext, ElementName=MainWindow}">
<TextBlock.ContextMenu>
<ContextMenu>
<MenuItem x:Name="mnuItemShow" Header="Item Show" Command="{Binding ShowCommand}" />
<Separator />
<MenuItem x:Name="mnuItemRefresh" Header="Item Refresh" Command="{Binding PlacementTarget.Tag.RefreshCommand, RelativeSource={RelativeSource AncestorType=ContextMenu}}" />
</ContextMenu>
</TextBlock.ContextMenu>
</TextBlock>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Window>
绑定仅用于测试目的。我希望找到其他不涉及Tag
的解决方案。
答案 0 :(得分:1)
使用Tag
属性是获得对DataContext
未继承的ContextMenu
的引用的一种技巧。您不能像这样直接在窗口中使用RelativeSource
,因为ContextMenu
驻留在其自己的可视树中,因此既不是父Window
也不是{{1}的后代}:
ListBox
您的解决方案非常好。如果由于某种原因不想使用Command="{Binding DataContext.RefreshCommand, RelativeSource={RelativeSource AncestorType=Window}}"
属性,则可以创建自己的attached property并设置并绑定到该属性。