我有一个.NET winform应用程序中的DataGridView。 我希望右键单击一行时弹出一个菜单,然后我可以选择复制、验证等事项。
如何做到:A) 弹出一个菜单;B) 找到右键单击的行。我知道可以使用selectedIndex,但我应该能够右键单击而不更改所选内容吗? 目前我可以使用selectedIndex,但如果有一种方法可以在不更改所选内容的情况下获取数据,则会很有用。
我有一个.NET winform应用程序中的DataGridView。 我希望右键单击一行时弹出一个菜单,然后我可以选择复制、验证等事项。
如何做到:A) 弹出一个菜单;B) 找到右键单击的行。我知道可以使用selectedIndex,但我应该能够右键单击而不更改所选内容吗? 目前我可以使用selectedIndex,但如果有一种方法可以在不更改所选内容的情况下获取数据,则会很有用。
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));
}
}
虽然这个问题很旧,但回答并不正确。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;
}
}
}
默认情况下,键盘快捷键不会显示上下文菜单,因此我们需要添加它们。
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
的示例,您可以指定每行要显示哪个上下文菜单。
在此上下文中,MultiSelect
为True
,而SelectionMode
为FullRowSelect
。这只是示例,不是限制。
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
}
}
}
FullRowSelect
作为 SelectionMode
,这里有一个小提示。如果你想要更改已选择行中的单元格,你必须不检查该单元格是否被选中。 - PetoMPPContextMenuStrip
将其链接到您的网格CellContextMenuStripNeeded
e.ColumnIndex
,e.RowIndex
我认为e.RowIndex
是你所要求的。
建议:当用户触发你的事件CellContextMenuStripNeeded
时,使用e.RowIndex
从网格中获取数据,例如ID。将ID存储为菜单事件的标签项。
现在,当用户实际点击菜单项时,使用Sender属性获取标签。使用包含您的ID的标签执行所需的操作。
if(e.RowIndex >= 0){e.ContextMenuStrip = yourContextMenuInstance;}
。这意味着菜单只会在右键单击有效行时显示(即不会在标题或空网格区域上显示)。 - James SCellContextMenuStripNeeded
只在你的 DGV 绑定了数据源或者它的 VirtualMode 被设置为 true 时才有效。在其他情况下,你需要在 CellMouseDown
事件中设置该标签。为了保险起见,在 MouseDown 事件处理程序中执行 DataGridView.HitTestInfo
来检查你是否在单元格上。 - LocEngineerDataGridView
上的 CellMouseDown
事件。通过事件处理程序参数可以确定所单击的单元格。使用 PointToClient()
方法,可以确定指针相对于 DataGridView 的位置,以便在正确的位置弹出菜单。DataGridViewCellMouseEvent
参数只提供了相对于所单击单元格的 X
和 Y
坐标,不太容易用于弹出上下文菜单。)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);
}
}
}
(sender as DataGridView)[e.ColumnIndex, e.RowIndex];
来更简单地调用单元格。 - QsirisselectedBiodataid是包含所选行信息的变量。
以下是代码:
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)
{
}
}
}
只需将ContextMenu或ContextMenuStrip组件拖入您的窗体并进行可视化设计,然后将其分配给所需控件的ContextMenu或ContextMenuStrip属性。
在上下文菜单的位置问题上,我发现需要相对于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);
}
这个主题有一个简单的答案,就是使用CellMouseDown
ContextMenu
// somewhere in your code
ContextMenu cm = new ContextMenu();
cm.MenuItems.Add(new MenuItem("Option1"));
cm.MenuItems.Add(new MenuItem("Option2"));
DataGridView
。myDataView.ContextMenu = cm;
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}";
}
}
m.Show(dataGridView1.PointToScreen(e.Location));
- Olivier Jacot-Descombes