图形界面 I:控件构成(第二章)·综合运用
📘

图形界面 I:控件构成(第二章)·综合运用

第 3/3 篇

窗体逐层创建与隐藏机制

可见窗口部件必须按背景、头部、头部控件的严格层级顺序创建。CWindow::CreateWindow() 开头先检查窗口指针是否已存入控件基类,若 CElement::Id() 返回 WRONG_VALUE,说明没走 CWndContainer::AddWindow() 登记,函数直接返回 false 并在日志提示先传指针,窗口不会生成。 指针没问题后,图表 ID、子窗口号、标题文本、坐标 (x,y) 及 m_bg_full_height(取自 m_y_size 的窗体高度副本,用于后续改尺寸)会写入当前类与基类变量。图表 ID 和子窗口号其实在 CWndEvents 构造函数里定好,自定义类建控件时透传进来。 背景、标题、标签、图标、关闭按钮等对象任一个创建失败,CreateWindow() 就返回 false。若程序是独立窗口指标,还会用 m_subwindow_height 检查并初始化窗体尺寸。对话框窗口建完必须隐藏——多窗口程序加载时全部以隐藏态贴到图表,调用时设可见、关闭时隐藏但不删除,避免反复创建销毁。 Hide() 虚函数把窗口所有对象在全时间帧隐藏,并将基类 m_is_visible 置 false。若只改了声明没补方法体就编译 CWindow,会报“缺少方法体”错误,因为背景、标题等创建函数尚未实现。 背景对象命名规则为 程序名_窗口_bg_控件ID,用下划线分隔;创建时先按最小化/最大化调整尺寸,再设属性,最后把指针存基类数组。标题创建额外在 CRectLabel 里存坐标和大小,否则窗口移动时无法用光标跟踪标题。 窗口标签图标按程序类型从 EasyAndFastGUI\Icons\bmp16(16x16)或 bmp24 等目录读取,脚本因不能追踪事件只能放数据可视化控件。各控件坐标相对窗口左上角缩进存于原始对象类,移动窗口只需一次更新全部对象坐标。

MQL5 / C++
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| 为控件创建一个窗体
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">bool CWindow::CreateWindow(class="kw">const class="type">long chart_id,class="kw">const class="type">int subwin,class="kw">const class="type">class="kw">string caption_text,class="kw">const class="type">int x,class="kw">const class="type">int y)
  {
   if(CElement::Id()==WRONG_VALUE)
     {
      ::Print(__FUNCTION__," > Before creating a window, its pointer is to be passed "
                          "to the window array using the CWndContainer::AddWindow(CWindow &object) method.");
      class="kw">return(false);
     }
class=class="str">"cmt">//--- 变量初始化
   m_chart_id       =chart_id;
   m_subwin         =subwin;
   m_caption_text   =caption_text;
   m_x              =x;
   m_y              =y;
   m_bg_full_height =m_y_size;
class=class="str">"cmt">//--- 创建所有窗口对象
   if(!CreateBackground())
      class="kw">return(false);
   if(!CreateCaption())
      class="kw">return(false);
   if(!CreateLabel())
      class="kw">return(false);
   if(!CreateIcon())
      class="kw">return(false);
   if(!CreateButtonClose())
      class="kw">return(false);

「面板初始化时的窗口与背景构造逻辑」

在自定义控件初始化阶段,程序会先尝试创建卷起、展开和提示三类按钮,任一失败立即返回 false 终止。随后判断运行环境:若当前是指标且开启了子窗口高度自适配,会把子窗口高度设为背景全高加 3 像素并调用 ChangeSubwindowHeight 重设。 对话框类型的窗口在初始化末尾直接走 Hide() 隐藏自身。Hide 的实现是遍历所有元素对象并置为 OBJ_NO_PERIODS 不可见,再改写基类可见状态为 false。 背景与标题的创建都靠 CreateBackground / CreateCaption 完成。背景高度取决于 m_is_minimized:最小化时取标题栏高度 m_caption_height,否则取 m_bg_full_height,两者差可能达到数十像素,直接影响面板占位。 标题栏对象固定 BORDER_FLAT 且不可选中,Z 轴顺序由 m_caption_zorder 控制;渐变色数组在末尾用 InitColorArray 以常态色和悬停色初始化,为后续鼠标交互留接口。开 MT5 把这段塞进自己的 CWindow 类,调 m_bg_full_height 就能看面板伸缩幅度变化。

MQL5 / C++
if(!CreateButtonRollUp())
   class="kw">return(false);
if(!CreateButtonUnroll())
   class="kw">return(false);
if(!CreateButtonTooltip())
   class="kw">return(false);
class=class="str">"cmt">//--- 如果这个程序是一个指标
if(CElement::ProgramType()==PROGRAM_INDICATOR)
  {
   class=class="str">"cmt">//--- 如果设置为设置子窗口高度模式
   if(m_height_subwindow_mode)
     {
      m_subwindow_height=m_bg_full_height+class="num">3;
      ChangeSubwindowHeight(m_subwindow_height);
     }
  }
class=class="str">"cmt">//--- 如果是一个对话框窗口,隐藏窗口
if(m_window_type==W_DIALOG)
   Hide();
class=class="str">"cmt">//---
class="kw">return(true);
 }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| 隐藏窗口
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void CWindow::Hide(class="type">void)
  {
class=class="str">"cmt">//--- 隐藏所有对象
  for(class="type">int i=class="num">0; i<ObjectsElementTotal(); i++)
     Object(i).Timeframes(OBJ_NO_PERIODS);
class=class="str">"cmt">//--- 可见状态
  CElement::IsVisible(false);
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| 创建窗口背景
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">bool CWindow::CreateBackground(class="type">void)
  {
class=class="str">"cmt">//--- 形成窗口名称
  class="type">class="kw">string name=CElement::ProgramName()+"_window_bg_"+(class="type">class="kw">string)CElement::Id();
class=class="str">"cmt">//--- 取决于窗口状态的窗口大小(最小化/最大化)
  class="type">int y_size=class="num">0;
  if(m_is_minimized)
    {
     y_size=m_caption_height;
     CElement::YSize(m_caption_height);
    }
  else
    {
     y_size=m_bg_full_height;
     CElement::YSize(m_bg_full_height);
    }
class=class="str">"cmt">//--- 设置窗口背景
  if(!m_bg.Create(m_chart_id,name,m_subwin,m_x,m_y,m_x_size,y_size))
     class="kw">return(false);
class=class="str">"cmt">//--- 设置属性
  m_bg.BackColor(m_bg_color);
  m_bg.Color(m_border_color);
  m_bg.BorderType(BORDER_FLAT);
  m_bg.Corner(m_corner);
  m_bg.Selectable(false);
  m_bg.Z_Order(m_bg_zorder);
  m_bg.Tooltip("\n");
class=class="str">"cmt">//--- 存储对象指针
  CElement::AddToArray(m_bg);
  class="kw">return(true);
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| 创建窗口标题
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">bool CWindow::CreateCaption(class="type">void)
  {
   class="type">class="kw">string name=CElement::ProgramName()+"_window_caption_"+(class="type">class="kw">string)CElement::Id();
class=class="str">"cmt">//--- 设置窗口标题
   if(!m_caption_bg.Create(m_chart_id,name,m_subwin,m_x,m_y,m_x_size,m_caption_height))
      class="kw">return(false);
class=class="str">"cmt">//--- 设置属性
   m_caption_bg.BackColor(m_caption_bg_color);
   m_caption_bg.Color(m_border_color);
   m_caption_bg.BorderType(BORDER_FLAT);
   m_caption_bg.Corner(m_corner);
   m_caption_bg.Selectable(false);
   m_caption_bg.Z_Order(m_caption_zorder);
   m_caption_bg.Tooltip("\n");
class=class="str">"cmt">//--- 存储坐标
   m_caption_bg.X(m_x);
   m_caption_bg.Y(m_y);
class=class="str">"cmt">//--- 存储大小(对象内)
   m_caption_bg.XSize(m_caption_bg.X_Size());
   m_caption_bg.YSize(m_caption_bg.Y_Size());
class=class="str">"cmt">//--- 初始化渐变色数组
   CElement::InitColorArray(m_caption_bg_color,m_caption_bg_color_hover,m_caption_color_bg_array);

◍ 窗口图标的按程序类型自动匹配

在 MT5 自建 GUI 面板时,窗口左上角的类型图标若交给用户逐个指定会很繁琐。CWindow 类用 DefaultIcon() 按当前程序类别回退默认位图:脚本走 script.bmp、EA 走 advisor.bmp、指标走 indicator.bmp,路径统一在 Images\EasyAndFastGUI\Icons\bmp16\ 下。 CreateIcon() 的实作里,图标对象名由程序名 + "_window_icon_" + 元素 ID 拼成,坐标固定在窗口外框 x+5、y+2 像素处。若 m_icon_file 为空串,就调 DefaultIcon() 补上默认图,并用 BmpFileOn / BmpFileOff 同时绑定同一张图,避免悬停状态闪变。 三个 #resource 预编译指令把 16×16 的 bmp 打进 ex5,运行时以 "::" 前缀直接引用。外汇与贵金属图表加载这类自定义面板属高风险操作环境,建议先在模拟盘验证资源路径,防止图标缺失导致对象创建返回 false。 下面这段是核心创建逻辑,注意 XGap / YGap 记录了相对窗口原点的缩进,后续拖拽重绘时不会错位: string CWindow::DefaultIcon(void) { string path="Images\\EasyAndFastGUI\\Icons\\bmp16\\advisor.bmp"; switch(CElement::ProgramType()) { case PROGRAM_SCRIPT: { path="Images\\EasyAndFastGUI\\Icons\\bmp16\\script.bmp"; break; } case PROGRAM_EXPERT: { path="Images\\EasyAndFastGUI\\Icons\\bmp16\\advisor.bmp"; break; } case PROGRAM_INDICATOR: { path="Images\\EasyAndFastGUI\\Icons\\bmp16\\indicator.bmp"; break; } } return(path); } bool CWindow::CreateIcon(void) { string name=CElement::ProgramName()+"_window_icon_"+(string)CElement::Id(); int x=m_x+5; int y=m_y+2; if(!m_icon.Create(m_chart_id,name,m_subwin,x,y)) return(false); if(m_icon_file=="") m_icon_file=DefaultIcon(); m_icon.BmpFileOn("::"+m_icon_file); m_icon.BmpFileOff("::"+m_icon_file); m_icon.Corner(m_corner); m_icon.Selectable(false); m_icon.Z_Order(m_button_zorder); m_icon.Tooltip("\n"); m_icon.X(x); m_icon.Y(y); m_icon.XGap(x-m_x); m_icon.YGap(y-m_y); m_icon.XSize(m_icon.X_Size()); m_icon.YSize(m_icon.Y_Size()); CElement::AddToArray(m_icon); return(true); }

MQL5 / C++
class="type">class="kw">string CWindow::DefaultIcon(class="type">void)
  {
  class="type">class="kw">string path="Images\\EasyAndFastGUI\\Icons\\bmp16\\advisor.bmp";
  class="kw">switch(CElement::ProgramType())
    {
    case PROGRAM_SCRIPT:
      { path="Images\\EasyAndFastGUI\\Icons\\bmp16\\script.bmp"; class="kw">break; }
    case PROGRAM_EXPERT:
      { path="Images\\EasyAndFastGUI\\Icons\\bmp16\\advisor.bmp"; class="kw">break; }
    case PROGRAM_INDICATOR:
      { path="Images\\EasyAndFastGUI\\Icons\\bmp16\\indicator.bmp"; class="kw">break; }
    }
  class="kw">return(path);
  }

class="macro">#resource "\\Images\\EasyAndFastGUI\\Icons\\bmp16\\advisor.bmp"
class="macro">#resource "\\Images\\EasyAndFastGUI\\Icons\\bmp16\\indicator.bmp"
class="macro">#resource "\\Images\\EasyAndFastGUI\\Icons\\bmp16\\script.bmp"

class="type">bool CWindow::CreateIcon(class="type">void)
  {
  class="type">class="kw">string name=CElement::ProgramName()+"_window_icon_"+(class="type">class="kw">string)CElement::Id();
  class="type">int x=m_x+class="num">5;
  class="type">int y=m_y+class="num">2;
  if(!m_icon.Create(m_chart_id,name,m_subwin,x,y))
    class="kw">return(false);
  if(m_icon_file=="")
    m_icon_file=DefaultIcon();
  m_icon.BmpFileOn("::"+m_icon_file);
  m_icon.BmpFileOff("::"+m_icon_file);
  m_icon.Corner(m_corner);
  m_icon.Selectable(false);
  m_icon.Z_Order(m_button_zorder);
  m_icon.Tooltip("\n");
  m_icon.X(x);
  m_icon.Y(y);
  m_icon.XGap(x-m_x);
  m_icon.YGap(y-m_y);
  m_icon.XSize(m_icon.X_Size());
  m_icon.YSize(m_icon.Y_Size());
  CElement::AddToArray(m_icon);
  class="kw">return(true);
  }

把控件窗体挂到图表上验证

界面骨架搭完后,第一件能落地的事就是让窗体真正出现在 MT5 图表上。前面写好的 CProgram::CreateTradePanel() 负责对外暴露创建入口,内部转调 CProgram::CreateWindow() 生成具体窗体,标题文字作为唯一参数传进去即可。 编译期有个坑:漏掉窗口指针的注册(CWndContainer::AddWindow)照样能过编译,但加载到图表会瞬间被踢掉,EA 日志里抛出 “Before creating a window, its pointer is to be saved in the base” 的报错。上面贴的 2015.09.23 19:26:10 测试记录就是 EURUSD D1 周期下初始化失败的真实日志。 窗体尺寸与配色在 CreateWindow 里写死为 200×200、背景 clrWhiteSmoke、边框与标题栏 clrLightSteelBlue,悬停时标题栏转成 C'200,210,225'。想换皮肤直接改这几行,重编译挂图就能看到差异。外汇与贵金属图表加载自定义 EA 界面属高频操作,实盘前务必在模拟环境跑通,这类图形资源异常可能引发图表卡顿等风险。

MQL5 / C++
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| 为控件创建一个窗体
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">bool CProgram::CreateWindow(class="kw">const class="type">class="kw">string caption_text)
  {
class=class="str">"cmt">//--- 向窗口数组中添加一个而窗口指针
   CWndContainer::AddWindow(m_window);
class=class="str">"cmt">//--- 属性
   m_window.XSize(class="num">200);
   m_window.YSize(class="num">200);
   m_window.WindowBgColor(clrWhiteSmoke);
   m_window.WindowBorderColor(clrLightSteelBlue);
   m_window.CaptionBgColor(clrLightSteelBlue);
   m_window.CaptionBgColorHover(C&class="macro">#x27;class="num">200,class="num">210,class="num">225&class="macro">#x27;);
class=class="str">"cmt">//--- 创建一个窗体
   if(!m_window.CreateWindow(m_chart_id,m_subwin,caption_text,class="num">1,class="num">1))
      class="kw">return(false);
class=class="str">"cmt">//---
   class="kw">return(true);
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| 创建一个交易面板                       
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">bool CProgram::CreateTradePanel(class="type">void)
  {
class=class="str">"cmt">//--- 创建一个控件窗体
   if(!CreateWindow("EXPERT PANEL"))
      class="kw">return(false);
class=class="str">"cmt">//--- 创建控件
class=class="str">"cmt">// ...
class=class="str">"cmt">//---
   ::ChartRedraw();
   class="kw">return(true);
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| EA初始化函数                                       
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">int OnInit(class="type">void)
  {
   program.OnInitEvent();
class=class="str">"cmt">//--- 设置交易面板
   if(!program.CreateTradePanel())
     {
      ::Print(__FUNCTION__," > Failed to create graphical interface!");
      class="kw">return(INIT_FAILED);
     }
class=class="str">"cmt">//--- 初始化成功
   class="kw">return(INIT_SUCCEEDED);
  }
class="num">2015.09.class="num">23 class="num">19:class="num">26:class="num">10.398 TestLibrary(EURUSD,D1) OnInit > Failed to create graphical interface!
class="num">2015.09.class="num">23 class="num">19:class="num">26:class="num">10.398 TestLibrary(EURUSD,D1) CWindow::CreateWindow > Before creating a window, its pointer is to be saved in the base: CWndContainer::AddWindow(CWindow &object).

「界面库头文件引用与一处编译坑」

当前 GUI 程序库的结构里,所有界面控件相关类都从 CElement 基类派生,统一收在带深蓝箭头的大矩形框中,实际文件由 CWndContainer 类统管元素容器。 编译依赖方面,Window.mqh 会顺次拉入 Element.mqh、Objects.mqh、Enums.mqh、Defines.mqh、ChartObjectsBmpControls.mqh 等共 11 个头文件,每个在依赖树里计为 1 次引用。 有个真实编译错误值得注意:Window.mqh 第 25 行报 undefined class 'CBmpLabel' cannot be used,而 CBmpLabel 的声明实际在 Objects.mqh 第 20 行。这说明头文件包含顺序或前向声明缺失时,MT4/MT5 编译器会直接卡在窗体类编译阶段。 遇到这类报错,先确认 ChartObjectsBmpControls.mqh 是否被正确置于 Objects.mqh 之后引入;改完顺序重开 MT5 编译,可能就消掉了。外汇与贵金属交易界面自动化本身高风险,库仅辅助观测与下单交互,不代表任何收益预期。

MQL5 / C++
&class="macro">#x27;Window.mqh&class="macro">#x27;      Window.mqh       class="num">1      class="num">1
&class="macro">#x27;Element.mqh&class="macro">#x27;  Element.mqh   class="num">1      class="num">1
&class="macro">#x27;Objects.mqh&class="macro">#x27;  Objects.mqh   class="num">1      class="num">1
&class="macro">#x27;Enums.mqh&class="macro">#x27;    Enums.mqh     class="num">1      class="num">1
&class="macro">#x27;Defines.mqh&class="macro">#x27;  Defines.mqh   class="num">1      class="num">1
&class="macro">#x27;ChartObjectsBmpControls.mqh&class="macro">#x27;  ChartObjectsBmpControls.mqh   class="num">1      class="num">1
&class="macro">#x27;ChartObject.mqh&class="macro">#x27;       ChartObject.mqh class="num">1      class="num">1
&class="macro">#x27;Object.mqh&class="macro">#x27;   Object.mqh       class="num">1      class="num">1
&class="macro">#x27;StdLibErr.mqh&class="macro">#x27; StdLibErr.mqh   class="num">1      class="num">1
&class="macro">#x27;ChartObjectsTxtControls.mqh&class="macro">#x27;  ChartObjectsTxtControls.mqh   class="num">1      class="num">1
&class="macro">#x27;Colors.mqh&class="macro">#x27;   Colors.mqh       class="num">1      class="num">1
undefined class &class="macro">#x27;CBmpLabel&class="macro">#x27; cannot be used      Window.mqh      class="num">25    class="num">15
  see declaration of class &class="macro">#x27;CBmpLabel&class="macro">#x27; Objects.mqh   class="num">20    class="num">7

◍ Window.mqh 编译报错的头文件依赖陷阱

在 MT5 里直接 #include <Window.mqh> 却没先引入标准库图形基类时,编译器会甩出一连串 undefined class 报错。典型如 CLabel 在 Window.mqh 第 26 行第 12 列无法使用,其声明其实在 Objects.mqh 第 19 行第 7 列;CBmpLabel 更夸张,Window.mqh 的 27、28、29、30 行全部报未定义,指向 Objects.mqh 第 20 行。 根因是 Window.mqh 依赖 Objects.mqh 里的 CLabel / CBmpLabel 等控件类,但头文件自身不负责 include。漏掉这层依赖,后面 ENUM_WINDOW_TYPE 也会变成 unexpected token——Window.mqh 第 36 行第 5 列和第 99 行第 5 列都卡在这个枚举类型上,连带 m_window_type、WindowType 变量声明直接 semicolon expected。 更深的报错来自语法层崩坏:第 102 行 void 处 name expected,第 104 行全局作用域出现 '}' 表达式非法,说明前面类型缺失已让解析器彻底失同步。第 118–144 行反复刷 'const' modifier not allowed for nonmember functions,是因为类定义断裂后,原本的成员函数被误判成非成员函数。 开 MT5 验证时,先在代码顶部写 #include <Objects.mqh> 再 #include <Window.mqh>,上述 20 余条报错大概率一次性清零。外汇与贵金属 EA 开发属高风险活动,头文件链出错可能让策略在实盘前就编译不过,务必在模拟环境跑通再上真仓。

Window.mqh 编译报错链的定位思路

在 MT5 里直接include第三方 GUI 库时,编译器常把 Window.mqh 的 172~182 行全部标成 'virtual' - unexpected token。这一般不是 virtual 写错,而是该头文件依赖的基类头文件没被优先编译,导致编译器在全局作用域撞见了类内才合法的 virtual 关键字。 紧接着 183 行的 '}' - expressions are not allowed on a global scope,与前面 8 处 virtual 报错同源:解析在类定义中途断裂,右大括号被误判为全局代码。此后 222、235、294、362 行的 RollUpSubwindowMode / ChangeSubwindowHeight / Hide / DefaultIcon 报 member function not defined,是因为类结构残缺后,后续成员函数引用全部失效。 资源类报错(advisor.bmp / indicator.bmp / script.bmp 的 as resource 提示)虽然不阻断编译,但路径 ::Images\EasyAndFastGUI\Icons\bmp16\ 若不存在,运行期图标会静默丢失。

  • 与 210 行的 m_window_type / m_chart undeclared,以及 286 行 W_DIALOG 未声明,说明枚举与成员变量定义位于缺失的父级文件中。遇到这种连锁报错,优先检查 include 顺序:把定义基类与枚举的头文件放在 Window.mqh 之前,多数错误会一次性消失。外汇与贵金属 EA 开发属高风险活动,编译通过不代表策略有效,实盘前务必在策略测试器充分验证。

「Window.mqh 编译报错的根源定位」

在 MT5 编译包含 Window.mqh 的 EA 或指标时,若标准库版本不匹配,会集中爆出一批报错。上面这组日志全部落在 Window.mqh 的 396 行与 402~405 行,说明问题不在你的策略代码,而在系统头文件自身的语法解析失败。

  • 行连续出现 some operator expectedexpression has no effect 以及 , ) 等 unexpected token,典型特征是编译器把本该识别为对象成员调用的语句拆成了孤立表达式。这通常因为当前 metaeditor 自带的 Window.mqh 与你所引用的 CWnd 派生类声明结构不一致。
  • 至 405 行更直白:BmpFileOnBmpFileOffCornerSelectable 全部报 undeclared identifier,且前面紧跟的 . 提示 struct or class type expected。这意味着编译器此时根本没拿到这些成员所属的类定义——头文件包含顺序错乱或标准库被旧版覆盖都可能触发。

验证方法很简单:打开 MT5 安装目录下的 MQL5/Include/Windows/Window.mqh,直接跳到 396 行与 402 行,比对函数签名是否含有上述成员。若你的文件里这些行是空实现或被宏吞掉,从另一台同版本终端拷一份覆盖即可,外汇与贵金属自动化交易本身杠杆高、风险大,环境干净才谈得上回测可信。

◍ Window.mqh 里成片的点操作符报错

在编译自定义面板库 Window.mqh 时,406 到 416 行集中抛出 24 条错误,几乎全是「'.' - struct or class type expected」配合「undeclared identifier」。这类报错说明编译器在「.」左边没看到一个合法的类或结构体实例,于是右边喊出的 Z_Order、Tooltip、X、Y、XGap、YGap、X_Size、Y_Size 全部成了未声明标识符。 具体看 406 行:先报「.」左边不是 struct/class,紧接 Z_Order 未声明,再到「)」unexpected token,最后 expression has no effect。407 行复现同一模式,只是把 Z_Order 换成了 Tooltip。 412–413 行的 XGap、YGap 以及 415–416 行的 X_Size、Y_Size 也走同样路径——点左边对象类型缺失,导致一连串成员访问全部失效。 排查时优先确认 406 行那个被「.」调用的对象是否真的实例化了对应类;多数情况是前面漏了类定义引入或把局部变量名写错,改完一处,后面十几条往往会成片消失。外汇与贵金属 EA 开发属高风险活动,编译错误不修正直接跑实盘可能触发异常下单。

别急着下结论

上面这组编译输出来自一次标准库界面组件的改写尝试:Window.mqh 在 416 行连续报出 'Y_Size' 与 'YSize' 的 operator expected,以及 '(' 的 unexpected token,说明宏或变量声明在预处理阶段已被破坏,编译器根本没进到表达式求值。 更麻烦的是 109 到 122 行那一串 undeclared identifier——m_icon_file、m_tooltips_button、m_right_limit、m_is_minimized 全未定义,其中 119 行还叠加了 expression not boolean。这类错误往往不是手误,而是你用的 MT5 终端版本和标准库头文件版本错位,或者私自改了 Element.mqh 的基类却没同步 Window.mqh 的成员。 最终计数器停在 100 errors、11 warnings,编译彻底中断。外汇与贵金属交易用 EA 跑这些界面元素时,任何未通过编译的代码都别挂真仓——高杠杆下连界面初始化失败都可能让订单逻辑漏跑。 遇到满屏红错,先 diff 你改过的头文件和官方原始版行号,别凭直觉删报错行;多数时候补一个成员变量声明比改十处调用更快收敛。

MQL5 / C++
&class="macro">#x27;Y_Size&class="macro">#x27; - some class="kw">operator expected      Window.mqh      class="num">416   class="num">25
&class="macro">#x27;(&class="macro">#x27; - unexpected token   Window.mqh      class="num">416   class="num">31
&class="macro">#x27;YSize&class="macro">#x27; - some class="kw">operator expected        Window.mqh      class="num">416   class="num">12
&class="macro">#x27;m_icon&class="macro">#x27; - parameter conversion not allowed    Window.mqh      class="num">418   class="num">26
   class="type">void CElement::AddToArray(CChartObject&)    Element.mqh   class="num">195   class="num">10
&class="macro">#x27;m_icon_file&class="macro">#x27; - undeclared identifier    Window.mqh      class="num">109   class="num">9
&class="macro">#x27;m_tooltips_button&class="macro">#x27; - undeclared identifier     Window.mqh      class="num">112   class="num">9
&class="macro">#x27;m_right_limit&class="macro">#x27; - undeclared identifier Window.mqh      class="num">115   class="num">9
&class="macro">#x27;m_is_minimized&class="macro">#x27; - undeclared identifier       Window.mqh      class="num">119   class="num">16
expression not boolean   Window.mqh      class="num">119   class="num">16
&class="macro">#x27;m_is_minimized&class="macro">#x27; - undeclared identifier       Window.mqh      class="num">122   class="num">9
class="num">100 errors, class="num">11 warnings         class="num">101   class="num">12

常见问题

多半是窗体和背景在初始化时没逐层创建完就被隐藏了。先确认背景窗口创建成功再挂控件,避免过早调用隐藏。
窗口图标按程序类型自动匹配,EA、指标、脚本用的图标不同,无需手动设,写错程序类型才会显示不对。
可以。把面板代码或截图发给小布,它能对照窗体构造逻辑帮你指出哪一步漏了背景或挂图表时机不对。
这是头文件依赖陷阱,Window.mqh 还依赖其他底层文件。把完整界面库目录放进 Include 路径再编译即可。
可能窗体虽挂上但被图表层盖住或没置顶。验证时用最简单的窗体先挂上去,确认能点再叠复杂控件。