WinForms Designer 和 TableLayoutPanel SmartTag 自定义

WinForms Designer and TableLayoutPanel SmartTag Customization

我正在尝试为 TableLayoutPanel Windows 表单控件自定义现有的智能标记内容,以便在 Windows 表单设计器中使用(我实现了一个利用 WinForms 设计器功能的设计器System.ComponentModel.Design 和 System.Windows.Forms.Design 命名空间)。无论提供何种方法作为解决方案,当我的控件被添加到 Visual Studio 工具箱时以及当我的控件在设计模式下在 Visual Studio IDE.

这是我正在尝试做的事情: 在设计模式下,窗体上的 TableLayoutPanel 控件实例会显示一个装饰,当单击该装饰时,会呈现由 "actions" 组成的智能标记面板,例如添加列、添加行等(其中一些操作也显示为属性 网格下的动词——我知道如何修改这些动词:我的问题只与智能标记操作列表有关。)

我想做的是用我自己的一些项目修改 TableLayoutPanel 控件的设计时智能标记操作列表。

不幸的是,似乎要做到这一点,您必须将控件与自定义设计器相关联。但是,我不想使用自定义设计器或派生自 ControlDesigner 的设计器。我发现当我这样做时,我失去了 TableLayoutPanel 控件提供的所有设计器功能——例如查看新行、新列以及能够将控件拖放到其客户端表面上。我必须保留 TableLayoutPanel 的此类视觉设计时功能。

如果我使用 Designer 属性将 TableLayoutPanelDesigner class 指定为设计器,那么我就不走运了,因为 class 被标记为内部 (https://referencesource.microsoft.com/#System.Design/System/Windows/Forms/Design/TableLayoutPanelDesigner.cs)。

如何利用 WinForm TableLayoutPanel 控件的现有设计器同时自定义其智能标记的操作列表?当然这必须可以使用反射访问(但我不知道如何)。

TableLayoutPanelDesiner 是内部的并且依赖于其他内部 类 并且你不能继承它。此外,从头开始为 TableLayoutPanel 创建一个新的控件设计器也不是一个好主意,因为您将失去所有当前的设计器功能。

一个非常好的技巧 是在设计时找到设计器并操纵设计器。查看 Do something!Back Color 新项目:

为此,您可以在设计时找到 TableLayoutPanel 的设计器并对其进行操作。 OnHandleCreated 方法是一个好点。您可以从控件的 Site 中获取 IDesignerHost 的实例,然后获取设计器。

然后您可以获取控件的当前操作列表并创建一个包含所有这些操作项的新操作列表,并向列表中添加一些新操作项。

MyTableLayoutPanel

using System;
using System.ComponentModel.Design;
using System.Windows.Forms;
using System.Windows.Forms.Design;
public class MyTableLayoutPanel : TableLayoutPanel
{
    private IDesignerHost designerHost;
    protected override void OnHandleCreated(EventArgs e)
    {
        base.OnHandleCreated(e);
        if (DesignMode && Site != null)
        {
            designerHost = Site.GetService(typeof(IDesignerHost)) as IDesignerHost;
            if (designerHost != null)
            {
                var designer = (ControlDesigner)designerHost.GetDesigner(this);
                if (designer != null)
                {
                    var actions = designer.ActionLists[0];
                    designer.ActionLists.Clear();
                    designer.ActionLists.Add(
                        new MyTableLayoutPanelActionList(designer, actions));
                }
            }
        }
    }
}

MyTableLayoutPanelActionList

using System.ComponentModel;
using System.ComponentModel.Design;
using System.Drawing;
using System.Windows;
using System.Windows.Forms.Design;
public class MyTableLayoutPanelActionList : DesignerActionList
{
    MyTableLayoutPanel control;
    ControlDesigner designer;
    DesignerActionList actionList;
    public MyTableLayoutPanelActionList(ControlDesigner designer, 
        DesignerActionList actionList) : base(designer.Component)
    {
        this.designer = designer;
        this.actionList = actionList;
        control = (MyTableLayoutPanel)designer.Control;
    }
    public Color BackColor
    {
        get { return control.BackColor; }
        set
        {
            TypeDescriptor.GetProperties(Component)["BackColor"]
                .SetValue(Component, value);
        }
    }
    private void DoSomething()
    {
        MessageBox.Show("My Custom Verb added!");
    }
    public override DesignerActionItemCollection GetSortedActionItems()
    {
        var items = new DesignerActionItemCollection();
        foreach (DesignerActionItem item in actionList.GetSortedActionItems())
            items.Add(item);
        var category = "New Actions";
        items.Add(new DesignerActionMethodItem(this, "DoSomething", 
            "Do something!", category, true));
        items.Add(new DesignerActionPropertyItem("BackColor", "Back Color",
            category));
        return items;
    }
}