Понравилась статья?
Поставьте ссылку на нее —
пусть другие почитают
Используй новые возможности MetaTrader 5

DoEasy. Элементы управления (Часть 9): Реорганизация методов WinForms-объектов, элементы управления "RadioButton" и "Button"

24 июня 2022, 13:52
Artyom Trishkin
0
738

Содержание


Концепция

Сегодня начнём потихоньку наводить порядок в названиях методов WinForms-объектов и переводить логику их построения на установленную для всех объектов библиотеки концепцию — у каждого объекта есть набор целочисленных, вещественных и строковых свойств, которые присущи всем WinForms-объектам, но для каждого конкретного объекта можно установить флаг поддержания объектом такого свойства. Если объект не поддерживает какое-либо свойство, то для этого свойства флаг снят, и объект его не использует.

Ранее многие свойства мы хранили только в переменных классов, но всё же мы переведём все свойства в русло общей концепции библиотеки. Это упростит создание, например, программ-визуальных конструкторов графических объектов для своих программ, работающих на основе данной библиотеки — когда мы просто в цикле по всем свойствам можем вывести на панель названия свойств строящегося графического объекта и элементы для изменения каждого свойства, и в этом списке будут присутствовать только поддерживаемые объектом свойства. Т.е. — для любого объекта использовать единый метод вывода его свойств, и для каждого из множества разных объектов будет построен отличный от других, собственный список поддерживаемых объектом свойств.

Также сегодня начнём работать над оживлением пока ещё статичных WinForms-объектов — теперь библиотека начнёт "видеть" над каким элементом находится курсор (ведь на панели-контейнере может быть расположено множество других элементов, и с ними нужно взаимодействие), а в последствии мы будем обрабатывать такие взаимодействия элементов с мышью и отсылать события для реакции на них.

Ну и, конечно же, сегодня создадим два WinForms-объекта —Button и RadioButton. Это стандартные WinForms-объекты из категории стандартных элементов управления, и в представлении не нуждаются. Конечно же, все уже созданные такие элементы далее будут дорабатываться для добавления в них функционала интерактивности, но основу для полноценных различных элементов управления мы потихонечку добавляем, а далее будем добавлять к ним всем методы для интерактивного взаимодействия с мышкой и разные визуальные эффекты при взаимодействии с ней.


Доработка классов библиотеки

Для элементов управления у нас есть значения, устанавливаемые по умолчанию при их создании. Дополним список таких значений.

В файле \MQL5\Include\DoEasy\Defines.mqh в разделе параметров канваса впишем макроподстановки для цветов элемента CheckBox:


#define CLR_DEF_SHADOW_OPACITY         (127)                      // Непрозрачность цвета по умолчанию для теней объектов на канвасе
#define DEF_SHADOW_BLUR                (4)                        // Размытие по умолчанию для теней объектов на канвасе

#define CLR_DEF_CHECK_BACK_COLOR       (C'0xD9,0xEC,0xEB')        // Цвет фона флажка проверки элемента управления
#define CLR_DEF_CHECK_BACK_OPACITY     (255)                      // Непрозрачность цвета фона флажка проверки элемента управления
#define CLR_DEF_CHECK_BACK_MOUSE_DOWN  (C'0xBA,0xEB,0xF5')        // Цвет фона флажка проверки элемента управления при нажатии мышки на элемент управления
#define CLR_DEF_CHECK_BACK_MOUSE_OVER  (C'0xCE,0xE0,0xE3')        // Цвет фона флажка проверки элемента управления при наведении мышки на элемент управления
#define CLR_DEF_CHECK_FORE_COLOR       (C'0x2D,0x43,0x48')        // Цвет рамки флажка проверки элемента управления
#define CLR_DEF_CHECK_FORE_OPACITY     (255)                      // Непрозрачность цвета рамки флажка проверки элемента управления
#define CLR_DEF_CHECK_FORE_MOUSE_DOWN  (C'0x06,0x0B,0xAA')        // Цвет рамки флажка проверки элемента управления при нажатии мышки на элемент управления
#define CLR_DEF_CHECK_FORE_MOUSE_OVER  (C'0x06,0x0B,0xAA')        // Цвет рамки флажка проверки элемента управления при наведении мышки на элемент управления
#define CLR_DEF_CHECK_FLAG_COLOR       (C'0x04,0x7B,0x0D')        // Цвет флажка проверки элемента управления
#define CLR_DEF_CHECK_FLAG_OPACITY     (255)                      // Непрозрачность цвета флажка проверки элемента управления
#define CLR_DEF_CHECK_FLAG_MOUSE_DOWN  (C'0x0E,0x9B,0x0B')        // Цвет флажка проверки элемента управления при нажатии мышки на элемент управления
#define CLR_DEF_CHECK_FLAG_MOUSE_OVER  (C'0x0E,0xC7,0x2E')        // Цвет флажка проверки элемента управления при наведении мышки на элемент управления

#define CLR_DEF_CONTROL_STD_BACK_COLOR (C'0xCD,0xD8,0xDA')        // Цвет фона стандартных элементов управления
#define CLR_DEF_CONTROL_STD_OPACITY    (255)                      // Непрозрачность цвета фона стандартных элементов управления

#define DEF_FONT                       ("Calibri")                // Шрифт по умолчанию
#define DEF_FONT_SIZE                  (8)                        // Размер шрифта по умолчанию


В списке типов графических элементов впишем два новых типа элементов, которые сегодня создадим:

//+------------------------------------------------------------------+
//| Список типов графических элементов                               |
//+------------------------------------------------------------------+
enum ENUM_GRAPH_ELEMENT_TYPE
  {
   GRAPH_ELEMENT_TYPE_STANDARD,                       // Стандартный графический объект
   GRAPH_ELEMENT_TYPE_STANDARD_EXTENDED,              // Расширенный стандартный графический объект
   GRAPH_ELEMENT_TYPE_SHADOW_OBJ,                     // Объект тени
   GRAPH_ELEMENT_TYPE_ELEMENT,                        // Элемент
   GRAPH_ELEMENT_TYPE_FORM,                           // Форма
   GRAPH_ELEMENT_TYPE_WINDOW,                         // Окно
   //--- WinForms
   GRAPH_ELEMENT_TYPE_WF_UNDERLAY,                    // Подложка объекта-панели
   GRAPH_ELEMENT_TYPE_WF_BASE,                        // Windows Forms Base
   GRAPH_ELEMENT_TYPE_WF_CONTAINER,                   // Windows Forms базовый объект-контейнер
   GRAPH_ELEMENT_TYPE_WF_PANEL,                       // Windows Forms Panel
   GRAPH_ELEMENT_TYPE_WF_GROUPBOX,                    // Windows Forms GroupBox
   GRAPH_ELEMENT_TYPE_WF_COMMON_BASE,                 // Windows Forms базовый стандартный элемент управления
   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
  };
//+------------------------------------------------------------------+


В перечисление целочисленных свойств графического элемента на канвасе впишем новые свойства элемента
и изменим общее количество целочисленных свойств с 48 до 71:

//+------------------------------------------------------------------+
//| Целочисленные свойства графического элемента на канвасе          |
//+------------------------------------------------------------------+
enum ENUM_CANV_ELEMENT_PROP_INTEGER
  {
   CANV_ELEMENT_PROP_ID = 0,                          // Идентификатор элемента
   CANV_ELEMENT_PROP_TYPE,                            // Тип графического элемента
   CANV_ELEMENT_PROP_BELONG,                          // Принадлежность графического элемента
   CANV_ELEMENT_PROP_NUM,                             // Номер элемента в списке
   CANV_ELEMENT_PROP_CHART_ID,                        // Идентификатор графика
   CANV_ELEMENT_PROP_WND_NUM,                         // Номер подокна графика
   CANV_ELEMENT_PROP_COORD_X,                         // X-координата элемента на графике
   CANV_ELEMENT_PROP_COORD_Y,                         // Y-координата элемента на графике
   CANV_ELEMENT_PROP_WIDTH,                           // Ширина элемента
   CANV_ELEMENT_PROP_HEIGHT,                          // Высота элемента
   CANV_ELEMENT_PROP_RIGHT,                           // Правая граница элемента
   CANV_ELEMENT_PROP_BOTTOM,                          // Нижняя граница элемента
   CANV_ELEMENT_PROP_ACT_SHIFT_LEFT,                  // Отступ активной зоны от левого края элемента
   CANV_ELEMENT_PROP_ACT_SHIFT_TOP,                   // Отступ активной зоны от верхнего края элемента
   CANV_ELEMENT_PROP_ACT_SHIFT_RIGHT,                 // Отступ активной зоны от правого края элемента
   CANV_ELEMENT_PROP_ACT_SHIFT_BOTTOM,                // Отступ активной зоны от нижнего края элемента
   CANV_ELEMENT_PROP_MOVABLE,                         // Флаг перемещаемости элемента
   CANV_ELEMENT_PROP_ACTIVE,                          // Флаг активности элемента
   CANV_ELEMENT_PROP_INTERACTION,                     // Флаг взаимодействия элемента со внешней средой
   CANV_ELEMENT_PROP_COORD_ACT_X,                     // X-координата активной зоны элемента
   CANV_ELEMENT_PROP_COORD_ACT_Y,                     // Y-координата активной зоны элемента
   CANV_ELEMENT_PROP_ACT_RIGHT,                       // Правая граница активной зоны элемента
   CANV_ELEMENT_PROP_ACT_BOTTOM,                      // Нижняя граница активной зоны элемента
   CANV_ELEMENT_PROP_ZORDER,                          // Приоритет графического объекта на получение события нажатия мышки на графике
   CANV_ELEMENT_PROP_ENABLED,                         // Флаг доступности элемента
   CANV_ELEMENT_PROP_FORE_COLOR,                      // Цвет текста по умолчанию для всех объектов элемента управления
   CANV_ELEMENT_PROP_FORE_COLOR_OPACITY,              // Непрозрачность цвета текста по умолчанию для всех объектов элемента управления
   
   CANV_ELEMENT_PROP_BACKGROUND_COLOR,                // Цвет фона элемента управления
   CANV_ELEMENT_PROP_BACKGROUND_COLOR_OPACITY,        // Непрозрачность цвета фона элемента управления
   CANV_ELEMENT_PROP_BACKGROUND_COLOR_MOUSE_DOWN,     // Цвет фона элемента управления при нажатии мышки на элемент управления
   CANV_ELEMENT_PROP_BACKGROUND_COLOR_MOUSE_OVER,     // Цвет фона элемента управления при наведении мышки на элемент управления
   
   CANV_ELEMENT_PROP_BOLD_TYPE,                       // Тип толщины шрифта
   CANV_ELEMENT_PROP_BORDER_STYLE,                    // Стиль рамки элемента управления
   
   CANV_ELEMENT_PROP_BORDER_SIZE_TOP,                 // Размер рамки элемента управления сверху
   CANV_ELEMENT_PROP_BORDER_SIZE_BOTTOM,              // Размер рамки элемента управления снизу
   CANV_ELEMENT_PROP_BORDER_SIZE_LEFT,                // Размер рамки элемента управления слева
   CANV_ELEMENT_PROP_BORDER_SIZE_RIGHT,               // Размер рамки элемента управления справа
   CANV_ELEMENT_PROP_BORDER_COLOR,                    // Цвет рамки элемента управления
   CANV_ELEMENT_PROP_BORDER_COLOR_MOUSE_DOWN,         // Цвет рамки элемента управления при нажатии мышки на элемент управления
   CANV_ELEMENT_PROP_BORDER_COLOR_MOUSE_OVER,         // Цвет рамки элемента управления при наведении мышки на элемент управления
   
   CANV_ELEMENT_PROP_AUTOSIZE,                        // Флаг автоматического изменения размера элемента управления под содержимое
   CANV_ELEMENT_PROP_AUTOSIZE_MODE,                   // Режим автоматического изменения размера элемента управления под содержимое
//--- ...   
//--- ...
   CANV_ELEMENT_PROP_CHECK_STATE,                     // Состояние элемента управления, имеющего флажок проверки
   CANV_ELEMENT_PROP_AUTOCHECK,                       // Автоматическое изменение состояния флажка при его выборе
   
   CANV_ELEMENT_PROP_CHECK_BACKGROUND_COLOR,          // Цвет фона флажка проверки элемента управления
   CANV_ELEMENT_PROP_CHECK_BACKGROUND_COLOR_OPACITY,  // Непрозрачность цвета фона флажка проверки элемента управления
   CANV_ELEMENT_PROP_CHECK_BACKGROUND_COLOR_MOUSE_DOWN,// Цвет фона флажка проверки элемента управления при нажатии мышки на элемент управления
   CANV_ELEMENT_PROP_CHECK_BACKGROUND_COLOR_MOUSE_OVER,// Цвет фона флажка проверки элемента управления при наведении мышки на элемент управления
   CANV_ELEMENT_PROP_CHECK_FORE_COLOR,                // Цвет рамки флажка проверки элемента управления
   CANV_ELEMENT_PROP_CHECK_FORE_COLOR_OPACITY,        // Непрозрачность цвета рамки флажка проверки элемента управления
   CANV_ELEMENT_PROP_CHECK_FORE_COLOR_MOUSE_DOWN,     // Цвет рамки флажка проверки элемента управления при нажатии мышки на элемент управления
   CANV_ELEMENT_PROP_CHECK_FORE_COLOR_MOUSE_OVER,     // Цвет рамки флажка проверки элемента управления при наведении мышки на элемент управления
   CANV_ELEMENT_PROP_CHECK_FLAG_COLOR,                // Цвет флажка проверки элемента управления
   CANV_ELEMENT_PROP_CHECK_FLAG_COLOR_OPACITY,        // Непрозрачность цвета флажка проверки элемента управления
   CANV_ELEMENT_PROP_CHECK_FLAG_COLOR_MOUSE_DOWN,     // Цвет флажка проверки элемента управления при нажатии мышки на элемент управления
   CANV_ELEMENT_PROP_CHECK_FLAG_COLOR_MOUSE_OVER,     // Цвет флажка проверки элемента управления при наведении мышки на элемент управления
   
  };
#define CANV_ELEMENT_PROP_INTEGER_TOTAL (71)          // Общее количество целочисленных свойств
#define CANV_ELEMENT_PROP_INTEGER_SKIP  (0)           // Количество неиспользуемых в сортировке целочисленных свойств


В список возможных критериев сортировки графических элементов на канвасе впишем новые критерии, соответствующие вновь добавленным свойствам:

//+------------------------------------------------------------------+
//| Возможные критерии сортировки графических элементов на канвасе   |
//+------------------------------------------------------------------+
#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_CANV_ELEMENT_ID = 0,                       // Сортировать по идентификатору элемента
   SORT_BY_CANV_ELEMENT_TYPE,                         // Сортировать по типу графического элемента

//--- ...
   SORT_BY_CANV_ELEMENT_FORE_COLOR,                   // Сортировать по цвету текста по умолчанию для всех объектов элемента управления
   SORT_BY_CANV_ELEMENT_FORE_COLOR_OPACITY,           // Сортировать по непрозрачности цвета текста по умолчанию для всех объектов элемента управления
   
   SORT_BY_CANV_ELEMENT_BACKGROUND_COLOR,             // Сортировать по цвету текста фона элемента управления
   SORT_BY_CANV_ELEMENT_BACKGROUND_COLOR_OPACITY,     // Сортировать по непрозрачности цвета фона фона элемента управления
   SORT_BY_CANV_ELEMENT_BACKGROUND_COLOR_MOUSE_DOWN,  // Сортировать по цвету текста фона элемента управления при нажатии мышки на элемент управления
   SORT_BY_CANV_ELEMENT_BACKGROUND_COLOR_MOUSE_OVER,  // Сортировать по цвету текста фона элемента управления при наведении мышки на элемент управления
   
   SORT_BY_CANV_ELEMENT_BOLD_TYPE,                    // Сортировать по типу толщины шрифта
   SORT_BY_CANV_ELEMENT_BORDER_STYLE,                 // Сортировать по стилю рамки элемента управления
   
   SORT_BY_CANV_ELEMENT_BORDER_SIZE_TOP,              // Сортировать по размеру рамки элемента управления сверху
   SORT_BY_CANV_ELEMENT_BORDER_SIZE_BOTTOM,           // Сортировать по размеру рамки элемента управления сверху
   SORT_BY_CANV_ELEMENT_BORDER_SIZE_LEFT,             // Сортировать по размеру рамки элемента управления сверху
   SORT_BY_CANV_ELEMENT_BORDER_SIZE_RIGHT,            // Сортировать по размеру рамки элемента управления сверху
   SORT_BY_CANV_ELEMENT_BORDER_COLOR,                 // Сортировать по цвету рамки элемента управления
   SORT_BY_CANV_ELEMENT_BORDER_COLOR_MOUSE_DOWN,      // Сортировать по цвету рамки элемента управления при нажатии мышки на элемент управления
   SORT_BY_CANV_ELEMENT_BORDER_COLOR_MOUSE_OVER,      // Сортировать по цвету рамки элемента управления при наведении мышки на элемент управления
   
   SORT_BY_CANV_ELEMENT_AUTOSIZE,                     // Сортировать по флагу автоматического изменения размера элемента управления под содержимое
   SORT_BY_CANV_ELEMENT_AUTOSIZE_MODE,                // Сортировать по режиму автоматического изменения размера элемента управления под содержимое
//--- ...

   SORT_BY_CANV_ELEMENT_CHECK_STATE,                  // Сортировать по состоянию элемента управления, имеющего флажок проверки
   SORT_BY_CANV_ELEMENT_AUTOCHECK,                    // Сортировать по автоматическому изменению состояния флажка при его выборе
   
   SORT_BY_CANV_ELEMENT_CHECK_BACKGROUND_COLOR,          // Сортировать по цвету фона флажка проверки элемента управления
   SORT_BY_CANV_ELEMENT_CHECK_BACKGROUND_COLOR_OPACITY,  // Сортировать по непрозрачности цвета фона флажка проверки элемента управления
   SORT_BY_CANV_ELEMENT_CHECK_BACKGROUND_COLOR_MOUSE_DOWN,// Сортировать по цвету фона флажка проверки элемента управления при нажатии мышки на элемент управления
   SORT_BY_CANV_ELEMENT_CHECK_BACKGROUND_COLOR_MOUSE_OVER,// Сортировать по цвету фона флажка проверки элемента управления при наведении мышки на элемент управления
   SORT_BY_CANV_ELEMENT_CHECK_FORE_COLOR,                // Сортировать по цвету рамки флажка проверки элемента управления
   SORT_BY_CANV_ELEMENT_CHECK_FORE_COLOR_OPACITY,        // Сортировать по непрозрачности цвета рамки флажка проверки элемента управления
   SORT_BY_CANV_ELEMENT_CHECK_FORE_COLOR_MOUSE_DOWN,     // Сортировать по цвету рамки флажка проверки элемента управления при нажатии мышки на элемент управления
   SORT_BY_CANV_ELEMENT_CHECK_FORE_COLOR_MOUSE_OVER,     // Сортировать по цвету рамки флажка проверки элемента управления при наведении мышки на элемент управления
   SORT_BY_CANV_ELEMENT_CHECK_FLAG_COLOR,                // Сортировать по цвету флажка проверки элемента управления
   SORT_BY_CANV_ELEMENT_CHECK_FLAG_COLOR_OPACITY,        // Сортировать по непрозрачности цвета флажка проверки элемента управления
   SORT_BY_CANV_ELEMENT_CHECK_FLAG_COLOR_MOUSE_DOWN,     // Сортировать по цвету флажка проверки элемента управления при нажатии мышки на элемент управления
   SORT_BY_CANV_ELEMENT_CHECK_FLAG_COLOR_MOUSE_OVER,     // Сортировать по цвету флажка проверки элемента управления при наведении мышки на элемент управления
//--- Сортировка по вещественным свойствам

//--- Сортировка по строковым свойствам
   SORT_BY_CANV_ELEMENT_NAME_OBJ = FIRST_CANV_ELEMENT_STR_PROP,// Сортировать по имени объекта-элемента
   SORT_BY_CANV_ELEMENT_NAME_RES,                     // Сортировать по имени графического ресурса
   SORT_BY_CANV_ELEMENT_TEXT,                         // Сортировать по тексту графического элемента
  };
//+------------------------------------------------------------------+

Теперь мы сможем искать, выбирать и сортировать графические элементы по новым свойствам.


В файле \MQL5\Include\DoEasy\Data.mqh впишем индексы новых сообщений:

   MSG_LIB_TEXT_NOVEMBER,                             // Ноябрь
   MSG_LIB_TEXT_DECEMBER,                             // Декабрь

   MSG_LIB_TEXT_FONT_STYLE_ITALIC,                    // Курсив
   MSG_LIB_TEXT_FONT_STYLE_UNDERLINE,                 // Подчёркивание
   MSG_LIB_TEXT_FONT_STYLE_STRIKEOUT,                 // Перечёркивание
   MSG_LIB_TEXT_FONT_STYLE_NORMAL,                    // Обычный
   
   MSG_LIB_TEXT_FRAME_STYLE_NONE,                     // Отсутствует
   MSG_LIB_TEXT_FRAME_STYLE_SIMPLE,                   // Простая
   MSG_LIB_TEXT_FRAME_STYLE_FLAT,                     // Плоская
   MSG_LIB_TEXT_FRAME_STYLE_BEVEL,                    // Рельефная выпуклая
   MSG_LIB_TEXT_FRAME_STYLE_STAMP,                    // Рельефная вдавленная
   
   MSG_LIB_TEXT_ALIGN_LEFT,                           // Выравнивание по левой границе 
   MSG_LIB_TEXT_ALIGN_CENTER,                         // Выравнивание по центру

...

   MSG_LIB_TEXT_BORDER_RAISED,                        // Выпуклый вид
   MSG_LIB_TEXT_BORDER_SUNKEN,                        // Вогнутый вид
   
   MSG_LIB_TEXT_AUTO_SIZE_MODE_GROW,                  // Только увеличение
   MSG_LIB_TEXT_AUTO_SIZE_MODE_GROW_SHRINK,           // Увеличение и уменьшение
   
   MSG_LIB_TEXT_DOCK_MODE_NONE,                       // Прикреплён к указанным координатам, размеры не меняются
   MSG_LIB_TEXT_DOCK_MODE_TOP,                        // Присоединение сверху и растягивание на ширину контейнера
   MSG_LIB_TEXT_DOCK_MODE_BOTTOM,                     // Присоединение снизу и растягивание на ширину контейнера
   MSG_LIB_TEXT_DOCK_MODE_LEFT,                       // Присоединение слева и растягивание на высоту контейнера
   MSG_LIB_TEXT_DOCK_MODE_RIGHT,                      // Присоединение справа и растягивание на высоту контейнера
   MSG_LIB_TEXT_DOCK_MODE_FILL,                       // Растягивание на ширину и высоту всего контейнера
   
   MSG_LIB_TEXT_CHEK_STATE_UNCHECKED,                 // Не установлен
   MSG_LIB_TEXT_CHEK_STATE_CHECKED,                   // Установлен
   MSG_LIB_TEXT_CHEK_STATE_INDETERMINATE,             // Неопределённый
   
   MSG_LIB_TEXT_SUNDAY,                               // Воскресение
   MSG_LIB_TEXT_MONDAY,                               // Понедельник

...

   MSG_GRAPH_ELEMENT_TYPE_WF_UNDERLAY,                // Подложка объекта-элемента управления WinForms "Панель"
   MSG_GRAPH_ELEMENT_TYPE_WF_BASE,                    // Базовый элемент управления WinForms
   
//--- WinForms-контейнеры
   MSG_GRAPH_ELEMENT_TYPE_WF_CONTAINER,               // Базовый элемент управления WinForms-контейнер
   MSG_GRAPH_ELEMENT_TYPE_WF_GROUPBOX,                // Элемент управления GroupBox
   MSG_GRAPH_ELEMENT_TYPE_WF_PANEL,                   // Элемент управления Panel
   
//--- WinForms-стандартные
   MSG_GRAPH_ELEMENT_TYPE_WF_COMMON_BASE,             // Базовый стандартный элемент управления WinForms
   MSG_GRAPH_ELEMENT_TYPE_WF_LABEL,                   // Элемент управления Label
   MSG_GRAPH_ELEMENT_TYPE_WF_CHECKBOX,                // Элемент управления CheckBox
   MSG_GRAPH_ELEMENT_TYPE_WF_RADIOBUTTON,             // Элемент управления RadioButton
   MSG_GRAPH_ELEMENT_TYPE_WF_BUTTON,                  // Элемент управления Button
   
   MSG_GRAPH_OBJ_BELONG_PROGRAM,                      // Графический объект принадлежит программе
   MSG_GRAPH_OBJ_BELONG_NO_PROGRAM,                   // Графический объект не принадлежит программе

...

//--- CPanel
   MSG_PANEL_OBJECT_ERR_FAILED_CREATE_UNDERLAY_OBJ,   // Не удалось создать объект-подложку
   MSG_PANEL_OBJECT_ERR_OBJ_MUST_BE_WFBASE,           // Ошибка. Создаваемый объект должен иметь тип WinForms Base или быть его наследником

//--- Целочисленные свойства графических элементов
   MSG_CANV_ELEMENT_PROP_ID,                          // Идентификатор элемента
   MSG_CANV_ELEMENT_PROP_TYPE,                        // Тип графического элемента
   MSG_CANV_ELEMENT_PROP_BELONG,                      // Принадлежность графического элемента
   MSG_CANV_ELEMENT_PROP_NUM,                         // Номер элемента в списке
   MSG_CANV_ELEMENT_PROP_COORD_X,                     // X-координата элемента на графике
   MSG_CANV_ELEMENT_PROP_COORD_Y,                     // Y-координата элемента на графике
   MSG_CANV_ELEMENT_PROP_WIDTH,                       // Ширина элемента
   MSG_CANV_ELEMENT_PROP_HEIGHT,                      // Высота элемента
   MSG_CANV_ELEMENT_PROP_RIGHT,                       // Правая граница элемента
   MSG_CANV_ELEMENT_PROP_BOTTOM,                      // Нижняя граница элемента
   MSG_CANV_ELEMENT_PROP_ACT_SHIFT_LEFT,              // Отступ активной зоны от левого края элемента
   MSG_CANV_ELEMENT_PROP_ACT_SHIFT_TOP,               // Отступ активной зоны от верхнего края элемента
   MSG_CANV_ELEMENT_PROP_ACT_SHIFT_RIGHT,             // Отступ активной зоны от правого края элемента
   MSG_CANV_ELEMENT_PROP_ACT_SHIFT_BOTTOM,            // Отступ активной зоны от нижнего края элемента
   MSG_CANV_ELEMENT_PROP_MOVABLE,                     // Флаг перемещаемости элемента
   MSG_CANV_ELEMENT_PROP_IS_MOVABLE,                  // Элемент перемещаемый
   MSG_CANV_ELEMENT_PROP_ACTIVE,                      // Флаг активности элемента
   MSG_CANV_ELEMENT_PROP_IS_ACTIVE,                   // Элемент активен
   MSG_CANV_ELEMENT_PROP_INTERACTION,                 // Флаг взаимодействия элемента со внешней средой
   MSG_CANV_ELEMENT_PROP_COORD_ACT_X,                 // X-координата активной зоны элемента
   MSG_CANV_ELEMENT_PROP_COORD_ACT_Y,                 // Y-координата активной зоны элемента
   MSG_CANV_ELEMENT_PROP_ACT_RIGHT,                   // Правая граница активной зоны элемента
   MSG_CANV_ELEMENT_PROP_ACT_BOTTOM,                  // Нижняя граница активной зоны элемента
   MSG_CANV_ELEMENT_PROP_ENABLED,                     // Флаг доступности элемента
   MSG_CANV_ELEMENT_PROP_FORE_COLOR,                  // Цвет текста по умолчанию для всех объектов элемента управления
   MSG_CANV_ELEMENT_PROP_FORE_COLOR_OPACITY,          // Непрозрачность цвета текста по умолчанию для всех объектов элемента управления
   MSG_CANV_ELEMENT_PROP_BACKGROUND_COLOR,            // Цвет фона элемента управления
   MSG_CANV_ELEMENT_PROP_BACKGROUND_COLOR_OPACITY,    // Непрозрачность цвета фона элемента управления
   MSG_CANV_ELEMENT_PROP_BACKGROUND_COLOR_MOUSE_DOWN, // Цвет фона элемента управления при нажатии мышки на элемент управления
   MSG_CANV_ELEMENT_PROP_BACKGROUND_COLOR_MOUSE_OVER, // Цвет фона элемента управления при наведении мышки на элемент управления
   MSG_CANV_ELEMENT_PROP_BOLD_TYPE,                   // Тип толщины шрифта
   MSG_CANV_ELEMENT_PROP_BORDER_STYLE,                // Стиль рамки элемента управления
   MSG_CANV_ELEMENT_PROP_BORDER_SIZE_TOP,             // Размер рамки элемента управления сверху
   MSG_CANV_ELEMENT_PROP_BORDER_SIZE_BOTTOM,          // Размер рамки элемента управления снизу
   MSG_CANV_ELEMENT_PROP_BORDER_SIZE_LEFT,            // Размер рамки элемента управления слева
   MSG_CANV_ELEMENT_PROP_BORDER_SIZE_RIGHT,           // Размер рамки элемента управления справа
   MSG_CANV_ELEMENT_PROP_BORDER_COLOR,                // Цвет рамки элемента управления
   MSG_CANV_ELEMENT_PROP_BORDER_COLOR_MOUSE_DOWN,     // Цвет рамки элемента управления при нажатии мышки на элемент управления
   MSG_CANV_ELEMENT_PROP_BORDER_COLOR_MOUSE_OVER,     // Цвет рамки элемента управления при наведении мышки на элемент управления
   MSG_CANV_ELEMENT_PROP_AUTOSIZE,                    // Флаг автоматического изменения размера элемента управления под содержимое
   MSG_CANV_ELEMENT_PROP_IS_AUTOSIZE,                 // Элемент автоматически измененяет размер под содержимое
   MSG_CANV_ELEMENT_PROP_AUTOSIZE_MODE,               // Режим автоматического изменения размера элемента управления под содержимое
   MSG_CANV_ELEMENT_PROP_AUTOSCROLL,                  // Флаг автоматического появления полосы прокрутки
   MSG_CANV_ELEMENT_PROP_IS_AUTOSCROLL,               // Полоса прокрутки автоматически появляется 
   MSG_CANV_ELEMENT_PROP_AUTOSCROLL_MARGIN_W,         // Ширина поля вокруг элемента при автоматической прокрутке
   MSG_CANV_ELEMENT_PROP_AUTOSCROLL_MARGIN_H,         // Высота поля вокруг элемента при автоматической прокрутке
   MSG_CANV_ELEMENT_PROP_DOCK_MODE,                   // Режим привязки границ элемента управления к контейнеру
   MSG_CANV_ELEMENT_PROP_MARGIN_TOP,                  // Промежуток сверху между полями данного и другого элемента управления
   MSG_CANV_ELEMENT_PROP_MARGIN_BOTTOM,               // Промежуток снизу между полями данного и другого элемента управления
   MSG_CANV_ELEMENT_PROP_MARGIN_LEFT,                 // Промежуток слева между полями данного и другого элемента управления
   MSG_CANV_ELEMENT_PROP_MARGIN_RIGHT,                // Промежуток справа между полями данного и другого элемента управления
   MSG_CANV_ELEMENT_PROP_PADDING_TOP,                 // Промежуток сверху внутри элемента управления
   MSG_CANV_ELEMENT_PROP_PADDING_BOTTOM,              // Промежуток снизу внутри элемента управления
   MSG_CANV_ELEMENT_PROP_PADDING_LEFT,                // Промежуток слева внутри элемента управления
   MSG_CANV_ELEMENT_PROP_PADDING_RIGHT,               // Промежуток справа внутри элемента управления
   MSG_CANV_ELEMENT_PROP_TEXT_ALIGN,                  // Положение текста в границах текстовой метки
   MSG_CANV_ELEMENT_PROP_CHECK_ALIGN,                 // Положение флажка проверки в границах элемента управления
   MSG_CANV_ELEMENT_PROP_CHECKED,                     // Состояние флажка проверки элемента управления
   MSG_CANV_ELEMENT_PROP_CHECK_STATE,                 // Состояние элемента управления, имеющего флажок проверки
   MSG_CANV_ELEMENT_PROP_AUTOCHECK,                   // Автоматическое изменение состояния флажка при его выборе
   
//--- Вещественные свойства графических элементов

//--- Строковые свойства графических элементов
   MSG_CANV_ELEMENT_PROP_NAME_OBJ,                    // Имя объекта-графического элемента
   MSG_CANV_ELEMENT_PROP_NAME_RES,                    // Имя графического ресурса
   MSG_CANV_ELEMENT_PROP_TEXT,                        // Текст графического элемента

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

и тексты сообщений, соответствующие вновь добавленным индексам:

   {"Ноябрь","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-контейнеры
   {"Базовый элемент управления WinForms-контейнер","Basic Control WinForms Container"},
   {"Элемент управления GroupBox","Control element \"GroupBox\""},
   {"Элемент управления \"Panel\"","Control element \"Panel\""},
   
//--- WinForms-стандартные
   {"Базовый стандартный элемент управления 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"},

//--- Целочисленные свойства графических элементов
   {"Идентификатор элемента","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"},

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

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

Все эти сообщения нам в дальнейшем потребуются для вывода описания свойств графических элементов управления.


Описание типов элементов управления у нас реализовано в методе, возвращающем описание типа графического элемента, класса базового графического объекта библиотеки в файле \MQL5\Include\DoEasy\Objects\Graph\GBaseObj.mqh. Дополним его строками, возвращающими описания новых типов:

//+------------------------------------------------------------------+
//| Возвращает описание типа графического элемента                   |
//+------------------------------------------------------------------+
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"
     );
  }
//+------------------------------------------------------------------+

В зависимости от типа графического элемента метод возвращает строку с описанием этого типа.

У нас есть некрасивые нестыковки в наименованиях методов, устанавливающих или возвращающих цвет. Например, метод ForeColor() возвращает цвет текста. В то же время, метод, возвращающий цвет фона, имеет наименование ColorBackground(). Чтобы привести наименования в соответствия друг с другом, метод ColorBackground(), и ему подобные, переименуем в BackgroundColor(). Помимо этого, мы ввели новые свойства графических элементов, и в них есть свойства цвета при наведении и нажатии курсора мышки на объект. Методы для установки и возврата этих, да и всех других новых свойств, нам нужно тоже добавить.

Откроем файл класса объекта графического элемента \MQL5\Include\DoEasy\Objects\Graph\GCnvElement.mqh.

Так как у нас цвет фона может быть градиентным, то для хранения всех цветов градиента мы используем массив цветов. Нам нужно добавить ещё два таких массива — для хранения цветов градиента фона при наведении и нажатии указателя мышки на объект.
В защищённой секции класса объявим два таких массива:

//+------------------------------------------------------------------+
//| Класс объекта графического элемента                              |
//+------------------------------------------------------------------+
class CGCnvElement : public CGBaseObj
  {
protected:
   CGCnvElement     *m_element_main;                           // Указатель на первоначальный родительский элемент в составе всех групп связанных объектов
   CGCnvElement     *m_element_base;                           // Указатель на родительский элемент в составе связанных объектов текущей группы
   CCanvas           m_canvas;                                 // Объект класса CCanvas
   CPause            m_pause;                                  // Объект класса "Пауза"
   bool              m_shadow;                                 // Наличие тени
   color             m_chart_color_bg;                         // Цвет фона графика
   uint              m_duplicate_res[];                        // Массив для хранения копии данных ресурса
   color             m_array_colors_bg[];                      // Массив цветов фона элемента
   color             m_array_colors_bg_dwn[];                  // Массив цветов фона элемента при нажатии мышки на элемент управления
   color             m_array_colors_bg_ovr[];                  // Массив цветов фона элемента при наведении мышки на элемент управления
   bool              m_gradient_v;                             // Флаг вертикальной градиентной заливки
   bool              m_gradient_c;                             // Флаг циклической градиентной заливки
   int               m_init_relative_x;                        // Первоначальная относительная координата X
   int               m_init_relative_y;                        // Первоначальная относительная координата Y

//--- Создаёт (1) структуру объекта, (2) объект из структуры
   virtual bool      ObjectToStruct(void);
   virtual void      StructToObject(void);
   
private:


У нас есть структура объекта, в которую записываются все свойства созданного графического элемента для последующего сохранения свойств объекта на носителе и чтения с него для восстановления объекта при перезапусках терминала и программы.

Впишем в структуру все новые свойства:

private:
   int               m_shift_coord_x;                          // Смещение координаты X относительно базового объекта
   int               m_shift_coord_y;                          // Смещение координаты Y относительно базового объекта
   struct SData
     {
      //--- Целочисленные свойства объекта
      int            id;                                       // Идентификатор элемента
      int            type;                                     // Тип графического элемента
      int            belong;                                   // Принадлежность графического элемента
      int            number;                                   // Номер элемента в списке
      long           chart_id;                                 // Идентификатор графика
      int            subwindow;                                // Номер подокна графика
      int            coord_x;                                  // X-координата элемента на графике
      int            coord_y;                                  // Y-координата элемента на графике
      int            width;                                    // Ширина элемента
      int            height;                                   // Высота элемента
      int            edge_right;                               // Правая граница элемента
      int            edge_bottom;                              // Нижняя граница элемента
      int            act_shift_left;                           // Отступ активной зоны от левого края элемента
      int            act_shift_top;                            // Отступ активной зоны от верхнего края элемента
      int            act_shift_right;                          // Отступ активной зоны от правого края элемента
      int            act_shift_bottom;                         // Отступ активной зоны от нижнего края элемента
      bool           movable;                                  // Флаг перемещаемости элемента
      bool           active;                                   // Флаг активности элемента
      bool           interaction;                              // Флаг взаимодействия элемента с внешней средой
      int            coord_act_x;                              // X-координата активной зоны элемента
      int            coord_act_y;                              // Y-координата активной зоны элемента
      int            coord_act_right;                          // Правая граница активной зоны элемента
      int            coord_act_bottom;                         // Нижняя граница активной зоны элемента
      long           zorder;                                   // Приоритет графического объекта на получение события нажатия мышки на графике
      bool           enabled;                                  // Флаг доступности элемента
      color          fore_color;                               // Цвет текста по умолчанию для всех объектов элемента управления
      uchar          fore_color_opacity;                       // Непрозрачность цвета текста по умолчанию для всех объектов элемента управления
      color          background_color;                         // Цвет фона элемента управления
      uchar          background_color_opacity;                 // Непрозрачность цвета фона элемента управления
      color          background_color_mouse_down;              // Цвет фона элемента управления при нажатии мышки на элемент управления
      color          background_color_mouse_over;              // Цвет фона элемента управления при наведении мышки на элемент управления
      int            bold_type;                                // Тип толщины шрифта
      int            border_style;                             // Стиль рамки элемента управления
      int            border_size_top;                          // Размер рамки элемента управления сверху
      int            border_size_bottom;                       // Размер рамки элемента управления снизу
      int            border_size_left;                         // Размер рамки элемента управления слева
      int            border_size_right;                        // Размер рамки элемента управления справа
      color          border_color;                             // Цвет рамки элемента управления
      color          border_color_mouse_down;                  // Цвет рамки элемента управления при нажатии мышки на элемент управления
      color          border_color_mouse_over;                  // Цвет рамки элемента управления при наведении мышки на элемент управления
      bool           autosize;                                 // Флаг автоматического изменения размера элемента управления под содержимое
      int            autosize_mode;                            // Режим автоматического изменения размера элемента управления под содержимое
      bool           autoscroll;                               // Флаг автоматического появления полосы прокрутки
      int            autoscroll_margin_w;                      // Ширина поля вокруг элемента при автоматической прокрутке
      int            autoscroll_margin_h;                      // Высота поля вокруг элемента при автоматической прокрутке
      int            dock_mode;                                // Режим привязки границ элемента управления к контейнеру
      int            margin_top;                               // Промежуток сверху между полями данного и другого элемента управления
      int            margin_bottom;                            // Промежуток снизу между полями данного и другого элемента управления
      int            margin_left;                              // Промежуток слева между полями данного и другого элемента управления
      int            margin_right;                             // Промежуток справа между полями данного и другого элемента управления
      int            padding_top;                              // Промежуток сверху внутри элемента управления
      int            padding_bottom;                           // Промежуток снизу внутри элемента управления
      int            padding_left;                             // Промежуток слева внутри элемента управления
      int            padding_right;                            // Промежуток справа внутри элемента управления
      int            text_align;                               // Положение текста в границах текстовой метки
      int            check_align;                              // Положение флажка проверки в границах элемента управления
      bool           checked;                                  // Состояние флажка проверки элемента управления
      int            check_state;                              // Состояние элемента управления, имеющего флажок проверки
      bool           autocheck;                                // Автоматическое изменение состояния флажка при его выборе
      color          check_background_color;                   // Цвет фона флажка проверки элемента управления
      color          check_background_color_opacity;           // Непрозрачность цвета фона флажка проверки элемента управления
      color          check_background_color_mouse_down;        // Цвет фона флажка проверки элемента управления при нажатии мышки на элемент управления
      color          check_background_color_mouse_over;        // Цвет фона флажка проверки элемента управления при наведении мышки на элемент управления
      color          check_fore_color;                         // Цвет рамки флажка проверки элемента управления
      color          check_fore_color_opacity;                 // Непрозрачность цвета рамки флажка проверки элемента управления
      color          check_fore_color_mouse_down;              // Цвет рамки флажка проверки элемента управления при нажатии мышки на элемент управления
      color          check_fore_color_mouse_over;              // Цвет рамки флажка проверки элемента управления при наведении мышки на элемент управления
      color          check_flag_color;                         // Цвет флажка проверки элемента управления
      color          check_flag_color_opacity;                 // Непрозрачность цвета флажка проверки элемента управления
      color          check_flag_color_mouse_down;              // Цвет флажка проверки элемента управления при нажатии мышки на элемент управления
      color          check_flag_color_mouse_over;              // Цвет флажка проверки элемента управления при наведении мышки на элемент управления
      
      //--- Вещественные свойства объекта

      //--- Строковые свойства объекта
      uchar          name_obj[64];                             // Имя объекта-графического элемента
      uchar          name_res[64];                             // Имя графического ресурса
      uchar          text[256];                                // Текст графического элемента
     };
   SData             m_struct_obj;                             // Структура объекта
   uchar             m_uchar_array[];                          // uchar-массив структуры объекта

Теперь у нас есть все необходимые поля свойств в структуре для правильного сохранения и восстановления объекта из структуры. Записью свойств объектов в файлы мы будем заниматься много позже.

Из приватной секции класса удалим две переменные для хранения цвета фона и его непрозрачности, так как теперь значения этих переменных хранятся непосредственно в свойствах объекта:

   ENUM_FRAME_ANCHOR m_text_anchor;                            // Текущее выравнивание текста
   int               m_text_x;                                 // Последняя координата X текста
   int               m_text_y;                                 // Последняя координата Y текста
   color             m_color_bg;                               // Цвет фона элемента
   uchar             m_opacity;                                // Непрозрачность элемента
   
//--- Возвращает индекс массива, по которому фактически расположено (1) double-свойство и (2) string-свойство ордера


Ранее у нас был один метод SaveColorsBG для сохраняения цвета в массив цветов фона. Теперь же у нас будут несколько различных градиентов, цвета которых необходимо будет сохранять в массивы. Поэтому объявим метод, копирующий массив цветов в указанный массив цветов фона и добавим ещё два метода для сохранения градиентных цветов фона:

//--- Возвращает индекс массива, по которому фактически расположено (1) double-свойство и (2) string-свойство ордера
   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;  }

//--- Копирует массив цветов в указанный массив цветов фона
   void              CopyArraysColors(color &array_dst[],const color &array_src[],const string source);
//--- Сохраняет цвета в массив цветов фона
   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:

Первый метод копирует цвета из переданного в метод массива в массив, указанный в параметрах метода, а два других копируют массив цветов, переданный в методы, в соответствующие массивы градиентных цветов, объявленные нами выше.

В публичной секции класса из метода, создающего графический элемент, удалим две переменные из его входных параметров, так как они нигде в методе не используются:

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


Добавим два метода, возвращающие флаги того, что объект является главным и (или) базовым:

//--- (1) Устанавливает, (2) возвращает указатель на родительский элемент в составе всех групп связанных объектов
   void              SetMain(CGCnvElement *element)                                    { this.m_element_main=element;               }
   CGCnvElement     *GetMain(void)                                                     { return this.m_element_main;                }
//--- Возвращает флаг, что объект является (1) главным, (2) базовым
   bool              IsMain(void)                                                      { return this.GetMain()==NULL;               }
   bool              IsBase(void)                                                      { return this.GetBase()==NULL;               }
//--- Возвращает указатель на объект-канвас
   CCanvas          *GetCanvasObj(void)                                                { return &this.m_canvas;                     }

Методы просто проверяют является ли указатель на главный и базовый объекты равными NULL.
При таком значении указателя объякт сам является таковым — главным или базовым, так как если объект привязан к какому-либо объекту, то указатель на главный и базовый объекты прописываются в переменных, возвращаемых методами GetMain() и GetBase(). Поэтому, если указатель равен NULL, то объект ни к какому другому не привязан, и может являться для других объектов иерархии либо главным (самый первый в иерархии связанных объектов), либо базовым (к нему привязаны другие объекты, но он сам не является главным, так как в свою очередь привязан к другому объекту в общей цепочке всей иерархии).

Переименуем метод, устанавливающий цвет фона, и добавим методы для установки цвета фона при нажатии или наведении курсора на объект:

//--- (5) все смещения краёв активной зоны относительно элемента, (6) непрозрачность
   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);
//--- Устанавливает основной цвет фона
   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]);
                       }
//--- Устанавливает цвет фона при нажатии мышки на элемент управления
   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]);
                       }
//--- Устанавливает цвет фона при наведении мышки на элемент управления
   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]);
                       }
//--- Устанавливает флаг (1) перемещаемости, (2) активности объекта, (3) флаг взаимодействия,

Теперь, вместо записи значений в удаённые переменные, мы записываем значения в свойства объекта. Эти методы мы рассматривали ранее в прошлых статьях и заново возвращаться к их описанию здесь, думаю, не обязательно.

Точно так же переименуем методы, возвращающие значения цветов фона и напишем новые методы для возврата значений дополнительных цветов фона:

//--- Возвращает количество цветов, установленных для градиентной заливки (1) основного фона, при нажатии (2), (3) наведении мышки на элемент управления
   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();                             }
//--- Возвращает цвет основного фона
   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]);
                       }
                       
//--- Возвращает цвет фона при нажатии мышки на элемент управления
   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]);
                       }
//--- Возвращает цвет фона при наведении мышки на элемент управления
   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]);
                       }
//--- Возвращает (1) непрозрачность, координату (2) правого, (3) нижнего края элемента
   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();                          }

Здесь мы так же возвращаем значения не переменных, теперь удалённых, а значения, записанные в свойствах объекта. Такие методы мы тоже уже рассматривали ранее, и здесь повторяться не будем.


Метод ChartColorBackground() тоже переименуем (как и все другие, которые возвращают параметры цвета, и их наименования не придерживаются общего принципа построения наименования методов для работы с цветом):

//--- Возвращает (1) идентификатор элемента, (2) номер элемента в списке, (3) флаг наличия тени у формы, (4) цвет фона чарта
   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;                                         }
//--- Устанавливает объект выше всех


В параметрическом конструкторе класса заменим запись значений в переменные вызовом методов, это делающих, впишем сохранение цвета фона в массивы дополнительных цветов фона (при нажатии и наведении курсора на объект), и инициализируем значениями по умолчанию все новые свойства графического элемента:

//+------------------------------------------------------------------+
//| Параметрический конструктор                                      |
//+------------------------------------------------------------------+
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()); // Имя графического ресурса
      this.SetProperty(CANV_ELEMENT_PROP_CHART_ID,CGBaseObj::ChartID());         // Идентификатор графика
      this.SetProperty(CANV_ELEMENT_PROP_WND_NUM,CGBaseObj::SubWindow());        // Номер подокна графика
      this.SetProperty(CANV_ELEMENT_PROP_NAME_OBJ,CGBaseObj::Name());            // Имя объекта-элемента
      this.SetProperty(CANV_ELEMENT_PROP_TYPE,element_type);                     // Тип графического элемента
      this.SetProperty(CANV_ELEMENT_PROP_ID,element_id);                         // Идентификатор элемента
      this.SetProperty(CANV_ELEMENT_PROP_NUM,element_num);                       // Номер элемента в списке
      this.SetProperty(CANV_ELEMENT_PROP_COORD_X,x);                             // X-координата элемента на графике
      this.SetProperty(CANV_ELEMENT_PROP_COORD_Y,y);                             // Y-координата элемента на графике
      this.SetProperty(CANV_ELEMENT_PROP_WIDTH,w);                               // Ширина элемента
      this.SetProperty(CANV_ELEMENT_PROP_HEIGHT,h);                              // Высота элемента
      this.SetProperty(CANV_ELEMENT_PROP_ACT_SHIFT_LEFT,0);                      // Отступ активной зоны от левого края элемента
      this.SetProperty(CANV_ELEMENT_PROP_ACT_SHIFT_TOP,0);                       // Отступ активной зоны от верхнего края элемента
      this.SetProperty(CANV_ELEMENT_PROP_ACT_SHIFT_RIGHT,0);                     // Отступ активной зоны от правого края элемента
      this.SetProperty(CANV_ELEMENT_PROP_ACT_SHIFT_BOTTOM,0);                    // Отступ активной зоны от нижнего края элемента
      this.SetProperty(CANV_ELEMENT_PROP_MOVABLE,movable);                       // Флаг перемещаемости элемента
      this.SetProperty(CANV_ELEMENT_PROP_ACTIVE,activity);                       // Флаг активности элемента
      this.SetProperty(CANV_ELEMENT_PROP_INTERACTION,false);                     // Флаг взаимодействия элемента с внешней средой
      this.SetProperty(CANV_ELEMENT_PROP_ENABLED,true);                          // Флаг доступности элемента
      this.SetProperty(CANV_ELEMENT_PROP_RIGHT,this.RightEdge());                // Правая граница элемента
      this.SetProperty(CANV_ELEMENT_PROP_BOTTOM,this.BottomEdge());              // Нижняя граница элемента
      this.SetProperty(CANV_ELEMENT_PROP_COORD_ACT_X,this.ActiveAreaLeft());     // X-координата активной зоны элемента
      this.SetProperty(CANV_ELEMENT_PROP_COORD_ACT_Y,this.ActiveAreaTop());      // Y-координата активной зоны элемента
      this.SetProperty(CANV_ELEMENT_PROP_ACT_RIGHT,this.ActiveAreaRight());      // Правая граница активной зоны элемента
      this.SetProperty(CANV_ELEMENT_PROP_ACT_BOTTOM,this.ActiveAreaBottom());    // Нижняя граница активной зоны элемента
      //---
      this.SetProperty(CANV_ELEMENT_PROP_BELONG,ENUM_GRAPH_OBJ_BELONG::GRAPH_OBJ_BELONG_PROGRAM);  // Принадлежность графического элемента
      this.SetProperty(CANV_ELEMENT_PROP_ZORDER,0);                              // Приоритет графического объекта на получение события нажатия мышки на графике
      this.SetProperty(CANV_ELEMENT_PROP_BACKGROUND_COLOR_MOUSE_DOWN,this.BackgroundColor());   // Цвет фона элемента управления при нажатии мышки на элемент управления
      this.SetProperty(CANV_ELEMENT_PROP_BACKGROUND_COLOR_MOUSE_OVER,this.BackgroundColor());   // Цвет фона элемента управления при наведении мышки на элемент управления
      this.SetProperty(CANV_ELEMENT_PROP_BOLD_TYPE,FW_NORMAL);                   // Тип толщины шрифта
      this.SetProperty(CANV_ELEMENT_PROP_BORDER_STYLE,FRAME_STYLE_NONE);         // Стиль рамки элемента управления
      this.SetProperty(CANV_ELEMENT_PROP_BORDER_SIZE_TOP,0);                     // Размер рамки элемента управления сверху
      this.SetProperty(CANV_ELEMENT_PROP_BORDER_SIZE_BOTTOM,0);                  // Размер рамки элемента управления снизу
      this.SetProperty(CANV_ELEMENT_PROP_BORDER_SIZE_LEFT,0);                    // Размер рамки элемента управления слева
      this.SetProperty(CANV_ELEMENT_PROP_BORDER_SIZE_RIGHT,0);                   // Размер рамки элемента управления справа
      this.SetProperty(CANV_ELEMENT_PROP_BORDER_COLOR,this.BackgroundColor());   // Цвет рамки элемента управления
      this.SetProperty(CANV_ELEMENT_PROP_BORDER_COLOR_MOUSE_DOWN,this.BackgroundColor()); // Цвет рамки элемента управления при нажатии мышки на элемент управления
      this.SetProperty(CANV_ELEMENT_PROP_BORDER_COLOR_MOUSE_OVER,this.BackgroundColor()); // Цвет рамки элемента управления при наведении мышки на элемент управления
      this.SetProperty(CANV_ELEMENT_PROP_AUTOSIZE,false);                        // Флаг автоматического изменения размера элемента управления под содержимое
      this.SetProperty(CANV_ELEMENT_PROP_AUTOSIZE_MODE,CANV_ELEMENT_AUTO_SIZE_MODE_GROW); // Режим автоматического изменения размера элемента управления под содержимое
      this.SetProperty(CANV_ELEMENT_PROP_AUTOSCROLL,false);                      // Флаг автоматического появления полосы прокрутки
      this.SetProperty(CANV_ELEMENT_PROP_AUTOSCROLL_MARGIN_W,0);                 // Ширина поля вокруг элемента при автоматической прокрутке
      this.SetProperty(CANV_ELEMENT_PROP_AUTOSCROLL_MARGIN_H,0);                 // Высота поля вокруг элемента при автоматической прокрутке
      this.SetProperty(CANV_ELEMENT_PROP_DOCK_MODE,CANV_ELEMENT_DOCK_MODE_NONE); // Режим привязки границ элемента управления к контейнеру
      this.SetProperty(CANV_ELEMENT_PROP_MARGIN_TOP,0);                          // Промежуток сверху между полями данного и другого элемента управления
      this.SetProperty(CANV_ELEMENT_PROP_MARGIN_BOTTOM,0);                       // Промежуток снизу между полями данного и другого элемента управления
      this.SetProperty(CANV_ELEMENT_PROP_MARGIN_LEFT,0);                         // Промежуток слева между полями данного и другого элемента управления
      this.SetProperty(CANV_ELEMENT_PROP_MARGIN_RIGHT,0);                        // Промежуток справа между полями данного и другого элемента управления
      this.SetProperty(CANV_ELEMENT_PROP_PADDING_TOP,0);                         // Промежуток сверху внутри элемента управления
      this.SetProperty(CANV_ELEMENT_PROP_PADDING_BOTTOM,0);                      // Промежуток снизу внутри элемента управления
      this.SetProperty(CANV_ELEMENT_PROP_PADDING_LEFT,0);                        // Промежуток слева внутри элемента управления
      this.SetProperty(CANV_ELEMENT_PROP_PADDING_RIGHT,0);                       // Промежуток справа внутри элемента управления
      this.SetProperty(CANV_ELEMENT_PROP_TEXT_ALIGN,ANCHOR_LEFT_UPPER);          // Положение текста в границах текстовой метки
      this.SetProperty(CANV_ELEMENT_PROP_CHECK_ALIGN,ANCHOR_LEFT_UPPER);         // Положение флажка проверки в границах элемента управления
      this.SetProperty(CANV_ELEMENT_PROP_CHECKED,false);                         // Состояние флажка проверки элемента управления
      this.SetProperty(CANV_ELEMENT_PROP_CHECK_STATE,CANV_ELEMENT_CHEK_STATE_UNCHECKED);  // Состояние элемента управления, имеющего флажок проверки
      this.SetProperty(CANV_ELEMENT_PROP_AUTOCHECK,true);                        // Автоматическое изменение состояния флажка при его выборе
      //---
      this.SetProperty(CANV_ELEMENT_PROP_CHECK_BACKGROUND_COLOR,CLR_DEF_CHECK_BACK_COLOR);            // Цвет фона флажка проверки элемента управления
      this.SetProperty(CANV_ELEMENT_PROP_CHECK_BACKGROUND_COLOR_OPACITY,CLR_DEF_CHECK_BACK_OPACITY);  // Непрозрачность цвета фона флажка проверки элемента управления
      this.SetProperty(CANV_ELEMENT_PROP_CHECK_BACKGROUND_COLOR_MOUSE_DOWN,CLR_DEF_CHECK_BACK_MOUSE_DOWN);// Цвет фона флажка проверки элемента управления при нажатии мышки на элемент управления
      this.SetProperty(CANV_ELEMENT_PROP_CHECK_BACKGROUND_COLOR_MOUSE_OVER,CLR_DEF_CHECK_BACK_MOUSE_OVER);// Цвет фона флажка проверки элемента управления при наведении мышки на элемент управления
      this.SetProperty(CANV_ELEMENT_PROP_CHECK_FORE_COLOR,CLR_DEF_CHECK_FORE_COLOR);                  // Цвет рамки флажка проверки элемента управления
      this.SetProperty(CANV_ELEMENT_PROP_CHECK_FORE_COLOR_OPACITY,CLR_DEF_CHECK_FORE_OPACITY);        // Непрозрачность цвета рамки флажка проверки элемента управления
      this.SetProperty(CANV_ELEMENT_PROP_CHECK_FORE_COLOR_MOUSE_DOWN,CLR_DEF_CHECK_FORE_MOUSE_DOWN);  // Цвет рамки флажка проверки элемента управления при нажатии мышки на элемент управления
      this.SetProperty(CANV_ELEMENT_PROP_CHECK_FORE_COLOR_MOUSE_OVER,CLR_DEF_CHECK_FORE_MOUSE_OVER);  // Цвет рамки флажка проверки элемента управления при наведении мышки на элемент управления
      this.SetProperty(CANV_ELEMENT_PROP_CHECK_FLAG_COLOR,CLR_DEF_CHECK_FLAG_COLOR);                  // Цвет флажка проверки элемента управления
      this.SetProperty(CANV_ELEMENT_PROP_CHECK_FLAG_COLOR_OPACITY,CLR_DEF_CHECK_FLAG_OPACITY);        // Непрозрачность цвета флажка проверки элемента управления
      this.SetProperty(CANV_ELEMENT_PROP_CHECK_FLAG_COLOR_MOUSE_DOWN,CLR_DEF_CHECK_FLAG_MOUSE_DOWN);  // Цвет флажка проверки элемента управления при нажатии мышки на элемент управления
      this.SetProperty(CANV_ELEMENT_PROP_CHECK_FLAG_COLOR_MOUSE_OVER,CLR_DEF_CHECK_FLAG_MOUSE_OVER);  // Цвет флажка проверки элемента управления при наведении мышки на элемент управления
     }
   else
     {
      ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_FAILED_CREATE_ELM_OBJ),": ",this.m_name);
     }
  }
//+------------------------------------------------------------------+

В защищённом конструкторе пропишем всё точно также. Здесь практически точно такие же изменения, как и в вышерассмотренном конструкторе, и мы их здесь не будем рассматривать — с ними можно ознакомиться в прикреплённых к статье файлах библиотеки.


Метод, создающий структуру объекта, тоже был доработан для записи в поля структуры значений новых свойств объекта (просто рассмотрим его целиком):

//+------------------------------------------------------------------+
//| Создаёт структуру объекта                                        |
//+------------------------------------------------------------------+
bool CGCnvElement::ObjectToStruct(void)
  {
//--- Сохранение целочисленных свойств
   this.m_struct_obj.id=(int)this.GetProperty(CANV_ELEMENT_PROP_ID);                               // Идентификатор элемента
   this.m_struct_obj.type=(int)this.GetProperty(CANV_ELEMENT_PROP_TYPE);                           // Тип графического элемента
   this.m_struct_obj.belong=(int)this.GetProperty(CANV_ELEMENT_PROP_BELONG);                       // Принадлежность графического элемента
   this.m_struct_obj.number=(int)this.GetProperty(CANV_ELEMENT_PROP_NUM);                          // Номер элемента в списке
   this.m_struct_obj.chart_id=this.GetProperty(CANV_ELEMENT_PROP_CHART_ID);                        // Идентификатор графика
   this.m_struct_obj.subwindow=(int)this.GetProperty(CANV_ELEMENT_PROP_WND_NUM);                   // Номер подокна графика
   this.m_struct_obj.coord_x=(int)this.GetProperty(CANV_ELEMENT_PROP_COORD_X);                     // X-координата формы на графике
   this.m_struct_obj.coord_y=(int)this.GetProperty(CANV_ELEMENT_PROP_COORD_Y);                     // Y-координата формы на графике
   this.m_struct_obj.width=(int)this.GetProperty(CANV_ELEMENT_PROP_WIDTH);                         // Ширина элемента
   this.m_struct_obj.height=(int)this.GetProperty(CANV_ELEMENT_PROP_HEIGHT);                       // Высота элемента
   this.m_struct_obj.edge_right=(int)this.GetProperty(CANV_ELEMENT_PROP_RIGHT);                    // Правая граница элемента
   this.m_struct_obj.edge_bottom=(int)this.GetProperty(CANV_ELEMENT_PROP_BOTTOM);                  // Нижняя граница элемента
   this.m_struct_obj.act_shift_left=(int)this.GetProperty(CANV_ELEMENT_PROP_ACT_SHIFT_LEFT);       // Отступ активной зоны от левого края элемента
   this.m_struct_obj.act_shift_top=(int)this.GetProperty(CANV_ELEMENT_PROP_ACT_SHIFT_TOP);         // Отступ активной зоны от верхнего края элемента
   this.m_struct_obj.act_shift_right=(int)this.GetProperty(CANV_ELEMENT_PROP_ACT_SHIFT_RIGHT);     // Отступ активной зоны от правого края элемента
   this.m_struct_obj.act_shift_bottom=(int)this.GetProperty(CANV_ELEMENT_PROP_ACT_SHIFT_BOTTOM);   // Отступ активной зоны от нижнего края элемента
   this.m_struct_obj.movable=(bool)this.GetProperty(CANV_ELEMENT_PROP_MOVABLE);                    // Флаг перемещаемости элемента
   this.m_struct_obj.active=(bool)this.GetProperty(CANV_ELEMENT_PROP_ACTIVE);                      // Флаг активности элемента
   this.m_struct_obj.interaction=(bool)this.GetProperty(CANV_ELEMENT_PROP_INTERACTION);            // Флаг взаимодействия элемента с внешней средой
   this.m_struct_obj.coord_act_x=(int)this.GetProperty(CANV_ELEMENT_PROP_COORD_ACT_X);             // X-координата активной зоны элемента
   this.m_struct_obj.coord_act_y=(int)this.GetProperty(CANV_ELEMENT_PROP_COORD_ACT_Y);             // Y-координата активной зоны элемента
   this.m_struct_obj.coord_act_right=(int)this.GetProperty(CANV_ELEMENT_PROP_ACT_RIGHT);           // Правая граница активной зоны элемента
   this.m_struct_obj.coord_act_bottom=(int)this.GetProperty(CANV_ELEMENT_PROP_ACT_BOTTOM);         // Нижняя граница активной зоны элемента
   this.m_struct_obj.zorder=this.GetProperty(CANV_ELEMENT_PROP_ZORDER);                            // Приоритет графического объекта на получение события нажатия мышки на графике
   this.m_struct_obj.enabled=(bool)this.GetProperty(CANV_ELEMENT_PROP_ENABLED);                    // Флаг доступности элемента
   this.m_struct_obj.fore_color=(color)this.GetProperty(CANV_ELEMENT_PROP_FORE_COLOR);             // Цвет текста по умолчанию для всех объектов элемента управления
   this.m_struct_obj.fore_color_opacity=(uchar)this.GetProperty(CANV_ELEMENT_PROP_FORE_COLOR_OPACITY); // Непрозрачность цвета текста по умолчанию для всех объектов элемента управления
   this.m_struct_obj.background_color=(color)this.GetProperty(CANV_ELEMENT_PROP_BACKGROUND_COLOR); // Цвет фона элемента
   this.m_struct_obj.background_color_opacity=(uchar)this.GetProperty(CANV_ELEMENT_PROP_BACKGROUND_COLOR_OPACITY);      // Непрозрачность элемента
   this.m_struct_obj.background_color_mouse_down=(color)this.GetProperty(CANV_ELEMENT_PROP_BACKGROUND_COLOR_MOUSE_DOWN);// Цвет фона элемента управления при нажатии мышки на элемент управления
   this.m_struct_obj.background_color_mouse_over=(color)this.GetProperty(CANV_ELEMENT_PROP_BACKGROUND_COLOR_MOUSE_OVER);// Цвет фона элемента управления при наведении мышки на элемент управления
   this.m_struct_obj.bold_type=(int)this.GetProperty(CANV_ELEMENT_PROP_BOLD_TYPE);                 // Тип толщины шрифта
   this.m_struct_obj.border_style=(int)this.GetProperty(CANV_ELEMENT_PROP_BORDER_STYLE);           // Стиль рамки элемента управления
   this.m_struct_obj.border_size_top=(int)this.GetProperty(CANV_ELEMENT_PROP_BORDER_SIZE_TOP);     // Размер рамки элемента управления сверху
   this.m_struct_obj.border_size_bottom=(int)this.GetProperty(CANV_ELEMENT_PROP_BORDER_SIZE_BOTTOM);// Размер рамки элемента управления снизу
   this.m_struct_obj.border_size_left=(int)this.GetProperty(CANV_ELEMENT_PROP_BORDER_SIZE_LEFT);   // Размер рамки элемента управления слева
   this.m_struct_obj.border_size_right=(int)this.GetProperty(CANV_ELEMENT_PROP_BORDER_SIZE_RIGHT); // Размер рамки элемента управления справа
   this.m_struct_obj.border_color=(color)this.GetProperty(CANV_ELEMENT_PROP_BORDER_COLOR);         // Цвет рамки элемента управления
   this.m_struct_obj.border_color_mouse_down=(color)this.GetProperty(CANV_ELEMENT_PROP_BORDER_COLOR_MOUSE_DOWN);// Цвет рамки элемента управления при нажатии мышки на элемент управления
   this.m_struct_obj.border_color_mouse_over=(color)this.GetProperty(CANV_ELEMENT_PROP_BORDER_COLOR_MOUSE_OVER);// Цвет рамки элемента управления при наведении мышки на элемент управления
   this.m_struct_obj.autosize=this.GetProperty(CANV_ELEMENT_PROP_AUTOSIZE);                        // Флаг автоматического изменения размера элемента управления под содержимое
   this.m_struct_obj.autosize_mode=(int)this.GetProperty(CANV_ELEMENT_PROP_AUTOSIZE_MODE);         // Режим автоматического изменения размера элемента управления под содержимое
   this.m_struct_obj.autoscroll=this.GetProperty(CANV_ELEMENT_PROP_AUTOSCROLL);        // Флаг автоматического появления полосы прокрутки
   this.m_struct_obj.autoscroll_margin_w=(int)this.GetProperty(CANV_ELEMENT_PROP_AUTOSCROLL_MARGIN_W);  // Ширина поля вокруг элемента при автоматической прокрутке
   this.m_struct_obj.autoscroll_margin_h=(int)this.GetProperty(CANV_ELEMENT_PROP_AUTOSCROLL_MARGIN_H);  // Высота поля вокруг элемента при автоматической прокрутке
   this.m_struct_obj.dock_mode=(int)this.GetProperty(CANV_ELEMENT_PROP_DOCK_MODE);                 // Режим привязки границ элемента управления к контейнеру
   this.m_struct_obj.margin_top=(int)this.GetProperty(CANV_ELEMENT_PROP_MARGIN_TOP);               // Промежуток сверху между полями данного и другого элемента управления
   this.m_struct_obj.margin_bottom=(int)this.GetProperty(CANV_ELEMENT_PROP_MARGIN_BOTTOM);         // Промежуток снизу между полями данного и другого элемента управления
   this.m_struct_obj.margin_left=(int)this.GetProperty(CANV_ELEMENT_PROP_MARGIN_LEFT);             // Промежуток слева между полями данного и другого элемента управления
   this.m_struct_obj.margin_right=(int)this.GetProperty(CANV_ELEMENT_PROP_MARGIN_RIGHT);           // Промежуток справа между полями данного и другого элемента управления
   this.m_struct_obj.padding_top=(int)this.GetProperty(CANV_ELEMENT_PROP_PADDING_TOP);             // Промежуток сверху внутри элемента управления
   this.m_struct_obj.padding_bottom=(int)this.GetProperty(CANV_ELEMENT_PROP_PADDING_BOTTOM);       // Промежуток снизу внутри элемента управления
   this.m_struct_obj.padding_left=(int)this.GetProperty(CANV_ELEMENT_PROP_PADDING_LEFT);           // Промежуток слева внутри элемента управления
   this.m_struct_obj.padding_right=(int)this.GetProperty(CANV_ELEMENT_PROP_PADDING_RIGHT);         // Промежуток справа внутри элемента управления
   this.m_struct_obj.text_align=(int)this.GetProperty(CANV_ELEMENT_PROP_TEXT_ALIGN);               // Положение текста в границах текстовой метки
   this.m_struct_obj.check_align=(int)this.GetProperty(CANV_ELEMENT_PROP_CHECK_ALIGN);             // Положение флажка проверки в границах элемента управления
   this.m_struct_obj.checked=(int)this.GetProperty(CANV_ELEMENT_PROP_CHECKED);                     // Состояние флажка проверки элемента управления
   this.m_struct_obj.check_state=(int)this.GetProperty(CANV_ELEMENT_PROP_CHECK_STATE);             // Состояние элемента управления, имеющего флажок проверки
   this.m_struct_obj.autocheck=(int)this.GetProperty(CANV_ELEMENT_PROP_AUTOCHECK);                 // Автоматическое изменение состояния флажка при его выборе
   
   this.m_struct_obj.check_background_color=(color)this.GetProperty(CANV_ELEMENT_PROP_CHECK_BACKGROUND_COLOR);           // Цвет фона флажка проверки элемента управления
   this.m_struct_obj.check_background_color_opacity=(color)this.GetProperty(CANV_ELEMENT_PROP_CHECK_BACKGROUND_COLOR_OPACITY); // Непрозрачность цвета фона флажка проверки элемента управления
   this.m_struct_obj.check_background_color_mouse_down=(color)this.GetProperty(CANV_ELEMENT_PROP_CHECK_BACKGROUND_COLOR_MOUSE_DOWN);// Цвет фона флажка проверки элемента управления при нажатии мышки на элемент управления
   this.m_struct_obj.check_background_color_mouse_over=(color)this.GetProperty(CANV_ELEMENT_PROP_CHECK_BACKGROUND_COLOR_MOUSE_OVER);// Цвет фона флажка проверки элемента управления при наведении мышки на элемент управления
   this.m_struct_obj.check_fore_color=(color)this.GetProperty(CANV_ELEMENT_PROP_CHECK_FORE_COLOR);                       // Цвет рамки флажка проверки элемента управления
   this.m_struct_obj.check_fore_color_opacity=(color)this.GetProperty(CANV_ELEMENT_PROP_CHECK_FORE_COLOR_OPACITY);       // Непрозрачность цвета рамки флажка проверки элемента управления
   this.m_struct_obj.check_fore_color_mouse_down=(color)this.GetProperty(CANV_ELEMENT_PROP_CHECK_FORE_COLOR_MOUSE_DOWN); // Цвет рамки флажка проверки элемента управления при нажатии мышки на элемент управления
   this.m_struct_obj.check_fore_color_mouse_over=(color)this.GetProperty(CANV_ELEMENT_PROP_CHECK_FORE_COLOR_MOUSE_OVER); // Цвет рамки флажка проверки элемента управления при наведении мышки на элемент управления
   this.m_struct_obj.check_flag_color=(color)this.GetProperty(CANV_ELEMENT_PROP_CHECK_FLAG_COLOR);                       // Цвет флажка проверки элемента управления
   this.m_struct_obj.check_flag_color_opacity=(color)this.GetProperty(CANV_ELEMENT_PROP_CHECK_FLAG_COLOR_OPACITY);       // Непрозрачность цвета флажка проверки элемента управления
   this.m_struct_obj.check_flag_color_mouse_down=(color)this.GetProperty(CANV_ELEMENT_PROP_CHECK_FLAG_COLOR_MOUSE_DOWN); // Цвет флажка проверки элемента управления при нажатии мышки на элемент управления
   this.m_struct_obj.check_flag_color_mouse_over=(color)this.GetProperty(CANV_ELEMENT_PROP_CHECK_FLAG_COLOR_MOUSE_OVER); // Цвет флажка проверки элемента управления при наведении мышки на элемент управления
//--- Сохранение вещественных свойств

//--- Сохранение строковых свойств
   ::StringToCharArray(this.GetProperty(CANV_ELEMENT_PROP_NAME_OBJ),this.m_struct_obj.name_obj);   // Имя объекта-графического элемента
   ::StringToCharArray(this.GetProperty(CANV_ELEMENT_PROP_NAME_RES),this.m_struct_obj.name_res);   // Имя графического ресурса
   ::StringToCharArray(this.GetProperty(CANV_ELEMENT_PROP_TEXT),this.m_struct_obj.text);           // Текст графического элемента
   //--- Сохранение структуры в uchar-массив
   ::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;
  }
//+------------------------------------------------------------------+


и точно так же был доработан метод, создающий объект из структуры:

//+------------------------------------------------------------------+
//| Создаёт объект из структуры                                      |
//+------------------------------------------------------------------+
void CGCnvElement::StructToObject(void)
  {
//--- Сохранение целочисленных свойств
   this.SetProperty(CANV_ELEMENT_PROP_ID,this.m_struct_obj.id);                                    // Идентификатор элемента
   this.SetProperty(CANV_ELEMENT_PROP_TYPE,this.m_struct_obj.type);                                // Тип графического элемента
   this.SetProperty(CANV_ELEMENT_PROP_BELONG,this.m_struct_obj.belong);                            // Принадлежность графического элемента
   this.SetProperty(CANV_ELEMENT_PROP_NUM,this.m_struct_obj.number);                               // Номер элемента в списке
   this.SetProperty(CANV_ELEMENT_PROP_CHART_ID,this.m_struct_obj.chart_id);                        // Идентификатор графика
   this.SetProperty(CANV_ELEMENT_PROP_WND_NUM,this.m_struct_obj.subwindow);                        // Номер подокна графика
   this.SetProperty(CANV_ELEMENT_PROP_COORD_X,this.m_struct_obj.coord_x);                          // X-координата формы на графике
   this.SetProperty(CANV_ELEMENT_PROP_COORD_Y,this.m_struct_obj.coord_y);                          // Y-координата формы на графике
   this.SetProperty(CANV_ELEMENT_PROP_WIDTH,this.m_struct_obj.width);                              // Ширина элемента
   this.SetProperty(CANV_ELEMENT_PROP_HEIGHT,this.m_struct_obj.height);                            // Высота элемента
   this.SetProperty(CANV_ELEMENT_PROP_RIGHT,this.m_struct_obj.edge_right);                         // Правая граница элемента
   this.SetProperty(CANV_ELEMENT_PROP_BOTTOM,this.m_struct_obj.edge_bottom);                       // Нижняя граница элемента
   this.SetProperty(CANV_ELEMENT_PROP_ACT_SHIFT_LEFT,this.m_struct_obj.act_shift_left);            // Отступ активной зоны от левого края элемента
   this.SetProperty(CANV_ELEMENT_PROP_ACT_SHIFT_TOP,this.m_struct_obj.act_shift_top);              // Отступ активной зоны от верхнего края элемента
   this.SetProperty(CANV_ELEMENT_PROP_ACT_SHIFT_RIGHT,this.m_struct_obj.act_shift_right);          // Отступ активной зоны от правого края элемента
   this.SetProperty(CANV_ELEMENT_PROP_ACT_SHIFT_BOTTOM,this.m_struct_obj.act_shift_bottom);        // Отступ активной зоны от нижнего края элемента
   this.SetProperty(CANV_ELEMENT_PROP_MOVABLE,this.m_struct_obj.movable);                          // Флаг перемещаемости элемента
   this.SetProperty(CANV_ELEMENT_PROP_ACTIVE,this.m_struct_obj.active);                            // Флаг активности элемента
   this.SetProperty(CANV_ELEMENT_PROP_INTERACTION,this.m_struct_obj.interaction);                  // Флаг взаимодействия элемента с внешней средой
   this.SetProperty(CANV_ELEMENT_PROP_COORD_ACT_X,this.m_struct_obj.coord_act_x);                  // X-координата активной зоны элемента
   this.SetProperty(CANV_ELEMENT_PROP_COORD_ACT_Y,this.m_struct_obj.coord_act_y);                  // Y-координата активной зоны элемента
   this.SetProperty(CANV_ELEMENT_PROP_ACT_RIGHT,this.m_struct_obj.coord_act_right);                // Правая граница активной зоны элемента
   this.SetProperty(CANV_ELEMENT_PROP_ACT_BOTTOM,this.m_struct_obj.coord_act_bottom);              // Нижняя граница активной зоны элемента
   this.SetProperty(CANV_ELEMENT_PROP_ZORDER,this.m_struct_obj.zorder);                            // Приоритет графического объекта на получение события нажатия мышки на графике
   this.SetProperty(CANV_ELEMENT_PROP_ENABLED,this.m_struct_obj.enabled);                          // Флаг доступности элемента
   this.SetProperty(CANV_ELEMENT_PROP_FORE_COLOR,this.m_struct_obj.fore_color);                    // Цвет текста по умолчанию для всех объектов элемента управления
   this.SetProperty(CANV_ELEMENT_PROP_FORE_COLOR_OPACITY,this.m_struct_obj.fore_color_opacity);    // Непрозрачность цвета текста по умолчанию для всех объектов элемента управления
   this.SetProperty(CANV_ELEMENT_PROP_BACKGROUND_COLOR,this.m_struct_obj.background_color);        // Цвет фона элемента
   this.SetProperty(CANV_ELEMENT_PROP_BACKGROUND_COLOR_OPACITY,this.m_struct_obj.background_color_opacity);       // Непрозрачность элемента
   this.SetProperty(CANV_ELEMENT_PROP_BACKGROUND_COLOR_MOUSE_DOWN,this.m_struct_obj.background_color_mouse_down); // Цвет фона элемента управления при нажатии мышки на элемент управления
   this.SetProperty(CANV_ELEMENT_PROP_BACKGROUND_COLOR_MOUSE_OVER,this.m_struct_obj.background_color_mouse_over); // Цвет фона элемента управления при наведении мышки на элемент управления
   this.SetProperty(CANV_ELEMENT_PROP_BOLD_TYPE,this.m_struct_obj.bold_type);                      // Тип толщины шрифта
   this.SetProperty(CANV_ELEMENT_PROP_BORDER_STYLE,this.m_struct_obj.border_style);                // Стиль рамки элемента управления
   this.SetProperty(CANV_ELEMENT_PROP_BORDER_SIZE_TOP,this.m_struct_obj.border_size_top);          // Размер рамки элемента управления сверху
   this.SetProperty(CANV_ELEMENT_PROP_BORDER_SIZE_BOTTOM,this.m_struct_obj.border_size_bottom);    // Размер рамки элемента управления снизу
   this.SetProperty(CANV_ELEMENT_PROP_BORDER_SIZE_LEFT,this.m_struct_obj.border_size_left);        // Размер рамки элемента управления слева
   this.SetProperty(CANV_ELEMENT_PROP_BORDER_SIZE_RIGHT,this.m_struct_obj.border_size_right);      // Размер рамки элемента управления справа
   this.SetProperty(CANV_ELEMENT_PROP_BORDER_COLOR,this.m_struct_obj.border_color);                // Цвет рамки элемента управления
   this.SetProperty(CANV_ELEMENT_PROP_BORDER_COLOR_MOUSE_DOWN,this.m_struct_obj.border_color_mouse_down);// Цвет рамки элемента управления при нажатии мышки на элемент управления
   this.SetProperty(CANV_ELEMENT_PROP_BORDER_COLOR_MOUSE_OVER,this.m_struct_obj.border_color_mouse_over);// Цвет рамки элемента управления при наведении мышки на элемент управления
   this.SetProperty(CANV_ELEMENT_PROP_AUTOSIZE,this.m_struct_obj.autosize);                        // Флаг автоматического изменения размера элемента управления под содержимое
   this.SetProperty(CANV_ELEMENT_PROP_AUTOSIZE_MODE,this.m_struct_obj.autosize_mode);              // Режим автоматического изменения размера элемента управления под содержимое
   this.SetProperty(CANV_ELEMENT_PROP_AUTOSCROLL,this.m_struct_obj.autoscroll);                    // Флаг автоматического появления полосы прокрутки
   this.SetProperty(CANV_ELEMENT_PROP_AUTOSCROLL_MARGIN_W,this.m_struct_obj.autoscroll_margin_w);  // Ширина поля вокруг элемента при автоматической прокрутке
   this.SetProperty(CANV_ELEMENT_PROP_AUTOSCROLL_MARGIN_H,this.m_struct_obj.autoscroll_margin_h);  // Высота поля вокруг элемента при автоматической прокрутке
   this.SetProperty(CANV_ELEMENT_PROP_DOCK_MODE,this.m_struct_obj.dock_mode);                      // Режим привязки границ элемента управления к контейнеру
   this.SetProperty(CANV_ELEMENT_PROP_MARGIN_TOP,this.m_struct_obj.margin_top);                    // Промежуток сверху между полями данного и другого элемента управления
   this.SetProperty(CANV_ELEMENT_PROP_MARGIN_BOTTOM,this.m_struct_obj.margin_bottom);              // Промежуток снизу между полями данного и другого элемента управления
   this.SetProperty(CANV_ELEMENT_PROP_MARGIN_LEFT,this.m_struct_obj.margin_left);                  // Промежуток слева между полями данного и другого элемента управления
   this.SetProperty(CANV_ELEMENT_PROP_MARGIN_RIGHT,this.m_struct_obj.margin_right);                // Промежуток справа между полями данного и другого элемента управления
   this.SetProperty(CANV_ELEMENT_PROP_PADDING_TOP,this.m_struct_obj.padding_top);                  // Промежуток сверху внутри элемента управления
   this.SetProperty(CANV_ELEMENT_PROP_PADDING_BOTTOM,this.m_struct_obj.padding_bottom);            // Промежуток снизу внутри элемента управления
   this.SetProperty(CANV_ELEMENT_PROP_PADDING_LEFT,this.m_struct_obj.padding_left);                // Промежуток слева внутри элемента управления
   this.SetProperty(CANV_ELEMENT_PROP_PADDING_RIGHT,this.m_struct_obj.padding_right);              // Промежуток справа внутри элемента управления
   this.SetProperty(CANV_ELEMENT_PROP_TEXT_ALIGN,this.m_struct_obj.text_align);                    // Положение текста в границах текстовой метки
   this.SetProperty(CANV_ELEMENT_PROP_CHECK_ALIGN,this.m_struct_obj.check_align);                  // Положение флажка проверки в границах элемента управления
   this.SetProperty(CANV_ELEMENT_PROP_CHECKED,this.m_struct_obj.checked);                          // Состояние флажка проверки элемента управления
   this.SetProperty(CANV_ELEMENT_PROP_CHECK_STATE,this.m_struct_obj.check_state);                  // Состояние элемента управления, имеющего флажок проверки
   this.SetProperty(CANV_ELEMENT_PROP_AUTOCHECK,this.m_struct_obj.autocheck);                      // Автоматическое изменение состояния флажка при его выборе
   
   this.SetProperty(CANV_ELEMENT_PROP_CHECK_BACKGROUND_COLOR,this.m_struct_obj.check_background_color);           // Цвет фона флажка проверки элемента управления
   this.SetProperty(CANV_ELEMENT_PROP_CHECK_BACKGROUND_COLOR_OPACITY,this.m_struct_obj.check_background_color_opacity); // Непрозрачность цвета фона флажка проверки элемента управления
   this.SetProperty(CANV_ELEMENT_PROP_CHECK_BACKGROUND_COLOR_MOUSE_DOWN,this.m_struct_obj.check_background_color_mouse_down);// Цвет фона флажка проверки элемента управления при нажатии мышки на элемент управления
   this.SetProperty(CANV_ELEMENT_PROP_CHECK_BACKGROUND_COLOR_MOUSE_OVER,this.m_struct_obj.check_background_color_mouse_over);// Цвет фона флажка проверки элемента управления при наведении мышки на элемент управления
   this.SetProperty(CANV_ELEMENT_PROP_CHECK_FORE_COLOR,this.m_struct_obj.check_fore_color);                       // Цвет рамки флажка проверки элемента управления
   this.SetProperty(CANV_ELEMENT_PROP_CHECK_FORE_COLOR_OPACITY,this.m_struct_obj.check_fore_color_opacity);       // Непрозрачность цвета рамки флажка проверки элемента управления
   this.SetProperty(CANV_ELEMENT_PROP_CHECK_FORE_COLOR_MOUSE_DOWN,this.m_struct_obj.check_fore_color_mouse_down); // Цвет рамки флажка проверки элемента управления при нажатии мышки на элемент управления
   this.SetProperty(CANV_ELEMENT_PROP_CHECK_FORE_COLOR_MOUSE_OVER,this.m_struct_obj.check_fore_color_mouse_over); // Цвет рамки флажка проверки элемента управления при наведении мышки на элемент управления
   this.SetProperty(CANV_ELEMENT_PROP_CHECK_FLAG_COLOR,this.m_struct_obj.check_flag_color);                       // Цвет флажка проверки элемента управления
   this.SetProperty(CANV_ELEMENT_PROP_CHECK_FLAG_COLOR_OPACITY,this.m_struct_obj.check_flag_color_opacity);       // Непрозрачность цвета флажка проверки элемента управления
   this.SetProperty(CANV_ELEMENT_PROP_CHECK_FLAG_COLOR_MOUSE_DOWN,this.m_struct_obj.check_flag_color_mouse_down); // Цвет флажка проверки элемента управления при нажатии мышки на элемент управления
   this.SetProperty(CANV_ELEMENT_PROP_CHECK_FLAG_COLOR_MOUSE_OVER,this.m_struct_obj.check_flag_color_mouse_over); // Цвет флажка проверки элемента управления при наведении мышки на элемент управления
//--- Сохранение вещественных свойств

//--- Сохранение строковых свойств
   this.SetProperty(CANV_ELEMENT_PROP_NAME_OBJ,::CharArrayToString(this.m_struct_obj.name_obj));   // Имя объекта-графического элемента
   this.SetProperty(CANV_ELEMENT_PROP_NAME_RES,::CharArrayToString(this.m_struct_obj.name_res));   // Имя графического ресурса
   this.SetProperty(CANV_ELEMENT_PROP_TEXT,::CharArrayToString(this.m_struct_obj.text));           // Текст графического элемента
  }
//+------------------------------------------------------------------+

Оба метода идентичны, но противоположны друг другу. Если в первом методе в поля структуры записываются соответствующие свойства объекта, то во втором — в свойства объекта записываются значения из соответствующих полей структуры.

В реализации метода, создающего графический объект-элемент, тоже удалим неиспользуемые переменные:

//+------------------------------------------------------------------+
//| Создаёт графический объект-элемент                               |
//+------------------------------------------------------------------+
bool CGCnvElement::Create(const long chart_id,     // Идентификатор графика
                          const int wnd_num,       // Подокно графика
                          const string name,       // Имя элемента
                          const int x,             // Координата X
                          const int y,             // Координата Y
                          const int w,             // Ширина
                          const int h,             // Высота
                          const color colour,      // Цвет фона
                          const uchar opacity,     // Непрозрачность
                          const bool redraw=false) // Флаг необходимости перерисовки
                         
  {
   ::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;
  }
//+------------------------------------------------------------------+


В методе устанавливающем непрозрачность элемента, вместо строки, сохраняющей значение непрозрачности в переменную,

this.m_opacity=value;

впишем её сохранение в свойство объекта:

//+------------------------------------------------------------------+
//| Устанавливает непрозрачность элемента                            |
//+------------------------------------------------------------------+
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);
  }
//+------------------------------------------------------------------+


Реализация метода, копирующего массив цветов в указанный массив цветов фона:

//+------------------------------------------------------------------+
//| Копирует массив цветов в указанный массив цветов фона            |
//+------------------------------------------------------------------+
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);
  }
//+------------------------------------------------------------------+

В метод передаётся массив, в который нужно записать все данные из массива-источника. Если размеры массивов не совпадают, то массив-приёмник изменяет свой размер под размер массива-источника, а затем в массив-приёмник копируется всё содержание массива-источника.


В конструкторе класса объекта-тени в файле \MQL5\Include\DoEasy\Objects\Graph\ShadowObj.mqh переименуем названия вызываемых методов под новые:

//+------------------------------------------------------------------+
//| Конструктор                                                      |
//+------------------------------------------------------------------+
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();
  }
//+------------------------------------------------------------------+


Объект-форма у нас является объектом, в котором реализован функционал для взаимодействия с мышкой. Базовый объект WinForms-объектов библиотеки является его наследником. В файле класса объекта-формы \MQL5\Include\DoEasy\Objects\Graph\Form.mqh нам нужно тоже доработать множество методов.

Методы для работы с цветом будут переименованы и добавлены новые, методы для работы с "рамкой" объекта тоже будут переименованы. Теперь это будет "Border" вместо "Frame". Кроме того, к объекту могут быть присоединены другие объекты, например, на панели создаются кнопки или тексты...
При наведении курсора на такую панель на данный момент библиотека определяет факт наведения курсора на панель. Но не "видит", что курсор находится над объектом, прикреплённым к этой панели. Соответственно, нет возможности взаимодействовать с прикреплённым к панели объектом.

Чтобы решить эту проблему, нам нужно пройтись по списку всех прикреплённых к панели объектов и определить из них самый последний в иерархии, над которым находится курсор. Для этого создадим список всех объектов, прикреплённых к панели. При этом к прикреплённому объекту в свою очередь могут быть прикреплены и другие. Т.е., нам нужно пройтись в цикле по всей иерархии таких объектов.

Для этого создадим список, в который будем записывать указатели на прикреплённые объекты — назовём их объектами взаимодействия, так как мы ищем тот объект, с которым необходимо взаимодействие мышкой, и метод, записывающий в список все прикреплённые к объекту другие элементы управления. Если у прикреплённого элемента тоже есть свои прикреплённые объекты, то и для него будет вызываться этот же метод, а в метод передаваться указатель на список, в который записываются указатели.

Таким образом мы сможем пройтись в цикле полностью по всей иерархии прикреплённых объектов и получить список указателей на них. А далее уже проще — как только определяем, что на панель наведён курсор, вызываем метод этой панели, создающей список всех прикреплённых объектов. И далее нам остаётся найти самый первый объект с конца списка, у которого стоит флаг нахождения курсора над ним. И этот указатель на этот найденный объект будем передавать в дальнейшую обработку, заготовки обработчиков которой у нас давно написаны.

Из защищённой секции класса удалим теперь не нужные переменные:

protected:
   CArrayObj         m_list_elements;                          // Список присоединённых элементов

   CAnimations      *m_animations;                             // Указатель на объект анимаций
   CShadowObj       *m_shadow_obj;                             // Указатель на объект тени
   CMouseState       m_mouse;                                  // Объект класса "Состояния мышки"
   ENUM_MOUSE_FORM_STATE m_mouse_form_state;                   // Состояние мышки относительно формы
   ushort            m_mouse_state_flags;                      // Флаги состояния мышки
   color             m_color_frame;                            // Цвет рамки формы
   int               m_offset_x;                               // Смещение координаты X относительно курсора
   int               m_offset_y;                               // Смещение координаты Y относительно курсора
   CArrayObj         m_list_tmp;                               // Список для размещения указателей
   int               m_frame_width_left;                       // Ширина рамки формы слева
   int               m_frame_width_right;                      // Ширина рамки формы справа
   int               m_frame_width_top;                        // Ширина рамки формы сверху
   int               m_frame_width_bottom;                     // Ширина рамки формы снизу
   int               m_init_x;                                 // Координата X формы при её создании
   int               m_init_y;                                 // Координата Y формы при её создании
   int               m_init_w;                                 // Ширина формы при её создании
   int               m_init_h;                                 // Высота формы при её создании
//--- Инициализирует переменные

Теперь вместо этих переменных все значения у нас хранятся в свойствах объекта.


Из метода CreateAndAddNewElement() удалим указатель на главный объект иерархии всех прикреплённых друг к другу объектов:

//--- Создаёт новый присоединённый элемент и добавляет его в список присоединённых объектов
   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:

Ранее нам при создании прикреплённых объектов приходилось явно указывать главный объект для создаваемого, передавая указатель на него в параметрах этого метода. Теперь же мы сделаем так, что главный или базовый объект в иерархии будет определяться автоматически, что избавит конечного пользователя от ненужного указания такого объекта при создании нового прикреплённого элемента управления.

Там же — в защищённой секции, объявим метод для создания списка всех объектов взаимодействия и метод, возвращающий флаг наличия объекта в этом списке по его имени:

//--- Создаёт новый присоединённый элемент и добавляет его в список присоединённых объектов
   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);
//--- Создаёт список всех объектов взаимодействия
   void              CreateListDepInteractObj(CArrayObj *list);
//--- Возвращает флаг наличия указателя на объект в списке объектов взаимодействия по имени
   bool              IsPresentInteractObj(const string name);
public:


В публичной секции объявим метод, создающий список всех объектов взаимодействия (но уже без передачи указателя на список в формальных параметрах метода), и метод, возвращающий указатель на объект-форму в списке объектов взаимодействия по его индексу:

public:
//--- Создаёт список всех объектов взаимодействия
   int               CreateListInteractObj(void);
//--- Возвращает указатель на объект-форму в списке объектов взаимодействия
   CForm            *GetInteractForm(const int index)    { return this.m_list_interact.At(index);  }

Первый метод будет вызываться из того объекта, на который наведён курсор мышки, а внутри него будет вызываться защищённый метод для поиска остальных объектов взаимодействия по всей иерархии главного объекта. Указатель на список будет передаваться в каждый последующий объект иерархии и таким образом в одном списке самого первого объекта, на который был наведён курсор, окажется список указателей на все прикреплённые к нему объекты.

Напишем методы, возвращающие и устанавливающие все размеры рамки объекта из его свойств:

//--- (1) Устанавливает, (2) возвращает смещение координат X и Y относительно курсора
   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;           }
   
//--- Возвращает размер рамки (1) слева, (2) сверху, (3) справа, (4) снизу, (5) со всех сторон
   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);}
//--- Устанавливает размер рамки (1) слева, (2) сверху, (3) справа, (4) снизу, (5) со всех сторон
   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);      }

//--- Обновляет координаты (сдвигает канвас)


Добавим метод, возвращающий список объектов взаимодействия, и метод, возвращающий количество объектов взаимодействия в созданном списке:

//--- Возвращает (1) себя, (2) список присоединённых объектов, (3) список объектов взаимодействия, (4) объект тени
   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;      }
//--- Возвращает указатель на (1) объект анимаций, список (2) текстовых, (3) прямоугольных кадров анимаций
   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);       }
   
//--- Возвращает количество (1) привязанных элементов, (2) объектов взаимодействия, (3) привязанный элемент по индексу в списке
   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);  }


Из метода, создающего новый присоединённый элемент, удалим передачу указателя на главный объект:

//--- Создаёт новый присоединённый элемент
   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);
//--- Добавляет новый присоединённый элемент

Теперь этот указатель нам не нужен — библиотека будет сама определять какой объект является главным, а какой базовым.


Переименуем методы для установки и возврата цвета рамки формы и добавим новые методы для работы с дополнительными цветами фона формы:

//+------------------------------------------------------------------+
//| Методы упрощённого доступа к свойствам объекта                   |
//+------------------------------------------------------------------+
//--- (1) Устанавливает, (2) возвращает цвет рамки элемента управления
   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) Устанавливает, (2) возвращает цвет рамки элемента управления при нажатии мышки на элемент управления
   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) Устанавливает, (2) возвращает цвет рамки элемента управления при наведении мышки на элемент управления
   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);  }

Теперь эти методы работают не с переменными, а со значениями, находящимися в свойствах объекта.


В методе инициализации переменных очистим список объектов взаимодействия и установим для него флаг сортированного списка, а вместо записи размеров рамки формы в переменные установим их при помощи написанных выше новых методов:

//+------------------------------------------------------------------+
//| Инициализирует переменные                                        |
//+------------------------------------------------------------------+
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);
  }
//+------------------------------------------------------------------+


Ранее мы указывали для каждого создаваемого объекта не только его реальные координаты (в системе координат графика), но и относительные — расстояние в пикселях от начала координат того объекта, к которому прикрепляется создаваемый. Мы просто указывали то значение отступа от края объекта, которое было задано при создании.

Но это не совсем эффективно, и требует постоянного отслеживания этих координат в случае, если они изменяются. Теперь же мы будем их рассчитывать из экранных координат — вычитанием экранной координаты привязанного объекта от экранной координаты базового. Таким образом, мы при любых изменениях координат привязанного объекта всегда будем точно знать его относительные координаты.

В методе, создающем новый графический объект, рассчитаем и запишем относительные координаты элемента:

//+------------------------------------------------------------------+
//| Создаёт новый графический объект                                 |
//+------------------------------------------------------------------+
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;
   //--- В зависимости от типа создаваемого объекта
   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;
      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;
  }
//+------------------------------------------------------------------+


В методе, создающем новый присоединённый элемент и добавляющем его в список присоединённых объектов, переименуем метод установки фона объекта, определим и запишем указатель на главный объект иерархии присоединённых объектов, и рассчитаем и установим относительные координаты созданного присоединённого элемента управления:

//+------------------------------------------------------------------+
//| Создаёт новый присоединённый элемент                             |
//| и добавляет его в список присоединённых объектов                 |
//+------------------------------------------------------------------+
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)
  {
//--- Если тип создаваемого графического элемента меньше, чем "элемент" - сообщаем об этом и возвращаем 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;
     }
//--- Задаём номер элемента в списке
   int num=this.m_list_elements.Total();
//--- Создаём имя графического элемента
   string ns=(::StringLen((string)num)<2 ? ::IntegerToString(num,2,'0') : (string)num);
   string name="Elm"+ns;
//--- Получаем экранные координаты объекта относительно системы координат базового объекта
   int elm_x=x;
   int elm_y=y;
   this.GetCoords(elm_x,elm_y);
//--- Создаём новый графический элемент
   CGCnvElement *obj=this.CreateNewGObject(element_type,num,name,elm_x,elm_y,w,h,colour,opacity,false,activity);
   if(obj==NULL)
      return NULL;
//--- и добавляем его в список привязанных графических элементов
   if(!this.AddNewElement(obj,elm_x,elm_y))
     {
      delete obj;
      return NULL;
     }
//--- Устанавливаем минимальные свойства привязанному графическому элементу
   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;
  }
//+------------------------------------------------------------------+

Каким образом мы определяем главный объект иерархии? Всё просто: если этот объект не присоединён ни к какому другому, то изначально у него  указатель на главный объект равен значению NULL. Если из такого объекта создаётся присоединённый к нему другой элемент управления, то для него проверяется значение указателя на главный объект. Если указатель равен NULL, то главным объектом будет этот. Иначе — в этот объект уже записан указатель на главный объект всей иерархии — его и записываем во вновь созданный элемент управления. Таким образом во всей иерархии главным объектом всегда будет один — самый первый, с которого началось создание иерархии присоединённых объектов.


Из метода, создающего новый присоединённый элемент, удалим передачу указателя на главный объект и, соответственно, уберём лишнее передаваемое значение в метод CreateAndAddNewElement():

//+------------------------------------------------------------------+
//| Создаёт новый присоединённый элемент                             |
//+------------------------------------------------------------------+
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)
  {
//--- Создаём новый графический элемент
   CGCnvElement *obj=this.CreateAndAddNewElement(element_type,main,x,y,w,h,colour,opacity,activity);
//--- Если объект создан - рисуем добавленный объект и возвращаем true
   if(obj==NULL)
      return false;
   obj.Erase(colour,opacity,redraw);
   return true;
  }
//+------------------------------------------------------------------+


Так как у нас теперь есть методы, возвращающие ширину рамки объекта, и эти значения хранятся не в переменных, а в свойствах объекта, то в методе, возвращающем начальные координаты привязанного объекта, заменим обращение к переменным на получение значений из новых методов:

//+------------------------------------------------------------------+
//| Возвращает начальные координаты привязанного объекта             |
//+------------------------------------------------------------------+
void CForm::GetCoords(int &x,int &y)
  {
   x=this.CoordX()+this.BorderSizeLeft()+x;
   y=this.CoordY()+this.BorderSizeTop()+y;
  }
//+------------------------------------------------------------------+


Переименуем вызываемые методы, заменим в методах работу с переменными на работу со свойствами объекта,

//+------------------------------------------------------------------+
//| Устанавливает цветовую схему                                     |
//+------------------------------------------------------------------+
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]);
  }
//+------------------------------------------------------------------+
//| Устанавливает стиль формы                                        |
//+------------------------------------------------------------------+
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)
  {
//--- Устанавливаем параметры непрозрачности и размера сторон рамки формы
   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];
//--- Создаём объект тени
   this.CreateShadowObj(clrNONE,(uchar)array_form_style[style][FORM_STYLE_FRAME_SHADOW_OPACITY]);
   
//--- Устанавливаем цветовую схему
   this.SetColorTheme(theme,opacity);
//--- Рассчитываем цвет тени с затемнением цвета
   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);
   
//--- Рисуем прямоугольную тень
   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]);
   
//--- Заполняем фон формы цветом и непрозрачностью
   this.Erase(this.m_array_colors_bg,this.Opacity(),this.m_gradient_v,this.m_gradient_c);
//--- В зависимости от выбранного стиля формы рисуем соответствующую рамку формы и внешнюю очерчивающую рамку
   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());
  }

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


Метод, возвращающий флаг наличия указателя на объект в списке объектов взаимодействия по имени:

//+------------------------------------------------------------------+
//| Возвращает флаг наличия указателя на объект                      |
//| в списке объектов взаимодействия по имени                        |
//+------------------------------------------------------------------+
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;
  }
//+------------------------------------------------------------------+

В цикле по списку объектов взаимодействия получаем очередной объект-форму и, если имя объекта равно переданному в метод — возвращаем true — объект с таким именем уже есть в списке. По завершении цикла возвращаем false — объект с указанным именем не был найден.


Защищённый метод, создающий список всех объектов взаимодействия:

//+------------------------------------------------------------------+
//| Создаёт список всех объектов взаимодействия                      |
//+------------------------------------------------------------------+
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);
     }
  }
//+------------------------------------------------------------------+

В цикле по всем прикреплённым объектам получаем очередной объект-форму.
Если объект не получен, или его тип меньше, чем объект-форма — идём к следующему.
Если объект с таким именем уже есть в списке — идём к следующему
.
Если объект удалось поместить в список объектов взаимодействия — вызываем для этого объекта этот же самый метод для поиска объектов взаимодействия, прикреплённых к нему
.
При этом в метод передаётся список, указанный во входных параметрах данного метода.
Таким образом в один список будут помещены указатели на все объекты взаимодействия всей иерархии присоединённых объектов.
Список же передаём тот, который находится в главном объекте, и указываем его
в публичном методе, создающем список всех объектов взаимодействия:

//+------------------------------------------------------------------+
//| Создаёт список всех объектов взаимодействия                      |
//+------------------------------------------------------------------+
int CForm::CreateListInteractObj(void)
  {
   this.CreateListDepInteractObj(this.GetListInteractObj());
   return this.m_list_interact.Total();
  }
//+------------------------------------------------------------------+

Здесь всё просто: вызываем вышерассмотренный метод для создания списка объектов взаимодействия, и передавая в него указатель на список текущего объекта. Из метода возвращаем количество указателей на объекты взаимодействия, добавленные в список.


Внесём доработки в файл \MQL5\Include\DoEasy\Objects\Graph\WForms\WinFormBase.mqh класса базового объекта WinForms-объектов библиотеки.

Так как мы удалили переменные, хранящие ширину рамки формы, и заменили их на методы в файле класса объекта-формы, то в этом файле нам нужно удалить теперь не нужные методы для установки и возврата ширины рамки:

   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);
                       }
   
//--- Устанавливает ширину рамки элемента (1) слева, (2) сверху, (3) справа, (4) снизу
   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);
                       }
   
//--- Возвращает ширину рамки элемента (1) слева, (2) сверху, (3) справа, (4) снизу
   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;                                                 }
   
//--- Возвращает промежуток (1) слева, (2) сверху, (3) справа, (4) снизу между полями внутри элемента управления
   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);                   }


В методах, устанавливающих размер отступа, заменим обращение к переменным на чтение значений, возвращаемых из методов:

//--- Устанавливает промежуток (1) слева, (2) сверху, (3) справа, (4) снизу, (5) со всех сторон внутри элемента управления
   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);
                       }


Заменим вызов старых методов FrameWidth на новые 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);
                       }
   
//--- Устанавливает ширину всех сторон рамки элемента
   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);
                       }
   
//--- Возвращает промежуток (1) слева, (2) сверху, (3) справа, (4) снизу между полями внутри элемента управления

...

//+------------------------------------------------------------------+
//| Очищает элемент с заполнением его цветом и непрозрачностью       |
//+------------------------------------------------------------------+
void CWinFormBase::Erase(const color colour,const uchar opacity,const bool redraw=false)
  {
//--- Закрашиваем элемент с указанным цветом и флагом необходимости перерисовки
   CGCnvElement::Erase(colour,opacity,redraw);
//--- Если у объекта есть рамка - рисуем её
   if(this.BorderStyle()!=FRAME_STYLE_NONE && redraw)
      this.DrawFormFrame(this.BorderSizeTop(),this.BorderSizeBottom(),this.BorderSizeLeft(),this.BorderSizeRight(),this.BorderColor(),this.Opacity(),this.BorderStyle());
//--- Обновляем элемент с указанным флагом необходимости перерисовки
   this.Update(redraw);
  }
//+------------------------------------------------------------------+
//| Очищает элемент заливкой градиентом                              |
//+------------------------------------------------------------------+
void CWinFormBase::Erase(color &colors[],const uchar opacity,const bool vgradient,const bool cycle,const bool redraw=false)
  {
//--- Закрашиваем элемент с указанным массивом цветов и флагом необходимости перерисовки
   CGCnvElement::Erase(colors,opacity,vgradient,cycle,redraw);
//--- Если у объекта есть рамка - рисуем её
   if(this.BorderStyle()!=FRAME_STYLE_NONE && redraw)
      this.DrawFormFrame(this.BorderSizeTop(),this.BorderSizeBottom(),this.BorderSizeLeft(),this.BorderSizeRight(),this.BorderColor(),this.Opacity(),this.BorderStyle());
//--- Обновляем элемент с указанным флагом необходимости перерисовки
   this.Update(redraw);
  }
//+------------------------------------------------------------------+


Объявим методы, возвращающие описания свойств элементов управления:

//--- Возвращает промежуток (1) слева, (2) сверху, (3) справа, (4) снизу между полями внутри элемента управления
   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);                   }
   
//--- Возвращает описание (1) целочисленного, (2) вещественного и (3) строкового свойства ордера
   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);

//--- Возвращает описание (1) режима автоматического изменения размера элемента под содержимое,
//--- (2) режима привязки границ элемента к контейнеру,
//--- (3) состояния элемента управления, имеющего флажок проверки,
//--- (4) стиля начертания шрифта, (5) типа толщины шрифта, (6) стиля рамки элемента управления
   string            AutoSizeModeDescription(void);
   string            DockModeDescription(void);
   string            CheckStateDescription(void);
   string            FontStyleDescription(void);
   string            FontBoldTypeDescription(void);
   string            BorderStyleDescription(void);

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


В конструкторе класса заменим присвоение значений переменным на установку значений свойств при помощи нового метода:

//+------------------------------------------------------------------+
//| Конструктор                                                      |
//+------------------------------------------------------------------+
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)
  {
//--- Установим тип графического элемента и тип объекта библиотеки как базовый WinForms-объект
   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; 
//--- Инициализируем все переменные
   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;
  }
//+------------------------------------------------------------------+


Метод, возвращающий описание стиля начертания шрифта:

//+------------------------------------------------------------------+
//| Возвращает описание стиля начертания шрифта                      |
//+------------------------------------------------------------------+
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)
     );
  }
//+------------------------------------------------------------------+

В зависимости от установленного стиля начертания шрифта возвращается соответствующее текстовое сообщение.


Метод, возвращающий описание типа толщины шрифта:

//+------------------------------------------------------------------+
//| Возвращает описание типа толщины шрифта                          |
//+------------------------------------------------------------------+
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);
  }
//+------------------------------------------------------------------+

Перечисление ENUM_FW_TYPE имеет в своём составе такие константы:

//+------------------------------------------------------------------+
//| Список типов толщины шрифта                                      |
//+------------------------------------------------------------------+
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
  };
//+------------------------------------------------------------------+

Для, например, типа толщины "Regular", нам нужно взять подстроку из константы FW_TYPE_REGULAR начиная с позиции 8. После вычленения подстроки из наименования константы мы получим строку "REGULAR". Здесь все символы прописные. Теперь нам нужно сделать все символы строчными кроме первого.
Для этого достаточно к коду символа добавить смещение 32 (0x20), так как коды строчных символов отличаются от прописных ровно на 32. Что мы делаем? Извлечённую подстроку, состоящую из прописных символов, заносим в uchar-массив, и далее в цикле по всем символам в массиве (по каждой букве) прибавляем к значению символа значение 32. Так как первый символ (в ячейке 0 массива) менять не нужно, то цикл начинаем с ячейки 1 массива — со второго символа. В итоге возвращаем преобразованный обратно в строку изменённый uchar-массив.


Метод, возвращающий описание стиля рамки элемента управления:

//+------------------------------------------------------------------+
//| Возвращает описание стиля рамки элемента управления              |
//+------------------------------------------------------------------+
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)
     );
  }
//+------------------------------------------------------------------+

В зависимости от стиля рамки, установленного для объекта, возвращается соответствующее текстовое сообщение.


Метод, возвращающий описание целочисленного свойства элемента:

//+------------------------------------------------------------------+
//| Возвращает описание целочисленного свойства элемента             |
//+------------------------------------------------------------------+
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)
         )  :
      ""
     );
  }
//+------------------------------------------------------------------+

В метод передаётся целочисленное свойство объекта. В зависимости от свойства, переданного в метод и флага поддержания объектом данного свойства, строится возвращаемая из метода строка. Подобные методы есть практически в каждом объекте библиотеки, и тут его повторно рассматривать не имеет смысла. Замечу лишь, что пока у нас каждый графический элемент поддерживает все свойства графических элементов. Методы, возвращающие флаги поддержания того, или иного свойства графических элементов мы будем добавлять в WinForms-объекты по мере возможности визуального контроля за значениями этих свойств и инструментария для их изменения мышкой или с клавиатуры — после создания большинства WinForms-объектов и функционала их интерактивности.


Метод, возвращающий описание вещественного свойства элемента:

//+------------------------------------------------------------------+
//| Возвращает описание вещественного свойства элемента              |
//+------------------------------------------------------------------+
string CWinFormBase::GetPropertyDescription(ENUM_CANV_ELEMENT_PROP_DOUBLE property,bool only_prop=false)
  {
   return("");
  }
//+------------------------------------------------------------------+

Так как вещественных свойств у нас пока нету для графических элементов, метод возвращает пустую строку.


Метод, возвращающий описание строкового свойства элемента:

//+------------------------------------------------------------------+
//| Возвращает описание строкового свойства элемента                 |
//+------------------------------------------------------------------+
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)+"\""       :
      ""
     );
  }
//+------------------------------------------------------------------+

В метод передаётся строковое свойство объекта. В зависимости от свойства, переданного в метод, строится возвращаемая из метода строка.


Метод, возвращающий описание режима автоматического изменения размера элемента под содержимое:

//+------------------------------------------------------------------+
//| Возвращает описание режима автоматического                       |
//| изменения размера элемента под содержимое                        |
//+------------------------------------------------------------------+
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)
     );
  }
//+------------------------------------------------------------------+

В зависимости от режима автоматического изменения размеров элемента, возвращается соответствующее текстовое сообщение.


Метод, возвращающий описание режима привязки границ элемента к контейнеру:

//+------------------------------------------------------------------+
//| Возвращает описание режима привязки границ элемента к контейнеру |
//+------------------------------------------------------------------+
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)
     );
  }
//+------------------------------------------------------------------+

В зависимости от режима привязки границ, возвращается соответствующее текстовое сообщение.


Метод, возвращающий описание состояния элемента управления, имеющего флажок проверки:

//+------------------------------------------------------------------+
//| Возвращает описание состояния элемента                           |
//| управления, имеющего флажок проверки                             |
//+------------------------------------------------------------------+
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)
     );
  }
//+------------------------------------------------------------------+

В зависимости от состояния флажка проверки элемента, возвращается соответствующее текстовое сообщение.


В файле \MQL5\Include\DoEasy\Objects\Graph\WForms\Common Controls\CommonBase.mqh класса базового объекта стандартных элементов управления WinForms-объектов сделаем небольшие доработки.

Сменим тип метода, автоматически устанавливающего ширину и высоту элемента, с void на bool — для каждого объекта-наследника реализация этого метода может быть своей, поэтому при необходимости изменения логики данного метода, он будет переопределяться в разных классах по-своему. Но необходимо, чтобы он мог возвращать результат своей работы. Если же объект не изменяет свои размеры, то и переопределять метод в классе не нужно — здесь он просто возвращает true, ничего при этом не делая:

//+------------------------------------------------------------------+
//| Класс базового объекта-стандартного элемента управления WForms   |
//+------------------------------------------------------------------+
class CCommonBase : public CWinFormBase
  {
private:

protected:
//--- Автоматически устанавливает ширину и высоту элемента
   virtual bool      AutoSetWH(void)   { return true; }
//--- Инициализирует переменные
   virtual void      Initialize(void);
   
public:


В методе инициализации переменных заменим запись значений ширины рамки в переменные на установку этих значений при помощи метода и переименуем метод, устанавливающий цвет фона:

//+------------------------------------------------------------------+
//| Инициализирует переменные                                        |
//+------------------------------------------------------------------+
void CCommonBase::Initialize(void)
  {
//--- Очищаем все списки объекта и устанавливаем им флаги сортированных списков
   this.m_list_elements.Clear();
   this.m_list_elements.Sort();
   this.m_list_tmp.Clear();
   this.m_list_tmp.Sort();
//--- Объекта тени нет у стандартного элемента управления
   this.m_shadow_obj=NULL;
   this.m_shadow=false;
//--- Ширина рамки объекта с каждой его стороны по умолчанию равна 1 пиксель
   this.SetBorderSizeAll(1);
//--- У объекта нет градиентной заливки (как вертикальной, так и горизонтальной)
   this.m_gradient_v=false;
   this.m_gradient_c=false;
//--- Сбросим все "рабочие" флаги и переменные
   this.m_mouse_state_flags=0;
   this.m_offset_x=0;
   this.m_offset_y=0;
   CGCnvElement::SetInteraction(false);
//--- Создадим объект анимации и добавим его в список для хранения таких объектов
   this.m_animations=new CAnimations(CGCnvElement::GetObject());
   this.m_list_tmp.Add(this.m_animations);
//--- Устанавливаем прозрачный цвет для фона объекта
   this.SetBackgroundColor(CLR_CANV_NULL);
   this.SetOpacity(0);
//--- Установим цвет и непрозрачность текста по умолчанию и отсутствие рамки объекта
   this.SetForeColor(CLR_DEF_FORE_COLOR);
   this.SetForeColorOpacity(CLR_DEF_FORE_COLOR_OPACITY);
   this.SetBorderStyle(FRAME_STYLE_NONE);
//--- Установим параметры текста по умолчанию
   this.SetFont(DEF_FONT,DEF_FONT_SIZE);
   this.SetText("");
   this.SetTextAnchor(FRAME_ANCHOR_LEFT_TOP);
   this.SetTextAlign(ANCHOR_LEFT_UPPER);
//--- Установим параметры объекта по умолчанию
   this.SetAutoSize(false,false);
   this.SetMarginAll(3);
   this.SetPaddingAll(0);
   this.SetEnabled(true);
   this.SetVisible(true,false);
  }
//+------------------------------------------------------------------+


Переименуем методы, возвращающие значения ширины рамки:

//+------------------------------------------------------------------+
//| Очищает элемент с заполнением его цветом и непрозрачностью       |
//+------------------------------------------------------------------+
void CCommonBase::Erase(const color colour,const uchar opacity,const bool redraw=false)
  {
//--- Закрашиваем элемент с указанным цветом и флагом необходимости перерисовки
   CGCnvElement::Erase(colour,opacity,redraw);
//--- Если у объекта есть рамка - рисуем её
   if(this.BorderStyle()!=FRAME_STYLE_NONE && redraw)
      this.DrawFormFrame(this.BorderSizeTop(),this.BorderSizeBottom(),this.BorderSizeLeft(),this.BorderSizeRight(),this.BorderColor(),255,this.BorderStyle());
//--- Обновляем элемент с указанным флагом необходимости перерисовки
   this.Update(redraw);
  }
//+------------------------------------------------------------------+
//| Очищает элемент заливкой градиентом                              |
//+------------------------------------------------------------------+
void CCommonBase::Erase(color &colors[],const uchar opacity,const bool vgradient,const bool cycle,const bool redraw=false)
  {
//--- Закрашиваем элемент с указанным массивом цветов и флагом необходимости перерисовки
   CGCnvElement::Erase(colors,opacity,vgradient,cycle,redraw);
//--- Если у объекта есть рамка - рисуем её
   if(this.BorderStyle()!=FRAME_STYLE_NONE && redraw)
      this.DrawFormFrame(this.BorderSizeTop(),this.BorderSizeBottom(),this.BorderSizeLeft(),this.BorderSizeRight(),this.BorderColor(),255,this.BorderStyle());
//--- Обновляем элемент с указанным флагом необходимости перерисовки
   this.Update(redraw);
  }
//+------------------------------------------------------------------+


В классе объекта текстовой метки в файле \MQL5\Include\DoEasy\Objects\Graph\WForms\Common Controls\Label.mqh у нас организован метод, рассчитывающий координаты расположения надписи в зависимости от точки привязки текста и его выравнивания. Но всё это сделано в методе перерисовки объекта. Это не оптимально — многие объекты, унаследованные от этого класса, могут тоже нуждаться в позиционировании надписи. Поэтому из метода перерисовки текстовой метки перенесём расчёт координат текста в новый метод, и будем его вызывать там, где необходимо, и не только в классе объекта-текстовой метки, но и в его наследниках.

В защищённой секции класса изменим тип метода автоматической установки размеров элемента, объявим метод, устанавливающий координаты и точку привязки текста в зависимости от режима его выравнивания, а в публичной секции класса объявим метод, устанавливающий флаг автоматического изменения размера элемента под содержимое:

//+------------------------------------------------------------------+
//| Класс объекта Label элементов управления WForms                  |
//+------------------------------------------------------------------+
class CLabel : public CCommonBase
  {
private:

protected:
//--- Автоматически устанавливает ширину и высоту элемента
   virtual bool      AutoSetWH(void);
//--- Устанавливает координаты и точку привязки текста в зависимости от режима его выравнивания
   void              SetTextParamsByAlign(int &x,int &y);
public:
//--- Перерисовывает объект
   virtual void      Redraw(bool redraw);
//--- Устанавливает текст элемента
   virtual void      SetText(const string text)
                       {
                        CWinFormBase::SetText(text);
                        if(this.AutoSize())
                           this.AutoSetWH();
                       }
//--- Устанавливает флаг автоматического изменения размера элемента под содержимое
   virtual void      SetAutoSize(const bool flag,const bool redraw);

//--- Конструктор


Так как метод расчёта координат текста теперь вынесен в отдельный метод, то метод перерисовки объекта стал короче:

//+------------------------------------------------------------------+
//| Перерисовывает объект                                            |
//+------------------------------------------------------------------+
void CLabel::Redraw(bool redraw)
  {
//--- Заполняем объект цветом фона с полной прозрачностью
   this.Erase(this.BackgroundColor(),0,true);
//--- Объявляем переменные для координат X и Y и устанавливаем их значения в зависимости от выравнивания текста
   int x=0,y=0;
   this.SetTextParamsByAlign(x,y);
//--- Рисуем текст в установленных координатах объекта и точкой привязки текста и обновляем объект 
   this.Text(x,y,this.Text(),this.ForeColor(),this.ForeColorOpacity(),this.TextAnchor());
   this.Update(redraw);
  }
//+------------------------------------------------------------------+


Метод, устанавливающий координаты и точку привязки текста в зависимости от режима его выравнивания:

//+------------------------------------------------------------------+
//| Устанавливает координаты и точку привязки текста                 |
//| в зависимости от режима его выравнивания                         |
//+------------------------------------------------------------------+
void CLabel::SetTextParamsByAlign(int &x,int &y)
  {
//--- В зависимости от типа выравнивания текста на элементе
   switch(this.TextAlign())
     {
      //--- Текст рисуется в левом верхнем углу объекта
      case ANCHOR_LEFT_UPPER : 
        //--- Устанавливаем координату точки привязки текста
        x=this.BorderSizeLeft();
        y=this.BorderSizeTop();
        //--- Устанавливаем точку привязки текста слева вверху
        this.SetTextAnchor(FRAME_ANCHOR_LEFT_TOP);
        break;
      //--- Текст рисуется от левой грани объекта в центре по вертикали
      case ANCHOR_LEFT : 
        //--- Устанавливаем координату точки привязки текста
        x=this.BorderSizeLeft();
        y=this.Height()/2;
        //--- Устанавливаем точку привязки текста слева в центре
        this.SetTextAnchor(FRAME_ANCHOR_LEFT_CENTER);
        break;
      //--- Текст рисуется в левом нижнем углу объекта
      case ANCHOR_LEFT_LOWER : 
        //--- Устанавливаем координату точки привязки текста
        x=this.BorderSizeLeft();
        y=this.Height()-this.BorderSizeBottom();
        //--- Устанавливаем точку привязки текста слева внизу
        this.SetTextAnchor(FRAME_ANCHOR_LEFT_BOTTOM);
        break;
      //--- Текст рисуется по центру нижней грани объекта
      case ANCHOR_LOWER : 
        //--- Устанавливаем координату точки привязки текста
        x=this.Width()/2;
        y=this.Height()-this.BorderSizeBottom();
        //--- Устанавливаем точку привязки текста снизу в центре
        this.SetTextAnchor(FRAME_ANCHOR_CENTER_BOTTOM);
        break;
      //--- Текст рисуется в правом нижнем углу объекта
      case ANCHOR_RIGHT_LOWER : 
        //--- Устанавливаем координату точки привязки текста
        x=this.Width()-this.BorderSizeRight();
        y=this.Height()-this.BorderSizeBottom();
        //--- Устанавливаем точку привязки текста справа внизу
        this.SetTextAnchor(FRAME_ANCHOR_RIGHT_BOTTOM);
        break;
      //--- Текст рисуется от правой грани объекта в центре по вертикали
      case ANCHOR_RIGHT : 
        //--- Устанавливаем координату точки привязки текста
        x=this.Width()-this.BorderSizeRight();
        y=this.Height()/2;
        //--- Устанавливаем точку привязки текста справа в центре
        this.SetTextAnchor(FRAME_ANCHOR_RIGHT_CENTER);
        break;
      //--- Текст рисуется в правом верхнем углу объекта
      case ANCHOR_RIGHT_UPPER : 
        //--- Устанавливаем координату точки привязки текста
        x=this.Width()-this.BorderSizeRight();
        y=this.BorderSizeTop();
        //--- Устанавливаем точку привязки текста справа вверху
        this.SetTextAnchor(FRAME_ANCHOR_RIGHT_TOP);
        break;
      //--- Текст рисуется по центру верхней грани объекта
      case ANCHOR_UPPER : 
        //--- Устанавливаем координату точки привязки текста
        x=this.Width()/2;
        y=this.BorderSizeTop();
        //--- Устанавливаем точку привязки текста сверху в центре
        this.SetTextAnchor(FRAME_ANCHOR_CENTER_TOP);
        break;
      //--- Текст рисуется по центру объекта
      //---ANCHOR_CENTER
      default:
        //--- Устанавливаем координату точки привязки текста
        x=this.Width()/2;
        y=this.Height()/2;
        //--- Устанавливаем точку привязки текста в центре
        this.SetTextAnchor(FRAME_ANCHOR_CENTER);
        break;
     }
  }
//+------------------------------------------------------------------+

В метод просто перенесены строки расчёта координат текста из метода перерисовки объекта. Но здесь теперь используются переименованные ранее методы.

Так как виртуальный метод, автоматически устанавливающий ширину и высоту элемента, теперь должен возвращать результат, то вместо просто установки новой ширины и высоты в свойства объекта, метод записывает в возвращаемую переменную результат изменения размеров объекта:

//+------------------------------------------------------------------+
//| Автоматически устанавливает ширину и высоту элемента             |
//+------------------------------------------------------------------+
bool CLabel::AutoSetWH(void)
  {
//--- Определяем переменные для получения ширины и высоты надписи
   int w=0, h=0;
//--- Получаем ширину и высоту в зависимости от текста объекта
   CGCnvElement::TextSize(this.Text()!="" && this.Text()!=NULL ? this.Text() : " ",w,h);
//--- Прибавляем к полученной ширине значения Margin объекта слева и справа
   w+=(this.MarginLeft()+this.MarginRight());
//--- Если ширину получить не удалось - устанавливаем её в три пикселя
   if(w==0)
      w=3;
//--- Прибавляем к полученной высоте значения Margin объекта сверху и снизу
   h+=(this.MarginTop()+this.MarginBottom());
//--- Если высоту получить не удалось - устанавливаем её как величину "размер шрифта" * коэффициент
   if(h==0)
      h=(int)ceil(FontSize()*1.625);
//--- Устанавливаем ширину и высоту объекта из полученных значений и записываем результат в res
   bool res=true;
   res &=this.SetWidth(w);
   res &=this.SetHeight(h);
//--- Возвращаем результат изменения ширины и высоты
   return res;
  }
//+------------------------------------------------------------------+


Метод, устанавливающий флаг автоматического изменения размера элемента под содержимое:

//+------------------------------------------------------------------+
//| Устанавливает флаг автоматического изменения                     |
//| размера элемента под содержимое                                  |
//+------------------------------------------------------------------+
void CLabel::SetAutoSize(const bool flag,const bool redraw)
  {
   if(flag && this.AutoSetWH())
      CWinFormBase::SetAutoSize(flag,redraw);
  }
//+------------------------------------------------------------------+

Если переданный в метод флаг установлен, и если удалось изменить размер объекта — записываем в свойства значение флага.


Сегодня будем делать класс WinForms-объекта RadioButton. По своему внутреннему содержанию объект похож на элемент управления CheckBox, реализованного в файле \MQL5\Include\DoEasy\Objects\Graph\WForms\Common Controls\CheckBox.mqh, но в отличие от последнего, объект RadioButton имеет вместо квадратного поля с галочкой, круглое поле с возможностью его выбора. Соответственно, нам выгодно унаследоваться от класса объекта CheckBox и переопределить методы рисования поля выбора. Удалим метод SetTextCoords(), так как он абсолютно повторяет метод родительского объекта SetTextParamsByAlign(), а чтобы все переменные объекта CheckBox были доступны в его наследниках, перенесём их из приватной секции в защищённую:

//+------------------------------------------------------------------+
//| Включаемые файлы                                                 |
//+------------------------------------------------------------------+
#include "Label.mqh"
//+------------------------------------------------------------------+
//| Класс объекта CheckBox элементов управления WForms               |
//+------------------------------------------------------------------+
class CCheckBox : public CLabel
  {
private:
//--- Устанавливает координаты X и Y флажка проверки
   void              SetCheckFlagCoords(int &x,int &y);
   void              SetTextCoords(int &x,int &y);
//--- Устанавливает сорректированные координаты текста в зависимости от выравнивания текста и флажка проверки
   void              SetCorrectTextCoords(void);

protected:
   int               m_text_x;                                       // Координата X текста
   int               m_text_y;                                       // Координата Y текста
   int               m_check_x;                                      // Координата X флажка
   int               m_check_y;                                      // Координата Y флажка
   int               m_check_w;                                      // Ширина флажка
   int               m_check_h;                                      // Высота флажка
//--- Автоматически устанавливает ширину и высоту элемента
   virtual bool      AutoSetWH(void);

Виртуальный метод, автоматически устанавливающий ширину и высоту элемента, должен теперь быть с типом bool.

В публичной секции класса изменим методы, устанавливающие состояние флажка проверки и элемента управления. Теперь перед установкой состояний, сначала будет проверяться текущее состояние, так как их установка должна вызвать перерисовку влажков и объекта, а не просто установить значение в свойства объекта. И напишем методы, устанавливающие флаги и цвета фона объекта:

public:
//--- Устанавливает (1) ширину, (2) высоту элемента,
   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) Устанавливает, (2) возвращает угол расположения (тип выравнивания) флажка проверки на элементе
   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) Устанавливает, (2) возвращает состояние флажка проверки
   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) Устанавливает, (2) возвращает состояние элемента управления
   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) Устанавливает, (2) возвращает флаг автоматического изменения флажка при его выборе
   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) Устанавливает, (2) возвращает цвет фона флажка проверки элемента управления
   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) Устанавливает, (2) возвращает непрозрачность цвета фона флажка проверки элемента управления
   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) Устанавливает, (2) возвращает цвет фона флажка проверки элемента управления при нажатии мышки на элемент управления
   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) Устанавливает, (2) возвращает цвет фона флажка проверки элемента управления при наведении мышки на элемент управления
   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) Устанавливает, (2) возвращает цвет рамки флажка проверки элемента управления
   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) Устанавливает, (2) возвращает непрозрачность цвета рамки флажка проверки элемента управления
   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) Устанавливает, (2) возвращает цвет рамки флажка проверки элемента управления при нажатии мышки на элемент управления
   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) Устанавливает, (2) возвращает цвет рамки флажка проверки элемента управления при наведении мышки на элемент управления
   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) Устанавливает, (2) возвращает цвет флажка проверки элемента управления
   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) Устанавливает, (2) возвращает непрозрачность цвета флажка проверки элемента управления
   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) Устанавливает, (2) возвращает цвет флажка проверки элемента управления при нажатии мышки на элемент управления
   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) Устанавливает, (2) возвращает цвет флажка проверки элемента управления при наведении мышки на элемент управления
   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);    }
   
//--- Перерисовывает объект
   virtual void      Redraw(bool redraw);

//--- Конструктор


В методе перерисовки объекта переименуем метод, устанавливающий цвет фона:

//+------------------------------------------------------------------+
//| Перерисовывает объект                                            |
//+------------------------------------------------------------------+
void CCheckBox::Redraw(bool redraw)
  {
//--- Заполняем объект цветом фона с полной прозрачностью
   this.Erase(this.BackgroundColor(),0,true);
//--- Устанавливаем скорректированные координаты текста относительно флажка проверки
   this.SetCorrectTextCoords();
//--- Рисуем текст и флажок выбора в установленных координатах объекта и точкой привязки и обновляем объект 
   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);
  }
//+------------------------------------------------------------------+


В методе SetCorrectTextCoords() вместо вызова удалённого метода SetTextCoords, пропишем вызов метода родительского класса SetTextParamsByAlign:

//+------------------------------------------------------------------+
//| Устанавливает корректные координаты текста в зависимости         |
//| от выравнивания текста и флажка проверки                         |
//+------------------------------------------------------------------+
void CCheckBox::SetCorrectTextCoords(void)
  {
//--- Устанавливаем координаты флажка и текста в зависимости от способа их выравнивания
   this.SetCheckFlagCoords(this.m_check_x,this.m_check_y);
   this.SetTextParamsByAlign(this.m_text_x,this.m_text_y);
//--- Получаем размеры текста
   int text_w=0, text_h=0;
   this.TextSize(this.Text(),text_w,text_h);
//--- В зависимости от расположения флажка проверки в границах объекта
//--- ...
//--- ...


Изменим метод, рисующий флажок выбора. Этот флажок должен рисоваться не на прозрачном фоне, как было ранее, а на фоне залитого цветом прямоугольника, обрамлённого рамкой. Галочка, рисуемая внутри этого поля, у нас получилась тонковатой, поэтому мы прорисуем не одну, а три линии, каждая из которой на один пиксель в центре будет выше, чем предыдущая. Конечно, это не верно с точки зрения масштабируемости флажка выбора, но относительный расчёт кооринат ломаной линии, рисующей флажок выбора, мы сделаем позже:

//+------------------------------------------------------------------+
//| Отображает флажок проверки по указанному состоянию               |
//+------------------------------------------------------------------+
void CCheckBox::ShowControlFlag(const ENUM_CANV_ELEMENT_CHEK_STATE state)
  {
//--- Рисуем закрашенный прямоугольник области флажка выбора
   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());
//--- Рисуем прямоугольник границ флажка
   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());
//--- Создаём массивы координат X и Y для рисования ломанной линии
   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};
//--- В зависимости от переданног в метод состояния флажка
   switch(state)
     {
      //--- Установленный флажок
      case CANV_ELEMENT_CHEK_STATE_CHECKED :
        //--- Рисуем ломанную линию в виде галочки внутри границ флажка
        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;
      //--- Неопределённое состояние
      case CANV_ELEMENT_CHEK_STATE_INDETERMINATE :
        //--- Рисуем закрашенный прямоугольник внутри границ флажка
        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;
      //--- Не установленный флажок
      default:
        break;
     }
  }
//+------------------------------------------------------------------+


Метод, автоматически устанавливающий ширину и высоту элемента, теперь имеет тип bool и возвращает результат:

//+------------------------------------------------------------------+
//| Автоматически устанавливает ширину и высоту элемента             |
//+------------------------------------------------------------------+
bool CCheckBox::AutoSetWH(void)
  {
//--- Определяем переменные для получения ширины и высоты надписи
   int w=0, h=0;
//--- Получаем ширину и высоту в зависимости от текста объекта
   CGCnvElement::TextSize(this.Text()!="" && this.Text()!=NULL ? this.Text() : " ",w,h);
//--- Прибавляем к полученной ширине значения Margin объекта слева и справа и размер флажка проверки
   w+=(this.MarginLeft()+this.MarginRight()+this.CheckWidth());
//--- Если ширина равна размеру флажка проверки - устанавливаем её в три пикселя + размер флажка проверки
   if(w==this.CheckWidth())
      w=this.CheckWidth()+3;
//--- Прибавляем к полученной высоте значения Margin объекта сверху и снизу
   h+=(this.MarginTop()+this.MarginBottom());
//--- Если высоту получить не удалось - устанавливаем её как величину "размер шрифта" * коэффициент
   if(h==0)
      h=(int)ceil(FontSize()*1.625);
//--- Если высота в итоге меньше размера флажка проверки - устанавливаем высоту равной высоте флажка проверки
   if(h<this.CheckHeight())
      h=this.CheckHeight();
//--- Устанавливаем ширину и высоту объекта из полученных значений и записываем результат в res
   bool res=true;
   res &=this.SetWidth(w);
   res &=this.SetHeight(h);
//--- Возвращаем результат изменения ширины и высоты
   return res;
  }
//+------------------------------------------------------------------+

Здесь точно так же, как и в вышерассмотренном методе класса объекта-текстовой метки. Объявляем переменную, записываем в неё результат изменения размеров и возвращаем его.


WinForms-объект RadioButton

Объект будет наследником WinForms-объекта CheckBox, так как практически повторяет его функционал и внутреннюю организацию.

В папке библиотеки \MQL5\Include\DoEasy\Objects\Graph\WForms\Common Controls\ создадим новый файл RadioButton.mqh класса CRadioButton.

Класс должен быть унаследован от класса CCheckBox, а его файл должен быть подключен к файлу создаваемого класса:

//+------------------------------------------------------------------+
//|                                                  RadioButton.mqh |
//|                                  Copyright 2022, MetaQuotes Ltd. |
//|                             https://mql5.com/ru/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2022, MetaQuotes Ltd."
#property link      "https://mql5.com/ru/users/artmedia70"
#property version   "1.00"
#property strict    // Нужно для mql4
//+------------------------------------------------------------------+
//| Включаемые файлы                                                 |
//+------------------------------------------------------------------+
#include "CheckBox.mqh"
//+------------------------------------------------------------------+
//| Класс объекта CheckBox элементов управления WForms               |
//+------------------------------------------------------------------+
class CRadioButton : public CCheckBox
  {
  }


В защищённой секции класса объявим виртуальный метод, отображающий флажок проверки, а в публичной секции — параметрический конструктор:

//+------------------------------------------------------------------+
//| Класс объекта CheckBox элементов управления WForms               |
//+------------------------------------------------------------------+
class CRadioButton : public CCheckBox
  {
private:

protected:
//--- Отображает флажок проверки по указанному состоянию
   virtual void      ShowControlFlag(const ENUM_CANV_ELEMENT_CHEK_STATE state);

public:

//--- Конструктор
                     CRadioButton(const long chart_id,
                                  const int subwindow,
                                  const string name,
                                  const int x,
                                  const int y,
                                  const int w,
                                  const int h);
  };
//+------------------------------------------------------------------+

Метод, отображающий флажок проверки, переопределяет метод родительского класса, так как здесь флажок круглый в отличие от квадратного у объекта CheckBox.

Рассмтрим эти методы.

Параметрический конструктор:

//+------------------------------------------------------------------+
//| Конструктор                                                      |
//+------------------------------------------------------------------+
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);
  }
//+------------------------------------------------------------------+

Здесь мы лишь определяем тип WinForms-объекта и перерисовываем объект. Всё остальное создаётся и устанавливается в конструкторе родительского класса.

Метод, отображающий флажок проверки по указанному состоянию:

//+------------------------------------------------------------------+
//| Отображает флажок проверки по указанному состоянию               |
//+------------------------------------------------------------------+
void CRadioButton::ShowControlFlag(const ENUM_CANV_ELEMENT_CHEK_STATE state)
  {
//--- Рисуем закрашенную окружность области флажка выбора
   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());
//--- Рисуем окружность в границах флажка
   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());
//--- В зависимости от переданног в метод состояния флажка
   switch(state)
     {
      //--- Установленный флажок
      case CANV_ELEMENT_CHEK_STATE_CHECKED :
        //--- Рисуем закрашенную окружность внутри границ флажка
        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;
      //--- Неопределённое состояние
      //--- Не установленный флажок
      default:
        break;
     }
  }
//+------------------------------------------------------------------+

По аналогии с родительским классом, здесь сначала рисуется закрашенная цветом фона окружность, далее рисуется её грань и, в зависимости от переданного в метод флага состояния либо внутри нарисованной окружности рисуется ещё одна — меньшего диаметра, либо ничего не рисуется.

И это всё, что нужно для создания этого объекта — всё остальное реализовано в родительских классах.


WinForms-объект Button

Объект-кнопка. По сути — это текстовая метка. У текстовой метки есть возможность рисовать границы объекта, есть возможность позиционировать текст внутри границ элемента управления. Метка лишь рисуется на прозрачном фоне, а кнопка — на закрашенном цветом. Поэтому объект-кнопка будет унаследована от объекта-текстовой метки.

В папке библиотеки \MQL5\Include\DoEasy\Objects\Graph\WForms\Common Controls\ создадим новый файл Button.mqh класса CButton.

Класс должен быть унаследован от класса CLabel, а его файл должен быть подключен к создаваемому:

//+------------------------------------------------------------------+
//|                                                       Button.mqh |
//|                                  Copyright 2022, MetaQuotes Ltd. |
//|                             https://mql5.com/ru/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2022, MetaQuotes Ltd."
#property link      "https://mql5.com/ru/users/artmedia70"
#property version   "1.00"
#property strict    // Нужно для mql4
//+------------------------------------------------------------------+
//| Включаемые файлы                                                 |
//+------------------------------------------------------------------+
#include "Label.mqh"
//+------------------------------------------------------------------+
//| Класс объекта Label элементов управления WForms                  |
//+------------------------------------------------------------------+
class CButton : public CLabel
  {
  }


В приватной секции класса объявим переменные для хранения координат надписи на кнопке, а в защищённой — виртуальный метод для автоматической установки размеров элемента управления:

class CButton : public CLabel
  {
private:
   int               m_text_x;                                       // Координата X текста
   int               m_text_y;                                       // Координата Y текста
protected:
//--- Автоматически устанавливает ширину и высоту элемента
   virtual bool      AutoSetWH(void);

public:


В публичной секции объявим виртуальный метод для перерисовки объекта и напишем методы для установки и возврата режима автоматического изменения размеров объекта под размер надписи:

public:
//--- Перерисовывает объект
   virtual void      Redraw(bool redraw);
//--- (1) Устанавливает, (2) возвращает режим автоматического изменения размера элемента под содержимое
   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); }

//--- Конструктор
                     CButton(const long chart_id,
                             const int subwindow,
                             const string name,
                             const int x,
                             const int y,
                             const int w,
                             const int h);
  };
//+------------------------------------------------------------------+


В параметрическом конструкторе указываем тип элемента управления, устанавливаем свойствам объекта значения по умолчанию и вызываем метод перерисовки:

//+------------------------------------------------------------------+
//| Конструктор                                                      |
//+------------------------------------------------------------------+
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);
  }
//+------------------------------------------------------------------+


Метод, перерисовывающий объект:

//+------------------------------------------------------------------+
//| Перерисовывает объект                                            |
//+------------------------------------------------------------------+
void CButton::Redraw(bool redraw)
  {
//--- Заполняем объект цветом фона с прозрачностью по умолчанию
   this.Erase(this.BackgroundColor(),CLR_DEF_CONTROL_STD_OPACITY,true);
//--- Объявляем переменные для координат X и Y и устанавливаем их значения в зависимости от выравнивания текста
   int x=0,y=0;
   CLabel::SetTextParamsByAlign(x,y);
//--- Рисуем текст в установленных координатах объекта и точкой привязки текста и обновляем объект 
   this.Text(x,y,this.Text(),this.ForeColor(),this.ForeColorOpacity(),this.TextAnchor());
   this.Update(redraw);
  }
//+------------------------------------------------------------------+

Сначала границы объекта заполняются цветом фона, затем на него выводится надпись в рассчитанных координитах в зависимости от выравнивания текста, и объект обновляется.

Метод, автоматически устанавливающий ширину и высоту элемента:

//+------------------------------------------------------------------+
//| Автоматически устанавливает ширину и высоту элемента             |
//+------------------------------------------------------------------+
bool CButton::AutoSetWH(void)
  {
//--- Определяем переменные для получения ширины и высоты надписи
   int w=0, h=0;
//--- Получаем ширину и высоту в зависимости от текста объекта
   CGCnvElement::TextSize(this.Text()!="" && this.Text()!=NULL ? this.Text() : " ",w,h);
//--- Прибавляем к полученной ширине значения Margin объекта слева и справа
   w+=(this.MarginLeft()+this.MarginRight());
//--- Если ширину получить не удалось - устанавливаем её в три пикселя
   if(w==0)
      w=3;
//--- Прибавляем к полученной высоте значения Margin объекта сверху и снизу
   h+=(this.MarginTop()+this.MarginBottom());
//--- Если высоту получить не удалось - устанавливаем её как величину "размер шрифта" * коэффициент
   if(h==0)
      h=(int)ceil(FontSize()*1.625);
//--- Устанавливаем ширину и высоту объекта из полученных значений и записываем результат в res
   bool res=true;
//--- Если режим автоизменени размера - только увеличение
   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);
     }
//--- Если режим автоизменени размера - увеличение и уменьшение
   else
     {
      if(w!=this.Width())
         res &=this.SetWidth(w);
      if(h!=this.Height())
         res &=this.SetHeight(h);
     }
//--- Возвращаем результат изменения ширины и высоты
   return res;
  }
//+------------------------------------------------------------------+

Метод практически идентичен таким же методам родительских классов, рассмотренные нами выше. Но здесь есть нюанс: размеры меняются в соответствии с установленым режимом изменения размеров. Т.е., если режим — только увеличение, то будут только увеличиваться размеры объекта в случае, если текст выходит за его пределы. Если же стоит режим увеличения и уменьшения размеров, то весь объект будет подгоняться под размеры текста внутри него.

Конечно же это ещё не всё, что будет из возможного функционала объекта-кнопки, но всё остальное будем добавлять в последующих статьях параллельно разработке других WinForms-объектов.


Объекты-контейнеры для создания внутри них привязанных объектов, должны знать о существовании вновь создаваемых нами элементов управения. Для этого нужно подключить файлы новых классов к файлам классов объектов-контейнеров.

Родительским классом для всех объектов-контейнеров является класс базового объекта-контейнера,
реализованного в файле \MQL5\Include\DoEasy\Objects\Graph\WForms\Containers\Container.mqh.

Подключим к нему файлы новых элементов управления. Вместо файла CheckBox.mqh" подключим файл объекта RadioButton, так как он является наследником элемента CheckBox, и поэтому они оба будут видны в классе:

//+------------------------------------------------------------------+
//|                                                    Container.mqh |
//|                                  Copyright 2022, MetaQuotes Ltd. |
//|                             https://mql5.com/ru/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2022, MetaQuotes Ltd."
#property link      "https://mql5.com/ru/users/artmedia70"
#property version   "1.00"
#property strict    // Нужно для mql4
//+------------------------------------------------------------------+
//| Включаемые файлы                                                 |
//+------------------------------------------------------------------+
#include "..\..\WForms\WinFormBase.mqh"
#include "..\..\WForms\Common Controls\RadioButton.mqh"
#include "..\..\WForms\Common Controls\Button.mqh"
//+------------------------------------------------------------------+


Так как метод создания нового графического объекта для каждого наследуемого класса свой, то здесь сделаем лишь объявление виртуального метода, а его реализацию удалим. Метод будет возвращать значение NULL:

//+------------------------------------------------------------------+
//| Класс базового объекта-контейнера элементов управления WForms    |
//+------------------------------------------------------------------+
class CContainer : public CWinFormBase
  {
private:
//--- Создаёт новый графический объект
   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; }

//--- Рассчитывает координаты привязки Dock-объектов


Переименуем публичные методы FrameWidth на методы BorderSize:

public:
//--- Возвращает размеры и координаты рабочей области
   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());
                       }

//--- Возвращает список прикреплённых WinForms-объектов с (1) любым, (2) указанным типом WinForms-объекта от базового и выше

...

//--- Устанавливает ширину рамки формы (1) слева, (2) сверху, (3) справа, (4) снизу, (5) всех сторон элемента управления
   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);
                       }

//--- Конструкторы


Из метода создания нового элемента удалим передачу в метод указателя на главный объект:

//--- Создаёт новый присоединённый элемент
   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);


В реализации метода пропишем создание всех известных элементов управления:

//+------------------------------------------------------------------+
//| Создаёт новый присоединённый элемент                             |
//+------------------------------------------------------------------+
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)
  {
//--- Если тип объекта - меньше, чем базовый WinForms-объект
   if(element_type<GRAPH_ELEMENT_TYPE_WF_BASE)
     {
      //--- сообщаем об ошибке и возвращаем false
      CMessage::ToLog(DFUN,MSG_PANEL_OBJECT_ERR_OBJ_MUST_BE_WFBASE);
      return false;
     }
//--- Если не удалось создать новый графический элемент - возвращаем false
   CWinFormBase *obj=CForm::CreateAndAddNewElement(element_type,x,y,w,h,colour,opacity,activity);
   if(obj==NULL)
      return false;
//--- Устанавливаем созданному объекту цвет текста как у базовой панели
   obj.SetForeColor(this.ForeColor());

//--- В зависимости от типа созданного объекта
   switch(obj.TypeGraphElement())
     {
      //--- Для WinForms-объектов "Контейнер", "Панель", "GroupBox"
      case GRAPH_ELEMENT_TYPE_WF_CONTAINER :
      case GRAPH_ELEMENT_TYPE_WF_PANEL :
      case GRAPH_ELEMENT_TYPE_WF_GROUPBOX :
        //--- устанавливаем цвет рамки равным цвету фона 
        obj.SetBorderColor(obj.BackgroundColor());
        break;
      //--- Для WinForms-объектов "Текстовая метка", "CheckBox", "RadioButton"
      case GRAPH_ELEMENT_TYPE_WF_LABEL       :
      case GRAPH_ELEMENT_TYPE_WF_CHECKBOX    :
      case GRAPH_ELEMENT_TYPE_WF_RADIOBUTTON :
        //--- устанавливаем цвет текста объекта в зависимости от переданного в метод:
        //--- либо цвет текста контейнера, либо переданный в метод.
        //--- Цвет рамки устанавливаем равным цвету текста
        obj.SetForeColor(colour==clrNONE ? this.ForeColor() : colour);
        obj.SetBorderColor(obj.ForeColor());
        break;
      //--- Для WinForms-объекта "Button"
      case GRAPH_ELEMENT_TYPE_WF_BUTTON      :
        //--- устанавливаем цвет текста объекта как цвет текста контейнера в зависимости от переданного в метод:
        //--- цвет фона устанавливаем  в зависимости от переданного в метод:
        //--- либо цвет фона стандартных элементов управления по умолчанию, либо переданный в метод.
        //--- Цвет рамки устанавливаем равным цвету текста
        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(this.AutoSize() && this.ElementsTotal()>0)
      this.AutoSizeProcess(redraw);
//--- Перерисовываем панель и все добавленные объекты и возвращаем true
   this.Redraw(redraw);
   return true;
  }
//+------------------------------------------------------------------+


Метод, подстраивающий размеры элемента под его внутреннее содержимое, был преобразован, но всё ещё работает некорректно, поэтому здесь лишь приложим его текущую реализацию без каких-либо пояснений, кроме уже имеющихся в комментариях к коду метода:

//+------------------------------------------------------------------+
//| Подстраивает размеры элемента под его внутреннее содержимое      |
//+------------------------------------------------------------------+
bool CContainer::AutoSizeProcess(const bool redraw)
  {
//--- Получаем список прикреплённых объектов с типом WinForms базовый и выше
   CArrayObj *list=this.GetListWinFormsObj();
   int maxcX=0;
   int maxcY=0;
//--- Рассчитываем максимальную координату правого и нижнего края из всех привязанных объектов
   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();
     }
//--- Рассчитываем необходимую ширину и высоту панели после подгонки её размеров под содержимое
   int w=maxcX-this.CoordX();
   int h=maxcY-this.CoordY();
//--- Рассчитываем количество пикселей, на которые необходимо изменить размер контейнера по ширине и высоте
   int excess_x=w-this.GetWidthWorkspace()-this.BorderSizeRight()-1;
   int excess_y=h-this.GetHeightWorkspace()-this.BorderSizeBottom()-1;

//--- Если размеры контейнера менять не нужно - возвращаем true
   if(excess_x==0 && excess_y==0)
      return true;

//--- Возвращаем результат изменения размеров контейнера
   return
     (
      //--- Если только увеличение размеров
      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) :
      //--- если увеличение и уменьшение размеров
      this.Resize(this.Width()+(excess_x!=0 ? excess_x : 0),this.Height()+(excess_y!=0 ? excess_y : 0),redraw)
     );
  }
//+------------------------------------------------------------------+

Остальные незначительные изменения в классе, являющиеся результатом экспериментов, и не относящиеся к теме этой статьи, здесь рассматривать не будем. Мы вернёмся к ним тогда, когда всё будет работать корректно.

Класс объекта-контейнера GroupBox в файле \MQL5\Include\DoEasy\Objects\Graph\WForms\Containers\GroupBox.mqh тоже был немного доработан.

Были скорректированы вызовы переименованных методов:

//--- Устанавливает стиль рамки
   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);
                       }
   
//--- Конструкторы

...

//+------------------------------------------------------------------+
//| Инициализирует переменные                                        |
//+------------------------------------------------------------------+
void CGroupBox::Initialize(void)
  {
//--- Очищаем все списки объекта и устанавливаем им флаги сортированных списков
   this.m_list_elements.Clear();
   this.m_list_elements.Sort();
   this.m_list_tmp.Clear();
   this.m_list_tmp.Sort();
//--- Объекта тени нет у GroupBox
   this.m_shadow_obj=NULL;
   this.m_shadow=false;
//--- Ширина рамки объекта с каждой его стороны по умолчанию равна 1 пиксель
   this.SetBorderSize(1,1,1,1);
//--- У объекта нет градиентной заливки (как вертикальной, так и горизонтальной)
   this.m_gradient_v=false;
   this.m_gradient_c=false;
//--- Сбросим все "рабочие" флаги и переменные
   this.m_mouse_state_flags=0;
   this.m_offset_x=0;
   this.m_offset_y=0;
   CGCnvElement::SetInteraction(false);
//--- Создадим объект анимации и добавим его в список для хранения таких объектов
   this.m_animations=new CAnimations(CGCnvElement::GetObject());
   this.m_list_tmp.Add(this.m_animations);
//--- Устанавливаем прозрачный цвет для фона объекта и цвет по умолчанию для рамки
   this.SetBackgroundColor(CLR_CANV_NULL);
   this.SetOpacity(0);
   this.SetBorderColor(CLR_DEF_FRAME_GBOX_COLOR);
//--- Установим цвет и непрозрачность текста по умолчанию и отсутствие рамки объекта
   this.SetForeColor(CLR_DEF_FORE_COLOR);
   this.SetForeColorOpacity(CLR_DEF_FORE_COLOR_OPACITY);
   this.SetBorderStyle(FRAME_STYLE_SIMPLE);
//--- Установим параметры текста по умолчанию
   this.SetFont(DEF_FONT,DEF_FONT_SIZE);
   this.SetText("GroupBox");
   this.SetTextAnchor(FRAME_ANCHOR_LEFT_TOP);
   this.SetTextAlign(ANCHOR_LEFT_UPPER);
//--- Установим параметры объекта по умолчанию
   this.SetAutoSize(false,false);
   this.SetMarginAll(3);
   this.SetPaddingAll(3);
   this.SetEnabled(true);
   this.SetVisible(true,false);
  }
//+------------------------------------------------------------------+

...

//+------------------------------------------------------------------+
//| Рисует рамку                                                     |
//+------------------------------------------------------------------+
void CGroupBox::DrawFrame(void)
  {
//--- Получаем половину высоты текста
   int w=0;
   int h=0;
   this.TextSize(Text(),w,h);
   int height=this.Height()-h/2;
//--- В зависимости от стиля рамки рисуем нужный её тип
   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(this.Text()!="")
      this.DrawRectangleFill(5,h/2-1,w+7,h/2+this.BorderSizeTop()+1,CLR_CANV_NULL,0);
  }
//+------------------------------------------------------------------+


В виртуальном методе, создающем новый графический объект, прописано создание всех известных на сейчас элементов:

//+------------------------------------------------------------------+
//| Создаёт новый графический объект                                 |
//+------------------------------------------------------------------+
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;
  }
//+------------------------------------------------------------------+


В классе-контейнере объекта "Панель" в файле \MQL5\Include\DoEasy\Objects\Graph\WForms\Containers\Panel.mqh тоже нужно сделать некоторые доработки.

В методах установки промежутков нужно перенести расчёт и установку смещения подложки после установки её координаты и размера:

//--- Устанавливает промежуток (1) слева, (2) сверху, (3) справа, (4) снизу, (5) со всех сторон внутри элемента управления
   virtual void      SetPaddingLeft(const uint value)
                       {
                        CWinFormBase::SetPaddingLeft(value);
                        if(this.m_underlay!=NULL)
                          {
                           //--- Устанавливаем координату X и ширину подложки
                           this.SetCoordXUnderlay(this.CoordX()+this.PaddingLeft());
                           this.SetWidthUnderlay(this.Width()-this.PaddingLeft()-this.PaddingRight());
                           //--- Устанавливаем значение смещения подложки по оси X
                           this.m_underlay.SetCoordXRelative(this.m_underlay.CoordX()-this.CoordX());
                          }
                       }
   virtual void      SetPaddingTop(const uint value)
                       {
                        CWinFormBase::SetPaddingTop(value);
                        if(this.m_underlay!=NULL)
                          {
                           //--- Устанавливаем координату Y и высоту подложки
                           this.SetCoordYUnderlay(this.CoordY()+this.PaddingTop());
                           this.SetHeightUnderlay(this.Height()-this.PaddingTop()-this.PaddingBottom());
                           //--- Устанавливаем значение смещения подложки по оси Y
                           this.m_underlay.SetCoordYRelative(this.m_underlay.CoordY()-this.CoordY());
                          }
                       }

Ранее расчёт смещения выполнялся до расчёта координат и размеров, что после внесения изменений вызывало ошибку расчёта смещений.


В методе создания нового графического объекта тоже добавим создание всех известных элементов:

//+------------------------------------------------------------------+
//| Создаёт новый графический объект                                 |
//+------------------------------------------------------------------+
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;
  }
//+------------------------------------------------------------------+


В методе, устанавливающем все параметры подложки, тоже сместим расчёт смещения ниже расчётов координат:

//+------------------------------------------------------------------+
//| Устанавливает все параметры подложки                             |
//+------------------------------------------------------------------+
bool CPanel::SetUnderlayParams(void)
  {
//--- Устанавливаем тип объекта
   this.m_underlay.SetTypeElement(GRAPH_ELEMENT_TYPE_WF_UNDERLAY);
//--- Устанавливаем координаты и размеры подложки
   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());
//--- Устанавливаем в переменные значения смещения подложки по осям X и Y
   this.m_underlay.SetCoordXRelative(this.m_underlay.CoordX()-this.CoordX());
   this.m_underlay.SetCoordYRelative(this.m_underlay.CoordY()-this.CoordY());
   return res;
  }
//+------------------------------------------------------------------+


В методе Move изменим расчёт величин смещения объекта:

//--- Смещаем все привязанные объекты
   if(!this.MoveDependentObj(x+this.GetCoordXUnderlayRelative(),y+this.GetCoordYUnderlayRelative(),false))
      return false;

Теперь здесь всё проще:

//+------------------------------------------------------------------+
//| Обновляет координаты элемента                                    |
//+------------------------------------------------------------------+
bool CPanel::Move(const int x,const int y,const bool redraw=false)
  {
//--- Получаем указатели на базовый и главный объекты в иерархии связанных объектов и объект-тень
   CGCnvElement *base=this.GetBase();
   CGCnvElement *main=this.GetMain();
   CShadowObj   *shadow=this.GetShadowObj();
//--- Если элемент не перемещаемый, и это базовый объект - уходим
   if(!this.Movable() && main==NULL)
      return false;
//--- Если есть тень у объекта и не удалось записать новые значения координат в свойства объекта-тени - возвращаем 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;
     }
//--- Если не удалось записать новые значения координат в свойства графического объекта - возвращаем false
   if(!this.SetCoordX(x) || !this.SetCoordY(y))
      return false;
//--- Если не удалось сместить подложку - возвращаем false
   if(this.m_underlay!=NULL && !this.m_underlay.Move(x+this.GetCoordXUnderlayRelative(),y+this.GetCoordYUnderlayRelative()))
      return false;
//--- Смещаем все привязанные объекты
   if(!this.MoveDependentObj(x,y,false))
      return false;
   //--- Если стоит флаг обновления, и это главный объект иерархии - перерисовываем график.
   if(redraw && main==NULL)
      ::ChartRedraw(this.ChartID());
   //--- Возвращаем true
   return true;
  }
//+------------------------------------------------------------------+

Так как теперь относительные координаты всех объектов рассчитываются, а не используются изначально установленные, то и не нужно ничего корректировать здесь — просто подставляем для перемещения переданные в метод значения X и Y.


В классе-коллекции графических элементов в файле \MQL5\Include\DoEasy\Collections\GraphElementsCollection.mqh было сделано много исправлений, так как мы переименовали сегодня некоторые методы, и обращение к ним в этом классе активно используется. Описывать простые переименования, думаю, здесь не имеет смысла — все похожие изменения мы рассматривали сегодня на протяжении статьи. Поэтому с такими изменениями можно будет ознакомиться самостоятельно в прикреплённых к статье файлах.

Но вот на чём необходимо заострить внимание, так это на поиске прикреплённых к панели объектов и создании списка объектов взаимодействия, что позволяет определить с каким именно объектом должна взаимодействовать мышка.

В методе, возвращающем указатель на форму, находящуюся под курсором, нам нужно добавить два блока кода (одинаковые, и после некоторого времени тестирования, скорее всего вынесем в отдельный метод). В этих блоках как раз создаётся список всех прикреплённых объектов к панели — их иерархия, и самый последний из них возвращается как тот объект, с которым необходимо взаимодействовать:

//+------------------------------------------------------------------+
//| Возвращает указатель на форму, находящуюся под курсором          |
//+------------------------------------------------------------------+
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)
  {
//--- Устанавливаем идентификатор расширенного стандартного графического объекта в -1 
//--- и индекс точки привязки, которой управляет форма, в -1
   obj_ext_id=WRONG_VALUE;
   form_index=WRONG_VALUE;
//--- Инициализируем состояние мышки относительно формы
   mouse_state=MOUSE_FORM_STATE_NONE;
//--- Объявим указатели на объекты класса-коллекции графических элементов
   CGCnvElement *elm=NULL;
   CForm *form=NULL;
//--- Получим список объектов, для которых установлен флаг взаимодействия (должен быть всего один объект)
   CArrayObj *list=CSelect::ByGraphCanvElementProperty(GetListCanvElm(),CANV_ELEMENT_PROP_INTERACTION,true,EQUAL);
//--- Если список получить удалось и он не пустой
   if(list!=NULL && list.Total()>0)
     {
      //--- Получаем единственный в нём графический элемент
      elm=list.At(0);
      //--- Если этот элемент - объект-форма, или её наследники
      if(elm.TypeGraphElement()>=GRAPH_ELEMENT_TYPE_WF_BASE)
        {
         //--- Присваиваем указатель на элемент указателю на объект-форму
         form=elm;
         //--- Получаем состояние мышки относительно формы
         mouse_state=form.MouseFormState(id,lparam,dparam,sparam);
         //--- Если курсор в пределах формы
         if(mouse_state>MOUSE_FORM_STATE_OUTSIDE_FORM_WHEEL)
           {
            //--- Создадим список объектов взаимодействия
            int count=form.CreateListInteractObj();
            //--- Если в списке есть объекты
            if(count>0)
              {
               //--- В цикле по созданному списку
               for(int j=count-1;j>WRONG_VALUE;j--)
                 {
                  //--- получаем очередной объект-форму
                  CForm *obj=form.GetInteractForm(j);
                  if(obj==NULL)
                     continue;
                  //--- если курсор мышки находится над объектом - записываем его в указатель и прерываем цикл
                  if(obj.MouseFormState(id,lparam,dparam,sparam)>MOUSE_FORM_STATE_OUTSIDE_FORM_WHEEL)
                    {
                     form=obj;
                     break;
                    }
                 }
              }
            //--- Возвращаем найденный объект-форму
            return form;
           }
        }
     }
//--- Если нет ни одного объекта-формы с установленным флагом взаимодействия -
//--- в цикле по всем объектам класса-коллекции графических элементов
   int total=this.m_list_all_canv_elm_obj.Total();
   for(int i=0;i<total;i++)
     {
      //--- получаем очередной элемент
      elm=this.m_list_all_canv_elm_obj.At(i);
      if(elm==NULL)
         continue;
      //--- если полученный элемент - объект-форма, или её наследники
      if(elm.TypeGraphElement()>=GRAPH_ELEMENT_TYPE_WF_BASE)
        {
         //--- Присваиваем указатель на элемент указателю на объект-форму
         form=elm;
         //--- Получаем состояние мышки относительно формы
         mouse_state=form.MouseFormState(id,lparam,dparam,sparam);
         //--- Если курсор в пределах формы - возвращаем указатель на форму
         if(mouse_state>MOUSE_FORM_STATE_OUTSIDE_FORM_WHEEL)
           {
            //--- Создадим список объектов взаимодействия
            int count=form.CreateListInteractObj();
            //--- Если в списке есть объекты
            if(count>0)
              {
               //--- В цикле по созданному списку
               for(int j=count-1;j>WRONG_VALUE;j--)
                 {
                  //--- получаем очередной объект-форму
                  CForm *obj=form.GetInteractForm(j);
                  if(obj==NULL)
                     continue;
                  //--- если курсор мышки находится над объектом - записываем его в указатель и прерываем цикл
                  if(obj.MouseFormState(id,lparam,dparam,sparam)>MOUSE_FORM_STATE_OUTSIDE_FORM_WHEEL)
                    {
                     form=obj;
                     break;
                    }
                 }
              }
            //--- Возвращаем найденный объект-форму
            return form;
           }
        }
     }
//--- Если нет ни одного объекта-формы из списка-коллекции -
//--- Получаем список расширенных стандартных графических объектов
   list=this.GetListStdGraphObjectExt();
   if(list!=NULL)
     {
      //--- в цикле по всем расширенным стандартным графическим объектам
      for(int i=0;i<list.Total();i++)
        {
         //--- получаем очередной графический объект,
         CGStdGraphObj *obj_ext=list.At(i);
         if(obj_ext==NULL)
            continue;
         //--- получаем объект его инструментария,
         CGStdGraphObjExtToolkit *toolkit=obj_ext.GetExtToolkit();
         if(toolkit==NULL)
            continue;
         //--- обрабатываем событие изменения графика для текущего графического объекта
         obj_ext.OnChartEvent(CHARTEVENT_CHART_CHANGE,lparam,dparam,sparam);
         //--- Получаем общее количество объектов-форм, созданных для текущего графического объекта
         total=toolkit.GetNumControlPointForms();
         //--- В цикле по всем объектам-формам
         for(int j=0;j<total;j++)
           {
            //--- получаем очередной объект-форму,
            form=toolkit.GetControlPointForm(j);
            if(form==NULL)
               continue;
            //--- получаем состояние мышки относительно формы
            mouse_state=form.MouseFormState(id,lparam,dparam,sparam);
            //--- Если курсор в пределах формы,
            if(mouse_state>MOUSE_FORM_STATE_OUTSIDE_FORM_WHEEL)
              {
               //--- записываем идентификатор объекта и индекс формы
               //--- и возвращаем указатель на форму
               obj_ext_id=obj_ext.ObjectID();
               form_index=j;
               return form;
              }
           }
        }
     }
//--- Ничего не нашли - возвращаем NULL
   return NULL;
  }
//+------------------------------------------------------------------+

В комментариях к коду вся логика расписана достаточно подробно. Надеюсь, тут всё понятно. В любом случае, любой вопрос можно задать в обсуждении статьи.

В обработчике событий класса CGraphElementsCollection::OnChartEvent() в блоке обработчика события "Курсор в пределах активной области, прокручивается колёсико мышки" впишем вывод в журнал типа графического элемента, над которым находится курсор, и имени этого элемента. Таким образом можно будет контролировать правильность отработки взаимодействия мышки с тем объектом, над которым находится курсор — если навести курсор на элемент и прокручивать колёсико мышки, то в журнал будут выводиться данные об этом объекте:

            //--- Заготовка обработчика события Курсор в пределах активной области, прокручивается колёсико мышки
            if(mouse_state==MOUSE_FORM_STATE_INSIDE_ACTIVE_AREA_WHEEL)
              {
               Print(DFUN,"Mouse scroll: ",form.TypeElementDescription()," ",form.Name());
              }


Тестирование

Для теста возьмём советник из прошлой статьи и сохраним его в новой папке \MQL5\Experts\TestDoEasy\Part109\ под новым именем TstDE109.mq5.

Во входные параметры советника добавим новые параметры, позволяющие задать выравнивание для флажка выбора и текста, а также параметры для объекта-кнопки:

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


В обработчике OnInit() создадим все необходимые нам объекты на панели и в контейнере GroupBox:

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- Установка глобальных переменных советника
   ArrayResize(array_clr,2);        // Массив цветов градиентной заливки
   array_clr[0]=C'26,100,128';      // Исходный ≈Тёмно-лазурный цвет
   array_clr[1]=C'35,133,169';      // Осветлённый исходный цвет
//--- Создадим массив с текущим символом и установим его для использования в библиотеке
   string array[1]={Symbol()};
   engine.SetUsedSymbols(array);
   //--- Создадим объект-таймсерию для текущего символа и периода и выведем его описание в журнал
   engine.SeriesCreate(Symbol(),Period());
   engine.GetTimeSeriesCollection().PrintShort(false); // Краткие описания

//--- Создадим объект WinForms Panel
   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)
     {
      //--- Установим значение Padding равным 4
      pnl.SetPaddingAll(4);
      //--- Установим флаги перемещаемости, автоизменения размеров и режим автоизменения из входных параметров
      pnl.SetMovable(InpMovable);
      pnl.SetAutoSize(InpAutoSize,false);
      pnl.SetAutoSizeMode((ENUM_CANV_ELEMENT_AUTO_SIZE_MODE)InpAutoSizeMode,false);
      //--- В цикле создадим 2 привязанных объекта-панели
      CPanel *obj=NULL;
      for(int i=0;i<2;i++)
        {
         //--- создадим объект-панель с рассчитанными координатами, шириной 90 и высотой 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);
            //--- Рассчитаем ширину и высоту будущего объекта-текстовой метки
            int w=obj.Width()-obj.BorderSizeLeft()-obj.BorderSizeRight();
            int h=obj.Height()-obj.BorderSizeTop()-obj.BorderSizeBottom();
            //--- Создадим объект-текстовую метку
            obj.CreateNewElement(GRAPH_ELEMENT_TYPE_WF_LABEL,pnl.BorderSizeLeft(),pnl.BorderSizeTop(),w,h,clrNONE,255,false,false);
            //--- Получаем указатель на вновь созданный объект
            CLabel *lbl=obj.GetElement(0);
            if(lbl!=NULL)
              {
               //--- Если объект имеет чётный, или нулевой индекс в списке - зададим ему цвет текста по умолчанию
               if(i % 2==0)
                  lbl.SetForeColor(CLR_DEF_FORE_COLOR);
               //--- Если индекс объекта в списке нечётный - зададим объекту непрозрачность 127
               else
                  lbl.SetForeColorOpacity(127);
               //--- Укажем для шрифта тип толщины "Black" и
               //--- укажем выравнивание текста из настроек советника
               lbl.SetFontBoldType(FW_TYPE_BLACK);
               lbl.SetTextAlign(InpTextAlign);
               lbl.SetAutoSize((bool)InpTextAutoSize,false);
               //--- Для объекта с чётным, или нулевым индексом укажем для текста цену Bid, иначе - цену Ask символа 
               lbl.SetText(GetPrice(i % 2==0 ? SYMBOL_BID : SYMBOL_ASK));
               //--- Укажем толщину, тип и цвет рамки для текстовой метки и обновим модифицированный объект
               lbl.SetBorderSizeAll(1);
               lbl.SetBorderStyle((ENUM_FRAME_STYLE)InpFrameStyle);
               lbl.SetBorderColor(CLR_DEF_FRAME_COLOR);
               lbl.Update(true);
              }
           }
        }
      //--- Создадим объект WinForms GroupBox
      CGroupBox *gbox=NULL;
      //--- Координатой Y GroupBox будет являться отступ от прикреплённых панелей на 6 пикселей

      int w=pnl.GetUnderlay().Width();
      int y=obj.BottomEdgeRelative()+6;
      //--- Если прикреплённый объект GroupBox создан
      if(pnl.CreateNewElement(GRAPH_ELEMENT_TYPE_WF_GROUPBOX,0,y,210,110,C'0x91,0xAA,0xAE',0,true,false))
        {
         //--- получим указатель на объект GroupBox по его индексу в списке прикреплённых объектов
         gbox=pnl.GetElement(2);
         if(gbox!=NULL)
           {
            //--- установим тип рамки "вдавленная рамка", цвет рамки как цвет фона основной панели,
            //--- а цвет текста - затемнённый на 1 цвет фона последней прикреплённой панели
            gbox.SetBorderStyle(FRAME_STYLE_STAMP);
            gbox.SetBorderColor(pnl.BackgroundColor());
            gbox.SetForeColor(gbox.ChangeColorLightness(obj.BackgroundColor(),-1));
            //--- Создадим объект CheckBox
            gbox.CreateNewElement(GRAPH_ELEMENT_TYPE_WF_CHECKBOX,2,10,50,20,clrNONE,255,true,false);
            CCheckBox *cbox=gbox.GetElement(0);
            //--- Если CheckBox создан и указатель на него получен
            if(cbox!=NULL)
              {
               //--- Установим параметры CheckBox из входных параметров советника
               cbox.SetAutoSize((bool)InpCheckAutoSize,false);
               cbox.SetCheckAlign(InpCheckAlign);
               cbox.SetTextAlign(InpCheckTextAlign);
               //--- Зададим выводимый текст, стиль и цвет рамки и состояние флажка
               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);
              }
            //--- Создадим объект RadioButton
            gbox.CreateNewElement(GRAPH_ELEMENT_TYPE_WF_RADIOBUTTON,2,cbox.BottomEdgeRelative(),50,20,clrNONE,255,true,false);
            CRadioButton *rbtn=gbox.GetElement(1);
            //--- Если RadioButton создан и указатель на него получен
            if(rbtn!=NULL)
              {
               //--- Установим параметры RadioButton из входных параметров советника
               rbtn.SetAutoSize((bool)InpCheckAutoSize,false);
               rbtn.SetCheckAlign(InpCheckAlign);
               rbtn.SetTextAlign(InpCheckTextAlign);
               //--- Зададим выводимый текст, стиль и цвет рамки и состояние флажка
               rbtn.SetText("RadioButton");
               rbtn.SetBorderStyle((ENUM_FRAME_STYLE)InpCheckFrameStyle);
               rbtn.SetBorderColor(CLR_DEF_FRAME_COLOR);
               rbtn.SetChecked(true);
              }
            //--- Создадим объект Button
            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);
            //--- Если Button создан и указатель на него получен
            if(butt!=NULL)
              {
               //--- Установим параметры RadioButton из входных параметров советника
               butt.SetAutoSize((bool)InpButtonAutoSize,false);
               butt.SetAutoSizeMode((ENUM_CANV_ELEMENT_AUTO_SIZE_MODE)InpButtonAutoSizeMode,false);
               butt.SetTextAlign(InpButtonTextAlign);
               //--- Зададим выводимый текст, стиль и цвет рамки и состояние флажка
               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);
              }
           }
        }
      //--- Перерисуем все объекты в порядке их иерархии
      pnl.Redraw(true);
     }
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+

Логика блока кода для создания прикреплённых к GroupBox трёх элементов управления прокомментирована в коде, думаю, там всё понятно.
Размер кнопки (WinForm-объекта Button) нарочито сделан таким, чтобы кнопка по высоте была больше текста, а по ширине — меньше. Тогда при влюченном режиме автоизменения размера мы сможем увидеть как кнопка подстраивает свои размеры под текст.

Скомпилируем советник и запустим его на графике:


Видно, что кнопка верно подстраивает свои размеры под текст в зависимости от режима автоизменения размера. CheckBox и RadioButton имеют нормальные светлые поля флажка выбора, а сам флажок элемента CheckBox обрёл толщину.

При наведении мышки на некоторые элементы управления я прокручивал колёсико мышки. В журнал при этом выводились записи:

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

Эти записи говорят о правильности выбора активного объекта для взаимодействия с мышкой.

Что дальше

В следующей статье продолжим разработку WinForms-объектов и функционала уже созданных.

Ниже прикреплены все файлы текущей версии библиотеки, файлы тестового советника и индикатора контроля событий графиков для MQL5. Их можно скачать и протестировать всё самостоятельно. При возникновении вопросов, замечаний и пожеланий вы можете озвучить их в комментариях к статье.

К содержанию

*Статьи этой серии:

DoEasy. Элементы управления (Часть 1): Первые шаги
DoEasy. Элементы управления (Часть 2): Продолжаем работу над классом CPanel
DoEasy. Элементы управления (Часть 3): Создание привязанных элементов управления
DoEasy. Элементы управления (Часть 4): Элемент управления "Панель", параметры Padding и Dock
DoEasy. Элементы управления (Часть 5): Базовый WinForms-объект, элемент управления "Панель", параметр AutoSize
DoEasy. Элементы управления (Часть 6): Элемент управления "Панель", автоизменение размеров контейнера под внутреннее содержимое
DoEasy. Элементы управления (Часть 7): Элемент управления "Текстовая метка"
DoEasy. Элементы управления (Часть 8): Базовые WinForms-объекты по категориям, элементы управления "GroupBox" и "CheckBox



Прикрепленные файлы |
MQL5.zip (4459.23 KB)
Нейросети — это просто (Часть 18): Ассоциативные правила Нейросети — это просто (Часть 18): Ассоциативные правила
В продолжение данной серии статей предлагаю познакомиться ещё с одним типом задач из методов обучения без учителя — поиск ассоциативных правил. Данный тип задач впервые был применен в ритейле для анализа корзин покупателей. О возможностях использования подобных алгоритмов в рамках трейдинга мы и поговорим в этой статье.
Биржевая сеточная торговля лимитными ордерами на полном автомате на Московской бирже MOEX Биржевая сеточная торговля лимитными ордерами на полном автомате на Московской бирже MOEX
Разработка торгового советника на языке торговых стратегий MQL5 для MetaTrader 5 Московской биржи MOEX. Советник будет торговать по сеточной стратегии на терминале MetaTrader 5 на рынках Московской биржи MOEX, которая также включает в себя закрытие позиции по стоп-лоссу или тейк-профиту, удаление отложенных ордеров при наступлении определенных рыночных условий.
Разработка торгового советника с нуля (Часть 13): Время и торговля (II) Разработка торгового советника с нуля (Часть 13): Время и торговля (II)
Сегодня мы построим вторую часть системы Times & Trade для анализа рынка. В предыдущей статье "Times & Trade (I)" мы рассмотрели альтернативную систему для организации графика, чтобы у нас был индикатор, позволяющий как можно быстрее интерпретировать сделки, совершенные на рынке.
Разработка торговой системы на основе индикатора CCI Разработка торговой системы на основе индикатора CCI
В очередной статье из серии, посвященной разработке торговых систем, я представлю индекс товарного канала (CCI), объясню его особенности и поделюсь тем, как создать торговую систему на основе этого индикатора.