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
(或者IListSource
, IBindingList
或者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>
您可以使用这些关键字进行绑定: Key
和Value
。
这里是ComboBox
Binding的例子,但是可以将字典绑定到DataGridView
(将DataPropertyName
的列设置为Key
或Value
)。
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)); }