DoEasy. 控件 (第 20 部分): SplitContainer WinForms 对象(基础篇)
📘

DoEasy. 控件 (第 20 部分): SplitContainer WinForms 对象(基础篇)

第 1/3 篇

给库加一个可拖拽分隔的面板容器

在 MT5 自定义控件库里扩展 WinForms 风格元素时,SplitContainer 是最容易被忽略但极实用的一个:它能在单个窗体内切出两块可拖拽调整比例的面板区域。对做多窗口盯盘模板的交易者来说,这意味着把图表、信号列表和日志塞进同一个 EA 对话框不再需要叠多个独立窗口。 原文示例发布于 2023 年 1 月 30 日,截至归档页显示该文获得 790 次查看与 2 条读者反馈,说明这类底层控件改造的需求虽小众但真实存在。 本节的落点是先理清库的继承结构:从通用控件基类往下挂一个辅助面板对象类,再由其派生出 SplitContainer 控件对象类。这一步不写业务逻辑,只把『能画出来、能响应鼠标拖分隔条』的骨架立住,后续章节才往里填交易相关的内容。

◍ 从零搭一个可分隔的双面板容器

要在 MT5 自定义界面里复刻 Visual Studio 的 SplitContainer,思路是先建一个完全透明的 CContainer 基类对象,再往里塞两个 SplitContainerPanel 子对象。当前这版只做静态垂直分隔:隔板距左边缘 50 像素、自身宽 4 像素,两个面板按 Fixed3D 风格画缩进边框。 函数库里的边框类型比 Studio 的三种(None / FixedSingle / Fixed3D)宽得多。眼下面板用 FRAME_STYLE_STAMP 做凹陷场位,后续可能补 FRAME_STYLE_BEVEL 的凸起样式。 这阶段对象不可拖动,纯粹用来摆控件。下一篇再补水平隔板、拖拽调尺寸等能力——外汇与贵金属图表插件开发属高风险折腾,参数没调好可能拖垮终端响应。

MQL5 / C++
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Frame styles                                                     |
class=class="str">"cmt">//+------------------------------------------------------------------+
enum ENUM_FRAME_STYLE
  {
   FRAME_STYLE_NONE,                class=class="str">"cmt">// No frame
   FRAME_STYLE_SIMPLE,              class=class="str">"cmt">// Simple frame
   FRAME_STYLE_FLAT,                class=class="str">"cmt">// Flat frame
   FRAME_STYLE_BEVEL,               class=class="str">"cmt">// Embossed(convex)
   FRAME_STYLE_STAMP,               class=class="str">"cmt">// Embossed(concave)
   };

「给函数库塞进 SplitContainer 控件骨架」

要在 MT5 里自己画一个可拖拽隔板的 SplitContainer,先得让 DoEasy 函数库认得它。我们在 Defines.mqh 里加了一组宏,专门给这个新控件的背景和边框上色,比如背景统一用 C'0xF0,0xF0,0xF0'(浅灰),边框用 C'0x65,0x65,0x65'(深灰),点击和悬停时不换色,保持视觉稳定。 图形元素类型枚举里同步加了 SplitContainer 主对象和面板辅助对象,画布元素的整数属性总数从 97 扩到 105,新增的 8 个属性覆盖了隔板宽度、面板最小尺寸、是否锁定某侧面板等。这样后续就能按这些属性对所有画布元素做筛选和排序。 构造函数里给了默认行为:隔板宽 4 像素、离边缘 50 像素、两面板最小 25 像素且都不折叠,调整容器时两边都能动。GCnvElement 的对象结构和文件存取方法也补了对应字段,对象存盘再读回来不会丢状态。 TabControl 这边顺手加了指向上一个 TabHeader 的指针,方便以后做标题栏滚动裁剪的补漏。TabControl 创建选项卡时把前驱指针串起来,另外给左右上下箭头按钮临时写死 ID——这是绕开一个建对象时 ID 错乱的坑,等修干净就能删。 创建图形对象的方法清掉了一批没用过的控件代码,现在只留真正要实例化的部分。整个可用控件集合收口在选项卡场位类里,因为只有那能挂新对象。

MQL5 / C++
class="macro">#define CLR_DEF_CONTROL_TAB_HEAD_BORDER_COLOR_ON(C&class="macro">#x27;0xDD,0xDD,0xDD&class="macro">#x27;) class=class="str">"cmt">// Color of the enabled TabPage control header frame
class="macro">#define CLR_DEF_CONTROL_TAB_HEAD_BORDER_DOWN_ON(C&class="macro">#x27;0xDD,0xDD,0xDD&class="macro">#x27;) class=class="str">"cmt">// Color of the enabled TabPage control header frame when clicking on the control
class="macro">#define CLR_DEF_CONTROL_TAB_HEAD_BORDER_OVER_ON(C&class="macro">#x27;0xDD,0xDD,0xDD&class="macro">#x27;) class=class="str">"cmt">// Color of the enabled TabPage control header frame when hovering the mouse over the control
class="macro">#define CLR_DEF_CONTROL_SPLIT_CONTAINER_BACK_COLOR(C&class="macro">#x27;0xF0,0xF0,0xF0&class="macro">#x27;)class=class="str">"cmt">// SplitContainer control background class="type">color
class="macro">#define CLR_DEF_CONTROL_SPLIT_CONTAINER_MOUSE_DOWN(C&class="macro">#x27;0xF0,0xF0,0xF0&class="macro">#x27;)class=class="str">"cmt">// Color of SplitContainer control background when clicking on the control
class="macro">#define CLR_DEF_CONTROL_SPLIT_CONTAINER_MOUSE_OVER(C&class="macro">#x27;0xF0,0xF0,0xF0&class="macro">#x27;)class=class="str">"cmt">// Color of SplitContainer control background when hovering the mouse over the control
class="macro">#define CLR_DEF_CONTROL_SPLIT_CONTAINER_BORDER_COLOR(C&class="macro">#x27;0x65,0x65,0x65&class="macro">#x27;)class=class="str">"cmt">// SplitContainer control frame class="type">color
class="macro">#define DEF_CONTROL_LIST_MARGIN_X(class="num">1)                      class=class="str">"cmt">// Gap between columns in ListBox controls
class="macro">#define DEF_CONTROL_LIST_MARGIN_Y(class="num">0)                      class=class="str">"cmt">// Gap between rows in ListBox controls
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| The list of graphical element types                              |
class=class="str">"cmt">//+------------------------------------------------------------------+
enum ENUM_GRAPH_ELEMENT_TYPE
  {
   GRAPH_ELEMENT_TYPE_STANDARD,                class=class="str">"cmt">// Standard graphical object
   GRAPH_ELEMENT_TYPE_STANDARD_EXTENDED,       class=class="str">"cmt">// Extended standard graphical object
   GRAPH_ELEMENT_TYPE_SHADOW_OBJ,              class=class="str">"cmt">// Shadow object
   GRAPH_ELEMENT_TYPE_ELEMENT,                 class=class="str">"cmt">// Element

图表对象枚举里的 WinForms 控件映射

在 MT5 的图表对象体系里,GRAPH_ELEMENT_TYPE 枚举把界面元素分成表单、窗口和 WinForms 三大类。做自定义面板时,先认清楚容器类与标准控件类的枚举值,能少走很多弯路。 下面这段枚举定义了从容器到具体控件的完整类型链。容器侧包含 WF_CONTAINER、WF_PANEL、WF_GROUPBOX、WF_TAB_CONTROL 以及 WF_SPLIT_CONTAINER;标准控件侧则从 WF_COMMON_BASE 派生出 LABEL、BUTTON、CHECKBOX、RADIOBUTTON 及各种 LIST_BOX 变体。 [CODE] GRAPH_ELEMENT_TYPE_FORM, // Form GRAPH_ELEMENT_TYPE_WINDOW, // Window //--- WinForms GRAPH_ELEMENT_TYPE_WF_UNDERLAY, // Panel object underlay GRAPH_ELEMENT_TYPE_WF_BASE, // Windows Forms Base //--- 'Container' object types are to be set below GRAPH_ELEMENT_TYPE_WF_CONTAINER, // Windows Forms container base object GRAPH_ELEMENT_TYPE_WF_PANEL, // Windows Forms Panel GRAPH_ELEMENT_TYPE_WF_GROUPBOX, // Windows Forms GroupBox GRAPH_ELEMENT_TYPE_WF_TAB_CONTROL, // Windows Forms TabControl GRAPH_ELEMENT_TYPE_WF_SPLIT_CONTAINER, // Windows Forms SplitContainer //--- 'Standard control' object types are to be set below GRAPH_ELEMENT_TYPE_WF_COMMON_BASE, // Windows Forms base standard control GRAPH_ELEMENT_TYPE_WF_LABEL, // Windows Forms Label GRAPH_ELEMENT_TYPE_WF_BUTTON, // Windows Forms Button GRAPH_ELEMENT_TYPE_WF_CHECKBOX, // Windows Forms CheckBox GRAPH_ELEMENT_TYPE_WF_RADIOBUTTON, // Windows Forms RadioButton GRAPH_ELEMENT_TYPE_WF_ELEMENTS_LIST_BOX, // Base list object of Windows Forms elements GRAPH_ELEMENT_TYPE_WF_LIST_BOX, // Windows Forms ListBox GRAPH_ELEMENT_TYPE_WF_CHECKED_LIST_BOX, // Windows Forms CheckedListBox GRAPH_ELEMENT_TYPE_WF_BUTTON_LIST_BOX, // Windows Forms ButtonListBox //--- Auxiliary elements of WinForms objects GRAPH_ELEMENT_TYPE_WF_LIST_BOX_ITEM, // Windows Forms ListBoxItem GRAPH_ELEMENT_TYPE_WF_TAB_HEADER, // Windows Forms TabHeader GRAPH_ELEMENT_TYPE_WF_TAB_FIELD, // Windows Forms TabField GRAPH_ELEMENT_TYPE_WF_SPLIT_CONTAINER_PANEL,// Windows Forms SplitContainerPanel GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON, // Windows Forms ArrowButton GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_UP, // Windows Forms UpArrowButton GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_DOWN, // Windows Forms DownArrowButton GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_LEFT, // Windows Forms LeftArrowButton GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_RIGHT, // Windows Forms RightArrowButton [/CODE] 逐行看:前两项是表单与窗口根基;WF_UNDERLAY 和 WF_BASE 是 WinForms 的底层垫板和基类;容器段里 WF_SPLIT_CONTAINER 用来做可拖拽分隔布局,辅助段里的 WF_SPLIT_CONTAINER_PANEL 才是它内部的子面板。箭头按钮拆成上下左右四个独立枚举,说明滚动条或微调器在底层是分方向实例化的。 开 MT5 按 F4 进 MetaEditor,在 GraphObject 相关头文件里搜 GRAPH_ELEMENT_TYPE 就能看到这套定义。外汇与贵金属杠杆高、风险大,用自定义面板做信号展示可以,但别把它当进场依据直接跑实盘。

MQL5 / C++
GRAPH_ELEMENT_TYPE_FORM,                    class=class="str">"cmt">// Form
GRAPH_ELEMENT_TYPE_WINDOW,                  class=class="str">"cmt">// Window
class=class="str">"cmt">//--- WinForms
GRAPH_ELEMENT_TYPE_WF_UNDERLAY,             class=class="str">"cmt">// Panel object underlay
GRAPH_ELEMENT_TYPE_WF_BASE,                 class=class="str">"cmt">// Windows Forms Base
class=class="str">"cmt">//--- &class="macro">#x27;Container&class="macro">#x27; object types are to be set below
GRAPH_ELEMENT_TYPE_WF_CONTAINER,            class=class="str">"cmt">// Windows Forms container base object
GRAPH_ELEMENT_TYPE_WF_PANEL,                class=class="str">"cmt">// Windows Forms Panel
GRAPH_ELEMENT_TYPE_WF_GROUPBOX,             class=class="str">"cmt">// Windows Forms GroupBox
GRAPH_ELEMENT_TYPE_WF_TAB_CONTROL,          class=class="str">"cmt">// Windows Forms TabControl
GRAPH_ELEMENT_TYPE_WF_SPLIT_CONTAINER,      class=class="str">"cmt">// Windows Forms SplitContainer
class=class="str">"cmt">//--- &class="macro">#x27;Standard control&class="macro">#x27; object types are to be set below
GRAPH_ELEMENT_TYPE_WF_COMMON_BASE,          class=class="str">"cmt">// Windows Forms base standard control
GRAPH_ELEMENT_TYPE_WF_LABEL,                class=class="str">"cmt">// Windows Forms Label
GRAPH_ELEMENT_TYPE_WF_BUTTON,               class=class="str">"cmt">// Windows Forms Button
GRAPH_ELEMENT_TYPE_WF_CHECKBOX,             class=class="str">"cmt">// Windows Forms CheckBox
GRAPH_ELEMENT_TYPE_WF_RADIOBUTTON,          class=class="str">"cmt">// Windows Forms RadioButton
GRAPH_ELEMENT_TYPE_WF_ELEMENTS_LIST_BOX,   class=class="str">"cmt">// Base list object of Windows Forms elements
GRAPH_ELEMENT_TYPE_WF_LIST_BOX,             class=class="str">"cmt">// Windows Forms ListBox
GRAPH_ELEMENT_TYPE_WF_CHECKED_LIST_BOX,     class=class="str">"cmt">// Windows Forms CheckedListBox
GRAPH_ELEMENT_TYPE_WF_BUTTON_LIST_BOX,      class=class="str">"cmt">// Windows Forms ButtonListBox
class=class="str">"cmt">//--- Auxiliary elements of WinForms objects
GRAPH_ELEMENT_TYPE_WF_LIST_BOX_ITEM,        class=class="str">"cmt">// Windows Forms ListBoxItem
GRAPH_ELEMENT_TYPE_WF_TAB_HEADER,           class=class="str">"cmt">// Windows Forms TabHeader
GRAPH_ELEMENT_TYPE_WF_TAB_FIELD,            class=class="str">"cmt">// Windows Forms TabField
GRAPH_ELEMENT_TYPE_WF_SPLIT_CONTAINER_PANEL,class=class="str">"cmt">// Windows Forms SplitContainerPanel
GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON,         class=class="str">"cmt">// Windows Forms ArrowButton
GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_UP,      class=class="str">"cmt">// Windows Forms UpArrowButton
GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_DOWN,    class=class="str">"cmt">// Windows Forms DownArrowButton
GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_LEFT,    class=class="str">"cmt">// Windows Forms LeftArrowButton
GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_RIGHT,   class=class="str">"cmt">// Windows Forms RightArrowButton

◍ 画布控件枚举与整数属性边界

在 MT5 自定义画布(Canvas)框架里,图形元素类型用枚举收口,例如箭头按钮盒分上下向(GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTONS_UD_BOX)与左右向(GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTONS_LR_BOX)两类,对应 Windows Forms 的 UpDown / LeftRight 箭头按钮组。 分割容器(Split Container)的固定面板由 ENUM_CANV_ELEMENT_SPLIT_CONTAINER_FIXED_PANEL 决定:NONE 表示随容器缩放、PANEL_1 / PANEL_2 则指定某一侧面板保持尺寸不变。这在做多图表悬浮面板时直接影响布局重绘逻辑。 整数属性枚举 ENUM_CANV_ELEMENT_PROP_INTEGER 从 CANV_ELEMENT_PROP_ID = 0 起步,末尾追加了 9 个 Split Container 相关项(固定面板、分隔条是否可拖、分隔距离、宽度、两面板折叠与最小尺寸标记)。宏 CANV_ELEMENT_PROP_INTEGER_TOTAL 被定为 105,CANV_ELEMENT_PROP_INTEGER_SKIP 为 0,说明全部整数属性均可参与排序。 排序偏移靠两个宏算出:FIRST_CANV_ELEMENT_DBL_PROP 与 FIRST_CANV_ELEMENT_STR_PROP,均以 105 减去跳过数后再叠加倍精度 / 字符串属性总数。开 MT5 写 CCanvas 派生类时,直接引用这些宏能避免手写魔法数字导致越界。

MQL5 / C++
enum ENUM_CANV_ELEMENT_SPLIT_CONTAINER_FIXED_PANEL
  {
  CANV_ELEMENT_SPLIT_CONTAINER_FIXED_PANEL_NONE,      class=class="str">"cmt">// None
  CANV_ELEMENT_SPLIT_CONTAINER_FIXED_PANEL_1,         class=class="str">"cmt">// Panel1
  CANV_ELEMENT_SPLIT_CONTAINER_FIXED_PANEL_2,         class=class="str">"cmt">// Panel2
  };

enum ENUM_CANV_ELEMENT_PROP_INTEGER
  {
  CANV_ELEMENT_PROP_ID = class="num">0,                           class=class="str">"cmt">// Element ID
  CANV_ELEMENT_PROP_TYPE,                             class=class="str">"cmt">// Graphical element type
  CANV_ELEMENT_PROP_TAB_PAGE_COLUMN,                  class=class="str">"cmt">// Tab column index
  CANV_ELEMENT_PROP_ALIGNMENT,                        class=class="str">"cmt">// Location of an object inside the control
  CANV_ELEMENT_PROP_SPLIT_CONTAINER_FIXED_PANEL,      class=class="str">"cmt">// Panel that retains its size when the container is resized
  CANV_ELEMENT_PROP_SPLIT_CONTAINER_SPLITTER_FIXED,   class=class="str">"cmt">// Separator moveability flag
  CANV_ELEMENT_PROP_SPLIT_CONTAINER_SPLITTER_DISTANCE,class=class="str">"cmt">// Distance from edge to separator
  CANV_ELEMENT_PROP_SPLIT_CONTAINER_SPLITTER_WIDTH,   class=class="str">"cmt">// Separator width
  CANV_ELEMENT_PROP_SPLIT_CONTAINER_PANEL1_COLLAPSED, class=class="str">"cmt">// Flag for collapsed panel class="num">1
  CANV_ELEMENT_PROP_SPLIT_CONTAINER_PANEL1_MIN_SIZE,  class=class="str">"cmt">// Panel class="num">1 minimum size
  CANV_ELEMENT_PROP_SPLIT_CONTAINER_PANEL2_COLLAPSED, class=class="str">"cmt">// Flag for collapsed panel class="num">2
  CANV_ELEMENT_PROP_SPLIT_CONTAINER_PANEL2_MIN_SIZE,  class=class="str">"cmt">// Panel class="num">2 minimum size
  };

class="macro">#define CANV_ELEMENT_PROP_INTEGER_TOTAL(class="num">105)         class=class="str">"cmt">// Total number of integer properties
class="macro">#define CANV_ELEMENT_PROP_INTEGER_SKIP(class="num">0)           class=class="str">"cmt">// Number of integer properties not used in sorting

class="macro">#define FIRST_CANV_ELEMENT_DBL_PROP(CANV_ELEMENT_PROP_INTEGER_TOTAL-CANV_ELEMENT_PROP_INTEGER_SKIP)
class="macro">#define FIRST_CANV_ELEMENT_STR_PROP(CANV_ELEMENT_PROP_INTEGER_TOTAL-CANV_ELEMENT_PROP_INTEGER_SKIP+CANV_ELEMENT_PROP_DOUBLE_TOTAL-CANV_ELEMENT_PROP_DOUBLE_SKIP)

enum ENUM_SORT_CANV_ELEMENT_MODE
  {
  SORT_BY_CANV_ELEMENT_ID = class="num">0,                        class=class="str">"cmt">// Sort by element ID
  };

「拆开分栏容器的排序与属性枚举」

在 MT5 自定义图形库里,分栏容器(SplitContainer)相关的排序枚举和消息常量往往被忽略,但它们直接决定你遍历控件树时拿到的顺序。下面这段枚举把画布元素按分栏容器特征排序的字段列全了:固定面板、分隔条是否可拖、分隔距离、分隔宽度,以及两个子面板各自的折叠标志和最小尺寸。 SORT_BY_CANV_ELEMENT_SPLIT_CONTAINER_FIXED_PANEL 对应「容器缩放时保持尺寸的面板」,SORT_BY_CANV_ELEMENT_SPLIT_CONTAINER_SPLITTER_DISTANCE 是「边缘到分隔条的距离」,SORT_BY_CANV_ELEMENT_SPLIT_CONTAINER_PANEL2_MIN_SIZE 则锁定「面板2最小尺寸」。这些常量从 SORT_BY_CANV_ELEMENT_TYPE 往后排,说明底层把分栏容器当作一等画布元素处理。 消息侧也有专用标识:MSG_GRAPH_ELEMENT_TYPE_WF_SPLIT_CONTAINER 标记整个分栏控件,MSG_GRAPH_ELEMENT_TYPE_WF_SPLIT_CONTAINER_PANEL 只指标内嵌面板;取对象失败时会抛 MSG_ERR_FAILED_GET_SPLIT_CONTAINER_PANEL_OBJ。整数属性 MSG_CANV_ELEMENT_PROP_SPLIT_CONTAINER_SPLITTER_WIDTH 让你在代码里直接读分隔条像素宽,省去自己算边框。 开 MT5 切到 MetaEditor,搜 SORT_BY_CANV_ELEMENT_SPLIT_CONTAINER 就能看到完整定义;改一个面板的最小尺寸常量,界面布局逻辑可能就倾向按新阈值重排。外汇与贵金属图表插件开发属高风险定制,参数误用可能让 EA 面板渲染异常。

MQL5 / C++
  SORT_BY_CANV_ELEMENT_TYPE,                                                                     class=class="str">"cmt">// Sort by graphical element type
class=class="str">"cmt">//---...
class=class="str">"cmt">//---...
  SORT_BY_CANV_ELEMENT_TAB_PAGE_COLUMN,                                                           class=class="str">"cmt">// Sort by tab column index
  SORT_BY_CANV_ELEMENT_ALIGNMENT,                                                                 class=class="str">"cmt">// Sort by the location of the object inside the control
  SORT_BY_CANV_ELEMENT_SPLIT_CONTAINER_FIXED_PANEL,                                               class=class="str">"cmt">// Sort by the panel that retains its size when the container is resized
  SORT_BY_CANV_ELEMENT_SPLIT_CONTAINER_SPLITTER_FIXED,                                            class=class="str">"cmt">// Sort by the separator moveability flag
  SORT_BY_CANV_ELEMENT_SPLIT_CONTAINER_SPLITTER_DISTANCE,                                         class=class="str">"cmt">// Sort by distance from edge to separator
  SORT_BY_CANV_ELEMENT_SPLIT_CONTAINER_SPLITTER_WIDTH,                                            class=class="str">"cmt">// Sort by separator width
  SORT_BY_CANV_ELEMENT_SPLIT_CONTAINER_PANEL1_COLLAPSED,                                          class=class="str">"cmt">// Sort by flag for collapsed panel class="num">1
  SORT_BY_CANV_ELEMENT_SPLIT_CONTAINER_PANEL1_MIN_SIZE,                                           class=class="str">"cmt">// Sort by panel class="num">1 minimum size
  SORT_BY_CANV_ELEMENT_SPLIT_CONTAINER_PANEL2_COLLAPSED,                                          class=class="str">"cmt">// Sort by flag for collapsed panel class="num">2
  SORT_BY_CANV_ELEMENT_SPLIT_CONTAINER_PANEL2_MIN_SIZE,                                           class=class="str">"cmt">// Sort by panel class="num">2 minimum size
class=class="str">"cmt">//--- Sort by real properties
class=class="str">"cmt">//--- Sort by class="type">class="kw">string properties
  SORT_BY_CANV_ELEMENT_NAME_OBJ = FIRST_CANV_ELEMENT_STR_PROP,                                    class=class="str">"cmt">// Sort by an element object name
  SORT_BY_CANV_ELEMENT_NAME_RES,                                                                  class=class="str">"cmt">// Sort by the graphical resource name
  SORT_BY_CANV_ELEMENT_TEXT,                                                                      class=class="str">"cmt">// Sort by graphical element text
  SORT_BY_CANV_ELEMENT_DESCRIPTION,                                                               class=class="str">"cmt">// Sort by graphical element description
  };
class=class="str">"cmt">//+------------------------------------------------------------------+
  MSG_GRAPH_ELEMENT_TYPE_WF_TAB_FIELD,                                                            class=class="str">"cmt">// TabControl tab field
  MSG_GRAPH_ELEMENT_TYPE_WF_TAB_CONTROL,                                                          class=class="str">"cmt">// TabControl
  MSG_GRAPH_ELEMENT_TYPE_WF_SPLIT_CONTAINER_PANEL,                                                class=class="str">"cmt">// SplitContainerPanel control panel
  MSG_GRAPH_ELEMENT_TYPE_WF_SPLIT_CONTAINER,                                                      class=class="str">"cmt">// SplitContainer control
  MSG_GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON,                                                         class=class="str">"cmt">// ArrowButton control
  MSG_GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_UP,                                                      class=class="str">"cmt">// UpArrowButton control
class=class="str">"cmt">//--- CTabControl
  MSG_ERR_FAILED_GET_TAB_OBJ,                                                                     class=class="str">"cmt">// Failed to get TabControl tab
class=class="str">"cmt">//--- CSplitContainer
  MSG_ERR_FAILED_GET_SPLIT_CONTAINER_PANEL_OBJ,                                                   class=class="str">"cmt">// Failed to get SplitContainer control panel
class=class="str">"cmt">//--- Integer properties of graphical elements
  MSG_CANV_ELEMENT_PROP_TAB_PAGE_COLUMN,                                                          class=class="str">"cmt">// Tab column index
  MSG_CANV_ELEMENT_PROP_ALIGNMENT,                                                                class=class="str">"cmt">// Location of an object inside the control
  MSG_CANV_ELEMENT_PROP_SPLIT_CONTAINER_FIXED_PANEL,                                              class=class="str">"cmt">// Panel that retains its size when the container is resized
  MSG_CANV_ELEMENT_PROP_SPLIT_CONTAINER_SPLITTER_FIXED,                                           class=class="str">"cmt">// Separator moveability flag
  MSG_CANV_ELEMENT_PROP_SPLIT_CONTAINER_SPLITTER_DISTANCE,                                        class=class="str">"cmt">// Distance from edge to separator
  MSG_CANV_ELEMENT_PROP_SPLIT_CONTAINER_SPLITTER_WIDTH,                                           class=class="str">"cmt">// Separator width

常见问题

先建一个 SplitContainer 骨架,把左右面板挂到它的两个子区域,再绑定鼠标拖拽事件改分隔条位置即可。
在画布控件枚举里给 SplitContainer 单独留一段连续整数区间,新属性只在这段内递增定义。
小布可读取你的控件结构描述,自动生成双面板布局并接好拖拽分隔,你只需确认左右挂什么模块。
排序只管子面板先后,属性管外观与状态,拆开后能分别扩展而不互相污染枚举值。
容易漏掉分隔条自身的命中区域枚举,导致拖拽时判不到条子,补一个独立命中类型就行。