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

Artyom Trishkin | 30 сентября, 2022

Содержание


Концепция

Сегодня начнём разрабатывать элемент управления SplitContainer из набора элементов MS Visual Studio. Этот элемент состоит из двух панелей, разделённых вертикальным или горизонтальным перемещаемым разделителем. На каждой панели можно размещать любые другие элементы управления:


Объект будет создан на основе класса WinForms-объекта "Контейнер" — основой будет полностью прозрачный контейнер, на котором разместим два вспомогательных объекта — SplitContainerPanel, тоже являющимися наследниками класса CContainer.
У каждого нового создаваемого сегодня объекта будет свой набор методов, предоставляющий функционал для работы с итоговым объектом SplitContainer.

Сегодня это будет простой статический объект, состоящий из двух панелей, разделённых вертикальным разделителем со значениями параметров по умолчанию, которые задаются при создании такого элемента управления в MS Visual Studio: дистанция разделителя от левого края объекта будет равна пятидесяти пикселям, ширина разделителя будет равна четырём пикселям. Обе панели будут в развёрнутом состоянии, но вот тип рамки сделаем по типу Fixed3D, а не None, как это задано по умолчанию в студии, где есть всего три режима выбора типа рамки:

(рамка отсутствует, простая рамка и объёмная, при которой панели выглядят вдавленным полем)

У нас в библиотеке есть более широкий выбор типов рамок:

//+------------------------------------------------------------------+
//| Стили рамок                                                      |
//+------------------------------------------------------------------+
enum ENUM_FRAME_STYLE
  {
   FRAME_STYLE_NONE,                            // Нет рамки
   FRAME_STYLE_SIMPLE,                          // Простая рамка
   FRAME_STYLE_FLAT,                            // Плоская рамка
   FRAME_STYLE_BEVEL,                           // Рельефная (выпуклая)
   FRAME_STYLE_STAMP,                           // Рельефная (вдавленная)
  };
//+------------------------------------------------------------------+

На данном этапе будет сделана панель в виде вдавленного поля (FRAME_STYLE_STAMP), но можно будет сделать тип рамки и FRAME_STYLE_BEVEL .

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


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

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


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

#define CLR_DEF_CONTROL_TAB_HEAD_BORDER_COLOR_ON (C'0xDD,0xDD,0xDD') // Цвет рамки заголовка элементов управления TabPage в состоянии "Включено"
#define CLR_DEF_CONTROL_TAB_HEAD_BORDER_DOWN_ON  (C'0xDD,0xDD,0xDD') // Цвет рамки заголовка элементов управления TabPage при нажатии мышки на элемент управления в состоянии "Включено"
#define CLR_DEF_CONTROL_TAB_HEAD_BORDER_OVER_ON  (C'0xDD,0xDD,0xDD') // Цвет рамки заголовка элементов управления TabPage при наведении мышки на элемент управления в состоянии "Включено"

#define CLR_DEF_CONTROL_SPLIT_CONTAINER_BACK_COLOR   (C'0xF0,0xF0,0xF0')// Цвет фона элемента управления SplitContainer
#define CLR_DEF_CONTROL_SPLIT_CONTAINER_MOUSE_DOWN   (C'0xF0,0xF0,0xF0')// Цвет фона элемента управления SplitContainer при нажатии мышки на элемент управления
#define CLR_DEF_CONTROL_SPLIT_CONTAINER_MOUSE_OVER   (C'0xF0,0xF0,0xF0')// Цвет фона элемента управления SplitContainer при наведении мышки на элемент управления
#define CLR_DEF_CONTROL_SPLIT_CONTAINER_BORDER_COLOR (C'0x65,0x65,0x65')// Цвет рамки элемента управления SplitContainer

#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
   //--- Вспомогательные элементы 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
  };
//+------------------------------------------------------------------+


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

Создадим перечисление для указания панели, не изменяющей свои размеры:
//+------------------------------------------------------------------+
//| Номер панели в Split Container,                                  |
//| сохраняющая размеры при их изменении у контейнера                |
//+------------------------------------------------------------------+
enum ENUM_CANV_ELEMENT_SPLIT_CONTAINER_FIXED_PANEL
  {
   CANV_ELEMENT_SPLIT_CONTAINER_FIXED_PANEL_NONE,     // None
   CANV_ELEMENT_SPLIT_CONTAINER_FIXED_PANEL_1,        // Panel1
   CANV_ELEMENT_SPLIT_CONTAINER_FIXED_PANEL_2,        // Panel2
  };
//+------------------------------------------------------------------+


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

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

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

   CANV_ELEMENT_PROP_TAB_PAGE_COLUMN,                 // Номер столбца вкладки
   CANV_ELEMENT_PROP_ALIGNMENT,                       // Местоположение объекта внутри элемента управления
   CANV_ELEMENT_PROP_SPLIT_CONTAINER_FIXED_PANEL,     // Панель, сохраняющая свои размеры при изменении размера контейнера
   CANV_ELEMENT_PROP_SPLIT_CONTAINER_SPLITTER_FIXED,  // Флаг перемещаемости разделителя
   CANV_ELEMENT_PROP_SPLIT_CONTAINER_SPLITTER_DISTANCE,// Расстояние от края до разделителя
   CANV_ELEMENT_PROP_SPLIT_CONTAINER_SPLITTER_WIDTH,  // Толщина разделителя
   CANV_ELEMENT_PROP_SPLIT_CONTAINER_PANEL1_COLLAPSED,// Флаг свёрнутости панели 1
   CANV_ELEMENT_PROP_SPLIT_CONTAINER_PANEL1_MIN_SIZE, // Минимальный размер панели 1
   CANV_ELEMENT_PROP_SPLIT_CONTAINER_PANEL2_COLLAPSED,// Флаг свёрнутости панели 1
   CANV_ELEMENT_PROP_SPLIT_CONTAINER_PANEL2_MIN_SIZE, // Минимальный размер панели 2
  };
#define CANV_ELEMENT_PROP_INTEGER_TOTAL (105)         // Общее количество целочисленных свойств
#define CANV_ELEMENT_PROP_INTEGER_SKIP  (0)           // Количество неиспользуемых в сортировке целочисленных свойств
//+------------------------------------------------------------------+


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

//+------------------------------------------------------------------+
//| Возможные критерии сортировки графических элементов на канвасе   |
//+------------------------------------------------------------------+
#define FIRST_CANV_ELEMENT_DBL_PROP  (CANV_ELEMENT_PROP_INTEGER_TOTAL-CANV_ELEMENT_PROP_INTEGER_SKIP)
#define FIRST_CANV_ELEMENT_STR_PROP  (CANV_ELEMENT_PROP_INTEGER_TOTAL-CANV_ELEMENT_PROP_INTEGER_SKIP+CANV_ELEMENT_PROP_DOUBLE_TOTAL-CANV_ELEMENT_PROP_DOUBLE_SKIP)
enum ENUM_SORT_CANV_ELEMENT_MODE
  {
//--- Сортировка по целочисленным свойствам
   SORT_BY_CANV_ELEMENT_ID = 0,                       // Сортировать по идентификатору элемента
   SORT_BY_CANV_ELEMENT_TYPE,                         // Сортировать по типу графического элемента

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

   SORT_BY_CANV_ELEMENT_TAB_PAGE_COLUMN,              // Сортировать по номеру столбца вкладки
   SORT_BY_CANV_ELEMENT_ALIGNMENT,                    // Сортировать по местоположению объекта внутри элемента управления
   SORT_BY_CANV_ELEMENT_SPLIT_CONTAINER_FIXED_PANEL,     // Сортировать по панели, сохраняющей свои размеры при изменении размера контейнера
   SORT_BY_CANV_ELEMENT_SPLIT_CONTAINER_SPLITTER_FIXED,  // Сортировать по флагу перемещаемости разделителя
   SORT_BY_CANV_ELEMENT_SPLIT_CONTAINER_SPLITTER_DISTANCE,// Сортировать по расстоянию от края до разделителя
   SORT_BY_CANV_ELEMENT_SPLIT_CONTAINER_SPLITTER_WIDTH,  // Сортировать по толщине разделителя
   SORT_BY_CANV_ELEMENT_SPLIT_CONTAINER_PANEL1_COLLAPSED,// Сортировать по флагу свёрнутости панели 1
   SORT_BY_CANV_ELEMENT_SPLIT_CONTAINER_PANEL1_MIN_SIZE, // Сортировать по минимальному размеру панели 1
   SORT_BY_CANV_ELEMENT_SPLIT_CONTAINER_PANEL2_COLLAPSED,// Сортировать по флагу свёрнутости панели 1
   SORT_BY_CANV_ELEMENT_SPLIT_CONTAINER_PANEL2_MIN_SIZE, // Сортировать по минимальному размеру панели 2
//--- Сортировка по вещественным свойствам

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

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


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

   MSG_GRAPH_ELEMENT_TYPE_WF_TAB_FIELD,               // Поле вкладки элемента управления TabControl
   MSG_GRAPH_ELEMENT_TYPE_WF_TAB_CONTROL,             // Элемент управления TabControl
   MSG_GRAPH_ELEMENT_TYPE_WF_SPLIT_CONTAINER_PANEL,   // Панель элемента управления SplitContainerPanel
   MSG_GRAPH_ELEMENT_TYPE_WF_SPLIT_CONTAINER,         // Элемент управления SplitContainer
   MSG_GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON,            // Элемент управления ArrowButton
   MSG_GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_UP,         // Элемент управления UpArrowButton

...

//--- CTabControl
   MSG_ERR_FAILED_GET_TAB_OBJ,                        // Не удалось получить вкладку элемента управления TabControl

//--- CSplitContainer
   MSG_ERR_FAILED_GET_SPLIT_CONTAINER_PANEL_OBJ,      // Не удалось получить панель элемента управления SplitContainer

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

...

   MSG_CANV_ELEMENT_PROP_TAB_PAGE_COLUMN,             // Номер столбца вкладки
   MSG_CANV_ELEMENT_PROP_ALIGNMENT,                   // Местоположение объекта внутри элемента управления
   
   MSG_CANV_ELEMENT_PROP_SPLIT_CONTAINER_FIXED_PANEL, // Панель, сохраняющая свои размеры при изменении размера контейнера
   MSG_CANV_ELEMENT_PROP_SPLIT_CONTAINER_SPLITTER_FIXED,    // Флаг перемещаемости разделителя
   MSG_CANV_ELEMENT_PROP_SPLIT_CONTAINER_SPLITTER_DISTANCE, // Расстояние от края до разделителя
   MSG_CANV_ELEMENT_PROP_SPLIT_CONTAINER_SPLITTER_WIDTH,    // Толщина разделителя
   MSG_CANV_ELEMENT_PROP_SPLIT_CONTAINER_PANEL1_COLLAPSED,  // Флаг свёрнутости панели 1
   MSG_CANV_ELEMENT_PROP_SPLIT_CONTAINER_PANEL1_MIN_SIZE,   // Минимальный размер панели 1
   MSG_CANV_ELEMENT_PROP_SPLIT_CONTAINER_PANEL2_COLLAPSED,  // Флаг свёрнутости панели 1
   MSG_CANV_ELEMENT_PROP_SPLIT_CONTAINER_PANEL2_MIN_SIZE,   // Минимальный размер панели 2
   
//--- Вещественные свойства графических элементов

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

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

   {"Поле вкладки элемента управления \"TabControl\"","Tab field of the Control element \"TabControl\""},
   {"Элемент управления \"TabControl\"","Control element \"TabControl\""},
   {"Панель элемента управления \"SplitContainerPanel\"","Panel of the Control element \"SplitContainerPanel\""},
   {"Элемент управления \"SplitContainer\"","Control element \"SplitContainer\""},
   {"Элемент управления \"ArrowButton\"","Control element \"ArrowButton\""},
   {"Элемент управления \"UpArrowButton\"","Control element \"UpArrowButton\""},

...

//--- CTabControl
   {"Не удалось получить вкладку элемента управления TabControl","Failed to get tab of TabControl"},

//--- CSplitContainer
   {"Не удалось получить панель элемента управления SplitContainer","Failed to get panel of SplitContainer control"},

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

...

   {"Номер столбца вкладки","Tab column number"},
   {"Местоположение объекта внутри элемента управления","Location of the object inside the control"},
   {"Панель, сохраняющая свои размеры при изменении размера контейнера","Panel that retains its size when the container is resized"},
   {"Флаг перемещаемости разделителя","Separator relocatability flag"},
   {"Расстояние от края до разделителя","Distance from edge to separator"},
   {"Толщина разделителя","Separator Width"},
   {"Флаг свёрнутости панели 1","Flag to indicate that panel 1 is collapsed"},
   {"Минимальный размер панели 1","Min size of Panel 1"},
   {"Флаг свёрнутости панели 2","Flag to indicate that panel 2 is collapsed"},
   {"Минимальный размер панели 2","Min size of Panel 2"},

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


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

В файле \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_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) :
      "Unknown"
     );
  }  
//+------------------------------------------------------------------+

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


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

//+------------------------------------------------------------------+
//| Возвращает описание целочисленного свойства элемента             |
//+------------------------------------------------------------------+
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_ALIGNMENT                    ?  CMessage::Text(MSG_CANV_ELEMENT_PROP_ALIGNMENT)+
         (only_prop ? "" : !this.SupportProperty(property)     ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+AlignmentDescription((ENUM_CANV_ELEMENT_ALIGNMENT)this.GetProperty(property))
         )  :
      property==CANV_ELEMENT_PROP_SPLIT_CONTAINER_FIXED_PANEL  ?  CMessage::Text(MSG_CANV_ELEMENT_PROP_SPLIT_CONTAINER_FIXED_PANEL)+
         (only_prop ? "" : !this.SupportProperty(property)     ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)this.GetProperty(property)
         )  :
      property==CANV_ELEMENT_PROP_SPLIT_CONTAINER_SPLITTER_FIXED  ?  CMessage::Text(MSG_CANV_ELEMENT_PROP_SPLIT_CONTAINER_SPLITTER_FIXED)+
         (only_prop ? "" : !this.SupportProperty(property)     ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)this.GetProperty(property)
         )  :
      property==CANV_ELEMENT_PROP_SPLIT_CONTAINER_SPLITTER_DISTANCE  ?  CMessage::Text(MSG_CANV_ELEMENT_PROP_SPLIT_CONTAINER_SPLITTER_DISTANCE)+
         (only_prop ? "" : !this.SupportProperty(property)     ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)this.GetProperty(property)
         )  :
      property==CANV_ELEMENT_PROP_SPLIT_CONTAINER_SPLITTER_WIDTH  ?  CMessage::Text(MSG_CANV_ELEMENT_PROP_SPLIT_CONTAINER_SPLITTER_WIDTH)+
         (only_prop ? "" : !this.SupportProperty(property)     ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)this.GetProperty(property)
         )  :
      property==CANV_ELEMENT_PROP_SPLIT_CONTAINER_PANEL1_COLLAPSED   ?  CMessage::Text(MSG_CANV_ELEMENT_PROP_SPLIT_CONTAINER_PANEL1_COLLAPSED)+
         (only_prop ? "" : !this.SupportProperty(property)     ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)this.GetProperty(property)
         )  :
      property==CANV_ELEMENT_PROP_SPLIT_CONTAINER_PANEL1_MIN_SIZE ?  CMessage::Text(MSG_CANV_ELEMENT_PROP_SPLIT_CONTAINER_PANEL1_MIN_SIZE)+
         (only_prop ? "" : !this.SupportProperty(property)     ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)this.GetProperty(property)
         )  :
      property==CANV_ELEMENT_PROP_SPLIT_CONTAINER_PANEL2_COLLAPSED   ?  CMessage::Text(MSG_CANV_ELEMENT_PROP_SPLIT_CONTAINER_PANEL2_COLLAPSED)+
         (only_prop ? "" : !this.SupportProperty(property)     ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)this.GetProperty(property)
         )  :
      property==CANV_ELEMENT_PROP_SPLIT_CONTAINER_PANEL2_MIN_SIZE ?  CMessage::Text(MSG_CANV_ELEMENT_PROP_SPLIT_CONTAINER_PANEL2_MIN_SIZE)+
         (only_prop ? "" : !this.SupportProperty(property)     ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)this.GetProperty(property)
         )  :
      ""
     );
  }
//+------------------------------------------------------------------+

В зависимости от свойства, переданного в метод, составляется текстовое сообщение и возвращается описание свойства либо со значением этого свойства, либо без — в зависимости от состояния флага only_prop.

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


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

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

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

   struct SData
     {
      //--- Целочисленные свойства объекта
      int            id;                                       // Идентификатор элемента
      int            type;                                     // Тип графического элемента

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

      int            visible_area_h;                           // Высота области видимости
      bool           displayed;                                // Флаг отображения не скрытого элемента управления
      int            split_container_fixed_panel;              // Панель, сохраняющая свои размеры при изменении размера контейнера
      bool           split_container_splitter_fixed;           // Флаг перемещаемости разделителя
      int            split_container_splitter_distance;        // Расстояние от края до разделителя
      int            split_container_splitter_width;           // Толщина разделителя
      bool           split_container_panel1_collapsed;         // Флаг свёрнутости панели 1
      int            split_container_panel1_min_size;          // Минимальный размер панели 1
      bool           split_container_panel2_collapsed;         // Флаг свёрнутости панели 2
      int            split_container_panel2_min_size;          // Минимальный размер панели 2
      //--- Вещественные свойства объекта

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


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

//+------------------------------------------------------------------+
//| Параметрический конструктор                                      |
//+------------------------------------------------------------------+
CGCnvElement::CGCnvElement(const ENUM_GRAPH_ELEMENT_TYPE element_type,
                           const int      element_id,
                           const int      element_num,
                           const long     chart_id,
                           const int      wnd_num,
                           const string   descript,
                           const int      x,
                           const int      y,
                           const int      w,
                           const int      h,
                           const color    colour,
                           const uchar    opacity,
                           const bool     movable=true,
                           const bool     activity=true,
                           const bool     redraw=false) : m_shadow(false)
  {
   this.SetTypeElement(element_type);
   this.m_type=OBJECT_DE_TYPE_GELEMENT; 
   this.m_element_main=NULL;
   this.m_element_base=NULL;
   this.m_chart_color_bg=(color)::ChartGetInteger((chart_id==NULL ? ::ChartID() : chart_id),CHART_COLOR_BACKGROUND);
   this.m_name=this.CreateNameGraphElement(element_type);
   this.m_chart_id=(chart_id==NULL || chart_id==0 ? ::ChartID() : chart_id);
   this.m_subwindow=wnd_num;
   this.SetFont(DEF_FONT,DEF_FONT_SIZE);
   this.m_text_anchor=0;
   this.m_text_x=0;
   this.m_text_y=0;
   this.SetBackgroundColor(colour,true);
   this.SetOpacity(opacity);
   this.m_shift_coord_x=0;
   this.m_shift_coord_y=0;
   if(::ArrayResize(this.m_array_colors_bg,1)==1)
      this.m_array_colors_bg[0]=this.BackgroundColor();
   if(::ArrayResize(this.m_array_colors_bg_dwn,1)==1)
      this.m_array_colors_bg_dwn[0]=this.BackgroundColor();
   if(::ArrayResize(this.m_array_colors_bg_ovr,1)==1)
      this.m_array_colors_bg_ovr[0]=this.BackgroundColor();
   if(this.Create(chart_id,wnd_num,x,y,w,h,redraw))
     {
      this.SetProperty(CANV_ELEMENT_PROP_NAME_RES,this.m_canvas.ResourceName()); // Имя графического ресурса
      this.SetProperty(CANV_ELEMENT_PROP_CHART_ID,CGBaseObj::ChartID());         // Идентификатор графика

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

      this.SetProperty(CANV_ELEMENT_PROP_TEXT,"");                                                    // Текст графического элемента
      this.SetProperty(CANV_ELEMENT_PROP_DESCRIPTION,descript);                                       // Описание графического элемента
      this.SetProperty(CANV_ELEMENT_PROP_SPLIT_CONTAINER_FIXED_PANEL,0);                              // Панель, сохраняющая свои размеры при изменении размера контейнера
      this.SetProperty(CANV_ELEMENT_PROP_SPLIT_CONTAINER_SPLITTER_FIXED,true);                        // Флаг перемещаемости разделителя
      this.SetProperty(CANV_ELEMENT_PROP_SPLIT_CONTAINER_SPLITTER_DISTANCE,50);                       // Расстояние от края до разделителя
      this.SetProperty(CANV_ELEMENT_PROP_SPLIT_CONTAINER_SPLITTER_WIDTH,4);                           // Толщина разделителя
      this.SetProperty(CANV_ELEMENT_PROP_SPLIT_CONTAINER_PANEL1_COLLAPSED,false);                     // Флаг свёрнутости панели 1
      this.SetProperty(CANV_ELEMENT_PROP_SPLIT_CONTAINER_PANEL1_MIN_SIZE,25);                         // Минимальный размер панели 1
      this.SetProperty(CANV_ELEMENT_PROP_SPLIT_CONTAINER_PANEL2_COLLAPSED,false);                     // Флаг свёрнутости панели 1
      this.SetProperty(CANV_ELEMENT_PROP_SPLIT_CONTAINER_PANEL2_MIN_SIZE,25);                         // Минимальный размер панели 2
      this.SetVisibleFlag(false,false);
     }
   else
     {
      ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_FAILED_CREATE_ELM_OBJ),"\"",this.TypeElementDescription(element_type),"\" ",this.NameObj());
     }
  }
//+------------------------------------------------------------------+
//| Защищённый конструктор                                           |
//+------------------------------------------------------------------+
CGCnvElement::CGCnvElement(const ENUM_GRAPH_ELEMENT_TYPE element_type,
                           const long    chart_id,
                           const int     wnd_num,
                           const string  descript,
                           const int     x,
                           const int     y,
                           const int     w,
                           const int     h) : m_shadow(false)
  {
   this.m_type=OBJECT_DE_TYPE_GELEMENT; 
   this.m_element_main=NULL;
   this.m_element_base=NULL;
   this.m_chart_color_bg=(color)::ChartGetInteger((chart_id==NULL ? ::ChartID() : chart_id),CHART_COLOR_BACKGROUND);
   this.m_name=this.CreateNameGraphElement(element_type);
   this.m_chart_id=(chart_id==NULL || chart_id==0 ? ::ChartID() : chart_id);
   this.m_subwindow=wnd_num;
   this.m_type_element=element_type;
   this.SetFont(DEF_FONT,DEF_FONT_SIZE);
   this.m_text_anchor=0;
   this.m_text_x=0;
   this.m_text_y=0;
   this.SetBackgroundColor(CLR_CANV_NULL,true);
   this.SetOpacity(0);
   this.m_shift_coord_x=0;
   this.m_shift_coord_y=0;
   if(::ArrayResize(this.m_array_colors_bg,1)==1)
      this.m_array_colors_bg[0]=this.BackgroundColor();
   if(::ArrayResize(this.m_array_colors_bg_dwn,1)==1)
      this.m_array_colors_bg_dwn[0]=this.BackgroundColor();
   if(::ArrayResize(this.m_array_colors_bg_ovr,1)==1)
      this.m_array_colors_bg_ovr[0]=this.BackgroundColor();
   if(this.Create(chart_id,wnd_num,x,y,w,h,false))
     {
      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_TEXT,"");                                                    // Текст графического элемента
      this.SetProperty(CANV_ELEMENT_PROP_DESCRIPTION,descript);                                       // Описание графического элемента
      this.SetProperty(CANV_ELEMENT_PROP_SPLIT_CONTAINER_FIXED_PANEL,0);                              // Панель, сохраняющая свои размеры при изменении размера контейнера
      this.SetProperty(CANV_ELEMENT_PROP_SPLIT_CONTAINER_SPLITTER_FIXED,true);                        // Флаг перемещаемости разделителя
      this.SetProperty(CANV_ELEMENT_PROP_SPLIT_CONTAINER_SPLITTER_DISTANCE,50);                       // Расстояние от края до разделителя
      this.SetProperty(CANV_ELEMENT_PROP_SPLIT_CONTAINER_SPLITTER_WIDTH,4);                           // Толщина разделителя
      this.SetProperty(CANV_ELEMENT_PROP_SPLIT_CONTAINER_PANEL1_COLLAPSED,false);                     // Флаг свёрнутости панели 1
      this.SetProperty(CANV_ELEMENT_PROP_SPLIT_CONTAINER_PANEL1_MIN_SIZE,25);                         // Минимальный размер панели 1
      this.SetProperty(CANV_ELEMENT_PROP_SPLIT_CONTAINER_PANEL2_COLLAPSED,false);                     // Флаг свёрнутости панели 1
      this.SetProperty(CANV_ELEMENT_PROP_SPLIT_CONTAINER_PANEL2_MIN_SIZE,25);                         // Минимальный размер панели 2
      this.SetVisibleFlag(false,false);
     }
   else
     {
      ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_FAILED_CREATE_ELM_OBJ),"\"",this.TypeElementDescription(element_type),"\" ",this.NameObj());
     }
  }
//+------------------------------------------------------------------+

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


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

//+------------------------------------------------------------------+
//| Создаёт структуру объекта                                        |
//+------------------------------------------------------------------+
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.tab_alignment=(int)this.GetProperty(CANV_ELEMENT_PROP_TAB_ALIGNMENT);                              // Местоположение вкладок внутри элемента управления
   this.m_struct_obj.alignment=(int)this.GetProperty(CANV_ELEMENT_PROP_ALIGNMENT);                                      // Местоположение объекта внутри элемента управления
   
   this.m_struct_obj.split_container_fixed_panel=(bool)this.GetProperty(CANV_ELEMENT_PROP_SPLIT_CONTAINER_FIXED_PANEL);             // Панель, сохраняющая свои размеры при изменении размера контейнера
   this.m_struct_obj.split_container_splitter_fixed=(bool)this.GetProperty(CANV_ELEMENT_PROP_SPLIT_CONTAINER_SPLITTER_FIXED);       // Флаг перемещаемости разделителя
   this.m_struct_obj.split_container_splitter_distance=(int)this.GetProperty(CANV_ELEMENT_PROP_SPLIT_CONTAINER_SPLITTER_DISTANCE);  // Расстояние от края до разделителя
   this.m_struct_obj.split_container_splitter_width=(int)this.GetProperty(CANV_ELEMENT_PROP_SPLIT_CONTAINER_SPLITTER_WIDTH);        // Толщина разделителя
   this.m_struct_obj.split_container_panel1_collapsed=(bool)this.GetProperty(CANV_ELEMENT_PROP_SPLIT_CONTAINER_PANEL1_COLLAPSED);   // Флаг свёрнутости панели 1
   this.m_struct_obj.split_container_panel1_min_size=(int)this.GetProperty(CANV_ELEMENT_PROP_SPLIT_CONTAINER_PANEL1_MIN_SIZE);      // Минимальный размер панели 1
   this.m_struct_obj.split_container_panel2_collapsed=(bool)this.GetProperty(CANV_ELEMENT_PROP_SPLIT_CONTAINER_PANEL2_COLLAPSED);   // Флаг свёрнутости панели 1
   this.m_struct_obj.split_container_panel2_min_size=(int)this.GetProperty(CANV_ELEMENT_PROP_SPLIT_CONTAINER_PANEL2_MIN_SIZE);      // Минимальный размер панели 2
//--- Сохранение вещественных свойств

//--- Сохранение строковых свойств
   ::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);// Описание графического элемента
   //--- Сохранение структуры в 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_TAB_ALIGNMENT,this.m_struct_obj.tab_alignment);                             // Местоположение вкладок внутри элемента управления
   this.SetProperty(CANV_ELEMENT_PROP_ALIGNMENT,this.m_struct_obj.alignment);                                     // Местоположение объекта внутри элемента управления
   
   this.SetProperty(CANV_ELEMENT_PROP_SPLIT_CONTAINER_FIXED_PANEL,this.m_struct_obj.split_container_fixed_panel);             // Панель, сохраняющая свои размеры при изменении размера контейнера
   this.SetProperty(CANV_ELEMENT_PROP_SPLIT_CONTAINER_SPLITTER_FIXED,this.m_struct_obj.split_container_splitter_fixed);       // Флаг перемещаемости разделителя
   this.SetProperty(CANV_ELEMENT_PROP_SPLIT_CONTAINER_SPLITTER_DISTANCE,this.m_struct_obj.split_container_splitter_distance); // Расстояние от края до разделителя
   this.SetProperty(CANV_ELEMENT_PROP_SPLIT_CONTAINER_SPLITTER_WIDTH,this.m_struct_obj.split_container_splitter_width);       // Толщина разделителя
   this.SetProperty(CANV_ELEMENT_PROP_SPLIT_CONTAINER_PANEL1_COLLAPSED,this.m_struct_obj.split_container_panel1_collapsed);   // Флаг свёрнутости панели 1
   this.SetProperty(CANV_ELEMENT_PROP_SPLIT_CONTAINER_PANEL1_MIN_SIZE,this.m_struct_obj.split_container_panel1_min_size);     // Минимальный размер панели 1
   this.SetProperty(CANV_ELEMENT_PROP_SPLIT_CONTAINER_PANEL2_COLLAPSED,this.m_struct_obj.split_container_panel2_collapsed);   // Флаг свёрнутости панели 1
   this.SetProperty(CANV_ELEMENT_PROP_SPLIT_CONTAINER_PANEL2_MIN_SIZE,this.m_struct_obj.split_container_panel2_min_size);     // Минимальный размер панели 2
//--- Сохранение вещественных свойств

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

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

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


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

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

В файле \MQL5\Include\DoEasy\Objects\Graph\WForms\Helpers\TabHeader.mqh в приватной секции объявим указатель на предыдущий заголовок:

//+------------------------------------------------------------------+
//| Класс объекта TabHeader элемента управления WForms TabControl    |
//+------------------------------------------------------------------+
class CTabHeader : public CButton
  {
private:
   CTabHeader       *m_prev;                             // Предыдущий заголовок в списке
   int               m_width_off;                        // Ширина объекта в состоянии "не выбран"
   int               m_height_off;                       // Высота объекта в состоянии "не выбран"


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

   virtual void      SetPadding(const int left,const int top,const int right,const int bottom)
                       {
                        this.SetPaddingLeft(left); this.SetPaddingTop(top); this.SetPaddingRight(right); this.SetPaddingBottom(bottom);
                       }
//--- (1) Устанавливает, (2) возвращает указатель на предыдущий заголовок в списке
   void              SetPrevHeader(CTabHeader *header)      { this.m_prev=header;   }
   CTabHeader       *PrevHeader(void)                 const { return this.m_prev;   }

protected:
//--- Защищённый конструктор с указанием типа объекта, идентификатора чарта и подокна


В файле \MQL5\Include\DoEasy\Objects\Graph\WForms\Containers\TabControl.mqh класса элемента управления TabControl в методе, создающем указанное количество вкладок, после создания нового заголовка запишем в него указатель на предыдущий созданный заголовок.

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

//+------------------------------------------------------------------+
//| Создаёт указанное количество вкладок                             |
//+------------------------------------------------------------------+
bool CTabControl::CreateTabPages(const int total,const int selected_page,const int tab_w=0,const int tab_h=0,const string header_text="")
  {
//--- Рассчитываем размеры и начальные координаты заголовка вкладки
   int w=(tab_w==0 ? this.ItemWidth()  : tab_w);
   int h=(tab_h==0 ? this.ItemHeight() : tab_h);

//--- В цикле по количеству вкладок
   CTabHeader *header=NULL;
   CTabField  *field=NULL;
   for(int i=0;i<total;i++)
     {
      //--- В зависимости от расположения заголовков вкладок устанавливаем их начальные координаты
      int header_x=2;
      int header_y=2;
      int header_w=w;
      int header_h=h;
      
      //--- Устанавливаем текущую координату X или Y в зависимости от расположения заголовков вкладок

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

      //--- Получаем смещение по Y расположения заголовка после изменения его высоты и
      //--- сдвигаем его на рассчитанную величину только для расположения заголовков слева
      int y_shift=header.Height()-h_prev;
      if(header.Move(header.CoordX(),header.CoordY()-(this.Alignment()==CANV_ELEMENT_ALIGNMENT_LEFT ? y_shift : 0)))
        {
         header.SetCoordXRelative(header.CoordX()-this.CoordX());
         header.SetCoordYRelative(header.CoordY()-this.CoordY());
        }
      header.SetVisibleFlag(this.IsVisible(),false);
      //--- Записываем в заголовок указатель на предыдущий объект в списке
      CTabHeader *prev=this.GetTabHeader(i-1);
      header.SetPrevHeader(prev);
      
      //--- В зависимости от расположения заголовков вкладок устанавливаем начальные координаты полей вкладок

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

//--- Создаём объект кнопки влево-вправо
   this.CreateNewElement(GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTONS_LR_BOX,this.Width()-32,0,15,15,clrNONE,255,this.Active(),false);
//--- Получаем указатель на вновь созданный объект

   CArrowLeftRightBox *box_lr=this.GetArrLeftRightBox();
   if(box_lr!=NULL)
     {
      this.SetVisibleLeftRightBox(false);
      this.SetSizeLeftRightBox(box_lr.Width());
      box_lr.SetMain(this.GetMain());
      box_lr.SetBase(this.GetObject());
      box_lr.SetID(this.GetMaxIDAll());
      box_lr.SetBorderStyle(FRAME_STYLE_NONE);
      box_lr.SetBackgroundColor(CLR_CANV_NULL,true);
      box_lr.SetOpacity(0);
      box_lr.Hide();
      CArrowLeftButton *lb=box_lr.GetArrowLeftButton();
      if(lb!=NULL)
        {
         lb.SetMain(this.GetMain());
         lb.SetBase(box_lr);
         lb.SetID(this.GetMaxIDAll());
        }
      CArrowRightButton *rb=box_lr.GetArrowRightButton();
      if(rb!=NULL)
        {
         rb.SetMain(this.GetMain());
         rb.SetBase(box_lr);
         rb.SetID(this.GetMaxIDAll());
        }
     }
//--- Создаём объект кнопки вверх-вниз
   this.CreateNewElement(GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTONS_UD_BOX,0,this.Height()-32,15,15,clrNONE,255,this.Active(),false);
//--- Получаем указатель на вновь созданный объект
   CArrowUpDownBox *box_ud=this.GetArrUpDownBox();
   if(box_ud!=NULL)
     {
      this.SetVisibleUpDownBox(false);
      this.SetSizeUpDownBox(box_ud.Height());
      box_ud.SetMain(this.GetMain());
      box_ud.SetBase(this.GetObject());
      box_ud.SetID(this.GetMaxIDAll());
      box_ud.SetBorderStyle(FRAME_STYLE_NONE);
      box_ud.SetBackgroundColor(CLR_CANV_NULL,true);
      box_ud.SetOpacity(0);
      box_ud.Hide();
      CArrowDownButton *db=box_ud.GetArrowDownButton();
      if(db!=NULL)
        {
         db.SetMain(this.GetMain());
         db.SetBase(box_ud);
         db.SetID(this.GetMaxIDAll());
        }
      CArrowUpButton *ub=box_ud.GetArrowUpButton();
      if(ub!=NULL)
        {
         ub.SetMain(this.GetMain());
         ub.SetBase(box_ud);
         ub.SetID(this.GetMaxIDAll());
        }
     }
//--- Выстраиваем все заголовки в соответствии с установленными режимами их отображения и выбираем указанную вкладку
   this.ArrangeTabHeaders();
   this.Select(selected_page,true);
   return true;
  }
//+------------------------------------------------------------------+

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


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

      case GRAPH_ELEMENT_TYPE_ELEMENT                 : element=new CGCnvElement(type,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.ChartID(),this.SubWindow(),descript,x,y,w,h);              break;
      case GRAPH_ELEMENT_TYPE_WF_CONTAINER            : element=new CContainer(this.ChartID(),this.SubWindow(),descript,x,y,w,h);         break;
      case GRAPH_ELEMENT_TYPE_WF_GROUPBOX             : element=new CGroupBox(this.ChartID(),this.SubWindow(),descript,x,y,w,h);          break;
      case GRAPH_ELEMENT_TYPE_WF_PANEL                : element=new CPanel(this.ChartID(),this.SubWindow(),descript,x,y,w,h);             break;
      case GRAPH_ELEMENT_TYPE_WF_LABEL                : element=new CLabel(this.ChartID(),this.SubWindow(),descript,x,y,w,h);             break;
      case GRAPH_ELEMENT_TYPE_WF_CHECKBOX             : element=new CCheckBox(this.ChartID(),this.SubWindow(),descript,x,y,w,h);          break;
      case GRAPH_ELEMENT_TYPE_WF_RADIOBUTTON          : element=new CRadioButton(this.ChartID(),this.SubWindow(),descript,x,y,w,h);       break;
      case GRAPH_ELEMENT_TYPE_WF_BUTTON               : element=new CButton(this.ChartID(),this.SubWindow(),descript,x,y,w,h);            break;
      case GRAPH_ELEMENT_TYPE_WF_LIST_BOX             : element=new CListBox(this.ChartID(),this.SubWindow(),descript,x,y,w,h);           break;
      case GRAPH_ELEMENT_TYPE_WF_LIST_BOX_ITEM        : element=new CListBoxItem(this.ChartID(),this.SubWindow(),descript,x,y,w,h);       break;
      case GRAPH_ELEMENT_TYPE_WF_CHECKED_LIST_BOX     : element=new CCheckedListBox(this.ChartID(),this.SubWindow(),descript,x,y,w,h);    break;
      case GRAPH_ELEMENT_TYPE_WF_BUTTON_LIST_BOX      : element=new CButtonListBox(this.ChartID(),this.SubWindow(),descript,x,y,w,h);     break;
      case GRAPH_ELEMENT_TYPE_WF_TAB_HEADER           : element=new CTabHeader(this.ChartID(),this.SubWindow(),descript,x,y,w,h);         break;
      case GRAPH_ELEMENT_TYPE_WF_TAB_FIELD            : element=new CTabField(this.ChartID(),this.SubWindow(),descript,x,y,w,h);          break;
      case GRAPH_ELEMENT_TYPE_WF_TAB_CONTROL          : element=new CTabControl(this.ChartID(),this.SubWindow(),descript,x,y,w,h);        break;
      case GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON         : element=new CArrowButton(this.ChartID(),this.SubWindow(),descript,x,y,w,h);       break;
      case GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_UP      : element=new CArrowUpButton(this.ChartID(),this.SubWindow(),descript,x,y,w,h);     break;
      case GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_DOWN    : element=new CArrowDownButton(this.ChartID(),this.SubWindow(),descript,x,y,w,h);   break;
      case GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_LEFT    : element=new CArrowLeftButton(this.ChartID(),this.SubWindow(),descript,x,y,w,h);   break;
      case GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_RIGHT   : element=new CArrowRightButton(this.ChartID(),this.SubWindow(),descript,x,y,w,h);  break;

В итоге метод теперь будет проще и компактнее:

//+------------------------------------------------------------------+
//| Создаёт новый графический объект                                 |
//+------------------------------------------------------------------+
CGCnvElement *CTabControl::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_TAB_HEADER           : element=new CTabHeader(this.ChartID(),this.SubWindow(),descript,x,y,w,h);         break;
      case GRAPH_ELEMENT_TYPE_WF_TAB_FIELD            : element=new CTabField(this.ChartID(),this.SubWindow(),descript,x,y,w,h);          break;
      case GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTONS_UD_BOX : element=new CArrowUpDownBox(this.ChartID(),this.SubWindow(),descript,x,y,w,h);    break;
      case GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTONS_LR_BOX : element=new CArrowLeftRightBox(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;
  }
//+------------------------------------------------------------------+

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


Класс вспомогательного объекта-панели для элемента SplitContainer

Элемент управления SplitContainer будет состоять из двух панелей, на которых можно будет размещать какие-либо объекты. Сами же эти панели будут прикреплены к собственному контейнеру, который и будет основой элемента управления, и который будет управлять этими панелями при изменении своих свойств.

Панели создадим на базе класса-контейнера, так как он более всего и подходит для этих целей.

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

//+------------------------------------------------------------------+
//|                                          SplitContainerPanel.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\Panel.mqh"
//+------------------------------------------------------------------+
//| Класс объекта SplitContainerPanel                                |
//| элемента управления WForms SplitContainer                        |
//+------------------------------------------------------------------+
class CSplitContainerPanel : public CContainer
  {
  }


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

class CSplitContainerPanel : public CContainer
  {
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);
protected:
//--- Защищённый конструктор с указанием типа объекта, идентификатора чарта и подокна
                     CSplitContainerPanel(const ENUM_GRAPH_ELEMENT_TYPE type,
                                          const long chart_id,
                                          const int subwindow,
                                          const string descript,
                                          const int x,
                                          const int y,
                                          const int w,
                                          const int h);
public:
//--- Рисует рамку панели
   virtual void      DrawFrame(void);
//--- Очищает элемент с заполнением его цветом и непрозрачностью
   virtual void      Erase(const color colour,const uchar opacity,const bool redraw=false);
//--- Очищает элемент заливкой градиентом
   virtual void      Erase(color &colors[],const uchar opacity,const bool vgradient,const bool cycle,const bool redraw=false);
//--- Конструктор
                     CSplitContainerPanel(const long chart_id,
                                          const int subwindow,
                                          const string descript,
                                          const int x,
                                          const int y,
                                          const int w,
                                          const int h);
  };
//+------------------------------------------------------------------+

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


Защищённый конструктор с указанием типа объекта, идентификатора чарта и подокна:

//+------------------------------------------------------------------+
//| Защищённый конструктор с указанием типа объекта,                 |
//| идентификатора чарта и подокна                                   |
//+------------------------------------------------------------------+
CSplitContainerPanel::CSplitContainerPanel(const ENUM_GRAPH_ELEMENT_TYPE type,
                                           const long chart_id,
                                           const int subwindow,
                                           const string descript,
                                           const int x,
                                           const int y,
                                           const int w,
                                           const int h) : CContainer(type,chart_id,subwindow,descript,x,y,w,h)
  {
   this.SetTypeElement(GRAPH_ELEMENT_TYPE_WF_SPLIT_CONTAINER_PANEL);
   this.m_type=OBJECT_DE_TYPE_GWF_HELPER;
   this.SetBorderSizeAll(0);
   this.SetBorderStyle(FRAME_STYLE_STAMP);
   this.SetOpacity(CLR_DEF_CONTROL_STD_OPACITY,true);
   this.SetBackgroundColor(CLR_DEF_CONTROL_SPLIT_CONTAINER_BACK_COLOR,true);
   this.SetBackgroundColorMouseDown(this.BackgroundColor());
   this.SetBackgroundColorMouseOver(this.BackgroundColor());
   this.SetBorderColor(CLR_DEF_CONTROL_SPLIT_CONTAINER_BORDER_COLOR,true);
   this.SetBorderColorMouseDown(this.BorderColor());
   this.SetBorderColorMouseOver(this.BorderColor());
   this.SetForeColor(CLR_DEF_FORE_COLOR,true);
   this.SetPaddingAll(0);
   this.SetPaddingAll(0);
  }
//+------------------------------------------------------------------+

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


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

//+------------------------------------------------------------------+
//| Конструктор                                                      |
//+------------------------------------------------------------------+
CSplitContainerPanel::CSplitContainerPanel(const long chart_id,
                                           const int subwindow,
                                           const string descript,
                                           const int x,
                                           const int y,
                                           const int w,
                                           const int h) : CContainer(GRAPH_ELEMENT_TYPE_WF_SPLIT_CONTAINER_PANEL,chart_id,subwindow,descript,x,y,w,h)
  {
   this.SetTypeElement(GRAPH_ELEMENT_TYPE_WF_SPLIT_CONTAINER_PANEL);
   this.m_type=OBJECT_DE_TYPE_GWF_HELPER;
   this.SetBorderSizeAll(0);
   this.SetBorderStyle(FRAME_STYLE_STAMP);
   this.SetOpacity(CLR_DEF_CONTROL_STD_OPACITY,true);
   this.SetBackgroundColor(CLR_DEF_CONTROL_SPLIT_CONTAINER_BACK_COLOR,true);
   this.SetBackgroundColorMouseDown(this.BackgroundColor());
   this.SetBackgroundColorMouseOver(this.BackgroundColor());
   this.SetBorderColor(CLR_DEF_CONTROL_SPLIT_CONTAINER_BORDER_COLOR,true);
   this.SetBorderColorMouseDown(this.BorderColor());
   this.SetBorderColorMouseOver(this.BorderColor());
   this.SetForeColor(CLR_DEF_FORE_COLOR,true);
   this.SetPaddingAll(0);
   this.SetPaddingAll(0);
  }
//+------------------------------------------------------------------+

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


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

//+------------------------------------------------------------------+
//| Рисует рамку элемента                                            |
//+------------------------------------------------------------------+
void CSplitContainerPanel::DrawFrame(void)
  {
//--- Определяем координаты и размеры поля
   int x=this.BorderSizeLeft();
   int y=this.BorderSizeTop();
   int w=this.Width()-this.BorderSizeLeft()-this.BorderSizeRight();
   int h=this.Height()-this.BorderSizeTop()-this.BorderSizeBottom();
   if(w<1)
      w=1;
   if(h<1)
      h=1;
//--- Рисуем поле в соответствии с типом рамки
   switch(this.BorderStyle())
     {
      case FRAME_STYLE_STAMP  : this.DrawFieldStamp(x,y,w,h,this.BackgroundColor(),this.Opacity());   break;
      case FRAME_STYLE_BEVEL  : this.DrawFieldBevel(x,y,w,h,this.BackgroundColor(),this.Opacity());   break;
      case FRAME_STYLE_FLAT   : this.DrawFrameFlat(0,0,this.Width(),this.Height(),this.BorderSizeTop(),this.BorderSizeBottom(),this.BorderSizeLeft(),this.BorderSizeRight(),this.BorderColor(),this.Opacity());   break;
      case FRAME_STYLE_SIMPLE : this.DrawFrameSimple(0,0,this.Width(),this.Height(),this.BorderSizeTop(),this.BorderSizeBottom(),this.BorderSizeLeft(),this.BorderSizeRight(),this.BorderColor(),this.Opacity()); break;
      default: break;
     }
  }
//+------------------------------------------------------------------+

В зависимости от типа рамки, установленной для объекта, рассчитываем её начальные координаты и размеры и вызываем соответствующий метод библиотеки для рисования нужной рамки или поля.
Поле (выпуклое или вдавленное) рисуется при типе рамки Stamp и Bevel соответственно.
При выборе другого типа рамки (Flat или Simple) рисуется соответствующая выбранному типу рамка.


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

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

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

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


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

//+------------------------------------------------------------------+
//| Создаёт новый графический объект                                 |
//+------------------------------------------------------------------+
CGCnvElement *CSplitContainerPanel::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.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.ChartID(),this.SubWindow(),descript,x,y,w,h);              break;
      case GRAPH_ELEMENT_TYPE_WF_CONTAINER            : element=new CContainer(this.ChartID(),this.SubWindow(),descript,x,y,w,h);         break;
      case GRAPH_ELEMENT_TYPE_WF_GROUPBOX             : element=new CGroupBox(this.ChartID(),this.SubWindow(),descript,x,y,w,h);          break;
      case GRAPH_ELEMENT_TYPE_WF_PANEL                : element=new CPanel(this.ChartID(),this.SubWindow(),descript,x,y,w,h);             break;
      case GRAPH_ELEMENT_TYPE_WF_LABEL                : element=new CLabel(this.ChartID(),this.SubWindow(),descript,x,y,w,h);             break;
      case GRAPH_ELEMENT_TYPE_WF_CHECKBOX             : element=new CCheckBox(this.ChartID(),this.SubWindow(),descript,x,y,w,h);          break;
      case GRAPH_ELEMENT_TYPE_WF_RADIOBUTTON          : element=new CRadioButton(this.ChartID(),this.SubWindow(),descript,x,y,w,h);       break;
      case GRAPH_ELEMENT_TYPE_WF_BUTTON               : element=new CButton(this.ChartID(),this.SubWindow(),descript,x,y,w,h);            break;
      case GRAPH_ELEMENT_TYPE_WF_LIST_BOX             : element=new CListBox(this.ChartID(),this.SubWindow(),descript,x,y,w,h);           break;
      case GRAPH_ELEMENT_TYPE_WF_LIST_BOX_ITEM        : element=new CListBoxItem(this.ChartID(),this.SubWindow(),descript,x,y,w,h);       break;
      case GRAPH_ELEMENT_TYPE_WF_CHECKED_LIST_BOX     : element=new CCheckedListBox(this.ChartID(),this.SubWindow(),descript,x,y,w,h);    break;
      case GRAPH_ELEMENT_TYPE_WF_BUTTON_LIST_BOX      : element=new CButtonListBox(this.ChartID(),this.SubWindow(),descript,x,y,w,h);     break;
      case GRAPH_ELEMENT_TYPE_WF_TAB_HEADER           : element=new CTabHeader(this.ChartID(),this.SubWindow(),descript,x,y,w,h);         break;
      case GRAPH_ELEMENT_TYPE_WF_TAB_FIELD            : element=new CTabField(this.ChartID(),this.SubWindow(),descript,x,y,w,h);          break;
      case GRAPH_ELEMENT_TYPE_WF_TAB_CONTROL          : element=new CTabControl(this.ChartID(),this.SubWindow(),descript,x,y,w,h);        break;
      case GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON         : element=new CArrowButton(this.ChartID(),this.SubWindow(),descript,x,y,w,h);       break;
      case GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_UP      : element=new CArrowUpButton(this.ChartID(),this.SubWindow(),descript,x,y,w,h);     break;
      case GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_DOWN    : element=new CArrowDownButton(this.ChartID(),this.SubWindow(),descript,x,y,w,h);   break;
      case GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_LEFT    : element=new CArrowLeftButton(this.ChartID(),this.SubWindow(),descript,x,y,w,h);   break;
      case GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_RIGHT   : element=new CArrowRightButton(this.ChartID(),this.SubWindow(),descript,x,y,w,h);  break;
      case GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTONS_UD_BOX : element=new CArrowUpDownBox(this.ChartID(),this.SubWindow(),descript,x,y,w,h);    break;
      case GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTONS_LR_BOX : element=new CArrowLeftRightBox(this.ChartID(),this.SubWindow(),descript,x,y,w,h); break;
      case GRAPH_ELEMENT_TYPE_WF_SPLIT_CONTAINER      : element=new CSplitContainer(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;
  }
//+------------------------------------------------------------------+

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

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


Класс объекта элемента управления SplitContainer

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

//+------------------------------------------------------------------+
//|                                               SplitContainer.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 "Container.mqh"
#include "..\Helpers\SplitContainerPanel.mqh"
//+------------------------------------------------------------------+
//| Класс объекта-элемента управления WForms SplitContainer          |
//+------------------------------------------------------------------+
class CSplitContainer : public CContainer
  {
  }


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

//+------------------------------------------------------------------+
//| Класс объекта-элемента управления WForms SplitContainer          |
//+------------------------------------------------------------------+
class CSplitContainer : public CContainer
  {
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);
                                           
//--- Создаёт панели
   void              CreatePanels(void);
public:
//--- Возвращает указатель на указанную панель
   CSplitContainerPanel *GetPanel(const int index)             { return CForm::GetElement(index);  }
   
//--- Возвращает указатель на (1) панель1, (2) панель2
   CSplitContainerPanel *GetPanel1(void)                       { return this.GetPanel(0);          }
   CSplitContainerPanel *GetPanel2(void)                       { return this.GetPanel(1);          }
   
//--- Возвращает с указанной панели элемент (1) по индексу, (2) по типу и индексу, (3) по имени
   CGCnvElement     *GetPanelElement(const int panel,const int index);
   CGCnvElement     *GetPanelElementByType(const int panel,const ENUM_GRAPH_ELEMENT_TYPE type,const int index);
   CGCnvElement     *GetPanelElementByName(const int panel,const string name);
   
//--- (1) устанавливает, (2) возвращает минимально-возможный размер панели 1 и 2
   void              SetPanel1MinSize(const int value)         { this.SetProperty(CANV_ELEMENT_PROP_SPLIT_CONTAINER_PANEL1_MIN_SIZE,value);           }
   int               Panel1MinSize(void)                 const { return (int)this.GetProperty(CANV_ELEMENT_PROP_SPLIT_CONTAINER_PANEL1_MIN_SIZE);     }
   void              SetPanel2MinSize(const int value)         { this.SetProperty(CANV_ELEMENT_PROP_SPLIT_CONTAINER_PANEL2_MIN_SIZE,value);           }
   int               Panel2MinSize(void)                 const { return (int)this.GetProperty(CANV_ELEMENT_PROP_SPLIT_CONTAINER_PANEL2_MIN_SIZE);     }
   
//--- (1) устанавливает, (2) возвращает флаг свёрнутости панели 1 и 2
   void              SetPanel1Collapsed(const int value)       { this.SetProperty(CANV_ELEMENT_PROP_SPLIT_CONTAINER_PANEL1_COLLAPSED,value);          }
   bool              Panel1Collapsed(void)               const { return (bool)this.GetProperty(CANV_ELEMENT_PROP_SPLIT_CONTAINER_PANEL1_COLLAPSED);   }
   void              SetPanel2Collapsed(const int value)       { this.SetProperty(CANV_ELEMENT_PROP_SPLIT_CONTAINER_PANEL2_COLLAPSED,value);          }
   bool              Panel2Collapsed(void)               const { return (bool)this.GetProperty(CANV_ELEMENT_PROP_SPLIT_CONTAINER_PANEL2_COLLAPSED);   }
   
//--- (1) устанавливает, (2) возвращает дистанцию разделителя от края
   void              SetSplitterDistance(const int value)      { this.SetProperty(CANV_ELEMENT_PROP_SPLIT_CONTAINER_SPLITTER_DISTANCE,value);         }
   int               SplitterDistance(void)              const { return (int)this.GetProperty(CANV_ELEMENT_PROP_SPLIT_CONTAINER_SPLITTER_DISTANCE);   }
   
//--- (1) устанавливает, (2) возвращает флаг неперемещаемости разделителя
   void              SetSplitterFixed(const bool flag)         { this.SetProperty(CANV_ELEMENT_PROP_SPLIT_CONTAINER_SPLITTER_FIXED,flag);             }
   bool              SplitterFixed(void)                 const { return (bool)this.GetProperty(CANV_ELEMENT_PROP_SPLIT_CONTAINER_SPLITTER_FIXED);     }
   
//--- (1) устанавливает, (2) возвращает толщину разделителя
   void              SetSplitterWidth(const int value)         { this.SetProperty(CANV_ELEMENT_PROP_SPLIT_CONTAINER_SPLITTER_WIDTH,value);            }
   int               SplitterWidth(void)                 const { return (int)this.GetProperty(CANV_ELEMENT_PROP_SPLIT_CONTAINER_SPLITTER_WIDTH);      }
   
//--- (1) устанавливает, (2) возвращает панель, не изменяющую свои размеры при изменении размеров контейнера
   void              SetFixedPanel(const ENUM_CANV_ELEMENT_SPLIT_CONTAINER_FIXED_PANEL value)
                       { this.SetProperty(CANV_ELEMENT_PROP_SPLIT_CONTAINER_FIXED_PANEL,value);                                                       }
   ENUM_CANV_ELEMENT_SPLIT_CONTAINER_FIXED_PANEL FixedPanel(void) const
                       { return(ENUM_CANV_ELEMENT_SPLIT_CONTAINER_FIXED_PANEL)this.GetProperty(CANV_ELEMENT_PROP_SPLIT_CONTAINER_FIXED_PANEL);        }
   
//--- Создаёт новый присоединённый элемент на указанной панели
   bool              CreateNewElement(const int panel_index,
                                      const ENUM_GRAPH_ELEMENT_TYPE element_type,
                                      const int x,
                                      const int y,
                                      const int w,
                                      const int h,
                                      const color colour,
                                      const uchar opacity,
                                      const bool activity,
                                      const bool redraw);
   
//--- Конструктор
                     CSplitContainer(const long chart_id,
                                     const int subwindow,
                                     const string descript,
                                     const int x,
                                     const int y,
                                     const int w,
                                     const int h);
  };
//+------------------------------------------------------------------+

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

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

Конструктор с указанием идентификатора чарта и подокна:

//+------------------------------------------------------------------+
//| Конструктор с указанием идентификатора чарта и подокна           |
//+------------------------------------------------------------------+
CSplitContainer::CSplitContainer(const long chart_id,
                                 const int subwindow,
                                 const string descript,
                                 const int x,
                                 const int y,
                                 const int w,
                                 const int h) : CContainer(GRAPH_ELEMENT_TYPE_WF_SPLIT_CONTAINER,chart_id,subwindow,descript,x,y,w,h)
  {
   this.SetTypeElement(GRAPH_ELEMENT_TYPE_WF_SPLIT_CONTAINER);
   this.m_type=OBJECT_DE_TYPE_GWF_CONTAINER;
   this.SetBorderSizeAll(0);
   this.SetBorderStyle(FRAME_STYLE_NONE);
   this.SetPaddingAll(0);
   this.SetMarginAll(3);
   this.SetOpacity(0,true);
   this.SetBackgroundColor(CLR_CANV_NULL,true);
   this.SetBackgroundColorMouseDown(CLR_CANV_NULL);
   this.SetBackgroundColorMouseOver(CLR_CANV_NULL);
   this.SetBorderColor(CLR_CANV_NULL,true);
   this.SetBorderColorMouseDown(CLR_CANV_NULL);
   this.SetBorderColorMouseOver(CLR_CANV_NULL);
   this.SetForeColor(CLR_DEF_FORE_COLOR,true);
   this.CreatePanels();
  }
//+------------------------------------------------------------------+

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


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

//+------------------------------------------------------------------+
//| Создаёт новый графический объект                                 |
//+------------------------------------------------------------------+
CGCnvElement *CSplitContainer::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_SPLIT_CONTAINER_PANEL : element=new CSplitContainerPanel(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. Других объектов (пока) мы внутри этого элемента управления создавать не будем. Когда в дальнейшем потребуется создание других вспомогательных объектов, то мы их создание добавим к этому методу.


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

//+------------------------------------------------------------------+
//| Создаёт новый присоединённый элемент на указанной панели         |
//+------------------------------------------------------------------+
bool CSplitContainer::CreateNewElement(const int panel_index,
                                       const ENUM_GRAPH_ELEMENT_TYPE element_type,
                                       const int x,
                                       const int y,
                                       const int w,
                                       const int h,
                                       const color colour,
                                       const uchar opacity,
                                       const bool activity,
                                       const bool redraw)
  {
   CSplitContainerPanel *panel=this.GetPanel(panel_index);
   if(panel==NULL)
     {
      ::Print(DFUN,CMessage::Text(MSG_ERR_FAILED_GET_SPLIT_CONTAINER_PANEL_OBJ)," (Panel",(string)panel_index,")");
      return false;
     }
   return panel.CreateNewElement(element_type,x,y,w,h,colour,opacity,activity,redraw);
  }
//+------------------------------------------------------------------+

Здесь: получаем объект-панель по указанному индексу.
Если панель получить не удалось — сообщаем об этом и возвращаем false
.
При успешном получении указателя на объект-панель — возвращаем результат вызова метода CreateNewElement()
полученного объекта для создания нового присоединённого к панели графического элемента.


Метод, создающий панели:

//+------------------------------------------------------------------+
//| Создаёт панели                                                   |
//+------------------------------------------------------------------+
void CSplitContainer::CreatePanels(void)
  {
   int x=0, y=0;
   int w=this.SplitterDistance();
   int h=this.Height();
   if(!this.CreateNewElement(GRAPH_ELEMENT_TYPE_WF_SPLIT_CONTAINER_PANEL,x,y,w,h,clrNONE,255,true,false))
      return;
   x=this.SplitterDistance()+this.SplitterWidth();
   w=this.Width()-x;
   if(!this.CreateNewElement(GRAPH_ELEMENT_TYPE_WF_SPLIT_CONTAINER_PANEL,x,y,w,h,clrNONE,255,true,false))
      return;
  }
//+------------------------------------------------------------------+

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

Следует отметить, что метод располагает панели по вертикальному разделителю. Горизонтальное размещение разделителя и панелей будем делать в следующей статье.


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

//+------------------------------------------------------------------+
//| Возвращает с указанной панели элемент по индексу                 |
//+------------------------------------------------------------------+
CGCnvElement *CSplitContainer::GetPanelElement(const int panel,const int index)
  {
   CSplitContainerPanel *obj=this.GetPanel(panel);
   if(obj==NULL)
     {
      CMessage::ToLog(DFUN,MSG_ERR_FAILED_GET_SPLIT_CONTAINER_PANEL_OBJ);
      return NULL;
     }
   return obj.GetElement(index);
  }
//+------------------------------------------------------------------+

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


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

//+------------------------------------------------------------------+
//| Возвращает с указанной панели элемент по типу и индексу          |
//+------------------------------------------------------------------+
CGCnvElement *CSplitContainer::GetPanelElementByType(const int panel,const ENUM_GRAPH_ELEMENT_TYPE type,const int index)
  {
   CSplitContainerPanel *obj=this.GetPanel(panel);
   if(obj==NULL)
     {
      CMessage::ToLog(DFUN,MSG_ERR_FAILED_GET_SPLIT_CONTAINER_PANEL_OBJ);
      return NULL;
     }
   return obj.GetElementByType(type,index);
  }
//+------------------------------------------------------------------+

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


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

//+------------------------------------------------------------------+
//| Возвращает с указанной панели элемент по имени                   |
//+------------------------------------------------------------------+
CGCnvElement *CSplitContainer::GetPanelElementByName(const int panel,const string name)
  {
   CSplitContainerPanel *obj=this.GetPanel(panel);
   if(obj==NULL)
     {
      CMessage::ToLog(DFUN,MSG_ERR_FAILED_GET_SPLIT_CONTAINER_PANEL_OBJ);
      return NULL;
     }
   return obj.GetElementByName(name);
  }
//+------------------------------------------------------------------+

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

На сегодня это весь функционал объекта элемента управления SplitContainer.


Подключим файл созданного класса к файлу \MQL5\Include\DoEasy\Objects\Graph\WForms\Containers\Panel.mqh класса объекта-панели:

//+------------------------------------------------------------------+
//|                                                        Panel.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 "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 "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"
//+------------------------------------------------------------------+
//| Класс объекта Panel элементов управления WForms                  |
//+------------------------------------------------------------------+
class CPanel : public CContainer
  {

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


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

//+------------------------------------------------------------------+
//| Создаёт новый графический объект                                 |
//+------------------------------------------------------------------+
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.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.ChartID(),this.SubWindow(),descript,x,y,w,h);              break;
      case GRAPH_ELEMENT_TYPE_WF_CONTAINER            : element=new CContainer(this.ChartID(),this.SubWindow(),descript,x,y,w,h);         break;
      case GRAPH_ELEMENT_TYPE_WF_GROUPBOX             : element=new CGroupBox(this.ChartID(),this.SubWindow(),descript,x,y,w,h);          break;
      case GRAPH_ELEMENT_TYPE_WF_PANEL                : element=new CPanel(this.ChartID(),this.SubWindow(),descript,x,y,w,h);             break;
      case GRAPH_ELEMENT_TYPE_WF_LABEL                : element=new CLabel(this.ChartID(),this.SubWindow(),descript,x,y,w,h);             break;
      case GRAPH_ELEMENT_TYPE_WF_CHECKBOX             : element=new CCheckBox(this.ChartID(),this.SubWindow(),descript,x,y,w,h);          break;
      case GRAPH_ELEMENT_TYPE_WF_RADIOBUTTON          : element=new CRadioButton(this.ChartID(),this.SubWindow(),descript,x,y,w,h);       break;
      case GRAPH_ELEMENT_TYPE_WF_BUTTON               : element=new CButton(this.ChartID(),this.SubWindow(),descript,x,y,w,h);            break;
      case GRAPH_ELEMENT_TYPE_WF_LIST_BOX             : element=new CListBox(this.ChartID(),this.SubWindow(),descript,x,y,w,h);           break;
      case GRAPH_ELEMENT_TYPE_WF_LIST_BOX_ITEM        : element=new CListBoxItem(this.ChartID(),this.SubWindow(),descript,x,y,w,h);       break;
      case GRAPH_ELEMENT_TYPE_WF_CHECKED_LIST_BOX     : element=new CCheckedListBox(this.ChartID(),this.SubWindow(),descript,x,y,w,h);    break;
      case GRAPH_ELEMENT_TYPE_WF_BUTTON_LIST_BOX      : element=new CButtonListBox(this.ChartID(),this.SubWindow(),descript,x,y,w,h);     break;
      case GRAPH_ELEMENT_TYPE_WF_TAB_HEADER           : element=new CTabHeader(this.ChartID(),this.SubWindow(),descript,x,y,w,h);         break;
      case GRAPH_ELEMENT_TYPE_WF_TAB_FIELD            : element=new CTabField(this.ChartID(),this.SubWindow(),descript,x,y,w,h);          break;
      case GRAPH_ELEMENT_TYPE_WF_TAB_CONTROL          : element=new CTabControl(this.ChartID(),this.SubWindow(),descript,x,y,w,h);        break;
      case GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON         : element=new CArrowButton(this.ChartID(),this.SubWindow(),descript,x,y,w,h);       break;
      case GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_UP      : element=new CArrowUpButton(this.ChartID(),this.SubWindow(),descript,x,y,w,h);     break;
      case GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_DOWN    : element=new CArrowDownButton(this.ChartID(),this.SubWindow(),descript,x,y,w,h);   break;
      case GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_LEFT    : element=new CArrowLeftButton(this.ChartID(),this.SubWindow(),descript,x,y,w,h);   break;
      case GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_RIGHT   : element=new CArrowRightButton(this.ChartID(),this.SubWindow(),descript,x,y,w,h);  break;
      case GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTONS_UD_BOX : element=new CArrowUpDownBox(this.ChartID(),this.SubWindow(),descript,x,y,w,h);    break;
      case GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTONS_LR_BOX : element=new CArrowLeftRightBox(this.ChartID(),this.SubWindow(),descript,x,y,w,h); break;
      case GRAPH_ELEMENT_TYPE_WF_SPLIT_CONTAINER      : element=new CSplitContainer(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\Containers\Container.mqh класса объекта-контейнера в метод, устанавливающий параметры присоединённому объекту, добавим блоки кода для установки параметров вновь созданных новых объектов библиотеки, и поменяем константу для определения диапазона объектов-контейнеров, так как их стало больше, и ранее указанная константа GRAPH_ELEMENT_TYPE_WF_GROUPBOX теперь стала не верной и не указывает на границу диапазона объектов-контейнеров в перечислении типов графических объектов:

//+------------------------------------------------------------------+
//| Устанавливает параметры присоединённому объекту                  |
//+------------------------------------------------------------------+
void CContainer::SetObjParams(CWinFormBase *obj,const color colour)
  {
   obj.SetMain(this.GetMain()==NULL ? this.GetObject() : this.GetMain());
   obj.SetBase(this.GetObject());
//--- Устанавливаем объекту цвет текста как у базового контейнера
   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-объекта"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;
      default:
        break;
     }
   obj.Crop();
  }
//+------------------------------------------------------------------+


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

//+------------------------------------------------------------------+
//| Создаёт новый графический объект                                 |
//+------------------------------------------------------------------+
CGCnvElement *CGroupBox::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.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.ChartID(),this.SubWindow(),descript,x,y,w,h);              break;
      case GRAPH_ELEMENT_TYPE_WF_CONTAINER            : element=new CContainer(this.ChartID(),this.SubWindow(),descript,x,y,w,h);         break;
      case GRAPH_ELEMENT_TYPE_WF_GROUPBOX             : element=new CGroupBox(this.ChartID(),this.SubWindow(),descript,x,y,w,h);          break;
      case GRAPH_ELEMENT_TYPE_WF_PANEL                : element=new CPanel(this.ChartID(),this.SubWindow(),descript,x,y,w,h);             break;
      case GRAPH_ELEMENT_TYPE_WF_LABEL                : element=new CLabel(this.ChartID(),this.SubWindow(),descript,x,y,w,h);             break;
      case GRAPH_ELEMENT_TYPE_WF_CHECKBOX             : element=new CCheckBox(this.ChartID(),this.SubWindow(),descript,x,y,w,h);          break;
      case GRAPH_ELEMENT_TYPE_WF_RADIOBUTTON          : element=new CRadioButton(this.ChartID(),this.SubWindow(),descript,x,y,w,h);       break;
      case GRAPH_ELEMENT_TYPE_WF_BUTTON               : element=new CButton(this.ChartID(),this.SubWindow(),descript,x,y,w,h);            break;
      case GRAPH_ELEMENT_TYPE_WF_LIST_BOX             : element=new CListBox(this.ChartID(),this.SubWindow(),descript,x,y,w,h);           break;
      case GRAPH_ELEMENT_TYPE_WF_LIST_BOX_ITEM        : element=new CListBoxItem(this.ChartID(),this.SubWindow(),descript,x,y,w,h);       break;
      case GRAPH_ELEMENT_TYPE_WF_CHECKED_LIST_BOX     : element=new CCheckedListBox(this.ChartID(),this.SubWindow(),descript,x,y,w,h);    break;
      case GRAPH_ELEMENT_TYPE_WF_BUTTON_LIST_BOX      : element=new CButtonListBox(this.ChartID(),this.SubWindow(),descript,x,y,w,h);     break;
      case GRAPH_ELEMENT_TYPE_WF_TAB_HEADER           : element=new CTabHeader(this.ChartID(),this.SubWindow(),descript,x,y,w,h);         break;
      case GRAPH_ELEMENT_TYPE_WF_TAB_FIELD            : element=new CTabField(this.ChartID(),this.SubWindow(),descript,x,y,w,h);          break;
      case GRAPH_ELEMENT_TYPE_WF_TAB_CONTROL          : element=new CTabControl(this.ChartID(),this.SubWindow(),descript,x,y,w,h);        break;
      case GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON         : element=new CArrowButton(this.ChartID(),this.SubWindow(),descript,x,y,w,h);       break;
      case GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_UP      : element=new CArrowUpButton(this.ChartID(),this.SubWindow(),descript,x,y,w,h);     break;
      case GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_DOWN    : element=new CArrowDownButton(this.ChartID(),this.SubWindow(),descript,x,y,w,h);   break;
      case GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_LEFT    : element=new CArrowLeftButton(this.ChartID(),this.SubWindow(),descript,x,y,w,h);   break;
      case GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_RIGHT   : element=new CArrowRightButton(this.ChartID(),this.SubWindow(),descript,x,y,w,h);  break;
      case GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTONS_UD_BOX : element=new CArrowUpDownBox(this.ChartID(),this.SubWindow(),descript,x,y,w,h);    break;
      case GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTONS_LR_BOX : element=new CArrowLeftRightBox(this.ChartID(),this.SubWindow(),descript,x,y,w,h); break;
      case GRAPH_ELEMENT_TYPE_WF_SPLIT_CONTAINER      : element=new CSplitContainer(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\Helpers\TabField.mqh класса объекта-поля вкладки элемента управления TabControl точно так же в методе, создающем новый графический объект, добавим строку для создания нового элемента управления:

//+------------------------------------------------------------------+
//| Создаёт новый графический объект                                 |
//+------------------------------------------------------------------+
CGCnvElement *CTabField::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.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.ChartID(),this.SubWindow(),descript,x,y,w,h);              break;
      case GRAPH_ELEMENT_TYPE_WF_CONTAINER            : element=new CContainer(this.ChartID(),this.SubWindow(),descript,x,y,w,h);         break;
      case GRAPH_ELEMENT_TYPE_WF_GROUPBOX             : element=new CGroupBox(this.ChartID(),this.SubWindow(),descript,x,y,w,h);          break;
      case GRAPH_ELEMENT_TYPE_WF_PANEL                : element=new CPanel(this.ChartID(),this.SubWindow(),descript,x,y,w,h);             break;
      case GRAPH_ELEMENT_TYPE_WF_LABEL                : element=new CLabel(this.ChartID(),this.SubWindow(),descript,x,y,w,h);             break;
      case GRAPH_ELEMENT_TYPE_WF_CHECKBOX             : element=new CCheckBox(this.ChartID(),this.SubWindow(),descript,x,y,w,h);          break;
      case GRAPH_ELEMENT_TYPE_WF_RADIOBUTTON          : element=new CRadioButton(this.ChartID(),this.SubWindow(),descript,x,y,w,h);       break;
      case GRAPH_ELEMENT_TYPE_WF_BUTTON               : element=new CButton(this.ChartID(),this.SubWindow(),descript,x,y,w,h);            break;
      case GRAPH_ELEMENT_TYPE_WF_LIST_BOX             : element=new CListBox(this.ChartID(),this.SubWindow(),descript,x,y,w,h);           break;
      case GRAPH_ELEMENT_TYPE_WF_LIST_BOX_ITEM        : element=new CListBoxItem(this.ChartID(),this.SubWindow(),descript,x,y,w,h);       break;
      case GRAPH_ELEMENT_TYPE_WF_CHECKED_LIST_BOX     : element=new CCheckedListBox(this.ChartID(),this.SubWindow(),descript,x,y,w,h);    break;
      case GRAPH_ELEMENT_TYPE_WF_BUTTON_LIST_BOX      : element=new CButtonListBox(this.ChartID(),this.SubWindow(),descript,x,y,w,h);     break;
      case GRAPH_ELEMENT_TYPE_WF_TAB_HEADER           : element=new CTabHeader(this.ChartID(),this.SubWindow(),descript,x,y,w,h);         break;
      case GRAPH_ELEMENT_TYPE_WF_TAB_FIELD            : element=new CTabField(this.ChartID(),this.SubWindow(),descript,x,y,w,h);          break;
      case GRAPH_ELEMENT_TYPE_WF_TAB_CONTROL          : element=new CTabControl(this.ChartID(),this.SubWindow(),descript,x,y,w,h);        break;
      case GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON         : element=new CArrowButton(this.ChartID(),this.SubWindow(),descript,x,y,w,h);       break;
      case GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_UP      : element=new CArrowUpButton(this.ChartID(),this.SubWindow(),descript,x,y,w,h);     break;
      case GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_DOWN    : element=new CArrowDownButton(this.ChartID(),this.SubWindow(),descript,x,y,w,h);   break;
      case GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_LEFT    : element=new CArrowLeftButton(this.ChartID(),this.SubWindow(),descript,x,y,w,h);   break;
      case GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_RIGHT   : element=new CArrowRightButton(this.ChartID(),this.SubWindow(),descript,x,y,w,h);  break;
      case GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTONS_UD_BOX : element=new CArrowUpDownBox(this.ChartID(),this.SubWindow(),descript,x,y,w,h);    break;
      case GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTONS_LR_BOX : element=new CArrowLeftRightBox(this.ChartID(),this.SubWindow(),descript,x,y,w,h); break;
      case GRAPH_ELEMENT_TYPE_WF_SPLIT_CONTAINER      : element=new CSplitContainer(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;
  }
//+------------------------------------------------------------------+

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

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

Это все необходимые на сегодня доработки библиотеки.


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

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

Советник в обработчике OnInit() создаёт панель, а на ней — элемент управления TabControl.

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

//+------------------------------------------------------------------+
//| 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<1;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();
         Print(DFUN,"Описание панели: ",pnl.Description(),", Тип и имя: ",pnl.TypeElementDescription()," ",pnl.Name());
         //--- Установим значение Padding равным 4
         pnl.SetPaddingAll(3);
         //--- Установим флаги перемещаемости, автоизменения размеров и режим автоизменения из входных параметров
         pnl.SetMovable(InpMovable);
         pnl.SetAutoSize(InpAutoSize,false);
         pnl.SetAutoSizeMode((ENUM_CANV_ELEMENT_AUTO_SIZE_MODE)InpAutoSizeMode,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"));
            //--- Создадим на каждой вкладке текстовую метку с описанием вкладки
            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.GetTabElement(j,0);
               if(label==NULL)
                  continue;
               //--- Если это самая первая вкладка, то текста не будет
               label.SetText(j==0 ? "" : "TabPage"+string(j+1));
              }
            //--- Создадим на первой вкладке элемент управления SplitContainer
            tc.CreateNewElement(0,GRAPH_ELEMENT_TYPE_WF_SPLIT_CONTAINER,10,10,tc.Width()-22,tc.GetTabField(0).Height()-22,clrNONE,255,true,false);
            CSplitContainer *split_container=tc.GetTabElementByType(0,GRAPH_ELEMENT_TYPE_WF_SPLIT_CONTAINER,0);
            if(split_container!=NULL)
              {
               //--- На каждой из панелей элемента ...
               for(int j=0;j<2;j++)
                 {
                  CSplitContainerPanel *panel=split_container.GetPanel(j);
                  if(panel==NULL)
                     continue;
                  //--- ... создадим текстовую метку с названием панели
                  if(split_container.CreateNewElement(j,GRAPH_ELEMENT_TYPE_WF_LABEL,0,0,panel.Width(),panel.Height(),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(TextByLanguage("Панель","Panel")+string(j+1));
                    }
                 }
              }
           }
        }
     }
//--- Отобразим и перерисуем все созданные панели
   for(int i=0;i<1;i++)
     {
      pnl=engine.GetWFPanelByName("Panel"+(string)i);
      if(pnl!=NULL)
        {
         pnl.Show();
         pnl.Redraw(true);
        }
     }
        
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+

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

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


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


Что дальше

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

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

К содержанию

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

 
DoEasy. Элементы управления (Часть 13): Оптимизация взаимодействия WinForms-объектов с мышкой, начало разработки WinForms-объекта TabControl
DoEasy. Элементы управления (Часть 14): Новый алгоритм именования графических элементов. Продолжаем работу над WinForms-объектом TabControl
DoEasy. Элементы управления (Часть 15): WinForms-объект TabControl — несколько рядов заголовков вкладок, методы работы с вкладками 
DoEasy. Элементы управления (Часть 16): WinForms-объект TabControl — несколько рядов заголовков вкладок, режим растягивания заголовков под размеры контейнера
DoEasy. Элементы управления (Часть 17): Отсечение невидимых участков объектов, вспомогательные WinForms-объекты кнопки со стрелками
DoEasy. Элементы управления (Часть 18): Готовим функционал для прокрутки вкладок в TabControl
DoEasy. Элементы управления (Часть 19): Прокрутка вкладок в элементе TabControl, события WinForms-объектов