How do I correctly position a Context Menu when I right click a DataGridView's column header?

前端 未结 9 994
名媛妹妹
名媛妹妹 2020-12-29 02:58

I would like to extended DataGridView to add a second ContextMenu which to select what columns are visible in the gird. The new ContextMenu will be displayed on right click

相关标签:
9条回答
  • 2020-12-29 03:18

    Have you tried using the Show overload that accepts a control and a position?

    For example:

    contextMenuStrip.Show(dataGrid, e.Location);
    

    Edit: Full example

    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);
                }
            }
        }
    }
    
    0 讨论(0)
  • 2020-12-29 03:18

    Where I was going wrong was that DataGridViewCellMouseEventArgs returns the location/x,y of where the mouse clicked within the column header. Instead I need to use HitTest in the grid's MouseDown event for a hit on the column headers and then convert the position of the hit from the gird co-ordinates to the screen co-ordinates.

    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));
                }
            }
        }
    }
    
    0 讨论(0)
  • 2020-12-29 03:27

    You were nearly right. You just need to the apply the PointToScreen method to the calling control:

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

    I think this is the most elegant solution, because it uses only the ColumnHeaderMouseClick arguments and not Cursor.Position.

    0 讨论(0)
提交回复
热议问题