Come associare un DataGridViewComboBoxColumn a un oggetto?
-
08-07-2019 - |
Domanda
Sto cercando di associare un DataGridViewComboBoxColumn
a un'istanza di Foo, ma quando ho impostato un valore sulla griglia ho ricevuto un ArgumentException
che mi dice che non posso convertire da String a Foo.
var data = (from item in someTable
select new { Foo = item.foo, Bar = item.Bar }).ToList();
grid.DataSource = data;
column.DataPropertyName = "Foo";
column.DataSource = (from foo in Foo select foo).ToList (); //foo is an instance of Foo
column.DisplayMember = "SomeNameField"; //Foo.SomeNameField contains a description of the instance
Mi sto perdendo qualcosa? è possibile associare un database a un oggetto complesso?
UPDATE:
Ho implementato un TypeConverter e ho ignorato CanConvertFrom, CanConvertTo, ConvertTo, ConvertFrom. Ora sto ricevendo
FormatException: il valore DataGridViewComboBoxCell non è valido
Qualche idea?
Soluzione
Ti manca un possibile pezzo.
column.DataPropertyName = "Foo";
column.DisplayMember = "SomeNameField";
column.ValueMember = "Bar"; // must do this, empty string causes it to be
// of type string, basically the display value
// probably a bug in .NET
column.DataSource = from foo in Foo select foo;
grid.DataSource = data;
UPDATE:
In realtà, dopo aver letto di nuovo la tua domanda, penso che stai affrontando quel bug notato. Sfortunatamente non c'è modo di farlo restituire l'oggetto associato senza usare un TypeDescriptor / TypeConverter / BindingSource personalizzato.
Rispondi per l'associazione a un oggetto complesso. No per impostazione predefinita. Ne ho scritto uno abbastanza carino per il mio progetto attuale. Ciò comporta la creazione di un TypeDescriptor / TypeConverter / BindingSource personalizzato che restituisce tutte le proprietà nidificate. Un altro "bug", non puoi usare "." per un separatore membro, ho dovuto ricorrere a ':' invece.
Altri suggerimenti
DataGridViewComboBoxColumn dovrebbe sempre avere tutti i possibili valori nell'elenco degli elementi della casella combinata o genererà " FormatException: il valore DataGridViewComboBoxCell non è valido " ;.
Se stai cercando di recuperare i valori scelti da una colonna della casella combinata, puoi gestire l'evento CellGrars di DataGridView e ottenere l'elemento selezionato da DataGridView.EditingControl perché verrà impostato per il controllo di modifica dalla colonna modificata. Ecco un esempio:
private void dataGridView1_CellParsing(object sender,
DataGridViewCellParsingEventArgs e) {
if (dataGridView1.CurrentCell.OwningColumn is DataGridViewComboBoxColumn) {
DataGridViewComboBoxEditingControl editingControl =
(DataGridViewComboBoxEditingControl)dataGridView1.EditingControl;
e.Value = editingControl.SelectedItem;
e.ParsingApplied = true;
}
}
Puoi anche personalizzare il modo in cui i tuoi oggetti sono mostrati su ogni cella gestendo l'evento Formatting della cella, ecco un codice che mostra a String per qualsiasi oggetto o interfaccia.
private void dataGridView1_CellFormatting(object sender,
DataGridViewCellFormattingEventArgs e) {
if (e.Value != null) {
e.Value = e.Value.ToString();
e.FormattingApplied = true;
}
}
Gestisce questi due eventi dovrebbe essere sufficiente per mostrare e modificare i dati all'interno di qualsiasi oggetto bussiness ed è più semplice scrivere convertitori di tipi. Per questo lavoro impostare DataGridView e la colonna della casella combinata come segue:
var data = (from item in someTable
select new { Foo = item.foo, Bar = item.Bar }).ToList();
grid.DataSource = data;
column.DataPropertyName = "Foo";
column.DataSource = (from foo in Foo select foo).ToList ();
Non è necessario impostare alcuna proprietà DisplayMember o ValueMember, assicurati solo che l'elenco delle origini dati combobox abbia tutti i valori possibili per Foo.
Spero che sia d'aiuto.
In realtà, puoi usare un tipo complesso in DataGridViewComboBoxColumn
.
Ad esempio:
DataGridViewComboBoxColumn.DataPropertyName = "ValueMode";
DataGridViewComboBoxColumn.DisplayMember = "Label";
DataGridViewComboBoxColumn.ValueMember = "Self"; *
DataGridViewComboBoxColumn.ValueType = typeof(ValueModeItem);
Self
è:
public ValueModeItem Self
{
get
{
return this;
}
}
Molto importante: è necessario sostituire il metodo "Uguali" di un tipo complesso. Nel mio caso:
public override bool Equals(object obj)
{
if (obj is ValueModeItem && obj != null)
{
if (...)
return true;
}
return false;
}
Ho costantemente riscontrato lo stesso problema fino a quando non ho scoperto che non è possibile impostare il DisplayMember
del DataGridViewComboBoxCell
senza impostare anche il ValueMember
. < br>
Allo stesso modo, l'impostazione del ValueMember
e non del DisplayMember
non è riuscita, è necessario definire nessuno o entrambi.
Il tuo modello è Foo e sicuramente desideri che il valore di ComboBox sia l'elemento stesso. Per fare questo, il modo più semplice è anche creare una proprietà nel tuo foo, restituendo se stessa.
public class Foo
{
...
public Foo This { get {return this; } }
}
Quindi i binding diventano:
column.DataPropertyName = "Foo";
column.DataSource = (from foo in Foo select foo).ToList (); //foo is an instance of Foo
column.DisplayMember = "SomeNameField"; //Foo.SomeNameField contains a description of the instance
column.ValueMember = "This";
Questo dovrebbe funzionare e il valore della cella dovrebbe essere del tipo Foo come previsto.
Un riferimento interessante: Problemi con DataGridViewComboBoxColumn
Tuttavia, DataGridViewComboBoxColumn non funziona in questo modo, sebbene visualizzerà il valore ToString se non si imposta DisplayMember, qualcosa va storto internamente quando cerca di apparire su SelectedItem, devi impostare DisplayMember su un pubblico proprietà della tua classe. Ancora peggio, il comportamento predefinito se non lo fai impostare la proprietà ValueMember è di restituire DisplayMember, c'è nessun modo di ottenere l'oggetto reale stesso. L'unica soluzione è aggiungere a proprietà della classe che restituisce se stessa e imposta tale proprietà su ValueMember. Certo, se il tuo articolo non è qualcosa che puoi per cambiare (come una delle classi del framework) dovrai rannicchiarti insieme un oggetto contenitore che contiene un riferimento al tuo articolo.