假设我有一个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;
}
Run Code Online (Sandbox Code Playgroud)
这将显示列表项,列名称为"名称"和"年龄",从列表项的公共属性中巧妙地推断(通过反射).(根据我的测试,第一项.)
但是如果我使用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;
Run Code Online (Sandbox Code Playgroud)
... DataGridView如何知道如何使用DataTable的列而不是其公共属性?DataTable和DataRow似乎都没有实现提供此信息的任何接口.或者DataGridView是否了解DataTable类型,并以不同方式处理这种类型的数据源?
我问的原因是因为我想实现我自己的"动态"数据源类型,它不依赖于固定属性.
迈克尔为我指明了正确的方向。“神奇”的接口是ICustomTypeDescriptor,它是在 DataRowView 中实现的(而不是在 DataRow 中)。使用这些信息以及这篇博客文章,我创建了这个自定义 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); }
}
}
Run Code Online (Sandbox Code Playgroud)
然后我修改了 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);
}
}
Run Code Online (Sandbox Code Playgroud)
现在 GridView 看起来像这样......

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