2009-11-11 36 views
86

Tengo una vista de tabla 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.menú contextual con el botón derecho del mouse para datagridview

Cómo hago A) un menú emergente B) encuentro qué fila se hizo clic 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.

Respuesta

109

Puede usar el CellMouseEnter y el 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í está 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(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)); 

    } 
} 
+5

correcta! y una nota para usted, var r = dataGridView1.HitTest (e.X, e.Y); r.RowIndex funciona MUCHO MEJOR que usar mouse o currentMouseOverRow –

+3

usando .ToString() en string.Format es innecesariamente. – msavara

+12

Este método es antiguo: una vista de tabla de datos tiene una propiedad: ContextMenu. El menú contextual se abrirá tan pronto como el operador haga clic derecho. El evento correspondiente ContextMenuOpening le brinda la oportunidad de decidir qué mostrar según la celda actual o las celdas seleccionadas. Consulte una de las otras respuestas –

4

Basta con arrastrar un componente ContextMenu o ContextMenuStrip en el formulario y visualmente diseñarlo, a continuación, asignar a la propiedad ContextMenu o ContextMenuStrip de su control deseado

41

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

(El parámetro DataGridViewCellMouseEvent simplemente le da la X y Y relativa a la célula que ha hecho clic, que no es tan fácil de usar para que aparezca el menú contextual.)

Este es el código que utiliza para obtener la posición del ratón, y luego ajustar la posición de la DataGridView:

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

todo el controlador de eventos es el siguiente:

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

También podría usar '(remitente como DataGridView) [e. ColumnIndex, e.RowIndex]; 'para una llamada más simple a la celda. – Qsiris

+0

La respuesta comprobada no funciona correctamente en varias pantallas, pero esta respuesta funciona. –

29
  • Deja un menú contextual de su forma, el nombre, etc. subtítulos conjunto utilizando el editor incorporado
  • Enlace a su cuadrícula utilizando la propiedad rejilla ContextMenuStrip
  • Para su cuadrícula, crear 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 se 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 haga 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.

+3

No puedo votar lo suficiente. Las otras respuestas fueron obvias para mí, pero pude ver que había más soporte incorporado para los menús contextuales (y no solo para DataGrid). *Esta es la respuesta correcta. –

+0

Más 1 aquí –

+0

@ActualRandy, ¿cómo obtengo la etiqueta cuando el usuario hace clic en el menú contextual real? en el evento CellcontexMenustripNeeded, tengo algo así como contextMenuStrip1.Tag = e.RowIndex; –

60

Si bien esta pregunta es antigua, las respuestas no son las correctas. 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 las adecuadas 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; 
     } 
    } 
} 

atajos de teclado no mostrar el menú de contexto por defecto, así que habrá que añadirlos en.

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

he vuelto a trabajar este código para trabajar de forma estática, por lo que se pueden copiar y pegar en cualquier evento.

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

Aquí hay un ejemplo usando CellContextMenuStripNeeded donde puede especificar qué menú contextual mostrar si desea 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  
     } 
    } 
} 
+4

+1 para una respuesta completa y para considerar la accesibilidad (y para responder una pregunta de 3 años) –

+3

De acuerdo, esto es mucho mejor que el aceptado (aunque no hay nada realmente malo en ninguno de ellos) y aún más felicitaciones por incluir soporte de teclado , algo en lo que mucha gente parece no pensar. –

+2

Excelente respuesta, da toda la flexibilidad: diferentes menús contextuales dependiendo de lo que se haga clic. Y exactamente el comportamiento EXCEL –

2

Para la posición para el menú contextual, y encontraron el problema de que se necesitaba un ser en relación con el DataGridView, y el evento que necesitaba usar da poistion relativa a la célula se hace 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.

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

Sigue los pasos:

  1. crear un menú de contexto como: Sample context menu

  2. usuario tiene que hacer clic derecho sobre la fila para conseguir 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 la salida sería:

Final output

Cuestiones relacionadas