DataGridView中没有显示它们实现ICustomTypeDescriptor对象的性质在 [英] DataGridView not showing properites of objects which implement ICustomTypeDescriptor

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

问题描述

我在 DataGridView的显示对象的列表。一切都工作正常。柱automagicaly添加到 DataGridView的基于对象的属性。

现在我改变了我的显示类在网格实施 ICustomTypeDescriptor 。但现在的网格现在不再显示我的自定义对象的列表的列或当我设置它的行数据源

我猜这事做的事实,是 ICustomTypeDescriptor 每个网格中的每一行中显示每个实例可以返回一组不同的属性。

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

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


解决方案

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


  1. 如果该数据源实现 IListSource 的GetList()进行评估,并作为数据源(继续2)

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

  3. 如果一个类型(非 - 对象)索引,可以发现(即公共牛逼这个[INT指数] ),那么 T 通过 TypeDescriptor.GetProperties用作源(类型)

    1. 如果一个 TypeDescriptionProvider 分配,这将是用于对抗型(出口)的元数据

    2. ,否则反射被用于元数据(出口)


  4. 如果列表非空,第一个对象是通过 TypeDescriptor.GetProperties用于元数据(名单[0])

    1. 如果 ICustomTypeDescriptor 实现,那么它被使用(出口)[*]

    2. 如果一个 TypeDescriptionProvider 分配,这将是用于对抗型(出口)的元数据[*]

    3. 否则,使用反射(出口)


  5. 其他元数据是不可用(出口)

([*] =我不记得这两个左右的路要走...)

如果您使用的是列表< T> (或类似),然后你打的简单(IMO)的情况下 - #3。如果要提供自定义元数据,那么,你最好的赌注是写一个 TypeDescriptionProvider 和准它的类型。我可以写一个例子,但它会需要一段时间(上车,可能)...

编辑:这里的使用的例子 ITypedList ;我会尽量调整它使用 TypeDescriptionProvider ,而不是...

第二个编辑:使用完整的(但很少)例如: TypeDescriptionProvider 如下;长code警告...

 使用系统;
使用System.Collections.Generic;
使用System.ComponentModel;
使用System.Windows.Forms的;
//例子
静态类节目{
    [STAThread]
    静态无效的主要(){
        PropertyBag.AddProperty(用户名,typeof运算(字符串),新DisplayNameAttribute(用户名));
        PropertyBag.AddProperty(出生日期的typeof(DateTime的),新DisplayNameAttribute(出生日期));
        的BindingList<&的PropertyBag GT;名单=新的BindingList<&的PropertyBag GT;(){
            新的PropertyBag()随着​​(用户名,弗雷德)。随着(出生日期,新的datetime(1998,12,1))
            新的PropertyBag()随着​​(用户名,威廉)。随着(出生日期,新的datetime(1997,4,23))
        };        Application.Run(新表格{
            控制= {
                新的DataGridView {//证明它适用于复杂的绑定
                    码头= DockStyle.Fill,
                    数据源=名单,
                    只读=假,AllowUserToAddRows =真
                }
            },
            数据绑定= {
                {文本列表中,用户名} //证明它可以用于简单绑定
            }
        });
    }
}
//的PropertyBag文件1;核心袋
部分类的PropertyBag:INotifyPropertyChanged的{
    私有静态PropertyDescriptorCollection道具;
    公共事件PropertyChangedEventHandler的PropertyChanged;
    无效OnPropertyChanged(字符串propertyName的){
        PropertyChangedEventHandler处理器=的PropertyChanged;
        如果(!=处理空值)处理器(这一点,新PropertyChangedEventArgs(propertyName的));
    }
    静态的PropertyBag(){
        道具=新PropertyDescriptorCollection(新的PropertyDescriptor [0],真);
        //初始化的供应商;我回避TypeDescriptionProviderAttribute让我们
        //可以利用的乐趣和利润的默认实现
        TypeDescriptionProvider defaultProvider = TypeDescriptor.GetProvider(typeof运算(的PropertyBag))
            customProvider =新PropertyBagTypeDescriptionProvider(defaultProvider);
        TypeDescriptor.AddProvider(customProvider的typeof(PropertyBag中));
    }
    私人静态只读对象SYNCLOCK =新的对象();
    公共静态无效的方法addProperty(字符串名称,类型类型,则params属性[]属性){
        锁定(SYNCLOCK)
        {//追加新的道具,成为一个新的* *收集,从而使下游
            //调用者不必担心复杂性
            PropertyDescriptor的[] newProps =新的PropertyDescriptor [props.Count + 1];
            props.CopyTo(newProps,0);
            newProps [newProps.Length - 1] =新PropertyBagPropertyDescriptor(名称,类型,属性);
            道具=新PropertyDescriptorCollection(newProps,真);
        }
    }
    私人只读字典<字符串对象>值;
    公众的PropertyBag()
    {//主要是想强制,我们有一个公共的无参数构造函数
        值=新词典<字符串对象>();
    }
    公共对象本[字符串键] {
        获得{
            如果(string.IsNullOrEmpty(密钥))抛出新的ArgumentNullException(钥匙);
            对象值;
            values​​.TryGetValue(键,超时值);
            返回值;
        }
        集合{
            如果(string.IsNullOrEmpty(密钥))抛出新的ArgumentNullException(钥匙);
            VAR道具=道具[关键]
            如果(道具== NULL)抛出新的ArgumentException(无效的属性:+键,键);
            值[关键] =价值;
            OnPropertyChanged(键);
        }
    }
    内部空隙复位(字符串键){
        values​​.Remove(键);
    }
    内部布尔ShouldSerialize(字符串键){
        返回values​​.ContainsKey(键);
    }
}静态类PropertyBagExt
{
    //厚脸皮流利的API来让这个例子code更容易:
    公共静态的PropertyBag用(这的PropertyBag OBJ,字符串名称,对象的值){
        OBJ [名] =值;
        返回OBJ;
    }
}//的PropertyBag文件2:供应商/类型描述
部分类的PropertyBag {
    类PropertyBagTypeDescriptionProvider:TypeDescriptionProvider,ICustomTypeDescriptor {
        只读ICustomTypeDescriptor defaultDescriptor;
        公共PropertyBagTypeDescriptionProvider(TypeDescriptionProvider父):基地(父){
            this.defaultDescriptor = parent.GetTypeDescriptor(typeof运算(的PropertyBag));
        }
        公众覆盖ICustomTypeDescriptor GetTypeDescriptor(类型的​​objectType,对象实例){
            返回此;
        }
        AttributeCollection ICustomTypeDescriptor.GetAttributes(){
            返回defaultDescriptor.GetAttributes();
        }
        串ICustomTypeDescriptor.GetClassName(){
            返回defaultDescriptor.GetClassName();
        }
        串ICustomTypeDescriptor.GetComponentName(){
            返回defaultDescriptor.GetComponentName();
        }
        TypeConverter的ICustomTypeDescriptor.GetConverter(){
            返回defaultDescriptor.GetConverter();
        }
        EventDescriptor ICustomTypeDescriptor.GetDefaultEvent(){
            返回defaultDescriptor.GetDefaultEvent();
        }
        的PropertyDescriptor ICustomTypeDescriptor.GetDefaultProperty(){
            返回defaultDescriptor.GetDefaultProperty();
        }
        对象ICustomTypeDescriptor.GetEditor(类型editorBaseType){
            返回defaultDescriptor.GetEditor(editorBaseType);
        }
        EventDescriptorCollection ICustomTypeDescriptor.GetEvents(属性[]属性){
            返回defaultDescriptor.GetEvents(属性);
        }
        EventDescriptorCollection ICustomTypeDescriptor.GetEvents(){
            返回defaultDescriptor.GetEvents();
        }
        PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties(属性[]属性){
            返回PropertyBag.props; //应该被过滤,但咩!
        }
        PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties(){
            返回PropertyBag.props;
        }
        对象ICustomTypeDescriptor.GetPropertyOwner(PropertyDescriptor的PD){
            返回defaultDescriptor.GetPropertyOwner(PD);
        }
    }
}
//的PropertyBag文件3:属性描述
部分类的PropertyBag {
    类PropertyBagPropertyDescriptor:PropertyDescriptor的{
        私人只读类型类型;
        公共PropertyBagPropertyDescriptor(字符串名称,类型类型,属性[]属性)
            :碱(名称,属性){
            this.type =类型;
        }
        公众覆盖对象的GetValue(对象组件){
            返回((的PropertyBag)组件)[名];
        }
        公共覆盖无效的SetValue(对象组件,对象的值){
            ((的PropertyBag)组件)[名称] =值;
        }
        公共覆盖无效ResetValue(对象组件){
            ((的PropertyBag)成分).reset段(姓名);
        }
        公众覆盖布尔CanResetValue(对象组件){
            返回true;
        }
        公众覆盖布尔ShouldSerializeValue(对象组件){
            返回((的PropertyBag)组件).ShouldSerialize(姓名);
        }
        公众覆盖类型属性类型{
            {返回类型; }
        }
        公众覆盖BOOL IsReadOnly {
            获得{返回false; }
        }
        公众覆盖类型COMPONENTTYPE {
            得到{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天全站免登陆