数据绑定如何与DataTable一起使用?

时间:2013-12-10 16:01:46

标签: c# .net winforms

假设我有一个WinForms DataGridView控件,我将数据绑定到自定义类型的IList,如下所示:

public class Person
{
    public string Name { get; set; }
    public int Age { get; set; }
}

private void Form1_Load(object sender, EventArgs e)
{
    var data = new ArrayList();

    data.Add(new Person("Bob", 25));
    data.Add(new Person("Alice", 23));

    this.dataGridView1.DataSource = data;
}

这将显示列表名称为“名称”和“年龄”的列表项,从列表项的公共属性中巧妙地推断(通过反射)。 (根据我的测试,第一项。)

但如果我使用DataTable做同样的事情:

DataTable dt = new DataTable();

dt.Columns.Add("Name");
dt.Columns.Add("Row");

dt.Rows.Add("Bob", 25);
dt.Rows.Add("Alice", 23);

this.dataGridView1.DataSource = dt;

... DataGridView如何知道如何使用DataTable的列而不是公共属性? DataTable和DataRow似乎都没有实现提供此信息的任何接口。或者DataGridView是否了解DataTable类型,并以不同方式处理这种类型的数据源?

我问的原因是因为我想实现自己的“动态”数据源类型,而不依赖于固定属性。

3 个答案:

答案 0 :(得分:3)

迈克尔pointed me朝着正确的方向前进。 “魔术”界面是ICustomTypeDescriptor,它在DataRowView中实现(不在DataRow中)。使用此信息以及this blog post我创建了这个自定义PropertyDescriptor类:

public class MyPropertyDescriptor<TComponent, TValue> : PropertyDescriptor
{
    private readonly Func<TComponent, TValue> getter;

    private readonly Action<TComponent, TValue> setter;

    public MyPropertyDescriptor(string name, Func<TComponent, TValue> getter, Action<TComponent, TValue> setter)
        : base(name, null)
    {
        this.getter = getter;
        this.setter = setter;
    }

    public override bool CanResetValue(object component)
    {
        return true;
    }

    public override object GetValue(object component)
    {
        return getter((TComponent)component);
    }

    public override void ResetValue(object component)
    {
        setter((TComponent)component, default(TValue));
    }

    public override void SetValue(object component, object value)
    {
        setter((TComponent)component, (TValue)value);
    }

    public override bool ShouldSerializeValue(object component)
    {
        return true;
    }

    public override Type ComponentType
    {
        get { return typeof(TComponent); }
    }

    public override bool IsReadOnly
    {
        get { return false; }
    }

    public override Type PropertyType
    {
        get { return typeof(TValue); }
    }
}

然后我修改了Person类以实现ICustomTypeDescriptor:

public class Person : ICustomTypeDescriptor 
{
    public string Name { get; set; }

    public int Age { get; set; }

    /* ... Unimplemented ICustomTypeDescriptor methods left out ... */

    public PropertyDescriptorCollection GetProperties(Attribute[] attributes)
    {
        PropertyDescriptor[] propertyDescriptors = new PropertyDescriptor[]
                                                    {
                                                        new MyPropertyDescriptor<Person, string>("My Name", p => p.Name, (p, s) => p.Name = s), 
                                                        new MyPropertyDescriptor<Person, int>("My Age", p => p.Age, (p, i) => p.Age = i)
                                                    };

        return new PropertyDescriptorCollection(propertyDescriptors);
    }
}

现在GridView看起来像这样......

enter image description here

当然,这只是一个测试它有效的样本。在生产代码中,类型描述符可能不会映射到公共属性,尽管很高兴看到它们也可以用于自定义列显示名称(在类成员标识符中不允许使用空格和其他字符)。

答案 1 :(得分:2)

DataGridView的DataSource方法支持IListSource接口,例如DataTable和DataSet。

如果需要,此MSDN文章会提供更多信息。

答案 2 :(得分:1)

GridView1.DataSource = dt;
GridView1.DataBind();