Grafische Interfaces X: Updates für die Easy And Fast Bibliothek (Build 3)
Anatoli Kazharski | 3 November, 2016
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.
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.
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.
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.
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.
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.
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.
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
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.