c# - form - Columna ComboBox de DataGridView: ¿Cambiar el valor de la celda después de realizar la selección desde el menú desplegable?
propiedades de datagridview (12)
He configurado un ComboBoxColumn para mi DataGridView y establezco sus valores seleccionables de una enumeración. Sobre todo funciona como quisiera con la siguiente excepción.
Cada vez que hago clic en la flecha de la lista desplegable y luego selecciono uno de los valores de enumeración, permanece en una especie de estado "intermedio" donde el evento CellValueChanged no se activa. Necesito concentrarme en otra celda u otro control para que el evento se dispare.
También tengo un controlador de eventos para el evento Leaving de DataGridView que "valida" el contenido asegurándome de que no haya ninguna celda vacía.
Entonces, si creo una fila y lleno todas las celdas y llego a la columna ComboBox (actualmente en blanco), la cambio a un valor y luego hago clic en el botón Ejecutar; mi diálogo de error aparece porque la selección de ComboBox no fue "guardada".
¿Cómo puedo evitar esto? ¿Hay alguna forma de que después de seleccionar un valor del menú desplegable, automáticamente "establezca" el valor?
¡Gracias!
Así es como resolví el problema.
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
Debe usar CellValueChanged, que activa el evento de cambio en la cuadrícula y dentro del evento, debe confirmar los cambios y dejar el control para guardar el elemento después de que se haya seleccionado.
private void FilterdataGrid_CellValueChanged(object sender, DataGridViewCellEventArgs e)
{
FilterdataGrid.CommitEdit(DataGridViewDataErrorContexts.Commit);
FilterdataGrid.EndEdit(DataGridViewDataErrorContexts.LeaveControl);
}
¡Espero eso ayude!
El evento CurrentCellDirtyStateChanged solucionó la interacción del mouse para este problema, pero interrumpe la interacción del teclado: con F4 y luego las flechas arriba / abajo, cada clic de flecha produce un cambio de estado sucio y confirma la edición. La solución que encontré fue agarrar el "DataGridViewComboBoxEditingControl" cuando se creó, y adjuntarle un evento DropDownClosed. Esto funciona para la interacción del teclado y el ratón. En este ejemplo, extendimos DataGridView para que cada instancia heredara esta funcionalidad:
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();
}
}
En algunos casos, el valor no se mantendrá hasta que el enfoque haya abandonado completamente la fila. En ese caso, la única forma de forzar la finalización de la edición actual es finalizarla en todo el contexto de enlace:
mGridView.CommitEdit(DataGridViewDataErrorContexts.Commit);
mGridView.BindingContext[mGridView.DataSource].EndCurrentEdit(); // <<===
He encontrado este consejo here .
Estoy agregando mi respuesta como seguimiento a la discusión que ya se ha producido. Estaba tratando de construir un DataGridView que tuviera diferentes comboboxes por fila. También tenían que responder a un solo clic. Y, cuando se realizó la selección, otra celda de la fila debía cambiarse de acuerdo con la selección del cuadro combinado. El cambio debía ocurrir tan pronto como se hiciera la selección. Mi principal problema, como los OP, fue que el cambio no sucedería hasta que el combobox perdiera el enfoque.
Por lo tanto, aquí hay un ejemplo mínimo de tal DataGridView. Tuve que reducirlo al mínimo porque hacer que todos mis requisitos funcionen al mismo tiempo fue complicado. Se hicieron varias publicaciones de SO para hacer esto, y actualizaré mi publicación con referencias más adelante. Pero por ahora, aquí va ...
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;
}
}
}
}
Extendería la respuesta de Moop marcando el tipo de celda en lugar del tipo de columna.
dataGridView1.CurrentCellDirtyStateChanged += dataGridView1_CurrentCellDirtyStateChanged;
void dataGridView1_CurrentCellDirtyStateChanged(object sender, EventArgs e)
{
if (CurrentCell is DataGridViewComboBoxCell)
{
dataGridView1.CommitEdit(DataGridViewDataErrorContexts.Commit);
dataGridView1.EndEdit();
}
}
Extendería la respuesta de ionden comprobando si DataGridViewColumn
es el tipo de DataGridViewComboBoxColumn
antes de forzar el CommitEdit
. Esto evitará que otros objetos DataGridViewColumn
se comprometan demasiado pronto.
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);
}
}
Gracias a Droj por la sugerencia sobre EndCurrentEdit, que necesitaba para que funcionara para mí. Esto es lo que terminé haciendo para cometer instantáneamente DataGridViewComboBoxColumns y 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();
}
Pasé como dos horas buscando un error porque no noté que el valor de la celda no se guarda si no está desenfocado, o mejor dicho, me di cuenta de que la celda no está desenfocada porque el cuadro combinado se apagó mientras se guardaba ( evento btn). No solo eso, EditOnEnter-Mode prevalece en que la mayoría de los otros métodos mostrados arriba funcionan. La razón para usar EditOnEnter es que cuando usa DataGridViewComboBoxColumn, debe hacer clic dos veces para abrir el menú desplegable si no establece EditMode en EditOnEnter.
this.dataGridView.EditMode = DataGridViewEditMode.EditOnKeystrokeOrF2; this.dataGridView.EndEdit(); this.dataGridView.EditMode = DataGridViewEditMode.EditOnEnter;
Espero que esto ayude. Me costó alrededor de dos horas preguntarme por qué el valor en el objeto no es el mismo que el que se muestra en la GUI.
Un problema que vi: no funcionará si eliges: GridView.EditMode = System.Windows.Forms.DataGridViewEditMode.EditOnEnter;
Debería usar el evento CurrentCellDirtyStateChanged
y forzar una edición de confirmación en la cuadrícula:
private void dataGridView1_CurrentCellDirtyStateChanged(object sender, EventArgs e)
{
dataGridView1.CommitEdit(DataGridViewDataErrorContexts.Commit);
}
¡Espero eso ayude!
void dataGridView1_CurrentCellDirtyStateChanged(object sender, EventArgs e)
{
dataGridView1.BeginEdit(true);
ComboBox cmbMiCtrl=(ComboBox)dataGridView1.EditingControl;
string Valor= cmbMiCtrl.Text;
dataGridView1.EndEdit();
}