2009-11-11 4 views
86

У меня есть datagridview в приложении winform .NET. Я хотел бы щелкнуть правой кнопкой мыши по строке и открыть меню. Тогда я хотел бы выбрать такие вещи, как копирование, проверка и т. Д.контекстное меню правой кнопки мыши для datagridview

Как сделать A) всплывающее меню B) найти, какая строка была нажата правой кнопкой мыши. Я знаю, что я мог бы использовать selectedIndex, но я должен был бы щелкнуть правой кнопкой мыши, не меняя того, что выбрано? прямо сейчас я мог бы использовать выбранный индекс, но если есть способ получить данные без изменения того, что выбрано, это было бы полезно.

ответ

109

Вы можете использовать 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)); 

    } 
} 
+5

Правильно! и примечание для вас, var r = dataGridView1.HitTest (e.X, e.Y); r.RowIndex работает WAY BETTER, используя мышь или currentMouseOverRow – 2009-11-12 03:24:09

+3

с использованием .ToString() в string.Format не требуется. – msavara

+12

Этот метод устарел: datagridview обладает свойством: ContextMenu. Контекстное меню будет открыто, как только оператор щелкнет правой кнопкой мыши. Соответствующее событие ContextMenuOpening дает вам возможность решить, что показывать в зависимости от текущей ячейки или выбранных ячеек. См. Один из других ответов –

4

Просто перетащите компонент ContextMenu или ContextMenuStrip в вашу форму и визуально конструировать его, а затем присвоить его свойству ContextMenu или ContextMenuStrip из вашего желающий управление.

41

Используйте событие CellMouseDown на DataGridView. Из аргументов обработчика событий вы можете определить, какая ячейка была нажата. Используя метод PointToClient() в DataGridView, вы можете определить относительное положение указателя на DataGridView, чтобы вы могли отобразить меню в нужном месте.

(Параметр DataGridViewCellMouseEvent просто дает X и Y относительно ячейки, которую щелкнули, который не так легко использовать для вызова контекстного меню.)

Это код, который я использовал, чтобы получить положение мыши, а затем настроить для позиции 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); 
     } 
    } 
} 
+1

Вы также можете использовать '(отправитель как DataGridView) [e. ColumnIndex, e.RowIndex]; 'для более простого вызова ячейки. – Qsiris

+0

Проверенный ответ работает неправильно на нескольких экранах, но этот ответ работает. –

29
  • Помещенный контекстное меню на форме, имя его, установить подписи и т.д., используя встроенный редактор
  • подключитесь к сетке с помощью свойства ContextMenuStrip
  • сетки для вашей сетки, создать событие для обработки CellContextMenuStripNeeded
  • Событие Args e имеет полезные свойства e.ColumnIndex, e.RowIndex.

Я считаю, что e.RowIndex - это то, о чем вы просите.

Предложение: когда пользователь вызывает ваше событие CellContextMenuStripNeeded, выполните следующие действия: e.RowIndex, чтобы получить данные из вашей сетки, такие как идентификатор. Сохраните идентификатор в качестве элемента тега события меню.

Теперь, когда пользователь нажимает на ваш пункт меню, используйте свойство «Отправитель» для извлечения тега. Используйте тег, содержащий ваш идентификатор, чтобы выполнить необходимое действие.

+3

Я не могу выдвинуть это достаточно. Другие ответы были очевидны для меня, но я мог сказать, что есть более встроенная поддержка контекстных меню (а не только для DataGrid). *Это правильный ответ. –

+0

Плюс 1 здесь –

+0

@ActualRandy, как я могу получить тег, когда пользователь нажимает на фактическое контекстное меню? под событием CellcontexMenustripNeeded, у меня есть что-то вроде этого contextMenuStrip1.Tag = e.RowIndex; –

60

Хотя этот вопрос старый, ответы не являются правильными. Контекстные меню имеют свои собственные события в DataGridView.Существует событие для контекстного меню строки и контекстного меню ячейки.

Причина, по которой эти ответы не являются правильными, заключается в том, что они не учитывают разные схемы работы. Параметры доступности, удаленные подключения или перенос между Metro/Mono/Web/WPF могут не работать, а сочетания клавиш спускаются вправо (Shift + F10 или клавиша контекстного меню).

Выбор ячейки правой кнопкой мыши необходимо обрабатывать вручную. Отображать контекстное меню не нужно, поскольку это обрабатывается пользовательским интерфейсом.

Это полностью имитирует подход, используемый Microsoft Excel. Если ячейка является частью выбранного диапазона, выбор ячейки не изменяется, и ни CurrentCell. Если это не так, старый диапазон очищается и ячейка выбирается и становится CurrentCell.

Если вы неясны, CurrentCell - это то, где клавиатура имеет фокус при нажатии клавиш со стрелками. Selected является ли он частью SelectedCells. Контекстное меню будет отображаться при щелчке правой кнопкой мыши при работе с пользовательским интерфейсом.

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

+1 для всестороннего ответа и для рассмотрения вопроса о доступности (и для ответа на 3-летний вопрос) –

+3

Согласовано, это намного лучше, чем принятое (хотя нет ничего действительно неправильного с любым из них) - и еще больше наград за включение поддержки клавиатуры , что-то, о чем многие люди просто не думают. –

+2

Отличный ответ, дает всю гибкость: различные контекстные меню в зависимости от того, что нажато. И точно поведение EXCEL –

2

Для позиции для контекстного меню, у нашел проблему, что мне нужно, чтобы это было по отношению к DataGridView, и событие мне нужно использовать дает poistion относительно клетки щелкнули. Я не нашел лучшего решения, поэтому я реализовал эту функцию в классе 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

Выполните следующие действия:

  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