English Русский 中文 Español 日本語 Português
preview
DoEasy. Steuerung (Teil 27): Arbeiten am WinForms Objekt der ProgressBar

DoEasy. Steuerung (Teil 27): Arbeiten am WinForms Objekt der ProgressBar

MetaTrader 5Beispiele | 26 Januar 2023, 13:09
298 0
Artyom Trishkin
Artyom Trishkin

Inhalt


Konzept

Im aktuellen Artikel werde ich die Entwicklung des Steuerelements der ProgressBar fortsetzen, mit dessen Implementierung ich im vorherigen Artikel begonnen habe. Dieses Element besteht aus zwei Komponenten - der Unterlage und dem Fortschrittsbalken. In Zukunft wird es möglich sein, neben dem Fortschrittsbalken auch andere Gestaltungselemente auf der Unterlage zu platzieren - Texte, Bilder, etc. Im Moment ist dieses Steuerelement ein statisches WinForms-Objekt. Heute werde ich die Funktionsweise zur programmatischen Steuerung des Fortschrittsbalkens des Objekts entwickeln. Auf diese Weise können wir sie in unseren Programmen verwenden, um den Fortschritt von Vorgängen anzuzeigen, die eine große Anzahl von identischen Aktionen erfordern.

Wenn wir auf ähnliche Fortschrittsbalken in der Windows-Umgebung achten, werden wir feststellen, dass sich das Licht entlang des Fortschrittsbalkens bewegt oder dass es andere visuelle Effekte gibt, wenn er gefüllt ist. Neben der Tatsache, dass sie die Programmoberfläche animieren und damit attraktiver machen, haben die visuellen Effekte auf dem Fortschrittsbalken auch die Funktion einer zusätzlichen Anzeige des Prozessabschlusses. Wenn die Anzahl der vom Fortschrittsbalken angezeigten Vorgänge groß genug ist, wird der Fortschrittsbalken nicht bei jedem abgeschlossenen Vorgang, sondern nach einer bestimmten Anzahl von Vorgängen gefüllt. Selbst wenn der Fortschrittsbalken voll ist, können die Vorgänge also fortgesetzt werden. Wenn in einer solchen Situation weiterhin visuelle Effekte auf dem Fortschrittsbalken angezeigt werden, ist ein Vorgang noch nicht abgeschlossen. Sobald der Vorgang abgeschlossen ist, zeigt der Fortschrittsbalken keine visuellen Effekte mehr an.

Wenn der Prozess endet, wird normalerweise auch das ProgressBar-Steuerelement ausgeblendet, sodass die oben beschriebene Anzeige nicht sichtbar ist. Wird der Fortschrittsbalken jedoch in einem separaten Fenster erstellt, das nach Abschluss aller Vorgänge manuell geschlossen werden sollte, dann kann die Anzeige des Abschlusses/Nichtabschlusses des Prozesses durch visuelle Effekte auf dem Fortschrittsbalken beobachtet werden. Wenn es mehrere Fortschrittsbalken gleichzeitig gibt, die einen in Unterprozesse aufgeteilten Prozess anzeigen, werden alternativ die Fortschrittsbalken der bereits abgeschlossenen Prozesse angezeigt, bis der Gesamtprozess abgeschlossen ist. Bei den ProgressBar-Steuerelementen, die Teilprozesse anzeigen, kann man auch die doppelte Anzeige beobachten - wenn der Fortschrittsbalken vollständig gefüllt ist, aber weiterhin visuelle Effekte darauf angezeigt werden, dann ist dieser Teilprozess noch nicht vollständig abgeschlossen.

Die Implementierung der visuellen Effekte für das ProgressBar-Steuerelement gibt uns die Möglichkeit, sein Konzept in anderen Steuerelementen der Bibliothek zu verwenden. Jedes Objekt, das visuelle Effekte haben soll, wird in die Liste der aktiven Elemente aufgenommen. Die Bibliothek sieht diese Liste und ruft die Ereignishandlung des Timers für jedes der in dieser Liste enthaltenen Objekte der Reihe nach im Timer auf. Die erforderliche Funktionsweise wird in den Objekten selbst implementiert, und zwar in deren Timer.

Außerdem werde ich das Zeichnen von vordefinierten Symbolen etwas verfeinern. Im letzten Artikel habe ich das seltsame Verhalten von Anti-Aliasing-Linien erwähnt, wenn sie mit Transparenz gezeichnet werden. In der Tat werden alle grafischen Primitive, die mit den Glättungsmethoden der Standardbibliothek gezeichnet werden, nicht normal gezeichnet, wenn sie auf transparent gesetzt sind. Das bedeutet, dass wir kein glattes Aussehen oder Verschwinden solcher Linien erreichen können. Aber wir können folgenden Trick anwenden: Wenn die Farbdeckung für die Linie auf einen bestimmten Wert eingestellt ist, zum Beispiel von 128 bis 255, dann müssen wir mit der Anti-Aliasing-Methode zeichnen. Wenn die Deckkraft von 127 auf 0 gesetzt wird, zeichnen wir eine solche Linie mit der üblichen Methode ohne Glättung. Auf diese Weise wird der visuelle Effekt eines glatten Auftretens/Verschwindens der gezeichneten grafischen Primitive erzielt, und die Semitransparenz verdeckt die „Rauheit“ der ohne Anti-Aliasing gezeichneten Linien.


Verbesserung der Bibliotheksklassen

Bei der Erstellung eines Blendobjekts im vorherigen Artikel bin ich davon ausgegangen, dass es auf der Grundlage eines Schattenobjekts erstellt wird - es wird vom Schattenobjekt abgeleitet, um die in dieser Klasse verfügbaren Weichzeichnermethoden zu verwenden. Die Praxis hat jedoch gezeigt, dass sich dieser Ansatz nicht bewährt hat - abgesehen davon, dass er die Hinzufügung visueller Effekte zu einem beliebigen Objekt erschwert, schränkt die Verwendung einer Blende als Nachfolger eines Schattenobjekts dessen Verwendung im Rahmen eines bereits erstellten Konzepts ein.

Machen wir also Folgendes: Das Objekt der Blende (glare) wird von der Basisobjektklasse aller WinForms-Objekte der CWinFormBase-Bibliothek abgeleitet und in die Liste der Hilfsobjekte aufgenommen. Dann kann ich sie mit nur einer Zeile Code in der Klasse, in der sie verwendet werden soll, leicht hinzufügen. Ich habe bereits Handler für aktive Objekte implementiert. Dieser Ansatz erspart uns das routinemäßige Hinzufügen von Funktionen, die mit den Funktionen der Schattenobjektverarbeitung identisch sind, zu verschiedenen Bibliotheksklassen. Wir werden einfach die vorgefertigten Methoden für das Hinzufügen von gebundenen Objekten und deren Verarbeitung verwenden.

Verschieben wir die Zeile mit der ID der Blende in \MQL5\Include\DoEasy\Defines.mqh aus dem Abschnitt der grafischen Objekte in den Abschnitt der Hilfsbibliothekssteuerungen:

//+-----------------------------------------------+
//| The list of graphical element types           |
//+-----------------------------------------------+
enum ENUM_GRAPH_ELEMENT_TYPE
  {
   GRAPH_ELEMENT_TYPE_STANDARD,                       // Standard graphical object
   GRAPH_ELEMENT_TYPE_STANDARD_EXTENDED,              // Extended standard graphical object
   GRAPH_ELEMENT_TYPE_SHADOW_OBJ,                     // Shadow object
                                                                    
   GRAPH_ELEMENT_TYPE_ELEMENT,                        // Element
   GRAPH_ELEMENT_TYPE_FORM,                           // Form
   GRAPH_ELEMENT_TYPE_WINDOW,                         // Window
   //--- WinForms
   GRAPH_ELEMENT_TYPE_WF_UNDERLAY,                    // Panel object underlay
   GRAPH_ELEMENT_TYPE_WF_BASE,                        // Windows Forms Base
   //--- 'Container' object types are to be set below
   GRAPH_ELEMENT_TYPE_WF_CONTAINER,                   // Windows Forms container base object
   GRAPH_ELEMENT_TYPE_WF_PANEL,                       // Windows Forms Panel
   GRAPH_ELEMENT_TYPE_WF_GROUPBOX,                    // Windows Forms GroupBox
   GRAPH_ELEMENT_TYPE_WF_TAB_CONTROL,                 // Windows Forms TabControl
   GRAPH_ELEMENT_TYPE_WF_SPLIT_CONTAINER,             // Windows Forms SplitContainer
   //--- 'Standard control' object types are to be set below
   GRAPH_ELEMENT_TYPE_WF_COMMON_BASE,                 // Windows Forms base standard control
   GRAPH_ELEMENT_TYPE_WF_LABEL,                       // Windows Forms Label
   GRAPH_ELEMENT_TYPE_WF_BUTTON,                      // Windows Forms Button
   GRAPH_ELEMENT_TYPE_WF_CHECKBOX,                    // Windows Forms CheckBox
   GRAPH_ELEMENT_TYPE_WF_RADIOBUTTON,                 // Windows Forms RadioButton
   GRAPH_ELEMENT_TYPE_WF_ELEMENTS_LIST_BOX,           // Base list object of Windows Forms elements
   GRAPH_ELEMENT_TYPE_WF_LIST_BOX,                    // Windows Forms ListBox
   GRAPH_ELEMENT_TYPE_WF_CHECKED_LIST_BOX,            // Windows Forms CheckedListBox
   GRAPH_ELEMENT_TYPE_WF_BUTTON_LIST_BOX,             // Windows Forms ButtonListBox
   GRAPH_ELEMENT_TYPE_WF_TOOLTIP,                     // Windows Forms ToolTip
   GRAPH_ELEMENT_TYPE_WF_PROGRESS_BAR,                // Windows Forms ProgressBar
   //--- Auxiliary elements of WinForms objects
   GRAPH_ELEMENT_TYPE_WF_LIST_BOX_ITEM,               // Windows Forms ListBoxItem
   GRAPH_ELEMENT_TYPE_WF_TAB_HEADER,                  // Windows Forms TabHeader
   GRAPH_ELEMENT_TYPE_WF_TAB_FIELD,                   // Windows Forms TabField
   GRAPH_ELEMENT_TYPE_WF_SPLIT_CONTAINER_PANEL,       // Windows Forms SplitContainerPanel
   GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON,                // Windows Forms ArrowButton
   GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_UP,             // Windows Forms UpArrowButton
   GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_DOWN,           // Windows Forms DownArrowButton
   GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_LEFT,           // Windows Forms LeftArrowButton
   GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_RIGHT,          // Windows Forms RightArrowButton
   GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTONS_UD_BOX,        // Windows Forms UpDownArrowButtonsBox
   GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTONS_LR_BOX,        // Windows Forms LeftRightArrowButtonsBox
   GRAPH_ELEMENT_TYPE_WF_SPLITTER,                    // Windows Forms Splitter
   GRAPH_ELEMENT_TYPE_WF_HINT_BASE,                   // Windows Forms HintBase
   GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_LEFT,              // Windows Forms HintMoveLeft
   GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_RIGHT,             // Windows Forms HintMoveRight
   GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_UP,                // Windows Forms HintMoveUp
   GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_DOWN,              // Windows Forms HintMoveDown
   GRAPH_ELEMENT_TYPE_WF_BAR_PROGRESS_BAR,            // Windows Forms BarProgressBar
   GRAPH_ELEMENT_TYPE_WF_GLARE_OBJ,                   // Glare object
  };
//+------------------------------------------------------------------+


Um visuelle Effekte (in diesem Fall Glanzlichter) zu zeichnen, müssen wir eine Liste von visuellen Effektstilen festlegen:

//+-----------------------------------------------+
//| List of visual effect styles                  |
//+-----------------------------------------------+
enum ENUM_CANV_ELEMENT_VISUAL_EFF_STYLE
  {
   CANV_ELEMENT_VISUAL_EFF_STYLE_RECTANGLE,           // Rectangle
   CANV_ELEMENT_VISUAL_EFF_STYLE_PARALLELOGRAMM,      // Parallelogram
  };
//+------------------------------------------------------------------+

Im Moment werden wir nur zwei Arten von Blenden herstellen - ein regelmäßiges Rechteck und ein Parallelogramm. Anschließend können wir eine beliebige Anzahl von vordefinierten Blendenformen zeichnen und diese zur Gestaltung visueller Effekte verwenden. Sie müssen keine grafischen Primitive sein. Das können beliebige Bilder sein, die als Effekte über Objekte gelegt werden, zumal diese Bilder auch animiert werden können, was ich später als Beispiel machen werde.

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

//--- CForm
   MSG_FORM_OBJECT_TEXT_NO_SHADOW_OBJ_FIRST_CREATE_IT,// No shadow object. Create it using the CreateShadowObj() method
   MSG_FORM_OBJECT_TEXT_NO_GLARE_OBJ_FIRST_CREATE_IT, // No glare object. We must first create it using the CreateGlareObj() method
   MSG_FORM_OBJECT_ERR_FAILED_CREATE_SHADOW_OBJ,      // Failed to create new shadow object
   MSG_FORM_OBJECT_ERR_FAILED_CREATE_GLARE_OBJ,       // Failed to create new glare object
   MSG_FORM_OBJECT_ERR_FAILED_CREATE_PC_OBJ,          // Failed to create new pixel copier object
   MSG_FORM_OBJECT_PC_OBJ_ALREADY_IN_LIST,            // Pixel copier object with ID already present in the list 

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

//--- CForm
   {"Отсутствует объект тени. Необходимо сначала его создать при помощи метода CreateShadowObj()","There is no shadow object. You must first create it using the CreateShadowObj() method"},
   {"Отсутствует объект блика. Необходимо сначала его создать при помощи метода CreateGlareObj()","There is no glare object. You must first create it using the CreateGlareObj() method"},
   {"Не удалось создать новый объект для тени","Failed to create new object for shadow"},
   {"Не удалось создать новый объект для блика","Failed to create new object for glare"},
   {"Не удалось создать новый объект-копировщик пикселей","Failed to create new pixel copier object"},
   {"В списке уже есть объект-копировщик пикселей с идентификатором ","There is already a pixel copier object in the list with ID "},

Wir verschieben den Nachrichtenindex des Blendenobjekts (Glare object) in die Gruppe der Standard-WinForms-Objekte:

   MSG_GRAPH_ELEMENT_TYPE_ELEMENT,                    // Element
   MSG_GRAPH_ELEMENT_TYPE_SHADOW_OBJ,                 // Shadow object
                                                                    
   MSG_GRAPH_ELEMENT_TYPE_FORM,                       // Form
   MSG_GRAPH_ELEMENT_TYPE_WINDOW,                     // Window

   MSG_GRAPH_ELEMENT_TYPE_WF_BAR_PROGRESS_BAR,        // BarProgressBar control
   MSG_GRAPH_ELEMENT_TYPE_WF_PROGRESS_BAR,            // ProgressBar control
   MSG_GRAPH_ELEMENT_TYPE_WF_GLARE_OBJ,               // Glare object
//---
   MSG_GRAPH_ELEMENT_TYPE_WF_WRONG_TYPE_PASSED,       // Incorrect control type

Den Nachrichtentext verschieben wir auf dieselbe Weise:

   {"Элемент","Element"},
   {"Объект тени","Shadow object"},
                     
   {"Форма","Form"},
   {"Окно","Window"},

   {"Элемент управления BarProgressBar","Control element \"BarProgressBar\""},
   {"Элемент управления ProgressBar","Control element \"ProgressBar\""},
   {"Объект блика","Glare object"},
   
   {"Передан не правильный тип элемента управления","Wrong control type passed"},


In der Datei \MQL5\Include\DoEasy\GraphINI.mqh, in der die Parameter verschiedener Farbschemata festgelegt werden, ergänzen wir die Eigenschaft zur Angabe des Deckkraftwerts für die Blende hinzu. Wir erhöhen auch die Gesamtzahl der Eigenschaften auf 12:

//+-----------------------------------------------+
//| List of form style parameter indices          |
//+-----------------------------------------------+
enum ENUM_FORM_STYLE_PARAMS
  {
   //--- CForm
   FORM_STYLE_FRAME_GLARE_OPACITY,              // Glare opacity
   FORM_STYLE_FRAME_SHADOW_OPACITY,             // Shadow opacity
   FORM_STYLE_FRAME_SHADOW_BLUR,                // Shadow blur
   FORM_STYLE_DARKENING_COLOR_FOR_SHADOW,       // Form shadow color darkening
   FORM_STYLE_FRAME_SHADOW_X_SHIFT,             // Shadow X axis shift
   FORM_STYLE_FRAME_SHADOW_Y_SHIFT,             // Shadow Y axis shift
   FORM_STYLE_GRADIENT_V,                       // Vertical gradient filling flag
   FORM_STYLE_GRADIENT_C,                       // Cyclic gradient filling flag
   //--- CPanel
   FORM_STYLE_FRAME_WIDTH_LEFT,                 // Panel frame width to the left
   FORM_STYLE_FRAME_WIDTH_RIGHT,                // Panel frame width to the right
   FORM_STYLE_FRAME_WIDTH_TOP,                  // Panel frame width on top
   FORM_STYLE_FRAME_WIDTH_BOTTOM,               // Panel frame width below
  };
#define TOTAL_FORM_STYLE_PARAMS        (12)     // Number of form style parameters
//+------------------------------------------------------------------+


Legen wir die Standardwerte der Blendenopazität auf das Eigenschaftsfeld für zwei Farbschemata fest, die sich derzeit in der Bibliothek befinden:

//+-----------------------------------------------+
//| Array containing form style parameters        |
//+-----------------------------------------------+
int array_form_style[TOTAL_FORM_STYLES][TOTAL_FORM_STYLE_PARAMS]=
  {
//--- "Flat form" style parameters
   {
      //--- CForm
      40,                                       // Glare opacity
      80,                                       // Shadow opacity
      4,                                        // Shadow blur
      80,                                       // Form shadow color darkening
      2,                                        // Shadow X axis shift
      2,                                        // Shadow Y axis shift
      false,                                    // Vertical gradient filling flag
      false,                                    // Cyclic gradient filling flag
      //--- CPanel
      3,                                        // Panel frame width to the left
      3,                                        // Panel frame width to the right
      3,                                        // Panel frame width on top
      3,                                        // Panel frame width below
   },
//--- "Embossed form" style parameters
   {
      //--- CForm
      40,                                       // Glare opacity
      80,                                       // Shadow opacity
      4,                                        // Shadow blur
      80,                                       // Form shadow color darkening
      2,                                        // Shadow X axis shift
      2,                                        // Shadow Y axis shift
      true,                                     // Vertical gradient filling flag
      false,                                    // Cyclic gradient filling flag
      //--- CPanel
      3,                                        // Panel frame width to the left
      3,                                        // Panel frame width to the right
      3,                                        // Panel frame width on top
      3,                                        // Panel frame width below
   },
  };
//+------------------------------------------------------------------+

In Zukunft werde ich die Zusammensetzung dieser Eigenschaftswerte für verschiedene Farbschemata radikal ändern und ergänzen, wenn ich mit deren Umsetzung beginne.


Da ich die Kategorie des grafischen Elements der Blende geändert habe, muss ich in \MQL5\Include\DoEasy\Objects\Graph\GBaseObj.mqh der Basisobjektklasse aller grafischen Bibliotheksobjekte, d. h. in der Methode, die die Beschreibung des Typs des grafischen Elements zurückgibt, den Typ „glare object“ (Blendenobjekt), der an die Methode übergeben wird, von seiner vorherigen Position an die richtige Stelle verschieben:

//+------------------------------------------------------------------+
//| Return the description of the graphical element type             |
//+------------------------------------------------------------------+
string CGBaseObj::TypeElementDescription(const ENUM_GRAPH_ELEMENT_TYPE type)
  {
   return
     (
      type==GRAPH_ELEMENT_TYPE_STANDARD                  ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_STANDARD)                 :
      type==GRAPH_ELEMENT_TYPE_STANDARD_EXTENDED         ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_STANDARD_EXTENDED)        :
      type==GRAPH_ELEMENT_TYPE_ELEMENT                   ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_ELEMENT)                  :
      type==GRAPH_ELEMENT_TYPE_SHADOW_OBJ                ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_SHADOW_OBJ)               :
                                                                                                                            
      type==GRAPH_ELEMENT_TYPE_FORM                      ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_FORM)                     :
      type==GRAPH_ELEMENT_TYPE_WINDOW                    ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WINDOW)                   :
      //--- WinForms
      type==GRAPH_ELEMENT_TYPE_WF_UNDERLAY               ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_UNDERLAY)              :
      type==GRAPH_ELEMENT_TYPE_WF_BASE                   ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_BASE)                  :
      //--- Containers
      type==GRAPH_ELEMENT_TYPE_WF_CONTAINER              ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_CONTAINER)             :
      type==GRAPH_ELEMENT_TYPE_WF_GROUPBOX               ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_GROUPBOX)              :
      type==GRAPH_ELEMENT_TYPE_WF_PANEL                  ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_PANEL)                 :
      type==GRAPH_ELEMENT_TYPE_WF_TAB_CONTROL            ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_TAB_CONTROL)           :
      type==GRAPH_ELEMENT_TYPE_WF_SPLIT_CONTAINER        ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_SPLIT_CONTAINER)       :
      //--- Standard controls
      type==GRAPH_ELEMENT_TYPE_WF_COMMON_BASE            ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_COMMON_BASE)           :
      type==GRAPH_ELEMENT_TYPE_WF_LABEL                  ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_LABEL)                 :
      type==GRAPH_ELEMENT_TYPE_WF_CHECKBOX               ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_CHECKBOX)              :
      type==GRAPH_ELEMENT_TYPE_WF_RADIOBUTTON            ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_RADIOBUTTON)           :
      type==GRAPH_ELEMENT_TYPE_WF_BUTTON                 ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_BUTTON)                :
      type==GRAPH_ELEMENT_TYPE_WF_ELEMENTS_LIST_BOX      ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_ELEMENTS_LIST_BOX)     :
      type==GRAPH_ELEMENT_TYPE_WF_LIST_BOX               ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_LIST_BOX)              :
      type==GRAPH_ELEMENT_TYPE_WF_LIST_BOX_ITEM          ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_LIST_BOX_ITEM)         :
      type==GRAPH_ELEMENT_TYPE_WF_CHECKED_LIST_BOX       ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_CHECKED_LIST_BOX)      :
      type==GRAPH_ELEMENT_TYPE_WF_BUTTON_LIST_BOX        ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_BUTTON_LIST_BOX)       :
      type==GRAPH_ELEMENT_TYPE_WF_TOOLTIP                ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_TOOLTIP)               :
      type==GRAPH_ELEMENT_TYPE_WF_PROGRESS_BAR           ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_PROGRESS_BAR)          :
      //--- Auxiliary control objects
      type==GRAPH_ELEMENT_TYPE_WF_TAB_HEADER             ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_TAB_HEADER)            :
      type==GRAPH_ELEMENT_TYPE_WF_TAB_FIELD              ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_TAB_FIELD)             :
      type==GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON           ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON)          :
      type==GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_UP        ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_UP)       :
      type==GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_DOWN      ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_DOWN)     :
      type==GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_LEFT      ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_LEFT)     :
      type==GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_RIGHT     ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_RIGHT)    :
      type==GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTONS_UD_BOX   ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTONS_UD_BOX)  :
      type==GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTONS_LR_BOX   ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTONS_LR_BOX)  :
      type==GRAPH_ELEMENT_TYPE_WF_SPLIT_CONTAINER_PANEL  ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_SPLIT_CONTAINER_PANEL) :
      type==GRAPH_ELEMENT_TYPE_WF_SPLITTER               ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_SPLITTER)              :
      type==GRAPH_ELEMENT_TYPE_WF_HINT_BASE              ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_HINT_BASE)             :
      type==GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_LEFT         ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_LEFT)        :
      type==GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_RIGHT        ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_RIGHT)       :
      type==GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_UP           ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_UP)          :
      type==GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_DOWN         ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_DOWN)        :
      type==GRAPH_ELEMENT_TYPE_WF_BAR_PROGRESS_BAR       ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_BAR_PROGRESS_BAR)      :
      type==GRAPH_ELEMENT_TYPE_WF_GLARE_OBJ              ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_GLARE_OBJ)             :
      "Unknown"
     );
  }  
//+------------------------------------------------------------------+

Technisch gesehen gibt es keinen Unterschied in der Reihenfolge der Prüfung, aber der Ordnung halber verschieben wir sie an die „richtige“ Stelle. Es gibt natürlich einen Unterschied in der Zugriffsgeschwindigkeit, da die wenn-dann-Prüfung hier organisiert ist , und wenn ein Typ häufiger als andere geprüft wird, aber näher am Ende der Prüfliste liegt, dann erhöht sich die Zugriffszeit, zumindest theoretisch, im Rahmen der CPU-Zyklen. Damit jedoch der Zugriff auf jedes Element gleich ist, müssen wir alle diese Prüfungen in den Bibliotheksmethoden mit Hilfe des Switch-Operators durchführen, was später - ganz am Ende der Erstellung der Bibliothek - geschehen wird.

Zuvor war geplant (und im vorigen Artikel ausgeführt), dass das Glanzlichtobjekt vom Schattenobjekt abgeleitet wird. Der Grund dafür war das Vorhandensein der Gaußschen Unschärfemethoden in den Methoden für Schattenobjekte. Jetzt habe ich diese Idee aufgegeben, da ich keine Unschärfe verwenden muss, um die Blende zu implementieren. Außerdem wird die Verwendung eines Schattenobjekts als übergeordnetes Objekt für ein Blendenobjekt dessen Hinzufügung zu anderen grafischen Elementen als Teil der mit ihnen verbundenen Objekte erheblich erschweren. Daher wird das Blendenobjekt ein unabhängiges WinForms-Hilfsobjekt sein. Um jedoch den Weichzeichner in den erforderlichen Objekten (nicht nur im Schattenobjekt) verwenden zu können, werde ich die Weichzeichnermethoden vom Schattenobjekt in das grafische Elementobjekt verschieben. Von dort aus werden diese Methoden für alle grafischen Elemente der Bibliothek verfügbar sein.

Aus der Datei \MQL5\Include\DoEasy\Objects\Graph\ShadowObj.mqh entfernen wir die Einbeziehung der AlgLib-Bibliothek, die Deklaration der Unschärfemethoden und auch die Implementierungscodes dieser beiden Methoden:

//+------------------------------------------------------------------+
//|                                                    ShadowObj.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"
#include <Math\Alglib\alglib.mqh>
//+-----------------------------------------------+
//| Shadows object class                          |
//+-----------------------------------------------+
class CShadowObj : public CGCnvElement
  {
private:
   color             m_color;                         // Shadow color
   uchar             m_opacity;                       // Shadow opacity
   uchar             m_blur;                          // Blur
//--- Draw the object shadow form
   void              DrawShadowFigureRect(const int w,const int h);
protected:
//--- Gaussian blur
   bool              GaussianBlur(const uint radius);
//--- Return the array of weight ratios
   bool              GetQuadratureWeights(const double mu0,const int n,double &weights[]);
//--- Protected constructor with object type, chart ID and subwindow
                     CShadowObj(const ENUM_GRAPH_ELEMENT_TYPE type,
                                CGCnvElement *main_obj,CGCnvElement *base_obj,
                                const long chart_id,
                                const int subwindow,
                                const string descript,
                                const int x,
                                const int y,
                                const int w,
                                const int h);
public:


In \MQL5\Include\DoEasy\Objects\Graph\GCnvElement.mqh die binden wir die AlgLib-Bibliothek ein und deklarieren die Methoden für die Weichzeichner-Implementierung im geschützten Abschnitt:

//+------------------------------------------------------------------+
//|                                                  GCnvElement.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 "GBaseObj.mqh"
#include <Math\Alglib\alglib.mqh>
//+-----------------------------------------------+
//| Class of the graphical element object         |
//+-----------------------------------------------+
class CGCnvElement : public CGBaseObj
  {
protected:
   CGCnvElement     *m_element_main;                           // Pointer to the initial parent element within all the groups of bound objects
   CGCnvElement     *m_element_base;                           // Pointer to the parent element within related objects of the current group
   CCanvas           m_canvas;                                 // CCanvas class object
   CPause            m_pause;                                  // Pause class object
   bool              m_shadow;                                 // Shadow presence
   color             m_chart_color_bg;                         // Chart background color
   uint              m_duplicate_res[];                        // Array for storing resource data copy
   color             m_array_colors_bg[];                      // Array of element background colors
   color             m_array_colors_bg_dwn[];                  // Array of control background colors when clicking on the control
   color             m_array_colors_bg_ovr[];                  // Array of control background colors when hovering the mouse over the control
   bool              m_gradient_v;                             // Vertical gradient filling flag
   bool              m_gradient_c;                             // Cyclic gradient filling flag
   int               m_init_relative_x;                        // Initial relative X coordinate
   int               m_init_relative_y;                        // Initial relative Y coordinate
   color             m_array_colors_bg_init[];                 // Array of element background colors (initial color)

//--- Create (1) the object structure and (2) the object from the structure
   virtual bool      ObjectToStruct(void);
   virtual void      StructToObject(void);
//--- Copy the color array to the specified background color array
   void              CopyArraysColors(color &array_dst[],const color &array_src[],const string source);
   
//--- Return the number of graphical elements (1) by type, (2) by name and type
   int               GetNumGraphElements(const ENUM_GRAPH_ELEMENT_TYPE type) const;
   int               GetNumGraphElements(const string name,const ENUM_GRAPH_ELEMENT_TYPE type) const;
//--- Create and return the graphical element name by its type
   string            CreateNameGraphElement(const ENUM_GRAPH_ELEMENT_TYPE type);
   
//--- Gaussian blur
   bool              GaussianBlur(const uint radius);
//--- Return the array of weight ratios
   bool              GetQuadratureWeights(const double mu0,const int n,double &weights[]);

private:


Außerhalb des Klassenkörpers fügen wir die Implementierung der Blur-Methoden hinzu, die aus der Klasse CShadowObj entfernt wurden:

//+-----------------------------------------------+
//| Gaussian blur                                 |
//| https://www.mql5.com/en/articles/1612#chapter4|
//+-----------------------------------------------+
bool CGCnvElement::GaussianBlur(const uint radius)
  {
//---
   int n_nodes=(int)radius*2+1;
//--- Read graphical resource data. If failed, return false
   if(!CGCnvElement::ResourceStamp(DFUN))
      return false;
//--- Check the blur amount. If the blur radius exceeds half of the width or height, return 'false'
   if((int)radius>=this.Width()/2 || (int)radius>=this.Height()/2)
     {
      ::Print(DFUN,CMessage::Text(MSG_SHADOW_OBJ_IMG_SMALL_BLUR_LARGE));
      return false;
     }
     
//--- Decompose image data from the resource into a, r, g, b color components
   int  size=::ArraySize(this.m_duplicate_res);
//--- arrays for storing A, R, G and B color components
//--- for horizontal and vertical blur
   uchar a_h_data[],r_h_data[],g_h_data[],b_h_data[];
   uchar a_v_data[],r_v_data[],g_v_data[],b_v_data[];
   
//--- Change the size of component arrays according to the array size of the graphical resource data
   if(::ArrayResize(a_h_data,size)==-1)
     {
      CMessage::ToLog(DFUN,MSG_LIB_SYS_FAILED_ARRAY_RESIZE);
      ::Print(DFUN_ERR_LINE,": \"a_h_data\"");
      return false;
     }
   if(::ArrayResize(r_h_data,size)==-1)
     {
      CMessage::ToLog(DFUN,MSG_LIB_SYS_FAILED_ARRAY_RESIZE);
      ::Print(DFUN_ERR_LINE,": \"r_h_data\"");
      return false;
     }
   if(::ArrayResize(g_h_data,size)==-1)
     {
      CMessage::ToLog(DFUN,MSG_LIB_SYS_FAILED_ARRAY_RESIZE);
      ::Print(DFUN_ERR_LINE,": \"g_h_data\"");
      return false;
     }
   if(ArrayResize(b_h_data,size)==-1)
     {
      CMessage::ToLog(DFUN,MSG_LIB_SYS_FAILED_ARRAY_RESIZE);
      ::Print(DFUN_ERR_LINE,": \"b_h_data\"");
      return false;
     }
   if(::ArrayResize(a_v_data,size)==-1)
     {
      CMessage::ToLog(DFUN,MSG_LIB_SYS_FAILED_ARRAY_RESIZE);
      ::Print(DFUN_ERR_LINE,": \"a_v_data\"");
      return false;
     }
   if(::ArrayResize(r_v_data,size)==-1)
     {
      CMessage::ToLog(DFUN,MSG_LIB_SYS_FAILED_ARRAY_RESIZE);
      ::Print(DFUN_ERR_LINE,": \"r_v_data\"");
      return false;
     }
   if(::ArrayResize(g_v_data,size)==-1)
     {
      CMessage::ToLog(DFUN,MSG_LIB_SYS_FAILED_ARRAY_RESIZE);
      ::Print(DFUN_ERR_LINE,": \"g_v_data\"");
      return false;
     }
   if(::ArrayResize(b_v_data,size)==-1)
     {
      CMessage::ToLog(DFUN,MSG_LIB_SYS_FAILED_ARRAY_RESIZE);
      ::Print(DFUN_ERR_LINE,": \"b_v_data\"");
      return false;
     }
//--- Declare the array for storing blur weight ratios and,
//--- if failed to get the array of weight ratios, return 'false'
   double weights[];
   if(!this.GetQuadratureWeights(1,n_nodes,weights))
      return false;
      
//--- Set components of each image pixel to the color component arrays
   for(int i=0;i<size;i++)
     {
      a_h_data[i]=GETRGBA(this.m_duplicate_res[i]);
      r_h_data[i]=GETRGBR(this.m_duplicate_res[i]);
      g_h_data[i]=GETRGBG(this.m_duplicate_res[i]);
      b_h_data[i]=GETRGBB(this.m_duplicate_res[i]);
     }

//--- Blur the image horizontally (along the X axis)
   uint XY; // Pixel coordinate in the array
   double a_temp=0.0,r_temp=0.0,g_temp=0.0,b_temp=0.0;
   int coef=0;
   int j=(int)radius;
   //--- Loop by the image width
   for(int Y=0;Y<this.Height();Y++)
     {
      //--- Loop by the image height
      for(uint X=radius;X<this.Width()-radius;X++)
        {
         XY=Y*this.Width()+X;
         a_temp=0.0; r_temp=0.0; g_temp=0.0; b_temp=0.0;
         coef=0;
         //--- Multiply each color component by the weight ratio corresponding to the current image pixel
         for(int i=-1*j;i<j+1;i=i+1)
           {
            a_temp+=a_h_data[XY+i]*weights[coef];
            r_temp+=r_h_data[XY+i]*weights[coef];
            g_temp+=g_h_data[XY+i]*weights[coef];
            b_temp+=b_h_data[XY+i]*weights[coef];
            coef++;
           }
         //--- Save each rounded color component calculated according to the ratios to the component arrays
         a_h_data[XY]=(uchar)::round(a_temp);
         r_h_data[XY]=(uchar)::round(r_temp);
         g_h_data[XY]=(uchar)::round(g_temp);
         b_h_data[XY]=(uchar)::round(b_temp);
        }
      //--- Remove blur artifacts to the left by copying adjacent pixels
      for(uint x=0;x<radius;x++)
        {
         XY=Y*this.Width()+x;
         a_h_data[XY]=a_h_data[Y*this.Width()+radius];
         r_h_data[XY]=r_h_data[Y*this.Width()+radius];
         g_h_data[XY]=g_h_data[Y*this.Width()+radius];
         b_h_data[XY]=b_h_data[Y*this.Width()+radius];
        }
      //--- Remove blur artifacts to the right by copying adjacent pixels
      for(int x=int(this.Width()-radius);x<this.Width();x++)
        {
         XY=Y*this.Width()+x;
         a_h_data[XY]=a_h_data[(Y+1)*this.Width()-radius-1];
         r_h_data[XY]=r_h_data[(Y+1)*this.Width()-radius-1];
         g_h_data[XY]=g_h_data[(Y+1)*this.Width()-radius-1];
         b_h_data[XY]=b_h_data[(Y+1)*this.Width()-radius-1];
        }
     }

//--- Blur vertically (along the Y axis) the image already blurred horizontally
   int dxdy=0;
   //--- Loop by the image height
   for(int X=0;X<this.Width();X++)
     {
      //--- Loop by the image width
      for(uint Y=radius;Y<this.Height()-radius;Y++)
        {
         XY=Y*this.Width()+X;
         a_temp=0.0; r_temp=0.0; g_temp=0.0; b_temp=0.0;
         coef=0;
         //--- Multiply each color component by the weight ratio corresponding to the current image pixel
         for(int i=-1*j;i<j+1;i=i+1)
           {
            dxdy=i*(int)this.Width();
            a_temp+=a_h_data[XY+dxdy]*weights[coef];
            r_temp+=r_h_data[XY+dxdy]*weights[coef];
            g_temp+=g_h_data[XY+dxdy]*weights[coef];
            b_temp+=b_h_data[XY+dxdy]*weights[coef];
            coef++;
           }
         //--- Save each rounded color component calculated according to the ratios to the component arrays
         a_v_data[XY]=(uchar)::round(a_temp);
         r_v_data[XY]=(uchar)::round(r_temp);
         g_v_data[XY]=(uchar)::round(g_temp);
         b_v_data[XY]=(uchar)::round(b_temp);
        }
      //--- Remove blur artifacts at the top by copying adjacent pixels
      for(uint y=0;y<radius;y++)
        {
         XY=y*this.Width()+X;
         a_v_data[XY]=a_v_data[X+radius*this.Width()];
         r_v_data[XY]=r_v_data[X+radius*this.Width()];
         g_v_data[XY]=g_v_data[X+radius*this.Width()];
         b_v_data[XY]=b_v_data[X+radius*this.Width()];
        }
      //--- Remove blur artifacts at the bottom by copying adjacent pixels
      for(int y=int(this.Height()-radius);y<this.Height();y++)
        {
         XY=y*this.Width()+X;
         a_v_data[XY]=a_v_data[X+(this.Height()-1-radius)*this.Width()];
         r_v_data[XY]=r_v_data[X+(this.Height()-1-radius)*this.Width()];
         g_v_data[XY]=g_v_data[X+(this.Height()-1-radius)*this.Width()];
         b_v_data[XY]=b_v_data[X+(this.Height()-1-radius)*this.Width()];
        }
     }
     
//--- Set the twice blurred (horizontally and vertically) image pixels to the graphical resource data array
   for(int i=0;i<size;i++)
      this.m_duplicate_res[i]=ARGB(a_v_data[i],r_v_data[i],g_v_data[i],b_v_data[i]);
//--- Display the image pixels on the canvas in a loop by the image height and width from the graphical resource data array
   for(int X=0;X<this.Width();X++)
     {
      for(uint Y=radius;Y<this.Height()-radius;Y++)
        {
         XY=Y*this.Width()+X;
         this.m_canvas.PixelSet(X,Y,this.m_duplicate_res[XY]);
        }
     }
//--- Done
   return true;
  }
//+------------------------------------------------------------------+
//| Return the array of weight ratios                                |
//| https://www.mql5.com/en/articles/1612#chapter3_2                 |
//+------------------------------------------------------------------+
bool CGCnvElement::GetQuadratureWeights(const double mu0,const int n,double &weights[])
  {
   CAlglib alglib;
   double  alp[];
   double  bet[];
   ::ArrayResize(alp,n);
   ::ArrayResize(bet,n);
   ::ArrayInitialize(alp,1.0);
   ::ArrayInitialize(bet,1.0);
//---
   double out_x[];
   int    info=0;
   alglib.GQGenerateRec(alp,bet,mu0,n,info,out_x,weights);
   if(info!=1)
     {
      string txt=(info==-3 ? "internal eigenproblem solver hasn't converged" : info==-2 ? "Beta[i]<=0" : "incorrect N was passed");
      ::Print("Call error in CGaussQ::GQGenerateRec: ",txt);
      return false;
     }
   return true;
  }
//+------------------------------------------------------------------+

Die Methoden werden einfach von einer Klasse in eine andere verschoben.


Ich habe bereits erwähnt, dass es notwendig ist, die Methoden zum Zeichnen vordefinierter grafischer Primitive und Bilder zu verfeinern. Da die Methoden zum Zeichnen von Primitiven mit Anti-Aliasing beim Zeichnen halbtransparenter Linien nicht sehr gut funktionieren, muss ich das Zeichnen von geglätteten Primitiven durch ungeglättete ersetzen, wenn die Transparenz der Linie den festgelegten Schwellenwert - die Hälfte des vollen Deckkraftwerts - überschritten hat.

Werfen wir einen Blick auf die verbesserten Methoden. Falls der Deckkraftwert des gezeichneten grafischen Primitivs 127 übersteigt, wird das Primitiv nach der Methode mit Anti-Aliasing gezeichnet. Andernfalls zeichnen wir mit der Methode ohne Anti-Aliasing:

//+-----------------------------------------------+
//| Draw the Info icon                            |
//+-----------------------------------------------+
void CGCnvElement::DrawIconInfo(const int coord_x,const int coord_y,const uchar opacity)
  {
   int x=coord_x+8;
   int y=coord_y+8;
   this.DrawCircleFill(x,y,7,C'0x00,0x77,0xD7',opacity);
   if(opacity>127)
      this.DrawCircleWu(x,y,7.5,C'0x00,0x3D,0x8C',opacity);
   else
      this.DrawCircle(x,y,8,C'0x00,0x3D,0x8C',opacity);
   this.DrawRectangle(x,y-5,x+1,y-4, C'0xFF,0xFF,0xFF',opacity);
   this.DrawRectangle(x,y-2,x+1,y+4,C'0xFF,0xFF,0xFF',opacity);
  }
//+-----------------------------------------------+
//| Draw the Warning icon                         |
//+-----------------------------------------------+
void CGCnvElement::DrawIconWarning(const int coord_x,const int coord_y,const uchar opacity)
  {
   int x=coord_x+8;
   int y=coord_y+1;
   this.DrawTriangleFill(x,y,x+8,y+14,x-8,y+14,C'0xFC,0xE1,0x00',opacity);
   if(opacity>127)
      this.DrawTriangleWu(x,y,x+8,y+14,x-7,y+14,C'0xFF,0xB9,0x00',opacity);
   else
      this.DrawTriangle(x,y,x+8,y+14,x-7,y+14,C'0xFF,0xB9,0x00',opacity);
   this.DrawRectangle(x,y+5,x+1,y+9,  C'0x00,0x00,0x00',opacity);
   this.DrawRectangle(x,y+11,x+1,y+12,C'0x00,0x00,0x00',opacity);
  }
//+-----------------------------------------------+
//| Draw the Error icon                           |
//+-----------------------------------------------+
void CGCnvElement::DrawIconError(const int coord_x,const int coord_y,const uchar opacity)
  {
   int x=coord_x+8;
   int y=coord_y+8;
   this.DrawCircleFill(x,y,7,C'0xF0,0x39,0x16',opacity);
   if(opacity>127)
     {
      this.DrawCircleWu(x,y,7.5,C'0xA5,0x25,0x12',opacity);
      this.DrawLineWu(x-3,y-3,x+3,y+3,C'0xFF,0xFF,0xFF',opacity);
      this.DrawLineWu(x+3,y-3,x-3,y+3,C'0xFF,0xFF,0xFF',opacity);
     }
   else
     {
      this.DrawCircle(x,y,8,C'0xA5,0x25,0x12',opacity);
      this.DrawLine(x-3,y-3,x+3,y+3,C'0xFF,0xFF,0xFF',opacity);
      this.DrawLine(x+3,y-3,x-3,y+3,C'0xFF,0xFF,0xFF',opacity);
     }
  }
//+------------------------------------------------------------------+

Natürlich verstecke ich das Problem nur, aber hier ist es gerechtfertigt: Wenn die Deckkraft von 255 auf 128 sinkt, sieht die geglättete Linie mehr oder weniger normal aus, dann beginnt sie zu brechen und in Pixel zu zerbröckeln, anstatt transparenter zu werden. Das Ersetzen des Zeichnens einer Linie durch das Zeichnen ohne Anti-Aliasing ist hier völlig unsichtbar - die Linie ist bereits halbtransparent und in diesem Zustand ist ihr pixeliges Aussehen nicht bemerkbar, was nicht der Fall ist, wenn wir eine ungeglättete Linie völlig undurchsichtig zeichnen.

Die übrigen vordefinierten Methoden zum Zeichnen von Formen in dieser Klasse verwenden ebenfalls Anti-Aliasing-Zeichenmethoden. Aber ich habe noch nicht getestet, wie es aussehen wird, wenn ich die Transparenz ändere. Wenn es bei diesen Methoden auch visuelle Artefakte gibt, werde ich sie auf genau dieselbe Weise verfeinern.


Da das Blenden-Objekt nun kein separater isolierter Typ ist (wie ein Shadow-Objekt), sondern zur Liste der WinForms-Hilfsobjekte gehört, muss die Klassendatei dieses Objekts nun in die Klassendatei des Paneel-Objekts aufgenommen werden. Dieses Objekt ist ein Containerobjekt, mit dem die Dateien aller WinForms-Bibliotheksobjekte verbunden sind.
Aus der Datei \MQL5\Include\DoEasy\Objects\Graph\WForms\WinFormBase.mqh entfernen wir die Verbindungszeichenfolge der Blenden-Objektklasse:

//+------------------------------------------------------------------+
//|                                                  WinFormBase.mqh |
//|                                  Copyright 2022, MetaQuotes Ltd. |
//|                             https://mql5.com/en/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2022, MetaQuotes Ltd."
#property link      "https://mql5.com/en/users/artmedia70"
#property version   "1.00"
#property strict    // Necessary for mql4
//+-----------------------------------------------+
//| Include files                                 |
//+-----------------------------------------------+
#include "GlareObj.mqh"
#include "..\Form.mqh"
#include "..\..\..\Services\Select.mqh"
//+------------------------------------------------------------------+

und legen die Einbeziehung der Blendenobjektklassendatei in \MQL5\Include\DoEasy\Objects\Graph\WForms\Containers\Panel.mqh der Paneel-Objektklasse fest:

//+------------------------------------------------------------------+
//|                                                        Panel.mqh |
//|                                  Copyright 2022, MetaQuotes Ltd. |
//|                             https://mql5.com/en/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2022, MetaQuotes Ltd."
#property link      "https://mql5.com/en/users/artmedia70"
#property version   "1.00"
#property strict    // Necessary for mql4
//+-----------------------------------------------+
//| Include files                                 |
//+-----------------------------------------------+
#include "Container.mqh"
#include "..\Helpers\TabField.mqh"
#include "..\Helpers\ArrowUpButton.mqh"
#include "..\Helpers\ArrowDownButton.mqh"
#include "..\Helpers\ArrowLeftButton.mqh"
#include "..\Helpers\ArrowRightButton.mqh"
#include "..\Helpers\ArrowUpDownBox.mqh"
#include "..\Helpers\ArrowLeftRightBox.mqh"
#include "..\Helpers\HintMoveLeft.mqh"
#include "..\Helpers\HintMoveRight.mqh"
#include "..\Helpers\HintMoveUp.mqh"
#include "..\Helpers\HintMoveDown.mqh"
#include "GroupBox.mqh"
#include "TabControl.mqh"
#include "SplitContainer.mqh"
#include "..\..\WForms\Common Controls\ListBox.mqh"
#include "..\..\WForms\Common Controls\CheckedListBox.mqh"
#include "..\..\WForms\Common Controls\ButtonListBox.mqh"
#include "..\..\WForms\Common Controls\ToolTip.mqh"
#include "..\..\WForms\Common Controls\ProgressBar.mqh"
#include "..\..\WForms\GlareObj.mqh"
//+-----------------------------------------------+
//| Panel object class of WForms controls         |
//+-----------------------------------------------+
class CPanel : public CContainer

Jetzt wird das Blendenobjekt in allen Containerobjekten verfügbar sein, aus denen es erstellt und in die Listen der angehängten Objekte in jedem anderen grafischen Element der Bibliothek aufgenommen werden kann. Da jedes unabhängige GUI-Fenster von vornherein ein Container ist, können wir jedem Steuerelement, das mit diesem Fenster verbunden ist, ein Blendenobjekt (und anschließend ein visuelles Effektobjekt) hinzufügen.


In der Datei \MQL5\Include\DoEasy\Objects\Graph\WForms\Containers\Container.mqh der Container-Objektklasse, und zwar in ihrer Methode, die die Parameter für das gebundene Objekt festlegt, fügen wir den Codeblock zum Festlegen der Parameter eines neu erstellten Blendenobjekts hinzu:

//+-----------------------------------------------+
//| Set parameters for the attached object        |
//+-----------------------------------------------+
void CContainer::SetObjParams(CWinFormBase *obj,const color colour)
  {
//--- Set the text color of the object to be the same as that of the base container
   obj.SetForeColor(this.ForeColor(),true);
//--- If the created object is not a container, set the same group for it as the one for its base object
   if(obj.TypeGraphElement()<GRAPH_ELEMENT_TYPE_WF_CONTAINER || obj.TypeGraphElement()>GRAPH_ELEMENT_TYPE_WF_SPLIT_CONTAINER)
      obj.SetGroup(this.Group());
//--- Depending on the object type
   switch(obj.TypeGraphElement())
     {
      //--- For the Container, Panel and GroupBox WinForms objects
      case GRAPH_ELEMENT_TYPE_WF_CONTAINER            :
      case GRAPH_ELEMENT_TYPE_WF_PANEL                :
      case GRAPH_ELEMENT_TYPE_WF_GROUPBOX             :
        obj.SetBorderColor(obj.BackgroundColor(),true);
        break;
      //--- For "Label", "CheckBox" and "RadioButton" WinForms objects

      //---...
      //---...

      //--- For ProgressBar WinForms object
      case GRAPH_ELEMENT_TYPE_WF_PROGRESS_BAR         :
        obj.SetBackgroundColor(CLR_DEF_CONTROL_PROGRESS_BAR_BACK_COLOR,true);
        obj.SetBorderColor(CLR_DEF_CONTROL_PROGRESS_BAR_BORDER_COLOR,true);
        obj.SetForeColor(CLR_DEF_CONTROL_PROGRESS_BAR_FORE_COLOR,true);
        obj.SetBorderStyle(FRAME_STYLE_SIMPLE);
        break;
      //--- For "GlareObj" WinForms object
      case GRAPH_ELEMENT_TYPE_WF_GLARE_OBJ            :
        obj.SetBackgroundColor(CLR_CANV_NULL,true);
        obj.SetBorderColor(CLR_CANV_NULL,true);
        obj.SetForeColor(CLR_CANV_NULL,true);
        obj.SetBorderStyle(FRAME_STYLE_NONE);
        break;
      default:
        break;
     }
   obj.Crop();
  }
//+------------------------------------------------------------------+

Nach der Erstellung eines der grafischen Steuerelemente, die mit dem Container verbunden sind, wird diese Methode aufgerufen, die die Mindesteigenschaften für das Objekt festlegt, damit es ordnungsgemäß funktioniert.


Lassen Sie uns die Blendobjektklasse in \MQL5\Include\DoEasy\Objects\Graph\WForms\GlareObj.mqh abschließen.

Jetzt wird die Klasse von der Klasse des WinForms-Basisobjekts abgeleitet. Fügen wir die Datei in die Objektklassendatei der Blende ein und ändern die übergeordnete Klasse:

//+------------------------------------------------------------------+
//|                                                     GlareObj.mqh |
//|                                  Copyright 2022, MetaQuotes Ltd. |
//|                             https://mql5.com/en/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2022, MetaQuotes Ltd."
#property link      "https://mql5.com/en/users/artmedia70"
#property version   "1.00"
#property strict    // Necessary for mql4
//+-----------------------------------------------+
//| Include files                                 |
//+-----------------------------------------------+
#include "WinFormBase.mqh"
//+-----------------------------------------------+
//| Glare object class                            |
//+-----------------------------------------------+
class CGlareObj : public CWinFormBase
  {
   //---...


Im private Abschnitt deklarieren wir die Variable für die Speicherung des Blendenstils und ändern die Blendenzeichenmethoden , indem wir eine weitere deklarieren:

class CGlareObj : public CWinFormBase
  {
private:
   ENUM_CANV_ELEMENT_VISUAL_EFF_STYLE m_style;        // Glare style
   color             m_color;                         // Glare color
   uchar             m_opacity;                       // Glare opacity
   uchar             m_blur;                          // Blur
//--- Draw the object glare form
   void              DrawGlareFigure(void);
   void              DrawFigureRectangle(void);
   void              DrawFigureParallelogram(void);
protected:

Der Blendenstil bestimmt, welche Methode zum Zeichnen aufgerufen werden soll. Die Methode DrawGlareFigure() ruft die Zeichenmethoden auf, die dem angegebenen Stil entsprechen. Es wird keine formalen Parameter in den Blendenmethoden geben - es gibt noch nichts zu spezifizieren für das korrekte Rendern eines Bildes auf einem grafischen Element mit einer vordefinierten Größe. Die Farbe und die Deckkraft der gezeichneten Blende werden im Voraus mit den entsprechenden Methoden festgelegt. Der Weichzeichner wird noch nicht verwendet.

Im öffentlichen Teil der Klasse deklarieren wir die Methoden zum Zeichnen einer Blende, zum erneuten Zeichnen, zum Einstellen der Deckkraft und zum Initialisieren der Eigenschaften, und implementieren die Methoden zum Setzen und Abrufen einiger Objekteigenschaften:

public:
//--- Constructor indicating the main and base objects, chart ID and subwindow
                     CGlareObj(CGCnvElement *main_obj,CGCnvElement *base_obj,
                               const long chart_id,
                               const int subwindow,
                               const string descript,
                               const int x,
                               const int y,
                               const int w,
                               const int h);

//--- Supported object properties (1) integer, (2) real and (3) string ones
   virtual bool      SupportProperty(ENUM_CANV_ELEMENT_PROP_INTEGER property) { return true; }
   virtual bool      SupportProperty(ENUM_CANV_ELEMENT_PROP_DOUBLE property)  { return true; }
   virtual bool      SupportProperty(ENUM_CANV_ELEMENT_PROP_STRING property)  { return true; }
   
//--- Draw the object glare
   void              Draw(void);
//--- Redraw the object
   virtual void      Redraw(bool redraw);
//--- Set the element opacity
   virtual void      SetOpacity(const uchar value,const bool redraw=false);
//--- Initialize the properties
   void              Initialize(void);

//+------------------------------------------------------------------+
//| Methods of simplified access to object properties                |
//+------------------------------------------------------------------+
//--- (1) Set and (2) return the glare color
   void              SetColor(const color colour)                             { this.m_color=colour;     }
   color             Color(void)                                        const { return this.m_color;     }
//--- (1) Set and (2) return the opacity of the drawn glare
   void              SetOpacityDraw(const uchar opacity)                      { this.m_opacity=opacity;  }
   uchar             OpacityDraw(void)                                  const { return this.m_opacity;   }
//--- (1) Set and (2) return the glare blur
   void              SetBlur(const uchar blur)                                { this.m_blur=blur;        }
   uchar             Blur(void)                                         const { return this.m_blur;      }
//--- (1) Set and (2) return the glare visual effect
   void              SetVisualEffectStyle(const ENUM_CANV_ELEMENT_VISUAL_EFF_STYLE style)
                       { this.m_style=style;                                                             }
   ENUM_CANV_ELEMENT_VISUAL_EFF_STYLE VisualEffectStyle(void)           const { return this.m_style;     }
//--- Set the glare visual effect style as (1) rectangle and (2) parallelogram
   void              SetStyleRectangle(void)
                       { this.SetVisualEffectStyle(CANV_ELEMENT_VISUAL_EFF_STYLE_RECTANGLE);             }
   void              SetStyleParallelogramm(void)
                       { this.SetVisualEffectStyle(CANV_ELEMENT_VISUAL_EFF_STYLE_PARALLELOGRAMM);        }
  };
//+------------------------------------------------------------------+

Der Methodenblock für den vereinfachten Zugriff auf Objekteigenschaften umfasst die Methoden, in denen die an die Methode übergebenen Werte für private Variablen gesetzt und die Werte aus diesen Variablen zurückgegeben werden.

In den Klassenkonstruktoren ersetzen wir die Initialisierung der übergeordneten Klasse CShadowObj durch CWinFormBase und fügen den Aufruf der Variableninitialisierungsmethode hinzu:

//+-----------------------------------------------+
//| Protected constructor with an object type,    |
//| chart ID and subwindow                        |
//+-----------------------------------------------+
CGlareObj::CGlareObj(const ENUM_GRAPH_ELEMENT_TYPE type,
                     CGCnvElement *main_obj,CGCnvElement *base_obj,
                     const long chart_id,
                     const int subwindow,
                     const string descript,
                     const int x,
                     const int y,
                     const int w,
                     const int h) : CWinFormBase(type,main_obj,base_obj,chart_id,subwindow,descript,x-OUTER_AREA_SIZE,y-OUTER_AREA_SIZE,w+OUTER_AREA_SIZE*2,h+OUTER_AREA_SIZE*2)
  {
//--- Set the specified graphical element type for the object and assign the library object type to the current object
   this.SetTypeElement(type);
   this.m_type=OBJECT_DE_TYPE_GGLARE;
   this.Initialize();
  }
//+------------------------------------------------------------------+
//| Constructor indicating the main and base objects,                |
//| chart ID and subwindow                                           |
//+------------------------------------------------------------------+
CGlareObj::CGlareObj(CGCnvElement *main_obj,CGCnvElement *base_obj,
                     const long chart_id,
                     const int subwindow,
                     const string descript,
                     const int x,
                     const int y,
                     const int w,
                     const int h) : CWinFormBase(GRAPH_ELEMENT_TYPE_WF_GLARE_OBJ,main_obj,base_obj,chart_id,subwindow,descript,x,y,w,h)
  {
   this.m_type=OBJECT_DE_TYPE_GGLARE; 
   this.Initialize();
  }
//+------------------------------------------------------------------+

Anstatt nun in jedem Konstruktor die Initialisierungsstrings für jede Variable zu schreiben (die für beide Konstruktoren gleich sind), rufen wir einfach die Methode zur Initialisierung der Objekteigenschaften auf:

//+-----------------------------------------------+
//| Initialize the properties                     |
//+-----------------------------------------------+
void CGlareObj::Initialize(void)
  {
   CGCnvElement::SetBackgroundColor(CLR_CANV_NULL,true);
   CGCnvElement::SetActive(false);
   this.SetBlur(4);
   this.SetColor(clrWhite);
   this.SetOpacity(127);
   this.m_shadow=false;
   this.SetVisibleFlag(false,false);
   this.SetVisualEffectStyle(CANV_ELEMENT_VISUAL_EFF_STYLE_RECTANGLE);
   this.SetDisplayState(CANV_ELEMENT_DISPLAY_STATE_NORMAL);
   this.SetDisplayed(false);
  }
//+------------------------------------------------------------------+

Die Methode setzt Standardwerte für Objekteigenschaften. Der Weichzeichner ist auf 4 eingestellt, die Farbe des Weichzeichners ist weiß. Die Deckkraft ist auf 127 eingestellt - ein halbtransparentes Objekt, kein Schatten. Wir setzen das Objektunsichtbarkeitsflag, der visuelle Effekt ist eine rechteckige Blende. Der Anzeigezustand ist normal (das Objekt ist ausgeblendet), während das Anzeigeflag zurückgesetzt ist - das Objekt sollte nicht auf dem angezeigten übergeordneten Objekt angezeigt werden.


Die Methode zur Einstellung der Deckkraft des Elements:

//+-----------------------------------------------+
//| Set the element opacity                       |
//+-----------------------------------------------+
void CGlareObj::SetOpacity(const uchar value,const bool redraw=false)
  {
   CGCnvElement::SetOpacity(0,false);
   this.SetOpacityDraw(value>(uchar)CLR_DEF_SHADOW_OPACITY ? (uchar)CLR_DEF_SHADOW_OPACITY : value);
   this.m_canvas.Update(redraw);
  }
//+------------------------------------------------------------------+

Die Methode ist identisch mit der gleichnamigen Methode für Schattenobjekte. Hier wird die vollständige Transparenz für die Unterlage, auf der die Blende gezeichnet wird, festgelegt, während der an die Methode übergebene Wert für die Blendenfarbe festgelegt wird. Wenn der an die Methode übergebene Wert für die Deckkraft größer ist als der Standardwert für die Deckkraft des Schattens, wird der Standardwert verwendet. Andernfalls wird der an die Methode übergebene Wert verwendet. Dies geschieht, damit das Objekt nie völlig undurchsichtig ist. Ich verwende den Standardwert für den Schatten, weil er für diese Zwecke gut geeignet ist.

Methode zum Zeichnen der Blende:

//+-----------------------------------------------+
//| Draw the object glare                         |
//+-----------------------------------------------+
void CGlareObj::Draw(void)
  {
   if(!this.IsVisible())
      return;
//--- Draw the glare
   this.DrawGlareFigure();
  }
//+------------------------------------------------------------------+

Wenn das Objekt ausgeblendet ist, sollte nichts gezeichnet werden. Wir verlassen die Methode.
Dann rufen wir die Methode auf , in der die Zeichenmethode festgelegt ist:

//+-----------------------------------------------+
//| Draw the object glare form                    |
//+-----------------------------------------------+
void CGlareObj::DrawGlareFigure()
  {
   switch(this.VisualEffectStyle())
     {
      case CANV_ELEMENT_VISUAL_EFF_STYLE_RECTANGLE       : this.DrawFigureRectangle();       break;
      case CANV_ELEMENT_VISUAL_EFF_STYLE_PARALLELOGRAMM  : this.DrawFigureParallelogram();   break;
      default:
        break;
     }
  }
//+------------------------------------------------------------------+

Je nach Art der gezeichneten Blende rufen wir die entsprechende Methode auf, die die Blendenform tatsächlich zeichnet.


Die Methode, die die rechteckige Form eines Blendenobjekts der Blende zeichnet:

//+-----------------------------------------------+
//| Draw the rectangle object glare form          |
//+-----------------------------------------------+
void CGlareObj::DrawFigureRectangle(void)
  {
   CGCnvElement::DrawRectangleFill(0,0,this.Width()-1,this.Height()-1,this.m_color,this.OpacityDraw());
   CGCnvElement::Update();
  }
//+------------------------------------------------------------------+

Hier wird ein gefülltes Rechteck über die gesamte Größe des Objekts gezeichnet, wobei die Objektfarbe und die Zeichnungsopazität eingestellt sind.


Die Methode, die die Form des Blendenobjekts in Form eines Parallelogramms zeichnet:

//+------------------------------------------------------------------+
//| Draw the shape of the object glare as a parallelogram            |
//+------------------------------------------------------------------+
void CGlareObj::DrawFigureParallelogram(void)
  {
   int array_x[]={6,this.Width()-1,this.Width()-1-6,0};
   int array_y[]={0,0,this.Height()-1,this.Height()-1};
   CGCnvElement::DrawPolygonFill(array_x,array_y,this.m_color,this.OpacityDraw());
   CGCnvElement::Update();
  }
//+------------------------------------------------------------------+

Hier wird ein „schiefes“ Dreieck gezeichnet. Zunächst deklarieren wir Arrays mit den X- und Y-Koordinaten der Polygonscheitelpunkte, und zeichnen dann unter Verwendung der in den Arrays angegebenen Scheitelpunkte ein gefülltes Polygon in Form eines schiefen Rechtecks - ein Parallelogramm mit der für das Objekt eingestellten Farbe und Zeichnungsopazität.


Wenn ich einfach ein Objekt einmal zeichne und es dann verschiebe, bleibt das Blendenobjekt sichtbar, wenn das verschobene Objekt über die Grenzen des Fortschrittsbalkens hinausgeht (in diesem Fall bewegt sich das Objekt nämlich an ihm entlang). Optisch sieht es so aus, als ob die Blende aus dem Fortschrittsbalken „herausspringt“ und auf das Paneel gezeichnet wird, an das das ProgressBar-Steuerelement angehängt ist. Um dies zu vermeiden, müssen wir das Objekt neu zeichnen und dabei die Teile abschneiden, die über den Container hinausgehen. Hierfür benötigen wir die Methode zum Neuzeichnen von Objekten.

Die Methode zum Neuzeichnen eines Objekts:

//+-----------------------------------------------+
//| Redraw the object                             |
//+-----------------------------------------------+
void CGlareObj::Redraw(bool redraw)
  {
   CGCnvElement::Erase(false);
   this.Draw();
   this.Crop();
   this.Update(redraw);
  }
//+------------------------------------------------------------------+

Hier leeren wir zunächst das Objekt komplett und füllen es dann mit Farbe und Deckkraft. Da die transparente Farbe als Hintergrundfarbe festgelegt wird, während der Deckkraftwert Null ist, wird das Objekt vollständig transparent. Dann rufen wir die zuvor besprochene Methode zum Zeichnen der installierten Blendenform auf, schneiden die Bereiche ab, die über den Objektbereich-Container hinausragen, und aktualisieren die Leinwand (canvas).

Jetzt brauchen wir die Methode nur noch aufzurufen, wenn wir das Objekt entlang des Fortschrittsbalkens bewegen, und die Blende wird nicht über den Container hinausgehen.


Entwickeln wir die Funktionsweise des ProgressBar-Objekts entwickeln.

Um den Fortschritt eines bestimmten Prozesses anzuzeigen, müssen wir die Breite des BarProgressBar-Objekts um den Betrag des Erhöhungsschritts des Fortschrittsbalkens vergrößern. Im Allgemeinen sind alle Werte für die Breite des Fortschrittsbalkens relativ - als Prozentsatz der Breite des ProgressBar-Objekts und als Prozentsatz des Maximalwerts des Fortschrittsbalkenparameters Value. Der Fortschrittsbalken kann seine Werte innerhalb der in den Parametern Minimum und Maximum festgelegten Grenzen ändern. Diese Werte sollten jedoch nicht in Pixeln angegeben werden, sondern als Prozentsatz der Objektbreite. Dann ist der Wert 100 100 Pixel breit, wenn der Fortschrittsbalken die gleiche Breite hat, aber 200, wenn der Fortschrittsbalken 200 Pixel breit ist. Wenn in diesem Fall der Wert 50 ist, entspricht dies den Werten 50 und 100 Pixel für die obigen Objektbreitenwerte.

Damit wir die Werte des Fortschrittsbalkens einfach kontrollieren können, werden wir vor der Verwendung des Objekts vor dem Beginn des Zyklus, dessen Verlauf angezeigt werden soll, die Mindest- und Höchstwerte der Grenzwerte festlegen und den Schritt festlegen, um den der Wert der Breite des Fortschrittsbalkens erhöht werden soll. Nach Abschluss der nächsten Aktion im verfolgten Zyklus brauchen wir nur die Methode PerformStep() des ProgressBar-Objekts aufzurufen, und die Breite des Fortschrittsbalkens wird entsprechend dem angegebenen Erhöhungsschritt vergrößert. Daher sollten wir immer wie folgt vorgehen: Wenn wir die Anzahl der Iterationen der verfolgten Schleife kennen, setzen wir die erforderlichen Werte für das ProgressBar-Objekt, und innerhalb der Schleife, wenn die nächste Iteration abgeschlossen ist, rufen wir die PerformStep()-Methode auf, wodurch sich der Fortschrittsbalken um einen Schritt verändert.

In \MQL5\Include\DoEasy\Objects\Graph\WForms\Common Controls\ProgressBar.mqh, und zwar in seinem privaten Abschnitt, deklarieren wir die Variablen zum Speichern der Werte des Fortschrittsbalkens und die Methode zur Berechnung der Breite des Fortschrittsbalkens:

//+------------------------------------------------------------------+
//| ArrowLeftRightBox object class of WForms controls                |
//+------------------------------------------------------------------+
class CProgressBar : public CContainer
  {
private:
   int               m_progress_bar_max;  // Maximum progress bar width
   int               m_value_by_max;      // Value relative to Maximum
   int               m_steps_skipped;     // Number of skipped steps of increasing the width of the progress bar
//--- Create a new graphical object
   virtual CGCnvElement *CreateNewGObject(const ENUM_GRAPH_ELEMENT_TYPE type,
                                          const int element_num,
                                          const string descript,
                                          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 the progress bar object
   void              CreateProgressBar(void);
//--- Calculate the progress bar width
   int               CalculateProgressBarWidth(void);
//--- Initialize the element properties
   void              Initialize(void);

protected:


Wir verschieben die öffentliche Methode SetValue() außerhalb des Klassenkörpers und lassen nur ihre Deklaration im Körper:

//--- (1) Set and (2) return the progress bar increment to redraw it
   void              SetStep(const int value)
                       {
                        this.SetProperty(CANV_ELEMENT_PROP_PROGRESS_BAR_STEP,value);
                        CBarProgressBar *bar=this.GetProgressBar();
                        if(bar!=NULL)
                           bar.SetStep(value);
                       }
   int               Step(void)                          const { return (int)this.GetProperty(CANV_ELEMENT_PROP_PROGRESS_BAR_STEP);    }
   
//--- (1) Set and (2) return the current value of the progress bar in the range from Min to Max
   void              SetValue(const int value);
   int               Value(void)                         const { return (int)this.GetProperty(CANV_ELEMENT_PROP_PROGRESS_BAR_VALUE);   }
   
//--- (1) Set and (2) return the upper bound of the ProgressBar operating range
   void              SetMaximum(const int value)               { this.SetProperty(CANV_ELEMENT_PROP_PROGRESS_BAR_MAXIMUM,value);       }
   int               Maximum(void)                       const { return (int)this.GetProperty(CANV_ELEMENT_PROP_PROGRESS_BAR_MAXIMUM); }


Deklaration von weiteren Methoden:

//--- Return the pointer to the progress bar object
   CBarProgressBar  *GetProgressBar(void)                { return this.GetElementByType(GRAPH_ELEMENT_TYPE_WF_BAR_PROGRESS_BAR,0);     }

//--- Resets the progress bar values to the set minimum
   void              ResetProgressBar(void)              { this.SetValue(this.Minimum());    }
//--- Set the element width
   virtual bool      SetWidth(const int width);
//--- Initialize values for handling in PerformStep
   void              SetValuesForProcessing(const int minimum,const int maximum,const int step,const int steps_skipped);
//--- Increase the current position of the progress bar by the step value
   void              PerformStep();

//--- Supported object properties (1) integer, (2) real and (3) string ones
   virtual bool      SupportProperty(ENUM_CANV_ELEMENT_PROP_INTEGER property) { return true; }
   virtual bool      SupportProperty(ENUM_CANV_ELEMENT_PROP_DOUBLE property)  { return true; }
   virtual bool      SupportProperty(ENUM_CANV_ELEMENT_PROP_STRING property)  { return true; }

Die Methode ResetProgressBar() ist direkt im Körper der Klasse implementiert und setzt den Wert des Fortschrittsbalkens auf den in der Eigenschaft Minimum eingestellten Wert. So können wir den Fortschrittsbalken in seinen ursprünglichen Zustand zurückversetzen.

In der Initialisierungsmethode für den Standardwert setzen wir den Wert des Fortschrittsbalkens auf 50 % der Objektbreite und berechnen die maximale Breite in Pixeln für das BarProgressBar-Objekt und den Wert in % der Objektbreite:

//+-----------------------------------------------+
//| Initialize the element properties             |
//+-----------------------------------------------+
void CProgressBar::Initialize(void)
  {
   this.SetBorderSizeAll(1);
   this.SetBorderStyle(FRAME_STYLE_SIMPLE);
   this.SetBackgroundColor(CLR_DEF_CONTROL_PROGRESS_BAR_BACK_COLOR,true);
   this.SetBorderColor(CLR_DEF_CONTROL_PROGRESS_BAR_BORDER_COLOR,true);
   this.SetForeColor(CLR_DEF_CONTROL_PROGRESS_BAR_FORE_COLOR,true);
   this.SetMarqueeAnimationSpeed(10);
   this.SetMaximum(100);
   this.SetMinimum(0);
   this.SetValue(50);
   this.SetStep(10);
   this.SetStyle(CANV_ELEMENT_PROGRESS_BAR_STYLE_CONTINUOUS);
   this.m_progress_bar_max=this.Width()-this.BorderSizeLeft()-this.BorderSizeRight();
   this.m_value_by_max=this.Value()*100/this.Maximum();
   this.m_steps_skipped=0;
  }
//+------------------------------------------------------------------+

Die maximale Breite des Fortschrittsbalkens sollte nicht größer sein als die Breite des Hintergrunds abzüglich der Größe seines Rahmens auf der linken und rechten Seite, sodass der Fortschrittsbalken in den auf dem Hintergrund gezeichneten Rahmen passt und nicht darüber hinausgeht.


In der Methode, die das Fortschrittsbalkenobjekt erstellt, legen wir die Breite des erstellten Objekts anhand des mit der Methode CalculateProgressBarWidth() berechneten Werts fest und fügen die Erstellung des Blendenobjekts hinzu:

//+-----------------------------------------------+
//| Create the progress bar object                |
//+-----------------------------------------------+
void CProgressBar::CreateProgressBar(void)
  {
//--- Set the length of the progress bar equal to the object Value()
//--- The height of the progress bar is equal to the height of the object minus the top and bottom frame sizes
   int w=this.CalculateProgressBarWidth();
   int h=this.Height()-this.BorderSizeTop()-this.BorderSizeBottom();
//--- Create the progress bar object
   this.CreateNewElement(GRAPH_ELEMENT_TYPE_WF_BAR_PROGRESS_BAR,0,0,w,h,clrNONE,255,false,false);
//--- Create the glare object
   this.CreateNewElement(GRAPH_ELEMENT_TYPE_WF_GLARE_OBJ,0,0,w,h,CLR_CANV_NULL,0,true,false);
//--- Add the current CProgressBar object to the list of active elements of the collection
   this.AddObjToListActiveElements();
  }
//+------------------------------------------------------------------+

Die Logik der Methode wird in den Codekommentaren beschrieben. Hier ist alles ganz einfach: Wir berechnen die Breite des Fortschrittsbalkens und erstellen ein Objekt mit der berechneten Breite. Der Standardwert ist 50% der Breite des ProgressBar-Objekts. Dann erstellen wir ein Blendenobjekt und fügen das aktuelle Objekt zur Liste der aktiven Objekte hinzu. Alle Objekte in der Liste der aktiven Elemente werden im Zeitgeber der Bibliothek verarbeitet und können einige unabhängige Aktionen ausführen, die im Zeitgeber-Handler dieser Objekte implementiert sind. Bei diesem Steuerelement wird der Timer das Erscheinen einer Blende auf dem Fortschrittsbalken steuern.


Die Methode, die die in PerformStep zu verarbeitenden Werte initialisiert:

//+-----------------------------------------------+
//| Initialize values for handling in PerformStep |
//+-----------------------------------------------+
void CProgressBar::SetValuesForProcessing(const int minimum,const int maximum,const int step,const int steps_skipped)
  {
   this.SetMinimum(minimum<0 ? 0 : minimum);
   this.SetMaximum(maximum<this.Minimum() ? this.Minimum() : maximum);
   this.SetStep(step<0 ? 0 : step);
   this.m_steps_skipped=steps_skipped;
  }
//+------------------------------------------------------------------+

Hier werden die Werte der Mindest- und Höchstgrenzen an die Methode übergeben, innerhalb derer sich der Wert, der Änderungsschritt und die Anzahl der übersprungenen Schritte ändern können. Anschließend werden die Werte des Minimums, des Maximums und des Schritts auf ihre Korrektheit überprüft und der Wert der übersprungenen Schritte in die Variable eingetragen. Da der Wert des Fortschrittsbalkens als Prozentsatz der Breite des ProgressBar-Objekts berechnet wird, wird der Wert der übersprungenen Schritte höchstwahrscheinlich nicht verwendet - dies wird durch eine Reihe von Objekttests deutlich werden, nachdem das Objekt fertig ist. In der derzeitigen Implementierung wird dieser Wert nirgends verwendet.

Die Methode, die den aktuellen Wert des Fortschrittsbalkens festlegt:

//+-----------------------------------------------+
//| Set the current value of the progress bar     |
//+-----------------------------------------------+
void CProgressBar::SetValue(const int value)
  {
//--- Correct the value passed to the method and set it to the object property
   int v=(value<this.Minimum() ? this.Minimum() : value>this.Maximum() ? this.Maximum() : value);
   this.SetProperty(CANV_ELEMENT_PROP_PROGRESS_BAR_VALUE,v);
//--- Get the progress bar object
   CBarProgressBar *bar=this.GetProgressBar();
   if(bar!=NULL)
     {
      //--- Set 'value' for the progress bar
      bar.SetValue(v);
      //--- Calculate the width of the progress bar object
      int w=this.CalculateProgressBarWidth();
      //--- If the calculated width is greater than the maximum possible value, set the maximum width
      if(w>this.m_progress_bar_max)
         w=this.m_progress_bar_max;
      //--- If the width is less than 1, then
      if(w<1)
        {
         //--- hide the progress bar and redraw the chart to display changes immediately
         bar.Hide();
         ::ChartRedraw(bar.ChartID());
        }
      //--- If the width value is not less than 1
      else
        {
         //--- If the progress bar is hidden, display it and
         if(!bar.IsVisible())
            bar.Show();
         //--- change its size according to the received width
         bar.Resize(w,bar.Height(),true);
        }
     }
  }
//+------------------------------------------------------------------+

Die Methodenlogik wird in den Codekommentaren beschrieben. Wenn sich bei der Berechnung der Breite eines Objekts herausstellt, dass seine Breite weniger als ein Pixel beträgt, kann ein solcher Wert für das Objekt nicht festgelegt werden. Daher erhält er eine Breite von einem Pixel, und das Objekt wird ausgeblendet, wodurch ein Fortschrittsbalken mit einer Breite von Null simuliert wird.


Die Methode, die die aktuelle Position des Fortschrittsbalkens um den Schrittwert erhöht:

//+----------------------------------------------------------------------+
//| Increase the current position of the progress bar by the step value  |
//+----------------------------------------------------------------------+
void CProgressBar::PerformStep(void)
  {
   this.SetValue(this.Value()+this.Step());
  }
//+------------------------------------------------------------------+

Hier setzen wir einfach die Eigenschaft „Wert“ des Objekts auf seinen bisherigen Wert plus den zuvor festgelegten Wert der Erhöhung dieses Werts. Bei jedem weiteren Aufruf dieser Methode erhöht sich der Wert von Value also um die in Step aufgezeichnete Schrittweite.


Die Methode zur Berechnung der Breite des Fortschrittsbalkens:

//+-----------------------------------------------+
//| Calculate the width of the progress bar       |
//+-----------------------------------------------+
int CProgressBar::CalculateProgressBarWidth(void)   

  {
   this.m_value_by_max=this.Value()*100/this.Maximum();
   return this.m_progress_bar_max*this.m_value_by_max/100;
  }
//+------------------------------------------------------------------+

Hier berechnen wir zunächst den Wert als Prozentsatz des maximal möglichen Wertes und geben dann die relative Breite der maximalen Breite des Fortschrittsbalkens als Prozentsatz des oben berechneten Wertes zurück.


Die Methode, die die neue Objektbreite festlegt:

//+-----------------------------------------------+
//| Set a new width                               |
//+-----------------------------------------------+
bool CProgressBar::SetWidth(const int width)
  {
   if(!CGCnvElement::SetWidth(width))
      return false;
   this.m_progress_bar_max=this.Width()-this.BorderSizeLeft()-this.BorderSizeRight();
   CBarProgressBar *bar=this.GetProgressBar();
   if(bar==NULL)
      return false;
   int w=this.CalculateProgressBarWidth();
   bar.SetWidth(w);
   return true;
  }
//+------------------------------------------------------------------+

Hier wird zunächst die neue Breite der Unterlegung festgelegt und dann die maximale Breite des Fortschrittsbalkens in Pixeln berechnet.
Als Nächstes ermitteln wir die relative Breite des Fortschrittsbalkens in % und setzen sie auf das CBarProgressBar-Objekt.


Alle visuellen Effekte, die auf dem Fortschrittsbalken erscheinen, sollten im Timer des BarProgressBar-Objekts verarbeitet werden. Die Blende sollte nach einer Pause entlang des Fortschrittsbalkens laufen: Pause - Blende - Pause - Blende, usw. Um dieses Verhalten zu implementieren, müssen wir einige Werte für die Eigenschaften festlegen, die die Objekte bereits haben. In früheren Artikeln haben wir solche Eigenschaften festgelegt, um das reibungslose Erscheinen/Verschwinden von Tooltips zu implementieren. Diese Eigenschaften sind auch für das Blendenobjekt geeignet.

In die Datei \MQL5\Include\DoEasy\Objects\Graph\WForms\Helpers\BarProgressBar.mqh fügen wir die Fortschrittsbalken-Objektklasse der Glare-Objektdatei ein. In den privaten Abschnitt schreiben wir die Methoden zum Setzen/Rückgeben der Verzögerung und deklarieren die Methode zum Initialisieren der Objekteigenschaften:

//+------------------------------------------------------------------+
//|                                               BarProgressBar.mqh |
//|                                  Copyright 2022, MetaQuotes Ltd. |
//|                             https://mql5.com/en/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2022, MetaQuotes Ltd."
#property link      "https://mql5.com/en/users/artmedia70"
#property version   "1.00"
#property strict    // Necessary for mql4
//+-----------------------------------------------+
//| Include files                                 |
//+-----------------------------------------------+
#include "..\WinFormBase.mqh"
#include "..\GlareObj.mqh"
//+------------------------------------------------------------------+
//| BarProgressBar object class of the ProgressBar control           |
//+------------------------------------------------------------------+
class CBarProgressBar : public CWinFormBase
  {
private:
//--- (1) Set and (2) return a pause before displaying the effect
   void              SetShowDelay(const long delay)               { this.SetProperty(CANV_ELEMENT_PROP_TOOLTIP_RESHOW_DELAY,delay);             }
   ulong             ShowDelay(void)                              { return this.GetProperty(CANV_ELEMENT_PROP_TOOLTIP_RESHOW_DELAY);            }
//--- Initialize the properties
   void              Initialize(void);
protected:


In beiden Klassenkonstruktoren implementieren wir an Stelle ähnlicher Zeilen zum Einstellen von Eigenschaften den Aufruf der Methode zum Einstellen der Eigenschaften:

//+------------------------------------------------------------------+
//| Protected constructor with an object type,                       |
//| chart ID and subwindow                                           |
//+------------------------------------------------------------------+
CBarProgressBar::CBarProgressBar(const ENUM_GRAPH_ELEMENT_TYPE type,
                                 CGCnvElement *main_obj,CGCnvElement *base_obj,
                                 const long chart_id,
                                 const int subwindow,
                                 const string descript,
                                 const int x,
                                 const int y,
                                 const int w,
                                 const int h) : CWinFormBase(type,main_obj,base_obj,chart_id,subwindow,descript,x,y,w,h)
  {
//--- Set the specified graphical element type for the object and assign the library object type to the current object
   this.SetTypeElement(type);
   this.m_type=OBJECT_DE_TYPE_GWF_HELPER;
   this.Initialize();
  }
//+------------------------------------------------------------------+
//| Constructor indicating the main and base objects,                |
//| chart ID and subwindow                                           |
//+------------------------------------------------------------------+
CBarProgressBar::CBarProgressBar(CGCnvElement *main_obj,CGCnvElement *base_obj,
                                 const long chart_id,
                                 const int subwindow,
                                 const string descript,
                                 const int x,
                                 const int y,
                                 const int w,
                                 const int h) : CWinFormBase(GRAPH_ELEMENT_TYPE_WF_BAR_PROGRESS_BAR,main_obj,base_obj,chart_id,subwindow,descript,x,y,w,h)
  {
   this.SetTypeElement(GRAPH_ELEMENT_TYPE_WF_BAR_PROGRESS_BAR);
   this.m_type=OBJECT_DE_TYPE_GWF_HELPER;
   this.Initialize();
  }
//+------------------------------------------------------------------+


Wir verschieben die Zeilen zur Initialisierung von Eigenschaften, die aus Konstruktoren entfernt wurden, in die Initialisierungsmethode:

//+-----------------------------------------------+
//| Initialize the properties                     |
//+-----------------------------------------------+
void CBarProgressBar::Initialize(void)
  {
   this.SetPaddingAll(0);
   this.SetMarginAll(0);
   this.SetBorderSizeAll(0);
   this.SetBackgroundColor(CLR_DEF_CONTROL_PROGRESS_BAR_BAR_COLOR,true);
   this.SetBorderColor(CLR_DEF_CONTROL_PROGRESS_BAR_BAR_COLOR,true);
   this.SetForeColor(CLR_DEF_CONTROL_PROGRESS_BAR_FORE_COLOR,true);
   this.SetShowDelay(2000);
  }
//+------------------------------------------------------------------+

Zusätzlich zu den verschobenen Zeilen habe ich hier die Implementierung einer zwei Sekunden langen Pause zwischen den Blenden auf den Fortschrittsbalken hinzugefügt.

Zuvor haben wir im Objekt des Timers einfach den Wert der Funktion GetTickCount() an das Chart im Kommentar gesendet.
Nun wollen wir einen vollwertigen Ereignisbehandlung schreiben.

Ereignisbehandlung durch den Timer:

//+-----------------------------------------------+
//| Timer                                         |
//+-----------------------------------------------+
void CBarProgressBar::OnTimer(void)
  {
   CWinFormBase *base=this.GetBase();
   if(base==NULL)
      return;
   CWinFormBase *glare=base.GetElementByType(GRAPH_ELEMENT_TYPE_WF_GLARE_OBJ,0);
   if(glare==NULL)
      return;
      
//--- If the object is in the normal state (hidden)
   if(glare.DisplayState()==CANV_ELEMENT_DISPLAY_STATE_NORMAL)
     {
      //--- set the state of waiting for fading in to the object (in our case, waiting for a shift along the progress bar),
      //--- set the waiting duration and set the countdown time
      glare.SetDisplayState(CANV_ELEMENT_DISPLAY_STATE_WAITING_FADE_IN);
      this.m_pause.SetWaitingMSC(this.ShowDelay());
      this.m_pause.SetTimeBegin();
      //--- If the right edge of the glare object is to the right of the left edge of the progress bar object
      if(glare.RightEdge()>=this.CoordX())
        {
         //--- Hide the glare object and move it beyond the right edge of the progress bar
         glare.Hide();
         if(glare.Move(this.CoordX()-glare.Width(),this.CoordY()))
           {
            //--- Set the relative coordinates of the glare object
            glare.SetCoordXRelative(glare.CoordX()-this.CoordX());
            glare.SetCoordYRelative(glare.CoordY()-this.CoordY());
            //--- and its visibility scope equal to the entire object
            glare.SetVisibleArea(0,0,glare.Width(),glare.Height());
           }
        }
      return;
     }
//--- If the object is in the state of waiting for fading in (in our case, waiting for a shift along the progress bar)
   if(glare.DisplayState()==CANV_ELEMENT_DISPLAY_STATE_WAITING_FADE_IN)
     {
      //--- If the waiting time has not yet passed, leave
      if(this.m_pause.Passed()<this.ShowDelay())
         return;
      //--- Set the state of the object being in the process of shifting along the progress bar and
      //---  set the process start countdown time
      glare.SetDisplayState(CANV_ELEMENT_DISPLAY_STATE_PROCESS_FADE_IN);
      this.m_pause.SetTimeBegin();
      return;
     }
//--- If the object is in the state of a shift along the progress bar
   if(glare.DisplayState()==CANV_ELEMENT_DISPLAY_STATE_PROCESS_FADE_IN)
     {
      //--- If the glare object X coordinate still has not gone beyond the right edge of the progress bar
      if(glare.CoordX()<=this.RightEdge())
        {
         //--- set the display flag and show the object
         if(!glare.Displayed())
            glare.SetDisplayed(true);
         if(!glare.IsVisible())
            glare.Show();
         //--- bring the highlight object to the foreground
         glare.BringToTop();
         //--- Shift the highlight by 16 pixels to the right
         if(glare.Move(glare.CoordX()+16,this.CoordY()))
           {
            //--- Set the relative coordinates of the highlight object and redraw it
            glare.SetCoordXRelative(glare.CoordX()-this.CoordX());
            glare.SetCoordYRelative(glare.CoordY()-this.CoordY());
            glare.Redraw(true);
           }
         return;
        }
      //--- Set the completion state of the shift along the progress bar
      glare.SetDisplayState(CANV_ELEMENT_DISPLAY_STATE_COMPLETED_FADE_IN);
     }
//--- If the object is in the state of completion of shifting along the progress bar
   if(glare.DisplayState()==CANV_ELEMENT_DISPLAY_STATE_COMPLETED_FADE_IN)
     {
      //--- set the object to its normal state (invisible),
      //--- hide the object and set its invisibility flag
      glare.SetDisplayState(CANV_ELEMENT_DISPLAY_STATE_NORMAL);
      glare.Hide();
      glare.SetDisplayed(false);
      return;
     }
  }
//+------------------------------------------------------------------+

Die Logik der Methode ist in den Kommentaren zum Code vollständig beschrieben und ist identisch mit der Ereignisbehandlung des Timers, den ich für die Tooltip-Objekte im vorherigen Artikel implementiert habe. Dies ist nur eine stärker verkleinerte Version. Bei Tooltip-Objekten warten wir auf das Erscheinen des Objekts (auch hier), dann wird das Objekt eingeblendet (hier läuft es entlang des Fortschrittsbalkens), dann wird gewartet, bis es ausgeblendet wird, und dann wird es ausgeblendet (nicht in diesem Fall). Das ist der ganze Unterschied. Aber damit die Blende erscheint und entlang des Fortschrittsbalkens verläuft, verwenden wir die gleichen Werte der Enumeration ENUM_CANV_ELEMENT_DISPLAY_STATE wie bei den Tooltip-Objekten:

//+-----------------------------------------------+
//| List of control display states                |
//+-----------------------------------------------+
enum ENUM_CANV_ELEMENT_DISPLAY_STATE
  {
   CANV_ELEMENT_DISPLAY_STATE_NORMAL,                 // Normal
   CANV_ELEMENT_DISPLAY_STATE_WAITING_FADE_IN,        // Wait for fading in
   CANV_ELEMENT_DISPLAY_STATE_PROCESS_FADE_IN,        // Fading in
   CANV_ELEMENT_DISPLAY_STATE_COMPLETED_FADE_IN,      // Fading in end
   CANV_ELEMENT_DISPLAY_STATE_WAITING_FADE_OUT,       // Wait for fading out
   CANV_ELEMENT_DISPLAY_STATE_PROCESS_FADE_OUT,       // Fading out
   CANV_ELEMENT_DISPLAY_STATE_COMPLETED_FADE_OUT,     // Fading out end
   CANV_ELEMENT_DISPLAY_STATE_COMPLETED,              // Complete processing
  };
//+------------------------------------------------------------------+

Die Konstanten dieser Enumeration sind so universell, dass sie für die Behandlung verschiedener Ereignisse in verschiedenen Objekten verwendet werden können. Bei Bedarf kann diese Liste um Konstanten erweitert werden, die weitere mögliche Ereignisse beschreiben.

Alles ist bereit für einen Test.


Test

Um den Test durchzuführen, verwende ich den EA aus dem vorherigen Artikel und speichere ihn in \MQL5\Experts\TestDoEasy\Part127\ als TestDoEasy127.mq5.

Wir haben zuvor ein statisches ProgressBar-Objekt auf der zweiten Leiste des SplitContainer-Steuerelements erstellt, das sich auf der ersten Registerkarte des TabControls befindet. Nun müssen wir einen Zeiger auf das ProgressBar-Objekt erhalten und zunächst seine Größe in einer Schleife erhöhen, um visuell zu sehen, wie dies die relative Größe des Fortschrittsbalkens verändert, der auf 50 % der ProgressBar-Breite eingestellt ist. Dann erhöhen wir wiederum in einer Schleife den Wert des Fortschrittsbalkens mit der Methode PerformStep. Damit die Methode funktioniert, müssen wir die erforderlichen Parameter im Voraus festlegen: Minimum = 0, Maximum = 350, Step = 1. Nachdem beide Schleifen abgeschlossen sind, erhalten wir einen Zeiger auf das Blendenobjekt und setzen dessen Anzeigeparameter.

Um all dies zu implementieren, setzen wir den folgenden Codeblock an das Ende von OnInit():

//--- Display and redraw all created panels
   for(int i=0;i<FORMS_TOTAL;i++)
     {
      //--- Get the panel object
      pnl=engine.GetWFPanel("WinForms Panel"+(string)i);
      if(pnl!=NULL)
        {
         //--- display and redraw the panel
         pnl.Show();
         pnl.Redraw(true);
         //--- Get the TabControl object from the panel
         CTabControl *tc=pnl.GetElementByType(GRAPH_ELEMENT_TYPE_WF_TAB_CONTROL,0);
         //--- Get the SplitContainer object from the first tab of the TabControl object
         CSplitContainer *sc=tc.GetTabElementByType(0,GRAPH_ELEMENT_TYPE_WF_SPLIT_CONTAINER,0);
         //--- Get the second panel from the SplitContainer object
         CSplitContainerPanel *scp=sc.GetPanel(1);
         //--- Get the ProgressBar object from the received panel
         CProgressBar *pb=scp.GetElementByType(GRAPH_ELEMENT_TYPE_WF_PROGRESS_BAR,0);
         //--- Wait for 1/10 of a second
         Sleep(100);
         //--- Get the width of the ProgressBar object
         int w=pb.Width();
         //--- In the loop, increase the width of the ProgressBar by 180 pixels with a delay of 1/50
         for(int n=0;n<180;n++)
           {
            Sleep(20);
            pb.Resize(w+n,pb.Height(),true);
           }
         //--- Set the values for PerformStep of the ProgressBar object
         pb.SetValuesForProcessing(0,350,1,0);
         //--- Reset ProgressBar to minimum
         pb.ResetProgressBar();
         //--- Wait for 1/5 second
         Sleep(200);
         //--- In the loop from the minimum to the maximum value of ProgressBar
         for(int n=0;n<=pb.Maximum();n++)
           {
            //--- call the method for increasing the progress bar by a given step with a wait of 1/5 second
            pb.PerformStep();
            Sleep(20);
           }
         //--- Get the glare object
         CGlareObj *gl=pb.GetElementByType(GRAPH_ELEMENT_TYPE_WF_GLARE_OBJ,0);
         if(gl!=NULL)
           {
            //--- Set the glare type - rectangle, opacity 40, color - white
            gl.SetStyleRectangle();
            gl.SetOpacity(40);
            gl.SetColor(clrWhite);
           }
        }
     }
        
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+


Kompilieren und starten Sie den EA auf dem Chart:


In der ersten Schleife sehen wir, wie die Breite des ProgressBar-Objekts zunimmt und die Breite des Fortschrittsbalkens ebenfalls proportional zunimmt. Gleichzeitig wird der Wert anfangs auf 50 gesetzt, und das bleibt auch so, da die Breite des Fortschrittsbalkens in relativen Werten festgelegt wird.

In der zweiten Schleife rufen wir jedes Mal die Methode PerformStep auf, die bei jedem Schritt den (relativen) Wert um den Wert des Inkrementschrittes erhöht. Obwohl es 350 Schritte sind, können wir sehen, dass der Fortschrittsbalken um mehr als 1 Pixel pro Zeiteinheit wächst. Dies ist darauf zurückzuführen, dass alle Werte relativ sind und als Prozentsatz der Breite des Fortschrittsbalkens berechnet werden. Dies ist korrekt, da es unmöglich ist, bei jedem Schritt ein Pixel zu erhöhen. Wenn die Breite des Fortschrittsbalkens 100 Pixel und die Anzahl der Schritte 1000 beträgt, gibt es 10 Inkrementschritte pro Pixel. Sie werden bei der Berechnung der relativen Werte übersprungen.

Wenn die Schleife zur Erhöhung des Fortschrittsbalkens abgeschlossen ist, sehen wir alle zwei Sekunden ein grelles Licht durch ihn laufen. Das reicht zur Demonstration. Für ein normales Erscheinungsbild ist dies jedoch nicht ausreichend. Erstens sollte die Blende entlang des sich bewegenden Fortschrittsbalkens verlaufen und nicht entlang des abgeschlossenen Balkens, und zweitens ist der „Effekt“ zu simpel ausgefallen. Ich werde all dies schrittweise verbessern.

Die Icons der Tooltip-Objekte werden nun normal ausgeblendet.


Was kommt als Nächstes?

Im nächsten Artikel werde ich die Arbeit an den Objekten der WinForms-Bibliothek und dem ProgressBar-Objekt fortsetzen.

Zurück zum Inhalt

*Vorherige Artikel in dieser Reihe:

 
DoEasy. Steuerung (Teil 20): Das WinForms-Objekt SplitContainer
DoEasy. Steuerung (Teil 21): SplitContainer-Steuerung. Paneel-Trennlinie
DoEasy. Steuerung (Teil 22): SplitContainer. Ändern der Eigenschaften des erstellten Objekts
DoEasy. Steuerung (Teil 23): Verbesserung der WinForms-Objekte TabControl und SplitContainer
DoEasy. Steuerung (Teil 24): Hinweis auf WinForms-Hilfsobjekt
DoEasy. Steuerung (Teil 25): Tooltip WinForms-Objekt
DoEasy. Steuerung (Teil 26): Fertigstellung des ToolTip WinForms-Objekts und Weiterführung der ProgressBar-Entwicklung

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

Beigefügte Dateien |
MQL5.zip (4524.46 KB)
Algorithmen zur Optimierung mit Populationen Grauer-Wolf-Optimierung (GWO) Algorithmen zur Optimierung mit Populationen Grauer-Wolf-Optimierung (GWO)
Betrachten wir einen der neuesten modernen Optimierungsalgorithmen - die Grey-Wolf-Optimierung. Das originelle Verhalten bei Testfunktionen macht diesen Algorithmus zu einem der interessantesten unter den zuvor besprochenen Algorithmen. Dies ist einer der besten Algorithmen für das Training neuronaler Netze, glatte Funktionen mit vielen Variablen.
Algorithmen zur Optimierung mit Populationen Künstliches Bienenvolk (Artificial Bee Colony, ABC) Algorithmen zur Optimierung mit Populationen Künstliches Bienenvolk (Artificial Bee Colony, ABC)
In diesem Artikel werden wir den Algorithmus eines künstlichen Bienenvolkes untersuchen und unser Wissen durch neue Prinzipien zur Untersuchung funktionaler Räume ergänzen. In diesem Artikel werde ich meine Interpretation der klassischen Version des Algorithmus vorstellen.
Die Kategorientheorie in MQL5 (Teil 1) Die Kategorientheorie in MQL5 (Teil 1)
Die Kategorientheorie ist ein vielfältiger und expandierender Zweig der Mathematik, der in der MQL-Gemeinschaft noch relativ unentdeckt ist. In dieser Artikelserie sollen einige der Konzepte vorgestellt und untersucht werden, mit dem übergeordneten Ziel, eine offene Bibliothek einzurichten, die zu Kommentaren und Diskussionen anregt und hoffentlich die Nutzung dieses bemerkenswerten Bereichs für die Strategieentwicklung der Händler fördert.
Nicht-lineare Indikatoren Nicht-lineare Indikatoren
In diesem Artikel werde ich versuchen, einige Möglichkeiten zur Erstellung nichtlinearer Indikatoren und deren Verwendung im Handel zu besprechen. In der MetaTrader-Handelsplattform gibt es eine ganze Reihe von Indikatoren, die nicht-lineare Ansätze verwenden.