DataGridView不显示实现ICustomTypeDescriptor的对象的属性 [英] DataGridView not showing properites of objects which implement ICustomTypeDescriptor
问题描述
我在 DataGridView 中显示对象列表。一切都很好。根据对象的属性将列自动添加到 DataGridView 中。
现在,我将网格中显示的类更改为实现 ICustomTypeDescriptor 。但是现在,当我将 DataSource 设置为我的自定义对象列表时,现在网格不再显示任何列或行。
我是猜测这与使用 ICustomTypeDescriptor 的事实有关,每个网格的每行显示的每个实例可能会返回一组不同的属性。
我正在实现 ICustomTypeDescriptor ,以便我可以允许用户在运行时向对象动态添加自定义属性。这些自定义属性应该通过 DataGridView 可见和可编辑。
为什么 DataGridView 看不到我的 ICustomTypeDescriptor 方法?有另外一种方法,我可以动态地添加属性到一个将显示在 DataGridView 中的对象。
DataGridView
查看元数据的列表版本;这个规则是...复杂:
- 如果数据源实现
IListSource
,GetList()
被评估并用作数据源(继续为2) - 如果数据源实现
ITypedList
,GetProperties()
用于获取元数据(退出) - 如果可以找到一个类型(非
对象
)索引器(即public T this [int index]
),那么T
通过TypeDescriptor.GetProperties(type)
用作源代码:
- 如果分配了
TypeDescriptionProvider
,则根据类型(退出)使用此元数据 - 否则反射用于元数据(退出)
- 如果分配了
- 如果列表不为空,则第一个对象通过
TypeDescriptor.GetProperties(list [0])
:
- if
ICustomTypeDescriptor
被实现,然后它被使用(exit)[*] - 如果分配了一个
TypeDescriptionProvider
,则根据类型(exit)[*] - 否则使用反射(退出)
- if
- else元数据不可用(退出)
([*] =我不记得这两个方法去哪里了...)
如果您使用列表< T>
(或类似),则您打到最简单(IMO)案例 - #3。如果要提供自定义元数据,那么;最好是写一个 TypeDescriptionProvider
并将其与该类型相关联。我可以写一个例子,但需要一段时间(在火车上,大概)...
- if a
TypeDescriptionProvider
is assigned, the this is used for metadata against the type (exit) - otherwise reflection is used for metadata (exit)
TypeDescriptor.GetProperties(list[0])
:
- if
ICustomTypeDescriptor
is implemented, then it is used (exit) [*] - if a
TypeDescriptionProvider
is assigned, the this is used for metadata against the type (exit) [*] - otherwise reflection is used (exit)
([*]=I can't remember which way around these two go...)
If you are using List<T>
(or similar), then you hit the "simplest" (IMO) case - #3. If you want to provide custom metadata, then; you best bet is to write a TypeDescriptionProvider
and associate it with the type. I can write an example but it'll take a while (on the train, probably)...
Edit: here's an example that uses ITypedList
; I'll try to tweak it to use TypeDescriptionProvider
instead...
Second edit: a full (yet minimal) example using TypeDescriptionProvider
follows; long code warning...
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Windows.Forms;
// example
static class Program {
[STAThread]
static void Main() {
PropertyBag.AddProperty("UserName", typeof(string), new DisplayNameAttribute("User Name"));
PropertyBag.AddProperty("DateOfBirth", typeof(DateTime), new DisplayNameAttribute("Date of Birth"));
BindingList<PropertyBag> list = new BindingList<PropertyBag>() {
new PropertyBag().With("UserName", "Fred").With("DateOfBirth", new DateTime(1998,12,1)),
new PropertyBag().With("UserName", "William").With("DateOfBirth", new DateTime(1997,4,23))
};
Application.Run(new Form {
Controls = {
new DataGridView { // prove it works for complex bindings
Dock = DockStyle.Fill,
DataSource = list,
ReadOnly = false, AllowUserToAddRows = true
}
},
DataBindings = {
{"Text", list, "UserName"} // prove it works for simple bindings
}
});
}
}
// PropertyBag file 1; the core bag
partial class PropertyBag : INotifyPropertyChanged {
private static PropertyDescriptorCollection props;
public event PropertyChangedEventHandler PropertyChanged;
void OnPropertyChanged(string propertyName) {
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
static PropertyBag() {
props = new PropertyDescriptorCollection(new PropertyDescriptor[0], true);
// init the provider; I'm avoiding TypeDescriptionProviderAttribute so that we
// can exploit the default implementation for fun and profit
TypeDescriptionProvider defaultProvider = TypeDescriptor.GetProvider(typeof(PropertyBag)),
customProvider = new PropertyBagTypeDescriptionProvider(defaultProvider);
TypeDescriptor.AddProvider(customProvider, typeof(PropertyBag));
}
private static readonly object syncLock = new object();
public static void AddProperty(string name, Type type, params Attribute[] attributes) {
lock (syncLock)
{ // append the new prop, into a *new* collection, so that downstream
// callers don't have to worry about the complexities
PropertyDescriptor[] newProps = new PropertyDescriptor[props.Count + 1];
props.CopyTo(newProps, 0);
newProps[newProps.Length - 1] = new PropertyBagPropertyDescriptor(name, type, attributes);
props = new PropertyDescriptorCollection(newProps, true);
}
}
private readonly Dictionary<string, object> values;
public PropertyBag()
{ // mainly want to enforce that we have a public parameterless ctor
values = new Dictionary<string, object>();
}
public object this[string key] {
get {
if (string.IsNullOrEmpty(key)) throw new ArgumentNullException("key");
object value;
values.TryGetValue(key, out value);
return value;
}
set {
if (string.IsNullOrEmpty(key)) throw new ArgumentNullException("key");
var prop = props[key];
if (prop == null) throw new ArgumentException("Invalid property: " + key, "key");
values[key] = value;
OnPropertyChanged(key);
}
}
internal void Reset(string key) {
values.Remove(key);
}
internal bool ShouldSerialize(string key) {
return values.ContainsKey(key);
}
}
static class PropertyBagExt
{
// cheeky fluent API to make the example code easier:
public static PropertyBag With(this PropertyBag obj, string name, object value) {
obj[name] = value;
return obj;
}
}
// PropertyBag file 2: provider / type-descriptor
partial class PropertyBag {
class PropertyBagTypeDescriptionProvider : TypeDescriptionProvider, ICustomTypeDescriptor {
readonly ICustomTypeDescriptor defaultDescriptor;
public PropertyBagTypeDescriptionProvider(TypeDescriptionProvider parent) : base(parent) {
this.defaultDescriptor = parent.GetTypeDescriptor(typeof(PropertyBag));
}
public override ICustomTypeDescriptor GetTypeDescriptor(Type objectType, object instance) {
return this;
}
AttributeCollection ICustomTypeDescriptor.GetAttributes() {
return defaultDescriptor.GetAttributes();
}
string ICustomTypeDescriptor.GetClassName() {
return defaultDescriptor.GetClassName();
}
string ICustomTypeDescriptor.GetComponentName() {
return defaultDescriptor.GetComponentName();
}
TypeConverter ICustomTypeDescriptor.GetConverter() {
return defaultDescriptor.GetConverter();
}
EventDescriptor ICustomTypeDescriptor.GetDefaultEvent() {
return defaultDescriptor.GetDefaultEvent();
}
PropertyDescriptor ICustomTypeDescriptor.GetDefaultProperty() {
return defaultDescriptor.GetDefaultProperty();
}
object ICustomTypeDescriptor.GetEditor(Type editorBaseType) {
return defaultDescriptor.GetEditor(editorBaseType);
}
EventDescriptorCollection ICustomTypeDescriptor.GetEvents(Attribute[] attributes) {
return defaultDescriptor.GetEvents(attributes);
}
EventDescriptorCollection ICustomTypeDescriptor.GetEvents() {
return defaultDescriptor.GetEvents();
}
PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties(Attribute[] attributes) {
return PropertyBag.props; // should really be filtered, but meh!
}
PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties() {
return PropertyBag.props;
}
object ICustomTypeDescriptor.GetPropertyOwner(PropertyDescriptor pd) {
return defaultDescriptor.GetPropertyOwner(pd);
}
}
}
// PropertyBag file 3: property descriptor
partial class PropertyBag {
class PropertyBagPropertyDescriptor : PropertyDescriptor {
private readonly Type type;
public PropertyBagPropertyDescriptor(string name, Type type, Attribute[] attributes)
: base(name, attributes) {
this.type = type;
}
public override object GetValue(object component) {
return ((PropertyBag)component)[Name];
}
public override void SetValue(object component, object value) {
((PropertyBag)component)[Name] = value;
}
public override void ResetValue(object component) {
((PropertyBag)component).Reset(Name);
}
public override bool CanResetValue(object component) {
return true;
}
public override bool ShouldSerializeValue(object component) {
return ((PropertyBag)component).ShouldSerialize(Name);
}
public override Type PropertyType {
get { return type; }
}
public override bool IsReadOnly {
get { return false; }
}
public override Type ComponentType {
get { return typeof(PropertyBag); }
}
}
}
这篇关于DataGridView不显示实现ICustomTypeDescriptor的对象的属性的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!