我想向对象的属性添加一些以 PropertyGrid 为中心的自定义属性,以提供更丰富的编辑、隐藏一些值并将它们分组到类别中,因为我正在使用的那个类不提供这样的功能,我什么也做不了关于它。
确实,它是用于生成代码的 MS 的应用程序设置,因此您不能以任何方式扩展它的属性。请参阅我的另一个问题:运行时 AppSettings.settings 编辑器对话框
我想向对象的属性添加一些以 PropertyGrid 为中心的自定义属性,以提供更丰富的编辑、隐藏一些值并将它们分组到类别中,因为我正在使用的那个类不提供这样的功能,我什么也做不了关于它。
确实,它是用于生成代码的 MS 的应用程序设置,因此您不能以任何方式扩展它的属性。请参阅我的另一个问题:运行时 AppSettings.settings 编辑器对话框
与其他人建议的不同,这是很有可能的,也没有那么难。例如,您想为某些属性添加一些新属性,您可以在运行时根据某些条件选择这些属性。
我们需要两个帮助类来实现它。
首先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));
}
}
几点说明:
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;
}
}
相当简单:您只需在构造时提供类型描述符实例,然后就可以了。
最后,处理代码。例如,我们希望所有以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);
就是这样,现在所有以 结尾的属性ConnectionString
都可以通过ConnectionStringEditor
.
如您所见,我们只是每次都覆盖默认实现的一些功能,因此系统应该相当稳定并按预期运行。
接受的答案确实有效,但它有一个缺陷:如果将提供程序分配给基类,它也适用于派生类,但是,因为PropertyOverridingTypeDescriptor
父类(从中获取其属性)是基类类型,派生类型只会找到基类属性。这会导致例如 winforms 设计器中的破坏(如果您使用TypeDescriptor
序列化数据,可能会导致您丢失数据)。
仅作记录,我根据@Gman 的回答制作了一个通用解决方案,并将其发布在这里作为我自己问题的解决方案(这是一个不同的问题,尽管解决方案使用这个解决方案)。
如果您想要丰富的自定义 PropertyGrid,另一种设计是将您的类型包装在从 CustomTypeDescriptor 继承的类中。然后,您可以覆盖 GetProperties,使用 PropertyGrid 所需的属性注释基础类的属性。
回答相关问题的详细描述https://stackoverflow.com/a/12586865/284795
如果您需要将 [ExpandableObject] 或 [Editor] 等属性添加到您无法编辑的对象的属性中,您可以将属性添加到属性的类型中。所以你可以使用反射来检查对象并使用
TypeDescriptor.AddAttributes(typeof (*YourType*), new ExpandableObjectAttribute());
然后它的行为就像你用属性修饰了YourType类型的所有属性。