web-dev-qa-db-fra.com

Colonne DataGridView ComboBox: Modifier la valeur de la cellule après la sélection dans la liste déroulante?

J'ai configuré un ComboBoxColumn pour mon DataGridView et défini ses valeurs sélectionnables à partir d'une énumération. Cela fonctionne principalement comme je le voudrais à l'exception suivante.

Chaque fois que je clique sur la flèche de la liste déroulante, puis que je sélectionne l'une des valeurs enum, il reste dans une sorte d'état "intermédiaire" dans lequel l'événement CellValueChanged n'est pas déclenché. Je dois me concentrer sur une autre cellule ou un autre contrôle pour que l'événement se déclenche.

J'ai également un gestionnaire d'événements pour l'événement Leaving de DataGridView qui "valide" le contenu en s'assurant qu'aucune cellule n'est vide.

Par conséquent, si je crée une ligne, que je remplis toutes les cellules et que je parviens à la colonne ComboBox (actuellement vide), modifiez-la en une valeur, puis cliquez sur un bouton Exécuter; ma boîte de dialogue d'erreur s'affiche car la sélection de la liste déroulante n'a pas été "enregistrée".

Comment puis-je contourner cela? Y at-il un moyen qu'après avoir sélectionné une valeur dans la liste déroulante, celle-ci "définit" automatiquement la valeur?

Merci!

16
john

Vous devez utiliser l'événement CurrentCellDirtyStateChanged et forcer une édition de validation dans la grille:

    private void dataGridView1_CurrentCellDirtyStateChanged(object sender, EventArgs e)
    {
        dataGridView1.CommitEdit(DataGridViewDataErrorContexts.Commit);
    }

J'espère que ça aide!

22
ionden

J'étendrais la réponse de Moop en vérifiant le type de cellule au lieu du type de colonne.

dataGridView1.CurrentCellDirtyStateChanged += dataGridView1_CurrentCellDirtyStateChanged;

void dataGridView1_CurrentCellDirtyStateChanged(object sender, EventArgs e)
{
    if (CurrentCell is DataGridViewComboBoxCell)
    {
        dataGridView1.CommitEdit(DataGridViewDataErrorContexts.Commit);
        dataGridView1.EndEdit();
    }
}
12
Bioukh

Je voudrais étendre la réponse de ionden en vérifiant si la DataGridViewColumn est le type de DataGridViewComboBoxColumn avant de forcer la CommitEdit. Cela empêchera d’autres objets DataGridViewColumn de s’engager trop tôt.

    dataGridView1.CurrentCellDirtyStateChanged += dataGridView1_CurrentCellDirtyStateChanged;

    void dataGridView1_CurrentCellDirtyStateChanged(object sender, EventArgs e)
    {
        DataGridViewColumn col = dataGridView1.Columns[dataGridView1.CurrentCell.ColumnIndex];
        if (col is DataGridViewComboBoxColumn)
        {
            dataGridView1.CommitEdit(DataGridViewDataErrorContexts.Commit);
        }
    }
4
Moop

L'événement CurrentCellDirtyStateChanged a corrigé l'interaction de la souris pour ce problème, mais il rompt l'interaction au clavier: en utilisant F4, puis la flèche vers le haut/le bas, chaque clic sur la flèche entraîne un changement d'état incorrect et valide la modification. La solution que j'ai trouvée consistait à saisir le "DataGridViewComboBoxEditingControl" lors de sa création et à lui associer un événement DropDownClosed. Cela fonctionne pour l'interaction clavier et souris. Dans cet exemple, nous avons étendu DataGridView afin que chaque instance hérite de cette fonctionnalité:

    protected override void OnEditingControlShowing(DataGridViewEditingControlShowingEventArgs e)
    {
        DataGridViewComboBoxEditingControl control = e.Control as DataGridViewComboBoxEditingControl;
        if (control != null)
        {
            control.DropDownClosed -= ComboBoxDropDownClosedEvent;
            control.DropDownClosed += ComboBoxDropDownClosedEvent;
        }
        base.OnEditingControlShowing(e);
    }

    void ComboBoxDropDownClosedEvent(object sender, EventArgs e)
    {
        DataGridViewComboBoxCell cell = CurrentCell as DataGridViewComboBoxCell;
        if ((cell != null) && cell.IsInEditMode)
        {
            CommitEdit(DataGridViewDataErrorContexts.Commit);
            EndEdit();
        }
    }
3
Larry P

Dans certains cas, la valeur ne restera pas en place tant que le focus n'aura pas quitté la ligne. Dans ce cas, le seul moyen de forcer la modification en cours à se terminer est de la terminer sur l'ensemble du contexte de liaison:

mGridView.CommitEdit(DataGridViewDataErrorContexts.Commit);
mGridView.BindingContext[mGridView.DataSource].EndCurrentEdit(); // <<===

J'ai trouvé cette astuce ici .

1
Droj
void dataGridView1_CurrentCellDirtyStateChanged(object sender, EventArgs e)
{
    dataGridView1.BeginEdit(true);
    ComboBox cmbMiCtrl=(ComboBox)dataGridView1.EditingControl;
    string Valor= cmbMiCtrl.Text;
    dataGridView1.EndEdit();
}
0
JxDarkAngel

Voici comment j'ai résolu le problème

Private Sub dgvEcheancier_CurrentCellDirtyStateChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles dgvEcheancier.CurrentCellDirtyStateChanged
        nbreClick += 1
            With dgvEcheancier
                Select Case .CurrentCell.ColumnIndex
                Case 9
                    Dim col As DataGridViewComboBoxColumn = .Columns(9)
                    If TypeOf (col) Is DataGridViewComboBoxColumn Then
                        dgvEcheancier.CommitEdit(DataGridViewDataErrorContexts.Commit)
                        If nbreClick = 2 Then
                            MessageBox.Show("y" & "val=" & .CurrentCell.Value)
                            nbreClick = 0
                        End If
                    End If

            End Select
            End With
0
LANHA

Un problème que j'ai vu: cela ne fonctionnera pas si vous choisissez: GridView.EditMode = System.Windows.Forms.DataGridViewEditMode.EditOnEnter;

0
SeyoS

Je passe environ deux heures à rechercher une erreur car je ne remarquais pas que la valeur de la cellule n’était pas sauvegardée si elle n’était pas défocalisée, ou, mieux vaut dire que je venais de remarquer que la cellule n’était pas défocalisée car la liste déroulante était blanchie lors de la sauvegarde ( btn event). En plus de cela, EditOnEnter-Mode prévaut sur la plupart des autres méthodes présentées ci-dessus. La raison d'utiliser EditOnEnter est que lorsque vous utilisez un DataGridViewComboBoxColumn, vous devez cliquer deux fois pour ouvrir le menu déroulant si vous ne définissez pas EditMode sur EditOnEnter.

this.dataGridView.EditMode = DataGridViewEditMode.EditOnKeystrokeOrF2; this.dataGridView.EndEdit(); this.dataGridView.EditMode = DataGridViewEditMode.EditOnEnter;

J'espère que ça aide. Cela m'a coûté environ deux heures en me demandant pourquoi la valeur de l'objet n'est pas la même que celle indiquée sur l'interface graphique. 

0
Dennis E

Vous devez utiliser CellValueChanged qui déclenche l'événement change dans la grille et à l'intérieur de l'événement, vous devez valider les modifications et quitter le contrôle afin de sauvegarder l'élément après l'avoir sélectionné.

    private void FilterdataGrid_CellValueChanged(object sender, DataGridViewCellEventArgs e)
{
    FilterdataGrid.CommitEdit(DataGridViewDataErrorContexts.Commit);      

    FilterdataGrid.EndEdit(DataGridViewDataErrorContexts.LeaveControl);
}

J'espère que ça aide!

0
Shyqyri Rama

J'ajoute ma réponse pour donner suite à la discussion qui a déjà eu lieu. J'essayais de construire un DataGridView qui avait différents combobox par ligne. Ils devaient également être sensibles à un simple clic. Et, lorsque la sélection a été effectuée, une autre cellule de la ligne devait être modifiée en fonction de la sélection de la liste déroulante. Le changement devait avoir lieu dès que la sélection était faite. Mon problème principal, comme celui des PO, était que le changement ne se produirait pas tant que la liste déroulante n'aurait pas perdu la concentration.

Donc, voici un exemple de travail minimal et complet d’un tel DataGridView. Je devais le réduire au minimum car il était difficile de satisfaire toutes mes exigences en même temps. Plusieurs SO posts ont été créés, et je mettrai à jour mon post avec des références ultérieurement. Mais pour l'instant, voilà ...

using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Forms;

namespace TestDGV
{
public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
    }

    private Panel panel2;
    private DataGridView TestGrid;

    private void InitializeComponent()
    {
        this.panel2 = new System.Windows.Forms.Panel();
        this.SuspendLayout();
        // 
        // panel2
        // 
        this.panel2.Dock = DockStyle.Fill;
        this.panel2.Name = "panel2";
        this.panel2.TabIndex = 1;
        // 
        // Form1
        // 
        this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
        this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
        this.ClientSize = new System.Drawing.Size(661, 407);
        this.Controls.Add(this.panel2);
        this.Name = "Form1";
        this.Text = "Form1";
        this.Load += new System.EventHandler(this.Form1_Load);
        this.ResumeLayout(false);
    }

    private void Form1_Load(object sender, EventArgs e)
    {
        //basic grid properties
        TestGrid = new DataGridView();
        TestGrid.Dock = DockStyle.Fill;
        TestGrid.AutoGenerateColumns = false;
        TestGrid.Name = "TestGrid";
        TestGrid.ReadOnly = false;
        TestGrid.EditMode = DataGridViewEditMode.EditOnEnter;

        //Event handlers
        TestGrid.DataBindingComplete += TestGrid_DataBindingComplete;
        TestGrid.CurrentCellDirtyStateChanged += TestGrid_CurrentCellDirtyStateChanged;
        TestGrid.CellValueChanged += TestGrid_CellValueChanged;

        //columns
        var textCol = new DataGridViewTextBoxColumn();
        textCol.HeaderText = "Text";
        textCol.Name = "Text";
        textCol.DataPropertyName = "Text";
        textCol.AutoSizeMode = DataGridViewAutoSizeColumnMode.AllCells;
        TestGrid.Columns.Add(textCol);

        var comboCol = new DataGridViewComboBoxColumn();
        comboCol.HeaderText = "ComboBox";
        comboCol.Name = "ComboBox";
        comboCol.AutoComplete = true;
        comboCol.AutoSizeMode = DataGridViewAutoSizeColumnMode.AllCells;
        TestGrid.Columns.Add(comboCol);

        var resultCol = new DataGridViewTextBoxColumn();
        resultCol.HeaderText = "Result";
        resultCol.Name = "Result";
        resultCol.DataPropertyName = "Result";
        resultCol.AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill;
        TestGrid.Columns.Add(resultCol);

        //Bind the data
        Datum.TestLoad();
        TestGrid.DataSource = Datum.Data;

        panel2.Controls.Add(TestGrid);
    }

    void TestGrid_CellValueChanged(object sender, DataGridViewCellEventArgs e)
    {
        if (e.RowIndex < 0 || e.ColumnIndex < 0)
            return;

        var row = TestGrid.Rows[e.RowIndex];
        var cell = row.Cells[e.ColumnIndex];
        if (cell is DataGridViewComboBoxCell)
        {
            var val = cell.Value as string;
            var datum = row.DataBoundItem as Datum;
            datum.Current = val;
            row.Cells["Result"].Value = datum.Result;
            TestGrid.InvalidateRow(e.RowIndex);
        }
    }


    void TestGrid_CurrentCellDirtyStateChanged(object sender, EventArgs e)
    {
        if(TestGrid.CurrentCell is DataGridViewComboBoxCell)
        {
            TestGrid.CommitEdit(DataGridViewDataErrorContexts.Commit);
            TestGrid.EndEdit();
        }
    }

    void TestGrid_DataBindingComplete(object sender, DataGridViewBindingCompleteEventArgs e)
    {
        foreach (DataGridViewRow row in TestGrid.Rows)
        {
            var datum = row.DataBoundItem as Datum;
            if (datum == null)
                return;

            var cell = row.Cells["ComboBox"] as DataGridViewComboBoxCell;
            if (cell.DataSource == null)
            {
                cell.DisplayMember = "KeyDisplayValue";
                cell.ValueMember = "KeyValue";
                cell.DataSource = (row.DataBoundItem as Datum).Combo;
                cell.Value = (row.DataBoundItem as Datum).Current;
            }
        }
        TestGrid.DataBindingComplete -= TestGrid_DataBindingComplete;
    }

    public class Datum
    {
        public static void TestLoad()
        {
            var t1 = new Triplet[] {
                     new Triplet("1", "World", "Everyone" ),
                     new Triplet("2", "Charlie", "Friend of Algernon" ),
                     new Triplet("3", "Lester", "Phenomenal programmer" ),
            };
            var t2 = new Triplet[] {
                     new Triplet("1", "World", "Everyone" ),
                     new Triplet("4", "Mary", "Wife of George Bailey" ),
                     new Triplet("3", "Lester", "Phenomenal programmer" ),
            };
            Data.Add(new Datum("hello, ", t1.ToList()));
            Data.Add(new Datum("g'bye, ", t2.ToList()));
        }
        public static List<Datum> Data = new List<Datum>();

        public Datum(string text, List<Triplet> combo)
        {
            this._text = text;
            this._combo = combo.ToDictionary<Triplet,string>(o => o.KeyValue);
            this.Current = combo[0].KeyValue;
        }

        private string _text;
        public string Text
        {
            get
            {
                return _text;
            }
        }

        private Dictionary<string, Triplet> _combo;
        public List<Triplet> Combo
        {
            get
            {
                return _combo.Values.ToList();
            }
        }

        private string _result;
        public string Result
        {
            get
            {
                return _result;
            }
        }

        private string _current;
        public string Current
        {
            get
            {
                return _current;
            }
            set
            {
                if (value != null && _combo.ContainsKey(value))
                {
                    _current = value;
                    _result = _combo[value].Description;
                }
            }
        }
    }

    public class Triplet
    {
        public string KeyValue { get; set; }
        public string KeyDisplayValue { get; set; }
        public string Description { get; set; }
        public Triplet(string keyValue, string keyDisplayValue, string description)
        {
            KeyValue = keyValue;
            KeyDisplayValue = keyDisplayValue;
            Description = description;
        }
    }
}
}
0
Les

Merci à Droj pour le conseil concernant EndCurrentEdit, dont j’avais besoin pour que cela fonctionne pour moi. Voici ce que j’ai fait pour valider instantanément DataGridViewComboBoxColumns et DataGridViewCheckBoxColumns:

private void dataGridViewEnumerable_CurrentCellDirtyStateChanged(object sender, EventArgs e)
{
  var dataGridView = sender as DataGridView;
  if (dataGridView == null || dataGridView.CurrentCell == null)
    return;
  var isComboBox = dataGridView.CurrentCell is DataGridViewComboBoxCell;
  if ((isComboBox || dataGridView.CurrentCell is DataGridViewCheckBoxCell) 
    && dataGridView.CommitEdit(DataGridViewDataErrorContexts.Commit) 
    && isComboBox && dataGridView.EndEdit())
    dataGridView.BindingContext[dataGridView.DataSource].EndCurrentEdit();
}
0
Jeremy Thomas