134 votes

menu contextuel de clic droit pour datagridview

J'ai une grille de données dans une application .NET winform. Je voudrais faire un clic droit sur une ligne et faire apparaître un menu. Ensuite, je voudrais sélectionner des éléments tels que copier, valider, etc.

Comment faire pour A) faire apparaître un menu B) trouver la ligne sur laquelle on a fait un clic droit. Je sais que je pourrais utiliser selectedIndex mais je devrais pouvoir faire un clic droit sans changer ce qui est sélectionné ? Pour l'instant, je peux utiliser selectedIndex mais s'il y a un moyen d'obtenir les données sans changer ce qui est sélectionné, ce serait utile.

162voto

Stuart Helwig Points 3086

Vous pouvez utiliser les fonctions CellMouseEnter et CellMouseLeave pour suivre le numéro de la ligne sur laquelle la souris se trouve.

Utilisez ensuite un objet ContextMenu pour afficher votre menu contextuel, personnalisé pour la ligne actuelle.

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

    }
}

6 votes

Correct ! et une note pour vous, var r = dataGridView1.HitTest(e.X, e.Y) ; r.RowIndex fonctionne BIEN MIEUX que l'utilisation de la souris ou de currentMouseOverRow.

3 votes

L'utilisation de .ToString() dans string.Format est inutile.

29 votes

Cette méthode est ancienne : un datagridview a une propriété : ContextMenu. Le menu contextuel s'ouvre dès que l'opérateur fait un clic droit. L'événement ContextMenuOpening correspondant vous donne la possibilité de décider ce qu'il faut afficher en fonction de la cellule courante ou des cellules sélectionnées. Voir une des autres réponses

113voto

ShortFuse Points 562

Bien que cette question soit ancienne, les réponses ne sont pas appropriées. Les menus contextuels ont leurs propres événements sur DataGridView. Il y a 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 des différents schémas de fonctionnement. Les options d'accessibilité, les connexions à distance ou le portage Metro/Mono/Web/WPF peuvent ne pas fonctionner et les raccourcis clavier peuvent échouer (Shift+F10 ou touche Menu contextuel).

La sélection des cellules par un clic droit de la souris doit être gérée manuellement. L'affichage du menu contextuel n'a pas besoin d'être géré car il est géré par 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 la cellule ne change pas, pas plus que l'option 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 clair sur ce point, CurrentCell est l'endroit où le clavier a la priorité lorsque vous appuyez sur les touches fléchées. Selected est de savoir si elle fait partie de SelectedCells . Le menu contextuel s'affichera lors d'un clic droit comme le gère 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'affichent 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, afin que vous puissiez 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 non 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    
        }
    }
}

8 votes

+1 pour la réponse complète et pour avoir pris en compte l'accessibilité (et pour avoir répondu à une question vieille de 3 ans).

4 votes

Je suis d'accord, c'est bien mieux que ce qui a été accepté (bien qu'il n'y ait rien de vraiment mauvais dans aucun d'entre eux) - et encore plus de félicitations pour avoir inclus le support du clavier, quelque chose auquel tant de gens semblent ne pas penser.

2 votes

Excellente réponse, donne toute la flexibilité : différents menus contextuels en fonction de ce qui est cliqué. Et exactement le comportement d'EXCEL

52voto

ActualRandy Points 61
  • Placez un menu contextuel sur votre formulaire, donnez-lui un nom, définissez des légendes, etc. à l'aide de l'éditeur intégré.
  • Liez-le à votre grille en utilisant la propriété de la grille ContextMenuStrip
  • Pour votre grille, créez un événement pour gérer CellContextMenuStripNeeded
  • L'élément Event Args e a des propriétés utiles e.ColumnIndex , e.RowIndex .

Je crois que e.RowIndex est ce que vous demandez.

Suggestion : lorsque l'utilisateur provoque votre événement CellContextMenuStripNeeded pour tirer, utiliser e.RowIndex pour obtenir des données de votre grille, comme l'ID. Stockez l'ID comme élément de balise de l'événement de menu.

Maintenant, lorsque l'utilisateur clique sur l'élément de votre menu, utilisez la propriété Sender pour récupérer la balise. Utilisez la balise, contenant votre ID, pour effectuer l'action dont vous avez besoin.

6 votes

Je ne peux pas assez le mettre en avant. Les autres réponses étaient évidentes pour moi mais je pouvais dire qu'il y avait plus de support intégré pour les menus contextuels (et pas seulement pour DataGrid). Ce site est la bonne réponse.

1 votes

@ActualRandy, comment puis-je obtenir le tag lorsque l'utilisateur clique sur le menu contextuel actuel ? sous l'événement CellcontexMenustripNeeded, j'ai quelque chose comme ça contextMenuStrip1.Tag = e.RowIndex ;

4 votes

Cette réponse est presque complète, mais je vous suggère de NE PAS lier le menu contextuel à la propriété de la grille ContextMenuStrip. Au lieu de cela, dans la propriété CellContextMenuStripNeeded le gestionnaire d'événement fait if(e.RowIndex >= 0){e.ContextMenuStrip = yourContextMenuInstance;} Cela signifie que le menu ne s'affiche que lorsque l'on clique avec le bouton droit de la souris sur une ligne valide (c'est-à-dire pas sur un en-tête ou une zone de grille vide).

50voto

Matt Points 1042

Utilisez le CellMouseDown sur l'événement DataGridView . À partir des arguments du gestionnaire d'événement, vous pouvez déterminer quelle cellule a été cliquée. En utilisant les arguments du PointToClient() sur le DataGridView, vous pouvez déterminer la position relative du pointeur sur le DataGridView, afin de faire apparaître le menu au bon endroit.

(Le DataGridViewCellMouseEvent vous donne juste le paramètre X et Y par rapport à la cellule sur laquelle vous avez cliqué, ce qui n'est pas aussi facile à utiliser pour faire apparaître le menu contextuel).

Voici le code que j'ai utilisé pour obtenir la position de la souris, puis l'ajuster à la position du DataGridView :

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

L'ensemble du gestionnaire d'événements 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);
        }
    }
}

1 votes

Vous pouvez également utiliser (sender as DataGridView)[e.ColumnIndex, e.RowIndex]; pour un appel plus simple à la cellule.

0 votes

La réponse cochée ne fonctionne pas correctement sur plusieurs écrans, mais cette réponse fonctionne.

7voto

Captain Comic Points 3251

Il suffit de faire glisser un composant ContextMenu ou ContextMenuStrip dans votre formulaire et de le concevoir visuellement, puis de l'affecter à la propriété ContextMenu ou ContextMenuStrip du contrôle souhaité.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X