Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

DataGridView bound to a Dictionary

I have a Dictionary that contains items and prices. The items are unique but slowly get added and updated through the lifetime of the application (that is, I don't know the item strings in advance). I would like to bind this structure to a DataGridView, so I can show updates on my Form, something like:

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

But cannot, since Dictionary does not implement IList (or IListSource, IBindingList, or IBindingListView).

Is there a way to achieve this? I need to keep a unique list of items, but also update the price for an existing item, so a Dictionary is the ideal data structure I think, but I cannot find a way to display the data on my Form.


Update:

Marc's suggestion below works very nicely, but I'm still not sure how to update the DataGridView during execution.

I have a class-level variable:

private DictionaryBindingList<string, decimal> bList;  

Then instantiate that in Main():

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

Then during program execution if a new entry is added to the dictionary:

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

I thought that would refresh the DataGridView. Why not?

like image 432
WillH Avatar asked May 12 '09 21:05

WillH


2 Answers

Or, in LINQ, it's nice and quick:

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

That should then be bindable, to the columns 'Item' and 'Price'.

To use it as a data source in a grid view, you just have to follow it with ToArray().

dataGridView1.DataSource = _priceDataArray.ToArray(); 
like image 101
Chris Avatar answered Sep 20 '22 06:09

Chris


There are a couple of issues with Dictionary; the first is (as you've found) it doesn't implement the necessary IList/IListSource. The second is that there is no guaranteed order to the items (and indeed, no indexer), making random access by index (rather than by key) impossible.

However... it is probably doable with some some smoke and mirrors; something like below:

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

Note that the use of a custom extension method is entirely optional, and can be removed in C# 2.0, etc. by just using new DictionaryBindingList<string,decimal>(prices) instead.

like image 20
Marc Gravell Avatar answered Sep 24 '22 06:09

Marc Gravell