DoEasy. 控件(第 二十九 部分):滚动条(ScrollBar)辅助控件(基础篇)
「给滚动条控件打地基」
在 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 翻这三个文件,把下列宏和枚举补上,编译看滚动条在图表上悬停是否按预期变色即可验证。
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_ 前缀就能看到完整定义,复制这段枚举进自己的面板管理类,省得手写字符串比对。
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 看状态栏提示是否跟着变,就能验证映射有没有断。
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 对应的本地化串,验证你客户端语言包是否缺项。
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) :