
Graphische Interfaces III: Einfache und multifunktionale Buttons (Kapitel 1)
Inhalt
- Einleitung
- Entwicklung einer Klasse für die Erzeugung eines einfachen Buttons
- Entwicklung einer Klasse für die Erzeugung eines Icon-Buttons
- Entwicklung einer Klasse für die Erzeugung eines Split-Buttons
- Schlussfolgerung
Einleitung
In den vorangegangenen zwei Teilen dieser Serie haben wir einige Punkte über die Entwicklung einer Bibliotheksstruktur für die Erzeugung von grafischen Interfaces und grundlegende Mechanismen für die Verwaltung von Objekten besprochen.
- Grafische Interfaces I: Vorbereitung der Bibliotheksstruktur (Kapitel 1)
- Grafische Interfaces I: Formular mit Steuerelementen (Kapitel 2)
- Grafische Interfaces I: Animation des graphischen Interfaces (Kapitel 3)
- Grafische Interfaces I: Funktionen für die Form-Buttons und das Entfernen der Interface Elemente (Kapitel 4)
- Grafische Interfaces I: Testen der Bibliothek in unterschiedlichen Programmen und in dem MetaTrader 4 Terminal (Kapitel 5)
In dem zweiten Teil dieser Serie haben wir ein Beispiel für die Erzeugung eines Controls besprochen und wie man ihn mit der Bibliotheks-Engine verbindet. Das Beispiel war ziemlich schwierig. Hauptmenüs und Kontextmenüs gehören zu den schwierigsten Controls.
- Grafische Interfaces II:Das Menu-Item-Element(Kapitel 1)
- Grafische Interfaces II: Die Trennlinien und Kontext-Menüelemente (Kapitel 2)
- Grafische Interfaces II: Einrichtung des Eventhandlers für die Bibliothek (Kapitel 3)
- Grafische Interfaces II: Das Hauptmenü Element (Kapitel 4)
Dieser Teil des Artikels ist um einiges einfacher als der Vorherige. Hier besprechen wir lediglich das Control "Button".
Ein Button gehört zu den einfachsten Controls, mit welchem ein User interagieren kann. Gleichzeitig gibt es aber auch mehrere Wege der Implementation. In diesem Teil des Artikels werden wir drei Klassen für die Erzeugung von Buttons besprechen, in die unterschiedliche Ebenen der Komplexität besitzen.
- Einfacher Button. The CSimpleButton class.
- Icon Button. Die CIconButton Klasse.
- Split Button. Die CSplitButton Klasse.
Zuzüglich werden wir noch drei Klassen für die Erzeugung von Gruppen von interagierenden Buttons implementieren.
- Gruppe von einfachen Buttons Die CButtonsGroup Klasse.
- Gruppe von Icon-Buttons Die CIconButtonsGroup Klasse.
- Gruppe von Radio-Buttons Die CRadioButtons Klasse.
Wir werden zudem eine erweiterte Funktionalität des Kontextmenüs besprechen. Die CWindow Form-Klasse enthält ein weiteres Feld mit einer Methode, mit welcher wir definieren können, welches Control in dem Moment der Aktivierung des Formulars blockiert wird. Dies erlaubt es uns einen Zustand zu erzeugen, bei dem die Form nur über das Control freigegeben werden kann welches es auch blockiert hat.
Wie werden die Charakteristiken der Methoden aller Controls nicht weiter diskutieren, da wir sie schon in den vorherigen Artikeln besprochen haben. Diese Methoden werden in den nachfolgenden Programmcodes lediglich als Deklaration in den Körpern der Klassen dargestellt.
Entwicklung einer Klasse für die Erzeugung eines einfachen Buttons
Lassen Sie uns mit einem einfachen Button anfangen. Wir haben schon bereits eine Klasse für die Erzeugung eines einfachen Objektes vom TypCButton in der Objects.mqh Datei. CChartObjectButton ist die Basisklasse, welche dazu benutzt werden kann, ein grafisches Objekt vom Typ OBJ_BUTTON zu erstellen. Die Eigenschaften von diesem Objekt besitzen bereits zwei Zustände, an und aus. Die graphische Repräsentation kann auch zwei Optionen besitzen, in Abhängigkeit ob die Darstellung des Objektes aktiviert ist oder nicht. In beiden Modi, ist die Farbe des Buttons leicht dunkler, in dem Moment wo er gedrückt ist.
Dieses Objekt kann manuell zu einem Chart über das Hauptmenü hinzugefügt werden Einfügen -> Objects -> Graphische Objekte -> Button. Die Parameter eines grafischen Objektes können auch manuell über das Eigenschaftenfenster des grafischen Objektes geändert werden:
Abbildung 1. Das Eigenschaftenfenster eines grafischen Objektes.
Anschließend erstellen wir in dem Controls Verzeichnis die Datei SimpleButton.mqh. In dieser Datei erzeugen wir dieCSimpleButton Klasse mit den Feldern und Methoden der Standard-Controls, welche wir schon im Detail in den vorherigen Artikel besprochen haben.
//+------------------------------------------------------------------+ //| SimpleButton.mqh | //| Copyright 2015, MetaQuotes Software Corp. | //| http://www.mql5.com | //+------------------------------------------------------------------+ #include "Element.mqh" #include "Window.mqh" //+------------------------------------------------------------------+ //| Klasse für die Erzeugung eines einfachen Buttons | //+------------------------------------------------------------------+ class CSimpleButton : public CElement { private: //--- Ein Pointer zu der Form zu welchem das Element hinzugefügt worden ist CWindow *m_wnd; //--- public: CSimpleButton(void); ~CSimpleButton(void); //--- Speichert den Pointer 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 linken Maustaste virtual void SetZorders(void); virtual void ResetZorders(void); };
Lassen Sie uns definieren, welche Eigenschaften wir schon vor der Erstellung des Buttons festlegen können.
- Größe.
- Hintergrundfarben für die unterschiedlichen Zustände des Buttons und Farben in Abhängigkeit von der Position des Mauszeigers.
- Die Rahmenfarben für unterschiedliche Zustände des Buttons.
- Textfarben.
Manchmal wird auch ein Button benötigt, der automatisch seinen Zustand wieder umkehrt, nachdem er angeklickt wurde. Es gibt auch die Möglichkeit, dass ein Button, nachdem er angeklickt worden ist, in dem eingeschalteten Zustand verbleibt, und erst beim nächsten Klick wieder in seine normalen Zustand zurückkehrt. Wir werden den Button mit zwei Modi zur Auswahl austatten. Die IsPressed() Methode ist dazu da, um herauszufinden, ob sich der Button gerade in einem eingeschalteten oder ausgeschalteten Zustand befindet.
Zudem wird eineMöglichkeit zum Blockieren und Freigeben eines Buttons benötigt, falls der Entwickler einer Anwendung dieses verwenden will. Zum Beispiel kann eine Taste gesperrt werden, falls die Bedingung für die Verwendung der Funktion, die mit diesem Button in Zusammenhang steht, noch nicht erfüllt sind. Der Button ist in dem Moment wieder anklickbar, sobald die Bedingungen erfüllt sind. Wir werden dazu später ein paar Beispiele betrachten.
Lassen Sie uns die Methoden für die Erzeugung eines Buttons der Klasse hinzufügen. Sie sind im Prinzip kaum unterschiedlich zu dem, was wir schon betrachtet haben, und sie finden den Programmcode dieser Methoden in den angehängten Dateien zu diesem Artikel.
class CSimpleButton : public CElement { private: //--- Objekt für die Erzeugung eines Buttons CButton m_button; //--- Die Eigenschaften des Buttons: // (1) Text, (2) size string m_button_text; int m_button_x_size; int m_button_y_size; //--- Die Hintergrundfarbe color m_back_color; color m_back_color_off; color m_back_color_hover; color m_back_color_pressed; color m_back_color_array[]; //--- Die Farbe des Rahmens 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_button_zorder; //--- Der Modi für die zwei Zustände des Buttons bool m_two_state; //--- Verfügbar oder blockiert bool m_button_state; //--- public: //--- Methode für die Erzeugung eines einfachen Buttons bool CreateSimpleButton(const long chart_id,const int subwin,const string button_text,const int x,const int y); //--- private: bool CreateButton(void); //--- public: //--- (1) Festlegung des Modus, // (2) Genereller Zustand des Buttons(Verfügbar oder blockiert) void TwoState(const bool flag) { m_two_state=flag; } bool IsPressed(void) const { return(m_button.State()); } bool ButtonState(void) const { return(m_button_state); } void ButtonState(const bool state); //--- Die Größe des Buttons void ButtonXSize(const int x_size) { m_button_x_size=x_size; } void ButtonYSize(const int y_size) { m_button_y_size=y_size; } //--- (1) Gib den Text des Buttons zurück, (2) Festlegung der Textfarbe string Text(void) const { return(m_button.Description()); } 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; } //--- Festlegung der Hintergrundfarbe 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; } //--- Setting up the color of the button frame void BorderColor(const color clr) { m_border_color=clr; } void BorderColorOff(const color clr) { m_border_color_off=clr; } //--- }; //+-----------------------------------------------------------------+ //| Veränderung des Status des Buttons | //+-----------------------------------------------------------------+ void CSimpleButton::ButtonState(const bool state) { m_button_state=state; m_button.State(false); m_button.Color((state)? m_text_color : m_text_color_off); m_button.BackColor((state)? m_back_color : m_back_color_off); m_button.BorderColor((state)? m_border_color : m_border_color_off); }
Wir betrachten nun das Event Handling, in dem Moment wo der Button angeklickt wird. Wir fügen einen weiteren Identifizierer ON_CLICK_BUTTON der Defines.mqh Datei für die Erzeugung eines benutzerdefinierten Events hinzu. Dieser wird in allen Klassen verwendet, die sich mit der Erzeugung eines Buttons beschäftigen.
#define ON_CLICK_BUTTON (8) // Pressing the button
Jetzt erzeugen wir die CSimpleButton::OnClickButton() Methode um den Klick auf einen Button zu verarbeiten. Zu Beginn der Methode sind zwei Überprüfungen notwendig: Die Überprüfung des Namens des angeklickten Objektes und den Status des Objektes. Negative Ergebnisse führen zu dem Verlassen der Methode. Wenn diese Überprüfungen erfolgreich abgeschlossen worden sind, dann wird festgestellt, in welchem Modus sich der Button befindet. Wenn sich der Button selbst wieder zurück setzen soll, dann wird er in seinen anfänglichen Status gebracht und die Farben werden entsprechend gesetzt. Für den Modus mit 2 Zuständen, bekommt jeder Status zwei Gruppen von Farben. Am Ende der Methode wird mit dem ON_CLICK_BUTTON Identifizierer eine Nachricht gesendet, die das Element identifiziert, den Index des Elementes und den Namen enthält.
class CSimpleButton : public CElement { private: //--- Verarbeitendes Button click events bool OnClickButton(const string clicked_object); }; //+-----------------------------------------------------------------+ //| Event handling | //+-----------------------------------------------------------------+ void CSimpleButton::OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam) { //--- Verarbeitendes Events bei einem Klick mit der linken Maustaste if(id==CHARTEVENT_OBJECT_CLICK) { if(OnClickButton(sparam)) return; } } //+-----------------------------------------------------------------+ //| Verarbeiten eines Klicks auf einen Button | //+-----------------------------------------------------------------+ bool CSimpleButton::OnClickButton(const string clicked_object) { //--- Überprüfen des Namens des Objektes if(m_button.Name()!=clicked_object) return(false); //--- Falls der Button gesperrt ist if(!m_button_state) { m_button.State(false); return(false); } //--- Falls der Modus des Buttons nur einen Zustand hat if(!m_two_state) { m_button.State(false); m_button.Color(m_text_color); m_button.BackColor(m_back_color); } //--- Falls der Modus des Buttons zwei Zustände besitzt else { //--- Falls die Taste gedrückt ist if(m_button.State()) { //--- Veränderung der Farbe des Buttons m_button.State(true); m_button.Color(m_text_color_pressed); m_button.BackColor(m_back_color_pressed); CElement::InitColorArray(m_back_color_pressed,m_back_color_pressed,m_back_color_array); } //--- Falls der Button nicht geklickt ist else { //--- Veränderung der Farbe des Buttons m_button.State(false); m_button.Color(m_text_color); m_button.BackColor(m_back_color); CElement::InitColorArray(m_back_color,m_back_color_hover,m_back_color_array); } } //--- Senden eines Events ::EventChartCustom(m_chart_id,ON_CLICK_BUTTON,CElement::Id(),CElement::Index(),m_button.Description()); return(true); }
Wir können jetzt schon einen Test über die Einstellungen eines Buttons auf dem Chart durchführen und Nachrichten in dem Eventhandler der benutzerdefinierten Klasse dieser Applikation erhalten. Wir kopieren uns dazu den Test EA aus dem vorherigen Artikel. Lassen Sie nur das Hauptmenü mit den angefügten Kontextmenüs bestehen. Die Datei mit der CSimpleButton Klasse muss in der WndContainer.mqh Datei mit eingebunden werden.
//+------------------------------------------------------------------+ //| WndContainer.mqh | //| Copyright 2015, MetaQuotes Software Corp. | //| http://www.mql5.com | //+------------------------------------------------------------------+ #include "SimpleButton.mqh"
Anschließend können die Instanzen der CSimpleButton Klass Und die Methoden für die Erzeugung der Buttons in der CProgram Benutzerdefinierten Klasse dieser Anwendung deklariert werden. Lassen Sie uns als Beispiel drei Buttons erzeugen. Zwei von denen kehren nach dem Klick in ihren Normalzustand automatisch zurück, und der Dritte besitzt die Option fixiert zu werden. Aus diesem Grunde besitzt er zwei Zustände (Gedrückt/Nich gedrückt).
class CProgram : public CWndEvents { private: //--- Einfache buttons CSimpleButton m_simple_button1; CSimpleButton m_simple_button2; CSimpleButton m_simple_button3; //--- private: #define BUTTON1_GAP_X (7) #define BUTTON1_GAP_Y (50) bool CreateSimpleButton1(const string text); #define BUTTON2_GAP_X (128) #define BUTTON2_GAP_Y (50) bool CreateSimpleButton2(const string text); #define BUTTON3_GAP_X (7) #define BUTTON3_GAP_Y (75) bool CreateSimpleButton3(const string text); };
Wir betrachten hier nur den Code für einen Button. Bitte beachten Sie den hervorgehobenen Programmcode, wo der Modus für den Button mit den zwei Zuständen aktiviert wird.
//+-----------------------------------------------------------------+ //| Erzeugung des dritten einfachen buttons | //+-----------------------------------------------------------------+ bool CProgram::CreateSimpleButton3(string button_text) { //--- Übergabe des Panel Objektes m_simple_button3.WindowPointer(m_window); //--- Koordinaten int x=m_window.X()+BUTTON3_GAP_X; int y=m_window.Y()+BUTTON3_GAP_Y; //--- Festlegen der Eigenschaften bevor er erzeugt wird m_simple_button3.TwoState(true); m_simple_button3.ButtonXSize(237); m_simple_button3.TextColor(clrBlack); m_simple_button3.TextColorPressed(clrBlack); m_simple_button3.BackColor(clrLightGray); m_simple_button3.BackColorHover(C'193,218,255'); m_simple_button3.BackColorPressed(C'153,178,215'); //--- Die Erzeugung des Buttons if(!m_simple_button3.CreateSimpleButton(m_chart_id,m_subwin,button_text,x,y)) return(false); //--- Hinzufügen des Objektes zu dem gemeinsamen Array von Objektgruppen CWndContainer::AddToElementsArray(0,m_simple_button3); return(true); }
Alle Methoden für die Erzeugung des Elementes werden in der CProgram::CreateTradePanel() Methode aufgerufen:
//+-----------------------------------------------------------------+ //| Erzeugung des Trading-Panels | //+-----------------------------------------------------------------+ bool CProgram::CreateTradePanel(void) { //--- Erzeugung einer Form für Controls //---Erzeugung der Controls: // Hauptmenü //--- Kontextmenüs //--- Einfache buttons if(!CreateSimpleButton1("Simple Button 1")) return(false); if(!CreateSimpleButton2("Simple Button 2")) return(false); if(!CreateSimpleButton3("Simple Button 3")) return(false); //--- Neuzeichnen auf dem Chart m_chart.Redraw(); return(true); }
Die Nachricht mit dem ON_CLICK_BUTTON Event identifizierer wird in dem CProgram::OnEvent() Eventhandler empfangen. Als Beispiel implementieren wir den Programmcode, in welchem der Name des Buttons überprüft wird. Wenn sich herausstellt, dass der dritte Button angeklickt wurde, dann definiert sein aktueller Zustand den Zustand des zweiten Buttons. Das heißt, dass wenn der dritte Button gedrückt wurde, der Zweite blockiert wird und umgekehrt.
//+-----------------------------------------------------------------+ //| Event handler | //+-----------------------------------------------------------------+ void CProgram::OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam) { //--- Verarbeiten des Button-Events if(id==CHARTEVENT_CUSTOM+ON_CLICK_BUTTON) { ::Print("id: ",id,"; lparam: ",lparam,"; dparam: ",dparam,"; sparam: ",sparam); //--- if(sparam==m_simple_button3.Text()) { if(m_simple_button3.IsPressed()) m_simple_button1.ButtonState(false); else m_simple_button1.ButtonState(true); } } }
Der nachfolgende Screenshot zeigt das Ergebnis der Kompilierung und dem Hinzufügen des Programms zu dem Chart. In dem linken Screenshot ist der Simple Button 3 Button nicht gedrückt und der Simple Button 1 Button ist verfügbar. In dem rechten Screenshot ist in der Simple Button 3 gedrückt und der Simple Button 1 ist blockiert.
Abbildung 2. Test über das Hinzufügen des Buttons zu dem Chart. Buttons in unterschiedlichen Zuständen.
In der aktuellen Implementation fehlt noch eine kleine Änderung, welche die Interaktion mit dem Button realistischer erscheinen lässt. Wir müssen noch den Butten die Farbe wechseln lassen, direkt nachdem er angeklickt wurde. Eine Überprüfung ob die linke Maustaste gedrückt worden ist kann über den CHARTEVENT_MOUSE_MOVE Mauszeiger Bewegungs-Event durchgeführt werden. Dazu fügen wir den entsprechenden Code in den CSimpleButton::OnEvent() Eventhandler ein.
In den Fällen wenn (1) das Element versteckt ist, (2) die Form blockiert ist, (3) die linke Maustaste nicht gedrückt ist (4) und der Button blockiert ist, wird der Eventhandler verlassen. Wenn diese Überprüfungen erfolgreich abgeschlossen worden sind, dann wird die entsprechende Farbe des Buttons in Abhängigkeit von seinem Fokus und dem aktuellen Status geändert.
//+-----------------------------------------------------------------+ //| Event handling | //+-----------------------------------------------------------------+ void CSimpleButton::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; //--- Identifizierung des Focus int x=(int)lparam; int y=(int)dparam; CElement::MouseFocus(x>X() && x<X2() && y>Y() && y<Y2()); //--- Abbrechen, falls die Forum blockiert ist if(m_wnd.IsLocked()) return; //--- Abbrechen, falls die Maustaste nicht gedrückt ist if(sparam=="0") return; //--- Abbrechen, falls der Button blockiert ist if(!m_button_state) return; //--- Falls es keinen Fokus gibt if(!CElement::MouseFocus()) { //--- Falls der Button nicht geklickt ist if(!m_button.State()) { m_button.Color(m_text_color); m_button.BackColor(m_back_color); } //--- return; } //--- Falls er den Fokus besitzt else { m_button.Color(m_text_color_pressed); m_button.BackColor(m_back_color_pressed); return; } //--- return; } }
Nun sollte alles entsprechend funktionieren. Die Entwicklung der Klasse für die Erzeugung eines einfachen Buttons ist fertig. Sie können den zugehörigen Programmcode in den an diesen Artikel angehängten Dateien finden. Nun betrachten wir eine Klasse für einen Button mit erweiterter Funktionalität.
Entwicklung einer Klasse für die Erzeugung eines Icon-Buttons
Ein Icon-Button wird aus drei einfachen grafischen Objekten zusammengestellt:
- Hintergrund
- Icon.
- Text label.
Abbildung 3. Zusammengehörige Teile eines Icon-Buttons-Controls
Für die freie Positionierung des Textes wird ein Text-Label benötigt. Zum Beispiel könnte ein Button so zusammengestellt sein, dass sich einen Text im unteren Teil befindet und darüber ein Icon, oder auch umgekehrt. Wir werden dieses weiter unten in dem Artikel besprechen
Die Klasse für die Erzeugung dieses Controls wird die gleichen Felder und Methoden beinhalten, wie in der Klasse für den CSimpleButton. Neben den Eigenschaften, die sich mit der Größe und der Farbe des Buttons beschäftigen, benötigen wir noch Felder und Methoden für die Ränder des Icons und des Text-Labels in Relation zu den Control-Koordinaten, sowie die Label-Icons in dem aktiven und in dem blockierten Zustand. Wir sind zudem noch eine Option hinzu einen Button nur mit einem Icon zu erzeugen. Ein solcher Button beinhaltet nur ein Objekt vom Typ OBJ_BITMAP_LABEL.
Anschließend erzeugen wir die IconButton.mqh Datei und in ihr die CIconButton Klasse. Beziehen Sie diese in der WndContainer.mqh Datei mit ein. Der nachfolgende Programmcode zeigt nur die Felder und Methoden der CIconButton Klasse, die sie von der Klasse des CSimpleButton unterscheidet.
class CIconButton : public CElement { private: //--- Objekte für die Erzeugung eines Buttons CButton m_button; CBmpLabel m_icon; CLabel m_label; //--- Die Eigenschaften des Buttons: // Die Icons für den Button in dem aktiven und inaktiven Zustand string m_icon_file_on; string m_icon_file_off; //--- Icon Ränder int m_icon_x_gap; int m_icon_y_gap; //--- Text und Ränder des Text-Label string m_label_text; int m_label_x_gap; int m_label_y_gap; //--- Der Icon-Only-Mode, falls der Button nur aus einem BmpLabel Objekt besteht bool m_only_icon; //--- public: //--- Methoden für die Erzeugung eines Buttons bool CreateIconButton(const long chart_id,const int subwin,const string button_text,const int x,const int y); //--- private: bool CreateButton(void); bool CreateIcon(void); bool CreateLabel(void); //--- public: //--- Festlegung des Icon only Modus void OnlyIcon(const bool flag) { m_only_icon=flag; } //--- Festlegung der Icons des Buttons in dem aktiven und dem inaktiven 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; } //--- 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; } };
Alle Felder der Klasse müssen mit Standardwerten initialisiert werden:
//+-----------------------------------------------------------------+ //| Konstruktor | //+-----------------------------------------------------------------+ CIconButton::CIconButton(void) : m_icon_x_gap(4), m_icon_y_gap(3), m_label_x_gap(25), m_label_y_gap(4), m_icon_file_on(""), m_icon_file_off(""), m_button_state(true), m_two_state(false), m_only_icon(false), m_button_y_size(18), m_back_color(clrLightGray), m_back_color_off(clrLightGray), m_back_color_hover(clrSilver), m_back_color_pressed(clrBlack), m_border_color(clrWhite), m_border_color_off(clrDarkGray), m_label_color(clrBlack), m_label_color_off(clrDarkGray), m_label_color_hover(clrBlack), m_label_color_pressed(clrBlack) { //--- Abspeichern des namens der Elementklasse in der Basisklasse CElement::ClassName(CLASS_NAME); //--- Festlegung der Prioritäten für die linke Maustaste m_button_zorder =1; m_zorder =0; }
Die Methode für die Erzeugung aller Objekte des Buttons, enthält eine Überprüfung ob der Icon-Only-Modus existiert. Wenn sich herausstellt, dass der Button nur aus einem icon erzeugt werden soll, dann verlässt das Programm ganz am Anfang die Methode um die Erzeugung des Hintergrundes und das Text Label zu veranlassen.
//+-----------------------------------------------------------------+ //| Erzeugte den Hintergrund des Buttons | //+-----------------------------------------------------------------+ bool CIconButton::CreateButton(void) { //--- Verlassen, falls der Icon only Mode aktiviert ist if(m_only_icon) return(true); //--- ... etc. } //+-----------------------------------------------------------------+ //| Erzeugung des Textes für den Button | //+-----------------------------------------------------------------+ bool CIconButton::CreateLabel(void) { //--- Verlassen, falls der Icon only Mode aktiviert ist if(m_only_icon) return(true); //--- ... etc. }
Falls ein Button nur aus einem Icon erzeugt werden soll, dann wird die Methode einen weiteren Check für die Überprüfung des Vorliegens von Icons durchführen. Falls der User kein Icon definiert hat, dann wird die Erzeugung des grafischen Interfaces an dieser Stelle abgebrochen und in dem Journal wird eine Nachricht eingeblendet, dass die Icons in diesem Modus verpflichtend sind. The CIconButton::CreateIcon() method for creating a button icon is presented in the code below:
//+-----------------------------------------------------------------+ //| Erzeugt ein Button Icon | //+-----------------------------------------------------------------+ bool CIconButton::CreateIcon(void) { //--- Falls der Icon-Only-Modus deaktiviert ist if(!m_only_icon) { //--- Verlassen, falls das Icon für den Button nicht benötigt wird if(m_icon_file_on=="" || m_icon_file_off=="") return(true); } //--- Falls der Icon-Only-Modus aktiviert ist else { //--- Falls kein icon definiert worden ist, Ausgeben einer Nachricht und verlassen if(m_icon_file_on=="" || m_icon_file_off=="") { ::Print(__FUNCTION__," > The icon must be defined in the \"Icon only\" mode."); return(false); } } //--- Erstellen des Objektnamens string name=CElement::ProgramName()+"_icon_button_bmp_"+(string)CElement::Id(); //--- Koordinaten int x =(!m_only_icon)? m_x+m_icon_x_gap : m_x; int y =(!m_only_icon)? m_y+m_icon_y_gap : m_y; //--- Festlegen des Icons if(!m_icon.Create(m_chart_id,name,m_subwin,x,y)) return(false); //--- Festlegen der Eigenschaften m_icon.BmpFileOn("::"+m_icon_file_on); m_icon.BmpFileOff("::"+m_icon_file_off); m_icon.State(true); m_icon.Corner(m_corner); m_icon.GetInteger(OBJPROP_ANCHOR,m_anchor); m_icon.Selectable(false); m_icon.Z_Order((!m_only_icon)? m_zorder : m_button_zorder); m_icon.Tooltip((!m_only_icon)? "\n" : m_label_text); //--- Abspeichern der Koordinaten m_icon.X(x); m_icon.Y(y); //--- Abspeichern der Größe m_icon.XSize(m_icon.X_Size()); m_icon.YSize(m_icon.Y_Size()); //--- Ränder von den Kanten m_icon.XGap(x-m_wnd.X()); m_icon.YGap(y-m_wnd.Y()); //--- Abspeichern des Objekt-Pointers CElement::AddToArray(m_icon); return(true); }
Die CIconButton besitzt keine weiteren Unterschiede zu der CSimpleButton Klasse. Sie finden die vollständigen Versionen in den Dateien die diesen Artikel beigefügt sind.
Jetzt werden wir die Buttons vom Typ CIconButton testen. Alle Möglichkeiten demonstrieren zu können, erzeugen wir in dem Test EA fünf dieser Buttons mit unterschiedlichen Modi, Größen und Zuständen. Wir erzeugung fünf Instanzen der CIconButton Klasse in der benutzerdefinierten Klasse des Test EAs und deklarieren fünf Methoden für die Erzeugung von Buttons, wie es der nachfolgende Programmcode zeigt.
class CProgram : public CWndEvents { private: //--- Icon Buttons CIconButton m_icon_button1; CIconButton m_icon_button2; CIconButton m_icon_button3; CIconButton m_icon_button4; CIconButton m_icon_button5; //--- private: //--- Icon Buttons #define ICONBUTTON1_GAP_X (7) #define ICONBUTTON1_GAP_Y (105) bool CreateIconButton1(const string text); #define ICONBUTTON2_GAP_X (128) #define ICONBUTTON2_GAP_Y (105) bool CreateIconButton2(const string text); #define ICONBUTTON3_GAP_X (7) #define ICONBUTTON3_GAP_Y (130) bool CreateIconButton3(const string text); #define ICONBUTTON4_GAP_X (88) #define ICONBUTTON4_GAP_Y (130) bool CreateIconButton4(const string text); #define ICONBUTTON5_GAP_X (169) #define ICONBUTTON5_GAP_Y (130) bool CreateIconButton5(const string text); };
Wir verwenden hier nur die Implementation von einer dieser Methoden zeigen, da alle gleich aussehen. Sie haben lediglich unterschiedliche Werte von Parametern:
//+-----------------------------------------------------------------+ //| Erzeugung des icon button 5 | //+-----------------------------------------------------------------+ #resource "\\Images\\EasyAndFastGUI\\Icons\\bmp64\\gold.bmp" #resource "\\Images\\EasyAndFastGUI\\Icons\\bmp64\\gold_colorless.bmp" //--- bool CProgram::CreateIconButton5(const string button_text) { //--- Übergabe des Panel Objektes m_icon_button5.WindowPointer(m_window); //--- Koordinaten int x=m_window.X()+ICONBUTTON5_GAP_X; int y=m_window.Y()+ICONBUTTON5_GAP_Y; //--- Festlegen der Eigenschaften bevor er erzeugt wird m_icon_button5.ButtonXSize(76); m_icon_button5.ButtonYSize(87); m_icon_button5.LabelXGap(6); m_icon_button5.LabelYGap(69); m_icon_button5.LabelColor(clrBlack); m_icon_button5.LabelColorPressed(clrBlack); m_icon_button5.BackColor(clrGainsboro); m_icon_button5.BackColorHover(C'193,218,255'); m_icon_button5.BackColorPressed(C'210,210,220'); m_icon_button5.BorderColor(C'150,170,180'); m_icon_button5.BorderColorOff(C'178,195,207'); m_icon_button5.IconXGap(6); m_icon_button5.IconYGap(3); m_icon_button5.IconFileOn("Images\\EasyAndFastGUI\\Icons\\bmp64\\gold.bmp"); m_icon_button5.IconFileOff("Images\\EasyAndFastGUI\\Icons\\bmp64\\gold_colorless.bmp"); //--- Erzeugung des Controls if(!m_icon_button5.CreateIconButton(m_chart_id,m_subwin,button_text,x,y)) return(false); //--- Hinzufügen des Pointers von dem Control zu der Basisklasse CWndContainer::AddToElementsArray(0,m_icon_button5); return(true); }
Plazieren Sie die Aufrufe dieser Methoden in der Hauptmethode für die Erzeugung von grafischen Interfaces.
Dem Eventhandler die Nachverfolgung des Klicks auf den Icon Button 2 hinzufügen. In der EA Version, welche diesem Artikel beigefügt ist, arbeitet der Button in zwei Modi (gedrückt / nicht gedrückt). Die Verfügbarkeit von Icon Button 1 und Icon Button 4 hängt von dem Status des Icon Button 2 ab.. Falls dieser Button nicht gedrückt ist, dann sind alle von ihm abhängige Buttons blockiert und umgekehrt.
//+-----------------------------------------------------------------+ //| Event handler | //+-----------------------------------------------------------------+ void CProgram::OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam) { //--- Verarbeiten des Button-Events if(id==CHARTEVENT_CUSTOM+ON_CLICK_BUTTON) { Print("id: ",id,"; lparam: ",lparam,"; dparam: ",dparam,"; sparam: ",sparam); //--- if(sparam==m_simple_button3.Text()) { if(m_simple_button3.IsPressed()) m_simple_button1.ButtonState(false); else m_simple_button1.ButtonState(true); } //--- if(sparam==m_icon_button2.Text()) { if(m_icon_button2.IsPressed()) { m_icon_button1.ButtonState(true); m_icon_button4.ButtonState(true); } else { m_icon_button1.ButtonState(false); m_icon_button4.ButtonState(false); } } } }
Nachdem sie den Programmcode kompiliert haben und den Test EA dem Chart hinzugefügt haben, sollten Sie das folgende Ergebnis sehen:
Abbildung 4. Test des Icon Button Controls.
Wir haben die Entwicklung der CIconButton Klasse für die Erzeugung eines Buttons mit erweiterter Funktionalität fertiggestellt. Die Icons, die in den Buttons dargestellt werden, können sie am Ende dieses Artikels herunterladen. Nun werden wir die Klasse für die Erzeugung eines Split Buttons besprechen.
Entwicklung einer Klasse für die Erzeugung eines Split-Buttons
Was ist ein Split Button? Ein splitbutton besteht aus zwei Komponenten:
- Die erste Komponente besteht auf einen Button mit einer eingebauten Hauptfunktion.
- Die zweite Komponente besteht aus einem Button, welcher ein Kontextmenü mit zusätzlichen Funktionen hervorbringt.
Ein solches Control wird sehr häufig in grafischen Interfaces verwendet. Solche Buttons werden verwendet, wenn mehrere Funktionen dicht gruppiert werden müssen, und alle von Ihnen zur gleichen Kategorie gehören.
Ein Split Button wird aus 5 Objekten zusammengestellt und einem hinzufügbaren Element (Context-Menü):
- Hintergrund
- Icon.
- Text.
- Hintergrund des zusätzlichen Buttons.
- Drop-down Menü Indikator.
Abbildung 5. Bestandteile des Split Button Controls.
Wir sehen hier, dass das Kontextmenü (Ein Objekt der CContextMenu Klasse) Dem Objekt der CMenuItem Klasse nicht hinzugefügt werden kann. Das bedeutet, dass wir einen zusätzlichen Modus benötigen, wenn wir ein Kontextmenü zu irgendeinem anderen Objekt hinzufügen oder wieder davon entfernen wollen.
Zudem benötigen wir einen Bezugspunkt für die Einstellungen über die Interaktion zwischen diesen controls, welche durch den User temporär aktiviert werden können (Drop-Down-Elemente) Es muss so eingerichtet werden, dass eine Form nur von dem Element freigegeben werden kann welche sie auch gesperrt hat. Wenn wir dieses nicht machen, dann können Konflikte zwischen den einzelnen Elementen entstehen, da der Status von der Form auch den Status von einigen Elementen definiern kann. Wir werden dieses später an einem Beispiel veranschaulichen. Bei einem Test kann man besser darstellen, wie solche Konflikte entstehen können. Um diese Funktionalität implementieren zu können, müssen wir noch ein Feld und eine Methode für das Speichern und erhalten von aktivierten Elementen der CWindow Klasse hinzufügen:
class CWindow : public CElement { private: //--- Identifizierer des aktivierten zum Controls int m_id_activated_element; //--- public: //--- Methoden für das Speichern und das Erhalten der ID des aktivierten Elementes int IdActivatedElement(void) const { return(m_id_activated_element); } void IdActivatedElement(const int id) { m_id_activated_element=id; } };
Wenn während der Aktivierung des Elementes die Form geblockt werden soll, dann muss der Identifizierer dieses Elementes in der Form abgespeichert werden An der Stelle wo die Form wieder freigegeben werden soll, muss eine Überprüfung stattfinden, wie der Identifizierer des Elementes lautet, welcher die Form gesperrt hat. Eine Überprüfung der Identifizierung des Elementes, welches die Form gesperrt hat, ist nicht notwendig, da eine Überprüfung des Namens des Objektes stattgefunden hat.
Anschließend erweitern wir die CContextMenu Klasse. Sie wird die Aktivierung und Handhabung des abgelösten Kontextmenü-Modus ermöglichen, wie es im Folgenden Programmcode gezeigt wird: Der Modus des Kontextmenüs mit der Bindung zu dem vorherigen Knoten wird standardmäßig gesetzt:
class CContextMenu : public CElement { //--- Der freistehende Kontextmenü-Modus.. Das bedeutet, dass es keine Bindung zu einem vorherigen Knotenpunkt gibt. bool m_free_context_menu; //--- public: //--- Festlegen des freistehenden Kontextmenü-Modus void FreeContextMenu(const bool flag) { m_free_context_menu=flag; } }; //+-----------------------------------------------------------------+ //| Konstruktor | //+-----------------------------------------------------------------+ CContextMenu::CContextMenu(void) : m_free_context_menu(false) { }
Die internen Identifizierer für die Verwaltung eines Klicks auf den Menüpunkt unterscheiden sich je nachdem ob es sich um ein verbundenes oder ein freistehendes Kontextmenü handelt. Eine solche Aufteilung macht den Programmcode eindeutiger, minimiert die Anzahl der Bedingungen und erlaubt eine flexiblere Verwaltung der Events der Elemente.
Hinzufügen eines neuen Identifizierers (ON_CLICK_FREEMENU_ITEM) für die Erzeugung eines Events von einem freistehenden Kontextmenü zu der Defines.mqh Datei:
#define ON_CLICK_FREEMENU_ITEM (9) // Clicking on the item of a detached context menu
Für die Überprüfung des Modus des freistehenden Kontextmenüs, müssen noch zusätzliche Bedingungen in der CContextMenu Klasse hinzugefügt werden. Nachfolgend finden Sie die abgekürzten Versionen der Methoden. Die Kommentare dienen der Orientierung.
1. In dem Eventhandler:
//+-----------------------------------------------------------------+ //| Event handler | //+-----------------------------------------------------------------+ void CContextMenu::OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam) { //--- Verwaltung der Bewegung des Mauszeigers if(id==CHARTEVENT_MOUSE_MOVE) { //--- Abbrechen, falls das Element versteckt ist //--- Erhalten des Focus //--- Verlassen, falls es sich um ein freistehendes Kontextmenü handelt if(m_free_context_menu) return; //--- Falls das Kontextmenü aktiviert ist und die linke Maustaste betätigt wurde //--- Überprüfen der Bedingungen für das Schließen aller Kontextmenüs, welche unterhalb dieses Elements geöffnet sind. return; } //--- Verarbeitendes Events bei einem Klick mit der linken Maustaste //--- Verarbeiten des ON_CLICK_MENU_ITEM Events if(id==CHARTEVENT_CUSTOM+ON_CLICK_MENU_ITEM) { //--- Verlassen, falls es sich um ein freistehendes Kontextmenü handelt if(m_free_context_menu) return; //--- Empfangen der Nachricht von dem Menü Item für die Verarbeitung return; } }
2. In der Methode für die Erzeugung eines Kontextmenüs:
//+-----------------------------------------------------------------+ //| Erzeugt ein Kontextmenü | //+-----------------------------------------------------------------+ bool CContextMenu::CreateContextMenu(const long chart_id,const int subwin,const int x=0,const int y=0) { //--- Verlassen, wenn es keinen Pointer zu einer Form gibt //--- Falls es sich um ein angefügtes Kontextmenü handelt if(!m_free_context_menu) { //--- Verlassen, falls es keinen Pointer zu einem vorherigen Knotenpunkt gibt if(::CheckPointer(m_prev_node)==POINTER_INVALID) { ::Print(__FUNCTION__," > Before creating a context menu it must be passed " "the pointer to the previous node using the CContextMenu::PrevNodePointer(CMenuItem &object) method."); return(false); } } //--- Initialisierung der Variablen //--- Wenn die Koordinaten nicht spezifiziert wurden //--- Falls die Koordinaten spezifiziert wurden //--- Ränder von den Kanten //--- Erzeugen eines Kontextmenüs //--- Verstecken des Elementes return(true); }
3. In der Methode für die Erzeugung einer Liste von Menu Items:
//+-----------------------------------------------------------------+ //| Erzeugen einer Liste von Menü-Items | //+-----------------------------------------------------------------+ bool CContextMenu::CreateItems(void) { int s =0; // For identifying the location of separation lines int x =m_x+1; // X coordinate int y =m_y+1; // Y coordinate. Will be calculated in a loop for every menu item. //--- Anzahl von Abstandslinien //--- int items_total=ItemsTotal(); for(int i=0; i<items_total; i++) { //--- Berechnung der Y Koordinate //--- Abspeichern des Pointers der Form //--- Falls das Kontextmenü einen Anhang besitzt, wird der Pointer zu dem vorherigen Knotenpunkt hinzugefügt if(!m_free_context_menu) m_items[i].PrevNodePointer(m_prev_node); //--- Festlegen der Eigenschaften //--- Ränder von den Kanten des Panels //--- Erzeugung eines Menü-Items //--- Mit dem nächsten fortfahren, wenn alle Trennlinien gesetzt wurden //--- Wenn die Indizes übereinstimmen, dann wird eine Trennlinie nach dem Menüpunkt gesetz } return(true); }
4. In der Methode für das Anzeigen und Verstecken eines Kontextmenüs:
//+-----------------------------------------------------------------+ //| Anzeigen eines Kontextmenüs | //+-----------------------------------------------------------------+ void CContextMenu::Show(void) { //--- Verlassen, falls das Control schon sichtbar ist //--- Anzeigen der Objekte des Kontextmenüs //--- Anzeigen der Menüpunkt //--- Den Status eines sichtbaren Controls zuweisen //--- Status des Kontextmenüs //--- Registrieren des Status in dem vorherigen Knoten. if(!m_free_context_menu) m_prev_node.ContextMenuState(true); //--- Blockieren der Form } //+-----------------------------------------------------------------+ //| Versteckt ein Kontext-Menü | //+-----------------------------------------------------------------+ void CContextMenu::Hide(void) { //--- Abbrechen, falls das Element versteckt ist //--- Verstecken der Objekte des Kontextmenüs //--- Verstecken der Menüpunkte //--- Den Fokus löschen //--- Den Status eines versteckten Elementes zuweisen //--- Status des Kontextmenüs //--- Registrieren des Status in dem vorherigen Knoten. if(!m_free_context_menu) m_prev_node.ContextMenuState(false); }
5. In der Methode für die Verwaltung eines Klicks auf einen Menüpunkt. In dem nächsten Block wird innerhalb einer Schleife eine Überprüfung des Namens des angeklickten Objektes durchgeführt. Wenn ein solches Objekt gefunden wird, dann wird ein Event mit dem ON_CLICK_FREEMENU_ITEM Bezeichner gesendet. Später muss dieses Event von allen Controls verfolgt werden, welche ein Kontextmenü beinhalten (Dieses wird später in einem Beispiel dargestellt).
//+-----------------------------------------------------------------+ //| Verarbeiten eines Klicks auf einen Menüpunkt | //+-----------------------------------------------------------------+ bool CContextMenu::OnClickMenuItem(const string clicked_object) { //--- Abbrechen, Falls dieses Kontextmenü einen vorherigen Knoten besitzt und schon geöffnet ist. if(!m_free_context_menu && m_context_menu_state) return(true); //--- Abbrechen, falls der Klick nicht auf diesem Menüpunkt stattgefunden hat if(::StringFind(clicked_object,CElement::ProgramName()+"_menuitem_",0)<0) return(false); //--- Abfragen des Bezeichners und des Indexes über den Objektnamen int id =IdFromObjectName(clicked_object); int index =IndexFromObjectName(clicked_object); //--- Falls das Kontext-Menü vorherige Knoten besitzt if(!m_free_context_menu) { //--- Abbrechen, falls der Klick nicht auf ein Item stattgefunden hat, das zu diesem Kontextmenü gehört if(id!=m_prev_node.Id() || index!=m_prev_node.Index()) return(false); //--- Anzeigen des Kontextmenüs Show(); } //--- Falls es sich um ein freistehendes Kontext-Menü handelt else { //--- In einer Schleife herausfinden welcher Menüpunkt gedrückt wurde int total=ItemsTotal(); for(int i=0; i<total; i++) { if(m_items[i].Object(0).Name()!=clicked_object) continue; //--- Eine Nachricht darüber senden ::EventChartCustom(m_chart_id,ON_CLICK_FREEMENU_ITEM,CElement::Id(),i,DescriptionByIndex(i)); break; } } //--- return(true); }
Nun ist alles bereit um eine Klasse mit einem Split Button zu entwickeln Erzeugen sie eine SplitButton.mqh Datei mit der CSplitButton Klasse und Standardmethoden für alle Controls in dem Controls Verzeichnis:
//+------------------------------------------------------------------+ //| SplitButton.mqh | //| Copyright 2015, MetaQuotes Software Corp. | //| http://www.mql5.com | //+------------------------------------------------------------------+ #include "Element.mqh" #include "Window.mqh" #include "ContextMenu.mqh" //+------------------------------------------------------------------+ //| Klasse für die Erzeugung eines Split buttons | //+------------------------------------------------------------------+ class CSplitButton : public CElement { private: //--- Ein Pointer zu der Form zu welchem das Element hinzugefügt worden ist CWindow *m_wnd; //--- public: CSplitButton(); ~CSplitButton(); //--- Speichert den Pointer void WindowPointer(CWindow &object) { m_wnd=::GetPointer(object); } //--- 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 linken Maustaste virtual void SetZorders(void); virtual void ResetZorders(void); };
Zusätzlich zu den Eigenschaften und Methoden der Charakteristiken für Buttons aller Typen, die wir bereits in diesem Artikel beschrieben haben, brauchen wir jetzt zusätzlich noch welche für das erstellen von Buttons mit einem Dropdown Menüs:
- Größe. In dieser Version verwenden wir nur die Breite. Die Höhe wird der Höhe des Haupt-Buttons entsprechen
- Die Priorität für einen Klick mit der linken Maustaste Ein Button mit einem Dropdown-Menü benötigt einen höheren Prioritätlevel, als ein einfacher Button.
- Icons und Ränder für das Icon in dem Button.
- Status des Kontextmenüs in des Buttons (sichtbar/versteckt).
class CSplitButton : public CElement { private: //--- Größe und Priorität eines Klicks mit der linken Maustaste für einen Button mit Dropdown int m_drop_button_x_size; int m_drop_button_zorder; //--- Icon Ränder int m_drop_arrow_x_gap; int m_drop_arrow_y_gap; //--- Icons eines Buttons mit einem Drop-Down Menü im aktivierten und blockierten Zustand string m_drop_arrow_file_on; string m_drop_arrow_file_off; //--- Status des Kontextmenüs bool m_drop_menu_state; //--- public: //--- Die Größe eines Buttons mit einem Dropdown Menü void DropButtonXSize(const int x_size) { m_drop_button_x_size=x_size; } //--- Festlegung der Icons für einen Button mit einem Dropdown Menü im aktivierten und gesperrten Status void DropArrowFileOn(const string file_path) { m_drop_arrow_file_on=file_path; } void DropArrowFileOff(const string file_path) { m_drop_arrow_file_off=file_path; } //--- Icon Ränder void DropArrowXGap(const int x_gap) { m_drop_arrow_x_gap=x_gap; } void DropArrowYGap(const int y_gap) { m_drop_arrow_y_gap=y_gap; } };
Wie schon zuvor erklärt, benötigt die Erzeugung eines Split-Buttons fünf einfache Objekte und ein Kontextmenü. Lassen Sie die Instanzen der benötigen Klassen und Methoden für die Erzeugung deklarieren. Wir benötigen zu den Methoden für die Gestaltung des Kontextmenüs (hinzufügen von Menüpunkten und Trennlinien). Da die Eigenschaften des Kontextmenüs durch den Anwender festgelegt werden, wird eine Methode für das Erhalten eines Pointers zu dem Button mit dem Kontextmenü notwendig.
class CSplitButton : public CElement { private: //--- Objekte für die Erzeugung eines Buttons CButton m_button; CBmpLabel m_icon; CLabel m_label; CEdit m_drop_button; CBmpLabel m_drop_arrow; CContextMenu m_drop_menu; //--- public: //--- Methoden für die Erzeugung eines Buttons bool CreateSplitButton(const long chart_id,const string button_text,const int window,const int x,const int y); //--- private: bool CreateButton(void); bool CreateIcon(void); bool CreateLabel(void); bool CreateDropButton(void); bool CreateDropIcon(void); bool CreateDropMenu(void); //--- public: //--- Erhalt des Pointers von dem Kontextmenü, CContextMenu *GetContextMenuPointer(void) const { return(::GetPointer(m_drop_menu)); } //--- Fügt einen Menüpunkt mit den angegebenen Eigenschaften vor der Erzeugung des Kontextmenüs hinzu void AddItem(const string text,const string path_bmp_on,const string path_bmp_off); //--- Fügt eine Trennlinie nach dem spezifizierten Menüpunkt vor der Erzeugung des Kontextmenüs hinzu. void AddSeparateLine(const int item_index); };
Die Methoden für das Erzeugen der Objekte haben keine grundlegenden Unterschiede zu denen die wir bereits besprochen haben. Der einzige Unterschied besteht darin, dass in der Methode für die Erzeugung des Kontextmenüs der Bezeichner des Split Buttons als Teil des Controls mit angegeben werden muss. Dieses ermöglicht es uns später zu ermitteln, von welchen Bezeichner der ON_CLICK_FREEMENU_ITEM ausgelöst wurde.
//+-----------------------------------------------------------------+ //| Erzeugt ein drop-down Menü | //+-----------------------------------------------------------------+ bool CSplitButton::CreateDropMenu(void) { //--- Übergabe des Panel Objektes m_drop_menu.WindowPointer(m_wnd); //--- Ein ungebundenes Kontextmenü m_drop_menu.FreeContextMenu(true); //--- Koordinaten int x=m_x; int y=m_y+m_y_size; //--- Festlegen der Eigenschaften m_drop_menu.Id(CElement::Id()); m_drop_menu.XSize((m_drop_menu.XSize()>0)? m_drop_menu.XSize() : m_button_x_size); //--- Einrichten eines Kontextmenüs if(!m_drop_menu.CreateContextMenu(m_chart_id,m_subwin,x,y)) return(false); //--- return(true); }
Wir betrachten nun die Methoden für die Interaktion mit einem Split button. Da ein Button dieses Typs aus zwei Teilen besteht, benötigen wir zwei separate Methoden für die Handhabung der Klicks auf diese Objekte. These methods will be called in the class handler in the block of the CHARTEVENT_OBJECT_CLICK event:
class CSplitButton : public CElement { private: //--- Verarbeitendes Button click events bool OnClickButton(const string clicked_object); //--- Verarbeiten eines Klicks auf einen Button mit einem Dropdown Menü bool OnClickDropButton(const string clicked_object); }; //+-----------------------------------------------------------------+ //| Event handler | //+-----------------------------------------------------------------+ void CSplitButton::OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam) { //--- Verarbeitendes Events bei einem Klick mit der linken Maustaste if(id==CHARTEVENT_OBJECT_CLICK) { //--- Klick auf einen einfachen Button if(OnClickButton(sparam)) return; //--- Klick auf einen Button mit einem Dropdown Menü if(OnClickDropButton(sparam)) return; } }
In der CSplitButton::OnClickButton() Methode für die Verwaltung eines Klicks auf einen Haupt Button, wird zunächst der Name des Objektes geprüft. Wenn es sich um den Namen der Klasse dieser Instanz handelt, dann wird der Status des Buttons überprüft. Wenn der Button gesperrt ist, dann bricht das Programm diese Methode ab. Der Hauptmann kann nur einen Status besitzen. Das bedeutet, dass er zunächst zu einem nicht gedrückten Status zurückkehren muss, nachdem er gedrückt wurde. Wenn diese Überprüfungen erfolgreich waren, dann (1) muss das Kontextmenü versteckt werden, falls es sichtbar ist, (2) der entsprechende Status und die Farbe für das Menü und den Button müssen gesetzt werden, (3) Die Form muss entsperrt werden und der Bezeichner des aktivierenden Elementes muss im Speicher zurückgesetzt werden.
Am Ende der Methode wird eine Nachricht gesendet. Sie kann in der benutzerdefinierten Klasse empfangen werden. Diese Nachricht beinhaltet (1) die ON_CLICK_BUTTON Event Bezeichner, (2) den Element-Bezeichner, (3) den Index des Elementes (4) die dargestellte Beschreibung des Buttons.
//+-----------------------------------------------------------------+ //| Drücken des Buttons | //+-----------------------------------------------------------------+ bool CSplitButton::OnClickButton(const string clicked_object) { //--- Abbrechen, falls der Name des Objektes nicht zutrifft if(clicked_object!=m_button.Name()) return(false); //--- Abbrechen, falls der Button blockiert ist if(!m_button_state) { //--- Button zurücksetzen m_button.State(false); return(false); } //--- Verstecken des Menüs m_drop_menu.Hide(); m_drop_menu_state=false; //--- Zurücksetzen des Buttons und Farbe des Focus setzen m_button.State(false); m_button.BackColor(m_back_color_hover); m_drop_button.BackColor(m_back_color_hover); //--- Die Form entsperren m_wnd.IsLocked(false); m_wnd.IdActivatedElement(WRONG_VALUE); //--- Eine Nachricht darüber senden ::EventChartCustom(m_chart_id,ON_CLICK_BUTTON,CElement::Id(),CElement::Index(),m_label.Description()); return(true); }
Die CSplitButton::OnClickDropButton() Methode, welche die den Klick auf einen Button mit einem Dropdown Menü verarbeitet, führt am Anfang noch zwei Überprüfungen durch. Diese sind Überprüfungen über (1) den Namen und (2) Die Verfügbarkeit des Buttons. Anschließend wechselt das Programm zu einen der zwei Blöcke, in Abhängigkeit davon, ob der aktuelle Status sichtbar ist oder nicht.
//+-----------------------------------------------------------------+ //| Drücken eines Buttons mit einem Dropdown Menü | //+-----------------------------------------------------------------+ bool CSplitButton::OnClickDropButton(const string clicked_object) { //--- Abbrechen, falls der Name des Objektes nicht zutrifft if(clicked_object!=m_drop_button.Name()) return(false); //--- Abbrechen, falls der Button blockiert ist if(!m_button_state) { //--- Button zurücksetzen m_button.State(false); return(false); } //--- Falls die Liste angezeigt wird, verstecke sie if(m_drop_menu_state) { m_drop_menu_state=false; m_drop_menu.Hide(); m_button.BackColor(m_back_color_hover); m_drop_button.BackColor(m_back_color_hover); //--- Entsperre die Form und setze die aktivierende Element ID zurück m_wnd.IsLocked(false); m_wnd.IdActivatedElement(WRONG_VALUE); } //--- Falls die Liste versteckt ist, dann anzeigen else { m_drop_menu_state=true; m_drop_menu.Show(); m_button.BackColor(m_back_color_hover); m_drop_button.BackColor(m_back_color_pressed); //--- Sperre die Form und speichere die aktivierende Element ID m_wnd.IsLocked(true); m_wnd.IdActivatedElement(CElement::Id()); } //--- return(true); }
Wir haben bereits die CContextMenu Klasse mit einer Funktion versehen, welche in dem "Free Mode" das Senden von Events mit dem ON_CLICK_FREEMENU_ITEM Bezeichner für die interne Verarbeitung erleichtert. Diese Nachricht wird in dem Eventhandler der CSplitButton Klasse empfangen. Um zu überprüfen, ob diese Nachricht von einem relativen Kontext-Menü stammt, muss der Bezeichner überprüft werden. Dieser ist in dem lparam Parameter enthalten. Wenn der Bezeichner übereinstimmt, (1) Muss das Menü versteckt werden, (2) Die Farben müssen entsprechend des Status gesetzt werden (3) Die Form muss entsperrt werden, falls dieses Element das aktivierende Element war. Anschließend muss eine Nachricht mit dem ON_CLICK_CONTEXTMENU_ITEM Bezeichner gesendet werden.. Diese Nachricht kann in der benutzerdefinierten Klasse empfangen werden
Dafür erzeugen wir eine zusätzliche Methode CSplitButton::HideDropDownMenu(). Der Sinn der Methode ist das Menü zu verstecken, die Form zu entsperren und das Zurücksetzen des Bezeichners des aktivierenden Elementes.
class CSplitButton : public CElement { private: //--- Versteckt das Dropdown-Menü void HideDropDownMenu(void); }; //+-----------------------------------------------------------------+ //| Event handler | //+-----------------------------------------------------------------+ void CSplitButton::OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam) { //--- Verarbeitendes Events des Anklickens eines freien Menüpunktes if(id==CHARTEVENT_CUSTOM+ON_CLICK_FREEMENU_ITEM) { //--- Abbrechen, falls der Bezeichner nicht zutrifft if(CElement::Id()!=lparam) return; //--- Verstecken das Dropdown-Menüs HideDropDownMenu(); //--- Senden einer Nachricht ::EventChartCustom(m_chart_id,ON_CLICK_CONTEXTMENU_ITEM,lparam,dparam,sparam); return; } } //+-----------------------------------------------------------------+ //| Versteckt einen Drop down menu | //+-----------------------------------------------------------------+ void CSplitButton::HideDropDownMenu(void) { //--- Verstecken des Menüs und entsprechende Hinweise m_drop_menu.Hide(); m_drop_menu_state=false; m_button.BackColor(m_back_color); m_drop_button.BackColor(m_back_color); //--- Entsperren der Form, falls der Bezeichner der Form und des Elementes übereinstimmen if(m_wnd.IdActivatedElement()==CElement::Id()) { m_wnd.IsLocked(false); m_wnd.IdActivatedElement(WRONG_VALUE); } }
Jetzt müssen wir die Reaktion auf den Split Button in Zusammenhang mit dem Mauszeiger und dem Status der linken Maustaste, wenn sich der Mauszeiger über dem Button befindet programmieren. Dieses benötigt eine weitere Methode, die wir CSplitButton::CheckPressedOverButton() nennen. Diese Methode besitzt lediglich einen Parameter - Den Status der linken Maustaste. Zunächst werden zwei Überprüfungen durchgeführt. Wenn sich herausstellt, dass (1) Sich der Mauszeiger außerhalb des Bereichs des Buttons befindet und (2) Das Formular gesperrt ist, während dieses nicht das Aktivierende Element ist, dann wird hier die Methode verlassen. Wenn die Überprüfungen erfolgreich abgeschlossen worden sind, dann legt das Programm die Farben entsprechend dem Status der linken Maustaste und über welchem Teil des Buttons sich der Mauszeiger befindet, fest.
class CSplitButton : public CElement { private: //--- Überprüfung ob bei gedrückter linker Maustaste sich der Mauszeiger über einem Split Button befindet void CheckPressedOverButton(const bool mouse_state); }; //+-----------------------------------------------------------------+ //|Prüfen auf gedrückte linke Maustaste über einem Split button | //+-----------------------------------------------------------------+ void CSplitButton::CheckPressedOverButton(const bool mouse_state) { //--- Leave, if it is outside of the element area if(!CElement::MouseFocus()) return; //--- Abbrechen, wenn eine Form gesperrt ist und die Identifizierer der Form und des Elementes nicht übereinstimme if(m_wnd.IsLocked() && m_wnd.IdActivatedElement()!=CElement::Id()) return; //--- Gedrückte Maustaste if(mouse_state) { //--- Im Bereich des Menübuttons if(m_drop_button.MouseFocus()) { m_button.BackColor(m_back_color_hover); m_drop_button.BackColor(m_back_color_pressed); } else { m_button.BackColor(m_back_color_pressed); m_drop_button.BackColor(m_back_color_pressed); } } //--- Nicht gedrückte Maustaste else { if(m_drop_menu_state) { m_button.BackColor(m_back_color_hover); m_drop_button.BackColor(m_back_color_pressed); } } }
Die CSplitButton::CheckPressedOverButton() Methode wird in dem Eventhandler über das Event für die Bewegung des Mauszeigers aufgerufen. Dem Aufruf dieser Methode gehen mehrere Überprüfungen voraus, wie (1) Ob das Element versteckt ist, (2) Der Focus, (3) Ob das Element verfügbar ist und (4) Sich der Mauszeiger außerhalb des Bereichs des Elementes befindet, Was alles dazu führen kann kann man dass das nene versteckt wird und der eventhändler verlassen wird bevor die Methode CSplitButton::CheckPressedOverButton() aufgerufen wird.
//+-----------------------------------------------------------------+ //| Event handler | //+-----------------------------------------------------------------+ void CSplitButton::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; //--- Identifizierung des Focus int x=(int)lparam; int y=(int)dparam; CElement::MouseFocus(x>X() && x<X2() && y>Y() && y<Y2()); m_drop_button.MouseFocus(x>m_drop_button.X() && x<m_drop_button.X2() && y>m_drop_button.Y() && y<m_drop_button.Y2()); //--- Abbrechen, falls der Button blockiert ist if(!m_button_state) return; //--- Der Mauszeiger befindet sich außerhalb des Bereichs des Elementes und die Maus Taste ist gedrückt if(!CElement::MouseFocus() && sparam=="1") { //--- Verlassen, falls das Kontextmenü den Fokus besitzt if(m_drop_menu.MouseFocus()) return; //--- Verstecken das Dropdown-Menüs HideDropDownMenu(); return; } //--- Überprüfung ob bei gedrückter linker Maustaste sich der Mauszeiger über einem Split Button befindet CheckPressedOverButton(bool((int)sparam)); return; } }
Nun ist die Klasse für den speedbutton bereit für einen Test. Dafür muss sie in der Bibliotheksstruktur korrekt eingefügt werden. Dieses muss immer wieder gemacht werden, wenn ein komplexes Control erzeugt wird. In den Fällen der CSimpleButton und CIconButton waren keine Erweiterungen notwendig. In dem Falle des Split-Buttons ist dieses anders, da es neben dem aktuellen Button auch noch ein Kontext-Menü gibt. Der End-Anwender dieser Bibliothek wird nur die letzte Version verwenden und sich nicht mit dem darin enthaltenen Programmcode beschäftigen. Er hat keine Ahnung darüber, wie dieser Programmcode funktioniert. Das Hauptziel eines Entwicklers einer Bibliothek ist, die Verwendung dieser Bibliothek so einfach wie möglich zu gestalten, damit ein Anwender in nur wenigen Schritten ein grafisches Interface erstellen kann.
Beziehen Sie die Datei mit der CSplitButton Klasse in der WndContainer.mqh Datei mit ein:
//+------------------------------------------------------------------+ //| WndContainer.mqh | //| Copyright 2015, MetaQuotes Software Corp. | //| http://www.mql5.com | //+------------------------------------------------------------------+ #include "SplitButton.mqh"
Anschließend deklarieren und implementieren Sie die Methoden für das Hinzufügen eines Pointers zu dem Kontextmenü mit einem Split-Button zu dem privaten Array, welches zuvor schon erzeugt wurde:
//+------------------------------------------------------------------+ //| Klasse für das Abspeichern aller Interface Objekte | //+------------------------------------------------------------------+ class CWndContainer { private: //--- Speichert die Pointer zu den Split-Button-Elementen in der Basis ab bool AddSplitButtonElements(const int window_index,CElement &object); }; //+---------------------------------------------------------------------------+ //| Speichert die Pointer zu den Elementen eines Split-Buttons in der Basis ab| //+---------------------------------------------------------------------------+ bool CWndContainer::AddSplitButtonElements(const int window_index,CElement &object) { //--- Abbrechen, falls es sich nicht um einen Split-Button handelt if(object.ClassName()!="CSplitButton") return(false); //--- Erhalten des Splitbutton pointers CSplitButton *sb=::GetPointer(object); //--- Vergrößern des Elementen Arrays int size=::ArraySize(m_wnd[window_index].m_elements); ::ArrayResize(m_wnd[window_index].m_elements,size+1); //--- Erhalt des Kontextmenü pointers CContextMenu *cm=sb.GetContextMenuPointer(); //--- Abspeichern der Elemente und Objekte in der Basis m_wnd[window_index].m_elements[size]=cm; AddToObjectsArray(window_index,cm); //--- Abspeichern der Pointer zu den Objekten in der Basis int items_total=cm.ItemsTotal(); for(int i=0; i<items_total; i++) { //--- Vergrößern des Elementen Arrays size=::ArraySize(m_wnd[window_index].m_elements); ::ArrayResize(m_wnd[window_index].m_elements,size+1); //--- Erhalt des menu-item pointers CMenuItem *mi=cm.ItemPointerByIndex(i); //--- Abspeichern des pointers in dem Array m_wnd[window_index].m_elements[size]=mi; //--- Hinzufügen des pointers zu allen Menüpunkt-Objekten zu dem gemeinsamen Array AddToObjectsArray(window_index,mi); } //--- Hinzufügen des Pointers zu dem privaten Array AddToRefArray(cm,m_wnd[window_index].m_context_menus); return(true); }
Wie Sie sich erinnern, muss der Aufruf der Methoden wie CWndContainer::AddSplitButtonElements() in der CWndContainer::AddToElementsArray() Methode erfolgen.
//+-----------------------------------------------------------------+ //| Fügt den Pointer dem Elementen Array hinzu | //+-----------------------------------------------------------------+ void CWndContainer::AddToElementsArray(const int window_index,CElement &object) { //--- Falls die Basis keine Forms oder Controls enthält //--- Falls es eine Anfrage für eine nicht existierende Form gibt //--- Hinzufügen zu dem gemeinsamen Array von Elementen //--- Hinzufügen von Element-Objekten zu dem gemeinsamen Array von Objekten //--- Abspeichern der ID von dem letzten Element in allen Forms //--- Erhöhung des Zählers der Element-Bezeichners //--- Abspeichern der Pointer zu den Kontextmenü Objekten in der Basis //--- Abspeichern der Pointer zu den Hauptmenü Objekten in der Basis //--- Abspeichern der Pointer der splitbutton Objekte in der Basis if(AddSplitButtonElements(window_index,object)) return; }
Alles ist bereit um den Split Button zu testen Wie erzeugen 4 solcher Buttons in unserem Test EA Deklarieren Sie Instanzen der CSplitButton Klasse und Methoden für die Erzeugung der Buttons mit Abständen von dem oberen linken Punkt der Form. Platzieren Sie deren Aufruf in der Hauptmethode für das Erzeugen von grafischen Interfaces des Programms.
class CProgram : public CWndEvents { private: //--- Split buttons CSplitButton m_split_button1; CSplitButton m_split_button2; CSplitButton m_split_button3; CSplitButton m_split_button4; //--- private: //--- Split buttons #define SPLITBUTTON1_GAP_X (7) #define SPLITBUTTON1_GAP_Y (225) bool CreateSplitButton1(const string text); #define SPLITBUTTON2_GAP_X (128) #define SPLITBUTTON2_GAP_Y (225) bool CreateSplitButton2(const string text); #define SPLITBUTTON3_GAP_X (7) #define SPLITBUTTON3_GAP_Y (250) bool CreateSplitButton3(const string text); #define SPLITBUTTON4_GAP_X (128) #define SPLITBUTTON4_GAP_Y (250) bool CreateSplitButton4(const string text); }; //+-----------------------------------------------------------------+ //| Erzeugung des Trading-Panels | //+-----------------------------------------------------------------+ bool CProgram::CreateTradePanel(void) { //--- Erzeugung einer Form für Controls //---Erzeugung der Controls: // Hauptmenü //--- Kontextmenüs //--- Einfache buttons //--- Icon Buttons //--- Split buttons if(!CreateSplitButton1("Split Button 1")) return(false); if(!CreateSplitButton2("Split Button 2")) return(false); if(!CreateSplitButton3("Split Button 3")) return(false); if(!CreateSplitButton4("Split Button 4")) return(false); //--- Neuzeichnen auf dem Chart m_chart.Redraw(); return(true); }
Wir verwenden nun einen von denen als ein Beispiel und zeigen ihn in dem nachfolgenden Code. Bitte beachten Sie, dassVor dem Festlegen der Eigenschaften des Kontextmenü, der Pointer abgefragt werden muss. Dazu verwenden Sie die CSplitButton::GetContextMenuPointer() Methode.
//+-----------------------------------------------------------------+ //| Erzeugt den Split button 1 | //+-----------------------------------------------------------------+ bool CProgram::CreateSplitButton1(const string button_text) { //--- Drei Punkte im Kontextmenü #define CONTEXTMENU_ITEMS5 3 //--- Übergabe des Panel Objektes m_split_button1.WindowPointer(m_window); //--- Koordinaten int x=m_window.X()+SPLITBUTTON1_GAP_X; int y=m_window.Y()+SPLITBUTTON1_GAP_Y; //--- Array Mit den Namen der Menüpunkte string items_text[]= { "Item 1", "Item 2", "Item 3" }; //--- Array mit den Icons für den Verfügbarkeits-Modus string items_bmp_on[]= { "Images\\EasyAndFastGUI\\Icons\\bmp16\\coins.bmp", "Images\\EasyAndFastGUI\\Icons\\bmp16\\line_chart.bmp", "Images\\EasyAndFastGUI\\Icons\\bmp16\\bar_chart.bmp" }; //--- Array von Icons für den gesperrt Modus string items_bmp_off[]= { "Images\\EasyAndFastGUI\\Icons\\bmp16\\coins_colorless.bmp", "Images\\EasyAndFastGUI\\Icons\\bmp16\\line_chart_colorless.bmp", "Images\\EasyAndFastGUI\\Icons\\bmp16\\bar_chart_colorless.bmp" }; //--- Festlegen der Eigenschaften bevor er erzeugt wird m_split_button1.ButtonXSize(116); m_split_button1.ButtonYSize(22); m_split_button1.DropButtonXSize(16); m_split_button1.LabelColor(clrBlack); m_split_button1.LabelColorPressed(clrBlack); m_split_button1.BackColor(clrGainsboro); m_split_button1.BackColorHover(C'193,218,255'); m_split_button1.BackColorPressed(C'190,190,200'); m_split_button1.BorderColor(C'150,170,180'); m_split_button1.BorderColorOff(C'178,195,207'); m_split_button1.BorderColorHover(C'150,170,180'); m_split_button1.IconFileOn("Images\\EasyAndFastGUI\\Icons\\bmp16\\script.bmp"); m_split_button1.IconFileOff("Images\\EasyAndFastGUI\\Icons\\bmp16\\script_colorless.bmp"); //--- Erhalten des Pointers zu dem Kontextmenü des Buttons CContextMenu *cm=m_split_button1.GetContextMenuPointer(); //--- Festlegen der Eigenschaften des Kontextmenüs cm.AreaBackColor(C'240,240,240'); cm.AreaBorderColor(clrSilver); cm.ItemBackColor(C'240,240,240'); cm.ItemBorderColor(C'240,240,240'); cm.LabelColor(clrBlack); cm.LabelColorHover(clrWhite); cm.SeparateLineDarkColor(C'160,160,160'); cm.SeparateLineLightColor(clrWhite); //--- Hinzufügen von Menüpunkten zu dem Kontextmenü for(int i=0; i<CONTEXTMENU_ITEMS5; i++) m_split_button1.AddItem(items_text[i],items_bmp_on[i],items_bmp_off[i]); //--- Trennlinie nach dem ersten Menüpunkt m_split_button1.AddSeparateLine(1); //--- Erzeugung des Controls if(!m_split_button1.CreateSplitButton(m_chart_id,button_text,m_subwin,x,y)) return(false); //--- Hinzufügen des Pointers von dem Control zu der Basisklasse CWndContainer::AddToElementsArray(0,m_split_button1); return(true); }
Nach dem erfolgreichen Kompilieren und dem Starten des Programms auf einem Chart, sollten Sie das folgende Ergebnis sehen:
Fig. 6. Test des Split Button Controls.
Die Entwicklung der Klasse für einen Split Button ist abgeschlossen. Sie können den hier dargestellten EA als Datei, die diesen Artikel beigefügt ist, herunterladen. In dem nächsten Artikel werden wir die Entwicklung von Klassen für die Erzeugung von Gruppen Buttons besprechen (Interconnected Buttons).
Schlussfolgerung
Dieser Artikel hat sich damit beschäftigt, einen simplen und multifunktionalen Button zu erzeugen. In den nächsten Artikel werden wir unsere Bibliothek mit der Klasse für die Erzeugung von kopierten Buttons bereichern.
Die angefügten Dateiarchive beinhalten die Dateien, die dem aktuellen Entwicklungsstatus dieses Projektes entsprechen, so wie die Bilder, die in diesem Artikel verwendet worden sind. Sie können diese zum Test in dem MetaTrader Terminals herunterladen. 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:
- Graphische Interfaces III: Einfache und multifunktionale Buttons (Chapter 1)
- Graphische Interfaces III: Gruppen von einfachen und multifunktionalen Buttons(Kapitel 2)
Übersetzt aus dem Russischen von MetaQuotes Ltd.
Originalartikel: https://www.mql5.com/ru/articles/2296





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