English Русский 中文 Español 日本語 Português
Grafische Interfaces X: Updates für die Easy And Fast Bibliothek (Build 3)

Grafische Interfaces X: Updates für die Easy And Fast Bibliothek (Build 3)

MetaTrader 5Beispiele | 3 November 2016, 11:09
695 1
Anatoli Kazharski
Anatoli Kazharski

Inhalt


Einleitung

Der erste Artikel Grafische Interfaces I: Vorbereitung der Bibliotheksstruktur (Kapitel 1) betrachtet im Detail den Sinn und Zweck der Bibliothek. 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 Entwicklungsstand, herunterzuladen. Die Dateien müssen in den gleichen Verzeichnissen untergebracht werden, so, wie Sie auch in dem Archiv abgelegt sind.

In diesem Artikel wird die nächste Version der Easy And Fast Bibliothek (version 3) vorgestellt. Es wurden Fehler behoben und neue Features hinzugefügt. Mehr Details dazu finden Sie in dem Artikel.

Ab dieser Version, wird die Bibliothek nur noch für die MetaTrader 5 -Plattform entwickelt. Dies ist auf bestimmte grundlegende architektonische Unterschiede und Einschränkungen in der MetaTrader 4 zurückzuführen. Falls allerdings ein dringender Bedarf an der Unterstützung der Bibliothek für eine frühere Version der Plattform besteht, ist es möglich, eine Anfrage in dem Freelance -Service-Bereich zu stellen , die an den Autor oder einen anderen Entwickler gerichtet ist, der bereit und fähig ist, die Arbeit zu erledigen. 

 

Updates

1. Bis jetzt zeigten die MQL-Anwendungen in den vorherigen Artikeln, wie eine grafische Oberfläche in dem Indikator-Unterfenster implementiert wird. Natürlich ist dieser Ansatz für einige einfache Anwendungen ausreichend. Aber es wäre auch gut, wenn wir die Möglichkeit hätten, eine grafische Oberfläche in einem Unterfenster für ein "Expert"-Typ-Programm zu erstellen. Auf diese Weise wäre es möglich Anwendungen zu erstellen, die völlig getrennt von dem wichtigsten Chart-Fenster laufen. Das Arbeiten in diesem Modus wird dann einfacher sein: der Kurs-Chart und alle wichtigen Daten auf dem Chart blieben offen und übersichtlich und von der grafische Benutzeroberfläche der Anwendung getrennt. Die Umsetzung dieser Idee innerhalb der Bibliothek Easy And Fast wird weiter unten beschrieben.

Die Aufgabe war, den Expertenmodus "im Unterfenster" aktivieren zu können, indem unter Anderem die Ressource (SubWindow.ex5) durch einem Platzhalter in der Hauptdatei der MQL-Anwendung mit einbezogen wird. Der Platzhalter ist nur ein Unterfenster ohne Berechnungen. Aber da es derzeit mithilfe von MQL keine Möglichkeit gibt, zu wissen, ob der Indikator als eine Ressource enthalten ist, fügen wir vorübergehend die Richtlinie mit der EXPERT_IN_SUBWINDOW-Konstante der Defines.mqh -Datei hinzu. Es ist erforderlich, die Protokollierung von Fehlern zu eliminieren, die auftreten, wenn Sie versuchen das Handle eines Indikators abzufragen, welches in dem angegebenen Verzeichnis nicht verfügbar ist.

Der Standardwert ist false, was bedeutet, dass der Modus deaktiviert ist und die grafische Benutzeroberfläche im Hauptdiagramm Fenster erstellt werden (siehe folgenden Code).

//+----------------------------------------------------------------
//|                                                      Defines.mqh |
//|                        Copyright 2015, MetaQuotes Software Corp. |
//|                                              http://www.mql5.com |
//+----------------------------------------------------------------
//--- "Expert im subwindow" Modus
#define EXPERT_IN_SUBWINDOW false
...

Wenn sie die grafische Benutzeroberfläche in einem Unterfenster erzeugen wollen, legen Sie den Wert true fest und die Ressource mit dem Platzhalter-Indikator, der die Hauptdatei der MQL-Anwendung besitzt

//+----------------------------------------------------------------
//|                                                TestLibrary01.mq5 |
//|                        Copyright 2016, MetaQuotes Software Corp. |
//|                                              http://www.mql5.com |
//+----------------------------------------------------------------
#property copyright "2016, MetaQuotes Software Corp."
#property link      "http://www.mql5.com"
//--- Einbeziehen des Indikators für den "Expert in subwindow" Modus
#resource \\Indicators\\SubWindow.ex5
...

Als nächstes beziehen Sie den SubWindow.ex5 Indikator mit ein. Der vollständige Code des Indikators SubWindow.ex5 wird in der Liste unten dargestellt. Die Programm Parameter geben an, dass sich der Indikator in einem Unterfenster des Charts befindet. Die Anzahl der Speicher und Serien ist 0, und die Maximum- und Minimum-Angaben der vertikalen Skala sind ebenso 0. 

Der Indikator und Experte werden Nachrichten austauschen. Die Sache ist, das der Indikator nicht weiß, welche Modi für das Plotten der grafischen Oberfläche im Assistenten ausgewählt werden. Es muss eine Nachricht weitergegeben werden, wenn Entwickler beschließt, dass das Unterfenster eine feste Höhe besitzt, sowie die Höhenwerte in Pixeln. Dafür benötigen wir einen weiteren ON_SUBWINDOW_CHANGE_HEIGHT Identifizierer für das Erzeugen dieser Nachricht. Dieser Identifizierer sollte auch in der Defines.mqh Datei enthalten sein, damit dieser in allen Anwendungen verfügbar ist, welche die Bibliothek für das Erzeugen eines grafischen Interfaces verwenden. 

Damit der Expert Advisor das Event für das Ändern der Höhe des Unterfensters verarbeitet: Nach einer erfolgreichen Initialisierung des Indikators, und während des ersten automatischen Aufrufes der ::OnCalculate() Funktion (wenn der prev_calculated Wert 0 ist), wird die Nachricht an den Expert Advisor übergeben. Um sicherzustellen, dass die Nachricht von der SubWindow.ex5 -Indikator, ON_SUBWINDOW_CHANGE_HEIGHT -Ereignis-ID kam, wird der Name des Programms als String -Parameter (Sparam) gesendet. Diese Nachricht wird in dem Eventhandler der CWindow Klasse nachverfolgt. Wenn alle Bedingungen zutreffen, dann wird dem Indikator eine Antwort mit dem selben(1) Identifizierer (ON_SUBWINDOW_CHANGE_HEIGHT), (2) dem Wert der Höhe in dem long Parameter des Events (lparam) und (3) dem Namen des Experten gesendet. Sobald die Nachricht empfangen wurde, wird sie in der ::OnChartEvent() Funktion verarbeitet. Nach der Überprüfung des Namens, wird die Höhe des Unterfensters entsprechend dem übergebenen Wert gesetzt.  

//+----------------------------------------------------------------
//|                                                    SubWindow.mq5 |
//|                        Copyright 2016, MetaQuotes Software Corp. |
//|                                              http://www.mql5.com |
//+----------------------------------------------------------------
#property copyright "2016, MetaQuotes Software Corp."
#property link      "http://www.mql5.com"
#property indicator_separate_window
#property indicator_plots   0
#property indicator_buffers 0
#property indicator_minimum 0.0
#property indicator_maximum 0.0
//--- Program name
#define PROGRAM_NAME ::MQLInfoString(MQL_PROGRAM_NAME)
//--- Identifizierer des Events für das Ändern der Höhe des Unterfenster
#define ON_SUBWINDOW_CHANGE_HEIGHT (25)
//+----------------------------------------------------------------
//| Benutzerdefinierte Indikator-Initialisierungsfunktion                         |
//+----------------------------------------------------------------
int OnInit(void)
  {
//--- Der kurze Name des Indikators
   ::IndicatorSetString(INDICATOR_SHORTNAME,PROGRAM_NAME);
//--- Initialisierung erfolgreich
   return(INIT_SUCCEEDED);
  }
//+----------------------------------------------------------------
//| Deinitialization                                                 |
//+----------------------------------------------------------------
void OnDeinit(const int reason)
  {
  }
//+----------------------------------------------------------------
//| Benutzerdefinierte Indikator- Iterationsfunktion                              |
//+----------------------------------------------------------------
int OnCalculate(const int    rates_total,
                const int    prev_calculated,
                const int    begin,
                const double &price[])
  {
//--- Wenn die Initialisierung erfolgreich war
   if(prev_calculated<1)
      //--- Senden einer Nachricht an den Experten, um die Größe des Unterfensters von ihm zu bekommen
      ::EventChartCustom(0,ON_SUBWINDOW_CHANGE_HEIGHT,0,0.0,PROGRAM_NAME);
u//---
   return(rates_total);
  }
//+----------------------------------------------------------------
//| ChartEvent Funktion                                              |
//+----------------------------------------------------------------
void OnChartEvent(const int    id,
                  const long   &lparam,
                  const double &dparam,
                  const string &sparam)
  {
//--- Verarbeitendes Events für die Veränderung der Höhe des Unterfensters
   if(id==CHARTEVENT_CUSTOM+ON_SUBWINDOW_CHANGE_HEIGHT)
     {
      //--- Akzeptiere nur Nachrichten von dem Experten (Name)
      if(sparam==PROGRAM_NAME)
         return;
      //--- Ändern der Höhe des Unterfensters
      ::IndicatorSetInteger(INDICATOR_HEIGHT,(int)lparam);
     }
  }
//+----------------------------------------------------------------

Ein Codeblock sollte an den Ereignishandler der CWindow Klasse eingefügt werden, wie in der folgenden Liste dargestellt. Sobald ein Ereignis mit der Kennung ON_SUBWINDOW_CHANGE_HEIGHT eintrifft, müssen mehrere Überprüfungen durchgeführt werden. Das Programm verlässt die Methode, falls:

  • Die Nachricht von dem Experten für den Indikator gesendet wurde
  • Falls es sich bei dem Programm nicht um einen Experten handelt
  • Der Modus "feste Höhe" des Experten nicht gesetzt ist 
//+----------------------------------------------------------------
//| Chart Eventhandler                                              |
//+----------------------------------------------------------------
void CWindow::OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam)
  {
...
//--- Verarbeitendes Events für die Veränderung der Höhe des Unterfensters
   if(id==CHARTEVENT_CUSTOM+ON_SUBWINDOW_CHANGE_HEIGHT)
     {
      //--- Abbrechen, falls es sich auf eine Nachricht von dem Experten handelt
      if(sparam==PROGRAM_NAME)
         return;
      //--- Abbrechen, falls das Programm kein Experte ist
      if(CElement::ProgramType()!=PROGRAM_EXPERT)
         return;
      //--- Abbrechen, falls der Modus für die feste Höhe des Unterfensters nicht gesetzt ist
      if(!m_height_subwindow_mode)
         return;
      //--- Berechne und verändere die Höhe des Unterfensters
      m_subwindow_height=(m_is_minimized)? m_caption_height+3 : m_bg_full_height+3;
      ChangeSubwindowHeight(m_subwindow_height);
      return;
     }
  }

Wenn alle Prüfungen bestanden sind, wird die Höhe für das Unterfenster unter Berücksichtigung des aktuellen Zustands des Fensters (minimiert/maximiert) berechnet. Danach wird die CWindow::ChangeSubwindowHeight()-Methode aufgerufen, welche auch leicht abgeändert wurde. (siehe Code unten). Sein Zweck ist, dass, wenn es sich bei dem Programmtyp um einen «Expert» handelt, eine Meldung für den SubWindow.ex5-Indikator generiert wird

//+----------------------------------------------------------------
//| Veränderung der Höhe des Indikator Unterfensters                  |
//+----------------------------------------------------------------
void CWindow::ChangeSubwindowHeight(const int height)
  {
//--- Falls sich das grafischen Interface nicht in einem Unterfenster befindet, oder das Programm vom Typ "Script" ist
   if(CElement::m_subwin<=0 || CElement::m_program_type==PROGRAM_SCRIPT)
      return;
//--- Falls die Höhe des Unterfensters geändert werden muss
   if(height>0)
     {
      //--- Falls es sich bei dem Programm um einen Indikator handelt
      if(CElement::m_program_type==PROGRAM_INDICATOR)
        {
         if(!::IndicatorSetInteger(INDICATOR_HEIGHT,height))
            ::Print(__FUNCTION__," > Failed to change the height of indicator subwindow! Error code: ",::GetLastError());
        }
      //--- Falls es sich bei dem Programm um einen Experten handelt
      else
        {
         //--- Sende eine Nachricht an den SubWindow.ex5 Indikator, mit der Information, dass die Höhe des Fensters geändert werden muss
         ::EventChartCustom(m_chart_id,ON_SUBWINDOW_CHANGE_HEIGHT,(long)height,0,PROGRAM_NAME);
        }
     }
  }

Die Engine der Bibliothek (die CWndEvents -Klasse) erfordert auch bestimmte Ergänzungen, um die Nummer des Unterfenstzers ermitteln und überprüfen zu können, in welchem die grafische Oberfläche der MQL-Anwendung des Typs "Expert" platziert wurde. Der Programmcode für den "Expert in subwindow" Modus sollte ebenfalls der CWndEvents::DetermineSubwindow() Methode hinzugefügt werden. Die Codeauflistung unten zeigt die verkürzte Version dieser Methode. Eintritt in den Block erfolgt unter der Bedingung, dass der Programmtyp "Experte" ist. Als nächstes kommt die Prüfung, ob "im Unterfenster" Expertenmodus aktiviert ist. Wenn aktiviert, erhalten Sie das Handle des Indikators SubWindow.ex5 Und falls es damit keine Probleme gibt, dann wird zuerst die aktuelle Anzahl der Unterfenster im Diagrammfenster bestimmt. Der erhaltene Wert definiert die Anzahl der SubWindow.ex5 Indikator-Unterfenster, die durch die :: ChartIndicatorAdd() Funktion festgelegt wird. . Dann, falls es keine Fehler bei der Festlegung des Unterfensters gab, wird die (1) Nummer des unter Fensters gespeichert, (2) die aktuelle Anzahl an Unterfenstern (3) und der kurze Name des SubWindow.ex5 Indikators.  

//+----------------------------------------------------------------
//| Klasse für das Behandeln von Events                                         |
//+----------------------------------------------------------------
class CWndEvents : public CWndContainer
  {
protected:
   //--- Handle des Expert Subwindows
   int               m_subwindow_handle;
   //--- Name des expert subwindow
   string            m_subwindow_shortname;
   //--- Die Anzahl der Subwindows auf dem Chart nach dem Festlegen des Expert-subwindows
   int               m_subwindows_total;
  };
//+----------------------------------------------------------------
//| Identifizierung der Nummer des Unterfensters                                |
//+----------------------------------------------------------------
void CWndEvents::DetermineSubwindow(void)
  {
//--- Abbrechen, falls es sich um ein Skript handelt
//--- Zurücksetzen der letzten Fehlermeldung

//--- Falls es sich bei dem Typ des Programms um einen Experten handelt
   if(PROGRAM_TYPE==PROGRAM_EXPERT)
     {
      //--- Abbrechen, falls sich das grafische Interface des Experten in dem Hauptfenster befindet
      if(!EXPERT_IN_SUBWINDOW)
         return;
      //--- Abfrage des Handles des Platzhalter Indikators (leeres subwindow)
      m_subwindow_handle=iCustom(Symbol(),Period(),"::Indicators\\SubWindow.ex5");
      //--- Falls es keinen solchen Indikator gibt, dann erzeuge eine Fehlermeldung in dem Logfile
      if(m_subwindow_handle==INVALID_HANDLE)
         ::Print(__FUNCTION__," > Error getting the indicator handle in the directory ::Indicators\\SubWindow.ex5 !");
      //--- Falls wir ein Handle erhalten haben, dann existiert dieser Indikator und er wurde in der Anwendung als Resource eingefügt,
      //    Und dieses bedeutet, dass das grafische Interface der Anwendung in einem Unterfenster platziert werden muss.
      else
        {
         //--- Abfrage der Anzahl der Subwindows des Charts
         int subwindows_total=(int)::ChartGetInteger(m_chart_id,CHART_WINDOWS_TOTAL);
         //--- Festlegen des Unterfenster für das grafische Interface des Expert Advisors
         if(::ChartIndicatorAdd(m_chart_id,subwindows_total,m_subwindow_handle))
           {
            //--- Abspeichern der Nummer des Unterfensters und der Anzahl der Unterfenster des aktuellen Charts
            m_subwin           =subwindows_total;
            m_subwindows_total =subwindows_total+1;
            //--- Abfrage und Abspeichern des kurzen Namens des Unterfensters des Experten
            m_subwindow_shortname=::ChartIndicatorName(m_chart_id,m_subwin,0);
           }
         //--- Falls das Unterfenster nicht gesetzt werden konnte
         else
            ::Print(__FUNCTION__," > Error setting the expert subwindow! Error code: ",::GetLastError());
        }
      u//---
      return;
     }
//--- Identifizierung des Indikator-Fensters
//--- Abbrechen, falls die Nummer nicht identifiziert werden konnte
//--- Falls es sich nicht um das Haupt-Chartfenster handelt
...
  }

Für die einwandfreie Funktion des graphischen Interfaces ist es auch notwendig, dass die Anzahl der Unterfenster korrigiert wird, sobald andere Indikatoren hinzugefügt oder entfernt werden. Falls der Anwender das Unterfenster mit dem Experten entfernt, dann wird auch der Experte entfernt und es wird eine Nachricht in dem Expert-Jpurnal hinterlassen, in welcher auch der Grund für das Entfernen von dem Chart aufgeführt ist. Dafür wurde die CWndEvents::CheckExpertSubwindowNumber() Methode bereits implementiert. Der Eintritt zu dieser Methode erfolgt unter der Bedingung, dass das Programm über eine Art von "Experten" verfügt. Wenn die Überprüfung erfolgreich war, wird die Anzahl der Kindfenster im Diagrammfenster berechnet. Wenn sich herausstellt, dass sich die Anzahl der Unterfenster nicht geändert hat, dann verlässt das Programm die Methode. 

Dann ist es notwendig, das Experten-Unterfenster in einem Zyklus zu finden, und wenn es existiert – überprüfen sie, ob sich seine Unterfenster-Nummer verändert hat. Die Anzahl der Unterfenster könnte sich durch hinzufügen oder entfernen eines Indikators geändert haben, das sich auch in dem eigenen Unterfenster befand. Wenn sich die Unterfenster-Nummer geändert hat, ist es notwendig, seine Nummer zu speichern und den Wert in allen Steuerelementen des Hauptfensters zu aktualisiern

Wenn das Unterfenster nicht gefunden wird, kann nur einen Grund dafür geben: Es wurde gelöscht. In diesem Fall, wird der Experte auch aus dem Diagramm entfernt

class CWndEvents : public CWndContainer
  {
private:
   //--- Überprüfe aktualisiere die Nummer des Unterfensters
   void              CheckExpertSubwindowNumber(void);
  };
//+----------------------------------------------------------------
//| Überprüfe und aktualisiere die Nummer des Unterfenster                     |
//+----------------------------------------------------------------
void CWndEvents::CheckExpertSubwindowNumber(void)
  {
//--- Abbrechen, falls es sich nicht um einen Experten handelt
   if(PROGRAM_TYPE!=PROGRAM_EXPERT)
      return;
//--- Abfrage der Anzahl der Subwindows des Charts
   int subwindows_total=(int)::ChartGetInteger(m_chart_id,CHART_WINDOWS_TOTAL);
//--- Abbrechen, falls sich die Anzahl der Unterfenster und die Nummer des Indikators nicht geändert haben
   if(subwindows_total==m_subwindows_total)
      return;
//--- Abspeichern der aktuellen Nummer des Unterfensters
   m_subwindows_total=subwindows_total;
//--- Für die Überprüfung, ob das Unterfenster des Experten existiert
   bool is_subwindow=false;
//--- Finde das Unterfenster des Experten
   for(int sw=0; sw<subwindows_total; sw++)
     {
      //--- Breche den Zyklus ab, falls das Unterfenster des Experten gefunden wurde
      if(is_subwindow)
         break;
      //--- Die Nummer des Indikators in diesem Fenster/unter Fenster
      int indicators_total=::ChartIndicatorsTotal(m_chart_id,sw);
      //--- Durchlauf aller Indikatoren in diesem Fenster 
      for(int i=0; i<indicators_total; i++)
        {
         //--- Abfrage des Kurznamen des Indikators
         string indicator_name=::ChartIndicatorName(m_chart_id,sw,i);
         //--- Falls es sich nicht um das unter Fenster des Experten handelt, fahre mit dem nächsten fort
         if(indicator_name!=m_subwindow_shortname)
            continue;
         //--- Markieren, dass der Experte ein Unterfenster besitzt
         is_subwindow=true;
         //--- Falls ich die Nummer des unter Fensters geändert hat, dann 
         //    ist es notwendig, die neue Nummer in allen Controls des Hauptformulars abzuspeichern
         if(sw!=m_subwin)
           {
            //--- Abspeichern der Nummer des Unterfensters
            m_subwin=sw;
            //--- Abspeichern der Nummer in allen Controls des Hauptformulars innerhalb des Interfaces
            int elements_total=CWndContainer::ElementsTotal(0);
            for(int e=0; e<elements_total; e++)
               m_wnd[0].m_elements[e].SubwindowNumber(m_subwin);
           }
         u//---
         break;
        }
     }
//--- Falls die Nummer des unter Fensters nicht gefunden wurde, dann entferne den Experten
   if(!is_subwindow)
     {
      ::Print(__FUNCTION__," > Deleting expert subwindow causes the expert to be removed!");
      //--- Entfernen des EA von dem Chart
      ::ExpertRemove();
     }
  }

 

2. In der vorherigen Version der Bibliothek haben wir die Fähigkeit, den automatischen Wechsel der Formularbreite zu ändern, eingeführt. Wir fügen eine ähnliche Funktion für die Höhe ein. Der ON_WINDOW_CHANGE_SIZE Bezeichner für das Event der Veränderung der Größe des Formulars ist in diesem Fall nicht verwendbar, da die Veränderung der Breite und der Höhe als separate Events behandelt werden. Daher muss die Defines.mqh anstelle des ON_WINDOW_CHANGE_SIZEBezeichner zwei separate Bezeichner besitzen, so wie es in dem nachfolgenden Programmcode gezeigt wird. Der entsprechenden Ersatz des Bezeichners wurde in anderen Bibliotheksdateien ebenfalls durchgeführt.  

#define ON_WINDOW_CHANGE_XSIZE     (3)  // Change in the window size along the X axis
#define ON_WINDOW_CHANGE_YSIZE     (4)  // Change in the window size along the Y axis

Für die Veränderung der Höhe wurde die CWindow::ChangeWindowHeight() Methode hinzugefügt. Bei dem Aufruf der Methode für das Verändern der Größe des Formulars, wird am Ende eine Nachricht über die ausgeführte Aktion generiert

//+----------------------------------------------------------------
//| Klasse für das Erzeugen einer Form mit Controls                           |
//+----------------------------------------------------------------
class CWindow : public CElement
  {
public:
   //--- Verwalten der Größe
   void              ChangeWindowHeight(const int height);
  };
//+----------------------------------------------------------------
//| Verändert die Höhe des Fensters                                 |
//+----------------------------------------------------------------
void CWindow::ChangeWindowHeight(const int height)
  {
//--- Falls die Höhe nicht geändert wurde, verlassen
   if(height==m_bg.YSize())
      return;
//--- Abbrechen, falls das Fenster minimiert ist
   if(m_is_minimized)
      return;
//--- Aktualisierende Höhe für den Hintergrund
   CElement::YSize(height);
   m_bg.YSize(height);
   m_bg.Y_Size(height);
   m_bg_full_height=height;
//--- Eine Nachricht über die Veränderung der Fenstergröße
   ::EventChartCustom(m_chart_id,ON_WINDOW_CHANGE_YSIZE,(long)CElement::Id(),0,"");
  }

Damit sich die Höhe des Formulars automatisch ändert, sollte der Anwender, der eine MQL Anwendung entwickelt, den entsprechenden Modus unter Verwendung der CElement::AutoYResizeMode()-Methode festlegen: 

//+----------------------------------------------------------------
//| Base control class                                               |
//+----------------------------------------------------------------
class CElement#
  {
protected:
   //--- Modus für die automatische Größenänderung von Controls
   bool              m_auto_yresize_mode;
   u//---
public:
   //--- (1) Modus für die automatische Höhenänderung eines Controls
   bool              AutoYResizeMode(void)                     const { return(m_auto_yresize_mode);          }
   void              AutoYResizeMode(const bool flag)                { m_auto_yresize_mode=flag;             }
  };

Nun, falls der Modus für die automatische Änderung der Größe aktiviert wurde, und sich die Größe des Chart-Fensters ändert, wird der Eventhandler des Formulars für die Änderung der Größe des Formulars basierend auf dem CHARTEVENT_CHART_CHANGE Event ausgeführt. Dieses arbeitet sowohl in dem unter Fenster als auch in dem Charts-Fenster. Es ist möglich, einem der Modi oder beides gleichzeitig zu aktivieren. 

//--- Event für die Veränderung der Eigenschaften des Charts
   if(id==CHARTEVENT_CHART_CHANGE)
     {
      //--- Falls der Button nicht geklickt ist
      if(m_clamping_area_mouse==NOT_PRESSED)
        {
         //--- Abfrage der Größe des Chartfensters
         SetWindowProperties();
         //--- Anpassung der Koordinaten
         UpdateWindowXY(m_x,m_y);
        }
      //---Ändern der Breite, wenn der Modus aktiviert ist
      if(CElement::AutoXResizeMode())
         ChangeWindowWidth(m_chart.WidthInPixels()-2);
      //--- Ändern der Höhe, wenn der Modus aktiviert ist
      if(CElement::AutoYResizeMode())
         ChangeWindowHeight(m_chart.HeightInPixels(m_subwin)-3);
      u//---
      return;
     }

 

3. Der Modus für die automatische Höhenänderung gilt auch für alle Steuerelemente der Bibliothek. In der aktuellen Version, funktioniert dieses jedoch nur für die folgenden Steuerelemente:

  • CTabs – simple tabs.
  • CIconTabs – tabs with icons.
  • CCanvasTable – rendered table.
  • CLineGraph – linear chart.

Ähnlich, wie wir es schon mit der virtuellen Methode CElement::ChangeWidthByRightWindowSide() Für das Ändern der Breite eines Controls innerhalb der CElement Klasse gemacht haben, fügen wir nun auch eine entsprechendeMethode für die automatische Veränderung der Höhe hinzu. Darüber hinaus lassen Sie uns die Methoden zur Einstellung des Offsets vom unteren Rand des Formulars erstellen, ähnlich wie die zuvor hinzugefügten Offsets vom rechten Rand des Formulars, wenn wir seine Breite ändern. 

class CElement#
  {
protected:
   //---Offset vom rechten/unteren Rand des Formulars im Automatik-Modus für das Ändern der Breite/Höhe
   int               m_auto_xresize_right_offset;
   int               m_auto_yresize_bottom_offset;
   u//---
public:
   //--- Abrufen/festlegen des Offsets vom unteren Rand des Formulars
   int               AutoYResizeBottomOffset(void)             const { return(m_auto_yresize_bottom_offset); }
   void              AutoYResizeBottomOffset(const int offset)       { m_auto_yresize_bottom_offset=offset;  }
   u//---
public:
   //--- Verändern der Höhe an der unteren Ecke des Fensters
   virtual void      ChangeHeightByBottomWindowSide(void) {}
  };

Every control will have its own implementation of the ChangeWidthByRightWindowSide() method, which considers the unique features and modes. As an example, the listing below shows the code of this method in the CCanvasTable class: 

//+----------------------------------------------------------------
//| Die Höhe am unteren Rand des Fensters ändern |
//+----------------------------------------------------------------
void CCanvasTable::ChangeHeightByBottomWindowSide(void)
  {
//--- Verlassen, wenn der Verankerungs-Modus, am unteren Rand des Formulars aktiviert ist  
   if(m_anchor_bottom_window_side)
     return;
//--- Koordinaten
   int y=0;
//--- Größe
   int x_size=(m_auto_xresize_mode)? m_wnd.X2()-m_area.X()-m_auto_xresize_right_offset : m_x_size;
   int y_size=m_wnd.Y2()-m_area.Y()-m_auto_yresize_bottom_offset;
//--- Verlassen, wenn die Größe kleiner als angegeben ist
   if(y_size<60)
     return;
//---die neue Größe des Tabellenhintergrundes festlegen
   ChangeMainSize(x_size,y_size);
---Berechnen der Tabellegrößen
   CalculateTableSize();
//--- Überprüfen, ob eine Scrollbar existiert
   bool is_scrollh=!(m_table_visible_x_size>=m_table_x_size);
   bool is_scrollv=!(m_table_visible_y_size>=m_table_y_size);
//--- Der Offset relativ zu der Scrollbar
   int offset=(is_scrollh || (!is_scrollh && !is_scrollv))? 0 : 2;
//--- Berechne und setze die neue Koordinate für die horizontale Scrollbar
   y=m_area.Y2()-m_scrollh.ScrollWidth();
   m_scrollh.YDistance(y);
//--- Initialisiere die horizontale Scrollbar für die neue Größe
   m_scrollh.Reinit(m_table_x_size,m_table_visible_x_size);
//--- Berechne und ändere die Höhe der vertikalen Scrollbar
   m_scrollv.Reinit(m_table_y_size,m_table_visible_y_size-offset);
   m_scrollv.ChangeYSize((is_scrollh)? m_table_visible_y_size+2 : m_table_visible_y_size);
//--- Falls die vertikale Scrollbar nicht benötigt wird
   if(!is_scrollv)
     {
      //--- Verstecke die vertikale Scrollbar
      m_scrollv.Hide();
      //--- Ändere die Breite der Horizontalen Scrollbar
      m_scrollh.ChangeXSize(m_area.XSize());
     }
   else
     {
      //--- Zeige die vertikale Scrollbar
      if(CElement::IsVisible())
         m_scrollv.Show();
      //--- Berechne und ändere die Breite der Horizontalen Scrollbar
      m_scrollh.ChangeXSize(m_area.XSize()-m_scrollv.ScrollWidth()+1);
     }
//--- Verwaltet die Sichtbarkeit der Horizontalen Scrollbar
   if(CElement::IsVisible())
     {
      if(!is_scrollh) m_scrollh.Hide();
      else m_scrollh.Show();
     }
//--- Neue Größe der Tabelle
   ChangeTableSize(m_table_x_size,m_table_y_size,m_table_visible_x_size,m_table_visible_y_size-offset);
//--- Zeichne die Tabelle
   DrawTable();
//--- Aktualisiere die Positionen der Objekte
   Moving(m_wnd.X(),m_wnd.Y());
  }

For all this to work, certain additions and changes should be made to the CWndEvents class. Da die Größenänderungs-Ereignisse (Breite und Höhe) jetzt mit eindeutigen Kennungen generiert werden, sind zwei separate Methoden für deren Handhabung erforderlich. 

//+----------------------------------------------------------------
//| Klasse für das Behandeln von Events                                         |
//+----------------------------------------------------------------
class CWndEvents : public CWndContainer
  {
private:
   //--- Verarbeitet die Veränderung der Fenstergröße
   bool              OnWindowChangeXSize(void);
   bool              OnWindowChangeYSize(void);
  };
//+----------------------------------------------------------------
//| ON_WINDOW_CHANGE_XSIZE event                                     |
//+----------------------------------------------------------------
bool CWndEvents::OnWindowChangeXSize(void)
  {
//--- Falls es ein Signal für die "Neue Größe der Controls" ist
   if(m_id!=CHARTEVENT_CUSTOM+ON_WINDOW_CHANGE_XSIZE)
      return(false);
//--- Index des aktiven Fensters
   int awi=m_active_window_index;
//--- Falls die Fenster-Bezeichner übereinstimmen
   if(m_lparam!=m_windows[awi].Id())
      return(true);
//--- Verändere die Breite aller Controls, mit Ausnahme des Formulars
   int elements_total=CWndContainer::ElementsTotal(awi);
   for(int e=0; e<elements_total; e++)
     {
      //--- Falls es sich um ein Fenster handelt, fahre mit dem nächsten fort
      if(m_wnd[awi].m_elements[e].ClassName()=="CWindow")
         continue;
      //--- Wenn der Modus aktiviert ist, justiere die Breite
      if(m_wnd[awi].m_elements[e].AutoXResizeMode())
         m_wnd[awi].m_elements[e].ChangeWidthByRightWindowSide();
     }
u//---
   return(true);
  }
//+----------------------------------------------------------------
//| ON_WINDOW_CHANGE_YSIZE event                                     |
//+----------------------------------------------------------------
bool CWndEvents::OnWindowChangeYSize(void)
  {
//--- Falls es ein Signal für die "Neue Größe der Controls" ist
   if(m_id!=CHARTEVENT_CUSTOM+ON_WINDOW_CHANGE_YSIZE)
      return(false);
//--- Index des aktiven Fensters
   int awi=m_active_window_index;
//--- Falls die Fenster-Bezeichner übereinstimmen
   if(m_lparam!=m_windows[awi].Id())
      return(true);
//--- Verändere die Breite aller Controls, mit Ausnahme des Formulars
   int elements_total=CWndContainer::ElementsTotal(awi);
   for(int e=0; e<elements_total; e++)
     {
      //--- Falls es sich um ein Fenster handelt, fahre mit dem nächsten fort
      if(m_wnd[awi].m_elements[e].ClassName()=="CWindow")
         continue;
      //--- Falls der Modus aktiviert ist, justiere die Höhe
      if(m_wnd[awi].m_elements[e].AutoYResizeMode())
         m_wnd[awi].m_elements[e].ChangeHeightByBottomWindowSide();
     }
u//---
   return(true);
  }

Die CWndEvents::OnWindowChangeXSize() und CWndEvents::OnWindowChangeYSize() Methoden werden in der gemeinsamen CWndEvents::ChartEventCustom() Methode für das Behandeln von Events aufgerufen. Die Codeauflistung unten zeigt die verkürzte Version dieser Methode. 

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

//--- Wenn es sich um ein Signal für die neue Größe von Controls entlang der x-Achse handelt
   if(OnWindowChangeXSize())
      return;
//--- Wenn es sich um ein Signal für die neue Größe von Controls entlang der y-Achse handelt
   if(OnWindowChangeYSize())
      return;

//--- Falls es sich um ein Signal zum Verstecken des Kontextmenüs unterhalb des auslösenden Menüelementes handelt
//--- Falls es sich um ein Signal für das Verstecken aller Kontextmenüs handelt

//--- Falls es sich um ein Signal zum Öffnen eines Dialogfensters handelt
//--- Falls es sich um ein Signal zum Schließen eines Dialogs Fensters handelt
//--- Falls es sich um ein Signal handelt, die Farben alle Elemente eines angegebene Formulars zurückzusetzen
//--- Wenn es sich um ein Signal zum Zurücksetzen der Prioritäten der linken Maustaste handelt
//--- Wenn es sich um ein Signal zum Wiederherstellen der Prioritäten der linken Maustaste handelt
  }

Jetzt werden, wenn das Chartfenster in seiner Größe geändert wird und der Modus zur Größenänderung des Formulars und der Steuerelemente aktiviert ist, die darin angegebenen Controls automatisch in ihrer Größe angepasst. 

Die Screenshots unten zeigen ein Beispiel für eine grafische Oberfläche in einer MQL-Anwendung in dem Haupt-Chartfenster. Für das Anwendungsfenster (CWindow) sind die Modi für automatische Breiten- und Höhenanpassung an das Chartfenster eingestellt. Für die «Hauptmenü» (CMenuBar) und «Statusleiste» (CStatusBar) Controls, ist die automatische Anpassung der Breite und Höhe an das MQL-Anwendungsfenster eingestellt. Für die «Tabs» (CTabs) und «Rendered table» (CCanvasTable) Controls, ist der Modus für die automatische Anpassung der Breite und der Höhe entsprechend der Größe des Formulars aktiviert und die Offsets von der unteren Ecke der MQL Anwendung wurden angegeben.

Abbildung  1. Mindestgröße des Terminal-Fensters. Grafische Oberfläche einer MQL-Anwendung mit automatischer Größenänderung. 

Abbildung 1. Mindestgröße des Terminal-Fensters. Grafische Oberfläche einer MQL-Anwendung mit automatischer Größenänderung.


Wenn sich die Größe des Terminal-Fensters ändert und die oben angegebenen Modi aktiviert sind, dann ändert sich auch die Größe des grafischen Interfaces der MQL Anwendung entsprechend.

Abbildung  2. Wenn sich die Größe des Terminal-Fensters ändert, wird auch die Größe des grafischen sInterface der MQL-Anwendung in seiner Größe geändert. 

Abbildung 2. Wenn sich die Größe des Terminal-Fensters ändert, wird auch die Größe des grafischen sInterface der MQL-Anwendung in seiner Größe geändert.


4. Bei der Gestaltung der grafischen Benutzeroberfläche mit veränderlichen Formulargrößen ist es oft notwendig, dass die Steuerelemente an der rechten oder unteren Kante des Anwendungsfensters positioniert werden. Früher gab es eine Option, mit der Sie einfach die Koordinaten für das Steuerelement im Vergleich zu dem oberen linken Punkt des Formulars angeben konnten. Nun gibt es vier Optionen für die Neupositionierung der Controls relativ zu dem Formular.

  • Oben links.
  • Oben rechts.
  • Unten rechts.
  • Unten links.

Um diese Idee umzusetzen, fügen wir der Basisklasse der Steuerelemente (CElement) noch zwei Methoden zur Einstellung/Abfrage der Modi der Controls-Positionierung an dem rechten und unteren Rand des Formulars hinzu: 

class CElement#
  {
protected:
   //--- Ankerpunkte des Steuerelements an dem rechten und unteren Rand des Fensters
   bool              m_anchor_right_window_side;
   bool              m_anchor_bottom_window_side;
   u//---
public:
   //--- Modus (Abfrage/Setzen) der Ankerpunkte der Controls an die (1) rechte und (2) untere Kante des Fensters
   bool              AnchorRightWindowSide(void)               const { return(m_anchor_right_window_side);   }
   void              AnchorRightWindowSide(const bool flag)          { m_anchor_right_window_side=flag;      }
   bool              AnchorBottomWindowSide(void)              const { return(m_anchor_bottom_window_side);  }
   void              AnchorBottomWindowSide(const bool flag)         { m_anchor_bottom_window_side=flag;     }
  };

Damit alles einwandfrei funktioniert, wurden alle Klassen der Controls überarbeitet. Als Beispiel ist hier der Programmcode der Methode für die Erzeugung des Text-Labels für das «Checkbox» (CCheckBox) Control aufgeführt. Achten Sie auf die Linien, die die Koordinaten Offsets für das grafische Objekt berechnen. 

//+----------------------------------------------------------------
//| Erzeugen der Text-Label für die Checkbox                           |
//+----------------------------------------------------------------
bool CCheckBox::CreateLabel(void)
  {
//--- Den Objektnamen bilden
   string name=CElement::ProgramName()+"_checkbox_lable_"+(string)CElement::Id();
//--- Koordinaten
   int x =(m_anchor_right_window_side)? m_x-m_label_x_gap : m_x+m_label_x_gap;
   int y =(m_anchor_bottom_window_side)? m_y-m_label_y_gap : m_y+m_label_y_gap;
//--- Die Textfarbe entsprechend dem Status
   color label_color=(m_check_button_state) ? m_label_color : m_label_color_off;
//--- Setzen des Objektes
   if(!m_label.Create(m_chart_id,name,m_subwin,x,y))
      return(false);
//--- Setzen der Eigenschaften
   m_label.Description(m_label_text);
   m_label.Font(FONT);
   m_label.FontSize(FONT_SIZE);
   m_label.Color(label_color);
   m_label.Corner(m_corner);
   m_label.Anchor(m_anchor);
   m_label.Selectable(false);
   m_label.Z_Order(m_zorder);
   m_label.Tooltip("\n");
//--- Ränder von den Kanten
   m_label.XGap((m_anchor_right_window_side)? x : x-m_wnd.X());
   m_label.YGap((m_anchor_bottom_window_side)? y : y-m_wnd.Y());
//--- Initialisierung des Arrays
   CElement::InitColorArray(label_color,m_label_color_hover,m_label_color_array);
//--- Abspeichern des Objekt-Pointers
   CElement::AddToArray(m_label);
   return(true);
  }

Die Moving() Methoden aller Controls in der Bibliothek berücksichtigen nun die Modi für die Positionierung. Als Beispiel zeigen wir hier den Programmcode der CCheckBox::Moving() Methode: 

//+----------------------------------------------------------------
//| Moving controls                                                  |
//+----------------------------------------------------------------
void CCheckBox::Moving(const int x,const int y)
  {
//--- Abbrechen, falls das Element versteckt ist
   if(!CElement::IsVisible())
      return;
//--- Wenn der Ankerpunkt die rechte Seite ist
   if(m_anchor_right_window_side)
     {
      //--- Abspeichern der Koordinaten in den variablen der Elemente
      CElement::X(m_wnd.X2()-XGap());
      //--- Abspeichern der Koordinaten in den variablen der Objekte
      m_area.X(m_wnd.X2()-m_area.XGap());
      m_check.X(m_wnd.X2()-m_check.XGap());
      m_label.X(m_wnd.X2()-m_label.XGap());
     }
   else
     {
      //--- Abspeichern der Koordinaten in den variablen der Objekte
      CElement::X(x+XGap());
      //--- Abspeichern der Koordinaten in den variablen der Objekte
      m_area.X(x+m_area.XGap());
      m_check.X(x+m_check.XGap());
      m_label.X(x+m_label.XGap());
     }
//--- Wenn der Ankerpunkt unten ist
   if(m_anchor_bottom_window_side)
     {
      //--- Abspeichern der Koordinaten in den variablen der Elemente
      CElement::Y(m_wnd.Y2()-YGap());
      //--- Abspeichern der Koordinaten in den variablen der Objekte
      m_area.Y(m_wnd.Y2()-m_area.YGap());
      m_check.Y(m_wnd.Y2()-m_check.YGap());
      m_label.Y(m_wnd.Y2()-m_label.YGap());
     }
   else
     {
      //--- Abspeichern der Koordinaten in den variablen der Objekte
      CElement::Y(y+YGap());
      //--- Abspeichern der Koordinaten in den variablen der Objekte
      m_area.Y(y+m_area.YGap());
      m_check.Y(y+m_check.YGap());
      m_label.Y(y+m_label.YGap());
     }
//--- Aktualisieren der Koordinaten in den grafischen Objekten
   m_area.X_Distance(m_area.X());
   m_area.Y_Distance(m_area.Y());
   m_check.X_Distance(m_check.X());
   m_check.Y_Distance(m_check.Y());
   m_label.X_Distance(m_label.X());
   m_label.Y_Distance(m_label.Y());
  }

Aus Gründen der Übersichtlichkeit zeigt die folgende Abbildung schematisch alle mögliche Kombinationen der Positionierung-Modi und die automatische Größenänderung von Steuerelementen. Es ist ein abstraktes Beispiel, in welchem das Formular (siehe die neunte Spalte «Ergebnis») durch ein weißes Rechteck mit einem dicken schwarzen Rahmen mit der Größe von 400 x 400 Pixel und das Steuerelement – graues Rechteck mit einer Größe von 200 x 200 Pixel dargestellt wird. Die relativen Koordinaten und die Größe des Steuerelements werden für jede Kombination angezeigt. Striche zeigen, dass in diesen Fällen das festlegen der Größe nicht obligatorisch ist (wenn automatische Größenänderung Modus aktiviert ist). 

Abbildung 3. Tabelle mit verschiedenen Optionen in Kombination mit der Positionierung der Controls und automatischer Größenänderung. 

Abbildung 3. Tabelle mit verschiedenen Optionen in Kombination mit der Positionierung der Controls und automatischer Größenänderung.


5. Es wurde der ON_CLICK_TAB Eventbezeichner für das Generieren von Events beim umschalten zwischen den Tabs in den Klassen CTabs und CIconTabs hinzugefügt. 

//+----------------------------------------------------------------
//|                                                      Defines.mqh |
//|                        Copyright 2015, MetaQuotes Software Corp. |
//|                                              http://www.mql5.com |
//+----------------------------------------------------------------
...
#define ON_CLICK_TAB               (27) // Switching tabs

Die Events mit dem ON_CLICK_TAB Bezeichnet können nun in dem Eventhandler der benutzerdefinierten Klasse nachverfolgt werden, was weitere Möglichkeiten bei der Gestaltung des grafischen Interfaces eröffnet. 


6. Eine erzwungene Aktualisierung der Koordinaten wurde zu den Show() Methoden aller Steuerelemente hinzugefügt. Als Beispiel zeigt das nachfolgende listing die Methode der CSimpleButton Klasse (Die Zeile wurde in Gelb hervorgehoben):

//+----------------------------------------------------------------
//| Zeigt den Button                                                 |
//+----------------------------------------------------------------
void CSimpleButton::Show(void)
  {
//--- Verlassen, falls das Element bereits sichtbar ist
   if(CElement::IsVisible())
      return;
//--- Alle Objekte sichtbar machen
   m_button.Timeframes(OBJ_ALL_PERIODS);
//--- Status der Sichtbarkeit
   CElement::IsVisible(true);
//--- Aktualisiere die Positionen der Objekte
   Moving(m_wnd.X(),m_wnd.Y());
  }

In bestimmten Fällen konnte es bei der aktiven Nutzung von MQL Anwendungen mit einer grafischer Oberfläche zu einigen Problemen bei der Positionierung der Steuerelemente kommen. Dieses Problem ist nun gelöst.


7. Der CWindow Klasse wurden Methoden für die Abfrage der Pointer von Buttons hinzugefügt: 

//+----------------------------------------------------------------
//| Klasse für das Erzeugen einer Form mit Controls                           |
//+----------------------------------------------------------------
class CWindow : public CElement
  {
public:
   //--- Gibt die Pointer zu Formular-Buttons zurück
   CBmpLabel        *GetCloseButtonPointer(void)                             { return(::GetPointer(m_button_close));   }
   CBmpLabel        *GetRollUpButtonPointer(void)                            { return(::GetPointer(m_button_unroll));  }
   CBmpLabel        *GetUnrollButtonPointer(void)                            { return(::GetPointer(m_button_rollup));  }
   CBmpLabel        *GetTooltipButtonPointer(void)                           { return(::GetPointer(m_button_tooltip)); }
  };

So haben die Bibliotheksbenutzer jetzt die Möglichkeit, die Eigenschaften dieser grafischen Objekte zu jedem Zeitpunkt des Lebenszyklus der MMS-Anwendung zu ändern, nachdem die grafische Benutzeroberfläche geschaffen worden ist. Zum Beispiel können die Tooltips der Buttons, die zuvor nur anfänglich definiert werden konnten, nun auch im Nachhinein noch unabhängig erzeugt und geändert werden. Dieses kann bei der Erzeugung von mehrsprachigen MQL Anwendungen nützlich sein.


8. Fehlerbehebungen in der CTable Klasse. Es wurde eine weitere Überprüfung in der Hauptmethode für das Erzeugen eines Steuerelementes hinzugefügt (Sehen Sie sich dazu den nachfolgenden Programmcode an). Der sichtbare Teil muss kein gemeinsamer Teil mehr sein. Nun, wenn Benutzer bei der Festlegung der Tabelleneigenschaften einen Fehler macht, werden die Werte automatisch korrigiert. 

//+----------------------------------------------------------------
//| Erzeugung der Editbox Tabelle                                            |
//+----------------------------------------------------------------
bool CTable::CreateTable(const long chart_id,const int subwin,const int x,const int y)
  {
//--- Verlassen, wenn es keinen Pointer zu einer Form gibt
   if(!CElement::CheckWindowPointer(::CheckPointer(m_wnd)))
      return(false);
//--- Der sichtbare Bereich muss kein gemeinsamer Bereich mehr sein
   m_visible_rows_total    =(m_visible_rows_total>m_rows_total)? m_rows_total : m_visible_rows_total;
   m_visible_columns_total =(m_visible_columns_total>m_columns_total)? m_columns_total : m_visible_columns_total;
//--- Initialisierung der Variablen
   m_id       =m_wnd.LastId()+1;
   m_chart_id =chart_id;
   m_subwin   =subwin;
   m_x        =x;
   m_y        =y;
   m_x_size   =(m_x_size<1 || m_auto_xresize_mode)? (m_anchor_right_window_side)? m_wnd.X2()-m_x+m_x_size-(m_wnd.X2()-m_x)+1-m_auto_xresize_right_offset : m_wnd.X2()-m_x-m_auto_xresize_right_offset : m_x_size;
   m_y_size   =m_row_y_size*m_visible_rows_total-(m_visible_rows_total-1)+2;
//--- Ränder von den Kanten
   CElement::XGap((m_anchor_right_window_side)? m_x : m_x-m_wnd.X());
   CElement::YGap((m_anchor_bottom_window_side)? m_y : m_y-m_wnd.Y());
//--- Erzeugen der Tabelle
   if(!CreateArea())
      return(false);
   if(!CreateCells())
      return(false);
   if(!CreateScrollV())
      return(false);
   if(!CreateScrollH())
      return(false);
//--- Verstecke das Element, falls es sich um ein Dialogfenster handelt oder es minimiert ist
   if(m_wnd.WindowType()==W_DIALOG || m_wnd.IsMinimized())
      Hide();
u//---
   return(true);
  }


 

Anwendung für den Test der Updates

Lassen Sie uns für den Test kleine Änderung an der MQL Anwendung aus dem vorherigen Artikel durchführen, damit wir alle Neuerungen, die in diesem Artikel erwähnt worden sind, demonstrieren können. Erzeugen Sie den EA im Unterfenster des “SubWindow” Indikators. Die Fenstergröße des Hauptfensters des grafischen Interfaces wird nun automatisch die Größe des Unterfensters ändern. Die Höhe des Unterfensters kann manuell verändert werden. Um dieses zu tun, müssen Sie 'false' als Parameter übergeben, wenn Sie die CWindow::RollUpSubwindowMode() Methode aufrufen. 

Das nachfolgende Codebeispiel veranschaulicht auch den Zugriff auf die Eigenschaften der Schaltflächen im Hauptfenster des grafischen Interfaces. Das nachfolgende Beispiel zeigt das Festlegen der Tooltips.

//+----------------------------------------------------------------
//| Erzeugung einer Form für Controls                                      |
//+----------------------------------------------------------------
bool CProgram::CreateWindow(const string caption_text)
  {
//--- Hinzufügen des Pointers des Fensters zu dem Fenster-Array
   CWndContainer::AddWindow(m_window);
//--- Koordinaten
   int x=1;
   int y=1;
//--- Eigenschaften
   m_window.Movable(false);
   m_window.UseRollButton();
   m_window.AutoXResizeMode(true);
   m_window.AutoYResizeMode(true);
   m_window.RollUpSubwindowMode(false,false);
//--- Erzeugen des Formulars
   if(!m_window.CreateWindow(m_chart_id,m_subwin,caption_text,x,y))
      return(false);
//--- Festlegen der Tooltips
   m_window.GetCloseButtonPointer().Tooltip("Close program");
   m_window.GetUnrollButtonPointer().Tooltip("Unroll");
   m_window.GetRollUpButtonPointer().Tooltip("Roll up");
   return(true);
  }

Auf der ersten Registerkarte werden alle Steuerelemente auf der rechten Seite des Formulars verankert (siehe Screenshot unten). Wenn die Breite des Formulars geändert wird, bleiben sie in der gleichen Entfernung vom rechten Rand. 

 Abbildung 4. Die Controls der ersten Registerkarte sind an der rechten Seite des Formulars verankert.

Abbildung 4. Die Controls der ersten Registerkarte sind an der rechten Seite des Formulars verankert.


Die folgende Auflistung zeigt ein Beispiel für den Code zum Erstellen des "einfachen Buttons" (CSimpleButton). Um das Controls auf der rechten Seite zu verankern, ist es ausreichend, die AnchorRightWindowSide()-Methode aufzurufen, und den Wert true zu übergeben. 

//+----------------------------------------------------------------
//| Erzeugt den einfachen Button 1                                        |
//+----------------------------------------------------------------
bool CProgram::CreateSimpleButton1(const int x_gap,const int y_gap,string button_text)
  {
//--- Abspeichern des Fenster-Pointers
   m_simple_button1.WindowPointer(m_window);
//--- Hinzufügen zum ersten Tab
   m_tabs.AddToElementsArray(0,m_simple_button1);
//--- Koordinaten
   int x=m_window.X()+x_gap;
   int y=m_window.Y()+y_gap;
//--- Festlegen der Eigenschaften vor der Erzeugung
   m_simple_button1.ButtonXSize(140);
   m_simple_button1.BackColor(C'255,140,140');
   m_simple_button1.BackColorHover(C'255,180,180');
   m_simple_button1.BackColorPressed(C'255,120,120');
   m_simple_button1.AnchorRightWindowSide(true);
//--- Erzeugen eines Buttons
   if(!m_simple_button1.CreateSimpleButton(m_chart_id,m_subwin,button_text,x,y))
      return(false);
//--- Hinzufügen des Pointers des Elementes zu der Basis
   CWndContainer::AddToElementsArray(0,m_simple_button1);
   return(true);
  }

Der zweiten Registerkarte wird nur eine gerenderte Tabelle zugewiesen (CCanvasTable), die sich der Formulargröße anpassen wird.

 Abbildung 5. Gerenderte Tabelle, die sich der Größe der Formulars anpasst.

Abbildung 5. Gerenderte Tabelle, die sich der Größe der Formulars anpasst.


Damit alles so wie gewünscht arbeitet, ist es notwendig die AutoXResizeMode() und AutoYResizeMode() Methoden zu verwenden und die Modi für die automatische Größenänderung zu aktivieren. Mit den AutoXResizeRightOffset() und AutoYResizeBottomOffset() Methoden ist es möglich, die Offsets von der rechten und unteren Kante des Controls zu der rechten und unteren Kante des Formulars zu justieren. In diesem Fall, wird der Offset von der rechten Kante auf 1 Pixel gesetzt, und 25 Pixels von der unteren Kante (Wie sie sich dazu den nachfolgenden Programmcode an). 

//+----------------------------------------------------------------
//| Erzeugung einer gerenderten Tabelle                                          |
//+----------------------------------------------------------------
bool CProgram::CreateCanvasTable(const int x_gap,const int y_gap)
  {
#define COLUMNS3_TOTAL 15
#define ROWS3_TOTAL    30
//--- Abspeichern des Pointers des Formulars
   m_canvas_table.WindowPointer(m_window);
//--- Hinzufügen zum zweiten Tab
   m_tabs.AddToElementsArray(1,m_canvas_table);
//--- Koordinaten
   int x=m_window.X()+x_gap;
   int y=m_window.Y()+y_gap;
//--- Array mit den Breiten der Spalten
   int width[COLUMNS3_TOTAL];
   ::ArrayInitialize(width,70);
   width[0]=100;
   width[1]=90;
//--- Array mit den Textausrichtungen in den Spalten
   ENUM_ALIGN_MODE align[COLUMNS3_TOTAL];
   ::ArrayInitialize(align,ALIGN_CENTER);
   align[0]=ALIGN_RIGHT;
   align[1]=ALIGN_RIGHT;
   align[2]=ALIGN_LEFT;
//--- Festlegen der Eigenschaften vor der Erzeugung
   m_canvas_table.XSize(400);
   m_canvas_table.YSize(200);
   m_canvas_table.TableSize(COLUMNS3_TOTAL,ROWS3_TOTAL);
   m_canvas_table.TextAlign(align);
   m_canvas_table.ColumnsWidth(width);
   m_canvas_table.GridColor(clrLightGray);
   m_canvas_table.AutoXResizeMode(true);
   m_canvas_table.AutoYResizeMode(true);
   m_canvas_table.AutoXResizeRightOffset(1);
   m_canvas_table.AutoYResizeBottomOffset(25);
//--- Füllen der Tabelle mit Daten
   for(int c=0; c<COLUMNS3_TOTAL; c++)
      for(int r=0; r<ROWS3_TOTAL; r++)
         m_canvas_table.SetValue(c,r,string(c)+":"+string(r));
//--- Erzeugung des Controls
   if(!m_canvas_table.CreateTable(m_chart_id,m_subwin,x,y))
      return(false);
//--- Hinzufügen des Objektes zu dem gemeinsamen Array von Objektgruppen
   CWndContainer::AddToElementsArray(0,m_canvas_table);
   return(true);
  }

Auf dem dritten Tab wird ein (CLineGraph) platziert, der ebenfalls in seiner Größe automatisch geändert wird:

Abbildung 6. Das «Line graph» Control passt sich automatisch der Größe des Formulars an. 

Abbildung 6. Das «Line graph» Control passt sich automatisch der Größe des Formulars an.


Die vierten und fünften Registerkarten zeigen die Verankerung zum rechten Rand für viele andere Steuerelemente der Bibliothek:

Abbildung 7. Controls auf dem vierten Tab. 

Abbildung 7. Controls auf dem vierten Tab.


Fig. 8. Controls auf dem fünften Tab. 

Fig. 8. Controls auf dem fünften Tab.


Die Testanwendung, die in diesem Artikel vorgestellt wird, kann mit dem unten aufgeführten Link heruntergeladen werden. 

 


Schlussfolgerung

Die Bibliothek für das Erzeugen von grafischen Interfaces sieht zu dem aktuellen Stand der Entwicklung wie folgt aus: (Schematisch).

Abbildung 9 Die Struktur unserer Bibliothek zum aktuellen Stand der Entwicklung 

Abbildung 9 Die Struktur unserer Bibliothek zum aktuellen Stand der Entwicklung


In der nächsten Version wird die Bibliothek mit zusätzlichen Steuerelementen erweitert, die gegebenenfalls bei der Entwicklung von MQL-Anwendungen notwendig sein könnten. Die vorhandenen Steuerelemente werden verbessert und mit neuen Features ergänzt.

Nachfolgend können Sie die dritte Version der Easy And Fast Bibliothek herunterladen. Wenn Sie Fragen zur Anwendung des Materials dieser Dateien haben, können Sie auf die detaillierten Beschreibungen in den Artikel in dieser Serie zurückgreifen oder Ihre Fragen in den Kommentaren zu den Artikeln stellen. 


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

Beigefügte Dateien |
Letzte Kommentare | Zur Diskussion im Händlerforum (1)
Otto Pauser
Otto Pauser | 3 Nov. 2016 in 20:38

Lieber Anatoli,

diese grafischen Komponenten benötigen tiefes Programmierwissen. Vielen Dank für diese enorme Leistung.

Für den ungeübten Einsteiger in MQL5 sind diese Komponenten (ich nenne diese so, da ich früher in Delphi(Pascal) auch solche Objekte programmierte) dennoch schwierig zu verwenden.

Meine Zielvorstellung wäre eine Palette mit Elementen, die ich einfach auf einen Dialog ziehe, und fertig. So wie es in jeder modernen Programmieroberfläche implementiert ist.

Wäre toll wenn es sowas gäbe. Ein großer Schritt nach vorne in der Entwicklung von MT5.

Du schaffst das! Bitte mach es.

Vergleich von MQL5 und QLUA - warum sind Transaktionen in MQL5 bis zu 28 Mal schneller? Vergleich von MQL5 und QLUA - warum sind Transaktionen in MQL5 bis zu 28 Mal schneller?
Viele Trader machen sich keine Gedanken darüber, wie schnell ihre Order die Börse erreicht, wie schnell sie da ausgeführt wird und wie viel Zeit das Terminal braucht, um das Ergebnis zu erhalten. Wir uns vorgenommen, die Geschwindigkeit der Ausführung von Transaktionen zu vergleichen, denn noch keiner hat vor uns solche Messungen mithilfe von MQL5- und QLUA-Programmen durchgeführt.
Neuronales Netz: Selbstoptimierender Expert Advisor Neuronales Netz: Selbstoptimierender Expert Advisor
Ist es möglich, einen Expert Advisor zu erstellen, der nach Befehlen des Codes Kriterien für das Eröffnen und Schließen von Positionen in bestimmten Abständen optimieren würde? Was geschieht, wenn ein neuronales Netz als Modul (mehrschichtiges Perzeptron), das Historie analysiert und Strategie bewertet, im Expert Advisor implementiert wird? Wir können den Expert Advisor das neuronale Netz jeden Monat (jede Woche, jeden Tag oder jede Stunde) optimieren und die Arbeit anschließend fortsetzen lassen. Auf diese Weise kann ein selbstoptimierender Expert Advisor entwickelt werden.
MQL5 Grundlagen der Programmierung: Dateien MQL5 Grundlagen der Programmierung: Dateien
Dieser praxisorientierte Artikel konzentriert sich auf die Arbeit mit Dateien in MQL5. Freuen Sie sich auf eine Reihe von einfachen Aufgaben, die Ihnen helfen werden, die Grundlagen besser zu verstehen.
Statistische Verteilungen in MQL5 - Das Beste aus R und noch schneller Statistische Verteilungen in MQL5 - Das Beste aus R und noch schneller
Der Artikel beschäftigt sich mit Funktionen für die grundlegenden, statistischen Verteilungen, die in der Sprache R umgesetzt sind. Das umfasst die Cauchy-, Weibull-, Normal-, Log-Normal-, logistische, exponentielle, gleichmäßige und die Gamma-Verteilung, das zentrale und nicht-zentrale Beta, die Chi-Quadrat und F-Verteilung von Fisher, die Studentsche T-Verteilung, so wie die diskrete und negative Binomialverteilung und die geometrische, hypergeometrische und Poisson-Verteilung. Es gibt Funktionen zur Berechnung der theoretischen Momente der Verteilungen, um den Grad der Übereinstimmung mit einer realen Verteilung einzuschätzen.