Panels verbessern: Transparenz hinzufügen, Hintergrundfarbe ändern und von CAppDialog/CWndClient übernehmen

6 August 2018, 17:30
Vladimir Karputov
0
94

Inhaltsverzeichnis

Einführung

Panels auf Basis der Klassen CAppDialog fehlen Methoden für den direkten Zugriff auf die Eigenschaften der Steuerelemente, aus denen das Panel besteht, wie die Hintergrundfarbe und die Rahmenfarbe. Daher sind alle erzeugten Panels grau. 

Es ist nicht möglich, Designideen umzusetzen, ohne die Möglichkeit, die Farbe der Bedienelemente zu ändern. Dieses Problem könnte durch Vererben und Hinzufügen eigener Methoden gelöst werden. Aber diese Lösung würde viele Korrekturen im erstellten Code erfordern. Gibt es eine einfachere und schnellere Möglichkeit, auf die Eigenschaften "Hintergrundfarbe" und "Rahmenfarbe" der Bedienelemente zuzugreifen?


Transparenz eines Panels beim Ziehen

Ich werde zuerst zeigen, was für das Panel basierend auf der Klasse CAppDialog getan werden kann (dies ist ein Codebeispiel " Live panel.mq5").

Live-Panel

Dieses Gif-Bild zeigt, dass beim Ziehen des Panels nur der äußere Rand übrig bleibt. Beim Verschieben des Paneels wird zusätzlich die Farbe des Außenrahmens in beliebiger Reihenfolge verändert. Wenn das Ziehen abgeschlossen ist, wird das Formular normal, mit dem Ausfüllen von Arbeitsbereichen.

Umsetzung

Alle Arbeiten sind mit der Klasse CDialog verbunden. Sie befindet sich im Verzeichnis [Datenordner]\MQL5\Include\Controls\Dialog.mqh

Der folgende Code zeigt, aus welchen Objekten das Panel besteht (die Objekte sind in der Klasse CDialog im privaten Bereich deklariert) und wie sie visuell als grafische Elemente implementiert sind:

//+------------------------------------------------------------------+
//| Class CDialog                                                    |
//| Verwendung: Basisklasse zum Erstellen des Dialogfeldes           |
//|             und Indikator-Panel                                  |
//+------------------------------------------------------------------+
class CDialog : public CWndContainer
  {
private:
   //--- abhängige Steuerelemente
   CPanel            m_white_border;        // Das Objekt "weißer Rahmen"
   CPanel            m_background;          // Das Objekt Hintergrund
   CEdit             m_caption;             // Das Objekt Titelleiste des Fensters
   CBmpButton        m_button_close;        // Das Schaltflächenobjekt "Schließen"
   CWndClient        m_client_area;         // Das Objekt Client-Area

protected:

CDialog objects

Um das Panel beim Ziehen transparent zu machen, müssen wir vier Punkte berücksichtigen.

1. Wir interessieren uns für die grafischen Elemente Border und Back (erstellt durch die Objektze m_white_border und m_background der Klasse CPanel) und das Element Client (erzeugt durch das Element m_client_area, Objekt der Klasse CWndClient). Die Funktionen CDialog::CreateWhiteBorder, CDialog::CreateBackground und CDialog::CreateClientArea zeigen, wie die Elemente erstellt werden und welche Farbe für sie eingestellt ist.

2. Während der Erstellung erhält das Panel einen eindeutigen Namen, d.h. ein digitales Präfix, das vor den Namen aller grafischen Objekte eingefügt wird (das Präfix 03082 ist in der folgenden Abbildung dargestellt):

Objekte

3. Drei Ereignisbehandlungen zum Ziehen stellt CDialog bereit:

 Handhabung des Ziehens
 OnDialogDragStart  Das virtuelle Ereignisbehandlung von DialogDragStart
 OnDialogDragProcess  Das virtuelle Ereignisbehandlung von DialogDragProcess
 OnDialogDragEnd  Das virtuelle Ereignisbehandlung von DialogDragEnd

4. Die Lösung des Artikels "Wie erstellt man ein grafisches Panel beliebiger Komplexität?" — eine Iteration über die Objekte des Panels (hier ist ExtDialog das Klassenobjekt des Panels):

   int total=ExtDialog.ControlsTotal();
   for(int i=0;i<total;i++)
     {
      CWnd*obj=ExtDialog.Control(i);
...

Warum wird der Pointer auf das Objekt während der Iteration mit dem Typ CWnd deklariert?

Ganz einfach, CWnd ist die Basisklasse, aus der alle anderen Klassen abgeleitet werden:

Mit der Umsetzung fortfahren

Wir werden für die Arbeit mit Farben zwei Makros benötigen. Wir müssen auch drei Funktionen der Klasse CDialog für das Ziehen des Panels neu definieren:

//+------------------------------------------------------------------+
//|                                                   Live panel.mq5 |
//|                        Copyright 2018, MetaQuotes Software Corp. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2018, MetaQuotes Software Corp."
#property link      "http://www.mql5.com"
#property version   "1.000"
#property description "The panel transparency changes when dragging the panel"
#include <Controls\Dialog.mqh>
#define XRGB(r,g,b)    (0xFF000000|(uchar(r)<<16)|(uchar(g)<<8)|uchar(b))
#define GETRGB(clr)    ((clr)&0xFFFFFF)
//+------------------------------------------------------------------+
//| Class CLivePanel                                                 |
//| Verwendung: Hauptdialog der Anwendung des Steuerelements         |
//+------------------------------------------------------------------+
class CLivePanel : public CAppDialog
  {
public:
                     CLivePanel(void);
                    ~CLivePanel(void);
   //--- Erstellen
   virtual bool      Create(const long chart,const string name,const int subwin,const int x1,const int y1,const int x2,const int y2);
   //--- Ereignisbehandlungen zum Ziehen
   virtual bool      OnDialogDragStart(void);
   virtual bool      OnDialogDragProcess(void);
   virtual bool      OnDialogDragEnd(void);

  };

Wir übergehen den Standardteil der Paneloperationen (Erstellen, Löschen, Übergaben von Ereignissen) Schauen wir uns im Detail die Ereignisbehandlung des Ziehens an.

OnDialogDragStart-Ereignisbehandlung: Der Anfang für das Ziehen eines Panels

We obtain the prefix, and then loop through all objects of the panel and search for "Border", "Back" or "Client" with the prefix:

//+------------------------------------------------------------------+
//| Start dragging the dialog box                                    |
//+------------------------------------------------------------------+
bool CLivePanel::OnDialogDragStart(void)
  {
   string prefix=Name();
   int total=ExtDialog.ControlsTotal();
   for(int i=0;i<total;i++)
     {
      CWnd*obj=ExtDialog.Control(i);
      string name=obj.Name();
      //---
      if(name==prefix+"Border")
        {
         CPanel *panel=(CPanel*) obj;
         panel.ColorBackground(clrNONE);
         ChartRedraw();
        }
      if(name==prefix+"Back")
        {
         CPanel *panel=(CPanel*) obj;
         panel.ColorBackground(clrNONE);
         ChartRedraw();
        }
      if(name==prefix+"Client")
        {
         CWndClient *wndclient=(CWndClient*) obj;
         wndclient.ColorBackground(clrNONE);
         wndclient.ColorBorder(clrNONE);
         ChartRedraw();
        }
     }
   return(CDialog::OnDialogDragStart());
  }

Sobald ein Objekt gefunden wurde, wir dessen Hintergrundfarbe (die Methode ColorBackground) und die Rahmenfarbe (die Methode ColorBorder)gelöscht. Die Transparenz dieser Anzeige wird auch diese Art implementiert.

OnDialogDragProcess-Ereignisbehandlung: Fortsetzung für das Zeihen eines Panels

Wir suchen nur ein Objekt, z.B. "Back" und verändern dynamisch dessen Farbe (mittels zweier Macros: XRGB und GETRGB):

//+------------------------------------------------------------------+
//| Ziehen des Dialogfelds fortsetzen                                |
//+------------------------------------------------------------------+
bool CLivePanel::OnDialogDragProcess(void)
  {
   string prefix=Name();
   int total=ExtDialog.ControlsTotal();
   for(int i=0;i<total;i++)
     {
      CWnd*obj=ExtDialog.Control(i);
      string name=obj.Name();
      if(name==prefix+"Back")
        {
         CPanel *panel=(CPanel*) obj;
         color clr=(color)GETRGB(XRGB(rand()%255,rand()%255,rand()%255));
         panel.ColorBorder(clr);
         ChartRedraw();
        }
     }
   return(CDialog::OnDialogDragProcess());
  }

OnDialogDragEnd-Ereignisbehandlung: Das Ende für das Ziehen eines Panels

Jetzt stellen wir die Farben für den Hintergrund und der Rahmen für die Objekte "Border", "Back" oder "Client"wider her.

//+------------------------------------------------------------------+
//| Ziehen des Dialogfelds beenden                                   |
//+------------------------------------------------------------------+
bool CLivePanel::OnDialogDragEnd(void)
  {
   string prefix=Name();
   int total=ExtDialog.ControlsTotal();
   for(int i=0;i<total;i++)
     {
      CWnd*obj=ExtDialog.Control(i);
      string name=obj.Name();
      //---
      if(name==prefix+"Border")
        {
         CPanel *panel=(CPanel*) obj;
         panel.ColorBackground(CONTROLS_DIALOG_COLOR_BG);
         panel.ColorBorder(CONTROLS_DIALOG_COLOR_BORDER_LIGHT);
         ChartRedraw();
        }
      if(name==prefix+"Back")
        {
         CPanel *panel=(CPanel*) obj;
         panel.ColorBackground(CONTROLS_DIALOG_COLOR_BG);
         color border=(m_panel_flag) ? CONTROLS_DIALOG_COLOR_BG : CONTROLS_DIALOG_COLOR_BORDER_DARK;
         panel.ColorBorder(border);
         ChartRedraw();
        }
      if(name==prefix+"Client")
        {
         CWndClient *wndclient=(CWndClient*) obj;
         wndclient.ColorBackground(CONTROLS_DIALOG_COLOR_CLIENT_BG);
         wndclient.ColorBorder(CONTROLS_DIALOG_COLOR_CLIENT_BORDER);
         ChartRedraw();
        }
     }
   return(CDialog::OnDialogDragEnd());
  }

Hinzufügen einer Schaltfläche zum Panel, die beim Ziehen transparent wird

Dieses Beispiel ist in der Datei "Live panel and Button.mq5" verfügbar.

Um mit der Taste zu arbeiten, wir zuerst die Klasse der Taste in unseren Expert Advisor laden und Makros für die Position und Größe ergänzen:

#property description "The panel transparency changes when dragging the panel,"
#property description " but the color of the added button does not change"
#include <Controls\Dialog.mqh>
#include <Controls\Button.mqh>
#define XRGB(r,g,b)    (0xFF000000|(uchar(r)<<16)|(uchar(g)<<8)|uchar(b))
#define GETRGB(clr)    ((clr)&0xFFFFFF)
//+------------------------------------------------------------------+
//| Definitionen                                                     |
//+------------------------------------------------------------------+
//--- Einzüge und Abstände
#define INDENT_LEFT                         (11)      // Linker Einzug (im Bereich der Rahmenbreite)
#define INDENT_TOP                          (11)      // Oberer Einzug (im Bereich der Rahmenbreite)
#define CONTROLS_GAP_X                      (5)       // Abstand zur X Koordinate
//--- für Schaltflächen
#define BUTTON_WIDTH                        (100)     // Breite, X Koordinate
#define BUTTON_HEIGHT                       (20)      // Höhe, Y Koordinate
//+------------------------------------------------------------------+
//| Class CLivePanelAndButton                                        |
//| Verwendung: Hauptdialog der Anwendung des Steuerelements         |
//+------------------------------------------------------------------+
class CLivePanelAndButton : public CAppDialog

Damit wir auch mit den Tasten arbeiten können, müssen wir das Klassenobjekt CButton deklarieren.

//+------------------------------------------------------------------+
//| Class CLivePanelAndButton                                        |
//| Verwendung: Hauptdialog der Anwendung des Steuerelements         |
//+------------------------------------------------------------------+
class CLivePanelAndButton : public CAppDialog
  {
private:
   CButton           m_button1;                       // Das Objekt Schaltfläche

public:
                     CLivePanelAndButton(void);

und die Methode zum Erstellen der Taste

   virtual bool      OnDialogDragEnd(void);

protected:
   //--- Erstellen der abhängigen Steuerelemente
   bool              CreateButton1(void);

  };

CreateButton1 code — nach dem Erstellen der Taste, darf nicht vergessen werden die Taste dem Panel hinzuzufügen:

//+------------------------------------------------------------------+
//| Erstellen von "Button1"                                          |
//+------------------------------------------------------------------+
bool CLivePanelAndButton::CreateButton1(void)
  {
//--- Koordinaten
   int x1=INDENT_LEFT;        // x1            = 11  Pixel
   int y1=INDENT_TOP;         // y1            = 11  Pixel
   int x2=x1+BUTTON_WIDTH;    // x2 = 11 + 100 = 111 Pixel
   int y2=y1+BUTTON_HEIGHT;   // y2 = 11 + 20  = 32  Pixel
//--- Erstellen
   if(!m_button1.Create(0,"Button1",0,x1,y1,x2,y2))
      return(false);
   if(!m_button1.Text("Button1"))
      return(false);
   if(!Add(m_button1))
      return(false);
//--- war erfolgreich
   return(true);
  }

Hier ist das Ergebnis, wenn die Taste dem Panel hinzugefügt wurde:

Das Panel mit einer Taste

Wie Sie sehen können, wird das zu gezogene Panel transparent, während das hinzugefügte Steuerelement 'Button' undurchsichtig bleibt. Es gibt zwei Möglichkeiten: nur den Panelhintergrund beim Ziehen transparent zu machen oder sowohl das Panel als auch die Taste transparent zu machen. Betrachten wir die zweite Möglichkeit: den Button transparent machen, wenn das Panel gezogen wird.

Transparent machen beim Verschieben des Panels

Machen wir das mit dem Code der Datei "Live Panel und transparent Button.mq5".

Wenn ein einfaches oder kombiniertes Steuerelement zu einem Panel hinzugefügt wird, wird das Objekt, das das Steuerelement erzeugt, dem Objekt m_client_area hinzugefügt (nicht vergessen, das Objekt m_client_area wird in der CDialog-Klasse deklariert). Wenn das Ziehen des Panels erkannt wird, müssen wir also alle Objekte, die zu m_client_area hinzugefügt wurden, durchlaufen. Dies kann bequem durch die erste Ereignisbehandlung OnDialogDragStart (Anfang das Panel zu ziehen) erfolgen:

//+------------------------------------------------------------------+
//| Start dragging the dialog box                                    |
//+------------------------------------------------------------------+
bool CLivePaneTransparentButton::OnDialogDragStart(void)
  {
   string prefix=Name();
   int total=ExtDialog.ControlsTotal();
   for(int i=0;i<total;i++)
     {
      CWnd*obj=ExtDialog.Control(i);
      string name=obj.Name();
      //---
      if(name==prefix+"Border")
        {
         CPanel *panel=(CPanel*) obj;
         panel.ColorBackground(clrNONE);
         ChartRedraw();
        }
      if(name==prefix+"Back")
        {
         CPanel *panel=(CPanel*) obj;
         panel.ColorBackground(clrNONE);
         ChartRedraw();
        }
      if(name==prefix+"Client")
        {
         CWndClient *wndclient=(CWndClient*) obj;
         wndclient.ColorBackground(clrNONE);
         wndclient.ColorBorder(clrNONE);
         //---
         int client_total=wndclient.ControlsTotal();
         for(int j=0;j<client_total;j++)
           {
            CWnd*client_obj=wndclient.Control(j);
            string client_name=client_obj.Name();
            if(client_name=="Button1")
              {
               CButton *button=(CButton*) client_obj;
               button.ColorBackground(clrNONE);
               ChartRedraw();
              }
           }
         ChartRedraw();
        }
     }
   return(CDialog::OnDialogDragStart());
  }

Wenn das Ziehen zum ersten Mal erkannt wird, werden sowohl das Panel als auch die Taste transparent.

Die zweite Ereignisbehandlung OnDialogDragProcess muss nicht geändert werden (Fortsetzung des Panel-Ziehens). Die dritte Ereignisbehandlung OnDialogDragEnd (Ende des Panel-Ziehens) muss geändert werden, da wir die Farbe der Schaltfläche zurücksetzen müssen:

//+------------------------------------------------------------------+
//| Ziehen des Dialogfelds beenden                                   |
//+------------------------------------------------------------------+
bool CLivePaneTransparentButton::OnDialogDragEnd(void)
  {
   string prefix=Name();
   int total=ExtDialog.ControlsTotal();
   for(int i=0;i<total;i++)
     {
      CWnd*obj=ExtDialog.Control(i);
      string name=obj.Name();
      //---
      if(name==prefix+"Border")
        {
         CPanel *panel=(CPanel*) obj;
         panel.ColorBackground(CONTROLS_DIALOG_COLOR_BG);
         panel.ColorBorder(CONTROLS_DIALOG_COLOR_BORDER_LIGHT);
         ChartRedraw();
        }
      if(name==prefix+"Back")
        {
         CPanel *panel=(CPanel*) obj;
         panel.ColorBackground(CONTROLS_DIALOG_COLOR_BG);
         color border=(m_panel_flag) ? CONTROLS_DIALOG_COLOR_BG : CONTROLS_DIALOG_COLOR_BORDER_DARK;
         panel.ColorBorder(border);
         ChartRedraw();
        }
      if(name==prefix+"Client")
        {
         CWndClient *wndclient=(CWndClient*) obj;
         wndclient.ColorBackground(CONTROLS_DIALOG_COLOR_CLIENT_BG);
         wndclient.ColorBorder(CONTROLS_DIALOG_COLOR_CLIENT_BORDER);
         //---
         int client_total=wndclient.ControlsTotal();
         for(int j=0;j<client_total;j++)
           {
            CWnd*client_obj=wndclient.Control(j);
            string client_name=client_obj.Name();
            if(client_name=="Button1")
              {
               CButton *button=(CButton*) client_obj;
               button.ColorBackground(CONTROLS_BUTTON_COLOR_BG);
               ChartRedraw();
              }
           }
         ChartRedraw();
        }
     }
   return(CDialog::OnDialogDragEnd());
  }

Jetzt ist das Ändern des Dialogfelds des Panels und der Farbe der Taste während des Ziehens des Panels vollständig in der Datei "Live panel and transparent Button.mq5" umgesetzt.

Das panel mit transparenten Tasten


Hinzufügen von zwei Schaltflächen zum Panel: Einstellung der Hintergrundfarbe des Panels und der Farbe der Kopfzeilenbeschriftung

Dieses Beispiel ist in der Datei Code von "Live Panel und Button Clicks.mq5" verfügbar und wurde basierend auf dem vorher erwähnten Datei "Live Panel und transparent Button.mq5" erstellt. Allerdings müssen wir jetzt statt des Ereignisses des Ziehens des Panels den Klick auf die Taste "abfangen". Das Panel erhält zwei Tasten: eine Taste für die Änderung des Panelhintergrunds, die zweite für die Änderung der Textfarbe.

Um Ereignisse abzufangen, die mit den Steuerelementen auf dem Panel verbunden sind, müssen wir eine Ereignisbehandlung deklarieren und diese dann codieren:

//+------------------------------------------------------------------+
//| Class CLivePaneButtonClicks                                      |
//| Verwendung: Hauptdialog der Anwendung des Steuerelements         |
//+------------------------------------------------------------------+
class CLivePaneButtonClicks : public CAppDialog
  {
private:
   CButton           m_button1;                       // Das Objekt Schaltfläche
   CButton           m_button2;                       // Das Objekt Schaltfläche

public:
                     CLivePaneButtonClicks(void);
                    ~CLivePaneButtonClicks(void);
   //--- Erstellen
   virtual bool      Create(const long chart,const string name,const int subwin,const int x1,const int y1,const int x2,const int y2);
   //--- Ereignisbehandlung des Charts
   virtual bool      OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam);

protected:
   //--- Erstellen der abhängigen Steuerelemente
   bool              CreateButton1(void);
   bool              CreateButton2(void);
   //--- Ereignisbehandlung durch die abhängigen Steuerelemente
   void              OnClickButton1(void);
   void              OnClickButton2(void);

  };
//+------------------------------------------------------------------+
//| Ereignisbehandlung                                               |
//+------------------------------------------------------------------+
EVENT_MAP_BEGIN(CLivePaneButtonClicks)
ON_EVENT(ON_CLICK,m_button1,OnClickButton1)
ON_EVENT(ON_CLICK,m_button2,OnClickButton2)
EVENT_MAP_END(CAppDialog)
//+------------------------------------------------------------------+
//| Konstruktor                                                      |
//+------------------------------------------------------------------+

Die Ereignisbehandlung geschieht durch die Methode OnEvent, die mit Makros aus dem Block Events und dem Block "Wie erstellt man ein grafisches Panel beliebiger Komplexität?" der Datei Defines.mqh erstellt wurde (Für Details siehe Artikel Wie erstellt man ein grafisches Panel beliebiger Komplexität ).

Die Ereignisbehandlung wird wie folgt interpretiert:

  • Das Ereignis OnEvent für die Klasse CLivePaneButtonClicks:
    • Wenn es einen Klick auf das Steuerelement m_button1 gibt, rufen wir die Ereignisbehandlung durch OnClickButton1 auf.
    • Wenn es einen Klick auf das Steuerelement m_button2 gibt, rufen wir die Ereignisbehandlung OnClickButton2 auf.
  • Rückgabe des Ereignisses OnEvent für die übergeordnete Klasse CAppDialog

Die Ereignisbehandlung von OnClickButton1 und OnClickButton2

Beide Male enthält die Ereignisbehandlung Schleifen über alle Objekte, aus denen die Panels bestehen (sie sind im Abschnitt Umsetzung aufgelistet) - in unserem Fall Schleifen über alle Objekte des Panel-Objektes ExtDialog. Als Ergebnis wird die Methode CWndContainer::ControlsTotal() aufgerufen.

//+------------------------------------------------------------------+
//| Ereignisbehandlung                                               |
//+------------------------------------------------------------------+
void CLivePaneButtonClicks::OnClickButton1(void)
  {
   string prefix=Name();
   int total=ExtDialog.ControlsTotal();
   for(int i=0;i<total;i++)
     {
      CWnd*obj=ExtDialog.Control(i);
      string name=obj.Name();
      //---
      if(name==prefix+"Client")
        {
         CWndClient *wndclient=(CWndClient*) obj;
         color clr=(color)GETRGB(XRGB(rand()%255,rand()%255,rand()%255));
         wndclient.ColorBackground(clr);
         ChartRedraw();
         return;
        }
     }
  }
//+------------------------------------------------------------------+
//| Ereignisbehandlung                                               |
//+------------------------------------------------------------------+
void CLivePaneButtonClicks::OnClickButton2(void)
  {
   string prefix=Name();
   int total=ExtDialog.ControlsTotal();
   for(int i=0;i<total;i++)
     {
      CWnd*obj=ExtDialog.Control(i);
      string name=obj.Name();
      //---
      if(name==prefix+"Caption")
        {
         CEdit *edit=(CEdit*) obj;
         color clr=(color)GETRGB(XRGB(rand()%255,rand()%255,rand()%255));
         edit.ColorBackground(clr);
         ChartRedraw();
         return;
        }
     }
  }

Die Ereignisbehandlung durch OnClickButton1 sucht nach dem Objekt in der Client Area mit dem Namen präfix+"Client" (dies ist das Klassenobjekt CWndClient). OnClickButton2 sucht nach dem Objekt Header-Caption namens prefix+"Caption" (das Klassenobjekt CEdit). In beiden Fällen wird die Hintergrundfarbe für die gefundenen Objekte zufällig gewählt. Hier ist das Ergebnis:

Das Panel mit einem Klick auf die Taste


Vererben von CAppDialog

Das Implementierungsschema unterscheidet sich von dem in den Standard-Bibliotheksbeispielen (\MQL5\Experts\Beispiele\Controls\ und \MQL5\Indicators\Beispiele\Panels\SimplePanel\). Der Unterschied ist folgender: Die von CAppDialog abgeleitete Klasse CMyAppDialog wird in der Datei MyAppDialog.mqh angelegt. In der Klasse sind nur drei Methoden zur Verwaltung der Formularfarbe und der Beschriftungsfarbe implementiert. Es gibt keine Methoden zum Erstellen von zusätzlichen Steuerelemente, der Ereignisbehandlung von OnEvent und der des Klicks auf Testen. 

Die Klassenobjekte CButton (hinzugefügte Steuerelemente - zwei Tasten) werden in der Hauptdatei MyAppWindow.mq5 erstellt. Außerdem werden das Ereignis Klick auf Taste von der Ereignisbehandlung durch OnChartEvent der Datei MyAppWindow.mq5 überwacht. Dort werden auch die Methoden für den Farbwechsel aufgerufen.

MyAppDialog.mqh

Fügen wir unserer Klasse drei Methoden hinzu: 

  • CMyAppDialog::ColorBackground - Einstellung der Hintergrundfarbe,
  • void CMyAppDialog::ColorCaption - Einstellung der Textfarbe,
  • color CMyAppDialog::ColorCaption - Rückgabe der Textfarbe.

Der Algorithmus für den Zugriff auf Objekteigenschaften ist ähnlich wie bei den vorherigen Codes: Wir durchlaufen alle Objekte, aus denen das Panel besteht, und vergleichen die Objektnamen. Wir brauchen noch eine weitere Methode, um die Hintergrundfarbe abzufragen. Dies kann jedoch nicht mit einfachen Lösungen realisiert werden.

//+------------------------------------------------------------------+
//|                                                  MyAppDialog.mqh |
//|                        Copyright 2018, MetaQuotes Software Corp. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2018, MetaQuotes Software Corp."
#property link      "http://www.mql5.com"
#property version   "1.00"
#property description "The CMyAppDialog class derived from CAppDialog"
#property description "Added methods for setting the background and caption colors"
#include <Controls\Dialog.mqh>
//+------------------------------------------------------------------+
//| Class CLivePanelTwoButtons                                       |
//| Verwendung: Hauptdialog der Anwendung des Steuerelements         |
//+------------------------------------------------------------------+
class CMyAppDialog : public CAppDialog
  {
public:
   void              ColorBackground(const color clr);
   color             ColorCaption(void);
   void              ColorCaption(const color clr);
//--- Konstruktor und Destruktor   
public:
                     CMyAppDialog(void){};
                    ~CMyAppDialog(void){};
  };
//+------------------------------------------------------------------+
//| Setzen der Hintergrundfarbe                                      |
//+------------------------------------------------------------------+
void CMyAppDialog::ColorBackground(const color clr)
  {
   string prefix=Name();
   int total=ControlsTotal();
   for(int i=0;i<total;i++)
     {
      CWnd*obj=Control(i);
      string name=obj.Name();
      //---
      if(name==prefix+"Client")
        {
         CWndClient *wndclient=(CWndClient*) obj;
         wndclient.ColorBackground(clr);
         ChartRedraw();
         return;
        }
     }
//---     
  } 
//+------------------------------------------------------------------+
//| Setzen der Schriftfarbe                                          |
//+------------------------------------------------------------------+
void CMyAppDialog::ColorCaption(const color clr)
  {
   string prefix=Name();
   int total=ControlsTotal();
   for(int i=0;i<total;i++)
     {
      CWnd*obj=Control(i);
      string name=obj.Name();
      //---
      if(name==prefix+"Caption")
        {
         CEdit *edit=(CEdit*) obj;
         edit.ColorBackground(clr);
         ChartRedraw();
         return;
        }
     }
//---     
  }  
//+------------------------------------------------------------------+
//| Abfrage der Schriftfarbe                                         |
//+------------------------------------------------------------------+
color CMyAppDialog::ColorCaption(void)
  {
   string prefix=Name();
   int total=ControlsTotal();
   color clr=clrNONE;
   for(int i=0;i<total;i++)
     {
      CWnd*obj=Control(i);
      string name=obj.Name();
      //---
      if(name==prefix+"Caption")
        {
         CEdit *edit=(CEdit*) obj;
         clr=edit.ColorBackground(clr);
         return clr;
        }
     }
//--- Rückgabe der Farbe
   return clr;     
  }  
//+------------------------------------------------------------------+


MyAppWindow.mq5

 "MyAppWindow.mq5" ist die Hauptdatei. In dieser Datei werden die Makros XRGB und GETRGB deklariert. Es wird das Panel erstellt, die Tasten ergänzt und das Panel in OnInit() gestartet.

//+------------------------------------------------------------------+
//|                                                  MyAppWindow.mq5 |
//|                        Copyright 2018, MetaQuotes Software Corp. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2018, MetaQuotes Software Corp."
#property link      "http://www.mql5.com"
#property version   "1.00"
#property description "The MyAppWindow application based on the CMyAppDialog class"
#property description "Added buttons for setting the background and header colors"
#include "MyAppDialog.mqh"
#include <Controls\Button.mqh>
//--- Makros für die Farben
#define XRGB(r,g,b)    (0xFF000000|(uchar(r)<<16)|(uchar(g)<<8)|uchar(b))
#define GETRGB(clr)    ((clr)&0xFFFFFF)
//+------------------------------------------------------------------+
//| Definitionen                                                     |
//+------------------------------------------------------------------+
//--- Einzüge und Abstände
#define INDENT_LEFT                         (11)      // Linker Einzug (im Bereich der Rahmenbreite)
#define INDENT_TOP                          (11)      // Oberer Einzug (im Bereich der Rahmenbreite)
#define CONTROLS_GAP_X                      (5)       // Abstand zur X Koordinate
//--- für Schaltflächen
#define BUTTON_WIDTH                        (100)     // Breite, X Koordinate
#define BUTTON_HEIGHT                       (20)      // Höhe, Y Koordinate
//---
CMyAppDialog         AppWindow;
CButton              m_button1;                       // Das Objekt Schaltfläche
CButton              m_button2;                       // Das Objekt Schaltfläche
//+------------------------------------------------------------------+
//| Initialisierungsfunktion des Experten                            |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- Erstellen des Dialogs der Anwendung
   if(!AppWindow.Create(0,"CMyAppDialog: change Back and Caption colors",0,40,40,380,344))
      return(INIT_FAILED);
//--- Erstellen der abhängigen Steuerelemente
   if(!CreateBackButton())
      return(false);
   if(!CreateCaptionButton())
      return(false);
//--- Starten der Anwendung
   AppWindow.Run();
//--- war erfolgreich
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Deinitialisierungsfunktion des Experten                          |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//---
   Comment("");
//--- Löschen des Dialogs
   AppWindow.Destroy(reason);
  }
//+------------------------------------------------------------------+
//| Ereignisfunktion des Charts des Experten                         |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,         // Ereignis ID  
                  const long& lparam,   // Ereignisparameter vom Typ long
                  const double& dparam, // Ereignisparameter vom Typ double
                  const string& sparam) // Ereignisparameter vom Typ string
  {
//--- Ereignisbehandlung der Tasten kommt zuerst 
   if((StringFind(sparam,"Back")!=-1) && id==(CHARTEVENT_OBJECT_CLICK))
     {
      Print(__FUNCSIG__," sparam=",sparam);
      AppWindow.ColorBackground(GetRandomColor());
     }
   if((StringFind(sparam,"Caption")!=-1) && id==(CHARTEVENT_OBJECT_CLICK))
     {
      Print(__FUNCSIG__," sparam=",sparam);
      AppWindow.ColorCaption(GetRandomColor());
     }
//--- Dann werden alle anderen Ereignisse von der Methode der Klasse CMyAppDialog abgearbeitet   
   AppWindow.ChartEvent(id,lparam,dparam,sparam);
  }
//+------------------------------------------------------------------+
//| Erstellen von "Button1"                                          |
//+------------------------------------------------------------------+
bool CreateBackButton(void)
  {
//--- Koordinaten
   int x1=INDENT_LEFT;        // x1            = 11  Pixel
   int y1=INDENT_TOP;         // y1            = 11  Pixel
   int x2=x1+BUTTON_WIDTH;    // x2 = 11 + 100 = 111 Pixel
   int y2=y1+BUTTON_HEIGHT;   // y2 = 11 + 20  = 32  Pixel
//--- Erstellen
   if(!m_button1.Create(0,"Back",0,x1,y1,x2,y2))
      return(false);
   if(!m_button1.Text("Back"))
      return(false);
   if(!AppWindow.Add(m_button1))
      return(false);
//--- war erfolgreich
   return(true);
  }
//+------------------------------------------------------------------+
//| Erstellen von "Button2"                                          |
//+------------------------------------------------------------------+
bool CreateCaptionButton(void)
  {
//--- Koordinaten
   int x1=INDENT_LEFT+2*(BUTTON_WIDTH+CONTROLS_GAP_X);   // x1 = 11  + 2 * (100 + 5) = 221 pixels
   int y1=INDENT_TOP;                                    // y1                       = 11  Pixel
   int x2=x1+BUTTON_WIDTH;                               // x2 = 221 + 100           = 321 Pixel
   int y2=y1+BUTTON_HEIGHT;                              // y2 = 11  + 20            = 31  Pixel
//--- Erstellen
   if(!m_button2.Create(0,"Caption",0,x1,y1,x2,y2))
      return(false);
   if(!m_button2.Text("Caption"))
      return(false);
   if(!AppWindow.Add(m_button2))
      return(false);
//--- war erfolgreich
   return(true);
  }
//+------------------------------------------------------------------+
//| Abfrage einer Zufallsfarbe                                       |
//+------------------------------------------------------------------+
color GetRandomColor()
  {
   color clr=(color)GETRGB(XRGB(rand()%255,rand()%255,rand()%255));
   return clr;
  }
//+------------------------------------------------------------------+


Die Ereignisbehandlung durch OnChartEvent ist auch Teil der Hauptdatei. Es sendet unbedingt alle Ereignisse an das Panel, aber wenn das Ereignis eines Tastenklicks erkannt wird (CHARTEVENT_OBJECT_CLICK), ruft es Methoden der Klassen des Panels (AppWindow.ColorBackground oder AppWindow.ColorCaption) auf.

So funktionieren eine Reihe von zwei Dateien: die Hauptdatei mq5 und die Include-Datei mqh, in der sich die Panel-Klasse befindet.


Vererbung von CWndClient

Betrachten wir die Vererbung von CWndClient in dieser Klasse: wir erstellen ein Objekt der Klasse CWndClient. Das Objekt wird Folgendes ausführen können:

  • Erstellung des Objektes CMyWndClient, d.h. des Nutzerbereichs des Panels;
  • Erstellung des Objekts zum Hinzufügen von zwei Schaltflächen im Nutzerbereich;
  • Die Ereignisbehandlung von Klicks auf die hinzugefügten Tasten (Ändern der Hintergrundfarbe des Nutzerbereichs und der Farbe der Kopfzeile des Panels);
  • Zusätzlich wird ein horizontales Blättern für den Nutzerbereich aktiviert (beachten Sie, dass die Klasse CWndClient ein kombiniertes Steuerelement "Nutzerbereich" ist und die Basisklasse zum Erstellen von Bereichen mit Blättern ist);
  • Entsprechend gibt es die Ereignisbehandlung für Klicks auf das horizontale Blättern (Verschieben der hinzugefügten Buttons im Nutzerbereich).

Schauen wir uns die Dateien MyWndClient.mq5 und MyWndClient.mqh an.

MeinWndClient.mq5

Der Unterschied zu den Beispielen aus der Standardbibliotheken (\MQL5\Experts\Beispiele\Controls\ und \MQL5\Indikatoren\Beispiele\Panels\SimplePanel\) ist, dass die include-Datei eine Klasse hat, die von CWndClient 10 - dem Nutzerbereich - abgeleitet ist. Der gesamte Erstellungszyklus des Dialogfensters sieht wie folgt aus.

  1. Erstellung eines Panels (das Objekt AppWindow der Klasse CAppDialog ruft die Methode Create auf).
  2. Erstellung unseres Nutzerbereichs (das Objekt ClientArea der Klasse CMyWndClient aus die Include-Datei MyWndClient.mqh ruft die Methode Create auf)..
  3. Hinzufügen des erstellten Nutzerbereichs zum Panel (tatsächlich über den Nutzerbereich des Panels angewendet).
CAppDialog           AppWindow;
CMyWndClient         ClientArea;
//+------------------------------------------------------------------+
//| Initialisierungsfunktion des Experten                            |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- Erstellen des Dialogs der Anwendung
   bool result_create=false;
   if(!InpTwoButtonsVisible)
     {
      //--- nach dem Erstellen des Panels ist eine Taste sichtbar
      result_create=AppWindow.Create(0,"CAppDialog with CMyWndClient",0,40,40,360,344);
     }
   else
     {
      //--- nach dem Erstellen des Panels sind zwei Tasten sichtbar
      result_create=AppWindow.Create(0,"CAppDialog with CMyWndClient",0,40,40,420,344);
     }
   if(!result_create)
      return(INIT_FAILED);
//--- Erstellen des Panels
   PrintFormat("Application Rect: Height=%d  Width=%d",AppWindow.Rect().Height(),AppWindow.Rect().Width());
   CRect inner_rect=ClientArea.GetClientRect(GetPointer(AppWindow));
   PrintFormat("Client Area: Height=%d  Width=%d",inner_rect.Height(),inner_rect.Width());
   ClientArea.Create(0,"MyWndClient",0,0,0,inner_rect.Width(),inner_rect.Height());
   AppWindow.Add(ClientArea);
//--- setzen des Eigners
   ClientArea.SetOwner(GetPointer(AppWindow));
//--- das Unsichtbare ausblenden
   ClientArea.HideInvisble(HideInvisble);
//--- Starten der Anwendung
   AppWindow.Run();
//--- war erfolgreich
   return(INIT_SUCCEEDED);
  }

Die ausführbare Datei hat zwei Eingabeparameter:

  • Plattenbreite - Erstellen eines Panels mit normaler Breite oder eines breiten Panels;
  • Ausblenden des Unsichtbaren - Ein- oder Ausblenden von versteckten Steuerelementen.

Nur der Parameter Panelbreite wird bei der Erstellung eines Panels berücksichtigt. So entsteht ein Panel mit der Breite normal width:

//+------------------------------------------------------------------+
//| Initialisierungsfunktion des Experten                            |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- Erstellen des Dialogs der Anwendung
   bool result_create=false;
   if(!InpTwoButtonsVisible)
     {
      //--- nach dem Erstellen des Panels ist eine Taste sichtbar
      result_create=AppWindow.Create(0,"CAppDialog with CMyWndClient",0,40,40,360,344);
     }
   else
     {
      //--- nach dem Erstellen des Panels sind zwei Tasten sichtbar
      result_create=AppWindow.Create(0,"CAppDialog with CMyWndClient",0,40,40,420,344);
     }
   if(!result_create)
      return(INIT_FAILED);
//--- Erstellen des Panels

MyWndClient normal width panel

Und so wird ein breites Panel erstellt:

//+------------------------------------------------------------------+
//| Initialisierungsfunktion des Experten                            |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- Erstellen des Dialogs der Anwendung
   bool result_create=false;
   if(!InpTwoButtonsVisible)
     {
      //--- nach dem Erstellen des Panels ist eine Taste sichtbar
      result_create=AppWindow.Create(0,"CAppDialog with CMyWndClient",0,40,40,360,344);
     }
   else
     {
      //--- nach dem Erstellen des Panels sind zwei Tasten sichtbar
      result_create=AppWindow.Create(0,"CAppDialog with CMyWndClient",0,40,40,420,344);
     }
   if(!result_create)
      return(INIT_FAILED);
//--- Erstellen des Panels

MyWndClient wide panel

Der Code für das Erstellen des Panels unterscheidet sich nur in der Breite (360 und 420). Die Breite wird bei der Erstellung von zwei Tasten nicht berücksichtigt. Vergleichen wir die letzten beiden Bilder. Wenden wir sie nun übereinander an:

CMyWndClient imposition

Wie wir sehen können, befindet sich die Schaltfläche "Caption" nicht innerhalb der Grenzen des Panels mit der normalen Breite, d.h. sie passt nicht in den Nutzerbereich. Wenn die hinzugefügten Steuerelemente über die Grenzen hinausragen, werden sie für den Benutzer zwangsweise ausgeblendet (aber nicht gelöscht oder zerstört). Die Prozedur zur Bestimmung, ob ein Steuerelement ausgeblendet werden soll, beginnt beim Hinzufügen des Steuerelements zum Nutzerbereich, d.h. beim Aufruf von CWndContainer::Add. In unserem Beispiel wird die Methode 'Add' in AddButton2 aufgerufen:

//+------------------------------------------------------------------+
//| Erstellen von "Button2"                                          |
//+------------------------------------------------------------------+
bool CMyWndClient::AddButton2(void)
  {
...
   if(!Add(m_button2))
     {
      Print("Add(m_button2) --> false");
      return(false);
     }
//--- war erfolgreich
   return(true);
  }
//+------------------------------------------------------------------+
//| Hinzuf. des Steuerelements zur Gruppe (durch Referenz)           |
//+------------------------------------------------------------------+
bool CWndContainer::Add(CWnd &control)
  {
//--- ergänzen durch den Pointer
   return(Add((CWnd*)GetPointer(control)));
  }

Dann werden de Reihe nach die wichtigsten Teile aufgerufen - hier muss das Ausblenden der Steuerelemente bestimmt werden

//+------------------------------------------------------------------+
//| Hinzuf. des Steuerelements zur Gruppe (durch Pointer)            |
//+------------------------------------------------------------------+
bool CWndContainer::Add(CWnd *control)
  {
//--- Zeigerprüfung
   if(control==NULL)
      return(false);
//--- Korrigieren der Koordinaten des ergänzten Steuerelements
   control.Shift(Left(),Top());
 //--- "projektieren" der Flag-Gruppe "visibility" zum hinzugefügten Element
   if(IS_VISIBLE && control.IsVisible())
     {
      //--- Element ist nur sichtbar (visible), wenn die Gruppe sichtbar  ist und das Element sich vollständig innerhalb (within) der Gruppe ist
      control.Visible(Contains(control));
     }
   else
      control.Hide();
//--- "projektieren" der Flag-Gruppe "enables" zum hinzugefügten Element
   if(IS_ENABLED)
      control.Enable();
   else
      control.Disable();
//--- Hinzufügen
   return(m_controls.Add(control));
  }

Die Sichtbarkeit des Objekts wird nur zum Zeitpunkt des Hinzufügens des Steuerelements in den Nutzerbereich gesetzt. Dies ist der Nachteil, der sich nach der Minimierung und Maximierung des Panels zeigen kann.

Beispiel: Wir setzen einen "falschen" Wert für beide Eingabeparameter, minimieren dann das Panel und maximieren es wieder. Als Ergebnis wird die Beschriftung der Taste nach der Erstellung des Panels erstellt, aber sie wird optisch ausgeblendet (die Schaltfläche passt nicht in den Nutzerbereich, so dass sie vor diesem ausgeblendet wird). Aber nach dem Minimieren und Maximieren des Panels wird die Sichtbarkeit der hinzugefügten Steuerelemente nicht mehr überprüft, daher ist die Beschriftung der Schaltfläche sichtbar:

CMyWndClient Beschriftung der Taste

Die Datei enthält die aus der Klasse CWndClient des Nutzerbereichs abgeleitete Klasse. Die benötigten Funktionen sind in dieser Datei enthalten:

  • Erstellung unseres Nutzerbereiches, 
  • Erstellen und Hinzufügen von Steuerelementen, 
  • Umgang mit dem Ereignis der Maximierung des Panels
  • Behandlung des Ereignisses eines Klicks auf die horizontale Bildlaufleiste
  • Behandlung von Ereignissen von Klicks auf Tasten - Ändern der Hintergrundfarbe des Nutzerbereichs und der Farbe der Panel-Beschriftung.

Horizontales Blättern und versteckte Steuerelemente

Da die Panelklasse von CWndClient abgeleitet ist und CWndClient das kombinierte Steuerelement "Nutzerbereich" ist und eine Basisklasse für Erstellung von Bereichen mit Scrollbars ist, müssen wir die horizontale Bildlaufleiste in unserem Nutzerbereich aktivieren:

//+------------------------------------------------------------------+
//| Erstellen des Panels                                             |
//+------------------------------------------------------------------+
bool CMyWndClient::Create(const long chart,const string name,const int subwin,const int x1,const int y1,const int x2,const int y2)
  {
//---
   if(!CWndClient::Create(chart,name,subwin,x1,y1,x2,y2))
      return(false);
//--- aktivieren der horizontalen Bildlaufleiste
   if(!HScrolled(true))
      return(false);
   m_scroll_h.MaxPos(5);
   m_scroll_h.CurrPos(5);
   Print("CurrPos: ",m_scroll_h.CurrPos());
   if(!AddButton1())

Let's set gradations for the scroll to make it movable: maximum position value, and position the scroll in the rightmost position

The horizontal scroll is also used for detecting the panel maximizing event. For this purpose we need to "catch" the ON_SHOW event for the m_scroll_h object and call the OnShowScrollH handler:

//+------------------------------------------------------------------+
//| Ereignisbehandlung                                               |
//+------------------------------------------------------------------+
EVENT_MAP_BEGIN(CMyWndClient)
ON_EVENT(ON_CLICK,m_button1,OnClickButton1)
ON_EVENT(ON_CLICK,m_button2,OnClickButton2)
ON_EVENT(ON_SHOW,m_scroll_h,OnShowScrollH)
EVENT_MAP_END(CWndClient)

EVENT_MAP_BEGIN ist eigentlich die Methode OnEvent, geschrieben mit Makros aus dem Block Events und dem Block des Makros der Ereignisbehandlung map aus der Datei Defines.mqh (Details im Artikel Wie erstellt man ein grafisches Panel beliebiger Komplexität?).

In der Ereignisbehandlung durch OnShowScrollH müssen wir den Wert des internen m_hide_invisble flag überprüfen (der Flag-Wert ist gleich der Eingabevariablen "hide the invisible" der Datei MyWndClient.mq5, mittels der Methode CMyWndClient::HideInvisble). Wenn die Steuerelemente nicht ausgeblendet werden müssen, beenden wir die Prozedur:

//+------------------------------------------------------------------+
//| Eine Bildlaufleiste erscheint, Tasten zum ein-/ausblenden        |
//+------------------------------------------------------------------+
void CMyWndClient::OnShowScrollH(void)
  {
   if(!m_hide_invisble)
      return;
   int total=CWndClient::ControlsTotal();
   for(int i=0;i<total;i++)
     {
      CWnd*obj=Control(i);
      string name=obj.Name();
      //---
      if(StringFind(name,"Button")!=-1)
        {
         CButton *button=(CButton*)obj;
         button.Visible(Contains(GetPointer(button)));
         ChartRedraw();
        }
     }
  }

Wenn unsichtbare Elemente ausgeblendet werden sollen, dann iterieren wir über alle Objekte des Nutzerbereichs, um Objekte mit Namen zu finden, die Taste (Button) und Erzwungene Sichtbarkeit prüfen/setzen enthalten.

Klicks auf die horizontale Bildlaufleiste

Ermöglichen wir es, zwei Tasten des Client-Bereichs entlang der horizontalen Bildlaufleiste zu verschieben. Zu diesem Zweck überschreiben wir OnScrollLineRight und OnScrollLineLeft, die Ereignisbehandlung von Klicks auf die Tasten der horizontalen Bildlaufleiste. Wenn die rechte Taste der horizontale Bildlaufleiste angeklickt wird, verschieben wir die Taste (mit der Methode ShiftButton) um die der Schrittweite m_scroll_size. Wenn der linke Button angeklickt wird, verschieben wir die Taste um die Schrittweite von "-m_scroll_size", d.h. es ist eine negatives Verschieben:

//+------------------------------------------------------------------+
//| Ereignisbehandlung                                               |
//+------------------------------------------------------------------+
bool CMyWndClient::OnScrollLineRight(void)
  {
   Print(__FUNCTION__);
   ShiftButton(GetPointer(m_button1),m_scroll_size);
   ShiftButton(GetPointer(m_button2),m_scroll_size);
   return(true);
  }
//+------------------------------------------------------------------+
//| Ereignisbehandlung                                               |
//+------------------------------------------------------------------+
bool CMyWndClient::OnScrollLineLeft(void)
  {
   Print(__FUNCTION__);
   ShiftButton(GetPointer(m_button1),-m_scroll_size);
   ShiftButton(GetPointer(m_button2),-m_scroll_size);
   return(true);
  }

In der Methode CMyWndClient::ShiftButton erhalten wir das Objekt mit den Koordinaten der Taste, setzen die Verschiebung für die Koordinaten, verschieben die Taste und setzen die Sichtbarkeit der Taste zwangsweise:

//+------------------------------------------------------------------+
//| Taste rechts/links verschieben (abhängig vom Wert)               |
//+------------------------------------------------------------------+
bool CMyWndClient::ShiftButton(CButton *button,const int shift)
  {
   Print(__FUNCTION__);
//--- Verschieben der Taste
   CRect rect=button.Rect();
   rect.Move(rect.left+shift,rect.top);
   button.Move(rect.left,rect.top);
   button.Visible(Contains(GetPointer(button)));
   return(true);
  }

Und so schaut das dann aus (nicht vergessen, der Parameter "hide the invisible" muss auf 'true' gesetzt werden):

CMyWndClient move buttons



Neue Projekte. Wie können sie beim Studium der Panels helfen?

Programmieren muss man lernen. Wenn es um die Erstellung eines Panels geht, kann diese Lernen viel Zeit und Mühe in Anspruch nehmen. Dies liegt vor allem daran, dass es keine visuelle Darstellung der Klassenstruktur gibt. Es kann schwierig sein zu verstehen, welche Standard-Bibliotheksklassen für die Erstellung von Panels verwendet werden. 

Glücklicherweise wurde vor Kurzen im MetaEditor Neue Projekte implementiert.

Ein Projekt ist eine separate Datei mit der Erweiterung MQPROJ, die Programmeinstellungen, Kompilierungsparameter und Informationen über alle im Projekt verwendeten Dateien speichert. Unter einem eigenen Reiter des Navigators wird diese Form von Projektarbeit angeboten. Alle Dateien, wie Include-, Ressourcen-, Header- und andere Dateien sind in Kategorien in diesem Reiter angeordnet.

Beachten Sie die Beschreibung des entsprechenden Reiters: "Alle Dateien, wie Include-, Ressourcen-, Header- und andere Dateien sind in Kategorien in diesem Reiter angeordnet."! Das ist genau das, was wir brauchen!

Versuchen wir, ein Projekt mit der letzten Datei "Live panel and button Clicks.mq5" zu erstellen. Ein Klick mit der rechten Maustaste auf das Panel "Live-Panel und die Schaltfläche Clicks.mq5" und die Wahl von "New Project from Source":


Als Ergebnis wir ein neues Projekt erstellt und der Reiter des Projekts öffnet sich im Navigator. Der Reiter zeigt alle verwendeten Dateien:


sowie Wnd.mqh (enthält die Klasse CWnd), Dialog.mqh (enthält die Klassen CDialog und CAppDialog) und Button.mhq (enthält CButton). Von diesem Reiter aus können man bequem in die gewünschte Klasse wechseln. Dies ist viel bequemer als die Navigation durch die Reiter des MetaEditors. Zum Beispiel habe ich eine Auswahl von verschiedenen Dateien. Es ist schwierig, zu einer gewünschten Datei zu gelangen, z.B. Dialog.mqh, wenn man im Reiter sucht:


Schlussfolgerung

Der Artikel zeigt eine eher ungewöhnliche Möglichkeit, auf die Eigenschaften "Hintergrundfarbe", "Rahmenfarbe" und "Kopffarbe" der Elemente des Panels zuzugreifen. So etwas habe ich noch nie gesehen. Der Punkt ist, dass alle Elemente des Panels von der übergeordneten KLasse CWnd abgeleitet sind, so dass das Objekt, das die Klasse des erstellten Panels ist, der Container für alle Steuerelemente ist. So können Sie jetzt alle Steuerelemente durchlaufen und die gewünschten Eigenschaften abrufen/einstellen.

Dateiname Kommentar
 Live panel.mq5  Ein Panel ohne zusätzliche Steuerelemente. Wird beim Ziehen transparent
 Live panel and Button.mq5  Ein Panel mit einer zusätzlichen Taste. Während des Ziehens wird das Panel transparent und die Taste behält die Farbe.
 Live panel and transparent Button.mq5  Ein Panel mit einer zusätzlichen Taste. Beim Ziehen werden sowohl das Panel als auch die Testen transparent.
 Live panel and button Clicks.mq5  Ein Panel mit zwei Tasten. Tasten der Ereignisbehandlung: Generierung der Hintergrundfarbe für das Panel und der Kopfzeile
 MyAppWindow.mq5 and MyAppDialog.mqh  Beispiel für Panelerstellung durch Vererbung von CAppDialog
 MyWndClient.mq5 and MyWndClient.mqh  Beispiel für die Panelerstellung durch Vererbung aus der Klasse des Nutzerbereichs CWndClient

Übersetzt aus dem Russischen von MetaQuotes Software Corp.
Originalartikel: https://www.mql5.com/ru/articles/4575

Beigefügte Dateien |
MQL5.zip (18.28 KB)
Tiefe Neuronale Netzwerke (Teil VI). Gruppen von Klassifikatoren von Neuronalen Netzen: Bagging Tiefe Neuronale Netzwerke (Teil VI). Gruppen von Klassifikatoren von Neuronalen Netzen: Bagging

Der Artikel beschreibt die Methoden des Aufbaus und Trainings von Gruppen von Neuronalen Netzen mit einer Struktur für das Bagging, einer Methode, um Vorhersagen aus verschiedenen Regressions- oder Klassifikationsmodellen zu kombinieren. Es bestimmt auch die Besonderheiten der Hyperparameter-Optimierung für einzelne Neuronale Netzwerk-Klassifikatoren, aus denen sich das Ensemble zusammensetzt. Die Qualität des optimierten Neuronalen Netzes, das im vorherigen Artikel der Serie erhalten wurde, wird mit der Qualität des erzeugten Ensembles Neuronaler Netze verglichen. Möglichkeiten, die Qualität der Klassifizierung des Ensembles weiter zu verbessern, werden geprüft.

Social Trading. Kann eine gutes Signal weiter verbessert werden? Social Trading. Kann eine gutes Signal weiter verbessert werden?

Die meisten Abonnenten wählen ein Handelssignal nach der Schönheit der Bilanzkurve und nach der Anzahl der Abonnenten. Deshalb achten viele Anbieter heute eher auf schöne Statistiken als auf echte Signalqualität, spielen oft mit Losgrößen und reduzieren die Saldenkurve künstlich nur für ein ideales Erscheinungsbild. Dieses Papier befasst sich mit den Zuverlässigkeitskriterien und den Methoden, mit denen ein Anbieter seine Signalqualität verbessern kann. Eine beispielhafte Analyse einer bestimmten Signalhistorie wird vorgestellt, ebenso wie Methoden, die einem Anbieter helfen würden, diese profitabler und risikoärmer zu gestalten.

Ein Expert Advisor mit GUI: Erstellen des Panels (Teil I) Ein Expert Advisor mit GUI: Erstellen des Panels (Teil I)

Trotz der Tatsache, dass viele Händler immer noch den manuellen Handel bevorzugen, ist es kaum möglich, die Automatisierung von Routineoperationen vollständig zu vermeiden. Der Artikel zeigt ein Beispiel für die Entwicklung eines Expert Advisor mit Signalen von mehreren Symbolen für den manuellen Handel.

Mit der Monte-Carlo-Methode Handelsstrategien optimieren Mit der Monte-Carlo-Methode Handelsstrategien optimieren

Bevor wir einen Roboter auf einem Handelskonto starten, testen und optimieren wir ihn in der Regel anhand der Kurshistorie. Es stellt sich jedoch die berechtigte Frage: Wie können uns die Ergebnisse der Vergangenheit in Zukunft helfen? Der Artikel beschreibt die Anwendung der Monte-Carlo-Methode, um benutzerdefinierte Kriterien für die Optimierung der Handelsstrategie zu erstellen. Zusätzlich werden die EA-Stabilitätskriterien berücksichtigt.