如何在运行时将属性级属性添加到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可以是该类型的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; } }
相当简单:您只需在构造中提供类型描述符实例,然后就可以了。
最后,处理代码。 例如,我们希望在对象(或类型) _settings
以ConnectionString
结尾的所有属性都可以使用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的答案制作了一个通用的解决方案,我在这里发布它作为我自己的问题的解决方案(这是一个不同的问题,尽管解决方案使用这个解决方案)。
- NetworkStream.Read()和NetworkStream.BeginRead()之间的区别?
- Visual Studio注册表捕获实用程序已停止工作,在Windows7中编译C#项目时出错
- C#(或其他语言)中代理人的使用
- Visual Studio 2008 – 程序立即退出F5(开始调试)
- 版本号float,decimal或double
- AutoMapper并将日期时间转换为字符串
- 如何使用.NET 4中的任务并行库链接异步操作?
- 如何以编程方式validation程序集是否使用特定证书进行签名?
- Visual Studio / MSBuild将引用类库的app.config复制为* .dll.config到当前项目的bin文件夹