
Graphische Interfaces XI: Überarbeitung des Bibliothekscodes (build 14.1)
Inhalt
- Einführung
- Die gemeinsamen Eigenschaften der Steuerelemente
- Klasse für das Arbeiten mit Bilddaten
- Die Methoden für das Arbeiten mit Bildern
- Das Zusammenführen von Steuerelementen als Teil der Optimierung des Bibliothekscodes
- Die Hierarchie der Steuerelemente
- Der Array verschachtelter Steuerelemente
- Basiscode der virtuellen Methoden
- Die automatische Bestimmung der Prioritäten eines Klicks der linken Maustaste
- Anwendung zum Testen des Textfeldes
- Schlussfolgerung
Einführung
Der erste Artikel Grafische Interfaces I: Vorbereitung der Bibliotheksstruktur (Kapitel 1) beschreibt im Detail den Zweck der Bibliothek. Am Ende der Artikel in jedem Abschnitt ist eine Liste von Kapiteln mit Verweisen. Sie können auch die aktuellste Vollversion der Bibliothek von dort herunterladen. Die Dateien müssen in die gleichen Verzeichnissen kopiert werden, wie sie sich in dem Archiv befinden.
Die letzte Aktualisierung der Bibliothek zielt darauf ab, den Code zu optimieren, seine Größe zu reduzieren und seine Implementierung noch objektorientierter zu machen. Dadurch ist der Code leichter zu verstehen. Mit der detaillierten Beschreibung der letzten Änderungen kann der Leser auf Basis dieser Bibliothek seine eigenen Ziele umsetzen, und das mit einem geringem Zeitaufwand.
Wegen des großen Umfangs der Publikation wurde die Beschreibung der letzten Aktualisierung der Bibliothek auf zwei Artikel aufgeteilt. Dies hier ist der erste Teil.
Die gemeinsamen Eigenschaften der Steuerelemente
Zuerst wurden die gemeinsamen Eigenschaften der Steuerelemente in der Bibliothek geändert. Vorher wurden die Variablen und Methoden einer Klasse für bestimmte Eigenschaften (Hintergrundfarbe, Ränder, Text etc.) in abgeleiteten Klassen für jedes einzelne Steuerelement gespeichert. Im objektorientierten Sprachgebrauch nennt man es überladen. Jetzt, da alle relevanten Steuerelemente realisiert sind, ist es einfach wiederkehrende Variablen und Methoden, die allgemeine Eigenschaften betreffen, zu bestimmen und in einer Basisklasse zu versammeln.
Listen wir die gemeinsamen Eigenschaften aller Steuerelementen auf, die in dieser Basisklasse ihren Platz finden.
- Bild (Icon), gelesen aus einer Datei oder durch ein Programm erstellt.
- Einzüge des Bildes von der X- und der Y-Achse.
- Hintergrundfarbe.
- Farbe des Randes.
- Textfarbe.
- Beschreibungstext.
- Einzüge der Beschreibung.
Nun muss entschieden werden, welche Klasse die Variablen und Methoden zum Festlegen und Abfragen der Eigenschaften enthält.
Es gibt zwei Klassen in der Klassenhierarchie für jedes Steuerelement: CElementBase und CElement. Die Variablen und Methoden folgender Eigenschaften wandern in die Basisklasse CElementBase: Koordinaten, Größen, Identifikatoren, Indices und die speziellen Modi jeden Steuerelementes der Liste. Die Variablen und Methoden zur Handhabung des Aussehens der Steuerelemente kommen in die abgeleitete Klasse CElement.
Zusätzlich werden die Methoden für das Erstellen der Name von grafischen Objekten der Steuerelemente der Klasse CElementBase hinzugefügt. Davor wurde der Name in der Methode für das Erstellen der Steuerelemente erzeugt. Jetzt, da jedes Steuerelement als eigenes Objekt gezeichnet wird, können in der Basisklasse universelle Methoden erstellt werden.
Die Methode CElementBase::NamePart() dient dem Bestimmen und Abfragen des Typs des Steuerelements, der ein Teil des Namens ist.
//+------------------------------------------------------------------+ //| Basisklasse des Steuerelements | //+------------------------------------------------------------------+ class CElementBase { protected: //--- Teil des Namens (Art des Steuerelementes) string m_name_part; //--- public: //--- (1) Sichern und (2) Rückgabe dieses Teil Namens des Steuerelementes void NamePart(const string name_part) { m_name_part=name_part; } string NamePart(void) const { return(m_name_part); } };
Die Methode CElementBase::ElementName() erstellt den gesamten Namen des Grafikobjektes. Dieser Methode muss der Teil des Namens übergeben werden, der den Typ des Steuerelementes kennzeichnet. Sollte es sich herausstellen, dass der Name bereits bestimmt wurde, wird der übergebenen Wert nicht verwendet. Wegen der letzten Änderungen in dieser Bibliothek (siehe unten), wird diese Lösung dann verwendet, wenn ein Steuerelement aus einem anderen abgeleitet wurde, und der Name neu definiert werden muss.
class CElementBase { protected: //--- Name des Steuerelementes string m_element_name; //--- public: //--- Bilden des Objektnamens string ElementName(const string name_part=""); }; //+------------------------------------------------------------------+ //| Rückgabe des erstellten Namens des Steuerelementes | //+------------------------------------------------------------------+ string CElementBase::ElementName(const string name_part="") { m_name_part=(m_name_part!="")? m_name_part : name_part; //--- Bilden des Objektnamens string name=""; if(m_index==WRONG_VALUE) name=m_program_name+"_"+m_name_part+"_"+(string)CElementBase::Id(); else name=m_program_name+"_"+m_name_part+"_"+(string)CElementBase::Index()+"__"+(string)CElementBase::Id(); //--- return(name); }
Für die Reaktion auf einem Mausklick in einem bestimmten Steuerelement, muss der Name des geklickten Grafikobjektes überprüft werden. Diese Überprüfung wurde für viele Steuerelemente häufig benötigt, es wird daher die spezielle Methode CElementBase::CheckElementName() der Basisklasse hinzugefügt:
class CElementBase { public: //--- Prüfen, ob der Name des Steuerelementes einen entscheidenden Teil enthält bool CheckElementName(const string object_name); }; //+------------------------------------------------------------------+ //| Rückgabe des erstellten Namens des Steuerelementes | //+------------------------------------------------------------------+ bool CElementBase::CheckElementName(const string object_name) { //--- Wenn in diesem Steuerelemente eine Taste gedrückt wurde if(::StringFind(object_name,m_program_name+"_"+m_name_part+"_")<0) return(false); //--- return(true); }
Für die anderen Eigenschaften genügt es, nur mit der Beschreibung der Methode für den Umgang mit Bildern zu arbeiten.
Klasse für das Arbeiten mit Bilddaten
Für das Arbeiten mit Bildern wurde die Klasse CImage erstellt, in der die Bilddaten gesichert werden können:
- Array der Pixel des Bildes;
- Bildgröße (Breite und Höhe);
- Pfad zur Datei.
Um die Werte dieser Eigenschaften abzufragen, werden entsprechende Methoden benötigt:
//+------------------------------------------------------------------+ //| Klasse zum Sichern der Bilddaten | //+------------------------------------------------------------------+ class CImage { protected: uint m_image_data[]; // Array der Pixel des Bildes uint m_image_width; // Breite des Bildes uint m_image_height; // Höhe des Bildes string m_bmp_path; // Pfad zur Bilddatei //--- public: CImage(void); ~CImage(void); //--- (1) Größe des Datenarrays, (2) Bestimmen/Rückgabe der Daten (Pixelfarbe) uint DataTotal(void) { return(::ArraySize(m_image_data)); } uint Data(const uint data_index) { return(m_image_data[data_index]); } void Data(const uint data_index,const uint data) { m_image_data[data_index]=data; } //--- Bestimmen/Rückgabe der Bildbreite void Width(const uint width) { m_image_width=width; } uint Width(void) { return(m_image_width); } //--- Bestimmen/Rückgabe der Bildhöhe void Height(const uint height) { m_image_height=height; } uint Height(void) { return(m_image_height); } //--- Bestimmen/Rückgabe des Pfads zur Bilddatei void BmpPath(const string bmp_file_path) { m_bmp_path=bmp_file_path; } string BmpPath(void) { return(m_bmp_path); } };
Die Methode CImage::ReadImageData() wird für das Lesen und Sichern der Daten des Bildes verwendet. Dieser Methode sollte der Pfad zur Bilddatei übergeben werden:
class CImage { public: //--- Lesen und sichern der Daten des übergebenen Bildes bool ReadImageData(const string bmp_file_path); }; //+------------------------------------------------------------------+ //| Sichern des übergebenen Bildes in einen Array | //+------------------------------------------------------------------+ bool CImage::ReadImageData(const string bmp_file_path) { //--- Verlassen bei einer leeren Zeichenkette if(bmp_file_path=="") return(false); //--- Sichern des Pfads zur Bilddatei m_bmp_path=bmp_file_path; //--- Rücksetzen der Fehlervariablen ::ResetLastError(); //--- Lesen und Sichern der Daten des Bildes if(!::ResourceReadImage("::"+m_bmp_path,m_image_data,m_image_width,m_image_height)) { ::Print(__FUNCTION__," > Error when reading the image ("+m_bmp_path+"): ",::GetLastError()); return(false); } //--- return(true); }
Manchmal müssen die Daten des übergebenen Bildes kopiert werden. Das erledigt die Methode CImage::CopyImageData(). Ein Objekt des Typs CImage wird mittels einer Referenz dieser Methode übergeben, um den Datenarray zu kopieren. Hier wird zuerst die Größe des Quellarrays abgefragt und dann der Empfängerarray auf diese Größe zu gebracht. Danach wird die Methode CImage::Data() verwendet, um in einer Schleife die abgefragten Daten im Empfängerarray zu übertragen.
class CImage { public: //--- Kopieren der Daten des übergebenen Bildes void CopyImageData(CImage &array_source); }; //+------------------------------------------------------------------+ //| Kopieren der Daten des übergebenen Bildes | //+------------------------------------------------------------------+ void CImage::CopyImageData(CImage &array_source) { //--- Abfragen der Größe des Quellarrays uint source_data_total =array_source.DataTotal(); //--- Größenänderung des Empfängerarrays ::ArrayResize(m_image_data,source_data_total); //--- Kopieren der Daten for(uint i=0; i<source_data_total; i++) m_image_data[i]=array_source.Data(i); }
Die Methode CImage::DeleteImageData() löscht die Daten des Bildes:
class CImage { public: //--- Löschen der Bilddaten void DeleteImageData(void); }; //+------------------------------------------------------------------+ //| Löschen der Bilddaten | //+------------------------------------------------------------------+ void CImage::DeleteImageData(void) { ::ArrayFree(m_image_data); m_image_width =0; m_image_height =0; m_bmp_path =""; }
Die Klasse CImage befindet sich in der Datei Objects.mqh. Jetzt werden alle Steuerelemente gerendert, sodass die Klassen der grafischen Primitiven nicht länger benötigt werden. Sie wurden aus der Datei Objects.mqh entfernt. Nur für Subcharts werden Ausnahmen gemacht, das erlaubt das Erstellen von Charts ähnlich dem Hauptchart des Symbols. Alle Typen von MQL-Anwendungen werden in ihrem Fenster platziert, und es wird ein entsprechendes grafisches Interface erstellt.
//+------------------------------------------------------------------+ //| Objects.mqh | //| Copyright 2015, MetaQuotes Software Corp. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #include "Enums.mqh" #include "Defines.mqh" #include "Fonts.mqh" #include "Canvas\Charts\LineChart.mqh" #include <ChartObjects\ChartObjectSubChart.mqh> //--- Liste der Klassen in der Datei für eine schnelle Navigation (Alt+G) class CImage; class CRectCanvas; class CLineChartObject; class CSubChart; ...
Die Methoden für das Arbeiten mit Bildern
Für die Arbeit mit Bildern wurden mehrere Methoden erstellt. Sie befinden sich alle in der Klasse CElement. Jetzt kann man die benötigte Anzahl von Gruppen von Bildern (Arrays) eines bestimmten Steuerelementes festlegen. Das erlaubt ein informativeres Erscheinungsbild des Steuerelementes. Die Anzahl der gezeigten Icons im Steuerelement wird vom Entwickler der MQL-Anwendung bestimmt.
Dafür wurde die Struktur EImagesGroup erstellt und ein dynamischer Array der Instanzen in der Datei CElement deklariert. Dort befinden sich alle Eigenschaften der Bildgruppen (Einzüge und die zur Darstellung gewählten Bilder), mit den Bildern selbst, die in einem dynamischen Array des Typs CImage gesichert werden.
//+------------------------------------------------------------------+ //| Abgeleitete Klasse eines Steuerelementes | //+------------------------------------------------------------------+ class CElement : public CElementBase { protected: //--- Gruppen von Bildern struct EImagesGroup { //--- Array der Bilder CImage m_image[]; //--- Iconränder int m_x_gap; int m_y_gap; //--- Bild der Gruppe, ausgewählt zur Darstellung int m_selected_image; }; EImagesGroup m_images_group[]; };
Um ein Bild einem Steuerelement hinzufügen, muss zuerst eine Gruppe hinzugefügt werden. Das kann mit Hilfe der Methode CElement::AddImagesGroup() erreicht werden. Es müssen die Einzüge vom oberen, linken Punkt des Steuerelementes aller Bilder dieser Gruppe als Argument übergeben werden. Standardmäßig wird das erste Bild der Gruppe zur Darstellung ausgewählt.
class CElement : public CElementBase { public: //--- Hinzufügen einer Bildergruppe void AddImagesGroup(const int x_gap,const int y_gap); }; //+------------------------------------------------------------------+ //| Hinzufügen einer Bildergruppe | //+------------------------------------------------------------------+ void CElement::AddImagesGroup(const int x_gap,const int y_gap) { //--- Abfragen der Größe des Arrays der Bildergruppe uint images_group_total=::ArraySize(m_images_group); //--- Ergänzen einer Gruppe ::ArrayResize(m_images_group,images_group_total+1); //--- Setzen der Einzüge der Bilder m_images_group[images_group_total].m_x_gap=x_gap; m_images_group[images_group_total].m_y_gap=y_gap; //--- Das standardmäßige Bild m_images_group[images_group_total].m_selected_image=0; }
Die Methode CElement::AddImage() fügt ein Bild einer Gruppe hinzu. Der Index der Gruppe und den Pfad zur Bilddatei muss als Argument übergeben werden. Das Bild kann nicht hinzugefügt werden, wenn es keine Gruppe gibt. Eine Regelung, die das Überschreiten der Arraygrenzen verhindert, gibt es auch. Falls die Grenze überschritten würde, wird das Element der letzten Gruppe hinzugefügt.
class CElement : public CElementBase { public: //--- Hinzufügen eines Bildes einer bestimmten Gruppe void AddImage(const uint group_index,const string file_path); }; //+------------------------------------------------------------------+ //| Hinzufügen eines Bildes einer bestimmten Gruppe | //+------------------------------------------------------------------+ void CElement::AddImage(const uint group_index,const string file_path) { //--- Abfragen der Größe des Arrays der Bildergruppe uint images_group_total=::ArraySize(m_images_group); //--- Verlassen, wenn es keine Gruppe gibt if(images_group_total<1) { Print(__FUNCTION__, " > A group of images can be added using the CElement::AddImagesGroup() methods"); return; } //--- Schutz vor dem Überschreiten der Grenze uint check_group_index=(group_index<images_group_total)? group_index : images_group_total-1; //--- Abfragen der Größe des Bilderarrays uint images_total=::ArraySize(m_images_group[check_group_index].m_image); //--- Erhöhen der Arraygröße um eine Spalte ::ArrayResize(m_images_group[check_group_index].m_image,images_total+1); //--- Hinzufügen des Bildes m_images_group[check_group_index].m_image[images_total].ReadImageData(file_path); }
Mit der zweiten Version der Methode CElement::AddImagesGroup() kann man eine Gruppe mit einem Bilderarray direkt hinzufügen. Es muss dazu, zusätzlich zu den Einzügen, ein Array mit den Pfaden zu den Bildern als Argument übergeben werden. Nach dem Erhöhen der Größe des Arrays der Bildergruppe um Eins, ergänzt das Programm in einer Schleife den gesamten Array der übergebenen Bilder mit der Methode CElement::AddImage() (siehe oben).
class CElement : public CElementBase { public: //--- Hinzufügen einer Bildergruppe durch eine Bilderarray void AddImagesGroup(const int x_gap,const int y_gap,const string &file_pathways[]); }; //+------------------------------------------------------------------+ //| Hinzufügen einer Bildergruppe durch eine Bilderarray | //+------------------------------------------------------------------+ void CElement::AddImagesGroup(const int x_gap,const int y_gap,const string &file_pathways[]) { //--- Abfragen der Größe des Arrays der Bildergruppe uint images_group_total=::ArraySize(m_images_group); //--- Ergänzen einer Gruppe ::ArrayResize(m_images_group,images_group_total+1); //--- Setzen der Einzüge der Bilder m_images_group[images_group_total].m_x_gap =x_gap; m_images_group[images_group_total].m_y_gap =y_gap; //--- Das standardmäßige Bild m_images_group[images_group_total].m_selected_image=0; //--- Abfragen der Größe des Arrays der hinzuzufügenden Bilder uint images_total=::ArraySize(file_pathways); //--- Hinzufügen der Bilder zu einer neuen Gruppe, wenn ein nicht-leerer Array übergeben wurde for(uint i=0; i<images_total; i++) AddImage(images_group_total,file_pathways[i]); }
Das Bild einer bestimmten Gruppe kann während der Laufzeit eingefügt oder ersetzt werden. Das bietet die Methode CElement::SetImage(), man muss ihr (1) den Index der Gruppe, (2) den Index des Bildes und (3) den Pfad zur Datei als Argumente übergeben.
class CElement : public CElementBase { public: //--- Übergeben/Ersetzen des Bildes void SetImage(const uint group_index,const uint image_index,const string file_path); }; //+------------------------------------------------------------------+ //| Übergeben/Ersetzen des Bildes | //+------------------------------------------------------------------+ void CElement::SetImage(const uint group_index,const uint image_index,const string file_path) { //--- Prüfen der Arraygrenze if(!CheckOutOfRange(group_index,image_index)) return; //--- Löschen des Bildes m_images_group[group_index].m_image[image_index].DeleteImageData(); //--- Hinzufügen des Bildes m_images_group[group_index].m_image[image_index].ReadImageData(file_path); }
Wenn jedoch bereits beim Erstellen des Steuerelementes alle notwendigen Bilder übergeben wurden, ist es besser einen Wechsel von ihnen mit der Methode CElement :: ChangeImage() zu vereinfachen:
class CElement : public CElementBase { public: //--- Wechseln eines Bildes void ChangeImage(const uint group_index,const uint image_index); }; //+------------------------------------------------------------------+ //| Wechseln eines Bildes | //+------------------------------------------------------------------+ void CElement::ChangeImage(const uint group_index,const uint image_index) { //--- Prüfen der Arraygrenze if(!CheckOutOfRange(group_index,image_index)) return; //--- Sichern des Index des darzustellenden Bildes m_images_group[group_index].m_selected_image=(int)image_index; }
Um das aktuell gewählte Bild einer bestimmten Gruppe zu finden, gibt es die Methode CElement::SelectedImage(). Existiert keine Gruppe oder Bilder in der angegebenen Gruppe, gibt die Methode einen negativen Wert zurück.
class CElement : public CElementBase { public: //--- Rückgabe des dargestellten, gewählten Bildes der angegebenen Gruppe int SelectedImage(const uint group_index=0); }; //+------------------------------------------------------------------+ //| Rückgabe des dargestellten, gewählten Bildes der Gruppe | //+------------------------------------------------------------------+ int CElement::SelectedImage(const uint group_index=0) { //--- Verlassen, wenn es keine Gruppe gibt uint images_group_total=::ArraySize(m_images_group); if(images_group_total<1 || group_index>=images_group_total) return(WRONG_VALUE); //--- Verlassen, wenn es keine Bilder in der angegebenen Gruppe gibt uint images_total=::ArraySize(m_images_group[group_index].m_image); if(images_total<1) return(WRONG_VALUE); //--- Rückgabe des gewählten, dargestellten Bildes return(m_images_group[group_index].m_selected_image); }
Früher hatten alle Klassen der Steuerelemente, die Icons darstellen sollen, Methoden diese Bilder definieren. Zum Beispiel könnten gedrückte und losgelassene Tasten Icons zugewiesen werden oder auch ein gesperrter Zustand. Diese Funktion bleibt erhalten, sie ist eine klare und verständliche Option. Wie vorher können die Icons mit den Methoden CElement::IconXGap() und CElement::IconYGap() bestimmt werden.
class CElement : public CElementBase { protected: //--- Iconränder int m_icon_x_gap; int m_icon_y_gap; //--- public: //--- Iconränder void IconXGap(const int x_gap) { m_icon_x_gap=x_gap; } int IconXGap(void) const { return(m_icon_x_gap); } void IconYGap(const int y_gap) { m_icon_y_gap=y_gap; } int IconYGap(void) const { return(m_icon_y_gap); } //--- Bestimmen des aktiven und gesperrten Zustands des Icons void IconFile(const string file_path); string IconFile(void); void IconFileLocked(const string file_path); string IconFileLocked(void); //--- Bestimmen des aktuellen Zustandes (gedrückt/gesperrt) des Icons des Steuerelementes void IconFilePressed(const string file_path); string IconFilePressed(void); void IconFilePressedLocked(const string file_path); string IconFilePressedLocked(void); };
Der Code der Methode CElement::IconFile() wird als Beispiel angeführt. Hat das Steuerelement noch keine Bildergruppe, wird als Erstes eine Gruppe erstellt. Sind keine Einzüge beim Aufruf der Methode angegeben, werden die Werte auf Null gesetzt. Nach dem Hinzufügen der Gruppe, wird das als Argument übergebene Bild ergänzt und ebenso wird ein Platz für das Bild für den gesperrten Zustand des Steuerelementes reserviert.
//+------------------------------------------------------------------+ //| Bestimmen des Icons für den aktiven Zustand | //+------------------------------------------------------------------+ void CElement::IconFile(const string file_path) { //--- Gibt es noch keine Bildergruppe if(ImagesGroupTotal()<1) { m_icon_x_gap =(m_icon_x_gap!=WRONG_VALUE)? m_icon_x_gap : 0; m_icon_y_gap =(m_icon_y_gap!=WRONG_VALUE)? m_icon_y_gap : 0; //--- Hinzufügen einer Gruppe und eines Bildes AddImagesGroup(m_icon_x_gap,m_icon_y_gap); AddImage(0,file_path); AddImage(1,""); //--- Das standardmäßige Bild m_images_group[0].m_selected_image=0; return; } //--- Setzen des Bildes der ersten Gruppe als erstes Element SetImage(0,0,file_path); }
Um die Zahl der Bildergruppen oder die Zahl der Bilder in einer bestimmten Gruppe zu ermitteln, muss die entsprechende Methode verwendet werden (siehe den folgenden Code):
class CElement : public CElementBase { public: //--- Rückgabe der Zahl von Bildergruppen uint ImagesGroupTotal(void) const { return(::ArraySize(m_images_group)); } //--- Rückgabe der Zahl von Bildern der bestimmten Gruppe int ImagesTotal(const uint group_index); }; //+------------------------------------------------------------------+ //| Rückgabe der Zahl von Bildern der bestimmten Gruppe | //+------------------------------------------------------------------+ int CElement::ImagesTotal(const uint group_index) { //--- Prüfen des Gruppenindex uint images_group_total=::ArraySize(m_images_group); if(images_group_total<1 || group_index>=images_group_total) return(WRONG_VALUE); //--- Zahl der Bilder return(::ArraySize(m_images_group[group_index].m_image)); }
Das Zusammenführen von Steuerelementen als Teil der Optimierung des Bibliothekscodes
Bis jetzt wurden viele Steuerelemente praktisch kopiert und nur ein paar Methoden sind individuell. Das bläht den Code in hohem Maße auf. Daher wurden Änderungen vorgenommen, die den Code verringern und das Verstehen erleichtern.
1. Zuvor gab es zwei Klassen für die Tasten.
- CSimpleButton — einfache Taste
- CIconButton — Icontaste.
Jetzt aber können Icons in jedem Steuerelement aus der Basis erstellt werden. Daher werden für zwei Tasten mit unterschiedlichen Eigenschaften keine zwei Klassen mehr benötigt. Es bleibt nur eine veränderte Klasse - CButton. Um den Text der Taste zu zentrieren, muss nur der entsprechende Modus über die Methode CElement::IsCenterText() eingestellt werden, und das kann für jedes Steuerelement eingerichtet werden.
class CElement : public CElementBase { protected: //--- Modus für die Textausrichtung bool m_is_center_text; //--- public: //--- Linksbündig void IsCenterText(const bool state) { m_is_center_text=state; } bool IsCenterText(void) const { return(m_is_center_text); } };
2. Das Gleiche geschieht beim Erstellen von Gruppen von Tasten. In der vorherigen Version der Bibliothek gibt es drei Klassen, um Gruppen von Tasten mit unterschiedlichen Eigenschaften zu erstellen:
- CButtonsGroup — Gruppe der einfachen Tasten.
- CRadioButtons — Gruppe der Optionstasten (radio buttons).
- CIconButtonsGroup — Gruppe von Icontasten
In all diesen Klassen werden die Tasten aus den standardmäßigen Grafikprimitiven erstellt. Jetzt bleibt nur die Klasse CButtonsGroup. Sie erstellt Tasten als gebrauchsfertige Steuerelemente vom Typ CButton.
Der Modus für die Optionstaste (eine Taste der Gruppe ist immer ausgewählt) wird durch die Methode CButtonsGroup::RadioButtonsMode() eingestellt. Ein Aussehen ähnlich den Optionstasten kann mit der Methode CButtonsGroup::RadioButtonsStyle() erreicht werden.
class CButtonsGroup : public CElement { protected: // Der Modus der Optionstasten bool m_radio_buttons_mode; //--- Anzeigestil der Optionstasten bool m_radio_buttons_style; //--- public: //--- (1) Bestimmen von Modus und (2) Anzeigestil der Optionstasten void RadioButtonsMode(const bool flag) { m_radio_buttons_mode=flag; } void RadioButtonsStyle(const bool flag) { m_radio_buttons_style=flag; } };
3. Betrachten wir folgende drei Klassen für das Erstellen von Steuerelementen mit Eingabefeldern:
- CSpinEdit — Spin-Bearbeitungsfeld.
- CCheckBoxEdit — Spin-Bearbeitungsfeld mit Kontrollkästchen.
- CTextEdit — Text-Bearbeitungsfeld.
Alle Eigenschaften der obigen Klassen können in einer zusammengefasst werden, es sei diese die Klasse CTextEdit. Falls ein Spin-Bearbeitungsfeld mit Schaltern für In- und Dekrement erstellt werden soll, muss nur der entsprechende Modus mit der Methode CTextEdit::SpinEditMode() aktiviert werden. Wenn das Steuerelement ein Kontrollkästchen haben soll, muss nur dieser Modus mit der Methode CTextEdit::CheckBoxMode() gesetzt werden.
class CTextEdit : public CElement { protected: //--- Modus für Steuerelemente mit Kontrollkästchen bool m_checkbox_mode; //--- Modus eines Spin-Bearbeitungsfeldes mit Tasten bool m_spin_edit_mode; //--- public: //--- Modi für (1) Kontrollkästchen und (2) Spin-Bearbeitungsfelder void CheckBoxMode(const bool state) { m_checkbox_mode=state; } void SpinEditMode(const bool state) { m_spin_edit_mode=state; } };
4. Das Gleiche gilt für Elemente zum Erstellen von Kombinationsfelder. Vorher gab es zwei Klassen:
- CComboBox — Taste mit einer Auswahlliste.
- CCheckComboBox — Taste mit einer Auswahlliste und Kontrollkästchen.
Die Existenz von zwei fast identischen Klassen ist redundant, daher bleibt nur eine übrig — CComboBox. Der Modus für ein Steuerelement mit einem Kontrollkästchen kann mit der Methode CComboBox::CheckBoxMode() aktiviert werden.
class CComboBox : public CElement { protected: //--- Modus für Steuerelemente mit Kontrollkästchen bool m_checkbox_mode; //--- public: //--- Modusbestimmung eines Steuerelementes mit einem Kontrollkästchen void CheckBoxMode(const bool state) { m_checkbox_mode=state; } };
5. Früher wurden zwei Klassen für das Erstellen von Schieberegler benötigt:
- CSlider — einfache, numerische Schieberegler.
- CDualSlider — doppelte Schieberegler für Zahlenbereiche
Von beiden bleibt nur eine übrig — die Kasse CSlider. Der Modus der doppelten Schieberegler wird mit der Methode CSlider::DualSliderMode() aktiviert.
class CSlider : public CElement { protected: //--- Modus der doppelten Schieberegler bool m_dual_slider_mode; //--- public: //--- Modus der doppelten Schieberegler void DualSliderMode(const bool state) { m_dual_slider_mode=state; } bool DualSliderMode(void) const { return(m_dual_slider_mode); } };
6. Ursprünglich hatte die Bibliothek zwei Klassen für die Darstellung von Listen mit einer Bildlaufleiste, eine von ihnen erstellt eine Listendarstellung mit Kontrollkästchen.
- CListView — einfache Liste mit der Möglichkeit, ein Element auszuwählen.
- CCheckBoxList — Listendarstellung mit Kontrollkästchen.
Von beiden bleibt nur CListView. Um eine Liste mit Kontrollkästchen zu erstellen, muss nur der entsprechende Modus mit der Methode CListView::CheckBoxMode() aktiviert werden.
class CListView : public CElement { protected: //--- Modus einer Liste mit Kontrollkästchen bool m_checkbox_mode; //--- public: //--- Modusbestimmung eines Steuerelementes mit einem Kontrollkästchen void CheckBoxMode(const bool state) { m_checkbox_mode=state; } };
7. In der alten Version der Bibliothek gibt es drei Klassen für Tabellen:
- CTable — tabellarisches Eingabefeld.
- CLabelsTable — Tabelle für Text-Kennzeichen.
- CCanvasTable — Tabellendarstellung.
Im Laufe der Verbesserungen innerhalb der Artikelserie zeigt sich, dass die Klasse CCanvasTable die am weitesten fortgeschrittene ist. Daher wurden die anderen beiden entfernt und die Klasse CCanvasTable wurde umbenannt in CTable.
8. Es gab zwei Klassen für die Karteireiter:
- CTabs — einfache Karteireiter.
- CIconTabs — Karteireiter für Icons.
Beide müssen nun wirklich nicht behalten werden. Der Array der Karteireiter für Icons wurde ursprünglich aus den Objekten der grafische Primitive erstellt. Die Tasten des Typs CButton werden jetzt dafür verwendet, wo die Icons mit den oben beschriebenen Methoden erstellt werden können. Im Ergebnis bleibt nur die Klasse CTabs.
Die Hierarchie der Steuerelemente
Die Hierarchie der Steuerelemente des grafischen Interfaces wurde ebenfalls geändert. Bisher waren alle Steuerelemente Teil eines Formulars (cwindow). Die Steuerelemente wurden in diesem Formular relativ zu seinem oberen, linken Punkt ausgerichtet. Das ist notwendig, um die Koordinaten für jedes Steuerelement beim Erstellen eines grafischen Interfaces zu bestimmen. Und, wenn in der Endphase die Position eines Elementes geändert werden muss, war es notwendig, manuell alle Koordinaten aller Steuerelemente in diesem Bereich relativ zu diesem Bereich anzupassen. Es gibt zum Beispiel Gruppen von Steuerelementen innerhalb eines Bereiches eines Karteireiters-Steuerelementes. Und wenn jetzt das Steuerelement des Karteireiters an einer anderen Stelle des Formulars verschoben wird, dann würden alle Steuerelemente eines jeden Karteireiters an ihrem alten Platz bleiben. Dies ist sehr unpraktisch, aber das Problem ist jetzt gelöst.
Früher war es notwendig, vor dem Erstellen eines bestimmten Steuerelementes eine MQL-Anwendung, das Formularobjekt mit der Methode CElement::WindowPointer() zu übergeben, damit es gesichert werden kann. Jetzt wird die Methode CElement::MainPointer() verwendet. Als Argument wird das Objekt des Steuerelementes übergeben, in dem das zu erstellende Steuerelemente platziert werden soll.
class CElement : public CElementBase { protected: //--- Pointer zu dem Hauptsteuerelement CElement *m_main; //--- public: //--- Sichern und Rückgabe des Pointers an das Hauptsteuerelement CElement *MainPointer(void) { return(::GetPointer(m_main)); } void MainPointer(CElement &object) { m_main=::GetPointer(object); } };
Wie vorher kann kein Steuerelemente erstellt werden, es sei denn es ist Teil des Hauptsteuerelementes. Die Hauptmethode zum Erstellen von Steuerelementen (in allen Klassen) überprüft, ob es einen Pointer zum Hauptsteuerelement gibt. Die Methode für diese Prüfung wurde umbenannt und heißt nun CElement::CheckMainPointer(). Hier werden der Formularpointer und der Pointer zum Objekt der Maus gesichert. Dort wird auch der Identifikator des Steuerelementes bestimmt und gesichert, so wie der Identifikator des Chart und die Nummer des Unterfensters der MQL-Anwendung des grafischen Interfaces. Vorher wurde dieser Code in verschiedenen Klassen immer wieder wiederholt.
class CElement : public CElementBase { protected: //--- Prüfen des Pointers auf das Hauptsteuerelement bool CheckMainPointer(void); }; //+------------------------------------------------------------------+ //| Prüfen des Pointers auf das Hauptsteuerelement | //+------------------------------------------------------------------+ bool CElement::CheckMainPointer(void) { //--- Falle es keinen Pointer gibt if(::CheckPointer(m_main)==POINTER_INVALID) { //--- Nachrichtenausgabe in das Journal des Terminals ::Print(__FUNCTION__, " > Before creating a control... \n...it is necessary to pass a pointer to the main control: "+ ClassName()+"::MainPointer(CElementBase &object)"); //--- Beenden des Erstellens des grafischen Interfaces der Anwendung return(false); } //--- Sichern des Pointers zum Formular m_wnd=m_main.WindowPointer(); //--- Sichern der Pointers zum Mauskursor m_mouse=m_main.MousePointer(); //--- Sichern der Eigenschaften m_id =m_wnd.LastId()+1; m_chart_id =m_wnd.ChartId(); m_subwin =m_wnd.SubwindowNumber(); //--- Senden des Flags ob der Existenz des Pointers return(true); }
Dieser Ansatz, das Steuerelement in das Hauptsteuerelement einzufügen, wird in alle Klassen weitergegeben. Komplexe, zusammengesetzte Steuerelemente werden aus mehreren erstellt, und alle werden in einer bestimmten Reihenfolge miteinander verbunden. Ausnahmen gibt es nur für drei Klassen:
- CTable — Klassen zum Erstellen einer Tabelle.
- CListView — Klasse zum Erstellen eine Listendarstellung.
- CTextBox — Klasse zum Erstellen eines mehrzeiligen Textfeldes.
Diese werden aus mehreren Grafikobjekten des Typs OBJ_BITMAP_LABEL zusammengebaut, der Inhalt jedoch wird auch gerendert. Versuche mit verschiedenen Ansätzen haben gezeigt, dass die Verwendung mehrere Objekte in der augenblicklichen Situation optimal für die Bibliothek ist.
Die Klasse CButton wurde zu eine Art universellen "Ziegelstein", der praktisch von allen anderen Steuerelementen der Bibliothek benutzt wird. Darüber hinaus ist jetzt CButton (Taste) die Basisklasse für bestimmte andere Klassen, wie:
- CMenuItem — Menü.
- CTreeItem — Baumansicht.
Im Ergebnis schaut das allgemeine Schema der Beziehung der Klassen im Format "abgeleitet-aus" ("Eltern-Kind") aus:
Fig. 1. Das Schema der Beziehungen der Klassen als "abgeleitet-aus" (Eltern-Kind).
Bis jetzt wurden 33 (dreiunddreißig) verschiedene Steuerelemente in der Bibliothek realisiert. Unten ist eine Liste der Schemata, die alle Steuerelemente der Bibliothek in alphabetische Reihenfolge auflistet. Die Wurzel-Steuerelemente sind grün und nummeriert. Danach die verbundenen Steuerelemente in ihrer gesamten Vernetzung. Jede Spalte kennzeichnet die nächste Ebene der verbundenen Steuerelemente eines bestimmten. Das Symbol ‘[]’ steht für Steuerelemente, die in mehreren Instanzen vorkommen können. Diese Sichtweise hilft dem Leser schneller und leichter das OOP-Schema dieser Bibliothek zu verstehen.
Die Schemata der folgenden Steuerelemente sind unten angezeigt:
1. CButton — Taste.
2. CButtonsGroup — Tastengruppe.
3. CCalendar — Kalender.
4. CCheckBox — Kontrollkästchen.
5. CColorButton —Taste zur Farbauswahl.
6. CColorPicker — Farbauswahl.
7. CComboBox — Taste zum Aufruf einer Auswahlliste (Kombinationsfeld).
8. CContextMenu — Kontextmenü.
Fig. 2. Schema der Steuerelemente (1. Teil).
9. CDropCalendar — Taste zum Ausruf eines Kalenders.
10. CFileNavigator — Dateinavigator.
11. CLineGraph — Linienchart.
12. CListView — Liste anzeigen.
Fig. 3. Schematische Darstellung der Steuerelemente (2. Teil).
13. CMenuBar — Hauptmenü.
14. CMenuItem — Element des Menüs.
15. CPicture — Bild.
16. CPicturesSlider — Schieberegler für Bilder.
17. CProgressBar — Fortschrittsanzeige.
18. CScroll — Bildlaufleiste.
19. CSeparateLine — Teilungslinie.
20. CSlider — numerischer Schieberegler.
21. CSplitButton — Splitt-Taste.
22. CStandartChart — Standardchart.
23. CStatusBar — Statuszeile.
24. CTable — Tabelle.
Fig. 4. Schematische Darstellung der Steuerelemente (3. Teil).
25. CTabs — Karteireiter.
26. CTextBox — Texteingabefeld mit der Option für ein mehrzeiliges Texteingabefeld.
27. CTextEdit — Eingabefeld.
28. CTextLabel — Kennzeichnung.
29. CTimeEdit — Steuerelemente zur Zeitangabe.
30. CTooltip — Tooltip.
31. CTreeItem — Element einer Baumansicht.
32. CTreeView — Baumansicht.
33. CWindow — Formular (Fenster) der Steuerelemente.
Fig. 5. Schematische Darstellung der Steuerelemente (4. Teil).
Der Array verschachtelter Steuerelemente
Wenn in der vorherigen Version der Bibliothek Steuerelemente erstellt wurden, sicherten ihre Basisklassen die Pointer zu den Grafikprimitiven. Jetzt sichern sie die Pointer auf die Steuerelemente, die selbst Komponenten bestimmter Steuerelemente des grafischen Interfaces sind.
Pointer auf Steuerelemente werden dem dynamischen Array des Typs CElement hinzugefügt mittels der geschützten Methode CElement::AddToArray(). Diese Methode ist nur für den internen Gebrauch und wird nur von den Klassen der Steuerelemente verwendet.
class CElement : public CElementBase { protected: //--- Pointer auf verschachtelte Steuerelemente CElement *m_elements[]; //--- protected: //--- Methode zum Hinzufügen von Pointern zu verschachtelten Steuerelementen void AddToArray(CElement &object); }; //+------------------------------------------------------------------+ //| Methode zum Hinzufügen von Pointern zu versch. Steuerelementen | //+------------------------------------------------------------------+ void CElement::AddToArray(CElement &object) { int size=ElementsTotal(); ::ArrayResize(m_elements,size+1); m_elements[size]=::GetPointer(object); }
Man kann den Pointer auf ein Steuerelement vom Array über den bestimmten Index erhalten. Das erledigt die 'public' Methode CElement::Element(). Zusätzlich gibt es Methoden, um die Anzahl die Anzahl der verschachtelten Steuerelemente zu erhalten und dem Löschen von Arrays.
class CElement : public CElementBase { public: //--- (1) Abfrage der Anzahl verschachtelter Steuerelemente, (2) löschen des Arrays der verschachtelten Steuerelemente int ElementsTotal(void) const { return(::ArraySize(m_elements)); } void FreeElementsArray(void) { ::ArrayFree(m_elements); } //--- Rückgabe des Pointers des verschachtelten Steuerelementes mit dem angegebenen Index CElement *Element(const uint index); }; //+------------------------------------------------------------------+ //| Rückgabe des Pointers des verschachtelten Steuerelementes | //+------------------------------------------------------------------+ CElement *CElement::Element(const uint index) { uint array_size=::ArraySize(m_elements); //--- Prüfen der Größe des Arrays der Objekte if(array_size<1) { ::Print(__FUNCTION__," > This control ("+m_class_name+") has no nested controls!"); return(NULL); } //--- Anpassen, wenn der Bereich überschritten wurde uint i=(index>=array_size)? array_size-1 : index; //--- Rückgabe des Pointers des Objektes return(::GetPointer(m_elements[i])); }
Basiscode der virtuellen Methoden
Der Basiscode der virtuellen Methoden, die die Steuerelemente verwalten, ist definiert. Diese Methoden umfassen:
- Moving() — verschieben.
- Show() — zeigen.
- Hide() — ausblenden.
- Delete() — löschen.
- SetZorders() — bestimmen der Prioritäten.
- ResetZorders() — zurücksetzen der Prioritäten.
Jedes Steuerelement wird jetzt als eigenständiges Grafikobjekt gezeichnet. Daher können die oben erwähnte Methoden verallgemeinert werden, um so die Wiederholungen in allen Klassen zu eliminieren. Es gibt Fälle, da diese Methoden in den Klassen bestimmter Steuerelemente überschrieben werden. Aus diesem Grund wurden diese Methoden als virtuell deklariert. Diese Steuerelemente umfassen zum Beispiel CListView, CTable und CTextBox. Es wurde früher erwähnt, dass in der aktuellen Bibliothek nur Steuerelemente sind, die aus mehreren Grafikobjekten zusammengebaut werden.
Betrachten wir beispielsweise die Methode CElement::Moving(). Jetzt hat sie keine Argumente mehr. Vorher mussten ihr die Koordinaten und der Modus übergeben werden. Dafür gibt jetzt keinen Grund mehr — es wurde alles viel einfacher. Das liegt in den grundsätzlichen Änderungen im Kern der Bibliothek (der Klasse CWndEvents), und das wird detailliert im folgenden Abschnitt des Artikels erklärt.
Ein Steuerelement wird relativ zur augenblicklichen Position im Hauptsteuerelement, dem es zugeordnet ist, verschoben. Wird das Grafikobjekt bewegt, werden auch die verschachtelten Steuerelemente (wenn es sie gibt) in einer Schleife verschoben. Die gleiche Kopie der Methode CElement::Moving() wird einfach von jedem Steuerelement aufgerufen. Und das für alle, verschachtelten Ebenen. Falls also ein bestimmtes Steuerelement verschoben werden muss, dann genügt es, die Methode einmal aufzurufen und die vergleichbaren Methoden der anderen Steuerelemente werden automatisch aufgerufen (in der Reihenfolge ihrer Erstellung).
Die Auflistung unten zeigt den Code der virtuellen Methode CElement::Moving():
class CElement : public CElementBase { public: //--- Verschieben eines Steuerelementes virtual void Moving(void); }; //+------------------------------------------------------------------+ //| Verschieben eines Steuerelementes | //+------------------------------------------------------------------+ void CElement::Moving(void) { //--- Verlassen, wenn ausgeblendet if(!CElementBase::IsVisible()) return; //--- Falls rechtsbündig if(m_anchor_right_window_side) { //--- Sichern der Koordinaten des Steuerelementes CElementBase::X(m_main.X2()-XGap()); //--- Sichern der Koordinaten in den Feldern der Objekte m_canvas.X(m_main.X2()-m_canvas.XGap()); } else { CElementBase::X(m_main.X()+XGap()); m_canvas.X(m_main.X()+m_canvas.XGap()); } //--- Wenn unten verankert if(m_anchor_bottom_window_side) { CElementBase::Y(m_main.Y2()-YGap()); m_canvas.Y(m_main.Y2()-m_canvas.YGap()); } else { CElementBase::Y(m_main.Y()+YGap()); m_canvas.Y(m_main.Y()+m_canvas.YGap()); } //--- Aktualisieren der Koordinaten des Grafikobjektes m_canvas.X_Distance(m_canvas.X()); m_canvas.Y_Distance(m_canvas.Y()); //--- Verschieben der verschachtelten Steuerelemente int elements_total=ElementsTotal(); for(int i=0; i<elements_total; i++) m_elements[i].Moving(); }
Die automatische Bestimmung der Prioritäten eines Klicks der linken Maustaste
In der vorherigen Version der Bibliothek waren die Prioritäten des Klick mit der linken Maustaste direkt in jedem Steuerelement codiert. Jetzt, da jedes Steuerelement ein einzelnes Grafikobjekt ist, kann man den Code zur automatischen Erkennung der Prioritäten implementieren. Jedes Grafikobjekt, das über einem anderen liegt, erhält eine höhere Priorität:
Fig. 6. Visuelle Darstellung zur Bestimmung der Prioritäten des linken Mausklicks.
Es gibt aber Steuerelemente, die überhaupt keine Grafikobjekte haben. Wie bereits erwähnt, sind das Steuerelemente, die aus mehreren gezeichneten Objekten zusammengesetzt sind. In beiden Fällen werden wird die Priorität in der Klasse jedes einzelnen Steuerelementes angepasst. Zuerst werden die Steuerelement bestimmt, die eine solche Anpassung benötigen.
Steuerelemente ohne Grafikobjekt:
- CButtonsGroup — Gruppe von Tasten.
- CDropCalendar — Taste zur Anzeige des Kalenders.
- CSplitButton — Splitt-Taste.
- CStandardChart — Standardchart.
Steuerelemente mit mehreren Grafikobjekten:
- CListView — Liste anzeigen.
- CTable — Tabelle.
- CTextBox — Text-Bearbeitungsfeld.
Die folgenden Methoden wurden der Klasse CElement für das festlegen und abfragen der Prioritäten hinzugefügt:
class CElement : public CElementBase { protected: //--- Priorität eines linken Mausklicks long m_zorder; //--- public: //--- Priorität eines linken Mausklicks long Z_Order(void) const { return(m_zorder); } void Z_Order(const long z_order); }; //+------------------------------------------------------------------+ //| Priorität eines linken Mausklicks | //+------------------------------------------------------------------+ void CElement::Z_Order(const long z_order) { m_zorder=z_order; SetZorders(); }
Steuerelemente ohne Grafikobjekte sind eine Art von Modulen mit anderen verschachtelten Steuerelementen. Es gibt nichts, um in einem solchen Steuerelement eine Priorität zu bestimmen. Aber die Priorität der verschachtelten Steuerelemente wird berechnet relativ zur Priorität des Hauptsteuerelementes. Daher müssen die Werte manuell für alles gesetzt werden, damit alles korrekt funktioniert.
Die Priorität für ein Steuerelement ohne Objekte wird gleich der des Hauptsteuerelementes gesetzt:
...
//--- Priorität des Hauptsteuerelementes, da das Steuerelement keinen eigenen Bereich zum Klicken hat
CElement::Z_Order(m_main.Z_Order());
...
Für alle anderen Steuerelemente wird die Priorität nach dem Erstellen des Hintergrundobjektes bestimmt. Bei Formularen mit einem Wert gleich Null muss er um 1 relativ zum Hauptsteuerelement für jedes nachrangige Steuerelement erhöht werden:
... //--- Alle Steuerelemente außer Formulare haben eine höhere Priorität als das Hauptsteuerelement Z_Order((dynamic_cast<CWindow*>(&this)!=NULL)? 0 : m_main.Z_Order()+1); ...
Als Beispiel betrachten wir noch ein Schema. Stellen Sie sich ein grafisches Interface mit folgenden Steuerelementen (aufgelistet in der Reihenfolge der Erstellung) vor:
- Formular für die Steuerelemente (CWindow). Formular kann über Tasten (CButton) verfügen, um (1) das Programm zu schließen, (2) das Formular zu minimieren/maximieren und (3) Tooltips etc. Weitere Tasten können bei einer Aktualisierung der Bibliothek ergänzt werden, um die Möglichkeiten der Steuerelemente zu erweitern.
- Karteireiter (CTabs). Zusätzlich zu dem Arbeitsbereich, in dem sich die Gruppen von Steuerelementen befinden, verfügt dieses Steuerelement über eine Gruppe von Tasten (CButton), die für die Karteireiter stehen.
- Eine Listenansicht (CListView) mit einer Bildlaufleiste (CScrollV) mit Tasten zum Auf und Abrollen (CButton) wird in einem der Karteireiter platziert.
- Ein anderer Karteireiter hat ein mehrzeilige Textfeld (CTextBox) mit einer horizontalen (CScrollH) und einer vertikalen (CScrollV) Bildlaufleiste.
Es bedarf keiner weiteren Aktion, damit für die Grafikobjekte die Prioritäten gesetzt werden. Alles wird gemäß diesem Schema automatisch gesetzt:
Fig. 7. Beispiel für das das Setzen der Prioritäten des linken Mausklicks.
Das Formular erhält die kleinste Priorität mit dem Wert 0 (Null). Tasten des Formulars erhalten die Priorität 1.
Jede Komponententaste des Steuerelementes des Typs CTabs (Karteireiter) erhält die Priorität 1, der Arbeitsbereich dieser Karteireiter — auch eine Priorität von 1. Aber das Steuerelement CButtonsGroup erhält den Wert 0, da es keine Grafikobjekt enthält, es ist im Wesentlichen ein Modul für Tasten des Typs CButton. In der Nutzerklasse der MQL-Anwendung wird über die Methode CElement::MainPointer() das Hauptsteuerelement bestimmt (siehe den Code unten). Hier ist das Formular (CWindow) das Hauptsteuerelement, dem das Steuerelement CTabs hinzugefügt wird. Der Pointer sollte gesichert werden, bevor die Methode zum Erstellen des Steuerelementes aufgerufen wird.
...
//--- Sichern des Pointers im Hauptsteuerelement
m_tabs1.MainPointer(m_window);
...
Die Listenansicht erhält die Priorität 2, denn ihr Hauptsteuerelement ist CTabs. Das muss vor dem Erstellen des Steuerelementes angegebenen werden:
...
//--- Sichern des Pointers im Hauptsteuerelement
m_listview1.MainPointer(m_tabs1);
...
Es ist nicht nötig das Hauptsteuerelement der Bildlaufleiste selbst zu übergeben, da es ja bereits der Klasse für die Listenansicht (CListView) bekannt ist. Das Gleiche gilt für alle anderen Steuerelemente der Bibliothek, die Komponenten andere Steuerelemente sind. Falls das Hauptsteuerelement einer Bildlaufleiste eine Listenansicht (CListView) ist mit einer Priorität von 2, wird der Wert um Eins erhöht auf (3). Und die Tasten dieser Bildlaufleiste, die ihr Hauptsteuerelement sind, erhalten den Wert 4.
Alles, das in der Listenansicht (CListView) funktioniert, funktioniert auch im Steuerelement CTextBox.
Anwendung zum Testen der Kontrollelemente
Eine MQL-Anwendung wurde zum Zwecke des Testens mitgegeben. Dessen grafisches Interface verfügt über alle Steuerelemente der Bibliothek, damit Sie sehen können wie alles funktioniert. Und so sieht das Ganze aus:
Fig. 12. Grafisches Interface zum Testen in einer MQL-Anwendung.
Diese Testanwendung ist am Ende des Artikels für weitere Studien beigefügt.
Schlussfolgerung
Diese Version der Bibliothek weist erhebliche Unterschiede zu der Version, die in dem Artikel Graphisches Interface X: Textauswahl im mehrzeiligen Textfeld (build 13). Es wurde viel Arbeit investiert, die fast alle Dateien der Bibliothek betrifft. Jetzt werden alle Steuerelemente der Bibliothek als eigenständige Objekte gezeichnet. Die Lesbarkeit des Code wurde verbessert, der Umfang um ungefähr 30% reduziert, und die Möglichkeiten erweitert. Einige Fehler und Mängel, die von den Nutzern bekannt gemacht wurden, wurden behoben.
Wenn Sie gerade begonnen haben eine MQL-Anwendung zu erstellen und Sie die vorherige Version der Bibliothek verwenden, ist es empfehlenswert, zunächst die neue Version als eine eigenständige Kopie herunterzuladen und für das MetaTrader 5-Terminal zu installieren, um alles ausgiebig testen zu können.
Das Schema der Bibliothek des grafischen Interfaces im aktuellen Entwicklungsstand schaut aus wie unten abgebildet. Das ist aber nicht die finale Version; die Bibliothek wird sich in der Zukunft weiterentwickeln und verbessern.
Fig. 13. Die Struktur der Bibliothek im aktuellen Zustand der Entwicklung
Bei Fragen zur Verwendung des bereitgestellten Materials, können Sie auf die detaillierten Beschreibungen im Lauf der Entwicklung der Bibliothek in den vorangegangenen Artikeln dieser Serie zurückgreifen oder Sie stellen Ihre Fragen im Kommentarteil diese Artikels.
Übersetzt aus dem Russischen von MetaQuotes Ltd.
Originalartikel: https://www.mql5.com/ru/articles/3365





- Freie Handelsapplikationen
- Über 8.000 Signale zum Kopieren
- Wirtschaftsnachrichten für die Lage an den Finanzmärkte
Sie stimmen der Website-Richtlinie und den Nutzungsbedingungen zu.