指标子窗口滚动条:用画布与矩形标签搭出轻量控件(基础篇)
📜

指标子窗口滚动条:用画布与矩形标签搭出轻量控件(基础篇)

(1/3)· 在指标子窗口里塞进一个能拖动的滚动条,很多新手卡在图形对象选型

新手友好 第 1/3 篇
直接堆几十个 OBJ_LABEL 做长列表,图表对象数爆表后事件响应就开始卡顿。不少人没意识到,位图标签加画布能只用一个对象画出整片文本区。滚动条若用错图形类型,拖动时重绘会拖累整个终端。

「在指标子窗口里塞一个能拖的滚动条」

MT5 的指标子窗口默认只画曲线,想放可交互控件得自己用 MQL5 的自定义对象去堆。Anatoli Kazharski 在 2014-05-27 发布的示例贴里给出了一套可运行的滚动条做法,原帖浏览量 2447、收藏 8,说明这类需求在老交易员里并不冷门。 核心思路是用 OBJ_RECTANGLE 画轨道、OBJ_RECTANGLE_LABEL 或按钮类对象做滑块,再用 ChartEvent 捕获鼠标拖拽,把滑块的 y 坐标映射成数值区间。难点在子窗口坐标系和普通图表不一样,直接用 WindowY() 会偏。 下面这段是示例里初始化滚动条轨道的骨架,注意子窗口索引用 subwin 变量传入,别写死 0:

MQL5 / C++
class="type">int subwin = class="num">1;
class="type">class="kw">string track = "ScrollTrack";
ObjectCreate(class="num">0, track, OBJ_RECTANGLE, subwin, class="num">0, class="num">0);
ObjectSetInteger(class="num">0, track, OBJPROP_XSIZE, class="num">12);
ObjectSetInteger(class="num">0, track, OBJPROP_YSIZE, class="num">200);
ObjectSetInteger(class="num">0, track, OBJPROP_COLOR, clrDimGray);
ObjectSetInteger(class="num">0, track, OBJPROP_STYLE, STYLE_SOLID);
逐行看:第 1 行声明子窗口号 1(主图是 0,指标子窗从 1 起算);第 2 行给轨道对象起名;第 3 行在指定子窗口建矩形;第 4~5 行把轨道锁成宽 12 像素、高 200 像素的竖条;第 6~7 行设颜色和线型。跑起来后你拖滑块,只要在 OnChartEvent 里读滑块 y 再反算,就能把滚动位置喂给指标缓冲区的偏移量。 外汇和贵金属品种波动大、跳空多,这类自定义控件在实盘重载时可能丢对象,建议在 OnDeinit 里显式 DeleteObject 清场,别依赖终端自动回收。

MQL5 / C++
class="type">int subwin = class="num">1;
class="type">class="kw">string track = "ScrollTrack";
ObjectCreate(class="num">0, track, OBJ_RECTANGLE, subwin, class="num">0, class="num">0);
ObjectSetInteger(class="num">0, track, OBJPROP_XSIZE, class="num">12);
ObjectSetInteger(class="num">0, track, OBJPROP_YSIZE, class="num">200);
ObjectSetInteger(class="num">0, track, OBJPROP_COLOR, clrDimGray);
ObjectSetInteger(class="num">0, track, OBJPROP_STYLE, STYLE_SOLID);

◍ 在指标子窗口里造一个极简垂直滚动条

继续做控件实验,这次把注意力放到滚动条上,而且仍然限定在指标子窗口里跑。前面那篇讲按钮的文章已经把 OnChartEvent() 里的事件处理拆得很细,本文只在必要处顺带提一句,没读过的建议先去把事件回调那套弄明白再动手。 为了演示,我们给一个“大列表”配一条垂直滚动条,列表内容是用 MQL5 资源能取到的全部金融工具属性。之前几篇用 OBJ_LABEL 堆文本标签,这次换成画布:只需一个 OBJ_BITMAP_LABEL 位图标签,把所有文字画上去,不用再建几十上百个对象,MT5 图表对象树也干净。 滚动条刻意做简单:不要上下箭头按钮,只留背景条和滚动框(thumb)。鼠标悬停滚动框时换色,按下再换一色,提示“已选中可拖”。底层用 OBJ_RECTANGLE_LABEL 矩形标签来生成滚动区与滑块,参数调起来很直接。 外汇与贵金属品种属性多、点差跳变快,这类列表在实盘品种上刷新可能卡顿,上 MT5 用 EURUSD+XAUUSD 各挂一个实例对比就知道了。

用画布和矩形标签搭一个可滚动品种属性面板

在 MT5 指标子窗口里画交互列表,核心是先挂 CCanvas。原文把属性清单规模定死为 71 行(LIST_SIZE 71),这意味着列表超出可视高度时必须靠滚动条翻页,而不是动态伸缩。 初始化阶段要做的第一件事是声明画布实例并包含标准库 Canvas.mqh。子窗口编号、高度、前缀名等全部进全局变量,其中前缀用指标短名加下划线,避免和图表上其他对象重名。 画布本身用 CCanvas 的 CreateBitmapLabel() 第二变体建立,配套 ResizeCanvas() 去跟随子窗口尺寸变化;销毁时调 Destroy()。实际印字只用到 FontSet()、TextHeight()、TextOut()、Erase()、Update() 这几个方法,Erase() 本质是用指定色填满整块画布再做重绘。 ShowSymbolInfo() 靠一个 current_thumb_position 参数决定从哪一行开始印,默认 0 即从头显示。内部先由 InitializePropertyArrays() 填值和颜色数组,再 Erase() 清屏、TextOut() 逐行输出、Update() 刷新。 滚动条不用自绘图形,而是两个 OBJ_RECTANGLE_LABEL:一个当背景,一个当可拖动的 thumb。thumb 高度在 AdjustScrollbarThumb() 里按列表占比算出来;鼠标左键按下时把控件交给 thumb,避免窄条拖动时光标横向飘。 真正让列表动起来的是事件链:OnChartEvent() 捕获鼠标,MoveThumb() 改 thumb 坐标,ThumbYCoordinateToPercent() 把像素位置转成百分比,UpdateListAndScrollbarThumb() 同步翻页。OnDeinit() 里必须删对象,否则切换周期会留垃圾。 把下面这段全局声明直接丢进 MetaEditor 5 新建指标模板,编译后挂 EURUSD 这类品种,子窗口就会出静态属性表;滚动功能需补完事件函数才生效,外汇与贵金属品种属性随行情实时变,实盘使用前请在策略测试器验证稳定性,相关品种波动大、风险高。

MQL5 / C++
class="macro">#define LIST_SIZE class="num">71                                                                       class=class="str">"cmt">// Number of strings in the list of financial instrument properties
class=class="str">"cmt">//--- Include the class for working with the canvas
class="macro">#include <Canvas\Canvas.mqh>
CCanvas canvas;
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//|  Global variables                                                |
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//--- Indicator subwindow properties
class="type">int                subwindow_number            =WRONG_VALUE;                                        class=class="str">"cmt">// Subwindow number
class="type">int                subwindow_height            =class="num">0;                                                  class=class="str">"cmt">// Subwindow height
class="type">class="kw">string             subwindow_shortname         ="TestScrollbar";                                     class=class="str">"cmt">// Short name of the indicator
class="type">class="kw">string             prefix                      =subwindow_shortname+"_";                             class=class="str">"cmt">// Prefix for objects
class="type">int                chart_width                 =class="num">0;                                                  class=class="str">"cmt">// Chart width

「画布与列表控件的初始变量怎么设」

在 MT5 里用 Canvas 自绘一个可滚动的行情列表,第一步是把所有几何与样式变量先归零或给默认值,避免后续 OnChartEvent 里读到脏数据。下面这段声明集中定义了图表子窗口高度、画布名称、背景色以及字体字号等基础属性。

MQL5 / C++
class="type">int chart_height=class="num">0; class=class="str">"cmt">// Chart height
class="type">int chart_y_offset=class="num">0; class=class="str">"cmt">// Distance from the chart top to the subwindow
class=class="str">"cmt">//--- Canvas properties
class="type">class="kw">string canvas_name=prefix+"canvas"; class=class="str">"cmt">// Canvas name
class="type">class="kw">color canvas_background_color=C&class="macro">#x27;class="num">20,class="num">20,class="num">20&class="macro">#x27;; class=class="str">"cmt">// Canvas background class="type">class="kw">color
ENUM_COLOR_FORMAT color_format=COLOR_FORMAT_XRGB_NOALPHA; class=class="str">"cmt">// Alpha channel component is ignored
class=class="str">"cmt">//--- List properties
class="type">int list_height=class="num">0; class=class="str">"cmt">// List height
class="type">int text_height=class="num">0; class=class="str">"cmt">// Text height
class="type">int font_size=class="num">15; class=class="str">"cmt">// Font size
class="type">class="kw">string font_name="Calibri"; class=class="str">"cmt">// Font
class="type">class="kw">double line_size=class="num">100/LIST_SIZE; class=class="str">"cmt">// Size of a single class="type">class="kw">string on the list, expressed as percentage
class=class="str">"cmt">//--- Scrollbar properties: scroll box
class="type">class="kw">string scrollbar_thumb_name=prefix+"scrollbar_thumb"; class=class="str">"cmt">// Scroll box object name
class="type">int scrollbar_thumb_x1=class="num">0; class=class="str">"cmt">// x1 coordinate
class="type">int scrollbar_thumb_y1=class="num">0; class=class="str">"cmt">// y1 coordinate
逐行拆解:chart_height 与 chart_y_offset 初始为 0,代表还没拿到主图客户区尺寸;canvas_name 用 prefix 拼接 "canvas" 防止多实例对象名冲突;背景色 C'20,20,20' 是近黑灰,适合暗色终端。 color_format 选 COLOR_FORMAT_XRGB_NOALPHA,意味着像素写入时不带透明通道,绘制效率更高。font_size 写死 15、font_name 用 Calibri,而 line_size 用 100/LIST_SIZE 表示每行占列表总高的百分比——若 LIST_SIZE 为 20,单行就是 5%。 scrollbar_thumb_x1/y1 归零,说明滚动块位置要在计算完列表可视区后才重算。开 MT5 把这段贴进 EA 全局区,改一下 prefix 和 LIST_SIZE,就能看到变量面板里这些字段的初始态。外汇与贵金属波动剧烈,这类自绘控件只作辅助盯盘,实盘仍以小周期价格行为为准。

MQL5 / C++
class="type">int chart_height=class="num">0; class=class="str">"cmt">// Chart height
class="type">int chart_y_offset=class="num">0; class=class="str">"cmt">// Distance from the chart top to the subwindow
class=class="str">"cmt">//--- Canvas properties
class="type">class="kw">string canvas_name=prefix+"canvas"; class=class="str">"cmt">// Canvas name
class="type">class="kw">color canvas_background_color=C&class="macro">#x27;class="num">20,class="num">20,class="num">20&class="macro">#x27;; class=class="str">"cmt">// Canvas background class="type">class="kw">color
ENUM_COLOR_FORMAT color_format=COLOR_FORMAT_XRGB_NOALPHA; class=class="str">"cmt">// Alpha channel component is ignored
class=class="str">"cmt">//--- List properties
class="type">int list_height=class="num">0; class=class="str">"cmt">// List height
class="type">int text_height=class="num">0; class=class="str">"cmt">// Text height
class="type">int font_size=class="num">15; class=class="str">"cmt">// Font size
class="type">class="kw">string font_name="Calibri"; class=class="str">"cmt">// Font
class="type">class="kw">double line_size=class="num">100/LIST_SIZE; class=class="str">"cmt">// Size of a single class="type">class="kw">string on the list, expressed as percentage
class=class="str">"cmt">//--- Scrollbar properties: scroll box
class="type">class="kw">string scrollbar_thumb_name=prefix+"scrollbar_thumb"; class=class="str">"cmt">// Scroll box object name
class="type">int scrollbar_thumb_x1=class="num">0; class=class="str">"cmt">// x1 coordinate
class="type">int scrollbar_thumb_y1=class="num">0; class=class="str">"cmt">// y1 coordinate

◍ 自定义滚动条的结构体变量拆解

在 MT5 面板里手搓滚动条,第一步是把滑块、背景和锚点三类状态拆成独立变量。下面这段声明直接给出了各字段的初始值与类型,复制进 EA 的全局区就能用。 滑块本体用 x2/y2 记录右下角坐标,height 初始为 0 表示还没根据内容区算高度;width 锁死 9 像素,和背景同宽才不露缝。y_percent 存的是滑块中心在轨道里的百分比位置,后面拖拽时拿它反推可视行号。 clicked 这个 bool 是交互核心:false 时走 clrSilver,鼠标悬停切 clrDimGray,按下变 clrSlateGray,三态配色直接在变量层写完,不用进 OnEvent 里硬判。 背景对象名用 prefix 拼接,避免多实例重名;颜色 C'50,50,50' 是深灰,和滑块银色形成对比。fix_point 与 fix_point_y_offest 记录「按下瞬间鼠标相对滑块顶部的偏移」,拖拽时鼠标动、滑块跟着动但不跳帧,就靠这两个 int 做差量修正。 外汇和贵金属图表加载这类自定义控件时波动剧烈,实盘前务必在策略测试器里先拖几十次确认无重绘卡顿。

MQL5 / C++
class="type">int               scrollbar_thumb_x2           =class="num">0;                                                 class=class="str">"cmt">// x2 coordinate
class="type">int               scrollbar_thumb_y2           =class="num">0;                                                 class=class="str">"cmt">// y2 coordinate
class="type">class="kw">double            scrollbar_thumb_y_percent   =class="num">0.0;                                                 class=class="str">"cmt">// Y-coordinate expressed as percentage
class="type">int               scrollbar_thumb_width       =class="num">9;                                                 class=class="str">"cmt">// Width
class="type">int               scrollbar_thumb_height      =class="num">0;                                                 class=class="str">"cmt">// Height
class="type">bool              scrollbar_thumb_clicked     =false;                                             class=class="str">"cmt">// State(whether or not the scroll box is clicked)
class="type">class="kw">color             scrollbar_thumb_color       =clrSilver;                                         class=class="str">"cmt">// Scroll box class="type">class="kw">color
class="type">class="kw">color             scrollbar_thumb_color_on_hover=clrDimGray;                                      class=class="str">"cmt">// Scroll box class="type">class="kw">color when the cursor hovers over it
class="type">class="kw">color             scrollbar_thumb_color_on_click=clrSlateGray;                                    class=class="str">"cmt">// Scroll box class="type">class="kw">color when clicked
class=class="str">"cmt">//--- Scrollbar properties: background
class="type">class="kw">string            scrollbar_background_name   =prefix+"scrollbar_background";                    class=class="str">"cmt">// Background object name
class="type">int               scrollbar_background_width  =class="num">9;                                                 class=class="str">"cmt">// Background width
class="type">class="kw">color             scrollbar_background_color  =C&class="macro">#x27;class="num">50,class="num">50,class="num">50&class="macro">#x27;;                                       class=class="str">"cmt">// Background class="type">class="kw">color
class=class="str">"cmt">//--- Scrollbar properties: other
class="type">int               scrollbar_fix_point         =class="num">0;                                                 class=class="str">"cmt">// Y-coordinate of the fix point upon clicking
class="type">int               scrollbar_fix_point_y_offest=class="num">0;                                                 class=class="str">"cmt">// Distance along the Y-axis from the scrollbar top to the fix point

把品种属性一次性塞进数组

在 MT5 自定义指标或 EA 里批量读取品种属性时,先声明一个字符串数组把属性名固化下来,比每次硬写 SymbolInfoInteger/SymbolInfoDouble 调用更利于维护。下面这段声明把鼠标状态、颜色与数值数组、以及一份覆盖 60+ 项的品种属性名清单一次性铺开。 数组 symbol_propety_names 长度为 LIST_SIZE,内容从「当前时段成交笔数」「即时买/卖挂单总数」一路列到「结算价」「均价加权」等。实际取数时可用循环下标对应 SYMBOL_xxx 枚举,避免散落各处。 外汇与贵金属品种的属性(如点差、掉期、保证金)随经纪商和行情波动,读取结果仅反映当下状态,交易前需自行核对,杠杆品类高风险。

MQL5 / C++
class=class="str">"cmt">//--- Mouse button state(pressed/released)
class="type">bool                 mouse_button_state=false;
class=class="str">"cmt">//--- Arrays for financial instrument properties
class="type">class="kw">color                symbol_property_colors[];                     class=class="str">"cmt">// Colors of values
class="type">class="kw">string               symbol_property_values[];                     class=class="str">"cmt">// Values
class=class="str">"cmt">//--- Financial instrument class="kw">property names
class="type">class="kw">string symbol_propety_names[LIST_SIZE]=
  {
   "Number of deals in the current session",
   "Total number of Buy orders at the moment",
   "Total number of Sell orders at the moment",
   "Volume of the last deal",
   "Maximum daily volume",
   "Minimum daily volume",
   "Time of the last quote",
   "Number of decimal places",
   "Spread in points",
   "Floating spread indication",
   "Maximum number of requests displayed in the Depth of Market",
   "Contract price calculation mode",
   "Order execution type",
   "Trading start date for an instrument(usually used for futures)",
   "Trading end date for an instrument(usually used for futures)",
   "Minimum distance in points from the current closing price for the purpose of setting Stop orders",
   "Freeze distance for trading operations(in points)",
   "Deal execution mode",
   "Swap calculation model",
   "Day of the week when triple swap is charged",
   "Flags of allowed order expiration modes",
   "Flags of allowed order filling modes",
class=class="str">"cmt">//---
   "Bid - best price at which an instrument can be sold",
   "Maximum Bid of the day",
   "Minimum Bid of the day",
   "Ask - best price at which an instrument can be bought",
   "Maximum Ask of the day",
   "Minimum Ask of the day",
   "Last - last deal price",
   "Maximum Last of the day",
   "Minimum Last of the day",
   "Point value",
   "Calculated tick value for a winning position",
   "Calculated tick value for a losing position",
   "Minimum price change",
   "Trade contract size",
   "Minimum volume for deal execution",
   "Maximum volume for deal execution",
   "Minimum step of volume change for deal execution",
   "Maximum allowable total volume of an open position and pending orders in the same direction",
   "Long swap value",
   "Short swap value",
   "Initial margin - amount in the margin currency required for opening a position(class="num">1 lot)",
   "Maintenance margin for an instrument",
   "Margin requirement applicable to class="type">long positions",
   "Margin requirement applicable to class="type">short positions",
   "Margin requirement applicable to Limit orders",
   "Margin requirement applicable to Stop orders",
   "Margin requirement applicable to Stop Limit orders",
   "Total volume of deals in the current session",
   "Total turnover in the current session",
   "Total volume of open positions",
   "Total volume of buy orders at the moment",
   "Total volume of sell orders at the moment",
   "Open price of the session",
   "Close price of the session",
   "Average weighted price of the session",
   "Settlement price of the current session",
让小布替你跑这套
这些控件结构小布盯盘的 AIGC 已内置示例模板,打开对应品种页即可直接套用,你只需改属性字段,不用从零写画布初始化。

常见问题

矩形标签能作为独立可拖拽层,配合画布位图刷新,比多个按钮拼更接近原生滚动框,且事件处理集中在 OnChartEvent 一次完成。
它允许把画布挂到指标子窗口的位图标签上,新手不必管设备上下文,只调用 Resize 和 TextOut 就能画列表。
可以,小布盯盘的品种页内置了基于画布的属性列表控件样例,拖动逻辑和本文一致,可对照改代码。
Erase 只清缓冲,Update 才把位图推到图表,漏掉后者画面会停在上一张帧,看起来像没刷新。
第 2、3 篇分别进入实战拖动与多控件协同,关于拖动坐标映射的完整讨论见《指标子窗口滚动条·实战篇》。