Скачать MetaTrader 5

Графические интерфейсы III: Группы простых и многофункциональных кнопок (Глава 2)

30 марта 2016, 12:55
Anatoli Kazharski
2
2 909

Содержание

 


Введение

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

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

 


Разработка класса для создания групп простых кнопок

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

  1. создать группу из уже реализованного элемента типа CSimpleButton;
  2. создать группу из объектов примитивов типа CButton.

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

Создаём файл ButtonsGroup.mqh с классом CButtonsGroup в папке всех элементов (Controls) и сразу подключаем его к файлу WndContainer.mqh. В классе должны быть стандартные виртуальные методы и указатель на форму, как это было уже неоднократно показано во всех других ранее разработанных элементах. Поэтому не будем снова показывать их здесь, а сразу перейдём к описанию свойств и методов для их установки. 

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

Общие свойства кнопок:

  • высота;
  • цвет заблокированных кнопок;
  • цвет рамок в доступном и заблокированном режимах;
  • цвета текста в разных состояниях;
  • приоритет на нажатие левой кнопкой мыши.

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

  • состояние кнопки (нажата/отжата);
  • отступы от крайней точки формы;
  • текст;
  • ширина;
  • цвета кнопки в разных состояниях;
  • градиенты для кнопок.

Отступы для каждой кнопки позволят организовать расположение в любой последовательности:

Рис. 1.  Примеры расположения кнопок в группе.

Рис. 1.  Примеры расположения кнопок в группе.


Для объектов типа CButton и уникальных свойств кнопок объявим динамические массивы: 

//+------------------------------------------------------------------+
//| Класс для создания группы простых кнопок                         |
//+------------------------------------------------------------------+
class CButtonsGroup : public CElement
  {
private:
   //--- Объекты для создания кнопки
   CButton           m_buttons[];
   //--- Градиенты кнопок
   struct ButtonsGradients
     {
      color             m_buttons_color_array[];
     };
   ButtonsGradients  m_buttons_total[];
   //--- Свойства кнопок:
   //    Массивы для уникальных свойств кнопок
   bool              m_buttons_state[];
   int               m_buttons_x_gap[];
   int               m_buttons_y_gap[];
   string            m_buttons_text[];
   int               m_buttons_width[];
   color             m_buttons_color[];
   color             m_buttons_color_hover[];
   color             m_buttons_color_pressed[];
   //--- Высота кнопок
   int               m_button_y_size;
   //--- Цвет заблокированных кнопок
   color             m_back_color_off;
   //--- Цвет рамки в активном и заблокированных режимах
   color             m_border_color;
   color             m_border_color_off;
   //--- Цвета текста
   color             m_text_color;
   color             m_text_color_off;
   color             m_text_color_pressed;
   //--- Приоритет на нажатие левой кнопкой мыши
   int               m_buttons_zorder;
   //---
public:
   //--- Количество кнопок
   int               ButtonsTotal(void)                       const { return(::ArraySize(m_buttons));  }
   //--- (1) Высота кнопок
   void              ButtonYSize(const int y_size)                  { m_button_y_size=y_size;          }
   //--- (1) Цвета фона заблокированной кнопки и рамки ((2) доступен/(3) заблокирован)
   void              BackColorOff(const color clr)                  { m_back_color_off=clr;            }
   void              BorderColor(const color clr)                   { m_border_color=clr;              }
   void              BorderColorOff(const color clr)                { m_border_color_off=clr;          }
   //--- Цвета текста
   void              TextColor(const color clr)                     { m_text_color=clr;                }
   void              TextColorOff(const color clr)                  { m_text_color_off=clr;            }
   void              TextColorPressed(const color clr)              { m_text_color_pressed=clr;        }
  };

Размер динамических массивов будет определяться при формировании группы кнопок до её создания (установки на график). При каждом вызове метода CButtonsGroup::AddButton() массивы увеличиваются на один элемент, который заполняется переданными параметрами.

class CButtonsGroup : public CElement
  {
public:
   //--- Добавляет кнопку с указанными свойствами до создания
   void              AddButton(const int x_gap,const int y_gap,const string text,const int width,
                               const color button_color,const color button_color_hover,const color button_color_pressed);
  };
//+------------------------------------------------------------------+
//| Добавляет кнопку                                                 |
//+------------------------------------------------------------------+
void CButtonsGroup::AddButton(const int x_gap,const int y_gap,const string text,const int width,
                              const color button_color,const color button_color_hover,const color pressed_button_color)
  {
//--- Увеличим размер массивов на один элемент
   int array_size=::ArraySize(m_buttons);
   ::ArrayResize(m_buttons,array_size+1);
   ::ArrayResize(m_buttons_total,array_size+1);
   ::ArrayResize(m_buttons_state,array_size+1);
   ::ArrayResize(m_buttons_x_gap,array_size+1);
   ::ArrayResize(m_buttons_y_gap,array_size+1);
   ::ArrayResize(m_buttons_text,array_size+1);
   ::ArrayResize(m_buttons_width,array_size+1);
   ::ArrayResize(m_buttons_color,array_size+1);
   ::ArrayResize(m_buttons_color_hover,array_size+1);
   ::ArrayResize(m_buttons_color_pressed,array_size+1);
//--- Сохраним значения переданных параметров
   m_buttons_x_gap[array_size]         =x_gap;
   m_buttons_y_gap[array_size]         =y_gap;
   m_buttons_text[array_size]          =text;
   m_buttons_width[array_size]         =width;
   m_buttons_color[array_size]         =button_color;
   m_buttons_color_hover[array_size]   =button_color_hover;
   m_buttons_color_pressed[array_size] =pressed_button_color;
   m_buttons_state[array_size]         =false;
  }

В процессе создания группы кнопок, если до этого ни одной кнопки не было добавлено с помощью метода CButtonsGroup::AddButton(), процесс создания графического интерфейса будет остановлен, и в журнал выведется сообщение с подсказкой. Создание кнопок осуществляется в цикле (см. сокращённую версию метода CButtonsGroup::CreateButtons() в листинге кода ниже). 

class CButtonsGroup : public CElement
  {
public:
   //--- Методы для создания кнопки
   bool              CreateButtonsGroup(const long chart_id,const int subwin,const int x,const int y);
   //---
private:
   bool              CreateButtons(void);
  };
//+------------------------------------------------------------------+
//| Создаёт кнопки                                                   |
//+------------------------------------------------------------------+
bool CButtonsGroup::CreateButtons(void)
  {
//--- Координаты
   int l_x =m_x;
   int l_y =m_y;
//--- Получим количество кнопок
   int buttons_total=ButtonsTotal();
//--- Если нет ни одной кнопки в группе, сообщить об этом
   if(buttons_total<1)
     {
      ::Print(__FUNCTION__," > Вызов этого метода нужно осуществлять, "
              "когда в группе есть хотя бы одна кнопка! Воспользуйтесь методом CButtonsGroup::AddButton()");
      return(false);
     }
//--- Создадим указанное количество кнопок
   for(int i=0; i<buttons_total; i++)
     {
      //--- Формирование имени объекта
      //--- Расчёт координат
      //--- Установим кнопку
      //--- Установка свойств
      //--- Сохраним отступы от крайней точки панели, координаты и размеры
      //--- Инициализация массива градиента
      //--- Сохраним указатель объекта
     }
//---
   return(true);
  }

Сделаем два режима для этого типа группы кнопок. Для установки режима добавим в класс специальное поле и метод (см. листинг ниже). По умолчанию будет установлено значение false, и это будет означать, что включен режим, когда в группе могут быть отжаты все кнопки. Если установлено значение true, то в группе всегда будет нажата хотя бы одна кнопка.

class CButtonsGroup : public CElement
  {
public:
   //--- Режим радио-кнопок
   bool              m_radio_buttons_mode;
   //---
public:
   //--- Установка режима радио-кнопок
   void              RadioButtonsMode(const bool flag)              { m_radio_buttons_mode=flag;       }
  };

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

class CButtonsGroup : public CElement
  {
private:
   //--- Доступен/заблокирован
   bool              m_buttons_group_state;
   //---
public:
   //--- Общее состояние группы кнопок (доступен/заблокирован)
   bool              ButtonsGroupState(void)                  const { return(m_buttons_group_state);   }
   void              ButtonsGroupState(const bool state);
  };
//+------------------------------------------------------------------+
//| Изменение состояния кнопок                                       |
//+------------------------------------------------------------------+
void CButtonsGroup::ButtonsGroupState(const bool state)
  {
   m_buttons_group_state=state;
//---
   int buttons_total=ButtonsTotal();
   for(int i=0; i<buttons_total; i++)
     {
      m_buttons[i].State(false);
      m_buttons[i].Color((state)? m_text_color : m_text_color_off);
      m_buttons[i].BackColor((state)? m_buttons_color[i]: m_back_color_off);
      m_buttons[i].BorderColor((state)? m_border_color : m_border_color_off);
     }
//--- Нажать кнопку, если до блокировки была нажатая
   if(m_buttons_group_state)
     {
      if(m_selected_button_index!=WRONG_VALUE)
        {
         m_buttons_state[m_selected_button_index]=true;
         m_buttons[m_selected_button_index].Color(m_text_color_pressed);
         m_buttons[m_selected_button_index].BackColor(m_buttons_color_pressed[m_selected_button_index]);
        }
     }
  }

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

В методе переключения кнопки (см. листинг кода ниже) в самом начале стоит проверка на количество кнопок в группе. Если окажется, что кнопок вообще нет, то в журнал будет выведено сообщение с подсказкой. При этом выход из метода не будет осуществлен. Программа пойдёт дальше и столкнётся с ошибкой выхода из диапазона массива m_buttons_state[]. То есть, разработчик приложения должен самостоятельно изначально всё сделать правильно (добавив хотя бы одну кнопку в группу), чтобы такой ошибки не было. А вот уже после проверки, если в группе есть хотя бы одна кнопка, программа скорректирует переданный индекс в случае выхода из диапазона. Затем осуществляется изменение состояния кнопки по указанному индексу.

После этого в цикле все кнопки, кроме нажатой, отжимаются (устанавливается соответствующий цвет), при этом учитывается режим группы. Иными словами, если включен режим «радио-кнопки», когда хотя бы одна кнопка всегда должна быть нажата, то во время каждой итерации проходит проверка условия на равенство переданного индекса с текущим индексом цикла.

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

class CButtonsGroup : public CElement
  {
private:
   //--- (1) Текст и (2) индекс выделенной кнопки
   string            m_selected_button_text;
   int               m_selected_button_index;
   //---
public:
   //--- Возвращает (1) текст и (2) индекс выделенной кнопки
   string            SelectedButtonText(void)                 const { return(m_selected_button_text);  }
   int               SelectedButtonIndex(void)                const { return(m_selected_button_index); }
   //--- Переключает кнопку по указанному индексу
   void              SelectionButton(const int index);
  };
//+------------------------------------------------------------------+
//| Переключает кнопку по указанному индексу                         |
//+------------------------------------------------------------------+
void CButtonsGroup::SelectionButton(const int index)
  {
//--- Для проверки существования нажатой в группе кнопки
   bool check_pressed_button=false;
//--- Получим количество кнопок
   int buttons_total=ButtonsTotal();
//--- Если нет ни одной кнопки в группе, сообщить об этом
   if(buttons_total<1)
     {
      ::Print(__FUNCTION__," > Вызов этого метода нужно осуществлять, "
              "когда в группе есть хотя бы одна кнопка! Воспользуйтесь методом CButtonsGroup::AddButton()");
     }
//--- Скорректировать значение индекса, если выходит из диапазона
   int correct_index=(index>=buttons_total)? buttons_total-1 : (index<0)? 0 : index;
//--- Изменить состояние кнопки на противоположное
   m_buttons_state[correct_index]=(m_buttons_state[correct_index])? false : true;
//--- Пройдёмся в цикле по группе кнопок
   for(int i=0; i<buttons_total; i++)
     {
      //--- В зависимости от режима осуществляется соответствующая проверка
      bool condition=(m_radio_buttons_mode)? (i==correct_index) : (i==correct_index && m_buttons_state[i]);
      //--- Если условие исполнено, сделаем кнопку нажатой
      if(condition)
        {
         if(m_radio_buttons_mode)
            m_buttons_state[i]=true;
         //--- Есть нажатая кнопка
         check_pressed_button=true;
         //--- Установить цвета
         m_buttons[i].Color(m_text_color_pressed);
         m_buttons[i].BackColor(m_buttons_color_pressed[i]);
         CElement::InitColorArray(m_buttons_color_pressed[i],m_buttons_color_pressed[i],m_buttons_total[i].m_buttons_color_array);
        }
      //--- Если условие не исполнилось, сделаем кнопку отжатой
      else
        {
         //--- Установить отключенное состояние и цвета
         m_buttons_state[i]=false;
         m_buttons[i].Color(m_text_color);
         m_buttons[i].BackColor(m_buttons_color[i]);
         CElement::InitColorArray(m_buttons_color[i],m_buttons_color_hover[i],m_buttons_total[i].m_buttons_color_array);
        }
      //--- Обнулить штатное состояние кнопки
      m_buttons[i].State(false);
     }
//--- Если есть нажатая кнопка, сохраним её текст и индекс
   m_selected_button_text  =(check_pressed_button) ? m_buttons[correct_index].Description() : "";
   m_selected_button_index =(check_pressed_button) ? correct_index : WRONG_VALUE;
  }

Для обработки нажатия на кнопке группы создадим метод CButtonsGroup::OnClickButton(). Так же, как и во многих других одноимённых методах, рассмотренных ранее, здесь осуществляются проверки:

  • по имени;
  • по идентификатору. Для извлечения идентификатора из имени объекта используется метод CButtonsGroup::IdFromObjectName(). Код метода аналогичен одноимённым методам, рассмотренным ранее в других классах элементов управления, поэтому не будем здесь его приводить;
  • по текущему состоянию (доступен/заблокирован).

Если все проверки пройдены и программа не вышла из метода, с помощью рассмотренного ранее метода CButtonsGroup::SelectionButton() осуществляется переключение кнопки в группе. В самом конце метода в поток событий отправляется сообщение с данными нажатой кнопки, которое можно принять в обработчике пользовательского класса.

class CButtonsGroup : public CElement
  {
private:
   //--- Обработка нажатия на кнопку
   bool              OnClickButton(const string clicked_object);
   //--- Получение идентификатора из имени кнопки
   int               IdFromObjectName(const string object_name);
  };
//+------------------------------------------------------------------+
//| Обработчик события графика                                       |
//+------------------------------------------------------------------+
void CButtonsGroup::OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam)
  {
//--- Обработка события нажатия левой кнопки мыши на объекте
   if(id==CHARTEVENT_OBJECT_CLICK)
     {
      if(OnClickButton(sparam))
         return;
     }
  }
//+------------------------------------------------------------------+
//| Нажатие на кнопку в группе                                       |
//+------------------------------------------------------------------+
bool CButtonsGroup::OnClickButton(const string pressed_object)
  {
//--- Выйдем, если нажатие было не на пункте меню
   if(::StringFind(pressed_object,CElement::ProgramName()+"_buttons_",0)<0)
      return(false);
//--- Получим идентификатор из имени объекта
   int id=IdFromObjectName(pressed_object);
//--- Выйдем, если идентификаторы не совпадают
   if(id!=CElement::Id())
      return(false);
//--- Для проверки индекса
   int check_index=WRONG_VALUE;
//--- Проверим, было ли нажатие на одной из кнопок этой группы
   int buttons_total=ButtonsTotal();
//--- Выйти, если кнопки заблокированы
   if(!m_buttons_group_state)
     {
      for(int i=0; i<buttons_total; i++)
         m_buttons[i].State(false);
      //---
      return(false);
     }
//--- Если нажатие было, то запомним индекс
   for(int i=0; i<buttons_total; i++)
     {
      if(m_buttons[i].Name()==pressed_object)
        {
         check_index=i;
         break;
        }
     }
//--- Выйдем, если не было нажатия на кнопку в этой группе
   if(check_index==WRONG_VALUE)
      return(false);
//--- Переключить кнопку
   SelectionButton(check_index);
//--- Отправить сигнал об этом
   ::EventChartCustom(m_chart_id,ON_CLICK_BUTTON,CElement::Id(),m_selected_button_index,m_selected_button_text);
   return(true);
  }

Теперь нужно настроить реакцию кнопок группы на перемещение над ними курсора мыши и нажатие левой кнопки мыши. Для этого напишем метод CButtonsGroup::CheckPressedOverButton(), который будет вызываться в обработчике элемента при обработке события CHARTEVENT_MOUSE_MOVE. Перед вызовом осуществляется несколько проверок, таких как: (1) видим ли мы сейчас элемент, (2) доступен ли он, (3) доступна ли форма, (4) нажата ли левая кнопка мыши.

class CButtonsGroup : public CElement
  {
private:
   //--- Проверка нажатой левой кнопки мыши над кнопками группы
   void              CheckPressedOverButton(void);
  };
//+------------------------------------------------------------------+
//| Обработчик события графика                                       |
//+------------------------------------------------------------------+
void CButtonsGroup::OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam)
  {
//--- Обработка события перемещения курсора
   if(id==CHARTEVENT_MOUSE_MOVE)
     {
      //--- Выйти, если элемент скрыт
      if(!CElement::IsVisible())
         return;
      //--- Выйти, если кнопки заблокированы
      if(!m_buttons_group_state)
         return;
      //--- Определим фокус
      int x=(int)lparam;
      int y=(int)dparam;
      int buttons_total=ButtonsTotal();
      for(int i=0; i<buttons_total; i++)
        {
         m_buttons[i].MouseFocus(x>m_buttons[i].X() && x<m_buttons[i].X2() && 
                                 y>m_buttons[i].Y() && y<m_buttons[i].Y2());
        }
      //--- Выйти, если форма заблокирована
      if(m_wnd.IsLocked())
         return;
      //--- Выйти, если кнопка мыши не нажата
      if(sparam!="1")
         return;
      //--- Проверка нажатой левой кнопки мыши над кнопками группы
      CheckPressedOverButton();
      return;
     }
  }
//+------------------------------------------------------------------+
//| Проверка нажатой левой кнопки мыши над кнопками группы           |
//+------------------------------------------------------------------+
void CButtonsGroup::CheckPressedOverButton(void)
  {
   int buttons_total=ButtonsTotal();
//--- Установить цвет в зависимости от местоположения зажатой левой кнопки мыши
   for(int i=0; i<buttons_total; i++)
     {
      //--- Если есть фокус, то цвет нажатой кнопки
      if(m_buttons[i].MouseFocus())
         m_buttons[i].BackColor(m_buttons_color_pressed[i]);
      //--- Если фокуса нет, то...
      else
        {
         //--- ...если кнопка группы не нажата, присвоить цвет фона
         if(!m_buttons_state[i])
            m_buttons[i].BackColor(m_buttons_color[i]);
        }
     }
  }

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

В пользовательском классе создаём экземпляр класса группы кнопок CButtonsGroup. Объявляем метод CProgram::CreateButtonsGroup1() для его создания и отступы от крайней точки формы: 

class CProgram : public CWndEvents
  {
private:
   //--- Группа простых кнопок
   CButtonsGroup     m_buttons_group1;
   //---
private:
   //--- Группа простых кнопок
#define BUTTONS_GROUP1_GAP_X (7)
#define BUTTONS_GROUP1_GAP_Y (50)
   bool              CreateButtonsGroup1(void);
  };

Создадим группу из четырёх кнопок. Расположим их горизонтально. Для примера сделаем две кнопки красного цвета, а две — синего. Имплементация метода CProgram::CreateButtonsGroup1() показана в листинге кода ниже. После создания группы кнопок, если нужно, чтобы какая-то из них была сразу выделена, воспользуйтесь публичным методом CButtonsGroup::SelectionButton()

//+------------------------------------------------------------------+
//| Создаёт группу из простых кнопкок                                |
//+------------------------------------------------------------------+
bool CProgram::CreateButtonsGroup1(void)
  {
//--- Сохраним указатель на окно
   m_buttons_group1.WindowPointer(m_window);
//--- Координаты
   int x =m_window.X()+BUTTONS_GROUP1_GAP_X;
   int y =m_window.Y()+BUTTONS_GROUP1_GAP_Y;
//--- Свойства
   int    buttons_x_gap[]         ={0,72,144,216};
   string buttons_text[]          ={"BUTTON 1","BUTTON 2","BUTTON 3","BUTTON 4"};
   int    buttons_width[]         ={70,70,70,70};
   color  buttons_color[]         ={C'195,0,0',C'195,0,0',clrRoyalBlue,clrRoyalBlue};
   color  buttons_color_hover[]   ={C'255,51,51',C'255,51,51',C'85,170,255',C'85,170,255'};
   color  buttons_color_pressed[] ={C'135,0,0',C'135,0,0',C'50,100,135',C'50,100,135'};
//--- Установим свойства
   m_buttons_group1.TextColor(clrWhite);
   m_buttons_group1.TextColorPressed(clrGold);
//--- Добавим четыре кнопки в группу
   for(int i=0; i<4; i++)
      m_buttons_group1.AddButton(buttons_x_gap[i],0,buttons_text[i],buttons_width[i],
                                 buttons_color[i],buttons_color_hover[i],buttons_color_pressed[i]);
//--- Создать группу кнопок
   if(!m_buttons_group1.CreateButtonsGroup(m_chart_id,m_subwin,x,y))
      return(false);
//--- Выделим вторую кнопку в группе
   m_buttons_group1.SelectionButton(1);
//--- Добавим объект в общий массив групп объектов
   CWndContainer::AddToElementsArray(0,m_buttons_group1);
   return(true);
  }

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

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

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


С первой группой кнопок мы разобрались. Готовую версию класса CButtonsGroup можно скачать в конце статьи. Следующий на очереди элемент управления – группа радио-кнопок. 

 


Разработка класса для создания групп радио-кнопок

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

Каждая радио-кнопка будет собираться из трёх объектов примитивов:

  1. фон;
  2. ярлык;
  3. текстовая метка.

Рис. 3. Составные части радио-кнопок.

Рис. 3. Составные части радио-кнопок.


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

Общие свойства:

  • Цвет фона и рамки
  • Цвета текста
  • Высота
  • Ярлыки кнопки в (1) активном, (2) отключенном и (3) заблокированном состоянии
  • Приоритет на нажатие левой кнопкой мыши

Уникальные свойства:

  • Текст
  • Ширина
  • Состояние. Только одна кнопка в группе может быть нажата
  • Отступы. Также, как и простые кнопки в группе, радио-кнопки можно расположить в любом порядке (мозаикой, горизонтально, вертикально и т.д.)
  • Градиенты для текстовых меток


Ниже показано, как это всё выглядит в коде класса CRadioButtons. Размер массивов уникальных свойств и заполнение значениями осуществляются с помощью публичного метода CRadioButtons::AddButton() перед созданием элемента в пользовательском классе. Если ни одной кнопки не добавлено, то создание графического интерфейса программы будет прервано. Это уже было показано на примере разработки класса для создания группы простых кнопок, поэтому не будем больше на этом останавливаться подробно.

//+------------------------------------------------------------------+
//| Класс для создания группы радио-кнопок                           |
//+------------------------------------------------------------------+
class CRadioButtons : public CElement
  {
private:
   //--- Градиенты текстовых меток
   struct LabelsGradients
     {
      color             m_labels_color_array[];
     };
   LabelsGradients   m_labels_total[];
   //--- Свойства кнопок:
   //    (1) Цвет и (2) приоритет фона на нажатие левой кнопкой мыши
   color             m_area_color;
   int               m_area_zorder;
   //--- Массивы для уникальных свойств кнопок
   bool              m_buttons_state[];
   int               m_buttons_x_gap[];
   int               m_buttons_y_gap[];
   int               m_buttons_width[];
   string            m_buttons_text[];
   //--- Высота кнопок
   int               m_button_y_size;
   //--- Ярлыки кнопки в активном, отключенном и заблокированном состоянии
   string            m_icon_file_on;
   string            m_icon_file_off;
   string            m_icon_file_on_locked;
   string            m_icon_file_off_locked;
   //--- Цвета текста
   color             m_text_color;
   color             m_text_color_off;
   color             m_text_color_hover;
   //--- Приоритет на нажатие левой кнопкой мыши
   int               m_buttons_zorder;
   //---
public:
   //--- Установка ярлыков для кнопки в активном, отключенном и заблокированном состояниях
   void              IconFileOn(const string file_path)           { m_icon_file_on=file_path;         }
   void              IconFileOff(const string file_path)          { m_icon_file_off=file_path;        }
   void              IconFileOnLocked(const string file_path)     { m_icon_file_on_locked=file_path;  }
   void              IconFileOffLocked(const string file_path)    { m_icon_file_off_locked=file_path; }
   //--- (1) Цвета фона, (2) цвета текста
   void              AreaColor(const color clr)                   { m_area_color=clr;                 }
   void              TextColor(const color clr)                   { m_text_color=clr;                 }
   void              TextColorOff(const color clr)                { m_text_color_off=clr;             }
   void              TextColorHover(const color clr)              { m_text_color_hover=clr;           }

   //--- Добавляет кнопку с указанными свойствами до создания
   void              AddButton(const int x_gap,const int y_gap,const string text,const int width);
  };

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

class CRadioButtons : public CElement
  {
private:
   //--- Объекты для создания кнопки
   CRectLabel        m_area[];
   CBmpLabel         m_icon[];
   CLabel            m_label[];
   //---
public:
   //--- Методы для создания кнопки
   bool              CreateRadioButtons(const long chart_id,const int window,const int x,const int y);
   //---
private:
   bool              CreateArea(const int index);
   bool              CreateRadio(const int index);
   bool              CreateLabel(const int index);
   //---
public:
   //--- Количество кнопок
   int               RadioButtonsTotal(void)                const { return(::ArraySize(m_icon));      }
  };
//+------------------------------------------------------------------+
//| Создаёт группу объектов Кнопки                                   |
//+------------------------------------------------------------------+
bool CRadioButtons::CreateRadioButtons(const long chart_id,const int window,const int x,const int y)
  {
//--- Выйти, если нет указателя на форму
   if(::CheckPointer(m_wnd)==POINTER_INVALID)
     {
      ::Print(__FUNCTION__," > Перед созданием группы радио-кнопок классу нужно передать "
              "указатель на форму: CButtonsGroup::WindowPointer(CWindow &object)");
      return(false);
     }
//--- Инициализация переменных
   m_id       =m_wnd.LastId()+1;
   m_chart_id =chart_id;
   m_subwin   =window;
   m_x        =x;
   m_y        =y;
//--- Получим количество кнопок в группе
   int radio_buttons_total=RadioButtonsTotal();
//--- Если нет ни одной кнопки в группе, сообщить об этом
   if(radio_buttons_total<1)
     {
      ::Print(__FUNCTION__," > Вызов этого метода нужно осуществлять, "
              "когда в группе есть хотя бы одна кнопка! Воспользуйтесь методом CRadioButtons::AddButton()");
      return(false);
     }
//--- Установим группу кнопок
   for(int i=0; i<radio_buttons_total; i++)
     {
      CreateArea(i);
      CreateRadio(i);
      CreateLabel(i);
      //--- Обнуление фокуса
      m_area[i].MouseFocus(false);
     }
//--- Скрыть элемент, если окно диалоговое или оно минимизировано
   if(m_wnd.WindowType()==W_DIALOG || m_wnd.IsMinimized())
      Hide();
//---
   return(true);
  }

Методы для изменения состояния элемента (доступен/заблокирован) и для переключения кнопки по указанному индексу в этом классе тоже должны быть. Здесь они даже немного проще, чем в классе CButtonsGroup, так как у группы радио-кнопок есть только один режим, поэтому с кодом этих методов можете ознакомиться в приложенных к статье файлах. Кроме этого, так же как и в классе для простых кнопок, необходимы методы для получения текста и индекса выделенной кнопки.

class CButtonsGroup : public CElement
  {
private:
   //--- (1) Текст и (2) индекс выделенной кнопки
   string            m_selected_button_text;
   int               m_selected_button_index;
   //--- Доступен/заблокирован
   bool              m_buttons_group_state;
   //---
public:
   //--- Общее состояние группы кнопок (доступен/заблокирован)
   bool              ButtonsGroupState(void)                  const { return(m_buttons_group_state);   }
   void              ButtonsGroupState(const bool state);
   //--- Возвращает (1) текст и (2) индекс выделенной кнопки
   string            SelectedButtonText(void)                 const { return(m_selected_button_text);  }
   int               SelectedButtonIndex(void)                const { return(m_selected_button_index); }
   //--- Переключает кнопку по указанному индексу
   void              SelectionButton(const int index);
  };

Для отправки сообщения о нажатии на элементах управления, которые визуально выглядят на форме как текстовая метка, будем применять новый идентификатор ON_CLICK_LABEL. Добавим его в файл Defines.mqh:

#define ON_CLICK_LABEL            (10) // Нажатие на текстовой метке

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

class CButtonsGroup : public CElement
  {
private:
   //--- Обработка нажатия на кнопку
   bool              OnClickButton(const string pressed_object);
   //--- Получение идентификатора из имени радио-кнопки
   int               IdFromObjectName(const string object_name);
  };
//+------------------------------------------------------------------+
//| Обработчик события графика                                       |
//+------------------------------------------------------------------+
void CRadioButtons::OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam)
  {
//--- Обработка события нажатия левой кнопки мыши на объекте
   if(id==CHARTEVENT_OBJECT_CLICK)
     {
      //--- Переключить кнопку
      if(OnClickButton(sparam))
         return;
     }
  }
//+------------------------------------------------------------------+
//| Нажатие на радио-кнопку                                          |
//+------------------------------------------------------------------+
bool CRadioButtons::OnClickButton(const string pressed_object)
  {
//--- Выйдем, если нажатие было не на пункте меню
   if(::StringFind(pressed_object,CElement::ProgramName()+"_radio_area_",0)<0)
      return(false);
//--- Получим идентификатор и индекс из имени объекта
   int id=IdFromObjectName(pressed_object);
//--- Выйдем, если нажали не на пункте, к которому это контекстное меню привязано
   if(id!=CElement::Id())
      return(false);
//--- Для проверки индекса
   int check_index=WRONG_VALUE;
//--- Выйти, если кнопки заблокированы
   if(!m_radio_buttons_state)
      return(false);
//--- Если нажатие было, то запомним индекс
   int radio_buttons_total=RadioButtonsTotal();
   for(int i=0; i<radio_buttons_total; i++)
     {
      if(m_area[i].Name()==pressed_object)
        {
         check_index=i;
         break;
        }
     }
//--- Выйдем, если не было нажатия на кнопку в этой группе или
//    если это уже выделенная радио-кнопка
   if(check_index==WRONG_VALUE || check_index==m_selected_button_index)
      return(false);
//--- Переключить кнопку
   SelectionRadioButton(check_index);
//--- Отправить сигнал об этом
   ::EventChartCustom(m_chart_id,ON_CLICK_LABEL,CElement::Id(),check_index,m_selected_button_text);
   return(true);
  }

Всё готово для тестов. Добавим в эксперта, в котором тестировали ранее группу из простых кнопок, четыре группы из радио-кнопок (CRadioButtons) и ещё одну группу из простых кнопок (CButtonsGroup). 

class CProgram : public CWndEvents
  {
private:
   //--- Группа радио-кнопок 1
   CRadioButtons     m_radio_buttons1;
   //--- Группа простых кнопок 2
   CButtonsGroup     m_buttons_group2;
   //--- Группы радио-кнопок 2,3,4
   CRadioButtons     m_radio_buttons2;
   CRadioButtons     m_radio_buttons3;
   CRadioButtons     m_radio_buttons4;
   //---
private:
   //--- Группа радио-кнопок 1
#define RADIO_BUTTONS1_GAP_X     (7)
#define RADIO_BUTTONS1_GAP_Y     (75)
   bool              CreateRadioButtons1();
   //--- Группа простых кнопок 2
#define BUTTONS_GROUP2_GAP_X     (7)
#define BUTTONS_GROUP2_GAP_Y     (100)
   bool              CreateButtonsGroup2(void);
   //--- Группы радио-кнопок 2,3,4
#define RADIO_BUTTONS2_GAP_X     (7)
#define RADIO_BUTTONS2_GAP_Y     (125)
   bool              CreateRadioButtons2();
#define RADIO_BUTTONS3_GAP_X     (105)
#define RADIO_BUTTONS3_GAP_Y     (125)
   bool              CreateRadioButtons3();
#define RADIO_BUTTONS4_GAP_X     (203)
#define RADIO_BUTTONS4_GAP_Y     (125)
   bool              CreateRadioButtons4();
  };
//+------------------------------------------------------------------+
//| Создаёт торговую панель                                          |
//+------------------------------------------------------------------+
bool CProgram::CreateTradePanel(void)
  {
//--- Создание формы для элементов управления
//--- Создание элементов управления:
//    Главное меню
//--- Контекстные меню
//--- Группа простых кнопок 1
//--- Группа радио-кнопок 1
   if(!CreateRadioButtons1())
      return(false);
//--- Группа простых кнопок 2
   if(!CreateButtonsGroup2())
      return(false);
   //--- Группы радио-кнопок 2,3,4
   if(!CreateRadioButtons2())
      return(false);
   if(!CreateRadioButtons3())
      return(false);
   if(!CreateRadioButtons4())
      return(false);
//--- Перерисовка графика
   m_chart.Redraw();
   return(true);
  }

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

//+------------------------------------------------------------------+
//| Создаёт группу радио-кнопок 1                                    |
//+------------------------------------------------------------------+
bool CProgram::CreateRadioButtons1(void)
  {
//--- Передать объект панели
   m_radio_buttons1.WindowPointer(m_window);
//--- Координаты
   int x =m_window.X()+RADIO_BUTTONS1_GAP_X;
   int y =m_window.Y()+RADIO_BUTTONS1_GAP_Y;
//--- Свойства
   int    buttons_x_offset[] ={0,98,196};
   int    buttons_y_offset[] ={0,0,0};
   string buttons_text[]     ={"Radio Button 1","Radio Button 2","Radio Button 3"};
   int    buttons_width[]    ={92,92,92};
//---
   for(int i=0; i<3; i++)
      m_radio_buttons1.AddButton(buttons_x_offset[i],buttons_y_offset[i],buttons_text[i],buttons_width[i]);
//--- Создать группу кнопок
   if(!m_radio_buttons1.CreateRadioButtons(m_chart_id,m_subwin,x,y))
      return(false);
//--- Выделим вторую кнопку в группе
   m_radio_buttons1.SelectedRadioButton(1);
//--- Заблокируем радио-кнопки
   m_radio_buttons1.RadioButtonsState(false);
//--- Добавим объект в общий массив групп объектов
   CWndContainer::AddToElementsArray(0,m_radio_buttons1);
   return(true);
  }

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

//+------------------------------------------------------------------+
//| Обработчик событий                                               |
//+------------------------------------------------------------------+
void CProgram::OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam)
  {
//--- Событие нажатия на кнопке
   if(id==CHARTEVENT_CUSTOM+ON_CLICK_BUTTON)
     {
      ::Print("id: ",id,"; lparam: ",lparam,"; dparam: ",dparam,"; sparam: ",sparam);
      //--- Если идентификатор от второй группы простых кнопок и 
      //    текст выделенной кнопки в этой группе совпадает со строковым параметром сообщения
      if(lparam==m_buttons_group2.Id() && sparam==m_buttons_group2.SelectedButtonText())
        {
         //--- Если индекс первой кнопки, разблокируем указанные элементы
         if((int)dparam==0)
           {
            m_buttons_group1.ButtonsGroupState(true);
            m_radio_buttons1.RadioButtonsState(true);
           }
         //--- Если индекс второй кнопки, заблокируем указанные элементы
         else
           {
            m_buttons_group1.ButtonsGroupState(false);
            m_radio_buttons1.RadioButtonsState(false);
           }
        }
      return;
     }     
  }

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

Рис. 4. Тест элемента «Радио-кнопки».

Рис. 4. Тест элемента «Радио-кнопки».


Разработка класса для создания элемента «Группа радио-кнопок» завершена. Полную версию Вы можете скачать в приложенных к статье файлах. 

 


Разработка класса для создания групп кнопок с картинками

Ранее был создан класс CIconButton для создания элемента «Кнопка с картинкой». Сейчас реализуем элемент управления, который позволит создавать группу таких кнопок. Уже в привычной папке Controls, где расположены другие созданные ранее файлы с классами элементов управления, создайте файл IconButtonsGroup.mqh с классом CIconButtonsGroup, в котором сразу можно объявить и имплементировать стандартные виртуальные методы. Подключите этот файл к библиотеке (файл WndContainer.mqh).

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

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

//+------------------------------------------------------------------+
//| Класс для создания группы кнопок с картинками                    |
//+------------------------------------------------------------------+
class CIconButtonsGroup : public CElement
  {
private:
   //--- Указатель на форму, к которой элемент присоединён
   CWindow          *m_wnd;
   //--- Объекты для создания кнопки
   CButton           m_buttons[];
   CBmpLabel         m_icons[];
   CLabel            m_labels[];
   //--- Градиенты текстовых меток
   struct IconButtonsGradients
     {
      color             m_back_color_array[];
      color             m_label_color_array[];
     };
   IconButtonsGradients   m_icon_buttons_total[];
   //--- Свойства кнопок:
   //    Массивы для уникальных свойств кнопок
   bool              m_buttons_state[];
   int               m_buttons_x_gap[];
   int               m_buttons_y_gap[];
   string            m_buttons_text[];
   int               m_buttons_width[];
   string            m_icon_file_on[];
   string            m_icon_file_off[];
   //--- Высота кнопок
   int               m_buttons_y_size;
   //--- Цвет фона в различных режимах
   color             m_back_color;
   color             m_back_color_off;
   color             m_back_color_hover;
   color             m_back_color_pressed;
   //--- Цвет рамки
   color             m_border_color;
   color             m_border_color_off;
   //--- Отступы ярлыка
   int               m_icon_x_gap;
   int               m_icon_y_gap;
   //--- Текст и отступы текстовой метки
   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_pressed;
   //--- (1) Текст и (2) индекс выделенной кнопки
   string            m_selected_button_text;
   int               m_selected_button_index;
   //--- Общий приоритет для некликабельных объектов
   int               m_zorder;
   //--- Приоритет на нажатие левой кнопкой мыши
   int               m_buttons_zorder;
   //--- Доступен/заблокирован
   bool              m_icon_buttons_state;
   //---
public:
                     CIconButtonsGroup(void);
                    ~CIconButtonsGroup(void);
   //--- Методы для создания кнопки
   bool              CreateIconButtonsGroup(const long chart_id,const int window,const int x,const int y);
   //---
private:
   bool              CreateButton(const int index);
   bool              CreateIcon(const int index);
   bool              CreateLabel(const int index);
   //---
public:
   //--- (1) Сохраняет указатель формы, (2) высота кнопок, (3) количество кнопок,
   //    (4) общее состояние кнопки (доступен/заблокирован)
   void              WindowPointer(CWindow &object)               { m_wnd=::GetPointer(object);      }
   void              ButtonsYSize(const int y_size)               { m_buttons_y_size=y_size;         }
   int               IconButtonsTotal(void)                 const { return(::ArraySize(m_icons));    }
   bool              IconButtonsState(void)                 const { return(m_icon_buttons_state);    }
   void              IconButtonsState(const bool state);
   //--- Цвета фона кнопки
   void              BackColor(const color clr)                   { m_back_color=clr;                }
   void              BackColorOff(const color clr)                { m_back_color_off=clr;            }
   void              BackColorHover(const color clr)              { m_back_color_hover=clr;          }
   void              BackColorPressed(const color clr)            { m_back_color_pressed=clr;        }
   //--- Отступы ярлыка
   void              IconXGap(const int x_gap)                    { m_icon_x_gap=x_gap;              }
   void              IconYGap(const int y_gap)                    { m_icon_y_gap=y_gap;              }
   //--- Отступы текстовой метки
   void              LabelXGap(const int x_gap)                   { m_label_x_gap=x_gap;             }
   void              LabelYGap(const int y_gap)                   { m_label_y_gap=y_gap;             }
   //--- Возвращает (1) текст и (2) индекс выделенной кнопки
   string            SelectedButtonText(void)               const { return(m_selected_button_text);  }
   int               SelectedButtonIndex(void)              const { return(m_selected_button_index); }
   //--- Переключает радио-кнопку по указанному индексу
   void              SelectedRadioButton(const int index);

   //--- Добавляет кнопку с указанными свойствами до создания
   void              AddButton(const int x_gap,const int y_gap,const string text,
                               const int width,const string icon_file_on,const string icon_file_off);
   //--- Изменение цвета
   void              ChangeObjectsColor(void);
   //---
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);
   //---
private:
   //--- Обработка нажатия на кнопку
   bool              OnClickButton(const string pressed_object);
   //--- Проверка нажатой левой кнопки мыши над кнопками группы
   void              CheckPressedOverButton(void);
   //--- Получение идентификатора из имени радио-кнопки
   int               IdFromObjectName(const string object_name);
  };

Далее протестируем этот элемент управления. Для примера создадим одну группу кнопок с картинками на форме того же эксперта, в котором тестировали ранее группы кнопок типа CButtonsGroup и CRadioButtons. Для этого в пользовательский класс добавьте строки кода, как это показано в листинге ниже.

class CProgram : public CWndEvents
  {
private:
   //--- Группа кнопок с картинками 1
   CIconButtonsGroup m_icon_buttons_group1;
   //---
private:
   //--- Группа кнопок с картинками 1
#define IBUTTONS_GROUP1_GAP_X    (7)
#define IBUTTONS_GROUP1_GAP_Y    (190)
   bool              CreateIconButtonsGroup1(void);
  };
//+------------------------------------------------------------------+
//| Создаёт торговую панель                                          |
//+------------------------------------------------------------------+
bool CProgram::CreateTradePanel(void)
  {
//--- Создание формы для элементов управления
//--- Создание элементов управления:
//    Главное меню
//--- Контекстные меню
//--- Группа простых кнопок 1
//--- Группа радио-кнопок 1
//--- Группа простых кнопок 2
//--- Группы радио-кнопок 2,3,4
//--- Группа кнопок с картинками 1
   if(!CreateIconButtonsGroup1())
      return(false);
//--- Перерисовка графика
   m_chart.Redraw();
   return(true);
  }

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

Рис. 5. Тест группы кнопок с картинками.

Рис. 5. Тест группы кнопок с картинками.


Разработка класса для создания элемента «Группа кнопок с картинками» завершена. Полную версию вы можете скачать в приложенных к статье файлах.

 


Заключение

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

Рис. 6. Структура библиотеки на текущей стадии разработки.

Рис. 6. Структура библиотеки на текущей стадии разработки.


В следующей (четвёртой) части продолжим развивать библиотеку и рассмотрим следующие темы: 

  • Многооконный режим
  • Система управления приоритетами на нажатие левой кнопкой мыши на графических объектах
  • Информационные элементы интерфейса «Статусная строка» и "Всплывающие подсказки"

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

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

Прикрепленные файлы |
Alexander
Alexander | 14 май 2016 в 18:46
При компиляции файла SplitButton.mqh выдает ошибку "'return' - cannot convert from const pointer to nonconst pointer SplitButton.mqh 90 65"

Anatoli Kazharski
Anatoli Kazharski | 14 май 2016 в 18:58
Alexander:
При компиляции файла SplitButton.mqh выдает ошибку "'return' - cannot convert from const pointer to nonconst pointer SplitButton.mqh 90 65"

Да, после последнего обновления терминала появилась такая ошибка. Правила "игры" немного изменились. Исправить можно просто удалив спецификатор const.

Перейдите к строке с ошибкой и замените эту строку:

CContextMenu     *GetContextMenuPointer(void)        const { return(::GetPointer(m_drop_menu)); }

На эту: 

CContextMenu     *GetContextMenuPointer(void)              { return(::GetPointer(m_drop_menu)); }

//---

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

Спасибо за сообщение. 

В MetaTrader 5 добавлена хеджинговая система учета позиций В MetaTrader 5 добавлена хеджинговая система учета позиций

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

Графические интерфейсы III: Простые и многофункциональные кнопки (Глава 1) Графические интерфейсы III: Простые и многофункциональные кнопки (Глава 1)

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

Графические интерфейсы IV: Информационные элементы интерфейса (Глава 1) Графические интерфейсы IV: Информационные элементы интерфейса (Глава 1)

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

Калькулятор сигналов Калькулятор сигналов

Калькулятор сигналов работает прямо из терминала MetaTrader 5, и это большое его преимущество, так как терминал осуществляет предварительный отбор и сортировку сигналов. Таким образом, пользователь видит в терминале MetaTrader 5 только сигналы с максимальной совместимостью с его торговым счётом.