图形界面 X: 文本编辑框, 图片滑块和简单控件 (构建 5)(基础篇)
📘

图形界面 X: 文本编辑框, 图片滑块和简单控件 (构建 5)(基础篇)

第 1/3 篇

在 MT5 里搭一个能用的文本编辑框

想在 MT5 的 EA 或指标面板上放一个可输入参数的文本框,核心是用 Windows API 的 CreateWindowExW 配合 EDIT 类。官方示例里这一套控件被归在「图形界面 X」构建 5,发布于 2017-01-19,累计查看 4826 次,说明老交易者早就在折腾自定义面板了。 外汇和贵金属杠杆高、滑点猛,面板只是辅助,别指望控件能替你管风险。文本编辑框适合让人工临时改手数或偏离阈值,比每次重编译省事。 下面这段是示例里创建编辑框的关键调用,逐行拆一下你就知道改哪几个参数能挪位置、限定长度。

MQL5 / C++
m_editHandle = CreateWindowExW(class="num">0, L"EDIT", L"", WS_CHILD | WS_VISIBLE | WS_BORDER | ES_AUTOHSCROLL, class="num">10, class="num">10, class="num">120, class="num">20, m_parentHandle, NULL, m_instanceHandle, NULL);

「标准控件类的封装与字体处理」

MQL5 标准库把文本编辑框、图片滑块、文本标签和图片控件都做成了独立类,交易者开 MT5 后可在 Include/Controls 路径下直接翻到这些封装,省去从零写窗口消息循环的麻烦。 文本编辑框类负责接收键盘输入与光标管理,图片滑块类则把位图资源映射到可拖拽轨道,二者都依赖底层 OnEvent 回调把鼠标坐标转成控件状态。 字体渲染由专门的 CFonts 类接管,它能按字号、粗细、字符集动态创建 GDI 资源;外汇与贵金属图表挂自定义面板时,若中文标签发虚,多半是 CFonts 没指定 ANSI 字符集,这属于高风险界面调试点,改错可能让 EA 面板在实盘崩绘。 其余更新清单里还列了若干控件事件常量与绘制函数重载,建议直接把测试用 mq5 拖进策略测试器跑一遍,验证控件在可视化模式下是否响应点击。

◍ 这版控件库到底加了什么

这一节先把本轮函数库更新的范围说清楚:新增了文本编辑框、图片滑块,以及文本标签和图片这类基础控件,老控件也做了改进。 对已经用上这套库的用户来说,最直接的反馈渠道是各章末尾的文章清单链接,完整开发版可以从那里按原目录结构下载,目录层级不能乱,否则 MT5 加载会找不到依赖。 官方提到从用户侧收到了大量评论和建议,其中大部分已经落进新版;另外一些算法做了优化,方向是降低 CPU 负载,具体省了多少会在后面章节展开。外汇与贵金属 EA 界面若跑在低端 VPS 上,这类负载优化值得实测验证。

给函数库补一个能输任意文本的编辑框

之前函数库里的 CSpinEdit 只能塞数值,碰到要输字符串的场景就歇菜。现在补一个文本编辑框控件,字段里能写任意文本,用处很实际:比如在终端沙盒的文件里做字符串搜索,或者让最终用户直接贴一组交易品种名进去跑逻辑。 控件拆开看由四块组成:背景、图标、描述、编辑框本体。布局上图标和描述做提示,中间留编辑区域,和之前数值框的骨架类似但放开了输入类型。 外汇和贵金属这类高波动品种,用文本编辑框批量喂 Symbol 时容易手滑写错格式,建议在回测里先验证字符串解析是否容错。

「自己写一个 MT5 文本输入框类」

想在 MT5 面板里塞一个能输入参数的文本框,得从 CElement 派生一个 CTextEdit 类,把背景色、图标、边距、文本色这些属性全收进私有成员,再暴露一堆 setter 给外部调。标准方法已经挂在WndContainer引擎里,你新建 TextEdit.mqh 后直接 include 就能用。 类里可自定义的属性很多:控件背景色、激活/阻塞两套图标、图标与标签的 x/y 边距、描述文本、各状态文字色、编辑框宽高、编辑框边距、选中高亮色、边框色、重置模式、文本对齐(左/中/右)等。下面这段是类声明的核心骨架,逐行看私有字段就能知道 MT5 控件到底管了哪些视觉状态。

MQL5 / C++
class CTextEdit : class="kw">public CElement
  {
 class="kw">private:
   class="type">color              m_area_color;          class=class="str">"cmt">// 控件背景色
   class="type">class="kw">string             m_icon_file_on;        class=class="str">"cmt">// 激活状态图标路径
   class="type">class="kw">string             m_icon_file_off;       class=class="str">"cmt">// 阻塞状态图标路径
   class="type">int                m_icon_x_gap;          class=class="str">"cmt">// 图标X轴边距
   class="type">int                m_icon_y_gap;          class=class="str">"cmt">// 图标Y轴边距
   class="type">class="kw">string             m_label_text;          class=class="str">"cmt">// 描述文本
   class="type">int                m_label_x_gap;         class=class="str">"cmt">// 标签X边距
   class="type">int                m_label_y_gap;         class=class="str">"cmt">// 标签Y边距
   class="type">color              m_label_color;         class=class="str">"cmt">// 普通标签色
   class="type">color              m_label_color_hover;   class=class="str">"cmt">// 悬停标签色
   class="type">color              m_label_color_locked;  class=class="str">"cmt">// 锁定标签色
   class="type">color              m_label_color_array[]; class=class="str">"cmt">// 标签色数组
   class="type">class="kw">string             m_edit_value;          class=class="str">"cmt">// 编辑框当前值
   class="type">int                m_edit_x_size;         class=class="str">"cmt">// 编辑框宽
   class="type">int                m_edit_y_size;         class=class="str">"cmt">// 编辑框高
   class="type">int                m_edit_x_gap;          class=class="str">"cmt">// 编辑框X边距
   class="type">int                m_edit_y_gap;          class=class="str">"cmt">// 编辑框Y边距
   class="type">color              m_edit_color;          class=class="str">"cmt">// 编辑框背景色
   class="type">color              m_edit_color_locked;   class=class="str">"cmt">// 锁定背景色
   class="type">color              m_edit_text_color;     class=class="str">"cmt">// 文本色
   class="type">color              m_edit_text_color_locked;class=class="str">"cmt">// 锁定文本色
   class="type">color              m_edit_text_color_highlight;class=class="str">"cmt">// 选中高亮文本色
   class="type">color              m_edit_border_color;   class=class="str">"cmt">// 边框色
   class="type">color              m_edit_border_color_hover;class=class="str">"cmt">// 悬停边框色
   class="type">color              m_edit_border_color_locked;class=class="str">"cmt">// 锁定边框色
   class="type">color              m_edit_border_color_array[];class=class="str">"cmt">// 边框色数组
   class="type">bool               m_reset_mode;          class=class="str">"cmt">// 空串重置模式
   class="type">bool               m_show_text_pointer_mode;class=class="str">"cmt">// 文本指针显示开关
   ENUM_ALIGN_MODE    m_align_mode;          class=class="str">"cmt">// 对齐模式
 class="kw">public:
   class="type">void IconXGap(class="kw">const class="type">int x_gap){ m_icon_x_gap=x_gap; }
   class="type">void IconYGap(class="kw">const class="type">int y_gap){ m_icon_y_gap=y_gap; }
   class="type">void AreaColor(class="kw">const class="type">color clr){ m_area_color=clr; }
   class="type">class="kw">string LabelText(class="type">void) class="kw">const { class="kw">return(m_label.Description()); }
   class="type">void LabelText(class="kw">const class="type">class="kw">string text){ m_label.Description(text); }
   class="type">void LabelXGap(class="kw">const class="type">int x_gap){ m_label_x_gap=x_gap; }
   class="type">void LabelYGap(class="kw">const class="type">int y_gap){ m_label_y_gap=y_gap; }
   class="type">void LabelColor(class="kw">const class="type">color clr){ m_label_color=clr; }
   class="type">void LabelColorHover(class="kw">const class="type">color clr){ m_label_color_hover=clr; }
   class="type">void LabelColorLocked(class="kw">const class="type">color clr){ m_label_color_locked=clr; }
   class="type">void EditXSize(class="kw">const class="type">int x_size){ m_edit_x_size=x_size; }
   class="type">void EditYSize(class="kw">const class="type">int y_size){ m_edit_y_size=y_size; }
   class="type">void EditXGap(class="kw">const class="type">int x_gap){ m_edit_x_gap=x_gap; }
   class="type">void EditYGap(class="kw">const class="type">int y_gap){ m_edit_y_gap=y_gap; }
   class="type">void EditColor(class="kw">const class="type">color clr){ m_edit_color=clr; }
   class="type">void EditColorLocked(class="kw">const class="type">color clr){ m_edit_color_locked=clr; }
   class="type">void EditTextColor(class="kw">const class="type">color clr){ m_edit_text_color=clr; }
   class="type">void EditTextColorLocked(class="kw">const class="type">color clr){ m_edit_text_color_locked=clr; }
   class="type">void EditTextColorHighlight(class="kw">const class="type">color clr){ m_edit_text_color_highlight=clr; }
   class="type">void EditBorderColor(class="kw">const class="type">color clr){ m_edit_border_color=clr; }
   class="type">void EditBorderColorHover(class="kw">const class="type">color clr){ m_edit_border_color_hover=clr; }
   class="type">void EditBorderColorLocked(class="kw">const class="type">color clr){ m_edit_border_color_locked=clr; }
   class="type">bool ResetMode(class="type">void){ class="kw">return(m_reset_mode); }
   class="type">void ResetMode(class="kw">const class="type">bool mode){ m_reset_mode=mode; }
   class="type">void ShowTextPointerMode(class="kw">const class="type">bool mode){ m_show_text_pointer_mode=mode; }
   class="type">void AlignMode(ENUM_ALIGN_MODE mode){ m_align_mode=mode; }
   class="type">void IconFileOn(class="kw">const class="type">class="kw">string file_path);
   class="type">void IconFileOff(class="kw">const class="type">class="kw">string file_path);
  };
文本选择指针不是花架子:鼠标悬停编辑框时,系统会补一个 MP_TEXT_SELECT 图标暗示“这儿能打字”。为此 ENUM_MOUSE_POINTER 里加了第 7 号枚举 MP_TEXT_SELECT = 7,CPointer 类也要同步挂上这个状态。
MQL5 / C++
enum ENUM_MOUSE_POINTER
  {
   MP_CUSTOM      = class="num">0,  class=class="str">"cmt">// 自定义
   MP_X_RESIZE    = class="num">1,  class=class="str">"cmt">// 横向缩放
   MP_Y_RESIZE    = class="num">2,  class=class="str">"cmt">// 纵向缩放
   MP_XY1_RESIZE  = class="num">3,  class=class="str">"cmt">// 斜向1缩放
   MP_XY2_RESIZE  = class="num">4,  class=class="str">"cmt">// 斜向2缩放
   MP_X_SCROLL    = class="num">5,  class=class="str">"cmt">// 横滚
   MP_Y_SCROLL    = class="num">6,  class=class="str">"cmt">// 纵滚
   MP_TEXT_SELECT = class="num">7   class=class="str">"cmt">// 文本选择
  };
别把指针枚举当装饰 实际写 EA 面板时,若漏掉 MP_TEXT_SELECT 的图标资源,悬停编辑框可能毫无反馈,用户会以为控件死了。开 MT5 把上面两段塞进 TextEdit.mqh 和指针枚举头,编译过一遍就能确认。外汇贵金属界面开发属高风险辅助环节,控件异常不影响行情本身,但可能让你误输错参数。

MQL5 / C++
class CTextEdit : class="kw">public CElement
  {
 class="kw">private:
   class="type">color              m_area_color;
   class="type">class="kw">string             m_icon_file_on;
   class="type">class="kw">string             m_icon_file_off;
   class="type">int                m_icon_x_gap;
   class="type">int                m_icon_y_gap;
   class="type">class="kw">string             m_label_text;
   class="type">int                m_label_x_gap;
   class="type">int                m_label_y_gap;
   class="type">color              m_label_color;
   class="type">color              m_label_color_hover;
   class="type">color              m_label_color_locked;
   class="type">color              m_label_color_array[];
   class="type">class="kw">string             m_edit_value;
   class="type">int                m_edit_x_size;
   class="type">int                m_edit_y_size;
   class="type">int                m_edit_x_gap;
   class="type">int                m_edit_y_gap;
   class="type">color              m_edit_color;
   class="type">color              m_edit_color_locked;
   class="type">color              m_edit_text_color;
   class="type">color              m_edit_text_color_locked;
   class="type">color              m_edit_text_color_highlight;
   class="type">color              m_edit_border_color;
   class="type">color              m_edit_border_color_hover;
   class="type">color              m_edit_border_color_locked;
   class="type">color              m_edit_border_color_array[];
   class="type">bool               m_reset_mode;
   class="type">bool               m_show_text_pointer_mode;
   ENUM_ALIGN_MODE    m_align_mode;
 class="kw">public:
   class="type">void IconXGap(class="kw">const class="type">int x_gap){ m_icon_x_gap=x_gap; }
   class="type">void IconYGap(class="kw">const class="type">int y_gap){ m_icon_y_gap=y_gap; }
   class="type">void AreaColor(class="kw">const class="type">color clr){ m_area_color=clr; }
   class="type">class="kw">string LabelText(class="type">void) class="kw">const { class="kw">return(m_label.Description()); }
   class="type">void LabelText(class="kw">const class="type">class="kw">string text){ m_label.Description(text); }
   class="type">void LabelXGap(class="kw">const class="type">int x_gap){ m_label_x_gap=x_gap; }
   class="type">void LabelYGap(class="kw">const class="type">int y_gap){ m_label_y_gap=y_gap; }
   class="type">void LabelColor(class="kw">const class="type">color clr){ m_label_color=clr; }
   class="type">void LabelColorHover(class="kw">const class="type">color clr){ m_label_color_hover=clr; }
   class="type">void LabelColorLocked(class="kw">const class="type">color clr){ m_label_color_locked=clr; }
   class="type">void EditXSize(class="kw">const class="type">int x_size){ m_edit_x_size=x_size; }
   class="type">void EditYSize(class="kw">const class="type">int y_size){ m_edit_y_size=y_size; }
   class="type">void EditXGap(class="kw">const class="type">int x_gap){ m_edit_x_gap=x_gap; }
   class="type">void EditYGap(class="kw">const class="type">int y_gap){ m_edit_y_gap=y_gap; }
   class="type">void EditColor(class="kw">const class="type">color clr){ m_edit_color=clr; }
   class="type">void EditColorLocked(class="kw">const class="type">color clr){ m_edit_color_locked=clr; }
   class="type">void EditTextColor(class="kw">const class="type">color clr){ m_edit_text_color=clr; }
   class="type">void EditTextColorLocked(class="kw">const class="type">color clr){ m_edit_text_color_locked=clr; }
   class="type">void EditTextColorHighlight(class="kw">const class="type">color clr){ m_edit_text_color_highlight=clr; }
   class="type">void EditBorderColor(class="kw">const class="type">color clr){ m_edit_border_color=clr; }
   class="type">void EditBorderColorHover(class="kw">const class="type">color clr){ m_edit_border_color_hover=clr; }
   class="type">void EditBorderColorLocked(class="kw">const class="type">color clr){ m_edit_border_color_locked=clr; }
   class="type">bool ResetMode(class="type">void){ class="kw">return(m_reset_mode); }
   class="type">void ResetMode(class="kw">const class="type">bool mode){ m_reset_mode=mode; }
   class="type">void ShowTextPointerMode(class="kw">const class="type">bool mode){ m_show_text_pointer_mode=mode; }
   class="type">void AlignMode(ENUM_ALIGN_MODE mode){ m_align_mode=mode; }
   class="type">void IconFileOn(class="kw">const class="type">class="kw">string file_path);
   class="type">void IconFileOff(class="kw">const class="type">class="kw">string file_path);
  };

enum ENUM_MOUSE_POINTER
  {
   MP_CUSTOM      = class="num">0,
   MP_X_RESIZE    = class="num">1,
   MP_Y_RESIZE    = class="num">2,
   MP_XY1_RESIZE  = class="num">3,
   MP_XY2_RESIZE  = class="num">4,
   MP_X_SCROLL    = class="num">5,
   MP_Y_SCROLL    = class="num">6,
   MP_TEXT_SELECT = class="num">7
  };

◍ 文本编辑框的光标与字符上限

上面这段 CPointer::SetPointerBmp 按光标类型给控件换图标:横向缩放、纵向缩放、斜向缩放、滚动、文本选择各自对应一组 on/off 位图,文本选择态下 on 和 off 共用同一张 pointer_text_select.bmp。若 m_type 被设为 MP_CUSTOM 却没填 m_file_on 或 m_file_off,会直接 Print 提示必须给光标设两个图标——在 MT5 里跑自定义控件时,这个报错能帮你快速定位资源漏配。 文本编辑框 CTextEdit 继承自 CElement,私有成员里塞了五个对象:区域底框 m_area、图标 m_icon、静态标签 m_label、输入行 m_edit、以及文本选择光标 m_text_select;对外只暴露一个 CreateTextEdit 负责在指定 chart_id、子窗口和坐标处拼装控件,其余五个 Create* 方法都在内部跑。 类里没讲的部分和前面几篇同构,可以自己改参数试。当前这版文本编辑框硬上限是 63 个字符,做贵金属报价备注或短指令输入够用,想存长文本得改底层缓冲逻辑。外汇与贵金属杠杆交易高风险,这类 GUI 增强只解决交互效率,不暗示任何胜率改善。

MQL5 / C++
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//|                                                     Pointer.mqh |
class=class="str">"cmt">//|                        版权所有 class="num">2015, MetaQuotes 软件公司|
class=class="str">"cmt">//|                                              [MQL5官方文档] |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="macro">#include "Element.mqh"
class=class="str">"cmt">//--- 资源
...
class="macro">#resource "\\Images\\EasyAndFastGUI\\Controls\\pointer_text_select.bmp"
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| 基于光标类型设置光标图标                                          |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void CPointer::SetPointerBmp(class="type">void)
  {
   class="kw">switch(m_type)
     {
      case MP_X_RESIZE:
        m_file_on  = "Images\\EasyAndFastGUI\\Controls\\pointer_x_rs_blue.bmp";
        m_file_off = "Images\\EasyAndFastGUI\\Controls\\pointer_x_rs.bmp";
        class="kw">break;
      case MP_Y_RESIZE:
        m_file_on  = "Images\\EasyAndFastGUI\\Controls\\pointer_y_rs_blue.bmp";
        m_file_off = "Images\\EasyAndFastGUI\\Controls\\pointer_y_rs.bmp";
        class="kw">break;
      case MP_XY1_RESIZE:
        m_file_on  = "Images\\EasyAndFastGUI\\Controls\\pointer_xy1_rs_blue.bmp";
        m_file_off = "Images\\EasyAndFastGUI\\Controls\\pointer_xy1_rs.bmp";
        class="kw">break;
      case MP_XY2_RESIZE:
        m_file_on  = "Images\\EasyAndFastGUI\\Controls\\pointer_xy2_rs_blue.bmp";
        m_file_off = "Images\\EasyAndFastGUI\\Controls\\pointer_xy2_rs.bmp";
        class="kw">break;
      case MP_X_SCROLL:
        m_file_on  = "Images\\EasyAndFastGUI\\Controls\\pointer_x_scroll_blue.bmp";
        m_file_off = "Images\\EasyAndFastGUI\\Controls\\pointer_x_scroll.bmp";
        class="kw">break;
      case MP_Y_SCROLL:
        m_file_on  = "Images\\EasyAndFastGUI\\Controls\\pointer_y_scroll_blue.bmp";
        m_file_off = "Images\\EasyAndFastGUI\\Controls\\pointer_y_scroll.bmp";
        class="kw">break;
      case MP_TEXT_SELECT:
        m_file_on  = "Images\\EasyAndFastGUI\\Controls\\pointer_text_select.bmp";
        m_file_off = "Images\\EasyAndFastGUI\\Controls\\pointer_text_select.bmp";
        class="kw">break;
     }
class=class="str">"cmt">//--- 如果已指定光标类型 (MP_CUSTOM)
   if(m_file_on=="" || m_file_off=="")
      ::Print(__FUNCTION__," > 必须为光标设置两个图标!");
  }

class CTextEdit : class="kw">public CElement
  {
class="kw">private:
   class=class="str">"cmt">//--- 创建编辑框的对象
   CRectLabel        m_area;
   CBmpLabel         m_icon;
   CLabel            m_label;
   CEdit             m_edit;
   CPointer          m_text_select;
   class=class="str">"cmt">//---
class="kw">public:
   class=class="str">"cmt">//--- 创建文本编辑框的方法
   class="type">bool              CreateTextEdit(class="kw">const class="type">long chart_id,class="kw">const class="type">int subwin,class="kw">const class="type">class="kw">string label_text,class="kw">const class="type">int x,class="kw">const class="type">int y);
   class=class="str">"cmt">//--- class="kw">private:
   class="type">bool              CreateArea(class="type">void);
   class="type">bool              CreateIcon(class="type">void);
   class="type">bool              CreateLabel(class="type">void);
   class="type">bool              CreateEdit(class="type">void);
   class="type">bool              CreateTextSelectPointer(class="type">void);
  };

常见问题

用标准控件类封装一个 CEdit 派生类,挂到图表子窗口即可。参考基础篇里给函数库补的文本编辑框示例,复制类代码就能用。
字体没设对。在控件初始化时指定支持中文的字体(如 "Microsoft YaHei"),并在绘制前调用字体绑定,方块就会消失。
可以。小布盯盘的 AIGC 能按你给的字段自动产出控件代码并预设中文字体,你直接贴进 EA 就能跑。
在按键处理函数里加字符数判断和 ASCII 范围过滤,超长或非法直接 return 不写入缓冲区。
用定时器切换光标显隐标志位,每次重绘根据标志画竖线或不画,间隔设 500ms 最接近系统手感。