English Русский 中文 Español Deutsch 日本語
preview
DoEasy. Controles (Parte 10): Objetos WinForms, dando vida à interface

DoEasy. Controles (Parte 10): Objetos WinForms, dando vida à interface

MetaTrader 5Exemplos | 20 outubro 2022, 08:45
168 0
Artyom Trishkin
Artyom Trishkin

Conteúdo


Ideia

Todos os objetos WinForms que criamos até agora são essencialmente apenas uma imagem estática, apenas uns «acessórios de teatro» que podem ser movidos pela tela. Isso carece de sentido, portanto está na hora de dar vida à interface gráfica, e criar funcionalidades para a interação de objetos com o usuário e com outros objetos.

Os objetos WinForms que já criamos são tanto unidades independentes para construir interfaces gráficas como componentes para elaborar objetos mais complexos. E para que objetos mais complexos funcionem corretamente, já precisamos que os objetos interajam entre si e interajam com o usuário. Hoje vamos começar a plasmar isso e criar um componente visual para quando os objetos interagem com o mouse. Nos próximos artigos, elaboraremos a funcionalidade de evento desses objetos e continuaremos criando novos objetos WinForms da biblioteca.

Cada objeto GUI ativo que é capaz de interagir com o mouse e que tem um sinalizador disponível deve avisar ao usuário que ele poder interagir. Exemplifico isso com um botão simples. Ao passar o mouse sobre ele, o botão mudará ligeiramente de cor, informando ao usuário que está ativo e pronto para interagir. Quando pressionado, ele mudará de cor novamente, mas não será ativado. A ativação ocorrerá quando o botão do mouse é liberado dentro do objeto de interação. Se você clicar no objeto e, sem soltá-lo, mover o cursor para longe do objeto e soltar, esse objeto não deverá ativar-se, ou seja, ele retornará ao estado que tinha antes do clique. É assim que todos os objetos no sistema operacional Windows se comportam. E aqui geraremos o mesmo comportamento par os objetos WinForms.

Depois de criar o componente visual, começaremos a criar a funcionalidade de evento dos objetos WinForms, e isso nos permitirá criar objetos mais complexos a partir de objetos simples. O próprio objeto processará e analisará os eventos dos objetos simples (que compõem o objeto complexo), sua aparência mudará em conformidade e, como resultado, o objeto enviará o evento que ocorreu no respectivo objeto para o gráfico do programa de controle.

Como agora o objeto precisará ter várias cores a mais para o mesmo estado (cor base, cor quando o cursor do mouse passar sobre ele, cor quando o botão do objeto for pressionado), escreveremos essas novas cores para diferentes propriedades do objeto. Se novas cores estiverem presentes nas propriedades do objeto, mais tarde será mais fácil criar editores visuais para esses objetos, porque, assim, todas as propriedades podem ser exibidas e processadas em um painel. Se novas cores complementares não forem gravadas nas propriedades do objeto, para exibi-las, teremos que escrevê-las manualmente para cada objeto, o que não é o ideal.


Modificando as classes da biblioteca

No arquivo \MQL5\Include\DoEasy\Defines.mqh, vamos adicionar novas macro-substituições para as cores padrão dos diferentes estados dos objetos WinForms:

//--- Canvas parameters
#define PAUSE_FOR_CANV_UPDATE          (16)                       // Canvas update frequency
#define CLR_CANV_NULL                  (0x00FFFFFF)               // Zero for the canvas with the alpha channel
#define CLR_DEF_FORE_COLOR             (C'0x2D,0x43,0x48')        // Default color for texts of objects on canvas
#define CLR_DEF_FORE_COLOR_MOUSE_DOWN  (C'0x0E,0x11,0x98')        // Default color for texts of objects on canvas when clicking the mouse on the control
#define CLR_DEF_FORE_COLOR_MOUSE_OVER  (C'0x14,0x67,0xF1')        // Default color for texts of objects on canvas when hovering the mouse over the control
#define CLR_DEF_FORE_COLOR_OPACITY     (255)                      // Default color non-transparency for canvas object texts
#define CLR_DEF_BORDER_COLOR           (C'0x2D,0x43,0x48')        // Default color for object frames on canvas
#define CLR_DEF_BORDER_MOUSE_DOWN      (C'0x61,0x88,0xC9')        // Default color for object frames on canvas when clicking the mouse on the control
#define CLR_DEF_BORDER_MOUSE_OVER      (C'0x93,0xAD,0xC8')        // Default color for object frames on canvas when hovering the mouse over the control
#define CLR_DEF_BORDER_COLOR_OPACITY   (255)                      // Default color non-transparency for canvas object frames
#define CLR_DEF_BORDER_COLOR_DARKNESS  (-2.0)                     // Default color opacity for canvas object frames (when using the background color)
#define CLR_DEF_FRAME_GBOX_COLOR       (C'0xDC,0xDC,0xDC')        // Default color for GroupBox object frames on canvas
#define CLR_DEF_OPACITY                (200)                      // Default color non-transparency for canvas objects
#define CLR_DEF_SHADOW_COLOR           (C'0x6B,0x6B,0x6B')        // Default color for canvas object shadows
#define CLR_DEF_SHADOW_OPACITY         (127)                      // Default color opacity for canvas objects
#define DEF_SHADOW_BLUR                (4)                        // Default blur for canvas object shadows

#define CLR_DEF_CHECK_BACK_COLOR       (C'0xFF,0xFF,0xFF')        // Color of control checkbox background
#define CLR_DEF_CHECK_BACK_OPACITY     (255)                      // Opacity of the control checkbox background color
#define CLR_DEF_CHECK_BACK_MOUSE_DOWN  (C'0xC0,0xDC,0xF3')        // Color of control checkbox background when clicking on the control
#define CLR_DEF_CHECK_BACK_MOUSE_OVER  (C'0xD8,0xE6,0xF2')        // Color of control checkbox background when hovering the mouse over the control
#define CLR_DEF_CHECK_BORDER_COLOR     (C'0x2D,0x43,0x48')        // Color of control checkbox frame
#define CLR_DEF_CHECK_BORDER_OPACITY   (255)                      // Opacity of the control checkbox frame color
#define CLR_DEF_CHECK_BORDER_MOUSE_DOWN (C'0x00,0x54,0x99')       // Color of control checkbox frame when clicking on the control
#define CLR_DEF_CHECK_BORDER_MOUSE_OVER (C'0x00,0x78,0xD7')       // Color of control checkbox frame when hovering the mouse over the control
#define CLR_DEF_CHECK_FLAG_COLOR       (C'0x04,0x7B,0x0D')        // Color of control checkbox
#define CLR_DEF_CHECK_FLAG_OPACITY     (255)                      // Opacity of the control checkbox color
#define CLR_DEF_CHECK_FLAG_MOUSE_DOWN  (C'0x00,0x54,0x99')        // Color of control checkbox when clicking on the control
#define CLR_DEF_CHECK_FLAG_MOUSE_OVER  (C'0x00,0x78,0xD7')        // Color of control checkbox when hovering the mouse over the control

#define CLR_DEF_CONTROL_STD_BACK_COLOR (C'0xF0,0xF0,0xF0')        // Standard controls background color
#define CLR_DEF_CONTROL_STD_MOUSE_DOWN (C'0xC0,0xDC,0xF3')        // Color of standard control background when clicking on the control
#define CLR_DEF_CONTROL_STD_MOUSE_OVER (C'0xD8,0xE6,0xF2')        // Color of standard controls background when hovering the mouse over the control
#define CLR_DEF_CONTROL_STD_OPACITY    (255)                      // Opacity of standard controls background color

#define CLR_DEF_CONTROL_STD_BACK_COLOR_ON (C'0xC9,0xDE,0xD0')     // Background color of standard controls which are on
#define CLR_DEF_CONTROL_STD_BACK_DOWN_ON (C'0xA6,0xC8,0xB0')      // Color of standard control background when clicking on the control when it is on
#define CLR_DEF_CONTROL_STD_BACK_OVER_ON (C'0xB8,0xD3,0xC0')      // Color of standard control background when hovering the mouse over the control when it is on

#define DEF_FONT                       ("Calibri")                // Default font
#define DEF_FONT_SIZE                  (8)                        // Default font size
#define DEF_CHECK_SIZE                 (12)                       // Verification flag default size
#define OUTER_AREA_SIZE                (16)                       // Size of one side of the outer area around the form workspace
#define DEF_FRAME_WIDTH_SIZE           (3)                        // Default form/panel/window frame width
//--- Graphical object parameters

Afinal, o ForeColor para a caixa de seleção ainda é desnecessário. Portanto, vamos substituí-lo por BorderColor, que é a cor da moldura da caixa de seleção. Desse modo, ela mudará junto com a cor de fundo ao interagir com o mouse. E usamos a cor da caixa de seleção como ForeColor.

A biblioteca rastreará outro estado do mouse em relação à forma, especificamente quando o cursor estiver na área ativa e o botão do mouse for liberado após clicar. Vamos escrever o novo estado na lista de possíveis estados do mouse em relação à forma:

//+------------------------------------------------------------------+
//| The list of possible mouse states relative to the form           |
//+------------------------------------------------------------------+
enum ENUM_MOUSE_FORM_STATE
  {
   MOUSE_FORM_STATE_NONE = 0,                         // Undefined state
//--- Outside the form
   MOUSE_FORM_STATE_OUTSIDE_FORM_NOT_PRESSED,         // The cursor is outside the form, the mouse buttons are not clicked
   MOUSE_FORM_STATE_OUTSIDE_FORM_PRESSED,             // The cursor is outside the form, the mouse button (any) is clicked
   MOUSE_FORM_STATE_OUTSIDE_FORM_WHEEL,               // The cursor is outside the form, the mouse wheel is being scrolled
//--- Within the form
   MOUSE_FORM_STATE_INSIDE_FORM_NOT_PRESSED,          // The cursor is inside the form, no mouse buttons are clicked
   MOUSE_FORM_STATE_INSIDE_FORM_PRESSED,              // The cursor is inside the form, the mouse button (any) is clicked
   MOUSE_FORM_STATE_INSIDE_FORM_WHEEL,                // The cursor is inside the form, the mouse wheel is being scrolled
//--- Within the window header area
   MOUSE_FORM_STATE_INSIDE_ACTIVE_AREA_NOT_PRESSED,   // The cursor is inside the active area, the mouse buttons are not clicked
   MOUSE_FORM_STATE_INSIDE_ACTIVE_AREA_PRESSED,       // The cursor is inside the active area,  any mouse button is clicked
   MOUSE_FORM_STATE_INSIDE_ACTIVE_AREA_WHEEL,         // The cursor is inside the active area, the mouse wheel is being scrolled
   MOUSE_FORM_STATE_INSIDE_ACTIVE_AREA_RELEASED,      // The cursor is inside the active area, left mouse button is released
//--- Within the window scrolling area
   MOUSE_FORM_STATE_INSIDE_SCROLL_AREA_NOT_PRESSED,   // The cursor is within the window scrolling area, the mouse buttons are not clicked
   MOUSE_FORM_STATE_INSIDE_SCROLL_AREA_PRESSED,       // The cursor is within the window scrolling area, the mouse button (any) is clicked
   MOUSE_FORM_STATE_INSIDE_SCROLL_AREA_WHEEL,         // The cursor is within the window scrolling area, the mouse wheel is being scrolled
  };
//+------------------------------------------------------------------+

Acompanhando este estado, poderemos determinar o momento em que o botão do mouse foi liberado, e este momento será a chave para alterar o estado do objeto gráfico.

Para reagir à interação com o mouse, precisamos saber qual evento do mouse ocorreu. Posteriormente, com base no processamento deste evento, enviaremos ao programa para que o usuário da biblioteca possa processar este evento em seu programa.

Vamos criar uma nova enumeração com uma lista de eventos do mouse:

//+------------------------------------------------------------------+
//| List of possible mouse events                                    |
//+------------------------------------------------------------------+
enum ENUM_MOUSE_EVENT
  {
   MOUSE_EVENT_NO_EVENT = CHART_OBJ_EVENTS_NEXT_CODE, // No event
//---
   MOUSE_EVENT_OUTSIDE_FORM_NOT_PRESSED,              // The cursor is outside the form, the mouse buttons are not clicked
   MOUSE_EVENT_OUTSIDE_FORM_PRESSED,                  // The cursor is outside the form, the mouse button (any) is clicked
   MOUSE_EVENT_OUTSIDE_FORM_WHEEL,                    // The cursor is outside the form, the mouse wheel is being scrolled
//--- Within the form
   MOUSE_EVENT_INSIDE_FORM_NOT_PRESSED,               // The cursor is inside the form, no mouse buttons are clicked
   MOUSE_EVENT_INSIDE_FORM_PRESSED,                   // The cursor is inside the form, the mouse button (any) is clicked
   MOUSE_EVENT_INSIDE_FORM_WHEEL,                     // The cursor is inside the form, the mouse wheel is being scrolled
//--- Within the window header area
   MOUSE_EVENT_INSIDE_ACTIVE_AREA_NOT_PRESSED,        // The cursor is inside the active area, the mouse buttons are not clicked
   MOUSE_EVENT_INSIDE_ACTIVE_AREA_PRESSED,            // The cursor is inside the active area, any mouse button is clicked
   MOUSE_EVENT_INSIDE_ACTIVE_AREA_WHEEL,              // The cursor is inside the active area, the mouse wheel is being scrolled
   MOUSE_EVENT_INSIDE_ACTIVE_AREA_RELEASED,           // The cursor is inside the active area, left mouse button is released
//--- Within the window scrolling area
   MOUSE_EVENT_INSIDE_SCROLL_AREA_NOT_PRESSED,        // The cursor is within the window scrolling area, the mouse buttons are not clicked
   MOUSE_EVENT_INSIDE_SCROLL_AREA_PRESSED,            // The cursor is within the window scrolling area, the mouse button (any) is clicked
   MOUSE_EVENT_INSIDE_SCROLL_AREA_WHEEL,              // The cursor is within the window scrolling area, the mouse wheel is being scrolled
  };
#define ENUM_MOUSE_EVENT_NEXT_CODE  (MOUSE_EVENT_INSIDE_SCROLL_AREA_WHEEL+1)  // The code of the next event after the last chart event code
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| Data for handling graphical elements                             |
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| List of possible graphical object events                         |
//+------------------------------------------------------------------+
enum ENUM_GRAPH_OBJ_EVENT
  {
   GRAPH_OBJ_EVENT_NO_EVENT = ENUM_MOUSE_EVENT_NEXT_CODE,// No event
   GRAPH_OBJ_EVENT_CREATE,                            // "Creating a new graphical object" event
   GRAPH_OBJ_EVENT_CHANGE,                            // "Changing graphical object properties" event
   GRAPH_OBJ_EVENT_RENAME,                            // "Renaming graphical object" event
   GRAPH_OBJ_EVENT_DELETE,                            // "Removing graphical object" event
   GRAPH_OBJ_EVENT_DEL_CHART,                         // "Removing a graphical object together with the chart window" event
  };
#define GRAPH_OBJ_EVENTS_NEXT_CODE  (GRAPH_OBJ_EVENT_DEL_CHART+1) // The code of the next event after the last graphical object event code
//+------------------------------------------------------------------+

Como escrevemos uma nova enumeração de eventos, "comprimindo-a" entre a lista de eventos do gráfico e a lista de eventos do objeto gráfico, o valor inicial do primeiro evento do objeto gráfico agora será calculado como o último evento do mouse + 1.

Vamos adicionar novas propriedades à lista de propriedades inteiras do elemento gráfico:

//+------------------------------------------------------------------+
//| Integer properties of the graphical element on the canvas        |
//+------------------------------------------------------------------+
enum ENUM_CANV_ELEMENT_PROP_INTEGER
  {
   CANV_ELEMENT_PROP_ID = 0,                          // Element ID
   CANV_ELEMENT_PROP_TYPE,                            // Graphical element type
   //---...
   //---...
   CANV_ELEMENT_PROP_FORE_COLOR,                      // Default text color for all control objects
   CANV_ELEMENT_PROP_FORE_COLOR_OPACITY,              // Default text color opacity for all control objects
   CANV_ELEMENT_PROP_FORE_COLOR_MOUSE_DOWN,           // Default control text color when clicking on the control
   CANV_ELEMENT_PROP_FORE_COLOR_MOUSE_OVER,           // Default control text color when hovering the mouse over the control
   CANV_ELEMENT_PROP_FORE_COLOR_TOGGLE,               // Text color of the control which is on
   CANV_ELEMENT_PROP_FORE_COLOR_TOGGLE_MOUSE_DOWN,    // Default control text color when clicking on the control which is on
   CANV_ELEMENT_PROP_FORE_COLOR_TOGGLE_MOUSE_OVER,    // Default control text color when hovering the mouse over the control which is on
   CANV_ELEMENT_PROP_BACKGROUND_COLOR,                // Control background color
   CANV_ELEMENT_PROP_BACKGROUND_COLOR_OPACITY,        // Opacity of control background color
   CANV_ELEMENT_PROP_BACKGROUND_COLOR_MOUSE_DOWN,     // Control background color when clicking on the control
   CANV_ELEMENT_PROP_BACKGROUND_COLOR_MOUSE_OVER,     // Control background color when hovering the mouse over the control
   CANV_ELEMENT_PROP_BACKGROUND_COLOR_TOGGLE,           // Background color of the control which is on
   CANV_ELEMENT_PROP_BACKGROUND_COLOR_TOGGLE_MOUSE_DOWN,// Control background color when clicking on the control which is on
   CANV_ELEMENT_PROP_BACKGROUND_COLOR_TOGGLE_MOUSE_OVER,// Control background color hovering the mouse over control which is on
   CANV_ELEMENT_PROP_BOLD_TYPE,                       // Font width type
   CANV_ELEMENT_PROP_BORDER_STYLE,                    // Control frame style
   //---...
   //---...
   CANV_ELEMENT_PROP_CHECK_STATE,                     // Status of a control having a checkbox
   CANV_ELEMENT_PROP_AUTOCHECK,                       // Auto change flag status when it is selected
   CANV_ELEMENT_PROP_BUTTON_TOGGLE,                   // Toggle flag of the control featuring a button
   CANV_ELEMENT_PROP_BUTTON_STATE,                    // Status of the Toggle control featuring a button
   CANV_ELEMENT_PROP_CHECK_BACKGROUND_COLOR,          // Color of control checkbox background
   CANV_ELEMENT_PROP_CHECK_BACKGROUND_COLOR_OPACITY,  // Opacity of the control checkbox background color
   //---...
   //---...
   CANV_ELEMENT_PROP_CHECK_FLAG_COLOR_MOUSE_DOWN,     // Color of control checkbox when clicking on the control
   CANV_ELEMENT_PROP_CHECK_FLAG_COLOR_MOUSE_OVER,     // Color of control checkbox when hovering the mouse over the control
   
  };
#define CANV_ELEMENT_PROP_INTEGER_TOTAL (81)          // Total number of integer properties
#define CANV_ELEMENT_PROP_INTEGER_SKIP  (0)           // Number of integer properties not used in sorting
//+------------------------------------------------------------------+

e aumentamos o número total de propriedades de 71 para 81.

E adicionamos estas novas propriedades à lista de possíveis critérios para ordenar elementos gráficos na tela:

//+------------------------------------------------------------------+
//| Possible sorting criteria of graphical elements on the canvas    |
//+------------------------------------------------------------------+
#define FIRST_CANV_ELEMENT_DBL_PROP  (CANV_ELEMENT_PROP_INTEGER_TOTAL-CANV_ELEMENT_PROP_INTEGER_SKIP)
#define FIRST_CANV_ELEMENT_STR_PROP  (CANV_ELEMENT_PROP_INTEGER_TOTAL-CANV_ELEMENT_PROP_INTEGER_SKIP+CANV_ELEMENT_PROP_DOUBLE_TOTAL-CANV_ELEMENT_PROP_DOUBLE_SKIP)
enum ENUM_SORT_CANV_ELEMENT_MODE
  {
//--- Sort by integer properties
   SORT_BY_CANV_ELEMENT_ID = 0,                       // Sort by element ID
   SORT_BY_CANV_ELEMENT_TYPE,                         // Sort by graphical element type
   //---...
   //---...
   SORT_BY_CANV_ELEMENT_FORE_COLOR,                   // Sort by default text color for all control objects
   SORT_BY_CANV_ELEMENT_FORE_COLOR_OPACITY,           // Sort by default text color opacity for all control objects
   SORT_BY_CANV_ELEMENT_FORE_COLOR_MOUSE_DOWN,        // Sort by control text color when clicking on the control
   SORT_BY_CANV_ELEMENT_FORE_COLOR_MOUSE_OVER,        // Sort by control text color when hovering the mouse over the control
   SORT_BY_CANV_ELEMENT_FORE_COLOR_TOGGLE,            // Sort by control text color when the control is on
   SORT_BY_CANV_ELEMENT_FORE_COLOR_TOGGLE_MOUSE_DOWN, // Sort by the default control text color when clicking on the control while it is on
   SORT_BY_CANV_ELEMENT_FORE_COLOR_TOGGLE_MOUSE_OVER, // Sort by the default control text color when hovering the mouse over the control while it is on
   SORT_BY_CANV_ELEMENT_BACKGROUND_COLOR,             // Sort by control background text color
   SORT_BY_CANV_ELEMENT_BACKGROUND_COLOR_OPACITY,     // Sort by control background color opacity
   SORT_BY_CANV_ELEMENT_BACKGROUND_COLOR_MOUSE_DOWN,  // Sort by control background text color when clicking on the control
   SORT_BY_CANV_ELEMENT_BACKGROUND_COLOR_MOUSE_OVER,  // Sort by control background text color when hovering the mouse over the control
   SORT_BY_CANV_ELEMENT_BACKGROUND_COLOR_TOGGLE,           // Sort by control background color when the control is on
   SORT_BY_CANV_ELEMENT_BACKGROUND_COLOR_TOGGLE_MOUSE_DOWN,// Sort by control background color when clicking on the control while it is on
   SORT_BY_CANV_ELEMENT_BACKGROUND_COLOR_TOGGLE_MOUSE_OVER,// Sort by control background color when hovering the mouse over the control while it is on
   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_CHECK_STATE,                  // Sort by status of a control having a checkbox
   SORT_BY_CANV_ELEMENT_AUTOCHECK,                    // Sort by auto change flag status when it is selected
   SORT_BY_CANV_ELEMENT_BUTTON_TOGGLE,                // Sort by the Toggle flag of the control featuring a button
   SORT_BY_CANV_ELEMENT_BUTTON_STATE,                 // Sort by the status of the Toggle control featuring a button  
   SORT_BY_CANV_ELEMENT_CHECK_BACKGROUND_COLOR,       // Sort by color of control checkbox background
   SORT_BY_CANV_ELEMENT_CHECK_BACKGROUND_COLOR_OPACITY,   // Sort by opacity of control checkbox background color
   //---...
   //---...
   SORT_BY_CANV_ELEMENT_CHECK_FLAG_COLOR_MOUSE_DOWN,  // Sort by color of control checkbox when clicking on the control
   SORT_BY_CANV_ELEMENT_CHECK_FLAG_COLOR_MOUSE_OVER,  // Sort by color of control checkbox when hovering the mouse over 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
   SORT_BY_CANV_ELEMENT_TEXT,                         // Sort by graphical element text
  };
//+------------------------------------------------------------------+

Agora poderemos classificar e filtrar listas e selecionar objetos nos guiando por essas novas propriedades.


Inserimos os índices das novas mensagens da biblioteca no arquivo \MQL5\Include\DoEasy\Data.mqh:

   MSG_CANV_ELEMENT_PROP_FORE_COLOR,                  // Default text color for all control objects
   MSG_CANV_ELEMENT_PROP_FORE_COLOR_OPACITY,          // Default text color opacity for all control objects
   MSG_CANV_ELEMENT_PROP_FORE_COLOR_MOUSE_DOWN,       // Default control text color when clicking on the control
   MSG_CANV_ELEMENT_PROP_FORE_COLOR_MOUSE_OVER,       // Default control text color when hovering the mouse over the control
   MSG_CANV_ELEMENT_PROP_FORE_COLOR_TOGGLE,           // Text color of the control which is on
   MSG_CANV_ELEMENT_PROP_FORE_COLOR_TOGGLE_MOUSE_DOWN,// Default control text color when clicking on the control which is on
   MSG_CANV_ELEMENT_PROP_FORE_COLOR_TOGGLE_MOUSE_OVER,// Default control text color when hovering the mouse over the control which is on
   MSG_CANV_ELEMENT_PROP_BACKGROUND_COLOR,            // Control background color
   MSG_CANV_ELEMENT_PROP_BACKGROUND_COLOR_OPACITY,    // Opacity of control background color
   MSG_CANV_ELEMENT_PROP_BACKGROUND_COLOR_MOUSE_DOWN, // Control background color when clicking on the control
   MSG_CANV_ELEMENT_PROP_BACKGROUND_COLOR_MOUSE_OVER, // Control background color when hovering the mouse over the control
   MSG_CANV_ELEMENT_PROP_BACKGROUND_COLOR_TOGGLE,           // Background color of the control which is on
   MSG_CANV_ELEMENT_PROP_BACKGROUND_COLOR_TOGGLE_MOUSE_DOWN,// Control background color when clicking on the control which is on
   MSG_CANV_ELEMENT_PROP_BACKGROUND_COLOR_TOGGLE_MOUSE_OVER,// Control background color when hovering the mouse over control which is on
   MSG_CANV_ELEMENT_PROP_BOLD_TYPE,                   // Font width type
   MSG_CANV_ELEMENT_PROP_BORDER_STYLE,                // Control frame style

...

   MSG_CANV_ELEMENT_PROP_CHECK_STATE,                 // Status of a control having a checkbox
   MSG_CANV_ELEMENT_PROP_AUTOCHECK,                   // Auto change flag status when it is selected
   MSG_CANV_ELEMENT_PROP_BUTTON_TOGGLE,               // Toggle flag of the control featuring a button
   MSG_CANV_ELEMENT_PROP_BUTTON_STATE,                // Status of the Toggle control featuring a button
   MSG_CANV_ELEMENT_PROP_CHECK_BACKGROUND_COLOR,      // Color of control checkbox background
   MSG_CANV_ELEMENT_PROP_CHECK_BACKGROUND_COLOR_OPACITY,// Opacity of the control checkbox background color
   MSG_CANV_ELEMENT_PROP_CHECK_BACKGROUND_COLOR_MOUSE_DOWN,// Color of control checkbox background when clicking on the control
   MSG_CANV_ELEMENT_PROP_CHECK_BACKGROUND_COLOR_MOUSE_OVER,// Color of control checkbox background when hovering the mouse over the control
   MSG_CANV_ELEMENT_PROP_CHECK_FORE_COLOR,            // Color of control checkbox frame
   MSG_CANV_ELEMENT_PROP_CHECK_FORE_COLOR_OPACITY,    // Opacity of the control checkbox frame color
   MSG_CANV_ELEMENT_PROP_CHECK_FORE_COLOR_MOUSE_DOWN, // Color of control checkbox frame when clicking on the control
   MSG_CANV_ELEMENT_PROP_CHECK_FORE_COLOR_MOUSE_OVER, // Color of control checkbox frame when hovering the mouse over the control
   MSG_CANV_ELEMENT_PROP_CHECK_FLAG_COLOR,            // Color of control checkbox
   MSG_CANV_ELEMENT_PROP_CHECK_FLAG_COLOR_OPACITY,    // Opacity of the control checkbox color
   MSG_CANV_ELEMENT_PROP_CHECK_FLAG_COLOR_MOUSE_DOWN, // Color of control checkbox when clicking on the control
   MSG_CANV_ELEMENT_PROP_CHECK_FLAG_COLOR_MOUSE_OVER, // Color of control checkbox when hovering the mouse over the control
   
//--- Real properties of graphical elements

//--- String properties of graphical elements
   MSG_CANV_ELEMENT_PROP_NAME_OBJ,                    // Graphical element object name
   MSG_CANV_ELEMENT_PROP_NAME_RES,                    // Graphical resource name
   MSG_CANV_ELEMENT_PROP_TEXT,                        // Graphical element text


e os textos das mensagens correspondentes aos índices recém-adicionados:

   {"Цвет текста по умолчанию для всех объектов элемента управления","Default text color for all objects in the control"},
   {"Непрозрачность цвета текста по умолчанию для всех объектов элемента управления","Default text color opacity for all objects in the control"},
   {"Цвет текста элемента по умолчанию при нажатии мышки на элемент управления","The default text color of an element when the mouse is pressed on the control"},
   {"Цвет текста элемента по умолчанию при наведении мышки на элемент управления","The default text color of an element when hovering over the control"},
   {"Цвет текста элемента управления в состоянии \"включено\"","The text color of a control in the enabled state"},
   {"Цвет текста элемента управления по умолчанию в состоянии \"включено\" при нажатии мышки на элемент управления","The default text color of the control in the \"On\" state when the mouse is pressed on the control"},
   {"Цвет текста элемента управления по умолчанию в состоянии \"включено\" при наведении мышки на элемент управления","The default text color of a control in the \"On\" state when hovering the mouse over the control"},
   {"Цвет фона элемента управления","Background color of the control"},
   {"Непрозрачность цвета фона элемента управления","Opacity of the control's background color"},
   {"Цвет фона элемента управления при нажатии мышки на элемент управления","Background color of the control when the mouse is clicked on the control"},
   {"Цвет фона элемента управления при наведении мышки на элемент управления","Background color of the control when hovering the mouse over the control"},
   {"Цвет фона элемента управления в состоянии \"включено\"","Background color of the control in the enabled state"},
   {"Цвет фона элемента управления в состоянии \"включено\" при нажатии мышки на элемент управления","The background color of the control in the \"On\" state when the mouse is pressed on the control"},
   {"Цвет фона элемента управления в состоянии \"включено\" при наведении мышки на элемент управления","The background color of a control in the \"On\" state when the mouse is over the control"},
   {"Тип толщины шрифта","Font weight type"},
   {"Стиль рамки элемента управления","Control's border style"},

...

   {"Состояние элемента управления, имеющего флажок проверки","The state of a control that has a checkbox"},
   {"Автоматическое изменение состояния флажка при его выборе","Automatically change the state of the checkbox when it is selected"},
   {"Флаг \"Переключатель\" элемента управления, имеющего кнопку","\"Button-toggle\" flag of a control"},
   {"Состояние элемента управления \"Переключатель\", имеющего кнопку","The \"Toggle-button\" control state"},
   {"Цвет фона флажка проверки элемента управления","The background color of the control's validation checkbox"},
   {"Непрозрачность цвета фона флажка проверки элемента управления","Opacity of the backgroung color of the checkbox control"},
   {"Цвет фона флажка проверки элемента управления при нажатии мышки на элемент управления","The background color of the control's checkbox when the control is pressed with the mouse"},
   {"Цвет фона флажка проверки элемента управления при наведении мышки на элемент управления","The background color of the control's validation checkbox when hovering the mouse over the control"},
   {"Цвет рамки флажка проверки элемента управления","Border color of the checkbox control"},
   {"Непрозрачность цвета рамки флажка проверки элемента управления","Border color opacity of the checkbox control"},
   {"Цвет рамки флажка проверки элемента управления при нажатии мышки на элемент управления","Border color of the checkbox control when the mouse is pressed on the control"},
   {"Цвет рамки флажка проверки элемента управления при наведении мышки на элемент управления","Border color of the checkbox control when hovering the mouse over the control"},
   {"Цвет флажка проверки элемента управления","Control Checkbox Color"},
   {"Непрозрачность цвета флажка проверки элемента управления","Opacity of control's checkbox color"},
   {"Цвет флажка проверки элемента управления при нажатии мышки на элемент управления","Control Checkbox Colorl when the mouse is pressed on the control"},
   {"Цвет флажка проверки элемента управления при наведении мышки на элемент управления","Control Checkbox Colorl when hovering the mouse over the control"},

//--- String properties of graphical elements
   {"Имя объекта-графического элемента","The name of the graphic element object"},
   {"Имя графического ресурса","Image resource name"},
   {"Текст графического элемента","Text of the graphic element"},


Ao passar o mouse ou clicar com o mouse sobre um objeto, precisamos alterar sua cor de fundo. Mas, para restaurar a cor original, precisamos lembrá-la na primeira vez que o objeto é construído e, em seguida, pegar a cor desejada das cores pré-salvas.

Como a cor de fundo de um objeto gráfico pode ser gradiente, ou seja, usar um array de cores, precisamos não apenas de uma variável para armazenar a cor inicial, mas de um array. Ele será preenchido exatamente da mesma forma que o array de cores de gradiente é preenchido quando o objeto é construído. Ao restaurar a cor, pegaremos a cor do array salvo.

No arquivo \MQL5\Include\DoEasy\Objects\Graph\GCnvElement.mqh, em sua seção protected, declaramos um array de cores de fundo iniciais e transferimos aqui desde a seção privada um método para preenchimento de arrays de cores - vamos precisar dele em classes herdadas:

//+------------------------------------------------------------------+
//| Class of the graphical element object                            |
//+------------------------------------------------------------------+
class CGCnvElement : public CGBaseObj
  {
protected:
   CGCnvElement     *m_element_main;                           // Pointer to the initial parent element within all the groups of bound objects
   CGCnvElement     *m_element_base;                           // Pointer to the parent element within related objects of the current group
   CCanvas           m_canvas;                                 // CCanvas class object
   CPause            m_pause;                                  // Pause class object
   bool              m_shadow;                                 // Shadow presence
   color             m_chart_color_bg;                         // Chart background color
   uint              m_duplicate_res[];                        // Array for storing resource data copy
   color             m_array_colors_bg[];                      // Array of element background colors
   color             m_array_colors_bg_dwn[];                  // Array of control background colors when clicking on the control
   color             m_array_colors_bg_ovr[];                  // Array of control background colors when hovering the mouse over the control
   bool              m_gradient_v;                             // Vertical gradient filling flag
   bool              m_gradient_c;                             // Cyclic gradient filling flag
   int               m_init_relative_x;                        // Initial relative X coordinate
   int               m_init_relative_y;                        // Initial relative Y coordinate
   color             m_array_colors_bg_init[];                 // Array of element background colors (initial color)

//--- Create (1) the object structure and (2) the object from the structure
   virtual bool      ObjectToStruct(void);
   virtual void      StructToObject(void);
//--- Copy the color array to the specified background color array
   void              CopyArraysColors(color &array_dst[],const color &array_src[],const string source);
   
private:


Na seção privada da classe, na estrutura de dados do objeto, escrevemos novas propriedades inteiras 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
      //---...
      //---...
      color          check_flag_color_mouse_down;              // Color of control checkbox when clicking on the control
      color          check_flag_color_mouse_over;              // Color of control checkbox when clicking on the control
      color          fore_color_mouse_down;                    // Default control text color when clicking on the control
      color          fore_color_mouse_over;                    // Default control text color when hovering the mouse over the control
      color          fore_color_toggle;                        // Text color of the control which is on
      color          fore_color_toggle_mouse_down;             // Default control text color when clicking on the control which is on
      color          fore_color_toggle_mouse_over;             // Default control text color when hovering the mouse over the control which is on
      color          background_color_toggle;                  // Background color of the control which is on
      color          background_color_toggle_mouse_down;       // Control background color when clicking on the control which is on
      color          background_color_toggle_mouse_over;       // Control background color when hovering the mouse over the control which is on
      bool           button_toggle;                            // Toggle flag of the control featuring a button
      bool           button_state;                             // Status of the Toggle control featuring a button
      //--- Object real properties

      //--- Object string properties
      uchar          name_obj[64];                             // Graphical element object name
      uchar          name_res[64];                             // Graphical resource name
      uchar          text[256];                                // Graphical element text
     };
   SData             m_struct_obj;                             // Object structure
   uchar             m_uchar_array[];                          // uchar array of the object structure

Isso é necessário para salvar corretamente o objeto no futuro e lê-lo.


Vamos escrever outro método para salvar um array de cores de gradiente para lembrar a cor de fundo inicial:

//--- Save the colors to the background color array
   void              SaveColorsBG(color &colors[])                         { this.CopyArraysColors(this.m_array_colors_bg,colors,DFUN);      }
   void              SaveColorsBGMouseDown(color &colors[])                { this.CopyArraysColors(this.m_array_colors_bg_dwn,colors,DFUN);  }
   void              SaveColorsBGMouseOver(color &colors[])                { this.CopyArraysColors(this.m_array_colors_bg_ovr,colors,DFUN);  }
   void              SaveColorsBGInit(color &colors[])                     { this.CopyArraysColors(this.m_array_colors_bg_init,colors,DFUN); }
   
public:

O método será chamado no método que define a cor de fundo. Para isso, introduziremos um sinalizador em suas variáveis formais, tal sinalizador inicará a necessidade de preservar a cor de fundo original após definir a cor de fundo do objeto. Assim, teremos uma escolha, isto é: ou definimos a cor de fundo do objeto e armazenamos suas cores originais no array, ou simplesmente definimos uma nova cor de fundo, e então podemos restaurá-la a partir de todo o conjunto de cores gradientes de fundo previamente escritas no array. Faremos isso para todos os objetos cuja cor deve mudar ao interagir com o mouse:

//--- Set the main background color
   void              SetBackgroundColor(const color colour,const bool set_init_color)
                       {
                        this.SetProperty(CANV_ELEMENT_PROP_BACKGROUND_COLOR,colour);
                        color arr[1];
                        arr[0]=colour;
                        this.SaveColorsBG(arr);
                        if(set_init_color)
                           this.SetBackgroundColorInit(this.BackgroundColor());
                       }
   void              SetBackgroundColors(color &colors[],const bool set_init_colors)
                       {
                        this.SaveColorsBG(colors);
                        this.SetProperty(CANV_ELEMENT_PROP_BACKGROUND_COLOR,this.m_array_colors_bg[0]);
                        if(set_init_colors)
                           this.SetBackgroundColorsInit(colors);
                       }


Vamos escrever métodos idênticos aos discutidos acima para definir a cor de fundo inicial:

   void              SetBackgroundColorsMouseOver(color &colors[])
                       {
                        this.SaveColorsBGMouseOver(colors);
                        this.SetProperty(CANV_ELEMENT_PROP_BACKGROUND_COLOR_MOUSE_OVER,this.m_array_colors_bg_ovr[0]);
                       }
//--- Set the initial main background color
   void              SetBackgroundColorInit(const color colour)
                       {
                        color arr[1];
                        arr[0]=colour;
                        this.SaveColorsBGInit(arr);
                       }
   void              SetBackgroundColorsInit(color &colors[])
                       {
                        this.SaveColorsBGInit(colors);
                       }
                       
//--- Set (1) object movability, (2) activity, (3) interaction,

Tudo aqui é igual aos métodos para definir a cor de fundo principal, exceto pelo sinalizador que indica a necessidade de preservar a cor original, que é salva apenas chamando esses métodos.

Vamos escrever métodos que retornam a cor de fundo original:

//--- Return the background color when hovering the mouse over the control
   color             BackgroundColorMouseOver(void)      const { return (color)this.GetProperty(CANV_ELEMENT_PROP_BACKGROUND_COLOR_MOUSE_OVER); }
   color             BackgroundColorMouseOver(const uint index) const
                       {
                        uint total=this.m_array_colors_bg_ovr.Size();
                        if(total==0)
                           return this.BackgroundColorMouseOver();
                        return(index>total-1 ? this.m_array_colors_bg_ovr[total-1] : this.m_array_colors_bg_ovr[index]);
                       }
//--- Return the initial color of the main background
   color             BackgroundColorInit(void)           const { return (color)this.m_array_colors_bg_init[0];    }
   color             BackgroundColorInit(const uint index)const
                       {
                        uint total=this.m_array_colors_bg_init.Size();
                        if(total==0)
                           return this.BackgroundColor();
                        return(index>total-1 ? this.m_array_colors_bg_init[total-1] : this.m_array_colors_bg_init[index]);
                       }
   
//--- Return (1) the opacity, coordinate (2) of the right and (3) bottom element edge

Os métodos retornam a única cor de fundo ou uma cor do conjunto de cores de gradiente correspondente ao índice passado para o método.


Em ambos os construtores de classe, escreveremos a inicialização de novas propriedades e a transferência de um sinalizador para preservar a cor de fundo original:

//+------------------------------------------------------------------+
//| Parametric constructor                                           |
//+------------------------------------------------------------------+
CGCnvElement::CGCnvElement(const ENUM_GRAPH_ELEMENT_TYPE element_type,
                           const int      element_id,
                           const int      element_num,
                           const long     chart_id,
                           const int      wnd_num,
                           const string   name,
                           const int      x,
                           const int      y,
                           const int      w,
                           const int      h,
                           const color    colour,
                           const uchar    opacity,
                           const bool     movable=true,
                           const bool     activity=true,
                           const bool     redraw=false) : m_shadow(false)
  {
   this.m_type=OBJECT_DE_TYPE_GELEMENT; 
   this.m_element_main=NULL;
   this.m_element_base=NULL;
   this.m_chart_color_bg=(color)::ChartGetInteger((chart_id==NULL ? ::ChartID() : chart_id),CHART_COLOR_BACKGROUND);
   this.m_name=(::StringFind(name,this.m_name_prefix)<0 ? this.m_name_prefix : "")+name;
   this.m_chart_id=(chart_id==NULL || chart_id==0 ? ::ChartID() : chart_id);
   this.m_subwindow=wnd_num;
   this.m_type_element=element_type;
   this.SetFont(DEF_FONT,DEF_FONT_SIZE);
   this.m_text_anchor=0;
   this.m_text_x=0;
   this.m_text_y=0;
   this.SetBackgroundColor(colour,true);
   this.SetOpacity(opacity);
   this.m_shift_coord_x=0;
   this.m_shift_coord_y=0;
   if(::ArrayResize(this.m_array_colors_bg,1)==1)
      this.m_array_colors_bg[0]=this.BackgroundColor();
   if(::ArrayResize(this.m_array_colors_bg_dwn,1)==1)
      this.m_array_colors_bg_dwn[0]=this.BackgroundColor();
   if(::ArrayResize(this.m_array_colors_bg_ovr,1)==1)
      this.m_array_colors_bg_ovr[0]=this.BackgroundColor();
   if(this.Create(chart_id,wnd_num,this.m_name,x,y,w,h,redraw))
     {
      this.SetProperty(CANV_ELEMENT_PROP_NAME_RES,this.m_canvas.ResourceName()); // Graphical resource name
      this.SetProperty(CANV_ELEMENT_PROP_CHART_ID,CGBaseObj::ChartID());         // Chart ID
      this.SetProperty(CANV_ELEMENT_PROP_WND_NUM,CGBaseObj::SubWindow());        // Chart subwindow index
      this.SetProperty(CANV_ELEMENT_PROP_NAME_OBJ,CGBaseObj::Name());            // Element object name
      this.SetProperty(CANV_ELEMENT_PROP_TYPE,element_type);                     // Graphical element type
      this.SetProperty(CANV_ELEMENT_PROP_ID,element_id);                         // Element ID
      this.SetProperty(CANV_ELEMENT_PROP_NUM,element_num);                       // Element index in the list
      this.SetProperty(CANV_ELEMENT_PROP_COORD_X,x);                             // Element's X coordinate on the chart
      this.SetProperty(CANV_ELEMENT_PROP_COORD_Y,y);                             // Element's Y coordinate on the chart
      this.SetProperty(CANV_ELEMENT_PROP_WIDTH,w);                               // Element width
      this.SetProperty(CANV_ELEMENT_PROP_HEIGHT,h);                              // Element height
      this.SetProperty(CANV_ELEMENT_PROP_ACT_SHIFT_LEFT,0);                      // Active area offset from the left edge of the element
      this.SetProperty(CANV_ELEMENT_PROP_ACT_SHIFT_TOP,0);                       // Active area offset from the upper edge of the element
      this.SetProperty(CANV_ELEMENT_PROP_ACT_SHIFT_RIGHT,0);                     // Active area offset from the right edge of the element
      this.SetProperty(CANV_ELEMENT_PROP_ACT_SHIFT_BOTTOM,0);                    // Active area offset from the bottom edge of the element
      this.SetProperty(CANV_ELEMENT_PROP_MOVABLE,movable);                       // Element moveability flag
      this.SetProperty(CANV_ELEMENT_PROP_ACTIVE,activity);                       // Element activity flag
      this.SetProperty(CANV_ELEMENT_PROP_INTERACTION,false);                     // Flag of interaction with the outside environment
      this.SetProperty(CANV_ELEMENT_PROP_ENABLED,true);                          // Element availability flag
      this.SetProperty(CANV_ELEMENT_PROP_RIGHT,this.RightEdge());                // Element right border
      this.SetProperty(CANV_ELEMENT_PROP_BOTTOM,this.BottomEdge());              // Element bottom border
      this.SetProperty(CANV_ELEMENT_PROP_COORD_ACT_X,this.ActiveAreaLeft());     // X coordinate of the element active area
      this.SetProperty(CANV_ELEMENT_PROP_COORD_ACT_Y,this.ActiveAreaTop());      // Y coordinate of the element active area
      this.SetProperty(CANV_ELEMENT_PROP_ACT_RIGHT,this.ActiveAreaRight());      // Right border of the element active area
      this.SetProperty(CANV_ELEMENT_PROP_ACT_BOTTOM,this.ActiveAreaBottom());    // Bottom border of the element active area
      //---
      this.SetProperty(CANV_ELEMENT_PROP_BELONG,ENUM_GRAPH_OBJ_BELONG::GRAPH_OBJ_BELONG_PROGRAM);  // Graphical element affiliation
      this.SetProperty(CANV_ELEMENT_PROP_ZORDER,0);                              // Priority of a graphical object for receiving the event of clicking on a chart
      this.SetProperty(CANV_ELEMENT_PROP_BOLD_TYPE,FW_NORMAL);                   // Font width type
      this.SetProperty(CANV_ELEMENT_PROP_BORDER_STYLE,FRAME_STYLE_NONE);         // Control frame style
      this.SetProperty(CANV_ELEMENT_PROP_BORDER_SIZE_TOP,0);                     // Control frame top size
      this.SetProperty(CANV_ELEMENT_PROP_BORDER_SIZE_BOTTOM,0);                  // Control frame bottom size
      this.SetProperty(CANV_ELEMENT_PROP_BORDER_SIZE_LEFT,0);                    // Control frame left size
      this.SetProperty(CANV_ELEMENT_PROP_BORDER_SIZE_RIGHT,0);                   // Control frame right size
      this.SetProperty(CANV_ELEMENT_PROP_BORDER_COLOR,this.BackgroundColor());   // Control frame color
      this.SetProperty(CANV_ELEMENT_PROP_AUTOSIZE,false);                        // Flag of the element auto resizing depending on the content
      this.SetProperty(CANV_ELEMENT_PROP_AUTOSIZE_MODE,CANV_ELEMENT_AUTO_SIZE_MODE_GROW); // Mode of the element auto resizing depending on the content
      this.SetProperty(CANV_ELEMENT_PROP_AUTOSCROLL,false);                      // Auto scrollbar flag
      this.SetProperty(CANV_ELEMENT_PROP_AUTOSCROLL_MARGIN_W,0);                 // Width of the field inside the element during auto scrolling
      this.SetProperty(CANV_ELEMENT_PROP_AUTOSCROLL_MARGIN_H,0);                 // Height of the field inside the element during auto scrolling
      this.SetProperty(CANV_ELEMENT_PROP_DOCK_MODE,CANV_ELEMENT_DOCK_MODE_NONE); // Mode of binding control borders to the container
      this.SetProperty(CANV_ELEMENT_PROP_MARGIN_TOP,0);                          // Top margin between the fields of this and another control
      this.SetProperty(CANV_ELEMENT_PROP_MARGIN_BOTTOM,0);                       // Bottom margin between the fields of this and another control
      this.SetProperty(CANV_ELEMENT_PROP_MARGIN_LEFT,0);                         // Left margin between the fields of this and another control
      this.SetProperty(CANV_ELEMENT_PROP_MARGIN_RIGHT,0);                        // Right margin between the fields of this and another control
      this.SetProperty(CANV_ELEMENT_PROP_PADDING_TOP,0);                         // Top margin inside the control
      this.SetProperty(CANV_ELEMENT_PROP_PADDING_BOTTOM,0);                      // Bottom margin inside the control
      this.SetProperty(CANV_ELEMENT_PROP_PADDING_LEFT,0);                        // Left margin inside the control
      this.SetProperty(CANV_ELEMENT_PROP_PADDING_RIGHT,0);                       // Right margin inside the control
      this.SetProperty(CANV_ELEMENT_PROP_TEXT_ALIGN,ANCHOR_LEFT_UPPER);          // Text position within text label boundaries
      this.SetProperty(CANV_ELEMENT_PROP_CHECK_ALIGN,ANCHOR_LEFT_UPPER);         // Position of the checkbox within control borders
      this.SetProperty(CANV_ELEMENT_PROP_CHECKED,false);                         // Control checkbox status
      this.SetProperty(CANV_ELEMENT_PROP_CHECK_STATE,CANV_ELEMENT_CHEK_STATE_UNCHECKED);  // Status of a control having a checkbox
      this.SetProperty(CANV_ELEMENT_PROP_AUTOCHECK,true);                        // Auto change flag status when it is selected
      //---
      this.SetProperty(CANV_ELEMENT_PROP_CHECK_BACKGROUND_COLOR,CLR_DEF_CHECK_BACK_COLOR);            // Color of control checkbox background
      this.SetProperty(CANV_ELEMENT_PROP_CHECK_BACKGROUND_COLOR_OPACITY,CLR_DEF_CHECK_BACK_OPACITY);  // Opacity of the control checkbox background color
      this.SetProperty(CANV_ELEMENT_PROP_CHECK_BACKGROUND_COLOR_MOUSE_DOWN,CLR_DEF_CHECK_BACK_MOUSE_DOWN);// Color of control checkbox background when clicking on the control
      this.SetProperty(CANV_ELEMENT_PROP_CHECK_BACKGROUND_COLOR_MOUSE_OVER,CLR_DEF_CHECK_BACK_MOUSE_OVER);// Color of control checkbox background when hovering the mouse over the control
      this.SetProperty(CANV_ELEMENT_PROP_CHECK_FORE_COLOR,CLR_DEF_CHECK_BORDER_COLOR);                // Color of control checkbox frame
      this.SetProperty(CANV_ELEMENT_PROP_CHECK_FORE_COLOR_OPACITY,CLR_DEF_CHECK_BORDER_OPACITY);      // Opacity of the control checkbox frame color
      this.SetProperty(CANV_ELEMENT_PROP_CHECK_FORE_COLOR_MOUSE_DOWN,CLR_DEF_CHECK_BORDER_MOUSE_DOWN);// Color of control checkbox frame when clicking on the control
      this.SetProperty(CANV_ELEMENT_PROP_CHECK_FORE_COLOR_MOUSE_OVER,CLR_DEF_CHECK_BORDER_MOUSE_OVER);// Color of control checkbox frame when hovering the mouse over the control
      this.SetProperty(CANV_ELEMENT_PROP_CHECK_FLAG_COLOR,CLR_DEF_CHECK_FLAG_COLOR);                  // Control checkbox color
      this.SetProperty(CANV_ELEMENT_PROP_CHECK_FLAG_COLOR_OPACITY,CLR_DEF_CHECK_FLAG_OPACITY);        // Control checkbox color opacity
      this.SetProperty(CANV_ELEMENT_PROP_CHECK_FLAG_COLOR_MOUSE_DOWN,CLR_DEF_CHECK_FLAG_MOUSE_DOWN);  // Control checkbox color when clicking on the control
      this.SetProperty(CANV_ELEMENT_PROP_CHECK_FLAG_COLOR_MOUSE_OVER,CLR_DEF_CHECK_FLAG_MOUSE_OVER);  // Control checkbox color when hovering the mouse over the control
      this.SetProperty(CANV_ELEMENT_PROP_FORE_COLOR,CLR_DEF_FORE_COLOR);                              // Default text color for all control objects
      this.SetProperty(CANV_ELEMENT_PROP_FORE_COLOR_OPACITY,CLR_DEF_FORE_COLOR_OPACITY);              // Opacity of the default text color for all control objects
      this.SetProperty(CANV_ELEMENT_PROP_FORE_COLOR_MOUSE_DOWN,CLR_DEF_FORE_COLOR_MOUSE_DOWN);        // Default control text color when clicking on the control
      this.SetProperty(CANV_ELEMENT_PROP_FORE_COLOR_MOUSE_OVER,CLR_DEF_FORE_COLOR_MOUSE_OVER);        // Default control text color when hovering the mouse over the control
      this.SetProperty(CANV_ELEMENT_PROP_FORE_COLOR_TOGGLE,CLR_DEF_FORE_COLOR);                       // Text color of the control which is on
      this.SetProperty(CANV_ELEMENT_PROP_FORE_COLOR_TOGGLE_MOUSE_DOWN,CLR_DEF_FORE_COLOR_MOUSE_DOWN); // Default control text color when clicking on the control which is on
      this.SetProperty(CANV_ELEMENT_PROP_FORE_COLOR_TOGGLE_MOUSE_OVER,CLR_DEF_FORE_COLOR_MOUSE_OVER); // Default control text color when hovering the mouse over the control which is on
      this.SetProperty(CANV_ELEMENT_PROP_BACKGROUND_COLOR_MOUSE_DOWN,this.BackgroundColor());         // Control background color when clicking on the control
      this.SetProperty(CANV_ELEMENT_PROP_BACKGROUND_COLOR_MOUSE_OVER,this.BackgroundColor());         // Control background color when hovering the mouse over the control
      this.SetProperty(CANV_ELEMENT_PROP_BACKGROUND_COLOR_TOGGLE,CLR_DEF_CONTROL_STD_BACK_COLOR_ON);  // Background color of the control which is on
      this.SetProperty(CANV_ELEMENT_PROP_BACKGROUND_COLOR_TOGGLE_MOUSE_DOWN,CLR_DEF_CONTROL_STD_BACK_DOWN_ON); // Control background color when clicking on the control which is on
      this.SetProperty(CANV_ELEMENT_PROP_BACKGROUND_COLOR_TOGGLE_MOUSE_OVER,CLR_DEF_CONTROL_STD_BACK_OVER_ON); // Control background color when clicking on the control which is on
      this.SetProperty(CANV_ELEMENT_PROP_BORDER_COLOR_MOUSE_DOWN,CLR_DEF_BORDER_MOUSE_DOWN);          // Control frame color when clicking on the control
      this.SetProperty(CANV_ELEMENT_PROP_BORDER_COLOR_MOUSE_OVER,CLR_DEF_BORDER_MOUSE_OVER);          // Control frame color when hovering the mouse over the control
      this.SetProperty(CANV_ELEMENT_PROP_BUTTON_TOGGLE,false);                                        // Toggle flag of the control featuring a button
      this.SetProperty(CANV_ELEMENT_PROP_BUTTON_STATE,false);                                         // Status of the Toggle control featuring a button
     }
   else
     {
      ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_FAILED_CREATE_ELM_OBJ),": ",this.m_name);
     }
  }
//+------------------------------------------------------------------+

As propriedades ForeColor da caixa de seleção agora estão definidas para os valores de BorderColor porque a caixa de seleção tem sua própria cor.

O segundo construtor é feito de forma idêntica e não é necessário considerá-lo aqui.


No método que cria a estrutura do objeto, escrevemos o salvamento das novas propriedades do objeto nos campos da estrutura:

//+------------------------------------------------------------------+
//| Create the object structure                                      |
//+------------------------------------------------------------------+
bool CGCnvElement::ObjectToStruct(void)
  {
//--- Save integer properties
   this.m_struct_obj.id=(int)this.GetProperty(CANV_ELEMENT_PROP_ID);                               // Element ID
   this.m_struct_obj.type=(int)this.GetProperty(CANV_ELEMENT_PROP_TYPE);                           // Graphical element type
   //---...
   //---...
   this.m_struct_obj.check_flag_color_mouse_down=(color)this.GetProperty(CANV_ELEMENT_PROP_CHECK_FLAG_COLOR_MOUSE_DOWN);// Control checkbox color when clicking on the control
   this.m_struct_obj.check_flag_color_mouse_over=(color)this.GetProperty(CANV_ELEMENT_PROP_CHECK_FLAG_COLOR_MOUSE_OVER);// Control checkbox color when hovering the mouse over the control

   this.m_struct_obj.fore_color_mouse_down=(color)this.GetProperty(CANV_ELEMENT_PROP_FORE_COLOR_MOUSE_DOWN);            // Default control text color when clicking on the control
   this.m_struct_obj.fore_color_mouse_over=(color)this.GetProperty(CANV_ELEMENT_PROP_FORE_COLOR_MOUSE_OVER);            // Default control text color when hovering the mouse over the control
   this.m_struct_obj.fore_color_toggle=(color)this.GetProperty(CANV_ELEMENT_PROP_FORE_COLOR_TOGGLE);                    // Text color of the control which is on
   this.m_struct_obj.fore_color_toggle_mouse_down=(color)this.GetProperty(CANV_ELEMENT_PROP_FORE_COLOR_TOGGLE_MOUSE_DOWN);// Default control text color when clicking on the control which is on
   this.m_struct_obj.fore_color_toggle_mouse_over=(color)this.GetProperty(CANV_ELEMENT_PROP_FORE_COLOR_TOGGLE_MOUSE_OVER);// Default control text color when hovering the mouse over the control which is on
   this.m_struct_obj.background_color_toggle=(color)this.GetProperty(CANV_ELEMENT_PROP_BACKGROUND_COLOR_TOGGLE);        // Background color of the control which is on
   this.m_struct_obj.background_color_toggle_mouse_down=(color)this.GetProperty(CANV_ELEMENT_PROP_BACKGROUND_COLOR_TOGGLE_MOUSE_DOWN);// Control background color when clicking on the control which is on
   this.m_struct_obj.background_color_toggle_mouse_over=(color)this.GetProperty(CANV_ELEMENT_PROP_BACKGROUND_COLOR_TOGGLE_MOUSE_OVER);// Control background color when hovering the mouse over the control which is on
   this.m_struct_obj.button_toggle=(bool)this.GetProperty(CANV_ELEMENT_PROP_BUTTON_TOGGLE);                             // Toggle flag of the control featuring a button
   this.m_struct_obj.button_state=(bool)this.GetProperty(CANV_ELEMENT_PROP_BUTTON_STATE);                               // Status of the Toggle control featuring a button
//--- Save real properties

//--- Save string properties
   ::StringToCharArray(this.GetProperty(CANV_ELEMENT_PROP_NAME_OBJ),this.m_struct_obj.name_obj);   // Graphical element object name
   ::StringToCharArray(this.GetProperty(CANV_ELEMENT_PROP_NAME_RES),this.m_struct_obj.name_res);   // Graphical resource name
   ::StringToCharArray(this.GetProperty(CANV_ELEMENT_PROP_TEXT),this.m_struct_obj.text);           // Graphical element text
   //--- Save the structure to the uchar array
   ::ResetLastError();
   if(!::StructToCharArray(this.m_struct_obj,this.m_uchar_array))
     {
      CMessage::ToLog(DFUN,MSG_LIB_SYS_FAILED_SAVE_OBJ_STRUCT_TO_UARRAY,true);
      return false;
     }
   return true;
  }
//+------------------------------------------------------------------+

E no método que cria o objeto da estrutura, escrevemos a configuração das novas propriedades do objeto a partir dos campos da estrutura:

//+------------------------------------------------------------------+
//| Create the object from the structure                             |
//+------------------------------------------------------------------+
void CGCnvElement::StructToObject(void)
  {
//--- Save integer properties
   this.SetProperty(CANV_ELEMENT_PROP_ID,this.m_struct_obj.id);                                    // Element ID
   this.SetProperty(CANV_ELEMENT_PROP_TYPE,this.m_struct_obj.type);                                // Graphical element type
   //---...
   //---...
   this.SetProperty(CANV_ELEMENT_PROP_CHECK_FLAG_COLOR_MOUSE_OVER,this.m_struct_obj.check_flag_color_mouse_over); // Control checkbox color when hovering the mouse over the control
   this.SetProperty(CANV_ELEMENT_PROP_FORE_COLOR_MOUSE_DOWN,this.m_struct_obj.fore_color_mouse_down);             // Default control text color when clicking on the control
   this.SetProperty(CANV_ELEMENT_PROP_FORE_COLOR_MOUSE_OVER,this.m_struct_obj.fore_color_mouse_over);             // Default control text color when hovering the mouse over the control
   this.SetProperty(CANV_ELEMENT_PROP_FORE_COLOR_TOGGLE,this.m_struct_obj.fore_color_toggle);                     // Text color of the control which is on
   this.SetProperty(CANV_ELEMENT_PROP_FORE_COLOR_TOGGLE_MOUSE_DOWN,this.m_struct_obj.fore_color_toggle_mouse_down);// Default control text color when clicking on the control which is on
   this.SetProperty(CANV_ELEMENT_PROP_FORE_COLOR_TOGGLE_MOUSE_OVER,this.m_struct_obj.fore_color_toggle_mouse_over);// Default control text color when hovering the mouse over the control which is on
   this.SetProperty(CANV_ELEMENT_PROP_BACKGROUND_COLOR_TOGGLE,this.m_struct_obj.background_color_toggle);         // Background color of the control which is on
   this.SetProperty(CANV_ELEMENT_PROP_BACKGROUND_COLOR_TOGGLE_MOUSE_DOWN,this.m_struct_obj.background_color_toggle_mouse_down);// Control background color when clicking on the control which is on
   this.SetProperty(CANV_ELEMENT_PROP_BACKGROUND_COLOR_TOGGLE_MOUSE_OVER,this.m_struct_obj.background_color_toggle_mouse_over);// Control background color when clicking on the control which is on
   this.SetProperty(CANV_ELEMENT_PROP_BUTTON_TOGGLE,this.m_struct_obj.button_toggle);                             // Toggle flag of the control featuring a button
   this.SetProperty(CANV_ELEMENT_PROP_BUTTON_STATE,this.m_struct_obj.button_state);                               // Status of the Toggle control featuring a button
//--- Save real properties

//--- Save string properties
   this.SetProperty(CANV_ELEMENT_PROP_NAME_OBJ,::CharArrayToString(this.m_struct_obj.name_obj));   // Graphical element object name
   this.SetProperty(CANV_ELEMENT_PROP_NAME_RES,::CharArrayToString(this.m_struct_obj.name_res));   // Graphical resource name
   this.SetProperty(CANV_ELEMENT_PROP_TEXT,::CharArrayToString(this.m_struct_obj.text));           // Graphical element text
  }
//+------------------------------------------------------------------+


No construtor da classe do objeto-sombra no arquivo \MQL5\Include\DoEasy\Objects\Graph\ShadowObj.mqh, passamos o sinalizador que indica necessidade de preservar a cor original ao definir a cor de fundo:

//+------------------------------------------------------------------+
//| Constructor                                                      |
//+------------------------------------------------------------------+
CShadowObj::CShadowObj(const long chart_id,
                       const int subwindow,
                       const string name,
                       const int x,
                       const int y,
                       const int w,
                       const int h) : CGCnvElement(GRAPH_ELEMENT_TYPE_SHADOW_OBJ,chart_id,subwindow,name,x,y,w,h)
  {
   this.m_type=OBJECT_DE_TYPE_GSHADOW; 
   CGCnvElement::SetBackgroundColor(clrNONE,true);
   CGCnvElement::SetOpacity(0);
   CGCnvElement::SetActive(false);
   this.m_opacity=CLR_DEF_SHADOW_OPACITY;
   this.m_blur=DEF_SHADOW_BLUR;
   color gray=CGCnvElement::ChangeColorSaturation(this.ChartBackgroundColor(),-100);
   this.m_color=CGCnvElement::ChangeColorLightness(gray,-50);
   this.m_shadow=false;
   this.m_visible=true;
   CGCnvElement::Erase();
  }
//+------------------------------------------------------------------+

Não precisamos armazenar uma cor completamente transparente aqui, mas vamos mantê-la para evitar valores de propriedade não inicializados.


O objeto-forma é o objeto que serve de base para o elemento gráfico, que contém a funcionalidade de interação com o mouse. Nele, escreveremos um manipulador de eventos de mouse virtual e manipuladores virtuais para cada evento definido pelo manipulador de eventos.

O manipulador de eventos será virtual caso tenha que ser sobrescrito em classes herdadas, mas os manipuladores de cada um dos eventos de mouse já sabemos claramente que devem ser virtuais, pois para cada um dos objetos herdeiros da classe de objeto-forma, o processamento de um ou outro evento de mouse será individual. Para restaurar o estado do objeto e sua cor após o cursor sair de sua área ativa, precisamos de um método que lide com tal estado (retirada do cursor para fora do objeto). Como quando o cursor é retirado para fora do objeto, outro objeto, ou nenhum, sob o cursor automaticamente se torna ativo, então para processar esse estado, precisamos percorrer todos os objetos WinForms, determinar seu último estado e processá-lo em caso se seu último estado foi o cursor sobre ele. E então mudamos este estado para outro que corresponda ao seu estado atual. Assim, apenas processaremos a retirada do cursor para fora do objeto uma vez e colocaremos o objeto em seu estado correto - cursor fora da forma.

No arquivo de objeto-forma \MQL5\Include\DoEasy\Objects\Graph\Form.mqh, na seção protegida da classe, vamos declarar duas variáveis: uma para armazenar o último evento do mouse (necessário para o último manipulador de eventos) e outra para armazenar a cor inicial da moldura da forma (já que ela pode mudar de cor ao interagir com o mouse e precisa ser restaurada). No mesmo lugar, vamos declarar manipuladores virtuais para cada um dos eventos do mouse:

protected:
   CArrayObj         m_list_elements;                          // List of attached elements
   CArrayObj         m_list_interact;                          // List of interaction elements
   CAnimations      *m_animations;                             // Pointer to the animation object
   CShadowObj       *m_shadow_obj;                             // Pointer to the shadow object
   CMouseState       m_mouse;                                  // "Mouse status" class object
   ENUM_MOUSE_FORM_STATE m_mouse_form_state;                   // Mouse status relative to the form
   ENUM_MOUSE_EVENT  m_mouse_event_last;                       // Last mouse event
   ushort            m_mouse_state_flags;                      // Mouse status flags
   int               m_offset_x;                               // Offset of the X coordinate relative to the cursor
   int               m_offset_y;                               // Offset of the Y coordinate relative to the cursor
   CArrayObj         m_list_tmp;                               // List for storing the pointers
   int               m_init_x;                                 // Newly created form X coordinate
   int               m_init_y;                                 // Newly created form Y coordinate
   int               m_init_w;                                 // Newly created form width
   int               m_init_h;                                 // Newly created form height
   color             m_border_color_init;                      // Initial color of the control frame
   
//--- Initialize the variables
   virtual void      Initialize(void);
   void              Deinitialize(void);
//--- Create a shadow object
   void              CreateShadowObj(const color colour,const uchar opacity);
//--- Return the name of the dependent object
   string            CreateNameDependentObject(const string base_name)  const
                       { return ::StringSubstr(this.NameObj(),::StringLen(::MQLInfoString(MQL_PROGRAM_NAME))+1)+"_"+base_name;   }
//--- Update coordinates of bound objects
   virtual bool      MoveDependentObj(const int x,const int y,const bool redraw=false);
//--- Create a new bound element and add it to the list of bound objects
   virtual CGCnvElement *CreateAndAddNewElement(const ENUM_GRAPH_ELEMENT_TYPE element_type,
                                                const int x,
                                                const int y,
                                                const int w,
                                                const int h,
                                                const color colour,
                                                const uchar opacity,
                                                const bool activity);
//--- Create the list of all interaction objects
   void              CreateListDepInteractObj(CArrayObj *list);
//--- Return the flag indicating the presence of the pointer to an object in the list of interaction objects by name
   bool              IsPresentInteractObj(const string name);
   
//--- 'The cursor is outside the form, no mouse buttons are clicked' event handler
   virtual void      MouseOutsideNotPressedHandler(const int id,const long& lparam,const double& dparam,const string& sparam);
//--- 'The cursor is outside the form, a mouse button is clicked (any)' event handler
   virtual void      MouseOutsidePressedHandler(const int id,const long& lparam,const double& dparam,const string& sparam);
//--- 'The cursor is outside the form, the mouse wheel is being scrolled' event handler
   virtual void      MouseOutsideWhellHandler(const int id,const long& lparam,const double& dparam,const string& sparam);
//--- 'The cursor is inside the form, no mouse buttons are clicked' event handler
   virtual void      MouseInsideNotPressedHandler(const int id,const long& lparam,const double& dparam,const string& sparam);
//--- 'The cursor is inside the form, a mouse button is clicked (any)' event handler
   virtual void      MouseInsidePressedHandler(const int id,const long& lparam,const double& dparam,const string& sparam);
//--- 'The cursor is inside the form, the mouse wheel is being scrolled' event handler
   virtual void      MouseInsideWhellHandler(const int id,const long& lparam,const double& dparam,const string& sparam);
//--- 'The cursor is inside the active area, the mouse buttons are not clicked' event handler
   virtual void      MouseActiveAreaNotPressedHandler(const int id,const long& lparam,const double& dparam,const string& sparam);
//--- 'The cursor is inside the active area, a mouse button is clicked (any)' event handler
   virtual void      MouseActiveAreaPressedHandler(const int id,const long& lparam,const double& dparam,const string& sparam);
//--- 'The cursor is inside the active area, the mouse wheel is being scrolled' event handler
   virtual void      MouseActiveAreaWhellHandler(const int id,const long& lparam,const double& dparam,const string& sparam);
//--- 'The cursor is inside the active area, the left mouse button is clicked' event handler
   virtual void      MouseActiveAreaReleasedHandler(const int id,const long& lparam,const double& dparam,const string& sparam);
//--- 'The cursor is inside the window scrolling area, no mouse buttons are clicked' event handler
   virtual void      MouseScrollAreaNotPressedHandler(const int id,const long& lparam,const double& dparam,const string& sparam);
//--- 'The cursor is inside the window scrolling area, a mouse button is clicked (any)' event handler
   virtual void      MouseScrollAreaPressedHandler(const int id,const long& lparam,const double& dparam,const string& sparam);
//--- 'The cursor is inside the window scrolling area, the mouse wheel is being scrolled' event handler
   virtual void      MouseScrollAreaWhellHandler(const int id,const long& lparam,const double& dparam,const string& sparam);

public:


Na seção pública da classe, declaramos um método virtual para redesenhar o objeto, escrevemos um método que retorna uma variável que armazena o estado do mouse e
um método que retorna o último evento do mouse.
Vamos tornar os métodos que definem o tamanho da moldura em cada lado virtual
e vamos declarar manipuladores de eventos virtuais para o mouse e seu último evento:

public:
//--- Redraw the object
   virtual void      Redraw(bool redraw) { return; }
//--- Create the list of all interaction objects
   int               CreateListInteractObj(void);
//--- Return the pointer to the form object in the list of interaction objects
   CForm            *GetInteractForm(const int index)    { return this.m_list_interact.At(index);  }
//--- Return the initial (1) X and (2) Y coordinate, (3) form width and (4) height
   int               GetCoordXInit(void)                 const { return this.m_init_x;             }
   int               GetCoordYInit(void)                 const { return this.m_init_y;             }
   int               GetWidthInit(void)                  const { return this.m_init_w;             }
   int               GetHeightInit(void)                 const { return this.m_init_h;             }
//--- Set the initial (1) X and (2) Y coordinate, (3) form width and (4) height
   void              SetCoordXInit(const int value)            { this.m_init_x=value;              }
   void              SetCoordYInit(const int value)            { this.m_init_y=value;              }
   void              SetWidthInit(const int value)             { this.m_init_w=value;              }
   void              SetHeightInit(const int value)            { this.m_init_h=value;              }
//--- (1) Get and (2) return the mouse status relative to the form, as well as cursor (3) X, (4) Y coordinates and (4) the last mouse event
   ENUM_MOUSE_FORM_STATE MouseFormState(const int id,const long lparam,const double dparam,const string sparam);
   ENUM_MOUSE_FORM_STATE GetMouseState(void)             const { return this.m_mouse_form_state;   }
   int               MouseCursorX(void)                  const { return this.m_mouse.CoordX();     }
   int               MouseCursorY(void)                  const { return this.m_mouse.CoordY();     }
   ENUM_MOUSE_EVENT  MouseEventLast(void)                const { return this.m_mouse_event_last;   }
//--- Set the flags of mouse scrolling, context menu and the crosshairs tool for the chart
   void              SetChartTools(const bool flag);
//--- (1) Set and (2) return the shift of X and Y coordinates relative to the cursor
   void              SetOffsetX(const int value)               { this.m_offset_x=value;            }
   void              SetOffsetY(const int value)               { this.m_offset_y=value;            }
   int               OffsetX(void)                       const { return this.m_offset_x;           }
   int               OffsetY(void)                       const { return this.m_offset_y;           }
   
//--- Return the frame size (1) to the left, (2) at the top, (3) to the right, (4) at the bottom and (5) on all sides
   int               BorderSizeLeft(void)                const { return (int)this.GetProperty(CANV_ELEMENT_PROP_BORDER_SIZE_LEFT);  }
   int               BorderSizeTop(void)                 const { return (int)this.GetProperty(CANV_ELEMENT_PROP_BORDER_SIZE_TOP);   }
   int               BorderSizeRight(void)               const { return (int)this.GetProperty(CANV_ELEMENT_PROP_BORDER_SIZE_RIGHT); }
   int               BorderSizeBottom(void)              const { return (int)this.GetProperty(CANV_ELEMENT_PROP_BORDER_SIZE_BOTTOM);}
//--- Set the frame size (1) to the left, (2) at the top, (3) to the right, (4) at the bottom and (5) on all sides
   virtual void      SetBorderSizeLeft(const uint value)       { this.SetProperty(CANV_ELEMENT_PROP_BORDER_SIZE_LEFT,value);        }
   virtual void      SetBorderSizeTop(const uint value)        { this.SetProperty(CANV_ELEMENT_PROP_BORDER_SIZE_TOP,value);         }
   virtual void      SetBorderSizeRight(const uint value)      { this.SetProperty(CANV_ELEMENT_PROP_BORDER_SIZE_RIGHT,value);       }
   virtual void      SetBorderSizeBottom(const uint value)     { this.SetProperty(CANV_ELEMENT_PROP_BORDER_SIZE_BOTTOM,value);      }

//--- Update the coordinates (shift the canvas)
   virtual bool      Move(const int x,const int y,const bool redraw=false);
//--- Set the priority of a graphical object for receiving the event of clicking on a chart
   virtual bool      SetZorder(const long value,const bool only_prop);
//--- Set the object above all
   virtual void      BringToTop(void);
   
//--- Event handler
   virtual void      OnChartEvent(const int id,const long& lparam,const double& dparam,const string& sparam);
//--- Mouse event handler
   virtual void      OnMouseEvent(const int id,const long& lparam,const double& dparam,const string& sparam);
//--- Last mouse event handler
   virtual void      OnMouseEventPostProcessing(void);

//--- Constructors


Vamos alterar o método que define a cor da borda do objeto para que possamos determinar a partir do estado do sinalizador se devemos preservar a cor da borda original e escrever métodos para definir e retornar a cor original da borda do objeto:

//+------------------------------------------------------------------+
//| Methods of simplified access to object properties                |
//+------------------------------------------------------------------+
//--- (1) Set and (2) return the control frame color
   void              SetBorderColor(const color colour,const bool set_init_color)
                       {
                        this.SetProperty(CANV_ELEMENT_PROP_BORDER_COLOR,colour);
                        if(set_init_color)
                           this.SetBorderColorInit(colour);
                       }
   color             BorderColor(void)                            const { return (color)this.GetProperty(CANV_ELEMENT_PROP_BORDER_COLOR);             }
//--- (1) Set and (2) return the control frame color when clicking the control
   void              SetBorderColorMouseDown(const color colour)        { this.SetProperty(CANV_ELEMENT_PROP_BORDER_COLOR_MOUSE_DOWN,colour);         }
   color             BorderColorMouseDown(void)                   const { return (color)this.GetProperty(CANV_ELEMENT_PROP_BORDER_COLOR_MOUSE_DOWN);  }
//--- (1) Set and (2) return the control frame color when hovering the mouse over the control
   void              SetBorderColorMouseOver(const color colour)        { this.SetProperty(CANV_ELEMENT_PROP_BORDER_COLOR_MOUSE_OVER,colour);         }
   color             BorderColorMouseOver(void)                   const { return (color)this.GetProperty(CANV_ELEMENT_PROP_BORDER_COLOR_MOUSE_OVER);  }
//--- (1) Set and (2) get the initial color of the control frame
   void              SetBorderColorInit(const color colour)             { this.m_border_color_init=colour;                                            }
   color             BorderColorInit(void)                        const { return (color)this.m_border_color_init;                                     }

//--- (1) Set and (2) return the form shadow color


No método de inicialização do objeto, salvamos a cor inicial da moldura especificando o sinalizador desejado no método para definir a cor, e definimos as cores padrão para a cor da moldura ao passar o cursor do mouse sobre o objeto e clicar nele. Inicializamos o último evento do mouse:

//+------------------------------------------------------------------+
//| Initialize the variables                                         |
//+------------------------------------------------------------------+
void CForm::Initialize(void)
  {
   this.m_list_elements.Clear();
   this.m_list_elements.Sort();
   this.m_list_interact.Clear();
   this.m_list_interact.Sort();
   this.m_list_tmp.Clear();
   this.m_list_tmp.Sort();
   this.m_shadow_obj=NULL;
   this.m_shadow=false;
   this.SetBorderSizeTop(DEF_FRAME_WIDTH_SIZE);
   this.SetBorderSizeBottom(DEF_FRAME_WIDTH_SIZE);
   this.SetBorderSizeLeft(DEF_FRAME_WIDTH_SIZE);
   this.SetBorderSizeRight(DEF_FRAME_WIDTH_SIZE);
   this.SetBorderColor(CLR_DEF_BORDER_COLOR,true);
   this.SetBorderColorMouseDown(CLR_DEF_BORDER_MOUSE_DOWN);
   this.SetBorderColorMouseOver(CLR_DEF_BORDER_MOUSE_OVER);
   this.m_gradient_v=true;
   this.m_gradient_c=false;
   this.m_mouse_state_flags=0;
   this.m_mouse_event_last=MOUSE_EVENT_NO_EVENT;
   this.m_offset_x=0;
   this.m_offset_y=0;
   this.m_init_x=0;
   this.m_init_y=0;
   this.m_init_w=0;
   this.m_init_h=0;
   CGCnvElement::SetInteraction(false);
   this.m_animations=new CAnimations(CGCnvElement::GetObject());
   this.m_list_tmp.Add(this.m_animations);
  }
//+------------------------------------------------------------------+


Em todos os métodos de criação de objetos em que é necessário definir a cor de fundo, escreveremos esse sinalizador. No método CreateAndAddNewElement():

obj.SetBackgroundColor(colour,true);

No método que define o esquema de cores:

//+------------------------------------------------------------------+
//| Set the color scheme                                             |
//+------------------------------------------------------------------+
void CForm::SetColorTheme(const ENUM_COLOR_THEMES theme,const uchar opacity)
  {
   if(this.m_shadow && this.m_shadow_obj!=NULL)
      this.SetColorShadow(array_color_themes[theme][COLOR_THEME_COLOR_FORM_SHADOW]);
   this.SetOpacity(opacity);
   this.SetBackgroundColor(array_color_themes[theme][COLOR_THEME_COLOR_FORM_BG],true);
   this.SetBorderColor(array_color_themes[theme][COLOR_THEME_COLOR_FORM_FRAME],true);
  }
//+------------------------------------------------------------------+

Em cada caso, a cor de fundo original do elemento agora será preservada.


No método que define e retorna o estado do mouse em relação à forma, declaramos anteriormente uma variável local na qual escrevemos o estado do mouse e retornamos o valor dessa variável. Agora temos essa variável na classe para armazenar esse valor.
Vamos escrever valores nele e retornar a partir do método
:

//+------------------------------------------------------------------+
//| Set and get the mouse status relative to the form                |
//+------------------------------------------------------------------+
ENUM_MOUSE_FORM_STATE CForm::MouseFormState(const int id,const long lparam,const double dparam,const string sparam)
  {
//--- Get the mouse status relative to the form, as well as the states of mouse buttons and Shift/Ctrl keys
   this.m_mouse_form_state=MOUSE_FORM_STATE_OUTSIDE_FORM_NOT_PRESSED;
   ENUM_MOUSE_BUTT_KEY_STATE state=this.m_mouse.ButtonKeyState(id,lparam,dparam,sparam);
//--- Get the mouse status flags from the CMouseState class object and save them in the variable
   this.m_mouse_state_flags=this.m_mouse.GetMouseFlags();
//--- If the cursor is inside the form
   if(CGCnvElement::CursorInsideElement(m_mouse.CoordX(),m_mouse.CoordY()))
     {
      //--- Set bit 8 responsible for the "cursor inside the form" flag
      this.m_mouse_state_flags |= (0x0001<<8);
      //--- If the cursor is inside the active area, set bit 9 "cursor inside the active area"
      if(CGCnvElement::CursorInsideActiveArea(m_mouse.CoordX(),m_mouse.CoordY()))
         this.m_mouse_state_flags |= (0x0001<<9);
      //--- otherwise, release the bit "cursor inside the active area"
      else this.m_mouse_state_flags &=0xFDFF;
      //--- If one of the mouse buttons is clicked, check the cursor location in the active area and
      //--- return the appropriate value of the pressed key (in the active area or the form area)
      if((this.m_mouse_state_flags & 0x0001)!=0 || (this.m_mouse_state_flags & 0x0002)!=0 || (this.m_mouse_state_flags & 0x0010)!=0)
         this.m_mouse_form_state=((this.m_mouse_state_flags & 0x0200)!=0 ? MOUSE_FORM_STATE_INSIDE_ACTIVE_AREA_PRESSED : MOUSE_FORM_STATE_INSIDE_FORM_PRESSED);
      //--- otherwise, if not a single mouse button is pressed
      else
        {
         //--- if the mouse wheel is scrolled, return the appropriate wheel scrolling value (in the active area or the form area)
         if((this.m_mouse_state_flags & 0x0080)!=0)
            this.m_mouse_form_state=((this.m_mouse_state_flags & 0x0200)!=0 ? MOUSE_FORM_STATE_INSIDE_ACTIVE_AREA_WHEEL : MOUSE_FORM_STATE_INSIDE_FORM_WHEEL);
         //--- otherwise, return the appropriate value of the unpressed key (in the active area or the form area)
         else
            this.m_mouse_form_state=((this.m_mouse_state_flags & 0x0200)!=0 ? MOUSE_FORM_STATE_INSIDE_ACTIVE_AREA_NOT_PRESSED : MOUSE_FORM_STATE_INSIDE_FORM_NOT_PRESSED);
        } 
     }
//--- If the cursor is outside the form
   else
     {
      //--- return the appropriate button value in an inactive area
      this.m_mouse_form_state=
        (
         ((this.m_mouse_state_flags & 0x0001)!=0 || (this.m_mouse_state_flags & 0x0002)!=0 || (this.m_mouse_state_flags & 0x0010)!=0) ? 
          MOUSE_FORM_STATE_OUTSIDE_FORM_PRESSED : MOUSE_FORM_STATE_OUTSIDE_FORM_NOT_PRESSED
        );
     }
   return this.m_mouse_form_state;
  }
//+------------------------------------------------------------------+


Manipulador de eventos do mouse:

//+------------------------------------------------------------------+
//| Mouse event handler                                              |
//+------------------------------------------------------------------+
void CForm::OnMouseEvent(const int id,const long &lparam,const double &dparam,const string &sparam)
  {
   switch(id)
     {
      //--- The cursor is outside the form, the mouse buttons are not clicked
      //--- The cursor is outside the form, any mouse button is clicked
      //--- The cursor is outside the form, the mouse wheel is being scrolled
      case MOUSE_EVENT_OUTSIDE_FORM_NOT_PRESSED :
      case MOUSE_EVENT_OUTSIDE_FORM_PRESSED     :
      case MOUSE_EVENT_OUTSIDE_FORM_WHEEL       :
        break;
      //--- The cursor is inside the form, the mouse buttons are not clicked
      case MOUSE_EVENT_INSIDE_FORM_NOT_PRESSED :
        this.MouseInsideNotPressedHandler(id,lparam,dparam,sparam);
        break;
      //--- The cursor is inside the form, any mouse button is clicked
      case MOUSE_EVENT_INSIDE_FORM_PRESSED :
        this.MouseInsidePressedHandler(id,lparam,dparam,sparam);
        break;
      //--- The cursor is inside the form, the mouse wheel is being scrolled
      case MOUSE_EVENT_INSIDE_FORM_WHEEL :
        this.MouseInsideWhellHandler(id,lparam,dparam,sparam);
        break;
      //--- The cursor is inside the active area, the mouse buttons are not clicked
      case MOUSE_EVENT_INSIDE_ACTIVE_AREA_NOT_PRESSED :
        this.MouseActiveAreaNotPressedHandler(id,lparam,dparam,sparam);
        break;
      //--- The cursor is inside the active area, any mouse button is clicked
      case MOUSE_EVENT_INSIDE_ACTIVE_AREA_PRESSED :
        this.MouseActiveAreaPressedHandler(id,lparam,dparam,sparam);
        break;
      //--- The cursor is inside the active area, the mouse wheel is being scrolled
      case MOUSE_EVENT_INSIDE_ACTIVE_AREA_WHEEL :
        this.MouseActiveAreaWhellHandler(id,lparam,dparam,sparam);
        break;
      //--- The cursor is inside the active area, left mouse button is released
      case MOUSE_EVENT_INSIDE_ACTIVE_AREA_RELEASED :
        this.MouseActiveAreaReleasedHandler(id,lparam,dparam,sparam);
        break;
      //--- The cursor is within the window scrolling area, the mouse buttons are not clicked
      case MOUSE_EVENT_INSIDE_SCROLL_AREA_NOT_PRESSED :
        this.MouseScrollAreaNotPressedHandler(id,lparam,dparam,sparam);
        break;
      //--- The cursor is within the window scrolling area, any mouse button is clicked
      case MOUSE_EVENT_INSIDE_SCROLL_AREA_PRESSED :
        this.MouseScrollAreaPressedHandler(id,lparam,dparam,sparam);
        break;
      //--- The cursor is within the window scrolling area, the mouse wheel is being scrolled
      case MOUSE_EVENT_INSIDE_SCROLL_AREA_WHEEL :
        this.MouseScrollAreaWhellHandler(id,lparam,dparam,sparam);
        break;
      //--- MOUSE_EVENT_NO_EVENT
      default:
        break;
     }
   this.m_mouse_event_last=(ENUM_MOUSE_EVENT)id;
  }
//+------------------------------------------------------------------+

Aqui: de acordo com o identificador do evento do mouse, chamamos para cada evento o método correspondente e o processamos. Por fim, salvamos o evento passado para o método em uma variável que armazena o último evento do mouse. Então, primeiro tratamos o evento e depois o registramos como o último evento tratado. Todos os métodos chamados aqui, que discutiremos a seguir, são virtuais e devem ser redefinidos em classes herdadas.

Manipuladores virtuais para cada um dos eventos do mouse:

//+------------------------------------------------------------------+
//| 'The cursor is outside the form,                                 |
//| no mouse buttons are clicked' event handler                      |
//+------------------------------------------------------------------+
void CForm::MouseOutsideNotPressedHandler(const int id,const long& lparam,const double& dparam,const string& sparam)
  {
   return;
  }
//+------------------------------------------------------------------+
//| 'The cursor is outside the form,                                 |
//| a mouse button is clicked (any)                                  |
//+------------------------------------------------------------------+
void CForm::MouseOutsidePressedHandler(const int id,const long& lparam,const double& dparam,const string& sparam)
  {
   return;
  }
//+------------------------------------------------------------------+
//| 'The cursor is outside the form,                                 |
//| the mouse wheel is being scrolled                                |
//+------------------------------------------------------------------+
void CForm::MouseOutsideWhellHandler(const int id,const long& lparam,const double& dparam,const string& sparam)
  {
   return;
  }
//+------------------------------------------------------------------+
//| 'The cursor is inside the form,                                  |
//| no mouse buttons are clicked' event handler                      |
//+------------------------------------------------------------------+
void CForm::MouseInsideNotPressedHandler(const int id,const long& lparam,const double& dparam,const string& sparam)
  {
   return;
  }
//+------------------------------------------------------------------+
//| 'The cursor is inside the form,                                  |
//| a mouse button is clicked (any)                                  |
//+------------------------------------------------------------------+
void CForm::MouseInsidePressedHandler(const int id,const long& lparam,const double& dparam,const string& sparam)
  {
   return;
  }
//+------------------------------------------------------------------+
//| 'The cursor is inside the form,                                  |
//| the mouse wheel is being scrolled                                |
//+------------------------------------------------------------------+
void CForm::MouseInsideWhellHandler(const int id,const long& lparam,const double& dparam,const string& sparam)
  {
   return;
  }
//+------------------------------------------------------------------+
//| 'The cursor is inside the active area,                           |
//| no mouse buttons are clicked' event handler                      |
//+------------------------------------------------------------------+
void CForm::MouseActiveAreaNotPressedHandler(const int id,const long& lparam,const double& dparam,const string& sparam)
  {
   return;
  }
//+------------------------------------------------------------------+
//| 'The cursor is inside the active area,                           |
//| a mouse button is clicked (any)                                  |
//+------------------------------------------------------------------+
void CForm::MouseActiveAreaPressedHandler(const int id,const long& lparam,const double& dparam,const string& sparam)
  {
   return;
  }
//+------------------------------------------------------------------+
//| 'The cursor is inside the active area,                           |
//| the mouse wheel is being scrolled                                |
//+------------------------------------------------------------------+
void CForm::MouseActiveAreaWhellHandler(const int id,const long& lparam,const double& dparam,const string& sparam)
  {
   return;
  }
//+------------------------------------------------------------------+
//| 'The cursor is inside the active area,                           |
//| left mouse button released                                       |
//+------------------------------------------------------------------+
void CForm::MouseActiveAreaReleasedHandler(const int id,const long& lparam,const double& dparam,const string& sparam)
  {
   return;
  }
//+------------------------------------------------------------------+
//| 'The cursor is inside the window scrolling area,                 |
//| no mouse buttons are clicked' event handler                      |
//+------------------------------------------------------------------+
void CForm::MouseScrollAreaNotPressedHandler(const int id,const long& lparam,const double& dparam,const string& sparam)
  {
   return;
  }
//+------------------------------------------------------------------+
//| 'The cursor is inside the window scrolling area,                 |
//| a mouse button is clicked (any)                                  |
//+------------------------------------------------------------------+
void CForm::MouseScrollAreaPressedHandler(const int id,const long& lparam,const double& dparam,const string& sparam)
  {
   return;
  }
//+------------------------------------------------------------------+
//| 'The cursor is inside the window scrolling area,                 |
//| the mouse wheel is being scrolled                                |
//+------------------------------------------------------------------+
void CForm::MouseScrollAreaWhellHandler(const int id,const long& lparam,const double& dparam,const string& sparam)
  {
   return;
  }
//+------------------------------------------------------------------+

Os métodos não fazem nada, pois todo o tratamento de cada evento é individual para cada um dos objetos-herdeiros do objeto-forma e, se necessário, deve ser sobrescrito em cada objeto herdado.


Manipulador virtual para o último evento do mouse:

//+------------------------------------------------------------------+
//| Last mouse event handler                                         |
//+------------------------------------------------------------------+
void CForm::OnMouseEventPostProcessing(void)
  {
   ENUM_MOUSE_FORM_STATE state=GetMouseState();
   switch(state)
     {
      //--- The cursor is outside the form, the mouse buttons are not clicked
      //--- The cursor is outside the form, any mouse button is clicked
      //--- The cursor is outside the form, the mouse wheel is being scrolled
      case MOUSE_FORM_STATE_OUTSIDE_FORM_NOT_PRESSED :
      case MOUSE_FORM_STATE_OUTSIDE_FORM_PRESSED     :
      case MOUSE_FORM_STATE_OUTSIDE_FORM_WHEEL       :
        if(MouseEventLast()==MOUSE_EVENT_INSIDE_ACTIVE_AREA_NOT_PRESSED)
          {
           this.SetBackgroundColor(this.BackgroundColorInit(),false);
           this.SetBorderColor(this.BorderColorInit(),false);
           this.Redraw(true);
           this.m_mouse_event_last=ENUM_MOUSE_EVENT(state+MOUSE_EVENT_NO_EVENT);
          }
        break;

      //--- The cursor is inside the form, the mouse buttons are not clicked
      case MOUSE_FORM_STATE_INSIDE_FORM_NOT_PRESSED :
        break;
      //--- The cursor is inside the form, any mouse button is clicked
      case MOUSE_FORM_STATE_INSIDE_FORM_PRESSED :
        break;
      //--- The cursor is inside the form, the mouse wheel is being scrolled
      case MOUSE_FORM_STATE_INSIDE_FORM_WHEEL :
        break;
      //--- The cursor is inside the active area, the mouse buttons are not clicked
      case MOUSE_FORM_STATE_INSIDE_ACTIVE_AREA_NOT_PRESSED :
        break;
      //--- The cursor is inside the active area, any mouse button is clicked
      case MOUSE_FORM_STATE_INSIDE_ACTIVE_AREA_PRESSED :
        break;
      //--- The cursor is inside the active area, the mouse wheel is being scrolled
      case MOUSE_FORM_STATE_INSIDE_ACTIVE_AREA_WHEEL :
        break;
      //--- The cursor is inside the active area, left mouse button is released
      case MOUSE_FORM_STATE_INSIDE_ACTIVE_AREA_RELEASED :
        break;
      //--- The cursor is within the window scrolling area, the mouse buttons are not clicked
      case MOUSE_FORM_STATE_INSIDE_SCROLL_AREA_NOT_PRESSED :
        break;
      //--- The cursor is within the window scrolling area, any mouse button is clicked
      case MOUSE_FORM_STATE_INSIDE_SCROLL_AREA_PRESSED :
        break;
      //--- The cursor is within the window scrolling area, the mouse wheel is being scrolled
      case MOUSE_FORM_STATE_INSIDE_SCROLL_AREA_WHEEL :
        break;
      //--- MOUSE_EVENT_NO_EVENT
      default:
        break;
     }
  }
//+------------------------------------------------------------------+

O método também é virtual e deve ser redefinido em classes herdadas, se necessário.
O que isso faz: se o estado do mouse para o objeto for definido como "cursor fora da forma" (o estado dos botões e da roda não é importante), então verificamos qual foi o último evento do mouse para o objeto. Se for "o cursor está dentro da área ativa e o botão do mouse não está pressionado", consideramos que o cursor foi retirado para fora do objeto e processamos este evento, para tal definimos a cor de fundo inicial, a cor inicial da moldura do objeto, redesenhamos o objeto e o gravamos em uma variável que armazena o último evento do mouse, o evento atual.
Considerando que todos os valores constantes na enumeração ENUM_MOUSE_EVENT diferem dos valores das constantes na enumeração ENUM_MOUSE_FORM_STATE (com a mesma composição e ordem) pelo valor da constante MOUSE_EVENT_NO_EVENT da enumeração ENUM_MOUSE_EVENT, então para obter o valor correto, precisamos adicionar o valor da constante MOUSE_EVENT_NO_EVENT ao valor do evento do mouse obtido no estado da variável ENUM_MOUSE_FORM_STATE.

O texto localizado na forma, como p. e. o texto de um botão, também pode (mas não necessariamente sempre e em cada objeto, somente se desejado) mudar de cor ao passar o mouse ou clicar sobre o objeto. Os métodos para processar o texto dos objetos WinForms estão localizados na classe base do objeto WinForms no arquivo \MQL5\Include\DoEasy\Objects\Graph\WForms\WinFormBase.mqh.

Na seção protegida da classe, vamos declarar uma variável para armazenar a cor do texto inicial do objeto. Na seção pública, declararemos métodos que retornam uma lista de objetos vinculados por tipo, o número de elementos vinculados por tipo e um método que retorna um ponteiro para um objeto vinculado por tipo.
E escrevemos métodos para definir e retornar cores de texto de objeto adicionais:

//+------------------------------------------------------------------+
//| Form object class                                                |
//+------------------------------------------------------------------+
class CWinFormBase : public CForm
  {
protected:
   color             m_fore_color_init;                        // Initial color of the control text

private:
//--- Return the font flags
   uint              GetFontFlags(void);

public:
//--- Return by type the (1) list, (2) the number of bound controls, (3) the bound control by index in the list
   CArrayObj        *GetListElementsByType(const ENUM_GRAPH_ELEMENT_TYPE type);
   int               ElementsTotalByType(const ENUM_GRAPH_ELEMENT_TYPE type);
   CGCnvElement     *GetElementByType(const ENUM_GRAPH_ELEMENT_TYPE type,const int index);
//--- Clear the element filling it with color and opacity
   virtual void      Erase(const color colour,const uchar opacity,const bool redraw=false);
//--- Clear the element with a gradient fill
   virtual void      Erase(color &colors[],const uchar opacity,const bool vgradient,const bool cycle,const bool redraw=false);
//--- Clear the element completely
   virtual void      Erase(const bool redraw=false);
//--- Redraw the object
   virtual void      Redraw(bool redraw);
//--- Set the new size for the (1) current object and (2) the object specified by index
   virtual bool      Resize(const int w,const int h,const bool redraw);
   virtual bool      Resize(const int index,const int w,const int h,const bool redraw);

//--- Constructors
                     CWinFormBase(const long chart_id,
                                  const int subwindow,
                                  const string name,
                                  const int x,
                                  const int y,
                                  const int w,
                                  const int h);
                     CWinFormBase(const string name) : CForm(::ChartID(),0,name,0,0,0,0)
                       {
                        this.m_type=OBJECT_DE_TYPE_GWF_BASE; 
                       }
                       
//--- (1) Set and (2) return the default text color of all panel objects
   void              SetForeColor(const color clr,const bool set_init_color)
                       {
                        this.SetProperty(CANV_ELEMENT_PROP_FORE_COLOR,clr);
                        if(set_init_color)
                           this.SetForeColorInit(clr);
                       }
   color             ForeColor(void)                           const { return (color)this.GetProperty(CANV_ELEMENT_PROP_FORE_COLOR);                     }
//--- (1) Set and (2) return the initial default text color of all panel objects
   void              SetForeColorInit(const color clr)               { this.m_fore_color_init=clr;                                                       }
   color             ForeColorInit(void)                       const { return (color)this.m_fore_color_init;                                             }
//--- (1) Set and (2) return the default text color opacity of all panel objects
   void              SetForeColorOpacity(const uchar value)          { this.SetProperty(CANV_ELEMENT_PROP_FORE_COLOR_OPACITY,value);                     }
   uchar             ForeColorOpacity(void)                    const { return (uchar)this.GetProperty(CANV_ELEMENT_PROP_FORE_COLOR_OPACITY);             }
//--- (1) Set and (2) return the control text color when clicking the control
   void              SetForeColorMouseDown(const color clr)          { this.SetProperty(CANV_ELEMENT_PROP_FORE_COLOR_MOUSE_DOWN,clr);                    }
   color             ForeColorMouseDown(void)                  const { return (color)this.GetProperty(CANV_ELEMENT_PROP_FORE_COLOR_MOUSE_DOWN);          }
//--- (1) Set and (2) return the control text color when hovering the mouse over the control
   void              SetForeColorMouseOver(const color clr)          { this.SetProperty(CANV_ELEMENT_PROP_FORE_COLOR_MOUSE_OVER,clr);                    }
   color             ForeColorMouseOver(void)                  const { return (color)this.GetProperty(CANV_ELEMENT_PROP_FORE_COLOR_MOUSE_OVER);          }
   
//--- (1) Set and (2) return the element text
   virtual void      SetText(const string text)                      { this.SetProperty(CANV_ELEMENT_PROP_TEXT,text);                                    }
   string            Text(void)                                const { return this.GetProperty(CANV_ELEMENT_PROP_TEXT);                                  }


No construtor da classe, ao definir a cor do texto, indicamos a necessidade de preservar a cor do texto original:

//+------------------------------------------------------------------+
//| Constructor                                                      |
//+------------------------------------------------------------------+
CWinFormBase::CWinFormBase(const long chart_id,
                           const int subwindow,
                           const string name,
                           const int x,
                           const int y,
                           const int w,
                           const int h) : CForm(chart_id,subwindow,name,x,y,w,h)
  {
//--- Set the graphical element and library object types as a base WinForms object
   CGBaseObj::SetTypeElement(GRAPH_ELEMENT_TYPE_WF_BASE);
   CGCnvElement::SetProperty(CANV_ELEMENT_PROP_TYPE,GRAPH_ELEMENT_TYPE_WF_BASE);
   this.m_type=OBJECT_DE_TYPE_GWF_BASE; 
//--- Initialize all variables
   this.SetText("");
   this.SetForeColor(CLR_DEF_FORE_COLOR,true);
   this.SetForeColorOpacity(CLR_DEF_FORE_COLOR_OPACITY);
   this.SetFontBoldType(FW_TYPE_NORMAL);
   this.SetMarginAll(0);
   this.SetPaddingAll(0);
   this.SetBorderSizeAll(0);
   this.SetDockMode(CANV_ELEMENT_DOCK_MODE_NONE,false);
   this.SetBorderStyle(FRAME_STYLE_NONE);
   this.SetAutoSize(false,false);
   CForm::SetCoordXInit(x);
   CForm::SetCoordYInit(y);
   CForm::SetWidthInit(w);
   CForm::SetHeightInit(h);
   this.m_shadow=false;
   this.m_gradient_v=true;
   this.m_gradient_c=false;
  }
//+------------------------------------------------------------------+


No método que retorna a descrição da propriedade inteira do elemento, vamos escrever blocos de código para retornar as descrições das novas propriedades do objeto WInForms:

      property==CANV_ELEMENT_PROP_FORE_COLOR_OPACITY           ?  CMessage::Text(MSG_CANV_ELEMENT_PROP_FORE_COLOR_OPACITY)+
         (only_prop ? "" : !this.SupportProperty(property)     ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)this.GetProperty(property)
         )  :
      property==CANV_ELEMENT_PROP_FORE_COLOR_MOUSE_DOWN        ?  CMessage::Text(MSG_CANV_ELEMENT_PROP_FORE_COLOR_MOUSE_DOWN)+
         (only_prop ? "" : !this.SupportProperty(property)     ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+ColorToString((color)this.GetProperty(property),true)
         )  :
      property==CANV_ELEMENT_PROP_FORE_COLOR_MOUSE_OVER        ?  CMessage::Text(MSG_CANV_ELEMENT_PROP_FORE_COLOR_MOUSE_OVER)+
         (only_prop ? "" : !this.SupportProperty(property)     ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+ColorToString((color)this.GetProperty(property),true)
         )  :
      property==CANV_ELEMENT_PROP_FORE_COLOR_TOGGLE            ?  CMessage::Text(MSG_CANV_ELEMENT_PROP_FORE_COLOR_TOGGLE)+
         (only_prop ? "" : !this.SupportProperty(property)     ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+ColorToString((color)this.GetProperty(property),true)
         )  :
      property==CANV_ELEMENT_PROP_FORE_COLOR_TOGGLE_MOUSE_DOWN ?  CMessage::Text(MSG_CANV_ELEMENT_PROP_FORE_COLOR_TOGGLE_MOUSE_DOWN)+
         (only_prop ? "" : !this.SupportProperty(property)     ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+ColorToString((color)this.GetProperty(property),true)
         )  :
      property==CANV_ELEMENT_PROP_FORE_COLOR_TOGGLE_MOUSE_OVER ?  CMessage::Text(MSG_CANV_ELEMENT_PROP_FORE_COLOR_TOGGLE_MOUSE_OVER)+
         (only_prop ? "" : !this.SupportProperty(property)     ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+ColorToString((color)this.GetProperty(property),true)
         )  :
      property==CANV_ELEMENT_PROP_BACKGROUND_COLOR             ?  CMessage::Text(MSG_CANV_ELEMENT_PROP_BACKGROUND_COLOR)+
         (only_prop ? "" : !this.SupportProperty(property)     ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+ColorToString((color)this.GetProperty(property),true)
         )  :

...

      property==CANV_ELEMENT_PROP_BACKGROUND_COLOR_MOUSE_OVER  ?  CMessage::Text(MSG_CANV_ELEMENT_PROP_BACKGROUND_COLOR_MOUSE_OVER)+
         (only_prop ? "" : !this.SupportProperty(property)     ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+ColorToString((color)this.GetProperty(property),true)
         )  :
      property==CANV_ELEMENT_PROP_BACKGROUND_COLOR_TOGGLE      ?  CMessage::Text(MSG_CANV_ELEMENT_PROP_BACKGROUND_COLOR_TOGGLE)+
         (only_prop ? "" : !this.SupportProperty(property)     ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+ColorToString((color)this.GetProperty(property),true)
         )  :
      property==CANV_ELEMENT_PROP_BACKGROUND_COLOR_TOGGLE_MOUSE_DOWN ?  CMessage::Text(MSG_CANV_ELEMENT_PROP_BACKGROUND_COLOR_TOGGLE_MOUSE_DOWN)+
         (only_prop ? "" : !this.SupportProperty(property)     ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+ColorToString((color)this.GetProperty(property),true)
         )  :
      property==CANV_ELEMENT_PROP_BACKGROUND_COLOR_TOGGLE_MOUSE_OVER ?  CMessage::Text(MSG_CANV_ELEMENT_PROP_BACKGROUND_COLOR_TOGGLE_MOUSE_OVER)+
         (only_prop ? "" : !this.SupportProperty(property)     ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+ColorToString((color)this.GetProperty(property),true)
         )  :
      property==CANV_ELEMENT_PROP_BOLD_TYPE                    ?  CMessage::Text(MSG_CANV_ELEMENT_PROP_BOLD_TYPE)+
         (only_prop ? "" : !this.SupportProperty(property)     ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+FontBoldTypeDescription()
         )  :

...

      property==CANV_ELEMENT_PROP_AUTOCHECK                    ?  CMessage::Text(MSG_CANV_ELEMENT_PROP_AUTOCHECK)+
         (only_prop ? "" : !this.SupportProperty(property)     ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)(bool)this.GetProperty(property)
         )  :
      property==CANV_ELEMENT_PROP_BUTTON_TOGGLE                ?  CMessage::Text(MSG_CANV_ELEMENT_PROP_BUTTON_TOGGLE)+
         (only_prop ? "" : !this.SupportProperty(property)     ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)(bool)this.GetProperty(property)
         )  :
      property==CANV_ELEMENT_PROP_BUTTON_STATE                 ?  CMessage::Text(MSG_CANV_ELEMENT_PROP_BUTTON_STATE)+
         (only_prop ? "" : !this.SupportProperty(property)     ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)(bool)this.GetProperty(property)
         )  :
      property==CANV_ELEMENT_PROP_CHECK_BACKGROUND_COLOR       ?  CMessage::Text(MSG_CANV_ELEMENT_PROP_CHECK_BACKGROUND_COLOR)+
         (only_prop ? "" : !this.SupportProperty(property)     ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+::ColorToString((color)this.GetProperty(property),true)
         )  :
      property==CANV_ELEMENT_PROP_CHECK_BACKGROUND_COLOR_OPACITY  ?  CMessage::Text(MSG_CANV_ELEMENT_PROP_CHECK_BACKGROUND_COLOR_OPACITY)+
         (only_prop ? "" : !this.SupportProperty(property)     ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)this.GetProperty(property)
         )  :
      property==CANV_ELEMENT_PROP_CHECK_BACKGROUND_COLOR_MOUSE_DOWN  ?  CMessage::Text(MSG_CANV_ELEMENT_PROP_CHECK_BACKGROUND_COLOR_MOUSE_DOWN)+
         (only_prop ? "" : !this.SupportProperty(property)     ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+::ColorToString((color)this.GetProperty(property),true)
         )  :
      property==CANV_ELEMENT_PROP_CHECK_BACKGROUND_COLOR_MOUSE_OVER  ?  CMessage::Text(MSG_CANV_ELEMENT_PROP_CHECK_BACKGROUND_COLOR_MOUSE_OVER)+
         (only_prop ? "" : !this.SupportProperty(property)     ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+::ColorToString((color)this.GetProperty(property),true)
         )  :
      property==CANV_ELEMENT_PROP_CHECK_FORE_COLOR             ?  CMessage::Text(MSG_CANV_ELEMENT_PROP_CHECK_FORE_COLOR)+
         (only_prop ? "" : !this.SupportProperty(property)     ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+::ColorToString((color)this.GetProperty(property),true)
         )  :
      property==CANV_ELEMENT_PROP_CHECK_FORE_COLOR_OPACITY     ?  CMessage::Text(MSG_CANV_ELEMENT_PROP_CHECK_FORE_COLOR_OPACITY)+
         (only_prop ? "" : !this.SupportProperty(property)     ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)this.GetProperty(property)
         )  :
      property==CANV_ELEMENT_PROP_CHECK_FORE_COLOR_MOUSE_DOWN  ?  CMessage::Text(MSG_CANV_ELEMENT_PROP_CHECK_FORE_COLOR_MOUSE_DOWN)+
         (only_prop ? "" : !this.SupportProperty(property)     ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+::ColorToString((color)this.GetProperty(property),true)
         )  :
      property==CANV_ELEMENT_PROP_CHECK_FORE_COLOR_MOUSE_OVER  ?  CMessage::Text(MSG_CANV_ELEMENT_PROP_CHECK_FORE_COLOR_MOUSE_OVER)+
         (only_prop ? "" : !this.SupportProperty(property)     ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+::ColorToString((color)this.GetProperty(property),true)
         )  :
      property==CANV_ELEMENT_PROP_CHECK_FLAG_COLOR             ?  CMessage::Text(MSG_CANV_ELEMENT_PROP_CHECK_FLAG_COLOR)+
         (only_prop ? "" : !this.SupportProperty(property)     ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+::ColorToString((color)this.GetProperty(property),true)
         )  :
      property==CANV_ELEMENT_PROP_CHECK_FLAG_COLOR_OPACITY     ?  CMessage::Text(MSG_CANV_ELEMENT_PROP_CHECK_FLAG_COLOR_OPACITY)+
         (only_prop ? "" : !this.SupportProperty(property)     ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)this.GetProperty(property)
         )  :
      property==CANV_ELEMENT_PROP_CHECK_FLAG_COLOR_MOUSE_DOWN  ?  CMessage::Text(MSG_CANV_ELEMENT_PROP_CHECK_FLAG_COLOR_MOUSE_DOWN)+
         (only_prop ? "" : !this.SupportProperty(property)     ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+::ColorToString((color)this.GetProperty(property),true)
         )  :
      property==CANV_ELEMENT_PROP_CHECK_FLAG_COLOR_MOUSE_OVER  ?  CMessage::Text(MSG_CANV_ELEMENT_PROP_CHECK_FLAG_COLOR_MOUSE_OVER)+
         (only_prop ? "" : !this.SupportProperty(property)     ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+::ColorToString((color)this.GetProperty(property),true)
         )  :
      ""
     );
  }
//+------------------------------------------------------------------+
//| Return the description of the control real property              |
//+------------------------------------------------------------------+


Método que retorna uma lista de elementos vinculados por tipo:

//+------------------------------------------------------------------+
// Return the list of bound controls by type                         |
//+------------------------------------------------------------------+
CArrayObj *CWinFormBase::GetListElementsByType(const ENUM_GRAPH_ELEMENT_TYPE type)
  {
   return CSelect::ByGraphCanvElementProperty(this.GetListElements(),CANV_ELEMENT_PROP_TYPE,type,EQUAL);
  }
//+------------------------------------------------------------------+

Aqui nós simplesmente retornamos uma lista de elementos gráficos selecionados a partir da lista de objetos vinculados pelo tipo especificado.


Método que retorna o elemento vinculado por tipo por índice na lista:

//+------------------------------------------------------------------+
//| Get (by type) the bound element by index in the list             |
//+------------------------------------------------------------------+
CGCnvElement *CWinFormBase::GetElementByType(const ENUM_GRAPH_ELEMENT_TYPE type,const int index)
  {
   CArrayObj *list=this.GetListElementsByType(type);
   return list.At(index);
  }
//+------------------------------------------------------------------+

Aqui: obtemos uma lista de objetos pelo tipo especificado pelo método discutido acima e retornamos um ponteiro para o objeto na lista resultante pelo índice especificado. Se a lista não for recebida ou o índice estiver fora do tamanho da lista, o método retornará NULL.


Método que retorna o número de elementos vinculados por tipo:

//+------------------------------------------------------------------+
//| Get the list of bound elements by type                           |
//+------------------------------------------------------------------+
int CWinFormBase::ElementsTotalByType(const ENUM_GRAPH_ELEMENT_TYPE type)
  {
   CArrayObj *list=this.GetListElementsByType(type);
   return(list!=NULL ? list.Total() : 0);
  }
//+------------------------------------------------------------------+

Obtemos uma lista de objetos vinculados pelo tipo especificado e retornamos o número de ponteiros para objetos nesta lista. Se a lista não puder ser obtida, o método retornará zero.

Na classe do objeto base do contêiner WinForms no arquivo \MQL5\Include\DoEasy\Objects\Graph\WForms\Containers\Container.mqh , renomearemos os métodos para definir a largura da moldura. Anteriormente, usávamos "Frame" no nome e agora usamos "Border":

//--- Set the width of the form frame (1) to the left, (2) at the top, (3) to the right, (4) at the bottom and (5) on all sides of the control
   virtual void      SetBorderSizeLeft(const uint value)
                       {
                        CForm::SetBorderSizeLeft(value);
                        if(this.PaddingLeft()<this.BorderSizeLeft())
                           this.SetPaddingLeft(this.BorderSizeLeft());
                       }
   virtual void      SetBorderSizeTop(const uint value)
                       {
                        CForm::SetBorderSizeTop(value);
                        if(this.PaddingTop()<this.BorderSizeTop())
                           this.SetPaddingTop(this.BorderSizeTop());
                       }
   virtual void      SetBorderSizeRight(const uint value)
                       {
                        CForm::SetBorderSizeRight(value);
                        if(this.PaddingRight()<this.BorderSizeRight())
                           this.SetPaddingRight(this.BorderSizeRight());
                       }
   virtual void      SetBorderSizeBottom(const uint value)
                       {
                        CForm::SetBorderSizeBottom(value);
                        if(this.PaddingBottom()<this.BorderSizeBottom())
                           this.SetPaddingBottom(this.BorderSizeBottom());
                       }

Importante: se anteriormente esses métodos tinham um nome diferente e chamar o método da classe pai para definir a largura da borda não causava problemas, agora esse método se chamará recursivamente. Portanto, aqui indicamos explicitamente o contexto a partir do qual o método desejado da classe pai é chamado, ele tem exatamente o mesmo nome.

Nos construtores de classe, ao salvar a cor do texto, indicamos a necessidade de preservar a cor inicial dos rótulos do elemento:

//--- Constructors
                     CContainer(const long chart_id,
                                const int subwindow,
                                const string name,
                                const int x,
                                const int y,
                                const int w,
                                const int h);
                     CContainer(const string name) : CWinFormBase(::ChartID(),0,name,0,0,0,0)
                       {
                        CGBaseObj::SetTypeElement(GRAPH_ELEMENT_TYPE_WF_CONTAINER);
                        CGCnvElement::SetProperty(CANV_ELEMENT_PROP_TYPE,GRAPH_ELEMENT_TYPE_WF_CONTAINER);
                        this.m_type=OBJECT_DE_TYPE_GWF_CONTAINER; 
                        this.SetForeColor(CLR_DEF_FORE_COLOR,true);
                        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,false);
                        this.SetAutoScrollMarginAll(0);
                        this.SetAutoSize(false,false);
                        this.SetAutoSizeMode(CANV_ELEMENT_AUTO_SIZE_MODE_GROW,false);
                        this.Initialize();
                       }
//--- Destructor
                    ~CContainer();
  };
//+------------------------------------------------------------------+
//| Constructor indicating the chart and subwindow ID                |
//+------------------------------------------------------------------+
CContainer::CContainer(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_CONTAINER);
   CGCnvElement::SetProperty(CANV_ELEMENT_PROP_TYPE,GRAPH_ELEMENT_TYPE_WF_CONTAINER);
   this.m_type=OBJECT_DE_TYPE_GWF_CONTAINER;
   this.SetForeColor(CLR_DEF_FORE_COLOR,true);
   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,false);
   this.SetAutoScrollMarginAll(0);
   this.SetAutoSize(false,false);
   this.SetAutoSizeMode(CANV_ELEMENT_AUTO_SIZE_MODE_GROW,false);
   this.Initialize();
   this.SetCoordXInit(x);
   this.SetCoordYInit(y);
   this.SetWidthInit(w);
   this.SetHeightInit(h);
  }
//+------------------------------------------------------------------+


No método que cria um novo elemento anexado, indicamos em todos os lugares a necessidade de preservar a cor original:

//+------------------------------------------------------------------+
//| Create a new attached element                                    |
//+------------------------------------------------------------------+
bool CContainer::CreateNewElement(const ENUM_GRAPH_ELEMENT_TYPE element_type,
                                  const int x,
                                  const int y,
                                  const int w,
                                  const int h,
                                  const color colour,
                                  const uchar opacity,
                                  const bool activity,
                                  const bool redraw)
  {
//--- If the object type is less than the base WinForms object
   if(element_type<GRAPH_ELEMENT_TYPE_WF_BASE)
     {
      //--- report the error and return 'false'
      CMessage::ToLog(DFUN,MSG_PANEL_OBJECT_ERR_OBJ_MUST_BE_WFBASE);
      return false;
     }
//--- If failed to create a new graphical element, return 'false'
   CWinFormBase *obj=CForm::CreateAndAddNewElement(element_type,x,y,w,h,colour,opacity,activity);
   if(obj==NULL)
      return false;
//--- Set the text color of the created object as that of the base panel
   obj.SetForeColor(this.ForeColor(),true);

//--- Depending on the created object type,
   switch(obj.TypeGraphElement())
     {
      //--- For the Container, Panel and GroupBox WinForms objects
      case GRAPH_ELEMENT_TYPE_WF_CONTAINER :
      case GRAPH_ELEMENT_TYPE_WF_PANEL :
      case GRAPH_ELEMENT_TYPE_WF_GROUPBOX :
        //--- set the frame color equal to the background color 
        obj.SetBorderColor(obj.BackgroundColor(),true);
        break;
      //--- For the Text Label, CheckBox and RadioButton WinForms objects
      case GRAPH_ELEMENT_TYPE_WF_LABEL       :
      case GRAPH_ELEMENT_TYPE_WF_CHECKBOX    :
      case GRAPH_ELEMENT_TYPE_WF_RADIOBUTTON :
        //--- set the object text color depending on the one passed to the method:
        //--- either the container text color, or the one passed to the method.
        //--- The frame color is set equal to the text color
        obj.SetForeColor(colour==clrNONE ? this.ForeColor() : colour,true);
        obj.SetBorderColor(obj.ForeColor(),true);
        break;
      //--- For the Button WinForms object
      case GRAPH_ELEMENT_TYPE_WF_BUTTON      :
        //--- set the object text color as a container text color depending on the one passed to the method:
        //--- set the background color depending on the one passed to the method:
        //--- either the default standard control background color, or the one passed to the method.
        //--- The frame color is set equal to the text color
        obj.SetForeColor(this.ForeColor(),true);
        obj.SetBackgroundColor(colour==clrNONE ? CLR_DEF_CONTROL_STD_BACK_COLOR : colour,true);
        obj.SetBorderColor(obj.ForeColor(),true);
        obj.SetBorderStyle(FRAME_STYLE_SIMPLE);
        break;
      default:
        break;
     }
//--- If the panel has auto resize enabled and features bound objects, call the resize method
   if(this.AutoSize() && this.ElementsTotal()>0)
      this.AutoSizeProcess(redraw);
//--- Redraw the panel and all added objects, and return 'true'
   this.Redraw(redraw);
   return true;
  }
//+------------------------------------------------------------------+


No arquivo \MQL5\Include\DoEasy\Objects\Graph\WForms\Containers\GroupBox.mqh da classe do objeto WinForms GroupBox, em seu método de inicialização Initialize(), escrevemos a necessidade de preservar as cores originais ao definir as cores padrão:

//--- Create an animation object and add it to the list for storing such objects
   this.m_animations=new CAnimations(CGCnvElement::GetObject());
   this.m_list_tmp.Add(this.m_animations);
//--- Set a transparent background for the object background and the default color for the frame
   this.SetBackgroundColor(CLR_CANV_NULL,true);
   this.SetOpacity(0);
   this.SetBorderColor(CLR_DEF_FRAME_GBOX_COLOR,true);
//--- Set the default color and text opacity, as well as the absence of the object frame
   this.SetForeColor(CLR_DEF_FORE_COLOR,true);
   this.SetForeColorOpacity(CLR_DEF_FORE_COLOR_OPACITY);
   this.SetBorderStyle(FRAME_STYLE_SIMPLE);
//--- Set the default text parameters


No arquivo \MQL5\Include\DoEasy\Objects\Graph\WForms\Common Controls\CommonBase.mqh da classe do objeto padrão base WinForms, em seu método de inicialização Initialize(), especificaremos a necessidade de preservar as cores originais ao definir as cores padrão:

//--- Create an animation object and add it to the list for storing such objects
   this.m_animations=new CAnimations(CGCnvElement::GetObject());
   this.m_list_tmp.Add(this.m_animations);
//--- Set the transparent color for the object background
   this.SetBackgroundColor(CLR_CANV_NULL,true);
   this.SetOpacity(0);
//--- Set the default color and text opacity, as well as the absence of the object frame
   this.SetForeColor(CLR_DEF_FORE_COLOR,true);
   this.SetForeColorOpacity(CLR_DEF_FORE_COLOR_OPACITY);
   this.SetBorderStyle(FRAME_STYLE_NONE);
//--- Set the default text parameters


O objeto WinForms CheckBox consiste em um campo com uma caixa de seleção e texto. Quando o cursor do mouse é passado e o botão é pressionado, a cor do próprio campo, sua borda e a caixa de seleção mudam no objeto. Portanto, para todos esses componentes, precisamos criar variáveis para armazenar sua cor inicial e métodos para definir as cores correspondentes ao evento do mouse.

No arquivo \MQL5\Include\DoEasy\Objects\Graph\WForms\Common Controls\CheckBox.mqh, na seção protected da classe, vamos declarar variáveis para armazenar cores iniciais e métodos manipuladores virtuais de eventos de mouse:

//+------------------------------------------------------------------+
//| CheckBox object class of the WForms controls                     |
//+------------------------------------------------------------------+
class CCheckBox : public CLabel
  {
private:
//--- Set X and Y checkbox coordinates
   void              SetCheckFlagCoords(int &x,int &y);
//--- Set the corrected text coordinates depending on the text alignment and checkbox
   void              SetCorrectTextCoords(void);

protected:
   int               m_text_x;                                       // Text X coordinate
   int               m_text_y;                                       // Text Y coordinate
   int               m_check_x;                                      // Checkbox X coordinate
   int               m_check_y;                                      // Checkbox Y coordinate
   int               m_check_w;                                      // Checkbox width
   int               m_check_h;                                      // Checkbox height
   color             m_check_back_color_init;                        // Initial color of the checkbox background
   color             m_check_border_color_init;                      // Initial color of the checkbox background frame
   color             m_check_flag_color_init;                        // Initial color of the checkbox
//--- Set the element width and height automatically
   virtual bool      AutoSetWH(void);
//--- Displays the checkbox for the specified state
   virtual void      ShowControlFlag(const ENUM_CANV_ELEMENT_CHEK_STATE state);
   
//--- (1) Set and (2) return the checkbox size on the element
   void              SetCheckWidth(const int width)                  { this.m_check_w=(width<5  ? 5 : width);  }
   void              SetCheckHeight(const int height)                { this.m_check_h=(height<5 ? 5 : height); }
   int               CheckWidth(void)                          const { return this.m_check_w;                  }
   int               CheckHeight(void)                         const { return this.m_check_h;                  }

//--- 'The cursor is inside the active area, the mouse buttons are not clicked' event handler
   virtual void      MouseActiveAreaNotPressedHandler(const int id,const long& lparam,const double& dparam,const string& sparam);
//--- 'The cursor is inside the active area, a mouse button is clicked (any)' event handler
   virtual void      MouseActiveAreaPressedHandler(const int id,const long& lparam,const double& dparam,const string& sparam);
//--- 'The cursor is inside the active area, the left mouse button is clicked' event handler
   virtual void      MouseActiveAreaReleasedHandler(const int id,const long& lparam,const double& dparam,const string& sparam);

public:


Iremos alterar os métodos para definir a cor de fundo da caixa de seleção, a moldura do campo e a própria caixa de seleção para que seja possível salvar a cor original do campo pelo valor do sinalizador. E adicionamos métodos para definir e retornar cores iniciais e também declaramos um manipulador para o último evento do mouse:

//--- (1) Set and (2) return the flag of the checkbox auto change when it is selected
   void              SetAutoCheck(const bool flag)                   { this.SetProperty(CANV_ELEMENT_PROP_AUTOCHECK,flag);                                  }
   bool              AutoCheck(void)                           const { return (bool)this.GetProperty(CANV_ELEMENT_PROP_AUTOCHECK);                          }
   
//--- (1) Set and (2) return the control verification checkbox background color
   void              SetCheckBackgroundColor(const color clr,const bool set_init_color)
                       {
                        this.SetProperty(CANV_ELEMENT_PROP_CHECK_BACKGROUND_COLOR,clr);
                        if(set_init_color)
                           this.SetCheckBackgroundColorInit(clr);
                       }
   color             CheckBackgroundColor(void)                   const { return (color)this.GetProperty(CANV_ELEMENT_PROP_CHECK_BACKGROUND_COLOR);         }
//--- (1) Set and (2) return the control verification checkbox background color opacity
   void              SetCheckBackgroundColorOpacity(const uchar value)  { this.SetProperty(CANV_ELEMENT_PROP_CHECK_BACKGROUND_COLOR_OPACITY,value);         }
   uchar             CheckBackgroundColorOpacity(void)            const { return (uchar)this.GetProperty(CANV_ELEMENT_PROP_CHECK_BACKGROUND_COLOR_OPACITY); }
//--- (1) Set and (2) return the color of control checkbox background when clicking the control
   void              SetCheckBackgroundColorMouseDown(const color clr)  { this.SetProperty(CANV_ELEMENT_PROP_CHECK_BACKGROUND_COLOR_MOUSE_DOWN,clr);        }
   color             CheckBackgroundColorMouseDown(void)          const { return (color)this.GetProperty(CANV_ELEMENT_PROP_CHECK_BACKGROUND_COLOR_MOUSE_DOWN);}
//--- (1) Set and (2) return the color of control checkbox background when hovering the mouse over the control
   void              SetCheckBackgroundColorMouseOver(const color clr)  { this.SetProperty(CANV_ELEMENT_PROP_CHECK_BACKGROUND_COLOR_MOUSE_OVER,clr);        }
   color             CheckBackgroundColorMouseOver(void)          const { return (color)this.GetProperty(CANV_ELEMENT_PROP_CHECK_BACKGROUND_COLOR_MOUSE_OVER);}
//--- (1) Set and (2) return the initial color of the control verification checkbox background
   void              SetCheckBackgroundColorInit(const color clr)       { this.m_check_back_color_init=clr;                                                 }
   color             CheckBackgroundColorInit(void)               const { return (color)this.m_check_back_color_init;                                       }

//--- (1) Set and (2) return the control checkbox frame color
   void              SetCheckBorderColor(const color clr,const bool set_init_color)
                       {
                        this.SetProperty(CANV_ELEMENT_PROP_CHECK_FORE_COLOR,clr);
                        if(set_init_color)
                           this.SetCheckBorderColorInit(clr);
                       }
   color             CheckBorderColor(void)                       const { return (color)this.GetProperty(CANV_ELEMENT_PROP_CHECK_FORE_COLOR);               }
//--- (1) Set and (2) return the control checkbox frame color opacity
   void              SetCheckBorderColorOpacity(const uchar value)      { this.SetProperty(CANV_ELEMENT_PROP_CHECK_FORE_COLOR_OPACITY,value);               }
   uchar             CheckBorderColorOpacity(void)                const { return (uchar)this.GetProperty(CANV_ELEMENT_PROP_CHECK_FORE_COLOR_OPACITY);       }
//--- (1) Set and (2) return the color of control checkbox frame color when clicking on the control
   void              SetCheckBorderColorMouseDown(const color clr)      { this.SetProperty(CANV_ELEMENT_PROP_CHECK_FORE_COLOR_MOUSE_DOWN,clr);              }
   color             CheckBorderColorMouseDown(void)              const { return (color)this.GetProperty(CANV_ELEMENT_PROP_CHECK_FORE_COLOR_MOUSE_DOWN);    }
//--- (1) Set and (2) return the color of the control checkbox frame color when hovering the mouse over the control
   void              SetCheckBorderColorMouseOver(const color clr)      { this.SetProperty(CANV_ELEMENT_PROP_CHECK_FORE_COLOR_MOUSE_OVER,clr);              }
   color             CheckBorderColorMouseOver(void)              const { return (color)this.GetProperty(CANV_ELEMENT_PROP_CHECK_FORE_COLOR_MOUSE_OVER);    }
//--- (1) Set and (2) return the initial color of the control verification checkbox frame
   void              SetCheckBorderColorInit(const color clr)           { this.m_check_border_color_init=clr;                                               }
   color             CheckBorderColorInit(void)                   const { return (color)this.m_check_border_color_init;                                     }

//--- (1) Set and (2) return the control verification checkbox color
   void              SetCheckFlagColor(const color clr,const bool set_init_color)
                       {
                        this.SetProperty(CANV_ELEMENT_PROP_CHECK_FLAG_COLOR,clr);
                        if(set_init_color)
                           this.SetCheckFlagColorInit(clr);
                       }
   color             CheckFlagColor(void)                         const { return (color)this.GetProperty(CANV_ELEMENT_PROP_CHECK_FLAG_COLOR);               }
//--- (1) Set and (2) return the control verification checkbox color opacity
   void              SetCheckFlagColorOpacity(const uchar value)        { this.SetProperty(CANV_ELEMENT_PROP_CHECK_FLAG_COLOR_OPACITY,value);               }
   uchar             CheckFlagColorOpacity(void)                  const { return (uchar)this.GetProperty(CANV_ELEMENT_PROP_CHECK_FLAG_COLOR_OPACITY);       }
//--- (1) Set and (2) return the color of control checkbox when clicking on the control
   void              SetCheckFlagColorMouseDown(const color clr)        { this.SetProperty(CANV_ELEMENT_PROP_CHECK_FLAG_COLOR_MOUSE_DOWN,clr);              }
   color             CheckFlagColorMouseDown(void)                const { return (color)this.GetProperty(CANV_ELEMENT_PROP_CHECK_FLAG_COLOR_MOUSE_DOWN); }
//--- (1) Set and (2) return the color of the control checkbox when hovering the mouse over the control
   void              SetCheckFlagColorMouseOver(const color clr)        { this.SetProperty(CANV_ELEMENT_PROP_CHECK_FLAG_COLOR_MOUSE_OVER,clr);              }
   color             CheckFlagColorMouseOver(void)                const { return (color)this.GetProperty(CANV_ELEMENT_PROP_CHECK_FLAG_COLOR_MOUSE_OVER);    }
//--- (1) Set and (2) return the initial color of the control verification checkbox
   void              SetCheckFlagColorInit(const color clr)             { this.m_check_flag_color_init=clr;                                                 }
   color             CheckFlagColorInit(void)                     const { return (color)this.m_check_flag_color_init;                                       }
   
//--- Last mouse event handler
   virtual void      OnMouseEventPostProcessing(void);
   
//--- Redraw the object
   virtual void      Redraw(bool redraw);

//--- Constructor


No construtor de classe, definimos as cores padrão para vários eventos e estados do mouse:

//+------------------------------------------------------------------+
//| Constructor                                                      |
//+------------------------------------------------------------------+
CCheckBox::CCheckBox(const long chart_id,
                     const int subwindow,
                     const string name,
                     const int x,
                     const int y,
                     const int w,
                     const int h) : CLabel(chart_id,subwindow,name,x,y,w,h)
  {
   CGBaseObj::SetTypeElement(GRAPH_ELEMENT_TYPE_WF_CHECKBOX);
   CGCnvElement::SetProperty(CANV_ELEMENT_PROP_TYPE,GRAPH_ELEMENT_TYPE_WF_CHECKBOX);
   this.m_type=OBJECT_DE_TYPE_GWF_COMMON;
   this.SetCoordX(x);
   this.SetCoordY(y);
   this.SetCheckWidth(DEF_CHECK_SIZE);
   this.SetCheckHeight(DEF_CHECK_SIZE);
   this.SetWidth(w);
   this.SetHeight(h);
   this.Initialize();
   this.SetForeColorMouseDown(CLR_DEF_FORE_COLOR_MOUSE_DOWN);
   this.SetForeColorMouseOver(CLR_DEF_FORE_COLOR_MOUSE_OVER);
   this.SetCheckBackgroundColor(CLR_DEF_CHECK_BACK_COLOR,true);
   this.SetCheckBackgroundColorMouseDown(CLR_DEF_CHECK_BACK_MOUSE_DOWN);
   this.SetCheckBackgroundColorMouseOver(CLR_DEF_CHECK_BACK_MOUSE_OVER);
   this.SetCheckBorderColor(CLR_DEF_CHECK_BORDER_COLOR,true);
   this.SetCheckBorderColorMouseDown(CLR_DEF_CHECK_BORDER_MOUSE_DOWN);
   this.SetCheckBorderColorMouseOver(CLR_DEF_CHECK_BORDER_MOUSE_OVER);
   this.SetCheckFlagColor(CLR_DEF_CHECK_FLAG_COLOR,true);
   this.SetCheckFlagColorMouseDown(CLR_DEF_CHECK_FLAG_MOUSE_DOWN);
   this.SetCheckFlagColorMouseOver(CLR_DEF_CHECK_FLAG_MOUSE_OVER);
   this.SetWidthInit(this.Width());
   this.SetHeightInit(this.Height());
   this.SetCoordXInit(x);
   this.SetCoordYInit(y);
   this.SetTextAlign(ANCHOR_LEFT);
   this.m_text_x=0;
   this.m_text_y=0;
   this.m_check_x=0;
   this.m_check_y=0;
   this.Redraw(false);
  }
//+------------------------------------------------------------------+


Nem todos os manipuladores de eventos de mouse precisam ser redefinidos aqui. Vejamos os métodos substituídos da classe pai.

Manipulador do evento “Cursor dentro da área ativa, botões do mouse não pressionados”:

//+------------------------------------------------------------------+
//| 'The cursor is inside the active area,                           |
//| no mouse buttons are clicked' event handler                      |
//+------------------------------------------------------------------+
void CCheckBox::MouseActiveAreaNotPressedHandler(const int id,const long& lparam,const double& dparam,const string& sparam)
  {
   this.SetCheckBackgroundColor(this.CheckBackgroundColorMouseOver(),false);
   this.SetCheckBorderColor(this.CheckBorderColorMouseOver(),false);
   this.SetCheckFlagColor(this.CheckFlagColorMouseOver(),false);
   this.Redraw(true);
  }
//+------------------------------------------------------------------+

Se o cursor do mouse estiver sobre a área ativa do objeto, definimos as novas cores de fundo para o campo da caixa de seleção, seu quadro e a própria caixa de seleção, definimos para esse estado do mouse e do objeto e redesenhamos o objeto.


Manipulador do evento “Cursor dentro da área ativa, botão do mouse pressionado (qualquer)”:

//+------------------------------------------------------------------+
//| 'The cursor is inside the active area,                           |
//| a mouse button is clicked (any)                                  |
//+------------------------------------------------------------------+
void CCheckBox::MouseActiveAreaPressedHandler(const int id,const long& lparam,const double& dparam,const string& sparam)
  {
   this.SetCheckBackgroundColor(this.CheckBackgroundColorMouseDown(),false);
   this.SetCheckBorderColor(this.CheckBorderColorMouseDown(),false);
   this.SetCheckFlagColor(this.CheckFlagColorMouseDown(),false);
   this.Redraw(false);
  }
//+------------------------------------------------------------------+

Se o cursor do mouse estiver sobre a área ativa do objeto, definimos as novas cores de fundo para o campo da caixa de seleção, seu quadro e a própria caixa de seleção, definimos para esse estado do mouse e do objeto e redesenhamos o objeto.


Manipulador do evento “Cursor dentro da área ativa, botão do mouse liberado (esquerdo)”:

//+------------------------------------------------------------------+
//| 'The cursor is inside the active area,                           |
//| left mouse button released                                       |
//+------------------------------------------------------------------+
void CCheckBox::MouseActiveAreaReleasedHandler(const int id,const long& lparam,const double& dparam,const string& sparam)
  {
//--- The mouse button released outside the element means refusal to interact with the element
   if(lparam<this.CoordX() || lparam>this.RightEdge() || dparam<this.CoordY() || dparam>this.BottomEdge())
     {
      this.SetCheckBackgroundColor(this.BackgroundColorInit(),false);
      this.SetCheckBorderColor(this.CheckBorderColorInit(),false);
      this.SetCheckFlagColor(this.CheckFlagColorInit(),false);
      //--- Send a test entry to the journal
      Print(DFUN_ERR_LINE,TextByLanguage("Отмена","Cancel"));
     }
//--- The mouse button released within the element means a  click on the control
   else
     {
      this.SetCheckBackgroundColor(this.CheckBackgroundColorMouseOver(),false);
      this.SetCheckBorderColor(this.CheckBorderColorMouseOver(),false);
      this.SetCheckFlagColor(this.CheckFlagColorInit(),false);
      this.SetChecked(!this.Checked());
      //--- Send a test entry to the journal
      Print(DFUN_ERR_LINE,TextByLanguage("Щелчок","Click"));
     }
   this.Redraw(false);
  }
//+------------------------------------------------------------------+

Existem dois estados tratados aqui:

quando o botão do mouse é liberado e o cursor está fora da área ativa do objeto, e
quando o botão do mouse é liberado e o cursor está dentro da área ativa do objeto.

Se o cursor estiver fora do objeto, isso significa que o usuário clicou no botão, moveu o cursor para o lado e soltou o botão. Em tal situação, a caixa de seleção não deve alterar seu estado, porque isso é uma recusa em interagir com o objeto. Portanto, definimos as cores iniciais do objeto para a caixa de seleção e seu campo.

Se o cursor estiver dentro do objeto, isso significa que o usuário pressionou o botão do mouse e o soltou, o que deve levar a uma alteração no estado da caixa de seleção. Assim, alteramos a cor do campo, da moldura do campo e da caixa de seleção para aquelas que são definidas para o objeto quando o cursor passa sobre a área ativa (afinal, mesmo com o botão do mouse solto, o cursor permanecia sobre a área ativa, então a cor deve ser a mesma de quando passamos o mouse sobre o objeto) e definimos o estado oposto da caixa de seleção.
Quando terminar, redesenhamos o objeto. Para cada uma das situações processadas, enviaremos uma entrada de teste sobre o estado do sinalizador para o log. Posteriormente, quando o sinalizador for acionado, a exibição da entrada de log será substituída pelo envio de um evento para seu processamento posterior.


Manipulador virtual para o último evento do mouse:

//+------------------------------------------------------------------+
//| Last mouse event handler                                         |
//+------------------------------------------------------------------+
void CCheckBox::OnMouseEventPostProcessing(void)
  {
   ENUM_MOUSE_FORM_STATE state=GetMouseState();
   switch(state)
     {
      //--- The cursor is outside the form, the mouse buttons are not clicked
      //--- The cursor is outside the form, any mouse button is clicked
      //--- The cursor is outside the form, the mouse wheel is being scrolled
      case MOUSE_FORM_STATE_OUTSIDE_FORM_NOT_PRESSED :
      case MOUSE_FORM_STATE_OUTSIDE_FORM_PRESSED     :
      case MOUSE_FORM_STATE_OUTSIDE_FORM_WHEEL       :
        if(MouseEventLast()==MOUSE_EVENT_INSIDE_ACTIVE_AREA_NOT_PRESSED)
          {
           this.SetBackgroundColor(this.BackgroundColorInit(),false);
           this.SetBorderColor(this.BorderColorInit(),false);
           this.SetCheckBackgroundColor(this.CheckBackgroundColorInit(),false);
           this.SetCheckBorderColor(this.CheckBorderColorInit(),false);
           this.SetCheckFlagColor(this.CheckFlagColorInit(),false);
           this.m_mouse_event_last=ENUM_MOUSE_EVENT(state+MOUSE_EVENT_NO_EVENT);
           this.Redraw(false);
          }
        break;

      //--- The cursor is inside the form, the mouse buttons are not clicked
      case MOUSE_FORM_STATE_INSIDE_FORM_NOT_PRESSED :
        break;
      //--- The cursor is inside the form, any mouse button is clicked
      case MOUSE_FORM_STATE_INSIDE_FORM_PRESSED :
        break;
      //--- The cursor is inside the form, the mouse wheel is being scrolled
      case MOUSE_FORM_STATE_INSIDE_FORM_WHEEL :
        break;
      //--- The cursor is inside the active area, the mouse buttons are not clicked
      case MOUSE_FORM_STATE_INSIDE_ACTIVE_AREA_NOT_PRESSED :
        break;
      //--- The cursor is inside the active area, any mouse button is clicked
      case MOUSE_FORM_STATE_INSIDE_ACTIVE_AREA_PRESSED :
        break;
      //--- The cursor is inside the active area, the mouse wheel is being scrolled
      case MOUSE_FORM_STATE_INSIDE_ACTIVE_AREA_WHEEL :
        break;
      //--- The cursor is inside the active area, left mouse button is released
      case MOUSE_FORM_STATE_INSIDE_ACTIVE_AREA_RELEASED :
        break;
      //--- The cursor is within the window scrolling area, the mouse buttons are not clicked
      case MOUSE_FORM_STATE_INSIDE_SCROLL_AREA_NOT_PRESSED :
        break;
      //--- The cursor is within the window scrolling area, any mouse button is clicked
      case MOUSE_FORM_STATE_INSIDE_SCROLL_AREA_PRESSED :
        break;
      //--- The cursor is within the window scrolling area, the mouse wheel is being scrolled
      case MOUSE_FORM_STATE_INSIDE_SCROLL_AREA_WHEEL :
        break;
      //--- MOUSE_EVENT_NO_EVENT
      default:
        break;
     }
  }
//+------------------------------------------------------------------+

Aqui, apenas o estado quando o mouse está fora do objeto é processado, e o estado anterior era "Cursor dentro da área ativa e botão do mouse não pressionado". Em tal situação, definimos todas as propriedades de cores mutáveis do objeto para seus valores originais e atribuímos o estado atual ao último evento do mouse.


A classe do objeto WinForms RadioButton no arquivo \MQL5\Include\DoEasy\Objects\Graph\WForms\Common Controls\RadioButton.mqh é herdada da classe recém-aprimorada do objeto CheckBox, então aqui temos que fazer menos modificações.

Nesse objeto, a caixa de seleção está sempre marcada, e sua desmarcação depende de outro objeto semelhante com o qual trabalha em pares - se a caixa de seleção estiver marcada em um objeto, ela será desmarcada no outro e vice-versa. Assim, aqui só precisamos substituir um método virtual da classe pai:

//+------------------------------------------------------------------+
//| CheckBox object class of the WForms controls                     |
//+------------------------------------------------------------------+
class CRadioButton : public CCheckBox
  {
private:

protected:
//--- Displays the checkbox for the specified state
   virtual void      ShowControlFlag(const ENUM_CANV_ELEMENT_CHEK_STATE state);

//--- 'The cursor is inside the active area, the left mouse button is clicked' event handler
   virtual void      MouseActiveAreaReleasedHandler(const int id,const long& lparam,const double& dparam,const string& sparam);

public:


Manipulador do evento “Cursor dentro da área ativa, botão do mouse liberado (esquerdo)”:

//+------------------------------------------------------------------+
//| 'The cursor is inside the active area,                           |
//| left mouse button released                                       |
//+------------------------------------------------------------------+
void CRadioButton::MouseActiveAreaReleasedHandler(const int id,const long& lparam,const double& dparam,const string& sparam)
  {
//--- The mouse button released outside the element means refusal to interact with the element
   if(lparam<this.CoordX() || lparam>this.RightEdge() || dparam<this.CoordY() || dparam>this.BottomEdge())
     {
      this.SetCheckBackgroundColor(this.BackgroundColorInit(),false);
      this.SetCheckBorderColor(this.CheckBorderColorInit(),false);
      this.SetCheckFlagColor(this.CheckFlagColorInit(),false);
      Print(DFUN_ERR_LINE,TextByLanguage("Отмена","Cancel"));
     }
//--- The mouse button released within the element means a  click on the control
   else
     {
      this.SetCheckBackgroundColor(this.CheckBackgroundColorMouseOver(),false);
      this.SetCheckBorderColor(this.CheckBorderColorMouseOver(),false);
      this.SetCheckFlagColor(this.CheckFlagColorInit(),false);
      Print(DFUN_ERR_LINE,TextByLanguage("Щелчок","Click"));
     }
   this.Redraw(false);
  }
//+------------------------------------------------------------------+

O método é semelhante ao discutido para a classe de objeto CheckBox, exceto que aqui não precisamos definir a caixa de seleção para seu valor oposto no bloco que trata da liberação do botão dentro da área ativa do objeto.


Objeto WinForms "Botão" (Botão). O botão pode ser de dois tipos:

  1. Quando pressionado, o botão dispara e retorna ao seu estado original,
  2. Quando pressionado, o botão dispara e permanece pressionado, pressionando-o novamente muda seu estado para o oposto (trigger - Toggle-Button).

Assim, precisamos de um sinalizador que determine qual é o botão, de um sinalizador que determine o estado do botão de disparo (pressionado/liberado) e, claro, de outro conjunto para determinar as cores do botão no estado pressionado (fundo cor, cor ao passar o mouse e pressionar o botão).

No arquivo \MQL5\Include\DoEasy\Objects\Graph\WForms\Common Controls\Button.mqh, na seção privada da classe, vamos declarar arrays para armazenar as cores dos novos estados do botão:

//+------------------------------------------------------------------+
//| Label object class of WForms controls                            |
//+------------------------------------------------------------------+
class CButton : public CLabel
  {
private:
   int               m_text_x;                                 // Text X coordinate
   int               m_text_y;                                 // Text Y coordinate
   color             m_array_colors_bg_tgl[];                  // Array of element background colors for the 'enabled' state
   color             m_array_colors_bg_tgl_dwn[];              // Array of control background colors for the 'enabled' state when clicking on the control
   color             m_array_colors_bg_tgl_ovr[];              // Array of control background colors for the 'enabled' state when hovering the mouse over the control
   color             m_array_colors_bg_tgl_init[];             // Array of initial element background colors for the 'enabled' state
protected:


Na seção protected da classe, declararemos os manipuladores de eventos do mouse da classe pai, que precisam ser redefinidos aqui:

protected:
//--- Set the element width and height automatically
   virtual bool      AutoSetWH(void);

//--- 'The cursor is inside the active area, the mouse buttons are not clicked' event handler
   virtual void      MouseActiveAreaNotPressedHandler(const int id,const long& lparam,const double& dparam,const string& sparam);
//--- 'The cursor is inside the active area, a mouse button is clicked (any)' event handler
   virtual void      MouseActiveAreaPressedHandler(const int id,const long& lparam,const double& dparam,const string& sparam);
//--- 'The cursor is inside the active area, the left mouse button is clicked' event handler
   virtual void      MouseActiveAreaReleasedHandler(const int id,const long& lparam,const double& dparam,const string& sparam);

public:


Na seção pública da classe, escreveremos métodos para definir e retornar o sinalizador do botão gatilho, métodos para definir e retornar o estado do botão gatilho, métodos para trabalhar com as cores dos novos estados do botão e declararemos um método manipulador para o último evento do mouse:

public:
//--- Redraw the object
   virtual void      Redraw(bool redraw);

//--- (1) Set and (2) return the mode of the element auto resizing depending on the content
   void              SetAutoSizeMode(const ENUM_CANV_ELEMENT_AUTO_SIZE_MODE mode,const bool redraw)
                       {
                        ENUM_CANV_ELEMENT_AUTO_SIZE_MODE prev=this.AutoSizeMode();
                        if(prev==mode)
                           return;
                        this.SetProperty(CANV_ELEMENT_PROP_AUTOSIZE_MODE,mode);
                       }
   ENUM_CANV_ELEMENT_AUTO_SIZE_MODE AutoSizeMode(void)   const { return (ENUM_CANV_ELEMENT_AUTO_SIZE_MODE)this.GetProperty(CANV_ELEMENT_PROP_AUTOSIZE_MODE);   }

//--- (1) Set and (2) return the control Toggle flag
   void              SetToggleFlag(const bool flag)
                       {
                        this.SetProperty(CANV_ELEMENT_PROP_BUTTON_TOGGLE,flag);
                        if(this.Toggle())
                           this.SetColorsToggleON(CLR_DEF_CONTROL_STD_BACK_COLOR_ON,CLR_DEF_CONTROL_STD_BACK_DOWN_ON,CLR_DEF_CONTROL_STD_BACK_OVER_ON,true);
                       }
   bool              Toggle(void)                        const { return (bool)this.GetProperty(CANV_ELEMENT_PROP_BUTTON_TOGGLE);                               }
//--- (1) Set and (2) return the Toggle control status
   void              SetState(const bool flag)                 { this.SetProperty(CANV_ELEMENT_PROP_BUTTON_STATE,flag);                                        }
   bool              State(void)                         const { return (bool)this.GetProperty(CANV_ELEMENT_PROP_BUTTON_STATE);                                }
   
//--- (1,2) Set and (3) return the main background color for the 'enabled' status
   void              SetBackgroundColorToggleON(const color colour,const bool set_init_color)
                       {
                        this.SetProperty(CANV_ELEMENT_PROP_BACKGROUND_COLOR_TOGGLE,colour);
                        color arr[1];
                        arr[0]=colour;
                        this.CopyArraysColors(this.m_array_colors_bg_tgl,arr,DFUN);
                        if(set_init_color)
                           this.CopyArraysColors(this.m_array_colors_bg_tgl_init,arr,DFUN);
                       }
   void              SetBackgroundColorsToggleON(color &colors[],const bool set_init_colors)
                       {
                        this.CopyArraysColors(this.m_array_colors_bg_tgl,colors,DFUN);
                        this.SetProperty(CANV_ELEMENT_PROP_BACKGROUND_COLOR_TOGGLE,this.m_array_colors_bg_tgl[0]);
                        if(set_init_colors)
                           this.CopyArraysColors(this.m_array_colors_bg_tgl_init,colors,DFUN);
                       }
   color             BackgroundColorToggleON(void)       const { return (color)this.GetProperty(CANV_ELEMENT_PROP_BACKGROUND_COLOR_TOGGLE);                    }
   
//--- (1,2) Set and (3) return the background color when clicking on the control for the 'enabled' status
   void              SetBackgroundColorToggleONMouseDown(const color colour)
                       {
                        this.SetProperty(CANV_ELEMENT_PROP_BACKGROUND_COLOR_TOGGLE_MOUSE_DOWN,colour);
                        color arr[1];
                        arr[0]=colour;
                        this.CopyArraysColors(this.m_array_colors_bg_tgl_dwn,arr,DFUN);
                       }
   void              SetBackgroundColorsToggleONMouseDown(color &colors[])
                       {
                        this.CopyArraysColors(this.m_array_colors_bg_tgl_dwn,colors,DFUN);
                        this.SetProperty(CANV_ELEMENT_PROP_BACKGROUND_COLOR_TOGGLE_MOUSE_DOWN,this.m_array_colors_bg_dwn[0]);
                       }
   color             BackgroundColorToggleONMouseDown(void) const { return (color)this.GetProperty(CANV_ELEMENT_PROP_BACKGROUND_COLOR_TOGGLE_MOUSE_DOWN);      }
                       
//--- (1,2) Set and (3) return the background color when hovering the mouse over the control for the 'enabled' status
   void              SetBackgroundColorToggleONMouseOver(const color colour)
                       {
                        this.SetProperty(CANV_ELEMENT_PROP_BACKGROUND_COLOR_TOGGLE_MOUSE_OVER,colour);
                        color arr[1];
                        arr[0]=colour;
                        this.CopyArraysColors(this.m_array_colors_bg_tgl_ovr,arr,DFUN);
                       }
   void              SetBackgroundColorsToggleONMouseOver(color &colors[])
                       {
                        this.CopyArraysColors(this.m_array_colors_bg_tgl_ovr,colors,DFUN);
                        this.SetProperty(CANV_ELEMENT_PROP_BACKGROUND_COLOR_TOGGLE_MOUSE_OVER,this.m_array_colors_bg_ovr[0]);
                       }
   color             BackgroundColorToggleONMouseOver(void) const { return (color)this.GetProperty(CANV_ELEMENT_PROP_BACKGROUND_COLOR_TOGGLE_MOUSE_OVER);      }

//--- Return the initial main background color for the 'enabled' status
   color             BackgroundColorToggleONInit(void)      const { return this.m_array_colors_bg_tgl_init[0]; }
   
//--- Set the colors for the 'enabled' status
   void              SetColorsToggleON(const color back,const color back_down,const color back_over,const bool set_init_color);
   
//--- Last mouse event handler
   virtual void      OnMouseEventPostProcessing(void);

//--- Constructor
                     CButton(const long chart_id,
                             const int subwindow,
                             const string name,
                             const int x,
                             const int y,
                             const int w,
                             const int h);
  };
//+------------------------------------------------------------------+

Os métodos são idênticos aos mesmos métodos para definir e retornar as cores de objetos de outras classes, que discutimos acima.


No construtor da classe, definimos as cores padrão para o plano de fundo do objeto, o valor da opacidade e os valores das cores do plano de fundo ao passar o mouse e clicar no botão. Definimos o sinalizador de um botão regular (não um botão de opção) e, consequentemente, o estado do botão de opção como false:

//+------------------------------------------------------------------+
//| Constructor                                                      |
//+------------------------------------------------------------------+
CButton::CButton(const long chart_id,
                 const int subwindow,
                 const string name,
                 const int x,
                 const int y,
                 const int w,
                 const int h) : CLabel(chart_id,subwindow,name,x,y,w,h)
  {
   CGBaseObj::SetTypeElement(GRAPH_ELEMENT_TYPE_WF_BUTTON);
   CGCnvElement::SetProperty(CANV_ELEMENT_PROP_TYPE,GRAPH_ELEMENT_TYPE_WF_BUTTON);
   this.m_type=OBJECT_DE_TYPE_GWF_COMMON;
   this.SetCoordX(x);
   this.SetCoordY(y);
   this.SetWidth(w);
   this.SetHeight(h);
   this.Initialize();
   this.SetBackgroundColor(CLR_DEF_CONTROL_STD_BACK_COLOR,true);
   this.SetBackgroundColorMouseDown(CLR_DEF_CONTROL_STD_MOUSE_DOWN);
   this.SetBackgroundColorMouseOver(CLR_DEF_CONTROL_STD_MOUSE_OVER);
   this.SetOpacity(CLR_DEF_CONTROL_STD_OPACITY);
   this.SetTextAlign(ANCHOR_CENTER);
   this.SetMarginAll(3);
   this.SetWidthInit(this.Width());
   this.SetHeightInit(this.Height());
   this.SetCoordXInit(x);
   this.SetCoordYInit(y);
   this.SetToggleFlag(false);
   this.SetState(false);
   this.Redraw(false);
  }
//+------------------------------------------------------------------+


Redefinimos os manipuladores de eventos do mouse.

Manipulador do evento “Cursor dentro da área ativa, botões do mouse não pressionados”:

//+------------------------------------------------------------------+
//| 'The cursor is inside the active area,                           |
//| no mouse buttons are clicked' event handler                      |
//+------------------------------------------------------------------+
void CButton::MouseActiveAreaNotPressedHandler(const int id,const long& lparam,const double& dparam,const string& sparam)
  {
//--- If this is a simple button, set the color for the "The cursor is over the active area, the mouse button is not clicked" status
   if(!this.Toggle())
      this.SetBackgroundColor(this.BackgroundColorMouseOver(),false);
//--- If this is the toggle button, set the color for the status depending on whether the button is pressed or not
   else
      this.SetBackgroundColor(this.State() ? this.BackgroundColorToggleONMouseOver() : this.BackgroundColorMouseOver(),false);
//--- Set the frame color for the status
   this.SetBorderColor(this.BorderColorMouseOver(),false);
//--- Redraw the object
   this.Redraw(false);
  }
//+------------------------------------------------------------------+


Manipulador do evento “Cursor dentro da área ativa, botão do mouse pressionado (qualquer)”:

//+------------------------------------------------------------------+
//| 'The cursor is inside the active area,                           |
//| a mouse button is clicked (any)                                  |
//+------------------------------------------------------------------+
void CButton::MouseActiveAreaPressedHandler(const int id,const long& lparam,const double& dparam,const string& sparam)
  {
//--- If this is a simple button, set the color for the "The cursor is over the active area, the mouse button is clicked" status
   if(!this.Toggle())
      this.SetBackgroundColor(this.BackgroundColorMouseDown(),false);
//--- If this is the toggle button, set the color for the status depending on whether the button is pressed or not
   else
      this.SetBackgroundColor(this.State() ? this.BackgroundColorToggleONMouseDown() : this.BackgroundColorMouseDown(),false);
//--- Set the frame color for the status
   this.SetBorderColor(this.BorderColorMouseDown(),false);
//--- Redraw the object
   this.Redraw(false);
  }
//+------------------------------------------------------------------+


Manipulador do evento “Cursor dentro da área ativa, botão do mouse liberado (esquerdo)”:

//+------------------------------------------------------------------+
//| 'The cursor is inside the active area,                           |
//| left mouse button released                                       |
//+------------------------------------------------------------------+
void CButton::MouseActiveAreaReleasedHandler(const int id,const long& lparam,const double& dparam,const string& sparam)
  {
//--- The mouse button released outside the element means refusal to interact with the element
   if(lparam<this.CoordX() || lparam>this.RightEdge() || dparam<this.CoordY() || dparam>this.BottomEdge())
     {
      //--- If this is a simple button, set the initial background color
      if(!this.Toggle())
         this.SetBackgroundColor(this.BackgroundColorInit(),false);
      //--- If this is the toggle button, set the initial color depending on whether the button is pressed or not
      else
         this.SetBackgroundColor(!this.State() ? this.BackgroundColorInit() : this.BackgroundColorToggleONInit(),false);
      //--- Set the initial frame color
      this.SetBorderColor(this.BorderColorInit(),false);
      //--- Send the test message to the journal
      Print(DFUN_ERR_LINE,TextByLanguage("Отмена","Cancel"));
     }
//--- The mouse button released within the element means a  click on the control
   else
     {
      //--- If this is a simple button, set the color for "The cursor is over the active area" status
      if(!this.Toggle())
         this.SetBackgroundColor(this.BackgroundColorMouseOver(),false);
      //--- If this is the toggle button,
      else
        {
         //--- set the button status to the opposite one
         this.SetState(!this.State());
         //--- set the background color for "The cursor is over the active area" status depending on whether the button is clicked or not
         this.SetBackgroundColor(this.State() ? this.BackgroundColorToggleONMouseOver() : this.BackgroundColorMouseOver(),false);
        }
      //--- Send the test message to the journal
      Print(DFUN_ERR_LINE,TextByLanguage("Щелчок","Click"));
      //--- Set the frame color for "The cursor is over the active area" status
      this.SetBorderColor(this.BorderColorMouseOver(),false);
     }
//--- Redraw the object
   this.Redraw(false);
  }
//+------------------------------------------------------------------+

A lógica de cada método é descrita em detalhes nos comentários ao código e, espero, dispensa explicações.


Último manipulador de eventos do mouse:

//+------------------------------------------------------------------+
//| Last mouse event handler                                         |
//+------------------------------------------------------------------+
void CButton::OnMouseEventPostProcessing(void)
  {
   ENUM_MOUSE_FORM_STATE state=GetMouseState();
   switch(state)
     {
      //--- The cursor is outside the form, the mouse buttons are not clicked
      //--- The cursor is outside the form, any mouse button is clicked
      //--- The cursor is outside the form, the mouse wheel is being scrolled
      case MOUSE_FORM_STATE_OUTSIDE_FORM_NOT_PRESSED :
      case MOUSE_FORM_STATE_OUTSIDE_FORM_PRESSED     :
      case MOUSE_FORM_STATE_OUTSIDE_FORM_WHEEL       :
        if(MouseEventLast()==MOUSE_EVENT_INSIDE_ACTIVE_AREA_NOT_PRESSED)
          {
           this.SetBackgroundColor(this.State() ? this.BackgroundColorToggleON() : this.BackgroundColorInit(),false);
           this.SetBorderColor(this.BorderColorInit(),false);
           this.m_mouse_event_last=ENUM_MOUSE_EVENT(state+MOUSE_EVENT_NO_EVENT);
           this.Redraw(false);
          }
        break;

      //--- The cursor is inside the form, the mouse buttons are not clicked
      case MOUSE_FORM_STATE_INSIDE_FORM_NOT_PRESSED :
        break;
      //--- The cursor is inside the form, any mouse button is clicked
      case MOUSE_FORM_STATE_INSIDE_FORM_PRESSED :
        break;
      //--- The cursor is inside the form, the mouse wheel is being scrolled
      case MOUSE_FORM_STATE_INSIDE_FORM_WHEEL :
        break;
      //--- The cursor is inside the active area, the mouse buttons are not clicked
      case MOUSE_FORM_STATE_INSIDE_ACTIVE_AREA_NOT_PRESSED :
        break;
      //--- The cursor is inside the active area, any mouse button is clicked
      case MOUSE_FORM_STATE_INSIDE_ACTIVE_AREA_PRESSED :
        break;
      //--- The cursor is inside the active area, the mouse wheel is being scrolled
      case MOUSE_FORM_STATE_INSIDE_ACTIVE_AREA_WHEEL :
        break;
      //--- The cursor is inside the active area, left mouse button is released
      case MOUSE_FORM_STATE_INSIDE_ACTIVE_AREA_RELEASED :
        break;
      //--- The cursor is within the window scrolling area, the mouse buttons are not clicked
      case MOUSE_FORM_STATE_INSIDE_SCROLL_AREA_NOT_PRESSED :
        break;
      //--- The cursor is within the window scrolling area, any mouse button is clicked
      case MOUSE_FORM_STATE_INSIDE_SCROLL_AREA_PRESSED :
        break;
      //--- The cursor is within the window scrolling area, the mouse wheel is being scrolled
      case MOUSE_FORM_STATE_INSIDE_SCROLL_AREA_WHEEL :
        break;
      //--- MOUSE_EVENT_NO_EVENT
      default:
        break;
     }
  }
//+------------------------------------------------------------------+

O método processa apenas o estado quando o cursor do mouse está fora da forma e o último evento do mouse é o cursor sobre a forma e os botões do mouse não são pressionados. Nesse caso, a cor de fundo do botão é definida com sua cor inicial, dependendo de se o botão está pressionado ou não (para um botão simples, seu valor retornado pelo método State() é sempre false).

Método que define as cores para o estado "ligado" do botão de alternância:

//+------------------------------------------------------------------+
//| Set the colors for the toggle element 'enabled' status           |
//+------------------------------------------------------------------+
void CButton::SetColorsToggleON(const color back,const color back_down,const color back_over,const bool set_init_color)
  {
   this.SetBackgroundColorToggleON(back,set_init_color);
   this.SetBackgroundColorToggleONMouseDown(back_down);
   this.SetBackgroundColorToggleONMouseOver(back_over);
  }
//+------------------------------------------------------------------+

As cores dos três estados do botão são passadas para o método e são definidas para as variáveis correspondentes. Ao mesmo tempo, o sinalizador set_init_color permite determinar se desejamos manter a cor de fundo original do botão.


Vamos alterar a classe-coleção de objetos gráficos no arquivo \MQL5\Include\DoEasy\Collections\GraphElementsCollection.mqh.

Temos dois blocos de código idênticos no método GetFormUnderCursor() que procura por objetos anexados à forma que estão sob o cursor. Vamos mover este bloco de código para um método separado para encurtar o código do método e representar mais visualmente sua lógica.

Na seção privada da classe, declararemos este método e o método para manipular os eventos da forma do qual o cursor foi retirado:

//--- Add (1) the standard graphical object and (2) the graphical element on canvas to the collection
   bool              AddGraphObjToCollection(const string source,CChartObjectsControl *obj_control);
//--- Search for interaction objects
   CForm            *SearchInteractObj(CForm *form,const int id,const long &lparam,const double &dparam,const string &sparam);
//--- Return the pointer to the form located under the cursor
   CForm            *GetFormUnderCursor(const int id, 
                                        const long &lparam, 
                                        const double &dparam, 
                                        const string &sparam,
                                        ENUM_MOUSE_FORM_STATE &mouse_state,
                                        long &obj_ext_id,
                                        int &form_index);
//--- Reset all interaction flags for all forms except the specified one
   void              ResetAllInteractionExeptOne(CGCnvElement *form);
//--- Post-processing of the former active form under the cursor
   void              FormPostProcessing(void);
//--- Add the element to the collection list
   bool AddCanvElmToCollection(CGCnvElement *element);


Em todos os métodos de criação dos diversos elementos gráficos, os métodos de configuração de cores, nos quais é necessário especificar os sinalizadores para preservar a cor original, já possuem esses sinalizadores (passamos o valor true, pois precisamos preservar a cor original ao criar o objeto). Todas essas alterações já foram feitas na classe e não as consideraremos aqui para economizar espaço.

Vamos escrever um método que cria um objeto gráfico WinForms GroupBox no gráfico e na subjanela especificados:

//--- Create a graphical form object on canvas on a specified chart and subwindow
   int               CreateForm(const long chart_id,
                                const int subwindow,
                                const string name,
                                const int x,
                                const int y,
                                const int w,
                                const int h,
                                const color clr,
                                const uchar opacity,
                                const bool movable,
                                const bool activity,
                                const bool shadow=false,
                                const bool redraw=false)
                       {
                        int id=this.m_list_all_canv_elm_obj.Total();
                        CForm *obj=new CForm(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.SetBackgroundColor(clr,true);
                        obj.SetBorderColor(clr,true);
                        obj.SetOpacity(opacity,false);
                        obj.SetShadow(shadow);
                        obj.DrawRectangle(0,0,obj.Width()-1,obj.Height()-1,obj.BorderColor(),obj.Opacity());
                        obj.Done();
                        obj.Erase(clr,opacity,redraw);
                        return obj.ID();
                       }

Tais e semelhantes alterações foram feitas em todos os métodos onde há uma configuração de cor.


Método para pesquisar objetos de interação:

//+------------------------------------------------------------------+
//| Search for interaction objects                                   |
//+------------------------------------------------------------------+
CForm *CGraphElementsCollection::SearchInteractObj(CForm *form,const int id,const long &lparam,const double &dparam,const string &sparam)
  {
//--- If a non-empty pointer is passed
   if(form!=NULL)
     {
      //--- Create the list of interaction objects
      int total=form.CreateListInteractObj();
      //--- In the loop by the created list
      for(int i=total-1;i>WRONG_VALUE;i--)
        {
         //--- get the next form object
         CForm *obj=form.GetInteractForm(i);
         //--- If the object is received and the mouse cursor is located above the object, return the pointer to the found object
         if(obj!=NULL && obj.MouseFormState(id,lparam,dparam,sparam)>MOUSE_FORM_STATE_OUTSIDE_FORM_WHEEL)
            return obj;
        }
     }
//--- Return the same pointer
   return form;
  }
//+------------------------------------------------------------------+

No último artigo, consideramos a lógica desse método, mas lá esse código fazia parte do método GetFormUnderCursor().
Agora, em vez deste bloco de código, vamos simplesmente escrever uma chamada para este método:

//+------------------------------------------------------------------+
//| Return the pointer to the form located under the cursor          |
//+------------------------------------------------------------------+
CForm *CGraphElementsCollection::GetFormUnderCursor(const int id, 
                                                    const long &lparam, 
                                                    const double &dparam, 
                                                    const string &sparam,
                                                    ENUM_MOUSE_FORM_STATE &mouse_state,
                                                    long &obj_ext_id,
                                                    int &form_index)
  {
//--- Set the ID of the extended standard graphical object to -1 
//--- and the index of the anchor point managed by the form to -1
   obj_ext_id=WRONG_VALUE;
   form_index=WRONG_VALUE;
//--- Initialize the mouse status relative to the form
   mouse_state=MOUSE_FORM_STATE_NONE;
//--- Declare the pointers to graphical element collection class objects
   CGCnvElement *elm=NULL;
   CForm *form=NULL;
//--- Get the list of objects the interaction flag is set for (there should be only one object)
   CArrayObj *list=CSelect::ByGraphCanvElementProperty(GetListCanvElm(),CANV_ELEMENT_PROP_INTERACTION,true,EQUAL);
//--- If managed to obtain the list and it is not empty,
   if(list!=NULL && list.Total()>0)
     {
      //--- Get the only graphical element there
      elm=list.At(0);
      //--- If the element is a form object or its descendants
      if(elm.TypeGraphElement()>=GRAPH_ELEMENT_TYPE_WF_BASE)
        {
         //--- Assign the pointer to the element for the form object pointer
         form=elm;
         //--- Get the mouse status relative to the form
         mouse_state=form.MouseFormState(id,lparam,dparam,sparam);
         //--- If the cursor is inside the form,
         if(mouse_state>MOUSE_FORM_STATE_OUTSIDE_FORM_WHEEL)
           {
            //--- Find the interaction object.
            //--- This will be either the found object or the same form
            form=this.SearchInteractObj(form,id,lparam,dparam,sparam);
            //--- Return the form object
            return form;
           }
        }
     }
//--- If there is no a single form object with a specified interaction flag,
//--- in the loop by all graphical element collection class objects
   int total=this.m_list_all_canv_elm_obj.Total();
   for(int i=0;i<total;i++)
     {
      //--- get the next element
      elm=this.m_list_all_canv_elm_obj.At(i);
      if(elm==NULL)
         continue;
      //--- if the obtained element is a form object or its descendants
      if(elm.TypeGraphElement()>=GRAPH_ELEMENT_TYPE_WF_BASE)
        {
         //--- Assign the pointer to the element for the form object pointer
         form=elm;
         //--- Get the mouse status relative to the form
         mouse_state=form.MouseFormState(id,lparam,dparam,sparam);
         //--- If the cursor is within the form, return the pointer to the form
         if(mouse_state>MOUSE_FORM_STATE_OUTSIDE_FORM_WHEEL)
           {
            //--- Find the interaction object.
            //--- This will be either the found object or the same form
            form=this.SearchInteractObj(form,id,lparam,dparam,sparam);
            //--- Return the form object
            return form;
           }
        }
     }
//--- If there is no a single form object from the collection list
//--- Get the list of extended standard graphical objects
   list=this.GetListStdGraphObjectExt();
   if(list!=NULL)
     {
      //--- in the loop by all extended standard graphical objects
      for(int i=0;i<list.Total();i++)
        {
         //--- get the next graphical object,
         CGStdGraphObj *obj_ext=list.At(i);
         if(obj_ext==NULL)
            continue;
         //--- get the object of its toolkit,
         CGStdGraphObjExtToolkit *toolkit=obj_ext.GetExtToolkit();
         if(toolkit==NULL)
            continue;
         //--- handle the event of changing the chart for the current graphical object
         obj_ext.OnChartEvent(CHARTEVENT_CHART_CHANGE,lparam,dparam,sparam);
         //--- Get the total number of form objects created for the current graphical object
         total=toolkit.GetNumControlPointForms();
         //--- In the loop by all form objects
         for(int j=0;j<total;j++)
           {
            //--- get the next form object,
            form=toolkit.GetControlPointForm(j);
            if(form==NULL)
               continue;
            //--- get the mouse status relative to the form
            mouse_state=form.MouseFormState(id,lparam,dparam,sparam);
            //--- If the cursor is inside the form,
            if(mouse_state>MOUSE_FORM_STATE_OUTSIDE_FORM_WHEEL)
              {
               //--- set the object ID and form index
               //--- and return the pointer to the form
               obj_ext_id=obj_ext.ObjectID();
               form_index=j;
               return form;
              }
           }
        }
     }
//--- Nothing is found - return NULL
   return NULL;
  }
//+------------------------------------------------------------------+

Graças a um refinamento tão pequeno, o código do método se tornou mais legível.


Método que manipula eventos de mouse da antiga forma ativa sob o cursor:

//+------------------------------------------------------------------+
//| Post-processing of the former active form under the cursor       |
//+------------------------------------------------------------------+
void CGraphElementsCollection::FormPostProcessing(void)
  {
 //--- Get all the elements of the CForm type and above
   CArrayObj *list=GetList(CANV_ELEMENT_PROP_TYPE,GRAPH_ELEMENT_TYPE_FORM,EQUAL_OR_MORE);
   if(list==NULL)
      return;
   //--- In the loop by the list of received elements
   int total=list.Total();
   for(int i=0;i<total;i++)
     {
      //--- get the pointer to the object
      CForm *obj=list.At(i);
      //--- if failed to get the pointer, move on to the next one in the list
      if(obj==NULL)
         continue;
      //--- Create the list of interaction objects and get their number
      int count=obj.CreateListInteractObj();
      //--- In the loop by the obtained list
      for(int j=0;j<count;j++)
        {
         //--- get the next object
         CForm *elm=obj.GetInteractForm(j);
         if(elm==NULL)
            continue;
         //--- and call its method of handling mouse events
         elm.OnMouseEventPostProcessing();
        }
     }
  }
//+------------------------------------------------------------------+

Quando afastamos o cursor do objeto-forma no gráfico, esse objeto perde o foco e outro objeto é selecionado automaticamente, o que está sob o cursor. Caso não haja um único objeto sob o cursor, todas as funções do gráfico desabilitadas são habilitadas - rolagem, menu de contexto etc. Mas às vezes temos que processar a retirada do cursor em relação ao objeto quando é necessário, por exemplo, alterar a cor do mesmo - restaurar a cor original no nosso caso. É exatamente isso que esse método faz. Ele percorre todos os elementos gráficos do tipo "forma" e acima, cria para cada um uma lista de objetos anexados a ele e, em um loop sobre esses objetos, chama o método de manipulação de eventos do mouse que consideramos acima para cada objeto. Cada um dos objetos localizados em cada forma no gráfico será processado dessa maneira. Mas será assim somente quando o último evento do mouse for o que precisamos (cursor sobre o objeto e botões do mouse não pressionados). Após processar o evento, atribuímos o estado atual ao último evento de mouse do objeto - cursor fora da forma. Desta forma, apenas um objeto será processado, aquele a partir do qual o cursor foi retirado pela última vez, e só será processado uma vez.


Quando passamos o mouse sobre um objeto-forma móvel e pressionamos o botão do mouse, os sinalizadores de segurar e mover do mouse são ativados para a forma no manipulador de eventos da classe-coleção de elementos gráficos. Mas se pressionarmos o botão em um objeto anexado à forma, ele não poderá ser movido, porque neste caso o sinalizador de movimento será removido e o foco será colocado nele e não, na forma. Assim, ainda temos o sinalizador de “segurar” ativado, mas o sinalizador de movimento é apagado. O evento de movimento do mouse e o sinalizador de movimento são processados simultaneamente:
      //--- If this is a mouse movement event and the movement flag is active, move the form, above which the cursor is located (if the pointer to it is valid)
      if(id==CHARTEVENT_MOUSE_MOVE && move)
        {
         if(form!=NULL)
           {
            //---...
            //---...

Precisamos separar sua verificação e, em seguida, se o sinalizador de movimento estiver desmarcado, mas o mouse estiver se movendo (enquanto o sinalizador de “segurar” estiver definido), podemos determinar esse estado. E, se soltarmos o botão do mouse, o evento MOUSE_FORM_STATE_NONE será registrado. Este evento aparece apenas neste caso, pois ainda não processamos tal situação - evento de liberação do botão do mouse.

Assim, precisamos separar a verificação para o evento de movimento do cursor e para o sinalizador de movimento definido. E se o sinalizador de movimento for desmarcado, precisaremos definir o evento MOUSE_FORM_STATE_NONE e substituí-lo por um novo - MOUSE_FORM_STATE_INSIDE_ACTIVE_AREA_RELEASED.

Vou apenas dar um segmento de código retrabalhado como descrito acima. Aqui dividimos os dois eventos em verificações separadas:

      if(id==CHARTEVENT_MOUSE_MOVE)
        {
         //--- If the cursor is above the form
         if(form!=NULL)
           {
            //--- If the move flag is set, shift the form following the cursor
            if(move)
              {
               //--- calculate the cursor movement relative to the form coordinate origin
               int x=this.m_mouse.CoordX()-form.OffsetX();
               int y=this.m_mouse.CoordY()-form.OffsetY();
               //--- get the width and height of the chart the form is located at
               int chart_width=(int)::ChartGetInteger(form.ChartID(),CHART_WIDTH_IN_PIXELS,form.SubWindow());
               int chart_height=(int)::ChartGetInteger(form.ChartID(),CHART_HEIGHT_IN_PIXELS,form.SubWindow());
               //--- If the form is not within an extended standard graphical object
               if(form_index==WRONG_VALUE)
                 {
                  //--- Adjust the calculated form coordinates if the form is out of the chart range
                  if(x<0) x=0;
                  if(x>chart_width-form.Width()) x=chart_width-form.Width();
                  if(y<0) y=0;
                  if(y>chart_height-form.Height()) y=chart_height-form.Height();
                  //--- If the one-click trading panel is not present on the chart,
                  if(!::ChartGetInteger(form.ChartID(),CHART_SHOW_ONE_CLICK))
                    {
                     //--- calculate the form coordinates so that the form does not overlap with the one-click trading panel button
                     if(y<17 && x<41)
                        y=17;
                    }
                  //--- If the one-click trading panel is on the chart,
                  else
                    {
                     //--- calculate the form coordinates so that the form does not overlap with the one-click trading panel
                     if(y<80 && x<192)
                        y=80;
                    }
                 }
               //--- If the form is included into the extended standard graphical object
               else
                 {
                  if(graph_obj_id>WRONG_VALUE)
                    {
                     //--- Get the list of objects by object ID (there should be one object)
                     CArrayObj *list_ext=CSelect::ByGraphicStdObjectProperty(GetListStdGraphObjectExt(),GRAPH_OBJ_PROP_ID,0,graph_obj_id,EQUAL);
                     //--- If managed to obtain the list and it is not empty,
                     if(list_ext!=NULL && list_ext.Total()>0)
                       {
                        //--- get the graphical object from the list
                        CGStdGraphObj *ext=list_ext.At(0);
                        //--- If the pointer to the object has been received,
                        if(ext!=NULL)
                          {
                           //--- get the object type
                           ENUM_OBJECT type=ext.GraphObjectType();
                           //--- If the object is built using screen coordinates, set the coordinates to the object
                           if(type==OBJ_LABEL || type==OBJ_BUTTON || type==OBJ_BITMAP_LABEL || type==OBJ_EDIT || type==OBJ_RECTANGLE_LABEL)
                             {
                              ext.SetXDistance(x);
                              ext.SetYDistance(y);
                             }
                           //--- otherwise, if the object is built based on time/price coordinates,
                           else
                             {
                              //--- calculate the shift from the form coordinate origin to its center
                              int shift=(int)::ceil(form.Width()/2)+1;
                              //--- If the form is located on one of the graphical object pivot points,
                              if(form_index<ext.Pivots())
                                {
                                 //--- limit the form coordinates so that they do not move beyond the chart borders
                                 if(x+shift<0)
                                    x=-shift;
                                 if(x+shift>chart_width)
                                    x=chart_width-shift;
                                 if(y+shift<0)
                                    y=-shift;
                                 if(y+shift>chart_height)
                                    y=chart_height-shift;
                                 //--- set the calculated coordinates to the object
                                 ext.ChangeCoordsExtendedObj(x+shift,y+shift,form_index);
                                }
                              //--- If the form is central for managing all pivot points of a graphical object
                              else
                                {
                                 //--- Get screen coordinates of all object pivot points and write them to the m_data_pivot_point structure
                                 if(this.GetPivotPointCoordsAll(ext,m_data_pivot_point))
                                   {
                                    //--- In the loop by the number of object pivot points,
                                    for(int i=0;i<(int)this.m_data_pivot_point.Size();i++)
                                      {
                                       //--- limit the screen coordinates of the current pivot point so that they do not move beyond the chart borders
                                       //--- By X coordinate
                                       if(x+shift-::fabs(this.m_data_pivot_point[i].ShiftX)<0)
                                          x=-shift+::fabs(m_data_pivot_point[i].ShiftX);
                                       if(x+shift+::fabs(this.m_data_pivot_point[i].ShiftX)>chart_width)
                                          x=chart_width-shift-::fabs(this.m_data_pivot_point[i].ShiftX);
                                       //--- By Y coordinate
                                       if(y+shift-::fabs(this.m_data_pivot_point[i].ShiftY)<0)
                                          y=-shift+::fabs(this.m_data_pivot_point[i].ShiftY);
                                       if(y+shift+::fabs(this.m_data_pivot_point[i].ShiftY)>chart_height)
                                          y=chart_height-shift-::fabs(this.m_data_pivot_point[i].ShiftY);
                                       //--- set the calculated coordinates to the current object pivot point
                                       ext.ChangeCoordsExtendedObj(x+shift+this.m_data_pivot_point[i].ShiftX,y+shift+this.m_data_pivot_point[i].ShiftY,i);
                                      }
                                   }
                                }
                             }
                          }
                       }
                    }
                 }
               //--- Move the form by the obtained coordinates
               if(form.IsMain())
                  form.Move(x,y,true);
              }            
            //--- If the move flag is disabled
            else
              {
               //--- The undefined mouse status in mouse_state means releasing the left button
               //--- Assign the new mouse status to the variable
               if(mouse_state==MOUSE_FORM_STATE_NONE)
                  mouse_state=MOUSE_FORM_STATE_INSIDE_ACTIVE_AREA_RELEASED;
               //--- Handle moving the cursor mouse away from the graphical element
               this.FormPostProcessing();
              }
           }
        }

e agora temos a oportunidade de "capturar" um novo evento de mouse, desde que o sinalizador de movimento esteja desmarcado, e imediatamente manipular o evento de retirar o cursor para longe do objeto para o objeto-forma ativo.

Agora nos resta chamar o manipulador de eventos do mouse para o objeto no mesmo manipulador de eventos OnChartEvent() nos blocos do manipulador de eventos do mouse, indicando o evento encontrado anteriormente:

      //--- If the cursor is above the form
      else
        {
         //--- If the button is still pressed and held on the chart, exit
         if(pressed_chart)
           {
            return;
           }
         
         //--- If the flag of holding the button on the form is not set yet
         if(!pressed_form)
           {
            pressed_chart=false;    // The button is not pressed on the chart
            this.SetChartTools(form.ChartID(),false);
            //+---------------------------------------------------------------------------------------------+
            //| 'The cursor is inside the form, no mouse buttons are clicked' event handler                 |
            //+---------------------------------------------------------------------------------------------+
            if(mouse_state==MOUSE_FORM_STATE_INSIDE_FORM_NOT_PRESSED)
              {
               form.OnMouseEvent(MOUSE_EVENT_INSIDE_FORM_NOT_PRESSED,lparam,dparam,sparam);
               //--- If the cursor is above the form for managing the pivot point of an extended graphical object,
               if(graph_obj_id>WRONG_VALUE)
                 {
                  //--- get the object by its ID and by the chart ID
                  CGStdGraphObj *graph_obj=this.GetStdGraphObjectExt(graph_obj_id,form.ChartID());
                  if(graph_obj!=NULL)
                    {
                     //--- Get the toolkit of an extended standard graphical object
                     CGStdGraphObjExtToolkit *toolkit=graph_obj.GetExtToolkit();
                     if(toolkit!=NULL)
                       {
                        //--- Draw a point with a circle on the form and delete it on all other forms
                        toolkit.DrawOneControlPoint(form);
                       }
                    }
                 }
              }
            //+---------------------------------------------------------------------------------------------+
            //| 'The cursor is inside the form, a mouse button is clicked (any)' event handler              |
            //+---------------------------------------------------------------------------------------------+
            if(mouse_state==MOUSE_FORM_STATE_INSIDE_FORM_PRESSED)
              {
               this.SetChartTools(::ChartID(),false);
               //--- If the flag of holding the form is not set yet
               if(!pressed_form)
                 {
                  pressed_form=true;      // set the flag of pressing on the form
                  pressed_chart=false;    // disable the flag of pressing on the form
                 }
               form.OnMouseEvent(MOUSE_EVENT_INSIDE_FORM_PRESSED,lparam,dparam,sparam);
              }
            //+---------------------------------------------------------------------------------------------+
            //| 'The cursor is inside the form, the mouse wheel is being scrolled' event handler            |
            //+---------------------------------------------------------------------------------------------+
            if(mouse_state==MOUSE_FORM_STATE_INSIDE_FORM_WHEEL)
              {
               form.OnMouseEvent(MOUSE_EVENT_INSIDE_FORM_WHEEL,lparam,dparam,sparam);
              }
            //+---------------------------------------------------------------------------------------------+
            //| 'The cursor is inside the active area, the mouse buttons are not clicked' event handler     |
            //+---------------------------------------------------------------------------------------------+
            if(mouse_state==MOUSE_FORM_STATE_INSIDE_ACTIVE_AREA_NOT_PRESSED)
              {
               //--- Set the cursor shift relative to the form initial coordinates
               form.SetOffsetX(this.m_mouse.CoordX()-form.CoordX());
               form.SetOffsetY(this.m_mouse.CoordY()-form.CoordY());
               form.OnMouseEvent(MOUSE_EVENT_INSIDE_ACTIVE_AREA_NOT_PRESSED,lparam,dparam,sparam);
               
               //--- If the cursor is above the active area of the form for managing the pivot point of an extended graphical object,
               if(graph_obj_id>WRONG_VALUE)
                 {
                  //--- get the object by its ID and by the chart ID
                  CGStdGraphObj *graph_obj=this.GetStdGraphObjectExt(graph_obj_id,form.ChartID());
                  if(graph_obj!=NULL)
                    {
                     //--- Get the toolkit of an extended standard graphical object
                     CGStdGraphObjExtToolkit *toolkit=graph_obj.GetExtToolkit();
                     if(toolkit!=NULL)
                       {
                        //--- Draw a point with a circle on the form and delete it on all other forms
                        toolkit.DrawOneControlPoint(form);
                       }
                    }
                 }
              }
            //+---------------------------------------------------------------------------------------------+
            //| 'The cursor is inside the active area,  any mouse button is clicked' event handler          |
            //+---------------------------------------------------------------------------------------------+
            if(mouse_state==MOUSE_FORM_STATE_INSIDE_ACTIVE_AREA_PRESSED && !move)
              {
               pressed_form=true;                                       // the flag of holding the mouse button on the form
               //--- If the left mouse button is pressed
               if(this.m_mouse.IsPressedButtonLeft())
                 {
                  //--- Set flags and form parameters
                  move=true;                                            // movement flag
                  form.SetInteraction(true);                            // flag of the form interaction with the environment
                  form.BringToTop();                                    // form on the background - above all others
                  form.SetOffsetX(this.m_mouse.CoordX()-form.CoordX()); // Cursor shift relative to the X coordinate
                  form.SetOffsetY(this.m_mouse.CoordY()-form.CoordY()); // Cursor shift relative to the Y coordinate
                  this.ResetAllInteractionExeptOne(form);               // Reset interaction flags for all forms except the current one
                  
                  //--- Get the maximum ZOrder
                  long zmax=this.GetZOrderMax();
                  //--- If the maximum ZOrder has been received and the form's ZOrder is less than the maximum one or the maximum ZOrder of all forms is equal to zero
                  if(zmax>WRONG_VALUE && (form.Zorder()<zmax || zmax==0))
                    {
                     //--- If the form is not a control point for managing an extended standard graphical object,
                     //--- set the form's ZOrder above all others
                     if(form.Type()!=OBJECT_DE_TYPE_GFORM_CONTROL)
                        this.SetZOrderMAX(form);
                    }
                 }
               form.OnMouseEvent(MOUSE_EVENT_INSIDE_ACTIVE_AREA_PRESSED,lparam,dparam,sparam);
              }
            //+---------------------------------------------------------------------------------------------+
            //| 'The cursor is inside the active area, the mouse wheel is being scrolled' event handler     |
            //+---------------------------------------------------------------------------------------------+
            if(mouse_state==MOUSE_FORM_STATE_INSIDE_ACTIVE_AREA_WHEEL)
              {
               form.OnMouseEvent(MOUSE_EVENT_INSIDE_ACTIVE_AREA_WHEEL,lparam,dparam,sparam);
              }
            //+---------------------------------------------------------------------------------------------+
            //| 'The cursor is inside the active area, the left mouse button is clicked' event handler      |
            //+---------------------------------------------------------------------------------------------+
            if(mouse_state==MOUSE_FORM_STATE_INSIDE_ACTIVE_AREA_RELEASED)
              {
               form.OnMouseEvent(MOUSE_EVENT_INSIDE_ACTIVE_AREA_RELEASED,lparam,dparam,sparam);
              }
            //+---------------------------------------------------------------------------------------------+
            //| 'The cursor is inside the window scrolling area, no mouse buttons are clicked' event handler|
            //+---------------------------------------------------------------------------------------------+
            if(mouse_state==MOUSE_FORM_STATE_INSIDE_SCROLL_AREA_NOT_PRESSED)
              {
               form.OnMouseEvent(MOUSE_EVENT_INSIDE_SCROLL_AREA_NOT_PRESSED,lparam,dparam,sparam);
              }
            //+------------------------------------------------------------------------------------------------+
            //| 'The cursor is inside the window scrolling area, a mouse button is clicked (any)' event handler|
            //+------------------------------------------------------------------------------------------------+
            if(mouse_state==MOUSE_FORM_STATE_INSIDE_SCROLL_AREA_PRESSED)
              {
               form.OnMouseEvent(MOUSE_EVENT_INSIDE_SCROLL_AREA_PRESSED,lparam,dparam,sparam);
              }
            //+--------------------------------------------------------------------------------------------------+
            //| 'The cursor is inside the window scrolling area, the mouse wheel is being scrolled' event handler|
            //+--------------------------------------------------------------------------------------------------+
            if(mouse_state==MOUSE_FORM_STATE_INSIDE_SCROLL_AREA_WHEEL)
              {
               form.OnMouseEvent(MOUSE_EVENT_INSIDE_SCROLL_AREA_WHEEL,lparam,dparam,sparam);
              }
           }
        }
     }
  }

Imediatamente, adicionamos um novo manipulador para processar o evento de liberação do botão do mouse.


No método que define para o gráfico especificado os sinalizadores para rolar o gráfico com a roda do mouse, o menu de contexto e a ferramenta "crosshair", verificaremos o sinalizador já definido para não por constantemente o mesmo estado que já estava estabelecido para o gráfico:

//+------------------------------------------------------------------+
//| Set the flags of scrolling a chart                               |
//| context menu and crosshairs for the chart                        |
//+------------------------------------------------------------------+
void CGraphElementsCollection::SetChartTools(const long chart_id,const bool flag)
  {
   if(::ChartGetInteger(chart_id,CHART_MOUSE_SCROLL)==flag)
      return;
   ::ChartSetInteger(chart_id,CHART_MOUSE_SCROLL,flag);
   ::ChartSetInteger(chart_id,CHART_CONTEXT_MENU,flag);
   ::ChartSetInteger(chart_id,CHART_CROSSHAIR_TOOL,flag);
  }
//+------------------------------------------------------------------+


Hoje, essas são todas as modificações na biblioteca.


Teste

Para testar, vamos pegar o Expert Advisor do artigo anterior e salvá-lo na nova pasta \MQL5\Experts\TestDoEasy\Part110\ com o novo nome TstDE110.mq5.

O que vamos testar? Vamos deixar todos os objetos do Expert Advisor anterior "como estão", apenas vamos alterar um pouco o tamanho/localização do botão e obter ponteiros para objetos por tipo, como teste de operação do método.

Vamos adicionar outro aos parâmetros de entrada do EA (o sinalizador do botão de alternância) e alterar os parâmetros padrão:

//--- input parameters
sinput   bool                          InpMovable           =  true;                   // Panel Movable flag
sinput   ENUM_INPUT_YES_NO             InpAutoSize          =  INPUT_YES;              // Panel Autosize
sinput   ENUM_AUTO_SIZE_MODE           InpAutoSizeMode      =  AUTO_SIZE_MODE_GROW;    // Panel Autosize mode
sinput   ENUM_BORDER_STYLE             InpFrameStyle        =  BORDER_STYLE_SIMPLE;    // Label border style
sinput   ENUM_ANCHOR_POINT             InpTextAlign         =  ANCHOR_CENTER;          // Label text align
sinput   ENUM_INPUT_YES_NO             InpTextAutoSize      =  INPUT_NO;               // Label autosize
sinput   ENUM_ANCHOR_POINT             InpCheckAlign        =  ANCHOR_LEFT;            // Check flag align
sinput   ENUM_ANCHOR_POINT             InpCheckTextAlign    =  ANCHOR_LEFT;            // Check label text align
sinput   ENUM_CHEK_STATE               InpCheckState        =  CHEK_STATE_UNCHECKED;   // Check flag state
sinput   ENUM_INPUT_YES_NO             InpCheckAutoSize     =  INPUT_YES;              // CheckBox autosize
sinput   ENUM_BORDER_STYLE             InpCheckFrameStyle   =  BORDER_STYLE_NONE;      // CheckBox border style
sinput   ENUM_ANCHOR_POINT             InpButtonTextAlign   =  ANCHOR_CENTER;          // Button text align
sinput   ENUM_INPUT_YES_NO             InpButtonAutoSize    =  INPUT_YES;              // Button autosize
sinput   ENUM_AUTO_SIZE_MODE           InpButtonAutoSizeMode=  AUTO_SIZE_MODE_GROW;    // Button Autosize mode
sinput   ENUM_BORDER_STYLE             InpButtonFrameStyle  =  BORDER_STYLE_NONE;      // Button border style
sinput   bool                          InpButtonToggle      =  false;                  // Button toggle flag
//--- global variables

O manipulador OnInit() agora terá o seguinte código:

//+------------------------------------------------------------------+
//| 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 2 bound panel objects
      CPanel *obj=NULL;
      for(int i=0;i<2;i++)
        {
         //--- create the panel object with calculated coordinates, width of 90 and height of 40
         CPanel *prev=pnl.GetElement(i-1);
         int xb=0, yb=0;
         int x=(prev==NULL ? xb : xb+prev.Width()+20);
         int y=0;
         if(pnl.CreateNewElement(GRAPH_ELEMENT_TYPE_WF_PANEL,x,y,90,40,C'0xCD,0xDA,0xD7',200,true,false))
           {
            obj=pnl.GetElement(i);
            if(obj==NULL)
               continue;
            obj.SetBorderSizeAll(3);
            obj.SetBorderStyle(FRAME_STYLE_BEVEL);
            obj.SetBackgroundColor(obj.ChangeColorLightness(obj.BackgroundColor(),4*i),true);
            obj.SetForeColor(clrRed,true);
            //--- Calculate the width and height of the future text label object
            int w=obj.Width()-obj.BorderSizeLeft()-obj.BorderSizeRight();
            int h=obj.Height()-obj.BorderSizeTop()-obj.BorderSizeBottom();
            //--- Create a text label object
            obj.CreateNewElement(GRAPH_ELEMENT_TYPE_WF_LABEL,0,0,w,h,clrNONE,255,false,false);
            //--- Get the pointer to a newly created object
            CLabel *lbl=obj.GetElement(0);
            if(lbl!=NULL)
              {
               //--- If the object has an even or zero index in the list, set the default text color for it
               if(i % 2==0)
                  lbl.SetForeColor(CLR_DEF_FORE_COLOR,true);
               //--- If the object index in the list is odd, set the object opacity to 127
               else
                  lbl.SetForeColorOpacity(127);
               //--- Set the font Black width type and
               //--- specify the text alignment from the EA settings
               lbl.SetFontBoldType(FW_TYPE_BLACK);
               lbl.SetTextAlign(InpTextAlign);
               lbl.SetAutoSize((bool)InpTextAutoSize,false);
               //--- For an object with an even or zero index, specify the Bid price for the text, otherwise - the Ask price of the symbol 
               lbl.SetText(GetPrice(i % 2==0 ? SYMBOL_BID : SYMBOL_ASK));
               //--- Set the frame width, type and color for a text label and update the modified object
               lbl.SetBorderSizeAll(1);
               lbl.SetBorderStyle((ENUM_FRAME_STYLE)InpFrameStyle);
               lbl.SetBorderColor(CLR_DEF_BORDER_COLOR,true);
               lbl.Update(true);
              }
           }
        }
      //--- Create the 'GroupBox' WinForms object
      CGroupBox *gbox=NULL;
      //--- Indent from attached panels by 6 pixels is a Y coordinate for GroupBox
      int w=pnl.GetUnderlay().Width();
      int y=obj.BottomEdgeRelative()+6;
      //--- If the attached GroupBox object is created
      if(pnl.CreateNewElement(GRAPH_ELEMENT_TYPE_WF_GROUPBOX,0,y,210,110,C'0x91,0xAA,0xAE',0,true,false))
        {
         //--- get the pointer to the GroupBox object by its index in the list of bound GroupBox type objects
         gbox=pnl.GetElementByType(GRAPH_ELEMENT_TYPE_WF_GROUPBOX,0);
         if(gbox!=NULL)
           {
            //--- set the "indented frame" type, the frame color matches the main panel background color,
            //--- while the text color is the background color of the last attached panel darkened by 1
            gbox.SetBorderStyle(FRAME_STYLE_STAMP);
            gbox.SetBorderColor(pnl.BackgroundColor(),true);
            gbox.SetForeColor(gbox.ChangeColorLightness(obj.BackgroundColor(),-1),true);
            //--- Create the CheckBox object
            gbox.CreateNewElement(GRAPH_ELEMENT_TYPE_WF_CHECKBOX,2,10,50,20,clrNONE,255,true,false);
            //--- get the pointer to the CheckBox object by its index in the list of bound CheckBox type objects
            CCheckBox *cbox=gbox.GetElementByType(GRAPH_ELEMENT_TYPE_WF_CHECKBOX,0);
            //--- If CheckBox is created and the pointer to it is received
            if(cbox!=NULL)
              {
               //--- Set the CheckBox parameters from the EA inputs
               cbox.SetAutoSize((bool)InpCheckAutoSize,false);
               cbox.SetCheckAlign(InpCheckAlign);
               cbox.SetTextAlign(InpCheckTextAlign);
               //--- Set the displayed text, frame style and color, as well as checkbox status
               cbox.SetText("CheckBox");
               cbox.SetBorderStyle((ENUM_FRAME_STYLE)InpCheckFrameStyle);
               cbox.SetBorderColor(CLR_DEF_BORDER_COLOR,true);
               cbox.SetChecked(true);
               cbox.SetCheckState((ENUM_CANV_ELEMENT_CHEK_STATE)InpCheckState);
              }
            //--- Create the RadioButton object
            gbox.CreateNewElement(GRAPH_ELEMENT_TYPE_WF_RADIOBUTTON,2,cbox.BottomEdgeRelative()+4,50,20,clrNONE,255,true,false);
            //--- get the pointer to the RadioButton object by its index in the list of bound RadioButton type objects
            CRadioButton *rbtn=gbox.GetElementByType(GRAPH_ELEMENT_TYPE_WF_RADIOBUTTON,0);
            //--- If RadioButton is created and the pointer to it is received
            if(rbtn!=NULL)
              {
               //--- Set the RadioButton parameters from the EA inputs
               rbtn.SetAutoSize((bool)InpCheckAutoSize,false);
               rbtn.SetCheckAlign(InpCheckAlign);
               rbtn.SetTextAlign(InpCheckTextAlign);
               //--- Set the displayed text, frame style and color, as well as checkbox status
               rbtn.SetText("RadioButton");
               rbtn.SetBorderStyle((ENUM_FRAME_STYLE)InpCheckFrameStyle);
               rbtn.SetBorderColor(CLR_DEF_BORDER_COLOR,true);
               rbtn.SetChecked(true);
              }
            //--- Create the Button object
            gbox.CreateNewElement(GRAPH_ELEMENT_TYPE_WF_BUTTON,(int)fmax(rbtn.RightEdgeRelative(),cbox.RightEdgeRelative())+10,14,60,36,clrNONE,255,true,false);
            //--- get the pointer to the Button object by its index in the list of bound Button type objects
            CButton *butt=gbox.GetElementByType(GRAPH_ELEMENT_TYPE_WF_BUTTON,0);
            //--- If Button is created and the pointer to it is received
            if(butt!=NULL)
              {
               //--- Set the Button parameters from the EA inputs
               butt.SetAutoSize((bool)InpButtonAutoSize,false);
               butt.SetAutoSizeMode((ENUM_CANV_ELEMENT_AUTO_SIZE_MODE)InpButtonAutoSizeMode,false);
               butt.SetTextAlign(InpButtonTextAlign);
               //--- Set the text color, as well as frame style and color
               butt.SetForeColor(butt.ChangeColorLightness(CLR_DEF_FORE_COLOR,2),true);

               butt.SetBorderStyle((ENUM_FRAME_STYLE)InpButtonFrameStyle);
               butt.SetBorderColor(butt.ChangeColorLightness(butt.BackgroundColor(),-10),true);
               //--- Set the 'toggle' mode depending on the settings
               butt.SetToggleFlag(InpButtonToggle);
               //--- Set the displayed text on the button depending on the 'toggle' flag
               if(butt.Toggle())
                  butt.SetText("Toggle-Button");
               else
                  butt.SetText("Button");
              }
           }
        }
      //--- Redraw all objects according to their hierarchy
      pnl.Redraw(true);
     }
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+

Na verdade, tudo continua igual aqui, mas os nomes dos métodos renomeados na biblioteca foram alterados, sinalizadores (true) agora são passados nos métodos de configuração de cores para preservar a cor original, e quando selecionamos um ou outro modo de botão, o nome de tal modo é exibido como texto no botão.
Todas as alterações podem ser encontradas nos arquivos anexados ao artigo.

Compilamos o Expert Advisor e o iniciamos no gráfico:


Como se pode ver, todo o componente visual dos objetos funciona corretamente ao interagir com o mouse.


O que virá a seguir?

No próximo artigo, continuaremos trabalhando na interatividade dos objetos da biblioteca WinForms.

Todos os arquivos da versão atual da biblioteca, os arquivos do EA de teste e o indicador de controle de eventos do gráfico para MQL5 estão anexados abaixo. Você pode baixá-los e testar tudo sozinho. Se você tiver dúvidas, comentários e sugestões, pode colocá-los nos comentários do artigo.

Voltar ao conteúdo

*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
DoEasy. Controles (Parte 6): Controle "Painel", redimensionamento automático do contêiner para adequá-lo ao seu conteúdo
DoEasy. Controles (Parte 7): Controle "Rótulo de texto"
DoEasy. Controles (Parte 8): Objetos básicos do WinForms por categoria, controles GroupBox e CheckBox
DoEasy. Controles (Parte 9): Reorganizando métodos de objetos WinForms, controles "RadioButton" e "Button"



Traduzido do russo pela MetaQuotes Ltd.
Artigo original: https://www.mql5.com/ru/articles/11173

Arquivos anexados |
MQL5.zip (4389.02 KB)
DoEasy. Controles (Parte 11): Objetos WinForms - grupos, objeto WinForms CheckedListBox DoEasy. Controles (Parte 11): Objetos WinForms - grupos, objeto WinForms CheckedListBox
Neste artigo, consideraremos como agrupar objetos WinForms e criar um objeto-lista de objetos CheckBox.
Como desenvolver um sistema de negociação baseado no indicador Oscilador de Chaikin Como desenvolver um sistema de negociação baseado no indicador Oscilador de Chaikin
Bem-vindo ao nosso novo artigo da série sobre como desenvolver um sistema de negociação com base nos indicadores técnico mais populares. Através deste novo artigo, nós aprenderemos como desenvolver um sistema de negociação pelo indicador Oscilador de Chaikin.
Aprendendo a construindo um EA que opera de forma automática (Parte 02): Iniciando a programação Aprendendo a construindo um EA que opera de forma automática (Parte 02): Iniciando a programação
Aprenda como criar um EA que opera de forma automática, isto de forma simples e o mais seguro possível. No artigo anterior apresentei as primeiras etapas das quais você precisa compreender, antes mesmo de iniciar a construção de um EA, que opere de forma automática, ali mostrei.
Como desenvolver um sistema de negociação baseado no indicador Desvio Padrão Como desenvolver um sistema de negociação baseado no indicador Desvio Padrão
Aqui está um novo artigo em nossa série sobre como desenvolver um sistema de negociação pelos indicadores técnicos mais populares na plataforma de negociação MetaTrader 5. Neste novo artigo, nós aprenderemos como desenvolver um sistema de negociação pelo indicador Desvio Padrão.