简体   繁体   English

如何使用DataGridView显示字典?

[英]How to display a Dictionary using DataGridView?

I want to display a Dictionary<string, TData> in Windows Forms using a DataGridView . 我想使用DataGridView在Windows窗体中显示Dictionary<string, TData> The particular use case that lead me to that was the following: the dictionary key is the column name, representing a month, and the data type is decimal. 导致这种情况的特定用例如下:字典键是列名,代表一个月,数据类型是十进制。

So in that case, for each column we have a month and a corresponding decimal number. 因此,在这种情况下,对于每一列,我们都有一个月和一个对应的十进制数字。 I wanted the DataGridView to display the columns for the months and perform a data binding to the entries of the dictionary. 我希望DataGridView显示月份的列,并执行对字典条目的数据绑定。

Obviously this has another trivial solution: create a view model containing 12 properties, each of type decimal for each month. 显然,这还有另一个简单的解决方案:创建一个包含12个属性的视图模型,每个属性每个月的十进制类型。 Create a DataGridView and perform a traditional data binding to this view model, with the data source being a list of such view model objects. 创建一个DataGridView并执行与该视图模型的传统数据绑定,数据源是此类视图模型对象的列表。

But this is tedious. 但这很乏味。 We need to create a bunch of things that can be automated by using the dictionary. 我们需要创建一堆可以通过使用字典来自动化的东西。

My problem is that the columns will need to be dynamically created based on the dictionary and the data binding will have to be done in that way. 我的问题是,将需要根据字典动态创建列,并且必须以这种方式完成数据绑定。

I googled a little on that and found the Binding class, which allows to create bindings. 我对此进行了一些搜索,发现了Binding类,该类允许创建绑定。 But I don't know how to use this to bind dynamically created columns to entries in a dictionary. 但是我不知道如何使用它来将动态创建的列绑定到字典中的条目。

How can this be done? 如何才能做到这一点?

You need to implement ICustomTypeDescriptor for the dictionary to be editable in DataGridView or PropertyGrid . 您需要实现ICustomTypeDescriptor以便字典可以在DataGridViewPropertyGrid进行编辑。

Option 1 - Implement ICustomTypeDescriptor 选项1-实作ICustomTypeDescriptor

You can implement ICustomTypeDescriptor and then you can edit the dictionary in DataGridView . 您可以实现ICustomTypeDescriptor ,然后可以在DataGridView编辑字典。 You can use this implementation with some small changes. 您可以通过一些小的更改使用实现。 Then you can simply edit dictionary this way: 然后,您可以通过以下方式简单地编辑字典:

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

Or if you prefer, you can set it as SelectedObject of a PropertyGrid : 或者,如果愿意,可以将其设置为PropertyGrid SelectedObject

propertyGrid1.SelectedObject =  new DictionaryAdapter(dictionary);

Here is the implementation: 这是实现:

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

Option 2 - Use a DataTable 选项2-使用数据表

Also as a simple option you can shape the Dictionary to a DataTable and edit data. 另外,作为一个简单的选项,您可以将Dictionary成形为DataTable并编辑数据。

You can create extension methods for this task: 您可以为此任务创建扩展方法:

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

And use these extension methods this way: 并以这种方式使用这些扩展方法:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM