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

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

实际上,它是MS的应用程序设置生成代码,因此您无法以任何方式扩展它的属性。 请参阅我的另一个问题: 运行时AppSettings.settings编辑器对话框

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

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

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

 public class PropertyOverridingTypeDescriptor : CustomTypeDescriptor { private readonly Dictionary overridePds = new Dictionary(); 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 pdl = new List(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; } } 

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

最后,处理代码。 例如,我们希望在对象(或类型) _settingsConnectionString结尾的所有属性都可以使用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进行编辑。

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

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

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

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

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

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

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

仅仅为了记录,我已经基于@Gman的答案制作了一个通用的解决方案,我在这里发布它作为我自己的问题的解决方案(这是一个不同的问题,尽管解决方案使用这个解决方案)。