为啥我的 WinForms 上下文菜单没有出现在鼠标所在的位置?

Posted

技术标签:

【中文标题】为啥我的 WinForms 上下文菜单没有出现在鼠标所在的位置?【英文标题】:Why does my WinForms context menu not appear where the mouse is?为什么我的 WinForms 上下文菜单没有出现在鼠标所在的位置? 【发布时间】:2011-02-23 16:39:05 【问题描述】:

在我的应用程序中,我有一个DataGridView,用于配置一些选项。这个想法是您可以在第一列中输入您想要的任何文本,但是如果您右键单击它将为您提供明确支持的值。我需要这是一个文本框而不是下拉列表,因为我需要支持编辑无效(或旧)配置。

我想要的是用户在字段名称列中单击鼠标右键,并拥有一个上下文菜单,该菜单根据这是什么类型的配置而有效。因此,我编写了以下事件

    private void grvFieldData_CellMouseClick(object sender, DataGridViewCellMouseEventArgs e)
    
        // If this is a right click on the Field name column, create a context menu 
        //   with recognized options for that field
        if (e.Button == MouseButtons.Right && grvFieldData.Columns[e.ColumnIndex].Name == "clmFieldName")
        
            ContextMenu menu = new ContextMenu();

            if (_supportedDataGrids.ContainsKey((cmbDataGrid.SelectedItem as DataGridFieldList).GridName))
            
                // Loop through all the fields and add them to the context menu
                List<string> fields = _supportedDataGrids[((cmbDataGrid.SelectedItem as DataGridFieldList).GridName)];
                fields.Sort();

                foreach (string field in fields)
                    menu.MenuItems.Add(new MenuItem(field));

                // Make sure there is at least one field before displaying the context menu
                if (menu.MenuItems.Count > 0)
                    menu.Show(this, e.Location, LeftRightAlignment.Right);
            
        
    

这“正确”工作,但上下文菜单出现在表单的顶部,而不是鼠标指针所在的位置。如果我将 Show() 调用更改为使用 DataGridView 而不是表单,我会遇到同样的问题,但它会出现在网格的左上角,而不是鼠标所在的位置。

奇怪的是,如果我将此事件更改为 MouseClick 事件(而不是 CellMouseclick 事件),一切正常,并且上下文菜单准确出现在鼠标指针所在的位置。这个选项的问题是用户可能没有右键单击当前选择的单元格,这意味着当他们单击菜单项时,所选单元格将被更改,而不是他们右键单击的单元格。

有人知道为什么使用CellMouseClick 创建的上下文菜单没有显示在正确的位置吗?

【问题讨论】:

【参考方案1】:
 menu.Show(this, e.Location, LeftRightAlignment.Right);

第二个参数是鼠标位置,相对于单元格的左上角。按照编程,您使该偏移量相对于表单 this,这将使菜单出现在表单的左上角。使用 DGV 作为第一个参数也不起作用,现在它位于网格的左上角。

有几种方法可以解决这个问题,但这是最简单的方法:

 Point pos = this.PointToClient(Cursor.Position);
 menu.Show(this, pos, LeftRightAlignment.Right);

你可以任意替换this为grvFieldData。

【讨论】:

嘿,我的答案会稍微复杂一点,加上一堆 X 和 Y 坐标。我希望我几周前知道 Cursor.Position! +1 为简单起见! 太棒了,这就像一个魅力! Cursor.Position 是我错过的!【参考方案2】:

在datagridview鼠标点击事件中:

if e.button= mousebutton.right 

   contextmenu1.Show(MousePosition);

【讨论】:

【参考方案3】:

尝试使用PointToClient 获得合适的位置

【讨论】:

不。 PointToClient(e.Location) 将上下文菜单置于屏幕顶部,PointToScreen(e.Location) 出于某种原因将上下文菜单置于窗体中间。 grvFieldData.PointToClient(e.Location) 同样的问题,即使我将this 调用更改为grvFieldData :-/【参考方案4】:

它没有显示在正确的位置,因为 e.Location 是相对于父对象左上角的位置,在这种情况下是单元格本身。位置属性始终与其容器相关。

要获取鼠标光标相对于表单左上角的位置,可以使用

this.PointToClient(Cursor.Position);

【讨论】:

如何获取被点击单元格的位置(单元格不是控件,因此我无法将其传递给Show())或鼠标的屏幕位置?【参考方案5】:

我已经解决了这个问题......人们可能会觉得这个方法很奇怪,但它工作正常!) 如果我们想在 datagridview 单元格中按下鼠标右键时看到上下文菜单,并且就在那里,而不是在屏幕中间或其他地方,我们需要:

制作一些变量

int x=0;
int y=0;

为 datagridview1 创建一个“MouseMove”事件,例如:

private void dataGridView1_MouseMove(object sender, MouseEventArgs e)

   x = e.X;
   y = e.Y;

private void dataGridView1_CellMouseClick(object sender, DataGridViewCellMouseEventArgs e)

   if (e.Button == System.Windows.Forms.MouseButtons.Right)
   
      contextMenuStrip1.Show(dataGridView1, x,y);
   

欢迎您

【讨论】:

这个解决方案有不必要的开销,因为用户将鼠标移到网格上(我认为这种情况经常发生)它会不断地写入内存。该事件已包含该位置。

以上是关于为啥我的 WinForms 上下文菜单没有出现在鼠标所在的位置?的主要内容,如果未能解决你的问题,请参考以下文章

在 WinForms 中为上下文菜单动态选择菜单项的正确方法是啥?

为啥我的验证事件没有在 C# 中触发?

为啥我的隐式 ContextMenu 样式不会覆盖 TextBox 上下文菜单样式?

为啥我的 Delphi 2006 应用程序的任务栏按钮上下文菜单不完整?

上下文菜单父级?

为啥源代码管理下拉菜单中没有我的项目名称?