English Русский 中文 Español 日本語 Português 한국어 Français Italiano Türkçe
Benutzerdefinierte grafische Bedienelemente. Teil 1: Erstellen eines einfachen Bedienelements

Benutzerdefinierte grafische Bedienelemente. Teil 1: Erstellen eines einfachen Bedienelements

MetaTrader 5Beispiele | 25 Januar 2016, 15:03
1 671 0
Dmitry Fedoseev
Dmitry Fedoseev

Einleitung

Die MQL5-Sprache bietet Entwicklern eine große Auswahl an programmgesteuerten grafischen Objekten: einen Button, eine Textbeschriftung, ein Bearbeitungsfeld, ein Bitmap-Label (Abb. 1), unterschiedliche grafische Analyse-Tools (Abb. 2).


Abb. 1 Grafische Objekte: ein Button, eine Textbeschriftung, ein Bearbeitungsfeld, ein Bitmap-Label


Abb. 2 Einige grafische Objekte für die Analyse: eine Ellipse, der Fibonacci-Fächer, die Fibonacci Expansion

Insgesamt bietet das MetaTrader 5 Client Terminal über vierzig grafische Objekte. Alle diese Objekte können separat verwendet werden, meistens werden sie allerdings als Kette miteinander verknüpfter Objekte verwendet. Beispielsweise wird ein Bitmap-Label (OBJ_LABEL) sehr oft zusammen mit einem Bearbeitungsfeld (OBJ_EDIT) verwendet, um die Funktion des Bearbeitungsfelds zu verdeutlichen.

Bei der Verwendung eines Bearbeitungsfelds müssen Sie oft die Korrektheit der von einem Benutzer eingegebenen Daten prüfen sowie die Möglichkeit bieten, sowohl einen Punkt als auch ein Komma als Dezimaltrennzeichen zu verwenden.

Bei der Ausgabe der Daten im Programm sollten Sie die Daten formatieren, beispielsweise, indem Sie unnötige Nullen entfernen. Es wäre also einfacher, ein einziges Objekt zu haben, das das Bearbeitungsfeld, das Bitmap-Label und einige weitere Funktionen vereint.

Derzeit gibt es einen bestimmten Satz an grafischen Bedienelementen in der Programmierwelt, die in fast jeder Anwendung genutzt werden: eine Form (die Basis der Oberfläche einer Anwendung, in der sich alle Bedienelemente befinden), einen Rahmen (dieser ermöglicht die Gruppierung und Trennung von Elementen, die denselben funktionalen Zweck erfüllen), einen Button, ein Bearbeitungsfeld, ein Label, ein Kontrollkästchen, Optionsschaltflächen, vertikale und horizontale Scroll-Leisten, eine Liste, eine Drop-Down-Liste, eine Menüreihe, eine Menü-Registerkarte (Abb. 3). 

 
Abb. 3 Die Form mit den am häufigsten verwendeten Standard-Bedienelementen

Die Art, auf die die oben aufgezählten Elemente in MQL5 dargestellt werden, ist anderen Programmiersprachen ähnlich (ein Button und ein Bearbeitungsfeld). Es wäre sehr bequem, weitere häufig genutzte Bedienelemente in Ihrem Arsenal zu haben.

Etliche Entwicklungsumgebungen bieten Programmierern spezielle Tools zur Erstellung benutzerdefinierter Bedienelemente. MQL5 bietet kein solches Feature. Doch da es sich bei MQL5 um eine objektorientierte Sprache handelt, ist dies nicht unbedingt erforderlich. Alles ist in Form von separaten programmierten Objekten möglich.

Die Grundsätze und die Methoden der Erstellung benutzerdefinierter Bedienelemente werden später in diesem Beitrag besprochen. Auf dieser Basis kann jeder, der über Programmierkenntnisse verfügt, einen notwendigen Satz an Bedienelementen erstellen, die wiederholt in verschiedenen Anwendungen genutzt werden können.


1. Wie ein grafisches Bedienelement sein sollte

1.1. Allgemeine Anforderungen und Grundsätze

Um nützlich zu sein, sollten die grafischen Bedienelemente die Entwicklung von Anwendungen einfacher machen. Dazu müssen sie die folgenden Anforderungen erfüllen:

  1. Es sollte möglich sein, ein Bedienelement während der Entwicklung schnell zu erstellen. Dieses Problem wird durch die objektorientierte Herangehensweise an die Programmierung gelöst. Ein grafisches Bedienelement wird als ein programmiertes Objekt dargestellt.
     
  2. Ein Bedienelement sollte flexibel sein. Das heißt, es muss möglich sein, seine Eigenschaften (Größe, Position, Farbe usw.) zu ändern.
     
  3. Ein Bedienelement sollte benutzerfreundlich sein. Es muss nur über erforderliche Eigenschaften und Methoden verfügen, deren Zweck sich aus dem Zweck des Bedienelements und den Namen der Methoden ergibt. Lassen Sie uns die Eigenschaften von Bedienelementen in Kategorien unterteilen:
     
    1. Nicht gesteuerte Eigenschaften. Zu diesem Eigenschaften gehört das Farbschema. Alle Bedienelemente, die in einer Anwendung verwendet werden, sollten einem übergreifenden Stil folgen. Deshalb wäre die Einstellung der Farbe für jedes einzelne Bedienelement äußerst mühsam.
      Außerdem ist die Suche nach der richtigen Farbe für bestimmte Bedienelemente eine ziemlich schwierige Aufgabe, mit der man keine Zeit verlieren möchte. Nehmen wir zum Beispiel eine Scroll-Leiste. Einige Webentwickler standen möglicherweise schon vor dieser interessanten Aufgabe.
       
    2. Eigenschaften, die während der Erstellung eines Bedienelements festgelegt oder selten geändert werden. Beispielsweise die Größe eines Bedienelements. Die ausgewogene und praktische Positionierung aller Bedienelemente, die in einer Anwendung verwendet werden, ist eine besondere und schwierige Aufgabe, die während der Erstellung der Oberfläche gelöst wird.
      Aus diesem Grund wird die Größe von Bedienelementen für gewöhnlich nicht während der Ausführung des Programms verändert. Allerdings müssen Sie solche Eigenschaften möglicherweise gelegentlich anpassen. Deshalb sollten sie die Möglichkeit anbieten, diese Eigenschaften während der Ausführung des Programms zu verändern.
       
    3. Wichtigste Nutzungseigenschaften. Eigenschaften, die häufig durch das Programm verändert werden. Diese Eigenschaften stellen den Zweck eines Bedienelements dar. Sie lassen sich in zwei Kategorien unterteilen:
       
      1. Eigenschaften mit automatischer Aktualisierung der Anzeige eines Bedienelements. Beispielsweise ein Bearbeitungsfeld. Sobald ein Wert durch das Programm festgelegt wurde, sollten die Änderungen auf dem Bildschirm angezeigt werden. Bei der Programmierung sollte dies über eine einzige Codezeile geschehen.
         
      2. Eigenschaften, die eine verpflichtende Aktualisierung der Anzeige erfordern. Beispielsweise eine Liste. Bei Listen wird angenommen, dass mit Datenbereichen gearbeitet wird. Deshalb sollte eine Liste nach der Arbeit mit einem Listenelement nicht automatisch aktualisiert werden. Es ist besser, eine verpflichtende Aktualisierung nach der Beendigung der Arbeit mit allen Listenelementen durchzuführen. Durch diese Herangehensweise steigt die Performance der Anwendung deutlich.
         
  4. Es sollte die Möglichkeit bestehen, ein Bedienelement schnell aus- und einzublenden. Es darf keine wiederholte Festlegung der Anzeigeeigenschaften erforderlich sein, damit ein Bedienelement sichtbar wird. Der Zugriff auf Objekteigenschaften sollte unabhängig von der Sichtbarkeit der grafischen Objekte dieses Bedienelements möglich sein. Das heißt, dass ein programmiertes Objekt alle Eigenschaften des Bedienelements beinhalten muss und nicht die Eigenschaften von grafischen Objekten verwenden darf.
      
  5. Um Ausgabeergebnisse für ein Bedienelement zu erhalten, sollte eine Verarbeitung der Ereignisse einzelner grafischer Objekte stattfinden, die im Bedienelement enthalten sind.

1.2. Die Art der Nutzung eines Bedienelements und erforderliche Methoden 

Unter Berücksichtigung der oben erwähnten Anforderungen erhalten wir das folgende Schema für die Erstellung einer grafischen Oberfläche und der erforderlichen Eigenschaften und Methoden eines programmierten Objekts:

  1. Initialisierung eines Bedienelements und parallele Festlegung selten veränderter Eigenschaften. Diese Methode nennen wir Init(). Sie hat mehrere Parameter. Der 1. erforderliche Parameter ist der Name des Bedienelements. Dieser Parameter wird als Präfix für die Namen aller grafischen Objekte, die in dem Bedienelement enthalten sind, verwendet. Zusätzlich können die Parameter, die die Größe des Bedienelements festlegen, und weitere Parameter einbezogen werden (abhängig vom Zweck des Bedienelements).
     
  2. Bedienelemente können einen festen Platz in einem Diagramm haben. Ebenso ist es aber auch möglich, dass sie verschoben werden können müssen. Zur Bestimmung der Koordinaten verwenden wir zwei verschiedene Methoden: SetPosLeft() – zur Bestimmung der X-Koordinate, SetPosTop() – zur Bestimmung der Y-Koordinate. Jede dieser Methoden sollte einen Parameter haben. Häufig müssen beide Koordinaten angepasst werden. Deshalb ist es vorteilhaft, auf die SetPos()-Methode mit zwei Parametern zurückgreifen zu können, die gleichzeitig die X- und Y-Koordinate ändert.
    Um den Ort eines Bedienelements zu berechnen, müssen Sie möglicherweise die Informationen über die Größe und den Ort eines anderen Bedienelements abrufen. Zu diesem Zweck nutzen wir die folgenden Methoden: Width() – Breite, Height() – Höhe, Left() – X-Koordinate, Top() – Y-Koordinate. An dieser Stelle der Ausführung werden die Koordinaten des Bedienelements berechnet und die Methode zu ihrer Bestimmung aufgerufen.
     
  3. Nach der Erstellung oder an einer anderen Stelle der Ausführung der Anwendung müssen wir das Bedienelement sichtbar machen. Dazu wird die Methode Show() verwendet. Zum Ausblenden verwenden wir die Methode Hide().
     
  4. Wie bereits erwähnt, müssen wir möglicherweise während der Ausführung des Programms die Größe des Bedienelements verändern. Deshalb sollte das programmierte Objekt zwei unterschiedliche Methoden zur Festlegung seiner Größe haben: SetWidth() und/oder SetHeight(). Da es sich dabei um Eigenschaften handelt, die selten verändert werden, müssen wir die Methode Refresh() zur Aktualisierung der Anzeige aufrufen, damit die Änderungen übernommen werden.

  5. Die Ereignisse einzelner grafischer Objekte werden in der Methode Event() verarbeitet, die einen Wert ausgibt, der einem bestimmten Ereignis des Bedienelements entspricht. Die Event()-Methode sollte über die OnChartEvent()-Funktion aufgerufen werden. Sie hat den gleichen Parametersatz wie die OnChartEvent()-Funktion.

Somit erhalten wir den Satz der erforderlichen Methoden eines programmierten Objekts:

  • void Init(string aName...) – Initialisierung des Bedienelements;
  • void SetPosLeft(int aLeft) – Festlegen der X-Koordinate;
  • void SetPosTop(int aTop) – Festlegen der Y-Koordinate;
  • void SetPos(int aLeft, int aTop) – gleichzeitiges Festlegen der X- und Y-Koordinate;
  • void SetWidth(int aWidth) – Festlegen der Breite;
  • void SetHeight(int aHeight) – Festlegen der Höhe;
  • int Width() – Abrufen der Breite;
  • int Height() – Abrufen der Höhe;
  • int Left() – Abrufen der X-Koordinate;
  • int Top() – Abrufen der Y-Koordinate;
  • void Refresh() – vollständige Aktualisierung der Anzeige;
  • void Show() – Einblenden;
  • void Hide() – Ausblenden;
  • int Event(const int id, const long & lparam, const double & dparam, const string & sparam) – Verarbeitung von Diagrammereignissen;

Das Vorhandensein der anderen Methoden (oder das Fehlen bestimmter aufgezählter Methoden) hängt vom Bedienelement selbst und dessen Zweck ab.

Wir werden später versuchen, die oben beschriebenen Prinzipien umzusetzen. Wir erstellen ein Bedienelement, mit dem ein Benutzer einen Text oder Zahlen eingeben kann.

Davor benötigen wir die Werkzeuge für eine schnelle und bequeme Arbeit mit grafischen Objekten.


2. Schnelles und bequemes Arbeiten mit grafischen Objekten

Für die Arbeit mit grafischen Objekten bietet MQL5 die folgenden Grundfunktionen: ObjectCreate(), ObjectDelete(), ObjectSetDouble(), ObjectSetInteger(), ObjectSetString(), ObjectGetDouble(), ObjectGetInteger(), ObjectGetString(). Wir können diese Funktionen direkt nutzen, doch der Programmiervorgang wäre sehr arbeitsintensiv und aufwändig. Die Funktionen haben lange Namen und es müssen viele Indikatoren mit langen Namen an die Funktionen übergeben werden.

Um die Arbeit mit grafischen Objekten bequemer zu machen, können wir eine vorgefertigte Klasse nutzen, die im MetaTrader 5 Client-Terminal-Paket enthalten ist (die CChartObject-Klasse aus der Datei MQL5/Include/ChartObjects/ChartObject.mqh), oder unsere eigene Klasse schreiben und ihr alle benötigten Methoden geben.

Lustigerweise besteht diese Herangehensweise an die Programmierung aus dem Drücken eines Knopfs gefolgt von einem Punkt. Nach der Bestimmung des Namen eines Objekts muss ein Punkt eingefügt werden, damit die Liste seiner Eigenschaften und Methoden geöffnet wird. Wählen Sie einfach einen erforderlichen Listeneintrag aus (Abb. 4).


Abb. 4 Die Liste der Eigenschaften und Methoden des Objekts

Für die Verwaltung eines grafischen Objekts mithilfe der Hilfsklasse gibt es zwei Varianten:

  1. Für jedes grafische Objekt wird eine einzelne Klasseninstanz erstellt. Betrachtet man den Speicherverbrauch, ist diese Methode zwar ziemlich bequem, aber nicht sehr sparsam. Bei dieser Variante ist es besser, spezielle Klassen für jede Art von grafischem Objekt zu schreiben. Doch da diese Herangehensweise sehr aufwändig ist, ist sie nicht optimal. Anders als bei der Programmierung von Expert Advisors gibt es bei der Erstellung einer Benutzeroberfläche keine strengen Anforderungen an die Performance.
  2. Verwendung einer einzelnen Klasseninstanz. Falls es erforderlich ist, ein grafisches Objekt zu verwalten, wird das Objekt dieser Klasse hinzugefügt. Wir nutzen die zweite Variante.

Erstellen wir eine universelle Klasse, die am besten für die zweite Art der Verwaltung grafischer Objekte geeignet ist.


3. Die universelle Klasse für die Verwaltung grafischer Objekte

Während der Programmierung besteht die Arbeit mit jedem grafischen Objekt aus drei Phasen: Erstellung, Lesen/Festlegen von Eigenschaften, Löschen am Ende der Ausführung der Anwendung.

Als Erstes sollte die Klasse für die Verwaltung der grafischen Objekte also die Methoden zu ihrer Erstellung beinhalten. Ein verpflichtender Parameter für die Erstellung eines grafischen Objekts ist dessen Name. Somit werden die Methoden zur Erstellung von Objekten einen verpflichtenden Parameter für die Angabe des Namen eines erstellten Objekts haben.

Grafische Objekte werden für gewöhnlich in dem Diagramm erstellt, auf dem das Programm (Expert Advisor, Indikator oder Script) ausgeführt wird. Ein seltenerer Fall ist ein Unterfenster und ein noch seltenerer Fall ist ein anderes Diagrammfenster des Terminals. Deshalb ist der zweite optionale Parameter derjenige, der die Nummer eines Unterfensters bestimmt. Der dritte ist der Identifikator eines Diagramms.

Standardmäßig sind beide optionalen Parameter 0 (das Preisdiagramm in seinem "eigenen" Diagramm). Sehen Sie sich die Liste der Typen grafischer Objekte in der Dokumentation an. Fügen Sie die Create-Methode für jeden Typen hinzu.

Vorher müssen wir eine Datei erstellen. Öffnen Sie hierzu den MetaEditor, erstellen sie eine neue Include-Datei und nennen Sie sie IncGUI.mqh. Erstellen Sie in der geöffneten Datei die CGraphicObjectShell-Klasse mit protected- und public-Bereichen. Deklarieren Sie im protected-Bereich Variablen für den Namen eines Objekts und den Identifikator eines Diagramms.

In den Methoden zur Erstellung von Objekten werden diesen Variablen die Werte zugewiesen, die von Methoden als Parameter übergeben werden, damit eine Möglichkeit besteht, sie nach der Erstellung eines Objekts ohne Angabe eines Namen und Diagramm-Identifikators zu verwalten. Also können wir die Klasse auch für die erste Variante der Verwaltung der grafischen Objekte nutzen.

Um die Klasse für die zweite Variante nutzen zu können (zur Verwaltung eines beliebigen grafischen Objekts), geben Sie ihr eine Methode zum Anhängen eines grafischen Objekts (die Attach()-Methode). Diese Methode hat einen verpflichtenden Parameter, den Namen eines grafischen Objekts, und einen optionalen Parameter, einen Diagramm-Identifikator. Möglicherweise müssen Sie den Namen und den Identifikator eines angehängten grafischen Objekts in Erfahrung bringen. Fügen Sie dazu die folgenden Methoden zum Objekt hinzu: Name() und ChartID().

Als Ergebnis erhalten wir den folgenden Klassen"rohling": 

class CGraphicObjectShell
  {
protected:
   string            m_name;
   long              m_id;
public:
   void Attach(string aName,long aChartID=0)
     {
      m_name=aName;
      m_id=aChartID;
     }
   string Name()
     {
      return(m_name);
     }    
   long ChartID()
     {
      return(m_id);
     }
  };

Fügen Sie die oben beschriebenen Methoden zur Erstellung grafischer Objekte hinzu. Die Namen dieser Methoden beginnen mit "Create".

Die Leser können all dies überspringen, da die an diesen Beitrag angehängte Datei IncGUI.mqh bereits die vorgefertigte Klasse CGraphicObjectShell beinhaltet.

Hier sehen Sie beispielsweise eine Methode zur Erstellung einer vertikalen Linie als grafisches Objekt (OBJ_VLINE):

void CreateVLine(string aName,int aSubWindow=0,long aChartID=0)
  {
   ObjectCreate(m_id,m_name,OBJ_VLINE,aSubWindow,0,0);
   Attach(aName,aChartID);
  }

Öffnen Sie nun die Liste der Eigenschaften grafischer Objekte im Benutzerhandbuch und schreiben Sie Methoden zum Festlegen eines Werts für jede Eigenschaft mithilfe der Funktionen ObjectSetDouble(), ObjectSetInteger() und ObjectSetString(). Die Namen der Methoden beginnen mit "Set". Schreiben Sie als Nächstes Methoden zum Lesen der Eigenschaften mithilfe der Funktionen ObjectGetDouble() und ObjectGetInteger(). ObjectGetString().

Als Beispiel sehen Sie hier die Methoden zum Festlegen und Abrufen der Farbe:

void SetColor(color aColor)
  {
   ObjectSetInteger(m_id,m_name,OBJPROP_COLOR,aColor);
  }
color Color()
  {
   return(ObjectGetInteger(m_id,m_name,OBJPROP_COLOR));
  }

Nun verfügen wir zunächst über das erforderliche Minimum an notwendigen Werkzeugen für die Arbeit mit grafischen Objekten, doch es sind noch nicht alle.

Manchmal müssen Sie beim Arbeiten mit einem grafischen Objekt möglicherweise nur eine Aktion mit einem Objekt durchführen. In diesem Fall wäre es nicht bequem, die Attach()-Methode für das Objekt auszuführen, um dann zum Hauptobjekt zurückzukehren und Attach() erneut dafür auszuführen.

Erweitern wir die Klasse um zwei weitere Varianten aller Methoden zum Bestimmen/Abrufen von Eigenschaften.

Die erste – nach dem Namen im "eigenen" Diagramm:

void SetColor(string aName,color aColor)
  {
   ObjectSetInteger(0,aName,OBJPROP_COLOR,aColor);
  }
color Color(string aName)
  {
   return(ObjectGetInteger(0,aName,OBJPROP_COLOR));
  }

Die zweite – nach dem Namen und Identifikator eines Diagramms:

void SetColor(long aChartID,string aName,color aColor)
  {
   ObjectSetInteger(aChartID,aName,OBJPROP_COLOR,aColor);
  }
color Color(long aChartID,string aName)
  {
   return(ObjectGetInteger(aChartID,aName,OBJPROP_COLOR));
  }

Zusätzlich zu den Funktionen ObjectGet... und ObjectSet... gibt es weitere Funktionen für die Arbeit mit grafischen Objekten: ObjectDelete(), ObjectMove(), ObjectFind(), ObjectGetTimeByValue(), ObjectGetValueByTime(), ObjectsTotal(). Diese können ebenfalls mit je drei Aufrufvarianten der Klasse hinzugefügt werden.

Zuletzt deklarieren wir die Klasse CGraphicObjectShell in dieser Datei mit dem simplen und kurzen Namen "g". 

CGraphicObjectShell g;

Nun reicht es aus, die Datei IncGUI.mqh zu verbinden, um mit der Arbeit mit grafischen Objekten zu beginnen. So können wir mit der Klasse "g" arbeiten. Mithilfe dieser Klasse können alle verfügbaren grafischen Objekte einfach verwaltet werden.


4. Rohlinge für Bedienelemente

Obwohl wir die Klasse für die schnelle Arbeit mit grafischen Objekten haben, können wir Bedienelemente noch einfacher erstellen. Alle Bedienelemente können basierend auf vier grafischen Objekten erstellt werden:

  1. Rechteckige Beschriftung (OBJ_RECTANGLE_LABEL),
  2. Textbeschriftung (OBJ_LABEL),
  3. Bearbeitungsfeld (OBJ_EDIT),
  4. Button (OBJ_BUTTON).

Nach der Erstellung grafischer Objekte müssen zahlreiche Eigenschaften für sie bestimmt werden: Koordinaten, Größe, Farbe, Schriftgröße usw. Um diesen Prozess zu beschleunigen, erstellen wir eine weitere Klasse und nennen sie CWorkPiece (Rohlinge). Wir versehen sie mit Methoden zur Erstellung grafischer Objekte mit Eigenschaften, die in Parametern übergeben werden. 

Damit die Bedienelemente funktionieren, müssen Sie die Diagrammereignisse bearbeiten. Ereignisse anderer Diagramme sind nicht verfügbar. Deshalb arbeiten wir nur mit dem eigenen Diagramm. In den Parametern der Methoden der CWorkPiece-Klasse wird es somit keine Diagramm-Identifikatoren geben. 0 (eigenes Diagramm) wird überall verwendet.

Der Parameter, der die Nummer eines Unterfensters bestimmt, wird verwendet, um die Möglichkeit zur Erstellung von Bedienelementen im Preisdiagramm und in dessen Unterfenstern zu gewährleisten. Die grafischen Objekte werden nur an die obere linke Ecke gebunden. Sollte es erforderlich sein, ein Bedienelement in Bezug auf eine beliebige andere Ecke zu verschieben, können die Koordinaten des gesamten Bedienelements unter Berücksichtigung der Diagrammgröße viel einfacher neu berechnet werden. Um die Änderung der Größe des Diagramms zu kontrollieren, können Sie das CHARTEVENT_CHART_CHANGE-Ereignis bearbeiten.

Als Basis für viele Bedienelemente werden wir das Objekt "Rechteckige Beschriftung" nutzen. Fügen Sie die Methode zur Erstellung dieses Objekts zur CWorkPiece-Klasse hinzu. Die Methode heißt Canvas():

void Canvas(string aName="Canvas",
             int aSubWindow=0,
             int aLeft=100,
             int aTop=100,
             int aWidth=300,
             int aHeight=150,
             color aColorBg=clrIvory,
             int aColorBorder=clrDimGray)
  {
   g.CreateRectangleLabel(aName,aSubWindow); // Creation of rectangle label
   g.SetXDistance(aLeft);                    // Setting of the X coordinate
   g.SetYDistanse(aTop);                        // Setting of the Y coordinate
   g.SetXSize(aWidth);                          // Setting of width
   g.SetYSize(aHeight);                         // Setting of height
   g.SetBgColor(aColorBg);                   // Setting of background color
   g.SetColor(aColorBorder);                 // Setting of border color
   g.SetCorner(CORNER_LEFT_UPPER);             // Setting of a anchor point
   g.SetBorderType(BORDER_FLAT);             // Setting of border type
   g.SetTimeFrames(OBJ_ALL_PERIODS);            // Setting visibility at all timeframes
   g.SetSelected(false);                        // Disabling selection
   g.SetSelectable(false);                   // Disabling of selection possibility
   g.SetWidth(1);                               // Setting of border width
   g.SetStyle(STYLE_SOLID);                  // Setting of border style
  }

Beachten Sie, dass die Methode aus 14 Zeilen Code besteht. Es wäre äußerst frustrierend, jede davon jedes Mal, wenn Sie dieses Objekt erstellen, schreiben zu müssen. Jetzt reicht es aus, nur eine Zeile zu schreiben. Alle Parameter der Methode sind optional und in der Reihenfolge der Häufigkeit ihrer Verwendung aufgelistet: Position, Größe, Farbe usw.

Schreiben Sie analog zur Canvas()-Methode die Methoden zur Erstellung einer Textbeschriftung, eines Buttons und eines Bearbeitungsfelds: Label(), Button() und Edit(). Die vorgefertigte Klasse CWorkPiece ist in der Datei IncGUI.mqh an den Beitrag angehängt. Zusätzlich zu den oben erwähnten Methoden beinhaltet die Klasse noch einige weitere Methoden: Frame() und DeleteFrame() – Methoden zur Erstellung und Löschung eines Rahmens (Abb. 5). Ein Rahmen ist eine rechteckige Beschriftung mit Text in der oberen linken Ecke.

Die Rahmen sind für den Gebrauch beim Gruppieren von Bedienelementen in einer Form geplant.


Abb. 5 Rahmen mit Beschriftung.

Die Liste aller Methoden der CWorkPiece-Klasse ist an den Beitrag angehängt.

Deklarieren Sie die CWorkPiece-Klasse analog zur CGraphicObjectShell-Klasse mit dem Kurznamen "w", um sie nach der Verbindung mit der Datei IncGUI.mqh sofort nutzen zu können.

CWorkPiece w;

Alle Hilfswerkzeuge sind fertig, also können wir mit dem Thema des Beitrags fortfahren: der Erstellung eines benutzerdefinierten Bedienelements.


5. Erstellung des Bedienelements "Bearbeitungsfeld"

Um die Terminologie nicht durcheinanderzubringen, bezeichnen wir das grafische Objekt OBJ_EDIT als Textfeld, das Objekt OBJ_LABEL als Beschriftung und das erstellte Bedienelement als Bearbeitungsfeld. Das erstellte Bedienelement besteht aus zwei grafischen Objekten: einem Textfeld (OBJ_EDIT) und einer Textbeschriftung (OBJ_LABEL).

Das Bedienelement wird zwei Bedienarten unterstützen: Eingabe von Textdaten und Eingabe von numerischen Daten. Bei der Eingabe von numerischen Daten wird es eine Begrenzung des Bereichs eingegebener Werte geben und sowohl das Komma als auch der Punkt werden als Dezimaltrennzeichen akzeptiert. Bei der programmierten Ausgabe eines Werts im Bearbeitungsfeld wird dieser Wert in Übereinstimmung mit einer festgelegten Anzahl an Nachkommastellen formatiert.

Deshalb müssen wir bei der Initialisierung eines Bedienelements seine Bedienart festlegen: Text oder numerisch. Die Bedienart wird mithilfe des Parameters aDigits festgelegt. Ein Wert, der größer als Null ist, legt die numerische Bedienart mit der angegebenen Anzahl an Nachkommastellen fest. Ein negativer Wert legt die Text-Bedienart fest.

Standardmäßig liegt der Bereich akzeptabler Werte zwischen -DBL_MAX und DBL_MAX (der gesamte Wertebereich einer double-Variable). Falls nötig, können Sie durch Aufruf der Methoden SetMin() und SetMax() einen anderen Wert einstellen. Aus den Größenparametern wird nur die Breite für das Bedienelement festgelegt. Damit das Bearbeitungsfeld ausgewogen aussieht, sollte seine Höhe und Breite entsprechend festgelegt werden.

Eine Anpassung der Schriftgröße setzt eine entsprechende Anpassung der Höhe des grafischen Objekts voraus. Dafür müssten wiederum alle anderen Bedienelemente verschoben werden. Das möchte niemand. Wir nehmen an, dass wir eine konstante Schriftgröße für alle Bedienelemente und die entsprechenden Bearbeitungsfelder verwenden. Allerdings wird die Klasse der Bedienelemente über eine Methode verfügen, die ihre Höhe ausgibt, um die Berechnung der Koordinaten anderer Elemente zu erleichtern.

Es wird vier Farb-Parameter geben: Hintergrundfarbe, Textfarbe, Beschriftungsfarbe und Warnfarbe (es wird möglich sein, die Hintergrundfarbe des Textfelds zu ändern, um die Aufmerksamkeit des Benutzers beispielsweise auf die Eingabe eines falschen Werts zu lenken).

Wie bereits erwähnt, werden Bedienelemente in einem Unterfenster unterstützt. Zusätzlich zu den Hauptparametern, die benötigt werden, damit ein Bedienelement funktioniert, werden wir den Parameter Tag verwenden. Dabei handelt es sich um einen einfachen Textwert, der in einer Instanz der Klasse gespeichert wird. Ein Tag ist ein bequemes Hilfswerkzeug.

Wir nennen die Klasse CInputBox. Wir erhalten also den folgenden Satz an Variablen für die Klasse (im private-Bereich):

string m_NameEdit;    // Name of the Edit object
string m_NameLabel;   // Name of the Label object
int m_Left;           // X coordinate
int m_Top;            // Y coordinate
int m_Width;           // Width
int m_Height;          // Height
bool m_Visible;        // Visibility flag of the control
int m_Digits;          // Number of decimal places for the double number; -1 set the text mode
string m_Caption;      // Caption
string m_Value;        // Value
double m_ValueMin;     // Minimum value
double m_ValueMax;     // Maximum value
color m_BgColor;       // Background color
color m_TxtColor;     // Text color
color m_LblColor;      // Caption color
color m_WarningColor; // Warning font color
bool m_Warning;        // Flag of warning
int m_SubWindow;       // Subwindow
string m_Tag;           // Tag

Bei der Verwendung eines Bedienelements heißt die erste Methode, die aufgerufen wird, Init().

In dieser Methode bereiten wir die Werte aller vorher festgelegten Parameter vor:

// The initialization method
void Init(string aName="CInputBox",
           int aWidth=50,
           int aDigits=-1,
           string aCaption="CInputBox")
 { 
   m_NameEdit=aName+"_E";  // Preparing the name of the text field
   m_NameLabel=aName+"_L"; // Preparing the caption name
   m_Left=0;                 // X coordinate
   m_Top=0;                  // Y coordinate
   m_Width=aWidth;          // Width
   m_Height=15;             // Height
   m_Visible=false;         // Visibility
   m_Digits=aDigits;       // The mode of operation and the number of decimal places
   m_Caption=aCaption;     // Caption text
   m_Value="";              // Value in the text mode
   if(aDigits>=0)m_Value=DoubleToString(0,m_Digits); // Value in the numeric mode
   m_ValueMin=-DBL_MAX;                   // Minimal value
   m_ValueMax=DBL_MAX;                  // Maximal value
   m_BgColor=ClrScheme.Color(0);       // Background color of the text field
   m_TxtColor=ClrScheme.Color(1);      // Color of text and frame of the text field
   m_LblColor=ClrScheme.Color(2);      // Caption color
   m_WarningColor=ClrScheme.Color(3); // Warning color
   m_Warning=false;                      // Mode: warning, normal
   m_SubWindow=0; // Number of subwindow
   m_Tag=""; // Tag
 }

Bei Bedienelementen im Textmodus wird der Variable m_Value der Wert "" zugewiesen. Im numerischen Modus erhält sie eine Null mit der festgelegten Anzahl an Nachkommastellen. Die Farbparameter erhalten ihre Standardwerte. Wir werden die Farbschemata in der letzten Phase betrachten.

Variablen, die die Koordinaten des Bedienelements bestimmen, werden auf Null gesetzt, da das Bedienelement noch nicht sichtbar ist. Nach dem Aufruf der Methode Init() (falls geplant ist, dass das Bedienelement einen festen Ort im Diagramm haben wird) können wir die Koordinaten mithilfe der Methode SetPos() festlegen:

// Setting the X and Y coordinates
void SetPos(int aLeft,int aTop)
{ 
   m_Left=aLeft;
   m_Top=aTop;
}

Anschließend können wir das Bedienelement sichtbar machen (Methode Show()):

// Enable visibility on the previously specified position
void Show()
{ 
   m_Visible=true; // Registration of visibility
   Create();       // Creation of graphical objects
   ChartRedraw();   // Refreshing of the chart
}

Die Create()-Funktion wird über die Methode Show() aufgerufen. Sie erstellt grafische Objekte (im private-Bereich) und anschließend wird das Diagramm aktualisiert (ChartRedraw()). Den Code der Create()-Funktion sehen Sie unten:

// The function of creation of graphical objects
void Create(){ 
   color m_ctmp=m_BgColor;  // Normal background color
      if(m_Warning){ // The warning method is set
         m_ctmp=m_WarningColor; // The text field will be color in the warning color
      }
    // Creation of the text field
   w.Edit(m_NameEdit,m_SubWindow,m_Left,m_Top,m_Width,m_Height,m_Value,m_ctmp,m_TxtColor,7,"Arial"); 
      if(m_Caption!=""){ // There is a caption
          // Creation of caption
         w.Label(m_NameLabel,m_SubWindow,m_Left+m_Width+1,m_Top+2,m_Caption,m_LblColor,7,"Arial"); 
      } 
}   

Bei der Erstellung grafischer Objekte in der Create()-Funktion in Abhängigkeit vom Wert von m_Warning wird dem Textfeld die entsprechende Hintergrundfarbe zugewiesen. Falls die Variable m_Caption einen Wert hat, wird die Beschriftung erstellt (Sie können auch ein Bedienelement ohne Beschriftung erstellen).

Falls Sie beabsichtigen, ein verschiebbares Bedienelement zu erstellen, nutzen Sie die zweite Variante der Methode Show() mit Angabe der Koordinaten. In dieser Methode werden die Koordinaten festgelegt und die erste Variante der Methode Show() aufgerufen:

// Setting the X and Y coordinates
void SetPos(int aLeft,int aTop){ 
   m_Left=aLeft;
   m_Top=aTop;
}

Wenn das Bedienelement angezeigt wird, müssen Sie es irgendwann ausblenden.

Diesem Zweck dient die Methode Hide():

// Hiding (deletion of graphical objects)
void Hide()
{ 
   m_Visible=false; // Registration of the invisible state
   Delete();        // Deletion of graphical objects
   ChartRedraw();    // Refreshing of the chart
}  

Die Methode Hide() ruft die Delete()-Funktion auf, die grafische Objekte löscht, und anschließend die ChartRedraw()-Funktion, um das Diagramm zu aktualisieren. Die Delete()-Funktion befindet sich im private-Bereich:

// The function of deletion of graphical objects
void Delete()
{ 
   ObjectDelete(0,m_NameEdit);  // Deletion of the text field
   ObjectDelete(0,m_NameLabel); // Deletion of caption
}   

Da wir bereits eine Methode kennen, die nur die Werte von Eigenschaften festlegt, ohne die Anzeige eines Bedienelements zu ändern (die Methode SetPos()), ist es nur logisch, eine Methode zu erstellen, die die Aktualisierung eines Bedienelements erzwingt: die Methode Refresh(): 

// Refreshing of displaying (deletion and creation)
void Refresh()
{ 
   if(m_Visible)
   {   // Visibility enabled
      Delete();     // Deletion of graphical object
      Create();     // Creation of graphical objects
      ChartRedraw(); // Redrawing of the chart 
   }            
}   

Das Bedienelement ist ziemlich simpel. Deshalb nutzen wir die simple Methode zum Aktualisieren: Löschen und Erstellen. Wäre das Bedienelement komplexer, zum Beispiel eine Liste, die aus zahlreichen Bearbeitungsfeldern besteht, würden wir einen ausgeklügelteren Ansatz wählen.

Wir sind mit dem Platzieren des Bedienelements also fertig. Fahren wir nun mit der Festlegung eines Werts fort – die Methode SetValue(). Da das Bedienelement zwei potenzielle Bedienarten hat, wird es zwei Varianten der SetValue()-Methode geben: string und double. Im Textmodus wird der Wert unverändert verwendet:

// Setting a text value
void SetValue(string aValue)
{ 
   m_Value=aValue; // Assigning a value to variable to store it
      if(m_Visible)
      { // The visibility of the control is enabled
          // Assigning the text field to the object for managing graphical objects
         g.Attach(m_NameEdit); 
         g.SetText(m_Value); // Setting the value for the text field
         ChartRedraw();        // Redrawing the chart
      }
} 

Das erhaltene Argument wird der Variable m_Value zugewiesen und, sofern das Bedienelement sichtbar ist, im Textfeld angezeigt.

Im numerischen Modus wird das erhaltene Argument gemäß dem Wert m_Digits normalisiert, in Übereinstimmung mit dem maximalen und minimalen Wert (m_MaxValue, m_MinValue) korrigiert, in einen String umgewandelt und anschließend die erste Methode SetValue() aufgerufen.

// Setting a number value
void SetValue(double aValue)
{ 
   if(m_Digits>=0)
   {  // In the numeric mode
       // Normalization of the number according to the specified accuracy
      aValue=NormalizeDouble(aValue,m_Digits);
      // "Alignment" of the value according to the minimal acceptable value
      aValue=MathMax(aValue,m_ValueMin); 
       // "Alignment" of the value according to the maximal acceptable value
      aValue=MathMin(aValue,m_ValueMax); 
       // Setting the obtained value as a string
      SetValue(DoubleToString(aValue,m_Digits)); 
   }
   else
   { // In the text mode
      SetValue((string)aValue); // Assigning the value to the variable to store it as is
   }            
}

Schreiben wir zwei Methoden zum Abrufen von Werten: eine zum Abrufen von string-Werten, die andere zum Abrufen von double-Werten:

// Getting a text value
string ValueStrind()
{ 
   return(m_Value);
}

// Getting a numeric value
double ValueDouble()
{ 
   return(StringToDouble(m_Value));
}

Der Wert, der für das Bedienelement festgelegt wird, wird gemäß den maximalen und minimalen akzeptablen Werten korrigiert. Fügen wir Methoden zum Abrufen und Festlegen hinzu:

// Setting the maximal acceptable value
void SetMaxValue(double aValue)
{ 
   m_ValueMax=aValue; // Registration of the new maximal accepted value
      if(m_Digits>=0)
     { // The control works in the numeric mode
         if(StringToDouble(m_Value)>m_ValueMax)
         { /* The current value of the control is greater than the new maximal acceptable value*/
            SetValue(m_ValueMax); // Setting the new value that is equal to the maximal accepted value
         }
      }         
}

// Setting the minimal acceptable value
void SetMinValue(double aValue)
{ 
   m_ValueMin=aValue; // Registration of the new minimal acceptable value     
      if(m_Digits>=0)
      { // The control works in the numeric mode
         if(StringToDouble(m_Value)<m_ValueMin)
         { /* The current value of the control is less than the new minimal acceptable value*/
            SetValue(m_ValueMin); // Setting the new value that is equal to the minimum acceptable value
         }
      }
}

// Getting the maximal accepted value
double MaxValue()
{ 
   return(m_ValueMax); 
}

// Getting the minimal accepted value
double MinValue()
{ 
   return(m_ValueMin);
}

Falls das Bedienelement im numerischen Modus ist, wird die Überprüfung und Korrektur (falls erforderlich) des aktuellen Werts durchgeführt, wenn neue maximale und minimale akzeptable Werte festgelegt werden.

Widmen wir uns nun der Eingabe von Werten durch den Benutzer, der Methode Event(). Die Überprüfung von Daten, die durch einen Benutzer eingegeben wurden, wird mithilfe des Ereignisses CHARTEVENT_OBJECT_ENDEDIT durchgeführt. Wenn ein von einem Benutzer im Textmodus angegebener Wert nicht m_Value entspricht, wird der neue Wert genauso m_Value zugewiesen, wie der Wert 1 der Variable m_event zugewiesen wird, die von der Methode Event() ausgegeben wird.

Wenn wir im numerischen Modus arbeiten, merken wir uns den vorherigen Wert von m_Value in der Variable m_OldValue, ersetzen das Komma durch einen Punkt, wandeln den String in eine Zahl um und übergeben sie an die Funktion SetValue(). Falls m_Value und m_OldValue nicht gleich sind, "generieren" Sie anschließend das Ereignis (setzen Sie den Wert der Variable m_event auf 1).

// Handling of events
int Event(const int id,
           const long & lparam,
           const double & dparam,
           const string & sparam)
{ 
   bool m_event=0; // Variable for an event of this control
      if(id==CHARTEVENT_OBJECT_ENDEDIT)
      { // There has been an event of end of editing the text field
         if(sparam==m_NameEdit)
         { // The text field with the name m_NameEdit has been modified
            if(m_Digits<0)
            { // In the text mode
               g.Attach(m_NameEdit); // Assigning the text field for controlling it
                  if(g.Text()!=m_Value)
                  { // New value in the text field
                     m_Value=g.Text(); // Assigning the value to the variable to store it
                     m_event=1;         // There has been an event
                  }
            }
            else
            { // In the numeric mode
               string m_OldValue=m_Value; // The variable with the previous value of the control
               g.Attach(m_NameEdit);      // Attaching the text field for controlling it
               string m_stmp=g.Text();     // Getting text specified by a user in the text field
               StringReplace(m_stmp,",",".");       // Replacing comma with a dot
               double m_dtmp=StringToDouble(m_stmp); // Conversion to a number
               SetValue(m_dtmp);                     // Setting the new numeric value
                     // Comparing the new value with the previous one
                  if(StringToDouble(m_Value)!=StringToDouble(m_OldValue))
                  { 
                     m_event=1; // There has been an event 
                  }
            }
         }
      }               
   return(m_event); // Return the event. 0 - there is no event, 1 - there is an event
}

Sorgen wir dafür, dass das Bedienelement in Unterfenstern funktioniert. Dafür fügen Sie die Methode SetSubWindow() hinzu, die im Fall eines CHARTEVENT_CHART_CHANGE-Ereignisses über die Funktion OnChartEvent() aufgerufen werden soll. Falls Sie das Bedienelement nur in einem Preisdiagramm verwenden möchten, muss diese Methode nicht aufgerufen werden. 

Die Variable m_SubWindow ist bereits deklariert. Sie beträgt standardmäßig 0 und wird bei der Erstellung der grafischen Objekte eines Bedienelements an die Methoden Edit() und Label() der Klasse "w" übergeben. Die Nummer des Unterfensters wird an die Methode SetSubWindowName() übergeben. Wird die Nummer verändert, ändern Sie den Wert der Variable m_SubWindow und führen sie die Methode Refresh() aus.

// Setting a subwindow by number
void SetSubWindow(int aNumber)
{ 
   int m_itmp=(int)MathMax(aNumber,0); /* If the number is negative, 0 will be used - the price chart*/
      if(m_itmp!=m_SubWindow)
      { /* The specified number doesn't correspond the number where the control is located*/
         m_SubWindow=m_itmp; // Registration of the new number of subwindow
         Refresh(); // Recreation of the graphical objects
      }
} 

Wahrscheinlich wäre es praktischer, den Namen eines Unterfensters anstatt seiner Nummer an die Funktion zu übergeben. Fügen Sie eine weitere Variante der Methode SetSubWindow() hinzu:

// Setting a subwindow by name
void SetSubWindow(string aName)
{ 
   SetSubWindow(ChartWindowFind(0,aName)); // Determination of the number of the subwindow by its name and setting the subwindow by number
}

Vervollständigen Sie die Klasse des Bedienelements mit den anderen fehlenden Methoden gemäß dem am Anfang des Beitrags angegebenen Konzept.

Sobald wir die SetPos()-Methode haben, die die gleichzeitige Festlegung beider Koordinaten des Bedienelements ermöglicht, fügen Sie die Methoden für die separate Festlegung der Koordinaten hinzu:

// Setting the X coordinate
void SetPosLeft(int aLeft)
{ 
   m_Left=aLeft;
}      

// Setting the Y coordinate
void SetPosTop(int aTop)
{ 
   m_Top=aTop;
}  

Methode zum Festlegen der Breite:

// Setting the width
void SetWidth(int aWidth)
{ 
   m_Width=aWidth;
}

Methode zum Abrufen der Koordinaten und Größe:

// Getting the X coordinate
int Left()
{ 
   return(m_Left);
}

// Getting the Y coordinate
int Top()
{ 
   return(m_Top);
}

// Getting the width
int Width()
{ 
   return(m_Width);
}

// Getting the height
int Height()
{
   return(m_Height); 
}

Methoden für die Arbeit mit dem Tag:

// Setting the tag
void SetTag(string aValue)
{ 
   m_Tag=aValue;
}

// Getting the tag
string Tag()
{ 
   return(m_Tag);
}  

Warnmethoden:

// Setting the warning mode
void SetWarning(bool aValue)
{ 
      if(m_Visible)
      { // Visibility is enabled
         if(aValue)
         { // We need to turn on the warning mode
            if(!m_Warning)
            { // The warning mode has not been enabled
               g.Attach(m_NameEdit);         // Attaching the text field for controlling
               g.SetBgColor(m_WarningColor); // Setting the warning color of text in the text field
            }
         }
         else
         { // We need to disable the warning mode
            if(m_Warning)
            { // The warning mode is enabled
               g.Attach(m_NameEdit);    // Attach the text field for controlling 
               g.SetBgColor(m_BgColor); // Setting the normal font color                
            }
         }
      }
   m_Warning=aValue; // Registration of the current mode
}

// Getting the warning mode
bool Warning()
{ 
   return(m_Warning);
}

Falls das Bedienelement bei der Festlegung des Warnmodus sichtbar ist, wird der Wert des Parameters, der an die SetWarning-Methode übergeben wird, überprüft. Falls der Wert nicht dem aktuellen Status des Bedienelements entspricht, ändert sich die Hintergrundfarbe des Textfelds.

In jedem Fall wird der festgelegte Modus so registriert, dass die entsprechende Farbe für das Textfeld festgelegt wird, falls das Bedienelement bei seiner Erstellung unsichtbar ist.

Es bleibt noch eine Eigenschaft: m_Digits. Fügen wir Methoden zum Abrufen und Festlegen ihres Werts hinzu: 

// Setting the number of decimal places
void SetDigits(int aValue)
{ 
   m_Digits=aValue; // Registration of the new value
      if(m_Digits>=0)
      { // The numeric mode
         SetValue(ValueDouble()); // Resetting of the current value
      }
}  

// Getting the m_Digits value
int Digits()
{ 
   return(m_Digits);
}  

Nun sind wir mit dem interessantesten Teil fertig. Jetzt ist der schönste Teil an der Reihe.


6. Farbschemata

Farbschemata werden in den Variablen der CColorSchemes-Klasse gespeichert.

Die Klasse wird vorher mit dem Namen ClrScheme in der Datei IncGUI.mqh deklariert. Um ein Farbschema festzulegen, rufen wir die Methode SetScheme() mit der Nummer eines Farbschemas als Parameter auf. Wird die Methode SetScheme() nicht aufgerufen, wird das Farbschema mit der Nummer 0 verwendet.

Um eine Farbe zu erhalten, nutzen wir die Methode Color() mit einer festgelegten Nummer einer Farbe aus dem Farbschema. Schreiben wir die Klasse CColor Schemes mit private- und public-Bereichen. Deklarieren Sie im private-Bereich die Variable m_ShemeIndex für die Speicherung des Index eines Farbschemas. Im public-Bereich schreiben Sie die Methode SetScheme():

// Setting the color scheme number
void SetScheme(int aShemeIndex)
{ 
   m_ShemeIndex=aShemeIndex;
}

Die Color()-Methode. In der Methode wird ein zweidimensionales Array deklariert: Die erste Dimension ist die Nummer des Farbschemas, die zweite ist die Nummer der Farbe im Schema. Je nach der festgelegten Nummer eines Farbschemas wird die Farbe nach der in den Parametern der Methode festgelegten Nummer ausgegeben.

color Color(int aColorIndex)
{
   color m_Color[3][4];  // The first dimension - the color scheme number, the second one - the number of the color in the color scheme
   // default
   m_Color[0][0]=clrSnow;
   m_Color[0][1]=clrDimGray;
   m_Color[0][2]=clrDimGray;
   m_Color[0][3]=clrPink;
   // yellow-black
   m_Color[1][0]=clrLightYellow;
   m_Color[1][1]=clrBrown;
   m_Color[1][2]=clrBrown;
   m_Color[1][3]=clrPink;
   // blue
   m_Color[2][0]=clrAliceBlue;
   m_Color[2][1]=clrNavy;
   m_Color[2][2]=clrNavy;
   m_Color[2][3]=clrPink;
   return(m_Color[m_ShemeIndex][aColorIndex]); // Returning a value according to the scheme number and the number of color in the scheme
}

Bisher beinhaltet jedes Farbschema je vier Farben. Dabei haben zwei von ihnen die gleichen Werte. Ferner benötigen wir bei der Erstellung anderer Bedienelemente möglicherweise mehr Farben.

Um einfach eine geeignete Farbe im Schema zu finden oder sich für eine neue Farbe zu entscheiden, enthält die Klasse eine Methode, die das Ansehen der Farben ermöglicht: die Methode Show() (Abb. 6). Es gibt auch die umgekehrte Methode Hide() zum Löschen von Farbbeispielen aus dem Diagramm.


Abb. 6 Ansehen von Farbschemata mithilfe der Methode Show()

Die Datei ColorSchemesView.mq5 wurde an den Beitrag angehängt. Dabei handelt es sich um einen Expert Advisor zum Betrachten von Farbschemata (ColorSchemesView.mq5).

Lassen Sie uns die Methode Init() in der Klasse CInputBox ein wenig anpassen. Ersetzen Sie ihre Farben mit den Farben aus der Klasse ClrScheme:

m_BgColor=ClrScheme.Color(0);       // Background color of the text field
m_TxtColor=ClrScheme.Color(1);      // Font color and frame color of the text field
m_LblColor=ClrScheme.Color(2);     // Caption color
m_WarningColor=ClrScheme.Color(3); // Warning color

Damit endet die Erstellung eines Bedienelements und wir verfügen nun über die Basis für die Entwicklung aller anderen Bedienelemente.


7. Nutzen des Bedienelements

Erstellen wir einen Expert Advisor und nennen ihn GUITest. Verbinden Sie die Datei IncGUI.mqh:  

#include <IncGUI.mqh>
Deklarieren Sie die Klasse CInputBox mit dem Namen ib:
CInputBox ib;

Rufen Sie in der OnInit()-Funktion des EA die Methode Init() des Objekts ib auf:

ib.Init("InpytBox",50,4,"input");

Machen Sie das Bedienelement sichtbar und legen Sie eine Position dafür fest:

ib.Show(10,20);

Löschen Sie das Bedienelement in der OnDeinit()-Funktion des EA:

ib.Hide(); 

Kompilieren Sie den Expert Advisor und hängen Sie ihn an ein Diagramm an. Sie sehen unser Bedienelement (Abb. 7).


Abb. 7 Das Bedienelement InputBox

Erweitern Sie den Expert Advisor um die Möglichkeit, das Farbschema zu ändern.

Jetzt haben wir drei Farbschemata. Erstellen wir eine Aufzählung und eine externe Variable für die Auswahl eines Farbschemas: 

enum eColorScheme
  {
   DefaultScheme=0,
   YellowBrownScheme=1,
   BlueScheme=2
  };

input eColorScheme ColorScheme=DefaultScheme;

Fügen Sie gleich am Anfang der OnInit()-Funktion des Expert Advisors die Einstellung eines Farbschemas hinzu:

ClrScheme.SetScheme(ColorScheme);

Nun können wir im Eigenschaften-Fenster des EA eines von drei Farbschemata auswählen (Abb. 8).




Abb. 8 Verschiedene Farbschemata

Fügen Sie nun für den folgenden Code zur OnChartEvent()-Funktion des EA hinzu, um einen neuen Wert festlegen zu können:

if(ib.Event(id,lparam,dparam,sparam)==1)
  {
   Alert("Entered value "+ib.ValueStrind());
  }

Wenn nun ein neuer Wert im Bearbeitungsfeld festgelegt wird, öffnet sich eine Meldung über den festgelegten Wert.

Ergänzen Sie den Expert Advisor um die Möglichkeit, Bedienelemente in einem Unterfenster zu erstellen.

Erstellen Sie als Erstes einen Testindikator mit dem Namen TestSubWindow (in der Datei TestSubWindow.mq5 angehängt). Geben Sie bei der Erstellung des Indikators im MQL5 Wizard an, dass er in einem separaten Unterfenster arbeiten soll. Fügen Sie den folgenden Code zur OnChartEvent()-Funktion des EA hinzu:

if(CHARTEVENT_CHART_CHANGE)
  {
   ip.SetSubWindow("TestSubWindow");
  }

Nun wird das Bedienelement im Preisdiagramm erstellt, wenn der Indikator sich nicht im Diagramm befindet. Wenn Sie den Indikator an das Diagramm anhängen, springt das Bedienelement ins Unterfenster (Abb. 9). Wenn Sie den Indikator löschen, kehrt das Bedienelement zum Preisdiagramm zurück.

 
Abb. 9 Das Bedienelement im Unterfenster

Fazit

Als Ergebnis unserer Arbeit haben wir die Include-Datei IncGUI.mqh, die die folgenden Klassen beinhaltet: CGraphicObjectShell (Erstellung und Verwaltung von grafischen Objekten), CWorkPiece (schnelle Erstellung von mehreren grafischen Objekten mit Festlegung ihrer Eigenschaften mithilfe von Parametern), CColorSchemes (Festlegung eines Farbschemas und Abrufen der Farbe des aktuellen Farbschemas) und eine Bedienelementklasse, CInputBox.

Die Klassen CGraphicObjectShell, CWorkPiece und CColorSchemes sind bereits mit den Namen "g", "w" und "ClrScheme" in der Datei deklariert, d. h., sie sind einsatzbereit, sobald die Datei IncGUI.mqh verbunden wird.

Sehen wir uns noch einmal an, wie die CInputBox-Klasse verwendet wird:

  1. Verbinden Sie die Datei IncGUI.mqh.
  2. Deklarieren Sie eine Klasse des Typen CInputBox.
  3. Rufen Sie die Methode Init() auf.
  4. Legen Sie die Koordinaten mithilfe der Methode SetPos() fest. Machen Sie das Bedienelement mit Show() sichtbar, falls erforderlich. Die zweite Variante: Machen Sie das Bedienelement durch Show() mit Angabe der Koordinaten sichtbar.
  5. Blenden Sie das Bedienelement falls erforderlich oder am Ende der Ausführung des Expert Advisors mithilfe der Methode Hide() aus.
  6. Fügen Sie den Aufruf der Methode Event() zur OnChartEvent()-Funktion hinzu.
  7. Falls Sie ein Bedienelement in einem Unterfenster erstellen müssen, ergänzen Sie die OnChartEvent()-Funktion um einen Aufruf der Methode SetSubWindow(), wenn ein CHARTEVENT_CHART_CHANGE-Ereignis eintritt.
  8. Um Farbschemata zu verwenden, rufen Sie die SetScheme()-Methode der ClrScheme-Klasse vor dem Aufruf der Init()-Methode auf. 


Anhänge

  • IncGUI.mqh – Haupt-Include-Datei. Die Datei sollte im Unterordner MQL5/Include des Datenordners des Client Terminals abgelegt werden.
  • GUITest.mq5 – der Expert Advisor mit einem Beispiel des Bedienelements CInputBox. Die Datei sollte im Unterordner MQL5/Experts des Datenordners des Client Terminals abgelegt werden.
  • TestSubWindow.mq5 – der Indikator zum Testen der Funktion zum Anzeigen eines Bedienelements in einem Unterfenster. Die Datei sollte im Unterordner MQL5/Indicators des Datenordners des Client Terminals abgelegt werden.
  • ColorSchemesView.mq5 – der Expert Advisor zum Ansehen der Farbschemata. Ein Hilfswerkzeug für die Erstellung von Bedienelementen. Die Datei sollte im Unterordner MQL5/Experts des Datenordners des Client Terminals abgelegt werden.
  • IncGUImqh.chm – Dokumentation für die Datei IncGUI.mqh.  

Übersetzt aus dem Russischen von MetaQuotes Ltd.
Originalartikel: https://www.mql5.com/ru/articles/310

Beigefügte Dateien |
Erstellen eines Automatischen Handelssystems Erstellen eines Automatischen Handelssystems
Sie müssen zugeben, dass es verlockend klingt - Sie werden stolzer Besitzer eines Programms, das für Sie innerhalb von Minuten ein profitables Automatisches Handelssystem (AHS) entwickeln kann. Alles was Sie machen müssen, ist die wünschenswerten Eingaben und die Eingabetaste drücken. Und - hier ist es, Sie testen Ihr AHS und erwarten Sie positive Auszahlungen. Während andere Menschen tausende Stunden investieren, um dieses einzigartige AHS zu entwickeln, das sie "fürstlich bedient", klingt diese Aussage, gelinde gesagt, ziemlich gewagt. Auf der einen Seite sieht dies ein wenig überlebensgroß aus... Allerdings, nach meiner Meinung, kann dieses Problem gelöst werden.
MQL5 Wizard: Neue Version MQL5 Wizard: Neue Version
Dieser Beitrag liefert Beschreibungen der neuen Features im aktualisierten MQL5 Wizard. Die modifizierte Architektur von Signalen ermöglicht die Erstellung von Handelsrobotern, die auf der Kombination verschiedener Marktmuster beruhen. Das Beispiel in diesem Beitrag erläutert das Verfahren zur interaktiven Erstellung eines Expert Advisors.
Haltepunkte im Tester: Es ist möglich! Haltepunkte im Tester: Es ist möglich!
Dieser Artikel befasst sich mit Haltepunkt-Emulation beim Durchlaufen des Testers, Debug-Informationen werden angezeigt.
Wie man ein Handelssignal zum Abonnieren richtig auswählt. Eine Schritt-für-Schritt-Anleitung Wie man ein Handelssignal zum Abonnieren richtig auswählt. Eine Schritt-für-Schritt-Anleitung
Die vorliegende Anleitung handelt sich um den Signale Service, Handelssignalen und eine umfassende Herangehensweise an die Auswahl eines Signals, das den Kriterien der Wirtschaftlichkeit, des Risikos, der Aktivität des Handels und der Arbeit auf unterschiedlichen Kontotypen und Symbolen entsprechen würde.