演练:向 Windows 窗体组件添加智能标记

智能标记是类似于菜单的用户界面 (UI) 元素,用于提供常用的设计时选项。.NET Framework 提供的大多数标准组件和控件都包含智能标记和设计器谓词增强功能。本演练中的过程向您演示如何向组件和自定义控件添加智能标记支持。

您可以向 Window 窗体组件添加智能标记来提供常用的设计时选项。智能标记面板中的项是按类别进行逻辑分组的,各个 DesignerActionMethodItem 实例可被复制为设计器谓词项。.NET Framework 提供的许多标准组件和控件都包含智能标记和设计器谓词增强功能。组件和自定义控件作者还可以添加智能标记支持,通常使用推送模型。

使用推送模型添加智能标记要求向组件项目添加下列各项:

说明说明

智能标记面板不支持滚动或分页,因此注意不要向面板中填充大量智能标记项。过多的项可能导致智能标记面板超出屏幕边界。

下面的过程演示如何使用代码从一个简单的示例控件 ColorLabel 添加智能标记,此示例控件是从标准 Windows 窗体 Label 控件派生的。此控件有一个关联的设计器,其名称为 ColorLabelDesigner。

若要将此主题中的代码复制成一个清单,请参见如何:向 Windows 窗体组件附加智能标记

系统必备

若要完成本演练,您需要:

  • 拥有在安装有 .NET Framework 的计算机上创建和运行 Windows 窗体应用程序项目的足够权限。

实现从 DesignerActionList 派生的类

  1. 在与您的组件相同的命名空间中,为从 DesignerActionList 派生的类添加声明。

    说明说明

    必须添加对设计时程序集 System.Design.dll 的引用。此程序集不包含在 .NET Framework 4 Client Profile 中。若要添加对 System.Design.dll 的引用,必须将项目的目标框架更改为“.NET Framework 4”

    Public Class ColorLabelActionList
        Inherits System.ComponentModel.Design.DesignerActionList
    
    public class ColorLabelActionList :
              System.ComponentModel.Design.DesignerActionList
    
  2. 向此类添加一个带有关联控件的实例的构造函数。提供一个私有字段来存放对此实例的引用。还需提供一个私有字段以缓存对 DesignerActionService 的引用。这将用于更新列表。

    Private colLabel As ColorLabel
    
    
    ...
    
    
    Private designerActionUISvc As DesignerActionUIService = Nothing
    
    
    ...
    
    
    Public Sub New(ByVal component As IComponent)
    
        MyBase.New(component)
        Me.colLabel = component
    
        ' Cache a reference to DesignerActionUIService, so the
        ' DesigneractionList can be refreshed.
        Me.designerActionUISvc = _
        CType(GetService(GetType(DesignerActionUIService)), _
        DesignerActionUIService)
    
    End Sub
    
    private ColorLabel colLabel;
    
    
    ...
    
    
    private DesignerActionUIService designerActionUISvc = null;
    
    
    ...
    
    
    public ColorLabelActionList( IComponent component ) : base(component) 
    {
        this.colLabel = component as ColorLabel;
    
        // Cache a reference to DesignerActionUIService, so the
        // DesigneractionList can be refreshed.
        this.designerActionUISvc =
            GetService(typeof(DesignerActionUIService))
            as DesignerActionUIService;
    }
    
  3. 将您要关联的方法和属性添加到智能标记项中。在选中与方法对应的智能标记项时,将会执行这些方法。属性应当有 getter 部分,这样才能显示它们的当前值;如果想让它们的值可以从对应的智能标记项编辑,则它们还可以有使用 GetProperties 方法的 setter 部分。

    说明说明

    在整个设计时环境中,仅在基类型之一是由 .NET Framework 提供的,或者该类型可由提供的 TypeConverter 转换成基类型时,或者在提供了一个自定义 UITypeEditor 时,才可以编辑属性。

    Public Property ForeColor() As Color
        Get
            Return colLabel.ForeColor
        End Get
        Set(ByVal value As Color)
            GetPropertyByName("ForeColor").SetValue(colLabel, value)
        End Set
    End Property
    
    
    ...
    
    
    'Boolean properties are automatically displayed with binary 
    ' UI (such as a checkbox).
    Public Property LockColors() As Boolean
        Get
            Return colLabel.ColorLocked
        End Get
        Set(ByVal value As Boolean)
            GetPropertyByName("ColorLocked").SetValue(colLabel, value)
    
            ' Refresh the list.
            Me.designerActionUISvc.Refresh(Me.Component)
        End Set
    End Property
    
    
    ...
    
    
    Public Sub InvertColors()
        Dim currentBackColor As Color = colLabel.BackColor
        BackColor = Color.FromArgb( _
        255 - currentBackColor.R, _
        255 - currentBackColor.G, _
        255 - currentBackColor.B)
    
        Dim currentForeColor As Color = colLabel.ForeColor
        ForeColor = Color.FromArgb( _
        255 - currentForeColor.R, _
        255 - currentForeColor.G, _
        255 - currentForeColor.B)
    End Sub
    
    public Color ForeColor
    {
        get
        {
            return colLabel.ForeColor;
        }
        set
        {
            GetPropertyByName("ForeColor").SetValue(colLabel, value);
        }
    }
    
    
    ...
    
    
    // Boolean properties are automatically displayed with binary 
    // UI (such as a checkbox).
    public bool LockColors
    {
        get
        {
            return colLabel.ColorLocked;
        }
        set
        {
            GetPropertyByName("ColorLocked").SetValue(colLabel, value);
    
            // Refresh the list.
            this.designerActionUISvc.Refresh(this.Component);
        }
    }
    
    
    ...
    
    
    public void InvertColors()
    {
        Color currentBackColor = colLabel.BackColor;
        BackColor = Color.FromArgb(
            255 - currentBackColor.R, 
            255 - currentBackColor.G, 
            255 - currentBackColor.B);
    
        Color currentForeColor = colLabel.ForeColor;
        ForeColor = Color.FromArgb(
            255 - currentForeColor.R, 
            255 - currentForeColor.G, 
            255 - currentForeColor.B);
    }
    
  4. 可以实现 GetSortedActionItems 方法的重写版本以返回 DesignerActionItem 实例数组,其中的各项分别与前一步中创建的一个属性或方法关联。您可以通过此种方式更改项的顺序,给项分类,或显示项。此列表还可包括静态项,如逻辑组标题。

    Public Overrides Function GetSortedActionItems() _
    As DesignerActionItemCollection
        Dim items As New DesignerActionItemCollection()
    
        'Define static section header entries.
        items.Add(New DesignerActionHeaderItem("Appearance"))
        items.Add(New DesignerActionHeaderItem("Information"))
    
        'Boolean property for locking color selections.
        items.Add(New DesignerActionPropertyItem( _
        "LockColors", _
        "Lock Colors", _
        "Appearance", _
        "Locks the color properties."))
    
        If Not LockColors Then
            items.Add( _
            New DesignerActionPropertyItem( _
            "BackColor", _
            "Back Color", _
            "Appearance", _
            "Selects the background color."))
    
            items.Add( _
            New DesignerActionPropertyItem( _
            "ForeColor", _
            "Fore Color", _
            "Appearance", _
            "Selects the foreground color."))
    
            'This next method item is also added to the context menu 
            ' (as a designer verb).
            items.Add( _
            New DesignerActionMethodItem( _
            Me, _
            "InvertColors", _
            "Invert Colors", _
            "Appearance", _
            "Inverts the fore and background colors.", _
            True))
        End If
        items.Add( _
        New DesignerActionPropertyItem( _
        "Text", _
        "Text String", _
        "Appearance", _
        "Sets the display text."))
    
        'Create entries for static Information section.
        Dim location As New StringBuilder("Location: ")
        location.Append(colLabel.Location)
        Dim size As New StringBuilder("Size: ")
        size.Append(colLabel.Size)
    
        items.Add( _
        New DesignerActionTextItem( _
        location.ToString(), _
        "Information"))
    
        items.Add( _
        New DesignerActionTextItem( _
        size.ToString(), _
        "Information"))
    
        Return items
    End Function
    
    public override DesignerActionItemCollection GetSortedActionItems()
    {
        DesignerActionItemCollection items = new DesignerActionItemCollection();
    
        //Define static section header entries.
        items.Add(new DesignerActionHeaderItem("Appearance"));
        items.Add(new DesignerActionHeaderItem("Information"));
    
        //Boolean property for locking color selections.
        items.Add(new DesignerActionPropertyItem("LockColors",
                         "Lock Colors", "Appearance",
                         "Locks the color properties."));
        if (!LockColors)
        {
            items.Add(new DesignerActionPropertyItem("BackColor",
                             "Back Color", "Appearance",
                             "Selects the background color."));
            items.Add(new DesignerActionPropertyItem("ForeColor",
                             "Fore Color", "Appearance",
                             "Selects the foreground color."));
    
            //This next method item is also added to the context menu 
            // (as a designer verb).
            items.Add(new DesignerActionMethodItem(this,
                             "InvertColors", "Invert Colors",
                             "Appearance",
                             "Inverts the fore and background colors.",
                              true));
        }
        items.Add(new DesignerActionPropertyItem("Text",
                         "Text String", "Appearance",
                         "Sets the display text."));
    
        //Create entries for static Information section.
        StringBuilder location = new StringBuilder("Location: ");
        location.Append(colLabel.Location);
        StringBuilder size = new StringBuilder("Size: ");
        size.Append(colLabel.Size);
        items.Add(new DesignerActionTextItem(location.ToString(),
                         "Information"));
        items.Add(new DesignerActionTextItem(size.ToString(),
                         "Information"));
    
        return items;
    }
    

更新相关的设计器类以实现 ActionLists 属性

  1. 查找控件的设计器类。如果不存在,创建一个设计器类并将其与控件类关联。有关设计器的更多信息,请参见设计器基类

  2. 作为一种优化技术,添加类型为 DesignerActionListCollection 的私有字段。

    Private lists As DesignerActionListCollection
    
    private DesignerActionListCollection actionLists;
    
  3. 添加重写的 ActionLists 属性以返回您早先创建的 ColorLabelActionList 类的新实例。

    Public Overrides ReadOnly Property ActionLists() _
    As DesignerActionListCollection
        Get
            If lists Is Nothing Then
                lists = New DesignerActionListCollection()
                lists.Add( _
                New ColorLabelActionList(Me.Component))
            End If
            Return lists
        End Get
    End Property
    
    public override DesignerActionListCollection ActionLists
    {
        get
        {
            if (null == actionLists)
            {
                actionLists = new DesignerActionListCollection();
                actionLists.Add(
                    new ColorLabelActionList(this.Component));
            }
            return actionLists;
        }
    }
    

注释

代码有多个方面需要更详细的解释:

  • 当从 DesignerActionList 派生的类中的属性或方法更改关联控件的状态时,这些更改不应通过对组件的属性直接发出 setter 调用来进行。这些更改应通过一个适当创建的 PropertyDescriptor 来进行。这种间接方式可以确保智能标记撤消操作和 UI 更新操作正常进行。

  • 您可以通过调用 DesignerActionUIService.Refresh 来动态更新智能标记面板。此方法可用来动态更改智能标记面板的内容。在示例中,使用与更改颜色有关的智能标记是有条件的,使用与否取决于 LockColors 属性的状态。此布尔属性也与智能标记相关,所以开发人员可以锁定或解除锁定当前颜色选择,至少可以通过菜单这样做。

  • 通过将构造函数中的 includeAsDesignerVerb 参数设置为 true,可以将类型为 DesignerActionMethodItem 的智能标记项包括在关联的控件的快捷菜单中。然后,.NET Framework 隐式创建一个对应的 DesignerVerb 并将其添加到您的快捷菜单中。在此示例中,InvertColors 项就是以这种方式处理的。

  • 在一个面板中,按照智能标记项的 Category 属性将智能标记项分成了不同的类别,各项的构造函数中都设置了此属性。如果没有显式设置此属性,则该项将被指定为默认类别。在智能标记面板中,各项首先按类别排序,然后按照在从 DesignerActionList 类派生的类返回的 DesignerActionItem 数组中出现的顺序排序。此示例包含两个类别:Appearance 和 Information。

    说明说明

    没有为第二个类别提供 DesignerActionHeaderItem

  • 使用一个 DesignerActionTextItem 或者使用一个其关联属性只包含 setter 的 DesignerActionPropertyItem,可以实现一个显示静态文本信息的项。此示例采用了前一种方式。

后续步骤

在您开始将您的组件集成到设计时环境时,请考虑扩展其设计器支持。

请参见

参考

DesignerVerb

DesignerActionItem

DesignerActionList

ActionLists

DesignerActionService

概念

Windows 窗体的设计器命令和 DesignerAction 对象模型