改进面板:增加透明化、改变背景色以及继承于 CAppDialog/CWndClient(基础篇)
给面板加透明背景并接管窗口绘制
在 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 就是透明化要动的三处。
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 把这段塞进自己的面板类,拖一下就知道手感差别。
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。 这套机制对实盘无直接信号意义,但做盯盘面板时能让拖拽反馈更直观;外汇与贵金属波动剧烈、杠杆高风险大,面板美化别干扰你对价格行为的读取。
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()); }