DoEasy. 控件(第 二十九 部分):滚动条(ScrollBar)辅助控件(基础篇)
📘

DoEasy. 控件(第 二十九 部分):滚动条(ScrollBar)辅助控件(基础篇)

第 1/3 篇

「给滚动条控件打地基」

在 DoEasy 库的控件体系里,滚动条(ScrollBar)被拆成垂直与水平两类辅助控件,它们都从同一个基准控件类派生,而不是各自重写一遍底层逻辑。这样后续加交互、改样式,只需在基类里动一次。 目前这版库把 ScrollBarVertical 和 ScrollBarHorisontal 的共性——如轨道长度计算、滑块比例映射——收进基类,两个派生类只负责方向相关的坐标换算。从 2023 年 4 月公开的示例看,该系列文章累计已发到第二十九部分,说明控件层已迭代近一年。 在 MT5 里验证路径很简单:建个空 EA,把库里 ScrollBar 基类拖进包含列表,用标准测试窗口拖出垂直滚动条,看滑块是否随数据量自动缩放比例。外汇与贵金属图表加载长周期数据时用这类控件,仍需注意点差与滑点带来的高风险。

从 TrackBar 到滚动条基类

上一篇文章里我做了 TrackBar 控件,靠拖滑块就能输入数值。因为滚动条(ScrollBar)是它的父类,这一节直接接着开发 WinForms 里的 ScrollBar 对象。 滚动条本质是用来滚动窗体内容的:内容超出容器时,底部水平条左右滚,右侧垂直条上下滚。本节先写基准 ScrollBar,再派生 ScrollBarVertical 和 ScrollBarHorisontal,三者都是静态的,暂不管理窗体内容,留到后面处理。 每个滚动条对象由底图加控件组成:两个箭头按钮(垂直为上/下,水平为左/右)和一个可抓取的滑块(ThumbArea)。箭头和滑块都预留了鼠标交互,但本节只把对象建出来,交互逻辑下篇再写。 滚动条本身应是容器,能挂载其他控件。默认内容没超容器就隐藏;一旦超出就显示,让用户挪附加对象控制可见区域。本节把两个滚动条挂到主窗体上,只为看外观、定下一步怎么改。

◍ 给滚动条控件补上状态色与类型枚举

鼠标悬停或按下滚动条轨道、滑块时,控件应当切换颜色以给出视觉反馈。我们为悬停、点击、常态分别定义独立色值,并塞进 DoEasy 的 Defines.mqh 宏列表,方便后续统一调用。 新增色值里,滑块常态用 C'0xCD,0xCD,0xCD'(浅灰),鼠标悬停转 C'0xA6,0xA6,0xA6'(中灰),点击时进一步压到 C'0x60,0x60,0x60'(深灰)——三级梯度让操作者能直接感知交互深度。 图形元素类型枚举也得扩:在 ENUM_GRAPH_ELEMENT_TYPE 里加一个基准滚动条类型,再派生水平、垂直两个子类型,继承关系在编译期就锁定,避免运行时误判控件方向。 描述文本和消息索引要同步进 Data.mqh,否则在 GBaseObj 的 TypeElementDescription() 里按类型取不到 CMessage 文本。基类方法只认枚举,不补索引就会返回空描述,调试时容易误以为对象没创建成功。 开 MT5 后直接去 Include\DoEasy 翻这三个文件,把下列宏和枚举补上,编译看滚动条在图表上悬停是否按预期变色即可验证。

MQL5 / C++
class="macro">#define CLR_DEF_CONTROL_PROGRESS_BAR_BACK_COLOR(C&class="macro">#x27;0xF0,0xF0,0xF0&class="macro">#x27;)  class=class="str">"cmt">// ProgressBar control background class="type">class="kw">color
class="macro">#define CLR_DEF_CONTROL_PROGRESS_BAR_BORDER_COLOR(C&class="macro">#x27;0xBC,0xBC,0xBC&class="macro">#x27;)  class=class="str">"cmt">// ProgressBar control frame class="type">class="kw">color
class="macro">#define CLR_DEF_CONTROL_PROGRESS_BAR_FORE_COLOR(C&class="macro">#x27;0x00,0x78,0xD7&class="macro">#x27;)  class=class="str">"cmt">// ProgressBar control text class="type">class="kw">color
class="macro">#define CLR_DEF_CONTROL_PROGRESS_BAR_BAR_COLOR(C&class="macro">#x27;0x06,0xB0,0x25&class="macro">#x27;)  class=class="str">"cmt">// ProgressBar control progress line class="type">class="kw">color
class="macro">#define CLR_DEF_CONTROL_SCROLL_BAR_TRACK_BACK_COLOR(C&class="macro">#x27;0xF0,0xF0,0xF0&class="macro">#x27;)  class=class="str">"cmt">// ScrollBar control background class="type">class="kw">color
class="macro">#define CLR_DEF_CONTROL_SCROLL_BAR_TRACK_BORDER_COLOR(C&class="macro">#x27;0xFF,0xFF,0xFF&class="macro">#x27;)  class=class="str">"cmt">// ScrollBar control frame class="type">class="kw">color
class="macro">#define CLR_DEF_CONTROL_SCROLL_BAR_TRACK_FORE_COLOR(C&class="macro">#x27;0x60,0x60,0x60&class="macro">#x27;)  class=class="str">"cmt">// ScrollBar control text class="type">class="kw">color
class="macro">#define CLR_DEF_CONTROL_SCROLL_BAR_THUMB_COLOR(C&class="macro">#x27;0xCD,0xCD,0xCD&class="macro">#x27;)  class=class="str">"cmt">// ScrollBar control capture area class="type">class="kw">color
class="macro">#define CLR_DEF_CONTROL_SCROLL_BAR_THUMB__MOUSE_DOWN(C&class="macro">#x27;0x60,0x60,0x60&class="macro">#x27;)  class=class="str">"cmt">// Color of ScrollBar control capture area when clicking on the control
class="macro">#define CLR_DEF_CONTROL_SCROLL_BAR_THUMB_MOUSE_OVER(C&class="macro">#x27;0xA6,0xA6,0xA6&class="macro">#x27;)  class=class="str">"cmt">// Color of ScrollBar control capture area when hovering over the control
class="macro">#define DEF_CONTROL_LIST_MARGIN_X(class="num">1)                class=class="str">"cmt">// Gap between columns in ListBox controls
class="macro">#define DEF_CONTROL_LIST_MARGIN_Y(class="num">0)                class=class="str">"cmt">// Gap between rows in ListBox controls
class=class="str">"cmt">//+---------------------------------------------+
class=class="str">"cmt">//| The list of graphical element types          |
class=class="str">"cmt">//+---------------------------------------------+
enum ENUM_GRAPH_ELEMENT_TYPE
  {
   GRAPH_ELEMENT_TYPE_STANDARD,                    class=class="str">"cmt">// Standard graphical object
   GRAPH_ELEMENT_TYPE_STANDARD_EXTENDED,           class=class="str">"cmt">// Extended standard graphical object
   GRAPH_ELEMENT_TYPE_SHADOW_OBJ,                  class=class="str">"cmt">// Shadow object
   GRAPH_ELEMENT_TYPE_ELEMENT,                     class=class="str">"cmt">// Element
   GRAPH_ELEMENT_TYPE_FORM,                        class=class="str">"cmt">// Form
   GRAPH_ELEMENT_TYPE_WINDOW,                      class=class="str">"cmt">// Window
   class=class="str">"cmt">//--- WinForms
   GRAPH_ELEMENT_TYPE_WF_UNDERLAY,                 class=class="str">"cmt">// Panel object underlay
   GRAPH_ELEMENT_TYPE_WF_BASE,                     class=class="str">"cmt">// Windows Forms Base
   class=class="str">"cmt">//--- &class="macro">#x27;Container&class="macro">#x27; object types are to be set below

「WinForms 图形元素枚举的底子」

在 MT5 的自定义图形接口里,所有可绘制的 WinForms 风格对象都靠一组枚举常量来标识类型。上面这段枚举把容器、标准控件和辅助元素三类都列了出来,写 EA 面板或指标 UI 时,分辨父容器与子控件的归属就靠这些常量。 容器类从 GRAPH_ELEMENT_TYPE_WF_CONTAINER 到 GRAPH_ELEMENT_TYPE_WF_SPLIT_CONTAINER,覆盖了面板、分组框、选项卡和分割布局;标准控件则从 _WF_COMMON_BASE 往下,包含标签、按钮、勾选框、单选钮以及三种列表框变体。 辅助元素容易被忽略:像箭头按钮的四个方向常量、上下/左右箭头组合框、分割条、Tab 页头与页字段,都是运行时由主控件自动生成的子对象。你在用 OBJPROP_TYPE 取对象类型做递归遍历时,得把这些辅助枚举也纳入判断,否则树形结构会断。 直接在 MetaEditor 搜 GRAPH_ELEMENT_TYPE_WF_ 前缀就能看到完整定义,复制这段枚举进自己的面板管理类,省得手写字符串比对。

MQL5 / C++
  GRAPH_ELEMENT_TYPE_WF_CONTAINER,                   class=class="str">"cmt">// Windows Forms container base object
  GRAPH_ELEMENT_TYPE_WF_PANEL,                         class=class="str">"cmt">// Windows Forms Panel
  GRAPH_ELEMENT_TYPE_WF_GROUPBOX,                      class=class="str">"cmt">// Windows Forms GroupBox
  GRAPH_ELEMENT_TYPE_WF_TAB_CONTROL,                   class=class="str">"cmt">// Windows Forms TabControl
  GRAPH_ELEMENT_TYPE_WF_SPLIT_CONTAINER,               class=class="str">"cmt">// Windows Forms SplitContainer
  class=class="str">"cmt">//--- &class="macro">#x27;Standard control&class="macro">#x27; object types are to be set below
  GRAPH_ELEMENT_TYPE_WF_COMMON_BASE,                   class=class="str">"cmt">// Windows Forms base standard control
  GRAPH_ELEMENT_TYPE_WF_LABEL,                         class=class="str">"cmt">// Windows Forms Label
  GRAPH_ELEMENT_TYPE_WF_BUTTON,                        class=class="str">"cmt">// Windows Forms Button
  GRAPH_ELEMENT_TYPE_WF_CHECKBOX,                      class=class="str">"cmt">// Windows Forms CheckBox
  GRAPH_ELEMENT_TYPE_WF_RADIOBUTTON,                   class=class="str">"cmt">// Windows Forms RadioButton
  GRAPH_ELEMENT_TYPE_WF_ELEMENTS_LIST_BOX,             class=class="str">"cmt">// Base list object of Windows Forms elements
  GRAPH_ELEMENT_TYPE_WF_LIST_BOX,                      class=class="str">"cmt">// Windows Forms ListBox
  GRAPH_ELEMENT_TYPE_WF_CHECKED_LIST_BOX,              class=class="str">"cmt">// Windows Forms CheckedListBox
  GRAPH_ELEMENT_TYPE_WF_BUTTON_LIST_BOX,               class=class="str">"cmt">// Windows Forms ButtonListBox
  GRAPH_ELEMENT_TYPE_WF_TOOLTIP,                       class=class="str">"cmt">// Windows Forms ToolTip
  GRAPH_ELEMENT_TYPE_WF_PROGRESS_BAR,                  class=class="str">"cmt">// Windows Forms ProgressBar
  class=class="str">"cmt">//--- Auxiliary elements of WinForms objects
  GRAPH_ELEMENT_TYPE_WF_LIST_BOX_ITEM,                 class=class="str">"cmt">// Windows Forms ListBoxItem
  GRAPH_ELEMENT_TYPE_WF_TAB_HEADER,                    class=class="str">"cmt">// Windows Forms TabHeader
  GRAPH_ELEMENT_TYPE_WF_TAB_FIELD,                     class=class="str">"cmt">// Windows Forms TabField
  GRAPH_ELEMENT_TYPE_WF_SPLIT_CONTAINER_PANEL,         class=class="str">"cmt">// Windows Forms SplitContainerPanel
  GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON,                  class=class="str">"cmt">// Windows Forms ArrowButton
  GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_UP,               class=class="str">"cmt">// Windows Forms UpArrowButton
  GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_DOWN,             class=class="str">"cmt">// Windows Forms DownArrowButton
  GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_LEFT,             class=class="str">"cmt">// Windows Forms LeftArrowButton
  GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_RIGHT,            class=class="str">"cmt">// Windows Forms RightArrowButton
  GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTONS_UD_BOX,          class=class="str">"cmt">// Windows Forms UpDownArrowButtonsBox
  GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTONS_LR_BOX,          class=class="str">"cmt">// Windows Forms LeftRightArrowButtonsBox
  GRAPH_ELEMENT_TYPE_WF_SPLITTER,                      class=class="str">"cmt">// Windows Forms Splitter
  GRAPH_ELEMENT_TYPE_WF_HINT_BASE,                     class=class="str">"cmt">// Windows Forms HintBase

控件枚举与类型描述函数的对接细节

在 MT5 的图形库底层,Windows Forms 风格控件被集中编入枚举。上面列出的片段里,GRAPH_ELEMENT_TYPE_WF_SCROLL_BAR 及其水平、垂直变体,加上 BarProgressBar、Glare object,都是标准 GUI 元素类型常量,用于区分画布上不同交互部件。 紧跟着的 MSG_GRAPH_ELEMENT_TYPE_WF_* 系列常量,是这些控件的多语言描述映射键。例如 MSG_GRAPH_ELEMENT_TYPE_WF_SCROLL_BAR 对应俄语"Элемент управления ScrollBar"与英文"Control element \"ScrollBar\"",说明同一控件在终端内能按语言环境切换标签。 TypeElementDescription 函数用三元运算符把枚举值翻译成可读文本。传入 GRAPH_ELEMENT_TYPE_STANDARD 会返回 MSG_GRAPH_ELEMENT_TYPE_STANDARD 对应的字符串,传入 GRAPH_ELEMENT_TYPE_SHADOW_OBJ 则返回阴影对象描述;如果你自建面板时加了自定义 WF 控件,得在这里补一条 type==你的常量 ? 描述 的分支,否则调试时只能看到空串。 开 MT5 按 F4 进 MetaEditor,搜 CGBaseObj::TypeElementDescription 能看到完整判断链。改完枚举后重编译图形库,拖出 ScrollBar 看状态栏提示是否跟着变,就能验证映射有没有断。

MQL5 / C++
class="type">class="kw">string CGBaseObj::TypeElementDescription(const ENUM_GRAPH_ELEMENT_TYPE type)
  {
   class="kw">return
     (
      type==GRAPH_ELEMENT_TYPE_STANDARD                   ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_STANDARD)               :
      type==GRAPH_ELEMENT_TYPE_STANDARD_EXTENDED          ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_STANDARD_EXTENDED)     :
      type==GRAPH_ELEMENT_TYPE_ELEMENT                   ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_ELEMENT)               :
      type==GRAPH_ELEMENT_TYPE_SHADOW_OBJ                ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_SHADOW_OBJ)            :

◍ 图形元素类型到本地化文本的映射写法

在 MT5 的自定义图形库里,把枚举型图元类型转成界面可读文本,常见做法是嵌套三元运算符直接查消息表。下面这段分支覆盖了从基础画布 FORM、WINDOW 到 WinForms 容器与标准控件的全部映射,编译后界面显示文本由 CMessage::Text 按语言环境返回。 注意注释把元素分成三块:WinForms 底层(UNDERLAY / BASE)、容器类(CONTAINER / GROUPBOX / PANEL / TAB_CONTROL / SPLIT_CONTAINER)、标准控件(LABEL / CHECKBOX / RADIOBUTTON / BUTTON 及各类 LIST_BOX)。这种写法在元素类型达到 20 个以上时仍可读,但维护成本随分支增长线性上升,后续加类型建议抽成查表函数。 直接把下面代码贴进你的图形元素调试模块,开 MT5 跑一遍,能在日志里看到每种 type 对应的本地化串,验证你客户端语言包是否缺项。

MQL5 / C++
   type==GRAPH_ELEMENT_TYPE_FORM                   ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_FORM)              :
   type==GRAPH_ELEMENT_TYPE_WINDOW                 ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WINDOW)            :
   class=class="str">"cmt">//--- WinForms
   type==GRAPH_ELEMENT_TYPE_WF_UNDERLAY            ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_UNDERLAY)       :
   type==GRAPH_ELEMENT_TYPE_WF_BASE                ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_BASE)           :
   class=class="str">"cmt">//--- Containers
   type==GRAPH_ELEMENT_TYPE_WF_CONTAINER           ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_CONTAINER)      :
   type==GRAPH_ELEMENT_TYPE_WF_GROUPBOX            ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_GROUPBOX)       :
   type==GRAPH_ELEMENT_TYPE_WF_PANEL               ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_PANEL)          :
   type==GRAPH_ELEMENT_TYPE_WF_TAB_CONTROL         ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_TAB_CONTROL)    :
   type==GRAPH_ELEMENT_TYPE_WF_SPLIT_CONTAINER     ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_SPLIT_CONTAINER):
   class=class="str">"cmt">//--- Standard controls
   type==GRAPH_ELEMENT_TYPE_WF_COMMON_BASE          ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_COMMON_BASE)     :
   type==GRAPH_ELEMENT_TYPE_WF_LABEL               ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_LABEL)          :
   type==GRAPH_ELEMENT_TYPE_WF_CHECKBOX            ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_CHECKBOX)       :
   type==GRAPH_ELEMENT_TYPE_WF_RADIOBUTTON         ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_RADIOBUTTON)    :
   type==GRAPH_ELEMENT_TYPE_WF_BUTTON              ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_BUTTON)         :
   type==GRAPH_ELEMENT_TYPE_WF_ELEMENTS_LIST_BOX   ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_ELEMENTS_LIST_BOX) :
   type==GRAPH_ELEMENT_TYPE_WF_LIST_BOX            ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_LIST_BOX)       :
   type==GRAPH_ELEMENT_TYPE_WF_LIST_BOX_ITEM       ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_LIST_BOX_ITEM)  :
   type==GRAPH_ELEMENT_TYPE_WF_CHECKED_LIST_BOX    ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_CHECKED_LIST_BOX):
   type==GRAPH_ELEMENT_TYPE_WF_BUTTON_LIST_BOX     ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_BUTTON_LIST_BOX):
   type==GRAPH_ELEMENT_TYPE_WF_TOOLTIP             ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_TOOLTIP)        :

常见问题

在控件基类中补一组状态色枚举(如正常、悬停、按下),绘制时按当前状态取对应色值即可,别写死单一颜色。
把 TrackBar 里和滚动无关的逻辑抽进滚动条基类,只保留位置、范围、步长等通用字段,子类再补滚动专用行为。
可以,把控件代码贴给小布,它能对照枚举映射表指出状态色缺失或类型描述函数未对接的地方,省去手动排查。
建一个类型到本地化文本的映射函数,运行时按当前语言查表返回字符串,新增类型只改映射表不动绘制逻辑。
在枚举定义后立刻写全描述函数分支,并用静态断言或启动日志打印未覆盖的枚举值,能早发现问题。