图形界面 IX: 颜色选择器控件 (第一章)·综合运用
调色板控件的事件分发逻辑
做 MT5 自定义颜色选择器时,事件入口只有一个 CColorPicker::OnEvent(),内部按事件 ID 拆成 6 块分支,分别调 OnHoverColor、OnClickPalette、OnClickRadioButton、OnEndEdit、OnClickButtonOK、OnClickButtonCancel。理清这六个方法的职责,才能改得动交互细节。 OnHoverColor 只在鼠标落在调色板画布内才工作:先拿 m_canvas.MouseFocus() 判焦点,脱焦直接 return false;命中后用 x−m_canvas.X()、y−m_canvas.Y() 把屏幕坐标转本地像素,再用 ColorToARGB(m_canvas.PixelGet(lx,ly),0) 取色。取出的 m_hover_color 会同时写进标记对象和相关 tooltip,格式走 ColorToString 成 RGB 串。 点击调色板由 OnClickPalette 接手,先比对 clicked_object 与画布名,不一致就退出;一致则把悬停色固化为 m_picked_color,刷新已选标记,最后调 SetComponents() 按当前单选模型反算各分量。OnClickRadioButton 则双检 id 与按钮文字,过检后 DrawPalette(button_index) 重绘整套色板。 输入框改值走 OnEndEdit,只验元件 ID,随后依所选模型重算所有颜色空间分量;OK / Cancel 两个按钮里,Cancel 会判表单是否对话框类型再关窗,OK 当前版仅存为当前色,后续文章还会动它。另需注意 FastSwitching() 在编辑框快调时也要触发调色板重绘,其实现在附件文件里,不是标准库那套只换数值的逻辑。
class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| 取得鼠标光标所在处的颜色 | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">bool CColorPicker::OnHoverColor(class="kw">const class="type">int x,class="kw">const class="type">int y) { class=class="str">"cmt">//--- 如果焦点不在调色板上,退出 if(!m_canvas.MouseFocus()) class="kw">return(false); class=class="str">"cmt">//--- 定义鼠标光标处调色板的颜色 class="type">int lx =x-m_canvas.X(); class="type">int ly =y-m_canvas.Y(); m_hover_color=(class="type">color)::ColorToARGB(m_canvas.PixelGet(lx,ly),class="num">0); class=class="str">"cmt">//--- 在相关位置(标记)设置颜色和工具提示 m_hover.Color(m_hover_color); m_hover.BackColor(m_hover_color); m_hover.Tooltip(::ColorToString(m_hover_color)); class=class="str">"cmt">//--- 设置调色板的工具提示 m_canvas.Tooltip(::ColorToString(m_hover_color)); class="kw">return(true); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| 处理在调色板上的点击 | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">bool CColorPicker::OnClickPalette(class="kw">const class="type">class="kw">string clicked_object) { class=class="str">"cmt">//--- 如果对象名称不匹配,退出 if(clicked_object!=m_canvas.Name()) class="kw">return(false); class=class="str">"cmt">//--- 在相关位置(标记)设置颜色和工具提示 m_picked_color=m_hover_color; m_picked.Color(m_picked_color); m_picked.BackColor(m_picked_color); m_picked.Tooltip(::ColorToString(m_picked_color)); class=class="str">"cmt">//--- 根据所选的单选按钮计算和设置颜色组分值 SetComponents(); class="kw">return(true); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| 处理点击单选按钮 | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">bool CColorPicker::OnClickRadioButton(class="kw">const class="type">long id,class="kw">const class="type">int button_index,class="kw">const class="type">class="kw">string button_text) { class=class="str">"cmt">//--- 如果ID不匹配,退出 if(id!=CElement::Id()) class="kw">return(false); class=class="str">"cmt">//--- 如果单选按钮文字不匹配,退出 if(button_text!=m_radio_buttons.SelectedButtonText()) class="kw">return(false); class=class="str">"cmt">//--- 根据最新的变化更新调色板 DrawPalette(button_index); class="kw">return(true); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| 处理在编辑框中输入新的数值 | class=class="str">"cmt">//+------------------------------------------------------------------+
◍ 拾色器控件的事件回调怎么写
在 MT5 自定义 GUI 里,CColorPicker 这类控件靠几个布尔型回调把用户操作接住。OnEndEdit 先比对 id 是否等于 CElement::Id(),不匹配直接返回 false,匹配才调用 SetComponents 刷新各颜色模型的组分。 OnClickButtonOK 用 clicked_object 和 m_button_ok.Text() 做字符串比对,一致就把临时选色 m_picked_color 赋给 m_current_color,同时改背景色并写 tooltip。注意这里用的是 ::ColorToString 全局函数,不是成员方法。 取消键逻辑里有个判断:当 m_wnd.WindowType()==W_DIALOG 时才调 m_wnd.CloseDialogBox() 关窗,说明非对话框形态下取消键不自动销毁窗口。 鼠标移动事件走 CHARTEVENT_MOUSE_MOVE,从 lparam/dparam 拆出 x、y 整型坐标,sparam 转成 m_mouse_state 布尔值;可见性判断用 !CElement::IsVisible() 直接 return。画布焦点范围比控件本身少 1 像素(X2()-1、Y2()-1),这是为了避免右和下边缘的拾取重叠。
class="type">bool CColorPicker::OnEndEdit(class="kw">const class="type">long id,class="kw">const class="type">int button_index) { class=class="str">"cmt">//--- 如果ID不匹配,退出 if(id!=CElement::Id()) class="kw">return(false); class=class="str">"cmt">//--- 计算和设置所有颜色模型的颜色组分 SetComponents(button_index,false); class="kw">return(true); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| 处理点击了 &class="macro">#x27;OK&class="macro">#x27; 按钮 | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">bool CColorPicker::OnClickButtonOK(class="kw">const class="type">class="kw">string clicked_object) { class=class="str">"cmt">//--- 如果对象名称不匹配,退出 if(clicked_object!=m_button_ok.Text()) class="kw">return(false); class=class="str">"cmt">//--- 保存所选的颜色 m_current_color=m_picked_color; m_current.BackColor(m_current_color); m_current.Tooltip(::ColorToString(m_current_color)); class="kw">return(true); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| 处理点击 &class="macro">#x27;Cancel&class="macro">#x27; 按钮的事件 | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">bool CColorPicker::OnClickButtonCancel(class="kw">const class="type">class="kw">string clicked_object) { class=class="str">"cmt">//--- 如果对象名称不匹配,退出 if(clicked_object!=m_button_cancel.Text()) class="kw">return(false); class=class="str">"cmt">//--- 如果是对话框就关闭窗口 if(m_wnd.WindowType()==W_DIALOG) m_wnd.CloseDialogBox(); class=class="str">"cmt">//--- class="kw">return(true); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| 图表事件处理函数 | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void CColorPicker::OnEvent(class="kw">const class="type">int id,class="kw">const class="type">long &lparam,class="kw">const class="type">class="kw">double &dparam,class="kw">const class="type">class="kw">string &sparam) { class=class="str">"cmt">//--- 处理移动鼠标的事件 if(id==CHARTEVENT_MOUSE_MOVE) { class=class="str">"cmt">//--- 如果控件是隐藏的,退出 if(!CElement::IsVisible()) class="kw">return; class=class="str">"cmt">//--- 坐标以及鼠标左键的状态 class="type">int x=(class="type">int)lparam; class="type">int y=(class="type">int)dparam; m_mouse_state=(class="type">bool)class="type">int(sparam); CElement::MouseFocus(x>X() && x<X2() && y>Y() && y<Y2()); m_canvas.MouseFocus(x>m_canvas.X() && x<m_canvas.X2()-class="num">1 && y>m_canvas.Y() && y<m_canvas.Y2()-class="num">1); class=class="str">"cmt">//--- 取得鼠标光标所在的颜色 if(OnHoverColor(x,y)) class="kw">return; class=class="str">"cmt">//--- class="kw">return; } class=class="str">"cmt">//--- 处理鼠标左键点击对象的事件 if(id==CHARTEVENT_OBJECT_CLICK) { class=class="str">"cmt">//--- 如果点击了调色板 if(OnClickPalette(sparam)) class="kw">return; class=class="str">"cmt">//--- class="kw">return; } class=class="str">"cmt">//--- 处理在编辑框中输入数值
「图表控件事件的路由分发写法」
在 MT5 自定义面板里,所有交互都走 CHARTEVENT_CUSTOM 这条通道,靠偏移常量区分事件类型。上面这段把编辑完成、标签点击、数值增减、按钮点击四类事件做了集中路由,每个分支先调具体处理函数,返回真就直接 return,避免后续逻辑误触发。 注意 ON_CLICK_INC 和 ON_CLICK_DEC 共用了 OnEndEdit 做数值回写,说明加减按钮本质也是改编辑框值,只是入口事件不同。按钮分支里先用 lparam!=CElement::Id() 拦掉非本面板控件,再分 OK / CANCEL 派发,少这层判断可能在多实例面板下串事件。 实盘接这套逻辑时,外汇与贵金属波动快、点差跳变频繁,面板回写参数后最好加一次校验再下单,否则高波动段可能把脏值送进 EA。打开 MT5 把这段塞进你的 ChartEvent 回调,改几个 Id 常量就能跑起来验证路由是否漏事件。
if(id==CHARTEVENT_CUSTOM+ON_END_EDIT) { class=class="str">"cmt">//--- 检查输入的新数值 if(OnEndEdit(lparam,(class="type">int)dparam)) class="kw">return; class=class="str">"cmt">//--- class="kw">return; } class=class="str">"cmt">//--- 处理点击控件事件 if(id==CHARTEVENT_CUSTOM+ON_CLICK_LABEL) { class=class="str">"cmt">//--- 如果点击了单选按钮 if(OnClickRadioButton(lparam,(class="type">int)dparam,sparam)) class="kw">return; class=class="str">"cmt">//--- class="kw">return; } class=class="str">"cmt">//--- 处理点击切换编辑框 if(id==CHARTEVENT_CUSTOM+ON_CLICK_INC || id==CHARTEVENT_CUSTOM+ON_CLICK_DEC) { class=class="str">"cmt">//--- 检查输入的新数值 if(OnEndEdit(lparam,(class="type">int)dparam)) class="kw">return; class=class="str">"cmt">//--- class="kw">return; } class=class="str">"cmt">//--- 处理点击控件按钮 if(id==CHARTEVENT_CUSTOM+ON_CLICK_BUTTON) { class=class="str">"cmt">//--- 如果ID不匹配 if(lparam!=CElement::Id()) class="kw">return; class=class="str">"cmt">//--- 如果点击了 "OK" 按钮 if(OnClickButtonOK(sparam)) class="kw">return; class=class="str">"cmt">//--- 如果点击了 "CANCEL" 按钮 if(OnClickButtonCancel(sparam)) class="kw">return; class=class="str">"cmt">//--- class="kw">return; } }
把调色板挂到按钮上
颜色选择器类已经具备基础能力,但还缺一个能弹出调色板并回显当前颜色的触发件。做法是写一个 CColorButton 类,它在控件区放文字标签和颜色指示块,按钮区则直接以 RGB 字符串显示所选颜色,点击后拉起关联的调色板窗口。 要让按钮和调色板互相认识,得在 ColorPicker.mqh 里 include ColorButton.mqh,并在 CColorPicker 中声明 CColorButton *m_color_button 私有成员保存按钮指针。随后提供一个公有方法 ColorButtonPointer() 来存储指针:存指针的同时把调色板所有标记设为按钮当前色,并立即 Show() 打开窗口。 取色确认逻辑落在 CColorPicker::OnClickButtonOK() 中。若按钮指针非空,就把调色板选中色写回按钮、关掉窗口,并推送 ON_CHANGE_COLOR 事件(需在 Defines.mqh 新增该 ID),消息体带控件 ID、元件索引和按钮文字,方便在事件处理函数里定位来源;指针为空则在终端日志留一条开发者提示。 按钮自身用 CurrentColor() 的 getter/setter 管理颜色,setter 会同步刷新按钮上的颜色指示与 RGB 文字。整套互操作准备好后,开 MT5 挂上 ColorButton.mqh 与 ColorPicker.mqh 即可实测弹窗取色链路。
class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| ColorPicker.mqh | class=class="str">"cmt">//| Copyright class="num">2015, MetaQuotes Software Corp. | class=class="str">"cmt">//| [MQL5官方文档] | class=class="str">"cmt">//+------------------------------------------------------------------+ class="macro">#include "Element.mqh" class="macro">#include "Window.mqh" class="macro">#include "SpinEdit.mqh" class="macro">#include "SimpleButton.mqh" class="macro">#include "RadioButtons.mqh" class="macro">#include "ColorButton.mqh" class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| 用于创建颜色选择器的类 | class=class="str">"cmt">//+------------------------------------------------------------------+ class CColorPicker : class="kw">public CElement { class="kw">private: class=class="str">"cmt">//--- 调用控件用于选择颜色的按钮指针 CColorButton *m_color_button; }; class CColorPicker : class="kw">public CElement { class="kw">public: class=class="str">"cmt">//--- 保存调用颜色调色板的按钮指针 class="type">void ColorButtonPointer(CColorButton &object); }; class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| 保存调用调色板按钮的指针并且 | class=class="str">"cmt">//| 打开相关连的调色板窗口 | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void CColorPicker::ColorButtonPointer(CColorButton &object) { class=class="str">"cmt">//--- 保存按钮的指针 m_color_button=::GetPointer(object); class=class="str">"cmt">//--- 把发送按钮的颜色设置给所有的调色板标记 CurrentColor(object.CurrentColor()); class=class="str">"cmt">//--- 打开关联的调色板窗口 m_wnd.Show(); } class CColorButton : class="kw">public CElement { class="kw">public: class=class="str">"cmt">//--- 返回/设置当前颜色参数 class="type">color CurrentColor(class="type">void) class="kw">const { class="kw">return(m_current_color); } class="type">void CurrentColor(class="kw">const class="type">color clr); }; class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| 修改当前颜色参数 | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void CColorButton::CurrentColor(class="kw">const class="type">color clr) {
◍ 正文
m_current_color=clr; m_button_icon.BackColor(clr); m_button_label.Description(::<span class="functions">ColorToString</span>(clr)); } <span class="comment">//+------------------------------------------------------------------+</span>
| <span class="comment">// | 处理点击了 'OK' 按钮 | </span> |
|---|
<span class="comment">//+------------------------------------------------------------------+</span> <span class="keyword">bool</span> CColorPicker::OnClickButtonOK(<span class="keyword">const</span> <span class="keyword">string</span> clicked_object) { <span class="comment">//--- 如果对象名称不匹配,退出</span> <span class="keyword">if</span>(clicked_object!=m_button_ok.Text()) <span class="keyword">return</span>(<span class="macro">false</span>); <span class="comment">//--- 保存所选的颜色</span> m_current_color=m_picked_color; m_current.BackColor(m_current_color); m_current.Tooltip(::<span class="functions">ColorToString</span>(m_current_color)); <span class="comment">//--- 如果有调用颜色选择窗口的按钮的指针</span> <span style="background-color:rgb(255, 242, 153);"> <span class="keyword">if</span>(::<span class="functions">CheckPointer</span>(m_color_button)!=<span class="macro">POINTER_INVALID</span>) {
「用五个按钮共用一个调色板窗口」
拿之前任一篇的 EA 做底,复制一份只留主菜单和状态串,在主窗口 W_MAIN 里塞 5 个按钮去调同一个调色板对话框 W_DIALOG。整个 MQL 应用只需建一个 CColorPicker 实例,每次点按钮弹出的都是这同一个窗口,差别只在把被点按钮的指针独立送进 CColorPicker。 自定义类 CProgram 里要声明:第二个窗口对象 m_window2(类型 CWindow)、5 个 CColorButton、1 个 CColorPicker,以及对应的创建方法和边距设置。5 个按钮的创建逻辑几乎一致,仅初始颜色和描述文字不同,按钮自身会回显所选颜色。 CreateColorPicker() 里关键的一步是保存对话框窗口指针,控件库加控件时得传关联窗口索引,本例对话框索引为 [1]。点按钮时 OnEvent() 收到 ON_CLICK_BUTTON 消息,消息带按钮描述文本,据此判断该把哪个 CColorButton 对象喂给调色板,随后窗口打开、按钮色同步到所有标记。 选色后点调色板 OK,触发 ON_CHANGE_COLOR 消息做落地处理。编译挂到图表,实测主窗口 5 个按钮均能唤起同一选择器,运行符合预期。外汇与贵金属图表上做这类 GUI 改动仍属高风险环境,参数误用可能导致面板异常。
class CProgram : class="kw">public CWndEvents { class="kw">private: class=class="str">"cmt">//--- 表单 class="num">2 - 含有用于选择颜色的调色板的窗口 CWindow m_window2; class=class="str">"cmt">//--- 用于调用调色板窗口的按钮 CColorButton m_color_button1; CColorButton m_color_button2; CColorButton m_color_button3; CColorButton m_color_button4; CColorButton m_color_button5; class=class="str">"cmt">//--- 颜色调色板 CColorPicker m_color_picker; class=class="str">"cmt">//--- class="kw">private: class=class="str">"cmt">//--- 表单 class="num">2 class="type">bool CreateWindow2(class="kw">const class="type">class="kw">string text); class=class="str">"cmt">//--- 用于调用调色板的按钮 class="macro">#define COLORBUTTON1_GAP_X(class="num">7) class="macro">#define COLORBUTTON1_GAP_Y(class="num">50) class="type">bool CreateColorButton1(class="kw">const class="type">class="kw">string text); class="macro">#define COLORBUTTON2_GAP_X(class="num">7) class="macro">#define COLORBUTTON2_GAP_Y(class="num">75) class="type">bool CreateColorButton2(class="kw">const class="type">class="kw">string text); class="macro">#define COLORBUTTON3_GAP_X(class="num">7) class="macro">#define COLORBUTTON3_GAP_Y(class="num">100) class="type">bool CreateColorButton3(class="kw">const class="type">class="kw">string text); class="macro">#define COLORBUTTON4_GAP_X(class="num">7) class="macro">#define COLORBUTTON4_GAP_Y(class="num">125) class="type">bool CreateColorButton4(class="kw">const class="type">class="kw">string text); class="macro">#define COLORBUTTON5_GAP_X(class="num">7) class="macro">#define COLORBUTTON5_GAP_Y(class="num">150) class="type">bool CreateColorButton5(class="kw">const class="type">class="kw">string text); class=class="str">"cmt">//--- 颜色调色板 class="macro">#define COLORPICKER_GAP_X(class="num">1) class="macro">#define COLORPICKER_GAP_Y(class="num">20) class="type">bool CreateColorPicker(class="type">void); }; class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| 创建调用调色板的按钮1 | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">bool CProgram::CreateColorButton1(class="kw">const class="type">class="kw">string text) { class=class="str">"cmt">//--- 保存窗口的指针 m_color_button1.WindowPointer(m_window1); class=class="str">"cmt">//--- 坐标 class="type">int x=m_window1.X()+COLORBUTTON1_GAP_X; class="type">int y=m_window1.Y()+COLORBUTTON1_GAP_Y;
调色板控件的创建与窗口挂载
在 MT5 自定义 GUI 里做颜色选择器,先得把 CColorButton 的几何与配色定死再 Create。下面这段把按钮区设为 195×18、可点区域 100×18,背景用 C'220,220,220'、当前色直接给 clrRed,意味着用户一打开就看到红色被选中,省去初始化空值。 m_color_button1.XSize(195); // 按钮整体宽度 195 像素 m_color_button1.YSize(18); // 按钮整体高度 18 像素 m_color_button1.ButtonXSize(100); // 可点击区域宽 100 m_color_button1.ButtonYSize(18); // 可点击区域高 18 m_color_button1.AreaColor(clrWhiteSmoke); // 显示区底色 m_color_button1.LabelColor(clrBlack); // 文字颜色 m_color_button1.BackColor(C'220,220,220'); // 背景灰 m_color_button1.BorderColor(clrSilver); // 边框银色 m_color_button1.CurrentColor(clrRed); // 当前选中红 if(!m_color_button1.CreateColorButton(m_chart_id,m_subwin,text,x,y)) return(false); // 创建失败直接退出 CWndContainer::AddToElementsArray(0,m_color_button1); // 挂到元素库 0 号位 return(true); 调色板窗口本身用 W_DIALOG 类型,尺寸 350×286,边框和标题栏都用 clrLightSteelBlue。注意图标走 #resource 预编译指令嵌 bmp16/color_picker.bmp,运行时用 IconFile() 指同一路径,少一个都会让窗口缺图标。 m_window2.WindowType(W_DIALOG); // 对话框样式 m_window2.XSize(350); // 宽 350 m_window2.YSize(286); // 高 286 m_window2.WindowBorderColor(clrLightSteelBlue); // 边框色 m_window2.IconFile("Images\\EasyAndFastGUI\\Icons\\bmp16\\color_picker.bmp"); // 图标 CreateColorPicker() 里用 m_color_picker.WindowPointer(m_window2) 把调色板绑到窗口2,坐标靠 COLORPICKER_GAP_X/Y 偏移,最后 AddToElementsArray(1,...) 进 1 号位。外汇贵金属面板涉及下单界面,MT5 自定义控件出错可能让 EA 卡死,实盘前务必在策略测试器用历史数据跑一遍窗体创建。
class=class="str">"cmt">//--- 在创建之前设置属性 m_color_button1.XSize(class="num">195); m_color_button1.YSize(class="num">18); m_color_button1.ButtonXSize(class="num">100); m_color_button1.ButtonYSize(class="num">18); m_color_button1.AreaColor(clrWhiteSmoke); m_color_button1.LabelColor(clrBlack); m_color_button1.BackColor(C&class="macro">#x27;class="num">220,class="num">220,class="num">220&class="macro">#x27;); m_color_button1.BorderColor(clrSilver); m_color_button1.CurrentColor(clrRed); class=class="str">"cmt">//--- 创建元件 if(!m_color_button1.CreateColorButton(m_chart_id,m_subwin,text,x,y)) class="kw">return(false); class=class="str">"cmt">//--- 把控件指针加到库中 CWndContainer::AddToElementsArray(class="num">0,m_color_button1); class="kw">return(true); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| 创建用于调色板的表单 class="num">2 | class=class="str">"cmt">//+------------------------------------------------------------------+ class="macro">#resource "\\Images\\EasyAndFastGUI\\Icons\\bmp16\\color_picker.bmp" class=class="str">"cmt">//--- class="type">bool CProgram::CreateWindow2(class="kw">const class="type">class="kw">string caption_text) { class=class="str">"cmt">//--- 保存窗口的指针 CWndContainer::AddWindow(m_window2); class=class="str">"cmt">//--- 坐标 class="type">int x=(m_window2.X()>class="num">0) ? m_window2.X() : class="num">30; class="type">int y=(m_window2.Y()>class="num">0) ? m_window2.Y() : class="num">30; class=class="str">"cmt">//--- 属性 m_window2.Movable(true); m_window2.XSize(class="num">350); m_window2.YSize(class="num">286); m_window2.WindowType(W_DIALOG); m_window2.WindowBgColor(clrWhiteSmoke); m_window2.WindowBorderColor(clrLightSteelBlue); m_window2.CaptionBgColor(clrLightSteelBlue); m_window2.CaptionBgColorHover(clrLightSteelBlue); m_window2.IconFile("Images\\EasyAndFastGUI\\Icons\\bmp16\\color_picker.bmp"); class=class="str">"cmt">//--- 创建一个表单 if(!m_window2.CreateWindow(m_chart_id,m_subwin,caption_text,x,y)) 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::CreateColorPicker(class="type">void) { class=class="str">"cmt">//--- 保存窗口的指针 m_color_picker.WindowPointer(m_window2); class=class="str">"cmt">//--- 坐标 class="type">int x=m_window2.X()+COLORPICKER_GAP_X; class="type">int y=m_window2.Y()+COLORPICKER_GAP_Y; class=class="str">"cmt">//--- 创建控件 if(!m_color_picker.CreateColorPicker(m_chart_id,m_subwin,x,y)) class="kw">return(false); class=class="str">"cmt">//--- 把控件指针加到库中 CWndContainer::AddToElementsArray(class="num">1,m_color_picker); class="kw">return(true); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| 事件处理函数 | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void CProgram::OnEvent(class="kw">const class="type">int id,class="kw">const class="type">long &lparam,class="kw">const class="type">class="kw">double &dparam,class="kw">const class="type">class="kw">string &sparam) { class=class="str">"cmt">//--- 按钮点击事件
◍ 调色板与按钮的事件联动写法
在 MT5 自定义面板里,想让调色板跟着某个颜色按钮走,核心是把按钮点击和颜色变更拆成两个自定义事件来接。 第一段逻辑处理 CHARTEVENT_CUSTOM+ON_CLICK_BUTTON:用 sparam 比对各按钮的 LabelText(),命中哪个就把该按钮指针交给 m_color_picker.ColorButtonPointer(),随后直接 return。五个按钮的写法完全同构,只是 m_color_button1 到 m_color_button5 的变量名轮换。 第二段在 OnEvent() 里接 CHARTEVENT_CUSTOM+ON_CHANGE_COLOR:先判 lparam==m_color_picker.Id() 确认事件源,再用 sparam 匹配按钮标签,决定具体改哪一号按钮的颜色。 [CODE] 段里逐行看:if(id==CHARTEVENT_CUSTOM+ON_CLICK_BUTTON) 是拦截点击自定义按钮事件;if(sparam==m_color_button1.LabelText()) 判断是不是按了按钮1;m_color_picker.ColorButtonPointer(m_color_button1) 把调色板绑定到该按钮;return 跳出避免继续判其他按钮。第二段 if(lparam==m_color_picker.Id()) 则保证只处理调色板自己发出的变更信号,不会误吞别的控件消息。 开 MT5 建个带 5 个 CButton 和 CColorPicker 的 EA,把这段直接粘进 OnEvent,跑起来点不同按钮,调色板焦点会切到对应按钮,验证联动是否生效。外汇与贵金属面板定制属高风险环境,代码未做异常处理,实盘前需自测控件生命周期。
if(id==CHARTEVENT_CUSTOM+ON_CLICK_BUTTON) { class=class="str">"cmt">//--- 如果点击了按钮 class="num">1 if(sparam==m_color_button1.LabelText()) { m_color_picker.ColorButtonPointer(m_color_button1); class="kw">return; } class=class="str">"cmt">//--- 如果点击了按钮2 if(sparam==m_color_button2.LabelText()) { m_color_picker.ColorButtonPointer(m_color_button2); class="kw">return; } class=class="str">"cmt">//--- 如果点击了按钮3 if(sparam==m_color_button3.LabelText()) { m_color_picker.ColorButtonPointer(m_color_button3); class="kw">return; } class=class="str">"cmt">//--- 如果点击了按钮4 if(sparam==m_color_button4.LabelText()) { m_color_picker.ColorButtonPointer(m_color_button4); class="kw">return; } class=class="str">"cmt">//--- 如果点击了按钮5 if(sparam==m_color_button5.LabelText()) { m_color_picker.ColorButtonPointer(m_color_button5); class="kw">return; } } class="type">void CProgram::OnEvent(class="kw">const class="type">int id,class="kw">const class="type">long &lparam,class="kw">const class="type">class="kw">double &dparam,class="kw">const class="type">class="kw">string &sparam) { class=class="str">"cmt">//--- 使用调色板改变颜色的事件 if(id==CHARTEVENT_CUSTOM+ON_CHANGE_COLOR) { class=class="str">"cmt">//---如果控件ID匹配 if(lparam==m_color_picker.Id()) { class=class="str">"cmt">//--- 如果回应来自按钮1 if(sparam==m_color_button1.LabelText()) { class=class="str">"cmt">//--- 根据按钮1改变颜色... class="kw">return; } class=class="str">"cmt">//--- 如果回应来自按钮2 if(sparam==m_color_button2.LabelText()) { class=class="str">"cmt">//--- 根据按钮2改变颜色... class="kw">return; } class=class="str">"cmt">//--- 如果回应来自按钮3 if(sparam==m_color_button3.LabelText()) { class=class="str">"cmt">//--- 根据按钮3改变颜色... class="kw">return; } class=class="str">"cmt">//--- 如果回应来自按钮4 if(sparam==m_color_button4.LabelText())
「按钮事件里的颜色分支怎么写」
在 MT5 的图表事件回调里,靠 sparam 比对按钮标签文本来区分点击源是最直接的做法。上面这段片段只处理了按钮4和按钮5的响应,二者逻辑对称:命中标签就改对应对象颜色然后 return,避免继续往下跑。 实际调试时建议把 m_color_button4.LabelText() 和 m_color_button5.LabelText() 打印到 Experts 日志,确认字符串没被多语言资源或空格截断。外汇与贵金属图表上挂这类面板属于高频交互,点错对象改错色会干扰判线,实盘前务必在策略测试器里用历史 tick 跑一遍点击事件。 若你自己的面板按钮超过五个,别照抄这种 if 平铺,改用 map 存标签到回调的映射,否则 OnChartEvent 会越来越臃肿。
{
class=class="str">"cmt">//--- 根据按钮4改变对象颜色...
class="kw">return;
}
class=class="str">"cmt">//--- 如果回应来自按钮5
if(sparam==m_color_button5.LabelText())
{
class=class="str">"cmt">//--- 根据按钮5改变对象颜色...
class="kw">return;
}
}
class="kw">return;
}调色板之外的下一步
这一节把颜色选择器和一个能唤起调色板的特殊按钮做进了图形库,MT5 用户现在可以直接在 EA 或指标的界面里改对象颜色,不用为了换根线色重启程序。 库里附带的两个压缩包 easyandfastgui_mql4.zip(353.29 KB)与 easyandfastgui_mql5.zip(353.27 KB)可下到本地,挂载后能在画布上看到调色板实际响应。外汇与贵金属 GUI 自动化涉及实盘逻辑,属高风险操作,建议先于策略测试器验证。 后续章节会补进度条和线形图表类;想自己扩库的,现在就可以把这两份文件解压进 MQL5/Include 试编译。