English 中文 Español Deutsch 日本語 Português
preview
DoEasy. Элементы управления (Часть 29): Вспомогательный элемент управления "ScrollBar"

DoEasy. Элементы управления (Часть 29): Вспомогательный элемент управления "ScrollBar"

MetaTrader 5Примеры | 9 декабря 2022, 08:52
964 0
Artyom Trishkin
Artyom Trishkin

Содержание


Концепция

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


Так как такой объект является наследником элемента управления ScrollBar (полоса прокрутки), то сегодня начнём создание этого WinForms-объекта.

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

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

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


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

При наведении курсора мышки и захвате области управления ScrollBar (кнопки и ползунок прокрутки) они должны менять свой цвет. Цвет меняется при наведении курсора и захвате — для каждого состояния установлен свой цвет.
Внесём их в список макроподстановок в файле \MQL5\Include\DoEasy\Defines.mqh:

#define CLR_DEF_CONTROL_PROGRESS_BAR_BACK_COLOR       (C'0xF0,0xF0,0xF0')  // Цвет фона элемента управления ProgressBar
#define CLR_DEF_CONTROL_PROGRESS_BAR_BORDER_COLOR     (C'0xBC,0xBC,0xBC')  // Цвет рамки элемента управления ProgressBar
#define CLR_DEF_CONTROL_PROGRESS_BAR_FORE_COLOR       (C'0x00,0x78,0xD7')  // Цвет текста элемента управления ProgressBar
#define CLR_DEF_CONTROL_PROGRESS_BAR_BAR_COLOR        (C'0x06,0xB0,0x25')  // Цвет линии прогресса элемента управления ProgressBar

#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_THUMB_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 DEF_CONTROL_LIST_MARGIN_X                     (1)                  // Зазор между столбцами в элементах управления ListBox
#define DEF_CONTROL_LIST_MARGIN_Y                     (0)                  // Зазор между строками в элементах управления ListBox


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

//+------------------------------------------------------------------+
//| Список типов графических элементов                               |
//+------------------------------------------------------------------+
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,                  // Windows Forms ScrollBar
   GRAPH_ELEMENT_TYPE_WF_SCROLL_BAR_HORISONTAL,       // Windows Forms ScrollBarHorisontal
   GRAPH_ELEMENT_TYPE_WF_SCROLL_BAR_VERTICAL,         // Windows Forms ScrollBarVertical
  };
//+------------------------------------------------------------------+

Здесь у нас добавлен один базовый элемент-полоса прокрутки и два производных от него элемента управления — горизонтальная и вертикальная полосы прокрутки.

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

   MSG_GRAPH_ELEMENT_TYPE_WF_BAR_PROGRESS_BAR,        // Элемент управления BarProgressBar
   MSG_GRAPH_ELEMENT_TYPE_WF_PROGRESS_BAR,            // Элемент управления ProgressBar
   MSG_GRAPH_ELEMENT_TYPE_WF_SCROLL_BAR,              // Элемент управления ScrollBar
   MSG_GRAPH_ELEMENT_TYPE_WF_SCROLL_BAR_VERTICAL,     // Элемент управления ScrollBarVertical
   MSG_GRAPH_ELEMENT_TYPE_WF_SCROLL_BAR_HORISONTAL,   // Элемент управления ScrollBarHorisontal
   MSG_GRAPH_ELEMENT_TYPE_WF_GLARE_OBJ,               // Объект блика

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

   {"Элемент управления BarProgressBar","Control element \"BarProgressBar\""},
   {"Элемент управления ProgressBar","Control element \"ProgressBar\""},
   {"Элемент управления ScrollBar","Control element \"ScrollBar\""},
   {"Элемент управления ScrollBarVertical","Control element \"ScrollBarVertical\""},
   {"Элемент управления ScrollBarHorisontal","Control element \"ScrollBarHorisontal\""},
   {"Объект блика","Glare object"},

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


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

Внесём в него вывод описания новых объектов:

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

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


Базовый класс вспомогательного элемента управления ScrollBar

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

В папке вспомогательных объектов библиотеки \MQL5\Include\DoEasy\Objects\Graph\WForms\Helpers\ создадим новый класс CScrollBar в файле ScrollBar.mqh. Класс должен быть унаследован от класса CWinFormBase, а файлы этого и других классов должны  быть подключены к файлу создаваемого объекта:

//+------------------------------------------------------------------+
//|                                                    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 "ArrowDownButton.mqh"
#include "ArrowUpButton.mqh"
#include "ArrowLeftButton.mqh"
#include "ArrowRightButton.mqh"
//+------------------------------------------------------------------+
//| Класс объекта CScrollBar элементов управления WForms             |
//+------------------------------------------------------------------+
class CScrollBar : public CWinFormBase
  {


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

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

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

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

protected:
//--- Создаёт объект-область захвата
   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; }
   
//--- Конструктор
                     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);
  };
//+------------------------------------------------------------------+

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

Рассмотрим реализацию объявленных методов.

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

//+------------------------------------------------------------------+
//| Защищённый конструктор с указанием типа объекта,                 |
//| идентификатора чарта и подокна                                   |
//+------------------------------------------------------------------+
CScrollBar::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) : CWinFormBase(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.Initialize();
  }
//+------------------------------------------------------------------+

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


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

//+------------------------------------------------------------------+
//| Конструктор с указанием главного и базового объектов,            |
//| идентификатора чарта и подокна                                   |
//+------------------------------------------------------------------+
CScrollBar::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) : CWinFormBase(GRAPH_ELEMENT_TYPE_WF_SCROLL_BAR,main_obj,base_obj,chart_id,subwindow,descript,x,y,w,h)
  {
   this.SetTypeElement(GRAPH_ELEMENT_TYPE_WF_SCROLL_BAR);
   this.m_type=OBJECT_DE_TYPE_GWF_HELPER;
   this.Initialize();
  }
//+------------------------------------------------------------------+

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


Метод инициализации свойства элемента:

//+------------------------------------------------------------------+
//| Инициализирует свойства элемента                                 |
//+------------------------------------------------------------------+
void CScrollBar::Initialize(void)
  {
   this.SetBorderSizeAll(1);
   this.SetBorderStyle(FRAME_STYLE_SIMPLE);
   this.SetBackgroundColor(CLR_DEF_CONTROL_SCROLL_BAR_TRACK_BACK_COLOR,true);
   this.SetBorderColor(CLR_DEF_CONTROL_SCROLL_BAR_TRACK_BORDER_COLOR,true);
   this.SetForeColor(CLR_DEF_CONTROL_SCROLL_BAR_TRACK_FORE_COLOR,true);
  }
//+------------------------------------------------------------------+

Здесь: устанавливаем размер рамки объекта в один пиксель с каждой стороны, устанавлиаем тип рамки "простая" и задаём цвета фона, рамки и текста элемента.

Метод, создающий объект-область захвата:

//+------------------------------------------------------------------+
//| Создаёт объект-область захвата                                   |
//+------------------------------------------------------------------+
void CScrollBar::CreateThumbArea(void)
  {
   this.CreateArrowButtons(DEF_ARROW_BUTTON_SIZE,DEF_ARROW_BUTTON_SIZE);
  }
//+------------------------------------------------------------------+

В методе вызывается виртуальный метод CreateArrowButtons() для создания объектов-кнопок со стрелками и ползунка, который должен быть переопределён в объектах-наследниках.

Метод, рассчитывающий размер области захвата:

//+------------------------------------------------------------------+
//| Рассчитывает размер области захвата                              |
//+------------------------------------------------------------------+
int CScrollBar::CalculateThumbAreaSize(void)
  {
   return 0;
  }
//+------------------------------------------------------------------+

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

Метод, создающий новый графический объект:

//+------------------------------------------------------------------+
//| Создаёт новый графический объект                                 |
//+------------------------------------------------------------------+
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_BUTTON               :
         element=new CButton(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::OnTimer(void)
  {

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

На данный момент метод пустой и ничего не делает — им будем заниматься в последующих статьях.

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


Производные от базового объекты ScrollBarVertical и ScrollBarHorisontal

Создадим объекты-наследники от базового абстрактного объекта-полосы прокрутки — вертикальную и горизонтальную.

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

//+------------------------------------------------------------------+
//|                                            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 "..\Containers\Container.mqh"
#include "..\Common Controls\Button.mqh"
#include "ArrowDownButton.mqh"
#include "ArrowUpButton.mqh"
#include "ScrollBar.mqh"
//+------------------------------------------------------------------+
//| Класс объекта CScrollBarVertical элементов управления WForms     |
//+------------------------------------------------------------------+
class CScrollBarVertical : public CScrollBar
  {
  }


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

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


Рассмотрим объявленные методы подробнее.

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

//+------------------------------------------------------------------+
//| Защищённый конструктор с указанием типа объекта,                 |
//| идентификатора чарта и подокна                                   |
//+------------------------------------------------------------------+
CScrollBarVertical::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) : CScrollBar(type,main_obj,base_obj,chart_id,subwindow,descript,x,y,w,h)
  {
//--- Установим объекту указанный тип графического элемента, а тип объекта библиотеки - как тип этого объекта
   this.SetTypeElement(type);
   this.CreateThumbArea();
  }
//+------------------------------------------------------------------+

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


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

//+------------------------------------------------------------------+
//| Конструктор с указанием главного и базового объектов,            |
//| идентификатора чарта и подокна                                   |
//+------------------------------------------------------------------+
CScrollBarVertical::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) : CScrollBar(GRAPH_ELEMENT_TYPE_WF_SCROLL_BAR_VERTICAL,main_obj,base_obj,chart_id,subwindow,descript,x,y,w,h)
  {
   this.SetTypeElement(GRAPH_ELEMENT_TYPE_WF_SCROLL_BAR_VERTICAL);
   this.CreateThumbArea();
  }
//+------------------------------------------------------------------+

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


Метод, создающий объекты ArrowButton:

//+------------------------------------------------------------------+
//| Создаёт объекты ArrowButton                                      |
//+------------------------------------------------------------------+
void CScrollBarVertical::CreateArrowButtons(const int width,const int height)
  {
   this.CreateNewElement(GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_UP,  -1,0,                   width,height,this.BackgroundColor(),255,true,false);
   this.CreateNewElement(GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_DOWN,-1,this.Height()-2*height,width,height,this.BackgroundColor(),255,true,false);
   this.CreateNewElement(GRAPH_ELEMENT_TYPE_WF_BUTTON,0,this.Height()/2-height,width,height,CLR_DEF_CONTROL_SCROLL_BAR_THUMB_COLOR,255,true,false);
  }
//+------------------------------------------------------------------+

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

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


Метод, рассчитывающий размер области захвата:

//+------------------------------------------------------------------+
//| Рассчитывает размер области захвата                              |
//+------------------------------------------------------------------+
int CScrollBarVertical::CalculateThumbAreaSize(void)
  {
   return 0;
  }
//+------------------------------------------------------------------+

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


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

//+------------------------------------------------------------------+
//| Таймер                                                           |
//+------------------------------------------------------------------+
void CScrollBarVertical::OnTimer(void)
  {

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

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


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

Класс должен быть создан в файле ScrollBarHorisontal.mqh в папке \MQL5\Include\DoEasy\Objects\Graph\WForms\Helpers\ScrollBarHorisontal.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 "..\Containers\Container.mqh"
#include "..\Common Controls\Button.mqh"
#include "ArrowLeftButton.mqh"
#include "ArrowRightButton.mqh"
//+------------------------------------------------------------------+
//| Класс объекта CScrollBarHorisontal элементов управления WForms     |
//+------------------------------------------------------------------+
class CScrollBarHorisontal : public CScrollBar
  {
private:
//--- Создаёт объекты ArrowButton
   virtual void      CreateArrowButtons(const int width,const int height);
//--- Рассчитывает размер области захвата
   virtual int       CalculateThumbAreaSize(void);

protected:
//--- Защищённый конструктор с указанием типа объекта, идентификатора чарта и подокна
                     CScrollBarHorisontal(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; }
   
//--- Конструктор
                     CScrollBarHorisontal(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);
  };
//+------------------------------------------------------------------+
//| Защищённый конструктор с указанием типа объекта,                 |
//| идентификатора чарта и подокна                                   |
//+------------------------------------------------------------------+
CScrollBarHorisontal::CScrollBarHorisontal(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) : CScrollBar(type,main_obj,base_obj,chart_id,subwindow,descript,x,y,w,h)
  {
//--- Установим объекту указанный тип графического элемента, а тип объекта библиотеки - как тип этого объекта
   this.SetTypeElement(type);
   this.CreateThumbArea();
  }
//+------------------------------------------------------------------+
//| Конструктор с указанием главного и базового объектов,            |
//| идентификатора чарта и подокна                                   |
//+------------------------------------------------------------------+
CScrollBarHorisontal::CScrollBarHorisontal(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) : CScrollBar(GRAPH_ELEMENT_TYPE_WF_SCROLL_BAR_HORISONTAL,main_obj,base_obj,chart_id,subwindow,descript,x,y,w,h)
  {
   this.SetTypeElement(GRAPH_ELEMENT_TYPE_WF_SCROLL_BAR_HORISONTAL);
   this.CreateThumbArea();
  }
//+------------------------------------------------------------------+
//| Создаёт объекты ArrowButton                                      |
//+------------------------------------------------------------------+
void CScrollBarHorisontal::CreateArrowButtons(const int width,const int height)
  {
   this.CreateNewElement(GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_LEFT,  -1,-1,                   width,height,this.BackgroundColor(),255,true,false);
   this.CreateNewElement(GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_RIGHT,this.Width()-2*width,-1,width,height,this.BackgroundColor(),255,true,false);
   this.CreateNewElement(GRAPH_ELEMENT_TYPE_WF_BUTTON,this.Width()/2-width,0,width,height,CLR_DEF_CONTROL_SCROLL_BAR_THUMB_COLOR,255,true,false);
  }
//+------------------------------------------------------------------+
//| Рассчитывает размер области захвата                              |
//+------------------------------------------------------------------+
int CScrollBarHorisontal::CalculateThumbAreaSize(void)
  {
   return 0;
  }
//+------------------------------------------------------------------+
//| Таймер                                                           |
//+------------------------------------------------------------------+
void CScrollBarHorisontal::OnTimer(void)
  {

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


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

Соответственно, раз объекты должны присутствовать по умолчанию в объектах-контейнерах, то их создание необходимо организовать в классе объекта-контейнера в файле \MQL5\Include\DoEasy\Objects\Graph\WForms\Containers\Container.mqh.

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

//+------------------------------------------------------------------+
//|                                                    Container.mqh |
//|                                  Copyright 2022, MetaQuotes Ltd. |
//|                             https://mql5.com/ru/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2022, MetaQuotes Ltd."
#property link      "https://mql5.com/ru/users/artmedia70"
#property version   "1.00"
#property strict    // Нужно для mql4
//+------------------------------------------------------------------+
//| Включаемые файлы                                                 |
//+------------------------------------------------------------------+
#include "..\WinFormBase.mqh"
#include "..\Common Controls\RadioButton.mqh"
#include "..\Common Controls\Button.mqh"
#include "..\Helpers\ScrollBarVertical.mqh"
#include "..\Helpers\ScrollBarHorisontal.mqh"
//+------------------------------------------------------------------+
//| Класс базового объекта-контейнера элементов управления WForms    |
//+------------------------------------------------------------------+
class CContainer : public CWinFormBase
  {
private:
//--- Создаёт новый графический объект
   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);// { return NULL; }

//--- Рассчитывает координаты привязки Dock-объектов
   void              CalculateCoords(CArrayObj *list);
   
//--- Создаёт (1) вертикальный, (2) горизонтальный ScrollBar
   CWinFormBase     *CreateScrollBarVertical(const int width);
   CWinFormBase     *CreateScrollBarHorisontal(const int width);

protected:
//--- Подстраивает размеры элемента под его внутреннее содержимое
   bool              AutoSizeProcess(const bool redraw);
//--- Устанавливает параметры присоединённому объекту
   void              SetObjParams(CWinFormBase *obj,const color colour);
//--- Создаёт объекты ScrollBar вертикальный и горизонтальный
   void              CreateScrollBars(const int width);

public:


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

//+------------------------------------------------------------------+
//| Защищённый конструктор с указанием типа объекта,                 |
//| идентификатора чарта и подокна                                   |
//+------------------------------------------------------------------+
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(10);
  }
//+------------------------------------------------------------------+
//| Конструктор с указанием главного и базового объектов,            |
//| идентификатора чарта и подокна                                   |
//+------------------------------------------------------------------+
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(10);
  }
//+------------------------------------------------------------------+

После создания объекта-контейнера будут созданы прикреплённые к нему две полосы прокрутки. После создания полосы будут скрыты.


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

//+------------------------------------------------------------------+
//| Устанавливает параметры присоединённому объекту                  |
//+------------------------------------------------------------------+
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-объекта "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();
  }
//+------------------------------------------------------------------+

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


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

//+------------------------------------------------------------------+
//| Создаёт объекты ScrollBar вертикальный и горизонтальный          |
//+------------------------------------------------------------------+
void CContainer::CreateScrollBars(const int width)
  {
//--- Создаём объект-вертикальную полосу прокрутки
   CScrollBarVertical *sbv=this.CreateScrollBarVertical(DEF_ARROW_BUTTON_SIZE);
//--- Если объект создан
   if(sbv!=NULL)
     {
      //--- устанавливаем флаг неотображения объекта и скрываем его
      sbv.SetDisplayed(false);
      sbv.Hide();
     }
//--- Создаём объект-горизонтальную полосу прокрутки
   CScrollBarHorisontal *sbh=this.CreateScrollBarHorisontal(DEF_ARROW_BUTTON_SIZE);
//--- Если объект создан
   if(sbh!=NULL)
     {
      //--- устанавливаем флаг неотображения объекта и скрываем его
      sbh.SetDisplayed(false);
      sbh.Hide();
     }
  }
//+------------------------------------------------------------------+

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


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

//+------------------------------------------------------------------+
//| Создаёт вертикальный ScrollBar                                   |
//+------------------------------------------------------------------+
CWinFormBase *CContainer::CreateScrollBarVertical(const int width)
  {
//--- Если не удалось создать объект-вертикальную полосу прокрутки - возвращаем NULL
   if(!this.CreateNewElement(GRAPH_ELEMENT_TYPE_WF_SCROLL_BAR_VERTICAL,this.RightEdge()-width,0,width,this.HeightWorkspace(),this.BackgroundColor(),255,true,false))
      return NULL;
//--- Возвращаем указатель на объект-вертикальную полосу прокрутки
   return this.GetElementByType(GRAPH_ELEMENT_TYPE_WF_SCROLL_BAR_VERTICAL,0);
  }
//+------------------------------------------------------------------+

Создаём объект и возвращаем указатель на него из списка прикреплённых объектов.


Метод, создающий горизонтальный ScrollBar:

//+------------------------------------------------------------------+
//| Создаёт горизонтальный ScrollBar                                 |
//+------------------------------------------------------------------+
CWinFormBase *CContainer::CreateScrollBarHorisontal(const int width)
  {
//--- Если не удалось создать объект-горизонтальную полосу прокрутки - возвращаем NULL
   if(!this.CreateNewElement(GRAPH_ELEMENT_TYPE_WF_SCROLL_BAR_HORISONTAL,0,this.BottomEdge()-width,this.WidthWorkspace(),width,this.BackgroundColor(),255,true,false))
      return NULL;
//--- Возвращаем указатель на объект-горизонтальную полосу прокрутки
   return this.GetElementByType(GRAPH_ELEMENT_TYPE_WF_SCROLL_BAR_HORISONTAL,0);
  }
//+------------------------------------------------------------------+

Создаём объект и возвращаем указатель на него из списка прикреплённых объектов.


Метод, создающий новый графический объект:

//+------------------------------------------------------------------+
//| Создаёт новый графический объект                                 |
//+------------------------------------------------------------------+
CGCnvElement *CContainer::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           : element=new CScrollBar(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h);          break;
      case GRAPH_ELEMENT_TYPE_WF_SCROLL_BAR_VERTICAL  : element=new CScrollBarVertical(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h);  break;
      case GRAPH_ELEMENT_TYPE_WF_SCROLL_BAR_HORISONTAL: element=new CScrollBarHorisontal(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;
  }
//+------------------------------------------------------------------+

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


Так как полосы прокрутки прикреплены к объектам-контейнерам по их сторонам — снизу и справа, то изменение размеров формы должно вызывать и изменение объектов-полос прокрутки. Сделаем это в файле \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;
  }
//+------------------------------------------------------------------+

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


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

В файле \MQL5\Include\DoEasy\Objects\Graph\WForms\Containers\Panel.mqh подключим файлы новых элементов управления:

//+------------------------------------------------------------------+
//| Включаемые файлы                                                 |
//+------------------------------------------------------------------+
#include "Container.mqh"
#include "..\Helpers\TabField.mqh"
#include "..\Helpers\ArrowUpButton.mqh"
#include "..\Helpers\ArrowDownButton.mqh"
#include "..\Helpers\ArrowLeftButton.mqh"
#include "..\Helpers\ArrowRightButton.mqh"
#include "..\Helpers\ArrowUpDownBox.mqh"
#include "..\Helpers\ArrowLeftRightBox.mqh"
#include "..\Helpers\HintMoveLeft.mqh"
#include "..\Helpers\HintMoveRight.mqh"
#include "..\Helpers\HintMoveUp.mqh"
#include "..\Helpers\HintMoveDown.mqh"
#include "..\Helpers\ScrollBarVertical.mqh"
#include "..\Helpers\ScrollBarHorisontal.mqh"
#include "GroupBox.mqh"
#include "TabControl.mqh"
#include "SplitContainer.mqh"
#include "..\..\WForms\Common Controls\ListBox.mqh"
#include "..\..\WForms\Common Controls\CheckedListBox.mqh"
#include "..\..\WForms\Common Controls\ButtonListBox.mqh"
#include "..\..\WForms\Common Controls\ToolTip.mqh"
#include "..\..\WForms\Common Controls\ProgressBar.mqh"
#include "..\..\WForms\GlareObj.mqh"
//+------------------------------------------------------------------+
//| Класс объекта Panel элементов управления WForms                  |
//+------------------------------------------------------------------+
class CPanel : public CContainer


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

//+------------------------------------------------------------------+
//| Создаёт новый графический объект                                 |
//+------------------------------------------------------------------+
CGCnvElement *CPanel::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_ELEMENT                 : element=new CGCnvElement(type,this.GetMain(),this.GetObject(),this.ID(),obj_num,this.ChartID(),this.SubWindow(),descript,x,y,w,h,colour,opacity,movable,activity); break;
      case GRAPH_ELEMENT_TYPE_FORM                    : element=new CForm(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h);               break;
      case GRAPH_ELEMENT_TYPE_WF_CONTAINER            : element=new CContainer(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h);          break;
      case GRAPH_ELEMENT_TYPE_WF_GROUPBOX             : element=new CGroupBox(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h);           break;
      case GRAPH_ELEMENT_TYPE_WF_PANEL                : element=new CPanel(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h);              break;
      case GRAPH_ELEMENT_TYPE_WF_LABEL                : element=new CLabel(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h);              break;
      case GRAPH_ELEMENT_TYPE_WF_CHECKBOX             : element=new CCheckBox(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h);           break;
      case GRAPH_ELEMENT_TYPE_WF_RADIOBUTTON          : element=new CRadioButton(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h);        break;
      case GRAPH_ELEMENT_TYPE_WF_BUTTON               : element=new CButton(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h);             break;
      case GRAPH_ELEMENT_TYPE_WF_LIST_BOX             : element=new CListBox(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h);            break;
      case GRAPH_ELEMENT_TYPE_WF_LIST_BOX_ITEM        : element=new CListBoxItem(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h);        break;
      case GRAPH_ELEMENT_TYPE_WF_CHECKED_LIST_BOX     : element=new CCheckedListBox(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h);     break;
      case GRAPH_ELEMENT_TYPE_WF_BUTTON_LIST_BOX      : element=new CButtonListBox(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h);      break;
      case GRAPH_ELEMENT_TYPE_WF_TAB_HEADER           : element=new CTabHeader(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h);          break;
      case GRAPH_ELEMENT_TYPE_WF_TAB_FIELD            : element=new CTabField(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h);           break;
      case GRAPH_ELEMENT_TYPE_WF_TAB_CONTROL          : element=new CTabControl(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h);         break;
      case GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON         : element=new CArrowButton(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_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_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;
      case GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTONS_UD_BOX : element=new CArrowUpDownBox(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h);     break;
      case GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTONS_LR_BOX : element=new CArrowLeftRightBox(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h);  break;
      case GRAPH_ELEMENT_TYPE_WF_SPLIT_CONTAINER      : element=new CSplitContainer(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h);     break;
      case GRAPH_ELEMENT_TYPE_WF_SPLITTER             : element=new CSplitter(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h);           break;
      case GRAPH_ELEMENT_TYPE_WF_HINT_BASE            : element=new CHintBase(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h);           break;
      case GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_LEFT       : element=new CHintMoveLeft(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h);       break;
      case GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_RIGHT      : element=new CHintMoveRight(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h);      break;
      case GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_UP         : element=new CHintMoveUp(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h);         break;
      case GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_DOWN       : element=new CHintMoveDown(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h);       break;
      case GRAPH_ELEMENT_TYPE_WF_TOOLTIP              : element=new CToolTip(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h);            break;
      case GRAPH_ELEMENT_TYPE_WF_PROGRESS_BAR         : element=new CProgressBar(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h);        break;
      case GRAPH_ELEMENT_TYPE_WF_SCROLL_BAR           : element=new CScrollBar(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h);          break;
      case GRAPH_ELEMENT_TYPE_WF_SCROLL_BAR_VERTICAL  : element=new CScrollBarVertical(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h);  break;
      case GRAPH_ELEMENT_TYPE_WF_SCROLL_BAR_HORISONTAL: element=new CScrollBarHorisontal(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;
  }
//+------------------------------------------------------------------+

Теперь в этом объекте мы сможем создавать прикреплённые к нему объекты-полосы прокрутки.

Точно такие же строки нам необходимо добавить и в других классах объектов-контейнеров в эти же методы.

Эти изменения уже внесены в файлы библиотеки:

SplitContainerPanel.mqh, Containers\TabControl.mqh, TabField.mqh, SplitContainer.mqh.

Посмотрим что у нас получилось.


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

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

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

//--- Отобразим и перерисуем все созданные панели
   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.Redraw(true);
         //--- Получаем указатель на объект-горизонтальную полосу прокрутки основной панели
         CScrollBarHorisontal *sbh=pnl.GetElementByType(GRAPH_ELEMENT_TYPE_WF_SCROLL_BAR_HORISONTAL,0);
         //--- Устанавливаем для объекта флаг его отображения и показываем полосу прокрутки
         sbh.SetDisplayed(true);
         sbh.Show();
         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);
         //--- Ожидаем 1/10 секунды
         Sleep(100);
         //--- Получаем ширину объекта ProgressBar
         int w=pb.Width();
         //--- В цикле с задержкой 1/50 увеличиваем ширину ProgressBar на 180 пикселей
         for(int n=0;n<180;n++)
           {
            Sleep(20);
            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();
         //--- Ожидаем 1/5 секунды
         Sleep(200);
         //--- Если стиль полосы прогресса не "Непрерывная прокрутка"
         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(20);
              }
           }
         //--- Ожидаем 1/2 секунды, устанавливаем для описания тип шрифта Bold и пишем на полосе прогресса сообщение о выполнении
         Sleep(500);
         pb.SetBarDescriptionFontFlags(FW_BOLD);
         pb.SetBarDescriptionText("Progress Bar: Done");
         //--- Устанавливаем для объекта-блика тип - прямоугольник, непрозрачность 40, цвет - белый
         pb.SetGlareStyle(CANV_ELEMENT_VISUAL_EFF_STYLE_RECTANGLE);
         pb.SetGlareOpacity(40);
         pb.SetGlareColor(clrWhite);
        }
     }


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


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


Что дальше

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

К содержанию

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

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

Прикрепленные файлы |
MQL5.zip (4628.1 KB)
Рецепты MQL5 — Сервисы Рецепты MQL5 — Сервисы
В статье описаны разносторонние возможности сервисов — таких MQL5-программ, для которых не нужен график привязки. Приводятся отличия сервисов от других MQL5-программ, подчёркиваются нюансы работы разработчика с сервисами. В качестве примеров читателю предложены различные задачи, охватывающие широкий спектр функционала, который может быть реализован в виде cервиса.
Популяционные алгоритмы оптимизации: Алгоритм оптимизации с кукушкой (Cuckoo Optimization Algorithm — COA) Популяционные алгоритмы оптимизации: Алгоритм оптимизации с кукушкой (Cuckoo Optimization Algorithm — COA)
Следующий алгоритм, который рассмотрим — оптимизация поиском кукушки с использованием полётов Леви. Это один из новейших алгоритмов оптимизации и новый лидер в рейтинговой таблице.
Популяционные алгоритмы оптимизации: Поиск косяком рыб (Fish School Search — FSS) Популяционные алгоритмы оптимизации: Поиск косяком рыб (Fish School Search — FSS)
Поиск косяком рыб (FSS) — новый современный алгоритм оптимизации, вдохновленный поведением рыб в стае, большинство из которых, до 80%, плавают организовано в сообществе сородичей. Доказано, что объединения рыб играют важную роль в эффективности поиска пропитания и защиты от хищников.
Разработка торговой системы на основе индикатора Alligator Разработка торговой системы на основе индикатора Alligator
Это новая статья из серии, в которой мы учимся создавать торговые системы по показателям самых популярных технических индикаторов. В ней мы будем изучать индикатор Alligator, а также создадим на его основе торговые системы.