MetaTrader 5 herunterladen

Graphical Interfaces I: Formular mit Steuerelementen (Kapitel 2)

30 Mai 2016, 09:29
Anatoli Kazharski
0
378

Inhalt

 

Einleitung

Dieser Artikel ist die Fortsetzung des ersten Teils dieser Serie über grafische Interfaces. Der erste Artikel Grafische Interfaces I: Vorbereitung der Bibliotheksstruktur (Kapitel 1) betrachtet im Detail den Sinn und Zweck der Bibliothek. Eine vollständige Liste der Links zu den Artikeln des ersten Teils finden Sie am Ende von jedem Kapitel. Zudem finden Sie dort eine Möglichkeit das Projekt, entsprechend dem aktuellen Entwicklungszustand, herunterzuladen. Die Dateien müssen in den gleichen Verzeichnissen untergebracht werden, so, wie Sie auch in dem Archiv abgelegt sind.

In dem vorherigen Kapitel haben wir die Struktur der Bibliothek für die Erzeugung von grafischen Interfaces besprochen. Deren (1) abgeleitete Klassen für einfache Objekte, (2) eine Basisklasse für alle Controls, (3) und eine grundlegende Klasse für das Abspeichern von Pointern zu den Controls und das Verwalten der Controls in einem gemeinsamen Eventhandler.

In diesem Artikel werden wir das erste wichtigste Element eines grafischen Interfaces, ein Formular (Form) mit Steuerelementen (Controls) erzeugen. Es können eine Vielzahl von Controls zu jeder Zeit und in jeder Kombination zu dieser Form hinzugefügt werden. Die Form ist bewegbar und alle sich auf ihr befindenden Controls bewegen sich entsprechend mit.

 

Die Form Klasse für Controls

Wie bereits besprochen, enthält die CElement Klasse bereits einige virtuelle Methoden, welche für jedes Control einzigartig sind. Lassen Sie uns deren Duplikate in die CWindow Klasse einfügen und mit jeder weiteren Beschreibung von anderen Controls machen wir das gleiche. Der Programmcode für diese Methoden wird besprochen, nachdem wir Methoden für das Erzeugen einer Form für die Controls durchgeführt haben.

//+------------------------------------------------------------------+
//|                                                       Window.mqh |
//|                        Copyright 2015, MetaQuotes Software Corp. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
#include "Element.mqh"
//+------------------------------------------------------------------+
//| Klasse für das Erzeugen einer Form für Controls                  |
//+------------------------------------------------------------------+
class CWindow : public CElement
  {
public:
                     CWindow(void);
                    ~CWindow(void);
   //--- Chart Eventhandler
   virtual void      OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam);
   //--- Timer
   virtual void      OnEventTimer(void);
   //--- Verschieben eines Controls
   virtual void      Moving(const int x,const int y);
   //--- Anzeigen, verstecken, zurücksetzen, löschen
   virtual void      Show(void);
   virtual void      Hide(void);
   virtual void      Reset(void);
   virtual void      Delete(void);
   //--- Setzen, Zurücksetzen der Prioritäten für die linke Maustaste
   virtual void      SetZorders(void);
   virtual void      ResetZorders(void);
  };
//+------------------------------------------------------------------+
//| Konstruktor                                                      |
//+------------------------------------------------------------------+
CWindow::CWindow(void)
  {
  }
//+------------------------------------------------------------------+
//| Destruktor                                                       |
//+------------------------------------------------------------------+
CWindow::~CWindow(void)
  {
  }
//+------------------------------------------------------------------+

In der CWindow Klasse Und in einigen anderen Klassen, werden unterschiedliche Modi für unterschiedliche Aktionen verwendet. Listen der benannten Konstanten-Identifizierer für alle Modi und Datentypen werden in einer separaten Enums.mqh Datei gespeichert. Beziehen Sie diese in derObjects.mqh Datei mit ein, damit alle Modi und Enumerationen in allen Bibliotheksdateien verfügbar sind:

//+------------------------------------------------------------------+
//|                                                      Objects.mqh |
//|                        Copyright 2015, MetaQuotes Software Corp. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
#include "Enums.mqh"
#include "Defines.mqh"
#include <ChartObjects\ChartObjectsBmpControls.mqh>
#include <ChartObjects\ChartObjectsTxtControls.mqh>

Aus welchen Teilen wird die Form beziehungsweise das Fenster, welches wir nun erzeugen wollen, bestehen?

  1. Hintergrund Alle Controls werden sich in diesem Bereich befinden.
  2. Kopfteil. Dieser Teil erlaubt das Bewegen des Fensters und er beinhaltet die Interface Controls, die nachfolgend aufgelistet sind.
  3. Icon. Zusätzliche Attribute für die visuelle Identifizierung.
  4. Beschriftung. Der Name des Fensters.
  5. Der "Tooltip" Button. Ein Klick auf diesen Button aktiviert den Modus für das Anzeigen von Tooltips, soweit sie verfügbar sind.
  6. Button für Minimierung/Maximierung des Fensters.
  7. Einen Button für das Schließen des Fensters.

Abbildung  1. Verbindende Teile des Fensters für die Controls.

Abbildung 1. Verbindende Teile des Fensters für die Controls.

In der Bibliothek für graphische Interfaces, welche wir gerade entwickeln, wird es auch einen Multi-Fenster-Modus geben. Eine Anwendung kann nur ein Fenster haben. Aber wenn ein Programm so viele Optionen besitzt, dass es keine Möglichkeit mehr gibt alle Controls in einem Fenster unterzubringen, dann wird ein zweites Fenster benötigt. Es kann ein Fenster für einige gemeinsame Einstellungen der Anwendung, ein Fenster zum Öffnen und Speichern von Dateien oder sogar als eine Palette für die Auswahl einer Farbe verwendet werden. Es gibt also eine Vielzahl von Ideen, welche ein zusätzliches Fenster notwendig machen. Normalerweise werden sie als "Dialogfenster" bezeichnet

Die CWindow Klasse muss eine Eigenschaft für die Definition des Typs des Fensters besitzen, also ob es das Hauptfenster der Anwendung ist oder ob es sich um ein Dialogfenster handelt. Während der weiteren Entwicklung könnten natürlich noch weitere Arten von Fenstern notwendig werden, aber in unserem Fall werden wir nur eine Enumeration für zwei Fenstertypen definieren, das Hauptfenster (1) und das Dialogfenster (2):

//+------------------------------------------------------------------+
//| Enumeration of window types                                      |
//+------------------------------------------------------------------+
enum ENUM_WINDOW_TYPE
  {
   W_MAIN   =0,
   W_DIALOG =1
  };

Der Hintergrund und der Kopf wird mit dem einfachen Objekt "Rectangle Label" erzeugt. Für diesen Zweck haben wir die CRectLabel Klasse. Für die Beschriftung verwenden wir die CLabel Klasse, mit welcher das "Text Label" Objekt erzeugt werden kann. Für das oben aufgeführte Window-Icon und die Buttons, verwenden wir das Objekt "Bitmap Label", und dafür haben wir bereits die CBmpLabel Klasse.

Falls die Element.mqh Datei bereits miteinbezogen wurde, dann sind alle Klassen, die in der Object.mqh Datei definiert sind, für uns zugreifbar. Lassen Sie uns in dem Körper (body) der CWindow Klasse die Instanzen für jedes Control und alle notwendigen Methoden für die Erzeugung eines Fensters erzeugen:

class CWindow : public CElement
  {
private:
   //--- Objekte für die Erzeugung einer Form
   CRectLabel        m_bg;
   CRectLabel        m_caption_bg;
   CBmpLabel         m_icon;
   CLabel            m_label;
   CBmpLabel         m_button_tooltip;
   CBmpLabel         m_button_unroll;
   CBmpLabel         m_button_rollup;
   CBmpLabel         m_button_close;
   //---
public:
   //--- Methoden für die Erzeugung eines Fensters
   bool              CreateWindow(const long chart_id,const int window,const string caption_text,const int x,const int y);
   //---
private:
   bool              CreateBackground(void);
   bool              CreateCaption(void);
   bool              CreateIcon(void);
   bool              CreateLabel(void);
   bool              CreateButtonClose(void);
   bool              CreateButtonRollUp(void);
   bool              CreateButtonUnroll(void);
   bool              CreateButtonTooltip(void);
  };

Bevor wir die Methoden mit Programmcode ausfüllen, müssen wir einige Funktionalitäten in der CWndContainer Klasse vorbereiten, in welcher Arrays alle Pointer zu allen Interface Controls und deren Bestandteile abspeichern. Wir müssen auch den folgenden Schritt in der Entwicklung der Bibliotheksstruktur machen und ein Programm für die Tests vorbereiten.

Beziehen Sie die Datei Window.mqh mit der CWindow Klasse mit in die WndContainer.mqh Datei mit der CWndContainer Klasse mit ein. Wir beziehen auch alle anderen Klassen mit Controls hier ein.

//+------------------------------------------------------------------+
//|                                                 WndContainer.mqh |
//|                        Copyright 2015, MetaQuotes Software Corp. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
#include "Window.mqh"
// ...
// Wir beziehen auch alle anderen Klassen mit Controls hier ein.
// ...

Über diesen Weg sind sie auch für die Verwendung in der CProgram Klasse verfügbar, welche sich innerhalb der Klasse befindet, in der das Userinterface der MQL Anwendung erzeugt wird. Damit wird die Erzeugung und Verwendung von Klassen-Membern dieses Datentyps in den CWndContainer und CWndEvents Klassen möglich sein. Zum Beispiel, wenn nach dem Einbeziehen der Window.mqh Datei, ein dynamisches Array mit Pointern des CWindow -Typs erzeugt wurde (es ist für die Funktion nicht notwenig), wird es keine Probleme und keine Fehlermeldung geben.

//+------------------------------------------------------------------+
//|                                                 WndContainer.mqh |
//|                        Copyright 2015, MetaQuotes Software Corp. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
#include "Window.mqh"
//+------------------------------------------------------------------+
//| Klasse für das Speichern aller Objekte des Interfaces            |
//+------------------------------------------------------------------+
class CWndContainer
  {
protected:
   //--- Window Array
   CWindow          *m_windows[];
  };

Aber wenn die Window.mqh Datei nicht mit einbezogen wurde, dann erhalten wir während der Erzeugung des CWindow-Typs und einem Kompilierungsversuch folgende Fehlermeldung :

Abbildung  2. Die Fehlermeldung des Compilers, dass ein bestimmter Typ fehlt

Abbildung 2. Die Fehlermeldung des Compilers, dass ein bestimmter Typ fehlt

Zusammen mit dem dynamischen Fenster-Array, benötigen wir eine Struktur von dynamischen Arrays mit Pointern zu allen Controls (CElement) und Arrays aller weiteren Objektbestandteile (CChartObject). Als Beispiel dieser Struktur, erzeugen wir ein dynamisches Array, welches in seiner Größe gleich dem des Fenster Arrays (m_window[]) ist. Als Ergebnis erhalten wir ein Array mit Pointer Arrays zu allen Controls jeder Form (Siehe nachfolgenen Code).

Bitte beachten Sie, dass das m_objects[] Array mit dem CChartObject Typ deklariert wird. Bei der Kompilierung gibt es keine Fehlermeldung, da dieser Objekttyp bereits in der Struktur der Bibliothek in der Datei Objects.mqh enthalten ist.

class CWndContainer
  {
protected:
   //--- Struktur von Control Arrays
   struct WindowElements
     {
      //--- Gemeinsames Array von allen Objekten
      CChartObject     *m_objects[];
      //--- Gemeinsames Array von allen Controls
      CElement         *m_elements[];
     };
   //--- Array mit Controls für jedes Fenster
   WindowElements    m_wnd[];
  };

Dieses ist keine vollständige Liste der dynamischen Arrays der WindowElements Struktur. Sobald weitere Controls erzeugt werden, wird diese Liste aktualisiert.

Um die Größe der Arrays zu erhalten, sind noch entsprechende Methoden erforderlich. Die Anzahl der Objekte von allen Controls und die Anzahl der Controls für jedes Fenster kann man mit Hilfe der Übergabe des Fenster-Index als Parameter erhalten (window_index).

class CWndContainer
  {
public:
   //--- Anzahl der Fenster in dem Interface
   int               WindowsTotal(void) { return(::ArraySize(m_windows)); }
   //--- Anzahl der Objekte aller Controls
   int               ObjectsElementsTotal(const int window_index);
   //--- Anzahl der Controls
   int               ElementsTotal(const int window_index);
  };

Wir müssen die Überschreitung der Array-Größe überprüfen. Eine sich wiederholende Phrase kann meist als Makro in der Form (__FUNCTION__) geschrieben werden. Lassen Sie uns eine solche Makroersetzung schreiben (PREVENTING_OUT_OF_RANGE) und sie der Defines.mqh Datei hinzufügen:

//+------------------------------------------------------------------+
//|                                                      Defines.mqh |
//|                        Copyright 2015, MetaQuotes Software Corp. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
//--- Schutz vor Überschreitung der Array-Größe
#define PREVENTING_OUT_OF_RANGE __FUNCTION__," > Schutz vor Überschreitung der Array-Größe."

Jetzt ist es natürlich zweckmäßig dieses in allen Funktionen zu verwenden, wo wir eine Überschreitung der Array-Größe prüfen müssen

//+----------------------------------------------------------------------------------+
//| Gibt die Anzahl der Objekte entsprechen dem angegebenen Fenster-Index zurück     |
//+----------------------------------------------------------------------------------+
int CWndContainer::ObjectsElementsTotal(const int window_index)
  {
   if(window_index>=::ArraySize(m_wnd))
     {
      ::Print(PREVENTING_OUT_OF_RANGE);
      return(WRONG_VALUE);
     }
//---
   return(::ArraySize(m_wnd[window_index].m_objects));
  }
//+----------------------------------------------------------------------------------+
//| Gibt die Anzahl der Controls, entsprechend des angegebenen Window-Index zurück   |
//+----------------------------------------------------------------------------------+
int CWndContainer::ElementsTotal(const int window_index)
  {
   if(window_index>=::ArraySize(m_wnd))
     {
      ::Print(PREVENTING_OUT_OF_RANGE);
      return(WRONG_VALUE);
     }
//---
   return(::ArraySize(m_wnd[window_index].m_elements));
  }

Stellen Sie sich nun vor, dass sie der Anwender dieser Bibliothek sind und gehen Sie zu der CProgram Klasse des EAs, den wir zuvor für Testzwecke erzeugt haben. Nehmen wir an, dass wir ein Trading Panel erzeugen wollen. Dafür erzeugen wir die CreateTradePanel() Methode in der CProgram Klasse. Zur Erinnerung: Da wir ja Platz in diesem Artikel sparen wollen, bauen wir nur auf dem auf, was wir bisher schon erledigt haben und diskutieren daher natürlich auch nur das, was wir zu den bestehenden Klassen hinzufügen.

class CProgram : public CWndEvents
  {
public:
   //--- Erzeugt ein Trading Panel
   bool              CreateTradePanel(void);
  };
//+--------------------------------------------------------------------+
//| Erzeugt ein Prading Panel                                          |
//+--------------------------------------------------------------------+
bool CProgram::CreateTradePanel(void)
  {
//--- Erzeugung einer Form für Controls
// ...
//--- Erzeugung der Controls
// ...
//---
   ::ChartRedraw();
   return(true);
  }

Der Körper dieser Methode ist noch leer, aber er wird sehr bald mit der notwendigen Funktionalität gefüllt. Den Kommentaren in dem Programmcode können wir entnehmen, dass das Fenster das erste Control ist, welches erzeugt wird. Jetzt benötigen wir eine Methode um die Eigenschaften des erzeugten Fensters angeben zu können. Da wir bereits die Window.mqh Datei in der WndContainer.mqh Datei miteinbezogen haben, ist die CWindow Klasse in der CProgram Klasse bereits verfügbar. Daher werden wir nun eine Instanz dieser Klasse und auch eine Methode für das Erstellen eines Fenster für Controls erzeugen: CreateWindow():

class CProgram : public CWndEvents
  {
private:
   //--- Form für die Controls
   CWindow           m_window;
   //---
private:
   //--- Erzegen der Form
   bool              CreateWindow(const string caption_text);
  };

Jetzt ist es an der Zeit die folgenden Fragen zu beantworten:

  1. Wie finden die Pointer zu den Controls und deren Objekte ihren Weg in die Arrays, welchen wir vorher in der CWndContainer Klasse erzeugt haben?
  2. Wie wird jeder Identifizierer von jedem Interface-Control definiert?

Da die Erzeugung eines Interfaces einem bestimmten Ablauf folgen muss, sollte die Entwicklung eines Interfaces so organisiert werden, dass es den Entwicklern nicht passieren kann, dass sie sich in einer Situation befinden wo sie nicht mehr genau wissen, wie die nächste Schritte aussehen müssen. Somit sollte jeder Schritt, den der Anwender/Entwickler unternimmt, durch die Engine der Bibliothek kontrolliert werden. Lassen Sie uns die Struktur so anordnen, dass es unmöglich ist eine Form für Controls zu erzeugen, während die Pointer sich noch nicht in der Pointer Basis der CWndContainer Klasse befinden. Ein Control sollte nicht erzeugt werden können, solange ein Pointer der Form, zu welcher das Control hinzugefügt werden soll, noch nicht in der Klasse gespeichert ist.

Lassen Sie uns die AddWindow() Methode für das Hinzufügen eines Pointers zu der Basis des Interfaces in der CWndContainer Klasse erzeugen. Hier werden wir auch das Abspeichern (1) eines Pointers in dem Control Array vornehmen,(2) das Abspeichern von Pointern der Control Objects in das gemeinsamen Array von Object-Pointern, für welches wir die AddToObjectsArray() Methode benötigen, und auch den Identifizierer (3) festlegen. Zudem ist es noch notwendig (4) den letzten Identifizierer in den Fenster-Eigenschaften abzuspeichern, wie es auch für die Definition der Identifizierer für jedes Control innerhalb deren Klassen notwendig ist. Dieses ist möglich, da jedes Control einen Pointer zu dem Fenster besitzt, zu welchen es hinzugefügt worden ist.

Lassen Sie uns mit der Erzeugung der LastId() Methode in der CWindow Klasse für das Abspeichern und Abfragen des identifizieren des letzten erzeugten Interface-Controls beginnen:

class CWindow : public CElement
  {
private:
   //--- Identifizierer des letzten Controls
   int               m_last_id;
   //---
public:
   //--- Methode für das Abspeichern und Abfragen der ID des zuletzt erzeugten Controls
   void              LastId(const int id)                                    { m_last_id=id;                       }
   int               LastId(void)                                      const { return(m_last_id);                  }
  };

Lassen Sie uns die restlichen Methoden in der CWndContainer Klasse erzeugen, welche in der AddWindow() Methode verwendet wird. Jedes Control bekommt einen eindeutigen Klassennamen. Daher ist die AddToObjectsArray() Methode eine Vorlage, der die unterschiedlichen Control-Objekte übergeben werden. In dieser Methode werden wir das Array der Control-Objekte durchlaufen und ein Pointer zu jedem Objekt in dem Basis-Array hinzufügen. Dafür benötigen wir eine weitere Methode. Lassen Sie sie uns AddToArray() nennen. Da ihr nur Objekte eines Typs (CChartObject) übergeben werden, brauchen wir sie nicht als Vorlage zu erzeugen.

class CWndContainer
  {
protected:
   //--- Hinzufügen von Pointern der Control-Objekte zu dem gemeinsamen Array
   template<typename T>
   void              AddToObjectsArray(const int window_index,T &object);
   //--- Fügt ein Objekt Pointer dem array hinzu
   void              AddToArray(const int window_index,CChartObject &object);
  };
//+--------------------------------------------------------------------------+
//| Typ Pointer von Control Objekten dem gemeinsamen Array hinzu             |
//+--------------------------------------------------------------------------+
template<typename T>
void CWndContainer::AddToObjectsArray(const int window_index,T &object)
  {
   int total=object.ObjectsElementTotal();
   for(int i=0; i<total; i++)
      AddToArray(window_index,object.Object(i));
  }
//+--------------------------------------------------------------------------+
//| Fügt ein Objekt-Pointer dem Array hinzu                                  |
//+--------------------------------------------------------------------------+
void CWndContainer::AddToArray(const int window_index,CChartObject &object)
  {
   int size=::ArraySize(m_wnd[window_index].m_objects);
   ::ArrayResize(m_wnd[window_index].m_objects,size+1);
   m_wnd[window_index].m_objects[size]=::GetPointer(object);
  }

Nun kann die AddWindow() Methode erzeugt werden. Hier brauchen wir auch einen Control-Zähler (m_counter_element_id). Dieser Zähler wird immer um einen erhöht, wenn ein weiteres Control hinzugefügt wird.

class CWndContainer
  {
private:
   //--- Control Zähler
   int               m_counter_element_id;
   //---
protected:
   //--- Fügt einen Fenster-Pointer zu der Basis der Interface-Controls hinzu
   void              AddWindow(CWindow &object);
  };
//+---------------------------------------------------------------------------------+
//| Fügt einen Fenster-Pointer zu der Basis von Interface Controls hinzu            |
//+---------------------------------------------------------------------------------+
void CWndContainer::AddWindow(CWindow &object)
  {
   int windows_total=::ArraySize(m_windows);
//--- Wenn es keine Fenster gibt, dann wird der Zähler auf Null gesetzt
   if(windows_total<1)
      m_counter_element_id=0;
//--- Einen Pointer zu dem Fenster-Array hinzufügen
   ::ArrayResize(m_wnd,windows_total+1);
   ::ArrayResize(m_windows,windows_total+1);
   m_windows[windows_total]=::GetPointer(object);
//--- Einen Pointer zu dem gemeinsamen Array der Controls hinzufügen
   int elements_total=::ArraySize(m_wnd[windows_total].m_elements);
   ::ArrayResize(m_wnd[windows_total].m_elements,elements_total+1);
   m_wnd[windows_total].m_elements[elements_total]=::GetPointer(object);
//--- Hinzufügen von einem Control-Object zu dem gemeinsamen Array von Objekten
   AddToObjectsArray(windows_total,object);
//--- Festlegen des Identifizierers und Abspeichern der ID des letzten Controls
   m_windows[windows_total].Id(m_counter_element_id);
   m_windows[windows_total].LastId(m_counter_element_id);
//--- Erhöhen des Zählers für die Control-Identifizierer
   m_counter_element_id++;
  }

Ab jetzt müssen sie jedes Mal, wenn Sie ein neues Fenster der benutzerdefinierten Klasse in einer MQL Anwendung erzeugen (in diesem Artikel ist es CProgram), dieses über die Methode AddWindow() hinzufügen.

Anschließend müssen die Methoden für die Erzeugung des Fensters in der CWindow Klasse implementiert werden. Hierfür brauchen wir zusätzliche Variablen und Methoden in Abhängigkeit vom Typ, dem Aussehen, der Modi des Fensters und nach Abhängigkeit der Art der MQL-Anwendung. Sie werden hier mit einer kurzen Beschreibung aufgelistet:

  1. Methoden für das Setzen und das Abfragen des Fenster-Status(minimiert/maximiert).
  2. Methoden für das Setzen und Abfragen bis Fenster -Typs(main/dialog).
  3. Methoden für das Setzen des Modus für die Minimierung des Fensters, unter Berücksichtigung des Typs der MQL Anwendung (EA, indicator). Dafür benötigen wir Methoden, die es uns erlauben die Größe des Fensters zu verwalten, falls es sich um einen Indikator handelt, der sich nicht im Hauptchart-Fenster befindet.
  4. Methoden für das Festlegen der Farben von jedem Fenster-Objekt
  5. Methoden für das Setzen des Fenster-Labels
  6. Methode für die Festlegung des Standard-Fenster-Labels, falls der User dieses nicht angibt.
  7. Die Identifikation des Bereiches des Fensters-Kopfes.
  8. Konstanten für die Ränder für Buttons vom rechten Rand des Fensters.
  9. Anzeige des Buttons für den Modus für die Darstellung von Tooltips.
  10. Variablen für die Prioritäten der linken Maustaste für jedes Fenster-Objekt

Hinzufügen dieser Eigenschaften zu der CWindow Klasse. Die Konstanten für die Abstände der Buttons von der rechten Ecke des Fensters, müssen ganz am Anfang der Datei hinzugefügt werden:

//+------------------------------------------------------------------+
//|                                                       Window.mqh |
//|                        Copyright 2015, MetaQuotes Software Corp. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
#include "Element.mqh"
//--- Abstände der Buttons von der rechten Ecke des Fensters
#define CLOSE_BUTTON_OFFSET   (20)
#define ROLL_BUTTON_OFFSET    (36)
#define TOOLTIP_BUTTON_OFFSET (53)

Erstellen der genannten Variablen und Methoden in dem Klassen-Körper

//+------------------------------------------------------------------+
//| Klasse für das Erzeugen einer Form für Controls                  |
//+------------------------------------------------------------------+
class CWindow : public CElement
  {
private:
   //--- Identifizierer des letzten Controls
   int               m_last_id;
   //--- Status eines minimierten Fensters
   bool              m_is_minimized;
   //--- Window type
   ENUM_WINDOW_TYPE  m_window_type;
   //--- Modus für die Höhe eines Unter-Fensters (Für Indikatoren)
   bool              m_height_subwindow_mode;
   //--- Modus für die Minimierung des Fensters in dem Indikator Unterfenster.
   bool              m_rollup_subwindow_mode;
   //--- Höhe des Indikator Unterfensters
   int               m_subwindow_height;
   //--- Eigenschaften des Hintergrundes
   int               m_bg_zorder;
   color             m_bg_color;
   int               m_bg_full_height;
   //--- Eigenschaften des Kopfes
   int               m_caption_zorder;
   string            m_caption_text;
   int               m_caption_height;
   color             m_caption_bg_color;
   color             m_caption_bg_color_hover;
   color             m_caption_bg_color_off;
   color             m_caption_color_bg_array[];
   //--- Eigenschaften der Buttons
   int               m_button_zorder;
   //--- Farbe des Fensterrahmens (Hintergrund, Kopf)
   color             m_border_color;
   //--- Form Icon
   string            m_icon_file;
   //--- Anwesenheit des Buttons für den Modus für die Darstellung von Tooltips
   bool              m_tooltips_button;
   //--- Für die Identifizierung der Bereichsgrenzen des Fenster-Kopfes
   int               m_right_limit;
   //---
public:
   //--- Window type
   ENUM_WINDOW_TYPE  WindowType(void)                                  const { return(m_window_type);              }
   void              WindowType(const ENUM_WINDOW_TYPE flag)                 { m_window_type=flag;                 }
   //--- Default Icon
   string            DefaultIcon(void);
   //--- (1) Benutzerdefiniertes icon des Fensters, (2) Button für Referenz, (3) Begrenzung des Erfassungsbereichs des Headers
   void              IconFile(const string file_path)                        { m_icon_file=file_path;              }
   void              UseTooltipsButton(void)                                 { m_tooltips_button=true;             }
   void              RightLimit(const int value)                             { m_right_limit=value;                }
   //--- Status eines minimierten Fensters
   bool              IsMinimized(void)                                 const { return(m_is_minimized);             }
   void              IsMinimized(const bool flag)                            { m_is_minimized=flag;                }
   //--- Eigenschaften des Kopfes
   void              CaptionText(const string text);
   string            CaptionText(void)                                 const { return(m_caption_text);             }
   void              CaptionHeight(const int height)                         { m_caption_height=height;            }
   int               CaptionHeight(void)                               const { return(m_caption_height);           }
   void              CaptionBgColor(const color clr)                         { m_caption_bg_color=clr;             }
   color             CaptionBgColor(void)                              const { return(m_caption_bg_color);         }
   void              CaptionBgColorHover(const color clr)                    { m_caption_bg_color_hover=clr;       }
   color             CaptionBgColorHover(void)                         const { return(m_caption_bg_color_hover);   }
   void              CaptionBgColorOff(const color clr)                      { m_caption_bg_color_off=clr;         }
   //--- Eigenschaften des Fensters
   void              WindowBgColor(const color clr)                          { m_bg_color=clr;                     }
   color             WindowBgColor(void)                                     { return(m_bg_color);                 }
   void              WindowBorderColor(const color clr)                      { m_border_color=clr;                 }
   color             WindowBorderColor(void)                                 { return(m_border_color);             }
   //--- Modi des Indikator-Unterfensters
   void              RollUpSubwindowMode(const bool flag,const bool height_mode);
   //--- Verändern der Höhe des Indikator-Unterfensters
   void              ChangeSubwindowHeight(const int height);
  };

Initialisierung der Variablen in der in dem Klassenkonstruktor mit Standardwerten. In dem Konstruktor speichern wir auch den Klassennamen des Interface Controls ab und legen eine strikte Reihenfolge der Prioritäten für die linke Maustaste fest.

//+------------------------------------------------------------------+
//| Konstruktor                                                      |
//+------------------------------------------------------------------+
CWindow::CWindow(void) : m_last_id(0),
                         m_subwindow_height(0),
                         m_rollup_subwindow_mode(false),
                         m_height_subwindow_mode(false),
                         m_is_minimized(false),
                         m_tooltips_button(false),
                         m_window_type(W_MAIN),
                         m_icon_file(""),
                         m_right_limit(20),
                         m_caption_height(20),
                         m_caption_bg_color(C'88,157,255'),
                         m_caption_bg_color_off(clrSilver),
                         m_caption_bg_color_hover(C'118,177,255'),
                         m_bg_color(C'15,15,15'),
                         m_border_color(clrLightGray)

  {
//--- Abspeichern des Namens der Control-Klasse in der Basisklasse
   CElement::ClassName(CLASS_NAME);
//--- Festlegung einer Reihenfolge der Prioritäten
   m_bg_zorder      =0;
   m_caption_zorder =1;
   m_button_zorder  =2;
  }

Die Implementation der in Gelb hervorgehobenen Methoden in dem oben dargestellten Code, werden weiter unten präsentiert. Wenn Variablen und Methoden der Basisklasse benötigt werden, dann sollten Sie mit dem prefix Klassenname und einem doppelten Doppelpunkt angegeben werden (ClassName::). Auf diese Weise wird der Programmcode übersichtlicher und Sie können ihn schneller wieder in Erinnerung rufen, falls Sie in eine lange Zeit nicht betrachtet haben. Der Name der m_subwindow_height Variable wird automatisch definiert, wenn das Fenster erzeugt wird.

//+------------------------------------------------------------------+
//| Modus für die Minimierung des Indikator-Unterfensters            |
//+------------------------------------------------------------------+
void CWindow::RollUpSubwindowMode(const bool rollup_mode=false,const bool height_mode=false)
  {
   if(CElement::m_program_type!=PROGRAM_INDICATOR)
      return;
//---
   m_rollup_subwindow_mode =rollup_mode;
   m_height_subwindow_mode =height_mode;
//---
   if(m_height_subwindow_mode)
      ChangeSubwindowHeight(m_subwindow_height);
  }

Wir kommen auf die CWindow::RollUpSubwindowMode() Methode in einem der nachfolgenden Kapitel zurück, sobald wir einige praktische Beispiele in einem Chart in dem Terminal betrachten. Auf diese Weise ist es klarer, was jeder der vorgeschlagenen Modi darstellt.

Die CWindow::ChangeSubwindowHeight() Methode erlaubt die Veränderung der Höhe des Indikator Unterfensters, falls sich das Programm in diesem Modus befindet:

//+-------------------------------------------------------------------+
//| Veränderung der Höhe des Indikator Unterfensters                  |
//+-------------------------------------------------------------------+
void CWindow::ChangeSubwindowHeight(const int height)
  {
   if(CElement::m_subwin<1 || CElement::m_program_type!=PROGRAM_INDICATOR)
      return;
//---
   if(height>0)
      ::IndicatorSetInteger(INDICATOR_HEIGHT,height);
  }

 

Methoden für das Erzeugen einer Form

Damit alle zusammengesetzten Teile des Fensters nach der Erstellung sichtbar sind, müssen sie in einer strengen Reihenfolge (Schicht für Schicht) erzeugt werden.

  1. Hintergrund
  2. Kopfteil.
  3. Header controls.

Am Anfang der CWindow::CreateWindow() Methode, wo alle diese einzelnen Teile des Fensters erzeugt werden, fügen wir noch eine Überprüfung durch, ob der Pointer zu diesem Fenster auch in der Control-Basis gespeichert ist. Falls dieses bisher nicht gestehen ist, dann erhält der Identifizierer des Fensters den Wert WRONG_VALUE. In diesem Fall wird das Fenster nicht erzeugt und die Funktion gibt false zurück. Zudem wird eine entsprechende Fehlermeldung in dem Journal angezeigt. Falls alles in Ordnung ist, dann werden die Variablen der aktuellen Klasse und der Basisklasse mit den übergebenen Werten initialisiert. Zu diesem Variablen gehören:

  • Chart Identifizierer.
  • Die Nummer des Chartfensters
  • Der Name des Fensters(Der Text in der Kopfzeile).
  • Die Koordinaten für das Fenster (x, y).
  • Ein Duplikat der Variable der Fensterhöhe (m_bg_full_height). Sie wird für die Veränderung der Größe des Fensters verwendet. Die Größe des Fensters wird festgelegt, bevor die Methode für die Erzeugung der Form angewandt wird (Beispiele hierzu folgen später).

Der Chart-Identifizierer und die Chartfenster-Nummer und werden in der Konstruktor-Klasse CWndEvents definiert. Wenn Controls in der benutzerdefinierten Klasse (CWndEvents::CProgram) erzeugt werden, werden diese Werte den Methoden für die Erzeugung übergeben.

Anschließend folgen die Methoden für die Erzeugung alle Objekte des Fensters. Falls irgend eines dieser Objekte nicht erzeugt werden kann, dann gibt die Funktion false zurück. Nachdem alle Objekte in Abhängigkeit von dem Typ des Programms und dem Modus erzeugt wurden, muss eine Überprüfung der Fenstergröße vorgenommen werden (Falls es sich um einen Indikator in einem separaten Fenster handelt) Ich habe zuvor schon die m_subwindow_height Variable erwähnt, welche initialisiert wird, sobald das Fenster erzeugt wurde. Dies ist genau dort, wo diese Initialisierung geschieht. In dem nachfolgenden Programmcode, wird diese Stelle in Gelb hervorgehoben.

Und schließlich, wenn festgestellt wird, dass dieses Fenster ein Dialogfenster ist, muss dieses ausgeblendet werden. Hier ist eine Erklärung erforderlich. Falls das Programm-Interface mehr als nur ein Fenster enthält, dann werden diese Fenster zu dem Chart, während des Ladens des Programms, hinzugefügt. Wenn also das eine oder andere Dialogfenster aufgerufen wird, muss der an Anwender diese nicht immer neu erstellen und löschen. Sie befinden sich immer auf dem Chart, aber in einem versteckten Modus. Wenn eines dieser Fenster aufgerufen wird, wird es sichtbar gemacht und wenn es geschlossen wird, wird es nur wieder versteckt aber nicht gelöscht.

Alles was ich gerade besprochen habe, wird mit dem folgenden Code wiedergegeben:

//+----------------------------------------------------------------+
//| Erzeugung einer Form für Controls                              |
//+----------------------------------------------------------------+
bool CWindow::CreateWindow(const long chart_id,const int subwin,const string caption_text,const int x,const int y)
  {
   if(CElement::Id()==WRONG_VALUE)
     {
      ::Print(__FUNCTION__," > Before creating a window, its pointer is to be passed "
            "to the window array using the CWndContainer::AddWindow(CWindow &object) method.");
      return(false);
     }
//--- Initialisierung der Variablen
   m_chart_id       =chart_id;
   m_subwin         =subwin;
   m_caption_text   =caption_text;
   m_x              =x;
   m_y              =y;
   m_bg_full_height =m_y_size;
//--- Erzeugung aller Objekte des Fensters
   if(!CreateBackground())
      return(false);
   if(!CreateCaption())
      return(false);
   if(!CreateLabel())
      return(false);
   if(!CreateIcon())
      return(false);
   if(!CreateButtonClose())
      return(false);
   if(!CreateButtonRollUp())
      return(false);
   if(!CreateButtonUnroll())
      return(false);
   if(!CreateButtonTooltip())
      return(false);
//--- Falls das Programm ein Indikator ist
   if(CElement::ProgramType()==PROGRAM_INDICATOR)
     {
      //--- Wenn der Modus einer festgelegten Höhe eines Unterfensters gegeben ist
      if(m_height_subwindow_mode)
        {
         m_subwindow_height=m_bg_full_height+3;
         ChangeSubwindowHeight(m_subwindow_height);
        }
     }
//--- Verstecke das Fenster, falls es sich um ein Dialogfenster handelt
   if(m_window_type==W_DIALOG)
      Hide();
//---
   return(true);
  }

Die virtuelle Funktion Hide() Wird für das Verstecken von Objekten verwendet. In dem oben gezeigten Programmcode wird dieses in grün hervorgehoben Vorher, in der CWindow Klasse, Haben wir nur die Deklaration betrachtet. In den Programmcode, sind alle Objekte des Fensters in allen Timeframes versteckt und die m_is_visible Variable der Basisklasse erhält den false Status.

//+----------------------------------------------------------------+
//| Versteckt das Fenster                                          |
//+----------------------------------------------------------------+
void CWindow::Hide(void)
  {
//--- Versteckt alle Objekte
   for(int i=0; i<ObjectsElementTotal(); i++)
      Object(i).Timeframes(OBJ_NO_PERIODS);
//--- Sichtbarer Status
   CElement::IsVisible(false);
  }

Wenn Sie alle oben genannten Veränderungen vorgenommen haben, erhalten Sie dennoch eine Fehlermeldung beim Kompilieren der CWindow Klasse (Siehe nachfolgende Screenshot). Dieses geschieht, da alle Methoden für die Erzeugung von Teilen des Fensters noch keinen Inhalt besitzen. Wir werden uns nun einige Zeit für die Implementierung nehmen.

Abbildung 3. Nachricht über die Abwesenheit der Implementation von Methoden

Abbildung 3. Nachricht über die Abwesenheit der Implementation von Methoden

Lassen Sie uns mit dem Hintergrund und der CWindow::CreateBackground() Methode anfangen. Zu Beginn jeder Methode, die mit der Erzeugung von Objekten in Zusammenhang steht, sollten wir einen Namen für dieses Objekt bilden. Dieser sollte aus den folgenden Teilen bestehen:

  • Programmname.
  • Die Zugehörigkeit zu dem Control (“window”).
  • Die Zugehörigkeit zu einem Teil des Controls (“bg”).
  • Control Identifizierer.

Die Teile des Namens werden mit dem «_» separiert. Der Name des Programms wird in der Basisklasse CElement in der Liste der Konstruktor-Initialisierungen gespeichert. Den Identifizierer für das Control erhält man in dem Moment, wo das Fenster zu der Control Basis hinzugefügt wird.

Anschließend führen wir eine Überprüfung der Größe des Hintergrundes des Fensters durch und seine Einstellung in Bezug darauf, ob das Fenster minimiert oder maximiert ist. Schauen wir weiter nach vorne. Wenn die Timeframe oder das Symbol eines Charts geändert wird, werden alle Objekte den Funktionen für die Deinitialisierung übergeben. In der Funktion des Programms für die Initialisierung, werden diese entsprechend der neuen Werte wiederhergestellt.

Nach der Einstellung der Hintergrundgröße, werden die Objekteinstellungen durchgeführt, gefolgt von den Festlegungen der Eigenschaften. Am Ende der Methode wird der Object-Pointer in dem Array der Basisklasse abgelegt. Der Programmcode der CWindow::CreateBackground() Funktion wird nachfolgend dargestellt:

//+-----------------------------------------------------------------+
//| Erzeugt den Hintergrund des Fensters                            |
//+-----------------------------------------------------------------+
bool CWindow::CreateBackground(void)
  {
//--- Bilden des Namens des Fensters
   string name=CElement::ProgramName()+"_window_bg_"+(string)CElement::Id();
//--- Die Größe des Fensters in Abhängigkeit des Status(minimiert/maximiert)
   int y_size=0;
   if(m_is_minimized)
     {
      y_size=m_caption_height;
      CElement::YSize(m_caption_height);
     }
   else
     {
      y_size=m_bg_full_height;
      CElement::YSize(m_bg_full_height);
     }
//--- Festlegung des Hintergrundes
   if(!m_bg.Create(m_chart_id,name,m_subwin,m_x,m_y,m_x_size,y_size))
      return(false);
//--- Festlegen der Eigenschaften
   m_bg.BackColor(m_bg_color);
   m_bg.Color(m_border_color);
   m_bg.BorderType(BORDER_FLAT);
   m_bg.Corner(m_corner);
   m_bg.Selectable(false);
   m_bg.Z_Order(m_bg_zorder);
   m_bg.Tooltip("\n");
//--- Abspeichern des Objekt-Pointers
   CElement::AddToArray(m_bg);
   return(true);
  }

In der Methode für die Erzeugung des Fenster-Kopfes, müssen zu den grundlegenden Aktionen, wie es auch bei dem Hintergrund geschieht, noch die Koordinaten und die Größe des einfachen Objektes der CRectLabel Klasse, welche sich in der Objects.mqh Datei befindet, abgespeichert werden. Diese Werte werden automatisch aktualisiert, wenn das Fenster verschoben wird(und sich daher die Koordinaten ändern) und wenn sich die Größe verändert. Wenn dieses statische Werte sind, ist es nicht möglich, den Cursor über dem Fenster-Kopf zu verfolgen. Diese Regel gilt ebenso für alle anderen Objekte in allen anderen Controls.

Ein weiterer Unterschied liegt in der Fähigkeit des Fenster-Kopfes, auf den Mauszeiger zu reagieren wenn dieser in den Fensterbereich eintritt oder wenn er diesen verlässt. Für dieses Verhalten wurde die InitColorArray() Methode in der CElement Klasse entwickelt.

Code of the CElement::CreateCaption() method:

//+-----------------------------------------------------------------+
//| Erzeugt den Fenster-Kopf                                        |
//+-----------------------------------------------------------------+
bool CWindow::CreateCaption(void)
  {
   string name=CElement::ProgramName()+"_window_caption_"+(string)CElement::Id();
//--- Festlegen des Fenster-Kopfes
   if(!m_caption_bg.Create(m_chart_id,name,m_subwin,m_x,m_y,m_x_size,m_caption_height))
      return(false);
//--- Festlegen der Eigenschaften
   m_caption_bg.BackColor(m_caption_bg_color);
   m_caption_bg.Color(m_border_color);
   m_caption_bg.BorderType(BORDER_FLAT);
   m_caption_bg.Corner(m_corner);
   m_caption_bg.Selectable(false);
   m_caption_bg.Z_Order(m_caption_zorder);
   m_caption_bg.Tooltip("\n");
//--- Abspeichern der Koordinaten
   m_caption_bg.X(m_x);
   m_caption_bg.Y(m_y);
//--- Abspeichern der Größe (in Objekt)
   m_caption_bg.XSize(m_caption_bg.X_Size());
   m_caption_bg.YSize(m_caption_bg.Y_Size());
//--- Initialisierung des Arrays des Gradienten
   CElement::InitColorArray(m_caption_bg_color,m_caption_bg_color_hover,m_caption_color_bg_array);
//--- Abspeichern des Objekt-Pointers
   CElement::AddToArray(m_caption_bg);
   return(true);
  }

Das Label des Programms in dem Hauptfenster des Interfaces, wird standardmäßig durch vorher definierte Icons repräsentiert. Welches dieser Label dargestellt wird, hängt von dem Typ des Programms ab (ENUM_PROGRAM_TYPE). Alle Label-Icons können am Ende dieses Artikels heruntergeladen werden. Die Verwendung von Labels, die in einem Programm nicht anklickbar sind, sollten in dem folgenden Verzeichnis <data folder>\MQLX\Images\EasyAndFastGUI\Icons\bmp16 abgelegt werden. Der Name bmp16 bedeutet, dass das Verzeichnis Bilder mit der Größe von 16x16 pixeln beinhaltet. Wenn ihre Sammlung von Icons die Abmessungen von 24x24 haben, Dann sollte ihr Verzeichnis bmp24 heißen etc.

Standard Icons, die auch standardmäßig für die Erzeugung von Controls verwendet werden, müssen in das Verzeichnis <data folder>\MQLX\Images\EasyAndFastGUI\Controls kopiert werden.

Für die automatische Identifizierung des Lebens des Hauptprogramms, wurde die DefaultIcon() Methode in der CWindow Klasse deklariert. Hier ist der Programmcode dieser Methode:

//+----------------------------------------------------------------+
//| Identifizierung des Standard-Labels                            |
//+----------------------------------------------------------------+
string CWindow::DefaultIcon(void)
  {
   string path="Images\\EasyAndFastGUI\\Icons\\bmp16\\advisor.bmp";
//---
   switch(CElement::ProgramType())
     {
      case PROGRAM_SCRIPT:
        {
         path="Images\\EasyAndFastGUI\\Icons\\bmp16\\script.bmp";
         break;
        }
      case PROGRAM_EXPERT:
        {
         path="Images\\EasyAndFastGUI\\Icons\\bmp16\\advisor.bmp";
         break;
        }
      case PROGRAM_INDICATOR:
        {
         path="Images\\EasyAndFastGUI\\Icons\\bmp16\\indicator.bmp";
         break;
        }
     }
//---
   return(path);
  }

Es ist zu beachten, dass in Skripten nur Interface Controls verwendet werden können, die Daten visualisieren. Die Erklärung hierfür ist, dass Skripte keine Möglichkeiten für die Verfolgung von Events bieten.

Falls ein Icon für ein Fenster neu definiert werden muss, kann die CWindow::IconFile() Methode verwendet werden. Beispiele hierfür - und andere Methoden - werden besprochen, wenn wir ein Interface, welches auf dieser Bibliothek basiert, in einem Chart des Terminals testen.

Ressourcen für Icons (#resource) von unterschiedlichen Controls können direkt neben der Methode (ausserhalb des Körpers der Methode) mit einbezogen werden (Hervorgehoben in Gelb). Das Label hat immer nur einen Status (ein Icon), und daher wird in den Methoden der einfachen Objekte nur ein Pfad zu einer Version angegeben CChartObjectBmpLabel::BmpFileOn() und CChartObjectBmpLabel::BmpFileOff().

Alle Objektkoordinaten (Mit Ausnahme des Fenster-Kopfes) von allen Controls jeden Fensters, werden in Relation zu den Koordinaten des Fensters in lokalen Variablen dieser Methode berechnet. Folglich werden alle Objektabstände relativ zu den Fensterkoordinaten in den entsprechenden Feldern der Klasse für einfache Objekte in der Objects.mqh Datei abgespeichert. Sie werden dazu verwendet, um die Koordinaten nicht neu berechnen zu müssen, wenn ein Fenster verschoben wird. Das heißt, es wird nur eine Operation anstelle von Zweien für die Aktualisierung der Koordinaten von jedem Objekt und aller Steuerelemente (Controls) benötigt, wie zum Beispiel in dem Moment der Objekterstellung. In dem nachfolgendem Programmcode ist das Fehlen der Abstände von der Ecke des Fensters in Grün hervorgehoben.

//+----------------------------------------------------------------+
//| Erzeugt das Programm-Label                                     |
//+----------------------------------------------------------------+
//--- Icons (Standardmäßig) Symbolisieren den Typ des Programms
#resource "\\Images\\EasyAndFastGUI\\Icons\\bmp16\\advisor.bmp"
#resource "\\Images\\EasyAndFastGUI\\Icons\\bmp16\\indicator.bmp"
#resource "\\Images\\EasyAndFastGUI\\Icons\\bmp16\\script.bmp"
//---
bool CWindow::CreateIcon(void)
  {
   string name=CElement::ProgramName()+"_window_icon_"+(string)CElement::Id();
//--- Objektkoordinaten
   int x=m_x+5;
   int y=m_y+2;
//--- Festlegen des Fenster-Icons
   if(!m_icon.Create(m_chart_id,name,m_subwin,x,y))
      return(false);
//--- Standard-Icon, Falls es vom User nicht anders angegeben wurde
   if(m_icon_file=="")
      m_icon_file=DefaultIcon();
//--- Festlegen der Eigenschaften
   m_icon.BmpFileOn("::"+m_icon_file);
   m_icon.BmpFileOff("::"+m_icon_file);
   m_icon.Corner(m_corner);
   m_icon.Selectable(false);
   m_icon.Z_Order(m_button_zorder);
   m_icon.Tooltip("\n");
//--- Abspeichern der Koordinaten
   m_icon.X(x);
   m_icon.Y(y);   
//--- Abstände vom Eckpunkt
   m_icon.XGap(x-m_x);
   m_icon.YGap(y-m_y);
//--- Abspeichern der Größe (in Objekt)
   m_icon.XSize(m_icon.X_Size());
   m_icon.YSize(m_icon.Y_Size());
//--- Hinzufügen von Objekten zudem Gruppen-Array
   CElement::AddToArray(m_icon);
   return(true);
  }

Da es in den Programmcode zu der Methode für das Erzeugen des Text-Labels des Kopfes CWindow::CreateLabel() keine wesentlichen Unterschiede zu der Methode für das Erzeugen des Programm-Labels gibt, werden wir diese hier nicht weiter ausführlich besprechen um den Artikel überschaubar zu halten. Sie finden den Programmcode am Ende dieses Artikels in der Window.mqh Datei.

Jetzt besprechen wir die Buttons, die sich in dem Fenster-Kopf befinden. Alle diese Buttons besitzen Regeln, wodurch sie sich von den vorherigen Methoden unterscheiden:

  • Sie ändern ihr Aussehen wenn sich der Mauszeiger über ihnen befindet. Aus diesem Grund benötigen wir zwei Icons für jeden Button. Einer wird für den ON Status gebraucht, wenn der Mauszeiger sich über dem Button befindet. Das zweite Icon wird für den OFF-Status benötigt, wenn sich der Mauszeiger nicht über dem Button befindet.
  • In Skripten werden Buttons nicht gesetzt.
  • Für Dialogfenster wird es nur den Button zum Schließen des Fensters geben.
  • Die Größe des Kopf-Bereichs für das Bewegen des Fensters wird in Abhängigkeit von der Anzahl der verwendeten Buttons berechnet.

Für die Funktionalität des Minimierens und Maximierens des Fensters, werden zwei Buttons benötigt. Sie werden übereinander gelegt und je nach Status des Fensters ein- oder ausgeblendet. Wenn das Fenster maximiert ist, dann ist der Button für das Minimieren des Fensters sichtbar, und umgekehrt.

Bei allen anderen Dingen gibt es keine Unterschiede zu den anderen Methoden für das Erzeugen von Controls auf dem Formular (der Form). Den detaillierten Programmcode für dieCreateButtonClose(), CreateButtonRollUp(), CreateButtonUnroll() und CreateButtonTooltip() Methoden finden Sie in der angefügten Window.mqh Datei am Ende dieses Artikels.

Die hier verwendeten Icons für die Buttons können Sie ebenfalls am Ende des Artikels herunterladen. Sie können natürlich auch ihre eigenen Icons verwenden. Hierbei müssen Sie beachten, dass sie die gleichen Dateinamen verwenden, oder sie müssen die Pfade zu den BMP-Dateien in dem Programmcode entsprechend abändern.

 

Hinzufügen der Form zu dem Chart

Nun sind wir in der Phase, in welcher wir einen kleinen Test durchführen und auch ein Ergebnis ansehen können. Lassen Sie uns nun anschauen, wie die Control-Form (Formular mit Steuerelemeneten / das Fenster) auf dem Chart aussieht. Die Funktionalität für das Erzeugen dieser Form, haben wir weiter oben schon erzeugt.

Wir fahren nun mit unserer Datei für die Tests, welche wir vorher schon erzeugt haben, fort. Wir haben das Arbeiten mit dieser Datei an der Stelle aufgehört, als wir die Methode für das Erstellen des CProgram::CreateTradePanel() Interfaces und die Methode für das Erstellen der Form für die Controls CProgram::CreateWindow erzeugt haben. Nachfolgend nun der Programmcode für das Erstellen eines Formulars (Form). Bitte beachten Sie, wie die Hintergrundfarbe und der Kopf der Form neu definiert werden kann.

//+----------------------------------------------------------------+
//| Erzeugung einer Form für Controls                              |
//+----------------------------------------------------------------+
bool CProgram::CreateWindow(const string caption_text)
  {
//--- Hinzufügen eines Pointers des Fensters zu dem Fenster-Array
   CWndContainer::AddWindow(m_window);
//--- Eigenschaften
   m_window.XSize(200);
   m_window.YSize(200);
   m_window.WindowBgColor(clrWhiteSmoke);
   m_window.WindowBorderColor(clrLightSteelBlue);
   m_window.CaptionBgColor(clrLightSteelBlue);
   m_window.CaptionBgColorHover(C'200,210,225');
//--- Erzegen der Form
   if(!m_window.CreateWindow(m_chart_id,m_subwin,caption_text,1,1))
      return(false);
//---
   return(true);
  }

In der gemeinsamen Methode CProgram::CreateTradePanel() Rufen Sie die CProgram::CreateWindow() Methode mit dem Namen des Fensters als Parameter auf:

//+----------------------------------------------------------------+
//| Erzeugt ein Prading Panel                                      |
//+----------------------------------------------------------------+
bool CProgram::CreateTradePanel(void)
  {
//--- Erzeugung einer Form für Controls
   if(!CreateWindow("EXPERT PANEL"))
      return(false);
//--- Erzeugung der Controls
// ...
//---
   ::ChartRedraw();
   return(true);
  }

Jetzt müssen wir nur noch einen Aufruf der CProgram::CreateTradePanel() Methode in der Haupt-Datei des Programms in der OnInit() Funktion durchführen. Falls es bei der Erzeugung des Interfaces zu einem Fehler kommt, wird eine entsprechende Nachricht in dem Journal angezeigt und die Arbeit wird abgeschlossen:

//+-----------------------------------------------------------------+
//| Expert Initialisierungs-Funktion                                |
//+-----------------------------------------------------------------+
int OnInit(void)
  {
   program.OnInitEvent();
//--- Festlegen eines Trading Panels
   if(!program.CreateTradePanel())
     {
      ::Print(__FUNCTION__," > Failed to create graphical interface!");
      return(INIT_FAILED);
     }
//--- Initialisierung erfolgreich
   return(INIT_SUCCEEDED);
  }

Kompilieren Sie den Programmcode und laden Sie das Programm auf einen Chart. Falls alles korrekt ist, dann sehen Sie ein Formular, bzw. ein Fenster auf dem Chart, so wie in dem abgebildeten Screenshot:

Abbildung 4. Erster Test für das Hinzufügen eines Fensters zu dem Chart

Abbildung 4. Erster Test für das Hinzufügen eines Fensters zu dem Chart

Auch wenn sie vergessen sollten, einen Fenster-Pointer zu der Pointer-Basis hinzuzufügen, können Sie diesen Programmcode dennoch kompilieren. Aber wenn sie dann versuchen dieses Programm auf den Chart zu laden, dann werden Sie feststellen dass das Programm sofort beendet wird und Sie die folgende Nachricht in dem Journal "Expert Advisors" Tab erhalten (das "Tools" Panel):

2015.09.23 19:26:10.398 TestLibrary (EURUSD,D1) OnInit > Failed to create graphical interface!
2015.09.23 19:26:10.398 TestLibrary (EURUSD,D1) CWindow::CreateWindow > Before creating a window, its pointer is to be saved in the base: CWndContainer::AddWindow(CWindow &object).

Mir detaillierte Beispiele für die Verwendung der Methoden der CWindow Klasse in einem Indikator oder Skript, finden Sie in den nachfolgenden Kapiteln dieser Serie.

 

Schlussfolgerung

Die aktuelle Struktur der Bibliothek, die wir hier entwickelt haben, wird in dem nachfolgenden Diagramm dargestellt:

Abbildung 5. Hinzufügen des Haupt-Interface-Controls und einer Form für Controls zu dem Projekt.

Abbildung 5. Hinzufügen des Haupt-Interface-Controls und einer Form für Controls zu dem Projekt.

Alle Klassen, die sich auf die Interface Controls beziehen und die von deren CElement Basis-Klasse abgeleitet wurden, befinden sich in dem großen Rechteck mit dem dicken blauen Pfeil. Alle Interface-Elemente befinden sich in der Datei mit der KlasseCWndContainer.

Sie können das gesamte Material des ersten Teils dieser Serie auf Ihren Rechner herunterladen und testen. Wenn Sie fragen zur Verwendung dieses Materials haben, dann können Sie zunächst auf die detaillierte Beschreibung in der Bibliothek zurückgreifen oder Sie stellen Ihre Frage(n) in den Kommentaren zu diesem Artikel.

Liste der Artikel (Kapitel) des ersten Teils:

Übersetzt aus dem Russischen von MetaQuotes Software Corp.
Originalartikel: https://www.mql5.com/ru/articles/2126

Beigefügte Dateien |
Wie schnell ein Bedienfeld zu einem Indikator und Expert Advisor hinzugefügt werden kann Wie schnell ein Bedienfeld zu einem Indikator und Expert Advisor hinzugefügt werden kann

Möchten Sie zu Ihrem EA oder einem Indikator ein grafisches Feld für eine einfache und schnelle Kontrolle hinzufügen, aber Sie wissen nicht, wie Sie dies tun können? In diesem Artikel werde ich Ihnen Schritt um Schritt zeigen, wie man ein einfaches Feld zu Ihrem MQL4 / MQL5-Programm hinzufügen kann.

Die Fehlerverarbeitung und Protokollierung in MQL5 Die Fehlerverarbeitung und Protokollierung in MQL5

In diesem Artikel werden die meisten Fragen bezüglich der Fehlerverarbeitung im Programm betrachtet. Außerdem betrachten wir Protokollierung und werden ein Beispiel der Log-Realisierung mit MQL5 darstellen.

Graphical Interfaces I: Animation des graphischen Interfaces (Kapitel 3) Graphical Interfaces I: Animation des graphischen Interfaces (Kapitel 3)

In dem vorherigen Artikel haben wir eine Formular-Klasse (Form class) mit Steuerelementen (Controls) entwickelt. In diesem Artikel werden wir die Klasse mit Methoden auffüllen, welche es möglich machen, das Formular über den Chartbereich zu bewegen. Wir werden anschließend diese Komponente in den Kern der Bibliothek mit aufnehmen. Zudem werden wir sicherstellen, dass sich die Farbe der Form verändert, sobald sich die Maus darüber befindet.

Grafische Interfaces I: Funktionen für die Form-Buttons und das Entfernen der Interface Elemente (Kapitel 4) Grafische Interfaces I: Funktionen für die Form-Buttons und das Entfernen der Interface Elemente (Kapitel 4)

In diesem Artikel werden wir mit der Entwicklung der CWindow Klasse fortschreiten, indem wir ihr Methoden hinzufügen, welche es uns erlauben das Formular über das Anklicken seiner Controls zu verwalten. Wir sind dann in der Lage das Formular über einen Button zu schließen, so wie es zu minimieren und maximieren.