English Русский 中文 Español 日本語 Português
preview
DoEasy. Steuerung (Teil 6): Paneel-Steuerung, automatische Größenanpassung des Containers an den inneren Inhalt

DoEasy. Steuerung (Teil 6): Paneel-Steuerung, automatische Größenanpassung des Containers an den inneren Inhalt

MetaTrader 5Beispiele | 18 Juli 2022, 11:14
143 0
Artyom Trishkin
Artyom Trishkin

Inhalt


Konzept

In diesem Artikel werde ich die automatische Größenanpassung des Paneels (engl. panel) für den Fall implementieren, dass die Dock-Eigenschaft als aktiv für das an das Paneel gebundene Objekt festgelegt ist. Wenn die Eigenschaft für eines der gebundenen Objekte festgelegt ist, sollte es sich an seinem Bindungsort befinden, während das Paneel seine Größe an die kombinierte Größe aller gebundenen Objekte anpassen sollte. Mit anderen Worten: Die Größe des Bereichs ändert sich, nachdem die Dock-Eigenschaft für eines der damit verbundenen Objekte festgelegt wurde. Wenn die Eigenschaft für eine große Anzahl von angehängten Objekten nacheinander gesetzt wird, sollte das Paneel seine Größe anpassen, um seinen geänderten internen Inhalt mit jedem nächsten gebundenen Objekt anzupassen.

Die Anpassung der Paneelgröße an eine neue Objektanordnung verursacht unangenehme visuelle Effekte. Um diese zu vermeiden, habe ich die Batch-Verarbeitung beim Binden von Objekten an das Paneel und beim Ändern des Paneels optimiert. Das Bedienfeld passt seine Größe erst dann visuell an, wenn sich das letzte gebundene Objekt an der richtigen Stelle befindet.
Im aktuellen Artikel werde ich die Optimierung der Handhabung einer Batch-Platzierung von Objekten in ihrem Container fortsetzen.

Neben der Arbeit an WinForms-Objekten werde ich neue Eigenschaften zu dem bereits für MetaTrader 5 Build 3260 angekündigten Symbol-Bibliotheksobjekt hinzufügen:

MQL5: Neue Eigenschaften in der Enumeration ENUM_SYMBOL_INFO_DOUBLE:

  • SYMBOL_SWAP_SUNDAY
  • SYMBOL_SWAP_MONDAY
  • SYMBOL_SWAP_TUESDAY
  • SYMBOL_SWAP_WEDNESDAY
  • SYMBOL_SWAP_THURSDAY
  • SYMBOL_SWAP_FRIDAY
  • SYMBOL_SWAP_SATURDAY

Verwenden Sie die Werte, um die Berechnungssätze für den Swap bestimmter Wochentage zu erhalten. 1 — einfacher Swap, 3 — dreifacher Swap, 0 — kein Swap.

Die neuen Eigenschaften werden als Klassenvariablen für die neu erstellten WinForms-Objekte hinzugefügt. Gleichzeitig sind alle Bibliotheksobjekte nach einem bestimmten Konzept aufgebaut, das im ersten Artikel über die Erstellung der Bibliothek beschrieben wurde: Jedes Objekt hat eine Reihe von Eigenschaften, die sich in drei Aufzählungen von Integer-, Real- und String-Objekteigenschaften befinden. Im aktuellen Artikel werde ich dafür sorgen, dass alle zuvor hinzugefügten neuen WinForms-Objekteigenschaften zu Konstanten dieser Enumeration werden. In diesem Fall befinden sich die neuen Eigenschaften in den allgemeinen Eigenschaftslisten der einzelnen grafischen Objekte. Später wird es möglich sein, alle Eigenschaften von WinForms-Objekten zu nutzen, um sie in grafischen Elementen darzustellen, z. B. in einer grafischen Nutzeroberfläche eines Programms zur visuellen Erstellung einer grafischen Shell von EAs oder Indikatoren wie MS Visual Studio im Terminal.


Verbesserung des Bibliotheksklassen

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

   MSG_LIB_PROP_NOT_SUPPORTED_MT5_LESS_3245,          // Property not supported in MetaTrader 5 versions lower than 3245
   MSG_LIB_PROP_NOT_SUPPORTED_MT5_LESS_3260,          // Property not supported in MetaTrader 5 versions lower than 3260
   MSG_LIB_PROP_NOT_SUPPORTED_POSITION,               // Property not supported for position

...

   MSG_SYM_PROP_SUBSCRIPTION_DELAY,                   // Delay for quotes passed by symbol for instruments working on subscription basis
   MSG_SYM_PROP_SWAP_SUNDAY,                          // Swap accrual ratio. Sunday
   MSG_SYM_PROP_SWAP_MONDAY,                          // Swap accrual ratio. Monday
   MSG_SYM_PROP_SWAP_TUESDAY,                         // Swap accrual ratio. Tuesday
   MSG_SYM_PROP_SWAP_WEDNESDAY,                       // Swap accrual ratio. Wednesday
   MSG_SYM_PROP_SWAP_THURSDAY,                        // Swap accrual ratio. Thursday
   MSG_SYM_PROP_SWAP_FRIDAY,                          // Swap accrual ratio. Friday
   MSG_SYM_PROP_SWAP_SATURDAY,                        // Swap accrual ratio. Saturday
   MSG_SYM_PROP_SWAP_1,                               // Single swap accrual
   MSG_SYM_PROP_SWAP_3,                               // Triple swap accrual
   MSG_SYM_PROP_SWAP_0,                               // No swap accrual
   //---
   MSG_SYM_PROP_BIDHIGH,                              // Maximal Bid of the day

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

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

...

   {"Размер задержки у котировок, передаваемых по символу, для инструментов, работающих по подписке","Delay size for quotes transmitted per symbol for instruments working by subscription"},
   {"Коэффициент начисления свопов. Воскресение","Swap rate. Sunday"},
   {"Коэффициент начисления свопов. Понедельник","Swap rate. Monday"},
   {"Коэффициент начисления свопов. Вторник","Swap rate. Tuesday"},
   {"Коэффициент начисления свопов. Среда","Swap rate. Wednesday"},
   {"Коэффициент начисления свопов. Четверг","Swap rate. Thursday"},
   {"Коэффициент начисления свопов. Пятница","Swap rate. Friday"},
   {"Коэффициент начисления свопов. Суббота","Swap rate. Saturday"},
   {"Одиночное начисление свопов","Single swap accrual"},
   {"Тройное начисление свопов","Triple swap accrual"},
   {"Начисление свопов отсутствует","No accrual"},
   
   {"Максимальный Bid за день","Maximal Bid of the day"},


In \MQL5\Include\DoEasy\Defines.mqh, und zwar in der Enumeration der realen Eigenschaften des Symbolobjekts, fügen wir neue Eigenschaften hinzu und erhöhen die Anzahl der realen Symboleigenschaften von 68 auf 75:

   SYMBOL_PROP_PRICE_SENSITIVITY,                           // Option/warrant sensitivity.  Shows by how many points the price of the option's underlying asset should change so that the price of the option changes by one point
   SYMBOL_PROP_SWAP_SUNDAY,                                 // Swap accrual ratio (Sunday)
   SYMBOL_PROP_SWAP_MONDAY,                                 // Swap accrual ratio (Monday)
   SYMBOL_PROP_SWAP_TUESDAY,                                // Swap accrual ratio (Tuesday)
   SYMBOL_PROP_SWAP_WEDNESDAY,                              // Swap accrual ratio (Wednesday)
   SYMBOL_PROP_SWAP_THURSDAY,                               // Swap accrual ratio (Thursday)
   SYMBOL_PROP_SWAP_FRIDAY,                                 // Swap accrual ratio (Friday)
   SYMBOL_PROP_SWAP_SATURDAY,                               // Swap accrual ratio (Saturday)
  };
#define SYMBOL_PROP_DOUBLE_TOTAL     (75)                   // Total number of real properties
#define SYMBOL_PROP_DOUBLE_SKIP      (0)                    // Number of real symbol properties not used in sorting
//+------------------------------------------------------------------+

Um Symbolobjekte nach neuen Eigenschaften sortieren und auswählen zu können, fügen wir neue Konstanten, die den ergänzten Eigenschaften entsprechen, zur Enumeration der möglichen Sortierkriterien für Symbolobjekte hinzu:

   SORT_BY_SYMBOL_PRICE_SENSITIVITY,                        // Sort by option/warrant sensitivity
   SORT_BY_SYMBOL_SWAP_SUNDAY,                              // Sort by swap accrual ratio (Sunday)
   SORT_BY_SYMBOL_SWAP_MONDAY,                              // Sort by swap accrual ratio (Monday)
   SORT_BY_SYMBOL_SWAP_TUESDAY,                             // Sort by swap accrual ratio (Tuesday)
   SORT_BY_SYMBOL_SWAP_WEDNESDAY,                           // Sort by swap accrual ratio (Wednesday)
   SORT_BY_SYMBOL_SWAP_THURSDAY,                            // Sort by swap accrual ratio (Thursday)
   SORT_BY_SYMBOL_SWAP_FRIDAY,                              // Sort by swap accrual ratio (Friday)
   SORT_BY_SYMBOL_SWAP_SATURDAY,                            // Sort by swap accrual ratio (Saturday)
//--- Sort by string properties
   SORT_BY_SYMBOL_NAME = FIRST_SYM_STR_PROP,                // Sort by a symbol name


Fügen wir in der Enumeration der ganzzahligen Eigenschaften von Canvas-basierten grafischen Elementen die Eigenschaften hinzu, die den zuvor hinzugefügten Variablen von WinForms-Objekten entsprechen, die diese Eigenschaften speichern, und erhöhen die Anzahl der ganzzahligen Objekteigenschaften von 25 auf 38:

   CANV_ELEMENT_PROP_ENABLED,                         // Element availability flag
   CANV_ELEMENT_PROP_FORE_COLOR,                      // Default text color for all control objects
   CANV_ELEMENT_PROP_BOLD_TYPE,                       // Font width type
   CANV_ELEMENT_PROP_BORDER_STYLE,                    // Control frame style
   CANV_ELEMENT_PROP_AUTOSIZE,                        // Flag of the element auto resizing depending on the content
   CANV_ELEMENT_PROP_DOCK_MODE,                       // Mode of binding control borders to the container
   CANV_ELEMENT_PROP_MARGIN_TOP,                      // Top margin between the fields of this and another control
   CANV_ELEMENT_PROP_MARGIN_BOTTOM,                   // Bottom margin between the fields of this and another control
   CANV_ELEMENT_PROP_MARGIN_LEFT,                     // Left margin between the fields of this and another control
   CANV_ELEMENT_PROP_MARGIN_RIGHT,                    // Right margin between the fields of this and another control
   CANV_ELEMENT_PROP_PADDING_TOP,                     // Top margin inside the control
   CANV_ELEMENT_PROP_PADDING_BOTTOM,                  // Bottom margin inside the control
   CANV_ELEMENT_PROP_PADDING_LEFT,                    // Left margin inside the control
   CANV_ELEMENT_PROP_PADDING_RIGHT,                   // Right margin inside the control
  };
#define CANV_ELEMENT_PROP_INTEGER_TOTAL (38)          // Total number of integer properties
#define CANV_ELEMENT_PROP_INTEGER_SKIP  (0)           // Number of integer properties not used in sorting

In der Enumeration der möglichen Sortierkriterien für WinForms-Objekte, fügen wir die Sortierung nach neu hinzugefügten Integer-Eigenschaften hinzu:

   SORT_BY_CANV_ELEMENT_ENABLED,                      // Sort by the element availability flag
   SORT_BY_CANV_ELEMENT_FORE_COLOR,                   // Sort by default text color for all control objects
   SORT_BY_CANV_ELEMENT_BOLD_TYPE,                    // Sort by font width type
   SORT_BY_CANV_ELEMENT_BORDER_STYLE,                 // Sort by control frame style
   SORT_BY_CANV_ELEMENT_AUTOSIZE,                     // Sort by the flag of the element auto resizing depending on the content
   SORT_BY_CANV_ELEMENT_DOCK_MODE,                    // Sort by mode of binding control borders to the container
   SORT_BY_CANV_ELEMENT_MARGIN_TOP,                   // Sort by top margin between the fields of this and another control
   SORT_BY_CANV_ELEMENT_MARGIN_BOTTOM,                // Sort by bottom margin between the fields of this and another control
   SORT_BY_CANV_ELEMENT_MARGIN_LEFT,                  // Sort by left margin between the fields of this and another control
   SORT_BY_CANV_ELEMENT_MARGIN_RIGHT,                 // Sort by right margin between the fields of this and another control
   SORT_BY_CANV_ELEMENT_PADDING_TOP,                  // Sort by top margin inside the control
   SORT_BY_CANV_ELEMENT_PADDING_BOTTOM,               // Sort by bottom margin inside the control
   SORT_BY_CANV_ELEMENT_PADDING_LEFT,                 // Sort by left margin inside the control
   SORT_BY_CANV_ELEMENT_PADDING_RIGHT,                // Sort by right margin inside the control
//--- Sort by real properties

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

Jetzt können WinForms-Objekte ausgewählt und nach grafischen Objekteigenschaften sortiert werden, die nur für WinForms-Objekte gelten.


Da dem Symbolobjekt neue Eigenschaften hinzugefügt wurden, müssen wir deren Handhabung in der Symbolobjektklasse implementieren.

Nehmen wir die erforderlichen Verbesserungen in der Klassendatei \MQL5\Include\DoEasy\Objects\Symbols\Symbol.mqh vor.

Im geschützten Teil der Klasse, d. h. im Block der Methoden zum Empfang und zur Rückgabe der realen Eigenschaften eines ausgewählten Symbols aus seinen Parametern, deklarieren wir die Methode, die das Swap-Rate für einen bestimmten Wochentag zurückgibt:

   double            SymbolMarginHedged(void)            const;
   double            SymbolSwapRatio(ENUM_DAY_OF_WEEK day)const;
   bool              SymbolMarginLong(void);


Im öffentlichen Teil der Klasse deklarieren wir die Methode, die die Beschreibung der Swap Accrual Ratio für einen bestimmten Wochentag zurückgibt:

   string            GetSectorDescription(void)                   const;
   string            GetIndustryDescription(void)                 const;
   string            GetSwapRatioDescription(const ENUM_DAY_OF_WEEK day)const;


In den Methodenblock für den vereinfachten Zugriff auf die Eigenschaften des Realsymbols schreiben wir die Methoden, die die Swap Accrual Ratio für jeden Tag der Woche zurückgeben und deklarieren die Methode, die die Swap Accrual Ratio für den angegebenen Tag der Woche zurückgibt:

   double            PriceSensitivity(void)                       const { return this.GetProperty(SYMBOL_PROP_PRICE_SENSITIVITY);                           }
   double            SwapRatioSunday(void)                        const { return this.GetProperty(SYMBOL_PROP_SWAP_SUNDAY);                                 }
   double            SwapRatioMonday(void)                        const { return this.GetProperty(SYMBOL_PROP_SWAP_MONDAY);                                 }
   double            SwapRatioTuesday(void)                       const { return this.GetProperty(SYMBOL_PROP_SWAP_TUESDAY);                                }
   double            SwapRatioWednesday(void)                     const { return this.GetProperty(SYMBOL_PROP_SWAP_WEDNESDAY);                              }
   double            SwapRatioThursday(void)                      const { return this.GetProperty(SYMBOL_PROP_SWAP_THURSDAY);                               }
   double            SwapRatioFriday(void)                        const { return this.GetProperty(SYMBOL_PROP_SWAP_FRIDAY);                                 }
   double            SwapRatioSaturday(void)                      const { return this.GetProperty(SYMBOL_PROP_SWAP_SATURDAY);                               }
   double            SwapRatioDay(const ENUM_DAY_OF_WEEK day)     const;
   double            NormalizedPrice(const double price)          const;


In den Methodenblock für den Empfang und die Einstellung der Parameter von verfolgten Eigenschaftsänderungen fügen wir die Methoden für die Behandlung neuer Symbolobjekteigenschaften hinzu:

   //--- Option/warrant sensitivity
   //--- setting the controlled maximum Bid price (1) increase, (2) decrease value and (3) option/warrant sensitivity control level in points
   //--- getting (4) option/warrant sensitivity change value in points,
   //--- getting the flag of the option/warrant sensitivity change exceeding the (5) increase, (6) decrease value
   void              SetControlPriceSensitivityInc(const double value)        { this.SetControlledValueINC(SYMBOL_PROP_PRICE_SENSITIVITY,::fabs(value));             }
   void              SetControlPriceSensitivityDec(const double value)        { this.SetControlledValueDEC(SYMBOL_PROP_PRICE_SENSITIVITY,::fabs(value));             }
   void              SetControlPriceSensitivityLevel(const double value)      { this.SetControlledValueLEVEL(SYMBOL_PROP_PRICE_SENSITIVITY,::fabs(value));           }
   double            GetValueChangedPriceSensitivity(void)              const { return this.GetPropDoubleChangedValue(SYMBOL_PROP_PRICE_SENSITIVITY);                }
   bool              IsIncreasedPriceSensitivity(void)                  const { return (bool)this.GetPropDoubleFlagINC(SYMBOL_PROP_PRICE_SENSITIVITY);               }
   bool              IsDecreasedPriceSensitivity(void)                  const { return (bool)this.GetPropDoubleFlagDEC(SYMBOL_PROP_PRICE_SENSITIVITY);               }
//--- Swap accrual ratio
   //--- setting the controlled Sunday swap accrual ratio (1) increase, (2) decrease and (3) control levels
   //--- get (4) the swap accrual ratio change (Sunday),
   //--- get the swap accrual ratio change for Sunday exceeding the (5) increase and (6) decrease values
   void              SetControlSwapSundayInc(const double value)              { this.SetControlledValueINC(SYMBOL_PROP_SWAP_SUNDAY,::fabs(value));                   }
   void              SetControlSwapSundayDec(const double value)              { this.SetControlledValueDEC(SYMBOL_PROP_SWAP_SUNDAY,::fabs(value));                   }
   void              SetControlSwapSundayLevel(const double value)            { this.SetControlledValueLEVEL(SYMBOL_PROP_SWAP_SUNDAY,::fabs(value));                 }
   double            GetValueChangedSwapSunday(void)                    const { return this.GetPropDoubleChangedValue(SYMBOL_PROP_SWAP_SUNDAY);                      }
   bool              IsIncreasedSwapSunday(void)                        const { return (bool)this.GetPropDoubleFlagINC(SYMBOL_PROP_SWAP_SUNDAY);                     }
   bool              IsDecreasedSwapSunday(void)                        const { return (bool)this.GetPropDoubleFlagDEC(SYMBOL_PROP_SWAP_SUNDAY);                     }
   //--- setting the controlled Monday swap accrual ratio (1) increase, (2) decrease and (3) control levels
   //--- get (4) the swap accrual ratio change (Monday),
   //--- get the swap accrual ratio change for Monday exceeding the (5) increase and (6) decrease values
   void              SetControlSwapMondayInc(const double value)              { this.SetControlledValueINC(SYMBOL_PROP_SWAP_MONDAY,::fabs(value));                   }
   void              SetControlSwapMondayDec(const double value)              { this.SetControlledValueDEC(SYMBOL_PROP_SWAP_MONDAY,::fabs(value));                   }
   void              SetControlSwapMondayLevel(const double value)            { this.SetControlledValueLEVEL(SYMBOL_PROP_SWAP_MONDAY,::fabs(value));                 }
   double            GetValueChangedSwapMonday(void)                    const { return this.GetPropDoubleChangedValue(SYMBOL_PROP_SWAP_MONDAY);                      }
   bool              IsIncreasedSwapMonday(void)                        const { return (bool)this.GetPropDoubleFlagINC(SYMBOL_PROP_SWAP_MONDAY);                     }
   bool              IsDecreasedSwapMonday(void)                        const { return (bool)this.GetPropDoubleFlagDEC(SYMBOL_PROP_SWAP_MONDAY);                     }
   //--- setting the controlled Tuesday swap accrual ratio (1) increase, (2) decrease and (3) control levels
   //--- get (4) the swap accrual ratio change (Tuesday),
   //--- get the swap accrual ratio change for Tuesday exceeding the (5) increase and (6) decrease values
   void              SetControlSwapTuesdayInc(const double value)             { this.SetControlledValueINC(SYMBOL_PROP_SWAP_TUESDAY,::fabs(value));                  }
   void              SetControlSwapTuesdayDec(const double value)             { this.SetControlledValueDEC(SYMBOL_PROP_SWAP_TUESDAY,::fabs(value));                  }
   void              SetControlSwapTuesdayLevel(const double value)           { this.SetControlledValueLEVEL(SYMBOL_PROP_SWAP_TUESDAY,::fabs(value));                }
   double            GetValueChangedSwapTuesday(void)                   const { return this.GetPropDoubleChangedValue(SYMBOL_PROP_SWAP_TUESDAY);                     }
   bool              IsIncreasedSwapTuesday(void)                       const { return (bool)this.GetPropDoubleFlagINC(SYMBOL_PROP_SWAP_TUESDAY);                    }
   bool              IsDecreasedSwapTuesday(void)                       const { return (bool)this.GetPropDoubleFlagDEC(SYMBOL_PROP_SWAP_TUESDAY);                    }
   //--- setting the controlled Wednesday swap accrual ratio (1) increase, (2) decrease and (3) control levels
   //--- get (4) the swap accrual ratio change (Wednesday),
   //--- get the swap accrual ratio change for Wednesday exceeding the (5) increase and (6) decrease values
   void              SetControlSwapWednesdayInc(const double value)           { this.SetControlledValueINC(SYMBOL_PROP_SWAP_WEDNESDAY,::fabs(value));                }
   void              SetControlSwapWednesdayDec(const double value)           { this.SetControlledValueDEC(SYMBOL_PROP_SWAP_WEDNESDAY,::fabs(value));                }
   void              SetControlSwapWednesdayLevel(const double value)         { this.SetControlledValueLEVEL(SYMBOL_PROP_SWAP_WEDNESDAY,::fabs(value));              }
   double            GetValueChangedSwapWednesday(void)                 const { return this.GetPropDoubleChangedValue(SYMBOL_PROP_SWAP_WEDNESDAY);                   }
   bool              IsIncreasedSwapWednesday(void)                     const { return (bool)this.GetPropDoubleFlagINC(SYMBOL_PROP_SWAP_WEDNESDAY);                  }
   bool              IsDecreasedSwapWednesday(void)                     const { return (bool)this.GetPropDoubleFlagDEC(SYMBOL_PROP_SWAP_WEDNESDAY);                  }
   //--- setting the controlled Thursday swap accrual ratio (1) increase, (2) decrease and (3) control levels
   //--- get (4) the swap accrual ratio change (Thursday),
   //--- get the swap accrual ratio change for Thursday exceeding the (5) increase and (6) decrease values
   void              SetControlSwapThursdayInc(const double value)            { this.SetControlledValueINC(SYMBOL_PROP_SWAP_THURSDAY,::fabs(value));                 }
   void              SetControlSwapThursdayDec(const double value)            { this.SetControlledValueDEC(SYMBOL_PROP_SWAP_THURSDAY,::fabs(value));                 }
   void              SetControlSwapThursdayLevel(const double value)          { this.SetControlledValueLEVEL(SYMBOL_PROP_SWAP_THURSDAY,::fabs(value));               }
   double            GetValueChangedSwapThursday(void)                  const { return this.GetPropDoubleChangedValue(SYMBOL_PROP_SWAP_THURSDAY);                    }
   bool              IsIncreasedSwapThursday(void)                      const { return (bool)this.GetPropDoubleFlagINC(SYMBOL_PROP_SWAP_THURSDAY);                   }
   bool              IsDecreasedSwapThursday(void)                      const { return (bool)this.GetPropDoubleFlagDEC(SYMBOL_PROP_SWAP_THURSDAY);                   }
   //--- setting the controlled Friday swap accrual ratio (1) increase, (2) decrease and (3) control levels
   //--- get (4) the swap accrual ratio change (Friday),
   //--- get the swap accrual ratio change for Friday exceeding the (5) increase and (6) decrease values
   void              SetControlSwapFridayInc(const double value)              { this.SetControlledValueINC(SYMBOL_PROP_SWAP_FRIDAY,::fabs(value));                   }
   void              SetControlSwapFridayDec(const double value)              { this.SetControlledValueDEC(SYMBOL_PROP_SWAP_FRIDAY,::fabs(value));                   }
   void              SetControlSwapFridayLevel(const double value)            { this.SetControlledValueLEVEL(SYMBOL_PROP_SWAP_FRIDAY,::fabs(value));                 }
   double            GetValueChangedSwapFriday(void)                    const { return this.GetPropDoubleChangedValue(SYMBOL_PROP_SWAP_FRIDAY);                      }
   bool              IsIncreasedSwapFriday(void)                        const { return (bool)this.GetPropDoubleFlagINC(SYMBOL_PROP_SWAP_FRIDAY);                     }
   bool              IsDecreasedSwapFriday(void)                        const { return (bool)this.GetPropDoubleFlagDEC(SYMBOL_PROP_SWAP_FRIDAY);                     }
   //--- setting the controlled Saturday swap accrual ratio (1) increase, (2) decrease and (3) control levels
   //--- get (4) the swap accrual ratio change (Saturday),
   //--- get the swap accrual ratio change for Saturday exceeding the (5) increase and (6) decrease values
   void              SetControlSwapSaturdayInc(const double value)            { this.SetControlledValueINC(SYMBOL_PROP_SWAP_SATURDAY,::fabs(value));                 }
   void              SetControlSwapSaturdayDec(const double value)            { this.SetControlledValueDEC(SYMBOL_PROP_SWAP_SATURDAY,::fabs(value));                 }
   void              SetControlSwapSaturdayLevel(const double value)          { this.SetControlledValueLEVEL(SYMBOL_PROP_SWAP_SATURDAY,::fabs(value));               }
   double            GetValueChangedSwapSaturday(void)                  const { return this.GetPropDoubleChangedValue(SYMBOL_PROP_SWAP_SATURDAY);                    }
   bool              IsIncreasedSwapSaturday(void)                      const { return (bool)this.GetPropDoubleFlagINC(SYMBOL_PROP_SWAP_SATURDAY);                   }
   bool              IsDecreasedSwapSaturday(void)                      const { return (bool)this.GetPropDoubleFlagDEC(SYMBOL_PROP_SWAP_SATURDAY);                   }

//--- Return a trading object
   CTradeObj        *GetTradeObj(void)                                           { return &this.m_trade; }

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

Diese Methoden ermöglichen es, den verfolgten Wert, um den sich der kontrollierte Parameter ändern soll, direkt aus ihren Programmen heraus einzustellen. Wenn eine solche Änderung registriert wird, sollten wir ein Signal über das Ereignis im Programm erhalten. Ich habe sie bereits bei der Entwicklung der Interaktivität von Bibliotheksobjekten besprochen.

Im Konstruktor der geschützten parametrischen Klasse fügen wir neue Eigenschaften eines Symbolobjekts hinzu:

   this.m_double_prop[this.IndexProp(SYMBOL_PROP_MARGIN_SELL_STOPLIMIT_MAINTENANCE)]= this.m_margin_rate.SellStopLimit.Maintenance;
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_SWAP_SUNDAY)]                      = this.SymbolSwapRatio(SUNDAY);
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_SWAP_MONDAY)]                      = this.SymbolSwapRatio(MONDAY);
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_SWAP_TUESDAY)]                     = this.SymbolSwapRatio(TUESDAY);
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_SWAP_WEDNESDAY)]                   = this.SymbolSwapRatio(WEDNESDAY);
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_SWAP_THURSDAY)]                    = this.SymbolSwapRatio(THURSDAY);
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_SWAP_FRIDAY)]                      = this.SymbolSwapRatio(FRIDAY);
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_SWAP_SATURDAY)]                    = this.SymbolSwapRatio(SATURDAY);
//--- Save string properties
   this.m_string_prop[this.IndexProp(SYMBOL_PROP_NAME)]                             = this.m_name;

Die mit der Methode SymbolSwapRatio() erhaltenen Werte (siehe unten) werden hier in das Array der Objekteigenschaften eingetragen.

Lassen Sie uns Implementierungen der deklarierten neuen Methoden außerhalb des Klassenkörpers schreiben.

Die geschützte Methode, die das Swap-Kumulierungsverhältnis für einen bestimmten Wochentag zurückgibt:

//+------------------------------------------------------------------+
//|Return the swap accrual ratio for a specified day of the week     |
//+------------------------------------------------------------------+
double CSymbol::SymbolSwapRatio(ENUM_DAY_OF_WEEK day) const
  {
#ifdef __MQL4__ 
   return 0;
#else 
   switch(day)
     {
      case MONDAY    :  return ::SymbolInfoDouble(this.m_name,SYMBOL_SWAP_MONDAY);
      case TUESDAY   :  return ::SymbolInfoDouble(this.m_name,SYMBOL_SWAP_TUESDAY);
      case WEDNESDAY :  return ::SymbolInfoDouble(this.m_name,SYMBOL_SWAP_WEDNESDAY);
      case THURSDAY  :  return ::SymbolInfoDouble(this.m_name,SYMBOL_SWAP_THURSDAY);
      case FRIDAY    :  return ::SymbolInfoDouble(this.m_name,SYMBOL_SWAP_FRIDAY);
      case SATURDAY  :  return ::SymbolInfoDouble(this.m_name,SYMBOL_SWAP_SATURDAY);
      //---SUNDAY
      default        :  return (int)::SymbolInfoDouble(this.m_name,SYMBOL_SWAP_SUNDAY);
     }
#endif 
  }
//+------------------------------------------------------------------+

MQL4 hat kein solches Symbol - es wird Null zurückgegeben. Im Falle von MQL5 wird die entsprechende Symboleigenschaft zurückgegeben, abhängig vom Wochentag, der an die Methode übergeben wird. Die Methode wird verwendet, um eine Symboleigenschaft auf die Enumeration ihrer realen Eigenschaften im Klassenkonstruktor zu setzen.

Die öffentliche Methode, die die Swap-Abgrenzungsquote für einen bestimmten Wochentag zurückgibt:

//+------------------------------------------------------------------+
//|Return the swap accrual ratio for a specified day of the week     |
//+------------------------------------------------------------------+
double CSymbol::SwapRatioDay(const ENUM_DAY_OF_WEEK day) const
  {
   switch(day)
     {
      case MONDAY    :  return this.SwapRatioMonday();
      case TUESDAY   :  return this.SwapRatioTuesday();
      case WEDNESDAY :  return this.SwapRatioWednesday();
      case THURSDAY  :  return this.SwapRatioThursday();
      case FRIDAY    :  return this.SwapRatioFriday();
      case SATURDAY  :  return this.SwapRatioSaturday();
      //---SUNDAY
      default        :  return this.SwapRatioSunday();
     }
  }
//+------------------------------------------------------------------+

Hier wird, je nach Wochentag, der an die Methode übergeben wurde, der Eigenschaftswert mithilfe öffentlicher Methoden abgerufen, die einen Eigenschaftswert für einen bestimmten Wochentag zurückgeben, der in der Enumeration der reellen Objekteigenschaften festgelegt ist.

In der Methode, die die Beschreibung einer realen Symboleigenschaft zurückgibt, fügen wir die Rückgabe der Beschreibung neuer Eigenschaften eines Symbolobjekts hinzu:

      property==SYMBOL_PROP_PRICE_SENSITIVITY   ?  CMessage::Text(MSG_SYM_PROP_PRICE_SENSITIVITY)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+::DoubleToString(this.GetProperty(property),dg)
         )  :
      property==SYMBOL_PROP_SWAP_SUNDAY      ?  CMessage::Text(MSG_SYM_PROP_SWAP_SUNDAY)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
         #ifdef __MQL5__
         (::TerminalInfoInteger(TERMINAL_BUILD)<3260 ?  ": ("+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED_MT5_LESS_3260)+")" : 
                                                        ": "+this.GetSwapRatioDescription(SUNDAY))
         #else ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED_MQL4) #endif 
         )  :
      property==SYMBOL_PROP_SWAP_MONDAY      ?  CMessage::Text(MSG_SYM_PROP_SWAP_MONDAY)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
         #ifdef __MQL5__
         (::TerminalInfoInteger(TERMINAL_BUILD)<3260 ?  ": ("+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED_MT5_LESS_3260)+")" : 
                                                        ": "+this.GetSwapRatioDescription(MONDAY))
         #else ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED_MQL4) #endif 
         )  :
      property==SYMBOL_PROP_SWAP_TUESDAY     ?  CMessage::Text(MSG_SYM_PROP_SWAP_TUESDAY)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
         #ifdef __MQL5__
         (::TerminalInfoInteger(TERMINAL_BUILD)<3260 ?  ": ("+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED_MT5_LESS_3260)+")" : 
                                                        ": "+this.GetSwapRatioDescription(TUESDAY))
         #else ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED_MQL4) #endif 
         )  :
      property==SYMBOL_PROP_SWAP_WEDNESDAY   ?  CMessage::Text(MSG_SYM_PROP_SWAP_WEDNESDAY)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
         #ifdef __MQL5__
         (::TerminalInfoInteger(TERMINAL_BUILD)<3260 ?  ": ("+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED_MT5_LESS_3260)+")" : 
                                                        ": "+this.GetSwapRatioDescription(WEDNESDAY))
         #else ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED_MQL4) #endif 
         )  :
      property==SYMBOL_PROP_SWAP_THURSDAY    ?  CMessage::Text(MSG_SYM_PROP_SWAP_THURSDAY)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
         #ifdef __MQL5__
         (::TerminalInfoInteger(TERMINAL_BUILD)<3260 ?  ": ("+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED_MT5_LESS_3260)+")" : 
                                                        ": "+this.GetSwapRatioDescription(THURSDAY))
         #else ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED_MQL4) #endif 
         )  :
      property==SYMBOL_PROP_SWAP_FRIDAY      ?  CMessage::Text(MSG_SYM_PROP_SWAP_FRIDAY)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
         #ifdef __MQL5__
         (::TerminalInfoInteger(TERMINAL_BUILD)<3260 ?  ": ("+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED_MT5_LESS_3260)+")" : 
                                                        ": "+this.GetSwapRatioDescription(FRIDAY))
         #else ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED_MQL4) #endif 
         )  :
      property==SYMBOL_PROP_SWAP_SATURDAY    ?  CMessage::Text(MSG_SYM_PROP_SWAP_SATURDAY)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
         #ifdef __MQL5__
         (::TerminalInfoInteger(TERMINAL_BUILD)<3260 ?  ": ("+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED_MT5_LESS_3260)+")" : 
                                                        ": "+this.GetSwapRatioDescription(SATURDAY))
         #else ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED_MQL4) #endif 
         )  :
      ""
     );
  }
//+------------------------------------------------------------------+

Hier wird der Terminal-Build überprüft, und wenn er unter 3260 liegt, wird mitgeteilt, dass diese Eigenschaft in dieser Version nicht unterstützt wird. Andernfalls erhalten wir die Eigenschaftsbeschreibung für MQL5, während wir im Falle von MQL4 die Meldung sehen, dass die Eigenschaft nicht unterstützt wird.

Die Methode liefert die Beschreibung einer Swap-Abgrenzungsquote für einen bestimmten Wochentag:

//+------------------------------------------------------------------+
//| Return the swap accrual ratio description                        |
//| for a specified day of the week                                  |
//+------------------------------------------------------------------+
string CSymbol::GetSwapRatioDescription(const ENUM_DAY_OF_WEEK day) const
  {
   double ratio=this.SwapRatioDay(day);
   return
     (
      ratio==0 ? CMessage::Text(MSG_SYM_PROP_SWAP_0)  :
      ratio==1 ? CMessage::Text(MSG_SYM_PROP_SWAP_1)  :
      ratio==3 ? CMessage::Text(MSG_SYM_PROP_SWAP_3)  : 
      ::DoubleToString(ratio,3)
     );
  }
//+------------------------------------------------------------------+

Hier erhalten wir zunächst den Eigenschaftswert für einen an die Methode übergebenen Wochentag, dann geben wir entweder eine Textbeschreibung eines Eigenschaftswertes zurück (wenn der Wert 0, 1 oder 3 ist) oder zeigen einen in einen Text umgewandelten Double-Wert mit drei Dezimalstellen an. (Die Anzahl der anzuzeigenden Dezimalstellen kann erst nach Durchführung einer ausreichenden Anzahl von Tests festgelegt werden).

Um Objekte in grafischen Objektdateien (und in Zukunft in allen Bibliotheksobjekten) zu speichern, sollten wir die Objekteigenschaftsstruktur verwenden. Alle Eigenschaften werden in der Struktur gespeichert, während die Struktur selbst in der Datei gespeichert wird. Auf die gleiche Weise wird sie aus der Datei gelesen, um die Objekteigenschaften wiederherzustellen.
Da wir neue Eigenschaften für grafische Objekte haben, müssen wir sie der Struktur hinzufügen.

In \MQL5\Include\DoEasy\Objects\Graph\GCnvElement.mqh fügen wir die neue Eigenschaften zu der Objektstruktur hinzu:

private:
   int               m_shift_coord_x;                          // Offset of the X coordinate relative to the base object
   int               m_shift_coord_y;                          // Offset of the Y coordinate relative to the base object
   struct SData
     {
      //--- Object integer properties
      int            id;                                       // Element ID
      int            type;                                     // Graphical element type
      int            number;                                   // Element index in the list
      long           chart_id;                                 // Chart ID
      int            subwindow;                                // Chart subwindow index
      int            coord_x;                                  // Element X coordinate on the chart
      int            coord_y;                                  // Element Y coordinate on the chart
      int            width;                                    // Element width
      int            height;                                   // Element height
      int            edge_right;                               // Element right border
      int            edge_bottom;                              // Element bottom border
      int            act_shift_left;                           // Active area offset from the left edge of the element
      int            act_shift_top;                            // Active area offset from the top edge of the element
      int            act_shift_right;                          // Active area offset from the right edge of the element
      int            act_shift_bottom;                         // Active area offset from the bottom edge of the element
      bool           movable;                                  // Element moveability flag
      bool           active;                                   // Element activity flag
      bool           interaction;                              // Flag of interaction with the outside environment
      int            coord_act_x;                              // X coordinate of the element active area
      int            coord_act_y;                              // Y coordinate of the element active area
      int            coord_act_right;                          // Right border of the element active area
      int            coord_act_bottom;                         // Bottom border of the element active area
      long           zorder;                                   // Priority of a graphical object for receiving the event of clicking on a chart
      bool           enabled;                                  // Element availability flag
      int            belong;                                   // Graphical element affiliation
      color          fore_color;                               // Default text color for all control objects
      int            bold_type;                                // Font width type
      int            border_style;                             // Control frame style
      bool           autosize;                                 // Flag of the element auto resizing depending on the content
      int            dock_mode;                                // Mode of binding control borders to the container
      int            margin_top;                               // Top margin between the fields of this and another control
      int            margin_bottom;                            // Bottom margin between the fields of this and another control
      int            margin_left;                              // Left margin between the fields of this and another control
      int            margin_right;                             // Right margin between the fields of this and another control
      int            padding_top;                              // Top margin inside the control
      int            padding_bottom;                           // Bottom margin inside the control
      int            padding_left;                             // Left margin inside the control
      int            padding_right;                            // Right margin inside the control
      uchar          opacity;                                  // Element opacity
      color          color_bg;                                 // Element background color
      //--- Object real properties

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


Fügen wir in der Methode, die die Objektstruktur erstellt, schreibende Objekteigenschaften zu den Strukturfeldern hinzu:

   this.m_struct_obj.coord_act_bottom=(int)this.GetProperty(CANV_ELEMENT_PROP_ACT_BOTTOM);      // Bottom border of the element active area
   this.m_struct_obj.belong=(int)this.GetProperty(CANV_ELEMENT_PROP_BELONG);                    // Graphical element affiliation
   this.m_struct_obj.zorder=this.GetProperty(CANV_ELEMENT_PROP_ZORDER);                         // Priority of a graphical object for receiving the on-chart mouse click event
   this.m_struct_obj.fore_color=(color)this.GetProperty(CANV_ELEMENT_PROP_FORE_COLOR);          // Default text color for all control objects
   this.m_struct_obj.bold_type=(int)this.GetProperty(CANV_ELEMENT_PROP_BOLD_TYPE);              // Font width type
   this.m_struct_obj.border_style=(int)this.GetProperty(CANV_ELEMENT_PROP_BORDER_STYLE);        // Control frame style
   this.m_struct_obj.autosize=this.GetProperty(CANV_ELEMENT_PROP_AUTOSIZE);                     // Flag of the element auto resizing depending on the content
   this.m_struct_obj.dock_mode=(int)this.GetProperty(CANV_ELEMENT_PROP_DOCK_MODE);              // Mode of binding control borders to the container
   this.m_struct_obj.margin_top=(int)this.GetProperty(CANV_ELEMENT_PROP_MARGIN_TOP);            // Top margin between the fields of this and another control
   this.m_struct_obj.margin_bottom=(int)this.GetProperty(CANV_ELEMENT_PROP_MARGIN_BOTTOM);      // Bottom margin between the fields of this and another control
   this.m_struct_obj.margin_left=(int)this.GetProperty(CANV_ELEMENT_PROP_MARGIN_LEFT);          // Left margin between the fields of this and another control
   this.m_struct_obj.margin_right=(int)this.GetProperty(CANV_ELEMENT_PROP_MARGIN_RIGHT);        // Right margin between the fields of this and another control
   this.m_struct_obj.padding_top=(int)this.GetProperty(CANV_ELEMENT_PROP_PADDING_TOP);          // Top margin inside the control
   this.m_struct_obj.padding_bottom=(int)this.GetProperty(CANV_ELEMENT_PROP_PADDING_BOTTOM);    // Bottom margin inside the control
   this.m_struct_obj.padding_left=(int)this.GetProperty(CANV_ELEMENT_PROP_PADDING_LEFT);        // Left margin inside the control
   this.m_struct_obj.padding_right=(int)this.GetProperty(CANV_ELEMENT_PROP_PADDING_RIGHT);      // Right margin inside the control
   this.m_struct_obj.color_bg=this.m_color_bg;                                                  // Element background color
   this.m_struct_obj.opacity=this.m_opacity;                                                    // Element opacity


In der Methode, die ein Objekt aus der Struktur erstellt, fügen wir das Lesen von Objekteigenschaftswerten aus den Strukturfeldern hinzu:

   this.SetProperty(CANV_ELEMENT_PROP_ACT_BOTTOM,this.m_struct_obj.coord_act_bottom);           // Bottom border of the element active area
   this.SetProperty(CANV_ELEMENT_PROP_BELONG,this.m_struct_obj.belong);                         // Graphical element affiliation
   this.SetProperty(CANV_ELEMENT_PROP_FORE_COLOR,this.m_struct_obj.fore_color);                 // Default text color for all control objects
   this.SetProperty(CANV_ELEMENT_PROP_BOLD_TYPE,this.m_struct_obj.bold_type);                   // Font width type
   this.SetProperty(CANV_ELEMENT_PROP_BORDER_STYLE,this.m_struct_obj.border_style);             // Control frame style
   this.SetProperty(CANV_ELEMENT_PROP_AUTOSIZE,this.m_struct_obj.autosize);                     // Flag of the element auto resizing depending on the content
   this.SetProperty(CANV_ELEMENT_PROP_DOCK_MODE,this.m_struct_obj.dock_mode);                   // Mode of binding control borders to the container
   this.SetProperty(CANV_ELEMENT_PROP_MARGIN_TOP,this.m_struct_obj.margin_top);                 // Top margin between the fields of this and another control
   this.SetProperty(CANV_ELEMENT_PROP_MARGIN_BOTTOM,this.m_struct_obj.margin_bottom);           // Bottom margin between the fields of this and another control
   this.SetProperty(CANV_ELEMENT_PROP_MARGIN_LEFT,this.m_struct_obj.margin_left);               // Left margin between the fields of this and another control
   this.SetProperty(CANV_ELEMENT_PROP_MARGIN_RIGHT,this.m_struct_obj.margin_right);             // Right margin between the fields of this and another control
   this.SetProperty(CANV_ELEMENT_PROP_PADDING_TOP,this.m_struct_obj.padding_top);               // Top margin inside the control
   this.SetProperty(CANV_ELEMENT_PROP_PADDING_BOTTOM,this.m_struct_obj.padding_bottom);         // Bottom margin inside the control
   this.SetProperty(CANV_ELEMENT_PROP_PADDING_LEFT,this.m_struct_obj.padding_left);             // Left margin inside the control
   this.SetProperty(CANV_ELEMENT_PROP_PADDING_RIGHT,this.m_struct_obj.padding_right);           // Right margin inside the control
   this.SetZorder(this.m_struct_obj.zorder,false);                                              // Priority of a graphical object for receiving the on-chart mouse click event
   this.m_color_bg=this.m_struct_obj.color_bg;                                                  // Element background color
   this.m_opacity=this.m_struct_obj.opacity;                                                    // Element opacity

Jetzt werden alle grafischen Objekte korrekt in der Datei gespeichert und aus ihr wiederhergestellt, wenn wir die Eigenschaften von Bibliotheksobjekten in Dateien speichern.


Lassen Sie uns eine kleine Verbesserung in der Basisklasse von WinForms-Objekten in \MQL5\Include\DoEasy\Objects\Graph\WForms\WinFormBase.mqh vornehmen.

Zunächst müssen wir in der Lage sein, diese Objekte auszuwählen und sie nach ihren Eigenschaften zu sortieren. Um dies zu erreichen, schließen wir die CSelect-Klassendatei ein:

//+------------------------------------------------------------------+
//|                                                  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 "..\Form.mqh"
#include "..\..\..\Services\Select.mqh"
//+------------------------------------------------------------------+

Jetzt können wir die CSelect-Klasse in allen abgeleiteten Klassen verwenden, um WinForms-Objekte nach ihren Eigenschaften auszuwählen und zu sortieren.

Fügen wir außerdem in der Schleife, die alle angehängten Objekte in der Methode zum Neuzeichnen des Objekts behandelt, eine Prüfung auf das Neuzeichnungs-Flag hinzu, damit die Objekte nur dann tatsächlich neu gezeichnet werden, wenn das Neuzeichnungs-Flag aktiviert ist. Ist dies nicht der Fall, müssen gebundene Objekte nicht neu gezeichnet werden (z. B. um visuelle Artefakte zu vermeiden, wenn Änderungen an den Eigenschaften des Bedienfeldobjekts vorgenommen werden). Wenn das Neuzeichnungs-Flag zurückgesetzt wird, sollten die an den Container gebundenen Objekte nicht tatsächlich neu gezeichnet werden, da sich ihre Eigenschaften in anderen Methoden ändern, während hier alle Objekte tatsächlich neu gezeichnet werden.

Fügen wir also die Prüfung auf das Flag und den Aufruf zum Neuzeichnen des Objekts nur dann hinzu, wenn das Flag gesetzt ist:

//+------------------------------------------------------------------+
//| Redraw the object                                                |
//+------------------------------------------------------------------+
void CWinFormBase::Redraw(bool redraw)
  {
//--- If the object type is less than the "Base WinForms object", exit
   if(this.TypeGraphElement()<GRAPH_ELEMENT_TYPE_WF_BASE)
      return;
//--- Get the "Shadow" object
   CShadowObj *shadow=this.GetShadowObj();
//--- If the object has a shadow and the "Shadow" object exists, redraw it
   if(this.IsShadow() && shadow!=NULL)
     {
      //--- remove the previously drawn shadow,
      shadow.Erase();
      //--- save the relative shadow coordinates,
      int x=shadow.CoordXRelative();
      int y=shadow.CoordYRelative();
      //--- redraw the shadow,
      if(redraw)
         shadow.Draw(0,0,shadow.Blur(),redraw);
      //--- restore relative shadow coordinates
      shadow.SetCoordXRelative(x);
      shadow.SetCoordYRelative(y);
     }
//--- If the redraw flag is set,
   if(redraw)
     {
      //--- completely redraw the object and save its new initial look
      this.Erase(this.m_array_colors_bg,this.Opacity(),this.m_gradient_v,this.m_gradient_c,redraw);
      this.Done();
     }
//--- otherwise, remove the object
   else
      this.Erase();
//--- Redraw all bound objects with the redraw flag
   for(int i=0;i<this.ElementsTotal();i++)
     {
      CWinFormBase *element=this.GetElement(i);
      if(element==NULL)
         continue;
      if(redraw)
         element.Redraw(redraw);
     }
//--- If the redraw flag is set and if this is the main object the rest are bound to,
//--- redraw the chart to display changes immediately
   if(redraw && this.GetMain()==NULL)
      ::ChartRedraw(this.ChartID());
  }
//+------------------------------------------------------------------+


Lassen Sie uns nun die Klasse des Objekts „Panel WinForms“ in \MQL5\Include\DoEasy\Objects\Graph\WForms\Containers\Panel.mqh verbessern.

Deklarieren wir im privaten Abschnitt der Klasse die Methode zur Berechnung der Bindungskoordinaten der Dock-Objekte:

//--- Set the underlay as a coordinate system zero
   void              SetUnderlayAsBase(void);
//--- Calculate Dock objects' binding coordinates
   void              CalculateCoords(CArrayObj *list);

protected:

Ich werde die Methode in diesem Artikel nicht implementieren, also fügen wir eine leere Methode außerhalb des Klassenkörpers hinzu:

//+------------------------------------------------------------------+
//| Calculate Dock objects' binding coordinates                      |
//+------------------------------------------------------------------+
void CPanel::CalculateCoords(CArrayObj *list)
  {
   
  }
//+------------------------------------------------------------------+

Ich werde mich nach dem Erstellen der WinForms-Objektklasse "Text Label" (Label) mit der Implementierung befassen, damit wir die Sortierung von Objekten visuell sehen können, wenn sie gemäß dem Wert der Dock-Eigenschaft gebunden sind, und gleichzeitig die richtige behandeln Bewegung von Objekten, die an ihre Behälter gebunden sind, die wiederum an den Hauptbehältern befestigt sind — Paneele.

Entfernen wir aus dem geschützten Abschnitt der Klasse die Deklaration der Methoden, die den maximalen Wert der Grenzen des Dock-Objekts zurückgeben, die den Container in Breite und Höhe überschreiten:

protected:
//--- Return the maximum value of Dock object borders going beyond the container by width and (2) height
   int               GetExcessMaxX(void);
   int               GetExcessMaxY(void);
//--- Set (1) X, (2) Y coordinate, (3) width, (4) height and (5) all underlay parameters

Entfernen wir auch die Implementierung der Methoden, die außerhalb des Klassenkörpers liegen.

Jetzt sind diese Methoden nicht mehr erforderlich, da wir die Bibliotheksklasse CSelect verwenden können, um die erforderlichen Werte zu finden.

Deklarieren wir im öffentlichen Abschnitt der Klasse die Methode, die die Liste der gebundenen Objekte mit WinForms Typ basic und höher zurückgibt:

public:
//--- Return the underlay
   CGCnvElement     *GetUnderlay(void)                               { return this.m_underlay;              }
//--- Return the list of bound objects with WinForms type basic and higher
   CArrayObj        *GetListWinFormsObj(void);


Das Paneel-Objekt sollte weiterhin ohne Rahmen erstellt werden. Wenn der Rahmen für das Paneel erforderlich ist, kann er nach der Erstellung des Objekts hinzugefügt werden. Setzen wir daher den Rahmentyp in den Klassenkonstruktoren als nicht vorhanden:

//--- Constructors
                     CPanel(const long chart_id,
                           const int subwindow,
                           const string name,
                           const int x,
                           const int y,
                           const int w,
                           const int h);
                     CPanel(const string name) : CWinFormBase(::ChartID(),0,name,0,0,0,0)
                       {
                        CGBaseObj::SetTypeElement(GRAPH_ELEMENT_TYPE_WF_PANEL);
                        CGCnvElement::SetProperty(CANV_ELEMENT_PROP_TYPE,GRAPH_ELEMENT_TYPE_WF_PANEL);
                        this.m_type=OBJECT_DE_TYPE_GWF_PANEL; 
                        this.SetForeColor(CLR_DEF_FORE_COLOR);
                        this.SetFontBoldType(FW_TYPE_NORMAL);
                        this.SetMarginAll(3);
                        this.SetPaddingAll(0);
                        this.SetDockMode(CANV_ELEMENT_DOCK_MODE_NONE,false);
                        this.SetBorderStyle(FRAME_STYLE_NONE);
                        this.SetAutoScroll(false);
                        this.SetAutoScrollMarginAll(0);
                        this.SetAutoSize(false,false);
                        this.SetAutoSizeMode(CANV_ELEMENT_AUTO_SIZE_MODE_GROW,false);
                        this.Initialize();
                        if(this.CreateUnderlayObj())
                           this.SetUnderlayAsBase();
                       }
//--- Destructor
                    ~CPanel();
  };
//+------------------------------------------------------------------+
//| Constructor indicating the chart and subwindow ID                |
//+------------------------------------------------------------------+
CPanel::CPanel(const long chart_id,
               const int subwindow,
               const string name,
               const int x,
               const int y,
               const int w,
               const int h) : CWinFormBase(chart_id,subwindow,name,x,y,w,h)
  {
   CGBaseObj::SetTypeElement(GRAPH_ELEMENT_TYPE_WF_PANEL);
   CGCnvElement::SetProperty(CANV_ELEMENT_PROP_TYPE,GRAPH_ELEMENT_TYPE_WF_PANEL);
   this.m_type=OBJECT_DE_TYPE_GWF_PANEL;
   this.SetForeColor(CLR_DEF_FORE_COLOR);
   this.SetFontBoldType(FW_TYPE_NORMAL);
   this.SetMarginAll(3);
   this.SetPaddingAll(0);
   this.SetDockMode(CANV_ELEMENT_DOCK_MODE_NONE,false);
   this.SetBorderStyle(FRAME_STYLE_NONE);
   this.SetAutoScroll(false);
   this.SetAutoScrollMarginAll(0);
   this.SetAutoSize(false,false);
   this.SetAutoSizeMode(CANV_ELEMENT_AUTO_SIZE_MODE_GROW,false);
   this.Initialize();
   if(this.CreateUnderlayObj())
      this.SetUnderlayAsBase();
   this.SetCoordXInit(x);
   this.SetCoordYInit(y);
   this.SetWidthInit(w);
   this.SetHeightInit(h);
  }
//+------------------------------------------------------------------+

Im letzten Konstruktor entfernen Sie die Rahmenerstellung und legen einen Rahmentyp fest:

   this.Initialize();
   this.DrawFormFrame(this.FrameWidthTop(),this.FrameWidthBottom(),this.FrameWidthLeft(),this.FrameWidthRight(),this.ColorFrame(),this.Opacity(),this.BorderStyle());
   if(this.CreateUnderlayObj())
      this.SetUnderlayAsBase();


Zuvor enthielt die Methode, die gebundene Objekte in der Reihenfolge ihrer Dock-Bindung anordnet, keine Behandlung der Objektposition für den Fall, dass das Flag für automatische Größenänderung für den Container (Paneel) nicht gesetzt war. In diesem Artikel werde ich die Handles für den Bindungsmodus einfach von den Handles für das feste Bedienfeld kopieren. Ich werde jedoch vorläufig eine Prüfung für die Unterlage hinzufügen, während die Liste der Objekte jetzt mit einer neuen Methode empfangen wird, die nur die Liste der WinForms-Objekte zurückgibt. Wenn die automatische Größenanpassung des Paneels aktiviert ist, ändern wir zunächst die Größe des Paneels auf die ursprüngliche Größe und rufen dann die Methode auf, die die Größe des Paneels an den Inhalt anpasst. Danach werden in der Schleife die Bindungsmodi aller gebundenen Objekte behandelt und die Größe des Bedienfelds wird an die darin geänderten Objekte angepasst:

//+------------------------------------------------------------------+
//| Place bound objects in the order of their Dock binding           |
//+------------------------------------------------------------------+
bool CPanel::ArrangeObjects(const bool redraw)
  {
//--- If the panel has no underlay, return 'false'
   if(this.m_underlay==NULL)
      return false;
//--- Get the list of bound objects with WinForms type basic and higher
   CArrayObj *list=this.GetListWinFormsObj();
   CWinFormBase *prev=NULL, *obj=NULL, *elm=NULL;
   //--- If auto resizing mode is enabled
   if(this.AutoSize())
     {
      //--- Return the original panel size and then adjust it to the objects located inside it
      this.Resize(this.GetWidthInit(),this.GetHeightInit(),false);
      this.AutoSizeProcess(false);
      //--- In the loop by all bound objects,
      for(int i=0;i<list.Total();i++)
        {
         //--- Get the current and previous elements from the list
         obj=list.At(i);
         prev=list.At(i-1);
         //--- If there is no previous element, set the underlay as a previous element
         if(prev==NULL)
            this.SetUnderlayAsBase();
         //--- If the object has not been received or its type is less than the base WinForms object or the current element has no underlay, move on
         if(obj==NULL)
            continue;
         int x=0, y=0; // Object binding coordinates
         //--- Depending on the current object binding mode...
         //--- Top
         if(obj.DockMode()==CANV_ELEMENT_DOCK_MODE_TOP)
           {
            //--- If failed to change the object size (for the entire underlay width and by the initial object height), move on to the next one
            if(!obj.Resize(this.GetWidthUnderlay(),obj.GetHeightInit(),false))
               continue;
            //--- Get the pointer to the object at the top whose edges are used to bind the current one
            CGCnvElement *coord_base=this.GetTopObj();
            //--- Get the object binding coordinates
            x=this.GetCoordXUnderlay();
            y=(coord_base.TypeGraphElement()==GRAPH_ELEMENT_TYPE_WF_UNDERLAY ? coord_base.CoordY() : coord_base.BottomEdge()+1);
            //--- If failed to move the object to the obtained coordinates, move on to the next one
            if(!obj.Move(x,y,false))
               continue;
            //--- Set the current object as the top one whose edges will be used to bind the next one
            this.m_obj_top=obj;
           }
         //--- Bottom
         if(obj.DockMode()==CANV_ELEMENT_DOCK_MODE_BOTTOM)
           {
            //--- If failed to change the object size (for the entire underlay width and by the initial object height), move on to the next one
            if(!obj.Resize(this.GetWidthUnderlay(),obj.GetHeightInit(),false))
               continue;
            //--- Get the pointer to the object at the bottom whose edges are used to bind the current one
            CGCnvElement *coord_base=this.GetBottomObj();
            //--- Get the object binding coordinates
            x=this.GetCoordXUnderlay();
            y=(coord_base.TypeGraphElement()==GRAPH_ELEMENT_TYPE_WF_UNDERLAY ? coord_base.BottomEdge()-obj.Height() : coord_base.CoordY()-obj.Height()-1);
            //--- If failed to move the object to the obtained coordinates, move on to the next one
            if(!obj.Move(x,y,false))
               continue;
            //--- Set the current object as the bottom one whose edges will be used to bind the next one
            this.m_obj_bottom=obj;
           }
         //--- Left
         if(obj.DockMode()==CANV_ELEMENT_DOCK_MODE_LEFT)
           {
            //--- If failed to change the object size (for the initial object width and the entire underlay height), move on to the next one
            if(!obj.Resize(obj.GetWidthInit(),this.GetHeightUnderlay(),false))
               continue;
            //--- Get the pointer to the object at the left whose edges are used to bind the current one
            CGCnvElement *coord_base=this.GetLeftObj();
            //--- Get the object binding coordinates
            x=(coord_base.TypeGraphElement()==GRAPH_ELEMENT_TYPE_WF_UNDERLAY ? coord_base.CoordX() : coord_base.RightEdge()+1);
            y=this.GetCoordYUnderlay();
            //--- If failed to move the object to the obtained coordinates, move on to the next one
            if(!obj.Move(x,y,false))
               continue;
            //--- Set the current object as the left one whose edges will be used to bind the next one
            this.m_obj_left=obj;
           }
         //--- Right
         if(obj.DockMode()==CANV_ELEMENT_DOCK_MODE_RIGHT)
           {
            //--- If failed to change the object size (for the initial object width and the entire underlay height), move on to the next one
            if(!obj.Resize(obj.GetWidthInit(),this.GetHeightUnderlay(),false))
               continue;
            //--- Get the pointer to the object at the right whose edges are used to bind the current one
            CGCnvElement *coord_base=this.GetRightObj();
            //--- Get the object binding coordinates
            x=(coord_base.TypeGraphElement()==GRAPH_ELEMENT_TYPE_WF_UNDERLAY ? m_underlay.RightEdge()-obj.Width() : coord_base.CoordX()-obj.Width()-1);
            y=this.GetCoordYUnderlay();
            //--- If failed to move the object to the obtained coordinates, move on to the next one
            if(!obj.Move(x,y,false))
               continue;
            //--- Set the current object as the right one whose edges will be used to bind the next one
            this.m_obj_right=obj;
           }
         //--- Binding with filling
         if(obj.DockMode()==CANV_ELEMENT_DOCK_MODE_FILL)
           {
            //--- If failed to change the object size (for the entire underlay width and height), move on to the next one
            if(!obj.Resize(this.GetWidthUnderlay(),this.GetHeightUnderlay(),false))
               continue;
            //--- Set the underlay as a binding object
            this.SetUnderlayAsBase();
            //--- Get the object binding coordinates
            x=this.GetLeftObj().CoordX();
            y=this.GetTopObj().CoordY();
            //--- If failed to move the object to the obtained coordinates, move on to the next one
            if(!obj.Move(x,y,false))
               continue;
           }
         //--- No binding
         if(obj.DockMode()==CANV_ELEMENT_DOCK_MODE_NONE)
           {
            //--- Reset the object size
            obj.Resize(obj.GetWidthInit(),obj.GetHeightInit(),false);
            //--- Get the initial object location coordinates
            x=this.GetCoordXUnderlay()+obj.CoordXRelativeInit();
            y=this.GetCoordYUnderlay()+obj.CoordYRelativeInit();
            //--- If failed to move the object to the obtained coordinates, move on to the next one
            if(!obj.Move(x,y,false))
               continue;
           }
         //--- Calculate and set the relative object coordinates
         obj.SetCoordXRelative(x-this.m_underlay.CoordX());
         obj.SetCoordYRelative(y-this.m_underlay.CoordY());
        }
      this.Resize(this.GetWidthInit(),this.GetHeightInit(),false);
      this.AutoSizeProcess(false);
     }
   //--- If auto resizing mode disabled 
   else
     {
      //--- In the loop by all bound objects,
      for(int i=0;i<list.Total();i++)
        {
         //--- Get the current and previous elements from the list
         obj=list.At(i);
         prev=list.At(i-1);
         //--- If there is no previous element, set the underlay as a previous element
         if(prev==NULL)
            this.SetUnderlayAsBase();
         //--- If the object has not been received or its type is less than the base WinForms object or the current element has no underlay, move on
         if(obj==NULL)
            continue;
         int x=0, y=0; // Object binding coordinates
         //--- Depending on the current object binding mode...
         //--- Top
         if(obj.DockMode()==CANV_ELEMENT_DOCK_MODE_TOP)
           {
            //--- If failed to change the object size (for the entire underlay width and by the initial object height), move on to the next one
            if(!obj.Resize(this.GetWidthUnderlay(),obj.GetHeightInit(),false))
               continue;
            //--- Get the pointer to the object at the top whose edges are used to bind the current one
            CGCnvElement *coord_base=this.GetTopObj();
            //--- Get the object binding coordinates
            x=this.GetCoordXUnderlay();
            y=(coord_base.TypeGraphElement()==GRAPH_ELEMENT_TYPE_WF_UNDERLAY ? coord_base.CoordY() : coord_base.BottomEdge()+1);
            //--- If failed to move the object to the obtained coordinates, move on to the next one
            if(!obj.Move(x,y,false))
               continue;
            //--- Set the current object as the top one whose edges will be used to bind the next one
            this.m_obj_top=obj;
           }
         //--- Bottom
         if(obj.DockMode()==CANV_ELEMENT_DOCK_MODE_BOTTOM)
           {
            //--- If failed to change the object size (for the entire underlay width and by the initial object height), move on to the next one
            if(!obj.Resize(this.GetWidthUnderlay(),obj.GetHeightInit(),false))
               continue;
            //--- Get the pointer to the object at the bottom whose edges are used to bind the current one
            CGCnvElement *coord_base=this.GetBottomObj();
            //--- Get the object binding coordinates
            x=this.GetCoordXUnderlay();
            y=(coord_base.TypeGraphElement()==GRAPH_ELEMENT_TYPE_WF_UNDERLAY ? coord_base.BottomEdge()-obj.Height()-1 : coord_base.CoordY()-obj.Height()-1);
            //--- If failed to move the object to the obtained coordinates, move on to the next one
            if(!obj.Move(x,y,false))
               continue;
            //--- Set the current object as the bottom one whose edges will be used to bind the next one
            this.m_obj_bottom=obj;
           }
         //--- Left
         if(obj.DockMode()==CANV_ELEMENT_DOCK_MODE_LEFT)
           {
            //--- If failed to change the object size (for the initial object width and the entire underlay height), move on to the next one
            if(!obj.Resize(obj.GetWidthInit(),this.GetHeightUnderlay(),false))
               continue;
            //--- Get the pointer to the object at the left whose edges are used to bind the current one
            CGCnvElement *coord_base=this.GetLeftObj();
            //--- Get the object binding coordinates
            x=(coord_base.TypeGraphElement()==GRAPH_ELEMENT_TYPE_WF_UNDERLAY ? coord_base.CoordX() : coord_base.RightEdge()+1);
            y=this.GetCoordYUnderlay();
            //--- If failed to move the object to the obtained coordinates, move on to the next one
            if(!obj.Move(x,y,false))
               continue;
            //--- Set the current object as the left one whose edges will be used to bind the next one
            this.m_obj_left=obj;
           }
         //--- Right
         if(obj.DockMode()==CANV_ELEMENT_DOCK_MODE_RIGHT)
           {
            //--- If failed to change the object size (for the initial object width and the entire underlay height), move on to the next one
            if(!obj.Resize(obj.GetWidthInit(),this.GetHeightUnderlay(),false))
               continue;
            //--- Get the pointer to the object at the right whose edges are used to bind the current one
            CGCnvElement *coord_base=this.GetRightObj();
            //--- Get the object binding coordinates
            x=(coord_base.TypeGraphElement()==GRAPH_ELEMENT_TYPE_WF_UNDERLAY ? m_underlay.RightEdge()-obj.Width() : coord_base.CoordX()-obj.Width()-1);
            y=this.GetCoordYUnderlay();
            //--- If failed to move the object to the obtained coordinates, move on to the next one
            if(!obj.Move(x,y,false))
               continue;
            //--- Set the current object as the right one whose edges will be used to bind the next one
            this.m_obj_right=obj;
           }
         //--- Binding with filling
         if(obj.DockMode()==CANV_ELEMENT_DOCK_MODE_FILL)
           {
            //--- If failed to change the object size (for the entire underlay width and height), move on to the next one
            if(!obj.Resize(this.GetWidthUnderlay(),this.GetHeightUnderlay(),false))
               continue;
            //--- Set the underlay as a binding object
            this.SetUnderlayAsBase();
            //--- Get the object binding coordinates
            x=this.GetLeftObj().CoordX();
            y=this.GetTopObj().CoordY();
            //--- If failed to move the object to the obtained coordinates, move on to the next one
            if(!obj.Move(x,y,false))
               continue;
           }
         //--- No binding
         if(obj.DockMode()==CANV_ELEMENT_DOCK_MODE_NONE)
           {
            //--- Reset the object size
            obj.Resize(obj.GetWidthInit(),obj.GetHeightInit(),false);
            //--- Get the initial object location coordinates
            x=this.GetCoordXUnderlay()+obj.CoordXRelativeInit();
            y=this.GetCoordYUnderlay()+obj.CoordYRelativeInit();
            //--- If failed to move the object to the obtained coordinates, move on to the next one
            if(!obj.Move(x,y,false))
               continue;
           }
         //--- Calculate and set the relative object coordinates
         obj.SetCoordXRelative(x-this.m_underlay.CoordX());
         obj.SetCoordYRelative(y-this.m_underlay.CoordY());
        }
     }
//--- Redraw the object with the redraw flag and return 'true'
   this.Redraw(redraw); 
   return true;
  }
//+------------------------------------------------------------------+

Hier können wir alles in einer einzigen Schleife machen, d.h. ohne Aufteilung in zwei Blöcke mit zwei separaten identischen Schleifen, sowie zusätzlich das Paneel Auto Resize Flag überprüfen. Dadurch wird der Code auf die Hälfte reduziert. Aber ich habe beschlossen, es zu lassen, falls ich den Code-Block für die automatische Größenanpassung des Paneels weiter verfeinern muss. Erst wenn alles debuggt ist und klar ist, dass keine weiteren Änderungen und Verbesserungen erforderlich sind, werde ich den Code dieser Methode optimieren.

Die Methode zur Anpassung der Elementgröße an den Inhalt wurde ebenfalls überarbeitet, da wir die erforderlichen Daten nun mit Hilfe der Klasse CSelect erhalten können:

//+------------------------------------------------------------------+
//| Adjust the element size to fit its content                       |
//+------------------------------------------------------------------+
bool CPanel::AutoSizeProcess(const bool redraw)
  {
//--- Get the list of bound objects with WinForms type basic and higher
   CArrayObj *list=this.GetListWinFormsObj();
//--- Get object indices in the list with the maximum and minimum X and Y coordinates
   int imaxx=CSelect::FindGraphCanvElementMax(list,CANV_ELEMENT_PROP_COORD_X);
   int iminx=CSelect::FindGraphCanvElementMin(list,CANV_ELEMENT_PROP_COORD_X);
   int imaxy=CSelect::FindGraphCanvElementMax(list,CANV_ELEMENT_PROP_COORD_Y);
   int iminy=CSelect::FindGraphCanvElementMin(list,CANV_ELEMENT_PROP_COORD_Y);
//--- Get objects with the maximum and minimum X and Y coordinates from the list
   CWinFormBase *maxx=list.At(imaxx);
   CWinFormBase *minx=list.At(iminx);
   CWinFormBase *maxy=list.At(imaxy);
   CWinFormBase *miny=list.At(iminy);
//--- If at least one of the four objects is not received, return 'false'
   if(maxx==NULL || minx==NULL || maxy==NULL || miny==NULL)
      return false;
//--- Get the minimum X and Y coordinate
   int min_x=minx.CoordX();
   int min_y=fmin(miny.CoordY(),maxy.BottomEdge());
//--- Calculate the total width and height of all bound objects
   int w=maxx.RightEdge()-min_x;
   int h=int(fmax(miny.CoordY(),maxy.BottomEdge())-min_y);
//--- Calculate the number of pixels, by which we need to resize the panel in width and height
   int excess_x=w-this.m_underlay.Width();
   int excess_y=h-this.m_underlay.Height();
//--- Calculate the offset, by which the bound objects are to be moved
   int shift_x=m_underlay.CoordX()-min_x;
   int shift_y=m_underlay.CoordY()-min_y;
//--- If failed to change the panel size, return 'true'
   if(excess_x==0 && excess_y==0)
      return true;
//--- If it is necessary to move the attached objects inside the panel along the X or Y coordinate
   bool res=true;
   if(shift_x>0 || shift_y>0)
     {
      //--- In the loop by all attached objects,
      for(int i=0;i<list.Total();i++)
        {
         //--- get the next object.
         CWinFormBase *obj=list.At(i);
         if(obj==NULL)
            continue;
         //--- If the object needs to be shifted horizontally, write the shift result to 'res'
         if(shift_x>0)
            res &=obj.Move(obj.CoordX()+shift_x,obj.CoordY());
         //--- If the object needs to be shifted vertically, write the shift result to 'res'
         if(shift_y>0)
            res &=obj.Move(obj.CoordX(),obj.CoordY()+shift_y);
         //--- Set new relative object X and Y coordinates
         obj.SetCoordXRelative(obj.CoordX()-this.m_underlay.CoordX());
         obj.SetCoordYRelative(obj.CoordY()-this.m_underlay.CoordY());
        }
     }
//--- Return the result of resizing the panel
   return
     (
      //--- If we failed to move at least one bound object, return 'false'
      !res ? false :
      //--- Otherwise, if only a size increase
      this.AutoSizeMode()==CANV_ELEMENT_AUTO_SIZE_MODE_GROW ? 
      this.Resize(this.Width()+(excess_x>0  ? excess_x : 0),this.Height()+(excess_y>0  ? excess_y : 0),redraw) :
      //--- if both increase and decrease
      this.Resize(this.Width()+(excess_x!=0 ? excess_x : 0),this.Height()+(excess_y!=0 ? excess_y : 0),redraw)
     );
  }
//+------------------------------------------------------------------+

Die Methodenlogik wird in den Codekommentaren beschrieben. Wenn ich die minimale Y-Achsenkoordinate ermittle, stoße ich gelegentlich auf ein seltsames Verhalten in Bezug auf den erhaltenen Wert... Wenn das Objekt zum ersten Mal konstruiert wird, wird die maximale Koordinate mit dem Maximalwert zurückgegeben, während die minimale Koordinate mit dem Minimalwert zurückgegeben wird. Alles ist korrekt. Nach der Neuanordnung der Objekte wird jedoch die maximale Koordinate mit dem minimalen Wert zurückgegeben, während die minimale Koordinate mit dem maximalen Wert zurückgegeben wird. Ich konnte die Gründe für dieses Verhalten noch nicht herausfinden, und ich musste zwischen zwei Werten wählen — wenn ich den Maximalwert anfordere, erhalten wir das Maximum der beiden Werte, während wir bei der Anforderung des Minimalwerts das Minimum der beiden Werte erhalten.
Warum berechnen wir X- und Y-Abstand? Beim Binden der Objekte an den rechten oder unteren Rand des Paneels werden sie entweder vom rechten oder vom unteren Rand des Paneels aus aufgebaut. So können sie über das linke oder obere Bedienfeld hinausgehen. Da der Koordinatenursprung des Paneels (und jedes anderen grafischen Elements) in der oberen linken Ecke beginnt, vergrößert sich das Paneel nach rechts oder unten, wenn die Größe des Paneels erhöht wird, um die Größe aller darin befindlichen Objekte anzupassen. So hat das Paneel die Größe aller darin angeordneten Objekte, aber der Koordinatenursprung des Paneels entspricht nicht dem sichtbaren Ursprung aller Objekte. Daher müssen wir diese Objekte je nach Objektbindungsmodus entweder nach rechts oder nach unten um den berechneten Betrag verschieben.

Die Methode gibt die Liste der angehängten Objekte vom Typ WinForms Base oder höher zurück:

//+------------------------------------------------------------------+
//| Return the list of bound objects                                 |
//| of WinForms base type and higher                                 |
//+------------------------------------------------------------------+
CArrayObj *CPanel::GetListWinFormsObj(void)
  {
   return CSelect::ByGraphCanvElementProperty(this.GetListElements(),CANV_ELEMENT_PROP_TYPE,GRAPH_ELEMENT_TYPE_WF_BASE,EQUAL_OR_MORE);
  }
//+------------------------------------------------------------------+

Die Methode gibt einfach die Liste zurück, die nur Objekte vom Typ WinForms basic oder höher enthält, d. h. entweder das Basis-WinForms-Objekt oder seine Nachkommen. Die Auswahl von Objekten erfolgt durch Sortierung nach Typ aus der allgemeinen Liste aller an das Paneel gebundenen Objekte unter Verwendung der Klasse CSelect.

Führen wir eine Optimierung durch und beseitigen wir Logikfehler in den Methoden zur Erstellung der Paneel-WinForms-Objekte der Klasse CGraphElementsCollection in \MQL5\Include\DoEasy\Collections\GraphElementsCollection.mqh.

Ich habe einen Fehler gemacht, weil der Objektrahmen zweimal gezeichnet wurde, obwohl die Rahmenfarbe möglicherweise nicht für ihn eingestellt wurde. Außerdem kann es sein, dass die Rahmengröße des Paneels nicht richtig eingestellt wurde, wenn sie den Methoden als Standardwert gleich -1 übergeben wurde. Wir hatten keine Prüfung für diesen Wert, also wurde er gesetzt, anstatt die Standardrahmengröße im Falle des Wertes -1 zu setzen.

Bei allen Methoden zur Erstellung von Paneelen wurden identische oder ähnliche Änderungen vorgenommen:

//--- Create a WinForms Panel object graphical object on canvas on a specified chart and subwindow with the vertical gradient filling
   int               CreatePanelVGradient(const long chart_id,
                                          const int subwindow,
                                          const string name,
                                          const int x,
                                          const int y,
                                          const int w,
                                          const int h,
                                          color &clr[],
                                          const uchar opacity,
                                          const bool movable,
                                          const bool activity,
                                          const int  frame_width=-1,
                                          ENUM_FRAME_STYLE frame_style=FRAME_STYLE_BEVEL,
                                          const bool shadow=false,
                                          const bool redraw=false)
                       {
                        int id=this.m_list_all_canv_elm_obj.Total();
                        CPanel *obj=new CPanel(chart_id,subwindow,name,x,y,w,h);
                        ENUM_ADD_OBJ_RET_CODE res=this.AddOrGetCanvElmToCollection(obj,id);
                        if(res==ADD_OBJ_RET_CODE_ERROR)
                           return WRONG_VALUE;
                        obj.SetID(id);
                        obj.SetActive(activity);
                        obj.SetMovable(movable);
                        obj.SetColorsBackground(clr);
                        obj.SetColorFrame(obj.ColorBackground());
                        obj.SetBorderStyle(frame_style);
                        obj.SetOpacity(opacity,false);
                        obj.SetFrameWidthAll(frame_width==WRONG_VALUE ? DEF_FRAME_WIDTH_SIZE : frame_width);
                        //--- Draw the shadow drawing flag
                        obj.SetShadow(shadow);
                        if(shadow)
                          {
                           color clrS=obj.ChangeColorLightness(obj.ChangeColorSaturation(obj.ColorBackground(),-100),-20);
                           obj.DrawShadow(3,3,clrS,CLR_DEF_SHADOW_OPACITY,DEF_SHADOW_BLUR);
                          }
                        obj.DrawRectangle(0,0,obj.Width()-1,obj.Height()-1,obj.ColorFrame(),obj.Opacity());
                        if(redraw)
                          {
                           obj.Erase(clr,opacity,true,false,redraw);
                           obj.DrawRectangle(0,0,obj.Width()-1,obj.Height()-1,obj.ColorFrame(),obj.Opacity());
                          } 
                        obj.SetActiveAreaShift(obj.FrameWidthLeft(),obj.FrameWidthBottom(),obj.FrameWidthRight(),obj.FrameWidthTop());
                        obj.Done();
                        return obj.ID();
                       }

Legen wir die Rahmenfarbe gleich der Hintergrundfarbe des Paneels fest (die erste oder einzige Farbe des Farbverlaufs). Beim Einstellen der Rahmengröße prüft , welcher Wert an die Methode übergeben wurde. Wenn -1, wird der Standardwert gesetzt, der in der Makrosubstitution DEF_FRAME_WIDTH_SIZE in Defines.mqh festgelegt ist. Wenn das Neuzeichnungs-Flag gesetzt ist, zeichnen wir das Feld mit einer Hintergrundfarbe oder einem Farbverlauf aus und dann ein umrandendes Rechteck darüber. In diesem Fall wird der Rahmen mit der virtuellen Methode Erase() der Klasse CPanel gezeichnet.

Solche Verbesserungen wurden bei allen Methoden zur Erstellung von Paneelen vorgenommen. Sie sind in den Dateien im Anhang zu diesem Artikel zu finden.


Test

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

Da an den Container gebundene Paneel-Objekte nun standardmäßig ohne Rahmen erstellt werden, geben wir die Rahmenbreite und den Rahmentyp für jedes dieser Objekte in OnInit() an. Die Farbe jedes nachfolgenden Feldes wird um den Wert aufgehellt, der sich aus dem Schleifenindex ergibt (Hintergrundfarbe aufgehellt um einen Betrag, der dem Schleifenindex * 4 entspricht). Dies ist notwendig, um die Änderungen in der Anordnung des Paneels im Container deutlich zu erkennen, wenn die Art und Weise, wie sie gebunden sind, geändert wird:

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- Set EA global variables
   ArrayResize(array_clr,2);        // Array of gradient filling colors
   array_clr[0]=C'26,100,128';      // Original ≈Dark-azure color
   array_clr[1]=C'35,133,169';      // Lightened original color
//--- Create the array with the current symbol and set it to be used in the library
   string array[1]={Symbol()};
   engine.SetUsedSymbols(array);
   //--- Create the timeseries object for the current symbol and period, and show its description in the journal
   engine.SeriesCreate(Symbol(),Period());
   engine.GetTimeSeriesCollection().PrintShort(false); // Short descriptions
//--- Create WinForms Panel object
   CPanel *pnl=NULL;
   pnl=engine.CreateWFPanel("WFPanel",50,50,230,150,array_clr,200,true,true,false,-1,FRAME_STYLE_BEVEL,true,false);
   if(pnl!=NULL)
     {
      //--- Set Padding to 4
      pnl.SetPaddingAll(4);
      //--- Set the flags of relocation, auto resizing and auto changing mode from the inputs
      pnl.SetMovable(InpMovable);
      pnl.SetAutoSize(InpAutoSize,false);
      pnl.SetAutoSizeMode((ENUM_CANV_ELEMENT_AUTO_SIZE_MODE)InpAutoSizeMode,false);
      //--- In the loop, create 6 bound panel objects
      for(int i=0;i<6;i++)
        {
         //--- create the panel object with coordinates along the X axis in the center and 10 along the Y axis, the width of 80 and the height of 50
         CPanel *prev=pnl.GetElement(i-1);
         int xb=0, yb=0;
         int x=(i<3 ? (prev==NULL ? xb : prev.CoordXRelative()) : xb+prev.Width()+20);
         int y=(i<3 ? (prev==NULL ? yb : prev.BottomEdgeRelative()+16) : (i==3 ? yb : prev.BottomEdgeRelative()+16));
         if(pnl.CreateNewElement(GRAPH_ELEMENT_TYPE_WF_PANEL,pnl,x,y,80,40,C'0xCD,0xDA,0xD7',200,true,false))
           {
            CPanel *obj=pnl.GetElement(i);
            if(obj==NULL)
               continue;
            obj.SetFrameWidthAll(3);
            obj.SetBorderStyle(FRAME_STYLE_BEVEL);
            obj.SetColorBackground(obj.ChangeColorLightness(obj.ColorBackground(),4*i));
           }
        }
      pnl.Redraw(true);
     }
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+


Kompilieren Sie den EA und starten Sie ihn in einem Symbolchart:


So passt sich das Paneel an die allgemeine Größe der angehängten Objekte an, verschiedene Bindungsmethoden funktionieren korrekt und das Paneel ändert seine Größe ebenfalls korrekt. Beim Platzieren von Objekten mit unterschiedlichen Bindungsmethoden werden sie nicht so angeordnet, wie ich es gerne hätte - sie sollten nicht an den Rändern des Bedienfelds angebracht werden, und jedes nachfolgende Objekt in der Liste sollte an den Rändern des vorherigen angebracht werden, wenn es die gleiche Bindung hat. Insbesondere wird das allerletzte Objekt auf die volle Breite und Höhe des Containers gedehnt, was nicht korrekt ist, da es auf die inneren Kanten der zuvor an die Kanten des Paneels gebundenen Objekte beschränkt sein sollte, d. h. es sollte innerhalb des freien Raums zwischen ihnen liegen. Ich werde das korrekte Verhalten von gebundenen Objekten in späteren Artikeln implementieren.


Was kommt als Nächstes?

Im nächsten Artikel werde ich meine Arbeit am Paneel-Objekt fortsetzen und mit der Entwicklung neuer Steuerelemente beginnen, einschließlich des WinForms Textlabel-Objekts.

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

Zurück zum Inhalt

*Vorherige Artikel in dieser Reihe:

DoEasy. Steuerung (Teil 1): Erste Schritte
DoEasy. Steuerung (Teil 2): Arbeiten an der Klasse CPanel
DoEasy. Steuerung (Teil 3): Erstellen gebundener Steuerelemente
DoEasy. Steuerung (Teil 4): Paneel-Steuerung, Parameter für Padding und Dock
DoEasy. Steuerung (Teil 5): Basisobjekt von WinForms, Paneel-Steuerelement, Parameter AutoSize


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

Beigefügte Dateien |
MQL5.zip (4351.9 KB)
Techniken des MQL5-Assistenten, die Sie kennen sollten (Teil 02): Kohonen-Karten Techniken des MQL5-Assistenten, die Sie kennen sollten (Teil 02): Kohonen-Karten
Der Händler von heute ist ein Philomath, der fast immer (entweder bewusst oder unbewusst...) nach neuen Ideen sucht, sie ausprobiert, sich entscheidet, sie zu modifizieren oder zu verwerfen; ein explorativer Prozess, der einiges an Sorgfalt kosten sollte. Dies legt eindeutig einen hohen Stellenwert auf die Zeit des Händlers und die Notwendigkeit, Fehler zu vermeiden. Diese Artikelserie wird vorschlagen, dass der MQL5-Assistent eine Hauptstütze für Händler sein sollte. Warum? Denn der Händler spart nicht nur Zeit, indem er seine neuen Ideen mit dem MQL5-Assistenten zusammenstellt, und reduziert Fehler durch doppelte Codierung erheblich. Er ist letztendlich so eingestellt, dass er seine Energie auf die wenigen kritischen Bereiche seiner Handelsphilosophie konzentriert.
Einen handelnden Expert Advisor von Grund auf neu entwickeln (Teil 14): Hinzufügen des Volumens zum Preis (II) Einen handelnden Expert Advisor von Grund auf neu entwickeln (Teil 14): Hinzufügen des Volumens zum Preis (II)
Heute werden wir unserem EA weitere Ressourcen hinzufügen. Dieser interessante Artikel kann einige neue Ideen und Methoden zur Präsentation von Informationen liefern. Gleichzeitig kann es Ihnen helfen, kleinere Fehler in Ihren Projekten zu beheben.
Einen handelnden Expert Advisor von Grund auf neu entwickeln (Teil 15): Zugang zu Daten im Internet (I) Einen handelnden Expert Advisor von Grund auf neu entwickeln (Teil 15): Zugang zu Daten im Internet (I)
Wie kann man über den MetaTrader 5 auf Online-Daten zugreifen? Es gibt viele Webseiten und Orte im Internet, die eine riesige Menge an Informationen bieten. Sie müssen nur wissen, wo Sie suchen und wie Sie diese Informationen am besten nutzen können.
Datenwissenschaft und maschinelles Lernen (Teil 05): Entscheidungsbäume Datenwissenschaft und maschinelles Lernen (Teil 05): Entscheidungsbäume
Entscheidungsbäume imitieren die Art und Weise, wie Menschen denken, um Daten zu klassifizieren. Schauen wir mal, wie man so einen Baum erstellt und ihn zur Klassifizierung und Vorhersage einiger Daten verwenden kann. Das Hauptziel des Entscheidungsbaum-Algorithmus ist es, die Daten mit Fremdanteilen und die reinen oder knotennahen Daten abzutrennen.