DoEasy.控件(第 33 部分):垂直滚动条·进阶篇
控件对象的相对坐标与图形基件封装
在自定义图形控件里,高光(glare)对象的位置不能写死绝对坐标,否则父容器一移动它就错位。正确做法是在初始化块里用父对象坐标做差,把高光改成相对定位:X 相对值 = 高光自身 X 减父对象 X,Y 同理。 可见区域也要跟着父对象走。调用 SetVisibleArea(0,0,glare.Width(),glare.Height(),false) 时,前两个 0 代表从父对象左上角起算,宽高直接吃高光自己的尺寸,最后一个 false 表示不裁剪子对象——这样高光范围恰好罩住整个控件。 对外暴露的接口层,GetObject() 直接返回 this 指针方便外部持有;SetTypeNode(int) 只存节点类型不做事。真正画图交给 CreateForm 的三个重载和 CreateTrendLine:趋势线函数默认线宽 1、实线样式,调用时若不给 width 和 style 参数就走这套默认值。 开 MT5 新建 EA 把这段塞进你的 CGraphElmControl 派生类,改两行坐标差看看高光是否跟手;外汇和贵金属图表上做控件高风险,参数没调稳前别挂实盘。
{
class=class="str">"cmt">//--- Set the relative coordinates of the glare object
glare.SetCoordXRelative(glare.CoordX()-this.CoordX());
glare.SetCoordYRelative(glare.CoordY()-this.CoordY());
class=class="str">"cmt">//--- and its visibility scope equal to the entire object
glare.SetVisibleArea(class="num">0,class="num">0,glare.Width(),glare.Height(),false);
}
}
class="kw">return;
}
class=class="str">"cmt">//--- ...
class=class="str">"cmt">//--- ...
class="kw">public:
class=class="str">"cmt">//--- Return itself
CGraphElmControl *GetObject(class="type">void) { class="kw">return &this; }
class=class="str">"cmt">//--- Set a type of the object the graphics is constructed for
class="type">void SetTypeNode(const class="type">int type_node) { this.m_type_node=type_node; }
class=class="str">"cmt">//--- Create a form object
CForm *CreateForm(const class="type">int form_id,const class="type">long chart_id,const class="type">int wnd,const class="type">class="kw">string name,const class="type">int x,const class="type">int y,const class="type">int w,const class="type">int h);
CForm *CreateForm(const class="type">int form_id,const class="type">int wnd,const class="type">class="kw">string name,const class="type">int x,const class="type">int y,const class="type">int w,const class="type">int h);
CForm *CreateForm(const class="type">int form_id,const class="type">class="kw">string name,const class="type">int x,const class="type">int y,const class="type">int w,const class="type">int h);
class=class="str">"cmt">//--- Creates the trend line standard graphical object
class="type">bool CreateTrendLine(const class="type">long chart_id,const class="type">class="kw">string name,const class="type">int subwindow,
const class="type">class="kw">datetime time1,const class="type">class="kw">double price1,
const class="type">class="kw">datetime time2,const class="type">class="kw">double price2,
class="type">class="kw">color clr,class="type">int width=class="num">1,ENUM_LINE_STYLE style=STYLE_SOLID);
class="type">bool CreateTrendLine(const class="type">class="kw">string name,const class="type">int subwindow,「把趋势线和箭头塞进自己的图形类」
在 MT5 里做价格行为标记,如果每次都调 ObjectCreate,代码会散得到处都是。把趋势线和箭头封装成一个控制类的成员函数,回测和盯盘脚本都能直接复用。 下面这组声明给出了三种重载思路:带 chart_id 的版本用于指定图表和子窗口,不带 chart_id 的版本默认当前图表,只传 name、time、price 的版本则连子窗口都走默认。外汇与贵金属波动剧烈,用这类封装快速重画结构线,能降低手动拖线的误差,但信号失效风险始终存在。 CreateTrendLine 固定收 time1/price1 与 time2/price2 两组坐标,线宽默认 1、线型默认 STYLE_SOLID;CreateArrow 则多一个 uchar arrow_code 参数决定箭头样式,同样 width 默认 1。构造函数里把 m_type 设为 OBJECT_DE_TYPE_GELEMENT_CONTROL,等于给这类对象打了个类型标签,后面做批量清理或属性修改时可以直接筛。
const class="type">class="kw">datetime time1,const class="type">class="kw">double price1, const class="type">class="kw">datetime time2,const class="type">class="kw">double price2, class="type">class="kw">color clr,class="type">int width=class="num">1,ENUM_LINE_STYLE style=STYLE_SOLID); class="type">bool CreateTrendLine(const class="type">class="kw">string name, const class="type">class="kw">datetime time1,const class="type">class="kw">double price1, const class="type">class="kw">datetime time2,const class="type">class="kw">double price2, class="type">class="kw">color clr,class="type">int width=class="num">1,ENUM_LINE_STYLE style=STYLE_SOLID); class=class="str">"cmt">//--- Create the arrow standard graphical object class="type">bool CreateArrow(const class="type">long chart_id,const class="type">class="kw">string name,const class="type">int subwindow, const class="type">class="kw">datetime time1,const class="type">class="kw">double price1, class="type">class="kw">color clr,class="type">uchar arrow_code,class="type">int width=class="num">1); class="type">bool CreateArrow(const class="type">class="kw">string name,const class="type">int subwindow, const class="type">class="kw">datetime time1,const class="type">class="kw">double price1, class="type">class="kw">color clr,class="type">uchar arrow_code,class="type">int width=class="num">1); class="type">bool CreateArrow(const class="type">class="kw">string name, const class="type">class="kw">datetime time1,const class="type">class="kw">double price1, class="type">class="kw">color clr,class="type">uchar arrow_code,class="type">int width=class="num">1); class=class="str">"cmt">//--- Constructors CGraphElmControl(){ this.m_type=OBJECT_DE_TYPE_GELEMENT_CONTROL; }
◍ 图形对象管理类的隐藏与不可选设定
在 MT5 里批量绘制趋势线、通道等图形时,如果不把对象设为隐藏且不可选,鼠标误触会拖歪整组分析线,回测可视化也容易被干扰。下面这段 CGraphElmControl 类的私有方法,专门给标准图形对象统一设置基础属性。 SetCommonParamsStdGraphObj 接收图表 ID 和对象名两个参数,内部连续调用四次 ObjectSetInteger:先把 OBJPROP_HIDDEN 置 true 让对象不在对象列表里显示;再把 OBJPROP_SELECTED 设 false、OBJPROP_SELECTABLE 设 false,彻底禁止鼠标点选;最后 OBJPROP_TIMEFRAMES 配 OBJ_ALL_PERIODS,使图形在所有周期都可见。 注意 CreateTrendLine 在构造 OBJ_TREND 前会先调 CreateNewStdGraphObject,失败则用 Print 输出错误描述。外汇与贵金属图表波动剧烈,这类自动画线工具仅作概率辅助,实盘仍需人工复核,杠杆品种高风险。
class CGraphElmControl : class="kw">public CObject { class="kw">private: class="type">int m_type; class=class="str">"cmt">// Object type class="type">int m_type_node; class=class="str">"cmt">// Type of the object the graphics is constructed for class=class="str">"cmt">//--- Set general parameters for standard graphical objects class="type">void SetCommonParamsStdGraphObj(const class="type">long chart_id,const class="type">class="kw">string name); class="kw">public: class=class="str">"cmt">//--- Return itself CGraphElmControl *GetObject(class="type">void) { class="kw">return &this; } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//|Set general parameters for standard graphical objects | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void CGraphElmControl::SetCommonParamsStdGraphObj(const class="type">long chart_id,const class="type">class="kw">string name) { ::ObjectSetInteger(chart_id,name,OBJPROP_HIDDEN,true); ::ObjectSetInteger(chart_id,name,OBJPROP_SELECTED,false); ::ObjectSetInteger(chart_id,name,OBJPROP_SELECTABLE,false); ::ObjectSetInteger(chart_id,name,OBJPROP_TIMEFRAMES,OBJ_ALL_PERIODS); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Create the trend line standard graphical object | class=class="str">"cmt">//| on a specified chart in a specified subwindow | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">bool CGraphElmControl::CreateTrendLine(const class="type">long chart_id,const class="type">class="kw">string name,const class="type">int subwindow, const class="type">class="kw">datetime time1,const class="type">class="kw">double price1, const class="type">class="kw">datetime time2,const class="type">class="kw">double price2, class="type">class="kw">color clr,class="type">int width=class="num">1,ENUM_LINE_STYLE style=STYLE_SOLID) { if(!CreateNewStdGraphObject(chart_id,name,OBJ_TREND,subwindow,time1,price1,time2,price2)) { ::Print(DFUN,CMessage::Text(MSG_GRAPH_STD_OBJ_ERR_FAILED_CREATE_STD_GRAPH_OBJ),": ",StdGraphObjectTypeDescription(OBJ_TREND));
趋势线与箭头对象的封装创建
在 MT5 自定义指标或 EA 里,反复调用 ObjectCreate 写趋势线很啰嗦。把创建逻辑收进 CGraphElmControl 类的方法,主图与子图各行其是,调用端只传坐标和样式即可。 CreateTrendLine 提供了两个重载:一个显式接收 subwindow 参数,可把线画到指定子窗口;另一个默认把 subwindow 填 0,直接落主图。两者最终都转发给带 chart_id 的内部实现,后者统一设置颜色、线宽与线型。 底层那段设置值得逐行看:SetCommonParamsStdGraphObj 先写公共属性,随后 ObjectSetInteger 三次分别绑定 OBJPROP_COLOR、OBJPROP_WIDTH、OBJPROP_STYLE,默认 width=1、style=STYLE_SOLID。若前面校验不通过会 return false,正常则 return true,调用方据此判断对象是否真的建出来了。 外汇与贵金属图表上加这类对象属于高风险环境下的辅助标记,线段本身不代表任何方向判定,仅帮你把价格行为区肉眼锁定。
class="kw">return false; } this.SetCommonParamsStdGraphObj(chart_id,name); ::ObjectSetInteger(chart_id,name,OBJPROP_COLOR,clr); ::ObjectSetInteger(chart_id,name,OBJPROP_WIDTH,width); ::ObjectSetInteger(chart_id,name,OBJPROP_STYLE,style); class="kw">return true; } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Create the trend line standard graphical object | class=class="str">"cmt">//| on the current chart in a specified subwindow | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">bool CGraphElmControl::CreateTrendLine(const class="type">class="kw">string name,const class="type">int subwindow, const class="type">class="kw">datetime time1,const class="type">class="kw">double price1, const class="type">class="kw">datetime time2,const class="type">class="kw">double price2, class="type">class="kw">color clr,class="type">int width=class="num">1,ENUM_LINE_STYLE style=STYLE_SOLID) { class="kw">return this.CreateTrendLine(::ChartID(),name,subwindow,time1,price1,time2,price2,clr,width,style); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Create the trend line standard graphical object | class=class="str">"cmt">//| on the current chart in the main window | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">bool CGraphElmControl::CreateTrendLine(const class="type">class="kw">string name, const class="type">class="kw">datetime time1,const class="type">class="kw">double price1, const class="type">class="kw">datetime time2,const class="type">class="kw">double price2, class="type">class="kw">color clr,class="type">int width=class="num">1,ENUM_LINE_STYLE style=STYLE_SOLID) { class="kw">return this.CreateTrendLine(::ChartID(),name,class="num">0,time1,price1,time2,price2,clr,width,style); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Create the arrow standard graphical object | class=class="str">"cmt">//| on a specified chart in a specified subwindow | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">bool CGraphElmControl::CreateArrow(const class="type">long chart_id,const class="type">class="kw">string name,const class="type">int subwindow,
「箭头对象的三种重载入口」
在 MT5 自定义图形库里,箭头对象(OBJ_ARROW)的创建被拆成了三个不同签名的成员函数,核心都收敛到带 chart_id 参数的那个重载。 带全参数的版本先调用 CreateNewStdGraphObject 尝试在主图或子窗口建对象,失败就通过 Print 输出标准错误描述并返回 false;成功后才用 ObjectSetInteger 依次写颜色、线宽、箭头编码。width 默认值是 1,arrow_code 是 uchar 类型,直接决定箭头外观。 另外两个重载省略了 chart_id,内部用 ChartID() 取当前图表句柄:一个允许指定 subwindow 子窗口号,另一个硬编码为 0 直接丢进主窗口。这样写的好处是脚本层调用时不用每次传图表 ID,但调试时得清楚真正干活的是第一个函数。 想验证的话,在 EA 里 include 这个类,用 CreateArrow("myarr",Time[0],Low[0],clrRed,159) 就能在主图最低价画一个红色箭头,159 对应 Wingdings 里的下指三角。
class="type">bool CGraphElmControl::CreateArrow(const class="type">long chart_id,const class="type">class="kw">string name,const class="type">int subwindow, const class="type">class="kw">datetime time1,const class="type">class="kw">double price1, class="type">class="kw">color clr,class="type">uchar arrow_code,class="type">int width=class="num">1) { if(!CreateNewStdGraphObject(chart_id,name,OBJ_ARROW,subwindow,time1,price1)) { ::Print(DFUN,CMessage::Text(MSG_GRAPH_STD_OBJ_ERR_FAILED_CREATE_STD_GRAPH_OBJ),": ",StdGraphObjectTypeDescription(OBJ_ARROW)); class="kw">return false; } this.SetCommonParamsStdGraphObj(chart_id,name); ::ObjectSetInteger(chart_id,name,OBJPROP_COLOR,clr); ::ObjectSetInteger(chart_id,name,OBJPROP_WIDTH,width); ::ObjectSetInteger(chart_id,name,OBJPROP_ARROWCODE,arrow_code); class="kw">return true; } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Create the arrow standard graphical object | class=class="str">"cmt">//| on the current chart in a specified subwindow | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">bool CGraphElmControl::CreateArrow(const class="type">class="kw">string name,const class="type">int subwindow, const class="type">class="kw">datetime time1,const class="type">class="kw">double price1, class="type">class="kw">color clr,class="type">uchar arrow_code,class="type">int width=class="num">1) { class="kw">return this.CreateArrow(::ChartID(),name,subwindow,time1,price1,clr,arrow_code,width); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Create the arrow standard graphical object | class=class="str">"cmt">//| on the current chart in the main window | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">bool CGraphElmControl::CreateArrow(const class="type">class="kw">string name, const class="type">class="kw">datetime time1,const class="type">class="kw">double price1, class="type">class="kw">color clr,class="type">uchar arrow_code,class="type">int width=class="num">1) { class="kw">return this.CreateArrow(::ChartID(),name,class="num">0,time1,price1,clr,arrow_code,width); }
◍ 封装图形对象的几个重载入口
在自定义指标或 EA 里批量画图时,直接调底层 CGraphElements 容易把 chart_id、subwindow 这些参数写散。上面这组 CreateForm 用了三个重载:带 chart_id 的版本可指定任意图表,只带 wnd 的版本锁定当前图表子窗口,最简版本默认丢进主窗口。 实际写面板类时,推荐统一走带 chart_id 的重载,避免切换品种后对象跑到错误图表。MT5 里图表 ID 用 ChartID() 取,子窗口序号从 0(主图)开始递增,IndicatorWindowTotal() 能拿到当前指标挂了几个子窗口。 趋势线封装更直白:CreateTrendLine 把时间-价格两点、颜色、线宽、线型一次传进去,默认 width=1、STYLE_SOLID。若想画虚线通道,把 style 改成 STYLE_DASH 即可, price1/price2 用 iLow/iHigh 取到的极值,回测里肉眼校一根趋势线平均省 3 行对象句柄代码。 外汇和贵金属波动大,图形对象只是辅助标记,任何画线信号都只是概率参考,下单前仍按自己的风控过一遍。
CForm *CreateForm(const class="type">int form_id,const class="type">long chart_id,const class="type">int wnd,const class="type">class="kw">string name,const class="type">int x,const class="type">int y,const class="type">int w,const class="type">int h) { class="kw">return this.m_graph_elm.CreateForm(form_id,chart_id,wnd,name,x,y,w,h); } class=class="str">"cmt">//--- Create a form object on the current chart in a specified subwindow CForm *CreateForm(const class="type">int form_id,const class="type">int wnd,const class="type">class="kw">string name,const class="type">int x,const class="type">int y,const class="type">int w,const class="type">int h) { class="kw">return this.m_graph_elm.CreateForm(form_id,wnd,name,x,y,w,h); } class=class="str">"cmt">//--- Create the form object on the current chart in the main window CForm *CreateForm(const class="type">int form_id,const class="type">class="kw">string name,const class="type">int x,const class="type">int y,const class="type">int w,const class="type">int h) { class="kw">return this.m_graph_elm.CreateForm(form_id,name,x,y,w,h); } class=class="str">"cmt">//--- Create a standard graphical trend line object in the specified subwindow of the specified chart class="type">bool CreateTrendLine(const class="type">long chart_id,const class="type">class="kw">string name,const class="type">int subwindow, const class="type">class="kw">datetime time1,const class="type">class="kw">double price1, const class="type">class="kw">datetime time2,const class="type">class="kw">double price2, class="type">class="kw">color clr,class="type">int width=class="num">1,ENUM_LINE_STYLE style=STYLE_SOLID) { class="kw">return this.m_graph_elm.CreateTrendLine(chart_id,name,subwindow,time1,price1,time2,price2,clr,width,style); } class=class="str">"cmt">//--- Create a standard graphical trend line object in the specified subwindow of the current chart class="type">bool CreateTrendLine(const class="type">class="kw">string name,const class="type">int subwindow,