English 中文 Español Deutsch 日本語 Português
Графические интерфейсы VI: Элементы "Чекбокс", "Поле ввода" и их смешанные типы (Глава 1)

Графические интерфейсы VI: Элементы "Чекбокс", "Поле ввода" и их смешанные типы (Глава 1)

MetaTrader 5Примеры | 23 мая 2016, 13:02
3 864 26
Anatoli Kazharski
Anatoli Kazharski

Содержание



Введение

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

Шестая часть серии будет состоять из двух глав. В первой из них создадим четыре элемента управления:

  • Чекбокс
  • Поле ввода
  • Поле ввода с чекбоксом
  • Комбобокс с чекбоксом

Из этих четырёх элементов опишем в статье только «чекбокс» и «поле ввода», так как «поле ввода с чекбоксом» и «комбобокс с чекбоксом» уже не будут содержать в себе ничего такого, что не было бы описано ранее. 

Во второй главе будут следующие элементы:

  • Слайдер
  • Двухсторонний слайдер

 


Элемент «Чекбокс»

Элемент управления «чекбокс» предназначен для управления параметрами, у которых может быть только два состояния. Чтобы понять, в каком состоянии сейчас находится параметр, к которому привязан элемент, используется кнопка с двумя изображениями. Изображение с символом «галочка» обозначает состояние «включено» (on). Изображение без этого символа «галочка» обозначит состояние «выключено» (off). Рядом с кнопкой располагается краткое описание параметра. 

Собирать этот элемент будем из трёх графических объектов. Перечислим их.

  1. Фон
  2. Картинка (кнопка)
  3. Текстовая метка

Рис. 1. Составные части элемента управления «Чекбокс».

Рис. 1. Составные части элемента управления «Чекбокс».

Рассмотрим, как устроен класс этого элемента.

 


Разработка класса для создания элемента «Чекбокс»

В третьей части этой серии мы уже рассматривали похожий элемент управления – «Кнопка с картинкой», класс CIconButton  (см. статью Графические интерфейсы III: Простые и многофункциональные кнопки (Глава 1). Элемент «Чекбокс» похож на кнопку с картинкой в режиме с двумя состояниями. Отличие состоит только в том, что у кнопки в различных состояниях (on/off) изменяется цвет фона и цвет текста (если он задан), а у чекбокса — картинка и цвет текста (если он задан). Цвет фона у чекбокса обычно будет сливаться с цветом фона окна, к которому присоединён элемент. Фон здесь будет использоваться как область для определения местоположения курсора мыши относительно границ этой области, а также для определения нажатия на элементе левой кнопкой мыши. При этом элемент «чекбокс» даже ещё проще, чем элемент «кнопка с картинкой», поскольку имеет меньше свойств, которые может задать пользователь.

Создаём файл CheckBox.mqh и сразу подключаем его к библиотеке (к файлу WndContainer.mqh):

//+------------------------------------------------------------------+
//|                                                 WndContainer.mqh |
//|                        Copyright 2015, MetaQuotes Software Corp. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
#include "CheckBox.mqh"

В файле CheckBox.mqh создаём класс CCheckBox со стандартными для всех элементов библиотеки методами:

//+------------------------------------------------------------------+
//|                                                     CheckBox.mqh |
//|                        Copyright 2015, MetaQuotes Software Corp. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
#include "Element.mqh"
#include "Window.mqh"
//+------------------------------------------------------------------+
//| Класс для создания чекбокса                                     |
//+------------------------------------------------------------------+
class CCheckBox : public CElement
  {
private:
   //--- Указатель на форму, к которой элемент присоединён
   CWindow          *m_wnd;
   //---
public:
                     CCheckBox(void);
                    ~CCheckBox(void);
   //--- Сохраняет указатель формы
   void              WindowPointer(CWindow &object)                 { m_wnd=::GetPointer(object);            }
   //---
public:
   //--- Обработчик событий графика
   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);
   //--- (1) Показ, (2) скрытие, (3) сброс, (4) удаление
   virtual void      Show(void);
   virtual void      Hide(void);
   virtual void      Reset(void);
   virtual void      Delete(void);
   //--- (1) Установка, (2) сброс приоритетов на нажатие левой кнопки мыши
   virtual void      SetZorders(void);
   virtual void      ResetZorders(void);
   //--- Сбросить цвет
   virtual void      ResetColors(void);
  };

Перед созданием элемента пользователю будут доступны методы для установки свойств представленных в списке ниже:

  • Фон элемента
  • Ярлыки чекбокса в активном и заблокированном состоянии
  • Отступы для текстовой метки
  • Цвета текстовой метки в разных состояниях

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

class CCheckBox : public CElement
  {
private:
   //--- Цвет фона чекбокса
   color             m_area_color;
   //--- Ярлыки чекбокса в активном и заблокированном состоянии
   string            m_check_bmp_file_on;
   string            m_check_bmp_file_off;
   string            m_check_bmp_file_on_locked;
   string            m_check_bmp_file_off_locked;
   //--- Текст чекбокса
   string            m_label_text;
   //--- Отступы текстовой метки
   int               m_label_x_gap;
   int               m_label_y_gap;
   //--- Цвета текстовой метки в разных состояниях
   color             m_label_color;
   color             m_label_color_off;
   color             m_label_color_hover;
   color             m_label_color_locked;
   color             m_label_color_array[];
   //--- Приоритеты на нажатие левой кнопки мыши
   int               m_zorder;
   int               m_area_zorder;
   //---
public:
   //--- Установка ярлыков для кнопки в активном и заблокированном состояниях
   void              CheckFileOn(const string file_path)            { m_check_bmp_file_on=file_path;         }
   void              CheckFileOff(const string file_path)           { m_check_bmp_file_off=file_path;        }
   void              CheckFileOnLocked(const string file_path)      { m_check_bmp_file_on_locked=file_path;  }
   void              CheckFileOffLocked(const string file_path)     { m_check_bmp_file_off_locked=file_path; }
   //--- (1) Цвет фона, (2) отступы текстовой метки
   void              AreaColor(const color clr)                     { m_area_color=clr;                      }
   void              LabelXGap(const int x_gap)                     { m_label_x_gap=x_gap;                   }
   void              LabelYGap(const int y_gap)                     { m_label_y_gap=y_gap;                   }
   //--- Цвета текста в разных состояниях
   void              LabelColor(const color clr)                    { m_label_color=clr;                     }
   void              LabelColorOff(const color clr)                 { m_label_color_off=clr;                 }
   void              LabelColorHover(const color clr)               { m_label_color_hover=clr;               }
   void              LabelColorLocked(const color clr)              { m_label_color_locked=clr;              }
   //--- Описание чекбокса
   string            LabelText(void)                          const { return(m_label.Description());         }
  };

Для создания элемента «чекбокс» понадобятся три приватных (private) метода и один основной публичный (public):

class CCheckBox : public CElement
  {
private:
   //--- Объекты для создания чек-бокса
   CRectLabel        m_area;
   CBmpLabel         m_check;
   CLabel            m_label;
   //---
public:
   //--- Методы для создания чек-бокса
   bool              CreateCheckBox(const long chart_id,const int subwin,const string text,const int x,const int y);
   //---
private:
   bool              CreateArea(void);
   bool              CreateCheck(void);
   bool              CreateLabel(void);
  };

Для управления состоянием чекбокса создадим методы CCheckBox::CheckButtonState() и CCheckBox::CheckBoxState():

class CCheckBox : public CElement
  {
private:
   //--- Состояние кнопки чекбокса
   bool              m_check_button_state;
   //--- Состояние элемента (доступен/заблокирован)
   bool              m_checkbox_state;
   //---
public:
   //--- Возвращение/установка состояния чекбокса
   bool              CheckBoxState(void)                      const { return(m_checkbox_state);              }
   void              CheckBoxState(const bool state);
   //--- Возвращение/установка состояния кнопки чекбокса
   bool              CheckButtonState(void)                   const { return(m_check.State());               }
   void              CheckButtonState(const bool state);
  };

Для блокировки/разблокировки элемента в целом нужно использовать метод CCheckBox::CheckBoxState():

//+------------------------------------------------------------------+
//| Установка состояния элемента                                     |
//+------------------------------------------------------------------+
void CCheckBox::CheckBoxState(const bool state)
  {
//--- Состояние элемента
   m_checkbox_state=state;
//--- Картинка
   m_check.BmpFileOn((state)? "::"+m_check_bmp_file_on : "::"+m_check_bmp_file_on_locked);
   m_check.BmpFileOff((state)? "::"+m_check_bmp_file_off : "::"+m_check_bmp_file_off_locked);
//--- Цвет текстовой метки
   m_label.Color((state)? m_label_color : m_label_color_locked);
  }

Для установки состояния кнопки чекбокса нужно использовать метод CCheckBox::CheckButtonState(): 

//+------------------------------------------------------------------+
//| Установка состояния кнопки чекбокса                             |
//+------------------------------------------------------------------+
void CCheckBox::CheckButtonState(const bool state)
  {
//--- Выйти, если элемент заблокирован
   if(!m_checkbox_state)
      return;
//--- Установим состояние кнопке
   m_check.State(state);
   m_check_button_state=state;
//--- Изменим цвета относительно состояния
   m_label.Color((state)? m_label_color : m_label_color_off);
   CElement::InitColorArray((state)? m_label_color : m_label_color_off,m_label_color_hover,m_label_color_array);
  }

Осталось только создать метод-обработчик нажатия на элементе «чекбокс», который будет вызываться в главном обработчике элемента CCheckBox::OnEvent() по событию с идентификатором CHARTEVENT_OBJECT_CLICK. Назовём этот метод CCheckBox::OnClickLabel(). В самом начале этого метода проверяется, было ли произведено нажатие на площади (фоне) элемента. Чтобы не возникало путаницы в том, на каком именно объекте элемента был клик, значение приоритета на нажатие левой кнопкой мыши у фона равно 1, а у всех остальных - 0. Поэтому, даже если нажатие было осуществлено, когда курсор находится над кнопкой чекбокса или над текстовой меткой, будет считаться, что клик был на фоне элемента, то есть, на том объекте, чей приоритет выше в данной области. Далее с помощью метода CCheckBox::CheckButtonState() устанавливается состояние, противоположное кнопке чекбокса. В конце метода отправляется пользовательское событие с (1) идентификатором события ON_CLICK_LABEL, (2) идентификатором элемента и (3) описанием элемента.

class CCheckBox : public CElement
  {
private:
   //--- Обработка нажатия на элемент
   bool              OnClickLabel(const string clicked_object);
  };
//+------------------------------------------------------------------+
//| Обработка событий                                                |
//+------------------------------------------------------------------+
void CCheckBox::OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam)
  {
//--- Обработка события нажатия левой кнопки мыши на объекте
   if(id==CHARTEVENT_OBJECT_CLICK)
     {
      //--- Нажатие на чекбоксе
      if(OnClickLabel(sparam))
         return;
     }
  }
//+------------------------------------------------------------------+
//| Нажатие на заголовок элемента                                    |
//+------------------------------------------------------------------+
bool CCheckBox::OnClickLabel(const string clicked_object)
  {
//--- Выйдем, если чужое имя объекта
   if(m_area.Name()!=clicked_object)
      return(false);
//--- Выйти, если элемент заблокирован
   if(!m_checkbox_state)
      return(false);
//--- Переключить на противоположный режим
   CheckButtonState(!m_check.State());
//--- Курсор мыши сейчас над элементом
   m_label.Color(m_label_color_hover);
//--- Отправим сообщение об этом
   ::EventChartCustom(m_chart_id,ON_CLICK_LABEL,CElement::Id(),0,m_label.Description());
   return(true);
  }

 

 


Тест элемента «Чекбокс»

Все методы класса CCheckBox готовы, и теперь можно протестировать, как работает этот элемент управления. Файл с классом этого элемента уже должен быть подключен к библиотеке. Эксперта для тестов можно скопировать из предыдущей статьи. Удалим из него все элементы управления, оставив только главное меню и строку состояния. Затем создадим для теста два чекбокса. Сделаем так, чтобы второй чекбокс по умолчанию был заблокирован при загрузке программы на график. Первый чекбокс будет доступен для взаимодействия с пользователем, но в отключенном состоянии. Включение первого чекбокса будет сигналом для разблокировки второго чекбокса. И наоборот, то есть, отключение первого чекбокса будет сигналом для блокировки второго чекбокса.

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

//+------------------------------------------------------------------+
//| Класс для создания приложения                                    |
//+------------------------------------------------------------------+
class CProgram : public CWndEvents
  {
private:
   //--- Чек-боксы
   CCheckBox         m_checkbox1;
   CCheckBox         m_checkbox2;
   //---
private:
   //--- Чек-боксы
#define CHECKBOX1_GAP_X       (7)
#define CHECKBOX1_GAP_Y       (50)
   bool              CreateCheckBox1(const string text);
#define CHECKBOX2_GAP_X       (30)
#define CHECKBOX2_GAP_Y       (75)
   bool              CreateCheckBox2(const string text);
  };

Реализация методов отличается только тем, что доступность второго чекбокса будет зависеть от состояния первого чекбокса:

//+------------------------------------------------------------------+
//| Создаёт чекбокс 2                                                |
//+------------------------------------------------------------------+
bool CProgram::CreateCheckBox2(string text)
  {
//--- Передать объект панели
   m_checkbox2.WindowPointer(m_window1);
//--- Координаты
   int x=m_window1.X()+CHECKBOX2_GAP_X;
   int y=m_window1.Y()+CHECKBOX2_GAP_Y;
//--- Установим свойства перед созданием
   m_checkbox2.XSize(90);
   m_checkbox2.YSize(18);
   m_checkbox2.AreaColor(clrWhiteSmoke);
   m_checkbox2.LabelColor(clrBlack);
   m_checkbox2.LabelColorOff(clrBlack);
   m_checkbox2.LabelColorLocked(clrSilver);
//--- Создадим элемент управления
   if(!m_checkbox2.CreateCheckBox(m_chart_id,m_subwin,text,x,y))
      return(false);
//--- Доступность будет зависеть от текущего состояния первого чекбокса
   m_checkbox2.CheckBoxState(m_checkbox1.CheckButtonState()); 
//--- Добавим объект в общий массив групп объектов
   CWndContainer::AddToElementsArray(0,m_checkbox2);
   return(true);
  }

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

//+------------------------------------------------------------------+
//| Создаёт торговую панель                                          |
//+------------------------------------------------------------------+
bool CProgram::CreateTradePanel(void)
  {
//--- Создание формы 1 для элементов управления
//--- Создание элементов управления:
//    Главное меню
//--- Контекстные меню
//--- Создание статусной строки

//--- Чек-боксы
   if(!CreateCheckBox1("Checkbox 1"))
      return(false);
   if(!CreateCheckBox2("Checkbox 2"))
      return(false);
//--- Перерисовка графика
   m_chart.Redraw();
   return(true);
  }

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

//+------------------------------------------------------------------+
//| Обработчик событий                                               |
//+------------------------------------------------------------------+
void CProgram::OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam)
  {
//--- Событие нажатия на чек-боксе
   if(id==CHARTEVENT_CUSTOM+ON_CLICK_LABEL)
     {
      ::Print(__FUNCTION__," > id: ",id,"; lparam: ",lparam,"; dparam: ",dparam,"; sparam: ",sparam);
      //--- Если нажали на первом чекбоксе
      if(lparam==m_checkbox1.Id())
        {
         //--- Установить состояние второму чекбоксу
         m_checkbox2.CheckBoxState(m_checkbox1.CheckButtonState());
        }
     }
  }

Скомпилируйте файлы и загрузите эксперта на график. Должен получиться результат, как на скриншоте ниже:

Рис. 2. Тест элемента управления «чекбокс».

Рис. 2. Тест элемента управления «чекбокс».


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

 


Элемент «Поле ввода»

Элемент управления «Поле ввода» предназначен для установки числового значения в текстовое поле. Можно ввести значение вручную или же воспользоваться кнопками для прокрутки. Минимальное и максимальное значение (ограничения), а также шаг для прокрутки пользователь библиотеки может устанавливать самостоятельно. Если нажать кнопку со стрелкой вверх/вниз, то значение будет увеличено/уменьшено на указанный в настройках шаг. Если зажать и удерживать кнопку вверх/вниз, то значение в поле ввода будет изменяться в ускоренном режиме. Дойдя до разрешенного максимума или минимума, прокрутка значения останавливается.

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

  1. Фон
  2. Текстовая метка
  3. Поле ввода
  4. Две кнопки для прокрутки значений в поле ввода

Рис. 3. Составные части элемента управления «Поле ввода».

Рис. 3. Составные части элемента управления «Поле ввода».


Далее приступим к разработке класса для создания этого элемента интерфейса.

 


Разработка класса для создания элемента «Поле ввода»

Так же, как и элемент «чекбокс», элемент «поле ввода» — простой, а не не составной. Иными словами, он состоит из простых объектов, но не содержит в своём составе других элементов управления. Поэтому в файл WndContainer.mqh не придётся вносить никаких дополнений. Достаточно будет просто подключить файл с классом элемента «поле ввода».

Теперь создаём файл SpinEdit.mqh и подключаем его к файлу WndContainer.mqh:

//+------------------------------------------------------------------+
//|                                                 WndContainer.mqh |
//|                        Copyright 2015, MetaQuotes Software Corp. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
#include "SpinEdit.mqh"

В файле SpinEdit.mqh создаём класс CSpinEdit со стандартными для всех элементов библиотеки методами, точно так же, как это уже было показано ранее в этой статье в разделе описания класса CCheckBox. Поэтому сразу перейдём к описанию свойств элемента интерфейса «поле ввода». 

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

  • Цвет фона
  • Текст описания поля ввода
  • Отступы текстовой метки
  • Цвета текста в разных состояниях
  • Размеры поля ввода
  • Отступ поля ввода от правого края элемента
  • Цвета поля ввода в разных состояниях
  • Цвета текста поля ввода в разных состояниях
  • Цвета рамки поля ввода в разных состояниях
  • Ярлыки переключателей в активном и заблокированном состоянии
  • Отступы кнопок (от правого края)
  • Режим сброса значения до минимального
  • Минимальное и максимальное значение
  • Шаг для изменения значения в поле ввода с помощью кнопок
  • Режим выравнивания текста
  • Количество знаков после запятой

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

//+------------------------------------------------------------------+
//| Класс для создания поля ввода                                    |
//+------------------------------------------------------------------+
class CSpinEdit : public CElement
  {
private:
   //--- Цвет фона элемента
   color             m_area_color;
   //--- Текст описания поля ввода
   string            m_label_text;
   //--- Отступы текстовой метки
   int               m_label_x_gap;
   int               m_label_y_gap;
   //--- Цвета текста в разных состояниях
   color             m_label_color;
   color             m_label_color_hover;
   color             m_label_color_locked;
   color             m_label_color_array[];
   //--- Размеры поля ввода
   int               m_edit_x_size;
   int               m_edit_y_size;
   //--- Отступ поля ввода от правого края
   int               m_edit_x_gap;
   //--- Цвета поля ввода и текста поля ввода в разных состояниях
   color             m_edit_color;
   color             m_edit_color_locked;
   color             m_edit_text_color;
   color             m_edit_text_color_locked;
   color             m_edit_text_color_highlight;
   //--- Цвета рамки поля ввода в разных состояниях
   color             m_edit_border_color;
   color             m_edit_border_color_hover;
   color             m_edit_border_color_locked;
   color             m_edit_border_color_array[];
   //--- Ярлыки переключателей в активном и заблокированном состоянии
   string            m_inc_bmp_file_on;
   string            m_inc_bmp_file_off;
   string            m_inc_bmp_file_locked;
   string            m_dec_bmp_file_on;
   string            m_dec_bmp_file_off;
   string            m_dec_bmp_file_locked;
   //--- Отступы кнопок (от правого края)
   int               m_inc_x_gap;
   int               m_inc_y_gap;
   int               m_dec_x_gap;
   int               m_dec_y_gap;
   //--- Режим сброса значения до минимального
   bool              m_reset_mode;
   //--- Минимальное/максимальное значение
   double            m_min_value;
   double            m_max_value;
   //--- Шаг для изменения значения в поле ввода
   double            m_step_value;
   //--- Режим выравнивания текста
   ENUM_ALIGN_MODE   m_align_mode;
   //--- Количество знаков после запятой
   int               m_digits;
   //---
public:
   //--- (1) Цвет фона, (2) текст описания поля ввода, (3) отступы текстовой метки
   void              AreaColor(const color clr)                     { m_area_color=clr;                   }
   string            LabelText(void)                          const { return(m_label.Description());      }
   void              LabelXGap(const int x_gap)                     { m_label_x_gap=x_gap;                }
   void              LabelYGap(const int y_gap)                     { m_label_y_gap=y_gap;                }
   //--- Цвета текстовой метки в разных состояниях
   void              LabelColor(const color clr)                    { m_label_color=clr;                  }
   void              LabelColorHover(const color clr)               { m_label_color_hover=clr;            }
   void              LabelColorLocked(const color clr)              { m_label_color_locked=clr;           }
   //--- (1) Размеры поля ввода, (2) отступ поля ввода от правого края
   void              EditXSize(const int x_size)                    { m_edit_x_size=x_size;               }
   void              EditYSize(const int y_size)                    { m_edit_y_size=y_size;               }
   void              EditXGap(const int x_gap)                      { m_edit_x_gap=x_gap;                 }
   //--- Цвета поля ввода в разных состояниях
   void              EditColor(const color clr)                     { m_edit_color=clr;                   }
   void              EditColorLocked(const color clr)               { m_edit_color_locked=clr;            }
   //--- Цвета текста поля ввода в разных состояниях
   void              EditTextColor(const color clr)                 { m_edit_text_color=clr;              }
   void              EditTextColorLocked(const color clr)           { m_edit_text_color_locked=clr;       }
   void              EditTextColorHighlight(const color clr)        { m_edit_text_color_highlight=clr;    }
   //--- Цвета рамки поля ввода в разных состояниях
   void              EditBorderColor(const color clr)               { m_edit_border_color=clr;            }
   void              EditBorderColorHover(const color clr)          { m_edit_border_color_hover=clr;      }
   void              EditBorderColorLocked(const color clr)         { m_edit_border_color_locked=clr;     }
   //--- Установка ярлыков для кнопки в активном и заблокированном состояниях
   void              IncFileOn(const string file_path)              { m_inc_bmp_file_on=file_path;        }
   void              IncFileOff(const string file_path)             { m_inc_bmp_file_off=file_path;       }
   void              IncFileLocked(const string file_path)          { m_inc_bmp_file_locked=file_path;    }
   void              DecFileOn(const string file_path)              { m_dec_bmp_file_on=file_path;        }
   void              DecFileOff(const string file_path)             { m_dec_bmp_file_off=file_path;       }
   void              DecFileLocked(const string file_path)          { m_dec_bmp_file_locked=file_path;    }
   //--- Отступы для кнопок поля ввода
   void              IncXGap(const int x_gap)                       { m_inc_x_gap=x_gap;                  }
   void              IncYGap(const int y_gap)                       { m_inc_y_gap=y_gap;                  }
   void              DecXGap(const int x_gap)                       { m_dec_x_gap=x_gap;                  }
   void              DecYGap(const int y_gap)                       { m_dec_y_gap=y_gap;                  }
   //--- Режим сброса при нажатии на текстовой метке
   bool              ResetMode(void)                                { return(m_reset_mode);               }
   void              ResetMode(const bool mode)                     { m_reset_mode=mode;                  }
   //--- Минимальное значение
   double            MinValue(void)                           const { return(m_min_value);                }
   void              MinValue(const double value)                   { m_min_value=value;                  }
   //--- Максимальное значение
   double            MaxValue(void)                           const { return(m_max_value);                }
   void              MaxValue(const double value)                   { m_max_value=value;                  }
   //--- Шаг значения
   double            StepValue(void)                          const { return(m_step_value);               }
   void              StepValue(const double value)                  { m_step_value=(value<=0)? 1 : value; }
   //--- (1) Количество знаков после запятой, (2) режим выравнивания текста
   void              SetDigits(const int digits)                    { m_digits=::fabs(digits);            }
   void              AlignMode(ENUM_ALIGN_MODE mode)                { m_align_mode=mode;                  }
  };

Для создания элемента «поле ввода» понадобится пять приватных (private) методов для создания графических объектов-примитивов и один главный публичный (public) метод, который будет вызываться в пользовательском классе при создании графического интерфейса. Картинки для кнопок прокрутки значения можно скачать в архиве в конце статьи. По умолчанию используются именно они, но вы при желании можете заменить их на свои версии.

class CSpinEdit : public CElement
  {
private:
   //--- Объекты для создания поля ввода
   CRectLabel        m_area;
   CLabel            m_label;
   CEdit             m_edit;
   CBmpLabel         m_spin_inc;
   CBmpLabel         m_spin_dec;
   //---
public:
   //--- Методы для создания поля ввода
   bool              CreateSpinEdit(const long chart_id,const int subwin,const string label_text,const int x,const int y);
   //---
private:
   bool              CreateArea(void);
   bool              CreateLabel(void);
   bool              CreateEdit(void);
   bool              CreateSpinInc(void);
   bool              CreateSpinDec(void);
  };

Теперь рассмотрим режимы и методы, с помощью которых можно управлять состоянием и свойствами элемента уже после его создания. Начнём с метода CSpinEdit::SetValue() для корректировки и сохранения значения для поля ввода, а также вспомогательного метода CSpinEdit::HighlightLimit(), который предназначен для подсветки (подмигивания) текста в поле ввода при превышении максимального и минимального ограничений.

В начале метода CSpinEdit::SetValue() производится корректировка с учётом шага для изменения значения. Далее идут проверки на выход за установленные ограничения (максимум/минимум). Если есть выход за ограничение, то устанавливается соответствующее проверке значение и вызывается метод CSpinEdit::HighlightLimit() для подсветки текста. Затем, если после всех проверок и корректировок оказалось, что значение изменилось по сравнению с тем, что было сохранено ранее, то запоминается новое значение.

Метод CSpinEdit::HighlightLimit() устроен довольно просто. В нём сначала устанавливается цвет подмигивания, который может быть переопределён пользователем (по умолчанию это красный цвет). После этого воспроизводится задержка (пауза) на 100 миллисекунд. Этого достаточно, чтобы кратковременное изменение цвета было заметно. В конце снова устанавливается исходный цвет текста.

С кодом описанных выше методов можно ознакомиться в листинге кода ниже: 

class CSpinEdit : public CElement
  {
private:
   //--- Текущее значение в поле ввода
   double            m_edit_value;
   //---
public:
   //--- Возвращение и установка значения поля ввода
   double            GetValue(void)                           const { return(m_edit_value);               }
   bool              SetValue(const double value);
   //--- Подмигивание при достижении лимита
   void              HighlightLimit(void);
  };
//+------------------------------------------------------------------+
//| Проверка текущего значения                                       |
//+------------------------------------------------------------------+
bool CSpinEdit::SetValue(double value)
  {
//--- Для корректировки
   double corrected_value =0.0;
//--- Скорректируем с учетом шага
   corrected_value=::MathRound(value/m_step_value)*m_step_value;
//--- Проверка на минимум/максимум
   if(corrected_value<m_min_value)
     {
      //--- Установить минимальное значение
      corrected_value=m_min_value;
      //--- Установим состояние On
      m_spin_dec.State(true);
      //--- Подмигнуть, сообщив этим о достижении лимита
      HighlightLimit();
     }
   if(corrected_value>m_max_value)
     {
      //--- Установить максимальное значение
      corrected_value=m_max_value;
      //--- Установим состояние On
      m_spin_inc.State(true);
      //--- Подмигнуть, сообщив этим о достижении лимита
      HighlightLimit();
     }
//--- Если значение было изменено
   if(m_edit_value!=corrected_value)
     {
      m_edit_value=corrected_value;
      m_edit.Color(m_edit_text_color);
      return(true);
     }
//--- Значение без изменений
   return(false);
  }
//+------------------------------------------------------------------+
//| Подсвечивание лимита                                             |
//+------------------------------------------------------------------+
void CSpinEdit::HighlightLimit(void)
  {
//--- Временно изменить цвет текста
   m_edit.Color(m_edit_text_color_highlight);
//--- Обновить
   ::ChartRedraw();
//--- Задержка перед возвращением к исходному цвету
   ::Sleep(100);
//--- Изменить цвет текста на исходный
   m_edit.Color(m_edit_text_color);
  }

Метод CSpinEdit::SetValue() предназначен только для корректировки значения в случае выхода за установленный лимит, а для изменения значения в поле ввода нужно использовать метод CSpinEdit:: ChangeValue():

class CSpinEdit : public CElement
  {
private:
   //--- Изменение значения в поле ввода
   void              ChangeValue(const double value);
  };
//+------------------------------------------------------------------+
//| Изменение значения в поле ввода                                  |
//+------------------------------------------------------------------+
void CSpinEdit::ChangeValue(const double value)
  {
//--- Проверим, скорректируем и запомним новое значение
   SetValue(value);
//--- Установим новое значение в поле ввода
   m_edit.Description(::DoubleToString(GetValue(),m_digits));
  }

Так же, как и в других элементах, понадобится метод для управления доступностью элемента «поле ввода» (см. листинг кода ниже):

class CSpinEdit : public CElement
  {
private:
   //--- Состояние элемента (доступен/заблокирован)
   bool              m_spin_edit_state;
   //---
public:
   //--- Возвращение/установка состояния доступности поля ввода
   bool              SpinEditState(void)                      const { return(m_spin_edit_state);          }
   void              SpinEditState(const bool state);
  };
//+------------------------------------------------------------------+
//| Установка состояния элемента                                     |
//+------------------------------------------------------------------+
void CSpinEdit::SpinEditState(const bool state)
  {
   m_spin_edit_state=state;
//--- Цвет текстовой метки
   m_label.Color((state)? m_label_color : m_label_color_locked);
//--- Цвет поля ввода
   m_edit.Color((state)? m_edit_text_color : m_edit_text_color_locked);
   m_edit.BackColor((state)? m_edit_color : m_edit_color_locked);
   m_edit.BorderColor((state)? m_edit_border_color : m_edit_border_color_locked);
//--- Картинка переключателей
   m_spin_inc.BmpFileOn((state)? "::"+m_inc_bmp_file_on : "::"+m_inc_bmp_file_locked);
   m_spin_dec.BmpFileOn((state)? "::"+m_dec_bmp_file_on : "::"+m_dec_bmp_file_locked);
//--- Настройка относительно текущего состояния
   if(!m_spin_edit_state)
     {
      //--- Приоритеты
      m_edit.Z_Order(-1);
      m_spin_inc.Z_Order(-1);
      m_spin_dec.Z_Order(-1);
      //--- Поле ввода в режим "Только чтение"
      m_edit.ReadOnly(true);
     }
   else
     {
      //--- Приоритеты
      m_edit.Z_Order(m_edit_zorder);
      m_spin_inc.Z_Order(m_spin_zorder);
      m_spin_dec.Z_Order(m_spin_zorder);
      //--- Поле ввода в режим редактирования
      m_edit.ReadOnly(false);
     }
  }

Теперь рассмотрим методы для обработки событий элемента «Поле ввода». При нажатии на текстовую метку будет генерироваться пользовательское событие с (1) идентификатором события ON_CLICK_LABEL, (2) идентификатором элемента, (3) индексом элемента и (4) описанием из текстовой метки. Ранее уже был упомянут режим для сброса значения в поле ввода до минимального. По умолчанию этот режим отключен (false). Включается он с помощью метода CSpinEdit::ResetMode(): укажите в единственном его аргументе значение true. Если режим для сброса значения включен, то нажатие на текстовую метку будет вызывать метод для установки минимального значения в поле ввода.

С учётом всего перечисленного код метода CSpinEdit::OnClickLabel() для обработки нажатия на текстовой метке будет таким, как показано в листинге ниже:

class CSpinEdit : public CElement
  {
private:
   //--- Обработка нажатия на текстовую метку
   bool              OnClickLabel(const string clicked_object);
  };
//+------------------------------------------------------------------+
//| Нажатие на заголовок элемента                                    |
//+------------------------------------------------------------------+
bool CSpinEdit::OnClickLabel(const string clicked_object)
  {
//--- Выйдем, если чужое имя объекта
   if(m_area.Name()!=clicked_object)
      return(false);
//--- Если включен режим сброса значения
   if(m_reset_mode)
     {
      //--- Установим минимальное значение
      ChangeValue(MinValue());
     }
//--- Отправим сообщение об этом
   ::EventChartCustom(m_chart_id,ON_CLICK_LABEL,CElement::Id(),CElement::Index(),m_label.Description());
   return(true);
  }

Значение в поле ввода пользователь может изменить, введя новое значение вручную либо посредством кнопок-переключателей инкремента и декремента. Нам понадобятся новые идентификаторы пользовательских событий. Добавим их в файл Defines.mqh:

//+------------------------------------------------------------------+
//|                                                      Defines.mqh |
//|                        Copyright 2015, MetaQuotes Software Corp. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
#define ON_END_EDIT               (18) // Окончание редактирования значения в поле ввода
#define ON_CLICK_INC              (19) // Изменение счётчика вверх
#define ON_CLICK_DEC              (20) // Изменение счётчика вниз

Чтобы обработать ввод нового значения вручную, напишем метод CSpinEdit::OnEndEdit(). Вызываться он будет в общем обработчике CSpinEdit::OnEvent() по приходу события с идентификатором CHARTEVENT_OBJECT_ENDEDIT. При вводе нового значение оно при необходимости будет скорректировано. В конце метода будем генерировать пользовательское событие с (1) идентификатором события ON_END_EDIT, (2) идентификатором элемента, (3) индексом элемента и (4) описанием из текстовой метки (см. листинг кода ниже). 

class CSpinEdit : public CElement
  {
private:
   //--- Обработка ввода значения в поле ввода
   bool              OnEndEdit(const string edited_object);
  };
//+------------------------------------------------------------------+
//| Обработка ввода значения в поле ввода                            |
//+------------------------------------------------------------------+
bool CSpinEdit::OnEndEdit(const string edited_object)
  {
//--- Выйдем, если чужое имя объекта
   if(m_edit.Name()!=edited_object)
      return(false);
//--- Получим введённое значение
   double entered_value=::StringToDouble(m_edit.Description());
//--- Проверим, скорректируем и запомним новое значение
   ChangeValue(entered_value);
//--- Отправим сообщение об этом
   ::EventChartCustom(m_chart_id,ON_END_EDIT,CElement::Id(),CElement::Index(),m_label.Description());
   return(true);
  }

Для обработки нажатия на кнопках инкремента и декремента нужно создать два отдельных метода CSpinEdit::OnClickSpinInc() и CSpinEdit::OnClickSpinDec(). В них всё просто устроено. После нажатия на графическом объекте по его имени определяется, является ли он кнопкой нашего элемента. Далее получаем текущее значение в поле ввода и увеличиваем/уменьшаем его на установленный в свойствах элемента шаг. В конце методов генерируется пользовательское событие с (1) идентификатором события ON_CLICK_INC/ON_CLICK_DEC, (2) идентификатором элемента, (3) индексом элемента и (4) описанием из текстовой метки (см. листинг кода ниже).

class CSpinEdit : public CElement
  {
private:
   //--- Обработка нажатия кнопки поля ввода
   bool              OnClickSpinInc(const string clicked_object);
   bool              OnClickSpinDec(const string clicked_object);
  };
//+------------------------------------------------------------------+
//| Нажатие на переключатель инкремента                              |
//+------------------------------------------------------------------+
bool CSpinEdit::OnClickSpinInc(const string clicked_object)
  {
//--- Выйдем, если чужое имя объекта
   if(m_spin_inc.Name()!=clicked_object)
      return(false);
//--- Получим текущее значение
   double value=GetValue();
//--- Увеличим на один шаг и проверим на выход за ограничение
   ChangeValue(value+m_step_value);
//--- Установим состояние On
   m_spin_inc.State(true);
//--- Отправим сообщение об этом
   ::EventChartCustom(m_chart_id,ON_CLICK_INC,CElement::Id(),CElement::Index(),m_label.Description());
   return(true);
  }
//+------------------------------------------------------------------+
//| Нажатие на переключатель декремента                              |
//+------------------------------------------------------------------+
bool CSpinEdit::OnClickSpinDec(const string clicked_object)
  {
//--- Выйдем, если чужое имя объекта
   if(m_spin_dec.Name()!=clicked_object)
      return(false);
//--- Получим текущее значение
   double value=GetValue();
//--- Уменьшим на один шаг и проверим на выход за ограничение
   ChangeValue(value-m_step_value);
//--- Установим состояние On
   m_spin_dec.State(true);
//--- Отправим сообщение об этом
   ::EventChartCustom(m_chart_id,ON_CLICK_DEC,CElement::Id(),CElement::Index(),m_label.Description());
   return(true);
  }

Вызывать все методы-обработчики нужно в главном методе обработки событий (см. листинг кода ниже). Допуск в тело методов-обработчиков контролируется доступностью элемента в текущий момент.

//+------------------------------------------------------------------+
//| Обработка событий                                                |
//+------------------------------------------------------------------+
void CSpinEdit::OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam)
  {
//--- Обработка события нажатия левой кнопки мыши на объекте
   if(id==CHARTEVENT_OBJECT_CLICK)
     {
      //--- Выйти, если элемент заблокирован
      if(!m_spin_edit_state)
         return;
      //--- Обработка нажатия на текстовую метку
      if(OnClickLabel(sparam))
         return;
      //--- Обработка нажатия кнопок поля ввода
      if(OnClickSpinInc(sparam))
         return;
      if(OnClickSpinDec(sparam))
         return;
      //---
      return;
     }
//--- Обработка события изменения значения в поле ввода
   if(id==CHARTEVENT_OBJECT_ENDEDIT)
     {
      //--- Выйти, если элемент заблокирован
      if(!m_spin_edit_state)
         return;
      //--- Обработка ввода значения
      if(OnEndEdit(sparam))
         return;
     }
  }

Для ускоренной промотки значений при зажатии левой кнопки мыши над кнопками-переключателями элемента, создадим специальный метод CSpinEdit::FastSwitching(), который нужно будет вызывать в таймере элемента. Ранее мы уже рассматривали подобный метод, когда рассматривали класс CListView для создания списков с полосой прокрутки. Если там этот метод нужен для прокрутки списка, то здесь он будет выполнять функцию для изменения значения в поле ввода в большую или меньшую сторону. Для более подробного изучения метода CSpinEdit::FastSwitching() смотрите листинг кода ниже. 

class CSpinEdit : public CElement
  {
private:
   //--- Ускоренная перемотка значений в поле ввода
   void              FastSwitching(void);
  };
//+------------------------------------------------------------------+
//| Таймер                                                           |
//+------------------------------------------------------------------+
void CSpinEdit::OnEventTimer(void)
  {
//--- Если элемент выпадающий
   if(CElement::IsDropdown())
     {
      ChangeObjectsColor();
      FastSwitching();
     }
   else
     {
      //--- Отслеживаем изменение цвета и перемотку значений, 
      //    только если форма не заблокирована
      if(!m_wnd.IsLocked())
        {
         ChangeObjectsColor();
         FastSwitching();
        }
     }
  }
//+------------------------------------------------------------------+
//| Ускоренная промотка значения в поле ввода                        |
//+------------------------------------------------------------------+
void CSpinEdit::FastSwitching(void)
  {
//--- Выйдем, если нет фокуса на элементе
   if(!CElement::MouseFocus())
      return;
//--- Вернём счётчик к первоначальному значению, если кнопка мыши отжата
   if(!m_mouse_state)
      m_timer_counter=SPIN_DELAY_MSC;
//--- Если же кнопка мыши нажата
   else
     {
      //--- Увеличим счётчик на установленный интервал
      m_timer_counter+=TIMER_STEP_MSC;
      //--- Выйдем, если меньше нуля
      if(m_timer_counter<0)
         return;
      //--- Получим текущее значение в поле ввода
      double current_value=::StringToDouble(m_edit.Description());
      //--- Если увеличить 
      if(m_spin_inc.State())
         SetValue(current_value+m_step_value);
      //--- Если уменьшить
      else if(m_spin_dec.State())
         SetValue(current_value-m_step_value);
      //--- Изменим значение, если кнопка-переключатель всё ещё нажата
      if(m_spin_inc.State() || m_spin_dec.State())
         m_edit.Description(::DoubleToString(GetValue(),m_digits));
     }
  }

 

 


Тест элемента «Поле ввода»

Все методы элемента управления «Поле ввода» реализованы. Теперь протестируем его в программе, которую подготовили до этого. В пользовательском классе приложения CProgram создаём один экземпляр класса CSpinEdit и объявляем метод для создания элемента «поле ввода».

//+------------------------------------------------------------------+
//| Класс для создания приложения                                    |
//+------------------------------------------------------------------+
class CProgram : public CWndEvents
  {
private:
   //--- Поля ввода
   CSpinEdit         m_spin_edit1;
   //---
private:
   //--- Поля ввода
#define SPINEDIT1_GAP_X       (150)
#define SPINEDIT1_GAP_Y       (75)
   bool              CreateSpinEdit1(const string text);
  };

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

//+------------------------------------------------------------------+
//| Создаёт поле ввода 1                                             |
//+------------------------------------------------------------------+
bool CProgram::CreateSpinEdit1(string text)
  {
//--- Сохраним указатель на окно
   m_spin_edit1.WindowPointer(m_window1);
//--- Координаты
   int x=m_window1.X()+SPINEDIT1_GAP_X;
   int y=m_window1.Y()+SPINEDIT1_GAP_Y;
//--- Значение
   double v=(m_spin_edit1.GetValue()==WRONG_VALUE) ? 4 : m_spin_edit1.GetValue();
//--- Установим свойства перед созданием
   m_spin_edit1.XSize(150);
   m_spin_edit1.YSize(18);
   m_spin_edit1.EditXSize(76);
   m_spin_edit1.MaxValue(1000);
   m_spin_edit1.MinValue(-1000);
   m_spin_edit1.StepValue(1);
   m_spin_edit1.SetDigits(0);
   m_spin_edit1.SetValue(v);
   m_spin_edit1.ResetMode(true);
   m_spin_edit1.AreaColor(clrWhiteSmoke);
   m_spin_edit1.LabelColor(clrBlack);
   m_spin_edit1.LabelColorLocked(clrSilver);
   m_spin_edit1.EditColorLocked(clrWhiteSmoke);
   m_spin_edit1.EditTextColor(clrBlack);
   m_spin_edit1.EditTextColorLocked(clrSilver);
   m_spin_edit1.EditBorderColor(clrSilver);
   m_spin_edit1.EditBorderColorLocked(clrSilver);
//--- Создадим элемент управления
   if(!m_spin_edit1.CreateSpinEdit(m_chart_id,m_subwin,text,x,y))
      return(false);
//--- Доступность будет зависеть от текущего состояния первого чекбокса
   m_spin_edit1.SpinEditState(m_checkbox1.CheckButtonState());
//--- Добавим объект в общий массив групп объектов
   CWndContainer::AddToElementsArray(0,m_spin_edit1);
   return(true);
  }

Как и другие элементы, метод CProgram::CreateSpinEdit1() нужно вызывать в главном методе создания графического интерфейса программы. В листинге кода ниже показана сокращённая версия метода:

//+------------------------------------------------------------------+
//| Создаёт торговую панель                                          |
//+------------------------------------------------------------------+
bool CProgram::CreateTradePanel(void)
  {
//--- Создание формы 1 для элементов управления
//--- Создание элементов управления:
//    Главное меню
//--- Контекстные меню
//--- Создание статусной строки
//--- Чекбоксы
//--- Поля ввода
   if(!CreateSpinEdit1("Spin Edit 1:"))
      return(false);
//--- Перерисовка графика
   m_chart.Redraw();
   return(true);
  }

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

//+------------------------------------------------------------------+
//| Обработчик событий                                               |
//+------------------------------------------------------------------+
void CProgram::OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam)
  {
//--- Событие нажатия на текстовой метке
   if(id==CHARTEVENT_CUSTOM+ON_CLICK_LABEL)
     {
      ::Print(__FUNCTION__," > id: ",id,"; lparam: ",lparam,"; dparam: ",dparam,"; sparam: ",sparam);
      //--- Если нажали на первом чекбоксе
      if(lparam==m_checkbox1.Id())
        {
         //--- Установить состояние второму чекбоксу и первому полю ввода
         m_checkbox2.CheckBoxState(m_checkbox1.CheckButtonState());
         m_spin_edit1.SpinEditState(m_checkbox1.CheckButtonState());
        }
     }
//--- Событие окончания ввода значения в поле ввода
   if(id==CHARTEVENT_CUSTOM+ON_END_EDIT)
     {
      ::Print(__FUNCTION__," > id: ",id,"; lparam: ",lparam,"; dparam: ",dparam,"; sparam: ",sparam);
     }
//--- События нажатия на кнопках-переключателях поля ввода
   if(id==CHARTEVENT_CUSTOM+ON_CLICK_INC || id==CHARTEVENT_CUSTOM+ON_CLICK_DEC)
     {
      ::Print(__FUNCTION__," > id: ",id,"; lparam: ",lparam,"; dparam: ",dparam,"; sparam: ",sparam);
     }
  }

Скомпилируйте программу и загрузите её на график. На скриншоте ниже показано, как выглядит графический интерфейс приложения на текущий момент:

Рис. 4. Тест элемента управления «поле ввода».

Рис. 4. Тест элемента управления «поле ввода».

 


Другие элементы управления с чекбоксами

В начале статьи упоминалось о том, что кроме элементов «чекбокс» и «поле ввода» будут рассмотрены ещё два элемента управления, такие как: «поле ввода с чекбоксом» и «комбобокс с чекбоксом». Поле ввода с чекбоксом это расширенный вариант класса CSpinEdit дополненный полями и методами класса CCheckBox, которые были рассмотрены в этой статье. 

 Рис. 5. Составные части элемента управления «Поле ввода с чекбоксом».

Рис. 5. Составные части элемента управления «Поле ввода с чекбоксом».


Комбобокс с чекбоксом — это аналогичное совмещение классов CComboBox и CCheckBox:

 Рис. 6. Составные части элемента управления «Комбобокс с чекбоксом».

Рис. 6. Составные части элемента управления «Комбобокс с чекбоксом».


Реализации классов CCheckBoxEdit (поле ввода с чекбоксом) и CCheckComboBox (комбобокс с чекбоксом) вы найдёте в приложенных к статье архивах с файлами библиотеки для самостоятельного изучения. Так как в элементе типа CCheckComboBox содержится выпадающий список, то в файл WndContainer.mqh нужно внести соответствующие дополнения, подобно тому, как это было сделано с другими элементами, у которых есть выпадающие части. В данном случае нужно, чтобы указатель на выпадающий список попал в персональный массив указателей m_drop_lists[]. Более подробное описание того, как это можно сделать, можете посмотреть в статье Графические интерфейсы V - Элемент Комбинированный список (Глава 3).

Здесь в качестве примера дополним тестовое приложение этими элементами, чтобы вы смогли увидеть, как это работает. Добавим ещё два чекбокса типа CCheckBox и по одному элементу типа CCheckBoxEdit и CCheckComboBox. Доступность элемента типа CCheckBoxEdit будет зависеть от состояния третьего чекбокса, а доступность элемента типа CCheckComboBox будет зависеть от состояния четвёртого чекбокса.

 Рис. 7. Тест элементов управления смешанных типов.

Рис. 7. Тест элементов управления смешанных типов.

 


Заключение

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

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

Список статей (глав) шестой части:
Прикрепленные файлы |
Последние комментарии | Перейти к обсуждению на форуме трейдеров (26)
Artyom Trishkin
Artyom Trishkin | 16 мая 2017 в 15:50
Anatoli Kazharski:
Даже если бы и всплыли, ничего страшного. Всегда можно всё исправить.

Так то естественно, но забирает дополнительное время... Потому и подумалось.

Впрочем - рад, что всё движется, и всё хорошо.

Вопрос: Толь, подскажи, планируется ли в очередном обновлении добавить возможность менять цвет фона ячеек CCanvasTable ?

Anatoli Kazharski
Anatoli Kazharski | 16 мая 2017 в 15:55
Artyom Trishkin:

...

Вопрос: Толь, подскажи, планируется ли в очередном обновлении добавить возможность менять цвет фона ячеек CCanvasTable ?

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

Artyom Trishkin
Artyom Trishkin | 16 мая 2017 в 15:58
Anatoli Kazharski:

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

Хорошо. Спасибо.
Oleksii Chepurnyi
Oleksii Chepurnyi | 20 июн. 2017 в 23:05

У меня такая беда - я никогда не слежу за раскладкой клавиатуры :) (Авторереключалка есть) Часто в SpinEdit забиваю "," вместо "."

//+------------------------------------------------------------------+
//| Обработка ввода значения в поле ввода                            |
//+------------------------------------------------------------------+
bool CSpinEdit::OnEndEdit(const string edited_object)
  {
//--- Выйдем, если чужое имя объекта
   if(m_edit.Name()!=edited_object)
      return(false);
//--- Получим введённое значение
   string entered_text=m_edit.Description();
   StringReplace(entered_text,",",".");
   double entered_value=::StringToDouble(entered_text);
//--- Проверим, скорректируем и запомним новое значение
   ChangeValue(entered_value);
//--- Отправим сообщение об этом
   ::EventChartCustom(m_chart_id,ON_END_EDIT,CElement::Id(),CElement::Index(),m_label.Description());
   return(true);
  }
Немного добавил. Думаю, не помешает...
Андрей
Андрей | 21 июл. 2021 в 19:03
спасибо за статью, а возможно ли между полями ввода для цены переключатся клавишей ТАБ ?
Регулярные выражения для трейдеров Регулярные выражения для трейдеров
Регулярные выражения (англ. regular expressions) — специальный язык для обработки текстов по заданному правилу, которое также называют шаблоном или маской регулярного выражения. В этой статье мы покажем, как обработать торговый отчет с помощью библиотеки RegularExpressions для MQL5, а также продемонстрируем результаты оптимизации с ее использованием.
Битва за скорость: QLUA vs MQL5 - почему MQL5 быстрее от 50 до 600 раз? Битва за скорость: QLUA vs MQL5 - почему MQL5 быстрее от 50 до 600 раз?
Для сравнения языков MQL5 и QLUA мы написали несколько тестов, которые замеряют скорость выполнения базовых операций. В тестах использовался компьютер с Windows 7 Professional 64 bit , MetaTrader 5 build 1340 и QUIK версии 7.2.0.45.
Графические интерфейсы VI: Элементы "Слайдер" и "Двухсторонний слайдер" (Глава 2) Графические интерфейсы VI: Элементы "Слайдер" и "Двухсторонний слайдер" (Глава 2)
В предыдущей статье разрабатываемая библиотека была пополнена сразу четырьмя довольно часто используемыми в графических интерфейсах элементами управления: «чекбокс», «поле ввода», «поле ввода с чекбоксом» и «комбобокс с чекбоксом». Вторая глава шестой части серии будет посвящена таким элементам управления, как слайдер и двухсторонний слайдер.
Универсальный торговый эксперт: Работа с пользовательскими трейлинг-стопами (часть 6) Универсальный торговый эксперт: Работа с пользовательскими трейлинг-стопами (часть 6)
Шестая часть статьи об универсальном торговом эксперте описывает работу с трейлинг-стопами. Прочитав ее, Вы узнаете, как с помощью унифицированных правил создать свой собственный модуль трейлинг-стопа и подключить его в торговый движок таким образом, чтобы управление позицией с его помощью происходило в автоматическом режиме.