指标子窗口滚动条:用画布与矩形标签搭出轻量控件(基础篇)
(1/3)· 在指标子窗口里塞进一个能拖动的滚动条,很多新手卡在图形对象选型
「在指标子窗口里塞一个能拖的滚动条」
MT5 的指标子窗口默认只画曲线,想放可交互控件得自己用 MQL5 的自定义对象去堆。Anatoli Kazharski 在 2014-05-27 发布的示例贴里给出了一套可运行的滚动条做法,原帖浏览量 2447、收藏 8,说明这类需求在老交易员里并不冷门。
核心思路是用 OBJ_RECTANGLE 画轨道、OBJ_RECTANGLE_LABEL 或按钮类对象做滑块,再用 ChartEvent 捕获鼠标拖拽,把滑块的 y 坐标映射成数值区间。难点在子窗口坐标系和普通图表不一样,直接用 WindowY() 会偏。
下面这段是示例里初始化滚动条轨道的骨架,注意子窗口索引用 subwin 变量传入,别写死 0:
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);
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 这类品种,子窗口就会出静态属性表;滚动功能需补完事件函数才生效,外汇与贵金属品种属性随行情实时变,实盘使用前请在策略测试器验证稳定性,相关品种波动大、风险高。
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 里读到脏数据。下面这段声明集中定义了图表子窗口高度、画布名称、背景色以及字体字号等基础属性。
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
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 做差量修正。 外汇和贵金属图表加载这类自定义控件时波动剧烈,实盘前务必在策略测试器里先拖几十次确认无重绘卡顿。
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 枚举,避免散落各处。 外汇与贵金属品种的属性(如点差、掉期、保证金)随经纪商和行情波动,读取结果仅反映当下状态,交易前需自行核对,杠杆品类高风险。
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",