简体   繁体   English

WPF DataGrid:单击鼠标左键显示动态上下文菜单

[英]WPF DataGrid: Show dynamic context menu on left click

From time to time I'd like to show a context menu when clicking on a Cell in a DataGrid. 当我在DataGrid中单击一个单元格时,我会不时显示一个上下文菜单。 I create the ContextMenu programmatically and then display it with ContextMenu.IsOpen=true. 我以编程方式创建ContextMenu,然后使用ContextMenu.IsOpen = true显示它。 In the example below, it works when clicking inside the Grid panel, but it doesn't, wenn clicking on a cell(UIElement inside a cell) of the DataGrid. 在下面的示例中,当在“网格”面板内单击时,它可以工作,但不能单击DataGrid的单元格(单元格内的UIElement)。

That is the difference? 那有区别吗? What do I need to do to make it work on a DataGridCell as well? 我需要怎么做才能使其也可以在DataGridCell上工作?

Here comes a demo version, first XAML and below the code behind. 这是一个演示版本,第一个是XAML,后面是代码。

    <Window x:Class="WpfApplication7_delete_me.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:WpfApplication7_delete_me"
            mc:Ignorable="d"
            Title="MainWindow" Height="350" Width="525">
        <Grid MouseDown="Grid_MouseDown" Background="Beige">

        <DataGrid x:Name="dataGrid" HorizontalAlignment="Left"  VerticalAlignment="Top" AutoGenerateColumns="False">

          <DataGrid.Columns>
            <DataGridTemplateColumn Header="Name">
              <DataGridTemplateColumn.CellTemplate>
                <DataTemplate>
                  <TextBlock Text="{Binding Name}" MouseDown="TextBlock_MouseDown"  />
                </DataTemplate>
              </DataGridTemplateColumn.CellTemplate>
            </DataGridTemplateColumn>
          </DataGrid.Columns>

        </DataGrid>

      </Grid>
    </Window>

Here comes the code behind: 代码如下:

    using System.Collections.Generic;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Input;

    namespace WpfApplication7_delete_me {
      /// <summary>
      /// Interaction logic for MainWindow.xaml
      /// </summary>
      public partial class MainWindow : Window {
        public MainWindow() {
          InitializeComponent();

          Person p1 = new Person(); p1.Name = "abc";
          Person p2 = new Person(); p2.Name = "1q23";
          List<Person> l = new List<Person>() { p1, p2 };
          dataGrid.ItemsSource = l;
        }

        private void Grid_MouseDown(object sender, MouseButtonEventArgs e) {
          ContextMenu cm = new ContextMenu();
          MenuItem mi = new MenuItem();
          mi.Header = "hallo";
          cm.Items.Add(mi);
          cm.IsOpen = true;
        }

        private void TextBlock_MouseDown(object sender, MouseButtonEventArgs e) {
          ContextMenu cm = new ContextMenu();
          MenuItem mi = new MenuItem();
          mi.Header = "hallo";
          cm.Items.Add(mi);
          cm.IsOpen = true;
        }
      }

      class Person {
        public string Name { get; set; }
      }
    }

After some time I found two solutions: 一段时间后,我找到了两种解决方案:

1) It's working when using PreviewMouseDown instead of MouseDown. 1)当使用PreviewMouseDown而不是MouseDown时,它可以工作。

2) Using: Dispatcher.BeginInvoke(new Action(() => { c.IsOpen = true; }), null); 2)使用: Dispatcher.BeginInvoke(new Action(() => { c.IsOpen = true; }), null);

But why is setting IsOpen inside MouseDown event not working? 但是,为什么在MouseDown事件中设置IsOpen无效?

About the first solution. 关于第一个解决方案。

there are some issues while using the MouseDown event because the event might be marked as handled by other controls. 使用MouseDown事件时会出现一些问题,因为该事件可能被标记为由其他控件处理。 the PreviewMouseDown is a preview event and it is not marked, hence when you use it , it comes all the way down from the root element and controls to your implementation. PreviewMouseDown是一个预览事件,没有标记,因此在使用它时,它会从根元素和控件一直到实现。

for more information you can read here: MSDN UIElement.MouseDown Event 有关更多信息,您可以在这里阅读: MSDN UIElement.MouseDown事件

Define an empty ContextMenu for the DataGrid . DataGrid定义一个空的ContextMenu

<DataGrid.ContextMenu>
    <ContextMenu x:Name="CtxMenu">                    
    </ContextMenu>
</DataGrid.ContextMenu>

And handle ContextMenuOpening event : 并处理ContextMenuOpening事件:

private void DataGrid_ContextMenuOpening_1(object sender, ContextMenuEventArgs e)
    {
        ContextMenu ctxmenu = (sender as DataGrid).ContextMenu;
        // suppress ContextMenu if empty
        e.Handled  = ctxmenu.Items.Count == 0;            
    }

private void TextBlock_MouseDown(object sender, MouseButtonEventArgs e)
    {
        ContextMenu ctxmenu = Dgrd.ContextMenu;

        MenuItem mi = new MenuItem();
        mi.Header = "hallo";
        ctxmenu.Items.Add(mi);
    }

Better would be to handle PreviewMouseDown event at DataGrid level like this <DataGrid ... DataGridCell.PreviewMouseDown="DataGridCell_MouseDown" ... /> . 最好是像这样<DataGrid ... DataGridCell.PreviewMouseDown="DataGridCell_MouseDown" ... />这样在DataGrid级别处理PreviewMouseDown event

That way you get ContextMenu as ContextMenu ctxmenu = (sender as DataGrid).ContextMenu; 这样,您将ContextMenu作为ContextMenu ctxmenu = (sender as DataGrid).ContextMenu; . Also its good to use preview events if you want to do some initial preparation. 如果您想做一些初步的准备,最好使用预览事件。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM