PropertyGrid 中的自动展开属性





2.00/5 (3投票s)
2006年1月4日
4分钟阅读

73788

1989
本文描述了一系列类,它们将自动使您自定义类型的每个公共属性在 PropertyGrid 中可扩展,而无需编写显式的 TypeConverter。您还可以编辑支持“set”访问器的属性。
引言
本文解释了如何在不关联 TypeConverter 的情况下为自定义类型添加 PropertyGrid
支持。
当我终于下定决心,从 Visual Studio 2002 升级到 Visual Studio 2005 时,我非常失望(委婉地说),我发现当我将我的 Form
类的一个实例选择到 PropertyGrid
中时,我的自定义类型的公共属性要么不可扩展,要么不可编辑,正如我(确定)它们过去一样。对于我的每个属性,我所能看到的只是一个相对无用的灰色文本的 GridItem
,显示 <namespace>.<type-name>。
当然,通过在每个自定义类型中重写 ToString()
,我可以使显示稍微美观一些,我甚至可以使用这种方法显示类型的字段和属性。但是,这不允许编辑任何对象,甚至不能指示哪些是可编辑的,哪些是只读的。当然,PropertyGrid
2002 年最实用的功能就是它允许您 非常快速地 为自定义类型提供类型编辑器/查看器。
起初,我花了一天左右的时间,用各种组合的 ComponentModel
属性标签装饰我的类和成员,试图获得
- 我想要的,以及
- 属性名称暗示它们会提供的行为。
哈哈!接着是痛苦的 3-4 天,我查阅 MSDN、论坛等,最初试图找出微软对行为改变的解释以及相应的解决方案(几乎不可能!);然后,当这个搜索无果而终时,我试图找到其他人也遇到了同样的问题,并且更重要的是,*解决了* 这个问题。我没找到太多运气。我在一些网站上找到了一些帖子,来自美国的一位开发经理,他早在今年 6 月就遇到了同样的问题,但当我写邮件询问解决方案时,他回信说他放弃了,选择了另一条路。
最终,我开始意识到,尽管我承认,这花了我一些时间才接受,但唯一的解决方案(至少根据微软的文档)是为我的每一个自定义属性类型编写一个自定义 TypeConverter
!这将使 PropertyGrid
能够将每个自定义类型 *看作* 我想要的子类型的聚合。这似乎是一个相当大的要求!
然后我找到了 Stephen Toub 的优秀 NetMatters 文章:参见下方的 URL 背景。
- NET Matters ICustomTypeDescriptor,第一部分 -- MSDN Magazine,2005 年 4 月
- NET Matters ICustomTypeDescriptor,第二部分 -- MSDN Magazine,2005 年 5 月
…在其中,他介绍了一系列类:FieldsToPropertiesTypeDescriptor
、FieldsToPropertiesTypeDescriptorProvider
等,它们的目的是,给定一个现有的类型 MyClass
(例如),它 *只* 暴露公共访问字段(即,没有属性支持),自动将每个字段包装成一个伪属性,该属性将在 PropertyGrid
中显示,而无需修改原始 MyClass
。
我鼓励任何对 PropertyGrid
感兴趣的人阅读这两篇文章,以更深入地了解其内部工作原理;同时,也有助于理解本文提供的代码。
尽管 FieldsToPropertiesTypeDescriptor
等并没有完全提供期望的行为,但可以使用相同的技术和架构来解决我自己的特定问题。结果如下所示,作为 ExpandableObject
和 ExpandablePropertiesTypeDescriptionProvider
。
请注意,ExpandableObject
中使用的 *缓存机制*,以及 *基本架构*,都借鉴自 Stephen Taub 文章中的代码。感谢作者提供的信息/技术,并允许用于非盈利目的的再利用。
背景
推荐阅读
- NET Matters ICustomTypeDescriptor,第一部分 -- MSDN Magazine,2005 年 4 月
- NET Matters ICustomTypeDescriptor,第二部分 -- MSDN Magazine,2005 年 5 月
使用代码
要为您的类添加 PropertyGrid
可扩展/可编辑公共属性的支持,只需继承自 ExpandableObject
即可,例如:
using ExpandablePropertiesTypeDescriptor;
public class MyCustomType : ExpandableObject
{
private int myIntField = 0;
public int MyIntProperty
{
get{ . . . }
set{ . . . }
}
...
}
现在,当您将 MyCustomType
的一个实例选择到 PropertyGrid
中时,例如:
propertyGrid1.SelectedObject = MyCustomTypeInstance;
您将在标准的 [+] 小部件下看到其所有公共访问属性。任何支持“set
”的属性也将可在 PropertyGrid
中编辑。
请注意,如果继承自 ExpandableObject
对您来说不方便,例如,您想在 PropertyGrid
中选择的对象是 Form
(例如),并且您想让 PropertyGrid
显示其(Form
的)公共属性,您可以这样做:
using ExpandablePropertiesTypeDescriptor;
public partial class MyFormClass : Form
{
public MyFormClass()
{
TypeDescriptor.AddProvider(new
ExpandablePropertiesTypeDescriptionProvider(GetType()),
this);
InitializeComponent();
}
}
重要!
如果您使用上述场景,请务必在应用程序关闭前调用 TypeDescriptor.RemoveProvider
。您可以在 Form
的 Dispose()
方法中这样做:
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
TypeDescriptor.RemoveProvider(new
ExpandablePropertiesTypeDescriptionProvider(GetType()),
this);
base.Dispose(disposing);
}
关注点
查看 ExpandableObjectDemo.DemoForm
,了解 ExpandableObject
如何用于表单拥有的自定义类型属性。请注意,这里使用了两种技术(如上所述):即 ExpandableObjectDemo.CustomType
继承自 ExpandableObject
,而 ExpandableObjectDemo.DemoForm
调用 TypeDescriptor.AddProvider
/ TypeDescriptor.RemoveProvider
。这是因为被选在 PropertyGrid
中的对象是 Form
对象。如果我们只想在 PropertyGrid
中显示 ExpandableObjectDemo.CustomType
,而不想显示其与父对象的关联,则无需在 Form
对象中调用 Add
/RemoveProvider
。
我想,这次我(重新)学到的最重要的教训是:“除非您(i)享受痛苦,并且(ii)没有什么更重要(或更紧急)的事情要做,否则不要搞乱您的开发环境。”