如何添加属性级别属性的TypeDescriptor在运行时?属性、级别、TypeDescriptor

2023-09-02 01:50:52 作者:老子最可爱

我想一些自定义的PropertyGrid为中心的属性添加到对象的属性,为客户提供更丰富的编辑,隐藏一些值,并将其组类,因为这个类我正在与不提供这样的功能,我可以'做任何事情了。

I want to add some custom PropertyGrid-centric Attributes to the object's properties, to provide richer editing, hide some values and group them in categories, because that class I'm working with doesn't provide such functionality and I can't do anything about it.

说真的,这对MS的应用程序设置,生成code,所以你不能扩展它以任何方式财产明智的。见我的其他问题:Runtime AppSettings.settings编辑器对话框

Really, it's for MS's Application Settings that generates code, so you can't extend it in any way property-wise. See my other question: Runtime AppSettings.settings editor dialog

推荐答案

不像其他人所说,这是很有可能的,而且也没有那么难。例如,您要添加的部分的新属性的部分的属性,可以在运行时根据一些标准进行选择。

Unlike others have suggested, it's quite possible, and also not that hard. For example, you want to add some new attributes to some properties, which you can select at runtime based on some criteria.

这里有两种辅助类,我们需要实现这一点。

There're two helper classes we'll need to implement this.

首先进入 PropertyOverridingTypeDescriptor ,它使我们能够提供我们自己的财产描述符的部分的性能,同时保持其他不变:

First goes PropertyOverridingTypeDescriptor, it allows us to supply our own property descriptors for some properties, while keeping others intact:

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可以eithr 键入对象的类型。 OverrideProperty 不正是我们所需要的,更多的后来。 Constructor takes ICustomTypeDescriptor, no worries here, we can get one for any type or it's instance with the TypeDescriptor.GetProvider(_settings).GetTypeDescriptor(_settings) where _settings can be eithr Type or object of that type. OverrideProperty does just what we need, more on it later.

我们需要的其他类是 TypeDescriptionProvider 将返回默认我们的一个自定义类型描述符代替。在这里,它是:

The other class we need is the TypeDescriptionProvider that will return our custom type descriptor instead of the default one. Here it is:

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;
        }
    }

很简单:您只需提供该类型描述符的实例对cunstruction,在这里你去

Fairly simple: you just supply the type descriptor instance on cunstruction and here you go.

最后,处理code。比如,我们希望所有的属性结尾用的ConnectionString 在我们的对象(或类型) _settings 可编辑与 System.Web.UI.Design.ConnectionStringEditor 。为了实现这一点,我们可以用这个code:

And finally, processing code. For example, we want all properties ending with ConnectionString in our object (or type) _settings to be editable with the System.Web.UI.Design.ConnectionStringEditor. To achieve that, we can use this code:

// 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 istead of default
TypeDescriptor.AddProvider(new TypeDescriptorOverridingProvider(ctd), _settings);

就是这样,现在的ConnectionString 结束将编辑过 ConnectionStringEditor 的所有属性。

That's it, now all properties ending with ConnectionString will be editable through ConnectionStringEditor.

正如你所看到的,我们只是覆盖了每一次的默认实现一些功能,因此系统应该是相当稳定的,表现符合预期。

As you can see, we just override some functionality of the default implementation every time, so the system should be fairly stable and behave as expected.