DoEasy. Elementos de control (Parte 9): Reorganizando los métodos de los objetos WinForms, los controles "RadioButton" y "Button"
Contenido
- Concepto
- Mejorando las clases de la biblioteca
- El objeto WinForms RadioButton
- El objeto WinForms Button
- Simulación
- ¿Qué es lo próximo?
Concepto
Hoy vamos a poner un poco de orden en los nombres de los métodos de los objetos WinForms y a adaptar la lógica de construcción al concepto de todos los objetos de la biblioteca: cada objeto tiene un conjunto de propiedades enteras, reales y string que son inherentes a todos los objetos WinForms, pero, al mismo tiempo, a cada objeto individual se le puede asignar una bandera para que el objeto ofrezca soporte a tal propiedad. Si un objeto no admite una cierta propiedad, la bandera se eliminará para esa propiedad y el objeto no la utilizará.
Antes, muchas de las propiedades solo se almacenaban en las variables de clase, pero seguiremos trasladando todas las propiedades al concepto general de la biblioteca. Esto facilitará la creación, por ejemplo, de constructores de objetos gráficos visuales para nuestros programas basados en esta biblioteca, cuando podemos simplemente crear un ciclo a través de todas las propiedades para mostrar los nombres de las propiedades del objeto gráfico que se está construyendo y los elementos para cambiar cada propiedad, y esta lista solo incluirá las propiedades soportadas por el objeto. Es decir, que cualquier objeto use un único método para mostrar sus propiedades, y que para cada uno de los muchos objetos diferentes se construya una lista diferente y personalizada de propiedades soportadas por el objeto.
Asimismo, hoy empezaremos a hacer que los objetos WinForms todavía estáticos cobren vida: ahora la biblioteca "verá" sobre qué elemento se encuentra el cursor (porque en un panel contenedor se pueden encontrar muchos otros elementos que requieren la interacción del ratón), y más tarde procesaremos dichas interacciones del ratón y enviaremos eventos para responder a ellas.
Y, por supuesto, hoy crearemos dos objetos WinForms: Button y RadioButton. Son objetos estándar de WinForms en la categoría de controles estándar, y no requieren de presentación. Obviamente, todos los controles ya creados serán perfeccionados para añadirles la funcionalidad relacionada con la interactividad; además, poco a poco iremos añadiendo la base de los diversos controles completos, y les añadiremos todos los métodos de interacción con el ratón y diversos efectos visuales al interactuar con él.
Mejorando las clases de la biblioteca
Para los elementos de control, tenemos valores predeterminados que se establecen por defecto cuando aquellos son creados. Vamos a completar la lista de estos valores.
En el archivo \MQL5\Include\DoEasy\Defines.mqh, en el apartado de los parámetros del lienzo, añadimos las macrosustituciones para los colores del 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
En la lista de tipos de elementos gráficos, escribimos los dos nuevos tipos de elementos que crearemos hoy:
//+------------------------------------------------------------------+ //| 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 }; //+------------------------------------------------------------------+
En la lista de propiedades enteras del elemento gráfico en el lienzo, escribimos las nuevas propiedades del elemento y cambiamos el número total de propiedades enteras de 48 a 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
En la lista de posibles criterios para ordenar los elementos gráficos en el lienzo, añadimos los nuevos criterios correspondientes a las propiedades recién añadidas:
//+------------------------------------------------------------------+ //| 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 }; //+------------------------------------------------------------------+
Ahora podremos clasificar, seleccionar y filtrar todos los elementos gráficos según las nuevas propiedades.
En el archivo \MQL5\Include\DoEasy\Data.mqh, escribimos los índices de los nuevos mensajes:
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 }; //+------------------------------------------------------------------+
y los mensajes de texto correspondientes a los nuevos índices añadidos:
{"Ноябрь","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"}, }; //+---------------------------------------------------------------------+
En el futuro, necesitaremos todos estos mensajes para mostrar la descripción de las propiedades de los controles gráficos.
Hemos implementado la descripción de los tipos de control en el método que retorna la descripción del tipo de elemento gráfico, la clase del objeto gráfico básico de la biblioteca en el archivo \MQL5\Include\DoEasy\Objects\Graph\GBaseObj.mqh. Lo complementaremos con líneas que retornen las descripciones de los nuevos 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" ); } //+------------------------------------------------------------------+
Según el tipo de elemento gráfico, el método retorna una línea que describe ese tipo.
Tenemos inconsistencias bastante feas en los nombres de los métodos que establecen o retornan el color. Por ejemplo, el método ForeColor() retorna el color del texto. Al mismo tiempo, el método que retorna el color de fondo se llama ColorBackground(). Para alinear los nombres, renombramos el método ColorBackground(), y otros similares, como BackgroundColor(). Además, hemos introducido nuevas propiedades para los elementos gráficos, y estos tienen propiedades de color al pasar el cursor del ratón sobre el objeto y pulsar sobre el mismo. También tenemos que añadir los métodos para establecer y retornar estas propiedades, así como todas las demás propiedades nuevas.
Vamos a abrir el archivo de clase del elemento gráfico \MQL5\Include\DoEasy\Objects\Graph\GCnvElement.mqh.
Como nuestro color de fondo puede ser un color degradado, usaremos un array de colores para almacenar todos los colores degradados. También necesitaremos añadir otros dos arrays de este tipo para almacenar los colores del gradiente de fondo cuando el puntero del ratón pasa por encima de un objeto y clica sobre él.
En la sección protegida de la clase, declaramos dos arrays así:
//+------------------------------------------------------------------+ //| 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:
Disponemos de una estructura de objetos en la que se escriben todas las propiedades del elemento gráfico creado para guardar las propiedades del objeto en el soporte y leer luego desde él para restaurar el objeto al reiniciarse el terminal y el programa.
Vamos a poner todas las nuevas propiedades en la estructura:
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
Ahora tenemos todos los campos de propiedades necesarios en la estructura para guardar y restaurar correctamente el objeto de la estructura. Más adelante, escribiremos las propiedades de los objetos en archivos.
Ahora, de la sección privada de la clase, eliminamos las dos variables para almacenar el color de fondo y la opacidad, ya que los valores de estas variables se almacenan ahora directamente en las propiedades del 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
Antes, teníamos el método SaveColorsBG para guardar los colores en un array de colores de fondo. Ahora tendremos varios gradientes diferentes cuyos colores tendremos que almacenar en arrays. Así que vamos a declarar un método que copie un array de colores en un array específico de colores de fondo, y añadamos dos métodos más para guardar los colores de fondo del 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:
El primer método copia los colores del array transmitido al método en el array indicado en los parámetros del método, y los otros dos copian el array de colores transmitido a los métodos en los arrays correspondientes de colores de gradiente que hemos declarado anteriormente.
En la sección pública de la clase, del método que crea un elemento gráfico, eliminamos dos variables de sus parámetros de entrada, porque no se usan en ninguna parte del 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);
Asimismo, añadimos dos métodos que retornan las banderas que indican que el objeto es el principal y/o básico:
//--- (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; }
Los métodos simplemente comprueban si el puntero a los objetos principal y básico es NULL.
Con este valor del puntero, el objeto en sí mismo será un objeto principal o un objeto básico, porque si el objeto está vinculado a algún objeto, el puntero a los objetos principal y básico se escribirá en las variables retornadas por los métodos GetMain() y GetBase(). Por lo tanto, si el puntero es NULL, el objeto no estará unido a ningún otro objeto, y podrá ser el objeto principal (el primero en una jerarquía de objetos enlazados) o básico (otros objetos están unidos a él, pero no es el objeto principal en sí mismo, ya que está unido a otro objeto en la cadena general de toda la jerarquía) para otros objetos en la jerarquía.
Vamos a renombrar el método que establece el color de fondo y a añadir los métodos necesarios para establecer el color de fondo al clicar sobre un objeto o colocar el cursor sobre el mismo:
//--- (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,
Ahora, en lugar de escribir los valores en las variables eliminadas, escribiremos los valores en las propiedades de los objetos. Ya hemos analizado estos métodos en artículos anteriores, por lo que no será necesario volver a tratarlos aquí.
Del mismo modo, cambiaremos el nombre de los métodos que retornan los valores de los colores de fondo, y escribiremos los nuevos métodos para retornar los valores de los colores de fondo adicionales:
//--- 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(); }
Aquí tampoco estamos retornando los valores de las variables, ahora eliminadas, sino los valores escritos en las propiedades del objeto. También hemos analizado estos métodos antes y no los repetiremos aquí.
El método ChartColorBackground() también será renombrado (al igual que todos los demás que retornan los parámetros de color, y sus nombres no se adhieren al principio general de nombramiento de los métodos para trabajar con el color):
//--- 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
En el constructor paramétrico de la clase, sustituimos la escritura de los valores en las variables por la llamada a los métodos que lo hagan, escribimos los colores de fondo guardados en arrays de colores de fondo adicionales (cuando se clica en el objeto y se pasa el ratón por encima del mismo) e inicializamos todas las nuevas propiedades del elemento gráfico con los valores por defecto:
//+------------------------------------------------------------------+ //| 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); } } //+------------------------------------------------------------------+
En el constructor protegido, escribimos todo exactamente igual. Los cambios aquí son casi exactamente los mismos que en el anterior constructor, y no los analizaremos aquí: podrá encontrarlos en los archivos de la biblioteca adjuntos al artículo.
También hemos mejorado el método que crea la estructura del objeto para escribir los valores de las nuevas propiedades del objeto en los campos de la estructura (simplemente lo veremos al 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; } //+------------------------------------------------------------------+
y del mismo modo, hemos perfeccionado el método que crea el objeto a partir de la estructura:
//+------------------------------------------------------------------+ //| Create the object from the structure | //+------------------------------------------------------------------+ void CGCnvElement::StructToObject(void) { //--- Save integer properties this.SetProperty(CANV_ELEMENT_PROP_ID,this.m_struct_obj.id); // Element ID this.SetProperty(CANV_ELEMENT_PROP_TYPE,this.m_struct_obj.type); // Graphical element type this.SetProperty(CANV_ELEMENT_PROP_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 métodos son idénticos, pero opuestos entre sí. Mientras que el primer método escribe las correspondientes propiedades del objeto en los campos de la estructura, el segundo método escribirá los valores de los correspondientes campos de la estructura en las propiedades del objeto.
En la implementación del método que crea el objeto de elemento gráfico, eliminamos también las variables no 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; } //+------------------------------------------------------------------+
En el método que establece la opacidad de un elemento, en lugar de la línea que almacena el valor de la opacidad en una variable,
this.m_opacity=value;
escribimos su almacenamiento en la propiedad del 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); } //+------------------------------------------------------------------+
Implementación del método que copia un array de colores en el array especificado de colores de fondo:
//+------------------------------------------------------------------+ //| 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); } //+------------------------------------------------------------------+
Luego transmitimos al método el array en el que se deben anotar todos los datos del array de origen. Si los arrays no coinciden, el array de destino se redimensionará para que coincida con el tamaño del array de origen y, a continuación, se copiará todo el contenido del array de origen en el array de destino.
En el constructor de la clase del objeto de sombra, en el archivo \MQL5\Include\DoEasy\Objects\Graph\ShadowObj.mqh , cambiamos los nombres de los métodos llamados por los nuevos:
//+------------------------------------------------------------------+ //| 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(); } //+------------------------------------------------------------------+
El objeto de formulario será el objeto en el que se implementará la funcionalidad para interactuar con el ratón. El objeto básico de la biblioteca de objetos WinForms será su heredero. En el archivo de la clase de objeto de formulario \MQL5\Include\DoEasy\Objects\Graph\Form.mqh, también tendremos que mejorar muchos métodos.
Renombraremos los métodos para trabajar con el color, y también añadiremos otros nuevos; los métodos para trabajar con el "marco" del objeto también se renombrarán. Ahora será "Border" en lugar de "Frame". Además, podremos adjuntar otros objetos al objeto, por ejemplo, podremos crear botones o textos en un panel...
Al pasar el cursor por encima de un panel de este tipo, la biblioteca detectará el hecho de que el cursor está pasando por encima del panel, pero no "verá" que el cursor se encuentra sobre un objeto vinculado a este panel. Por lo tanto, no habrá manera de interactuar con el objeto vinculado al panel.
Para resolver este problema, tendremos que recorrer la lista con todos los objetos adjuntos al panel e identificar el último objeto de la jerarquía sobre el que se encuentra el cursor. Para ello, crearemos una lista con todos los objetos adjuntos al panel. El objeto adjunto podrá, a su vez, tener otros adjuntos. Es decir, tendremos que recorrer toda la jerarquía de dichos objetos.
Para ello, crearemos una lista en la que escribiremos los punteros a los objetos adjuntos, llamémoslos objetos de interacción, ya que estamos buscando el objeto con el que queremos que interactúe el ratón, así como el método que escribe en la lista todos los demás controles adjuntos al objeto. Si el elemento adjunto también tiene sus propios objetos adjuntos, llamaremos al mismo método para él, y transmitiremos al método el puntero a la lista donde se escriben los punteros.
De esta forma, podremos recorrer toda la jerarquía de objetos adjuntos y obtener una lista con los punteros a los mismos. Luego ya resultará más simple: una vez que hayamos determinado que el cursor se ha colocado encima de un panel, llamaremos al método del panel que crea una lista de todos los objetos adjuntos. Entonces, todo lo que tendremos que hacer es encontrar el primer objeto al final de la lista que tenga la bandera del cursor encima. Y este puntero a este objeto encontrado se transmitirá para el procesamiento posterior, para el cual escribimos hace mucho los espacios en blanco de los manejadores.
Ahora, procederemos a eliminar las variables innecesarias de la sección protegida de la clase:
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
En lugar de estas variables, ahora tendremos todos los valores almacenados en las propiedades del objeto.
Del método CreateAndAddNewElement(), eliminaremos el puntero al objeto principal de la jerarquía de todos los objetos adjuntos:
//--- 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:
Antes, al crear objetos adjuntos, teníamos que indicar explícitamente el objeto principal para el objeto creado, transmitiéndole a él un puntero en los parámetros de este método. Ahora haremos que el objeto principal o básico de la jerarquía se defina automáticamente, lo cual nos evitará tener que especificar dicho objeto innecesariamente al crear un nuevo control adjunto.
En la misma sección protegida, declararemos un método para crear una lista con todos los objetos de interacción y un método que retornará una bandera para indicar si un objeto está en la lista según su nombre:
//--- 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:
En la sección pública, declararemos un método que creará una lista con todos los objetos de interacción (pero sin transmitir un puntero a la lista en los parámetros formales del método) y un método que retornará el puntero al objeto de formulario en la lista de objetos de interacción según su í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); }
El primer método se llamará desde el objeto sobre el que se posa el cursor del ratón, y dentro de él se llamará a un método protegido encargado de buscar otros objetos de interacción en toda la jerarquía del objeto principal. El puntero a la lista se transmitirá a cada objeto subsiguiente en la jerarquía, por lo tanto, en la lista del primer objeto sobre el que se pasó el cursor, se encontrará la lista de punteros a todos los objetos adjuntos.
Escribimos los métodos que retornan y establecen todas las dimensiones del marco de un objeto a partir de sus propiedades:
//--- (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)
Asimismo, añadimos el método que retorna una lista de objetos de interacción y el método que retorna el número de objetos de interacción en la lista creada:
//--- 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); }
Después, eliminamos del método que crea un nuevo elemento adjunto la transmisión del puntero al 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
Ahora no necesitaremos este puntero, pues la biblioteca determinará qué objeto es el principal y cuál es el básico.
Ahora, cambiamos el nombre de los métodos para establecer y retornar el color del marco del formulario y añadimos los nuevos métodos para gestionar los colores de fondo adicionales del formulario:
//+------------------------------------------------------------------+ //| 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); }
Estos métodos ya no trabajan con variables, sino con los valores que están en las propiedades del objeto.
En el método de inicialización de las variables, borramos la lista de objetos de interacción y establecemos la bandera de lista clasificada para ella, y en lugar de escribir los tamaños de los marcos de los formularios en las variables, los establecemos usando los nuevos métodos escritos anteriormente:
//+------------------------------------------------------------------+ //| 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); } //+------------------------------------------------------------------+
Antes, para cada objeto creado, especificábamos no solo sus coordenadas reales (en el sistema de coordenadas del gráfico), sino también sus coordenadas relativas, es decir, la distancia en píxeles desde el inicio de las coordenadas del objeto al que se une el objeto creado. Simplemente especificamos el valor de separación desde el borde del objeto establecido al crear el objeto.
Pero esto no es del todo eficiente, y requiere un seguimiento constante de dichas coordenadas en caso de que estas cambien. Ahora vamos a calcular estas a partir de las coordenadas de la pantalla: para ello, restaremos la coordenada de la pantalla del objeto vinculado a la coordenada de la pantalla del objeto básico. De esta forma, siempre conoceremos las coordenadas relativas exactas del objeto vinculado cuando sus coordenadas cambien.
En el método que crea el nuevo objeto gráfico, calculamos y registramos las coordenadas relativas del 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; } //+------------------------------------------------------------------+
En el método que crea un nuevo elemento vinculado y lo añade a la lista de objetos vinculados, cambiamos el nombre del método para establecer el fondo del objeto,definimos y escribimos el puntero al objeto principal en la jerarquía de objetos vinculados, y calculamos y establecemos las coordenadas relativas del objeto vinculado creado:
//+------------------------------------------------------------------+ //| 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; } //+------------------------------------------------------------------+
¿De qué forma definimos el objeto principal de la jerarquía? Es simple: si este objeto no está vinculado a ningún otro, inicialmente tendrá el puntero al objeto principal establecido en NULL. Si creamos otro control a partir de dicho objeto, se comprobará el valor del puntero al objeto principal. Si el puntero es NULL, el objeto principal será éste. De lo contrario, este objeto ya contendrá un puntero al objeto principal de toda la jerarquía: esto es precisamente lo que escribiremos en el control recién creado. De esta forma, el objeto principal de toda la jerarquía será siempre aquel -el primer objeto- a partir del cual se ha iniciado la creación de la jerarquía de objetos vinculados.
Así, del método que crea un nuevo elemento vinculado, vamos a eliminar la transmisión del puntero al objeto principal y, por tanto, a eliminar el valor sobrante transmitido al 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 ahora tenemos métodos que retornan la anchura del marco de un objeto, y estos valores no se almacenan en variables sino en las propiedades del objeto, vamos a sustituir en el método que retorna las coordenadas iniciales del objeto vinculado la referencia a las variables por la obtención de los valores de los nuevos 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; } //+------------------------------------------------------------------+
A continuación, renombraremos los métodos a llamar, y reemplazaremos en los métodos el trabajo con las variables por el trabajo con las propiedades de los objetos,
//+------------------------------------------------------------------+ //| 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 la bandera que indica que un puntero a un objeto está presente en la lista de objetos de interacción según el nombre:
//+-----------------------------------------------------------------------+ //| 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; } //+------------------------------------------------------------------+
En el ciclo a través de la lista de objetos de interacción, obtenemos el siguiente objeto de formulario y, si el nombre del objeto es igual al transmitido al método, retornaremos true, el objeto con este nombre ya existe en la lista. Cuando el ciclo se complete, retornaremos false, no se ha encontrado ningún objeto con el nombre especificado.
Método seguro que crea una lista de todos los objetos de interacción:
//+------------------------------------------------------------------+ //| 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); } } //+------------------------------------------------------------------+
En un ciclo por todos los objetos adjuntos, obtenemos el siguiente objeto de formulario.
Si el objeto no ha sido obtenido o su tipo es menor que el objeto de formulario, pasaremos al siguiente.
Si ya tenemos en la lista un objeto con este nombre, pasaremos al siguiente.
Si el objeto se ha colocado con éxito en la lista de objetos de interacción, llamaremos al mismo método para ese objeto, para buscar los objetos de interacción vinculados a él.
En este caso, transmitiremos al método la lista especificada en los parámetros de entrada del método.
De este modo, los punteros a todos los objetos de interacción de toda la jerarquía de objetos vinculados se colocarán en una única lista.
Transmitiremos precisamente la lista que se encuentra en el objeto principal, y la indicaremos en el método público que crea la lista de todos los objetos de interacción:
//+------------------------------------------------------------------+ //| Create the list of all interaction objects | //+------------------------------------------------------------------+ int CForm::CreateListInteractObj(void) { this.CreateListDepInteractObj(this.GetListInteractObj()); return this.m_list_interact.Total(); } //+------------------------------------------------------------------+
Esto es sencillo: llamamos al método anterior para crear una lista de objetos de interacción y transmitimos el puntero a la lista del objeto actual. Desde el método, retornamos el número de punteros a los objetos de interacción añadidos a la lista.
Vamos a realizar mejoras en el archivo \MQL5\Include\DoEasy\Objects\Graph\WForms\WinFormBase.mqh de la clase de objeto básico de la biblioteca WinForms.
Como hemos eliminado las variables que guardan la anchura del marco del formulario y las hemos sustituido por métodos en el archivo de la clase del objeto de formulario, ahora eliminaremos los métodos innecesarios para establecer y retornar la anchura del marco:
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); }
En los métodos que establecen el tamaño de la separación, sustituiremos la referencia a las variables por la lectura de los valores retornados por los 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); }
Asimismo, reemplazaremos las llamadas a los antiguos métodos de FrameWidth por los nuevos 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); } //+------------------------------------------------------------------+
Luego declararemos los métodos que retornan las descripciones de las propiedades de los 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); }; //+------------------------------------------------------------------+
En el constructor de la clase, sustituiremos la asignación de valores a las variables por el establecimiento de los valores de las propiedades usando el nuevo 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 la descripción del estilo de la fuente:
//+------------------------------------------------------------------+ //| 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) ); } //+------------------------------------------------------------------+
Dependiendo del estilo de fuente establecido, se retornará el mensaje de texto correspondiente.
Método que retorna la descripción del tipo de grosor de la fuente:
//+------------------------------------------------------------------+ //| 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); } //+------------------------------------------------------------------+
La enumeración ENUM_FW_TYPE contiene estas 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 el tipo de grosor "Regular", por ejemplo, necesitaremos tomar una subcadena de la constante FW_TYPE_REGULAR, comenzando por la posición 8. Tras extraer la subcadena del nombre de la constante, obtendremos la cadena "REGULAR". Todos los caracteres aquí están en mayúsculas. Ahora tendremos que hacer que todos los caracteres estén en minúsculas, salvo el primero.
Para ello, bastará con añadir un desplazamiento de 32 (0x20) al código de caracteres, ya que los códigos de caracteres en minúscula se distinguen de los códigos en mayúscula exactamente en 32. ¿Qué estamos haciendo? La subcadena extraída, formada por caracteres en mayúsculas, se colocará en un array uchar, y luego en un ciclo por todos los caracteres del array (por cada letra), añadiremos 32 al valor del carácter. Como el primer carácter (en la celda 0 del array) no necesita ser cambiado, el ciclo comenzará desde la celda 1 del array, desde el segundo carácter. El resultado será un array uchar convertido nuevamente en cadena.
Método que retorna la descripción del estilo del marco de un control:
//+------------------------------------------------------------------+ //| 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) ); } //+------------------------------------------------------------------+
Dependiendo del estilo de marco establecido para el objeto, se retornará el mensaje de texto correspondiente.
Método que retorna la descripción de una propiedad entera de un 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) ) : "" ); } //+------------------------------------------------------------------+
Transmitiremos al método una propiedad entera del objeto. Dependiendo de la propiedad transmitida al método y de la bandera de soporte de esa propiedad por parte del objeto, se construirá la cadena retornada por el método. Hay métodos similares en casi todos los objetos de la biblioteca, por lo que no tiene sentido revisar este punto aquí. Solo señalaremos que, por ahora, tenemos que cada elemento gráfico ofrece soporte a todas las propiedades de los elementos gráficos. Iremos añadiendo los métodos que retornan las banderas de soporte de una determinada propiedad de los elementos gráficos a los objetos WinForms, a medida que podamos controlar visualmente los valores de estas propiedades y las herramientas para modificarlas con el ratón o el teclado, una vez que la mayoría de los objetos WinForms estén creados y su interactividad sea funcional.
Método que retorna la descripción de una propiedad de tipo real de un elemento:
//+------------------------------------------------------------------+ //| Return the description of the control real property | //+------------------------------------------------------------------+ string CWinFormBase::GetPropertyDescription(ENUM_CANV_ELEMENT_PROP_DOUBLE property,bool only_prop=false) { return(""); } //+------------------------------------------------------------------+
Como todavía no tenemos ninguna propiedad real para los elementos gráficos, el método retornará una cadena vacía.
Método que retorna la descripción de una propiedad de tipo string del 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)+"\"" : "" ); } //+------------------------------------------------------------------+
Al método se transmite una propiedad de tipo string del objeto. Dependiendo de la propiedad transmitida al método, se construirá la cadena retornada desde el método.
Método que retorna la descripción del modo de cambio de tamaño automático del elemento para ajustarse al contenido:
//+------------------------------------------------------------------+ //| 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) ); } //+------------------------------------------------------------------+
Dependiendo del modo de cambio de tamaño automático del artículo, se retornará el mensaje de texto correspondiente.
Método que retorna la descripción del modo de vinculación de los límites de un elemento a un contenedor:
//+-------------------------------------------------------------------------------------+ //| 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) ); } //+------------------------------------------------------------------+
Dependiendo del modo de vinculación de los límites, se retornará el mensaje de texto correspondiente.
Método que retorna la descripción del estado del control que tiene una casilla de verificación:
//+------------------------------------------------------------------+ //| 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) ); } //+------------------------------------------------------------------+
Dependiendo del estado de la casilla de verificación del elemento, se retornará el mensaje de texto correspondiente.
Vamos a realizar algunas mejoras el archivo \MQL5\Include\DoEasy\Objects\Graph\WForms\Common Controls\CommonBase.mqh de la clase de objeto básico de los controles estándar de WinForms.
Así, cambiaremos el tipo del método que establece automáticamente la anchura y la altura de un elemento de void a bool, para cada objeto descendiente, la implementación de este método puede ser diferente, por lo que si necesitamos cambiar la lógica de este método, se redefinirá en diferentes clases a su manera. Sin embargo, tiene que ser capaz de retornar el resultado de su trabajo. Si el objeto no cambia de tamaño, no habrá necesidad de redefinir el método en la clase: aquí simplemente este retornará true sin hacer 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:
En el método de inicialización de las variables, sustituimos la escritura de los valores de la anchura del marco en las variables por el establecimiento de estos valores usando el método y cambiamos el nombre del método que establece el color de fondo:
//+------------------------------------------------------------------+ //| 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); } //+------------------------------------------------------------------+
Luego cambiamos el nombre de los métodos que retornan los valores de la anchura del marco:
//+------------------------------------------------------------------+ //| 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); } //+------------------------------------------------------------------+
En la clase del objeto etiqueta de texto en \MQL5\Include\DoEasy\Objects\Graph\WForms\Common Controls\Label.mqh tenemos un método que calcula las coordenadas de ubicación del texto en función del punto de anclaje del texto y su alineación. No obstante, todo se hace en el método de redibujado del objeto. Esto no es óptimo: muchos objetos heredados de esta clase también pueden necesitar el posicionamiento de la inscripción. Por ello, desde el método de redibujado de la etiqueta de texto, trasladamos el cálculo de las coordenadas del texto a un nuevo método y lo llamamos donde sea necesario, no solo en la clase del objeto de etiqueta de texto, sino también en sus descendientes.
En la sección protegida de la clase, cambiamos el tipo del método de cambio de tamaño automático del elemento, declaramos el método que establece las coordenadas y el punto de anclaje del texto dependiendo de su modo de alineación, y en la sección pública de la clase, declaramos el método que establece la bandera para cambiar automáticamente el tamaño del elemento para que se ajuste al contenido:
//+------------------------------------------------------------------+ //| 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 el método para calcular las coordenadas del texto es ahora un método aparte, el método de redibujado del objeto será ahora más corto:
//+------------------------------------------------------------------+ //| 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 establece las coordenadas y el punto de anclaje del texto según su modo de alineación:
//+------------------------------------------------------------------+ //| 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; } } //+------------------------------------------------------------------+
Simplemente trasladamos al método las líneas para calcular las coordenadas del texto del método de redibujado de objetos. Pero aquí se usan ahora los métodos previamente renombrados.
Como el método virtual que establece automáticamente la anchura y la altura de un elemento debe ahora retornar el resultado, en lugar de limitarse a establecer la nueva anchura y altura en las propiedades del objeto, el método escribirá en la variable de retorno el resultado del cambio de tamaño del objeto:
//+------------------------------------------------------------------+ //| 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 establece la bandera para cambiar de tamaño automáticamente el elemento para que se ajuste al contenido:
//+------------------------------------------------------------------+ //| 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); } //+------------------------------------------------------------------+
Si la bandera transmitida al método está activada, y si el objeto ha sido redimensionado con éxito, escribiremos el valor de la bandera en las propiedades.
Hoy vamos a crear la clase de objeto WinForms RadioButton. En su contenido interno, el objeto es similar al control CheckBox implementado en \MQL5\Include\DoEasy\Objects\Graph\WForms\Common Controls\CheckBox.mqh, pero, a diferencia de este último, el objeto RadioButton tiene un campo redondo con una casilla de verificación en lugar de un campo cuadrado. En consecuencia, resultará ventajoso para nosotros heredar de la clase de objeto CheckBox y sobrescribir los métodos para dibujar el campo de selección. Ahora, eliminaremos el método SetTextCoords(), porque es exactamente igual al método SetTextParamsByAlign() del objeto padre, y para que todas las variables del objeto CheckBox estén disponibles en sus herederos, las trasladaremos de la sección privada a la sección 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);
El método virtual que establece automáticamente la anchura y la altura de un elemento deberá ser ahora de tipo bool.
En la sección pública de la clase, cambiamos los métodos que establecen el estado de la casilla de verificación y del control. Ahora, antes de establecer los estados, primero se comprobará el estado actual, ya que el establecimiento de estos debería hacer que se redibujen las casillas y el objeto, en lugar de simplemente establecerse un valor en las propiedades del objeto. A continuación, escribimos los métodos que establecen las banderas y los colores de fondo del 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
En el método de redibujado del objeto, cambiamos el nombre del método que establece el color de fondo:
//+------------------------------------------------------------------+ //| 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); } //+------------------------------------------------------------------+
En el método SetCorrectTextCoords(), en lugar de llamar al método SetTextCoords eliminado, escribimos la llamada al método de la clase padre 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 a cambiar el método que dibuja la casilla de verificación. Esta casilla no debe dibujarse sobre un fondo transparente como antes, sino sobre el fondo de un rectángulo de color, rodeada por un marco. La marca de verificación dibujada dentro de esta caja es un poco fina, así que dibujaremos no una sino tres líneas, cada una de las cuales será un píxel más alta en el centro que la anterior. Por supuesto, en términos de escalabilidad de la casilla de verificación, esto es incorrecto, pero realizaremos un cálculo relativo a las coordenadas de la línea discontinua que dibuja la casilla de verificación más adelante:
//+------------------------------------------------------------------+ //| 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; } } //+------------------------------------------------------------------+
El método que establece automáticamente la anchura y la altura de un elemento es ahora de tipo bool, y retorna el 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; } //+------------------------------------------------------------------+
Esto es exactamente igual que en el método de la clase de objeto de etiqueta de texto anterior. Declaramos la variable, escribimos en ella el resultado del cambio de tamaño y lo retornamos.
El objeto WinForms RadioButton
El objeto será sucesor del objeto WinForms CheckBox, ya que casi copia su funcionalidad y organización interna.
En el directorio de la biblioteca \MQL5\Include\DoEasy\Objects\Graph\WForms\Common Controls\ crearemos el nuevo archivo RadioButton.mqh de la clase CRadioButton
La clase deberá heredar de la clase CCheckBox y su archivo deberá vincularse al archivo de la clase que estamos creando:
//+------------------------------------------------------------------+ //| 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 { }
En la sección de la clase protegida, declararemos un método virtual que mostrará la casilla de verificación, y en la sección pública, declararemos un constructor 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); }; //+------------------------------------------------------------------+
El método que muestra la casilla de verificación redefine el método de la clase padre, ya que aquí la casilla de verificación es redonda, a diferencia de la casilla cuadrada del objeto CheckBox.
Echemos un vistazo a estos métodos.
Constructor 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); } //+------------------------------------------------------------------+
Aquí solo tenemos que definir el tipo del objeto WinForms y redibujar el objeto. Todo lo demás se crea y establece en el constructor de la clase padre.
Método que muestra la casilla de verificación para el 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; } } //+------------------------------------------------------------------+
De forma similar a la clase padre, aquí dibujaremos primero un círculo con un color de fondo, luego dibujaremos el borde y, dependiendo de la bandera de estado transmitida al método, dibujaremos un círculo más pequeño dentro del círculo dibujado o no se dibujará nada.
Y eso es todo lo que necesitamos para crear este objeto, todo lo demás se implementará en las clases padre.
El objeto WinForms Button
Objeto de botón. En esencia, es una etiqueta de texto. La etiqueta de texto tiene la capacidad de dibujar los límites del objeto, y tiene la posibilidad de posicionar el texto dentro de los límites del control. La etiqueta solo se dibuja sobre un fondo transparente, mientras que el botón se dibuja sobre un fondo de color. Por lo tanto, el objeto de botón será heredado del objeto de etiqueta de texto.
En el directorio de la biblioteca \MQL5\Include\DoEasy\Objects\Graph\WForms\Common Controls\ crearemos el nuevo archivo Button.mqh de la clase CButton
La clase deberá heredar de la clase CLabel y su archivo deberá estar vinculado al que estamos creando:
//+------------------------------------------------------------------+ //| 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 { }
En la sección privada de la clase, declararemos las variables para almacenar las coordenadas de la inscripción en el botón, y en la sección protegida, declararemos un método virtual para establecer el tamaño del control automáticamente:
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:
En la sección pública, declararemos el método virtual para redibujar el objeto y escribiremos los métodos para establecer y retornar el modo de cambio de tamaño automático del objeto hasta el tamaño de la inscripción:
public: //--- Redraw the object virtual void Redraw(bool redraw); //--- (1) Set and (2) return the mode of the element auto resizing depending on the content void SetAutoSizeMode(const ENUM_CANV_ELEMENT_AUTO_SIZE_MODE mode,const bool redraw) { ENUM_CANV_ELEMENT_AUTO_SIZE_MODE prev=this.AutoSizeMode(); if(prev==mode) return; this.SetProperty(CANV_ELEMENT_PROP_AUTOSIZE_MODE,mode); } ENUM_CANV_ELEMENT_AUTO_SIZE_MODE AutoSizeMode(void) const { return (ENUM_CANV_ELEMENT_AUTO_SIZE_MODE)this.GetProperty(CANV_ELEMENT_PROP_AUTOSIZE_MODE); } //--- Constructor CButton(const long chart_id, const int subwindow, const string name, const int x, const int y, const int w, const int h); }; //+------------------------------------------------------------------+
En el constructor paramétrico, indicaremos el tipo de control, restableceremos las propiedades del objeto a los valores por defecto y llamaremos al método de redibujado:
//+------------------------------------------------------------------+ //| 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 virtual que redibuja un 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); } //+------------------------------------------------------------------+
En primer lugar, rellenaremos los bordes del objeto con el color de fondo, luego mostraremos la inscripción en las coordenadas calculadas según la alineación del texto y actualizaremos el objeto.
Método que establece automáticamente la anchura y la altura de un 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; } //+------------------------------------------------------------------+
El método es casi idéntico a los mismos métodos de las clases padre que hemos comentado anteriormente. Pero aquí hay un pequeño matiz: las dimensiones cambiarán según el modo de cambio de tamaño establecido. Es decir, si el modo es simplemente zoom, solo aumentará el tamaño del objeto si el texto se sale de sus límites. Si se selecciona el modo zoom y de alejamiento, todo el objeto se ajustará al tamaño del texto que contiene.
Por supuesto, esto no es todo lo que mostrará la posible funcionalidad del objeto de botón, pero todo lo demás se añadirá en futuros artículos, de forma paralela al desarrollo de otros objetos WinForms.
Los objetos contenedores, para poder crear objetos vinculados dentro de ellos, deberán conocer la existencia de los elementos de control que estamos creando nuevamente. Para ello, conectaremos los nuevos archivos de clase con los archivos de clase del objeto contenedor.
La clase padre de todos los objetos contenedores será la clase de objeto contenedor básica, implementada en \MQL5\Include\DoEasy\Objects\Graph\WForms\Containers\Container.mqh.
Vamos a conectar a ella los nuevos archivos de control. En lugar del archivo CheckBox.mqh, conectaremos el archivo del objeto RadioButton, ya que es un descendiente del elemento CheckBox, por lo que ambos serán visibles en la clase:
//+------------------------------------------------------------------+ //| 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 el método para crear un nuevo objeto gráfico es diferente para cada clase heredada, aquí solo declararemos un método virtual y eliminaremos su implementación. El método retornará el valor 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
A continuación, cambiaremos el nombre de los métodos públicos FrameWidth por 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
Del método de creación de un nuevo elemento, eliminaremos la transmisión del puntero al objeto principal en el método:
//--- 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);
En la implementación del método, escribiremos la creación de todos los controles conocidos:
//+------------------------------------------------------------------+ //| 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; } //+------------------------------------------------------------------+
El método que ajusta las dimensiones de los elementos a su contenido interno ha sido transformado, pero sigue sin funcionar correctamente, así que aquí mostraremos un ejemplo de su implementación actual sin más explicaciones que las ya mostradas en los comentarios al código del 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) ); } //+------------------------------------------------------------------+
Los otros cambios menores en la clase, que son resultado de la experimentación y no resultan relevantes para el tema de este artículo, no se tratarán aquí. Volveremos a ellos cuando todo funcione correctamente.
La clase de objeto contenedor GroupBox en el archivo \MQL5\Include\DoEasy\Objects\Graph\WForms\Containers\GroupBox.mqh también se ha mejorado ligeramente.
Hemos corregido las llamadas a los métodos renombrados:
//--- 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); } //+------------------------------------------------------------------+
El método virtual que crea un nuevo objeto gráfico se escribe para crear todos los elementos conocidos actualmente:
//+------------------------------------------------------------------+ //| 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; } //+------------------------------------------------------------------+
En la clase contenedora del objeto "Panel", en el archivo \MQL5\Include\DoEasy\Objects\Graph\WForms\Containers\Panel.mqh, también deberemos hacer algunas modificaciones.
En los métodos para establecer los intervalos, el cálculo y la establecimiento del desplazamiento del sustrato deberán transferirse después de establecer sus coordenadas y su tamaño:
//--- 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, el cálculo del desplazamiento se realizaba antes de calcular las coordenadas y las dimensiones, lo cual provocaba un error en el cálculo del desplazamiento después de introducir los cambios.
En el método para crear un nuevo objeto gráfico, añadiremos la creación de todos los elementos desconocidos:
//+------------------------------------------------------------------+ //| 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; } //+------------------------------------------------------------------+
En el método que establece todos los parámetros del sustrato, desplazaremos también el cálculo del desplazamiento por debajo de los cálculos de las 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; } //+------------------------------------------------------------------+
En el método Move cambiaremos el cálculo de los valores de desplazamiento del objeto:
//--- Shift all bound objects if(!this.MoveDependentObj(x+this.GetCoordXUnderlayRelative(),y+this.GetCoordYUnderlayRelative(),false)) return false;
Ahora será más sencillo:
//+------------------------------------------------------------------+ //| 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 las coordenadas relativas de todos los objetos se calculan ahora en lugar de usar las coordenadas establecidas originalmente, no hay necesidad de ajustar nada aquí, simplemente sustituiremos los valores X e Y transmitidos al método para el desplazamiento.
Hoy hemos hecho muchas correcciones en la clase de colección de elementos gráficos, en el archivo \MQL5\Include\DoEasy\Collections\GraphElementsCollection.mqh, ya que hemos cambiado el nombre de algunos métodos, y la referencia a ellos en esta clase se usa activamente. A nuestro juicio, describir simplemente el cambio del nombre resulta inútil aquí, ya hemos analizado todos los cambios similares a lo largo del artículo de hoy. Por lo tanto, podrá leer usted mismo dichos cambios en los archivos adjuntos al artículo.
No obstante, en lo que debemos centrarnos es en hallar los objetos vinculados al panel y en crear una lista de objetos de interacción que permita determinar con qué objeto deberá interactuar el ratón.
En el método que retorna el puntero al formulario debajo del cursor, tendremos que añadir dos bloques de código (idénticos, y después de algún tiempo de pruebas, probablemente se pondrán en un método aparte). Estos bloques solo crean una lista con todos los objetos vinculados al panel -su jerarquía- y el más reciente se retornará como el objeto con el que se va a interactuar:
//+------------------------------------------------------------------+ //| 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; } //+------------------------------------------------------------------+
Los comentarios al código describen la lógica con gran detalle. Esperamos que todo se entienda bien. En cualquier caso, podrá plantear cualquier duda en los comentarios al artículo.
En el bloque del manejador de eventos de la clase CGraphElementsCollection::OnChartEvent(), en el manejador de eventos "El cursor se encuentra dentro del área activa, la rueda del ratón se está desplazando", escribiremos en el diario de registro el tipo de elemento gráfico sobre el que se encuentra el cursor y el nombre de este elemento. De esta forma, podremos controlar si el ratón está interactuando correctamente con el objeto sobre el que se sitúa el cursor: si se pasa por encima de un elemento y se desplaza la rueda del ratón, el registro mostrará los datos sobre ese objeto:
//--- '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()); }
Simulación
Para la prueba, tomaremos el asesor del artículo anterior y lo guardaremos en la nueva carpeta \MQL5\Experts\TestDoEasy\Part109\ con el nuevo nombre TstDE109.mq5.
Vamos a añadir nuevos parámetros a los parámetros de entrada del asesor, lo cual nos permitirá establecer la alineación para la casilla de verificación y el texto, así como los parámetros para el objeto de botón:
//--- 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
En el manejador OnInit(), creamos todos los objetos que necesitamos en el panel y en el contenedor 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); } //+------------------------------------------------------------------+
La lógica del bloque de código para crear los tres controles adjuntos a GroupBox se comenta en el código; a nuestro juicio, ahí está todo claro.
El tamaño del botón (el objeto WinForm Button) se ha hecho así deliberadamente para que el botón sea más alto que el texto y más estrecho en anchura. Luego, al activarse el cambio de tamaño automático, podremos ver cómo el botón ajusta su tamaño al texto.
Vamos a compilar el asesor y ejecutarlo en el gráfico:
Puede ver que el botón ajusta fielmente su tamaño al texto dependiendo del modo de cambio de tamaño automático. CheckBox y RadioButton tienen campos normales y claros para la casilla de verificación, y el propio elemento CheckBox ha adquirido grosor.
Al pasar el ratón por encima de algunos controles, hemos desplazado la rueda del ratón. Al mismo tiempo, se ha mostrado la entrada:
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
Estas entradas indican la selección correcta del objeto activo para interactuar con el ratón.
¿Qué es lo próximo?
En el próximo artículo, continuaremos desarrollando los objetos WinForms, así como la funcionalidad de los ya creados.
*Artículos de esta serie:
DoEasy. Controles (Parte 1): Primeros pasos
DoEasy. Elementos de control (Parte 2): Continuamos trabajando con la clase CPanel
DoEasy. Elementos de control (Parte 3): Creando controles vinculados
DoEasy. Elementos de control (Parte 4): Elemento de control "Panel", parámetros Padding y Dock
DoEasy. Elementos de control (Parte 5): Objeto básico WinForms, control «Panel», parámetro AutoSize
DoEasy. Elementos de control (Parte 6): Control «Panel», cambio automático del tamaño del contenedor según el contenido interno
DoEasy. Elementos de control (Parte 7): Elemento de control «etiqueta de texto»
DoEasy. Elementos de control (Parte 8): Objetos básicos WinForms por categorías, controles "GroupBox" y "CheckBox"
Traducción del ruso hecha por MetaQuotes Ltd.
Artículo original: https://www.mql5.com/ru/articles/11121
- Aplicaciones de trading gratuitas
- 8 000+ señales para copiar
- Noticias económicas para analizar los mercados financieros
Usted acepta la política del sitio web y las condiciones de uso