DataGridView associato a un dizionario

Ho un Dictionary che contiene oggetti e prezzi. Gli articoli sono unici ma vengono lentamente aggiunti e aggiornati durante la vita dell’applicazione (ovvero, non conosco le stringhe dell’articolo in anticipo). Vorrei associare questa struttura a un DataGridView, così posso mostrare gli aggiornamenti sul mio Form, qualcosa del tipo:

 Dictionary _priceData = new Dictionary(); BindingSource _bindingSource = new BindingSource(); dataGridView1.DataSource = _bindingSource; _bindingSource.DataSource = _priceData; 

Ma non può, dal momento che Dictionary non implementa IList (o IListSource , IBindingList o IBindingListView ).

C’è un modo per ottenere questo? Devo conservare un elenco univoco di elementi, ma anche aggiornare il prezzo di un articolo esistente, quindi un Dictionary è la struttura dati ideale, ma non riesco a trovare un modo per visualizzare i dati sul mio modulo.


Aggiornare:

Il seguente suggerimento di Marc funziona molto bene, ma non sono ancora sicuro di come aggiornare DataGridView durante l’esecuzione.

Ho una variabile a livello di class:

 private DictionaryBindingList bList; 

Quindi creare un’istanza in Main() :

 bList = new DictionaryBindingList(prices); dgv.DataSource = bList; 

Quindi durante l’esecuzione del programma se una nuova voce viene aggiunta al dizionario:

 prices.Add("foobar", 234.56M); bList.ResetBindings(); 

Ho pensato che avrebbe aggiornato il DataGridView. Perchè no?

Ci sono un paio di problemi con il Dictionary ; il primo è (come hai trovato) non implementa il necessario IList / IListSource . Il secondo è che non esiste un ordine garantito per gli oggetti (e in effetti, nessun indicizzatore), rendendo imansible l’accesso casuale per indice (piuttosto che per chiave).

Comunque … è probabilmente fattibile con alcuni fumi e specchi; qualcosa di simile qui sotto:

 using System; using System.Collections.Generic; using System.ComponentModel; using System.Windows.Forms; static class Program { [STAThread] static void Main() { Dictionary prices = new Dictionary(); prices.Add("foo", 123.45M); prices.Add("bar", 678.90M); Application.EnableVisualStyles(); Form form = new Form(); DataGridView dgv = new DataGridView(); dgv.Dock = DockStyle.Fill; form.Controls.Add(dgv); var bl = prices.ToBindingList(); dgv.DataSource = bl; Button btn = new Button(); btn.Dock = DockStyle.Bottom; btn.Click += delegate { prices.Add(new Random().Next().ToString(), 0.1M); bl.Reset(); }; form.Controls.Add(btn); Application.Run(form); } public static DictionaryBindingList ToBindingList(this IDictionary data) { return new DictionaryBindingList(data); } public sealed class Pair { private readonly TKey key; private readonly IDictionary data; public Pair(TKey key, IDictionary data) { this.key = key; this.data = data; } public TKey Key { get { return key; } } public TValue Value { get { TValue value; data.TryGetValue(key, out value); return value; } set { data[key] = value; } } } public class DictionaryBindingList : BindingList> { private readonly IDictionary data; public DictionaryBindingList(IDictionary data) { this.data = data; Reset(); } public void Reset() { bool oldRaise = RaiseListChangedEvents; RaiseListChangedEvents = false; try { Clear(); foreach (TKey key in data.Keys) { Add(new Pair(key, data)); } } finally { RaiseListChangedEvents = oldRaise; ResetBindings(); } } } } 

Si noti che l’uso di un metodo di estensione personalizzato è del tutto facoltativo e può essere rimosso in C # 2.0, ecc. Utilizzando semplicemente il new DictionaryBindingList(prices) .

Oppure, in LINQ, è bello e veloce:

 var _priceDataArray = from row in _priceData select new { Item = row.Key, Price = row.Value }; 

Dovrebbe quindi essere associabile, alle colonne “Item” e “Price”.

Per utilizzarlo come origine dati in una visualizzazione griglia, devi solo seguirlo con ToArray() .

 dataGridView1.DataSource = _priceDataArray.ToArray(); 

Probabilmente questo è il modo più semplice:

 Dictionary myList = new Dictionary(); dataGridView1.Columns.Add("Key", "KEY"); dataGridView1.Columns.Add("Values", "VALUES"); foreach (KeyValuePair item in , myList) { dataGridView1.Rows.Add(item.Key, item.Value); } 

Se lo usi, la visualizzazione di dati deve essere ordinabile.

Per il Dictionary puoi utilizzare queste parole chiave per l’associazione: Key e Value .

Ecco un esempio di ComboBox Binding, ma è ansible associare il dizionario a DataGridView (imposta DataPropertyName per la colonna su Key o Value ).

  ComboBox1.DataSource = new BindingSource(Pricelevel.GetPricelevels(), null); // GetPricelevels() returns Dictionary ComboBox1.ValueMember = "Key"; ComboBox1.DisplayMember = "Value"; 

Io penso che questo risolverà il tuo problema che ho affrontato pochi mesi fa.

usa dictionay come vuoi aggiornare i prezzi degli articoli e solo quando hai finito l’aggiornamento e vuoi mostrarlo in datagrid, fai semplicemente questo. la speranza ti aiuterà

 Grd.DataSource=null; Grd.DataSource = Dictionary.Values.ToList(); 

Fai una class in questo modo:

 class MyRow { public string key; public double value; public string Key {get {return key;}} public string Value {get {return value;}} } 

Quindi fai un elenco di loro:

 List rows = new List(); 

Quindi inserirli in quell’elenco e fare il databind alla lista.

Per inciso, se hai LINQ, penso che ci sia un metodo ToArray che semplificherà tutto questo …

Come estensione del suggerimento di Marc, vorrei proporre la seguente soluzione che consentirà anche la manipolazione in fase di esecuzione del dizionario:

 public class DictionaryBindingList : BindingList> { public readonly IDictionary Dictionary; public DictionaryBindingList() { Dictionary = new Dictionary(); } public void Add(TKey key, TValue value) { base.Add(new KeyValuePair(key, value)); } public void Remove(TKey key) { var item = this.First(x => x.Key.Equals(key)); base.Remove(item); } protected override void InsertItem(int index, KeyValuePair item) { Dictionary.Add(item.Key, item.Value); base.InsertItem(index, item); } protected override void RemoveItem(int index) { Dictionary.Remove(this[index].Key); base.RemoveItem(index); } public int IndexOf(TKey key) { var item = this.FirstOrDefault(x => x.Key.Equals(key)); return item.Equals(null) ? -1 : base.IndexOf(item); } } 

Come estensione di Bleiers DictionaryBindingList ho apportato una piccola modifica per consentire ai valori Aggiungi di sovrascrivere i valori esistenti. Sto usando il metodo con una websocket WAMP in modo da permettermi di mantenere i valori aggiornati semplicemente aggiornando la collezione, quindi ho bisogno di bind gli eventi ai valori.

  public void Add(TKey key, TValue value) { if (Dictionary.ContainsKey(key)) { int position = IndexOf(key); Dictionary.Remove(key); Remove(key); InsertItem(position, new KeyValuePair(key, value)); return; } base.Add(new KeyValuePair(key, value)); }