English Русский 中文 Español 日本語 Português
preview
DoEasy. Steuerung (Teil 1): Erste Schritte

DoEasy. Steuerung (Teil 1): Erste Schritte

MetaTrader 5Beispiele | 7 Juni 2022, 08:47
407 0
Artyom Trishkin
Artyom Trishkin

Inhalt


Konzept

Der Artikel startet eine neue Serie, die sich dem Erstellen von Steuerelementen im Windows Forms-Stil widmet. Natürlich ist es unmöglich, alle in der Liste der Steuerelemente enthaltenen Elemente in MS Visual Studio zu reproduzieren. Ich werde die beliebtesten Elemente für die Entwicklung von App-GUIs mit MQL5 implementieren.

Der Grund, warum ich zu einem neuen Thema gewechselt bin, ohne die vorherigen abgeschlossen zu haben, war die Notwendigkeit, die Steuerelemente zu verwenden, um die Entwicklung der grafischen Bibliotheksobjekte fortzusetzen, die in den vorherigen Themen behandelt wurden. Schon jetzt wird es schwierig, die Dinge ohne Kontrolle zu managen. Daher werde ich alle möglichen Steuerelemente im Windows Forms-Stil erstellen. Dann werde ich zu den vorherigen Themen zurückkehren, während ich alle notwendigen Entwicklungswerkzeuge habe.

Wenn wir das Bedienfeld der Elemente in MS Visual Studio öffnen, sehen wir die Liste der Kontrollgruppen:

  • All Windows Forms – alle für die Implementierung verfügbaren Formulare
  • Standardsteuerung
  • Container
  • Menüs und Symbolleisten
  • Daten
  • Komponenten
  • Drucken
  • Dialogboxen

Dies sind nicht alle Gruppen, die in der Liste des Elementbereichs von MS Visual Studio verfügbar sind. Jede dieser Gruppen enthält eine große Menge von Elementen. Nicht alle sind für die Bibliothek notwendig. Ich werde mich auf das Wesentliche konzentrieren.

Ich beginne mit dem Panel-Element, da es als Basis für Fensterelemente dient. Außerdem ist das Panel ein Container zum Speichern anderer Steuerelemente, während das Panel mit allen gespeicherten Elementen wiederum in das übergeordnete Panel platziert werden kann, während letzteres auch ein Objekt innerhalb eines anderen Panels sein kann usw.

Wir haben bereits die Klasse eines grafischen Elementobjekts auf einer Leinwand (canvas), die eine Elternklasse für alle anderen grafischen Objekte ist, die auf der Klasse CCanvas basieren. Das Formularklassenobjekt basiert auf einem grafischen Element. Das Formularobjekt verfügt bereits über eine Reihe von Funktionen, um es zu manipulieren und zu verschieben. Das Panel-Objekt wird basierend auf einem Formularobjekt erstellt. Die neuen Eigenschaften werden dem Formularobjekt hinzugefügt, um seine Funktionalität zu implementieren.

Das Panel kann alle Steuerelemente speichern, die ich im aktuellen Abschnitt der Bibliotheksentwicklungsbeschreibung erstellen werde. Das Panel wird es uns auch ermöglichen, Basis- und Dialogfenster einer Anwendung zu implementieren, die im Terminal arbeitet.

Vor der Entwicklung der Panel-Klasse sollten wir die bereits entwickelten Bibliotheksobjektklassen verbessern. Schließlich habe ich meine Arbeit an den vorangegangenen Themen noch nicht abgeschlossen. Ich werde die bestehenden Bibliotheksobjekte nach und nach fertigstellen und die festgestellten Fehler beheben.


Verbesserung des Bibliotheksunterrichts

Das letzte Update der Terminal-Version (3260) enthält neue Eigenschaften für Symbol und Konto:

  • MQL5: Das Element SYMBOL_SUBSCRIPTION_DELAY wurde zur Enumeration ENUM_SYMBOL_INFO_INTEGER für die Verzögerung bei versendeten Kursen für bestimmte Symbole hinzugefügt.
    Es wird nur für abonnementbasierte Handelssymbole verwendet. Die Verzögerung gilt normalerweise für Daten, die im Testmodus bereitgestellt werden.
    Die Eigenschaft kann nur für Symbole abgefragt werden, die in der Marktübersicht ausgewählt wurden. Andernfalls wird der Fehler ERR_MARKET_NOT_SELECTED (4302) zurückgegeben.

  • MQL5: Die Eigenschaft ACCOUNT_HEDGE_ALLOWED wurde zur Enumeration EENUM_ACCOUNT_INFO_INTEGER hinzugefügt — sie ermöglicht das Öffnen von entgegengesetzten Positionen und Pending Orders. Die Eigenschaft gilt nur für Hedging-Konten, um bestimmte regulatorische Anforderungen zu erfüllen, nach denen ein Konto keine entgegengesetzten Positionen für dasselbe Symbol haben kann, während Positionen in derselben Richtung zulässig sind.
    Wenn diese Option deaktiviert ist, dürfen Konten keine entgegengesetzten Positionen und Aufträge für dasselbe Finanzinstrument haben. Wenn das Konto beispielsweise eine Kaufposition hat, kann ein Nutzer keine Verkaufsposition eröffnen oder eine ausstehende Verkaufsorder platzieren. Wenn der Nutzer versucht, eine solche Operation durchzuführen, wird der Fehler TRADE_RETCODE_HEDGE_PROHIBITED zurückgegeben.

Fügen wir diese Eigenschaften den Symbol- und Bibliothekskontoobjekten hinzu.

In \MQL5\Include\DoEasy\Data.mqh wurden neuen Nachrichtenindizes hinzugefügt:

//+------------------------------------------------------------------+
//| List of the library's text message indices                       |
//+------------------------------------------------------------------+
enum ENUM_MESSAGES_LIB
  {
   MSG_LIB_PARAMS_LIST_BEG=ERR_USER_ERROR_FIRST,      // Beginning of the parameter list
   MSG_LIB_PARAMS_LIST_END,                           // End of the parameter list
   MSG_LIB_PROP_NOT_SUPPORTED,                        // Property not supported
   MSG_LIB_PROP_NOT_SUPPORTED_MQL4,                   // Property not supported in MQL4
   MSG_LIB_PROP_NOT_SUPPORTED_MT5_LESS_2155,          // Property not supported in MetaTrader 5 versions lower than 2155
   MSG_LIB_PROP_NOT_SUPPORTED_MT5_LESS_3245,          // Property not supported in MetaTrader 5 versions lower than 3245
   MSG_LIB_PROP_NOT_SUPPORTED_POSITION,               // Property not supported for position

...

  
   MSG_SYM_PROP_BACKGROUND_COLOR,                     // Background color of the symbol in Market Watch
   MSG_SYM_PROP_SUBSCRIPTION_DELAY,                   // Delay for quotes passed by symbol for instruments working on subscription basis
   //---

...

   MSG_ACC_PROP_FIFO_CLOSE,                           // Flag of a position closure by FIFO rule only
   MSG_ACC_PROP_HEDGE_ALLOWED,                        // Permission to open opposite positions and set pending orders
   //---
   MSG_ACC_PROP_BALANCE,                              // Account balance

...

   MSG_GRAPH_ELEMENT_TYPE_FORM,                       // Form
   MSG_GRAPH_ELEMENT_TYPE_WINDOW,                     // Window
   MSG_GRAPH_ELEMENT_TYPE_PANEL,                      // Panel control
   MSG_GRAPH_OBJ_BELONG_PROGRAM,                      // Graphical object belongs to a program
   MSG_GRAPH_OBJ_BELONG_NO_PROGRAM,                   // Graphical object does not belong to a program
//---

und die Textnachrichten, die den neu hinzugefügten Indizes entsprechen:

   {"Свойство не поддерживается в MetaTrader5 версии ниже 2155","The property is not supported in MetaTrader5, build lower than 2155"},
   {"Свойство не поддерживается в MetaTrader5 версии ниже 3245","The property is not supported in MetaTrader5, build lower than 3245"},
   {"Свойство не поддерживается у позиции","Property not supported for position"},

...

   {"Цвет фона символа в Market Watch","Background color of the symbol in Market Watch"},
   {"Размер задержки у котировок, передаваемых по символу, для инструментов, работающих по подписке","Delay size for quotes transmitted per symbol for instruments working by subscription"},
   {"Максимальный Bid за день","Maximum Bid of the day"},

...

   {"Тип торгового сервера","Type of trading server"},
   {"Признак закрытия позиций только по правилу FIFO","Sign of closing positions only according to the FIFO rule"},
   {"Разрешение на открытие встречных позиций и отложенных ордеров","Permission to open opposite positions and pending orders"},
   //---
   {"Баланс счета","Account balance"},

...

   {"Форма","Form"},
   {"Окно","Window"},
   {"Элемент управления \"Panel\"","Control element \"Panel\""},
   {"Графический объект принадлежит программе","The graphic object belongs to the program"},
   {"Графический объект не принадлежит программе","The graphic object does not belong to the program"},


Auf jedem im aktuellen Artikel erstellten Panel-Objekt sollen die Standardparameter für Textnachrichten angezeigt werden. Diese Parameter werden für jeden Text verwendet, der auf dem Panel oder seinen untergeordneten Objekten angezeigt wird oder an das Panel angehängt wird, wenn es als Container für diese Objekte angesehen wird. Wir müssen Standardwerte für den Namen, die Größe und die Farbe der Schriftart festlegen.

Wir öffnen \MQL5\Include\DoEasy\Defines.mqh und fügen die neuen Makroersetzungen für diese Texteigenschaften im Panel hinzu:

//--- Canvas parameters
#define PAUSE_FOR_CANV_UPDATE          (16)                       // Canvas update frequency
#define CLR_CANV_NULL                  (0x00FFFFFF)               // Zero for the canvas with the alpha channel
#define CLR_FORE_COLOR                 (C'0x2D,0x43,0x48')        // Default color for texts of objects on canvas
#define DEF_FONT                       ("Calibri")                // Default font
#define DEF_FONT_SIZE                  (8)                        // Default font size
#define OUTER_AREA_SIZE                (16)                       // Size of one side of the outer area around the form workspace
//--- Graphical object parameters

Der Liste der Bibliotheksobjekttypen fügen wir einen neuen Typ hinzu:

//+------------------------------------------------------------------+
//| List of library object types                                     |
//+------------------------------------------------------------------+
enum ENUM_OBJECT_DE_TYPE
  {
//--- Graphics
   OBJECT_DE_TYPE_GBASE =  COLLECTION_ID_LIST_END+1,              // "Base object of all library graphical objects" object type
   OBJECT_DE_TYPE_GELEMENT,                                       // "Graphical element" object type
   OBJECT_DE_TYPE_GFORM,                                          // Form object type
   OBJECT_DE_TYPE_GFORM_CONTROL,                                  // "Form for managing pivot points of graphical object" object type
   OBJECT_DE_TYPE_GSHADOW,                                        // Shadow object type
   //--- WinForms
   OBJECT_DE_TYPE_GWF_PANEL,                                      // WinForms Panel object type
//--- Animation
   OBJECT_DE_TYPE_GFRAME,                                         // "Single animation frame" object type
   OBJECT_DE_TYPE_GFRAME_TEXT,                                    // "Single text animation frame" object type
   OBJECT_DE_TYPE_GFRAME_QUAD,                                    // "Single rectangular animation frame" object type
   OBJECT_DE_TYPE_GFRAME_GEOMETRY,                                // "Single geometric animation frame" object type
   OBJECT_DE_TYPE_GANIMATIONS,                                    // "Animations" object type
//--- Managing graphical objects

In diesem Abschnitt (WinForms) werde ich neue Objekttypen hinzufügen, sobald sie erstellt werden.

Der Enumeration der ganzzahligen Eigenschaften des Kontos füge wir eine neue Eigenschaft hinzu und erhöhen die Anzahl der ganzzahligen Objekteigenschaften von 11 auf 12:

//+------------------------------------------------------------------+
//| Account integer properties                                       |
//+------------------------------------------------------------------+
enum ENUM_ACCOUNT_PROP_INTEGER
  {
   ...
   ACCOUNT_PROP_FIFO_CLOSE,                                 // Flag of a position closure by FIFO rule only
   ACCOUNT_PROP_HEDGE_ALLOWED                               // Permission to open opposite positions and set pending orders
  };
#define ACCOUNT_PROP_INTEGER_TOTAL    (12)                  // Total number of integer properties
#define ACCOUNT_PROP_INTEGER_SKIP     (0)                   // Number of integer account properties not used in sorting
//+------------------------------------------------------------------+

Die neue Eigenschaft fügen wir der Liste möglicher Kontensortierkriterien hinzu:

//+------------------------------------------------------------------+
//| Possible account sorting criteria                                |
//+------------------------------------------------------------------+
#define FIRST_ACC_DBL_PROP            (ACCOUNT_PROP_INTEGER_TOTAL-ACCOUNT_PROP_INTEGER_SKIP)
#define FIRST_ACC_STR_PROP            (ACCOUNT_PROP_INTEGER_TOTAL-ACCOUNT_PROP_INTEGER_SKIP+ACCOUNT_PROP_DOUBLE_TOTAL-ACCOUNT_PROP_DOUBLE_SKIP)
enum ENUM_SORT_ACCOUNT_MODE
  {
   ... 
   SORT_BY_ACCOUNT_FIFO_CLOSE,                              // Sort by the flag of a position closure by FIFO rule only
   SORT_BY_ACCOUNT_HEDGE_ALLOWED,                           // Sort by permission to open opposite positions and set pending orders
//--- Sort by real properties
   SORT_BY_ACCOUNT_BALANCE = FIRST_ACC_DBL_PROP,            // Sort by an account balance in the deposit currency
   SORT_BY_ACCOUNT_CREDIT,                                  // Sort by credit in a deposit currency
   ... 
   SORT_BY_ACCOUNT_COMPANY                                  // Sort by a name of a company serving an account
  };
//+------------------------------------------------------------------+


In der Enumeration der ganzzahligen Symboleigenschaften fügen wir eine neue Eigenschaft hinzu und erhöhen die Anzahl der ganzzahligen Eigenschaften von 40 auf 41:

//+------------------------------------------------------------------+
//| Symbol integer properties                                        |
//+------------------------------------------------------------------+
enum ENUM_SYMBOL_PROP_INTEGER
  {
   //--- ...
   SYMBOL_PROP_OPTION_MODE,                                 // Option type (from the ENUM_SYMBOL_OPTION_MODE enumeration)
   SYMBOL_PROP_OPTION_RIGHT,                                // Option right (Call/Put) (from the ENUM_SYMBOL_OPTION_RIGHT enumeration)
   SYMBOL_PROP_SUBSCRIPTION_DELAY,                          // Delay for quotes passed by symbol for instruments working on subscription basis
   //--- skipped property
   SYMBOL_PROP_BACKGROUND_COLOR                             // The color of the background used for the symbol in Market Watch
  }; 
#define SYMBOL_PROP_INTEGER_TOTAL    (41)                   // Total number of integer properties
#define SYMBOL_PROP_INTEGER_SKIP     (1)                    // Number of symbol integer properties not used in sorting
//+------------------------------------------------------------------+

Das Sortieren nach einer neuen Eigenschaft zur Enumeration möglicher Sortierkriterien für Symbole wird hinzugefügt:

//+------------------------------------------------------------------+
//| Possible symbol sorting criteria                                 |
//+------------------------------------------------------------------+
#define FIRST_SYM_DBL_PROP          (SYMBOL_PROP_INTEGER_TOTAL-SYMBOL_PROP_INTEGER_SKIP)
#define FIRST_SYM_STR_PROP          (SYMBOL_PROP_INTEGER_TOTAL-SYMBOL_PROP_INTEGER_SKIP+SYMBOL_PROP_DOUBLE_TOTAL-SYMBOL_PROP_DOUBLE_SKIP)
enum ENUM_SORT_SYMBOLS_MODE
  {
   ... 
   SORT_BY_SYMBOL_OPTION_MODE,                              // Sort by option type (from the ENUM_SYMBOL_OPTION_MODE enumeration)
   SORT_BY_SYMBOL_OPTION_RIGHT,                             // Sort by option right (Call/Put) (from the ENUM_SYMBOL_OPTION_RIGHT enumeration)
   SORT_BY_SYMBOL_SUBSCRIPTION_DELAY,                       // Sort by delay for quotes passed by symbol for instruments working on subscription basis


Der Liste der grafischen Elementtypen fügen wir einen neuen Elementtyp hinzu:

//+------------------------------------------------------------------+
//| The list of graphical element types                              |
//+------------------------------------------------------------------+
enum ENUM_GRAPH_ELEMENT_TYPE
  {
   GRAPH_ELEMENT_TYPE_STANDARD,                       // Standard graphical object
   GRAPH_ELEMENT_TYPE_STANDARD_EXTENDED,              // Extended standard graphical object
   GRAPH_ELEMENT_TYPE_ELEMENT,                        // Element
   GRAPH_ELEMENT_TYPE_SHADOW_OBJ,                     // Shadow object
   GRAPH_ELEMENT_TYPE_FORM,                           // Form
   GRAPH_ELEMENT_TYPE_WINDOW,                         // Window
   //--- WinForms
   GRAPH_ELEMENT_TYPE_PANEL,                          // Windows Forms Panel
  };
//+------------------------------------------------------------------+

Bei der Erstellung jedes nachfolgenden Steuerelements wird sein Typ in diesen Unterabschnitt der Enumeration (WinForms) eingetragen.

Wenn ein weiteres Objekt (größer als das Container-Panel) zum Panel-Objekt hinzugefügt wird und die automatische Änderung seiner Größe für das Panel erlaubt ist, gibt es zwei Optionen für die Größenänderung:

  1. Wir erhöhen nur die Panelgröße.
  2. Erhöhen und Verringern der Panelgröße.

Im ersten Fall werden die Plattenseiten, die das darin platzierte Objekt nicht enthalten, so vergrößert, dass das Objekt vollständig hineinpasst. Im zweiten Fall ermöglicht es zusätzlich zu der oben beschriebenen Aktion, die Seiten zu verringern, die größer sind als das darin platzierte Objekt.

Nach der Enumeration möglicher Sortierkriterien für grafische Objekte fügen wir eine neue Enumeration hinzu, die die Modi zum automatischen Ändern der Größe der Oberflächenelemente festlegt:

//+------------------------------------------------------------------+
//| Mode of automatic interface element resizing                     |
//+------------------------------------------------------------------+
enum ENUM_CANV_ELEMENT_AUTO_SIZE_MODE
  {
   CANV_ELEMENT_AUTO_SIZE_MODE_GROW,                  // Increase only
   CANV_ELEMENT_AUTO_SIZE_MODE_GROW_SHRINK,           // Increase and decrease
  };
//+------------------------------------------------------------------+

Beim Platzieren eines Objekts im Panel kann das Objekt an jeder Seite seines Containers angebracht werden – oben, unten, rechts und links. In diesem Fall "klebt" die nächste Seite an der entsprechenden Seite des Behälterobjekts und die Abmessungen des angebrachten Objekts werden zu den Behälterseiten gestreckt, die senkrecht zu der Seite sind, an der das Objekt angebracht ist. Wenn das Objekt beispielsweise an der Oberkante seines Containers befestigt ist, wird die Oberkante des Objekts an die Oberkante des Containers gezogen, während die linke und die rechte Seite des Objekts zu den entsprechenden Seiten des Containers gestreckt werden. Die Objekthöhe ändert sich nicht. Das Anbringen an anderen Seiten des Behälters funktioniert ähnlich.

Die neue Enumeration fügen wir direkt nach der Enumeration der Modi für die automatische Größenänderung hinzu:

//+------------------------------------------------------------------+
//| Control borders bound to the container                           |
//+------------------------------------------------------------------+
enum ENUM_CANV_ELEMENT_DOCK_MODE
  {
   CANV_ELEMENT_DOCK_MODE_TOP,                        // Attaching to the top and stretching along the container width
   CANV_ELEMENT_DOCK_MODE_BOTTOM,                     // Attaching to the bottom and stretching along the container width
   CANV_ELEMENT_DOCK_MODE_LEFT,                       // Attaching to the left and stretching along the container height
   CANV_ELEMENT_DOCK_MODE_RIGHT,                      // Attaching to the right and stretching along the container height
   CANV_ELEMENT_DOCK_MODE_FILL,                       // Stretching along the entire container width and height
   CANV_ELEMENT_DOCK_MODE_NONE,                       // Attached to the specified coordinates, size does not change
  };
//+------------------------------------------------------------------+

Abgesehen von den vier oben genannten Methoden, ein Objekt an einen Container anzuhängen, gibt es noch zwei weitere: Füllung (Objektgröße wird an die Containergröße angepasst) und keine Bindung (ein Objekt wird nur innerhalb seines Containers an bestimmte Koordinaten angehängt, während seine Größe erhalten bleibt unverändert).

Wenn ein Steuerelement die Funktion hat, mit einem Nutzer zu interagieren, kann ein solches Objekt unter bestimmten Bedingungen als für eine Interaktion unzugänglich angesehen werden (z. B. wenn eine Schaltfläche inaktiv ist). Wir fügen eine neue grafische Elementeigenschaft hinzu, um die Möglichkeit der Interaktion mit dem Element anzuzeigen.

In der Enumeration der ganzzahligen Eigenschaften von grafischen Elementen fügen wir eine neue Eigenschaft hinzu und erhöhen die Anzahl der ganzzahligen Objekteigenschaften von 24 auf 25:

//+------------------------------------------------------------------+
//| Integer properties of the graphical element on the canvas        |
//+------------------------------------------------------------------+
enum ENUM_CANV_ELEMENT_PROP_INTEGER
  {
   ... 
   CANV_ELEMENT_PROP_ZORDER,                          // Priority of a graphical object for receiving the event of clicking on a chart
   CANV_ELEMENT_PROP_ENABLED,                         // Element availability flag
  };
#define CANV_ELEMENT_PROP_INTEGER_TOTAL (25)          // Total number of integer properties
#define CANV_ELEMENT_PROP_INTEGER_SKIP  (0)           // Number of integer properties not used in sorting
//+------------------------------------------------------------------+


Wir sortieren nach neuer Eigenschaft zur Enumeration möglicher Kriterien zum Sortieren von grafischen Elementen auf der Leinwand hinzufügen:

//+------------------------------------------------------------------+
//| Possible sorting criteria of graphical elements on the canvas    |
//+------------------------------------------------------------------+
#define FIRST_CANV_ELEMENT_DBL_PROP  (CANV_ELEMENT_PROP_INTEGER_TOTAL-CANV_ELEMENT_PROP_INTEGER_SKIP)
#define FIRST_CANV_ELEMENT_STR_PROP  (CANV_ELEMENT_PROP_INTEGER_TOTAL-CANV_ELEMENT_PROP_INTEGER_SKIP+CANV_ELEMENT_PROP_DOUBLE_TOTAL-CANV_ELEMENT_PROP_DOUBLE_SKIP)
enum ENUM_SORT_CANV_ELEMENT_MODE
  {
   ... 
   SORT_BY_CANV_ELEMENT_ZORDER,                       // Sort by the priority of a graphical object for receiving the event of clicking on a chart
   SORT_BY_CANV_ELEMENT_ENABLED,                      // Sort by the element availability flag
   ... 
  };
//+------------------------------------------------------------------+


Da wir jetzt die neuen Symbol- und Kontoeigenschaften haben, müssen wir die Objektklassen verbessern.

Wir öffnen \MQL5\Include\DoEasy\Objects\Accounts\Account.mqh und nehmen Verbesserungen in der Kontoobjektklasse vor.

Der Objekteigenschaftsstruktur fügen wir eine neue ganzzahlige Eigenschaft hinzu:

//+------------------------------------------------------------------+
//| Account class                                                    |
//+------------------------------------------------------------------+
class CAccount : public CBaseObjExt
  {
private:
   struct SData
     {
      //--- Account integer properties
      ... 
      bool           fifo_close;                   // ACCOUNT_FIFO_CLOSE (The flag indicating that positions can be closed only by the FIFO rule)
      bool           hedge_allowed;                // ACCOUNT_HEDGE_ALLOWED (Permission to open opposite positions and set pending orders)
      ... 
     };
   SData             m_struct_obj;                                      // Account object structure
   uchar             m_uchar_array[];                                   // uchar array of the account object structure
   
//--- Object properties


Im Methodenblock fügen wir eine neue Methode für einen vereinfachten Zugriff auf Kontoobjekteigenschaften hinzu:

//+------------------------------------------------------------------+
//| Methods of a simplified access to the account object properties  |
//+------------------------------------------------------------------+
//--- Return the account's integer properties
   ... 
   bool              FIFOClose(void)                                 const { return (bool)this.GetProperty(ACCOUNT_PROP_FIFO_CLOSE);                              }
   bool              IsHedge(void)                                   const { return this.MarginMode()==ACCOUNT_MARGIN_MODE_RETAIL_HEDGING;                        }
   bool              HedgeAllowed(void)                              const { return (bool)this.GetProperty(ACCOUNT_PROP_HEDGE_ALLOWED);                           }
   ... 

Die Methode gibt einfach den im Objekteigenschaftsarray gespeicherten Wert zurück.

Den Wert fügen wir dem Array von Objekteigenschaften im Klassenkonstruktor hinzu:

//+------------------------------------------------------------------+
//| Constructor                                                      |
//+------------------------------------------------------------------+
CAccount::CAccount(void)
  {
   this.m_type=OBJECT_DE_TYPE_ACCOUNT;
//--- Initialize control data
   this.SetControlDataArraySizeLong(ACCOUNT_PROP_INTEGER_TOTAL);
   this.SetControlDataArraySizeDouble(ACCOUNT_PROP_DOUBLE_TOTAL);
   this.ResetChangesParams();
   this.ResetControlsParams();
   ... 
   this.m_long_prop[ACCOUNT_PROP_SERVER_TYPE]                        = (::TerminalInfoString(TERMINAL_NAME)=="MetaTrader 5" ? 5 : 4);
   this.m_long_prop[ACCOUNT_PROP_FIFO_CLOSE]                         = (#ifdef __MQL5__::TerminalInfoInteger(TERMINAL_BUILD)<2155 ? false : ::AccountInfoInteger(ACCOUNT_FIFO_CLOSE) #else false #endif );
   this.m_long_prop[ACCOUNT_PROP_HEDGE_ALLOWED]                      = (#ifdef __MQL5__::TerminalInfoInteger(TERMINAL_BUILD)<3245 ? false : ::AccountInfoInteger(ACCOUNT_HEDGE_ALLOWED) #else false #endif );  
   ... 
//--- Update the base object data and search for changes
   CBaseObjExt::Refresh();
  }
//+-------------------------------------------------------------------+

Wenn die MQL5-Version unter 3245 liegt, gibt es diese Eigenschaft noch nicht. Ist sie auf false gesetzt. Wenn die Terminalversion 3245 oder höher ist, rufen wir den Wert aus der neuen Kontoeigenschaft ab und tragen ihn inf das Array der Ganzzahleigenschaften des Objekts ein. Im Falle von MQL4 ist sie immer false, da es eine solche Eigenschaft sowie viele andere Eigenschaften nicht gibt.

In der Methode zum Aktualisieren aller Kontodaten setzen wir den Wert auf die neue Objekteigenschaft auf genau die gleiche Weise:

//+------------------------------------------------------------------+
//| Update all account data                                          |
//+------------------------------------------------------------------+
void CAccount::Refresh(void)
  {
//--- Initialize event data
   this.m_is_event=false;
   this.m_hash_sum=0;
   ... 
   this.m_long_prop[ACCOUNT_PROP_FIFO_CLOSE]                            = (#ifdef __MQL5__::TerminalInfoInteger(TERMINAL_BUILD)<2155 ? false : ::AccountInfoInteger(ACCOUNT_FIFO_CLOSE) #else false #endif );
   this.m_long_prop[ACCOUNT_PROP_HEDGE_ALLOWED]                         = (#ifdef __MQL5__::TerminalInfoInteger(TERMINAL_BUILD)<3245 ? false : ::AccountInfoInteger(ACCOUNT_HEDGE_ALLOWED) #else false #endif );
   ... 
   CBaseObjExt::Refresh();
   this.CheckEvents();
  }
//+------------------------------------------------------------------+


In der Methode fügen wir zum Erstellen der Kontoobjektstruktur Eingabedaten zu den beiden Feldern der Struktur hinzu:

//+------------------------------------------------------------------+
//| Create the account object structure                              |
//+------------------------------------------------------------------+
bool CAccount::ObjectToStruct(void)
  {
//--- Save integer properties
   ... 
   this.m_struct_obj.server_type=(int)this.ServerType();
   this.m_struct_obj.fifo_close=this.FIFOClose();
   this.m_struct_obj.hedge_allowed=this.HedgeAllowed();
   ... 
   //--- Save the structure to the uchar array
   ::ResetLastError();
   if(!::StructToCharArray(this.m_struct_obj,this.m_uchar_array))
     {
      ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_FAILED_SAVE_OBJ_STRUCT_TO_UARRAY),(string)::GetLastError());
      return false;
     }
   return true;
  }
//+------------------------------------------------------------------+

Hier fügen wir den Integer-Feldern der Objektstruktur eine neue Eigenschaft hinzu und setzen die der Version 2155 hinzugefügte FIFOClose-Kontoeigenschaft.

In der Methode zum Erstellen eines Kontoobjekts aus der Struktur fügen wir das Festlegen des Werts aus dem Strukturfeld zur Objekteigenschaft für eine neue Eigenschaft hinzu:

//+------------------------------------------------------------------+
//| Create the account object from the structure                     |
//+------------------------------------------------------------------+
void CAccount::StructToObject(void)
  {
//--- Save integer properties
   ... 
   this.m_long_prop[ACCOUNT_PROP_FIFO_CLOSE]                         = this.m_struct_obj.fifo_close;
   this.m_long_prop[ACCOUNT_PROP_HEDGE_ALLOWED]                      = this.m_struct_obj.hedge_allowed;
   ... 
  }
//+------------------------------------------------------------------+


In der Methode, die die Beschreibung der ganzzahligen Eigenschaft des Kontos zurückgibt, fügen wir den Codeblock zum Anzeigen der neuen Eigenschaftsbeschreibung hinzu:

//+------------------------------------------------------------------+
//| Return the description of the account integer property           |
//+------------------------------------------------------------------+
string CAccount::GetPropertyDescription(ENUM_ACCOUNT_PROP_INTEGER property)
  {
   return
     (
      ... 
      property==ACCOUNT_PROP_FIFO_CLOSE      ?  CMessage::Text(MSG_ACC_PROP_FIFO_CLOSE)+": "+
                                                   (this.GetProperty(property) ? CMessage::Text(MSG_LIB_TEXT_YES) : CMessage::Text(MSG_LIB_TEXT_NO))  :
      property==ACCOUNT_PROP_HEDGE_ALLOWED   ?  CMessage::Text(MSG_ACC_PROP_HEDGE_ALLOWED)+": "+
                                                   (this.GetProperty(property) ? CMessage::Text(MSG_LIB_TEXT_YES) : CMessage::Text(MSG_LIB_TEXT_NO))  :
      ""
     );
  }
//+------------------------------------------------------------------+


Ähnliche Verbesserungen nehmen wir in der Symbolobjektdatei in \MQL5\Include\DoEasy\Objects\Symbols\Symbol.mqh vor.

Im geschützten Abschnitt der Klasse deklarieren wir die Methode, die den Wert einer neuen Symboleigenschaft zurückgibt:

protected:
//--- Protected parametric constructor
                     CSymbol(ENUM_SYMBOL_STATUS symbol_status,const string name,const int index);

//--- Get and return integer properties of a selected symbol from its parameters
   ... 
   long              SymbolCalcMode(void)                const;
   long              SymbolSwapMode(void)                const;
   long              SymbolSubscriptionDelay(void)       const;
   long              SymbolDigitsLot(void);
   int               SymbolDigitsBySwap(void);
   ... 
//--- Search for a symbol and return the flag indicating its presence on the server
   bool              Exist(void)                         const;

public:


Im öffentlichen Abschnitt des Methodenblocks legen wir für einen vereinfachten Zugriff auf Symbolobjekteigenschaften die Methode fest, die den neuen Eigenschaftswert zurückgibt:

//+------------------------------------------------------------------+
//| Methods for a simplified access to symbol object properties      |
//+------------------------------------------------------------------+
//--- Integer properties
   ... 
   ENUM_SYMBOL_OPTION_MODE OptionMode(void)                       const { return (ENUM_SYMBOL_OPTION_MODE)this.GetProperty(SYMBOL_PROP_OPTION_MODE);        }
   ENUM_SYMBOL_OPTION_RIGHT OptionRight(void)                     const { return (ENUM_SYMBOL_OPTION_RIGHT)this.GetProperty(SYMBOL_PROP_OPTION_RIGHT);      }
   long              SubscriptionDelay(void)                      const { return this.GetProperty(SYMBOL_PROP_SUBSCRIPTION_DELAY);                          }
//--- Real properties

Hier verwenden wir einfach die Methode GetProperty(), um den Wert zurückzugeben, der im Array der Integer-Eigenschaften des Symbolobjekts festgelegt ist.

Im geschlossenen parametrischen Konstruktor legen wir eine neue Eigenschaft für das Array der Ganzzahleigenschaften des Objekts fest:

//+------------------------------------------------------------------+
//| Closed parametric constructor                                    |
//+------------------------------------------------------------------+
CSymbol::CSymbol(ENUM_SYMBOL_STATUS symbol_status,const string name,const int index)
  {
//--- Save integer properties
   ... 
   this.m_long_prop[SYMBOL_PROP_BOOKDEPTH_STATE]                                    = this.m_book_subscribed;
   this.m_long_prop[SYMBOL_PROP_SUBSCRIPTION_DELAY]                                 = this.SymbolSubscriptionDelay();
   ... 
//--- Initializing default values of a trading object
   this.m_trade.Init(this.Name(),0,this.LotsMin(),5,0,0,false,this.GetCorrectTypeFilling(),this.GetCorrectTypeExpiration(),LOG_LEVEL_ERROR_MSG);
  }
//+------------------------------------------------------------------+


Die Methode gibt die Verzögerung der Kurse der abonnementbasierten Symbolen zurück:

//+------------------------------------------------------------------+
//| Return the delay size for quotes passed by symbol                |
//| in case of subscription-based symbols                            |
//+------------------------------------------------------------------+
long CSymbol::SymbolSubscriptionDelay(void) const
  {
   return
     (
      #ifdef __MQL5__ (::TerminalInfoInteger(TERMINAL_BUILD)>=3245 ? ::SymbolInfoInteger(this.m_name,SYMBOL_SUBSCRIPTION_DELAY) : 0)
      #else 0
      #endif 
     );
  }
//+------------------------------------------------------------------+

Hier, im Falle von MQL5, wenn die Terminalversion 3245 oder höher ist, wird der Wert einer neuenSymboleigenschaft zurückgegeben, andernfalls — Null.
Im Fall von MQL4 wird immer Null zurückgeben
. Es hat dort keine solche Eigenschaft.

In der Methode, die die Beschreibung einer Symbol-Integer-Eigenschaft zurückgibt, fügen wir einen Codeblock hinzu, der eine neue Eigenschaftsbeschreibung zurückgibt:

//+------------------------------------------------------------------+
//| Return the description of the symbol integer property            |
//+------------------------------------------------------------------+
string CSymbol::GetPropertyDescription(ENUM_SYMBOL_PROP_INTEGER property)
  {
   return
     (
      ... 
      property==SYMBOL_PROP_BACKGROUND_COLOR    ?  CMessage::Text(MSG_SYM_PROP_BACKGROUND_COLOR)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
         #ifdef __MQL5__
         (this.GetProperty(property)==CLR_MW_DEFAULT || this.GetProperty(property)==CLR_NONE ?  ": ("+CMessage::Text(MSG_LIB_PROP_EMPTY)+")" : ": "+::ColorToString((color)this.GetProperty(property),true))
         #else ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED_MQL4) #endif 
         )  :
      property==SYMBOL_PROP_SUBSCRIPTION_DELAY  ?  CMessage::Text(MSG_SYM_PROP_SUBSCRIPTION_DELAY)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
         #ifdef __MQL5__
         (::TerminalInfoInteger(TERMINAL_BUILD)<3245 ?  ": ("+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED_MT5_LESS_3245)+")" : ": "+(string)this.GetProperty(property))
         #else ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED_MQL4) #endif 
         )  :
      ""
     );
  }
//+------------------------------------------------------------------+


Für Farbschemata von GUI-Elementen in \MQL5\Include\DoEasy\GraphINI.mqh, fügen wir denFarbwert von Text hinzu, erhöhen die Anzahl der Parameter im Farbschema von 4 auf 5 und fügen die Textfarbwerte zu den Arrays der Farbschemen hinzu:

//+------------------------------------------------------------------+
//| List of indices of color scheme parameters                       |
//+------------------------------------------------------------------+
enum ENUM_COLOR_THEME_COLORS
  {
   COLOR_THEME_COLOR_FORM_BG,                   // Form background color
   COLOR_THEME_COLOR_FORM_FRAME,                // Form frame color
   COLOR_THEME_COLOR_FORM_RECT_OUTER,           // Form outline rectangle color
   COLOR_THEME_COLOR_FORM_SHADOW,               // Form shadow color
   COLOR_THEME_COLOR_FORM_TEXT,                 // Form text color
  };
#define TOTAL_COLOR_THEME_COLORS       (5)      // Number of parameters in the color theme
//+------------------------------------------------------------------+
//| The array containing color schemes                               |
//+------------------------------------------------------------------+
color array_color_themes[TOTAL_COLOR_THEMES][TOTAL_COLOR_THEME_COLORS]=
  {
//--- Parameters of the "Blue steel" color scheme
   {
      C'134,160,181',                           // Form background color
      C'134,160,181',                           // Form frame color
      clrDimGray,                               // Form outline rectangle color
      clrGray,                                  // Form shadow color
      C'0x3E,0x3E,0x3E',                        // Form text color
   },
//--- Parameters of the "Light cyan gray" color scheme
   {
      C'181,196,196',                           // Form background color
      C'181,196,196',                           // Form frame color
      clrGray,                                  // Form outline rectangle color
      clrGray,                                  // Form shadow color
      C'0x3E,0x3E,0x3E',                        // Form text color
   },
  };
//+------------------------------------------------------------------+


In der Enumeration der Rahmenstile fügen wir das Feld hinzu, das das Fehlen des Rahmens angibt:

//+------------------------------------------------------------------+
//| Frame styles                                                     |
//+------------------------------------------------------------------+
enum ENUM_FRAME_STYLE
  {
   FRAME_STYLE_NONE,                            // No frame
   FRAME_STYLE_SIMPLE,                          // Simple frame
   FRAME_STYLE_FLAT,                            // Flat frame
   FRAME_STYLE_BEVEL,                           // Embossed (convex)
   FRAME_STYLE_STAMP,                           // Embossed (concave)
  };
//+------------------------------------------------------------------+


Wir verbessern die Klasse des Basisobjekts aller grafischen Bibliotheksobjekte in \MQL5\Include\DoEasy\Objects\Graph\GBaseObj.mqh.

Wir müssen in nutzerdefinierten Programmen 0 oder NULL setzen können, wenn wir die aktuelle Diagramm-ID angeben, und, anstatt einen numerischen ID-Wert anzugeben oder die Funktion ChartID() zu übergeben, den an die Methode SetChartID() übergeben Wert überprüfen:

public:
//--- Return the prefix name
   string            NamePrefix(void)                    const { return this.m_name_prefix;           }
//--- Set the values of the class variables
   void              SetObjectID(const long value)             { this.m_object_id=value;              }
   void              SetBelong(const ENUM_GRAPH_OBJ_BELONG belong){ this.m_belong=belong;             }
   void              SetTypeGraphObject(const ENUM_OBJECT obj) { this.m_type_graph_obj=obj;           }
   void              SetTypeElement(const ENUM_GRAPH_ELEMENT_TYPE type) { this.m_type_element=type;   }
   void              SetSpecies(const ENUM_GRAPH_OBJ_SPECIES species){ this.m_species=species;        }
   void              SetGroup(const int group)                 { this.m_group=group;                  }
   void              SetName(const string name)                { this.m_name=name;                    }
   void              SetDigits(const int value)                { this.m_digits=value;                 }
   void              SetChartID(const long chart_id)
                       { this.m_chart_id=(chart_id==NULL || chart_id==0 ? ::ChartID() : chart_id);    }
   
//--- Set the "Background object" flag

Hier prüfen wir, welcher Wert an die Methode übergeben wurde. Wenn es 0 oder NULList, weisen wir der Variablen die aktuelle Chart-ID zu. Andernfalls weisen wir den an die Methode übergebenen Wert zu.

In der Methode, die die Typbeschreibung des grafischen Elements zurückgibt, fügen wir die Rückgabe der Panel-Objektbeschreibung hinzu:

//+------------------------------------------------------------------+
//| Return the description of the graphical element type             |
//+------------------------------------------------------------------+
string CGBaseObj::TypeElementDescription(void)
  {
   return
     (
      this.TypeGraphElement()==GRAPH_ELEMENT_TYPE_STANDARD           ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_STANDARD)           :
      this.TypeGraphElement()==GRAPH_ELEMENT_TYPE_STANDARD_EXTENDED  ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_STANDARD_EXTENDED)  :
      this.TypeGraphElement()==GRAPH_ELEMENT_TYPE_ELEMENT            ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_ELEMENT)            :
      this.TypeGraphElement()==GRAPH_ELEMENT_TYPE_SHADOW_OBJ         ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_SHADOW_OBJ)         :
      this.TypeGraphElement()==GRAPH_ELEMENT_TYPE_FORM               ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_FORM)               :
      this.TypeGraphElement()==GRAPH_ELEMENT_TYPE_WINDOW             ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WINDOW)             :
      //---
      this.TypeGraphElement()==GRAPH_ELEMENT_TYPE_PANEL              ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_PANEL)              :
      "Unknown"
     );
  }
//+------------------------------------------------------------------+


In der Klasse des grafischen Elementobjekts in \MQL5\Include\DoEasy\Objects\Graph\GCnvElement.mqh, nämlich in der Objektstruktur, fügen wir ein neues Feld für die EigenschaftElementverfügbarkeit hinzu:

//+------------------------------------------------------------------+
//| Class of the graphical element object                            |
//+------------------------------------------------------------------+
class CGCnvElement : public CGBaseObj
  {
protected:
   CCanvas           m_canvas;                                 // CCanvas class object
   CPause            m_pause;                                  // Pause class object
   bool              m_shadow;                                 // Shadow presence
   color             m_chart_color_bg;                         // Chart background color
   uint              m_duplicate_res[];                        // Array for storing resource data copy

//--- Create (1) the object structure and (2) the object from the structure
   virtual bool      ObjectToStruct(void);
   virtual void      StructToObject(void);
   
private:
   struct SData
     {
      //--- Object integer properties
      ... 
      int            coord_act_bottom;                         // Bottom border of the element active area
      long           zorder;                                   // Priority of a graphical object for receiving the event of clicking on a chart
      bool           enabled;                                  // Element availability flag
      //--- Object real properties

      //--- Object string properties
      uchar          name_obj[64];                             // Graphical element object name
      uchar          name_res[64];                             // Graphical resource name
     };
   SData             m_struct_obj;                             // Object structure
   uchar             m_uchar_array[];                          // uchar array of the object structure


Neue Methoden zum Setzen und Zurückgeben des Elementverfügbarkeitswerts fügen wir im Methodenblock hinzu, um den Zugriff auf die Objekteigenschaften zu vereinfachen:

//+------------------------------------------------------------------+
//| Methods of simplified access to object properties                |
//+------------------------------------------------------------------+
   ... 
//--- Set (1) object movability, (2) activity, (3) interaction,
//--- (4) element ID, (5) element index in the list, (6) availability and (7) shadow flag
   void              SetMovable(const bool flag)               { this.SetProperty(CANV_ELEMENT_PROP_MOVABLE,flag);                     }
   void              SetActive(const bool flag)                { this.SetProperty(CANV_ELEMENT_PROP_ACTIVE,flag);                      }
   void              SetInteraction(const bool flag)           { this.SetProperty(CANV_ELEMENT_PROP_INTERACTION,flag);                 }
   void              SetID(const int id)                       { this.SetProperty(CANV_ELEMENT_PROP_ID,id);                            }
   void              SetNumber(const int number)               { this.SetProperty(CANV_ELEMENT_PROP_NUM,number);                       }
   void              SetEnabled(const bool flag)               { this.SetProperty(CANV_ELEMENT_PROP_ENABLED,flag);                     }
   void              SetShadow(const bool flag)                { this.m_shadow=flag;                                                   } 
   ... 
//--- Return the (1) element movability, (2) activity, (3) interaction and (4) availability flag
   bool              Movable(void)                       const { return (bool)this.GetProperty(CANV_ELEMENT_PROP_MOVABLE);             }
   bool              Active(void)                        const { return (bool)this.GetProperty(CANV_ELEMENT_PROP_ACTIVE);              }
   bool              Interaction(void)                   const { return (bool)this.GetProperty(CANV_ELEMENT_PROP_INTERACTION);         }
   bool              Enabled(void)                       const { return (bool)this.GetProperty(CANV_ELEMENT_PROP_ENABLED);             }
//--- Return (1) the object name, (2) the graphical resource name, (3) the chart ID and (4) the chart subwindow index



In einer der Löschmethoden der Leinwand (cancas) entfernen wir die Standard-Flag-Werte:

//+------------------------------------------------------------------+
//| The methods of filling, clearing and updating raster data        |
//+------------------------------------------------------------------+
//--- Clear the element filling it with color and opacity
   void              Erase(const color colour,const uchar opacity,const bool redraw=false);
//--- Clear the element with a gradient fill
   void              Erase(color &colors[],const uchar opacity,const bool vgradient,const bool cycle,const bool redraw=false);
//--- Clear the element completely
   void              Erase(const bool redraw=false);
//--- Update the element
   void              Update(const bool redraw=false)           { this.m_canvas.Update(redraw);                                         }

Vorher sah die Methode so aus:

void              Erase(color &colors[],const uchar opacity,const bool vgradient=true,const bool cycle=false,const bool redraw=false);

Außerdem war es unmöglich, sie zu verwenden, da der Compiler keine korrekte überladene Methode auswählen konnte.

Im parametrischen Konstruktor füge wir die Überprüfung eines übergebenen Wertes der Chart-ID und das Setzen des Elementverfügbarkeits-Flags sowie Standardschriftwerte hinzu:

//+------------------------------------------------------------------+
//| Parametric constructor                                           |
//+------------------------------------------------------------------+
CGCnvElement::CGCnvElement(const ENUM_GRAPH_ELEMENT_TYPE element_type,
                           const int      element_id,
                           const int      element_num,
                           const long     chart_id,
                           const int      wnd_num,
                           const string   name,
                           const int      x,
                           const int      y,
                           const int      w,
                           const int      h,
                           const color    colour,
                           const uchar    opacity,
                           const bool     movable=true,
                           const bool     activity=true,
                           const bool     redraw=false) : m_shadow(false)
  {
   this.m_type=OBJECT_DE_TYPE_GELEMENT; 
   this.m_chart_color_bg=(color)::ChartGetInteger((chart_id==NULL ? ::ChartID() : chart_id),CHART_COLOR_BACKGROUND);
   this.m_name=(::StringFind(name,this.m_name_prefix)<0 ? this.m_name_prefix : "")+name;
   this.m_chart_id=(chart_id==NULL || chart_id==0 ? ::ChartID() : chart_id);
   this.m_subwindow=wnd_num;
   this.m_type_element=element_type;
   this.SetFont(DEF_FONT,DEF_FONT_SIZE);
   this.m_text_anchor=0;
   this.m_text_x=0;
   this.m_text_y=0;
   this.m_color_bg=colour;
   this.m_opacity=opacity;
   if(this.Create(chart_id,wnd_num,this.m_name,x,y,w,h,colour,opacity,redraw))
     {
      ... 
      this.SetProperty(CANV_ELEMENT_PROP_ACTIVE,activity);                       // Element activity flag
      this.SetProperty(CANV_ELEMENT_PROP_INTERACTION,false);                     // Flag of interaction with the outside environment
      this.SetProperty(CANV_ELEMENT_PROP_ENABLED,true);                          // Element availability flag
      this.SetProperty(CANV_ELEMENT_PROP_RIGHT,this.RightEdge());                // Element right border
   ... 
  }
//+------------------------------------------------------------------+

Dasselbe machen wir im geschützten Konstruktor:

//+------------------------------------------------------------------+
//| Protected constructor                                            |
//+------------------------------------------------------------------+
CGCnvElement::CGCnvElement(const ENUM_GRAPH_ELEMENT_TYPE element_type,
                           const long    chart_id,
                           const int     wnd_num,
                           const string  name,
                           const int     x,
                           const int     y,
                           const int     w,
                           const int     h) : m_shadow(false)
  {
   this.m_type=OBJECT_DE_TYPE_GELEMENT; 
   this.m_chart_color_bg=(color)::ChartGetInteger((chart_id==NULL ? ::ChartID() : chart_id),CHART_COLOR_BACKGROUND);
   this.m_name=(::StringFind(name,this.m_name_prefix)<0 ? this.m_name_prefix : "")+name;
   this.m_chart_id=(chart_id==NULL || chart_id==0 ? ::ChartID() : chart_id);
   this.m_subwindow=wnd_num;
   this.m_type_element=element_type;
   this.SetFont(DEF_FONT,DEF_FONT_SIZE);
   this.m_text_anchor=0;
   this.m_text_x=0;
   this.m_text_y=0;
   this.m_color_bg=CLR_CANV_NULL;
   this.m_opacity=0;
   if(this.Create(chart_id,wnd_num,this.m_name,x,y,w,h,this.m_color_bg,this.m_opacity,false))
     {
      ... 
      this.SetProperty(CANV_ELEMENT_PROP_ACTIVE,false);                          // Element activity flag
      this.SetProperty(CANV_ELEMENT_PROP_INTERACTION,false);                     // Flag of interaction with the outside environment
      this.SetProperty(CANV_ELEMENT_PROP_ENABLED,true);                          // Element availability flag
      this.SetProperty(CANV_ELEMENT_PROP_RIGHT,this.RightEdge());                // Element right border
      ... 

  }
//+------------------------------------------------------------------+


In der Methode zum Erstellen einer Objektstruktur fügen wir das Füllen eines neuen Strukturfelds mit dem neuen Elementverfügbarkeits-Flag hinzu:

//+------------------------------------------------------------------+
//| Create the object structure                                      |
//+------------------------------------------------------------------+
bool CGCnvElement::ObjectToStruct(void)
  {
//--- Save integer properties
   ... 
   this.m_struct_obj.active=(bool)this.GetProperty(CANV_ELEMENT_PROP_ACTIVE);                   // Element activity flag
   this.m_struct_obj.interaction=(bool)this.GetProperty(CANV_ELEMENT_PROP_INTERACTION);         // Flag of interaction with the outside environment
   this.m_struct_obj.enabled=(bool)this.GetProperty(CANV_ELEMENT_PROP_ENABLED);                 // Element availability flag
   this.m_struct_obj.coord_act_x=(int)this.GetProperty(CANV_ELEMENT_PROP_COORD_ACT_X);          // X coordinate of the element active area
   this.m_struct_obj.coord_act_y=(int)this.GetProperty(CANV_ELEMENT_PROP_COORD_ACT_Y);          // Y coordinate of the element active area
   ... 
   return true;
  }
//+------------------------------------------------------------------+

 

In der Methode zum Erstellen eines Objekts aus der Struktur fügen wir den Eintrag zur Eigenschaft Wertobjektverfügbarkeit aus dem entsprechenden Strukturfeld hinzu:

//+------------------------------------------------------------------+
//| Create the object from the structure                             |
//+------------------------------------------------------------------+
void CGCnvElement::StructToObject(void)
  {
//--- Save integer properties
   ... 
   this.SetProperty(CANV_ELEMENT_PROP_ACTIVE,this.m_struct_obj.active);                         // Element activity flag
   this.SetProperty(CANV_ELEMENT_PROP_INTERACTION,this.m_struct_obj.interaction);               // Flag of interaction with the outside environment
   this.SetProperty(CANV_ELEMENT_PROP_ENABLED,this.m_struct_obj.enabled);                       // Element availability flag
   this.SetProperty(CANV_ELEMENT_PROP_COORD_ACT_X,this.m_struct_obj.coord_act_x);               // X coordinate of the element active area
   this.SetProperty(CANV_ELEMENT_PROP_COORD_ACT_Y,this.m_struct_obj.coord_act_y);               // Y coordinate of the element active area
   this.SetProperty(CANV_ELEMENT_PROP_ACT_RIGHT,this.m_struct_obj.coord_act_right);             // Right border of the element active area
   this.SetProperty(CANV_ELEMENT_PROP_ACT_BOTTOM,this.m_struct_obj.coord_act_bottom);           // Bottom border of the element active area
   this.m_color_bg=this.m_struct_obj.color_bg;                                                  // Element background color
   this.m_opacity=this.m_struct_obj.opacity;                                                    // Element opacity
   this.m_zorder=this.m_struct_obj.zorder;                                                      // Priority of a graphical object for receiving the on-chart mouse click event
   ... 
//--- Save string properties
   this.SetProperty(CANV_ELEMENT_PROP_NAME_OBJ,::CharArrayToString(this.m_struct_obj.name_obj));// Graphical element object name
   this.SetProperty(CANV_ELEMENT_PROP_NAME_RES,::CharArrayToString(this.m_struct_obj.name_res));// Graphical resource name
  }
//+------------------------------------------------------------------+


In der Methode zum Erstellen eines grafischen Elementobjekts fügen wir auch die Überprüfung der übergebenen Chart-ID hinzu :

//+------------------------------------------------------------------+
//| Create the graphical element object                              |
//+------------------------------------------------------------------+
bool CGCnvElement::Create(const long chart_id,     // Chart ID
                          const int wnd_num,       // Chart subwindow
                          const string name,       // Element name
                          const int x,             // X coordinate
                          const int y,             // Y coordinate
                          const int w,             // Width
                          const int h,             // Height
                          const color colour,      // Background color
                          const uchar opacity,     // Opacity
                          const bool redraw=false) // Flag indicating the need to redraw
                         
  {
   ::ResetLastError();
   if(this.m_canvas.CreateBitmapLabel((chart_id==NULL ? ::ChartID() : chart_id),wnd_num,name,x,y,w,h,COLOR_FORMAT_ARGB_NORMALIZE))
     {
      this.Erase(CLR_CANV_NULL);
      this.m_canvas.Update(redraw);
      this.m_shift_y=(int)::ChartGetInteger((chart_id==NULL ? ::ChartID() : chart_id),CHART_WINDOW_YDISTANCE,wnd_num);
      return true;
     }
   CMessage::ToLog(DFUN,::GetLastError(),true);
   return false;
  }
//+------------------------------------------------------------------+


Nun soll auch die Formularobjektklasse in \MQL5\Include\DoEasy\Objects\Graph\Form.mqh verbessert werden.

Initialisierungsmethode für private Variablen

//+------------------------------------------------------------------+
//| Form object class                                                |
//+------------------------------------------------------------------+
class CForm : public CGCnvElement
  {
private:
   CArrayObj         m_list_elements;                          // List of attached elements
   CAnimations      *m_animations;                             // Pointer to the animation object
   CShadowObj       *m_shadow_obj;                             // Pointer to the shadow object
   CMouseState       m_mouse;                                  // "Mouse status" class object
   ENUM_MOUSE_FORM_STATE m_mouse_form_state;                   // Mouse status relative to the form
   ushort            m_mouse_state_flags;                      // Mouse status flags
   color             m_color_frame;                            // Form frame color
   int               m_frame_width_left;                       // Form frame width to the left
   int               m_frame_width_right;                      // Form frame width to the right
   int               m_frame_width_top;                        // Form frame width at the top
   int               m_frame_width_bottom;                     // Form frame width at the bottom
   int               m_offset_x;                               // Offset of the X coordinate relative to the cursor
   int               m_offset_y;                               // Offset of the Y coordinate relative to the cursor
   
//--- Initialize the variables
   void              Initialize(void);
//--- Reset the array size of (1) text, (2) rectangular and (3) geometric animation frames
   void              ResetArrayFrameT(void);
   void              ResetArrayFrameQ(void);
   void              ResetArrayFrameG(void);

Wir wechseln in den geschützten Klassenabschnitt, da die Methode in untergeordneten Objekten benötigt wird, und deklarieren eine neue Methode zum Deinitialisieren des Klassenobjekts:

//--- Reset the array size of (1) text, (2) rectangular and (3) geometric animation frames
   void              ResetArrayFrameT(void);
   void              ResetArrayFrameQ(void);
   void              ResetArrayFrameG(void);
   
//--- Return the name of the dependent object
   string            CreateNameDependentObject(const string base_name)  const
                       { return ::StringSubstr(this.NameObj(),::StringLen(::MQLInfoString(MQL_PROGRAM_NAME))+1)+"_"+base_name;   }
  
//--- Create a new graphical object
   CGCnvElement     *CreateNewGObject(const ENUM_GRAPH_ELEMENT_TYPE type,
                                      const int element_num,
                                      const string name,
                                      const int x,
                                      const int y,
                                      const int w,
                                      const int h,
                                      const color colour,
                                      const uchar opacity,
                                      const bool movable,
                                      const bool activity);

//--- Create a shadow object
   void              CreateShadowObj(const color colour,const uchar opacity);
   
protected:
//--- Initialize the variables
   void              Initialize(void);
   void              Deinitialize(void);
   
public:
//--- Return (1) the mouse status relative to the form, as well as (2) X and (3) Y coordinate of the cursor


Die Methode GetList(), die die Liste der angehängten Objekte zurückgibt, benennen wir in GetListElements()um, was von seiner Funktion her besser geeignet ist:

//--- Return (1) the list of attached objects and (2) the shadow object
   CForm            *GetObject(void)                                          { return &this;                  }
   CArrayObj        *GetListElements(void)                                    { return &this.m_list_elements;  }
   CGCnvElement     *GetShadowObj(void)                                       { return this.m_shadow_obj;      }
//--- Return the pointer to (1) the animation object, the list of (2) text and (3) rectangular animation frames


Im öffentlichen Abschnitt der Klasse deklarieren wir die Methode, indem wir der Liste der angehängten Formularelemente ein neues angehängtes Element hinzufügen :

//--- Create a new attached element
   bool              CreateNewElement(const int element_num,
                                      const string name,
                                      const int x,
                                      const int y,
                                      const int w,
                                      const int h,
                                      const color colour,
                                      const uchar opacity,
                                      const bool movable,
                                      const bool activity);
//--- Add a new attached element
   bool              AddNewElement(CGCnvElement *obj,const int x,const int y);

//--- Draw an object shadow
   void              DrawShadow(const int shift_x,const int shift_y,const color colour,const uchar opacity=127,const uchar blur=4);


Den Codeblock, der alle verwendeten dynamischen Klassenobjekte entfernt, schieben wir aus dem Klassendestruktor

//+------------------------------------------------------------------+
//| Destructor                                                       |
//+------------------------------------------------------------------+
CForm::~CForm()
  {
   if(this.m_shadow_obj!=NULL)
      delete this.m_shadow_obj;
   if(this.m_animations!=NULL)
      delete this.m_animations;
  }
//+------------------------------------------------------------------+

zur neuen Deinitialisierungsmethode:

//+------------------------------------------------------------------+
//| Deinitialize the variables                                       |
//+------------------------------------------------------------------+
void CForm::Deinitialize(void)
  {
   if(this.m_shadow_obj!=NULL)
      delete this.m_shadow_obj;
   if(this.m_animations!=NULL)
      delete this.m_animations;
  }  
//+------------------------------------------------------------------+

Die folgende Methode rufen wir im Destruktor auf:

//+------------------------------------------------------------------+
//| Destructor                                                       |
//+------------------------------------------------------------------+
CForm::~CForm()
  {
   this.Deinitialize();
  }
//+------------------------------------------------------------------+

Dadurch können wir unnötige dynamische Objekte der übergeordneten Klasse aus geerbten Klassen entfernen.

Die Methode zum Hinzufügen eines neuen angehängten Elements zur Liste der angehängten Objektelemente:

//+------------------------------------------------------------------+
//| Add a new attached element                                       |
//+------------------------------------------------------------------+
bool CForm::AddNewElement(CGCnvElement *obj,const int x,const int y)
  {
   if(obj==NULL)
      return false;
   this.m_list_elements.Sort(SORT_BY_CANV_ELEMENT_NAME_OBJ);
   int index=this.m_list_elements.Search(obj);
   if(index>WRONG_VALUE)
     {
      ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_OBJ_ALREADY_IN_LIST),": ",obj.NameObj());
      return false;
     }
   if(!this.m_list_elements.Add(obj))
     {
      ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_FAILED_OBJ_ADD_TO_LIST),": ",obj.NameObj());
      return false;
     }
   return true;
  }
//+------------------------------------------------------------------+

Die Methode empfängt den Zeiger auf das Objekt, das der Liste der angefügten Objekte hinzugefügt werden soll.
Die Liste der Elemente sortieren wir nach dem Namen eines bestimmten Objekts und suchen sie nach einem solchen Objekt in der Liste.
Wenn noch kein Objekt mit demselben Namen in der Liste vorhanden ist, informieren wir darüber und geben false zurück.
Wenn ein Objekt nicht in die Liste der angehängten Objekte eingefügt werden konnte, informieren darüber und geben false zurück.
Ansonsten geben wir als Ergebnis true zurück.

Die Methode, die ein neues angehängtes Element erstellt, ruft nun die Methode auf, die das erstellte Objekt zur Liste hinzufügt:

//+------------------------------------------------------------------+
//| Create a new attached element                                    |
//+------------------------------------------------------------------+
bool CForm::CreateNewElement(const int element_num,
                             const string element_name,
                             const int x,
                             const int y,
                             const int w,
                             const int h,
                             const color colour,
                             const uchar opacity,
                             const bool movable,
                             const bool activity)
  {
   CGCnvElement *obj=this.CreateNewGObject(GRAPH_ELEMENT_TYPE_ELEMENT,element_num,element_name,x,y,w,h,colour,opacity,movable,activity);
   if(obj==NULL)
      return false;
   if(!this.AddNewElement(obj,x,y))
     {
      delete obj;
      return false;
     }
   return true;
  }
//+------------------------------------------------------------------+

Zuvor habe ich das neu erstellte Objekt in dieser Methode zur Liste hinzugefügt. Dies war irrational, da wir (nicht nur beim Erstellen eines Objekts) grafische Elemente aus anderen Programmteilen in die Liste der angehängten Objekte einfügen können.

In der Methode zum Erstellen eines Schattenobjekts wurde das Flag für die Verschiebbarkeit auf true gesetzt. Dadurch wurde das Schattenobjekt beweglich. Ich glaube, dieses Verhalten ist falsch. Stattdessen sollte der Eigenschaftswert von dem Objekt geerbt werden, für das das Schattenobjekt erstellt wurde. Beheben wir das:

//+------------------------------------------------------------------+
//| Create the shadow object                                         |
//+------------------------------------------------------------------+
void CForm::CreateShadowObj(const color colour,const uchar opacity)
  {
//--- If the shadow flag is disabled or the shadow object already exists, exit
   if(!this.m_shadow || this.m_shadow_obj!=NULL)
      return;
//--- Calculate the shadow object coordinates according to the offset from the top and left
   int x=this.CoordX()-OUTER_AREA_SIZE;
   int y=this.CoordY()-OUTER_AREA_SIZE;
//--- Calculate the width and height in accordance with the top, bottom, left and right offsets
   int w=this.Width()+OUTER_AREA_SIZE*2;
   int h=this.Height()+OUTER_AREA_SIZE*2;
//--- Create a new shadow object and set the pointer to it in the variable
   this.m_shadow_obj=new CShadowObj(this.ChartID(),this.SubWindow(),this.CreateNameDependentObject("Shadow"),x,y,w,h);
   if(this.m_shadow_obj==NULL)
     {
      ::Print(DFUN,CMessage::Text(MSG_FORM_OBJECT_ERR_FAILED_CREATE_SHADOW_OBJ));
      return;
     }
//--- Set the properties for the created shadow object
   this.m_shadow_obj.SetID(this.ID());
   this.m_shadow_obj.SetNumber(-1);
   this.m_shadow_obj.SetOpacityShadow(opacity);
   this.m_shadow_obj.SetColorShadow(colour);
   this.m_shadow_obj.SetMovable(this.Movable());
   this.m_shadow_obj.SetActive(false);
   this.m_shadow_obj.SetVisible(false,false);
//--- Move the form object to the foreground
   this.BringToTop();
  }
//+------------------------------------------------------------------+

Alle Vorbereitungsschritte sind abgeschlossen.


Die Objektklasse WinForms Panel

Das Panel-Objekt wird von der Form-Objektklasse abgeleitet. Mit anderen Worten, es enthält die gesamte Funktionsweise und -eigenschaften des Formulars. Außerdem werde ich neue Eigenschaften und Funktionen hinzufügen. Das Panel kann andere Objekte darin platzieren, seine Größe an den Inhalt anpassen und das automatische Scrollen aktivieren, wenn der Inhalt über das Panel hinausgeht.

Im aktuellen Artikel werde ich nur eine Vorbereitung des Panel-Objekts treffen - ich werde alle seine Eigenschaften definieren und Methoden erstellen, um sie festzulegen und zurückzugeben. In den folgenden Artikeln werde ich nach und nach alle Funktionen des Panel-Objekts hinzufügen. Hier kann ich nur ein Panel-Objekt mit seinem Konstruktor erstellen.

Für alle WinForms-Verwaltungselemente definieren wir ein neues Bibliotheksverzeichnis.

Wir erstellen einen neuen Ordner \MQL5\Include\DoEasy\Objects\Graph\WForms\ mit Unterordnern, die nach MS Visual Studio-Kontrollgruppen in der am Anfang des Artikels definierten Anzahl benannt sind:

  • \MQL5\Include\DoEasy\Objects\Graph\WForms\Common Controls\
  • \MQL5\Include\DoEasy\Objects\Graph\WForms\Components\
  • \MQL5\Include\DoEasy\Objects\Graph\WForms\Containers\
  • \MQL5\Include\DoEasy\Objects\Graph\WForms\Data\
  • \MQL5\Include\DoEasy\Objects\Graph\WForms\Dialogs\
  • \MQL5\Include\DoEasy\Objects\Graph\WForms\Menu & Toolbars\
  • \MQL5\Include\DoEasy\Objects\Graph\WForms\Printing     

Da das Panel ein Container für andere Objekte ist, befindet sich die Objektklassendatei im entsprechenden Ordner \MQL5\Include\DoEasy\Objects\Graph\WForms\Containers\.

In der angegebenen Datei erstellen wir eine neue Datei Panel.mqh des CPanel, abgeleitet von CForm, dessen Datei enthalten sein soll:

//+------------------------------------------------------------------+
//|                                                        Panel.mqh |
//|                                  Copyright 2021, MetaQuotes Ltd. |
//|                             https://mql5.com/en/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2021, MetaQuotes Ltd."
#property link      "https://mql5.com/en/users/artmedia70"
#property version   "1.00"
#property strict    // Necessary for mql4
//+------------------------------------------------------------------+
//| Include files                                                    |
//+------------------------------------------------------------------+
#include "..\..\Form.mqh"
//+------------------------------------------------------------------+
//| Panel object class of WForms controls                            |
//+------------------------------------------------------------------+
class CPanel : public CForm
  {
  }

Wir deklarieren im privaten Abschnitt der Klasse alle erforderlichen Variablen und Arrays:

//+------------------------------------------------------------------+
//| Panel object class of WForms controls                            |
//+------------------------------------------------------------------+
class CPanel : public CForm
  {
private:
   color             m_fore_color;                                   // Default text color for all panel objects
   ENUM_FRAME_STYLE  m_border_style;                                 // Panel frame style
   bool              m_autoscroll;                                   // Auto scrollbar flag
   int               m_autoscroll_margin[2];                         // Array of fields around the control during an auto scroll
   bool              m_autosize;                                     // Flag of the element auto resizing depending on the content
   ENUM_CANV_ELEMENT_AUTO_SIZE_MODE m_autosize_mode;                 // Mode of the element auto resizing depending on the content
   ENUM_CANV_ELEMENT_DOCK_MODE m_dock_mode;                          // Mode of binding element borders to the container
   int               m_margin[4];                                    // Array of gaps of all sides between the fields of the current and adjacent controls
   int               m_padding[4];                                   // Array of gaps of all sides inside controls
public:

Um die Begriffe Margin, Padding und AutoSize zu verstehen, betrachten wir das folgende Beispiel aus der MS Windows Forms .NET Framework 4.X-Hilfe:

... Drei der wichtigsten sind die Eigenschaften Margin, Padding und AutoSize, die in allen Windows Forms-Steuerelementen vorhanden sind.

Die Margin-Eigenschaft definiert den Abstand um das Steuerelement herum, der andere Steuerelemente in einem bestimmten Abstand von den Rändern des Steuerelements hält.

Die Padding-Eigenschaft definiert den Platz im Inneren eines Steuerelements, der den Inhalt des Steuerelements (z. B. den Wert seiner Text-Eigenschaft) in einem bestimmten Abstand von den Rändern des Steuerelements hält.

Die AutoSize-Eigenschaft weist ein Steuerelement an, sich automatisch an seinen Inhalt anzupassen. Die Größe wird nicht kleiner als der Wert der ursprünglichen Size-Eigenschaft, und der Wert der Padding-Eigenschaft wird berücksichtigt.


Im öffentlichen Abschnitt der Klasse schreiben wir die Methoden zum Setzen und Zurückgeben der Werte aller deklarierten Klassenvariablen:

public:
//--- (1) Set and (2) return the default text color of all panel objects
   void              ForeColor(const color clr)                      { this.m_fore_color=clr;               }
   color             ForeColor(void)                           const { return this.m_fore_color;            }

//--- (1) Set and (2) return the frame style
   void              BorderStyle(const ENUM_FRAME_STYLE style)       { this.m_border_style=style;           }
   ENUM_FRAME_STYLE  BorderStyle(void)                         const { return this.m_border_style;          }

//--- (1) Set and (2) return the auto scrollbar flag
   void              AutoScroll(const bool flag)                     { this.m_autoscroll=flag;              }
   bool              AutoScroll(void)                                { return this.m_autoscroll;            }
//--- Set the (1) field width, (2) height, (3) the height of all fields around the control during auto scrolling
   void              AutoScrollMarginWidth(const int value)          { this.m_autoscroll_margin[0]=value;   }
   void              AutoScrollMarginHeight(const int value)         { this.m_autoscroll_margin[1]=value;   }
   void              AutoScrollMarginAll(const int value)
                       {
                        this.AutoScrollMarginWidth(value); this.AutoScrollMarginHeight(value);
                       }
//--- Return the (1) field width and (2) height around the control during auto scrolling
   int               AutoScrollMarginWidth(void)               const { return this.m_autoscroll_margin[0];  }
   int               AutoScrollMarginHeight(void)              const { return this.m_autoscroll_margin[1];  }
   
//--- (1) Set and (2) return the flag of the element auto resizing depending on the content
   void              AutoSize(const bool flag)                       { this.m_autosize=flag;                }
   bool              AutoSize(void)                                  { return this.m_autosize;              }
//--- (1) Set and (2) return the mode of the element auto resizing depending on the content
   void              AutoSizeMode(const ENUM_CANV_ELEMENT_AUTO_SIZE_MODE mode) { this.m_autosize_mode=mode; }
   ENUM_CANV_ELEMENT_AUTO_SIZE_MODE AutoSizeMode(void)         const { return this.m_autosize_mode;         }
   
//--- (1) Set and (2) return the mode of binding element borders to the container
   void              DockMode(const ENUM_CANV_ELEMENT_DOCK_MODE mode){ this.m_dock_mode=mode;               }
   ENUM_CANV_ELEMENT_DOCK_MODE DockMode(void)                  const { return this.m_dock_mode;             }

//--- Set the gap (1) to the left, (2) at the top, (3) to the right, (4) at the bottom and (5) on all sides between the fields of this and another control
   void              MarginLeft(const int value)                     { this.m_margin[0]=value;              }
   void              MarginTop(const int value)                      { this.m_margin[1]=value;              }
   void              MarginRight(const int value)                    { this.m_margin[2]=value;              }
   void              MarginBottom(const int value)                   { this.m_margin[3]=value;              }
   void              MarginAll(const int value)
                       {
                        this.MarginLeft(value); this.MarginTop(value); this.MarginRight(value); this.MarginBottom(value);
                       }
//--- Return the gap (1) to the left, (2) at the top, (3) to the right and (4) at the bottom between the fields of this and another control
   int               MarginLeft(void)                          const { return this.m_margin[0];             }
   int               MarginTop(void)                           const { return this.m_margin[1];             }
   int               MarginRight(void)                         const { return this.m_margin[2];             }
   int               MarginBottom(void)                        const { return this.m_margin[3];             }

//--- Set the gap (1) to the left, (2) at the top, (3) to the right, (4) at the bottom and (5) on all sides inside the control
   void              PaddingLeft(const int value)                     { this.m_padding[0]=value;            }
   void              PaddingTop(const int value)                      { this.m_padding[1]=value;            }
   void              PaddingRight(const int value)                    { this.m_padding[2]=value;            }
   void              PaddingBottom(const int value)                   { this.m_padding[3]=value;            }
   void              PaddingAll(const int value)
                       {
                        this.PaddingLeft(value); this.PaddingTop(value); this.PaddingRight(value); this.PaddingBottom(value);
                       }
//--- Return the gap (1) to the left, (2) at the top, (3) to the right and (4) at the bottom between the fields inside the control
   int               PaddingLeft(void)                          const { return this.m_padding[0];           }
   int               PaddingTop(void)                           const { return this.m_padding[1];           }
   int               PaddingRight(void)                         const { return this.m_padding[2];           }
   int               PaddingBottom(void)                        const { return this.m_padding[3];           }
   
//--- Constructors
                     CPanel(const long chart_id,
                           const int subwindow,
                           const string name,
                           const int x,
                           const int y,
                           const int w,
                           const int h);
                     CPanel(const int subwindow,
                           const string name,
                           const int x,
                           const int y,
                           const int w,
                           const int h);
                     CPanel(const string name,
                           const int x,
                           const int y,
                           const int w,
                           const int h);
                     CPanel(const string name) : CForm(::ChartID(),0,name,0,0,0,0)
                       {
                        CGBaseObj::SetTypeElement(GRAPH_ELEMENT_TYPE_PANEL);
                        this.m_type=OBJECT_DE_TYPE_GWF_PANEL; 
                        this.m_fore_color=CLR_FORE_COLOR;
                        this.MarginAll(3);
                        this.PaddingAll(0);
                        this.Initialize(); 
                       }
//--- Destructor
                    ~CPanel();
  };
//+------------------------------------------------------------------+

Bei einigen von ihnen ist es möglich, jede Eigenschaft gleichzeitig festzulegen, die jeder Seite des Objekts entspricht.

Beispielsweise ist es für den Margin-Wert in MS Visual Studio möglich, sowohl jede Eigenschaft separat als auch alle vier gleichzeitig festzulegen:


Wir haben vier Klassenkonstruktoren: mit Angabe (1) der Chart-ID, des Chart-Unterfensters, des Objektnamens und der Koordinaten mit Größe, (2) des aktuellen Chart-Unterfensters, des Objektnamens und der Koordinaten mit Größe, (3) des Objektnamens und der Koordinaten mit der Größe, (4 ) Objektname mit Nullkoordinaten und -größe:

//+------------------------------------------------------------------+
//| Constructor indicating the chart and subwindow ID                |
//+------------------------------------------------------------------+
CPanel::CPanel(const long chart_id,
               const int subwindow,
               const string name,
               const int x,
               const int y,
               const int w,
               const int h) : CForm(chart_id,subwindow,name,x,y,w,h)
  {
   CGBaseObj::SetTypeElement(GRAPH_ELEMENT_TYPE_PANEL);
   this.m_type=OBJECT_DE_TYPE_GWF_PANEL;
   this.m_fore_color=CLR_FORE_COLOR;
   this.MarginAll(3);
   this.PaddingAll(0);
   this.Initialize();
  }
//+------------------------------------------------------------------+
//| Current chart constructor specifying the subwindow               |
//+------------------------------------------------------------------+
CPanel::CPanel(const int subwindow,
               const string name,
               const int x,
               const int y,
               const int w,
               const int h) : CForm(::ChartID(),subwindow,name,x,y,w,h)
  {
   CGBaseObj::SetTypeElement(GRAPH_ELEMENT_TYPE_PANEL);
   this.m_type=OBJECT_DE_TYPE_GWF_PANEL; 
   this.m_fore_color=CLR_FORE_COLOR;
   this.MarginAll(3);
   this.PaddingAll(0);
   this.Initialize();
  }
//+------------------------------------------------------------------+
//| Constructor on the current chart in the main chart window        |
//+------------------------------------------------------------------+
CPanel::CPanel(const string name,
               const int x,
               const int y,
               const int w,
               const int h) : CForm(::ChartID(),0,name,x,y,w,h)
  {
   CGBaseObj::SetTypeElement(GRAPH_ELEMENT_TYPE_PANEL);
   this.m_type=OBJECT_DE_TYPE_GWF_PANEL; 
   this.m_fore_color=CLR_FORE_COLOR;
   this.MarginAll(3);
   this.PaddingAll(0);
   this.Initialize();
  }
//+------------------------------------------------------------------+

Wir übergeben in der Initialisierungszeichenfolge jedes Konstruktors die erforderlichen Parameter an den Konstruktor der übergeordneten Klasse.
Legen wir als Nächstes im Konstruktorkörper den Typ des grafischen Elements, den Typ des Bibliotheksobjekts, die Standardtextfarbe des Bedienfelds fest, setzen Margin für alle Seiten auf 3, Padding auf 0 und initialisieren die Variablen der übergeordneten Klasse.

Dies ist für eine einfache Erstellung eines Panel-Objekts auf einem Klemmenplan ausreichend. Alle anderen Dinge für das Panel-Objekt werden in den kommenden Artikeln implementiert.

Rufen wir im Klassendestruktor die Deinitialisierungsmethode der übergeordneten Klasse auf:

//+------------------------------------------------------------------+
//| Destructor                                                       |
//+------------------------------------------------------------------+
CPanel::~CPanel()
  {
   CForm::Deinitialize();
  }
//+------------------------------------------------------------------+


Jetzt müssen wir die Kollektionsklasse der grafischen Elemente \MQL5\Include\DoEasy\Collections\GraphElementsCollection.mqh verbessern.

Schließen wir anstelle der Formularobjektdatei die Panel-Objektdatei ein:

//+------------------------------------------------------------------+
//|                                      GraphElementsCollection.mqh |
//|                                  Copyright 2021, MetaQuotes Ltd. |
//|                             https://mql5.com/en/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2021, MetaQuotes Ltd."
#property link      "https://mql5.com/en/users/artmedia70"
#property version   "1.00"
//+------------------------------------------------------------------+
//| Include files                                                    |
//+------------------------------------------------------------------+
#include "ListObj.mqh"
#include "..\Services\Select.mqh"
#include "..\Objects\Graph\WForms\Containers\Panel.mqh"
#include "..\Objects\Graph\Standard\GStdVLineObj.mqh"

Da das Panel-Objekt vom Formularobjekt abgeleitet ist, sind alle Objekte seiner Elternhierarchie in der Sammlungsklasse sichtbar.

Schreiben wir im öffentlichen Abschnitt der Klasse zwei Methoden, die die Liste der grafischen Elemente nach Diagramm- und Objekt-IDs sowie nach Diagramm-ID und Objektname zurückgeben:

//--- Return the list of graphical objects by chart ID and group
   CArrayObj        *GetListStdGraphObjByGroup(const long chart_id,const int group)
                       {
                        CArrayObj *list=GetList(GRAPH_OBJ_PROP_CHART_ID,0,chart_id,EQUAL);
                        return CSelect::ByGraphicStdObjectProperty(list,GRAPH_OBJ_PROP_GROUP,0,group,EQUAL);
                       }
//--- Return the list of graphical elements by chart and object IDs
   CArrayObj        *GetListCanvElementByID(const long chart_id,const int element_id)
                       {
                        CArrayObj *list=CSelect::ByGraphCanvElementProperty(this.GetListCanvElm(),CANV_ELEMENT_PROP_CHART_ID,chart_id,EQUAL);
                        return CSelect::ByGraphCanvElementProperty(list,CANV_ELEMENT_PROP_ID,element_id,EQUAL);;
                       }
//--- Return the list of graphical elements by chart ID and object name
   CArrayObj        *GetListCanvElementByName(const long chart_id,const string name)
                       {
                        CArrayObj *list=CSelect::ByGraphCanvElementProperty(this.GetListCanvElm(),CANV_ELEMENT_PROP_CHART_ID,chart_id,EQUAL);
                        return CSelect::ByGraphCanvElementProperty(list,CANV_ELEMENT_PROP_NAME_OBJ,name,EQUAL);;
                       }
   
//--- Constructor

Über die Logik solcher Methoden habe ich mich früher immer wieder Gedanken gemacht. Hier sortieren wir die Liste einfach nach den notwendigen Parametern und geben die resultierende Liste zurück, die den Zeiger auf das in der Sammlungsliste gefundene Objekt enthalten soll.
Wenn kein Objekt gefunden wird, geben die Methoden NULL zurück.

Schreiben wir ganz am Ende des Klassenhauptteils die Methoden zum Erstellen von grafischen Element-, Formular- und Bedienfeldobjekten:

//--- Create a graphical element object on canvas on a specified chart and subwindow
   int               CreateElement(const long chart_id,
                                   const int subwindow,
                                   const string name,
                                   const int x,
                                   const int y,
                                   const int w,
                                   const int h,
                                   const color clr,
                                   const uchar opacity,
                                   const bool movable,
                                   const bool activity,
                                   const bool redraw=false)
                       {
                        int id=this.m_list_all_canv_elm_obj.Total();
                        CGCnvElement *obj=new CGCnvElement(GRAPH_ELEMENT_TYPE_ELEMENT,id,0,chart_id,subwindow,name,x,y,w,h,clr,opacity,movable,activity,redraw);
                        if(!this.AddCanvElmToCollection(obj))
                          {
                           delete obj;
                           return WRONG_VALUE;
                          }
                        obj.Erase(clr,opacity,redraw);
                        return obj.ID();
                       }
//--- Create a graphical element object on canvas on a specified chart and subwindow with the vertical gradient filling
   int               CreateElementVGradient(const long chart_id,
                                            const int subwindow,
                                            const string name,
                                            const int x,
                                            const int y,
                                            const int w,
                                            const int h,
                                            color &clr[],
                                            const uchar opacity,
                                            const bool movable,
                                            const bool activity,
                                            const bool redraw=false)
                       {
                        int id=this.m_list_all_canv_elm_obj.Total();
                        CGCnvElement *obj=new CGCnvElement(GRAPH_ELEMENT_TYPE_ELEMENT,id,0,chart_id,subwindow,name,x,y,w,h,clr[0],opacity,movable,activity,redraw);
                        if(!this.AddCanvElmToCollection(obj))
                          {
                           delete obj;
                           return WRONG_VALUE;
                          }
                        obj.Erase(clr,opacity,true,false,redraw);
                        return obj.ID();
                       }
//--- Create a graphical element object on canvas on a specified chart and subwindow with the horizontal gradient filling
   int               CreateElementHGradient(const long chart_id,
                                            const int subwindow,
                                            const string name,
                                            const int x,
                                            const int y,
                                            const int w,
                                            const int h,
                                            color &clr[],
                                            const uchar opacity,
                                            const bool movable,
                                            const bool activity,
                                            const bool redraw=false)
                       {
                        int id=this.m_list_all_canv_elm_obj.Total();
                        CGCnvElement *obj=new CGCnvElement(GRAPH_ELEMENT_TYPE_ELEMENT,id,0,chart_id,subwindow,name,x,y,w,h,clr[0],opacity,movable,activity,redraw);
                        if(!this.AddCanvElmToCollection(obj))
                          {
                           delete obj;
                           return WRONG_VALUE;
                          }
                        obj.Erase(clr,opacity,false,false,redraw);
                        return obj.ID();
                       }
//--- Create a graphical element object on canvas on a specified chart and subwindow with the cyclic vertical gradient filling
   int               CreateElementVGradientCicle(const long chart_id,
                                                 const int subwindow,
                                                 const string name,
                                                 const int x,
                                                 const int y,
                                                 const int w,
                                                 const int h,
                                                 color &clr[],
                                                 const uchar opacity,
                                                 const bool movable,
                                                 const bool activity,
                                                 const bool redraw=false)
                       {
                        int id=this.m_list_all_canv_elm_obj.Total();
                        CGCnvElement *obj=new CGCnvElement(GRAPH_ELEMENT_TYPE_ELEMENT,id,0,chart_id,subwindow,name,x,y,w,h,clr[0],opacity,movable,activity,redraw);
                        if(!this.AddCanvElmToCollection(obj))
                          {
                           delete obj;
                           return WRONG_VALUE;
                          }
                        obj.Erase(clr,opacity,true,true,redraw);
                        return obj.ID();
                       }
//--- Create a graphical element object on canvas on a specified chart and subwindow with the cyclic horizontal gradient filling
   int               CreateElementHGradientCicle(const long chart_id,
                                                 const int subwindow,
                                                 const string name,
                                                 const int x,
                                                 const int y,
                                                 const int w,
                                                 const int h,
                                                 color &clr[],
                                                 const uchar opacity,
                                                 const bool movable,
                                                 const bool activity,
                                                 const bool redraw=false)
                       {
                        int id=this.m_list_all_canv_elm_obj.Total();
                        CGCnvElement *obj=new CGCnvElement(GRAPH_ELEMENT_TYPE_ELEMENT,id,0,chart_id,subwindow,name,x,y,w,h,clr[0],opacity,movable,activity,redraw);
                        if(!this.AddCanvElmToCollection(obj))
                          {
                           delete obj;
                           return WRONG_VALUE;
                          }
                        obj.Erase(clr,opacity,false,true,redraw);
                        return obj.ID();
                       }
 
//--- Create a graphical object form object on canvas on a specified chart and subwindow
   int               CreateForm(const long chart_id,
                                const int subwindow,
                                const string name,
                                const int x,
                                const int y,
                                const int w,
                                const int h,
                                const color clr,
                                const uchar opacity,
                                const bool movable,
                                const bool activity,
                                const bool shadow=false,
                                const bool redraw=false)
                       {
                        int id=this.m_list_all_canv_elm_obj.Total();
                        CForm *obj=new CForm(chart_id,subwindow,name,x,y,w,h);
                        if(!this.AddCanvElmToCollection(obj))
                          {
                           delete obj;
                           return WRONG_VALUE;
                          }
                        obj.SetID(id);
                        obj.SetActive(activity);
                        obj.SetMovable(movable);
                        obj.SetColorBackground(clr);
                        obj.SetColorFrame(clr);
                        obj.SetOpacity(opacity,false);
                        obj.SetShadow(shadow);
                        obj.DrawRectangle(0,0,obj.Width()-1,obj.Height()-1,obj.ColorFrame(),obj.Opacity());
                        obj.Done();
                        obj.Erase(clr,opacity,redraw);
                        return obj.ID();
                       }
//--- Create a graphical object form object on canvas on a specified chart and subwindow with the vertical gradient filling
   int               CreateFormVGradient(const long chart_id,
                                         const int subwindow,
                                         const string name,
                                         const int x,
                                         const int y,
                                         const int w,
                                         const int h,
                                         color &clr[],
                                         const uchar opacity,
                                         const bool movable,
                                         const bool activity,
                                         const bool shadow=false,
                                         const bool redraw=false)
                       {
                        int id=this.m_list_all_canv_elm_obj.Total();
                        CForm *obj=new CForm(chart_id,subwindow,name,x,y,w,h);
                        if(!this.AddCanvElmToCollection(obj))
                          {
                           delete obj;
                           return WRONG_VALUE;
                          }
                        obj.SetID(id);
                        obj.SetActive(activity);
                        obj.SetMovable(movable);
                        obj.SetColorBackground(clr[0]);
                        obj.SetColorFrame(clr[0]);
                        obj.SetOpacity(opacity,false);
                        obj.SetShadow(shadow);
                        obj.DrawRectangle(0,0,obj.Width()-1,obj.Height()-1,obj.ColorFrame(),obj.Opacity());
                        obj.Done();
                        obj.Erase(clr,opacity,true,false,redraw);
                        return obj.ID();
                       }
//--- Create a graphical object form object on canvas on a specified chart and subwindow with the horizontal gradient filling
   int               CreateFormHGradient(const long chart_id,
                                         const int subwindow,
                                         const string name,
                                         const int x,
                                         const int y,
                                         const int w,
                                         const int h,
                                         color &clr[],
                                         const uchar opacity,
                                         const bool movable,
                                         const bool activity,
                                         const bool shadow=false,
                                         const bool redraw=false)
                       {
                        int id=this.m_list_all_canv_elm_obj.Total();
                        CForm *obj=new CForm(chart_id,subwindow,name,x,y,w,h);
                        if(!this.AddCanvElmToCollection(obj))
                          {
                           delete obj;
                           return WRONG_VALUE;
                          }
                        obj.SetID(id);
                        obj.SetActive(activity);
                        obj.SetMovable(movable);
                        obj.SetColorBackground(clr[0]);
                        obj.SetColorFrame(clr[0]);
                        obj.SetOpacity(opacity,false);
                        obj.SetShadow(shadow);
                        obj.DrawRectangle(0,0,obj.Width()-1,obj.Height()-1,obj.ColorFrame(),obj.Opacity());
                        obj.Done();
                        obj.Erase(clr,opacity,false,false,redraw);
                        return obj.ID();
                       }
//--- Create a graphical object form object on canvas on a specified chart and subwindow with the cyclic vertical gradient filling
   int               CreateFormVGradientCicle(const long chart_id,
                                              const int subwindow,
                                              const string name,
                                              const int x,
                                              const int y,
                                              const int w,
                                              const int h,
                                              color &clr[],
                                              const uchar opacity,
                                              const bool movable,
                                              const bool activity,
                                              const bool shadow=false,
                                              const bool redraw=false)
                       {
                        int id=this.m_list_all_canv_elm_obj.Total();
                        CForm *obj=new CForm(chart_id,subwindow,name,x,y,w,h);
                        if(!this.AddCanvElmToCollection(obj))
                          {
                           delete obj;
                           return WRONG_VALUE;
                          }
                        obj.SetID(id);
                        obj.SetActive(activity);
                        obj.SetMovable(movable);
                        obj.SetColorBackground(clr[0]);
                        obj.SetColorFrame(clr[0]);
                        obj.SetOpacity(opacity,false);
                        obj.SetShadow(shadow);
                        obj.DrawRectangle(0,0,obj.Width()-1,obj.Height()-1,obj.ColorFrame(),obj.Opacity());
                        obj.Done();
                        obj.Erase(clr,opacity,true,true,redraw);
                        return obj.ID();
                       }
//--- Create a graphical object form object on canvas on a specified chart and subwindow with the cyclic horizontal gradient filling
   int               CreateFormHGradientCicle(const long chart_id,
                                              const int subwindow,
                                              const string name,
                                              const int x,
                                              const int y,
                                              const int w,
                                              const int h,
                                              color &clr[],
                                              const uchar opacity,
                                              const bool movable,
                                              const bool activity,
                                              const bool shadow=false,
                                              const bool redraw=false)
                       {
                        int id=this.m_list_all_canv_elm_obj.Total();
                        CForm *obj=new CForm(chart_id,subwindow,name,x,y,w,h);
                        if(!this.AddCanvElmToCollection(obj))
                          {
                           delete obj;
                           return WRONG_VALUE;
                          }
                        obj.SetID(id);
                        obj.SetActive(activity);
                        obj.SetMovable(movable);
                        obj.SetColorBackground(clr[0]);
                        obj.SetColorFrame(clr[0]);
                        obj.SetOpacity(opacity,false);
                        obj.SetShadow(shadow);
                        obj.DrawRectangle(0,0,obj.Width()-1,obj.Height()-1,obj.ColorFrame(),obj.Opacity());
                        obj.Done();
                        obj.Erase(clr,opacity,false,true,redraw);
                        return obj.ID();
                       }
 
//--- Create graphical object WinForms Panel object on canvas on a specified chart and subwindow
   int               CreatePanel(const long chart_id,
                                 const int subwindow,
                                 const string name,
                                 const int x,
                                 const int y,
                                 const int w,
                                 const int h,
                                 const color clr,
                                 const uchar opacity,
                                 const bool movable,
                                 const bool activity,
                                 const bool shadow=false,
                                 const bool redraw=false)
                       {
                        int id=this.m_list_all_canv_elm_obj.Total();
                        CPanel *obj=new CPanel(chart_id,subwindow,name,x,y,w,h);
                        if(!this.AddCanvElmToCollection(obj))
                          {
                           delete obj;
                           return WRONG_VALUE;
                          }
                        obj.SetID(id);
                        obj.SetActive(activity);
                        obj.SetMovable(movable);
                        obj.SetColorBackground(clr);
                        obj.SetColorFrame(clr);
                        obj.SetOpacity(opacity,false);
                        obj.SetShadow(shadow);
                        obj.DrawRectangle(0,0,obj.Width()-1,obj.Height()-1,obj.ColorFrame(),obj.Opacity());
                        obj.Done();
                        obj.Erase(clr,opacity,redraw);
                        return obj.ID();
                       }

  };
//+------------------------------------------------------------------+
//| Constructor                                                      |
//+------------------------------------------------------------------+

Die Methoden zum Erstellen von Elementen und Formularen sind nahezu identisch. Der einzige Unterschied besteht in der Methode, den Hintergrund mit Farbe zu füllen. Es ist entweder eine einzelne permanente Farbe oder eine Füllung mit Farbverlauf. Die Verlaufsfüllung hat mehrere Arten: vertikal, horizontal und zyklisch vertikal und horizontal. Unmittelbar nach dem Erstellen eines Objekts wird es der Kollektionsliste der grafischen Elemente hinzugefügt, und die mindestens erforderlichen Eigenschaften (die der Methode beim Aufruf übergeben werden) werden dafür festgelegt.

In der Methode zum Zurücksetzen der Interaktionsflags für alle Formulare außer dem angegebenen ändern wir den Objekttyp auf das Element, da grafische Elemente das Mindestobjekt zum Erstellen von GUI-Elementen sind:

//+--------------------------------------------------------------------+
//| Reset all interaction flags for all forms except the specified one |
//+--------------------------------------------------------------------+
void CGraphElementsCollection::ResetAllInteractionExeptOne(CGCnvElement *form_exept)
  {
   //--- In the loop by all graphical element collection class objects
   int total=this.m_list_all_canv_elm_obj.Total();
   for(int i=0;i<total;i++)
     {
      //--- get the pointer to the object
      CGCnvElement *obj=this.m_list_all_canv_elm_obj.At(i);
      //--- if failed to receive the pointer, or it is not a form, or it is not a form whose pointer has been passed to the method, move on
      if(obj==NULL || obj.TypeGraphElement()!=GRAPH_ELEMENT_TYPE_FORM || (obj.Name()==form_exept.Name() && obj.ChartID()==form_exept.ChartID()))
         continue;
      //--- Reset the interaction flag for the current form in the loop
      obj.SetInteraction(false);
     }
  }
//+------------------------------------------------------------------+


Da wir jetzt die Methoden zum Erstellen von grafischen Elementen, Formularen und Panels haben, wird die Methode zum Hinzufügen eines grafischen Elements zur Sammlungsliste in \MQL5\Include\DoEasy\Engine.mqh des Hauptbibliotheksobjekts der CEngine nicht mehr benötigt. Also löschen wir sie:

//--- Return the list of graphical elements on canvas
   CArrayObj           *GetListCanvElement(void)                           { return this.m_graph_objects.GetListCanvElm();                }
//--- Add the graphical element on canvas to the collection
   bool                 GraphAddCanvElmToCollection(CGCnvElement *element) { return this.m_graph_objects.AddCanvElmToCollection(element); }
   
//--- Fill in the array with IDs of the charts opened in the terminal
   void              GraphGetArrayChartsID(long &array_charts_id[])

Sie wird durch Methoden ersetzt, die die Liste der grafischen Elemente nach Diagramm- und Objekt-IDs und die Liste der grafischen Elemente nach Diagramm-ID und Objektname zurückgeben:

//--- Return the list of graphical elements on canvas
   CArrayObj           *GetListCanvElement(void)                           { return this.m_graph_objects.GetListCanvElm();                }
//--- Return the list of graphical elements by chart and object IDs
   CArrayObj           *GetListCanvElementByID(const long chart_id,const int element_id)
                          {
                           return this.m_graph_objects.GetListCanvElementByID(chart_id,element_id);
                          }
//--- Return the list of graphical elements by chart ID and object name
   CArrayObj           *GetListCanvElementByName(const long chart_id,const string name)
                          {
                           return this.m_graph_objects.GetListCanvElementByName(chart_id,name);
                          }
   
//--- Fill in the array with IDs of the charts opened in the terminal
   void              GraphGetArrayChartsID(long &array_charts_id[])

Diese beiden Methoden geben einfach das Ergebnis der Anfrage von den gleichnamigen Methoden der Sammlungsklasse von grafischen Elementen zurück, die wir oben betrachtet haben.

Jetzt sind wir bereit für den Test.


Test

Um den Test durchzuführen, verwenden wir den EA aus dem vorherigen Artikel und speichern ihn unter \MQL5\Experts\TestDoEasy\Part101\ als TestDoEasyPart101.mq5.

Ich werde die Erstellung von drei Formularobjekten aus dem vorherigen Artikel verlassen und drei Elementobjekte und ein Panelobjekt hinzufügen.

Zeigen wir auf jedem der Objekte den Text mit seinem Namen und seiner ID in der grafischen Elementsammlung an.

In OnInit() fügen wir die Erstellung aller Objekte mit den Methoden hinzu, die der Methodenkollektionsklasse im aktuellen Artikel hinzugefügt wurden:

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- Set EA global variables
   ArrayResize(array_clr,2);        // Array of gradient filling colors
   array_clr[0]=C'26,100,128';      // Original ≈Dark-azure color
   array_clr[1]=C'35,133,169';      // Lightened original color
//--- Create the array with the current symbol and set it to be used in the library
   string array[1]={Symbol()};
   engine.SetUsedSymbols(array);
   //--- Create the timeseries object for the current symbol and period, and show its description in the journal
   engine.SeriesCreate(Symbol(),Period());
   engine.GetTimeSeriesCollection().PrintShort(false); // Short descriptions
//--- Create form objects
   int obj_id=WRONG_VALUE;
   CArrayObj *list=NULL;
   CForm *form=NULL;
   for(int i=0;i<FORMS_TOTAL;i++)
     {
      obj_id=engine.GetGraphicObjCollection().CreateFormVGradient(ChartID(),0,"Form_0"+string(i+1),30,(form==NULL ? 100 : form.BottomEdge()+20),100,30,array_clr,245,true,true);
      list=engine.GetListCanvElementByID(ChartID(),obj_id);
      form=list.At(0);
      if(form==NULL)
         continue;
      //--- Set ZOrder to zero, display the text describing the gradient type and update the form
      //--- Text parameters: the text coordinates and the anchor point in the form center
      //--- Create a new text animation frame with the ID of 0 and display the text on the form
      form.SetZorder(0,false);
      form.TextOnBG(0,"Form: ID "+(string)form.ID()+", ZOrder "+(string)form.Zorder(),form.Width()/2,form.Height()/2,FRAME_ANCHOR_CENTER,C'211,233,149',255,true,false);
     }
//--- Create four graphical elements
   CGCnvElement *elm=NULL;
   array_clr[0]=C'0x65,0xA4,0xA9';
   array_clr[1]=C'0x48,0x75,0xA2';
//--- Vertical gradient
   obj_id=engine.GetGraphicObjCollection().CreateElementVGradient(NULL,0,"CElmVG",form.RightEdge()+50,20,200,50,array_clr,127,true,true,true);
   list=engine.GetGraphicObjCollection().GetListCanvElementByID(ChartID(),obj_id);
   elm=list.At(0);
   if(elm!=NULL)
     {
      elm.SetFontSize(10);
      elm.Text(elm.Width()/2,elm.Height()/2,"Element: ID "+(string)elm.ID(),C'0xDB,0xEE,0xF2',elm.Opacity(),FRAME_ANCHOR_CENTER);
      elm.Update();
     }
//--- Vertical cyclic gradient
   obj_id=engine.GetGraphicObjCollection().CreateElementVGradientCicle(NULL,0,"CElmVGC",form.RightEdge()+50,80, 200,50,array_clr,127,true,true,true);
   list=engine.GetGraphicObjCollection().GetListCanvElementByID(ChartID(),obj_id);
   elm=list.At(0);
   if(elm!=NULL)
     {
      elm.SetFontSize(10);
      elm.Text(elm.Width()/2,elm.Height()/2,"Element: ID "+(string)elm.ID(),C'0xDB,0xEE,0xF2',elm.Opacity(),FRAME_ANCHOR_CENTER);
      elm.Update();
     }
//--- Horizontal gradient
   obj_id=engine.GetGraphicObjCollection().CreateElementHGradient(NULL,0,"CElmHG",form.RightEdge()+50,140,200,50,array_clr,127,true,true,true);
   list=engine.GetGraphicObjCollection().GetListCanvElementByID(ChartID(),obj_id);
   elm=list.At(0);
   if(elm!=NULL)
     {
      elm.SetFontSize(10);
      elm.Text(elm.Width()/2,elm.Height()/2,"Element: ID "+(string)elm.ID(),C'0xDB,0xEE,0xF2',elm.Opacity(),FRAME_ANCHOR_CENTER);
      elm.Update();
     }
//--- Horizontal cyclic gradient
   obj_id=engine.GetGraphicObjCollection().CreateElementHGradientCicle(NULL,0,"CElmHGC",form.RightEdge()+50,200,200,50,array_clr,127,true,true,false);
   list=engine.GetGraphicObjCollection().GetListCanvElementByID(ChartID(),obj_id);
   elm=list.At(0);
   if(elm!=NULL)
     {
      elm.SetFontSize(10);
      elm.Text(elm.Width()/2,elm.Height()/2,"Element: ID "+(string)elm.ID(),C'0xDB,0xEE,0xF2',elm.Opacity(),FRAME_ANCHOR_CENTER);
      elm.Update();
     }

//--- Create WinForms Panel object
   CPanel *pnl=NULL;
   obj_id=engine.GetGraphicObjCollection().CreatePanel(ChartID(),0,"WFPanel",elm.RightEdge()+50,50,150,150,array_clr[0],200,true,true,false,true);
   list=engine.GetListCanvElementByID(ChartID(),obj_id);
   pnl=list.At(0);
   if(pnl!=NULL)
     {
      pnl.SetFontSize(10);
      pnl.TextOnBG(0,"WinForm Panel: ID "+(string)pnl.ID(),4,2,FRAME_ANCHOR_LEFT_TOP,pnl.ForeColor(),pnl.Opacity());
      pnl.Update(true);
     }
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+

Formobjekte werden mit einem vertikalen Verlauf gefüllt, während jedes Elementobjekt mit einem eigenen Verlaufstyp gefüllt wird. Das Panel-Objekt wird mit einer Farbe gefüllt.

Kompilieren Sie den EA und starten Sie ihn auf dem Chart:


Formulare reagieren auf Mausbewegungen und werden immer über grafischen Objekten platziert, die dem Chart hinzugefügt werden. Verlaufsfüllungen von Elementobjekten werden korrekt gezeichnet und es gibt nur eine Panelobjektfarbe. Allerdings reagieren weder die Elemente noch das Panel auf die Maus und befinden sich im Hintergrund unter allen grafischen Objekten. Dies geschieht, weil ich Mausereignisse nur für Formularobjekte verarbeitet habe. Die Tatsache, dass das Panel im Wesentlichen ein Formular ist, spielt keine Rolle, da ich explizit nur die CForm-Klasse handhabe. Ich werde das alles später reparieren.


Was kommt als Nächstes?

Im nächsten Artikel werde ich die Entwicklung der WinForms-Panel-Objektklasse fortsetzen.

Alle Dateien der aktuellen Bibliotheksversion, des Test-EA und des Chartereignis-Kontrollindikators für MQL5 sind unten angehängt, damit Sie sie testen und herunterladen können. Hinterlassen Sie Ihre Fragen, Kommentare und Vorschläge in den Kommentaren.

Zurück zum Inhalt

*Der letzte Artikel der vorherigen Serie:

Grafiken in der DoEasy-Bibliothek (Teil 100): Verbesserungen bei der Handhabung erweiterter standardmäßiger grafischer Objekte


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

Beigefügte Dateien |
MQL5.zip (4381.34 KB)
Einen handelnden Expert Advisor von Grund auf neu entwickeln (Teil 7): Hinzufügen des Volumens zum Preis (I) Einen handelnden Expert Advisor von Grund auf neu entwickeln (Teil 7): Hinzufügen des Volumens zum Preis (I)
Dies ist einer der stärksten Indikatoren, die es derzeit gibt. Jeder, der versucht, ein gewisses Maß an Selbstvertrauen zu haben, muss diesen Indikator auf seinem Chart haben. Am häufigsten wird der Indikator von denjenigen verwendet, die beim Handel das „Tape Reading” bevorzugen. Dieser Indikator kann auch von denjenigen verwendet werden, die beim Handel nur die Preisbewegungen (Price Action) verwenden.
Datenwissenschaft und maschinelles Lernen (Teil 03): Matrix-Regression Datenwissenschaft und maschinelles Lernen (Teil 03): Matrix-Regression
Diesmal werden unsere Modelle mit Hilfe von Matrizen erstellt, was uns eine gewisse Flexibilität ermöglicht, während wir gleichzeitig leistungsstarke Modelle erstellen können, die nicht nur mit fünf unabhängigen Variablen, sondern auch mit vielen Variablen umgehen können, solange wir innerhalb der Berechnungsgrenzen eines Computers bleiben.
Video: Als Nächstes tragen Sie den Servernamen, Ihre Kontonummer und das Master-Passwort an. Video: Als Nächstes tragen Sie den Servernamen, Ihre Kontonummer und das Master-Passwort an.
Die Mehrheit der Studenten in meinen Kursen war der Meinung, dass MQL5 wirklich schwer zu verstehen ist. Darüber hinaus suchten sie nach einer einfachen Methode, um einige Prozesse zu automatisieren. Entdecken Sies, wie Sie sofort mit MQL5 arbeiten können, einfach durch das Lesen der in diesem Artikel enthaltenen Informationen. Selbst, wenn Sie noch nie etwas programmiert haben. Und auch für den Fall, dass Sie die vorhergehenden Illustrationen, die Sie beobachtet haben, nicht nachvollziehen können.
Lernen Sie, wie man ein Handelssystem mit Hilfe von Parabolic SAR entwickelt Lernen Sie, wie man ein Handelssystem mit Hilfe von Parabolic SAR entwickelt
In diesem Artikel setzen wir unsere Serie darüber fort, wie man ein Handelssystem mit den beliebtesten Indikatoren entwickelt. In diesem Artikel lernen wir den Parabolic SAR Indikator im Detail kennen und erfahren, wie wir ein Handelssystem für den MetaTrader 5 mit Hilfe einiger einfacher Strategien entwickeln können.