右击 datagridview 的上下文菜单

我有一个 datagridview。NET winform 应用程序。我想右键单击一行,弹出一个菜单。然后我想选择的东西,如复制,验证等

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

274457 次浏览

可以使用 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));


}
}

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

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

(DataGridViewCellMouseEvent参数只提供相对于单元格的 XY,而单元格不太容易弹出上下文菜单。)

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

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

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

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);
}
}
}
  • 使用内置的编辑器在表单上放一个上下文菜单,命名它,设置标题等等
  • 使用网格属性 ContextMenuStrip将其链接到您的网格
  • 对于您的网格,创建一个事件来处理 CellContextMenuStripNeeded
  • 事件 Args e 具有有用的属性 e.ColumnIndexe.RowIndex

我相信 e.RowIndex就是你想要的。

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

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

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

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

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

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

如果您不清楚这一点,按下箭头键时键盘的焦点就是 CurrentCellSelected是否是 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;
}
}
}

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

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

我已经重新编写了这段代码,以便静态工作,所以您可以将它们复制并粘贴到任何事件中。

关键是使用 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
}
}
}

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

它经过了很好的测试,我希望你能发现它的用处。

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

按照以下步骤:

  1. 创建一个上下文菜单,如: Sample context menu

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


}
}
}

产出将是:

Final output

这个问题的答案很简单,就是使用 CellMouseDown

  1. 在设计你的 ContextMenu之后
// somewhere in your code


ContextMenu cm = new ContextMenu();
cm.MenuItems.Add(new MenuItem("Option1"));
cm.MenuItems.Add(new MenuItem("Option2"));
  1. 分配到 DataGridView
myDataView.ContextMenu = cm;
  1. 在不更改 Selected单元格的情况下从单元格中获取数据
private void myDataView_CellMouseDown(object sender, DataGridViewCellMouseEventArgs e)
{
if (e.Button == MouseButtons.Right)
{
string myData = myDataView.Rows[e.RowIndex].Cells[e.ColumnIndex].Value.ToString();


// Now ADD the captured DATA to the ContextMenu
cm.MenuItems.Add(new MenuItem("myData"));


// OR if you are doing it by the designer and YOU ALREADY have an Item
// you can simply change the name of it in the designer page to your
// desired one and then just change the Text of it
MenuItem_BTN.Text = "$Properties of {myData}";
}
}