Graphische Interfaces III: Gruppen von einfachen und multifunktionalen Buttons (Kapitel 2)

Anatoli Kazharski | 3 Juli, 2016

Inhalt

 


Einleitung

Der erste ArtikelGrafische Interfaces I: Vorbereitung der Bibliotheksstruktur(Kapitel 1) betrachtet im Detail den Sinn und Zweck der Bibliothek. Eine vollständige Liste der Links zu den Artikeln finden Sie am Ende von jedem Kapitel. Zudem finden Sie dort eine Möglichkeit das Projekt, entsprechend dem aktuellen Entwicklungszustand, herunterzuladen. Die Dateien müssen in den gleichen Verzeichnissen untergebracht werden, so, wie Sie auch in dem Archiv abgelegt sind.

In dem ersten Kapitel dieser Serie ging es um einfache und multifunktionelle Buttons. Der zweite Artikel handelt über Gruppen von interagierenden Buttons, mit denen der Programmierer Elemente erzeugen kann, bei der der Anwender ein Element aus der Gruppe auswählen kann.

 


Entwicklung einer Klasse für die Erzeugung von Gruppen von einfachen Buttons

Eine Gruppe von einfachen Buttons ist im Wesentlichen ein Array von grafischen Objekten des Typs OBJ_BUTTON. Die unterschiedliche Verhaltensweise solcher Controls besteht darin, dass immer nur ein Button dieser Gruppe gleichzeitig ausgewählt werden kann. In dem augenblicklichen Stadium, kann eine Klasse dieser Controls auf zwei Wege erzeugt werden:

  1. Durch das Erzeugen einer Gruppe von schon bereits implementierten Controls des Typs CSimpleButton;
  2. Durch das Erzeugen einer Gruppe von einfachen Objekten des Typs CButton.

Die zweite Option ist einfacher, da sie keine weitere Methode für die jedes Control des TypsCSimpleButton benötigt, um an die Basis der Pointer zu gelangen. Wie werden diese Option verwenden. 

Erzeugen Sie die ButtonsGroup.mqh Datei mit der CButtonsGroup Klasse in dem Controls Verzeichnis, welches alle anderen Controls beinhaltet und beziehen Sie sie in derWndContainer.mqh Datei mit ein. Diese Klasse muss virtuelle Methoden besitzen und den Pointer der Form, so wie es auch schon in allen anderen zuvor entwickelten Controls der Fall war. Diese werden wir hier nicht weiter besprechen und gehen direkt zu der Beschreibung der Eigenschaften und Methoden für die Konstruktion der neuen Klasse über. 

Es wird einige gemeinsame Eigenschaften für jeden Button innerhalb einer Gruppe geben, aber es wird auch ein paar spezielle Eigenschaften geben. Im Folgenden sehen Sie zwei Gruppen von Eigenschaften, bezogen auf ihr Erscheinungsbild / Funktion:

Gemeinsame Eigenschaften der Buttons:

  • Höhe:
  • Farbe der gesperrten Buttons;
  • Rahmenfarbe der verfügbaren und gesperrten Modi;
  • Die Textfarbe in den unterschiedlichen Zuständen;
  • Priorität der linken Maustaste.

Gemeinsame Eigenschaften der Buttons:

  • Status des Buttons (gedrückt/nicht gedrückt);
  • Abstände vom Rand des Formulars;
  • Text;
  • Breite;
  • Die Farbe der Buttons in den unterschiedlichen Zuständen;
  • Die Gradienten für die Buttons.

Die Ränder der Buttons erlauben es, die Buttons in jeder gewünschten Art anzuordnen.

Abbildung  1.  Ein Beispiel für die Anordnung von Buttons in einer Gruppe.

Abbildung 1. Ein Beispiel für die Anordnung von Buttons in einer Gruppe.


Deklarieren Sie dynamische Arrays für die Objekte des Typs CButton und ihre eindeutigen Eigenschaften, so wie es in dem nachfolgenden Programmcode gezeigt ist. 

//+----------------------------------------------------------------
//| Klasse für die Erzeugung einer Gruppe von einfachen Buttons                     |
//+----------------------------------------------------------------
class CButtonsGroup : public CElement
  {
private:
   //--- Objekte für die Erzeugung eines Buttons
   CButton           m_buttons[];
   //--- Button Gradienten
   struct ButtonsGradients
     {
      color             m_buttons_color_array[];
     };
   ButtonsGradients  m_buttons_total[];
   //--- Die Eigenschaften des Buttons:
   //    Arrays Für spezielle Eigenschaften von Buttons
   bool              m_buttons_state[];
   int               m_buttons_x_gap[];
   int               m_buttons_y_gap[];
   string            m_buttons_text[];
   int               m_buttons_width[];
   color             m_buttons_color[];
   color             m_buttons_color_hover[];
   color             m_buttons_color_pressed[];
   //--- Höhe der Buttons
   int               m_button_y_size;
   //--- Farbe der gesperrten Buttons
   color             m_back_color_off;
   //--- Rahmenfarbe in dem aktiven und gesperrten Modus
   color             m_border_color;
   color             m_border_color_off;
   //--- Die Textfarbe
   color             m_text_color;
   color             m_text_color_off;
   color             m_text_color_pressed;
   //--- Die Priorität der linken Maustaste
   int               m_buttons_zorder;
   //---
public:
   //--- Anzahl der Buttons
   int               ButtonsTotal(void)                       const { return(::ArraySize(m_buttons));  }
   //--- (1) Höhe der Buttons
   void              ButtonYSize(const int y_size)                  { m_button_y_size=y_size;          }
   //--- (1) Hintergrundfarben eines blockierten Buttons und Rahmens ((2) verfügbar/(3) gesperrt)
   void              BackColorOff(const color clr)                  { m_back_color_off=clr;            }
   void              BorderColor(const color clr)                   { m_border_color=clr;              }
   void              BorderColorOff(const color clr)                { m_border_color_off=clr;          }
   //--- Die Textfarbe
   void              TextColor(const color clr)                     { m_text_color=clr;                }
   void              TextColorOff(const color clr)                  { m_text_color_off=clr;            }
   void              TextColorPressed(const color clr)              { m_text_color_pressed=clr;        }
  };

Die Größe des dynamischen Arrays wird definiert, wenn die Gruppe von Buttons gebildet wird und bevor sie erzeugt werden (Hinzufügen zu dem Chart). Jedesmal, wenn die CButtonsGroup::AddButton() Methode aufgerufen wird, wird das Array vergrößert und mit den Elementen aufgefüllt, die mit der Methode übergeben wurden.

class CButtonsGroup : public CElement
  {
public:
   //--- Fügt vor der Erzeugung einen Button mit den angegebenen Eigenschaften hinzu
   void              AddButton(const int x_gap,const int y_gap,const string text,const int width,
                               const color button_color,const color button_color_hover,const color button_color_pressed);
  };
//+----------------------------------------------------------------
//| Hinzufügen eines Buttons                                                    |
//+----------------------------------------------------------------
void CButtonsGroup::AddButton(const int x_gap,const int y_gap,const string text,const int width,
                              const color button_color,const color button_color_hover,const color pressed_button_color)
  {
//--- Vergrößern des Arrays um ein Element
   int array_size=::ArraySize(m_buttons);
   ::ArrayResize(m_buttons,array_size+1);
   ::ArrayResize(m_buttons_total,array_size+1);
   ::ArrayResize(m_buttons_state,array_size+1);
   ::ArrayResize(m_buttons_x_gap,array_size+1);
   ::ArrayResize(m_buttons_y_gap,array_size+1);
   ::ArrayResize(m_buttons_text,array_size+1);
   ::ArrayResize(m_buttons_width,array_size+1);
   ::ArrayResize(m_buttons_color,array_size+1);
   ::ArrayResize(m_buttons_color_hover,array_size+1);
   ::ArrayResize(m_buttons_color_pressed,array_size+1);
//--- Abspeichern der übergebenen Werte
   m_buttons_x_gap[array_size]         =x_gap;
   m_buttons_y_gap[array_size]         =y_gap;
   m_buttons_text[array_size]          =text;
   m_buttons_width[array_size]         =width;
   m_buttons_color[array_size]         =button_color;
   m_buttons_color_hover[array_size]   =button_color_hover;
   m_buttons_color_pressed[array_size] =pressed_button_color;
   m_buttons_state[array_size]         =false;
  }

Wenn kein Button mit der CButtonsGroup::AddButton() Methode erzeugt worden ist, wird der Prozess für das Erzeugen eines grafischen Interfaces angehalten und eine entsprechende Nachricht wird in dem Journal angezeigt.. Die Buttons werden in einer Schleife, wie es in der hier verkürzten Methode CButtonsGroup::CreateButtons() dargestellt ist. 

class CButtonsGroup : public CElement
  {
public:
   //--- Methoden für die Erzeugung eines Buttons
   bool              CreateButtonsGroup(const long chart_id,const int subwin,const int x,const int y);
   //---
private:
   bool              CreateButtons(void);
  };
//+----------------------------------------------------------------
//| Erzeugte Buttons                                                  |
//+----------------------------------------------------------------
bool CButtonsGroup::CreateButtons(void)
  {
//--- Koordinaten
   int l_x =m_x;
   int l_y =m_y;
//--- Anzahl der Buttons abfragen
   int buttons_total=ButtonsTotal();
//--- Falls es keinen Button in dieser Gruppe gibt, dann benachrichtigen
   if(buttons_total<1)
     {
      ::Print(__FUNCTION__," > This method is to be called "
              "if a group contains at least one button! Use the CButtonsGroup::AddButton() method");
      return(false);
     }
//--- Erzeugen der angegebenen Anzahl von Buttons
   for(int i=0; i<buttons_total; i++)
     {
      //--- Den Objektnamen bilden
      //--- Die Koordinaten berechnen
      //--- Einen Button einrichten
      //--- Festlegen seiner Eigenschaften
      //--- Abspeichern der Abstände, die Koordinaten und seine Größe
      //--- Initialisierung des Gradienten-Arrays
      //--- Abspeichern des Objekt-Pointers
     }
//---
   return(true);
  }

Lassen Sie uns für diesen Typ von Gruppen-Buttons zwei Modi erzeugen. Um dieses einzurichten, fügen wir in der Klasse einen Bereich ein, wie es in dem folgenden Code dargestellt ist: Der Standardwert ist false, was bedeutet, dass es erlaubt ist, dass alle Buttons in der Gruppe nicht gedrückt sind. Der Standardwert true bedeutet, dass immer ein Button gedrückt sein muss.

class CButtonsGroup : public CElement
  {
public:
   //--- Der Radio-Button Modus
   bool              m_radio_buttons_mode;
   //---
public:
   //--- Installieren des Radio-Button Modus
   void              RadioButtonsMode(const bool flag)              { m_radio_buttons_mode=flag;       }
  };

Wie bei jedem anderen Control auch, muss diese Klasse ebenfalls eine Methode für das Blockieren des Controls enthalten. Für diesen Typ von Gruppen-Buttons, ist es wichtig sicherzustellen, dass wenn ein vorheriger gedrückter Button wieder entsperrt wird, er sein Erscheinungsbild auch wieder aktualisiert

class CButtonsGroup : public CElement
  {
private:
   //--- Verfügbar oder blockiert
   bool              m_buttons_group_state;
   //---
public:
   //--- Genereller Status der Button-Gruppe (available/blocked)
   bool              ButtonsGroupState(void)                  const { return(m_buttons_group_state);   }
   void              ButtonsGroupState(const bool state);
  };
//+----------------------------------------------------------------
//| Ändern des Status des Buttons                                    |
//+----------------------------------------------------------------
void CButtonsGroup::ButtonsGroupState(const bool state)
  {
   m_buttons_group_state=state;
//---
   int buttons_total=ButtonsTotal();
   for(int i=0; i<buttons_total; i++)
     {
      m_buttons[i].State(false);
      m_buttons[i].Color((state)? m_text_color : m_text_color_off);
      m_buttons[i].BackColor((state)? m_buttons_color[i]: m_back_color_off);
      m_buttons[i].BorderColor((state)? m_border_color : m_border_color_off);
     }
//--- Den Button drücken falls er vor dem Blockieren gedrückt war
   if(m_buttons_group_state)
     {
      if(m_selected_button_index!=WRONG_VALUE)
        {
         m_buttons_state[m_selected_button_index]=true;
         m_buttons[m_selected_button_index].Color(m_text_color_pressed);
         m_buttons[m_selected_button_index].BackColor(m_buttons_color_pressed[m_selected_button_index]);
        }
     }
  }

Wir brauchen eine Methode um den Status des Buttonshin und her zu schalten, wenn er gedrückt wird.. Wir benötigen zudem Felder und Methoden für das Abspeichern und Abfragen des Textes und des Indexes des hervorgehobenen Buttons..

Am Anfang der Methode, die für das hin- und herschalten des Status des Buttons zuständig ist, ist eine Überprüfung der Anzahl der Buttons innerhalb einer Gruppe. Wenn sich herausstellt, dass es keine Buttons gibt, dann wird eine entsprechende Nachricht herausgegeben. Das Programm wird die Methode aber nicht verlassen. Das Programm wird fortführen und an einem bestimmten Punkt eine Fehlermeldung über das Überschreiten der Größe des m_buttons_state[] Arrays melden. Das bedeutet, dass ein Entwickler eine Applikation mindestens einen Button zu der Gruppe hinzufügen muss, damit ein solcher Fehler nicht auftritt. Nach der Überprüfung, wenn es mindestens einen Button gibt, wird das Programm den mitgegebenen Index anpassen, falls die Größe des Arrays überschritten wird. Anschließend wird der Status des Buttons, der dem angegebenen Index entspricht, geändert. Anschließend bekommen in einer Schleife alle anderen Buttons den Zustand "nicht gedrückt"(Die entsprechende Farbe wird gesetzt) unter Berücksichtigung des Gruppenmodus. Mit anderen Worten, falls der Radio-Button Modus aktiv ist und es mindestens einen gedrückten Button geben muss, dann wird während der Iteration eine Überprüfung der Übereinstimmung des aktuellen Indexes mit dem übergebenen Index durchgeführt. Wenn der Modus, dass alle Buttons nicht gedrückt sein können aktiviert ist, dann wird zusätzlich zu der Überprüfung des Index auch noch der aktuelle Status des Buttons überprüft. Wenn die Bedingung erfüllt ist, dann wird das Flag für den gedrückten Button gesetzt, unabhängig von dem aktuellen Modus. Am Ende der Methode, in der Klasse wo der Text und der Index des hervorgehobenen Buttons gespeichert wird, werden die Felder mit den entsprechenden Werten unter Berücksichtigung des Flags gefüllt. Wenn es keinen gedrückten Button gibt, werden leere Werte gesetzt("" und WRONG_VALUE). 

class CButtonsGroup : public CElement
  {
private:
   //--- (1) Text und (2) Index des hervorgehobenen Buttons
   string            m_selected_button_text;
   int               m_selected_button_index;
   //---
public:
   //--- Gibt (1) den Text und (2) den Index des hervorgehobenen Buttons zurück
   string            SelectedButtonText(void)                 const { return(m_selected_button_text);  }
   int               SelectedButtonIndex(void)                const { return(m_selected_button_index); }
   //--- Wechselt den Status des Buttons mit dem angegebenen Index
   void              SelectionButton(const int index);
  };
//+----------------------------------------------------------------
//| Wechselt den Status des Buttons mit dem angegebenen Index        |
//+----------------------------------------------------------------
void CButtonsGroup::SelectionButton(const int index)
  {
//--- Für die Überprüfung ob ein Button innerhalb einer Gruppe gedrückt ist
   bool check_pressed_button=false;
//--- Anzahl der Buttons abfragen
   int buttons_total=ButtonsTotal();
//--- Falls es keinen Button in dieser Gruppe gibt, dann benachrichtigen
   if(buttons_total<1)
     {
      ::Print(__FUNCTION__," > This method is to be called "
              "if a group contains at least one button! Use the CButtonsGroup::AddButton() method");
     }
//--- Anpassen des Indexwertes, falls die Größe des Arrays überschritten wird
   int correct_index=(index>=buttons_total)? buttons_total-1 : (index<0)? 0 : index;
//--- Wechseln des Status aller entgegengesetzten Buttons
   m_buttons_state[correct_index]=(m_buttons_state[correct_index])? false : true;
//--- Iteration über eine Gruppe von Buttons
   for(int i=0; i<buttons_total; i++)
     {
      //--- Wird ein relevanter check in Abhängigkeit des Modus durchgeführt
      bool condition=(m_radio_buttons_mode)? (i==correct_index) : (i==correct_index && m_buttons_state[i]);
      //--- Falls die Bedingungen zutreffen, bekommt der Button den gedrückten Status
      if(condition)
        {
         if(m_radio_buttons_mode)
            m_buttons_state[i]=true;
         //--- Es gibt einen gedrückten Button
         check_pressed_button=true;
         //--- Festlegen der Farbe
         m_buttons[i].Color(m_text_color_pressed);
         m_buttons[i].BackColor(m_buttons_color_pressed[i]);
         CElement::InitColorArray(m_buttons_color_pressed[i],m_buttons_color_pressed[i],m_buttons_total[i].m_buttons_color_array);
        }
      //--- Falls die Bedingungen nicht zutreffen, wird der Button in den nicht gedrückten Zustand versetzt
      else
        {
         //--- Setzen des deaktivierte Status und der Farbe
         m_buttons_state[i]=false;
         m_buttons[i].Color(m_text_color);
         m_buttons[i].BackColor(m_buttons_color[i]);
         CElement::InitColorArray(m_buttons_color[i],m_buttons_color_hover[i],m_buttons_total[i].m_buttons_color_array);
        }
      //--- Zurücksetzen des normalen Status des Buttons
      m_buttons[i].State(false);
     }
//--- Wenn es einen gedrückten Button gibt, abspeichern des Textes und des Indexes
   m_selected_button_text  =(check_pressed_button) ? m_buttons[correct_index].Description() : "";
   m_selected_button_index =(check_pressed_button) ? correct_index : WRONG_VALUE;
  }

Um einen Klick auf einen gut wachsen behandeln zu können, erzeugen wir die CButtonsGroup::OnClickButton() Methode. Entsprechend zu einigen anderen gleichartigen Methoden, die Wir vorher schon besprochen haben, werden die folgenden Überprüfungen durchgeführt:

  • Überprüfung des Namens;
  • Der Identifizierer, bzw. Bezeichner. Um den Bezeichner von den Objektnamen zu extrahieren, verwenden wir die CButtonsGroup::IdFromObjectName() Methode. Der Code dieser Methode ist ähnlich wie in schon vorherigen besprochenen gleichartigen Methoden aus anderen Klassen. Wir werden diesen hier daher nicht weiter besprechen;
  • Für den aktuellen Status(verfügbar/gesperrt).

Wenn alle Überprüfungen erfolgreich abgeschlossen worden sind und das Programm die Methode nicht verlassen hat, wird der Wechsel von den Zuständen der Buttons durch dieCButtonsGroup::SelectionButton() Methode ausgeführt. Am Ende dieser Methode, wird über die Events eine Nachricht mit den Daten über den gedrückten Button versendet. Diese Nachricht kann in dem Eventhandler der benutzerdefinierten Klasse empfangen werden.

class CButtonsGroup : public CElement
  {
private:
   //--- Verarbeiten eines Klicks auf einen Button
   bool              OnClickButton(const string clicked_object);
   //--- Den Bezeichner aus dem Namen des Buttons entnehmen
   int               IdFromObjectName(const string object_name);
  };
//+----------------------------------------------------------------
//| Chart Eventhandler                                              |
//+----------------------------------------------------------------
void CButtonsGroup::OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam)
  {
//--- Verarbeiten eines Klicks mit der linken Maustaste auf ein Objekt
   if(id==CHARTEVENT_OBJECT_CLICK)
     {
      if(OnClickButton(sparam))
         return;
     }
  }
//+----------------------------------------------------------------
//| Drücken eines Buttons innerhalb einer Gruppe                     |
//+----------------------------------------------------------------
bool CButtonsGroup::OnClickButton(const string pressed_object)
  {
//--- Abbrechen, falls der Klick nicht auf diesem Menüpunkt stattgefunden hat
   if(::StringFind(pressed_object,CElement::ProgramName()+"_buttons_",0)<0)
      return(false);
//--- Den Bezeichner aus dem Objektnamen entnehmen
   int id=IdFromObjectName(pressed_object);
//--- Abbrechen, falls der Bezeichner nicht passt
   if(id!=CElement::Id())
      return(false);
//--- Für die Überprüfung des Index
   int check_index=WRONG_VALUE;
//--- Überprüfen ob der Klick auf einen der Buttons dieser Gruppe ausgeführt wurde
   int buttons_total=ButtonsTotal();
//--- Abbrechen wenn die Buttons gesperrt sind
   if(!m_buttons_group_state)
     {
      for(int i=0; i<buttons_total; i++)
         m_buttons[i].State(false);
      //---
      return(false);
     }
//--- Falls ein Krieg stattgefunden hat, Abspeichern des Index
   for(int i=0; i<buttons_total; i++)
     {
      if(m_buttons[i].Name()==pressed_object)
        {
         check_index=i;
         break;
        }
     }
//--- Abbrechen, falls kein Button dieser Gruppe gedrückt wurde
   if(check_index==WRONG_VALUE)
      return(false);
//--- Wechsel des Status des Buttons
   SelectionButton(check_index);
//--- Eine Nachricht darüber senden
   ::EventChartCustom(m_chart_id,ON_CLICK_BUTTON,CElement::Id(),m_selected_button_index,m_selected_button_text);
   return(true);
  }

Jetzt richten wir noch die Reaktion der Gruppe bei einer Bewegung des Mauszeigers über die Buttons hinweg ein, sowie den Klick mit der linken Maustaste. Dafür schreiben wir die CButtonsGroup::CheckPressedOverButton() Methode, welche in dem Eventhandler während der Verarbeitung des CHARTEVENT_MOUSE_MOVE Events aufgerufen wird. Bevor die Methode aufgerufen wird, werden folgende Überprüfungen vorgenommen: (1) ob das Konto sichtbar ist, (2) ob es verfügbar ist, (3) ob die Form verfügbar ist und(4) ob die linke Maustaste gedrückt worden ist.

class CButtonsGroup : public CElement
  {
private:
   //--- Überprüfen, ob die linke Maustaste über den Group Buttons gedrückt wurde
   void              CheckPressedOverButton(void);
  };
//+----------------------------------------------------------------
//| Chart Eventhandler                                              |
//+----------------------------------------------------------------
void CButtonsGroup::OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam)
  {
//--- Verarbeiten der Events über die Bewegung des Mauszeigers
   if(id==CHARTEVENT_MOUSE_MOVE)
     {
      //--- Abbrechen, falls das Control versteckt ist
      if(!CElement::IsVisible())
         return;
      //--- Abbrechen wenn die Buttons gesperrt sind
      if(!m_buttons_group_state)
         return;
      //--- Den Fokus definieren
      int x=(int)lparam;
      int y=(int)dparam;
      int buttons_total=ButtonsTotal();
      for(int i=0; i<buttons_total; i++)
        {
         m_buttons[i].MouseFocus(x>m_buttons[i].X() && x<m_buttons[i].X2() && 
                                 y>m_buttons[i].Y() && y<m_buttons[i].Y2());
        }
      //--- Abbrechen, falls die Forum blockiert ist
      if(m_wnd.IsLocked())
         return;
      //--- Abbrechen, falls die Maustaste nicht gedrückt ist
      if(sparam!="1")
         return;
      //--- Überprüfen, ob die linke Maustaste über den Group Buttons gedrückt wurde
      CheckPressedOverButton();
      return;
     }
  }
//+----------------------------------------------------------------
//| Überprüfung der gedrückten linken Maustaste über den Group Buttons|
//+----------------------------------------------------------------
void CButtonsGroup::CheckPressedOverButton(void)
  {
   int buttons_total=ButtonsTotal();
//--- Die Farbe, entsprechend dem Ort wo die linke Maustaste gedrückt wurde, setzen
   for(int i=0; i<buttons_total; i++)
     {
      //--- Wenn es einen Fokus gibt dann die Farbe des gedrückten Buttons
      if(m_buttons[i].MouseFocus())
         m_buttons[i].BackColor(m_buttons_color_pressed[i]);
      //--- Falls es keinen Fokus gibt, dann...
      else
        {
         //--- ...Falls kein Group-Button gedrückt ist, Zuweisen der Hintergrundfarbe
         if(!m_buttons_state[i])
            m_buttons[i].BackColor(m_buttons_color[i]);
        }
     }
  }

Nun ist alles für einen ersten Test der einfachen Group-Buttons in unserer Test Anwendung bereit. Erzeugen Sie eine Kopie des EAs, welchen wir zuvor getestet haben Entfernen Sie alle Controls mit Ausnahme des Hauptmenüs und seinen Kontextmenüs. Wir werden dieses Programm für den Test von verschiedenen Button-Groups in diesem Artikel verwenden.

Erzeugen sie eine Instanz der CButtonsGroup Klasse der Button-Groups in der benutzerdefinierten Klasse. Deklarieren Sie die CProgram::CreateButtonsGroup1() Methode für ihre Erzeugung und die Ränder von den Seiten der Form. 

class CProgram : public CWndEvents
  {
private:
   //--- Gruppe mit einfachen Buttons
   CButtonsGroup     m_buttons_group1;
   //---
private:
   //--- Gruppe mit einfachen Buttons
#define BUTTONS_GROUP1_GAP_X (7)
#define BUTTONS_GROUP1_GAP_Y (50)
   bool              CreateButtonsGroup1(void);
  };

Erzeugen sie eine Gruppe mit 4 Buttons. Platzieren Sie diese horizontal. In diesem Beispiel werden wir zwei Buttons rot machen und zwei blau. Die Implementation der CProgram::CreateButtonsGroup1() Methode wird in den nachfolgenden Code gezeigt. Wenn Sie direkt nach dem Start einen Button hervorheben möchten, dann verwenden Sie die CButtonsGroup::SelectionButton() public Methode

//+----------------------------------------------------------------
//| Erzeugt eine Gruppe von einfachen Buttons                                |
//+----------------------------------------------------------------
bool CProgram::CreateButtonsGroup1(void)
  {
//--- Abspeichern des Fenster-Pointers
   m_buttons_group1.WindowPointer(m_window);
//--- Koordinaten
   int x =m_window.X()+BUTTONS_GROUP1_GAP_X;
   int y =m_window.Y()+BUTTONS_GROUP1_GAP_Y;
//--- Eigenschaften
   int    buttons_x_gap[]         ={0,72,144,216};
   string buttons_text[]          ={"BUTTON 1","BUTTON 2","BUTTON 3","BUTTON 4"};
   int    buttons_width[]         ={70,70,70,70};
   color  buttons_color[]         ={C'195,0,0',C'195,0,0',clrRoyalBlue,clrRoyalBlue};
   color  buttons_color_hover[]   ={C'255,51,51',C'255,51,51',C'85,170,255',C'85,170,255'};
   color  buttons_color_pressed[] ={C'135,0,0',C'135,0,0',C'50,100,135',C'50,100,135'};
//--- Festlegen der Eigenschaften
   m_buttons_group1.TextColor(clrWhite);
   m_buttons_group1.TextColorPressed(clrGold);
//--- Vier Buttons einer Gruppe hinzufügen
   for(int i=0; i<4; i++)
      m_buttons_group1.AddButton(buttons_x_gap[i],0,buttons_text[i],buttons_width[i],
                                 buttons_color[i],buttons_color_hover[i],buttons_color_pressed[i]);
//--- Eine Gruppe von Buttons erzeugen
   if(!m_buttons_group1.CreateButtonsGroup(m_chart_id,m_subwin,x,y))
      return(false);
//--- Hervorheben des zweiten Buttons in der Gruppe
   m_buttons_group1.SelectionButton(1);
//--- Ein Objekt dem gemeinsamen Array von Objektgruppen hinzufügen
   CWndContainer::AddToElementsArray(0,m_buttons_group1);
   return(true);
  }

Nachdem sie den Programmcode kompiliert haben und den Test EA dem Chart hinzugefügt haben, sollten Sie das folgende Ergebnis sehen:

Abbildung  2. Der Test einer Gruppe mit einfachen Buttons.

Abbildung 2. Der Test einer Gruppe mit einfachen Buttons.


Wir haben nun mit der ersten Gruppe von Buttons abgeschlossen. Die vollständige Version in der CButtonsGroup Klasse kann am Ende des Artikels heruntergeladen werden. Bei dem nächsten control handelt es sich um eine Gruppe von Radiobuttons. 


 


Entwicklung einer Klasse für die Erzeugung einer Gruppe von Radiobuttons

Erzeugen Sie die RadioButtons.mqh Datei mit der CRadioButtons Klasse, welche Standard virtuelle Methoden und Klassen für das Abspeichern und Abfragen des Formular-Pointers besitzen muss. Sie können sich die Beispiele in den Klassen der anderen Controls ansehen, die wir oben gezeigt haben. Beziehen Sie die RadioButtons.mqh Datei in der Bibliothek (WndContainer.mqh) mit ein.

Jeder Radio-Button besteht aus drei einfachen Objekten:

  1. Hintergrund;
  2. Icon;
  3. Text-Label.

Abbildung 3. Komponenten des Radio-Buttons

Abbildung 3. Komponenten des Radio-Buttons


Anders als die Gruppe der einfachen Buttons, hat die Gruppe der Radio-Buttons nur einen Modus. Das liegt daran, dass in diesem Control nicht alle Buttons gleichzeitig ungedrückt sein können. In dieser Gruppe ist immer ein Button gedrückt. Die Hintergrundfarbe der Gruppe ist normalerweise gleich der Hintergrundfarbe des Formulars, zu welchem die Gruppe hinzugefügt worden ist. Im Wesentlichen dient sie dazu, den Fokus zu identifizieren und den Klick auf einen Radiobutton nachzuverfolgen (höhere Priorität) In dieser Version werden wir nur einen Farbwechsel des Textlabels durchführen, sobald der Mauszeiger sich über dem Hintergrund des Buttons befindet, oder ihn verlässt. Die Liste der speziellen und gemeinsamen Eigenschaften von einer Radiobutton-Group sind unterschiedlich zu der Liste und Eigenschaften einer Gruppe von einfachen Buttons, wie es wie folgt dargestellt wird:

Gemeinsame Eigenschaften:

  • Farbe des Hintergrundes und des Rahmens.
  • Textfarbe.
  • Höhe.
  • Icons der Buttons für (1) aktiv, (2) deaktiviert und (3) gesperrten Status.
  • Priorität eines Klicks mit der linken Maustaste.

Spezielle Eigenschaften:

  • Text.
  • Breite.
  • Status. Nur ein Button in der Gruppe kann gedrückt sein.
  • Ränder. Ebenso wie die einfachen Buttons können auch die Radiobuttons in jeder beliebigen Reihenfolge angeordnet werden. (Mosaic, horizontal, vertikal...)
  • Gradienten für das Text-Label.


Nachfolgend können Sie den Programmcode der CRadioButtons Klasse sehen. Die Größenanpassung der Arrays für die speziellen Eigenschaften und das Auffüllen der Werte, werden in der CRadioButtons::AddButton() public Methode vor der Erzeugung der Controls in der benutzerdefinierten Klasse durchgeführt. Wenn keine Buttons hinzugefügt worden sind, dann wird die Erzeugung des grafischen Interfaces abgebrochen. Dieses haben wir bereits während der Entwicklung der Klasse für die einfachen Gruppen-Buttons besprochen und werden daher hier nicht näher darauf eingehen.

//+----------------------------------------------------------------
//| Klasse für die Erzeugung einer Gruppe mit Radiobuttons                       |
//+----------------------------------------------------------------
class CRadioButtons : public CElement
  {
private:
   //--- Gradienten für das Text-Label.
   struct LabelsGradients
     {
      color             m_labels_color_array[];
     };
   LabelsGradients   m_labels_total[];
   //--- Die Eigenschaften des Buttons:
   //    (1) Color and (2) priority of the background of the left mouse click
   color             m_area_color;
   int               m_area_zorder;
   //--- Arrays für die speziellen Eigenschaften von Buttons
   bool              m_buttons_state[];
   int               m_buttons_x_gap[];
   int               m_buttons_y_gap[];
   int               m_buttons_width[];
   string            m_buttons_text[];
   //--- Höhe der Buttons
   int               m_button_y_size;
   //--- Die Icons für die Buttons in dem aktiven, inaktiven und gesperrten Zustand
   string            m_icon_file_on;
   string            m_icon_file_off;
   string            m_icon_file_on_locked;
   string            m_icon_file_off_locked;
   //--- Die Textfarbe
   color             m_text_color;
   color             m_text_color_off;
   color             m_text_color_hover;
   //--- Die Priorität der linken Maustaste
   int               m_buttons_zorder;
   //---
public:
   //--- Festlegung der Icons des Buttons in dem aktiven, inaktiven und gesperrten Status
   void              IconFileOn(const string file_path)           { m_icon_file_on=file_path;         }
   void              IconFileOff(const string file_path)          { m_icon_file_off=file_path;        }
   void              IconFileOnLocked(const string file_path)     { m_icon_file_on_locked=file_path;  }
   void              IconFileOffLocked(const string file_path)    { m_icon_file_off_locked=file_path; }
   //--- (1) Hintergrundfarbe, (2) Textfarbe
   void              AreaColor(const color clr)                   { m_area_color=clr;                 }
   void              TextColor(const color clr)                   { m_text_color=clr;                 }
   void              TextColorOff(const color clr)                { m_text_color_off=clr;             }
   void              TextColorHover(const color clr)              { m_text_color_hover=clr;           }

   //--- Fügt vor der Erzeugung einen Button mit den angegebenen Eigenschaften hinzu
   void              AddButton(const int x_gap,const int y_gap,const string text,const int width);
  };

Jetzt müssen wir die Arrays für die Instanzen von primitiven Objektklassen und Methoden für deren Erzeugung erschaffen. Ähnlich wie bei der Gruppe der einfachen Buttons, werden die Radiobuttons in einer Schleife erzeugt. Aber hier Befindet sich die Schleife in der Hauptmethode und der Index, welcher zur Erzeugung des Namens von jedem Objekt beiträgt, wird der Methode für die Erzeugung dieser Objekte übergeben.

class CRadioButtons : public CElement
  {
private:
   //--- Objekte für die Erzeugung eines Buttons
   CRectLabel        m_area[];
   CBmpLabel         m_icon[];
   CLabel            m_label[];
   //---
public:
   //--- Methoden für die Erzeugung eines Buttons
   bool              CreateRadioButtons(const long chart_id,const int window,const int x,const int y);
   //---
private:
   bool              CreateArea(const int index);
   bool              CreateRadio(const int index);
   bool              CreateLabel(const int index);
   //---
public:
   //--- Anzahl der Buttons
   int               RadioButtonsTotal(void)                const { return(::ArraySize(m_icon));      }
  };
//+----------------------------------------------------------------
//| Erzeugt eine Gruppe von Button Objekten                            |
//+----------------------------------------------------------------
bool CRadioButtons::CreateRadioButtons(const long chart_id,const int window,const int x,const int y)
  {
//--- Verlassen, wenn es keinen Pointer zu einer Form gibt
   if(::CheckPointer(m_wnd)==POINTER_INVALID)
     {
      ::Print(__FUNCTION__," > Before creating a group of radio buttons, the class must be passed "
              "the form pointer: CButtonsGroup::WindowPointer(CWindow &object)");
      return(false);
     }
//--- Initialisierung der Variablen
   m_id       =m_wnd.LastId()+1;
   m_chart_id =chart_id;
   m_subwin   =window;
   m_x        =x;
   m_y        =y;
//--- Abfrage der Anzahl der Buttons in der Gruppe
   int radio_buttons_total=RadioButtonsTotal();
//--- Falls es keinen Button in dieser Gruppe gibt, dann benachrichtigen
   if(radio_buttons_total<1)
     {
      ::Print(__FUNCTION__," > This method is to be called "
              "if a group contains at least one button! Use the CRadioButtons::AddButton() method");
      return(false);
     }
//--- Einrichten einer Gruppe von Buttons
   for(int i=0; i<radio_buttons_total; i++)
     {
      CreateArea(i);
      CreateRadio(i);
      CreateLabel(i);
      //--- Zurücksetzen des Fokus
      m_area[i].MouseFocus(false);
     }
//--- Verstecken des Elementes, falls es sich um ein Dialogfenster handelt, oder falls es minimiert ist
   if(m_wnd.WindowType()==W_DIALOG || m_wnd.IsMinimized())
      Hide();
//---
   return(true);
  }

Diese Klasse benötigt zu den Methoden für die Änderung des Zustandes eines Controls (verfügbar/gesperrt) und das Wechseln des Zustands über den angegebenen Index. In diesem Falle sind die Methoden ein wenig einfacher als in der CButtonsGroup Klasse, da eine Gruppe von Radiobutton es nur einen Modus besitzt. Diese Methoden finden Sie in den beigefügten Dateien zu diesem Artikel. Zusätzlich benötigen wir noch Methoden für die Abfrage des Textes und des Indexes von einem hervorgehobenen Button, so wie es auch in der Klasse für die einfachen Buttons der Fall ist.

class CButtonsGroup : public CElement
  {
private:
   //--- (1) Text und (2) Index des hervorgehobenen Buttons
   string            m_selected_button_text;
   int               m_selected_button_index;
   //--- Verfügbar oder blockiert
   bool              m_buttons_group_state;
   //---
public:
   //--- Genereller Status der Button-Gruppe (available/blocked)
   bool              ButtonsGroupState(void)                  const { return(m_buttons_group_state);   }
   void              ButtonsGroupState(const bool state);
   //--- Gibt (1) den Text und (2) den Index des hervorgehobenen Buttons zurück
   string            SelectedButtonText(void)                 const { return(m_selected_button_text);  }
   int               SelectedButtonIndex(void)                const { return(m_selected_button_index); }
   //--- Wechselt den Status des Buttons mit dem angegebenen Index
   void              SelectionButton(const int index);
  };

Für das Senden einer Nachricht über das Anklicken eines Controls, welches wie ein Text-Label auf dem Formular aussieht, verwenden wir den neuen Bezeichner ON_CLICK_LABEL Fügen Sie folgendes der Defines.mqh Datei hinzu:

#define ON_CLICK_LABEL            (10) // Pressing of the text label

Nachfolgend wird die Implementation der Methoden für die Verarbeitung eines Klicks auf die Group-Buttons dargestellt. Nach dem erfolgreichen Abschluss der folgenden Überprüfungen (1) Die Zugehörigkeit zu dem Control Typ, (2) der Bezeichner und (3) die Verfügbarkeit, Wird der Index des angeklickten Buttons über den exakten Namen in der Schleife identifiziert. Falls ein Button dieser Gruppe gedrückt wurde und dieser Button im Augenblick nicht hervorgehoben ist, dann wird ein Wechsel des Zustandes durchgeführt und es wird eine NAchricht gesendet, welche in der benutzerdefinierten Klasse empfangen werden kann.

class CButtonsGroup : public CElement
  {
private:
   //--- Verarbeiten eines Klicks auf einen Button
   bool              OnClickButton(const string pressed_object);
   //--- Abfrage des Bezeichners über den Namen des radio buttons
   int               IdFromObjectName(const string object_name);
  };
//+----------------------------------------------------------------
//| Chart Eventhandler                                              |
//+----------------------------------------------------------------
void CRadioButtons::OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam)
  {
//--- Verarbeiten eines Klicks mit der linken Maustaste auf ein Objekt
   if(id==CHARTEVENT_OBJECT_CLICK)
     {
      //--- Wechsel des Status des Buttons
      if(OnClickButton(sparam))
         return;
     }
  }
//+----------------------------------------------------------------
//| Klick auf einen Radiobutton                                       |
//+----------------------------------------------------------------
bool CRadioButtons::OnClickButton(const string pressed_object)
  {
//--- Abbrechen, falls der Klick nicht auf diesem Menüpunkt stattgefunden hat
   if(::StringFind(pressed_object,CElement::ProgramName()+"_radio_area_",0)<0)
      return(false);
//--- Abfragen des Bezeichners und des Indexes über den Objektnamen
   int id=IdFromObjectName(pressed_object);
//--- Abbrechen, falls es nicht ein Klick auf ein Item war, zu welchem dieses Kontextmenü hinzugefügt wurde
   if(id!=CElement::Id())
      return(false);
//--- Für die Überprüfung des Index
   int check_index=WRONG_VALUE;
//--- Abbrechen wenn die Buttons gesperrt sind
   if(!m_radio_buttons_state)
      return(false);
//--- Falls ein Krieg stattgefunden hat, Abspeichern des Index
   int radio_buttons_total=RadioButtonsTotal();
   for(int i=0; i<radio_buttons_total; i++)
     {
      if(m_area[i].Name()==pressed_object)
        {
         check_index=i;
         break;
        }
     }
//--- Abbrechen, falls es keinen Klick auf einen Button dieser Gruppe gab, oder
//    Falls dieser Radiobutton schon hervorgehoben ist
   if(check_index==WRONG_VALUE || check_index==m_selected_button_index)
      return(false);
//--- Wechsel des Status des Buttons
   SelectionRadioButton(check_index);
//--- Eine Nachricht darüber senden
   ::EventChartCustom(m_chart_id,ON_CLICK_LABEL,CElement::Id(),check_index,m_selected_button_text);
   return(true);
  }

Nun ist alles bereit für einen Test. Fügen Sie dem EA vier Gruppen mit je 4 Radiobuttons hinzu(CRadioButtons) und eine Gruppe mit einfachen Buttons (CButtonsGroup). 

class CProgram : public CWndEvents
  {
private:
   //--- Gruppe mit Radiobuttons 1
   CRadioButtons     m_radio_buttons1;
   //--- Gruppe mit einfachen Buttons 2
   CButtonsGroup     m_buttons_group2;
   //--- Gruppe mit Radiobuttons 2,3,4
   CRadioButtons     m_radio_buttons2;
   CRadioButtons     m_radio_buttons3;
   CRadioButtons     m_radio_buttons4;
   //---
private:
   //--- Gruppe mit Radiobuttons 1
#define RADIO_BUTTONS1_GAP_X     (7)
#define RADIO_BUTTONS1_GAP_Y     (75)
   bool              CreateRadioButtons1();
   //--- Gruppe mit einfachen Buttons 2
#define BUTTONS_GROUP2_GAP_X     (7)
#define BUTTONS_GROUP2_GAP_Y     (100)
   bool              CreateButtonsGroup2(void);
   //--- Gruppe mit Radiobuttons 2,3,4
#define RADIO_BUTTONS2_GAP_X     (7)
#define RADIO_BUTTONS2_GAP_Y     (125)
   bool              CreateRadioButtons2();
#define RADIO_BUTTONS3_GAP_X     (105)
#define RADIO_BUTTONS3_GAP_Y     (125)
   bool              CreateRadioButtons3();
#define RADIO_BUTTONS4_GAP_X     (203)
#define RADIO_BUTTONS4_GAP_Y     (125)
   bool              CreateRadioButtons4();
  };
//+----------------------------------------------------------------
//| Erzeugung des Trading-Panels                                     |
//+----------------------------------------------------------------
bool CProgram::CreateTradePanel(void)
  {
//--- Erzeugung einer Form für Controls
//---Erzeugung der Controls:
//    Hauptmenü
//--- Kontextmenüs
//--- Gruppe mit einfachen Buttons 1
//--- Gruppe mit Radiobuttons 1
   if(!CreateRadioButtons1())
      return(false);
//--- Gruppe mit einfachen Buttons 2
   if(!CreateButtonsGroup2())
      return(false);
   //--- Gruppe mit Radiobuttons 2,3,4
   if(!CreateRadioButtons2())
      return(false);
   if(!CreateRadioButtons3())
      return(false);
   if(!CreateRadioButtons4())
      return(false);
//--- Neuzeichnen auf dem Chart
   m_chart.Redraw();
   return(true);
  }

Beispielhaft zeigen wir hier die Implementation einer der Methoden für diese Gruppen. Der Rest unterscheidet sich nur durch Parameterwerte. 

//+----------------------------------------------------------------
//| Erzeugte Gruppe mit Radiobuttons 1                                 |
//+----------------------------------------------------------------
bool CProgram::CreateRadioButtons1(void)
  {
//--- Übergabe des Panel Objektes
   m_radio_buttons1.WindowPointer(m_window);
//--- Koordinaten
   int x =m_window.X()+RADIO_BUTTONS1_GAP_X;
   int y =m_window.Y()+RADIO_BUTTONS1_GAP_Y;
//--- Eigenschaften
   int    buttons_x_offset[] ={0,98,196};
   int    buttons_y_offset[] ={0,0,0};
   string buttons_text[]     ={"Radio Button 1","Radio Button 2","Radio Button 3"};
   int    buttons_width[]    ={92,92,92};
//---
   for(int i=0; i<3; i++)
      m_radio_buttons1.AddButton(buttons_x_offset[i],buttons_y_offset[i],buttons_text[i],buttons_width[i]);
//--- Eine Gruppe von Buttons erzeugen
   if(!m_radio_buttons1.CreateRadioButtons(m_chart_id,m_subwin,x,y))
      return(false);
//--- Hervorheben des zweiten Buttons in der Gruppe
   m_radio_buttons1.SelectedRadioButton(1);
//--- Sperren der Radiobuttons
   m_radio_buttons1.RadioButtonsState(false);
//--- Ein Objekt dem gemeinsamen Array von Objektgruppen hinzufügen
   CWndContainer::AddToElementsArray(0,m_radio_buttons1);
   return(true);
  }

Lassen Sie uns zwei Buttons in der zusätzlichen (zweiten) Gruppe von Buttons des Typs CButtonsGroup erzeugen. Lassen Sie uns diese für Illustrationszwecke wie folgt arrangieren: Der zweite Button dieser Gruppe blockiert die erste Gruppe mit einfachen Radiobuttons und der Erste macht sie wieder verfügbar.

//+----------------------------------------------------------------
//| Event handler                                                    |
//+----------------------------------------------------------------
void CProgram::OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam)
  {
//--- Der Button event
   if(id==CHARTEVENT_CUSTOM+ON_CLICK_BUTTON)
     {
      ::Print("id: ",id,"; lparam: ",lparam,"; dparam: ",dparam,"; sparam: ",sparam);
      //--- Falls der Bezeichner der zweiten Gruppe oder der einfachen Buttons und 
      //    der Text des hervorgehobenen Buttons in dieser Gruppe mit dem String-Parameter der Nachricht uebereinstimmt
      if(lparam==m_buttons_group2.Id() && sparam==m_buttons_group2.SelectedButtonText())
        {
         //--- Falls es sich um den Index des ersten Buttons handelt, entsperrung der angegebenen Controls
         if((int)dparam==0)
           {
            m_buttons_group1.ButtonsGroupState(true);
            m_radio_buttons1.RadioButtonsState(true);
           }
         //--- Falls es sich um den Index des zweiten Buttons handelt, sperren der angegebenen Controls
         else
           {
            m_buttons_group1.ButtonsGroupState(false);
            m_radio_buttons1.RadioButtonsState(false);
           }
        }
      return;
     }     
  }

Kompilieren Sie die Projektdateien und laden Sie das Programm auf einem Chart. In diesem Stadium der Entwicklung, sollten Sie ein Ergebnis erhalten, wie es in der nachfolgenden Abbildung gezeigt ist:

Abbildung 4. Test des Radiobutton Controls.

Abbildung 4. Test des Radiobutton Controls.


Wir haben hiermit die Entwicklung einer Klasse für die Erzeugung einer Gruppe mit Radiobuttons abgeschlossen. Sie können eine vollständige Version am Ende dieses Artikels herunterladen. 

 


Erzeugen einer Klasse für die Erzeugung einer Gruppe von Icon-Buttons

Wir haben zuvor schon die CIconButton Klasse für die Erzeugung eines Icon Button Controls erzeugt Nun werden wir ein Control implementieren, welches uns die Möglichkeit gibt, eine Gruppe von diesen Buttons zu erzeugen. Erzeugen Sie die IconButtonsGroup.mqh Datei mit der CIconButtonsGroup Klasse in dem Controls Verzeichnis, in welcher Sie die Standard virtuellen Methoden implementieren. Beziehen Sie diese Datei in der WndContainer.mqh Datei mit ein.

Wir beschreiben dieses nicht im Detail, da die CIconButtonsGroup Klasse im wesentlichen eine Zusammenstellung von Methoden ist, die wir bereits in der CButtonsGroup und CRadioButtons Klasse besprochen haben. Ähnlich wie bei der CRadioButtons Klasse mit Radiobuttons, werden die Objekte der Button Gruppe mit den privaten Methoden in einer Schleife der Hauptmethode für das Erzeugen des Controls erzeugt. Der einzige Parameter dieser Methode ist der Index, der für die Bildung des Namens des grafischen Objektes benötigt wird. Wir gestalten es so, dass die Buttons dieser Gruppe ihre Hintergrundfarbe und den Text ändern, wenn sich der Mauszeiger über ihnen befindet. 

Die Methoden für die Verarbeitung von Events haben wir in diesem Artikel bereits besprochen. Sie sind bei dieser Art von Gruppen-Buttons identisch, und daher zeigen wir hier nur den Inhalt derCIconButtonsGroup Klasse. Die Implementation der Methoden, die sich außerhalb des Körpers der Klasse befinden, können in den an diesen Artikel angefügten Dateien nachgesehen werden.

//+----------------------------------------------------------------
//| Klasse für die Erzeugung einer Gruppe mit Icon-Buttons           |
//+----------------------------------------------------------------
class CIconButtonsGroup : public CElement
  {
private:
   //--- Pointer zu dem Formular, zu welchem das Control hinzugefügt wurde
   CWindow          *m_wnd;
   //--- Objekte für die Erzeugung eines Buttons
   CButton           m_buttons[];
   CBmpLabel         m_icons[];
   CLabel            m_labels[];
   //--- Gradienten für das Text-Label.
   struct IconButtonsGradients
     {
      color             m_back_color_array[];
      color             m_label_color_array[];
     };
   IconButtonsGradients   m_icon_buttons_total[];
   //--- Die Eigenschaften des Buttons:
   //    Arrays Für spezielle Eigenschaften von Buttons
   bool              m_buttons_state[];
   int               m_buttons_x_gap[];
   int               m_buttons_y_gap[];
   string            m_buttons_text[];
   int               m_buttons_width[];
   string            m_icon_file_on[];
   string            m_icon_file_off[];
   //--- Höhe der Buttons
   int               m_buttons_y_size;
   //--- Die Hintergrundfarbe in den unterschiedlichen Modi
   color             m_back_color;
   color             m_back_color_off;
   color             m_back_color_hover;
   color             m_back_color_pressed;
   //--- Die Farbe des Rahmens
   color             m_border_color;
   color             m_border_color_off;
   //--- Icon Ränder
   int               m_icon_x_gap;
   int               m_icon_y_gap;
   //--- Text und Text Label Abstände
   int               m_label_x_gap;
   int               m_label_y_gap;
   //--- Text Label Farbe in den unterschiedlichen Modi
   color             m_label_color;
   color             m_label_color_off;
   color             m_label_color_hover;
   color             m_label_color_pressed;
   //--- (1) Text und (2) Index des hervorgehobenen Buttons
   string            m_selected_button_text;
   int               m_selected_button_index;
   //--- Generelle Priorität nicht anklickbarer Objekte
   int               m_zorder;
   //--- Die Priorität der linken Maustaste
   int               m_buttons_zorder;
   //--- Verfügbar oder blockiert
   bool              m_icon_buttons_state;
   //---
public:
                     CIconButtonsGroup(void);
                    ~CIconButtonsGroup(void);
   //--- Methoden für die Erzeugung eines Buttons
   bool              CreateIconButtonsGroup(const long chart_id,const int window,const int x,const int y);
   //---
private:
   bool              CreateButton(const int index);
   bool              CreateIcon(const int index);
   bool              CreateLabel(const int index);
   //---
public:
   //--- (1) Speichert den Pointer des Formulars, (2) Button-Höhe, (3) Anzahl von Buttons,
   //    (4) Genereller Status des Buttons (verfügbar/gesperrt)
   void              WindowPointer(CWindow &object)               { m_wnd=::GetPointer(object);      }
   void              ButtonsYSize(const int y_size)               { m_buttons_y_size=y_size;         }
   int               IconButtonsTotal(void)                 const { return(::ArraySize(m_icons));    }
   bool              IconButtonsState(void)                 const { return(m_icon_buttons_state);    }
   void              IconButtonsState(const bool state);
   //--- Hintergrundfarben der Buttons
   void              BackColor(const color clr)                   { m_back_color=clr;                }
   void              BackColorOff(const color clr)                { m_back_color_off=clr;            }
   void              BackColorHover(const color clr)              { m_back_color_hover=clr;          }
   void              BackColorPressed(const color clr)            { m_back_color_pressed=clr;        }
   //--- Icon Ränder
   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;             }
   //--- Gibt (1) den Text und (2) den Index des hervorgehobenen Buttons zurück
   string            SelectedButtonText(void)               const { return(m_selected_button_text);  }
   int               SelectedButtonIndex(void)              const { return(m_selected_button_index); }
   //--- Wechselt den Status eines Radiobuttons über den angegebenen Index
   void              SelectedRadioButton(const int index);

   //--- Fügt vor der Erzeugung einen Button mit den angegebenen Eigenschaften hinzu
   void              AddButton(const int x_gap,const int y_gap,const string text,
                               const int width,const string icon_file_on,const string icon_file_off);
   //--- Wechsel der Farbe
   void              ChangeObjectsColor(void);
   //---
public:
   //--- Chart Eventhandler
   virtual void      OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam);
   //--- Timer
   virtual void      OnEventTimer(void);
   //--- Verschieben eines Controls
   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 des Klicks mit der linken Maustaste
   virtual void      SetZorders(void);
   virtual void      ResetZorders(void);
   //---
private:
   //--- Verarbeiten eines Klicks auf einen Button
   bool              OnClickButton(const string pressed_object);
   //--- Überprüfen, ob die linke Maustaste über den Group Buttons gedrückt wurde
   void              CheckPressedOverButton(void);
   //--- Abfrage des Bezeichners über den Namen des radio buttons
   int               IdFromObjectName(const string object_name);
  };

Lassen Sie uns dieses Control nun testen. Als Beispiel erzeugen sie eine Gruppe von Icon-Buttons auf dem Formular desselben EAs, welchen wir zuvor für den Test der Group-Buttons des Typs CButtonsGroup und CRadioButtons verwendet haben. Dazu fügen Sie den nachfolgenden Programmcode der benutzerdefinierten Klasse hinzu.

class CProgram : public CWndEvents
  {
private:
   //--- Gruppe von Icon Buttons 1
   CIconButtonsGroup m_icon_buttons_group1;
   //---
private:
   //--- Gruppe von Icon Buttons 1
#define IBUTTONS_GROUP1_GAP_X    (7)
#define IBUTTONS_GROUP1_GAP_Y    (190)
   bool              CreateIconButtonsGroup1(void);
  };
//+----------------------------------------------------------------
//| Erzeugung des Trading-Panels                                     |
//+----------------------------------------------------------------
bool CProgram::CreateTradePanel(void)
  {
//--- Erzeugung einer Form für Controls
//---Erzeugung der Controls:
//    Hauptmenü
//--- Kontextmenüs
//--- Gruppe mit einfachen Buttons 1
//--- Gruppe mit Radiobuttons 1
//--- Gruppe mit einfachen Buttons 2
//--- Gruppe mit Radiobuttons 2,3,4
//--- Gruppe von Icon Buttons 1
   if(!CreateIconButtonsGroup1())
      return(false);
//--- Neuzeichnen auf dem Chart
   m_chart.Redraw();
   return(true);
  }

Kompilieren Sie die Projektdateien und laden Sie das Programm auf einem Chart. Das Ergebnis sollte so aussehen, wie es in dem nachfolgenden Screenshot gezeigt wird.

Abbildung 5. Test einer Gruppe mit Icon-Buttons

Abbildung 5. Test einer Gruppe mit Icon-Buttons


Die Entwicklung einer Klasse für die Erzeugung einer Gruppe mit Icon-Buttons ist nun abgeschlossen. Sie können eine vollständige Version am Ende dieses Artikels herunterladen.

 


Schlussfolgerung

Dieses ist das Ende des dritten Teils der Serie über die Entwicklung einer Bibliothek für die Erzeugung von grafischen Interfaces in den MetaTrader Trading Terminals. Im Augenblick sieht die Struktur der Bibliothek wie folgt aus:

Abbildung 6. Die Struktur der Bibliothek zum aktuellen Stand der Entwicklung.

Abbildung 6. Die Struktur der Bibliothek zum aktuellen Stand der Entwicklung.


In dem nächsten (vierten) Teil dieser Serie, werden wir die Entwicklung der Bibliothek fortsetzen. Wir werden die folgenden Punkte betrachten: 

  • Multi-Window Modus. 
  • Ein System für die Verwaltung von Prioritäten eines Klicks mit der linken Maustaste auf grafische Objekte. 
  • Informationselemente wie die Statusbar und Tooltips.

Die nachfolgend angehängten Archive mit den Programmdateien und Bildern entsprechend dem aktuellen Entwicklungsstatus, können für Tests in den MetaTrader Terminals heruntergeladen werden. Wenn Sie fragen zur Verwendung dieses Materials haben, dann können Sie zunächst auf die detaillierte Beschreibung in dem Artikel zu dieser Bibliothek zurückgreifen oder Sie stellen Ihre Frage(n) in den Kommentaren zu diesem Artikel.

Liste der Artikel(Kapitel) des dritten Teils: