
Wie erstellt man ein grafisches Panel beliebiger Komplexität?
Inhaltsverzeichnis
- Einführung
- Erstellen einer Panel-Basis mit CAppDialog
- Was AppWindow tun kann
- Die Struktur des CAppDialog-Objekts
- Das Schema der Vererbung von Objekten
- Wo finde ich die wichtigsten Konstanten zum Erstellen von Objekten und wie man sie mit #undef neu definiert?
- So werden neue Steuerelemente hinzugefügt: zwei Schaltflächen
- Geschachtelte Steuerelemente werden verschoben und gezeichnet
- Hinzufügen von CAppDialog zur Gruppe der Steuerelemente mittels CDialog
- So überschreiben wird das Verhalten von standardmäßigen Steuerelementen überschrieben
- Wie integrierte Makros vom Typ Ereignisverarbeitung gelesen werden
- Erstellen Sie Ihr eigenes Panel - es ist ganz einfach!
Einführung
Auch heute noch nutzen die meisten Programmierer, die Indikatoren und Expert Advisors für die MetaTrader 5 Plattform entwickeln, nicht die verfügbaren Möglichkeiten zur Erstellung von grafischen Oberflächen in ihren Anwendungen. Ich glaube, das liegt daran, dass die Klassen Panels und Dialogs der Standardbibliothek nur eine kurze technische Beschreibung der Methoden liefern. Die Referenz der Programmiersprache enthält Code-Beispiele mit Kommentaren für viele grafische Steuerelemente. Aber Sie können nicht anfangen, Ihre eigenen Panels zu erstellen, ohne deren Struktur und Idee vollständig zu verstehen.
Ich habe versucht zu verstehen, wie die Panels angeordnet sind. Nun möchte ich das erworbene Wissen mit anderen Entwicklern teilen. Ich begann mit einer einfachen Anwendung, die ein grafisches Panel basierend auf der Klasse CAppDialog erstellt. Dann modifizierte ich es in Schritten und analysierte die erzielten Ergebnisse.
Der Artikel enthält alle notwendigen Details zur Bedienung der CAppDialog-Klasse: wie man ein Panel erstellt, welche Funktionen mindestens benötigt werden und wie man zusätzliche Elemente (z.B. Schaltflächen) hinzufügt. Wir analysieren die Objekte, aus denen das Panel besteht, und die Reihenfolge, in der sie erstellt werden sollen. Ich werde auch zeigen, welche Konstanten bei der Erstellung eines Panels verwendet werden und wie man sie ändert.
Erstellen einer Panel-Basis mit CAppDialog
Beginnen wir mit ein wenig Hintergrundinformationen.
CAppDialog ist eine Klasse des Steuerelementes des kombinierten "Application Dialog" Controls. Die Klasse CAppDialog vereint visuell Gruppen von funktional zusammenhängenden unähnlichen Elementen innerhalb einer MQL5-Anwendung.
Der Mindestcode, der ein Panel erzeugt, wird unten gezeigt:
//+------------------------------------------------------------------+ //| LearnCAppDialog.mq5 | //| Copyright 2018, MetaQuotes Software Corp. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Copyright 2018, MetaQuotes Software Corp." #property link "https://www.mql5.com" #property version "1.00" #include <Controls\Dialog.mqh> CAppDialog AppWindow; //+------------------------------------------------------------------+ //| Initialisierungsfunktion des Experten | //+------------------------------------------------------------------+ int OnInit() { //--- Erstellen des Dialogs der Anwendung if(!AppWindow.Create(0,"AppWindow",0,20,20,360,324)) return(INIT_FAILED); //--- Starten der Anwendung AppWindow.Run(); //--- war erfolgreich return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Deinitialisierungsfunktion des Experten | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { //--- Löschen des Dialogs AppWindow.Destroy(reason); } //+------------------------------------------------------------------+ //| Ereignisfunktion des Charts des Experten | //+------------------------------------------------------------------+ void OnChartEvent(const int id, // Ereignis ID const long& lparam, // Ereignisparameter vom Typ long const double& dparam, // Ereignisparameter vom Typ double const string& sparam) // Ereignisparameter vom Typ string { AppWindow.ChartEvent(id,lparam,dparam,sparam); } //+------------------------------------------------------------------+
Das Ergebnis der Ausführung des Expert Advisors LearnCAppDialogs.mq5 ist das erstellte Panel der Steuerelemente:
Der Expert Advisor LearnCAppDialog.mq5 enthält ein Minimum an Befehlen, die für die Erstellung eines Panels und dessen Bedienung erforderlich sind. Führen Sie die folgenden Schritte aus:
- Deklaration einer Instanz der CAppDialog-Klasse auf der globalen Programmebene:
#include <Controls\Dialog.mqh> CAppDialog AppWindow; //+------------------------------------------------------------------+ //| Initialisierungsfunktion des Experten | //+------------------------------------------------------------------+
- Erstellen des Panels AppWindow und der Start des Panels:
int OnInit() { //--- Erstellen des Dialogs der Anwendung if(!AppWindow.Create(0,"AppWindow",0,20,20,360,324)) return(INIT_FAILED); //--- Starten der Anwendung AppWindow.Run(); //--- war erfolgreich return(INIT_SUCCEEDED); }
- Übergabe der Ereignisse ChartEvent an AppWindow Panel :
//+------------------------------------------------------------------+ //| Ereignisfunktion des Charts des Experten | //+------------------------------------------------------------------+ void OnChartEvent(const int id, // Ereignis ID const long& lparam, // Ereignisparameter vom Typ long const double& dparam, // Ereignisparameter vom Typ double const string& sparam) // Ereignisparameter vom Typ string { AppWindow.ChartEvent(id,lparam,dparam,sparam); }
Und der letzte, ganz besonders wichtige Schritt:
- Löschen des Steuerelementes durch den Aufruf der Methode Destroy
void OnDeinit(const int reason) { //--- Löschen des Dialogs AppWindow.Destroy(reason); }
Falls die Panele nicht gelöscht werden, würde bei jedem Wechsel des Zeitrahmens oder des Symbols ein neues zusätzliches Element über den bereits existierenden erstellt werden.
Was AppWindow tun kann
Ein Panel auf Basis von CAppDialog kann theoretisch folgende Ereignisse bearbeiten:
//+------------------------------------------------------------------+ //| Ereignis | //+------------------------------------------------------------------+ #define ON_CLICK (0) // Klick auf das Steuerelement #define ON_DBL_CLICK (1) // Doppelklick auf das Steuerelement #define ON_SHOW (2) // Anzeige des Steuerelements #define ON_HIDE (3) // Ausblenden des Steuerelements #define ON_CHANGE (4) // Ändern des Steuerelements #define ON_START_EDIT (5) // Beginn des Bearbeitungsereignisses #define ON_END_EDIT (6) // Ende des Bearbeitungsereignisses #define ON_SCROLL_INC (7) // Inkrement des Ereignisses der Bildlaufleiste #define ON_SCROLL_DEC (8) // Dekrement des Ereignisses der Bildlaufleiste #define ON_MOUSE_FOCUS_SET (9) // Der "Mauskursor betritt das Steuerelement" #define ON_MOUSE_FOCUS_KILL (10) // Der "Mauskursor verlässt das Steuerelement" #define ON_DRAG_START (11) // Das "Ziehen des Steuerelements" beginnt #define ON_DRAG_PROCESS (12) // Das "Steuerelement wird gerade gezogen" #define ON_DRAG_END (13) // Das "Ziehen des Steuerelements" endet #define ON_BRING_TO_TOP (14) // Die "Priorität der Maus" wurde erhöht #define ON_APP_CLOSE (100) // Das Ereignis "Anwendung schließen"
Diese Ereignisse sind Teil des Blocks Events in der Datei [Datenordner]\MQL5\Include\Controls\Definiert.mqh. Damit verfügen die Ereignisse den Klick, Doppelklick, Start und Ende der Bearbeitung, Fokussierung, Ziehen (Anfang, Prozess und Ende), Ein- und Ausblenden des Panels. Beispiele für das Arbeiten mit diesen Ereignissen finden Sie in den Beispielen im Abschnitt Panels und Dialogs. Das Ereignis ON_CHANGE wird im Beispiel CRadioGroup behandelt, ON_SCROLL_INC und ON_SCROLL_DEC werden in CScrollV behandelt.
Die Struktur des CAppDialog-Objekts
Launch the LearnCAppDialog.mq5 Expert Advisor on an empty chart, press Ctrl+B and click "All" to see all objects the panel consists of:
Objekte aus dem Abschnitt Panels und Dialogs der Standardbibliothek werden in der folgenden Reihenfolge angelegt und angewendet. Zuerst wird ein "Border"-Objekt erstellt, darin wird der Panelhintergrund als "Back"-Objekt eingefügt. Dann wird der Client-Area "ClientBack" über den Hintergrund gelegt. Untergeordnete Steuerelemente können innerhalb der Client-Area hinzugefügt werden. Das Caption-Objekt mit dem Namen des Panels und zwei Steuertasten werden dem oberen Teil des Panels hinzugefügt.
Der Prozess kann schematisch dargestellt werden, um die Reihenfolge der Erstellung dieser Objekte zu sehen:
Das Objekt Border ist OBJ_RECTANGLE_LABEL mit einem weißen Rahmen (der Standard für alle Panele). Das Objekt Border wird also für rein ästhetische Zwecke verwendet: es zeigt einen weißen Rand, während der Körper des Objekts Border hinter dem Objekt Back verborgen ist.
Das Schema der Vererbung von Objekten
Es mag den Anschein haben, dass der Abschnitt Panels und Dialogs zu viele Klassen mit umfangreichen Beziehungen und Vererbungsstrukturen hat. Aber die Hierarchie ist sehr einfach. Wenn Sie also verstehen, woraus CAppDialog besteht und wie er erstellt wird, ist es auch einfach, die andere Klassen zu verstehen. Hier ist das Vererbungsschema aller Klassen aus der Standardbibliothek:
Das Fenster AppWindow im Expert Advisor LearnCAppDialog.mq5 besteht aus sechs Objekten, von denen jedes seine spezifische Aufgabe erfüllt.
Ein CAppDialog-basiertes Panel kann aus einem Expert Advisor oder aus einem Indikator erstellt werden. Die Erstellung des Panels kann jedoch je nach Art des Programms (Expert Advisor oder Indikator), das das Panel und das Unterfenster, in dem das Programm läuft, erstellt, unterschiedlich sein:
- Wenn ein Programm ein Expert Advisor ist (der Typ des laufenden Programms ist PROGRAM_EXPERT), dann wird das Panel NUR im Hauptfenster erstellt (der Fensterindex ist "0") und nur mit der Methode CAppDialog::CreateExpert.
- Ist ein Programm ein Indikator (der Typ des laufenden Programms ist PROGRAM_INDICATOR), dann wird die Nummer des Fensters, in dem das Programm läuft, geprüft:
- Wenn es das Hauptfenster ist (die Fensternummer ist 0), wird das Panel mit der Methode CAppDialog::CreateIndicator erstellt
- Wenn es sich um ein Subfenster handelt, wird das Panel mit der Methode CAppDialog::CreateExpert erstellt.
Die Besonderheit der Methode CAppDialogs::CreateIndicator st, dass das Panel während der Erstellung automatisch folgendes macht:
- Anpassen an die Fensterbreite
- Anpassen an die Fensterhöhe des Panels
Ein Beispiel für ein Panel eines Indikators [Datenordner]\MQL5\Indikatoren\Beispiele\Panels\SimplePanel\SimplePanel.mq5 nach der Erstellung und Minimierung:
CreateExpert erstellt ein Panel im Hauptfenster (die Fensternummer ist 0) und impliziert, dass das Programm, das das Panel erstellt, ein Expert Advisor ist.
Es gibt eine Ausnahme von diesen Regeln: Im Hauptfenster eines Indikators kann ein Panel erstellt werden. In diesem Fall wird die Methode CreateIndicator zur Panelerstellung angewendet.
Wo finde ich die wichtigsten Konstanten zum Erstellen von Objekten und wie man sie mit #undef neu definiert?
Der Code wird im Expert Advisor AppWindowEditDefine.mq5 implementiert werden.
Die grundlegenden Konstanten des Panels und seiner Steuerelemente befinden sich in der Datei [Datenordner]\MQL5\Include\Controls\Defines.mqh, die in der Klasse CWnd eingebunden ist:
//+------------------------------------------------------------------+ //| Wnd.mqh | //| Copyright 2009-2017, MetaQuotes Software Corp. | //| http://www.mql5.com | //+------------------------------------------------------------------+ #include "Rect.mqh" #include "Defines.mqh" #include <Object.mqh> class CDragWnd;
Die Hierarchie der Vererbung ist wie folgt:
- CWnd
- CWndContainer
- CDialog
- CAppDialog
Speziell interessiert uns die folgende Gruppe von Konstanten:
//+------------------------------------------------------------------+ //| Stile und Farben der Darstellung | //+------------------------------------------------------------------+ //--- Allgemein #define CONTROLS_FONT_NAME "Trebuchet MS" #define CONTROLS_FONT_SIZE (10) //--- Text #define CONTROLS_COLOR_TEXT C'0x3B,0x29,0x28' #define CONTROLS_COLOR_TEXT_SEL White #define CONTROLS_COLOR_BG White #define CONTROLS_COLOR_BG_SEL C'0x33,0x99,0xFF' //--- Schaltfläche #define CONTROLS_BUTTON_COLOR C'0x3B,0x29,0x28' #define CONTROLS_BUTTON_COLOR_BG C'0xDD,0xE2,0xEB' #define CONTROLS_BUTTON_COLOR_BORDER C'0xB2,0xC3,0xCF' //--- Label #define CONTROLS_LABEL_COLOR C'0x3B,0x29,0x28' //--- Edit #define CONTROLS_EDIT_COLOR C'0x3B,0x29,0x28' #define CONTROLS_EDIT_COLOR_BG White #define CONTROLS_EDIT_COLOR_BORDER C'0xB2,0xC3,0xCF' //--- Blättern #define CONTROLS_SCROLL_COLOR_BG C'0xEC,0xEC,0xEC' #define CONTROLS_SCROLL_COLOR_BORDER C'0xD3,0xD3,0xD3' //--- Client #define CONTROLS_CLIENT_COLOR_BG C'0xDE,0xDE,0xDE' #define CONTROLS_CLIENT_COLOR_BORDER C'0x2C,0x2C,0x2C' //--- Listenansicht #define CONTROLS_LISTITEM_COLOR_TEXT C'0x3B,0x29,0x28' #define CONTROLS_LISTITEM_COLOR_TEXT_SEL White #define CONTROLS_LISTITEM_COLOR_BG White #define CONTROLS_LISTITEM_COLOR_BG_SEL C'0x33,0x99,0xFF' #define CONTROLS_LIST_COLOR_BG White #define CONTROLS_LIST_COLOR_BORDER C'0xB2,0xC3,0xCF' //--- CheckGroup #define CONTROLS_CHECKGROUP_COLOR_BG C'0xF7,0xF7,0xF7' #define CONTROLS_CHECKGROUP_COLOR_BORDER C'0xB2,0xC3,0xCF' //--- RadioGroup #define CONTROLS_RADIOGROUP_COLOR_BG C'0xF7,0xF7,0xF7' #define CONTROLS_RADIOGROUP_COLOR_BORDER C'0xB2,0xC3,0xCF' //--- Dialog #define CONTROLS_DIALOG_COLOR_BORDER_LIGHT White #define CONTROLS_DIALOG_COLOR_BORDER_DARK C'0xB6,0xB6,0xB6' #define CONTROLS_DIALOG_COLOR_BG C'0xF0,0xF0,0xF0' #define CONTROLS_DIALOG_COLOR_CAPTION_TEXT C'0x28,0x29,0x3B' #define CONTROLS_DIALOG_COLOR_CLIENT_BG C'0xF7,0xF7,0xF7' #define CONTROLS_DIALOG_COLOR_CLIENT_BORDER C'0xC8,0xC8,0xC8'
Um diese Makro-Substitutionen zu ändern, verwenden wir die Direktive #undef:
Die #undef-Direktive wird verwendet, um ein zuvor deklariertes Makro zu löschen.
Wir haben also folgenden Algorithmus: das zuvor deklarierte Makro abbrechen; dann das Makro mit einem geänderten Parameter neu deklarieren. Dafür sollten wir den folgenden Trick anwenden: binden wir die Datei Defines.mqh VOR Dialog.mqh ein:
//+------------------------------------------------------------------+ //| AppWindowEditDefine.mq5 | //| Copyright 2018, MetaQuotes Software Corp. | //| http://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Copyright 2018, MetaQuotes Software Corp." #property link "http://www.mql5.com" #property version "1.001" #property description "Control Panels and Dialogs. Demonstration class CBmpButton" #include <Controls\Defines.mqh>
Löschen des Macros nach dem Einbinden von "Defines.mqh":
#undef CONTROLS_FONT_NAME #undef CONTROLS_FONT_SIZE #undef CONTROLS_BUTTON_COLOR #undef CONTROLS_BUTTON_COLOR_BG #undef CONTROLS_BUTTON_COLOR_BORDER #undef CONTROLS_DIALOG_COLOR_BORDER_LIGHT #undef CONTROLS_DIALOG_COLOR_BORDER_DARK #undef CONTROLS_DIALOG_COLOR_BG #undef CONTROLS_DIALOG_COLOR_CAPTION_TEXT #undef CONTROLS_DIALOG_COLOR_CLIENT_BG #undef CONTROLS_DIALOG_COLOR_CLIENT_BORDER
Schreiben der Eingabeparameter:
input string font_name = "Trebuchet MS"; input int font_size = 10; input color button_color = C'0x3B,0x29,0x28'; input color button_color_bg = C'0xDD,0xE2,0xEB'; input color button_color_border = C'0xB2,0xC3,0xCF'; input color dialog_color_border_light = White; input color dialog_color_border_dark = C'0xB6,0xB6,0xB6'; input color dialog_color_bg = C'0xF0,0xF0,0xF0'; input color dialog_color_caption_text = C'0x28,0x29,0x3B'; input color dialog_color_client_bg = C'0xF7,0xF7,0xF7'; input color dialog_color_client_border = C'0xC8,0xC8,0xC8';
Der interessanteste Teil: Wir deklarieren die Macros erneut, aber diesmal mit den Eingabeparameter als deren Werte:
#define CONTROLS_FONT_NAME font_name #define CONTROLS_FONT_SIZE font_size #define CONTROLS_BUTTON_COLOR button_color #define CONTROLS_BUTTON_COLOR_BG button_color_bg #define CONTROLS_BUTTON_COLOR_BORDER button_color_border #define CONTROLS_DIALOG_COLOR_BORDER_LIGHT dialog_color_border_light #define CONTROLS_DIALOG_COLOR_BORDER_DARK dialog_color_border_dark #define CONTROLS_DIALOG_COLOR_BG dialog_color_bg #define CONTROLS_DIALOG_COLOR_CAPTION_TEXT dialog_color_caption_text #define CONTROLS_DIALOG_COLOR_CLIENT_BG dialog_color_client_bg #define CONTROLS_DIALOG_COLOR_CLIENT_BORDER dialog_color_client_border #include <Controls\Dialog.mqh> #include <Controls\BmpButton.mqh>
Beispiel:
Zusammenfassung von CAppDialog
Unser Panel ist das Objekt der Klasse CAppDialog. Es hat die Methode ControlsTotal (die Anzahl der Controls im Container) von der Klasse CWndContainer geerbt. Daher können wir alle Steuerelemente des Panels durchgehen und einige Aktionen auf sie anwenden. Diese Elemente werden im Bereich private der Elternklasse CDialog deklariert:
//+------------------------------------------------------------------+ //| Class CDialog | //| Verwendung: Basisklasse zum Erstellen des Dialogfeldes | //| and indicator panels | //+------------------------------------------------------------------+ class CDialog : public CWndContainer { private: //--- abhängige Steuerelemente CPanel m_white_border; // Das Objekt "weißer Rahmen" CPanel m_background; // Das Objekt Hintergrund CEdit m_caption; // Das Objekt Titelleiste des Fensters CBmpButton m_button_close; // Das Schaltflächenobjekt "Schließen" CWndClient m_client_area; // Das Objekt Client-Area protected:
Der Debugger erlaubt einen Blick auf das Erstellen der Objekte:
//+------------------------------------------------------------------+ //| Erstellen eines Steuerelements | //+------------------------------------------------------------------+ bool CDialog::Create(const long chart,const string name,const int subwin,const int x1,const int y1,const int x2,const int y2) { //--- Methodenaufruf der Elternklasse if(!CWndContainer::Create(chart,name,subwin,x1,y1,x2,y2)) return(false); //--- Erstellen der abhängigen Steuerelemente if(!m_panel_flag && !CreateWhiteBorder()) return(false); if(!CreateBackground()) return(false); if(!CreateCaption()) return(false); if(!CreateButtonClose()) return(false); if(!CreateClientArea()) return(false);
sowie auf die Namensvergabe: m_white_border -> "29437Border", m_background -> "29437Back", m_caption -> "29437Caption", m_button_close -> "29437Close", m_client_area -> "29437Client". In diesen Namen ist die Nummer "29437" die Kennung des Panels für seine Lebensdauer.
So können wir einige Eigenschaften der Elemente der Panele ändern. Zum Beispiel können wir die Farbe des m_client_area und m_background objects ändern:
//+------------------------------------------------------------------+ //| LearnCAppDialog_1.mq5 | //| Copyright 2018, MetaQuotes Software Corp. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Copyright 2018, MetaQuotes Software Corp." #property link "https://www.mql5.com" #property version "1.00" #include <Controls\Dialog.mqh> CAppDialog AppWindow; //+------------------------------------------------------------------+ //| Initialisierungsfunktion des Experten | //+------------------------------------------------------------------+ int OnInit() { //--- Erstellen des Dialogs der Anwendung if(!AppWindow.Create(0,"AppWindow",0,20,20,360,324)) return(INIT_FAILED); //--- int total=AppWindow.ControlsTotal(); CWndClient*myclient; for(int i=0;i<total;i++) { CWnd*obj=AppWindow.Control(i); string name=obj.Name(); PrintFormat("%d is %s",i,name); //--- Farbe if(StringFind(name,"Client")>0) { CWndClient *client=(CWndClient*)obj; client.ColorBackground(clrRed); myclient=client; Print("client.ColorBackground(clrRed);"); ChartRedraw(); } //--- if(StringFind(name,"Back")>0) { CPanel *panel=(CPanel*) obj; panel.ColorBackground(clrGreen); Print("panel.ColorBackground(clrGreen);"); ChartRedraw(); } } AppWindow.Delete(myclient); //--- Starten der Anwendung AppWindow.Run(); //--- war erfolgreich return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Deinitialisierungsfunktion des Experten | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { //--- Löschen des Dialogs AppWindow.Destroy(reason); } //+------------------------------------------------------------------+ //| Ereignisfunktion des Charts des Experten | //+------------------------------------------------------------------+ void OnChartEvent(const int id, // Ereignis ID const long& lparam, // Ereignisparameter vom Typ long const double& dparam, // Ereignisparameter vom Typ double const string& sparam) // Ereignisparameter vom Typ string { AppWindow.ChartEvent(id,lparam,dparam,sparam); } //+------------------------------------------------------------------+
Achten wir auf die Zeile: Sie enthält den Aufruf der Methode CWndContainer::Delete, die ein Element aus der Gruppe (Container) löscht. Nachdem das Element m_client_area aus der Gruppe gelöscht wurde, wird kein entsprechender Befehl an das Objekt m_client_area übergeben, falls Sie versuchen, das Panel zu verschieben. Diese m_client_area bleibt in gleicher Stelle:
Wenn Sie das Panel schließen, wird das Element m_client_area zusammen mit anderen Elementen aus dem Chart gelöscht.
Im folgenden Beispiel verwenden wir statt CWndContainer::Delete die Methode CWndContainer::Destroy, um das Objekt m_client_area zu löschen:
//+------------------------------------------------------------------+ //| LearnCAppDialog_2.mq5 | //| Copyright 2018, MetaQuotes Software Corp. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Copyright 2018, MetaQuotes Software Corp." #property link "https://www.mql5.com" #property version "1.00" #include <Controls\Dialog.mqh> CAppDialog AppWindow; //+------------------------------------------------------------------+ //| Initialisierungsfunktion des Experten | //+------------------------------------------------------------------+ int OnInit() { //--- Erstellen des Dialogs der Anwendung if(!AppWindow.Create(0,"AppWindow",0,20,20,360,324)) return(INIT_FAILED); //--- int total=AppWindow.ControlsTotal(); CWndClient*myclient; for(int i=0;i<total;i++) { CWnd*obj=AppWindow.Control(i); string name=obj.Name(); PrintFormat("%d is %s",i,name); //--- Farbe if(StringFind(name,"Client")>0) { CWndClient *client=(CWndClient*)obj; client.ColorBackground(clrRed); myclient=client; Print("client.ColorBackground(clrRed);"); ChartRedraw(); } //--- if(StringFind(name,"Back")>0) { CPanel *panel=(CPanel*) obj; panel.ColorBackground(clrGreen); Print("panel.ColorBackground(clrGreen);"); ChartRedraw(); } } Sleep(5000); myclient.Destroy(); //--- Starten der Anwendung AppWindow.Run(); //--- war erfolgreich return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Deinitialisierungsfunktion des Experten | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { //--- Löschen des Dialogs AppWindow.Destroy(reason); } //+------------------------------------------------------------------+ //| Ereignisfunktion des Charts des Experten | //+------------------------------------------------------------------+ void OnChartEvent(const int id, // Ereignis ID const long& lparam, // Ereignisparameter vom Typ long const double& dparam, // Ereignisparameter vom Typ double const string& sparam) // Ereignisparameter vom Typ string { AppWindow.ChartEvent(id,lparam,dparam,sparam); } //+------------------------------------------------------------------+
Und so funktioniert es: Eine Pause von 5 Sekunden nach dem Erstellen des Panels und dann wird Client-Area gelöscht:
So werden neue Steuerelemente hinzugefügt: zwei Schaltflächen
Ändern wir den EA aus dem Abschnitt "Erstellen eines Panels basierend auf CAppDialog", indem wir dem Panel zwei Schaltflächen basierend auf der Klasse CButton hinzufügen und als AppWindowTwoButtons.mq5 speichern. Bevor Sie die Schaltflächen hinzufügen (ähnlich wie bei der Gestaltung von Panels), müssen Sie zuerst über deren Größe und Lage nachdenken. Angenommen, das Bild unten zeigt das Panel mit den Schaltflächen, die wir erstellen wollen:
Wobei:
- TOP ist der Abstand vom oberen Rand der Client-Area (festgelegt durch die Konstante INDENT_TOP)
- LEFT ist der Abstand vom linken Rand der Client-Area (gesetzt durch die Konstante INDENT_LEFT)
- HEIGHT ist die Höhe der Schaltfläche (wird durch die Konstante BUTTON_HEIGHT gesetzt).
- WIDTH ist die Breite der Schaltfläche (wird durch die Konstante BUTTON_WIDTH gesetzt).
Eine weitere Konstante, die wir brauchen, ist der minimale horizontale Einzug zwischen den Steuerelementen. Nennen wir es "CONTROLS_GAP_X".
Um die Klasse CButton zu verwenden, müssen wir sie zuerst einbinden:
//+------------------------------------------------------------------+ //| AppWindowTwoButtons.mq5 | //| Copyright 2018, MetaQuotes Software Corp. | //| http://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Copyright 2018, MetaQuotes Software Corp." #property link "http://www.mql5.com" #property version "1.001" #property description "Control Panels and Dialogs. Demonstration class CButton" #include <Controls\Dialog.mqh> #include <Controls\Button.mqh>
Als nächstes ergänzen wir die Konstanten der Größe und des Ortes der Schaltfläche:
//+------------------------------------------------------------------+ //| Definitionen | //+------------------------------------------------------------------+ //--- Einzüge und Abstände #define INDENT_LEFT (11) // Linker Einzug (im Bereich der Rahmenbreite) #define INDENT_TOP (11) // Oberer Einzug (im Bereich der Rahmenbreite) #define CONTROLS_GAP_X (5) // Abstand zur X Koordinate //--- für Schaltflächen #define BUTTON_WIDTH (100) // Breite, X Koordinate #define BUTTON_HEIGHT (20) // Höhe, Y Koordinate //---
Deklaration von zwei Instanzen der Klasse von CButton im globalen Teil des Programms:
#define BUTTON_HEIGHT (20) // Höhe, Y Koordinate //--- CAppDialog AppWindow; CButton m_button1; // Das Objekt Schaltfläche CButton m_button2; // Das Objekt Schaltfläche //+------------------------------------------------------------------+ //| Initialisierungsfunktion des Experten | //+------------------------------------------------------------------+ int OnInit()
Die Deklaration von Schaltflächen auf globaler Ebene ist ein schlechter Stil, da diese Instanzen (und damit ihre Methoden) von überall im Expert Advisor zu sehen sind. Allerdings habe ich es hier bewusst getan, um den Umfang des Codes zu reduzieren.
OnInit() ist leicht zu ändern: wir fügen Aufrufe hinzu und überprüfen die Ergebnisse der Erstellung der Schaltflächen:
//+------------------------------------------------------------------+ //| Initialisierungsfunktion des Experten | //+------------------------------------------------------------------+ int OnInit() { //--- Erstellen des Dialogs der Anwendung if(!AppWindow.Create(0,"AppWindow with Two Buttons",0,40,40,380,344)) return(INIT_FAILED); //--- Erstellen der abhängigen Steuerelemente if(!CreateButton1()) return(false); if(!CreateButton2()) return(false); //--- Starten der Anwendung AppWindow.Run(); //--- war erfolgreich return(INIT_SUCCEEDED); }
Analysieren wir einmal CreateButton1(), um den Prozess der Erstellung der Schaltflächen und der Verknüpfung mit einem Panel im Detail zu betrachten.
Wir verwenden die folgenden Methoden der KLasse CButton: Create zum Erstellen der Schaltfläche:
und Text zum Hinzufügen eines Textes zur Schaltfläche (die Methode Text wird von der Klasse CWndObj abgeleitet):
Die Schaltfläche wird in diesem Stadium erstellt, existiert aber getrennt vom Panel. Um sie zu binden, müssen wir die Methode CDialog::Add ausführen, die die Schaltfläche zur Client-Area des Panels hinzufügt:
if(!AppWindow.Add(m_button1)) return(false); //--- war erfolgreich return(true); }
Hier ist der ganze Code zum Erstellen einer Schaltfläche:
//+------------------------------------------------------------------+ //| Erstellen von "Button1" | //+------------------------------------------------------------------+ bool CreateButton1(void) { //--- Koordinaten int x1=INDENT_LEFT; // x1 = 11 Pixel int y1=INDENT_TOP; // y1 = 11 Pixel int x2=x1+BUTTON_WIDTH; // x2 = 11 + 100 = 111 Pixel int y2=y1+BUTTON_HEIGHT; // y2 = 11 + 20 = 32 Pixel //--- Erstellen if(!m_button1.Create(0,"Button1",0,x1,y1,x2,y2)) return(false); if(!m_button1.Text("Button1")) return(false); if(!AppWindow.Add(m_button1)) return(false); //--- war erfolgreich return(true); }
Vergessen wir nicht, dass wir das Panel in OnDeinit() löschen müssen, und dass wir alle Ereignisse an OnChartEvent() übergeben müssen:
//+------------------------------------------------------------------+ //| Deinitialisierungsfunktion des Experten | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { //--- Comment(""); //--- Löschen des Dialogs AppWindow.Destroy(reason); } //+------------------------------------------------------------------+ //| Ereignisfunktion des Charts des Experten | //+------------------------------------------------------------------+ void OnChartEvent(const int id, // Ereignis ID const long& lparam, // Ereignisparameter vom Typ long const double& dparam, // Ereignisparameter vom Typ double const string& sparam) // Ereignisparameter vom Typ string { AppWindow.ChartEvent(id,lparam,dparam,sparam); } //+------------------------------------------------------------------+ //| Erstellen von "Button1" | //+------------------------------------------------------------------+
Geschachtelte Steuerelemente werden verschoben und gezeichnet
Denken wir daran, dass das Panel AppWindow ein Objekt der Klasse CAppDialog ist, die wiederum ein Kind von CDialog ist. CDialog selbst ist abgeleitet von CWndContainer:
CWndContainer ist eine Basisklasse für eine Gruppe von Steuerelementen der Standardbibliothek.
Die übergeordnete Klasse CWndContainer steuert also die Bewegung der gesamten Gruppe von Steuerelementen, die im Panel enthalten sind.
Die Bewegung aller Steuerelemente des Panels wird in einer Schleife in CWndContainer::Shift durchgeführt.
//+------------------------------------------------------------------+ //| Relative Bewegung der Gruppe von Steuerelementen | //+------------------------------------------------------------------+ bool CWndContainer::Shift(const int dx,const int dy) { //--- Methodenaufruf der Elternklasse if(!CWnd::Shift(dx,dy)) return(false); //--- Schleife durch die Elemente der Gruppe int total=m_controls.Total(); for(int i=0;i<total;i++) { CWnd *control=Control(i); //--- Zeigerprüfung if(control==NULL) continue; //--- Bewegung der Gruppenelemente control.Shift(dx,dy); } //--- war erfolgreich return(true); }
Wir verwendeten ein Beispiel aus der Referenz - CBmpButton (befindet sich in \MQL5\Experts\MyExp\Help\With the Panel. EN\ControlsBmpButton.mq5).
Accessing the CWndContainer::Shift method:
Hinzufügen von CAppDialog zur Gruppe der Steuerelemente mittels CDialog
Oben sehen Sie ein Beispiel für ein Panel mit zwei Schaltflächen. Erinnern wir uns, ich erwähnte, dass die Deklaration von Schaltflächen auf globaler Ebene kein gutes Beispiel ist? Hier ist ein korrekteres Beispiel: Der gesamte Code für die Erstellung des Panels und der Schaltflächen wird in der Klasse CAppDialog platziert. Ein Beispiel für die Panelerstellung ist in AppWindowTwoButtonsClass.mq5 dargestellt.
CAppWindowTwoButtons ist ein Kind von CAppDialog und enthält die folgenden Methoden:
Creation | |
---|---|
Create | Erstellen des Hauptsteuerelementes: das Panel |
CreateButton1 | Erstellen eines abhängigen Steuerelementes: Schaltfläche #1 |
CreateButton2 | Erstellen eines abhängigen Steuerelementes: Schaltfläche #2 |
Der Code von AppWindowTwoButtonsClass.mq5: der Teil des Codes der sich jetzt in der Klasse befindet, ist farbig hervorgehoben:
//+------------------------------------------------------------------+ //| AppWindowTwoButtonsClass.mq5 | //| Copyright 2018, MetaQuotes Software Corp. | //| http://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Copyright 2018, MetaQuotes Software Corp." #property link "http://www.mql5.com" #property version "1.000" #property description "Control Panels and Dialogs. Demonstration class CButton" #include <Controls\Dialog.mqh> #include <Controls\Button.mqh> //+------------------------------------------------------------------+ //| Definitionen | //+------------------------------------------------------------------+ //--- Einzüge und Abstände #define INDENT_LEFT (11) // Linker Einzug (im Bereich der Rahmenbreite) #define INDENT_TOP (11) // Oberer Einzug (im Bereich der Rahmenbreite) #define CONTROLS_GAP_X (5) // Abstand zur X Koordinate //--- für Schaltflächen #define BUTTON_WIDTH (100) // Breite, X Koordinate #define BUTTON_HEIGHT (20) // Höhe, Y Koordinate //--- //+------------------------------------------------------------------+ //| Class CAppWindowTwoButtons | //| Verwendung: Hauptdialog der Anwendung des Steuerelements | //+------------------------------------------------------------------+ class CAppWindowTwoButtons : public CAppDialog { private: CButton m_button1; // Das Objekt Schaltfläche CButton m_button2; // Das Objekt Schaltfläche public: CAppWindowTwoButtons(void); ~CAppWindowTwoButtons(void); //--- Erstellen virtual bool Create(const long chart,const string name,const int subwin,const int x1,const int y1,const int x2,const int y2); protected: //--- Erstellen der abhängigen Steuerelemente bool CreateButton1(void); bool CreateButton2(void); }; //+------------------------------------------------------------------+ //| Konstruktor | //+------------------------------------------------------------------+ CAppWindowTwoButtons::CAppWindowTwoButtons(void) { } //+------------------------------------------------------------------+ //| Destructor | //+------------------------------------------------------------------+ CAppWindowTwoButtons::~CAppWindowTwoButtons(void) { } //+------------------------------------------------------------------+ //| Erstellen | //+------------------------------------------------------------------+ bool CAppWindowTwoButtons::Create(const long chart,const string name,const int subwin,const int x1,const int y1,const int x2,const int y2) { if(!CAppDialog::Create(chart,name,subwin,x1,y1,x2,y2)) return(false); //--- Erstellen der abhängigen Steuerelemente if(!CreateButton1()) return(false); if(!CreateButton2()) return(false); //--- war erfolgreich return(true); } //+------------------------------------------------------------------+ //| Globale Variable | //+------------------------------------------------------------------+ CAppWindowTwoButtons ExtDialog; //+------------------------------------------------------------------+ //| Initialisierungsfunktion des Experten | //+------------------------------------------------------------------+ int OnInit() { //--- Erstellen des Dialogs der Anwendung if(!ExtDialog.Create(0,"AppWindowClass with Two Buttons",0,40,40,380,344)) return(INIT_FAILED); //--- Starten der Anwendung ExtDialog.Run(); //--- war erfolgreich return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Deinitialisierungsfunktion des Experten | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { //--- Comment(""); //--- Löschen des Dialogs ExtDialog.Destroy(reason); } //+------------------------------------------------------------------+ //| Ereignisfunktion des Charts des Experten | //+------------------------------------------------------------------+ void OnChartEvent(const int id, // Ereignis ID const long& lparam, // Ereignisparameter vom Typ long const double& dparam, // Ereignisparameter vom Typ double const string& sparam) // Ereignisparameter vom Typ string { ExtDialog.ChartEvent(id,lparam,dparam,sparam); } //+------------------------------------------------------------------+ //| Erstellen von "Button1" | //+------------------------------------------------------------------+ bool CAppWindowTwoButtons::CreateButton1(void) { //--- Koordinaten int x1=INDENT_LEFT; // x1 = 11 Pixel int y1=INDENT_TOP; // y1 = 11 Pixel int x2=x1+BUTTON_WIDTH; // x2 = 11 + 100 = 111 Pixel int y2=y1+BUTTON_HEIGHT; // y2 = 11 + 20 = 32 Pixel //--- Erstellen if(!m_button1.Create(0,"Button1",0,x1,y1,x2,y2)) return(false); if(!m_button1.Text("Button1")) return(false); if(!Add(m_button1)) return(false); //--- war erfolgreich return(true); } //+------------------------------------------------------------------+ //| Erstellen von "Button2" | //+------------------------------------------------------------------+ bool CAppWindowTwoButtons::CreateButton2(void) { //--- Koordinaten int x1=INDENT_LEFT+2*(BUTTON_WIDTH+CONTROLS_GAP_X); // x1 = 11 + 2 * (100 + 5) = 221 pixels int y1=INDENT_TOP; // y1 = 11 Pixel int x2=x1+BUTTON_WIDTH; // x2 = 221 + 100 = 321 Pixel int y2=y1+BUTTON_HEIGHT; // y2 = 11 + 20 = 31 Pixel //--- Erstellen if(!m_button2.Create(0,"Button2",0,x1,y1,x2,y2)) return(false); if(!m_button2.Text("Button2")) return(false); if(!Add(m_button2)) return(false); //--- war erfolgreich return(true); } //+------------------------------------------------------------------+
Betrachten wir den Algorithmus, der auf Basis des Beispiels AppWindowTwoButtonsClass.mq5 ein Panel und die Steuerelemente erstellt. Alles passiert in CAppWindowTwoButtons::Create.
- Erstellen des Panels:
if(!CAppDialog::Create(chart,name,subwin,x1,y1,x2,y2)) return(false);
- Erstellen des abhängigen Steuerelementes:
//--- Erstellen der abhängigen Steuerelemente if(!CreateButton1()) return(false); if(!CreateButton2()) return(false);
- Der wichtigste Moment ist, dass die Schaltfläche, wenn sie erstellt wurde, kein abhängiges Element unseres Panels ist, sondern aus sich selbst existiert. Um es zu einem der abhängigen Elemente des Panels zu machen, sollten wir die Methode Add aufrufen (CDialog:Add fügt der Client-Area ein Steuerelement an dem angegebenen Zeiger/Referenz hinzu.
... if(!Add(m_button1)) return(false); ... if(!Add(m_button2)) return(false); ...
Danach wird das Steuerelement zu einem abhängigen Element des Panels: Alle Ereignisse werden zentral vom Panel auf die abhängigen Steuerelemente verteilt.
So überschreiben wird das Verhalten von standardmäßigen Steuerelementen überschrieben
Wenn Sie das Panel minimieren, wird es auf die Koordinate (10;10) positioniert. Das minimierte Panel wird teilweise mit dem Ein-Klick-Handelspanel überlagert:
Lassen Sie uns eine solche Positionierung korrigieren und eine Überprüfung hinzufügen, ob das Ein-Klick-Handelspanel maximiert ist. Dazu müssen wir die übergeordnete Methode CAppDialog::Minimize überschreiben. Lassen Sie uns ein weiteres Beispiel schaffen: AppWindowCorrectMinimization.mq5 basierend auf dem Code von AppWindowTwoButtons.mq5 aus dem Abschnitt "CAppDialog zur Gruppe der Controls über CDialog hinzufügen".
Änderungen: Deklaration der Methode Minimize:
protected: //--- Erstellen der abhängigen Steuerelemente bool CreateButton1(void); bool CreateButton2(void); //--- Überschreiben der Elternmethode virtual void Minimize(void); };
und im Hauptteil der Methode:
//+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void CAppWindowCorrectMinimization::Minimize(void) { //--- Eine Variable zur Prüfung des Panels des Ein-Klick Handels long one_click_visible=-1; // 0 - das Panel des Ein-Klick Handels ist vorhanden if(!ChartGetInteger(m_chart_id,CHART_SHOW_ONE_CLICK,0,one_click_visible)) { //--- Anzeigen der Fehlermeldung im Expertjournal Print(__FUNCTION__+", Error Code = ",GetLastError()); } //--- Mindesteinzug des minimierten Panels int min_y_indent=28; if(one_click_visible) min_y_indent=100; // Zu verwendender Einzug, wenn das Panel des Ein-Klick Handels auf dem Chart existiert //--- Abfrage des aktuellen Einzugs des minimierten Panels int current_y_top=m_min_rect.top; int current_y_bottom=m_min_rect.bottom; int height=current_y_bottom-current_y_top; //--- сalculating the minimum indent from top for a minimized panel of the application if(m_min_rect.top!=min_y_indent) { m_min_rect.top=min_y_indent; //--- Verschieben des unteren Rahmens des minimierten Icons m_min_rect.bottom=m_min_rect.top+height; } //--- Jetzt erfolgt der Aufruf der Basisklasse CAppDialog::Minimize(); }
Wie integrierte Makros vom Typ Ereignisverarbeitung gelesen werden
Das Panel kann mit folgenden Ereignissen umgehen (aus [Datenordner]\MQL5\Include\Controls\Defines.mqh" in"Events")
//+------------------------------------------------------------------+ //| Ereignis | //+------------------------------------------------------------------+ #define ON_CLICK (0) // Klick auf das Steuerelement #define ON_DBL_CLICK (1) // Doppelklick auf das Steuerelement #define ON_SHOW (2) // Anzeige des Steuerelements #define ON_HIDE (3) // Ausblenden des Steuerelements #define ON_CHANGE (4) // Ändern des Steuerelements #define ON_START_EDIT (5) // Beginn des Bearbeitungsereignisses #define ON_END_EDIT (6) // Ende des Bearbeitungsereignisses #define ON_SCROLL_INC (7) // Inkrement des Ereignisses der Bildlaufleiste #define ON_SCROLL_DEC (8) // Dekrement des Ereignisses der Bildlaufleiste #define ON_MOUSE_FOCUS_SET (9) // Der "Mauskursor betritt das Steuerelement" #define ON_MOUSE_FOCUS_KILL (10) // Der "Mauskursor verlässt das Steuerelement" #define ON_DRAG_START (11) // Das "Ziehen des Steuerelements" beginnt #define ON_DRAG_PROCESS (12) // Das "Steuerelement wird gerade gezogen" #define ON_DRAG_END (13) // Das "Ziehen des Steuerelements" endet #define ON_BRING_TO_TOP (14) // Die "Priorität der Maus" wurde erhöht #define ON_APP_CLOSE (100) // Das Ereignis "Anwendung schließen"
Diese Ereignisse werden in der Methode CAppDialog::OnEvent behandelt. Für eine bessere Darstellung der verschiedenen Arten von Ereignissen werden mehrere Makros in [Datenordner]\MQL5\Include\Controls\Defines.mqh" im Block "Makro der Ereignisbehandlung map" beschrieben:
//+------------------------------------------------------------------+ //| Macro der Ereignisbehandlung | //+------------------------------------------------------------------+ #define INTERNAL_EVENT (-1) //--- Anfang der map #define EVENT_MAP_BEGIN(class_name) bool class_name::OnEvent(const int id,const long& lparam,const double& dparam,const string& sparam) { //--- Ende der map #define EVENT_MAP_END(parent_class_name) return(parent_class_name::OnEvent(id,lparam,dparam,sparam)); } //--- Ereignisbehandlung über die numerische ID #define ON_EVENT(event,control,handler) if(id==(event+CHARTEVENT_CUSTOM) && lparam==control.Id()) { handler(); return(true); } //--- Ereignisbehandlung über die numerische ID durch den Zeiger auf das Steuerelement #define ON_EVENT_PTR(event,control,handler) if(control!=NULL && id==(event+CHARTEVENT_CUSTOM) && lparam==control.Id()) { handler(); return(true); } //--- Ereignisbehandlung ohne Berücksichtigung der ID #define ON_NO_ID_EVENT(event,handler) if(id==(event+CHARTEVENT_CUSTOM)) { return(handler()); } //--- Ereignisbehandlung durch Zeilen ID #define ON_NAMED_EVENT(event,control,handler) if(id==(event+CHARTEVENT_CUSTOM) && sparam==control.Name()) { handler(); return(true); } //--- Behandlung indizierter Ereignisse #define ON_INDEXED_EVENT(event,controls,handler) { int total=ArraySize(controls); for(int i=0;i<total;i++) if(id==(event+CHARTEVENT_CUSTOM) && lparam==controls[i].Id()) return(handler(i)); } //--- Reagieren auf externe Ereignisse #define ON_EXTERNAL_EVENT(event,handler) if(id==(event+CHARTEVENT_CUSTOM)) { handler(lparam,dparam,sparam); return(true); }
Macros aus "Events" und "Macro of event handling map" lassen die Methode OnEvent wie folgt ausschauen:
//+------------------------------------------------------------------+ //| Ereignisbehandlung | //+------------------------------------------------------------------+ EVENT_MAP_BEGIN(CControlsDialog) ON_EVENT(ON_CLICK,m_bmpbutton1,OnClickBmpButton1) ON_EVENT(ON_CLICK,m_bmpbutton2,OnClickBmpButton2) EVENT_MAP_END(CAppDialog)
Dies ist der Code aus der Referenz von CBmpButton und CControlsDialog hier ist es die Instanz der Klasse CAppDialog, die ein Panel in Form einer Klasse ist.
Unter Berücksichtigung der Makros aus "Macro of event handling map" sieht das OnEvent jetzt wie folgt aus:
bool CControlsDialog::OnEvent(const int id,const long& lparam,const double& dparam,const string& sparam) { if(id==(ON_CLICK+CHARTEVENT_CUSTOM) && lparam==m_bmpbutton1.Id()) { OnClickBmpButton1(); return(true); } if(id==(ON_CLICK+CHARTEVENT_CUSTOM) && lparam==m_bmpbutton2.Id()) { OnClickBmpButton2(); return(true); } return(CAppDialog::OnEvent(id,lparam,dparam,sparam)); }
und nach dem Styler so:
bool CControlsDialog::OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam) { if(id==(ON_CLICK+CHARTEVENT_CUSTOM) && lparam==m_bmpbutton1.Id()) { OnClickBmpButton1(); return(true); } if(id==(ON_CLICK+CHARTEVENT_CUSTOM) && lparam==m_bmpbutton2.Id()) { OnClickBmpButton2(); return(true); } return(CAppDialog::OnEvent(id,lparam,dparam,sparam)); }
Der resultierende Code kann wie folgt gelesen werden: Wenn ein benutzerdefiniertes Ereignis eines Klicks auf das Element m_bmpbutton1 empfangen wird, wird die Methode OnClickBmpButton1() aufgerufen. Wenn ein benutzerdefiniertes Ereignis eines Klicks auf m_bmpbutton2 empfangen wird, wird OnClickBmpButton2() aufgerufen.
Beispiel einer Ereignisbehandlung
Wir verwenden AppWindowTwoButtonsClass.mq5 als Basis und erstellen AppWindowTwoButtonsClasssEvents.mq5 durch Hinzufügen der Ereignisbehandlung durch eine Schaltfläche.
Der erste Schritt besteht darin, OnEvent, sowie OnClickButton1 und OnClickButton2. zu deklarieren.
//+------------------------------------------------------------------+ //| Class CControlsDialog | //| Verwendung: Hauptdialog der Anwendung des Steuerelements | //+------------------------------------------------------------------+ class CAppWindowTwoButtons : public CAppDialog { private: CButton m_button1; // Das Objekt Schaltfläche CButton m_button2; // Das Objekt Schaltfläche public: CAppWindowTwoButtons(void); ~CAppWindowTwoButtons(void); //--- Erstellen virtual bool Create(const long chart,const string name,const int subwin,const int x1,const int y1,const int x2,const int y2); //--- Ereignisbehandlung des Charts virtual bool OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam); protected: //--- Erstellen der abhängigen Steuerelemente bool CreateButton1(void); bool CreateButton2(void); //--- Ereignisbehandlung durch die abhängigen Steuerelemente void OnClickButton1(void); void OnClickButton2(void); };
Schritt 2: die Methode OnEvent, die aufgrund der Verwendung von Makros aus "Events" und "Macro of event handling map" der Datei [Datenordner]\MQL5\Include\Controls\Defines.mqh:
protected: //--- Erstellen der abhängigen Steuerelemente bool CreateButton1(void); bool CreateButton2(void); //--- Ereignisbehandlung durch die abhängigen Steuerelemente void OnClickButton1(void); void OnClickButton2(void); }; //+------------------------------------------------------------------+ //| Ereignisbehandlung | //+------------------------------------------------------------------+ EVENT_MAP_BEGIN(CAppWindowTwoButtons) ON_EVENT(ON_CLICK,m_button1,OnClickButton1) ON_EVENT(ON_CLICK,m_button2,OnClickButton2) EVENT_MAP_END(CAppDialog) //+------------------------------------------------------------------+ //| Konstruktor | //+------------------------------------------------------------------+
Jetzt müssen wir den Hauptteil von OnClickButton1 und OnClickButton2 schreiben. Ein Klick auf die Schaltfläche 1 öffnet eine Kaufposition und ein Klick auf Schaltfläche 2 schließt die Position wieder.
Ändern wir also zuerst den Text auf den Schaltflächen (Änderungen sind in CreateButton1 und CreateButton2 implementiert):
... if(!m_button1.Text("Open BUY")) return(false); ... ... if(!m_button2.Text("Close")) return(false); ...
Nun bestimmen wir die Klassen, die wir einbinden müssen: die Klasse CTrade wird für den Handel benötigt, CPositionInfo wird für die Arbeit mit Positionen und der Typ des Handelskontos wird von CAccountInfo bearbeitet:
#property description "Control Panels and Dialogs. Demonstration class CButton" #include <Controls\Dialog.mqh> #include <Controls\Button.mqh> #include <Trade\PositionInfo.mqh> #include <Trade\Trade.mqh> #include <Trade\AccountInfo.mqh> //+------------------------------------------------------------------+ //| Definitionen |
Um mit diesen Klassen arbeiten zu können, müssen wir sie im Bereich 'proteted' der Klasse des Panels deklarieren:
class CAppWindowTwoButtons : public CAppDialog { protected: CPositionInfo m_position; // Positionsobjekt CTrade m_trade; // Handelsobjekt CAccountInfo m_account; // Kontoinformation private: CButton m_button1; // Das Objekt Schaltfläche
Die Methoden für die Klicks
//+------------------------------------------------------------------+ //| Ereignisbehandlung | //+------------------------------------------------------------------+ void CAppWindowTwoButtons::OnClickButton1(void) { if(m_account.TradeMode()==ACCOUNT_TRADE_MODE_DEMO) m_trade.Buy(1.0); } //+------------------------------------------------------------------+ //| Ereignisbehandlung | //+------------------------------------------------------------------+ void CAppWindowTwoButtons::OnClickButton2(void) { if(m_account.TradeMode()==ACCOUNT_TRADE_MODE_DEMO) for(int i=PositionsTotal()-1;i>=0;i--) // Rückgabe der Anzahl der aktuell offenen Positionen if(m_position.SelectByIndex(i)) // wählt die Position nach Index für den weiteren Zugriff auf seine Eigenschaften aus. if(m_position.Symbol()==Symbol()) m_trade.PositionClose(m_position.Ticket()); // Schließen einer Position des angegebenen Symbols }
Das Panel eines Demokontos fungiert nun als Handelspanel: Ein Klick auf die erste Schaltfläche öffnet eine Kaufposition und ein Klick auf die zweite schließt alle Positionen.
Erstellen Sie Ihr eigenes Panel - es ist ganz einfach!
Der Artikel enthält ein allgemeines Schema der Vererbung von Klassen aus dem Abschnitt Panels und Dialogs. Die Erstellung und Verwaltung eines beliebigen grafischen Panels auf Basis der Standardbibliothek wird am Beispiel der Klasse CAppDialog gezeigt. Das Beispiel zeigt auch, wie man auf die Eigenschaften aller grafischen Objekte in einem Panel, das auf CAppDialog basiert, zugreifen kann. Ebenso können Sie mit jedem Kind der Klasse CWnd arbeiten.
Außerdem bietet der Artikel einige nicht standardmäßige Methoden zur Änderung der Eigenschaften von internen Panel-Steuerelementen, die auf CAppDialog basieren. Diese Methoden helfen zu verstehen, wie grafische Objekte funktionieren:
- Wo die wichtigsten Konstanten für das Anlegen von Objekten zu finden sind und wie man sie mit #undef neu definiert
- Zusammenfassung von CAppDialog
Ich hoffe, dass diese Beispiele Ihnen helfen werden, Ihre eigenen Panels auf Basis von CAppDialog zu erstellen. Außerdem empfehle ich, einige Beispiele für die Erstellung von Steuerelemente aus dem Abschnitt Panels und Dialogs zu studieren.
Dateiname | Kommentar |
---|---|
LearnCAppDialog.mq5 | Der Mindestcode eines Panels auf Basis von CAppDialog |
AppWindowEditDefine.mq5 | Das Panel eines Expert Advisors, das die Konstanten aus Defines.mqh neu definiert. |
LearnCAppDialog_1.mq5 | Farbänderung der Objekte von "m_client_area" und "m_background" |
LearnCAppDialog_2.mq5 | Statt CWndContainer::Delete verwenden wir CWndContainer::Destroy, um das Objekt "m_client_area" zu löschen |
AppWindowTwoButtons.mq5 | Ein Panel mit zwei Schaltflächen |
AppWindowTwoButtonsClass.mq5 | Ein Panel mit zwei Schaltflächen als Klasse |
AppWindowCorrectMinimization.mq5 | Ein Beispiel der Standardposition eines Panels |
AppWindowTwoButtonsClasssEvents.mq5 | Ein Panel mit zwei Schaltflächen als Klasse. Ereignisbehandlung der Schaltflächen |
Übersetzt aus dem Russischen von MetaQuotes Ltd.
Originalartikel: https://www.mql5.com/ru/articles/4503





- 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.
Ein wunderbarer Artikel um diesem Thema näher zu kommen oder es zu vertiefen.
Leider ändert das nichts an der Tatsache, daß ein visueller Dialog-Editor im System fehlt.
Weiters wird der MausEvent in Dialogen nicht richtig abgefangen und es verschiebt sich der Chart im Hintergrund bei gedrückter Maustaste.
Auch kommen die Tooltips von dahinterliegenden Indikatoren durch den Dialog durch.
Und das, obwohl es richtiggestellten Code von Doerk gibt.
Die Ersetzung des CWndContainer - Bibliothek für den MetaTrader 5 https://www.mql5.com/de/code/13722
Ich frage mich, wie kann Metaquotes so stur sein um das zu ignorieren und nicht zu korrigieren.
Otto ich verstehe was dich da so bewegt.
Ich habe mich auf Grund von zu vielen Bastelbaustellen in der Grafik Programmierung des MT5 verabschiedet.
Welches Konzept MQ da verfolgt weiß ich nicht denke aber sie sind da mächtig auf dem Holzweg.
Dort wir ein eigens Grafik Framework in ein Fenster reingebastelt.
Sie reden immer von Sicherheit , deswegen kapseln sie so viel wie möglich .
Stellt einfach eine API zu den Daten bereit und lasst die User entscheiden welche Sprache sie nutzen. Das wäre der moderne Weg.
Gruß
Hallo Ihr beiden,
ich kann mich nicht oft genug bei euch beiden bedanken. Ich bin hier schon oft über den einen und anderen absoluten Schwachsinns-Programmcode gestolpert und dank eurer Kritik spare ich mir viel Zeit nach dem Sinn dieser Art von Programmierung zu suchen.
Gibt es denn irgendwo eine vernünftige und ins Deutsche übersetzte Beschreibung der kompletten MQL-Syntax?
Hallo Ihr beiden,
ich kann mich nicht oft genug bei euch beiden bedanken. Ich bin hier schon oft über den einen und anderen absoluten Schwachsinns-Programmcode gestolpert und dank eurer Kritik spare ich mir viel Zeit nach dem Sinn dieser Art von Programmierung zu suchen.
Gibt es denn irgendwo eine vernünftige und ins Deutsche übersetzte Beschreibung der kompletten MQL-Syntax?
Dies ist eine Liste in der man durchaus mit Ctrl+F nach Stichwörtern suchen kann nach dem Motto, wie hieß noch mal...
Dies ist eine Liste in der man durchaus mit Ctrl+F nach Stichwörtern suchen kann nach dem Motto, wie hieß noch mal...
Danke!
Grundsätzlich kenne ich die Doku‘s. Jedoch bin ich erneut inspiriert durch die von Dir angegebenen Links zur Übersichtsanzeige. So betrachtet ist es eine andere Herangehensweise.
Danke!
ich hab das mit den panels auch probiert, das funktioniert für mich nicht wirklich, da es immer im Chart Fenster ist.
Jetzt bin ich übergegangen und hab das ganze in einem Indikatorfenster platziert, das hab ich immer als unterstes im Chart