DoEasy. 控件 (第 11 部分): WinForms 对象 群组,CheckedListBox WinForms 对象(基础篇)
◍ 给 CheckedListBox 做个可勾选分组
在 DoEasy 控件库里,WinForms 风格的 CheckedListBox 不是单纯列表,而是能按群组归类的勾选控件。它把多个选项塞进一个带复选框的盒子,用户勾选状态由程序实时读取,适合做多条件开关面板。 原文示例发布于 2022 年 10 月 19 日,在 MetaTrader 5 环境跑通,当时示例帖阅读量 942、评论 0,说明这类底层控件改造偏冷门但能直接用。 把它接进 EA 的面板,可以在不开图表的情况下用勾选切换策略分支。外汇与贵金属波动大、杠杆高,这类界面只解决操作效率,不替你过滤风险。
「同一容器里切出多组独立单选」
在 WinForms 容器里,绑定到同一实体的对象会按容器规则合成一个群组。单独一个 RadioButton 若以未选中状态创建,点一下复选框激活后就再也点不回去了,必须再点同类型另一个对象,前一个才释放。 若把另一容器的 RadioButton 也点选,第一个容器里已选中的复选框不会自动取消,因为两边属于不同群组、互不干涉。 想在同一容器放两组互不影响的 RadioButton,就得引入对象群组索引。比如建 6 个 RadioButton,全给群组索引 1,点其中任意一个其余五个都释放。 把这 6 个里的 4 个改成群组 2、剩下 2 个改成群组 3,容器内部就裂出两个子群组;群组 2 之间互锁、群组 3 之间互锁,跨群组不联动。 利用这点,把两个切换按钮编进同一群组,就等于做了一个双按钮互锁开关:按下一个必释放另一个,整组要么全弹起、要么只留一个按下。 另外账户对象类要改:服务器不一定返回标准 “MetaTrader 5”,有的经纪商会往终端名里塞额外字符。用查找子串 “MetaTrader 5” 来判定服务器类型,比全等匹配更稳。
给 DoEasy 图形库补上群组与单选逻辑
在 \MQL5\Include\DoEasy\Defines.mqh 的图形元素类型枚举里塞进了 WinForms 新对象类型,同时把整数型属性枚举的总数从 81 扩到 83,新增了组索引与按钮群组标志。组索引决定元素从属哪一组,按钮群组标志则声明该按钮是同一切换组的一员:三个切换按钮必须同组且都打上群组标志,才能互斥联动。 账户类 Account.mqh 的构造函数改了服务器类型判定逻辑:不再从终端名硬抠字符串,而是直接搜 “MetaTrader 5” 子串拿到 4 或 5 的值写进 m_type_server,规避了旧逻辑偶发误判的坑。GBaseObj.mqh 里把设置和取对象群组的方法升为虚函数,逼着继承类各自重写,GUI 元素由此能靠相对位置串成主从层次——主对象(窗口/Form 的父级)是不绑任何别人的祖先,从属对象可再挂自己的链。 GCnvElement.mqh 实现了取基准对象 ID 和主对象 ID:若自身已是基准则返回自己 ID,否则顺指针上溯,拿不到就返 -1;Group() 虚方法读写 group 属性,赋值时先把父对象值落进去。原先从属对象直接继承创建者 ID,现在改为从主对象往下搜全层次最大 ID 再加 1 赋给新对象,保证每个元素都有独有 ID,按 ID 直取比遍历快得多。 Form.mqh 公开了四种搜最大值方法:带参的返回某整数属性峰值,无参的返回图形元素最大 ID。CreateAndAddNewElement() 改为从主对象全链找最大 ID 加 1 再挂给新元素并登入绑定列表。鼠标移出处理补了“光标仍在元素整区但离开活动区”的条件,多次测试里颜色不还原的问题因此被覆盖进非活动区判定。 RadioButton.mqh 私密部声明了同组清零方法,公开部暴露设状态接口:选定某钮时调群组内其余 RadioButton 取消并禁用复选框,再点已选中的不清除。Button.mqh 同理加了启用/禁用与群组协同,按下即释放同组其余钮。CheckBox.mqh 把状态方法虚化,构造里背景设满不透明度、活动区每侧缩 1 像素留间隙,重绘直接写属性不透明度——悬停才能显底色变化,邻近对象误染的修复仍在摸索。
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
◍ 图形元素枚举与画布整数属性
在 MT5 自定义面板开发里,图形对象被拆成一套枚举类型,从标准扩展对象、阴影对象到 WinForms 系的容器与控件都有独立标识。上面这段枚举里,GRAPH_ELEMENT_TYPE_WF_CHECKED_LIST_BOX 代表带勾选框的列表控件,常用于多周期参数批量开关。 画布元素还有一套整数属性枚举 ENUM_CANV_ELEMENT_PROP_INTEGER,从 CANV_ELEMENT_PROP_ID(元素 ID,取值从 0 起)到 CANV_ELEMENT_PROP_TYPE(图形元素类型)定义了基础描述字段。 其中 CANV_ELEMENT_PROP_GROUP 用来把多个图形元素归组,CANV_ELEMENT_PROP_ZORDER 决定图表点击事件优先派发给哪个图层对象。做贵金属 EA 面板时,把高频按钮的 ZORDER 调高,可能减少误触底层标签的概率,但外汇与贵金属杠杆交易本身高风险,界面逻辑错误也会放大操作亏损。
GRAPH_ELEMENT_TYPE_STANDARD_EXTENDED, class=class="str">"cmt">// Extended standard graphical object GRAPH_ELEMENT_TYPE_SHADOW_OBJ, class=class="str">"cmt">// Shadow object GRAPH_ELEMENT_TYPE_ELEMENT, class=class="str">"cmt">// Element GRAPH_ELEMENT_TYPE_FORM, class=class="str">"cmt">// Form GRAPH_ELEMENT_TYPE_WINDOW, class=class="str">"cmt">// Window class=class="str">"cmt">//--- WinForms GRAPH_ELEMENT_TYPE_WF_UNDERLAY, class=class="str">"cmt">// Panel object underlay GRAPH_ELEMENT_TYPE_WF_BASE, class=class="str">"cmt">// Windows Forms Base class=class="str">"cmt">//--- &class="macro">#x27;Container&class="macro">#x27; object types are to be set below GRAPH_ELEMENT_TYPE_WF_CONTAINER, class=class="str">"cmt">// Windows Forms container base object GRAPH_ELEMENT_TYPE_WF_PANEL, class=class="str">"cmt">// Windows Forms Panel GRAPH_ELEMENT_TYPE_WF_GROUPBOX, class=class="str">"cmt">// Windows Forms GroupBox 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_CHECKED_LIST_BOX, class=class="str">"cmt">// Windows Forms CheckedListBox }; class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Integer properties of the graphical element on the canvas | class=class="str">"cmt">//+------------------------------------------------------------------+ enum ENUM_CANV_ELEMENT_PROP_INTEGER { CANV_ELEMENT_PROP_ID = class="num">0, class=class="str">"cmt">// Element ID CANV_ELEMENT_PROP_TYPE, class=class="str">"cmt">// Graphical element type class=class="str">"cmt">//---... class=class="str">"cmt">//---... CANV_ELEMENT_PROP_ACT_BOTTOM, class=class="str">"cmt">// Bottom border of the element active area CANV_ELEMENT_PROP_GROUP, class=class="str">"cmt">// Group the graphical element belongs to CANV_ELEMENT_PROP_ZORDER, class=class="str">"cmt">// Priority of a graphical object for receiving the event of clicking on a chart class=class="str">"cmt">//---... class=class="str">"cmt">//---...
「画布控件的整型属性与排序枚举」
在自定义画布上堆按钮、勾选框这类 GUI 控件时,底层用一组整型属性描述每个元素的状态。例如 CANV_ELEMENT_PROP_BUTTON_TOGGLE 标记该控件是否为带按钮的开关,CANV_ELEMENT_PROP_BUTTON_GROUP 用来归组,CANV_ELEMENT_PROP_BUTTON_STATE 记录开关当前值。 宏定义里写死了 CANV_ELEMENT_PROP_INTEGER_TOTAL 为 83,也就是整套整型属性共 83 个,而 CANV_ELEMENT_PROP_INTEGER_SKIP 为 0,表示没有跳过不参与排序的属性。这两个数字直接决定了后面双精度、字符串属性的起始偏移。 排序枚举 ENUM_SORT_CANV_ELEMENT_MODE 把整数属性映射成可排序字段。FIRST_CANV_ELEMENT_DBL_PROP 用总数减跳过数算出双精度属性起点,字符串属性再叠一层 DOUBLE_TOTAL 偏移。实际排序时能按 ID、类型、Group、ZOrder,也能按按钮组、开关状态来排。 做面板时若想让某组按钮互斥,就盯住 SORT_BY_CANV_ELEMENT_BUTTON_GROUP 和 BUTTON_STATE:同组里只留一个 state=1,其余置 0,事件分发前先跑一遍这个排序就可能避免重复触发。外汇和贵金属图表上加这类控件属于高频交互,行情跳变时误触概率会明显上升,调试记得在 tick 回放里压一遍。
class="macro">#define CANV_ELEMENT_PROP_INTEGER_TOTAL(class="num">83) class=class="str">"cmt">// Total number of integer properties class="macro">#define CANV_ELEMENT_PROP_INTEGER_SKIP(class="num">0) class=class="str">"cmt">// Number of integer properties not used in sorting class="macro">#define FIRST_CANV_ELEMENT_DBL_PROP(CANV_ELEMENT_PROP_INTEGER_TOTAL-CANV_ELEMENT_PROP_INTEGER_SKIP) class="macro">#define FIRST_CANV_ELEMENT_STR_PROP(CANV_ELEMENT_PROP_INTEGER_TOTAL-CANV_ELEMENT_PROP_INTEGER_SKIP+CANV_ELEMENT_PROP_DOUBLE_TOTAL-CANV_ELEMENT_PROP_DOUBLE_SKIP) enum ENUM_SORT_CANV_ELEMENT_MODE { SORT_BY_CANV_ELEMENT_ID = class="num">0, SORT_BY_CANV_ELEMENT_TYPE, SORT_BY_CANV_ELEMENT_GROUP, SORT_BY_CANV_ELEMENT_ZORDER, SORT_BY_CANV_ELEMENT_BUTTON_TOGGLE, SORT_BY_CANV_ELEMENT_BUTTON_GROUP, SORT_BY_CANV_ELEMENT_BUTTON_STATE };
枚举尾段里的画布控件排序与报错常量
这一段收在源码末尾,把图形控件库里剩下的枚举值和中英双语报错文本一次性列清,主要给写自定义面板的开发者做对照用。 排序枚举收尾部分覆盖了复选框在鼠标点击、悬停时的边框色与旗标色,以及按元素对象名、资源名、文本排序的入口:SORT_BY_CANV_ELEMENT_NAME_OBJ 从 FIRST_CANV_ELEMENT_STR_PROP 起算,说明字符串类属性排序被单独偏移分组。 报错常量分三块:CPanel 要求底层对象必须是 WinForms Base 类型或其派生,否则抛 MSG_PANEL_OBJECT_ERR_OBJ_MUST_BE_WFBASE;CCheckedListBox 在创建或获取 CheckBox 子对象失败时用 MSG_CHECKED_LIST_ERR_FAILED_CREATE_CHECK_BOX_OBJ 等提示;图形元素整型属性里 MSG_CANV_ELEMENT_PROP_BUTTON_GROUP 对应按钮分组旗标,和 MSG_CANV_ELEMENT_PROP_BUTTON_TOGGLE 的单体切换旗标要区分开。 源码里俄英双串成对出现,例如 "Не удалось создать объект CheckBox" 与 "Failed to create CheckBox object" 同指一个错误号,本地化时直接查表即可,不必改枚举逻辑。开 MT5 把这段贴进自定义库头文件,编译后能直接拿这些常量做面板异常分支判断。
SORT_BY_CANV_ELEMENT_CHECK_FORE_COLOR_MOUSE_DOWN, class=class="str">"cmt">// Sort by class="type">color of control checkbox frame when clicking on the control SORT_BY_CANV_ELEMENT_CHECK_FORE_COLOR_MOUSE_OVER, class=class="str">"cmt">// Sort by class="type">color of control checkbox frame when hovering the mouse over the control SORT_BY_CANV_ELEMENT_CHECK_FLAG_COLOR, class=class="str">"cmt">// Sort by class="type">color of control checkbox SORT_BY_CANV_ELEMENT_CHECK_FLAG_COLOR_OPACITY, class=class="str">"cmt">// Sort by opacity of control checkbox class="type">color SORT_BY_CANV_ELEMENT_CHECK_FLAG_COLOR_MOUSE_DOWN, class=class="str">"cmt">// Sort by class="type">color of control checkbox when clicking on the control SORT_BY_CANV_ELEMENT_CHECK_FLAG_COLOR_MOUSE_OVER, class=class="str">"cmt">// Sort by class="type">color of control checkbox when hovering the mouse over the control class=class="str">"cmt">//--- Sort by real properties class=class="str">"cmt">//--- Sort by class="type">class="kw">string properties SORT_BY_CANV_ELEMENT_NAME_OBJ = FIRST_CANV_ELEMENT_STR_PROP,class=class="str">"cmt">// Sort by an element object name SORT_BY_CANV_ELEMENT_NAME_RES, class=class="str">"cmt">// Sort by the graphical resource name SORT_BY_CANV_ELEMENT_TEXT, class=class="str">"cmt">// Sort by graphical element text }; class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//--- CPanel MSG_PANEL_OBJECT_ERR_FAILED_CREATE_UNDERLAY_OBJ, class=class="str">"cmt">// Failed to create the underlay object MSG_PANEL_OBJECT_ERR_OBJ_MUST_BE_WFBASE, class=class="str">"cmt">// Error. The created object should be of WinForms Base type or be derived from it class=class="str">"cmt">//--- CCheckedListBox MSG_CHECKED_LIST_ERR_FAILED_CREATE_CHECK_BOX_OBJ, class=class="str">"cmt">// Failed to create the CheckBox object MSG_CHECKED_LIST_ERR_FAILED_GET_CHECK_BOX_OBJ, class=class="str">"cmt">// Failed to get the CheckBox object from the object list class=class="str">"cmt">//--- Integer properties of graphical elements MSG_CANV_ELEMENT_PROP_AUTOCHECK, class=class="str">"cmt">// Auto change flag status when it is selected MSG_CANV_ELEMENT_PROP_BUTTON_TOGGLE, class=class="str">"cmt">// Toggle flag of the control featuring a button MSG_CANV_ELEMENT_PROP_BUTTON_GROUP, class=class="str">"cmt">// Button group flag MSG_CANV_ELEMENT_PROP_BUTTON_STATE, class=class="str">"cmt">// Status of the Toggle control featuring a button MSG_CANV_ELEMENT_PROP_CHECK_BACKGROUND_COLOR, class=class="str">"cmt">// Color of control checkbox background class=class="str">"cmt">//--- CPanel {"Не удалось создать объект-подложку","Failed to create underlay object"}, {"Ошибка. Создаваемый объект должен иметь тип WinForms Base или быть его наследником","Error. The object being created must be of type WinForms Base or be derived from it"}, class=class="str">"cmt">//--- CCheckedListBox {"Не удалось создать объект CheckBox","Failed to create CheckBox object"}, {"Не удалось получить объект CheckBox из списка объектов","Failed to get CheckBox object from list of objects"}, class=class="str">"cmt">//--- Integer properties of graphical elements {"Автоматическое изменение состояния флажка при его выборе","Automatically change the state of the checkbox when it is selected"}, {"Флаг \"Переключатель\" элемента управления, имеющего кнопку","\"Button-toggle\" flag of a control"}, {"Флаг группы кнопки","Button group flag"}, {"Состояние элемента управления \"Переключатель\", имеющего кнопку","The \"Toggle-button\" control state"}, {"Цвет фона флажка проверки элемента управления","The background class="type">color of the control&class="macro">#x27;s validation checkbox"}