MetaTrader 5 herunterladen

Grafische Interfaces II: Das Menu-Item-Element (Kapitel 1)

28 Juni 2016, 14:31
Anatoli Kazharski
0
306

Inhalt


Einleitung

In dem ersten Kapitel dieser Serie, haben wir den vollständigen Prozess der Entwicklung der Hauptstruktur der Bibliothek für die Erzeugung von grafischen Interfaces besprochen. Hier haben wir auch das Hauptelement der interfaces erzeugt, ein Formular mit Controls. Der erste Artikel: Graphische Interfaces I: Vorbereiten der Bibliotheksstruktur (Kapitel 1) Beschreibt im Detail wofür diese Bibliothek gedacht ist. Eine vollständige Liste mit Links zu diesem Artikel 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.

Einige Klassen sind zu diesem Zeitpunkt der Entwicklung noch nicht vollständig. Da die Bibliothek um einigen neue Controls bereichert wurde, müssen noch einige Ergänzungen inm der CWndContainer Klasse vorgenommen werden, welche die Basis für das Abspeichern von Pointern zu Elementen und Objekten darstellt. Zudem muss die CWndEvents Klasse erweitert werden. Dieses müssen wir machen, da hier die Events der Controls verarbeitet werden.

Die aktuelle Version der CWindow Formularklasse für die Controls ist ebenso noch nicht fertig. Der Multi-Fenstermodus, den wir in den vorherigen Artikeln angesprochen haben, wurde bisher noch nicht implementiert. Wir werden diesen Modus in dem vierten Teil dieser Serie besprechen. Hier werden wir dann auch die Erzeugung eines Kontextmenüs demonstrieren. Dieses ist nicht nur ein Teil des Hauptmenüs, es kann auch für andere Controls verwendet werden, welche wir in weiteren Artikeln dieser Serie besprechen werden.

Wir empfehlen, alle hier vorgestellten Aktionen vollständig nachzuvollziehen, damit sie dieses Material besser verstehen können. Programmcode von Methoden, der schon in vorherigen Methoden und Klassen vorgekommen ist, wird hier ausgelassen um den Artikel nicht zu groß werden zu lassen. Wenn sie auf solche Methoden treffen, können Sie sich den entsprechenden Programmcodes aus den angehängten Dateien zu diesem Artikel heraussuchen und diesen dann weiter studieren.


Das Hauptmenü des Programms

Es gibt kaum ein Programm, welches kein Hauptmenü besitzt. Die MetaTrader Terminals besitzen ebenfalls ein solches Interface-Element (Wie sie im nachfolgenden Screenshot sehen können). Normalerweise befindet sich das Hauptmenü in der linken oberen Ecke eines Programms und besteht aus einigen Punkten. Ein Klick mit der linken Maustaste öffnet ein Dropdown Menü mit weiteren Optionen.

Abbildung  1. Und das Hauptmenü im MetaTrader 5 Terminal

Abbildung 1. Und das Hauptmenü im MetaTrader 5 Terminal

Ein solches Dropdownmenü wird auch als Kontextmenü bezeichnet und kann verschiedene Arten von weiteren Punkten enthalten. Lassen Sie uns diese im Detail anschauen:

  • Einen Button. Dieses gehört zu den einfachsten Elementen in einem Kontextmenü. Normalerweise öffnet ein Klick mit der linken Maustaste ein Fenster mit weiteren Funktionalitäten für die Einstellungen des Programms oder ein Fenster mit weiteren Informationen. Es kann hier auch ganz einfache Funktionen geben. Zum Beispiel kann sich auch einfach nur das Erscheinungsbild des Programms ändern, nachdem einer dieser Button angeklickt wurde.
  • Ein Element mit zwei Zuständen vom Typ Checkbox. Ein solches Element kann dazu verwendet werden, einen Prozess zu aktivieren oder ein Teil des Programms zu öffnen, beziehungsweise sichtbar zu machen. In so einem Fall ändert dieses Element sein Erscheinungsbild und zeigt damit dem Anwender, in welchem Zustand es sich befindet.
  • Eine Gruppe von Elementen. In solch einer Gruppe kann immer nur ein Element aktiviert werden. Ein solcher Typ eines Controls wird auch Radiobutton oder Schalter genannt. In diesem Artikel werden wir es ein Radio-Element (Button) nennen.
  • Ein Element für den Aufruf eines Kontextmenüs. Ein Kontextmenü, welches von dem Hauptprogramm aufgerufen wurde, kann Elemente von einem oder mehreren weiteren Kontextmenüs enthalten. Nachdem ein solches Element angeklickt wurde, erscheint auf der rechten Seite ein Kontextmenü.

Der MetaEditor Programmeditor, besitzt ebenfalls solche Menüs:

Abbildung  2. Hauptmenü in dem MetaEditor Code-Editor.

Abbildung 2. Hauptmenü in dem MetaEditor Code-Editor.

Jetzt müssen wir herausfinden, welche Klassen für die Zusammenstellung eines solchen komplexen Interfaces benötigt werden. Es ist offensichtlich, dass man nicht alle Eigenschaften in einer Klasse zusammenfassen sollte, da dann diese Klasse unübersichtlich wird und sie sehr schwer zu studieren ist. Daher macht es Sinn alles so zu realisieren, dass der gesamte Programmcode aus einfachen Teilen zusammengesetzt wird. Lassen Sie uns nun entscheiden, welche Teile dieses sein können.

Das Hauptmenü und die Kontextmenüs bestehen aus unterschiedlichen Elementen. Die selbe Klasse dieser Elemente kann in beiden Menü-Arten verwendet werden. Ein Kontextmenü besitzt auch sehr häufig eine Trennlinie, welche dazu dient, die verschiedenen Kategorien des Menüs leichter unterscheiden zu können. Daher können wir nun absehen, dass wir bisher mindestens vier Klassen für die Erzeugung dieser Interface Elemente benötigen:

  1. Ein Menüelement. Um dieses Element erzeugen zu können, entwickeln wir die CMenuItem Klasse.
  2. Die CSeparateLine Klasse Wird für die Erzeugung von Trennlinien entwickelt.
  3. Ein Kontextmenü. Die CContextMenu Klasse. Diese Interface-Elemente, werden aus den Objekten der CMenuItem Klasse zusammengesetzt.
  4. Das Hauptmenü. Die CMenuBar Klasse. Wie auch in den Kontextmenü, besteht das Hauptmenü aus Elementen vom Typ(CMenuItem).

Wir haben nun unsere Aufgaben definiert. Wie oben schon besprochen, ist es klar, dass dr wichtigste Part für die Erzeugung eines Haupt und eines Kontextmenü das Menüelement ist. Daher werden wir hier mit der Erzeugung der CMenuItem Klasse fortfahren.


Entwicklung einer Klasse für die Erzeugung eines Menüpunktes

In dem Controls Verzeichnis, wo sich auch alle anderen Dateien dieser Bibliothek befinden, erzeugen Sie die MenuItem.mqh Datei mit der abgeleiteten Klasse CMenuItem. Sie können hier sofort die Standardmethoden für alle Controls deklarieren. Die Basisklasse hierfür ist die CElement Klasse, welche wir im Detail in dem vorherigen Artikel besprochen haben. Wir implementieren diesen Methoden hier noch nicht, da wir später in dem Artikel ein besonderes Highlight dieses Compilers aufzeigen wollen.

//+------------------------------------------------------------------+
//|                                                     MenuItem.mqh |
//|                        Copyright 2015, MetaQuotes Software Corp. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
#include "Element.mqh"
//+------------------------------------------------------------------+
//| Die Klasse für das Erzeugen eines Menüelementes                  |
//+------------------------------------------------------------------+
class CMenuItem : public CElement
  {
   //---
public:
                     CMenuItem(void);
                    ~CMenuItem(void);  
   //---
public:
   //--- Chart Eventhandler
   virtual void      OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam);
   //--- Timer
   virtual void      OnEventTimer(void);
   //--- Verschieben des Elementes
   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                                                      |
//+------------------------------------------------------------------+
CMenuItem::CMenuItem(void)
  {
  }
//+------------------------------------------------------------------+
//| Destruktor                                                       |
//+------------------------------------------------------------------+
CMenuItem::~CMenuItem(void)
  {
  }  
//+------------------------------------------------------------------+

Welche grafischen Objekte benötigen wir jetzt um das Element zusammenzustellen? In dem Hauptmenü, sind dieses normalerweise Beschriftungen, welche ihre Farbe oder ihre Schrift ändern, sobald sich der Mauszeiger über ihnen befindet. Ein Kontextmenü besitzt normalerweise zudem ein Icon. Wenn ein Element zu dem noch ein Kontextmenü besitzt, dann gibt es an der rechten Seite dieses Elementes einen Pfeil der nach rechts zeigt, was darauf hinweist, dass es hier noch ein weiteres angefügtes Menü gibt. Das bedeutet, dass je nachdem was dieses Menü-Element für eine Funktion hat, dieses aus unterschiedlichen Elementen bestehen kann. Lassen Sie uns an dieser Stelle alle möglichen Bestandteile aufzählen:

  1. Hintergrund
  2. Label.
  3. Beschriftung.
  4. Hinweis auf die Anwesenheit eines Kontextmenüs.

Abbildung 3. Bestandteile des Menü-Element-Controls.

Abbildung 3. Bestandteile des Menü-Element-Controls.

Wir fügen Instanzen von Klassen von grafischen Objekten zu, welche in der Element.mqh Datei verfügbar sind, und wir fügen in der CMenuItem Klasse Deklarationen von Methoden für die Erzeugung von grafischen Objekten hinzu. In dem Moment, wo ein Menüelement dem Chart hinzugefügt wird, muss die Indexnummer des Menüelementes dieser Methode übergeben werden. Diese Nummer wird für die Bildung des Namens des grafischen Objektes benötigt, welcher für die Zusammenstellung des Menüelementes verwendet wird. Diese wird zudem für die Identifikation in der Liste der Elemente verwendet, sobald eines dieser Elemente angeklickt wird.

class CMenuItem : public CElement
  {
private:
   //--- Objekte für die Erzeugung eines Menüelements
   CRectLabel        m_area;
   CBmpLabel         m_icon;
   CLabel            m_label;
   CBmpLabel         m_arrow;
   //---
public:
   //--- Methoden für die Erzeugung eines Menüelements
   bool              CreateMenuItem(const long chart_id,const int window,const int index_number,const string label_text,const int x,const int y);
   //---
private:
   bool              CreateArea(void);
   bool              CreateIcon(void);
   bool              CreateLabel(void);
   bool              CreateArrow(void);
   //---
  };

Da diese Elemente aus verschiedenen Typen bestehen können, muss es eine Möglichkeit geben, vor der Erzeugung den Typ anzugeben, zu welchem dieses Element gehört. Daher brauchen wir eine Aufzählung über die verschiedenen Arten der Menüelemente(ENUM_TYPE_MENU_ITEM).

Fügen Sie diese der Enums.mqh Datei hinzu. Die ENUM_TYPE_MENU_ITEM Enumeration muss die oben erwähnten Punkte besitzen:

  • MI_SIMPLE — a simple item.
  • MI_HAS_CONTEXT_MENU — an item, containing a context menu.
  • MI_CHECKBOX — a checkbox item.
  • MI_RADIOBUTTON — an item, belonging to a group of radio items.
//+----------------------------------------------------------------+
//| Enumeration der Typen von Menüelementen                        |
//+----------------------------------------------------------------+
enum ENUM_TYPE_MENU_ITEM
  {
   MI_SIMPLE           =0,
   MI_HAS_CONTEXT_MENU =1,
   MI_CHECKBOX         =2,
   MI_RADIOBUTTON      =3
  };

Wir fügen entsprechende Felder und Methoden für das Festlegen und Abfragen des Typs eines Menüelementes der CMenuItem Klasse hinzu:

class CMenuItem : public CElement
  {
private:
   //--- Eigenschaften des Menüelementes
   ENUM_TYPE_MENU_ITEM m_type_menu_item;
   //---
public:
   //--- Festlegen und Abfragen des Typs
   void              TypeMenuItem(const ENUM_TYPE_MENU_ITEM type)   { m_type_menu_item=type;                 }
   ENUM_TYPE_MENU_ITEM TypeMenuItem(void)                     const { return(m_type_menu_item);              }
   //---
  };

Der MI_SIMPLE Typ. Dieser Typ wird standardmäßig eingestellt:

//+------------------------------------------------------------------+
//| Konstruktor                                                      |
//+------------------------------------------------------------------+
CMenuItem::CMenuItem(void) : m_type_menu_item(MI_SIMPLE)
  {
  }

Die Methoden für das Festlegen des Erscheinungsbildes von jedem grafischen Objekt eines Menüelementes, muss genauso erfolgen, wie es auch in dem vorherigen Artikel für das Formular gezeigt wurde. Diese Methoden werden ein paar spezielle Eigenschaften besitzen. Lassen Sie uns alles aufzählen, was für dieses Control benötigt wird:

  1. Die Veränderung der Hintergrundfarbe.
  2. Die Hintergrundfarbe wenn ein Menüelement nicht verfügbar ist.
  3. Der Wechsel der Hintergrundfarbe des Rahmens.
  4. Die Priorität der linken Maustaste muss für alle Objekte gleich sein, mit Ausnahme des Hintergrundes.
  5. Die Neudefinierung der Icons für die Labels und die Identifizierung ob ein Kontext-Menü verfügbar ist.
  6. Methoden für die Verwaltung der Eigenschaften von Text Labels, wie:
    • Die Abstände von der Null-Koordinate des Hintergrundes des Menüelementes;
    • Die Schriftfarbe;
    • Die Schriftfarbe, wenn sich der Mauszeiger darüber befindet.
    • Die Schriftfarbe wenn das Element blockiert ist.
  7. Variablen für die Verfolgung des Zustandes des Elementes:
    • Der generelle Status (Verfügbar/Gesperrt);
    • Status der Checkbox;
    • Status des Radio-punktes;
    • Der Status des Kontextmenüs, falls es zu diesem Element gehört.
  8. Der Bezeichnung für eine Gruppe von Radio Elementen. Dieses ist notwendig, da ein Kontextmenü verschiedenen Gruppen von Radio Elementen besitzen kann. Der bezeichne erlaubt es uns zu verstehen, zu welcher Gruppe ein Radio-Punkt gehört.
  9. Die Indexnummer eines Menüelementes

Fügen Sie alles, was wir hier aufgelistet haben, der CMenuItem Klasse hinzu:

class CMenuItem : public CElement
  {
private:
   //--- Die Eigenschaften des Hintergrundes
   int               m_area_zorder;
   color             m_area_border_color;
   color             m_area_color;
   color             m_area_color_off;
   color             m_area_color_hover;
   //--- Die Eigenschaften der Labels
   string            m_icon_file_on;
   string            m_icon_file_off;
   //--- Die Textlabel Eigenschaften
   string            m_label_text;
   int               m_label_x_gap;
   int               m_label_y_gap;
   color             m_label_color;
   color             m_label_color_off;
   color             m_label_color_hover;
   //--- Die Eigenschaften für die Identifizierung eines Kontextmenüs
   string            m_right_arrow_file_on;
   string            m_right_arrow_file_off;
   //--- Die generelle Priorität für das Anklicken
   int               m_zorder;
   //--- Verfügbar oder blockiert
   bool              m_item_state;
   //--- Status der Checkbox
   bool              m_checkbox_state;
   //--- Status eines Radiobuttons und sein Bezeichner
   bool              m_radiobutton_state;
   int               m_radiobutton_id;
   //--- Status des Kontextmenüs
   bool              m_context_menu_state;
   //---
public:
   //--- Hintergrund-Methoden
   void              AreaBackColor(const color clr)                 { m_area_color=clr;                      }
   void              AreaBackColorOff(const color clr)              { m_area_color_off=clr;                  }
   void              AreaBorderColor(const color clr)               { m_area_border_color=clr;               }
   //--- Label Methoden
   void              IconFileOn(const string file_path)             { m_icon_file_on=file_path;              }
   void              IconFileOff(const string file_path)            { m_icon_file_off=file_path;             }
   //--- Text Label Methoden
   string            LabelText(void)                          const { return(m_label.Description());         }
   void              LabelXGap(const int x_gap)                     { m_label_x_gap=x_gap;                   }
   void              LabelYGap(const int y_gap)                     { m_label_y_gap=y_gap;                   }
   void              LabelColor(const color clr)                    { m_label_color=clr;                     }
   void              LabelColorOff(const color clr)                 { m_label_color_off=clr;                 }
   void              LabelColorHover(const color clr)               { m_label_color_hover=clr;               }
   //--- Methoden für den Hinweis auf die Anwesenheit eines Kontextmenüs
   void              RightArrowFileOn(const string file_path)       { m_right_arrow_file_on=file_path;       }
   void              RightArrowFileOff(const string file_path)      { m_right_arrow_file_off=file_path;      }
   //--- Gemeinsamer (1) Status eines Elementes und (2) This Checkbox Elements
   void              ItemState(const bool state);
   bool              ItemState(void)                          const { return(m_item_state);                  }
   void              CheckBoxState(const bool flag)                 { m_checkbox_state=flag;                 }
   bool              CheckBoxState(void)                      const { return(m_checkbox_state);              }
   //--- Bezeichner für das Radio-Element
   void              RadioButtonID(const int id)                    { m_radiobutton_id=id;                   }
   int               RadioButtonID(void)                      const { return(m_radiobutton_id);              }
   //--- Status des Radio-Elementes
   void              RadioButtonState(const bool flag)              { m_radiobutton_state=flag;              }
   bool              RadioButtonState(void)                   const { return(m_radiobutton_state);           }
   //--- Status des Kontextmenüs, welches diesem Element hinzugefügt wurde
   bool              ContextMenuState(void)                   const { return(m_context_menu_state);          }
   void              ContextMenuState(const bool flag)              { m_context_menu_state=flag;             }
   //---
  };

In den vorherigen Artikel haben wir erklärt, dass wir die Struktur der Bibliothek so organisieren, dass es den Anwendern nicht passieren kann, dass sie sich in einer Situation befinden, wo sie nicht mehr wissen was sie tun sollen. Die Abfolge unserer Aktionen können mit der Zeit in Vergessenheit geraten. Bevor wir einen Control erzeugen können, muss ihm ein Pointer von der Form zugewiesen werden, zu welcher es angefügt werden soll. Falls dieses nicht erfolgt, kann das Programm die Controls nicht vollständig dem Formular hinzufügen und es wird eine entsprechende Nachricht über das Problem ausgegeben. Diese Nachricht muss dem Anwender ganz deutlich zeigen, wo er im Ablauf seiner Aktionen einen Fehler gemacht hat.

Beziehen Sie die Datei mit der Formularklasse (CWindow) mit ein und deklarieren Sie einen Pointer mit dem Typ des Formulars. Um den Formular-Pointer in den Klassen der Controls abspeichern zu können, benötigen wir noch eine entsprechende Methode. Lassen Sie sie uns WindowPointer() nennen. Als einzigen Parameter akzeptiert in diese ein Objekt des Typs CWindow als Link. Ihre Aufgabe ist es, den Pointer in dem zugewiesenen Objekt zu speichern. Die GetPointer() Funktion kann dazu verwendet werden einen Object-Pointer abzufragen. Dieses muss in jeder Klasse erzeugen, die wir hier entwickeln.

//+------------------------------------------------------------------+
//|                                                     MenuItem.mqh |
//|                        Copyright 2015, MetaQuotes Software Corp. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
#include "Element.mqh"
#include "Window.mqh"
//+------------------------------------------------------------------+
//| Die Klasse für das Erzeugen eines Menüelementes                  |
//+------------------------------------------------------------------+
class CMenuItem : public CElement
  {
private:
   //--- Pointer zu dem Formular, zu welchem das Control hinzugefügt wurde
   CWindow          *m_wnd;
   //---
public:
   //--- Speichert den Pointer des zugewiesenen Formulars
   void              WindowPointer(CWindow &object)                 { m_wnd=::GetPointer(object);            }
   //---
  };

Nun betrachten wir uns die CMenuItem::CreateMenuItem() Methode, die ein Menüelement erzeugt. Am Anfang der Methode, prüfen wir die Gültigkeit des Formular-Pointers, zu welchen dieses Element hinzugefügt werden soll: CheckPointer() Funktion. Falls dieser Pointer nicht gültig ist, dann gibt das Programm eine Nachricht aus und verlässt anschließend die Methode. Wenn der Pointer gültig ist, dann werden die Variablen des Elementes initialisiert.

In dem vorherigen Artikel, haben wir ermittelt, dass jedes Interface-Element seinen eigenen Bezeichner haben muss und wir haben im Detail besprochen wie dieser gebildet wird. Um Wiederholungen zu vermeiden, werde ich Sie hier nur kurz darüber informieren, wie dieses geschieht.

In dem Moment wo das Hauptformular erzeugt und gleichzeitig ein Pointer des Formulars zu der Basis der Elemente hinzugefügt wird (der CWndContainer Klasse), wird der Bezeichner über den Zähler der Interface-Elemente in der CWndContainer::AddWindow() Methode definiert. Der Wert des Zählers wird mit jedem hinzufügen eines Elementes in der Formularklasse abgespeichert. Da der Formular-Pointer in jedem Element zwingend vorgeschrieben ist, ist die Nummer des letzten Controls für jedes neu erzeugten Interface Elementes über den Formular-Pointer erhältlich. Vor dem Erzeugen eines Elementes, muss sein Bezeichner, wie in dem nachfolgend gezeigtem Programmcode, gebildet werden (Hervorgehoben in Gelb.

Die Abstände von den Eckpunkt der Form werden berechnet und in dem Element gespeichert. Der Zugriff auf die Koordinaten des Formulars ist über die Pointer des Formulars möglich und ermöglicht somit die Berechnung der Abstände. Auf die gleiche Art und Weise werden die Abstände für jedes Objekt eines Elementes berechnet. Nachdem alle Objekte und Elemente erzeugt worden sind, wird eine Überprüfung des Status des Fensters durchgeführt. Falls das Fenster minimiert ist, Müssen die Elemente versteckt werden.

//+------------------------------------------------------------------+
//| Erzeugt das Menüpunkt-Element                                    |
//+------------------------------------------------------------------+
bool CMenuItem::CreateMenuItem(const long chart_id,const int subwin,const int index_number,const string label_text,const int x,const int y)
  {
//--- Abbrechen, falls es keinen Pointer zu dem Formular gibt
   if(::CheckPointer(m_wnd)==POINTER_INVALID)
     {
      ::Print(__FUNCTION__," > Before creating a menu item, the class has to be passed  "
            "the window pointer: CMenuItem::WindowPointer(CWindow &object)");
      return(false);
     }
//--- Initialisierung der Variablen
   m_id           =m_wnd.LastId()+1;
   m_index        =index_number;
   m_chart_id     =chart_id;
   m_subwin       =subwin;
   m_label_text   =label_text;
   m_x            =x;
   m_y            =y;
//--- Abstände von Eckpunkt
   CElement::XGap(m_x-m_wnd.X());
   CElement::YGap(m_y-m_wnd.Y());
//--- Erzeugung eines Menü-Items
   if(!CreateArea())
      return(false);
   if(!CreateIcon())
      return(false);
   if(!CreateLabel())
      return(false);
   if(!CreateArrow())
      return(false);
//--- Falls das Fenster minimiert ist, dann müssen die Elemente nach der Erzeugung versteckt werden
   if(m_wnd.IsMinimized())
      Hide();
//---
   return(true);
  }

In der CMenuItem::Hide() Methode des zu versteckenden Elementes, müssen alle Objekte versteckt und einige Variablen und Farben zurückgesetzt werden:

//+-----------------------------------------------------------------------+
//| Versteckt das Menüelement                                             |
//+-----------------------------------------------------------------------+
void CMenuItem::Hide(void)
  {
//--- Abbrechen, falls das Element versteckt ist
   if(!CElement::m_is_visible)
      return;
//--- Versteckt alle Objekte
   for(int i=0; i<ObjectsElementTotal(); i++)
      Object(i).Timeframes(OBJ_NO_PERIODS);
//--- Zurücksetzen der Variablen
   m_context_menu_state=false;
   CElement::m_is_visible=false;
   CElement::MouseFocus(false);
//--- Zurücksetzen der Farbe
   m_area.BackColor(m_area_color);
   m_arrow.State(false);
  }

Das Bilden des Namens für das grafische Objekt eines Menüelements ist ein wenig komplizierter als es in der CWindow Formularklasse der Fall war. Falls es sich bei dem erzeugten Menüelement um ein separates Element, wie zum Beispiel einem unabhängigen Menüelement handelt, welches nicht ein Teil eines Haupt- oder Kontextmenüs ist (Beispiele hierzu folgen noch), dann wird es keine Probleme geben, da dieses Element einen eindeutigen Identifizierer besitzt. Wenn dieses Element innerhalb einer Gruppe erzeugt wird, wo es noch andere gleichartige Objekte gibt, dann ist ein Identifizierer nicht mehr ausreichend. Das liegt daran dass dann die Namen aller Ein-Typ Objekte gleich sind und als Ergebnis würde nur ein Element auf dem Chart angezeigt werden.

Wenn ein Menü Element erzeugt wird, dann wird seine Index-Nummer der CMenuItem::CreateMenuItem() Methode übergeben. Anschließend wird sie in dem Feld der m_index_number Klasse abgespeichert, welche später dazu verwendet wird um den Namen jedes Objektes dieses Elementes zu bilden. Im Folgenden sind die Bestandteile für den Objektnamen des Menüpunktes aufgeführt:

  • Programmname.
  • Zu welchem Element es gehört
  • Zu welchem Teil des Elementes es gehört.
  • Die Indexnummer des Elementes
  • Der identifizierer des Elementes.

Die Methoden für die Erzeugung des Hintergrundes, des Text Labels und des Hinweises auf ein Kontext-Menü unterscheiden sich im Prinzip nicht von den Methoden, der CWindow Klasse und daher können Sie zum Verständnis auch den Code aus der angehängten MenuItem.mqh Datei studieren. Hier demonstrieren wir nur beispielhaft die CMenuItem::CreateIcon() Methode für die Erzeugung eines Icons. In dieser Methode wird der Typ des Menüelementes überprüft und in Abhängigkeit davon ein entsprechendes Icon definiert.

Einfache Menüelemente (MI_SIMPLE) und Elemente, welche ein Kontext-Menü besitzen(MI_HAS_CONTEXT_MENU) haben normalerweise kein Icon. Falls diese vom Benutzer nicht definiert werden, dann verlässt das Programm die Methode mit true, da es sich hierbei nicht um einen Fehler handelt und ein Icon nicht benötigt wird. Falls für Menüelemente des Typs Checkbox oder Radio-Punkt keine Icons definiert werden, dann werden die standardmäßigen Icons verwendet. Die Icons die in dieser Bibliothek verwendet werden, sind dem Artikel beigefügt.

Der Programmcode der CMenuItem::CreateIcon() Methode:

//+----------------------------------------------------------------------+
//| Erzeugt ein Label Element                                            |
//+----------------------------------------------------------------------+
#resource "\\Images\\Controls\\CheckBoxOn_min_gray.bmp"
#resource "\\Images\\Controls\\CheckBoxOn_min_white.bmp"
//---
bool CMenuItem::CreateIcon(void)
  {
//--- Falls es sich um ein einfaches Element oder ein Element mit einem Kontextmenü handelt
   if(m_type_menu_item==MI_SIMPLE || m_type_menu_item==MI_HAS_CONTEXT_MENU)
     {
      //--- Verlassen, falls das Icon nicht benötigt wird (Ein Icon wurde nicht definiert)
      if(m_icon_file_on=="" || m_icon_file_off=="")
         return(true);
     }
//--- Falls es sich um eine Checkbox handelt
   else if(m_type_menu_item==MI_CHECKBOX)
     {
      //--- Falls kein Icon definiert wurde, dann das standardmäßige verwenden
      if(m_icon_file_on=="")
         m_icon_file_on="Images\\Controls\\CheckBoxOn_min_white.bmp";
      if(m_icon_file_off=="")
         m_icon_file_off="Images\\Controls\\CheckBoxOn_min_gray.bmp";
     }
//--- Falls es sich um ein Radio-Element handelt     
   else if(m_type_menu_item==MI_RADIOBUTTON)
     {
      //--- Falls kein Icon definiert wurde, dann das standardmäßige verwenden
      if(m_icon_file_on=="")
         m_icon_file_on="Images\\Controls\\CheckBoxOn_min_white.bmp";
      if(m_icon_file_off=="")
         m_icon_file_off="Images\\Controls\\CheckBoxOn_min_gray.bmp";
     }
//--- Den Objektnamen bilden
   string name=CElement::ProgramName()+"_menuitem_icon_"+(string)CElement::Index()+"__"+(string)CElement::Id();
//--- Objektkoordinaten
   int x =m_x+7;
   int y =m_y+4;
//--- Das Label festlegen
   if(!m_icon.Create(m_chart_id,name,m_subwin,x,y))
      return(false);
//--- Festlegen der Eigenschaften
   m_icon.BmpFileOn("::"+m_icon_file_on);
   m_icon.BmpFileOff("::"+m_icon_file_off);
   m_icon.State(m_item_state);
   m_icon.Corner(m_corner);
   m_icon.GetInteger(OBJPROP_ANCHOR,m_anchor);
   m_icon.Selectable(false);
   m_icon.Z_Order(m_zorder);
   m_icon.Tooltip("\n");
//--- Abstände von Eckpunkt
   m_icon.XGap(x-m_wnd.X());
   m_icon.YGap(y-m_wnd.Y());
//--- Abspeichern des Objekt-Pointers
   CElement::AddToArray(m_icon);
   return(true);
  }

Die CMenuItem::Hide() Methode für das Verstecken von Elementen wurde vorher schon gezeigt. Nun implementieren wir die CMenuItem::Show() Methode, die die Elemente wieder sichtbar macht. Falls es sich bei dem Element um eine Checkbox oder einen Radio-Punkt handelt, muss diese Methode den aktuellen Status berücksichtigen(Aktiviert/deaktiviert):

//+---------------------------------------------------------------------+
//| Macht das Menüelement sichtbar                                      |
//+---------------------------------------------------------------------+
void CMenuItem::Show(void)
  {
//--- Verlassen, falls das Element bereits sichtbar ist
   if(CElement::m_is_visible)
      return;
//--- Alle Objekte sichtbar machen
   for(int i=0; i<ObjectsElementTotal(); i++)
      Object(i).Timeframes(OBJ_ALL_PERIODS);
//--- Falls es sich um eine Checkbox handelt, dann den Status berücksichtigen
   if(m_type_menu_item==MI_CHECKBOX)
      m_icon.Timeframes((m_checkbox_state)? OBJ_ALL_PERIODS : OBJ_NO_PERIODS);
//--- Falls es sich um ein Radio-Element handelt, dann den Status berücksichtigen
   else if(m_type_menu_item==MI_RADIOBUTTON)
      m_icon.Timeframes((m_radiobutton_state)? OBJ_ALL_PERIODS : OBJ_NO_PERIODS);
//--- Zurücksetzen der Variablen
   CElement::m_is_visible=true;
   CElement::MouseFocus(false);
  }

Sobald alle diese Methoden für das Erzeugen eines Interface-Elementes implementiert wurden, kann das hinzufügen des Interfaces zu dem Chart getestet werden. Beziehen sie die CMenuItem Klasse in der WndContainer.mqh Datei mit ein:

//+------------------------------------------------------------------+
//|                                                 WndContainer.mqh |
//|                        Copyright 2015, MetaQuotes Software Corp. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
#include "Window.mqh"
#include "MenuItem.mqh"

Wir verwenden nun den gleichen EA, den wir auch in dem vorherigen Artikel zum Test verwendet haben. In der CProgram Klasse erzeugen Sie eine Instanz der CMenuItem Klasse und der CProgram::CreateMenuItem1() Methode für die Erzeugung eines Menüelementes. Die Abstände von den Eckpunkten jedes Elementes des Formulars, werden ganz praktisch in den Makros gehandhabt. Wenn wir eine große Anzahl von Elementen haben, dann ist dies ist die einfachste und schnellste Art deren Position in Verhältnis zueinander zu verändern, als von einer Methode zu der nächsten zu springen.

//+-------------------------------------------------------------------+
//| Klasse für das Erzeugen einer Anwendung                           |
//+-------------------------------------------------------------------+
class CProgram : public CWndEvents
  {
private:
   //--- Window
   CWindow           m_window;
   //--- Menüelement
   CMenuItem         m_menu_item1;
public:
                     CProgram();
                    ~CProgram();
   //--- Initialisierung/Deinitialisierung
   void              OnInitEvent(void);
   void              OnDeinitEvent(const int reason);
   //--- Timer
   void              OnTimerEvent(void);
   //---
protected:
   //--- Chart Eventhandler
   virtual void      OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam);
   //---
public:
   //--- Erzeugt das Trading-Panel
   bool              CreateTradePanel(void);
   //---
private:
//--- Erzeugen der Form
   bool              CreateWindow(const string text);
//--- Erzeugung eines Menü-Items
#define MENU_ITEM1_GAP_X (6)
#define MENU_ITEM1_GAP_Y (25)
   bool              CreateMenuItem1(const string item_text);
  };

Um uns ein vollständiges Menüelement ansehen zu können, lassen Sie uns ein Menüelement erzeugen, welches ein Icon und ein Kontextmenü besitzt. Wir haben zwei Icons - ein farbiges und ein farbloses. Das farblose Icon verwenden wir, falls das Menüelement blockiert ist (Nicht verfügbar).

Nachfolgend sehen Sie den Programmcode für die CProgram::CreateMenuItem1() Methode. Die hier mit einbezogenenresources (Icons) finden Sie auch am Ende des Artikels. Am Anfang der Methode wird der Pointer des Formulars, zu welchen dieses Element hinzugefügt worden ist, in der Elementen-Klasse gespeichert. Anschließend werden die Koordinaten berechnet, die benötigten Eigenschaften der Elemente werden gesetzt und das Menüelement wird dem Chart hinzugefügt.

//+------------------------------------------------------------------+
//| Erzeugt ein Menüelement                                          |
//+------------------------------------------------------------------+
#resource "\\Images\\Controls\\bar_chart.bmp"
#resource "\\Images\\Controls\\bar_chart_colorless.bmp"
//---
bool CProgram::CreateMenuItem1(string item_text)
  {
//--- Abspeichern des Fenster-Pointers
   m_menu_item1.WindowPointer(m_window);
//--- Koordinaten  
   int x=m_window.X()+MENU_ITEM1_GAP_X;
   int y=m_window.Y()+MENU_ITEM1_GAP_Y;
//--- Festlegen der Eigenschaften bevor er erzeugt wird
   m_menu_item1.XSize(193);
   m_menu_item1.YSize(24);
   m_menu_item1.TypeMenuItem(MI_HAS_CONTEXT_MENU);
   m_menu_item1.IconFileOn("Images\\Controls\\bar_chart.bmp");
   m_menu_item1.IconFileOff("Images\\Controls\\bar_chart_colorless.bmp");
   m_menu_item1.LabelColor(clrWhite);
   m_menu_item1.LabelColorHover(clrWhite);
//--- Erzeugung eines Menü-Items
   if(!m_menu_item1.CreateMenuItem(m_chart_id,m_subwin,0,item_text,x,y))
      return(false);
//---
   return(true);
  }

Nun kann der CProgram::CreateTradePanel() Methode ein Aufruf der Methode für das Erzeugen des Menüelementes hinzugefügt werden:

//+------------------------------------------------------------------+
//| Erzeugung des Trading-Panels                                     |
//+------------------------------------------------------------------+
bool CProgram::CreateTradePanel(void)
  {
//--- Erzeugung einer Form für Controls
   if(!CreateWindow("EXPERT PANEL"))
      return(false);
//---Erzeugung der Controls:
//    Menüelement
   if(!CreateMenuItem1("Menu item"))
      return(false);
//--- Neuzeichnen auf dem Chart
   m_chart.Redraw();
   return(true);
  }

Ein aufmerksamer Leser wird sicherlich folgende Frage stellen: "Wie kommt der Pointer des Elementes in die Basis?". Dieses ist eine gute Frage, da wir dieses noch nicht implementiert haben und somit der Pointer auch noch nicht abgespeichert wird. Wie Sie sich erinnern, habe ich zuvor darauf hingewiesen dass wir an einer Stelle eine Besonderheit des Compilers aufzeigen wollen. Nun ist es an der Zeit. Die Methoden für die Verwaltung der Elemente und die Verarbeitung der Events haben wir im Augenblick noch nicht in der CMenuItem Klasse implementiert und es gibt in der Basis auch noch keinen Pointer. Die Kompilierung der Datei mit der CMenuItem Klasse ergab keine Fehler. Wenn Sie versuchen die Datei des EAs zu kompilieren, erhalten Sie eine Fehlermeldung mit der Angabe der Methode, für welche noch eine Implementation benötigt wird. (Schauen Sie sich den nachfolgenden Screenshot an).

Abbildung 4. Nachricht über das Fehlen einer Implementation einer Methode

Abbildung 4. Nachricht über das Fehlen einer Implementation einer Methode

Als die Datei mit der CMenuItem Klasse kompiliert wurde, gab es keine Fehlermeldungen, da in dieser Klasse zu dem augenblicklichen Stand der Entwicklung, die Methoden ohne Implementation nicht aufgerufen worden sind. Im Augenblick besitzt die Basis nur den Formular-Pointer. Es sieht so aus, als ob der Aufruf der Methoden, die wir oben in den Screenshot sehen, nur durch das Formular (CWindow) in der CWndEvents Klasse in den Schleifen der CheckElementsEvents(), MovingWindow(), CheckElementsEventsTimer() und Destroy() Methoden aufgerufen wird. Die Pointer der Elemente werden in dem Array des Typs CElement gespeichert, was bedeutet, dass der Aufruf durch die CElement Basisklasse durchgeführt wird, Wo diese Methoden als virtuell deklariert wurden.

Seit in der WndContainer.mqh Datei in den zwei Dateien Window.mqh und MenuItem.mqh die Elemente enthalten sind und deren Klassen von der CElement Klasse abgeleitet wurden, erkennt der Compiler alle abgeleiteten Klassen und fordert die Implementation der enthaltenen Methoden, auch wenn sie keinen direkten Aufruf besitzen. Lassen Sie uns die benötigten Methoden in der CMenuItem Klasse erzeugen. Wir werden auch Methoden erzeugen, die es uns erlauben, die Pointer zu der Basis in der CWndContainer Klasse hinzuzufügen.

Zum aktuellen Zeitpunkt kann die CMenuItem::ChangeObjectsColor() Methode für das Ändern der Farbe des Elementes, falls sich der Mauszeiger darüber befindet, hinzugefügt werden. In dieser Methode muss der Typ und der Status eines Elementes berücksichtigt werden(Verfügbar/gesperrt). Zu Beginn dieser Methode muss eine Überprüfung durchgeführt werden, ob das Elements ein Kontextmenü enthält und ob das Letztere zurzeit aktiviert ist. Der Grund dafür ist, dass wenn ein Kontextmenü aktiviert ist, ihm auch die Verwaltung übergeben werden muss. Nebenher muss auch noch die Farbe von dem Element, welches es aufgerufen hat, protokolliert werden.

Später benötigen wir in dem Menüelement auch noch eine Methode für den "Focus Color Reset". Lassen Sie uns dafür die Methode CMenuItem::ResetColors() erzeugen.

class CMenuItem : public CElement
  {
public:
   //--- Farbänderungen der Control Objekte
   void              ChangeObjectsColor(void);
   //--- Zurücksetzen der Farbe
   void              ResetColors(void);
   //---
  };
//+-------------------------------------------------------------------+
//| Ändern der Objektfarbe, wenn sich der Mauszeiger darüber befindet |
//+-------------------------------------------------------------------+
void CMenuItem::ChangeObjectsColor(void)
  {
//--- Verlassen, falls das Element ein aktiviertes Kontextmenü besitzt
   if(m_type_menu_item==MI_HAS_CONTEXT_MENU && m_context_menu_state)
      return;
//--- Programmcode für einfache Elemente und Elemente mit Kontextmenü
   if(m_type_menu_item==MI_HAS_CONTEXT_MENU || m_type_menu_item==MI_SIMPLE)
     {
      //--- Falls Sie den Fokus besitzen
      if(CElement::MouseFocus())
        {
         m_icon.State(m_item_state);
         m_area.BackColor((m_item_state)? m_area_color_hover : m_area_color_off);
         m_label.Color((m_item_state)? m_label_color_hover : m_label_color_off);
         if(m_item_state)
            m_arrow.State(true);
        }
      //--- Falls es keinen Fokus gibt
      else
        {
         m_arrow.State(false);
         m_area.BackColor(m_area_color);
         m_label.Color((m_item_state)? m_label_color : m_label_color_off);
        }
     }
//--- Programmcode für Checkboxen und Radio Elemente
   else if(m_type_menu_item==MI_CHECKBOX || m_type_menu_item==MI_RADIOBUTTON)
     {
      m_icon.State(CElement::MouseFocus());
      m_area.BackColor((CElement::MouseFocus())? m_area_color_hover : m_area_color);
      m_label.Color((CElement::MouseFocus())? m_label_color_hover : m_label_color);
     }
  }
//+------------------------------------------------------------------+
//| Zurücksetzen der Farbe des Elementes                             |
//+------------------------------------------------------------------+
void CMenuItem::ResetColors(void)
  {
   CElement::MouseFocus(false);
   m_area.BackColor(m_area_color);
   m_label.Color(m_label_color);
  }

Die Methoden für das Bewegen und das Löschen von Objekten sind ähnlich wie in der CWindow Klasse und werden daher hier nicht wiederholt. Sie finden den Programmcode in den angehängten Dateien. Fügen Sie nun den minimal benötigten Programmcode (siehe unten) zu der CMenuItem::OnEvent() und CMenuItem::OnEventTimer() Methode hinzu und kompilieren Sie die Dateien der Bibliothek und des EAs. Nun gibt es keine Fehlermeldung mehr, die auf eine notwendige Implementierung von Methoden hinweisen

//+------------------------------------------------------------------+
//| Event handler                                                    |
//+------------------------------------------------------------------+
void CMenuItem::OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam)
  {
   if(id==CHARTEVENT_MOUSE_MOVE)
     {
      //--- Falls das Control nicht versteckt ist
      if(!CElement::m_is_visible)
         return;
      //--- Identifizierung des Focus
      int x=(int)lparam;
      int y=(int)dparam;
      CElement::MouseFocus(x>X() && x<X2() && y>Y() && y<Y2());
      return;
     }  
  }
//+------------------------------------------------------------------+
//| Timer                                                            |
//+------------------------------------------------------------------+
void CMenuItem::OnEventTimer(void)
  {
//--- Die Veränderung der Farbe der Objekte des Formulars
   ChangeObjectsColor();
  }


Test des Hinzufügens eines Menüpunktes

Wenn Sie nun den EA auf den Chart laden, dann sehen Sie ein Formular mit den Menüelementen, so wie es in dem nachfolgenden Screenshot dargestellt ist. Sie können es hier nicht sehen, da die standardmäßige Farbe des Elementhintergrundes, der Farbe des Hintergrundes des Formulars entspricht. Sie können die Funktionalitäten der Klasse bereits zum Wechsel von vielen Eigenschaften der Objektes verwenden, sowohl für das Formular als auch für die Menüelemente.

Abbildung 5. Test über das Hinzufügen der Menüelemente zu dem Chart

Abbildung 5. Test über das Hinzufügen der Menüelemente zu dem Chart

Das Element wurde jetzt dem Chart hinzugefügt, aber wenn Sie versuchen dieses zu bewegen, dann bleibt das Elements an seiner Position stehen und auch wenn Sie den Mauszeiger darüber bewegen, verändert sich sein Erscheinungsbild nicht. Lassen Sie uns nur Methoden zum Hinzufügen der Pointer zu der Basis erzeugen. Die Art und Weise wie die Eigenschaften aller Elemente verwaltet werden können funktioniert, indem jedes von ihnen in einer Schleife aufgerufen wird.

Lassen Sie uns diese Methode CWndContainer::AddToElementsArray() nennen. Sie besitzt zwei Parameter: (1) Die Nummer des Formular-Indexes, Zu welchem dieses Element hinzugefügt werden soll (2) Element-Objekt, ein Pointer, welcher in der Basis abgespeichert werden muss. Zu Beginn der Methode, wird überprüft, ob die Basis mindestens ein Formular enthält. Falls keine existieren, dann wird eine entsprechende Nachricht ausgegeben. Anschließend wird eine Überprüfung der Überschreitung der Array-Größe vorgenommen.

Falls es keine Probleme gibt, dann (1) wird der Pointer zu dem des Formulars, zu welchen er hinzugefügt werden soll hinzugefügt, (2) Die Element-Objekte werden dem gemeinsamen Array von Objekten hinzugefügt, (3) Der letzte Identifizierer des Elementes wird in allen Formularen abgespeichert (4) Der Zähler für die Elemente wird um einen erhöht. Dieses ist noch nicht die finale Version dieser Methode. Wir werden später hierhin zurückkehren. Der Programmcode der aktuellen Version wird nachfolgend gezeigt:

//+------------------------------------------------------------------+
//| Fügt einen Pointer zu dem Array der Elemente hinzu               |
//+------------------------------------------------------------------+
void CWndContainer::AddToElementsArray(const int window_index,CElement &object)
  {
//--- Falls die Basis keine Formulare für Controls enthält
   if(ArraySize(m_windows)<1)
     {
      Print(__FUNCTION__," > Before creating a control, create a form  "
            "and add it to the base using the CWndContainer::AddWindow(CWindow &object) method.");
      return;
     }
//--- Falls es eine Anfrage für ein nicht existierendes Formular gibt
   if(window_index>=ArraySize(m_windows))
     {
      Print(PREVENTING_OUT_OF_RANGE," window_index: ",window_index,"; ArraySize(m_windows): ",ArraySize(m_windows));
      return;
     }
//--- Hinzufügen zu dem gemeinsamen Array von Elementen
   int size=ArraySize(m_wnd[window_index].m_elements);
   ArrayResize(m_wnd[window_index].m_elements,size+1);
   m_wnd[window_index].m_elements[size]=GetPointer(object);
//--- Hinzufügen von Element-Objekten zu dem gemeinsamen Array von Objekten
   AddToObjectsArray(window_index,object);
//--- Abspeichern der ID von dem letzten Element in allen Forms
   int windows_total=ArraySize(m_windows);
   for(int w=0; w<windows_total; w++)
      m_windows[w].LastId(m_counter_element_id);
//--- Erhöhen des Zählers für die Elemente
   m_counter_element_id++;
  }

Die aktuelle Version der CWndContainer::AddToElementsArray() Methode ist bereits ausreichend für einen Test mit den EA. Am Ende der CProgram::CreateMenuItem() Methode, nach der Erzeugung eines Elementes, fügen Sie die folgende Programmzeile, so wie sie in der abgekürzten Version nachfolgend dargestellt ist, in die Methode ein.

//+------------------------------------------------------------------+
//| Erzeugt ein Menüelement                                          |
//+------------------------------------------------------------------+
bool CProgram::CreateMenuItem(string item_text)
  {
//--- Abspeichern des Fenster-Pointers
//--- Koordinaten  
//--- Festlegen der Eigenschaften bevor er erzeugt wird
//--- Erzeugung eines Menü-Items
//--- Hinzufügen des Pointers des Elementes zu der Basis
   CWndContainer::AddToElementsArray(0,m_menu_item);
   return(true);
  }

Wenn Sie nun alle Dateien, in welchen Veränderungen stattgefunden haben, kompilieren und den EA auf den Charts Laden, erscheint wieder das Formular mit dem Menüelement. Wenn Sie das Formular bewegen, dann bewegt sich auch das Menüelement mit all seinen zugehörigen Elementen und es verändert sich auch das Erscheinungsbild, sobald sich der Mauszeiger darüber befindet:

Abbildung 6. Test des Menüelementes, als ein Teil des grafischen Interfaces

Abbildung 6. Test des Menüelementes, als ein Teil des grafischen Interfaces


Die weitere Entwicklung der Bibliothek der Hauptklasse

Wenn die Form nun minimiert wird, dann werden die Menüelemente nicht wie erwartet versteckt Wie kann das Verstecken der Elemente nun implementiert werden? Die Verwaltung der Chart-Events wird zur Zeit in der CWndEvents Klasse vorgenommen, welche Zugriff auf die Basis aller Elemente der Basisklasse CWndContainer hat. Gibt es hier eine Hinweis darauf dass eine Minimierung oder Maximierung gewünscht(angeklickt) wurde? Für diese Fälle besitzt MQL die EventChartCustom() Funktion, die benutzerdefinierte Events erzeugen kann.

Wenn nun der Button für die Minimierung des Formulars angeklickt wird, verfolgt das Programm das CHARTEVENT_OBJECT_CLICK Event in dem OnEvent() Handler der CWindow Klasse und, nach der Verifizierung des Wertes des String-Parameters(sparam) dieses Events mit den Namen des grafischen Objektes, wird die CWindow::RollUp() Methode aufgerufen. Das ist der Zeitpunkt, wo eine Nachricht zu der Warteschlange der Event-Nachrichten hinzugefügt werden kann und welche in dem Handler der CwndEvents Klasse empfangen werden kann. Da die CWndEvents Klasse Zugriff zu allen Elementen hat, kann die CElement::Hide() Methode jedes Elementes in einer Schleife aufgerufen werden. Das Selbe muss natürlich auch für die Maximierung durchgeführt werden, damit alle Elemente wieder über ihre Methoden CElement::Show() sichtbar gemacht werden können.

Jeder benutzerdefinierte Event benötigt seinen eigenen eindeutigen Bezeichner. Fügen Sie diese Bezeichner des Formulars zu der Defines.mqh Datei hinzu:

//--- Events
#define ON_WINDOW_UNROLL          (1) // Form maximization
#define ON_WINDOW_ROLLUP          (2) // Form minimization

Am Ende der CWindow::RollUp() und CWindow::Unroll() Methoden rufen Sie die EventChartCustom() Funktion mit den folgenden Parametern auf:

  1. Den Bezeichner des Charts.
  2. Den Bezeichner für das benutzerdefinierte Event.
  3. Den Bezeichner des Elementes als dritten Parameter (lparam).
  4. Die Anzahl der Chart-Unterfenster, in welchen sich das Programm befindet als 4. Parameter(dparam).

Der dritte und vierte Parameter wird benötigt, für eine zusätzliche Überprüfung für den Fall, dass dieses Event von einem anderen Element oder einem anderen Programm gesendet worden ist.

Im Folgenden finden Sie abgekürzte Versionen der CWindow::RollUp() und CWindow::Unroll() Methoden, mit dem Programmcode, was ihnen noch hinzugefügt werden muss :

//+---------------------------------------------------------------+
//| Minimierung des Fensters                                      |
//+---------------------------------------------------------------+
void CWindow::RollUp(void)
  {
//--- Wechseln des Buttons
//--- Setzen und speichern der Größe
//--- Deaktivieren des Buttons
//--- Minimierter Status des Formulars
//--- Falls es sich um einen Indikator mit einer festgelegten Höhe und Breite handelt, dann setzt der Unterfenster Minimierungs-Modus
//    die Größe des Indikator Unterfensters
//--- Eine Nachricht darüber senden
   ::EventChartCustom(m_chart_id,ON_WINDOW_ROLLUP,CElement::Id(),m_subwin,"");
  }
//+----------------------------------------------------------------+
//| Maximiert das Fenster                                          |
//+----------------------------------------------------------------+
void CWindow::Unroll(void)
  {
//--- Wechseln des Buttons
//--- Setzen und speichern der Größe
//--- Deaktivieren des Buttons
//--- Maximierter Status des Formulars
//--- Falls es sich um einen Indikator mit einer festgelegten Höhe und Breite handelt, dann setzt der Unterfenster Minimierungs-Modus
//    die Größe des Indikator Unterfensters
//--- Eine Nachricht darüber senden
   ::EventChartCustom(m_chart_id,ON_WINDOW_UNROLL,CElement::Id(),m_subwin,"");
  }

Nun müssen wir Methoden in der CWndEvents Klasse erzeugen, welche diese benutzerdefinierten Events behandeln. Wir benennen sie genauso wie die Makros für die Bezeichner. Nachfolgend finden Sie den Programmcode für die Deklaration und Implementation der Methoden in der CWndEvents Klasse:

class CWndEvents : public CWndContainer
  {
private:
   //--- Minimierung und Maximierung des Formulars
   bool              OnWindowRollUp(void);
   bool              OnWindowUnroll(void);
   //---
  };
//+----------------------------------------------------------------+
//| ON_WINDOW_ROLLUP event                                         |
//+----------------------------------------------------------------+
bool CWndEvents::OnWindowRollUp(void)
  {
//--- Falls es sich um ein Signal zur Minimierung des Formulars handelt
   if(m_id!=CHARTEVENT_CUSTOM+ON_WINDOW_ROLLUP)
      return(false);
//--- Falls die Bezeichner des Fensters und des Unterfensters stimmen
   if(m_lparam==m_windows[0].Id() && (int)m_dparam==m_subwin)
     {
      int elements_total=CWndContainer::ElementsTotal(0);
      for(int e=0; e<elements_total; e++)
        {
         //--- Verstecken aller Elemente, mit Ausnahme des Formulars
         if(m_wnd[0].m_elements[e].ClassName()!="CWindow")
            m_wnd[0].m_elements[e].Hide();
        }
     }
//---
   return(true);
  }
//+----------------------------------------------------------------+
//| ON_WINDOW_UNROLL event                                         |
//+----------------------------------------------------------------+
bool CWndEvents::OnWindowUnroll(void)
  {
//--- Falls es sich um ein Signal für die Maximierung des Formulars handelt
   if(m_id!=CHARTEVENT_CUSTOM+ON_WINDOW_UNROLL)
      return(false);
//--- Falls die Bezeichner des Fensters und des Unterfensters stimmen
   if(m_lparam==m_windows[0].Id() && (int)m_dparam==m_subwin)
     {
      int elements_total=CWndContainer::ElementsTotal(0);
      for(int e=0; e<elements_total; e++)
        {
         //--- Mache alle Elemente sichtbar, mit Ausnahme des Formulars und der
         //    drop-down Elemente
         if(m_wnd[0].m_elements[e].ClassName()!="CWindow")
            if(!m_wnd[0].m_elements[e].IsDropdown())
               m_wnd[0].m_elements[e].Show();
        }
     }
//---
   return(true);
  }

Die CWindow::Show() Methode wurde bisher noch nicht implementiert. Da die Methode in allen Elementen innerhalb einer Schleife aufgerufen wird, ist ihre implementation zwingend notwendig, auch falls sich keine Bedingung ergibt die zu einem Aufruf in der CWindow Klasse führt.

Programmcode in der CWindow::Show() Methode:

//+----------------------------------------------------------------+
//| Zeigt das Fenster                                              |
//+----------------------------------------------------------------+
void CWindow::Show(void)
  {
//--- Alle Objekte sichtbar machen
   for(int i=0; i<ObjectsElementTotal(); i++)
      Object(i).Timeframes(OBJ_ALL_PERIODS);
//--- Sichtbarer Status
   CElement::m_is_visible=true;
//--- Zurücksetzen des Fokus
   CElement::MouseFocus(false);
   m_button_close.MouseFocus(false);
   m_button_close.State(false);
  }

Wir rufen diese Methoden in der CWndEvents::ChartEventCustom() Methode auf:

//+----------------------------------------------------------------+
//| CHARTEVENT_CUSTOM event                                        |
//+----------------------------------------------------------------+
void CWndEvents::ChartEventCustom(void)
  {
//--- Falls es sich um ein Signal zur Minimierung des Formulars handelt
   if(OnWindowRollUp())
      return;
//--- Falls es sich um ein Signal für die Maximierung des Formulars handelt
   if(OnWindowUnroll())
      return;
  }

Von nun an befinden sich alle Methoden, die benutzerdefinierte Events behandeln, in der CWndEvents::ChartEventCustom() Methode.

Gleichzeitig muss der Aufruf von CWndEvents::ChartEventCustom() in der CWndEvents::ChartEvent() Methode, bevor die Methoden, in welchen die Charts Events behandelt werden, aufgerufen werden:

//+----------------------------------------------------------------+
//| Behandeln von Programm-Events                                  |
//+----------------------------------------------------------------+
void CWndEvents::ChartEvent(const int id,const long &lparam,const double &dparam,const string &sparam)
  {
//--- Verlassen, falls das Array leer ist
   if(CWndContainer::WindowsTotal()<1)
      return;
//--- Initialisierung der Felder der Event-Parameter
   InitChartEventsParams(id,lparam,dparam,sparam);
//--- Benutzerdefiniertes Event
   ChartEventCustom();
//--- Überprüfung der Events der Interface-Elemente
   CheckElementsEvents();
//--- Event für die Mausbewegung
   ChartEventMouseMove();
//--- Event für die Veränderung der Eigenschaften des Charts
   ChartEventChartChange();
  }

Laden Sie das Programm auf den Chart, nachdem Sie die Dateien, welche geändert worden sind, kompiliert haben. Wenn das Formular minimiert ist, dann werden die Menüelemente versteckt und sie werden wieder sichtbar, sobald das Formular wieder maximiert wird.

Wir haben die Entwicklung des Hauptteils der CMenuItem Klasse abgeschlossen. Nun müssen wir nur noch den Eventhandler des Controls einrichten. Wir kommen darauf zurück, sobald wir die Klasse für das Erzeugen eines Kontextmenüs implementiert haben. Aber vorher werden wir ein anderes Interface-Element entwickeln, welches ebenfalls ein Teil eines Kontextmenüs darstellt: eine Trennlinie.


Schlussfolgerung

In diesem Artikel haben wir uns ausführlich mit dem Prozess für die Erzeugung eines Menüelement-Controls befasst. Wir haben zudem notwendige Ergänzungen an der Formular Klasse(CWindow) und der Hauptklasse für das Behandeln von Events(CWndEvents) vorgenommen. In dem nächsten Artikel werden wir Klassen für die Erzeugung einer Trennlinie und des Kontextmenüs besprechen.

Die unten aufgelisteten Archive enthalten die Dateien der Bibliothek zu dem aktuellen Stand der Entwicklung, sowie Bilder und Dateien der besprochenen Programme (Der EA, die Indicatoren und das Skript). Sie können für Tests in dem MetaTrader 4 und MetaTrader 5 Terminal heruntergeladen werden. Wenn Sie fragen zur Verwendung dieses Materials haben, dann können Sie zunächst auf die detaillierte Beschreibung in dem Artikel zu dieser Bibliothek zurückgreifen oder Sie stellen Ihre Frage(n) in den Kommentaren zu diesem Artikel.

Liste der Artikeln (Kapitel) des zweiten Teils:

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

Beigefügte Dateien |
Spontane Änderung der Expert-Advisor-Parameter vom Bedienfeld aus Spontane Änderung der Expert-Advisor-Parameter vom Bedienfeld aus

Dieser Artikel zeigt anhand eines Beispiels die Implementierung eines Expert Advisors, dessen Parameter vom Bedienfeld aus kontrolliert werden können. Bei einer spontanen Änderung der Parameter schreibt der Expert Advisor die Werte vom Infofeld in eine Datei, um sie später von dieser Datei zu lesen und sie im Feld darzustellen. Dieser Artikel ist interessant für alle, die manuell oder semi-automatisch handeln wollen.

Maschinelles Lernen: Wie Support Vector Machines beim Handeln verwendet werden können Maschinelles Lernen: Wie Support Vector Machines beim Handeln verwendet werden können

Support Vector Machines wurden lange in Bereichen wie Bioinformatik und angewandter Mathematik verwendet, um komplexe Datensätze zu evaluieren und nützliche Muster für die Datenklassifikation zu extrahieren. In diesem Artikel wird besprochen, was eine Support Vector Machine ist, wie sie arbeitet und warum sie so nützlich bei der Extraktion komplexer Muster ist. Danach werden wir untersuchen, wie sie auf dem Markt angewandt werden können und vielleicht beim Handeln Ratschläge geben. Mit dem "Support Vector Machine Learning Tool" bietet der Artikel Beispiele, anhand derer Leser mit ihrem eigenen Handeln experimentieren können.

Universeller Expert Advisor: Ein benutzerdefiniertes Trailing Stop (Part 6) Universeller Expert Advisor: Ein benutzerdefiniertes Trailing Stop (Part 6)

Der sechste Teil des Artikels über den universellen Expert Advisor beschreibt die Verwendung eines Trailingstops. Dieser Artikel führt sie durch die Erstellung eines Trailingstop-Moduls, welches einheitliche Regeln verwendet, sowie durch den Vorgang, wie dieses Modul der Trading Engine hinzugefügt wird, damit es automatisch Positionen verwaltet.

Grafische Interfaces II: Die Trennlinien und Context-Menüelemente (Kapitel 2) Grafische Interfaces II: Die Trennlinien und Context-Menüelemente (Kapitel 2)

In diesem Artikel erzeugen wir das Trendlinien-Element Es kann nicht nur als unabhängiges Interface-Element verwendet werden, sondern auch als ein Teil von vielen anderen Elementen. Anschließend haben wir alles, was für die Entwicklung der Kontextmenü Klasse benötigt wird. Diese Klasse werden wir in diesem Artikel im Detail besprechen. Zudem werden wir alle notwendigen Ergänzungen dieser Klasse hinzufügen, die für das Abspeichern von Pointern aller Elemente des grafischen Interfaces dieser Anwendung benötigt werden.