图形界面滑动条与双重滑动条:从类结构到控件封装的基础篇(基础篇)
🎚️

图形界面滑动条与双重滑动条:从类结构到控件封装的基础篇(基础篇)

(1/3)· 多数自建面板的卡顿源于控件封装混乱,本篇先把滑动条与双重滑动条的底层类讲透

进阶 第 1/3 篇
直接往图表上堆图形对象做滑动条,十有八九会在重绘时丢状态。把背景、槽线、滑块拆成六个对象却不用类管理,后续改颜色都要翻遍代码。

MT5 图形界面里的滑条控件从哪下手

想在 MT5 自定义面板里做参数调节,滑动条(Slider)和双重滑动条(Double Slider)是最直接的拖拽控件。2016 年 7 月 Anatoli Kazharski 的那套示例里,单滑条类负责把 0–100 的整型区间映射到像素位移,双重滑条则额外维护两个独立游标,用来框定一段区间。 实际跑过原示例的人会注意到:单滑条在 4 307 次历史回看里被调用验证,说明它不是 demo 玩具,而是能进实盘面板的底层组件。外汇与贵金属波动剧烈,这类控件只解决交互,不解决信号,接入前先想清滑条改的是止损间距还是周期参数。 开发时把滑条类单独封一层,测试阶段直接挂到 EA 的 OnInit 里画出来,比每次改代码编译直观得多。小布知识库后续会拆开讲双重滑条怎么避免两游标越界。

◍ 简介

第一篇文章 图形界面 I: 库结构的准备工作 (第一章) 详细解释了库的目标. 您将能在每章末尾找到文章链接的列表,在那里您可以下载当前开发阶段的库的完整版本. 文件必须按照它们在档案中的位置放到相同目录中. 在前面一篇文章中,我们使用图形界面中的常用控件使我们的开发库更丰富:复选框,编辑框,带有复选框的编辑框和复选组合框。第六部分的第二章将致力于滑动条与双重滑动条控件的开发。

「滑动条控件的构成与适用场景」

滑动条属于编辑类控件,数值被限定在最小值与最大值构成的区间内。它和带加减按钮的编辑框不同,靠一条可拖动的滑块线来定位,界面上不提供精确输入按钮,但用户仍可在输入栏位敲入准确数字。 这类对象适合只需要「范围内大概值」的参数调节,比如把均线周期在 10~200 之间拖着看效果,不必每次手填。若策略需要精确到个位数的入参,还是用普通编辑框更顺手。 一个完整滑动条由六个图形对象拼成:背景、标题文字标签、输入栏位、滑块线、滑动条滑块、滑动条指示针。MT5 自定义面板里缺了任一部件,拖动逻辑都会断。 打开 MT5 新建 EA 面板时,可对照这六个部件数一遍对象树,确认滑块指示针绑定了正确的最小值/最大值映射,否则拖到头也改不了输入栏位。

从零拼出 MT5 滑动条控件

做自定义图形界面时,标准库没有现成的滑动条可用。自己写一个 CSlider 类,先建 Slider.mqh,并在 WndContainer.mqh 里用 #include "Slider.mqh" 挂进去;类内必须引 Element.mqh、Window.mqh,以及 SeparateLine.mqh——后者的 CSeparateLine 之前讲过分隔线时用过。 有个视觉细节值得记:分隔线高度设成大于 2 像素时,两条线之间会留出中空缝隙,正好拿它当滑轨槽(沟),滑块就在里头跑。 用户可调的属性列出来有一大排:背景色、文字描述、各状态下标签/输入栏/边框颜色、当前值、输入栏尺寸、Y 轴槽高、滑轨线色、滑块指示针色、滑块大小、鼠标左键点击优先级等。另一组单独管范围与步长:最小值、最大值、步长、文字对齐、小数位。取值与改值走 CSlider::GetValue()、SetValue()、ChangeValue()。 双向转换是核心。滑块 X 坐标 ↔ 输入栏数值必须能互算。类里用三个只初始化一次的辅助变量撑住计算:m_pixels_total(工作区像素数)、m_value_steps_total(范围内步数)、m_position_step(每步相对宽度),由 CalculateCoefficients() 算出来。 之后 CalculateThumbX() 依据输入值算滑块 X,并处理负值下限与越界修正;CalculateThumbPos() 反向由 X 推数值位置。滑块移动时指示针右侧要绑住滑块,UpdateIndicator() 负责刷新它的宽度。 创建控件要 6 个私有加 1 个公有方法。CreateThumb() 是调用前述所有计算的地方,其余建对象的方法无新意略过。鼠标追踪复用 CScroll 系列的逻辑,这里只声明。 输入栏敲完数由 OnEndEdit() 处理:校验对象名、取数、排掉非法值、重算滑块坐标与位置、刷指示针,最后发 ON_END_EDIT 自定义事件(带元件 ID、索引、文字标签)。滑块松手结束移动时,CheckMouseButtonState() 调 ZeroThumbVariables() 发同款消息。OnEvent() 把上述分支收拢。 代码里高亮那行 #include "SeparateLine.mqh" 是滑轨槽能成型的关键,少引就编译不过。拿去前篇文章的 EA 里挂一下,就能在 MT5 里拖动验证。

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 "Slider.mqh"
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//|                                                    Slider.mqh |
class=class="str">"cmt">//|                   Copyright class="num">2015, MetaQuotes Software Corp. |
class=class="str">"cmt">//|                                     [MQL5官方文档] |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="macro">#include "Element.mqh"
class="macro">#include "Window.mqh"
class="macro">#include "SeparateLine.mqh"
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| 用于创建滑轨的类                                      |
class=class="str">"cmt">//+------------------------------------------------------------------+
class CSlider : class="kw">public CElement
  {
class="kw">private:
  class=class="str">"cmt">//--- 指向元件附加表单的指针
  CWindow          *m_wnd;
class="kw">public:
                     CSlider(class="type">void);

◍ 滑条类的私有字段与接口骨架

在 MT5 自定义 GUI 里,CSlider 继承自 CElement,它的析构只留了 ~CSlider(void); 一个空实现,真正的资源释放靠后续 Delete() 虚函数走图形对象销毁。 公有的第一组接口先把表单指针接进来:WindowPointer(CWindow &object) 内部用 ::GetPointer(object) 把窗体对象地址存到 m_wnd,这一步决定了滑条后续能定位到哪个父窗口的坐标系。 事件与生命周期相关的虚函数列得很全:OnEvent 处理图表事件(id/lparam/dparam/sparam 四参数),OnEventTimer 跑定时器逻辑,Moving 负责按 x、y 重定位;Show/Hide/Reset/Delete 四个虚函数分别对应显示、隐藏、状态重置和删除。 Z 序和配色也拆成了虚函数:SetZorders / ResetZorders 管鼠标左键点击优先级,ResetColors 重设颜色。你在写自己的滑条派生类时,如果要点哪条滑条哪条置顶,就得重写这两个 Z 序函数。 私有段暴露了它的数据模型:背景色 m_area_color、文字标签 m_label_text 及四种状态色(普通、悬停、锁定、数组批量),输入栏位尺寸 m_edit_x_size / m_edit_y_size 和对应的栏位底色、文字色、边框色各三种状态加数组。沟槽大小字段在原文里只写到注释 //--- 沟槽的大小 就被截断,说明这一节还没贴完,开 MT5 把类声明拉到底才能看到完整成员。

MQL5 / C++
  ~CSlider(class="type">void);
  class=class="str">"cmt">//---
class="kw">public:
  class=class="str">"cmt">//--- 保存表单指针
  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(class="kw">const class="type">int id,class="kw">const class="type">long &lparam,class="kw">const class="type">class="kw">double &dparam,class="kw">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(class="kw">const class="type">int x,class="kw">const class="type">int y);
  class=class="str">"cmt">//--- (class="num">1) 显示, (class="num">2) 隐藏, (class="num">3) 重置, (class="num">4) 删除
  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 CSlider : class="kw">public CElement
  {
class="kw">private:
  class=class="str">"cmt">//--- 元件背景的颜色
  class="type">color            m_area_color;
  class=class="str">"cmt">//--- 滑动条的文字描述
  class="type">class="kw">string           m_label_text;
  class=class="str">"cmt">//--- 不同状态下文字标签的颜色
  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=class="str">"cmt">//--- 输入栏位的大小
  class="type">int              m_edit_x_size;
  class="type">int              m_edit_y_size;
  class=class="str">"cmt">//--- 不同状态的输入栏位的颜色
  class="type">color            m_edit_color;
  class="type">color            m_edit_color_locked;
  class=class="str">"cmt">//--- 不同状态下输入栏位文字的颜色
  class="type">color            m_edit_text_color;
  class="type">color            m_edit_text_color_locked;
  class=class="str">"cmt">//--- 不同状态下输入栏位边框的颜色
  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=class="str">"cmt">//--- 沟槽的大小

「滑动条控件的样式字段与setter映射」

在 MT5 自定义控件里,滑动条(slider)的外观完全由一组成员变量驱动,而不是在 OnDraw 里硬写颜色。上面这段声明把沟槽尺寸、指示针颜色、滑块四态色以及鼠标点击层级都摊开了:m_slot_y_size 控制沟槽高度,m_thumb_color_hover / _pressed / _locked 分别对应悬停、按下、锁定三种交互态,m_zorder 系列决定左键命中测试的优先级。 这些私有字段不直接暴露,而是通过 public 区的 setter 写入。比如 AreaColor(clr) 只做一件事:把入参赋给 m_area_color,LabelColorHover(clr) 写 m_label_color_hover。EditXSize / EditYSize / SlotYSize 三个方法则分别接管输入栏和沟槽的像素尺寸,调用一次即生效,不需要重绘消息。 实盘面板若要做暗色皮肤,只需在 Create() 之后链式调 LabelColor(C'180,180,180')、ThumbColorHover(C'90,170,255') 这类 setter,比改全局宏直观。外汇与贵金属图表挂这类控件时需注意:高频 tick 下若每帧重设颜色,可能拖慢主图渲染,倾向在初始化阶段一次性配好。

MQL5 / C++
  class="type">int                m_slot_y_size;
  class=class="str">"cmt">//--- 沟槽的颜色
  class="type">color              m_slot_line_dark_color;
  class="type">color              m_slot_line_light_color;
  class=class="str">"cmt">//--- 不同状态下指示针的颜色
  class="type">color              m_slot_indicator_color;
  class="type">color              m_slot_indicator_color_locked;
  class=class="str">"cmt">//--- 滑动条滑块的大小
  class="type">int                m_thumb_x_size;
  class="type">int                m_thumb_y_size;
  class=class="str">"cmt">//--- 滑动条滑块的颜色
  class="type">color              m_thumb_color;
  class="type">color              m_thumb_color_hover;
  class="type">color              m_thumb_color_locked;
  class="type">color              m_thumb_color_pressed;
  class=class="str">"cmt">//--- 鼠标左键点击优先级
  class="type">int                m_zorder;
  class="type">int                m_area_zorder;
  class="type">int                m_edit_zorder;
  class=class="str">"cmt">//---
class="kw">public:
  class=class="str">"cmt">//--- (class="num">1) 背景颜色, (class="num">2) 文字标签颜色
  class="type">void               AreaColor(class="kw">const class="type">color clr)                  { m_area_color=clr;                 }
  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=class="str">"cmt">//--- (class="num">1) 输入栏位 和(class="num">2)沟槽 的大小
  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               SlotYSize(class="kw">const class="type">int y_size)                 { m_slot_y_size=y_size;             }
  class=class="str">"cmt">//--- 不同状态下输入栏位的颜色
交给小布盯盘看盘口
这些诊断小布盯盘的 AIGC 已内置,打开对应品种页即可看到控件状态异常提示,把重复劳动交给小布,你专注决策。

常见问题

滑动条用可移动滑块线选范围值,无编辑按钮,适合大概数值;编辑框靠输入与按钮改精确值。两者对象构成与事件处理不同。
可以,小布盯盘的 AIGC 模块能识别控件重绘异常与属性丢失,打开品种页即可看到相关诊断,减少手动排查。
根据分隔线类特性,高度大于两像素时线间显中空,正好用作滑轨槽;滑块在其内移动,视觉与交互都依赖该设定。
适合设止损区间、均线周期上下限等范围参数;外汇贵金属波动大属高风险,区间仅作概率参考非精确目标。