DoEasy. Steuerung (Teil 6): Paneel-Steuerung, automatische Größenanpassung des Containers an den inneren Inhalt
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:
- 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.
*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
- Freie Handelsapplikationen
- Über 8.000 Signale zum Kopieren
- Wirtschaftsnachrichten für die Lage an den Finanzmärkte
Sie stimmen der Website-Richtlinie und den Nutzungsbedingungen zu.