有关自定义控件的 Visual Studio 设计时支持 (Windows Forms .NET)

正如你在与 Windows Forms 设计器交互时注意到的,Windows Forms 控件提供了许多不同的设计时功能。 Visual Studio Designer 提供的一些功能包括对齐线、操作项和属性网格。 所有这些功能都提供了一种在设计时交互和自定义控件的简单方法。 本文概述了可以添加到自定义控件的支持类型,以便更好地为控件使用者提供设计时体验。

与 .NET Framework 有何不同

自定义控件的许多基本设计元素在 .NET Framework 中保持不变。 但是,如果使用更高级的设计器自定义功能,例如操作列表、类型转换器、自定义对话框,则可以处理一些独特的方案。

Visual Studio 是基于 .NET Framework 的应用程序,因此,你在 Windows Forms 上看到的可视设计器也基于 .NET Framework。 对于 .NET Framework 项目,Visual Studio 环境和正在设计的 Windows Forms 应用都在同一 devenv.exe 进程中运行。 当你使用 Windows Forms .NET(而不是 .NET Framework)应用时,这会带来问题。 .NET 和 .NET Framework 不能在同一进程中运行。 因此,Windows Forms .NET 会使用不同的设计器,即“进程外”设计器。

进程外设计器是一个名为 DesignToolsServer.exe 的进程,在 Visual Studio 的 devenv.exe 进程中运行。 DesignToolsServer.exe 进程在应用所面向的 .NET 的同一版本和平台(如 .NET 7 和 x64)中运行。 当自定义控件需要在 devenv.exe 中显示 UI 时,自定义控件必须实现客户端-服务器体系结构,以便与 devenv.exe 进行通信。 有关详细信息,请参阅自 .NET Framework (Windows Forms .NET) 以来的设计器更改

“属性”窗口

Visual Studio“属性”窗口显示所选控件或窗体的属性和事件。 这通常是对自定义控件或组件执行的第一个自定义点。

下图显示了在视觉设计器中选择的 Button 控件,以及显示按钮属性的属性网格:

Visual Studio 中的 Windows Form 设计器显示一个按钮和属性窗口

可以控制有关自定义控件的信息在属性网格中的显示方式的一些方面。 特性将应用于自定义控件类或类属性。

类的属性

下表显示了可用于在设计时指定自定义控件和组件的行为的特性。

属性 说明
DefaultEventAttribute 指定组件的默认事件。
DefaultPropertyAttribute 指定组件的默认属性。
DesignerAttribute 指定用于为组件实现设计时服务的类。
DesignerCategoryAttribute 指定类设计器属于某一类别。
ToolboxItemAttribute 表示工具箱项的特性。
ToolboxItemFilterAttribute 指定要用于工具箱项的筛选器字符串和筛选器类型。

属性的特性

下表显示了可应用于自定义控件和组件的属性或其他成员的特性。

属性 说明
AmbientValueAttribute 指定要传递给属性的值,以使该属性从另一个源中获取其值。 这称为“环境”
BrowsableAttribute 指定属性或事件是否应在“属性”窗口中显示。
CategoryAttribute 指定当属性或事件显示在一个设置为 Categorized 模式的 PropertyGrid 控件中时,用于对属性或事件分组的类别的名称。
DefaultValueAttribute 指定属性的默认值。
DescriptionAttribute 指定属性或事件的说明。
DisplayNameAttribute 指定不返回值且不采用任何自变量的属性、事件或公共方法的显示名称。
EditorAttribute 指定用于更改属性的编辑器。
EditorBrowsableAttribute 指定可在编辑器中查看的属性或方法。
HelpKeywordAttribute 为类或成员指定上下文关键字。
LocalizableAttribute 指定是否应本地化某一属性。
PasswordPropertyTextAttribute 指示对象的文本表示形式由星号等字符隐藏。
ReadOnlyAttribute 指定此特性绑定到的属性在设计时是只读还是可读/写。
RefreshPropertiesAttribute 指示关联的属性值更改时应刷新属性网格。
TypeConverterAttribute 指定对于此属性绑定到的对象要使用哪种类型作为转换器。

自定义控件设计器

可以通过创作关联的自定义设计器来增强自定义控件的设计时体验。 默认情况下,自定义控件显示在主机的设计图面上,看起来与运行时一样。 使用自定义设计器,可以增强控件的设计时视图、添加操作项、对齐线和其他项,从而帮助用户确定如何布局和配置控件。 例如,在设计时,ToolStrip 设计器会为用户添加额外控件以添加、删除和配置各个项目,如下图所示:

Visual Studio 中的 Windows Form 设计器显示一个拆分容器的设计时视图。

可以通过执行以下步骤来创建自己的自定义设计器:

  1. 添加对 Microsoft.WinForms.Designer.SDK NuGet 包的引用。
  2. 创建从 Microsoft.DotNet.DesignTools.Designers.ControlDesigner 类继承的类型。
  3. 在用户控件类中,使用 System.ComponentModel.DesignerAttribute 类特性标记该类,并传递在上一步中创建的类型。

有关详细信息,请参阅与 .NET Framework 有何不同部分。

操作项

设计器操作是上下文相关的菜单,允许用户快速执行常见任务。 例如,如果将 TabControl 添加到窗体,则需向控件添加和删除选项卡。 选项卡在“属性”窗口中通过显示选项卡集合编辑器的 TabPages 属性进行管理。 TabControl 提供仅在选择控件时可见的智能标记按钮,而不是强制用户始终浏览“属性”列表查找 TabPages 属性,如下图所示:

Visual Studio 中的 Windows Form设计器显示选项卡控件的智能标记按钮。

选择智能标记后,将显示操作列表:

Visual Studio 中的 Windows Form 设计器显示已按下的选项卡控件的智能标记按钮,这会显示一个操作列表。

通过添加“添加选项卡”和“删除选项卡”操作,控件的设计器可使其快速添加或删除选项卡。

创建操作项列表

操作项列表由你创建的 ControlDesigner 类型提供。 以下步骤是创建自己的操作列表的基本指南:

  1. 添加对 Microsoft.WinForms.Designer.SDK NuGet 包的引用。
  2. 创建继承自 Microsoft.DotNet.DesignTools.Designers.Actions.DesignerActionList 的新操作列表类。
  3. 将属性添加到希望用户访问的操作列表。 例如,将 boolBoolean(在 Visual Basic 中)属性添加到类会在操作列表中创建 CheckBox 控件。
  4. 按照自定义控件设计器部分中的步骤创建新设计器。
  5. 在设计器类中,重写 ActionLists 属性,其将返回 Microsoft.DotNet.DesignTools.Designers.Actions.DesignerActionListCollection 类型。
  6. 将操作列表添加到 DesignerActionListCollection 实例,并返回该列表。

有关操作列表的示例,请参阅 Windows Forms 设计器扩展性文档和示例 GitHub 存储库,特别是 TileRepeater.Designer.Server/ControlDesigner 文件夹。

在“属性”窗口中,大多数属性都可以轻松地在网格中编辑,例如当属性的后备类型是枚举、布尔或数字时。

Windows Form 应用的 Visual Studio 属性窗口,显示对齐属性。

有时,属性更为复杂,需要一个自定义对话框,用户可以使用该对话来更改属性。 例如,Font 属性是 System.Drawing.Font 类型,其中包含许多更改字体外观的属性。 这在“属性”窗口中不容易显示,因此此属性使用自定义对话框编辑字体:

Windows Form 应用的 Visual Studio 字体对话框。

如果自定义控件属性使用 Windows Forms 提供的内置类型编辑器,则可使用 EditorAttribute 通过你希望 Visual Studio 使用的相应 .NET Framework 编辑器来标记你的属性。 通过使用内置编辑器,可以避免复制进程外设计器提供的代理-对象客户端-服务器通信的要求。

引用内置类型编辑器时,请使用 .NET Framework 类型,而不是 .NET 类型:

[Editor("System.Windows.Forms.Design.FileNameEditor, System.Design, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a",
        "System.Drawing.Design.UITypeEditor, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a")]
public string? Filename { get; set; }
<Editor("System.Windows.Forms.Design.FileNameEditor, System.Design, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", "System.Drawing.Design.UITypeEditor, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a")>
Public Property Filename As String