我如何正确地定位上下文菜单,当我右键单击一个DataGridView's列标题

本文关键字:DataGridView 一个 标题 定位 正确地 上下文 菜单 右键 单击 | 更新日期: 2023-09-27 18:15:10

我想扩展DataGridView以添加第二个ContextMenu,以选择哪些列在网格中可见。新的ContextMenu将在右击列标题时显示。

我很难得到正确的水平位置来显示上下文菜单。我怎样才能纠正这个问题呢?

public partial class Form1 : Form
{
    DataGridView dataGrid;
    ContextMenuStrip contextMenuStrip;        
    public Form1()
    {
        InitializeComponent();
        dataGrid = new DataGridView();
        Controls.Add(dataGrid);
        dataGrid.Dock = System.Windows.Forms.DockStyle.Fill;
        dataGrid.ColumnHeaderMouseClick += new System.Windows.Forms.DataGridViewCellMouseEventHandler(ColumnHeaderMouseClick);
        dataGrid.DataSource = new Dictionary<string, string>().ToList();
        contextMenuStrip = new ContextMenuStrip();
        contextMenuStrip.Items.Add("foo");
        contextMenuStrip.Items.Add("bar");
    }
    private void ColumnHeaderMouseClick(object sender, DataGridViewCellMouseEventArgs e)
    {
        if (e.Button == System.Windows.Forms.MouseButtons.Right)
        {
            contextMenuStrip.Show(PointToScreen(e.Location));
        }
    }
}

我如何正确地定位上下文菜单,当我右键单击一个DataGridView's列标题

这是一个非常简单的方法,使上下文菜单出现在你右键单击它的地方。

处理事件ColumnHeaderMouseClick

private void grid_ColumnHeaderMouseClick(object sender, DataGridViewCellMouseEventArgs e) 
{
  if (e.Button == System.Windows.Forms.MouseButtons.Right)
    contextMenuHeader.Show(Cursor.Position);
}

contextMenuHeader是一个ContextMenuStrip,可以在Designer视图中定义,也可以在运行时定义。

要获取鼠标光标的坐标,可以这样做。

ContextMenu.Show(this, myDataGridView.PointToClient(Cursor.Position)); 

您是否尝试过使用接受控件和位置的Show重载?

例如:

contextMenuStrip.Show(dataGrid, e.Location);

编辑:完整的例子

public partial class Form1 : Form
{
    DataGridView dataGrid;
    ContextMenuStrip contextMenuStrip;        
    public Form1()
    {
        InitializeComponent();
        dataGrid = new DataGridView();
        Controls.Add(dataGrid);
        dataGrid.Dock = System.Windows.Forms.DockStyle.Fill;
        dataGrid.MouseDown += MouseDown;
        dataGrid.DataSource = new Dictionary<string, string>().ToList();
        contextMenuStrip = new ContextMenuStrip();
        contextMenuStrip.Items.Add("foo");
        contextMenuStrip.Items.Add("bar");
    }
    private void MouseDown(object sender, MouseEventArgs e)
    {
        if (e.Button == System.Windows.Forms.MouseButtons.Right)
        {
            if (dataGrid.HitTest(e.X, e.Y).Type == DataGridViewHitTestType.ColumnHeader)
            {
                contextMenuStrip.Show(dataGrid, e.Location);
            }
        }
    }
}

e.Location不显示弹出式菜单在正确的坐标,而只是使用MousePosition属性如下:

ContextMenuStrip.Show(MousePosition)

,或者影响

ContextMenuStrip.Show(Control.MousePosition)

返回的位置是相对于单元格的。所以我们要加上偏移量

    private void grdView_CellMouseClick(object sender, DataGridViewCellMouseEventArgs e)
    {
        if (e.Button == MouseButtons.Right)
        {
            var pos = ((DataGridView)sender).GetCellDisplayRectangle(e.ColumnIndex, 
            e.RowIndex, false).Location;
            pos.X += e.X;
            pos.Y += e.Y;
            contextMenuStrip.Show((DataGridView)sender,pos);
        }
    }

终于成功了。

ContextMenu.Show(myDataGridView, myDataGridView.PointToClient(Cursor.Position)); 

两次调用Show将获得光标的确切位置。这个答案是为那些无法得到以上所有答案的结果的人准备的。

private void MouseDown(object sender, MouseEventArgs e)
{
    if (e.Button == System.Windows.Forms.MouseButtons.Right)
    {
        contextMenuStrip.Show(dataGrid, e.Location));
        contextMenuStrip.Show(Cursor.Position);
    }
}

我出错的地方是DataGridViewCellMouseEventArgs返回鼠标在列标题中单击的位置/x,y。相反,我需要在网格的MouseDown事件中使用HitTest来命中列标头,然后将命中的位置从网格坐标转换为屏幕坐标。

public partial class Form1 : Form
{
    DataGridView dataGrid;
    ContextMenuStrip contextMenuStrip;        
    public Form1()
    {
        InitializeComponent();
        dataGrid = new DataGridView();
        Controls.Add(dataGrid);
        dataGrid.Dock = System.Windows.Forms.DockStyle.Fill;
        //dataGrid.ColumnHeaderMouseClick += ColumnHeaderMouseClick;
        dataGrid.MouseDown += MouseDown;
        dataGrid.DataSource = new Dictionary<string, string>().ToList();
        contextMenuStrip = new ContextMenuStrip();
        contextMenuStrip.Items.Add("foo");
        contextMenuStrip.Items.Add("bar");
    }
    private void ColumnHeaderMouseClick(object sender, DataGridViewCellMouseEventArgs e)
    {
        if (e.Button == System.Windows.Forms.MouseButtons.Right)
        {
            contextMenuStrip.Show(PointToScreen(e.Location));
        }
    }
    private void MouseDown(object sender, MouseEventArgs e)
    {
        if (e.Button == System.Windows.Forms.MouseButtons.Right)
        {
            if (dataGrid.HitTest(e.X, e.Y).Type == DataGridViewHitTestType.ColumnHeader)
            {
                contextMenuStrip.Show(dataGrid.PointToScreen(e.Location));
            }
        }
    }
}

你差一点就对了。您只需要将PointToScreen方法应用于调用控件:

private void ColumnHeaderMouseClick(object sender, DataGridViewCellMouseEventArgs e)
{
    if (e.Button == System.Windows.Forms.MouseButtons.Right)
    {
        contextMenuStrip.Show(((DataGridView)sender).PointToScreen(e.Location));
    }
}

我认为这是最优雅的解决方案,因为它只使用ColumnHeaderMouseClick参数而不是Cursor.Position

相关文章: