我想在 Windows 窗体中使用
Dictionary<string, TData>
显示 DataGridView
。导致我这样做的特定用例如下:字典键是列名,代表月份,数据类型是十进制。
所以在那种情况下,对于每一列,我们都有一个月份和一个相应的十进制数。我希望 DataGridView 显示月份的列并对字典的条目执行数据绑定。
显然这有另一个简单的解决方案:创建一个包含 12 个属性的视图模型,每个属性的类型为 decimal 代表每个月。创建一个 DataGridView 并执行到该视图模型的传统数据绑定,数据源是此类视图模型对象的列表。
但这很乏味。我们需要创建一堆可以通过使用字典自动化的东西。
我的问题是列需要根据字典动态创建,数据绑定必须以这种方式完成。
我在谷歌上搜索了一下,找到了
Binding
类,它允许创建绑定。但我不知道如何使用它来将动态创建的列绑定到字典中的条目。
如何做到这一点?
ICustomTypeDescriptor
才能在DataGridView
或PropertyGrid
中编辑字典。
选项 1 - 实施 ICustomTypeDescriptor
您可以实现
ICustomTypeDescriptor
,然后您可以在DataGridView
中编辑字典。您可以使用 this 实现并进行一些小的更改。然后你可以这样简单地编辑字典:
Dictionary<string, int> dictionary;
public void Form1_Load(object sender, EventArgs e)
{
dictionary = new Dictionary<string, int>() { { "A", 1 }, { "B", 2 }, { "C", 3 } };
dataGridView1.DataSource = new BindingSource(new DictionaryAdapter(dictionary) , "");
}
或者,如果您愿意,可以将其设置为
SelectedObject
的PropertyGrid
:
propertyGrid1.SelectedObject = new DictionaryAdapter(dictionary);
这里是实现:
public class DictionaryAdapter : ICustomTypeDescriptor
{
IDictionary dictionary;
public DictionaryAdapter(IDictionary d)
{
dictionary = d;
}
public string GetComponentName()
{
return TypeDescriptor.GetComponentName(this, true);
}
public EventDescriptor GetDefaultEvent()
{
return TypeDescriptor.GetDefaultEvent(this, true);
}
public string GetClassName()
{
return TypeDescriptor.GetClassName(this, true);
}
public EventDescriptorCollection GetEvents(Attribute[] attributes)
{
return TypeDescriptor.GetEvents(this, attributes, true);
}
EventDescriptorCollection System.ComponentModel.ICustomTypeDescriptor.GetEvents()
{
return TypeDescriptor.GetEvents(this, true);
}
public TypeConverter GetConverter()
{
return TypeDescriptor.GetConverter(this, true);
}
public object GetPropertyOwner(PropertyDescriptor pd)
{
return dictionary;
}
public AttributeCollection GetAttributes()
{
return TypeDescriptor.GetAttributes(this, true);
}
public object GetEditor(Type editorBaseType)
{
return TypeDescriptor.GetEditor(this, editorBaseType, true);
}
public PropertyDescriptor GetDefaultProperty()
{
return null;
}
PropertyDescriptorCollection
System.ComponentModel.ICustomTypeDescriptor.GetProperties()
{
return ((ICustomTypeDescriptor)this).GetProperties(new Attribute[] { });
}
public PropertyDescriptorCollection GetProperties(Attribute[] attributes)
{
ArrayList properties = new ArrayList();
foreach (DictionaryEntry e in dictionary)
{
properties.Add(new DictionaryPropertyDescriptor(dictionary,
e.Key.ToString()));
}
PropertyDescriptor[] props =
(PropertyDescriptor[])properties.ToArray(typeof(PropertyDescriptor));
return new PropertyDescriptorCollection(props);
}
}
public class DictionaryPropertyDescriptor : PropertyDescriptor
{
IDictionary dictionary;
string key;
internal DictionaryPropertyDescriptor(IDictionary d, string k)
: base(k.ToString(), null)
{
dictionary = d;
key = k;
}
public override Type PropertyType
{
get { return dictionary[key].GetType(); }
}
public override void SetValue(object component, object value)
{
dictionary[key] = value;
}
public override object GetValue(object component)
{
return dictionary[key];
}
public override bool IsReadOnly
{
get { return false; }
}
public override Type ComponentType
{
get { return null; }
}
public override bool CanResetValue(object component)
{
return false;
}
public override void ResetValue(object component)
{
}
public override bool ShouldSerializeValue(object component)
{
return false;
}
}
选项 2 - 使用数据表
此外,作为一个简单的选项,您可以将
Dictionary
塑造成 DataTable
并编辑数据。
您可以为此任务创建扩展方法:
public static class DictionaryExtensions
{
public static DataTable ToDataTable<T>(this Dictionary<string, T> dictionary)
{
var dt = new DataTable();
dictionary.Keys.ToList().ForEach(x => dt.Columns.Add(x, typeof(T)));
dt.Rows.Add(dictionary.Values.Cast<object>().ToArray());
return dt;
}
public static void UpdateFromDataTable<T>(this Dictionary<string, T> dictionary,
DataTable table)
{
if (table.Rows.Count == 1)
table.Columns.Cast<DataColumn>().ToList().ForEach(x =>
dictionary[x.ColumnName] = table.Rows[0].Field<T>(x.ColumnName));
}
}
并以这种方式使用这些扩展方法:
Dictionary<string, int> dictionary;
public void Form1_Load(object sender, EventArgs e)
{
dictionary = new Dictionary<string, int>() { { "A", 1 }, { "B", 2 }, { "C", 3 } };
dataGridView1.DataSource = dictionary.ToDataTable();
}
private void button1_Click(object sender, EventArgs e)
{
dictionary.UpdateFromDataTable(dataGridView1.DataSource as DataTable);
}
我在 VB.net 中使用这个例程
Sub CopyDictToGrid(dict As Dictionary(Of String, String), grid As DataGridView)
Dim dt As New DataTable("DataTableName")
Dim column As DataColumn
column = New DataColumn()
column.DataType = System.Type.GetType("System.String")
column.ColumnName = "nr"
column.Caption = "Nr."
column.ReadOnly = True
column.Unique = False
dt.Columns.Add(column)
' Create second column.
column = New DataColumn()
column.DataType = System.Type.GetType("System.String")
column.ColumnName = "naam"
column.AutoIncrement = False
column.Caption = "Eigenaar"
column.ReadOnly = True
column.Unique = False
dt.Columns.Add(column)
' Create DataRow objects and add
For Each kvp As KeyValuePair(Of String, String) In dict
Dim row As DataRow = dt.NewRow()
row("nr") = kvp.Key
row("naam") = kvp.Value
dt.Rows.Add(row)
Next
grid.DataSource = dt
grid.Columns("nr").Resizable = 1 ' 0 none, 1 true, 2 false
grid.Columns("naam").Resizable = 1
End Sub