
Erstellen eines Administrator-Panels für den Handel in MQL5 (Teil III): Erweiterung der installierten Klassen für die Theme-Verwaltung (II)
Inhalt:
- Einführung
- Verstehen von Klassen in MQL5
- Hinzufügen von Methoden zur Verwaltung der Farbmodi (Theme) zu (CDialog, CEdit und CButton)
- Die Klasse CButton für die Theme-Verwaltung
- Die Klasse CEdit für die Theme-Verwaltung
- Anpassen des Admin-Panels für einen Theme-Wechsel
- Endgültiger Code und Ergebnisse
- Schlussfolgerung
Einführung
Es ist möglich, Bibliotheksklassen für MQL5 zu ändern und neue zu erstellen. Da die eingebauten Bibliotheken jedoch von der Plattform gemeinsam genutzt werden, können alle Änderungen, die wir an diesen Dateien vornehmen, entweder zu positiven Verbesserungen oder zu negativen Auswirkungen auf die aktuellen Plattformfunktionen führen. In unserem letzten Artikel haben wir kurz besprochen, wie wir die Farbe der Root-Dialogklasse bearbeitet haben, um das Aussehen unseres Panels zu beeinflussen. Unsere Schaltfläche zum Umschalten des Farbmodus oder engl. Themes änderte zwar erfolgreich die Textfarbe, nicht aber das Erscheinungsbild des Panels oder die Hintergrundfarbe der Schaltfläche.
Durch Forschung haben wir schließlich Methoden gefunden, um Theme-Verändernde Funktionalitäten sicher in die verfügbaren Klassen zu integrieren. Nach der erfolgreichen Implementierung dieser Änderungen haben wir den Algorithmus des Admin Panels an die neu integrierten Funktionen angepasst.
Erfolgreicher Theme-Wechsel
Die heutige Diskussion konzentriert sich auf den Prozess, den wir unternommen haben, um das visuell ansprechende Panel auf der rechten Seite zu erhalten. Die gezeigten Theme-Farben basieren auf meiner Einstellung zur Farbauswahl während der Entwicklung; sie können im Code optimiert werden, um anderen Nutzerpräferenzen zu entsprechen, sodass Sie mit verschiedenen Farben experimentieren können, um herauszufinden, was Ihnen gefällt. Es ist wichtig, die Schlüsselkomponenten unseres Programms hervorzuheben, die zur Gesamtfunktionalität des Panels beitragen.
Ich werde sie auflisten:
- Textfarbe
- Oberflächenfarbe der Schaltfläche
- Ränder
- Hintergrundfarbe
Das sind im Wesentlichen die sichtbarsten Merkmale unseres Programms. Wenn wir eine Änderung des Themes auslösen, muss jede Komponente reagieren, indem sie ihre Anzeigeeigenschaften ändert, um die gewünschten, im Code definierten Farben zu zeigen. Am Ende dieser Diskussion möchten wir Sie mit den Fähigkeiten ausstatten, die Sie benötigen, um die verfügbaren Klassen bei der Arbeit mit Schnittstellen zu ändern und zu erweitern, wie in diesem Projekt demonstriert.
Verstehen von Klassen in MQL5.
Um sicherzustellen, dass sowohl Experten als auch Neulinge folgen können, möchte ich alle zunächst mit dem Konzept der Klassen vertraut machen, wie es in MQL5 verwendet wird. Im Folgenden finden Sie die Definitionen und Schlüsselkonzepte, die uns helfen, die Funktionsweise von Klassen in dieser Programmierumgebung zu verstehen.
Klassen:
Klassen sind die Grundlage der objektorientierten Programmierung (OOP) in MQL5. Sie ermöglichen es Entwicklern, verwandte Variablen (Attribute) und Funktionen (Methoden) in einer einzigen Einheit zusammenzufassen, um komplexe Konzepte und Verhaltensweisen in einem Programm darzustellen.
Aufteilung einer Klasse in zwei Klassen:
- Attribute: Variablen, die den Zustand oder die Daten von Objekten der Klasse speichern.
- Methoden: Funktionen, die das Verhalten oder die Aktionen für Objekte der Klasse definieren
Überblick über die wichtigsten Merkmale einer Klasse:
- Bei der Kapselung in einer Klasse werden Daten (Variablen) und Methoden (Funktionen), die mit diesen Daten arbeiten, gebündelt, sodass sie vor externem Zugriff und Missbrauch geschützt sind.
- Die Vererbung ermöglicht es einer Klasse, Eigenschaften und Methoden von einer anderen Klasse zu erben, wodurch die Wiederverwendung von Code gefördert und eine hierarchische Struktur geschaffen wird.
- Polymorphismus ermöglicht die Überschreibung von Methoden, sodass Unterklassen spezifische Implementierungen für Methoden bereitstellen können, die bereits in ihren Elternklassen definiert sind.
- Die Abstraktion vereinfacht die Modellierung komplexer Systeme, indem sie sich nur auf die relevanten Daten und Methoden konzentriert und unnötige Details vor dem Nutzer verbirgt.
Auffinden der MQL5-Header-Dateien
Ich habe einen typischen MQL5-Klassenquelltext verwendet, um uns zu helfen, die Klassen und ihre Strukturen aus einem praktischen Blickwinkel heraus zu verstehen. Siehe den Codeausschnitt unten, und ich habe den Aufbau direkt darunter in Tabellenform erklärt.
//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); };
Diese Tabelle ist eine Zusammenfassung der Attribute, die in den obigen Codeschnipseln verfügbar sind, und ihrer Beschreibung.
Attribute (Eigenschaften) | Beschreibung |
---|---|
bool m_panel_flag; | Flag, das anzeigt, ob das Panel sichtbar ist. |
bool m_minimized; | Flag, das anzeigt, ob der Dialog minimiert ist |
CWnd m_caption; | Steuerung für den Beschriftungstext. |
CWnd m_client_area; | Steuerung für den Client-Bereich, in dem sich andere Elemente befinden. |
CRect m_norm_rect; | Koordinaten für den normalen (nicht-minimierten) Zustand. |
CRect m_min_rect; | Koordinaten für den minimierten Zustand. |
CWnd m_white_border; | Steuerung für den weißen Rahmen um das Dialogfeld. |
In dieser Tabelle sind die im Code der Beispielklasse verwendeten Methoden zusammengefasst.
Methoden | Beschreibung |
---|---|
CDialog(void) | Konstruktor, der den Dialog initialisiert. |
~CDialog(void) | Destruktor zum Aufräumen von Ressourcen. |
Create(...) | Erzeugt das Dialogfenster und seine Steuerelemente. |
OnEvent(...) | Behandelt Chartereignisse für den Dialog. |
Caption(void) | Gibt den aktuellen Beschriftungstext zurück. |
Caption(const string text) | Legt den Beschriftungstext fest. |
Add(CWnd *control) | Fügt dem Client-Bereich ein Steuerelement per Zeiger hinzu. |
Add(CWnd &control) | Fügt dem Client-Bereich ein Steuerelement per Referenz hinzu. |
Save(const int file_handle) | Speichert den Zustand des Dialogs in einer Datei. |
Load(const int file_handle) | Lädt den Dialogstatus aus einer Datei. |
UpdateThemeColors(bool darkTheme) | Aktualisiert die Farben des Themes (dunkel oder hell). |
CreateWhiteBorder(void) | Erzeugt den weißen Rahmen für den Dialog. |
CreateBackground(void) | Erzeugt den Hintergrund des Dialogs. |
CreateCaption(void) | Erzeugt den Beschriftungsbereich. |
CreateButtonClose(void) | Erzeugt die Schaltfläche zum Schließen. |
CreateClientArea(void) | Erzeugt den Client-Bereich. |
OnClickCaption(void) | Behandelt das Klickereignis auf der Beschriftung. |
OnClickButtonClose(void) | Behandelt das Klickereignis Schließen. |
OnDialogDragStart(void) | Behandelt den Beginn eines Dialog-Zieh-Ereignisses. |
OnDialogDragProcess(void) | Behandelt das Ende eines Dialog-Zieh-Ereignisses |
OnDialogDragEnd(void) | Behandelt das Ende eines Dialog-Zieh-Ereignisses. |
Schauen wir uns im Folgenden kurz eine der prominenten Klassen an, die wir in unserem Programm verwenden.
Hinzufügen von Methoden für die Theme-Verwaltung zu (CDialog, CEdit und CButton)
Ich glaube, wir haben jetzt ein klareres Verständnis für die Methoden, die wir einsetzen müssen, um unser Ziel des Theme-Wechsels zu erreichen. Die Dialogbibliothek enthält bereits die wesentlichen Funktionen, die erforderlich sind, und unser nächster Schritt wird darin bestehen, die erforderlichen Methoden einzubauen.
Die Methode CDialog für die Theme-Verwaltung:
CDialog:
Die CDialog-Klasse in MQL5 ist für die Erstellung und Verwaltung von nutzerdefinierten grafischen Dialogfenstern oder Panels innerhalb der MetaTrader 5-Plattform verantwortlich. Es ermöglicht Entwicklern, Dialogfelder zu erstellen, die UI-Komponenten wie Beschriftungen, Client-Bereiche, Rahmen und Schließen-Schaltflächen enthalten. Die Klasse verarbeitet Nutzerinteraktionen wie das Klicken und Ziehen des Dialogs sowie die dynamische Aktualisierung seines Themes (z. B. das Umschalten zwischen dunklem und hellem Farbmodus). Darüber hinaus bietet es Methoden zum Speichern und Laden des Status des Dialogs, um sicherzustellen, dass seine Größe, Position und der Minimierungsstatus erhalten bleiben. Dem Dialog können Steuerelemente wie Schaltflächen und Textfelder hinzugefügt werden, was ihn zu einem vielseitigen Werkzeug für den Aufbau interaktiver und visuell ansprechender Schnittstellen in Handelsanwendungen macht.
In der Klasse CDialog haben wir eine Methode zur Handhabung dynamischer Theme-aktualisierungen eingeführt. Diese Methode ist für die Aktualisierung des visuellen Erscheinungsbildes des Dialogs verantwortlich, je nachdem, ob darkTheme aktiv ist oder nicht. Im Folgenden wird erläutert, wie die Methode eingebunden ist und wie sie mit den anderen Komponenten der Klasse CDialog zusammenhängt. Ich werde das in zwei Schritten erklären. Sie können den ersten Schritt jedoch überspringen, wenn Sie nicht beabsichtigen, Farben zu definieren.
Step 1: Definition der Farben der Themes
Es ist notwendig, die Farben zu definieren, damit das Programm die Alternativen kennt, wenn ein Theme-Wechsel aufgerufen wird. In dieser Implementierung verwendet unsere Methode spezifische Farbdefinitionen für dunkle und helle Farbmodi. Dabei kann es sich um vordefinierte Konstanten handeln oder um die Übergabe von Parametern.
// 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;
Schritt 2: Die Methode zum Aktualisieren der Farben des Themes
Diese Funktion prüft, ob darkTheme aktiv ist (true oder false) und wendet die entsprechenden Farben auf die Hauptkomponenten an: der weiße Rand (m_white_border) wird mit den Hintergrund- und Randfarben aktualisiert; der Hintergrund (m_background) passt seine Hintergrund- und Randfarben an; die Überschrift (m_caption) ändert die Text- und Hintergrundfarben der Titelleiste; und der Client-Bereich (m_client_area) wendet Farbänderungen auf den Client-Bereich an. Schließlich ruft die Funktion Redraw() auf, um sicherzustellen, dass das neue Theme visuell angewendet wird, ohne dass Objekte neu erstellt werden. Wenn Sie den zweiten Schritt übersprungen haben, funktionieren die hervorgehobenen Farbdefinitionen nicht und die Farbe muss z. B. als ClrBlack oder ClrBlue usw. eingegeben werden.
//+------------------------------------------------------------------+ //| 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(); }
Die Klasse CButton für die Theme-Verwaltung
Mit den gleichen Begriffen wie oben haben wir die Methoden SetTextColor, SetBackgroundColor und SetBorderColor zur Klasse CButton hinzugefügt. Mit diesen Methoden können wir die Text-, Hintergrund- und Rahmenfarbe der Schaltfläche festlegen. Der folgende Codeabschnitt zeigt die Implementierung der Methoden.
//--- 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); }
Das Standardprogramm von 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 mit integrierter Methode für die Theme-Verwaltung:
Siehe den hervorgehobenen Abschnitt.
//+------------------------------------------------------------------+ //| 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()); } //+------------------------------------------------------------------+
Die Klasse CEdit für die Theme-Verwaltung
Dies ist eine der wichtigsten Klassen in unserem Projekt, die das Eingabefeld steuert, in das wir unsere Nachricht eingeben werden. Standardmäßig sind unser Panel und seine Komponenten auf einen weißen Hintergrund mit schwarzem Text im Vordergrund eingestellt. Wenn wir auf die Schaltfläche zum Wechseln des Themes klicken, ändert sich die Vordergrundfarbe zu Weiß. Während der Entwicklung ist mir jedoch aufgefallen, dass die Farbe des Eingabefeldes unverändert blieb, sodass sie beim Umschalten des Themes gelegentlich mit dem Text verschmolz. Daher müssen wir der Klasse CEdit eine Methode hinzufügen, um den Wechsel des Themes zu handhaben und sicherzustellen, dass das Texteingabefeld mit den Zielen unseres Themes übereinstimmt.
Die Standardklasse CEdit verfügt bereits über Methoden zum Einstellen von Farben (OnSetColor, OnSetColorBackground und OnSetColorBorder). Wir können diese Methoden verwenden, um das Aussehen des CEdit-Objekts zu aktualisieren, wenn sich das Theme ändert. Wir setzen neue Methoden für Theme Switching ein, indem wir diese Terme hinzufügen: SetTextColor-, SetBackgroundColor- und SetBorderColor-Methoden in die CEdit-Klasse. Diese Methoden aktualisieren die jeweiligen Farben und rufen die vorhandenen Methoden (OnSetColor, OnSetColorBackground, OnSetColorBorder) auf, um die Änderungen auf das Chartobjekt anzuwenden.
//+------------------------------------------------------------------+ //| 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()); }
Nachfolgend sehen wir uns den unbearbeiteten Quellcode der Klasse CEdit an und werden dann das eingebaute Programm direkt darunter weitergeben.
CEdit-Standard von MQL5:
//+------------------------------------------------------------------+ //| 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 mit integrierter Theme Management Methode:
Siehe die hervorgehobenen Abschnitte.
//+------------------------------------------------------------------+ //| 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()); } //+------------------------------------------------------------------+Wir haben unsere Control-Include-Dateien für das Admin-Panel erfolgreich vorbereitet und sind der Fertigstellung unseres Projekts näher als je zuvor. Im nächsten Abschnitt werden wir unsere Bemühungen abschließen, indem wir den Code des Admin-Panels Expert Advisor so anpassen, dass der Wechsel des Themes unterstützt wird, um mit der jüngsten Entwicklung übereinzustimmen.
Anpassen des Admin-Panels für den Theme-Wechsel.
In unserer Theme-Verwaltung gibt es logischerweise vier Schlüsselbereiche.
- Die Funktion zum Wechseln des Farbmodus in unserem Admin-Panel muss sich um die boolesche Variable darkTheme und die Funktion UpdateThemeColors() drehen. Und so funktioniert sie:
bool darkTheme = false;
- Das obige Flag bestimmt, ob das aktuelle Theme dunkel oder hell ist. Sie wird durch Drücken von toggleThemeButton umgeschaltet, wie unten dargestellt.
void OnToggleThemeButtonClick() { darkTheme = !darkTheme; UpdateThemeColors(); Print("Theme toggled: ", darkTheme ? "Dark" : "Light"); }
- Ein Klick auf die Schaltfläche zum Umschalten des Themes ruft diese Funktion auf, die das Flag darkTheme umkehrt und anschließend das Theme der Nutzeroberfläche über UpdateThemeColors() aktualisiert.
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(); }
Basierend auf dem Flag darkTheme, haben wir verschiedene Farben für Text, Schaltflächenhintergründe, Rahmen und Hintergründe gewählt. Farben werden wie folgt auf verschiedene UI-Komponenten angewendet:
- Text Box (inputBox): Die Funktionen SetTextColor, SetBackgroundColor und SetBorderColor werden verwendet, um das Design anzuwenden.
- Buttons: Die Funktion UpdateButtonTheme() wird für jede Schaltfläche aufgerufen, wobei die Textfarbe, die Hintergrundfarbe und die Farbe des Rahmens wie festgelegt eingestellt werden.
- Character Counter: Legt die Farbe direkt fest, wenn wir auf die Schaltfläche „Theme“ klicken.
//Theme button application void UpdateButtonTheme(CButton &button, color textColor, color bgColor, color borderColor) { button.SetTextColor(textColor); button.SetBackgroundColor(bgColor); button.SetBorderColor(borderColor); }
Wir haben oben eine Hilfsfunktion verwendet, um alle relevanten Theme-bezogenen Farbeinstellungen auf jede Schaltfläche anzuwenden. Dadurch wird der sich wiederholende Code bereinigt und die Konsistenz zwischen den Schaltflächen gewährleistet. Wenn man alle Codeschnipsel zusammenfasst und sie in das Hauptprogramm des Admin-Panels integriert, haben wir alle Funktionen, die dem Ziel entsprechen.
Endgültiger Code und Ergebnisse
Hier ist der endgültige Entwurf unseres Programms mit den neuen Funktionen.
//+------------------------------------------------------------------+ //| 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; } }
Neue Themetische Administrationsoberfläche
Die folgende Abbildung zeigt alle Operationen, die auf dem Panel ausgeführt werden, einschließlich der Fehlerbehandlung. Das Scheitern beim Senden einer nutzerdefinierten Nachricht kann auf fehlende oder falsche Einträge für Telegram-Bot-Token und Chat-ID zurückgeführt werden. Wie auf dem Bild zu sehen ist, wurden diese Felder leer gelassen. Es ist wichtig, dass diese Daten korrekt eingegeben werden, da sie für den Betrieb entscheidend sind. Bitte denken Sie daran, diese Anmeldedaten sicher aufzubewahren, um unbefugten Zugriff zu verhindern.
Expert-Log
Schlussfolgerung
Dies markiert einen weiteren Meilenstein in der Entwicklung unseres Trading Systems Admin Panel. Wir haben erfolgreich Algorithmen für die Theme-Verwaltung in bestehende Klassen integriert, ohne dass dabei Leistungsprobleme mit anderen Plattformfunktionen, die auf denselben Bibliotheken basieren, beobachtet wurden. Diese Fortschritte dienen in erster Linie zu Lern- und Forschungszwecken. Die Änderung von Klassen und die Integration neuer Methoden können sich jedoch positiv auswirken, bergen aber auch die Gefahr unerwünschter Ergebnisse, wenn sie nicht korrekt umgesetzt werden. Unser Projekt ist nun komplexer geworden, da wir sowohl Telegram-Funktionen als auch erweiterte Visualisierungsfunktionen integriert haben.
Ich freue mich über unsere Fortschritte und hoffe, dass Sie aus der Arbeit mit den Bibliotheksdateien in MQL5 wertvolle Erkenntnisse gewonnen haben. Es gibt noch viel mehr, was mit einigen der in diesem Projekt verwendeten Ansätze erreicht werden kann. Ich habe die geänderten Quelldateien unten angehängt. Bitte beachten Sie, dass die Funktion zum Wechseln des Themes auf das Vorhandensein dieser Bibliotheksklassen angewiesen ist. Wenn Sie Probleme haben, sollten Sie eine Neuinstallation des MetaTrader 5 in Erwägung ziehen, um die Systemdateien wiederherzustellen und die Klassen auf ihren Standardzustand zurückzusetzen.
Übersetzt aus dem Englischen von MetaQuotes Ltd.
Originalartikel: https://www.mql5.com/en/articles/16045





- Freie Handelsapplikationen
- Über 8.000 Signale zum Kopieren
- Wirtschaftsnachrichten für die Lage an den Finanzmärkte
Sie stimmen der Website-Richtlinie und den Nutzungsbedingungen zu.