Контекстное меню правой кнопки мыши для datagridview

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

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

Ответ 1

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

    }
}

Ответ 2

Пока этот вопрос старый, ответы не являются правильными. Контекстные меню имеют свои собственные события в 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    
        }
    }
}

Ответ 3

Используйте событие 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);
        }
    }
}

Ответ 4

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

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

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

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

Ответ 5

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

Ответ 6

Для позиции контекстного меню y обнаружила, что проблема в том, что она была относительно 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);
    }

Ответ 7

Следуйте шагам:

  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