DataGridView绑定到一个Dictionary

我有一个包含项目和价格的Dictionary 。 这些项目是独一无二的,但在应用程序的整个生命周期中慢慢地被添加和更新(也就是说,我事先不知道项目string)。 我想将这个结构绑定到一个DataGridView,所以我可以在我的窗体上显示更新,如下所示:

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

但是不能,因为Dictionary没有实现IList (或者IListSourceIBindingList或者IBindingListView )。

有没有办法做到这一点? 我需要保持一个唯一的项目列表,但也更新现有项目的价格,所以一个Dictionary是我想的理想的数据结构,但我无法find一种方法来显示我的表单上的数据。


更新:

下面的Marc的build议很好地工作,但我仍然不知道如何在执行过程中更新DataGridView。

我有一个类级variables:

 private DictionaryBindingList<string, decimal> bList; 

然后在Main()实例化:

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

然后在程序执行过程中,如果新的条目被添加到字典中:

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

我想这会刷新DataGridView。 为什么不?

Dictionary有几个问题; 第一个(就像你发现的那样)它没有实现必要的IList / IListSource 。 第二个是没有保证顺序的项目(实际上,没有索引),使索引(而不是按键)随机访问不可能。

但是,这可能是一些烟雾和镜子, 如下所示:

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

请注意,自定义扩展方法的使用是完全可选的,只需使用new DictionaryBindingList<string,decimal>(prices) ,就可以在C#2.0中删除。

或者,在LINQ中,它很好,很快:

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

那应该是可以绑定的,到'物品'和'价格'这两列。

要在网格视图中将其用作数据源,只需使用ToArray()

 dataGridView1.DataSource = _priceDataArray.ToArray(); 

可能这是最简单的方法:

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

如果使用这个,你的datagridview应该是可sorting的。

对于Dictionary<TKey, TValue>您可以使用这些关键字进行绑定: KeyValue

这里是ComboBox Binding的例子,但是可以将字典绑定到DataGridView (将DataPropertyName的列设置为KeyValue )。

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

做一个这样的class级:

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

然后列出他们的名单:

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

然后将它们插入到列表中,并将数据绑定到列表中。

另外,如果你有LINQ,我想有一个ToArray方法可以简化所有这些…

我的事情,这将解决你我几个月前面临的问题。

使用dictionay,因为你想更新项目价格,只是当你完成更新,并希望在datagrid中显示只是做到这一点。 希望能帮助你

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

作为对Marcbuild议的扩展,我想提出下面的解决scheme,它也允许运行时对字典进行操作:

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

作为Bleiers DictionaryBindingList的扩展,我做了一个小改动来允许添加值来覆盖现有的值。 我正在使用WAMP websocket的方法,这样我就可以通过更新集合来更新值,接下来我需要将事件绑定到值上。

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