自定义图形控件表单:从零搭出可拖拽可管理的 MT5 界面容器(基础篇)
🪟

自定义图形控件表单:从零搭出可拖拽可管理的 MT5 界面容器(基础篇)

(1/2)· 还在用零散图形对象拼界面?表单让你把按钮标签收进一个能整体移动隐藏的矩形里

新手友好 第 1/2 篇
很多交易者写 EA 面板时,把按钮和标签直接丢在图表上,移动一个就要改一堆坐标。一旦控件多了,隐藏和重排就成了手工苦力。表单正是用来终结这种混乱的基础容器。

在 MT5 里搭一个自定义表单控件

MetaTrader 5 自带的图形对象不足以覆盖复杂交互需求,自己写控件才有掌控感。2013 年 11 月 6 日发布的表单示例帖累计浏览 3102 次、评论 26 条,说明这类底层控件在 MQ5 社区有稳定需求。 表单(Form)本质是一个可拖拽的容器,内部承载按钮、编辑框、标签等子控件。核心思路是用 CObject 派生类管理坐标与消息路由,在 OnChartEvent 里把鼠标和键盘事件分发给当前焦点控件。 下面这段是表单类最小骨架,先跑通再扩展。注意所有子对象坐标都相对表单左上角,避免直接在绝对坐标上画。 外汇与贵金属图表上叠加自绘表单会占用主图资源,高频刷新可能引发卡顿,实盘前务必在模拟盘验证性能。

MQL5 / C++
class CForm : class="kw">public CObject
{
class="kw">protected:
   class="type">int            m_x;          class=class="str">"cmt">// 表单左上角X
   class="type">int            m_y;          class=class="str">"cmt">// 表单左上角Y
   class="type">int            m_w;          class=class="str">"cmt">// 宽度
   class="type">int            m_h;          class=class="str">"cmt">// 高度
   CArrayObj      m_controls;   class=class="str">"cmt">// 子控件数组
class="kw">public:
   CForm(class="type">int x,class="type">int y,class="type">int w,class="type">int h):m_x(x),m_y(y),m_w(w),m_h(h){}
   class="type">void Add(CObject* c){ m_controls.Add(c); }
   class="kw">virtual class="type">void OnEvent(const class="type">int id,const class="type">long& l,const class="type">class="kw">double& d,const class="type">class="kw">string& s)
   {
      for(class="type">int i=class="num">0;i<m_controls.Total();i++)
         m_controls.At(i).OnEvent(id,l,d,s);
   }
};

◍ 表单与控件容器的底层改动

在 MT5 自定义图形界面里,表单(Frame)不是装饰,而是一块被明确圈定的矩形区域,控件只在它内部绘制与排布。更关键的是,表单充当容器:隐藏、显示、整体平移都能一次作用于内部所有子控件,省去逐个遍历的开销。 本篇对应的类库已并入 IncGUI_v3.mqh(前身是 IncGUI.mqh)。新增 CFrame 等价于早先 CWorkPiece::Frame() 产出的框架;CButton 封装常规 OBJ_BUTTON;CLabel 在 OBJ_LABEL 基础上支持用 "\n" 做多行文本。CColorSchemes 随之补进了新控件的配色槽位。 CHMenu / CVMenu 的改动指向两级下拉菜单能力,后文“创建主菜单”会展开。修掉的两个硬伤值得注意:CWorkPiece::Frame() 原先无法给矩形标签设子窗口编号;CListMS 的 Selected()/SetSelected() 此前不校验数组尺寸,空列表调用会越界。 外汇与贵金属图表加载这类 EA 界面属高风险操作,建议先在策略测试器离线跑通再上真实账户。

「用矩形标签和按钮搭出可拖拽表单」

MT5 里做自定义面板,地基是图形对象 OBJ_RECTANGLE_LABEL 当容器,再往里塞 OBJ_BUTTON。视觉上就是个带顶栏的矩形:左边抓手图标管拖动,右边最小化方块和叉号管收起或关闭。拖的时候按住移动钮,图表上点哪左上角就落哪;若点在图表边缘外,系统会自动回拉保证整块面板可见。 表单分三种型:0 型只有基础三钮;1 型多 Cancel / Apply;2 型多 Close。代码层面靠 CFormBase 基类与 CFormTemplate 子类分工,前者管显示隐藏刷新事件,后者当模板被复制重命名后承载具体控件逻辑。 同步是个坑。标准控件各自独立,表单却强制绑定内部控件——你显隐表单时,里面按钮也得跟着显隐。若手动逐个调 Show()/Hide(),多表单项目会乱成一团。正确做法是在 CFormTemplate 副本里重写受保护区的虚方法,基类按事件自动回调,你只填控件操作。 OnCancelEvent 返回 false 能拦住关闭,适合弹确认窗;OnApplyEvent 里存控件值,下次 OnInitEvent 读回,EA 重启也能复原界面状态。外汇和贵金属图表挂这类面板,遇极端波动可能事件堆积,回测前先在模拟盘验证交互。

MQL5 / C++
class="kw">virtual class="type">void MainProperties() {}
class="kw">virtual class="type">void OnInitEvent() {}
class="kw">virtual class="type">void OnShowEvent() {}
class="kw">virtual class="type">void OnHideEvent() {}
class="kw">virtual class="type">void EventsHandler(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="kw">virtual class="type">void OnWindowChangeEvent(class="type">int aSubWindow) {}
class="kw">virtual class="type">bool OnCancelEvent() {class="kw">return(true);}
class="kw">virtual class="type">bool OnApplyEvent()  {class="kw">return(true);}

从零拉起一个 GUI 表单子类

在 MT5 里用 IncGUI_v3 体系搭界面,第一步是把 IncGUI_v3.mqh 包含进来,再把库里的 CFormTemplate 类整段抄到 EA 文件末尾或独立 include 里,重命名成你自己的子类(例里叫 CForm)。这动作等价于可视编辑器里点「新建表单」——项目里立刻多出一个带事件骨架的表单文件。 表单名不在 Init() 里传,而是在 MainProperties() 里给 m_Name 赋值。子类只声明一次,所以调 Init() 时不用反复改名字;唯一能塞进 Init() 的是 State 参数,非强制:传 1 表单初始最大化,传 2 最小化,不传就走默认。 控件接入走标准四连:OnInit() 调 Init()、OnDeinit() 调 Deinit()、OnChartEvent() 调 Event(),遇到 CHARTEVENT_CHART_CHANGE 再补一句 SetSubWindow()。跑起来后表单能拖、能最小、能关,之后再往里堆按钮文本框都行。 下面这段是模板子类的最小可编译形态,直接复制到 EA 里就能在图表上看到 200×150、带「应用/取消」按钮的窗体。外汇贵金属界面自动化本质是高杠杆环境下的辅助工具,实盘前务必在模拟盘验证事件响应。

MQL5 / C++
class="macro">#include <IncGUI_v3.mqh>
class CFormTemplate: class="kw">public CFormBase{
   class="kw">public:
   class="kw">protected: 
      class="type">void MainProperties()
     {
        m_Name        = "Form";       class=class="str">"cmt">// 表单名. 表单的所有控件名必须有这个前缀.
        m_Width       = class="num">200;           class=class="str">"cmt">// 表单宽度
        m_Height      = class="num">150;           class=class="str">"cmt">// 表单高度
        m_Type        = class="num">1;             class=class="str">"cmt">// 表单类型: class="num">0 - 无按钮, class="num">1 - 带 "应用" 和 "取消" 按钮, class="num">2 - 带 "关闭" 按钮
        m_Caption     = "FormCaption"; class=class="str">"cmt">// 表单标题
        m_Movable     = true;          class=class="str">"cmt">// 可移动表单 (带手势图像按钮,显示在左上角)
        m_Resizable   =  true;         class=class="str">"cmt">// 表单最小/最大允许 (带正方形图像按钮 
                                           class=class="str">"cmt">// 显示在右上角)
        m_CloseButton = true;          class=class="str">"cmt">// 表单关闭允许 (带交叉图像按钮显示在右上角)
     }
      class="type">void OnInitEvent() {} 
      class="type">void OnShowEvent(class="type">int aLeft, class="type">int aTop) {}
      class="type">void OnHideEvent() {}
      class="type">void OnWindowChangeEvent(class="type">int aSubWindow) {}
      class="type">void EventsHandler(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="type">bool OnApplyEvent()  {class="kw">return(true);}
      class="type">bool OnCancelEvent() {class="kw">return(true);}
};
交给小布盯盘看盘口
这些诊断小布盯盘的 AIGC 已内置,打开对应品种页即可看到控件层级与表单状态的自动梳理,你只管调策略逻辑。

常见问题

CFrame 是独立类,功能和 CWorkPiece 的 Frame 方法创建的框架一致,但可作为独立对象管理,更方便在表单中嵌套使用。
若指定位置让表单超出图表边界,系统会自动修正坐标,保证表单完整显示在图表内,不会丢控件。
目前小布盯盘侧重行情诊断与 AIGC 看盘,不直接产出 MQL5 表单代码,但可帮你核对控件布局逻辑是否合理。
CLabel 支持用 \n 分行显示文本,普通标签只能单行,多信息面板用它会省很多对象。