English 中文 Español Deutsch 日本語 Português
preview
DoEasy. Элементы управления (Часть 10): WinForms-объекты — оживляем интерфейс

DoEasy. Элементы управления (Часть 10): WinForms-объекты — оживляем интерфейс

MetaTrader 5Примеры | 8 июля 2022, 14:38
746 0
Artyom Trishkin
Artyom Trishkin

Содержание


Концепция

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

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

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

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

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


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

В файле \MQL5\Include\DoEasy\Defines.mqh впишем новые макроподстановки для цветов по умолчанию различных состояний WinForms-объектов:

//--- Параметры канваса
#define PAUSE_FOR_CANV_UPDATE          (16)                       // Частота обновления канваса
#define CLR_CANV_NULL                  (0x00FFFFFF)               // Ноль для канваса с альфа-каналом
#define CLR_DEF_FORE_COLOR             (C'0x2D,0x43,0x48')        // Цвет по умолчанию для текстов объектов на канвасе
#define CLR_DEF_FORE_COLOR_MOUSE_DOWN  (C'0x0E,0x11,0x98')        // Цвет по умолчанию для текстов объектов на канвасе при нажатии мышки на элемент управления
#define CLR_DEF_FORE_COLOR_MOUSE_OVER  (C'0x14,0x67,0xF1')        // Цвет по умолчанию для текстов объектов на канвасе при наведении мышки на элемент управления
#define CLR_DEF_FORE_COLOR_OPACITY     (255)                      // Непрозрачность цвета по умолчанию для текстов объектов на канвасе
#define CLR_DEF_BORDER_COLOR           (C'0x2D,0x43,0x48')        // Цвет по умолчанию для рамок объектов на канвасе
#define CLR_DEF_BORDER_MOUSE_DOWN      (C'0x61,0x88,0xC9')        // Цвет по умолчанию для рамок объектов на канвасе при нажатии мышки на элемент управления
#define CLR_DEF_BORDER_MOUSE_OVER      (C'0x93,0xAD,0xC8')        // Цвет по умолчанию для рамок объектов на канвасе при наведении мышки на элемент управления
#define CLR_DEF_BORDER_COLOR_OPACITY   (255)                      // Непрозрачность цвета по умолчанию для рамок объектов на канвасе
#define CLR_DEF_BORDER_COLOR_DARKNESS  (-2.0)                     // Затемнённость цвета по умолчанию для рамок объектов на канвасе (при использовании цвета фона)
#define CLR_DEF_FRAME_GBOX_COLOR       (C'0xDC,0xDC,0xDC')        // Цвет по умолчанию для рамок объектов типа GroupBox на канвасе
#define CLR_DEF_OPACITY                (200)                      // Непрозрачность цвета по умолчанию для объектов на канвасе
#define CLR_DEF_SHADOW_COLOR           (C'0x6B,0x6B,0x6B')        // Цвет по умолчанию для теней объектов на канвасе
#define CLR_DEF_SHADOW_OPACITY         (127)                      // Непрозрачность цвета по умолчанию для теней объектов на канвасе
#define DEF_SHADOW_BLUR                (4)                        // Размытие по умолчанию для теней объектов на канвасе

#define CLR_DEF_CHECK_BACK_COLOR       (C'0xFF,0xFF,0xFF')        // Цвет фона флажка проверки элемента управления
#define CLR_DEF_CHECK_BACK_OPACITY     (255)                      // Непрозрачность цвета фона флажка проверки элемента управления
#define CLR_DEF_CHECK_BACK_MOUSE_DOWN  (C'0xC0,0xDC,0xF3')        // Цвет фона флажка проверки элемента управления при нажатии мышки на элемент управления
#define CLR_DEF_CHECK_BACK_MOUSE_OVER  (C'0xD8,0xE6,0xF2')        // Цвет фона флажка проверки элемента управления при наведении мышки на элемент управления
#define CLR_DEF_CHECK_BORDER_COLOR     (C'0x2D,0x43,0x48')        // Цвет рамки флажка проверки элемента управления
#define CLR_DEF_CHECK_BORDER_OPACITY   (255)                      // Непрозрачность цвета рамки флажка проверки элемента управления
#define CLR_DEF_CHECK_BORDER_MOUSE_DOWN (C'0x00,0x54,0x99')       // Цвет рамки флажка проверки элемента управления при нажатии мышки на элемент управления
#define CLR_DEF_CHECK_BORDER_MOUSE_OVER (C'0x00,0x78,0xD7')       // Цвет рамки флажка проверки элемента управления при наведении мышки на элемент управления
#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'0x00,0x54,0x99')        // Цвет флажка проверки элемента управления при нажатии мышки на элемент управления
#define CLR_DEF_CHECK_FLAG_MOUSE_OVER  (C'0x00,0x78,0xD7')        // Цвет флажка проверки элемента управления при наведении мышки на элемент управления

#define CLR_DEF_CONTROL_STD_BACK_COLOR (C'0xF0,0xF0,0xF0')        // Цвет фона стандартных элементов управления
#define CLR_DEF_CONTROL_STD_MOUSE_DOWN (C'0xC0,0xDC,0xF3')        // Цвет фона стандартных элементов управления при нажатии мышки на элемент управления
#define CLR_DEF_CONTROL_STD_MOUSE_OVER (C'0xD8,0xE6,0xF2')        // Цвет фона стандартных элементов управления при наведении мышки на элемент управления
#define CLR_DEF_CONTROL_STD_OPACITY    (255)                      // Непрозрачность цвета фона стандартных элементов управления

#define CLR_DEF_CONTROL_STD_BACK_COLOR_ON (C'0xC9,0xDE,0xD0')     // Цвет фона стандартных элементов управления в состоянии "Включено"
#define CLR_DEF_CONTROL_STD_BACK_DOWN_ON (C'0xA6,0xC8,0xB0')      // Цвет фона стандартных элементов управления при нажатии мышки на элемент управления в состоянии "Включено"
#define CLR_DEF_CONTROL_STD_BACK_OVER_ON (C'0xB8,0xD3,0xC0')      // Цвет фона стандартных элементов управления при наведении мышки на элемент управления в состоянии "Включено"

#define DEF_FONT                       ("Calibri")                // Шрифт по умолчанию
#define DEF_FONT_SIZE                  (8)                        // Размер шрифта по умолчанию
#define DEF_CHECK_SIZE                 (12)                       // Размер флажка проверки по умолчанию
#define OUTER_AREA_SIZE                (16)                       // Размер одной стороны внешней области вокруг рабочего пространства формы
#define DEF_FRAME_WIDTH_SIZE           (3)                        // Ширина рамки формы/панели/окна по умолчанию
//--- Параметры графических объектов

ForeColor для флажка проверки всё-таки не нужен. Поэтому заменим его на BorderColor — цвет рамки флажка проверки. Вот он и будет изменяться вместе с цветом фона при взаимодействии с мышкой. А в качестве ForeColor у нас тут используется цвет флажка.

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

//+------------------------------------------------------------------+
//| Список возможных состояний мышки относительно формы              |
//+------------------------------------------------------------------+
enum ENUM_MOUSE_FORM_STATE
  {
   MOUSE_FORM_STATE_NONE = 0,                         // Неопределённое состояние
//--- За пределами формы
   MOUSE_FORM_STATE_OUTSIDE_FORM_NOT_PRESSED,         // Курсор за пределами формы, кнопки мышки не нажаты
   MOUSE_FORM_STATE_OUTSIDE_FORM_PRESSED,             // Курсор за пределами формы, нажата кнопка мышки (любая)
   MOUSE_FORM_STATE_OUTSIDE_FORM_WHEEL,               // Курсор за пределами формы, прокручивается колёсико мышки
//--- В пределах формы
   MOUSE_FORM_STATE_INSIDE_FORM_NOT_PRESSED,          // Курсор в пределах формы, кнопки мышки не нажаты
   MOUSE_FORM_STATE_INSIDE_FORM_PRESSED,              // Курсор в пределах формы, нажата кнопка мышки (любая)
   MOUSE_FORM_STATE_INSIDE_FORM_WHEEL,                // Курсор в пределах формы, прокручивается колёсико мышки
//--- В пределах области заголовка окна
   MOUSE_FORM_STATE_INSIDE_ACTIVE_AREA_NOT_PRESSED,   // Курсор в пределах активной области, кнопки мышки не нажаты
   MOUSE_FORM_STATE_INSIDE_ACTIVE_AREA_PRESSED,       // Курсор в пределах активной области, нажата кнопка мышки (любая)
   MOUSE_FORM_STATE_INSIDE_ACTIVE_AREA_WHEEL,         // Курсор в пределах активной области, прокручивается колёсико мышки
   MOUSE_FORM_STATE_INSIDE_ACTIVE_AREA_RELEASED,      // Курсор в пределах активной области, отжата кнопка мышки (левая)
//--- В пределах области прокрутки окна
   MOUSE_FORM_STATE_INSIDE_SCROLL_AREA_NOT_PRESSED,   // Курсор в пределах области прокрутки окна, кнопки мышки не нажаты
   MOUSE_FORM_STATE_INSIDE_SCROLL_AREA_PRESSED,       // Курсор в пределах области прокрутки окна, нажата кнопка мышки (любая)
   MOUSE_FORM_STATE_INSIDE_SCROLL_AREA_WHEEL,         // Курсор в пределах области прокрутки окна, прокручивается колёсико мышки
  };
//+------------------------------------------------------------------+

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

Для реакции на взаимодействие с мышью нам потребуется знать какое именно событие мышки произошло. Впоследствии на основе обработки этого события мы его будем посылать в программу — чтобы пользователь библиотеки мог обработать это событие в своей программе.

Создадим новое перечисление со списком событий мышки:

//+------------------------------------------------------------------+
//| Список возможных событий мышки                                   |
//+------------------------------------------------------------------+
enum ENUM_MOUSE_EVENT
  {
   MOUSE_EVENT_NO_EVENT = CHART_OBJ_EVENTS_NEXT_CODE, // Нет события
//---
   MOUSE_EVENT_OUTSIDE_FORM_NOT_PRESSED,              // Курсор за пределами формы, кнопки мышки не нажаты
   MOUSE_EVENT_OUTSIDE_FORM_PRESSED,                  // Курсор за пределами формы, нажата кнопка мышки (любая)
   MOUSE_EVENT_OUTSIDE_FORM_WHEEL,                    // Курсор за пределами формы, прокручивается колёсико мышки
//--- В пределах формы
   MOUSE_EVENT_INSIDE_FORM_NOT_PRESSED,               // Курсор в пределах формы, кнопки мышки не нажаты
   MOUSE_EVENT_INSIDE_FORM_PRESSED,                   // Курсор в пределах формы, нажата кнопка мышки (любая)
   MOUSE_EVENT_INSIDE_FORM_WHEEL,                     // Курсор в пределах формы, прокручивается колёсико мышки
//--- В пределах области заголовка окна
   MOUSE_EVENT_INSIDE_ACTIVE_AREA_NOT_PRESSED,        // Курсор в пределах активной области, кнопки мышки не нажаты
   MOUSE_EVENT_INSIDE_ACTIVE_AREA_PRESSED,            // Курсор в пределах активной области, нажата кнопка мышки (любая)
   MOUSE_EVENT_INSIDE_ACTIVE_AREA_WHEEL,              // Курсор в пределах активной области, прокручивается колёсико мышки
   MOUSE_EVENT_INSIDE_ACTIVE_AREA_RELEASED,           // Курсор в пределах активной области, отжата кнопка мышки (левая)
//--- В пределах области прокрутки окна
   MOUSE_EVENT_INSIDE_SCROLL_AREA_NOT_PRESSED,        // Курсор в пределах области прокрутки окна, кнопки мышки не нажаты
   MOUSE_EVENT_INSIDE_SCROLL_AREA_PRESSED,            // Курсор в пределах области прокрутки окна, нажата кнопка мышки (любая)
   MOUSE_EVENT_INSIDE_SCROLL_AREA_WHEEL,              // Курсор в пределах области прокрутки окна, прокручивается колёсико мышки
  };
#define ENUM_MOUSE_EVENT_NEXT_CODE  (MOUSE_EVENT_INSIDE_SCROLL_AREA_WHEEL+1)  // Код следующего события после последнего кода события чарта
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| Данные для работы с графическими элементами                      |
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| Список возможных событий графических объектов                    |
//+------------------------------------------------------------------+
enum ENUM_GRAPH_OBJ_EVENT
  {
   GRAPH_OBJ_EVENT_NO_EVENT = ENUM_MOUSE_EVENT_NEXT_CODE,// Нет события
   GRAPH_OBJ_EVENT_CREATE,                            // Событие "Создание нового графического объекта"
   GRAPH_OBJ_EVENT_CHANGE,                            // Событие "Изменение свойств графического объекта"
   GRAPH_OBJ_EVENT_RENAME,                            // Событие "Переименование графического объекта"
   GRAPH_OBJ_EVENT_DELETE,                            // Событие "Удаление графического объекта"
   GRAPH_OBJ_EVENT_DEL_CHART,                         // Событие "Удаление графического объекта вместе с окном графика"
  };
#define GRAPH_OBJ_EVENTS_NEXT_CODE  (GRAPH_OBJ_EVENT_DEL_CHART+1) // Код следующего события после последнего кода события графических объектов
//+------------------------------------------------------------------+

Так как мы вписали новое перечисление событий, "втиснув" его между списком событий чарта и списком событий графических объектов, то начальное значение первого события графических объектов теперь будет рассчитано как последнее событие мышки+1.

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

//+------------------------------------------------------------------+
//| Целочисленные свойства графического элемента на канвасе          |
//+------------------------------------------------------------------+
enum ENUM_CANV_ELEMENT_PROP_INTEGER
  {
   CANV_ELEMENT_PROP_ID = 0,                          // Идентификатор элемента
   CANV_ELEMENT_PROP_TYPE,                            // Тип графического элемента
   //---...
   //---...
   CANV_ELEMENT_PROP_FORE_COLOR,                      // Цвет текста по умолчанию для всех объектов элемента управления
   CANV_ELEMENT_PROP_FORE_COLOR_OPACITY,              // Непрозрачность цвета текста по умолчанию для всех объектов элемента управления
   CANV_ELEMENT_PROP_FORE_COLOR_MOUSE_DOWN,           // Цвет текста элемента управления по умолчанию при нажатии мышки на элемент управления
   CANV_ELEMENT_PROP_FORE_COLOR_MOUSE_OVER,           // Цвет текста элемента управления по умолчанию при наведении мышки на элемент управления
   CANV_ELEMENT_PROP_FORE_COLOR_TOGGLE,               // Цвет текста элемента управления в состоянии "включено"
   CANV_ELEMENT_PROP_FORE_COLOR_TOGGLE_MOUSE_DOWN,    // Цвет текста элемента управления по умолчанию в состоянии "включено" при нажатии мышки на элемент управления
   CANV_ELEMENT_PROP_FORE_COLOR_TOGGLE_MOUSE_OVER,    // Цвет текста элемента управления по умолчанию в состоянии "включено" при наведении мышки на элемент управления
   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_BACKGROUND_COLOR_TOGGLE,           // Цвет фона элемента управления в состоянии "включено"
   CANV_ELEMENT_PROP_BACKGROUND_COLOR_TOGGLE_MOUSE_DOWN,// Цвет фона элемента управления в состоянии "включено" при нажатии мышки на элемент управления
   CANV_ELEMENT_PROP_BACKGROUND_COLOR_TOGGLE_MOUSE_OVER,// Цвет фона элемента управления в состоянии "включено" при наведении мышки на элемент управления
   CANV_ELEMENT_PROP_BOLD_TYPE,                       // Тип толщины шрифта
   CANV_ELEMENT_PROP_BORDER_STYLE,                    // Стиль рамки элемента управления
   //---...
   //---...
   CANV_ELEMENT_PROP_CHECK_STATE,                     // Состояние элемента управления, имеющего флажок проверки
   CANV_ELEMENT_PROP_AUTOCHECK,                       // Автоматическое изменение состояния флажка при его выборе
   CANV_ELEMENT_PROP_BUTTON_TOGGLE,                   // Флаг "Переключатель" элемента управления, имеющего кнопку
   CANV_ELEMENT_PROP_BUTTON_STATE,                    // Состояние элемента управления "Переключатель", имеющего кнопку
   CANV_ELEMENT_PROP_CHECK_BACKGROUND_COLOR,          // Цвет фона флажка проверки элемента управления
   CANV_ELEMENT_PROP_CHECK_BACKGROUND_COLOR_OPACITY,  // Непрозрачность цвета фона флажка проверки элемента управления
   //---...
   //---...
   CANV_ELEMENT_PROP_CHECK_FLAG_COLOR_MOUSE_DOWN,     // Цвет флажка проверки элемента управления при нажатии мышки на элемент управления
   CANV_ELEMENT_PROP_CHECK_FLAG_COLOR_MOUSE_OVER,     // Цвет флажка проверки элемента управления при наведении мышки на элемент управления
   
  };
#define CANV_ELEMENT_PROP_INTEGER_TOTAL (81)          // Общее количество целочисленных свойств
#define CANV_ELEMENT_PROP_INTEGER_SKIP  (0)           // Количество неиспользуемых в сортировке целочисленных свойств
//+------------------------------------------------------------------+

и увеличим общее количество свойств с 71 до 81.

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

//+------------------------------------------------------------------+
//| Возможные критерии сортировки графических элементов на канвасе   |
//+------------------------------------------------------------------+
#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_FORE_COLOR_MOUSE_DOWN,        // Сортировать по цвету текста элемента по умолчанию при нажатии мышки на элемент управления
   SORT_BY_CANV_ELEMENT_FORE_COLOR_MOUSE_OVER,        // Сортировать по цвету текста элемента по умолчанию при наведении мышки на элемент управления
   SORT_BY_CANV_ELEMENT_FORE_COLOR_TOGGLE,            // Сортировать по цвету текста элемента управления в состоянии "включено"
   SORT_BY_CANV_ELEMENT_FORE_COLOR_TOGGLE_MOUSE_DOWN, // Сортировать по цвету текста элемента управления по умолчанию в состоянии "включено" при нажатии мышки на элемент управления
   SORT_BY_CANV_ELEMENT_FORE_COLOR_TOGGLE_MOUSE_OVER, // Сортировать по цвету текста элемента управления по умолчанию в состоянии "включено" при наведении мышки на элемент управления
   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_BACKGROUND_COLOR_TOGGLE,           // Сортировать по цвету фона элемента управления в состоянии "включено"
   SORT_BY_CANV_ELEMENT_BACKGROUND_COLOR_TOGGLE_MOUSE_DOWN,// Сортировать по цвету фона элемента управления в состоянии "включено" при нажатии мышки на элемент управления
   SORT_BY_CANV_ELEMENT_BACKGROUND_COLOR_TOGGLE_MOUSE_OVER,// Сортировать по цвету фона элемента управления в состоянии "включено" при наведении мышки на элемент управления
   SORT_BY_CANV_ELEMENT_BOLD_TYPE,                    // Сортировать по типу толщины шрифта
   SORT_BY_CANV_ELEMENT_BORDER_STYLE,                 // Сортировать по стилю рамки элемента управления
   //---...
   //---...
   SORT_BY_CANV_ELEMENT_CHECK_STATE,                  // Сортировать по состоянию элемента управления, имеющего флажок проверки
   SORT_BY_CANV_ELEMENT_AUTOCHECK,                    // Сортировать по автоматическому изменению состояния флажка при его выборе
   SORT_BY_CANV_ELEMENT_BUTTON_TOGGLE,                // Сортировать по флагу "Переключатель" элемента управления, имеющего кнопку
   SORT_BY_CANV_ELEMENT_BUTTON_STATE,                 // Сортировать по состоянию элемента управления "Переключатель", имеющего кнопку  
   SORT_BY_CANV_ELEMENT_CHECK_BACKGROUND_COLOR,       // Сортировать по цвету фона флажка проверки элемента управления
   SORT_BY_CANV_ELEMENT_CHECK_BACKGROUND_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_CANV_ELEMENT_PROP_FORE_COLOR,                  // Цвет текста по умолчанию для всех объектов элемента управления
   MSG_CANV_ELEMENT_PROP_FORE_COLOR_OPACITY,          // Непрозрачность цвета текста по умолчанию для всех объектов элемента управления
   MSG_CANV_ELEMENT_PROP_FORE_COLOR_MOUSE_DOWN,       // Цвет текста элемента по умолчанию при нажатии мышки на элемент управления
   MSG_CANV_ELEMENT_PROP_FORE_COLOR_MOUSE_OVER,       // Цвет текста элемента по умолчанию при наведении мышки на элемент управления
   MSG_CANV_ELEMENT_PROP_FORE_COLOR_TOGGLE,           // Цвет текста элемента управления в состоянии "включено"
   MSG_CANV_ELEMENT_PROP_FORE_COLOR_TOGGLE_MOUSE_DOWN,// Цвет текста элемента управления по умолчанию в состоянии "включено" при нажатии мышки на элемент управления
   MSG_CANV_ELEMENT_PROP_FORE_COLOR_TOGGLE_MOUSE_OVER,// Цвет текста элемента управления по умолчанию в состоянии "включено" при наведении мышки на элемент управления
   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_BACKGROUND_COLOR_TOGGLE,           // Цвет фона элемента управления в состоянии "включено"
   MSG_CANV_ELEMENT_PROP_BACKGROUND_COLOR_TOGGLE_MOUSE_DOWN,// Цвет фона элемента управления в состоянии "включено" при нажатии мышки на элемент управления
   MSG_CANV_ELEMENT_PROP_BACKGROUND_COLOR_TOGGLE_MOUSE_OVER,// Цвет фона элемента управления в состоянии "включено" при наведении мышки на элемент управления
   MSG_CANV_ELEMENT_PROP_BOLD_TYPE,                   // Тип толщины шрифта
   MSG_CANV_ELEMENT_PROP_BORDER_STYLE,                // Стиль рамки элемента управления

...

   MSG_CANV_ELEMENT_PROP_CHECK_STATE,                 // Состояние элемента управления, имеющего флажок проверки
   MSG_CANV_ELEMENT_PROP_AUTOCHECK,                   // Автоматическое изменение состояния флажка при его выборе
   MSG_CANV_ELEMENT_PROP_BUTTON_TOGGLE,               // Флаг "Переключатель" элемента управления, имеющего кнопку
   MSG_CANV_ELEMENT_PROP_BUTTON_STATE,                // Состояние элемента управления "Переключатель", имеющего кнопку
   MSG_CANV_ELEMENT_PROP_CHECK_BACKGROUND_COLOR,      // Цвет фона флажка проверки элемента управления
   MSG_CANV_ELEMENT_PROP_CHECK_BACKGROUND_COLOR_OPACITY,// Непрозрачность цвета фона флажка проверки элемента управления
   MSG_CANV_ELEMENT_PROP_CHECK_BACKGROUND_COLOR_MOUSE_DOWN,// Цвет фона флажка проверки элемента управления при нажатии мышки на элемент управления
   MSG_CANV_ELEMENT_PROP_CHECK_BACKGROUND_COLOR_MOUSE_OVER,// Цвет фона флажка проверки элемента управления при наведении мышки на элемент управления
   MSG_CANV_ELEMENT_PROP_CHECK_FORE_COLOR,            // Цвет рамки флажка проверки элемента управления
   MSG_CANV_ELEMENT_PROP_CHECK_FORE_COLOR_OPACITY,    // Непрозрачность цвета рамки флажка проверки элемента управления
   MSG_CANV_ELEMENT_PROP_CHECK_FORE_COLOR_MOUSE_DOWN, // Цвет рамки флажка проверки элемента управления при нажатии мышки на элемент управления
   MSG_CANV_ELEMENT_PROP_CHECK_FORE_COLOR_MOUSE_OVER, // Цвет рамки флажка проверки элемента управления при наведении мышки на элемент управления
   MSG_CANV_ELEMENT_PROP_CHECK_FLAG_COLOR,            // Цвет флажка проверки элемента управления
   MSG_CANV_ELEMENT_PROP_CHECK_FLAG_COLOR_OPACITY,    // Непрозрачность цвета флажка проверки элемента управления
   MSG_CANV_ELEMENT_PROP_CHECK_FLAG_COLOR_MOUSE_DOWN, // Цвет флажка проверки элемента управления при нажатии мышки на элемент управления
   MSG_CANV_ELEMENT_PROP_CHECK_FLAG_COLOR_MOUSE_OVER, // Цвет флажка проверки элемента управления при наведении мышки на элемент управления
   
//--- Вещественные свойства графических элементов

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


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

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

...

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

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


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

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

В файле \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
   color             m_array_colors_bg_init[];                 // Массив цветов фона элемента (первоначальный цвет)

//--- Создаёт (1) структуру объекта, (2) объект из структуры
   virtual bool      ObjectToStruct(void);
   virtual void      StructToObject(void);
//--- Копирует массив цветов в указанный массив цветов фона
   void              CopyArraysColors(color &array_dst[],const color &array_src[],const string source);
   
private:


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

private:
   int               m_shift_coord_x;                          // Смещение координаты X относительно базового объекта
   int               m_shift_coord_y;                          // Смещение координаты Y относительно базового объекта
   struct SData
     {
      //--- Целочисленные свойства объекта
      int            id;                                       // Идентификатор элемента
      int            type;                                     // Тип графического элемента
      //---...
      //---...
      color          check_flag_color_mouse_down;              // Цвет флажка проверки элемента управления при нажатии мышки на элемент управления
      color          check_flag_color_mouse_over;              // Цвет флажка проверки элемента управления при наведении мышки на элемент управления
      color          fore_color_mouse_down;                    // Цвет текста элемента управления по умолчанию при нажатии мышки на элемент управления
      color          fore_color_mouse_over;                    // Цвет текста элемента управления по умолчанию при наведении мышки на элемент управления
      color          fore_color_toggle;                        // Цвет текста элемента управления в состоянии "включено"
      color          fore_color_toggle_mouse_down;             // Цвет текста элемента управления по умолчанию в состоянии "включено" при нажатии мышки на элемент управления
      color          fore_color_toggle_mouse_over;             // Цвет текста элемента управления по умолчанию в состоянии "включено" при наведении мышки на элемент управления
      color          background_color_toggle;                  // Цвет фона элемента управления в состоянии "включено"
      color          background_color_toggle_mouse_down;       // Цвет фона элемента управления в состоянии "включено" при нажатии мышки на элемент управления
      color          background_color_toggle_mouse_over;       // Цвет фона элемента управления в состоянии "включено" при наведении мышки на элемент управления
      bool           button_toggle;                            // Флаг "Переключатель" элемента управления, имеющего кнопку
      bool           button_state;                             // Состояние элемента управления "Переключатель", имеющего кнопку
      //--- Вещественные свойства объекта

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

Это необходимо для правильного сохранения в будущем объекта на носитель и чтения с него.


Напишем ещё один метод сохранения массива градиентных цветов для запоминания первоначального цвета фона:

//--- Сохраняет цвета в массив цветов фона
   void              SaveColorsBG(color &colors[])                         { this.CopyArraysColors(this.m_array_colors_bg,colors,DFUN);      }
   void              SaveColorsBGMouseDown(color &colors[])                { this.CopyArraysColors(this.m_array_colors_bg_dwn,colors,DFUN);  }
   void              SaveColorsBGMouseOver(color &colors[])                { this.CopyArraysColors(this.m_array_colors_bg_ovr,colors,DFUN);  }
   void              SaveColorsBGInit(color &colors[])                     { this.CopyArraysColors(this.m_array_colors_bg_init,colors,DFUN); }
   
public:

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

//--- Устанавливает основной цвет фона
   void              SetBackgroundColor(const color colour,const bool set_init_color)
                       {
                        this.SetProperty(CANV_ELEMENT_PROP_BACKGROUND_COLOR,colour);
                        color arr[1];
                        arr[0]=colour;
                        this.SaveColorsBG(arr);
                        if(set_init_color)
                           this.SetBackgroundColorInit(this.BackgroundColor());
                       }
   void              SetBackgroundColors(color &colors[],const bool set_init_colors)
                       {
                        this.SaveColorsBG(colors);
                        this.SetProperty(CANV_ELEMENT_PROP_BACKGROUND_COLOR,this.m_array_colors_bg[0]);
                        if(set_init_colors)
                           this.SetBackgroundColorsInit(colors);
                       }


Напишем идентичные вышерассмотренным методы для установки первоначального цвета фона:

   void              SetBackgroundColorsMouseOver(color &colors[])
                       {
                        this.SaveColorsBGMouseOver(colors);
                        this.SetProperty(CANV_ELEMENT_PROP_BACKGROUND_COLOR_MOUSE_OVER,this.m_array_colors_bg_ovr[0]);
                       }
//--- Устанавливает изначальный основной цвет фона
   void              SetBackgroundColorInit(const color colour)
                       {
                        color arr[1];
                        arr[0]=colour;
                        this.SaveColorsBGInit(arr);
                       }
   void              SetBackgroundColorsInit(color &colors[])
                       {
                        this.SaveColorsBGInit(colors);
                       }
                       
//--- Устанавливает флаг (1) перемещаемости, (2) активности объекта, (3) флаг взаимодействия,

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

Напишем методы, возвращающие изначальный цвет фона:

//--- Возвращает цвет фона при наведении мышки на элемент управления
   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]);
                       }
//--- Возвращает изначальный цвет основного фона
   color             BackgroundColorInit(void)           const { return (color)this.m_array_colors_bg_init[0];    }
   color             BackgroundColorInit(const uint index)const
                       {
                        uint total=this.m_array_colors_bg_init.Size();
                        if(total==0)
                           return this.BackgroundColor();
                        return(index>total-1 ? this.m_array_colors_bg_init[total-1] : this.m_array_colors_bg_init[index]);
                       }
   
//--- Возвращает (1) непрозрачность, координату (2) правого, (3) нижнего края элемента

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


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

//+------------------------------------------------------------------+
//| Параметрический конструктор                                      |
//+------------------------------------------------------------------+
CGCnvElement::CGCnvElement(const ENUM_GRAPH_ELEMENT_TYPE element_type,
                           const int      element_id,
                           const int      element_num,
                           const long     chart_id,
                           const int      wnd_num,
                           const string   name,
                           const int      x,
                           const int      y,
                           const int      w,
                           const int      h,
                           const color    colour,
                           const uchar    opacity,
                           const bool     movable=true,
                           const bool     activity=true,
                           const bool     redraw=false) : m_shadow(false)
  {
   this.m_type=OBJECT_DE_TYPE_GELEMENT; 
   this.m_element_main=NULL;
   this.m_element_base=NULL;
   this.m_chart_color_bg=(color)::ChartGetInteger((chart_id==NULL ? ::ChartID() : chart_id),CHART_COLOR_BACKGROUND);
   this.m_name=(::StringFind(name,this.m_name_prefix)<0 ? this.m_name_prefix : "")+name;
   this.m_chart_id=(chart_id==NULL || chart_id==0 ? ::ChartID() : chart_id);
   this.m_subwindow=wnd_num;
   this.m_type_element=element_type;
   this.SetFont(DEF_FONT,DEF_FONT_SIZE);
   this.m_text_anchor=0;
   this.m_text_x=0;
   this.m_text_y=0;
   this.SetBackgroundColor(colour,true);
   this.SetOpacity(opacity);
   this.m_shift_coord_x=0;
   this.m_shift_coord_y=0;
   if(::ArrayResize(this.m_array_colors_bg,1)==1)
      this.m_array_colors_bg[0]=this.BackgroundColor();
   if(::ArrayResize(this.m_array_colors_bg_dwn,1)==1)
      this.m_array_colors_bg_dwn[0]=this.BackgroundColor();
   if(::ArrayResize(this.m_array_colors_bg_ovr,1)==1)
      this.m_array_colors_bg_ovr[0]=this.BackgroundColor();
   if(this.Create(chart_id,wnd_num,this.m_name,x,y,w,h,redraw))
     {
      this.SetProperty(CANV_ELEMENT_PROP_NAME_RES,this.m_canvas.ResourceName()); // Имя графического ресурса
      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_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_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_BORDER_COLOR);                // Цвет рамки флажка проверки элемента управления
      this.SetProperty(CANV_ELEMENT_PROP_CHECK_FORE_COLOR_OPACITY,CLR_DEF_CHECK_BORDER_OPACITY);      // Непрозрачность цвета рамки флажка проверки элемента управления
      this.SetProperty(CANV_ELEMENT_PROP_CHECK_FORE_COLOR_MOUSE_DOWN,CLR_DEF_CHECK_BORDER_MOUSE_DOWN);// Цвет рамки флажка проверки элемента управления при нажатии мышки на элемент управления
      this.SetProperty(CANV_ELEMENT_PROP_CHECK_FORE_COLOR_MOUSE_OVER,CLR_DEF_CHECK_BORDER_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);  // Цвет флажка проверки элемента управления при наведении мышки на элемент управления
      this.SetProperty(CANV_ELEMENT_PROP_FORE_COLOR,CLR_DEF_FORE_COLOR);                              // Цвет текста по умолчанию для всех объектов элемента управления
      this.SetProperty(CANV_ELEMENT_PROP_FORE_COLOR_OPACITY,CLR_DEF_FORE_COLOR_OPACITY);              // Непрозрачность цвета текста по умолчанию для всех объектов элемента управления
      this.SetProperty(CANV_ELEMENT_PROP_FORE_COLOR_MOUSE_DOWN,CLR_DEF_FORE_COLOR_MOUSE_DOWN);        // Цвет текста элемента управления по умолчанию при нажатии мышки на элемент управления
      this.SetProperty(CANV_ELEMENT_PROP_FORE_COLOR_MOUSE_OVER,CLR_DEF_FORE_COLOR_MOUSE_OVER);        // Цвет текста элемента управления по умолчанию при наведении мышки на элемент управления
      this.SetProperty(CANV_ELEMENT_PROP_FORE_COLOR_TOGGLE,CLR_DEF_FORE_COLOR);                       // Цвет текста элемента управления в состоянии "включено"
      this.SetProperty(CANV_ELEMENT_PROP_FORE_COLOR_TOGGLE_MOUSE_DOWN,CLR_DEF_FORE_COLOR_MOUSE_DOWN); // Цвет текста элемента управления по умолчанию в состоянии "включено" при нажатии мышки на элемент управления
      this.SetProperty(CANV_ELEMENT_PROP_FORE_COLOR_TOGGLE_MOUSE_OVER,CLR_DEF_FORE_COLOR_MOUSE_OVER); // Цвет текста элемента управления по умолчанию в состоянии "включено" при наведении мышки на элемент управления
      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_BACKGROUND_COLOR_TOGGLE,CLR_DEF_CONTROL_STD_BACK_COLOR_ON);  // Цвет фона элемента управления в состоянии "включено"
      this.SetProperty(CANV_ELEMENT_PROP_BACKGROUND_COLOR_TOGGLE_MOUSE_DOWN,CLR_DEF_CONTROL_STD_BACK_DOWN_ON); // Цвет фона элемента управления в состоянии "включено" при нажатии мышки на элемент управления
      this.SetProperty(CANV_ELEMENT_PROP_BACKGROUND_COLOR_TOGGLE_MOUSE_OVER,CLR_DEF_CONTROL_STD_BACK_OVER_ON); // Цвет фона элемента управления в состоянии "включено" при наведении мышки на элемент управления
      this.SetProperty(CANV_ELEMENT_PROP_BORDER_COLOR_MOUSE_DOWN,CLR_DEF_BORDER_MOUSE_DOWN);          // Цвет рамки элемента управления при нажатии мышки на элемент управления
      this.SetProperty(CANV_ELEMENT_PROP_BORDER_COLOR_MOUSE_OVER,CLR_DEF_BORDER_MOUSE_OVER);          // Цвет рамки элемента управления при наведении мышки на элемент управления
      this.SetProperty(CANV_ELEMENT_PROP_BUTTON_TOGGLE,false);                                        // Флаг "Переключатель" элемента управления, имеющего кнопку
      this.SetProperty(CANV_ELEMENT_PROP_BUTTON_STATE,false);                                         // Состояние элемента управления "Переключатель", имеющего кнопку
     }
   else
     {
      ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_FAILED_CREATE_ELM_OBJ),": ",this.m_name);
     }
  }
//+------------------------------------------------------------------+

Теперь в свойства ForeColor флажка проверки прописываются значения BorderColor, так как для флажка есть свой собственный цвет.

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


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

//+------------------------------------------------------------------+
//| Создаёт структуру объекта                                        |
//+------------------------------------------------------------------+
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.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);// Цвет флажка проверки элемента управления при наведении мышки на элемент управления

   this.m_struct_obj.fore_color_mouse_down=(color)this.GetProperty(CANV_ELEMENT_PROP_FORE_COLOR_MOUSE_DOWN);            // Цвет текста элемента управления по умолчанию при нажатии мышки на элемент управления
   this.m_struct_obj.fore_color_mouse_over=(color)this.GetProperty(CANV_ELEMENT_PROP_FORE_COLOR_MOUSE_OVER);            // Цвет текста элемента управления по умолчанию при наведении мышки на элемент управления
   this.m_struct_obj.fore_color_toggle=(color)this.GetProperty(CANV_ELEMENT_PROP_FORE_COLOR_TOGGLE);                    // Цвет текста элемента управления в состоянии "включено"
   this.m_struct_obj.fore_color_toggle_mouse_down=(color)this.GetProperty(CANV_ELEMENT_PROP_FORE_COLOR_TOGGLE_MOUSE_DOWN);// Цвет текста элемента управления по умолчанию в состоянии "включено" при нажатии мышки на элемент управления
   this.m_struct_obj.fore_color_toggle_mouse_over=(color)this.GetProperty(CANV_ELEMENT_PROP_FORE_COLOR_TOGGLE_MOUSE_OVER);// Цвет текста элемента управления по умолчанию в состоянии "включено" при наведении мышки на элемент управления
   this.m_struct_obj.background_color_toggle=(color)this.GetProperty(CANV_ELEMENT_PROP_BACKGROUND_COLOR_TOGGLE);        // Цвет фона элемента управления в состоянии "включено"
   this.m_struct_obj.background_color_toggle_mouse_down=(color)this.GetProperty(CANV_ELEMENT_PROP_BACKGROUND_COLOR_TOGGLE_MOUSE_DOWN);// Цвет фона элемента управления в состоянии "включено" при нажатии мышки на элемент управления
   this.m_struct_obj.background_color_toggle_mouse_over=(color)this.GetProperty(CANV_ELEMENT_PROP_BACKGROUND_COLOR_TOGGLE_MOUSE_OVER);// Цвет фона элемента управления в состоянии "включено" при наведении мышки на элемент управления
   this.m_struct_obj.button_toggle=(bool)this.GetProperty(CANV_ELEMENT_PROP_BUTTON_TOGGLE);                             // Флаг "Переключатель" элемента управления, имеющего кнопку
   this.m_struct_obj.button_state=(bool)this.GetProperty(CANV_ELEMENT_PROP_BUTTON_STATE);                               // Состояние элемента управления "Переключатель", имеющего кнопку
//--- Сохранение вещественных свойств

//--- Сохранение строковых свойств
   ::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_CHECK_FLAG_COLOR_MOUSE_OVER,this.m_struct_obj.check_flag_color_mouse_over); // Цвет флажка проверки элемента управления при наведении мышки на элемент управления
   this.SetProperty(CANV_ELEMENT_PROP_FORE_COLOR_MOUSE_DOWN,this.m_struct_obj.fore_color_mouse_down);             // Цвет текста элемента управления по умолчанию при нажатии мышки на элемент управления
   this.SetProperty(CANV_ELEMENT_PROP_FORE_COLOR_MOUSE_OVER,this.m_struct_obj.fore_color_mouse_over);             // Цвет текста элемента управления по умолчанию при наведении мышки на элемент управления
   this.SetProperty(CANV_ELEMENT_PROP_FORE_COLOR_TOGGLE,this.m_struct_obj.fore_color_toggle);                     // Цвет текста элемента управления в состоянии "включено"
   this.SetProperty(CANV_ELEMENT_PROP_FORE_COLOR_TOGGLE_MOUSE_DOWN,this.m_struct_obj.fore_color_toggle_mouse_down);// Цвет текста элемента управления по умолчанию в состоянии "включено" при нажатии мышки на элемент управления
   this.SetProperty(CANV_ELEMENT_PROP_FORE_COLOR_TOGGLE_MOUSE_OVER,this.m_struct_obj.fore_color_toggle_mouse_over);// Цвет текста элемента управления по умолчанию в состоянии "включено" при наведении мышки на элемент управления
   this.SetProperty(CANV_ELEMENT_PROP_BACKGROUND_COLOR_TOGGLE,this.m_struct_obj.background_color_toggle);         // Цвет фона элемента управления в состоянии "включено"
   this.SetProperty(CANV_ELEMENT_PROP_BACKGROUND_COLOR_TOGGLE_MOUSE_DOWN,this.m_struct_obj.background_color_toggle_mouse_down);// Цвет фона элемента управления в состоянии "включено" при нажатии мышки на элемент управления
   this.SetProperty(CANV_ELEMENT_PROP_BACKGROUND_COLOR_TOGGLE_MOUSE_OVER,this.m_struct_obj.background_color_toggle_mouse_over);// Цвет фона элемента управления в состоянии "включено" при наведении мышки на элемент управления
   this.SetProperty(CANV_ELEMENT_PROP_BUTTON_TOGGLE,this.m_struct_obj.button_toggle);                             // Флаг "Переключатель" элемента управления, имеющего кнопку
   this.SetProperty(CANV_ELEMENT_PROP_BUTTON_STATE,this.m_struct_obj.button_state);                               // Состояние элемента управления "Переключатель", имеющего кнопку
//--- Сохранение вещественных свойств

//--- Сохранение строковых свойств
   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));           // Текст графического элемента
  }
//+------------------------------------------------------------------+


В конструкторе класса объекта-тени в файле \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,true);
   CGCnvElement::SetOpacity(0);
   CGCnvElement::SetActive(false);
   this.m_opacity=CLR_DEF_SHADOW_OPACITY;
   this.m_blur=DEF_SHADOW_BLUR;
   color gray=CGCnvElement::ChangeColorSaturation(this.ChartBackgroundColor(),-100);
   this.m_color=CGCnvElement::ChangeColorLightness(gray,-50);
   this.m_shadow=false;
   this.m_visible=true;
   CGCnvElement::Erase();
  }
//+------------------------------------------------------------------+

Здесь нам нет необходимости хранить полностью прозрачный цвет, но пусть будет сохранён, чтобы избегать неинициализированных значений свойств.


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

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

В файле объекта-формы \MQL5\Include\DoEasy\Objects\Graph\Form.mqh в защищённой секции класса объявим две переменные — для хранения последнего события мышки (потребуется для обработчика последнего события) и для хранения первоначального цвета рамки формы (так как она может изменять свой цвет при взаимодействии с мышкой, и его нужно восстанавливать). Там же объявим виртуальные обработчики каждого из событий мышки:

protected:
   CArrayObj         m_list_elements;                          // Список присоединённых элементов
   CArrayObj         m_list_interact;                          // Список элементов взаимодействия
   CAnimations      *m_animations;                             // Указатель на объект анимаций
   CShadowObj       *m_shadow_obj;                             // Указатель на объект тени
   CMouseState       m_mouse;                                  // Объект класса "Состояния мышки"
   ENUM_MOUSE_FORM_STATE m_mouse_form_state;                   // Состояние мышки относительно формы
   ENUM_MOUSE_EVENT  m_mouse_event_last;                       // Последнее событие мышки
   ushort            m_mouse_state_flags;                      // Флаги состояния мышки
   int               m_offset_x;                               // Смещение координаты X относительно курсора
   int               m_offset_y;                               // Смещение координаты Y относительно курсора
   CArrayObj         m_list_tmp;                               // Список для размещения указателей
   int               m_init_x;                                 // Координата X формы при её создании
   int               m_init_y;                                 // Координата Y формы при её создании
   int               m_init_w;                                 // Ширина формы при её создании
   int               m_init_h;                                 // Высота формы при её создании
   color             m_border_color_init;                      // Первоначальный цвет рамки элемента
   
//--- Инициализирует переменные
   virtual void      Initialize(void);
   void              Deinitialize(void);
//--- Создаёт объект для тени
   void              CreateShadowObj(const color colour,const uchar opacity);
//--- Возвращает имя зависимого объекта
   string            CreateNameDependentObject(const string base_name)  const
                       { return ::StringSubstr(this.NameObj(),::StringLen(::MQLInfoString(MQL_PROGRAM_NAME))+1)+"_"+base_name;   }
//--- Обновляет координаты привязанных объектов
   virtual bool      MoveDependentObj(const int x,const int y,const bool redraw=false);
//--- Создаёт новый присоединённый элемент и добавляет его в список присоединённых объектов
   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);
   
//--- Обработчик события  Курсор за пределами формы, кнопки мышки не нажаты
   virtual void      MouseOutsideNotPressedHandler(const int id,const long& lparam,const double& dparam,const string& sparam);
//--- Обработчик события  Курсор за пределами формы, нажата кнопка мышки (любая)
   virtual void      MouseOutsidePressedHandler(const int id,const long& lparam,const double& dparam,const string& sparam);
//--- Обработчик события  Курсор за пределами формы, прокручивается колёсико мышки
   virtual void      MouseOutsideWhellHandler(const int id,const long& lparam,const double& dparam,const string& sparam);
//--- Обработчик события  Курсор в пределах формы, кнопки мышки не нажаты
   virtual void      MouseInsideNotPressedHandler(const int id,const long& lparam,const double& dparam,const string& sparam);
//--- Обработчик события  Курсор в пределах формы, нажата кнопка мышки (любая)
   virtual void      MouseInsidePressedHandler(const int id,const long& lparam,const double& dparam,const string& sparam);
//--- Обработчик события  Курсор в пределах формы, прокручивается колёсико мышки
   virtual void      MouseInsideWhellHandler(const int id,const long& lparam,const double& dparam,const string& sparam);
//--- Обработчик события  Курсор в пределах активной области, кнопки мышки не нажаты
   virtual void      MouseActiveAreaNotPressedHandler(const int id,const long& lparam,const double& dparam,const string& sparam);
//--- Обработчик события  Курсор в пределах активной области, нажата кнопка мышки (любая)
   virtual void      MouseActiveAreaPressedHandler(const int id,const long& lparam,const double& dparam,const string& sparam);
//--- Обработчик события  Курсор в пределах активной области, прокручивается колёсико мышки
   virtual void      MouseActiveAreaWhellHandler(const int id,const long& lparam,const double& dparam,const string& sparam);
//--- Обработчик события  Курсор в пределах активной области, отжата кнопка мышки (левая)
   virtual void      MouseActiveAreaReleasedHandler(const int id,const long& lparam,const double& dparam,const string& sparam);
//--- Обработчик события  Курсор в пределах области прокрутки окна, кнопки мышки не нажаты
   virtual void      MouseScrollAreaNotPressedHandler(const int id,const long& lparam,const double& dparam,const string& sparam);
//--- Обработчик события  Курсор в пределах области прокрутки окна, нажата кнопка мышки (любая)
   virtual void      MouseScrollAreaPressedHandler(const int id,const long& lparam,const double& dparam,const string& sparam);
//--- Обработчик события  Курсор в пределах области прокрутки окна, прокручивается колёсико мышки
   virtual void      MouseScrollAreaWhellHandler(const int id,const long& lparam,const double& dparam,const string& sparam);

public:


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

public:
//--- Перерисовывает объект
   virtual void      Redraw(bool redraw) { return; }
//--- Создаёт список всех объектов взаимодействия
   int               CreateListInteractObj(void);
//--- Возвращает указатель на объект-форму в списке объектов взаимодействия
   CForm            *GetInteractForm(const int index)    { return this.m_list_interact.At(index);  }
//--- Возвращает начальную координату (1) X, (2) Y, (3) ширину, (4) высоту формы
   int               GetCoordXInit(void)                 const { return this.m_init_x;             }
   int               GetCoordYInit(void)                 const { return this.m_init_y;             }
   int               GetWidthInit(void)                  const { return this.m_init_w;             }
   int               GetHeightInit(void)                 const { return this.m_init_h;             }
//--- Устанавливает начальную координату (1) X, (2) Y, (3) ширину, (4) высоту формы
   void              SetCoordXInit(const int value)            { this.m_init_x=value;              }
   void              SetCoordYInit(const int value)            { this.m_init_y=value;              }
   void              SetWidthInit(const int value)             { this.m_init_w=value;              }
   void              SetHeightInit(const int value)            { this.m_init_h=value;              }
//--- (1) Получает, (2) возвращает состояние мышки относительно формы, координату (3) X, (4) Y курсора, (4) последнее событие мышки
   ENUM_MOUSE_FORM_STATE MouseFormState(const int id,const long lparam,const double dparam,const string sparam);
   ENUM_MOUSE_FORM_STATE GetMouseState(void)             const { return this.m_mouse_form_state;   }
   int               MouseCursorX(void)                  const { return this.m_mouse.CoordX();     }
   int               MouseCursorY(void)                  const { return this.m_mouse.CoordY();     }
   ENUM_MOUSE_EVENT  MouseEventLast(void)                const { return this.m_mouse_event_last;   }
//--- Устанавливает для графика флаги прокрутки колёсиком мышки, контекстного меню и инструмента "перекрестие"
   void              SetChartTools(const bool flag);
//--- (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) со всех сторон
   virtual void      SetBorderSizeLeft(const uint value)       { this.SetProperty(CANV_ELEMENT_PROP_BORDER_SIZE_LEFT,value);        }
   virtual void      SetBorderSizeTop(const uint value)        { this.SetProperty(CANV_ELEMENT_PROP_BORDER_SIZE_TOP,value);         }
   virtual void      SetBorderSizeRight(const uint value)      { this.SetProperty(CANV_ELEMENT_PROP_BORDER_SIZE_RIGHT,value);       }
   virtual void      SetBorderSizeBottom(const uint value)     { this.SetProperty(CANV_ELEMENT_PROP_BORDER_SIZE_BOTTOM,value);      }

//--- Обновляет координаты (сдвигает канвас)
   virtual bool      Move(const int x,const int y,const bool redraw=false);
//--- Устанавливает приоритет графического объекта на получение события нажатия мышки на графике
   virtual bool      SetZorder(const long value,const bool only_prop);
//--- Устанавливает объект выше всех
   virtual void      BringToTop(void);
   
//--- Обработчик событий
   virtual void      OnChartEvent(const int id,const long& lparam,const double& dparam,const string& sparam);
//--- Обработчик событий мышки
   virtual void      OnMouseEvent(const int id,const long& lparam,const double& dparam,const string& sparam);
//--- Обработчик последнего события мышки
   virtual void      OnMouseEventPostProcessing(void);

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


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

//+------------------------------------------------------------------+
//| Методы упрощённого доступа к свойствам объекта                   |
//+------------------------------------------------------------------+
//--- (1) Устанавливает, (2) возвращает цвет рамки элемента управления
   void              SetBorderColor(const color colour,const bool set_init_color)
                       {
                        this.SetProperty(CANV_ELEMENT_PROP_BORDER_COLOR,colour);
                        if(set_init_color)
                           this.SetBorderColorInit(colour);
                       }
   color             BorderColor(void)                            const { return (color)this.GetProperty(CANV_ELEMENT_PROP_BORDER_COLOR);             }
//--- (1) Устанавливает, (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);  }
//--- (1) Устанавливает, (2) возвращает изначальный цвет рамки элемента
   void              SetBorderColorInit(const color colour)             { this.m_border_color_init=colour;                                            }
   color             BorderColorInit(void)                        const { return (color)this.m_border_color_init;                                     }

//--- (1) Устанавливает, (2) возвращает цвет тени формы


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

//+------------------------------------------------------------------+
//| Инициализирует переменные                                        |
//+------------------------------------------------------------------+
void CForm::Initialize(void)
  {
   this.m_list_elements.Clear();
   this.m_list_elements.Sort();
   this.m_list_interact.Clear();
   this.m_list_interact.Sort();
   this.m_list_tmp.Clear();
   this.m_list_tmp.Sort();
   this.m_shadow_obj=NULL;
   this.m_shadow=false;
   this.SetBorderSizeTop(DEF_FRAME_WIDTH_SIZE);
   this.SetBorderSizeBottom(DEF_FRAME_WIDTH_SIZE);
   this.SetBorderSizeLeft(DEF_FRAME_WIDTH_SIZE);
   this.SetBorderSizeRight(DEF_FRAME_WIDTH_SIZE);
   this.SetBorderColor(CLR_DEF_BORDER_COLOR,true);
   this.SetBorderColorMouseDown(CLR_DEF_BORDER_MOUSE_DOWN);
   this.SetBorderColorMouseOver(CLR_DEF_BORDER_MOUSE_OVER);
   this.m_gradient_v=true;
   this.m_gradient_c=false;
   this.m_mouse_state_flags=0;
   this.m_mouse_event_last=MOUSE_EVENT_NO_EVENT;
   this.m_offset_x=0;
   this.m_offset_y=0;
   this.m_init_x=0;
   this.m_init_y=0;
   this.m_init_w=0;
   this.m_init_h=0;
   CGCnvElement::SetInteraction(false);
   this.m_animations=new CAnimations(CGCnvElement::GetObject());
   this.m_list_tmp.Add(this.m_animations);
  }
//+------------------------------------------------------------------+


Во всех методах создания объектов, где требуется установка цвета фона, пропишем флаг. В методе CreateAndAddNewElement():

obj.SetBackgroundColor(colour,true);

В методе, устанавливающем цветовую схему:

//+------------------------------------------------------------------+
//| Устанавливает цветовую схему                                     |
//+------------------------------------------------------------------+
void CForm::SetColorTheme(const ENUM_COLOR_THEMES theme,const uchar opacity)
  {
   if(this.m_shadow && this.m_shadow_obj!=NULL)
      this.SetColorShadow(array_color_themes[theme][COLOR_THEME_COLOR_FORM_SHADOW]);
   this.SetOpacity(opacity);
   this.SetBackgroundColor(array_color_themes[theme][COLOR_THEME_COLOR_FORM_BG],true);
   this.SetBorderColor(array_color_themes[theme][COLOR_THEME_COLOR_FORM_FRAME],true);
  }
//+------------------------------------------------------------------+

В каждом случае теперь будет сохраняться изначальный цвет фона элемента.


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

//+------------------------------------------------------------------+
//| Устанавливает и возвращает состояние мышки относительно формы    |
//+------------------------------------------------------------------+
ENUM_MOUSE_FORM_STATE CForm::MouseFormState(const int id,const long lparam,const double dparam,const string sparam)
  {
//--- Получаем состояние мышки относительно формы и состояние кнопок мыши и клавиш Shift и Ctrl
   this.m_mouse_form_state=MOUSE_FORM_STATE_OUTSIDE_FORM_NOT_PRESSED;
   ENUM_MOUSE_BUTT_KEY_STATE state=this.m_mouse.ButtonKeyState(id,lparam,dparam,sparam);
//--- Получаем флаги состоянии мышки из объекта класса CMouseState и сохраняем их в переменной
   this.m_mouse_state_flags=this.m_mouse.GetMouseFlags();
//--- Если курсор внутри формы
   if(CGCnvElement::CursorInsideElement(m_mouse.CoordX(),m_mouse.CoordY()))
     {
      //--- Устанавливаем бит 8, отвечающий за флаг "курсор внутри формы"
      this.m_mouse_state_flags |= (0x0001<<8);
      //--- Если курсор внутри активной зоны - устанавливаем бит 9 "курсор внутри активной зоны"
      if(CGCnvElement::CursorInsideActiveArea(m_mouse.CoordX(),m_mouse.CoordY()))
         this.m_mouse_state_flags |= (0x0001<<9);
      //--- иначе - снимаем бит "курсор внутри активной зоны"
      else this.m_mouse_state_flags &=0xFDFF;
      //--- Если нажата одна из трёх кнопок мыши - проверяем расположение курсора в активной области и
      //--- возвращаем соответствующее значение нажатой кнопки (в активной зоне или в области формы)
      if((this.m_mouse_state_flags & 0x0001)!=0 || (this.m_mouse_state_flags & 0x0002)!=0 || (this.m_mouse_state_flags & 0x0010)!=0)
         this.m_mouse_form_state=((this.m_mouse_state_flags & 0x0200)!=0 ? MOUSE_FORM_STATE_INSIDE_ACTIVE_AREA_PRESSED : MOUSE_FORM_STATE_INSIDE_FORM_PRESSED);
      //--- иначе если ни одна кнопка мышки не нажата
      else
        {
         //--- если колесо мышки прокручивается, возвращаем соответствующее значение прокрутки колеса (в активной зоне или в области формы)
         if((this.m_mouse_state_flags & 0x0080)!=0)
            this.m_mouse_form_state=((this.m_mouse_state_flags & 0x0200)!=0 ? MOUSE_FORM_STATE_INSIDE_ACTIVE_AREA_WHEEL : MOUSE_FORM_STATE_INSIDE_FORM_WHEEL);
         //--- иначе - возвращаем соответствующее значение ненажатой кнопки (в активной зоне или в области формы)
         else
            this.m_mouse_form_state=((this.m_mouse_state_flags & 0x0200)!=0 ? MOUSE_FORM_STATE_INSIDE_ACTIVE_AREA_NOT_PRESSED : MOUSE_FORM_STATE_INSIDE_FORM_NOT_PRESSED);
        } 
     }
//--- Если курсор снаружи формы
   else
     {
      //--- возвращаем соответствующее значение кнопок в неактивной зоне
      this.m_mouse_form_state=
        (
         ((this.m_mouse_state_flags & 0x0001)!=0 || (this.m_mouse_state_flags & 0x0002)!=0 || (this.m_mouse_state_flags & 0x0010)!=0) ? 
          MOUSE_FORM_STATE_OUTSIDE_FORM_PRESSED : MOUSE_FORM_STATE_OUTSIDE_FORM_NOT_PRESSED
        );
     }
   return this.m_mouse_form_state;
  }
//+------------------------------------------------------------------+


Обработчик событий мышки:

//+------------------------------------------------------------------+
//| Обработчик событий мышки                                         |
//+------------------------------------------------------------------+
void CForm::OnMouseEvent(const int id,const long &lparam,const double &dparam,const string &sparam)
  {
   switch(id)
     {
      //--- Курсор за пределами формы, кнопки мышки не нажаты
      //--- Курсор за пределами формы, нажата кнопка мышки (любая)
      //--- Курсор за пределами формы, прокручивается колёсико мышки
      case MOUSE_EVENT_OUTSIDE_FORM_NOT_PRESSED :
      case MOUSE_EVENT_OUTSIDE_FORM_PRESSED     :
      case MOUSE_EVENT_OUTSIDE_FORM_WHEEL       :
        break;
      //--- Курсор в пределах формы, кнопки мышки не нажаты
      case MOUSE_EVENT_INSIDE_FORM_NOT_PRESSED :
        this.MouseInsideNotPressedHandler(id,lparam,dparam,sparam);
        break;
      //--- Курсор в пределах формы, нажата кнопка мышки (любая)
      case MOUSE_EVENT_INSIDE_FORM_PRESSED :
        this.MouseInsidePressedHandler(id,lparam,dparam,sparam);
        break;
      //--- Курсор в пределах формы, прокручивается колёсико мышки
      case MOUSE_EVENT_INSIDE_FORM_WHEEL :
        this.MouseInsideWhellHandler(id,lparam,dparam,sparam);
        break;
      //--- Курсор в пределах активной области, кнопки мышки не нажаты
      case MOUSE_EVENT_INSIDE_ACTIVE_AREA_NOT_PRESSED :
        this.MouseActiveAreaNotPressedHandler(id,lparam,dparam,sparam);
        break;
      //--- Курсор в пределах активной области, нажата кнопка мышки (любая)
      case MOUSE_EVENT_INSIDE_ACTIVE_AREA_PRESSED :
        this.MouseActiveAreaPressedHandler(id,lparam,dparam,sparam);
        break;
      //--- Курсор в пределах активной области, прокручивается колёсико мышки
      case MOUSE_EVENT_INSIDE_ACTIVE_AREA_WHEEL :
        this.MouseActiveAreaWhellHandler(id,lparam,dparam,sparam);
        break;
      //--- Курсор в пределах активной области, отжата кнопка мышки (левая)
      case MOUSE_EVENT_INSIDE_ACTIVE_AREA_RELEASED :
        this.MouseActiveAreaReleasedHandler(id,lparam,dparam,sparam);
        break;
      //--- Курсор в пределах области прокрутки окна, кнопки мышки не нажаты
      case MOUSE_EVENT_INSIDE_SCROLL_AREA_NOT_PRESSED :
        this.MouseScrollAreaNotPressedHandler(id,lparam,dparam,sparam);
        break;
      //--- Курсор в пределах области прокрутки окна, нажата кнопка мышки (любая)
      case MOUSE_EVENT_INSIDE_SCROLL_AREA_PRESSED :
        this.MouseScrollAreaPressedHandler(id,lparam,dparam,sparam);
        break;
      //--- Курсор в пределах области прокрутки окна, прокручивается колёсико мышки
      case MOUSE_EVENT_INSIDE_SCROLL_AREA_WHEEL :
        this.MouseScrollAreaWhellHandler(id,lparam,dparam,sparam);
        break;
      //---MOUSE_EVENT_NO_EVENT
      default:
        break;
     }
   this.m_mouse_event_last=(ENUM_MOUSE_EVENT)id;
  }
//+------------------------------------------------------------------+

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

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

//+------------------------------------------------------------------+
//| Обработчик события Курсор за пределами формы,                    |
//| кнопки мышки не нажаты                                           |
//+------------------------------------------------------------------+
void CForm::MouseOutsideNotPressedHandler(const int id,const long& lparam,const double& dparam,const string& sparam)
  {
   return;
  }
//+------------------------------------------------------------------+
//| Обработчик события Курсор за пределами формы,                    |
//| нажата кнопка мышки (любая)                                      |
//+------------------------------------------------------------------+
void CForm::MouseOutsidePressedHandler(const int id,const long& lparam,const double& dparam,const string& sparam)
  {
   return;
  }
//+------------------------------------------------------------------+
//| Обработчик события Курсор за пределами формы,                    |
//| прокручивается колёсико мышки                                    |
//+------------------------------------------------------------------+
void CForm::MouseOutsideWhellHandler(const int id,const long& lparam,const double& dparam,const string& sparam)
  {
   return;
  }
//+------------------------------------------------------------------+
//| Обработчик события Курсор в пределах формы,                      |
//| кнопки мышки не нажаты                                           |
//+------------------------------------------------------------------+
void CForm::MouseInsideNotPressedHandler(const int id,const long& lparam,const double& dparam,const string& sparam)
  {
   return;
  }
//+------------------------------------------------------------------+
//| Обработчик события Курсор в пределах формы,                      |
//| нажата кнопка мышки (любая)                                      |
//+------------------------------------------------------------------+
void CForm::MouseInsidePressedHandler(const int id,const long& lparam,const double& dparam,const string& sparam)
  {
   return;
  }
//+------------------------------------------------------------------+
//| Обработчик события Курсор в пределах формы,                      |
//| прокручивается колёсико мышки                                    |
//+------------------------------------------------------------------+
void CForm::MouseInsideWhellHandler(const int id,const long& lparam,const double& dparam,const string& sparam)
  {
   return;
  }
//+------------------------------------------------------------------+
//| Обработчик события Курсор в пределах активной области,           |
//| кнопки мышки не нажаты                                           |
//+------------------------------------------------------------------+
void CForm::MouseActiveAreaNotPressedHandler(const int id,const long& lparam,const double& dparam,const string& sparam)
  {
   return;
  }
//+------------------------------------------------------------------+
//| Обработчик события Курсор в пределах активной области,           |
//| нажата кнопка мышки (любая)                                      |
//+------------------------------------------------------------------+
void CForm::MouseActiveAreaPressedHandler(const int id,const long& lparam,const double& dparam,const string& sparam)
  {
   return;
  }
//+------------------------------------------------------------------+
//| Обработчик события Курсор в пределах активной области,           |
//| прокручивается колёсико мышки                                    |
//+------------------------------------------------------------------+
void CForm::MouseActiveAreaWhellHandler(const int id,const long& lparam,const double& dparam,const string& sparam)
  {
   return;
  }
//+------------------------------------------------------------------+
//| Обработчик события Курсор в пределах активной области,           |
//| отжата кнопка мышки (левая)                                      |
//+------------------------------------------------------------------+
void CForm::MouseActiveAreaReleasedHandler(const int id,const long& lparam,const double& dparam,const string& sparam)
  {
   return;
  }
//+------------------------------------------------------------------+
//| Обработчик события Курсор в пределах области прокрутки окна,     |
//| кнопки мышки не нажаты                                           |
//+------------------------------------------------------------------+
void CForm::MouseScrollAreaNotPressedHandler(const int id,const long& lparam,const double& dparam,const string& sparam)
  {
   return;
  }
//+------------------------------------------------------------------+
//| Обработчик события Курсор в пределах области прокрутки окна,     |
//| нажата кнопка мышки (любая)                                      |
//+------------------------------------------------------------------+
void CForm::MouseScrollAreaPressedHandler(const int id,const long& lparam,const double& dparam,const string& sparam)
  {
   return;
  }
//+------------------------------------------------------------------+
//| Обработчик события Курсор в пределах области прокрутки окна,     |
//| прокручивается колёсико мышки                                    |
//+------------------------------------------------------------------+
void CForm::MouseScrollAreaWhellHandler(const int id,const long& lparam,const double& dparam,const string& sparam)
  {
   return;
  }
//+------------------------------------------------------------------+

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


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

//+------------------------------------------------------------------+
//| Обработчик последнего события мышки                              |
//+------------------------------------------------------------------+
void CForm::OnMouseEventPostProcessing(void)
  {
   ENUM_MOUSE_FORM_STATE state=GetMouseState();
   switch(state)
     {
      //--- Курсор за пределами формы, кнопки мышки не нажаты
      //--- Курсор за пределами формы, нажата кнопка мышки (любая)
      //--- Курсор за пределами формы, прокручивается колёсико мышки
      case MOUSE_FORM_STATE_OUTSIDE_FORM_NOT_PRESSED :
      case MOUSE_FORM_STATE_OUTSIDE_FORM_PRESSED     :
      case MOUSE_FORM_STATE_OUTSIDE_FORM_WHEEL       :
        if(MouseEventLast()==MOUSE_EVENT_INSIDE_ACTIVE_AREA_NOT_PRESSED)
          {
           this.SetBackgroundColor(this.BackgroundColorInit(),false);
           this.SetBorderColor(this.BorderColorInit(),false);
           this.Redraw(true);
           this.m_mouse_event_last=ENUM_MOUSE_EVENT(state+MOUSE_EVENT_NO_EVENT);
          }
        break;

      //--- Курсор в пределах формы, кнопки мышки не нажаты
      case MOUSE_FORM_STATE_INSIDE_FORM_NOT_PRESSED :
        break;
      //--- Курсор в пределах формы, нажата кнопка мышки (любая)
      case MOUSE_FORM_STATE_INSIDE_FORM_PRESSED :
        break;
      //--- Курсор в пределах формы, прокручивается колёсико мышки
      case MOUSE_FORM_STATE_INSIDE_FORM_WHEEL :
        break;
      //--- Курсор в пределах активной области, кнопки мышки не нажаты
      case MOUSE_FORM_STATE_INSIDE_ACTIVE_AREA_NOT_PRESSED :
        break;
      //--- Курсор в пределах активной области, нажата кнопка мышки (любая)
      case MOUSE_FORM_STATE_INSIDE_ACTIVE_AREA_PRESSED :
        break;
      //--- Курсор в пределах активной области, прокручивается колёсико мышки
      case MOUSE_FORM_STATE_INSIDE_ACTIVE_AREA_WHEEL :
        break;
      //--- Курсор в пределах активной области, отжата кнопка мышки (левая)
      case MOUSE_FORM_STATE_INSIDE_ACTIVE_AREA_RELEASED :
        break;
      //--- Курсор в пределах области прокрутки окна, кнопки мышки не нажаты
      case MOUSE_FORM_STATE_INSIDE_SCROLL_AREA_NOT_PRESSED :
        break;
      //--- Курсор в пределах области прокрутки окна, нажата кнопка мышки (любая)
      case MOUSE_FORM_STATE_INSIDE_SCROLL_AREA_PRESSED :
        break;
      //--- Курсор в пределах области прокрутки окна, прокручивается колёсико мышки
      case MOUSE_FORM_STATE_INSIDE_SCROLL_AREA_WHEEL :
        break;
      //---MOUSE_EVENT_NO_EVENT
      default:
        break;
     }
  }
//+------------------------------------------------------------------+

Метод тоже виртуальный, и при необходимости должен быть переопределён в наследуемых классах.
Что он делает: если состояние мышки для объекта определено как "курсор за пределами формы" (не важно состояние кнопок и колёсика), то проверяем какое было последнее событие мышки для объекта. Если это "курсор в пределах активной зоны и кнопки мышки не нажаты", значит считаем, что курсор уведён с объекта и обрабатываем это событие — устанавливаем изначальный цвет фона, изначальный цвет рамки объекта, перерисовываем объект и записываем в переменную, хранящую последнее событие мышки, текущее её событие.
Учитывая, что все значения констант в перечислении ENUM_MOUSE_EVENT отличаются от значений констант в перечислении ENUM_MOUSE_FORM_STATE (при одинаковом их составе и очерёдности) на величину значения константы MOUSE_EVENT_NO_EVENT перечисления ENUM_MOUSE_EVENT, то для получения корректного значения нам нужно прибавить значение константы MOUSE_EVENT_NO_EVENT к значению события мышки, полученном в переменную ENUM_MOUSE_FORM_STATE state.

Текст, находящийся на форме, например, текст кнопки, тоже может (но вовсе не обязательно всегда и в каждом объекте — лишь по желанию) изменять свой цвет при наведении или нажатии мышки на объект. Методы для обработки текста WinForms-объектов у нас расположены в классе базового WinForms-объекта в файле \MQL5\Include\DoEasy\Objects\Graph\WForms\WinFormBase.mqh.

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

//+------------------------------------------------------------------+
//| Класс объекта "форма"                                            |
//+------------------------------------------------------------------+
class CWinFormBase : public CForm
  {
protected:
   color             m_fore_color_init;                        // Первоначальный цвет текста элемента

private:
//--- Возвращает флаги шрифта
   uint              GetFontFlags(void);

public:
//--- Возвращает по типу (1) список, (2) количество привязанных элементов, (3) привязанный элемент по индексу в списке
   CArrayObj        *GetListElementsByType(const ENUM_GRAPH_ELEMENT_TYPE type);
   int               ElementsTotalByType(const ENUM_GRAPH_ELEMENT_TYPE type);
   CGCnvElement     *GetElementByType(const ENUM_GRAPH_ELEMENT_TYPE type,const int index);
//--- Очищает элемент с заполнением его цветом и непрозрачностью
   virtual void      Erase(const color colour,const uchar opacity,const bool redraw=false);
//--- Очищает элемент заливкой градиентом
   virtual void      Erase(color &colors[],const uchar opacity,const bool vgradient,const bool cycle,const bool redraw=false);
//--- Полностью очищает элемент
   virtual void      Erase(const bool redraw=false);
//--- Перерисовывает объект
   virtual void      Redraw(bool redraw);
//--- Устанавливает новые размеры (1) текущему, (2) указанному по индексу объекту
   virtual bool      Resize(const int w,const int h,const bool redraw);
   virtual bool      Resize(const int index,const int w,const int h,const bool redraw);

//--- Конструкторы
                     CWinFormBase(const long chart_id,
                                  const int subwindow,
                                  const string name,
                                  const int x,
                                  const int y,
                                  const int w,
                                  const int h);
                     CWinFormBase(const string name) : CForm(::ChartID(),0,name,0,0,0,0)
                       {
                        this.m_type=OBJECT_DE_TYPE_GWF_BASE; 
                       }
                       
//--- (1) Устанавливает, (2) возвращает цвет текста по умолчанию всех объектов на панели
   void              SetForeColor(const color clr,const bool set_init_color)
                       {
                        this.SetProperty(CANV_ELEMENT_PROP_FORE_COLOR,clr);
                        if(set_init_color)
                           this.SetForeColorInit(clr);
                       }
   color             ForeColor(void)                           const { return (color)this.GetProperty(CANV_ELEMENT_PROP_FORE_COLOR);                     }
//--- (1) Устанавливает, (2) возвращает первоначальный цвет текста по умолчанию всех объектов на панели
   void              SetForeColorInit(const color clr)               { this.m_fore_color_init=clr;                                                       }
   color             ForeColorInit(void)                       const { return (color)this.m_fore_color_init;                                             }
//--- (1) Устанавливает, (2) возвращает непрозрачность цвета текста по умолчанию всех объектов на панели
   void              SetForeColorOpacity(const uchar value)          { this.SetProperty(CANV_ELEMENT_PROP_FORE_COLOR_OPACITY,value);                     }
   uchar             ForeColorOpacity(void)                    const { return (uchar)this.GetProperty(CANV_ELEMENT_PROP_FORE_COLOR_OPACITY);             }
//--- (1) Устанавливает, (2) возвращает цвет текста элемента при нажатии мышки на элемент управления
   void              SetForeColorMouseDown(const color clr)          { this.SetProperty(CANV_ELEMENT_PROP_FORE_COLOR_MOUSE_DOWN,clr);                    }
   color             ForeColorMouseDown(void)                  const { return (color)this.GetProperty(CANV_ELEMENT_PROP_FORE_COLOR_MOUSE_DOWN);          }
//--- (1) Устанавливает, (2) возвращает цвет текста элемента при наведении мышки на элемент управления
   void              SetForeColorMouseOver(const color clr)          { this.SetProperty(CANV_ELEMENT_PROP_FORE_COLOR_MOUSE_OVER,clr);                    }
   color             ForeColorMouseOver(void)                  const { return (color)this.GetProperty(CANV_ELEMENT_PROP_FORE_COLOR_MOUSE_OVER);          }
   
//--- (1) Устанавливает, (2) возвращает текст элемента
   virtual void      SetText(const string text)                      { this.SetProperty(CANV_ELEMENT_PROP_TEXT,text);                                    }
   string            Text(void)                                const { return this.GetProperty(CANV_ELEMENT_PROP_TEXT);                                  }


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

//+------------------------------------------------------------------+
//| Конструктор                                                      |
//+------------------------------------------------------------------+
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,true);
   this.SetForeColorOpacity(CLR_DEF_FORE_COLOR_OPACITY);
   this.SetFontBoldType(FW_TYPE_NORMAL);
   this.SetMarginAll(0);
   this.SetPaddingAll(0);
   this.SetBorderSizeAll(0);
   this.SetDockMode(CANV_ELEMENT_DOCK_MODE_NONE,false);
   this.SetBorderStyle(FRAME_STYLE_NONE);
   this.SetAutoSize(false,false);
   CForm::SetCoordXInit(x);
   CForm::SetCoordYInit(y);
   CForm::SetWidthInit(w);
   CForm::SetHeightInit(h);
   this.m_shadow=false;
   this.m_gradient_v=true;
   this.m_gradient_c=false;
  }
//+------------------------------------------------------------------+


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

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

...

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

...

      property==CANV_ELEMENT_PROP_AUTOCHECK                    ?  CMessage::Text(MSG_CANV_ELEMENT_PROP_AUTOCHECK)+
         (only_prop ? "" : !this.SupportProperty(property)     ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)(bool)this.GetProperty(property)
         )  :
      property==CANV_ELEMENT_PROP_BUTTON_TOGGLE                ?  CMessage::Text(MSG_CANV_ELEMENT_PROP_BUTTON_TOGGLE)+
         (only_prop ? "" : !this.SupportProperty(property)     ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)(bool)this.GetProperty(property)
         )  :
      property==CANV_ELEMENT_PROP_BUTTON_STATE                 ?  CMessage::Text(MSG_CANV_ELEMENT_PROP_BUTTON_STATE)+
         (only_prop ? "" : !this.SupportProperty(property)     ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)(bool)this.GetProperty(property)
         )  :
      property==CANV_ELEMENT_PROP_CHECK_BACKGROUND_COLOR       ?  CMessage::Text(MSG_CANV_ELEMENT_PROP_CHECK_BACKGROUND_COLOR)+
         (only_prop ? "" : !this.SupportProperty(property)     ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+::ColorToString((color)this.GetProperty(property),true)
         )  :
      property==CANV_ELEMENT_PROP_CHECK_BACKGROUND_COLOR_OPACITY  ?  CMessage::Text(MSG_CANV_ELEMENT_PROP_CHECK_BACKGROUND_COLOR_OPACITY)+
         (only_prop ? "" : !this.SupportProperty(property)     ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)this.GetProperty(property)
         )  :
      property==CANV_ELEMENT_PROP_CHECK_BACKGROUND_COLOR_MOUSE_DOWN  ?  CMessage::Text(MSG_CANV_ELEMENT_PROP_CHECK_BACKGROUND_COLOR_MOUSE_DOWN)+
         (only_prop ? "" : !this.SupportProperty(property)     ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+::ColorToString((color)this.GetProperty(property),true)
         )  :
      property==CANV_ELEMENT_PROP_CHECK_BACKGROUND_COLOR_MOUSE_OVER  ?  CMessage::Text(MSG_CANV_ELEMENT_PROP_CHECK_BACKGROUND_COLOR_MOUSE_OVER)+
         (only_prop ? "" : !this.SupportProperty(property)     ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+::ColorToString((color)this.GetProperty(property),true)
         )  :
      property==CANV_ELEMENT_PROP_CHECK_FORE_COLOR             ?  CMessage::Text(MSG_CANV_ELEMENT_PROP_CHECK_FORE_COLOR)+
         (only_prop ? "" : !this.SupportProperty(property)     ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+::ColorToString((color)this.GetProperty(property),true)
         )  :
      property==CANV_ELEMENT_PROP_CHECK_FORE_COLOR_OPACITY     ?  CMessage::Text(MSG_CANV_ELEMENT_PROP_CHECK_FORE_COLOR_OPACITY)+
         (only_prop ? "" : !this.SupportProperty(property)     ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)this.GetProperty(property)
         )  :
      property==CANV_ELEMENT_PROP_CHECK_FORE_COLOR_MOUSE_DOWN  ?  CMessage::Text(MSG_CANV_ELEMENT_PROP_CHECK_FORE_COLOR_MOUSE_DOWN)+
         (only_prop ? "" : !this.SupportProperty(property)     ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+::ColorToString((color)this.GetProperty(property),true)
         )  :
      property==CANV_ELEMENT_PROP_CHECK_FORE_COLOR_MOUSE_OVER  ?  CMessage::Text(MSG_CANV_ELEMENT_PROP_CHECK_FORE_COLOR_MOUSE_OVER)+
         (only_prop ? "" : !this.SupportProperty(property)     ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+::ColorToString((color)this.GetProperty(property),true)
         )  :
      property==CANV_ELEMENT_PROP_CHECK_FLAG_COLOR             ?  CMessage::Text(MSG_CANV_ELEMENT_PROP_CHECK_FLAG_COLOR)+
         (only_prop ? "" : !this.SupportProperty(property)     ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+::ColorToString((color)this.GetProperty(property),true)
         )  :
      property==CANV_ELEMENT_PROP_CHECK_FLAG_COLOR_OPACITY     ?  CMessage::Text(MSG_CANV_ELEMENT_PROP_CHECK_FLAG_COLOR_OPACITY)+
         (only_prop ? "" : !this.SupportProperty(property)     ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)this.GetProperty(property)
         )  :
      property==CANV_ELEMENT_PROP_CHECK_FLAG_COLOR_MOUSE_DOWN  ?  CMessage::Text(MSG_CANV_ELEMENT_PROP_CHECK_FLAG_COLOR_MOUSE_DOWN)+
         (only_prop ? "" : !this.SupportProperty(property)     ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+::ColorToString((color)this.GetProperty(property),true)
         )  :
      property==CANV_ELEMENT_PROP_CHECK_FLAG_COLOR_MOUSE_OVER  ?  CMessage::Text(MSG_CANV_ELEMENT_PROP_CHECK_FLAG_COLOR_MOUSE_OVER)+
         (only_prop ? "" : !this.SupportProperty(property)     ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+::ColorToString((color)this.GetProperty(property),true)
         )  :
      ""
     );
  }
//+------------------------------------------------------------------+
//| Возвращает описание вещественного свойства элемента              |
//+------------------------------------------------------------------+


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

//+------------------------------------------------------------------+
// Возвращает по типу список привязанных элементов                   |
//+------------------------------------------------------------------+
CArrayObj *CWinFormBase::GetListElementsByType(const ENUM_GRAPH_ELEMENT_TYPE type)
  {
   return CSelect::ByGraphCanvElementProperty(this.GetListElements(),CANV_ELEMENT_PROP_TYPE,type,EQUAL);
  }
//+------------------------------------------------------------------+

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


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

//+------------------------------------------------------------------+
//| Возвращает по типу привязанный элемент по индексу в списке       |
//+------------------------------------------------------------------+
CGCnvElement *CWinFormBase::GetElementByType(const ENUM_GRAPH_ELEMENT_TYPE type,const int index)
  {
   CArrayObj *list=this.GetListElementsByType(type);
   return list.At(index);
  }
//+------------------------------------------------------------------+

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


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

//+------------------------------------------------------------------+
//| Возвращает по типу количество привязанных элементов              |
//+------------------------------------------------------------------+
int CWinFormBase::ElementsTotalByType(const ENUM_GRAPH_ELEMENT_TYPE type)
  {
   CArrayObj *list=this.GetListElementsByType(type);
   return(list!=NULL ? list.Total() : 0);
  }
//+------------------------------------------------------------------+

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

В классе объекта базового WinForms-контейнера в файле \MQL5\Include\DoEasy\Objects\Graph\WForms\Containers\Container.mqh переименуем методы для установки ширины рамки. Ранее мы использовали в наименовании "Frame", а сейчас используем "Border":

//--- Устанавливает ширину рамки формы (1) слева, (2) сверху, (3) справа, (4) снизу, (5) всех сторон элемента управления
   virtual void      SetBorderSizeLeft(const uint value)
                       {
                        CForm::SetBorderSizeLeft(value);
                        if(this.PaddingLeft()<this.BorderSizeLeft())
                           this.SetPaddingLeft(this.BorderSizeLeft());
                       }
   virtual void      SetBorderSizeTop(const uint value)
                       {
                        CForm::SetBorderSizeTop(value);
                        if(this.PaddingTop()<this.BorderSizeTop())
                           this.SetPaddingTop(this.BorderSizeTop());
                       }
   virtual void      SetBorderSizeRight(const uint value)
                       {
                        CForm::SetBorderSizeRight(value);
                        if(this.PaddingRight()<this.BorderSizeRight())
                           this.SetPaddingRight(this.BorderSizeRight());
                       }
   virtual void      SetBorderSizeBottom(const uint value)
                       {
                        CForm::SetBorderSizeBottom(value);
                        if(this.PaddingBottom()<this.BorderSizeBottom())
                           this.SetPaddingBottom(this.BorderSizeBottom());
                       }

Что важно. Если ранее эти методы имели иное название, и вызов метода родительского класса для установки ширины рамки не вызывал проблем, то сейчас получится рекурсивный вызов этим методом самого себя. Поэтому здесь мы явно указываем контекст, из которого вызывается нужный метод родительского класса, который имеет точно такое же наименование.

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

//--- Конструкторы
                     CContainer(const long chart_id,
                                const int subwindow,
                                const string name,
                                const int x,
                                const int y,
                                const int w,
                                const int h);
                     CContainer(const string name) : CWinFormBase(::ChartID(),0,name,0,0,0,0)
                       {
                        CGBaseObj::SetTypeElement(GRAPH_ELEMENT_TYPE_WF_CONTAINER);
                        CGCnvElement::SetProperty(CANV_ELEMENT_PROP_TYPE,GRAPH_ELEMENT_TYPE_WF_CONTAINER);
                        this.m_type=OBJECT_DE_TYPE_GWF_CONTAINER; 
                        this.SetForeColor(CLR_DEF_FORE_COLOR,true);
                        this.SetFontBoldType(FW_TYPE_NORMAL);
                        this.SetMarginAll(3);
                        this.SetPaddingAll(0);
                        this.SetDockMode(CANV_ELEMENT_DOCK_MODE_NONE,false);
                        this.SetBorderStyle(FRAME_STYLE_NONE);
                        this.SetAutoScroll(false,false);
                        this.SetAutoScrollMarginAll(0);
                        this.SetAutoSize(false,false);
                        this.SetAutoSizeMode(CANV_ELEMENT_AUTO_SIZE_MODE_GROW,false);
                        this.Initialize();
                       }
//--- Деструктор
                    ~CContainer();
  };
//+------------------------------------------------------------------+
//| Конструктор с указанием идентификатора чарта и подокна           |
//+------------------------------------------------------------------+
CContainer::CContainer(const long chart_id,
                       const int subwindow,
                       const string name,
                       const int x,
                       const int y,
                       const int w,
                       const int h) : CWinFormBase(chart_id,subwindow,name,x,y,w,h)
  {
   CGBaseObj::SetTypeElement(GRAPH_ELEMENT_TYPE_WF_CONTAINER);
   CGCnvElement::SetProperty(CANV_ELEMENT_PROP_TYPE,GRAPH_ELEMENT_TYPE_WF_CONTAINER);
   this.m_type=OBJECT_DE_TYPE_GWF_CONTAINER;
   this.SetForeColor(CLR_DEF_FORE_COLOR,true);
   this.SetFontBoldType(FW_TYPE_NORMAL);
   this.SetMarginAll(3);
   this.SetPaddingAll(0);
   this.SetDockMode(CANV_ELEMENT_DOCK_MODE_NONE,false);
   this.SetBorderStyle(FRAME_STYLE_NONE);
   this.SetAutoScroll(false,false);
   this.SetAutoScrollMarginAll(0);
   this.SetAutoSize(false,false);
   this.SetAutoSizeMode(CANV_ELEMENT_AUTO_SIZE_MODE_GROW,false);
   this.Initialize();
   this.SetCoordXInit(x);
   this.SetCoordYInit(y);
   this.SetWidthInit(w);
   this.SetHeightInit(h);
  }
//+------------------------------------------------------------------+


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

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

//--- В зависимости от типа созданного объекта
   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(),true);
        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,true);
        obj.SetBorderColor(obj.ForeColor(),true);
        break;
      //--- Для WinForms-объекта "Button"
      case GRAPH_ELEMENT_TYPE_WF_BUTTON      :
        //--- устанавливаем цвет текста объекта как цвет текста контейнера в зависимости от переданного в метод:
        //--- цвет фона устанавливаем  в зависимости от переданного в метод:
        //--- либо цвет фона стандартных элементов управления по умолчанию, либо переданный в метод.
        //--- Цвет рамки устанавливаем равным цвету текста
        obj.SetForeColor(this.ForeColor(),true);
        obj.SetBackgroundColor(colour==clrNONE ? CLR_DEF_CONTROL_STD_BACK_COLOR : colour,true);
        obj.SetBorderColor(obj.ForeColor(),true);
        obj.SetBorderStyle(FRAME_STYLE_SIMPLE);
        break;
      default:
        break;
     }
//--- Если у панели включено автоизменение размеров и есть привязанные объекты - вызываем метод изменения размеров
   if(this.AutoSize() && this.ElementsTotal()>0)
      this.AutoSizeProcess(redraw);
//--- Перерисовываем панель и все добавленные объекты и возвращаем true
   this.Redraw(redraw);
   return true;
  }
//+------------------------------------------------------------------+


В файле \MQL5\Include\DoEasy\Objects\Graph\WForms\Containers\GroupBox.mqh класса WinForms-объекта GroupBox, в его методе инициализации Initialize(), пропишем необходимость сохранения изначальных цветов при установке цветов по умолчанию:

//--- Создадим объект анимации и добавим его в список для хранения таких объектов
   this.m_animations=new CAnimations(CGCnvElement::GetObject());
   this.m_list_tmp.Add(this.m_animations);
//--- Устанавливаем прозрачный цвет для фона объекта и цвет по умолчанию для рамки
   this.SetBackgroundColor(CLR_CANV_NULL,true);
   this.SetOpacity(0);
   this.SetBorderColor(CLR_DEF_FRAME_GBOX_COLOR,true);
//--- Установим цвет и непрозрачность текста по умолчанию и отсутствие рамки объекта
   this.SetForeColor(CLR_DEF_FORE_COLOR,true);
   this.SetForeColorOpacity(CLR_DEF_FORE_COLOR_OPACITY);
   this.SetBorderStyle(FRAME_STYLE_SIMPLE);
//--- Установим параметры текста по умолчанию


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

//--- Создадим объект анимации и добавим его в список для хранения таких объектов
   this.m_animations=new CAnimations(CGCnvElement::GetObject());
   this.m_list_tmp.Add(this.m_animations);
//--- Устанавливаем прозрачный цвет для фона объекта
   this.SetBackgroundColor(CLR_CANV_NULL,true);
   this.SetOpacity(0);
//--- Установим цвет и непрозрачность текста по умолчанию и отсутствие рамки объекта
   this.SetForeColor(CLR_DEF_FORE_COLOR,true);
   this.SetForeColorOpacity(CLR_DEF_FORE_COLOR_OPACITY);
   this.SetBorderStyle(FRAME_STYLE_NONE);
//--- Установим параметры текста по умолчанию


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

В файле \MQL5\Include\DoEasy\Objects\Graph\WForms\Common Controls\CheckBox.mqh в защищённой секции класса объявим переменные для хранения изначальных цветов и виртуальные методы-обработчики событий мышки:

//+------------------------------------------------------------------+
//| Класс объекта CheckBox элементов управления WForms               |
//+------------------------------------------------------------------+
class CCheckBox : public CLabel
  {
private:
//--- Устанавливает координаты X и Y флажка проверки
   void              SetCheckFlagCoords(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;                                      // Высота флажка
   color             m_check_back_color_init;                        // Первоначальный цвет фона флажка проверки
   color             m_check_border_color_init;                      // Первоначальный цвет рамки фона флажка проверки
   color             m_check_flag_color_init;                        // Первоначальный цвет флажка проверки
//--- Автоматически устанавливает ширину и высоту элемента
   virtual bool      AutoSetWH(void);
//--- Отображает флажок проверки по указанному состоянию
   virtual void      ShowControlFlag(const ENUM_CANV_ELEMENT_CHEK_STATE state);
   
//--- (1) Устанавливает, (2) возвращает размеры флажка проверки на элементе
   void              SetCheckWidth(const int width)                  { this.m_check_w=(width<5  ? 5 : width);  }
   void              SetCheckHeight(const int height)                { this.m_check_h=(height<5 ? 5 : height); }
   int               CheckWidth(void)                          const { return this.m_check_w;                  }
   int               CheckHeight(void)                         const { return this.m_check_h;                  }

//--- Обработчик события  Курсор в пределах активной области, кнопки мышки не нажаты
   virtual void      MouseActiveAreaNotPressedHandler(const int id,const long& lparam,const double& dparam,const string& sparam);
//--- Обработчик события  Курсор в пределах активной области, нажата кнопка мышки (любая)
   virtual void      MouseActiveAreaPressedHandler(const int id,const long& lparam,const double& dparam,const string& sparam);
//--- Обработчик события  Курсор в пределах активной области, отжата кнопка мышки (левая)
   virtual void      MouseActiveAreaReleasedHandler(const int id,const long& lparam,const double& dparam,const string& sparam);

public:


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

//--- (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,const bool set_init_color)
                       {
                        this.SetProperty(CANV_ELEMENT_PROP_CHECK_BACKGROUND_COLOR,clr);
                        if(set_init_color)
                           this.SetCheckBackgroundColorInit(clr);
                       }
   color             CheckBackgroundColor(void)                   const { return (color)this.GetProperty(CANV_ELEMENT_PROP_CHECK_BACKGROUND_COLOR);         }
//--- (1) Устанавливает, (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              SetCheckBackgroundColorInit(const color clr)       { this.m_check_back_color_init=clr;                                                 }
   color             CheckBackgroundColorInit(void)               const { return (color)this.m_check_back_color_init;                                       }

//--- (1) Устанавливает, (2) возвращает цвет рамки флажка проверки элемента управления
   void              SetCheckBorderColor(const color clr,const bool set_init_color)
                       {
                        this.SetProperty(CANV_ELEMENT_PROP_CHECK_FORE_COLOR,clr);
                        if(set_init_color)
                           this.SetCheckBorderColorInit(clr);
                       }
   color             CheckBorderColor(void)                       const { return (color)this.GetProperty(CANV_ELEMENT_PROP_CHECK_FORE_COLOR);               }
//--- (1) Устанавливает, (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              SetCheckBorderColorInit(const color clr)           { this.m_check_border_color_init=clr;                                               }
   color             CheckBorderColorInit(void)                   const { return (color)this.m_check_border_color_init;                                     }

//--- (1) Устанавливает, (2) возвращает цвет флажка проверки элемента управления
   void              SetCheckFlagColor(const color clr,const bool set_init_color)
                       {
                        this.SetProperty(CANV_ELEMENT_PROP_CHECK_FLAG_COLOR,clr);
                        if(set_init_color)
                           this.SetCheckFlagColorInit(clr);
                       }
   color             CheckFlagColor(void)                         const { return (color)this.GetProperty(CANV_ELEMENT_PROP_CHECK_FLAG_COLOR);               }
//--- (1) Устанавливает, (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);    }
//--- (1) Устанавливает, (2) возвращает первоначальный цвет флажка проверки элемента управления
   void              SetCheckFlagColorInit(const color clr)             { this.m_check_flag_color_init=clr;                                                 }
   color             CheckFlagColorInit(void)                     const { return (color)this.m_check_flag_color_init;                                       }
   
//--- Обработчик последнего события мышки
   virtual void      OnMouseEventPostProcessing(void);
   
//--- Перерисовывает объект
   virtual void      Redraw(bool redraw);

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


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

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


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

Обработчик события Курсор в пределах активной области, кнопки мышки не нажаты:

//+------------------------------------------------------------------+
//| Обработчик события Курсор в пределах активной области,           |
//| кнопки мышки не нажаты                                           |
//+------------------------------------------------------------------+
void CCheckBox::MouseActiveAreaNotPressedHandler(const int id,const long& lparam,const double& dparam,const string& sparam)
  {
   this.SetCheckBackgroundColor(this.CheckBackgroundColorMouseOver(),false);
   this.SetCheckBorderColor(this.CheckBorderColorMouseOver(),false);
   this.SetCheckFlagColor(this.CheckFlagColorMouseOver(),false);
   this.Redraw(true);
  }
//+------------------------------------------------------------------+

Если курсор мышки наведён на активную область объекта, то устанавливаем для поля флажка проверки, его рамки и для самого флажка новые цвета фона, установленные для этого состояния мышки и объекта, и перерисовываем объект.


Обработчик события Курсор в пределах активной области, нажата кнопка мышки (любая):

//+------------------------------------------------------------------+
//| Обработчик события Курсор в пределах активной области,           |
//| нажата кнопка мышки (любая)                                      |
//+------------------------------------------------------------------+
void CCheckBox::MouseActiveAreaPressedHandler(const int id,const long& lparam,const double& dparam,const string& sparam)
  {
   this.SetCheckBackgroundColor(this.CheckBackgroundColorMouseDown(),false);
   this.SetCheckBorderColor(this.CheckBorderColorMouseDown(),false);
   this.SetCheckFlagColor(this.CheckFlagColorMouseDown(),false);
   this.Redraw(false);
  }
//+------------------------------------------------------------------+

Если курсор мышки наведён на активную область объекта, то устанавливаем для поля флажка проверки, его рамки и для самого флажка новые цвета фона, установленные для этого состояния мышки и объекта, и перерисовываем объект.


Обработчик события Курсор в пределах активной области, отжата кнопка мышки (левая):

//+------------------------------------------------------------------+
//| Обработчик события Курсор в пределах активной области,           |
//| отжата кнопка мышки (левая)                                      |
//+------------------------------------------------------------------+
void CCheckBox::MouseActiveAreaReleasedHandler(const int id,const long& lparam,const double& dparam,const string& sparam)
  {
//--- Если кнопка мышки отпущена за пределами элемента - это отказ от взаимодействия с элементом
   if(lparam<this.CoordX() || lparam>this.RightEdge() || dparam<this.CoordY() || dparam>this.BottomEdge())
     {
      this.SetCheckBackgroundColor(this.BackgroundColorInit(),false);
      this.SetCheckBorderColor(this.CheckBorderColorInit(),false);
      this.SetCheckFlagColor(this.CheckFlagColorInit(),false);
      //--- Выведем в журнал тестовую запись
      Print(DFUN_ERR_LINE,TextByLanguage("Отмена","Cancel"));
     }
//--- Если кнопка мышки отпущена в пределах элемента - это щелчок по элементу управления
   else
     {
      this.SetCheckBackgroundColor(this.CheckBackgroundColorMouseOver(),false);
      this.SetCheckBorderColor(this.CheckBorderColorMouseOver(),false);
      this.SetCheckFlagColor(this.CheckFlagColorInit(),false);
      this.SetChecked(!this.Checked());
      //--- Выведем в журнал тестовую запись
      Print(DFUN_ERR_LINE,TextByLanguage("Щелчок","Click"));
     }
   this.Redraw(false);
  }
//+------------------------------------------------------------------+

Здесь обрабатывается два состояния:

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

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

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


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

//+------------------------------------------------------------------+
//| Обработчик последнего события мышки                              |
//+------------------------------------------------------------------+
void CCheckBox::OnMouseEventPostProcessing(void)
  {
   ENUM_MOUSE_FORM_STATE state=GetMouseState();
   switch(state)
     {
      //--- Курсор за пределами формы, кнопки мышки не нажаты
      //--- Курсор за пределами формы, нажата кнопка мышки (любая)
      //--- Курсор за пределами формы, прокручивается колёсико мышки
      case MOUSE_FORM_STATE_OUTSIDE_FORM_NOT_PRESSED :
      case MOUSE_FORM_STATE_OUTSIDE_FORM_PRESSED     :
      case MOUSE_FORM_STATE_OUTSIDE_FORM_WHEEL       :
        if(MouseEventLast()==MOUSE_EVENT_INSIDE_ACTIVE_AREA_NOT_PRESSED)
          {
           this.SetBackgroundColor(this.BackgroundColorInit(),false);
           this.SetBorderColor(this.BorderColorInit(),false);
           this.SetCheckBackgroundColor(this.CheckBackgroundColorInit(),false);
           this.SetCheckBorderColor(this.CheckBorderColorInit(),false);
           this.SetCheckFlagColor(this.CheckFlagColorInit(),false);
           this.m_mouse_event_last=ENUM_MOUSE_EVENT(state+MOUSE_EVENT_NO_EVENT);
           this.Redraw(false);
          }
        break;

      //--- Курсор в пределах формы, кнопки мышки не нажаты
      case MOUSE_FORM_STATE_INSIDE_FORM_NOT_PRESSED :
        break;
      //--- Курсор в пределах формы, нажата кнопка мышки (любая)
      case MOUSE_FORM_STATE_INSIDE_FORM_PRESSED :
        break;
      //--- Курсор в пределах формы, прокручивается колёсико мышки
      case MOUSE_FORM_STATE_INSIDE_FORM_WHEEL :
        break;
      //--- Курсор в пределах активной области, кнопки мышки не нажаты
      case MOUSE_FORM_STATE_INSIDE_ACTIVE_AREA_NOT_PRESSED :
        break;
      //--- Курсор в пределах активной области, нажата кнопка мышки (любая)
      case MOUSE_FORM_STATE_INSIDE_ACTIVE_AREA_PRESSED :
        break;
      //--- Курсор в пределах активной области, прокручивается колёсико мышки
      case MOUSE_FORM_STATE_INSIDE_ACTIVE_AREA_WHEEL :
        break;
      //--- Курсор в пределах активной области, отжата кнопка мышки (левая)
      case MOUSE_FORM_STATE_INSIDE_ACTIVE_AREA_RELEASED :
        break;
      //--- Курсор в пределах области прокрутки окна, кнопки мышки не нажаты
      case MOUSE_FORM_STATE_INSIDE_SCROLL_AREA_NOT_PRESSED :
        break;
      //--- Курсор в пределах области прокрутки окна, нажата кнопка мышки (любая)
      case MOUSE_FORM_STATE_INSIDE_SCROLL_AREA_PRESSED :
        break;
      //--- Курсор в пределах области прокрутки окна, прокручивается колёсико мышки
      case MOUSE_FORM_STATE_INSIDE_SCROLL_AREA_WHEEL :
        break;
      //---MOUSE_EVENT_NO_EVENT
      default:
        break;
     }
  }
//+------------------------------------------------------------------+

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


Класс WinForms-объекта RadioButton в файле \MQL5\Include\DoEasy\Objects\Graph\WForms\Common Controls\RadioButton.mqh унаследован от только что доработанного класса объекта CheckBox, поэтому здесь нам придётся делать меньше доработок.

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

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

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

//--- Обработчик события  Курсор в пределах активной области, отжата кнопка мышки (левая)
   virtual void      MouseActiveAreaReleasedHandler(const int id,const long& lparam,const double& dparam,const string& sparam);

public:


Обработчик события Курсор в пределах активной области, отжата кнопка мышки (левая):

//+------------------------------------------------------------------+
//| Обработчик события Курсор в пределах активной области,           |
//| отжата кнопка мышки (левая)                                      |
//+------------------------------------------------------------------+
void CRadioButton::MouseActiveAreaReleasedHandler(const int id,const long& lparam,const double& dparam,const string& sparam)
  {
//--- Если кнопка мышки отпущена за пределами элемента - это отказ от взаимодействия с элементом
   if(lparam<this.CoordX() || lparam>this.RightEdge() || dparam<this.CoordY() || dparam>this.BottomEdge())
     {
      this.SetCheckBackgroundColor(this.BackgroundColorInit(),false);
      this.SetCheckBorderColor(this.CheckBorderColorInit(),false);
      this.SetCheckFlagColor(this.CheckFlagColorInit(),false);
      Print(DFUN_ERR_LINE,TextByLanguage("Отмена","Cancel"));
     }
//--- Если кнопка мышки отпущена в пределах элемента - это щелчок по элементу управления
   else
     {
      this.SetCheckBackgroundColor(this.CheckBackgroundColorMouseOver(),false);
      this.SetCheckBorderColor(this.CheckBorderColorMouseOver(),false);
      this.SetCheckFlagColor(this.CheckFlagColorInit(),false);
      Print(DFUN_ERR_LINE,TextByLanguage("Щелчок","Click"));
     }
   this.Redraw(false);
  }
//+------------------------------------------------------------------+

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


WinForms-объект "Кнопка" (Button). Кнопка может быть двух типов:

  1. При нажатии кнопка срабатывает и возвращается в исходное состояние,
  2. При нажатии кнопка срабатывает и остаётся в нажатом положении, повторное нажатие переключает её состояние на обратное (триггер — Toggle-Button).

Соответственно, нам необходим флаг, определяющий, какая это кнопка, и флаг, определяющий состояние кнопки-триггера (нажата/отжата), и, естественно, ещё один набор для определения цветов кнопки в нажатом состоянии (цвет фона, цвет при наведении курсора и нажатии на кнопку).

В файле \MQL5\Include\DoEasy\Objects\Graph\WForms\Common Controls\Button.mqh в приватной секции класса объявим массивы для хранения цветов новых состояний кнопки:

//+------------------------------------------------------------------+
//| Класс объекта Label элементов управления WForms                  |
//+------------------------------------------------------------------+
class CButton : public CLabel
  {
private:
   int               m_text_x;                                 // Координата X текста
   int               m_text_y;                                 // Координата Y текста
   color             m_array_colors_bg_tgl[];                  // Массив цветов фона элемента для состояния "включено"
   color             m_array_colors_bg_tgl_dwn[];              // Массив цветов фона элемента для состояния "включено" при нажатии мышки на элемент управления
   color             m_array_colors_bg_tgl_ovr[];              // Массив цветов фона элемента для состояния "включено" при наведении мышки на элемент управления
   color             m_array_colors_bg_tgl_init[];             // Массив изначальных цветов фона элемента для состояния "включено"
protected:


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

protected:
//--- Автоматически устанавливает ширину и высоту элемента
   virtual bool      AutoSetWH(void);

//--- Обработчик события  Курсор в пределах активной области, кнопки мышки не нажаты
   virtual void      MouseActiveAreaNotPressedHandler(const int id,const long& lparam,const double& dparam,const string& sparam);
//--- Обработчик события  Курсор в пределах активной области, нажата кнопка мышки (любая)
   virtual void      MouseActiveAreaPressedHandler(const int id,const long& lparam,const double& dparam,const string& sparam);
//--- Обработчик события  Курсор в пределах активной области, отжата кнопка мышки (левая)
   virtual void      MouseActiveAreaReleasedHandler(const int id,const long& lparam,const double& dparam,const string& sparam);

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

//--- (1) Устанавливает, (2) возвращает флаг "Переключатель" элемента управления
   void              SetToggleFlag(const bool flag)
                       {
                        this.SetProperty(CANV_ELEMENT_PROP_BUTTON_TOGGLE,flag);
                        if(this.Toggle())
                           this.SetColorsToggleON(CLR_DEF_CONTROL_STD_BACK_COLOR_ON,CLR_DEF_CONTROL_STD_BACK_DOWN_ON,CLR_DEF_CONTROL_STD_BACK_OVER_ON,true);
                       }
   bool              Toggle(void)                        const { return (bool)this.GetProperty(CANV_ELEMENT_PROP_BUTTON_TOGGLE);                               }
//--- (1) Устанавливает, (2) возвращает состояние элемента управления "Переключатель"
   void              SetState(const bool flag)                 { this.SetProperty(CANV_ELEMENT_PROP_BUTTON_STATE,flag);                                        }
   bool              State(void)                         const { return (bool)this.GetProperty(CANV_ELEMENT_PROP_BUTTON_STATE);                                }
   
//--- (1,2) Устанавливает, (3) возвращает основной цвет фона для состояния "включено"
   void              SetBackgroundColorToggleON(const color colour,const bool set_init_color)
                       {
                        this.SetProperty(CANV_ELEMENT_PROP_BACKGROUND_COLOR_TOGGLE,colour);
                        color arr[1];
                        arr[0]=colour;
                        this.CopyArraysColors(this.m_array_colors_bg_tgl,arr,DFUN);
                        if(set_init_color)
                           this.CopyArraysColors(this.m_array_colors_bg_tgl_init,arr,DFUN);
                       }
   void              SetBackgroundColorsToggleON(color &colors[],const bool set_init_colors)
                       {
                        this.CopyArraysColors(this.m_array_colors_bg_tgl,colors,DFUN);
                        this.SetProperty(CANV_ELEMENT_PROP_BACKGROUND_COLOR_TOGGLE,this.m_array_colors_bg_tgl[0]);
                        if(set_init_colors)
                           this.CopyArraysColors(this.m_array_colors_bg_tgl_init,colors,DFUN);
                       }
   color             BackgroundColorToggleON(void)       const { return (color)this.GetProperty(CANV_ELEMENT_PROP_BACKGROUND_COLOR_TOGGLE);                    }
   
//--- (1,2) Устанавливает, (3) возвращает цвет фона при нажатии мышки на элемент управления для состояния "включено"
   void              SetBackgroundColorToggleONMouseDown(const color colour)
                       {
                        this.SetProperty(CANV_ELEMENT_PROP_BACKGROUND_COLOR_TOGGLE_MOUSE_DOWN,colour);
                        color arr[1];
                        arr[0]=colour;
                        this.CopyArraysColors(this.m_array_colors_bg_tgl_dwn,arr,DFUN);
                       }
   void              SetBackgroundColorsToggleONMouseDown(color &colors[])
                       {
                        this.CopyArraysColors(this.m_array_colors_bg_tgl_dwn,colors,DFUN);
                        this.SetProperty(CANV_ELEMENT_PROP_BACKGROUND_COLOR_TOGGLE_MOUSE_DOWN,this.m_array_colors_bg_dwn[0]);
                       }
   color             BackgroundColorToggleONMouseDown(void) const { return (color)this.GetProperty(CANV_ELEMENT_PROP_BACKGROUND_COLOR_TOGGLE_MOUSE_DOWN);      }
                       
//--- (1,2) Устанавливает, (3) возвращает цвет фона при наведении мышки на элемент управления для состояния "включено"
   void              SetBackgroundColorToggleONMouseOver(const color colour)
                       {
                        this.SetProperty(CANV_ELEMENT_PROP_BACKGROUND_COLOR_TOGGLE_MOUSE_OVER,colour);
                        color arr[1];
                        arr[0]=colour;
                        this.CopyArraysColors(this.m_array_colors_bg_tgl_ovr,arr,DFUN);
                       }
   void              SetBackgroundColorsToggleONMouseOver(color &colors[])
                       {
                        this.CopyArraysColors(this.m_array_colors_bg_tgl_ovr,colors,DFUN);
                        this.SetProperty(CANV_ELEMENT_PROP_BACKGROUND_COLOR_TOGGLE_MOUSE_OVER,this.m_array_colors_bg_ovr[0]);
                       }
   color             BackgroundColorToggleONMouseOver(void) const { return (color)this.GetProperty(CANV_ELEMENT_PROP_BACKGROUND_COLOR_TOGGLE_MOUSE_OVER);      }

//--- Возвращает изначальный основной цвет фона для состояния "включено"
   color             BackgroundColorToggleONInit(void)      const { return this.m_array_colors_bg_tgl_init[0]; }
   
//--- Устанавливает цвета для состояния "включено"
   void              SetColorsToggleON(const color back,const color back_down,const color back_over,const bool set_init_color);
   
//--- Обработчик последнего события мышки
   virtual void      OnMouseEventPostProcessing(void);

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

Методы идентичны таким же методам для установки и возврата цветов объектов других классов, рассмотренных нами выше.


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

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


Переопределим обработчики событий мышки.

Обработчик события Курсор в пределах активной области, кнопки мышки не нажаты:

//+------------------------------------------------------------------+
//| Обработчик события Курсор в пределах активной области,           |
//| кнопки мышки не нажаты                                           |
//+------------------------------------------------------------------+
void CButton::MouseActiveAreaNotPressedHandler(const int id,const long& lparam,const double& dparam,const string& sparam)
  {
//--- Если это простая кнопка - устанавливаем цвет для состояния "Курсор над активной зоной, кнопка мышки не нажата"
   if(!this.Toggle())
      this.SetBackgroundColor(this.BackgroundColorMouseOver(),false);
//--- Если это кнопка-переключатель - устанавливаем цвет для этого состояния в зависимости от того нажата кнопка или нет
   else
      this.SetBackgroundColor(this.State() ? this.BackgroundColorToggleONMouseOver() : this.BackgroundColorMouseOver(),false);
//--- Устанавливаем цвет рамки для этого состояния
   this.SetBorderColor(this.BorderColorMouseOver(),false);
//--- Перерисовываем объект
   this.Redraw(false);
  }
//+------------------------------------------------------------------+


Обработчик события Курсор в пределах активной области, нажата кнопка мышки (любая):

//+------------------------------------------------------------------+
//| Обработчик события Курсор в пределах активной области,           |
//| нажата кнопка мышки (любая)                                      |
//+------------------------------------------------------------------+
void CButton::MouseActiveAreaPressedHandler(const int id,const long& lparam,const double& dparam,const string& sparam)
  {
//--- Если это простая кнопка - устанавливаем цвет для состояния "Курсор над активной зоной, кнопка мышки нажата"
   if(!this.Toggle())
      this.SetBackgroundColor(this.BackgroundColorMouseDown(),false);
//--- Если это кнопка-переключатель - устанавливаем цвет для этого состояния в зависимости от того нажата кнопка или нет
   else
      this.SetBackgroundColor(this.State() ? this.BackgroundColorToggleONMouseDown() : this.BackgroundColorMouseDown(),false);
//--- Устанавливаем цвет рамки для этого состояния
   this.SetBorderColor(this.BorderColorMouseDown(),false);
//--- Перерисовываем объект
   this.Redraw(false);
  }
//+------------------------------------------------------------------+


Обработчик события Курсор в пределах активной области, отжата кнопка мышки (левая):

//+------------------------------------------------------------------+
//| Обработчик события Курсор в пределах активной области,           |
//| отжата кнопка мышки (левая)                                      |
//+------------------------------------------------------------------+
void CButton::MouseActiveAreaReleasedHandler(const int id,const long& lparam,const double& dparam,const string& sparam)
  {
//--- Если кнопка мышки отпущена за пределами элемента - это отказ от взаимодействия с элементом
   if(lparam<this.CoordX() || lparam>this.RightEdge() || dparam<this.CoordY() || dparam>this.BottomEdge())
     {
      //--- Если это простая кнопка - устанавливаем изначальный цвет фона
      if(!this.Toggle())
         this.SetBackgroundColor(this.BackgroundColorInit(),false);
      //--- Если это кнопка-переключатель - устанавливаем изначальный цвет в зависимости от того нажата кнопка или нет
      else
         this.SetBackgroundColor(!this.State() ? this.BackgroundColorInit() : this.BackgroundColorToggleONInit(),false);
      //--- Устанавливаем изначальный цвет рамки
      this.SetBorderColor(this.BorderColorInit(),false);
      //--- Выводим тестовое сообщение в журнал
      Print(DFUN_ERR_LINE,TextByLanguage("Отмена","Cancel"));
     }
//--- Если кнопка мышки отпущена в пределах элемента - это щелчок по элементу управления
   else
     {
      //--- Если это простая кнопка - устанавливаем цвет фона для состояния "Курсор мышки над активной зоной"
      if(!this.Toggle())
         this.SetBackgroundColor(this.BackgroundColorMouseOver(),false);
      //--- Если это кнопка-переключатель -
      else
        {
         //--- устанавливаем состояние кнопки на противоположное
         this.SetState(!this.State());
         //--- устанавливаем цвет фона для состояния "Курсор мышки над активной зоной" в зависимости от того нажата кнопка или нет
         this.SetBackgroundColor(this.State() ? this.BackgroundColorToggleONMouseOver() : this.BackgroundColorMouseOver(),false);
        }
      //--- Выводим тестовое сообщение в журнал
      Print(DFUN_ERR_LINE,TextByLanguage("Щелчок","Click"));
      //--- Устанавливаем цвет рамки для состояния "Курсор мышки над активной зоной"
      this.SetBorderColor(this.BorderColorMouseOver(),false);
     }
//--- Перерисовываем объект
   this.Redraw(false);
  }
//+------------------------------------------------------------------+

Логика каждого метода подробно расписана в комментариях к коду и, надеюсь, в пояснениях не нуждается.


Обработчик последнего события мышки:

//+------------------------------------------------------------------+
//| Обработчик последнего события мышки                              |
//+------------------------------------------------------------------+
void CButton::OnMouseEventPostProcessing(void)
  {
   ENUM_MOUSE_FORM_STATE state=GetMouseState();
   switch(state)
     {
      //--- Курсор за пределами формы, кнопки мышки не нажаты
      //--- Курсор за пределами формы, нажата кнопка мышки (любая)
      //--- Курсор за пределами формы, прокручивается колёсико мышки
      case MOUSE_FORM_STATE_OUTSIDE_FORM_NOT_PRESSED :
      case MOUSE_FORM_STATE_OUTSIDE_FORM_PRESSED     :
      case MOUSE_FORM_STATE_OUTSIDE_FORM_WHEEL       :
        if(MouseEventLast()==MOUSE_EVENT_INSIDE_ACTIVE_AREA_NOT_PRESSED)
          {
           this.SetBackgroundColor(this.State() ? this.BackgroundColorToggleON() : this.BackgroundColorInit(),false);
           this.SetBorderColor(this.BorderColorInit(),false);
           this.m_mouse_event_last=ENUM_MOUSE_EVENT(state+MOUSE_EVENT_NO_EVENT);
           this.Redraw(false);
          }
        break;

      //--- Курсор в пределах формы, кнопки мышки не нажаты
      case MOUSE_FORM_STATE_INSIDE_FORM_NOT_PRESSED :
        break;
      //--- Курсор в пределах формы, нажата кнопка мышки (любая)
      case MOUSE_FORM_STATE_INSIDE_FORM_PRESSED :
        break;
      //--- Курсор в пределах формы, прокручивается колёсико мышки
      case MOUSE_FORM_STATE_INSIDE_FORM_WHEEL :
        break;
      //--- Курсор в пределах активной области, кнопки мышки не нажаты
      case MOUSE_FORM_STATE_INSIDE_ACTIVE_AREA_NOT_PRESSED :
        break;
      //--- Курсор в пределах активной области, нажата кнопка мышки (любая)
      case MOUSE_FORM_STATE_INSIDE_ACTIVE_AREA_PRESSED :
        break;
      //--- Курсор в пределах активной области, прокручивается колёсико мышки
      case MOUSE_FORM_STATE_INSIDE_ACTIVE_AREA_WHEEL :
        break;
      //--- Курсор в пределах активной области, отжата кнопка мышки (левая)
      case MOUSE_FORM_STATE_INSIDE_ACTIVE_AREA_RELEASED :
        break;
      //--- Курсор в пределах области прокрутки окна, кнопки мышки не нажаты
      case MOUSE_FORM_STATE_INSIDE_SCROLL_AREA_NOT_PRESSED :
        break;
      //--- Курсор в пределах области прокрутки окна, нажата кнопка мышки (любая)
      case MOUSE_FORM_STATE_INSIDE_SCROLL_AREA_PRESSED :
        break;
      //--- Курсор в пределах области прокрутки окна, прокручивается колёсико мышки
      case MOUSE_FORM_STATE_INSIDE_SCROLL_AREA_WHEEL :
        break;
      //---MOUSE_EVENT_NO_EVENT
      default:
        break;
     }
  }
//+------------------------------------------------------------------+

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

Метод, устанавливающий цвета для состояния "включено" кнопки-переключателя:

//+------------------------------------------------------------------+
//| Устанавливает цвета для состояния "включено" toggle-элемента     |
//+------------------------------------------------------------------+
void CButton::SetColorsToggleON(const color back,const color back_down,const color back_over,const bool set_init_color)
  {
   this.SetBackgroundColorToggleON(back,set_init_color);
   this.SetBackgroundColorToggleONMouseDown(back_down);
   this.SetBackgroundColorToggleONMouseOver(back_over);
  }
//+------------------------------------------------------------------+

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


Доработаем класс коллекции графических элементов в файле \MQL5\Include\DoEasy\Collections\GraphElementsCollection.mqh.

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

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

//--- Добавляет (1) стандартный графический объект, (2) графический элемент на канвасе в коллекцию
   bool              AddGraphObjToCollection(const string source,CChartObjectsControl *obj_control);
//--- Ищет объекты взаимодействия
   CForm            *SearchInteractObj(CForm *form,const int id,const long &lparam,const double &dparam,const string &sparam);
//--- Возвращает указатель на форму, находящуюся под курсором
   CForm            *GetFormUnderCursor(const int id, 
                                        const long &lparam, 
                                        const double &dparam, 
                                        const string &sparam,
                                        ENUM_MOUSE_FORM_STATE &mouse_state,
                                        long &obj_ext_id,
                                        int &form_index);
//--- Сбрасывает всем формам флаги взаимодействия кроме указанной
   void              ResetAllInteractionExeptOne(CGCnvElement *form);
//--- Постобработка бывшей активной формы под курсором
   void              FormPostProcessing(void);
//--- Добавляет элемент в список-коллекцию
   bool AddCanvElmToCollection(CGCnvElement *element);


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

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

//--- Создаёт графический объект-форму на канвасе на указанном графике и подокне
   int               CreateForm(const long chart_id,
                                const int subwindow,
                                const string name,
                                const int x,
                                const int y,
                                const int w,
                                const int h,
                                const color clr,
                                const uchar opacity,
                                const bool movable,
                                const bool activity,
                                const bool shadow=false,
                                const bool redraw=false)
                       {
                        int id=this.m_list_all_canv_elm_obj.Total();
                        CForm *obj=new CForm(chart_id,subwindow,name,x,y,w,h);
                        ENUM_ADD_OBJ_RET_CODE res=this.AddOrGetCanvElmToCollection(obj,id);
                        if(res==ADD_OBJ_RET_CODE_ERROR)
                           return WRONG_VALUE;
                        obj.SetID(id);
                        obj.SetActive(activity);
                        obj.SetMovable(movable);
                        obj.SetBackgroundColor(clr,true);
                        obj.SetBorderColor(clr,true);
                        obj.SetOpacity(opacity,false);
                        obj.SetShadow(shadow);
                        obj.DrawRectangle(0,0,obj.Width()-1,obj.Height()-1,obj.BorderColor(),obj.Opacity());
                        obj.Done();
                        obj.Erase(clr,opacity,redraw);
                        return obj.ID();
                       }

Такие, и похожие изменения внесены во все методы, где есть установка цвета.


Метод для поиска объектов взаимодействия:

//+------------------------------------------------------------------+
//| Ищет объекты взаимодействия                                      |
//+------------------------------------------------------------------+
CForm *CGraphElementsCollection::SearchInteractObj(CForm *form,const int id,const long &lparam,const double &dparam,const string &sparam)
  {
//--- Если передан не пустой указатель
   if(form!=NULL)
     {
      //--- Создадим список объектов взаимодействия
      int total=form.CreateListInteractObj();
      //--- В цикле по созданному списку
      for(int i=total-1;i>WRONG_VALUE;i--)
        {
         //--- получаем очередной объект-форму
         CForm *obj=form.GetInteractForm(i);
         //--- Если объёкт получен и курсор мышки находится над объектом - возвращаем указатель на найденный объект
         if(obj!=NULL && obj.MouseFormState(id,lparam,dparam,sparam)>MOUSE_FORM_STATE_OUTSIDE_FORM_WHEEL)
            return obj;
        }
     }
//--- Возвращаем обратно тот же указатель
   return form;
  }
//+------------------------------------------------------------------+

В прошлой статье мы рассматривали логику этого метода, но там этот код был в составе метода GetFormUnderCursor().
Теперь же, вместо этого блока кода мы просто впишем вызов этого метода:

//+------------------------------------------------------------------+
//| Возвращает указатель на форму, находящуюся под курсором          |
//+------------------------------------------------------------------+
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)
           {
            //--- Найдём объект взаимодействия.
            //--- Это будет либо найденный объект, либо та же форма
            form=this.SearchInteractObj(form,id,lparam,dparam,sparam);
            //--- Возвращаем объект-форму
            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)
           {
            //--- Найдём объект взаимодействия.
            //--- Это будет либо найденный объект, либо та же форма
            form=this.SearchInteractObj(form,id,lparam,dparam,sparam);
            //--- Возвращаем объект-форму
            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;
  }
//+------------------------------------------------------------------+

Благодаря такой небольшой доработке код метода стал более читаем.


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

//+------------------------------------------------------------------+
//| Постобработка бывшей активной формы под курсором                 |
//+------------------------------------------------------------------+
void CGraphElementsCollection::FormPostProcessing(void)
  {
 //--- Получаем все элементы с типом CForm и выше
   CArrayObj *list=GetList(CANV_ELEMENT_PROP_TYPE,GRAPH_ELEMENT_TYPE_FORM,EQUAL_OR_MORE);
   if(list==NULL)
      return;
   //--- В цикле по списку полученных элементов
   int total=list.Total();
   for(int i=0;i<total;i++)
     {
      //--- получаем указатель на объект
      CForm *obj=list.At(i);
      //--- если указатель получить не удалось - идём к следующему объекту в списке
      if(obj==NULL)
         continue;
      //--- Создаём список объектов взаимодействия объекта и получаем их количество
      int count=obj.CreateListInteractObj();
      //--- В цикле по полученному списку
      for(int j=0;j<count;j++)
        {
         //--- получаем очередной объект
         CForm *elm=obj.GetInteractForm(j);
         if(elm==NULL)
            continue;
         //--- и вызываем его метод обработки событий мышки
         elm.OnMouseEventPostProcessing();
        }
     }
  }
//+------------------------------------------------------------------+

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


Когда мы наводим курсор на объект-форму, доступную для перемещения, и нажимаем кнопку мышки, для формы в обработчике событий класса-коллекции графических элементов включаются флаги удержания мышкой и перемещения. Но если мы нажимаем кнопку на прикреплённом к форме объекте, то его нельзя смещать — для него флаг перемещения снимается, да и фокус устанавливается на него, а не на форму. Таким образом у нас остаётся включенным флаг удержания, но снят флаг перемещения. Событие перемещения мышки и флаг перемещения у нас обрабатываются одновременно:
      //--- Если событие перемещения мышки и стоит флаг перемещения - смещаем форму за курсором (если указатель на неё валидный)
      if(id==CHARTEVENT_MOUSE_MOVE && move)
        {
         if(form!=NULL)
           {
            //---...
            //---...

Нам нужно разделить их проверку, и тогда, если снят флаг перемещения, но мышка движется (при этом флаг удержания установлен), то мы сможем определить это состояние. И, если отпустить кнопку мышки, то будет зарегистрировано событие MOUSE_FORM_STATE_NONE. Это событие появляется только в этом случае, так как обработку такой ситуации мы ещё не делали, а это и есть событие отпускания кнопки мышки.

Таким образом, нам необходимо разделить проверку события перемещения курсора и установленного флага перемещения. И в случае снятого флага перемещения нам нужно определить событие MOUSE_FORM_STATE_NONE и заменить его на новое — MOUSE_FORM_STATE_INSIDE_ACTIVE_AREA_RELEASED.

Просто приведу участок кода, переработанный так, как и было описано выше. Здесь мы разделили два события на раздельную их проверку:

      if(id==CHARTEVENT_MOUSE_MOVE)
        {
         //--- Если курсор над формой
         if(form!=NULL)
           {
            //--- Если стоит флаг перемещения - смещаем форму за курсором
            if(move)
              {
               //--- рассчитываем смещение курсора относительно начала координат формы
               int x=this.m_mouse.CoordX()-form.OffsetX();
               int y=this.m_mouse.CoordY()-form.OffsetY();
               //--- получаем ширину и высоту графика, на котором находится форма
               int chart_width=(int)::ChartGetInteger(form.ChartID(),CHART_WIDTH_IN_PIXELS,form.SubWindow());
               int chart_height=(int)::ChartGetInteger(form.ChartID(),CHART_HEIGHT_IN_PIXELS,form.SubWindow());
               //--- Если форма не в составе расширенного стандартного графического объекта
               if(form_index==WRONG_VALUE)
                 {
                  //--- Корректируем рассчитанные координаты для формы в случае выхода формы за пределы графика
                  if(x<0) x=0;
                  if(x>chart_width-form.Width()) x=chart_width-form.Width();
                  if(y<0) y=0;
                  if(y>chart_height-form.Height()) y=chart_height-form.Height();
                  //--- Если на графике нет панели торговли в один клик
                  if(!::ChartGetInteger(form.ChartID(),CHART_SHOW_ONE_CLICK))
                    {
                     //--- рассчитаем координаты формы так, чтобы при её перемещении на кнопку включения панели торговли в один клик, форма под неё не попадала
                     if(y<17 && x<41)
                        y=17;
                    }
                  //--- Если на графике отображена панель торговли в один клик
                  else
                    {
                     //--- рассчитаем координаты формы так, чтобы при её перемещении на панель торговли в один клик, форма под неё не попадала
                     if(y<80 && x<192)
                        y=80;
                    }
                 }
               //--- Если форма входит в состав расширенного стандартного графического объекта
               else
                 {
                  if(graph_obj_id>WRONG_VALUE)
                    {
                     //--- Получаем список объектов по идентификатору объекта (должен быть один объект)
                     CArrayObj *list_ext=CSelect::ByGraphicStdObjectProperty(GetListStdGraphObjectExt(),GRAPH_OBJ_PROP_ID,0,graph_obj_id,EQUAL);
                     //--- Если список получить удалось и он не пустой
                     if(list_ext!=NULL && list_ext.Total()>0)
                       {
                        //--- получаем графический объект из списка
                        CGStdGraphObj *ext=list_ext.At(0);
                        //--- Если указатель на объект получен
                        if(ext!=NULL)
                          {
                           //--- получаем тип объекта
                           ENUM_OBJECT type=ext.GraphObjectType();
                           //--- Если объект строится по экранным координатам - записываем координаты в объект
                           if(type==OBJ_LABEL || type==OBJ_BUTTON || type==OBJ_BITMAP_LABEL || type==OBJ_EDIT || type==OBJ_RECTANGLE_LABEL)
                             {
                              ext.SetXDistance(x);
                              ext.SetYDistance(y);
                             }
                           //--- иначе, если объект строится по координатам время/цена
                           else
                             {
                              //--- рассчитываем смещение от начала координат формы до её центра
                              int shift=(int)::ceil(form.Width()/2)+1;
                              //--- Если форма находится на одной из опорных точек графического объекта
                              if(form_index<ext.Pivots())
                                {
                                 //--- ограничиваем координаты формы так, чтобы они не выходили за пределы графика
                                 if(x+shift<0)
                                    x=-shift;
                                 if(x+shift>chart_width)
                                    x=chart_width-shift;
                                 if(y+shift<0)
                                    y=-shift;
                                 if(y+shift>chart_height)
                                    y=chart_height-shift;
                                 //--- устанавливаем в объект рассчитанные координаты
                                 ext.ChangeCoordsExtendedObj(x+shift,y+shift,form_index);
                                }
                              //--- Если форма - центральная для управления всеми опорными точками графического объекта
                              else
                                {
                                 //--- Получаем и записываем в структуру m_data_pivot_point экранные координаты всех опорных точек объекта
                                 if(this.GetPivotPointCoordsAll(ext,m_data_pivot_point))
                                   {
                                    //--- В цикле по количеству опорных точек объекта
                                    for(int i=0;i<(int)this.m_data_pivot_point.Size();i++)
                                      {
                                       //--- ограничиваем экранные координаты текущей опорной точки так, чтобы они не выходили за пределы графика
                                       //--- По X-координае
                                       if(x+shift-::fabs(this.m_data_pivot_point[i].ShiftX)<0)
                                          x=-shift+::fabs(m_data_pivot_point[i].ShiftX);
                                       if(x+shift+::fabs(this.m_data_pivot_point[i].ShiftX)>chart_width)
                                          x=chart_width-shift-::fabs(this.m_data_pivot_point[i].ShiftX);
                                       //--- По Y-координае
                                       if(y+shift-::fabs(this.m_data_pivot_point[i].ShiftY)<0)
                                          y=-shift+::fabs(this.m_data_pivot_point[i].ShiftY);
                                       if(y+shift+::fabs(this.m_data_pivot_point[i].ShiftY)>chart_height)
                                          y=chart_height-shift-::fabs(this.m_data_pivot_point[i].ShiftY);
                                       //--- устанавливаем в текущую опорную точку объекта рассчитанные координаты
                                       ext.ChangeCoordsExtendedObj(x+shift+this.m_data_pivot_point[i].ShiftX,y+shift+this.m_data_pivot_point[i].ShiftY,i);
                                      }
                                   }
                                }
                             }
                          }
                       }
                    }
                 }
               //--- Смещаем форму на полученные координаты
               if(form.IsMain())
                  form.Move(x,y,true);
              }            
            //--- Если флаг перемещения снят
            else
              {
               //--- Если в mouse_state состояние мышки неопределённое - это отжатие левой кнопки
               //--- Присваиваем переменной новое состояние мышки
               if(mouse_state==MOUSE_FORM_STATE_NONE)
                  mouse_state=MOUSE_FORM_STATE_INSIDE_ACTIVE_AREA_RELEASED;
               //--- Обработаем увод курсора мышки с графического элемента
               this.FormPostProcessing();
              }
           }
        }

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

Теперь нам остаётся в том же обработчике событий OnChartEvent() в блоках обработчиков событий мышки вызвать для объекта обработчик событий мышки с указанием найденного ранее события:

      //--- Если курсор над формой
      else
        {
         //--- Если кнопка всё ещё нажата и удерживается на графике - уходим
         if(pressed_chart)
           {
            return;
           }
         
         //--- Если ещё не установлен флаг удержания кнопки на форме
         if(!pressed_form)
           {
            pressed_chart=false;    // Кнопка не удерживается на графике
            this.SetChartTools(form.ChartID(),false);
            //+---------------------------------------------------------------------------------------------+
            //| Обработчик события Курсор в пределах формы, кнопки мышки не нажаты                          |
            //+---------------------------------------------------------------------------------------------+
            if(mouse_state==MOUSE_FORM_STATE_INSIDE_FORM_NOT_PRESSED)
              {
               form.OnMouseEvent(MOUSE_EVENT_INSIDE_FORM_NOT_PRESSED,lparam,dparam,sparam);
               //--- Если курсор над формой управления опорной точкой расширенного графического объекта
               if(graph_obj_id>WRONG_VALUE)
                 {
                  //--- получаем объект по его идентификатору и идентификатору графика
                  CGStdGraphObj *graph_obj=this.GetStdGraphObjectExt(graph_obj_id,form.ChartID());
                  if(graph_obj!=NULL)
                    {
                     //--- Получаем инструментарий расширенного стандартного графического объекта
                     CGStdGraphObjExtToolkit *toolkit=graph_obj.GetExtToolkit();
                     if(toolkit!=NULL)
                       {
                        //--- Рисуем на форме точку с окружностью, на всех остальных формах - удаляем
                        toolkit.DrawOneControlPoint(form);
                       }
                    }
                 }
              }
            //+---------------------------------------------------------------------------------------------+
            //| Обработчик события Курсор в пределах формы, нажата кнопка мышки (любая)                     |
            //+---------------------------------------------------------------------------------------------+
            if(mouse_state==MOUSE_FORM_STATE_INSIDE_FORM_PRESSED)
              {
               this.SetChartTools(::ChartID(),false);
               //--- Если ещё не установлен флаг удержания формы
               if(!pressed_form)
                 {
                  pressed_form=true;      // ставим флаг нажатия на форме
                  pressed_chart=false;    // снимаем флаг нажатия на графике
                 }
               form.OnMouseEvent(MOUSE_EVENT_INSIDE_FORM_PRESSED,lparam,dparam,sparam);
              }
            //+---------------------------------------------------------------------------------------------+
            //| Обработчик события Курсор в пределах формы, прокручивается колёсико мышки                   |
            //+---------------------------------------------------------------------------------------------+
            if(mouse_state==MOUSE_FORM_STATE_INSIDE_FORM_WHEEL)
              {
               form.OnMouseEvent(MOUSE_EVENT_INSIDE_FORM_WHEEL,lparam,dparam,sparam);
              }
            //+---------------------------------------------------------------------------------------------+
            //| Обработчик события Курсор в пределах активной области, кнопки мышки не нажаты               |
            //+---------------------------------------------------------------------------------------------+
            if(mouse_state==MOUSE_FORM_STATE_INSIDE_ACTIVE_AREA_NOT_PRESSED)
              {
               //--- Установим смещение курсора относительно начальных координат формы
               form.SetOffsetX(this.m_mouse.CoordX()-form.CoordX());
               form.SetOffsetY(this.m_mouse.CoordY()-form.CoordY());
               form.OnMouseEvent(MOUSE_EVENT_INSIDE_ACTIVE_AREA_NOT_PRESSED,lparam,dparam,sparam);
               
               //--- Если курсор над активной зоной формы управления опорной точкой расширенного графического объекта
               if(graph_obj_id>WRONG_VALUE)
                 {
                  //--- получаем объект по его идентификатору и идентификатору графика
                  CGStdGraphObj *graph_obj=this.GetStdGraphObjectExt(graph_obj_id,form.ChartID());
                  if(graph_obj!=NULL)
                    {
                     //--- Получаем инструментарий расширенного стандартного графического объекта
                     CGStdGraphObjExtToolkit *toolkit=graph_obj.GetExtToolkit();
                     if(toolkit!=NULL)
                       {
                        //--- Рисуем на форме точку с окружностью, на всех остальных формах - удаляем
                        toolkit.DrawOneControlPoint(form);
                       }
                    }
                 }
              }
            //+---------------------------------------------------------------------------------------------+
            //| Обработчик события Курсор в пределах активной области, нажата кнопка мышки (любая)          |
            //+---------------------------------------------------------------------------------------------+
            if(mouse_state==MOUSE_FORM_STATE_INSIDE_ACTIVE_AREA_PRESSED && !move)
              {
               pressed_form=true;                                       // флаг удержания кнопки мышки на форме
               //--- Если зажата левая кнопка мышки
               if(this.m_mouse.IsPressedButtonLeft())
                 {
                  //--- Установим флаги и параметры формы
                  move=true;                                            // флаг перемещения
                  form.SetInteraction(true);                            // флаг взаимодействия формы со внешней средой
                  form.BringToTop();                                    // форма на передний план - поверх всех остальных
                  form.SetOffsetX(this.m_mouse.CoordX()-form.CoordX()); // Смещение курсора относительно координаты X
                  form.SetOffsetY(this.m_mouse.CoordY()-form.CoordY()); // Смещение курсора относительно координаты Y
                  this.ResetAllInteractionExeptOne(form);               // Сбросим флаги взаимодействия для всех форм, кроме текущей
                  
                  //--- Получаем максимальный ZOrder
                  long zmax=this.GetZOrderMax();
                  //--- Если максимальный ZOrder получен, при этом ZOrder формы меньше максимального, или максимальный ZOrder всех форм равен нулю
                  if(zmax>WRONG_VALUE && (form.Zorder()<zmax || zmax==0))
                    {
                     //--- Если форма - не контрольная точка управления расширенным стандартным графическим объектом -
                     //--- ставим ZOrder формы выше всех
                     if(form.Type()!=OBJECT_DE_TYPE_GFORM_CONTROL)
                        this.SetZOrderMAX(form);
                    }
                 }
               form.OnMouseEvent(MOUSE_EVENT_INSIDE_ACTIVE_AREA_PRESSED,lparam,dparam,sparam);
              }
            //+---------------------------------------------------------------------------------------------+
            //| Обработчик события Курсор в пределах активной области, прокручивается колёсико мышки        |
            //+---------------------------------------------------------------------------------------------+
            if(mouse_state==MOUSE_FORM_STATE_INSIDE_ACTIVE_AREA_WHEEL)
              {
               form.OnMouseEvent(MOUSE_EVENT_INSIDE_ACTIVE_AREA_WHEEL,lparam,dparam,sparam);
              }
            //+---------------------------------------------------------------------------------------------+
            //| Обработчик события Курсор в пределах активной области, отжата кнопка мышки (левая)          |
            //+---------------------------------------------------------------------------------------------+
            if(mouse_state==MOUSE_FORM_STATE_INSIDE_ACTIVE_AREA_RELEASED)
              {
               form.OnMouseEvent(MOUSE_EVENT_INSIDE_ACTIVE_AREA_RELEASED,lparam,dparam,sparam);
              }
            //+---------------------------------------------------------------------------------------------+
            //| Обработчик события Курсор в пределах области прокрутки окна, кнопки мышки не нажаты         |
            //+---------------------------------------------------------------------------------------------+
            if(mouse_state==MOUSE_FORM_STATE_INSIDE_SCROLL_AREA_NOT_PRESSED)
              {
               form.OnMouseEvent(MOUSE_EVENT_INSIDE_SCROLL_AREA_NOT_PRESSED,lparam,dparam,sparam);
              }
            //+---------------------------------------------------------------------------------------------+
            //| Обработчик события Курсор в пределах области прокрутки окна, нажата кнопка мышки (любая)    |
            //+---------------------------------------------------------------------------------------------+
            if(mouse_state==MOUSE_FORM_STATE_INSIDE_SCROLL_AREA_PRESSED)
              {
               form.OnMouseEvent(MOUSE_EVENT_INSIDE_SCROLL_AREA_PRESSED,lparam,dparam,sparam);
              }
            //+---------------------------------------------------------------------------------------------+
            //| Обработчик события Курсор в пределах области прокрутки окна, прокручивается колёсико мышки  |
            //+---------------------------------------------------------------------------------------------+
            if(mouse_state==MOUSE_FORM_STATE_INSIDE_SCROLL_AREA_WHEEL)
              {
               form.OnMouseEvent(MOUSE_EVENT_INSIDE_SCROLL_AREA_WHEEL,lparam,dparam,sparam);
              }
           }
        }
     }
  }

Тут же у нас добавлен новый обработчик для обработки события отпускания кнопки мышки.


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

//+------------------------------------------------------------------+
//| Устанавливает для указанного графика флаги прокрутки чарта       |
//| колёсиком мышки,  контекстного меню и инструмента "перекрестие"  |
//+------------------------------------------------------------------+
void CGraphElementsCollection::SetChartTools(const long chart_id,const bool flag)
  {
   if(::ChartGetInteger(chart_id,CHART_MOUSE_SCROLL)==flag)
      return;
   ::ChartSetInteger(chart_id,CHART_MOUSE_SCROLL,flag);
   ::ChartSetInteger(chart_id,CHART_CONTEXT_MENU,flag);
   ::ChartSetInteger(chart_id,CHART_CROSSHAIR_TOOL,flag);
  }
//+------------------------------------------------------------------+


На сегодня это все доработки классов библиотеки.


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

Для теста возьмём советник из прошлой статьи и сохраним его в новой папке \MQL5\Experts\TestDoEasy\Part110\ под новым именем TstDE110.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_SIMPLE;    // Label border style
sinput   ENUM_ANCHOR_POINT             InpTextAlign         =  ANCHOR_CENTER;          // Label text align
sinput   ENUM_INPUT_YES_NO             InpTextAutoSize      =  INPUT_NO;               // Label autosize
sinput   ENUM_ANCHOR_POINT             InpCheckAlign        =  ANCHOR_LEFT;            // Check flag align
sinput   ENUM_ANCHOR_POINT             InpCheckTextAlign    =  ANCHOR_LEFT;            // Check label text align
sinput   ENUM_CHEK_STATE               InpCheckState        =  CHEK_STATE_UNCHECKED;   // Check flag state
sinput   ENUM_INPUT_YES_NO             InpCheckAutoSize     =  INPUT_YES;              // CheckBox autosize
sinput   ENUM_BORDER_STYLE             InpCheckFrameStyle   =  BORDER_STYLE_NONE;      // CheckBox border style
sinput   ENUM_ANCHOR_POINT             InpButtonTextAlign   =  ANCHOR_CENTER;          // Button text align
sinput   ENUM_INPUT_YES_NO             InpButtonAutoSize    =  INPUT_YES;              // Button autosize
sinput   ENUM_AUTO_SIZE_MODE           InpButtonAutoSizeMode=  AUTO_SIZE_MODE_GROW;    // Button Autosize mode
sinput   ENUM_BORDER_STYLE             InpButtonFrameStyle  =  BORDER_STYLE_NONE;      // Button border style
sinput   bool                          InpButtonToggle      =  false;                  // Button toggle flag
//--- global variables

В обработчике OnInit() теперь будет такой код:

//+------------------------------------------------------------------+
//| 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.SetBorderSizeAll(3);
            obj.SetBorderStyle(FRAME_STYLE_BEVEL);
            obj.SetBackgroundColor(obj.ChangeColorLightness(obj.BackgroundColor(),4*i),true);
            obj.SetForeColor(clrRed,true);
            //--- Рассчитаем ширину и высоту будущего объекта-текстовой метки
            int w=obj.Width()-obj.BorderSizeLeft()-obj.BorderSizeRight();
            int h=obj.Height()-obj.BorderSizeTop()-obj.BorderSizeBottom();
            //--- Создадим объект-текстовую метку
            obj.CreateNewElement(GRAPH_ELEMENT_TYPE_WF_LABEL,0,0,w,h,clrNONE,255,false,false);
            //--- Получаем указатель на вновь созданный объект
            CLabel *lbl=obj.GetElement(0);
            if(lbl!=NULL)
              {
               //--- Если объект имеет чётный, или нулевой индекс в списке - зададим ему цвет текста по умолчанию
               if(i % 2==0)
                  lbl.SetForeColor(CLR_DEF_FORE_COLOR,true);
               //--- Если индекс объекта в списке нечётный - зададим объекту непрозрачность 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_BORDER_COLOR,true);
               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 по его индексу в списке прикреплённых объектов с типом GroupBox
         gbox=pnl.GetElementByType(GRAPH_ELEMENT_TYPE_WF_GROUPBOX,0);
         if(gbox!=NULL)
           {
            //--- установим тип рамки "вдавленная рамка", цвет рамки как цвет фона основной панели,
            //--- а цвет текста - затемнённый на 1 цвет фона последней прикреплённой панели
            gbox.SetBorderStyle(FRAME_STYLE_STAMP);
            gbox.SetBorderColor(pnl.BackgroundColor(),true);
            gbox.SetForeColor(gbox.ChangeColorLightness(obj.BackgroundColor(),-1),true);
            //--- Создадим объект CheckBox
            gbox.CreateNewElement(GRAPH_ELEMENT_TYPE_WF_CHECKBOX,2,10,50,20,clrNONE,255,true,false);
            //--- получим указатель на объект CheckBox по его индексу в списке прикреплённых объектов с типом CheckBox
            CCheckBox *cbox=gbox.GetElementByType(GRAPH_ELEMENT_TYPE_WF_CHECKBOX,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_BORDER_COLOR,true);
               cbox.SetChecked(true);
               cbox.SetCheckState((ENUM_CANV_ELEMENT_CHEK_STATE)InpCheckState);
              }
            //--- Создадим объект RadioButton
            gbox.CreateNewElement(GRAPH_ELEMENT_TYPE_WF_RADIOBUTTON,2,cbox.BottomEdgeRelative()+4,50,20,clrNONE,255,true,false);
            //--- получим указатель на объект RadioButton по его индексу в списке прикреплённых объектов с типом RadioButton
            CRadioButton *rbtn=gbox.GetElementByType(GRAPH_ELEMENT_TYPE_WF_RADIOBUTTON,0);
            //--- Если 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_BORDER_COLOR,true);
               rbtn.SetChecked(true);
              }
            //--- Создадим объект Button
            gbox.CreateNewElement(GRAPH_ELEMENT_TYPE_WF_BUTTON,(int)fmax(rbtn.RightEdgeRelative(),cbox.RightEdgeRelative())+10,14,60,36,clrNONE,255,true,false);
            //--- получим указатель на объект Button по его индексу в списке прикреплённых объектов с типом Button
            CButton *butt=gbox.GetElementByType(GRAPH_ELEMENT_TYPE_WF_BUTTON,0);
            //--- Если Button создан и указатель на него получен
            if(butt!=NULL)
              {
               //--- Установим параметры Button из входных параметров советника
               butt.SetAutoSize((bool)InpButtonAutoSize,false);
               butt.SetAutoSizeMode((ENUM_CANV_ELEMENT_AUTO_SIZE_MODE)InpButtonAutoSizeMode,false);
               butt.SetTextAlign(InpButtonTextAlign);
               //--- Зададим цвет текста, стиль и цвет рамки
               butt.SetForeColor(butt.ChangeColorLightness(CLR_DEF_FORE_COLOR,2),true);

               butt.SetBorderStyle((ENUM_FRAME_STYLE)InpButtonFrameStyle);
               butt.SetBorderColor(butt.ChangeColorLightness(butt.BackgroundColor(),-10),true);
               //--- Установим режим toggle в зависимости от настроек
               butt.SetToggleFlag(InpButtonToggle);
               //--- Зададим выводимый текст на кнопке в зависимости от флага toggle
               if(butt.Toggle())
                  butt.SetText("Toggle-Button");
               else
                  butt.SetText("Button");
              }
           }
        }
      //--- Перерисуем все объекты в порядке их иерархии
      pnl.Redraw(true);
     }
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+

По сути, здесь всё остаётся как было, но изменены наименования методов, переименованных в библиотеке, в методах установки цветов теперь передаются флаги (true) для сохранения изначального цвета, а при выборе того или иного режима кнопки, название этого режима выводится текстом на кнопку.
Ознакомиться со всеми изменениями советника можно в прилагаемых к статье файлах.

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


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


Что дальше

В следующей статье продолжим работу над интерактивностью 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
DoEasy. Элементы управления (Часть 9): Реорганизация методов WinForms-объектов, элементы управления "RadioButton" и "Button"



Прикрепленные файлы |
MQL5.zip (4469.13 KB)
Разработка торгового советника с нуля (Часть 17): Доступ к данным в Интернете (III) Разработка торгового советника с нуля (Часть 17): Доступ к данным в Интернете (III)
В этой статье мы продолжим с просмотром того, как получать данные из Интернета для их использования в советнике. Давайте приступим к работе, а точнее к кодированию альтернативной системы.
Модель движения цены и ее основные положения (Часть 2):  Уравнение эволюции вероятностного поля цены и возникновение наблюдаемого случайного блуждания Модель движения цены и ее основные положения (Часть 2): Уравнение эволюции вероятностного поля цены и возникновение наблюдаемого случайного блуждания
Выведено уравнение эволюции вероятностного поля цены, найден критерий приближения ценового скачка, раскрыты суть ценовых значений на графиках котировок и механизм возникновения случайного блуждания этих значений.
Разработка торговой системы на основе Стохастика Разработка торговой системы на основе Стохастика
Это очередная статья из обучающей серии, в которой мы знакомимся с различными индикаторами. В этот раз мы обратимся к другому популярному индикатору — Stochastic Oscillator. Изучим его, рассмотрим стратегии на его основе и создадим торговую систему.
Разработка торгового советника с нуля (Часть 16): Доступ к данным в Интернете (II) Разработка торгового советника с нуля (Часть 16): Доступ к данным в Интернете (II)
Знание того, как вводить данные из Web в советник, не так очевидно, вернее, не так просто, чтобы это можно было сделать без понимания всех возможностей, которые есть в MetaTrader 5.