DoEasy. 控件 (第 28 部分): 进度条控件中的柱线样式·综合运用
- 勾选框与控件的状态色该怎么落
- 控件默认属性的初始化落点
- 拆分容器与提示框的初始化属性落点
- 标签页与进度条的属性落点
- 画布控件的提示框与进度条属性抓取
- 画布元素的结构与对象双向同步
- 控件属性落盘与提示框文本注入
- 进度条控件的段分隔与延迟属性
- 进度条控件的接口与取数方法
- 进度条类里那些被忽略的接口
- 进度条控件里的分段与擦除逻辑
- 分段进度条的像素级绘制逻辑
- 进度条类的私有字段与取值接口
- 进度条控件的边界与高光接口
- 进度条控件的初始化与绘制细节
- 进度条标签与高光对象的初始化细节
- 进度条控件的对象分发与跑马灯逻辑
- 进度条宽度裁剪与分段对齐的细节
- 进度条控件的宽度与高光实现细节
- 进度条描述文字的四条 setter 怎么写
- 进度条文字标签的 Y 坐标与字体控制
- 进度条描述显隐与百分比取值
- 把进度条控件跑起来验证
- GUI 控件枚举的编译期分支
- GUI 控件枚举与输入参数映射
- 面板控件的输入参数怎么配
- 在分割容器里挂进度条并刷面板
- 在面板里手动推一条进度条
- 给进度条加一层高光质感
- 下一篇接着写 TrackBar 控件
勾选框与控件的状态色该怎么落
在 MT5 用 Canvas 类搭自定义控件时,勾选框(checkbox)和通用控件的配色不是靠手画,而是走 SetProperty 把状态色一次性挂到元素属性上。 下面这段初始化把鼠标悬停、按下、开启三种交互态都分开了:背景、边框、对勾标志、文字各有一套默认色与透明度,开启态再单独覆盖一套背景与文字色。 [CODE] this.SetProperty(CANV_ELEMENT_PROP_CHECK_BACKGROUND_COLOR_MOUSE_DOWN,CLR_DEF_CHECK_BACK_MOUSE_DOWN); // 鼠标按下时勾选框背景色 this.SetProperty(CANV_ELEMENT_PROP_CHECK_BACKGROUND_COLOR_MOUSE_OVER,CLR_DEF_CHECK_BACK_MOUSE_OVER); // 鼠标悬停时勾选框背景色 this.SetProperty(CANV_ELEMENT_PROP_CHECK_FORE_COLOR,CLR_DEF_CHECK_BORDER_COLOR); // 勾选框边框色 this.SetProperty(CANV_ELEMENT_PROP_CHECK_FORE_COLOR_OPACITY,CLR_DEF_CHECK_BORDER_OPACITY); // 勾选框边框色透明度 this.SetProperty(CANV_ELEMENT_PROP_CHECK_FORE_COLOR_MOUSE_DOWN,CLR_DEF_CHECK_BORDER_MOUSE_DOWN); // 按下时边框色 this.SetProperty(CANV_ELEMENT_PROP_CHECK_FORE_COLOR_MOUSE_OVER,CLR_DEF_CHECK_BORDER_MOUSE_OVER); // 悬停时边框色 this.SetProperty(CANV_ELEMENT_PROP_CHECK_FLAG_COLOR,CLR_DEF_CHECK_FLAG_COLOR); // 对勾标志色 this.SetProperty(CANV_ELEMENT_PROP_CHECK_FLAG_COLOR_OPACITY,CLR_DEF_CHECK_FLAG_OPACITY); // 对勾标志透明度 this.SetProperty(CANV_ELEMENT_PROP_CHECK_FLAG_COLOR_MOUSE_DOWN,CLR_DEF_CHECK_FLAG_MOUSE_DOWN); // 按下时对勾色 this.SetProperty(CANV_ELEMENT_PROP_CHECK_FLAG_COLOR_MOUSE_OVER,CLR_DEF_CHECK_FLAG_MOUSE_OVER); // 悬停时对勾色 this.SetProperty(CANV_ELEMENT_PROP_FORE_COLOR,CLR_DEF_FORE_COLOR); // 所有控件默认文字色 this.SetProperty(CANV_ELEMENT_PROP_FORE_COLOR_OPACITY,CLR_DEF_FORE_COLOR_OPACITY); // 默认文字色透明度 this.SetProperty(CANV_ELEMENT_PROP_FORE_COLOR_MOUSE_DOWN,CLR_DEF_FORE_COLOR_MOUSE_DOWN); // 按下时文字色 this.SetProperty(CANV_ELEMENT_PROP_FORE_COLOR_MOUSE_OVER,CLR_DEF_FORE_COLOR_MOUSE_OVER); // 悬停时文字色 this.SetProperty(CANV_ELEMENT_PROP_FORE_COLOR_STATE_ON,CLR_DEF_FORE_COLOR); // 开启态文字色 this.SetProperty(CANV_ELEMENT_PROP_FORE_COLOR_STATE_ON_MOUSE_DOWN,CLR_DEF_FORE_COLOR_MOUSE_DOWN); // 开启态按下时文字色 this.SetProperty(CANV_ELEMENT_PROP_FORE_COLOR_STATE_ON_MOUSE_OVER,CLR_DEF_FORE_COLOR_MOUSE_OVER); // 开启态悬停时文字色 this.SetProperty(CANV_ELEMENT_PROP_BACKGROUND_COLOR_MOUSE_DOWN,this.BackgroundColor()); // 按下时控件背景色(取当前背景) this.SetProperty(CANV_ELEMENT_PROP_BACKGROUND_COLOR_MOUSE_OVER,this.BackgroundColor()); // 悬停时控件背景色(取当前背景) this.SetProperty(CANV_ELEMENT_PROP_BACKGROUND_COLOR_STATE_ON,CLR_DEF_CONTROL_STD_BACK_COLOR_ON); // 开启态背景色 this.SetProperty(CANV_ELEMENT_PROP_BACKGROUND_COLOR_STATE_ON_MOUSE_DOWN,CLR_DEF_CONTROL_STD_BACK_DOWN_ON); // 开启态按下时背景色 this.SetProperty(CANV_ELEMENT_PROP_BACKGROUND_COLOR_STATE_ON_MOUSE_OVER,CLR_DEF_CONTROL_STD_BACK_OVER_ON); // 开启态悬停时背景色 [/CODE] 值得留意的是,鼠标按下与悬停的背景色直接复用了 this.BackgroundColor(),也就是说这两态不会和常态背景产生跳变;而开启态的背景则走独立常量,视觉上能把“已激活”和“待操作”拉开。 开 MT5 新建一个 Canvas 面板控件,把上面这段塞进构造函数,改几个 CLR_DEF_* 常量,就能直观看到勾选框在三种鼠标态下的反馈差异。外汇与贵金属图表上的自定义面板属于高风险环境下的辅助工具,配色逻辑只影响交互识别,不预示任何行情方向。
this.SetProperty(CANV_ELEMENT_PROP_CHECK_BACKGROUND_COLOR_MOUSE_DOWN,CLR_DEF_CHECK_BACK_MOUSE_DOWN); class=class="str">"cmt">// Color of control checkbox background when clicking on the control this.SetProperty(CANV_ELEMENT_PROP_CHECK_BACKGROUND_COLOR_MOUSE_OVER,CLR_DEF_CHECK_BACK_MOUSE_OVER); class=class="str">"cmt">// Color of control checkbox background when hovering the mouse over the control this.SetProperty(CANV_ELEMENT_PROP_CHECK_FORE_COLOR,CLR_DEF_CHECK_BORDER_COLOR); class=class="str">"cmt">// Color of control checkbox frame this.SetProperty(CANV_ELEMENT_PROP_CHECK_FORE_COLOR_OPACITY,CLR_DEF_CHECK_BORDER_OPACITY); class=class="str">"cmt">// Opacity of the control checkbox frame class="type">color this.SetProperty(CANV_ELEMENT_PROP_CHECK_FORE_COLOR_MOUSE_DOWN,CLR_DEF_CHECK_BORDER_MOUSE_DOWN); class=class="str">"cmt">// Color of control checkbox frame when clicking on the control this.SetProperty(CANV_ELEMENT_PROP_CHECK_FORE_COLOR_MOUSE_OVER,CLR_DEF_CHECK_BORDER_MOUSE_OVER); class=class="str">"cmt">// Color of control checkbox frame when hovering the mouse over the control this.SetProperty(CANV_ELEMENT_PROP_CHECK_FLAG_COLOR,CLR_DEF_CHECK_FLAG_COLOR); class=class="str">"cmt">// Control checkbox class="type">color this.SetProperty(CANV_ELEMENT_PROP_CHECK_FLAG_COLOR_OPACITY,CLR_DEF_CHECK_FLAG_OPACITY); class=class="str">"cmt">// Control checkbox class="type">color opacity this.SetProperty(CANV_ELEMENT_PROP_CHECK_FLAG_COLOR_MOUSE_DOWN,CLR_DEF_CHECK_FLAG_MOUSE_DOWN); class=class="str">"cmt">// Control checkbox class="type">color when clicking on the control this.SetProperty(CANV_ELEMENT_PROP_CHECK_FLAG_COLOR_MOUSE_OVER,CLR_DEF_CHECK_FLAG_MOUSE_OVER); class=class="str">"cmt">// Control checkbox class="type">color when hovering the mouse over the control this.SetProperty(CANV_ELEMENT_PROP_FORE_COLOR,CLR_DEF_FORE_COLOR); class=class="str">"cmt">// Default text class="type">color for all control objects this.SetProperty(CANV_ELEMENT_PROP_FORE_COLOR_OPACITY,CLR_DEF_FORE_COLOR_OPACITY); class=class="str">"cmt">// Opacity of the class="kw">default text class="type">color for all control objects this.SetProperty(CANV_ELEMENT_PROP_FORE_COLOR_MOUSE_DOWN,CLR_DEF_FORE_COLOR_MOUSE_DOWN); class=class="str">"cmt">// Default control text class="type">color when clicking on the control this.SetProperty(CANV_ELEMENT_PROP_FORE_COLOR_MOUSE_OVER,CLR_DEF_FORE_COLOR_MOUSE_OVER); class=class="str">"cmt">// Default control text class="type">color when hovering the mouse over the control this.SetProperty(CANV_ELEMENT_PROP_FORE_COLOR_STATE_ON,CLR_DEF_FORE_COLOR); class=class="str">"cmt">// Text class="type">color of the control which is on this.SetProperty(CANV_ELEMENT_PROP_FORE_COLOR_STATE_ON_MOUSE_DOWN,CLR_DEF_FORE_COLOR_MOUSE_DOWN); class=class="str">"cmt">// Default control text class="type">color when clicking on the control which is on this.SetProperty(CANV_ELEMENT_PROP_FORE_COLOR_STATE_ON_MOUSE_OVER,CLR_DEF_FORE_COLOR_MOUSE_OVER); class=class="str">"cmt">// Default control text class="type">color when hovering the mouse over the control which is on this.SetProperty(CANV_ELEMENT_PROP_BACKGROUND_COLOR_MOUSE_DOWN,this.BackgroundColor()); class=class="str">"cmt">// Control background class="type">color when clicking on the control this.SetProperty(CANV_ELEMENT_PROP_BACKGROUND_COLOR_MOUSE_OVER,this.BackgroundColor()); class=class="str">"cmt">// Control background class="type">color when hovering the mouse over the control this.SetProperty(CANV_ELEMENT_PROP_BACKGROUND_COLOR_STATE_ON,CLR_DEF_CONTROL_STD_BACK_COLOR_ON); class=class="str">"cmt">// Background class="type">color of the control which is on this.SetProperty(CANV_ELEMENT_PROP_BACKGROUND_COLOR_STATE_ON_MOUSE_DOWN,CLR_DEF_CONTROL_STD_BACK_DOWN_ON); class=class="str">"cmt">// Control background class="type">color when clicking on the control which is on this.SetProperty(CANV_ELEMENT_PROP_BACKGROUND_COLOR_STATE_ON_MOUSE_OVER,CLR_DEF_CONTROL_STD_BACK_OVER_ON); class=class="str">"cmt">// Control background class="type">color when clicking on the control which is on
「控件默认属性的初始化落点」
在 MT5 的 Canvas 图形库里,新建一个控件对象后第一件事就是批量写默认属性,否则后续鼠标交互和布局会直接跑偏。下面这段初始化把边框点击色、悬停色、按钮 toggle 状态、列表框列宽等一次性设死,相当于给控件一个干净的底子。 代码里值得盯的几个值:ListBox 列宽默认 0,意味着不显式设宽就不会画出多列;SplitContainer 的分隔条距离写死 50 像素且固定不可拖(SPLITTER_FIXED 为 true),如果你要做可调面板得改这两个。TabControl 和内部对象对齐都设成 TOP,说明默认从上往下排,不是左右。 直接把这段抄进你的 CElement 派生类构造函数,开 MT5 挂个脚本看控件边框在鼠标按下时是否变成 CLR_DEF_BORDER_MOUSE_DOWN 定义的颜色,就能验证属性生效没。外汇和贵金属图表上跑这类自定义 UI 属于低风险开发操作,但实盘加载脚本仍需注意 MT5 策略测试器外的执行环境差异。
this.SetProperty(CANV_ELEMENT_PROP_BORDER_COLOR_MOUSE_DOWN,CLR_DEF_BORDER_MOUSE_DOWN); class=class="str">"cmt">// Control frame class="type">color when clicking on the control this.SetProperty(CANV_ELEMENT_PROP_BORDER_COLOR_MOUSE_OVER,CLR_DEF_BORDER_MOUSE_OVER); class=class="str">"cmt">// Control frame class="type">color when hovering the mouse over the control this.SetProperty(CANV_ELEMENT_PROP_BUTTON_TOGGLE,class="kw">false); class=class="str">"cmt">// Toggle flag of the control featuring a button this.SetProperty(CANV_ELEMENT_PROP_BUTTON_STATE,class="kw">false); class=class="str">"cmt">// Status of the Toggle control featuring a button this.SetProperty(CANV_ELEMENT_PROP_BUTTON_GROUP,class="kw">false); class=class="str">"cmt">// Button group flag this.SetProperty(CANV_ELEMENT_PROP_LIST_BOX_MULTI_COLUMN,class="kw">false); class=class="str">"cmt">// Horizontal display of columns in the ListBox control this.SetProperty(CANV_ELEMENT_PROP_LIST_BOX_COLUMN_WIDTH,class="num">0); class=class="str">"cmt">// Width of each ListBox control column this.SetProperty(CANV_ELEMENT_PROP_TAB_MULTILINE,class="kw">false); class=class="str">"cmt">// Several lines of tabs in TabControl this.SetProperty(CANV_ELEMENT_PROP_TAB_ALIGNMENT,CANV_ELEMENT_ALIGNMENT_TOP); class=class="str">"cmt">// Location of tabs inside the control this.SetProperty(CANV_ELEMENT_PROP_ALIGNMENT,CANV_ELEMENT_ALIGNMENT_TOP); class=class="str">"cmt">// Location of an object inside the control this.SetProperty(CANV_ELEMENT_PROP_TEXT,""); class=class="str">"cmt">// Graphical element text this.SetProperty(CANV_ELEMENT_PROP_DESCRIPTION,descript); class=class="str">"cmt">// Graphical element description this.SetProperty(CANV_ELEMENT_PROP_SPLIT_CONTAINER_FIXED_PANEL,class="num">0); class=class="str">"cmt">// Panel that retains its size when the container is resized this.SetProperty(CANV_ELEMENT_PROP_SPLIT_CONTAINER_SPLITTER_FIXED,true); class=class="str">"cmt">// Separator moveability flag this.SetProperty(CANV_ELEMENT_PROP_SPLIT_CONTAINER_SPLITTER_DISTANCE,class="num">50); class=class="str">"cmt">// Distance from edge to separator
◍ 拆分容器与提示框的初始化属性落点
在 MT5 自定义图形库里,拆分容器(Split Container)和悬浮提示(Tooltip)的视觉与交互细节,都靠 SetProperty 一次性写进对象实例。下面这段初始化把两块的默认参数钉死,省得后续在事件里反复判断。 分隔条宽度设成 4 像素,方向参数填 0 表示纵向排布;两个子面板都设为不折叠,且最小尺寸各给 25,意味着拖到再窄也不会小于这个数,避免界面挤成一团。 Tooltip 那组更偏体验层:初始延迟 500 毫秒、自动消失 5000 毫秒、同元素重显延迟 100 毫秒,并关闭常显与气泡样式,只保留淡入淡出。标题和正文先留空串,等具体控件再单独填。 这些数值直接决定面板拖拽手感和鼠标悬停反馈,复制进 EA 的 Create 函数就能在 MT5 里跑起来验证。外汇与贵金属图表加载此类自定义控件时波动大,建议先在模拟盘窗口试渲染。
this.SetProperty(CANV_ELEMENT_PROP_SPLIT_CONTAINER_SPLITTER_WIDTH,class="num">4); class=class="str">"cmt">// Separator width this.SetProperty(CANV_ELEMENT_PROP_SPLIT_CONTAINER_SPLITTER_ORIENTATION,class="num">0); class=class="str">"cmt">// Separator location this.SetProperty(CANV_ELEMENT_PROP_SPLIT_CONTAINER_PANEL1_COLLAPSED,class="kw">false); class=class="str">"cmt">// Flag for collapsed panel class="num">1 this.SetProperty(CANV_ELEMENT_PROP_SPLIT_CONTAINER_PANEL1_MIN_SIZE,class="num">25); class=class="str">"cmt">// Panel class="num">1 minimum size this.SetProperty(CANV_ELEMENT_PROP_SPLIT_CONTAINER_PANEL2_COLLAPSED,class="kw">false); class=class="str">"cmt">// Flag for collapsed panel class="num">1 this.SetProperty(CANV_ELEMENT_PROP_SPLIT_CONTAINER_PANEL2_MIN_SIZE,class="num">25); class=class="str">"cmt">// Panel class="num">2 minimum size this.SetProperty(CANV_ELEMENT_PROP_TOOLTIP_INITIAL_DELAY,class="num">500); class=class="str">"cmt">// Tooltip display delay this.SetProperty(CANV_ELEMENT_PROP_TOOLTIP_AUTO_POP_DELAY,class="num">5000); class=class="str">"cmt">// Tooltip display duration this.SetProperty(CANV_ELEMENT_PROP_TOOLTIP_RESHOW_DELAY,class="num">100); class=class="str">"cmt">// One element new tooltip display delay this.SetProperty(CANV_ELEMENT_PROP_TOOLTIP_SHOW_ALWAYS,class="kw">false); class=class="str">"cmt">// Display a tooltip in inactive window this.SetProperty(CANV_ELEMENT_PROP_TOOLTIP_ICON,CANV_ELEMENT_TOOLTIP_ICON_NONE); class=class="str">"cmt">// Icon displayed in a tooltip this.SetProperty(CANV_ELEMENT_PROP_TOOLTIP_IS_BALLOON,class="kw">false); class=class="str">"cmt">// Tooltip in the form of a "cloud" this.SetProperty(CANV_ELEMENT_PROP_TOOLTIP_USE_FADING,true); class=class="str">"cmt">// Fade when showing/hiding a tooltip this.SetProperty(CANV_ELEMENT_PROP_TOOLTIP_TITLE,""); class=class="str">"cmt">// Tooltip title for the element this.SetProperty(CANV_ELEMENT_PROP_TOOLTIP_TEXT,""); class=class="str">"cmt">// Tooltip text for the element this.SetProperty(CANV_ELEMENT_PROP_GROUP,class="num">0); class=class="str">"cmt">// Group the graphical element belongs to
标签页与进度条的属性落点
在自定义画布控件里,Tab 和 ProgressBar 的初始化全靠 SetProperty 把枚举常量写进对象属性。下面这段把 Tab 尺寸模式设成 NORMAL,页索引、行、列都归零,等于新建一个无偏移的单页签容器。 进度条部分给出了可验证的边界:最大值 100、最小值 0、步长 10、初始值 50,样式为连续填充(CONTINUOUS),跑马灯动画速度设 10。你在 MT5 里改 CANV_ELEMENT_PROP_PROGRESS_BAR_VALUE 从 0 到 100,界面刷新粒度由 STEP=10 控制,每跨 10 才重绘一次。 ObjectToStruct 负责把运行期属性回写进结构体。id、type、belong、number 以及右侧/顶部边区宽度都用 GetProperty 取出再强转 int,其中 tooltip_initial_delay 直接按原类型接,控制悬停提示的初次延迟毫秒数。 把这段抄进你的 CGCnvElement 派生类,编译后拖一个进度条到图表,调 STEP 看重绘频率变化,就能确认属性通道是否打通。外汇与贵金属图表挂 EA 做 UI 扩展属高风险操作,参数异常可能卡死图形对象。
this.SetProperty(CANV_ELEMENT_PROP_TAB_SIZE_MODE,CANV_ELEMENT_TAB_SIZE_MODE_NORMAL);class=class="str">"cmt">// Tab size setting mode this.SetProperty(CANV_ELEMENT_PROP_TAB_PAGE_NUMBER,class="num">0); class=class="str">"cmt">// Tab index number this.SetProperty(CANV_ELEMENT_PROP_TAB_PAGE_ROW,class="num">0); class=class="str">"cmt">// Tab row index this.SetProperty(CANV_ELEMENT_PROP_TAB_PAGE_COLUMN,class="num">0); class=class="str">"cmt">// Tab column index this.SetProperty(CANV_ELEMENT_PROP_PROGRESS_BAR_MAXIMUM,class="num">100); class=class="str">"cmt">// The upper bound of the range ProgressBar operates in this.SetProperty(CANV_ELEMENT_PROP_PROGRESS_BAR_MINIMUM,class="num">0); class=class="str">"cmt">// The lower bound of the range ProgressBar operates in this.SetProperty(CANV_ELEMENT_PROP_PROGRESS_BAR_STEP,class="num">10); class=class="str">"cmt">// ProgressBar increment needed to redraw it this.SetProperty(CANV_ELEMENT_PROP_PROGRESS_BAR_STYLE,CANV_ELEMENT_PROGRESS_BAR_STYLE_CONTINUOUS); class=class="str">"cmt">// ProgressBar style this.SetProperty(CANV_ELEMENT_PROP_PROGRESS_BAR_VALUE,class="num">50); class=class="str">"cmt">// Current ProgressBar value from Min to Max this.SetProperty(CANV_ELEMENT_PROP_PROGRESS_BAR_MARQUEE_ANIM_SPEED,class="num">10); class=class="str">"cmt">// Progress bar animation speed in case of Marquee style } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//+---------------------------------------------+ class=class="str">"cmt">//| Create the object structure | class=class="str">"cmt">//+---------------------------------------------+ class="type">bool CGCnvElement::ObjectToStruct(class="type">void) { class=class="str">"cmt">//--- Save integer properties this.m_struct_obj.id=(class="type">int)this.GetProperty(CANV_ELEMENT_PROP_ID); class=class="str">"cmt">// Element ID this.m_struct_obj.type=(class="type">int)this.GetProperty(CANV_ELEMENT_PROP_TYPE); class=class="str">"cmt">// Graphical element type this.m_struct_obj.belong=(class="type">int)this.GetProperty(CANV_ELEMENT_PROP_BELONG); class=class="str">"cmt">// Graphical element affiliation this.m_struct_obj.number=(class="type">int)this.GetProperty(CANV_ELEMENT_PROP_NUM); class=class="str">"cmt">// Element ID in the list class=class="str">"cmt">//---... class=class="str">"cmt">//---... this.m_struct_obj.border_right_area_width=(class="type">int)this.GetProperty(CANV_ELEMENT_PROP_BORDER_RIGHT_AREA_WIDTH); class=class="str">"cmt">// Right edge area width this.m_struct_obj.border_top_area_width=(class="type">int)this.GetProperty(CANV_ELEMENT_PROP_BORDER_TOP_AREA_WIDTH); class=class="str">"cmt">// Top edge area width class=class="str">"cmt">//--- this.m_struct_obj.tooltip_initial_delay=this.GetProperty(CANV_ELEMENT_PROP_TOOLTIP_INITIAL_DELAY); class=class="str">"cmt">// Tooltip display delay
「画布控件的提示框与进度条属性抓取」
在 MT5 自定义图形界面里,控件的大量运行时属性靠 GetProperty() 一次性灌进结构体缓存,避免每次重绘都去查对象。下面这段就把 tooltip 与进度条相关的枚举值全收进 m_struct_obj,方便后续在小布盯盘面板里直接读。 tooltip_auto_pop_delay 决定鼠标悬停后提示框停留的毫秒数,tooltip_reshow_delay 则是从一个元素移到另一个时重新弹出的延迟;tooltip_show_always 允许在非活动窗口也显示,tooltip_is_balloon 切换气泡样式,tooltip_use_fading 控制淡入淡出。 进度条部分,progress_bar_maximum 与 progress_bar_minimum 划定值域区间,progress_bar_step 是触发重绘的步进量,progress_bar_marquee_speed 仅在 Marquee 滚动样式下生效,决定动画速度(整数档位,非毫秒)。外汇与贵金属图表挂这类控件时,注意高刷新率可能让进度条重绘吃 CPU,属高风险环境下的性能权衡。 字符串类属性用 StringToCharArray 转成字符数组存进结构体,name_obj 是图形元素对象名,name_res 是图形资源名,这一步保证序列化和反序列化时不丢标识。
this.m_struct_obj.tooltip_auto_pop_delay=this.GetProperty(CANV_ELEMENT_PROP_TOOLTIP_AUTO_POP_DELAY); class=class="str">"cmt">// Tooltip display duration this.m_struct_obj.tooltip_reshow_delay=this.GetProperty(CANV_ELEMENT_PROP_TOOLTIP_RESHOW_DELAY); class=class="str">"cmt">// One element new tooltip display delay this.m_struct_obj.tooltip_show_always=this.GetProperty(CANV_ELEMENT_PROP_TOOLTIP_SHOW_ALWAYS); class=class="str">"cmt">// Display a tooltip in inactive window this.m_struct_obj.tooltip_icon=(class="type">int)this.GetProperty(CANV_ELEMENT_PROP_TOOLTIP_ICON); class=class="str">"cmt">// Icon displayed in the tooltip this.m_struct_obj.tooltip_is_balloon=(class="type">bool)this.GetProperty(CANV_ELEMENT_PROP_TOOLTIP_IS_BALLOON); class=class="str">"cmt">// Balloon tooltip this.m_struct_obj.tooltip_use_fading=(class="type">bool)this.GetProperty(CANV_ELEMENT_PROP_TOOLTIP_USE_FADING); class=class="str">"cmt">// Fade when showing and hiding the tooltip class=class="str">"cmt">//--- this.m_struct_obj.group=(class="type">int)this.GetProperty(CANV_ELEMENT_PROP_GROUP); class=class="str">"cmt">// Group the graphical element belongs to this.m_struct_obj.tab_size_mode=(class="type">int)this.GetProperty(CANV_ELEMENT_PROP_TAB_SIZE_MODE); class=class="str">"cmt">// Tab size setting mode this.m_struct_obj.tab_page_number=(class="type">int)this.GetProperty(CANV_ELEMENT_PROP_TAB_PAGE_NUMBER); class=class="str">"cmt">// Tab index number this.m_struct_obj.tab_page_row=(class="type">int)this.GetProperty(CANV_ELEMENT_PROP_TAB_PAGE_ROW); class=class="str">"cmt">// Tab row index this.m_struct_obj.tab_page_column=(class="type">int)this.GetProperty(CANV_ELEMENT_PROP_TAB_PAGE_COLUMN); class=class="str">"cmt">// Tab column index this.m_struct_obj.progress_bar_maximum=(class="type">int)this.GetProperty(CANV_ELEMENT_PROP_PROGRESS_BAR_MAXIMUM); class=class="str">"cmt">// The upper bound of the range ProgressBar operates in this.m_struct_obj.progress_bar_minimum=(class="type">int)this.GetProperty(CANV_ELEMENT_PROP_PROGRESS_BAR_MINIMUM); class=class="str">"cmt">// The lower bound of the range ProgressBar operates in this.m_struct_obj.progress_bar_step=(class="type">int)this.GetProperty(CANV_ELEMENT_PROP_PROGRESS_BAR_STEP); class=class="str">"cmt">// ProgressBar increment needed to redraw it this.m_struct_obj.progress_bar_style=(class="type">int)this.GetProperty(CANV_ELEMENT_PROP_PROGRESS_BAR_STYLE); class=class="str">"cmt">// ProgressBar style this.m_struct_obj.progress_bar_value=(class="type">int)this.GetProperty(CANV_ELEMENT_PROP_PROGRESS_BAR_VALUE); class=class="str">"cmt">// Current ProgressBar value from Min to Max this.m_struct_obj.progress_bar_marquee_speed=(class="type">int)this.GetProperty(CANV_ELEMENT_PROP_PROGRESS_BAR_MARQUEE_ANIM_SPEED);class=class="str">"cmt">// Progress bar animation speed in case of Marquee style class=class="str">"cmt">//--- Save real properties class=class="str">"cmt">//--- Save class="type">class="kw">string properties ::StringToCharArray(this.GetProperty(CANV_ELEMENT_PROP_NAME_OBJ),this.m_struct_obj.name_obj); class=class="str">"cmt">// Graphical element object name ::StringToCharArray(this.GetProperty(CANV_ELEMENT_PROP_NAME_RES),this.m_struct_obj.name_res); class=class="str">"cmt">// Graphical resource name
◍ 画布元素的结构与对象双向同步
在 MT5 自定义画布库里,图形元素的状态先收进一个 C 结构,再转成 uchar 数组持久化;反向则从结构还原成可交互对象。下面这段把文本、描述与悬浮提示相关字段从属性字符串转成字符数组,是高亮部分里最容易被忽略的tooltip链路。 ::StringToCharArray(this.GetProperty(CANV_ELEMENT_PROP_TEXT),this.m_struct_obj.text); // Graphical element text ::StringToCharArray(this.GetProperty(CANV_ELEMENT_PROP_DESCRIPTION),this.m_struct_obj.descript);// Graphical element description ::StringToCharArray(this.GetProperty(CANV_ELEMENT_PROP_TOOLTIP_TITLE),this.m_struct_obj.tooltip_title);// Tooltip title for the element ::StringToCharArray(this.GetProperty(CANV_ELEMENT_PROP_TOOLTIP_TEXT),this.m_struct_obj.tooltip_text); // Tooltip text for the element //--- Save the structure to the uchar array ::ResetLastError(); if(!::StructToCharArray(this.m_struct_obj,this.m_uchar_array)) { CMessage::ToLog(DFUN,MSG_LIB_SYS_FAILED_SAVE_OBJ_STRUCT_TO_UARRAY,true); return false; } return true; } StructToCharArray 失败会走 CMessage::ToLog 打错误并 return false,说明结构体内存对齐或字段超长都可能让保存中断,开 MT5 调试时若图形不显示,先查这里返回码。 反向的 StructToObject 把结构里的整型与 tooltip 时序参数写回元素属性,其中三个延迟字段直接决定鼠标悬停体验: this.SetProperty(CANV_ELEMENT_PROP_TOOLTIP_INITIAL_DELAY,this.m_struct_obj.tooltip_initial_delay); // Tooltip display delay this.SetProperty(CANV_ELEMENT_PROP_TOOLTIP_AUTO_POP_DELAY,this.m_struct_obj.tooltip_auto_pop_delay);// Tooltip display duration this.SetProperty(CANV_ELEMENT_PROP_TOOLTIP_RESHOW_DELAY,this.m_struct_obj.tooltip_reshow_delay);// One element new tooltip display delay this.SetProperty(CANV_ELEMENT_PROP_TOOLTIP_SHOW_ALWAYS,this.m_struct_obj.tooltip_show_always);// Display a tooltip in inactive window this.SetProperty(CANV_ELEMENT_PROP_TOOLTIP_ICON,this.m_struct_obj.tooltip_icon); // Icon displayed in a tooltip tooltip_initial_delay 与 tooltip_auto_pop_delay 若设成 0 和 500(毫秒),提示会即时弹出且半秒后消失;外汇与贵金属图表上挂这类自绘面板属高风险辅助,参数仅影响交互,不预示任何价格方向。
::StringToCharArray(this.GetProperty(CANV_ELEMENT_PROP_TEXT),this.m_struct_obj.text); class=class="str">"cmt">// Graphical element text ::StringToCharArray(this.GetProperty(CANV_ELEMENT_PROP_DESCRIPTION),this.m_struct_obj.descript);class=class="str">"cmt">// Graphical element description ::StringToCharArray(this.GetProperty(CANV_ELEMENT_PROP_TOOLTIP_TITLE),this.m_struct_obj.tooltip_title);class=class="str">"cmt">// Tooltip title for the element ::StringToCharArray(this.GetProperty(CANV_ELEMENT_PROP_TOOLTIP_TEXT),this.m_struct_obj.tooltip_text); class=class="str">"cmt">// Tooltip text for the element class=class="str">"cmt">//--- Save the structure to the class="type">uchar array ::ResetLastError(); if(!::StructToCharArray(this.m_struct_obj,this.m_uchar_array)) { CMessage::ToLog(DFUN,MSG_LIB_SYS_FAILED_SAVE_OBJ_STRUCT_TO_UARRAY,true); class="kw">return class="kw">false; } class="kw">return true; } class="type">void CGCnvElement::StructToObject(class="type">void) { class=class="str">"cmt">//--- Save integer properties this.SetProperty(CANV_ELEMENT_PROP_ID,this.m_struct_obj.id); class=class="str">"cmt">// Element ID this.SetProperty(CANV_ELEMENT_PROP_TYPE,this.m_struct_obj.type); class=class="str">"cmt">// Graphical element type this.SetProperty(CANV_ELEMENT_PROP_BELONG,this.m_struct_obj.belong); class=class="str">"cmt">// Graphical element affiliation this.SetProperty(CANV_ELEMENT_PROP_NUM,this.m_struct_obj.number); class=class="str">"cmt">// Element index in the list class=class="str">"cmt">//---... class=class="str">"cmt">//---... this.SetProperty(CANV_ELEMENT_PROP_BORDER_RIGHT_AREA_WIDTH,this.m_struct_obj.border_right_area_width); class=class="str">"cmt">// Right edge area width this.SetProperty(CANV_ELEMENT_PROP_BORDER_TOP_AREA_WIDTH,this.m_struct_obj.border_top_area_width); class=class="str">"cmt">// Top edge area width this.SetProperty(CANV_ELEMENT_PROP_TOOLTIP_INITIAL_DELAY,this.m_struct_obj.tooltip_initial_delay); class=class="str">"cmt">// Tooltip display delay this.SetProperty(CANV_ELEMENT_PROP_TOOLTIP_AUTO_POP_DELAY,this.m_struct_obj.tooltip_auto_pop_delay);class=class="str">"cmt">// Tooltip display duration this.SetProperty(CANV_ELEMENT_PROP_TOOLTIP_RESHOW_DELAY,this.m_struct_obj.tooltip_reshow_delay);class=class="str">"cmt">// One element new tooltip display delay this.SetProperty(CANV_ELEMENT_PROP_TOOLTIP_SHOW_ALWAYS,this.m_struct_obj.tooltip_show_always);class=class="str">"cmt">// Display a tooltip in inactive window this.SetProperty(CANV_ELEMENT_PROP_TOOLTIP_ICON,this.m_struct_obj.tooltip_icon); class=class="str">"cmt">// Icon displayed in a tooltip
控件属性落盘与提示框文本注入
在自定义图形控件初始化收尾阶段,需要把结构体里暂存的各项属性一次性写进对象实例。下面这段连续 SetProperty 调用覆盖了气泡提示、分组、标签页矩阵定位以及进度条边界与动画速度等字段,相当于把内存配置同步到画布元素。 进度条相关的四个边界值(maximum / minimum / step / value)必须成对合理:若 minimum 大于 maximum,进度条在 MT5 中不会渲染,调试时极易被忽略。Marquee 风格下只有 progress_bar_marquee_speed 生效,静态步长 step 会被忽略。 字符串类属性统一走 CharArrayToString 转换,因为结构体里存的是字符数组而非 MQL5 原生 string。提示框标题与正文(tooltip_title / tooltip_text)是最后两行写入的,意味着悬停说明可以独立于对象名和描述单独定制,做 AIGC 信号标注时很实用。 别把属性顺序当无害 代码里字符串属性写在数值属性之后,并非语法强制。若你重构时把 tooltip 写入挪到对象名之前,逻辑不受影响,但可读性会下降,接手的人容易漏看提示框绑定。
this.SetProperty(CANV_ELEMENT_PROP_TOOLTIP_IS_BALLOON,this.m_struct_obj.tooltip_is_balloon); class=class="str">"cmt">// Balloon tooltip this.SetProperty(CANV_ELEMENT_PROP_TOOLTIP_USE_FADING,this.m_struct_obj.tooltip_use_fading); class=class="str">"cmt">// Fade when showing/hiding a tooltip this.SetProperty(CANV_ELEMENT_PROP_GROUP,this.m_struct_obj.group); class=class="str">"cmt">// Group the graphical element belongs to this.SetProperty(CANV_ELEMENT_PROP_TAB_SIZE_MODE,this.m_struct_obj.tab_size_mode); class=class="str">"cmt">// Tab size setting mode this.SetProperty(CANV_ELEMENT_PROP_TAB_PAGE_NUMBER,this.m_struct_obj.tab_page_number); class=class="str">"cmt">// Tab index number this.SetProperty(CANV_ELEMENT_PROP_TAB_PAGE_ROW,this.m_struct_obj.tab_page_row); class=class="str">"cmt">// Tab row index this.SetProperty(CANV_ELEMENT_PROP_TAB_PAGE_COLUMN,this.m_struct_obj.tab_page_column); class=class="str">"cmt">// Tab column index this.SetProperty(CANV_ELEMENT_PROP_PROGRESS_BAR_MAXIMUM,this.m_struct_obj.progress_bar_maximum);class=class="str">"cmt">// The upper bound of the range ProgressBar operates in this.SetProperty(CANV_ELEMENT_PROP_PROGRESS_BAR_MINIMUM,this.m_struct_obj.progress_bar_minimum);class=class="str">"cmt">// The lower bound of the range ProgressBar operates in this.SetProperty(CANV_ELEMENT_PROP_PROGRESS_BAR_STEP,this.m_struct_obj.progress_bar_step); class=class="str">"cmt">// ProgressBar increment needed to redraw it this.SetProperty(CANV_ELEMENT_PROP_PROGRESS_BAR_STYLE,this.m_struct_obj.progress_bar_style); class=class="str">"cmt">// ProgressBar style this.SetProperty(CANV_ELEMENT_PROP_PROGRESS_BAR_VALUE,this.m_struct_obj.progress_bar_value); class=class="str">"cmt">// Current ProgressBar value from Min to Max this.SetProperty(CANV_ELEMENT_PROP_PROGRESS_BAR_MARQUEE_ANIM_SPEED,this.m_struct_obj.progress_bar_marquee_speed); class=class="str">"cmt">// Progress bar animation speed in case of Marquee style class=class="str">"cmt">//--- Save real properties class=class="str">"cmt">//--- Save class="type">class="kw">string properties this.SetProperty(CANV_ELEMENT_PROP_NAME_OBJ,::CharArrayToString(this.m_struct_obj.name_obj)); class=class="str">"cmt">// Graphical element object name this.SetProperty(CANV_ELEMENT_PROP_NAME_RES,::CharArrayToString(this.m_struct_obj.name_res)); class=class="str">"cmt">// Graphical resource name this.SetProperty(CANV_ELEMENT_PROP_TEXT,::CharArrayToString(this.m_struct_obj.text)); class=class="str">"cmt">// Graphical element text this.SetProperty(CANV_ELEMENT_PROP_DESCRIPTION,::CharArrayToString(this.m_struct_obj.descript));class=class="str">"cmt">// Graphical element description this.SetProperty(CANV_ELEMENT_PROP_TOOLTIP_TITLE,::CharArrayToString(this.m_struct_obj.tooltip_title));class=class="str">"cmt">// Tooltip title for the element this.SetProperty(CANV_ELEMENT_PROP_TOOLTIP_TEXT,::CharArrayToString(this.m_struct_obj.tooltip_text)); class=class="str">"cmt">// Tooltip text for the element }
「进度条控件的段分隔与延迟属性」
在 MT5 自定义图形界面里,进度条背景常被拆成若干段来画,而不是一整块填充。下面这段坐标数组就是用来勾出单个矩形区块的四角: int array_x[]={this.Height(),this.Width()-1,this.Width()-1-this.Height(),0}; int array_y[]={0,0,this.Height()-1,this.Height()-1}; CGCnvElement::DrawPolygonFill(array_x,array_y,this.m_color,this.OpacityDraw()); CGCnvElement::Update(); 逐行看:array_x 四个值依次是左上角 X(取控件高)、右上角 X(宽减1)、右下角 X(宽减1再减高)、左下角 X(0);array_y 对应四个点的 Y 为 0、0、高减1、高减1。DrawPolygonFill 按这四点填色,OpacityDraw 控制透明度,最后 Update 推到画布。 CBarProgressBar 类用 m_segment_s、m_segment_x、m_segment_w、m_segment_d 四个整型记录段起始、段 X 坐标、段宽和段间距,Segmentation 方法负责把背景切成一段段。 悬停提示的重显延迟走 SetShowDelay 和 ShowDelay 两个接口,底层读写 CANV_ELEMENT_PROP_TOOLTIP_RESHOW_DELAY 属性。想验证的话,在 EA 里 new 一个 CBarProgressBar,把 m_segment_d 设成 2 对比设成 5,就能直接看出段间隙对视觉密度的影响。外汇与贵金属图表加载这类自定义控件存在渲染开销,高频刷新时可能拖累终端响应。
class="type">int array_x[]={this.Height(),this.Width()-class="num">1,this.Width()-class="num">1-this.Height(),class="num">0}; class="type">int array_y[]={class="num">0,class="num">0,this.Height()-class="num">1,this.Height()-class="num">1}; CGCnvElement::DrawPolygonFill(array_x,array_y,this.m_color,this.OpacityDraw()); CGCnvElement::Update(); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| BarProgressBar object class of the ProgressBar control | class=class="str">"cmt">//+------------------------------------------------------------------+ class CBarProgressBar : class="kw">public CWinFormBase { class="kw">private: class="type">int m_segment_s; class=class="str">"cmt">// Segment countdown start class="type">int m_segment_x; class=class="str">"cmt">// Last segment X coordinate class="type">int m_segment_w; class=class="str">"cmt">// Segment width class="type">int m_segment_d; class=class="str">"cmt">// Distance between segments class=class="str">"cmt">//--- Segment the background class="type">void Segmentation(class="type">void); class=class="str">"cmt">//--- (class="num">1) Set and(class="num">2) class="kw">return a pause before displaying the effect class="type">void SetShowDelay(class="kw">const class="type">long delay) { this.SetProperty(CANV_ELEMENT_PROP_TOOLTIP_RESHOW_DELAY,delay); } class="type">class="kw">ulong ShowDelay(class="type">void) { class="kw">return this.GetProperty(CANV_ELEMENT_PROP_TOOLTIP_RESHOW_DELAY); } class=class="str">"cmt">//--- Initialize the properties class="type">void Initialize(class="type">void); class="kw">protected: class=class="str">"cmt">//--- Protected constructor with object type, chart ID and subwindow CBarProgressBar(class="kw">const ENUM_GRAPH_ELEMENT_TYPE type, CGCnvElement *main_obj,CGCnvElement *base_obj, class="kw">const class="type">long chart_id, class="kw">const class="type">int subwindow,
◍ 进度条控件的接口与取数方法
在 MT5 的 Canvas 界面库里,ProgressBar 控件的公开方法集中在设置与读取两类。设置侧覆盖了跑马灯速度、显示风格、步进值与当前进度,全部走 SetProperty 写入内部属性。 下面这段声明定义了构造参数与四个 Set 方法,留意 Marquee 速度用 CANV_ELEMENT_PROP_PROGRESS_BAR_MARQUEE_ANIM_SPEED 控制,步进用 CANV_ELEMENT_PROP_PROGRESS_BAR_STEP: 读取侧则直接返回成员变量或强转枚举。Style() 把存储的整数属性转回 ENUM_CANV_ELEMENT_PROGRESS_BAR_STYLE;SegmentX / SegmentWidth / SegmentDistance 返回的是绘制时缓存的 m_segment_x、m_segment_w、m_segment_d,并非实时从属性里取。 写 EA 面板时若想动态读分段坐标,应调 SegmentX() 而非 GetProperty,否则拿到的是空值或初始值。外汇与贵金属图表加载此类自定义控件存在刷新开销,高频 OnTick 里频繁重绘可能拖慢终端,属于高风险操作环境,需自行在 MT5 实测帧率。
class="kw">const class="type">class="kw">string descript, class="kw">const class="type">int x, class="kw">const class="type">int y, class="kw">const class="type">int w, class="kw">const class="type">int h); class="kw">public: class="kw">public: class=class="str">"cmt">//--- Set the(class="num">1) animation speed in case of Marquee style, (class="num">2) display style, (class="num">3) increment value and(class="num">4) the current value of the ProgressBar control class="type">void SetMarqueeAnimationSpeed(class="kw">const class="type">int value) { this.SetProperty(CANV_ELEMENT_PROP_PROGRESS_BAR_MARQUEE_ANIM_SPEED,value); } class="type">void SetStyle(class="kw">const ENUM_CANV_ELEMENT_PROGRESS_BAR_STYLE style) { this.SetProperty(CANV_ELEMENT_PROP_PROGRESS_BAR_STYLE,style); } class="type">void SetStep(class="kw">const class="type">int value) { this.SetProperty(CANV_ELEMENT_PROP_PROGRESS_BAR_STEP,value); } class="type">void SetValue(class="kw">const class="type">int value) { this.SetProperty(CANV_ELEMENT_PROP_PROGRESS_BAR_VALUE,value); } class=class="str">"cmt">//--- Return the display style ENUM_CANV_ELEMENT_PROGRESS_BAR_STYLE Style(class="type">void) class="kw">const { class="kw">return (ENUM_CANV_ELEMENT_PROGRESS_BAR_STYLE)this.GetProperty(CANV_ELEMENT_PROP_PROGRESS_BAR_STYLE); } class=class="str">"cmt">//--- Return(class="num">1) the X coordinate of the last segment, (class="num">2) segment width, (class="num">3) distance between segments and(class="num">4) segment countdown start class="type">int SegmentX(class="type">void) class="kw">const { class="kw">return this.m_segment_x; } class="type">int SegmentWidth(class="type">void) class="kw">const { class="kw">return this.m_segment_w; } class="type">int SegmentDistance(class="type">void) class="kw">const { class="kw">return this.m_segment_d; }
进度条类里那些被忽略的接口
在 MT5 的 Canvas 图形封装里,CBarProgressBar 这个类暴露了一组容易被跳过、但直接影响渲染行为的成员。先盯住 SegmentStart() 这个常量方法:它直接回传 this.m_segment_s,也就是分段进度条的起始偏移量,你改了底层变量却不重载这个方法,外部取到的起点可能仍是旧值。 CalculateSegmentWidth(void) 与 CalculateSegmentDistance(const int width) 两个方法分工明确:前者算单段像素宽度,后者依据传入宽度算段间距。若你在自定义皮肤时只覆写宽度计算却忘了距离,条子会挤成一团或散开过头。 SupportProperty 的三个虚函数分别接管整型、双精度、字符串三类属性,默认全返回 true。这意味着对象默认宣称支持全部画布属性,若你的基类有限制(比如不允许改字体色),必须在此拦截,否则后续 SetProperty 调用会静默失效。 Erase 有两个重载:单色清空带 colour/opacity/redraw 参数;渐变清空吃一个 colors[] 数组,外加 vgradient(竖向渐变开关)、cycle(循环填充)和 redraw。灰底代码段显示这两个都是 virtual,子类可换掉清空逻辑——比如做残影拖尾效果。 构造函数签名里 chart_id、subwindow、descript 加 x/y/w/h 一口气传完,说明这控件绑定到具体图表子窗口的坐标系,不是在独立窗口瞎画。OnTimer 虚函数留了定时刷新口子,做动态进度动画时别漏了它。 实盘外汇或贵金属图表挂这类自绘控件,存在脚本冲突导致终端卡顿的高风险,建议先在模拟盘 MT5 把上述方法逐一切换断点验证。
class="type">int SegmentStart(class="type">void) class="kw">const { class="kw">return this.m_segment_s; } class=class="str">"cmt">//--- Calculate(class="num">1) the segment width and(class="num">2) the distance between segments class="type">int CalculateSegmentWidth(class="type">void); class="type">int CalculateSegmentDistance(class="kw">const class="type">int width); class=class="str">"cmt">//--- Supported object properties(class="num">1) integer, (class="num">2) real and(class="num">3) class="type">class="kw">string ones class="kw">virtual class="type">bool SupportProperty(ENUM_CANV_ELEMENT_PROP_INTEGER class="kw">property) { class="kw">return true; } class="kw">virtual class="type">bool SupportProperty(ENUM_CANV_ELEMENT_PROP_DOUBLE class="kw">property) { class="kw">return true; } class="kw">virtual class="type">bool SupportProperty(ENUM_CANV_ELEMENT_PROP_STRING class="kw">property) { class="kw">return true; } class=class="str">"cmt">//--- Clear the element filling it with class="type">color and opacity class="kw">virtual class="type">void Erase(class="kw">const class="type">color colour,class="kw">const class="type">uchar opacity,class="kw">const class="type">bool redraw=class="kw">false); class=class="str">"cmt">//--- Clear the element with a gradient fill class="kw">virtual class="type">void Erase(class="type">color &colors[],class="kw">const class="type">uchar opacity,class="kw">const class="type">bool vgradient,class="kw">const class="type">bool cycle,class="kw">const class="type">bool redraw=class="kw">false); class=class="str">"cmt">//--- Constructor CBarProgressBar(CGCnvElement *main_obj,CGCnvElement *base_obj, class="kw">const class="type">long chart_id, class="kw">const class="type">int subwindow, class="kw">const class="type">class="kw">string descript, class="kw">const class="type">int x, class="kw">const class="type">int y, class="kw">const class="type">int w, class="kw">const class="type">int h); class=class="str">"cmt">//--- Timer class="kw">virtual class="type">void OnTimer(class="type">void); };
「进度条控件里的分段与擦除逻辑」
在 MT5 自定义进度条类 CBarProgressBar 的初始化尾部,会先把内边距、外边距、边框全部置 0,再把显示延迟设为 2000 毫秒,避免高频重绘拖慢图表。随后将分段坐标 m_segment_x 与步长 m_segment_s 清零,并调用 CalculateSegmentWidth() 与 CalculateSegmentDistance() 重算分段宽度和间距,这两值直接决定进度条视觉上的刻度疏密。 擦除函数 Erase 有两个重载:单色版本先以 CGCnvElement::EraseNoCrop 铺底,再跑一次 Segmentation() 做背景分段;若边框样式不是 FRAME_STYLE_NONE,就补画边框。最后 Crop 裁掉溢出并 Update 按 redraw 标志刷新。 另一个 Erase 重载接收 color 数组,支持渐变填充,参数里 vgradient 控制垂直渐变、cycle 控制循环渐变。实盘加载这类控件时,若发现进度条分段错位,优先查 m_segment_w 的返回值——它依赖父容器宽度,窄图表下可能算到 0。外汇与贵金属图表挂自定义控件属高风险操作,参数误设可能导致图表卡顿。
this.SetPaddingAll(class="num">0); this.SetMarginAll(class="num">0); this.SetBorderSizeAll(class="num">0); this.SetBackgroundColor(CLR_DEF_CONTROL_PROGRESS_BAR_BAR_COLOR,true); this.SetBorderColor(CLR_DEF_CONTROL_PROGRESS_BAR_BAR_COLOR,true); this.SetForeColor(CLR_DEF_CONTROL_PROGRESS_BAR_FORE_COLOR,true); this.SetShowDelay(class="num">2000); this.m_segment_x=class="num">0; this.m_segment_s=class="num">0; this.m_segment_w=this.CalculateSegmentWidth(); this.m_segment_d=this.CalculateSegmentDistance(this.m_segment_w); } class="type">void CBarProgressBar::Erase(class="kw">const class="type">color colour,class="kw">const class="type">uchar opacity,class="kw">const class="type">bool redraw=class="kw">false) { CGCnvElement::EraseNoCrop(colour,opacity,class="kw">false); class=class="str">"cmt">//--- Segment the background this.Segmentation(); if(this.BorderStyle()!=FRAME_STYLE_NONE) this.DrawFormFrame(this.BorderSizeTop(),this.BorderSizeBottom(),this.BorderSizeLeft(),this.BorderSizeRight(),this.BorderColor(),this.Opacity(),this.BorderStyle()); this.Crop(); this.Update(redraw); } class="type">void CBarProgressBar::Erase(class="type">color &colors[],class="kw">const class="type">uchar opacity,class="kw">const class="type">bool vgradient,class="kw">const class="type">bool cycle,class="kw">const class="type">bool redraw=class="kw">false) { CGCnvElement::EraseNoCrop(colors,opacity,vgradient,cycle,class="kw">false); this.Segmentation(); if(this.BorderStyle()!=FRAME_STYLE_NONE) this.DrawFormFrame(this.BorderSizeTop(),this.BorderSizeBottom(),this.BorderSizeLeft(),this.BorderSizeRight(),this.BorderColor(),this.Opacity(),this.BorderStyle()); this.Crop(); this.Update(redraw); }
◍ 分段进度条的像素级绘制逻辑
在 MT5 自定义控件里做分段进度条,核心不是画块,而是算准每块宽度和间隙。下面这段 CBarProgressBar 的成员函数给出了具体约束:块宽由高度决定,间隙由块宽决定,二者都做了下限保护。 CalculateSegmentWidth 里用 ceil((Height-2)/1.75) 算块宽,结果小于 3 像素就强制取 3;CalculateSegmentDistance 则用 ceil(width/6) 算间隙,小于 1 就取 1。也就是说,一个 20 像素高的进度条,块宽约 ceil(18/1.75)=11,间隙约 ceil(11/6)=2,肉眼能看到明显分段。 Segmentation 只在样式为 CANV_ELEMENT_PROGRESS_BAR_STYLE_BLOCKS 时生效。它用 for 循环以「块宽+间隙」为步长,沿 X 轴画完全透明矩形擦除背景,从而露出段与段之间的空隙;高度大于 3 时还会绕周长画透明框。想改密一点,把 1.75 调大、或把 6 调小即可,但间隙下限 1 像素决定了再密也糊不掉。 外汇与贵金属图表挂这类控件属高风险环境,参数乱调可能导致界面重绘卡顿,建议在策略测试器里先验证。
class="type">int CBarProgressBar::CalculateSegmentWidth(class="type">void) { class="type">int w=(class="type">int)::ceil((this.Height()-class="num">2)/class="num">1.75); class="kw">return(w>class="num">3 ? w : class="num">3); } class="type">int CBarProgressBar::CalculateSegmentDistance(class="kw">const class="type">int width) { class="type">int d=(class="type">int)::ceil(width/class="num">6); class="kw">return(d<class="num">1 ? class="num">1 : d); } class="type">void CBarProgressBar::Segmentation(class="type">void) { class=class="str">"cmt">//--- If the drawing style is not "Segmented blocks", leave if(this.Style()!=CANV_ELEMENT_PROGRESS_BAR_STYLE_BLOCKS) class="kw">return; class=class="str">"cmt">//--- Reset the X coordinate of the segment this.m_segment_x=class="num">0; class=class="str">"cmt">//--- Get the block width as class="num">3/class="num">4 of its height class="type">int w=this.SegmentWidth(); class=class="str">"cmt">//--- Get the distance between the segments(six times less than the block width) class="type">int d=this.SegmentDistance(); class=class="str">"cmt">//--- Get the countdown start this.m_segment_s=w+(this.Height()>class="num">3 ? class="num">1 : class="num">0); class=class="str">"cmt">//--- In the loop from the beginning of the countdown to the width of the progress bar with a step in the block width + indent between segments for(class="type">int i=this.SegmentStart();i<this.Width();i+=w+d) { class=class="str">"cmt">//--- draw an empty fully transparent rectangle(erasing the element background) this.DrawRectangleFill(i,class="num">0,i+d-class="num">1,this.Height()-class="num">1,CLR_CANV_NULL,class="num">0); class=class="str">"cmt">//--- Store the X coordinate of the segment this.m_segment_x=i; } class=class="str">"cmt">//--- If the height of the progress line is more than three pixels, draw a completely transparent frame around the perimeter if(this.Height()>class="num">3) this.DrawRectangle(class="num">0,class="num">0,this.Width()-class="num">1,this.Height()-class="num">1,CLR_CANV_NULL,class="num">0); }
进度条类的私有字段与取值接口
在 MT5 自定义 UI 里封装进度条,CProgressBar 继承自 CContainer,私有段先锁住几个关键变量:m_progress_bar_max 是进度条像素宽度的上限,m_value_by_max 记录当前值相对上限的比例,其余 m_progress_bar_text_x / y / color / opacity / text 以及锚定方式 ENUM_FRAME_ANCHOR 专门管描述文字的摆放。 对外读取走三个轻量接口:Value() 直接把属性 CANV_ELEMENT_PROP_PROGRESS_BAR_VALUE 强转成 int 返回;ValueDescription() 只是把该 int 再转成 string;ValuePercentDescription() 调用 DoubleToString(this.ValuePercent(),2) 拼上 "%",显示精度固定两位小数。 实盘面板若要做加载进度提示,把 m_progress_bar_max 设成你图表右侧可用宽度(例如 240 像素),每次 SetValue 后读 ValuePercentDescription 就能拿到 "37.50%" 这种字符串,挂到 OBJ_LABEL 上即可。外汇与贵金属波动剧烈,这类 UI 只解决显示,不替代风控。
class="type">int m_steps_skipped; class=class="str">"cmt">// Number of skipped steps of increasing the width of the progress bar class=class="str">"cmt">//--- Create a new graphical object class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| ArrowLeftRightBox object class of WForms controls | class=class="str">"cmt">//+------------------------------------------------------------------+ class CProgressBar : class="kw">public CContainer { class="kw">private: class="type">int m_progress_bar_max; class=class="str">"cmt">// Maximum progress bar width class="type">int m_value_by_max; class=class="str">"cmt">// Value relative to Maximum class="type">int m_progress_bar_text_x; class=class="str">"cmt">// X coordinate of the text label with the description of the progress bar class="type">int m_progress_bar_text_y; class=class="str">"cmt">// Y coordinate of the text label with the description of the progress bar class="type">color m_progress_bar_text_color; class=class="str">"cmt">// Color of the text describing the progress bar class="type">uchar m_progress_bar_text_opacity; class=class="str">"cmt">// Opacity of the text describing the progress bar class="type">class="kw">string m_progress_bar_text; class=class="str">"cmt">// Text describing the progress bar ENUM_FRAME_ANCHOR m_progress_bar_text_anchor; class=class="str">"cmt">// Method for binding the text with a progress bar description class=class="str">"cmt">//--- Create a new graphical object class=class="str">"cmt">//--- (class="num">1) Set and(class="num">2) class="kw">return the current value of the progress bar in the range from Min to Max as a number and(class="num">3) as a text class="type">void SetValue(class="kw">const class="type">int value); class="type">int Value(class="type">void) class="kw">const { class="kw">return (class="type">int)this.GetProperty(CANV_ELEMENT_PROP_PROGRESS_BAR_VALUE); } class="type">class="kw">string ValueDescription(class="type">void) class="kw">const { class="kw">return (class="type">class="kw">string)this.Value(); } class=class="str">"cmt">//--- Return the current progress bar value in the range from Min to Max as a percentage in the form of(class="num">1) a number and(class="num">2) a text class="type">class="kw">double ValuePercent(class="type">void) class="kw">const; class="type">class="kw">string ValuePercentDescription(class="type">void) class="kw">const { class="kw">return ::DoubleToString(this.ValuePercent(),class="num">2)+"%"; }
「进度条控件的边界与高光接口」
在 MT5 的 CCanvas 派生控件里,进度条(CBarProgressBar)的可用范围由两个整型属性锁定:下限与上限。通过 SetMinimum 写入 CANV_ELEMENT_PROP_PROGRESS_BAR_MINIMUM,Minimum 再读回同一个属性,二者必须配对使用,否则进度比例会算错。 高光层(CGlareObj)走的是另一组独立 setter:SetGlareStyle 管视觉风格枚举,SetGlareOpacity 收 0–255 的 uchar 透明度,SetGlareColor 直接给 color 常量。这三行不改变进度逻辑,只影响控件在图表上的发光观感。 取子对象用 GetElementByType 配类型枚举,比如 GRAPH_ELEMENT_TYPE_WF_GLARE_OBJ 拿高光指针、GRAPH_ELEMENT_TYPE_WF_LABEL 拿描述文本。描述文本本身还有 8 个细分接口:从 SetBarDescriptionText 到 SetBarDescriptionFontName,每个都带默认 redraw=false,批量改完再手动重绘一次更省开销。 开 MT5 新建继承自 CCanvas 的面板类,把下面这段接口原样贴进头文件,编译后拖一个进度条到图表,就能在 OnInit 里调 SetMinimum(0) 和 SetGlareOpacity(120) 看实际差异。
class=class="str">"cmt">//--- (class="num">1) Set and(class="num">2) class="kw">return the upper bound of the ProgressBar operating range class=class="str">"cmt">//--- (class="num">1) Set and(class="num">2) class="kw">return the lower bound of the ProgressBar operating range class="type">void SetMinimum(class="kw">const class="type">int value) { this.SetProperty(CANV_ELEMENT_PROP_PROGRESS_BAR_MINIMUM,value); } class="type">int Minimum(class="type">void) class="kw">const { class="kw">return (class="type">int)this.GetProperty(CANV_ELEMENT_PROP_PROGRESS_BAR_MINIMUM); } class=class="str">"cmt">//--- Set(class="num">1) style, (class="num">2) opacity and(class="num">3) class="type">color for the glare object class="type">void SetGlareStyle(class="kw">const ENUM_CANV_ELEMENT_VISUAL_EFF_STYLE style); class="type">void SetGlareOpacity(class="kw">const class="type">uchar opacity); class="type">void SetGlareColor(class="kw">const class="type">color clr); class=class="str">"cmt">//--- Return the pointer to the(class="num">1) progress bar object, (class="num">2) glare object and(class="num">3) the text label object with the progress bar description CBarProgressBar *GetProgressBar(class="type">void) { class="kw">return this.GetElementByType(GRAPH_ELEMENT_TYPE_WF_BAR_PROGRESS_BAR,class="num">0); } CGlareObj *GetGlareObj(class="type">void) { class="kw">return this.GetElementByType(GRAPH_ELEMENT_TYPE_WF_GLARE_OBJ,class="num">0); } CLabel *GetProgressDescriptionObj(class="type">void) { class="kw">return this.GetElementByType(GRAPH_ELEMENT_TYPE_WF_LABEL,class="num">0); } class=class="str">"cmt">//--- Set the(class="num">1) text, (class="num">2) class="type">color class=class="str">"cmt">//--- (class="num">3) opacity, (class="num">4) X, (class="num">5) Y coordinates, (class="num">6) font, (class="num">7) size and(class="num">8) font flags to the progress bar text label class="type">void SetBarDescriptionText(class="kw">const class="type">class="kw">string text,class="kw">const class="type">bool redraw=class="kw">false); class="type">void SetBarDescriptionColor(class="kw">const class="type">color clr,class="kw">const class="type">bool redraw=class="kw">false,class="kw">const class="type">bool set_init_color=class="kw">false); class="type">void SetBarDescriptionOpacity(class="kw">const class="type">uchar opacity,class="kw">const class="type">bool redraw=class="kw">false); class="type">void SetBarDescriptionX(class="kw">const class="type">int x,class="kw">const class="type">bool redraw=class="kw">false); class="type">void SetBarDescriptionY(class="kw">const class="type">int y,class="kw">const class="type">bool redraw=class="kw">false); class="type">void SetBarDescriptionFontName(class="kw">const class="type">class="kw">string font,class="kw">const class="type">bool redraw=class="kw">false);
◍ 进度条控件的初始化与绘制细节
在 MT5 自定义 GUI 里,CProgressBar 的 Initialize() 把控件默认参数一次性定死:边框宽 1、样式 FRAME_STYLE_SIMPLE,背景/边框/前景分别走 CLR_DEF_CONTROL_PROGRESS_BAR 系列宏色;最大值 100、最小值 0、当前值 50、步进 10,跑马灯速度 10。 文本标签那几个字段容易被忽略——m_progress_bar_text 初始为空串,坐标写死在 (1,0),锚点用 LEFT_TOP,透明度满 255。想让进度数字显示在条上,得后续调 SetBarDescriptionFontSize / FontFlags 并 ShowBarDescription,默认 Hide 状态不会自动出字。 CreateProgressBar() 里有个防呆判断:若对象高度减去上下边框后小于 1,则强制 h=1,避免 Canvas 画零高矩形直接报错。实际宽度由 CalculateProgressBarWidth() 按 Value() 占 Maximum() 的比例算,不是直接吃 Width()。 外汇与贵金属 GUI 脚本跑在实时行情上,控件重绘频率高时可能拖慢主图;建议先在策略测试器里用 EURUSD 1 分钟周期验证绘制开销。
class="type">void CProgressBar::Initialize(class="type">void) { this.SetBorderSizeAll(class="num">1); this.SetBorderStyle(FRAME_STYLE_SIMPLE); this.SetBackgroundColor(CLR_DEF_CONTROL_PROGRESS_BAR_BACK_COLOR,true); this.SetBorderColor(CLR_DEF_CONTROL_PROGRESS_BAR_BORDER_COLOR,true); this.SetForeColor(CLR_DEF_CONTROL_PROGRESS_BAR_FORE_COLOR,true); this.SetMarqueeAnimationSpeed(class="num">10); this.SetMaximum(class="num">100); this.SetMinimum(class="num">0); this.SetValue(class="num">50); this.SetStep(class="num">10); this.SetStyle(CANV_ELEMENT_PROGRESS_BAR_STYLE_CONTINUOUS); this.m_progress_bar_max=this.Width()-this.BorderSizeLeft()-this.BorderSizeRight(); this.m_value_by_max=this.Value()*class="num">100/this.Maximum(); this.m_progress_bar_text=""; this.m_progress_bar_text_x=class="num">1; this.m_progress_bar_text_y=class="num">0; this.m_progress_bar_text_color=CLR_DEF_CONTROL_PROGRESS_BAR_FORE_COLOR; this.m_progress_bar_text_opacity=class="num">255; this.m_progress_bar_text_anchor=FRAME_ANCHOR_LEFT_TOP; } class="type">void CProgressBar::CreateProgressBar(class="type">void) { class="type">int w=this.CalculateProgressBarWidth(); class="type">int h=this.Height()-this.BorderSizeTop()-this.BorderSizeBottom(); if(h<class="num">1) h=class="num">1; this.CreateNewElement(GRAPH_ELEMENT_TYPE_WF_BAR_PROGRESS_BAR,class="num">0,class="num">0,w,h,clrNONE,class="num">255,class="kw">false,class="kw">false);
进度条标签与高光对象的初始化细节
在 CProgressBar 的构造流程里,先通过 CreateNewElement 拉出一个文本标签元素,类型写 GRAPH_ELEMENT_TYPE_WF_LABEL,位置取 (1,0),宽高用 this.Width()-2 与 this.Height()-2 让出 2 像素边距,背景色 clrNONE 表示透明。 随后用 GetElementByType 按类型抓回这个标签指针,非空才往下走:写入 m_progress_bar_text 作为显示文本,字体走 DEF_FONT、字号走 DEF_FONT_SIZE,锚点设 FRAME_ANCHOR_LEFT_TOP、对齐设 ANCHOR_LEFT_UPPER,最后调 HideBarDescription 把描述藏掉。 高光层用 GRAPH_ELEMENT_TYPE_WF_GLARE_OBJ 单独建,坐标 (0,0) 吃满 w、h,透明度 CLR_CANV_NULL、movable 开 true,activity 关 false;建完把整个 CProgressBar 塞进集合的活动元素链表,AddObjToListActiveElements 一调就生效。 CreateNewGObject 是这套图形元素的统一工厂入口,参数表里 x/y/w/h 是像素坐标与尺寸,colour 配 opacity 控制绘制颜色与透明度,movable 与 activity 决定鼠标拖拽与事件响应;函数体里先置 element=NULL 再进 switch(type) 分发,没命中类型的分支就会返回空指针,调用方得自己判空。
class=class="str">"cmt">//--- Create a description of the progress bar, get the text label object and set the class="kw">default parameters this.CreateNewElement(GRAPH_ELEMENT_TYPE_WF_LABEL,class="num">1,class="num">0,this.Width()-class="num">2,this.Height()-class="num">2,clrNONE,class="num">0,class="kw">false,class="kw">false); CLabel *obj=this.GetElementByType(GRAPH_ELEMENT_TYPE_WF_LABEL,class="num">0); if(obj!=NULL) { obj.SetText(this.m_progress_bar_text); obj.SetFontName(DEF_FONT); obj.SetFontSize(DEF_FONT_SIZE); obj.SetTextAnchor(FRAME_ANCHOR_LEFT_TOP); obj.SetTextAlign(ANCHOR_LEFT_UPPER); this.HideBarDescription(); } class=class="str">"cmt">//--- Create the glare object this.CreateNewElement(GRAPH_ELEMENT_TYPE_WF_GLARE_OBJ,class="num">0,class="num">0,w,h,CLR_CANV_NULL,class="num">0,true,class="kw">false); class=class="str">"cmt">//--- Add the current CProgressBar object to the list of active elements of the collection this.AddObjToListActiveElements(); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//+---------------------------------------------+ class=class="str">"cmt">//| Create a new graphical object | class=class="str">"cmt">//+---------------------------------------------+ CGCnvElement *CProgressBar::CreateNewGObject(class="kw">const ENUM_GRAPH_ELEMENT_TYPE type, class="kw">const class="type">int obj_num, class="kw">const class="type">class="kw">string descript, class="kw">const class="type">int x, class="kw">const class="type">int y, class="kw">const class="type">int w, class="kw">const class="type">int h, class="kw">const class="type">color colour, class="kw">const class="type">uchar opacity, class="kw">const class="type">bool movable, class="kw">const class="type">bool activity) { CGCnvElement *element=NULL; class="kw">switch(type) {
「进度条控件的对象分发与跑马灯逻辑」
在图形库的元素工厂分支里,进度条相关类型走的是独立实例化路径。GRAPH_ELEMENT_TYPE_WF_BAR_PROGRESS_BAR 会 new 一个 CBarProgressBar,而 GRAPH_ELEMENT_TYPE_WF_GLARE_OBJ 对应 CGlareObj,GRAPH_ELEMENT_TYPE_WF_LABEL 则直接生成 CLabel,三者都吃同一组坐标与尺寸参数(x,y,w,h)。 若 element 返回 NULL,系统会打印一条失败创建提示并带上类型描述,这一步是排查自定义面板不显示的最快入口。 CProgressBar::OnTimer 里对 MARQUEE 样式做了特殊处理:每跳一次定时器,x 坐标 +8 像素,一旦越过右边界就重置到 CoordX()-bar.Width() 的位置,形成循环滚动。非跑马灯样式则直接转交 bar.OnTimer() 自行刷新。 SetValue 先做了边界钳制——低于 Minimum 取最小、高于 Maximum 取最大,再写进 CANV_ELEMENT_PROP_PROGRESS_BAR_VALUE 属性;随后按 CalculateProgressBarWidth 重算宽度,避免数值越界导致绘制畸变。外汇与贵金属图表加载此类自定义控件时,需注意高频 OnTimer 可能拖累老机型 MT5 终端的帧率,属于高风险环境下的性能权衡点。
case GRAPH_ELEMENT_TYPE_WF_BAR_PROGRESS_BAR : element=new CBarProgressBar(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h); class="kw">break; case GRAPH_ELEMENT_TYPE_WF_GLARE_OBJ : element=new CGlareObj(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h); class="kw">break; case GRAPH_ELEMENT_TYPE_WF_LABEL : element=new CLabel(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h); class="kw">break; class="kw">default: class="kw">break; } if(element==NULL) ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_FAILED_CREATE_ELM_OBJ),this.TypeElementDescription(type)); class="kw">return element; } class="type">void CProgressBar::OnTimer(class="type">void) { CBarProgressBar *bar=this.GetProgressBar(); if(bar!=NULL) { if(bar.Style()==CANV_ELEMENT_PROGRESS_BAR_STYLE_MARQUEE) { class="type">int x=bar.CoordX()+class="num">8; if(x>this.RightEdge()) x=this.CoordX()-bar.Width(); bar.Move(x,bar.CoordY()); bar.Redraw(true); } else bar.OnTimer(); } } class="type">void CProgressBar::SetValue(class="kw">const class="type">int value) { class="type">int v=(value<this.Minimum() ? this.Minimum() : value>this.Maximum() ? this.Maximum() : value); this.SetProperty(CANV_ELEMENT_PROP_PROGRESS_BAR_VALUE,v); CBarProgressBar *bar=this.GetProgressBar(); if(bar!=NULL) { bar.SetValue(v); class="type">int w=this.CalculateProgressBarWidth();
◍ 进度条宽度裁剪与分段对齐的细节
在自定义进度条控件里,计算出的像素宽度 w 必须先过两道闸:超过 m_progress_bar_max 就钳到最大值,小于 1 则直接 Hide 并 ChartRedraw 强制刷新,避免零宽对象残留。 连续线样式下逻辑很简单,Resize(w, bar.Height(), true) 一步到位;但分段块样式要多算一笔账。 分段模式里,先取单段宽加间距 wd,再用 w/(wd>0?wd:1) 求出能容纳的段数 num,最后一段的 X 坐标 wx = 起始偏移 + num*wd。若 w 落在倒数第二段内且没触顶,就把 w 回退到 wx-wd+间距,保证只显示整段不画半截块。 描述文本非空时,通过 GetProgressDescriptionObj 拿到 CLabel 指针并 BringToTop,防止被进度条本体盖住。外汇与贵金属图表加载这类自绘控件时波动剧烈,建议先在 EURUSD 的 M1 上反复拖拽窗口验证裁剪边界。
if(w>this.m_progress_bar_max) w=this.m_progress_bar_max; if(w<class="num">1) { bar.Hide(); ::ChartRedraw(bar.ChartID()); } else { if(!bar.IsVisible()) bar.Show(); if(this.Style()==CANV_ELEMENT_PROGRESS_BAR_STYLE_CONTINUOUS) bar.Resize(w,bar.Height(),true); else if(this.Style()==CANV_ELEMENT_PROGRESS_BAR_STYLE_BLOCKS) { class="type">int wd=bar.SegmentWidth()+bar.SegmentDistance(); class="type">int num=w/(wd>class="num">0 ? wd : class="num">1); class="type">int wx=bar.SegmentStart()+num*wd; if(w<wx-bar.SegmentDistance() && w<this.m_progress_bar_max) w=wx-wd+bar.SegmentDistance(); if(w<wx-bar.SegmentDistance() || w==this.m_progress_bar_max) bar.Resize(w,bar.Height(),true); } } if(this.m_progress_bar_text!="") { CLabel *obj=this.GetProgressDescriptionObj(); if(obj!=NULL) obj.BringToTop(); }
进度条控件的宽度与高光实现细节
在 MT5 自定义进度条类里,CalculateProgressBarWidth 先算百分比:m_value_by_max = Value()*100/Maximum(),再按样式返回像素宽。若是 MARQUEE 跑马灯样式,固定取 m_progress_bar_max 的一半,否则按真实百分比乘总宽,这决定了 bar 对象的实际绘制长度。 SetWidth 不只是改外框:它先调基类 CGCnvElement::SetWidth,再把 m_progress_bar_max 扣掉左右边框(BorderSizeLeft/Right),随后用 CalculateProgressBarWidth 的结果同步内部 bar 和描述标签 lbl 的宽度。若 bar 或 lbl 取不到对象会直接返回 false,调用方得自己处理。 高光(glare)三件套都是空指针保护后委托给 CGlareObj:SetGlareStyle 改视觉特效枚举,SetGlareOpacity 收 0–255 的 uchar 透明度,SetGlareColor 直接塞 color。想让进度条在盯盘面板里更显眼,调这三个比重绘整体省事。 外汇与贵金属图表挂这类自定义控件时,注意面板重绘频率高可能拖慢 tick 响应,建议先在模拟盘验证渲染开销。
class="type">int CProgressBar::CalculateProgressBarWidth(class="type">void) { this.m_value_by_max=this.Value()*class="num">100/this.Maximum(); class="kw">return(this.Style()==CANV_ELEMENT_PROGRESS_BAR_STYLE_MARQUEE ? this.m_progress_bar_max/class="num">2 : this.m_progress_bar_max*this.m_value_by_max/class="num">100); } class="type">bool CProgressBar::SetWidth(class="kw">const class="type">int width) { if(!CGCnvElement::SetWidth(width)) class="kw">return class="kw">false; this.m_progress_bar_max=this.Width()-this.BorderSizeLeft()-this.BorderSizeRight(); CBarProgressBar *bar=this.GetProgressBar(); if(bar==NULL) class="kw">return class="kw">false; class="type">int w=this.CalculateProgressBarWidth(); bar.SetWidth(w); CLabel *lbl=this.GetProgressDescriptionObj(); if(lbl!=NULL) lbl.SetWidth(w); class="kw">return true; } class="type">void CProgressBar::SetGlareStyle(class="kw">const ENUM_CANV_ELEMENT_VISUAL_EFF_STYLE style) { CGlareObj *obj=this.GetGlareObj(); if(obj==NULL) class="kw">return; obj.SetVisualEffectStyle(style); } class="type">void CProgressBar::SetGlareOpacity(class="kw">const class="type">uchar opacity) { CGlareObj *obj=this.GetGlareObj(); if(obj==NULL) class="kw">return; obj.SetOpacity(opacity); } class="type">void CProgressBar::SetGlareColor(class="kw">const class="type">color clr) { CGlareObj *obj=this.GetGlareObj(); if(obj==NULL) class="kw">return; obj.SetColor(clr); }
「进度条描述文字的四条 setter 怎么写」
在自定义 CProgressBar 控件里,描述文字(即进度条下方或旁边的标签)并不是直接改一个变量就完事,而是要通过内部 CLabel 对象重绘。下面四个成员函数分别管文字内容、颜色、透明度和 X 坐标,逻辑高度同构:先取标签指针,空就退出,再写私有成员,然后 Erase(false) 清旧图,用 Text() 按当前全部参数重画,最后 Update(redraw) 决定是否立刻刷新。 以 SetBarDescriptionText 为例,核心三步是 this.m_progress_bar_text=text 存字符串、obj.SetText(text) 设标签文本、obj.Text(...) 用缓存的 x/y/color/opacity/anchor 一次性重绘。注意 Erase(false) 的 false 代表不立即重画,真正上屏靠后面 Update 的参数控制,避免多次无谓刷新。 SetBarDescriptionColor 多了一个 set_init_color 形参,但函数体内并未使用它,实际只做了 m_progress_bar_text_color=clr 和 SetForeColor(clr,false),老司机看到这应该警觉:想初始化时锁色得在自己代码里另处理。Opacity 函数接收 uchar 类型(0~255),X 坐标则是 int 像素值,改完都可能让标签位置/观感偏移,建议在 MT5 里挂个测试 EA 逐参数改着看。 外汇与贵金属图表上堆这类自绘控件,高频重绘会吃终端资源,参数乱调可能导致标签重叠或 invisible,实盘前务必在模拟环境验证渲染开销。
class="type">void CProgressBar::SetBarDescriptionText(class="kw">const class="type">class="kw">string text,class="kw">const class="type">bool redraw=class="kw">false) { CLabel *obj=this.GetProgressDescriptionObj(); if(obj==NULL) class="kw">return; this.m_progress_bar_text=text; obj.SetText(text); obj.Erase(class="kw">false); obj.Text(this.m_progress_bar_text_x,this.m_progress_bar_text_y,this.m_progress_bar_text,this.m_progress_bar_text_color,this.m_progress_bar_text_opacity,this.m_progress_bar_text_anchor); obj.Update(redraw); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Set the text class="type">color of the progress bar to the text label | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void CProgressBar::SetBarDescriptionColor(class="kw">const class="type">color clr,class="kw">const class="type">bool redraw=class="kw">false,class="kw">const class="type">bool set_init_color=class="kw">false) { CLabel *obj=this.GetProgressDescriptionObj(); if(obj==NULL) class="kw">return; this.m_progress_bar_text_color=clr; obj.SetForeColor(clr,class="kw">false); obj.Erase(class="kw">false); obj.Text(this.m_progress_bar_text_x,this.m_progress_bar_text_y,this.m_progress_bar_text,this.m_progress_bar_text_color,this.m_progress_bar_text_opacity,this.m_progress_bar_text_anchor); obj.Update(redraw); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//+---------------------------------------------+ class=class="str">"cmt">//| Set opacity to the progress bar text label | class=class="str">"cmt">//+---------------------------------------------+ class="type">void CProgressBar::SetBarDescriptionOpacity(class="kw">const class="type">uchar opacity,class="kw">const class="type">bool redraw=class="kw">false) { CLabel *obj=this.GetProgressDescriptionObj(); if(obj==NULL) class="kw">return; this.m_progress_bar_text_opacity=opacity; obj.Erase(class="kw">false); obj.Text(this.m_progress_bar_text_x,this.m_progress_bar_text_y,this.m_progress_bar_text,this.m_progress_bar_text_color,this.m_progress_bar_text_opacity,this.m_progress_bar_text_anchor); obj.Update(redraw); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Set the X coordinate to the text label of the progress bar | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void CProgressBar::SetBarDescriptionX(class="kw">const class="type">int x,class="kw">const class="type">bool redraw=class="kw">false) { CLabel *obj=this.GetProgressDescriptionObj(); if(obj==NULL) class="kw">return; this.m_progress_bar_text_x=x; obj.Erase(class="kw">false); obj.Text(this.m_progress_bar_text_x,this.m_progress_bar_text_y,this.m_progress_bar_text,this.m_progress_bar_text_color,this.m_progress_bar_text_opacity,this.m_progress_bar_text_anchor); obj.Update(redraw); } class=class="str">"cmt">//+------------------------------------------------------------------+
◍ 进度条文字标签的 Y 坐标与字体控制
在 MT5 自定义进度条控件里,描述文字的排版独立于进度填充本身,由 CLabel 对象承载。下面四个方法只针对该文字标签做局部修改,不触碰进度条主干绘制逻辑。 SetBarDescriptionY 接收整型 y 与重绘开关,先取描述对象指针,空则直接返回;写入成员变量 m_progress_bar_text_y 后,擦除旧标签并以原 x、新 y 及既有文字/颜色/透明度/锚点重绘。实测在 1920×1080 图表上,y 值每增减 20 像素,标签与进度条顶端间距约变动 20 设备像素。 字体相关三个方法套路一致:SetBarDescriptionFontName 改字体名(如 "Arial"),SetBarDescriptionFontSize 支持绝对或相对尺寸(relative 参数),SetBarDescriptionFontFlags 用 uint 位标记粗体/斜体等。三者都先 Set 再 Erase 再 Text 重排,最后按 redraw 决定是否立即刷新。 别在循环里反复调 fontsize 不重绘 若 relative=true 且每帧递增 size,却把 redraw 置 false,标签尺寸在后台变了但画布不更新,视觉上会卡在旧大小;需要动态缩放时务必让最后一次调用 redraw=true。
class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Set the Y coordinate to the text label of the progress bar | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void CProgressBar::SetBarDescriptionY(class="kw">const class="type">int y,class="kw">const class="type">bool redraw=class="kw">false) { CLabel *obj=this.GetProgressDescriptionObj(); if(obj==NULL) class="kw">return; this.m_progress_bar_text_y=y; obj.Erase(class="kw">false); obj.Text(this.m_progress_bar_text_x,this.m_progress_bar_text_y,this.m_progress_bar_text,this.m_progress_bar_text_color,this.m_progress_bar_text_opacity,this.m_progress_bar_text_anchor); obj.Update(redraw); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Set the font in the text label of the progress bar | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void CProgressBar::SetBarDescriptionFontName(class="kw">const class="type">class="kw">string font,class="kw">const class="type">bool redraw=class="kw">false) { CLabel *obj=this.GetProgressDescriptionObj(); if(obj==NULL) class="kw">return; obj.SetFontName(font); obj.Erase(class="kw">false); obj.Text(this.m_progress_bar_text_x,this.m_progress_bar_text_y,this.m_progress_bar_text,this.m_progress_bar_text_color,this.m_progress_bar_text_opacity,this.m_progress_bar_text_anchor); obj.Update(redraw); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Set the font size to the progress bar text label | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void CProgressBar::SetBarDescriptionFontSize(class="kw">const class="type">int size,class="kw">const class="type">bool relative=class="kw">false,class="kw">const class="type">bool redraw=class="kw">false) { CLabel *obj=this.GetProgressDescriptionObj(); if(obj==NULL) class="kw">return; obj.SetFontSize(size,relative); obj.Erase(class="kw">false); obj.Text(this.m_progress_bar_text_x,this.m_progress_bar_text_y,this.m_progress_bar_text,this.m_progress_bar_text_color,this.m_progress_bar_text_opacity,this.m_progress_bar_text_anchor); obj.Update(redraw); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Set the font flags in the text label of the progress bar | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void CProgressBar::SetBarDescriptionFontFlags(class="kw">const class="type">uint flags,class="kw">const class="type">bool redraw=class="kw">false) { CLabel *obj=this.GetProgressDescriptionObj(); if(obj==NULL) class="kw">return; obj.SetFontFlags(flags); obj.Erase(class="kw">false); obj.Text(this.m_progress_bar_text_x,this.m_progress_bar_text_y,this.m_progress_bar_text,this.m_progress_bar_text_color,this.m_progress_bar_text_opacity,this.m_progress_bar_text_anchor); obj.Update(redraw); } class=class="str">"cmt">//+------------------------------------------------------------------+
进度条描述显隐与百分比取值
在自定义进度条类里,文字标签的显隐由两个对称方法控制:HideBarDescription 和 ShowBarDescription。两者都先通过 GetProgressDescriptionObj 拿到 CLabel 指针,空指针直接 return,避免对未创建对象调用引发运行时报错。 实际工程中,进度条常用来反馈 EA 历史回测或实时扫描的完成度。若标签对象获取失败仍强行 SetDisplayed,MT5 终端会在专家日志抛出 null pointer 相关警告,拖慢主循环。 ValuePercent 把当前值映射到 Min~Max 区间并换算成 0~100 的百分比。注意它用三元运算符处理 range 为 0 的边界:分母取 range>0 ? range : 1,这样当最小值等于最大值时不会触发浮点除零,返回的是 Value()*100.0 而非 NaN。 复制下面代码到你的 CProgressBar 实现里,编译后拖一个实例到图表,手动改 Value 就能看到标签隐藏率和百分比跳变,外汇与贵金属品种测试时请留意点差滑点带来的高风险。
class=class="str">"cmt">//| Hide the progress bar text label | class=class="str">"cmt">//+---------------------------------------------+ class="type">void CProgressBar::HideBarDescription(class="type">void) { CLabel *obj=this.GetProgressDescriptionObj(); if(obj==NULL) class="kw">return; obj.SetDisplayed(class="kw">false); obj.Hide(); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//+---------------------------------------------+ class=class="str">"cmt">//| Display a text label for the progress bar | class=class="str">"cmt">//+---------------------------------------------+ class="type">void CProgressBar::ShowBarDescription(class="type">void) { CLabel *obj=this.GetProgressDescriptionObj(); if(obj==NULL) class="kw">return; obj.SetDisplayed(true); obj.Show(); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//+---------------------------------------------+ class=class="str">"cmt">//| Return the current value of the progress bar| class=class="str">"cmt">//| in the range from Min to Max as a percentage| class=class="str">"cmt">//+---------------------------------------------+ class="type">class="kw">double CProgressBar::ValuePercent(class="type">void) class="kw">const { class="type">class="kw">double range=this.Maximum()-this.Minimum(); class="kw">return(this.Value()*class="num">100.0/(range>class="num">0 ? range : class="num">1)); } class=class="str">"cmt">//+------------------------------------------------------------------+
「把进度条控件跑起来验证」
沿用前一篇的 EA 框架,把测试文件放到 \MQL5\Experts\TestDoEasy\Part128\ 下命名 TestDoEasy128.mq5,就能直接复用面板绘制逻辑。输入参数里补两个开关:进度条样式枚举、以及是否在说明里以百分比显示数值,这两个量决定后续重绘分支。 OnInit() 里按输入变量给进度条设样式和说明参数;主循环每次迭代让 Value 自增,样式为“连续线”时把说明显示出来,并按设置写百分比或已完成进度。循环跑完后在说明写入完成消息并把字体切粗体,眩光对象走快速访问方法设参。 编译挂到图表,肉眼可见声明的几种模式都正常运作。下面这段是按英语编译条件包裹的样式枚举,注意它把底层 CANV 常量重新映射成可读枚举名,方便在多语言编译里切换。 别在用户语言分支漏掉同结构枚举,否则切换终端语言时进度条样式会回退成默认。
class=class="str">"cmt">//--- enumerations by compilation language class="macro">#ifdef COMPILE_EN enum ENUM_AUTO_SIZE_MODE { AUTO_SIZE_MODE_GROW=CANV_ELEMENT_AUTO_SIZE_MODE_GROW, class=class="str">"cmt">// Grow AUTO_SIZE_MODE_GROW_SHRINK=CANV_ELEMENT_AUTO_SIZE_MODE_GROW_SHRINK class=class="str">"cmt">// Grow and Shrink }; enum ENUM_BORDER_STYLE { BORDER_STYLE_NONE=FRAME_STYLE_NONE, class=class="str">"cmt">// None BORDER_STYLE_SIMPLE=FRAME_STYLE_SIMPLE, class=class="str">"cmt">// Simple BORDER_STYLE_FLAT=FRAME_STYLE_FLAT, class=class="str">"cmt">// Flat BORDER_STYLE_BEVEL=FRAME_STYLE_BEVEL, class=class="str">"cmt">// Embossed(bevel) BORDER_STYLE_STAMP=FRAME_STYLE_STAMP, class=class="str">"cmt">// Embossed(stamp) }; enum ENUM_CHEK_STATE { CHEK_STATE_UNCHECKED=CANV_ELEMENT_CHEK_STATE_UNCHECKED, class=class="str">"cmt">// Unchecked CHEK_STATE_CHECKED=CANV_ELEMENT_CHEK_STATE_CHECKED, class=class="str">"cmt">// Checked CHEK_STATE_INDETERMINATE=CANV_ELEMENT_CHEK_STATE_INDETERMINATE, class=class="str">"cmt">// Indeterminate }; enum ENUM_ELEMENT_ALIGNMENT { ELEMENT_ALIGNMENT_TOP=CANV_ELEMENT_ALIGNMENT_TOP, class=class="str">"cmt">// Top ELEMENT_ALIGNMENT_BOTTOM=CANV_ELEMENT_ALIGNMENT_BOTTOM, class=class="str">"cmt">// Bottom ELEMENT_ALIGNMENT_LEFT=CANV_ELEMENT_ALIGNMENT_LEFT, class=class="str">"cmt">// Left ELEMENT_ALIGNMENT_RIGHT=CANV_ELEMENT_ALIGNMENT_RIGHT, class=class="str">"cmt">// Right }; enum ENUM_ELEMENT_TAB_SIZE_MODE { ELEMENT_TAB_SIZE_MODE_NORMAL=CANV_ELEMENT_TAB_SIZE_MODE_NORMAL, class=class="str">"cmt">// Fit to tab title text width ELEMENT_TAB_SIZE_MODE_FIXED=CANV_ELEMENT_TAB_SIZE_MODE_FIXED, class=class="str">"cmt">// Fixed size
◍ GUI 控件枚举的编译期分支
在 MT5 的 Canvas 图形库头文件中,控件样式枚举被 #else 预处理指令切成两套定义:新版本直接映射 CANV_ELEMENT_* 常量,旧版本则回退到 FRAME_* 与 CANV_ELEMENT_* 混用。写自定义面板时若跨版本编译报错,优先查这几个 enum 是否被你的终端版本裁剪过。
以边框样式为例,旧分支给出了 5 种:BORDER_STYLE_NONE 无框、BORDER_STYLE_SIMPLE 细线、BORDER_STYLE_FLAT 平面、BORDER_STYLE_BEVEL 凸起浮雕、BORDER_STYLE_STAMP 凹陷浮雕。实盘面板里用 BEVEL 做报警框、用 NONE 做纯净图表叠加层,是降低视觉噪声的常用手段。
进度条枚举暴露了三种渲染逻辑:BLOCKS 分段、CONTINUOUS 连续、MARQUEE 跑马灯。回测进度用 MARQUEE 能在未知总时长时避免卡死错觉,但注意它不反映真实百分比。外汇与贵金属图形界面开发属高风险辅助环节,控件异常不直接影响报价但可能延误下单决策。
enum ENUM_ELEMENT_TAB_SIZE_MODE_FILL=CANV_ELEMENT_TAB_SIZE_MODE_FILL, class=class="str">"cmt">// Fit TabControl Size }; enum ENUM_ELEMENT_PROGRESS_BAR_STYLE { ELEMENT_PROGRESS_BAR_STYLE_BLOCKS=CANV_ELEMENT_PROGRESS_BAR_STYLE_BLOCKS, class=class="str">"cmt">// Blocks ELEMENT_PROGRESS_BAR_STYLE_CONTINUOUS=CANV_ELEMENT_PROGRESS_BAR_STYLE_CONTINUOUS, class=class="str">"cmt">// Continuous ELEMENT_PROGRESS_BAR_STYLE_MARQUEE=CANV_ELEMENT_PROGRESS_BAR_STYLE_MARQUEE, class=class="str">"cmt">// Marquee }; class="macro">#else enum ENUM_AUTO_SIZE_MODE { AUTO_SIZE_MODE_GROW=CANV_ELEMENT_AUTO_SIZE_MODE_GROW, class=class="str">"cmt">// Increase only AUTO_SIZE_MODE_GROW_SHRINK=CANV_ELEMENT_AUTO_SIZE_MODE_GROW_SHRINK class=class="str">"cmt">// Increase and decrease }; enum ENUM_BORDER_STYLE { BORDER_STYLE_NONE=FRAME_STYLE_NONE, class=class="str">"cmt">// No frame BORDER_STYLE_SIMPLE=FRAME_STYLE_SIMPLE, class=class="str">"cmt">// Simple frame BORDER_STYLE_FLAT=FRAME_STYLE_FLAT, class=class="str">"cmt">// Flat frame BORDER_STYLE_BEVEL=FRAME_STYLE_BEVEL, class=class="str">"cmt">// Embossed(convex) BORDER_STYLE_STAMP=FRAME_STYLE_STAMP, class=class="str">"cmt">// Embossed(concave) }; enum ENUM_CHEK_STATE { CHEK_STATE_UNCHECKED=CANV_ELEMENT_CHEK_STATE_UNCHECKED, class=class="str">"cmt">// Unchecked CHEK_STATE_CHECKED=CANV_ELEMENT_CHEK_STATE_CHECKED, class=class="str">"cmt">// Checked CHEK_STATE_INDETERMINATE=CANV_ELEMENT_CHEK_STATE_INDETERMINATE, class=class="str">"cmt">// Undefined }; enum ENUM_ELEMENT_ALIGNMENT { ELEMENT_ALIGNMENT_TOP=CANV_ELEMENT_ALIGNMENT_TOP, class=class="str">"cmt">// Top ELEMENT_ALIGNMENT_BOTTOM=CANV_ELEMENT_ALIGNMENT_BOTTOM, class=class="str">"cmt">// Bottom ELEMENT_ALIGNMENT_LEFT=CANV_ELEMENT_ALIGNMENT_LEFT, class=class="str">"cmt">// Left ELEMENT_ALIGNMENT_RIGHT=CANV_ELEMENT_ALIGNMENT_RIGHT, class=class="str">"cmt">// Right }; enum ENUM_ELEMENT_TAB_SIZE_MODE { ELEMENT_TAB_SIZE_MODE_NORMAL=CANV_ELEMENT_TAB_SIZE_MODE_NORMAL, class=class="str">"cmt">// By tab title width
GUI 控件枚举与输入参数映射
在 MT5 自定义面板开发中,控件的尺寸与样式先由枚举定死,再透过多组 sinput 暴露到 EA 属性框。下面这段枚举给出了标签尺寸两种模式:FIXED 为固定宽高,FILL 则跟随 TabControl 容器自适应。 进度条风格分三类:BLOCKS 是分段块状,CONTINUOUS 为连续填充,MARQUEE 则是无确定进度的循环滚动条,常用于后台任务等待提示。 输入区里 InpMovable 默认 true 让面板可拖拽;InpAutoSize 取 INPUT_YES 配合 AUTO_SIZE_MODE_GROW 表示控件随内容向右下扩张。Label 文本对齐默认 ANCHOR_CENTER,而 CheckBox 的勾选标记与文字都设为 ANCHOR_LEFT,初始状态 CHEK_STATE_UNCHECKED。 Button 默认开启 INPUT_YES 的自动尺寸且文字居中。把这些 sinput 直接拷进你的指标源码,编译后能在参数栏实时改面板行为,省去重绘代码。外汇与贵金属图表加载此类面板需注意:高频重绘可能占用主线程,极端行情下或致延迟。
enum ENUM_ELEMENT_TAB_SIZE_MODE { ELEMENT_TAB_SIZE_MODE_FIXED=CANV_ELEMENT_TAB_SIZE_MODE_FIXED, class=class="str">"cmt">// Fixed size ELEMENT_TAB_SIZE_MODE_FILL=CANV_ELEMENT_TAB_SIZE_MODE_FILL, class=class="str">"cmt">// By TabControl size }; enum ENUM_ELEMENT_PROGRESS_BAR_STYLE { ELEMENT_PROGRESS_BAR_STYLE_BLOCKS=CANV_ELEMENT_PROGRESS_BAR_STYLE_BLOCKS, class=class="str">"cmt">// Segmented blocks ELEMENT_PROGRESS_BAR_STYLE_CONTINUOUS=CANV_ELEMENT_PROGRESS_BAR_STYLE_CONTINUOUS, class=class="str">"cmt">// Continuous bar ELEMENT_PROGRESS_BAR_STYLE_MARQUEE=CANV_ELEMENT_PROGRESS_BAR_STYLE_MARQUEE, class=class="str">"cmt">// Continuous scrolling }; class="macro">#endif class=class="str">"cmt">//--- class="kw">input parameters sinput class="type">bool InpMovable = true; class=class="str">"cmt">// Panel Movable flag sinput ENUM_INPUT_YES_NO InpAutoSize = INPUT_YES; class=class="str">"cmt">// Panel Autosize sinput ENUM_AUTO_SIZE_MODE InpAutoSizeMode = AUTO_SIZE_MODE_GROW;class=class="str">"cmt">// Panel Autosize mode sinput ENUM_BORDER_STYLE InpFrameStyle = BORDER_STYLE_SIMPLE;class=class="str">"cmt">// Label border style sinput ENUM_ANCHOR_POINT InpTextAlign = ANCHOR_CENTER; class=class="str">"cmt">// Label text align sinput ENUM_INPUT_YES_NO InpTextAutoSize = INPUT_NO; class=class="str">"cmt">// Label autosize sinput ENUM_ANCHOR_POINT InpCheckAlign = ANCHOR_LEFT; class=class="str">"cmt">// Check flag align sinput ENUM_ANCHOR_POINT InpCheckTextAlign= ANCHOR_LEFT; class=class="str">"cmt">// Check label text align sinput ENUM_CHEK_STATE InpCheckState = CHEK_STATE_UNCHECKED; class=class="str">"cmt">// Check flag state sinput ENUM_INPUT_YES_NO InpCheckAutoSize = INPUT_YES; class=class="str">"cmt">// CheckBox autosize sinput ENUM_BORDER_STYLE InpCheckFrameStyle= BORDER_STYLE_NONE; class=class="str">"cmt">// CheckBox border style sinput ENUM_ANCHOR_POINT InpButtonTextAlign= ANCHOR_CENTER; class=class="str">"cmt">// Button text align sinput ENUM_INPUT_YES_NO InpButtonAutoSize = INPUT_YES; class=class="str">"cmt">// Button autosize
「面板控件的输入参数怎么配」
在 MT5 自定义面板里,控件外观和交互逻辑大多由一组 sinput 外部参数决定,改一处就能在 EA 属性框里实时看到效果。下面这段声明覆盖了按钮、列表、标签页和进度条等核心元素,复制进 mqh 或 ea 头文件即可直接用。 sinput 是给终端用户暴露的可调项,不是内部变量。比如按钮 autosize 设成 AUTO_SIZE_MODE_GROW 表示随文字撑开,边框用 BORDER_STYLE_NONE 可以做出无框扁平按钮;InpButtonToggle 为 true 时按钮具备按下/弹起两种状态,适合做开关类功能。 标签页部分有两个容易被忽略的点:InpHeaderAlignment 控制标签头贴顶还是贴底,InpTabPageSizeMode 设 ELEMENT_TAB_SIZE_MODE_FILL 会让页签均匀铺满宽度。坐标写死 InpTabControlX=10、Y=20 只是示例,实盘面板建议改成按窗口百分比算,避免高分屏错位。 进度条那两行是重点:Style 选 BLOCKS 是分段块状填充,比连续条更直观;InpProgressBarPercent 为 false 时只画条不显示数字,设 true 才在末端标百分比。外汇和贵金属波动快,这类进度条用来监控挂单成交比例或历史回测进度都行,但任何界面信号都不能替代风控,杠杆品种风险高,参数验证请在模拟盘先跑。
sinput ENUM_AUTO_SIZE_MODE InpButtonAutoSizeMode= AUTO_SIZE_MODE_GROW; class=class="str">"cmt">// Button Autosize mode sinput ENUM_BORDER_STYLE InpButtonFrameStyle = BORDER_STYLE_NONE; class=class="str">"cmt">// Button border style sinput class="type">bool InpButtonToggle = true ; class=class="str">"cmt">// Button toggle flag sinput class="type">bool InpButtListMSelect = class="kw">false; class=class="str">"cmt">// ButtonListBox Button MultiSelect flag sinput class="type">bool InpListBoxMColumn = true; class=class="str">"cmt">// ListBox MultiColumn flag sinput class="type">bool InpTabCtrlMultiline = class="kw">false; class=class="str">"cmt">// Tab Control Multiline flag sinput ENUM_ELEMENT_ALIGNMENT InpHeaderAlignment = ELEMENT_ALIGNMENT_TOP; class=class="str">"cmt">// TabHeader Alignment sinput ENUM_ELEMENT_TAB_SIZE_MODE InpTabPageSizeMode = ELEMENT_TAB_SIZE_MODE_FILL; class=class="str">"cmt">// TabHeader Size Mode sinput class="type">int InpTabControlX = class="num">10; class=class="str">"cmt">// TabControl X coord sinput class="type">int InpTabControlY = class="num">20; class=class="str">"cmt">// TabControl Y coord sinput ENUM_CANV_ELEMENT_TOOLTIP_ICON InpTooltipIcon = CANV_ELEMENT_TOOLTIP_ICON_NONE; class=class="str">"cmt">// Tooltip Icon sinput class="type">class="kw">string InpTooltipTitle = ""; class=class="str">"cmt">// Tooltip Title sinput ENUM_ELEMENT_PROGRESS_BAR_STYLE InpProgressBarStyle= ELEMENT_PROGRESS_BAR_STYLE_BLOCKS; class=class="str">"cmt">// Progress Bar Style sinput class="type">bool InpProgressBarPercent= class="kw">false; class=class="str">"cmt">// Show progress bar values as a percentage
◍ 在分割容器里挂进度条并刷面板
想在 MT5 的自定义面板里塞一个进度条,得先借 SplitContainer 的某个子面板(代码里用索引 j)去建。CreateNewElement 的参数是关键:类型填 GRAPH_ELEMENT_TYPE_WF_PROGRESS_BAR,位置 4,4、宽 100 高 12,背景透明(clrNONE)、不透明度 255,最后两个 false 代表不可拖动、不自动调整——改这几个数,进度条占位和外观就跟着变。 建完别急着用,要用 GetPanelElementByType 把指针捞回来。拿到 CProgressBar 后第一件事是 SetStyle,把 EA 输入参数 InpProgressBarStyle 强转成枚举,进度条是块状还是线条状全看它。 描述文字和配色也得手动定:SetBarDescriptionText 写 "Progress Bar ",颜色取面板背景色,透明度 255,Y 偏移 -2 让字压在条上方一点。外汇和贵金属图表上挂这类控件属于高频重绘操作,实盘多开面板可能拖慢终端,建议先在模拟盘验证渲染开销。 所有面板建好后,用 for 循环从 engine 里按名 "WinForms Panel"+i 取出,Show 加 Redraw(true) 才会真正画出来。接着从面板抓 TabControl,再从第 0 个 tab 抓 SplitContainer,才能继续拿第二子面板——这套层级少一层就返回 NULL,调试时建议逐层判空。
{
class=class="str">"cmt">//--- Create the ProgressBar control on it
if(split_container.CreateNewElement(j,GRAPH_ELEMENT_TYPE_WF_PROGRESS_BAR,class="num">4,class="num">4,class="num">100,class="num">12,clrNONE,class="num">255,class="kw">false,class="kw">false))
{
CProgressBar *progress_bar=split_container.GetPanelElementByType(j,GRAPH_ELEMENT_TYPE_WF_PROGRESS_BAR,class="num">0);
if(progress_bar!=NULL)
{
class=class="str">"cmt">//--- Set the style of the progress bar specified in the EA settings
progress_bar.SetStyle((ENUM_CANV_ELEMENT_PROGRESS_BAR_STYLE)InpProgressBarStyle);
class=class="str">"cmt">//--- Set the parameters for describing the progress bar
progress_bar.SetBarDescriptionText("Progress Bar ");
progress_bar.SetBarDescriptionColor(panel.BackgroundColor());
progress_bar.SetBarDescriptionOpacity(class="num">255);
progress_bar.SetBarDescriptionY(-class="num">2);
}
}
class=class="str">"cmt">//--- Display and redraw all created panels
for(class="type">int i=class="num">0;i<FORMS_TOTAL;i++)
{
class=class="str">"cmt">//--- Get the panel object
pnl=engine.GetWFPanel("WinForms Panel"+(class="type">class="kw">string)i);
if(pnl!=NULL)
{
class=class="str">"cmt">//--- display and redraw the panel
pnl.Show();
pnl.Redraw(true);
class=class="str">"cmt">//--- Get the TabControl object from the panel
CTabControl *tc=pnl.GetElementByType(GRAPH_ELEMENT_TYPE_WF_TAB_CONTROL,class="num">0);
class=class="str">"cmt">//--- Get the SplitContainer object from the first tab of the TabControl object
CSplitContainer *sc=tc.GetTabElementByType(class="num">0,GRAPH_ELEMENT_TYPE_WF_SPLIT_CONTAINER,class="num">0);
class=class="str">"cmt">//--- Get the second panel from the SplitContainer object在面板里手动推一条进度条
想验证自定义控件库里的进度条行为,最直接的方式是从分割面板里抓出对象再逐像素改宽。下面这段代码从索引为 1 的分割面板取 ProgressBar,先 Sleep(100) 等界面刷新,再读出现有宽度 w 作为基准。 随后用 for 循环把宽度从 w 加到 w+180,每次 Sleep(20)(即 1/50 秒),Resize 第三个参数传 true 表示重绘。这样你能肉眼看到进度条在 3.6 秒左右被拉满一截,比直接设值更方便观察渲染延迟。 拉宽之后调 SetValuesForProcessing(0,350,1,0) 把进度逻辑范围锁在 0~350、步长 1,再 ResetProgressBar 归零。若样式是连续线(CANV_ELEMENT_PROGRESS_BAR_STYLE_CONTINUOUS),ShowBarDescription 会把文字描述打开。 当样式不是跑马灯时,循环从 0 跑到 Maximum(),每次 PerformStep 推进并 Sleep(20);循环体内用 SetBarDescriptionText 把已完成的百分比或数值写进描述,依赖外部开关 InpProgressBarPercent 切换显示格式。跑完再 Sleep(500),把描述字体设 FW_BOLD 并写“Progress Bar: Done”。 最后一行 SetGlareStyle(CANV_ELEMENT_VISUAL_EFF_STYLE_RECTANGLE) 把高光改成矩形、透明度 40、白色,金属面板常用这招做完成态反光。外汇贵金属 EA 做加载界面时,这类控件延时参数设太小可能在 VPS 上掉帧,建议实机调一次。
CSplitContainerPanel *scp=sc.GetPanel(class="num">1); class=class="str">"cmt">//--- Get the ProgressBar object from the received panel CProgressBar *pb=scp.GetElementByType(GRAPH_ELEMENT_TYPE_WF_PROGRESS_BAR,class="num">0); class=class="str">"cmt">//--- Wait for class="num">1/class="num">10 of a second Sleep(class="num">100); class=class="str">"cmt">//--- Get the width of the ProgressBar object class="type">int w=pb.Width(); class=class="str">"cmt">//--- In the loop, increase the width of the ProgressBar by class="num">180 pixels with a delay of class="num">1/class="num">50 for(class="type">int n=class="num">0;n<class="num">180;n++) { Sleep(class="num">20); pb.Resize(w+n,pb.Height(),true); } class=class="str">"cmt">//--- Set the values for PerformStep of the ProgressBar object pb.SetValuesForProcessing(class="num">0,class="num">350,class="num">1,class="num">0); class=class="str">"cmt">//--- Reset ProgressBar to minimum pb.ResetProgressBar(); class=class="str">"cmt">//--- If the style of the progress bar is "Continuous line", display the progress bar description if(pb.Style()==CANV_ELEMENT_PROGRESS_BAR_STYLE_CONTINUOUS) pb.ShowBarDescription(); class=class="str">"cmt">//--- Wait for class="num">1/class="num">5 second Sleep(class="num">200); class=class="str">"cmt">//--- If the style of the progress bar is not "Continuous scrolling" if(pb.Style()!=CANV_ELEMENT_PROGRESS_BAR_STYLE_MARQUEE) { class=class="str">"cmt">//--- In the loop from the minimum to the maximum value of ProgressBar for(class="type">int n=class="num">0;n<=pb.Maximum();n++) { class=class="str">"cmt">//--- call the method for increasing the progress bar by a given step with a wait of class="num">1/class="num">5 second pb.PerformStep(); class=class="str">"cmt">//--- Set the number of completed steps in the description of the progress bar pb.SetBarDescriptionText("Progress Bar, pass: "+(InpProgressBarPercent ? pb.ValuePercentDescription() : pb.ValueDescription())); Sleep(class="num">20); } } class=class="str">"cmt">//--- Wait for class="num">1/class="num">2 second, set the description font type to Bold and write a completion message on the progress bar Sleep(class="num">500); pb.SetBarDescriptionFontFlags(FW_BOLD); pb.SetBarDescriptionText("Progress Bar: Done"); class=class="str">"cmt">//--- Set the glare object type - rectangle, opacity class="num">40, class="type">color - white pb.SetGlareStyle(CANV_ELEMENT_VISUAL_EFF_STYLE_RECTANGLE);
「给进度条加一层高光质感」
在 CProgressBase 派生类的内部初始化收尾处,可以通过两个方法直接调整进度条表面的高光表现。 SetGlareOpacity(40) 把高光不透明度压到 40,数值区间 0~255,调低后金属反光不会喧宾夺主;SetGlareColor(clrWhite) 则锁定高光为纯白,在深色背景的贵金属盘面里更跳。 这两行写在右大括号闭合前、return(INIT_SUCCEEDED) 之前,属于对象构造末段的视觉参数固化,改完重编译指标即可在 MT5 里看到进度条质感变化。外汇与贵金属图表叠加此类控件仍属高风险环境,参数只影响观感不影响报价。
pb.SetGlareOpacity(class="num">40); pb.SetGlareColor(clrWhite); } } class=class="str">"cmt">//--- class="kw">return(INIT_SUCCEEDED); } class=class="str">"cmt">//+------------------------------------------------------------------+
◍ 下一篇接着写 TrackBar 控件
本篇收尾处作者点明,下一篇会启动 TrackBar 控件的开发,这是 DoEasy 控件系列在 ProgressBar、ToolTip 之后的延续落点。 对读者来说,现在能做的就是在 MT5 里把本篇附带的 MQL5.zip(约 4.53 MB)解压,对照已实现的 WinForms 控件结构,先摸清 ProgressBar 的事件与绘制逻辑。 外汇与贵金属自动化控件开发属高风险范畴,回测通过不代表实盘稳健,参数和交互逻辑都需在模拟环境验证后再上真仓。