web-dev-qa-db-fra.com

clic droit menu contextuel pour datagridview

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.

103
user34537

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

    }
}
133
Stuart Helwig

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    
        }
    }
}
76
ShortFuse

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);
        }
    }
}
43
Matt
  • Mettez un menu contextuel sur votre formulaire, nommez-le, définissez les légendes, etc. à l'aide de l'éditeur intégré
  • Liez-le à votre grille en utilisant la propriété grid ContextMenuStrip
  • Pour votre grille, créez un événement pour gérer CellContextMenuStripNeeded
  • Les événements ont des propriétés utiles 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.

38
ActualRandy

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é.

4
Captain Comic

Suis les étapes:

  1. Créez un menu contextuel tel que: Sample context menu

  2. 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:

Final output

4
Kshitij Jhangra

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);
    }
3
Gers0n