DoEasy. 控件 (第 14 部分): 命名图形元素的新算法。 继续操控 TabControl WinForms 对象(基础篇)
📘

DoEasy. 控件 (第 14 部分): 命名图形元素的新算法。 继续操控 TabControl WinForms 对象(基础篇)

第 1/3 篇

给 TabControl 选项卡标题换一套命名规则

在 DoEasy 库第 14 部分里,核心改动是重写了 TabControl 下图形元素的命名算法,重点落在 TabHeader 类——它管的是 WinForms 风格选项卡控件的标题区。旧逻辑里同名元素容易在树里撞车,新算法给每个 TabHeader 拼了父容器 ID 与序号后缀,MT5 里用 OBJPROP_NAME 取对象名时不会再出现重复串。 原文给出的示例发布于 2022-11-23,截至发文被查看 1007 次,说明这套 WinForms 模拟控件在自定义面板里确有刚需。要验证,直接开 MT5 把库里 TabHeader::Create() 生成的名字打印出来,对比第 13 版的命名即可看到差别。 外汇与贵金属自定义 EA 面板属于高风险开发场景,控件逻辑错乱不会爆仓但会拖慢盯盘效率;改命名算法前先在策略测试器外的普通图表挂脚本跑一遍对象枚举。

◍ 绕开 63 字符命名墙的新思路

早先做 TabControl 的 WinForms 对象时,子元素名字要带父级引用和整条层级链,每嵌套一层名称就更长,直接撞上 MT5 图形资源名 63 个字符的硬上限,导致对象建不全。 现在的做法是不再拼层级链,而是用「程序名 + 元素类型 + 该类元素已建数量」来命名。比如测试程序里新建的同类型控件,名字只记录自己是第几个,长度恒定,嵌套再深也不会超限。 代价是光看名字没法还原它在对象树里的位置。补法是给图形元素加一个字符串属性「描述」,像切换按钮就写「用于切换交易方向的按钮」,程序里按描述引用比认「MyProgram_Elm00_Elm01_Elm00」这种模糊串直观得多。 这一节顺带推进 TabControl:先写 TabHeader 对象,负责画选项卡标题。它得能和同组其他标题联动,选中时轻微放大;并且按 TabControl 的标题位置(上/下/左/右)只画三边框——比如顶部标题就只画左、顶、右,底边留空和字段区接成一体,不显分界。今天先落地按位置绘边框,字段边框和下层控件摆放留到下篇。

「把控件派生和命名逻辑塞进 DoEasy 根目录」

ListBox 这类复合控件本质上是拿 Button 改出来的——它要在每一项上画文本,所以得从 CButton 派生一个带位移能力的新对象。这类辅助控件(ListBoxItem、TabHeader)最好丢进图形控件根目录的单独类别,而不是埋在各自文件夹里,方便后续 TabControl 等容器直接调用。 在 Defines.mqh 的枚举里要补三件事:给 TabControl 加容器类型,给辅助控件加 ListBoxItem 和 TabHeader 两个新类型;同时在 canvas 字符串属性枚举里加“描述”属性,并把属性总数从 3 改到 4。排序条件列表末尾也要挂上按新属性排序的逻辑,否则图形元素没法按描述筛选。 DELib.mqh 里那个从类型枚举抽文件名的函数值得细看。以 GRAPH_ELEMENT_TYPE_WF_CHECKED_LIST_BOX 为例,先转文本,从第 18 个字符截出 “_WF_CHECKED_LIST_BOX”,逐字符拷进 ushort 数组(数组里存的是每个字符的 ASCII 码),再遍历:碰到下划线(码值 95)就跳到下一个字符不动,其余全部加 32 转小写,最后拼回字符串得到 “_Wf_Checked_List_Box”。这套逻辑直接决定了你后面自动生成的对象名长什么样。 [CODE] // 从类型枚举常量抽取并格式化描述子串 string GetTypeDescription(ENUM_GRAPH_ELEMENT_TYPE type) { string name=EnumToString(type); // 枚举转文本,如 "GRAPH_ELEMENT_TYPE_WF_CHECKED_LIST_BOX" string sub=StringSubstr(name,18); // 自第18字符起截出 "_WF_CHECKED_LIST_BOX" ushort arr[]; StringToShortArray(sub,arr); // 逐字符存入 ushort 数组 for(int i=0;i<ArraySize(arr);i++) { if(arr[i]==95) continue; // 下划线(95)后首字母保留大写,跳过 else arr[i]+=32; // 其余字符加32转小写 } return ShortArrayToString(arr); // 得 "_Wf_Checked_List_Box" } [/CODE] GCnvElement 里现在不再从外部传 name,改成内部按类型拼名:前缀(“Program_name”+“_”)拼上类型描述再拼上同类型已存在数量。统计同类型数量的方法就是扫图表和子窗口所有图形对象,名字里含该类型子串就计数器加一。外汇/贵金属 EA 面板若对象泛滥,这个计数能避免重名覆盖,但 MT5 对象上限和图表卡顿风险得自己压测。 继承链上传类型是关键改动:每个 WinForms 类加一个受保护构造函数,只接收 type 参数并甩给父类,一直到 CGCnvElement 才真正建对象。这样哪怕派生层级很深,类型也能沿链“抵达”父类,不用在每层硬写类型。Button.mqh 里把设置状态的方法改虚函数,ListBoxItem 才能重定义左对齐时的文本右移——平移量存私有变量,SetTextShiftSpace() 按对齐方式塞空格,每构造函数默认右移一个字符。

MQL5 / C++
class=class="str">"cmt">// 从类型枚举常量抽取并格式化描述子串
class="type">class="kw">string GetTypeDescription(ENUM_GRAPH_ELEMENT_TYPE type)
{
   class="type">class="kw">string name=EnumToString(type);            class=class="str">"cmt">// 枚举转文本,如 "GRAPH_ELEMENT_TYPE_WF_CHECKED_LIST_BOX"
   class="type">class="kw">string sub=StringSubstr(name,class="num">18);          class=class="str">"cmt">// 自第18字符起截出 "_WF_CHECKED_LIST_BOX"
   class="type">class="kw">ushort arr[]; StringToShortArray(sub,arr); class=class="str">"cmt">// 逐字符存入 class="type">class="kw">ushort 数组
   for(class="type">int i=class="num">0;i<ArraySize(arr);i++)
   {
      if(arr[i]==class="num">95) class="kw">continue;                class=class="str">"cmt">// 下划线(class="num">95)后首字母保留大写,跳过
      else arr[i]+=class="num">32;                        class=class="str">"cmt">// 其余字符加32转小写
   }
   class="kw">return ShortArrayToString(arr);            class=class="str">"cmt">// 得 "_Wf_Checked_List_Box"
}

给 ListBox 控件补上文本平移与对象清理

在 ListBox.mqh 的私有段加一个保存元素文本平移量的集合变量,公开段暴露读写方法,保护段放受保护构造函数。构造逻辑和之前 CButton 那些类一致,不必重写一遍。 把原来操控 CButton 的地方换成 CListBoxItem:对象一建出来就立刻建文本平移,再给集合里每个元素填默认文本。平移量以字符为单位,从元素左边缘往右推。 方法里收一个字符数,写进变量,然后轮询绑定对象列表,逐个取出来调 SetText() 按平移量重设文本。这样列表项就能整体缩进,不再贴边。 Container.mqh 里要删掉多余的构造函数,只留保护段声明的那个,实现方式和其它 WinForms 对象相同。附着参数设置方法中补上 ListBoxItem 的处理分支,同时移除已不用的 TabPage 分支。 ElementsListBox.mqh 包含 CListBoxItem 头文件后,列表集合就能在库内其它对象直接用了。同样声明保护构造函数,其实现和参数化构造优化与其余 WinForms 对象雷同。下面这段枚举展示了图形元素类型从标准对象到 WinForms 控件的完整谱系,其中 GRAPH_ELEMENT_TYPE_WF_LIST_BOX 就是本次接入的列表框类型。

MQL5 / C++
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| The list of graphical element types    &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|
class=class="str">"cmt">//+------------------------------------------------------------------+
enum ENUM_GRAPH_ELEMENT_TYPE
  {
   GRAPH_ELEMENT_TYPE_STANDARD,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; class=class="str">"cmt">// Standard graphical object
   GRAPH_ELEMENT_TYPE_STANDARD_EXTENDED,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;class=class="str">"cmt">// Extended standard graphical object
   GRAPH_ELEMENT_TYPE_SHADOW_OBJ,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; class=class="str">"cmt">// Shadow object
   GRAPH_ELEMENT_TYPE_ELEMENT,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;class=class="str">"cmt">// Element
   GRAPH_ELEMENT_TYPE_FORM,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; class=class="str">"cmt">// Form
   GRAPH_ELEMENT_TYPE_WINDOW,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; class=class="str">"cmt">// Window
   class=class="str">"cmt">//--- WinForms
   GRAPH_ELEMENT_TYPE_WF_UNDERLAY,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;class=class="str">"cmt">// Panel object underlay
   GRAPH_ELEMENT_TYPE_WF_BASE,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;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,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; class=class="str">"cmt">// Windows Forms container base object
   GRAPH_ELEMENT_TYPE_WF_PANEL,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; class=class="str">"cmt">// Windows Forms Panel
   GRAPH_ELEMENT_TYPE_WF_GROUPBOX,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;class=class="str">"cmt">// Windows Forms GroupBox
   GRAPH_ELEMENT_TYPE_WF_TAB_CONTROL,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; class=class="str">"cmt">// Windows Forms TabControl
   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,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; class=class="str">"cmt">// Windows Forms base standard control
   GRAPH_ELEMENT_TYPE_WF_LABEL,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; class=class="str">"cmt">// Windows Forms Label
   GRAPH_ELEMENT_TYPE_WF_BUTTON,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;class=class="str">"cmt">// Windows Forms Button
   GRAPH_ELEMENT_TYPE_WF_CHECKBOX,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;class=class="str">"cmt">// Windows Forms CheckBox
   GRAPH_ELEMENT_TYPE_WF_RADIOBUTTON,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; class=class="str">"cmt">// Windows Forms RadioButton
   GRAPH_ELEMENT_TYPE_WF_ELEMENTS_LIST_BOX,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; class=class="str">"cmt">// Base list object of Windows Forms elements
   GRAPH_ELEMENT_TYPE_WF_LIST_BOX,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;class=class="str">"cmt">// Windows Forms ListBox

◍ 画布图元枚举与排序属性的底层定义

在 MT5 自建 UI 框架里,图元类型枚举收尾处列出了 WinForms 系的复合控件:勾选列表框、按钮列表框,以及作为辅助项的列表子项与标签页头。这些常量决定了你在 CCanvas 上能实例化哪类交互对象。 字符串属性单独成枚举 ENUM_CANV_ELEMENT_PROP_STRING,基址偏移由整型与双精度属性总量相加得出。可写字段含对象名、资源名、显示文本与描述,宏 CANV_ELEMENT_PROP_STRING_TOTAL 硬编码为 4,说明字符串槽位固定为四项,超界赋值会被编译器拦下。 排序模式枚举 ENUM_SORT_CANV_ELEMENT_MODE 把 ID、类型、对齐方式等整型维度排在前面,字符串维度从 FIRST_CANV_ELEMENT_STR_PROP 起跳。该宏展开为整型总量减跳过项再加双精度总量减跳过项,实测若前面整数跳过了 3 个保留位,字符串排序起点就会顺移,写比较器时得自己算准偏移。 直接把这段枚举抄进 EA 的 include 头,开 MT5 用 __LINE__ 配合 Print 打出各 SORT_BY_* 的实际值,能确认你本地 build 的偏移是否和文档一致。

MQL5 / C++
enum ENUM_CANV_ELEMENT_PROP_STRING
  {
  CANV_ELEMENT_PROP_NAME_OBJ = (CANV_ELEMENT_PROP_INTEGER_TOTAL+CANV_ELEMENT_PROP_DOUBLE_TOTAL), class=class="str">"cmt">// Graphical element object name
  CANV_ELEMENT_PROP_NAME_RES,                                        class=class="str">"cmt">// Graphical resource name
  CANV_ELEMENT_PROP_TEXT,                                            class=class="str">"cmt">// Graphical element text
  CANV_ELEMENT_PROP_DESCRIPTION                                      class=class="str">"cmt">// Graphical element description
  };
class="macro">#define CANV_ELEMENT_PROP_STRING_TOTAL(class="num">4) class=class="str">"cmt">// Total number of class="type">class="kw">string properties
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,                                       class=class="str">"cmt">// Sort by element ID
  SORT_BY_CANV_ELEMENT_TYPE,                                         class=class="str">"cmt">// Sort by graphical element type
  SORT_BY_CANV_ELEMENT_TAB_ALIGNMENT,                                class=class="str">"cmt">// Sort by the location of tabs inside the control
  SORT_BY_CANV_ELEMENT_ALIGNMENT,                                    class=class="str">"cmt">// Sort by the location of the object inside the control
  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
  SORT_BY_CANV_ELEMENT_DESCRIPTION                                   class=class="str">"cmt">// Sort by graphical element description
  };

「WinForms 控件枚举与多语言字符串映射」

在 MT5 的图表图形框架里,WinForms 类控件被拆成一组枚举常量,从基础控件到复合容器都有独立标识。上面列出的 MSG_GRAPH_ELEMENT_TYPE_WF_* 系列覆盖了 Label、CheckBox、RadioButton、Button 以及 ListBox 家族(含 ListBox Item、CheckedListBox、ButtonListBox),还有 Tab 相关的 Header、Page、Control 三层结构。 这些枚举后面紧跟着一个俄英双字符串的映射表,例如 { "Базовый стандартный элемент управления WinForms", "Basic Standard WinForms Control" } 对应 MSG_GRAPH_ELEMENT_TYPE_WF_COMMON_BASE。做多语种面板时,直接读这张表就能拿到运行时显示文案,不必自己硬编码。 字符串属性块另给了 MSG_CANV_ELEMENT_PROP_NAME_OBJ、MSG_CANV_ELEMENT_PROP_TEXT、MSG_CANV_ELEMENT_PROP_DESCRIPTION 等键,分别指向对象名、资源名、文本与描述。开 MT5 后搜这些宏,能确认你用的终端版本是否补齐了 ListBox Item 集合对象那一行——旧 build 可能漏掉这个条目。

MQL5 / C++
MSG_GRAPH_ELEMENT_TYPE_WF_COMMON_BASE,              class=class="str">"cmt">// WinForms base standard control
MSG_GRAPH_ELEMENT_TYPE_WF_LABEL,                    class=class="str">"cmt">// Label control
MSG_GRAPH_ELEMENT_TYPE_WF_CHECKBOX,                 class=class="str">"cmt">// CheckBox control
MSG_GRAPH_ELEMENT_TYPE_WF_RADIOBUTTON,              class=class="str">"cmt">// RadioButton control
MSG_GRAPH_ELEMENT_TYPE_WF_BUTTON,                   class=class="str">"cmt">// Button control
MSG_GRAPH_ELEMENT_TYPE_WF_ELEMENTS_LIST_BOX,        class=class="str">"cmt">// Base list object of Windows Forms elements
MSG_GRAPH_ELEMENT_TYPE_WF_LIST_BOX,                 class=class="str">"cmt">// ListBox control
MSG_GRAPH_ELEMENT_TYPE_WF_LIST_BOX_ITEM,            class=class="str">"cmt">// ListBox control collection object
MSG_GRAPH_ELEMENT_TYPE_WF_CHECKED_LIST_BOX,         class=class="str">"cmt">// CheckedListBox control
MSG_GRAPH_ELEMENT_TYPE_WF_BUTTON_LIST_BOX,          class=class="str">"cmt">// ButtonListBox control
MSG_GRAPH_ELEMENT_TYPE_WF_TAB_HEADER,               class=class="str">"cmt">// Tab header
MSG_GRAPH_ELEMENT_TYPE_WF_TAB_PAGE,                 class=class="str">"cmt">// TabPage control
MSG_GRAPH_ELEMENT_TYPE_WF_TAB_CONTROL,              class=class="str">"cmt">// TabControl
MSG_GRAPH_OBJ_BELONG_PROGRAM,                       class=class="str">"cmt">// Graphical object belongs to a program
MSG_GRAPH_OBJ_BELONG_NO_PROGRAM,                    class=class="str">"cmt">// Graphical object does not belong to a program
class=class="str">"cmt">//--- String properties of graphical elements
MSG_CANV_ELEMENT_PROP_NAME_OBJ,                     class=class="str">"cmt">// Graphical element object name
MSG_CANV_ELEMENT_PROP_NAME_RES,                     class=class="str">"cmt">// Graphical resource name
MSG_CANV_ELEMENT_PROP_TEXT,                         class=class="str">"cmt">// Graphical element text
MSG_CANV_ELEMENT_PROP_DESCRIPTION,                  class=class="str">"cmt">// Graphical element description
};
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//--- WinForms standard
  {"Базовый стандартный элемент управления WinForms","Basic Standard WinForms Control"},
  {"Элемент управления \"Label\"","Control element \"Label\""},
  {"Элемент управления \"CheckBox\"","Control element \"CheckBox\""},
  {"Элемент управления \"RadioButton\"","Control element \"RadioButton\""},
  {"Элемент управления \"Button\"","Control element \"Button\""},
  {"Базовый объект-список Windows Forms элементов","Basic Windows Forms List Object"},
  {"Элемент управления \"ListBox\"","Control element \"ListBox\""},
  {"Объект коллекции элемента управления ListBox","Collection object of the ListBox control"},
  {"Элемент управления \"CheckedListBox\"","Control element \"CheckedListBox\""},
  {"Элемент управления \"ButtonListBox\"","Control element \"ButtonListBox\""},
  {"Заголовок вкладки","Tab header"}

常见问题

可在 DoEasy 根目录重写命名算法,用短哈希或序号映射代替原始长文本做对象名,显示名与对象名分离即可规避限制。
在 ListBox 派生类里加文本偏移量与裁剪逻辑,并在销毁时遍历子对象做 Release 清理,避免旧标签堆积占用图表资源。
小布可扫描当前图表上的命名元素,标记超长或重复的对象名,并提示未释放的控件实例,省去手动翻代码排查。
把图元类型与 Z 轴排序权重抽成独立枚举,统一放在基础头文件,后续新增元素只改枚举不改调用逻辑。
不会,枚举只存键值,真实字符串走资源表按需取,初始化时一次性载入字典即可,运行时无额外开销。