English 中文 Español Deutsch 日本語 Português
Графические интерфейсы I: Форма для элементов управления (Глава 2)

Графические интерфейсы I: Форма для элементов управления (Глава 2)

MetaTrader 5Примеры | 16 декабря 2015, 14:32
10 371 7
Anatoli Kazharski
Anatoli Kazharski

Содержание

 

Введение

Эта статья является продолжением первой части серии о графических интерфейсах. Более подробно, для чего предназначена эта библиотека, можно прочитать в самой первой статье: Графические интерфейсы I: Подготовка структуры библиотеки (Глава 1). Полный список ссылок на статьи первой части находится в конце каждой главы и там же есть возможность загрузить к себе на компьютер полную версию библиотеки на текущей стадии разработки. Файлы нужно разместить по тем же директориям, как они расположены в архиве.

В предыдущей главе мы рассматривали структуру библиотеки для создания графических интерфейсов. Были созданы (1) производные классы для объектов-примитивов, (2) базовый класс для всех элементов управления, (3) основные классы, предназначенные для хранения указателей на элементы и управления этими элементами в общем обработчике событий.

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

 

Класс формы для элементов управления

Напомню, что ранее, в описании класса CElement, уже рассказывалось о некоторых виртуальных методах, которые будут уникальными для каждого элемента управления. Сразу поместим их дубликаты в класс CWindow и в каждом последующем описании любого другого элемента управления будем делать то же самое. Код этих методов будем рассматривать после того, как создадим методы для построения формы (окна) для элементов управления.

//+------------------------------------------------------------------+
//|                                                       Window.mqh |
//|                        Copyright 2015, MetaQuotes Software Corp. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
#include "Element.mqh"
//+------------------------------------------------------------------+
//| Класс создания формы для элементов управления                    |
//+------------------------------------------------------------------+
class CWindow : public CElement
  {
public:
                     CWindow(void);
                    ~CWindow(void);
   //--- Обработчик событий графика
   virtual void      OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam);
   //--- Таймер
   virtual void      OnEventTimer(void);
   //--- Перемещение элемента
   virtual void      Moving(const int x,const int y);
   //--- Показ, скрытие, сброс, удаление
   virtual void      Show(void);
   virtual void      Hide(void);
   virtual void      Reset(void);
   virtual void      Delete(void);
   //--- Установка, сброс приоритетов на нажатие левой кнопки мыши
   virtual void      SetZorders(void);
   virtual void      ResetZorders(void);
  };
//+------------------------------------------------------------------+
//| Constructor                                                      |
//+------------------------------------------------------------------+
CWindow::CWindow(void)
  {
  }
//+------------------------------------------------------------------+
//| Destructor                                                       |
//+------------------------------------------------------------------+
CWindow::~CWindow(void)
  {
  }
//+------------------------------------------------------------------+

В классе CWindow, а также в некоторых других классах, будет применяться множество различных режимов для тех или иных действий. Списки идентификаторов именованных констант для всех режимов и типов данных будем хранить в отдельном файле Enums.mqh. Подключим его к файлу Objects.mqh, чтобы все перечисления режимов и типов были доступны во всех файлах библиотеки:

//+------------------------------------------------------------------+
//|                                                      Objects.mqh |
//|                        Copyright 2015, MetaQuotes Software Corp. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
#include "Enums.mqh"
#include "Defines.mqh"
#include <ChartObjects\ChartObjectsBmpControls.mqh>
#include <ChartObjects\ChartObjectsTxtControls.mqh>

Из каких частей состоит окно, которое мы собираемся создать?

  1. Фон. В этой области будут размещаться все элементы управления.
  2. Заголовок. Эта часть служит для перемещения окна, а также содержит в себе элементы интерфейса, которые перечислены в пунктах ниже.
  3. Ярлык. Дополнительный атрибут визуальной идентификации.
  4. Надпись. Название окна.
  5. Кнопка «Всплывающие подсказки». Если нажата, то включен режим показа всплывающих подсказок элементов управления, в которых они есть.
  6. Кнопка для сворачивания/разворачивания окна.
  7. Кнопка для закрытия окна.

Рис. 1. Составные части формы для элементов управления

Рис. 1. Составные части формы для элементов управления

В библиотеке для создания графических интерфейсов, которую мы сейчас разрабатываем, будет реализован многооконный режим. Приложение может состоять и из одного окна. Но иногда, если в программе очень много опций и нет возможности разместить их все в рамках одной формы, возникает необходимость еще одной формы. На ней могут быть размещены какие-то общие настройки приложения, это может быть справочное руководство или окно для открытия/сохранения файла и даже палитра для выбора цвета. В общем, может возникнуть множество различных идей для создания дополнительных окон. Обычно их называют «диалоговые окна».

В классе CWindow нужно иметь возможность определять тип окна, то есть, какое окно является главным окном приложения, а какое — диалоговым. Возможно, в процессе разработки появятся еще какие-нибудь идеи с новыми типами окон, но сейчас в файле Enums.mqh создадим перечисление, содержащее в себе только два типа для (1) основного и (2) диалогового окон:

//+------------------------------------------------------------------+
//| Перечисление типов окон                                          |
//+------------------------------------------------------------------+
enum ENUM_WINDOW_TYPE
  {
   W_MAIN   =0,
   W_DIALOG =1
  };

Фон и заголовок создадим из объекта-примитива «Прямоугольная метка». У нас сейчас есть для этого класс CRectLabel. Для надписи будем использовать класс CLabel, с помощью которого можно создать объект «Текстовая метка». Для ярлыка окна и перечисленных ранее кнопок нужен объект «Графическая метка» и для нее уже готов класс CBmpLabel.

Если файл Element.mqh уже подключен, то все эти классы, определенные в файле Object.mqh, доступны для использования. Создадим их экземпляры для каждого элемента окна в теле класса CWindow, а также все необходимые методы для создания окна:

class CWindow : public CElement
  {
private:
   //--- Объекты для создания формы
   CRectLabel        m_bg;
   CRectLabel        m_caption_bg;
   CBmpLabel         m_icon;
   CLabel            m_label;
   CBmpLabel         m_button_tooltip;
   CBmpLabel         m_button_unroll;
   CBmpLabel         m_button_rollup;
   CBmpLabel         m_button_close;
   //---
public:
   //--- Методы для создания окна
   bool              CreateWindow(const long chart_id,const int window,const string caption_text,const int x,const int y);
   //---
private:
   bool              CreateBackground(void);
   bool              CreateCaption(void);
   bool              CreateIcon(void);
   bool              CreateLabel(void);
   bool              CreateButtonClose(void);
   bool              CreateButtonRollUp(void);
   bool              CreateButtonUnroll(void);
   bool              CreateButtonTooltip(void);
  };

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

Подключим файл Window.mqh с классом CWindow к файлу WndContainer.mqh с классом CWndContainer. Сюда же будем подключать и все остальные классы с элементами управления.

//+------------------------------------------------------------------+
//|                                                 WndContainer.mqh |
//|                        Copyright 2015, MetaQuotes Software Corp. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
#include "Window.mqh"
// ...
// Сюда же будем подключать и все остальные классы с элементами управления
// ...

Таким образом, они станут доступными для использования в классе CProgram, то есть в том классе, где будет создаваться интерфейс MQL-приложения, а в классах CWndContainer и CWndEvents будет возможность создавать и использовать члены класса с этими типами данных. Например, если сейчас после подключения файла Window.mqh создать динамический массив указателей типа CWindow (он нам понадобится для работы), то не возникнет никаких проблем, то есть сообщений об ошибках не будет.

//+------------------------------------------------------------------+
//|                                                 WndContainer.mqh |
//|                        Copyright 2015, MetaQuotes Software Corp. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
#include "Window.mqh"
//+------------------------------------------------------------------+
//| Класс для хранения всех объектов интерфейса                      |
//+------------------------------------------------------------------+
class CWndContainer
  {
protected:
   //--- Массив окон
   CWindow          *m_windows[];
  };

Но если бы файл Window.mqh не был подключен, то при создании члена класса с типом CWindow и попытке компилировать файл вы получили бы сообщение об ошибке, как на скриншоте ниже:

Рис. 2. Сообщение при компиляции об отсутствии указываемого типа

Рис. 2. Сообщение при компиляции об отсутствии указываемого типа

Кроме динамического массива окон нам понадобится структура динамических массивов указателей на все элементы управления (CElement) и массивов всех объектов (CChartObject), из которых они состоят. В качестве экземпляра этой структуры создадим динамический массив, который по размеру будет равен массиву окон (m_window[]). В итоге получится массив массивов указателей на элементы управления для каждой формы (см. листинг кода ниже).

Кстати, массив m_objects[] объявлен с типом CChartObject. При компиляции не возникнет ошибок, так как этот тип объектов уже присутствует в структуре разрабатываемой библиотеки и подключен к файлу Objects.mqh.

class CWndContainer
  {
protected:
   //--- Структура массивов элементов
   struct WindowElements
     {
      //--- Общий массив всех объектов
      CChartObject     *m_objects[];
      //--- Общий массив всех элементов
      CElement         *m_elements[];
     };
   //--- Массив массивов элементов для каждого окна
   WindowElements    m_wnd[];
  };

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

Для получения размеров массивов нужны соответствующие методы. Количество объектов всех элементов и количество элементов для каждого окна можно будет получить, передав в качестве параметра индекс окна (window_index).

class CWndContainer
  {
public:
   //--- Количество окон в интерфейсе
   int               WindowsTotal(void) { return(::ArraySize(m_windows)); }
   //--- Количество объектов всех элементов
   int               ObjectsElementsTotal(const int window_index);
   //--- Количество элементов
   int               ElementsTotal(const int window_index);
  };

Обязательно сделаем проверку на выход за пределы размера массива. Часто повторяющиеся фразы можно написать в виде макроса, добавив в начале предопределенную макроподстановку, содержащую имя функции (__FUNCTION__). Напишем такой макрос (PREVENTING_OUT_OF_RANGE) и добавим его в файл Defines.mqh:

//+------------------------------------------------------------------+
//|                                                      Defines.mqh |
//|                        Copyright 2015, MetaQuotes Software Corp. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
//--- Предотвращение выхода из диапазона
#define PREVENTING_OUT_OF_RANGE __FUNCTION__," > Предотвращение выхода за пределы массива."

Теперь его будет удобно использовать во всех функциях, где производится проверка на выход за пределы массива:

//+------------------------------------------------------------------+
//| Возвращает кол-во объектов по указанному индексу окна            |
//+------------------------------------------------------------------+
int CWndContainer::ObjectsElementsTotal(const int window_index)
  {
   if(window_index>=::ArraySize(m_wnd))
     {
      ::Print(PREVENTING_OUT_OF_RANGE);
      return(WRONG_VALUE);
     }
//---
   return(::ArraySize(m_wnd[window_index].m_objects));
  }
//+------------------------------------------------------------------+
//| Возвращает кол-во элементов по указанному индексу окна           |
//+------------------------------------------------------------------+
int CWndContainer::ElementsTotal(const int window_index)
  {
   if(window_index>=::ArraySize(m_wnd))
     {
      ::Print(PREVENTING_OUT_OF_RANGE);
      return(WRONG_VALUE);
     }
//---
   return(::ArraySize(m_wnd[window_index].m_elements));
  }

Представим себя сейчас пользователем этой библиотеки и переместимся в класс CProgram для эксперта, которого уже создали ранее для тестов. Допустим, нужно сделать торговую панель, поэтому создадим в классе CProgram метод CreateTradePanel(). Не забывайте о том, что для экономии пространства статьи мы дополняем то, что уже было сделано до этого, а по мере продвижения в статье приводим лишь то, что добавляем в тот или иной класс.

class CProgram : public CWndEvents
  {
public:
   //--- Создает торговую панель
   bool              CreateTradePanel(void);
  };
//+------------------------------------------------------------------+
//| Создает торговую панель                                          |
//+------------------------------------------------------------------+
bool CProgram::CreateTradePanel(void)
  {
//--- Создание формы для элементов управления
// ...
//--- Создание элементов управления
// ...
//---
   ::ChartRedraw();
   return(true);
  }

Тело этого метода на текущий момент пусто, но уже скоро его пространство будет заполнено нужным функционалом. Из комментариев в теле метода ясно, что в первую очередь нужно создать окно для элементов управления. Нам нужен метод, в котором мы будем указывать свойства создаваемого окна. Так как до этого мы уже подключили файл Window.mqh к файлу WndContainer.mqh, то сейчас в классе CProgram доступен класс CWindow. Поэтому далее создадим экземпляр этого класса, а также метод создания окна для элементов управления CreateWindow():

class CProgram : public CWndEvents
  {
private:
   //--- Форма для элементов управления
   CWindow           m_window;
   //---
private:
   //--- Создание формы
   bool              CreateWindow(const string caption_text);
  };

Мы подошли к моменту, когда нужно ответить на вопросы:

  1. Как указатели элементов управления и их объектов будут попадать в массивы, которые мы создали ранее в классе CWndContainer?
  2. Каким образом будет определяться идентификатор каждого элемента интерфейса?

Поскольку создание интерфейса требует определенной (строгой) последовательности, нужно реализовать процесс разработки интерфейса таким образом, чтобы разработчик при случайном несоблюдении последовательности не попал в ситуацию, когда он не понимает, что делать дальше. Поэтому каждое действие пользователя библиотеки будет контролироваться «движком» библиотеки. Сделаем так, чтобы невозможно было создать форму для элементов управления, пока указатель на нее не попал в базу указателей в классе CWndContainer. Элемент управления не получится создать до тех пор, пока в его классе не будет сохранен указатель на форму, к которой он присоединяется.

В классе CWndContainer создадим метод AddWindow() для добавления указателя окна в базу элементов интерфейса. Здесь же будет производиться (1) сохранение указателя в массиве элементов, (2) сохранение указателей объектов элемента в общем массиве указателей объектов, для чего нам понадобится метод AddToObjectsArray(), а также будет (3) устанавливаться идентификатор. Кроме этого, нужно (4) запомнить последний идентификатор в свойствах окна, так как он понадобится для определения идентификаторов для каждого элемента управления внутри их классов, ведь, как уже упоминалось в предыдущем абзаце, в каждом элементе управления будет доступен указатель на окно, к которому он будет присоединен, поэтому это будет возможно.

Начнем с создания методов LastId() в классе CWindow для сохранения и получения идентификатора последнего созданного элемента интерфейса:

class CWindow : public CElement
  {
private:
   //--- Идентификатор последнего элемента управления
   int               m_last_id;
   //---
public:
   //--- Методы для сохранения и получения id последнего созданного элемента
   void              LastId(const int id)                                    { m_last_id=id;                       }
   int               LastId(void)                                      const { return(m_last_id);                  }
  };

Далее создадим остальные методы в классе CWndContainer, которые будут использоваться в методе AddWindow(). У каждого элемента управления будет уникальное имя класса, поэтому метод AddToObjectsArray() будет шаблонным (template), так как в него будут передаваться объекты разных элементов управления. В этом методе будем в цикле проходить по массиву объектов элемента управления, по очереди добавляя указатель каждого в массив базы. Для этого нам понадобится еще один метод. Назовем его AddToArray(). В этот метод будут передаваться объекты одного типа (CChartObject), поэтому нет необходимости делать его шаблонным.

class CWndContainer
  {
protected:
   //--- Добавление указателей объектов элемента в общий массив
   template<typename T>
   void              AddToObjectsArray(const int window_index,T &object);
   //--- Добавляет указатель объекта в массив
   void              AddToArray(const int window_index,CChartObject &object);
  };
//+------------------------------------------------------------------+
//| Добавляет указатели объектов элемента в общий массив             |
//+------------------------------------------------------------------+
template<typename T>
void CWndContainer::AddToObjectsArray(const int window_index,T &object)
  {
   int total=object.ObjectsElementTotal();
   for(int i=0; i<total; i++)
      AddToArray(window_index,object.Object(i));
  }
//+------------------------------------------------------------------+
//| Добавляет указатель объекта в массив                             |
//+------------------------------------------------------------------+
void CWndContainer::AddToArray(const int window_index,CChartObject &object)
  {
   int size=::ArraySize(m_wnd[window_index].m_objects);
   ::ArrayResize(m_wnd[window_index].m_objects,size+1);
   m_wnd[window_index].m_objects[size]=::GetPointer(object);
  }

Теперь можно создать метод AddWindow(). Здесь нам еще понадобится счетчик элементов (m_counter_element_id). Значение этой переменной нужно увеличивать каждый раз после добавления очередного элемента управления в базу.

class CWndContainer
  {
private:
   //--- Счетчик элементов
   int               m_counter_element_id;
   //---
protected:
   //--- Добавляет указатель окна в базу элементов интерфейса
   void              AddWindow(CWindow &object);
  };
//+------------------------------------------------------------------+
//| Добавляет указатель окна в базу элементов интерфейса             |
//+------------------------------------------------------------------+
void CWndContainer::AddWindow(CWindow &object)
  {
   int windows_total=::ArraySize(m_windows);
//--- Если окон нет, обнулим счетчик элементов
   if(windows_total<1)
      m_counter_element_id=0;
//--- Добавим указатель в массив окон
   ::ArrayResize(m_wnd,windows_total+1);
   ::ArrayResize(m_windows,windows_total+1);
   m_windows[windows_total]=::GetPointer(object);
//--- Добавим указатель в общий массив элементов
   int elements_total=::ArraySize(m_wnd[windows_total].m_elements);
   ::ArrayResize(m_wnd[windows_total].m_elements,elements_total+1);
   m_wnd[windows_total].m_elements[elements_total]=::GetPointer(object);
//--- Добавим объекты элемента в общий массив объектов
   AddToObjectsArray(windows_total,object);
//--- Установим идентификатор и запомним id последнего элемента
   m_windows[windows_total].Id(m_counter_element_id);
   m_windows[windows_total].LastId(m_counter_element_id);
//--- Увеличим счетчик идентификаторов элементов
   m_counter_element_id++;
  }

Теперь, каждый раз создавая новое окно в пользовательском классе разработчика MQL-приложения (в статье это CProgram), нужно добавлять его в базу, используя метод AddWindow().

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

  1. Методы для установки и получения статуса окна (свернуто/развернуто).
  2. Методы для установки и получения типа окна (главное/диалоговое).
  3. Методы для установки режима сворачивания окна с учетом того, какого типа MQL-программа создается (эксперт, индикатор). Для этого понадобятся методы, которые позволят управлять размером окна, если это индикатор, находящийся не в главном окне графика.
  4. Методы для установки пользователем цвета каждого объекта окна.
  5. Метод для установки ярлыка окна.
  6. Метод для определения ярлыка окна по умолчанию, если пользователь не предоставил свой вариант.
  7. Определение границ области захвата в заголовке окна.
  8. Константы для отступов кнопок от правого края окна.
  9. Отображение кнопки для включения режима показа всплывающих подсказок.
  10. Переменные приоритетов на нажатие левой кнопкой мыши для каждого объекта окна.

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

//+------------------------------------------------------------------+
//|                                                       Window.mqh |
//|                        Copyright 2015, MetaQuotes Software Corp. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
#include "Element.mqh"
//--- Отступы для кнопок от правого края окна
#define CLOSE_BUTTON_OFFSET   (20)
#define ROLL_BUTTON_OFFSET    (36)
#define TOOLTIP_BUTTON_OFFSET (53)

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

//+------------------------------------------------------------------+
//| Класс создания формы для элементов управления                    |
//+------------------------------------------------------------------+
class CWindow : public CElement
  {
private:
   //--- Идентификатор последнего элемента управления
   int               m_last_id;
   //--- Статус свернутого окна
   bool              m_is_minimized;
   //--- Тип окна
   ENUM_WINDOW_TYPE  m_window_type;
   //--- Режим фиксированной высоты подокна (для индикаторов)
   bool              m_height_subwindow_mode;
   //--- Режим сворачивания формы в подокне индикатора
   bool              m_rollup_subwindow_mode;
   //--- Высота подокна индикатора
   int               m_subwindow_height;
   //--- Свойства фона
   int               m_bg_zorder;
   color             m_bg_color;
   int               m_bg_full_height;
   //--- Свойства заголовка
   int               m_caption_zorder;
   string            m_caption_text;
   int               m_caption_height;
   color             m_caption_bg_color;
   color             m_caption_bg_color_hover;
   color             m_caption_bg_color_off;
   color             m_caption_color_bg_array[];
   //--- Свойства кнопок
   int               m_button_zorder;
   //--- Цвет рамок формы (фона, заголовка)
   color             m_border_color;
   //--- Ярлык формы
   string            m_icon_file;
   //--- Наличие кнопки для режима показа всплывающих подсказок
   bool              m_tooltips_button;
   //--- Для определения границ области захвата в заголовке окна
   int               m_right_limit;
   //---
public:
   //--- Тип окна
   ENUM_WINDOW_TYPE  WindowType(void)                                  const { return(m_window_type);              }
   void              WindowType(const ENUM_WINDOW_TYPE flag)                 { m_window_type=flag;                 }
   //--- Ярлык по умолчанию
   string            DefaultIcon(void);
   //--- (1) пользовательский ярлык окна, (2) использовать кнопку подсказок, (3) ограничение области захвата заголовка
   void              IconFile(const string file_path)                        { m_icon_file=file_path;              }
   void              UseTooltipsButton(void)                                 { m_tooltips_button=true;             }
   void              RightLimit(const int value)                             { m_right_limit=value;                }
   //--- Статус свернутого окна
   bool              IsMinimized(void)                                 const { return(m_is_minimized);             }
   void              IsMinimized(const bool flag)                            { m_is_minimized=flag;                }
   //--- Свойства заголовка
   void              CaptionText(const string text);
   string            CaptionText(void)                                 const { return(m_caption_text);             }
   void              CaptionHeight(const int height)                         { m_caption_height=height;            }
   int               CaptionHeight(void)                               const { return(m_caption_height);           }
   void              CaptionBgColor(const color clr)                         { m_caption_bg_color=clr;             }
   color             CaptionBgColor(void)                              const { return(m_caption_bg_color);         }
   void              CaptionBgColorHover(const color clr)                    { m_caption_bg_color_hover=clr;       }
   color             CaptionBgColorHover(void)                         const { return(m_caption_bg_color_hover);   }
   void              CaptionBgColorOff(const color clr)                      { m_caption_bg_color_off=clr;         }
   //--- Свойства окна
   void              WindowBgColor(const color clr)                          { m_bg_color=clr;                     }
   color             WindowBgColor(void)                                     { return(m_bg_color);                 }
   void              WindowBorderColor(const color clr)                      { m_border_color=clr;                 }
   color             WindowBorderColor(void)                                 { return(m_border_color);             }
   //--- Режимы подокна индикатора
   void              RollUpSubwindowMode(const bool flag,const bool height_mode);
   //--- Изменение высоты подокна индикатора
   void              ChangeSubwindowHeight(const int height);
  };

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

//+------------------------------------------------------------------+
//| Constructor                                                      |
//+------------------------------------------------------------------+
CWindow::CWindow(void) : m_last_id(0),
                         m_subwindow_height(0),
                         m_rollup_subwindow_mode(false),
                         m_height_subwindow_mode(false),
                         m_is_minimized(false),
                         m_tooltips_button(false),
                         m_window_type(W_MAIN),
                         m_icon_file(""),
                         m_right_limit(20),
                         m_caption_height(20),
                         m_caption_bg_color(C'88,157,255'),
                         m_caption_bg_color_off(clrSilver),
                         m_caption_bg_color_hover(C'118,177,255'),
                         m_bg_color(C'15,15,15'),
                         m_border_color(clrLightGray)

  {
//--- Сохраним имя класса элемента в базовом классе
   CElement::ClassName(CLASS_NAME);
//--- Установим строгую последовательность приоритетов
   m_bg_zorder      =0;
   m_caption_zorder =1;
   m_button_zorder  =2;
  }

Реализации тех методов, которые в листинге выше выделены желтым цветом, представлены ниже. Если в классе нужны переменные и методы из базового класса, то лучше их использовать через двойное двоеточие, указывая вначале имя класса (ClassName::), где они объявлены. Так код становится намного понятнее для изучения и быстрее вспоминаешь, что и откуда, если долго не заглядывал в код. Значение переменной m_subwindow_height будет определяться автоматически при создании окна.

//+------------------------------------------------------------------+
//| Режим сворачивания подокна индикатора                            |
//+------------------------------------------------------------------+
void CWindow::RollUpSubwindowMode(const bool rollup_mode=false,const bool height_mode=false)
  {
   if(CElement::m_program_type!=PROGRAM_INDICATOR)
      return;
//---
   m_rollup_subwindow_mode =rollup_mode;
   m_height_subwindow_mode =height_mode;
//---
   if(m_height_subwindow_mode)
      ChangeSubwindowHeight(m_subwindow_height);
  }

К методу CWindow::RollUpSubwindowMode() еще вернемся в одной из следующих глав, когда будем рассматривать практические примеры на графике в терминале. Так будет понятнее, что из себя представляет каждый из предложенных режимов.

Метод CWindow::ChangeSubwindowHeight() позволяет изменить высоту подокна индикатора, если программа настроена на такой режим:

//+------------------------------------------------------------------+
//| Изменяет высоту подокна индикатора                               |
//+------------------------------------------------------------------+
void CWindow::ChangeSubwindowHeight(const int height)
  {
   if(CElement::m_subwin<1 || CElement::m_program_type!=PROGRAM_INDICATOR)
      return;
//---
   if(height>0)
      ::IndicatorSetInteger(INDICATOR_HEIGHT,height);
  }

 

Методы для создания формы

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

  1. Фон.
  2. Заголовок.
  3. Элементы заголовка.

В методе CWindow::CreateWindow(), в котором будут создаваться все эти части окна, в самом начале поставим проверку на то, был ли указатель этого окна сохранен в базе элементов. Если этого не было сделано, то идентификатор окна будет со значением WRONG_VALUE. В таком случае окно не будет создано и функция вернет false, выведя перед этим сообщение с подсказкой в журнал о том, что нужно сделать. Если все в порядке, то далее производится инициализация переменных текущего и базового классов значениями переданных параметров. В числе этих переменных будут:

  • Идентификатор графика.
  • Номер окна графика.
  • Название окна (текст в заголовке).
  • Координаты для установки окна (x, y).
  • Дублирующая переменная высоты формы (m_bg_full_height). Будет использоваться для изменения размеров формы. Высота формы устанавливается до применения метода создания формы (примеры будут немного позже).

Идентификатор графика и номер окна графика определяются в конструкторе класса CWndEvents. При создании элементов управления в пользовательском классе (CWndEvents::CProgram) в методы их создания передаются именно эти значения.

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

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

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

//+------------------------------------------------------------------+
//| Создает форму для элементов управления                           |
//+------------------------------------------------------------------+
bool CWindow::CreateWindow(const long chart_id,const int subwin,const string caption_text,const int x,const int y)
  {
   if(CElement::Id()==WRONG_VALUE)
     {
      ::Print(__FUNCTION__," > Перед созданием окна указатель окна нужно передать "
            "в массив окон с помощью метода CWndContainer::AddWindow(CWindow &object).");
      return(false);
     }
//--- Инициализация переменных
   m_chart_id       =chart_id;
   m_subwin         =subwin;
   m_caption_text   =caption_text;
   m_x              =x;
   m_y              =y;
   m_bg_full_height =m_y_size;
//--- Создание всех объектов окна
   if(!CreateBackground())
      return(false);
   if(!CreateCaption())
      return(false);
   if(!CreateLabel())
      return(false);
   if(!CreateIcon())
      return(false);
   if(!CreateButtonClose())
      return(false);
   if(!CreateButtonRollUp())
      return(false);
   if(!CreateButtonUnroll())
      return(false);
   if(!CreateButtonTooltip())
      return(false);
//--- Если эта программа индикатор
   if(CElement::ProgramType()==PROGRAM_INDICATOR)
     {
      //--- Если установлен режим фиксированной высоты подокна
      if(m_height_subwindow_mode)
        {
         m_subwindow_height=m_bg_full_height+3;
         ChangeSubwindowHeight(m_subwindow_height);
        }
     }
//--- Спрятать окно, если оно диалоговое
   if(m_window_type==W_DIALOG)
      Hide();
//---
   return(true);
  }

Для скрытия объектов элемента используется виртуальная функция Hide(). В коде выше она выделена зеленым маркером. Ранее в классе CWindow было показано лишь ее объявление. В ее коде все объекты окна скрываются на всех таймфреймах, а переменная m_is_visible базового класса получает статус false.

//+------------------------------------------------------------------+
//| Скрывает окно                                                    |
//+------------------------------------------------------------------+
void CWindow::Hide(void)
  {
//--- Скрыть все объекты
   for(int i=0; i<ObjectsElementTotal(); i++)
      Object(i).Timeframes(OBJ_NO_PERIODS);
//--- Состояние видимости
   CElement::IsVisible(false);
  }

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

Рис. 3. Сообщения об отсутствии реализации методов

Рис. 3. Сообщения об отсутствии реализации методов

Начнем с фона, метод CWindow::CreateBackground(). В начале каждого метода, относящегося к созданию объекта, будем формировать имя для этого объекта. Оно будет состоять из нескольких частей:

  • Имя программы.
  • Принадлежность к элементу (“window”).
  • Принадлежность к части элемента (“bg”).
  • Идентификатор элемента.

Каждая часть будет разделяться знаком подчеркивания «_». Имя программы сохранено в базовом классе CElement в списке инициализации конструктора. Идентификатор элемента получен в момент добавления окна в базу элементов.

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

После корректировки размера фона производится установка объекта, затем установка свойств, и в самом конце метода сохраняется указатель на объект в массиве базового класса. Весь код функции CWindow::CreateBackground() представлен ниже:

//+------------------------------------------------------------------+
//| Создает фон окна                                                 |
//+------------------------------------------------------------------+
bool CWindow::CreateBackground(void)
  {
//--- Формирование имени объекта
   string name=CElement::ProgramName()+"_window_bg_"+(string)CElement::Id();
//--- Размер окна зависит от состояния (свернуто/развернуто)
   int y_size=0;
   if(m_is_minimized)
     {
      y_size=m_caption_height;
      CElement::YSize(m_caption_height);
     }
   else
     {
      y_size=m_bg_full_height;
      CElement::YSize(m_bg_full_height);
     }
//--- Установим фон окна
   if(!m_bg.Create(m_chart_id,name,m_subwin,m_x,m_y,m_x_size,y_size))
      return(false);
//--- Установим свойства
   m_bg.BackColor(m_bg_color);
   m_bg.Color(m_border_color);
   m_bg.BorderType(BORDER_FLAT);
   m_bg.Corner(m_corner);
   m_bg.Selectable(false);
   m_bg.Z_Order(m_bg_zorder);
   m_bg.Tooltip("\n");
//--- Сохраним указатель объекта
   CElement::AddToArray(m_bg);
   return(true);
  }

В методе создания заголовка окна, кроме основных действий, подобно тому, как это реализовано в методе создания фона окна, нужно еще сохранить координаты и размеры в классе объекта-примитива CRectLabel, который, напомню, находится в файле Objects.mqh. Значения этих полей будут обновляться при перемещении окна (координаты), а также при изменении его размеров. Если значения будут статичны, то не получится отслеживать курсор над заголовком. Это же правило будет действовать для всех других объектов во всех других элементах управления.

Еще одно отличие заключается в том, что заголовок может реагировать на курсор мыши, когда он находится в области окна или когда курсор выходит из области окна. Для этого ранее в классе CElement был создан метод InitColorArray().

Код метода CElement::CreateCaption():

//+------------------------------------------------------------------+
//| Создает заголовок окна                                           |
//+------------------------------------------------------------------+
bool CWindow::CreateCaption(void)
  {
   string name=CElement::ProgramName()+"_window_caption_"+(string)CElement::Id();
//--- Установим заголовок окна
   if(!m_caption_bg.Create(m_chart_id,name,m_subwin,m_x,m_y,m_x_size,m_caption_height))
      return(false);
//--- Установим свойства
   m_caption_bg.BackColor(m_caption_bg_color);
   m_caption_bg.Color(m_border_color);
   m_caption_bg.BorderType(BORDER_FLAT);
   m_caption_bg.Corner(m_corner);
   m_caption_bg.Selectable(false);
   m_caption_bg.Z_Order(m_caption_zorder);
   m_caption_bg.Tooltip("\n");
//--- Сохраним координаты
   m_caption_bg.X(m_x);
   m_caption_bg.Y(m_y);
//--- Сохраним размеры (в объекте)
   m_caption_bg.XSize(m_caption_bg.X_Size());
   m_caption_bg.YSize(m_caption_bg.Y_Size());
//--- Инициализация массива градиента
   CElement::InitColorArray(m_caption_bg_color,m_caption_bg_color_hover,m_caption_color_bg_array);
//--- Сохраним указатель объекта
   CElement::AddToArray(m_caption_bg);
   return(true);
  }

Ярлык программы в главном окне интерфейса будет определяться по умолчанию заранее подготовленными картинками, и какая из них будет отображаться, зависит от типа разрабатываемой программы (ENUM_PROGRAM_TYPE). Все ярлыки можно скачать в конце статьи. Для использования в своих программах, те из них, которые не относятся к «кликабельным» элементам управления, нужно поместить в директорию <каталог данных>\MQLX\Images\EasyAndFastGUI\Icons\bmp16. Название bmp16 означает, что в папке находятся изображения размером 16x16 пикселей. Например, если в вашей коллекции изображения для библиотеки будут размером 24x24, то тогда папка будет называться bmp24 и т.д.

Стандартные картинки, которые используются по умолчанию для создания элементов управления, нужно поместить в директорию <каталог данных>\MQLX\Images\EasyAndFastGUI\Controls.

Для автоматического определения ярлыка главного окна программы ранее в классе CWindow уже был объявлен метод DefaultIcon(). Код этого метода представлен ниже:

//+------------------------------------------------------------------+
//| Определение ярлыка по умолчанию                                  |
//+------------------------------------------------------------------+
string CWindow::DefaultIcon(void)
  {
   string path="Images\\EasyAndFastGUI\\Icons\\bmp16\\advisor.bmp";
//---
   switch(CElement::ProgramType())
     {
      case PROGRAM_SCRIPT:
        {
         path="Images\\EasyAndFastGUI\\Icons\\bmp16\\script.bmp";
         break;
        }
      case PROGRAM_EXPERT:
        {
         path="Images\\EasyAndFastGUI\\Icons\\bmp16\\advisor.bmp";
         break;
        }
      case PROGRAM_INDICATOR:
        {
         path="Images\\EasyAndFastGUI\\Icons\\bmp16\\indicator.bmp";
         break;
        }
     }
//---
   return(path);
  }

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

Если нужно переопределить картинку для окна, то для этого есть метод CWindow::IconFile(). Примеры использования этого и других методов будут представлены, когда будем тестировать интерфейсы на основе этой библиотеки на графике в терминале.

Ресурсы с картинками для того или иного объекта элемента управления будем подключать (#resource) рядом с методом (вне тела метода), в котором они будут использоваться (отмечено желтым маркером). Ярлык всегда будет иметь одно состояние (одну картинку), поэтому в методах объекта-примитива CChartObjectBmpLabel::BmpFileOn() и CChartObjectBmpLabel::BmpFileOff() будет указан путь к одной версии.

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

//+------------------------------------------------------------------+
//| Создает ярлык программы                                          |
//+------------------------------------------------------------------+
//--- Картинки (по умолчанию) символизирующие тип программы
#resource "\\Images\\EasyAndFastGUI\\Icons\\bmp16\\advisor.bmp"
#resource "\\Images\\EasyAndFastGUI\\Icons\\bmp16\\indicator.bmp"
#resource "\\Images\\EasyAndFastGUI\\Icons\\bmp16\\script.bmp"
//---
bool CWindow::CreateIcon(void)
  {
   string name=CElement::ProgramName()+"_window_icon_"+(string)CElement::Id();
//--- Координаты объекта
   int x=m_x+5;
   int y=m_y+2;
//--- Установим ярлык окна
   if(!m_icon.Create(m_chart_id,name,m_subwin,x,y))
      return(false);
//--- Ярлык по умолчанию, если не определен пользователем
   if(m_icon_file=="")
      m_icon_file=DefaultIcon();
//--- Установим свойства
   m_icon.BmpFileOn("::"+m_icon_file);
   m_icon.BmpFileOff("::"+m_icon_file);
   m_icon.Corner(m_corner);
   m_icon.Selectable(false);
   m_icon.Z_Order(m_button_zorder);
   m_icon.Tooltip("\n");
//--- Сохраним координаты
   m_icon.X(x);
   m_icon.Y(y);   
//--- Отступы от крайней точки
   m_icon.XGap(x-m_x);
   m_icon.YGap(y-m_y);
//--- Сохраним размеры (в объекте)
   m_icon.XSize(m_icon.X_Size());
   m_icon.YSize(m_icon.Y_Size());
//--- Добавим объекты в массив группы
   CElement::AddToArray(m_icon);
   return(true);
  }

Код метода для создания текстовой метки заголовка CWindow::CreateLabel() не имеет каких либо особенностей, отличных от тех, которые были описаны в методе для создания ярлыка программы, поэтому не будем его приводить здесь для экономии места в статье. Вы можете найти его код в файле Window.mqh в конце статьи.

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

  • Изменение внешнего вида при наведении курсора. Поэтому для кнопки будут загружены две картинки. Одна будет использоваться для состояния ON, когда курсор находится над кнопкой. Вторая — для состояния OFF, когда курсор находится вне области кнопки.
  • Для скриптов кнопки не будут устанавливаться.
  • Для диалоговых окон будет доступна только кнопка закрытия окна.
  • Область захвата в заголовке для перемещения окна будет рассчитываться в зависимости от того, сколько кнопок установлено.

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

В остальном же эти кнопки ничем не отличаются от других методов создания частей формы элементов управления. Подробнее код методов CreateButtonClose(), CreateButtonRollUp(), CreateButtonUnroll() и CreateButtonTooltip() можно изучить в приложенном в конце статьи файле Window.mqh.

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

 

Установка формы на график

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

Продолжим работу в файле для тестов, который был создан ранее. В нем мы остановились на том, что создали общий метод для построения интерфейса CProgram::CreateTradePanel() и метод для создания формы для элементов управления CProgram::CreateWindow(). Ниже представлен код для создания формы. Обратите внимание на то, как можно переопределить цвета фона и заголовка формы.

//+------------------------------------------------------------------+
//| Создает форму для элементов управления                           |
//+------------------------------------------------------------------+
bool CProgram::CreateWindow(const string caption_text)
  {
//--- Добавим указатель окна в массив окон
   CWndContainer::AddWindow(m_window);
//--- Свойства
   m_window.XSize(200);
   m_window.YSize(200);
   m_window.WindowBgColor(clrWhiteSmoke);
   m_window.WindowBorderColor(clrLightSteelBlue);
   m_window.CaptionBgColor(clrLightSteelBlue);
   m_window.CaptionBgColorHover(C'200,210,225');
//--- Создание формы
   if(!m_window.CreateWindow(m_chart_id,m_subwin,caption_text,1,1))
      return(false);
//---
   return(true);
  }

В общем методе CProgram::CreateTradePanel() нужно вызвать метод CProgram::CreateWindow(), передав в качестве единственного параметра текст для заголовка окна:

//+------------------------------------------------------------------+
//| Создает торговую панель                                          |
//+------------------------------------------------------------------+
bool CProgram::CreateTradePanel(void)
  {
//--- Создание формы для элементов управления
   if(!CreateWindow("EXPERT PANEL"))
      return(false);
//--- Создание элементов управления
// ...
//---
   ::ChartRedraw();
   return(true);
  }

И теперь осталось только добавить вызов метода CProgram::CreateTradePanel() в главном файле программы в функции OnInit(). В случае ошибки при создании интерфейса программы выведем в журнал соответствующее сообщение и завершим работу:

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit(void)
  {
   program.OnInitEvent();
//--- Установим торговую панель
   if(!program.CreateTradePanel())
     {
      ::Print(__FUNCTION__," > Не удалось создать графический интерфейс!");
      return(INIT_FAILED);
     }
//--- Инициализация прошла успешно
   return(INIT_SUCCEEDED);
  }

Скомпилируйте код и загрузите программу на график. Если все сделано правильно, то на графике вы увидите форму, как на скриншоте ниже:

Рис. 4. Первая проверка установки формы на график

Рис. 4. Первая проверка установки формы на график

Если при разработке MQL-приложения вы вдруг забудете добавить указатель окна в базу указателей, то вы сможете скомпилировать код, но при попытке загрузить программу на график столкнетесь с тем, что программа тут же будет удалена, а в журнал на вкладке «Эксперты» (панель «Инструменты») будут выведены следующие сообщения:

2015.09.23 19:26:10.398 TestLibrary (EURUSD,D1) OnInit > Не удалось создать графический интерфейс!
2015.09.23 19:26:10.398 TestLibrary (EURUSD,D1) CWindow::CreateWindow > Перед созданием окна его указатель нужно сохранить в базе: CWndContainer::AddWindow(CWindow &object).

Более подробные примеры использования методов класса CWindow в индикаторе и скрипте будут показаны в следующих главах (статьях) серии.

 

Заключение

На текущий момент структуру создаваемой библиотеки можно изобразить так, как показано на схеме ниже:

Рис. 5. Добавление в проект главного элемента интерфейса, формы для элементов управления.

Рис. 5. Добавление в проект главного элемента интерфейса, формы для элементов управления

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

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

Список статей (глав) первой части:

Прикрепленные файлы |
Последние комментарии | Перейти к обсуждению на форуме трейдеров (7)
Pavel Trofimov
Pavel Trofimov | 22 дек. 2015 в 19:01

Доброго времени суток. Анатолий, конечно же хочу высказать глубочайшее уважение к Вашему труду и работе, которую Вы проделали. Но у меня при тестировании библиотеки на примере советника возник вопрос. Почему цвет окна получается черным, тогда как все цвета элементов, из которых оно состоит,по умолчанию выставлены как LightGray? Не происходит ли где-то наложения цветов, в результате чего окно получается черным? Скриншот прилагаю:


Anatoli Kazharski
Anatoli Kazharski | 23 дек. 2015 в 07:38
Pavel Trofimov:

Доброго времени суток. Анатолий, конечно же хочу высказать глубочайшее уважение к Вашему труду и работе, которую Вы проделали. Но у меня при тестировании библиотеки на примере советника возник вопрос. Почему цвет окна получается черным, тогда как все цвета элементов, из которых оно состоит,по умолчанию выставлены как LightGray? Не происходит ли где-то наложения цветов, в результате чего окно получается черным? Скриншот прилагаю:

Какими у формы выставлены цвета по умолчанию Вы можете посмотреть в конструкторе класса CWindow (файл Window.mqh).

Если Вам нужно изменить цвета формы, воспользуйтесь соответствующими методами класса CWindow. Свойства формы устанавливаются перед её созданием.

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

//+------------------------------------------------------------------+
//| Создает форму для элементов управления                           |
//+------------------------------------------------------------------+
bool CProgram::CreateWindow(const string caption_text)
  {
//--- Добавим указатель окна в массив окон
   CWndContainer::AddWindow(m_window);
//--- Свойства
   m_window.XSize(200);
   m_window.YSize(200);
   m_window.WindowBgColor(clrWhiteSmoke);
   m_window.WindowBorderColor(clrLightSteelBlue);
   m_window.CaptionBgColor(clrLightSteelBlue);
   m_window.CaptionBgColorHover(C'200,210,225');
//--- Создание формы
   if(!m_window.CreateWindow(m_chart_id,m_subwin,caption_text,1,1))
      return(false);
//---
   return(true);
  }

//---

Результат:

 

Pavel Trofimov
Pavel Trofimov | 23 дек. 2015 в 08:30
Спасибо за разъяснение!
Alexander Fedosov
Alexander Fedosov | 24 февр. 2017 в 09:42

Не нашел метод, который изменял цвет текста Заголовка, поэтому решил добавить в window.mqh:

//--- Свойства заголовка
   string            m_caption_text;
   int               m_caption_height;
   color             m_caption_text_color;
   color             m_caption_bg_color;
   color             m_caption_bg_color_off;
   color             m_caption_bg_color_hover;
   color             m_caption_color_bg_array[];

И методы: 

void              CaptionTextColor(const color text_color)                { m_caption_text_color=text_color;        }
color             CaptionTextColor(void)                            const { return(m_caption_text_color);           }

Ну и этот метод соответственно меняется:

//+------------------------------------------------------------------+
//| Создаёт текстовую метку заголовка                                |
//+------------------------------------------------------------------+
bool CWindow::CreateLabel(void)
  {
   string name=CElementBase::ProgramName()+"_window_label_"+(string)CElementBase::Id();
//--- Координаты объекта
   int x=CElementBase::X()+m_label_x_gap;
   int y=CElementBase::Y()+m_label_y_gap;
//--- Установим текстовую метку
   if(!m_label.Create(m_chart_id,name,m_subwin,x,y))
      return(false);
//--- Установим свойства
   m_label.Description(m_caption_text);
   m_label.Font(CElementBase::Font());
   m_label.FontSize(CElementBase::FontSize());
   m_label.Color(m_caption_text_color);
   m_label.Corner(m_corner);
   m_label.Selectable(false);
   m_label.Z_Order(m_button_zorder);
   m_label.Tooltip("\n");
//--- Сохраним координаты
   m_label.X(x);
   m_label.Y(y);
//--- Отступы от крайней точки
   m_label.XGap(x-m_x);
   m_label.YGap(y-m_y);
//--- Сохраним размеры
   m_label.XSize(m_label.X_Size());
   m_label.YSize(m_label.Y_Size());
//--- Сохраним указатель объекта
   CElementBase::AddToArray(m_label);
   return(true);
  }


 

Anatoli Kazharski
Anatoli Kazharski | 24 февр. 2017 в 12:07
Alexander Fedosov:

Не нашел метод, который изменял цвет текста Заголовка, поэтому решил добавить в window.mqh:

Хорошо. Тоже добавлю такую возможность в одной из следующих статей.

Последнюю на текущий момент версию библиотеки можно скачать вот в этой статье: Графические интерфейсы X: Новые возможности для нарисованной таблицы (build 9)

Возможно уже на следующей неделе выйдет новая версия (build 10).

Графические интерфейсы I: "Оживление" графического интерфейса (Глава 3) Графические интерфейсы I: "Оживление" графического интерфейса (Глава 3)
В предыдущей статье серии был начат процесс разработки класса формы для элементов управления. В этой статье продолжим развивать класс, наполняя его методами для перемещения формы в области графика, а также интегрируем этот элемент интерфейса в ядро библиотеки. Кроме этого, настроим всё таким образом, чтобы при наведении курсора на элементы формы изменялся их цвет.
Графические интерфейсы I: Подготовка структуры библиотеки (Глава 1) Графические интерфейсы I: Подготовка структуры библиотеки (Глава 1)
С этой статьи я начинаю еще одну серию, относящуюся к разработке графических интерфейсов. На текущий момент нет ни одной библиотеки кода, которая позволяла бы легко и быстро создавать качественные графические интерфейсы в MQL-приложениях. Я имею в виду графические интерфейсы, к которым мы все привыкли в известных операционных системах.
Универсальный торговый эксперт: Торговые режимы стратегий (Часть 1) Универсальный торговый эксперт: Торговые режимы стратегий (Часть 1)
Каждый экспертописатель, независимо от уровня своей подготовки, ежедневно сталкивается с одними и теми же торговыми задачами и алгоритмическими проблемами, которые так или иначе приходится решать для организации надежного торгового процесса. Данная статья описывает возможности торгового движка CStrategy, способного взять на себя решение этих задач и предоставить пользователю удобные механизмы для описания своей торговой идеи.
Изучаем класс CCanvas. Сглаживание и тени Изучаем класс CCanvas. Сглаживание и тени
Алгоритм сглаживания класса CCanvas — основа всех построений, в которых используется сглаживание. В статье рассказано о том, как работает этот алгоритм, приведены примеры визуализации его работы. Кроме того, рассмотрено рисование теней графических объектов и разработан подробный алгоритм отрисовки тени на канвасе. Для расчетов применена библиотека численного анализа ALGLIB.