DataGridView ComboBox colonne: changer la valeur de la cellule après la sélection de dropdown est fait?
j'ai configuré une ComboBoxColumn pour mon DataGridView et défini ses valeurs sélectionnables à partir d'une énumération. Il fonctionne la plupart du temps comme je voudrais avec l'exception suivante.
chaque fois que je clique sur la flèche déroulante et que je sélectionne une des valeurs enum, elle reste dans une sorte d'état "intermédiaire" où 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 tire.
j'ai aussi un gestionnaire d'événement pour l' DataGridView quitte l'événement qui "valide" le contenu en s'assurant qu'aucune cellule n'est vide.
donc, si je crée une ligne et que je remplis toutes les cellules et que j'arrive à la colonne ComboBox (actuellement vide), changez-la en valeur, puis cliquez sur un bouton Exécuter; ma boîte de dialogue erreur s'affiche parce que la sélection ComboBox n'a pas été "sauvegardée".
Comment puis-je contourner cela? Est-il possible que quand je sélectionne une valeur dans la liste déroulante automatiquement en "séries" de la de la valeur?
Merci!
12 réponses
Vous devez utiliser CurrentCellDirtyStateChanged
événement et la force d'un commit modifier sur la grille:
private void dataGridView1_CurrentCellDirtyStateChanged(object sender, EventArgs e)
{
dataGridView1.CommitEdit(DataGridViewDataErrorContexts.Commit);
}
j'Espère que ça aide!
je voudrais étendre 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();
}
}
je voudrais étendre ionden réponse en vérifiant si le DataGridViewColumn
est le type de DataGridViewComboBoxColumn
avant de forcer l' CommitEdit
. Cela permettra d'éviter d'autres DataGridViewColumn
objets de commettre 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);
}
}
l'évènement modifié a changé l'interaction de la souris fixe pour ce numéro, mais il casse l'interaction du clavier - en utilisant F4 puis la flèche Haut/Bas, chaque clic de flèche entraîne un changement d'état sale et engage l'édition. La solution que j'ai trouvée, était de saisir le "Datagridviewcombobeditingcontrol" quand il est créé, et y attacher un événement DropDownClosed. Cela fonctionne pour l'interaction clavier et souris. Dans cet exemple, nous avons étendu DataGridView pour que chaque instance hérite de ceci 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();
}
}
dans certains cas, la valeur ne tiendra pas tant que la mise au point n'aura pas complètement quitté la rangée. Dans ce cas, la seule façon de forcer la fin de l'édition courante est de la terminer sur l'ensemble du contexte contraignant:
mGridView.CommitEdit(DataGridViewDataErrorContexts.Commit);
mGridView.BindingContext[mGridView.DataSource].EndCurrentEdit(); // <<===
j'ai trouvé cette astuce ici.
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
void dataGridView1_CurrentCellDirtyStateChanged(object sender, EventArgs e)
{
dataGridView1.BeginEdit(true);
ComboBox cmbMiCtrl=(ComboBox)dataGridView1.EditingControl;
string Valor= cmbMiCtrl.Text;
dataGridView1.EndEdit();
}
Un problème que j'ai vu : Il ne fonctionnera pas si vous choisissez :
GridView.EditMode = System.Windows.Forms.DataGridViewEditMode.EditOnEnter;
je passe deux heures à chercher une erreur parce que je n'ai pas remarqué que la valeur de la cellule ne soit pas sauvegardée si elle n'est pas désamorcée, ou mieux pour dire que je viens de remarquer que la cellule n'est pas désamorcée parce que le combobox s'est dégommé tout en sauvegardant(événement btn). Non seulement cela, le mode EditOnEnter prévaut que la plupart des autres méthodes indiquées ci-dessus fonctionnent. La raison d'utiliser EditOnEnter est que lorsque vous utilisez une DataGridViewComboBoxColumn, vous devez cliquer deux fois pour ouvrir le menu déroulant si vous ne définissez pas EditMode to EditOnEnter.
this.dataGridView.EditMode = DataGridViewEditMode.EditOnKeystrokeOrF2;
this.dataGridView.EndEdit();
this.dataGridView.EditMode = DataGridViewEditMode.EditOnEnter;
j'espère que cette aide. Il m'a coûté environ deux heures en se demandant pourquoi la valeur de l'objet n'est pas la même alors affichées sur l'interface graphique.
j'ajoute ma réponse pour faire suite à la discussion qui a déjà eu lieu. J'essayais de construire un DataGridView qui avait des ComboBox différents par ligne. Ils devaient aussi être sensible à un seul clic. Et, lorsque la sélection a été faite, une autre cellule dans la rangée a dû être changée selon la sélection de combobox. Le changement devait se produire dès que la sélection était faite. Mon problème principal, comme celui de L'OP, était que le changement n'aurait pas lieu tant que le combobox n'aurait pas perdu concentrer.
donc, voici un exemple de minimum de fonctionnement complet d'un tel DataGridView. J'ai dû le réduire au minimum parce qu'il était difficile de faire fonctionner toutes mes exigences en même temps. Plusieurs messages SO sont allés dans ce faire, et je vais mettre à jour mon post avec des références plus tard. Mais pour l'instant, va ici...
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;
}
}
}
}
Vous devez utiliser CellValueChanged qui déclenche l'événement de changement sur la grille et à l'intérieur de l'événement vous devez propager les changements et quitter le contrôle afin de sauvegarder l'élément une fois qu'il est sélectionné.
private void FilterdataGrid_CellValueChanged(object sender, DataGridViewCellEventArgs e)
{
FilterdataGrid.CommitEdit(DataGridViewDataErrorContexts.Commit);
FilterdataGrid.EndEdit(DataGridViewDataErrorContexts.LeaveControl);
}
j'Espère que ça aide!
merci à Droj pour le tuyau sur EndCurrentEdit, dont j'avais besoin pour le faire fonctionner pour moi. C'est ce que j'ai fini par faire pour commettre 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();
}