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

Gma*_*man 11 .net c# propertygrid typedescriptor system.componentmodel

我想为对象的属性添加一些自定义的以PropertyGrid为中心的属性,以提供更丰富的编辑,隐藏一些值并将它们分类,因为我正在使用的那个类不提供这样的功能而且我什么也做不了关于它.

真的,这是MS的应用程序设置生成代码,所以你不能以任何方式扩展它的属性.请参阅我的另一个问题:运行时AppSettings.settings编辑器对话框

Gma*_*man 27

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

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

首先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));
        }
    }
Run Code Online (Sandbox Code Playgroud)

几句话:

  • 构造函数ICustomTypeDescriptor,不用担心,我们可以为任何类型获取一个或者它的实例,TypeDescriptor.GetProvider(_settings).GetTypeDescriptor(_settings)其中_settings可以是Type或者object是那种类型.
  • 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;
        }
    }
Run Code Online (Sandbox Code Playgroud)

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

最后,处理代码.例如,我们希望所有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);
Run Code Online (Sandbox Code Playgroud)

就是这样,现在所有以...结尾的属性ConnectionString都可以编辑ConnectionStringEditor.

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

  • FWIW,此解决方案仅在未定义派生类型的情况下才有效,因为“TypeDescriptor”适用于所有派生类,但仅返回基类中的属性。我已经找到了所有子类并为每个子类添加了自己的 typedescriptorprovider...可能有点矫枉过正,但似乎并没有减慢设计时间,而且我个人在运行时不需要这个。 (2认同)