C# 右键单击 datagridview 的上下文菜单

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1718389/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-06 20:14:13  来源:igfitidea点击:

right click context menu for datagridview

c#winformsdatagridviewcontextmenuright-click

提问by

I have a datagridview in a .NET winform app. I would like to rightclick on a row and have a menu pop up. Then i would like to select things such as copy, validate, etc

我在 .NET winform 应用程序中有一个 datagridview。我想右键单击一行并弹出一个菜单。然后我想选择诸如复制、验证等内容

How do i make A) a menu pop up B) find which row was right clicked. I know i could use selectedIndex but i should be able to right click without changing what is selected? right now i could use selected index but if there is a way to get the data without changing what is selected then that would be useful.

我如何制作 A)弹出菜单 B)找到哪一行被右键单击。我知道我可以使用 selectedIndex 但我应该能够在不更改所选内容的情况下右键单击?现在我可以使用选定的索引,但如果有一种方法可以在不更改所选内容的情况下获取数据,那么这将很有用。

回答by Stuart Helwig

You can use the CellMouseEnter and CellMouseLeave to track the row number that the mouse is currently hovering over.

您可以使用 CellMouseEnter 和 CellMouseLeave 来跟踪鼠标当前悬停在上面的行号。

Then use a ContextMenu object to display you popup menu, customised for the current row.

然后使用 ContextMenu 对象显示为当前行定制的弹出菜单。

Here's a quick and dirty example of what I mean...

这是我的意思的一个快速而肮脏的例子......

private void dataGridView1_MouseClick(object sender, MouseEventArgs e)
{
    if (e.Button == MouseButtons.Right)
    {
        ContextMenu m = new ContextMenu();
        m.MenuItems.Add(new MenuItem("Cut"));
        m.MenuItems.Add(new MenuItem("Copy"));
        m.MenuItems.Add(new MenuItem("Paste"));

        int currentMouseOverRow = dataGridView1.HitTest(e.X,e.Y).RowIndex;

        if (currentMouseOverRow >= 0)
        {
            m.MenuItems.Add(new MenuItem(string.Format("Do something to row {0}", currentMouseOverRow.ToString())));
        }

        m.Show(dataGridView1, new Point(e.X, e.Y));

    }
}

回答by Captain Comic

Simply drag a ContextMenu or ContextMenuStrip component into your form and visually design it, then assign it to the ContextMenu or ContextMenuStrip property of your desired control.

只需将 ContextMenu 或 ContextMenuStrip 组件拖入您的窗体并对其进行可视化设计,然后将其分配给所需控件的 ContextMenu 或 ContextMenuStrip 属性。

回答by Matt

Use the CellMouseDownevent on the DataGridView. From the event handler arguments you can determine which cell was clicked. Using the PointToClient()method on the DataGridView you can determine the relative position of the pointer to the DataGridView, so you can pop up the menu in the correct location.

使用CellMouseDown上的事件DataGridView。从事件处理程序参数中,您可以确定单击了哪个单元格。使用PointToClient()DataGridView 上的方法可以确定指针相对于DataGridView 的位置,从而可以在正确的位置弹出菜单。

(The DataGridViewCellMouseEventparameter just gives you the Xand Yrelative to the cell you clicked, which isn't as easy to use to pop up the context menu.)

(该DataGridViewCellMouseEvent参数只是为您提供了相对于您单击的单元格的XY,这并不容易用于弹出上下文菜单。)

This is the code I used to get the mouse position, then adjust for the position of the DataGridView:

这是我用来获取鼠标位置的代码,然后针对 DataGridView 的位置进行调整:

var relativeMousePosition = DataGridView1.PointToClient(Cursor.Position);
this.ContextMenuStrip1.Show(DataGridView1, relativeMousePosition);

The entire event handler looks like this:

整个事件处理程序如下所示:

private void DataGridView1_CellMouseDown(object sender, DataGridViewCellMouseEventArgs e)
{
    // Ignore if a column or row header is clicked
    if (e.RowIndex != -1 && e.ColumnIndex != -1)
    {
        if (e.Button == MouseButtons.Right)
        {
            DataGridViewCell clickedCell = (sender as DataGridView).Rows[e.RowIndex].Cells[e.ColumnIndex];

            // Here you can do whatever you want with the cell
            this.DataGridView1.CurrentCell = clickedCell;  // Select the clicked cell, for instance

            // Get mouse position relative to the vehicles grid
            var relativeMousePosition = DataGridView1.PointToClient(Cursor.Position);

            // Show the context menu
            this.ContextMenuStrip1.Show(DataGridView1, relativeMousePosition);
        }
    }
}

回答by ActualRandy

  • Put a context menu on your form, name it, set captions etc. using the built-in editor
  • Link it to your grid using the grid property ContextMenuStrip
  • For your grid, create an event to handle CellContextMenuStripNeeded
  • The Event Args e has useful properties e.ColumnIndex, e.RowIndex.
  • 使用内置编辑器在表单上放置上下文菜单、命名、设置标题等
  • 使用 grid 属性将其链接到您的网格 ContextMenuStrip
  • 对于您的网格,创建一个事件来处理 CellContextMenuStripNeeded
  • 事件参数e的化合物具有有用的特性e.ColumnIndexe.RowIndex

I believe that e.RowIndexis what you are asking for.

我相信这e.RowIndex就是你所要求的。

Suggestion: when user causes your event CellContextMenuStripNeededto fire, use e.RowIndexto get data from your grid, such as the ID. Store the ID as the menu event's tag item.

建议:当用户导致您的事件CellContextMenuStripNeeded触发时,用于e.RowIndex从您的网格中获取数据,例如 ID。将 ID 存储为菜单事件的标记项。

Now, when user actually clicks your menu item, use the Sender property to fetch the tag. Use the tag, containing your ID, to perform the action you need.

现在,当用户实际单击您的菜单项时,使用 Sender 属性来获取标签。使用包含您的 ID 的标签来执行您需要的操作。

回答by ShortFuse

While this question is old, the answers aren't proper. Context menus have their own events on DataGridView. There is an event for row context menu and cell context menu.

虽然这个问题很老,但答案并不正确。上下文菜单在 DataGridView 上有自己的事件。行上下文菜单和单元格上下文菜单有一个事件。

The reason for which these answers aren't proper is they do not account for different operation schemes. Accessibility options, remote connections, or Metro/Mono/Web/WPF porting might not work and keyboard shortcuts will down right fail (Shift+F10 or Context Menu key).

这些答案不正确的原因是它们没有考虑不同的操作方案。辅助功能选项、远程连接或 Metro/Mono/Web/WPF 移植可能无法正常工作,键盘快捷键将无法使用(Shift+F10 或上下文菜单键)。

Cell selection on right mouse click has to be handled manually. Showing the context menu does not need to be handled as this is handled by the UI.

必须手动处理鼠标右键单击的单元格选择。不需要处理显示上下文菜单,因为这是由 UI 处理的。

This completely mimics the approach used by Microsoft Excel. If a cell is part of a selected range, the cell selection doesn't change and neither does CurrentCell. If it isn't, the old range is cleared and the cell is selected and becomes CurrentCell.

这完全模仿了 Microsoft Excel 使用的方法。如果单元格是选定范围的一部分,则单元格选择不会更改, 也不会更改CurrentCell。如果不是,则旧范围被清除,单元格被选中并变为CurrentCell

If you are unclear on this, CurrentCellis where the keyboard has focus when you press the arrow keys. Selectedis whether it is part of SelectedCells. The context menu will show on right click as handled by the UI.

如果您对此不清楚,CurrentCell则是按箭头键时键盘的焦点所在。Selected是它是否属于SelectedCells. 右键单击时将显示上下文菜单,由 UI 处理。

private void dgvAccount_CellMouseDown(object sender, DataGridViewCellMouseEventArgs e)
{
    if (e.ColumnIndex != -1 && e.RowIndex != -1 && e.Button == System.Windows.Forms.MouseButtons.Right)
    {
        DataGridViewCell c = (sender as DataGridView)[e.ColumnIndex, e.RowIndex];
        if (!c.Selected)
        {
            c.DataGridView.ClearSelection();
            c.DataGridView.CurrentCell = c;
            c.Selected = true;
        }
    }
}

Keyboard shortcuts do not show the context menu by default, so we have to add them in.

默认情况下,键盘快捷键不显示上下文菜单,因此我们必须添加它们。

private void dgvAccount_KeyDown(object sender, KeyEventArgs e)
{
    if ((e.KeyCode == Keys.F10 && e.Shift) || e.KeyCode == Keys.Apps)
    {
        e.SuppressKeyPress = true;
        DataGridViewCell currentCell = (sender as DataGridView).CurrentCell;
        if (currentCell != null)
        {
            ContextMenuStrip cms = currentCell.ContextMenuStrip;
            if (cms != null)
            {
                Rectangle r = currentCell.DataGridView.GetCellDisplayRectangle(currentCell.ColumnIndex, currentCell.RowIndex, false);
                Point p = new Point(r.X + r.Width, r.Y + r.Height);
                cms.Show(currentCell.DataGridView, p);
            }
        }
    }
}

I've reworked this code to work statically, so you can copy and paste them into any event.

我重新编写了此代码以使其静态工作,因此您可以将它们复制并粘贴到任何事件中。

The key is to use CellContextMenuStripNeededsince this will give you the context menu.

关键是使用,CellContextMenuStripNeeded因为这将为您提供上下文菜单。

Here's an example using CellContextMenuStripNeededwhere you can specify which context menu to show if you want to have different ones per row.

这是一个示例CellContextMenuStripNeeded,如果您希望每行显示不同的上下文菜单,您可以在其中指定要显示的上下文菜单。

In this context MultiSelectis Trueand SelectionModeis FullRowSelect. This is just for the example and not a limitation.

在这种情况下MultiSelectTrueSelectionModeFullRowSelect。这仅用于示例而非限制。

private void dgvAccount_CellContextMenuStripNeeded(object sender, DataGridViewCellContextMenuStripNeededEventArgs e)
{
    DataGridView dgv = (DataGridView)sender;

    if (e.RowIndex == -1 || e.ColumnIndex == -1)
        return;
    bool isPayment = true;
    bool isCharge = true;
    foreach (DataGridViewRow row in dgv.SelectedRows)
    {
        if ((string)row.Cells["P/C"].Value == "C")
            isPayment = false;
        else if ((string)row.Cells["P/C"].Value == "P")
            isCharge = false;
    }
    if (isPayment)
        e.ContextMenuStrip = cmsAccountPayment;
    else if (isCharge)
        e.ContextMenuStrip = cmsAccountCharge;
}

private void cmsAccountPayment_Opening(object sender, CancelEventArgs e)
{
    int itemCount = dgvAccount.SelectedRows.Count;
    string voidPaymentText = "&Void Payment"; // to be localized
    if (itemCount > 1)
        voidPaymentText = "&Void Payments"; // to be localized
    if (tsmiVoidPayment.Text != voidPaymentText) // avoid possible flicker
        tsmiVoidPayment.Text = voidPaymentText;
}

private void cmsAccountCharge_Opening(object sender, CancelEventArgs e)
{
    int itemCount = dgvAccount.SelectedRows.Count;
    string deleteChargeText = "&Delete Charge"; //to be localized
    if (itemCount > 1)
        deleteChargeText = "&Delete Charge"; //to be localized
    if (tsmiDeleteCharge.Text != deleteChargeText) // avoid possible flicker
        tsmiDeleteCharge.Text = deleteChargeText;
}

private void tsmiVoidPayment_Click(object sender, EventArgs e)
{
    int paymentCount = dgvAccount.SelectedRows.Count;
    if (paymentCount == 0)
        return;

    bool voidPayments = false;
    string confirmText = "Are you sure you would like to void this payment?"; // to be localized
    if (paymentCount > 1)
        confirmText = "Are you sure you would like to void these payments?"; // to be localized
    voidPayments = (MessageBox.Show(
                    confirmText,
                    "Confirm", // to be localized
                    MessageBoxButtons.YesNo,
                    MessageBoxIcon.Warning,
                    MessageBoxDefaultButton.Button2
                   ) == DialogResult.Yes);
    if (voidPayments)
    {
        // SQLTransaction Start
        foreach (DataGridViewRow row in dgvAccount.SelectedRows)
        {
            //do Work    
        }
    }
}

private void tsmiDeleteCharge_Click(object sender, EventArgs e)
{
    int chargeCount = dgvAccount.SelectedRows.Count;
    if (chargeCount == 0)
        return;

    bool deleteCharges = false;
    string confirmText = "Are you sure you would like to delete this charge?"; // to be localized
    if (chargeCount > 1)
        confirmText = "Are you sure you would like to delete these charges?"; // to be localized
    deleteCharges = (MessageBox.Show(
                    confirmText,
                    "Confirm", // to be localized
                    MessageBoxButtons.YesNo,
                    MessageBoxIcon.Warning,
                    MessageBoxDefaultButton.Button2
                   ) == DialogResult.Yes);
    if (deleteCharges)
    {
        // SQLTransaction Start
        foreach (DataGridViewRow row in dgvAccount.SelectedRows)
        {
            //do Work    
        }
    }
}

回答by Gers0n

For the position for the context menu, y found the problem that I needed a it to be relative to the DataGridView, and the event I needed to use gives the poistion relative to the cell clicked. I haven't found a better solution so I implemented this function in the commons class, so I call it from wherever I need.

对于上下文菜单的位置,你发现了我需要它相对于 DataGridView 的问题,我需要使用的事件给出了相对于单击单元格的位置。我还没有找到更好的解决方案,所以我在 commons 类中实现了这个函数,所以我从任何需要的地方调用它。

It's quite tested and works well. I Hope you find it useful.

它经过了充分的测试并且运行良好。希望对你有帮助。

    /// <summary>
    /// When DataGridView_CellMouseClick ocurs, it gives the position relative to the cell clicked, but for context menus you need the position relative to the DataGridView
    /// </summary>
    /// <param name="dgv">DataGridView that produces the event</param>
    /// <param name="e">Event arguments produced</param>
    /// <returns>The Location of the click, relative to the DataGridView</returns>
    public static Point PositionRelativeToDataGridViewFromDataGridViewCellMouseEventArgs(DataGridView dgv, DataGridViewCellMouseEventArgs e)
    {
        int x = e.X;
        int y = e.Y;
        if (dgv.RowHeadersVisible)
            x += dgv.RowHeadersWidth;
        if (dgv.ColumnHeadersVisible)
            y += dgv.ColumnHeadersHeight;
        for (int j = 0; j < e.ColumnIndex; j++)
            if (dgv.Columns[j].Visible)
                x += dgv.Columns[j].Width;
        for (int i = 0; i < e.RowIndex; i++)
            if (dgv.Rows[i].Visible)
                y += dgv.Rows[i].Height;
        return new Point(x, y);
    }

回答by Kshitij Jhangra

Follow the steps:

按照步骤:

  1. Create a context menu like: Sample context menu

  2. User needs to right click on the row to get this menu. We need to handle the _MouseClick event and _CellMouseDown event.

  1. 创建一个上下文菜单,如: 示例上下文菜单

  2. 用户需要右键单击该行才能获得此菜单。我们需要处理 _MouseClick 事件和 _CellMouseDown 事件。

selectedBiodataid is the variable that contains the selected row information.

selectedBiodataid 是包含所选行信息的变量。

Here is the code:

这是代码:

private void dgrdResults_MouseClick(object sender, MouseEventArgs e)
{   
    if (e.Button == System.Windows.Forms.MouseButtons.Right)
    {                      
        contextMenuStrip1.Show(Cursor.Position.X, Cursor.Position.Y);
    }   
}

private void dgrdResults_CellMouseDown(object sender, DataGridViewCellMouseEventArgs e)
{
    //handle the row selection on right click
    if (e.Button == MouseButtons.Right)
    {
        try
        {
            dgrdResults.CurrentCell = dgrdResults.Rows[e.RowIndex].Cells[e.ColumnIndex];
            // Can leave these here - doesn't hurt
            dgrdResults.Rows[e.RowIndex].Selected = true;
            dgrdResults.Focus();

            selectedBiodataId = Convert.ToInt32(dgrdResults.Rows[e.RowIndex].Cells[1].Value);
        }
        catch (Exception)
        {

        }
    }
}

and the output would be:

输出将是:

Final output

最终输出