在 WPF/MVVM 中获取 DataGrid 上的上下文菜单

Getting ContextMenu on DataGrid in WPF/MVVM

更新!!! 我发现当我把它放在 DataGrid 中时,即使这样也不起作用:

<DataGrid.ContextMenu>
            <ContextMenu >
                <MenuItem Header="Add Divider" Click="MenuItem_Click"  />
            </ContextMenu>
        </DataGrid.ContextMenu>

这绝对适用于从头开始设置的虚拟项目。所以我不认为我有绑定问题或数据上下文问题......我没有那么远。我怀疑程序的其他部分正在拦截和处理上下文菜单(或右键单击)。我没有编写原始代码,它是一个庞大的代码库。有人可以告诉我我应该寻找什么吗?什么会阻止 DataGrid 上的 ContextMenu 被调用。请注意,DataGrid 位于 ScrollViewer 和 StackPanel 中,并且整个控件是更大 window 的一部分。事实上,它是众多选项卡中的一个。
-结束更新

我正在努力尝试在我的 WPF/MVVM 项目中的 DataGrid 上获取上下文菜单。我post下面的XAML和ViewModel。我希望能够右键单击一行并在 ViewModel 中调用一个命令,该命令将更改该行的其中一个列条目。

XAML(下面我把我认为是重点的地方都说出来)

<UserControl x:Class="Sears.UserInterface.Views.Technician.Alerts.AlertsLogView"
         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
         xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
         xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
         xmlns:local="clr-namespace:Sears.UserInterface.Views.Technician.Alerts"
         xmlns:converters="clr-namespace:Common.Controls.Converters;assembly=Common.Controls"
         mc:Ignorable="d" 
         d:DesignHeight="800"
         d:DesignWidth="1200">
<UserControl.Resources>
    <BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter" />
    <converters:InverseBooleanToVisibilityConverter x:Key="InverseBooleanToVisibilityConverter" />

</UserControl.Resources>

<StackPanel Orientation="Horizontal">
    <ScrollViewer>
        <DataGrid Width="1000"
                  Margin="0"
                  HorizontalAlignment="Left"
                  AutoGenerateColumns="False"
                  Background="Transparent"
                  DataContext="{Binding}"
                  HeadersVisibility="Column"
                  ItemsSource="{Binding Alerts}"
                  SelectedItem="{Binding SelectedItemProperty, Mode=TwoWay}"
                  RowBackground="Transparent"
                  RowHeight="30"
                  Tag="{Binding DataContext, RelativeSource={RelativeSource AncestorType=UserControl}}"
            >


            **<DataGrid.ContextMenu>
                <ContextMenu DataContext="{Binding PlacementTarget.Tag, RelativeSource={RelativeSource Self}}">

                    <MenuItem Command="{Binding CloseAlertCommand}" Header="Close Alert"/>
                </ContextMenu>
            </DataGrid.ContextMenu>**


            <DataGrid.CellStyle>
                <Style TargetType="{x:Type DataGridCell}">
                    <Setter Property="Foreground" Value="Black" />
                    <Style.Triggers>
                        <Trigger Property="IsSelected" Value="True">
                            <Setter Property="Background" Value="{x:Null}" />
                            <Setter Property="BorderBrush" Value="{x:Null}" />
                        </Trigger>
                    </Style.Triggers>
                </Style>
            </DataGrid.CellStyle>
            <DataGrid.ColumnHeaderStyle>
                <Style TargetType="{x:Type DataGridColumnHeader}">
                    <Setter Property="FontWeight" Value="Bold" />
                </Style>
            </DataGrid.ColumnHeaderStyle>
            <DataGrid.Columns>
                <DataGridTemplateColumn Width="*" Header="Id">
                    <DataGridTemplateColumn.CellTemplate>
                        <DataTemplate>
                            <TextBlock VerticalAlignment="Center"
                                       Padding="5"
                                       Text="{Binding Id}" />
                        </DataTemplate>
                    </DataGridTemplateColumn.CellTemplate>
                </DataGridTemplateColumn>
                <DataGridTemplateColumn Width="*" Header="TimeStamp">
                    <DataGridTemplateColumn.CellTemplate>
                        <DataTemplate>
                            <TextBlock VerticalAlignment="Center"
                                       Padding="5"
                                       Text="{Binding AlertTime}" >

                            </TextBlock>
                        </DataTemplate>
                    </DataGridTemplateColumn.CellTemplate>
                </DataGridTemplateColumn>
                <DataGridTemplateColumn Width="*" Header="Severity">
                    <DataGridTemplateColumn.CellTemplate>
                        <DataTemplate>
                            <TextBlock VerticalAlignment="Center"
                                       Padding="5"
                                       Text="{Binding Severity}" />
                        </DataTemplate>
                    </DataGridTemplateColumn.CellTemplate>
                </DataGridTemplateColumn>
                <DataGridTemplateColumn Width="*" Header="AlertText">
                    <DataGridTemplateColumn.CellTemplate>
                        <DataTemplate>
                            <TextBlock VerticalAlignment="Center"
                                       Padding="5"
                                       Text="{Binding AlertText}" />
                        </DataTemplate>
                    </DataGridTemplateColumn.CellTemplate>
                </DataGridTemplateColumn>
                <DataGridTemplateColumn Width="*" Header="Details">
                    <DataGridTemplateColumn.CellTemplate>
                        <DataTemplate>
                            <TextBlock VerticalAlignment="Center"
                                       Padding="5"
                                       Text="{Binding Details}" />
                        </DataTemplate>
                    </DataGridTemplateColumn.CellTemplate>
                </DataGridTemplateColumn>
                <DataGridTemplateColumn Width="*" Header="Active">
                    <DataGridTemplateColumn.CellTemplate>
                        <DataTemplate>
                            <TextBlock VerticalAlignment="Center"
                                       Padding="5"
                                       Text="{Binding Active}" />
                        </DataTemplate>
                    </DataGridTemplateColumn.CellTemplate>
                </DataGridTemplateColumn>
                <DataGridTemplateColumn Width="*" Header="Acknowledged">
                    <DataGridTemplateColumn.CellTemplate>
                        <DataTemplate>
                            <TextBlock VerticalAlignment="Center"
                                       Padding="5"
                                       Text="{Binding Acknowledged}" />
                        </DataTemplate>
                    </DataGridTemplateColumn.CellTemplate>
                </DataGridTemplateColumn>

                <DataGridTemplateColumn Width="*" Header="Reported">
                    <DataGridTemplateColumn.CellTemplate>
                        <DataTemplate>
                            <TextBlock VerticalAlignment="Center"
                                       Padding="5"
                                       Text="{Binding Reported}" />
                        </DataTemplate>
                    </DataGridTemplateColumn.CellTemplate>
                </DataGridTemplateColumn>

                <DataGridTemplateColumn Width="*" Header="Status">
                    <DataGridTemplateColumn.CellTemplate>
                        <DataTemplate>
                            <TextBlock VerticalAlignment="Center"
                                       Padding="5"
                                       Text="{Binding Status}" />
                        </DataTemplate>
                    </DataGridTemplateColumn.CellTemplate>
                </DataGridTemplateColumn>

                <DataGridTemplateColumn Width="*" Header="Resolution">
                    <DataGridTemplateColumn.CellTemplate>
                        <DataTemplate>
                            <TextBlock VerticalAlignment="Center"
                                       Padding="5"
                                       Text="{Binding Resolution}" />
                        </DataTemplate>
                    </DataGridTemplateColumn.CellTemplate>
                </DataGridTemplateColumn>

                <DataGridTemplateColumn Width="*" Header="Category">
                    <DataGridTemplateColumn.CellTemplate>
                        <DataTemplate>
                            <TextBlock VerticalAlignment="Center"
                                       Padding="5"
                                       Text="{Binding Category}" />
                        </DataTemplate>
                    </DataGridTemplateColumn.CellTemplate>
                </DataGridTemplateColumn>

            </DataGrid.Columns>
        </DataGrid>
    </ScrollViewer>
</StackPanel>

注意 DataGrid.ContextMenu 部分。我搜索了 Whosebug 并尝试了各种想法来显示上下文菜单。没有什么。此特定示例使用 "Tag"。我还尝试将上下文菜单放在资源部分中:

<ContextMenu  x:Key="DataRowContextMenu" DataContext="{Binding DataContext, RelativeSource={RelativeSource AncestorType=DataGrid, Mode=FindAncestor}}">
        <MenuItem x:Name="RowContMenuTransfer"  Header="Close Alert" Command="{Binding DataContext.CloseAlertCommand}" CommandParameter="{Binding}" >
        </MenuItem>
    </ContextMenu>

"Tag" 方法不会导致绑定错误。您只需右键单击,什么也没有!其他方式(我可以 post)在输出中给出了绑定错误 window。可能值得注意的是 DataGrid 位于 ScrollViewer 和 StackPanel 中。

这是视图模型

public class AlertsLogViewModel : ViewModelBase, IAlertsLogViewModel, IDisposable
{
    private IAlertManager _model;
    private readonly object _lock = new object();
    private ICommand _closeAlertCommand;

    public AlertsLogViewModel(IAlertManager model) : base("AlertsLogViewModel")
    {
        Alerts = new ObservableCollection<IAlert>();
        _model = model ?? throw new ArgumentNullException("model");
        _model.AlertStatusUpdated += _model_AlertStatusUpdated;
        UpdateAlerts();
    }

    public IAlert SelectedItemProperty { get; set; }
    public ICommand CloseAlertCommand
    {
        get { return _closeAlertCommand ?? (_closeAlertCommand = new DelegateCommand(CloseAlert)); }
    }

    private void CloseAlert()
    {
        _model.CloseAlert(SelectedItemProperty.SystemErrorGuid);
    }

    public ObservableCollection<IAlert> Alerts { get; private set; }
    private void _model_AlertStatusUpdated(object sender, DataAccess.AlertStatusEventArgs e)
    {
        UpdateAlerts();
    }

    private void UpdateAlerts()
    {

        RunOnDispatcher(() =>
            {
                lock (_lock)
                {
                    var modelAlerts = _model.GetAlerts().OrderBy(p => (int)p.Status).ThenByDescending(p => p.AlertTime);
                    Alerts.Clear();
                    if (modelAlerts != null)
                    {
                        foreach (var alert in modelAlerts)
                        {
                            Alerts.Add(alert);
                        }
                    }

                }
            }
        );
        NotifyPropertyChanged("Alerts");

    }
    public void Dispose()
    {
        if (_model != null)
            _model.AlertStatusUpdated -= _model_AlertStatusUpdated;
    }

    public event PropertyChangedEventHandler PropertyChanged;

    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

注意 CloseAlertCommand 和 SelectedItemProperty。我确实看到 SelectedItemProperty 在我左键单击时被调用,但不是右键单击。从未调用 CloseAlert。

最后,如果它是相关的,让我提一下 AlertsLogView 在 Technicial.xaml 页面内,它在这里绑定到 ViewModel:

<TabItem Header="Alerts" 
                 PreviewMouseDown="OnPreviewMouseDown"
                 PreviewTouchDown="OnPreviewTouchDown">
            <alertlog:AlertsLogView  Margin="5"
                                                DataContext="{Binding AlertsLogViewModel}" />
        </TabItem>

非常感谢任何提示、参考、指点或解决方案!

-戴夫

所以,正如我已经在评论中写的那样,绑定有效。我想过,您会调用 ContextMenu,但您写的是您没有调用。 MouseRightButtonClick 可以被几件事拦截:

  • 网格或其父级的隐式或显式样式(如果显式 - 将其注释掉,如果隐含 - 设置您自己的虚拟样式)
  • 网格或其父项的行为(注释掉)
  • 隐藏代码(检查,网格中是否有事件处理程序或它的父级)

保持一致,您会发现问题的根源。