haga clic derecho en el menú contextual para datagridview

Tengo una vista de cuadrícula de datos en una aplicación. Winform .NET. Me gustaría hacer clic derecho en una fila y tener un menú emergente. Entonces me gustaría seleccionar cosas como copiar, validar, etc.

¿Cómo hago que A) aparezca un menú emergente? B) encuentre en qué fila se hizo clic con el botón derecho. Sé que podría usar selectedIndex, pero debería poder hacer clic derecho sin cambiar lo que está seleccionado. ahora mismo podría usar el índice seleccionado, pero si hay una manera de obtener los datos sin cambiar lo que se selecciona, entonces sería útil.

Puede usar CellMouseEnter y CellMouseLeave para rastrear el número de fila sobre el que se encuentra el mouse actualmente.

Luego use un objeto ContextMenu para mostrar su menú emergente, personalizado para la fila actual.

Aquí hay un ejemplo rápido y sucio de lo que quiero decir …

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(eX,eY).RowIndex; if (currentMouseOverRow >= 0) { m.MenuItems.Add(new MenuItem(string.Format("Do something to row {0}", currentMouseOverRow.ToString()))); } m.Show(dataGridView1, new Point(eX, eY)); } } 

Si bien esta pregunta es antigua, las respuestas no son adecuadas. Los menús contextuales tienen sus propios eventos en DataGridView. Hay un evento para el menú contextual de la fila y el menú contextual de la celda.

La razón por la cual estas respuestas no son correctas es que no tienen en cuenta los diferentes esquemas de operación. Es posible que las opciones de accesibilidad, las conexiones remotas o las conexiones Metro / Mono / Web / WPF no funcionen y los atajos de teclado fallarán a la derecha (Shift + F10 o la tecla de menú contextual).

La selección de celda con el clic derecho del mouse tiene que ser manejada manualmente. No es necesario que se muestre el menú contextual, ya que la IU lo maneja.

Esto imita por completo el enfoque utilizado por Microsoft Excel. Si una celda es parte de un rango seleccionado, la selección de celda no cambia y tampoco lo hace CurrentCell . Si no es así, se borra el rango anterior y se selecciona la celda y se convierte en CurrentCell .

Si no está seguro de esto, CurrentCell es donde el teclado tiene foco cuando presiona las teclas de flecha. Selected es si es parte de SelectedCells . El menú contextual se mostrará con un clic derecho manejado por la IU.

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

Los atajos de teclado no muestran el menú contextual de manera predeterminada, por lo que debemos agregarlos.

 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(rX + r.Width, rY + r.Height); cms.Show(currentCell.DataGridView, p); } } } } 

He vuelto a trabajar este código para trabajar estáticamente, para que pueda copiarlo y pegarlo en cualquier evento.

La clave es usar CellContextMenuStripNeeded ya que esto le dará el menú contextual.

Aquí hay un ejemplo usando CellContextMenuStripNeeded donde puedes especificar qué menú contextual mostrar si quieres tener diferentes por fila.

En este contexto, MultiSelect es True y SelectionMode es FullRowSelect . Esto es solo por el ejemplo y no una limitación.

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

Use el evento CellMouseDown en DataGridView . Desde los argumentos del controlador de eventos puede determinar en qué celda se hizo clic. Utilizando el método PointToClient() en DataGridView puede determinar la posición relativa del puntero a DataGridView, para que pueda abrir el menú en la ubicación correcta.

(El parámetro DataGridViewCellMouseEvent solo le proporciona la X y la Y relativas a la celda en la que hizo clic, que no es tan fácil de usar como para abrir el menú contextual).

Este es el código que utilicé para obtener la posición del mouse y luego ajustar la posición de DataGridView:

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

Todo el controlador de eventos se ve así:

 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); } } } 
  • Ponga un menú contextual en su formulario, nómbrelo, establezca leyendas, etc. usando el editor incorporado
  • Vincúlelo a su cuadrícula utilizando la propiedad de cuadrícula ContextMenuStrip
  • Para su cuadrícula, cree un evento para manejar CellContextMenuStripNeeded
  • El evento Args e tiene propiedades útiles e.ColumnIndex , e.RowIndex .

Creo que e.RowIndex es lo que estás pidiendo.

Sugerencia: cuando el usuario hace que su evento CellContextMenuStripNeeded dispare, use e.RowIndex para obtener datos de su cuadrícula, como la ID. Almacene la ID como elemento de etiqueta del evento del menú.

Ahora, cuando el usuario realmente hace clic en su elemento de menú, use la propiedad del remitente para buscar la etiqueta. Use la etiqueta, que contiene su identificación, para realizar la acción que necesita.

Simplemente arrastre un componente ContextMenu o ContextMenuStrip a su formulario y visualícelo, luego asígnelo a la propiedad ContextMenu o ContextMenuStrip del control que desee.

Para la posición del menú de contexto, encontré el problema que necesitaba que fuera relativo a DataGridView, y el evento que necesitaba usar da la poción relativa a la celda en la que se hizo clic. No he encontrado una mejor solución, así que implementé esta función en la clase de los comunes, así que la llamo desde donde sea que necesite.

Está bastante probado y funciona bien. Espero que le sea útil.

  ///  /// 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 ///  /// DataGridView that produces the event /// Event arguments produced /// The Location of the click, relative to the DataGridView public static Point PositionRelativeToDataGridViewFromDataGridViewCellMouseEventArgs(DataGridView dgv, DataGridViewCellMouseEventArgs e) { int x = eX; int y = eY; 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); } 

Sigue los pasos:

  1. Crea un menú contextual como: Menú de contexto de muestra

  2. El usuario debe hacer clic derecho en la fila para obtener este menú. Necesitamos manejar el evento _MouseClick y el evento _CellMouseDown.

selectedBiodataid es la variable que contiene la información de la fila seleccionada.

Aquí está el código:

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

y el resultado sería:

Salida final