English Русский 中文 Deutsch 日本語 Português
preview
DoEasy. Elementos de control (Parte 6): Control Panel, cambio automático del tamaño del contenedor según el contenido interno

DoEasy. Elementos de control (Parte 6): Control Panel, cambio automático del tamaño del contenedor según el contenido interno

MetaTrader 5Ejemplos | 20 julio 2022, 11:46
334 0
Artyom Trishkin
Artyom Trishkin

Contenido


Concepto

Hoy implementaremos el cambio automático del tamaño del panel en el caso de que la propiedad Dock esté activa para los objetos adjuntos. Al asignar esta propiedad a cualquiera de los objetos vinculados, dicho objeto deberá fijarse a su lugar de anclaje, y el panel deberá ajustar su tamaño a los tamaños combinados de todos los objetos adjuntos. Es decir, el cambio del tamaño del panel se producirá después de asignar la propiedad Dock a cualquiera de los objetos adjuntos. Y si dicha propiedad se establece a su vez en un gran número de objetos adjuntos, entonces, con cada siguiente objeto que se adjunte a su lugar, el panel deberá ajustar en consonancia su tamaño a su estado interno modificado.

Para evitar ajustar visualmente el tamaño del panel a la nueva disposición de los objetos con cada nuevo objeto adjunto (lo cual provocará efectos visuales desagradables), hemos optimizado el procesamiento por lotes de la adición de objetos al panel, así como la modificación del aspecto de los mismos: el panel ajustará su tamaño visualmente solo después de que el último objeto adjunto se coloque en su lugar.
Hoy continuaremos con esta optimización del procesamiento de la colocación por lotes de objetos dentro de nuestro contenedor.

Además de trabajar en los objetos WinForms, añadiremos al objeto de biblioteca "Símbolo" nuevas propiedades anteriormente anunciadas para MetaTrader 5, en el Build 3260:

MQL5: Añadidas nuevas propiedades a la enumeración 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

Estas propiedades permiten obtener la tasa de cálculo de swaps para cada día de la semana. 1 — cálculo único de swaps, 3 — cálculo triple de swaps, 0 — los swaps no se calculan.

Para los objetos de WinForms que creamos, añadiremos las nuevas propiedades como variables de clase. Al mismo tiempo, todos los objetos de la biblioteca se construyen según un determinado concepto, descrito en en el primer artículo sobre la creación de la biblioteca: cada objeto tiene un conjunto de propiedades ubicadas en las tres enumeraciones de las propiedades de tipo entero, real y string de los objetos. En esta ocasión, haremos que todas las propiedades nuevas (añadidas anteriormente) de los objetos de WinForms sean constantes de estas enumeraciones, de forma que para cada objeto gráfico dichas propiedades nuevas se encuentren en las listas generales de todas sus propiedades. Esto permitirá en el futuro usar todas las propiedades de los objetos de WinForms para mostrarlas en los elementos gráficos, por ejemplo, un programa de GUI para construir visualmente una estructura gráfica de asesores o indicadores como MS Visual Studio en el terminal.


Mejorando las clases de la biblioteca

En el archivo \MQL5\Include\DoEasy\Data.mqh, añadimos los índices de los nuevos mensajes:

   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

y los textos de los mensajes que se corresponden con los índices nuevamente añadidos:

   {"Свойство не поддерживается в 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"},


En el archivo \MQL5\Include\DoEasy\Defines.mqh, añadimos a la enumeración de las propiedades reales del objeto de símbolo las nuevas propiedades y aumentamos la cantidad de propiedades reales del símbolo de 68 a 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
//+------------------------------------------------------------------+

Para que podamos clasificar y seleccionar los objetos símbolo según las nuevas propiedades, en la enumeración de posibles criterios de clasificación para objetos símbolo, introduciremos las nuevas constantes correspondientes a las propiedades añadidas:

   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


En la enumeración de las propiedades enteras del elemento gráfico en el lienzo, añadimos las propiedades correspondientes a las variables previamente agregadas de los objetos WinForms que almacenan estas propiedadesy aumentamos el número de propiedades enteras del objeto de 25 a 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

En la enumeración de posibles criterios para ordenar objetos de WinForms, agreguemos la ordenación por propiedades enteras recién agregadas:

   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 control 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
  };
//+------------------------------------------------------------------+

Ahora todos los objetos WinForms se pueden seleccionar y clasificar según las propiedades de los objetos gráficos que son exclusivas de los objetos WinForms.


Como hemos añadido nuevas propiedades al objeto de símbolo, deberemos implementar el trabajo con ellas en la clase de objeto de símbolo.

En el archivo de clase \MQL5\Include\DoEasy\Objects\Symbols\Symbol.mqh, introducimos las mejoras necesarias.

En la sección protegida de la clase, en el bloque con los métodos para obtener y retornar las propiedades reales del símbolo seleccionado a partir de sus parámetros, declaramos el método que retorna la tasa de cálculo del swap para el día de la semana indicado:

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


En la sección pública de la clase, declaramos el método que retorna una descripción de la tasa de cálculo del swap para el día de la semana indicado:

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


En el bloque de métodos para el acceso simplificado a las propiedades reales del objeto símbolo, escribimos los métodos que retornan la tasa de cálculo del swap para cada día de la semana y declaramos el método que retorna la tasa de cálculo del swap para el día de la semana indicado:

   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;


En el bloque de métodos para obtener y configurar los parámetros de los cambios de propiedad monitoreados, añadimos los métodos para trabajar con las nuevas propiedades del objeto de símbolo:

   //--- 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; }

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

Estos métodos permiten, directamente desde sus programas, establecer el valor monitoreado según el cual deberemos cambiar el parámetro controlado, y al registrar dicho cambio, recibir una señal en el programa sobre este evento. Ya los analizamos anteriormente al desarrollar la interactividad de los objetos de la biblioteca.

En el constructor paramétrico protegido de la clase, añadimos el guardado de las nuevas propiedades del objeto símbolo:

   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;

Aquí, los valores obtenidos por el método SymbolSwapRatio(), que analizaremos a continuación, se añaden a la matriz de propiedades del objeto.

Fuera del cuerpo de la clase, escribimos las implementaciones de los nuevos métodos declarados.

Método protegido que retorna la tasa de cálculo del swap para el día de la semana indicado:

//+------------------------------------------------------------------+
//|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 
  }
//+------------------------------------------------------------------+

Aquí: en MQL4 no existe tal propiedad del símbolo; retornamos cero. Para MQL5, , según el día de la semana transmitido al método, retornamos la propiedad del símbolo correspondiente. El método se usa para escribir una propiedad de símbolo en la enumeración de sus propiedades reales en el constructor de clases.

Método público que retorna la tasa de cálculo del swap para el día de la semana indicado:

//+------------------------------------------------------------------+
//|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();
     }
  }
//+------------------------------------------------------------------+

Aquí: dependiendo del día de la semana transmitido al método, retornamos el valor de la propiedad usando los métodos públicos que retornan el valor de la propiedad para un día específico de la semana, escrito en la enumeración de las propiedades reales del objeto.

En el método que retorna la descripción de una propiedad real del símbolo, escribimos el retorno de la descripción de las nuevas propiedades del objeto símbolo:

      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 
         )  :
      ""
     );
  }
//+------------------------------------------------------------------+

Aquí, verificamos el build del terminal y, si está por debajo de 3260, se retornará un texto que indica que esta propiedad no es compatible con esta versión. De lo contrario, para MQL5, se mostrará una descripción de la propiedad, mientras que para MQL4, se mostrará un texto que indica que la propiedad no es compatible.

Método que retorna la descripción de la tasa de cálculo del swap para el día de la semana indicado:

//+------------------------------------------------------------------+
//| 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)
     );
  }
//+------------------------------------------------------------------+

Aquí: primero obtenemos el valor de la propiedad para el día de la semana transmitido al método, luego retornamos la descripción textual del valor de la propiedad (si el valor es igual a 0, 1 o 3), o generamos un valor double convertido a texto con tres decimales. (En realidad, solo el tiempo y las pruebas mostrarán cuántos decimales mostrar).

Para guardar los objetos en archivos, los objetos gráficos (y todos los objetos de biblioteca en el futuro) tienen una estructura de propiedad de objeto. Todas las propiedades se almacenan en la estructura; luego, dicha estructura se guarda en un archivo. De la misma forma se lee desde el archivo para restaurar las propiedades del objeto.
Como tenemos nuevas propiedades para los objetos gráficos, deberemos añadirlas a la estructura.

En el archivo \MQL5\Include\DoEasy\Objects\Graph\GCnvElement.mqh, añadimos a la estructura del objeto las nuevas propiedades:

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


En el método que crea la estructura del objeto, añadimos la escritura de las propiedades del objeto en los campos de la estructura:

   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


En el método que crea un objeto a partir de una estructura, añadimos la lectura de los campos de la estructura desde los valores de las propiedades del objeto:

   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

Ahora todos los objetos gráficos se guardarán correctamente en un archivo y se restaurarán cuando comencemos a almacenar las propiedades de los objetos de la biblioteca en los archivos.


Vamos a hacer una pequeña modificación en la clase básica de los objetos WinForms, en el archivo \MQL5\Include\DoEasy\Objects\Graph\WForms\WinFormBase.mqh.

Primero, necesitaremos poder seleccionar y clasificar estos objetos según sus propiedades. Para hacer esto, incluiremos el archivo de clase CSelect en el archivo:

//+------------------------------------------------------------------+
//|                                                  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"
//+------------------------------------------------------------------+

Ahora podremos usar la clase CSelect en todas las clases heredadas para seleccionar y clasificar los objetos WinForms según sus propiedades.

En segundo lugar, en el método que redibuja el objeto, añadimos una verificación que compruebe si se ha establecido la bandera de redibujado en el ciclo de procesamiento para todos los objetos adjuntos, de forma que en realidad se redibujen solo si la bandera de redibujado está establecida. De lo contrario, no será necesario redibujar los objetos adjuntos, por ejemplo, para deshacerse de los artefactos visuales al realizar cambios en las propiedades del objeto de panel. Al resetear la bandera de redibujado, no será necesario redibujar los objetos adjuntos al contenedor; sus propiedades se cambiarán en los otros métodos, y aquí solo tendrá lugar el redibujado real de todos los objetos.

Por lo tanto, escribiremos una comprobación para la bandera establecida y una llamada para redibujar los objetos solo si la bandera está establecida:

//+------------------------------------------------------------------+
//| 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());
  }
//+------------------------------------------------------------------+


Vamos a mejorar la clase del objeto de panel en el archivo \MQL5\Include\DoEasy\Objects\Graph\WForms\Containers\Panel.mqh.

En la sección privada de la clase, declaramos un método para calcular las coordenadas de vinculación de los objetos Dock:

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

protected:

Hoy no implementaremos este método: simplemente escribiremos un método vacío fuera del cuerpo de la clase:

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

Nos ocuparemos de su implementación después de crear la clase de objeto WinForms "Etiqueta de texto" (Label), para que podamos comprobar visualmente la clasificación de los objetos cuando estén vinculados según el valor de la propiedad Dock y al mismo tiempo procesar el movimiento correcto de los objetos adjuntos a sus contenedores, que a su vez están unidos al contenedor principal, los paneles.

De la sección protegida de la clase, eliminamos la declaración de los métodos que retornan el valor máximo de los límites del objeto Dock que van más allá del contenedor en anchura y altura:

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

También eliminaremos la implementación de estos métodos escritos fuera del cuerpo de la clase.

Ahora no necesitamos estos métodos, ya que podemos usar la clase de biblioteca CSelect para encontrar los valores deseados.

En la sección pública de la clase, declaramos el método que retorna la lista de objetos adjuntos con el tipo básico WinForms y superior:

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);


El objeto de panel aún así debe crearse sin un marco. Si el panel lo necesita, siempre podremos añadirlo después de crear el objeto. Por consiguiente, en los constructores de clases, estableceremos el tipo de marco como ausente:

//--- 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);
  }
//+------------------------------------------------------------------+

En el último constructor, además de establecer el tipo de marco, también eliminaremos su creación:

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


En el método que organiza los objetos vinculados según el orden de su enlace Dock, no hemos procesado antes la ubicación de los objetos si para el contenedor (panel) la bandera no estaba configurada para realizarse su cambio automático de tamaño de forma que se ajuste al contenido interno. Hoy solo copiaremos los manejadores del modo de anclaje desde los manejadores del panel que cambia de tamaño. No obstante, añadiremos una verificación preliminar de la presencia de un sustrato, ahora obtendremos la lista de objetos con un nuevo método que retorna una lista solo de objetos WinForms. Si el modo de cambio automático del tamaño del panel está habilitado, primero cambiaremos el tamaño del panel a su tamaño originaly luego llamaremos al método que cambia el tamaño del panel para que se ajuste a su contenido. Después de eso, en un ciclo, procesaremos los modos de vinculación de todos los objetos adjuntos y reajustaremos las dimensiones del panel según los objetos cambiados en su interior:

//+------------------------------------------------------------------+
//| 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;
  }
//+------------------------------------------------------------------+

Aquí podríamos hacer todo en un ciclo, es decir, sin dividirnos en dos bloques con dos ciclos separados idénticos entre sí, y además verificar la bandera de cambio automático del tamaño del panel. Esto reducirá el código a la mitad. Pero por ahora, lo dejaremos así, en caso de que tengamos que mejorar aún más el bloque de código con el procesamiento de la bandera de cambio automático del tamaño del panel. Solo cuando todo esté depurado y quede claro que no se requieren más cambios y mejoras, optimizaremos el código de este método.

Asimismo, el método que ajusta el tamaño de un elemento según su contenido interno también ha sido rediseñado, debido a que ahora podemos obtener los datos necesarios usando la clase CSelect:

//+------------------------------------------------------------------+
//| 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)
     );
  }
//+------------------------------------------------------------------+

La lógica del método se explica con detalle en los comentarios al código. Un aspecto que quería señalar: al obtener la coordenada mínima en el eje Y, noté algo extraño que sucede de vez en cuando con la magnitud del valor obtenido... Cuando el objeto se construye por primera vez, la coordenada máxima se retorna con el valor máximo y la coordenada mínima con el valor mínimo. Aquí, todo es correcto. Pero después de reconstruir los objetos, la coordenada máxima se retorna con el valor mínimo y la mínima con el máximo. Todavía no he podido averiguar los motivos de semejante comportamiento, así que tuve que elegir entre dos valores: al solicitar el valor máximo, obtenemos el máximo de los dos, y al solicitar el mínimo, obtenemos el mínimo de los dos.
¿Por qué calculamos los desplazamientos en X e Y? El hecho es que, al vincular los objetos a la derecha o la parte inferior del panel, estos se construyen desde el borde derecho o inferior del mismo. Por lo tanto, pueden salirse de los límites del panel por la izquierda o por arriba. Y como el origen de las coordenadas del panel (y cualquier otro elemento gráfico) parte desde la esquina superior izquierda, cuando el tamaño del panel aumenta para adaptarse al tamaño de todos los objetos ubicados dentro de él (y se salen de los bordes del panel, ya sea por la izquierda o por arriba), el panel aumentará hacia la derecha o hacia abajo. Así, tendremos que el panel posee las dimensiones correspondientes a todos los objetos alineados en su interior, pero el origen del panel no se corresponderá con el origen visible de todos los objetos. Y es por eso que estos objetos deberán desplazarse hacia la derecha o hacia abajo en la magnitud calculada, según el modo de vinculación del objeto.

Método que retorna la lista de objetos adjuntos con el tipo WinForms básico y superior:

//+------------------------------------------------------------------+
//| 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);
  }
//+------------------------------------------------------------------+

El método simplemente retorna una lista que contiene solo objetos de tipo WinForms Base o superior, es decir, o bien el objeto WinForms básico, o bien sus descendientes. Los objetos se seleccionan usando el filtrado por tipo de la lista general de todos los objetos adjuntos al panel utilizando la clase CSelect.

Vamos a optimizar y eliminar los errores lógicos en los métodos de creación de objetos WinForms "Panel" de la clase CGraphElementsCollection en el archivo \MQL5\Include\DoEasy\Collections\GraphElementsCollection.mqh.

Teníamos errores en el sentido de que el marco del objeto se dibujaba dos veces y no se podía establecer el color del mismo. Además, las dimensiones del marco del panel no se podían establecer si se transmitían a los métodos como un valor por defecto igual a -1. Antes no teníamos una comprobación para este valor, por lo que precisamente este se establecía en lugar de configurar los valores por defecto de los tamaños del marco con un valor de -1.

Todos los métodos para crear paneles se han modificado de la siguiente manera, o de una forma similar en su lógica:

//--- 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();
                       }

Establecemos el color del marco en el mismo color que el fondo del panel (el primero en el gradiente, o bien el único). Al establecer el tamaño del marco, verificamos qué valor se transmite al método y, si es -1, establecemos el valor por defecto escrito en la macrosustitución DEF_FRAME_WIDTH_SIZE en el archivo Defines.mqh. Si la bandera de redibujado ha sido establecida, rellenaremos el panel con un color de fondo o bien con un gradiente, y dibujaremos un rectángulo de contorno encima. En este caso, el marco se dibujará en el método virtual Erase() de la clase CPanel.

Dichas mejoras se han realizado en todos los métodos de creación de paneles, y se pueden encontrar en los archivos adjuntos al artículo.


Simulación

Para la simulación, tomaremos el asesor del artículo anterior y lo guardaremos en la nueva carpeta \MQL5\Experts\TestDoEasy\Part106\ con el nombre TestDoEasyPart106.mq5.

¿Cómo realizaremos la simulación? Como ahora los objetos del panel vinculados al contenedor se crean sin marco por defecto, después de su creación, en el manejador OnInit() , indicaremos la anchura del marco y su tipo para cada uno de ellos. El color de cada panel subsiguiente será más claro en el valor calculado a partir del índice de ciclo (color de fondo aclarado en una magnitud igual al índice de ciclo * 4). Necesitamos esto para ver claramente el cambio en la disposición de los paneles en el contenedor al cambiar el método de vinculación:

//+------------------------------------------------------------------+
//| 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);
  }
//+------------------------------------------------------------------+


Vamos a compilar el asesor y a ejecutarlo en el gráfico:


Por lo tanto, el panel se adapta a los tamaños generales de los objetos adjuntos; los diferentes métodos de vinculación funcionan correctamente y el panel cambia sus tamaños correctamente. Al colocar objetos con diferentes métodos de vinculación, no se organizan como nos gustaría: no deberían vincularse a los bordes del panel, y cada objeto posterior en la lista debería adjuntarse a los bordes del anterior si tiene la misma vinculación. En particular, el último objeto se expande a todo lo ancho y alto del contenedor, y debería limitarse a los bordes interiores de los objetos previamente adjuntos a los bordes del panel, es decir, ubicarse en el espacio libre entre ellos. Pero este comportamiento de los objetos adjuntos lo implementaremos en artículos posteriores.


¿Qué es lo próximo?

En el próximo artículo, continuaremos trabajando en el objeto "Panel" e iniciaremos el desarrollo de nuevos controles, en concreto, del objeto de etiqueta de texto de WinForms.

Más abajo, adjuntamos todos los archivos de la versión actual de la biblioteca, así como los archivos del asesor de prueba y el indicador de control de eventos de los gráficos para MQL5. Podrá descargarlo todo y ponerlo a prueba por sí mismo. Si tiene preguntas, observaciones o sugerencias, podrá concretarlas en los comentarios al artículo.

Volver al contenido

*Artículos de esta serie:

DoEasy. Elementos de control (Parte 1): Primeros pasos
DoEasy. Elementos de control (Parte 2): Continuamos trabajando con la clase CPanel
DoEasy. Elementos de control (Parte 3): Creando controles vinculados
DoEasy. Elementos de control (Parte 4): Elemento de control "Panel", parámetros Padding y Dock
DoEasy. Elementos de control (Parte 5): Objeto básico WinForms, control «Panel», parámetro AutoSize


Traducción del ruso hecha por MetaQuotes Ltd.
Artículo original: https://www.mql5.com/ru/articles/10989

Archivos adjuntos |
MQL5.zip (4347.87 KB)
Aprendiendo a diseñar un sistema comercial basado en Momentum Aprendiendo a diseñar un sistema comercial basado en Momentum
En el artículo anterior, mencionamos la importancia de detectar las tendencias, es decir, de determinar la dirección del movimiento del precio. En este artículo, hablaremos sobre otro concepto importante en el trading, que también existe en forma de indicador: el impulso del precio o el indicador Momentum. Asimismo, desarrollaremos nuestro propio sistema comercial basado en este indicador.
Desarrollando un EA comercial desde cero (Parte 21): Un nuevo sistema de órdenes (IV) Desarrollando un EA comercial desde cero (Parte 21): Un nuevo sistema de órdenes (IV)
Finalmente el sistema visual funcionará... aún no del todo. Aquí terminaremos de hacer los cambios básicos, y no serán pocos, serán muchos, y todos ellos necesarios, y todo el trabajo será bastante interesante.
Aprendiendo a diseñar un sistema de trading con ATR Aprendiendo a diseñar un sistema de trading con ATR
En este artículo, analizaremos una nueva herramienta técnica que puede usarse en el trading. Esta es una continuación de nuestra serie para aprender a diseñar sistemas de trading sencillos. En esta ocasión, trabajaremos con otro popular indicador técnico, el rango medio verdadero (Average True Range, ATR).
Indicadores con control interactivo en el gráfico. Indicadores con control interactivo en el gráfico.
Una nueva mirada a la interfaz del indicador. Lo más importante es la comodidad. Tras probar docenas de estrategias comerciales diferentes a lo largo de los años, además de probar cientos de indicadores diferentes, he llegado a algunas conclusiones propias que quiero compartir con ustedes en este artículo.