[英]DataGridView bound to a Dictionary
我有一本包含物品和价格的Dictionary
。 这些项目是唯一的,但会在应用程序的整个生命周期内慢慢添加和更新(也就是说,我事先不知道这些项目字符串)。 我想将此结构绑定到DataGridView,以便可以在Form上显示更新,例如:
Dictionary<string, double> _priceData = new Dictionary<string, double>();
BindingSource _bindingSource = new BindingSource();
dataGridView1.DataSource = _bindingSource;
_bindingSource.DataSource = _priceData;
但是不能,因为Dictionary
不实现IList
(或IListSource
, IBindingList
或IBindingListView
)。
有没有办法做到这一点? 我需要保留唯一的商品列表,还需要更新现有商品的价格,因此我认为Dictionary
是理想的数据结构,但是我找不到在表单上显示数据的方法。
更新:
Marc的以下建议非常有效,但是我仍然不确定执行期间如何更新DataGridView。
我有一个类级变量:
private DictionaryBindingList<string, decimal> bList;
然后在Main()
实例化它:
bList = new DictionaryBindingList<string,decimal>(prices);
dgv.DataSource = bList;
然后在程序执行过程中,是否将新条目添加到字典中:
prices.Add("foobar", 234.56M); bList.ResetBindings();
我以为那会刷新DataGridView。 为什么不?
或者,在LINQ中,它又好又快:
var _priceDataArray = from row in _priceData select new { Item = row.Key, Price = row.Value };
然后应该可以绑定到“项目”和“价格”列。
要将其用作网格视图中的数据源,只需跟随ToArray()
。
dataGridView1.DataSource = _priceDataArray.ToArray();
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();
}
}
}
}
请注意,自定义扩展方法的使用完全是可选的,并且可以在C#2.0等中删除,只需使用new DictionaryBindingList<string,decimal>(prices)
。
可能这是最简单的方法:
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应该是可排序的。
我认为这可以解决您几个月前遇到的问题。
使用dictionay来更新商品价格,并且当您完成更新并想在datagrid中显示时,只需执行此操作即可。 希望对你有帮助
Grd.DataSource=null;
Grd.DataSource = Dictionary.Values.ToList();
对于Dictionary<TKey, TValue>
您可以使用以下关键字进行绑定: Key
和Value
。
这是ComboBox
绑定的示例,但是可以将字典绑定到DataGridView
(将列的DataPropertyName
设置为Key
或Value
)。
ComboBox1.DataSource =
new BindingSource(Pricelevel.GetPricelevels(), null); // GetPricelevels() returns Dictionary<string, string>
ComboBox1.ValueMember = "Key";
ComboBox1.DisplayMember = "Value";
作为Marc建议的扩展,我想提出以下解决方案,该解决方案还允许对字典进行运行时操作:
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);
}
}
像这样上课:
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>();
然后将它们插入该列表,并与该列表进行数据绑定。
ToArray
说一句,如果您有LINQ,我认为有一个ToArray
方法可以简化所有步骤。
作为Bleiers DictionaryBindingList的扩展,我做了一个小改动,以允许Add值覆盖现有值。 我在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));
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.