Grafiken in der Bibliothek DoEasy (Teil 76): Das Formularobjekt und vordefinierte Farbschemata

Artyom Trishkin | 22 Juli, 2021

Inhalt


Konzept

Im vorherigen Artikel habe ich die Basis-Objektklasse für grafische Elemente entwickelt, die als Grundlage für die Erstellung komplexerer grafischer Objekte der Bibliothek dient, sowie die Methoden zum Zeichnen grafischer Primitive und Texte erstellt. Heute werde ich dieses grafische Element-Objekt als Grundlage für seine abgeleitete Objektklasse — das Formular-Objekt — verwenden. Das Formularobjekt kann bereits eine absolut unabhängige Einheit für die Gestaltung und Darstellung einiger Steuerelemente und die Durchführung der Visualisierung in Programmen sein, die auf der Bibliothek basieren.

Aber bevor wir das Formularobjekt erstellen, sollten wir über die GUI und ihre Design-Methoden sprechen, sowie einen Anfangssatz von Farbschemata und Typen der grafischen Objektpräsentation erstellen.
Viele Programme, die eine grafische Datendarstellung verwenden und über ihre Grafik-Engine eine Interaktion mit der Außenwelt ermöglichen, erlauben es dem Nutzer, das Aussehen und Design der grafischen Objekte schnell zu ändern. Ich werde einen Satz von Schemata verwenden, um das Aussehen und das Farbschema schnell zu ändern. Die Parameter der erstellten Schemata werden in einer separaten Bibliotheksdatei enthalten sein, in der ein Programmbenutzer oder ein Programmierer schnell verschiedene Einstellungen für das Aussehen und die Farbe des grafischen Objekts ändern kann.

In diesem Artikel beginne ich mit der Entwicklung von zwei Schemata, die nach und nach immer mehr unterschiedliche Parameter und deren Werte enthalten werden, wenn ich neue Bibliotheksobjekte und Funktionen weiter entwickle.
Es besteht keine Notwendigkeit, die in der Bibliothek entwickelten Schemata für die Erstellung eigener grafischer Objekte zu verwenden, aber sie können als gutes Beispiel dafür dienen, wie genau bestimmte Objekte für die spätere Verwendung erstellt werden müssen.


Verbesserung der Klassenbibliothek

Fügen wir wie gewohnt die Indizes der neuen Nachrichten in \MQL5\Include\DoEasy\Data.mqh ein:

   MSG_LIB_SYS_FAILED_ADD_SYM_OBJ,                    // Failed to add symbol
   MSG_LIB_SYS_FAILED_CREATE_ELM_OBJ,                 // Failed to create the graphical element object
   MSG_LIB_SYS_OBJ_ALREADY_IN_LIST,                   // Such an object is already present in the list
   MSG_LIB_SYS_FAILED_GET_DATA_GRAPH_RES,             // Failed to receive graphical resource data

...

   MSG_LIB_SYS_FAILED_ADD_BUFFER,                     // Failed to add buffer object to the list
   MSG_LIB_SYS_FAILED_CREATE_BUFFER_OBJ,              // Failed to create \"Indicator buffer\" object
   MSG_LIB_SYS_FAILED_OBJ_ADD_TO_LIST,                // Could not add object to the list

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

   {"Не удалось добавить символ ","Failed to add "},
   {"Не удалось создать объект-графический элемент ","Failed to create graphic element object "},
   {"Такой объект уже есть в списке","Such an object is already in the list"},
   {"Не удалось получить данные графического ресурса","Failed to get graphic resource data"},

...

   {"Не удалось добавить объект-буфер в список","Failed to add buffer object to list"},
   {"Не удалось создать объект \"Индикаторный буфер\"","Failed to create object \"Indicator buffer\""},
   {"Не удалось добавить объект в список","Failed to add object to the list"},

Bei der Entwicklung des Formularobjekts werde ich ein Muster für die spätere Erzeugung von Schatten, die das Formular auf die Objekte wirft, über denen sich das Formular befindet, erstellen. Das Formular benötigt einen kleinen Abstand um sich herum. Dieser Raum soll zum Zeichnen des Schattens verwendet werden. Um die Größe des Raums festzulegen, benötigen wir eine Makro-Substitution, die die Größe einer Seite dieses Raums in Pixeln angibt. Wenn wir fünf Pixel einstellen, wird das Formular oben, unten, links und rechts freien Raum haben — fünf Pixel auf jeder Seite.

Der Umgang mit dem grafischen Elementobjekt zeigt, dass einige seiner Eigenschaften in der Eigenschaftsliste nicht benötigt werden. Sie werden nicht zum Suchen und Sortieren von Objekten verwendet. Daher sollten sie aus der Enumeration der Integer-Eigenschaften des Elementobjekts entfernt werden. Sie werden in gewöhnlichen geschützten Klassenvariablen enthalten sein.

Öffnen wir nun \MQL5\Include\DoEasy\Defines.mqh und implementieren die oben genannten Änderungen:

In der Liste der Canvas-Parameter fügen wir den Abstand einer Seite zum Einfügen von Schatten hinzu:

//--- Parameters of the DOM snapshot series
#define MBOOKSERIES_DEFAULT_DAYS_COUNT (1)                        // The default required number of days for DOM snapshots in the series
#define MBOOKSERIES_MAX_DATA_TOTAL     (200000)                   // Maximum number of stored DOM snapshots of a single symbol
//--- Canvas parameters
#define PAUSE_FOR_CANV_UPDATE          (16)                       // Canvas update frequency
#define NULL_COLOR                     (0x00FFFFFF)               // Zero for the canvas with the alpha channel
#define OUTER_AREA_SIZE                (5)                        // Size of one side of the outer area around the workspace
//+------------------------------------------------------------------+
//| Enumerations                                                     |
//+------------------------------------------------------------------+

Wir entfernen zwei unnötige Eigenschaften aus der Liste der ganzzahligen Eigenschaften des grafischen Elements:

   CANV_ELEMENT_PROP_ACT_SHIFT_BOTTOM,                // Active area offset from the bottom edge of the element
   CANV_ELEMENT_PROP_OPACITY,                         // Element opacity
   CANV_ELEMENT_PROP_COLOR_BG,                        // Element background color
   CANV_ELEMENT_PROP_MOVABLE,                         // Element moveability flag

Jetzt sieht die Liste der Integer-Eigenschaften wie folgt aus:

//+------------------------------------------------------------------+
//| Integer properties of the graphical element on the canvas        |
//+------------------------------------------------------------------+
enum ENUM_CANV_ELEMENT_PROP_INTEGER
  {
   CANV_ELEMENT_PROP_ID = 0,                          // Element ID
   CANV_ELEMENT_PROP_TYPE,                            // Graphical element type
   CANV_ELEMENT_PROP_NUM,                             // Element index in the list
   CANV_ELEMENT_PROP_CHART_ID,                        // Chart ID
   CANV_ELEMENT_PROP_WND_NUM,                         // Chart subwindow index
   CANV_ELEMENT_PROP_COORD_X,                         // Form's X coordinate on the chart
   CANV_ELEMENT_PROP_COORD_Y,                         // Form's Y coordinate on the chart
   CANV_ELEMENT_PROP_WIDTH,                           // Element width
   CANV_ELEMENT_PROP_HEIGHT,                          // Element height
   CANV_ELEMENT_PROP_RIGHT,                           // Element right border
   CANV_ELEMENT_PROP_BOTTOM,                          // Element bottom border
   CANV_ELEMENT_PROP_ACT_SHIFT_LEFT,                  // Active area offset from the left edge of the element
   CANV_ELEMENT_PROP_ACT_SHIFT_TOP,                   // Active area offset from the upper edge of the element
   CANV_ELEMENT_PROP_ACT_SHIFT_RIGHT,                 // Active area offset from the right edge of the element
   CANV_ELEMENT_PROP_ACT_SHIFT_BOTTOM,                // Active area offset from the bottom edge of the element
   CANV_ELEMENT_PROP_MOVABLE,                         // Element moveability flag
   CANV_ELEMENT_PROP_ACTIVE,                          // Element activity flag
   CANV_ELEMENT_PROP_COORD_ACT_X,                     // X coordinate of the element active area
   CANV_ELEMENT_PROP_COORD_ACT_Y,                     // Y coordinate of the element active area
   CANV_ELEMENT_PROP_ACT_RIGHT,                       // Right border of the element active area
   CANV_ELEMENT_PROP_ACT_BOTTOM,                      // Bottom border of the element active area
  };
#define CANV_ELEMENT_PROP_INTEGER_TOTAL (21)          // Total number of integer properties
#define CANV_ELEMENT_PROP_INTEGER_SKIP  (0)           // Number of integer properties not used in sorting
//+------------------------------------------------------------------+

Die Gesamtzahl der ganzzahligen Eigenschaften wird um 2 reduziert — wir setzen 21 anstelle von 23.

Außerdem werden zwei unnötige Konstanten aus der Enumeration der möglichen Sortierkriterien für Canvas-basierte grafische Elemente entfernt:

   SORT_BY_CANV_ELEMENT_ACT_SHIFT_BOTTOM,             // Sort by the active area offset from the bottom edge of the element
   SORT_BY_CANV_ELEMENT_OPACITY,                      // Sort by the element opacity
   SORT_BY_CANV_ELEMENT_COLOR_BG,                     // Sort by the element background color
   SORT_BY_CANV_ELEMENT_MOVABLE,                      // Sort by the element moveability flag

Die vollständige Liste soll wie folgt lauten:

//+------------------------------------------------------------------+
//| 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 integer properties
   SORT_BY_CANV_ELEMENT_ID = 0,                       // Sort by element ID
   SORT_BY_CANV_ELEMENT_TYPE,                         // Sort by graphical element type
   SORT_BY_CANV_ELEMENT_NUM,                          // Sort by form index in the list
   SORT_BY_CANV_ELEMENT_CHART_ID,                     // Sort by chart ID
   SORT_BY_CANV_ELEMENT_WND_NUM,                      // Sort by chart window index
   SORT_BY_CANV_ELEMENT_COORD_X,                      // Sort by the element X coordinate on the chart
   SORT_BY_CANV_ELEMENT_COORD_Y,                      // Sort by the element Y coordinate on the chart
   SORT_BY_CANV_ELEMENT_WIDTH,                        // Sort by the element width
   SORT_BY_CANV_ELEMENT_HEIGHT,                       // Sort by the element height
   SORT_BY_CANV_ELEMENT_RIGHT,                        // Sort by the element right border
   SORT_BY_CANV_ELEMENT_BOTTOM,                       // Sort by the element bottom border
   SORT_BY_CANV_ELEMENT_ACT_SHIFT_LEFT,               // Sort by the active area offset from the left edge of the element
   SORT_BY_CANV_ELEMENT_ACT_SHIFT_TOP,                // Sort by the active area offset from the top edge of the element
   SORT_BY_CANV_ELEMENT_ACT_SHIFT_RIGHT,              // Sort by the active area offset from the right edge of the element
   SORT_BY_CANV_ELEMENT_ACT_SHIFT_BOTTOM,             // Sort by the active area offset from the bottom edge of the element
   SORT_BY_CANV_ELEMENT_MOVABLE,                      // Sort by the element moveability flag
   SORT_BY_CANV_ELEMENT_ACTIVE,                       // Sort by the element activity flag
   SORT_BY_CANV_ELEMENT_COORD_ACT_X,                  // Sort by X coordinate of the element active area
   SORT_BY_CANV_ELEMENT_COORD_ACT_Y,                  // Sort by Y coordinate of the element active area
   SORT_BY_CANV_ELEMENT_ACT_RIGHT,                    // Sort by the right border of the element active area
   SORT_BY_CANV_ELEMENT_ACT_BOTTOM,                   // Sort by the bottom border of the element active area
//--- Sort by real properties

//--- Sort by string properties
   SORT_BY_CANV_ELEMENT_NAME_OBJ = FIRST_CANV_ELEMENT_STR_PROP,// Sort by an element object name
   SORT_BY_CANV_ELEMENT_NAME_RES,                     // Sort by the graphical resource name
  };
//+------------------------------------------------------------------+

Alle erzeugten grafischen Objekte basieren auf dem Objekt des grafischen Elements. Das Objekt selbst ist abgeleitet vom Basisobjekt aller grafischen Objekte der Bibliothek (das wiederum ein Nachkomme der Basisklasse CObject der Standardbibliothek ist). Alle Eigenschaften jeder Elternklasse werden an ihre Nachkommen vererbt. Wenn wir also Eigenschaften benötigen, die allen grafischen Objekten gemeinsam sind, sollten sie sich in den Basisobjekten des gesamten Ableitungsbaums befinden. Das Klassenobjekt CGBaseObj dient als Objekt für grafische Bibliotheksobjekte.

Wir werden die Sichtbarkeit von grafischen Objekten im Diagramm verwalten müssen. Dazu müssen wir das grafische Objekt nicht entfernen oder ausblenden. Alles, was wir tun müssen, ist, die notwendigen Flags in der Eigenschaft OBJPROP_TIMEFRAMES des grafischen Objekts zu spezifizieren, damit es entfernt oder im Diagramm über allen anderen angezeigt wird. So können wir die Sichtbarkeit des Objekts im Diagramm verwalten und das gewünschte Objekt über allen anderen platzieren. Es wird als das aktuelle Objekt dienen, mit dem der Programmnutzer arbeiten soll.

Aus dem gesamten Satz der Objekt-Flags benötigen wir die folgenden: OBJ_NO_PERIODS — zum Ausblenden des Objekts und OBJ_ALL_PERIODS — zum Anzeigen des Objekts in einem Chart. Um das Objekt in den Vordergrund zu bringen, verstecken wir es einfach und zeigen es dann wieder an.

Fügen wir der Basisobjektdatei \MQL5\Include\DoEasy\Objects\Graph\GBaseObj.mqh neue Eigenschaften und Methoden hinzu.

Im geschützten Teil der Klasse wird die Variable zum Speichern der Eigenschaft Objektsichtbarkeit deklariert:

//+------------------------------------------------------------------+
//| Class of the base object of the library graphical objects        |
//+------------------------------------------------------------------+
class CGBaseObj : public CObject
  {
private:

protected:
   string            m_name_prefix;                      // Object name prefix
   string            m_name;                             // Object name
   long              m_chart_id;                         // Chart ID
   int               m_subwindow;                        // Subwindow index
   int               m_shift_y;                          // Subwindow Y coordinate shift
   int               m_type;                             // Object type
   bool              m_visible;                          // Object visibility
   
//--- Create (1) the object structure and (2) the object from the structure
   virtual bool      ObjectToStruct(void)                      { return true; }
   virtual void      StructToObject(void){;}

public:

Im öffentlichen Abschnitt der Klasse wird die Methode zum Setzen des Sichtbarkeitsflags des Objekts sowie zum gleichzeitigen Setzen der Eigenschaft selbst an das Objekt und die Methode zum Zurückgeben der Objektsichtbarkeit im Chart eingefügt:

public:
//--- Return the values of class variables
   string            Name(void)                          const { return this.m_name;      }
   long              ChartID(void)                       const { return this.m_chart_id;  }
   int               SubWindow(void)                     const { return this.m_subwindow; }
//--- (1) Set and (2) return the object visibility
   void              SetVisible(const bool flag)   
                       { 
                        long value=(flag ? OBJ_ALL_PERIODS : 0);
                        if(::ObjectSetInteger(this.m_chart_id,this.m_name,OBJPROP_TIMEFRAMES,value))
                           this.m_visible=flag;
                       }
   bool              IsVisible(void)                     const { return this.m_visible;   }

//--- The virtual method returning the object type
   virtual int       Type(void)                          const { return this.m_type;      }

Die Methode, die die Sichtbarkeit des Objekts setzt, prüft zunächst den Wert des Flags. Abhängig vom übergebenen Wert (true oder false) sendet sie eine Anforderung zum Setzen eines Wertes an das Objekt, oder OBJ_ALL_PERIODS zum Anzeigen eines Objekts im Chart, oder 0 zum Ausblenden. Wenn eine Anforderung erfolgreich in die Warteschlange der Chart-Ereignisse gestellt wird, erhält die Variable m_visible den an die Methode übergebenen Flag-Wert. Der Wert kann mit der Methode IsVisible() abgerufen werden, die den Variablenwert zurückgibt.

In der Initialisierungsliste des Klassenkonstruktors initialisieren wir eine neue Variable mit false:

//+------------------------------------------------------------------+
//| Constructor                                                      |
//+------------------------------------------------------------------+
CGBaseObj::CGBaseObj() : m_shift_y(0), m_type(0),m_visible(false), m_name_prefix(::MQLInfoString(MQL_PROGRAM_NAME)+"_")
  {
  }
//+------------------------------------------------------------------+


Verbessern wir die Objektklasse Grafisches Element in \MQL5\Include\DoEasy\Objects\Graph\GCnvElement.mqh.

Im geschützten Abschnitt der Klasse deklarieren wir die Flag-Variable, in der das Objekt Schattenanwesenheit/-fehlen gesetzt wird, und die Variable zum Speichern der Chart-Hintergrundfarbe — wir werden sie in Zukunft beim Zeichnen von Schatten benötigen:

//+------------------------------------------------------------------+
//| Class of the base object of the library graphical objects        |
//+------------------------------------------------------------------+
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
//--- Return the cursor position relative to the (1) entire element and (2) the element's active area
   bool              CursorInsideElement(const int x,const int y);
   bool              CursorInsideActiveArea(const int x,const int y);
//--- Create (1) the object structure and (2) the object from the structure
   virtual bool      ObjectToStruct(void);
   virtual void      StructToObject(void);

private:

Da wir zwei Konstanten aus der Enumeration der Objekt-Ganzzahl-Eigenschaften entfernt haben, müssen wir sie nun in den Klassenvariablen speichern.
Deklarieren wir sie im privaten Abschnitt
:

   long              m_long_prop[ORDER_PROP_INTEGER_TOTAL];    // Integer properties
   double            m_double_prop[ORDER_PROP_DOUBLE_TOTAL];   // Real properties
   string            m_string_prop[ORDER_PROP_STRING_TOTAL];   // String properties
   
   ENUM_TEXT_ANCHOR  m_text_anchor;                            // Current text alignment
   color             m_color_bg;                               // Element background color
   uchar             m_opacity;                                // Element opacity
   
//--- Return the index of the array the order's (1) double and (2) string properties are located at

In diesen Variablen werden nun die "Element-Hintergrundfarbe" und die "Element-Transparenz" eingestellt und ausgelesen.

Um das Aussehen von grafischen Objekten zu gestalten, benötigen wir eine Methode, die es erlaubt, die Farbhelligkeit zu verändern.
Dies ist eine der Komponenten des HSL-Farbmodells:

HSL, HLS oder HSI (Ton, Sättigung, Helligkeit (Intensität)) ist ein Farbmodell, in dem Farbton, Sättigung und Helligkeit als Farbkoordinaten verwendet werden. HSV und HSL sind zwei verschiedene Farbmodelle (Helligkeit (lightness) sollte nicht mit Glanz (brightness) verwechselt werden).

Wenn wir grafische Primitive zeichnen, müssen wir die konventionell beleuchteten Teile der Zeichnung aufhellen und die konventionell schattierten Teile abdunkeln. Die Farbe des Bildes selbst sollte davon nicht betroffen sein. Um dies zu gewährleisten, werde ich die Methode anwenden, die das ARGB-Farbmodell in HSL umwandelt und die Helligkeit der Pixel des gewünschten Teils des Bildes ändert.

Deklarieren wir diese Methode im privaten Bereich der Klasse:

//--- Update the coordinates (shift the canvas)
   bool              Move(const int x,const int y,const bool redraw=false);

//--- Change the color lightness by the specified amount
   uint              ChangeColorLightness(const uint clr,const double change_value);

protected:

Das grafische Elementobjekt ist ein Hauptobjekt für die Erstellung komplexerer grafischer Objekte, die davon abgeleitet werden. Unter Berücksichtigung des Konzepts der Konstruktion von Bibliotheksobjekten (wobei die übergeordnete Klasse einen geschützten Konstruktor besitzt, der die Parameter des erstellten Nachfolgeobjekts angibt), müssen wir einen geschützten parametrischen Konstruktor für das Elementobjekt erstellen. Er soll klärende Parameter über den Typ des zu erzeugenden Nachfolgeobjekts auf der Basis des grafischen Elements (hier ist es das Formularobjekt) erhalten.

Im geschützten Bereich der Klasse deklarieren wir einen neuen geschützten parametrischen Konstruktor:

protected:
//--- Protected constructor
                     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);
public:

Es soll nur grundlegende Parameter für die Erstellung des Objekts erhalten. Alle anderen Parameter werden erst nach seiner erfolgreichen Erstellung gesetzt. Dies wird in der Kollektionsklasse für grafische Objekte der Bibliothek geschehen, mit der ich noch nicht begonnen habe.

Fügen wir die Initialisierung des Flag für das Vorhandensein von Schatten und Hintergrundfarbe des Charts zum Standard-Konstruktor (nicht-parametrisch) hinzu, und zwar zu seiner Initialisierungsliste:

public:
//--- Parametric constructor
                     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);
//--- Default constructor/Destructor
                     CGCnvElement() : m_shadow(false),m_chart_color_bg((color)::ChartGetInteger(::ChartID(),CHART_COLOR_BACKGROUND)) {;}


Ergänzen wir die neuen Methoden zum Setzen von Objekteigenschaften in den Methodenblöcken für einen vereinfachten Zugriff auf Objektparameter:

//+------------------------------------------------------------------+
//| Methods of simplified access to object properties                |
//+------------------------------------------------------------------+
//--- Set the (1) X, (2) Y coordinates, (3) element width, (4) height, (5) right (6) and bottom edge,
   bool              SetCoordX(const int coord_x);
   bool              SetCoordY(const int coord_y);
   bool              SetWidth(const int width);
   bool              SetHeight(const int height);
   void              SetRightEdge(void)                        { this.SetProperty(CANV_ELEMENT_PROP_RIGHT,this.RightEdge());           }
   void              SetBottomEdge(void)                       { this.SetProperty(CANV_ELEMENT_PROP_BOTTOM,this.BottomEdge());         }
//--- Set the shift of the (1) left, (2) top, (3) right, (4) bottom edge of the active area relative to the element,
//--- (5) all shifts of the active area edges relative to the element, (6) the element background color and (7) the element opacity
   void              SetActiveAreaLeftShift(const int value)   { this.SetProperty(CANV_ELEMENT_PROP_ACT_SHIFT_LEFT,fabs(value));       }
   void              SetActiveAreaRightShift(const int value)  { this.SetProperty(CANV_ELEMENT_PROP_ACT_SHIFT_RIGHT,fabs(value));      }
   void              SetActiveAreaTopShift(const int value)    { this.SetProperty(CANV_ELEMENT_PROP_ACT_SHIFT_TOP,fabs(value));        }
   void              SetActiveAreaBottomShift(const int value) { this.SetProperty(CANV_ELEMENT_PROP_ACT_SHIFT_BOTTOM,fabs(value));     }
   void              SetActiveAreaShift(const int left_shift,const int bottom_shift,const int right_shift,const int top_shift);
   void              SetColorBackground(const color colour)    { this.m_color_bg=colour;                                               }
   void              SetOpacity(const uchar value,const bool redraw=false);

//--- Set the flag of (1) object moveability, (2) activity, (3) element ID, (4) element index in the list and (5) shadow presence
   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              SetID(const int id)                       { this.SetProperty(CANV_ELEMENT_PROP_ID,id);                            }
   void              SetNumber(const int number)               { this.SetProperty(CANV_ELEMENT_PROP_NUM,number);                       }
   void              SetShadow(const bool flag);
   
//--- Return the shift (1) of the left, (2) right, (3) top and (4) bottom edge of the element active area

Die Methoden zur Rückgabe der Hintergrundfarbe und Transparenz geben jetzt die Werte zurück, die in den neu deklarierten Variablen gesetzt sind:

//--- Return (1) the background color, (2) the opacity, coordinate (3) of the right and (4) bottom element edge
   color             ColorBackground(void)               const { return this.m_color_bg;                                               }
   uchar             Opacity(void)                       const { return this.m_opacity;                                                }
   int               RightEdge(void)                     const { return this.CoordX()+this.m_canvas.Width();                           }
   int               BottomEdge(void)                    const { return this.CoordY()+this.m_canvas.Height();                          }
//--- Return the (1) X, (2) Y coordinates, (3) element width and (4) height,

Am Ende der Liste fügen wir die Methode zur Rückgabe des Flags zum Zeichnen des vom Objekt geworfenen Schattens, die Methode zur Rückgabe der Chart-Hintergrundfarbe
und die Methode, um das Objekt in den Vordergrund zu rücken (über alle anderen grafischen Objekte im Chart):

//--- Return (1) the element ID, (2) element index in the list, (3) flag of the form shadow presence and (4) the chart background color
   int               ID(void)                            const { return (int)this.GetProperty(CANV_ELEMENT_PROP_ID);                   }
   int               Number(void)                        const { return (int)this.GetProperty(CANV_ELEMENT_PROP_NUM);                  }
   bool              IsShadow(void)                      const { return this.m_shadow;                                                 }
   color             ChartColorBackground(void)          const { return this.m_chart_color_bg;                                         }
//--- Set the object above all
   void              BringToTop(void)                          { CGBaseObj::SetVisible(false); CGBaseObj::SetVisible(true);            }
//+------------------------------------------------------------------+

Wie wir sehen, sollten wir das Objekt mit Hilfe der oben betrachteten Methoden der Elternklasse ausblenden und sofort wieder einblenden, um es vor alle anderen zu positionieren.

Wir löschen die Zeilen, die die nun entfernten Eigenschaften setzen, aus dem parametrischen Konstruktor:

      this.SetProperty(CANV_ELEMENT_PROP_ACT_SHIFT_BOTTOM,0);                    // Active area offset from the bottom edge of the element
      this.SetProperty(CANV_ELEMENT_PROP_OPACITY,opacity);                       // Element opacity
      this.SetProperty(CANV_ELEMENT_PROP_COLOR_BG,colour);                       // Element color
      this.SetProperty(CANV_ELEMENT_PROP_MOVABLE,movable);                       // Element moveability flag

Nun werden diese, sowie neue Eigenschaften, in den neuen Variablen gesetzt:

//+------------------------------------------------------------------+
//| 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_chart_color_bg=(color)::ChartGetInteger(chart_id,CHART_COLOR_BACKGROUND);
   this.m_name=this.m_name_prefix+name;
   this.m_chart_id=chart_id;
   this.m_subwindow=wnd_num;
   this.m_type=element_type;
   this.SetFont("Calibri",8);
   this.m_text_anchor=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_NAME_RES,this.m_canvas.ResourceName()); // Graphical resource name
      this.SetProperty(CANV_ELEMENT_PROP_CHART_ID,CGBaseObj::ChartID());         // Chart ID
      this.SetProperty(CANV_ELEMENT_PROP_WND_NUM,CGBaseObj::SubWindow());        // Chart subwindow index
      this.SetProperty(CANV_ELEMENT_PROP_NAME_OBJ,CGBaseObj::Name());            // Element object name
      this.SetProperty(CANV_ELEMENT_PROP_TYPE,element_type);                     // Graphical element type
      this.SetProperty(CANV_ELEMENT_PROP_ID,element_id);                         // Element ID
      this.SetProperty(CANV_ELEMENT_PROP_NUM,element_num);                       // Element index in the list
      this.SetProperty(CANV_ELEMENT_PROP_COORD_X,x);                             // Element's X coordinate on the chart
      this.SetProperty(CANV_ELEMENT_PROP_COORD_Y,y);                             // Element's Y coordinate on the chart
      this.SetProperty(CANV_ELEMENT_PROP_WIDTH,w);                               // Element width
      this.SetProperty(CANV_ELEMENT_PROP_HEIGHT,h);                              // Element height
      this.SetProperty(CANV_ELEMENT_PROP_ACT_SHIFT_LEFT,0);                      // Active area offset from the left edge of the element
      this.SetProperty(CANV_ELEMENT_PROP_ACT_SHIFT_TOP,0);                       // Active area offset from the upper edge of the element
      this.SetProperty(CANV_ELEMENT_PROP_ACT_SHIFT_RIGHT,0);                     // Active area offset from the right edge of the element
      this.SetProperty(CANV_ELEMENT_PROP_ACT_SHIFT_BOTTOM,0);                    // Active area offset from the bottom edge of the element
      this.SetProperty(CANV_ELEMENT_PROP_MOVABLE,movable);                       // Element moveability flag
      this.SetProperty(CANV_ELEMENT_PROP_ACTIVE,activity);                       // Element activity flag
      this.SetProperty(CANV_ELEMENT_PROP_RIGHT,this.RightEdge());                // Element right border
      this.SetProperty(CANV_ELEMENT_PROP_BOTTOM,this.BottomEdge());              // Element bottom border
      this.SetProperty(CANV_ELEMENT_PROP_COORD_ACT_X,this.ActiveAreaLeft());     // X coordinate of the element active area
      this.SetProperty(CANV_ELEMENT_PROP_COORD_ACT_Y,this.ActiveAreaTop());      // Y coordinate of the element active area
      this.SetProperty(CANV_ELEMENT_PROP_ACT_RIGHT,this.ActiveAreaRight());      // Right border of the element active area
      this.SetProperty(CANV_ELEMENT_PROP_ACT_BOTTOM,this.ActiveAreaBottom());    // Bottom border of the element active area
     }
   else
     {
      ::Print(CMessage::Text(MSG_LIB_SYS_FAILED_CREATE_ELM_OBJ),this.m_name);
     }
  }
//+------------------------------------------------------------------+

Der neue geschützte paramterische Konstruktor unterscheidet sich nicht von dem oben besprochenen:

//+------------------------------------------------------------------+
//| 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_chart_color_bg=(color)::ChartGetInteger(chart_id,CHART_COLOR_BACKGROUND);
   this.m_name=this.m_name_prefix+name;
   this.m_chart_id=chart_id;
   this.m_subwindow=wnd_num;
   this.m_type=element_type;
   this.SetFont("Calibri",8);
   this.m_text_anchor=0;
   this.m_color_bg=NULL_COLOR;
   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_NAME_RES,this.m_canvas.ResourceName()); // Graphical resource name
      this.SetProperty(CANV_ELEMENT_PROP_CHART_ID,CGBaseObj::ChartID());         // Chart ID
      this.SetProperty(CANV_ELEMENT_PROP_WND_NUM,CGBaseObj::SubWindow());        // Chart subwindow index
      this.SetProperty(CANV_ELEMENT_PROP_NAME_OBJ,CGBaseObj::Name());            // Element object name
      this.SetProperty(CANV_ELEMENT_PROP_TYPE,element_type);                     // Graphical element type
      this.SetProperty(CANV_ELEMENT_PROP_ID,0);                                  // Element ID
      this.SetProperty(CANV_ELEMENT_PROP_NUM,0);                                 // Element index in the list
      this.SetProperty(CANV_ELEMENT_PROP_COORD_X,x);                             // Element's X coordinate on the chart
      this.SetProperty(CANV_ELEMENT_PROP_COORD_Y,y);                             // Element's Y coordinate on the chart
      this.SetProperty(CANV_ELEMENT_PROP_WIDTH,w);                               // Element width
      this.SetProperty(CANV_ELEMENT_PROP_HEIGHT,h);                              // Element height
      this.SetProperty(CANV_ELEMENT_PROP_ACT_SHIFT_LEFT,0);                      // Active area offset from the left edge of the element
      this.SetProperty(CANV_ELEMENT_PROP_ACT_SHIFT_TOP,0);                       // Active area offset from the upper edge of the element
      this.SetProperty(CANV_ELEMENT_PROP_ACT_SHIFT_RIGHT,0);                     // Active area offset from the right edge of the element
      this.SetProperty(CANV_ELEMENT_PROP_ACT_SHIFT_BOTTOM,0);                    // Active area offset from the bottom edge of the element
      this.SetProperty(CANV_ELEMENT_PROP_MOVABLE,false);                         // Element moveability flag
      this.SetProperty(CANV_ELEMENT_PROP_ACTIVE,false);                          // Element activity flag
      this.SetProperty(CANV_ELEMENT_PROP_RIGHT,this.RightEdge());                // Element right border
      this.SetProperty(CANV_ELEMENT_PROP_BOTTOM,this.BottomEdge());              // Element bottom border
      this.SetProperty(CANV_ELEMENT_PROP_COORD_ACT_X,this.ActiveAreaLeft());     // X coordinate of the element active area
      this.SetProperty(CANV_ELEMENT_PROP_COORD_ACT_Y,this.ActiveAreaTop());      // Y coordinate of the element active area
      this.SetProperty(CANV_ELEMENT_PROP_ACT_RIGHT,this.ActiveAreaRight());      // Right border of the element active area
      this.SetProperty(CANV_ELEMENT_PROP_ACT_BOTTOM,this.ActiveAreaBottom());    // Bottom border of the element active area
     }
   else
     {
      ::Print(CMessage::Text(MSG_LIB_SYS_FAILED_CREATE_ELM_OBJ),this.m_name);
     }
  }
//+------------------------------------------------------------------+

Es werden weniger Werte übergeben, die Hintergrundfarbe des Elements auf ein transparentes Weiß gesetzt und die volle Element-Transparenz gesetzt.

Entfernen wir die jetzt unnötigen Zeilen aus der Methode zur Erstellung der Objektstruktur:

   this.m_struct_obj.act_shift_bottom=(int)this.GetProperty(CANV_ELEMENT_PROP_ACT_SHIFT_BOTTOM);// Active area offset from the bottom edge of the element
   this.m_struct_obj.opacity=(uchar)this.GetProperty(CANV_ELEMENT_PROP_OPACITY);                // Element opacity
   this.m_struct_obj.color_bg=(color)this.GetProperty(CANV_ELEMENT_PROP_COLOR_BG);              // Element background color
   this.m_struct_obj.movable=(bool)this.GetProperty(CANV_ELEMENT_PROP_MOVABLE);                 // Element moveability flag

und fügen das Speichern der Parameter aus den neuen Variablen unten hinzu:

//+------------------------------------------------------------------+
//| Create the object structure                                      |
//+------------------------------------------------------------------+
bool CGCnvElement::ObjectToStruct(void)
  {
//--- Save integer properties
   this.m_struct_obj.id=(int)this.GetProperty(CANV_ELEMENT_PROP_ID);                            // Element ID
   this.m_struct_obj.type=(int)this.GetProperty(CANV_ELEMENT_PROP_TYPE);                        // Graphical element type
   this.m_struct_obj.number=(int)this.GetProperty(CANV_ELEMENT_PROP_NUM);                       // Eleemnt ID in the list
   this.m_struct_obj.chart_id=this.GetProperty(CANV_ELEMENT_PROP_CHART_ID);                     // Chart ID
   this.m_struct_obj.subwindow=(int)this.GetProperty(CANV_ELEMENT_PROP_WND_NUM);                // Chart subwindow index
   this.m_struct_obj.coord_x=(int)this.GetProperty(CANV_ELEMENT_PROP_COORD_X);                  // Form's X coordinate on the chart
   this.m_struct_obj.coord_y=(int)this.GetProperty(CANV_ELEMENT_PROP_COORD_Y);                  // Form's Y coordinate on the chart
   this.m_struct_obj.width=(int)this.GetProperty(CANV_ELEMENT_PROP_WIDTH);                      // Element width
   this.m_struct_obj.height=(int)this.GetProperty(CANV_ELEMENT_PROP_HEIGHT);                    // Element height
   this.m_struct_obj.edge_right=(int)this.GetProperty(CANV_ELEMENT_PROP_RIGHT);                 // Element right edge
   this.m_struct_obj.edge_bottom=(int)this.GetProperty(CANV_ELEMENT_PROP_BOTTOM);               // Element bottom edge
   this.m_struct_obj.act_shift_left=(int)this.GetProperty(CANV_ELEMENT_PROP_ACT_SHIFT_LEFT);    // Active area offset from the left edge of the element
   this.m_struct_obj.act_shift_top=(int)this.GetProperty(CANV_ELEMENT_PROP_ACT_SHIFT_TOP);      // Active area offset from the top edge of the element
   this.m_struct_obj.act_shift_right=(int)this.GetProperty(CANV_ELEMENT_PROP_ACT_SHIFT_RIGHT);  // Active area offset from the right edge of the element
   this.m_struct_obj.act_shift_bottom=(int)this.GetProperty(CANV_ELEMENT_PROP_ACT_SHIFT_BOTTOM);// Active area offset from the bottom edge of the element


   this.m_struct_obj.movable=(bool)this.GetProperty(CANV_ELEMENT_PROP_MOVABLE);                 // Element moveability flag
   this.m_struct_obj.active=(bool)this.GetProperty(CANV_ELEMENT_PROP_ACTIVE);                   // Element activity 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
   this.m_struct_obj.coord_act_right=(int)this.GetProperty(CANV_ELEMENT_PROP_ACT_RIGHT);        // Right border of the element active area
   this.m_struct_obj.coord_act_bottom=(int)this.GetProperty(CANV_ELEMENT_PROP_ACT_BOTTOM);      // Bottom border of the element active area
   this.m_struct_obj.color_bg=this.m_color_bg;                                                  // Element background color
   this.m_struct_obj.opacity=this.m_opacity;                                                    // Element opacity
//--- Save real properties

//--- Save string properties
   ::StringToCharArray(this.GetProperty(CANV_ELEMENT_PROP_NAME_OBJ),this.m_struct_obj.name_obj);// Graphical element object name
   ::StringToCharArray(this.GetProperty(CANV_ELEMENT_PROP_NAME_RES),this.m_struct_obj.name_res);// Graphical resource name
   //--- 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;
  }
//+------------------------------------------------------------------+

Das Gleiche geschieht in der Methode zum Erzeugen des Objekts aus der Struktur:

//+------------------------------------------------------------------+
//| Create the object from the structure                             |
//+------------------------------------------------------------------+
void CGCnvElement::StructToObject(void)
  {
//--- Save integer properties
   this.SetProperty(CANV_ELEMENT_PROP_ID,this.m_struct_obj.id);                                 // Element ID
   this.SetProperty(CANV_ELEMENT_PROP_TYPE,this.m_struct_obj.type);                             // Graphical element type
   this.SetProperty(CANV_ELEMENT_PROP_NUM,this.m_struct_obj.number);                            // Element index in the list
   this.SetProperty(CANV_ELEMENT_PROP_CHART_ID,this.m_struct_obj.chart_id);                     // Chart ID
   this.SetProperty(CANV_ELEMENT_PROP_WND_NUM,this.m_struct_obj.subwindow);                     // Chart subwindow index
   this.SetProperty(CANV_ELEMENT_PROP_COORD_X,this.m_struct_obj.coord_x);                       // Form's X coordinate on the chart
   this.SetProperty(CANV_ELEMENT_PROP_COORD_Y,this.m_struct_obj.coord_y);                       // Form's Y coordinate on the chart
   this.SetProperty(CANV_ELEMENT_PROP_WIDTH,this.m_struct_obj.width);                           // Element width
   this.SetProperty(CANV_ELEMENT_PROP_HEIGHT,this.m_struct_obj.height);                         // Element height
   this.SetProperty(CANV_ELEMENT_PROP_RIGHT,this.m_struct_obj.edge_right);                      // Element right edge
   this.SetProperty(CANV_ELEMENT_PROP_BOTTOM,this.m_struct_obj.edge_bottom);                    // Element bottom edge
   this.SetProperty(CANV_ELEMENT_PROP_ACT_SHIFT_LEFT,this.m_struct_obj.act_shift_left);         // Active area offset from the left edge of the element
   this.SetProperty(CANV_ELEMENT_PROP_ACT_SHIFT_TOP,this.m_struct_obj.act_shift_top);           // Active area offset from the upper edge of the element
   this.SetProperty(CANV_ELEMENT_PROP_ACT_SHIFT_RIGHT,this.m_struct_obj.act_shift_right);       // Active area offset from the right edge of the element
   this.SetProperty(CANV_ELEMENT_PROP_ACT_SHIFT_BOTTOM,this.m_struct_obj.act_shift_bottom);     // Active area offset from the bottom edge of the element
                                                                                           
                                                                                           
   this.SetProperty(CANV_ELEMENT_PROP_MOVABLE,this.m_struct_obj.movable);                       // Element moveability flag
   this.SetProperty(CANV_ELEMENT_PROP_ACTIVE,this.m_struct_obj.active);                         // Element activity 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
//--- Save real properties

//--- 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, die das grafische Objektelement erstellt, wird der Objekthintergrund nun komplett gelöscht und mit weißer, transparenter Farbe gefüllt:

//+------------------------------------------------------------------+
//| 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
                         
  {
   if(this.m_canvas.CreateBitmapLabel(chart_id,wnd_num,name,x,y,w,h,COLOR_FORMAT_ARGB_NORMALIZE))
     {
      this.Erase(NULL_COLOR);
      this.m_canvas.Update(redraw);
      this.m_shift_y=(int)::ChartGetInteger(chart_id,CHART_WINDOW_YDISTANCE,wnd_num);
      return true;
     }
   return false;
  }
//+------------------------------------------------------------------+

In der Methode, die die Transparenz des Elements setzt, wird die Transparenz nun in der Variable gesetzt, anstatt eine Eigenschaft des Objekts zu entfernen:

//+------------------------------------------------------------------+
//| Set the element opacity                                          |
//+------------------------------------------------------------------+
void CGCnvElement::SetOpacity(const uchar value,const bool redraw=false)
  {
   this.m_canvas.TransparentLevelSet(value);
   this.m_opacity=value;
   this.m_canvas.Update(redraw);
  }
//+------------------------------------------------------------------+

Die neue Methode ändert die Farbhelligkeit um den angegebenen Wert:

//+------------------------------------------------------------------+
//| Change the color lightness by the specified amount               |
//+------------------------------------------------------------------+
uint CGCnvElement::ChangeColorLightness(const uint clr,const double change_value)
  {
   if(change_value==0.0)
      return clr;
   double a=GETRGBA(clr);
   double r=GETRGBR(clr);
   double g=GETRGBG(clr);
   double b=GETRGBB(clr);
   double h=0,s=0,l=0;
   CColors::RGBtoHSL(r,g,b,h,s,l);
   double nl=l+change_value;
   if(nl>1.0) nl=1.0;
   if(nl<0.0) nl=0.0;
   CColors::HSLtoRGB(h,s,nl,r,g,b);
   return ARGB(a,r,g,b);
  }
//+------------------------------------------------------------------+

Hier:
Wird der übergebene Wert geprüft, um den die Helligkeit verändert werden soll. Wenn Null übergeben wird, soll nichts geändert werden — Farbe wird unverändert zurückgeben.
Als Nächstes wird jede der ARGB-Farbkomponenten der an die Methode übergebenen Farbe empfangen und die RGB-Komponenten in das HSL-Farbmodell konvertiert.
Nach der Konvertierung der Werte der einzelnen Komponenten werden die HSL-Modelle in die entsprechenden Variablen gesetzt (wir benötigen die l-Komponente).
Der an die Methode übergebene Wert (Änderungswert kann von -1,0 bis 1,0 variieren) und anpassen, wenn er über akzeptable Wertebereiche hinausgeht.
Als Nächste wird das HSL-Modells zurück nach RGB konvertiert und das ARGB-Modells zurückgegeben, das sich aus den neuen Farbkomponenten ergibt, die durch die Konvertierung vom HSL-Modell nach RGB erzeugt wurden.


Typen der Farbschemata und der Formulare

Die Bibliothek soll die Erstellung verschiedener Objekte unterstützen — grafische Elemente, entsprechende Formulare, Fenster usw. Jedes Formular, Fenster oder Bild (Rahmen, Trennlinien, Dropdown-Listen usw.) kann unterschiedliche Darstellungsstile haben. Es wäre jedoch seltsam, verschiedene Objekte mit unterschiedlichen Zeichenstilen, Farben und Dekorationen innerhalb eines einzigen Programms zu haben.
Um die Entwicklung von identisch aussehenden Objekten, die zu einem einzigen Programm gehören, zu vereinfachen, werde ich Zeichenstile, Objekttypen und Farbschemata einführen. Dies wird es dem Endanwender ermöglichen, den gewünschten Stil und das Farbschema in den Programmeinstellungen auszuwählen, während der Programmierer sich um all das nicht allzu sehr kümmern muss — ausgewählte Themen und Stile werden sofort alle Objekte nach einem einzigen Kriterium umgestalten. Ich muss nur die notwendigen Änderungen und Ergänzungen in der Datei mit den grafischen Einstellungen vornehmen, in der alle notwendigen Farben sowie Objekt- und Primitivparameter enthalten sind.

Eine solche Taktik habe ich bereits bei der Entwicklung der Bibliothek Nachrichtenklasse angewendet. Sie enthält die Liste der Nachrichtenindizes und das Array der Texte, die den Nachrichtenindizes entsprechen. Das erste, was ich normalerweise zu Beginn eines jeden Artikels mache, ist das Setzen neuer Daten.
Die Datei der grafischen Einstellungen wird auf die gleiche Weise aufgebaut: Sie enthält die Enumeration der Farbthemen und Objektstile sowie die entsprechenden Arrays, die nach und nach neue Parameter und deren Werte für jede neu hinzugefügte Eigenschaft, jedes Thema oder jede Farbe erhalten.

Legen wir im Stammordner \MQL5\Include\DoEasy\ die neue Include-Datei GraphINI.mqh an und ergänzen die Anzahl der Farbschemata:

//+------------------------------------------------------------------+
//|                                                     GraphINI.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"
//+------------------------------------------------------------------+
//| Macro substitutions                                              |
//+------------------------------------------------------------------+
#define TOTAL_COLOR_THEMES             (2)      // Number of color schemes
//+------------------------------------------------------------------+

Zwei Farbschemata werden ausreichen, um die Verwendung der Einstellungsdatei zu demonstrieren. Ich werde ihre Anzahl später erhöhen.

Unten setze ich die Indizes der Farbschemata und die Indizes der Parameter eines Themas. Jedes Schema wird die gleiche Anzahl von Parametern haben:

//+------------------------------------------------------------------+
//|                                                     GraphINI.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"
//+------------------------------------------------------------------+
//| Macro substitutions                                              |
//+------------------------------------------------------------------+
#define TOTAL_COLOR_THEMES             (2)      // Number of color schemes
//+------------------------------------------------------------------+
//| Enumerations                                                     |
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| List of color scheme indices                                     |
//+------------------------------------------------------------------+
enum ENUM_COLOR_THEMES
  {
   COLOR_THEME_BLUE_STEEL,                      // Blue steel
   COLOR_THEME_LIGHT_CYAN_GRAY,                 // Light cyan gray
  };
//+------------------------------------------------------------------+
//| 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_FRAME_OUTER,          // Form outer frame color
   COLOR_THEME_COLOR_FORM_SHADOW,               // Form shadow color
  };
#define TOTAL_COLOR_THEME_COLORS       (4)      // Number of parameters in the color theme
//+------------------------------------------------------------------+

Der Zugriff auf die einzelnen Farbschemata und die erforderlichen Parameter erfolgt bequem über die Namen der Enumeration-Konstanten.

Im Folgenden schreiben wir ein zweidimensionales Array, das in der ersten Dimension Farbschemata und in der zweiten Dimension Farbparameter-Indizes zum Zeichnen verschiedener Objekteigenschaften enthält:

//+------------------------------------------------------------------+
//|                                                     GraphINI.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"
//+------------------------------------------------------------------+
//| Macro substitutions                                              |
//+------------------------------------------------------------------+
#define TOTAL_COLOR_THEMES             (2)      // Number of color schemes
//+------------------------------------------------------------------+
//| Enumerations                                                     |
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| List of color scheme indices                                     |
//+------------------------------------------------------------------+
enum ENUM_COLOR_THEMES
  {
   COLOR_THEME_BLUE_STEEL,                      // Blue steel
   COLOR_THEME_LIGHT_CYAN_GRAY,                 // Light cyan gray
  };
//+------------------------------------------------------------------+
//| 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_FRAME_OUTER,          // Form outer frame color
   COLOR_THEME_COLOR_FORM_SHADOW,               // Form shadow color
  };
#define TOTAL_COLOR_THEME_COLORS       (4)      // 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 outer frame color
      C'46,85,117',                             // Form shadow 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 outer frame color
      C'130,147,153',                           // Form shadow color
   },
  };
//+------------------------------------------------------------------+

Dies ist das Array, das nach und nach neue Farben für jeden neu hinzugefügten grafischen Objektparameter erhalten soll, dessen Farbe vom gewählten Farbschema abhängen soll.

Als Nächstes fügen wir die Enumerationen von Glättungsarten beim Zeichnen von Primitiven, Rahmenstilen, Typen und Formularstilen hinzu, gefolgt von den Enumerationen von Formularstil-Eigenschaftsindizes und ihren Parametern, ähnlich denen, die für Farbschemata entworfen wurden:

//+------------------------------------------------------------------+
//| Smoothing types                                                  |
//+------------------------------------------------------------------+
enum ENUM_SMOOTHING_TYPE
  {
   SMOOTHING_TYPE_NONE,                         // No smoothing
   SMOOTHING_TYPE_AA,                           // Anti-aliasing
   SMOOTHING_TYPE_WU,                           // Wu
   SMOOTHING_TYPE_THICK,                        // Thick
   SMOOTHING_TYPE_DUAL,                         // Dual
  };
//+------------------------------------------------------------------+
//| Frame styles                                                     |
//+------------------------------------------------------------------+
enum ENUM_FRAME_STYLE
  {
   FRAME_STYLE_SIMPLE,                          // Simple frame
   FRAME_STYLE_FLAT,                            // Flat frame
   FRAME_STYLE_BEVEL,                           // Embossed (convex)
   FRAME_STYLE_STAMP,                           // Embossed (concave)
  };
//+------------------------------------------------------------------+
//| Form types                                                       |
//+------------------------------------------------------------------+
enum ENUM_FORM_TYPE
  {
   FORM_TYPE_SQUARE,                            // Rectangular
  };
//+------------------------------------------------------------------+
//| Form styles                                                      |
//+------------------------------------------------------------------+
enum ENUM_FORM_STYLE
  {
   FORM_STYLE_FLAT,                             // Flat form
   FORM_STYLE_BEVEL,                            // Embossed form
  };
#define TOTAL_FORM_STYLES
//+------------------------------------------------------------------+
//| List of form style parameter indices                             |
//+------------------------------------------------------------------+
enum ENUM_FORM_STYLE_PARAMS
  {
   FORM_STYLE_FRAME_WIDTH_LEFT,                 // Form frame width to the left
   FORM_STYLE_FRAME_WIDTH_RIGHT,                // Form frame width to the right
   FORM_STYLE_FRAME_WIDTH_TOP,                  // Form frame width on top
   FORM_STYLE_FRAME_WIDTH_BOTTOM,               // Form frame width below
   FORM_STYLE_FRAME_SHADOW_OPACITY,             // Shadow opacity
  };
#define TOTAL_FORM_STYLE_PARAMS        (5)      // Number of form style parameters
//+------------------------------------------------------------------+
//| Array containing form style parameters                           |
//+------------------------------------------------------------------+
int array_form_style[TOTAL_FORM_STYLES][TOTAL_FORM_STYLE_PARAMS]=
  {
//--- "Flat form" style parameters
   {
      3,                                        // Form frame width to the left
      3,                                        // Form frame width to the right
      3,                                        // Form frame width on top
      3,                                        // Form frame width below
      80,                                       // Shadow opacity
   },
//--- "Embossed form" style parameters
   {
      4,                                        // Form frame width to the left
      4,                                        // Form frame width to the right
      4,                                        // Form frame width on top
      4,                                        // Form frame width below
      100,                                      // Shadow opacity
   },
  };
//+------------------------------------------------------------------+

Das zweite Array, dessen Konstruktionslogik mit dem Farbschema-Array identisch ist, wird nach und nach neue Parameter für die Konstruktion von Elementen, Formularen, Fenstern und anderen Objekten erhalten, deren Parameter von einem gewählten Stil für die Gestaltung des Erscheinungsbildes der Objekte abhängen sollen.

Die neuen Enumerationen für Programmeingaben fügen wir der Datei \MQL5\Include\DoEasy\InpData.mqh hinzu, um den erforderlichen Stil der Konstruktion von Objekten und ein Farbschema auswählen zu können. Gleich zu Beginn binden wir die neu erstellte Datei GraphINI.mqh ein:

//+------------------------------------------------------------------+
//|                                                      InpData.mqh |
//|                        Copyright 2020, MetaQuotes Software Corp. |
//|                             https://mql5.com/en/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2020, MetaQuotes Software Corp."
#property link      "https://mql5.com/en/users/artmedia70"
//+------------------------------------------------------------------+
//| Include files                                                    |
//+------------------------------------------------------------------+
#include "GraphINI.mqh"
//+------------------------------------------------------------------+

Im Codeblock für die Kompilierung in Englisch und Russisch fügen wir die neuen Eingaben der Enumerationen für die Auswahl eines Farbschemas hinzu:

//+------------------------------------------------------------------+
//|                                                      InpData.mqh |
//|                        Copyright 2020, MetaQuotes Software Corp. |
//|                             https://mql5.com/en/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2020, MetaQuotes Software Corp."
#property link      "https://mql5.com/en/users/artmedia70"
//+------------------------------------------------------------------+
//| Include files                                                    |
//+------------------------------------------------------------------+
#include "GraphINI.mqh"
//+------------------------------------------------------------------+
//| Macro substitutions                                              |
//+------------------------------------------------------------------+
#define COMPILE_EN // Comment out the string for compilation in Russian 
//+------------------------------------------------------------------+
//| Input enumerations                                               |
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| English language inputs                                          |
//+------------------------------------------------------------------+
#ifdef COMPILE_EN
//+------------------------------------------------------------------+
//| Modes of working with symbols                                    |
//+------------------------------------------------------------------+
enum ENUM_SYMBOLS_MODE
  {
   SYMBOLS_MODE_CURRENT,                              // Work only with the current Symbol
   SYMBOLS_MODE_DEFINES,                              // Work with a given list of Symbols
   SYMBOLS_MODE_MARKET_WATCH,                         // Working with Symbols from the "Market Watch" window
   SYMBOLS_MODE_ALL                                   // Work with a complete list of Symbols
  };
//+------------------------------------------------------------------+
//| Mode of working with timeframes                                  |
//+------------------------------------------------------------------+
enum ENUM_TIMEFRAMES_MODE
  {
   TIMEFRAMES_MODE_CURRENT,                           // Work only with the current timeframe
   TIMEFRAMES_MODE_LIST,                              // Work with a given list of timeframes
   TIMEFRAMES_MODE_ALL                                // Work with a complete list of timeframes
  };
//+------------------------------------------------------------------+
//| "Yes"/"No"                                                       |
//+------------------------------------------------------------------+
enum ENUM_INPUT_YES_NO
  {
   INPUT_NO  = 0,                                     // No
   INPUT_YES = 1                                      // Yes
  };
//+------------------------------------------------------------------+
//| Select color themes                                              |
//+------------------------------------------------------------------+
enum ENUM_INPUT_COLOR_THEME
  {
   INPUT_COLOR_THEME_BLUE_STEEL,                      // Blue steel
   INPUT_COLOR_THEME_LIGHT_CYAN_GRAY,                 // Light cyan gray
  };
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| Russian language inputs                                          |
//+------------------------------------------------------------------+
#else  
//+------------------------------------------------------------------+
//| Modes of working with symbols                                    |
//+------------------------------------------------------------------+
enum ENUM_SYMBOLS_MODE
  {
   SYMBOLS_MODE_CURRENT,                              // Работа только с текущим символом
   SYMBOLS_MODE_DEFINES,                              // Работа с заданным списком символов
   SYMBOLS_MODE_MARKET_WATCH,                         // Работа с символами из окна "Обзор рынка"
   SYMBOLS_MODE_ALL                                   // Работа с полным списком символов
  };
//+------------------------------------------------------------------+
//| Mode of working with timeframes                                  |
//+------------------------------------------------------------------+
enum ENUM_TIMEFRAMES_MODE
  {
   TIMEFRAMES_MODE_CURRENT,                           // Работа только с текущим таймфреймом
   TIMEFRAMES_MODE_LIST,                              // Работа с заданным списком таймфреймов
   TIMEFRAMES_MODE_ALL                                // Работа с полным списком таймфреймов
  };
//+------------------------------------------------------------------+
//| "Да"/"Нет"                                                       |
//+------------------------------------------------------------------+
enum ENUM_INPUT_YES_NO
  {
   INPUT_NO  = 0,                                     // Нет
   INPUT_YES = 1                                      // Да
  };
//+------------------------------------------------------------------+
//| Select color themes                                              |
//+------------------------------------------------------------------+
enum ENUM_COLOR_THEME
  {
   COLOR_THEME_BLUE_STEEL,                            // Голубая сталь
   COLOR_THEME_LIGHT_CYAN_GRAY,                       // Светлый серо-циановый
  };
//+------------------------------------------------------------------+
#endif 
//+------------------------------------------------------------------+

Damit können wir beim Start des Programms das gewünschte Farbschema auswählen. Später werde ich die Auswahl von Objektzeichnungsstilen und Konstruktionstypen hinzufügen.

Objektklasse des Formulars

Das Formularobjekt ist eine erweiterte Version eines grafischen Objekts. Es erlaubt das Zeichnen von "dimensionalen" Rahmen und anderen Primitiven, sowie das Anhängen anderer Elemente an es. Natürlich können Sie alles, was Sie brauchen, "von Hand" zeichnen, aber das Formular erlaubt Ihnen, Ihre Arbeit zu automatisieren.

In E:\MetaQuotes\MetaTrader 5\MQL5\Include\DoEasy\Objects\Graph\ erstellen wir die neue Datei Form.mqh der Klasse CForm. Die Klasse sollte vom grafischen Elementobjekt geerbt werden, d. h. die Elementobjektdatei sollte ebenfalls enthalten sein:

//+------------------------------------------------------------------+
//|                                                         Form.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 "GCnvElement.mqh"
//+------------------------------------------------------------------+
//| Form object class                                                |
//+------------------------------------------------------------------+
class CForm : public CGCnvElement
  {
  }

Im privaten Bereich der Klasse deklarieren wir die benötigten Objekte, Variablen und Klassenhilfsmethoden:

//+------------------------------------------------------------------+
//| Form object class                                                |
//+------------------------------------------------------------------+
class CForm : public CGCnvElement
  {
private:
   CArrayObj         m_list_elements;                          // List of attached elements
   CGCnvElement     *m_shadow_obj;                             // Pointer to the shadow object
   color             m_color_frame;                            // Form frame color
   color             m_color_shadow;                           // Form shadow 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

//--- Initialize the variables
   void              Initialize(void);

//--- 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);
   
public:

Der öffentliche Teil der Klasse enthält die für die Bibliotheksobjekte standardmäßigen Methoden und mehrere Konstruktoren — die Standardmethoden sowie die, die es dem Nutzer ermöglichen, ein Formular-Objekt für ein bestimmtes Chart und Unterfenster zu erstellen, für ein bestimmtes Unterfenster des aktuellen Charts und für das aktuelle Chart im Hauptfenster:

public:
   //--- Constructors
                     CForm(const long chart_id,
                           const int subwindow,
                           const string name,
                           const int x,
                           const int y,
                           const int w,
                           const int h);
                     CForm(const int subwindow,
                           const string name,
                           const int x,
                           const int y,
                           const int w,
                           const int h);
                     CForm(const string name,
                           const int x,
                           const int y,
                           const int w,
                           const int h);
                     CForm() { this.Initialize(); }
//--- Destructor
                    ~CForm();
                           
//--- Supported form properties (1) integer and (2) string ones
   virtual bool      SupportProperty(ENUM_CANV_ELEMENT_PROP_INTEGER property) { return true; }
   virtual bool      SupportProperty(ENUM_CANV_ELEMENT_PROP_STRING property)  { return true; }
   
//--- Return (1) the list of attached objects and (2) the shadow object
   CArrayObj        *GetList(void)                                            { return &this.m_list_elements;  }
   CGCnvElement     *GetShadowObj(void)                                       { return this.m_shadow_obj;      }

Im Folgenden werden die Methoden zur Arbeit mit dem Formularobjekt beschrieben:

//--- Set the form (1) color scheme and (2) style
   virtual void      SetColorTheme(const ENUM_COLOR_THEMES theme,const uchar opacity);
   virtual void      SetFormStyle(const ENUM_FORM_STYLE style,
                                  const ENUM_COLOR_THEMES theme,
                                  const uchar opacity,
                                  const bool shadow=false,
                                  const bool redraw=false);
   
//--- 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);

//--- Create a shadow object
   void              CreateShadow(const uchar opacity);
//--- Draw an object shadow
   void              DrawShadow(const uchar opacity);

//--- Draw the form frame
   void              DrawFormFrame(const int wd_top,                          // Frame upper segment width
                                   const int wd_bottom,                       // Frame lower segment width
                                   const int wd_left,                         // Frame left segment width
                                   const int wd_right,                        // Frame right segment width
                                   const color colour,                        // Frame color
                                   const uchar opacity,                       // Frame opacity
                                   const ENUM_FRAME_STYLE style);             // Frame style
//--- Draw a simple frame
   void              DrawFrameSimple(const int x,                             // X coordinate relative to the form
                                     const int y,                             // Y coordinate relative to the form
                                     const int width,                         // Frame width
                                     const int height,                        // Frame height
                                     const int wd_top,                        // Frame upper segment width
                                     const int wd_bottom,                     // Frame lower segment width
                                     const int wd_left,                       // Frame left segment width
                                     const int wd_right,                      // Frame right segment width
                                     const color colour,                      // Frame color
                                     const uchar opacity);                    // Frame opacity
//--- Draw a flat frame
   void              DrawFrameFlat(const int x,                               // X coordinate relative to the form
                                   const int y,                               // Y coordinate relative to the form
                                   const int width,                           // Frame width
                                   const int height,                          // Frame height
                                   const int wd_top,                          // Frame upper segment width
                                   const int wd_bottom,                       // Frame lower segment width
                                   const int wd_left,                         // Frame left segment width
                                   const int wd_right,                        // Frame right segment width
                                   const color colour,                        // Frame color
                                   const uchar opacity);                      // Frame opacity

//--- Draw an embossed (convex) frame
   void              DrawFrameBevel(const int x,                              // X coordinate relative to the form
                                    const int y,                              // Y coordinate relative to the form
                                    const int width,                          // Frame width
                                    const int height,                         // Frame height
                                    const int wd_top,                         // Frame upper segment width
                                    const int wd_bottom,                      // Frame lower segment width
                                    const int wd_left,                        // Frame left segment width
                                    const int wd_right,                       // Frame right segment width
                                    const color colour,                       // Frame color
                                    const uchar opacity);                     // Frame opacity

//--- Draw an embossed (concave) frame
   void              DrawFrameStamp(const int x,                              // X coordinate relative to the form
                                    const int y,                              // Y coordinate relative to the form
                                    const int width,                          // Frame width
                                    const int height,                         // Frame height
                                    const int wd_top,                         // Frame upper segment width
                                    const int wd_bottom,                      // Frame lower segment width
                                    const int wd_left,                        // Frame left segment width
                                    const int wd_right,                       // Frame right segment width
                                    const color colour,                       // Frame color
                                    const uchar opacity);                     // Frame opacity

//--- Draw a simple field
   void              DrawFieldFlat(const int x,                               // X coordinate relative to the form
                                   const int y,                               // Y coordinate relative to the form
                                   const int width,                           // Field width
                                   const int height,                          // Field height
                                   const color colour,                        // Field color
                                   const uchar opacity);                      // Field opacity

//--- Draw an embossed (convex) field
   void              DrawFieldBevel(const int x,                              // X coordinate relative to the form
                                    const int y,                              // Y coordinate relative to the form
                                    const int width,                          // Field width
                                    const int height,                         // Field height
                                    const color colour,                       // Field color
                                    const uchar opacity);                     // Field opacity

//--- Draw an embossed (concave) field
   void              DrawFieldStamp(const int x,                              // X coordinate relative to the form
                                    const int y,                              // Y coordinate relative to the form
                                    const int width,                          // Field width
                                    const int height,                         // Field height
                                    const color colour,                       // Field color
                                    const uchar opacity);                     // Field opacity
   
//+------------------------------------------------------------------+
//| Methods of simplified access to object properties                |
//+------------------------------------------------------------------+
//--- (1) Set and (2) get the form frame color
   void              SetColorFrame(const color colour)                        { this.m_color_frame=colour;  }
   color             ColorFrame(void)                                   const { return this.m_color_frame;  }
//--- (1) Set and (2) return the form shadow color
   void              SetColorShadow(const color colour)                       { this.m_color_shadow=colour; }
   color             ColorShadow(void)                                  const { return this.m_color_shadow; }

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

Betrachten wir die deklarierten Methoden im Detail.

Der Konstruktor, der die ID des Charts und des Unterfensters angibt:

//+------------------------------------------------------------------+
//| Constructor indicating the chart and subwindow ID                |
//+------------------------------------------------------------------+
CForm::CForm(const long chart_id,
             const int subwindow,
             const string name,
             const int x,
             const int y,
             const int w,
             const int h) : CGCnvElement(GRAPH_ELEMENT_TYPE_FORM,chart_id,subwindow,name,x,y,w,h)
  {
   this.Initialize();
  }
//+------------------------------------------------------------------+

Der Konstruktor erhält die ID des Charts und den Index des Unterfensters, mit dem ein Formularobjekt erzeugt wird, seinen Namen, die Koordinaten des linken oberen Formularwinkels und seine Größe. In der Initialisierung wird der Konstruktor der Elementobjektklasse aufgerufen und der Typ des Formularobjekts angegeben. Im Hauptteil der Klasse wird die Initialisierungsmethode aufgerufen.

Aktueller Chart-Konstruktor mit Angabe des Unterfensters:

//+------------------------------------------------------------------+
//| Current chart constructor specifying the subwindow               |
//+------------------------------------------------------------------+
CForm::CForm(const int subwindow,
             const string name,
             const int x,
             const int y,
             const int w,
             const int h) : CGCnvElement(GRAPH_ELEMENT_TYPE_FORM,::ChartID(),subwindow,name,x,y,w,h)
  {
   this.Initialize();
  }
//+------------------------------------------------------------------+

Der Konstruktor erhält die Nummer des Teilfensters, auf dem das Formularobjekt erstellt werden soll (das aktuelle Chart), den Namen des Formularobjekts, die Koordinaten des linken oberen Formularwinkels und seine Größe. In der Initialisierungsliste wird der Konstruktor der Elementobjektklasse aufgerufen und dabei der Typ des Formularobjekts und die aktuelle Chart-ID angegeben. Im Hauptteil der Klasse wird die Initialisierungsmethode aufgerufen.

Der Konstruktor auf dem aktuellen Chart im Hauptfenster des Charts:

//+------------------------------------------------------------------+
//| Constructor on the current chart in the main chart window        |
//+------------------------------------------------------------------+
CForm::CForm(const string name,
             const int x,
             const int y,
             const int w,
             const int h) : CGCnvElement(GRAPH_ELEMENT_TYPE_FORM,::ChartID(),0,name,x,y,w,h)
  {
   this.Initialize();
  }
//+------------------------------------------------------------------+

Der Konstruktor erhält den Namen des Formularobjekts, die Koordinaten des linken oberen Formularwinkels und seine Größe. In der Initialisierung wird der Konstruktor der Elementobjektklasse aufgerufen und der Typ des Formularobjekts und die aktuelle Chart-ID sowie den Hauptfensterindex (0) angegeben. Im Hauptteil der Klasse wird die Initialisierungsmethode aufgerufen.

Im Destruktor der Klasse wird die Gültigkeit des Zeigers auf das Schattenobjekt geprüft und das Objekt, falls es existiert, entfernt:

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

Die Methode der Variableninitialisierung:

//+------------------------------------------------------------------+
//| Initialize the variables                                         |
//+------------------------------------------------------------------+
void CForm::Initialize(void)
  {
   this.m_list_elements.Clear();
   this.m_list_elements.Sort();
   this.m_shadow_obj=NULL;
   this.m_shadow=false;
   this.m_frame_width_right=2;
   this.m_frame_width_left=2;
   this.m_frame_width_top=2;
   this.m_frame_width_bottom=2;
  }
//+------------------------------------------------------------------+

Hier lösche ich die Liste der an das Formular angehängten Elemente, setze das Flag für die sortierte Liste und gebe die Standardwerte für den Zeiger auf das Formularobjekt (NULL), das Flag für das Zeichnen des Schattens (false) und die Größe des Formularrahmens (zwei Pixel auf jeder Seite) an.

Die private Methode, die ein neues grafisches Objekt erzeugt:

//+------------------------------------------------------------------+
//| Create a new graphical object                                    |
//+------------------------------------------------------------------+
CGCnvElement *CForm::CreateNewGObject(const ENUM_GRAPH_ELEMENT_TYPE type,
                                      const int obj_num,
                                      const string obj_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)
  {
   int pos=::StringLen(::MQLInfoString(MQL_PROGRAM_NAME));
   string pref=::StringSubstr(NameObj(),pos+1);
   string name=pref+"_"+obj_name;
   CGCnvElement *element=new CGCnvElement(type,this.ID(),obj_num,this.ChartID(),this.SubWindow(),name,x,y,w,h,colour,opacity,movable,activity);
   if(element==NULL)
      ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_FAILED_CREATE_ELM_OBJ),": ",name);
   return element;
  }
//+------------------------------------------------------------------+

Die Methode erhält alle Parameter, die für die Erstellung eines neuen Objekts notwendig sind — seinen Typ, seinen Index in der Liste der angehängten Objekte, Name, Koordinaten, Größe, Farbe, Transparenz und die Flags der Beweglichkeit und Aktivität des Objekts.

Im Klassenkörper wird das Ende aus dem Namensobjekt abgefragt (der Name besteht aus dem Programmnamen und dem Objektnamen, der bei der Erstellung vergeben wurde). Wir müssen den Objektnamen bei seiner Erstellung abfragen und den an die Methode übergebenen Namen hinzufügen.
Im Falle eines Namens wie beispielsweise "Programm_name_Form01" extrahieren wir die Teilzeichenkette "Form01" und fügen den an die Methode übergebenen Namen hinzu. Wenn wir ein Shadow-Objekt erzeugen und den Namen"Shadow" übergeben, lautet der Objektname "Form01_ Shadow", während der endgültige Name des erzeugten Objekts wie folgt lautet: "Programm_name_Form01_Schatten".

Als Nächstes erstellen wir ein neues Objekt, wobei wir seinen Typ, die Parameter des Charts (auf dem das aktuelle Formularobjekt erstellt wurde), den spezifischen Namen und andere an die Methode übergebene Parameter angeben. Es wird der Zeiger auf das erstellte Objekt in der Methode zurückgegeben zurück oder NULL im Falle eines Fehlers.

Die Methode, die ein neues angehängtes Element erstellt:

//+------------------------------------------------------------------+
//| 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;
   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());
      delete obj;
      return false;
     }
   if(!this.m_list_elements.Add(obj))
     {
      ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_FAILED_OBJ_ADD_TO_LIST),": ",obj.NameObj());
      delete obj;
      return false;
     }
   return true;
  }
//+------------------------------------------------------------------+

Die Methode erzeugt ein neues Grafikelement-Objekt mit Hilfe der obigen Methode und fügt es der Liste der angehängten Objekte im Formularobjekt hinzu. Wenn das Erstellen eines neuen Objekts oder das Hinzufügen zur Liste der angehängten Objekte fehlschlägt, wird eine Fehlermeldung angezeigt und false zurückgegeben. Wenn ein neues Element erfolgreich erstellt und der Liste hinzugefügt wurde, wird true zurückgegeben.

Die Methode, die das Schattenobjekt erzeugt:

//+------------------------------------------------------------------+
//| Create the shadow object                                         |
//+------------------------------------------------------------------+
void CForm::CreateShadow(const uchar opacity)
  {
//--- If the shadow flag is disabled, exit
   if(!this.m_shadow)
      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 element object and set the pointer to it in the variable
   this.m_shadow_obj=this.CreateNewGObject(GRAPH_ELEMENT_TYPE_ELEMENT,-1,"Shadow",x,y,w,h,this.m_chart_color_bg,opacity,Movable(),false);
   if(this.m_shadow_obj==NULL)
      return;
//--- Move the form object to the foreground
   this.BringToTop();
  }
//+------------------------------------------------------------------+

Die Methodenlogik ist im Code kommentiert. Kurz gesagt, da das Elementobjekt, auf das der Schatten gezeichnet werden soll, größer sein sollte als das Formularobjekt, für das es erstellt wird (wir benötigen oben, unten, links und rechts freien Platz, um den Schatten zu zeichnen), wird die Größe des neuen Objekts in Abhängigkeit vom Makro-Substitution OUTER_AREA_SIZE berechnet.
Nachdem das Objekt erfolgreich erstellt wurde, wird es automatisch über das Formularobjekt gesetzt, auf dem es erstellt wurde. Daher müssen wir das Formularobjekt zwangsweise in den Vordergrund stellen. Dies geschieht ganz am Ende der Methode.

Die Methode zum Zeichnen von Schatten:

//+------------------------------------------------------------------+
//| Draw the shadow                                                  |
//+------------------------------------------------------------------+
void CForm::DrawShadow(const uchar opacity)
  {
//--- If the shadow flag is disabled, exit
   if(!this.m_shadow)
      return;
//--- Calculate rectangle coordinates relative to the shadow object borders
   int x=OUTER_AREA_SIZE+1;
   int y=OUTER_AREA_SIZE+1;
//--- Draw a filled rectangle starting from the calculated coordinates and having the size of the current form object
   m_shadow_obj.DrawRectangleFill(x,y,x+Width(),y+Height(),this.ColorShadow(),opacity);
//--- Update the shadow object for displaying changes
   m_shadow_obj.Update();
   return;
  }
//+------------------------------------------------------------------+

Die Methodenlogik ist mit Kommentaren im Code versehen. Derzeit ist diese Methode nur ein Werkstück für die Erstellung einer vollwertigen Methode zum Zeichnen von Objektschatten. Derzeit zeichnet die Methode lediglich ein einfaches, nach rechts unten verschobenes Rechteck "unter" dem aktuellen Objekt auf das zum Zeichnen von Schatten angelegte Elementobjekt.

Die Methode setzt ein Farbschema:

//+------------------------------------------------------------------+
//| Set a color scheme                                               |
//+------------------------------------------------------------------+
void CForm::SetColorTheme(const ENUM_COLOR_THEMES theme,const uchar opacity)
  {
   this.SetOpacity(opacity);
   this.SetColorBackground(array_color_themes[theme][COLOR_THEME_COLOR_FORM_BG]);
   this.SetColorFrame(array_color_themes[theme][COLOR_THEME_COLOR_FORM_FRAME]);
   this.SetColorShadow(array_color_themes[theme][COLOR_THEME_COLOR_FORM_SHADOW]);
  }
//+------------------------------------------------------------------+

Die Methode dient zum Einstellen eines bestimmten Farbschemas für das Objekt. Die Methode erhält das gewünschte Schema und den Wert für die Transparenz des Formularobjekts. Die Transparenz des Formulars, die Hintergrundfarbe des Formulars, die Rahmenfarbe des Formulars und die Schattenfarbe des Formulars werden dann anhand der Werte im Array der Farbschemata, das ich oben erstellt habe, eingestellt.

Die Methode setzt den Stil des Formulars:

//+------------------------------------------------------------------+
//| Set the form style                                               |
//+------------------------------------------------------------------+
void CForm::SetFormStyle(const ENUM_FORM_STYLE style,
                         const ENUM_COLOR_THEMES theme,
                         const uchar opacity,
                         const bool shadow=false,
                         const bool redraw=false)
  {
//--- Set opacity parameters and the size of the form frame side
   this.m_shadow=shadow;
   this.m_frame_width_top=array_form_style[style][FORM_STYLE_FRAME_WIDTH_TOP];
   this.m_frame_width_bottom=array_form_style[style][FORM_STYLE_FRAME_WIDTH_BOTTOM];
   this.m_frame_width_left=array_form_style[style][FORM_STYLE_FRAME_WIDTH_LEFT];
   this.m_frame_width_right=array_form_style[style][FORM_STYLE_FRAME_WIDTH_RIGHT];
//--- Set the color scheme
   this.SetColorTheme(theme,opacity);
//--- Create the shadow object and draw a simple distinct shadow
   this.CreateShadow((uchar)array_form_style[style][FORM_STYLE_FRAME_SHADOW_OPACITY]);
   this.DrawShadow((uchar)array_form_style[style][FORM_STYLE_FRAME_SHADOW_OPACITY]);
//--- Fill in the form background with color and opacity
   this.Erase(this.ColorBackground(),this.Opacity());
//--- Depending on the selected form style, draw the corresponding form frame and the outer bounding frame
   switch(style)
     {
      case FORM_STYLE_BEVEL   :
        this.DrawFormFrame(this.m_frame_width_top,this.m_frame_width_bottom,this.m_frame_width_left,this.m_frame_width_right,this.ColorFrame(),this.Opacity(),FRAME_STYLE_BEVEL);
        this.DrawRectangle(0,0,Width()-1,Height()-1,array_color_themes[theme][COLOR_THEME_COLOR_FORM_FRAME_OUTER],this.Opacity());
        break;
      //---FORM_STYLE_FLAT
      default:
        this.DrawFormFrame(this.m_frame_width_top,this.m_frame_width_bottom,this.m_frame_width_left,this.m_frame_width_right,this.ColorFrame(),this.Opacity(),FRAME_STYLE_FLAT);
        this.DrawRectangle(0,0,Width()-1,Height()-1,array_color_themes[theme][COLOR_THEME_COLOR_FORM_FRAME_OUTER],this.Opacity());
        break;
     }
  }
//+------------------------------------------------------------------+

Die Methodenlogik ist im Code kommentiert.

In der Tat ist diese Methode ein Beispiel für das Erstellen eines Formularobjekts mit den erforderlichen Parametern.

Die Methode zeichnet einen Formularrahmen:

//+------------------------------------------------------------------+
//| Draw the form frame                                              |
//+------------------------------------------------------------------+
void CForm::DrawFormFrame(const int wd_top,              // Frame upper segment width
                          const int wd_bottom,           // Frame lower segment width
                          const int wd_left,             // Frame left segment width
                          const int wd_right,            // Frame right segment width
                          const color colour,            // Frame color
                          const uchar opacity,           // Frame opacity
                          const ENUM_FRAME_STYLE style)  // Frame style
  {
//--- Depending on the passed frame style
   switch(style)
     {
      //--- draw a dimensional (convex) frame
      case FRAME_STYLE_BEVEL :
         DrawFrameBevel(0,0,Width(),Height(),wd_top,wd_bottom,wd_left,wd_right,colour,opacity);
        break;
      //--- draw a dimensional (concave) frame
      case FRAME_STYLE_STAMP :
         DrawFrameStamp(0,0,Width(),Height(),wd_top,wd_bottom,wd_left,wd_right,colour,opacity);
        break;
      //--- draw a flat frame
      case FRAME_STYLE_FLAT :
         DrawFrameFlat(0,0,Width(),Height(),wd_top,wd_bottom,wd_left,wd_right,colour,opacity);
        break;
      //--- draw a simple frame
      default:
        //---FRAME_STYLE_SIMPLE
         DrawFrameSimple(0,0,Width(),Height(),wd_top,wd_bottom,wd_left,wd_right,colour,opacity);
        break;
     }
  }
//+------------------------------------------------------------------+

Je nach Rahmenstil zeichnen wir den entsprechenden Formularrahmen.

Die Methode Zeichnen eines einfachen Rahmens:

//+------------------------------------------------------------------+
//| Draw a simple frame                                              |
//+------------------------------------------------------------------+
void CForm::DrawFrameSimple(const int x,           // X coordinate relative to the form
                            const int y,           // Y coordinate relative to the form
                            const int width,       // Frame width
                            const int height,      // Frame height
                            const int wd_top,      // Frame upper segment width
                            const int wd_bottom,   // Frame lower segment width
                            const int wd_left,     // Frame left segment width
                            const int wd_right,    // Frame right segment width
                            const color colour,    // Frame color
                            const uchar opacity)   // Frame opacity
  {
//--- Set rectangle coordinates
   int x1=x, y1=y;
   int x2=x1+width-1;
   int y2=y1+height-1;
//--- Draw the first rectangle
   CGCnvElement::DrawRectangle(x1,y1,x2,y2,colour,opacity);
//--- If the frame width exceeds 1 on all sides, draw the second rectangle
   if(wd_left>1 || wd_right>1 || wd_top>1 || wd_bottom>1)
      CGCnvElement::DrawRectangle(x1+wd_left-1,y1+wd_top-1,x2-wd_right+1,y2-wd_bottom+1,colour,opacity);
//--- Search for "voids" between the lines of two rectangles and fill them with color
   if(wd_left>2 && wd_right>2 && wd_top>2 && wd_bottom>2)
      this.Fill(x1+1,y1+1,colour,opacity);
   else if(wd_left>2 && wd_top>2)
      this.Fill(x1+1,y1+1,colour,opacity);
   else if(wd_right>2 && wd_bottom>2)
      this.Fill(x2-1,y2-1,colour,opacity);
   else if(wd_left<3 && wd_right<3)
     {
      if(wd_top>2)
         this.Fill(x1+1,y1+1,colour,opacity);
      if(wd_bottom>2)
         this.Fill(x1+1,y2-1,colour,opacity);
     }
   else if(wd_top<3 && wd_bottom<3)
     {
      if(wd_left>2)
         this.Fill(x1+1,y1+1,colour,opacity);
      if(wd_right>2)
         this.Fill(x2-1,y1+1,colour,opacity);
     }
  }
//+------------------------------------------------------------------+

Die Methodenlogik ist im Code ausführlich kommentiert. Kurz gesagt, es werden zwei Rechtecke gezeichnet — eines im anderen. Wenn zwischen den Rechtecken an den Stellen, die die Seiten des zukünftigen Rahmens bilden, eine Lücke ist (die Seiten der Rechtecke überlappen sich nicht), füllen wir diese mit der Farbe, die zum Zeichnen der Rechtecke verwendet wurde.

Die Methode zum Zeichnen eines flachen Rahmens:

//+------------------------------------------------------------------+
//| Draw the flat frame                                              |
//+------------------------------------------------------------------+
void CForm::DrawFrameFlat(const int x,
                          const int y,
                          const int width,
                          const int height,
                          const int wd_top,
                          const int wd_bottom,
                          const int wd_left,
                          const int wd_right,
                          const color colour,
                          const uchar opacity)
  {
//--- Draw a simple frame
   this.DrawFrameSimple(x,y,width,height,wd_top,wd_bottom,wd_left,wd_right,colour,opacity);
//--- If the width of the frame top and bottom exceeds one pixel
   if(wd_top>1 && wd_bottom>1)
     {
      //--- Darken the horizontal sides of the frame
      for(int i=0;i<width;i++)
        {
         this.m_canvas.PixelSet(x+i,y,CGCnvElement::ChangeColorLightness(this.GetPixel(x+i,y),-0.05));
         this.m_canvas.PixelSet(x+i,y+height-1,CGCnvElement::ChangeColorLightness(this.GetPixel(x+i,y+height-1),-0.07));
        }
     }
//--- If the width of the frame left and right sides exceeds one pixel
   if(wd_left>1 && wd_right>1)
     {
      //--- Darken the vertical sides of the frame
      for(int i=1;i<height-1;i++)
        {
         this.m_canvas.PixelSet(x,y+i,CGCnvElement::ChangeColorLightness(this.GetPixel(x,y+1),-0.01));
         this.m_canvas.PixelSet(x+width-1,y+i,CGCnvElement::ChangeColorLightness(this.GetPixel(x+width-1,y+1),-0.02));
        }
     }
  }
//+------------------------------------------------------------------+

Das Verfahren zeichnet einen geprägten (konvex) Rahmen:

//+------------------------------------------------------------------+
//| Draw an embossed (convex) frame                                  |
//+------------------------------------------------------------------+
void CForm::DrawFrameBevel(const int x,
                           const int y,
                           const int width,
                           const int height,
                           const int wd_top,
                           const int wd_bottom,
                           const int wd_left,
                           const int wd_right,
                           const color colour,
                           const uchar opacity)
  {
//--- Draw a simple frame
   this.DrawFrameSimple(x,y,width,height,wd_top,wd_bottom,wd_left,wd_right,colour,opacity);
//--- If the width of the frame top and bottom exceeds one pixel
   if(wd_top>1 && wd_bottom>1)
     {
      //--- Lighten and darken the required sides of the frame edges
      for(int i=0;i<width;i++)
        {
         this.m_canvas.PixelSet(x+i,y,CGCnvElement::ChangeColorLightness(this.GetPixel(x+i,y),0.25));
         this.m_canvas.PixelSet(x+i,y+height-1,CGCnvElement::ChangeColorLightness(this.GetPixel(x+i,y+height-1),-0.2));
        }
      for(int i=wd_left;i<width-wd_right;i++)
        {
         this.m_canvas.PixelSet(x+i,y+wd_top-1,CGCnvElement::ChangeColorLightness(this.GetPixel(x+i,y+wd_top-1),-0.2));
         this.m_canvas.PixelSet(x+i,y+height-wd_bottom,CGCnvElement::ChangeColorLightness(this.GetPixel(x+i,y+height-wd_bottom),0.1));
        }
     }
//--- If the width of the frame left and right sides exceeds one pixel
   if(wd_left>1 && wd_right>1)
     {
      //--- Lighten and darken the required sides of the frame edges
      for(int i=1;i<height-1;i++)
        {
         this.m_canvas.PixelSet(x,y+i,CGCnvElement::ChangeColorLightness(this.GetPixel(x,y+i),0.1));
         this.m_canvas.PixelSet(x+width-1,y+i,CGCnvElement::ChangeColorLightness(this.GetPixel(x+width-1,y+i),-0.1));
        }
      for(int i=wd_top;i<height-wd_bottom;i++)
        {
         this.m_canvas.PixelSet(x+wd_left-1,y+i,CGCnvElement::ChangeColorLightness(this.GetPixel(x+wd_left-1,y+i),-0.1));
         this.m_canvas.PixelSet(x+width-wd_right,y+i,CGCnvElement::ChangeColorLightness(this.GetPixel(x+width-wd_right,y+i),0.1));
        }
     }
  }
//+------------------------------------------------------------------+

Das Verfahren zeichnet einen geprägten (konkav) Rahmen:

//+------------------------------------------------------------------+
//| Draw an embossed (concave) frame                                 |
//+------------------------------------------------------------------+
void CForm::DrawFrameStamp(const int x,
                           const int y,
                           const int width,
                           const int height,
                           const int wd_top,
                           const int wd_bottom,
                           const int wd_left,
                           const int wd_right,
                           const color colour,
                           const uchar opacity)
  {
//--- Draw a simple frame
   this.DrawFrameSimple(x,y,width,height,wd_top,wd_bottom,wd_left,wd_right,colour,opacity);
//--- If the width of the frame top and bottom exceeds one pixel
   if(wd_top>1 && wd_bottom>1)
     {
      //--- Lighten and darken the required sides of the frame edges
      for(int i=0;i<width;i++)
        {
         this.m_canvas.PixelSet(x+i,y,CGCnvElement::ChangeColorLightness(this.GetPixel(x+i,y),-0.25));
         this.m_canvas.PixelSet(x+i,y+height-1,CGCnvElement::ChangeColorLightness(this.GetPixel(x+i,y+height-1),0.2));
        }
      for(int i=wd_left;i<width-wd_right;i++)
        {
         this.m_canvas.PixelSet(x+i,y+wd_top-1,CGCnvElement::ChangeColorLightness(this.GetPixel(x+i,y+wd_top-1),0.2));
         this.m_canvas.PixelSet(x+i,y+height-wd_bottom,CGCnvElement::ChangeColorLightness(this.GetPixel(x+i,y+height-wd_bottom),-0.25));
        }
     }
//--- If the width of the frame left and right sides exceeds one pixel
   if(wd_left>1 && wd_right>1)
     {
      //--- Lighten and darken the required sides of the frame edges
      for(int i=1;i<height-1;i++)
        {
         this.m_canvas.PixelSet(x,y+i,CGCnvElement::ChangeColorLightness(this.GetPixel(x,y+i),-0.1));
         this.m_canvas.PixelSet(x+width-1,y+i,CGCnvElement::ChangeColorLightness(this.GetPixel(x+width-1,y+i),0.2));
        }
      for(int i=wd_top;i<height-wd_bottom;i++)
        {
         this.m_canvas.PixelSet(x+wd_left-1,y+i,CGCnvElement::ChangeColorLightness(this.GetPixel(x+wd_left-1,y+i),0.2));
         this.m_canvas.PixelSet(x+width-wd_right,y+i,CGCnvElement::ChangeColorLightness(this.GetPixel(x+width-wd_right,y+i),-0.2));
        }
     }
  }
//+------------------------------------------------------------------+

Die Methoden zum Zeichnen der Felder (einfache und geprägte):

//+------------------------------------------------------------------+
//| Draw a simple field                                              |
//+------------------------------------------------------------------+
void CForm::DrawFieldFlat(const int x,const int y,const int width,const int height,const color colour,const uchar opacity)
  {
//--- Draw a filled rectangle
   CGCnvElement::DrawRectangleFill(x,y,x+width-1,y+height-1,colour,opacity);
//--- Darken all its edges
   for(int i=0;i<width;i++)
     {
      this.m_canvas.PixelSet(x+i,y,CGCnvElement::ChangeColorLightness(this.GetPixel(x+i,y),-0.05));
      this.m_canvas.PixelSet(x+i,y+height-1,CGCnvElement::ChangeColorLightness(this.GetPixel(x+i,y+height-1),-0.05));
     }
   for(int i=1;i<height-1;i++)
     {
      this.m_canvas.PixelSet(x,y+i,CGCnvElement::ChangeColorLightness(this.GetPixel(x,y+1),-0.05));
      this.m_canvas.PixelSet(x+width-1,y+i,CGCnvElement::ChangeColorLightness(this.GetPixel(x+width-1,y+1),-0.05));
     }
  }
//+------------------------------------------------------------------+
//| Draw an embossed (convex) field                                  |
//+------------------------------------------------------------------+
void CForm::DrawFieldBevel(const int x,const int y,const int width,const int height,const color colour,const uchar opacity)
  {
//--- Draw a filled rectangle
   CGCnvElement::DrawRectangleFill(x,y,x+width-1,y+height-1,colour,opacity);
//--- Lighten its top and left and darken its bottom and right
   for(int i=0;i<width;i++)
     {
      this.m_canvas.PixelSet(x+i,y,CGCnvElement::ChangeColorLightness(this.GetPixel(x+i,y),0.1));
      this.m_canvas.PixelSet(x+i,y+height-1,CGCnvElement::ChangeColorLightness(this.GetPixel(x+i,y+height-1),-0.1));
     }
   for(int i=1;i<height-1;i++)
     {
      this.m_canvas.PixelSet(x,y+i,CGCnvElement::ChangeColorLightness(this.GetPixel(x,y+1),0.05));
      this.m_canvas.PixelSet(x+width-1,y+i,CGCnvElement::ChangeColorLightness(this.GetPixel(x+width-1,y+1),-0.05));
     }
  }
//+------------------------------------------------------------------+
//| Draw an embossed (concave) field                                 |
//+------------------------------------------------------------------+
void CForm::DrawFieldStamp(const int x,const int y,const int width,const int height,const color colour,const uchar opacity)
  {
//--- Draw a filled rectangle
   CGCnvElement::DrawRectangleFill(x,y,x+width-1,y+height-1,colour,opacity);
//--- Darken its top and left and lighten its bottom and right
   for(int i=0;i<width;i++)
     {
      this.m_canvas.PixelSet(x+i,y,CGCnvElement::ChangeColorLightness(this.GetPixel(x+i,y),-0.1));
      this.m_canvas.PixelSet(x+i,y+height-1,CGCnvElement::ChangeColorLightness(this.GetPixel(x+i,y+height-1),0.1));
     }
   for(int i=1;i<height-1;i++)
     {
      this.m_canvas.PixelSet(x,y+i,CGCnvElement::ChangeColorLightness(this.GetPixel(x,y+1),-0.05));
      this.m_canvas.PixelSet(x+width-1,y+i,CGCnvElement::ChangeColorLightness(this.GetPixel(x+width-1,y+1),0.05));
     }
  }
//+------------------------------------------------------------------+

Die Logik aller oben genannten Methoden ist nahezu identisch und mit Kommentaren im Methodencode versehen. Ich denke, die Methoden sind gut nachvollziehbar und leicht zu verstehen. In jedem Fall können Sie gerne den Abschnitt Kommentare verwenden.

Damit ist die Erstellung des Formularobjekts vorerst abgeschlossen.


Test

Der heutige Test wird ziemlich einfach sein. Ich werde zwei verschiedene Formulare mit unterschiedlichen Konstruktionsstilen und Farbschemata erstellen. Nachdem die Formulare erstellt sind, werde ich ihnen die Felder hinzufügen. Das obere Formular wird ein dimensionales konkaves Feld erhalten, während das zweite Formular ein halbtransparentes dimensionales konkaves Feld erhalten wird.

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

Die Bibliotheks-Formularobjektdatei wird in den EA eingebunden und die Liste der Elementobjekte in die Liste der Formularobjekte umbenannt:

//+------------------------------------------------------------------+
//|                                             TestDoEasyPart76.mq5 |
//|                                  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"
//--- includes
#include <Arrays\ArrayObj.mqh>
#include <DoEasy\Services\Select.mqh>
#include <DoEasy\Objects\Graph\Form.mqh>
//--- defines
#define        FORMS_TOTAL (2)   // Number of created forms
//--- input parameters
sinput   bool  InpMovable  = true;  // Movable flag
//--- global variables
CArrayObj      list_forms;
//+------------------------------------------------------------------+

In OnInit() werden zwei Formulare erstellt und konkave Felder darauf gezeichnet:

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- Set the permissions to send cursor movement and mouse scroll events
   ChartSetInteger(ChartID(),CHART_EVENT_MOUSE_MOVE,true);
   ChartSetInteger(ChartID(),CHART_EVENT_MOUSE_WHEEL,true);
//--- Set EA global variables

//--- Create the specified number of form objects
   list_forms.Clear();
   int total=FORMS_TOTAL;
   for(int i=0;i<total;i++)
     {
      //--- When creating an object, pass all the required parameters to it
      CForm *form=new CForm("Form_0"+(string)(i+1),300,40+(i*80),100,70);
      if(form==NULL)
         continue;
      //--- Set activity and moveability flags for the form
      form.SetActive(true);
      form.SetMovable(false);
      //--- Set the form ID equal to the loop index and the index in the list of objects
      form.SetID(i);
      form.SetNumber(0);   // (0 - main form object) Auxiliary objects may be attached to the main one. The main object is able to manage them
      //--- Set the full opacity for the top form and the partial opacity for the bottom one
      uchar opacity=(i==0 ? 255 : 250);
      //--- Set the form style and its color theme depending on the loop index
      ENUM_FORM_STYLE style=(ENUM_FORM_STYLE)i;
      ENUM_COLOR_THEMES theme=(ENUM_COLOR_THEMES)i;
      //--- Set the form style and theme
      form.SetFormStyle(style,theme,opacity,true);
      //--- If this is the first (top) form
      if(i==0)
        {
         //--- Draw a concave field slightly shifted from the center of the form downwards
         form.DrawFieldStamp(3,10,form.Width()-6,form.Height()-13,form.ColorBackground(),form.Opacity());
         form.Update(true);
        }
      //--- If this is the second (bottom) form
      if(i==1)
        {
         //--- Draw a concave semi-transparent "tainted glass" field in the center
         form.DrawFieldStamp(10,10,form.Width()-20,form.Height()-20,clrWheat,200);
         form.Update(true);
        }
      //--- Add objects to the list
      if(!list_forms.Add(form))
        {
         delete form;
         continue;
        }
     }
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+

Wir entfernen die Behandlung von Mausklicks auf Objekte aus der Methode OnChartEvent():

//+------------------------------------------------------------------+
//| ChartEvent function                                              |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,
                  const long &lparam,
                  const double &dparam,
                  const string &sparam)
  {
//--- If clicking on an object
   if(id==CHARTEVENT_OBJECT_CLICK)
     {

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

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


Wie Sie sehen können, haben wir es geschafft, zwei verschiedene Formulare mit unterschiedlichen Farben der Komponenten und des Zeichenstils zu erstellen, indem wir einfach den gewünschten Stil und das Farbschema angeben.

Was kommt als Nächstes?

Im nächsten Artikel werde ich die Entwicklung des Formularobjekts fortsetzen und seine Funktionalität ergänzen.

Alle Dateien der aktuellen Version der Bibliothek sind unten zusammen mit der Test-EA-Datei für MQL5 zum Testen und Herunterladen angehängt.
Ihre Fragen und Vorschläge schreiben Sie bitte in den Kommentarteil.

Zurück zum Inhalt

*Frühere Artikel dieser Serie:

Grafiken in der Bibliothek DoEasy (Teil 73): Das Formularobjekt eines grafischen Elements
Grafiken in der Bibliothek DoEasy (Teil 74): Das grafisches Basiselement, das von der Klasse CCanvas unterstützt wird
Grafiken in der Bibliothek DoEasy (Teil 75): Methoden zur Handhabung von Primitiven und Text im grafischen Grundelement