DoEasy. 控件 (第 11 部分): WinForms 对象  群组,CheckedListBox WinForms 对象(基础篇)
📘

DoEasy. 控件 (第 11 部分): WinForms 对象 群组,CheckedListBox WinForms 对象(基础篇)

第 1/3 篇

◍ 给 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 像素留间隙,重绘直接写属性不透明度——悬停才能显底色变化,邻近对象误染的修复仍在摸索。

MQL5 / C++
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 调高,可能减少误触底层标签的概率,但外汇与贵金属杠杆交易本身高风险,界面逻辑错误也会放大操作亏损。

MQL5 / C++
  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 回放里压一遍。

MQL5 / C++
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 把这段贴进自定义库头文件,编译后能直接拿这些常量做面板异常分支判断。

MQL5 / C++
  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"}

常见问题

用群组容器把每组 CheckedListBox 包起来,每组绑定独立群组 ID,组内单选、组间互不影响。
整数属性放在画布控件枚举尾段,排序枚举同段声明,按枚举值大小决定控件绘制先后。
可以,小布能按你给的群组结构生成预览描述并标出可能重叠或排序异常,省去手动搭界面。
报错常量放枚举尾段且取值避开排序区间即可,既不影响排序也比较容易捕获越界错误。
群组能统一管显隐和单选状态,比手动叠列表少写同步逻辑,后续改结构也只动群组配置。