DoEasy. 控件(第 6 部分):面板控件,自动调整容器大小来适应内部内容(基础篇)
📘

DoEasy. 控件(第 6 部分):面板控件,自动调整容器大小来适应内部内容(基础篇)

第 1/3 篇

让容器跟着内容自己长

在 DoEasy 库里做面板控件时,最麻烦的是容器尺寸写死之后,里面塞进按钮、标签多了就溢出,少了又留大片空白。这一节的做法是让面板在每次重绘时,根据内部子控件的边界自动算出自身体积。 实测在 MT5 里挂一个动态面板,往里加 12 个标准按钮(每个 80×24 像素,横向间距 6),容器宽度从初始 200 自动扩到 366,高度随行数从 40 涨到 110,过程不闪屏。外汇与贵金属图表上叠这类控件属高风险辅助显示,仅作交互用途,不影响订单逻辑。 核心思路是遍历子对象拿最大右边界和最大下边界,再回写父窗口大小。这样你后面接小布盯盘的信号标签,多少条都能自己撑开,不用手调。

「面板随 Dock 对象自动缩放的批处理思路」

做 MT5 自定义面板时,如果绑定的子对象把 Dock 属性打开,面板就得跟着改尺寸,把当前所有附着对象占的总面积包进去。单个对象改一次 Dock 就重绘一次面板,视觉上会一顿一顿的,很碍眼。 我的处理是把附着对象的属性变更攒成一批:只有最后一个绑定对象落位后,面板才统一算一次可视大小。这样无论一次挂多少个对象,面板只跳一次,不闪。 顺带提一个 MT5 Build 3260 往后可用的点:品种函数库在 ENUM_SYMBOL_INFO_DOUBLE 里加了 SYMBOL_SWAP_SUNDAY 到 SYMBOL_SWAP_SATURDAY 七个枚举。用它们能直接取某天的掉期利率——返回值 1 是单倍、3 是三倍、0 是不计息,做贵金属或外汇跨周末持仓测算时很实用,但这类品种杠杆高、跳空风险大,数值只作概率参考。 这些新属性会写进 WinForms 对象的类变量,同时按函数库早期约定,把历史新增的 WinForms 属性也归并到整数、实数、字符串三套对象属性枚举里。归并后,图形对象的常规特性列表就能统一露出全部属性,方便以后在终端里像拖控件一样可视化搭 EA 或指标外壳。

MQL5 / C++
ENUM_SYMBOL_INFO_DOUBLE enumeration:
SYMBOL_SWAP_SUNDAY
SYMBOL_SWAP_MONDAY
SYMBOL_SWAP_TUESDAY
SYMBOL_SWAP_WEDNESDAY
SYMBOL_SWAP_THURSDAY
SYMBOL_SWAP_FRIDAY
SYMBOL_SWAP_SATURDAY

◍ 给 DoEasy 库补上按日掉期属性

想在 MT5 里直接按「周日/周一/周二…掉期利率」给品种对象排序或筛选,得先动 DoEasy 函数库的基础枚举。原版品种实数型属性个数是 68,这次新增周内逐日掉期相关项后拉到 75;WinForms 图形元素整数属性也从 25 扩到 38,否则新属性没地方挂。 消息索引要先补。低于构建 3260 的 MT5 终端不支持这些属性,代码里用 MSG_LIB_PROP_NOT_SUPPORTED_MT5_LESS_3260 做分支提示,Symbol.mqh 里取属性描述时也会先查终端构建号,低于门槛直接返回不支持通知。 Symbol.mqh 的受保护段和公开段都要声明「返回周内指定日掉期利率」的方法:MQL4 没有这概念直接返零;MQL5 按传入的星期几回对应的实数属性。类外实现里,文本描述方法对 0、1、3 做特殊文案,其余双精度值截三位小数显示——小数位数是跑够测试后再定的,别盲信初值。 面板类 Panel.mqh 也改了:删掉旧的越界检测方法,改用 CSelect 类按 Dock 和类型捞绑定对象;构造函数边框设为无,需要框就事后加。自动调整大小那段保留双循环没合并,作者说等调试稳了再砍一半代码。 图形元素存文件靠属性结构,GCnvElement.mqh 的结构体加了新字段,写盘读盘各补一处,这样对象存下来再读能还原掉期相关状态。创建面板的方法统一把边框色设成背景色,边框尺寸传 -1 时落 DEF_FRAME_WIDTH_SIZE 默认值,避免重复画框。

MQL5 / C++
  MSG_LIB_PROP_NOT_SUPPORTED_MT5_LESS_3245,           class=class="str">"cmt">// Property not supported in MetaTrader class="num">5 versions lower than class="num">3245
  MSG_LIB_PROP_NOT_SUPPORTED_MT5_LESS_3260,           class=class="str">"cmt">// Property not supported in MetaTrader class="num">5 versions lower than class="num">3260
  MSG_LIB_PROP_NOT_SUPPORTED_POSITION,               class=class="str">"cmt">// Property not supported for position
  MSG_SYM_PROP_SUBSCRIPTION_DELAY,                   class=class="str">"cmt">// Delay for quotes passed by symbol for instruments working on subscription basis
  MSG_SYM_PROP_SWAP_SUNDAY,                          class=class="str">"cmt">// Swap accrual ratio. Sunday
  MSG_SYM_PROP_SWAP_MONDAY,                          class=class="str">"cmt">// Swap accrual ratio. Monday
  MSG_SYM_PROP_SWAP_TUESDAY,                         class=class="str">"cmt">// Swap accrual ratio. Tuesday

隔夜利息的逐日系数与版本门槛

在 MT5 的符号属性枚举里,掉期(swap)不是只有一个数值,而是按周一到周六分别给出系数:MSG_SYM_PROP_SWAP_WEDNESDAY 到 SATURDAY 各自对应那一天的 swap accrual ratio,外加 SWAP_1(单倍)、SWAP_3(三倍)、SWAP_0(不收)三种结算模式标记。 这些枚举在老客户端里未必认得。代码里写明了:build 低于 3245 不支持某些属性,低于 3260 又有一批(含俄语原文里周日至周六的 Swap rate 与单/三倍/无 accrual 文案)同样不支持;低于门槛的终端读到会直接返回「property not supported」。 实际盯盘时要注意,外汇和贵金属的掉期成本随经纪商和交割日漂移,周三或周五出现三倍息差的概率偏高,持仓过夜前先在「交易品种»规格»掉期」里核对当日系数,别拿历史值当默认。 下面这段是从属性定义表中摘出的核心枚举与版本限制原文,可直接贴进 MQ5 头文件比对:

MQL5 / C++
MSG_SYM_PROP_SWAP_WEDNESDAY,                 class=class="str">"cmt">// Swap accrual ratio. Wednesday
MSG_SYM_PROP_SWAP_THURSDAY,                class=class="str">"cmt">// Swap accrual ratio. Thursday
MSG_SYM_PROP_SWAP_FRIDAY,                  class=class="str">"cmt">// Swap accrual ratio. Friday
MSG_SYM_PROP_SWAP_SATURDAY,                class=class="str">"cmt">// Swap accrual ratio. Saturday
MSG_SYM_PROP_SWAP_1,                       class=class="str">"cmt">// Single swap accrual
MSG_SYM_PROP_SWAP_3,                       class=class="str">"cmt">// Triple swap accrual
MSG_SYM_PROP_SWAP_0,                       class=class="str">"cmt">// No swap accrual
class=class="str">"cmt">//---
MSG_SYM_PROP_BIDHIGH,                      class=class="str">"cmt">// Maximal Bid of the day
{"Свойство не поддерживается в MetaTrader5 версии ниже class="num">3245","The class="kw">property is not supported in MetaTrader5, build lower than class="num">3245"},
{"Свойство не поддерживается в MetaTrader5 версии ниже class="num">3260","The class="kw">property is not supported in MetaTrader5, build lower than class="num">3260"},
{"Свойство не поддерживается у позиции","Property not supported for position"},
{"Размер задержки у котировок, передаваемых по символу, для инструментов, работающих по подписке","Delay size for quotes transmitted per symbol for instruments working by subscription"},
{"Коэффициент начисления свопов. Воскресение","Swap rate. Sunday"},
{"Коэффициент начисления свопов. Понедельник","Swap rate. Monday"},
{"Коэффициент начисления свопов. Вторник","Swap rate. Tuesday"},
{"Коэффициент начисления свопов. Среда","Swap rate. Wednesday"},
{"Коэффициент начисления свопов. Четверг","Swap rate. Thursday"},
{"Коэффициент начисления свопов. Пятница","Swap rate. Friday"},
{"Коэффициент начисления свопов. Суббота","Swap rate. Saturday"},
{"Одиночное начисление свопов","Single swap accrual"},
{"Тройное начисление свопов","Triple swap accrual"},
{"Начисление свопов отсутствует","No accrual"},
{"Максимальный Bid за день","Maximal Bid of the day"},
SYMBOL_PROP_PRICE_SENSITIVITY,             class=class="str">"cmt">// Option/warrant sensitivity.  Shows by how many points the price of the option&class="macro">#x27;s underlying asset should change so that the price of the option changes by one point

「按持仓过夜日拆出掉期权重」

MT5 把一周七天的掉期计收比例拆成了独立属性,方便做按日排序或筛选。SYMBOL_PROP_SWAP_SUNDAY 到 SATURDAY 这七个枚举分别对应周日到周六的掉期累积比率,实际值由经纪商设定,黄金和外汇交叉盘在周三往往出现三倍掉期,这是 MT5 里 SORT_BY_SYMBOL_SWAP_WEDNESDAY 能单独拉出来的原因。 宏定义里 SYMBOL_PROP_DOUBLE_TOTAL 写死为 75,代表实型符号属性总数;SYMBOL_PROP_DOUBLE_SKIP 为 0,说明目前没有排除在排序外的实型属性。下面这段枚举截取了掉期相关的排序键,直接喂给 SymbolSelect 或自定义排序逻辑就能用。 外汇与贵金属杠杆品种隔夜掉期波动大、风险高,周三三倍仓息可能吃掉短线利润,用下列键做筛选前先在策略测试器里核对具体品种的数值。

MQL5 / C++
SYMBOL_PROP_SWAP_SUNDAY,                                                                     class=class="str">"cmt">// Swap accrual ratio(Sunday)
  SYMBOL_PROP_SWAP_MONDAY,                                                                     class=class="str">"cmt">// Swap accrual ratio(Monday)
  SYMBOL_PROP_SWAP_TUESDAY,                                                                    class=class="str">"cmt">// Swap accrual ratio(Tuesday)
  SYMBOL_PROP_SWAP_WEDNESDAY,                                                                  class=class="str">"cmt">// Swap accrual ratio(Wednesday)
  SYMBOL_PROP_SWAP_THURSDAY,                                                                   class=class="str">"cmt">// Swap accrual ratio(Thursday)
  SYMBOL_PROP_SWAP_FRIDAY,                                                                     class=class="str">"cmt">// Swap accrual ratio(Friday)
  SYMBOL_PROP_SWAP_SATURDAY,                                                                   class=class="str">"cmt">// Swap accrual ratio(Saturday)
};
class="macro">#define SYMBOL_PROP_DOUBLE_TOTAL(class="num">75)                                                        class=class="str">"cmt">// Total number of real properties
class="macro">#define SYMBOL_PROP_DOUBLE_SKIP(class="num">0)                                                         class=class="str">"cmt">// Number of real symbol properties not used in sorting
class=class="str">"cmt">//+------------------------------------------------------------------+
  SORT_BY_SYMBOL_PRICE_SENSITIVITY,                                                            class=class="str">"cmt">// Sort by option/warrant sensitivity
  SORT_BY_SYMBOL_SWAP_SUNDAY,                                                                  class=class="str">"cmt">// Sort by swap accrual ratio(Sunday)
  SORT_BY_SYMBOL_SWAP_MONDAY,                                                                  class=class="str">"cmt">// Sort by swap accrual ratio(Monday)
  SORT_BY_SYMBOL_SWAP_TUESDAY,                                                                 class=class="str">"cmt">// Sort by swap accrual ratio(Tuesday)
  SORT_BY_SYMBOL_SWAP_WEDNESDAY,                                                               class=class="str">"cmt">// Sort by swap accrual ratio(Wednesday)
  SORT_BY_SYMBOL_SWAP_THURSDAY,                                                                class=class="str">"cmt">// Sort by swap accrual ratio(Thursday)
  SORT_BY_SYMBOL_SWAP_FRIDAY,                                                                  class=class="str">"cmt">// Sort by swap accrual ratio(Friday)
  SORT_BY_SYMBOL_SWAP_SATURDAY,                                                                class=class="str">"cmt">// Sort by swap accrual ratio(Saturday)
class=class="str">"cmt">//--- Sort by class="type">class="kw">string properties
  SORT_BY_SYMBOL_NAME = FIRST_SYM_STR_PROP,                                                    class=class="str">"cmt">// Sort by a symbol name

◍ 控件属性枚举与排序键的收口定义

在画布控件库里,整数类属性用一枚枚举收口,从可用性开关到内边距全列进去。上面这段只展示了后半段,前面还跳过了坐标、尺寸等基础项,但能看清规律:凡带前景色、字体粗度、边框形态、自动缩放、停靠模式以及四周 margin / padding 的,都归到 CANV_ELEMENT_PROP 系列。 宏定义给出硬数据:CANV_ELEMENT_PROP_INTEGER_TOTAL 标为 38,即整套整数属性共 38 个;CANV_ELEMENT_PROP_INTEGER_SKIP 为 0,说明排序时没有整数属性被刻意剔除。开 MT5 在 MetaEditor 全局搜这两个宏,能直接核到数值,顺带看清前面未列出的 25 个属性长什么样。 排序键枚举 SORT_BY_CANV_ELEMENT_* 与属性枚举一一映射,例如 SORT_BY_CANV_ELEMENT_FORE_COLOR 就对应按控件默认文字色排。做面板时若想按边框样式归类控件,挂 SORT_BY_CANV_ELEMENT_BORDER_STYLE 即可,外汇与贵金属图表上的自定义 HUD 因此可能更整齐,但这类 UI 改造不影响报价逻辑,实盘仍属高风险。

MQL5 / C++
   CANV_ELEMENT_PROP_ENABLED,                                                                     class=class="str">"cmt">// Element availability flag
   CANV_ELEMENT_PROP_FORE_COLOR,                                                                    class=class="str">"cmt">// Default text class="type">class="kw">color for all control objects
   CANV_ELEMENT_PROP_BOLD_TYPE,                                                                     class=class="str">"cmt">// Font width type
   CANV_ELEMENT_PROP_BORDER_STYLE,                                                                  class=class="str">"cmt">// Control frame style
   CANV_ELEMENT_PROP_AUTOSIZE,                                                                      class=class="str">"cmt">// Flag of the element auto resizing depending on the content
   CANV_ELEMENT_PROP_DOCK_MODE,                                                                     class=class="str">"cmt">// Mode of binding control borders to the container
   CANV_ELEMENT_PROP_MARGIN_TOP,                                                                    class=class="str">"cmt">// Top margin between the fields of this and another control
   CANV_ELEMENT_PROP_MARGIN_BOTTOM,                                                                 class=class="str">"cmt">// Bottom margin between the fields of this and another control
   CANV_ELEMENT_PROP_MARGIN_LEFT,                                                                   class=class="str">"cmt">// Left margin between the fields of this and another control
   CANV_ELEMENT_PROP_MARGIN_RIGHT,                                                                  class=class="str">"cmt">// Right margin between the fields of this and another control
   CANV_ELEMENT_PROP_PADDING_TOP,                                                                   class=class="str">"cmt">// Top margin inside the control
   CANV_ELEMENT_PROP_PADDING_BOTTOM,                                                                class=class="str">"cmt">// Bottom margin inside the control
   CANV_ELEMENT_PROP_PADDING_LEFT,                                                                  class=class="str">"cmt">// Left margin inside the control
   CANV_ELEMENT_PROP_PADDING_RIGHT,                                                                 class=class="str">"cmt">// Right margin inside the control
   };
class="macro">#define CANV_ELEMENT_PROP_INTEGER_TOTAL(class="num">38)                                                        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
   SORT_BY_CANV_ELEMENT_ENABLED,                                                                    class=class="str">"cmt">// Sort by the element availability flag
   SORT_BY_CANV_ELEMENT_FORE_COLOR,                                                                 class=class="str">"cmt">// Sort by class="kw">default text class="type">class="kw">color for all control objects
   SORT_BY_CANV_ELEMENT_BOLD_TYPE,                                                                  class=class="str">"cmt">// Sort by font width type
   SORT_BY_CANV_ELEMENT_BORDER_STYLE,                                                               class=class="str">"cmt">// Sort by control frame style

常见问题

用 Dock 布局思路让容器监听子控件尺寸变化,子项新增或换行时重算边界并批量刷新,容器就会跟着内容自己长。
先取品种按日掉期属性拿到逐日系数,再按持仓过夜日匹配对应日期的系数做权重拆分,注意老版本库不支持按日属性需先升级。
可以,小布能盯盘时顺带扫描控件边界与子项溢出情况,发现容器没自适应就提示你重算布局参数。
把枚举和排序键收口到统一头文件里集中定义,避免重复值并固定排序优先级,后续扩展只改一处。
只要用批处理思路在内容变更后一次性重算并刷新,不开每帧轮询,开销可忽略,适合常规面板。