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

时间:2016-10-09 10:37:25

标签: c# wpf datagrid contextmenu

有时我想在DataGrid中单击一个Cell时显示一个上下文菜单。 我以编程方式创建ContextMenu,然后使用ContextMenu.IsOpen = true显示它。在下面的示例中,它在“网格”面板内部单击时起作用,但它不会,单击DataGrid中的单元格(单元格内的UIElement)。

那有什么区别?我还需要做些什么才能使它在DataGridCell上运行?

这是一个演示版本,第一个是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>

后面是代码:

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

一段时间后,我找到了两个解决方案:

1)使用PreviewMouseDown而不是MouseDown时它正在工作。

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

但为什么在MouseDown事件中设置IsOpen不起作用?

2 个答案:

答案 0 :(得分:0)

关于第一个解决方案。

使用MouseDown事件时存在一些问题,因为事件可能被标记为由其他控件处理。 PreviewMouseDown是一个预览事件,它没有被标记,因此当你使用它时,它会从根元素和控件一直到你的实现。

有关详细信息,请参阅此处:MSDN UIElement.MouseDown Event

答案 1 :(得分:0)

ContextMenu定义一个空的DataGrid

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

处理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);
    }

最好在PreviewMouseDown event级别DataGrid处理<DataGrid ... DataGridCell.PreviewMouseDown="DataGridCell_MouseDown" ... />

通过这种方式,ContextMenuContextMenu ctxmenu = (sender as DataGrid).ContextMenu;。如果您想进行初步准备,也可以使用预览事件。