如何在运行时将属性级属性添加到TypeDescriptor?

时间:2012-08-27 14:10:11

标签: c# .net propertygrid system.componentmodel typedescriptor

我想在对象的属性中添加一些自定义的以PropertyGrid为中心的属性,以提供更丰富的编辑,隐藏一些值并将它们分类,因为我正在使用的那个类不提供这样的功能而且我可以'做任何事情。

实际上,这是MS的应用程序设置生成代码,因此您无法以任何方式扩展它。请参阅我的其他问题:Runtime AppSettings.settings editor dialog

4 个答案:

答案 0 :(得分:24)

与其他人的建议不同,它很可能,而且也不那么难。例如,您希望将一些新属性添加到某些属性中,您可以根据某些条件在运行时选择这些属性。

我们需要两个辅助类来实现它。

首先进入PropertyOverridingTypeDescriptor,它允许我们为某些属性提供我们自己的属性描述符,同时保持其他属性完整:

public class PropertyOverridingTypeDescriptor : CustomTypeDescriptor
    {
        private readonly Dictionary<string, PropertyDescriptor> overridePds = new Dictionary<string, PropertyDescriptor>();

        public PropertyOverridingTypeDescriptor(ICustomTypeDescriptor parent)
            : base(parent)
        { }

        public void OverrideProperty(PropertyDescriptor pd)
        {
            overridePds[pd.Name] = pd;
        }

        public override object GetPropertyOwner(PropertyDescriptor pd)
        {
            object o = base.GetPropertyOwner(pd);

            if (o == null)
            {
                return this;
            }

            return o;
        }

        public PropertyDescriptorCollection GetPropertiesImpl(PropertyDescriptorCollection pdc)
        {
            List<PropertyDescriptor> pdl = new List<PropertyDescriptor>(pdc.Count+1);

            foreach (PropertyDescriptor pd in pdc)
            {
                if (overridePds.ContainsKey(pd.Name))
                {
                    pdl.Add(overridePds[pd.Name]);
                }
                else
                {
                    pdl.Add(pd);
                }
            }

            PropertyDescriptorCollection ret = new PropertyDescriptorCollection(pdl.ToArray());

            return ret;
        }

        public override PropertyDescriptorCollection GetProperties()
        {
            return GetPropertiesImpl(base.GetProperties());
        }
        public override PropertyDescriptorCollection GetProperties(Attribute[] attributes)
        {
            return GetPropertiesImpl(base.GetProperties(attributes));
        }
    }

几点评论:

  • 构造函数需要ICustomTypeDescriptor,这里不用担心,我们可以为TypeDescriptor.GetProvider(_settings).GetTypeDescriptor(_settings)获取任何类型或实例,其中_settings可以是Typeobject类型。
  • OverrideProperty正是我们需要的,稍后会有更多内容。

我们需要的另一个类是TypeDescriptionProvider,它将返回我们的自定义类型描述符而不是默认类型描述符。这是:

public class TypeDescriptorOverridingProvider : TypeDescriptionProvider
    {
        private readonly ICustomTypeDescriptor ctd;

        public TypeDescriptorOverridingProvider(ICustomTypeDescriptor ctd)
        {
            this.ctd = ctd;
        }

        public override ICustomTypeDescriptor GetTypeDescriptor (Type objectType, object instance)
        {
            return ctd;
        }
    }

非常简单:您只需在构造中提供类型描述符实例,然后就可以了。

最后,处理代码。例如,我们希望在对象(或类型)ConnectionString中以_settings结尾的所有属性都可以使用System.Web.UI.Design.ConnectionStringEditor进行编辑。为此,我们可以使用以下代码:

// prepare our property overriding type descriptor
PropertyOverridingTypeDescriptor ctd = new PropertyOverridingTypeDescriptor(TypeDescriptor.GetProvider(_settings).GetTypeDescriptor(_settings));

// iterate through properies in the supplied object/type
foreach (PropertyDescriptor pd in TypeDescriptor.GetProperties(_settings))
{
    // for every property that complies to our criteria
    if (pd.Name.EndsWith("ConnectionString"))
    {
        // we first construct the custom PropertyDescriptor with the TypeDescriptor's
        // built-in capabilities
        PropertyDescriptor pd2 =
            TypeDescriptor.CreateProperty(
                _settings.GetType(), // or just _settings, if it's already a type
                pd, // base property descriptor to which we want to add attributes
                    // The PropertyDescriptor which we'll get will just wrap that
                    // base one returning attributes we need.
                new EditorAttribute( // the attribute in question
                    typeof (System.Web.UI.Design.ConnectionStringEditor),
                    typeof (System.Drawing.Design.UITypeEditor)
                )
                // this method really can take as many attributes as you like,
                // not just one
            );

        // and then we tell our new PropertyOverridingTypeDescriptor to override that property
        ctd.OverrideProperty(pd2);
    }
}

// then we add new descriptor provider that will return our descriptor instead of default
TypeDescriptor.AddProvider(new TypeDescriptorOverridingProvider(ctd), _settings);

就是这样,现在所有以ConnectionString结尾的属性都可以通过ConnectionStringEditor进行编辑。

正如您所看到的,我们每次都会覆盖默认实现的某些功能,因此系统应该相当稳定并且按预期运行。

答案 1 :(得分:2)

如果需要将[ExpandableObject]或[Editor]等属性添加到无法编辑的类的对象属性,则可以将属性添加到属性的类型中。因此,您可以使用反射来检查对象并使用

TypeDescriptor.AddAttributes(typeof (*YourType*), new ExpandableObjectAttribute());

然后它的行为类似于使用属性修饰 YourType 类型的所有属性。

答案 2 :(得分:1)

如果你想要丰富的自定义PropertyGrid,另一种设计是让你的类型包装在一个继承自CustomTypeDescriptor的类中。然后,您可以覆盖GetProperties,使用PropertyGrid所需的属性注释基础类的属性。

回答相关问题的详细说明https://stackoverflow.com/a/12586865/284795

答案 3 :(得分:1)

接受的答案确实有效,但是它有一个缺陷:如果你将提供者分配给一个基类,它也适用于派生类,但是,因为PropertyOverridingTypeDescriptor父类(从它开始)得到它的属性)是基类型,派生类型只会找到基类属性。这会导致例如winforms设计者的havok(如果您使用TypeDescriptor来序列化数据,可能会导致丢失数据)。

为了记录,我根据@Gman的答案制作了一个通用解决方案,并且我发布了here作为我自己问题的解决方案(这是一个不同的问题,尽管解决方案有效使用这个)。