Создай свои графические панели на MQL5

MetaQuotes | 11 января, 2012


Введение

В составе Стандартной Библиотеки появился новый набор классов. Эти классы предназначены для самостоятельного создания диалогов управления и панелей индикации в составе MQL5-программ.

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

Разработанные классы позволяют следующие варианты их использования:
  1. Панель индикации в отдельном подокне чарта;
  2. Панель управления экспертом;
  3. Панель управления пользовательским индикатором.

В данной статье мы покажем как легко и просто можно создавать собственные панели индикации в отдельном подокне чарта на основе классов Стандартной Библиотеки.


Что нам предлагает Стандартная Библиотека

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

1. Простые элементы управления:

Элемент управления Применение Реализация на основе встроенного объекта Файл Стандартной Библиотеки

Кнопка с текстом

обеспечение взаимодействия "мышки" с MQL-программой

"Кнопка"  <Controls\Button.mqh>

Кнопка с картинкой

обеспечение взаимодействия "мышки" с MQL-программой

"Графическая метка"  <Controls\BmpButton.mqh>

Поле ввода

ввод или отображение (в режиме "Только чтение") текстовой информации

"Поле ввода"  <Controls\Edit.mqh>

Надпись

отображение вспомогательной текстовой информации

"Текстовая метка"  <Controls\Label.mqh>

Панель

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

"Прямоугольная метка"  <Controls\Panel.mqh>

Картинка

декоративный элемент

 "Графическая метка"  <Controls\Picture.mqh>



2. Комбинированные элементы управления:


Элемент управления Применение Реализация на основе элементов Файл Стандартной Библиотеки

Список

просмотр списка

"Прямоугольник", "Кнопка с картинкой" и "Поле ввода"  <Controls\List.mqh>

Поле с выпадающим списком

выбор из выпадающего списка

"Поле ввода", "Кнопка с картинкой" и "Список"  <Controls\ComboBox.mqh>

Поле инкремента/декремента

перебор значений

"Поле ввода" и "Кнопка с картинкой"  <Controls\SpinEdit.mqh>

Радиокнопка

переключатель "Кнопка с картинкой" и "Надпись"  <Controls\RadioButton.mqh>
 Группа радиокнопок  редактирование поля типа enum  "Прямоугольник" и "Радиокнопка"  <Controls\RadioGroup.mqh>

Чекбокс

опция выбора

"Кнопка с картинкой" и "Надпись"  <Controls\CheckBox.mqh>

Группа чекбоксов

редактирование набора флагов

 "Прямоугольник" и "Чекбокс"  <Controls\CheckGroup.mqh>
 Диалог  форма диалога  "Прямоугольник", "Кнопка с картинкой" и "Поле ввода"  <Controls\Dialog.mqh>



Создаём панель индикации

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

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



Для создания панели-индикатора нам понадобятся два файла:

  1. Включаемый файл с описанием класса индикаторной панели.
  2. Файл с исходным кодом индикатора.

Заготовки для этих файлов можно получить при помощи Мастера MQL5. Создадим в общей папке индикаторов (MQL5\Indicators) отдельную папку MyIndicators, а в ней вложенную подпапку MyPanel. Процесс создания папок мы здесь рассматривать не будем, он хорошо описан в справке.


Описание класса

Итак, рабочую папку мы уже создали. Теперь найдём её в окне "Навигатор" и щёлкнем по ней правой кнопкой мышки. В появившемся меню выберем пункт "Новый файл". Из предложенных Мастером MQL5 вариантов продолжения выберем "Новый класс", нажмём кнопку "Далее >". Заполняем диалог описания класса, как показано на рисунке:



Жмём кнопку "Готово". В результате наших действий мы получили следующий код:

//+------------------------------------------------------------------+
//|                                                  PanelDialog.mqh |
//|                        Copyright 2011, MetaQuotes Software Corp. |
//|                                              https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2011, MetaQuotes Software Corp."
#property link      "https://www.mql5.com"
#property version   "1.00"
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
class CPanelDialog : public CAppDialog
  {
private:

public:
                     CPanelDialog();
                    ~CPanelDialog();
  };
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
CPanelDialog::CPanelDialog()
  {
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
CPanelDialog::~CPanelDialog()
  {
  }
//+------------------------------------------------------------------+


Добавим включаемый файл <Controls\Dialog.mqh> из состава Стандартной Библиотеки с описанием базового класса CAppDialog и комментарии.

//+------------------------------------------------------------------+
//|                                                  PanelDialog.mqh |
//|                        Copyright 2011, MetaQuotes Software Corp. |
//|                                              https://www.mql5.com |
//+------------------------------------------------------------------+
#include <Controls\Dialog.mqh>
//+------------------------------------------------------------------+
//| Класс CPanelDialog                                               |
//| Назначение: главный диалог приложения                            |
//+------------------------------------------------------------------+
class CPanelDialog : public CAppDialog
  {
private:

public:
                     CPanelDialog(void);
                    ~CPanelDialog(void);
  };
//+------------------------------------------------------------------+
//| Конструктор                                                      |
//+------------------------------------------------------------------+
CPanelDialog::CPanelDialog(void)
  {
  }
//+------------------------------------------------------------------+
//| Деструктор                                                       |
//+------------------------------------------------------------------+
CPanelDialog::~CPanelDialog(void)
  {
  }
//+------------------------------------------------------------------+

Мы получили описание класса, который позволит нам использовать в своём индикаторе диалоговое окно. Наш диалог пока пустой, позже мы наполним его элементами управления. А сейчас займёмся индикатором.


Исходный код индикатора

Для создания индикатора также воспользуемся Мастером MQL5. Наши действия будут аналогичны тем, которые мы произвели при создании описателя класса. Единственное отличие - из предложенных Мастером MQL5 вариантов продолжения выберем "Пользовательский индикатор". Для создания индикатора нужно пройти три диалога.

В первом диалоге нужно ввести имя индикатора:



Во втором диалоге нужно установить галочку "OnChartEvent" (обязательно) и галочку "OnTimer":



В третьем диалоге нужно установить галочку "Индикатор в отдельном окне" (обязательно):



Нажимаем кнопку "Готово". Получаем код:

//+------------------------------------------------------------------+
//|                                               PanelIndicator.mq5 |
//|                        Copyright 2011, MetaQuotes Software Corp. |
//|                                              https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2011, MetaQuotes Software Corp."
#property link      "https://www.mql5.com"
#property version   "1.00"
#property indicator_separate_window
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- indicator buffers mapping
   
//---
   return(0);
  }
//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
                const int prev_calculated,
                const int begin,

                const double &price[])
  {
//---
   
//--- return value of prev_calculated for next call
   return(rates_total);
  }
//+------------------------------------------------------------------+
//| Timer function                                                   |
//+------------------------------------------------------------------+
void OnTimer()
  {
//---
   
  }
//+------------------------------------------------------------------+
//| ChartEvent function                                              |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,
                  const long &lparam,
                  const double &dparam,
                  const string &sparam)
  {
//---
   
  }
//+------------------------------------------------------------------+


Теперь в нашу "заготовку" добавляем:

Получили готовый к использованию индикатор:

//+------------------------------------------------------------------+
//|                                               PanelIndicator.mq5 |
//|                        Copyright 2011, MetaQuotes Software Corp. |
//|                                              https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2011, MetaQuotes Software Corp."
#property link      "https://www.mql5.com"
#property version   "1.00"
#property indicator_separate_window
#property indicator_plots               0
#property indicator_buffers             0
#property indicator_minimum             0.0
#property indicator_maximum             0.0
//+------------------------------------------------------------------+
//| Включаемые файлы                                                 |
//+------------------------------------------------------------------+
#include "PanelDialog.mqh"
//+------------------------------------------------------------------+
//| Глобальные переменные                                            |
//+------------------------------------------------------------------+
CPanelDialog ExtDialog;
//+------------------------------------------------------------------+
//| Инициализация                                                    |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- создаём диалог приложения
   if(!ExtDialog.Create(0,"Panel Indicator",0,0,0,0,130))
     return(-1);
//--- запускаем приложение
   if(!ExtDialog.Run())
     return(-2);
//--- создаём таймер
   EventSetTimer(1);
//--- удача
   return(0);
  }
//+------------------------------------------------------------------+
//| Деинициализация                                                  |
//+------------------------------------------------------------------+
int OnDeinit()
  {
//--- удаляем диалог
   ExtDialog.Destroy();
//--- удаляем таймер
   EventKillTimer();
  }
//+------------------------------------------------------------------+
//| Итерация                                                         |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
                const int prev_calculated,
                const int begin,
                const double &price[])
  {
//---
   
//--- возвращаем значение prev_calculated для следующего вызова
   return(rates_total);
  }
//+------------------------------------------------------------------+
//| Обработчик события таймера                                       |
//+------------------------------------------------------------------+
void OnTimer()
  {
//---
   
  }
//+------------------------------------------------------------------+
//| Обработчик события чарта                                         |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,
                  const long &lparam,
                  const double &dparam,
                  const string &sparam)
  {
//--- обрабатываем событие
   ExtDialog.ChartEvent(id,lparam,dparam,sparam);
  }
//+------------------------------------------------------------------+


В таком виде наш индикатор пока ничего не показывает. Если его скомпилировать и набросить из Навигатора на чарт, мы увидим пустой диалог в отдельном подокне.

Но и пустой диалог уже наделил наш индикатор определёнными свойствами:


Да будет индикация

Для того чтобы наша панель начала отображать какую-либо информацию нам нужно определиться с ответами на три вопроса:

  1. Какую информацию мы хотим отображать?
  2. Какие дополнительные элементы отображения и/или управления для этого необходимо расположить в нашем диалоге?
  3. Как эти дополнительные элементы будут между собой взаимодействовать?

Ещё один немаловажный фактор - наш диалог должен быть красивым и удобным в использовании.  Это не влияет на работоспособность диалога, но показывает нашу заботу о пользователе будущей MQL5-программы.

   Шаг 1. Какую информацию мы хотим отображать?

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

Значения параметров будут задаваться следующим образом:

  • Значением уровня "красного" будет число в диапазоне от 0 до 255, которое будет изменяться случайным образом при каждом возникновении события Calculate;
  • Значением уровня "зелёного" будет число в диапазоне от 0 до 255, которое будет изменяться случайным образом при каждом возникновении события Timer;
  • Значением уровня "синего" будет число в диапазоне от 0 до 255, которое будет изменяться "вручную" при помощи специального элемента управления.

Кстати, значения этих уровней мы тоже отобразим в нашем индикаторе.


   Шаг 2. Какие дополнительные элементы нам понадобятся?

  1. Для отображения цвета мы будем использовать элемент "Панель".
  2. Для отображения уровней "красного" и "зелёного" будем использовать элементы "Поле ввода" в режиме "Только чтение".
  3. Для управления уровнем "синего" будем использовать элемент "Поле (+/-)". Этот же элемент будет отображать значение уровня.
  4. Оба элемента "Поле ввода" и элемент "Поле (+/-)" снабдим поясняющими надписями, для которых будем использовать, соответственно, элементы "Надпись".

Добавим включаемые файлы из состава Стандартной Библиотеки, в описание класса необходимые элементы и переменные для хранения параметров, снабдив их комментариями.

Получим:

//+------------------------------------------------------------------+
//|                                                  PanelDialog.mqh |
//|                        Copyright 2011, MetaQuotes Software Corp. |
//|                                              https://www.mql5.com |
//+------------------------------------------------------------------+
#include <Controls\Dialog.mqh>
#include <Controls\Panel.mqh>
#include <Controls\Edit.mqh>
#include <Controls\Label.mqh>
#include <Controls\SpinEdit.mqh>
//+------------------------------------------------------------------+
//| Класс CPanelDialog                                               |
//| Назначение: главный диалог приложения                            |
//+------------------------------------------------------------------+
class CPanelDialog : public CAppDialog
  {
private:
   //--- дополнительные элементы
   CPanel            m_color;                         // объект для отображения цвета
   CLabel            m_label_red;                     // объект надпись для "красного"
   CEdit             m_field_red;                     // объект индикации значения "красного"
   CLabel            m_label_green;                   // объект надпись для "зелёного"
   CEdit             m_field_green;                   // объект индикации значения "зелёного"
   CLabel            m_label_blue;                    // объект надпись для "синего"
   CSpinEdit         m_edit_blue;                     // объект управления значением "синего"
   //--- значения параметров
   int               m_red;                           // значение "красного"
   int               m_green;                         // значение "зелёного"
   int               m_blue;                          // значение "синего"

public:
                     CPanelDialog(void);
                    ~CPanelDialog(void);
  };
//+------------------------------------------------------------------+
//| Конструктор                                                      |
//+------------------------------------------------------------------+
CPanelDialog::CPanelDialog(void)
  {
  }
//+------------------------------------------------------------------+
//| Деструктор                                                       |
//+------------------------------------------------------------------+
CPanelDialog::~CPanelDialog(void)
  {
  }
//+------------------------------------------------------------------+


   Шаг 3. Как дополнительные элементы диалога будут между собой взаимодействовать?
Принцип взаимодействия между элементами диалога будет предельно прост: "Изменение любого параметра (уровней "красного", "зелёного" и "синего") должно отображаться на диалоге". Реализацией алгоритмов взаимодействия мы займёмся позже, а сейчас самое время перейти к созданию диалога.


Немного о красоте

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

Использование предопределённых именованных констант даёт следующие преимущества:


Предлагается использовать следующие константы:

//+------------------------------------------------------------------+
//| defines                                                          |
//+------------------------------------------------------------------+
//--- отступы и зазоры
#define INDENT_LEFT                         (11)      // отступ слева  (с учётом толщины рамки)
#define INDENT_TOP                          (11)      // отступ сверху (с учётом толщины рамки)
#define INDENT_RIGHT                        (11)      // отступ справа (с учётом толщины рамки)
#define INDENT_BOTTOM                       (11)      // отступ снизу  (с учётом толщины рамки)
#define CONTROLS_GAP_X                      (10)      // зазор по X
#define CONTROLS_GAP_Y                      (10)      // зазор по Y
//--- для надписей
#define LABEL_WIDTH                         (50)      // размер по X
//--- для едитов
#define EDIT_WIDTH                          (50)      // размер по X
#define EDIT_HEIGHT                         (20)      // размер по Y
//--- для базовых цветов (RGB)
#define BASE_COLOR_MIN                      (0)       // минимальное значение для цветовой компоненты
#define BASE_COLOR_MAX                      (255)     // максимальное значение для цветовой компоненты


Наполняем панель индикации

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

1. Переопределить метод Create(...) родительского класса. Первоначально наш метод будет выглядеть так:

//+------------------------------------------------------------------+
//| Создание                                                         |
//+------------------------------------------------------------------+
bool CPanelDialog::Create(const long chart,const string name,const int subwin,const int x1,const int y1,const int x2,const int y2)
  {
//--- вызываем метод родительского класса
   if(!CAppDialog::Create(chart,name,subwin,x1,y1,x2,y2))                           return(false);
//--- здесь нужно создать дополнительные элементы

//--- удача
   return(true);
  }


2. Создать дополнительные элементы.

Тут сделаем небольшое лирическое отступление. Конечно, можно разместить код создания всех дополнительных элементов прямо в теле метода Create(...), но в результате мы рискуем получить большую нечитабельную "портянку".

Поэтому разделим создание на функционально законченные куски-методы:

Эти методы мы последовательно вызовем из Create(...):

//+------------------------------------------------------------------+
//| Создание                                                         |
//+------------------------------------------------------------------+
bool CPanelDialog::Create(const long chart,const string name,const int subwin,const int x1,const int y1,const int x2,const int y2)
  {
//--- вызываем метод родительского класса
   if(!CAppDialog::Create(chart,name,subwin,x1,y1,x2,y2))                           return(false);

//--- создаём дополнительные элементы
   if(!CreateColor())                                                               return(false);
   if(!CreateRed())                                                                 return(false);
   if(!CreateGreen())                                                               return(false);
   if(!CreateBlue())                                                                return(false);
//--- удача
   return(true);
  }


Создание элементов

Мы не будем рассматривать создание каждого дополнительного элемента, а рассмотрим подробнее один метод - bool CreateBlue(void).

Он выглядит следующим образом:

//+------------------------------------------------------------------+
//| Создание элемента управления "Blue" с поясняющей надписью        |
//+------------------------------------------------------------------+
bool CPanelDialog::CreateBlue(void)
  {
//--- координаты
   int x1=INDENT_LEFT;
   int y1=INDENT_TOP+2*(EDIT_HEIGHT+CONTROLS_GAP_Y);
   int x2=x1+EDIT_WIDTH;
   int y2=y1+EDIT_HEIGHT;
//--- создаём надпись
   if(!m_label_blue.Create(m_chart_id,m_name+"LabelBlue",m_subwin,x1,y1+1,x2,y2))   return(false);
   if(!m_label_blue.Text("Blue"))                                                   return(false);
   if(!Add(m_label_blue))                                                           return(false);
//--- корректируем координаты
   x1+=LABEL_WIDTH+CONTROLS_GAP_X;
   x2=x1+EDIT_WIDTH;
//--- создаём элемент
   if(!m_edit_blue.Create(m_chart_id,m_name+"Blue",m_subwin,x1,y1,x2,y2))           return(false);
   if(!Add(m_edit_blue))                                                           return(false);
   m_edit_blue.MinValue(BASE_COLOR_MIN);
   m_edit_blue.MaxValue(BASE_COLOR_MAX);
   m_edit_blue.Value(m_blue);
//--- удача
   return(true);
  }

Есть две тонкости:

  1. Элемент создаётся с относительными координатами, т.е. смещение задаётся относительно верхнего левого угла того контейнера (комбинированного элемента), в который элемент добавится после создания.
  2. После создания элемент обязательно должен быть добавлен в какой-либо контейнер при помощи метода Add(...). В нашем случае таким контейнером является диалог.


Изменение параметров

Для изменения цвета "цветной панели" добавим метод void SetColor(void);

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

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

Для примера приведем код одного из методов:

//+------------------------------------------------------------------+
//| Установка значения "Red"                                         |
//+------------------------------------------------------------------+
void CPanelDialog::SetRed(const int value)
  {
//--- проверяем
   if(value<0 || value>255) return;
//--- сохраняем
   m_red=value;
//--- устанавливаем
   m_field_red.Text(IntegerToString(value));
//--- устанавливаем цвет панели
   SetColor();
  }

Как мы условились выше:

Добавим соответствующий код в исходный текст нашего индикатора:

//+------------------------------------------------------------------+
//|                                               PanelIndicator.mq5 |
//|                        Copyright 2011, MetaQuotes Software Corp. |
//|                                              https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2011, MetaQuotes Software Corp."
#property link      "https://www.mql5.com"
#property version   "1.00"
#property indicator_separate_window
#property indicator_plots               0
#property indicator_buffers             0
#property indicator_minimum             0.0
#property indicator_maximum             0.0
//+------------------------------------------------------------------+
//| Включаемые файлы                                                 |
//+------------------------------------------------------------------+
#include "PanelDialog.mqh"
//+------------------------------------------------------------------+
//| Глобальные переменные                                            |
//+------------------------------------------------------------------+
CPanelDialog ExtDialog;
//+------------------------------------------------------------------+
//| Инициализация                                                    |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- создаём диалог приложения
   if(!ExtDialog.Create(0,"Panel Indicator",0,0,0,0,130))
      return(-1);
//--- запускаем приложение
   if(!ExtDialog.Run())
      return(-2);
//--- создаём таймер
   EventSetTimer(1);
//--- удача
   return(0);
  }
//+------------------------------------------------------------------+
//| Деинициализация                                                  |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//--- уничтожаем диалог
   ExtDialog.Destroy();
//--- уничтожаем таймер
   EventKillTimer();
  }
//+------------------------------------------------------------------+
//| Итерация                                                         |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
                const int prev_calculated,
                const int begin,
                const double &price[])
  {
//--- изменяем свойство диалога
   ExtDialog.SetRed(MathRand()%256);
//--- возвращаем значение prev_calculated для следующего вызова
   return(rates_total);
  }
//+------------------------------------------------------------------+
//| Обработчик события таймера                                       |
//+------------------------------------------------------------------+
void OnTimer()
  {
//--- изменяем свойство диалога
   ExtDialog.SetGreen(MathRand()%256);
  }
//+------------------------------------------------------------------+
//| Обработчик события чарта                                         |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,
                  const long &lparam,
                  const double &dparam,
                  const string &sparam)
  {
//--- обрабатываем событие
   ExtDialog.ChartEvent(id,lparam,dparam,sparam);
  }
//+------------------------------------------------------------------+


Обработка событий

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

События условно можно разделить на две группы:

Мы будем обрабатывать оба типа событий.

Из "внутренних" событий нам нужно обработать только событие изменения размера диалога. Для этого перегрузим метод OnResize() родительского класса. Наш метод будет простым, так как высота диалога не изменяется, а при изменении ширины диалога нам нужно изменить только ширину цветовой панели:

//+------------------------------------------------------------------+
//| Обработчик изменения размеров                                    |
//+------------------------------------------------------------------+
bool CPanelDialog::OnResize(void)
  {
//--- вызываем метод родительского класса
   if(!CAppDialog::OnResize()) return(false);
//--- измененяем ширину цветовой панели
   m_color.Width(ClientAreaWidth()-(INDENT_RIGHT+LABEL_WIDTH+CONTROLS_GAP_X+EDIT_WIDTH+CONTROLS_GAP_X+INDENT_LEFT));
//--- удача
   return(true);
  }

Перечень "внешних" событий у нас тоже ограничится одним - это будет событие изменения элемента управления уровнем "синего". Требования к обработчику "внешнего" события минимальные: обработчик должен быть методом класса без параметров типа void.

Опишем обработчик этого события:

//+------------------------------------------------------------------+
//| Обработчик события изменения уровня "синего"                     |
//+------------------------------------------------------------------+
void CPanelDialog::OnChangeBlue(void)
  {
//--- сохраняем
   m_blue=m_edit_blue.Value();
//--- устанавливаем цвет панели
   SetColor();
  }

Как видите, ничего сложного нет.

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

virtual bool  OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam);

Теперь немного о "мистическом". Если вы уже открыли файл PanelDialog.mqh в редакторе, то тела метода OnEvent(...) вы не обнаружите.

Пусть вас это не смущает - всё дело в том, что для описания обработки "внешних" событий создан набор макросов (см. файл Стандартной Библиотеки <Controls\Defines.mqh>).

Наш обработчик событий выглядит следующим образом:

//+------------------------------------------------------------------+
//| Обработка событий                                                |
//+------------------------------------------------------------------+
EVENT_MAP_BEGIN(CPanelDialog)
   ON_EVENT(ON_CHANGE,m_edit_blue,OnChangeBlue)
EVENT_MAP_END(CAppDialog)

Этот непонятный на первый взгляд "псевдокод" выполняет следующие действия:


Заключение

В этой статье мы рассмотрели процесс создания панели-индикатора на базе классов Стандартной Библиотеки.

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

Более сложные примеры из стандартной поставки вы можете посмотреть в следующих папках своего терминала: