用 CChartObject 扩展 MQL5 图形控件:进度条、微调器、表格
为什么要从裸 ObjectCreate 迁移到 CChartObject
在上一篇用 GUI 做半自动 EA 之后,作者发现更复杂的指标和 EA 还需要进度条、数字微调框、表格这类组件。如果不碰标准库,这些只能靠 ObjectCreate 加一堆 ObjectSet* 字符串属性硬搭,繁琐且极易拼错。MQL5 的 OOP 让我们可以“继承 CChartObjectEdit 写自己的控件”:声明实例、调方法改属性,编辑器还能补全,代码既好记又快。
三个组件,都派生自 CChartObjectEdit
本文实施了三个派生类,全部继承自 CChartObjectEdit:CChartObjectProgressBar(进度条)、CChartObjectSpinner(带 +/− 的数字框)、CChartObjectEditTable(二维表格)。它们共用同一套路——在 Create 里先调基类建好 OBJ_EDIT 对象,再叠加自己的子对象与逻辑。理解一个,其余两个都是它的变体。
CChartObjectProgressBar:进度条
ProgressBar 的 Create(long chart_id, string name, int window, int X, int Y, int sizeX, int sizeY, int direction=0) 多了一个 direction 参数:当 direction!=0 表示竖向,构造函数会把 Y 改成 Y−sizeY,让条从下往上长。内部条 m_bar 是另一个 CChartObjectEdit,设成白底、不可选、只读。SetValue(int val) 时,横向按 stepSize = sizeX/(max−min) 重算 m_bar 的宽度,竖向则重算高度并调整 Y 锚点。Type() 固定返回 OBJ_EDIT,这样它仍是一个编辑框对象,只是被当成进度条用。
CChartObjectSpinner:带加减按钮的数字框
Spinner 在编辑框右侧叠两个 CChartObjectButton:上半高 m_up(文字“+”)、下半高 m_down(文字“−”)。Create 里还接了初值 val、步长 stepSize 和精度 precision(默认 8 位)。Inc()/Dec() 用 NormalizeDouble 按精度比较,越界就停在 [min,max],然后改 Description 显示新值。精度参数很关键——不直接用浮点比较,避免 0.1+0.1+0.1≠0.3 之类的显示抖动。
CChartObjectEditTable:二维动态对象数组表格
表格是最复杂的一个:用 CArrayObj 做“列数组里再套行编辑框”的二维动态结构,每个单元格命名为 name+i+":"+j 便于定位。Create(long chart_id, string name, int window, int rows, int cols, int startX, int startY, int sizeX, int sizeY, color Bg, int deltaX, int deltaY) 按行列和间距铺满单元格。SetText/GetText(row,col) 通过 array2D.At(col).At(row) 找到对应编辑框再读写。Delete() 要递归清掉列数组并释放内存,否则留下一堆孤立对象。
用 OnChartEvent 让 Spinner 真正动起来
让 Spinner 能响应点击靠 OnChartEvent:当 id==CHARTEVENT_OBJECT_CLICK 时,用 sparam 比对按钮名(如 spinner1_up / spinner1_down),命中就调对应 spinner 的 Inc()/Dec(),再把最新值写进某个标签的 Description 并 ChartRedraw()。这是标准库对象事件最典型的用法,也是把“静态控件”变成“可交互控件”的桥梁。
接入与几个坑
- 这些组件不是凭空能用——EA 或指标里必须 #include <ChartObjectsExtControls.mqh>
- 继承自编辑框的控件记得设 ReadOnly(true) 和 Selectable(false),否则用户能乱改或选中背景
- 坐标一律用 X_Distance/Y_Distance 的像素距离定位,而不是价格/时间锚点,这样控件不会随行情滚动
- 进度条/表格内部子对象多,销毁时必须统一清理,否则重复创建会越积越多