右键单击datagridview的上下文菜单

108 c# datagridview contextmenu right-click winforms

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

如何制作A)弹出菜单B)找到右键单击的行.我知道我可以使用selectedIndex,但我应该能够右键单击而不更改所选内容?现在我可以使用选定的索引,但如果有办法获取数据而不改变选择的内容那么这将是有用的.

Stu*_*wig 135

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

然后使用ContextMenu对象显示为当前行自定义的弹出菜单.

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

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

    }
}
Run Code Online (Sandbox Code Playgroud)

  • 这个方法很旧:datagridview有一个属性:ContextMenu.操作员右键单击后,将立即打开上下文菜单.相应的ContextMenuOpening事件使您有机会根据当前单元格或所选单元格决定要显示的内容.请参阅其他答案之一 (17认同)
  • 正确!和你的注释,var r = dataGridView1.HitTest(eX,eY); r.RowIndex使用鼠标或currentMouseOverRow更好地工作 (5认同)
  • 为了获得正确的屏幕坐标,您应该打开上下文菜单:`m.Show(dataGridView1.PointToScreen(e.Location));` (4认同)
  • 在string.Format中使用.ToString()是不必要的. (3认同)

Sho*_*use 82

虽然这个问题很老,但答案却不合适.上下文菜单在DataGridView上有自己的事件.行上下文菜单和单元格上下文菜单有一个事件.

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

必须手动处理右键单击时的单元格选择.显示上下文菜单不需要处理,因为它由UI处理.

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

如果您不清楚这一点,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;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

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

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);
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

我已经将此代码重新编写为静态工作,因此您可以将它们复制并粘贴到任何事件中.

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

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

在这种情况下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    
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

  • +1用于综合答案和考虑可访问性(以及回答3年前的问题) (5认同)
  • 同意,这比被接受的要好得多(虽然它们中没有任何一个真正出错) - 甚至包括键盘支持的更多荣誉,这是很多人似乎都没有想到的. (3认同)
  • 很好的答案,提供所有灵活性:不同的上下文菜单取决于点击的内容.而且正是EXCEL的行为 (2认同)
  • 我不喜欢这种方法,因为使用我的简单DataGridView,我不使用数据源或虚拟模式.`只有在设置DataGridView控件DataSource属性或其VirtualMode属性为true时,才会发生CellContextMenuStripNeeded事件. (2认同)

Mat*_*att 45

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

(该DataGridViewCellMouseEvent参数只是给你的X,并Y相对于您单击的单元格,这是不容易使用弹出上下文菜单.)

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

var relativeMousePosition = DataGridView1.PointToClient(Cursor.Position);
this.ContextMenuStrip1.Show(DataGridView1, relativeMousePosition);
Run Code Online (Sandbox Code Playgroud)

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

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);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)


小智 42

  • 在表单上放置一个上下文菜单,为其命名,使用内置编辑器设置标题等
  • 使用grid属性将其链接到您的网格 ContextMenuStrip
  • 对于您的网格,创建要处理的事件 CellContextMenuStripNeeded
  • 事件参数e的化合物具有有用的特性e.ColumnIndex,e.RowIndex.

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

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

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

  • 我不能赞成这一点.其他答案对我来说很明显,但我可以说有更多内置支持上下文菜单(而不仅仅是DataGrid).*这*是正确的答案. (5认同)
  • 这个答案几乎就在那里,但是我建议您不要将上下文菜单链接到网格属性ContextMenuStrip。而是在“ CellContextMenuStripNeeded”事件处理程序内执行“ if(e.RowIndex> = 0){e.ContextMenuStrip = yourContextMenuInstance;}”,这意味着菜单仅在右键单击有效行时显示(即不在标题或空网格区域) (2认同)

Ksh*_*gra 8

按照步骤:

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

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

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

这是代码:

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)
        {

        }
    }
}
Run Code Online (Sandbox Code Playgroud)

输出将是:

最终输出


Cap*_*mic 6

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