English Русский 中文 Deutsch 日本語 Português
preview
DoEasy. Elementos de control (Parte 10): Objetos WinForms: dando vida a la interfaz

DoEasy. Elementos de control (Parte 10): Objetos WinForms: dando vida a la interfaz

MetaTrader 5Ejemplos | 30 septiembre 2022, 17:01
347 0
Artyom Trishkin
Artyom Trishkin

Contenido


Concepto

En esencia, todos los objetos WinForms que hemos creado anteriormente son imágenes estáticas, meros "accesorios" que pueden moverse por la pantalla. Esto tiene muy poco sentido, y es hora de empezar a revitalizar la interfaz gráfica de usuario, haciendo que los objetos puedan interactuar con el usuario y con otros objetos.

Los objetos WinForms que ya hemos creado suponen tanto unidades independientes para construir una interfaz gráfica, como bloques de construcción para objetos más complejos. Y para que los objetos más complejos funcionen correctamente, necesitaremos una funcionalidad que permita a los objetos interactuar entre sí y con el usuario. Hoy vamos a poner en práctica esto y a crear un componente visual cuando los objetos interactúan con el ratón. En futuros artículos, implementaremos funcionalidades basadas en eventos para estos objetos, y continuaremos creando nuevos objetos de la biblioteca WinForms.

Todo objeto de GUI activo para el que se haya definido la posibilidad de interactuar con el ratón y que tenga una bandera de accesibilidad deberá indicar al usuario que es posible interactuar con él. Por ejemplo, un botón sencillo. Al pasar el ratón por encima, el botón cambiará ligeramente de color, indicando al usuario que está activo y listo para interactuar. Cuando lo pulsemos, volverá a cambiar de color, pero no se activará aún, se disparará cuando pulsemos el botón del ratón dentro del objeto de interacción. Si, por el contrario, pulsamos el botón del ratón sobre un objeto y, sin soltarlo, alejamos el cursor del objeto para a continuación soltar el botón del ratón, el objeto no debería activarse: volverá a su estado original, antes de que se pulsara el botón del ratón sobre él. Así se comportan todos los objetos del sistema operativo Windows. Aquí también implementaremos el mismo comportamiento de los objetos WinForms.

Después de crear el componente visual, empezaremos a crear objetos WinForms controlados por eventos, lo cual nos permitirá crear objetos más complejos a partir de otros simples. Los eventos de los objetos simples que componen el objeto complejo serán procesados por el propio objeto; una vez analizados, su apariencia cambiará en consecuencia, y como resultado el objeto enviará el evento ocurrido en dicho objeto al gráfico del programa de control.

Como el objeto necesitará ahora varios colores más para el mismo estado (color básico, color al colocar el cursor del ratón, color al pulsar el botón sobre el objeto), vamos a escribir estos nuevos colores para las diferentes propiedades del objeto que puedan tenerlos. Si los nuevos colores están presentes en las propiedades de los objetos, nos resultará más sencillo crear editores visuales para estos objetos más adelante: todas las propiedades podrán mostrarse en un panel y luego gestionarse. Si no especificamos los nuevos colores complementarios en las propiedades del objeto, tendremos que indicarlos manualmente para cada objeto para su muestra, lo cual no resulta en absoluto óptimo.


Mejorando las clases de la biblioteca

En el archivo \MQL5\Include\DoEasy\Defines.mqh , escribimos las nuevas macros para los colores por defecto de los diferentes estados de los 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

Después de todo, ForeColor no será necesario para la casilla de verificación. Por lo tanto, lo sustituiremos por BorderColor, el color del marco de la casilla de verificación. Esto cambiará junto con el color de fondo al darse la interacción con el ratón. Aquí utilizaremos el color de la casilla como ForeColour.

La biblioteca monitoreará el otro estado del ratón en relación con el formulario: cuando el cursor está en el área activa y el botón del ratón se ha soltado después de la pulsación. Vamos a añadir ahora el nuevo estado a la lista de posibles estados del ratón en relación con el formulario:

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

Al monitorear este estado, podremos determinar cuándo se ha soltado el botón del ratón, y este será el momento clave para cambiar el estado del objeto gráfico.

Para reaccionar a las interacciones del ratón, necesitaremos saber qué evento del ratón se ha producido. Posteriormente, basándonos en el procesamiento de dicho evento, enviaremos este al programa, para que el usuario de la biblioteca pueda procesar el evento en su programa.

A continuación, crearemos una nueva enumeración con la lista de eventos de ratón:

//+------------------------------------------------------------------+
//| 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 hemos insertado la nueva enumeración de eventos "embutiéndola" entre la lista de eventos del gráfico y la lista de eventos del objeto gráfico, el valor inicial del primer evento de los objetos gráficos se calculará ahora como el último evento de ratón+1.

En la lista de propiedades enteras del elemento gráfico añadiremos las nuevas propiedades:

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

y aumentaremos el número total de propiedades de 71 a 81.

Vamos a añadir a la enumeración de posibles criterios de clasificación de los objetos gráficos en el lienzo estas nuevas propiedades:

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

Ahora podremos clasificar y filtrar las listas, y también elegir los objetos según estas nuevas propiedades.


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

   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


y los textos de los mensajes correspondientes a los nuevos índices añadidos:

   {"Цвет текста по умолчанию для всех объектов элемента управления","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"},


Al pasar el ratón por encima de un objeto o clicar en él, tendremos que cambiar su color de fondo. Pero para restablecer el color original, hay que memorizarlo la primera vez que se construye un objeto, y luego tomar el color necesario del color anteriormente almacenado.

Como el color de fondo del objeto gráfico puede ser un color de gradiente, es decir, se puede utilizar un array de colores, necesitaremos no solo una variable para almacenar el color original, sino un array. Este se rellenará exactamente de la misma forma que se rellena el array de colores de gradiente al construir un objeto. Al restaurar un color, tomaremos el color del array guardado.

En el archivo \MQL5\Include\DoEasy\Objects\Graph\GCnvElement.mqh, en su sección protegida, declaramos el array de colores de fondo iniciales y trasladamos aquí desde la clase privada el método para rellenar las matrices de colores: lo necesitaremos en las clases herederas:

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


En la sección privada de la clase, en la estructura de datos del objeto, escribiremos las nuevas propiedades enteras del 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

Esto será necesario para garantizar que el objeto se guarde correctamente en el soporte y se lea de él en el futuro.


Vamos a escribir otro método para almacenar un array de colores de gradiente para recordar el color de fondo original:

//--- 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:

El método se llamará en el método que establece el color de fondo. Para ello, introduciremos en sus variables formales una bandera indicando que se debe mantener el color de fondo original después de establecer el color de fondo del objeto. De esta forma, podremos elegir: o bien establecemos el color de fondo del objeto y almacenamos sus colores originales en un array, o simplemente establecemos un nuevo color de fondo que luego podremos restaurar a partir de todo el conjunto de colores de gradiente de fondo previamente escritos en el array. Haremos esto para todos los objetos cuyo color deba cambiar cuando interactúan con el ratón:

//--- 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 a escribir métodos para establecer el color de fondo inicial idénticos a los anteriormente analizados:

   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,

Aquí todo será igual que en los métodos para establecer el color de fondo básico, salvo por la bandera que indica que debemos guardar el color original, que es exactamente lo que guardamos al llamar a estos métodos.

Vamos a escribir métodos que retornan el color de fondo 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

Los métodos retornan un único color de fondo o un color formado por el conjunto de colores de gradiente correspondiente al índice transmitido al método.


En ambos constructores de la clase, inicializamos las nuevas propiedades y transmitimos la bandera para guardar el color de fondo 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);
     }
  }
//+------------------------------------------------------------------+

La propiedad ForeColor de la casilla de verificación está ahora escrita con los valores de BorderColor, ya que la casilla de verificación tiene su propio color.

El segundo constructor se implementa de forma idéntica y no requiere de análisis aquí.


En el método que crea la estructura del objeto, escribimos las nuevas propiedades del objeto que se guardarán en los campos de la estructura:

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

Y en el método que crea el objeto de su estructura, escribimos la configuración de las nuevas propiedades del objeto desde los campos de la estructura:

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


En el constructor de la clase del objeto sombra, en el archivo \MQL5\Include\DoEasy\Objects\Graph\ShadowObj.mqh, transmitimos la bandera que indica la necesidad de guardar el color inicial al establecer el color de fondo:

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

Aquí no necesitamos almacenar el color totalmente transparente, pero dejaremos que se guarde para evitar valores de propiedad no inicializados.


El objeto de formulario es el objeto básico del elemento gráfico en el que se escribe la funcionalidad de interacción con el ratón. También escribiremos un manejador de eventos virtual para el ratón y manejadores virtuales para cada evento determinado por el manejador de eventos.

El manejador de eventos será virtual, por si tenemos que redefinirlo en las clases heredadas, pero los manejadores de cada uno de los eventos del ratón ya son conocidos, y se entiende que son virtuales, ya que para cada uno de los objetos herederos de la clase de objeto de formulario, la gestión de uno u otro evento de ratón será individual. Para restaurar el estado de un objeto y sus colores después de que el cursor se haya alejado de su área activa, necesitaremos un método que gestione este estado (alejando el cursor del objeto). Como al alejarse el cursor de un objeto, automáticamente se activa otro objeto que estaba bajo el cursor, o ningún objeto, necesitaremos hacer un ciclo a través de todos los objetos WinForms para procesar dicho estado, determinar su último estado y procesarlo en caso de que su último estado haya sido el del ratón. y luego cambiar ese estado a uno que corresponda a su estado actual. Así, solo procesaremos el cursor fuera del objeto una vez y pondremos el objeto en su estado correcto: el cursor está fuera del formulario.

En el archivo del objeto formulario \MQL5\Include\DoEasy\Objects\Graph\Form.mqh, en la sección protegida de la clase, declararemos dos variables: una para almacenar el último evento del ratón (necesario para el manejador del último evento) y otra para almacenar el color inicial del marco del formulario (ya que puede cambiar su color al interactuar con el ratón, por lo que deberá ser restaurado). Aquí también declaramos los manejadores virtuales para cada uno de los eventos del ratón:

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:


En la sección pública de la clase, declaramos el método virtual para redibujar el objeto, escribimos el método que retorna la variable que almacena el estado del ratón y
el método que retorna el último evento del ratón.
Los métodos que establecen las dimensiones del marco de cada uno de los lados los haremos virtuales,
y después declararemos los eventos de ratón y su ú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 a modificar el método que establece el color del marco del objeto para poder determinar a partir del estado de la bandera si debemos mantener el color original del marco; también escribiremos los métodos para establecer y retornar el color original del marco del 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


En el método de inicialización del objeto, guardamos el color original del marco especificando la bandera necesaria en el método para establecer su color, y establecemos los colores por defecto para el color del marco cuando el cursor del ratón se encuentre sobre el objeto y haga clic en él. Inicializamos el último evento del ratón:

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


En todos los métodos de creación de objetos en los que se requiere un color de fondo, escribimos una bandera. En el método CreateAndAddNewElement():

obj.SetBackgroundColor(colour,true);

En el método que establece el esquema de colores:

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

En cada caso, ahora se mantendrá el color de fondo original del elemento.


En el método que establece y retorna el estado del ratón respecto al formulario, antes declarábamos la variable local en la que escribíamos el estado del ratón y retornábamos el valor de esa variable. Ahora tenemos una variable de este tipo en la clase para almacenar este valor.
Aquí es donde escribiremos los valores y los retornaremos desde el 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;
  }
//+------------------------------------------------------------------+


Manejador de eventos del ratón:

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

Aquí, según el identificador del evento del ratón, llamamos para cada evento a su correspondiente método de manejo. Finalmente, guardamos el evento transmitido al método en una variable que almacena el último evento del ratón. De esta forma, primero procesaremos el evento y luego lo registraremos como el último evento procesado. Todos los métodos llamados aquí, analizados a continuación, son virtuales y deberán redefinirse en las clases herederas.

Manejadores virtuales para cada uno de los eventos del ratón:

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

Los métodos no hacen nada, ya que el procesamiento completo de cada evento es individual para cada uno de los objetos herederos del objeto de formulario, y deberá redefinirse en cada objeto heredero de ser necesario.


Manejador virtual del último evento del ratón:

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

El método también es virtual, y deberá redefinirse en las clases herederas de ser necesario.
De qué se encarga: si el estado del ratón para el objeto está definido como "cursor fuera del formulario" (no importa el estado de los botones y la ruleta), entonces comprobaremos cuál ha sido el último evento de ratón para el objeto. Si es "cursor dentro del área activa y sin botón del ratón pulsado", entonces considera que el cursor se ha retirado del objeto y procesa este evento - establece el color de fondo original, el color del marco del objeto original, redibuja el objeto y escribe su evento actual en la variable que almacena el último evento de ratón.
Teniendo en cuenta que todos los valores constantes de la enumeración ENUM_MOUSE_EVENT difieren de las constantes de la enumeración ENUM_MOUSE_FORM_STATE (con la misma composición y secuencia) en el valor de la constante MOUSE_EVENT_NO_EVENT de la enumeración ENUM_MOUSE_EVENT, para obtener el valor correcto, deberemos sumar el valor de la constante MOUSE_EVENT_NO_EVENT al valor del evento de ratón obtenido en la variable de estado ENUM_MOUSE_FORM_STATE.

El texto que se encuentre en el formulario, como el texto del botón, también puede (pero no necesariamente siempre y en todos los objetos, solo a voluntad) cambiar de color cuando se pasa el cursor por encima o se hace clic en el objeto. Nuestros métodos para el procesamiento de texto de los objetos WinForms se encuentran en la clase básica de los objetos WinForms, en el archivo \MQL5\Include\DoEasy\Objects\Graph\WForms\WinFormBase.mqh.

En la sección protegida de la clase, declaramos una variable para almacenar el color original del texto del objeto. En la sección pública, declaramos los métodos que retornan la lista de objetos adjuntos según el tipo, el número de elementos adjuntos según el tipo y el método que retorna el puntero a un objeto adjunto según el tipo.
Asimismo, escribimos los métodos para establecer y retornar los colores del texto de los objetos adicionales:

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


En el constructor de la clase, al establecer el color del texto , indicamos la necesidad de mantener el color original del texto:

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


En el método que retorna la descripción de una propiedad entera de un elemento , escribimos los bloques de código necesarios para retornar las descripciones de las nuevas propiedades del 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 la lista de elementos vinculados según el 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);
  }
//+------------------------------------------------------------------+

Aquí solo hay que retornar la lista de elementos gráficos seleccionados de la lista de objetos vinculados según el tipo indicado.


Método que retorna el elemento vinculado por tipo según el índice en la 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);
  }
//+------------------------------------------------------------------+

Aquí: recuperamos la lista de objetos según el tipo especificado usando el método descrito anteriormente, y retornamos el puntero al objeto en la lista obtenida según el índice indicado. Si no logramos obtener la lista, o si el índice excede el tamaño de la misma, el método retornará NULL.


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

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

Obtenemos una lista de objetos vinculados del tipo especificado y retornamos el número de punteros a los objetos de la lista. Si no hemos logrado obtener la lista, el método retornará cero.

En la clase de objeto de contenedor WinForms básico, en el archivo \MQL5\Include\DoEasy\Objects\Graph\WForms\Containers\Container.mqh, cambiamos el nombre de los métodos para establecer la anchura del marco. Antes usábamos "Frame" en el nombre, pero ahora usaremos "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());
                       }

Qué es lo importante: mientras que antes estos métodos tenían un nombre diferente, y llamar a un método de la clase padre para establecer la anchura del marco no suponía ningún problema, ahora se producirá una llamada recursiva al propio método. Así que aquí indicaremos explícitamente el contexto desde el que se llamará al método necesario de la clase padre que tiene exactamente el mismo nombre.

En los constructores de la clase, al guardar el color del texto, indicaremos la necesidad de conservar el color original de los rótulos del 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);
  }
//+------------------------------------------------------------------+


En el método que crea un nuevo elemento vinculado, indicaremos en todas partes la necesidad de guardar el color 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;
  }
//+------------------------------------------------------------------+


En el archivo \MQL5\Include\DoEasy\Objects\Graph\WForms\Containers\GroupBox.mqh de la clase WinForms del objeto GroupBox, en su método de inicialización Initialize(), escribiremos la necesidad de guardar los colores originales al establecer los colores por defecto:

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


En el archivo \MQL5\Include\DoEasy\Objects\Graph\WForms\Common Controls\CommonBase.mqh de la clase básica del objeto WinForms estándar, en su método Initialize(), escribiremos la necesidad de guardar los colores iniciales al establecer los colores por defecto:

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


El objeto CheckBox WinForms contiene una casilla de verificación y un texto. Al mover el cursor del ratón y pulsar en el botón, el color del propio campo, su marco y la casilla de verificación (marca de verificación) cambiarán en el objeto. Por consiguiente, para todos estos componentes, necesitaremos crear variables para almacenar sus colores originales, y también métodos para establecer los colores correspondientes al evento de ratón.

En el archivo \MQL5\Include\DoEasy\Objects\Graph\WForms\Common Controls\CheckBox.mqh, en la sección protegida de la clase, declaramos las variables para almacenar los colores iniciales y los métodos virtuales que manejan los eventos de ratón:

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


Modificaremos los métodos para establecer el color de fondo de la casilla, el marco del campo y la propia casilla de tal forma que podamos guardar el color original de campo según el valor de la bandera. Asimismo, añadiremos los métodos para establecer y retornar los colores originales, y declararemos un manejador para el último evento de ratón:

//--- (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


En el constructor de la clase, estableceremos los colores por defecto para los diferentes eventos y estados de ratón:

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


No todos los manejadores de eventos de ratón necesitan ser redefinidos aquí. Vamos a analizar los métodos redefinidos de la clase padre.

Manejador de eventos Cursor dentro del área activa, sin botones del ratón presionados:

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

Si el puntero del ratón se encuentra sobre el área activa del objeto, estableceremos la casilla de verificación, su marco y la propia casilla de verificación con los nuevos colores de fondo establecidos para ese estado de ratón y del objeto, y redibujaremos el objeto.


Controlador de eventos Cursor dentro del área activa, botón del ratón presionado (cualquiera):

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

Si el puntero del ratón se encuentra sobre el área activa del objeto, estableceremos la casilla de verificación, su marco y la propia casilla de verificación con los nuevos colores de fondo establecidos para ese estado de ratón y del objeto, y redibujaremos el objeto.


Manejador de eventos Cursor dentro del área activa, botón del ratón sin pulsar (izquierdo):

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

Aquí se procesan dos estados:

cuando el botón del ratón está sin pulsar y el cursor está fuera del área activa del objeto, y
cuando el botón del ratón está sin pulsar y el cursor está dentro del área activa del objeto.

Si el cursor está fuera del objeto, significará que el usuario ha pulsado el botón, luego ha alejado el cursor y ha soltado el botón. En esta situación, el cuadro de selección no deberá cambiar su estado: será una negativa a interactuar con el objeto. Por lo tanto, asignaremos al objeto los colores originales para la casilla y su campo.

Si el cursor está dentro del objeto, significará que el usuario ha pulsado el botón del ratón y luego lo ha soltado, lo cual debería hacer que el estado de la casilla de verificación cambie. Así que vamos a cambiar el color del campo, del marco del campo y de la casilla de verificación al color establecido para el objeto cuando el cursor pasa por encima de la zona activa (aunque se haya soltado el botón del ratón, el cursor permanece sobre el objeto, por lo que el color deberá ser el mismo que cuando el cursor está pasando por encima del objeto), y también a establecer el estado contrario para la casilla de verificación.
Al terminar, redibujaremos el objeto. Para cada una de las situaciones analizadas, mostraremos en el diario de registro una entrada de texto sobre el estado de la casilla. Posteriormente, al activarse la casilla, sustituiremos la muestra de la entrada por el envío de un evento para su posterior procesamiento.


Manejador virtual del último evento del ratón:

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

Aquí solo se procesa el estado cuando el ratón está fuera del objeto, y el estado anterior era "Cursor dentro del área activa y sin botones del ratón pulsados". En esta situación, estableceremos todas las propiedades de color modificables del objeto en sus valores originales y asignaremos al último evento de ratón su estado actual.


La clase de objeto WinForms RadioButton en el archivo \MQL5\Include\DoEasy\Objects\Graph\WForms\Common Controls\RadioButton.mqh se hereda de la clase recién finalizada del objeto CheckBox, por lo que aquí tendremos que hacer menos cambios.

Este objeto siempre está marcado y desmarcado dependiendo del otro objeto similar con el que está emparejado: si está marcado en un objeto, estará desmarcado en el otro, y viceversa. Por lo tanto, todo lo que debemos hacer aquí es redefinir un método virtual de la clase padre:

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


Manejador de eventos Cursor dentro del área activa, botón del ratón sin pulsar (izquierdo):

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

El método es similar al analizado para la clase de objeto CheckBox, salvo que aquí no necesitaremos establecer la casilla de verificación en su valor opuesto en el bloque que procesa la pulsación del botón dentro del área activa del objeto.


El objeto WinForms "Botón" (Button). El botón puede ser de dos tipos:

  1. Al ser pulsado, el botón se accionará y volverá a su estado original,
  2. Al ser pulsado, el botón se accionará y permanecerá en la posición pulsada; si se vuelve a pulsar, se invertirá su estado (disparador - Toggle-Button).

En consecuencia, necesitaremos una bandera que determine de qué botón se trata, una bandera para identificar el estado del botón de activación (pulsado/sin pulsar) y, por supuesto, otro conjunto para identificar los colores del botón cuando este sea pulsado (color de fondo, color al colocar el cursor y color de la pulsación del botón).

En el archivo \MQL5\Include\DoEasy\Objects\Graph\WForms\Common Controls\Button.mqh, en la sección privada de la clase, declaramos los arrays necesarios para almacenar los colores de los nuevos estados de los botones:

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


En la sección protegida de la clase, declaramos los manejadores de eventos de ratón de la clase padre para que sean redefinidos aquí:

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:


En la sección pública de la clase, escribimos los métodos para establecer y retornar la bandera del botón-disparador, los métodos para establecer y retornar el estado del botón-disparador, los métodos para trabajar con los colores de los nuevos estados del botón y declaramos el método-manejador para el último evento de ratón:

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

Los métodos son idénticos a los mismos métodos encargados de establecer y retornar los colores de los objetos en las otras clases que hemos examinado con anterioridad.


En el constructor de la clase, establecemos los colores por defecto para el fondo del objeto, su valor de opacidad y los valores de sus colores de fondo al pasar el ratón por encima del botón y clicar en él. A continuación, establecemos la bandera del botón normal (no del interruptor) y, en consecuencia, el estado del botón del interruptor en 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);
  }
//+------------------------------------------------------------------+


Después redefinimos los manejadores de eventos del ratón.

Manejador de eventos Cursor dentro del área activa, sin botones del ratón presionados:

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


Controlador de eventos Cursor dentro del área activa, botón del ratón presionado (cualquiera):

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


Manejador de eventos Cursor dentro del área activa, botón del ratón sin pulsar (izquierdo):

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

La lógica de cada método se detalla en los comentarios al código y esperamos que no requiera explicación.


Último manejador de eventos del ratón:

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

El método solo procesa el estado cuando el cursor del ratón está fuera del formulario y el último evento de ratón es el cursor sobre el formulario y no hay botones del ratón presionados. El color de fondo del botón se escribirá con su color original, dependiendo de si el botón está pulsado o no (para un botón simple, su valor retornado por el método State() es siempre false).

Método que establece los colores para el estado "activado" de un botón-interruptor:

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

Los colores para los tres estados del botón son transmitidos al método, y se establecen en las variables correspondientes. En este caso, la bandera set_init_color permite determinar si el color original del fondo del botón debe guardarse o no.


Vamos a mejorar la clase de colección de elementos gráficos en el archivo \MQL5\Include\DoEasy\Collections\GraphElementsCollection.mqh.

Tenemos dos bloques de código idénticos en el método GetFormUnderCursor(), donde se realiza la búsqueda de los objetos vinculados al formulario y que además se encuentran bajo el cursor. Sacaremos este bloque de código a un método aparte para acortar el código del método y hacer más clara su lógica.

En la sección privada de la clase, declaramos este método y el método para procesar los eventos del formulario del que se ha quitado el cursor:

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


En todos los métodos encargados de crear diversos elementos gráficos, los métodos para establecer los colores en los que es necesario indicar la bandera para almacenar el color original, ya tienen estas banderas escritas en sus métodos de ajuste de color (transmitimos el valor true porque necesitamos almacenar el color original al crear un objeto). Todos estos cambios ya los hemos realizado en la clase, y no los analizaremos aquí para ahorrar espacio.

Como ejemplo, aquí tenemos un método que crea un objeto de formulario gráfico en el lienzo en el gráfico y la subventana 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();
                       }

Estos cambios, y otros similares, se han realizado en todos los métodos en los que existe el ajuste de color.


Método para encontrar los objetos de interacción:

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

En el último artículo, vimos la lógica de este método, pero allí este código formaba parte del método GetFormUnderCursor().
Ahora, en lugar de este bloque de código, simplemente insertaremos una llamada al mismo:

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

Con este pequeño ajuste, el código del método ahora será más legible.


Método que procesa los eventos de ratón del antiguo formulario activo bajo el 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();
        }
     }
  }
//+------------------------------------------------------------------+

Cuando alejamos el cursor de un objeto de formulario en un gráfico, ese objeto perderá el foco y se seleccionará automáticamente otro objeto debajo del cursor. O bien no habrá ningún objeto bajo el cursor, en cuyo caso se activarán todas las funciones desactivadas del gráfico (su desplazamiento, su menú contextual, etcétera). No obstante, a veces debemos procesar los movimientos de los objetos, como cuando queremos cambiar el color del cursor al alejarlo de un objeto, en nuestro caso, para restaurar su color original. De eso exactamente se encarga este método. El método itera en un ciclo por todos los elementos gráficos de tipo "formulario" y superiores, creando para cada uno de ellos una lista de objetos vinculados, y luego recorre esos objetos para llamar al método de gestión de eventos de ratón que comentamos anteriormente para cada objeto sucesivo. De esta forma, se procesará cada uno de los objetos de cada formulario del gráfico, pero solo si su último evento de ratón es el que queremos (el cursor se encuentra sobre el objeto y ningún botón del ratón está pulsado). Después de procesar el evento, asignaremos al último evento de ratón del objeto su estado actual: el cursor está fuera del formulario. De esta manera, solo se procesará un objeto, aquel del que se ha retirado el cursor por última vez, y solo se procesará una vez.


Al colocar el cursor sobre un objeto de formulario que se puede mover y pulsar el botón del ratón, se habilitarán para el formulario en el manejador de eventos de la clase de colección de elementos gráficos las banderas de retención y movimiento del ratón. Pero si pulsamos un botón en un objeto vinculado al formulario, el objeto no se podrá mover: la bandera de movimiento se borrará para él, y el foco se establecerá en él, no en el formulario. Esto dejará la bandera de retención activada, mientras que la bandera de movimiento quedará desactivada. El evento de movimiento y la bandera de movimiento se manejarán simultáneamente:
      //--- 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)
           {
            //---...
            //---...

Tenemos que separarlos y entonces, si la bandera de movimiento está desactivada pero el ratón se está moviendo (con la bandera de retención activada), podremos detectar esta condición. Y si soltamos el botón del ratón, se registrará el evento MOUSE_FORM_STATE_NONE. Este evento solo aparece en este caso, ya que aún no hemos procesado tal situación; hablamos del evento de liberación del botón del ratón.

Por ello, necesitaremos separar la comprobación del evento de desplazamiento del cursor y la bandera de movimiento establecida. Y, en el caso de que la bandera de desplazamiento esté desactivada, deberemos definir el evento MOUSE_FORM_STATE_NONE y sustituirlo por el nuevo evento MOUSE_FORM_STATE_INSIDE_ACTIVE_AREA_RELEASED.

Simplemente mostraremos un fragmento de código reelaborado de la forma que hemos descrito anteriormente. Aquí hemos separado los dos eventos para comprobarlos por separado:

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

y ahora somos capaces de "atrapar" el nuevo evento de ratón, siempre que la bandera de desplazamiento esté desactivada, y también procesar inmediatamente el evento de desplazamiento para el objeto de formulario activo.

Ahora, en el propio manejador de eventos OnChartEvent(), en los bloques de los manejadores de eventos de ratón, solo nos queda llamar el manejador de eventos de ratón para el objeto indicando el evento hallado 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);
              }
           }
        }
     }
  }

Aquí también hemos añadido un nuevo manejador para procesar el evento de liberación del botón del ratón.


En el método que establece para el gráfico indicado las banderas de scrolling (con la ruleta del ratón) del gráfico, el menú contextual y la herramienta «retícula», comprobamos la bandera ya establecida, para así evitar poner constantemente el mismo estado que ya se ha establecido para el 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);
  }
//+------------------------------------------------------------------+


Estas son todas las mejoras de las clases de la biblioteca hasta ahora.


Simulación

Para la prueba, tomaremos el asesor del artículo anterior y lo guardaremos en la nueva carpeta \MQL5\Experts\TestDoEasy\Part110\ con el nuevo nombre TstDE110.mq5.

¿Cómo realizaremos la prueba? Dejaremos todos los objetos del asesor anterior "como están", solo cambiaremos un poco el tamaño y la posición del botón, y haremos que obtenga punteros a los objetos según su tipo, como prueba de que el método funciona.

En los parámetros del asesor, añadiremos otro: la bandera del botón-interruptor y cambiaremos los parámetros por defecto:

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

El manejador OnInit() contendrá ahora el siguiente 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);
  }
//+------------------------------------------------------------------+

En esencia, todo sigue como estaba, pero hemos cambiado los nombres de los métodos renombrados en la biblioteca: en los métodos de establecimiento del color, ahora se transmiten banderas(true) para almacenar el color original, y cuando seleccionamos un modo de botón concreto, el nombre de ese modo se mostrará como texto en el botón.
Podrá encontrar todos los cambios realizados en el asesor en los archivos adjuntos al artículo.

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


Como podemos ver, todos los componentes visuales declarados de los objetos funcionan correctamente al interactuar con el ratón.


¿Qué es lo próximo?

En el próximo artículo seguiremos trabajando en la interactividad de los objetos de la biblioteca WinForms.

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

Volver al contenido

*Artículos de esta serie:

DoEasy. Controles (Parte 1): Primeros pasos
DoEasy. Elementos de control (Parte 2): Continuamos trabajando con la clase CPanel
DoEasy. Elementos de control (Parte 3): Creando controles vinculados
DoEasy. Elementos de control (Parte 4): Elemento de control "Panel", parámetros Padding y Dock
DoEasy. Elementos de control (Parte 5): Objeto básico WinForms, control «Panel», parámetro AutoSize
DoEasy. Elementos de control (Parte 6): Control «Panel», cambio automático del tamaño del contenedor según el contenido interno
DoEasy. Elementos de control (Parte 7): Elemento de control «etiqueta de texto»
DoEasy. Elementos de control (Parte 8): Objetos básicos WinForms por categorías, controles "GroupBox" y "CheckBox
DoEasy. Elementos de control (Parte 9): Reorganizando los métodos de los objetos WinForms, los controles "RadioButton" y "Button"



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

Archivos adjuntos |
MQL5.zip (4389.02 KB)
Trabajamos con matrices y vectores en MQL5 Trabajamos con matrices y vectores en MQL5
Para resolver problemas matemáticos, se han añadido a MQL5 matrices y vectores. Los nuevos tipos tienen métodos incorporados para escribir un código conciso y fácilmente comprensible que se acerque a una notación matemática. Los arrays son algo bueno, pero las matrices, en muchos casos, resultan mejores.
Desarrollo de un EA comercial desde cero (Parte 23): Un nuevo sistema de órdenes (VI) Desarrollo de un EA comercial desde cero (Parte 23): Un nuevo sistema de órdenes (VI)
Haremos más fluido el sistema de ordenes. Aquí les mostraré cómo y dónde hacer cambios en el código para tener algo más fluido que nos permita modificar los límites de posición mucho más rápido.
Desarrollo de un EA comercial desde cero (Parte 24): Dotando de robustez al sistema (I) Desarrollo de un EA comercial desde cero (Parte 24): Dotando de robustez al sistema (I)
En este artículo haremos que el sistema sea más robusto, para que sea más estable y seguro de usar. Una forma de conseguir robustez es intentar reutilizar el código lo máximo posible, de esta forma él mismo será probado todo el tiempo y en diversas ocasiones. Pero esta es solo una de las formas, otra forma es el uso de la programación OOP.
Desarrollando un EA comercial desde cero (Parte 22): Un nuevo sistema de órdenes (V) Desarrollando un EA comercial desde cero (Parte 22): Un nuevo sistema de órdenes (V)
Hoy seguiremos desarrollando el nuevo sistema de ordenes. No es nada fácil implementar un nuevo sistema, muchas veces nos encontramos con problemas que dificultan mucho el proceso, cuando suceden hay que parar y volver a analizar el rumbo que se está tomando.