【问题标题】:Getting ContextMenu on DataGrid in WPF/MVVM在 WPF/MVVM 中的 DataGrid 上获取 ContextMenu
【发布时间】:2017-05-12 11:02:25
【问题描述】:

更新!!! 我发现当我将它放入 DataGrid 时,即使这也不起作用:

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

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

我正在努力在我的 WPF/MVVM 项目中的 DataGrid 上获取上下文菜单。我在下面发布了 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 部分。我搜索了 StackOverflow 并尝试了各种想法来显示上下文菜单。没有。此特定示例使用“标签”。我还尝试将上下文菜单放在资源部分中:

<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>

执行此操作的“标记”方式不会导致绑定错误。你只需右键单击,什么都没有!其他方式(我可以发布)在输出窗口中给出了绑定错误。可能值得注意的是,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>

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

-戴夫

【问题讨论】:

  • 在简化形式中,您的代码对我有用。尝试将 Header 而非命令绑定到 VM 中的某个属性,看看它是否有价值。可能是,你有死锁吗?你使用 lock()..
  • 有趣。你能告诉(或分享)简化代码是什么吗?你必须拿出什么?感谢您阅读我的问题!
  • Rekshino,我删除了 Command 绑定,并从 Header(ContextMenu 中的 MenuItem 到 ViewModel 属性)中放入了绑定。不走运。
  • 您是否尝试过检查您的 MenuItem 有哪些 ViewModel(如果您在绑定中放置转换器并在其中设置制动点,您可以做到这一点)?我已将用户控件直接放在窗口中(而不是 TabItem),并从窗口的静态资源中设置 DataContext。用户控件几乎相同,MenuItem 的绑定相同。你说,所选项目的绑定有效?!嗯..奇怪..
  • 谢谢。看我更新。我认为有些东西甚至阻止了上下文菜单被调用。也许右键在其他地方被拦截了?

标签: wpf mvvm binding datagrid contextmenu


【解决方案1】:

所以,正如我已经在 cmets 中编写的那样,绑定有效。我曾想过,你会调用 ContextMenu,但你写的是你没有。 MouseRightButtonClick 可以被几个东西拦截:

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

保持一致,您会发现问题的原因。

【讨论】:

  • 谢谢 Rekshino。就是这样。在 ParentWindow (几级)是 private void OnPreviewMouseRightButtonDown(object sender, MouseButtonEventArgs e) 或在 XAML PreviewMouseRightButtonDown="OnPreviewMouseRightButtonDown"
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-12-06
  • 2020-03-09
  • 1970-01-01
  • 2016-03-12
  • 1970-01-01
  • 2013-03-21
相关资源
最近更新 更多