winforms 如何使用DataGridView显示字典?

c8ib6hqw  于 2023-03-03  发布在  其他
关注(0)|答案(2)|浏览(162)

我想使用DataGridView在Windows窗体中显示Dictionary<string, TData>。导致我这样做的特定用例如下:字典键是表示月份的列名,数据类型是十进制。
因此,在这种情况下,对于每一列,我们都有一个月份和一个对应的十进制数。我希望DataGridView显示月份的列,并执行到字典条目的数据绑定。
显然,还有另一个简单的解决方案:创建一个包含12个属性的视图模型,每个属性的类型为decimal,每个属性对应一个月。创建一个DataGridView,并执行到该视图模型的传统数据绑定,数据源是这样的视图模型对象的列表。
但这很乏味,我们需要创建一堆可以通过使用字典自动化的东西。
我的问题是,列需要基于字典动态创建,数据绑定也必须以这种方式完成。
我在谷歌上搜索了一下,找到了Binding类,它允许创建绑定,但我不知道如何使用它来将动态创建的列绑定到字典中的条目。
如何才能做到这一点?

tez616oj

tez616oj1#

您需要实现ICustomTypeDescriptor,这样字典才能在DataGridViewPropertyGrid中编辑。

选项1 -实施IC自定义类型描述符

您可以实现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) , "");
}

或者,如果您愿意,可以将其设置为PropertyGridSelectedObject

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

wztqucjr2#

我将此例程用于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

相关问题