DoEasy. Elementos de control (Parte 6): Control Panel, cambio automático del tamaño del contenedor según el contenido interno
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:
- 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.
*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
- Aplicaciones de trading gratuitas
- 8 000+ señales para copiar
- Noticias económicas para analizar los mercados financieros
Usted acepta la política del sitio web y las condiciones de uso