DataGridView不显示实现ICustomTypeDescriptor的对象的属性 [英] DataGridView not showing properites of objects which implement ICustomTypeDescriptor

查看:165
本文介绍了DataGridView不显示实现ICustomTypeDescriptor的对象的属性的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在 DataGridView 中显示对象列表。一切都很好。根据对象的属性将列自动添加到 DataGridView 中。



现在,我将网格中显示的类更改为实现 ICustomTypeDescriptor 。但是现在,当我将 DataSource 设置为我的自定义对象列表时,现在网格不再显示任何列或行。



我是猜测这与使用 ICustomTypeDescriptor 的事实有关,每个网格的每行显示的每个实例可能会返回一组不同的属性。



我正在实现 ICustomTypeDescriptor ,以便我可以允许用户在运行时向对象动态添加自定义属性。这些自定义属性应该通过 DataGridView 可见和可编辑。



为什么 DataGridView 看不到我的 ICustomTypeDescriptor 方法?有另外一种方法,我可以动态地添加属性到一个将显示在 DataGridView 中的对象。

解决方案

DataGridView 查看元数据的列表版本;这个规则是...复杂:


  1. 如果数据源实现 IListSource GetList()被评估并用作数据源(继续为2)

  2. 如果数据源实现 ITypedList GetProperties()用于获取元数据(退出)

  3. 如果可以找到一个类型(非对象)索引器(即 public T this [int index] ),那么 T 通过 TypeDescriptor.GetProperties(type)用作源代码:


    1. 如果分配了 TypeDescriptionProvider ,则根据类型(退出)使用此元数据

    2. 否则反射用于元数据(退出)


  4. 如果列表不为空,则第一个对象通过 TypeDescriptor.GetProperties(list [0])


    1. if ICustomTypeDescriptor 被实现,然后它被使用(exit)[*]

    2. 如果分配了一个 TypeDescriptionProvider ,则根据类型(exit)[*]

    3. 否则使用反射(退出)


  5. else元数据不可用(退出)

([*] =我不记得这两个方法去哪里了...)



如果您使用列表< T> (或类似),则您打到最简单(IMO)案例 - #3。如果要提供自定义元数据,那么;最好是写一个 TypeDescriptionProvider 并将其与该类型相关联。我可以写一个例子,但需要一段时间(在火车上,大概)...



编辑:这里是一个使用 ITypedList 的示例;我会尝试调整它以使用 TypeDescriptionProvider 而不是...

第二个编辑:一个完整​​的)示例使用 TypeDescriptionProvider 跟随;长代码警告...

  using System; 
使用System.Collections.Generic;
使用System.ComponentModel;
使用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),新的DisplayNameAttribute(出生日期));
BindingList< PropertyBag> with(UserName,Fred)。(DateOfBirth,新的DateTime(1998,12,1)),$ b $ (UserName,William)。(DateOfBirth,新的DateTime(1997,4,23))
};

Application.Run(new Form {
Controls = {
new DataGridView {//证明它适用于复杂的绑定
Dock = DockStyle.Fill,
DataSource = list,
ReadOnly = false,AllowUserToAddRows = true
}
},
DataBindings = {
{Text,list,UserName} / /证明它适用于简单绑定
}
});
}
}
// PropertyBag文件1;核心包
部分类PropertyBag:INotifyPropertyChanged {
private static PropertyDescriptorCollection道具;
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提供者;我避免使用TypeDescriptionProviderAttribute,以便我们
//可以利用默认的实现来获取乐趣和利润
TypeDescriptionProvider defaultProvider = TypeDescriptor.GetProvider(typeof(PropertyBag)),
customProvider = new PropertyBagTypeDescriptionProvider(defaultProvider );
TypeDescriptor.AddProvider(customProvider,typeof(PropertyBag));
}
private static只读对象syncLock = new object();
public static void AddProperty(string name,Type type,params Attribute [] attributes){
lock(syncLock)
{//将新的支持附加到* new *集合中,所以下游
//调用者不必担心复杂性
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字典< string,object>价值观
public PropertyBag()
{//主要是要强制我们有一个public parameterless ctor
values = new Dictionary< string,object>();
}
public object this [string key] {
get {
if(string.IsNullOrEmpty(key))throw new ArgumentNullException(key);
对象值;
values.TryGetValue(key,out value);
返回值;
}
set {
if(string.IsNullOrEmpty(key))throw new ArgumentNullException(key);
var prop = props [key];
if(prop == null)throw new ArgumentException(Invalid property:+ key,key);
值[key] = value;
OnPropertyChanged(key);
}
}
internal void重置(字符串键){
values.Remove(key);
}
内部bool ShouldSerialize(string key){
return values.ContainsKey(key);
}
}

静态类PropertyBagExt
{
// cheeky流畅的API使示例代码更容易:
public static PropertyBag With (此PropertyBag对象,字符串名称,对象值){
obj [name] = value;
return obj;
}
}

// PropertyBag文件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();
}
对象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; //应该真的被过滤了,但是呃!
}
PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties(){
return PropertyBag.props;
}
对象ICustomTypeDescriptor.GetPropertyOwner(PropertyDescriptor pd){
return defaultDescriptor.GetPropertyOwner(pd);
}
}
}
// PropertyBag文件3:属性描述符
partial class PropertyBag {
class PropertyBagPropertyDescriptor:PropertyDescriptor {
private readonly类型;
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类型PropertyType {
get {return type; }
}
public override bool IsReadOnly {
get {return false; }
}
public override Type ComponentType {
get {return typeof(PropertyBag); }
}
}
}


I'm displaying a list of objects in a DataGridView. Everything was working fine. Columns were automagicaly added to the DataGridView based on the properties of the objects.

Now I changed the class I'm displaying in the grid to implement ICustomTypeDescriptor. But now the grid now no longer shows any columns or rows when I set it's DataSource to a list of my custom object.

I'm guessing this has something to do with the fact that with ICustomTypeDescriptor each instance shown in each row of each grid could be returning a different set of properties.

I'm implementing ICustomTypeDescriptor so that I can allow the users to dynamically add custom properties to objects at run time. These custom properties should be visible and editable through the DataGridView.

Why does DataGridView not see my ICustomTypeDescriptor methods? Is there another way that I can dynamically add properties to an object that will be displayed in a DataGridView?

解决方案

DataGridView looks at the list version of metadata; the rules for this are... complex:

  1. if the data-source implements IListSource, GetList() is evaluated and used as the data-source (continue at 2)
  2. if the data-source implements ITypedList, GetProperties() is used to obtain metadata (exit)
  3. if a typed (non-object) indexer can be found (i.e. public T this[int index]), then T is used as the source via TypeDescriptor.GetProperties(type):

    1. if a TypeDescriptionProvider is assigned, the this is used for metadata against the type (exit)
    2. otherwise reflection is used for metadata (exit)

  4. if the list is non-empty, the first object is used for metadata via TypeDescriptor.GetProperties(list[0]):

    1. if ICustomTypeDescriptor is implemented, then it is used (exit) [*]
    2. if a TypeDescriptionProvider is assigned, the this is used for metadata against the type (exit) [*]
    3. otherwise reflection is used (exit)

  5. else metadata is unavailable (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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆