English Русский Español Deutsch 日本語
preview
在MQL5中创建交易管理员面板(第三部分):扩展内置类以进行主题管理(II)

在MQL5中创建交易管理员面板(第三部分):扩展内置类以进行主题管理(II)

MetaTrader 5示例 | 29 五月 2025, 14:53
290 0
Clemence Benjamin
Clemence Benjamin

内容:


引言

我们可以修改并为MQL5创建新的类库。然而,由于内置库由平台共享,我们对这些文件所做的任何修改,都可能对当前平台的功能产生正面或负面影响。在最近文章中,我们简要讨论了如何编辑根对话框类颜色,以控制我们面板的外观。尽管主题切换按钮成功地改变了文本颜色,但它并没有改变面板皮肤或按钮背景颜色。

通过研究,我们终于确定了将主题更改功能安全地整合到现有类中的方法。在成功实现这些更改后,我们调整了管理员面板算法,使其与新整合的功能保持一致。

新的面板主题

主题切换成功

今天的讨论重点是我们为实现右侧显示的美观面板所采取的步骤。所展示的主题颜色是基于我在开发过程中对颜色选择的看法;它们可以在代码中进行优化,以适应其他用户的偏好,允许您尝试不同的颜色,以找到与您产生共鸣的颜色。重要的是要突出我们程序的关键组成部分,这些部分有助于面板的整体功能。

我将列出它们:

  • 文本颜色
  • 按钮皮肤颜色
  • 边框
  • 背景颜色

本质上,这些是我们程序中最显而易见的特征。当我们触发主题更改时,每个组件都必须通过更改其显示属性来响应,以展示代码中定义的所需颜色。在本次讨论结束时,我们旨在赋予您在处理界面时修改和扩展现有类所需的技能,正如本项目所展示的那样。 

了解MQL5中的类。

为了确保专家和新手都能跟上,我想先让大家熟悉MQL5中使用的类的概念。以下是定义和关键概念,将帮助我们理解类在这个编程环境中的功能。

类: 

类是MQL5中面向对象编程(OOP)的基础,允许开发人员将相关的变量(属性)和函数(方法)组合成一个单元,以在程序中表示复杂的概念和行为。

将一个类分解为两个部分:

  1. 属性:存储类对象的状态或数据的变量。
  2. 方法:定义类对象的行为或动作的函数。

类的主要特征概述:

  • 封装是指将数据(变量)和操作这些数据的方法(函数)捆绑在一起,确保它们受到保护,防止外部访问和滥用。
  • 继承允许一个类从另一个类继承属性和方法,促进代码重用并创建层次结构。
  • 多态性允许方法重写,允许子类为其父类中已经定义的方法提供特定的实现。
  • 抽象通过仅关注相关数据和方法,隐藏不必要的细节,简化复杂系统的建模。
要访问包含对我们项目有用的GUI类的MetaQuotes头文件,请参考下面的图像,它说明了我们如何定位这些文件。

定位MQ头文件

定位MQL5头文件


我使用了一个典型的MQL5类源代码片段,以帮助我们从实践角度清楚地理解类及其结构。请参阅下面的代码片段,我在表格形式中对其结构进行了说明。

//Basic parts of a class.

class CDialog : public CWndContainer
{
public:
   // Constructor and Destructor (Methods)
   CDialog(void);   // Constructor
   ~CDialog(void);  // Destructor

   // Public Methods (Functions)
   virtual bool Create(const long chart, const string name, const int subwin, const int x1, const int y1, const int x2, const int y2);
   virtual bool OnEvent(const int id, const long &lparam, const double &dparam, const string &sparam);
   string Caption(void) const;
   bool Caption(const string text);
   bool Add(CWnd *control);   // Add control by pointer
   bool Add(CWnd &control);   // Add control by reference
   virtual bool Save(const int file_handle);
   virtual bool Load(const int file_handle);
   void UpdateThemeColors(bool darkTheme);

protected:
   // Attributes (Variables)
   bool m_panel_flag;        // Panel visibility flag
   bool m_minimized;         // Minimized state flag
   CWnd m_caption;           // Caption control
   CWnd m_client_area;       // Client area control
   CRect m_norm_rect;        // Normal (non-minimized) rectangle
   CRect m_min_rect;         // Minimized rectangle
   CWnd m_white_border;      // White border control

   // Protected Methods (Internal functions)
   virtual bool CreateWhiteBorder(void);
   virtual bool CreateBackground(void);
   virtual bool CreateCaption(void);
   virtual bool CreateButtonClose(void);
   virtual bool CreateClientArea(void);
   virtual void OnClickCaption(void);
   virtual void OnClickButtonClose(void);
   virtual bool OnDialogDragStart(void);
   virtual bool OnDialogDragProcess(void);
   virtual bool OnDialogDragEnd(void);
};

该表是对上述代码片段中给出的属性及其描述的总结。

属性(特性) 说明
bool m_panel_flag;

 用于指示面板是否可见的标志。
bool m_minimized;

 用于指示对话框是否最小化的标志。
CWnd m_caption;

标题文本的控件。
CWnd m_client_area;

用于存放其他元素的客户区域控件。
CRect m_norm_rect;

正常(非最小化)状态的坐标。
CRect m_min_rect;

最小化状态的坐标。
CWnd m_white_border;

对话框周围白色边框的控件。

该表总结了示例类代码中使用的方法。

 方法 说明
CDialog(void)
初始化对话框的构造函数。
~CDialog(void)
用于清理资源的析构函数。
Create(...)

创建对话框窗口及其控件。
 
OnEvent(...)

  处理对话框的图表事件。
Caption(void)

返回当前标题文本。
Caption(const string text)

设置标题文本。
Add(CWnd *control)

通过指针将控件添加到客户区域。
Add(CWnd &control)

通过引用将控件添加到客户区域。
Save(const int file_handle)

将对话框状态保存到文件。
Load(const int file_handle)

从文件加载对话框状态。
UpdateThemeColors(bool darkTheme)

更新主题颜色(深色或浅色)。
CreateWhiteBorder(void)
为对话框创建白色边框。
 
CreateBackground(void)
   创建对话框的背景。
 
CreateCaption(void)

   创建标题区域。
 
CreateButtonClose(void)
创建关闭按钮。   
 
CreateClientArea(void)

  创建用户区域。
 
OnClickCaption(void)
   处理标题点击事件。
 
OnClickButtonClose(void)

   处理关闭按钮点击事件。
 
OnDialogDragStart(void)

   处理对话框拖动开始事件。
 
OnDialogDragProcess(void)

  处理对话框拖动过程。
 
OnDialogDragEnd(void)
   处理对话框拖动结束事件。

让我们简要了解一下我们在程序中使用的一个关键类。 


向(CDialog, CEdit, and CButton)添加主题管理方法

现在,我相信我们对实现主题切换目标所需的方法有了更清晰的理解。对话框库已经包含了所需的基本功能,我们下一步将是整合必要的方法。


CDialog Theme Management Methods:


CDialog:

在MQL5中,CDialog类负责在MetaTrader 5平台内创建和管理自定义图形对话框窗口或面板。它允许开发人员构建包含标题、客户区域、边框和关闭按钮等UI组件的对话框。该类处理用户交互,如点击和拖动对话框,以及动态更新其主题(例如,在深色和浅色模式之间切换)。此外,它还提供方法来保存和加载对话框的状态,确保其大小、位置和最小化状态得以保留。可以将按钮和文本字段等控件添加到对话框中,使其成为构建交易应用程序中交互式和视觉吸引力界面的多功能工具。

在CDialog类中,我们引入了一个用于处理动态主题更新的方法。该方法负责根据深色主题是否激活来更新对话框的视觉外观。下面是如何整合该方法以及它如何与其他CDialog类组件结合的说明。我将分两步进行解释。然而,如果你不打算定义颜色,可以跳过第一步。

第一步:定义主题颜色

需要定义颜色,以便程序在调用主题更改时知道替代方案。在此实现中,我们的方法为深色和浅色主题使用特定的颜色定义。这些可以是预定义的常量,也可以通过参数传递。    

// Theme colors that can be defined elsewhere in our program
const color DARK_THEME_BG = clrBlack;
const color DARK_THEME_BORDER = clrGray;
const color LIGHT_THEME_BG = clrWhite;
const color LIGHT_THEME_BORDER = clrSilver;


第二步:更新主题颜色方法

该函数检查深色主题是否激活(true或false),并为关键组件应用相应的颜色:白色边框(m_white_border)更新背景和边框颜色;背景(m_background)调整其背景和边框颜色;标题(m_caption)更改标题栏的文本和背景颜色;客户区域(m_client_area)将颜色更改应用于客户区域。最后,该函数调用Redraw(),以确保在不重新创建对象的情况下视觉上应用新主题。如果你跳到了第二步,那么突出显示的颜色定义将无法工作,颜色需要直接指定为例如ClrBlack或ClrBlue等。


//+------------------------------------------------------------------+
//| Method for dynamic theme updates                                 |
//+------------------------------------------------------------------+


void CDialog::UpdateThemeColors(bool darkTheme)
{
   color backgroundColor = darkTheme ? DARK_THEME_BG : LIGHT_THEME_BG;
   color borderColor = darkTheme ? DARK_THEME_BORDER : LIGHT_THEME_BORDER;

   // Update White Border colors
   m_white_border.ColorBackground(backgroundColor);
   m_white_border.ColorBorder(borderColor);

   // Update Background colors
   m_background.ColorBackground(backgroundColor);
   m_background.ColorBorder(borderColor);

   // Update Caption colors (optional for text-based themes)
   m_caption.Color(darkTheme ? clrWhite : clrBlack);
   m_caption.ColorBackground(backgroundColor);

   // Update Client Area colors
   m_client_area.ColorBackground(backgroundColor);
   m_client_area.ColorBorder(borderColor);

   // Redraw the controls to reflect the theme changes
   Redraw();
}


CButton类的主题管理

用上述相同方式,我们在 CButton 类中添加了 SetTextColor、SetBackgroundColor 和 SetBorderColor 方法。这些方法分别允许我们设置按钮的文本、背景和边框颜色。以下是展示这些方法实现的代码片段。

 //--- theme methods
   void              SetTextColor(color clr)       { m_button.Color(clr);                           }
   void              SetBackgroundColor(color clr) { m_button.BackColor(clr);                       }
   void              SetBorderColor(color clr)     { m_button.BorderColor(clr);                     }

MQL5中默认的CButton类

//+------------------------------------------------------------------+
//|                                                       Button.mqh |
//|                             Copyright 2000-2024, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#include "WndObj.mqh"
#include <ChartObjects\ChartObjectsTxtControls.mqh>
//+------------------------------------------------------------------+
//| Class CButton                                                    |
//| Usage: control that is displayed by                              |
//|             the CChartObjectButton object                        |
//+------------------------------------------------------------------+
class CButton : public CWndObj
  {
private:
   CChartObjectButton m_button;             // chart object

public:
                     CButton(void);
                    ~CButton(void);
   //--- create
   virtual bool      Create(const long chart,const string name,const int subwin,const int x1,const int y1,const int x2,const int y2);
   //--- state
   bool              Pressed(void)          const { return(m_button.State());                       }
   bool              Pressed(const bool pressed)  { return(m_button.State(pressed));                }
   //--- properties
   bool              Locking(void)          const { return(IS_CAN_LOCK);                            }
   void              Locking(const bool flag);

protected:
   //--- handlers of object settings
   virtual bool      OnSetText(void)              { return(m_button.Description(m_text));           }
   virtual bool      OnSetColor(void)             { return(m_button.Color(m_color));                }
   virtual bool      OnSetColorBackground(void)   { return(m_button.BackColor(m_color_background)); }
   virtual bool      OnSetColorBorder(void)       { return(m_button.BorderColor(m_color_border));   }
   virtual bool      OnSetFont(void)              { return(m_button.Font(m_font));                  }
   virtual bool      OnSetFontSize(void)          { return(m_button.FontSize(m_font_size));         }
   //--- internal event handlers
   virtual bool      OnCreate(void);
   virtual bool      OnShow(void);
   virtual bool      OnHide(void);
   virtual bool      OnMove(void);
   virtual bool      OnResize(void);
   //--- íîâûå îáðàáîò÷èêè
   virtual bool      OnMouseDown(void);
   virtual bool      OnMouseUp(void);
  };
//+------------------------------------------------------------------+
//| Constructor                                                      |
//+------------------------------------------------------------------+
CButton::CButton(void)
  {
   m_color           =CONTROLS_BUTTON_COLOR;
   m_color_background=CONTROLS_BUTTON_COLOR_BG;
   m_color_border    =CONTROLS_BUTTON_COLOR_BORDER;
  }
//+------------------------------------------------------------------+
//| Destructor                                                       |
//+------------------------------------------------------------------+
CButton::~CButton(void)
  {
  }
//+------------------------------------------------------------------+
//| Create a control                                                 |
//+------------------------------------------------------------------+
bool CButton::Create(const long chart,const string name,const int subwin,const int x1,const int y1,const int x2,const int y2)
  {
//--- call method of the parent class
   if(!CWndObj::Create(chart,name,subwin,x1,y1,x2,y2))
      return(false);
//--- create the chart object
   if(!m_button.Create(chart,name,subwin,x1,y1,Width(),Height()))
      return(false);
//--- call the settings handler
   return(OnChange());
  }
//+------------------------------------------------------------------+
//| Locking flag                                                     |
//+------------------------------------------------------------------+
void CButton::Locking(const bool flag)
  {
   if(flag)
      PropFlagsSet(WND_PROP_FLAG_CAN_LOCK);
   else
      PropFlagsReset(WND_PROP_FLAG_CAN_LOCK);
  }
//+------------------------------------------------------------------+
//| Create object on chart                                           |
//+------------------------------------------------------------------+
bool CButton::OnCreate(void)
  {
//--- create the chart object by previously set parameters
   return(m_button.Create(m_chart_id,m_name,m_subwin,m_rect.left,m_rect.top,m_rect.Width(),m_rect.Height()));
  }
//+------------------------------------------------------------------+
//| Display object on chart                                          |
//+------------------------------------------------------------------+
bool CButton::OnShow(void)
  {
   return(m_button.Timeframes(OBJ_ALL_PERIODS));
  }
//+------------------------------------------------------------------+
//| Hide object from chart                                           |
//+------------------------------------------------------------------+
bool CButton::OnHide(void)
  {
   return(m_button.Timeframes(OBJ_NO_PERIODS));
  }
//+------------------------------------------------------------------+
//| Absolute movement of the chart object                            |
//+------------------------------------------------------------------+
bool CButton::OnMove(void)
  {
//--- position the chart object
   return(m_button.X_Distance(m_rect.left) && m_button.Y_Distance(m_rect.top));
  }
//+------------------------------------------------------------------+
//| Resize the chart object                                          |
//+------------------------------------------------------------------+
bool CButton::OnResize(void)
  {
//--- resize the chart object
   return(m_button.X_Size(m_rect.Width()) && m_button.Y_Size(m_rect.Height()));
  }
//+------------------------------------------------------------------+
//| Handler of click on the left mouse button                        |
//+------------------------------------------------------------------+
bool CButton::OnMouseDown(void)
  {
   if(!IS_CAN_LOCK)
      Pressed(!Pressed());
//--- call of the method of the parent class
   return(CWnd::OnMouseDown());
  }
//+------------------------------------------------------------------+
//| Handler of click on the left mouse button                        |
//+------------------------------------------------------------------+
bool CButton::OnMouseUp(void)
  {
//--- depress the button if it is not fixed
   if(m_button.State() && !IS_CAN_LOCK)
      m_button.State(false);
//--- call of the method of the parent class
   return(CWnd::OnMouseUp());
  }
//+------------------------------------------------------------------+


整合了主题管理方法的CButton类:

请看高亮部分。

//+------------------------------------------------------------------+
//|                                                       Button.mqh |
//|                             Copyright 2000-2024, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#include "WndObj.mqh"
#include <ChartObjects\ChartObjectsTxtControls.mqh>
//+------------------------------------------------------------------+
//| Class CButton                                                    |
//| Usage: control that is displayed by                              |
//|             the CChartObjectButton object                        |
//+------------------------------------------------------------------+
class CButton : public CWndObj
  {
private:
   CChartObjectButton m_button;             // chart object

public:
                     CButton(void);
                    ~CButton(void);
   //--- create
   virtual bool      Create(const long chart,const string name,const int subwin,const int x1,const int y1,const int x2,const int y2);
   //--- state
   bool              Pressed(void)          const { return(m_button.State());                       }
   bool              Pressed(const bool pressed)  { return(m_button.State(pressed));                }
   //--- properties
   bool              Locking(void)          const { return(IS_CAN_LOCK);                            }
   void              Locking(const bool flag);
   
   //--- theme methods
   void              SetTextColor(color clr)       { m_button.Color(clr);                           }
   void              SetBackgroundColor(color clr) { m_button.BackColor(clr);                       }
   void              SetBorderColor(color clr)     { m_button.BorderColor(clr);                     }

protected:
   //--- handlers of object settings
   virtual bool      OnSetText(void)              { return(m_button.Description(m_text));           }
   virtual bool      OnSetColor(void)             { return(m_button.Color(m_color));                }
   virtual bool      OnSetColorBackground(void)   { return(m_button.BackColor(m_color_background)); }
   virtual bool      OnSetColorBorder(void)       { return(m_button.BorderColor(m_color_border));   }
   virtual bool      OnSetFont(void)              { return(m_button.Font(m_font));                  }
   virtual bool      OnSetFontSize(void)          { return(m_button.FontSize(m_font_size));         }
   //--- internal event handlers
   virtual bool      OnCreate(void);
   virtual bool      OnShow(void);
   virtual bool      OnHide(void);
   virtual bool      OnMove(void);
   virtual bool      OnResize(void);
   virtual bool      OnMouseDown(void);
   virtual bool      OnMouseUp(void);
  };
//+------------------------------------------------------------------+
//| Constructor                                                      |
//+------------------------------------------------------------------+
CButton::CButton(void)
  {
   m_color           =CONTROLS_BUTTON_COLOR;
   m_color_background=CONTROLS_BUTTON_COLOR_BG;
   m_color_border    =CONTROLS_BUTTON_COLOR_BORDER;
  }
//+------------------------------------------------------------------+
//| Destructor                                                       |
//+------------------------------------------------------------------+
CButton::~CButton(void)
  {
  }
//+------------------------------------------------------------------+
//| Create a control                                                 |
//+------------------------------------------------------------------+
bool CButton::Create(const long chart,const string name,const int subwin,const int x1,const int y1,const int x2,const int y2)
  {
//--- call method of the parent class
   if(!CWndObj::Create(chart,name,subwin,x1,y1,x2,y2))
      return(false);
//--- create the chart object
   if(!m_button.Create(chart,name,subwin,x1,y1,Width(),Height()))
      return(false);
//--- call the settings handler
   return(OnChange());
  }
//+------------------------------------------------------------------+
//| Locking flag                                                     |
//+------------------------------------------------------------------+
void CButton::Locking(const bool flag)
  {
   if(flag)
      PropFlagsSet(WND_PROP_FLAG_CAN_LOCK);
   else
      PropFlagsReset(WND_PROP_FLAG_CAN_LOCK);
  }
//+------------------------------------------------------------------+
//| Create object on chart                                           |
//+------------------------------------------------------------------+
bool CButton::OnCreate(void)
  {
//--- create the chart object by previously set parameters
   return(m_button.Create(m_chart_id,m_name,m_subwin,m_rect.left,m_rect.top,m_rect.Width(),m_rect.Height()));
  }
//+------------------------------------------------------------------+
//| Display object on chart                                          |
//+------------------------------------------------------------------+
bool CButton::OnShow(void)
  {
   return(m_button.Timeframes(OBJ_ALL_PERIODS));
  }
//+------------------------------------------------------------------+
//| Hide object from chart                                           |
//+------------------------------------------------------------------+
bool CButton::OnHide(void)
  {
   return(m_button.Timeframes(OBJ_NO_PERIODS));
  }
//+------------------------------------------------------------------+
//| Absolute movement of the chart object                            |
//+------------------------------------------------------------------+
bool CButton::OnMove(void)
  {
//--- position the chart object
   return(m_button.X_Distance(m_rect.left) && m_button.Y_Distance(m_rect.top));
  }
//+------------------------------------------------------------------+
//| Resize the chart object                                          |
//+------------------------------------------------------------------+
bool CButton::OnResize(void)
  {
//--- resize the chart object
   return(m_button.X_Size(m_rect.Width()) && m_button.Y_Size(m_rect.Height()));
  }
//+------------------------------------------------------------------+
//| Handler of click on the left mouse button                        |
//+------------------------------------------------------------------+
bool CButton::OnMouseDown(void)
  {
   if(!IS_CAN_LOCK)
      Pressed(!Pressed());
//--- call of the method of the parent class
   return(CWnd::OnMouseDown());
  }
//+------------------------------------------------------------------+
//| Handler of click on the left mouse button                        |
//+------------------------------------------------------------------+
bool CButton::OnMouseUp(void)
  {
//--- depress the button if it is not fixed
   if(m_button.State() && !IS_CAN_LOCK)
      m_button.State(false);
//--- call of the method of the parent class
   return(CWnd::OnMouseUp());
  }
//+------------------------------------------------------------------+


CEdit类的主题管理

这是项目中的一个关键类,用于控制我们将要输入消息的输入框。默认情况下,我们的面板及其组件设置为白色背景和黑色前景文本。当我们点击主题切换按钮时,前景颜色变为白色。然而,在开发过程中,我注意到输入框的颜色保持不变,这在主题切换时会导致它偶尔与文本融为一体。因此,我们需要在 CEdit 类中添加一个方法来处理主题切换,并确保文本输入框与我们的主题目标保持一致。

默认的 CEdit 类已经具有设置颜色的方法(OnSetColor、OnSetColorBackground 和 OnSetColorBorder)。我们可以使用这些方法在主题更改时更新 CEdit 对象的外观。我们为主题切换添加了新方法,比如通过添加这些术语:SetTextColor、SetBackgroundColor 和 SetBorderColor 方法到 CEdit 类中。这些方法更新相应的颜色,并调用现有方法(OnSetColor、OnSetColorBackground、OnSetColorBorder)将更改应用于图表对象。

//+------------------------------------------------------------------+
//| Set text color                                                   |
//+------------------------------------------------------------------+
bool CEdit::SetTextColor(const color clr)
  {
   m_color = clr;
   return(OnSetColor());
  }

//+------------------------------------------------------------------+
//| Set background color                                             |
//+------------------------------------------------------------------+
bool CEdit::SetBackgroundColor(const color clr)
  {
   m_color_background = clr;
   return(OnSetColorBackground());
  }

//+------------------------------------------------------------------+
//| Set border color                                                 |
//+------------------------------------------------------------------+
bool CEdit::SetBorderColor(const color clr)
  {
   m_color_border = clr;
   return(OnSetColorBorder());
  }

我们将查看未经修改的 CEdit 类源代码,并在下方分享整合后的程序代码。

MQL5中默认的CEdit类:

//+------------------------------------------------------------------+
//|                                                         Edit.mqh |
//|                             Copyright 2000-2024, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#include "WndObj.mqh"
#include <ChartObjects\ChartObjectsTxtControls.mqh>
//+------------------------------------------------------------------+
//| Class CEdit                                                      |
//| Usage: control that is displayed by                              |
//|             the CChartObjectEdit object                          |
//+------------------------------------------------------------------+
class CEdit : public CWndObj
  {
private:
   CChartObjectEdit  m_edit;                // chart object
   //--- parameters of the chart object
   bool              m_read_only;           // "read-only" mode flag
   ENUM_ALIGN_MODE   m_align_mode;          // align mode

public:
                     CEdit(void);
                    ~CEdit(void);
   //--- create
   virtual bool      Create(const long chart,const string name,const int subwin,const int x1,const int y1,const int x2,const int y2);
   //--- chart event handler
   virtual bool      OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam);
   //--- parameters of the chart object
   bool              ReadOnly(void)         const { return(m_read_only);                          }
   bool              ReadOnly(const bool flag);
   ENUM_ALIGN_MODE   TextAlign(void)        const { return(m_align_mode);                         }
   bool              TextAlign(const ENUM_ALIGN_MODE align);
   //--- data access
   string            Text(void)             const { return(m_edit.Description());                 }
   bool              Text(const string value)     { return(CWndObj::Text(value));                 }

protected:
   //--- handlers of object events
   virtual bool      OnObjectEndEdit(void);
   //--- handlers of object settings
   virtual bool      OnSetText(void)              { return(m_edit.Description(m_text));           }
   virtual bool      OnSetColor(void)             { return(m_edit.Color(m_color));                }
   virtual bool      OnSetColorBackground(void)   { return(m_edit.BackColor(m_color_background)); }
   virtual bool      OnSetColorBorder(void)       { return(m_edit.BorderColor(m_color_border));   }
   virtual bool      OnSetFont(void)              { return(m_edit.Font(m_font));                  }
   virtual bool      OnSetFontSize(void)          { return(m_edit.FontSize(m_font_size));         }
   virtual bool      OnSetZOrder(void)            { return(m_edit.Z_Order(m_zorder));             }
   //--- internal event handlers
   virtual bool      OnCreate(void);
   virtual bool      OnShow(void);
   virtual bool      OnHide(void);
   virtual bool      OnMove(void);
   virtual bool      OnResize(void);
   virtual bool      OnChange(void);
   virtual bool      OnClick(void);
  };
//+------------------------------------------------------------------+
//| Common handler of chart events                                   |
//+------------------------------------------------------------------+
bool CEdit::OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam)
  {
   if(m_name==sparam && id==CHARTEVENT_OBJECT_ENDEDIT)
      return(OnObjectEndEdit());
//--- event was not handled
   return(CWndObj::OnEvent(id,lparam,dparam,sparam));
  }
//+------------------------------------------------------------------+
//| Constructor                                                      |
//+------------------------------------------------------------------+
CEdit::CEdit(void) : m_read_only(false),
                     m_align_mode(ALIGN_LEFT)
  {
   m_color           =CONTROLS_EDIT_COLOR;
   m_color_background=CONTROLS_EDIT_COLOR_BG;
   m_color_border    =CONTROLS_EDIT_COLOR_BORDER;
  }
//+------------------------------------------------------------------+
//| Destructor                                                       |
//+------------------------------------------------------------------+
CEdit::~CEdit(void)
  {
  }
//+------------------------------------------------------------------+
//| Create a control                                                 |
//+------------------------------------------------------------------+
bool CEdit::Create(const long chart,const string name,const int subwin,const int x1,const int y1,const int x2,const int y2)
  {
//--- call method of the parent class
   if(!CWndObj::Create(chart,name,subwin,x1,y1,x2,y2))
      return(false);
//--- create the chart object
   if(!m_edit.Create(chart,name,subwin,x1,y1,Width(),Height()))
      return(false);
//--- call the settings handler
   return(OnChange());
  }
//+------------------------------------------------------------------+
//| Set parameter                                                    |
//+------------------------------------------------------------------+
bool CEdit::ReadOnly(const bool flag)
  {
//--- save new value of parameter
   m_read_only=flag;
//--- set up the chart object
   return(m_edit.ReadOnly(flag));
  }
//+------------------------------------------------------------------+
//| Set parameter                                                    |
//+------------------------------------------------------------------+
bool CEdit::TextAlign(const ENUM_ALIGN_MODE align)
  {
//--- save new value of parameter
   m_align_mode=align;
//--- set up the chart object
   return(m_edit.TextAlign(align));
  }
//+------------------------------------------------------------------+
//| Create object on chart                                           |
//+------------------------------------------------------------------+
bool CEdit::OnCreate(void)
  {
//--- create the chart object by previously set parameters
   return(m_edit.Create(m_chart_id,m_name,m_subwin,m_rect.left,m_rect.top,m_rect.Width(),m_rect.Height()));
  }
//+------------------------------------------------------------------+
//| Display object on chart                                          |
//+------------------------------------------------------------------+
bool CEdit::OnShow(void)
  {
   return(m_edit.Timeframes(OBJ_ALL_PERIODS));
  }
//+------------------------------------------------------------------+
//| Hide object from chart                                           |
//+------------------------------------------------------------------+
bool CEdit::OnHide(void)
  {
   return(m_edit.Timeframes(OBJ_NO_PERIODS));
  }
//+------------------------------------------------------------------+
//| Absolute movement of the chart object                            |
//+------------------------------------------------------------------+
bool CEdit::OnMove(void)
  {
//--- position the chart object
   return(m_edit.X_Distance(m_rect.left) && m_edit.Y_Distance(m_rect.top));
  }
//+------------------------------------------------------------------+
//| Resize the chart object                                          |
//+------------------------------------------------------------------+
bool CEdit::OnResize(void)
  {
//--- resize the chart object
   return(m_edit.X_Size(m_rect.Width()) && m_edit.Y_Size(m_rect.Height()));
  }
//+------------------------------------------------------------------+
//| Set up the chart object                                          |
//+------------------------------------------------------------------+
bool CEdit::OnChange(void)
  {
//--- set up the chart object
   return(CWndObj::OnChange() && ReadOnly(m_read_only) && TextAlign(m_align_mode));
  }
//+------------------------------------------------------------------+
//| Handler of the "End of editing" event                            |
//+------------------------------------------------------------------+
bool CEdit::OnObjectEndEdit(void)
  {
//--- send the ON_END_EDIT notification
   EventChartCustom(CONTROLS_SELF_MESSAGE,ON_END_EDIT,m_id,0.0,m_name);
//--- handled
   return(true);
  }
//+------------------------------------------------------------------+
//| Handler of the "click" event                                     |
//+------------------------------------------------------------------+
bool CEdit::OnClick(void)
  {
//--- if editing is enabled, send the ON_START_EDIT notification
   if(!m_read_only)
     {
      EventChartCustom(CONTROLS_SELF_MESSAGE,ON_START_EDIT,m_id,0.0,m_name);
      //--- handled
      return(true);
     }
//--- else send the ON_CLICK notification
   return(CWnd::OnClick());
  }
//+------------------------------------------------------------------+


整合主题管理方法的CEdit 类

请看高亮部分。

//+------------------------------------------------------------------+
//|                                                         Edit.mqh |
//|                             Copyright 2000-2024, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#include "WndObj.mqh"
#include <ChartObjects\ChartObjectsTxtControls.mqh>

//+------------------------------------------------------------------+
//| Class CEdit                                                      |
//| Usage: control that is displayed by                              |
//|             the CChartObjectEdit object                          |
//+------------------------------------------------------------------+
class CEdit : public CWndObj
  {
private:
   CChartObjectEdit  m_edit;                // chart object
   //--- parameters of the chart object
   bool              m_read_only;           // "read-only" mode flag
   ENUM_ALIGN_MODE   m_align_mode;          // align mode

public:
                     CEdit(void);
                    ~CEdit(void);
   //--- create
   virtual bool      Create(const long chart,const string name,const int subwin,const int x1,const int y1,const int x2,const int y2);
   //--- chart event handler
   virtual bool      OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam);
   //--- parameters of the chart object
   bool              ReadOnly(void)         const { return(m_read_only);                          }
   bool              ReadOnly(const bool flag);
   ENUM_ALIGN_MODE   TextAlign(void)        const { return(m_align_mode);                         }
   bool              TextAlign(const ENUM_ALIGN_MODE align);
   //--- data access
   string            Text(void)             const { return(m_edit.Description());                 }
   bool              Text(const string value)     { return(CWndObj::Text(value));                 }
   //--- theme handling
   bool              SetTextColor(const color clr);
   bool              SetBackgroundColor(const color clr);
   bool              SetBorderColor(const color clr);

protected:
   //--- handlers of object events
   virtual bool      OnObjectEndEdit(void);
   //--- handlers of object settings
   virtual bool      OnSetText(void)              { return(m_edit.Description(m_text));           }
   virtual bool      OnSetColor(void)             { return(m_edit.Color(m_color));                }
   virtual bool      OnSetColorBackground(void)   { return(m_edit.BackColor(m_color_background)); }
   virtual bool      OnSetColorBorder(void)       { return(m_edit.BorderColor(m_color_border));   }
   virtual bool      OnSetFont(void)              { return(m_edit.Font(m_font));                  }
   virtual bool      OnSetFontSize(void)          { return(m_edit.FontSize(m_font_size));         }
   virtual bool      OnSetZOrder(void)            { return(m_edit.Z_Order(m_zorder));             }
   //--- internal event handlers
   virtual bool      OnCreate(void);
   virtual bool      OnShow(void);
   virtual bool      OnHide(void);
   virtual bool      OnMove(void);
   virtual bool      OnResize(void);
   virtual bool      OnChange(void);
   virtual bool      OnClick(void);
  };

//+------------------------------------------------------------------+
//| Constructor                                                      |
//+------------------------------------------------------------------+
CEdit::CEdit(void) : m_read_only(false),
                     m_align_mode(ALIGN_LEFT)
  {
   m_color           =CONTROLS_EDIT_COLOR;
   m_color_background=CONTROLS_EDIT_COLOR_BG;
   m_color_border    =CONTROLS_EDIT_COLOR_BORDER;
  }

//+------------------------------------------------------------------+
//| Destructor                                                       |
//+------------------------------------------------------------------+
CEdit::~CEdit(void)
  {
  }

//+------------------------------------------------------------------+
//| Create a control                                                 |
//+------------------------------------------------------------------+
bool CEdit::Create(const long chart,const string name,const int subwin,const int x1,const int y1,const int x2,const int y2)
  {
   //--- call method of the parent class
   if(!CWndObj::Create(chart,name,subwin,x1,y1,x2,y2))
      return(false);
   //--- create the chart object
   if(!m_edit.Create(chart,name,subwin,x1,y1,Width(),Height()))
      return(false);
   //--- call the settings handler
   return(OnChange());
  }

//+------------------------------------------------------------------+
//| Set parameter                                                    |
//+------------------------------------------------------------------+
bool CEdit::ReadOnly(const bool flag)
  {
   //--- save new value of parameter
   m_read_only=flag;
   //--- set up the chart object
   return(m_edit.ReadOnly(flag));
  }

//+------------------------------------------------------------------+
//| Set parameter                                                    |
//+------------------------------------------------------------------+
bool CEdit::TextAlign(const ENUM_ALIGN_MODE align)
  {
   //--- save new value of parameter
   m_align_mode=align;
   //--- set up the chart object
   return(m_edit.TextAlign(align));
  }

//+------------------------------------------------------------------+
//| Set text color                                                   |
//+------------------------------------------------------------------+
bool CEdit::SetTextColor(const color clr)
  {
   m_color = clr;
   return(OnSetColor());
  }

//+------------------------------------------------------------------+
//| Set background color                                             |
//+------------------------------------------------------------------+
bool CEdit::SetBackgroundColor(const color clr)
  {
   m_color_background = clr;
   return(OnSetColorBackground());
  }

//+------------------------------------------------------------------+
//| Set border color                                                 |
//+------------------------------------------------------------------+
bool CEdit::SetBorderColor(const color clr)
  {
   m_color_border = clr;
   return(OnSetColorBorder());
  }

//+------------------------------------------------------------------+
//| Create object on chart                                           |
//+------------------------------------------------------------------+
bool CEdit::OnCreate(void)
  {
   //--- create the chart object by previously set parameters
   return(m_edit.Create(m_chart_id,m_name,m_subwin,m_rect.left,m_rect.top,m_rect.Width(),m_rect.Height()));
  }

//+------------------------------------------------------------------+
//| Display object on chart                                          |
//+------------------------------------------------------------------+
bool CEdit::OnShow(void)
  {
   return(m_edit.Timeframes(OBJ_ALL_PERIODS));
  }

//+------------------------------------------------------------------+
//| Hide object from chart                                           |
//+------------------------------------------------------------------+
bool CEdit::OnHide(void)
  {
   return(m_edit.Timeframes(OBJ_NO_PERIODS));
  }

//+------------------------------------------------------------------+
//| Absolute movement of the chart object                            |
//+------------------------------------------------------------------+
bool CEdit::OnMove(void)
  {
   //--- position the chart object
   return(m_edit.X_Distance(m_rect.left) && m_edit.Y_Distance(m_rect.top));
  }

//+------------------------------------------------------------------+
//| Resize the chart object                                          |
//+------------------------------------------------------------------+
bool CEdit::OnResize(void)
  {
   //--- resize the chart object
   return(m_edit.X_Size(m_rect.Width()) && m_edit.Y_Size(m_rect.Height()));
  }

//+------------------------------------------------------------------+
//| Set up the chart object                                          |
//+------------------------------------------------------------------+
bool CEdit::OnChange(void)
  {
   //--- set up the chart object
   return(CWndObj::OnChange() && ReadOnly(m_read_only) && TextAlign(m_align_mode));
  }

//+------------------------------------------------------------------+
//| Handler of the "End of editing" event                            |
//+------------------------------------------------------------------+
bool CEdit::OnObjectEndEdit(void)
  {
   //--- send the ON_END_EDIT notification
   EventChartCustom(CONTROLS_SELF_MESSAGE,ON_END_EDIT,m_id,0.0,m_name);
   //--- handled
   return(true);
  }

//+------------------------------------------------------------------+
//| Handler of the "click" event                                     |
//+------------------------------------------------------------------+
bool CEdit::OnClick(void)
  {
   //--- if editing is enabled, send the ON_START_EDIT notification
   if(!m_read_only)
     {
      EventChartCustom(CONTROLS_SELF_MESSAGE,ON_START_EDIT,m_id,0.0,m_name);
      //--- handled
      return(true);
     }
   //--- else send the ON_CLICK notification
   return(CWnd::OnClick());
  }

//+------------------------------------------------------------------+
我们已经成功地为管理员面板准备好了控件的头文件,我们比以往任何时候都更接近完成我们的项目。在下一部分中,我们将通过调整管理员面板EA的代码以支持主题切换功能,从而完善我们的项目,使其与最新的开发进展保持一致。

为管理员面板调整主题切换功能。

 在我们的主题管理中有四个逻辑上的关键点。 

  • The theme switching functionality in our Admin panel must be centered around the darkTheme boolean variable and the UpdateThemeColors() function. 以下是它如何运作的:

bool darkTheme = false;

  • 上述标志决定了当前主题是深色还是浅色。当按下 toggleThemeButton 按钮时,该标志会切换,如下所示。

void OnToggleThemeButtonClick()
{
    darkTheme = !darkTheme;
    UpdateThemeColors();
    Print("Theme toggled: ", darkTheme ? "Dark" : "Light");
}

  • 点击主题切换按钮会调用这个函数,它会切换 darkTheme 标志,并通过 UpdateThemeColors() 更新用户界面的主题。

void UpdateThemeColors()
{
    // Determine colors based on the current theme
    color textColor = darkTheme ? clrWhite : clrBlack;
    color buttonBgColor = darkTheme ? clrDarkSlateGray : clrGainsboro;
    color borderColor = darkTheme ? clrSlateGray : clrGray;
    color bgColor     = darkTheme ? clrDarkBlue : clrWhite;

    // Set text box colors
    inputBox.SetTextColor(textColor);
    inputBox.SetBackgroundColor(bgColor);
    inputBox.SetBorderColor(borderColor);

    // Update button colors
    UpdateButtonTheme(clearButton, textColor, buttonBgColor, borderColor);
    UpdateButtonTheme(sendButton, textColor, buttonBgColor, borderColor);
    UpdateButtonTheme(toggleThemeButton, textColor, buttonBgColor, borderColor);
    UpdateButtonTheme(changeFontButton, textColor, buttonBgColor, borderColor);
    UpdateButtonTheme(minimizeButton, textColor, buttonBgColor, borderColor);
    UpdateButtonTheme(maximizeButton, textColor, buttonBgColor, borderColor);
    UpdateButtonTheme(closeButton, textColor, buttonBgColor, borderColor);

    // Update quick message buttons
    for (int i = 0; i < ArraySize(quickMessageButtons); i++)
    {
        UpdateButtonTheme(quickMessageButtons[i], textColor, buttonBgColor, borderColor);
    }

    // Update character counter color
    charCounter.Color(textColor);

    // Redraw chart to apply changes
    ChartRedraw();
}

 基于 darkTheme 标志,我们为文本、按钮背景、边框和背景选择了不同的颜色。颜色被应用于各个用户界面组件,如下所示:

  • 文本框(inputBox):使用函数 SetTextColorSetBackgroundColorSetBorderColor 来应用主题。
  • 按钮:为每个按钮调用 UpdateButtonTheme() 函数,根据确定的颜色设置它们的文本颜色、背景颜色和边框颜色。
  • 字符计数器:在点击主题按钮时直接设置其颜色。

//Theme button application
void UpdateButtonTheme(CButton &button, color textColor, color bgColor, color borderColor)
{
    button.SetTextColor(textColor);
    button.SetBackgroundColor(bgColor);
    button.SetBorderColor(borderColor);
}

我们在上面使用了一个辅助函数来将所有相关的主题颜色设置应用于任何按钮。这清理了重复的代码,并确保了按钮之间的一致性。将所有代码片段汇总并整合到主管理员面板程序中,我们实现了所有功能,达到了预期目标。


最终的代码和结果

以下是包含新功能的程序的最终版本。


//+------------------------------------------------------------------+
//|                                             Admin Panel.mq5      |
//|                     Copyright 2024, Clemence Benjamin            |
//|     https://www.mql5.com/en/users/billionaire2024/seller         |
//+------------------------------------------------------------------+
#property copyright "Copyright 2024, Clemence Benjamin"
#property link      "https://www.mql5.com/en/users/billionaire2024/seller"
#property version   "1.12"

#include <Trade\Trade.mqh>
#include <Controls\Dialog.mqh>
#include <Controls\Button.mqh>
#include <Controls\Edit.mqh>
#include <Controls\Label.mqh>

// Input parameters
input string QuickMessage1 = "Updates";
input string QuickMessage2 = "Close all";
input string QuickMessage3 = "In deep profits";
input string QuickMessage4 = "Hold position";
input string QuickMessage5 = "Swing Entry";
input string QuickMessage6 = "Scalp Entry";
input string QuickMessage7 = "Book profit";
input string QuickMessage8 = "Invalid Signal";
input string InputChatId = "Enter Chat ID from Telegram bot API";
input string InputBotToken = "Enter BOT TOKEN from your Telegram bot";

// Global variables
CDialog adminPanel;
CButton sendButton, clearButton, changeFontButton, toggleThemeButton;
CButton quickMessageButtons[8], minimizeButton, maximizeButton, closeButton;
CEdit inputBox;
CLabel charCounter;
bool minimized = false;
bool darkTheme = false;
int MAX_MESSAGE_LENGTH = 4096;
string availableFonts[] = { "Arial", "Courier New", "Verdana", "Times New Roman" };
int currentFontIndex = 0;

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
{
    // Initialize the Dialog
    if (!adminPanel.Create(ChartID(), "Admin Panel", 0, 30, 30, 500, 500))
    {
        Print("Failed to create dialog");
        return INIT_FAILED;
    }

    // Create controls
    if (!CreateControls())
    {
        Print("Control creation failed");
        return INIT_FAILED;
    }

    adminPanel.Show();
    UpdateThemeColors();

    Print("Initialization complete");
    return INIT_SUCCEEDED;
}

//+------------------------------------------------------------------+
//| Create necessary UI controls                                     |
//+------------------------------------------------------------------+
bool CreateControls()
{
    long chart_id = ChartID();

    // Create the input box
    if (!inputBox.Create(chart_id, "InputBox", 0, 5, 25, 460, 95))
    {
        Print("Failed to create input box");
        return false;
    }
    adminPanel.Add(inputBox);

    // Character counter
    if (!charCounter.Create(chart_id, "CharCounter", 0, 380, 5, 460, 25))
    {
        Print("Failed to create character counter");
        return false;
    }
    charCounter.Text("0/" + IntegerToString(MAX_MESSAGE_LENGTH));
    adminPanel.Add(charCounter);

    // Clear button
    if (!clearButton.Create(chart_id, "ClearButton", 0, 235, 95, 345, 125))
    {
        Print("Failed to create clear button");
        return false;
    }
    clearButton.Text("Clear");
    adminPanel.Add(clearButton);

    // Send button
    if (!sendButton.Create(chart_id, "SendButton", 0, 350, 95, 460, 125))
    {
        Print("Failed to create send button");
        return false;
    }
    sendButton.Text("Send");
    adminPanel.Add(sendButton);

    // Change font button
    if (!changeFontButton.Create(chart_id, "ChangeFontButton", 0, 95, 95, 230, 115))
    {
        Print("Failed to create change font button");
        return false;
    }
    changeFontButton.Text("Font<>");
    adminPanel.Add(changeFontButton);

    // Toggle theme button
    if (!toggleThemeButton.Create(chart_id, "ToggleThemeButton", 0, 5, 95, 90, 115))
    {
        Print("Failed to create toggle theme button");
        return false;
    }
    toggleThemeButton.Text("Theme<>");
    adminPanel.Add(toggleThemeButton);

    // Minimize button
    if (!minimizeButton.Create(chart_id, "MinimizeButton", 0, 375, -22, 405, 0))
    {
        Print("Failed to create minimize button");
        return false;
    }
    minimizeButton.Text("_");
    adminPanel.Add(minimizeButton);

    // Maximize button
    if (!maximizeButton.Create(chart_id, "MaximizeButton", 0, 405, -22, 435, 0))
    {
        Print("Failed to create maximize button");
        return false;
    }
    maximizeButton.Text("[ ]");
    adminPanel.Add(maximizeButton);

    // Close button
    if (!closeButton.Create(chart_id, "CloseButton", 0, 435, -22, 465, 0))
    {
        Print("Failed to create close button");
        return false;
    }
    closeButton.Text("X");
    adminPanel.Add(closeButton);

    // Quick messages
    return CreateQuickMessageButtons();
}

//+------------------------------------------------------------------+
//| Create quick message buttons                                     |
//+------------------------------------------------------------------+
bool CreateQuickMessageButtons()
{
    string quickMessages[8] = { QuickMessage1, QuickMessage2, QuickMessage3, QuickMessage4, QuickMessage5, QuickMessage6, QuickMessage7, QuickMessage8 };
    int startX = 5, startY = 160, width = 222, height = 65, spacing = 5;

    for (int i = 0; i < 8; i++)
    {
        if (!quickMessageButtons[i].Create(ChartID(), "QuickMessageButton" + IntegerToString(i + 1), 0, startX + (i % 2) * (width + spacing), startY + (i / 2) * (height + spacing), startX + (i % 2) * (width + spacing) + width, startY + (i / 2) * (height + spacing) + height))
        {
            Print("Failed to create quick message button ", i + 1);
            return false;
        }
        quickMessageButtons[i].Text(quickMessages[i]);
        adminPanel.Add(quickMessageButtons[i]);
    }
    return true;
}

//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
    adminPanel.Destroy();
    Print("Deinitialization complete");
}

//+------------------------------------------------------------------+
//| Handle chart events                                              |
//+------------------------------------------------------------------+
void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
{
    switch (id)
    {
        case CHARTEVENT_OBJECT_CLICK:
            if (sparam == "SendButton") OnSendButtonClick();
            else if (sparam == "ClearButton") OnClearButtonClick();
            else if (sparam == "ChangeFontButton") OnChangeFontButtonClick();
            else if (sparam == "ToggleThemeButton") OnToggleThemeButtonClick();
            else if (sparam == "MinimizeButton") OnMinimizeButtonClick();
            else if (sparam == "MaximizeButton") OnMaximizeButtonClick();
            else if (sparam == "CloseButton") OnCloseButtonClick();
            else if (StringFind(sparam, "QuickMessageButton") != -1)
            {
                long index = StringToInteger(StringSubstr(sparam, 18));
                OnQuickMessageButtonClick(index - 1);
            }
            break;

        case CHARTEVENT_OBJECT_ENDEDIT:
            if (sparam == "InputBox") OnInputChange();
            break;
    }
}

//+------------------------------------------------------------------+
//| Handle custom message send button click                          |
//+------------------------------------------------------------------+
void OnSendButtonClick()
{
    string message = inputBox.Text();
    if (message != "")
    {
        if (SendMessageToTelegram(message))
            Print("Custom message sent: ", message);
        else
            Print("Failed to send custom message.");
    }
    else
    {
        Print("No message entered.");
    }
}

//+------------------------------------------------------------------+
//| Handle clear button click                                        |
//+------------------------------------------------------------------+
void OnClearButtonClick()
{
    inputBox.Text("");
    OnInputChange();
    Print("Input box cleared.");
}

//+------------------------------------------------------------------+
//| Handle quick message button click                                |
//+------------------------------------------------------------------+
void OnQuickMessageButtonClick(int index)
{
    string quickMessages[8] = { QuickMessage1, QuickMessage2, QuickMessage3, QuickMessage4, QuickMessage5, QuickMessage6, QuickMessage7, QuickMessage8 };
    string message = quickMessages[index];

    if (SendMessageToTelegram(message))
        Print("Quick message sent: ", message);
    else
        Print("Failed to send quick message.");
}

//+------------------------------------------------------------------+
//| Update character counter                                         |
//+------------------------------------------------------------------+
void OnInputChange()
{
    int currentLength = StringLen(inputBox.Text());
    charCounter.Text(IntegerToString(currentLength) + "/" + IntegerToString(MAX_MESSAGE_LENGTH));
    ChartRedraw();
}

//+------------------------------------------------------------------+
//| Handle toggle theme button click                                 |
//+------------------------------------------------------------------+
void OnToggleThemeButtonClick()
{
    darkTheme = !darkTheme;
    UpdateThemeColors();
    Print("Theme toggled: ", darkTheme ? "Dark" : "Light");
}

//+------------------------------------------------------------------+
//| Update theme colors for the panel                                |
//+------------------------------------------------------------------+
void UpdateThemeColors()
{
    // Use the dialog's theme update method as a placeholder.
    adminPanel.UpdateThemeColors(darkTheme);

    color textColor = darkTheme ? clrWhite : clrBlack;
    color buttonBgColor = darkTheme ? clrDarkSlateGray : clrGainsboro;
    color borderColor = darkTheme ? clrSlateGray : clrGray;
    color bgColor     = darkTheme?  clrDarkBlue : clrWhite;

          inputBox.SetTextColor(textColor);
          inputBox.SetBackgroundColor(bgColor);
          inputBox.SetBorderColor(borderColor);

    UpdateButtonTheme(clearButton, textColor, buttonBgColor, borderColor);
    UpdateButtonTheme(sendButton, textColor, buttonBgColor, borderColor);
    UpdateButtonTheme(toggleThemeButton, textColor, buttonBgColor, borderColor);
    UpdateButtonTheme(changeFontButton, textColor, buttonBgColor, borderColor);
    UpdateButtonTheme(minimizeButton, textColor, buttonBgColor, borderColor);
    UpdateButtonTheme(maximizeButton, textColor, buttonBgColor,borderColor);
    UpdateButtonTheme(closeButton, textColor, buttonBgColor, borderColor);
    

    for (int i = 0; i < ArraySize(quickMessageButtons); i++)
    {
        UpdateButtonTheme(quickMessageButtons[i], textColor, buttonBgColor, borderColor);
    }

    charCounter.Color(textColor);

    ChartRedraw();
}

//+------------------------------------------------------------------+
//| Apply theme settings to a button                                 |
//+------------------------------------------------------------------+
void UpdateButtonTheme(CButton &button, color textColor, color bgColor, color borderColor)
{
    button.SetTextColor(textColor);
    button.SetBackgroundColor(bgColor);
    button.SetBorderColor(borderColor);
}

//+------------------------------------------------------------------+
//| Handle change font button click                                  |
//+------------------------------------------------------------------+
void OnChangeFontButtonClick()
{
    currentFontIndex = (currentFontIndex + 1) % ArraySize(availableFonts);

    inputBox.Font(availableFonts[currentFontIndex]);
    clearButton.Font(availableFonts[currentFontIndex]);
    sendButton.Font(availableFonts[currentFontIndex]);
    toggleThemeButton.Font(availableFonts[currentFontIndex]);
    changeFontButton.Font(availableFonts[currentFontIndex]);

    for (int i = 0; i < ArraySize(quickMessageButtons); i++)
    {
        quickMessageButtons[i].Font(availableFonts[currentFontIndex]);
    }

    Print("Font changed to: ", availableFonts[currentFontIndex]);
    ChartRedraw();
}

//+------------------------------------------------------------------+
//| Handle minimize button click                                     |
//+------------------------------------------------------------------+
void OnMinimizeButtonClick()
{
    minimized = true;
    adminPanel.Hide();
    minimizeButton.Hide();
    maximizeButton.Show();
    closeButton.Show();
    Print("Panel minimized.");
}

//+------------------------------------------------------------------+
//| Handle maximize button click                                     |
//+------------------------------------------------------------------+
void OnMaximizeButtonClick()
{
    if (minimized)
    {
        adminPanel.Show();
        minimizeButton.Show();
        maximizeButton.Hide();
        closeButton.Hide();
        Print("Panel maximized.");
    }
}

//+------------------------------------------------------------------+
//| Handle close button click                                        |
//+------------------------------------------------------------------+
void OnCloseButtonClick()
{
    ExpertRemove();
    Print("Admin Panel closed.");
}

//+------------------------------------------------------------------+
//| Send the message to Telegram                                     |
//+------------------------------------------------------------------+
bool SendMessageToTelegram(string message)
{
    string url = "https://api.telegram.org/bot" + InputBotToken + "/sendMessage";
    string jsonMessage = "{\"chat_id\":\"" + InputChatId + "\", \"text\":\"" + message + "\"}";
    char post_data[];
    ArrayResize(post_data, StringToCharArray(jsonMessage, post_data, 0, WHOLE_ARRAY) - 1);

    int timeout = 5000;
    char result[];
    string responseHeaders;

    int res = WebRequest("POST", url, "Content-Type: application/json\r\n", timeout, post_data, result, responseHeaders);

    if (res == 200)
    {
        Print("Message sent successfully: ", message);
        return true;
    }
    else
    {
        Print("Failed to send message. HTTP code: ", res, " Error code: ", GetLastError());
        Print("Response: ", CharArrayToString(result));
        return false;
    }
}


成功编译后,我们启动了程序,如下所示,展示了主题按钮的一些令人印象深刻的效果。主题切换功能运行良好,显著提升了视觉呈现效果。一个令人惊叹的方面是,这些颜色可以在代码中进行优化,以适应您的偏好。此外,我们还可以加入颜色输入选项,以实现在代码之外的颜色定制。


高级管理员面板

全新主题管理面板

下图是控制面板上执行的所有操作,包括错误处理的示意图。无法发送自定义消息的原因可能是Telegram机器人令牌和聊天 ID填写缺失或错误。如图所示,这些字段被留空了。确保这些认证信息输入正确非常重要,因为它们对于后续操作至关重要。请记住保护好这些认证信息,以防止未经授权的访问。 

EA的日志

EA的日志

结论

这是我们交易系统管理面板开发过程中的又一里程碑。我们已经成功地将主题管理算法整合到现有类中,并且没有观察到对依赖相同库的其他平台功能造成任何性能影响。这些改进主要是为了学习和研究目的。然而,修改类和整合新方法可能会产生积极影响,但如果实施不当,也可能会带来不良后果的风险。我们的项目现在变得更加复杂,集成了Telegram功能和高级可视化特性。

我对我们的进展感到满意,并希望您从使用 MQL5 库文件的工作中获得宝贵的洞见。使用本项目中所采用的一些方法,仍然可以实现更多功能。我已将修改后的源文件附在下方。请注意,主题切换功能依赖于这些库类的存在。如果您遇到任何问题,请考虑重新安装MetaTrader 5以恢复系统文件并将类重置为默认状态。

关于 MQL5 库文件的修改和讨论,特别是与 GUI 组件相关的部分,仅供教育目的。虽然编辑这些文件可以产生视觉上令人满意的结果,但请谨慎操作。修改头文件可能会在您的交易应用程序中引入意外行为、不稳定或兼容性问题。彻底了解所做的更改并保留原始文件的备份至关重要。我们建议在部署到实时交易系统之前,在一个安全的环境中对任何修改进行测试。本材料的作者对因编辑 MQL5 头文件而可能出现的任何问题概不负责。


回到内容页


本文由MetaQuotes Ltd译自英文
原文地址: https://www.mql5.com/en/articles/16045

附加的文件 |
Admin_Panel.mq5 (14.69 KB)
非洲水牛优化(ABO) 非洲水牛优化(ABO)
本文介绍了非洲水牛优化(ABO)算法,这是一种于2015年开发的元启发式方法,基于这些动物的独特行为。文章详细描述了算法实现的各个阶段及其在解决复杂问题时的效率,这使得它成为优化领域中一个有价值的工具。
从基础到中级:Include 指令 从基础到中级:Include 指令
在今天的文章中,我们将讨论一个在 MQL5 中可以找到的各种代码中广泛使用的编译指令。虽然这里对这个指令的解释相当肤浅,但重要的是你要开始了解如何使用它,因为随着你进入更高层次的编程,它很快就会变得不可或缺。此处提供的内容仅用于教育目的。在任何情况下,除了学习和掌握所提出的概念外,都不应出于任何目的使用此应用程序。
您应当知道的 MQL5 向导技术(第 37 部分):配以线性和 Matérn 内核的高斯过程回归 您应当知道的 MQL5 向导技术(第 37 部分):配以线性和 Matérn 内核的高斯过程回归
线性内核是机器学习中,针对线性回归和支持向量机所用的同类中最简单的矩阵。另一方面,Matérn 内核是我们在之前的文章中讲述的径向基函数的更普遍版本,它擅长映射不如 RBF 假设那样平滑的函数。我们构建了一个自定义信号类,即利用两个内核来预测做多和做空条件。
Connexus的头(第三部分):掌握HTTP请求头的使用方法 Connexus的头(第三部分):掌握HTTP请求头的使用方法
我们继续开发Connexus库。在本章中,我们探讨HTTP协议中请求头的概念,解释它们是什么、它们的用途以及如何在请求中使用它们。我们将涵盖用于与API通信的主要头信息,并展示了如何在库中配置它们的实例。