DoEasy. 控件 (第 14 部分): 命名图形元素的新算法。 继续操控 TabControl WinForms 对象·进阶篇
(2/3)· 63 字符限制曾让嵌套控件直接建不出来,本篇换一套命名逻辑彻底绕开
把图形元素枚举名洗成可读字符串
在 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 里建个脚本跑这个函数,传入任意图形元素枚举,就能拿到干净的类型标签,方便做面板控件的调试输出。 外汇与贵金属图表上挂这类自定义面板属于高风险操作,参数或对象归属判断失误可能导致脚本异常,建议先在策略测试器里验证。
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 逻辑需注意:对象名冲突可能导致脚本异常,属高风险操作环境,建议先在策略测试器里验证。
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 脚本涉及实时报价刷新,这类自定义面板在高波动时段可能卡顿,属于高风险调试场景,参数改动前建议先离线回测。
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 数组落盘可提高恢复概率。
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 再批量挂。
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 自定义图形在高波动行情下可能出现重绘延迟,属高风险调试项。
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,