DoEasy 进度条控件基础:用 WinForms 对象搭出可编程进度指示(基础篇)
(1/3)· 还在用 Print 刷屏看任务进度?这篇把静态进度条变成可驱动的状态面板
「给进度条控件补上缺失的库方法」
这一节接着折腾 DoEasy 库里的 ProgressBar 控件,目标是把它往 WinForms 风格的可用对象上再推一步。原实现里进度条只能设值和读值,缺少对范围、步进和可见状态的统一封装,调用方每次都要写重复逻辑。 作者在 2023 年 3 月 30 日的示例里给出基础结构,原文标注示例阅读量 1 095,说明这类底层控件改造在 MT5 用户里确有刚需。进度条属于 WinForms 模拟对象,不依赖系统原生组件,因此在 MT5 的图表子窗口里也能稳定绘制。 下一步计划补上最小值、最大值、步进增量和重绘触发的方法,让进度条可以像标准控件一样被库统一调度。外汇与贵金属图表上叠加这类自绘控件存在刷新开销,实盘使用前建议在策略测试器里跑一遍观察资源占用。
◍ 给进度条加上动态眩光与子进程指示
这一节接着上篇的 WinForms 静态进度条往下做,目标是把控件变成可被程序驱动的对象。它由底图和进度条两层组成,后续还能在底图上叠文本或图像,但当前核心是先实现可编程填充与视觉反馈。 留意过 Windows 自带进度条的人会注意到,填充时有一条沿条移动的眩光。它不只是好看:当总操作量很大时,进度条不会每次操作都涨一格,而是攒够一批才刷新。于是可能出现条已满格但任务还在跑的情况——只要眩光还在动,就说明子进程没结束。 若进度条放在独立窗口,满格后需手动关窗才能消掉提示;若是拆成多个子进度条,已完成的那个会保持显示,直到整条流程跑完。双重指示很实用:条满了但眩光仍在 = 该子进程尚未彻底完成。外汇/贵金属 EA 里跑批量回测或历史校验时,这种指示能避免误判卡死,但请记住自动化交易本身属高风险,指示仅作辅助。 带视觉效果的控件必须登记进活动元素列表,函数库轮询该列表并触发各自计时器。具体平滑逻辑上篇踩过坑:标准库抗锯齿在透明时画不出来。实测把不透明度设在 128–255 用抗锯齿,0–127 用普通画法,半透明能盖住锯齿感,线条该平滑平滑、该隐没隐没。
把眩光从阴影继承里抽出来
原来设想眩光(Glare)继承阴影对象,靠父类的高斯模糊做特效,实测这条路走不通:继承关系把眩光锁死在阴影概念里,往别的图形元素上挂特效时极其别扭。现在改让眩光直接派生自 CWinFormBase——所有 WinForms 对象的基类,并塞进辅助对象列表,哪个类要用,一行包含就能挂上,不用再往各个库类里重复写阴影那套处理。 模糊方法也从 CShadowObj 搬到 GCnvElement,所有图形元素都能调,不再被阴影私有。改动涉及 Defines.mqh(眩光 ID 从图形对象区挪到辅助控件区)、Data.mqh(新增消息索引与文本)、GraphINI.mqh(配色方案属性总数加到 12,含眩光不透明度默认值)。GBaseObj 里类型描述方法的眩光分支也顺手挪到惯例位置——if-else 链里靠后的类型理论上多耗 CPU 周期,后续全库改用 switch 才彻底解决。 半透明线条的抗锯齿是个坑:不透明度 >127 时平滑绘制还正常,一旦降到 128 以下线条就碎成像素。代码里直接判阈值,超了用抗锯齿、否则调无平滑方法,半透明状态下像素化根本看不出来。其余预定义形状若也出现伪影,照同法处理。 眩光类本体改为继承 CWinFormBase,私密段存样式变量并声明绘图切换方法;DrawGlareFigure() 按样式分流。目前只实装矩形和平行四边形两种:矩形铺满对象画填充块,平行四边形用倾斜三角形多边形。构造函数里模糊默认 4、白模糊色、不透明度 127、隐藏态,保证对象永不彻底不透明。 进度条ProgressBar 同步改造:Value 按对象宽度百分比算而非像素,私密段加极限/步长变量与宽度计算方法。创建时按 50% 宽度建 BarProgressBar,并挂一个眩光进活动对象列表,由库计时器驱动沿条运动。PerformStep() 每次把 Value 加步长,宽度重算;小于 1 像素时藏起来模拟零宽。避免眩光溢出容器的关键是重绘方法——先全清成透明,再画形状、裁剪越界区、刷画布。 面板 Panel 和容器 Container 类里补上眩光包含与参数设置,任何独立 GUI 窗口内的控件都能加眩光。计时器里做“暂停-眩光-暂停”循环,复用此前工具提示的平滑显隐属性。外汇/贵金属 GUI 自动化展示此类特效时,注意 MT5 策略测试器与实际终端渲染差异可能带来视觉偏差,属高风险界面层改动。
「进度条眩光的对象封装与计时器接管」
在 BarProgressBar.mqh 这个进度条类里,眩光效果被拆成了独立的辅助对象。私密区里补了设置和读取延迟的方法,同时声明了一个统一的属性初始化函数,把原来散在构造函数里的赋值逻辑收拢到一起。 两个构造函数现在只调用初始化方法,原先那堆直接写死的属性赋值被整段搬走。除了挪代码,还顺手加了眩光在进度条上停留两秒的间隔控制——之前计时器只是把 GetTickCount() 的返回值丢进图表注释,现在换成完整事件处理。 新的计时器处理沿用了工具提示对象那套 ENUM_CANV_ELEMENT_DISPLAY_STATE 枚举:等对象就绪、沿进度条移动、再淡出,区别只在于进度条眩光不做淡出等待。这个枚举常量通用性够强,以后要扩新的显示事件直接往里加就行。 下面这段是图形元素类型枚举的节选,涵盖了从标准对象到 WinForms 控件体系的分类,编译前核对一下 GRAPH_ELEMENT_TYPE_SHADOW_OBJ 后面那行空位,避免枚举值错位。
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 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
◍ GUI 元素枚举里的 WinForms 映射细节
在 MT5 自定义图形接口里,GRAPH_ELEMENT_TYPE 枚举把大量 Windows Forms 控件抽象成图形元素类型。上面这段列出了 ToolTip、ProgressBar 以及 ListBoxItem、TabHeader、SplitContainerPanel 等辅助元素,还包含四个方向的 ArrowButton 和左右/上下箭头组合框,共 20 余个 WinForms 派生类型。 注意 GRAPH_ELEMENT_TYPE_WF_GLARE_OBJ 被标亮,它代表“眩光对象”(Glare object),不属于标准 WinForms 控件,而是终端图形层附加的视觉元素。如果你的 CForm 文本对象报错 MSG_FORM_OBJECT_TEXT_NO_GLARE_OBJ_FIRST_CREATE_IT,说明还没调用 CreateGlareObj() 生成该对象,对应错误码 MSG_FORM_OBJECT_ERR_FAILED_CREATE_GLARE_OBJ 则表示创建失败。 另有一个独立枚举 ENUM_CANV_ELEMENT_VISUAL_EFF_STYLE,仅含 RECTANGLE 与 PARALLELOGRAMM 两种视觉特效样式,用于画布元素的形变渲染。开 MT5 新建指标把下面枚举贴进头文件,编译后能在诊断栏直接看到这些类型常量,方便后续做 GUI 对象调试。
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 GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_LEFT, class=class="str">"cmt">// Windows Forms HintMoveLeft GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_RIGHT, class=class="str">"cmt">// Windows Forms HintMoveRight GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_UP, class=class="str">"cmt">// Windows Forms HintMoveUp GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_DOWN, class=class="str">"cmt">// Windows Forms HintMoveDown GRAPH_ELEMENT_TYPE_WF_BAR_PROGRESS_BAR, class=class="str">"cmt">// Windows Forms BarProgressBar GRAPH_ELEMENT_TYPE_WF_GLARE_OBJ, class=class="str">"cmt">// Glare object }; enum ENUM_CANV_ELEMENT_VISUAL_EFF_STYLE { CANV_ELEMENT_VISUAL_EFF_STYLE_RECTANGLE, class=class="str">"cmt">// Rectangle CANV_ELEMENT_VISUAL_EFF_STYLE_PARALLELOGRAMM, class=class="str">"cmt">// Parallelogram }; MSG_FORM_OBJECT_TEXT_NO_SHADOW_OBJ_FIRST_CREATE_IT,class=class="str">"cmt">// No shadow object. Create it class="kw">using the CreateShadowObj() method MSG_FORM_OBJECT_TEXT_NO_GLARE_OBJ_FIRST_CREATE_IT, class=class="str">"cmt">// No glare object. We must first create it class="kw">using the CreateGlareObj() method MSG_FORM_OBJECT_ERR_FAILED_CREATE_SHADOW_OBJ, class=class="str">"cmt">// Failed to create new shadow object MSG_FORM_OBJECT_ERR_FAILED_CREATE_GLARE_OBJ, class=class="str">"cmt">// Failed to create new glare object
图形元素类型与窗体样式枚举的落地细节
在 MT5 自定义界面库里,图形元素被拆成 Element、Shadow object、Form、Window 以及 BarProgressBar / ProgressBar 控件等类型,每种都对应一条双语错误消息常量,例如 MSG_GRAPH_ELEMENT_TYPE_WF_GLARE_OBJ 专门指向高光对象(Glare object)。如果没先调用 CreateShadowObj() 或 CreateGlareObj() 就引用对应对象,运行时会直接返回「There is no shadow object」或「There is no glare object」这类英文报错。 窗体样式参数用 ENUM_FORM_STYLE_PARAMS 枚举管理,其中 FORM_STYLE_FRAME_GLARE_OPACITY 控制高光不透明度,FORM_STYLE_FRAME_SHADOW_OPACITY 管阴影不透明度,FORM_STYLE_FRAME_SHADOW_BLUR 决定阴影模糊半径,另有 X/Y 轴偏移与暗化色可供调。 开 MT5 把这段枚举抄进你的面板基类,先确认 CreateShadowObj / CreateGlareObj 的调用顺序,再逐个改 FORM_STYLE_FRAME_SHADOW_BLUR 的值,能直观看到阴影软硬变化。外汇与贵金属图表挂这类 UI 属高风险环境,参数异常可能让 EA 界面卡死,建议先在模拟账户验证。
enum ENUM_FORM_STYLE_PARAMS { class=class="str">"cmt">//--- CForm FORM_STYLE_FRAME_GLARE_OPACITY, class=class="str">"cmt">// Glare opacity FORM_STYLE_FRAME_SHADOW_OPACITY, class=class="str">"cmt">// Shadow opacity FORM_STYLE_FRAME_SHADOW_BLUR, class=class="str">"cmt">// Shadow blur FORM_STYLE_DARKENING_COLOR_FOR_SHADOW, class=class="str">"cmt">// Form shadow class="type">color darkening FORM_STYLE_FRAME_SHADOW_X_SHIFT, class=class="str">"cmt">// Shadow X axis shift FORM_STYLE_FRAME_SHADOW_Y_SHIFT, class=class="str">"cmt">// Shadow Y axis shift };