MQL5 表格控件基础:三类二维数据展示类的骨架与属性(基础篇)
📊

MQL5 表格控件基础:三类二维数据展示类的骨架与属性(基础篇)

(1/3)·从文本标签到绘制型表格,理清控件组成与类结构再写界面少走弯路

案例拆解 第 1/3 篇

在 MQL5 自定义面板里塞二维数据,很多人直接堆 Label 和 Edit 控件,滚动一多坐标就乱。表格控件的滚动条、锁定表头、可见区域计算若不在类里封装好,后期改一列宽度能牵动半个界面。本文先把三种表格类型的底层组成和类属性讲透,再谈扩展。

◍ 给 MT5 面板加一张可滚动的表格

在 MQL5 里做自定义图形界面,表格控件是最容易被低估的一块。它不像按钮或标签那样直来直去,但一旦跑起来,能把多品种报价、持仓明细或信号日志直接铺在图表上,省掉切窗口的麻烦。 官方示例工程发布于 2016 年 9 月 14 日,至今在客户端标准库里仍可找到对应实现,原贴浏览量约 3694 次、收藏 22 次——说明这套接口虽老,却仍是多数人抄作业的第一站。 表格本质是一个带行/列容器的 CTable 类封装,内部用 CEdit 或 CLable 做单元格。真正麻烦的是滚动条联动:当行数超过可视高度,必须自己处理 OnScroll 事件并重绘可视区,标准库给的 demo 已经把这套逻辑写死了,直接继承改字段即可。

控件选型前的目录地图

做 MT5 面板时,表格类控件至少有三条实现路径:文本标签堆砌、可编辑框承载、以及直接在 CCanvas 上自绘。原文给出的实战顺序是先吃透 CLabelsTable,再过渡到 CTable,最后用 CCanvasTable 解决高频重绘场景。 这 6 节原文里,表格相关开发占 4 节(两类封装 + 两类测试),说明多数坑不在画出来,而在交互与刷新。你开 MT5 新建 EA 面板前,先想清楚表格要不要用户改值——要改就别用纯标签方案,后续重构成本偏高。 目录里「结论」单独成节,意味着三种方案会有明确取舍基准,而不是堆代码。建议边读边在 MetaEditor 建三个空类骨架,对照着填,比纯看顺一遍留存率高得多。

「三类表格控件先铺开」

这一篇在界面库里落地了三个类,专门用来在 MQL 应用的图形界面里呈现二维数据。分别是文本标签型、编辑框型、绘制型表格,每种都有各自的适用场景和取舍。 文本标签型最轻量,只读展示优先;编辑框型允许终端用户直接改单元格内容,适合参数录入;绘制型则用图形对象画格子,灵活度最高但开销也最大。 原文把这三个类作为本章核心交付,档案里的文件需按原有目录结构原样放置,否则 MT5 加载时会找不到依赖。下一章会接着讲页面和带图形的页面控件,本篇先不展开。

◍ 文本标签型表格的内部构件

在 MT5 自定义指标或 EA 面板里,表格是最容易踩坑的 GUI 控件——它本身不是单一对象,而是把背景、文本标签和两套滚动条打包在一起的复合体。 当行数或列宽超过你给定的显示区域,垂直与水平滚动条就会接管视图偏移;若忽略这一点,数据写出来却看不到是常态。 该控件至少由四部分构成:底层背景、承载文字的文本标签、垂直滚动条、水平滚动条。理清这四块归属,后续写 CTable 派生类时才不会把坐标算错。

从零搭一个标签表格控件

在 MT5 自定义图形界面里,原生控件没有现成的「多行多列文字表」。要自己造一个 CLabelsTable,先建 LabelsTable.mqh,并在 WndContainer.mqh 里 include 它,把类挂进控件库。 表格在创建前要先定属性:行高、首列左缩进、列间距、背景色、文字色、首行/首列锁定模式、总行列数、可见行列数。文本标签锚点用 ANCHOR_CENTER,所以缩进和间距都按每列标签的中心点算,不是左缘对齐。 锁定模式值得单独说:首行(列名)和首列(序列名)可设成滚动时永远钉在顶部/左边。表头锁定能单独用也能组合用,实现时数据数组从索引 2 开始偏移即可保住表头。 TableSize() 和 VisibleTableSize() 两个方法传列、行两参数,内部会强制修正——列数小于 1 或行数小于 2 直接拦掉,避免数组越界。另用 LTLabels 结构存可见标签、LTOptions 存单元值与颜色。 构造函数里把所有属性设默认值,外部只暴露四个 private 加一个 public 方法,再补返回滚动条指针的接口方便用户配滚动。CreateLabels() 建标签数组时,对象名必须拼上列行索引,否则多单元会重名冲突。 改值用 SetValue(列,行,值)、读值用 GetValue(列,行),开头都做范围检查;想让正绿负红就调 TextColor(列,行,色)。改完必须调 UpdateTable() 才会重绘,它读滚动条滑块位置决定从哪偏移,双重循环搬数据和颜色。 左键按住滚动条快进的逻辑和之前 CListView 它们同构,不重复贴。复杂控件要把滚动条指针塞进指针库,WndContainer 里加对应数组,这部分前文已交待。

MQL5 / C++
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//|                                                    WndContainer.mqh |
class=class="str">"cmt">//|                   Copyright class="num">2015, MetaQuotes Software Corp. |
class=class="str">"cmt">//|                                     [MQL5官方文档] |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="macro">#include "LabelsTable.mqh"
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| 用于创建文字标签型表格的类                                      |
class=class="str">"cmt">//+------------------------------------------------------------------+
class CLabelsTable : class="kw">public CElement
  {
class="kw">private:
  class=class="str">"cmt">//--- 指向元件附加表单的指针
  CWindow            *m_wnd;
  class=class="str">"cmt">//---
class="kw">public:
                     CLabelsTable(class="type">void);
                    ~CLabelsTable(class="type">void);
  class=class="str">"cmt">//--- (class="num">1) 保存表单指针, (class="num">2) 返回滚动条的指针
  class="type">void               WindowPointer(CWindow &object)                { m_wnd=::GetPointer(object);       }
  class=class="str">"cmt">//---
class="kw">public:
  class=class="str">"cmt">//--- 图表事件处理函数
  class="kw">virtual class="type">void       OnEvent(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">//--- 计时器
  class="kw">virtual class="type">void       OnEventTimer(class="type">void);
  class=class="str">"cmt">//--- 移动元件
  class="kw">virtual class="type">void       Moving(const class="type">int x,const class="type">int y);
  class=class="str">"cmt">//--- (class="num">1) 显示, (class="num">2) 隐藏, (class="num">3) 重置, (class="num">4) 删除

「标签表格类的空壳与绘制接口」

在 MT5 自定义控件体系里,CLabelsTable 作为 CElement 的派生类,先把显隐、重置、销毁这组生命周期接口全声明成虚函数,方便后续在子类里改写绘制逻辑。 Show、Hide、Reset、Delete 四个虚函数构成基础控制面,而 SetZorders 与 ResetZorders 专门管鼠标左键点击的层级优先级——这在多控件叠加的贵金属盯盘面板里,直接决定你点表头会不会误触到下方 K 线。 构造函数与析构函数目前都是空实现,说明这一版只是把架子搭好:CLabelsTable::CLabelsTable(void) {} 和 ~CLabelsTable(void) {} 里没有任何资源申请或释放动作。 私有成员已经把表格的几何与配色参数预留出来了:m_row_y_size 控制每行高度,m_x_offset 是第一列锚点到左边缘的距离,m_column_x_offset 管列间距,m_fix_first_row 与 m_fix_first_column 则决定是否冻结首行首列。外汇与贵金属图表挂这类浮动表,高杠杆下误读层级可能放大操作风险。 公开设值接口走的是内联写法,例如 AreaColor(const color clr){ m_area_color=clr; } 和 RowYSize(const int y_size){ m_row_y_size=y_size; },改一个参数即时写进成员变量,不用等重绘消息。你开 MT5 新建个空 EA 把这段类声明贴进去,编译能过就说明环境对齐了。

MQL5 / C++
class="kw">virtual class="type">void        Show(class="type">void);
class="kw">virtual class="type">void        Hide(class="type">void);
class="kw">virtual class="type">void        Reset(class="type">void);
class="kw">virtual class="type">void        Delete(class="type">void);
class=class="str">"cmt">//--- (class="num">1) 设置, (class="num">2) 重设鼠标左键点击的优先级
class="kw">virtual class="type">void        SetZorders(class="type">void);
class="kw">virtual class="type">void        ResetZorders(class="type">void);
class=class="str">"cmt">//--- 重置颜色
class="kw">virtual class="type">void        ResetColors(class="type">void) {}
};
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| 构造函数                                    |
class=class="str">"cmt">//+------------------------------------------------------------------+
CLabelsTable::CLabelsTable(class="type">void)
  {
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| 析构函数                                        |
class=class="str">"cmt">//+------------------------------------------------------------------+
CLabelsTable::~CLabelsTable(class="type">void)
  {
  }
class CLabelsTable : class="kw">public CElement
  {
class="kw">private:
  class=class="str">"cmt">//--- 每行高度
  class="type">int               m_row_y_size;
  class=class="str">"cmt">//--- 表格背景颜色
  class="type">color             m_area_color;
  class=class="str">"cmt">//--- 表格文字的默认颜色
  class="type">color             m_text_color;
  class=class="str">"cmt">//--- 第一列的中心锚点与控件左方边缘的距离
  class="type">int               m_x_offset;
  class=class="str">"cmt">//--- 列间锚点的距离
  class="type">int               m_column_x_offset;
  class=class="str">"cmt">//--- 第一行的锁定模式
  class="type">bool              m_fix_first_row;
  class=class="str">"cmt">//--- 第一列的锁定模式
  class="type">bool              m_fix_first_column;
  class=class="str">"cmt">//--- 鼠标左键点击优先级
  class="type">int               m_zorder;
  class="type">int               m_area_zorder;
  class=class="str">"cmt">//---
class="kw">public:
  class=class="str">"cmt">//--- (class="num">1) 背景颜色, (class="num">2) 文字颜色
  class="type">void              AreaColor(const class="type">color clr)                { m_area_color=clr;                }
  class="type">void              TextColor(const class="type">color clr)                { m_text_color=clr;               }
  class=class="str">"cmt">//--- (class="num">1) 行的高度, (class="num">2) 设置第一列锚点与表格左方边缘的距离,
  class=class="str">"cmt">//    (class="num">3) 设置列间锚点的距离
  class="type">void              RowYSize(const class="type">int y_size)                { m_row_y_size=y_size;            }
交给小布盯盘看盘口
这些诊断小布盯盘的 AIGC 已内置,打开对应品种页即可看到由表格类渲染的多周期信号矩阵,你专注决策而非重复画控件。

常见问题

文本标签型用 CLabelsTable 只展示不可编辑的二维数据,编辑框型 CTable 允许单元格输入;前者坐标按中心锚点算,后者需处理焦点与提交。
数据超出可视区时滚动条要联动重绘单元格坐标与锁定表头,外挂难以同步状态,类内封装才能保证移动滑块后显示一致。
偏向绘制型与标签型混合,由 AIGC 在品种页直接渲染信号矩阵,省去手动搭 CLabelsTable 或 CCanvasTable 的重复劳动。
可以。锁定模式支持仅锁首行、仅锁首列或同时锁,离开滑块初始位置后对应表头仍固定显示。
控件区域固定而数据集可能很大,VisibleTableSize 决定面板内画多少格,TableSize 存全量,滚动时按偏移取切片。