DataGridView vinculado a un diccionario

Tengo un Dictionary que contiene artículos y precios. Los elementos son únicos, pero se agregan y actualizan lentamente a lo largo de la vida útil de la aplicación (es decir, no conozco las cadenas de elementos de antemano). Me gustaría vincular esta estructura a DataGridView, para poder mostrar las actualizaciones en mi Formulario, algo así como:

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

Pero no puede, ya que Dictionary no implementa IList (o IListSource , IBindingList o IBindingListView ).

¿Hay una manera de lograr esto? Necesito mantener una lista única de artículos, pero también actualizar el precio de un artículo existente, por lo que un Dictionary es la estructura de datos ideal, creo, pero no puedo encontrar una manera de mostrar los datos en mi formulario.


Actualizar:

La sugerencia de Marc a continuación funciona muy bien, pero todavía no estoy seguro de cómo actualizar DataGridView durante la ejecución.

Tengo una variable de nivel de clase:

 private DictionaryBindingList bList; 

Luego crea una instancia en Main() :

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

Luego, durante la ejecución del progtwig si se agrega una nueva entrada al diccionario:

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

Pensé que refrescaría DataGridView. Por qué no?

Hay un par de problemas con Dictionary ; el primero es (como has encontrado) que no implementa el IList / IListSource necesario. El segundo es que no hay un orden garantizado para los artículos (y, de hecho, ningún indexador), lo que imposibilita el acceso aleatorio por índice (en lugar de por clave).

Sin embargo … probablemente sea factible con algo de humo y espejos; algo como a continuación:

 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(); } } } } 

Tenga en cuenta que el uso de un método de extensión personalizado es completamente opcional, y se puede eliminar en C # 2.0, etc. con solo usar new DictionaryBindingList(prices) lugar.

O, en LINQ, es agradable y rápido:

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

Eso debería ser vinculable, a las columnas ‘Artículo’ y ‘Precio’.

Para utilizarlo como fuente de datos en una vista de cuadrícula, solo tiene que seguirlo con ToArray() .

 dataGridView1.DataSource = _priceDataArray.ToArray(); 

Probablemente esta es la manera más fácil:

 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); } 

Si usa esto su datagridview será ordenable.

Para Dictionary puede usar estas palabras clave para el enlace: Key y Value .

Aquí hay un ejemplo para la vinculación de ComboBox , pero es posible vincular el diccionario a DataGridView (establecer DataPropertyName para la columna en Key o Value ).

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

Creo que esto resolverá su problema al que me enfrenté hace unos meses.

utilice dictionay ya que desea actualizar los precios de los artículos y justo cuando termine la actualización y quiera mostrar en la cuadrícula de datos simplemente haga esto. la esperanza te ayudará

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

Haz una clase así:

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

Luego haz una lista de ellos:

 List rows = new List(); 

Luego insértelos en esa lista, y enlace de datos a la lista.

Por otro lado, si tienes LINQ, creo que hay un método ToArray que simplificará todo esto …

Como una extensión de la sugerencia de Marc, me gustaría proponer la siguiente solución que también permitirá la manipulación en tiempo de ejecución del diccionario:

 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); } } 

Como extensión de Bleiers DictionaryBindingList, realicé una pequeña modificación para permitir que Add values ​​sobrescriba los valores existentes. Estoy usando el método con un websocket WAMP, por lo que me permite mantener los valores actualizados simplemente actualizando la colección, luego necesito vincular los eventos a los valores.

  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)); }