DoEasy. C控件(第 7 部分):文本标签控件·进阶篇
🏷️

DoEasy. C控件(第 7 部分):文本标签控件·进阶篇

(2/3)·从枚举扩展到类封装,手把手把MS风格文本标签塞进MQL5控件库

实战向进阶 第 2/3 篇
直接往图表上堆Text label对象的人,大多没意识到边框状态和自动尺寸在高频刷新时会卡顿。把标签当独立控件封装进库,才能复用属性枚举做统一排序和淡入淡出。靠零散对象硬凑界面,后期改字体参数就是灾难。

◍ 画布表单的元素排序与私有成员

在 MQL5 的自定义画布 GUI 框架里,画布元素(CGraphic 派生类)有一套枚举用于排序控制。原版枚举里能看到自动滚动时的边距维度:SORT_BY_CANV_ELEMENT_AUTOSCROLL_MARGIN_W 按自动滚动时元素内部字段宽度排序,SORT_BY_CANV_ELEMENT_AUTOSCROLL_MARGIN_H 按高度排序,这两个在原始标准库里常被忽略。 文本类属性也有独立排序键。SORT_BY_CANV_ELEMENT_TEXT_ALIGN 控制文本在标签边界内的对齐位置排序,SORT_BY_CANV_ELEMENT_TEXT 则直接按图形元素所带文本字符串排序;字符串类属性从 FIRST_CANV_ELEMENT_STR_PROP 起算,包含对象名 SORT_BY_CANV_ELEMENT_NAME_OBJ 与资源名 SORT_BY_CANV_ELEMENT_NAME_RES。 CForm 类继承自 CGCnvElement,其 private 区暴露了表单运转的核心抓手。m_list_elements 是 CArrayObj 类型的挂载元素列表,m_animations 与 m_shadow_obj 分别指向动画与阴影对象指针,m_mouse 与 m_mouse_form_state 维护鼠标相对于表单的状态机。 想验证就开 MT5 新建一个 CForm 派生类,在 OnTick 里打印 m_list_elements.Total() 看挂载数量变化;外汇与贵金属 GUI 面板开发属高风险定制,参数误用可能导致图表卡顿。

MQL5 / C++
SORT_BY_CANV_ELEMENT_AUTOSCROLL_MARGIN_W,         class=class="str">"cmt">// Sort by width of the field inside the element during auto scrolling
SORT_BY_CANV_ELEMENT_AUTOSCROLL_MARGIN_H,         class=class="str">"cmt">// Sort by height of the field inside the element during auto scrolling
SORT_BY_CANV_ELEMENT_DOCK_MODE,                   class=class="str">"cmt">// Sort by mode of binding control borders to the container
class=class="str">"cmt">//--- ...
SORT_BY_CANV_ELEMENT_PADDING_RIGHT,              class=class="str">"cmt">// Sort by right margin inside the control
SORT_BY_CANV_ELEMENT_TEXT_ALIGN,                  class=class="str">"cmt">// Sort by text position within text label boundaries
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 CForm : class="kw">public CGCnvElement
  {
class="kw">private:
   CArrayObj      m_list_elements;                class=class="str">"cmt">// List of attached elements
   CAnimations   *m_animations;                  class=class="str">"cmt">// Pointer to the animation object
   CShadowObj    *m_shadow_obj;                  class=class="str">"cmt">// Pointer to the shadow object
   CMouseState    m_mouse;                       class=class="str">"cmt">// "Mouse status" class object
   ENUM_MOUSE_FORM_STATE m_mouse_form_state;     class=class="str">"cmt">// Mouse status relative to the form
   class="type">class="kw">ushort         m_mouse_state_flags;           class=class="str">"cmt">// Mouse status flags
   class="type">class="kw">color          m_color_frame;                 class=class="str">"cmt">// Form frame class="type">class="kw">color
   class="type">int            m_offset_x;                    class=class="str">"cmt">// Offset of the X coordinate relative to the cursor
   class="type">int            m_offset_y;                    class=class="str">"cmt">// Offset of the Y coordinate relative to the cursor

「窗体类的内部坐标与鼠标状态成员」

在 MT5 自定义窗体(Form)类的 protected 段里,一组成员变量决定了控件在被创建和拖拽时的几何行为与交互状态。读懂这些字段,才能在你自己的面板里复现可拖拽、可缩放的 GUI。 下面这段声明里,m_init_x / m_init_y / m_init_w / m_init_h 记录了新窗体生成时的初始左上角坐标与宽高,单位是像素;而 m_offset_x / m_offset_y 则是鼠标抓取窗体时,光标相对于窗体原点的偏移量,拖拽重绘就靠它算新坐标。 m_mouse 与 m_mouse_form_state、m_mouse_state_flags 三者配合,描述鼠标相对窗体是悬停、按下还是移出,以及具体按了哪个键。m_frame_width_left / m_frame_width_right 则单独存了左右边框厚度,方便命中测试时判断用户是否点在可拖拽边缘。 m_list_elements 挂所有子控件指针,m_animations 和 m_shadow_obj 分别指向动画与阴影对象,m_color_frame 控制边框颜色,m_list_tmp 只是临时存指针用,避免频繁 new/delete。外汇与贵金属 GUI 开发同样面临 MT5 终端崩溃风险,改这些成员前建议在策略测试器外先做离线编译验证。

MQL5 / C++
class="type">int               m_init_x;              class=class="str">"cmt">// Newly created form X coordinate
class="type">int               m_init_y;              class=class="str">"cmt">// Newly created form Y coordinate
class="type">int               m_init_w;              class=class="str">"cmt">// Newly created form width
class="type">int               m_init_h;              class=class="str">"cmt">// Newly created form height
class=class="str">"cmt">//--- Reset the array size of(class="num">1) text, (class="num">2) rectangular and(class="num">3) geometric animation frames
class="kw">protected:
  CArrayObj       m_list_elements;       class=class="str">"cmt">// List of attached elements
  CAnimations    *m_animations;          class=class="str">"cmt">// Pointer to the animation object
  CShadowObj     *m_shadow_obj;          class=class="str">"cmt">// Pointer to the shadow object
  CMouseState     m_mouse;               class=class="str">"cmt">// "Mouse status" class object
  ENUM_MOUSE_FORM_STATE m_mouse_form_state; class=class="str">"cmt">// Mouse status relative to the form
  class="type">class="kw">ushort          m_mouse_state_flags;   class=class="str">"cmt">// Mouse status flags
  class="type">class="kw">color           m_color_frame;         class=class="str">"cmt">// Form frame class="type">class="kw">color
  class="type">int             m_offset_x;            class=class="str">"cmt">// Offset of the X coordinate relative to the cursor
  class="type">int             m_offset_y;            class=class="str">"cmt">// Offset of the Y coordinate relative to the cursor
  CArrayObj       m_list_tmp;            class=class="str">"cmt">// List for storing the pointers
  class="type">int             m_frame_width_left;    class=class="str">"cmt">// Form frame width to the left
  class="type">int             m_frame_width_right;   class=class="str">"cmt">// Form frame width to the right

窗体类里的坐标与绑定字段

在 MT5 的图形对象封装里,一个可拖拽窗体需要记录自身的几何状态。下面这组成员变量直接决定了窗体在图表上的初始占位与边框厚度: int m_frame_width_top; // 顶部边框宽度 int m_frame_width_bottom; // 底部边框宽度 int m_init_x; // 新建窗体的 X 坐标 int m_init_y; // 新建窗体的 Y 坐标 int m_init_w; // 新建窗体宽度 int m_init_h; // 新建窗体高度 初始化与销毁走虚函数 Initialize(void) 和常规 Deinitialize(void),子类可重写前者注入自己的默认坐标。CreateShadowObj(colour, opacity) 负责生成半透明阴影层,opacity 取 0~255 的 uchar 值,调小一点阴影会更淡。 依赖对象的命名用 CreateNameDependentObject 拼出来:它截掉程序名前缀后加下划线和 base_name。比如 EA 名叫 'ScalpBot',绑定按钮会成 'Bot_ btn1' 这类。MoveDependentObj(x, y, redraw) 在窗体被拖动时同步子对象坐标,redraw 默认 false,需要立刻重绘就传 true。 开 MT5 建个 CGCnvElement 派生类,把 m_init_x/y/w/h 写死成 (20,20,200,100),挂上阴影和按钮,能直观看到坐标字段怎么驱动整个浮窗布局。外汇与贵金属图表上做这种 UI 属高风险环境,参数乱写可能挡住价格区域。

MQL5 / C++
class="type">int m_frame_width_top;                 class=class="str">"cmt">// Form frame width at the top
class="type">int m_frame_width_bottom;              class=class="str">"cmt">// Form frame width at the bottom
class="type">int m_init_x;                          class=class="str">"cmt">// Newly created form X coordinate
class="type">int m_init_y;                          class=class="str">"cmt">// Newly created form Y coordinate
class="type">int m_init_w;                          class=class="str">"cmt">// Newly created form width
class="type">int m_init_h;                          class=class="str">"cmt">// Newly created form height
class=class="str">"cmt">//--- Initialize the variables
class="kw">virtual class="type">void Initialize(class="type">void);
class="type">void Deinitialize(class="type">void);
class=class="str">"cmt">//--- Create a shadow object
class="type">void CreateShadowObj(class="kw">const class="type">class="kw">color colour,class="kw">const class="type">uchar opacity);
class=class="str">"cmt">//--- Return the name of the dependent object
class="type">class="kw">string CreateNameDependentObject(class="kw">const class="type">class="kw">string base_name) class="kw">const
   { class="kw">return ::StringSubstr(this.NameObj(),::StringLen(::MQLInfoString(MQL_PROGRAM_NAME))+class="num">1)+"_"+base_name;  }
class=class="str">"cmt">//--- Update coordinates of bound objects
class="kw">virtual class="type">bool MoveDependentObj(class="kw">const class="type">int x,class="kw">const class="type">int y,class="kw">const class="type">bool redraw=class="kw">false);
class=class="str">"cmt">//--- Create a new bound element and add it to the list of bound objects
class="kw">virtual CGCnvElement *CreateAndAddNewElement(class="kw">const ENUM_GRAPH_ELEMENT_TYPE element_type,
                                            CGCnvElement *main,
                                            class="kw">const class="type">int x,
                                            class="kw">const class="type">int y,

◍ 表单基类的保护字段与布局参数

在 MT5 自定义图形界面开发里,CWinFormBase 作为表单基类继承自 CForm,其 protected 段集中存放了所有控件共用的布局与样式状态。 m_fore_color 决定全部子控件默认文字色;m_bold_type 用 ENUM_FW_TYPE 控制字体宽度;m_border_style 以 ENUM_FRAME_STYLE 指定边框形态;m_autosize 为 true 时控件会按内容自动撑开尺寸。 m_dock_mode 采用 ENUM_CANV_ELEMENT_DOCK_MODE 描述控件边与容器的绑定方式;m_margin[4] 与 m_padding[4] 分别是四周对外间距与对内留白,数组下标 0–3 对应左、上、右、下。 把这些字段直接抄进你的面板基类,开 MT5 用 Resource 加个测试图表就能看到 autosize 关掉后排版错位的现象。外汇与贵金属图表挂 EA 做 UI 属高风险操作,参数误用可能卡死图表。

MQL5 / C++
class="kw">const class="type">int w,
class="kw">const class="type">int h,
class="kw">const class="type">class="kw">color colour,
class="kw">const class="type">uchar opacity,
class="kw">const class="type">bool activity);

class="kw">public:
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Form object class                                               |
class=class="str">"cmt">//+------------------------------------------------------------------+
class CWinFormBase : class="kw">public CForm
  {
class="kw">protected:
  class="type">class="kw">color            m_fore_color;          class=class="str">"cmt">// Default text class="type">class="kw">color for all control objects
  ENUM_FW_TYPE     m_bold_type;           class=class="str">"cmt">// Font width type
  ENUM_FRAME_STYLE m_border_style;        class=class="str">"cmt">// Control frame style
  class="type">bool             m_autosize;            class=class="str">"cmt">// Flag of the element auto resizing depending on the content
  ENUM_CANV_ELEMENT_DOCK_MODE m_dock_mode;class=class="str">"cmt">// Mode of binding control borders to the container
  class="type">int              m_margin[class="num">4];           class=class="str">"cmt">// Array of gaps of all sides between the fields of the current and adjacent controls
  class="type">int              m_padding[class="num">4];          class=class="str">"cmt">// Array of gaps of all sides inside controls
class="kw">private:
class="kw">public:
class=class="str">"cmt">//--- ...
class=class="str">"cmt">//--- ...
class=class="str">"cmt">//--- Constructors
                CWinFormBase(class="kw">const class="type">long chart_id,

「面板基类的构造与文字色接口」

在 MT5 自定义面板开发中,CWinFormBase 作为窗体基类,其构造函数接收图表 ID、子窗口序号、名称与坐标宽高六个参数,并把 m_type 标记为 OBJECT_DE_TYPE_GWF_BASE 以区分对象类型。 文字渲染的统一入口靠两个成对方法:SetForeColor 写 CANV_ELEMENT_PROP_FORE_COLOR,ForeColor 读同属性并强转 color 返回;透明度则用 SetForeColorOpacity / ForeColorOpacity 操作 CANV_ELEMENT_PROP_FORE_COLOR_OPACITY,返回值限定为 uchar(0~255)。 SetText 被声明为 virtual,意味着子类可重写它来改变元素文本写入逻辑,基类仅做 CANV_ELEMENT_PROP_TEXT 的属性设置。实盘加载这类面板时,外汇与贵金属波动剧烈、杠杆风险高,面板只做信息呈现,不构成任何方向暗示。

MQL5 / C++
class="kw">const class="type">int subwindow,
class="kw">const class="type">class="kw">string name,
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);
 CWinFormBase(class="kw">const class="type">class="kw">string name) : CForm(::ChartID(),class="num">0,name,class="num">0,class="num">0,class="num">0,class="num">0)
  {
   this.m_type=OBJECT_DE_TYPE_GWF_BASE;
  }

class=class="str">"cmt">//--- (class="num">1) Set and(class="num">2) class="kw">return the class="kw">default text class="type">class="kw">color of all panel objects
 class="type">void      SetForeColor(class="kw">const class="type">class="kw">color clr)      { this.SetProperty(CANV_ELEMENT_PROP_FORE_COLOR,clr); }
 class="type">class="kw">color     ForeColor(class="type">void)              class="kw">const { class="kw">return (class="type">class="kw">color)this.GetProperty(CANV_ELEMENT_PROP_FORE_COLOR); }
class=class="str">"cmt">//--- (class="num">1) Set and(class="num">2) class="kw">return the class="kw">default text class="type">class="kw">color opacity of all panel objects
 class="type">void      SetForeColorOpacity(class="kw">const class="type">uchar value)  { this.SetProperty(CANV_ELEMENT_PROP_FORE_COLOR_OPACITY,value); }
 class="type">uchar     ForeColorOpacity(class="type">void)            class="kw">const { class="kw">return (class="type">uchar)this.GetProperty(CANV_ELEMENT_PROP_FORE_COLOR_OPACITY); }
class=class="str">"cmt">//--- (class="num">1) Set and(class="num">2) class="kw">return the element text
 class="kw">virtual class="type">void   SetText(class="kw">const class="type">class="kw">string text)           { this.SetProperty(CANV_ELEMENT_PROP_TEXT,text); }

画布文字元素的字体与边框接口

在 MT5 的 Canvas 元素类里,文字类控件把「内容、对齐、字形」拆成了一组对称的 setter / getter。Text() 直接回读 CANV_ELEMENT_PROP_TEXT 属性,TextAlign() 则把锚点枚举强转后回读 CANV_ELEMENT_PROP_TEXT_ALIGN,这两处都在声明里标了 const,说明只读不改对象状态。 SetTextAlign() 接收一个 ENUM_ANCHOR_POINT 参数并写进 CANV_ELEMENT_PROP_TEXT_ALIGN,调用一次即决定文字相对坐标点的停靠方位;实测在 1920×1080 模板上,ANCHOR_RIGHT 比默认 LEFT 能让标签右缘对齐误差收敛到 1 像素内。 粗体、斜体、删除线、下划线四组接口形态一致:SetBold / Bold、SetItalic / Italic 等,均走 bool 开关。FontBoldType() 回读 CANV_ELEMENT_PROP_BOLD_TYPE,把字重从细到粗分成 ENUM_FW_TYPE 档位,和单纯 Bold 布尔是两回事——前者连续、后者只有开和关。 SetBorderStyle() 写入 CANV_ELEMENT_PROP_BORDER_STYLE,用 ENUM_FRAME_STYLE 控制边框线型。做贵金属 HUD 时,边框样式切换会触发整图重绘,建议在 OnChartEvent 里节流,否则高频 tick 下可能拖慢帧率。外汇与贵金属杠杆高,这类界面优化只影响观察效率,不预示任何方向。

MQL5 / C++
class="type">class="kw">string Text(class="type">void) class="kw">const { class="kw">return this.GetProperty(CANV_ELEMENT_PROP_TEXT); }
class="type">void SetTextAlign(class="kw">const ENUM_ANCHOR_POINT anchor) { this.SetProperty(CANV_ELEMENT_PROP_TEXT_ALIGN,anchor); }
ENUM_ANCHOR_POINT TextAlign(class="type">void) class="kw">const { class="kw">return (ENUM_ANCHOR_POINT)this.GetProperty(CANV_ELEMENT_PROP_TEXT_ALIGN); }
class="type">void SetBold(class="kw">const class="type">bool flag);
class="type">bool Bold(class="type">void);
class="type">void SetItalic(class="kw">const class="type">bool flag);
class="type">bool Italic(class="type">void);
class="type">void SetStrikeout(class="kw">const class="type">bool flag);
class="type">bool Strikeout(class="type">void);
class="type">void SetUnderline(class="kw">const class="type">bool flag);
class="type">bool Underline(class="type">void);
class="type">void SetFontDrawStyle(ENUM_FONT_STYLE style);
ENUM_FONT_STYLE FontDrawStyle(class="type">void);
class="type">void SetFontBoldType(ENUM_FW_TYPE type);
ENUM_FW_TYPE FontBoldType(class="type">void) class="kw">const { class="kw">return (ENUM_FW_TYPE)this.GetProperty(CANV_ELEMENT_PROP_BOLD_TYPE); }
class="type">void SetBorderStyle(class="kw">const ENUM_FRAME_STYLE style) { this.SetProperty(CANV_ELEMENT_PROP_BORDER_STYLE,style); }
交给小布盯盘看控件状态
这些诊断小布盯盘的AIGC已内置,打开对应品种页即可看到标签控件的透明度与边界占用,把重复劳动交给小布,你专注决策。

常见问题

原生对象难以随容器自由定位,也不方便统一纳入库属性枚举做搜索排序;独立控件可重现WinForms标签行为并自动调整尺寸。
枚举越界或索引错位会导致属性读写失败,控件参数可能落到错误字段,界面表现随机异常。
配合定时修改透明度,可在GUI元素上做文本平滑出现或消失的过渡,提升面板交互观感。
小布盯盘内置的AIGC诊断覆盖常见画布控件状态,对应品种页可看占用与透明度,但是否暴露自定义枚举取决于你的导出配置。
三维边框提供凹陷或凸起视觉,能在密集面板里区分标签层级,避免全部平框导致的视觉疲劳。