English Русский 中文 Español 日本語 Português
Graphische Interfaces XI: Überarbeitung des Bibliothekscodes (build 14.1)

Graphische Interfaces XI: Überarbeitung des Bibliothekscodes (build 14.1)

MetaTrader 5Beispiele | 21 August 2017, 09:51
522 0
Anatoli Kazharski
Anatoli Kazharski

Inhalt

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).

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). 

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).

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).

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).

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. 

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. 

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.

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

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

Beigefügte Dateien |
Testen der Muster, die beim Handel mit Körben von Währungspaaren auftreten. Teil I Testen der Muster, die beim Handel mit Körben von Währungspaaren auftreten. Teil I
Wir beginnen mit dem Testen der Muster und der Prüfung der Methoden, die in den Artikeln über den Handel mit Körben von Währungspaaren beschrieben wurden. Schauen wir, wie die Ausbruchsmuster der Levels für Überkauft/Überverkauft in der Praxis angewandt werden.
Flaggenformation Flaggenformation
Der Artikel befasst sich mit den Formationen Flagge, Wimpel, Keil, rechteckige Formation, Fallendes Dreieck und Steigendes Dreieck. Es werden ihre Ähnlichkeiten und Unterschiede analysiert sowie Indikatoren für deren Erkennung auf dem Chart und ein Tester-Indikator für eine schnelle Einschätzung der Effizienz erstellt.
Graphisches Interface XI: Gezeichnete Steuerelemente (build 14.2) Graphisches Interface XI: Gezeichnete Steuerelemente (build 14.2)
In der neuen Version der Bibliothek werden alle Steuerelemente als eigenständige Grafikobjekte des Typs OBJ_BITMAP_LABEL gezeichnet. Der Code wird auch weiterhin optimiert: die Änderungen in den Kernklassen werden beschrieben.
Cross-Plattform Expert Advisor: Geldmanagement Cross-Plattform Expert Advisor: Geldmanagement
Dieser Artikel beschreibt die Implementierung von Methoden des Geldmanagements für einen Cross-Plattform Expert Advisor. Die Klassen des Geldmanagements führen die Berechnungen der Lotgröße der nächsten Position des Expert Advisors durch.