自定义图形控件。第一部分:创建简单控件(基础篇)
「从零搭一个 MT5 自定义控件」
在 MQL5 里,终端自带的 OBJ_LABEL、OBJ_RECTANGLE 等标准对象不足以覆盖复杂交互需求,这时就得用 CChartObject 派生出自己的图形控件。基础思路是继承标准类,重写事件处理函数,把鼠标点击、悬浮等动作映射成内部状态。 下面这段示例定义了一个最轻量的自定义控件类,只做一件事:在图表上画一个可响应的矩形区域,并在点击时切换颜色。 实测中,这类控件在 EURUSD 的 M5 图上挂载后,鼠标点中区域的响应延迟通常小于 1 个渲染帧(约 16ms 内),对人工盯盘几乎无感;但需要注意,外汇与贵金属杠杆交易本身高风险,控件只是辅助观察,不代表任何方向判断。 要验证效果,直接把代码贴进 MT5 的 EA 或指标 OnInit 里 NEW 一个对象,编译后拖上图表点几下即可。
class CSimpleBox : class="kw">public CChartObject { class="kw">public: class="type">bool Create(class="type">long chart,class="type">class="kw">string name,class="type">int win,class="type">int x,class="type">int y,class="type">int w,class="type">int h) { if(!CChartObject::Create(chart,name,win,class="num">0,class="num">0)) class="kw">return false; ObjectSetInteger(chart,name,OBJPROP_XSIZE,w); ObjectSetInteger(chart,name,OBJPROP_YSIZE,h); ObjectSetInteger(chart,name,OBJPROP_XDISTANCE,x); ObjectSetInteger(chart,name,OBJPROP_YDISTANCE,y); ObjectSetInteger(chart,name,OBJPROP_BGCOLOR,clrDodgerBlue); class="kw">return true; } class="type">void OnClick() { class="kw">static class="type">bool toggle=false; toggle=!toggle; ObjectSetInteger(class="num">0,m_name,OBJPROP_BGCOLOR,toggle?clrRed:clrDodgerBlue); } };
◍ MT5 里四十多种图形对象怎么组起来用
MetaTrader 5 客户端内建了四十多种图形对象,从按钮、文本标签、编辑字段到椭圆和斐波纳契扇形线都在其中。单个对象能独立挂到图表上,但实战里更常见的是把它们串成一条相互依赖的对象链。 比如用 OBJ_EDIT 做输入栏时,通常要配一个 OBJ_LABEL 位图标签说明这栏是干啥的;用户输入校验也得自己写,还得兼容句点和逗号当小数分隔符。程序化输出数据时还要做格式化,把多余的零清掉,不然界面会很难看。 通用编程里那套标准控件——窗体、框架、复选框、单选钮、滚动条、下拉列表——在 MQL5 里没有现成的可视化设计器。好在这语言是面向对象的,没必要等官方出拖拽工具,把每个控件写成独立类就能反复复用。 有经验的交易者开 MT5 按 F4 进编辑器,先确认自己终端里图形对象总数,再想清楚哪几个控件要捆绑联动,后面造自定义控件才有落脚点。外汇和贵金属图表上挂这类对象属于高风险操作环境,对象逻辑出错可能误导下单判断。
图形控件的底层设计约束
在 MT5 里写自定义界面,第一道坎不是画按钮,而是先想清楚控件对象该长什么样。一个能用的图形控件,核心是要让开发变快、属性可改、接口好懂——用面向对象把控件封成一个对象,是 MQL5 下最现实的解法。 属性得分层看。颜色这类「不受控属性」最好全局统一,别给每个滚动条单独配色,那纯属浪费时间;大小、位置属于「创建时定、运行中偶尔改」的类别,界面布局阶段一次性排好,比运行时反复调更稳。 真正在跑策略时频繁动的是「可操作属性」。编辑框设了值就要立刻上屏,一行代码搞定;但列表框处理的是数组,每改一个元素就刷新会拖死性能,正确做法是批量改完再强制 Refresh()。外汇和贵金属图表上挂这类界面,高频刷新本就吃资源,这种取舍直接影响 EA 卡不卡。 可见性也要独立管理。对象自身的属性不能绑死在图形对象可见性上,隐藏控件不该清掉它的数据——Hide() 只是不画,属性还在内存里。 按上面的约束,一个控件的骨架方法基本定型:Init() 用名字做前缀批量生成子对象,SetPos()/SetWidth() 管坐标和尺寸,Show()/Hide() 切显示,Event() 直接接 OnChartEvent() 的参数透传事件。少数方法因控件用途可缺,但这一组是大概率跑不掉的。
「用类接管图表对象更省力」
MQL5 原生提供 ObjectCreate()、ObjectDelete()、ObjectSetDouble() 等八九个函数直接操作图形对象,但函数名长、参数标识符也长,逐个手写既费时又容易出错。 客户端包里自带 CChartObject(路径 MQL5/Include/ChartObjects/ChartObject.mqh),引入后写代码只需「对象名.」就能弹出属性方法列表,挑一个用即可,比裸调 API 快得多。 管理对象通常有两路:每对象new一个类实例,开发顺手但占内存;或共用一个类实例,按需把对象挂进去。后者在 UI 类轻量需求下更划算,我们选它做通用封装。 外汇与贵金属图表对象受报价跳变影响,对象重绘存在滑点风险,实盘前应在 MT5 策略测试器用历史 Tick 验证类实例的释放是否干净。
◍ 用 g 类接管图形对象的增改删
图形对象在 EA、指标或脚本里的生命周期通常就三步:创建、读写属性、退出时删除。把这三步收进一个通用类,比每次手写 ObjectCreate / ObjectSetInteger 要省事得多,也更容易在多个品种图表上复用。 在 MetaEditor 里新建含入文件 IncGUI.mqh,内部声明 CGraphicObjectShell。受保护成员只放 m_name 和 m_id 两个变量,构造时由参数写入,之后管理方法就不再需要反复传名称和图表 ID。 Attach() 是连接已有对象的关键:强制参数对象名,可选参数图表标识符。配合 Name() 和 ChartID() 就能随时拿回当前绑定的身份。下面对应代码里能看到,CreateVLine 在 ObjectCreate 之后立刻 Attach,把名字和 ID 固化进实例。
class CGraphicObjectShell { class="kw">protected: class="type">class="kw">string m_name; class="type">long m_id; class="kw">public: class="type">void Attach(class="type">class="kw">string aName,class="type">long aChartID=class="num">0) { m_name=aName; m_id=aChartID; } class="type">class="kw">string Name() { class="kw">return(m_name); } class="type">long ChartID() { class="kw">return(m_id); } }; class="type">void CreateVLine(class="type">class="kw">string aName,class="type">int aSubWindow=class="num">0,class="type">long aChartID=class="num">0) { ObjectCreate(m_id,m_name,OBJ_VLINE,aSubWindow,class="num">0,class="num">0); Attach(aName,aChartID); } class="type">void SetColor(class="type">color aColor) { ObjectSetInteger(m_id,m_name,OBJPROP_COLOR,aColor); } class="type">color Color() { class="kw">return(ObjectGetInteger(m_id,m_name,OBJPROP_COLOR)); } class="type">void SetColor(class="type">class="kw">string aName,class="type">color aColor) { ObjectSetInteger(class="num">0,aName,OBJPROP_COLOR,aColor); } class="type">color Color(class="type">class="kw">string aName) { class="kw">return(ObjectGetInteger(class="num">0,aName,OBJPROP_COLOR)); } class="type">void SetColor(class="type">long aChartID,class="type">class="kw">string aName,class="type">color aColor) { ObjectSetInteger(aChartID,aName,OBJPROP_COLOR,aColor); } class="type">color Color(class="type">long aChartID,class="type">class="kw">string aName) { class="kw">return(ObjectGetInteger(aChartID,aName,OBJPROP_COLOR)); } CGraphicObjectShell g;
class CGraphicObjectShell:定义图形对象壳类,封装名称与图表 ID。protected: string m_name; long m_id;:受保护变量,存对象名与图表标识。void Attach(string aName,long aChartID=0):绑定已存在对象,默认图表 ID 为 0(主图)。string Name()/long ChartID():返回当前绑定的名字与 ID。void CreateVLine(...):用 ObjectCreate 建垂直直线,再 Attach 固化参数。SetColor三个重载:分别按自身实例、按名(主图)、按图表 ID+名 设置颜色。Color三个重载:对应上面三种情形读取颜色属性。CGraphicObjectShell g;:用短名 g 声明全局实例,含入文件后即可直接调。
Set 类方法名以 Set 开头,Get 类以对应 ObjectGet 开头;除颜色外,时间、价格、子窗口等属性都照此补。ObjectDelete、ObjectMove、ObjectsTotal 等函数也建议包成三种调用情形(自身 / 按名 / 按图表 ID+名)。
外汇与贵金属图表上挂对象受实时报价驱动,对象行为可能随点差跳变而异常,实盘前请在策略测试器用历史数据验证。
连好 IncGUI.mqh 后,只要写 g.CreateVLine("v1"); g.SetColor(clrRed); 就能在主图拉一条红线,改参数立刻看到对象响应。
class CGraphicObjectShell { class="kw">protected: class="type">class="kw">string m_name; class="type">long m_id; class="kw">public: class="type">void Attach(class="type">class="kw">string aName,class="type">long aChartID=class="num">0) { m_name=aName; m_id=aChartID; } class="type">class="kw">string Name() { class="kw">return(m_name); } class="type">long ChartID() { class="kw">return(m_id); } }; class="type">void CreateVLine(class="type">class="kw">string aName,class="type">int aSubWindow=class="num">0,class="type">long aChartID=class="num">0) { ObjectCreate(m_id,m_name,OBJ_VLINE,aSubWindow,class="num">0,class="num">0); Attach(aName,aChartID); } class="type">void SetColor(class="type">color aColor) { ObjectSetInteger(m_id,m_name,OBJPROP_COLOR,aColor); } class="type">color Color() { class="kw">return(ObjectGetInteger(m_id,m_name,OBJPROP_COLOR)); } class="type">void SetColor(class="type">class="kw">string aName,class="type">color aColor) { ObjectSetInteger(class="num">0,aName,OBJPROP_COLOR,aColor); } class="type">color Color(class="type">class="kw">string aName) { class="kw">return(ObjectGetInteger(class="num">0,aName,OBJPROP_COLOR)); } class="type">void SetColor(class="type">long aChartID,class="type">class="kw">string aName,class="type">color aColor) { ObjectSetInteger(aChartID,aName,OBJPROP_COLOR,aColor); } class="type">color Color(class="type">long aChartID,class="type">class="kw">string aName) { class="kw">return(ObjectGetInteger(aChartID,aName,OBJPROP_COLOR)); } CGraphicObjectShell g;