DoEasy. Элементы управления (Часть 21): Элемент управления SplitContainer. Разделитель панелей
Содержание
- Концепция
- Доработка классов библиотеки
- Класс вспомогательного объекта-разделителя
- Тестирование
- Что дальше
Концепция
В прошлой статье мы начали работу по созданию элемента управления SplitContainer. На данный момент библиотека может создать такой элемент в виде статического объекта с двумя панелями со значениями параметров по умолчанию. Объект имеет две панели, разделённые разделителем. В оригинальном объекте в MS Visual Studio мы можем перемещать разделитель, тем самым в итоге изменяя размеры панелей.
При наведении курсора мышки на область разделителя появляется характерный курсор, указывающий на возможность смещения разделителя ( ), а при его захвате мышкой, разделитель закрашивается штрихованной областью, которую можно перемещать, тем самым указывая новое расположение разделителя. При отпускании кнопки мышки панели изменяют свои размеры в соответствии с новым положением разделителя.
Так как в MQL5 не предусмотрена возможность смены внешнего вида курсора, то пока не будем делать никаких "сигнализаторов" о том, что "тут возможно схватить и перетащить", а просто сразу будем накладывать штрихованную область на область разделителя, тем самым и указывая на возможность его перемещения. В объекте SplitContainer MS Visual Studio порядок такой:
- При наведении курсора на область разделителя появляется курсор, указывающий на возможность перемещения;
- При нажатии и удержании клавиши мышки (но не смещении курсора) появляется пунктирный прямоугольник, очерчивающий область разделителя;
- При смещении курсора мышки появляется заштрихованная область, размером с разделитель и движущаяся вслед за курсором, указывая на новое положение разделителя, которое будет при отпускании кнопки мышки;
- При отпускании кнопки мышки размеры панелей перестраиваются под новое положение разделителя.
У нас же будет более простая схема:
- При наведении курсора на область разделителя появляется штрихованная область;
- При захвате мышкой штрихованной области и её перемещении, сразу меняются размеры панелей в соответствии с новым положением разделителя;
- При отпускании кнопки мышки и уводе курсора с области разделителя, штрихованная область скрывается, а панели остаются с новыми размерами.
Объект-разделитель будем строить как производный объект от базового объекта всех WinForms-объектов библиотеки — от класса CWinFormBase, в котором будут переопределены виртуальные методы для очистки и перерисовки объекта — в них будет рисоваться штрихованное поле, заполняющее всю область объекта. Управлять видимостью этого объекта будем из обработчика событий элемента управления SplitContainer. При наведении мышки на область управления объект будет отображаться, а при уводе курсора с этой области — скрываться. В такой области могут находиться как сам разделитель в данном объекте, так и управляющие кнопки в других объектах, такие, как сворачивание/разворачивание/закрытие окна в будущих объектах библиотеки, и т.п.
Доработка классов библиотеки
Для указания координат и размеров области управления добавим новые целочисленные свойства объекта-графического элемента и новые идентификаторы событий и состояний мышки.
В файле \MQL5\Include\DoEasy\Defines.mqh добавим в список возможных состояний мышки относительно формы новые идентификаторы:
//+------------------------------------------------------------------+ //| Список возможных состояний мышки относительно формы | //+------------------------------------------------------------------+ enum ENUM_MOUSE_FORM_STATE { MOUSE_FORM_STATE_NONE = 0, // Неопределённое состояние //--- За пределами формы MOUSE_FORM_STATE_OUTSIDE_FORM_NOT_PRESSED, // Курсор за пределами формы, кнопки мышки не нажаты MOUSE_FORM_STATE_OUTSIDE_FORM_PRESSED, // Курсор за пределами формы, нажата кнопка мышки (любая) MOUSE_FORM_STATE_OUTSIDE_FORM_WHEEL, // Курсор за пределами формы, прокручивается колёсико мышки //--- В пределах формы MOUSE_FORM_STATE_INSIDE_FORM_NOT_PRESSED, // Курсор в пределах формы, кнопки мышки не нажаты MOUSE_FORM_STATE_INSIDE_FORM_PRESSED, // Курсор в пределах формы, нажата кнопка мышки (любая) MOUSE_FORM_STATE_INSIDE_FORM_WHEEL, // Курсор в пределах формы, прокручивается колёсико мышки //--- В пределах области заголовка окна MOUSE_FORM_STATE_INSIDE_ACTIVE_AREA_NOT_PRESSED, // Курсор в пределах активной области, кнопки мышки не нажаты MOUSE_FORM_STATE_INSIDE_ACTIVE_AREA_PRESSED, // Курсор в пределах активной области, нажата кнопка мышки (любая) MOUSE_FORM_STATE_INSIDE_ACTIVE_AREA_WHEEL, // Курсор в пределах активной области, прокручивается колёсико мышки MOUSE_FORM_STATE_INSIDE_ACTIVE_AREA_RELEASED, // Курсор в пределах активной области, отжата кнопка мышки (левая) //--- В пределах области прокрутки окна MOUSE_FORM_STATE_INSIDE_SCROLL_AREA_NOT_PRESSED, // Курсор в пределах области прокрутки окна, кнопки мышки не нажаты MOUSE_FORM_STATE_INSIDE_SCROLL_AREA_PRESSED, // Курсор в пределах области прокрутки окна, нажата кнопка мышки (любая) MOUSE_FORM_STATE_INSIDE_SCROLL_AREA_WHEEL, // Курсор в пределах области прокрутки окна, прокручивается колёсико мышки //--- В пределах области изменения размеров окна MOUSE_FORM_STATE_INSIDE_RESIZE_AREA_NOT_PRESSED, // Курсор в пределах области изменения размеров окна, кнопки мышки не нажаты MOUSE_FORM_STATE_INSIDE_RESIZE_AREA_PRESSED, // Курсор в пределах области изменения размеров окна, нажата кнопка мышки (любая) MOUSE_FORM_STATE_INSIDE_RESIZE_AREA_WHEEL, // Курсор в пределах области изменения размеров окна, прокручивается колёсико мышки //--- В пределах области резделителя окна MOUSE_FORM_STATE_INSIDE_SPLITTER_AREA_NOT_PRESSED, // Курсор в пределах области резделителя окна, кнопки мышки не нажаты MOUSE_FORM_STATE_INSIDE_SPLITTER_AREA_PRESSED, // Курсор в пределах области резделителя окна, нажата кнопка мышки (любая) MOUSE_FORM_STATE_INSIDE_SPLITTER_AREA_WHEEL, // Курсор в пределах области резделителя окна, прокручивается колёсико мышки }; //+------------------------------------------------------------------+
Область изменения размеров окна — это та область сверху, снизу, слева и справа, при наведении курсора на которую появляется возможность изменения размеров графического элемента, для которого такая возможность конструктивно предусмотрена. Сюда мы вписали эти идентификаторы с прицелом на будущее — нам всё равно необходимо будет делать изменение размеров окон мышкой, и почему бы не вписать эти идентификаторы уже сейчас.
В список возможных событий мышки впишем новые идентификаторы событий, соответствующие новым состояниям мышки относительно формы:
//+------------------------------------------------------------------+ //| Список возможных событий мышки | //+------------------------------------------------------------------+ enum ENUM_MOUSE_EVENT { MOUSE_EVENT_NO_EVENT = CHART_OBJ_EVENTS_NEXT_CODE, // Нет события //--- MOUSE_EVENT_OUTSIDE_FORM_NOT_PRESSED, // Курсор за пределами формы, кнопки мышки не нажаты MOUSE_EVENT_OUTSIDE_FORM_PRESSED, // Курсор за пределами формы, нажата кнопка мышки (любая) MOUSE_EVENT_OUTSIDE_FORM_WHEEL, // Курсор за пределами формы, прокручивается колёсико мышки //--- В пределах формы MOUSE_EVENT_INSIDE_FORM_NOT_PRESSED, // Курсор в пределах формы, кнопки мышки не нажаты MOUSE_EVENT_INSIDE_FORM_PRESSED, // Курсор в пределах формы, нажата кнопка мышки (любая) MOUSE_EVENT_INSIDE_FORM_WHEEL, // Курсор в пределах формы, прокручивается колёсико мышки //--- В пределах области заголовка окна MOUSE_EVENT_INSIDE_ACTIVE_AREA_NOT_PRESSED, // Курсор в пределах активной области, кнопки мышки не нажаты MOUSE_EVENT_INSIDE_ACTIVE_AREA_PRESSED, // Курсор в пределах активной области, нажата кнопка мышки (любая) MOUSE_EVENT_INSIDE_ACTIVE_AREA_WHEEL, // Курсор в пределах активной области, прокручивается колёсико мышки MOUSE_EVENT_INSIDE_ACTIVE_AREA_RELEASED, // Курсор в пределах активной области, отжата кнопка мышки (левая) //--- В пределах области прокрутки окна MOUSE_EVENT_INSIDE_SCROLL_AREA_NOT_PRESSED, // Курсор в пределах области прокрутки окна, кнопки мышки не нажаты MOUSE_EVENT_INSIDE_SCROLL_AREA_PRESSED, // Курсор в пределах области прокрутки окна, нажата кнопка мышки (любая) MOUSE_EVENT_INSIDE_SCROLL_AREA_WHEEL, // Курсор в пределах области прокрутки окна, прокручивается колёсико мышки //--- В пределах области изменения размеров окна MOUSE_EVENT_INSIDE_RESIZE_AREA_NOT_PRESSED, // Курсор в пределах области изменения размеров окна, кнопки мышки не нажаты MOUSE_EVENT_INSIDE_RESIZE_AREA_PRESSED, // Курсор в пределах области изменения размеров окна, нажата кнопка мышки (любая) MOUSE_EVENT_INSIDE_RESIZE_AREA_WHEEL, // Курсор в пределах области изменения размеров окна, прокручивается колёсико мышки //--- В пределах области резделителя окна MOUSE_EVENT_INSIDE_SPLITTER_AREA_NOT_PRESSED, // Курсор в пределах области резделителя окна, кнопки мышки не нажаты MOUSE_EVENT_INSIDE_SPLITTER_AREA_PRESSED, // Курсор в пределах области резделителя окна, нажата кнопка мышки (любая) MOUSE_EVENT_INSIDE_SPLITTER_AREA_WHEEL, // Курсор в пределах области резделителя окна, прокручивается колёсико мышки }; #define MOUSE_EVENT_NEXT_CODE (MOUSE_EVENT_INSIDE_SPLITTER_AREA_WHEEL+1) // Код следующего события после последнего кода события мышки //+------------------------------------------------------------------+
Так как здесь появились новые константы перечисления, то в макроподстановку MOUSE_EVENT_NEXT_CODE необходимо вписать самую последнюю константу перечисления MOUSE_EVENT_INSIDE_SPLITTER_AREA_WHEEL вместо прежней MOUSE_EVENT_INSIDE_SCROLL_AREA_WHEEL.
В список типов графических элементов добавим новый тип вспомогательного объекта, который сегодня будем делать:
//+------------------------------------------------------------------+ //| Список типов графических элементов | //+------------------------------------------------------------------+ 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 GRAPH_ELEMENT_TYPE_WF_SPLITTER, // Windows Forms Splitter }; //+------------------------------------------------------------------+
В список возможных событий элементов управления WinForms добавим новое событие — перемещение разделителя:
//+------------------------------------------------------------------+ //| Список возможных событий элементов управления WinForms | //+------------------------------------------------------------------+ enum ENUM_WF_CONTROL_EVENT { WF_CONTROL_EVENT_NO_EVENT = GRAPH_OBJ_EVENTS_NEXT_CODE,// Нет события WF_CONTROL_EVENT_CLICK, // Событие "Щелчок по элементу управления" WF_CONTROL_EVENT_CLICK_CANCEL, // Событие "Отмена щелчка по элементу управления" WF_CONTROL_EVENT_TAB_SELECT, // Событие "Выбор вкладки элемента управления TabControl" WF_CONTROL_EVENT_CLICK_SCROLL_LEFT, // Событие "Щелчок по кнопке влево элемента управления" WF_CONTROL_EVENT_CLICK_SCROLL_RIGHT, // Событие "Щелчок по кнопке влево элемента управления" WF_CONTROL_EVENT_CLICK_SCROLL_UP, // Событие "Щелчок по кнопке влево элемента управления" WF_CONTROL_EVENT_CLICK_SCROLL_DOWN, // Событие "Щелчок по кнопке влево элемента управления" WF_CONTROL_EVENT_SPLITTER_MOVE, // Событие "Перемещение разделителя элемента управления" }; #define WF_CONTROL_EVENTS_NEXT_CODE (WF_CONTROL_EVENT_SPLITTER_MOVE+1) // Код следующего события после последнего кода события графических элементов //+------------------------------------------------------------------+
Здесь точно так же, как и выше — в макроподстановку WF_CONTROL_EVENTS_NEXT_CODE необходимо вписать самую последнюю константу перечисления WF_CONTROL_EVENT_SPLITTER_MOVE, так как это теперь самая последняя константа этого перечисления.
Для возможности указания каким образом должен в объекте располагаться разделитель, напишем перечисление:
//+------------------------------------------------------------------+ //| Расположение разделителя в Split Container | //+------------------------------------------------------------------+ enum ENUM_CANV_ELEMENT_SPLITTER_ORIENTATION { CANV_ELEMENT_SPLITTER_ORIENTATION_VERTICAL, // Вертикальный CANV_ELEMENT_SPLITTER_ORIENTATION_HORISONTAL, // Горизонтальный }; //+------------------------------------------------------------------+ //| Целочисленные свойства графического элемента на канвасе | //+------------------------------------------------------------------+
В список целочисленных свойств графического элемента на канвасе добавим новые свойства и увеличим их общее количество до 122:
//+------------------------------------------------------------------+ //| Целочисленные свойства графического элемента на канвасе | //+------------------------------------------------------------------+ enum ENUM_CANV_ELEMENT_PROP_INTEGER { CANV_ELEMENT_PROP_ID = 0, // Идентификатор элемента CANV_ELEMENT_PROP_TYPE, // Тип графического элемента //---... //---... CANV_ELEMENT_PROP_VISIBLE_AREA_WIDTH, // Ширина области видимости CANV_ELEMENT_PROP_VISIBLE_AREA_HEIGHT, // Высота области видимости CANV_ELEMENT_PROP_CONTROL_AREA_X, // X-координата области управления CANV_ELEMENT_PROP_CONTROL_AREA_Y, // Y-координата области управления CANV_ELEMENT_PROP_CONTROL_AREA_WIDTH, // Ширина области управления CANV_ELEMENT_PROP_CONTROL_AREA_HEIGHT, // Высота области управления CANV_ELEMENT_PROP_SCROLL_AREA_X_RIGHT, // X-координата области прокрутки справа CANV_ELEMENT_PROP_SCROLL_AREA_Y_RIGHT, // Y-координата области прокрутки справа CANV_ELEMENT_PROP_SCROLL_AREA_WIDTH_RIGHT, // Ширина области прокрутки справа CANV_ELEMENT_PROP_SCROLL_AREA_HEIGHT_RIGHT, // Высота области прокрутки справа CANV_ELEMENT_PROP_SCROLL_AREA_X_BOTTOM, // X-координата области прокрутки снизу CANV_ELEMENT_PROP_SCROLL_AREA_Y_BOTTOM, // Y-координата области прокрутки снизу CANV_ELEMENT_PROP_SCROLL_AREA_WIDTH_BOTTOM, // Ширина области прокрутки снизу CANV_ELEMENT_PROP_SCROLL_AREA_HEIGHT_BOTTOM, // Высота области прокрутки снизу CANV_ELEMENT_PROP_BORDER_LEFT_AREA_WIDTH, // Ширина области левой грани CANV_ELEMENT_PROP_BORDER_BOTTOM_AREA_WIDTH, // Ширина области нижней грани CANV_ELEMENT_PROP_BORDER_RIGHT_AREA_WIDTH, // Ширина области правой грани CANV_ELEMENT_PROP_BORDER_TOP_AREA_WIDTH, // Ширина области верхней грани CANV_ELEMENT_PROP_DISPLAYED, // Флаг отображения не скрытого элемента управления CANV_ELEMENT_PROP_GROUP, // Группа, к которой принадлежит графический элемент //---... //---... CANV_ELEMENT_PROP_SPLIT_CONTAINER_SPLITTER_DISTANCE,// Расстояние от края до разделителя CANV_ELEMENT_PROP_SPLIT_CONTAINER_SPLITTER_WIDTH, // Толщина разделителя CANV_ELEMENT_PROP_SPLIT_CONTAINER_SPLITTER_ORIENTATION,// Расположение разделителя 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 (122) // Общее количество целочисленных свойств #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_VISIBLE_AREA_WIDTH, // Сортировать по ширине области видимости SORT_BY_CANV_ELEMENT_VISIBLE_AREA_HEIGHT, // Сортировать по высоте области видимости SORT_BY_CANV_ELEMENT_CONTROL_AREA_X, // Сортировать по X-координате области управления SORT_BY_CANV_ELEMENT_CONTROL_AREA_Y, // Сортировать по Y-координате области управления SORT_BY_CANV_ELEMENT_CONTROL_AREA_WIDTH, // Сортировать по ширине области управления SORT_BY_CANV_ELEMENT_CONTROL_AREA_HEIGHT, // Сортировать по высоте области управления SORT_BY_CANV_ELEMENT_SCROLL_AREA_X_RIGHT, // Сортировать по X-координате области прокрутки справа SORT_BY_CANV_ELEMENT_SCROLL_AREA_Y_RIGHT, // Сортировать по Y-координате области прокрутки справа SORT_BY_CANV_ELEMENT_SCROLL_AREA_WIDTH_RIGHT, // Сортировать по ширине области прокрутки справа SORT_BY_CANV_ELEMENT_SCROLL_AREA_HEIGHT_RIGHT, // Сортировать по высоте области прокрутки справа SORT_BY_CANV_ELEMENT_SCROLL_AREA_X_BOTTOM, // Сортировать по X-координате области прокрутки снизу SORT_BY_CANV_ELEMENT_SCROLL_AREA_Y_BOTTOM, // Сортировать по Y-координате области прокрутки снизу SORT_BY_CANV_ELEMENT_SCROLL_AREA_WIDTH_BOTTOM, // Сортировать по ширине области прокрутки снизу SORT_BY_CANV_ELEMENT_SCROLL_AREA_HEIGHT_BOTTOM, // Сортировать по высоте области прокрутки снизу SORT_BY_CANV_ELEMENT_BORDER_LEFT_AREA_WIDTH, // Сортировать по ширине области левой грани SORT_BY_CANV_ELEMENT_BORDER_BOTTOM_AREA_WIDTH, // Сортировать по ширине области нижней грани SORT_BY_CANV_ELEMENT_BORDER_RIGHT_AREA_WIDTH, // Сортировать по ширине области правой грани SORT_BY_CANV_ELEMENT_BORDER_TOP_AREA_WIDTH, // Сортировать по ширине области верхней грани SORT_BY_CANV_ELEMENT_DISPLAYED, // Сортировать по флагу отображения не скрытого элемента управления SORT_BY_CANV_ELEMENT_GROUP, // Сортировать по группе, к которой принадлежит графический элемент //---... //---... SORT_BY_CANV_ELEMENT_SPLIT_CONTAINER_SPLITTER_DISTANCE,// Сортировать по расстоянию от края до разделителя SORT_BY_CANV_ELEMENT_SPLIT_CONTAINER_SPLITTER_WIDTH, // Сортировать по толщине разделителя SORT_BY_CANV_ELEMENT_SPLIT_CONTAINER_SPLITTER_ORIENTATION,// Сортировать по расположению разделителя 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_LIB_TEXT_TOP, // Сверху MSG_LIB_TEXT_BOTTOM, // Снизу MSG_LIB_TEXT_LEFT, // Слева MSG_LIB_TEXT_RIGHT, // Справа MSG_LIB_TEXT_VERTICAL, // Вертикально MSG_LIB_TEXT_HORISONTAL, // Горизонтально
...
MSG_GRAPH_ELEMENT_TYPE_WF_SPLIT_CONTAINER_PANEL, // Панель элемента управления SplitContainer MSG_GRAPH_ELEMENT_TYPE_WF_SPLIT_CONTAINER, // Элемент управления SplitContainer MSG_GRAPH_ELEMENT_TYPE_WF_SPLITTER, // Элемент управления Splitter MSG_GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON, // Элемент управления ArrowButton MSG_GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_UP, // Элемент управления UpArrowButton
...
MSG_CANV_ELEMENT_PROP_VISIBLE_AREA_WIDTH, // Ширина области видимости MSG_CANV_ELEMENT_PROP_VISIBLE_AREA_HEIGHT, // Высота области видимости MSG_CANV_ELEMENT_PROP_CONTROL_AREA_X, // X-координата области управления MSG_CANV_ELEMENT_PROP_CONTROL_AREA_Y, // Y-координата области управления MSG_CANV_ELEMENT_PROP_CONTROL_AREA_WIDTH, // Ширина области управления MSG_CANV_ELEMENT_PROP_CONTROL_AREA_HEIGHT, // Высота области управления MSG_CANV_ELEMENT_PROP_SCROLL_AREA_X_RIGHT, // X-координата области прокрутки справа MSG_CANV_ELEMENT_PROP_SCROLL_AREA_Y_RIGHT, // Y-координата области прокрутки справа MSG_CANV_ELEMENT_PROP_SCROLL_AREA_WIDTH_RIGHT, // Ширина области прокрутки справа MSG_CANV_ELEMENT_PROP_SCROLL_AREA_HEIGHT_RIGHT, // Высота области прокрутки справа MSG_CANV_ELEMENT_PROP_SCROLL_AREA_X_BOTTOM, // X-координата области прокрутки снизу MSG_CANV_ELEMENT_PROP_SCROLL_AREA_Y_BOTTOM, // Y-координата области прокрутки снизу MSG_CANV_ELEMENT_PROP_SCROLL_AREA_WIDTH_BOTTOM, // Ширина области прокрутки снизу MSG_CANV_ELEMENT_PROP_SCROLL_AREA_HEIGHT_BOTTOM, // Высота области прокрутки снизу MSG_CANV_ELEMENT_PROP_BORDER_LEFT_AREA_WIDTH, // Ширина области левой грани MSG_CANV_ELEMENT_PROP_BORDER_BOTTOM_AREA_WIDTH, // Ширина области нижней грани MSG_CANV_ELEMENT_PROP_BORDER_RIGHT_AREA_WIDTH, // Ширина области правой грани MSG_CANV_ELEMENT_PROP_BORDER_TOP_AREA_WIDTH, // Ширина области верхней грани MSG_CANV_ELEMENT_PROP_DISPLAYED, // Флаг отображения не скрытого элемента управления MSG_CANV_ELEMENT_PROP_ENABLED, // Флаг доступности элемента
...
MSG_CANV_ELEMENT_PROP_SPLIT_CONTAINER_SPLITTER_DISTANCE, // Расстояние от края до разделителя MSG_CANV_ELEMENT_PROP_SPLIT_CONTAINER_SPLITTER_WIDTH, // Толщина разделителя MSG_CANV_ELEMENT_PROP_SPLIT_CONTAINER_SPLITTER_ORIENTATION, // Расположение разделителя MSG_CANV_ELEMENT_PROP_SPLIT_CONTAINER_PANEL1_COLLAPSED, // Флаг свёрнутости панели 1 MSG_CANV_ELEMENT_PROP_SPLIT_CONTAINER_PANEL1_MIN_SIZE, // Минимальный размер панели 1
и текстовые сообщения, соответствующие вновь добавленным индексам:
{"Сверху","Top"}, {"Снизу","Bottom"}, {"Слева","Left"}, {"Справа","Right"}, {"Вертикально","Vertical"}, {"Горизонтально","Horisontal"},
...
{"Панель элемента управления \"SplitContainer\"","Panel of the Control element \"SplitContainer\""}, {"Элемент управления \"SplitContainer\"","Control element \"SplitContainer\""}, {"Элемент управления \"Splitter\"","Control element \"Splitter\""}, {"Элемент управления \"ArrowButton\"","Control element \"ArrowButton\""}, {"Элемент управления \"UpArrowButton\"","Control element \"UpArrowButton\""},
...
{"Ширина области видимости","Width of object visibility area"}, {"Высота области видимости","Height of object visibility area"}, {"X-координата области управления","X-coordinate of the control area"}, {"Y-координата области управления","Y-coordinate of the control area"}, {"Ширина области управления","Control area width"}, {"Высота области управления","Control area height"}, {"X-координата области прокрутки справа","X-coordinate of the right scroll area"}, {"Y-координата области прокрутки справа","Y-coordinate of the right scroll area"}, {"Ширина области прокрутки справа","Width of the right scroll area"}, {"Высота области прокрутки справа","Height of the right scroll area"}, {"X-координата области прокрутки снизу","X-coordinate of the bottom scroll area"}, {"Y-координата области прокрутки снизу","Y-coordinate of the bottom scroll area"}, {"Ширина области прокрутки снизу","Width of the bottom scroll area"}, {"Высота области прокрутки снизу","Height of the bottom scroll area"}, {"Ширина области левой грани","Width of the left border area"}, {"Ширина области нижней грани","Width of the bottom border area"}, {"Ширина области правой грани","Width of the right border area"}, {"Ширина области верхней грани","Width of the top border area"}, {"Флаг отображения не скрытого элемента управления","Flag that sets the display of a non-hidden control"}, {"Флаг доступности элемента","Element Availability Flag"},
...
{"Расстояние от края до разделителя","Distance from edge to splitter"}, {"Толщина разделителя","Splitter Width"}, {"Расположение разделителя","Splitter orientation"}, {"Флаг свёрнутости панели 1","Flag to indicate that panel 1 is collapsed"}, {"Минимальный размер панели 1","Min size of Panel 1"},
Для возможности получения описания типа нового объекта, в файле \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) : type==GRAPH_ELEMENT_TYPE_WF_SPLITTER ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_SPLITTER) : "Unknown" ); } //+------------------------------------------------------------------+
Метод возвращает соответствующее текстовое сообщение в зависимости от переданного в него типа графического элемента.
Так как у нас появились новые свойства графического элемента, нам необходимо добавить их в структуру объекта для его правильного сохранения в файл и чтения из файла.
В файле \MQL5\Include\DoEasy\Objects\Graph\GCnvElement.mqh впишем в структуру новые свойства:
private: int m_shift_coord_x; // Смещение координаты X относительно базового объекта int m_shift_coord_y; // Смещение координаты Y относительно базового объекта struct SData { //--- Целочисленные свойства объекта int id; // Идентификатор элемента int type; // Тип графического элемента //---... //---... bool displayed; // Флаг отображения не скрытого элемента управления int split_container_fixed_panel; // Панель, сохраняющая свои размеры при изменении размера контейнера bool split_container_splitter_fixed; // Флаг перемещаемости разделителя int split_container_splitter_distance; // Расстояние от края до разделителя int split_container_splitter_width; // Толщина разделителя int split_container_splitter_orientation; // Расположение разделителя 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 int control_area_x; // X-координата области управления int control_area_y; // Y-координата области управления int control_area_width; // Ширина области управления int control_area_height; // Высота области управления int scroll_area_x_right; // X-координата области прокрутки справа int scroll_area_y_right; // Y-координата области прокрутки справа int scroll_area_width_right; // Ширина области прокрутки справа int scroll_area_height_right; // Высота области прокрутки справа int scroll_area_x_bottom; // X-координата области прокрутки снизу int scroll_area_y_bottom; // Y-координата области прокрутки снизу int scroll_area_width_bottom; // Ширина области прокрутки снизу int scroll_area_height_bottom; // Высота области прокрутки снизу int border_left_area_width; // Ширина области левой грани int border_bottom_area_width; // Ширина области нижней грани int border_right_area_width; // Ширина области правой грани int border_top_area_width; // Ширина области верхней грани //--- Вещественные свойства объекта //--- Строковые свойства объекта uchar name_obj[64]; // Имя объекта-графического элемента uchar name_res[64]; // Имя графического ресурса uchar text[256]; // Текст графического элемента uchar descript[256]; // Описание графического элемента }; SData m_struct_obj; // Структура объекта
В публичной секции класса объявим метод, возвращающий положение курсора относительно области управления элемента:
//--- (1) Сохраняет графический ресурс в массив, (2) восстанавливает ресурс из массива bool ResourceStamp(const string source); virtual bool Reset(void); //--- Возвращает положение курсора относительно (1) всего элемента, (2) активной зоны элемента, (3) зоны управления bool CursorInsideElement(const int x,const int y); bool CursorInsideActiveArea(const int x,const int y); bool CursorInsideControlArea(const int x,const int y); //--- Создаёт элемент bool Create(const long chart_id, const int wnd_num, const int x, const int y, const int w, const int h, const bool redraw=false);
Метод будет возвращать флаг нахождения курсора внутри его области управления, в которой могут быть различные управляющие элементы, в данном случае — разделитель.
Для метода, возвращающего флаг скрытости элемента, добавим пропущенный ранее модификатор константности:
//--- (1) Устанавливает, (2) возвращает флаг отображения не скрытого элемента управления void SetDisplayed(const bool flag) { this.SetProperty(CANV_ELEMENT_PROP_DISPLAYED,flag); } bool Displayed(void) const { return (bool)this.GetProperty(CANV_ELEMENT_PROP_DISPLAYED); } //--- (1) Устанавливает, (2) возвращает тип графического элемента
В публичной секции добавим методы, возвращающие новые свойства объекта, которые сегодня в него были добавлены:
//--- Возвращает координату (1) левого, (2) правого, (3) верхнего, (4) нижнего края активной зоны элемента int ActiveAreaLeft(void) const { return int(this.CoordX()+this.ActiveAreaLeftShift()); } int ActiveAreaRight(void) const { return int(this.RightEdge()-this.ActiveAreaRightShift()); } int ActiveAreaTop(void) const { return int(this.CoordY()+this.ActiveAreaTopShift()); } int ActiveAreaBottom(void) const { return int(this.BottomEdge()-this.ActiveAreaBottomShift()); } //--- Возвращает (1) координату X, (2) координату Y, (3) ширину, (4) высоту зоны управления элемента int ControlAreaX(void) const { return (int)this.GetProperty(CANV_ELEMENT_PROP_CONTROL_AREA_X); } int ControlAreaY(void) const { return (int)this.GetProperty(CANV_ELEMENT_PROP_CONTROL_AREA_Y); } int ControlAreaWidth(void) const { return (int)this.GetProperty(CANV_ELEMENT_PROP_CONTROL_AREA_WIDTH); } int ControlAreaHeight(void) const { return (int)this.GetProperty(CANV_ELEMENT_PROP_CONTROL_AREA_HEIGHT); } //--- Возвращает (1) координату X, (2) координату Y, (3) ширину, (4) высоту области прокрутки справа элемента int ScrollAreaXRight(void) const { return (int)this.GetProperty(CANV_ELEMENT_PROP_SCROLL_AREA_X_RIGHT); } int ScrollAreaYRight(void) const { return (int)this.GetProperty(CANV_ELEMENT_PROP_SCROLL_AREA_Y_RIGHT); } int ScrollAreaWidthRight(void) const { return (int)this.GetProperty(CANV_ELEMENT_PROP_SCROLL_AREA_WIDTH_RIGHT); } int ScrollAreaHeightRight(void) const { return (int)this.GetProperty(CANV_ELEMENT_PROP_SCROLL_AREA_HEIGHT_RIGHT); } //--- Возвращает (1) координату X, (2) координату Y, (3) ширину, (4) высоту области прокрутки снизу элемента int ScrollAreaXBottom(void) const { return (int)this.GetProperty(CANV_ELEMENT_PROP_SCROLL_AREA_X_BOTTOM); } int ScrollAreaYBottom(void) const { return (int)this.GetProperty(CANV_ELEMENT_PROP_SCROLL_AREA_Y_BOTTOM); } int ScrollAreaWidthBottom(void) const { return (int)this.GetProperty(CANV_ELEMENT_PROP_SCROLL_AREA_WIDTH_BOTTOM); } int ScrollAreaHeightBottom(void) const { return (int)this.GetProperty(CANV_ELEMENT_PROP_SCROLL_AREA_HEIGHT_BOTTOM); } //--- Возвращает ширину (1) левой, (2) правой, (3) верхней, (4) нижней области грани элемента int BorderResizeAreaLeft(void) const { return (int)this.GetProperty(CANV_ELEMENT_PROP_BORDER_LEFT_AREA_WIDTH); } int BorderResizeAreaRight(void) const { return (int)this.GetProperty(CANV_ELEMENT_PROP_BORDER_RIGHT_AREA_WIDTH); } int BorderResizeAreaTop(void) const { return (int)this.GetProperty(CANV_ELEMENT_PROP_BORDER_TOP_AREA_WIDTH); } int BorderResizeAreaBottom(void) const { return (int)this.GetProperty(CANV_ELEMENT_PROP_BORDER_BOTTOM_AREA_WIDTH); } //--- Возвращает количество цветов, установленных для градиентной заливки (1) основного фона, при нажатии (2), (3) наведении мышки на элемент управления
В конструкторах класса во все новые свойства объекта впишем значения по умолчанию:
//+------------------------------------------------------------------+ //| Параметрический конструктор | //+------------------------------------------------------------------+ 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_WND_NUM,CGBaseObj::SubWindow()); // Номер подокна графика //---... //---... this.SetProperty(CANV_ELEMENT_PROP_NAME_OBJ,CGBaseObj::Name()); // Имя объекта-элемента this.SetProperty(CANV_ELEMENT_PROP_TYPE,element_type); // Тип графического элемента this.SetProperty(CANV_ELEMENT_PROP_VISIBLE_AREA_HEIGHT,h); // Высота области видимости this.SetProperty(CANV_ELEMENT_PROP_DISPLAYED,true); // Флаг отображения не скрытого элемента управления this.SetProperty(CANV_ELEMENT_PROP_CONTROL_AREA_X,0); // X-координата области управления this.SetProperty(CANV_ELEMENT_PROP_CONTROL_AREA_Y,0); // Y-координата области управления this.SetProperty(CANV_ELEMENT_PROP_CONTROL_AREA_WIDTH,0); // Ширина области управления this.SetProperty(CANV_ELEMENT_PROP_CONTROL_AREA_HEIGHT,0); // Высота области управления this.SetProperty(CANV_ELEMENT_PROP_SCROLL_AREA_X_RIGHT,0); // X-координата области прокрутки справа this.SetProperty(CANV_ELEMENT_PROP_SCROLL_AREA_Y_RIGHT,0); // Y-координата области прокрутки справа this.SetProperty(CANV_ELEMENT_PROP_SCROLL_AREA_WIDTH_RIGHT,0); // Ширина области прокрутки справа this.SetProperty(CANV_ELEMENT_PROP_SCROLL_AREA_HEIGHT_RIGHT,0); // Высота области прокрутки справа this.SetProperty(CANV_ELEMENT_PROP_SCROLL_AREA_X_BOTTOM,0); // X-координата области прокрутки снизу this.SetProperty(CANV_ELEMENT_PROP_SCROLL_AREA_Y_BOTTOM,0); // Y-координата области прокрутки снизу this.SetProperty(CANV_ELEMENT_PROP_SCROLL_AREA_WIDTH_BOTTOM,0); // Ширина области прокрутки снизу this.SetProperty(CANV_ELEMENT_PROP_SCROLL_AREA_HEIGHT_BOTTOM,0); // Высота области прокрутки снизу this.SetProperty(CANV_ELEMENT_PROP_BORDER_LEFT_AREA_WIDTH,0); // Ширина области левой грани this.SetProperty(CANV_ELEMENT_PROP_BORDER_BOTTOM_AREA_WIDTH,0); // Ширина области нижней грани this.SetProperty(CANV_ELEMENT_PROP_BORDER_RIGHT_AREA_WIDTH,0); // Ширина области правой грани this.SetProperty(CANV_ELEMENT_PROP_BORDER_TOP_AREA_WIDTH,0); // Ширина области верхней грани //--- this.SetProperty(CANV_ELEMENT_PROP_BELONG,ENUM_GRAPH_OBJ_BELONG::GRAPH_OBJ_BELONG_PROGRAM); // Принадлежность графического элемента this.SetProperty(CANV_ELEMENT_PROP_ZORDER,0); // Приоритет графического объекта на получение события нажатия мышки на графике this.SetProperty(CANV_ELEMENT_PROP_BOLD_TYPE,FW_NORMAL); // Тип толщины шрифта //---... //---... this.SetProperty(CANV_ELEMENT_PROP_SPLIT_CONTAINER_SPLITTER_DISTANCE,50); // Расстояние от края до разделителя this.SetProperty(CANV_ELEMENT_PROP_SPLIT_CONTAINER_SPLITTER_WIDTH,4); // Толщина разделителя this.SetProperty(CANV_ELEMENT_PROP_SPLIT_CONTAINER_SPLITTER_ORIENTATION,0); // Расположение разделителя 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_WND_NUM,CGBaseObj::SubWindow()); // Номер подокна графика //---... //---... this.SetProperty(CANV_ELEMENT_PROP_NAME_OBJ,CGBaseObj::Name()); // Имя объекта-элемента this.SetProperty(CANV_ELEMENT_PROP_TYPE,element_type); // Тип графического элемента this.SetProperty(CANV_ELEMENT_PROP_VISIBLE_AREA_HEIGHT,h); // Высота области видимости this.SetProperty(CANV_ELEMENT_PROP_DISPLAYED,true); // Флаг отображения не скрытого элемента управления this.SetProperty(CANV_ELEMENT_PROP_CONTROL_AREA_X,0); // X-координата области управления this.SetProperty(CANV_ELEMENT_PROP_CONTROL_AREA_Y,0); // Y-координата области управления this.SetProperty(CANV_ELEMENT_PROP_CONTROL_AREA_WIDTH,0); // Ширина области управления this.SetProperty(CANV_ELEMENT_PROP_CONTROL_AREA_HEIGHT,0); // Высота области управления this.SetProperty(CANV_ELEMENT_PROP_SCROLL_AREA_X_RIGHT,0); // X-координата области прокрутки справа this.SetProperty(CANV_ELEMENT_PROP_SCROLL_AREA_Y_RIGHT,0); // Y-координата области прокрутки справа this.SetProperty(CANV_ELEMENT_PROP_SCROLL_AREA_WIDTH_RIGHT,0); // Ширина области прокрутки справа this.SetProperty(CANV_ELEMENT_PROP_SCROLL_AREA_HEIGHT_RIGHT,0); // Высота области прокрутки справа this.SetProperty(CANV_ELEMENT_PROP_SCROLL_AREA_X_BOTTOM,0); // X-координата области прокрутки снизу this.SetProperty(CANV_ELEMENT_PROP_SCROLL_AREA_Y_BOTTOM,0); // Y-координата области прокрутки снизу this.SetProperty(CANV_ELEMENT_PROP_SCROLL_AREA_WIDTH_BOTTOM,0); // Ширина области прокрутки снизу this.SetProperty(CANV_ELEMENT_PROP_SCROLL_AREA_HEIGHT_BOTTOM,0); // Высота области прокрутки снизу this.SetProperty(CANV_ELEMENT_PROP_BORDER_LEFT_AREA_WIDTH,0); // Ширина области левой грани this.SetProperty(CANV_ELEMENT_PROP_BORDER_BOTTOM_AREA_WIDTH,0); // Ширина области нижней грани this.SetProperty(CANV_ELEMENT_PROP_BORDER_RIGHT_AREA_WIDTH,0); // Ширина области правой грани this.SetProperty(CANV_ELEMENT_PROP_BORDER_TOP_AREA_WIDTH,0); // Ширина области верхней грани //--- this.SetProperty(CANV_ELEMENT_PROP_BELONG,ENUM_GRAPH_OBJ_BELONG::GRAPH_OBJ_BELONG_PROGRAM); // Принадлежность графического элемента this.SetProperty(CANV_ELEMENT_PROP_ZORDER,0); // Приоритет графического объекта на получение события нажатия мышки на графике this.SetProperty(CANV_ELEMENT_PROP_BOLD_TYPE,FW_NORMAL); // Тип толщины шрифта //---... //---... this.SetProperty(CANV_ELEMENT_PROP_BORDER_STYLE,FRAME_STYLE_NONE); // Стиль рамки элемента управления this.SetProperty(CANV_ELEMENT_PROP_BORDER_SIZE_TOP,0); // Размер рамки элемента управления сверху this.SetProperty(CANV_ELEMENT_PROP_SPLIT_CONTAINER_SPLITTER_DISTANCE,50); // Расстояние от края до разделителя this.SetProperty(CANV_ELEMENT_PROP_SPLIT_CONTAINER_SPLITTER_WIDTH,4); // Толщина разделителя this.SetProperty(CANV_ELEMENT_PROP_SPLIT_CONTAINER_SPLITTER_ORIENTATION,0); // Расположение разделителя 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()); } } //+------------------------------------------------------------------+
Всем свойствам по умолчанию устанавливаем нулевые значения. Для свойства "Ориентиция разделителя" нулевое значение означает вертикальное расположение разделителя.
В методе, создающем структуру объекта, запишем в поля структуры значения новых свойств элемента:
//+------------------------------------------------------------------+ //| Создаёт структуру объекта | //+------------------------------------------------------------------+ bool CGCnvElement::ObjectToStruct(void) { //--- Сохранение целочисленных свойств this.m_struct_obj.id=(int)this.GetProperty(CANV_ELEMENT_PROP_ID); // Идентификатор элемента this.m_struct_obj.type=(int)this.GetProperty(CANV_ELEMENT_PROP_TYPE); // Тип графического элемента this.m_struct_obj.belong=(int)this.GetProperty(CANV_ELEMENT_PROP_BELONG); // Принадлежность графического элемента this.m_struct_obj.number=(int)this.GetProperty(CANV_ELEMENT_PROP_NUM); // Номер элемента в списке //---... //---... this.m_struct_obj.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_splitter_orientation=(int)this.GetProperty(CANV_ELEMENT_PROP_SPLIT_CONTAINER_SPLITTER_ORIENTATION); // Расположение разделителя 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 this.m_struct_obj.control_area_x=(int)this.GetProperty(CANV_ELEMENT_PROP_CONTROL_AREA_X); // X-координата области управления this.m_struct_obj.control_area_y=(int)this.GetProperty(CANV_ELEMENT_PROP_CONTROL_AREA_Y); // Y-координата области управления this.m_struct_obj.control_area_width=(int)this.GetProperty(CANV_ELEMENT_PROP_CONTROL_AREA_WIDTH); // Ширина области управления this.m_struct_obj.control_area_height=(int)this.GetProperty(CANV_ELEMENT_PROP_CONTROL_AREA_HEIGHT); // Высота области управления this.m_struct_obj.scroll_area_x_right=(int)this.GetProperty(CANV_ELEMENT_PROP_SCROLL_AREA_X_RIGHT); // X-координата области прокрутки справа this.m_struct_obj.scroll_area_y_right=(int)this.GetProperty(CANV_ELEMENT_PROP_SCROLL_AREA_Y_RIGHT); // Y-координата области прокрутки справа this.m_struct_obj.scroll_area_width_right=(int)this.GetProperty(CANV_ELEMENT_PROP_SCROLL_AREA_WIDTH_RIGHT); // Ширина области прокрутки справа this.m_struct_obj.scroll_area_height_right=(int)this.GetProperty(CANV_ELEMENT_PROP_SCROLL_AREA_HEIGHT_RIGHT); // Высота области прокрутки справа this.m_struct_obj.scroll_area_x_bottom=(int)this.GetProperty(CANV_ELEMENT_PROP_SCROLL_AREA_X_BOTTOM); // X-координата области прокрутки снизу this.m_struct_obj.scroll_area_y_bottom=(int)this.GetProperty(CANV_ELEMENT_PROP_SCROLL_AREA_Y_BOTTOM); // Y-координата области прокрутки снизу this.m_struct_obj.scroll_area_width_bottom=(int)this.GetProperty(CANV_ELEMENT_PROP_SCROLL_AREA_WIDTH_BOTTOM); // Ширина области прокрутки снизу this.m_struct_obj.scroll_area_height_bottom=(int)this.GetProperty(CANV_ELEMENT_PROP_SCROLL_AREA_HEIGHT_BOTTOM);// Высота области прокрутки снизу this.m_struct_obj.border_left_area_width=(int)this.GetProperty(CANV_ELEMENT_PROP_BORDER_LEFT_AREA_WIDTH); // Ширина области левой грани this.m_struct_obj.border_bottom_area_width=(int)this.GetProperty(CANV_ELEMENT_PROP_BORDER_BOTTOM_AREA_WIDTH); // Ширина области нижней грани this.m_struct_obj.border_right_area_width=(int)this.GetProperty(CANV_ELEMENT_PROP_BORDER_RIGHT_AREA_WIDTH); // Ширина области правой грани this.m_struct_obj.border_top_area_width=(int)this.GetProperty(CANV_ELEMENT_PROP_BORDER_TOP_AREA_WIDTH); // Ширина области верхней грани //--- Сохранение вещественных свойств //--- Сохранение строковых свойств ::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_BELONG,this.m_struct_obj.belong); // Принадлежность графического элемента this.SetProperty(CANV_ELEMENT_PROP_NUM,this.m_struct_obj.number); // Номер элемента в списке //---... //---... 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_SPLITTER_ORIENTATION,this.m_struct_obj.split_container_splitter_orientation); // Расположение разделителя 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_CONTROL_AREA_X,this.m_struct_obj.control_area_x); // X-координата области управления this.SetProperty(CANV_ELEMENT_PROP_CONTROL_AREA_Y,this.m_struct_obj.control_area_y); // Y-координата области управления this.SetProperty(CANV_ELEMENT_PROP_CONTROL_AREA_WIDTH,this.m_struct_obj.control_area_width); // Ширина области управления this.SetProperty(CANV_ELEMENT_PROP_CONTROL_AREA_HEIGHT,this.m_struct_obj.control_area_height); // Высота области управления this.SetProperty(CANV_ELEMENT_PROP_SCROLL_AREA_X_RIGHT,this.m_struct_obj.scroll_area_x_right); // X-координата области прокрутки справа this.SetProperty(CANV_ELEMENT_PROP_SCROLL_AREA_Y_RIGHT,this.m_struct_obj.scroll_area_y_right); // Y-координата области прокрутки справа this.SetProperty(CANV_ELEMENT_PROP_SCROLL_AREA_WIDTH_RIGHT,this.m_struct_obj.scroll_area_width_right); // Ширина области прокрутки справа this.SetProperty(CANV_ELEMENT_PROP_SCROLL_AREA_HEIGHT_RIGHT,this.m_struct_obj.scroll_area_height_right); // Высота области прокрутки справа this.SetProperty(CANV_ELEMENT_PROP_SCROLL_AREA_X_BOTTOM,this.m_struct_obj.scroll_area_x_bottom); // X-координата области прокрутки снизу this.SetProperty(CANV_ELEMENT_PROP_SCROLL_AREA_Y_BOTTOM,this.m_struct_obj.scroll_area_y_bottom); // Y-координата области прокрутки снизу this.SetProperty(CANV_ELEMENT_PROP_SCROLL_AREA_WIDTH_BOTTOM,this.m_struct_obj.scroll_area_width_bottom); // Ширина области прокрутки снизу this.SetProperty(CANV_ELEMENT_PROP_SCROLL_AREA_HEIGHT_BOTTOM,this.m_struct_obj.scroll_area_height_bottom); // Высота области прокрутки снизу this.SetProperty(CANV_ELEMENT_PROP_BORDER_LEFT_AREA_WIDTH,this.m_struct_obj.border_left_area_width); // Ширина области левой грани this.SetProperty(CANV_ELEMENT_PROP_BORDER_BOTTOM_AREA_WIDTH,this.m_struct_obj.border_bottom_area_width); // Ширина области нижней грани this.SetProperty(CANV_ELEMENT_PROP_BORDER_RIGHT_AREA_WIDTH,this.m_struct_obj.border_right_area_width); // Ширина области правой грани this.SetProperty(CANV_ELEMENT_PROP_BORDER_TOP_AREA_WIDTH,this.m_struct_obj.border_top_area_width); // Ширина области верхней грани //--- Сохранение вещественных свойств //--- Сохранение строковых свойств 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));// Описание графического элемента } //+------------------------------------------------------------------+
За пределами тела класса напишем реализацию метода, возвращающего положение курсора относительно зоны управления элемента:
//+------------------------------------------------------------------+ //|Возвращает положение курсора относительно зоны управления элемента| //+------------------------------------------------------------------+ bool CGCnvElement::CursorInsideControlArea(const int x,const int y) { return(x>=this.ControlAreaX() && x<=this.ControlAreaX()+this.ControlAreaWidth() && y>=this.ControlAreaY() && y<=this.ControlAreaY()+this.ControlAreaHeight()); } //+------------------------------------------------------------------+
В метод передаются координаты курсора и возвращается флаг нахождения значений координат X и Y курсора в пределах размеров области управления, установленных для объекта.
В файле класса объекта-формы \MQL5\Include\DoEasy\Objects\Graph\Form.mqh в методе, показывающем форму, добавим проверку для прикреплённых объектов на то, что для объекта установлен флаг его отображения и, если объект не должен отображаться, то и показывать его не нужно:
//+------------------------------------------------------------------+ //| Показывает форму | //+------------------------------------------------------------------+ void CForm::Show(void) { //--- Если элемент не должен показываться (скрыт внутри другого элемента управления) - уходим if(!this.Displayed()) return; //--- Если у объекта есть тень - отобразим её if(this.m_shadow_obj!=NULL) this.m_shadow_obj.Show(); //--- Отобразим главную форму CGCnvElement::Show(); //--- В цикле по всем привязанным графическим объектам for(int i=0;i<this.m_list_elements.Total();i++) { //--- получим очередной графический элемент CGCnvElement *element=this.m_list_elements.At(i); if(element==NULL || !element.Displayed()) continue; //--- и отобразим его element.Show(); } //--- Обновим форму CGCnvElement::Update(); } //+------------------------------------------------------------------+
В методе, устанавливающем и возвращающем состояние мышки относительно формы, впишем блок кода, заполняющий бит переменной m_mouse_state_flags, отвечающий за расположение курсора внутри области управления:
//+------------------------------------------------------------------+ //| Устанавливает и возвращает состояние мышки относительно формы | //+------------------------------------------------------------------+ ENUM_MOUSE_FORM_STATE CForm::MouseFormState(const int id,const long lparam,const double dparam,const string sparam) { //--- Получаем состояние мышки относительно формы и состояние кнопок мыши и клавиш Shift и Ctrl this.m_mouse_form_state=MOUSE_FORM_STATE_OUTSIDE_FORM_NOT_PRESSED; ENUM_MOUSE_BUTT_KEY_STATE state=this.m_mouse.ButtonKeyState(id,lparam,dparam,sparam); //--- Получаем флаги состоянии мышки из объекта класса CMouseState и сохраняем их в переменной this.m_mouse_state_flags=this.m_mouse.GetMouseFlags(); //--- Если курсор внутри формы if(CGCnvElement::CursorInsideElement(this.m_mouse.CoordX(),this.m_mouse.CoordY())) { //--- Устанавливаем бит 8, отвечающий за флаг "курсор внутри формы" this.m_mouse_state_flags |= (0x0001<<8); //--- Если курсор внутри активной зоны - устанавливаем бит 9 "курсор внутри активной зоны" if(CGCnvElement::CursorInsideActiveArea(this.m_mouse.CoordX(),this.m_mouse.CoordY())) this.m_mouse_state_flags |= (0x0001<<9); //--- иначе - снимаем бит "курсор внутри активной зоны" else this.m_mouse_state_flags &=0xFDFF; //--- Если курсор внутри области управления - устанавливаем бит 10 "курсор внутри области управления" if(CGCnvElement::CursorInsideControlArea(this.m_mouse.CoordX(),this.m_mouse.CoordY())) this.m_mouse_state_flags |= (0x0001<<10); //--- иначе - снимаем бит "курсор внутри области управления" else this.m_mouse_state_flags &=0xFBFF; //--- Если нажата одна из трёх кнопок мыши - проверяем расположение курсора в активной области и //--- возвращаем соответствующее значение нажатой кнопки (в активной зоне или в области формы) if((this.m_mouse_state_flags & 0x0001)!=0 || (this.m_mouse_state_flags & 0x0002)!=0 || (this.m_mouse_state_flags & 0x0010)!=0) this.m_mouse_form_state=((this.m_mouse_state_flags & 0x0200)!=0 ? MOUSE_FORM_STATE_INSIDE_ACTIVE_AREA_PRESSED : MOUSE_FORM_STATE_INSIDE_FORM_PRESSED); //--- иначе если ни одна кнопка мышки не нажата else { //--- если колесо мышки прокручивается, возвращаем соответствующее значение прокрутки колеса (в активной зоне или в области формы) if((this.m_mouse_state_flags & 0x0080)!=0) this.m_mouse_form_state=((this.m_mouse_state_flags & 0x0200)!=0 ? MOUSE_FORM_STATE_INSIDE_ACTIVE_AREA_WHEEL : MOUSE_FORM_STATE_INSIDE_FORM_WHEEL); //--- иначе - возвращаем соответствующее значение ненажатой кнопки (в активной зоне или в области формы) else this.m_mouse_form_state=((this.m_mouse_state_flags & 0x0200)!=0 ? MOUSE_FORM_STATE_INSIDE_ACTIVE_AREA_NOT_PRESSED : MOUSE_FORM_STATE_INSIDE_FORM_NOT_PRESSED); } } //--- Если курсор снаружи формы else { //--- возвращаем соответствующее значение кнопок в неактивной зоне this.m_mouse_form_state= ( ((this.m_mouse_state_flags & 0x0001)!=0 || (this.m_mouse_state_flags & 0x0002)!=0 || (this.m_mouse_state_flags & 0x0010)!=0) ? MOUSE_FORM_STATE_OUTSIDE_FORM_PRESSED : MOUSE_FORM_STATE_OUTSIDE_FORM_NOT_PRESSED ); } return this.m_mouse_form_state; } //+------------------------------------------------------------------+
Если метод CursorInsideControlArea() вернул true, это означает, что курсор находится в области управления формы, и в этом случае нужно установить бит 10 (выставить в 1), сигнализирующий об этом. Если же курсор не в области управления, то бит 10 снимается (выставляется в ноль).
Обработчик событий мышки просто сократим, написав все кейсы оператора switch в одну строку:
//+------------------------------------------------------------------+ //| Обработчик событий мышки | //+------------------------------------------------------------------+ void CForm::OnMouseEvent(const int id,const long &lparam,const double &dparam,const string &sparam) { switch(id) { //--- Курсор за пределами формы, кнопки мышки не нажаты //--- Курсор за пределами формы, нажата кнопка мышки (любая) //--- Курсор за пределами формы, прокручивается колёсико мышки case MOUSE_EVENT_OUTSIDE_FORM_NOT_PRESSED : case MOUSE_EVENT_OUTSIDE_FORM_PRESSED : case MOUSE_EVENT_OUTSIDE_FORM_WHEEL : break; //--- Курсор в пределах формы, кнопки мышки не нажаты case MOUSE_EVENT_INSIDE_FORM_NOT_PRESSED : this.MouseInsideNotPressedHandler(id,lparam,dparam,sparam); break; //--- Курсор в пределах формы, нажата кнопка мышки (любая) case MOUSE_EVENT_INSIDE_FORM_PRESSED : this.MouseInsidePressedHandler(id,lparam,dparam,sparam); break; //--- Курсор в пределах формы, прокручивается колёсико мышки case MOUSE_EVENT_INSIDE_FORM_WHEEL : this.MouseInsideWhellHandler(id,lparam,dparam,sparam); break; //--- Курсор в пределах активной области, кнопки мышки не нажаты case MOUSE_EVENT_INSIDE_ACTIVE_AREA_NOT_PRESSED : this.MouseActiveAreaNotPressedHandler(id,lparam,dparam,sparam);break; //--- Курсор в пределах активной области, нажата кнопка мышки (любая) case MOUSE_EVENT_INSIDE_ACTIVE_AREA_PRESSED : this.MouseActiveAreaPressedHandler(id,lparam,dparam,sparam); break; //--- Курсор в пределах активной области, прокручивается колёсико мышки case MOUSE_EVENT_INSIDE_ACTIVE_AREA_WHEEL : this.MouseActiveAreaWhellHandler(id,lparam,dparam,sparam); break; //--- Курсор в пределах активной области, отжата кнопка мышки (левая) case MOUSE_EVENT_INSIDE_ACTIVE_AREA_RELEASED : this.MouseActiveAreaReleasedHandler(id,lparam,dparam,sparam); break; //--- Курсор в пределах области прокрутки окна, кнопки мышки не нажаты case MOUSE_EVENT_INSIDE_SCROLL_AREA_NOT_PRESSED : this.MouseScrollAreaNotPressedHandler(id,lparam,dparam,sparam);break; //--- Курсор в пределах области прокрутки окна, нажата кнопка мышки (любая) case MOUSE_EVENT_INSIDE_SCROLL_AREA_PRESSED : this.MouseScrollAreaPressedHandler(id,lparam,dparam,sparam); break; //--- Курсор в пределах области прокрутки окна, прокручивается колёсико мышки case MOUSE_EVENT_INSIDE_SCROLL_AREA_WHEEL : this.MouseScrollAreaWhellHandler(id,lparam,dparam,sparam); break; //---MOUSE_EVENT_NO_EVENT default: break; } this.m_mouse_event_last=(ENUM_MOUSE_EVENT)id; } //+------------------------------------------------------------------+
Таким образом метод просто становится лучше читаемым — его весь сразу можно окинуть взглядом. Впрочем, это из области личных предпочтений и привычек.
В обработчик последнего события мышки добавим заготовки для обработки новых "прошлых" состояний:
//+------------------------------------------------------------------+ //| Обработчик последнего события мышки | //+------------------------------------------------------------------+ void CForm::OnMouseEventPostProcessing(void) { if(!this.IsVisible() || !this.Enabled()) return; ENUM_MOUSE_FORM_STATE state=this.GetMouseState(); switch(state) { //--- Курсор за пределами формы, кнопки мышки не нажаты //--- Курсор за пределами формы, нажата кнопка мышки (любая) //--- Курсор за пределами формы, прокручивается колёсико мышки case MOUSE_FORM_STATE_OUTSIDE_FORM_NOT_PRESSED : case MOUSE_FORM_STATE_OUTSIDE_FORM_PRESSED : case MOUSE_FORM_STATE_OUTSIDE_FORM_WHEEL : case MOUSE_FORM_STATE_NONE : if(this.MouseEventLast()==MOUSE_EVENT_INSIDE_ACTIVE_AREA_NOT_PRESSED || this.MouseEventLast()==MOUSE_EVENT_INSIDE_FORM_NOT_PRESSED || this.MouseEventLast()==MOUSE_EVENT_OUTSIDE_FORM_NOT_PRESSED || this.MouseEventLast()==MOUSE_EVENT_NO_EVENT) { this.SetBackgroundColor(this.BackgroundColorInit(),false); this.SetBorderColor(this.BorderColorInit(),false); this.m_mouse_event_last=ENUM_MOUSE_EVENT(state+MOUSE_EVENT_NO_EVENT); } break; //--- Курсор в пределах формы, кнопки мышки не нажаты //--- Курсор в пределах формы, нажата кнопка мышки (любая) //--- Курсор в пределах формы, прокручивается колёсико мышки //--- Курсор в пределах активной области, кнопки мышки не нажаты //--- Курсор в пределах активной области, нажата кнопка мышки (любая) //--- Курсор в пределах активной области, прокручивается колёсико мышки //--- Курсор в пределах активной области, отжата кнопка мышки (левая) //--- Курсор в пределах области прокрутки окна, кнопки мышки не нажаты //--- Курсор в пределах области прокрутки окна, нажата кнопка мышки (любая) //--- Курсор в пределах области прокрутки окна, прокручивается колёсико мышки //--- Курсор в пределах области изменения размеров окна, кнопки мышки не нажаты //--- Курсор в пределах области изменения размеров окна, нажата кнопка мышки (любая) //--- Курсор в пределах области изменения размеров окна, прокручивается колёсико мышки //--- Курсор в пределах области резделителя окна, кнопки мышки не нажаты //--- Курсор в пределах области резделителя окна, нажата кнопка мышки (любая) //--- Курсор в пределах области резделителя окна, прокручивается колёсико мышки case MOUSE_FORM_STATE_INSIDE_FORM_NOT_PRESSED : case MOUSE_FORM_STATE_INSIDE_FORM_PRESSED : case MOUSE_FORM_STATE_INSIDE_FORM_WHEEL : case MOUSE_FORM_STATE_INSIDE_ACTIVE_AREA_NOT_PRESSED : case MOUSE_FORM_STATE_INSIDE_ACTIVE_AREA_PRESSED : case MOUSE_FORM_STATE_INSIDE_ACTIVE_AREA_WHEEL : case MOUSE_FORM_STATE_INSIDE_ACTIVE_AREA_RELEASED : case MOUSE_FORM_STATE_INSIDE_SCROLL_AREA_NOT_PRESSED : case MOUSE_FORM_STATE_INSIDE_SCROLL_AREA_PRESSED : case MOUSE_FORM_STATE_INSIDE_SCROLL_AREA_WHEEL : case MOUSE_FORM_STATE_INSIDE_RESIZE_AREA_NOT_PRESSED : case MOUSE_FORM_STATE_INSIDE_RESIZE_AREA_PRESSED : case MOUSE_FORM_STATE_INSIDE_RESIZE_AREA_WHEEL : case MOUSE_FORM_STATE_INSIDE_SPLITTER_AREA_NOT_PRESSED: case MOUSE_FORM_STATE_INSIDE_SPLITTER_AREA_PRESSED : case MOUSE_FORM_STATE_INSIDE_SPLITTER_AREA_WHEEL : break; //---MOUSE_EVENT_NO_EVENT default: break; } } //+------------------------------------------------------------------+
В последующем нам может потребоваться обработать эти события, которые были последними у мышки относительно объекта, поэтому сразу же добавили сюда заготовки обработчиков этих событий. На данный момент они никак не обрабатываются.
В файле класса базового WinForms-объекта \MQL5\Include\DoEasy\Objects\Graph\WForms\WinFormBase.mqh в методе, перерисовывающем объект, впишем проверку на флаг отображения объекта и, если объект отображать не нужно, то и перерисовывать его не надо:
//+------------------------------------------------------------------+ //| Перерисовывает объект | //+------------------------------------------------------------------+ void CWinFormBase::Redraw(bool redraw) { //--- Если тип объекта меньше, чем "Базовый WinForms-объект" - уходим if(this.TypeGraphElement()<GRAPH_ELEMENT_TYPE_WF_BASE || !this.Displayed()) return; //--- Получим объект "Тень" CShadowObj *shadow=this.GetShadowObj(); //--- Если у объекта есть тень, и объект "Тень" существует - перерисуем тень if(this.IsShadow() && shadow!=NULL) { //--- сотрём ранее нарисованную тень, shadow.Erase(); //--- запомним относительные координаты тени, int x=shadow.CoordXRelative(); int y=shadow.CoordYRelative(); //--- перерисуем тень, if(redraw) shadow.Draw(0,0,shadow.Blur(),redraw); //--- восстановим относительные координаты тени shadow.SetCoordXRelative(x); shadow.SetCoordYRelative(y); } //--- Если стоит флаг перерисовки if(redraw) { //--- полностью перерисовываем объект и запоминаем его новый изначальный вид this.Erase(this.m_array_colors_bg,this.Opacity(),this.m_gradient_v,this.m_gradient_c,redraw); this.Done(); } //--- иначе - стираем объект else this.Erase(); //--- Перерисуем с флагом перерисовки все прикреплённые объекты for(int i=0;i<this.ElementsTotal();i++) { CWinFormBase *element=this.GetElement(i); if(element==NULL) continue; if(redraw) element.Redraw(redraw); } //--- При установленном флаге перерисовки, и если это главный объект, к которому прикреплены остальные - //--- перерисуем чарт для немедленного отображения изменений if(redraw && this.GetMain()==NULL) ::ChartRedraw(this.ChartID()); } //+------------------------------------------------------------------+
В методе, возвращающем описание целочисленного свойства элемента, добавим блок кода для возврата описаний новых свойств графического элемента:
//+------------------------------------------------------------------+ //| Возвращает описание целочисленного свойства элемента | //+------------------------------------------------------------------+ string CWinFormBase::GetPropertyDescription(ENUM_CANV_ELEMENT_PROP_INTEGER property,bool only_prop=false) { return ( property==CANV_ELEMENT_PROP_ID ? CMessage::Text(MSG_CANV_ELEMENT_PROP_ID)+ (only_prop ? "" : !this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(string)this.GetProperty(property) ) : property==CANV_ELEMENT_PROP_TYPE ? CMessage::Text(MSG_CANV_ELEMENT_PROP_TYPE)+ (only_prop ? "" : !this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+this.TypeElementDescription() ) : //---... //---... property==CANV_ELEMENT_PROP_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_SPLITTER_ORIENTATION ? CMessage::Text(MSG_CANV_ELEMENT_PROP_SPLIT_CONTAINER_SPLITTER_ORIENTATION)+ (only_prop ? "" : !this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(string)this.GetProperty(property) ) : property==CANV_ELEMENT_PROP_CONTROL_AREA_X ? CMessage::Text(MSG_CANV_ELEMENT_PROP_CONTROL_AREA_X)+ (only_prop ? "" : !this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(string)this.GetProperty(property) ) : property==CANV_ELEMENT_PROP_CONTROL_AREA_Y ? CMessage::Text(MSG_CANV_ELEMENT_PROP_CONTROL_AREA_Y)+ (only_prop ? "" : !this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(string)this.GetProperty(property) ) : property==CANV_ELEMENT_PROP_CONTROL_AREA_WIDTH ? CMessage::Text(MSG_CANV_ELEMENT_PROP_CONTROL_AREA_WIDTH)+ (only_prop ? "" : !this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(string)this.GetProperty(property) ) : property==CANV_ELEMENT_PROP_CONTROL_AREA_HEIGHT ? CMessage::Text(MSG_CANV_ELEMENT_PROP_CONTROL_AREA_HEIGHT)+ (only_prop ? "" : !this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(string)this.GetProperty(property) ) : property==CANV_ELEMENT_PROP_SCROLL_AREA_X_RIGHT ? CMessage::Text(MSG_CANV_ELEMENT_PROP_SCROLL_AREA_X_RIGHT)+ (only_prop ? "" : !this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(string)this.GetProperty(property) ) : property==CANV_ELEMENT_PROP_SCROLL_AREA_Y_RIGHT ? CMessage::Text(MSG_CANV_ELEMENT_PROP_SCROLL_AREA_Y_RIGHT)+ (only_prop ? "" : !this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(string)this.GetProperty(property) ) : property==CANV_ELEMENT_PROP_SCROLL_AREA_WIDTH_RIGHT ? CMessage::Text(MSG_CANV_ELEMENT_PROP_SCROLL_AREA_WIDTH_RIGHT)+ (only_prop ? "" : !this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(string)this.GetProperty(property) ) : property==CANV_ELEMENT_PROP_SCROLL_AREA_HEIGHT_RIGHT ? CMessage::Text(MSG_CANV_ELEMENT_PROP_SCROLL_AREA_HEIGHT_RIGHT)+ (only_prop ? "" : !this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(string)this.GetProperty(property) ) : property==CANV_ELEMENT_PROP_SCROLL_AREA_X_BOTTOM ? CMessage::Text(MSG_CANV_ELEMENT_PROP_SCROLL_AREA_X_BOTTOM)+ (only_prop ? "" : !this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(string)this.GetProperty(property) ) : property==CANV_ELEMENT_PROP_SCROLL_AREA_Y_BOTTOM ? CMessage::Text(MSG_CANV_ELEMENT_PROP_SCROLL_AREA_Y_BOTTOM)+ (only_prop ? "" : !this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(string)this.GetProperty(property) ) : property==CANV_ELEMENT_PROP_SCROLL_AREA_WIDTH_BOTTOM ? CMessage::Text(MSG_CANV_ELEMENT_PROP_SCROLL_AREA_WIDTH_BOTTOM)+ (only_prop ? "" : !this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(string)this.GetProperty(property) ) : property==CANV_ELEMENT_PROP_SCROLL_AREA_HEIGHT_BOTTOM ? CMessage::Text(MSG_CANV_ELEMENT_PROP_SCROLL_AREA_HEIGHT_BOTTOM)+ (only_prop ? "" : !this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(string)this.GetProperty(property) ) : property==CANV_ELEMENT_PROP_BORDER_LEFT_AREA_WIDTH ? CMessage::Text(MSG_CANV_ELEMENT_PROP_BORDER_LEFT_AREA_WIDTH)+ (only_prop ? "" : !this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(string)this.GetProperty(property) ) : property==CANV_ELEMENT_PROP_BORDER_BOTTOM_AREA_WIDTH ? CMessage::Text(MSG_CANV_ELEMENT_PROP_BORDER_BOTTOM_AREA_WIDTH)+ (only_prop ? "" : !this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(string)this.GetProperty(property) ) : property==CANV_ELEMENT_PROP_BORDER_RIGHT_AREA_WIDTH ? CMessage::Text(MSG_CANV_ELEMENT_PROP_BORDER_RIGHT_AREA_WIDTH)+ (only_prop ? "" : !this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(string)this.GetProperty(property) ) : property==CANV_ELEMENT_PROP_BORDER_TOP_AREA_WIDTH ? CMessage::Text(MSG_CANV_ELEMENT_PROP_BORDER_TOP_AREA_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, либо выводится просто наименование свойства, либо вместе с присвоенным ему значением.
Теперь мы можем приступить к созданию нового объекта библиотеки.
Класс вспомогательного объекта-разделителя
Вспомогательные объекты библиотеки сами по себе не являются полноценными элементами управления, но используются для построения таких объектов. Объект-разделитель нам нужен для сигнализации о том, что мы можем переместить область, разделяющую две панели в элементе управления SplitContainer. Перемещением этого объекта мышкой мы вызовем обработчик события элемента SplitContainer, где будет обработано это событие и изменены размеры панелей. В то же время, такой объект нам может потребоваться для взаимодействия и с другими элементами управления, поэтому он будет находиться в папке вспомогательных объектов и использоваться в элементах управления, в которых потребуется для реализации их функционала.
В папке \MQL5\Include\DoEasy\Objects\Graph\WForms\Helpers\ создадим новый файл Splitter.mqh класса CSplitter.
Класс должен быть унаследован от класса базового WinForms-объекта библиотеки, и его файл должен быть подключен к файлу создаваемого класса:
//+------------------------------------------------------------------+ //| Splitter.mqh | //| Copyright 2022, MetaQuotes Ltd. | //| https://mql5.com/ru/users/artmedia70 | //+------------------------------------------------------------------+ #property copyright "Copyright 2022, MetaQuotes Ltd." #property link "https://mql5.com/ru/users/artmedia70" #property version "1.00" #property strict // Нужно для mql4 //+------------------------------------------------------------------+ //| Включаемые файлы | //+------------------------------------------------------------------+ #include "..\WinFormBase.mqh" //+------------------------------------------------------------------+ //| Класс объекта Splitter элементов управления WForms | //+------------------------------------------------------------------+ class CSplitter : public CWinFormBase { }
В защищённой секции класса объявим виртуальный метод, рисующий сетку (штриховку объекта) и защищённый конструктор. В публичной секции класса объявим параметрический конструктор и методы для перерисовки и очистки фона графического элемента:
//+------------------------------------------------------------------+ //| Класс объекта Splitter элементов управления WForms | //+------------------------------------------------------------------+ class CSplitter : public CWinFormBase { private: protected: //--- Рисует сетку virtual void DrawGrid(void); //--- Защищённый конструктор с указанием типа объекта, идентификатора чарта и подокна CSplitter(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: //--- Конструктор CSplitter(const long chart_id, const int subwindow, const string descript, const int x, const int y, const int w, const int h); //--- Перерисовывает объект virtual void Redraw(bool redraw); //--- Очищает элемент с заполнением его цветом и непрозрачностью 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); }; //+------------------------------------------------------------------+
Метод, рисующий сетку объекта, объявлен виртуальным на случай, если нужно будет делать производные классы с иной отрисовкой.
Рассмотрим объявленные методы подробнее.
Защищённый конструктор класса:
//+------------------------------------------------------------------+ //| Защищённый конструктор с указанием типа объекта, | //| идентификатора чарта и подокна | //+------------------------------------------------------------------+ CSplitter::CSplitter(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) : CWinFormBase(type,chart_id,subwindow,descript,x,y,w,h) { //--- Установим объекту указанный тип графического элемента, а тип объекта библиотеки - как тип этого объекта this.SetTypeElement(type); this.m_type=OBJECT_DE_TYPE_GWF_HELPER; this.SetPaddingAll(0); this.SetMarginAll(0); this.SetBorderSizeAll(0); } //+------------------------------------------------------------------+
В формальных параметрах конструктора передаётся тип создаваемого объекта, который в строке инициализации передаётся в конструктор родительского класса. В теле класса устанавливается переданный в конструктор тип графического элемента и тип графического объекта библиотеки как вспомогательный объект. Значения Padding, Margin и размеры рамки выставляются в ноль.
Параметрический конструктор:
//+------------------------------------------------------------------+ //| Конструктор | //+------------------------------------------------------------------+ CSplitter::CSplitter(const long chart_id, const int subwindow, const string descript, const int x, const int y, const int w, const int h) : CWinFormBase(GRAPH_ELEMENT_TYPE_WF_SPLITTER,chart_id,subwindow,descript,x,y,w,h) { this.SetTypeElement(GRAPH_ELEMENT_TYPE_WF_SPLITTER); this.m_type=OBJECT_DE_TYPE_GWF_HELPER; this.SetPaddingAll(0); this.SetMarginAll(0); this.SetBorderSizeAll(0); this.SetDisplayed(false); } //+------------------------------------------------------------------+
Здесь всё аналогично защищённому конструктору, но тип графического элемента указывается напрямую, а не передаётся в формальных параметрах.
Метод, перерисовывающий объект:
//+------------------------------------------------------------------+ //| Перерисовывает объект | //+------------------------------------------------------------------+ void CSplitter::Redraw(bool redraw) { //--- Если элемент не должен показываться (скрыт внутри другого элемента управления) - уходим if(!this.Displayed()) return; //--- Заполняем объект цветом фона с прозрачностью this.Erase(this.BackgroundColor(),this.Opacity(),true); } //+------------------------------------------------------------------+
Если для объекта снят флаг его отображения на видимом и доступном для взаимодействия элементе управления, то перерисовывать объект не нужно — уходим из метода. Если флаг отображения установлен, то вызываем метод заполнения объекта цветом.
Методы, очищающие элемент с заполнением его цветом и непрозрачностью:
//+------------------------------------------------------------------+ //| Очищает элемент с заполнением его цветом и непрозрачностью | //+------------------------------------------------------------------+ void CSplitter::Erase(const color colour,const uchar opacity,const bool redraw=false) { //--- Если элемент не должен показываться (скрыт внутри другого элемента управления) - уходим if(!this.Displayed()) return; //--- Закрашиваем элемент с указанным цветом и флагом необходимости перерисовки CGCnvElement::EraseNoCrop(colour,opacity,false); //--- Рисуем сетку this.DrawGrid(); //--- Обрезаем и обновляем элемент с указанным флагом необходимости перерисовки this.Crop(); this.Update(redraw); } //+------------------------------------------------------------------+ //| Очищает элемент заливкой градиентом | //+------------------------------------------------------------------+ void CSplitter::Erase(color &colors[],const uchar opacity,const bool vgradient,const bool cycle,const bool redraw=false) { //--- Если элемент не должен показываться (скрыт внутри другого элемента управления) - уходим if(!this.Displayed()) return; //--- Закрашиваем элемент с указанным массивом цветов и флагом необходимости перерисовки CGCnvElement::EraseNoCrop(colors,opacity,vgradient,cycle,false); //--- Рисуем сетку this.DrawGrid(); //--- Обрезаем и обновляем элемент с указанным флагом необходимости перерисовки this.Crop(); this.Update(redraw); } //+------------------------------------------------------------------+
Логика методов полностью прокомментирована в коде. Первый метод заполняет фон одним цветом, второй — градиентом.
Метод, рисующий сетку:
//+------------------------------------------------------------------+ //| Рисует сетку | //+------------------------------------------------------------------+ void CSplitter::DrawGrid(void) { for(int y=0;y<this.Height()-1;y++) for(int x=0;x<this.Width();x++) this.SetPixel(x,y,this.ForeColor(),uchar(y%2==0 ? (x%2==0 ? 255 : 0) : (x%2==0 ? 0 : 255))); } //+------------------------------------------------------------------+
Нам необходимо заполнить фон объекта точками в шахматном порядке. Для этого организуем два цикла: цикл по строкам и цикл по столбцам.
- Если строка чётная, то:
- Если столбец чётный — ставим точку с полной непрозрачностью,
- Если столбец нечётный — ставим точку с полной прозрачностью.
- Если строка нечётная, то:
- Если столбец чётный — ставим точку с полной прозрачностью,
- Если столбец нечётный — ставим точку с полной непрозрачностью.
Таким образом мы заполняем весь фон точками, расположенными в шахматном порядке.
Это всё, что необходмо на данном этапе для работы класса объекта-разделителя.
Теперь необходимо подключить его к классу элемента управления SplitContainer, создать его и управлять им.
В файле \MQL5\Include\DoEasy\Objects\Graph\WForms\Containers\SplitContainer.mqh, в приватной секции класса объявим переменные для хранения координат и размеров панелей и разделителя, и объявим метод для установки параметров панелей:
//+------------------------------------------------------------------+ //| 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" #include "..\Helpers\Splitter.mqh" //+------------------------------------------------------------------+ //| Класс объекта-элемента управления WForms SplitContainer | //+------------------------------------------------------------------+ class CSplitContainer : public CContainer { private: int m_panel1_x; // X-координата панели1 int m_panel1_y; // Y-координата панели1 int m_panel1_w; // ширина панели1 int m_panel1_h; // высота панели1 int m_panel2_x; // X-координата панели2 int m_panel2_y; // Y-координата панели2 int m_panel2_w; // ширина панели2 int m_panel2_h; // высота панели2 int m_splitter_x; // X-координата разделителя int m_splitter_y; // Y-координата разделителя int m_splitter_w; // ширина разделителя int m_splitter_h; // высота разделителя //--- Создаёт новый графический объект 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); //--- Устанавливает параметры панелям bool SetsPanelParams(void); public:
В публичной секции класса объявим/напишем новые методы, у методов получения указателей на панели изменим возвращаемый тип (из-за ошибок компиляции при отдельной компиляции файла класса CSplitContainerPanel), а реализацию некоторых методов вынесем за пределы класса, оставив здесь только объявление методов:
public: //--- Создаёт панели void CreatePanels(void); //--- Возвращает указатель на указанную панель CWinFormBase *GetPanel(const int index) { return CForm::GetElement(index); } //--- Возвращает указатель на (1) панель1, (2) панель2 CWinFormBase *GetPanel1(void) { return this.GetPanel(0); } CWinFormBase *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); //--- Возвращает указатель на разделитель CSplitter *GetSplitter(void) { return this.GetElementByType(GRAPH_ELEMENT_TYPE_WF_SPLITTER,0); } //--- (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 void SetPanel1Collapsed(const int flag); bool Panel1Collapsed(void) const { return (bool)this.GetProperty(CANV_ELEMENT_PROP_SPLIT_CONTAINER_PANEL1_COLLAPSED); } //--- (1) устанавливает, (2) возвращает флаг свёрнутости панели 2 void SetPanel2Collapsed(const int flag); bool Panel2Collapsed(void) const { return (bool)this.GetProperty(CANV_ELEMENT_PROP_SPLIT_CONTAINER_PANEL2_COLLAPSED); } //--- (1) устанавливает, (2) возвращает дистанцию разделителя от края void SetSplitterDistance(const int 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); int SplitterWidth(void) const { return (int)this.GetProperty(CANV_ELEMENT_PROP_SPLIT_CONTAINER_SPLITTER_WIDTH); } //--- (1) устанавливает, (2) возвращает расположение разделителя void SetSplitterOrientation(const ENUM_CANV_ELEMENT_SPLITTER_ORIENTATION value) { this.SetProperty(CANV_ELEMENT_PROP_SPLIT_CONTAINER_SPLITTER_ORIENTATION,value); } ENUM_CANV_ELEMENT_SPLITTER_ORIENTATION SplitterOrientation(void) const { return(ENUM_CANV_ELEMENT_SPLITTER_ORIENTATION)this.GetProperty(CANV_ELEMENT_PROP_SPLIT_CONTAINER_SPLITTER_ORIENTATION); } //--- (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); //--- Обработчик событий virtual void OnChartEvent(const int id,const long& lparam,const double& dparam,const string& sparam); //--- Обработчик события Курсор в пределах активной области, кнопки мышки не нажаты virtual void MouseActiveAreaNotPressedHandler(const int id,const long& lparam,const double& dparam,const string& sparam); //--- Конструктор CSplitContainer(const long chart_id, const int subwindow, const string descript, const int x, const int y, const int w, const int h); }; //+------------------------------------------------------------------+
В методе, создающем новый графический объект, добавим ещё один тип объекта, который может быть создан:
//+------------------------------------------------------------------+ //| Создаёт новый графический объект | //+------------------------------------------------------------------+ 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; case GRAPH_ELEMENT_TYPE_WF_SPLITTER : element=new CSplitter(this.ChartID(),this.SubWindow(),descript,x,y,w,h); break; default : break; } if(element==NULL) ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_FAILED_CREATE_ELM_OBJ),this.TypeElementDescription(type)); return element; } //+------------------------------------------------------------------+
Так как теперь нам нужно внутри объекта кроме панелей создать ещё и разделитель, то метод должен уметь это делать — для создания нового объекта-разделителя и служит добавленная строка.
В методе, создающем панели, добавим блок кода для создания объекта-разделителя:
//+------------------------------------------------------------------+ //| Создаёт панели | //+------------------------------------------------------------------+ void CSplitContainer::CreatePanels(void) { this.m_list_elements.Clear(); if(this.SetsPanelParams()) { if(!this.CreateNewElement(GRAPH_ELEMENT_TYPE_WF_SPLIT_CONTAINER_PANEL,this.m_panel1_x,this.m_panel1_y,this.m_panel1_w,this.m_panel1_h,clrNONE,255,true,false)) return; if(!this.CreateNewElement(GRAPH_ELEMENT_TYPE_WF_SPLIT_CONTAINER_PANEL,this.m_panel2_x,this.m_panel2_y,this.m_panel2_w,this.m_panel2_h,clrNONE,255,true,false)) return; //--- if(!this.CreateNewElement(GRAPH_ELEMENT_TYPE_WF_SPLITTER,this.m_splitter_x,this.m_splitter_y,this.m_splitter_w,this.m_splitter_h,clrNONE,255,true,false)) return; CSplitter *splitter=this.GetSplitter(); if(splitter!=NULL) { splitter.SetMovable(true); splitter.SetDisplayed(false); splitter.Hide(); } } } //+------------------------------------------------------------------+
Здесь: если не удалось создать объект-разделитель — уходим из метода. Далее получаем указатель на созданный объект-разделитель, устанавливаем ему флаг перемещаемости (его же нужно будет смещать мышкой), устанавливаем флаг того, что его не нужно отображать и скрываем созданный объект.
Создание панелей тоже изменено. Теперь мы сначала устанавливаем параметры панелям в новом методе SetsPanelParams(), который будет рассмотрен ниже, и в котором в зависимости от расположения разделителя и флагов свёрнутости панелей устанавливаются их начальные координаты и размеры, записываемые в новые, специально для этого предназначенные переменные, значения которых и передаются в методы создания панелей.
Метод, устанавливающий параметры панелям:
//+------------------------------------------------------------------+ //| Устанавливает параметры панелям | //+------------------------------------------------------------------+ bool CSplitContainer::SetsPanelParams(void) { switch(this.SplitterOrientation()) { //--- Разделитель расположен вертикально case CANV_ELEMENT_SPLITTER_ORIENTATION_VERTICAL : //--- Если обе панели не свёрнуты if(!this.Panel1Collapsed() && !this.Panel2Collapsed()) { //--- записываем координаты и размеры панели1 this.m_panel1_x=0; this.m_panel1_y=0; this.m_panel1_w=this.SplitterDistance(); this.m_panel1_h=this.Height(); //--- записываем координаты и размеры панели2 this.m_panel2_x=this.SplitterDistance()+this.SplitterWidth(); this.m_panel2_y=0; this.m_panel2_w=this.Width()-this.m_panel2_x; this.m_panel2_h=this.Height(); //--- записываем координаты и размеры разделителя this.m_splitter_x=this.SplitterDistance(); this.m_splitter_y=0; this.m_splitter_w=this.SplitterWidth(); this.m_splitter_h=this.Height(); } //--- Если свёрнута панель2 else if(this.Panel2Collapsed()) { //--- записываем координаты и размеры панели1 this.m_panel1_x=0; this.m_panel1_y=0; this.m_panel1_w=this.Width(); this.m_panel1_h=this.Height(); //--- записываем координаты и размеры панели2 this.m_panel2_x=this.SplitterDistance()+this.SplitterWidth(); this.m_panel2_y=0; this.m_panel2_w=this.Width()-this.m_panel2_x; this.m_panel2_h=this.Height(); //--- записываем координаты и размеры разделителя this.m_splitter_x=-this.SplitterWidth(); this.m_splitter_y=0; this.m_splitter_w=this.SplitterWidth(); this.m_splitter_h=this.Height(); } //--- Если свёрнута панель1 else if(this.Panel1Collapsed()) { //--- записываем координаты и размеры панели1 this.m_panel1_x=0; this.m_panel1_y=0; this.m_panel1_w=this.SplitterDistance(); this.m_panel1_h=this.Height(); //--- записываем координаты и размеры панели2 this.m_panel2_x=0; this.m_panel2_y=0; this.m_panel2_w=this.Width(); this.m_panel2_h=this.Height(); //--- записываем координаты и размеры разделителя this.m_splitter_x=-this.SplitterWidth(); this.m_splitter_y=0; this.m_splitter_w=this.SplitterWidth(); this.m_splitter_h=this.Height(); } break; //--- Разделитель расположен горизонтально case CANV_ELEMENT_SPLITTER_ORIENTATION_HORISONTAL : if(!this.Panel1Collapsed() && !this.Panel2Collapsed()) { //--- записываем координаты и размеры панели1 this.m_panel1_x=0; this.m_panel1_y=0; this.m_panel1_w=this.Width(); this.m_panel1_h=this.SplitterDistance(); //--- записываем координаты и размеры панели2 this.m_panel2_x=0; this.m_panel2_y=this.SplitterDistance()+this.SplitterWidth(); this.m_panel2_w=this.Width(); this.m_panel2_h=this.Height()-this.m_panel2_y; //--- записываем координаты и размеры разделителя this.m_splitter_x=0; this.m_splitter_y=this.SplitterDistance(); this.m_splitter_w=this.Width(); this.m_splitter_h=this.SplitterWidth(); } //--- Если свёрнута панель2 else if(this.Panel2Collapsed()) { //--- записываем координаты и размеры панели1 this.m_panel1_x=0; this.m_panel1_y=0; this.m_panel1_w=this.Width(); this.m_panel1_h=this.Height(); //--- записываем координаты и размеры панели2 this.m_panel2_x=0; this.m_panel2_y=this.SplitterDistance()+this.SplitterWidth(); this.m_panel2_w=this.Width(); this.m_panel2_h=this.Height()-this.m_panel2_y; //--- записываем координаты и размеры разделителя this.m_splitter_x=0; this.m_splitter_y=-this.SplitterDistance(); this.m_splitter_w=this.Width(); this.m_splitter_h=this.SplitterWidth(); } //--- Если свёрнута панель1 else if(this.Panel1Collapsed()) { //--- записываем координаты и размеры панели1 this.m_panel1_x=0; this.m_panel1_y=0; this.m_panel1_w=this.Width(); this.m_panel1_h=this.SplitterDistance(); //--- записываем координаты и размеры панели2 this.m_panel2_x=0; this.m_panel2_y=0; this.m_panel2_w=this.Width(); this.m_panel2_h=this.Height(); //--- записываем координаты и размеры разделителя this.m_splitter_x=0; this.m_splitter_y=-this.SplitterDistance(); this.m_splitter_w=this.Width(); this.m_splitter_h=this.SplitterWidth(); } break; default: return false; break; } //--- Устанавливаем координаты и размеры области управления, равными свойствам, установленным разделителю this.SetProperty(CANV_ELEMENT_PROP_CONTROL_AREA_X,this.m_splitter_x); this.SetProperty(CANV_ELEMENT_PROP_CONTROL_AREA_Y,this.m_splitter_y); this.SetProperty(CANV_ELEMENT_PROP_CONTROL_AREA_WIDTH,this.m_splitter_w); this.SetProperty(CANV_ELEMENT_PROP_CONTROL_AREA_HEIGHT,this.m_splitter_h); return true; } //+------------------------------------------------------------------+
В зависимости от расположения разделителя (вертикально или горизонтально) и в зависимости от того не свёрнуты ли обе панели, либо свёрнута какая-то одна из них, записываем в служебные переменные координаты и размеры панелей и разделителя. В конце метода параметры разделителя, установленные в методе, записываем в свойства координат и размеров области управления.
Метод, устанавливающий флаг свёрнутости панели 1:
//+------------------------------------------------------------------+ //| Устанавливает флаг свёрнутости панели 1 | //+------------------------------------------------------------------+ void CSplitContainer::SetPanel1Collapsed(const int flag) { //--- Записываем в свойство объекта переданный в метод флаг this.SetProperty(CANV_ELEMENT_PROP_SPLIT_CONTAINER_PANEL1_COLLAPSED,flag); //--- Если панель1 должна быть свёрнута if(this.Panel1Collapsed()) { //--- устанавливаем для панели2 флаг того, что она развёрнута this.SetPanel2Collapsed(false); //--- Если указатель на панель1 получен if(this.GetPanel1()!=NULL) { //--- устанавливаем флаг неотображения панели и скрываем её this.GetPanel1().SetDisplayed(false); this.GetPanel1().Hide(); } //--- Если указатель на панель2 получен if(this.GetPanel2()!=NULL) { //--- устанавливаем флаг отображения панели, отображаем её и выводим на передний план this.GetPanel2().SetDisplayed(true); this.GetPanel2().Show(); this.GetPanel2().BringToTop(); } } } //+------------------------------------------------------------------+
Метод полностью прокомментирован в коде. Помимо того, что мы устанавливаем флаг свёрнутости панели (если в метод передано значение false), мы её к тому же скрываем и устанавливаем для неё флаг неотображения. А панель 2 при этом отображаем с установкой флага отображения и выводим на передний план.
Метод, устанавливающий флаг свёрнутости панели 2:
//+------------------------------------------------------------------+ //| Устанавливает флаг свёрнутости панели 2 | //+------------------------------------------------------------------+ void CSplitContainer::SetPanel2Collapsed(const int flag) { //--- Записываем в свойство объекта переданный в метод флаг this.SetProperty(CANV_ELEMENT_PROP_SPLIT_CONTAINER_PANEL2_COLLAPSED,flag); //--- Если панель2 должна быть свёрнута if(Panel2Collapsed()) { //--- устанавливаем для панели1 флаг того, что она развёрнута this.SetPanel1Collapsed(false); //--- Если указатель на панель2 получен if(this.GetPanel2()!=NULL) { //--- устанавливаем флаг неотображения панели и скрываем её this.GetPanel2().SetDisplayed(false); this.GetPanel2().Hide(); } //--- Если указатель на панель1 получен if(this.GetPanel1()!=NULL) { //--- устанавливаем флаг отображения панели, отображаем её и выводим на передний план this.GetPanel1().SetDisplayed(true); this.GetPanel1().Show(); this.GetPanel1().BringToTop(); } } } //+------------------------------------------------------------------+
Логика метода аналогична вышерассмотренному, но применяется к панели 2.
Метод, устанавливающий дистанцию разделителя от края:
//+------------------------------------------------------------------+ //| Устанавливает дистанцию разделителя от края | //+------------------------------------------------------------------+ void CSplitContainer::SetSplitterDistance(const int value) { //--- Записываем в свойство объекта переданное в метод значение this.SetProperty(CANV_ELEMENT_PROP_SPLIT_CONTAINER_SPLITTER_DISTANCE,value); //--- В зависимости от ориентации разделителя (вертикально или горизонтально) //--- записываем значения в координаты области управления объекта switch(this.SplitterOrientation()) { case CANV_ELEMENT_SPLITTER_ORIENTATION_VERTICAL : this.SetProperty(CANV_ELEMENT_PROP_CONTROL_AREA_X,this.SplitterDistance()); this.SetProperty(CANV_ELEMENT_PROP_CONTROL_AREA_Y,0); break; //---CANV_ELEMENT_SPLITTER_ORIENTATION_HORISONTAL default: this.SetProperty(CANV_ELEMENT_PROP_CONTROL_AREA_X,0); this.SetProperty(CANV_ELEMENT_PROP_CONTROL_AREA_Y,this.SplitterDistance()); break; } } //+------------------------------------------------------------------+
Так как от расположения разделителя зависит начало координат области управления объекта, то в зависимости от ориентации разделителя записываем в область управления координаты разделителя — он является физическим отображением этой виртуальной области.
Метод, устанавливающий толщину разделителя:
//+------------------------------------------------------------------+ //| Устанавливает толщину разделителя | //+------------------------------------------------------------------+ void CSplitContainer::SetSplitterWidth(const int value) { //--- Записываем в свойство объекта переданное в метод значение this.SetProperty(CANV_ELEMENT_PROP_SPLIT_CONTAINER_SPLITTER_WIDTH,value); //--- В зависимости от ориентации разделителя (вертикально или горизонтально) //--- записываем значения в свойства ширины и высоты области управления объекта switch(this.SplitterOrientation()) { case CANV_ELEMENT_SPLITTER_ORIENTATION_VERTICAL : this.SetProperty(CANV_ELEMENT_PROP_CONTROL_AREA_WIDTH,this.SplitterWidth()); this.SetProperty(CANV_ELEMENT_PROP_CONTROL_AREA_HEIGHT,this.Height()); break; //---CANV_ELEMENT_SPLITTER_ORIENTATION_HORISONTAL default: this.SetProperty(CANV_ELEMENT_PROP_CONTROL_AREA_WIDTH,this.Width()); this.SetProperty(CANV_ELEMENT_PROP_CONTROL_AREA_HEIGHT,this.SplitterWidth()); break; } } //+------------------------------------------------------------------+
Метод аналогичен вышерассмотренному. В зависимости от ориентации разделителя записываем в область управления размеры разделителя, так он физически отображает эту виртуальную область.
Обработчик событий:
//+------------------------------------------------------------------+ //| Обработчик событий | //+------------------------------------------------------------------+ void CSplitContainer::OnChartEvent(const int id,const long &lparam,const double &dparam,const string &sparam) { //--- Корректируем смещение по Y для подокна CGCnvElement::OnChartEvent(id,lparam,dparam,sparam); //--- Если идентификатор события - перемещение разделителя if(id==WF_CONTROL_EVENT_SPLITTER_MOVE) { //--- Получаем указатель на объект-разделитель CSplitter *splitter=this.GetSplitter(); if(splitter==NULL) return; //--- Объявляем переменные для координат разделителя int x=(int)lparam; int y=(int)dparam; //--- В зависимости от ориентации разделителя switch(this.SplitterOrientation()) { //--- вертикальное положение разделителя case CANV_ELEMENT_SPLITTER_ORIENTATION_VERTICAL : //--- Устанавливаем координату Y равной координате Y элемента управления y=this.CoordY(); //--- Корректируем координату X так, чтобы разделитель не выходил за пределы элемента управления //--- с учётом получающейся в итоге минимальной ширины панелей if(x<this.CoordX()+this.Panel1MinSize()) x=this.CoordX()+this.Panel1MinSize(); if(x>this.CoordX()+this.Width()-this.Panel2MinSize()-this.SplitterWidth()) x=this.CoordX()+this.Width()-this.Panel2MinSize()-this.SplitterWidth(); break; //---CANV_ELEMENT_SPLITTER_ORIENTATION_HORISONTAL //--- горизонтальное положение разделителя default: //--- Устанавливаем координату X равной координате X элемента управления x=this.CoordX(); //--- Корректируем координату Y так, чтобы разделитель не выходил за пределы элемента управления //--- с учётом получающейся в итоге минимальной высоты панелей if(y<this.CoordY()+this.Panel1MinSize()) y=this.CoordY()+this.Panel1MinSize(); if(y>this.CoordY()+this.Height()-this.Panel2MinSize()-this.SplitterWidth()) y=this.CoordY()+this.Height()-this.Panel2MinSize()-this.SplitterWidth(); break; } //--- Если разделитель смещён на рассчитанные координаты if(splitter.Move(x,y,true)) { //--- устанавливаем разделителю его относительные координаты splitter.SetCoordXRelative(splitter.CoordX()-this.CoordX()); splitter.SetCoordYRelative(splitter.CoordY()-this.CoordY()); //--- Получаем указатели на обе панели CSplitContainerPanel *p1=this.GetPanel1(); CSplitContainerPanel *p2=this.GetPanel2(); if(p1==NULL || p2==NULL) return; //--- В зависимости от ориентации разделителя устанавливаем его новые координаты this.SetSplitterDistance(!this.SplitterOrientation() ? splitter.CoordX()-this.CoordX() : splitter.CoordY()-this.CoordY()); //--- Устанавливаем новые координаты и размеры панелей в зависимости от координат разделителя if(this.SetsPanelParams()) { //--- Если размеры панели 1 успешно изменены if(p1.Resize(this.m_panel1_w,this.m_panel1_h,true)) { //--- Если координаты панели 2 изменены на новые if(p2.Move(this.CoordX()+this.m_panel2_x,this.CoordY()+this.m_panel2_y,true)) { //--- если размеры панели 2 успешно изменены if(p2.Resize(this.m_panel2_w,this.m_panel2_h,true)) { //--- устанавливаем новые относительные координаты панели 2 p2.SetCoordXRelative(p2.CoordX()-this.CoordX()); p2.SetCoordYRelative(p2.CoordY()-this.CoordY()); } } } } } } } //+------------------------------------------------------------------+
Логика метода полностью расписана в комментариях к коду. Вкратце: обработчик получает идентификатор события "Перемещение разделителя" и рассчитывает новые координаты и размеры панелей. Панель1 всегда остаётся на своих координатах и лишь изменяет размер вслед за перемещением разделителя. А панель 2 кроме изменения размера, должна ещё и смещаться вслед за разделителем — ведь её начальные координаты привязаны к нему. Соответственно, её размер изменяется так, чтобы она всегда оставалась внутри своего контейнера при смещении за разделителем.
Обработчик события Курсор в пределах активной области, кнопки мышки не нажаты:
//+------------------------------------------------------------------+ //| Обработчик события Курсор в пределах активной области, | //| кнопки мышки не нажаты | //+------------------------------------------------------------------+ void CSplitContainer::MouseActiveAreaNotPressedHandler(const int id,const long& lparam,const double& dparam,const string& sparam) { //--- Получаем указатель на разделитель CSplitter *splitter=this.GetSplitter(); if(splitter==NULL) { ::Print(DFUN,CMessage::Text(MSG_ELM_LIST_ERR_FAILED_GET_GRAPH_ELEMENT_OBJ),": ",this.TypeElementDescription(GRAPH_ELEMENT_TYPE_WF_SPLITTER)); return; } //--- Если разделитель не отображается if(!splitter.Displayed()) { //--- Включаем отображение разделителя, показываем и перерисовываем его splitter.SetDisplayed(true); splitter.Show(); splitter.Redraw(true); } } //+------------------------------------------------------------------+
Логика метода расписана в комментариях к коду. Когда мы наводим указатель мышки на область управления, генерируется событие, которое отправляется в обработчик событий мышки объекта-формы класса CForm. Внутри обработчика идёт перенаправление на обработку каждого события в свой виртуальный метод. В объекте-форме все эти методы ничего не делают — их нужно переопределять в наследуемых классах. В данном классе элемента управления SplitContainer такой обработчик получает указатель на объект-разделитель и, если он не отображается (сброшен флаг его отображения), то для него устанавливается флаг отображения, сам объект отображается и перерисовывается.
Немного доработаем класс объекта панели элемента управления SplitContainer.
В файле класса \MQL5\Include\DoEasy\Objects\Graph\WForms\Helpers\SplitContainerPanel.mqh, в публичной секции напишем методы для установки флагов свёрнутости панели и объявим метод для отображения панели и обработчик события "Курсор в пределах активной области, кнопки мышки не нажаты":
//+------------------------------------------------------------------+ //| Класс объекта SplitContainerPanel | //| элемента управления WForms SplitContainer | //+------------------------------------------------------------------+ 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: //--- (1) Устанавливает, (2) возвращает флаг свёрнутости панели void SetCollapsed(const bool flag) { this.SetDisplayed(!flag); } bool Collapsed(void) const { return !this.Displayed(); } //--- Отображает панель virtual void Show(void); //--- Рисует рамку панели 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); //--- Обработчик события Курсор в пределах активной области, кнопки мышки не нажаты virtual void MouseActiveAreaNotPressedHandler(const int id,const long& lparam,const double& dparam,const string& sparam); //--- Конструктор CSplitContainerPanel(const long chart_id, const int subwindow, const string descript, const int x, const int y, const int w, const int h); }; //+------------------------------------------------------------------+
Методы SetCollapsed() и Collapsed() — это противоположность методам SetDisplayed() и Displayed(). Поэтому, они и вызываются внутри объявленных методов, но передаваемый в метод флаг или возвращаемый из метода инвертируются.
В методе, создающем новый графический объект, добавим создание объекта-разделителя:
//+------------------------------------------------------------------+ //| Создаёт новый графический объект | //+------------------------------------------------------------------+ 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; case GRAPH_ELEMENT_TYPE_WF_SPLITTER : element=new CSplitter(this.ChartID(),this.SubWindow(),descript,x,y,w,h); break; default : break; } if(element==NULL) ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_FAILED_CREATE_ELM_OBJ),this.TypeElementDescription(type)); return element; } //+------------------------------------------------------------------+
Понятно, что все вспомогательные объекты сами по себе отдельно не представляют никакой ценности, но все объекты-контейнеры всё же должны уметь создавать все возможные объекты внутри себя. Поэтому мы и прописываем создание в объектах-контейнерах все имеющиеся и новые объекты независимо от того, к какой категории объектов библиотеки они относятся.
Метод, отображающий панель:
//+------------------------------------------------------------------+ //| Отображает панель | //+------------------------------------------------------------------+ void CSplitContainerPanel::Show(void) { //--- Если панель свёрнута - уходим if(this.Collapsed()) return; //--- Отображаем панель и все прикреплённые к ней объекты CForm::Show(); } //+------------------------------------------------------------------+
Здесь сначала проверяем флаг свёрнутости панели, и если панель находится в свёрнутом состоянии, то отображать нечего — уходим. Иначе — отображаем панель при помощи метода родительского класса объекта-формы.
Обработчик события "Курсор в пределах активной области, кнопки мышки не нажаты":
//+------------------------------------------------------------------+ //| Обработчик события Курсор в пределах активной области, | //| кнопки мышки не нажаты | //+------------------------------------------------------------------+ void CSplitContainerPanel::MouseActiveAreaNotPressedHandler(const int id,const long& lparam,const double& dparam,const string& sparam) { //--- Получаем указатель на базовый объект CSplitContainer *base=this.GetBase(); if(base==NULL) return; //--- Из базового объекта получаем указатель на объект-разделитель CSplitter *splitter=base.GetSplitter(); if(splitter==NULL) { ::Print(DFUN,CMessage::Text(MSG_ELM_LIST_ERR_FAILED_GET_GRAPH_ELEMENT_OBJ),": ",this.TypeElementDescription(GRAPH_ELEMENT_TYPE_WF_SPLITTER)); return; } //--- Если разделитель отображается if(splitter.Displayed()) { //--- Выключаем отображение разделителя и скрываем его splitter.SetDisplayed(false); splitter.Hide(); } } //+------------------------------------------------------------------+
Логика метода прокомментирована в коде. Вкратце: когда мы уводим курсор с области управления элемента управления SplitContainer, то курсор сразу же попадает на область первой или второй панели этого элемента управления. Т.е. мы не можем определить в объекте класса CSplitContainer, что курсор вышел за область управления — курсор сразу же попадает на прикреплённый к контейнеру объект-панель. А вот на ней-то и срабатывает событие, что курсор находится над формой или над её активной областью. Поэтому в обработчике события мышки панели нужно получить указатель на разделитель из базового объекта и скрыть полученный разделитель, что здесь и выполняется.
В файле \MQL5\Include\DoEasy\Objects\Graph\WForms\Containers\Container.mqh класса объекта-контейнера, в его методе, устанавливающем параметры присоединённому объекту, добавим блок кода для установки параметров вновь созданного объекта-разделителя:
//+------------------------------------------------------------------+ //| Устанавливает параметры присоединённому объекту | //+------------------------------------------------------------------+ 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-объекта "Splitter" case GRAPH_ELEMENT_TYPE_WF_SPLITTER : obj.SetBackgroundColor(colour==clrNONE ? CLR_CANV_NULL : colour,true); obj.SetBorderColor(CLR_CANV_NULL,true); obj.SetForeColor(CLR_DEF_FORE_COLOR,true); obj.SetOpacity(0); obj.SetDisplayed(false); obj.Hide(); break; //--- Для WinForms-объекта"ArrowButton" case GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON : case GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_UP : case GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_DOWN : case GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_LEFT : case GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_RIGHT : obj.SetBorderColor(CLR_DEF_CONTROL_TAB_HEAD_BORDER_COLOR,true); obj.SetBorderStyle(FRAME_STYLE_SIMPLE); break; default: break; } obj.Crop(); } //+------------------------------------------------------------------+
Для вновь созданного объекта-разделителя устанавливаем прозрачный цвет фона (если в метод передан цвет clrNONE), прозрачный цвет рамки, устанавливаем полную прозрачность объекта, флаг неотображения и скрываем созданный объект — изначально объект-разделитель не должен быть видимым.
В файле \MQL5\Include\DoEasy\Collections\GraphElementsCollection.mqh класса-коллекции графических элементов, в его обработчик событий добавим блок кода для обработки перемещения объекта-разделителя:
//--- Если событие перемещения мышки if(id==CHARTEVENT_MOUSE_MOVE) { //--- Если курсор над формой if(form!=NULL) { //--- Если стоит флаг перемещения if(move) { //--- рассчитываем смещение курсора относительно начала координат формы int x=this.m_mouse.CoordX()-form.OffsetX(); int y=this.m_mouse.CoordY()-form.OffsetY(); //--- получаем ширину и высоту графика, на котором находится форма int chart_width=(int)::ChartGetInteger(form.ChartID(),CHART_WIDTH_IN_PIXELS,form.SubWindow()); int chart_height=(int)::ChartGetInteger(form.ChartID(),CHART_HEIGHT_IN_PIXELS,form.SubWindow()); //--- Если форма не в составе расширенного стандартного графического объекта if(form_index==WRONG_VALUE) { //--- Если форма - объект-разделитель if(form.TypeGraphElement()==GRAPH_ELEMENT_TYPE_WF_SPLITTER) { //--- получаем его базовый объект CWinFormBase *base=form.GetBase(); if(base==NULL) return; //--- и отправляем событие "Перемещение разделителя" в обработчик событий базового объекта const long lp=x; const double dp=y; base.OnChartEvent(WF_CONTROL_EVENT_SPLITTER_MOVE,lp,dp,sparam); } //--- Корректируем рассчитанные координаты для формы в случае выхода формы за пределы графика if(x<0) x=0; if(x>chart_width-form.Width()) x=chart_width-form.Width(); if(y<0) y=0; if(y>chart_height-form.Height()) y=chart_height-form.Height(); //--- Если на графике нет панели торговли в один клик if(!::ChartGetInteger(form.ChartID(),CHART_SHOW_ONE_CLICK)) { //--- рассчитаем координаты формы так, чтобы при её перемещении на кнопку включения панели торговли в один клик, форма под неё не попадала
В методе, внутри блока обработки перемещения графического объекта проверяем тип перемещаемого объекта, и если это объект-разделитель, то вызываем его обработчик событий, отправляя в него событие WF_CONTROL_EVENT_SPLITTER_MOVE. Внутри же обработчика события этого графического элемента это событие обрабатывается так, как было рассмотрено нами выше.
На сегодня это все изменения и доработки библиотеки.
Протестируем что у нас вышло.
Тестирование
Для теста возьмём советник из прошлой статьи и сохраним его в новой папке \MQL5\Experts\TestDoEasy\Part121\ под новым именем TestDoEasy121.mq5.
Всё, что требуется поменять в советнике — это координаты и размеры текстовых меток на панелях элемента управления SplitContainer:
//--- На каждой из панелей элемента ... 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,3,3,panel.Width()-6,panel.Height()-6,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)); } }
Почему так? Просто, если размеры текстовых меток будут совпадать с размерами панелей, на которых они созданы, то при уводе курсора с области управления, курсор не попадёт на область панели, а попадёт сразу на область текстовой метки. Таким образом объект-разделитель не получится скрыть. Это недоработка, и она требует решения. Это мы решим, но по мере развития элемента управления SplitContainer.
Скомпилируем советник и запустим его на графике:
В принципе, если не обращать внимания на явные задержки с перерисовкой, то работает пока удовлетворительно. К сожалению, на моём маломощном старом ноутбуке, я не могу проверить откуда такие фризы. То ли это перегруженный процессами ноутбук не может в полной мере плавно отображать изменения координат и размеров панелей, то ли где-то в коде нужно будет далее оптимизировать. Но, как я заметил, такие фризы далеко не всегда у меня проявляются. Но в любом случае код библиотеки будет подвергаться оптимизации после окончания её разработки. Что ещё заметил: не всегда надёжно срабатывает отображение/скрытие объекта-разделителя. Это тоже решим по мере разработки элемента управления.
Что дальше
В следующей статье продолжим разработку элемента управления SplitContainer и начнём делать функционал для изменения параметров уже созданного элемента управления.
*Статьи этой серии:
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-объектов
DoEasy. Элементы управления (Часть 20): WinForms-объект SplitContainer
- Бесплатные приложения для трейдинга
- 8 000+ сигналов для копирования
- Экономические новости для анализа финансовых рынков
Вы принимаете политику сайта и условия использования
Посмотрите исходник изменения размера канваса, там лишнее есть, пару тактов можно освободить.
Спасибо. Всё будет делаться вкупе с оптимизацией остального кода библиотеки уже на стадии завершения её разработки.
Indicators\\DoEasy\\EventControl.ex5, открыт был у меня на 10 чартах по 4-5 раз на чарте после использования кода из статьи
Только сегодня заметил лишние индикаторы, и вчера комп завис пришлось перегрузить, скорее всего в коде, баг
Indicators\\DoEasy\\EventControl.ex5, открыт был у меня на 10 чартах по 4-5 раз на чарте после использования кода из статьи
Только сегодня заметил лишние индикаторы, и вчера комп завис пришлось перегрузить, скорее всего в коде, баг
Спасибо. Поищу причину.