English Русский 中文 Español Deutsch 日本語
preview
DoEasy. Controles (Parte 9): Reorganizando métodos de objetos WinForms, controles "RadioButton" e "Button"

DoEasy. Controles (Parte 9): Reorganizando métodos de objetos WinForms, controles "RadioButton" e "Button"

MetaTrader 5Exemplos | 21 setembro 2022, 09:53
172 0
Artyom Trishkin
Artyom Trishkin

Conteúdo


Ideia

Hoje começaremos a limpar os nomes dos métodos dos objetos WinForms e adaptar sua lógica de construção ao conceito estabelecido para todos os objetos da biblioteca - cada objeto possui um conjunto de propriedades inteiras, real e de string que são inerentes a todos os WinForms objetos, mas cada objeto específico tem um sinalizador para habilitar tal o qual propriedade. Se um objeto não suportar determinada propriedade, o sinalizador dessa propriedade será desmarcado e o objeto não a usará.

Embora antes armazenássemos muitas propriedades apenas em variáveis de classe, agora todas as propriedades serão adaptadas ao conceito geral que está por trás da biblioteca. Desse modo, facilitaremos a criação, por exemplo, de construtores visuais de objetos gráficos baseados nesta biblioteca - simplesmente percorrendo em um loop todas as propriedades, podemos exibir, em um painel, os nomes das propriedades do objeto gráfico em construção e o elementos para alterar cada propriedade, sendo que nesta lista somente as propriedades suportadas pelo objeto estarão presentes. Isto quer dizer que qualquer objeto deve usar um único método para mostrar suas propriedades, e para cada um dos diversos objetos deve ser construída uma lista específica e personalizada de propriedades suportadas.

Hoje também vamos começar a trabalhar para que objetos WinForms ainda estáticos ganhem vida: a biblioteca vai agora "ver" quais elementos têm o cursor (porque um painel-contêiner pode conter muitos outros elementos que requerem interação), e mais tarde vamos manejar tais interações com o mouse e enviar eventos para responder a eles.

Naturalmente, hoje vamos criar dois objetos WinForms, nomeadamente Button e RadioButton. Dado que esses objetos WinForms são padrão provenientes da categoria de controles padrão, não precisam ser apresentados. Claro, todos esses elementos já criados serão modificados de tal maneira que funcionalidade de interatividade esteja presente neles, porém, adicionaremos a base para vários controles e os métodos que permitem a interação com o mouse e efeitos visuais.


Modificando as classes da biblioteca

A valores que são definidos por padrão quando os controles são criados. Vamos adicionar uma lista de tais valores.

No arquivo \MQL5\Include\DoEasy\Defines.mqh, na seção de parâmetros da tela, inserimos as macro-substituições para as cores do elemento CheckBox:


#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'0xD9,0xEC,0xEB')        // 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'0xBA,0xEB,0xF5')        // Color of control checkbox background when clicking on the control
#define CLR_DEF_CHECK_BACK_MOUSE_OVER  (C'0xCE,0xE0,0xE3')        // Color of control checkbox background when hovering the mouse over the control
#define CLR_DEF_CHECK_FORE_COLOR       (C'0x2D,0x43,0x48')        // Color of control checkbox frame
#define CLR_DEF_CHECK_FORE_OPACITY     (255)                      // Opacity of the control checkbox frame color
#define CLR_DEF_CHECK_FORE_MOUSE_DOWN  (C'0x06,0x0B,0xAA')        //  Color of control checkbox frame when clicking on the control
#define CLR_DEF_CHECK_FORE_MOUSE_OVER  (C'0x06,0x0B,0xAA')        // 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'0x0E,0x9B,0x0B')        // Color of control checkbox when clicking on the control
#define CLR_DEF_CHECK_FLAG_MOUSE_OVER  (C'0x0E,0xC7,0x2E')        // Color of control checkbox when hovering the mouse over the control

#define CLR_DEF_CONTROL_STD_BACK_COLOR (C'0xCD,0xD8,0xDA')        // Standard controls background color
#define CLR_DEF_CONTROL_STD_OPACITY    (255)                      // Opacity of standard controls background color

#define DEF_FONT                       ("Calibri")                // Default font
#define DEF_FONT_SIZE                  (8)                        // Default font size


Na lista de tipos de elementos gráficos, inserimos os dois novos tipos de elementos que criaremos hoje:

//+------------------------------------------------------------------+
//| The list of graphical element types                              |
//+------------------------------------------------------------------+
enum ENUM_GRAPH_ELEMENT_TYPE
  {
   GRAPH_ELEMENT_TYPE_STANDARD,                       // Standard graphical object
   GRAPH_ELEMENT_TYPE_STANDARD_EXTENDED,              // Extended standard graphical object
   GRAPH_ELEMENT_TYPE_SHADOW_OBJ,                     // Shadow object
   GRAPH_ELEMENT_TYPE_ELEMENT,                        // Element
   GRAPH_ELEMENT_TYPE_FORM,                           // Form
   GRAPH_ELEMENT_TYPE_WINDOW,                         // Window
   //--- WinForms
   GRAPH_ELEMENT_TYPE_WF_UNDERLAY,                    // Panel object underlay
   GRAPH_ELEMENT_TYPE_WF_BASE,                        // Windows Forms Base
   GRAPH_ELEMENT_TYPE_WF_CONTAINER,                   // Windows Forms container base object
   GRAPH_ELEMENT_TYPE_WF_PANEL,                       // Windows Forms Panel
   GRAPH_ELEMENT_TYPE_WF_GROUPBOX,                    // Windows Forms GroupBox
   GRAPH_ELEMENT_TYPE_WF_COMMON_BASE,                 // Windows Forms base standard control
   GRAPH_ELEMENT_TYPE_WF_LABEL,                       // Windows Forms Label
   GRAPH_ELEMENT_TYPE_WF_BUTTON,                      // Windows Forms Button
   GRAPH_ELEMENT_TYPE_WF_CHECKBOX,                    // Windows Forms ChackBox
   GRAPH_ELEMENT_TYPE_WF_RADIOBUTTON,                 // Windows Forms RadioButton
  };
//+------------------------------------------------------------------+


Na enumeração das propriedades inteiras do elemento gráfico na tela, adicionamos novas propriedades do elemento e alteramos o número total de propriedades inteiras de 48 para 71:

//+------------------------------------------------------------------+
//| 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_BELONG,                          // Graphical element affiliation
   CANV_ELEMENT_PROP_NUM,                             // Element index in the list
   CANV_ELEMENT_PROP_CHART_ID,                        // Chart ID
   CANV_ELEMENT_PROP_WND_NUM,                         // Chart subwindow index
   CANV_ELEMENT_PROP_COORD_X,                         // Element X coordinate on the chart
   CANV_ELEMENT_PROP_COORD_Y,                         // Element Y coordinate on the chart
   CANV_ELEMENT_PROP_WIDTH,                           // Element width
   CANV_ELEMENT_PROP_HEIGHT,                          // Element height
   CANV_ELEMENT_PROP_RIGHT,                           // Element right border
   CANV_ELEMENT_PROP_BOTTOM,                          // Element bottom border
   CANV_ELEMENT_PROP_ACT_SHIFT_LEFT,                  // Active area offset from the left edge of the element
   CANV_ELEMENT_PROP_ACT_SHIFT_TOP,                   // Active area offset from the upper edge of the element
   CANV_ELEMENT_PROP_ACT_SHIFT_RIGHT,                 // Active area offset from the right edge of the element
   CANV_ELEMENT_PROP_ACT_SHIFT_BOTTOM,                // Active area offset from the bottom edge of the element
   CANV_ELEMENT_PROP_MOVABLE,                         // Element moveability flag
   CANV_ELEMENT_PROP_ACTIVE,                          // Element activity flag
   CANV_ELEMENT_PROP_INTERACTION,                     // Flag of interaction with the outside environment
   CANV_ELEMENT_PROP_COORD_ACT_X,                     // X coordinate of the element active area
   CANV_ELEMENT_PROP_COORD_ACT_Y,                     // Y coordinate of the element active area
   CANV_ELEMENT_PROP_ACT_RIGHT,                       // Right border of the element active area
   CANV_ELEMENT_PROP_ACT_BOTTOM,                      // Bottom border of the element active area
   CANV_ELEMENT_PROP_ZORDER,                          // Priority of a graphical object for receiving the event of clicking on a chart
   CANV_ELEMENT_PROP_ENABLED,                         // Element availability flag
   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_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_BOLD_TYPE,                       // Font width type
   CANV_ELEMENT_PROP_BORDER_STYLE,                    // Control frame style
   
   CANV_ELEMENT_PROP_BORDER_SIZE_TOP,                 // Control frame top size
   CANV_ELEMENT_PROP_BORDER_SIZE_BOTTOM,              // Control frame bottom size
   CANV_ELEMENT_PROP_BORDER_SIZE_LEFT,                // Control frame left size
   CANV_ELEMENT_PROP_BORDER_SIZE_RIGHT,               // Control frame right size
   CANV_ELEMENT_PROP_BORDER_COLOR,                    // Control frame color
   CANV_ELEMENT_PROP_BORDER_COLOR_MOUSE_DOWN,         // Control frame color when clicking on the control
   CANV_ELEMENT_PROP_BORDER_COLOR_MOUSE_OVER,         // Control frame color when hovering the mouse over the control
   
   CANV_ELEMENT_PROP_AUTOSIZE,                        // Flag of the element auto resizing depending on the content
   CANV_ELEMENT_PROP_AUTOSIZE_MODE,                   // Mode of the element auto resizing depending on the content
//--- ...   
//--- ...
   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_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_BACKGROUND_COLOR_MOUSE_DOWN,// Color of control checkbox background when clicking on the control
   CANV_ELEMENT_PROP_CHECK_BACKGROUND_COLOR_MOUSE_OVER,// Color of control checkbox background when hovering the mouse over the control
   CANV_ELEMENT_PROP_CHECK_FORE_COLOR,                // Color of control checkbox frame
   CANV_ELEMENT_PROP_CHECK_FORE_COLOR_OPACITY,        // Opacity of the control checkbox frame color
   CANV_ELEMENT_PROP_CHECK_FORE_COLOR_MOUSE_DOWN,     // Color of control checkbox frame when clicking on the control
   CANV_ELEMENT_PROP_CHECK_FORE_COLOR_MOUSE_OVER,     // Color of control checkbox frame when hovering the mouse over the control
   CANV_ELEMENT_PROP_CHECK_FLAG_COLOR,                // Color of control checkbox
   CANV_ELEMENT_PROP_CHECK_FLAG_COLOR_OPACITY,        // Opacity of the control checkbox 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 (71)          // Total number of integer properties
#define CANV_ELEMENT_PROP_INTEGER_SKIP  (0)           // Number of integer properties not used in sorting


Na lista de possíveis critérios para classificar elementos gráficos na tela, inserimos novos critérios correspondentes às propriedades recém-adicionadas:

//+------------------------------------------------------------------+
//| 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_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_BOLD_TYPE,                    // Sort by font width type
   SORT_BY_CANV_ELEMENT_BORDER_STYLE,                 // Sort by control frame style
   
   SORT_BY_CANV_ELEMENT_BORDER_SIZE_TOP,              // Sort by control frame top size
   SORT_BY_CANV_ELEMENT_BORDER_SIZE_BOTTOM,           // Sort by control frame bottom size
   SORT_BY_CANV_ELEMENT_BORDER_SIZE_LEFT,             // Sort by control frame left size
   SORT_BY_CANV_ELEMENT_BORDER_SIZE_RIGHT,            // Sort by control frame right size
   SORT_BY_CANV_ELEMENT_BORDER_COLOR,                 // Sort by control frame color
   SORT_BY_CANV_ELEMENT_BORDER_COLOR_MOUSE_DOWN,      // Sort by control frame color when clicking on the control
   SORT_BY_CANV_ELEMENT_BORDER_COLOR_MOUSE_OVER,      // Sort by control frame color when hovering the mouse over the control
   
   SORT_BY_CANV_ELEMENT_AUTOSIZE,                     // Sort by the flag of the control auto resizing depending on the content
   SORT_BY_CANV_ELEMENT_AUTOSIZE_MODE,                // Sort by the mode of the control auto resizing depending on the content
//--- ...

   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_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_BACKGROUND_COLOR_MOUSE_DOWN,// Sort by color of control checkbox background when clicking on the control
   SORT_BY_CANV_ELEMENT_CHECK_BACKGROUND_COLOR_MOUSE_OVER,// Sort by color of control checkbox background when hovering the mouse over the control
   SORT_BY_CANV_ELEMENT_CHECK_FORE_COLOR,                // Sort by color of control checkbox frame
   SORT_BY_CANV_ELEMENT_CHECK_FORE_COLOR_OPACITY,        // Sort by opacity of control checkbox frame color
   SORT_BY_CANV_ELEMENT_CHECK_FORE_COLOR_MOUSE_DOWN,     // Sort by color of control checkbox frame when clicking on the control
   SORT_BY_CANV_ELEMENT_CHECK_FORE_COLOR_MOUSE_OVER,     // Sort by color of control checkbox frame when hovering the mouse over the control
   SORT_BY_CANV_ELEMENT_CHECK_FLAG_COLOR,                // Sort by color of control checkbox
   SORT_BY_CANV_ELEMENT_CHECK_FLAG_COLOR_OPACITY,        // Sort by opacity of control checkbox color
   SORT_BY_CANV_ELEMENT_CHECK_FLAG_COLOR_MOUSE_DOWN,     // Sort by color of control checkbox when clicking on the control
   SORT_BY_CANV_ELEMENT_CHECK_FLAG_COLOR_MOUSE_OVER,     // Sort by color of control checkbox when hovering the mouse over the control
//--- Sort by real properties

//--- Sort by string properties
   SORT_BY_CANV_ELEMENT_NAME_OBJ = FIRST_CANV_ELEMENT_STR_PROP,// Sort by an element object name
   SORT_BY_CANV_ELEMENT_NAME_RES,                     // Sort by the graphical resource name
   SORT_BY_CANV_ELEMENT_TEXT,                         // Sort by graphical element text
  };
//+------------------------------------------------------------------+

Agora poderemos ordenar, selecionar e filtrar todos os elementos gráficos por novas propriedades.


No arquivo \MQL5\Include\DoEasy\Data.mqh, escrevemos os índices das novas mensagens:

   MSG_LIB_TEXT_NOVEMBER,                             // November
   MSG_LIB_TEXT_DECEMBER,                             // December

   MSG_LIB_TEXT_FONT_STYLE_ITALIC,                    // Italic
   MSG_LIB_TEXT_FONT_STYLE_UNDERLINE,                 // Underline
   MSG_LIB_TEXT_FONT_STYLE_STRIKEOUT,                 // Strikeout
   MSG_LIB_TEXT_FONT_STYLE_NORMAL,                    // Normal
   
   MSG_LIB_TEXT_FRAME_STYLE_NONE,                     // None
   MSG_LIB_TEXT_FRAME_STYLE_SIMPLE,                   // Simple
   MSG_LIB_TEXT_FRAME_STYLE_FLAT,                     // Flat
   MSG_LIB_TEXT_FRAME_STYLE_BEVEL,                    // Embossed convex
   MSG_LIB_TEXT_FRAME_STYLE_STAMP,                    // Embossed concave
   
   MSG_LIB_TEXT_ALIGN_LEFT,                           // Left alignment 
   MSG_LIB_TEXT_ALIGN_CENTER,                         // Center alignment

...

   MSG_LIB_TEXT_BORDER_RAISED,                        // Raised
   MSG_LIB_TEXT_BORDER_SUNKEN,                        // Sunken
   
   MSG_LIB_TEXT_AUTO_SIZE_MODE_GROW,                  // Increase only
   MSG_LIB_TEXT_AUTO_SIZE_MODE_GROW_SHRINK,           // Increase and decrease
   
   MSG_LIB_TEXT_DOCK_MODE_NONE,                       // Attached to the specified coordinates, size does not change
   MSG_LIB_TEXT_DOCK_MODE_TOP,                        // Attaching to the top and stretching along the container width
   MSG_LIB_TEXT_DOCK_MODE_BOTTOM,                     // Attaching to the bottom and stretching along the container width
   MSG_LIB_TEXT_DOCK_MODE_LEFT,                       // Attaching to the left and stretching along the container height
   MSG_LIB_TEXT_DOCK_MODE_RIGHT,                      // Attaching to the right and stretching along the container height
   MSG_LIB_TEXT_DOCK_MODE_FILL,                       // Stretching along the entire container width and height
   
   MSG_LIB_TEXT_CHEK_STATE_UNCHECKED,                 // Unchecked
   MSG_LIB_TEXT_CHEK_STATE_CHECKED,                   // Checked
   MSG_LIB_TEXT_CHEK_STATE_INDETERMINATE,             // Undefined
   
   MSG_LIB_TEXT_SUNDAY,                               // Sunday
   MSG_LIB_TEXT_MONDAY,                               // Monday

...

   MSG_GRAPH_ELEMENT_TYPE_WF_UNDERLAY,                // Underlay of the Panel WinForms control object
   MSG_GRAPH_ELEMENT_TYPE_WF_BASE,                    // WinForms base control
   
//--- WinForms containers
   MSG_GRAPH_ELEMENT_TYPE_WF_CONTAINER,               // WinForms container base control
   MSG_GRAPH_ELEMENT_TYPE_WF_GROUPBOX,                // GroupBox control
   MSG_GRAPH_ELEMENT_TYPE_WF_PANEL,                   // Panel control
   
//--- WinForms standard
   MSG_GRAPH_ELEMENT_TYPE_WF_COMMON_BASE,             // WinForms base standard control
   MSG_GRAPH_ELEMENT_TYPE_WF_LABEL,                   // Label control
   MSG_GRAPH_ELEMENT_TYPE_WF_CHECKBOX,                // CheckBox control
   MSG_GRAPH_ELEMENT_TYPE_WF_RADIOBUTTON,             // RadioButton control
   MSG_GRAPH_ELEMENT_TYPE_WF_BUTTON,                  // Button control
   
   MSG_GRAPH_OBJ_BELONG_PROGRAM,                      // Graphical object belongs to a program
   MSG_GRAPH_OBJ_BELONG_NO_PROGRAM,                   // Graphical object does not belong to a program

...

//--- CPanel
   MSG_PANEL_OBJECT_ERR_FAILED_CREATE_UNDERLAY_OBJ,   // Failed to create the underlay object
   MSG_PANEL_OBJECT_ERR_OBJ_MUST_BE_WFBASE,           // Error. The created object should be of WinForms Base type or be derived from it

//--- Integer properties of graphical elements
   MSG_CANV_ELEMENT_PROP_ID,                          // Element ID
   MSG_CANV_ELEMENT_PROP_TYPE,                        // Graphical element type
   MSG_CANV_ELEMENT_PROP_BELONG,                      // Graphical element affiliation
   MSG_CANV_ELEMENT_PROP_NUM,                         // Element index in the list
   MSG_CANV_ELEMENT_PROP_COORD_X,                     // Element X coordinate on the chart
   MSG_CANV_ELEMENT_PROP_COORD_Y,                     // Element Y coordinate on the chart
   MSG_CANV_ELEMENT_PROP_WIDTH,                       // Element width
   MSG_CANV_ELEMENT_PROP_HEIGHT,                      // Element height
   MSG_CANV_ELEMENT_PROP_RIGHT,                       // Element right border
   MSG_CANV_ELEMENT_PROP_BOTTOM,                      // Element bottom border
   MSG_CANV_ELEMENT_PROP_ACT_SHIFT_LEFT,              // Active area offset from the left edge of the element
   MSG_CANV_ELEMENT_PROP_ACT_SHIFT_TOP,               // Active area offset from the upper edge of the element
   MSG_CANV_ELEMENT_PROP_ACT_SHIFT_RIGHT,             // Active area offset from the right edge of the element
   MSG_CANV_ELEMENT_PROP_ACT_SHIFT_BOTTOM,            // Active area offset from the bottom edge of the element
   MSG_CANV_ELEMENT_PROP_MOVABLE,                     // Element moveability flag
   MSG_CANV_ELEMENT_PROP_IS_MOVABLE,                  // Movable element
   MSG_CANV_ELEMENT_PROP_ACTIVE,                      // Element activity flag
   MSG_CANV_ELEMENT_PROP_IS_ACTIVE,                   // Element active
   MSG_CANV_ELEMENT_PROP_INTERACTION,                 // Flag of interaction with the outside environment
   MSG_CANV_ELEMENT_PROP_COORD_ACT_X,                 // X coordinate of the element active area
   MSG_CANV_ELEMENT_PROP_COORD_ACT_Y,                 // Y coordinate of the element active area
   MSG_CANV_ELEMENT_PROP_ACT_RIGHT,                   // Right border of the element active area
   MSG_CANV_ELEMENT_PROP_ACT_BOTTOM,                  // Bottom border of the element active area
   MSG_CANV_ELEMENT_PROP_ENABLED,                     // Element availability flag
   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_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_BOLD_TYPE,                   // Font width type
   MSG_CANV_ELEMENT_PROP_BORDER_STYLE,                // Control frame style
   MSG_CANV_ELEMENT_PROP_BORDER_SIZE_TOP,             // Control frame top size
   MSG_CANV_ELEMENT_PROP_BORDER_SIZE_BOTTOM,          // Control frame bottom size
   MSG_CANV_ELEMENT_PROP_BORDER_SIZE_LEFT,            // Control frame left size
   MSG_CANV_ELEMENT_PROP_BORDER_SIZE_RIGHT,           // Control frame right size
   MSG_CANV_ELEMENT_PROP_BORDER_COLOR,                // Control frame color
   MSG_CANV_ELEMENT_PROP_BORDER_COLOR_MOUSE_DOWN,     // Control frame color when clicking on the control
   MSG_CANV_ELEMENT_PROP_BORDER_COLOR_MOUSE_OVER,     // Control frame color when hovering the mouse over the control
   MSG_CANV_ELEMENT_PROP_AUTOSIZE,                    // Flag of the element auto resizing depending on the content
   MSG_CANV_ELEMENT_PROP_IS_AUTOSIZE,                 // The element automatically resizes to fit the content
   MSG_CANV_ELEMENT_PROP_AUTOSIZE_MODE,               // Mode of the element auto resizing depending on the content
   MSG_CANV_ELEMENT_PROP_AUTOSCROLL,                  // Auto scrollbar flag
   MSG_CANV_ELEMENT_PROP_IS_AUTOSCROLL,               // Scrollbar appears automatically 
   MSG_CANV_ELEMENT_PROP_AUTOSCROLL_MARGIN_W,         // Width of the field inside the element during auto scrolling
   MSG_CANV_ELEMENT_PROP_AUTOSCROLL_MARGIN_H,         // Height of the field inside the element during auto scrolling
   MSG_CANV_ELEMENT_PROP_DOCK_MODE,                   // Mode of binding control borders to the container
   MSG_CANV_ELEMENT_PROP_MARGIN_TOP,                  // Top margin between the fields of this and another control
   MSG_CANV_ELEMENT_PROP_MARGIN_BOTTOM,               // Bottom margin between the fields of this and another control
   MSG_CANV_ELEMENT_PROP_MARGIN_LEFT,                 // Left margin between the fields of this and another control
   MSG_CANV_ELEMENT_PROP_MARGIN_RIGHT,                // Right margin between the fields of this and another control
   MSG_CANV_ELEMENT_PROP_PADDING_TOP,                 // Top margin inside the control
   MSG_CANV_ELEMENT_PROP_PADDING_BOTTOM,              // Bottom margin inside the control
   MSG_CANV_ELEMENT_PROP_PADDING_LEFT,                // Left margin inside the control
   MSG_CANV_ELEMENT_PROP_PADDING_RIGHT,               // Right margin inside the control
   MSG_CANV_ELEMENT_PROP_TEXT_ALIGN,                  // Text position within text label boundaries
   MSG_CANV_ELEMENT_PROP_CHECK_ALIGN,                 // Position of the checkbox within control borders
   MSG_CANV_ELEMENT_PROP_CHECKED,                     // Control checkbox status
   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
   
//--- Real properties of graphical elements

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

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

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

   {"Ноябрь","November"},
   {"Декабрь","December"},
   
   {"Курсив","Italic"},
   {"Подчёркивание","Underline"},
   {"Перечёркивание","Strikeout"},
   {"Обычный","Normal"},
   
   {"Отсутствует","Enpty"},
   {"Простая","Simple"},
   {"Плоская","Flat"},
   {"Рельефная выпуклая","Bevel"},
   {"Рельефная вдавленная","Stamp"},
   
   {"Выравнивание по левой границе","Left alignment"},
   {"Выравнивание по центру","Centered"},

...

   {"Выпуклый вид","Prominent form"},
   {"Вогнутый вид","Concave form"},
   
   {"Только увеличение","Grow"},
   {"Увеличение и уменьшение","Grow and Shrink"},
   
   {"Прикреплён к указанным координатам, размеры не меняются","Attached to specified coordinates, size does not change"},
   {"Присоединение сверху и растягивание на ширину контейнера","Attached to the top and stretched to the container width"},
   {"Присоединение снизу и растягивание на ширину контейнера","Attached to the bottom and stretch to the width of the container"},
   {"Присоединение слева и растягивание на высоту контейнера","Attached to the left and stretched to the height of the container"},
   {"Присоединение справа и растягивание на высоту контейнера","Attached to the right and stretched to the height of the container"},
   {"Растягивание на ширину и высоту всего контейнера","Stretching to the width and height of the entire container"},
   
   {"Не установлен","Unchecked"},
   {"Установлен","Checked"},
   {"Неопределённый","Indeterminate"},
   
   {"Воскресение","Sunday"},
   {"Понедельник","Monday"},

...

   {"Подложка объекта-элемента управления WinForms \"Панель\"","Underlay object-control WinForms \"Panel\""},
   {"Базовый элемент управления WinForms","Base WinForms control"},
   
//--- WinForms containers
   {"Базовый элемент управления WinForms-контейнер","Basic Control WinForms Container"},
   {"Элемент управления GroupBox","Control element \"GroupBox\""},
   {"Элемент управления \"Panel\"","Control element \"Panel\""},
   
//--- WinForms standard
   {"Базовый стандартный элемент управления WinForms","Basic Standard WinForms Control"},
   {"Элемент управления \"Label\"","Control element \"Label\""},
   {"Элемент управления \"CheckBox\"","Control element \"CheckBox\""},
   {"Элемент управления \"RadioButton\"","Control element \"RadioButton\""},
   {"Элемент управления \"Button\"","Control element \"Button\""},
   
   {"Графический объект принадлежит программе","The graphic object belongs to the program"},
   {"Графический объект не принадлежит программе","The graphic object does not belong to the program"},

...

//--- CPanel
   {"Не удалось создать объект-подложку","Failed to create underlay object"},
   {"Ошибка. Создаваемый объект должен иметь тип WinForms Base или быть его наследником","Error. The object being created must be of type WinForms Base or be derived from it"},

//--- Integer properties of graphical elements
   {"Идентификатор элемента","Element ID"},
   {"Тип графического элемента","Graphic element type"},
   {"Принадлежность графического элемента","Graphic element belong"},
   {"Номер элемента в списке","The number of the element in the list"},
   {"X-координата элемента на графике","X-coordinate of the element on the chart"},
   {"Y-координата элемента на графике","Y-coordinate of the element on the chart"},
   {"Ширина элемента","Element Width"},
   {"Высота элемента","Element Height"},
   {"Правая граница элемента","Element's right border"},
   {"Нижняя граница элемента","Element's bottom border"},
   {"Отступ активной зоны от левого края элемента","Active area indent from the left edge of the element"},
   {"Отступ активной зоны от верхнего края элемента","Active area indent from the top edge of the element"},
   {"Отступ активной зоны от правого края элемента","Active area indent from the right edge of the element"},
   {"Отступ активной зоны от нижнего края элемента","Active area indent from the bottom edge of the element"},
   {"Флаг перемещаемости элемента","Element mobility flag"},
   {"Элемент перемещаемый","Element can be moved"},
   {"Флаг активности элемента","Element activity flag"},
   {"Элемент активен","Element active"},
   {"Флаг взаимодействия элемента со внешней средой","Flag of the interaction of the element with the external environment"},
   {"X-координата активной зоны элемента","X-coordinate of the element's active area"},
   {"Y-координата активной зоны элемента","Y-coordinate of the element's active area"},
   {"Правая граница активной зоны элемента","Right border of the element's active area"},
   {"Нижняя граница активной зоны элемента","Bottom border of the element's active area"},
   {"Флаг доступности элемента","Element Availability Flag"},
   {"Цвет текста по умолчанию для всех объектов элемента управления","Default text color for all objects in the control"},
   {"Непрозрачность цвета текста по умолчанию для всех объектов элемента управления","Default text color opacity for all objects in 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"},
   {"Тип толщины шрифта","Font weight type"},
   {"Стиль рамки элемента управления","Control's border style"},
   {"Размер рамки элемента управления сверху","Control's border size on the top"},
   {"Размер рамки элемента управления снизу","Control's border size on the bottom"},
   {"Размер рамки элемента управления слева","Control's border size on the left"},
   {"Размер рамки элемента управления справа","Control's border size on the right"},
   {"Цвет рамки элемента управления","Control's border color"},
   {"Цвет рамки элемента управления при нажатии мышки на элемент управления","Border color of the control when the mouse is clicked on the control"},
   {"Цвет рамки элемента управления при наведении мышки на элемент управления","Border color of the control when hovering the mouse over the control"},
   {"Флаг автоматического изменения размера элемента управления под содержимое","Automatically resize a control to fit its content"},
   {"Элемент автоматически измененяет размер под содержимое","Element automatically resizes to fit the content"},
   {"Режим автоматического изменения размера элемента управления под содержимое","Mode for automatically resizing a control to fit its content"},
   {"Флаг автоматического появления полосы прокрутки","Scrollbar auto-appear flag"},
   {"Полоса прокрутки автоматически появляется","Scroll bar automatically appears"},
   {"Ширина поля вокруг элемента при автоматической прокрутке","Margin width around element when auto scrolling"},
   {"Высота поля вокруг элемента при автоматической прокрутке","Height of margin around element when auto scrolling"},
   {"Режим привязки границ элемента управления к контейнеру","Binding mode of the control's borders to the container"},
   {"Промежуток сверху между полями данного и другого элемента управления","Top spacing between the margins of this control and another control"},
   {"Промежуток снизу между полями данного и другого элемента управления","Bottom spacing between the margins of this control and another control"},
   {"Промежуток слева между полями данного и другого элемента управления","Left spacing between the margins of this control and another control"},
   {"Промежуток справа между полями данного и другого элемента управления","Right spacing between the margins of this control and another control"},
   {"Промежуток сверху внутри элемента управления","Top spacing inside a control"},
   {"Промежуток снизу внутри элемента управления","Bottom spacing inside a control"},
   {"Промежуток слева внутри элемента управления","Left spacing inside a control"},
   {"Промежуток справа внутри элемента управления","Right spacing inside a control"},
   {"Положение текста в границах текстовой метки","Text position within text label bounds"},
   {"Положение флажка проверки в границах элемента управления","The position of the checkbox within the control's bounds"},
   {"Состояние флажка проверки элемента управления","Checkbox state of the control"},
   {"Состояние элемента управления, имеющего флажок проверки","The state of a control that has a checkbox"},
   {"Автоматическое изменение состояния флажка при его выборе","Automatically change the state of the checkbox when it is selected"},

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

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

Precisaremos de todas essas mensagens no futuro para exibir uma descrição das propriedades dos controles gráficos.


A descrição dos tipos de controle é gerada em um método que retorna uma descrição do tipo de elemento gráfico, a classe do objeto gráfico base da biblioteca no arquivo \MQL5\Include\DoEasy\Objects\Graph\GBaseObj.mqh. Vamos complementá-lo com linhas que retornam descrições de novos tipos:

//+------------------------------------------------------------------+
//| Return the description of the graphical element type             |
//+------------------------------------------------------------------+
string CGBaseObj::TypeElementDescription(void)
  {
   return
     (
      this.TypeGraphElement()==GRAPH_ELEMENT_TYPE_STANDARD           ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_STANDARD)           :
      this.TypeGraphElement()==GRAPH_ELEMENT_TYPE_STANDARD_EXTENDED  ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_STANDARD_EXTENDED)  :
      this.TypeGraphElement()==GRAPH_ELEMENT_TYPE_ELEMENT            ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_ELEMENT)            :
      this.TypeGraphElement()==GRAPH_ELEMENT_TYPE_SHADOW_OBJ         ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_SHADOW_OBJ)         :
      this.TypeGraphElement()==GRAPH_ELEMENT_TYPE_FORM               ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_FORM)               :
      this.TypeGraphElement()==GRAPH_ELEMENT_TYPE_WINDOW             ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WINDOW)             :
      //---
      this.TypeGraphElement()==GRAPH_ELEMENT_TYPE_WF_UNDERLAY        ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_UNDERLAY)        :
      this.TypeGraphElement()==GRAPH_ELEMENT_TYPE_WF_BASE            ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_BASE)            :
      
      this.TypeGraphElement()==GRAPH_ELEMENT_TYPE_WF_CONTAINER       ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_CONTAINER)       :
      this.TypeGraphElement()==GRAPH_ELEMENT_TYPE_WF_GROUPBOX        ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_GROUPBOX)        :
      this.TypeGraphElement()==GRAPH_ELEMENT_TYPE_WF_PANEL           ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_PANEL)           :
      this.TypeGraphElement()==GRAPH_ELEMENT_TYPE_WF_COMMON_BASE     ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_COMMON_BASE)     :
      this.TypeGraphElement()==GRAPH_ELEMENT_TYPE_WF_LABEL           ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_LABEL)           :
      this.TypeGraphElement()==GRAPH_ELEMENT_TYPE_WF_CHECKBOX        ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_CHECKBOX)        :
      this.TypeGraphElement()==GRAPH_ELEMENT_TYPE_WF_RADIOBUTTON     ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_RADIOBUTTON)     :
      this.TypeGraphElement()==GRAPH_ELEMENT_TYPE_WF_BUTTON          ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_BUTTON)          :
      "Unknown"
     );
  }
//+------------------------------------------------------------------+

Dependendo do tipo do elemento gráfico, o método retorna uma string com uma descrição desse tipo.

Temos inconsistências feias nos nomes dos métodos que definem ou retornam cores. Por exemplo, o método ForeColor() retorna a cor do texto. Ao mesmo tempo, o método que retorna a cor de fundo é denominado ColorBackground(). Para organizar os nomes entre si, o método ColorBackground() e outros semelhantes, vamos renomeá-los para BackgroundColor(). Além disso, introduzimos novas propriedades de elementos gráficos, e eles têm propriedades de cor ao passar e pressionar o cursor do mouse sobre um objeto. Também precisamos acrescentar métodos para definir e devolver estas e todas as outras novas propriedades.

Vamos apurar a classe do objeto-elemento gráfico no arquivo \MQL5\Include\DoEasy\Objects\Graph\GCnvElement.mqh.

Como podemos ter uma cor de fundo gradiente, usamos uma matriz de cores para armazenar todas as cores do gradiente. Precisamos adicionar mais duas dessas matrizes, uma para armazenar as cores do gradiente de fundo ao passar o mouse sobre o objeto e outra ao clicar com o cursor do mouse.
Na seção protected da classe, declaramos duas dessas matrizes:

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

//--- Create (1) the object structure and (2) the object from the structure
   virtual bool      ObjectToStruct(void);
   virtual void      StructToObject(void);
   
private:


Temos uma estrutura de objeto na qual todas as propriedades do elemento gráfico criado são escritas para, depois, salvar propriedades do objeto em um dispositivo externo e lê-las para restaurar o mesmo objeto quando o terminal e o programa forem reiniciados.

Vamos escrever todas as novas propriedades na estrutura:

private:
   int               m_shift_coord_x;                          // Offset of the X coordinate relative to the base object
   int               m_shift_coord_y;                          // Offset of the Y coordinate relative to the base object
   struct SData
     {
      //--- Object integer properties
      int            id;                                       // Element ID
      int            type;                                     // Graphical element type
      int            belong;                                   // Graphical element affiliation
      int            number;                                   // Element index in the list
      long           chart_id;                                 // Chart ID
      int            subwindow;                                // Chart subwindow index
      int            coord_x;                                  // Element X coordinate on the chart
      int            coord_y;                                  // Element Y coordinate on the chart
      int            width;                                    // Element width
      int            height;                                   // Element height
      int            edge_right;                               // Element right border
      int            edge_bottom;                              // Element bottom border
      int            act_shift_left;                           // Active area offset from the left edge of the element
      int            act_shift_top;                            // Active area offset from the top edge of the element
      int            act_shift_right;                          // Active area offset from the right edge of the element
      int            act_shift_bottom;                         // Active area offset from the bottom edge of the element
      bool           movable;                                  // Element moveability flag
      bool           active;                                   // Element activity flag
      bool           interaction;                              // Flag of interaction with the outside environment
      int            coord_act_x;                              // X coordinate of the element active area
      int            coord_act_y;                              // Y coordinate of the element active area
      int            coord_act_right;                          // Right border of the element active area
      int            coord_act_bottom;                         // Bottom border of the element active area
      long           zorder;                                   // Priority of a graphical object for receiving the event of clicking on a chart
      bool           enabled;                                  // Element availability flag
      color          fore_color;                               // Default text color for all control objects
      uchar          fore_color_opacity;                       // Default text color opacity for all control objects
      color          background_color;                         // Control background color
      uchar          background_color_opacity;                 // Opacity of control background color
      color          background_color_mouse_down;              // Control background color when clicking on the control
      color          background_color_mouse_over;              // Control background color when hovering the mouse over the control
      int            bold_type;                                // Font width type
      int            border_style;                             // Control frame style
      int            border_size_top;                          // Control frame top size
      int            border_size_bottom;                       // Control frame bottom size
      int            border_size_left;                         // Control frame left size
      int            border_size_right;                        // Control frame right size
      color          border_color;                             // Control frame color
      color          border_color_mouse_down;                  // Control frame color when clicking on the control
      color          border_color_mouse_over;                  // Control frame color when hovering the mouse over the control
      bool           autosize;                                 // Flag of the element auto resizing depending on the content
      int            autosize_mode;                            // Mode of the element auto resizing depending on the content
      bool           autoscroll;                               // Auto scrollbar flag
      int            autoscroll_margin_w;                      // Width of the field inside the element during auto scrolling
      int            autoscroll_margin_h;                      // Height of the field inside the element during auto scrolling
      int            dock_mode;                                // Mode of binding control borders to the container
      int            margin_top;                               // Top margin between the fields of this and another control
      int            margin_bottom;                            // Bottom margin between the fields of this and another control
      int            margin_left;                              // Left margin between the fields of this and another control
      int            margin_right;                             // Right margin between the fields of this and another control
      int            padding_top;                              // Top margin inside the control
      int            padding_bottom;                           // Bottom margin inside the control
      int            padding_left;                             // Left margin inside the control
      int            padding_right;                            // Right margin inside the control
      int            text_align;                               // Text position within text label boundaries
      int            check_align;                              // Position of the checkbox within control borders
      bool           checked;                                  // Control checkbox status
      int            check_state;                              // Status of a control having a checkbox
      bool           autocheck;                                // Auto change flag status when it is selected
      color          check_background_color;                   // Color of control checkbox background
      color          check_background_color_opacity;           // Opacity of the control checkbox background color
      color          check_background_color_mouse_down;        // Color of control checkbox background when clicking on the control
      color          check_background_color_mouse_over;        // Color of control checkbox background when hovering the mouse over the control
      color          check_fore_color;                         // Color of control checkbox frame
      color          check_fore_color_opacity;                 // Opacity of the control checkbox frame color
      color          check_fore_color_mouse_down;              // Color of control checkbox background when clicking on the control
      color          check_fore_color_mouse_over;              // Color of control checkbox background when hovering the mouse over the control
      color          check_flag_color;                         // Color of control checkbox
      color          check_flag_color_opacity;                 // Opacity of the control checkbox color
      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
      
      //--- 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

Agora temos todos os campos de propriedade necessários na estrutura para salvar e restaurar corretamente um objeto a partir da mesma. Vamos estar trabalhando no salvamento de propriedades de objetos em arquivos muito mais tarde.

Da seção privada da classe, removeremos duas variáveis para armazenar a cor de fundo e sua opacidade, pois agora os valores dessas variáveis são armazenados diretamente nas propriedades do objeto:

   ENUM_FRAME_ANCHOR m_text_anchor;                            // Current text alignment
   int               m_text_x;                                 // Text last X coordinate
   int               m_text_y;                                 // Text last Y coordinate
   color             m_color_bg;                               // Element background color
   uchar             m_opacity;                                // Element opacity
   
//--- Return the index of the array the order's (1) double and (2) string properties are located at


Anteriormente, tínhamos um método SaveColorsBG para salvar uma cor em uma matriz de cores de fundo. Agora teremos vários gradientes, cujas cores precisarão ser armazenadas em matrizes. Por isso, declararemos um método que copia a matriz de cores para a matriz de cores de fundo especificada e adicionaremos mais dois métodos para salvar as cores de fundo do gradiente:

//--- Return the index of the array the order's (1) double and (2) string properties are located at
   int               IndexProp(ENUM_CANV_ELEMENT_PROP_DOUBLE property)  const { return(int)property-CANV_ELEMENT_PROP_INTEGER_TOTAL;                                 }
   int               IndexProp(ENUM_CANV_ELEMENT_PROP_STRING property)  const { return(int)property-CANV_ELEMENT_PROP_INTEGER_TOTAL-CANV_ELEMENT_PROP_DOUBLE_TOTAL;  }

//--- Copy the color array to the specified background color array
   void              CopyArraysColors(color &array_dst[],const color &array_src[],const string source);
//--- 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);  }
   
public:

O primeiro método copia as cores - a partir da matriz passada para o método - para a matriz especificada nos parâmetros do método, enquanto os outros dois copiam a matriz de cores passada para os métodos para as matrizes de cores gradientes correspondentes que declaramos acima.

Na seção pública da classe, do método que cria o elemento gráfico, removemos duas variáveis de seus parâmetros de entrada, pois não são utilizadas em nenhum lugar do método:

   bool              Create(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 redraw=false);


Vamos adicionar dois métodos que retornam sinalizadores de que o objeto é main e (ou) base:

//--- (1) Set and (2) return the pointer to the parent element within all groups of related objects
   void              SetMain(CGCnvElement *element)                                    { this.m_element_main=element;               }
   CGCnvElement     *GetMain(void)                                                     { return this.m_element_main;                }
//--- Return the flag indicating that the object is (1) main, (2) base
   bool              IsMain(void)                                                      { return this.GetMain()==NULL;               }
   bool              IsBase(void)                                                      { return this.GetBase()==NULL;               }
//--- Return the pointer to a canvas object
   CCanvas          *GetCanvasObj(void)                                                { return &this.m_canvas;                     }

Os métodos simplesmente verificam se o ponteiro para os objetos principal e base é NULL.
Com este valor do ponteiro, o próprio objeto ou é um objeto main ou um objeto base, porque se o objeto está ligado a algum objeto, o ponteiro para os objetos main e base são escritos nas variáveis retornadas pelos métodos GetMain() e GetBase(). Portanto, se o ponteiro for NULL, o objeto não será vinculado a nenhum outro e poderá ser o objeto principal para outros objetos na hierarquia (o primeiro na hierarquia de objetos relacionados) ou o objeto base (outros objetos estão ligados a ele, mas não é o principal, porque por sua vez está ligado a outro objeto na cadeia comum de toda a hierarquia).

Vamos renomear o método que define a cor do plano de fundo adicionar métodos para definir a cor do plano de fundo ao clicar ou passar o mouse sobre o objeto:

//--- (5) all shifts of the active area edges relative to the element, (6) opacity
   void              SetActiveAreaLeftShift(const int value)   { this.SetProperty(CANV_ELEMENT_PROP_ACT_SHIFT_LEFT,fabs(value));       }
   void              SetActiveAreaRightShift(const int value)  { this.SetProperty(CANV_ELEMENT_PROP_ACT_SHIFT_RIGHT,fabs(value));      }
   void              SetActiveAreaTopShift(const int value)    { this.SetProperty(CANV_ELEMENT_PROP_ACT_SHIFT_TOP,fabs(value));        }
   void              SetActiveAreaBottomShift(const int value) { this.SetProperty(CANV_ELEMENT_PROP_ACT_SHIFT_BOTTOM,fabs(value));     }
   void              SetActiveAreaShift(const int left_shift,const int bottom_shift,const int right_shift,const int top_shift);
   void              SetOpacity(const uchar value,const bool redraw=false);
//--- Set the main background color
   void              SetBackgroundColor(const color colour)
                       {
                        this.SetProperty(CANV_ELEMENT_PROP_BACKGROUND_COLOR,colour);
                        color arr[1];
                        arr[0]=colour;
                        this.SaveColorsBG(arr);
                       }
   void              SetBackgroundColors(color &colors[])

                       {
                        this.SaveColorsBG(colors);
                        this.SetProperty(CANV_ELEMENT_PROP_BACKGROUND_COLOR,this.m_array_colors_bg[0]);
                       }
//--- Set the background color when clicking on the control
   void              SetBackgroundColorMouseDown(const color colour)
                       {
                        this.SetProperty(CANV_ELEMENT_PROP_BACKGROUND_COLOR_MOUSE_DOWN,colour);
                        color arr[1];
                        arr[0]=colour;
                        this.SaveColorsBGMouseDown(arr);
                       }
   void              SetBackgroundColorsMouseDown(color &colors[])
                       {
                        this.SaveColorsBGMouseDown(colors);
                        this.SetProperty(CANV_ELEMENT_PROP_BACKGROUND_COLOR_MOUSE_DOWN,this.m_array_colors_bg_dwn[0]);
                       }
//--- Set the background color when hovering the mouse over control
   void              SetBackgroundColorMouseOver(const color colour)
                       {
                        this.SetProperty(CANV_ELEMENT_PROP_BACKGROUND_COLOR_MOUSE_OVER,colour);
                        color arr[1];
                        arr[0]=colour;
                        this.SaveColorsBGMouseOver(arr);
                       }
   void              SetBackgroundColorsMouseOver(color &colors[])
                       {
                        this.SaveColorsBGMouseOver(colors);
                        this.SetProperty(CANV_ELEMENT_PROP_BACKGROUND_COLOR_MOUSE_OVER,this.m_array_colors_bg_ovr[0]);
                       }
//--- Set (1) object movability, (2) activity, (3) interaction,

Agora, ao invés de escrever valores para variáveis remotas, escrevemos valores para as propriedades do objeto. Consideramos esses métodos anteriormente em outros artigos e não é necessário tornar a descrevê-los.

Da mesma forma, renomearemos os métodos que retornam os valores das cores de fundo e escreveremos novos métodos para retornar os valores de cores de fundo adicionais:

//--- Return the number of colors set for the gradient filling of the (1) main background, when clicking (2), (3) when hovering the mouse over the control
   uint              BackgroundColorsTotal(void)         const { return this.m_array_colors_bg.Size();                                 }
   uint              BackgroundColorsMouseDownTotal(void)const { return this.m_array_colors_bg_dwn.Size();                             }
   uint              BackgroundColorsMouseOverTotal(void)const { return this.m_array_colors_bg_ovr.Size();                             }
//--- Return the main background color
   color             BackgroundColor(void)               const { return (color)this.GetProperty(CANV_ELEMENT_PROP_BACKGROUND_COLOR);   }
   color             BackgroundColor(const uint index)   const
                       {
                        uint total=this.m_array_colors_bg.Size();
                        if(total==0)
                           return this.BackgroundColor();
                        return(index>total-1 ? this.m_array_colors_bg[total-1] : this.m_array_colors_bg[index]);
                       }
                       
//--- Return the background color when clicking on the control
   color             BackgroundColorMouseDown(void)      const { return (color)this.GetProperty(CANV_ELEMENT_PROP_BACKGROUND_COLOR_MOUSE_DOWN); }
   color             BackgroundColorMouseDown(const uint index) const
                       {
                        uint total=this.m_array_colors_bg_dwn.Size();
                        if(total==0)
                           return this.BackgroundColorMouseDown();
                        return(index>total-1 ? this.m_array_colors_bg_dwn[total-1] : this.m_array_colors_bg_dwn[index]);
                       }
//--- 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 (1) the opacity, coordinate (2) of the right and (3) bottom element edge
   uchar             Opacity(void)                       const { return (uchar)this.GetProperty(CANV_ELEMENT_PROP_BACKGROUND_COLOR_OPACITY); }
   int               RightEdge(void)                     const { return this.CoordX()+this.m_canvas.Width();                           }
   int               BottomEdge(void)                    const { return this.CoordY()+this.m_canvas.Height();                          }

Aqui também não retornamos os valores das variáveis agora remotas, mas, sim, os valores registrados nas propriedades do objeto. Também consideramos esses métodos anteriormente e não repetiremos aqui.


Também renomearemos o método ChartColorBackground() (como todos os outros que retornam parâmetros de cores, e seus nomes não seguem o princípio geral de construção de nomes de métodos para trabalhar com cores):

//--- Return (1) the element ID, (2) element index in the list, (3) flag of the form shadow presence and (4) the chart background color
   int               ID(void)                            const { return (int)this.GetProperty(CANV_ELEMENT_PROP_ID);                   }
   int               Number(void)                        const { return (int)this.GetProperty(CANV_ELEMENT_PROP_NUM);                  }
   bool              IsShadow(void)                      const { return this.m_shadow;                                                 }
   color             ChartBackgroundColor(void)          const { return this.m_chart_color_bg;                                         }
//--- Set the object above all


No construtor paramétrico da classe, vamos substituir a entrada de valores em variáveis chamando métodos que fazem isso, vamos escrever a preservação da cor de fundo nas matrizes de cores de fundo adicionais (quando você clica e passa o mouse sobre o objeto) e inicializamos todas as novas propriedades do elemento gráfico com valores padrão:

//+------------------------------------------------------------------+
//| 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);
   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_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_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_BORDER_COLOR_MOUSE_DOWN,this.BackgroundColor()); // Control frame color when clicking on the control
      this.SetProperty(CANV_ELEMENT_PROP_BORDER_COLOR_MOUSE_OVER,this.BackgroundColor()); // Control frame color when hovering the mouse over the control
      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_FORE_COLOR);                  // Color of control checkbox frame
      this.SetProperty(CANV_ELEMENT_PROP_CHECK_FORE_COLOR_OPACITY,CLR_DEF_CHECK_FORE_OPACITY);        // Opacity of the control checkbox frame color
      this.SetProperty(CANV_ELEMENT_PROP_CHECK_FORE_COLOR_MOUSE_DOWN,CLR_DEF_CHECK_FORE_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_FORE_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
     }
   else
     {
      ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_FAILED_CREATE_ELM_OBJ),": ",this.m_name);
     }
  }
//+------------------------------------------------------------------+

No construtor protegido, escreveremos tudo da mesma maneira. As mudanças aqui são quase exatamente as mesmas do construtor acima, e não vamos discuti-las aqui, já que elas podem ser encontradas nos arquivos da biblioteca anexados ao artigo.


O método que cria a estrutura do objeto também foi apurado para escrever os valores das novas propriedades do objeto nos campos da estrutura (basta considerar o método completo):

//+------------------------------------------------------------------+
//| 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.belong=(int)this.GetProperty(CANV_ELEMENT_PROP_BELONG);                       // Graphical element affiliation
   this.m_struct_obj.number=(int)this.GetProperty(CANV_ELEMENT_PROP_NUM);                          // Element ID in the list
   this.m_struct_obj.chart_id=this.GetProperty(CANV_ELEMENT_PROP_CHART_ID);                        // Chart ID
   this.m_struct_obj.subwindow=(int)this.GetProperty(CANV_ELEMENT_PROP_WND_NUM);                   // Chart subwindow index
   this.m_struct_obj.coord_x=(int)this.GetProperty(CANV_ELEMENT_PROP_COORD_X);                     // Form's X coordinate on the chart
   this.m_struct_obj.coord_y=(int)this.GetProperty(CANV_ELEMENT_PROP_COORD_Y);                     // Form's Y coordinate on the chart
   this.m_struct_obj.width=(int)this.GetProperty(CANV_ELEMENT_PROP_WIDTH);                         // Element width
   this.m_struct_obj.height=(int)this.GetProperty(CANV_ELEMENT_PROP_HEIGHT);                       // Element height
   this.m_struct_obj.edge_right=(int)this.GetProperty(CANV_ELEMENT_PROP_RIGHT);                    // Element right edge
   this.m_struct_obj.edge_bottom=(int)this.GetProperty(CANV_ELEMENT_PROP_BOTTOM);                  // Element bottom edge
   this.m_struct_obj.act_shift_left=(int)this.GetProperty(CANV_ELEMENT_PROP_ACT_SHIFT_LEFT);       // Active area offset from the left edge of the element
   this.m_struct_obj.act_shift_top=(int)this.GetProperty(CANV_ELEMENT_PROP_ACT_SHIFT_TOP);         // Active area offset from the top edge of the element
   this.m_struct_obj.act_shift_right=(int)this.GetProperty(CANV_ELEMENT_PROP_ACT_SHIFT_RIGHT);     // Active area offset from the right edge of the element
   this.m_struct_obj.act_shift_bottom=(int)this.GetProperty(CANV_ELEMENT_PROP_ACT_SHIFT_BOTTOM);   // Active area offset from the bottom edge of the element
   this.m_struct_obj.movable=(bool)this.GetProperty(CANV_ELEMENT_PROP_MOVABLE);                    // Element moveability flag
   this.m_struct_obj.active=(bool)this.GetProperty(CANV_ELEMENT_PROP_ACTIVE);                      // Element activity flag
   this.m_struct_obj.interaction=(bool)this.GetProperty(CANV_ELEMENT_PROP_INTERACTION);            // Flag of interaction with the outside environment
   this.m_struct_obj.coord_act_x=(int)this.GetProperty(CANV_ELEMENT_PROP_COORD_ACT_X);             // X coordinate of the element active area
   this.m_struct_obj.coord_act_y=(int)this.GetProperty(CANV_ELEMENT_PROP_COORD_ACT_Y);             // Y coordinate of the element active area
   this.m_struct_obj.coord_act_right=(int)this.GetProperty(CANV_ELEMENT_PROP_ACT_RIGHT);           // Right border of the element active area
   this.m_struct_obj.coord_act_bottom=(int)this.GetProperty(CANV_ELEMENT_PROP_ACT_BOTTOM);         // Bottom border of the element active area
   this.m_struct_obj.zorder=this.GetProperty(CANV_ELEMENT_PROP_ZORDER);                            // Priority of a graphical object for receiving the on-chart mouse click event
   this.m_struct_obj.enabled=(bool)this.GetProperty(CANV_ELEMENT_PROP_ENABLED);                    // Element availability flag
   this.m_struct_obj.fore_color=(color)this.GetProperty(CANV_ELEMENT_PROP_FORE_COLOR);             // Default text color for all control objects
   this.m_struct_obj.fore_color_opacity=(uchar)this.GetProperty(CANV_ELEMENT_PROP_FORE_COLOR_OPACITY); // Opacity of the default text color for all control objects
   this.m_struct_obj.background_color=(color)this.GetProperty(CANV_ELEMENT_PROP_BACKGROUND_COLOR); // Element background color
   this.m_struct_obj.background_color_opacity=(uchar)this.GetProperty(CANV_ELEMENT_PROP_BACKGROUND_COLOR_OPACITY);      // Element opacity
   this.m_struct_obj.background_color_mouse_down=(color)this.GetProperty(CANV_ELEMENT_PROP_BACKGROUND_COLOR_MOUSE_DOWN);// Control background color when clicking on the control
   this.m_struct_obj.background_color_mouse_over=(color)this.GetProperty(CANV_ELEMENT_PROP_BACKGROUND_COLOR_MOUSE_OVER);// Control background color when hovering the mouse over the control
   this.m_struct_obj.bold_type=(int)this.GetProperty(CANV_ELEMENT_PROP_BOLD_TYPE);                 // Font width type
   this.m_struct_obj.border_style=(int)this.GetProperty(CANV_ELEMENT_PROP_BORDER_STYLE);           // Control frame style
   this.m_struct_obj.border_size_top=(int)this.GetProperty(CANV_ELEMENT_PROP_BORDER_SIZE_TOP);     // Control frame top size
   this.m_struct_obj.border_size_bottom=(int)this.GetProperty(CANV_ELEMENT_PROP_BORDER_SIZE_BOTTOM);// Control frame bottom size
   this.m_struct_obj.border_size_left=(int)this.GetProperty(CANV_ELEMENT_PROP_BORDER_SIZE_LEFT);   // Control frame left size
   this.m_struct_obj.border_size_right=(int)this.GetProperty(CANV_ELEMENT_PROP_BORDER_SIZE_RIGHT); // Control frame right size
   this.m_struct_obj.border_color=(color)this.GetProperty(CANV_ELEMENT_PROP_BORDER_COLOR);         // Control frame color
   this.m_struct_obj.border_color_mouse_down=(color)this.GetProperty(CANV_ELEMENT_PROP_BORDER_COLOR_MOUSE_DOWN);// Control frame color when clicking on the control
   this.m_struct_obj.border_color_mouse_over=(color)this.GetProperty(CANV_ELEMENT_PROP_BORDER_COLOR_MOUSE_OVER);// Control frame color when hovering the mouse over the control
   this.m_struct_obj.autosize=this.GetProperty(CANV_ELEMENT_PROP_AUTOSIZE);                        // Flag of the element auto resizing depending on the content
   this.m_struct_obj.autosize_mode=(int)this.GetProperty(CANV_ELEMENT_PROP_AUTOSIZE_MODE);         // Mode of the control auto resizing depending on the content
   this.m_struct_obj.autoscroll=this.GetProperty(CANV_ELEMENT_PROP_AUTOSCROLL);        // Auto scrollbar flag
   this.m_struct_obj.autoscroll_margin_w=(int)this.GetProperty(CANV_ELEMENT_PROP_AUTOSCROLL_MARGIN_W);  // Width of the field inside the element during auto scrolling
   this.m_struct_obj.autoscroll_margin_h=(int)this.GetProperty(CANV_ELEMENT_PROP_AUTOSCROLL_MARGIN_H);  // Height of the field inside the element during auto scrolling
   this.m_struct_obj.dock_mode=(int)this.GetProperty(CANV_ELEMENT_PROP_DOCK_MODE);                 // Mode of binding control borders to the container
   this.m_struct_obj.margin_top=(int)this.GetProperty(CANV_ELEMENT_PROP_MARGIN_TOP);               // Top margin between the fields of this and another control
   this.m_struct_obj.margin_bottom=(int)this.GetProperty(CANV_ELEMENT_PROP_MARGIN_BOTTOM);         // Bottom margin between the fields of this and another control
   this.m_struct_obj.margin_left=(int)this.GetProperty(CANV_ELEMENT_PROP_MARGIN_LEFT);             // Left margin between the fields of this and another control
   this.m_struct_obj.margin_right=(int)this.GetProperty(CANV_ELEMENT_PROP_MARGIN_RIGHT);           // Right margin between the fields of this and another control
   this.m_struct_obj.padding_top=(int)this.GetProperty(CANV_ELEMENT_PROP_PADDING_TOP);             // Top margin inside the control
   this.m_struct_obj.padding_bottom=(int)this.GetProperty(CANV_ELEMENT_PROP_PADDING_BOTTOM);       // Bottom margin inside the control
   this.m_struct_obj.padding_left=(int)this.GetProperty(CANV_ELEMENT_PROP_PADDING_LEFT);           // Left margin inside the control
   this.m_struct_obj.padding_right=(int)this.GetProperty(CANV_ELEMENT_PROP_PADDING_RIGHT);         // Right margin inside the control
   this.m_struct_obj.text_align=(int)this.GetProperty(CANV_ELEMENT_PROP_TEXT_ALIGN);               // Text position within text label boundaries
   this.m_struct_obj.check_align=(int)this.GetProperty(CANV_ELEMENT_PROP_CHECK_ALIGN);             // Position of the checkbox within control borders
   this.m_struct_obj.checked=(int)this.GetProperty(CANV_ELEMENT_PROP_CHECKED);                     // Control checkbox status
   this.m_struct_obj.check_state=(int)this.GetProperty(CANV_ELEMENT_PROP_CHECK_STATE);             // Status of a control having a checkbox
   this.m_struct_obj.autocheck=(int)this.GetProperty(CANV_ELEMENT_PROP_AUTOCHECK);                 // Auto change flag status when it is selected
   
   this.m_struct_obj.check_background_color=(color)this.GetProperty(CANV_ELEMENT_PROP_CHECK_BACKGROUND_COLOR);           // Control checkbox background color
   this.m_struct_obj.check_background_color_opacity=(color)this.GetProperty(CANV_ELEMENT_PROP_CHECK_BACKGROUND_COLOR_OPACITY); // Opacity of the control checkbox background color
   this.m_struct_obj.check_background_color_mouse_down=(color)this.GetProperty(CANV_ELEMENT_PROP_CHECK_BACKGROUND_COLOR_MOUSE_DOWN);// Control checkbox background color when clicking on the control
   this.m_struct_obj.check_background_color_mouse_over=(color)this.GetProperty(CANV_ELEMENT_PROP_CHECK_BACKGROUND_COLOR_MOUSE_OVER);// Control checkbox background color when hovering the mouse over the control
   this.m_struct_obj.check_fore_color=(color)this.GetProperty(CANV_ELEMENT_PROP_CHECK_FORE_COLOR);                       // Control checkbox frame color
   this.m_struct_obj.check_fore_color_opacity=(color)this.GetProperty(CANV_ELEMENT_PROP_CHECK_FORE_COLOR_OPACITY);       // Opacity of the control checkbox frame color
   this.m_struct_obj.check_fore_color_mouse_down=(color)this.GetProperty(CANV_ELEMENT_PROP_CHECK_FORE_COLOR_MOUSE_DOWN); // Control checkbox frame color when clicking on the control
   this.m_struct_obj.check_fore_color_mouse_over=(color)this.GetProperty(CANV_ELEMENT_PROP_CHECK_FORE_COLOR_MOUSE_OVER); // Control checkbox frame color when hovering the mouse over the control
   this.m_struct_obj.check_flag_color=(color)this.GetProperty(CANV_ELEMENT_PROP_CHECK_FLAG_COLOR);                       // Control checkbox color
   this.m_struct_obj.check_flag_color_opacity=(color)this.GetProperty(CANV_ELEMENT_PROP_CHECK_FLAG_COLOR_OPACITY);       // Opacity of the control checkbox color
   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
//--- Save real properties

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


e da mesma forma, foi ajustado o método que cria um objeto a partir de uma estrutura:

//+------------------------------------------------------------------+
//| Create the object from the structure                             |
//+------------------------------------------------------------------+
void CGCnvElement::StructToObject(void)
  {
//--- Save integer properties
   this.SetProperty(CANV_ELEMENT_PROP_ID,this.m_struct_obj.id);                                    // Element ID
   this.SetProperty(CANV_ELEMENT_PROP_TYPE,this.m_struct_obj.type);                                // Graphical element type
   this.SetProperty(CANV_ELEMENT_PROP_BELONG,this.m_struct_obj.belong);                            // Graphical element affiliation
   this.SetProperty(CANV_ELEMENT_PROP_NUM,this.m_struct_obj.number);                               // Element index in the list
   this.SetProperty(CANV_ELEMENT_PROP_CHART_ID,this.m_struct_obj.chart_id);                        // Chart ID
   this.SetProperty(CANV_ELEMENT_PROP_WND_NUM,this.m_struct_obj.subwindow);                        // Chart subwindow index
   this.SetProperty(CANV_ELEMENT_PROP_COORD_X,this.m_struct_obj.coord_x);                          // Form's X coordinate on the chart
   this.SetProperty(CANV_ELEMENT_PROP_COORD_Y,this.m_struct_obj.coord_y);                          // Form's Y coordinate on the chart
   this.SetProperty(CANV_ELEMENT_PROP_WIDTH,this.m_struct_obj.width);                              // Element width
   this.SetProperty(CANV_ELEMENT_PROP_HEIGHT,this.m_struct_obj.height);                            // Element height
   this.SetProperty(CANV_ELEMENT_PROP_RIGHT,this.m_struct_obj.edge_right);                         // Element right edge
   this.SetProperty(CANV_ELEMENT_PROP_BOTTOM,this.m_struct_obj.edge_bottom);                       // Element bottom edge
   this.SetProperty(CANV_ELEMENT_PROP_ACT_SHIFT_LEFT,this.m_struct_obj.act_shift_left);            // Active area offset from the left edge of the element
   this.SetProperty(CANV_ELEMENT_PROP_ACT_SHIFT_TOP,this.m_struct_obj.act_shift_top);              // Active area offset from the upper edge of the element
   this.SetProperty(CANV_ELEMENT_PROP_ACT_SHIFT_RIGHT,this.m_struct_obj.act_shift_right);          // Active area offset from the right edge of the element
   this.SetProperty(CANV_ELEMENT_PROP_ACT_SHIFT_BOTTOM,this.m_struct_obj.act_shift_bottom);        // Active area offset from the bottom edge of the element
   this.SetProperty(CANV_ELEMENT_PROP_MOVABLE,this.m_struct_obj.movable);                          // Element moveability flag
   this.SetProperty(CANV_ELEMENT_PROP_ACTIVE,this.m_struct_obj.active);                            // Element activity flag
   this.SetProperty(CANV_ELEMENT_PROP_INTERACTION,this.m_struct_obj.interaction);                  // Flag of interaction with the outside environment
   this.SetProperty(CANV_ELEMENT_PROP_COORD_ACT_X,this.m_struct_obj.coord_act_x);                  // X coordinate of the element active area
   this.SetProperty(CANV_ELEMENT_PROP_COORD_ACT_Y,this.m_struct_obj.coord_act_y);                  // Y coordinate of the element active area
   this.SetProperty(CANV_ELEMENT_PROP_ACT_RIGHT,this.m_struct_obj.coord_act_right);                // Right border of the element active area
   this.SetProperty(CANV_ELEMENT_PROP_ACT_BOTTOM,this.m_struct_obj.coord_act_bottom);              // Bottom border of the element active area
   this.SetProperty(CANV_ELEMENT_PROP_ZORDER,this.m_struct_obj.zorder);                            // Priority of a graphical object for receiving the event of clicking on a chart
   this.SetProperty(CANV_ELEMENT_PROP_ENABLED,this.m_struct_obj.enabled);                          // Element availability flag
   this.SetProperty(CANV_ELEMENT_PROP_FORE_COLOR,this.m_struct_obj.fore_color);                    // Default text color for all control objects
   this.SetProperty(CANV_ELEMENT_PROP_FORE_COLOR_OPACITY,this.m_struct_obj.fore_color_opacity);    // Opacity of the default text color for all control objects
   this.SetProperty(CANV_ELEMENT_PROP_BACKGROUND_COLOR,this.m_struct_obj.background_color);        // Element background color
   this.SetProperty(CANV_ELEMENT_PROP_BACKGROUND_COLOR_OPACITY,this.m_struct_obj.background_color_opacity);       // Element opacity
   this.SetProperty(CANV_ELEMENT_PROP_BACKGROUND_COLOR_MOUSE_DOWN,this.m_struct_obj.background_color_mouse_down); // Control background color when clicking on the control
   this.SetProperty(CANV_ELEMENT_PROP_BACKGROUND_COLOR_MOUSE_OVER,this.m_struct_obj.background_color_mouse_over); // Control background color when hovering the mouse over the control
   this.SetProperty(CANV_ELEMENT_PROP_BOLD_TYPE,this.m_struct_obj.bold_type);                      // Font width type
   this.SetProperty(CANV_ELEMENT_PROP_BORDER_STYLE,this.m_struct_obj.border_style);                // Control frame style
   this.SetProperty(CANV_ELEMENT_PROP_BORDER_SIZE_TOP,this.m_struct_obj.border_size_top);          // Control frame top size
   this.SetProperty(CANV_ELEMENT_PROP_BORDER_SIZE_BOTTOM,this.m_struct_obj.border_size_bottom);    // Control frame bottom size
   this.SetProperty(CANV_ELEMENT_PROP_BORDER_SIZE_LEFT,this.m_struct_obj.border_size_left);        // Control frame left size
   this.SetProperty(CANV_ELEMENT_PROP_BORDER_SIZE_RIGHT,this.m_struct_obj.border_size_right);      // Control frame right size
   this.SetProperty(CANV_ELEMENT_PROP_BORDER_COLOR,this.m_struct_obj.border_color);                // Control frame color
   this.SetProperty(CANV_ELEMENT_PROP_BORDER_COLOR_MOUSE_DOWN,this.m_struct_obj.border_color_mouse_down);// Control frame color when clicking on the control
   this.SetProperty(CANV_ELEMENT_PROP_BORDER_COLOR_MOUSE_OVER,this.m_struct_obj.border_color_mouse_over);// Control frame color when hovering the mouse over the control
   this.SetProperty(CANV_ELEMENT_PROP_AUTOSIZE,this.m_struct_obj.autosize);                        // Flag of the element auto resizing depending on the content
   this.SetProperty(CANV_ELEMENT_PROP_AUTOSIZE_MODE,this.m_struct_obj.autosize_mode);              // Mode of the element auto resizing depending on the content
   this.SetProperty(CANV_ELEMENT_PROP_AUTOSCROLL,this.m_struct_obj.autoscroll);                    // Auto scrollbar flag
   this.SetProperty(CANV_ELEMENT_PROP_AUTOSCROLL_MARGIN_W,this.m_struct_obj.autoscroll_margin_w);  // Width of the field inside the element during auto scrolling
   this.SetProperty(CANV_ELEMENT_PROP_AUTOSCROLL_MARGIN_H,this.m_struct_obj.autoscroll_margin_h);  // Height of the field inside the element during auto scrolling
   this.SetProperty(CANV_ELEMENT_PROP_DOCK_MODE,this.m_struct_obj.dock_mode);                      // Mode of binding control borders to the container
   this.SetProperty(CANV_ELEMENT_PROP_MARGIN_TOP,this.m_struct_obj.margin_top);                    // Top margin between the fields of this and another control
   this.SetProperty(CANV_ELEMENT_PROP_MARGIN_BOTTOM,this.m_struct_obj.margin_bottom);              // Bottom margin between the fields of this and another control
   this.SetProperty(CANV_ELEMENT_PROP_MARGIN_LEFT,this.m_struct_obj.margin_left);                  // Left margin between the fields of this and another control
   this.SetProperty(CANV_ELEMENT_PROP_MARGIN_RIGHT,this.m_struct_obj.margin_right);                // Right margin between the fields of this and another control
   this.SetProperty(CANV_ELEMENT_PROP_PADDING_TOP,this.m_struct_obj.padding_top);                  // Top margin inside the control
   this.SetProperty(CANV_ELEMENT_PROP_PADDING_BOTTOM,this.m_struct_obj.padding_bottom);            // Bottom margin inside the control
   this.SetProperty(CANV_ELEMENT_PROP_PADDING_LEFT,this.m_struct_obj.padding_left);                // Left margin inside the control
   this.SetProperty(CANV_ELEMENT_PROP_PADDING_RIGHT,this.m_struct_obj.padding_right);              // Right margin inside the control
   this.SetProperty(CANV_ELEMENT_PROP_TEXT_ALIGN,this.m_struct_obj.text_align);                    // Text position within text label boundaries
   this.SetProperty(CANV_ELEMENT_PROP_CHECK_ALIGN,this.m_struct_obj.check_align);                  // Position of the checkbox within control borders
   this.SetProperty(CANV_ELEMENT_PROP_CHECKED,this.m_struct_obj.checked);                          // Control checkbox status
   this.SetProperty(CANV_ELEMENT_PROP_CHECK_STATE,this.m_struct_obj.check_state);                  // Status of a control having a checkbox
   this.SetProperty(CANV_ELEMENT_PROP_AUTOCHECK,this.m_struct_obj.autocheck);                      // Auto change flag status when it is selected
   
   this.SetProperty(CANV_ELEMENT_PROP_CHECK_BACKGROUND_COLOR,this.m_struct_obj.check_background_color);           // Color of control checkbox background
   this.SetProperty(CANV_ELEMENT_PROP_CHECK_BACKGROUND_COLOR_OPACITY,this.m_struct_obj.check_background_color_opacity); // Opacity of the control checkbox background color
   this.SetProperty(CANV_ELEMENT_PROP_CHECK_BACKGROUND_COLOR_MOUSE_DOWN,this.m_struct_obj.check_background_color_mouse_down);// Color of control checkbox background when clicking on the control
   this.SetProperty(CANV_ELEMENT_PROP_CHECK_BACKGROUND_COLOR_MOUSE_OVER,this.m_struct_obj.check_background_color_mouse_over);// Color of control checkbox background when hovering the mouse over the control
   this.SetProperty(CANV_ELEMENT_PROP_CHECK_FORE_COLOR,this.m_struct_obj.check_fore_color);                       // Color of control checkbox frame
   this.SetProperty(CANV_ELEMENT_PROP_CHECK_FORE_COLOR_OPACITY,this.m_struct_obj.check_fore_color_opacity);       // Opacity of the control checkbox frame color
   this.SetProperty(CANV_ELEMENT_PROP_CHECK_FORE_COLOR_MOUSE_DOWN,this.m_struct_obj.check_fore_color_mouse_down); // Color of control checkbox frame when clicking on the control
   this.SetProperty(CANV_ELEMENT_PROP_CHECK_FORE_COLOR_MOUSE_OVER,this.m_struct_obj.check_fore_color_mouse_over); // Color of control checkbox frame when hovering the mouse over the control
   this.SetProperty(CANV_ELEMENT_PROP_CHECK_FLAG_COLOR,this.m_struct_obj.check_flag_color);                       // Control checkbox color
   this.SetProperty(CANV_ELEMENT_PROP_CHECK_FLAG_COLOR_OPACITY,this.m_struct_obj.check_flag_color_opacity);       // Control checkbox color opacity
   this.SetProperty(CANV_ELEMENT_PROP_CHECK_FLAG_COLOR_MOUSE_DOWN,this.m_struct_obj.check_flag_color_mouse_down); // Control checkbox color when clicking on the control
   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
//--- 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
  }
//+------------------------------------------------------------------+

Ambos os métodos são idênticos, mas opostos entre si. Se no primeiro método as propriedades correspondentes do objeto são gravadas nos campos da estrutura, no segundo método, os valores dos campos correspondentes da estrutura são gravados nas propriedades do objeto.

Na implementação do método que cria o elemento-objeto gráfico, também removeremos variáveis não utilizadas:

//+------------------------------------------------------------------+
//| Create the graphical element object                              |
//+------------------------------------------------------------------+
bool CGCnvElement::Create(const long chart_id,     // Chart ID
                          const int wnd_num,       // Chart subwindow
                          const string name,       // Element name
                          const int x,             // X coordinate
                          const int y,             // Y coordinate
                          const int w,             // Width
                          const int h,             // Height
                          const color colour,      // Background color
                          const uchar opacity,     // Opacity
                          const bool redraw=false) // Flag indicating the need to redraw
                         
  {
   ::ResetLastError();
   if(this.m_canvas.CreateBitmapLabel((chart_id==NULL ? ::ChartID() : chart_id),wnd_num,name,x,y,w,h,COLOR_FORMAT_ARGB_NORMALIZE))
     {
      this.Erase(CLR_CANV_NULL);
      this.m_canvas.Update(redraw);
      this.m_shift_y=(int)::ChartGetInteger((chart_id==NULL ? ::ChartID() : chart_id),CHART_WINDOW_YDISTANCE,wnd_num);
      return true;
     }
   CMessage::ToLog(DFUN,::GetLastError(),true);
   return false;
  }
//+------------------------------------------------------------------+


No método que define a opacidade do elemento, em vez da string que armazena o valor da opacidade em uma variável,

this.m_opacity=value;

escrevemos seu salvamento na propriedade do objeto:

//+------------------------------------------------------------------+
//| Set the element opacity                                          |
//+------------------------------------------------------------------+
void CGCnvElement::SetOpacity(const uchar value,const bool redraw=false)
  {
   this.SetProperty(CANV_ELEMENT_PROP_BACKGROUND_COLOR_OPACITY,value);
   this.m_canvas.TransparentLevelSet(value);
   this.m_canvas.Update(redraw);
  }
//+------------------------------------------------------------------+


Implementação de um método que copia uma matriz de cores para uma matriz de cores de fundo específica:

//+------------------------------------------------------------------+
//| Copy the color array to the specified background color array     |
//+------------------------------------------------------------------+
void CGCnvElement::CopyArraysColors(color &array_dst[],const color &array_src[],const string source)
  {
   if(array_dst.Size()!=array_src.Size())
     {
      ::ResetLastError();
      if(::ArrayResize(array_dst,array_src.Size())!=array_src.Size())
        {
         CMessage::ToLog(source,MSG_LIB_SYS_FAILED_COLORS_ARRAY_RESIZE);
         CMessage::ToLog(::GetLastError(),true);
         return;
        }
     }
   ::ArrayCopy(array_dst,array_src);
  }
//+------------------------------------------------------------------+

Ao método é passada uma matriz, na qual todos os dados da matriz de origem devem ser escritos. Se os tamanhos das matrizes não corresponderem, a matriz de destino altera seu tamanho para se ajustar ao tamanho da matriz de origem e, em seguida, todo o conteúdo da matriz de origem é copiado para a matriz de destino.


No construtor da classe objeto-sombra no arquivo \MQL5\Include\DoEasy\Objects\Graph\ShadowObj.mqh, renomeamos os nomes dos métodos chamados para novos:

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


Nosso objeto-forma é um objeto que implementa a funcionalidade de interação com o mouse. O objeto base dos objetos da biblioteca WinForms é seu sucessor. No arquivo da classe do objeto-forma \MQL5\Include\DoEasy\Objects\Graph\Form.mqh, também precisamos ajustar muitos métodos.

Os métodos para trabalhar com cores serão renomeados e novos métodos para trabalhar com a "moldura" do objeto também serão adicionados. Agora será "Border" em vez de "Frame". Além disso, outros objetos podem ser anexados ao objeto, por exemplo, botões ou textos são criados no painel...
Ao passar o mouse sobre esse painel, a biblioteca atualmente determina se o cursor está pairando sobre o painel. Mas não "vê" que o cursor está sobre um objeto anexado a este painel. Assim, não há como interagir com o objeto encaixado no painel.

Para resolver esse problema, precisamos percorrer a lista de todos os objetos anexados ao painel e determinar o mais recente na hierarquia, sobre o qual o cursor está localizado. Para fazer isso, vamos criar uma lista de todos os objetos anexados ao painel. O objeto anexado pode, por sua vez, ter outros anexados a ele. Ou seja, precisamos percorrer toda a hierarquia de tais objetos.

Para fazer isso, criaremos uma lista na qual escreveremos ponteiros para objetos anexados - vamos chamá-los de objetos de interação, já que estamos procurando o objeto com o qual precisamos da interação do mouse e um método que grava todos os outros controles anexados ao objeto à lista. Se o elemento anexado também tiver seus próprios objetos anexados, o mesmo método será chamado para ele e um ponteiro para a lista na qual os ponteiros são gravados será passado para o método.

Assim, podemos percorrer em um loop toda a hierarquia de objetos anexados e obter uma lista de ponteiros para eles. E depois é mais simples, uma vez determinado que um painel está pairando, chamamos o método desse painel, que cria uma lista de todos os objetos anexados. E então nos resta encontrar o primeiro objeto do final da lista, que possui um sinalizador para localizar o cursor sobre ele. E processaremos este ponteiro para este objeto encontrado posteriormente, nós escrevemos um rascunho de seu manipulador há muito tempo.

Agora vamos remover variáveis desnecessárias da seção protegida da classe:

protected:
   CArrayObj         m_list_elements;                          // List of attached 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
   ushort            m_mouse_state_flags;                      // Mouse status flags
   color             m_color_frame;                            // Form frame color
   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_frame_width_left;                       // Form frame width to the left
   int               m_frame_width_right;                      // Form frame width to the right
   int               m_frame_width_top;                        // Form frame width at the top
   int               m_frame_width_bottom;                     // Form frame width at the bottom
   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
//--- Initialize the variables

Agora, em vez dessas variáveis, todos os valores são armazenados nas propriedades do objeto.


Do método CreateAndAddNewElement() removemos o ponteiro para o objeto principal na hierarquia de todos os objetos anexados uns aos outros:

//--- Create a new bound element and add it to the list of bound objects
   virtual CGCnvElement *CreateAndAddNewElement(const ENUM_GRAPH_ELEMENT_TYPE element_type,
                                                CGCnvElement *main,
                                                const int x,
                                                const int y,
                                                const int w,
                                                const int h,
                                                const color colour,
                                                const uchar opacity,
                                                const bool activity);

public:

Anteriormente, ao criar objetos anexados, tínhamos que especificar explicitamente o objeto principal para o objeto que está sendo criado, passando um ponteiro para ele nos parâmetros desse método. Agora vamos garantir que o objeto principal ou base na hierarquia seja determinado automaticamente, o que evitará que o usuário final especifique desnecessariamente tal objeto ao criar um novo controle anexado.

No mesmo lugar, na seção protected, vamos declarar um método para criar uma lista de todos os objetos de interação e um método que retorna um sinalizador que indica a presença de um objeto nesta lista pelo seu nome:

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


Na seção public, vamos declarar um método que cria uma lista de todos os objetos de interação (mas sem passar um ponteiro para a lista nos parâmetros formais do método) e um método que retorna um ponteiro para o objeto-forma na lista de objetos de interação por seu índice:

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

O primeiro método será chamado a partir do objeto sobre o qual o cursor do mouse está e, dentro dele, um método protegido será chamado para buscar outros objetos de interação em toda a hierarquia do objeto principal. Um ponteiro para a lista será passado para cada objeto sucessivo da hierarquia e, portanto, em uma lista do primeiro objeto em que o cursor foi colocado, haverá uma lista de ponteiros para todos os objetos anexados a ele.

Vamos escrever métodos que retornem e definam todos os tamanhos da moldura do objeto a partir de suas propriedades:

//--- (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
   void              SetBorderSizeLeft(const uint value)       { this.SetProperty(CANV_ELEMENT_PROP_BORDER_SIZE_LEFT,value);        }
   void              SetBorderSizeTop(const uint value)        { this.SetProperty(CANV_ELEMENT_PROP_BORDER_SIZE_TOP,value);         }
   void              SetBorderSizeRight(const uint value)      { this.SetProperty(CANV_ELEMENT_PROP_BORDER_SIZE_RIGHT,value);       }
   void              SetBorderSizeBottom(const uint value)     { this.SetProperty(CANV_ELEMENT_PROP_BORDER_SIZE_BOTTOM,value);      }

//--- Update the coordinates (shift the canvas)


Vamos adicionar um método que retorna uma lista de objetos de interação e um método que retorna o número de objetos de interação na lista criada:

//--- Return (1) itself, the list of (2) attached objects, (3) the list of interaction objects and (4) shadow object
   CForm            *GetObject(void)                                          { return &this;                  }
   CArrayObj        *GetListElements(void)                                    { return &this.m_list_elements;  }
   CArrayObj        *GetListInteractObj(void)                                 { return &this.m_list_interact;  }
   CShadowObj       *GetShadowObj(void)                                       { return this.m_shadow_obj;      }
//--- Return the pointer to (1) the animation object, the list of (2) text and (3) rectangular animation frames
   CAnimations      *GetAnimationsObj(void)                                   { return this.m_animations;      }
   CArrayObj        *GetListFramesText(void)
                       { return(this.m_animations!=NULL ? this.m_animations.GetListFramesText() : NULL);       }
   CArrayObj        *GetListFramesQuad(void)
                       { return(this.m_animations!=NULL ? this.m_animations.GetListFramesQuad() : NULL);       }
   
//--- Return the number of (1) bound elements, (2) interaction objects and (3) the bound element by the index in the list
   int               ElementsTotal(void)                       const { return this.m_list_elements.Total();    }
   int               InteractTotal(void)                       const { return this.m_list_interact.Total();    }
   CGCnvElement     *GetElement(const int index)                     { return this.m_list_elements.At(index);  }


Do método que cria um novo elemento anexado, removemos a transferência de um ponteiro para o objeto principal:

//--- Create a new attached element
   virtual bool      CreateNewElement(const ENUM_GRAPH_ELEMENT_TYPE element_type,
                                      CGCnvElement *main,
                                      const int x,
                                      const int y,
                                      const int w,
                                      const int h,
                                      const color colour,
                                      const uchar opacity,
                                      const bool activity,
                                      const bool redraw);
//--- Add a new attached element

Agora não precisamos desse ponteiro - a biblioteca determinará por si mesma qual objeto é o principal e qual é o base.


Vamos renomear os métodos para definir e retornar a cor da borda da forma e adicionar novos métodos para trabalhar com cores adicionais do plano de fundo da forma:

//+------------------------------------------------------------------+
//| Methods of simplified access to object properties                |
//+------------------------------------------------------------------+
//--- (1) Set and (2) return the control frame color
   void              SetBorderColor(const color colour)                 { this.SetProperty(CANV_ELEMENT_PROP_BORDER_COLOR,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);  }

Agora esses métodos não funcionam com variáveis, mas sim com valores que estão nas propriedades do objeto.


No método de inicialização de variável, limpamos a lista de objetos de interação e definimos o sinalizador de lista classificada para ele e, em vez de gravar o tamanho da moldura da forma em variáveis, definimos usando os novos métodos escritos acima:

//+------------------------------------------------------------------+
//| 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.m_gradient_v=true;
   this.m_gradient_c=false;
   this.m_mouse_state_flags=0;
   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);
  }
//+------------------------------------------------------------------+


Anteriormente, para cada objeto criado, especificamos não apenas suas coordenadas reais (no sistema de coordenadas do gráfico), mas também as relativas - a distância em pixels em relação à origem das coordenadas do objeto ao qual o objeto criado está anexado. Simplesmente indicamos o valor do recuo em relação à borda do objeto, valor esse definido durante a criação.

Mas isso não é totalmente eficiente e requer monitoramento constante dessas coordenadas caso elas mudem. Agora vamos calculá-las a partir das coordenadas da tela, para tal, a coordenada da tela do objeto ancorado será subtraída da coordenada da tela do objeto base. Assim, com qualquer alteração nas coordenadas do objeto ancorado, sempre saberemos exatamente suas coordenadas relativas.

No método que cria um novo objeto gráfico, calculamos e anotamos as coordenadas relativas do elemento:

//+------------------------------------------------------------------+
//| Create a new graphical object                                    |
//+------------------------------------------------------------------+
CGCnvElement *CForm::CreateNewGObject(const ENUM_GRAPH_ELEMENT_TYPE type,
                                      const int obj_num,
                                      const string obj_name,
                                      const int x,
                                      const int y,
                                      const int w,
                                      const int h,
                                      const color colour,
                                      const uchar opacity,
                                      const bool movable,
                                      const bool activity)
  {
   string name=this.CreateNameDependentObject(obj_name);
   CGCnvElement *element=NULL;
   //--- Depending on the created object type,
   switch(type)
     {
      //--- create a graphical element object
      case GRAPH_ELEMENT_TYPE_ELEMENT :
         element=new CGCnvElement(type,this.ID(),obj_num,this.ChartID(),this.SubWindow(),name,x,y,w,h,colour,opacity,movable,activity);
        break;
      //--- create a form object
      case GRAPH_ELEMENT_TYPE_FORM :
         element=new CForm(this.ChartID(),this.SubWindow(),name,x,y,w,h);
        break;
      default:
        break;
     }
   if(element==NULL)
      ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_FAILED_CREATE_ELM_OBJ),": ",name);
   element.SetMovable(movable);
   element.SetCoordXRelative(element.CoordX()-this.CoordX());
   element.SetCoordYRelative(element.CoordY()-this.CoordY());
   return element;
  }
//+------------------------------------------------------------------+


No método que cria um novo elemento anexado e o adiciona à lista de objetos anexados, renomearemos o método para definir o plano de fundo do objeto, definiremos e escreveremos um ponteiro para o objeto principal da hierarquia de objetos anexados e calcularemos e definiremos as coordenadas relativas do controle anexado criado:

//+------------------------------------------------------------------+
//| Create a new attached element                                    |
//| and add it to the list of bound objects                          |
//+------------------------------------------------------------------+
CGCnvElement *CForm::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)
  {
//--- If the type of a created graphical element is less than the "element", inform of that and return 'false'
   if(element_type<GRAPH_ELEMENT_TYPE_ELEMENT)
     {
      ::Print(DFUN,CMessage::Text(MSG_FORM_OBJECT_ERR_NOT_INTENDED),::StringSubstr(::EnumToString(element_type),19));
      return NULL;
     }
//--- Specify the element index in the list
   int num=this.m_list_elements.Total();
//--- Create a graphical element name
   string ns=(::StringLen((string)num)<2 ? ::IntegerToString(num,2,'0') : (string)num);
   string name="Elm"+ns;
//--- Get the screen coordinates of the object relative to the coordinate system of the base object
   int elm_x=x;
   int elm_y=y;
   this.GetCoords(elm_x,elm_y);
//--- Create a new graphical element
   CGCnvElement *obj=this.CreateNewGObject(element_type,num,name,elm_x,elm_y,w,h,colour,opacity,false,activity);
   if(obj==NULL)
      return NULL;
//--- and add it to the list of bound graphical elements
   if(!this.AddNewElement(obj,elm_x,elm_y))
     {
      delete obj;
      return NULL;
     }
//--- Set the minimum properties for a bound graphical element
   obj.SetBackgroundColor(colour);
   obj.SetOpacity(opacity);
   obj.SetActive(activity);
   obj.SetMain(this.GetMain()==NULL ? this.GetObject() : this.GetMain());
   obj.SetBase(this.GetObject());
   obj.SetID(this.ID());
   obj.SetNumber(num);
   obj.SetCoordXRelative(obj.CoordX()-this.CoordX());
   obj.SetCoordYRelative(obj.CoordY()-this.CoordY());
   obj.SetZorder(this.Zorder(),false);
   obj.SetCoordXRelativeInit(obj.CoordXRelative());
   obj.SetCoordYRelativeInit(obj.CoordYRelative());
   return obj;
  }
//+------------------------------------------------------------------+

Como definimos o objeto principal da hierarquia? É simples: se esse objeto não estiver anexado a nenhum outro, inicialmente ele terá um ponteiro para o objeto principal igual a NULL. Se outro controle anexado a ele for criado a partir de tal objeto, verificar-se-á se o valor do ponteiro para o objeto principal corresponde ao objeto principal. Se o ponteiro for NULL, então este será o objeto principal. Caso contrário, um ponteiro para o objeto principal de toda a hierarquia já foi gravado nesse objeto - nós o gravamos no controle recém-criado. Assim, em toda a hierarquia, sempre haverá um objeto principal - o primeiro, a partir do qual começou a criação da hierarquia de objetos anexados.


Do método que cria um novo elemento anexado, removemos a transferência do ponteiro para o objeto principal e, consequentemente, removemos o valor extra passado para o método CreateAndAddNewElement():

//+------------------------------------------------------------------+
//| Create a new attached element                                    |
//+------------------------------------------------------------------+
bool CForm::CreateNewElement(const ENUM_GRAPH_ELEMENT_TYPE element_type,
                             CGCnvElement *main,
                             const int x,
                             const int y,
                             const int w,
                             const int h,
                             const color colour,
                             const uchar opacity,
                             const bool activity,
                             const bool redraw)
  {
//--- Create a new graphical element
   CGCnvElement *obj=this.CreateAndAddNewElement(element_type,main,x,y,w,h,colour,opacity,activity);
//--- If the object has been created, draw the added object and return 'true'
   if(obj==NULL)
      return false;
   obj.Erase(colour,opacity,redraw);
   return true;
  }
//+------------------------------------------------------------------+


Como agora temos métodos que retornam a largura da moldura do objeto, e esses valores são armazenados não em variáveis, mas, sim, nas propriedades do objeto, no método que retorna as coordenadas iniciais do objeto vinculado, substituiremos o acesso a variáveis com obtenção de valores de novos métodos:

//+------------------------------------------------------------------+
//| Return the initial coordinates of a bound object                 |
//+------------------------------------------------------------------+
void CForm::GetCoords(int &x,int &y)
  {
   x=this.CoordX()+this.BorderSizeLeft()+x;
   y=this.CoordY()+this.BorderSizeTop()+y;
  }
//+------------------------------------------------------------------+


renomearemos os métodos chamados, substituiremos o trabalho com variáveis por trabalho com propriedades do objeto,

//+------------------------------------------------------------------+
//| 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]);
   this.SetBorderColor(array_color_themes[theme][COLOR_THEME_COLOR_FORM_FRAME]);
  }
//+------------------------------------------------------------------+
//| Set the form style                                               |
//+------------------------------------------------------------------+
void CForm::SetFormStyle(const ENUM_FORM_STYLE style,
                         const ENUM_COLOR_THEMES theme,
                         const uchar opacity,
                         const bool shadow=false,
                         const bool use_bg_color=true,
                         const bool redraw=false)
  {
//--- Set opacity parameters and the size of the form frame side
   this.m_shadow=shadow;
   this.SetBorderSizeTop(array_form_style[style][FORM_STYLE_FRAME_WIDTH_TOP]);
   this.SetBorderSizeBottom(array_form_style[style][FORM_STYLE_FRAME_WIDTH_BOTTOM]);
   this.SetBorderSizeLeft(array_form_style[style][FORM_STYLE_FRAME_WIDTH_LEFT]);
   this.SetBorderSizeRight(array_form_style[style][FORM_STYLE_FRAME_WIDTH_RIGHT]);
   this.m_gradient_v=array_form_style[style][FORM_STYLE_GRADIENT_V];
   this.m_gradient_c=array_form_style[style][FORM_STYLE_GRADIENT_C];
//--- Create the shadow object
   this.CreateShadowObj(clrNONE,(uchar)array_form_style[style][FORM_STYLE_FRAME_SHADOW_OPACITY]);
   
//--- Set a color scheme
   this.SetColorTheme(theme,opacity);
//--- Calculate a shadow color with color darkening
   color clr=array_color_themes[theme][COLOR_THEME_COLOR_FORM_SHADOW];
   color gray=CGCnvElement::ChangeColorSaturation(this.ChartBackgroundColor(),-100);
   color color_shadow=CGCnvElement::ChangeColorLightness((use_bg_color ? gray : clr),-fabs(array_form_style[style][FORM_STYLE_DARKENING_COLOR_FOR_SHADOW]));
   this.SetColorShadow(color_shadow);
   
//--- Draw a rectangular shadow
   int shift_x=array_form_style[style][FORM_STYLE_FRAME_SHADOW_X_SHIFT];
   int shift_y=array_form_style[style][FORM_STYLE_FRAME_SHADOW_Y_SHIFT];
   this.DrawShadow(shift_x,shift_y,color_shadow,this.OpacityShadow(),(uchar)array_form_style[style][FORM_STYLE_FRAME_SHADOW_BLUR]);
   
//--- Fill in the form background with color and opacity
   this.Erase(this.m_array_colors_bg,this.Opacity(),this.m_gradient_v,this.m_gradient_c);
//--- Depending on the selected form style, draw the corresponding form frame and the outer bounding frame
   switch(style)
     {
      case FORM_STYLE_BEVEL   :
        this.DrawFormFrame(this.BorderSizeTop(),this.BorderSizeBottom(),this.BorderSizeLeft(),this.BorderSizeRight(),this.BorderColor(),this.Opacity(),FRAME_STYLE_BEVEL);
        break;
      //---FORM_STYLE_FLAT
      default:
        this.DrawFormFrame(this.BorderSizeTop(),this.BorderSizeBottom(),this.BorderSizeLeft(),this.BorderSizeRight(),this.BorderColor(),this.Opacity(),FRAME_STYLE_FLAT);
        break;
     }
   this.DrawRectangle(0,0,this.Width()-1,this.Height()-1,array_color_themes[theme][COLOR_THEME_COLOR_FORM_RECT_OUTER],this.Opacity());
  }

//+------------------------------------------------------------------+


Método que retorna o sinalizador que indica presença de um ponteiro para um objeto na lista de objetos de interação por nome:

//+-----------------------------------------------------------------------+
//| Return the flag indicating the presence of the pointer to an object   |
//| in the list of interaction objects by name                            |
//+-----------------------------------------------------------------------+
bool CForm::IsPresentInteractObj(const string name)
  {
   for(int i=0;i<this.InteractTotal();i++)
     {
      CForm *obj=this.GetInteractForm(i);
      if(obj==NULL)
         continue;
      if(obj.Name()==name)
         return true;
     }
   return false;
  }
//+------------------------------------------------------------------+

Percorremos em um loop lista de objetos de interação, obtemos o próximo objeto-forma e, se o nome do objeto for igual ao passado para o método, retornamos true - o objeto com o mesmo nome já está na lista. No final do loop, retornamos false — o objeto com o nome especificado não foi encontrado.


Método protegido que cria uma lista de todos os objetos de interação:

//+------------------------------------------------------------------+
//| Create the list of all interaction objects                       |
//+------------------------------------------------------------------+
void CForm::CreateListDepInteractObj(CArrayObj *list)
  {
   for(int i=0;i<this.ElementsTotal();i++)
     {
      CForm *form=this.GetElement(i);
      if(form==NULL || form.TypeGraphElement()<GRAPH_ELEMENT_TYPE_FORM)
         continue;
      if(this.IsPresentInteractObj(form.Name()))
         continue;
      if(list.Add(form))
         form.CreateListDepInteractObj(list);
     }
  }
//+------------------------------------------------------------------+

No loop sobre todos os objetos anexados, obtemos o próximo objeto-forma.
Se o objeto não for recebido ou seu tipo for menor que o objeto-forma, continuamos com o próximo.
Se um objeto com o mesmo nome já estiver na lista, continuamos com o próximo.
.
Se o objeto foi colocado com sucesso na lista de objetos de interação, chamamos o mesmo método para esse objeto para procurar objetos de interação anexados a ele
.
Neste caso, a lista especificada nos parâmetros de entrada deste método é passada para o método.
Assim, os ponteiros para todos os objetos de interação de toda a hierarquia de objetos anexados serão colocados em uma lista.
Passamos a lista que está no objeto principal, e especificamos no método public que cria uma lista de todos os objetos de interação:

//+------------------------------------------------------------------+
//| Create the list of all interaction objects                       |
//+------------------------------------------------------------------+
int CForm::CreateListInteractObj(void)
  {
   this.CreateListDepInteractObj(this.GetListInteractObj());
   return this.m_list_interact.Total();
  }
//+------------------------------------------------------------------+

Tudo é simples aqui: chamamos o método acima para criar uma lista de objetos de interação e passamos a ele um ponteiro para a lista do objeto atual. A partir do método, retornamos o número de ponteiros para objetos de interação adicionados à lista.


Vamos fazer modificações no arquivo \MQL5\Include\DoEasy\Objects\Graph\WForms\WinFormBase.mqh da classe de objeto base dos objetos WinForms da biblioteca.

Como removemos as variáveis que armazenam a largura da moldura da forma e as substituímos por métodos no arquivo de classe do objeto-forma, neste arquivo precisamos remover os métodos agora desnecessários para definir e retornar a largura da moldura:

   virtual void      SetPadding(const int left,const int top,const int right,const int bottom)
                       {
                        this.SetPaddingLeft(left); this.SetPaddingTop(top); this.SetPaddingRight(right); this.SetPaddingBottom(bottom);
                       }
   
//--- Set the width of the element frame (1) to the left, (2) at the top, (3) to the right and (4) at the bottom
   virtual void      SetFrameWidthLeft(const uint value)             { this.m_frame_width_left=(int)value;                                               }
   virtual void      SetFrameWidthTop(const uint value)              { this.m_frame_width_top=(int)value;                                                }
   virtual void      SetFrameWidthRight(const uint value)            { this.m_frame_width_right=(int)value;                                              }
   virtual void      SetFrameWidthBottom(const uint value)           { this.m_frame_width_bottom=(int)value;                                             }
   virtual void      SetFrameWidthAll(const uint value)
                       {
                        this.SetFrameWidthLeft(value); this.SetFrameWidthTop(value); this.SetFrameWidthRight(value); this.SetFrameWidthBottom(value);
                       }
   virtual void      SetFrameWidth(const uint left,const uint top,const uint right,const uint bottom)
                       {
                        this.SetFrameWidthLeft(left); this.SetFrameWidthTop(top); this.SetFrameWidthRight(right); this.SetFrameWidthBottom(bottom);
                       }
   
//--- Return the width of the element frame (1) to the left, (2) at the top, (3) to the right and (4) at the bottom
   int               FrameWidthLeft(void)                      const { return this.m_frame_width_left;                                                   }
   int               FrameWidthTop(void)                       const { return this.m_frame_width_top;                                                    }
   int               FrameWidthRight(void)                     const { return this.m_frame_width_right;                                                  }
   int               FrameWidthBottom(void)                    const { return this.m_frame_width_bottom;                                                 }
   
//--- Return the gap (1) to the left, (2) at the top, (3) to the right and (4) at the bottom between the fields inside the control
   int               PaddingLeft(void)                         const { return (int)this.GetProperty(CANV_ELEMENT_PROP_PADDING_LEFT);                     }
   int               PaddingTop(void)                          const { return (int)this.GetProperty(CANV_ELEMENT_PROP_PADDING_TOP);                      }
   int               PaddingRight(void)                        const { return (int)this.GetProperty(CANV_ELEMENT_PROP_PADDING_RIGHT);                    }
   int               PaddingBottom(void)                       const { return (int)this.GetProperty(CANV_ELEMENT_PROP_PADDING_BOTTOM);                   }


Nos métodos que definem o tamanho do recuo, substituiremos o acesso às variáveis pela leitura dos valores retornados dos métodos:

//--- Set the gap (1) to the left, (2) at the top, (3) to the right, (4) at the bottom and (5) on all sides inside the control
   virtual void      SetPaddingLeft(const uint value)
                       {
                        int padding=((int)value<this.BorderSizeLeft() ? this.BorderSizeLeft() : (int)value);
                        this.SetProperty(CANV_ELEMENT_PROP_PADDING_LEFT,padding);
                       }
   virtual void      SetPaddingTop(const uint value)
                       {
                        int padding=((int)value<this.BorderSizeTop() ? this.BorderSizeTop() : (int)value);
                        this.SetProperty(CANV_ELEMENT_PROP_PADDING_TOP,padding);
                       }
   virtual void      SetPaddingRight(const uint value)
                       {
                        int padding=((int)value<this.BorderSizeRight() ? this.BorderSizeRight() : (int)value);
                        this.SetProperty(CANV_ELEMENT_PROP_PADDING_RIGHT,padding);
                       }
   virtual void      SetPaddingBottom(const uint value)
                       {
                        int padding=((int)value<this.BorderSizeBottom() ? this.BorderSizeBottom() : (int)value);
                        this.SetProperty(CANV_ELEMENT_PROP_PADDING_BOTTOM,padding);
                       }


Vamos substituir a chamada aos antigos métodos FrameWidth pelo novo BorderSize:

   virtual void      SetPadding(const int left,const int top,const int right,const int bottom)
                       {
                        this.SetPaddingLeft(left); this.SetPaddingTop(top); this.SetPaddingRight(right); this.SetPaddingBottom(bottom);
                       }
   
//--- Set the width of all sides of the element frame
   virtual void      SetBorderSizeAll(const uint value)
                       {
                        this.SetBorderSizeLeft(value); this.SetBorderSizeTop(value); this.SetBorderSizeRight(value); this.SetBorderSizeBottom(value);
                       }
   virtual void      SetBorderSize(const uint left,const uint top,const uint right,const uint bottom)
                       {
                        this.SetBorderSizeLeft(left); this.SetBorderSizeTop(top); this.SetBorderSizeRight(right); this.SetBorderSizeBottom(bottom);
                       }
   
//--- Return the gap (1) to the left, (2) at the top, (3) to the right and (4) at the bottom between the fields inside the control

...

//+------------------------------------------------------------------+
//| Clear the element filling it with color and opacity              |
//+------------------------------------------------------------------+
void CWinFormBase::Erase(const color colour,const uchar opacity,const bool redraw=false)
  {
//--- Fill the element having the specified color and the redrawing flag
   CGCnvElement::Erase(colour,opacity,redraw);
//--- If the object has a frame, draw it
   if(this.BorderStyle()!=FRAME_STYLE_NONE && redraw)
      this.DrawFormFrame(this.BorderSizeTop(),this.BorderSizeBottom(),this.BorderSizeLeft(),this.BorderSizeRight(),this.BorderColor(),this.Opacity(),this.BorderStyle());
//--- Update the element having the specified redrawing flag
   this.Update(redraw);
  }
//+------------------------------------------------------------------+
//| Clear the element with a gradient fill                           |
//+------------------------------------------------------------------+
void CWinFormBase::Erase(color &colors[],const uchar opacity,const bool vgradient,const bool cycle,const bool redraw=false)
  {
//--- Fill the element having the specified color array and the redrawing flag
   CGCnvElement::Erase(colors,opacity,vgradient,cycle,redraw);
//--- If the object has a frame, draw it
   if(this.BorderStyle()!=FRAME_STYLE_NONE && redraw)
      this.DrawFormFrame(this.BorderSizeTop(),this.BorderSizeBottom(),this.BorderSizeLeft(),this.BorderSizeRight(),this.BorderColor(),this.Opacity(),this.BorderStyle());
//--- Update the element having the specified redrawing flag
   this.Update(redraw);
  }
//+------------------------------------------------------------------+


Vamos declarar métodos que retornam descrições de propriedades de controles:

//--- Return the gap (1) to the left, (2) at the top, (3) to the right and (4) at the bottom between the fields inside the control
   int               PaddingLeft(void)                         const { return (int)this.GetProperty(CANV_ELEMENT_PROP_PADDING_LEFT);                     }
   int               PaddingTop(void)                          const { return (int)this.GetProperty(CANV_ELEMENT_PROP_PADDING_TOP);                      }
   int               PaddingRight(void)                        const { return (int)this.GetProperty(CANV_ELEMENT_PROP_PADDING_RIGHT);                    }
   int               PaddingBottom(void)                       const { return (int)this.GetProperty(CANV_ELEMENT_PROP_PADDING_BOTTOM);                   }
   
//--- Get description of an order's (1) integer, (2) real and (3) string property
   string            GetPropertyDescription(ENUM_CANV_ELEMENT_PROP_INTEGER property,bool only_prop=false);
   string            GetPropertyDescription(ENUM_CANV_ELEMENT_PROP_DOUBLE property,bool only_prop=false);
   string            GetPropertyDescription(ENUM_CANV_ELEMENT_PROP_STRING property,bool only_prop=false);

//--- Return the description (1) of the control auto resizing depending on the content,
//--- (2) mode of binding the control borders to the container,
//--- (3) status of a control having a checkbox,
//--- (4) font style, (5) font width type and (6) control frame style
   string            AutoSizeModeDescription(void);
   string            DockModeDescription(void);
   string            CheckStateDescription(void);
   string            FontStyleDescription(void);
   string            FontBoldTypeDescription(void);
   string            BorderStyleDescription(void);

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


No construtor da classe, substituiremos a atribuição de valores a variáveis pela definição o de valores de propriedade usando o novo método:

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


Método que retorna uma descrição do estilo de fonte:

//+------------------------------------------------------------------+
//| Return the font style description                                |
//+------------------------------------------------------------------+
string CWinFormBase::FontStyleDescription(void)
  {
   return
     (
      this.FontDrawStyle()==FONT_STYLE_ITALIC      ? CMessage::Text(MSG_LIB_TEXT_FONT_STYLE_ITALIC)      :
      this.FontDrawStyle()==FONT_STYLE_UNDERLINE   ? CMessage::Text(MSG_LIB_TEXT_FONT_STYLE_UNDERLINE)   :
      this.FontDrawStyle()==FONT_STYLE_STRIKEOUT   ? CMessage::Text(MSG_LIB_TEXT_FONT_STYLE_STRIKEOUT)   :
      CMessage::Text(MSG_LIB_TEXT_FONT_STYLE_NORMAL)
     );
  }
//+------------------------------------------------------------------+

Dependendo do estilo de fonte definido, a mensagem de texto apropriada é retornada.


Método que retorna uma descrição do tipo de peso da fonte:

//+------------------------------------------------------------------+
//| Return the font width type description                           |
//+------------------------------------------------------------------+
string CWinFormBase::FontBoldTypeDescription(void)
  {
   uchar array[];
   int total=StringToCharArray(EnumToString((ENUM_FW_TYPE)this.GetProperty(CANV_ELEMENT_PROP_BOLD_TYPE)),array,8);
   for(int i=1;i<total;i++)
      array[i]+=0x20;
   return CharArrayToString(array);
  }
//+------------------------------------------------------------------+

A enumeração ENUM_FW_TYPE tem as seguintes constantes:

//+------------------------------------------------------------------+
//| FOnt width type list                                             |
//+------------------------------------------------------------------+
enum ENUM_FW_TYPE
  {
   FW_TYPE_DONTCARE=FW_DONTCARE,
   FW_TYPE_THIN=FW_THIN,
   FW_TYPE_EXTRALIGHT=FW_EXTRALIGHT,
   FW_TYPE_ULTRALIGHT=FW_ULTRALIGHT,
   FW_TYPE_LIGHT=FW_LIGHT,
   FW_TYPE_NORMAL=FW_NORMAL,
   FW_TYPE_REGULAR=FW_REGULAR,
   FW_TYPE_MEDIUM=FW_MEDIUM,
   FW_TYPE_SEMIBOLD=FW_SEMIBOLD,
   FW_TYPE_DEMIBOLD=FW_DEMIBOLD,
   FW_TYPE_BOLD=FW_BOLD,
   FW_TYPE_EXTRABOLD=FW_EXTRABOLD,
   FW_TYPE_ULTRABOLD=FW_ULTRABOLD,
   FW_TYPE_HEAVY=FW_HEAVY,
   FW_TYPE_BLACK=FW_BLACK
  };
//+------------------------------------------------------------------+

Para, por exemplo, o tipo de espessura "Regular", precisamos pegar uma substring da constante FW_TYPE_REGULAR, começando na posição 8. Após extrair a substring do nome da constante, obtemos a string "REGULAR". Aqui todos os caracteres são maiúsculos. Agora precisamos tornar todos os caracteres minúsculos, exceto o primeiro.
Para fazer isso, basta adicionar o deslocamento 32 (0x20) ao código de caractere, pois os códigos de caracteres minúsculos diferem dos caracteres maiúsculos em exatamente 32. O que estamos fazendo? A substring extraída, composta por caracteres maiúsculos, é inserida na matriz uchar e, em seguida, em um loop sobre todos os caracteres da matriz (para cada letra), adicionamos o valor 32 ao valor do caractere. Como o primeiro caractere (na célula 0 da matriz) não precisa ser alterada, iniciamos o loop da célula 1 da matriz - a partir do segundo caractere. Como resultado, retornamos a matriz uchar modificada convertida de volta em uma string.


Método que retorna uma descrição do estilo da moldura do controle:

//+------------------------------------------------------------------+
//| Return the description of the control frame style                |
//+------------------------------------------------------------------+
string CWinFormBase::BorderStyleDescription(void)
  {
   ENUM_FRAME_STYLE property=(ENUM_FRAME_STYLE)this.GetProperty(CANV_ELEMENT_PROP_BORDER_STYLE);
   return
     (
      property==FRAME_STYLE_SIMPLE  ? CMessage::Text(MSG_LIB_TEXT_FRAME_STYLE_SIMPLE)  :
      property==FRAME_STYLE_FLAT    ? CMessage::Text(MSG_LIB_TEXT_FRAME_STYLE_FLAT)    :
      property==FRAME_STYLE_BEVEL   ? CMessage::Text(MSG_LIB_TEXT_FRAME_STYLE_BEVEL)   :
      property==FRAME_STYLE_STAMP   ? CMessage::Text(MSG_LIB_TEXT_FRAME_STYLE_STAMP)   :
      CMessage::Text(MSG_LIB_TEXT_FRAME_STYLE_NONE)
     );
  }
//+------------------------------------------------------------------+

Dependendo do estilo de moldura definida para o objeto, uma mensagem de texto é retornada.


Método que retorna a descrição da propriedade inteira do elemento:

//+------------------------------------------------------------------+
//| Return the description of the control integer property           |
//+------------------------------------------------------------------+
string CWinFormBase::GetPropertyDescription(ENUM_CANV_ELEMENT_PROP_INTEGER property,bool only_prop=false)
  {
   return
     (
      property==CANV_ELEMENT_PROP_ID                           ?  CMessage::Text(MSG_CANV_ELEMENT_PROP_ID)+
         (only_prop ? "" : !this.SupportProperty(property)     ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)this.GetProperty(property)
         )  :
      property==CANV_ELEMENT_PROP_TYPE                         ?  CMessage::Text(MSG_CANV_ELEMENT_PROP_TYPE)+
         (only_prop ? "" : !this.SupportProperty(property)     ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+this.TypeElementDescription()
         )  :
      property==CANV_ELEMENT_PROP_BELONG                       ?  CMessage::Text(MSG_CANV_ELEMENT_PROP_BELONG)+
         (only_prop ? "" : !this.SupportProperty(property)     ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+this.BelongDescription()
         )  :
      property==CANV_ELEMENT_PROP_NUM                          ?  CMessage::Text(MSG_CANV_ELEMENT_PROP_NUM)+
         (only_prop ? "" : !this.SupportProperty(property)     ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)this.GetProperty(property)
         )  :
      property==CANV_ELEMENT_PROP_CHART_ID                     ?  CMessage::Text(MSG_GRAPH_OBJ_PROP_CHART_ID)+
         (only_prop ? "" : !this.SupportProperty(property)     ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)this.GetProperty(property)
         )  :
      property==CANV_ELEMENT_PROP_WND_NUM                      ?  CMessage::Text(MSG_GRAPH_OBJ_PROP_WND_NUM)+
         (only_prop ? "" : !this.SupportProperty(property)     ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)this.GetProperty(property)
         )  :
      property==CANV_ELEMENT_PROP_COORD_X                      ?  CMessage::Text(MSG_CANV_ELEMENT_PROP_COORD_X)+
         (only_prop ? "" : !this.SupportProperty(property)     ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)this.GetProperty(property)
         )  :
      property==CANV_ELEMENT_PROP_COORD_Y                      ?  CMessage::Text(MSG_CANV_ELEMENT_PROP_COORD_Y)+
         (only_prop ? "" : !this.SupportProperty(property)     ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)this.GetProperty(property)
         )  :
      property==CANV_ELEMENT_PROP_WIDTH                        ?  CMessage::Text(MSG_CANV_ELEMENT_PROP_WIDTH)+
         (only_prop ? "" : !this.SupportProperty(property)     ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)this.GetProperty(property)
         )  :
      property==CANV_ELEMENT_PROP_HEIGHT                       ?  CMessage::Text(MSG_CANV_ELEMENT_PROP_HEIGHT)+
         (only_prop ? "" : !this.SupportProperty(property)     ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)this.GetProperty(property)
         )  :
      property==CANV_ELEMENT_PROP_RIGHT                        ?  CMessage::Text(MSG_CANV_ELEMENT_PROP_RIGHT)+
         (only_prop ? "" : !this.SupportProperty(property)     ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)this.GetProperty(property)
         )  :
      property==CANV_ELEMENT_PROP_BOTTOM                       ?  CMessage::Text(MSG_CANV_ELEMENT_PROP_BOTTOM)+
         (only_prop ? "" : !this.SupportProperty(property)     ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)this.GetProperty(property)
         )  :
      property==CANV_ELEMENT_PROP_ACT_SHIFT_LEFT               ?  CMessage::Text(MSG_CANV_ELEMENT_PROP_ACT_SHIFT_LEFT)+
         (only_prop ? "" : !this.SupportProperty(property)     ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)this.GetProperty(property)
         )  :
      property==CANV_ELEMENT_PROP_ACT_SHIFT_TOP                ?  CMessage::Text(MSG_CANV_ELEMENT_PROP_ACT_SHIFT_TOP)+
         (only_prop ? "" : !this.SupportProperty(property)     ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)this.GetProperty(property)
         )  :
      property==CANV_ELEMENT_PROP_ACT_SHIFT_RIGHT              ?  CMessage::Text(MSG_CANV_ELEMENT_PROP_ACT_SHIFT_RIGHT)+
         (only_prop ? "" : !this.SupportProperty(property)     ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)this.GetProperty(property)
         )  :
      property==CANV_ELEMENT_PROP_ACT_SHIFT_BOTTOM             ?  CMessage::Text(MSG_CANV_ELEMENT_PROP_ACT_SHIFT_BOTTOM)+
         (only_prop ? "" : !this.SupportProperty(property)     ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)this.GetProperty(property)
         )  :
      property==CANV_ELEMENT_PROP_MOVABLE                      ?  CMessage::Text(MSG_CANV_ELEMENT_PROP_MOVABLE)+
         (only_prop ? "" : !this.SupportProperty(property)     ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)(bool)this.GetProperty(property)
         )  :
      property==CANV_ELEMENT_PROP_ACTIVE                       ?  CMessage::Text(MSG_CANV_ELEMENT_PROP_ACTIVE)+
         (only_prop ? "" : !this.SupportProperty(property)     ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)(bool)this.GetProperty(property)
         )  :
      property==CANV_ELEMENT_PROP_INTERACTION                  ?  CMessage::Text(MSG_CANV_ELEMENT_PROP_INTERACTION)+
         (only_prop ? "" : !this.SupportProperty(property)     ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)(bool)this.GetProperty(property)
         )  :
      property==CANV_ELEMENT_PROP_COORD_ACT_X                  ?  CMessage::Text(MSG_CANV_ELEMENT_PROP_COORD_ACT_X)+
         (only_prop ? "" : !this.SupportProperty(property)     ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)this.GetProperty(property)
         )  :
      property==CANV_ELEMENT_PROP_COORD_ACT_Y                  ?  CMessage::Text(MSG_CANV_ELEMENT_PROP_COORD_ACT_Y)+
         (only_prop ? "" : !this.SupportProperty(property)     ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)this.GetProperty(property)
         )  :
      property==CANV_ELEMENT_PROP_ACT_RIGHT                    ?  CMessage::Text(MSG_CANV_ELEMENT_PROP_ACT_RIGHT)+
         (only_prop ? "" : !this.SupportProperty(property)     ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)this.GetProperty(property)
         )  :
      property==CANV_ELEMENT_PROP_ACT_BOTTOM                   ?  CMessage::Text(MSG_CANV_ELEMENT_PROP_ACT_BOTTOM)+
         (only_prop ? "" : !this.SupportProperty(property)     ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)this.GetProperty(property)
         )  :
      property==CANV_ELEMENT_PROP_ZORDER                       ?  CMessage::Text(MSG_GRAPH_OBJ_PROP_ZORDER)+
         (only_prop ? "" : !this.SupportProperty(property)     ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)this.GetProperty(property)
         )  :
      property==CANV_ELEMENT_PROP_ENABLED                      ?  CMessage::Text(MSG_CANV_ELEMENT_PROP_ENABLED)+
         (only_prop ? "" : !this.SupportProperty(property)     ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)(bool)this.GetProperty(property)
         )  :
      property==CANV_ELEMENT_PROP_FORE_COLOR                   ?  CMessage::Text(MSG_CANV_ELEMENT_PROP_FORE_COLOR)+
         (only_prop ? "" : !this.SupportProperty(property)     ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+ColorToString((color)this.GetProperty(property),true)
         )  :
      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_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_OPACITY     ?  CMessage::Text(MSG_CANV_ELEMENT_PROP_BACKGROUND_COLOR_OPACITY)+
         (only_prop ? "" : !this.SupportProperty(property)     ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)this.GetProperty(property)
         )  :
      property==CANV_ELEMENT_PROP_BACKGROUND_COLOR_MOUSE_DOWN  ?  CMessage::Text(MSG_CANV_ELEMENT_PROP_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_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_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_BORDER_STYLE                 ?  CMessage::Text(MSG_CANV_ELEMENT_PROP_BORDER_STYLE)+
         (only_prop ? "" : !this.SupportProperty(property)     ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+BorderStyleDescription()
         )  :
      property==CANV_ELEMENT_PROP_BORDER_SIZE_TOP              ?  CMessage::Text(MSG_CANV_ELEMENT_PROP_BORDER_SIZE_TOP)+
         (only_prop ? "" : !this.SupportProperty(property)     ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)this.GetProperty(property)
         )  :
      property==CANV_ELEMENT_PROP_BORDER_SIZE_BOTTOM           ?  CMessage::Text(MSG_CANV_ELEMENT_PROP_BORDER_SIZE_BOTTOM)+
         (only_prop ? "" : !this.SupportProperty(property)     ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)this.GetProperty(property)
         )  :
      property==CANV_ELEMENT_PROP_BORDER_SIZE_LEFT             ?  CMessage::Text(MSG_CANV_ELEMENT_PROP_BORDER_SIZE_LEFT)+
         (only_prop ? "" : !this.SupportProperty(property)     ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)this.GetProperty(property)
         )  :
      property==CANV_ELEMENT_PROP_BORDER_SIZE_RIGHT            ?  CMessage::Text(MSG_CANV_ELEMENT_PROP_BORDER_SIZE_RIGHT)+
         (only_prop ? "" : !this.SupportProperty(property)     ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)this.GetProperty(property)
         )  :
      property==CANV_ELEMENT_PROP_BORDER_COLOR                 ?  CMessage::Text(MSG_CANV_ELEMENT_PROP_BORDER_COLOR)+
         (only_prop ? "" : !this.SupportProperty(property)     ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+ColorToString((color)this.GetProperty(property),true)
         )  :
      property==CANV_ELEMENT_PROP_BORDER_COLOR_MOUSE_DOWN      ?  CMessage::Text(MSG_CANV_ELEMENT_PROP_BORDER_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_BORDER_COLOR_MOUSE_OVER      ?  CMessage::Text(MSG_CANV_ELEMENT_PROP_BORDER_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_AUTOSIZE                     ?  CMessage::Text(MSG_CANV_ELEMENT_PROP_AUTOSIZE)+
         (only_prop ? "" : !this.SupportProperty(property)     ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)(bool)this.GetProperty(property)
         )  :
      property==CANV_ELEMENT_PROP_AUTOSIZE_MODE                ?  CMessage::Text(MSG_CANV_ELEMENT_PROP_AUTOSIZE_MODE)+
         (only_prop ? "" : !this.SupportProperty(property)     ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+this.AutoSizeModeDescription()
         )  :
      property==CANV_ELEMENT_PROP_AUTOSCROLL                   ?  CMessage::Text(MSG_CANV_ELEMENT_PROP_AUTOSCROLL)+
         (only_prop ? "" : !this.SupportProperty(property)     ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)(bool)this.GetProperty(property)
         )  :
      property==CANV_ELEMENT_PROP_AUTOSCROLL_MARGIN_W          ?  CMessage::Text(MSG_CANV_ELEMENT_PROP_AUTOSCROLL_MARGIN_W)+
         (only_prop ? "" : !this.SupportProperty(property)     ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)this.GetProperty(property)
         )  :
      property==CANV_ELEMENT_PROP_AUTOSCROLL_MARGIN_H          ?  CMessage::Text(MSG_CANV_ELEMENT_PROP_AUTOSCROLL_MARGIN_H)+
         (only_prop ? "" : !this.SupportProperty(property)     ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)this.GetProperty(property)
         )  :
      property==CANV_ELEMENT_PROP_DOCK_MODE                    ?  CMessage::Text(MSG_CANV_ELEMENT_PROP_DOCK_MODE)+
         (only_prop ? "" : !this.SupportProperty(property)     ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+this.DockModeDescription()
         )  :
      property==CANV_ELEMENT_PROP_MARGIN_TOP                   ?  CMessage::Text(MSG_CANV_ELEMENT_PROP_MARGIN_TOP)+
         (only_prop ? "" : !this.SupportProperty(property)     ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)this.GetProperty(property)
         )  :
      property==CANV_ELEMENT_PROP_MARGIN_BOTTOM                ?  CMessage::Text(MSG_CANV_ELEMENT_PROP_MARGIN_BOTTOM)+
         (only_prop ? "" : !this.SupportProperty(property)     ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)this.GetProperty(property)
         )  :
      property==CANV_ELEMENT_PROP_MARGIN_LEFT                  ?  CMessage::Text(MSG_CANV_ELEMENT_PROP_MARGIN_LEFT)+
         (only_prop ? "" : !this.SupportProperty(property)     ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)this.GetProperty(property)
         )  :
      property==CANV_ELEMENT_PROP_MARGIN_RIGHT                 ?  CMessage::Text(MSG_CANV_ELEMENT_PROP_MARGIN_RIGHT)+
         (only_prop ? "" : !this.SupportProperty(property)     ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)this.GetProperty(property)
         )  :
      property==CANV_ELEMENT_PROP_PADDING_TOP                  ?  CMessage::Text(MSG_CANV_ELEMENT_PROP_PADDING_TOP)+
         (only_prop ? "" : !this.SupportProperty(property)     ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)this.GetProperty(property)
         )  :
      property==CANV_ELEMENT_PROP_PADDING_BOTTOM               ?  CMessage::Text(MSG_CANV_ELEMENT_PROP_PADDING_BOTTOM)+
         (only_prop ? "" : !this.SupportProperty(property)     ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)this.GetProperty(property)
         )  :
      property==CANV_ELEMENT_PROP_PADDING_LEFT                 ?  CMessage::Text(MSG_CANV_ELEMENT_PROP_PADDING_LEFT)+
         (only_prop ? "" : !this.SupportProperty(property)     ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)this.GetProperty(property)
         )  :
      property==CANV_ELEMENT_PROP_PADDING_RIGHT                ?  CMessage::Text(MSG_CANV_ELEMENT_PROP_PADDING_RIGHT)+
         (only_prop ? "" : !this.SupportProperty(property)     ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)this.GetProperty(property)
         )  :
      property==CANV_ELEMENT_PROP_TEXT_ALIGN                   ?  CMessage::Text(MSG_CANV_ELEMENT_PROP_TEXT_ALIGN)+
         (only_prop ? "" : !this.SupportProperty(property)     ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+AnchorForGraphicsObjDescription((ENUM_ANCHOR_POINT)this.GetProperty(property))
         )  :
      property==CANV_ELEMENT_PROP_CHECK_ALIGN                  ?  CMessage::Text(MSG_CANV_ELEMENT_PROP_CHECK_ALIGN)+
         (only_prop ? "" : !this.SupportProperty(property)     ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+AnchorForGraphicsObjDescription((ENUM_ANCHOR_POINT)this.GetProperty(property))
         )  :
      property==CANV_ELEMENT_PROP_CHECKED                      ?  CMessage::Text(MSG_CANV_ELEMENT_PROP_CHECKED)+
         (only_prop ? "" : !this.SupportProperty(property)     ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)(bool)this.GetProperty(property)
         )  :
      property==CANV_ELEMENT_PROP_CHECK_STATE                  ?  CMessage::Text(MSG_CANV_ELEMENT_PROP_CHECK_STATE)+
         (only_prop ? "" : !this.SupportProperty(property)     ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+this.CheckStateDescription()
         )  :
      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)
         )  :
      ""
     );
  }
//+------------------------------------------------------------------+

Uma propriedade inteira do objeto é passada para o método. Dependendo da propriedade passada ao método e do sinalizador que indica que o objeto suporta esta propriedade, a string retornada do método é construída. Existem métodos semelhantes em quase todos os objetos da biblioteca e não faz sentido reexaminar aqui. Apenas observarei que até agora cada elemento gráfico suporta todas as propriedades dos elementos gráficos. Adicionaremos métodos que retornam sinalizadores para suportar algumas propriedades dos elementos gráficos aos objetos WinForms, pois podemos controlar visualmente os valores dessas propriedades e ferramentas para modificá-los com o mouse ou teclado - uma vez que a maioria dos objetos WinForms são criados e sua interatividade é funcional.


Método que retorna uma descrição da propriedade real do elemento:

//+------------------------------------------------------------------+
//| Return the description of the control real property              |
//+------------------------------------------------------------------+
string CWinFormBase::GetPropertyDescription(ENUM_CANV_ELEMENT_PROP_DOUBLE property,bool only_prop=false)
  {
   return("");
  }
//+------------------------------------------------------------------+

Como ainda não temos propriedades reais para elementos gráficos, o método retorna uma string vazia.


Método que retorna uma descrição da propriedade string do elemento:

//+------------------------------------------------------------------+
//| Return the description of the control string property            |
//+------------------------------------------------------------------+
string CWinFormBase::GetPropertyDescription(ENUM_CANV_ELEMENT_PROP_STRING property,bool only_prop=false)
  {
   return
     (
      property==CANV_ELEMENT_PROP_NAME_OBJ   ? CMessage::Text(MSG_CANV_ELEMENT_PROP_NAME_OBJ)+": \""+this.GetProperty(property)+"\""   :
      property==CANV_ELEMENT_PROP_NAME_RES   ? CMessage::Text(MSG_CANV_ELEMENT_PROP_NAME_RES)+": \""+this.GetProperty(property)+"\""   :
      property==CANV_ELEMENT_PROP_TEXT       ? CMessage::Text(MSG_CANV_ELEMENT_PROP_TEXT)+": \""+this.GetProperty(property)+"\""       :
      ""
     );
  }
//+------------------------------------------------------------------+

A propriedade string do objeto é passada para o método. Dependendo da propriedade passada ao método, a string retornada do método é construída.


Método que retorna uma descrição do modo de redimensionamento automático de um elemento em função do conteúdo:

//+------------------------------------------------------------------+
//| Return the description of the mode for auto                      |
//| resizing the control to fit the content                          |
//+------------------------------------------------------------------+
string CWinFormBase::AutoSizeModeDescription(void)
  {
   return
     (
      this.GetProperty(CANV_ELEMENT_PROP_AUTOSIZE_MODE)==CANV_ELEMENT_AUTO_SIZE_MODE_GROW ? CMessage::Text(MSG_LIB_TEXT_AUTO_SIZE_MODE_GROW) :
      CMessage::Text(MSG_LIB_TEXT_AUTO_SIZE_MODE_GROW_SHRINK)
     );
  }
//+------------------------------------------------------------------+

Dependendo do modo de redimensionamento automático do elemento, a mensagem de texto correspondente é retornada.


Método que retorna uma descrição do modo de encaixamento das bordas do elemento ao contêiner:

//+-------------------------------------------------------------------------------------+
//| Return the description of the mode for binding the element borders to the container |
//+-------------------------------------------------------------------------------------+
string CWinFormBase::DockModeDescription(void)
  {
   return
     (
      this.GetProperty(CANV_ELEMENT_PROP_DOCK_MODE)==CANV_ELEMENT_DOCK_MODE_TOP     ? CMessage::Text(MSG_LIB_TEXT_DOCK_MODE_TOP)    :
      this.GetProperty(CANV_ELEMENT_PROP_DOCK_MODE)==CANV_ELEMENT_DOCK_MODE_BOTTOM  ? CMessage::Text(MSG_LIB_TEXT_DOCK_MODE_BOTTOM) :
      this.GetProperty(CANV_ELEMENT_PROP_DOCK_MODE)==CANV_ELEMENT_DOCK_MODE_LEFT    ? CMessage::Text(MSG_LIB_TEXT_DOCK_MODE_LEFT)   :
      this.GetProperty(CANV_ELEMENT_PROP_DOCK_MODE)==CANV_ELEMENT_DOCK_MODE_RIGHT   ? CMessage::Text(MSG_LIB_TEXT_DOCK_MODE_RIGHT)  :
      this.GetProperty(CANV_ELEMENT_PROP_DOCK_MODE)==CANV_ELEMENT_DOCK_MODE_FILL    ? CMessage::Text(MSG_LIB_TEXT_DOCK_MODE_FILL)   :
      CMessage::Text(MSG_LIB_TEXT_DOCK_MODE_NONE)
     );
  }
//+------------------------------------------------------------------+

Dependendo do modo de ajuste de borda, a mensagem de texto correspondente é retornada.


Método que retorna uma descrição do estado do controle que possui a caixa de seleção:

//+------------------------------------------------------------------+
//| Return the status description                                    |
//| of the control featuring the checkbox                            |
//+------------------------------------------------------------------+
string CWinFormBase::CheckStateDescription(void)
  {
   return
     (
      this.GetProperty(CANV_ELEMENT_PROP_CHECK_STATE)==CANV_ELEMENT_CHEK_STATE_CHECKED       ? CMessage::Text(MSG_LIB_TEXT_CHEK_STATE_CHECKED)        :
      this.GetProperty(CANV_ELEMENT_PROP_CHECK_STATE)==CANV_ELEMENT_CHEK_STATE_INDETERMINATE ? CMessage::Text(MSG_LIB_TEXT_CHEK_STATE_INDETERMINATE)  :
      CMessage::Text(MSG_LIB_TEXT_CHEK_STATE_UNCHECKED)
     );
  }
//+------------------------------------------------------------------+

Dependendo do estado da caixa de seleção do elemento, a mensagem de texto correspondente é retornada.


No arquivo \MQL5\Include\DoEasy\Objects\Graph\WForms\Common Controls\CommonBase.mqh da classe de objeto base dos controles padrão dos objetos WinForms, faremos pequenas melhorias.

Vamos alterar o tipo de método que define automaticamente a largura e a altura do elemento de void para bool - para cada objeto filho, a implementação desse método pode ser diferente, portanto, se você precisar alterar a lógica desse método, ele será redefinido em diferentes classes à sua maneira. Mas ele precisa ser capaz de devolver o resultado de seu trabalho. Se o objeto não alterar seu tamanho, não há necessidade de substituir o método na classe - aqui ele simplesmente retorna true, sem fazer nada:

//+------------------------------------------------------------------+
//| Class of the base WForms standard control object                 |
//+------------------------------------------------------------------+
class CCommonBase : public CWinFormBase
  {
private:

protected:
//--- Set the element width and height automatically
   virtual bool      AutoSetWH(void)   { return true; }
//--- Initialize the variables
   virtual void      Initialize(void);
   
public:


No método de inicialização de variável, substituiremos a gravação de valores de largura de moldura em variáveis pela definição desses valores usando o método e renomearemos o método que define a cor de fundo:

//+------------------------------------------------------------------+
//| Initialize the variables                                         |
//+------------------------------------------------------------------+
void CCommonBase::Initialize(void)
  {
//--- Clear all object lists and set sorted list flags for them
   this.m_list_elements.Clear();
   this.m_list_elements.Sort();
   this.m_list_tmp.Clear();
   this.m_list_tmp.Sort();
//--- Standard control has no shadow object
   this.m_shadow_obj=NULL;
   this.m_shadow=false;
//--- The width of the object frame on each side is 1 pixel by default
   this.SetBorderSizeAll(1);
//--- The object does not have a gradient filling (neither vertical, nor horizontal)
   this.m_gradient_v=false;
   this.m_gradient_c=false;
//--- Reset all "working" flags and variables
   this.m_mouse_state_flags=0;
   this.m_offset_x=0;
   this.m_offset_y=0;
   CGCnvElement::SetInteraction(false);
//--- 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);
   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);
   this.SetForeColorOpacity(CLR_DEF_FORE_COLOR_OPACITY);
   this.SetBorderStyle(FRAME_STYLE_NONE);
//--- Set the default text parameters
   this.SetFont(DEF_FONT,DEF_FONT_SIZE);
   this.SetText("");
   this.SetTextAnchor(FRAME_ANCHOR_LEFT_TOP);
   this.SetTextAlign(ANCHOR_LEFT_UPPER);
//--- Set the default object parameters
   this.SetAutoSize(false,false);
   this.SetMarginAll(3);
   this.SetPaddingAll(0);
   this.SetEnabled(true);
   this.SetVisible(true,false);
  }
//+------------------------------------------------------------------+


Vamos renomear os métodos que retornam valores de largura de moldura:

//+------------------------------------------------------------------+
//| Clear the element filling it with color and opacity              |
//+------------------------------------------------------------------+
void CCommonBase::Erase(const color colour,const uchar opacity,const bool redraw=false)
  {
//--- Fill the element having the specified color and the redrawing flag
   CGCnvElement::Erase(colour,opacity,redraw);
//--- If the object has a frame, draw it
   if(this.BorderStyle()!=FRAME_STYLE_NONE && redraw)
      this.DrawFormFrame(this.BorderSizeTop(),this.BorderSizeBottom(),this.BorderSizeLeft(),this.BorderSizeRight(),this.BorderColor(),255,this.BorderStyle());
//--- Update the element having the specified redrawing flag
   this.Update(redraw);
  }
//+------------------------------------------------------------------+
//| Clear the element with a gradient fill                           |
//+------------------------------------------------------------------+
void CCommonBase::Erase(color &colors[],const uchar opacity,const bool vgradient,const bool cycle,const bool redraw=false)
  {
//--- Fill the element having the specified color array and the redrawing flag
   CGCnvElement::Erase(colors,opacity,vgradient,cycle,redraw);
//--- If the object has a frame, draw it
   if(this.BorderStyle()!=FRAME_STYLE_NONE && redraw)
      this.DrawFormFrame(this.BorderSizeTop(),this.BorderSizeBottom(),this.BorderSizeLeft(),this.BorderSizeRight(),this.BorderColor(),255,this.BorderStyle());
//--- Update the element having the specified redrawing flag
   this.Update(redraw);
  }
//+------------------------------------------------------------------+


Na classe de objeto de rótulo de texto no arquivo \MQL5\Include\DoEasy\Objects\Graph\WForms\Common Controls\Label.mqh , temos um método que calcula as coordenadas da localização do rótulo dependendo do ponto de ancoragem do texto e seu alinhamento. Mas tudo isso é feito no método de redesenho de objeto. Isso não é o ideal, até porque muitos objetos herdados dessa classe também podem precisar posicionar o rótulo. Portanto, vamos transferir o cálculo de coordenadas de texto do método de redesenho de rótulo de texto para um novo método e chamá-lo quando necessário, e não apenas na classe de objeto de rótulo de texto, mas também em seus descendentes.

Na seção protegida da classe, alteramos o tipo do método para definir automaticamente o tamanho do elemento, declaramos o método que define as coordenadas e o ponto de ancoragem do texto dependendo de seu modo de alinhamentoe na seção pública de a classe , declaramos o método que define o sinalizador para redimensionar automaticamente o elemento em função do conteúdo:

//+------------------------------------------------------------------+
//| Label object class of WForms controls                            |
//+------------------------------------------------------------------+
class CLabel : public CCommonBase
  {
private:

protected:
//--- Set the element width and height automatically
   virtual bool      AutoSetWH(void);
//--- Set the text coordinates and anchor point depending on its alignment mode
   void              SetTextParamsByAlign(int &x,int &y);
public:
//--- Redraw the object
   virtual void      Redraw(bool redraw);
//--- Set the element text
   virtual void      SetText(const string text)
                       {
                        CWinFormBase::SetText(text);
                        if(this.AutoSize())
                           this.AutoSetWH();
                       }
//--- Set the flag of the element auto resizing depending on the content
   virtual void      SetAutoSize(const bool flag,const bool redraw);

//--- Constructor


Como o método para calcular as coordenadas do texto agora é movido para um método separado, o método de redesenho do objeto ficou mais curto:

//+------------------------------------------------------------------+
//| Redraw the object                                                |
//+------------------------------------------------------------------+
void CLabel::Redraw(bool redraw)
  {
//--- Fill the object with the background color having full transparency
   this.Erase(this.BackgroundColor(),0,true);
//--- Declare the variables for X and Y coordinates and set their values depending on the text alignment
   int x=0,y=0;
   this.SetTextParamsByAlign(x,y);
//--- Draw the text within the set coordinates of the object and the binding point of the text, and update the object 
   this.Text(x,y,this.Text(),this.ForeColor(),this.ForeColorOpacity(),this.TextAnchor());
   this.Update(redraw);
  }
//+------------------------------------------------------------------+


Método que define as coordenadas e o ponto de ancoragem do texto dependendo de seu modo de alinhamento:

//+------------------------------------------------------------------+
//| Set the text coordinates and anchor point                        |
//| depending on its alignment mode                                  |
//+------------------------------------------------------------------+
void CLabel::SetTextParamsByAlign(int &x,int &y)
  {
//--- Depending on the element text alignment type
   switch(this.TextAlign())
     {
      //--- The text is displayed in the upper left corner of the object
      case ANCHOR_LEFT_UPPER : 
        //--- Set the text binding point coordinate
        x=this.BorderSizeLeft();
        y=this.BorderSizeTop();
        //--- Set the text binding point at the top left
        this.SetTextAnchor(FRAME_ANCHOR_LEFT_TOP);
        break;
      //--- The text is drawn vertically from the left side of the object in the center
      case ANCHOR_LEFT : 
        //--- Set the text binding point coordinate
        x=this.BorderSizeLeft();
        y=this.Height()/2;
        //--- Set the text binding point at the center left
        this.SetTextAnchor(FRAME_ANCHOR_LEFT_CENTER);
        break;
      //--- The text is displayed in the lower left corner of the object
      case ANCHOR_LEFT_LOWER : 
        //--- Set the text binding point coordinate
        x=this.BorderSizeLeft();
        y=this.Height()-this.BorderSizeBottom();
        //--- Set the text binding point at the bottom left
        this.SetTextAnchor(FRAME_ANCHOR_LEFT_BOTTOM);
        break;
      //--- The text is drawn at the center of the bottom edge of the object
      case ANCHOR_LOWER : 
        //--- Set the text binding point coordinate
        x=this.Width()/2;
        y=this.Height()-this.BorderSizeBottom();
        //--- Set the text anchor point at the bottom center
        this.SetTextAnchor(FRAME_ANCHOR_CENTER_BOTTOM);
        break;
      //--- The text is displayed in the lower right corner of the object
      case ANCHOR_RIGHT_LOWER : 
        //--- Set the text binding point coordinate
        x=this.Width()-this.BorderSizeRight();
        y=this.Height()-this.BorderSizeBottom();
        //--- Set the text binding point at the bottom right
        this.SetTextAnchor(FRAME_ANCHOR_RIGHT_BOTTOM);
        break;
      //--- The text is drawn vertically from the right side of the object in the center
      case ANCHOR_RIGHT : 
        //--- Set the text binding point coordinate
        x=this.Width()-this.BorderSizeRight();
        y=this.Height()/2;
        //--- Set the text binding point at the center right
        this.SetTextAnchor(FRAME_ANCHOR_RIGHT_CENTER);
        break;
      //--- The text is displayed in the upper right corner of the object
      case ANCHOR_RIGHT_UPPER : 
        //--- Set the text binding point coordinate
        x=this.Width()-this.BorderSizeRight();
        y=this.BorderSizeTop();
        //--- Set the text binding point at the top right
        this.SetTextAnchor(FRAME_ANCHOR_RIGHT_TOP);
        break;
      //--- The text is drawn at the center of the upper edge of the object
      case ANCHOR_UPPER : 
        //--- Set the text binding point coordinate
        x=this.Width()/2;
        y=this.BorderSizeTop();
        //--- Set the text binding point at the center top
        this.SetTextAnchor(FRAME_ANCHOR_CENTER_TOP);
        break;
      //--- The text is drawn at the object center
      //---ANCHOR_CENTER
      default:
        //--- Set the text binding point coordinate
        x=this.Width()/2;
        y=this.Height()/2;
        //--- Set the text binding point at the center
        this.SetTextAnchor(FRAME_ANCHOR_CENTER);
        break;
     }
  }
//+------------------------------------------------------------------+

As linhas para calcular as coordenadas de texto do método de redesenho do objeto são simplesmente transferidas para o método. Mas aqui os métodos renomeados anteriormente agora são usados.

Como o método virtual que define automaticamente a largura e a altura do elemento deve agora retornar um resultado, em vez de simplesmente definir a nova largura e altura nas propriedades do objeto, o método grava o resultado do redimensionamento do objeto na variável de retorno:

//+------------------------------------------------------------------+
//| Set the element width and height automatically                   |
//+------------------------------------------------------------------+
bool CLabel::AutoSetWH(void)
  {
//--- Define the variables for receiving the label width and height
   int w=0, h=0;
//--- Get the width and height depending on the object text
   CGCnvElement::TextSize(this.Text()!="" && this.Text()!=NULL ? this.Text() : " ",w,h);
//--- Add the Margin values of the object on the left and right to the resulting width
   w+=(this.MarginLeft()+this.MarginRight());
//--- If failed to get the width, set it to three pixels
   if(w==0)
      w=3;
//--- Add the Margin values of the object on the top and bottom to the resulting height
   h+=(this.MarginTop()+this.MarginBottom());
//--- If failed to get the height, set it as "font size" * ratio
   if(h==0)
      h=(int)ceil(FontSize()*1.625);
//--- Set the object width and height from the received values and write the result to res
   bool res=true;
   res &=this.SetWidth(w);
   res &=this.SetHeight(h);
//--- Return the result of changing the width and height
   return res;
  }
//+------------------------------------------------------------------+


Método que define o sinalizador para redimensionar automaticamente o elemento em função do conteúdo:

//+------------------------------------------------------------------+
//| Set the flag of the control auto resizing                        |
//| depending on the content                                         |
//+------------------------------------------------------------------+
void CLabel::SetAutoSize(const bool flag,const bool redraw)
  {
   if(flag && this.AutoSetWH())
      CWinFormBase::SetAutoSize(flag,redraw);
  }
//+------------------------------------------------------------------+

Se o sinalizador passado para o método estiver definido e se for possível alterar o tamanho do objeto, escrevemos o valor do sinalizador nas propriedades.


Hoje faremos a classe do objeto WinForms RadioButton. Em seu conteúdo, o objeto é semelhante ao controle CheckBox implementado no arquivo \MQL5\Include\DoEasy\Objects\Graph\WForms\Common Controls\CheckBox.mqh, mas, ao contrário deste último, o objeto RadioButton tem uma caixa circular selecionável em vez de uma caixa quadrada. Assim, é vantajoso para nós herdarmos da classe de objeto CheckBox e substituirmos os métodos de desenho da caixa de seleção. Vamos remover o método SetTextCoords(), pois ele repete absolutamente o método SetTextParamsByAlign() do objeto pai, e para que todas as variáveis do objeto CheckBox estejam disponíveis em seus herdeiros, nós as movemos da seção privada para a protegida:

//+------------------------------------------------------------------+
//| Include files                                                    |
//+------------------------------------------------------------------+
#include "Label.mqh"
//+------------------------------------------------------------------+
//| CheckBox object class of the WForms controls                     |
//+------------------------------------------------------------------+
class CCheckBox : public CLabel
  {
private:
//--- Set X and Y checkbox coordinates
   void              SetCheckFlagCoords(int &x,int &y);
   void              SetTextCoords(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
//--- Set the element width and height automatically
   virtual bool      AutoSetWH(void);

O método virtual que define automaticamente a largura e a altura do elemento agora deve ser do tipo bool.

Na seção pública da classe, vamos alterar os métodos que definem o estado da caixa de seleção e do controle. Agora, antes de definir os estados, o estado atual será verificado primeiro, pois defini-los deve fazer com que as caixas de seleção e o objeto sejam redesenhadas, e não apenas definir o valor nas propriedades do objeto. E escrevemos os métodos que definam os sinalizadores e as cores de fundo do objeto:

public:
//--- Set the element (1) width and (2) height,
   virtual bool      SetWidth(const int width)                       { return CGCnvElement::SetWidth(width>this.m_check_w   ? width  : this.m_check_w);     }
   virtual bool      SetHeight(const int height)                     { return CGCnvElement::SetHeight(height>this.m_check_h ? height : this.m_check_h);     }
   
//--- (1) Set and (2) return the element checkbox location angle (alignment type)
   void              SetCheckAlign(const ENUM_ANCHOR_POINT anchor)   { this.SetProperty(CANV_ELEMENT_PROP_CHECK_ALIGN,anchor);                              }
   ENUM_ANCHOR_POINT CheckAlign(void)                          const { return (ENUM_ANCHOR_POINT)this.GetProperty(CANV_ELEMENT_PROP_CHECK_ALIGN);           }
   
//--- (1) Set and (2) return the checkbox status
   void              SetChecked(const bool flag)
                       {
                        this.SetProperty(CANV_ELEMENT_PROP_CHECKED,flag);
                        if((bool)this.CheckState()!=flag)
                           this.SetCheckState((ENUM_CANV_ELEMENT_CHEK_STATE)flag);
                       }
   bool              Checked(void)                             const { return (bool)this.GetProperty(CANV_ELEMENT_PROP_CHECKED);                            }
   
//--- (1) Set and (2) return the control status
   void              SetCheckState(const ENUM_CANV_ELEMENT_CHEK_STATE state)
                       {
                        this.SetProperty(CANV_ELEMENT_PROP_CHECK_STATE,state);
                        if((bool)state!=this.Checked())
                           this.SetChecked((bool)state);
                       }
   ENUM_CANV_ELEMENT_CHEK_STATE CheckState(void)               const { return (ENUM_CANV_ELEMENT_CHEK_STATE)this.GetProperty(CANV_ELEMENT_PROP_CHECK_STATE);}
   
//--- (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)           { this.SetProperty(CANV_ELEMENT_PROP_CHECK_BACKGROUND_COLOR,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 control checkbox frame color
   void              SetCheckBorderColor(const color clr)               { this.SetProperty(CANV_ELEMENT_PROP_CHECK_FORE_COLOR,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 control verification checkbox color
   void              SetCheckFlagColor(const color clr)                 { this.SetProperty(CANV_ELEMENT_PROP_CHECK_FLAG_COLOR,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);    }
   
//--- Redraw the object
   virtual void      Redraw(bool redraw);

//--- Constructor


No método de redesenho do objeto, renomeamos o método que define a cor de fundo:

//+------------------------------------------------------------------+
//| Redraw the object                                                |
//+------------------------------------------------------------------+
void CCheckBox::Redraw(bool redraw)
  {
//--- Fill the object with the background color having full transparency
   this.Erase(this.BackgroundColor(),0,true);
//--- Set corrected text coordinates relative to the checkbox
   this.SetCorrectTextCoords();
//--- Draw the text and checkbox within the set coordinates of the object and the binding point, and update the object 
   this.Text(this.m_text_x,this.m_text_y,this.Text(),this.ForeColor(),this.ForeColorOpacity(),this.TextAnchor());
   this.ShowControlFlag(this.CheckState());
   this.Update(redraw);
  }
//+------------------------------------------------------------------+


No método SetCorrectTextCoords(), em vez de chamar o método remoto SetTextCoords, chamamos o método da classe pai SetTextParamsByAlign:

//+------------------------------------------------------------------+
//| Set valid text coordinates depending on                          |
//| text alignment and checkbox                                      |
//+------------------------------------------------------------------+
void CCheckBox::SetCorrectTextCoords(void)
  {
//--- Set checkbox and text coordinates depending on their alignment method
   this.SetCheckFlagCoords(this.m_check_x,this.m_check_y);
   this.SetTextParamsByAlign(this.m_text_x,this.m_text_y);
//--- Get the text size
   int text_w=0, text_h=0;
   this.TextSize(this.Text(),text_w,text_h);
//--- Depending on the checkbox location within the object boundaries
//--- ...
//--- ...


Vamos alterar o método que desenha a caixa de seleção. Esta caixa de seleção não deve ser desenhada em um fundo transparente, como era antes, mas no fundo de um retângulo preenchido com cor, emoldurado. O sinal de verificação desenhado dentro desta caixa é um pouco estreito, então desenhamos não uma, mas três linhas, cada uma das quais é um pixel mais alta no centro do que a anterior. Claro, isso é incorreto do ponto de vista da escalabilidade da caixa de seleção, mas faremos o cálculo relativo das coordenadas da linha quebrada desenhando a caixa de seleção mais tarde:

//+------------------------------------------------------------------+
//| Display the checkbox for the specified state                     |
//+------------------------------------------------------------------+
void CCheckBox::ShowControlFlag(const ENUM_CANV_ELEMENT_CHEK_STATE state)
  {
//--- Draw a filled rectangle of the selection checkbox area
   this.DrawRectangleFill(this.m_check_x,this.m_check_y,this.m_check_x+this.CheckWidth(),this.m_check_y+this.CheckHeight(),this.CheckBackgroundColor(),this.CheckBackgroundColorOpacity());
//--- Draw the rectangle of checkbox boundaries
   this.DrawRectangle(this.m_check_x,this.m_check_y,this.m_check_x+this.CheckWidth(),this.m_check_y+this.CheckHeight(),this.CheckBorderColor(),this.CheckBorderColorOpacity());
//--- Create X and Y coordinate arrays for drawing a polyline
   int array_x[]={m_check_x+2,m_check_x+m_check_w/2-1,m_check_x+m_check_w-2};
   int array_y[]={m_check_y+m_check_h/2,m_check_y+m_check_h-3,m_check_y+3};
//--- Depending on the checkbox status passed to the method
   switch(state)
     {
      //--- Checked box
      case CANV_ELEMENT_CHEK_STATE_CHECKED :
        //--- Draw a polyline in the form of a checkmark inside the checkbox boundaries
        this.DrawPolylineAA(array_x,array_y,this.CheckFlagColor(),this.CheckFlagColorOpacity());
        array_y[1]=array_y[1]-1;
        this.DrawPolylineAA(array_x,array_y,this.CheckFlagColor(),this.CheckFlagColorOpacity());
        array_y[1]=array_y[1]-1;
        this.DrawPolylineAA(array_x,array_y,this.CheckFlagColor(),this.CheckFlagColorOpacity());
        break;
      //--- Undefined state
      case CANV_ELEMENT_CHEK_STATE_INDETERMINATE :
        //--- Draw a filled rectangle inside the checkbox boundaries
        this.DrawRectangleFill(this.m_check_x+3,this.m_check_y+3,this.m_check_x+this.m_check_w-3,this.m_check_y+this.m_check_h-3,this.CheckFlagColor(),this.CheckFlagColorOpacity());
        break;
      //--- Unchecked checkbox
      default:
        break;
     }
  }
//+------------------------------------------------------------------+


Método que define automaticamente a largura e a altura de um elemento agora é do tipo bool e retorna o resultado:

//+------------------------------------------------------------------+
//| Set the element width and height automatically                   |
//+------------------------------------------------------------------+
bool CCheckBox::AutoSetWH(void)
  {
//--- Define the variables for receiving the label width and height
   int w=0, h=0;
//--- Get the width and height depending on the object text
   CGCnvElement::TextSize(this.Text()!="" && this.Text()!=NULL ? this.Text() : " ",w,h);
//--- Add the Margin values of the object on the left and right to the resulting width, as well as the checkbox size
   w+=(this.MarginLeft()+this.MarginRight()+this.CheckWidth());
//--- If the width is equal to the size of the checkbox, set it to three pixels + checkbox size
   if(w==this.CheckWidth())
      w=this.CheckWidth()+3;
//--- Add the Margin values of the object on the top and bottom to the resulting height
   h+=(this.MarginTop()+this.MarginBottom());
//--- If failed to get the height, set it as "font size" * ratio
   if(h==0)
      h=(int)ceil(FontSize()*1.625);
//--- If the height is ultimately less than the size of the checkbox, set the height equal to the height of the checkbox
   if(h<this.CheckHeight())
      h=this.CheckHeight();
//--- Set the object width and height from the received values and write the result to res
   bool res=true;
   res &=this.SetWidth(w);
   res &=this.SetHeight(h);
//--- Return the result of changing the width and height
   return res;
  }
//+------------------------------------------------------------------+

Aqui é exatamente o mesmo que no método acima da classe do objeto de rótulo de texto. Declaramos uma variável, escrevemos o resultado do redimensionamento nela e a retornamos.


Objeto WinForms RadioButton

O objeto será o sucessor do objeto CheckBox WinForms, pois praticamente repete sua funcionalidade e organização interna.

No diretório de biblioteca \MQL5\Include\DoEasy\Objects\Graph\WForms\Common Controls\, criamos um novo arquivo Label.mqh da classe CLabel.

A classe deve ser herdada da classe CCheckBox, e seu arquivo deve estar anexo ao arquivo da classe que está sendo criada:

//+------------------------------------------------------------------+
//|                                                  RadioButton.mqh |
//|                                  Copyright 2022, MetaQuotes Ltd. |
//|                             https://mql5.com/en/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2022, MetaQuotes Ltd."
#property link      "https://mql5.com/en/users/artmedia70"
#property version   "1.00"
#property strict    // Necessary for mql4
//+------------------------------------------------------------------+
//| Include files                                                    |
//+------------------------------------------------------------------+
#include "CheckBox.mqh"
//+------------------------------------------------------------------+
//| CheckBox object class of the WForms controls                     |
//+------------------------------------------------------------------+
class CRadioButton : public CCheckBox
  {
  }


Na seção protected da classe, vamos declarar um método virtual que exibe a caixa de seleçãoe na seção public, um construtor paramétrico:

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

public:

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

O método que exibe a caixa de seleção substitui o método da classe pai, pois aqui a caixa de seleção é redonda, ao contrário do quadrado do objeto CheckBox.

Vamos ver esses métodos.

Construtor paramétrico:

//+------------------------------------------------------------------+
//| Constructor                                                      |
//+------------------------------------------------------------------+
CRadioButton::CRadioButton(const long chart_id,
                           const int subwindow,
                           const string name,
                           const int x,
                           const int y,
                           const int w,
                           const int h) : CCheckBox(chart_id,subwindow,name,x,y,w,h)
  {
   CGBaseObj::SetTypeElement(GRAPH_ELEMENT_TYPE_WF_RADIOBUTTON);
   CGCnvElement::SetProperty(CANV_ELEMENT_PROP_TYPE,GRAPH_ELEMENT_TYPE_WF_RADIOBUTTON);
   this.Redraw(false);
  }
//+------------------------------------------------------------------+

Aqui nós apenas determinamos o tipo do objeto WinForms e redesenhamos o arquivo. Todo o resto é criado e definido no construtor da classe pai.

Método que exibe a caixa de seleção para o estado especificado:

//+------------------------------------------------------------------+
//| Display the checkbox for the specified state                     |
//+------------------------------------------------------------------+
void CRadioButton::ShowControlFlag(const ENUM_CANV_ELEMENT_CHEK_STATE state)
  {
//--- Draw the filled circle of the selection checkbox area
   this.DrawEllipseFill(this.m_check_x,this.m_check_y,this.m_check_x+this.CheckWidth(),this.m_check_y+this.CheckHeight(),this.CheckBackgroundColor(),this.CheckBackgroundColorOpacity());
//--- Draw the circle within the checkbox borders
   DrawEllipseAA(this.m_check_x,this.m_check_y,this.m_check_x+this.CheckWidth(),this.m_check_y+this.CheckHeight(),this.CheckBorderColor(),this.CheckBorderColorOpacity());
//--- Depending on the checkbox status passed to the method
   switch(state)
     {
      //--- Checked box
      case CANV_ELEMENT_CHEK_STATE_CHECKED :
        //--- Draw a filled rectangle inside the checkbox borders
        DrawEllipseFill(this.m_check_x+3,this.m_check_y+3,this.m_check_x+this.CheckWidth()-3,this.m_check_y+this.CheckHeight()-3,this.CheckFlagColor(),this.CheckFlagColorOpacity());
        break;
      //--- Undefined state
      //--- Unchecked checkbox
      default:
        break;
     }
  }
//+------------------------------------------------------------------+

Por analogia com a classe pai, aqui, primeiro, um círculo preenchido com a cor de fundo é desenhado, depois sua borda é desenhada e, dependendo do estado passado para o método do sinalizador, dentro do círculo desenhado, outro é desenhado - de um diâmetro menor, ou nada é desenhado.

E isso é tudo o que é necessário para criar este objeto - todo o resto é implementado nas classes pai.


Objeto WinForms Button

O objeto botão, basicamente, é um rótulo de texto. O rótulo de texto tem a capacidade de desenhar as bordas do objeto, é possível posicionar o texto dentro das bordas do elemento de controle. O rótulo é desenhado apenas em um fundo transparente, enquanto o botão é desenhado em um fundo preenchido. Portanto, o objeto de botão será herdado do objeto de rótulo de texto.

No diretório de biblioteca \MQL5\Include\DoEasy\Objects\Graph\WForms\Common Controls\, criamos um novo arquivo Label.mqh da classe CLabel.

A classe deve ser herdada da classe CLabel, e seu arquivo deve ser incluído no gerado:

//+------------------------------------------------------------------+
//|                                                       Button.mqh |
//|                                  Copyright 2022, MetaQuotes Ltd. |
//|                             https://mql5.com/en/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2022, MetaQuotes Ltd."
#property link      "https://mql5.com/en/users/artmedia70"
#property version   "1.00"
#property strict    // Necessary for mql4
//+------------------------------------------------------------------+
//| Include files                                                    |
//+------------------------------------------------------------------+
#include "Label.mqh"
//+------------------------------------------------------------------+
//| Label object class of WForms controls                            |
//+------------------------------------------------------------------+
class CButton : public CLabel
  {
  }


Na seção privada da classe, vamos declarar variáveis para armazenar as coordenadas do rótulo no botão, e na seção protegida, um método virtual para definir automaticamente o tamanho do controle:

class CButton : public CLabel
  {
private:
   int               m_text_x;                                       // Text X coordinate
   int               m_text_y;                                       // Text Y coordinate
protected:
//--- Set the element width and height automatically
   virtual bool      AutoSetWH(void);

public:


Na seção pública, vamos declarar um método virtual para redesenhar o objeto e escrever métodos para definir e retornar o modo de redimensionamento automático do objeto em função do tamanho do rôtulo:

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

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


No construtor paramétrico, especificamos o tipo de controle, definimos as propriedades do objeto para os valores padrão e chamamos o método de redesenho:

//+------------------------------------------------------------------+
//| 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.SetTextAlign(ANCHOR_CENTER);
   this.SetMarginAll(3);
   this.SetWidthInit(this.Width());
   this.SetHeightInit(this.Height());
   this.SetCoordXInit(x);
   this.SetCoordYInit(y);
   this.Redraw(false);
  }
//+------------------------------------------------------------------+


Método que redesenha o objeto:

//+------------------------------------------------------------------+
//| Redraw the object                                                |
//+------------------------------------------------------------------+
void CButton::Redraw(bool redraw)
  {
//--- Fill the object with the background color featuring the default transparency
   this.Erase(this.BackgroundColor(),CLR_DEF_CONTROL_STD_OPACITY,true);
//--- Declare the variables for X and Y coordinates and set their values depending on the text alignment
   int x=0,y=0;
   CLabel::SetTextParamsByAlign(x,y);
//--- Draw the text within the set coordinates of the object and the binding point of the text, and update the object 
   this.Text(x,y,this.Text(),this.ForeColor(),this.ForeColorOpacity(),this.TextAnchor());
   this.Update(redraw);
  }
//+------------------------------------------------------------------+

Primeiro, os limites do objeto são preenchidos com a cor de fundo, depois um rótulo é exibido nas coordenadas calculadas, dependendo do alinhamento do texto, e o objeto é atualizado.

Método que define automaticamente a largura e a altura do elemento:

//+------------------------------------------------------------------+
//| Set the element width and height automatically                   |
//+------------------------------------------------------------------+
bool CButton::AutoSetWH(void)
  {
//--- Define the variables for receiving the label width and height
   int w=0, h=0;
//--- Get the width and height depending on the object text
   CGCnvElement::TextSize(this.Text()!="" && this.Text()!=NULL ? this.Text() : " ",w,h);
//--- Add the Margin values of the object on the left and right to the resulting width
   w+=(this.MarginLeft()+this.MarginRight());
//--- If failed to get the width, set it to three pixels
   if(w==0)
      w=3;
//--- Add the Margin values of the object on the top and bottom to the resulting height
   h+=(this.MarginTop()+this.MarginBottom());
//--- If failed to get the height, set it as "font size" * ratio
   if(h==0)
      h=(int)ceil(FontSize()*1.625);
//--- Set the object width and height from the received values and write the result to res
   bool res=true;
//--- In case of the auto resize mode, increase only
   if(this.AutoSizeMode()==CANV_ELEMENT_AUTO_SIZE_MODE_GROW)
     {
      if(w>this.Width())
         res &=this.SetWidth(w);
      if(h>this.Height())
         res &=this.SetHeight(h);
     }
//--- In case of the auto resize mode, increase and decrease
   else
     {
      if(w!=this.Width())
         res &=this.SetWidth(w);
      if(h!=this.Height())
         res &=this.SetHeight(h);
     }
//--- Return the result of changing the width and height
   return res;
  }
//+------------------------------------------------------------------+

O método é quase idêntico aos mesmos métodos das classes pai que consideramos acima. Mas aqui há uma nuance: as dimensões mudam de acordo com o modo de redimensionamento estabelecido. Ou seja, se o modo for apenas aumentar, o tamanho do objeto só aumentará se o texto o ultrapassar suas bordas. Se houver um modo de aumento e diminuição de tamanhos, todo o objeto será ajustado ao tamanho do texto dentro dele.

Claro, isso não é tudo o que será da possível funcionalidade do objeto botão, mas todo o resto será adicionado em artigos posteriores em paralelo com o desenvolvimento de outros objetos WinForms.


Os objetos-contêineres, para criar objetos encaixados dentro deles, devem estar cientes da existência dos controles recém-criados. Para fazer isso, devemos vincular os arquivos de novas classes aos arquivos de classe de objetos-contêineres.

A classe pai para todos os objetos-contêineres é a classe do objeto de contêiner base implementado no arquivo \MQL5\Include\DoEasy\Objects\Graph\WForms\Containers\Container.mqh.

Vamos incluir arquivos de novos controles nele. Em vez do arquivo CheckBox.mqh, incluiremos o arquivo do objeto RadioButton, pois ele é o sucessor do elemento CheckBox e, portanto, ambos estarão visíveis na classe:

//+------------------------------------------------------------------+
//|                                                    Container.mqh |
//|                                  Copyright 2022, MetaQuotes Ltd. |
//|                             https://mql5.com/en/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2022, MetaQuotes Ltd."
#property link      "https://mql5.com/en/users/artmedia70"
#property version   "1.00"
#property strict    // Necessary for mql4
//+------------------------------------------------------------------+
//| Include files                                                    |
//+------------------------------------------------------------------+
#include "..\..\WForms\WinFormBase.mqh"
#include "..\..\WForms\Common Controls\RadioButton.mqh"
#include "..\..\WForms\Common Controls\Button.mqh"
//+------------------------------------------------------------------+


Como o método de criação de um novo objeto gráfico é diferente para cada classe herdada, aqui apenas declararemos um método virtual e excluiremos sua implementação. O método retornará NULL:

//+------------------------------------------------------------------+
//| Class of the base container object of WForms controls            |
//+------------------------------------------------------------------+
class CContainer : public CWinFormBase
  {
private:
//--- Create a new graphical object
   virtual CGCnvElement *CreateNewGObject(const ENUM_GRAPH_ELEMENT_TYPE type,
                                          const int element_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,
                                          const bool activity) { return NULL; }

//--- Calculate Dock objects' binding coordinates


Renomeamos métodos públicos FrameWidth para métodos BorderSize:

public:
//--- Return the size and coordinates of the working area
   int               GetWidthWorkspace(void)       const
                       {
                        return this.Width()-::fmax(this.BorderSizeLeft(),this.PaddingLeft())-::fmax(this.BorderSizeRight(),this.PaddingRight());
                       }
   int               GetHeightWorkspace(void)      const
                       {
                        return this.Height()-::fmax(this.BorderSizeTop(),this.PaddingTop())-::fmax(this.BorderSizeBottom(),this.PaddingBottom());
                       }
   int               GetCoordXWorkspace(void)      const
                       {
                        return this.CoordX()+::fmax(this.BorderSizeLeft(),this.PaddingLeft());
                       }
   int               GetCoordYWorkspace(void)      const
                       {
                        return this.CoordY()+::fmax(this.BorderSizeTop(),this.PaddingTop());
                       }
   int               GetRightEdgeWorkspace(void)   const
                       {
                        return this.RightEdge()-::fmax(this.BorderSizeRight(),this.PaddingRight());
                       }
   int               GetBottomEdgeWorkspace(void)  const
                       {
                        return this.BottomEdge()-::fmax(this.BorderSizeBottom(),this.PaddingBottom());
                       }

//--- Return the list of bound WinForms objects with (1) any and (2) specified WinForms object type (from the base one and higher)

...

//--- 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      SetFrameWidthLeft(const uint value)
                       {
                        this.SetBorderSizeLeft(value);
                        if(this.PaddingLeft()<this.BorderSizeLeft())
                           this.SetPaddingLeft(this.BorderSizeLeft());
                       }
   virtual void      SetFrameWidthTop(const uint value)
                       {
                        this.SetBorderSizeTop(value);
                        if(this.PaddingTop()<this.BorderSizeTop())
                           this.SetPaddingTop(this.BorderSizeTop());
                       }
   virtual void      SetFrameWidthRight(const uint value)
                       {
                        this.SetBorderSizeRight(value);
                        if(this.PaddingRight()<this.BorderSizeRight())
                           this.SetPaddingRight(this.BorderSizeRight());
                       }
   virtual void      SetFrameWidthBottom(const uint value)
                       {
                        this.SetBorderSizeBottom(value);
                        if(this.PaddingBottom()<this.BorderSizeBottom())
                           this.SetPaddingBottom(this.BorderSizeBottom());
                       }
   virtual void      SetFrameWidthAll(const uint value)
                       {
                        this.SetBorderSizeLeft(value); this.SetBorderSizeTop(value); this.SetBorderSizeRight(value); this.SetBorderSizeBottom(value);
                       }

//--- Constructors


Do método de criação de um novo elemento, removemos a transferência para o método do ponteiro para o objeto principal:

//--- Create a new attached element
   virtual bool      CreateNewElement(const ENUM_GRAPH_ELEMENT_TYPE element_type,
                                      CGCnvElement *main,
                                      const int x,
                                      const int y,
                                      const int w,
                                      const int h,
                                      const color colour,
                                      const uchar opacity,
                                      const bool activity,
                                      const bool redraw);


Na implementação do método , escrevemos a criação de todos os controles conhecidos:

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

//--- 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());
        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);
        obj.SetBorderColor(obj.ForeColor());
        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());
        obj.SetBackgroundColor(colour==clrNONE ? CLR_DEF_CONTROL_STD_BACK_COLOR : colour);
        obj.SetBorderColor(obj.ForeColor());
        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;
  }
//+------------------------------------------------------------------+


O método que ajusta o tamanho do elemento ao seu conteúdo foi modificado, mas ainda não funciona corretamente, então aqui vamos apenas anexar sua implementação atual sem nenhuma explicação, exceto aquelas já nos comentários ao código do método:

//+------------------------------------------------------------------+
//| Adjust the element size to fit its content                       |
//+------------------------------------------------------------------+
bool CContainer::AutoSizeProcess(const bool redraw)
  {
//--- Get the list of bound objects with WinForms type basic and higher
   CArrayObj *list=this.GetListWinFormsObj();
   int maxcX=0;
   int maxcY=0;
//--- Calculate the maximum coordinate of the right and bottom edge from all bound objects
   for(int i=0;i<list.Total();i++)
     {
      CWinFormBase *obj=list.At(i);
      if(obj==NULL)
         continue;
      if(obj.RightEdge()>maxcX)
         maxcX=obj.RightEdge();
      if(obj.BottomEdge()>maxcY)
         maxcY=obj.BottomEdge();
     }
//--- Calculate the required width and height of the panel after adjusting its size to the content
   int w=maxcX-this.CoordX();
   int h=maxcY-this.CoordY();
//--- Calculate the number of pixels, by which we need to resize the container in width and height
   int excess_x=w-this.GetWidthWorkspace()-this.BorderSizeRight()-1;
   int excess_y=h-this.GetHeightWorkspace()-this.BorderSizeBottom()-1;

//--- If failed to change the container size, return 'true'
   if(excess_x==0 && excess_y==0)
      return true;

//--- Return the result of resizing the container
   return
     (
      //--- In case of size increase only
      this.AutoSizeMode()==CANV_ELEMENT_AUTO_SIZE_MODE_GROW ? 
      this.Resize(this.Width()+(excess_x>0  ? excess_x : 0),this.Height()+(excess_y>0  ? excess_y : 0),redraw) :
      //--- if both increase and decrease
      this.Resize(this.Width()+(excess_x!=0 ? excess_x : 0),this.Height()+(excess_y!=0 ? excess_y : 0),redraw)
     );
  }
//+------------------------------------------------------------------+

Outras pequenas alterações na classe, que são resultado de experimentos e não estão relacionadas ao tema deste artigo, não serão consideradas aqui. Voltaremos a elas quando tudo funcionar corretamente.

A classe do objeto-contêiner GroupBox no arquivo \MQL5\Include\DoEasy\Objects\Graph\WForms\Containers\GroupBox.mqh também foi ligeiramente alterada.

As chamadas para métodos renomeados foram ajustadas:

//--- Set a frame style
   virtual void      SetBorderStyle(const ENUM_FRAME_STYLE style)
                       {
                        if((this.BorderSizeTop()<2 || this.BorderSizeBottom()<2 || this.BorderSizeLeft()<2 || this.BorderSizeRight()<2) && 
                            style>FRAME_STYLE_FLAT)
                           this.SetBorderSizeAll(2);
                        this.SetProperty(CANV_ELEMENT_PROP_BORDER_STYLE,style);
                       }
   
//--- Constructors

...

//+------------------------------------------------------------------+
//| Initialize the variables                                         |
//+------------------------------------------------------------------+
void CGroupBox::Initialize(void)
  {
//--- Clear all object lists and set sorted list flags for them
   this.m_list_elements.Clear();
   this.m_list_elements.Sort();
   this.m_list_tmp.Clear();
   this.m_list_tmp.Sort();
//--- GroupBox has no shadow object
   this.m_shadow_obj=NULL;
   this.m_shadow=false;
//--- The width of the object frame on each side is 1 pixel by default
   this.SetBorderSize(1,1,1,1);
//--- The object does not have a gradient filling (neither vertical, nor horizontal)
   this.m_gradient_v=false;
   this.m_gradient_c=false;
//--- Reset all "working" flags and variables
   this.m_mouse_state_flags=0;
   this.m_offset_x=0;
   this.m_offset_y=0;
   CGCnvElement::SetInteraction(false);
//--- 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);
   this.SetOpacity(0);
   this.SetBorderColor(CLR_DEF_FRAME_GBOX_COLOR);
//--- Set the default color and text opacity, as well as the absence of the object frame
   this.SetForeColor(CLR_DEF_FORE_COLOR);
   this.SetForeColorOpacity(CLR_DEF_FORE_COLOR_OPACITY);
   this.SetBorderStyle(FRAME_STYLE_SIMPLE);
//--- Set the default text parameters
   this.SetFont(DEF_FONT,DEF_FONT_SIZE);
   this.SetText("GroupBox");
   this.SetTextAnchor(FRAME_ANCHOR_LEFT_TOP);
   this.SetTextAlign(ANCHOR_LEFT_UPPER);
//--- Set the default object parameters
   this.SetAutoSize(false,false);
   this.SetMarginAll(3);
   this.SetPaddingAll(3);
   this.SetEnabled(true);
   this.SetVisible(true,false);
  }
//+------------------------------------------------------------------+

...

//+------------------------------------------------------------------+
//| Draw the frame                                                   |
//+------------------------------------------------------------------+
void CGroupBox::DrawFrame(void)
  {
//--- Get half of the text height
   int w=0;
   int h=0;
   this.TextSize(Text(),w,h);
   int height=this.Height()-h/2;
//--- Depending on the frame style, draw its necessary type
   switch(this.BorderStyle())
     {
      case FRAME_STYLE_FLAT :
        this.DrawFrameFlat(0,h/2,this.Width(),height,this.BorderSizeTop(),this.BorderSizeBottom(),this.BorderSizeLeft(),this.BorderSizeRight(),this.BorderColor(),this.ForeColorOpacity());
        break;
      case FRAME_STYLE_BEVEL :
        this.DrawFrameBevel(0,h/2,this.Width(),height,this.BorderSizeTop(),this.BorderSizeBottom(),this.BorderSizeLeft(),this.BorderSizeRight(),this.BorderColor(),this.ForeColorOpacity());
        break;
      case FRAME_STYLE_STAMP :
        this.DrawFrameStamp(0,h/2,this.Width(),height,this.BorderSizeTop(),this.BorderSizeBottom(),this.BorderSizeLeft(),this.BorderSizeRight(),this.BorderColor(),this.ForeColorOpacity());
        break;
      //--- FRAME_STYLE_SIMPLE
      default:
        this.DrawFrameSimple(0,h/2,this.Width(),height,this.BorderSizeTop(),this.BorderSizeBottom(),this.BorderSizeLeft(),this.BorderSizeRight(),this.BorderColor(),this.ForeColorOpacity());
        break;
     }
//--- If the text set for an object is not an empty string, erase the frame area where a text should be located using the transparent color
   if(this.Text()!="")
      this.DrawRectangleFill(5,h/2-1,w+7,h/2+this.BorderSizeTop()+1,CLR_CANV_NULL,0);
  }
//+------------------------------------------------------------------+


O método virtual que cria um novo objeto gráfico especifica a criação de todos os elementos atualmente conhecidos:

//+------------------------------------------------------------------+
//| Create a new graphical object                                    |
//+------------------------------------------------------------------+
CGCnvElement *CGroupBox::CreateNewGObject(const ENUM_GRAPH_ELEMENT_TYPE type,
                                          const int obj_num,
                                          const string obj_name,
                                          const int x,
                                          const int y,
                                          const int w,
                                          const int h,
                                          const color colour,
                                          const uchar opacity,
                                          const bool movable,
                                          const bool activity)
  {
   string name=this.CreateNameDependentObject(obj_name);
   CGCnvElement *element=NULL;
   switch(type)
     {
      case GRAPH_ELEMENT_TYPE_ELEMENT :
         element=new CGCnvElement(type,this.ID(),obj_num,this.ChartID(),this.SubWindow(),name,x,y,w,h,colour,opacity,movable,activity);
        break;
      case GRAPH_ELEMENT_TYPE_FORM :
         element=new CForm(this.ChartID(),this.SubWindow(),name,x,y,w,h);
        break;
      case GRAPH_ELEMENT_TYPE_WF_CONTAINER :
         element=new CContainer(this.ChartID(),this.SubWindow(),name,x,y,w,h);
        break;
      case GRAPH_ELEMENT_TYPE_WF_GROUPBOX :
         element=new CGroupBox(this.ChartID(),this.SubWindow(),name,x,y,w,h);
        break;
      case GRAPH_ELEMENT_TYPE_WF_PANEL :
         element=new CPanel(this.ChartID(),this.SubWindow(),name,x,y,w,h);
        break;
      case GRAPH_ELEMENT_TYPE_WF_LABEL :
         element=new CLabel(this.ChartID(),this.SubWindow(),name,x,y,w,h);
        break;
      case GRAPH_ELEMENT_TYPE_WF_CHECKBOX :
         element=new CCheckBox(this.ChartID(),this.SubWindow(),name,x,y,w,h);
        break;
      case GRAPH_ELEMENT_TYPE_WF_RADIOBUTTON :
         element=new CRadioButton(this.ChartID(),this.SubWindow(),name,x,y,w,h);
        break;
      case GRAPH_ELEMENT_TYPE_WF_BUTTON :
         element=new CButton(this.ChartID(),this.SubWindow(),name,x,y,w,h);
        break;
      default:
        break;
     }
   if(element==NULL)
      ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_FAILED_CREATE_ELM_OBJ),": ",name);
   return element;
  }
//+------------------------------------------------------------------+


A classe contêiner do objeto "Painel" no arquivo \MQL5\Include\DoEasy\Objects\Graph\WForms\Containers\Panel.mqh também precisa de algumas modificações.

Nos métodos de configuração de espaços, precisamos transferir o cálculo e a configuração do deslocamento do substrato após definir suas coordenadas e tamanho:

//--- Set the gap (1) to the left, (2) at the top, (3) to the right, (4) at the bottom and (5) on all sides inside the control
   virtual void      SetPaddingLeft(const uint value)
                       {
                        CWinFormBase::SetPaddingLeft(value);
                        if(this.m_underlay!=NULL)
                          {
                           //--- Set the X coordinate and the underlay width
                           this.SetCoordXUnderlay(this.CoordX()+this.PaddingLeft());
                           this.SetWidthUnderlay(this.Width()-this.PaddingLeft()-this.PaddingRight());
                           //--- Set the underlay shift along the X axis
                           this.m_underlay.SetCoordXRelative(this.m_underlay.CoordX()-this.CoordX());
                          }
                       }
   virtual void      SetPaddingTop(const uint value)
                       {
                        CWinFormBase::SetPaddingTop(value);
                        if(this.m_underlay!=NULL)
                          {
                           //--- Set the Y coordinate and underlay height
                           this.SetCoordYUnderlay(this.CoordY()+this.PaddingTop());
                           this.SetHeightUnderlay(this.Height()-this.PaddingTop()-this.PaddingBottom());
                           //--- Set the underlay shift along the Y axis
                           this.m_underlay.SetCoordYRelative(this.m_underlay.CoordY()-this.CoordY());
                          }
                       }

Anteriormente, o cálculo dos deslocamentos era realizado antes do cálculo das coordenadas e dimensões, o que, após as alterações, causava um erro no cálculo dos deslocamentos.


No método de criação de um novo objeto gráfico, adicionaremos também a criação de todos os elementos conhecidos:

//+------------------------------------------------------------------+
//| Create a new graphical object                                    |
//+------------------------------------------------------------------+
CGCnvElement *CPanel::CreateNewGObject(const ENUM_GRAPH_ELEMENT_TYPE type,
                                       const int obj_num,
                                       const string obj_name,
                                       const int x,
                                       const int y,
                                       const int w,
                                       const int h,
                                       const color colour,
                                       const uchar opacity,
                                       const bool movable,
                                       const bool activity)
  {
   string name=this.CreateNameDependentObject(obj_name);
   CGCnvElement *element=NULL;
   switch(type)
     {
      case GRAPH_ELEMENT_TYPE_ELEMENT :
         element=new CGCnvElement(type,this.ID(),obj_num,this.ChartID(),this.SubWindow(),name,x,y,w,h,colour,opacity,movable,activity);
        break;
      case GRAPH_ELEMENT_TYPE_FORM :
         element=new CForm(this.ChartID(),this.SubWindow(),name,x,y,w,h);
        break;
      case GRAPH_ELEMENT_TYPE_WF_CONTAINER :
         element=new CContainer(this.ChartID(),this.SubWindow(),name,x,y,w,h);
        break;
      case GRAPH_ELEMENT_TYPE_WF_GROUPBOX :
         element=new CGroupBox(this.ChartID(),this.SubWindow(),name,x,y,w,h);
        break;
      case GRAPH_ELEMENT_TYPE_WF_PANEL :
         element=new CPanel(this.ChartID(),this.SubWindow(),name,x,y,w,h);
        break;
      case GRAPH_ELEMENT_TYPE_WF_LABEL :
         element=new CLabel(this.ChartID(),this.SubWindow(),name,x,y,w,h);
        break;
      case GRAPH_ELEMENT_TYPE_WF_CHECKBOX :
         element=new CCheckBox(this.ChartID(),this.SubWindow(),name,x,y,w,h);
        break;
      case GRAPH_ELEMENT_TYPE_WF_RADIOBUTTON :
         element=new CRadioButton(this.ChartID(),this.SubWindow(),name,x,y,w,h);
        break;
      case GRAPH_ELEMENT_TYPE_WF_BUTTON :
         element=new CButton(this.ChartID(),this.SubWindow(),name,x,y,w,h);
        break;
      default:
        break;
     }
   if(element==NULL)
      ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_FAILED_CREATE_ELM_OBJ),": ",name);
   return element;
  }
//+------------------------------------------------------------------+


No método que define todos os parâmetros do substrato, também deslocaremos o cálculo do deslocamento abaixo dos cálculos das coordenadas:

//+------------------------------------------------------------------+
//| Set all underlay parameters                                      |
//+------------------------------------------------------------------+
bool CPanel::SetUnderlayParams(void)
  {
//--- Set the object type
   this.m_underlay.SetTypeElement(GRAPH_ELEMENT_TYPE_WF_UNDERLAY);
//--- Set the underlay coordinates and size
   bool res=true;
   res &=this.SetCoordXUnderlay(this.CoordX()+this.PaddingLeft());
   res &=this.SetCoordYUnderlay(this.CoordY()+this.PaddingTop());
   res &=this.SetWidthUnderlay(this.Width()-this.PaddingLeft()-this.PaddingRight());
   res &=this.SetHeightUnderlay(this.Height()-this.PaddingTop()-this.PaddingBottom());
//--- Set the underlay shift values to the variables by X and Y axes
   this.m_underlay.SetCoordXRelative(this.m_underlay.CoordX()-this.CoordX());
   this.m_underlay.SetCoordYRelative(this.m_underlay.CoordY()-this.CoordY());
   return res;
  }
//+------------------------------------------------------------------+


No método Move, alteramos o cálculo dos valores de deslocamento do objeto:

//--- Shift all bound objects
   if(!this.MoveDependentObj(x+this.GetCoordXUnderlayRelative(),y+this.GetCoordYUnderlayRelative(),false))
      return false;

Agora tudo é mais fácil aqui:

//+------------------------------------------------------------------+
//| Update the coordinate elements                                   |
//+------------------------------------------------------------------+
bool CPanel::Move(const int x,const int y,const bool redraw=false)
  {
//--- Get the pointers to the base and main objects in the bound objects hierarchy, as well as the shadow object
   CGCnvElement *base=this.GetBase();
   CGCnvElement *main=this.GetMain();
   CShadowObj   *shadow=this.GetShadowObj();
//--- If the element is not movable and is a base object, leave
   if(!this.Movable() && main==NULL)
      return false;
//--- If the object has a shadow and we failed to set new coordinate values to the properties of the shadow object, return 'false'
   if(this.m_shadow && shadow!=NULL)
     {
      if(!shadow.Move(x-OUTER_AREA_SIZE+shadow.CoordXRelative(),y-OUTER_AREA_SIZE+shadow.CoordYRelative(),false))
         return false;
     }
//--- If failed to set new values into graphical object properties, return 'false'
   if(!this.SetCoordX(x) || !this.SetCoordY(y))
      return false;
//--- If failed to move the underlay, return 'false'
   if(this.m_underlay!=NULL && !this.m_underlay.Move(x+this.GetCoordXUnderlayRelative(),y+this.GetCoordYUnderlayRelative()))
      return false;
//--- Shift all bound objects
   if(!this.MoveDependentObj(x,y,false))
      return false;
   //--- If the update flag is set and this is the hierarchy main object, redraw the chart.
   if(redraw && main==NULL)
      ::ChartRedraw(this.ChartID());
   //--- Return 'true'
   return true;
  }
//+------------------------------------------------------------------+

Como agora as coordenadas relativas de todos os objetos são calculadas e as inicialmente definidas não são usadas, não há necessidade de ajustar nada aqui - simplesmente substituímos os valores X e Y passados para o método de deslocamento.


Muitas correções foram feitas na classe-coleção de elementos gráficos no arquivo \MQL5\Include\DoEasy\Collections\GraphElementsCollection.mqh, já que renomeamos alguns métodos hoje, e o acesso a eles nesta classe é ativamente utilizado. Eu acho que a simples renomeação é inútil aqui - nós cobrimos todas as mudanças similares ao longo do artigo hoje. Portanto, tais alterações podem ser encontradas de forma independente nos arquivos anexados ao artigo.

Mas o que precisa ser focado é encontrar os objetos anexados ao painel e criar uma lista de objetos de interação, que permite determinar com qual objeto o mouse deve interagir.

No método que retorna um ponteiro para a forma sob o cursor, precisamos adicionar dois blocos de código (eles são os mesmos e, após algum tempo de teste, provavelmente os moveremos para um método separado). Nesses blocos, é criada uma lista de todos os objetos anexados ao painel - sua hierarquia, e o mais recente deles é retornado como o objeto com o qual devemos interagir:

//+------------------------------------------------------------------+
//| 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)
           {
            //--- Create the list of interaction objects
            int count=form.CreateListInteractObj();
            //--- If the list has objects
            if(count>0)
              {
               //--- In the loop by the created list
               for(int j=count-1;j>WRONG_VALUE;j--)
                 {
                  //--- get the next form object
                  CForm *obj=form.GetInteractForm(j);
                  if(obj==NULL)
                     continue;
                  //--- if the mouse cursor is located above the object, write it to the pointer and break the loop
                  if(obj.MouseFormState(id,lparam,dparam,sparam)>MOUSE_FORM_STATE_OUTSIDE_FORM_WHEEL)
                    {
                     form=obj;
                     break;
                    }
                 }
              }
            //--- Return the found 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)
           {
            //--- Create the list of interaction objects
            int count=form.CreateListInteractObj();
            //--- If the list has objects
            if(count>0)
              {
               //--- In the loop by the created list
               for(int j=count-1;j>WRONG_VALUE;j--)
                 {
                  //--- get the next form object
                  CForm *obj=form.GetInteractForm(j);
                  if(obj==NULL)
                     continue;
                  //--- if the mouse cursor is located above the object, write it to the pointer and break the loop
                  if(obj.MouseFormState(id,lparam,dparam,sparam)>MOUSE_FORM_STATE_OUTSIDE_FORM_WHEEL)
                    {
                     form=obj;
                     break;
                    }
                 }
              }
            //--- Return the found 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;
  }
//+------------------------------------------------------------------+

Nos comentários sobre o código, a lógica é descrita em grande detalhe. Espero que tudo esteja claro aqui. Em qualquer caso, todas as perguntas podem ser colocadas na discussão do artigo.

No manipulador de eventos da classe CGraphElementsCollection::OnChartEvent(), no bloco do manipulador de eventos "O cursor está dentro da área ativa, a roda do mouse está rolando", escrevemos a entrada no log do tipo do elemento gráfico sobre o qual o cursor está localizado e o nome deste elemento. Assim, será possível controlar a interação do mouse com o objeto sobre o qual o cursor está localizado - se você passar o mouse sobre o elemento e rolar a roda do mouse, os dados sobre esse objeto serão exibidos no log:

            //--- 'The cursor is inside the active area, the mouse wheel is being scrolled' event handler workpiece
            if(mouse_state==MOUSE_FORM_STATE_INSIDE_ACTIVE_AREA_WHEEL)
              {
               Print(DFUN,"Mouse scroll: ",form.TypeElementDescription()," ",form.Name());
              }


Teste

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

Vamos adicionar novos parâmetros aos parâmetros de entrada do Expert Advisor que permitem definir o alinhamento da caixa de seleção e do texto, bem como parâmetros para o objeto botão:

//--- input parameters
sinput   bool                          InpMovable           =  true;                   // Panel Movable flag
sinput   ENUM_INPUT_YES_NO             InpAutoSize          =  INPUT_YES;              // Panel Autosize
sinput   ENUM_AUTO_SIZE_MODE           InpAutoSizeMode      =  AUTO_SIZE_MODE_GROW;    // Panel Autosize mode
sinput   ENUM_BORDER_STYLE             InpFrameStyle        =  BORDER_STYLE_NONE;      // Label border style
sinput   ENUM_ANCHOR_POINT             InpTextAlign         =  ANCHOR_LEFT_UPPER;      // Label text align
sinput   ENUM_INPUT_YES_NO             InpTextAutoSize      =  INPUT_YES;              // Label autosize
sinput   ENUM_ANCHOR_POINT             InpCheckAlign        =  ANCHOR_LEFT_UPPER;      // Check flag align
sinput   ENUM_ANCHOR_POINT             InpCheckTextAlign    =  ANCHOR_LEFT_UPPER;      // 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_LEFT_UPPER;      // 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
//--- global variables


No manipulador OnInit() , vamos criar todos os objetos que precisamos no painel e no container GroupBox:

//+------------------------------------------------------------------+
//| 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.SetFrameWidthAll(3);
            obj.SetBorderStyle(FRAME_STYLE_BEVEL);
            obj.SetBackgroundColor(obj.ChangeColorLightness(obj.BackgroundColor(),4*i));
            obj.SetForeColor(clrRed);
            //--- 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,pnl.BorderSizeLeft(),pnl.BorderSizeTop(),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);
               //--- 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_FRAME_COLOR);
               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 objects
         gbox=pnl.GetElement(2);
         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());
            gbox.SetForeColor(gbox.ChangeColorLightness(obj.BackgroundColor(),-1));
            //--- Create the CheckBox object
            gbox.CreateNewElement(GRAPH_ELEMENT_TYPE_WF_CHECKBOX,2,10,50,20,clrNONE,255,true,false);
            CCheckBox *cbox=gbox.GetElement(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_FRAME_COLOR);
               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(),50,20,clrNONE,255,true,false);
            CRadioButton *rbtn=gbox.GetElement(1);
            //--- 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_FRAME_COLOR);
               rbtn.SetChecked(true);
              }
            //--- Create the Button object
            gbox.CreateNewElement(GRAPH_ELEMENT_TYPE_WF_BUTTON,(int)fmax(rbtn.RightEdgeRelative(),cbox.RightEdgeRelative())+10,10,30,30,clrNONE,255,true,false);
            CButton *butt=gbox.GetElement(2);
            //--- If Button is created and the pointer to it is received
            if(butt!=NULL)
              {
               //--- Set the RadioButton 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 displayed text, frame style and color, as well as checkbox status
               butt.SetText("Button");
               butt.SetForeColor(butt.ChangeColorLightness(CLR_DEF_FORE_COLOR,2));
               butt.SetBorderStyle((ENUM_FRAME_STYLE)InpButtonFrameStyle);
               butt.SetBorderColor(butt.ChangeColorLightness(butt.BackgroundColor(),-10));
               butt.SetBorderColor(CLR_DEF_FRAME_COLOR);
              }
           }
        }
      //--- Redraw all objects according to their hierarchy
      pnl.Redraw(true);
     }
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+

A lógica do bloco de código para criação de três controles anexados ao GroupBox está comentada no código, acho que está tudo claro lá.
O tamanho do botão (objeto WinForm-Button) é feito deliberadamente de forma que o botão seja maior em altura que o texto e menor em largura. Então, com o modo de redimensionamento automático ativado, podemos ver como o botão ajusta seu tamanho em função do texto.

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


Pode-se ver que o botão ajusta corretamente seu tamanho ao texto dependendo do modo de autodimensionamento. O CheckBox e o RadioButton têm suas caixas de seleção normais, e a própria caixa de seleção do CheckBox ganhou espessura.

Ao passar o mouse sobre alguns controles, rolei a roda do mouse. As seguintes entradas foram exibidas no log:

CGraphElementsCollection::OnChartEvent: Mouse scroll: Control element "Button" TstDE109_WFPanel_Elm02_Elm02
CGraphElementsCollection::OnChartEvent: Mouse scroll: Control element "Button" TstDE109_WFPanel_Elm02_Elm02
CGraphElementsCollection::OnChartEvent: Mouse scroll: Control element "Button" TstDE109_WFPanel_Elm02_Elm02
CGraphElementsCollection::OnChartEvent: Mouse scroll: Control element "Button" TstDE109_WFPanel_Elm02_Elm02
CGraphElementsCollection::OnChartEvent: Mouse scroll: Control element "Label" TstDE109_WFPanel_Elm01_Elm00
CGraphElementsCollection::OnChartEvent: Mouse scroll: Control element "Label" TstDE109_WFPanel_Elm01_Elm00
CGraphElementsCollection::OnChartEvent: Mouse scroll: Control element "Label" TstDE109_WFPanel_Elm00_Elm00

...

CGraphElementsCollection::OnChartEvent: Mouse scroll: Control element "CheckBox" TstDE109_WFPanel_Elm02_Elm00
CGraphElementsCollection::OnChartEvent: Mouse scroll: Control element "CheckBox" TstDE109_WFPanel_Elm02_Elm00
CGraphElementsCollection::OnChartEvent: Mouse scroll: Control element "RadioButton" TstDE109_WFPanel_Elm02_Elm01
CGraphElementsCollection::OnChartEvent: Mouse scroll: Control element "RadioButton" TstDE109_WFPanel_Elm02_Elm01
CGraphElementsCollection::OnChartEvent: Mouse scroll: Control element "GroupBox" TstDE109_WFPanel_Elm02

Essas entradas indicam a escolha correta do objeto ativo para interagir com o mouse.

O que virá a seguir?

No próximo artigo, continuaremos o desenvolvimento de objetos WinForms e a funcionalidade dos já criados.

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

Voltar ao conteúdo

*Artigos desta série:

DoEasy. Controles (Parte 1): Primeiros passos
DoEasy. Controles (Parte 2): Continuamos trabalhando na classe CPanel
DoEasy. Controles (Parte 3): Criando controles vinculados
DoEasy. Controles (Parte 4): Controle "Painel", parâmetros Padding e Dock
DoEasy. Controles (Parte 5): Objeto base WinForms, controle Painel, parâmetro AutoSize
DoEasy. Controles (Parte 6): Controle "Painel", redimensionamento automático do contêiner para adequá-lo ao seu conteúdo
DoEasy. Controles (Parte 7): Controle "Rótulo de texto"
DoEasy. Controles (Parte 8): Objetos básicos do WinForms por categoria, controles GroupBox e CheckBox



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

Arquivos anexados |
MQL5.zip (4379.91 KB)
O modelo de movimento dos preços e suas principais disposições (Parte 1): A versão do modelo mais simples e suas aplicações O modelo de movimento dos preços e suas principais disposições (Parte 1): A versão do modelo mais simples e suas aplicações
O artigo fornece os fundamentos de um movimento de preços matematicamente rigoroso e a teoria do funcionamento do mercado. Até o presente, nós não tivemos nenhuma teoria de movimento de preços matematicamente rigorosa. Em vez disso, nós tivemos que lidar com as suposições baseadas na experiência, afirmando que o preço se move de uma certa maneira após um determinado padrão. É claro que essas suposições não foram apoiadas nem pela estatística e nem pela teoria.
Desenvolvimento de um sistema de negociação baseado no indicador Ichimoku Desenvolvimento de um sistema de negociação baseado no indicador Ichimoku
Neste artigo continuamos a série em que aprendemos a construir sistemas de negociação com base nos indicadores mais populares. Desta vez vamos falar sobre o indicador Ichimoku e criar um sistema de negociação baseado nos seus valores.
Como desenvolver um sistema de negociação baseado no indicador MFI Como desenvolver um sistema de negociação baseado no indicador MFI
O novo artigo de nossa série sobre como projetar um sistema de negociação baseado nos indicadores técnicos mais populares considera um novo indicador técnico - o Índice de Fluxo de Dinheiro (MFI). Estudaremos este indicador em detalhes e aprenderemos a desenvolver um sistema de negociação simples utilizando a linguagem MQL5 para, posteriormente, executá-lo na MetaTrader 5.
Redes neurais de maneira fácil (Parte 18): Regras de associação Redes neurais de maneira fácil (Parte 18): Regras de associação
Como continuação desta série, gostaria de apresentar a vocês outro tipo de tarefa dos métodos de aprendizado não supervisionado, em particular a busca de regras de associação. Este tipo de tarefa foi usado pela primeira vez no varejo para analisar cestas de compras. Neste artigo falaremos sobre as possibilidades de utilização de tais algoritmos no trading.