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

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

问题描述

我在 DataGridView 中显示对象列表.一切正常.根据对象的属性自动将列添加到 DataGridView.

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.

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

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.

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

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.

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

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.

为什么 DataGridView 看不到我的 ICustomTypeDescriptor 方法?有没有另一种方法可以将属性动态添加到将在 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 查看元数据的列表版本;这方面的规则……很复杂:

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

  1. 如果数据源实现了 IListSourceGetList() 被评估并用作数据源(在 2 处继续)
  2. 如果数据源实现了ITypedList,则使用GetProperties()获取元数据(退出)
  3. 如果可以找到类型化(非object)索引器(即public T this[int index]),则T是通过 TypeDescriptor.GetProperties(type) 用作源:
  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. 如果分配了 TypeDescriptionProvider,这将用于针对类型(退出)的元数据
  2. 否则反射用于元数据(退出)
  1. if a TypeDescriptionProvider is assigned, the this is used for metadata against the type (exit)
  2. otherwise reflection is used for metadata (exit)

  • 如果列表非空,第一个对象通过TypeDescriptor.GetProperties(list[0])用于元数据:

    1. 如果实现了ICustomTypeDescriptor,则使用它(退出)[*]
    2. 如果分配了 TypeDescriptionProvider,这将用于针对类型的元数据(退出)[*]
    3. 否则使用反射(退出)
    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)

  • 其他元数据不可用(退出)
  • ([*]=我不记得这两个走哪条路了...)

    如果您使用的是 List(或类似的),那么您会遇到最简单"(IMO)的情况 - #3.如果您想提供自定义元数据,那么;最好的办法是编写一个 TypeDescriptionProvider 并将其与类型相关联.我可以写一个例子,但需要一段时间(可能在火车上)...

    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)...

    这里是一个使用 ITypedList 的例子;我会尝试调整它以使用 TypeDescriptionProvider 代替...

    here's an example that uses ITypedList; I'll try to tweak it to use TypeDescriptionProvider instead...

    第二次下面是一个使用 TypeDescriptionProvider 的完整(但最少)示例;长代码警告...

    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天全站免登陆