加入两个上下文菜单或将上下文菜单项绑定到父视图模型的命令

时间:2019-02-11 14:58:12

标签: wpf

假设我有一个ListBox,上面绑定了一些项目。

ListBox本身有一些ContextMenuItem,它们与所选项目无关(例如“添加”或“清除”或“刷新”)。

ListBox中的每个项目都有ContextMenuItem个依赖于所选项目的内容(例如“删除”,“编辑”或“显示详细信息”)。

当然,即使我右键单击ListBox的某个项目,我也希望能够“添加”一个项目或“刷新”列表。

所以,这是我的问题。

是否可以通过某种方式将ContextMenuItem的{​​{1}}添加到每个项目的ListBox,以便用户在右键单击某个项目并ContextMenu中只有较少的ContextMenuItem个,以防他碰巧右键单击列表中不属于某项的部分?

如果必须(在设计时)将ListBox的这些ContextMenuItem手动添加到项目的ListBox,如何绑定ContextMenu的项目ContextMenuItem的视图模型中的ListBoxCommand的项?

第二部分之后的谷歌搜索仅带来了一些解决方案,包括以某种方式将列表的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的解决方案。

1 个答案:

答案 0 :(得分:1)

使用Tag属性是获得对DataContext未继承的ContextMenu的引用的一种技巧。您不能像这样直接在窗口中使用RelativeSource,因为ContextMenu驻留在其自己的可视树中,因此既不是父Window也不是{{1}的后代}:

ListBox

您的解决方案非常好。如果由于某种原因不想使用Command="{Binding DataContext.RefreshCommand, RelativeSource={RelativeSource AncestorType=Window}}" 属性,则可以创建自己的attached property并设置并绑定到该属性。