J'ai un datagridview dans une application .NET Winform. Je voudrais faire un clic droit sur une ligne et faire apparaître un menu. Ensuite, j'aimerais sélectionner des éléments tels que copier, valider, etc.
Comment puis-je faire A) un menu contextuel B) trouver quelle ligne a été cliqué avec le bouton droit de la souris. Je sais que je pourrais utiliser selectedIndex mais je devrais pouvoir faire un clic droit sans changer ce qui est sélectionné? À l'heure actuelle, je pourrais utiliser un index sélectionné, mais s'il existe un moyen d'obtenir les données sans changer ce qui est sélectionné, ce serait utile.
Vous pouvez utiliser CellMouseEnter et CellMouseLeave pour suivre le numéro de ligne survolé par la souris.
Utilisez ensuite un objet ContextMenu pour afficher votre menu contextuel, personnalisé pour la ligne en cours.
Voici un exemple rapide et sale de ce que je veux dire ...
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));
}
}
Bien que cette question soit ancienne, les réponses ne sont pas correctes. Les menus contextuels ont leurs propres événements sur DataGridView. Il existe un événement pour le menu contextuel de la ligne et le menu contextuel de la cellule.
La raison pour laquelle ces réponses ne sont pas correctes est qu'elles ne tiennent pas compte de différents schémas de fonctionnement. Les options d'accessibilité, les connexions à distance ou le portage Metro/Mono/Web/WPF risquent de ne pas fonctionner et les raccourcis clavier risquent d'échouer (touche Maj + F10 ou la touche Menu contextuel).
La sélection de cellule sur un clic droit de la souris doit être gérée manuellement. L'affichage du menu contextuel n'a pas besoin d'être traité car c'est l'interface utilisateur.
Cela imite complètement l'approche utilisée par Microsoft Excel. Si une cellule fait partie d'une plage sélectionnée, la sélection de cellule ne change pas, pas plus que CurrentCell
. Si ce n'est pas le cas, l'ancienne plage est effacée et la cellule est sélectionnée et devient CurrentCell
.
Si vous n'êtes pas certain à ce sujet, CurrentCell
est l'endroit où le clavier est activé lorsque vous appuyez sur les touches fléchées. Selected
indique si cela fait partie de SelectedCells
. Le menu contextuel apparaîtra avec le clic droit comme géré par l'interface utilisateur.
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;
}
}
}
Les raccourcis clavier n’affichant pas le menu contextuel par défaut, nous devons donc les ajouter.
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);
}
}
}
}
J'ai retravaillé ce code pour qu'il fonctionne de manière statique, vous pouvez donc les copier et les coller dans n'importe quel événement.
La clé est d'utiliser CellContextMenuStripNeeded
car cela vous donnera le menu contextuel.
Voici un exemple utilisant CellContextMenuStripNeeded
où vous pouvez spécifier le menu contextuel à afficher si vous souhaitez en avoir différents par ligne.
Dans ce contexte, MultiSelect
est True
et SelectionMode
est FullRowSelect
. Ceci est juste pour l'exemple et pas une limitation.
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
}
}
}
Utilisez l'événement CellMouseDown
sur le DataGridView
. À partir des arguments du gestionnaire d’événements, vous pouvez déterminer la cellule sur laquelle vous avez cliqué. À l'aide de la méthode PointToClient()
sur le DataGridView, vous pouvez déterminer la position relative du pointeur sur le DataGridView afin de pouvoir afficher le menu à l'emplacement correct.
(Le paramètre DataGridViewCellMouseEvent
vous donne simplement les valeurs X
et Y
par rapport à la cellule sur laquelle vous avez cliqué, ce qui n'est pas aussi facile à utiliser pour afficher le menu contextuel.)
C'est le code que j'ai utilisé pour obtenir la position de la souris, puis ajuster pour la position du DataGridView:
var relativeMousePosition = DataGridView1.PointToClient(Cursor.Position);
this.ContextMenuStrip1.Show(DataGridView1, relativeMousePosition);
Le gestionnaire d'événement entier ressemble à ceci:
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);
}
}
}
ContextMenuStrip
CellContextMenuStripNeeded
e.ColumnIndex
, e.RowIndex
.Je crois que e.RowIndex
est ce que vous demandez.
Suggestion: lorsque l'utilisateur déclenche votre événement CellContextMenuStripNeeded
, utilisez e.RowIndex
pour obtenir des données de votre grille, telles que l'ID. Stockez l'ID en tant qu'élément de balise de l'événement de menu.
Désormais, lorsque l'utilisateur clique sur votre élément de menu, utilisez la propriété Sender pour extraire la balise. Utilisez la balise contenant votre identifiant pour effectuer l'action dont vous avez besoin.
Faites simplement glisser un composant ContextMenu ou ContextMenuStrip dans votre formulaire et concevez-le visuellement, puis affectez-le à la propriété ContextMenu ou ContextMenuStrip du contrôle souhaité.
Suis les étapes:
L'utilisateur doit cliquer avec le bouton droit sur la ligne pour afficher ce menu. Nous devons gérer les événements _MouseClick et _CellMouseDown.
selectedBiodataid est la variable qui contient les informations sur la ligne sélectionnée.
Voici le code:
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)
{
}
}
}
et le résultat serait:
Pour la position du menu contextuel, y a trouvé le problème selon lequel j’avais besoin d’être relatif au DataGridView, et l’événement que je devais utiliser donne le poistion relatif à la cellule cliquée. Je n’ai pas trouvé de meilleure solution, j’ai donc implémenté cette fonction dans la classe des biens communs, je l’appelle donc de là où j’ai besoin.
C'est assez testé et fonctionne bien. J'espère que tu trouves cela utile.
/// <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);
}