C# 如何在运行时向TypeDescriptor添加属性级属性?
我想向对象的属性中添加一些以自定义PropertyGrid为中心的属性,以提供更丰富的编辑,隐藏一些值并将它们分组,因为我正在使用的类不提供此类功能,我对此无能为力C# 如何在运行时向TypeDescriptor添加属性级属性?,c#,.net,propertygrid,system.componentmodel,typedescriptor,C#,.net,Propertygrid,System.componentmodel,Typedescriptor,我想向对象的属性中添加一些以自定义PropertyGrid为中心的属性,以提供更丰富的编辑,隐藏一些值并将它们分组,因为我正在使用的类不提供此类功能,我对此无能为力 实际上,生成代码的是MS的应用程序设置,所以您不能以任何方式扩展它。请看我的另一个问题:与其他人的建议不同,这是完全可能的,也没有那么难。例如,您希望向某些属性添加一些新属性,您可以在运行时根据某些条件选择这些属性 我们需要两个助手类来实现这一点 首先,它允许我们为某些属性提供自己的属性描述符,同时保持其他属性的完整性: publi
实际上,生成代码的是MS的应用程序设置,所以您不能以任何方式扩展它。请看我的另一个问题:与其他人的建议不同,这是完全可能的,也没有那么难。例如,您希望向某些属性添加一些新属性,您可以在运行时根据某些条件选择这些属性 我们需要两个助手类来实现这一点 首先,它允许我们为某些属性提供自己的属性描述符,同时保持其他属性的完整性:
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));
}
}
相当简单:只需在构造时提供类型描述符实例,就可以了
最后,处理代码。例如,我们希望对象(或类型)\u设置
中以ConnectionString
结尾的所有属性都可以通过System.Web.UI.Design.ConnectionString编辑器
进行编辑。为此,我们可以使用以下代码:
// 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
进行编辑
正如您所看到的,我们每次都会覆盖默认实现的一些功能,因此系统应该相当稳定并按预期运行。如果您想要丰富的自定义PropertyGrid,另一种设计是将您的类型包装在继承自CustomTypeDescriptor的类中。然后可以重写GetProperties,用PropertyGrid所需的属性注释基础类的属性
回答相关问题的详细说明如果需要将[ExpandableObject]或[Editor]等属性添加到无法编辑的类的对象属性中,则可以将属性添加到属性类型中。因此,您可以使用反射来检查对象并使用
TypeDescriptor.AddAttributes(typeof (*YourType*), new ExpandableObjectAttribute());
然后,它的行为就像您用属性修饰了YourType类型的所有属性。接受的答案确实有效,但它有一个缺陷:如果您将提供程序分配给基类,它也适用于派生类,但是,因为
属性overridingtypedescriptor
父类(它将从中获取其属性)对于基类型,派生类型将仅查找基类属性。这会导致winforms designer中出现havok(如果使用TypeDescriptor
序列化数据,则可能会导致数据丢失)
为了记录在案,我根据@Gman的答案制作了一个通用解决方案,并将其作为我自己问题的解决方案发布(这是一个不同的问题,尽管该解决方案使用了这个问题)。我以一种稍微不同的方式使用您的逻辑:我试图在运行时在属性上添加一个
DisplayName
属性。您编写的创建逻辑似乎工作正常,但该属性到元数据的转换似乎没有发生。我创建了一个CustomModelMetadataProvider
,当为该属性调用CreateMatadata()
函数时,我的DisplayName
属性不在属性列表中,也不在元数据集中。我不确定我错过了什么…你好!我真的不明白,你的案子有什么不同。在上一个多行代码示例中,只需将EditorAttribute
更改为DisplayNameAttribute
。我检查过了,它对我有用。我对元数据一无所知,你需要它做什么?区别在于我没有使用PropertyGrid;但据我所知,这不会有什么不同。我想解决的问题是:嗨。在第三个代码块的最后一行(从TypeDescriptor.AddProvider
开始),您为TypeDescriptorOverridingProvider
使用了一个双参数构造函数,但在第二个代码块中,您只编写了一个单参数构造函数。@谢谢,我已经解决了这个问题。这只是第二个需要的_设置的类型不是必需的。
TypeDescriptor.AddAttributes(typeof (*YourType*), new ExpandableObjectAttribute());