English Русский 中文 Español 日本語 Português
Grafische Interfaces VII: Das Tab-Control (Kapitel 2)

Grafische Interfaces VII: Das Tab-Control (Kapitel 2)

MetaTrader 5Beispiele | 6 September 2016, 09:26
724 0
Anatoli Kazharski
Anatoli Kazharski

Inhalt

Einleitung

Der erste Artikel Grafisches Interface I: Vorbereiten der Bibliotheksstruktur (Kapitel 1) beschreibt im Detail wofür diese Bibliothek gedacht ist. Am Ende von jedem Kapitel, finden Sie eine vollständige Liste mit Links zu diesem Artikel. Zudem finden Sie dort eine Möglichkeit das Projekt, entsprechend dem aktuellen Entwicklungsstand, herunterzuladen. Die Dateien müssen in den gleichen Verzeichnissen untergebracht werden, so, wie Sie auch in dem Archiv abgelegt sind.

Das erste Kapitel des siebten Teil befasste sich mit drei Klassen von Controls für die Erzeugung der folgenden Tabellen: Text-Label-Tabelle (CLabelsTable), Edit-box-Tabelle (CTable) und die gerenderte Tabelle (CCanvasTable). In diesem Artikel (Kapitel 2) Werden wir das Tabs-Control besprechen. Wir werden zwei Klassen für dieses Control präsentieren - eine Einfache und eine mit erweiterten Funktionalitäten.

 


Tabs-Control

Tabs werden dazu verwendet, vordefinierte Gruppen von grafischen Interface-Controls darzustellen. Sehr oft benötigen multifunktionale Anwendungen eine ganze Menge von Controls, die auf einer bestimmten Fläche des grafischen Interfaces untergebracht werden müssen. Tabs können dazu verwendet werden, diese Controls zu kategorisieren und dann in Gruppen darzustellen. Eine solche Vorgehensweise macht ein Interface für den Anwender bedienungsfreundlicher. Auf der Oberfläche sieht einen Tab aus, wie eine Gruppe von Buttons mit Labeln (Die Namen der gruppierten Controls). Es kann immer nur ein Tab selektiert sein (Aktiv).

Lassen Sie uns alle Komponenten dieses Controls auflisten.

  1. Der Hintergrund, beziehungsweise der Bereich, wo die Controls gruppiert werden.
  2. Tabs

 Abbildung  1. Komponenten des «Tabs» Controls.

Abbildung 1. Komponenten des Tabs-Controls.

Lassen Sie uns vier Modi für die Position definieren, wo die Tabs in Relation zu dem Bereich, wo die gruppierten Kontos platziert werden, erscheinen: Oben, unten, links und rechts. 

 


Entwicklung einer Klasse für die Erzeugung des Tabs-Controls

Erzeugen Sie die Tabs.mqh Datei und beziehen Sie sie in der WndContainer.mqh Datei der Bibliothek mit ein:

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

Die CTabs Klasse muss in der Tabs.mqh Datei erzeugt werden. In dieser Klasse, müssen, wie in allen anderen Klassen auch, die notwendigen Standardmethoden, sowie die Methoden für das Abspeichern des Pointers zu dem Formular, zu welchen das Control hinzugefügt wurde, hinzugefügt werden.

//+------------------------------------------------------------------+
//| Klasse für die Erzeugung von Tabs                                |
//+------------------------------------------------------------------+
class CTabs : public CElement
  {
private:
   //--- Ein Pointer zu der Form zu welchem das Element hinzugefügt worden ist
   CWindow          *m_wnd;
   //---
public:
                     CTabs(void);
                    ~CTabs(void);
   //--- (1) Speichert den Pointer das Formulars, (2) Gibt den Pointer zu den Scrollbars zurück
   void              WindowPointer(CWindow &object)               { m_wnd=::GetPointer(object);      }
   //---
public:
   //--- Chart Eventhandler
   virtual void      OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam);
   //--- Timer
   virtual void      OnEventTimer(void);
   //--- Bewegen des Elementes
   virtual void      Moving(const int x,const int y);
   //--- (1) Anzeigen, (2) verstecken, (3) zurücksetzen, (4) löschen
   virtual void      Show(void);
   virtual void      Hide(void);
   virtual void      Reset(void);
   virtual void      Delete(void);
   //--- (1) Setzen, (2) Zurücksetzen der Prioritäten der linken Maustaste
   virtual void      SetZorders(void);
   virtual void      ResetZorders(void);
   //--- Zurücksetzen der Farbe
   virtual void      ResetColors(void) {}
  };
//+-----------------------------------------------------------------+
//| Konstruktor                                                     |
//+-----------------------------------------------------------------+
CTabs::CTabs(void)
  {
  }
//+-----------------------------------------------------------------+
//| Destruktor                                                      |
//+-----------------------------------------------------------------+
CTabs::~CTabs(void)
  {
  }

Die Eigenschaften der Tabs können in besondere Eigenschaften und gemeinsame Eigenschaften aufgeteilt werden. Lassen Sie uns beide Listen aufzeigen:

Die besonderen Eigenschaften:

  • Array mit Pointern zu den Controls, die dem Tab hinzugefügt werden
  • Text
  • Breite

Erzeugen Sie die TElements Struktur für die besonderen Eigenschaften und deklarieren Sie ein dynamisches Array mit diesem Typ: 

class CTabs : public CElement
  {
private:
   //--- Struktur der Eigenschaften und ein Array für die Controls, die dem Tab hinzugefügt wurden
   struct TElements
     {
      CElement         *elements[];
      string            m_text;
      int               m_width;
     };
   TElements         m_tab[];
  };

Gemeinsame Eigenschaften

  • Positionierungsmodus der Tabs
  • Farbe des Hintergrundes
  • Die Größe der Tabs entlang der Y Achse (Höhe)
  • Die Farbe der Tabs in den unterschiedlichen Zuständen
  • Die Farbe des Textes in den unterschiedlichen Zuständen
  • Die Farbe der Ränder der Tabs
  • Prioritäten für einen Klick mit der linken Maustaste

Für den Modus der Positionierung, benötigen wir die ENUM_TABS_POSITION Aufzählung, welche der Enums.mqh Datei hinzugefügt werden muss: 

//+-----------------------------------------------------------------+
//| Enumeration für die Positionierung der Tabs                     |
//+-----------------------------------------------------------------+
enum ENUM_TABS_POSITION
  {
   TABS_TOP    =0,
   TABS_BOTTOM =1,
   TABS_LEFT   =2,
   TABS_RIGHT  =3
  };

Die Deklaration der Variablen und Methoden für das Festlegen der Eigenschaften, werden in dem nachfolgenden Listing gezeigt: 

class CTabs : public CElement
  {
private:
   //--- Positionierung der Tabs
   ENUM_TABS_POSITION m_position_mode;
   //--- Farbe des Hintergrundes
   int               m_area_color;
   //--- Die Größe der Tabs entlang der Y Achse
   int               m_tab_y_size;
   //--- Die Farbe der Tabs in den unterschiedlichen Zuständen
   color             m_tab_color;
   color             m_tab_color_hover;
   color             m_tab_color_selected;
   color             m_tab_color_array[];
   //--- Die Farbe des Textes in den unterschiedlichen Zuständen
   color             m_tab_text_color;
   color             m_tab_text_color_selected;
   //--- Die Farbe der Rahmen der Tabs
   color             m_tab_border_color;
   //--- Priorität für die linke Maustaste
   int               m_zorder;
   int               m_tab_zorder;
   //---
public:
   //--- (1) Setzen der Positionierung (Oben, unten, links, rechts), (2) Setzen der Größe der Tabs entlang der Y Achse
   void              PositionMode(const ENUM_TABS_POSITION mode)     { m_position_mode=mode;          }
   ENUM_TABS_POSITION PositionMode(void)                       const { return(m_position_mode);       }
   void              TabYSize(const int y_size)                      { m_tab_y_size=y_size;           }
   //--- Farbe (1) des Hintergrundes, (2) Farbe der Tabs in den unterschiedlichen Zuständen, (3) Farbe der Rahmen der Tabs
   void              AreaColor(const color clr)                      { m_area_color=clr;              }
   void              TabBackColor(const color clr)                   { m_tab_color=clr;               }
   void              TabBackColorHover(const color clr)              { m_tab_color_hover=clr;         }
   void              TabBackColorSelected(const color clr)           { m_tab_color_selected=clr;      }
   void              TabBorderColor(const color clr)                 { m_tab_border_color=clr;        }
   //--- Die Farbe des Textes in den unterschiedlichen Zuständen
   void              TabTextColor(const color clr)                   { m_tab_text_color=clr;          }
   void              TabTextColorSelected(const color clr)           { m_tab_text_color_selected=clr; }
  };

Bevor wir das Control erzeugen, muss die benötigte Anzahl der Tabs mit dem zugehörigen Text und deren Breite angegeben werden. Lassen Sie uns dazu die CTabs::AddTab() Methode schreiben. Die Standardwerte für die Argumente dieser Methode sind«» (leerer string) und 50 (Breite)

class CTabs : public CElement
  {
public:
   //--- Fügt einen Tab hinzu
   void              AddTab(const string tab_text="",const int tab_width=50);
  };
//+-----------------------------------------------------------------+
//| Fügt einen Tab hinzu                                            |
//+-----------------------------------------------------------------+
void CTabs::AddTab(const string tab_text,const int tab_width)
  {
//--- Legt die Größe des Arrays fest
   int array_size=::ArraySize(m_tabs);
   ::ArrayResize(m_tabs,array_size+1);
   ::ArrayResize(m_tab,array_size+1);
//--- Speichert die übergebenen Eigenschaften
   m_tab[array_size].m_text  =tab_text;
   m_tab[array_size].m_width =tab_width;
//--- Speichert die Anzahl der Tabs
   m_tabs_total=array_size+1;
  }

Falls ein bestimmter Tab bei dem Start einer Anwendung vorselektiert sein soll, dann kann dieses über die Methode CTabs::SelectedTab() angegeben werden. Wir benötigen zudem die private CTabs::CheckTabIndex() Methode um die Korrektheit des angegebenen Index zu überprüfen. 

class CTabs : public CElement
  {
private:
   //--- Index des selektierten Tabs
   int               m_selected_tab;
   //---
public:
   //--- (1) Speichern und (2) Rückgabe des Index des selektierten Tabs
   void              SelectedTab(const int index)                    { m_selected_tab=index;          }
   int               SelectedTab(void)                         const { return(m_selected_tab);        }
   //---
private:
   //--- Überprüfung des Index des selektierten Tabs
   void              CheckTabIndex(void);
  };
//+-----------------------------------------------------------------+
//| Überprüfung des Index des selektierten Tabs                     |
//+-----------------------------------------------------------------+
void CTabs::CheckTabIndex(void)
  {
//--- Überprüfung der Überschreitung des ABCs
   int array_size=::ArraySize(m_tab);
   if(m_selected_tab<0)
      m_selected_tab=0;
   if(m_selected_tab>=array_size)
      m_selected_tab=array_size-1;
  }

Für die Erzeugung des Controls brauchen wir drei private Methoden und eine public (öffentliche) Methode: 

class CTabs : public CElement
  {
private:
   //--- Objekte für die Erzeugung des Elementes
   CRectLabel        m_main_area;
   CRectLabel        m_tabs_area;
   CEdit             m_tabs[];
   //---
public:
   //--- Methoden für die Erzeugung des Tabs
   bool              CreateTabs(const long chart_id,const int subwin,const int x,const int y);
   //---
private:
   bool              CreateMainArea(void);
   bool              CreateTabsArea(void);
   bool              CreateButtons(void);
  };

Falls dem Control keine Tabs hinzugefügt worden sind, dann führt der Aufruf der CTabs::CreateTabs() Methode zu einem Abbruch der Erzeugung des grafischen Interfaces und einer entsprechenden Nachricht: 

//--- Falls es in der Gruppe keinen Tab gibt, dann benachrichtigen
   if(m_tabs_total<1)
     {
      ::Print(__FUNCTION__," > This method is to be called, "
              "if a group contains at least one tab! Use the CTabs::AddTab() method");
      return(false);
     }

Die Ermittlung und Berechnung der Koordinaten für die Komponenten des Controls, geschieht in Abhängigkeit des gewählten Positionierungsmodus. Diese Berechnungen benötigen die CTabs::SumWidthTabs() Methode, die die gesamte Breite aller Tabs zurück gibt. In dem linken (TABS_LEFT) und rechten (TABS_RIGHT) Positionierungsmodus, wird die Breite des ersten Tabs zurückgegeben. Für die 'oben' (TABS_TOP) und 'unten' (TABS_BOTTOM) Modi, Wird die Breite aller Tabs aufsummiert. 

class CTabs : public CElement
  {
private:
   //--- Positionierung der Tabs
   ENUM_TABS_POSITION m_position_mode;
   //---
private:
   //--- Breite aller Tabs
   int               SumWidthTabs(void);
  };
//+-----------------------------------------------------------------+
//| Gesamtbreite aller Tabs                                         |
//+-----------------------------------------------------------------+
int CTabs::SumWidthTabs(void)
  {
   int width=0;
//--- Falls die Tabs links oder rechts positioniert wurden, dann wird die Breite des ersten Tabs zurückgegeben
   if(m_position_mode==TABS_LEFT || m_position_mode==TABS_RIGHT)
      return(m_tab[0].m_width);
//--- Die Summe der Breiten aller Tabs
   for(int i=0; i<m_tabs_total; i++)
      width=width+m_tab[i].m_width;
//--- Unter der Berücksichtigung mit einem Pixel Überlappung
   width=width-(m_tabs_total-1);
   return(width);
  }

Die CTabs::CreateMainArea() Methode dient dazu, den Bereich, in welchen die Controls platziert werden, zu erzeugen. Die Berechnung der Koordinaten und der Größe der Objekte sieht wie folgt aus (eine abgekürzte Version der Methode): 

//+-----------------------------------------------------------------+
//| Erzeugt den Bereich für den Hintergrund                         |
//+-----------------------------------------------------------------+
bool CTabs::CreateMainArea(void)
  {
//--- Den Objektnamen bilden
   string name=CElement::ProgramName()+"_tabs_main_area_"+(string)CElement::Id();
//--- Koordinaten
   int x=0;
   int y=0;
//--- Größe
   int x_size=0;
   int y_size=0;
//--- Berechnung der Koordinaten und der Größen in Relation zu der Positionierung der Tabs
   switch(m_position_mode)
     {
      case TABS_TOP :
         x      =CElement::X();
         y      =CElement::Y()+m_tab_y_size-1;
         x_size =CElement::XSize();
         y_size =CElement::YSize()-m_tab_y_size;
         break;
      case TABS_BOTTOM :
         x      =CElement::X();
         y      =CElement::Y();
         x_size =CElement::XSize();
         y_size =CElement::YSize()-m_tab_y_size;
         break;
      case TABS_RIGHT :
         x      =CElement::X();
         y      =CElement::Y();
         x_size =CElement::XSize()-SumWidthTabs()+1;
         y_size =CElement::YSize();
         break;
      case TABS_LEFT :
         x      =CElement::X()+SumWidthTabs()-1;
         y      =CElement::Y();
         x_size =CElement::XSize()-SumWidthTabs()+1;
         y_size =CElement::YSize();
         break;
     }
//--- Erzeugung des Objektes
   if(!m_main_area.Create(m_chart_id,name,m_subwin,x,y,x_size,y_size))
      return(false);
//--- Festlegen der Eigenschaften
//--- Ränder von den Kanten
//--- Abspeichern der Größe
//--- Abspeichern der Koordinaten
//--- Abspeichern des Objekt-Pointers
//...
   return(true);
  }

Der folgende Abschnitt zeigt die Berechnung der Koordinaten und der Größe des Hintergrundes für die Tabs, in Abhängigkeit des positionierungs Modus, welche in der CTabs::CreateTabsArea() Methode angegeben werden: 

//+-----------------------------------------------------------------+
//| Erzeugung des Tab Hintergrundes                                 |
//+-----------------------------------------------------------------+
bool CTabs::CreateTabsArea(void)
  {
//--- Den Objektnamen bilden
   string name=CElement::ProgramName()+"_tabs_area_"+(string)CElement::Id();
//--- Koordinaten
   int x=CElement::X();
   int y=CElement::Y();
//--- Größe
   int x_size=SumWidthTabs();
   int y_size=0;
//--- Berechnung der Größen in Relation zu der Positionierung der Tabs
   if(m_position_mode==TABS_TOP || m_position_mode==TABS_BOTTOM)
     {
      y_size=m_tab_y_size;
     }
   else
     {
      y_size=m_tab_y_size*m_tabs_total-(m_tabs_total-1);
     }
//--- Einstellungen der Koordinaten für die Positionierung der Tabs auf der unteren und rechten Seite
   if(m_position_mode==TABS_BOTTOM)
     {
      y=CElement::Y2()-m_tab_y_size-1;
     }
   else if(m_position_mode==TABS_RIGHT)
     {
      x=CElement::X2()-x_size;
     }
//--- Erzeugung des Objektes
   if(!m_tabs_area.Create(m_chart_id,name,m_subwin,x,y,x_size,y_size))
      return(false);
//--- Festlegen der Eigenschaften
//--- Ränder von den Kanten
//--- Abspeichern der Größe
//--- Abspeichern der Koordinaten
//--- Abspeichern des Objekt-Pointers
//...
   return(true);
  }

Die CTabs::CreateButtons() Methode benötigt nur die Berechnung der Koordinaten um die Tabs zu erzeugen. Die Breite wird in einer benutzerdefinierten Klasse der Anwendung festgelegt, bevor das Control erzeugt wird. Andernfalls wird der Standardwert verwendet (Breite). Nachfolgend ist die abgekürzte Version der Methode: 

//+-----------------------------------------------------------------+
//| Erzeugung der Tabs                                              |
//+-----------------------------------------------------------------+
bool CTabs::CreateButtons(void)
  {
//--- Koordinaten
   int x =CElement::X();
   int y =CElement::Y();
//--- Berechnung der Koordinaten in Relation zu der Positionierung der Tabs
   if(m_position_mode==TABS_BOTTOM)
      y=CElement::Y2()-m_tab_y_size-1;
   else if(m_position_mode==TABS_RIGHT)
      x=CElement::X2()-SumWidthTabs();
//--- Überprüfung des Index des selektierten Tabs
   CheckTabIndex();
//--- Erzeugung der Tabs
   for(int i=0; i<m_tabs_total; i++)
     {
      //--- Den Objektnamen bilden
      string name=CElement::ProgramName()+"_tabs_edit_"+(string)i+"__"+(string)CElement::Id();
      //--- Berechnung der Koordinaten in Relation zu der Positionierung der Tabs für jeden individuellen Tab
      if(m_position_mode==TABS_TOP || m_position_mode==TABS_BOTTOM)
         x=(i>0) ? x+m_tab[i-1].m_width-1 : CElement::X();
      else
         y=(i>0) ? y+m_tab_y_size-1 : CElement::Y();
      //--- Erzeugung des Objektes
      if(!m_tabs[i].Create(m_chart_id,name,m_subwin,x,y,m_tab[i].m_width,m_tab_y_size))
         return(false);
      //--- Festlegen der Eigenschaften
      //--- Ränder von den Kanten des Panels
      //--- Koordinaten
      //--- Größe
      //--- Initialisierung des Arrays
      //--- Abspeichern des Objekt-Pointers
     }
//---
   return(true);
  }

Um einen bestimmten Tab einen control hinzufügen zu können, schreiben wir die CTabs::AddToElementsArray() Methode. Sie besitzt zwei Argumente: (1) Tab Index, zu welchem das Control hinzugefügt werden soll und (2) Eine Referenz zu dem Control, Ein Pointer, welcher in dem Array der Tab-Ccontrols abgespeichert werden muss

class CTabs : public CElement
  {
public:
   //--- Minecontrol dem Tab-Array hinzufügen
   void              AddToElementsArray(const int tab_index,CElement &object);
  };
//+-----------------------------------------------------------------+
//| Ein Control dem Array ausgewählten Tab hinzufügen               |
//+-----------------------------------------------------------------+
void CTabs::AddToElementsArray(const int tab_index,CElement &object)
  {
//--- Überprüfung der Überschreitung des ABCs
   int array_size=::ArraySize(m_tab);
   if(array_size<1 || tab_index<0 || tab_index>=array_size)
      return;
//--- Hinzufügen des Pointers des übergebenen Controls zu dem Array des angegebenen Tabs
   int size=::ArraySize(m_tab[tab_index].elements);
   ::ArrayResize(m_tab[tab_index].elements,size+1);
   m_tab[tab_index].elements[size]=::GetPointer(object);
  }

Bei einem Wechsel zwischen den Tabs, müssen die Controls des zuletzt selektierten Tabs versteckt werden und die Controls des jetzt selektierten Tabs angezeigt werden. Hierfür schreiben wir die CTabs::ShowTabElements() Methode. Zu Beginn wird eine Überprüfung der Sichtbarkeit der Controls durchgeführt. Falls das Control versteckt ist, dann bricht das Programm die Methode ab. Als nächstes wird der Index des aktiven Tabs überprüft und falls notwendig korrigiert. Dann werden in einer Schleife alle Tabs überprüft und die Hauptaufgabe der Methode wird ausgeführt.  

class CTabs : public CElement
  {
public:
   //--- Zeige nur die Controls des selektierten Tabs
   void              ShowTabElements(void);
  };
//+-----------------------------------------------------------------+
//| Zeige nur die Controls des selektierten Tabs                    |
//+-----------------------------------------------------------------+
void CTabs::ShowTabElements(void)
  {
//--- Abbrechen, falls die Tabs versteckt sind
   if(!CElement::IsVisible())
      return;
//--- Überprüfung des Index des selektierten Tabs
   CheckTabIndex();
//---
   for(int i=0; i<m_tabs_total; i++)
     {
      //--- Abfrage der Anzahl der controls, die zu diesem Tab gehören
      int tab_elements_total=::ArraySize(m_tab[i].elements);
      //--- Falls dieses Tab selektiert ist
      if(i==m_selected_tab)
        {
         //--- Anzeigen der Controls des Tabs
         for(int j=0; j<tab_elements_total; j++)
            m_tab[i].elements[j].Show();
        }
      //--- Verstecke in die Controls von inaktiven Tabs
      else
        {
         for(int j=0; j<tab_elements_total; j++)
            m_tab[i].elements[j].Hide();
        }
     }
  }

Die CTabs::OnClickTab() Methode wird für die Verarbeitung eines Klicks auf einen Tab benötigt. Zu nächsten müssen zwei Überprüfungen vorgenommen werden: (1) basierend auf den Namen des angeklickten Objektes und (2) basierend auf dem Bezeichner des Controls, der aus dem Objektnamen unter Verwendung der CTabs::IdFromObjectName() Methode gewonnen wird. Wenn die Überprüfung erfolgreich waren, dann (1) ermittelt das Programm das angeklickte Tab in einer Schleife, (2) speichert seinen Index ab und (3) setzt die entsprechenden Farben. Am Ende der CTabs::ShowTabElements() Methode, werden nur die Controls des aktiven Tabs sichtbar gemacht. 

class CTabs : public CElement
  {
private:
   //--- Verarbeitung eines Klicks auf einen Tab
   bool              OnClickTab(const string pressed_object);
   //--- Den Bezeichner aus dem Objektnamen entnehmen
   int               IdFromObjectName(const string object_name);
  };
//+-----------------------------------------------------------------+
//| Verarbeitung eines Klicks auf einen Tab                         |
//+-----------------------------------------------------------------+
bool CTabs::OnClickTab(const string clicked_object)
  {
//--- Abbrechen, falls es sich nicht um einen Klick auf eine Zelle dieser Tabelle handelt
   if(::StringFind(clicked_object,CElement::ProgramName()+"_tabs_edit_",0)<0)
      return(false);
//--- Den Bezeichner aus dem Objektnamen entnehmen
   int id=IdFromObjectName(clicked_object);
//--- Abbrechen, falls der Bezeichner nicht übereinstimmt
   if(id!=CElement::Id())
      return(false);
//---
   for(int i=0; i<m_tabs_total; i++)
     {
      //--- Falls auf dieses Tab geklickt wurde
      if(m_tabs[i].Name()==clicked_object)
        {
         //--- Abspeichern des Index des selektierten Tabs
         SelectedTab(i);
         //--- Setzen der Farben
         m_tabs[i].Color(m_tab_text_color_selected);
         m_tabs[i].BackColor(m_tab_color_selected);
        }
      else
        {
         //--- Festlegen der Farbe für die inaktiven Tabs
         m_tabs[i].Color(m_tab_text_color);
         m_tabs[i].BackColor(m_tab_color);
        }
     }
//--- Zeige nur die Controls des selektierten Tabs
   ShowTabElements();
   return(true);
  }

Nachfolgend wird der Programmcode für den CTabs::OnEvent() Eventhandler gezeigt: 

//+-----------------------------------------------------------------+
//| Chart Eventhandler                                              |
//+-----------------------------------------------------------------+
void CTabs::OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam)
  {
//--- Verarbeiten des Mauszeiger Bewegungs Events
   if(id==CHARTEVENT_MOUSE_MOVE)
     {
      //--- Abbrechen, falls das Element versteckt ist
      if(!CElement::IsVisible())
         return;
      //--- Koordinaten
      int x=(int)lparam;
      int y=(int)dparam;
      for(int i=0; i<m_tabs_total; i++)
         m_tabs[i].MouseFocus(x>m_tabs[i].X() && x<m_tabs[i].X2() && y>m_tabs[i].Y() && y<m_tabs[i].Y2());
      //---
      return;
     }
//--- Verarbeiten eines Klicks mit der linken Maustaste auf ein Objekt
   if(id==CHARTEVENT_OBJECT_CLICK)
     {
      //--- Anklicken eines Tabs
      if(OnClickTab(sparam))
         return;
     }
  }

Es wurden nun alle Methoden der CTabs Klasse berücksichtigt. Lassen Sie uns nun einen Test durchführen. 

 


Testen des Tabs Control

Lassen Sie uns nun den EA aus dem vorherigen Teil dieser Serie für den Test verwenden. Entfernen Sie alle Elemente, bis auf das Hauptmenü und die Statusbar. Lassen Sie uns einen Test für alle Positionierungsmodi (Oben unten rechts links) Testen, sowie deren Größe (Höhe). Lassen Sie uns dafür den EA weitere externe Parameter in der Program.mqh Datei hinzufügen:

//--- Externe Parameter des Expert Advisors
input ENUM_TABS_POSITION TabsPosition =TABS_TOP; // Tabs Position
input                int TabsHeight   =20;       // Tabs Height

Anschließend deklarieren wir in der benutzerdefinierten Klasse der Anwendung(CProgram) eine Instanz der CTabs Klasse und Methoden für die Erzeugung des Tabs Controls mit Abständen von den Eckpunkten des Formulars: 

class CProgram : public CWndEvents
  {
private:
   //--- Tabs
   CTabs             m_tabs;
   //---
private:
   //--- Tabs
#define TABS1_GAP_X           (4)
#define TABS1_GAP_Y           (45)
   bool              CreateTabs(void);
  };

Es wird insgesamt 4 Tipps geben. Der angezeigte Text und die Breite der Tabs kann über die Initialisierungsarrays eingestellt werden. Die Werte der Elemente werden dann in einer Schleife unter Verwendung der CTabs::AddTab() Methode übergeben. Die Höhe und die Positionierung der Tabs wird über externe Parameter festgelegt. Das zweite Tab (index 1) wird standardmäßig vorselektiert sein, wenn das Programm auf dem Chart geladen wird. Der vollständige Programmcode der CProgram::CreateTabs() Methode wird nachfolgend dargestellt: 

//+-----------------------------------------------------------------+
//| Erzeuge einen Bereich mit Tabs                                  |
//+-----------------------------------------------------------------+
bool CProgram::CreateTabs(void)
  {
#define TABS1_TOTAL 4
//--- Übergabe des Panel Objektes
   m_tabs.WindowPointer(m_window1);
//--- Koordinaten
   int x=m_window1.X()+TABS1_GAP_X;
   int y=m_window1.Y()+TABS1_GAP_Y;
//--- Arrays mit dem Text und der Breite für die Tabs
   string tabs_text[]={"Tab 1","Tab 2","Tab 3","Tab 4"};
   int tabs_width[]={90,90,90,90};
//--- Festlegen der Eigenschaften vor der Erzeugung
   m_tabs.XSize(596);
   m_tabs.YSize(243);
   m_tabs.TabYSize(TabsHeight);
   m_tabs.PositionMode(TabsPosition);
   m_tabs.SelectedTab((m_tabs.SelectedTab()==WRONG_VALUE) ? 1 : m_tabs.SelectedTab());
   m_tabs.AreaColor(clrWhite);
   m_tabs.TabBackColor(C'225,225,225');
   m_tabs.TabBackColorHover(C'240,240,240');
   m_tabs.TabBackColorSelected(clrWhite);
   m_tabs.TabBorderColor(clrSilver);
   m_tabs.TabTextColor(clrGray);
   m_tabs.TabTextColorSelected(clrBlack);
//--- Füge Tabs mit angegebenen Eigenschaften hinzu
   for(int i=0; i<TABS1_TOTAL; i++)
      m_tabs.AddTab(tabs_text[i],tabs_width[i]);
//--- Erzeugung des Controls
   if(!m_tabs.CreateTabs(m_chart_id,m_subwin,x,y))
      return(false);
//--- Hinzufügen des Objektes zu dem gemeinsamen Array von Objektgruppen
   CWndContainer::AddToElementsArray(0,m_tabs);
   return(true);
  }

Die Methode muss in der Hauptmethode für die Erzeugung des grafischen Interfaces der Anwendung aufgerufen werden (Sehen Sie dazu nachfolgend die verkürzte Version der Methode): 

//+-----------------------------------------------------------------+
//| Erzeugung eines Expert-Bedienfeldes                             |
//+-----------------------------------------------------------------+
bool CProgram::CreateExpertPanel(void)
  {
//--- Erzeugen des Formulars 1 für die Controls
//---Erzeugung der Controls:
//    Hauptmenü
//--- Kontextmenüs
//--- Tabs
   if(!CreateTabs())
      return(false);
//--- Neuzeichnen des Charts
   m_chart.Redraw();
   return(true);
  }

Kompilieren Sie dieses Programm und laden Sie es auf einen Chart. Die Positionierungsmodi werden sukzessive mit den externen Parametern geändert (Sehen Sie sich dazu die nachfolgenden Screenshots an):

 Abbildung  2. Tabs Positionoierungsmodus — «Top».

Abbildung 2. Tabs Positionoierungsmodus — «Top».

 Abbildung 3. Tabs Positionoierungsmodus — «Bottom».

Abbildung 3. Tabs Positionoierungsmodus — «Bottom».

 Abbildung 4. Tabs Positionoierungsmodus — «Left».

Abbildung 4. Tabs Positionoierungsmodus — «Left».

 Abbildung 5. Tabs Positionoierungsmodus — «Right».

Abbildung 5. Tabs Positionoierungsmodus — «Right».

Lassen Sie uns nun testen, wie das Ganze mit Gruppen von Controls funktioniert, die wir jetzt jedem Tab hinzufügen. Erzeugen Sie dafür eine separate Kopie des gleich EAs und entfernen Sie alle externen Parameter. Hier werden die Tabs oben auf der Arbeitsfläche positioniert (TABS_TOP). 

  1. Dem ersten Tab wird eine Text-Label-Tabelle hinzugefügt. 
  2. Dem zweiten Tab wird eine Editbox-Tabelle hinzugefügt 
  3. Dem dritten Tab wird eine gerenderte Tabelle hinzugefügt.
  4. Dem vierten Tab wird eine Gruppe von Controls hinzugefügt, dazu gehören:

    • Vier Checkboxen
    • Vier Checkboxen mit Edit-Boxen
    • Vier Comboboxen mit Checkboxen
    • Eine Trennlinie 

In der benutzerdefinierten Klasse der Testanwendung (CProgram), deklarieren Sie (1) Instanzen dieser Controls, (2) Methoden für deren Erzeugung und (3) Abstände von der Ecke des Formulars (Sehen Sie dazu den Programmcode in dem nachfolgenden Listing): 

class CProgram : public CWndEvents
  {
private:
   //--- Text-Label-Tabelle
   CLabelsTable      m_labels_table;
   //--- Editbox-Tabelle
   CTable            m_table;
   //--- Gerenderte Tabelle
   CCanvasTable      m_canvas_table;
   //--- Checkboxen
   CCheckBox         m_checkbox1;
   CCheckBox         m_checkbox2;
   CCheckBox         m_checkbox3;
   CCheckBox         m_checkbox4;
   //--- Vier Checkboxen mit Edit-Boxen
   CCheckBoxEdit     m_checkboxedit1;
   CCheckBoxEdit     m_checkboxedit2;
   CCheckBoxEdit     m_checkboxedit3;
   CCheckBoxEdit     m_checkboxedit4;
   //--- Vier Comboboxen mit Checkboxen
   CCheckComboBox    m_checkcombobox1;
   CCheckComboBox    m_checkcombobox2;
   CCheckComboBox    m_checkcombobox3;
   CCheckComboBox    m_checkcombobox4;
   //--- Trennlinie
   CSeparateLine     m_sep_line;
   //---
private:
   //--- Text-Label-Tabelle
#define TABLE1_GAP_X          (5)
#define TABLE1_GAP_Y          (65)
   bool              CreateLabelsTable(void);
   //--- Editbox-Tabelle
#define TABLE2_GAP_X          (5)
#define TABLE2_GAP_Y          (65)
   bool              CreateTable(void);
   //--- Gerenderte Tabelle
#define TABLE3_GAP_X          (5)
#define TABLE3_GAP_Y          (65)
   bool              CreateCanvasTable(void);
   //--- Trennlinie
#define SEP_LINE_GAP_X        (300)
#define SEP_LINE_GAP_Y        (70)
   bool              CreateSepLine(void);
   //--- Checkboxen
#define CHECKBOX1_GAP_X       (18)
#define CHECKBOX1_GAP_Y       (75)
   bool              CreateCheckBox1(const string text);
#define CHECKBOX2_GAP_X       (18)
#define CHECKBOX2_GAP_Y       (175)
   bool              CreateCheckBox2(const string text);
#define CHECKBOX3_GAP_X       (315)
#define CHECKBOX3_GAP_Y       (75)
   bool              CreateCheckBox3(const string text);
#define CHECKBOX4_GAP_X       (315)
#define CHECKBOX4_GAP_Y       (175)
   bool              CreateCheckBox4(const string text);
   //--- Vier Checkboxen mit Edit-Boxen
#define CHECKBOXEDIT1_GAP_X   (40)
#define CHECKBOXEDIT1_GAP_Y   (105)
   bool              CreateCheckBoxEdit1(const string text);
#define CHECKBOXEDIT2_GAP_X   (40)
#define CHECKBOXEDIT2_GAP_Y   (135)
   bool              CreateCheckBoxEdit2(const string text);
#define CHECKBOXEDIT3_GAP_X   (337)
#define CHECKBOXEDIT3_GAP_Y   (105)
   bool              CreateCheckBoxEdit3(const string text);
#define CHECKBOXEDIT4_GAP_X   (337)
#define CHECKBOXEDIT4_GAP_Y   (135)
   bool              CreateCheckBoxEdit4(const string text);
   //--- Vier Comboboxen mit Checkboxen
#define CHECKCOMBOBOX1_GAP_X  (40)
#define CHECKCOMBOBOX1_GAP_Y  (205)
   bool              CreateCheckComboBox1(const string text);
#define CHECKCOMBOBOX2_GAP_X  (40)
#define CHECKCOMBOBOX2_GAP_Y  (235)
   bool              CreateCheckComboBox2(const string text);
#define CHECKCOMBOBOX3_GAP_X  (337)
#define CHECKCOMBOBOX3_GAP_Y  (205)
   bool              CreateCheckComboBox3(const string text);
#define CHECKCOMBOBOX4_GAP_X  (337)
#define CHECKCOMBOBOX4_GAP_Y  (235)
   bool              CreateCheckComboBox4(const string text);
  };

In den vorherigen Artikel haben wir wiederholt gezeigt, wie Controls erzeugt und zu einem Formular hinzugefügt werden. Daher werden wir hier nur zeigen, wie ein Konto zu einem Tab hinzugefügt wird. Hier reicht das einfachste Beispiel - eine Trennlinie - aus. In dem nachfolgenden Programmcode, ist die Zeile mit dem Aufruf der CTabs::AddToElementsArray() Methode in Gelb hervorgehoben. Das erste Argument, ist der Index des Tabs, zu welchem ein Control hinzugefügt werden soll. In diesem Fall ist der Index 3, was dem vierten Tab entspricht. Das zweite Argument ist das Objekt, welches dem Tab hinzugefügt werden soll.

//+-----------------------------------------------------------------+
//| Erzeugt eine Trennlinie                                         |
//+-----------------------------------------------------------------+
bool CProgram::CreateSepLine(void)
  {
//--- Abspeichern des Fenster-Pointers
   m_sep_line.WindowPointer(m_window1);
//--- Hinzufügen zu dem vierten Tab der ersten Gruppe von Tabs
   m_tabs.AddToElementsArray(3,m_sep_line);
//--- Koordinaten  
   int x=m_window1.X()+SEP_LINE_GAP_X;
   int y=m_window1.Y()+SEP_LINE_GAP_Y;
//--- Größe
   int x_size=2;
   int y_size=210;
//--- Festlegen der Eigenschaften vor der Erzeugung
   m_sep_line.DarkColor(C'213,223,229');
   m_sep_line.LightColor(clrWhite);
   m_sep_line.TypeSepLine(V_SEP_LINE);
//--- Erzeugung des Elementes
   if(!m_sep_line.CreateSeparateLine(m_chart_id,m_subwin,0,x,y,x_size,y_size))
      return(false);
//--- Hinzufügen des Pointers des Elementes zu der Basis
   CWndContainer::AddToElementsArray(0,m_sep_line);
   return(true);
  }

Nachdem das grafische Interface der Anwendung erzeugt wurde, muss die CTabs::ShowTabElements() Methode aufgerufen werden, damit nur noch die Controls des aktiven Tabs dargestellt werden. (Nachfolgend sehen Sie eine verkürzte Version der Methode). Falls dieses nicht gemacht wird, dann sind alle Controls von allen Tabs sichtbar. 

//+-----------------------------------------------------------------+
//| Erzeugung eines Expert-Bedienfeldes                             |
//+-----------------------------------------------------------------+
bool CProgram::CreateExpertPanel(void)
  {
//--- Erzeugen des Formulars 1 für die Controls
//---Erzeugung der Controls:
//--- Hauptmenü
//--- Kontextmenüs
//--- Status bar
//--- Tabs
//...
//--- Text-Label-Tabelle
   if(!CreateLabelsTable())
      return(false);
//--- Editbox-Tabelle
   if(!CreateTable())
      return(false);
//--- Erzeugung der gerenderten Tabelle
   if(!CreateCanvasTable())
      return(false);
//--- Trennlinie
   if(!CreateSepLine())
      return(false);
//--- Checkboxen
   if(!CreateCheckBox1("Checkbox 1"))
      return(false);
   if(!CreateCheckBox2("Checkbox 2"))
      return(false);
   if(!CreateCheckBox3("Checkbox 3"))
      return(false);
   if(!CreateCheckBox4("Checkbox 4"))
      return(false);
//--- Vier Checkboxen mit Edit-Boxen
   if(!CreateCheckBoxEdit1("Checkbox Edit 1:"))
      return(false);
   if(!CreateCheckBoxEdit2("Checkbox Edit 2:"))
      return(false);
   if(!CreateCheckBoxEdit3("Checkbox Edit 3:"))
      return(false);
   if(!CreateCheckBoxEdit4("Checkbox Edit 4:"))
      return(false);
//--- Vier Comboboxen mit Checkboxen
   if(!CreateCheckComboBox1("CheckCombobox 1:"))
      return(false);
   if(!CreateCheckComboBox2("CheckCombobox 2:"))
      return(false);
   if(!CreateCheckComboBox3("CheckCombobox 3:"))
      return(false);
   if(!CreateCheckComboBox4("CheckCombobox 4:"))
      return(false);
//--- Nur die Controls des aktiven Tabs anzeigen
   m_tabs.ShowTabElements();
//--- Neuzeichnen des Charts
   m_chart.Redraw();
   return(true);
  }

Das Ergebnis sollte so wie in dem nachfolgend gezeigten Screenshot aussehen:

 Abbildung 6. Die Controls des ersten Tabs.

Abbildung 6. Die Controls des ersten Tabs.

Abbildung 7. Die Controls des zweiten Tabs 

Abbildung 7. Die Controls des zweiten Tabs

 Fig. 8. Die Controls bis dritten Tabs

Fig. 8. Die Controls bis dritten Tabs

 Fig. 9. Die Controls des vierten Tabs

Fig. 9. Die Controls des vierten Tabs

Alles funktioniert wie vorgesehen. Der siebte Teil der Serie der Bibliothek, der den grafischen Schnittstellen gewidmet ist, kann nun abgeschlossen werden. Als Ergänzung können Sie eine weitere Klasse (CIconTabs) für die Erzeugung von Tabs mit einer erweiterten Funktionalität herunterladen, die diesem Artikel beigefügt sind. Anders als bei der CTabs Klasse, können die Controls vom Typ CIconTabs einstellbare Icons für jeden Tab besitzen. Dieses kann das grafische Interface noch benutzerfreundlicher machen. 

Die Icons und der Text können mit der nachfolgenden Methode sehr genau auf jedem Tab platziert werden: 

//+-----------------------------------------------------------------+
//| Klasse für die Erzeugung von Icon-Tabs                          |
//+-----------------------------------------------------------------+
class CIconTabs : public CElement
  {
private:
   //--- Label Abstände
   int               m_icon_x_gap;
   int               m_icon_y_gap;
   //--- Die Ränder des Text Labels
   int               m_label_x_gap;
   int               m_label_y_gap;
   //---
public:
   //--- Label Abstände
   void              IconXGap(const int x_gap)                       { m_icon_x_gap=x_gap;            }
   void              IconYGap(const int y_gap)                       { m_icon_y_gap=y_gap;            }
   //--- Die Ränder des Text Labels
   void              LabelXGap(const int x_gap)                      { m_label_x_gap=x_gap;           }
   void              LabelYGap(const int y_gap)                      { m_label_y_gap=y_gap;           }
  };

Ein Beispiel für das Aussehen der Icon-Tabs-Controls, wird in dem nachfolgenden Screenshot gezeigt:

 Abbildung 10. «Icon Tabs» Control.

Abbildung 10. Icon Tabs Control.

Der Programmcode dieses Expert Advisors kann ebenfalls mit den an diesen Artikel beigefügten Dateien heruntergeladen werden. 

 

 


Schlussfolgerung

Zur Zeit sieht das schematische Diagramm unserer Bibliothek für das Erzeugen von grafischen Interfaces wie folgt aus:

 Abbildung 11. Die Struktur unserer Bibliothek zum aktuellen Stand der Entwicklung

Abbildung 11. Die Struktur unserer Bibliothek zum aktuellen Stand der Entwicklung

Der siebte Teil der Serie über grafische Interfaces innerhalb des MetaTrader Trading-Terminals, deckt die Erzeugung von Tabellen und Tab-Controls ab. Es werden damit dre Tabellenklassen (CLabelsTable, CTable und CCanvasTable) für die Erzeugung von Tabellen angeboten und zwei Klassen (CTabs and CIconTabs) für die Erzeugung von Tabs. 

Der nächste (achte) wird sich mit den folgenden Controls beschäftigen.

  • Ein statischer und ein Dropdown-Kalender
  • Ein Baumverzeichnis
  • Ein Dateien-Navigator

Sie können das Material des siebten Teils dieser Serie über die angehängten Dateien herunterladen und testen. Wenn Sie fragen zur Verwendung dieses Materials haben, dann können Sie zunächst auf die detaillierte Beschreibung in der Bibliothek zurückgreifen oder Sie stellen Ihre Frage(n) in den Kommentaren zu diesem Artikel.

Liste der Artikel (Kapitel) des siebten Teils:

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

Beigefügte Dateien |
Wie man eine Handelsstrategie in MetaTrader 5 schnell entwickeln und debuggen kann Wie man eine Handelsstrategie in MetaTrader 5 schnell entwickeln und debuggen kann
Automatische Scalping-Systeme gelten zurecht als der Höhepunkt des algorithmischen Tradings, aber es ist auch am kompliziertesten, einen Code für diese Systeme zu schreiben. In diesem Artikel zeigen wir, wie man mithilfe von eingebauten Werkzeugen für Debugging und visuelles Testen Strategien entwickeln kann, die auf der Analyse eingehender Ticks basieren. Um Regeln für Einstieg und Ausstieg zu erarbeiten, braucht man häufig jahrelang manuellen zu handeln. Aber mithilfe von MetaTrader 5 können Sie jede solche Strategie anhand realer historischer Daten schnell testen.
Cross-Plattform Expert Advisor: Wiederverwendung von Komponenten aus der Standardbibliothek von MQL5 Cross-Plattform Expert Advisor: Wiederverwendung von Komponenten aus der Standardbibliothek von MQL5
Es gibt einige Komponenten in der Standardbibliothek von MQL5, die für eine MQL4-Version eines Cross-Plattform Expert Advisors hilfreich sein könnten. Dieser Artikel befasst sich mit einem Verfahren zur Herstellung von bestimmten Komponenten aus der Standardbibliothek von MQL5, das sie kompatibel für den Kompiler der Programmiersprache MQL4 macht.
Graphische Interfaces VIII: Der Kalender (Kapitel 1) Graphische Interfaces VIII: Der Kalender (Kapitel 1)
Im Kapitel VIII der Reihe von Artikeln, die sich der Erstellung graphischer Oberflächen im MetaTrader widmet, betrachten wir komplexe, zusammengesetzte Steuerelemente wie Kalender, Baumdarstellung und einen Dateinavigator. Aufgrund der umfangreichen Informationen gibt es für jedes Thema eigene Artikel. Das erste Kapitel dieses Teil beschreibt das Kalenderelement und seine erweiterte Version, ein Dropdown-Kalender.
Cross-Plattform Expert Advisor: Einführung Cross-Plattform Expert Advisor: Einführung
Dieser Artikel erläutert eine Methode, durch die leichter und schneller Cross-Plattform Expert Advisors entwickelt werden können. Die vorgeschlagene Methode vereinigt die Funktionen beider Versionen in einer einzigen Klasse, und trennt die Umsetzung der abgeleiteten Klassen für die inkompatible Funktionen.