DoEasy. 控件 (第 26 部分): 完成 ToolTip(工具提示)WinForms 对象,并转移至 ProgressBar(进度条)开发(基础篇)
◍ 从 ToolTip 收尾切到 ProgressBar 控件
DoEasy 库这一轮先把 WinForms 风格的 ToolTip 对象收口,把悬停提示的命中区域与绘制逻辑定死,避免之后写进度条时还要回头补提示层。原文示例发布于 2023 年 3 月 3 日,累计浏览 1427 次,说明这类底层控件封装在中低频更新里仍有稳定检索需求。 下一阶段直接转进 ProgressBar 开发,意味着 ToolTip 的接口要先按可复用方式沉淀进库类,否则进度条在回测面板或实时盯盘 HUD 上挂提示会重复造轮子。MT5 上打开 DoEasy 库对应头文件,搜 ToolTip 相关方法确认是否已暴露 SetText/Show 即可验证这一步是否真收尾。 外汇与贵金属品种挂这类自绘控件时,注意点差跳变和报价冻结会让进度条数值瞬时失真,属高风险环境下的界面层干扰,验证时建议先用 EURUSD 模拟tick跑。
「给工具提示加上延迟与淡出的计时逻辑」
上一篇文章里,鼠标一悬停到对象上工具提示就立刻弹出,这跟 MS Visual Studio 编译出的程序行为不一致。本文要补上停顿和消失的节奏:悬停后短暂停顿,提示才平滑出现;光标移开立即隐藏;若停在对象上不动,提示过一阵也会自己淡掉。 为了驱动这套行为,需要给图形元素集合挂一个计时器。每个对象先定义一个虚拟的计时器事件处理程序,真正逻辑只在需要的子类里实现。同时维护一个“活动对象列表”,只装需要计时器处理的对象指针,循环只扫这个表,不必每次遍历全部图形元素——界面动态化和动画帧交替才有了基础。 今天先给工具提示控件写计时器处理:悬停停顿、平滑显隐都在集合事件处理程序和集合计时器里联动鼠标状态来操控。做完工具提示,下一篇再碰进度条,目前只建静态属性和外观,操作逻辑留到后面。 别把立即弹出当完成态:MT5 里若你之前抄过旧版 ToolTip 代码,把“悬停即显示”改成“列表+计时器延迟”才能接近桌面程序的体感,否则悬停闪烁会很碍眼。
给画布控件加上计时器与淡入淡出状态机
在 DoEasy 函数库里扩展画布图形元素,核心是先给集合级计时器补一套参数:暂停 16 毫秒、计数器步长 16、ID 10,专门驱动画布上的动画控件。原先图形对象集合的整数型属性总数从 129 扩到 138,新增字段覆盖动画状态、透明度步数等,方便存盘恢复。 工具提示(ToolTip)做成了有状态机的对象:正常隐藏、等待淡入、淡入中、等待淡出、淡出中。图形元素集合计时器每 16 毫秒跑一次,想让提示用约 1000 毫秒完成淡入,就得按 1000/16≈62.5 步去改不透明度,所以构造函数里依淡入时长和步长算出了透明度步数变量。 实测把对象显示命令挪到方法末尾、去掉整对象重绘后,透明度渐变时的闪烁明显减轻;但抗锯齿基元在极低不透明度下仍会对背景对比过度,补画布更新指示线也没彻底解决。 计时器处理程序逻辑很直接:每次到期先存常规状态,正常态就预置“等待淡入”并倒计时;下一轮看计数器到点就进淡入态,逐步加不透明度;全不透明后转“等待淡出”,对称地降透明度;完整周期结束不从处理列表删指针,而是由集合类看到结束态再摘指针、复位正常态。这样鼠标移开或移动时也能中途剔除。 顺带把阴影对象的高斯模糊方法和权重数组从私有挪到保护区,加了受保护构造函数,删掉参数化构造里“创建即绘制”的尾巴——否则空图表上会先冒阴影再画 GUI。进度条要用的眩光对象就从 ShadowObj 继承,复用模糊但自定义渲染。
class=class="str">"cmt">//--- Parameters of the graphical objects collection timer class="macro">#define COLLECTION_GRAPH_OBJ_PAUSE(class="num">250) class=class="str">"cmt">// Graphical objects collection timer pause in milliseconds class="macro">#define COLLECTION_GRAPH_OBJ_COUNTER_STEP(class="num">16) class=class="str">"cmt">// Graphical objects timer counter increment class="macro">#define COLLECTION_GRAPH_OBJ_COUNTER_ID(class="num">10) class=class="str">"cmt">// Graphical objects timer counter ID class=class="str">"cmt">//--- Parameters of the timer for the collection of graphical elements on canvas class="macro">#define COLLECTION_GRAPH_ELM_PAUSE(class="num">16) class=class="str">"cmt">// Graphical elements collection timer pause in milliseconds class="macro">#define COLLECTION_GRAPH_ELM_COUNTER_STEP(class="num">16) class=class="str">"cmt">// Graphical elements timer counter increment
◍ 控件配色与对象类型枚举的底层的写法
在 MT5 自定义图形库里,颜色常量和对象类型枚举通常集中放在头文件顶部,方便后续界面控件统一换肤。下面这段宏定义给出了提示框(Hint)与进度条(ProgressBar)的默认 RGB 配色:提示框背景 0xFF,0xFF,0xE1(浅黄)、边框 0x76,0x76,0x76(灰)、文字 0x5A,0x5A,0x5A(深灰);进度条背景 0xF0,0xF0,0xF0、边框 0xBC,0xBC,0xBC、文字 0x00,0x78,0xD7(蓝)、进度线 0x06,0xB0,0x25(绿)。 对象类型用 ENUM_OBJECT_DE_TYPE 枚举管理,起点不是 0 而是 COLLECTION_ID_LIST_END+1,说明它接在已有集合 ID 后面避免冲突。枚举里区分了纯图形(GBASE、GELEMENT、GFORM、GSHADOW、GGLARE 等)和仿 WinForms 控件(GWF_BASE、GWF_CONTAINER、GWF_COMMON、GWF_HELPER),共 14 个类型常量。 开 MT5 新建一个 include 头文件,把下面代码贴进去编译,就能在自家 EA 面板里直接引用这些配色与类型,改一处颜色全库联动。外汇与贵金属图表加载此类自定义库存在平台兼容性风险,建议先在策略测试器里验证。
class="macro">#define COLLECTION_GRAPH_ELM_COUNTER_ID(class="num">11) class=class="str">"cmt">// Graphical elements timer counter ID class=class="str">"cmt">//--- Collection list IDs class="macro">#define CLR_DEF_CONTROL_HINT_BACK_COLOR(C&class="macro">#x27;0xFF,0xFF,0xE1&class="macro">#x27;) class=class="str">"cmt">// Hint control background class="type">color class="macro">#define CLR_DEF_CONTROL_HINT_BORDER_COLOR(C&class="macro">#x27;0x76,0x76,0x76&class="macro">#x27;) class=class="str">"cmt">// Hint control frame class="type">color class="macro">#define CLR_DEF_CONTROL_HINT_FORE_COLOR(C&class="macro">#x27;0x5A,0x5A,0x5A&class="macro">#x27;) class=class="str">"cmt">// Hint control text class="type">color 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">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">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">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">color class=class="str">"cmt">//+------------------------------------------------+ class=class="str">"cmt">//| List of library object types | class=class="str">"cmt">//+------------------------------------------------+ enum ENUM_OBJECT_DE_TYPE { class=class="str">"cmt">//--- Graphics OBJECT_DE_TYPE_GBASE = COLLECTION_ID_LIST_END+class="num">1, class=class="str">"cmt">// "Base object of all library graphical objects" object type OBJECT_DE_TYPE_GELEMENT, class=class="str">"cmt">// "Graphical element" object type OBJECT_DE_TYPE_GFORM, class=class="str">"cmt">// Form object type OBJECT_DE_TYPE_GFORM_CONTROL, class=class="str">"cmt">// "Form for managing pivot points of graphical object" object type OBJECT_DE_TYPE_GSHADOW, class=class="str">"cmt">// Shadow object type OBJECT_DE_TYPE_GGLARE, class=class="str">"cmt">// Glare object type class=class="str">"cmt">//--- WinForms OBJECT_DE_TYPE_GWF_BASE, class=class="str">"cmt">// WinForms Base object type(base abstract WinForms object) OBJECT_DE_TYPE_GWF_CONTAINER, class=class="str">"cmt">// WinForms container object type OBJECT_DE_TYPE_GWF_COMMON, class=class="str">"cmt">// WinForms standard control object type OBJECT_DE_TYPE_GWF_HELPER, class=class="str">"cmt">// WinForms auxiliary control object type };
「图形元素类型的枚举分层」
在 MT5 自定义面板的底层架构里,图形元素不是一刀切的同类对象,而是用枚举 ENUM_GRAPH_ELEMENT_TYPE 做了明确分层。最基础的 GRAPH_ELEMENT_TYPE_STANDARD 到 GRAPH_ELEMENT_TYPE_WINDOW 覆盖了从标准对象、阴影、高光到窗体容器的原生层级。 往下看 WinForms 派生部分,类型号从 GRAPH_ELEMENT_TYPE_WF_UNDERLAY 一路排到 GRAPH_ELEMENT_TYPE_WF_TOOLTIP,一共定义了 22 个以 WF_ 开头的控件类型。这意味着你若要在 EA 里挂一个带 TabControl 和 CheckedListBox 的交互界面,底层对象类型早已预留好槽位,不必自己造轮子。 值得留意的是,容器类(CONTAINER / PANEL / GROUPBOX / TAB_CONTROL / SPLIT_CONTAINER)和标准控件类(COMMON_BASE 之后的 LABEL、BUTTON、CHECKBOX 等)在枚举里是被注释明确切开的。写 UI 框架时,用类型判断做命中测试,比靠对象名前缀硬匹配要稳得多。
class=class="str">"cmt">//--- Animation 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_GLARE_OBJ, class=class="str">"cmt">// Glare 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 GRAPH_ELEMENT_TYPE_WF_TOOLTIP class=class="str">"cmt">// Windows Forms ToolTip };
WinForms 控件与提示图标枚举的收尾定义
在图形元素类型枚举的末尾,除了常规的 WinForms 子构件,还补了两个进度条变体:GRAPH_ELEMENT_TYPE_WF_PROGRESS_BAR 与 GRAPH_ELEMENT_TYPE_WF_BAR_PROGRESS_BAR,前者是标准进度条,后者偏向条形填充样式,开 MT5 调 CCanvas 控件时这两个常量容易混淆。 紧随其后是 ENUM_CANV_ELEMENT_TOOLTIP_ICON,预定义了 5 个提示图标状态:NONE、INFO、WARNING、ERROR、USER,数值从 0 到 4 顺序排列,写自定义悬浮提示可直接按索引调用。 最后给出的 ENUM_CANV_ELEMENT_DISPLAY_STATE 以 CANV_ELEMENT_DISPLAY_STATE_NORMAL 打头,代表控件默认绘制状态,后续通常还会跟悬停、按下、禁用等枚举值,具体个数以你本地 Include 文件为准。 把这些枚举抄进 EA 的 GUI 模块,能在不依赖外部 DLL 的情况下,把面板状态机和提示逻辑一次对齐。
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 }; class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//+------------------------------------------------+ class=class="str">"cmt">//| The list of predefined icons | class=class="str">"cmt">//+------------------------------------------------+ enum ENUM_CANV_ELEMENT_TOOLTIP_ICON { CANV_ELEMENT_TOOLTIP_ICON_NONE, class=class="str">"cmt">// None CANV_ELEMENT_TOOLTIP_ICON_INFO, class=class="str">"cmt">// Info CANV_ELEMENT_TOOLTIP_ICON_WARNING, class=class="str">"cmt">// Warning CANV_ELEMENT_TOOLTIP_ICON_ERROR, class=class="str">"cmt">// Error CANV_ELEMENT_TOOLTIP_ICON_USER, class=class="str">"cmt">// User }; class=class="str">"cmt">//+------------------------------------------------+ class=class="str">"cmt">//| List of control display states | class=class="str">"cmt">//+------------------------------------------------+ enum ENUM_CANV_ELEMENT_DISPLAY_STATE { CANV_ELEMENT_DISPLAY_STATE_NORMAL, class=class="str">"cmt">// Normal