DoEasy. Controles (Parte 6): Controle "Painel", redimensionamento automático do contêiner para adequá-lo ao seu conteúdo
Conteúdo
Ideia
Hoje elaboraremos o redimensionamento automático do painel no caso em que a propriedade Dock estiver ativa para os objetos inseridos nele. Quando definimos esta propriedade para qualquer um dos objetos encaixados, o objeto em questão deve permanecer em seu local de encaixe e o painel deve ajustar seu tamanho à extensão de todos os objetos anexados a ele. Ou seja, o redimensionamento do painel deve ocorrer depois que a propriedade Dock é definida para qualquer um dos objetos encaixados nele. E se esse redimensionamento for definido alternadamente para um grande número de objetos inseridos, com cada objeto encaixado o painel deve ajustar seu próprio tamanho ao seu estado interno alterado.
Para evitar efeitos visuais desagradáveis e não termos que ajustar visualmente o tamanho do painel para cada novo arranjo de objetos com cada novo encaixe de objeto, otimizamos o processamento em lote de inserção de objetos no painel e sua modificação visual. Desse modo, o painel ajustará seu tamanho visualmente somente após o último objeto encaixado nele ser colocado em seu lugar.
Hoje continuaremos a trabalhar nessa otimização do processamento da colocação em lote de objetos dentro do nosso contêiner.
Além de nos ocupar em objetos WinForms, vamos adicionar novas propriedades ao objeto de biblioteca "Símbolo", anunciadas anteriormente para MetaTrader 5 Build 3260:
- SYMBOL_SWAP_SUNDAY
- SYMBOL_SWAP_MONDAY
- SYMBOL_SWAP_TUESDAY
- SYMBOL_SWAP_WEDNESDAY
- SYMBOL_SWAP_THURSDAY
- SYMBOL_SWAP_FRIDAY
- SYMBOL_SWAP_SATURDAY
Elas permitem que você obtenha a taxa de acumulação de swaps para cada dia da semana. 1 — único swap acumulado, 3 — triplo, 0 — sem acumulação.
Para os objetos WinForms que criamos, adicionamos novas propriedades como variáveis de classe. E todos os objetos de biblioteca são construídos de acordo com o conceito descrito no primeiro artigo sobre como criar uma biblioteca: cada objeto possui um conjunto de propriedades localizadas em três enumerações de propriedades de objeto inteiro, real e string. Hoje vamos fazer com que todas as novas propriedades de objetos WinForms adicionadas anteriormente sejam constantes dessas enumerações para que para cada objeto gráfico essas novas propriedades estejam nas listas gerais de propriedades. Isso facilitará no futuro usar todas as propriedades dos objetos WinForms para exibi-los em elementos gráficos, por exemplo, ao usar um programa GUI para gerar o shell gráfico de EAs ou indicadores, semelhante ao MS Visual Studio no terminal.
Modificando as classes da biblioteca
No arquivo \MQL5\Include\DoEasy\Data.mqh, adicionamos os índices das novas mensagens:
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
e as mensagens de texto correspondentes aos índices recém-adicionados:
{"Свойство не поддерживается в 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"},
No arquivo \MQL5\Include\DoEasy\Defines.mqh, adicionamos novas propriedades à lista de propriedades reais do objeto símbolo e aumentamos o número de propriedades reais do símbolo de 68 para 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 os objetos símbolos sejam ordenados e selecionados de acordo com novas propriedades, na enumeração de possíveis critérios para ordenar objetos símbolos inseriremos novas constantes correspondentes às propriedades adicionadas:
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
Na enumeração das propriedades inteiras do elemento gráfico na tela, adicionamos as propriedades correspondentes às variáveis adicionadas anteriormente (dentro dos objetos WinForms) que armazenam essas propriedades e aumentamos o número de propriedades inteiras do objeto de 25 para 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
Na enumeração de possíveis critérios para classificar objetos WinForms, vamos adicionar a classificação por propriedades inteiras recém-adicionadas:
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 }; //+------------------------------------------------------------------+
Agora todos os objetos WinForms podem ser selecionados e classificados de acordo com essas propriedades de objetos gráficos que são exclusivos para objetos WinForms.
Como novas propriedades foram adicionadas ao objeto símbolo, precisamos implementar o trabalho com elas na classe de objeto símbolo.
No arquivo de classe \MQL5\Include\DoEasy\Objects\Symbols\Symbol.mqh, faremos as melhorias necessárias.
Na seção protegida da classe, no bloco de métodos para obter e retornar as propriedades reais do símbolo selecionado a partir de seus parâmetros, declararemos um método que retorna a taxa de swap acumulados para o dia da semana especificado:
double SymbolMarginHedged(void) const; double SymbolSwapRatio(ENUM_DAY_OF_WEEK day)const; bool SymbolMarginLong(void);
Na seção pública da classe, vamos declarar um método que retorna uma descrição da taxa de swap acumulado para o dia da semana especificado:
string GetSectorDescription(void) const; string GetIndustryDescription(void) const; string GetSwapRatioDescription(const ENUM_DAY_OF_WEEK day)const;
No bloco de métodos para acesso simplificado às propriedades reais do objeto símbolo, escrevemos métodos que retornam as taxas de swap acumulado para cada dia da semana e declaramos um método que retorna a taxa de swap acumulado para o dia da semana especificado:
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;
No bloco de métodos para obter e definir os parâmetros das alterações de propriedades rastreadas, adicionamos métodos para trabalhar com as novas propriedades do objeto 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; } }; //+------------------------------------------------------------------+
Esses métodos permitem, diretamente a partir de nossos programas, definir o valor monitorado segundo o qual o parâmetro controlado deve mudar e registrar tal mudança para logo receber um sinal no programa sobre este evento. Já vimos esses métodos ao desenvolver a interatividade de objetos de biblioteca.
No construtor paramétrico protegido da classe, escrevemos a preservação das novas propriedades do 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;
Aqui, os valores obtidos pelo método SymbolSwapRatio(), que consideraremos a seguir, se inserem no array de propriedades do objeto.
Fora do corpo da classe, escreveremos implementações dos novos métodos declarados.
Método protegido que retorna a taxa de swaps acumulados para o dia da semana especificado:
//+------------------------------------------------------------------+ //|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 } //+------------------------------------------------------------------+
Aqui: como MQL4 não possui tal propriedade de símbolo, retornamos zero. Para MQL5 , dependendo do dia da semana passado para o método, retornamos a propriedade do símbolo correspondente. O método é usado para introduzir uma propriedade de símbolo na sua enumeração de propriedades reais no construtor de classe.
Método público que retorna a taxa de swaps acumulados para o dia da semana especificado:
//+------------------------------------------------------------------+ //|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(); } } //+------------------------------------------------------------------+
Aqui: dependendo do dia da semana passado para o método, retornamos o valor da propriedade usando métodos públicos que retornam o valor da propriedade para um determinado dia da semana escrito na enumeração das propriedades reais do objeto .
No método que retorna a descrição da propriedade real do símbolo, escrevemos o retorno da descrição das novas propriedades do 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 ) : "" ); } //+------------------------------------------------------------------+
Aqui, a compilação do terminal é verificada e, se estiver abaixo de 3260, é retornado o texto de que essa propriedade não é suportada nesta versão. Caso contrário, para MQL5, uma descrição da propriedade é exibida, e, para MQL4, um texto indicando que a propriedade não é suportada é exibido.
Método que retorna uma descrição da taxa de swap acumulado para o dia da semana especificado:
//+------------------------------------------------------------------+ //| 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) ); } //+------------------------------------------------------------------+
Aqui: primeiro obtemos o valor da propriedade para o dia da semana passado para o método, depois retornamos uma descrição textual do valor da propriedade (se o valor for 0, 1 ou 3) ou geramos um valor double convertido em texto com três casas decimais. (Na realidade, apenas o tempo e os testes mostrarão quantas casas decimais exibir).
Para salvar objetos em arquivos, os objetos gráficos (e todos os objetos de biblioteca no futuro) têm uma estrutura de propriedades de objetos. Todas as propriedades são armazenadas na estrutura e, em seguida, essa estrutura é salva em um arquivo. Da mesma forma, a restauração das propriedades do objeto é realizada mediante a leitura do arquivo.
Como temos novas propriedades para objetos gráficos, precisamos adicioná-los à estrutura.
No arquivo \MQL5\Include\DoEasy\Objects\Graph\GCnvElement.mqh, adicionamos suas novas propriedades à estrutura do objeto:
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
No método que cria a estrutura do objeto, vamos adicionar um registro das propriedades do objeto aos campos da estrutura:
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
No método que cria um objeto a partir de uma estrutura, adicionamos a leitura desde os campos da estrutura dos valores das propriedades do 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
Agora todos os objetos gráficos serão salvos corretamente em um arquivo e restaurados a partir dele quando começarmos a salvar as propriedades dos objetos da biblioteca em arquivos.
Vamos fazer uma pequena modificação na classe base dos objetos WinForms no arquivo \MQL5\Include\DoEasy\Objects\Graph\WForms\WinFormBase.mqh.
Primeiro, precisamos ser capazes de selecionar e classificar esses objetos segundo suas propriedades. Para fazer isso, anexamos o arquivo de classe CSelec ao arquivo:
//+------------------------------------------------------------------+ //| 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" //+------------------------------------------------------------------+
Agora podemos usar a classe CSelect em todas as classes herdadas para selecionar e classificar objetos WinForms segundo suas propriedades.
Depois, com o método que redesenha o objeto, verificamos o sinalizador de redesenho definido no loop de processamento para todos os objetos encaixados, para que eles sejam realmente redesenhados apenas se esse sinalizador estiver definido. Se não for assim, não precisamos redesenhar os objetos encaixados, por exemplo, para evitar itens indesejados ao fazer alterações nas propriedades do objeto painel. Quando o sinalizador de redesenho está desativado, os objetos encaixados no contêiner não precisam ser realmente redesenhados, uma vez que suas propriedades são alteradas em outros métodos, e aqui ocorre apenas o redesenho real de todos os objetos.
Assim, verificamos o sinalizador, e fazemos uma chamada para redesenhar objetos somente se o sinalizador estiver definido:
//+------------------------------------------------------------------+ //| 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()); } //+------------------------------------------------------------------+
Modificamos a classe do objeto WinForms Painel no arquivo \MQL5\Include\DoEasy\Objects\Graph\WForms\Containers\Panel.mqh.
Na seção privada da classe, declaramos um método para calcular as coordenadas de encaixe de objetos Dock:
//--- Set the underlay as a coordinate system zero void SetUnderlayAsBase(void); //--- Calculate Dock objects' binding coordinates void CalculateCoords(CArrayObj *list); protected:
Hoje não estaremos implementando este método, então simplesmente escreveremos um método vazio fora do corpo da classe:
//+------------------------------------------------------------------+ //| Calculate Dock objects' binding coordinates | //+------------------------------------------------------------------+ void CPanel::CalculateCoords(CArrayObj *list) { } //+------------------------------------------------------------------+
Trataremos de sua implementação após a criação da classe do objeto WinForms "Rótulo" (Label), para, desse modo, visualizar a ordenação dos objetos quando eles são vinculados de acordo com o valor da propriedade Dock e processar o correto movimento dos objetos associados a seus contêineres, por sua vez, vinculados ao contêiner principal, ao painel.
Da seção protegida da classe, removeremos a declaração de métodos que retornam o valor máximo dos limites (largura e altura) do objeto Dock antes de ultrapassar o contêiner:
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
Também removeremos a implementação desses métodos escritos fora do corpo da classe.
Agora não há necessidade desses métodos, pois podemos usar a classe de biblioteca CSelect para encontrar os valores desejados.
Na seção pública da classe, declaramos um método que retorna a lista de objetos anexados com o tipo WinForms base e acima:
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);
O objeto painel ainda deve ser criado sem uma moldura. Se o painel precisar dela, ela sempre poderá ser adicionada depois de criar o objeto. Portanto, nos construtores de classe, definimos o tipo da moldura 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); } //+------------------------------------------------------------------+
No último construtor, além de definir o tipo de moldura, também removemos sua criação:
this.Initialize(); this.DrawFormFrame(this.FrameWidthTop(),this.FrameWidthBottom(),this.FrameWidthLeft(),this.FrameWidthRight(),this.ColorFrame(),this.Opacity(),this.BorderStyle()); if(this.CreateUnderlayObj()) this.SetUnderlayAsBase();
No método que posiciona os objetos inseridos segundo seu encaixe Dock, não processamos antes a localização dos objetos quando o sinalizador do contêiner (do painel) para redimensioná-lo automaticamente consoante seu interior não era definido. Hoje os manipuladores do modo de encaixe serão copiados dos manipuladores do painel fixo. Mas vamos acrescentar uma verificação preliminar da presença do substrato, a lista de objetos agora será obtida por um novo método, que retorna uma lista de objetos WinForms apenas. Se o modo de autodimensionamento do painel estiver ativado, primeiro redimensionamos o painel para seu tamanho original e, em seguida, chamamos o método que ajusta o tamanho do painel consoante seu conteúdo. Depois disso, em um loop, vamos processar os modos de encaixe de todos os objetos inseridos e reajustamos as dimensões do painel em função dos objetos alterados dentro dele:
//+------------------------------------------------------------------+ //| 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; } //+------------------------------------------------------------------+
Aqui poderíamos fazer tudo em um loop, ou seja, sem nos dividirmos em dois blocos com dois loops separados e idênticos, e além disso, verificar o sinalizador de redimensionamento automático do painel. Isto reduzirá o código para a metade. Mas por enquanto, vamos deixar assim caso tenhamos que modificar ainda mais o bloco de código com o processamento do sinalizador de redimensionamento automático do painel. E somente quando tudo estiver depurado e estiver claro que não serão necessárias mais alterações e melhorias, otimizaremos o código desse método.
Como agora podemos obter os dados necessários usando a classe CSelect, o método que ajusta o tamanho do elemento ao seu interior também foi modificado:
//+------------------------------------------------------------------+ //| 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) ); } //+------------------------------------------------------------------+
A lógica do método é descrita nos comentários ao código. Veja que, ao obter a coordenada mínima ao longo do eixo Y, notei uma coisa estranha que acontece de vez em quando com o valor do valor recebido... Quando o objeto é construído pela primeira vez, a coordenada máxima é retornada com o valor máximo e a coordenada mínima com o valor mínimo. E aqui está tudo correto. Mas depois de reconstruir os objetos, a coordenada máxima é retornada com o valor mínimo e a mínima com o máximo. Ainda não consegui descobrir os motivos desse comportamento, e tive que fazer uma escolha entre dois valores, isto é: ao solicitar o valor máximo, obtemos o máximo de dois, e, ao solicitar o valor mínimo, obtemos o mínimo de dois.
Por que calculamos deslocamentos em X e Y? A questão é que quando os objetos são encaixados na parte direita ou inferior do painel, eles são construídos ou a partir da borda direita do painel ou a partir da parte inferior. Desse modo, eles podem ultrapassar o painel à esquerda ou acima. E como a origem das coordenadas do painel (e qualquer outro elemento gráfico) começa no canto superior esquerdo, quando o tamanho do painel aumenta consoante o tamanho de todos os objetos dentro dele (sendo que eles ultrapassam as bordas do painel desde a esquerda ou desde acima), o painel aumenta para a direita ou para baixo. Assim, o painel terá dimensões em função de todos os objetos dentro dele, mas a origem do painel não corresponderá à origem visível de todos os objetos. E é por isso que esses objetos precisarão ser deslocados para a direita ou para baixo de acordo com a quantidade calculada, dependendo do modo de encaixe do objeto.
Método que retorna uma lista de objetos encaixados do tipo WinForms base e 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); } //+------------------------------------------------------------------+
O método simplesmente retorna uma lista contendo apenas objetos do tipo WinForms base ou superior, ou seja, o objeto WinForms base ou seus descendentes. Os objetos são selecionados por tipo mediante a filtragem da lista geral de objetos encaixados no painel usando a classe CSelect.
Otimizamos e eliminamos erros de lógica nos métodos de criação de objetos WinForms "Painel" da classe CGraphElementsCollection no arquivo \MQL5\Include\DoEasy\Collections\GraphElementsCollection.mqh.
Cometemos erros porque a moldura do objeto foi desenhada duas vezes e a cor da moldura não foi definida. Cabe dizer que as dimensões da moldura do painel não poderiam ser definidas se fossem passadas para métodos como um valor padrão de -1. Não verificamos esse valor, por isso foi definido em vez de definir os valores padrão dos tamanhos de borda com um valor de -1.
Todos os métodos para criar painéis foram alterados da seguinte forma:
//--- 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(); }
Definimos a cor da moldura com a mesma cor do plano de fundo do painel (com a primeira no gradiente ou a única). Ao definir o tamanho da moldura, verificamos qual valor é passado para o método e, se -1, definimos o valor padrão escrito na macro-substituição DEF_FRAME_WIDTH_SIZE no arquivo Defines.mqh. Se o sinalizador de redesenho estiver definido, pintamos o painel com a cor de fundo ou com um gradiente e desenhamos um retângulo de contorno. Nesse caso, a moldura é desenhada no método virtual Erase() da classe CPanel.
Tais melhorias foram feitas em todos os métodos de criação de painéis e podem ser encontradas nos arquivos anexos ao artigo.
Teste
Para testar, vamos pegar o Expert Advisor do artigo anterior e salvá-lo na nova pasta \MQL5\Experts\TestDoEasy\Part106\ com o novo nome TestDoEasyPart106.mq5.
O que vamos testar? Como agora os objetos paineis encaixados no contêiner são criados sem uma moldura por padrão, após sua criação, no manipulador OnInit(), especificamos a largura/tipo de moldura para cada um deles. A cor de cada painel será mais clara de acordo com o valor calculado a partir do índice de loop (cor de fundo clareada com uma intensidade igual ao índice do loop * 4). Isso é necessário para ver claramente a mudança na disposição dos painéis no contêiner ao alterar o método de encaixe:
//+------------------------------------------------------------------+ //| 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); } //+------------------------------------------------------------------+
Compilamos o Expert Advisor e o iniciamos no gráfico:
O painel se ajusta, desta forma, às dimensões gerais dos objetos encaixados nele, os diferentes métodos de encaixe funcionam corretamente e o painel muda suas dimensões corretamente. Quando objetos com diferentes métodos de encaixe são posicionados, não são colocados como desejado, porque, primeiro, não devem ser associados às bordas do painel e, segundo, cada objeto que se segue na lista deve ser vinculado às bordas do anterior no caso de ter o mesmo método de encaixe. Sobretudo, o último objeto é esticado em toda a largura e altura do contêiner, mas deve ser delimitado pelas bordas internas dos objetos já encaixados nas bordas do painel, ou seja, deve estar posicionado dentro do espaço livre entre elas. Mas este será o comportamento dos objetos encaixados que estaremos fazendo em artigos posteriores.
O que virá a seguir?
No próximo artigo, continuaremos trabalhando no objeto "Painel" e começaremos a desenvolver novos controles, em especial, o objeto WinForms rótulo.
*Artigos desta série:
DoEasy. Controles (Parte 1): Primeiros passos
DoEasy. Controles (Parte 2): Continuamos trabalhando na classe CPanel
DoEasy. Controles (Parte 3): Criando controles vinculados
DoEasy. Controles (Parte 4): Controle "Painel", parâmetros Padding e Dock
DoEasy. Controles (Parte 5): Objeto base WinForms, controle Painel, parâmetro AutoSize
Traduzido do russo pela MetaQuotes Ltd.
Artigo original: https://www.mql5.com/ru/articles/10989
- Aplicativos de negociação gratuitos
- 8 000+ sinais para cópia
- Notícias econômicas para análise dos mercados financeiros
Você concorda com a política do site e com os termos de uso