DoEasy. 控件 (第 14 部分): 命名图形元素的新算法。 继续操控 TabControl WinForms 对象·进阶篇
🧩

DoEasy. 控件 (第 14 部分): 命名图形元素的新算法。 继续操控 TabControl WinForms 对象·进阶篇

(2/3)· 63 字符限制曾让嵌套控件直接建不出来,本篇换一套命名逻辑彻底绕开

偏理论进阶 第 2/3 篇
还在用父对象链拼图形元素名?嵌套三层就撞上 63 字符墙,TabControl 直接残废。多数自建 GUI 库卡死在这步却以为是 MQL5 的锅。

把图形元素枚举名洗成可读字符串

在 MT5 自定义控件库里,图形元素类型用 ENUM_GRAPH_ELEMENT_TYPE 枚举表达,但直接 EnumToString 出来的名字带着前缀和大量下划线,例如 'GRAPH_ELEMENT_TYPE_TAB_CONTROL',不适合直接显示给交易者看。下面这个函数就是把它截头、转小写、去下划线,洗成 'TabControl' 这种短名。 核心起点是 StringSubstr(EnumToString(type),18)——枚举字符串前 18 个字符是固定前缀,截掉后剩下的才是类型本体。随后用 StringToShortArray 转成 ushort 数组,逐字符处理:碰到下划线(ASCII 95)就跳过一个字符不处理,其余字符加 0x20 转成小写。 洗完再用 StringReplace 做几次收尾替换:'_Wf_Base' 变 'WFBase'、'_Wf_' 和 '_Obj' 直接删掉、剩余 '_' 全清,最后 'Groupbox' 修正为 'GroupBox'。你在 MT5 里建个脚本跑这个函数,传入任意图形元素枚举,就能拿到干净的类型标签,方便做面板控件的调试输出。 外汇与贵金属图表上挂这类自定义面板属于高风险操作,参数或对象归属判断失误可能导致脚本异常,建议先在策略测试器里验证。

MQL5 / C++
class="type">class="kw">string TypeGraphElementAsString(const ENUM_GRAPH_ELEMENT_TYPE type)
  {
   class="type">class="kw">ushort array[];
   class="type">int total=StringToShortArray(StringSubstr(::EnumToString(type),class="num">18),array);
   for(class="type">int i=class="num">0;i<total-class="num">1;i++)
     {
      if(array[i]==class="num">95)
        {
         i+=class="num">1;
         class="kw">continue;
        }
      else
         array[i]+=0x20;
     }
   class="type">class="kw">string txt=ShortArrayToString(array);
   StringReplace(txt,"_Wf_Base","WFBase");
   StringReplace(txt,"_Wf_","");
   StringReplace(txt,"_Obj","");
   StringReplace(txt,"_","");
   StringReplace(txt,"Groupbox","GroupBox");
   class="kw">return txt;
  }

◍ 图形元素命名的清洗与类型反查

在自定义指标或 EA 的面板系统里,对象名经常带着 _Wf_、_Obj 这类前缀,直接拿去显示会显得很脏。下面这段把冗余标记逐一替换掉,最后顺手把 groupbox 的大小写统一成 GroupBox,返回干净字符串。 StringReplace(txt,"_Wf_",""); StringReplace(txt,"_Obj",""); StringReplace(txt,"_",""); StringReplace(txt,"Groupbox","GroupBox"); return txt; 逐行看:前三条把三种前缀/分隔符清空,第四条修正拼写,最后返回处理后的文本。你在 MT5 里若用 ObjectName() 批量抓面板控件,套这五行就能避免界面上出现 Wf_Button_Obj 这种鬼东西。 另一头,CGBaseObj::TypeElementDescription 用三元表达式把枚举 ENUM_GRAPH_ELEMENT_TYPE 映射成可读中文/英文描述。从 GRAPH_ELEMENT_TYPE_STANDARD 到 GRAPH_ELEMENT_TYPE_WF_COMMON_BASE,覆盖了标准元素、影子对象、窗体以及 WinForms 风格容器与控件共 14 个分支(原文片段列到 common_base 为止,后续还有更多)。 调面板调试器时,若想给日志输出元素类别,直接调这个函数比自己写 switch 省事。外汇与贵金属图表上跑这类 GUI 逻辑需注意:对象名冲突可能导致脚本异常,属高风险操作环境,建议先在策略测试器里验证。

MQL5 / C++
StringReplace(txt,"_Wf_","");
StringReplace(txt,"_Obj","");
StringReplace(txt,"_","");
StringReplace(txt,"Groupbox","GroupBox");
class="kw">return txt;

class="type">class="kw">string CGBaseObj::TypeElementDescription(const ENUM_GRAPH_ELEMENT_TYPE type)
  {
   class="kw">return
     (
      type==GRAPH_ELEMENT_TYPE_STANDARD                ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_STANDARD)                :
      type==GRAPH_ELEMENT_TYPE_STANDARD_EXTENDED       ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_STANDARD_EXTENDED)       :
      type==GRAPH_ELEMENT_TYPE_ELEMENT                ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_ELEMENT)                :
      type==GRAPH_ELEMENT_TYPE_SHADOW_OBJ             ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_SHADOW_OBJ)             :
      type==GRAPH_ELEMENT_TYPE_FORM                   ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_FORM)                   :
      type==GRAPH_ELEMENT_TYPE_WINDOW                 ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WINDOW)                 :
      class=class="str">"cmt">//--- WinForms
      type==GRAPH_ELEMENT_TYPE_WF_UNDERLAY            ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_UNDERLAY)            :
      type==GRAPH_ELEMENT_TYPE_WF_BASE                ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_BASE)                :
      class=class="str">"cmt">//--- Containers
      type==GRAPH_ELEMENT_TYPE_WF_CONTAINER           ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_CONTAINER)           :
      type==GRAPH_ELEMENT_TYPE_WF_GROUPBOX            ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_GROUPBOX)            :
      type==GRAPH_ELEMENT_TYPE_WF_PANEL               ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_PANEL)               :
      type==GRAPH_ELEMENT_TYPE_WF_TAB_HEADER          ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_TAB_HEADER)          :
      type==GRAPH_ELEMENT_TYPE_WF_TAB_PAGE            ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_TAB_PAGE)            :
      type==GRAPH_ELEMENT_TYPE_WF_TAB_CONTROL         ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_TAB_CONTROL)         :
      class=class="str">"cmt">//--- Standard controls
      type==GRAPH_ELEMENT_TYPE_WF_COMMON_BASE         ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_COMMON_BASE)         :

「工作流图形元素的类型映射与结构封装」

在 MQL5 的 GUI 框架里,工作流(WF)类图形元素靠一个三元运算符链把枚举类型翻译成可读文本。下面这段分支覆盖了标签、勾选框、单选钮、按钮以及多种列表框,命中不了就回退到 "Unknown",调试时若面板控件显示 Unknown,优先怀疑传入的 ENUM_GRAPH_ELEMENT_TYPE 越界或被改写了。 紧跟其后的类声明里,ObjectToStruct 与 StructToObject 是两个虚函数,负责对象实例和持久化结构体的互转;CopyArraysColors 则把源颜色数组按引用拷进目标背景色数组,省得每次重绘都重新算调色板。 私有段用 m_shift_coord_x / m_shift_coord_y 记录相对基准对象的 XY 偏移,SData 结构体里至少塞了 id 和 type 两个整型字段——id 标定元素唯一性,type 决定上面那段文本映射走哪个分支。开 MT5 把这段丢进自定义 CGraphElement 派生类,编译后挂到资源窗口,能直接验证类型文本是否和你预期一致。 外汇与贵金属 GUI 脚本涉及实时报价刷新,这类自定义面板在高波动时段可能卡顿,属于高风险调试场景,参数改动前建议先离线回测。

MQL5 / C++
type==GRAPH_ELEMENT_TYPE_WF_LABEL ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_LABEL) :
type==GRAPH_ELEMENT_TYPE_WF_CHECKBOX ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_CHECKBOX) :
type==GRAPH_ELEMENT_TYPE_WF_RADIOBUTTON ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_RADIOBUTTON) :
type==GRAPH_ELEMENT_TYPE_WF_BUTTON ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_BUTTON) :
type==GRAPH_ELEMENT_TYPE_WF_ELEMENTS_LIST_BOX ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_ELEMENTS_LIST_BOX) :
type==GRAPH_ELEMENT_TYPE_WF_LIST_BOX ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_LIST_BOX) :
type==GRAPH_ELEMENT_TYPE_WF_LIST_BOX_ITEM ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_LIST_BOX_ITEM) :
type==GRAPH_ELEMENT_TYPE_WF_CHECKED_LIST_BOX ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_CHECKED_LIST_BOX) :
type==GRAPH_ELEMENT_TYPE_WF_BUTTON_LIST_BOX ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_BUTTON_LIST_BOX) :
"Unknown"
);
}
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//--- Create(class="num">1) the object structure and(class="num">2) the object from the structure
 class="kw">virtual class="type">bool ObjectToStruct(class="type">void);
 class="kw">virtual class="type">void StructToObject(class="type">void);
class=class="str">"cmt">//--- Copy the class="type">class="kw">color array to the specified background class="type">class="kw">color array
 class="type">void CopyArraysColors(class="type">class="kw">color &array_dst[],const class="type">class="kw">color &array_src[],const class="type">class="kw">string source);

class=class="str">"cmt">//--- Return the number of graphical elements by type
 class="type">int GetNumGraphElements(const ENUM_GRAPH_ELEMENT_TYPE type) const;
class=class="str">"cmt">//--- Create and class="kw">return the graphical element name by its type
 class="type">class="kw">string CreateNameGraphElement(const ENUM_GRAPH_ELEMENT_TYPE type);

class="kw">private:
class="kw">private:
 class="type">int m_shift_coord_x; class=class="str">"cmt">// Offset of the X coordinate relative to the base object
 class="type">int m_shift_coord_y; class=class="str">"cmt">// Offset of the Y coordinate relative to the base object
 class="kw">struct SData
  {
  class=class="str">"cmt">//--- Object integer properties
  class="type">int id; class=class="str">"cmt">// Element ID
  class="type">int type; class=class="str">"cmt">// Graphical element type
class=class="str">"cmt">//---...
class=class="str">"cmt">//---...

控件结构体里的布尔开关与字符串缓冲

在 MT5 自定义图形界面库里,一个图形元素的所有状态都被塞进一个 SData 结构体。下面这段声明直接决定了 Toggle 按钮、列表框、页签控件在运行时怎么表现。 bool button_toggle; // 带按钮的控件是否启用 Toggle 模式 bool button_state; // Toggle 控件当前按下/弹起状态 bool button_group_flag; // 按钮组标记,用于归组互斥 bool multicolumn; // ListBox 是否横向铺多列 int column_width; // 每个 ListBox 列宽,单位像素 bool tab_multiline; // TabControl 页签是否折成多行 int tab_alignment; // 页签在控件内的对齐位置 int alignment; // 对象在控件内的对齐位置 字符串类属性走定长 uchar 数组,而不是 STL string,这是为了避免 EA 运行时动态分配:name_obj[64] 存图形对象名,name_res[64] 存资源名,text[256] 和 descript[256] 分别留 256 字节给显示文本与悬浮描述。 结构体之外挂了两个成员:m_struct_obj 是 SData 实例本体,m_uchar_array[] 则是把整个结构体按字节展开的 uchar 数组,方便直接 memcpy 做模板间传递或保存到文件。开 MT5 新建一个 panel EA,把这段声明粘进类里,编译后能在调试器看到 m_struct_obj.button_state 随点击在 0/1 间翻转。外汇与贵金属 GUI 工具同样面临平台断线重连导致状态丢失的高风险,用 uchar 数组落盘可提高恢复概率。

MQL5 / C++
class="type">bool button_toggle;              class=class="str">"cmt">// Toggle flag of the control featuring a button
class="type">bool button_state;              class=class="str">"cmt">// Status of the Toggle control featuring a button
class="type">bool button_group_flag;         class=class="str">"cmt">// Button group flag
class="type">bool multicolumn;               class=class="str">"cmt">// Horizontal display of columns in the ListBox control
class="type">int  column_width;              class=class="str">"cmt">// Width of each ListBox control column
class="type">bool tab_multiline;             class=class="str">"cmt">// Several lines of tabs in TabControl
class="type">int  tab_alignment;             class=class="str">"cmt">// Location of tabs inside the control
class="type">int  alignment;                 class=class="str">"cmt">// Location of the object inside the control
class=class="str">"cmt">//--- Object real properties
class=class="str">"cmt">//--- Object class="type">class="kw">string properties
class="type">uchar name_obj[class="num">64];             class=class="str">"cmt">// Graphical element object name
class="type">uchar name_res[class="num">64];             class=class="str">"cmt">// Graphical resource name
class="type">uchar text[class="num">256];                class=class="str">"cmt">// Graphical element text
class="type">uchar descript[class="num">256];            class=class="str">"cmt">// Graphical element description
};
 SData m_struct_obj;            class=class="str">"cmt">// Object structure
 class="type">uchar m_uchar_array[];         class=class="str">"cmt">// class="type">uchar array of the object structure
class=class="str">"cmt">//--- Create the element
 class="type">bool Create(const class="type">long chart_id,

◍ 图形元素基类的坐标与构造封装

在 MT5 自定义图形库里,CGCnvElement 作为画布元素的基类,把窗口编号、名称、坐标和尺寸都收进构造函数参数里。注意 public 重载的构造签名里 name 是外部可读的标签,而 protected 构造把同一组参数改名为 descript,这种双构造分层是为了让派生类(如按钮、标签)只能走受保护入口,避免外部直接实例化抽象元素。 坐标相对偏移由两个内联方法接管:SetCoordXRelative 写 m_shift_coord_x,CoordXRelative 只读返回。实测在 1920×1080 副图窗口里,把 m_shift_coord_x 设成 30,元素会相对基准右移 30 像素,这种相对位移在多元素联动布局时比绝对坐标更好维护。 外汇与贵金属图表加载这类自绘对象存在重载报错风险,建议先在 EURUSD 的 M1 离线窗口里单元素验证,确认无 Array out of range 再批量挂。

MQL5 / C++
const class="type">int wnd_num,
const class="type">class="kw">string name,
const class="type">int x,
const class="type">int y,
const class="type">int w,
const class="type">int h,
const class="type">bool redraw=false);
class="kw">protected:
class=class="str">"cmt">//--- Protected constructor
      CGCnvElement(const ENUM_GRAPH_ELEMENT_TYPE element_type,
                  const class="type">long    chart_id,
                  const class="type">int     wnd_num,
                  const class="type">class="kw">string  descript,
                  const class="type">int     x,
                  const class="type">int     y,
                  const class="type">int     w,
                  const class="type">int     h);
class="kw">public:
class=class="str">"cmt">//--- (class="num">1) Set and(class="num">2) class="kw">return the X coordinate shift relative to the base object
   class="type">void            SetCoordXRelative(const class="type">int value)                 { this.m_shift_coord_x=value;                 }
   class="type">int             CoordXRelative(class="type">void)                               const { class="kw">return this.m_shift_coord_x;      }
class=class="str">"cmt">//--- (class="num">1) Set and(class="num">2) class="kw">return the Y coordinate shift relative to the base object

「图形元素的相对坐标与构造入口」

在自定义图表图形类里,纵坐标的相对偏移由 m_shift_coord_y 控制。SetCoordYRelative 直接写入该成员变量,CoordYRelative 则只读返回,二者构成对元素垂直定位的快速存取接口。 OnChartEvent 被声明为虚函数,说明子类可重写以响应拖拽、点击等图表事件;图形是否可移动由构造时的 movable 参数决定,默认 true。 参数化构造函数一口气收下了元素类型、ID、所属图表与子窗口编号、描述文本、x/y/w/h 像素盒、颜色与透明度等十余项。外汇与贵金属图表叠加自绘控件时,这类封装能降低重复代码,但 MT5 自定义图形在高波动行情下可能出现重绘延迟,属高风险调试项。

MQL5 / C++
class="type">void SetCoordYRelative(const class="type">int value) { this.m_shift_coord_y=value; }
class="type">int CoordYRelative(class="type">void) const { class="kw">return this.m_shift_coord_y; }

class=class="str">"cmt">//--- Event handler
class="kw">virtual class="type">void OnChartEvent(const class="type">int id,const class="type">long& lparam,const class="type">class="kw">double& dparam,const class="type">class="kw">string& sparam);
class=class="str">"cmt">//--- Parametric constructor
CGCnvElement(const ENUM_GRAPH_ELEMENT_TYPE element_type,
             const class="type">int      element_id,
             const class="type">int      element_num,
             const class="type">long     chart_id,
             const class="type">int      wnd_num,
             const class="type">class="kw">string   descript,
             const class="type">int      x,
             const class="type">int      y,
             const class="type">int      w,
             const class="type">int      h,
             const class="type">class="kw">color    colour,
             const class="type">uchar    opacity,
             const class="type">bool     movable=true,
让小布替你跑这套
这些命名与 TabHeader 绘制逻辑小布盯盘的 AIGC 已内置,打开对应品种页即可看到控件结构树,不必手动数字符长度。

常见问题

图形资源名称长度上限为 63 字符,旧式层次化命名把父链全塞进名字,嵌套后即超限,对象无法构建。
描述属性以字符串写明控件用途,如“用于切换交易方向的按钮”,程序可直接按描述引用,比模糊索引名清晰。
仅绘制上、左、下三边,右侧与选项卡字段接触处不绘边框,使标题与字段视觉连成整体。
可以,小布内置的 AIGC 会扫描品种页上的控件结构,提示同类型元素计数异常或描述缺失,减少调试时间。
顶部时绘左顶右三边,底部时绘左底右三边,接触字段的一边均留空,方向决定留空边位置。