65.9K
CodeProject 正在变化。 阅读更多。
Home

PropertyGrid 中的自动展开属性

starIconstarIconemptyStarIconemptyStarIconemptyStarIcon

2.00/5 (3投票s)

2006年1月4日

4分钟阅读

viewsIcon

73788

downloadIcon

1989

本文描述了一系列类,它们将自动使您自定义类型的每个公共属性在 PropertyGrid 中可扩展,而无需编写显式的 TypeConverter。您还可以编辑支持“set”访问器的属性。

引言

本文解释了如何在不关联 TypeConverter 的情况下为自定义类型添加 PropertyGrid 支持。

当我终于下定决心,从 Visual Studio 2002 升级到 Visual Studio 2005 时,我非常失望(委婉地说),我发现当我将我的 Form 类的一个实例选择到 PropertyGrid 中时,我的自定义类型的公共属性要么不可扩展,要么不可编辑,正如我(确定)它们过去一样。对于我的每个属性,我所能看到的只是一个相对无用的灰色文本的 GridItem,显示 <namespace>.<type-name>

当然,通过在每个自定义类型中重写 ToString(),我可以使显示稍微美观一些,我甚至可以使用这种方法显示类型的字段和属性。但是,这不允许编辑任何对象,甚至不能指示哪些是可编辑的,哪些是只读的。当然,PropertyGrid 2002 年最实用的功能就是它允许您 非常快速地 为自定义类型提供类型编辑器/查看器。

起初,我花了一天左右的时间,用各种组合的 ComponentModel 属性标签装饰我的类和成员,试图获得

  1. 我想要的,以及
  2. 属性名称暗示它们会提供的行为。

哈哈!接着是痛苦的 3-4 天,我查阅 MSDN、论坛等,最初试图找出微软对行为改变的解释以及相应的解决方案(几乎不可能!);然后,当这个搜索无果而终时,我试图找到其他人也遇到了同样的问题,并且更重要的是,*解决了* 这个问题。我没找到太多运气。我在一些网站上找到了一些帖子,来自美国的一位开发经理,他早在今年 6 月就遇到了同样的问题,但当我写邮件询问解决方案时,他回信说他放弃了,选择了另一条路。

最终,我开始意识到,尽管我承认,这花了我一些时间才接受,但唯一的解决方案(至少根据微软的文档)是为我的每一个自定义属性类型编写一个自定义 TypeConverter!这将使 PropertyGrid 能够将每个自定义类型 *看作* 我想要的子类型的聚合。这似乎是一个相当大的要求!

然后我找到了 Stephen Toub 的优秀 NetMatters 文章:参见下方的 URL 背景

  • NET Matters ICustomTypeDescriptor,第一部分 -- MSDN Magazine,2005 年 4 月
  • NET Matters ICustomTypeDescriptor,第二部分 -- MSDN Magazine,2005 年 5 月

…在其中,他介绍了一系列类:FieldsToPropertiesTypeDescriptorFieldsToPropertiesTypeDescriptorProvider 等,它们的目的是,给定一个现有的类型 MyClass(例如),它 *只* 暴露公共访问字段(即,没有属性支持),自动将每个字段包装成一个伪属性,该属性将在 PropertyGrid 中显示,而无需修改原始 MyClass

我鼓励任何对 PropertyGrid 感兴趣的人阅读这两篇文章,以更深入地了解其内部工作原理;同时,也有助于理解本文提供的代码。

尽管 FieldsToPropertiesTypeDescriptor 等并没有完全提供期望的行为,但可以使用相同的技术和架构来解决我自己的特定问题。结果如下所示,作为 ExpandableObjectExpandablePropertiesTypeDescriptionProvider

请注意,ExpandableObject 中使用的 *缓存机制*,以及 *基本架构*,都借鉴自 Stephen Taub 文章中的代码。感谢作者提供的信息/技术,并允许用于非盈利目的的再利用。

背景

推荐阅读

使用代码

要为您的类添加 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。您可以在 FormDispose() 方法中这样做:

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)没有什么更重要(或更紧急)的事情要做,否则不要搞乱您的开发环境。”

© . All rights reserved.