Контекстное меню правой кнопки мыши для 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
Следуйте шагам:
-
Создайте контекстное меню, например: ![Sample context menu]()
-
Пользователь должен щелкнуть правой кнопкой мыши по строке, чтобы получить это меню. Нам нужно обработать событие _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]()