2009-05-12 22 views
25

Tengo un Dictionary que contiene elementos 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 enlazar esta estructura a un DataGridView, por lo que puede mostrar cambios en mi forma, algo así como:DataGridView vinculado a un diccionario

Dictionary<string, double> _priceData = new Dictionary<string, double>(); 
BindingSource _bindingSource = new BindingSource(); 
dataGridView1.DataSource = _bindingSource; 
_bindingSource.DataSource = _priceData; 

pero no puede, desde Dictionary no implementa IList (o IListSource, IBindingList, o IBindingListView).

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


Actualización: sugerencia

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

que tienen una variable de nivel de clase:

private DictionaryBindingList<string, decimal> bList; 

Entonces instanciar que en Main():

bList = new DictionaryBindingList<string,decimal>(prices); 
dgv.DataSource = bList; 

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

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

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

+3

como un lado - para "precio" normalmente debería usar decimal, no doble –

+0

Actualizado su comentario; a menos que subclasifiquemos el diccionario <,>, no hay una manera simple de saber que ha agregado un elemento; en lugar de esto, he agregado un método Reset() (con uso de ejemplo) que rellena la lista. Vea el botón en la parte inferior del formulario para ver un ejemplo. –

Respuesta

21

Hay un par de problemas con Dictionary; el primero es (como has encontrado) no implementa el necesario IList/IListSource. 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 ... es posible con algo de humo y espejos; algo parecido a continuación:

using System; 
using System.Collections.Generic; 
using System.ComponentModel; 
using System.Windows.Forms; 

static class Program 
{ 
    [STAThread] 
    static void Main() 
    { 
     Dictionary<string, decimal> prices = 
      new Dictionary<string, decimal>(); 
     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<TKey, TValue> 
     ToBindingList<TKey, TValue>(this IDictionary<TKey, TValue> data) 
    { 
     return new DictionaryBindingList<TKey, TValue>(data); 
    } 
    public sealed class Pair<TKey, TValue> 
    { 
     private readonly TKey key; 
     private readonly IDictionary<TKey, TValue> data; 
     public Pair(TKey key, IDictionary<TKey, TValue> 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<TKey, TValue> 
     : BindingList<Pair<TKey, TValue>> 
    { 
     private readonly IDictionary<TKey, TValue> data; 
     public DictionaryBindingList(IDictionary<TKey, TValue> data) 
     { 
      this.data = data; 
      Reset(); 
     } 
     public void Reset() 
     { 
      bool oldRaise = RaiseListChangedEvents; 
      RaiseListChangedEvents = false; 
      try 
      { 
       Clear(); 
       foreach (TKey key in data.Keys) 
       { 
        Add(new Pair<TKey, TValue>(key, data)); 
       } 
      } 
      finally 
      { 
       RaiseListChangedEvents = oldRaise; 
       ResetBindings(); 
      } 
     } 

    } 
} 

Tenga en cuenta que el uso de un método de extensión personalizada es totalmente opcional, y se puede quitar en C# 2.0, etc. sólo por el uso new DictionaryBindingList<string,decimal>(prices) lugar.

1

hacer una clase de esta manera:

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

Luego haga una lista de ellos:

List<MyRow> rows = new List<MyRow>(); 

Entonces insertarlos en esa lista, y databind a la lista.

Como un aparte, si tienes LINQ, creo que hay un método ToArray que simplificará todo esto ...

+0

Las listas no hacen cumplir fácilmente la singularidad, y no es fácil actualizar un valor en una estructura de lista así, por lo que prefiero usar un diccionario. Gracias sin embargo. – WillH

47

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

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

que luego debe ser capaz de unirse, a las columnas 'Elemento' y 'precio'.

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

dataGridView1.DataSource = _priceDataArray.ToArray(); 
+7

+1 Mucho más simple y limpio que la respuesta aceptada. – GuiSim

+2

+1 ¡Funcionó para mí! ¡Fácil como un pastel (no pi)! –

+0

¿Fácil como un pastel? De hecho, encuentro que el pie es bastante difícil de conseguir: P –

2

Para Dictionary<TKey, TValue> Puede utilizar estas palabras clave para la unión: Key y Value.

Aquí es ejemplo para ComboBox vinculante, pero es posible enlazar diccionario para DataGridView (DataPropertyName establecido para la columna a Key o Value).

ComboBox1.DataSource = 
     new BindingSource(Pricelevel.GetPricelevels(), null); // GetPricelevels() returns Dictionary<string, string> 

    ComboBox1.ValueMember = "Key"; 
    ComboBox1.DisplayMember = "Value"; 
2

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

use dictionay como quiera 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. espero le ayudará a

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

Probablemente esto es manera más fácil:

Dictionary<char, double> myList = new Dictionary<char, double>(); 

     dataGridView1.Columns.Add("Key", "KEY"); 
     dataGridView1.Columns.Add("Values", "VALUES"); 

     foreach (KeyValuePair<char,double> item in , myList) 
     { 
      dataGridView1.Rows.Add(item.Key, item.Value); 
     } 

Si el uso de este dataGridView que será clasificable.

1

Como una extensión a 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<TKey, TValue> : BindingList<KeyValuePair<TKey, TValue>> 
{ 
    public readonly IDictionary<TKey, TValue> Dictionary; 
    public DictionaryBindingList() 
    { 
     Dictionary = new Dictionary<TKey, TValue>(); 
    } 

    public void Add(TKey key, TValue value) 
    { 
     base.Add(new KeyValuePair<TKey, TValue>(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<TKey, TValue> 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); 
    } 
} 
0

Como una extensión de Bleiers DictionaryBindingList hice una pequeña alteración de permite Agregar valores para sobrescribir 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 debo 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<TKey, TValue>(key, value)); 
      return; 
     } 
     base.Add(new KeyValuePair<TKey, TValue>(key, value)); 
    } 
Cuestiones relacionadas