English Русский 中文 Español 日本語 Português
preview
Wie erstellt man ein grafisches Panel beliebiger Komplexität?

Wie erstellt man ein grafisches Panel beliebiger Komplexität?

MetaTrader 5Beispiele | 3 Mai 2018, 10:18
2 797 6
Vladimir Karputov
Vladimir Karputov

Inhaltsverzeichnis

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:

CAppDialog-Panel

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.

Grafische Bedienelemente im AppWindow-Panel

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:

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.

DateinameKommentar
 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

Beigefügte Dateien |
MQL5.zip (14.12 KB)
Letzte Kommentare | Zur Diskussion im Händlerforum (6)
Christian
Christian | 3 Mai 2018 in 19:28
Otto Pauser:

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ß 

Johann Kern
Johann Kern | 30 Dez. 2020 in 11:04

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?

Carl Schreiber
Carl Schreiber | 30 Dez. 2020 in 12:08
Joosy:

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?

  1. https://www.mql5.com/files/pdf/mql5_german.pdf
  2. https://www.mql5.com/de/docs/function_indices
    Dies ist eine Liste in der man durchaus mit Ctrl+F nach Stichwörtern suchen kann nach dem Motto, wie hieß noch mal...
  3. Im Editor kann man auf eine MQL5-Funktion die Maus stellen und F1 drücken, um so direkt zur Dokumentation dieser Funktion zu kommen, manche mit Beispiel..
Johann Kern
Johann Kern | 30 Dez. 2020 in 18:32
Carl Schreiber:
  1. https://www.mql5.com/files/pdf/mql5_german.pdf
  2. https://www.mql5.com/de/docs/function_indices
    Dies ist eine Liste in der man durchaus mit Ctrl+F nach Stichwörtern suchen kann nach dem Motto, wie hieß noch mal...
  3. Im Editor kann man auf eine MQL5-Funktion die Maus stellen und F1 drücken, um so direkt zur Dokumentation dieser Funktion zu kommen, manche mit Beispiel..
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!
amando
amando | 30 Dez. 2020 in 18:44
Joosy:
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

Geschwindigkeitsvergleich von sich selbst speichernden Indikatoren Geschwindigkeitsvergleich von sich selbst speichernden Indikatoren
Der Artikel vergleicht den klassischen MQL5-Zugriff auf Indikatoren mit alternativen MQL4-Methoden. Mehrere Varianten des Zugriffs auf Indikatoren im MQL4-Stil werden berücksichtigt: mit und ohne Speicherung (caching) der Handles der Indikatoren. Die Handles auf die Indikatoren innerhalb des MQL5-Kerns werden ebenfalls analysiert.
Die Darstellung der Optimierung einer Handelsstrategie im MetaTrader 5 Die Darstellung der Optimierung einer Handelsstrategie im MetaTrader 5
Der Artikel implementiert eine MQL-Anwendung mit einem grafischen Interface zur erweiterten Darstellung der Optimierung. Das grafische Interface verwendet die letzte Version der Bibliothek EasyAndFast. Viele Anwender fragen sich, warum MQL-Anwendungen überhaupt grafische Interfaces benötigen. Dieser Artikel zeigt einen von mehreren Fällen, die für Händler nützlich sein können.
Tiefe neuronale Netzwerke (Teil V). Bayes'sche Optimierung von DNN-Hyperparametern Tiefe neuronale Netzwerke (Teil V). Bayes'sche Optimierung von DNN-Hyperparametern
Der Artikel beschäftigt sich mit der Möglichkeit, die Bayes'sche Optimierung auf Hyperparameter von tiefen neuronalen Netzen anzuwenden, die durch verschiedene Trainingsvarianten gewonnen werden. Die Klassifikationsqualität eines DNN mit den optimalen Hyperparametern in verschiedenen Trainingsvarianten wird verglichen. Die Tiefe der Effektivität der optimalen DNN-Hyperparameter wurde in Vorwärtstests überprüft. Die möglichen Richtungen zur Verbesserung der Klassifizierungsqualität wurden festgelegt.
Money Management von Vince. Implementierung als Modul für MQL5 Wizard Money Management von Vince. Implementierung als Modul für MQL5 Wizard
Der Artikel basiert auf dem Buch 'The Mathematics of Money Management' von Ralph Vince. Es bietet eine Beschreibung der empirischen und parametrischen Methoden zur Ermittlung der optimalen Größe des Handelsvolumens. Der Artikel beinhaltet auch die Implementierung von Handelsmodulen für den MQL5 Wizard, die auf diesen Methoden basieren.