preview
DoEasy. Элементы управления (Часть 30): Оживляем элемент управления "ScrollBar"

DoEasy. Элементы управления (Часть 30): Оживляем элемент управления "ScrollBar"

MetaTrader 5Примеры | 16 декабря 2022, 17:42
712 0
Artyom Trishkin
Artyom Trishkin

Содержание


Концепция

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

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


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

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

#define CLR_DEF_CONTROL_SCROLL_BAR_TRACK_BACK_COLOR   (C'0xF0,0xF0,0xF0')  // Цвет фона элемента управления ScrollBar
#define CLR_DEF_CONTROL_SCROLL_BAR_TRACK_BORDER_COLOR (C'0xFF,0xFF,0xFF')  // Цвет рамки элемента управления ScrollBar
#define CLR_DEF_CONTROL_SCROLL_BAR_TRACK_FORE_COLOR   (C'0x60,0x60,0x60')  // Цвет текста элемента управления ScrollBar
#define CLR_DEF_CONTROL_SCROLL_BAR_TRACK_FORE_MOUSE_DOWN (C'0x00,0x00,0x00')// Цвет текста элемента управления ScrollBar при нажатии мышки на элемент управления
#define CLR_DEF_CONTROL_SCROLL_BAR_TRACK_FORE_MOUSE_OVER (C'0x00,0x00,0x00')// Цвет текста элемента управления ScrollBar при наведении мышки на элемент управления

#define CLR_DEF_CONTROL_SCROLL_BAR_THUMB_COLOR        (C'0xCD,0xCD,0xCD')  // Цвет области захвата элемента управления ScrollBar
#define CLR_DEF_CONTROL_SCROLL_BAR_THUMB_BORDER_COLOR (C'0xCD,0xCD,0xCD')  // Цвет рамки области захвата элемента управления ScrollBar
#define CLR_DEF_CONTROL_SCROLL_BAR_THUMB_MOUSE_DOWN   (C'0x60,0x60,0x60')  // Цвет области захвата элемента управления ScrollBar при нажатии мышки на элемент управления
#define CLR_DEF_CONTROL_SCROLL_BAR_THUMB_MOUSE_OVER   (C'0xA6,0xA6,0xA6')  // Цвет области захвата элемента управления ScrollBar при наведении мышки на элемент управления
#define CLR_DEF_CONTROL_SCROLL_BAR_THUMB_FORE_COLOR   (C'0x60,0x60,0x60')  // Цвет текста области захвата элемента управления ScrollBar
#define CLR_DEF_CONTROL_SCROLL_BAR_THUMB_FORE_MOUSE_DOWN (C'0xFF,0xFF,0xFF')// Цвет текста области захвата элемента управления ScrollBar при нажатии мышки на элемент управления
#define CLR_DEF_CONTROL_SCROLL_BAR_THUMB_FORE_MOUSE_OVER (C'0x00,0x00,0x00')// Цвет текста области захвата элемента управления ScrollBar при наведении мышки на элемент управления

#define CLR_DEF_CONTROL_SCROLL_BAR_BUTT_COLOR         (C'0xF0,0xF0,0xF0')  // Цвет кнопок элемента управления ScrollBar
#define CLR_DEF_CONTROL_SCROLL_BAR_BUTT_BORDER_COLOR  (C'0xCD,0xCD,0xCD')  // Цвет рамки кнопок элемента управления ScrollBar
#define CLR_DEF_CONTROL_SCROLL_BAR_BUTT_MOUSE_DOWN    (C'0x60,0x60,0x60')  // Цвет кнопок элемента управления ScrollBar при нажатии мышки на элемент управления
#define CLR_DEF_CONTROL_SCROLL_BAR_BUTT_MOUSE_OVER    (C'0xDA,0xDA,0xDA')  // Цвет кнопок элемента управления ScrollBar при наведении мышки на элемент управления
#define CLR_DEF_CONTROL_SCROLL_BAR_BUTT_FORE_COLOR    (C'0x60,0x60,0x60')  // Цвет текста кнопок элемента управления ScrollBar
#define CLR_DEF_CONTROL_SCROLL_BAR_BUTT_FORE_MOUSE_DOWN (C'0xFF,0xFF,0xFF')// Цвет текста кнопок элемента управления ScrollBar при нажатии мышки на элемент управления
#define CLR_DEF_CONTROL_SCROLL_BAR_BUTT_FORE_MOUSE_OVER (C'0x00,0x00,0x00')// Цвет текста кнопок элемента управления ScrollBar при наведении мышки на элемент управления

#define DEF_CONTROL_SCROLL_BAR_WIDTH                  (11)                 // Ширина элемента управления ScrollBar по умолчанию
#define DEF_CONTROL_CORNER_AREA                       (4)                  // Количество пикселей, определяющих область угла для изменения размеров
#define DEF_CONTROL_LIST_MARGIN_X                     (1)                  // Зазор между столбцами в элементах управления ListBox
#define DEF_CONTROL_LIST_MARGIN_Y                     (0)                  // Зазор между строками в элементах управления ListBox

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

В список возможных состояний мышки относительно формы добавим новые значения. Размеры объекта можно изменять в восьми направлениях:

  1. При нахождении курсора на верхней грани объекта — можно изменять его высоту смещением грани вверх,
  2. При нахождении курсора на нижней грани объекта — можно изменять его высоту смещением грани вниз,
  3. При нахождении курсора на левой грани объекта — можно изменять его ширину смещением грани влево,
  4. При нахождении курсора на правой грани объекта — можно изменять его высоту смещением грани вправо,
  5. При нахождении курсора в области левого верхнего угла объекта — можно изменять его высоту и ширину смещением вверх и влево,
  6. При нахождении курсора в области правого верхнего угла объекта — можно изменять его высоту и ширину смещением вверх и вправо,
  7. При нахождении курсора в области левого нижнего угла объекта — можно изменять его высоту и ширину смещением вниз и влево,
  8. При нахождении курсора в области правого нижнего угла объекта — можно изменять его высоту и ширину смещением вниз и вправо.

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

//+------------------------------------------------------------------+
//| Список возможных состояний мышки относительно формы              |
//+------------------------------------------------------------------+
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_RIGHT_NOT_PRESSED,// Курсор в пределах области прокрутки окна справа, кнопки мышки не нажаты
   MOUSE_FORM_STATE_INSIDE_SCROLL_AREA_RIGHT_PRESSED,    // Курсор в пределах области прокрутки окна справа, нажата кнопка мышки (любая)
   MOUSE_FORM_STATE_INSIDE_SCROLL_AREA_RIGHT_WHEEL,      // Курсор в пределах области прокрутки окна справа, прокручивается колёсико мышки
//--- В пределах области прокрутки окна снизу
   MOUSE_FORM_STATE_INSIDE_SCROLL_AREA_BOTTOM_NOT_PRESSED,// Курсор в пределах области прокрутки окна снизу, кнопки мышки не нажаты
   MOUSE_FORM_STATE_INSIDE_SCROLL_AREA_BOTTOM_PRESSED,   // Курсор в пределах области прокрутки окна снизу, нажата кнопка мышки (любая)
   MOUSE_FORM_STATE_INSIDE_SCROLL_AREA_BOTTOM_WHEEL,     // Курсор в пределах области прокрутки окна снизу, прокручивается колёсико мышки

//--- В пределах области изменения размеров окна сверху
   MOUSE_FORM_STATE_INSIDE_RESIZE_TOP_AREA_NOT_PRESSED,  // Курсор в пределах области изменения размеров окна сверху, кнопки мышки не нажаты
   MOUSE_FORM_STATE_INSIDE_RESIZE_TOP_AREA_PRESSED,      // Курсор в пределах области изменения размеров окна сверху, нажата кнопка мышки (любая)
   MOUSE_FORM_STATE_INSIDE_RESIZE_TOP_AREA_WHEEL,        // Курсор в пределах области изменения размеров окна сверху, прокручивается колёсико мышки
//--- В пределах области изменения размеров окна снизу
   MOUSE_FORM_STATE_INSIDE_RESIZE_BOTTOM_AREA_NOT_PRESSED,// Курсор в пределах области изменения размеров окна снизу, кнопки мышки не нажаты
   MOUSE_FORM_STATE_INSIDE_RESIZE_BOTTOM_AREA_PRESSED,   // Курсор в пределах области изменения размеров окна снизу, нажата кнопка мышки (любая)
   MOUSE_FORM_STATE_INSIDE_RESIZE_BOTTOM_AREA_WHEEL,     // Курсор в пределах области изменения размеров окна снизу, прокручивается колёсико мышки
//--- В пределах области изменения размеров окна слева
   MOUSE_FORM_STATE_INSIDE_RESIZE_LEFT_AREA_NOT_PRESSED, // Курсор в пределах области изменения размеров окна слева, кнопки мышки не нажаты
   MOUSE_FORM_STATE_INSIDE_RESIZE_LEFT_AREA_PRESSED,     // Курсор в пределах области изменения размеров окна слева, нажата кнопка мышки (любая)
   MOUSE_FORM_STATE_INSIDE_RESIZE_LEFT_AREA_WHEEL,       // Курсор в пределах области изменения размеров окна слева, прокручивается колёсико мышки
//--- В пределах области изменения размеров окна справа
   MOUSE_FORM_STATE_INSIDE_RESIZE_RIGHT_AREA_NOT_PRESSED,// Курсор в пределах области изменения размеров окна справа, кнопки мышки не нажаты
   MOUSE_FORM_STATE_INSIDE_RESIZE_RIGHT_AREA_PRESSED,    // Курсор в пределах области изменения размеров окна справа, нажата кнопка мышки (любая)
   MOUSE_FORM_STATE_INSIDE_RESIZE_RIGHT_AREA_WHEEL,      // Курсор в пределах области изменения размеров окна справа, прокручивается колёсико мышки
//--- В пределах области изменения размеров окна сверху-слева
   MOUSE_FORM_STATE_INSIDE_RESIZE_TOP_LEFT_AREA_NOT_PRESSED,   // Курсор в пределах области изменения размеров окна сверху-слева, кнопки мышки не нажаты
   MOUSE_FORM_STATE_INSIDE_RESIZE_TOP_LEFT_AREA_PRESSED,       // Курсор в пределах области изменения размеров окна сверху-слева, нажата кнопка мышки (любая)
   MOUSE_FORM_STATE_INSIDE_RESIZE_TOP_LEFT_AREA_WHEEL,         // Курсор в пределах области изменения размеров окна сверху-слева, прокручивается колёсико мышки
//--- В пределах области изменения размеров окна сверху-справа
   MOUSE_FORM_STATE_INSIDE_RESIZE_TOP_RIGHT_AREA_NOT_PRESSED,  // Курсор в пределах области изменения размеров окна сверху-справа, кнопки мышки не нажаты
   MOUSE_FORM_STATE_INSIDE_RESIZE_TOP_RIGHT_AREA_PRESSED,      // Курсор в пределах области изменения размеров окна сверху-справа, нажата кнопка мышки (любая)
   MOUSE_FORM_STATE_INSIDE_RESIZE_TOP_RIGHT_AREA_WHEEL,        // Курсор в пределах области изменения размеров окна сверху-справа, прокручивается колёсико мышки
//--- В пределах области изменения размеров окна снизу-слева
   MOUSE_FORM_STATE_INSIDE_RESIZE_BOTTOM_LEFT_AREA_NOT_PRESSED,// Курсор в пределах области изменения размеров окна снизу-слева, кнопки мышки не нажаты
   MOUSE_FORM_STATE_INSIDE_RESIZE_BOTTOM_LEFT_AREA_PRESSED,    // Курсор в пределах области изменения размеров окна снизу-слева, нажата кнопка мышки (любая)
   MOUSE_FORM_STATE_INSIDE_RESIZE_BOTTOM_LEFT_AREA_WHEEL,      // Курсор в пределах области изменения размеров окна снизу-слева, прокручивается колёсико мышки
//--- В пределах области изменения размеров окна снизу-справа
   MOUSE_FORM_STATE_INSIDE_RESIZE_BOTTOM_RIGHT_AREA_NOT_PRESSED,// Курсор в пределах области изменения размеров окна снизу-справа, кнопки мышки не нажаты
   MOUSE_FORM_STATE_INSIDE_RESIZE_BOTTOM_RIGHT_AREA_PRESSED,   // Курсор в пределах области изменения размеров окна снизу-справа, нажата кнопка мышки (любая)
   MOUSE_FORM_STATE_INSIDE_RESIZE_BOTTOM_RIGHT_AREA_WHEEL,     // Курсор в пределах области изменения размеров окна снизу-справа, прокручивается колёсико мышки
   
//--- В пределах области управления
   MOUSE_FORM_STATE_INSIDE_CONTROL_AREA_NOT_PRESSED,  // Курсор в пределах области управления, кнопки мышки не нажаты
   MOUSE_FORM_STATE_INSIDE_CONTROL_AREA_PRESSED,      // Курсор в пределах области управления, нажата кнопка мышки (любая)
   MOUSE_FORM_STATE_INSIDE_CONTROL_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_RIGHT_NOT_PRESSED,  // Курсор в пределах области прокрутки окна справа, кнопки мышки не нажаты
   MOUSE_EVENT_INSIDE_SCROLL_AREA_RIGHT_PRESSED,      // Курсор в пределах области прокрутки окна справа, нажата кнопка мышки (любая)
   MOUSE_EVENT_INSIDE_SCROLL_AREA_RIGHT_WHEEL,        // Курсор в пределах области прокрутки окна справа, прокручивается колёсико мышки
//--- В пределах области прокрутки окна снизу
   MOUSE_EVENT_INSIDE_SCROLL_AREA_BOTTOM_NOT_PRESSED, // Курсор в пределах области прокрутки окна снизу, кнопки мышки не нажаты
   MOUSE_EVENT_INSIDE_SCROLL_AREA_BOTTOM_PRESSED,     // Курсор в пределах области прокрутки окна снизу, нажата кнопка мышки (любая)
   MOUSE_EVENT_INSIDE_SCROLL_AREA_BOTTOM_WHEEL,       // Курсор в пределах области прокрутки окна снизу, прокручивается колёсико мышки

//--- В пределах области изменения размеров окна сверху
   MOUSE_EVENT_INSIDE_RESIZE_TOP_AREA_NOT_PRESSED,    // Курсор в пределах области изменения размеров окна сверху, кнопки мышки не нажаты
   MOUSE_EVENT_INSIDE_RESIZE_TOP_AREA_PRESSED,        // Курсор в пределах области изменения размеров окна сверху, нажата кнопка мышки (любая)
   MOUSE_EVENT_INSIDE_RESIZE_TOP_AREA_WHEEL,          // Курсор в пределах области изменения размеров окна сверху, прокручивается колёсико мышки
//--- В пределах области изменения размеров окна снизу
   MOUSE_EVENT_INSIDE_RESIZE_BOTTOM_AREA_NOT_PRESSED, // Курсор в пределах области изменения размеров окна снизу, кнопки мышки не нажаты
   MOUSE_EVENT_INSIDE_RESIZE_BOTTOM_AREA_PRESSED,     // Курсор в пределах области изменения размеров окна снизу, нажата кнопка мышки (любая)
   MOUSE_EVENT_INSIDE_RESIZE_BOTTOM_AREA_WHEEL,       // Курсор в пределах области изменения размеров окна снизу, прокручивается колёсико мышки
//--- В пределах области изменения размеров окна слева
   MOUSE_EVENT_INSIDE_RESIZE_LEFT_AREA_NOT_PRESSED,   // Курсор в пределах области изменения размеров окна слева, кнопки мышки не нажаты
   MOUSE_EVENT_INSIDE_RESIZE_LEFT_AREA_PRESSED,       // Курсор в пределах области изменения размеров окна слева, нажата кнопка мышки (любая)
   MOUSE_EVENT_INSIDE_RESIZE_LEFT_AREA_WHEEL,         // Курсор в пределах области изменения размеров окна слева, прокручивается колёсико мышки
//--- В пределах области изменения размеров окна справа
   MOUSE_EVENT_INSIDE_RESIZE_RIGHT_AREA_NOT_PRESSED,  // Курсор в пределах области изменения размеров окна справа, кнопки мышки не нажаты
   MOUSE_EVENT_INSIDE_RESIZE_RIGHT_AREA_PRESSED,      // Курсор в пределах области изменения размеров окна справа, нажата кнопка мышки (любая)
   MOUSE_EVENT_INSIDE_RESIZE_RIGHT_AREA_WHEEL,        // Курсор в пределах области изменения размеров окна справа, прокручивается колёсико мышки
//--- В пределах области изменения размеров окна сверху-слева
   MOUSE_EVENT_INSIDE_RESIZE_TOP_LEFT_AREA_NOT_PRESSED,// Курсор в пределах области изменения размеров окна сверху-слева, кнопки мышки не нажаты
   MOUSE_EVENT_INSIDE_RESIZE_TOP_LEFT_AREA_PRESSED,   // Курсор в пределах области изменения размеров окна сверху-слева, нажата кнопка мышки (любая)
   MOUSE_EVENT_INSIDE_RESIZE_TOP_LEFT_AREA_WHEEL,     // Курсор в пределах области изменения размеров окна сверху-слева, прокручивается колёсико мышки
//--- В пределах области изменения размеров окна сверху-справа
   MOUSE_EVENT_INSIDE_RESIZE_TOP_RIGHT_AREA_NOT_PRESSED,// Курсор в пределах области изменения размеров окна сверху-справа, кнопки мышки не нажаты
   MOUSE_EVENT_INSIDE_RESIZE_TOP_RIGHT_AREA_PRESSED,  // Курсор в пределах области изменения размеров окна сверху-справа, нажата кнопка мышки (любая)
   MOUSE_EVENT_INSIDE_RESIZE_TOP_RIGHT_AREA_WHEEL,    // Курсор в пределах области изменения размеров окна сверху-справа, прокручивается колёсико мышки
//--- В пределах области изменения размеров окна снизу-слева
   MOUSE_EVENT_INSIDE_RESIZE_BOTTOM_LEFT_AREA_NOT_PRESSED,// Курсор в пределах области изменения размеров окна снизу-слева, кнопки мышки не нажаты
   MOUSE_EVENT_INSIDE_RESIZE_BOTTOM_LEFT_AREA_PRESSED,// Курсор в пределах области изменения размеров окна снизу-слева, нажата кнопка мышки (любая)
   MOUSE_EVENT_INSIDE_RESIZE_BOTTOM_LEFT_AREA_WHEEL,  // Курсор в пределах области изменения размеров окна снизу-слева, прокручивается колёсико мышки
//--- В пределах области изменения размеров окна снизу-справа
   MOUSE_EVENT_INSIDE_RESIZE_BOTTOM_RIGHT_AREA_NOT_PRESSED,// Курсор в пределах области изменения размеров окна снизу-справа, кнопки мышки не нажаты
   MOUSE_EVENT_INSIDE_RESIZE_BOTTOM_RIGHT_AREA_PRESSED,// Курсор в пределах области изменения размеров окна снизу-справа, нажата кнопка мышки (любая)
   MOUSE_EVENT_INSIDE_RESIZE_BOTTOM_RIGHT_AREA_WHEEL, // Курсор в пределах области изменения размеров окна снизу-справа, прокручивается колёсико мышки
//--- В пределах области управления
   MOUSE_EVENT_INSIDE_CONTROL_AREA_NOT_PRESSED,       // Курсор в пределах области управления, кнопки мышки не нажаты
   MOUSE_EVENT_INSIDE_CONTROL_AREA_PRESSED,           // Курсор в пределах области управления, нажата кнопка мышки (любая)
   MOUSE_EVENT_INSIDE_CONTROL_AREA_WHEEL,             // Курсор в пределах области управления, прокручивается колёсико мышки
  };
#define MOUSE_EVENT_NEXT_CODE  (MOUSE_EVENT_INSIDE_CONTROL_AREA_WHEEL+1)   // Код следующего события после последнего кода события мышки
//+------------------------------------------------------------------+

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

//+------------------------------------------------------------------+
//| Список типов графических элементов                               |
//+------------------------------------------------------------------+
enum ENUM_GRAPH_ELEMENT_TYPE
  {
   GRAPH_ELEMENT_TYPE_STANDARD,                       // Стандартный графический объект
   GRAPH_ELEMENT_TYPE_STANDARD_EXTENDED,              // Расширенный стандартный графический объект
   GRAPH_ELEMENT_TYPE_SHADOW_OBJ,                     // Объект тени
   GRAPH_ELEMENT_TYPE_ELEMENT,                        // Элемент
   GRAPH_ELEMENT_TYPE_FORM,                           // Форма
   GRAPH_ELEMENT_TYPE_WINDOW,                         // Окно
   //--- WinForms
   GRAPH_ELEMENT_TYPE_WF_UNDERLAY,                    // Подложка объекта-панели
   GRAPH_ELEMENT_TYPE_WF_BASE,                        // Windows Forms Base
   //--- Ниже нужно вписывать типы объектов "контейнер"
   GRAPH_ELEMENT_TYPE_WF_CONTAINER,                   // Windows Forms базовый объект-контейнер
   GRAPH_ELEMENT_TYPE_WF_PANEL,                       // Windows Forms Panel
   GRAPH_ELEMENT_TYPE_WF_GROUPBOX,                    // Windows Forms GroupBox
   GRAPH_ELEMENT_TYPE_WF_TAB_CONTROL,                 // Windows Forms TabControl
   GRAPH_ELEMENT_TYPE_WF_SPLIT_CONTAINER,             // Windows Forms SplitContainer
   //--- Ниже нужно вписывать типы объектов "стандартный элемент управления"
   GRAPH_ELEMENT_TYPE_WF_COMMON_BASE,                 // Windows Forms базовый стандартный элемент управления
   GRAPH_ELEMENT_TYPE_WF_LABEL,                       // Windows Forms Label
   GRAPH_ELEMENT_TYPE_WF_BUTTON,                      // Windows Forms Button
   GRAPH_ELEMENT_TYPE_WF_CHECKBOX,                    // Windows Forms CheckBox
   GRAPH_ELEMENT_TYPE_WF_RADIOBUTTON,                 // Windows Forms RadioButton
   GRAPH_ELEMENT_TYPE_WF_ELEMENTS_LIST_BOX,           // Базовый объект-список Windows Forms элементов
   GRAPH_ELEMENT_TYPE_WF_LIST_BOX,                    // Windows Forms ListBox
   GRAPH_ELEMENT_TYPE_WF_CHECKED_LIST_BOX,            // Windows Forms CheckedListBox
   GRAPH_ELEMENT_TYPE_WF_BUTTON_LIST_BOX,             // Windows Forms ButtonListBox
   GRAPH_ELEMENT_TYPE_WF_TOOLTIP,                     // Windows Forms ToolTip
   GRAPH_ELEMENT_TYPE_WF_PROGRESS_BAR,                // Windows Forms ProgressBar
   //--- Вспомогательные элементы WinForms-объектов
   GRAPH_ELEMENT_TYPE_WF_LIST_BOX_ITEM,               // Windows Forms ListBoxItem
   GRAPH_ELEMENT_TYPE_WF_TAB_HEADER,                  // Windows Forms TabHeader
   GRAPH_ELEMENT_TYPE_WF_TAB_FIELD,                   // Windows Forms TabField
   GRAPH_ELEMENT_TYPE_WF_SPLIT_CONTAINER_PANEL,       // Windows Forms SplitContainerPanel
   GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON,                // Windows Forms ArrowButton
   GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_UP,             // Windows Forms UpArrowButton
   GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_DOWN,           // Windows Forms DownArrowButton
   GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_LEFT,           // Windows Forms LeftArrowButton
   GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_RIGHT,          // Windows Forms RightArrowButton
   GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTONS_UD_BOX,        // Windows Forms UpDownArrowButtonsBox
   GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTONS_LR_BOX,        // Windows Forms LeftRightArrowButtonsBox
   GRAPH_ELEMENT_TYPE_WF_SPLITTER,                    // Windows Forms Splitter
   GRAPH_ELEMENT_TYPE_WF_HINT_BASE,                   // Windows Forms HintBase
   GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_LEFT,              // Windows Forms HintMoveLeft
   GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_RIGHT,             // Windows Forms HintMoveRight
   GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_UP,                // Windows Forms HintMoveUp
   GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_DOWN,              // Windows Forms HintMoveDown
   GRAPH_ELEMENT_TYPE_WF_BAR_PROGRESS_BAR,            // Windows Forms BarProgressBar
   GRAPH_ELEMENT_TYPE_WF_GLARE_OBJ,                   // Объект блика
   GRAPH_ELEMENT_TYPE_WF_SCROLL_BAR_THUMB,            // Windows Forms ScrollBarThumb
   GRAPH_ELEMENT_TYPE_WF_SCROLL_BAR,                  // Windows Forms ScrollBar
   GRAPH_ELEMENT_TYPE_WF_SCROLL_BAR_HORISONTAL,       // Windows Forms ScrollBarHorisontal
   GRAPH_ELEMENT_TYPE_WF_SCROLL_BAR_VERTICAL,         // Windows Forms ScrollBarVertical
  };
//+------------------------------------------------------------------+

Это будет новый вспомогательный элемент управления "Область захвата" (ползунок полосы прокрутки).

В перечисление целочисленных свойств графического элемента на канвасе добавим два новых свойства и увеличим их общее количество со 138 до 140:

//+------------------------------------------------------------------+
//| Целочисленные свойства графического элемента на канвасе          |
//+------------------------------------------------------------------+
enum ENUM_CANV_ELEMENT_PROP_INTEGER
  {
   CANV_ELEMENT_PROP_ID = 0,                          // Идентификатор элемента
   CANV_ELEMENT_PROP_TYPE,                            // Тип графического элемента

   //---...
   //---...

   CANV_ELEMENT_PROP_ZORDER,                          // Приоритет графического объекта на получение события нажатия мышки на графике
   CANV_ELEMENT_PROP_ENABLED,                         // Флаг доступности элемента
   CANV_ELEMENT_PROP_RESIZABLE,                       // Флаг изменяемого размера элемента
   CANV_ELEMENT_PROP_FORE_COLOR,                      // Цвет текста по умолчанию для всех объектов элемента управления
   CANV_ELEMENT_PROP_FORE_COLOR_OPACITY,              // Непрозрачность цвета текста по умолчанию для всех объектов элемента управления

   //---...
   //---...

   CANV_ELEMENT_PROP_PROGRESS_BAR_VALUE,              // Текущее значение элемента ProgressBar в диапазоне от Min до Max
   CANV_ELEMENT_PROP_PROGRESS_BAR_MARQUEE_ANIM_SPEED, // Скорость анимации полосы прогресса при стиле Marquee
   CANV_ELEMENT_PROP_BUTTON_ARROW_SIZE,               // Размер стрелки, рисуемой на кнопке
  };
#define CANV_ELEMENT_PROP_INTEGER_TOTAL (140)         // Общее количество целочисленных свойств
#define CANV_ELEMENT_PROP_INTEGER_SKIP  (0)           // Количество неиспользуемых в сортировке целочисленных свойств
//+------------------------------------------------------------------+

У каждого из графических элементов будет флаг, указывающий на возможность изменения его размеров при помощи мышки. А стрелки, рисуемые на элементе (например, кнопка со стрелкой) будут иметь относительные размеры, указываемые этим праметром. Например, стрелка с размером 1 будет рисоваться от центральной точки с отступом вершин в 1 пиксель. Стрелка с размером 2 будет рисоваться от центральной точки с отступом вершин по два пикселя, и т.д.

Например, стрелка вверх с размером 1:

⊡⊠⊡
⊠⊠⊠

Стрелка вверх с размером 2:

⊡⊡⊠⊡⊡
⊡⊠⊠⊠⊡
⊠⊠⊠⊠⊠

Стрелка вверх с размером 3:

⊡⊡⊡⊠⊡⊡⊡
⊡⊡⊠⊠⊠⊡⊡
⊡⊠⊠⊠⊠⊠⊡
⊠⊠⊠⊠⊠⊠⊠

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

Аналогично строятся и стрелки вниз, влево и вправо.

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

   MSG_GRAPH_ELEMENT_TYPE_WF_PROGRESS_BAR,            // Элемент управления ProgressBar
   MSG_GRAPH_ELEMENT_TYPE_WF_SCROLL_BAR,              // Элемент управления ScrollBar
   MSG_GRAPH_ELEMENT_TYPE_WF_SCROLL_BAR_THUMB,        // Область захвата элемента управления ScrollBar
   MSG_GRAPH_ELEMENT_TYPE_WF_SCROLL_BAR_VERTICAL,     // Элемент управления ScrollBarVertical
   MSG_GRAPH_ELEMENT_TYPE_WF_SCROLL_BAR_HORISONTAL,   // Элемент управления ScrollBarHorisontal

...

   MSG_CANV_ELEMENT_PROP_DISPLAY_DURATION,            // Продолжительность процесса отображения элемента управления
   MSG_CANV_ELEMENT_PROP_ENABLED,                     // Флаг доступности элемента
   MSG_CANV_ELEMENT_PROP_RESIZABLE,                   // Флаг изменяемости размеров элемента
   MSG_CANV_ELEMENT_PROP_FORE_COLOR,                  // Цвет текста по умолчанию для всех объектов элемента управления
   MSG_CANV_ELEMENT_PROP_FORE_COLOR_OPACITY,          // Непрозрачность цвета текста по умолчанию для всех объектов элемента управления

...

   MSG_CANV_ELEMENT_PROP_PROGRESS_BAR_VALUE,          // Текущее начение элемента ProgressBar в диапазоне от Min до Max
   MSG_CANV_ELEMENT_PROP_PROGRESS_BAR_MARQUEE_ANIM_SPEED,// Скорость анимации полосы прогресса при стиле Marquee
   MSG_CANV_ELEMENT_PROP_BUTTON_ARROW_SIZE,           // Размер стрелки, рисуемой на кнопке
//--- Вещественные свойства графических элементов

//--- Строковые свойства графических элементов

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

   {"Элемент управления \"ProgressBar\"","Control element \"ProgressBar\""},
   {"Элемент управления \"ScrollBar\"","Control element \"ScrollBar\""},
   {"Область захвата элемента управления \"ScrollBar\"","The grip area of the \"ScrollBar\" control"},
   {"Элемент управления \"ScrollBarVertical\"","Control element \"ScrollBarVertical\""},
   {"Элемент управления \"ScrollBarHorisontal\"","Control element \"ScrollBarHorisontal\""},

...

   {"Продолжительность процесса отображения элемента управления","Duration of the process of displaying the control"},
   {"Флаг доступности элемента","Element Availability flag"},
   {"Флаг изменяемости размеров элемента","Element Resizable flag"},
   {"Цвет текста по умолчанию для всех объектов элемента управления","Default text color for all objects in the control"},
   {"Непрозрачность цвета текста по умолчанию для всех объектов элемента управления","Default text color opacity for all objects in the control"},

...

   {"Текущее начение элемента ProgressBar в диапазоне от Min до Max","Current value of the ProgressBar in the range from Min to Max"},
   {"Скорость анимации полосы прогресса при стиле Marquee","Marquee style progress bar animation speed"},
   {"Размер стрелки, рисуемой на кнопке","Size of arrow drawn on the button"},
   
//--- Строковые свойства графических элементов


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

//+------------------------------------------------------------------+
//| Возвращает описание типа графического элемента                   |
//+------------------------------------------------------------------+
string CGBaseObj::TypeElementDescription(const ENUM_GRAPH_ELEMENT_TYPE type)
  {
   return
     (
      type==GRAPH_ELEMENT_TYPE_STANDARD                  ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_STANDARD)                 :
      type==GRAPH_ELEMENT_TYPE_STANDARD_EXTENDED         ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_STANDARD_EXTENDED)        :
      type==GRAPH_ELEMENT_TYPE_ELEMENT                   ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_ELEMENT)                  :
      type==GRAPH_ELEMENT_TYPE_SHADOW_OBJ                ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_SHADOW_OBJ)               :
      type==GRAPH_ELEMENT_TYPE_FORM                      ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_FORM)                     :
      type==GRAPH_ELEMENT_TYPE_WINDOW                    ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WINDOW)                   :
      //--- WinForms
      type==GRAPH_ELEMENT_TYPE_WF_UNDERLAY               ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_UNDERLAY)              :
      type==GRAPH_ELEMENT_TYPE_WF_BASE                   ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_BASE)                  :
      //--- Контейнеры
      type==GRAPH_ELEMENT_TYPE_WF_CONTAINER              ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_CONTAINER)             :
      type==GRAPH_ELEMENT_TYPE_WF_GROUPBOX               ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_GROUPBOX)              :
      type==GRAPH_ELEMENT_TYPE_WF_PANEL                  ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_PANEL)                 :
      type==GRAPH_ELEMENT_TYPE_WF_TAB_CONTROL            ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_TAB_CONTROL)           :
      type==GRAPH_ELEMENT_TYPE_WF_SPLIT_CONTAINER        ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_SPLIT_CONTAINER)       :
      //--- Стандартные элементы управления
      type==GRAPH_ELEMENT_TYPE_WF_COMMON_BASE            ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_COMMON_BASE)           :
      type==GRAPH_ELEMENT_TYPE_WF_LABEL                  ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_LABEL)                 :
      type==GRAPH_ELEMENT_TYPE_WF_CHECKBOX               ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_CHECKBOX)              :
      type==GRAPH_ELEMENT_TYPE_WF_RADIOBUTTON            ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_RADIOBUTTON)           :
      type==GRAPH_ELEMENT_TYPE_WF_BUTTON                 ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_BUTTON)                :
      type==GRAPH_ELEMENT_TYPE_WF_ELEMENTS_LIST_BOX      ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_ELEMENTS_LIST_BOX)     :
      type==GRAPH_ELEMENT_TYPE_WF_LIST_BOX               ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_LIST_BOX)              :
      type==GRAPH_ELEMENT_TYPE_WF_LIST_BOX_ITEM          ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_LIST_BOX_ITEM)         :
      type==GRAPH_ELEMENT_TYPE_WF_CHECKED_LIST_BOX       ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_CHECKED_LIST_BOX)      :
      type==GRAPH_ELEMENT_TYPE_WF_BUTTON_LIST_BOX        ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_BUTTON_LIST_BOX)       :
      type==GRAPH_ELEMENT_TYPE_WF_TOOLTIP                ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_TOOLTIP)               :
      type==GRAPH_ELEMENT_TYPE_WF_PROGRESS_BAR           ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_PROGRESS_BAR)          :
      //--- Вспомогательные объекты элементов управления
      type==GRAPH_ELEMENT_TYPE_WF_TAB_HEADER             ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_TAB_HEADER)            :
      type==GRAPH_ELEMENT_TYPE_WF_TAB_FIELD              ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_TAB_FIELD)             :
      type==GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON           ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON)          :
      type==GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_UP        ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_UP)       :
      type==GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_DOWN      ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_DOWN)     :
      type==GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_LEFT      ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_LEFT)     :
      type==GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_RIGHT     ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_RIGHT)    :
      type==GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTONS_UD_BOX   ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTONS_UD_BOX)  :
      type==GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTONS_LR_BOX   ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTONS_LR_BOX)  :
      type==GRAPH_ELEMENT_TYPE_WF_SPLIT_CONTAINER_PANEL  ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_SPLIT_CONTAINER_PANEL) :
      type==GRAPH_ELEMENT_TYPE_WF_SPLITTER               ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_SPLITTER)              :
      type==GRAPH_ELEMENT_TYPE_WF_HINT_BASE              ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_HINT_BASE)             :
      type==GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_LEFT         ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_LEFT)        :
      type==GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_RIGHT        ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_RIGHT)       :
      type==GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_UP           ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_UP)          :
      type==GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_DOWN         ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_DOWN)        :
      type==GRAPH_ELEMENT_TYPE_WF_BAR_PROGRESS_BAR       ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_BAR_PROGRESS_BAR)      :
      type==GRAPH_ELEMENT_TYPE_WF_GLARE_OBJ              ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_GLARE_OBJ)             :
      type==GRAPH_ELEMENT_TYPE_WF_SCROLL_BAR             ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_SCROLL_BAR)            :
      type==GRAPH_ELEMENT_TYPE_WF_SCROLL_BAR_VERTICAL    ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_SCROLL_BAR_VERTICAL)   :
      type==GRAPH_ELEMENT_TYPE_WF_SCROLL_BAR_HORISONTAL  ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_SCROLL_BAR_HORISONTAL) :
      type==GRAPH_ELEMENT_TYPE_WF_SCROLL_BAR_THUMB       ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_SCROLL_BAR_THUMB)      :
      "Unknown"
     );
  }  
//+------------------------------------------------------------------+

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

В файле \MQL5\Include\DoEasy\Objects\Graph\GCnvElement.mqh впишем новые целочисленные поля в структуру объекта:

private:
   int               m_shift_coord_x;                          // Смещение координаты X относительно базового объекта
   int               m_shift_coord_y;                          // Смещение координаты Y относительно базового объекта
   struct SData
     {
      //--- Целочисленные свойства объекта
      int            id;                                       // Идентификатор элемента
      int            type;                                     // Тип графического элемента

      //---...
      //---...

      long           zorder;                                   // Приоритет графического объекта на получение события нажатия мышки на графике
      bool           enabled;                                  // Флаг доступности элемента
      bool           resizable;                                // Флаг изменяемости размеров
      color          fore_color;                               // Цвет текста по умолчанию для всех объектов элемента управления
      uchar          fore_color_opacity;                       // Непрозрачность цвета текста по умолчанию для всех объектов элемента управления

      //---...
      //---...

      int            progress_bar_value;                       // Текущее значение элемента ProgressBar в диапазоне от Min до Max
      int            progress_bar_marquee_speed;               // Скорость анимации полосы прогресса при стиле Marquee
      uchar          button_arrow_size;                        // Размер стрелки, рисуемой на кнопке
      //---
      ulong          tooltip_initial_delay;                    // Задержка отображения подсказки
      ulong          tooltip_auto_pop_delay;                   // Длительность отображения подсказки
      ulong          tooltip_reshow_delay;                     // Задержка отображения новой подсказки одного элемента
      bool           tooltip_show_always;                      // Отображать подсказку в неактивном окне
      int            tooltip_icon;                             // Значок, отображаемый в подсказке
      bool           tooltip_is_balloon;                       // Подсказка в форме "облачка"
      bool           tooltip_use_fading;                       // Угасание при отображении и скрытии подсказки
      //--- Вещественные свойства объекта

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

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

//--- Возвращает положение курсора относительно (1) всего элемента, (2) видимой области, (3) активной зоны, (4) зоны управления элемента
   bool              CursorInsideElement(const int x,const int y);
   bool              CursorInsideVisibleArea(const int x,const int y);
   bool              CursorInsideActiveArea(const int x,const int y);
   bool              CursorInsideControlArea(const int x,const int y);
//--- Возвращает положение курсора относительно (1) правой, (2) нижней зоны прокрутки элемента
   bool              CursorInsideScrollRightArea(const int x,const int y);
   bool              CursorInsideScrollBottomArea(const int x,const int y);
//--- Возвращает положение курсора относительно (1) верхней, (2) нижней, (3) левой, (4) правой зоны изменения размера элемента
   bool              CursorInsideResizeTopArea(const int x,const int y);
   bool              CursorInsideResizeBottomArea(const int x,const int y);
   bool              CursorInsideResizeLeftArea(const int x,const int y);
   bool              CursorInsideResizeRightArea(const int x,const int y);
//--- Возвращает положение курсора относительно (1) верхнего-левого, (2) верхнего-правого,
//--- (3) нижнего-левого, (4) нижнего-правого угла зоны изменения размера элемента
   bool              CursorInsideResizeTopLeftArea(const int x,const int y);
   bool              CursorInsideResizeTopRightArea(const int x,const int y);
   bool              CursorInsideResizeBottomLeftArea(const int x,const int y);
   bool              CursorInsideResizeBottomRightArea(const int x,const int y);

//--- Создаёт элемент
   bool              Create(const long chart_id,

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

//--- Устанавливает флаг (1) перемещаемости, (2) активности объекта, (3) флаг взаимодействия,
//--- (4) идентификатор элемента, (5) номер элемента в списке, флаг (6) доступности, (7) изменяемого размера, (8) наличие тени
   void              SetMovable(const bool flag)               { this.SetProperty(CANV_ELEMENT_PROP_MOVABLE,flag);                     }
   void              SetActive(const bool flag)                { this.SetProperty(CANV_ELEMENT_PROP_ACTIVE,flag);                      }
   void              SetInteraction(const bool flag)           { this.SetProperty(CANV_ELEMENT_PROP_INTERACTION,flag);                 }
   void              SetID(const int id)                       { this.SetProperty(CANV_ELEMENT_PROP_ID,id);                            }
   void              SetNumber(const int number)               { this.SetProperty(CANV_ELEMENT_PROP_NUM,number);                       }
   void              SetEnabled(const bool flag)               { this.SetProperty(CANV_ELEMENT_PROP_ENABLED,flag);                     }
   void              SetResizable(const bool flag)             { this.SetProperty(CANV_ELEMENT_PROP_RESIZABLE,flag);                   }
   void              SetShadow(const bool flag)                { this.m_shadow=flag;                                                   }
   
//--- Устанавливает (1) координату X, (2) координату Y, (3) ширину, (4) высоту зоны управления элемента
   void              SetControlAreaX(const int value)          { this.SetProperty(CANV_ELEMENT_PROP_CONTROL_AREA_X,value);             }
   void              SetControlAreaY(const int value)          { this.SetProperty(CANV_ELEMENT_PROP_CONTROL_AREA_Y,value);             }
   void              SetControlAreaWidth(const int value)      { this.SetProperty(CANV_ELEMENT_PROP_CONTROL_AREA_WIDTH,value);         }
   void              SetControlAreaHeight(const int value)     { this.SetProperty(CANV_ELEMENT_PROP_CONTROL_AREA_HEIGHT,value);        }
   
//--- Возвращает смещение (1) левого, (2) правого, (3) верхнего, (4) нижнего края активной зоны элемента
   int               ActiveAreaLeftShift(void)           const { return (int)this.GetProperty(CANV_ELEMENT_PROP_ACT_SHIFT_LEFT);       }
   int               ActiveAreaRightShift(void)          const { return (int)this.GetProperty(CANV_ELEMENT_PROP_ACT_SHIFT_RIGHT);      }
   int               ActiveAreaTopShift(void)            const { return (int)this.GetProperty(CANV_ELEMENT_PROP_ACT_SHIFT_TOP);        }
   int               ActiveAreaBottomShift(void)         const { return (int)this.GetProperty(CANV_ELEMENT_PROP_ACT_SHIFT_BOTTOM);     }
//--- Возвращает координату (1) левого, (2) правого, (3) верхнего, (4) нижнего края активной зоны элемента
   int               ActiveAreaLeft(void)                const { return int(this.CoordX()+this.ActiveAreaLeftShift());                 }
   int               ActiveAreaRight(void)               const { return int(this.RightEdge()-this.ActiveAreaRightShift());             }
   int               ActiveAreaTop(void)                 const { return int(this.CoordY()+this.ActiveAreaTopShift());                  }
   int               ActiveAreaBottom(void)              const { return int(this.BottomEdge()-this.ActiveAreaBottomShift());           }

//--- Возвращает смещение координаты (1) X, (2) Y, (3) ширину, (4) высоту зоны управления элемента
   int               ControlAreaXShift(void)             const { return (int)this.GetProperty(CANV_ELEMENT_PROP_CONTROL_AREA_X);       }
   int               ControlAreaYShift(void)             const { return (int)this.GetProperty(CANV_ELEMENT_PROP_CONTROL_AREA_Y);       }
   int               ControlAreaWidth(void)              const { return (int)this.GetProperty(CANV_ELEMENT_PROP_CONTROL_AREA_WIDTH);   }
   int               ControlAreaHeight(void)             const { return (int)this.GetProperty(CANV_ELEMENT_PROP_CONTROL_AREA_HEIGHT);  }
//--- Возвращает координату (1) левого, (2) правого, (3) верхнего, (4) нижнего края зоны управления элемента
   int               ControlAreaLeft(void)               const { return this.CoordX()+this.ControlAreaXShift();                        }
   int               ControlAreaRight(void)              const { return this.ControlAreaLeft()+this.ControlAreaWidth();                }
   int               ControlAreaTop(void)                const { return this.CoordY()+this.ControlAreaYShift();                        }
   int               ControlAreaBottom(void)             const { return this.ControlAreaTop()+this.ControlAreaHeight();                }
//--- Возвращает относительную координату (1) левого, (2) правого, (3) верхнего, (4) нижнего края зоны управления элемента
   int               ControlAreaLeftRelative(void)       const { return this.ControlAreaLeft()-this.CoordX();                          }
   int               ControlAreaRightRelative(void)      const { return this.ControlAreaRight()-this.CoordX();                         }
   int               ControlAreaTopRelative(void)        const { return this.ControlAreaTop()-this.CoordY();                           }
   int               ControlAreaBottomRelative(void)     const { return this.ControlAreaBottom()-this.CoordY();                        }
   
//--- Возвращает смещение координаты (1) X, (2) Y, (3) ширину, (4) высоту области прокрутки элемента справа
   int               ScrollAreaRightXShift(void)         const { return (int)this.GetProperty(CANV_ELEMENT_PROP_SCROLL_AREA_X_RIGHT);     }
   int               ScrollAreaRightYShift(void)         const { return (int)this.GetProperty(CANV_ELEMENT_PROP_SCROLL_AREA_Y_RIGHT);     }
   int               ScrollAreaRightWidth(void)          const { return (int)this.GetProperty(CANV_ELEMENT_PROP_SCROLL_AREA_WIDTH_RIGHT); }
   int               ScrollAreaRightHeight(void)         const { return (int)this.GetProperty(CANV_ELEMENT_PROP_SCROLL_AREA_HEIGHT_RIGHT);}
//--- Возвращает координату (1) левого, (2) правого, (3) верхнего, (4) нижнего края зоны управления элемента
   int               ScrollAreaRightLeft(void)           const { return this.CoordX()+this.ScrollAreaRightXShift();                       }
   int               ScrollAreaRightRight(void)          const { return this.ScrollAreaRightLeft()+this.ScrollAreaRightWidth();           }
   int               ScrollAreaRightTop(void)            const { return this.CoordY()+this.ScrollAreaRightYShift();                       }
   int               ScrollAreaRightBottom(void)         const { return this.ScrollAreaRightTop()+this.ScrollAreaRightHeight();           }
//--- Возвращает относительную координату (1) левого, (2) правого, (3) верхнего, (4) нижнего края зоны управления элемента
   int               ScrollAreaRightLeftRelative(void)   const { return this.ScrollAreaRightLeft()-this.CoordX();                         }
   int               ScrollAreaRightRightRelative(void)  const { return this.ScrollAreaRightRight()-this.CoordX();                        }
   int               ScrollAreaRightTopRelative(void)    const { return this.ScrollAreaRightTop()-this.CoordY();                          }
   int               ScrollAreaRightBottomRelative(void) const { return this.ScrollAreaRightBottom()-this.CoordY();                       }
   
//--- Возвращает смещение координаты (1) X, (2) Y, (3) ширину, (4) высоту области прокрутки элемента снизу
   int               ScrollAreaBottomXShift(void)        const { return (int)this.GetProperty(CANV_ELEMENT_PROP_SCROLL_AREA_X_BOTTOM);    }
   int               ScrollAreaBottomYShift(void)        const { return (int)this.GetProperty(CANV_ELEMENT_PROP_SCROLL_AREA_Y_BOTTOM);    }
   int               ScrollAreaBottomWidth(void)         const { return (int)this.GetProperty(CANV_ELEMENT_PROP_SCROLL_AREA_WIDTH_BOTTOM);}
   int               ScrollAreaBottomHeight(void)        const { return (int)this.GetProperty(CANV_ELEMENT_PROP_SCROLL_AREA_HEIGHT_BOTTOM);}
//--- Возвращает координату (1) левого, (2) правого, (3) верхнего, (4) нижнего края зоны управления элемента
   int               ScrollAreaBottomLeft(void)          const { return this.CoordX()+this.ScrollAreaBottomXShift();                      }
   int               ScrollAreaBottomRight(void)         const { return this.ScrollAreaBottomLeft()+this.ScrollAreaBottomWidth();         }
   int               ScrollAreaBottomTop(void)           const { return this.CoordY()+this.ScrollAreaBottomYShift();                      }
   int               ScrollAreaBottomBottom(void)        const { return this.ScrollAreaBottomTop()+this.ScrollAreaBottomHeight();         }
//--- Возвращает относительную координату (1) левого, (2) правого, (3) верхнего, (4) нижнего края зоны управления элемента
   int               ScrollAreaBottomLeftRelative(void)  const { return this.ScrollAreaBottomLeft()-this.CoordX();                        }
   int               ScrollAreaBottomRightRelative(void) const { return this.ScrollAreaBottomRight()-this.CoordX();                       }
   int               ScrollAreaBottomTopRelative(void)   const { return this.ScrollAreaBottomTop()-this.CoordY();                         }
   int               ScrollAreaBottomBottomRelative(void)const { return this.ScrollAreaBottomBottom()-this.CoordY();                      }

//--- Возвращает ширину (1) левой, (2) правой, (3) верхней, (4) нижней области грани элемента
   int               BorderResizeAreaLeft(void)          const { return (int)this.GetProperty(CANV_ELEMENT_PROP_BORDER_LEFT_AREA_WIDTH);     }
   int               BorderResizeAreaRight(void)         const { return (int)this.GetProperty(CANV_ELEMENT_PROP_BORDER_RIGHT_AREA_WIDTH);    }
   int               BorderResizeAreaTop(void)           const { return (int)this.GetProperty(CANV_ELEMENT_PROP_BORDER_TOP_AREA_WIDTH);      }
   int               BorderResizeAreaBottom(void)        const { return (int)this.GetProperty(CANV_ELEMENT_PROP_BORDER_BOTTOM_AREA_WIDTH);   }

Ниже добавим метод, возвращающий флаг изменяемости размера элемента:

//--- Возвращает флаг (1) перемещаемости, (2) активности, (3) взаимодействия, (4) доступности элемента, (5) изменяемости размера
   bool              Movable(void)                       const { return (bool)this.GetProperty(CANV_ELEMENT_PROP_MOVABLE);             }
   bool              Active(void)                        const { return (bool)this.GetProperty(CANV_ELEMENT_PROP_ACTIVE);              }
   bool              Interaction(void)                   const { return (bool)this.GetProperty(CANV_ELEMENT_PROP_INTERACTION);         }
   bool              Enabled(void)                       const { return (bool)this.GetProperty(CANV_ELEMENT_PROP_ENABLED);             }
   bool              Resizable(void)                     const { return (bool)this.GetProperty(CANV_ELEMENT_PROP_RESIZABLE);           }
//--- Возвращает (1) имя объекта, (2) имя графического ресурса, (3) идентификатор графика, (4) номер подокна графика

Изменим объявление методов, рисующих стрелки:

//+------------------------------------------------------------------+
//| Методы рисования предопределённых стандартных изображений        |
//+------------------------------------------------------------------+
//--- Рисует иконку Info
   void              DrawIconInfo(const int coord_x,const int coord_y,const uchar opacity);
//--- Рисует иконку Warning
   void              DrawIconWarning(const int coord_x,const int coord_y,const uchar opacity);
//--- Рисует иконку Error
   void              DrawIconError(const int coord_x,const int coord_y,const uchar opacity);
//--- Рисует стрелку влево
   void              DrawArrowLeft(const int base_x,const int base_y,const int size,const color clr,const uchar opacity);
//--- Рисует стрелку вправо
   void              DrawArrowRight(const int base_x,const int base_y,const int size,const color clr,const uchar opacity);
//--- Рисует стрелку вверх
   void              DrawArrowUp(const int base_x,const int base_y,const int size,const color clr,const uchar opacity);
//--- Рисует стрелку вниз
   void              DrawArrowDown(const int base_x,const int base_y,const int size,const color clr,const uchar opacity);
  };
//+------------------------------------------------------------------+

Теперь в методы будем передавать координаты центральной точки и относительный размер стрелки.

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

//+------------------------------------------------------------------+
//| Инициализирует свойства                                          |
//+------------------------------------------------------------------+
void CGCnvElement::Initialize(const ENUM_GRAPH_ELEMENT_TYPE element_type,
                              const int element_id,const int element_num,
                              const int x,const int y,const int w,const int h,
                              const string descript,const bool movable,const bool activity)
  {
   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_RESIZABLE,false);                       // Флаг изменяемого размера элемента
   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_VISIBLE_AREA_X,0);                      // Координата X области видимости
   this.SetProperty(CANV_ELEMENT_PROP_VISIBLE_AREA_Y,0);                      // Координата Y области видимости
   this.SetProperty(CANV_ELEMENT_PROP_VISIBLE_AREA_WIDTH,w);                  // Ширина области видимости
   this.SetProperty(CANV_ELEMENT_PROP_VISIBLE_AREA_HEIGHT,h);                 // Высота области видимости
   this.SetProperty(CANV_ELEMENT_PROP_DISPLAYED,true);                        // Флаг отображения не скрытого элемента управления
   this.SetProperty(CANV_ELEMENT_PROP_DISPLAY_STATE,CANV_ELEMENT_DISPLAY_STATE_NORMAL);// Состояние отображения элемента управления
   this.SetProperty(CANV_ELEMENT_PROP_DISPLAY_DURATION,DEF_CONTROL_PROCESS_DURATION);  // Продолжительность процесса отображения элемента управления
   this.SetProperty(CANV_ELEMENT_PROP_CONTROL_AREA_X,0);                      // X-координата области управления
   this.SetProperty(CANV_ELEMENT_PROP_CONTROL_AREA_Y,0);                      // Y-координата области управления
   this.SetProperty(CANV_ELEMENT_PROP_CONTROL_AREA_WIDTH,0);                  // Ширина области управления
   this.SetProperty(CANV_ELEMENT_PROP_CONTROL_AREA_HEIGHT,0);                 // Высота области управления
   this.SetProperty(CANV_ELEMENT_PROP_SCROLL_AREA_X_RIGHT,0);                 // X-координата области прокрутки справа
   this.SetProperty(CANV_ELEMENT_PROP_SCROLL_AREA_Y_RIGHT,0);                 // Y-координата области прокрутки справа
   this.SetProperty(CANV_ELEMENT_PROP_SCROLL_AREA_WIDTH_RIGHT,0);             // Ширина области прокрутки справа
   this.SetProperty(CANV_ELEMENT_PROP_SCROLL_AREA_HEIGHT_RIGHT,0);            // Высота области прокрутки справа
   this.SetProperty(CANV_ELEMENT_PROP_SCROLL_AREA_X_BOTTOM,0);                // X-координата области прокрутки снизу
   this.SetProperty(CANV_ELEMENT_PROP_SCROLL_AREA_Y_BOTTOM,0);                // Y-координата области прокрутки снизу
   this.SetProperty(CANV_ELEMENT_PROP_SCROLL_AREA_WIDTH_BOTTOM,0);            // Ширина области прокрутки снизу
   this.SetProperty(CANV_ELEMENT_PROP_SCROLL_AREA_HEIGHT_BOTTOM,0);           // Высота области прокрутки снизу
   this.SetProperty(CANV_ELEMENT_PROP_BORDER_LEFT_AREA_WIDTH,2);              // Ширина области левой грани 
   this.SetProperty(CANV_ELEMENT_PROP_BORDER_BOTTOM_AREA_WIDTH,2);            // Ширина области нижней грани
   this.SetProperty(CANV_ELEMENT_PROP_BORDER_RIGHT_AREA_WIDTH,2);             // Ширина области правой грани
   this.SetProperty(CANV_ELEMENT_PROP_BORDER_TOP_AREA_WIDTH,2);               // Ширина области верхней грани
   //---
   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_PROGRESS_BAR_VALUE,50);                 // Текущее значение элемента ProgressBar в диапазоне от Min до Max
   this.SetProperty(CANV_ELEMENT_PROP_PROGRESS_BAR_MARQUEE_ANIM_SPEED,10);    // Скорость анимации полосы прогресса при стиле Marquee
   this.SetProperty(CANV_ELEMENT_PROP_BUTTON_ARROW_SIZE,3);                   // Размер стрелки, рисуемой на кнопке
  }
//+------------------------------------------------------------------+

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

//+------------------------------------------------------------------+
//| Создаёт структуру объекта                                        |
//+------------------------------------------------------------------+
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.zorder=this.GetProperty(CANV_ELEMENT_PROP_ZORDER);                            // Приоритет графического объекта на получение события нажатия мышки на графике
   this.m_struct_obj.enabled=(bool)this.GetProperty(CANV_ELEMENT_PROP_ENABLED);                    // Флаг доступности элемента
   this.m_struct_obj.resizable=(bool)this.GetProperty(CANV_ELEMENT_PROP_RESIZABLE);                // Флаг изменяемости размеров элемента
   this.m_struct_obj.fore_color=(color)this.GetProperty(CANV_ELEMENT_PROP_FORE_COLOR);             // Цвет текста по умолчанию для всех объектов элемента управления
   this.m_struct_obj.fore_color_opacity=(uchar)this.GetProperty(CANV_ELEMENT_PROP_FORE_COLOR_OPACITY); // Непрозрачность цвета текста по умолчанию для всех объектов элемента управления
   
   //---...
   //---...

   this.m_struct_obj.progress_bar_value=(int)this.GetProperty(CANV_ELEMENT_PROP_PROGRESS_BAR_VALUE);     // Текущее значение элемента ProgressBar в диапазоне от Min до Max
   this.m_struct_obj.progress_bar_marquee_speed=(int)this.GetProperty(CANV_ELEMENT_PROP_PROGRESS_BAR_MARQUEE_ANIM_SPEED);// Скорость анимации полосы прогресса при стиле Marquee
   this.m_struct_obj.button_arrow_size=(uchar)this.GetProperty(CANV_ELEMENT_PROP_BUTTON_ARROW_SIZE);     // Размер стрелки, рисуемой на кнопке
//--- Сохранение вещественных свойств

//--- Сохранение строковых свойств
   ::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);           // Текст графического элемента
   ::StringToCharArray(this.GetProperty(CANV_ELEMENT_PROP_DESCRIPTION),this.m_struct_obj.descript);// Описание графического элемента
   ::StringToCharArray(this.GetProperty(CANV_ELEMENT_PROP_TOOLTIP_TITLE),this.m_struct_obj.tooltip_title);// Заголовок Tooltip для элемента
   ::StringToCharArray(this.GetProperty(CANV_ELEMENT_PROP_TOOLTIP_TEXT),this.m_struct_obj.tooltip_text);  // Текст Tooltip для элемента
   //--- Сохранение структуры в 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_ZORDER,this.m_struct_obj.zorder);                            // Приоритет графического объекта на получение события нажатия мышки на графике
   this.SetProperty(CANV_ELEMENT_PROP_ENABLED,this.m_struct_obj.enabled);                          // Флаг доступности элемента
   this.SetProperty(CANV_ELEMENT_PROP_RESIZABLE,this.m_struct_obj.resizable);                      // Флаг изменяемости размеров элемента
   this.SetProperty(CANV_ELEMENT_PROP_FORE_COLOR,this.m_struct_obj.fore_color);                    // Цвет текста по умолчанию для всех объектов элемента управления
   this.SetProperty(CANV_ELEMENT_PROP_FORE_COLOR_OPACITY,this.m_struct_obj.fore_color_opacity);    // Непрозрачность цвета текста по умолчанию для всех объектов элемента управления
   
   //---...
   //---...

   this.SetProperty(CANV_ELEMENT_PROP_PROGRESS_BAR_VALUE,this.m_struct_obj.progress_bar_value); // Текущее значение элемента ProgressBar в диапазоне от Min до Max
   this.SetProperty(CANV_ELEMENT_PROP_PROGRESS_BAR_MARQUEE_ANIM_SPEED,this.m_struct_obj.progress_bar_marquee_speed);  // Скорость анимации полосы прогресса при стиле Marquee
   this.SetProperty(CANV_ELEMENT_PROP_BUTTON_ARROW_SIZE,this.m_struct_obj.button_arrow_size);   // Размер стрелки, рисуемой на кнопке
//--- Сохранение вещественных свойств

//--- Сохранение строковых свойств
   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));           // Текст графического элемента
   this.SetProperty(CANV_ELEMENT_PROP_DESCRIPTION,::CharArrayToString(this.m_struct_obj.descript));// Описание графического элемента
   this.SetProperty(CANV_ELEMENT_PROP_TOOLTIP_TITLE,::CharArrayToString(this.m_struct_obj.tooltip_title));// Заголовок Tooltip для элемента
   this.SetProperty(CANV_ELEMENT_PROP_TOOLTIP_TEXT,::CharArrayToString(this.m_struct_obj.tooltip_text));  // Текст Tooltip для элемента
  }
//+------------------------------------------------------------------+

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

//+------------------------------------------------------------------+
//|Возвращает положение курсора относительно зоны управления элемента|
//+------------------------------------------------------------------+
bool CGCnvElement::CursorInsideControlArea(const int x,const int y)
  {
   return(x>=this.ControlAreaLeft() && x<=this.ControlAreaRight() && y>=this.ControlAreaTop() && y<=this.ControlAreaBottom());
  }
//+------------------------------------------------------------------+
//| Возвращает положение курсора относительно                        |
//| правой зоны прокрутки элемента                                   |
//+------------------------------------------------------------------+
bool CGCnvElement::CursorInsideScrollRightArea(const int x,const int y)
  {
   return(x>=this.ScrollAreaRightLeft() && x<=this.ScrollAreaRightRight() && y>=this.ScrollAreaRightTop() && y<=this.ScrollAreaRightBottom());
  }
//+------------------------------------------------------------------+
//| Возвращает положение курсора относительно                        |
//| нижней зоны прокрутки элемента                                   |
//+------------------------------------------------------------------+
bool CGCnvElement::CursorInsideScrollBottomArea(const int x,const int y)
  {
   return(x>=this.ScrollAreaBottomLeft() && x<=this.ScrollAreaBottomRight() && y>=this.ScrollAreaBottomTop() && y<=this.ScrollAreaBottomBottom());
  }
//+------------------------------------------------------------------+
//| Возвращает положение курсора относительно                        |
//| верхней зоны изменения размера элемента                          |
//+------------------------------------------------------------------+
bool CGCnvElement::CursorInsideResizeTopArea(const int x,const int y)
  {
   return(x>=this.CoordX()+DEF_CONTROL_CORNER_AREA && x<=this.RightEdge()-DEF_CONTROL_CORNER_AREA && y>=this.CoordY() && y<=this.CoordY()+this.BorderResizeAreaTop());
  }
//+------------------------------------------------------------------+
//| Возвращает положение курсора относительно                        |
//| нижней зоны изменения размера элемента                           |
//+------------------------------------------------------------------+
bool CGCnvElement::CursorInsideResizeBottomArea(const int x,const int y)
  {
   return(x>=this.CoordX()+DEF_CONTROL_CORNER_AREA && x<=this.RightEdge()-DEF_CONTROL_CORNER_AREA && y>=this.BottomEdge()-this.BorderResizeAreaBottom() && y<=this.BottomEdge());
  }
//+------------------------------------------------------------------+
//| Возвращает положение курсора относительно                        |
//| левой зоны изменения размера элемента                            |
//+------------------------------------------------------------------+
bool CGCnvElement::CursorInsideResizeLeftArea(const int x,const int y)
  {
   return(x>=this.CoordX() && x<=this.CoordX()+this.BorderResizeAreaLeft() && y>=this.CoordY()+DEF_CONTROL_CORNER_AREA && y<=this.BottomEdge()-DEF_CONTROL_CORNER_AREA);
  }
//+------------------------------------------------------------------+
//| Возвращает положение курсора относительно                        |
//| правой зоны изменения размера элемента                           |
//+------------------------------------------------------------------+
bool CGCnvElement::CursorInsideResizeRightArea(const int x,const int y)
  {
   return(x>=this.RightEdge()-this.BorderResizeAreaRight() && x<=this.RightEdge() && y>=this.CoordY()+DEF_CONTROL_CORNER_AREA && y<=this.BottomEdge()-DEF_CONTROL_CORNER_AREA);
  }
//+------------------------------------------------------------------+
//| Возвращает положение курсора относительно                        |
//| верхнего-левого угла зоны изменения размера элемента             |
//+------------------------------------------------------------------+
bool CGCnvElement::CursorInsideResizeTopLeftArea(const int x,const int y)
  {
   return
     (
      (x>=this.CoordX() && x<this.CoordX()+DEF_CONTROL_CORNER_AREA && y>=this.CoordY() && y<=this.CoordY()+this.BorderResizeAreaTop()) ||
      (x>=this.CoordX() && x<=this.BorderResizeAreaLeft() && y>=this.CoordY() && y<=this.CoordY()+DEF_CONTROL_CORNER_AREA)
     );
  }
//+------------------------------------------------------------------+
//| Возвращает положение курсора относительно                        |
//| верхнего-правого угла зоны изменения размера элемента            |
//+------------------------------------------------------------------+
bool CGCnvElement::CursorInsideResizeTopRightArea(const int x,const int y)
  {
   return
     (
      (x>this.RightEdge()-DEF_CONTROL_CORNER_AREA && x<=this.RightEdge() && y>=this.CoordY() && y<=this.CoordY()+this.BorderResizeAreaTop()) ||
      (x>=this.RightEdge()-this.BorderResizeAreaRight() && x<=this.RightEdge() && y>=this.CoordY() && y<=this.CoordY()+DEF_CONTROL_CORNER_AREA)
     );
  }
//+------------------------------------------------------------------+
//| Возвращает положение курсора относительно                        |
//| нижнего-левого угла зоны изменения размера элемента              |
//+------------------------------------------------------------------+
bool CGCnvElement::CursorInsideResizeBottomLeftArea(const int x,const int y)
  {
   return
     (
      (x>=this.CoordX() && x<this.CoordX()+DEF_CONTROL_CORNER_AREA && y>=this.BottomEdge()-this.BorderResizeAreaBottom() && y<=this.BottomEdge()) ||
      (x>=this.CoordX() && x<=this.CoordX()+this.BorderResizeAreaLeft() && y>this.BottomEdge()-DEF_CONTROL_CORNER_AREA && y<=this.BottomEdge())
     );
  }
//+------------------------------------------------------------------+
//| Возвращает положение курсора относительно                        |
//| нижнего-правого угла зоны изменения размера элемента             |
//+------------------------------------------------------------------+
bool CGCnvElement::CursorInsideResizeBottomRightArea(const int x,const int y)
  {
   return
     (
      (x>this.RightEdge()-DEF_CONTROL_CORNER_AREA && x<=this.RightEdge() && y>=this.BottomEdge()-this.BorderResizeAreaBottom() && y<=this.BottomEdge()) ||
      (x>=this.RightEdge()-this.BorderResizeAreaRight() && x<=this.RightEdge() && y>this.BottomEdge()-DEF_CONTROL_CORNER_AREA && y<=this.BottomEdge())
     );
  }
//+------------------------------------------------------------------+
//| Обновляет координаты элемента                                    |
//+------------------------------------------------------------------+

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

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

//+------------------------------------------------------------------+
//| Рисует стрелку влево                                             |
//+------------------------------------------------------------------+
void CGCnvElement::DrawArrowLeft(const int base_x,const int base_y,const int size,const color clr,const uchar opacity)
  {
   int x=base_x;
   int y=base_y;
   int s=(size<1 ? 1 : size);
   this.DrawTriangleFill(x-s,y,x,y-s,x,y+s,clr,opacity);
   this.DrawTriangleWu(  x-s,y,x,y-s,x,y+s,clr,opacity);
  }
//+------------------------------------------------------------------+
//| Рисует стрелку вправо                                            |
//+------------------------------------------------------------------+
void CGCnvElement::DrawArrowRight(const int base_x,const int base_y,const int size,const color clr,const uchar opacity)
  {
   int x=base_x;
   int y=base_y;
   int s=(size<1 ? 1 : size);
   this.DrawTriangleFill(x+s,y,x,y+s,x,y-s,clr,opacity);
   this.DrawTriangleWu(  x+s,y,x,y+s,x,y-s,clr,opacity);
  }
//+------------------------------------------------------------------+
//| Рисует стрелку вверх                                             |
//+------------------------------------------------------------------+
void CGCnvElement::DrawArrowUp(const int base_x,const int base_y,const int size,const color clr,const uchar opacity)
  {
   int x=base_x;
   int y=base_y;
   int s=(size<1 ? 1 : size);
   this.DrawTriangleFill(x,y-s,x+s,y,x-s,y,clr,opacity);
   this.DrawTriangleWu(  x,y-s,x+s,y,x-s,y,clr,opacity);
  }
//+------------------------------------------------------------------+
//| Рисует стрелку вниз                                              |
//+------------------------------------------------------------------+
void CGCnvElement::DrawArrowDown(const int base_x,const int base_y,const int size,const color clr,const uchar opacity)
  {
   int x=base_x;
   int y=base_y;
   int s=(size<1 ? 1 : size);
   this.DrawTriangleFill(x,y+s,x-s,y,x+s,y,clr,opacity);
   this.DrawTriangleWu(  x,y+s,x-s,y,x+s,y,clr,opacity);
  }
//+------------------------------------------------------------------+

Для расчёта координат каждой вершины мы просто прибавляем или отнимаем от координат центральной точки размер стрелки, переданный в параметрах.
Следует иметь в виду, что размер ограничивается только по нижнему пределу (1), но по верхнему нет ограничений. Тут нужно следить самому, чтобы стрелка рисовалась нормального размера, так как реальный размер стрелки получается как два указанных размера (size) + один центральный пиксель. Т.е. при размере 1, реальным размером будет 1+1+1, при размере 2, реальным размером будет 2+1+2, при размере 3 — 3+1+3 и т.д.

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

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

//+------------------------------------------------------------------+
//| Обработчик последнего события мышки                              |
//+------------------------------------------------------------------+
void CButton::OnMouseEventPostProcessing(void)
  {
   if(!this.IsVisible() || !this.Enabled())
      return;
   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(this.MouseEventLast()==MOUSE_EVENT_INSIDE_ACTIVE_AREA_NOT_PRESSED || this.MouseEventLast()==MOUSE_EVENT_INSIDE_FORM_NOT_PRESSED)
          {
           this.SetBackgroundColor(this.State() ? this.BackgroundStateOnColor() : this.BackgroundColorInit(),false);
           this.SetForeColor(this.State() ? this.ForeStateOnColor() : this.ForeColorInit(),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                     :
      case MOUSE_FORM_STATE_INSIDE_FORM_PRESSED                         :
      case MOUSE_FORM_STATE_INSIDE_FORM_WHEEL                           :
//--- В пределах активной области
      case MOUSE_FORM_STATE_INSIDE_ACTIVE_AREA_NOT_PRESSED              :
      case MOUSE_FORM_STATE_INSIDE_ACTIVE_AREA_PRESSED                  :
      case MOUSE_FORM_STATE_INSIDE_ACTIVE_AREA_WHEEL                    :
      case MOUSE_FORM_STATE_INSIDE_ACTIVE_AREA_RELEASED                 :
//--- В пределаз области прокрутки снизу
      case MOUSE_FORM_STATE_INSIDE_SCROLL_AREA_BOTTOM_NOT_PRESSED       :
      case MOUSE_FORM_STATE_INSIDE_SCROLL_AREA_BOTTOM_PRESSED           :
      case MOUSE_FORM_STATE_INSIDE_SCROLL_AREA_BOTTOM_WHEEL             :
//--- В пределаз области прокрутки справа
      case MOUSE_FORM_STATE_INSIDE_SCROLL_AREA_RIGHT_NOT_PRESSED        :
      case MOUSE_FORM_STATE_INSIDE_SCROLL_AREA_RIGHT_PRESSED            :
      case MOUSE_FORM_STATE_INSIDE_SCROLL_AREA_RIGHT_WHEEL              :
//--- В пределах области изменения размеров окна сверху
      case MOUSE_FORM_STATE_INSIDE_RESIZE_TOP_AREA_NOT_PRESSED          :
      case MOUSE_FORM_STATE_INSIDE_RESIZE_TOP_AREA_PRESSED              :
      case MOUSE_FORM_STATE_INSIDE_RESIZE_TOP_AREA_WHEEL                :
//--- В пределах области изменения размеров окна снизу
      case MOUSE_FORM_STATE_INSIDE_RESIZE_BOTTOM_AREA_NOT_PRESSED       :
      case MOUSE_FORM_STATE_INSIDE_RESIZE_BOTTOM_AREA_PRESSED           :
      case MOUSE_FORM_STATE_INSIDE_RESIZE_BOTTOM_AREA_WHEEL             :
//--- В пределах области изменения размеров окна слева
      case MOUSE_FORM_STATE_INSIDE_RESIZE_LEFT_AREA_NOT_PRESSED         :
      case MOUSE_FORM_STATE_INSIDE_RESIZE_LEFT_AREA_PRESSED             :
      case MOUSE_FORM_STATE_INSIDE_RESIZE_LEFT_AREA_WHEEL               :
//--- В пределах области изменения размеров окна справа
      case MOUSE_FORM_STATE_INSIDE_RESIZE_RIGHT_AREA_NOT_PRESSED        :
      case MOUSE_FORM_STATE_INSIDE_RESIZE_RIGHT_AREA_PRESSED            :
      case MOUSE_FORM_STATE_INSIDE_RESIZE_RIGHT_AREA_WHEEL              :
//--- В пределах области изменения размеров окна сверху-слева
      case MOUSE_FORM_STATE_INSIDE_RESIZE_TOP_LEFT_AREA_NOT_PRESSED     :
      case MOUSE_FORM_STATE_INSIDE_RESIZE_TOP_LEFT_AREA_PRESSED         :
      case MOUSE_FORM_STATE_INSIDE_RESIZE_TOP_LEFT_AREA_WHEEL           :
//--- В пределах области изменения размеров окна сверху-справа
      case MOUSE_FORM_STATE_INSIDE_RESIZE_TOP_RIGHT_AREA_NOT_PRESSED    :
      case MOUSE_FORM_STATE_INSIDE_RESIZE_TOP_RIGHT_AREA_PRESSED        :
      case MOUSE_FORM_STATE_INSIDE_RESIZE_TOP_RIGHT_AREA_WHEEL          :
//--- В пределах области изменения размеров окна снизу-слева
      case MOUSE_FORM_STATE_INSIDE_RESIZE_BOTTOM_LEFT_AREA_NOT_PRESSED  :
      case MOUSE_FORM_STATE_INSIDE_RESIZE_BOTTOM_LEFT_AREA_PRESSED      :
      case MOUSE_FORM_STATE_INSIDE_RESIZE_BOTTOM_LEFT_AREA_WHEEL        :
//--- В пределах области изменения размеров окна снизу-справа
      case MOUSE_FORM_STATE_INSIDE_RESIZE_BOTTOM_RIGHT_AREA_NOT_PRESSED :
      case MOUSE_FORM_STATE_INSIDE_RESIZE_BOTTOM_RIGHT_AREA_PRESSED     :
      case MOUSE_FORM_STATE_INSIDE_RESIZE_BOTTOM_RIGHT_AREA_WHEEL       :
//--- В пределах области управления
      case MOUSE_FORM_STATE_INSIDE_CONTROL_AREA_NOT_PRESSED             :
      case MOUSE_FORM_STATE_INSIDE_CONTROL_AREA_PRESSED                 :
      case MOUSE_FORM_STATE_INSIDE_CONTROL_AREA_WHEEL                   :
        break;
      //---MOUSE_EVENT_NO_EVENT
      default:
        break;
     }
  }
//+------------------------------------------------------------------+

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

Точно такие же изменения в этот метод уже внесены в файлах TabHeader.mqh, CheckBox.mqh и SplitContainer.mqh.

Для указания цвета рисуемой стрелки в классе-кнопки со стрелкой мы использовали приватную переменную m_arrow_color. Использование этой переменной является лишним, так как для указания цвета мы можем использовать метод ForeColor(), который к тому же умеет управлять цветом рисуемого текста. Поэтому в файле \MQL5\Include\DoEasy\Objects\Graph\WForms\Helpers\ArrowButton.mqh удалим эту переменную и методы работы с ней:

//+------------------------------------------------------------------+
//| Класс объекта Arrow Button элементов управления WForms           |
//+------------------------------------------------------------------+
class CArrowButton : public CButton
  {
private:
   color             m_arrow_color;                      // Цвет стрелки
protected:
   //--- Рисует стрелку
   virtual void      DrawArrow(void){return;}
//--- Защищённый конструктор с указанием типа объекта, идентификатора чарта и подокна
                     CArrowButton(const ENUM_GRAPH_ELEMENT_TYPE type,
                                  CGCnvElement *main_obj,CGCnvElement *base_obj,
                                  const long chart_id,
                                  const int subwindow,
                                  const string descript,
                                  const int x,
                                  const int y,
                                  const int w,
                                  const int h);
public:
//--- (1) Устанавливает, (2) возвращает цвет стрелки
   void              SetArrowColor(const color clr)      { this.m_arrow_color=clr;     }
   color             ArrowColor(void)              const { return this.m_arrow_color;  }
//--- Конструктор


Соответственно, заменим обращение к удалённым методам на методы работы с цветом текста:

//+------------------------------------------------------------------+
//| Защищённый конструктор с указанием типа объекта,                 |
//| идентификатора чарта и подокна                                   |
//+------------------------------------------------------------------+
CArrowButton::CArrowButton(const ENUM_GRAPH_ELEMENT_TYPE type,
                           CGCnvElement *main_obj,CGCnvElement *base_obj,
                           const long chart_id,
                           const int subwindow,
                           const string descript,
                           const int x,
                           const int y,
                           const int w,
                           const int h) : CButton(type,main_obj,base_obj,chart_id,subwindow,descript,x,y,w,h)
  {
//--- Установим объекту указанный тип графического элемента, а тип объекта библиотеки - как тип этого объекта
   this.SetTypeElement(type);
   this.m_type=OBJECT_DE_TYPE_GWF_HELPER;
   this.SetPaddingAll(0);
   this.SetMarginAll(0);
   this.SetBorderSizeAll(1);
   this.SetForeColor(CLR_DEF_FORE_COLOR,true);
  }
//+------------------------------------------------------------------+
//| Конструктор с указанием главного и базового объектов,            |
//| идентификатора чарта и подокна                                   |
//+------------------------------------------------------------------+
CArrowButton::CArrowButton(CGCnvElement *main_obj,CGCnvElement *base_obj,
                           const long chart_id,
                           const int subwindow,
                           const string descript,
                           const int x,
                           const int y,
                           const int w,
                           const int h) : CButton(GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON,main_obj,base_obj,chart_id,subwindow,descript,x,y,w,h)
  {
   this.SetTypeElement(GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON);
   this.m_type=OBJECT_DE_TYPE_GWF_HELPER;
   this.SetPaddingAll(0);
   this.SetMarginAll(0);
   this.SetBorderSizeAll(1);
   this.SetForeColor(CLR_DEF_FORE_COLOR,true);
  }
//+------------------------------------------------------------------+

В файле \MQL5\Include\DoEasy\Objects\Graph\WForms\Helpers\ArrowRightButton.mqh в конструкторах класса установим размер рисуемой стрелки равный 3:

//+------------------------------------------------------------------+
//| Защищённый конструктор с указанием типа объекта,                 |
//| идентификатора чарта и подокна                                   |
//+------------------------------------------------------------------+
CArrowRightButton::CArrowRightButton(const ENUM_GRAPH_ELEMENT_TYPE type,
                                     CGCnvElement *main_obj,CGCnvElement *base_obj,
                                     const long chart_id,
                                     const int subwindow,
                                     const string descript,
                                     const int x,
                                     const int y,
                                     const int w,
                                     const int h) : CArrowButton(type,main_obj,base_obj,chart_id,subwindow,descript,x,y,w,h)
  {
//--- Установим объекту указанный тип графического элемента и размер рисуемой стрелки, равный 3
   this.SetTypeElement(type);
   this.SetProperty(CANV_ELEMENT_PROP_BUTTON_ARROW_SIZE,3);
  }
//+------------------------------------------------------------------+
//| Конструктор с указанием главного и базового объектов,            |
//| идентификатора чарта и подокна                                   |
//+------------------------------------------------------------------+
CArrowRightButton::CArrowRightButton(CGCnvElement *main_obj,CGCnvElement *base_obj,
                                     const long chart_id,
                                     const int subwindow,
                                     const string descript,
                                     const int x,
                                     const int y,
                                     const int w,
                                     const int h) : CArrowButton(GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_RIGHT,main_obj,base_obj,chart_id,subwindow,descript,x,y,w,h)
  {
   this.SetTypeElement(GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_RIGHT);
   this.SetProperty(CANV_ELEMENT_PROP_BUTTON_ARROW_SIZE,3);
  }
//+------------------------------------------------------------------+

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

//+------------------------------------------------------------------+
//| Рисует стрелку                                                   |
//+------------------------------------------------------------------+
void CArrowRightButton::DrawArrow(void)
  {
   CGCnvElement::DrawArrowRight(this.Width()/2-1,this.Height()/2,(int)this.GetProperty(CANV_ELEMENT_PROP_BUTTON_ARROW_SIZE),this.ForeColor(),this.Opacity());
  }
//+------------------------------------------------------------------+

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

Аналогичные изменения уже сделаны и в остальных классах объектов кнопок со стрелками, расположенными в файлах ArrowLeftButton.mqh, ArrowDownButton.mqh и ArrowUpButton.mqh.

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

//--- Устанавливает размер рамки (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);      }

//--- Устанавливает (1) координату X, (2) координату Y, (3) ширину, (4) высоту правой зоны прокрутки элемента
   void              SetScrollAreaRightX(const int value)      { this.SetProperty(CANV_ELEMENT_PROP_SCROLL_AREA_X_RIGHT,value);     }
   void              SetScrollAreaRightY(const int value)      { this.SetProperty(CANV_ELEMENT_PROP_SCROLL_AREA_Y_RIGHT,value);     }
   void              SetScrollAreaRightWidth(const int value)  { this.SetProperty(CANV_ELEMENT_PROP_SCROLL_AREA_WIDTH_RIGHT,value); }
   void              SetScrollAreaRightHeight(const int value) { this.SetProperty(CANV_ELEMENT_PROP_SCROLL_AREA_HEIGHT_RIGHT,value);}
//--- Устанавливает (1) координату X, (2) координату Y, (3) ширину, (4) высоту нижней зоны прокрутки элемента
   void              SetScrollAreaBottomX(const int value)     { this.SetProperty(CANV_ELEMENT_PROP_SCROLL_AREA_X_BOTTOM,value);    }
   void              SetScrollAreaBottomY(const int value)     { this.SetProperty(CANV_ELEMENT_PROP_SCROLL_AREA_Y_BOTTOM,value);    }
   void              SetScrollAreaBottomWidth(const int value) { this.SetProperty(CANV_ELEMENT_PROP_SCROLL_AREA_WIDTH_BOTTOM,value);}
   void              SetScrollAreaBottomHeight(const int value){ this.SetProperty(CANV_ELEMENT_PROP_SCROLL_AREA_HEIGHT_BOTTOM,value);}

//--- Обновляет координаты (сдвигает канвас)
   virtual bool      Move(const int x,const int y,const bool redraw=false);

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

//+------------------------------------------------------------------+
//| Устанавливает и возвращает состояние мышки относительно формы    |
//+------------------------------------------------------------------+
ENUM_MOUSE_FORM_STATE CForm::MouseFormState(const int id,const long lparam,const double dparam,const string sparam)
  {
//--- Расположение данных в ushort-значении состояния кнопок
   //---------------------------------------------------------------------------
   //   bit    |    byte   |            state            |    dec    |   hex   |
   //---------------------------------------------------------------------------
   //    0     |     0     | левая кнопка мыши           |     1     |    1    |
   //---------------------------------------------------------------------------
   //    1     |     0     | правая кнопка мыши          |     2     |    2    |
   //---------------------------------------------------------------------------
   //    2     |     0     | клавиша SHIFT               |     4     |    4    |
   //---------------------------------------------------------------------------
   //    3     |     0     | клавиша CTRL                |     8     |    8    |
   //---------------------------------------------------------------------------
   //    4     |     0     | средняя кнопка мыши         |    16     |   10    |
   //---------------------------------------------------------------------------
   //    5     |     0     | 1 доп. кнопка мыши          |    32     |   20    |
   //---------------------------------------------------------------------------
   //    6     |     0     | 2 доп. кнопка мыши          |    64     |   40    |
   //---------------------------------------------------------------------------
   //    7     |     0     | прокрутка колёсика          |    128    |   80    |
   //---------------------------------------------------------------------------
   //---------------------------------------------------------------------------
   //    0     |     1     | курсор внутри формы         |    256    |   100   |
   //---------------------------------------------------------------------------
   //    1     |     1     | курсор внутри активной зоны |    512    |   200   |
   //---------------------------------------------------------------------------
   //    2     |     1     | курсор в области управления |   1024    |   400   |
   //---------------------------------------------------------------------------
   //    3     |     1     | курсор в области прокрутки  |   2048    |   800   |
   //---------------------------------------------------------------------------
   //    4     |     1     | курсор на левой грани       |   4096    |  1000   |
   //---------------------------------------------------------------------------
   //    5     |     1     | курсор на нижней грани      |   8192    |  2000   |
   //---------------------------------------------------------------------------
   //    6     |     1     | курсор на правой грани      |   16384   |  4000   |
   //---------------------------------------------------------------------------
   //    7     |     1     | курсор на верхней грани     |   32768   |  8000   |
   //---------------------------------------------------------------------------
//--- Получаем состояние мышки относительно формы и состояние кнопок мыши и клавиш 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(this.m_mouse.CoordX(),this.m_mouse.CoordY()))
     {
      //--- Устанавливаем бит 8, отвечающий за флаг "курсор внутри формы"
      this.m_mouse_state_flags |= (0x0001<<8);
      
      //--- Если курсор внутри активной зоны - устанавливаем бит 9 "курсор внутри активной зоны"
      if(CGCnvElement::CursorInsideActiveArea(this.m_mouse.CoordX(),this.m_mouse.CoordY()))
         this.m_mouse_state_flags |= (0x0001<<9);
      //--- иначе - снимаем бит "курсор внутри активной зоны"
      else this.m_mouse_state_flags &=0xFDFF;
      
      //--- Если курсор внутри области управления - устанавливаем бит 10 "курсор внутри области управления"
      if(CGCnvElement::CursorInsideControlArea(this.m_mouse.CoordX(),this.m_mouse.CoordY()))
         this.m_mouse_state_flags |= (0x0001<<10);
      //--- иначе - снимаем бит "курсор внутри области управления"
      else this.m_mouse_state_flags &=0xFBFF;
      
      //--- Если курсор внутри области прокрутки - устанавливаем бит 11 "курсор внутри области прокрутки"
      if(CGCnvElement::CursorInsideScrollRightArea(this.m_mouse.CoordX(),this.m_mouse.CoordY()) || CGCnvElement::CursorInsideScrollBottomArea(this.m_mouse.CoordX(),this.m_mouse.CoordY()))
         this.m_mouse_state_flags |= (0x0001<<11);
      //--- иначе - снимаем бит "курсор внутри области прокрутки"
      else this.m_mouse_state_flags &=0xF7FF;
      
      //--- Если курсор на левом верхнем углу - устанавливаем бит 15 "курсор на верхней грани" и бит 12 "курсор на левой грани"
      if(CGCnvElement::CursorInsideResizeTopLeftArea(this.m_mouse.CoordX(),this.m_mouse.CoordY()))
        {
         this.m_mouse_state_flags |= (0x0001<<15);
         this.m_mouse_state_flags |= (0x0001<<12);
        }
      //--- иначе - проверяем раздельно "курсор на левой грани" и "курсор на верхней грани"
      else
        {
         //--- Если курсор на левой грани - устанавливаем бит 12 "курсор на левой грани"
         if(CGCnvElement::CursorInsideResizeLeftArea(this.m_mouse.CoordX(),this.m_mouse.CoordY()))
            this.m_mouse_state_flags |= (0x0001<<12);
         //--- иначе - снимаем бит "курсор на левой грани"
         else this.m_mouse_state_flags &=0xEFFF;
         //--- Если курсор на верхней грани - устанавливаем бит 15 "курсор на верхней грани"
         if(CGCnvElement::CursorInsideResizeTopArea(this.m_mouse.CoordX(),this.m_mouse.CoordY()))
            this.m_mouse_state_flags |= (0x0001<<15);
         //--- иначе - снимаем бит "курсор на верхней грани"
         else this.m_mouse_state_flags &=0x7FFF;
        }
      
      //--- Если курсор на правом верхнем углу - устанавливаем бит 15 "курсор на верхней грани" и бит 14 "курсор на правой грани"
      if(CGCnvElement::CursorInsideResizeTopRightArea(this.m_mouse.CoordX(),this.m_mouse.CoordY()))
        {
         this.m_mouse_state_flags |= (0x0001<<15);
         this.m_mouse_state_flags |= (0x0001<<14);
        }
      //--- иначе - проверяем раздельно "курсор на правой грани" и "курсор на верхней грани"
      else
        {
         //--- Если курсор на правой грани - устанавливаем бит 14 "курсор на правой грани"
         if(CGCnvElement::CursorInsideResizeRightArea(this.m_mouse.CoordX(),this.m_mouse.CoordY()))
            this.m_mouse_state_flags |= (0x0001<<14);
         //--- иначе - снимаем бит "курсор на правой грани"
         else this.m_mouse_state_flags &=0xBFFF;
         //--- Если курсор на верхней грани - устанавливаем бит 15 "курсор на верхней грани"
         if(CGCnvElement::CursorInsideResizeTopArea(this.m_mouse.CoordX(),this.m_mouse.CoordY()))
            this.m_mouse_state_flags |= (0x0001<<15);
         //--- иначе - снимаем бит "курсор на верхней грани"
         else this.m_mouse_state_flags &=0x7FFF;
        }
      
      //--- Если курсор на левом нижнем углу - устанавливаем бит 13 "курсор на нижней грани" и бит 12 "курсор на левой грани"
      if(CGCnvElement::CursorInsideResizeBottomLeftArea(this.m_mouse.CoordX(),this.m_mouse.CoordY()))
        {
         this.m_mouse_state_flags |= (0x0001<<13);
         this.m_mouse_state_flags |= (0x0001<<12);
        }
      //--- иначе - проверяем раздельно "курсор на левой грани" и "курсор на нижней грани"
      else
        {
         //--- Если курсор на левой грани - устанавливаем бит 12 "курсор на левой грани"
         if(CGCnvElement::CursorInsideResizeLeftArea(this.m_mouse.CoordX(),this.m_mouse.CoordY()))
            this.m_mouse_state_flags |= (0x0001<<12);
         //--- иначе - снимаем бит "курсор на левой грани"
         else this.m_mouse_state_flags &=0xEFFF;
         //--- Если курсор на нижней грани - устанавливаем бит 13 "курсор на нижней грани"
         if(CGCnvElement::CursorInsideResizeLeftArea(this.m_mouse.CoordX(),this.m_mouse.CoordY()))
            this.m_mouse_state_flags |= (0x0001<<13);
         //--- иначе - снимаем бит "курсор на нижней грани"
         else this.m_mouse_state_flags &=0xDFFF;
        }
      
      //--- Если курсор на правом нижнем углу - устанавливаем бит 13 "курсор на нижней грани" и бит 14 "курсор на правой грани"
      if(CGCnvElement::CursorInsideResizeBottomRightArea(this.m_mouse.CoordX(),this.m_mouse.CoordY()))
        {
         this.m_mouse_state_flags |= (0x0001<<13);
         this.m_mouse_state_flags |= (0x0001<<14);
        }
      //--- иначе - проверяем раздельно "курсор на правой грани" и "курсор на нижней грани"
      else
        {
         //--- Если курсор на правой грани - устанавливаем бит 14 "курсор на правой грани"
         if(CGCnvElement::CursorInsideResizeRightArea(this.m_mouse.CoordX(),this.m_mouse.CoordY()))
            this.m_mouse_state_flags |= (0x0001<<14);
         //--- иначе - снимаем бит "курсор на правой грани"
         else this.m_mouse_state_flags &=0xBFFF;
         //--- Если курсор на нижней грани - устанавливаем бит 13 "курсор на нижней грани"
         if(CGCnvElement::CursorInsideResizeLeftArea(this.m_mouse.CoordX(),this.m_mouse.CoordY()))
            this.m_mouse_state_flags |= (0x0001<<13);
         //--- иначе - снимаем бит "курсор на нижней грани"
         else this.m_mouse_state_flags &=0xDFFF;
        }
      
      //--- Если нажата одна из трёх кнопок мыши - проверяем расположение курсора в областях формы и
      //--- возвращаем соответствующее значение нажатой кнопки
      if((this.m_mouse_state_flags & 0x0001)!=0 || (this.m_mouse_state_flags & 0x0002)!=0 || (this.m_mouse_state_flags & 0x0010)!=0)
        {
         //--- Если курсор внутри формы
         if((this.m_mouse_state_flags & 0x0100)!=0)
            this.m_mouse_form_state=MOUSE_FORM_STATE_INSIDE_FORM_PRESSED;
         //--- Если курсор внутри активной зоны формы
         if((this.m_mouse_state_flags & 0x0200)!=0)
            this.m_mouse_form_state=MOUSE_FORM_STATE_INSIDE_ACTIVE_AREA_PRESSED;
         //--- Если курсор внутри области управления формы
         if((this.m_mouse_state_flags & 0x0400)!=0)
            this.m_mouse_form_state=MOUSE_FORM_STATE_INSIDE_CONTROL_AREA_PRESSED;
            
         //--- Если курсор внутри области прокрутки формы
         if((this.m_mouse_state_flags & 0x0800)!=0)
           {
            //--- Если над правой областью
            if(CGCnvElement::CursorInsideScrollRightArea(this.m_mouse.CoordX(),this.m_mouse.CoordY()))
               this.m_mouse_form_state=MOUSE_FORM_STATE_INSIDE_SCROLL_AREA_RIGHT_PRESSED;
            //--- иначе - над нижней
            else
               this.m_mouse_form_state=MOUSE_FORM_STATE_INSIDE_SCROLL_AREA_BOTTOM_PRESSED;
           }
            
         //--- Если стоят флаги курсора на верхней и левой грани
         if((this.m_mouse_state_flags & 0x8000)!=0 && (this.m_mouse_state_flags & 0x1000)!=0)
            this.m_mouse_form_state=MOUSE_FORM_STATE_INSIDE_RESIZE_TOP_LEFT_AREA_PRESSED;
         //--- Раздельно проверим флаги курсора на верхней и на левой грани
         else
           {
            //--- Если курсор на верхней грани
            if((this.m_mouse_state_flags & 0x8000)!=0)
               this.m_mouse_form_state=MOUSE_FORM_STATE_INSIDE_RESIZE_TOP_AREA_PRESSED;
            //--- Если курсор на левой грани
            if((this.m_mouse_state_flags & 0x1000)!=0)
               this.m_mouse_form_state=MOUSE_FORM_STATE_INSIDE_RESIZE_LEFT_AREA_PRESSED;
           }
         //--- Если стоят флаги курсора на верхней и правой грани
         if((this.m_mouse_state_flags & 0x8000)!=0 && (this.m_mouse_state_flags & 0x4000)!=0)
            this.m_mouse_form_state=MOUSE_FORM_STATE_INSIDE_RESIZE_TOP_RIGHT_AREA_PRESSED;
         //--- Раздельно проверим флаги курсора на верхней и на правой грани
         else
           {
            //--- Если курсор на верхней грани
            if((this.m_mouse_state_flags & 0x8000)!=0)
               this.m_mouse_form_state=MOUSE_FORM_STATE_INSIDE_RESIZE_TOP_AREA_PRESSED;
            //--- Если курсор на правой грани
            if((this.m_mouse_state_flags & 0x4000)!=0)
               this.m_mouse_form_state=MOUSE_FORM_STATE_INSIDE_RESIZE_RIGHT_AREA_PRESSED;
           }
         
         //--- Если стоят флаги курсора на нижней и левой грани
         if((this.m_mouse_state_flags & 0x2000)!=0 && (this.m_mouse_state_flags & 0x1000)!=0)
            this.m_mouse_form_state=MOUSE_FORM_STATE_INSIDE_RESIZE_BOTTOM_LEFT_AREA_PRESSED;
         //--- Раздельно проверим флаги курсора на нижней и на левой грани
         else
           {
            //--- Если курсор на нижней грани
            if((this.m_mouse_state_flags & 0x2000)!=0)
               this.m_mouse_form_state=MOUSE_FORM_STATE_INSIDE_RESIZE_BOTTOM_AREA_PRESSED;
            //--- Если курсор на левой грани
            if((this.m_mouse_state_flags & 0x1000)!=0)
               this.m_mouse_form_state=MOUSE_FORM_STATE_INSIDE_RESIZE_LEFT_AREA_PRESSED;
           }
         //--- Если стоят флаги курсора на нижней и правой грани
         if((this.m_mouse_state_flags & 0x2000)!=0 && (this.m_mouse_state_flags & 0x4000)!=0)
            this.m_mouse_form_state=MOUSE_FORM_STATE_INSIDE_RESIZE_BOTTOM_RIGHT_AREA_PRESSED;
         //--- Раздельно проверим флаги курсора на нижней и на правой грани
         else
           {
            //--- Если курсор на нижней грани
            if((this.m_mouse_state_flags & 0x2000)!=0)
               this.m_mouse_form_state=MOUSE_FORM_STATE_INSIDE_RESIZE_BOTTOM_AREA_PRESSED;
            //--- Если курсор на правой грани
            if((this.m_mouse_state_flags & 0x4000)!=0)
               this.m_mouse_form_state=MOUSE_FORM_STATE_INSIDE_RESIZE_RIGHT_AREA_PRESSED;
           }
        }
      
      //--- иначе если ни одна кнопка мышки не нажата
      else
        {
         //--- если колесо мышки прокручивается, возвращаем соответствующее значение прокрутки колеса (в активной зоне, в области управления или в области формы)
         //--- Если курсор внутри формы
         if((this.m_mouse_state_flags & 0x0100)!=0)
           {
            //--- Если прокручивается колёсико мышки
            if((this.m_mouse_state_flags & 0x0080)!=0)
               this.m_mouse_form_state=MOUSE_FORM_STATE_INSIDE_FORM_WHEEL;
            else
               this.m_mouse_form_state=MOUSE_FORM_STATE_INSIDE_FORM_NOT_PRESSED;
           }
         //--- Если курсор внутри активной зоны формы
         if((this.m_mouse_state_flags & 0x0200)!=0)
           {
            //--- Если прокручивается колёсико мышки
            if((this.m_mouse_state_flags & 0x0080)!=0)
               this.m_mouse_form_state=MOUSE_FORM_STATE_INSIDE_ACTIVE_AREA_WHEEL;
            else
               this.m_mouse_form_state=MOUSE_FORM_STATE_INSIDE_ACTIVE_AREA_NOT_PRESSED;
           }
         //--- Если курсор внутри области управления формы
         if((this.m_mouse_state_flags & 0x0400)!=0)
           {
            //--- Если прокручивается колёсико мышки
            if((this.m_mouse_state_flags & 0x0080)!=0)
               this.m_mouse_form_state=MOUSE_FORM_STATE_INSIDE_CONTROL_AREA_WHEEL;
            else
               this.m_mouse_form_state=MOUSE_FORM_STATE_INSIDE_CONTROL_AREA_NOT_PRESSED;
           }
           
           
         //--- Если курсор внутри области прокрутки формы
         if((this.m_mouse_state_flags & 0x0800)!=0)
           {
            //--- Если над правой областью
            if(CGCnvElement::CursorInsideScrollRightArea(this.m_mouse.CoordX(),this.m_mouse.CoordY()))
               this.m_mouse_form_state=MOUSE_FORM_STATE_INSIDE_SCROLL_AREA_RIGHT_NOT_PRESSED;
            //--- иначе - над нижней
            else
               this.m_mouse_form_state=MOUSE_FORM_STATE_INSIDE_SCROLL_AREA_BOTTOM_NOT_PRESSED;
           }
            
         //--- Если стоят флаги курсора на верхней и левой грани
         if((this.m_mouse_state_flags & 0x8000)!=0 && (this.m_mouse_state_flags & 0x1000)!=0)
            this.m_mouse_form_state=MOUSE_FORM_STATE_INSIDE_RESIZE_TOP_LEFT_AREA_NOT_PRESSED;
         //--- Раздельно проверим флаги курсора на верхней и на левой грани
         else
           {
            //--- Если курсор на верхней грани
            if((this.m_mouse_state_flags & 0x8000)!=0)
               this.m_mouse_form_state=MOUSE_FORM_STATE_INSIDE_RESIZE_TOP_AREA_NOT_PRESSED;
            //--- Если курсор на левой грани
            if((this.m_mouse_state_flags & 0x1000)!=0)
               this.m_mouse_form_state=MOUSE_FORM_STATE_INSIDE_RESIZE_LEFT_AREA_NOT_PRESSED;
           }
         //--- Если стоят флаги курсора на верхней и правой грани
         if((this.m_mouse_state_flags & 0x8000)!=0 && (this.m_mouse_state_flags & 0x4000)!=0)
            this.m_mouse_form_state=MOUSE_FORM_STATE_INSIDE_RESIZE_TOP_RIGHT_AREA_NOT_PRESSED;
         //--- Раздельно проверим флаги курсора на верхней и на правой грани
         else
           {
            //--- Если курсор на верхней грани
            if((this.m_mouse_state_flags & 0x8000)!=0)
               this.m_mouse_form_state=MOUSE_FORM_STATE_INSIDE_RESIZE_TOP_AREA_NOT_PRESSED;
            //--- Если курсор на правой грани
            if((this.m_mouse_state_flags & 0x4000)!=0)
               this.m_mouse_form_state=MOUSE_FORM_STATE_INSIDE_RESIZE_RIGHT_AREA_NOT_PRESSED;
           }
         
         //--- Если стоят флаги курсора на нижней и левой грани
         if((this.m_mouse_state_flags & 0x2000)!=0 && (this.m_mouse_state_flags & 0x1000)!=0)
            this.m_mouse_form_state=MOUSE_FORM_STATE_INSIDE_RESIZE_BOTTOM_LEFT_AREA_NOT_PRESSED;
         //--- Раздельно проверим флаги курсора на нижней и на левой грани
         else
           {
            //--- Если курсор на нижней грани
            if((this.m_mouse_state_flags & 0x2000)!=0)
               this.m_mouse_form_state=MOUSE_FORM_STATE_INSIDE_RESIZE_BOTTOM_AREA_NOT_PRESSED;
            //--- Если курсор на левой грани
            if((this.m_mouse_state_flags & 0x1000)!=0)
               this.m_mouse_form_state=MOUSE_FORM_STATE_INSIDE_RESIZE_LEFT_AREA_NOT_PRESSED;
           }
         //--- Если стоят флаги курсора на нижней и правой грани
         if((this.m_mouse_state_flags & 0x2000)!=0 && (this.m_mouse_state_flags & 0x4000)!=0)
            this.m_mouse_form_state=MOUSE_FORM_STATE_INSIDE_RESIZE_BOTTOM_RIGHT_AREA_NOT_PRESSED;
         //--- Раздельно проверим флаги курсора на нижней и на правой грани
         else
           {
            //--- Если курсор на нижней грани
            if((this.m_mouse_state_flags & 0x2000)!=0)
               this.m_mouse_form_state=MOUSE_FORM_STATE_INSIDE_RESIZE_BOTTOM_AREA_NOT_PRESSED;
            //--- Если курсор на правой грани
            if((this.m_mouse_state_flags & 0x4000)!=0)
               this.m_mouse_form_state=MOUSE_FORM_STATE_INSIDE_RESIZE_RIGHT_AREA_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;
  }
//+------------------------------------------------------------------+

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

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

//+------------------------------------------------------------------+
//| Обработчик событий мышки                                         |
//+------------------------------------------------------------------+
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_RIGHT_NOT_PRESSED :  this.MouseScrollAreaNotPressedHandler(id,lparam,dparam,sparam);break;
      //--- Курсор в пределах области прокрутки окна справа, нажата кнопка мышки (любая)
      case MOUSE_EVENT_INSIDE_SCROLL_AREA_RIGHT_PRESSED     :  this.MouseScrollAreaPressedHandler(id,lparam,dparam,sparam);   break;
      //--- Курсор в пределах области прокрутки окна справа, прокручивается колёсико мышки
      case MOUSE_EVENT_INSIDE_SCROLL_AREA_RIGHT_WHEEL       :  this.MouseScrollAreaWhellHandler(id,lparam,dparam,sparam);     break;
      
      //--- Курсор в пределах области прокрутки окна снизу, кнопки мышки не нажаты
      case MOUSE_EVENT_INSIDE_SCROLL_AREA_BOTTOM_NOT_PRESSED:  this.MouseScrollAreaNotPressedHandler(id,lparam,dparam,sparam);break;
      //--- Курсор в пределах области прокрутки окна снизу, нажата кнопка мышки (любая)
      case MOUSE_EVENT_INSIDE_SCROLL_AREA_BOTTOM_PRESSED    :  this.MouseScrollAreaPressedHandler(id,lparam,dparam,sparam);   break;
      //--- Курсор в пределах области прокрутки окна снизу, прокручивается колёсико мышки
      case MOUSE_EVENT_INSIDE_SCROLL_AREA_BOTTOM_WHEEL      :  this.MouseScrollAreaWhellHandler(id,lparam,dparam,sparam);     break;
      
      //--- Курсор в пределах области управления, кнопки мышки не нажаты
      case MOUSE_EVENT_INSIDE_CONTROL_AREA_NOT_PRESSED   :  this.MouseControlAreaNotPressedHandler(id,lparam,dparam,sparam);  break;
      //--- Курсор в пределах области управления, нажата кнопка мышки (любая)
      case MOUSE_EVENT_INSIDE_CONTROL_AREA_PRESSED       :  this.MouseControlAreaPressedHandler(id,lparam,dparam,sparam);     break;
      //--- Курсор в пределах области управления, прокручивается колёсико мышки
      case MOUSE_EVENT_INSIDE_CONTROL_AREA_WHEEL         :  this.MouseControlAreaWhellHandler(id,lparam,dparam,sparam);       break;
      //---MOUSE_EVENT_NO_EVENT
      default: break;
     }
   this.m_mouse_event_last=(ENUM_MOUSE_EVENT)id;
  }
//+------------------------------------------------------------------+

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

//+------------------------------------------------------------------+
//| Обработчик последнего события мышки                              |
//+------------------------------------------------------------------+
void CForm::OnMouseEventPostProcessing(void)
  {
   if(!this.IsVisible() || !this.Enabled() || !this.Displayed())
      return;
   ENUM_MOUSE_FORM_STATE state=this.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              :
      case MOUSE_FORM_STATE_NONE                            :
        if(this.MouseEventLast()==MOUSE_EVENT_INSIDE_ACTIVE_AREA_NOT_PRESSED  || 
           this.MouseEventLast()==MOUSE_EVENT_INSIDE_FORM_NOT_PRESSED         || 
           this.MouseEventLast()==MOUSE_EVENT_OUTSIDE_FORM_NOT_PRESSED        ||
           this.MouseEventLast()==MOUSE_EVENT_NO_EVENT)
          {
           this.SetBackgroundColor(this.BackgroundColorInit(),false);
           this.SetBorderColor(this.BorderColorInit(),false);
           this.m_mouse_event_last=ENUM_MOUSE_EVENT(state+MOUSE_EVENT_NO_EVENT);
          }
        break;
      //--- Курсор в пределах формы, кнопки мышки не нажаты
      //--- Курсор в пределах формы, нажата кнопка мышки (любая)
      //--- Курсор в пределах формы, прокручивается колёсико мышки
      //--- Курсор в пределах активной области, кнопки мышки не нажаты
      //--- Курсор в пределах активной области, нажата кнопка мышки (любая)
      //--- Курсор в пределах активной области, прокручивается колёсико мышки
      //--- Курсор в пределах активной области, отжата кнопка мышки (левая)
      //--- Курсор в пределах области прокрутки окна, кнопки мышки не нажаты
      //--- Курсор в пределах области прокрутки окна, нажата кнопка мышки (любая)
      //--- Курсор в пределах области прокрутки окна, прокручивается колёсико мышки
      //--- Курсор в пределах области изменения размеров окна, кнопки мышки не нажаты
      //--- Курсор в пределах области изменения размеров окна, нажата кнопка мышки (любая)
      //--- Курсор в пределах области изменения размеров окна, прокручивается колёсико мышки
      //--- Курсор в пределах области резделителя окна, кнопки мышки не нажаты
      //--- Курсор в пределах области резделителя окна, нажата кнопка мышки (любая)
      //--- Курсор в пределах области резделителя окна, прокручивается колёсико мышки
      case MOUSE_FORM_STATE_INSIDE_FORM_NOT_PRESSED                     :
      case MOUSE_FORM_STATE_INSIDE_FORM_PRESSED                         :
      case MOUSE_FORM_STATE_INSIDE_FORM_WHEEL                           :
//--- В пределах активной области
      case MOUSE_FORM_STATE_INSIDE_ACTIVE_AREA_NOT_PRESSED              :
      case MOUSE_FORM_STATE_INSIDE_ACTIVE_AREA_PRESSED                  :
      case MOUSE_FORM_STATE_INSIDE_ACTIVE_AREA_WHEEL                    :
      case MOUSE_FORM_STATE_INSIDE_ACTIVE_AREA_RELEASED                 :
//--- В пределаз области прокрутки снизу
      case MOUSE_FORM_STATE_INSIDE_SCROLL_AREA_BOTTOM_NOT_PRESSED       :
      case MOUSE_FORM_STATE_INSIDE_SCROLL_AREA_BOTTOM_PRESSED           :
      case MOUSE_FORM_STATE_INSIDE_SCROLL_AREA_BOTTOM_WHEEL             :
//--- В пределаз области прокрутки справа
      case MOUSE_FORM_STATE_INSIDE_SCROLL_AREA_RIGHT_NOT_PRESSED        :
      case MOUSE_FORM_STATE_INSIDE_SCROLL_AREA_RIGHT_PRESSED            :
      case MOUSE_FORM_STATE_INSIDE_SCROLL_AREA_RIGHT_WHEEL              :
//--- В пределах области изменения размеров окна сверху
      case MOUSE_FORM_STATE_INSIDE_RESIZE_TOP_AREA_NOT_PRESSED          :
      case MOUSE_FORM_STATE_INSIDE_RESIZE_TOP_AREA_PRESSED              :
      case MOUSE_FORM_STATE_INSIDE_RESIZE_TOP_AREA_WHEEL                :
//--- В пределах области изменения размеров окна снизу
      case MOUSE_FORM_STATE_INSIDE_RESIZE_BOTTOM_AREA_NOT_PRESSED       :
      case MOUSE_FORM_STATE_INSIDE_RESIZE_BOTTOM_AREA_PRESSED           :
      case MOUSE_FORM_STATE_INSIDE_RESIZE_BOTTOM_AREA_WHEEL             :
//--- В пределах области изменения размеров окна слева
      case MOUSE_FORM_STATE_INSIDE_RESIZE_LEFT_AREA_NOT_PRESSED         :
      case MOUSE_FORM_STATE_INSIDE_RESIZE_LEFT_AREA_PRESSED             :
      case MOUSE_FORM_STATE_INSIDE_RESIZE_LEFT_AREA_WHEEL               :
//--- В пределах области изменения размеров окна справа
      case MOUSE_FORM_STATE_INSIDE_RESIZE_RIGHT_AREA_NOT_PRESSED        :
      case MOUSE_FORM_STATE_INSIDE_RESIZE_RIGHT_AREA_PRESSED            :
      case MOUSE_FORM_STATE_INSIDE_RESIZE_RIGHT_AREA_WHEEL              :
//--- В пределах области изменения размеров окна сверху-слева
      case MOUSE_FORM_STATE_INSIDE_RESIZE_TOP_LEFT_AREA_NOT_PRESSED     :
      case MOUSE_FORM_STATE_INSIDE_RESIZE_TOP_LEFT_AREA_PRESSED         :
      case MOUSE_FORM_STATE_INSIDE_RESIZE_TOP_LEFT_AREA_WHEEL           :
//--- В пределах области изменения размеров окна сверху-справа
      case MOUSE_FORM_STATE_INSIDE_RESIZE_TOP_RIGHT_AREA_NOT_PRESSED    :
      case MOUSE_FORM_STATE_INSIDE_RESIZE_TOP_RIGHT_AREA_PRESSED        :
      case MOUSE_FORM_STATE_INSIDE_RESIZE_TOP_RIGHT_AREA_WHEEL          :
//--- В пределах области изменения размеров окна снизу-слева
      case MOUSE_FORM_STATE_INSIDE_RESIZE_BOTTOM_LEFT_AREA_NOT_PRESSED  :
      case MOUSE_FORM_STATE_INSIDE_RESIZE_BOTTOM_LEFT_AREA_PRESSED      :
      case MOUSE_FORM_STATE_INSIDE_RESIZE_BOTTOM_LEFT_AREA_WHEEL        :
//--- В пределах области изменения размеров окна снизу-справа
      case MOUSE_FORM_STATE_INSIDE_RESIZE_BOTTOM_RIGHT_AREA_NOT_PRESSED :
      case MOUSE_FORM_STATE_INSIDE_RESIZE_BOTTOM_RIGHT_AREA_PRESSED     :
      case MOUSE_FORM_STATE_INSIDE_RESIZE_BOTTOM_RIGHT_AREA_WHEEL       :
//--- В пределах области управления
      case MOUSE_FORM_STATE_INSIDE_CONTROL_AREA_NOT_PRESSED             :
      case MOUSE_FORM_STATE_INSIDE_CONTROL_AREA_PRESSED                 :
      case MOUSE_FORM_STATE_INSIDE_CONTROL_AREA_WHEEL                   :
        break;
      //---MOUSE_EVENT_NO_EVENT
      default:
        break;
     }
  }
//+------------------------------------------------------------------+

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

//+------------------------------------------------------------------+
//| Отправляет сообщение о событии                                   |
//+------------------------------------------------------------------+
bool CForm::SendEvent(const long chart_id,const ushort event_id)
  {
   //--- Создаём событие:
   //--- Получаем базовый и главный объекты
   CGCnvElement *base=this.GetBase();
   CGCnvElement *main=this.GetMain();
   //--- находим имена главного и базового объектов
   string name_main=(main!=NULL ? main.Name() : this.IsMain() ? this.Name() : "Lost name of object");
   string name_base=(base!=NULL ? base.Name() : "Lost name of object");
   ENUM_GRAPH_ELEMENT_TYPE base_base_type=(base!=NULL ? (base.GetBase()!=NULL ? base.GetBase().TypeGraphElement() : base.TypeGraphElement()) : this.TypeGraphElement());
   //--- в long-параметре события передаём идентификатор объекта
   //--- в double-параметре события передаём тип объекта
   //--- в string-параметре события передаём имена главного, базового и текущего объектов, разделённые символом ";"
   long lp=this.ID();
   double dp=base_base_type;
   string sp=::StringSubstr(name_main,::StringLen(this.NamePrefix()))+";"+
             ::StringSubstr(name_base,::StringLen(this.NamePrefix()))+";"+
             ::StringSubstr(this.Name(),::StringLen(this.NamePrefix()));
   //--- Отправляем на график управляющей программы событие щелчка по элементу управления
   bool res=true;
   ::ResetLastError();
   res=::EventChartCustom(chart_id,event_id,lp,dp,sp);
   if(res)
      return true;
   ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_FAILED_ENQUEUE_EVENT),". ",CMessage::Text(MSG_LIB_SYS_ERROR),": ",CMessage::Text(::GetLastError()));
   return false;
  }
//+------------------------------------------------------------------+

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

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

В файле \MQL5\Include\DoEasy\Objects\Graph\WForms\WinFormBase.mqh класса базового WinForms-объекта из метода установки новых размеров удалим блок кода, корректирующий размеры и координаты полос прокрутки:

//+------------------------------------------------------------------+
//| Устанавливает новые размеры текущему объекту                     |
//+------------------------------------------------------------------+
bool CWinFormBase::Resize(const int w,const int h,const bool redraw)
  {
//--- Если ширина и высота объекта равны переданным - возвращаем true
   if(this.Width()==w && this.Height()==h)
      return true;
//--- Объявляем переменную с результатом изменения свойства
   bool res=true;
//--- Запоминаем изначальные размеры панели
   int prev_w=this.Width();
   int prev_h=this.Height();
//--- В переменную res записываем результат изменения свойств
//--- (если величина свойства не равна переданной величине)
   if(this.Width()!=w)
      res &=this.SetWidth(w);
   if(this.Height()!=h)
      res &=this.SetHeight(h);
   if(!res)
      return false;
//--- Полчаем вертикальную полосу прокрутки и, если есть
   CWinFormBase *scroll_v=this.GetElementByType(GRAPH_ELEMENT_TYPE_WF_SCROLL_BAR_VERTICAL,0);
   if(scroll_v!=NULL)
     {
      //--- изменяем вертикальный размер под размер рабочей области контейнера
      scroll_v.Resize(scroll_v.Width(),this.Height()-this.BorderSizeTop()-this.BorderSizeBottom(),false);
      //--- Получаем из объекта-вертикальной полосы прокрутки объект-кнопку со стрелкой вниз
      CWinFormBase *arr_d=scroll_v.GetElementByType(GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_DOWN,0);
      //--- Если кнопка получена
      if(arr_d!=NULL)
        {
         //--- Перемещаем её к нижнему краю вертикальной полосы прокрутки
         if(arr_d.Move(arr_d.CoordX(),scroll_v.BottomEdge()-2*arr_d.Height()))
           {
            arr_d.SetCoordXRelative(arr_d.CoordX()-scroll_v.CoordX());
            arr_d.SetCoordYRelative(arr_d.CoordY()-scroll_v.CoordY());
           }
        }
     }
//--- Полчаем горизонтальную полосу прокрутки и, если есть
   CWinFormBase *scroll_h=this.GetElementByType(GRAPH_ELEMENT_TYPE_WF_SCROLL_BAR_HORISONTAL,0);
   if(scroll_h!=NULL)
     {
      //--- изменяем горизонтальный размер под размер рабочей области контейнера
      scroll_h.Resize(this.Width()-this.BorderSizeLeft()-this.BorderSizeRight(),scroll_h.Height(),false);
      //--- Получаем из объекта-горизонтальной полосы прокрутки объект-кнопку со стрелкой вправо
      CWinFormBase *arr_r=scroll_h.GetElementByType(GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_RIGHT,0);
      //--- Если кнопка получена
      if(arr_r!=NULL)
        {
         //--- Перемещаем её к правому краю горизонтальной полосы прокрутки
         if(arr_r.Move(scroll_h.RightEdge()-2*arr_r.Width(),arr_r.CoordY()))
           {
            arr_r.SetCoordXRelative(arr_r.CoordX()-scroll_h.CoordX());
            arr_r.SetCoordYRelative(arr_r.CoordY()-scroll_h.CoordY());
           }
        }
     }
//--- Рассчитываем величину, на которую нужно изменить размеры
   int excess_w=this.Width()-prev_w;
   int excess_h=this.Height()-prev_h;
//--- Получим объект "Тень"
   CShadowObj *shadow=this.GetShadowObj();
//--- Если у объекта есть тень, и объект "Тень" получен
   if(this.IsShadow() && shadow!=NULL)
     {
      //--- запомним смещения тени по X и Y,
      int x=shadow.CoordXRelative();
      int y=shadow.CoordYRelative();
      //--- установим новые ширину и высоту тени
      res &=shadow.SetWidth(shadow.Width()+excess_w);
      res &=shadow.SetHeight(shadow.Height()+excess_h);
      //--- Если в переменной res записано значение false -
      //--- значит была ошибка изменения размеров - возвращаем false
      if(!res)
         return false;
      //--- Если не нужно перерисовывать - сотрём тень
      if(!redraw)
         shadow.Erase();
      //--- Запишем ранее сохранённые значения смещения тени относительно панели
      shadow.SetCoordXRelative(x);
      shadow.SetCoordYRelative(y);
     }
//--- Перерисуем весь элемент с новыми размерами
   if(redraw)
      this.Redraw(true);
//--- Всё успешно - возвращаем true
   return true;
  }
//+------------------------------------------------------------------+

Этот блок кода будет целиком перенесён в класс объекта-контейнера.

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

//+------------------------------------------------------------------+
//| Возвращает описание целочисленного свойства элемента             |
//+------------------------------------------------------------------+
string CWinFormBase::GetPropertyDescription(ENUM_CANV_ELEMENT_PROP_INTEGER property,bool only_prop=false)
  {
   return
     (
      property==CANV_ELEMENT_PROP_ID                           ?  CMessage::Text(MSG_CANV_ELEMENT_PROP_ID)+
         (only_prop ? "" : !this.SupportProperty(property)     ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)this.GetProperty(property)
         )  :
      property==CANV_ELEMENT_PROP_TYPE                         ?  CMessage::Text(MSG_CANV_ELEMENT_PROP_TYPE)+
         (only_prop ? "" : !this.SupportProperty(property)     ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+this.TypeElementDescription()
         )  :

      //---...
      //---...


      property==CANV_ELEMENT_PROP_ENABLED                      ?  CMessage::Text(MSG_CANV_ELEMENT_PROP_ENABLED)+
         (only_prop ? "" : !this.SupportProperty(property)     ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)(bool)this.GetProperty(property)
         )  :
      property==CANV_ELEMENT_PROP_RESIZABLE                    ?  CMessage::Text(MSG_CANV_ELEMENT_PROP_RESIZABLE)+
         (only_prop ? "" : !this.SupportProperty(property)     ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)(bool)this.GetProperty(property)
         )  :
      property==CANV_ELEMENT_PROP_FORE_COLOR                   ?  CMessage::Text(MSG_CANV_ELEMENT_PROP_FORE_COLOR)+
         (only_prop ? "" : !this.SupportProperty(property)     ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+ColorToString((color)this.GetProperty(property),true)
         )  :

      //---...
      //---...

      property==CANV_ELEMENT_PROP_PROGRESS_BAR_MARQUEE_ANIM_SPEED ?  CMessage::Text(MSG_CANV_ELEMENT_PROP_PROGRESS_BAR_MARQUEE_ANIM_SPEED)+
         (only_prop ? "" : !this.SupportProperty(property)     ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)this.GetProperty(property)
         )  :
      property==CANV_ELEMENT_PROP_BUTTON_ARROW_SIZE            ?  CMessage::Text(MSG_CANV_ELEMENT_PROP_BUTTON_ARROW_SIZE)+
         (only_prop ? "" : !this.SupportProperty(property)     ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)this.GetProperty(property)
         )  :
      ""
     );
  }
//+------------------------------------------------------------------+


Класс области захвата элемента управления ScrollBar

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

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

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

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

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

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

protected:
//--- Защищённый конструктор с указанием типа объекта, идентификатора чарта и подокна
                     CScrollBarThumb(const ENUM_GRAPH_ELEMENT_TYPE type,
                                     CGCnvElement *main_obj,CGCnvElement *base_obj,
                                     const long chart_id,
                                     const int subwindow,
                                     const string descript,
                                     const int x,
                                     const int y,
                                     const int w,
                                     const int h);
                             
public:
//--- Конструктор
                     CScrollBarThumb(CGCnvElement *main_obj,CGCnvElement *base_obj,
                                     const long chart_id,
                                     const int subwindow,
                                     const string descript,
                                     const int x,
                                     const int y,
                                     const int w,
                                     const int h);
  };
//+------------------------------------------------------------------+

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

Защищённый конструктор:

//+------------------------------------------------------------------+
//| Защищённый конструктор с указанием типа объекта,                 |
//| идентификатора чарта и подокна                                   |
//+------------------------------------------------------------------+
CScrollBarThumb::CScrollBarThumb(const ENUM_GRAPH_ELEMENT_TYPE type,
                                 CGCnvElement *main_obj,CGCnvElement *base_obj,
                                 const long chart_id,
                                 const int subwindow,
                                 const string descript,
                                 const int x,
                                 const int y,
                                 const int w,
                                 const int h) : CButton(type,main_obj,base_obj,chart_id,subwindow,descript,x,y,w,h)
  {
//--- Установим объекту указанный тип графического элемента, а тип объекта библиотеки - как тип этого объекта
   this.SetTypeElement(type);
   this.m_type=OBJECT_DE_TYPE_GWF_HELPER;
   this.SetBackgroundColor(CLR_DEF_CONTROL_SCROLL_BAR_THUMB_COLOR,true);
   this.SetBackgroundColorMouseDown(CLR_DEF_CONTROL_SCROLL_BAR_THUMB_MOUSE_DOWN);
   this.SetBackgroundColorMouseOver(CLR_DEF_CONTROL_SCROLL_BAR_THUMB_MOUSE_OVER);
   this.SetForeColor(CLR_DEF_CONTROL_SCROLL_BAR_THUMB_FORE_COLOR,true);
   this.SetForeColorMouseDown(CLR_DEF_CONTROL_SCROLL_BAR_THUMB_FORE_MOUSE_DOWN);
   this.SetForeColorMouseOver(CLR_DEF_CONTROL_SCROLL_BAR_THUMB_FORE_MOUSE_OVER);
  }
//+------------------------------------------------------------------+

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

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

//+------------------------------------------------------------------+
//| Конструктор с указанием главного и базового объектов,            |
//| идентификатора чарта и подокна                                   |
//+------------------------------------------------------------------+
CScrollBarThumb::CScrollBarThumb(CGCnvElement *main_obj,CGCnvElement *base_obj,
                                 const long chart_id,
                                 const int subwindow,
                                 const string descript,
                                 const int x,
                                 const int y,
                                 const int w,
                                 const int h) : CButton(GRAPH_ELEMENT_TYPE_WF_SCROLL_BAR_THUMB,main_obj,base_obj,chart_id,subwindow,descript,x,y,w,h)
  {
   this.SetTypeElement(GRAPH_ELEMENT_TYPE_WF_SCROLL_BAR_THUMB);
   this.m_type=OBJECT_DE_TYPE_GWF_HELPER;
   this.SetBackgroundColor(CLR_DEF_CONTROL_SCROLL_BAR_THUMB_COLOR,true);
   this.SetBackgroundColorMouseDown(CLR_DEF_CONTROL_SCROLL_BAR_THUMB_MOUSE_DOWN);
   this.SetBackgroundColorMouseOver(CLR_DEF_CONTROL_SCROLL_BAR_THUMB_MOUSE_OVER);
   this.SetForeColor(CLR_DEF_CONTROL_SCROLL_BAR_THUMB_FORE_COLOR,true);
   this.SetForeColorMouseDown(CLR_DEF_CONTROL_SCROLL_BAR_THUMB_FORE_MOUSE_DOWN);
   this.SetForeColorMouseOver(CLR_DEF_CONTROL_SCROLL_BAR_THUMB_FORE_MOUSE_OVER);
  }
//+------------------------------------------------------------------+

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

Доработаем класс объекта абстрактной полосы прокрутки в файле \MQL5\Include\DoEasy\Objects\Graph\WForms\Helpers\ScrollBar.mqh.

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

//+------------------------------------------------------------------+
//|                                                    ScrollBar.mqh |
//|                                  Copyright 2022, MetaQuotes Ltd. |
//|                             https://mql5.com/ru/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2022, MetaQuotes Ltd."
#property link      "https://mql5.com/ru/users/artmedia70"
#property version   "1.00"
#property strict    // Нужно для mql4
//+------------------------------------------------------------------+
//| Включаемые файлы                                                 |
//+------------------------------------------------------------------+
#include "..\WinFormBase.mqh"
#include "ScrollBarThumb.mqh"
#include "ArrowDownButton.mqh"
#include "ArrowUpButton.mqh"
#include "ArrowLeftButton.mqh"
#include "ArrowRightButton.mqh"
//+------------------------------------------------------------------+
//| Класс объекта CScrollBar элементов управления WForms             |
//+------------------------------------------------------------------+
class CScrollBar : public CWinFormBase
  {
private:
   int               m_thickness;         // Толщина полосы прокрутки (для вертикальной - Width, для горизонтальной - Height)
//--- Создаёт объекты ArrowButton
   virtual void      CreateArrowButtons(const int width,const int height) { return; }
//--- Создаёт новый графический объект
   virtual CGCnvElement *CreateNewGObject(const ENUM_GRAPH_ELEMENT_TYPE type,
                                          const int element_num,
                                          const string descript,
                                          const int x,
                                          const int y,
                                          const int w,
                                          const int h,
                                          const color colour,
                                          const uchar opacity,
                                          const bool movable,
                                          const bool activity);
//--- Рассчитывает размер области захвата
   virtual int       CalculateThumbAreaSize(void);
//--- Инициализирует свойства элемента
   void              Initialize(void);

protected:
//--- Устанавливает размеры кнопкам прокрутки
   void              SetArrowButtonsSize(const int size);
//--- Создаёт объект-область захвата
   virtual void      CreateThumbArea(void);
//--- Защищённый конструктор с указанием типа объекта, идентификатора чарта и подокна
                     CScrollBar(const ENUM_GRAPH_ELEMENT_TYPE type,
                                CGCnvElement *main_obj,CGCnvElement *base_obj,
                                const long chart_id,
                                const int subwindow,
                                const string descript,
                                const int x,
                                const int y,
                                const int w,
                                const int h);
public:
//--- Поддерживаемые свойства объекта (1) целочисленные, (2) вещественные, (3) строковые
   virtual bool      SupportProperty(ENUM_CANV_ELEMENT_PROP_INTEGER property) { return true; }
   virtual bool      SupportProperty(ENUM_CANV_ELEMENT_PROP_DOUBLE property)  { return true; }
   virtual bool      SupportProperty(ENUM_CANV_ELEMENT_PROP_STRING property)  { return true; }
   
//--- Возвращает объект-область захвата
   CScrollBarThumb  *GetThumb(void) { return this.GetElementByType(GRAPH_ELEMENT_TYPE_WF_SCROLL_BAR_THUMB,0);  }
   
//--- (1) Устанавливает, (2) возвращает толщину полосы прокрутки
   virtual void      SetThickness(const int value);
   int               Thickness(void)                        const { return this.m_thickness; }
//--- Устанавливает размер рисуемых стрелок на кнопках прокрутки
   void              SetArrowSize(const uchar size);
   
//--- Конструктор
                     CScrollBar(CGCnvElement *main_obj,CGCnvElement *base_obj,
                                const long chart_id,
                                const int subwindow,
                                const string descript,
                                const int x,
                                const int y,
                                const int w,
                                const int h);
//--- Таймер
   virtual void      OnTimer(void);
  };
//+------------------------------------------------------------------+

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

//+------------------------------------------------------------------+
//| Инициализирует свойства элемента                                 |
//+------------------------------------------------------------------+
void CScrollBar::Initialize(void)
  {
   this.SetBorderSizeAll(1);
   this.SetBorderStyle(FRAME_STYLE_SIMPLE);
   this.SetBackgroundColor(CLR_DEF_CONTROL_SCROLL_BAR_TRACK_BACK_COLOR,true);
   this.SetBackgroundColorMouseDown(CLR_DEF_CONTROL_SCROLL_BAR_TRACK_BACK_COLOR);
   this.SetBackgroundColorMouseOver(CLR_DEF_CONTROL_SCROLL_BAR_TRACK_BACK_COLOR);
   this.SetBorderColor(CLR_DEF_CONTROL_SCROLL_BAR_TRACK_BORDER_COLOR,true);
   this.SetForeColor(CLR_DEF_CONTROL_SCROLL_BAR_TRACK_FORE_COLOR,true);
   this.SetForeColorMouseDown(CLR_DEF_CONTROL_SCROLL_BAR_TRACK_FORE_MOUSE_DOWN);
   this.SetForeColorMouseOver(CLR_DEF_CONTROL_SCROLL_BAR_TRACK_FORE_MOUSE_OVER);
   this.SetThickness(DEF_CONTROL_SCROLL_BAR_WIDTH);
   this.SetArrowButtonsSize(this.m_thickness);
   this.SetArrowSize(uchar(this.m_thickness<4 ? 1 : this.m_thickness<6 ? 2 : this.m_thickness<8 ? 3 : this.m_thickness<10 ? 4 : this.m_thickness<12 ? 5 : 6));
  }
//+------------------------------------------------------------------+

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

//+------------------------------------------------------------------+
//| Создаёт новый графический объект                                 |
//+------------------------------------------------------------------+
CGCnvElement *CScrollBar::CreateNewGObject(const ENUM_GRAPH_ELEMENT_TYPE type,
                                           const int obj_num,
                                           const string descript,
                                           const int x,
                                           const int y,
                                           const int w,
                                           const int h,
                                           const color colour,
                                           const uchar opacity,
                                           const bool movable,
                                           const bool activity)
  {
   CGCnvElement *element=NULL;
   switch(type)
     {
      case GRAPH_ELEMENT_TYPE_WF_SCROLL_BAR_THUMB     :
         element=new CScrollBarThumb(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h);
        break;
      case GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_DOWN    :
         element=new CArrowDownButton(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h);
        break;
      case GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_UP      :
         element=new CArrowUpButton(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h);
        break;
      case GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_LEFT    :
         element=new CArrowLeftButton(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h);
        break;
      case GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_RIGHT   :
         element=new CArrowRightButton(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h);
        break;
      default:
        break;
     }
   if(element==NULL)
      ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_FAILED_CREATE_ELM_OBJ),this.TypeElementDescription(type));
   return element;
  }
//+------------------------------------------------------------------+

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

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

//+------------------------------------------------------------------+
//| Устанавливает толщину полосы прокрутки                           |
//+------------------------------------------------------------------+
void CScrollBar::SetThickness(const int value)
  {
//--- В зависимости от типа
   switch(this.TypeGraphElement())
     {
      //--- Для вертикальной полосы прокрутки
      case GRAPH_ELEMENT_TYPE_WF_SCROLL_BAR_VERTICAL  :
        //--- устанавливаем толщину равной переданному в метод значению и записываем её как ширину объекта
        this.m_thickness=value;
        this.SetWidth(this.m_thickness);
        this.Redraw(false);
        break;
      //--- Для горизонтальной полосы прокрутки
      case GRAPH_ELEMENT_TYPE_WF_SCROLL_BAR_HORISONTAL:
        //--- устанавливаем толщину равной переданному в метод значению и записываем её как высоту объекта
        this.m_thickness=value;
        this.SetHeight(this.m_thickness);
        this.Redraw(false);
        break;
      default:
        break;
     }
  }
//+------------------------------------------------------------------+

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

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

//+------------------------------------------------------------------+
//| Устанавливает размеры кнопкам прокрутки                          |
//+------------------------------------------------------------------+
void CScrollBar::SetArrowButtonsSize(const int size)
  {
//--- Объявляем указатели на кнопки со стрелками
   CArrowUpButton    *bu=NULL;
   CArrowDownButton  *bd=NULL;
   CArrowLeftButton  *bl=NULL;
   CArrowRightButton *br=NULL;
//--- В зависимости от типа
   switch(this.TypeGraphElement())
     {
      //--- Для вертикальной полосы прокрутки
      case GRAPH_ELEMENT_TYPE_WF_SCROLL_BAR_VERTICAL  :
        //--- Получаем указатель на кнопку со стрелкой вверх и устанавливаем её размеры, равные size
        bu=this.GetElementByType(GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_UP,0);
        if(bu!=NULL)
           bu.Resize(size,size,false);
        //--- Получаем указатель на кнопку со стрелкой вниз и устанавливаем её размеры, равные size
        bd=this.GetElementByType(GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_DOWN,0);
        if(bd!=NULL)
           bd.Resize(size,size,false);
        break;
      //--- Для горизонтальной полосы прокрутки
      case GRAPH_ELEMENT_TYPE_WF_SCROLL_BAR_HORISONTAL:
        //--- Получаем указатель на кнопку со стрелкой влево и устанавливаем её размеры, равные size
        bl=this.GetElementByType(GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_LEFT,0);
        if(bl!=NULL)
           bl.Resize(size,size,false);
        //--- Получаем указатель на кнопку со стрелкой вправо и устанавливаем её размеры, равные size
        br=this.GetElementByType(GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_RIGHT,0);
        if(br!=NULL)
           br.Resize(size,size,false);
        break;
      default:
        break;
     }
  }
//+------------------------------------------------------------------+

Кнопки у объекта полосы прокрутки имеют равные стороны. В метод передаётся размер и, в зависимости от типа объекта, получаем указатели на кнопки влево/вправо или вверх/вниз и устанавливаем для них переданный в метод размер — и для ширины, и для высоты.

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

//+------------------------------------------------------------------+
//| Устанавливает размер рисуемых стрелок на кнопках прокрутки       |
//+------------------------------------------------------------------+
void CScrollBar::SetArrowSize(const uchar size)
  {
//--- Объявляем указатели на кнопки со стрелками
   CArrowUpButton    *bu=NULL;
   CArrowDownButton  *bd=NULL;
   CArrowLeftButton  *bl=NULL;
   CArrowRightButton *br=NULL;
//--- В зависимости от типа
   switch(this.TypeGraphElement())
     {
      //--- Для вертикальной полосы прокрутки
      case GRAPH_ELEMENT_TYPE_WF_SCROLL_BAR_VERTICAL  :
        //--- Получаем указатель на кнопку со стрелкой вверх и устанавливаем размер её стрелки, равный size
        bu=this.GetElementByType(GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_UP,0);
        if(bu!=NULL)
          {
           bu.SetProperty(CANV_ELEMENT_PROP_BUTTON_ARROW_SIZE,size);
           bu.Redraw(false);
          }
        //--- Получаем указатель на кнопку со стрелкой вниз и устанавливаем размер её стрелки, равный size
        bd=this.GetElementByType(GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_DOWN,0);
        if(bd!=NULL)
          {
           bd.SetProperty(CANV_ELEMENT_PROP_BUTTON_ARROW_SIZE,size);
           bd.Redraw(false);
          }
        break;
      //--- Для горизонтальной полосы прокрутки
      case GRAPH_ELEMENT_TYPE_WF_SCROLL_BAR_HORISONTAL:
        //--- Получаем указатель на кнопку со стрелкой влево и устанавливаем размер её стрелки, равный size
        bl=this.GetElementByType(GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_LEFT,0);
        if(bl!=NULL)
          {
           bl.SetProperty(CANV_ELEMENT_PROP_BUTTON_ARROW_SIZE,size);
           bl.Redraw(false);
          }
        //--- Получаем указатель на кнопку со стрелкой вправо и устанавливаем размер её стрелки, равный size
        br=this.GetElementByType(GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_RIGHT,0);
        if(br!=NULL)
          {
           br.SetProperty(CANV_ELEMENT_PROP_BUTTON_ARROW_SIZE,size);
           br.Redraw(false);
          }
        break;
      default:
        break;
     }
  }
//+------------------------------------------------------------------+

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

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

//+------------------------------------------------------------------+
//|                                            ScrollBarVertical.mqh |
//|                                  Copyright 2022, MetaQuotes Ltd. |
//|                             https://mql5.com/ru/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2022, MetaQuotes Ltd."
#property link      "https://mql5.com/ru/users/artmedia70"
#property version   "1.00"
#property strict    // Нужно для mql4
//+------------------------------------------------------------------+
//| Включаемые файлы                                                 |
//+------------------------------------------------------------------+
#include "ScrollBarThumb.mqh"
#include "ArrowDownButton.mqh"
#include "ArrowUpButton.mqh"
#include "ScrollBar.mqh"
//+------------------------------------------------------------------+
//| Класс объекта CScrollBarVertical элементов управления WForms     |
//+------------------------------------------------------------------+
class CScrollBarVertical : public CScrollBar
  {
private:
//--- Создаёт объекты ArrowButton
   virtual void      CreateArrowButtons(const int width,const int height);
//--- Рассчитывает размер области захвата
   virtual int       CalculateThumbAreaSize(void);

protected:
//--- Защищённый конструктор с указанием типа объекта, идентификатора чарта и подокна
                     CScrollBarVertical(const ENUM_GRAPH_ELEMENT_TYPE type,
                                        CGCnvElement *main_obj,CGCnvElement *base_obj,
                                        const long chart_id,
                                        const int subwindow,
                                        const string descript,
                                        const int x,
                                        const int y,
                                        const int w,
                                        const int h);
public:
//--- Поддерживаемые свойства объекта (1) целочисленные, (2) вещественные, (3) строковые
   virtual bool      SupportProperty(ENUM_CANV_ELEMENT_PROP_INTEGER property) { return true; }
   virtual bool      SupportProperty(ENUM_CANV_ELEMENT_PROP_DOUBLE property)  { return true; }
   virtual bool      SupportProperty(ENUM_CANV_ELEMENT_PROP_STRING property)  { return true; }
   
//--- Возвращает кнопку со стрелкой (1) вверх, (2) вниз
   CArrowUpButton   *GetArrowButtonUp(void)     { return this.GetElementByType(GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_UP,0);      }
   CArrowDownButton *GetArrowButtonDown(void)   { return this.GetElementByType(GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_DOWN,0);    }
   
//--- Конструктор
                     CScrollBarVertical(CGCnvElement *main_obj,CGCnvElement *base_obj,
                                        const long chart_id,
                                        const int subwindow,
                                        const string descript,
                                        const int x,
                                        const int y,
                                        const int w,
                                        const int h);
//--- Таймер
   virtual void      OnTimer(void);
//--- Обработчик событий
   virtual void      OnChartEvent(const int id,const long& lparam,const double& dparam,const string& sparam);
  };
//+------------------------------------------------------------------+

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

//+------------------------------------------------------------------+
//| Создаёт объекты ArrowButton                                      |
//+------------------------------------------------------------------+
void CScrollBarVertical::CreateArrowButtons(const int width,const int height)
  {
//--- Устанавливаем размер кнопок, равный толщине полосы прокрутки без величины её рамки
   int size=this.Thickness()-this.BorderSizeLeft()-this.BorderSizeRight();
//--- Создаём кнопки со стрелками вверх и вниз и объект-область захвата. Размер стрелки устанавливаем равным 2
   this.CreateNewElement(GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_UP,  0,0,size,size,this.BackgroundColor(),255,true,false);
   this.CreateNewElement(GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_DOWN,0,this.BottomEdge()-this.BorderSizeBottom()-2*size,size,size,this.BackgroundColor(),255,true,false);
   this.CreateNewElement(GRAPH_ELEMENT_TYPE_WF_SCROLL_BAR_THUMB,0,this.Height()/2-height,size,30,CLR_DEF_CONTROL_SCROLL_BAR_THUMB_COLOR,255,true,false);
   this.SetArrowSize(2);
//--- Получаем указатель на кнопку со стрелкой вверх и устанавливаем для неё цвета различных её состояний
   CArrowUpButton *bu=this.GetArrowButtonUp();
   if(bu!=NULL)
     {
      bu.SetBackgroundColor(CLR_DEF_CONTROL_SCROLL_BAR_BUTT_COLOR,true);
      bu.SetBackgroundColorMouseDown(CLR_DEF_CONTROL_SCROLL_BAR_BUTT_MOUSE_DOWN);
      bu.SetBackgroundColorMouseOver(CLR_DEF_CONTROL_SCROLL_BAR_BUTT_MOUSE_OVER);
      bu.SetForeColor(CLR_DEF_CONTROL_SCROLL_BAR_BUTT_FORE_COLOR,true);
      bu.SetForeColorMouseDown(CLR_DEF_CONTROL_SCROLL_BAR_BUTT_FORE_MOUSE_DOWN);
      bu.SetForeColorMouseOver(CLR_DEF_CONTROL_SCROLL_BAR_BUTT_FORE_MOUSE_OVER);
     }
//--- Получаем указатель на кнопку со стрелкой вниз и устанавливаем для неё цвета различных её состояний
   CArrowDownButton *bd=this.GetArrowButtonDown();
   if(bd!=NULL)
     {
      bd.SetBackgroundColor(CLR_DEF_CONTROL_SCROLL_BAR_BUTT_COLOR,true);
      bd.SetBackgroundColorMouseDown(CLR_DEF_CONTROL_SCROLL_BAR_BUTT_MOUSE_DOWN);
      bd.SetBackgroundColorMouseOver(CLR_DEF_CONTROL_SCROLL_BAR_BUTT_MOUSE_OVER);
      bd.SetForeColor(CLR_DEF_CONTROL_SCROLL_BAR_BUTT_FORE_COLOR,true);
      bd.SetForeColorMouseDown(CLR_DEF_CONTROL_SCROLL_BAR_BUTT_FORE_MOUSE_DOWN);
      bd.SetForeColorMouseOver(CLR_DEF_CONTROL_SCROLL_BAR_BUTT_FORE_MOUSE_OVER);
     }
//--- Получаем указатель на объект-область захвата и устанавливаем для него цвета различных его состояний
   CScrollBarThumb *th=this.GetThumb();
   if(th!=NULL)
     {
      th.SetBackgroundColor(CLR_DEF_CONTROL_SCROLL_BAR_THUMB_COLOR,true);
      th.SetBorderColor(CLR_DEF_CONTROL_SCROLL_BAR_THUMB_BORDER_COLOR,true);
      th.SetBackgroundColorMouseDown(CLR_DEF_CONTROL_SCROLL_BAR_THUMB_MOUSE_DOWN);
      th.SetBackgroundColorMouseOver(CLR_DEF_CONTROL_SCROLL_BAR_THUMB_MOUSE_OVER);
      th.SetForeColor(CLR_DEF_CONTROL_SCROLL_BAR_THUMB_FORE_COLOR,true);
      th.SetForeColorMouseDown(CLR_DEF_CONTROL_SCROLL_BAR_THUMB_FORE_MOUSE_DOWN);
      th.SetForeColorMouseOver(CLR_DEF_CONTROL_SCROLL_BAR_THUMB_FORE_MOUSE_OVER);
     }
  }
//+------------------------------------------------------------------+

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

//+------------------------------------------------------------------+
//| Обработчик событий                                               |
//+------------------------------------------------------------------+
void CScrollBarVertical::OnChartEvent(const int id,const long &lparam,const double &dparam,const string &sparam)
  {
//--- Корректируем смещение по Y для подокна
   CGCnvElement::OnChartEvent(id,lparam,dparam,sparam);
//--- Если идентификатор события - перемещение объекта
   if(id==WF_CONTROL_EVENT_MOVING)
     {
      //--- Получаем указатель на объект-область захвата
      CScrollBarThumb *thumb=this.GetThumb();
      if(thumb==NULL)
         return;
      //--- Получаем указатель на объект-кнопку со стрелкой вверх
      CArrowUpButton *buttu=this.GetArrowButtonUp();
      if(buttu==NULL)
         return;
      //--- Получаем указатель на объект-кнопку со стрелкой вниз
      CArrowDownButton *buttd=this.GetArrowButtonDown();
      if(buttd==NULL)
         return;
      //--- Объявляем переменные для координат области захвата
      int x=(int)lparam;
      int y=(int)dparam;
      //--- Устанавливаем координату X равной координате X элемента управления
      x=this.CoordX()+this.BorderSizeLeft();
      //--- Корректируем координату Y так, чтобы область захвата не выходила за пределы элемента управления с учётом кнопок со стрелками
      if(y<buttu.BottomEdge())
        y=buttu.BottomEdge();
      if(y>buttd.CoordY()-thumb.Height())
        y=buttd.CoordY()-thumb.Height();
      //--- Если объект-область захвата смещён на рассчитанные координаты
      if(thumb.Move(x,y,true))
        {
         //--- устанавливаем объекту его относительные координаты
         thumb.SetCoordXRelative(thumb.CoordX()-this.CoordX());
         thumb.SetCoordYRelative(thumb.CoordY()-this.CoordY());
         ::ChartRedraw(this.ChartID());
        }
     }
  }
//+------------------------------------------------------------------+

Аналогичные доработки сделаны в классе объекта-горизонтальной полосы прокрутки в файле ScrollBarHorisontal.mqh.

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

//+------------------------------------------------------------------+
//| Создаёт объекты ArrowButton                                      |
//+------------------------------------------------------------------+
void CScrollBarHorisontal::CreateArrowButtons(const int width,const int height)
  {
   int size=this.Thickness()-this.BorderSizeTop()-this.BorderSizeBottom();
   this.CreateNewElement(GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_LEFT,  0,0,size,size,this.BackgroundColor(),255,true,false);
   this.CreateNewElement(GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_RIGHT,this.RightEdge()-this.BorderSizeRight()-2*size,0,size,size,this.BackgroundColor(),255,true,false);
   this.CreateNewElement(GRAPH_ELEMENT_TYPE_WF_SCROLL_BAR_THUMB,this.Width()/2-width,0,30,size,CLR_DEF_CONTROL_SCROLL_BAR_THUMB_COLOR,255,true,false);
   this.SetArrowSize(2);
   CArrowLeftButton *bl=this.GetArrowButtonLeft();
   if(bl!=NULL)
     {
      bl.SetBackgroundColor(CLR_DEF_CONTROL_SCROLL_BAR_BUTT_COLOR,true);
      bl.SetBackgroundColorMouseDown(CLR_DEF_CONTROL_SCROLL_BAR_BUTT_MOUSE_DOWN);
      bl.SetBackgroundColorMouseOver(CLR_DEF_CONTROL_SCROLL_BAR_BUTT_MOUSE_OVER);
      bl.SetForeColor(CLR_DEF_CONTROL_SCROLL_BAR_BUTT_FORE_COLOR,true);
      bl.SetForeColorMouseDown(CLR_DEF_CONTROL_SCROLL_BAR_BUTT_FORE_MOUSE_DOWN);
      bl.SetForeColorMouseOver(CLR_DEF_CONTROL_SCROLL_BAR_BUTT_FORE_MOUSE_OVER);
     }
   CArrowRightButton *br=this.GetArrowButtonRight();
   if(br!=NULL)
     {
      br.SetBackgroundColor(CLR_DEF_CONTROL_SCROLL_BAR_BUTT_COLOR,true);
      br.SetBackgroundColorMouseDown(CLR_DEF_CONTROL_SCROLL_BAR_BUTT_MOUSE_DOWN);
      br.SetBackgroundColorMouseOver(CLR_DEF_CONTROL_SCROLL_BAR_BUTT_MOUSE_OVER);
      br.SetForeColor(CLR_DEF_CONTROL_SCROLL_BAR_BUTT_FORE_COLOR,true);
      br.SetForeColorMouseDown(CLR_DEF_CONTROL_SCROLL_BAR_BUTT_FORE_MOUSE_DOWN);
      br.SetForeColorMouseOver(CLR_DEF_CONTROL_SCROLL_BAR_BUTT_FORE_MOUSE_OVER);
     }
   CScrollBarThumb *th=this.GetThumb();
   if(th!=NULL)
     {
      th.SetBackgroundColor(CLR_DEF_CONTROL_SCROLL_BAR_THUMB_COLOR,true);
      th.SetBorderColor(CLR_DEF_CONTROL_SCROLL_BAR_THUMB_BORDER_COLOR,true);
      th.SetBackgroundColorMouseDown(CLR_DEF_CONTROL_SCROLL_BAR_THUMB_MOUSE_DOWN);
      th.SetBackgroundColorMouseOver(CLR_DEF_CONTROL_SCROLL_BAR_THUMB_MOUSE_OVER);
      th.SetForeColor(CLR_DEF_CONTROL_SCROLL_BAR_THUMB_FORE_COLOR,true);
      th.SetForeColorMouseDown(CLR_DEF_CONTROL_SCROLL_BAR_THUMB_FORE_MOUSE_DOWN);
      th.SetForeColorMouseOver(CLR_DEF_CONTROL_SCROLL_BAR_THUMB_FORE_MOUSE_OVER);
     }
  }
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| Обработчик событий                                               |
//+------------------------------------------------------------------+
void CScrollBarHorisontal::OnChartEvent(const int id,const long &lparam,const double &dparam,const string &sparam)
  {
//--- Корректируем смещение по Y для подокна
   CGCnvElement::OnChartEvent(id,lparam,dparam,sparam);
//--- Если идентификатор события - перемещение объекта
   if(id==WF_CONTROL_EVENT_MOVING)
     {
      //--- Получаем указатель на объект-область захвата
      CScrollBarThumb *thumb=this.GetThumb();
      if(thumb==NULL)
         return;
      //--- Получаем указатель на объект-кнопку со стрелкой влево
      CArrowLeftButton *buttl=this.GetArrowButtonLeft();
      if(buttl==NULL)
         return;
      //--- Получаем указатель на объект-кнопку со стрелкой вправо
      CArrowRightButton *buttr=this.GetArrowButtonRight();
      if(buttr==NULL)
         return;
      //--- Объявляем переменные для координат области захвата
      int x=(int)lparam;
      int y=(int)dparam;
      //--- Устанавливаем координату Y равной координате Y элемента управления
      y=this.CoordY()+this.BorderSizeTop();
      //--- Корректируем координату X так, чтобы область захвата не выходила за пределы элемента управления с учётом кнопок со стрелками
      if(x<buttl.RightEdge())
        x=buttl.RightEdge();
      if(x>buttr.CoordX()-thumb.Width())
        x=buttr.CoordX()-thumb.Width();
      //--- Если объект-область захвата смещён на рассчитанные координаты
      if(thumb.Move(x,y,true))
        {
         //--- устанавливаем объекту его относительные координаты
         thumb.SetCoordXRelative(thumb.CoordX()-this.CoordX());
         thumb.SetCoordYRelative(thumb.CoordY()-this.CoordY());
         ::ChartRedraw(this.ChartID());
        }
     }
  }
//+------------------------------------------------------------------+

Доработаем класс объекта-контейнера в файле \MQL5\Include\DoEasy\Objects\Graph\WForms\Containers\Container.mqh.

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

//--- Устанавливает координату (1) X, (2) Y, (3) ширину, (4) высоту элемента
   virtual bool      SetCoordX(const int coord_x)              { return CGCnvElement::SetCoordX(coord_x);   }
   virtual bool      SetCoordY(const int coord_y)              { return CGCnvElement::SetCoordY(coord_y);   }
   virtual bool      SetWidth(const int width)                 { return CGCnvElement::SetWidth(width);      }
   virtual bool      SetHeight(const int height)               { return CGCnvElement::SetHeight(height);    }
   
//--- Устанавливает новые размеры (1) текущему, (2) указанному по индексу объекту
   virtual bool      Resize(const int w,const int h,const bool redraw);
   
//--- Создаёт новый присоединённый элемент

В этот метод перенесём блок кода, удалённый из класса базового WinForms-объекта.

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

//+------------------------------------------------------------------+
//| Защищённый конструктор с указанием типа объекта,                 |
//| идентификатора чарта и подокна                                   |
//+------------------------------------------------------------------+
CContainer::CContainer(const ENUM_GRAPH_ELEMENT_TYPE type,
                       CGCnvElement *main_obj,CGCnvElement *base_obj,
                       const long chart_id,
                       const int subwindow,
                       const string descript,
                       const int x,
                       const int y,
                       const int w,
                       const int h) : CWinFormBase(type,main_obj,base_obj,chart_id,subwindow,descript,x,y,w,h)
  {
//--- Установим объекту указанный тип графического элемента, а тип объекта библиотеки - как тип этого объекта
   this.SetTypeElement(type);
   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);
   this.CreateScrollBars(DEF_CONTROL_SCROLL_BAR_WIDTH);
   this.SetScrollAreaBottomX(this.CoordX());
   this.SetScrollAreaBottomY(this.BottomEdge()-DEF_CONTROL_SCROLL_BAR_WIDTH);
   this.SetScrollAreaBottomHeight(DEF_CONTROL_SCROLL_BAR_WIDTH);
  }
//+------------------------------------------------------------------+
//| Конструктор с указанием главного и базового объектов,            |
//| идентификатора чарта и подокна                                   |
//+------------------------------------------------------------------+
CContainer::CContainer(CGCnvElement *main_obj,CGCnvElement *base_obj,
                       const long chart_id,
                       const int subwindow,
                       const string descript,
                       const int x,
                       const int y,
                       const int w,
                       const int h) : CWinFormBase(GRAPH_ELEMENT_TYPE_WF_CONTAINER,main_obj,base_obj,chart_id,subwindow,descript,x,y,w,h)
  {
   this.SetTypeElement(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);
   this.CreateScrollBars(DEF_CONTROL_SCROLL_BAR_WIDTH);
   this.SetScrollAreaBottomX(this.CoordX());
   this.SetScrollAreaBottomY(this.BottomEdge()-DEF_CONTROL_SCROLL_BAR_WIDTH);
   this.SetScrollAreaBottomHeight(DEF_CONTROL_SCROLL_BAR_WIDTH);
  }
//+------------------------------------------------------------------+

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

//+------------------------------------------------------------------+
//| Устанавливает параметры присоединённому объекту                  |
//+------------------------------------------------------------------+
void CContainer::SetObjParams(CWinFormBase *obj,const color colour)
  {
//--- Устанавливаем объекту цвет текста как у базового контейнера
   obj.SetForeColor(this.ForeColor(),true);
//--- Если созданный объект не является контейнером - устанавливаем для него такую же группу, как у его базового объекта
   if(obj.TypeGraphElement()<GRAPH_ELEMENT_TYPE_WF_CONTAINER || obj.TypeGraphElement()>GRAPH_ELEMENT_TYPE_WF_SPLIT_CONTAINER)
      obj.SetGroup(this.Group());
//--- В зависимости от типа объекта
   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-объектов "Label", "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);
        obj.SetBackgroundColor(CLR_CANV_NULL,true);
        obj.SetOpacity(0,false);
        break;
      //--- Для WinForms-объекта "Button", "TabHeader", TabField, "ListBoxItem"
      case GRAPH_ELEMENT_TYPE_WF_BUTTON               :
      case GRAPH_ELEMENT_TYPE_WF_TAB_HEADER           :
      case GRAPH_ELEMENT_TYPE_WF_TAB_FIELD            :
      case GRAPH_ELEMENT_TYPE_WF_LIST_BOX_ITEM        :
        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;
      //--- Для WinForms-объекта "ListBox", "CheckedListBox", "ButtonListBox"
      case GRAPH_ELEMENT_TYPE_WF_LIST_BOX             :
      case GRAPH_ELEMENT_TYPE_WF_CHECKED_LIST_BOX     :
      case GRAPH_ELEMENT_TYPE_WF_BUTTON_LIST_BOX      :
        obj.SetBackgroundColor(colour==clrNONE ? CLR_DEF_CONTROL_STD_BACK_COLOR : colour,true);
        obj.SetBorderColor(CLR_DEF_BORDER_COLOR,true);
        obj.SetForeColor(CLR_DEF_FORE_COLOR,true);
        break;
      //--- Для WinForms-объекта "TabControl"
      case GRAPH_ELEMENT_TYPE_WF_TAB_CONTROL          :
        obj.SetBackgroundColor(colour==clrNONE ? CLR_DEF_CONTROL_TAB_BACK_COLOR : colour,true);
        obj.SetBorderColor(CLR_DEF_CONTROL_TAB_BORDER_COLOR,true);
        obj.SetForeColor(CLR_DEF_FORE_COLOR,true);
        obj.SetOpacity(CLR_DEF_CONTROL_TAB_OPACITY);
        break;
      //--- Для WinForms-объекта "SplitContainer"
      case GRAPH_ELEMENT_TYPE_WF_SPLIT_CONTAINER      :
        obj.SetBackgroundColor(colour==clrNONE ? CLR_CANV_NULL : colour,true);
        obj.SetBorderColor(CLR_CANV_NULL,true);
        obj.SetForeColor(CLR_DEF_FORE_COLOR,true);
        obj.SetOpacity(0);
        break;
      //--- Для WinForms-объекта "SplitContainerPanel"
      case GRAPH_ELEMENT_TYPE_WF_SPLIT_CONTAINER_PANEL:
        obj.SetBackgroundColor(colour==clrNONE ? CLR_DEF_CONTROL_SPLIT_CONTAINER_BACK_COLOR : colour,true);
        obj.SetBorderColor(CLR_DEF_CONTROL_SPLIT_CONTAINER_BORDER_COLOR,true);
        obj.SetForeColor(CLR_DEF_FORE_COLOR,true);
        break;
      //--- Для WinForms-объекта "Splitter"
      case GRAPH_ELEMENT_TYPE_WF_SPLITTER             :
        obj.SetBackgroundColor(colour==clrNONE ? CLR_CANV_NULL : colour,true);
        obj.SetBorderColor(CLR_CANV_NULL,true);
        obj.SetForeColor(CLR_DEF_FORE_COLOR,true);
        obj.SetOpacity(0);
        obj.SetDisplayed(false);
        obj.Hide();
        break;
      //--- Для WinForms-объекта"ArrowButton"
      case GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON         :
      case GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_UP      :
      case GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_DOWN    :
      case GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_LEFT    :
      case GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_RIGHT   :
        obj.SetBorderColor(CLR_DEF_CONTROL_TAB_HEAD_BORDER_COLOR,true);
        obj.SetBorderStyle(FRAME_STYLE_SIMPLE);
        break;
      //--- Для WinForms-объекта "Hint"
      case GRAPH_ELEMENT_TYPE_WF_HINT_BASE            :
        obj.SetBackgroundColor(CLR_CANV_NULL,true);
        obj.SetBorderColor(CLR_CANV_NULL,true);
        obj.SetForeColor(CLR_CANV_NULL,true);
        obj.SetOpacity(0,false);
        obj.SetBorderStyle(FRAME_STYLE_NONE);
        break;
      //--- Для WinForms-объекта "HintMoveLeft", "HintMoveRight", "HintMoveUp", "HintMoveDown", 
      case GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_LEFT       :
      case GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_RIGHT      :
      case GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_UP         :
      case GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_DOWN       :
        obj.SetBackgroundColor(CLR_CANV_NULL,true);
        obj.SetBorderColor(CLR_CANV_NULL,true);
        obj.SetForeColor(CLR_DEF_CONTROL_HINT_FORE_COLOR,true);
        obj.SetOpacity(0,false);
        obj.SetBorderStyle(FRAME_STYLE_NONE);
        break;
      //--- Для WinForms-объекта "ToolTip"
      case GRAPH_ELEMENT_TYPE_WF_TOOLTIP              :
        obj.SetBackgroundColor(CLR_DEF_CONTROL_HINT_BACK_COLOR,true);
        obj.SetBorderColor(CLR_DEF_CONTROL_HINT_BORDER_COLOR,true);
        obj.SetForeColor(CLR_DEF_CONTROL_HINT_FORE_COLOR,true);
        obj.SetBorderStyle(FRAME_STYLE_SIMPLE);
        obj.SetOpacity(0,false);
        obj.SetDisplayed(false);
        obj.Hide();
        break;
      //--- Для WinForms-объекта "BarProgressBar"
      case GRAPH_ELEMENT_TYPE_WF_BAR_PROGRESS_BAR     :
        obj.SetBackgroundColor(CLR_DEF_CONTROL_PROGRESS_BAR_BAR_COLOR,true);
        obj.SetBorderColor(CLR_DEF_CONTROL_PROGRESS_BAR_BAR_COLOR,true);
        obj.SetForeColor(CLR_DEF_CONTROL_PROGRESS_BAR_FORE_COLOR,true);
        obj.SetBorderStyle(FRAME_STYLE_NONE);
        break;
      //--- Для WinForms-объекта "ProgressBar"
      case GRAPH_ELEMENT_TYPE_WF_PROGRESS_BAR         :
        obj.SetBackgroundColor(CLR_DEF_CONTROL_PROGRESS_BAR_BACK_COLOR,true);
        obj.SetBorderColor(CLR_DEF_CONTROL_PROGRESS_BAR_BORDER_COLOR,true);
        obj.SetForeColor(CLR_DEF_CONTROL_PROGRESS_BAR_FORE_COLOR,true);
        obj.SetBorderStyle(FRAME_STYLE_SIMPLE);
        break;
      //--- Для WinForms-объекта "ScrollBar"
      case GRAPH_ELEMENT_TYPE_WF_SCROLL_BAR           :
      case GRAPH_ELEMENT_TYPE_WF_SCROLL_BAR_HORISONTAL:
      case GRAPH_ELEMENT_TYPE_WF_SCROLL_BAR_VERTICAL  :
        obj.SetBackgroundColor(CLR_DEF_CONTROL_SCROLL_BAR_TRACK_BACK_COLOR,true);
        obj.SetBorderColor(CLR_DEF_CONTROL_SCROLL_BAR_TRACK_BORDER_COLOR,true);
        obj.SetForeColor(CLR_DEF_CONTROL_SCROLL_BAR_TRACK_FORE_COLOR,true);
        obj.SetBorderSizeAll(1);
        obj.SetBorderStyle(FRAME_STYLE_SIMPLE);
        break;
      //--- Для WinForms-объекта "ScrollBarThumb"
      case GRAPH_ELEMENT_TYPE_WF_SCROLL_BAR_THUMB     :
        obj.SetBackgroundColor(CLR_DEF_CONTROL_SCROLL_BAR_THUMB_COLOR,true);
        obj.SetBorderColor(CLR_DEF_CONTROL_SCROLL_BAR_THUMB_BORDER_COLOR,true);
        obj.SetForeColor(CLR_DEF_CONTROL_SCROLL_BAR_THUMB_FORE_COLOR,true);
        obj.SetBorderSizeAll(0);
        obj.SetBorderStyle(FRAME_STYLE_NONE);
        break;
      //--- Для WinForms-объекта "GlareObj"
      case GRAPH_ELEMENT_TYPE_WF_GLARE_OBJ            :
        obj.SetBackgroundColor(CLR_CANV_NULL,true);
        obj.SetBorderColor(CLR_CANV_NULL,true);
        obj.SetForeColor(CLR_CANV_NULL,true);
        obj.SetBorderStyle(FRAME_STYLE_NONE);
        break;
      default:
        break;
     }
   obj.Crop();
  }
//+------------------------------------------------------------------+

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

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

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

//+------------------------------------------------------------------+
//| Подстраивает размеры элемента под его внутреннее содержимое      |
//+------------------------------------------------------------------+
bool CContainer::AutoSizeProcess(const bool redraw)
  {
//--- Получаем список прикреплённых объектов с типом WinForms базовый и выше
   CArrayObj *list=this.GetListWinFormsObj();
   int maxcX=0;
   int maxcY=0;
//--- Рассчитываем максимальную координату правого и нижнего края из всех привязанных объектов
   for(int i=0;i<list.Total();i++)
     {
      CWinFormBase *obj=list.At(i);
      if(obj==NULL || 
         obj.TypeGraphElement()==GRAPH_ELEMENT_TYPE_WF_SCROLL_BAR             || 
         obj.TypeGraphElement()==GRAPH_ELEMENT_TYPE_WF_SCROLL_BAR_HORISONTAL  || 
         obj.TypeGraphElement()==GRAPH_ELEMENT_TYPE_WF_SCROLL_BAR_VERTICAL
        ) continue;
      if(obj.RightEdge()>maxcX)
         maxcX=obj.RightEdge();
      if(obj.BottomEdge()>maxcY)
         maxcY=obj.BottomEdge();
     }
//--- Рассчитываем необходимую ширину и высоту панели после подгонки её размеров под содержимое
   int w=maxcX-this.CoordX();
   int h=maxcY-this.CoordY();
//--- Рассчитываем количество пикселей, на которые необходимо изменить размер контейнера по ширине и высоте
   int excess_x=w-this.WidthWorkspace()-this.BorderSizeRight()-1;
   int excess_y=h-this.HeightWorkspace()-this.BorderSizeBottom()-1;
//--- Если размеры контейнера менять не нужно - возвращаем true
   if(excess_x==0 && excess_y==0)
      return true;
//--- Возвращаем результат изменения размеров контейнера
   return
     (
      //--- Если только увеличение размеров
      this.AutoSizeMode()==CANV_ELEMENT_AUTO_SIZE_MODE_GROW ? 
      this.Resize(this.Width()+(excess_x>0  ? excess_x : 0),this.Height()+(excess_y>0  ? excess_y : 0),redraw) :
      //--- если увеличение и уменьшение размеров
      this.Resize(this.Width()+(excess_x!=0 ? excess_x : 0),this.Height()+(excess_y!=0 ? excess_y : 0),redraw)
     );
  }
//+------------------------------------------------------------------+

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

//+------------------------------------------------------------------+
//| Устанавливает новые размеры текущему объекту                     |
//+------------------------------------------------------------------+
bool CContainer::Resize(const int w,const int h,const bool redraw)
  {
//--- Если не удалось изменить размер контейнера - возвращаем false
   if(!CWinFormBase::Resize(w,h,redraw))
      return false;

//--- Получаем вертикальную полосу прокрутки и, если есть,
   CWinFormBase *scroll_v=this.GetElementByType(GRAPH_ELEMENT_TYPE_WF_SCROLL_BAR_VERTICAL,0);
   if(scroll_v!=NULL)
     {
      //--- изменяем вертикальный размер под размер рабочей области контейнера
      scroll_v.Resize(scroll_v.Width(),this.HeightWorkspace(),false);
      //--- Перемещаем вертикальную полосу прокрутки на новые координаты
      if(scroll_v.Move(this.RightEdgeWorkspace()-scroll_v.Width(),this.CoordYWorkspace()))
        {
         scroll_v.SetCoordXRelative(scroll_v.CoordX()-this.CoordX());
         scroll_v.SetCoordYRelative(scroll_v.CoordY()-this.CoordY());
        }
      //--- Получаем из объекта-вертикальной полосы прокрутки объект-кнопку со стрелкой вниз
      CWinFormBase *arr_d=scroll_v.GetElementByType(GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_DOWN,0);
      //--- Если кнопка получена
      if(arr_d!=NULL)
        {
         //--- Перемещаем её к нижнему краю вертикальной полосы прокрутки
         if(arr_d.Move(arr_d.CoordX(),scroll_v.BottomEdge()-scroll_v.BorderSizeBottom()-2*arr_d.Height()))
           {
            arr_d.SetCoordXRelative(arr_d.CoordX()-scroll_v.CoordX());
            arr_d.SetCoordYRelative(arr_d.CoordY()-scroll_v.CoordY());
           }
        }
     }
//--- Получаем горизонтальную полосу прокрутки и, если есть
   CWinFormBase *scroll_h=this.GetElementByType(GRAPH_ELEMENT_TYPE_WF_SCROLL_BAR_HORISONTAL,0);
   if(scroll_h!=NULL)
     {
      //--- изменяем горизонтальный размер под размер рабочей области контейнера
      scroll_h.Resize(this.WidthWorkspace(),scroll_h.Height(),false);
      //--- Перемещаем горизонтальную полосу прокрутки на новые координаты
      if(scroll_h.Move(this.CoordXWorkspace(),this.BottomEdgeWorkspace()-scroll_h.Height()))
        {
         scroll_h.SetCoordXRelative(scroll_h.CoordX()-this.CoordX());
         scroll_h.SetCoordYRelative(scroll_h.CoordY()-this.CoordY());
        }
      //--- Получаем из объекта-горизонтальной полосы прокрутки объект-кнопку со стрелкой вправо
      CWinFormBase *arr_r=scroll_h.GetElementByType(GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_RIGHT,0);
      //--- Если кнопка получена
      if(arr_r!=NULL)
        {
         //--- Перемещаем её к правому краю горизонтальной полосы прокрутки
         if(arr_r.Move(scroll_h.RightEdge()-BorderSizeRight()-2*arr_r.Width(),arr_r.CoordY()))
           {
            arr_r.SetCoordXRelative(arr_r.CoordX()-scroll_h.CoordX());
            arr_r.SetCoordYRelative(arr_r.CoordY()-scroll_h.CoordY());
           }
        }
     }
   return true;
  }
//+------------------------------------------------------------------+

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

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

Нам нужно при перемещении объекта-области захвата (ползунка полосы прокрутки) послать сообщение в библиотеку о его перемещении — чтобы вызвать его обработчик событий. У нас для этого уже всё есть — мы уже можем перемещать разделитель в объекте SplitContainer. А значит — нам нужно в то мето кода, где обрабатывается перемещение разделителя, добавить обработку перемещения и ползунка.

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

      //--- Если событие перемещения мышки
      if(id==CHARTEVENT_MOUSE_MOVE)
        {
         //--- Если курсор над формой
         if(::CheckPointer(form)!=POINTER_INVALID)
           {
            //--- Если стоит флаг перемещения
            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(form.TypeGraphElement()==GRAPH_ELEMENT_TYPE_WF_SPLITTER || form.TypeGraphElement()==GRAPH_ELEMENT_TYPE_WF_SCROLL_BAR_THUMB)
                    {
                     //--- получаем его базовый объект
                     CWinFormBase *base=form.GetBase();
                     if(base==NULL)
                        return;
                     //--- и отправляем событие "Перемещение объекта" в обработчик событий базового объекта
                     const long lp=x;
                     const double dp=y;
                     base.OnChartEvent(WF_CONTROL_EVENT_MOVING,lp,dp,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_RIGHT_NOT_PRESSED)
              {
               form.OnMouseEvent(MOUSE_EVENT_INSIDE_SCROLL_AREA_RIGHT_NOT_PRESSED,lparam,dparam,sparam);
              }
            //+-----------------------------------------------------------------------------------------------+
            //|Обработчик события Курсор в пределах области прокрутки окна справа, нажата кнопка мышки (любая)|
            //+-----------------------------------------------------------------------------------------------+
            if(mouse_state==MOUSE_FORM_STATE_INSIDE_SCROLL_AREA_RIGHT_PRESSED)
              {
               form.OnMouseEvent(MOUSE_EVENT_INSIDE_SCROLL_AREA_RIGHT_PRESSED,lparam,dparam,sparam);
              }
            //+-------------------------------------------------------------------------------------------------+
            //|Обработчик события Курсор в пределах области прокрутки окна справа, прокручивается колёсико мышки|
            //+-------------------------------------------------------------------------------------------------+
            if(mouse_state==MOUSE_FORM_STATE_INSIDE_SCROLL_AREA_RIGHT_WHEEL)
              {
               form.OnMouseEvent(MOUSE_EVENT_INSIDE_SCROLL_AREA_RIGHT_WHEEL,lparam,dparam,sparam);
              }
            //+--------------------------------------------------------------------------------------------+
            //| Обработчик события Курсор в пределах области прокрутки окна снизу, кнопки мышки не нажаты  |
            //+--------------------------------------------------------------------------------------------+
            if(mouse_state==MOUSE_FORM_STATE_INSIDE_SCROLL_AREA_BOTTOM_NOT_PRESSED)
              {
               form.OnMouseEvent(MOUSE_EVENT_INSIDE_SCROLL_AREA_BOTTOM_NOT_PRESSED,lparam,dparam,sparam);
              }
            //+----------------------------------------------------------------------------------------------+
            //|Обработчик события Курсор в пределах области прокрутки окна снизу, нажата кнопка мышки (любая)|
            //+----------------------------------------------------------------------------------------------+
            if(mouse_state==MOUSE_FORM_STATE_INSIDE_SCROLL_AREA_BOTTOM_PRESSED)
              {
               form.OnMouseEvent(MOUSE_EVENT_INSIDE_SCROLL_AREA_BOTTOM_PRESSED,lparam,dparam,sparam);
              }
            //+------------------------------------------------------------------------------------------------+
            //|Обработчик события Курсор в пределах области прокрутки окна снизу, прокручивается колёсико мышки|
            //+------------------------------------------------------------------------------------------------+
            if(mouse_state==MOUSE_FORM_STATE_INSIDE_SCROLL_AREA_BOTTOM_WHEEL)
              {
               form.OnMouseEvent(MOUSE_EVENT_INSIDE_SCROLL_AREA_BOTTOM_WHEEL,lparam,dparam,sparam);
              }
            //+---------------------------------------------------------------------------------------------+
            //| Обработчик события Курсор в пределах области управления, кнопки мышки не нажаты             |
            //+---------------------------------------------------------------------------------------------+
            if(mouse_state==MOUSE_FORM_STATE_INSIDE_CONTROL_AREA_NOT_PRESSED)
              {
               form.OnMouseEvent(MOUSE_EVENT_INSIDE_CONTROL_AREA_NOT_PRESSED,lparam,dparam,sparam);
              }
            //+---------------------------------------------------------------------------------------------+


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

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

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

//+------------------------------------------------------------------+
//| 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;
   for(int i=0;i<FORMS_TOTAL;i++)
     {
      pnl=engine.CreateWFPanel("WinForms Panel"+(string)i,(i==0 ? 50 : 70),(i==0 ? 50 : 70),410,200,array_clr,200,true,true,false,-1,FRAME_STYLE_BEVEL,true,false);
      if(pnl!=NULL)
        {
         pnl.Hide();
         //--- Установим значение Padding равным 4
         pnl.SetPaddingAll(3);
         //--- Установим флаги перемещаемости, автоизменения размеров и режим автоизменения из входных параметров
         pnl.SetMovable(InpMovable);
         pnl.SetAutoSize(InpAutoSize,false);
         pnl.SetAutoSizeMode((ENUM_CANV_ELEMENT_AUTO_SIZE_MODE)InpAutoSizeMode,false);

         //---
         pnl.CreateNewElement(GRAPH_ELEMENT_TYPE_WF_BUTTON,10,10,pnl.WidthWorkspace()-20,pnl.HeightWorkspace()-20,clrNONE,255,true,false);
         /*
         //--- Создадим элемент управления TabControl
         pnl.CreateNewElement(GRAPH_ELEMENT_TYPE_WF_TAB_CONTROL,InpTabControlX,InpTabControlY,pnl.Width()-30,pnl.Height()-40,clrNONE,255,true,false);
         CTabControl *tc=pnl.GetElementByType(GRAPH_ELEMENT_TYPE_WF_TAB_CONTROL,0);
         if(tc!=NULL)
           {
            tc.SetTabSizeMode((ENUM_CANV_ELEMENT_TAB_SIZE_MODE)InpTabPageSizeMode);
            tc.SetAlignment((ENUM_CANV_ELEMENT_ALIGNMENT)InpHeaderAlignment);
            tc.SetMultiline(InpTabCtrlMultiline);
            tc.SetHeaderPadding(6,0);
            tc.CreateTabPages(15,0,56,20,TextByLanguage("Вкладка","TabPage"));
            
            //--- Создадим Tooltip для кнопки "Влево"
            tc.CreateNewElement(GRAPH_ELEMENT_TYPE_WF_TOOLTIP,0,0,10,10,clrNONE,0,false,false);
            CToolTip *tooltip=tc.GetElementByType(GRAPH_ELEMENT_TYPE_WF_TOOLTIP,0);
            if(tooltip!=NULL)
              {
               tooltip.SetDescription("Left Button Tooltip");
               tooltip.SetIcon(InpTooltipIcon);
               tooltip.SetTitle(InpTooltipTitle);
               tooltip.SetTooltipText(TextByLanguage("Нажмите для прокрутки заголовков вправо","Click to scroll headings to the right"));
               tc.AddTooltipToArrowLeftButton(tooltip);
              }
            //--- Создадим Tooltip для кнопки "Вправо"
            tc.CreateNewElement(GRAPH_ELEMENT_TYPE_WF_TOOLTIP,0,0,10,10,clrNONE,0,false,false);
            tooltip=tc.GetElementByType(GRAPH_ELEMENT_TYPE_WF_TOOLTIP,1);
            if(tooltip!=NULL)
              {
               tooltip.SetDescription("Right Button Tooltip");
               tooltip.SetIcon(ENUM_CANV_ELEMENT_TOOLTIP_ICON(InpTooltipIcon+1));
               tooltip.SetTitle(InpTooltipTitle);
               tooltip.SetTooltipText(TextByLanguage("Нажмите для прокрутки заголовков влево","Click to scroll headings to the left"));
               tc.AddTooltipToArrowRightButton(tooltip);
              }
              
            //--- Создадим на каждой вкладке текстовую метку с описанием вкладки
            for(int j=0;j<tc.TabPages();j++)
              {
               tc.CreateNewElement(j,GRAPH_ELEMENT_TYPE_WF_LABEL,322,120,80,20,clrDodgerBlue,255,true,false);
               CLabel *label=tc.GetTabElementByType(j,GRAPH_ELEMENT_TYPE_WF_LABEL,0);
               if(label==NULL)
                  continue;
               //--- Если это самая первая вкладка, то текста не будет
               label.SetText(j<5 ? "" : "TabPage"+string(j+1));
              }
            for(int n=0;n<5;n++)
              {
               //--- Создадим на каждой вкладке элемент управления SplitContainer
               tc.CreateNewElement(n,GRAPH_ELEMENT_TYPE_WF_SPLIT_CONTAINER,10,10,tc.Width()-22,tc.GetTabField(0).Height()-22,clrNONE,255,true,false);
               //--- Получим элемент управления SplitContainer с каждой вкладки
               CSplitContainer *split_container=tc.GetTabElementByType(n,GRAPH_ELEMENT_TYPE_WF_SPLIT_CONTAINER,0);
               if(split_container!=NULL)
                 {
                  //--- Для каждой чётной вкладки разделитель будет вертикальным, для нечётной - горизонтальным
                  split_container.SetSplitterOrientation(n%2==0 ? CANV_ELEMENT_SPLITTER_ORIENTATION_VERTICAL : CANV_ELEMENT_SPLITTER_ORIENTATION_HORISONTAL,true);
                  //--- Дистанция разделителя на каждой вкладке будет 50 пикселей
                  split_container.SetSplitterDistance(50,true);
                  //--- Ширина разделителя на каждой последующей вкладке будет увеличиваться на 2 пикселя
                  split_container.SetSplitterWidth(6+2*n,false);
                  //--- Для вкладки с индексом 2 сделаем фиксированный разделитель, на остальных - перемещаемый
                  split_container.SetSplitterFixed(n==2 ? true : false);
                  //--- Для вкладки с индексом 3 вторая панель будет в свёрнутом состоянии (видна только первая)
                  if(n==3)
                     split_container.SetPanel2Collapsed(true);
                  //--- Для вкладки с индексом 4 первая панель будет в свёрнутом состоянии (видна только вторая)
                  if(n==4)
                     split_container.SetPanel1Collapsed(true);
                  
                  //--- На каждой из панелей элемента ...
                  for(int j=0;j<2;j++)
                    {
                     //--- Получаем панель по индексу цикла
                     CSplitContainerPanel *panel=split_container.GetPanel(j);
                     if(panel==NULL)
                        continue;
                     //--- устанавливаем для панели её описание
                     panel.SetDescription(TextByLanguage("Панель","Panel")+string(j+1));
                     
                     //--- Если это первая вкладка и вторая панель
                     if(n==0 && j==1)
                       {
                        //--- Создадим на ней элемент управления ProgressBar
                        if(split_container.CreateNewElement(j,GRAPH_ELEMENT_TYPE_WF_PROGRESS_BAR,4,4,100,12,clrNONE,255,false,false))
                          {
                           CProgressBar *progress_bar=split_container.GetPanelElementByType(j,GRAPH_ELEMENT_TYPE_WF_PROGRESS_BAR,0);
                           if(progress_bar!=NULL)
                             {
                              //--- Установим стиль полосы прогресса, заданный в настройках советника
                              progress_bar.SetStyle((ENUM_CANV_ELEMENT_PROGRESS_BAR_STYLE)InpProgressBarStyle);
                              //--- Установим параметры для описания полосы прогресса
                              progress_bar.SetBarDescriptionText("Progress Bar ");
                              progress_bar.SetBarDescriptionColor(panel.BackgroundColor());
                              progress_bar.SetBarDescriptionOpacity(255);
                              progress_bar.SetBarDescriptionY(-2);
                             }
                          }
                       }
                     
                     //--- ... создадим текстовую метку с названием панели
                     if(split_container.CreateNewElement(j,GRAPH_ELEMENT_TYPE_WF_LABEL,4,4,panel.Width()-8,panel.Height()-8,clrDodgerBlue,255,true,false))
                       {
                        CLabel *label=split_container.GetPanelElementByType(j,GRAPH_ELEMENT_TYPE_WF_LABEL,0);
                        if(label==NULL)
                           continue;
                        label.SetTextAlign(ANCHOR_CENTER);
                        label.SetText(panel.Description());
                       }
                    }
                 }
              }
           }
         */
        }
     }
//--- Отобразим и перерисуем все созданные панели
   for(int i=0;i<FORMS_TOTAL;i++)
     {
      //--- Получаем объект-панель
      pnl=engine.GetWFPanel("WinForms Panel"+(string)i);
      if(pnl!=NULL)
        {
         //--- отображаем и перерисовываем панель
         pnl.Show();
         pnl.Redraw(true);
         //--- Получаем указатель на объект-вертикальную полосу прокрутки основной панели
         CScrollBarVertical *sbv=pnl.GetElementByType(GRAPH_ELEMENT_TYPE_WF_SCROLL_BAR_VERTICAL,0);
         //--- Устанавливаем для объекта флаг его отображения и показываем полосу прокрутки
         sbv.SetDisplayed(true);
         sbv.Show();
         sbv.BringToTop();
         sbv.Redraw(true);
         //--- Получаем указатель на объект-горизонтальную полосу прокрутки основной панели
         CScrollBarHorisontal *sbh=pnl.GetElementByType(GRAPH_ELEMENT_TYPE_WF_SCROLL_BAR_HORISONTAL,0);
         //--- Устанавливаем для объекта флаг его отображения и показываем полосу прокрутки
         sbh.SetDisplayed(true);
         sbh.Show();
         sbh.BringToTop();
         sbh.Redraw(true);
         /*
         //--- Получаем с панели объект TabControl
         CTabControl *tc=pnl.GetElementByType(GRAPH_ELEMENT_TYPE_WF_TAB_CONTROL,0);
         //--- С первой вкладки объекта TabControl получаем объект SplitContainer
         CSplitContainer *sc=tc.GetTabElementByType(0,GRAPH_ELEMENT_TYPE_WF_SPLIT_CONTAINER,0);
         //--- С объекта SplitContainer получаем его вторую панель
         CSplitContainerPanel *scp=sc.GetPanel(1);
         //--- С полученной панели получаем объект ProgressBar
         CProgressBar *pb=scp.GetElementByType(GRAPH_ELEMENT_TYPE_WF_PROGRESS_BAR,0);
         //--- Ожидаем
         Sleep(1);
         //--- Получаем ширину объекта ProgressBar
         int w=pb.Width();
         //--- В цикле с задержкой увеличиваем ширину ProgressBar на 180 пикселей
         for(int n=0;n<180;n++)
           {
            //Sleep(1);
            pb.Resize(w+n,pb.Height(),true);
           }
         //--- Устанавливаем значения для PerformStep объекта ProgressBar
         pb.SetValuesForProcessing(0,350,1,0);
         //--- Сбрасываем полосу прогресса объекта ProgressBar в минимальное положение
         pb.ResetProgressBar();
         //--- Если стиль полосы прогресса "Непрерывная линия" - отображаем описание полосы прогресса
         if(pb.Style()==CANV_ELEMENT_PROGRESS_BAR_STYLE_CONTINUOUS)
            pb.ShowBarDescription();
         //--- Ожидаем
         Sleep(1);
         //--- Если стиль полосы прогресса не "Непрерывная прокрутка"
         if(pb.Style()!=CANV_ELEMENT_PROGRESS_BAR_STYLE_MARQUEE)
           {
            //--- В цикле от минимального до максимального значения ProgressBar
            for(int n=0;n<=pb.Maximum();n++)
              {
               //--- вызываем метод увеличения полосы прогресса на заданный шаг с ожиданием в 1/5 секунды
               pb.PerformStep();
               //--- Записываем в описание полосы прогресса количество пройденных шагов
               pb.SetBarDescriptionText("Progress Bar, pass: "+(InpProgressBarPercent ? pb.ValuePercentDescription() : pb.ValueDescription()));
               Sleep(1);
              }
           }
         //--- Ожидаем, устанавливаем для описания тип шрифта Bold и пишем на полосе прогресса сообщение о выполнении
         Sleep(1);
         pb.SetBarDescriptionFontFlags(FW_BOLD);
         pb.SetBarDescriptionText("Progress Bar: Done");
         //--- Устанавливаем для объекта-блика тип - прямоугольник, непрозрачность 40, цвет - белый
         pb.SetGlareStyle(CANV_ELEMENT_VISUAL_EFF_STYLE_RECTANGLE);
         pb.SetGlareOpacity(40);
         pb.SetGlareColor(clrWhite);
         */
        }
     }
        
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+

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


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

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


Что дальше

В следующей статье продолжим разработку объекта ScrollBar.

К содержанию

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

 
DoEasy. Элементы управления (Часть 26): Дорабатываем WinForms-объект "ToolTip" и начинаем разработку индикатора выполнения "ProgressBar"
DoEasy. Элементы управления (Часть 27): Продолжаем работу над WinForms-объектом "ProgressBar"
DoEasy. Элементы управления (Часть 28): Стили полосы в элементе управления "ProgressBar"
DoEasy. Элементы управления (Часть 29): Вспомогательный элемент управления "ScrollBar"

Прикрепленные файлы |
MQL5.zip (4639.44 KB)
Разработка торговой системы по индикатору фракталов Fractals Разработка торговой системы по индикатору фракталов Fractals
Перед вами новая статья из серии, в которой мы учимся создавать торговые системы на основе популярных технических индикаторов. Мы изучим еще один технический инструмент — индикатор Fractals, а также разработаем на его основе торговые системы для работы в терминале MetaTrader 5.
Разработка торгового советника с нуля (Часть 31): Навстречу будущему (IV) Разработка торгового советника с нуля (Часть 31): Навстречу будущему (IV)
Мы продолжаем удалять разные вещи внутри советника. Это будет последняя статья в этой серии. Последнее, что будет удалено в данной серии статей - это звуковая система. Это может сбить читателя с толку, если он не следил за этими статьями.
Популяционные алгоритмы оптимизации: Светлячковый алгоритм (Firefly Algorithm - FA) Популяционные алгоритмы оптимизации: Светлячковый алгоритм (Firefly Algorithm - FA)
Рассмотрим метод оптимизации "Поиск с помощью светлячкового алгоритма" (FA). Из аутсайдера путем модификации алгоритм превратился в настоящего лидера рейтинговой таблицы.
Популяционные алгоритмы оптимизации: Поиск косяком рыб (Fish School Search — FSS) Популяционные алгоритмы оптимизации: Поиск косяком рыб (Fish School Search — FSS)
Поиск косяком рыб (FSS) — новый современный алгоритм оптимизации, вдохновленный поведением рыб в стае, большинство из которых, до 80%, плавают организовано в сообществе сородичей. Доказано, что объединения рыб играют важную роль в эффективности поиска пропитания и защиты от хищников.