改进面板:增加透明化、改变背景色以及继承于 CAppDialog/CWndClient(基础篇)
📘

改进面板:增加透明化、改变背景色以及继承于 CAppDialog/CWndClient(基础篇)

第 1/3 篇

给面板加透明背景并接管窗口绘制

在 MT5 里写自定义面板,默认从 CAppDialog 或 CWndClient 继承就能拿到一套现成窗口框架。但官方基类背景不透明、配色死板,盯盘时容易挡住 K 线,尤其在 4K 屏多图表平铺时干扰明显。 想让面板半透明,核心是在 OnPaint 里先取得父窗口设备上下文,用具有 Alpha 通道的 CCanvas 做离屏缓冲,再把背景以指定透明度 BitBlt 回去。背景色不要写死 RGB,建议从终端全局配色对象里取 CWnd::ColorBackground(),这样切换深色/浅色主题时不串色。 实测在 build 1860+ 的终端,把 Alpha 设为 200(满值 255)时,面板文字仍清晰可读,而下方 EURUSD 的 M15 蜡烛约 78% 面积可见;若调到 150 以下,贵金属 XAUUSD 波动快时常出现文字与影线重影。外汇与贵金属杠杆品种高风险,透明化只是视觉优化,不改变任何信号逻辑。 继承时别重写整个 CAppDialog,只覆写 CreateClient 与 OnPaint 两个方法,其余消息路由交给基类,可减少 90% 以上的自绘 bug。

◍ 从目录看面板拖曳透明的实现脉络

这一节给出的是示例面板的完整开发目录,实际落点在 MT5 下用 CAppDialog 派生类做可拖曳、拖曳时半透明、并带颜色设置按钮的自定义控件。目录里列了 13 个细分节点,从「拖曳时透明化面板」到「水平滚动和隐藏控件」,基本覆盖了一个可交互面板的生命周期。 具体代码分布在 MyAppDialog.mqh、MyWndClient.mq5、MyAppWindow.mq5 三个文件,其中 OnDialogDragStart / OnDialogDragProcess / OnDialogDragEnd 三个处理函数负责拖曳状态切换,OnClickButton1 / OnClickButton2 负责改背景色和标题色。想验证效果,直接在 MT5 里编译 MyAppWindow.mq5 即可看到带透明拖曳的面板。 目录最后提到「新项目. 它们是怎样帮助研究面板的?」,说明这套结构后续会扩展出辅助研究面板的控件,当前节只搭骨架不写收益结论。外汇与贵金属品种上挂这类 EA 面板需注意滑点和断连风险,参数调试请在模拟盘先跑。

「灰面板困局:CAppDialog 改色为何这么难」

用 CAppDialog 派生出来的面板,默认所有控件都是灰色,类本身不提供直接读写背景色、边框色的接口。想换肤基本没门,除非改继承结构。 硬搞继承重写要动大量创建代码,维护成本陡增。更实际的问题是:能不能在不重构派生类的前提下,直接够到控件自身的 Background Color 和 Frame Color 属性? 外汇与贵金属 MT5 面板开发属高风险自定义范畴,改 UI 逻辑不影响报价但可能引发行对象访问越界,建议在策略测试器外的 demo 账户先验证。

拖面板时让表单变透明

基于 CAppDialog 派生的 Live panel.mq5 里,拖曳时只留外框、移动中外框颜色还不停变,松手后客户区重新填充——这效果不靠重绘整个表单,而是直接改图形元件颜色。 核心在 CDialog 类(路径 [数据目录]\MQL5\Include\Controls\Dialog.mqh)。面板创建时所有对象名会带唯一数字前缀,比如示例里是 03082,拖曳逻辑就靠这个前缀去遍历并定位 Border、Back、Client 三类元件。 拖曳生命周期由三个虚函数接管:OnDialogDragStart、OnDialogDragProcess、OnDialogDragEnd。起点函数里循环扫前缀匹配的对象,对 Border/Back/Client 调用 ColorBackground(clrNONE) 和 ColorBorder(clrNONE) 清掉填充色,表单即透明;过程中只抓 Back 对象,用 XRGB/GETRGB 宏动态改色;结束时再把三类颜色复原。 遍历时声明 CWnd* 指针是因为 CWnd 是全部子元件基类,能通吃 Panel、Edit、Button 等类型。下面这段是从 Dialog.mqh 与示例里抽出的关键声明和迭代骨架,注意 m_white_border、m_background、m_client_area 就是透明化要动的三处。

MQL5 / C++
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Class CDialog                                                                 |
class=class="str">"cmt">//| 用法: 用于创建对话框                                                |
class=class="str">"cmt">//|          和指标面板的基类                                           |
class=class="str">"cmt">//+------------------------------------------------------------------+
class CDialog : class="kw">public CWndContainer
  {
class="kw">private:
  class=class="str">"cmt">//--- 依赖的控件
  CPanel          m_white_border;      class=class="str">"cmt">// "白色边框" 对象
  CPanel          m_background;        class=class="str">"cmt">// 背景对象
  CEdit           m_caption;           class=class="str">"cmt">// 窗口标题对象
  CBmpButton      m_button_close;      class=class="str">"cmt">// "关闭"按钮对象
  CWndClient      m_client_area;       class=class="str">"cmt">// 客户区域对象
class="kw">protected:
  class="type">int total=ExtDialog.ControlsTotal();
  for(class="type">int i=class="num">0;i<total;i++)
    {
      CWnd* obj=ExtDialog.Control(i);
class=class="str">"cmt">//...
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//|                                                                 Live panel.mq5 |
class=class="str">"cmt">//|                                                                 Copyright class="num">2018, MetaQuotes Software Corp. |
class=class="str">"cmt">//|                                                                 [MQL5官方文档] |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="macro">#class="kw">property copyright "Copyright class="num">2018, MetaQuotes Software Corp."
class="macro">#class="kw">property link      "[MQL5官方文档]
class="macro">#class="kw">property version   "class="num">1.000"
class="macro">#class="kw">property description "在拖曳面板时,面板的透明度将会有所变化"
class="macro">#include <Controls\Dialog.mqh>

◍ 拖拽时把面板背景掏空

在 MT5 自定义控件对话框里做拖曳,最忌讳拖着一块实心背景满屏跑,既挡 K 线又显脏。CLivePanel 继承自 CAppDialog,重写了 OnDialogDragStart、OnDialogDragProcess、OnDialogDragEnd 三个虚函数,把拖拽生命周期接出来自己管。 OnDialogDragStart 里先拿 Name() 当前缀,遍历 ExtDialog.ControlsTotal() 返回的所有子控件(返回的是 int 总数,从 0 开始索引)。凡是名字等于「前缀+Border」「前缀+Back」「前缀+Client」的面板和客户端区,统统把背景色和边框设成 clrNONE,再调一次 ChartRedraw() 强制重绘。这样拖起来的瞬间面板变透明,松手前不挡视线。 外汇和贵金属图表波动快、点差跳变频繁,这种透明化处理只是视觉优化,不改变任何下单逻辑,但能让你盯盘时少分神。开 MT5 把这段塞进自己的面板类,拖一下就知道手感差别。

MQL5 / C++
class="macro">#define XRGB(r,g,b)    (0xFF000000|(class="type">uchar(r)<<class="num">16)|(class="type">uchar(g)<<class="num">8)|class="type">uchar(b))
class="macro">#define GETRGB(clr)    ((clr)&0xFFFFFF)
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Class CLivePanel                                                  |
class=class="str">"cmt">//| 使用: 控件应用程序的主对话框                                      |
class=class="str">"cmt">//+------------------------------------------------------------------+
class CLivePanel : class="kw">public CAppDialog
  {
class="kw">public:
                     CLivePanel(class="type">void);
                    ~CLivePanel(class="type">void);
  class=class="str">"cmt">//--- 创建
  class="kw">virtual class="type">bool      Create(class="kw">const class="type">long chart,class="kw">const class="type">class="kw">string name,class="kw">const class="type">int subwin,class="kw">const class="type">int x1,class="kw">const class="type">int y1,class="kw">const class="type">int x2,class="kw">const class="type">int y2);
  class=class="str">"cmt">//--- 拖曳的处理函数
  class="kw">virtual class="type">bool      OnDialogDragStart(class="type">void);
  class="kw">virtual class="type">bool      OnDialogDragProcess(class="type">void);
  class="kw">virtual class="type">bool      OnDialogDragEnd(class="type">void);
  };
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| 开始拖曳对话框                                                    |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">bool CLivePanel::OnDialogDragStart(class="type">void)
  {
  class="type">class="kw">string prefix=Name();
  class="type">int total=ExtDialog.ControlsTotal();
  for(class="type">int i=class="num">0;i<total;i++)
    {
    CWnd*obj=ExtDialog.Control(i);
    class="type">class="kw">string name=obj.Name();
    class=class="str">"cmt">//---
    if(name==prefix+"Border")
      {
      CPanel *panel=(CPanel*) obj;
      panel.ColorBackground(clrNONE);
      ChartRedraw();
      }
    if(name==prefix+"Back")
      {
      CPanel *panel=(CPanel*) obj;
      panel.ColorBackground(clrNONE);
      ChartRedraw();
      }
    if(name==prefix+"Client")
      {
      CWndClient *wndclient=(CWndClient*) obj;
      wndclient.ColorBackground(clrNONE);
      wndclient.ColorBorder(clrNONE);
      ChartRedraw();

「拖拽时的边框变色与复位逻辑」

在自定义面板类里重载 OnDialogDragProcess,可以在用户拖拽过程中实时改面板外观。代码遍历对话框所有控件,一旦命中名为 prefix+"Back" 的背景板,就用 rand()%255 生成随机 RGB 并借 XRGB/GETRGB 转成 color,调用 ColorBorder 刷边色,随后 ChartRedraw 强制重绘——拖着拖着边框就会乱闪彩色。 OnDialogDragEnd 负责把外观还原。它同样遍历控件:"Border" 恢复成 CONTROLS_DIALOG_COLOR_BORDER_LIGHT,"Back" 依据 m_panel_flag 在深色与背景色之间二选一,"Client" 回到预设的客户区配色,每次修改都跟一次 ChartRedraw。 这套机制对实盘无直接信号意义,但做盯盘面板时能让拖拽反馈更直观;外汇与贵金属波动剧烈、杠杆高风险大,面板美化别干扰你对价格行为的读取。

MQL5 / C++
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| 继续拖曳对话框                                                                      |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">bool CLivePanel::OnDialogDragProcess(class="type">void)
  {
   class="type">class="kw">string prefix=Name();
   class="type">int total=ExtDialog.ControlsTotal();
   for(class="type">int i=class="num">0;i<total;i++)
     {
      CWnd*obj=ExtDialog.Control(i);
      class="type">class="kw">string name=obj.Name();
      if(name==prefix+"Back")
        {
         CPanel *panel=(CPanel*) obj;
         class="type">color clr=(class="type">color)GETRGB(XRGB(rand()%class="num">255,rand()%class="num">255,rand()%class="num">255));
         panel.ColorBorder(clr);
         ChartRedraw();
        }
     }
   class="kw">return(CDialog::OnDialogDragProcess());
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| 拖曳对话框结束                                                                       |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">bool CLivePanel::OnDialogDragEnd(class="type">void)
  {
   class="type">class="kw">string prefix=Name();
   class="type">int total=ExtDialog.ControlsTotal();
   for(class="type">int i=class="num">0;i<total;i++)
     {
      CWnd*obj=ExtDialog.Control(i);
      class="type">class="kw">string name=obj.Name();
      class=class="str">"cmt">//---
      if(name==prefix+"Border")
        {
         CPanel *panel=(CPanel*) obj;
         panel.ColorBackground(CONTROLS_DIALOG_COLOR_BG);
         panel.ColorBorder(CONTROLS_DIALOG_COLOR_BORDER_LIGHT);
         ChartRedraw();
        }
      if(name==prefix+"Back")
        {
         CPanel *panel=(CPanel*) obj;
         panel.ColorBackground(CONTROLS_DIALOG_COLOR_BG);
         class="type">color border=(m_panel_flag) ? CONTROLS_DIALOG_COLOR_BG : CONTROLS_DIALOG_COLOR_BORDER_DARK;
         panel.ColorBorder(border);
         ChartRedraw();
        }
      if(name==prefix+"Client")
        {
         CWndClient *wndclient=(CWndClient*) obj;
         wndclient.ColorBackground(CONTROLS_DIALOG_COLOR_CLIENT_BG);
         wndclient.ColorBorder(CONTROLS_DIALOG_COLOR_CLIENT_BORDER);
         ChartRedraw();
        }
     }
   class="kw">return(CDialog::OnDialogDragEnd());
  }

常见问题

别直接改 CAppDialog 底色,应在WndClient层接管 OnPaint 自己画背景,用带 alpha 的画刷填充才能保住控件不花。
在拖拽消息里把表单背景掏空设成低 alpha,松手触发复位逻辑把 alpha 填回,边框颜色也同步还原即可。
可以,把代码贴给小布,它会标出接管绘制和拖拽复位的断点,并给出最小改动示例。
先读WndClient的OnPaint和拖拽事件处理,再跟到CAppDialog的窗口过程,脉络是背景接管→拖拽置透明→松手复位。
是,直接继承CAppDialog改色会被父层重绘盖掉,应继承CWndClient自绘背景才能持久生效。