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

Benutzerdefinierte grafische Bedienelemente. Teil 3. Formen

MetaTrader 5Beispiele | 26 Januar 2016, 11:41
714 0
Dmitry Fedoseev
Dmitry Fedoseev

 

Einleitung

Der erste Beitrag "Erstellen eines einfachen Bedienelements" behandelte die Prinzipien für die Erstellung eines grafischen Bedienelements und lieferte ein Beispiel, das die Erstellung eines einfachen Bedienelements Schritt für Schritt illustrierte. Der zweite Beitrag "Bibliothek von Bedienelementen" lieferte eine Reihe von vorgefertigten Bedienelementen. Es gibt noch eine weitere sehr wichtige Komponente der grafischen Oberfläche: die Form.

Die Form stellt einen rechteckigen, speziell festgelegten Bereich der Anzeige dar, auf dem die Bedienelemente angezeigt werden. Zusätzlich dient die Form auch als Container, der die gleichzeitige Verwaltung aller Bedienelemente, die sie beinhaltet, ermöglicht, um sie alle gleichzeitig auszublenden, einzublenden und zu verschieben.

Der abschließende Beitrag behandelt die Erstellung von Formen und deren Gebrauch in Kombination mit den Bedienelementen.

Klassen für die Arbeit mit Formen wurden der Datei IncGUI.mqh hinzugefügt, die in den vorherigen Beiträgen verwendet wurde (der neue Dateiname lautet IncGUI_v3.mqh). Zusätzlich zu den Klassen für die Arbeit mit Formen wurden noch einige weitere Klassen der Datei hinzugefügt, die Klassen CHMenu (horizontales Menü) und CVMenu (vertikales Menü) wurden aktualisiert und einige Fehler beseitigt.

Hinzugefügte Klassen:

  • CFrame-Klasse – Rahmen. Diese Klasse ist identisch mit dem Rahmen, der durch die Methode Frame der CWorkPiece-Klasse erstellt wird.
  • CButton-Klasse – Button. Ein herkömmlicher Button (OBJ_BUTTON).
  • CLabel-Klasse – Beschriftung. Neben der Anzeige der Beschriftung wie durch das grafische Objekt "Beschriftung" (OBJ_LABEL) ermöglicht diese Klasse die Darstellung eines Texts in mehreren Zeilen, die durch "\n" getrennt werden.

In Verbindung mit den neuen Klassen wurde die Klasse CColorSchemes um Farben für die neuen Bedienelemente erweitert.

Änderungen in den Klassen CHMenu und CVMenu: Die Änderungen ermöglichen die Erstellung von Menüs mit zwei Ebenen mit Drop-Down-Registerkarten. Das sehen wir uns im Abschnitt "Erstellen des Hauptmenüs" näher an.

Fehler: eine Korrektur an der Frame()-Methode der CWorkPiece-Klasse: Eine Unterfensternummer für eine rechteckige Beschriftung konnte nicht festgelegt werden. CListMS-Klasse – Methoden Selected() und SetSelected() – die Array-Größe wird nun überprüft, da es ansonsten unmöglich war, eine leere Liste zu verwenden.

 

1. Formen

Die Form basiert auf dem grafischen Objekt "rechteckige Beschriftung" (OBJ_RECTANGLE_LABEL) mit etlichen Buttons (OBJ_BUTTON). Optisch besteht die Form aus einem Rechteck (Abb. 1) mit einer Leiste im oberen Teil der Form, in der der Name der Form und die Kontrollbuttons dargestellt werden.

Ein Button zum Verschieben der Form (mit einem Hand-Piktogramm) befindet sich links, Buttons zum Minimieren (Rechteck) und Schließen (Kreuz) befinden sich rechts.

Abb. 1 Form

Abb. 1. Form

Um die Form zu bewegen, klicken Sie auf den Verschieben-Button (der Button wechselt in die gedrückte Position) und klicken Sie auf eine beliebige Stelle im Diagramm, an der sich die Form befinden soll. Der Verschieben-Button wird losgelassen und die Form zur angegebenen Stelle verschoben (ihre linke obere Ecke befindet sich am angeklickten Punkt).

Falls der angegebene Ort am Rand des Diagramms ist und die Form außerhalb der Diagrammgrenze platziert werden muss, wird die Position der Form angepasst, sodass sie vollständig im Diagramm sichtbar ist.

Es können mehrere Arten von Formen erstellt werden:

  • Typ 0 (Abb. 1);
  • Typ 1 – mit zusätzlichen "Abbrechen"- und "Übernehmen"-Buttons (Abb. 2);
  • Typ 2 – mit zusätzlichem "Schließen"-Button (Abb. 3);

Abb. 2 Form des Typen 1 (mit "Abbrechen"- und "Übernehmen"-Buttons)

Abb. 2. Form des Typen 1 (mit "Abbrechen"- und "Übernehmen"-Buttons)

Abb. 3 Form des Typen 2 (mit "Schließen"-Button)

Abb. 3 Form des Typen 2 (mit "Schließen"-Button)

Programmmittel für die Arbeit mit den Formen werden von zwei Klassen dargestellt: CFormBase und CFormTemplate.

Die CFormBase-Klasse ist eine Basisklasse, während CFormTemplate eine Unterklasse der CFormBase-Klasse ist. Zweck und Prinzip (einschließlich des Anwendungsprinzips) von CFormBase ist genauso ein Bedienelement wie andere bereits beschriebene Bedienelemente.

CFormBase hat die Init()-Funktion zur Vorbereitung des Bedienelements, die Methoden SetPos() und Show() zum Anzeigen einer Form (Erstellung grafischer Objekte, die eine Form darstellen), die Hide()-Methode zum Ausblenden, die Refresh()-Methode zum Aktualisieren der Anzeige, die Event()-Methode zum Verarbeiten von Ereignissen und sonstige Methoden, die auch alle anderen Bedienelemente beinhalten.

Die Anwendung der Form und die Nutzung der Bedienelemente beginnt mit dem Aufruf der Init()-Funktion, gefolgt von der Festlegung einer Position mithilfe der SetPos()-Methode und der Aktivierung der Anzeige mithilfe der Show()-Methode. Ansonsten kann die Anzeige nur mithilfe der Show()-Methode mit Parametern aktiviert und eine Form mithilfe der Hide()-Methode ausgeblendet werden.

Ein Aufruf der Event()-Methode wird der OnChartEvent()-Funktion hinzugefügt, um die Möglichkeit, die Form zu kontrollieren (Verschieben, Minimieren, Schließen usw.), zu gewährleisten. Wo es erforderlich ist, um die Bedienung der Form in einem Unterfenster zu gewährleisten, wird die SetSubWindow()-Methode verwendet, die direkt nach dem Aufruf der Init()-Methode sowie in der OnChartEven()-Funktion nach dem CHARTEVENT_CHART_CHANGE-Ereignis aufgerufen wird. Alles ist genau so wie bei anderen Bedienelementen.

Während die Standard-Bedienelemente unabhängig sind (sie müssen nicht mit anderen Bedienelementen verbunden werden), impliziert die Form das Vorhandensein einer verpflichtenden Verbindung mit anderen Bedienelementen, um ihr gleichzeitiges Ein-/Ausblenden innerhalb der Form zusammen mit dem Ein-/Ausblenden der Form selbst zu gewährleisten. Deshalb sollte eine synchronisierte Auslösung der Init()-, Show()- und Hide()-Methoden der Form und ihrer Bedienelementen gewährleistet werden.

Sicher ist es möglich, einfach die Show()-Methoden aller mit der Form zusammenhängenden Bedienelemente aufzurufen (z. B. beim Aufruf der Show()-Methode der Form), aber wenn in einem Programm mehrere Formen genutzt werden, führt eine solche Herangehensweise zu einem Code, der unstrukturiert und unpraktisch zu verstehen, debuggen und aktualisieren ist.

Man kann eine Kopie der Formklasse für jede Form erstellen und funktionierenden Code mit Bedienelementen darin einfügen. Allerdings führt auch das zu einem unstrukturierten Code (der Code der Form ist nicht vom Code für die Arbeit der Bedienelemente getrennt) und übermäßiger Code-Duplizierung, wobei nur der Code dupliziert wird, der für Aussehen und Funktionalität der Form zuständig ist. All das macht Veränderungen an Funktionen der Form schwierig, da es voraussetzt, dass die Änderungen in jeder Klassenkopie vorgenommen werden.

Die Aufteilung in Basisklasse und Unterklasse hilft bei der Lösung des Problems mit der Erstellung mehrerer Formen, der Synchronisierung der Anzeige der Form und damit verbundener Bedienelemente und der klaren Trennung von Codes, die zu verschiedenen Formen gehören. Zudem ermöglicht die Verwendung von Klassen und Unterklassen zukünftige Erweiterungen der Basisklasse, ohne Änderungen an bereits erledigter Arbeit vornehmen zu müssen.

Um den Aufruf der Init()-, Show()- und Hide()-Methoden der Bedienelemente und der Form zu synchronisieren, unterscheidet sich die CFormBase-Klasse in einigen Punkten von der Standardklasse von Bedienelementen: einige virtuelle Methoden in einem protected-Bereich:

virtual void MainProperties() {}
virtual void OnInitEvent() {}
virtual void OnShowEvent() {}
virtual void OnHideEvent() {}
virtual void EventsHandler(const int id, const long& lparam, const double& dparam, const string& sparam){}
virtual void OnWindowChangeEvent(int aSubWindow) {}
virtual bool OnCancelEvent() {return(true);}
virtual bool OnApplyEvent()  {return(true);} 

Diese Methoden sind virtuell. Das bedeutet, dass die Weiterleitung zu den entsprechenden realen Methoden der CFormTemplate-Unterklasse über sie stattfindet.

Die CFormTemplate-Unterklasse selbst wird nicht genutzt, sie dient als Template. Um eine neue Form zu erstellen, erstellen Sie eine Kopie der CFormTemplate-Klasse mit einem eindeutigen Namen (kopieren Sie einfach den Code der CFormTemplate-Klasse, benennen Sie sie um und wiederholen Sie den Vorgang für jede Form). Das Kopieren von Unterklassen ermöglicht die Trennung von Code, der zu verschiedenen Formen gehört.

Virtuelle Methoden befinden sich im protected-Bereich und sind deshalb nicht für einen Aufruf zugängig. Das ist auch gar nicht erforderlich, da die Methoden bereits über die Basisklasse nach verschiedenen Form-Ereignissen aufgerufen werden. Sie müssen nur einen Code für die Funktion der Bedienelemente gemäß diesen Ereignissen einfügen.

Das Diagramm unten zeigt die Interaktion zwischen den Funktionen eines Expert Advisors und Methoden einer Klasse eines Standard-Bedienelements (Abb. 4) und zum Vergleich die Interaktion mit den Methoden der Formklasse (Abb. 5).

Abb. 4 Interaktion zwischen Funktionen eines Expert Advisors und den Methoden eines Bedienelements
Abb. 4. Interaktion zwischen Funktionen eines Expert Advisors und den Methoden eines Bedienelements

Abb. 5 Interaktion zwischen Funktionen eines Expert Advisors und den Methoden einer Formklasse
Abb. 5. Interaktion zwischen Funktionen eines Expert Advisors und den Methoden einer Formklasse

Gehen wir näher auf den Zweck der einzelnen Methoden ein:

  • Die Methoden MainProperties() und OnInitEvent() werden über die Init()-Methode der Form aufgerufen. Die Methode MainProperties() legt das Aussehen der Form fest (Formtyp, Vorhandensein der Buttons zum Verschieben, Minimieren und Schließen). Die Methode OnInitEvent() ruft die Init()-Methoden aller Bedienelemente auf und legt Standardwerte für die Bedienelemente fest.

  • Die Methode OnShowEvent() wird über die Show()-Methode aufgerufen. Der Aufrufcode der Show()-Methoden aller Bedienelemente wird hinzugefügt.

  • Die Methode OnHideEvent() wird über die Hide()-Methode aufgerufen. Der Aufrufcode der Hide()-Methoden aller Bedienelemente wird hinzugefügt.

  • Die Methode EventsHandler() entspricht der OnChartEvent()-Funktion des Expert Advisors. Die Event()-Methode der Form wird über die OnChartEvent()-Funktion des Expert Advisors aufgerufen und alle Diagrammereignisse werden einfach an diese Methode weitergeleitet. Die Aufrufcodes der Event()-Methoden aller Bedienelemente und die entsprechende Ereignisverarbeitung werden zu EventsHandler() hinzugefügt.

  • Die OnWindowChangeEvent()-Methode wird über die SetSubWindow()-Methode beim Ändern eines Unterfensters aufgerufen. Der Aufrufcode der SetSubWindow()-Methoden aller Bedienelemente wird hinzugefügt. Die Methode hat einen Parameter, die eine neue Unterfensternummer übermittelt, die in allen Bedienelementen festgelegt wird.

  • Die OnCancelEvent()-Methode wird beim Schließen der Form ausgeführt (beim Anklicken des Kreuz-Buttons, der "Abbrechen"- oder "Schließen"-Buttons). Beim Schließen der Form kann man einen Überprüfungscode einfügen, beispielsweise einen Aufruf für einen Bestätigungsdialog, um sicherzustellen, dass die Form wirklich geschlossen werden soll. Wenn die Methode true ausgibt, wird der Code zum Schließen der Form ausgeführt und die Form geschlossen. Wenn die Methode false ausgibt, wird die Ausführung des Codes, der die Form schließt, unterbrochen und die Form bleibt geöffnet.

  • Die OnApplyEvent()-Methode wird beim Klicken auf den "Übernehmen"-Button der Form ausgeführt. Wie auch bei der OnCancelEvent()-Methode kann man hier eine Überprüfung ausführen und sehen, ob die Methode true oder false ausgibt und somit das Schließen der Form erlaubt oder nicht. Diese Methode kann auch einen Code zum Speichern der Werte der Bedienelemente enthalten, sodass die Werte der Bedienelemente beim nächsten Start des Expert Advisors die gleichen sind wie vor dem Schließen des Programms. Zu diesem Zweck sollten die gespeicherten Werte in der OnInitEvent()-Methode geladen und den Bedienelementen zugewiesen werden.

 

2. Eine Schritt-für-Schritt-Anleitung zum Erstellen einer neuen Form

Hier finden Sie eine Schritt-für-Schritt-Anleitung zum Erstellen einer neuen Form:

1. Verbinden Sie die Datei IncGUI_v3.mqh.

#include <IncGUI_v3.mqh>
2. Kopieren Sie den Code der CFormTemplate-Klasse aus der Datei IncGUI_v3.mqh in eine Datei Ihres Expert Advisors (die Unterklasse befindet sich am Ende der Datei) und benennen Sie sie um (im Beispiel für die Verwendung der Bibliothek heißt die Unterklasse CForm). 

 

class CFormTemplate: public CFormBase{
   public:
   protected      void MainProperties()
     {
        m_Name        = "Form";       // Form name. The names of all the controls of this form should start with it.
        m_Width       = 200;           // Form width
        m_Height      = 150;           // Form height
        m_Type        = 1;             // Form type: 0 - without buttons, 1 - with "Apply" and "Cancel" buttons, 2 - with the "Close" button
        m_Caption     = "FormCaption"; // Form caption
        m_Movable     = true;          // Movable form (the button with a hand image is displayed in the top left corner)
        m_Resizable   =  true;         // Form minimizing/maximizing allowed (the button with a rectangle image 
                                           // is displayed in the top right corner)
        m_CloseButton = true;          // Form closing allowed (the button with a cross image is displayed in the top right corner)
     }
      void OnInitEvent() {} 
      void OnShowEvent(int aLeft, int aTop) {}
      void OnHideEvent() {}
      void OnWindowChangeEvent(int aSubWindow) {}
      void EventsHandler(const int id,const long& lparam,const double& dparam,const string& sparam){}
      bool OnApplyEvent()  {return(true);}
      bool OnCancelEvent() {return(true);}
};

Man kann eine separate Include-Datei für den Code der Form erstellen, diese im Expert Advisor verbinden, den Code der CFormTemplate-Unterklasse hineinkopieren und das verbleibende Coding in Bezug auf die Funktionsweise der Form in dieser Datei durchführen.

Zieht man einen Vergleich mit dem Gebrauch eines visuellen Editors, wäre dieser Schritt dem Anklicken des Buttons zum Erstellen einer neuen Form, nach dem eine neue Formdatei mit Funktionen unterschiedlicher Formereignisse im Projekt erscheinen würde, sehr ähnlich.

3. Legen Sie die wichtigsten Eigenschaften der Form fest. Dieser Schritt wird in der MainProperties()-Methode durch Zuweisung von Werten an Variablen, die in der Basisklasse deklariert sind, ausgeführt. Detaillierte Bemerkungen zu den Zwecken aller Variablen finden Sie im Template. Sie könnten die Ähnlichkeit dieses Schritts mit der Verwendung des Formeigenschaften-Fensters in einem visuellen Editor bemerken (zum Beispiel VBA in Excel).

void MainProperties()
{
   m_Name         =  "Form";        // Form name. The names of all the controls of this form should start with this one.
   m_Width        =  200;           // Form width
   m_Height       =  150;           // Form height
   m_Type         =  1;             // Form type: 0 - without buttons, 1 - with "Apply" and "Cancel" buttons, 2 - with the "Close" button
   m_Caption      =  "FormCaption";   // Form caption
   m_Movable      =  true;          // Movable form (the button with a hand image is displayed in the top left corner)
   m_Resizable    =  true;          // Form minimizing/maximizing allowed (the button with a rectangle image 
                                    // is displayed in the top right corner)
   m_CloseButton = true;            // Form closing allowed (the button with a cross image is displayed in the top right corner)
}

Man kann noch einen weiteren Unterschied zwischen der Form und den restlichen Bedienelementen feststellen: Der Name der Form ist in der MainProperties()-Methode festgelegt, anstatt als Parameter der Init()-Methode übergeben zu werden.

Die Hauptarbeit der Form wird in einer Unterklasse durchgeführt. Jede Unterklasse der Form wird nur einmal deklariert, deshalb müssen beim Aufruf der Init()-Methode einer Unterklasse der Form keine unterschiedlichen Namen angegeben werden.

Der einzige Parameter, der an die Init()-Methode übertragen werden kann, ist der Parameter State, aber selbst dieser Parameter ist nicht verpflichtend. Der Wert des State-Parameters bestimmt den anfänglichen Status der Form, wenn sie angezeigt wird: maximiert oder minimiert. Ist der Wert 1, ist die Form maximiert (Abb. 1, 2, 3). Ist der Wert 2, ist die Form minimiert (Abb. 6).

Abb. 6 Minimierte Form

Abb. 6. Minimierte Form

4. Deklarieren Sie eine Klasse.

CForm frm;

5. Führen Sie Standardschritte zur Verwendung des Bedienelements durch: Rufen Sie die Init()-Methode der Form aus der OnInit()-Funktion des Expert Advisors auf, rufen Sie die Deinit()-Methode aus der OnDeinit()-Funktion auf, rufen Sie die Event()-Methode aus der OnChartEvent()-Funktion auf und, falls erforderlich, rufen Sie die SetSubWindow()-Methode nach dem CHARTEVENT_CHART_CHANGE-Ereignis auf.

Die Form sollte daraufhin im Diagramm sichtbar sein und auf Anklicken der Buttons zum Verschieben, Minimieren, Schließen usw. reagieren.

Anschließend fahren wir fort, indem wir die Bedienelemente zur Form hinzufügen.

 

3. Eine Schritt-für-Schritt-Anleitung zum Hinzufügen der Bedienelemente zur Form

  1. Deklarieren der Bedienelemente. Wenn Sie den Zugriff auf die Methoden der Bedienelemente außerhalb der Form gewährleisten müssen, sollten die Klassen der Bedienelemente im public-Bereich deklariert werden. Wenn alle Arbeiten innerhalb der Formklasse erledigt werden können, sollten die Klassen der Bedienelemente im protected-Bereich deklariert werden.

  2. Deklarieren der Variablen zum Wiederherstellen der Werte der Bedienelemente. Ein optionaler Schritt. Wenn Sie die Form mit "Abbrechen"- und "Übernehmen"-Buttons nutzen wollen, sollte jedem Bedienelement eine Variable zum Speichern des Wertes hinzugefügt werden, den das Bedienelement beim Öffnen der Form hatte, um den Wert wiederherzustellen, falls ein Benutzer den Wert des Bedienelements ändert, aber auf "Abbrechen" klickt.

  3. Aufrufen der Int()-Methoden aller Bedienelemente. Wird in der OnInitEvent()-Methode ausgeführt.

  4. Laden der gespeicherten Daten. Ein optionaler Schritt. Wird in der OnInitEvent()-Methode ausgeführt. Dieser Schritt wird ausgeführt, wenn es erforderlich ist, dass die Bedienelemente nach einem Neustart des Expert Advisors, Terminals oder Computers ihre Werte behalten. Das Speichern der Daten wird in Schritt 12 ausgeführt.

  5. Festlegen der Standardwerte. In diesem Schritt werden allen Bedienelementen Werte zugewiesen, die beim ersten Öffnen der Form nach dem Start des Expert Advisors sichtbar sein werden (bis ihre Werte durch einen Benutzer verändert werden). Wenn in Schritt 4 Daten geladen wurden, werden die geladenen Daten verwendet. Weitere vorbereitende Schritte für die Bedienelemente werden ebenfalls in diesem Schritt ausgeführt, zum Beispiel werden hier auch Listen- und Menüeinträge hinzugefügt.

  6. Das Aufrufen der Show()-Methoden (Version mit Parametern) für alle Bedienelemente wird in der OnShowEvent()-Methode ausgeführt. Die OnShowEvent()-Methode hat zwei Parameter (int aLeft, int aTop), die die Koordinaten der oberen linken Ecke des Arbeitsbereichs der Form übermitteln. Die Positionen der Bedienelemente werden abhängig von den Werten dieser Variablen festgelegt (es dürfen keine Werte hinzugefügt werden).

  7. Ein Aufruf der Hide()-Methode für alle Bedienelemente der Form wird in der OnHideEvent()-Methode ausgeführt.

  8. Aufrufen der SetSubWindow()-Methode. Ein optionaler Schritt. Falls es erforderlich ist, die Form in einem Unterfenster anzuzeigen, wird die SetSubWindow()-Methode für alle Bedienelemente aufgerufen. Sie wird in der OnWindowChangeEvent()-Methode ausgeführt, die einen Parameter hat (int aSubWindow), der eine neue Unterfensternummer übermittelt.

  9. Aufrufen der Event()-Methoden aller Bedienelemente. Dies wird in der EventsHandler()-Methode ausgeführt.

  10. Verarbeiten von Ereignissen der Bedienelemente. Ein optionaler Schritt. Falls es erforderlich ist, die Interaktion zwischen den Bedienelementen nach ihren Ereignissen zu gewährleisten, werden die Ereignisse in diesem Schritt verarbeitet und geeignete Aktionen ausgeführt. Dies wird in der EventsHandler()-Methode ausgeführt.

  11. Anwenden neuer Werte. Ein optionaler Schritt, der durch Anklicken des "Übernehmen"-Buttons ausgeführt wird. Er wird in der OnApplyEvent()-Methode ausgeführt. In diesem Schritt wird die Genauigkeit der Werte der Bedienelemente überprüft. Falls falsche Werte entdeckt werden, muss der Benutzer benachrichtigt werden. Die Methode muss in diesem Fall false ausgeben und die Form geöffnet bleiben. Wenn die Werte korrekt sind, wird true ausgegeben und die Form geschlossen.

  12. Speichern von Daten. Ein optionaler Schritt. Er wird in der OnApplyEvent()-Methode durch Anklicken des "Übernehmen"-Buttons ausgeführt. Daten können (abhängig von ihren Zielen und Zwecken) in einer Datei, globalen Variablen, unsichtbaren Objekten im Diagramm usw. gespeichert werden.

  13. Überprüfen in der OnCancelEvent()-Methode. Ein optionaler Schritt. Falls von der OnCancelEvent()-Methode false ausgegeben wird, bleibt die Form geöffnet, d. h. der Schließen-Button reagiert nicht. Damit die Form geschlossen wird, muss die Methode true ausgeben.

Ein Beispiel für die Arbeit mit Formen finden Sie in der angehängten Datei eIncGUI_v3_Test_Form.mq5. Die Hauptform mit unterschiedlichen Bedienelementen wird beim Start geöffnet (Abb. 7).

Beachten Sie, dass die Form keinen Schließen-Button hat. Das ist die Hauptform und darf nicht aus dem Diagramm verschwinden. Andernfalls muss der Expert Advisor neu gestartet werden, damit die Form wieder erscheint.

Abb. 7 Hauptform aus dem Beispiel eIncGUI_v3_Test_Form.mq5 mit geöffneter Hauptmenü-Registerkarte

Abb. 7. Hauptform aus dem Beispiel eIncGUI_v3_Test_Form.mq5 mit geöffneter Hauptmenü-Registerkarte

Über die zwei Befehle im Hauptmenü können Sie zwei weitere Varianten der Form öffnen.

Hauptmenü - Formtypen - Typ 1 öffnet eine Form des Typen 1 (mit "Abbrechen"- und "Übernehmen"-Buttons).
Hauptmenü - Formtypen - Typ 2 öffnet eine Form des Typen 2 (mit "Schließen"-Button).

Beide Formen benötigen eine Bestätigung ihrer Schließung (mithilfe der MessageBox()-Funktion). Die Form des Typen 1 hat ein Textfeld. Eingegebene Werte werden durch Klicken auf den "Übernehmen"-Button geprüft. Die Form des Typen 2 hat das neue Bedienelement "Beschriftung" (CLabel-Klasse) und ein paar Buttons (CButton-Klasse), um dieses zu prüfen.

Lassen Sie uns nun, wie in der Einleitung erwähnt, die Änderungen in den CHMenu- und CVMenu-Klassen ansehen, verdeutlicht durch die Erstellung des Hauptmenüs.

 

Zuallererst möchte ich erwähnen, dass nur Menüs mit zwei Ebenen erstellt werden können: eine horizontale Leiste (basierend auf der CHMenu-Klasse) und Registerkarten (basierend auf der CVMenu-Klasse). Prinzipiell können noch mehr Ebenen erstellt werden, doch der Prozess wäre sehr kompliziert und aufwändig. Deshalb wird dies nicht empfohlen und nicht weiter berücksichtigt.

Tatsächlich war die Erstellung des Hauptmenüs für die Form nicht einmal vorgesehen und die CHMenu- und CVMenu-Klassen sollten einzeln und unabhängig voneinander verwendet werden, um unterschiedliche Funktionen und Instrumente zu aktivieren/deaktivieren. Allerdings machte es eine kleine Verbesserung der CHMenu- und CVMenu-Klassen möglich, ein Hauptmenü umzusetzen, das für die meisten Fälle geeignet ist.

Um ein vollständiges mehrstufiges Menü zu erstellen, muss ein etwas anderer Ansatz verfolgt werden (Erstellung einer baumartigen Datenstruktur), was einen eigenen Beitrag wert wäre, weshalb wir uns auf die vorhandenen Möglichkeiten beschränken.

Sehen wir uns zunächst die Veränderungen und Korrekturen an den CHMenu- und CVMenu-Klassen an.

In der CVMenu-Klasse wurde eine Reaktion auf Anklicken des grafischen Objekts "Beschriftung" repariert, die genutzt wird, um das Tick-Symbol anzuzeigen. Dementsprechend gab es Änderungen an der Funktionsweise der Methoden LastClickedX(), LastClickedY() und LastClickedQuarter(). Beim Anklicken der Beschriftungen werden nun Werte ausgegeben, wie es bei Textfeldern der Fall ist. Ähnliche Korrekturen wurden an der CHMenu-Klasse und den Methoden LastClickedX(), LastClickedY(), LastClickedQuarter() und LastClickedW() vorgenommen.

Beide Klassen wurden durch Hinzufügen der Methoden SolvePosX() und SolvePosY() aktualisiert, die der Berechnung der Koordinaten eines grafischen Objekts, das beim Menübefehl angezeigt wird, dienen. Die Breite des angezeigten Objekts wird an die Methode SolvePosX() und die Höhe des Objekts an die Methode SolvePosY() übertragen, die jeweils die X- bzw. Y-Koordinate des angezeigten Objekts ausgeben. Jetzt müssen die Methoden LastClickedX(), LastClickedY(), LastClickedQuarter() und LastClickedW() nicht mehr verwendet werden.

Beide Klassen wurden durch Hinzufügen der Methoden LastClickedName1() und LastClickedName2() aktualisiert. Diese Methoden geben die Namen von grafischen Objekten aus, die den letzten angeklickten Menüeintrag bilden. LastClickedName1() gibt den Namen des Textfelds aus, LastClickedName2() den Namen der Beschriftung für das Tick-Symbol.

Die Methoden ToggleNameAdd() und ToggleNamesClear() wurden CVMenu hinzugefügt. Die ToggleNameAdd()-Methode wird verwendet, um eine Liste von "umschaltenden" Namen zu erstellen, ToggleNamesClear() wird verwendet, um diese Liste zu löschen. Bei der Verwendung der Methode ToggleNameAdd() (Hinzufügen eines Namen zur Liste) schließt das Menü automatisch nach jedem Diagrammereignis mit Ausnahme von Ereignissen der grafischen Objekte, die das Menü bilden, und der grafischen Objekte, deren Namen durch die Methode ToggleNameAdd() hinzugefügt wurden. Das Menü kehrt in seinen normalen Arbeitsmodus zurück, wenn die Liste durch die Methode ToggleNamesClear() gelöscht wird.

Die Funktionsweise des Zwei-Ebenen-Menüs ist folgende: Wenn das Ereignis des Anklickens des horizontalen Menüeintrags durch die Methoden LastClickedName1() und LastClickedName2() definiert ist, erhalten wir die Namen der Objekte dieses Eintrags, die weiter an die Methode ToggleNameAdd() des vertikalen Menüs übertragen werden.

Daraufhin wird das vertikale Menü nach jedem Diagrammereignis ausgeblendet mit Ausnahme von Ereignissen des vertikalen Menüs und eines Eintrags des horizontalen Menüs (durch den das vertikale Menü geöffnet wurde).

Hat ein Benutzer einen Eintrag des vertikalen Menüs ausgewählt, sollten das vertikale Menü geschlossen und die Aktionen, die diesem Eintrag entsprechen, ausgeführt werden. Hat ein Benutzer wiederholt auf denselben Eintrag des horizontalen Menüs geklickt (durch den das vertikale Menü geöffnet wurde), sollte das vertikale Menü ausgeblendet werden.

Sehen wir uns ein Beispiel für die Erstellung des Menüs an.

Das Hauptmenü hat drei Einträge. Deshalb brauchen wir drei vertikale Menüs. Deklarieren Sie eine horizontale Menüklasse und drei vertikale Menüklassen im public-Bereich der Form-Unterklasse (das Beispiel unten zeigt es auf exakt die gleiche Weise wie im Beispiel in eIncGUI_v3_Test_Form.mq5):

class CForm: public CFormBase{
public:
   CHMenu m_hm;
   CVMenu m_vm1;
   CVMenu m_vm2;
   CVMenu m_vm3; 

Initialisieren Sie die Menüklassen in der OnInitEvent()-Methode und fügen Sie Menüeinträge hinzu:

// Horizontal menu
m_hm.Init(m_Name+"_HM",m_Width,2);
// Adding horizontal menu items
m_hm.AddItem("Form types");
m_hm.AddItem("Item-2");
m_hm.AddItem("Item-3");
// Vertical menu 1
m_vm1.Init(m_Name+"_VM1",70,10); 
// Adding items to vertical menu 1
m_vm1.AddItem("Type-1");
m_vm1.AddItem("Type-2");
// Vertical menu 2
m_vm2.Init(m_Name+"_VM2",70,3);
// Adding items to vertical menu 2
m_vm2.AddItem("Item-2-1");
m_vm2.AddItem("Item-2-2");
m_vm2.AddItem("Item-2-3"); 
m_vm2.AddItem("Item-2-4");
m_vm2.AddItem("Item-2-5"); 
// Vertical menu 3
m_vm3.Init(m_Name+"_VM3",70,3);
// Adding items to vertical menu 3
m_vm3.AddItem("Item-3-1");
m_vm3.AddItem("Item-3-2");
m_vm3.AddItem("Item-3-3"); 
m_vm3.AddItem("Item-3-4");
m_vm3.AddItem("Item-3-5"); 

Blenden Sie das Menü in der OnHideEvent()-Methode aus:

void OnHideEvent()
{
   m_hm.Hide(); 
   m_vm1.Hide(); 
   m_vm2.Hide(); 
   m_vm3.Hide(); 
}

Zeigen sie das horizontale Menü in der OnShowEvent()-Methode an:

void OnShowEvent(int aLeft,int aTop)
{
    m_hm.Show(aLeft,aTop); 
}

Letztendlich die Hauptarbeit in der EventsHandler()-Methode.

Rufen Sie die Event()-Methoden aller Menüs auf:

void EventsHandler(const int id,const long& lparam,const double& dparam,const string& sparam)
{
    int m_event0=m_hm.Event(id,lparam,dparam,sparam);
    int m_event1=m_vm1.Event(id,lparam,dparam,sparam);
    int m_event2=m_vm2.Event(id,lparam,dparam,sparam);
    int m_event3=m_vm3.Event(id,lparam,dparam,sparam); 

Arbeiten Sie, abhängig vom Wert m_event0 (Index des Eintrags des horizontalen Menüs), im jeweiligen vertikalen Menü (z. B. mit Eintrag 0 und vertikalem Menü 1):

if(m_event0==0)
{ // Clicking item 0
   if(m_vm1.Visible())
   { 
      m_vm1.Hide(); // If the vertical menu is open, close it
   }
   else{ // If the vertical menu is closed
      m_vm1.ToggleNamesClear(); // Clear the list of "toggle" names
      // Add to the list the names of the graphical objects forming the item 
      // of the horizontal menu which led to the last event 
      // of the horizontal menu 
      m_vm1.ToggleNameAdd(m_hm.LastClickedName1()); 
      m_vm1.ToggleNameAdd(m_hm.LastClickedName2());
      // Display the vertical menu
      m_vm1.Show(m_hm.SolvePosLeft(m_vm1.Width()),m_hm.SolvePosTop(m_vm1.Height())); 
   }
}

Ähnliche Aktionen sollten für alle Einträge des horizontalen Menüs, die das vertikale Menü öffnen, ausgeführt werden. Wenn das Ereignis des vertikalen Menüs aufgetreten ist, schließen Sie das Menü.

Verfahren Sie je nach Index des angeklickten Eintrags folgendermaßen:

if(m_event1>=0)
{ // Vertical menu event 1
   m_vm1.Hide(); // Hide the menu
      if(m_event1==0)
      { // Clicking item 0
        //...
      }
      if(m_event1==1)
      { // Clicking item 1
        //...
      }
}

Ähnliche Aktionen sollten für alle vertikalen Menüs und alle ihre Einträge ausgeführt werden.

Die Erstellung des Hauptmenüs ist jetzt abgeschlossen.

 

Fazit

Dieser Beitrag beschließt die Beitragsserie zur Erstellung einer grafischen Oberfläche.

Als Ergebnis unserer Arbeit verfügen wir über ein beträchtliches Arsenal an Möglichkeiten für die schnelle Erstellung einer multifunktionalen und benutzerfreundlichen grafischen Oberfläche. Das entspricht weitestgehend den aktuellen Anforderungen an eine grafische Oberfläche:

  • Verwendung eines Grundgerüsts in Form einer Beschriftung und einer Form, die minimiert/maximiert/verschoben werden können;
  • Klar definierte Codebereiche, die sich auf eine bestimmte Form beziehen;
  • Ein-/Ausblenden der Bedienelemente ist mit dem Ein-/Ausblenden der Form synchronisiert.

Alle Bedienelemente und die Form haben ein gemeinsames Design und Farbschema, die schnell verändert werden können.

Wiederholen wir das Verfahren zum Erstellen einer Form.

Schnellverfahren zum Erstellen einer Form:

  1. Verbinden Sie die Datei IncGUI_v3.mqh.

  2. Erstellen Sie eine Kopie der CFormTemplate-Klasse mit einem eindeutigen Namen und deklarieren Sie diese Klasse.

  3. Legen Sie die Eigenschaften der Form in der MainProperties()-Methode der Unterklassenkopie fest.

  4. Rufen Sie die Init()-, Hide()- und Event()-Methoden der Form jeweils aus den OnInit()-, OnDeinit()- bzw. OnChartEvent()-Funktionen des Expert Advisors auf. Rufen Sie die Show()-Methode aus der OnInit()-Funktion des Expert Advisors oder nach Bedarf auf.

  5. Arbeiten Sie mit den Bedienelementen. Deklarieren Sie die Klassen der Bedienelemente in der Unterklassenkopie. Rufen Sie die Init()-, Show()-, Hide()- und Event()-Methoden der Bedienelemente aus den OnInitEvent()-, OnShowEvent()-, OnHideEvent()- und EventHandler()-Methoden der Unterklassenkopie auf.

 

Anhang

Liste angehängter Dateien:

  • IncGUI_v3.mqh – Include-Datei mit allen Klassen zur Erstellung einer grafischen Oberfläche. Die Datei sollte im Unterordner MQL5/Include des Datenordners des Terminals abgelegt werden.
  • eIncGUI_v3_Test_Form.mq5 – Beispiel für die Arbeit mit Formen. Die Datei sollte im Unterordner MQL5/Experts des Datenordners des Terminals abgelegt werden.
  • IncGUIv3mqh.chm – Dokumentation zur Datei IncGUI_v3.mqh.

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

Beigefügte Dateien |
Test Visualisierung: Handelshistorie Test Visualisierung: Handelshistorie
Beginnend mit Build 196 bietet das MetaTrader 4 Client Terminal eine Test-Visualisierungsfunktion. Sie ermöglicht das Kontrollieren von Expert Advisor Tests auf einem völlig neuen Niveau. Nun kann der Trading-Programmierer jede Aktion von seinem oder ihrem Expert Advisor beobachten, durch prüfen seiner Operationen in der Historie!
Benutzerdefinierte grafische Bedienelemente. Teil 2. Bibliothek von Bedienelementen Benutzerdefinierte grafische Bedienelemente. Teil 2. Bibliothek von Bedienelementen
Der zweite Beitrag der Serie "Benutzerdefinierte grafische Bedienelemente" führt eine Bibliothek von Bedienelementen für den Umgang mit den wichtigsten Problemen ein, die bei der Interaktion zwischen einem Programm (Expert Advisor, Script, Indikator) und einem Benutzer auftreten. Die Bibliothek enthält zahlreiche Klassen (CInputBox, CSpinInputBox, CCheckBox, CRadioGroup, CVSсrollBar, CHSсrollBar, CList, CListMS, CComBox, CHMenu, CVMenu, CHProgress, CDialer, CDialerInputBox, CTable) und Beispiele für ihren Gebrauch.
Schnellere Berechnungen mit dem MQL5 Cloud Network Schnellere Berechnungen mit dem MQL5 Cloud Network
Wie viele Kerne hat Ihr Computer zu Hause? Wie viele Computer können Sie zur Optimierung einer Handelsstrategie nutzen? Hier zeigen wir Ihnen, wie Sie das MQL5 Cloud Network nutzen können, um Berechnungen zu beschleunigen, indem Sie mit einem einzigen Mausklick Zugriff auf Rechenleistung aus aller Welt erhalten. Die Phrase "Zeit ist Geld" wird von Jahr zu Jahr relevanter und wir können es uns nicht leisten, etliche Stunden oder Tage auf wichtige Berechnungen zu warten.
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.