DoEasy. Элементы управления (Часть 28): Стили полосы в элементе управления "ProgressBar"

Artyom Trishkin | 2 декабря, 2022

Содержание


Концепция

Элемент управления ProgressBar, создаваемый нами для библиотеки, пока имеет один стиль отображения полосы прогресса — непрерывная линия (Continuous). Но у такого элемента управления есть ещё два стиля отображения — сегментированные блоки (Blocks) и непрерывная прокрутка блока в объекте (Marquee). Если со стилем Blocks всё достаточно понятно (вместо непрерывной линии рисуются отдельно расположенные блоки), то стиль Marquee можно использовать в случае, если заранее неизвестно количество итераций, которые необходимо визуально отобразить при помощи элемента управления ProgressBar. В таком случае во время хода выполнения внутри объекта будет постоянно прокручиваться единственный блок, равный половине ширины полосы прогресса. 

Помимо создания этих двух новых стилей, добавим к полосе прогресса текст, выводимый внутри неё. Сам текст будет представлен обычным объектом класса CLabel библиотеки, и привязан он будет не к объекту-полосе прогресса, а к объекту-подложке, который является основанием элемента управления ProgressBar. Текст будет выводиться внутри полностью прозрачного объекта-текстовой метки, имеющего размер, равный ширине и высоте полосы прогресса, и этот объект будет всегда находиться на переднем плане — всегда выше всех объектов элемента управления ProgressBar.

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


Доработка классов библиотеки

Перед тем, как начать разработку новых стилей полосы прогресса элемента управления ProgressBar, упростим конструкторы базового графического элемента. В защищённом и параметрическом конструкторах мы, после создания объекта, устанавливаем значения его свойств. В обоих конструкторах это длинный список из более сотни различных параметров графического элемента библиотеки, которые постоянно добавляются при создании новых объектов. Таким образом, нам постоянно приходится по два раза прописывать установку свойств объекта — в защищённом и в параметрическом конструкторах класса CGCnvElement. Логично будет вынести установку всех этих свойств в отдельный метод инициализации. При этом, если какие-то свойства должны по-разному устанавливаться в разных конструкторах, то они просто будут передаваться в формальных параметрах нового метода, и указываться в них.

В файле \MQL5\Include\DoEasy\Objects\Graph\GCnvElement.mqh в структуру объекта добавим новые свойства:

   struct SData
     {
      //--- Целочисленные свойства объекта
      int            id;                                       // Идентификатор элемента
      int            type;                                     // Тип графического элемента
      
      //---...
      //---...

      //---
      int            group;                                    // Группа, к которой принадлежит графический элемент
      int            tab_size_mode;                            // Режим установки размера вкладок
      int            tab_page_number;                          // Порядковый номер вкладки
      int            tab_page_row;                             // Номер ряда вкладки
      int            tab_page_column;                          // Номер столбца вкладки
      int            progress_bar_minimum;                     // Нижняя граница диапазона, в котором действует ProgressBar
      int            progress_bar_maximum;                     // Верхняя граница диапазона, в котором действует ProgressBar
      int            progress_bar_step;                        // Величина приращения значения ProgressBar для его перерисовки
      int            progress_bar_style;                       // Стиль элемента ProgressBar
      int            progress_bar_value;                       // Текущее значение элемента ProgressBar в диапазоне от Min до Max
      int            progress_bar_marquee_speed;               // Скорость анимации полосы прогресса при стиле Marquee
      //---
      ulong          tooltip_initial_delay;                    // Задержка отображения подсказки
      ulong          tooltip_auto_pop_delay;                   // Длительность отображения подсказки
      ulong          tooltip_reshow_delay;                     // Задержка отображения новой подсказки одного элемента
      bool           tooltip_show_always;                      // Отображать подсказку в неактивном окне
      int            tooltip_icon;                             // Значок, отображаемый в подсказке
      bool           tooltip_is_balloon;                       // Подсказка в форме "облачка"
      bool           tooltip_use_fading;                       // Угасание при отображении и скрытии подсказки
      //--- Вещественные свойства объекта

      //--- Строковые свойства объекта
      uchar          name_obj[64];                             // Имя объекта-графического элемента
      uchar          name_res[64];                             // Имя графического ресурса
      uchar          text[256];                                // Текст графического элемента
      uchar          descript[256];                            // Описание графического элемента
      uchar          tooltip_title[256];                       // Заголовок подсказки элемента
      uchar          tooltip_text[256];                        // Текст подсказки элемента
     };
   SData             m_struct_obj;                             // Структура объекта

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

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

//--- Сохраняет цвета в массив цветов фона
   void              SaveColorsBG(color &colors[])                         { this.CopyArraysColors(this.m_array_colors_bg,colors,DFUN);      }
   void              SaveColorsBGMouseDown(color &colors[])                { this.CopyArraysColors(this.m_array_colors_bg_dwn,colors,DFUN);  }
   void              SaveColorsBGMouseOver(color &colors[])                { this.CopyArraysColors(this.m_array_colors_bg_ovr,colors,DFUN);  }
   void              SaveColorsBGInit(color &colors[])                     { this.CopyArraysColors(this.m_array_colors_bg_init,colors,DFUN); }

//--- Инициализирует значения свойств
   void              Initialize(const ENUM_GRAPH_ELEMENT_TYPE element_type,
                                const int element_id,const int element_num,
                                const int x,const int y,const int w,const int h,
                                const string descript,const bool movable,const bool activity);

public:

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


Удалим в каждом из конструкторов длинный список установки свойств объекта, заменив его на вызов метода инициализации:

//+------------------------------------------------------------------+
//| Параметрический конструктор                                      |
//+------------------------------------------------------------------+
CGCnvElement::CGCnvElement(const ENUM_GRAPH_ELEMENT_TYPE element_type,
                           CGCnvElement *main_obj,CGCnvElement *base_obj,
                           const int      element_id,
                           const int      element_num,
                           const long     chart_id,
                           const int      wnd_num,
                           const string   descript,
                           const int      x,
                           const int      y,
                           const int      w,
                           const int      h,
                           const color    colour,
                           const uchar    opacity,
                           const bool     movable=true,
                           const bool     activity=true,
                           const bool     redraw=false) : m_shadow(false)
  {
   this.SetTypeElement(element_type);
   this.m_type=OBJECT_DE_TYPE_GELEMENT; 
   this.m_element_main=main_obj;
   this.m_element_base=base_obj;
   this.m_chart_color_bg=(color)::ChartGetInteger((chart_id==NULL ? ::ChartID() : chart_id),CHART_COLOR_BACKGROUND);
   this.m_name=this.CreateNameGraphElement(element_type);
   this.m_chart_id=(chart_id==NULL || chart_id==0 ? ::ChartID() : chart_id);
   this.m_subwindow=wnd_num;
   this.SetFont(DEF_FONT,DEF_FONT_SIZE);
   this.m_text_anchor=0;
   this.m_text_x=0;
   this.m_text_y=0;
   this.SetBackgroundColor(colour,true);
   this.SetOpacity(opacity);
   this.m_shift_coord_x=0;
   this.m_shift_coord_y=0;
   if(::ArrayResize(this.m_array_colors_bg,1)==1)
      this.m_array_colors_bg[0]=this.BackgroundColor();
   if(::ArrayResize(this.m_array_colors_bg_dwn,1)==1)
      this.m_array_colors_bg_dwn[0]=this.BackgroundColor();
   if(::ArrayResize(this.m_array_colors_bg_ovr,1)==1)
      this.m_array_colors_bg_ovr[0]=this.BackgroundColor();
   if(this.Create(chart_id,wnd_num,x,y,w,h,redraw))
     {
      this.Initialize(element_type,element_id,element_num,x,y,w,h,descript,movable,activity);
      this.SetVisibleFlag(false,false);
     }
   else
     {
      ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_FAILED_CREATE_ELM_OBJ),"\"",this.TypeElementDescription(element_type),"\" ",this.NameObj());
     }
  }
//+------------------------------------------------------------------+
//| Защищённый конструктор                                           |
//+------------------------------------------------------------------+
CGCnvElement::CGCnvElement(const ENUM_GRAPH_ELEMENT_TYPE element_type,
                           CGCnvElement *main_obj,CGCnvElement *base_obj,
                           const long    chart_id,
                           const int     wnd_num,
                           const string  descript,
                           const int     x,
                           const int     y,
                           const int     w,
                           const int     h) : m_shadow(false)
  {
   this.m_type=OBJECT_DE_TYPE_GELEMENT; 
   this.m_element_main=main_obj;
   this.m_element_base=base_obj;
   this.m_chart_color_bg=(color)::ChartGetInteger((chart_id==NULL ? ::ChartID() : chart_id),CHART_COLOR_BACKGROUND);
   this.m_name=this.CreateNameGraphElement(element_type);
   this.m_chart_id=(chart_id==NULL || chart_id==0 ? ::ChartID() : chart_id);
   this.m_subwindow=wnd_num;
   this.m_type_element=element_type;
   this.SetFont(DEF_FONT,DEF_FONT_SIZE);
   this.m_text_anchor=0;
   this.m_text_x=0;
   this.m_text_y=0;
   this.SetBackgroundColor(CLR_CANV_NULL,true);
   this.SetOpacity(0);
   this.m_shift_coord_x=0;
   this.m_shift_coord_y=0;
   if(::ArrayResize(this.m_array_colors_bg,1)==1)
      this.m_array_colors_bg[0]=this.BackgroundColor();
   if(::ArrayResize(this.m_array_colors_bg_dwn,1)==1)
      this.m_array_colors_bg_dwn[0]=this.BackgroundColor();
   if(::ArrayResize(this.m_array_colors_bg_ovr,1)==1)
      this.m_array_colors_bg_ovr[0]=this.BackgroundColor();
   if(this.Create(chart_id,wnd_num,x,y,w,h,false))
     {
      this.Initialize(element_type,0,0,x,y,w,h,descript,false,false);
      this.SetVisibleFlag(false,false);
     }
   else
     {
      ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_FAILED_CREATE_ELM_OBJ),"\"",this.TypeElementDescription(element_type),"\" ",this.NameObj());
     }
  }
//+------------------------------------------------------------------+

Как видно, в обоих конструкторах в метод инициализации передаются собственные значения, присущие каждому конкретному конструктору.


В новый метод инициализации перенесём установку свойств, удалённую из конструкторов класса:

//+------------------------------------------------------------------+
//| Инициализирует свойства                                          |
//+------------------------------------------------------------------+
void CGCnvElement::Initialize(const ENUM_GRAPH_ELEMENT_TYPE element_type,
                              const int element_id,const int element_num,
                              const int x,const int y,const int w,const int h,
                              const string descript,const bool movable,const bool activity)
  {
   this.SetProperty(CANV_ELEMENT_PROP_NAME_RES,this.m_canvas.ResourceName()); // Имя графического ресурса
   this.SetProperty(CANV_ELEMENT_PROP_CHART_ID,CGBaseObj::ChartID());         // Идентификатор графика
   this.SetProperty(CANV_ELEMENT_PROP_WND_NUM,CGBaseObj::SubWindow());        // Номер подокна графика
   this.SetProperty(CANV_ELEMENT_PROP_NAME_OBJ,CGBaseObj::Name());            // Имя объекта-элемента
   this.SetProperty(CANV_ELEMENT_PROP_TYPE,element_type);                     // Тип графического элемента
   this.SetProperty(CANV_ELEMENT_PROP_ID,element_id);                         // Идентификатор элемента
   this.SetProperty(CANV_ELEMENT_PROP_NUM,element_num);                       // Номер элемента в списке
   this.SetProperty(CANV_ELEMENT_PROP_COORD_X,x);                             // X-координата элемента на графике
   this.SetProperty(CANV_ELEMENT_PROP_COORD_Y,y);                             // Y-координата элемента на графике
   this.SetProperty(CANV_ELEMENT_PROP_WIDTH,w);                               // Ширина элемента
   this.SetProperty(CANV_ELEMENT_PROP_HEIGHT,h);                              // Высота элемента
   this.SetProperty(CANV_ELEMENT_PROP_ACT_SHIFT_LEFT,0);                      // Отступ активной зоны от левого края элемента
   this.SetProperty(CANV_ELEMENT_PROP_ACT_SHIFT_TOP,0);                       // Отступ активной зоны от верхнего края элемента
   this.SetProperty(CANV_ELEMENT_PROP_ACT_SHIFT_RIGHT,0);                     // Отступ активной зоны от правого края элемента
   this.SetProperty(CANV_ELEMENT_PROP_ACT_SHIFT_BOTTOM,0);                    // Отступ активной зоны от нижнего края элемента
   this.SetProperty(CANV_ELEMENT_PROP_MOVABLE,movable);                       // Флаг перемещаемости элемента
   this.SetProperty(CANV_ELEMENT_PROP_ACTIVE,activity);                       // Флаг активности элемента
   this.SetProperty(CANV_ELEMENT_PROP_INTERACTION,false);                     // Флаг взаимодействия элемента с внешней средой
   this.SetProperty(CANV_ELEMENT_PROP_ENABLED,true);                          // Флаг доступности элемента
   this.SetProperty(CANV_ELEMENT_PROP_RIGHT,this.RightEdge());                // Правая граница элемента
   this.SetProperty(CANV_ELEMENT_PROP_BOTTOM,this.BottomEdge());              // Нижняя граница элемента
   this.SetProperty(CANV_ELEMENT_PROP_COORD_ACT_X,this.ActiveAreaLeft());     // X-координата активной зоны элемента
   this.SetProperty(CANV_ELEMENT_PROP_COORD_ACT_Y,this.ActiveAreaTop());      // Y-координата активной зоны элемента
   this.SetProperty(CANV_ELEMENT_PROP_ACT_RIGHT,this.ActiveAreaRight());      // Правая граница активной зоны элемента
   this.SetProperty(CANV_ELEMENT_PROP_ACT_BOTTOM,this.ActiveAreaBottom());    // Нижняя граница активной зоны элемента
   this.SetProperty(CANV_ELEMENT_PROP_VISIBLE_AREA_X,0);                      // Координата X области видимости
   this.SetProperty(CANV_ELEMENT_PROP_VISIBLE_AREA_Y,0);                      // Координата Y области видимости
   this.SetProperty(CANV_ELEMENT_PROP_VISIBLE_AREA_WIDTH,w);                  // Ширина области видимости
   this.SetProperty(CANV_ELEMENT_PROP_VISIBLE_AREA_HEIGHT,h);                 // Высота области видимости
   this.SetProperty(CANV_ELEMENT_PROP_DISPLAYED,true);                        // Флаг отображения не скрытого элемента управления
   this.SetProperty(CANV_ELEMENT_PROP_DISPLAY_STATE,CANV_ELEMENT_DISPLAY_STATE_NORMAL);// Состояние отображения элемента управления
   this.SetProperty(CANV_ELEMENT_PROP_DISPLAY_DURATION,DEF_CONTROL_PROCESS_DURATION);  // Продолжительность процесса отображения элемента управления
   this.SetProperty(CANV_ELEMENT_PROP_CONTROL_AREA_X,0);                      // X-координата области управления
   this.SetProperty(CANV_ELEMENT_PROP_CONTROL_AREA_Y,0);                      // Y-координата области управления
   this.SetProperty(CANV_ELEMENT_PROP_CONTROL_AREA_WIDTH,0);                  // Ширина области управления
   this.SetProperty(CANV_ELEMENT_PROP_CONTROL_AREA_HEIGHT,0);                 // Высота области управления
   this.SetProperty(CANV_ELEMENT_PROP_SCROLL_AREA_X_RIGHT,0);                 // X-координата области прокрутки справа
   this.SetProperty(CANV_ELEMENT_PROP_SCROLL_AREA_Y_RIGHT,0);                 // Y-координата области прокрутки справа
   this.SetProperty(CANV_ELEMENT_PROP_SCROLL_AREA_WIDTH_RIGHT,0);             // Ширина области прокрутки справа
   this.SetProperty(CANV_ELEMENT_PROP_SCROLL_AREA_HEIGHT_RIGHT,0);            // Высота области прокрутки справа
   this.SetProperty(CANV_ELEMENT_PROP_SCROLL_AREA_X_BOTTOM,0);                // X-координата области прокрутки снизу
   this.SetProperty(CANV_ELEMENT_PROP_SCROLL_AREA_Y_BOTTOM,0);                // Y-координата области прокрутки снизу
   this.SetProperty(CANV_ELEMENT_PROP_SCROLL_AREA_WIDTH_BOTTOM,0);            // Ширина области прокрутки снизу
   this.SetProperty(CANV_ELEMENT_PROP_SCROLL_AREA_HEIGHT_BOTTOM,0);           // Высота области прокрутки снизу
   this.SetProperty(CANV_ELEMENT_PROP_BORDER_LEFT_AREA_WIDTH,0);              // Ширина области левой грани
   this.SetProperty(CANV_ELEMENT_PROP_BORDER_BOTTOM_AREA_WIDTH,0);            // Ширина области нижней грани
   this.SetProperty(CANV_ELEMENT_PROP_BORDER_RIGHT_AREA_WIDTH,0);             // Ширина области правой грани
   this.SetProperty(CANV_ELEMENT_PROP_BORDER_TOP_AREA_WIDTH,0);               // Ширина области верхней грани
   //---
   this.SetProperty(CANV_ELEMENT_PROP_BELONG,ENUM_GRAPH_OBJ_BELONG::GRAPH_OBJ_BELONG_PROGRAM);  // Принадлежность графического элемента
   this.SetProperty(CANV_ELEMENT_PROP_ZORDER,0);                              // Приоритет графического объекта на получение события нажатия мышки на графике
   this.SetProperty(CANV_ELEMENT_PROP_BOLD_TYPE,FW_NORMAL);                   // Тип толщины шрифта
   this.SetProperty(CANV_ELEMENT_PROP_BORDER_STYLE,FRAME_STYLE_NONE);         // Стиль рамки элемента управления
   this.SetProperty(CANV_ELEMENT_PROP_BORDER_SIZE_TOP,0);                     // Размер рамки элемента управления сверху
   this.SetProperty(CANV_ELEMENT_PROP_BORDER_SIZE_BOTTOM,0);                  // Размер рамки элемента управления снизу
   this.SetProperty(CANV_ELEMENT_PROP_BORDER_SIZE_LEFT,0);                    // Размер рамки элемента управления слева
   this.SetProperty(CANV_ELEMENT_PROP_BORDER_SIZE_RIGHT,0);                   // Размер рамки элемента управления справа
   this.SetProperty(CANV_ELEMENT_PROP_BORDER_COLOR,this.BackgroundColor());   // Цвет рамки элемента управления
   this.SetProperty(CANV_ELEMENT_PROP_AUTOSIZE,false);                        // Флаг автоматического изменения размера элемента управления под содержимое
   this.SetProperty(CANV_ELEMENT_PROP_AUTOSIZE_MODE,CANV_ELEMENT_AUTO_SIZE_MODE_GROW); // Режим автоматического изменения размера элемента управления под содержимое
   this.SetProperty(CANV_ELEMENT_PROP_AUTOSCROLL,false);                      // Флаг автоматического появления полосы прокрутки
   this.SetProperty(CANV_ELEMENT_PROP_AUTOSCROLL_MARGIN_W,0);                 // Ширина поля вокруг элемента при автоматической прокрутке
   this.SetProperty(CANV_ELEMENT_PROP_AUTOSCROLL_MARGIN_H,0);                 // Высота поля вокруг элемента при автоматической прокрутке
   this.SetProperty(CANV_ELEMENT_PROP_DOCK_MODE,CANV_ELEMENT_DOCK_MODE_NONE); // Режим привязки границ элемента управления к контейнеру
   this.SetProperty(CANV_ELEMENT_PROP_MARGIN_TOP,0);                          // Промежуток сверху между полями данного и другого элемента управления
   this.SetProperty(CANV_ELEMENT_PROP_MARGIN_BOTTOM,0);                       // Промежуток снизу между полями данного и другого элемента управления
   this.SetProperty(CANV_ELEMENT_PROP_MARGIN_LEFT,0);                         // Промежуток слева между полями данного и другого элемента управления
   this.SetProperty(CANV_ELEMENT_PROP_MARGIN_RIGHT,0);                        // Промежуток справа между полями данного и другого элемента управления
   this.SetProperty(CANV_ELEMENT_PROP_PADDING_TOP,0);                         // Промежуток сверху внутри элемента управления
   this.SetProperty(CANV_ELEMENT_PROP_PADDING_BOTTOM,0);                      // Промежуток снизу внутри элемента управления
   this.SetProperty(CANV_ELEMENT_PROP_PADDING_LEFT,0);                        // Промежуток слева внутри элемента управления
   this.SetProperty(CANV_ELEMENT_PROP_PADDING_RIGHT,0);                       // Промежуток справа внутри элемента управления
   this.SetProperty(CANV_ELEMENT_PROP_TEXT_ALIGN,ANCHOR_LEFT_UPPER);          // Положение текста в границах текстовой метки
   this.SetProperty(CANV_ELEMENT_PROP_CHECK_ALIGN,ANCHOR_LEFT_UPPER);         // Положение флажка проверки в границах элемента управления
   this.SetProperty(CANV_ELEMENT_PROP_CHECKED,false);                         // Состояние флажка проверки элемента управления
   this.SetProperty(CANV_ELEMENT_PROP_CHECK_STATE,CANV_ELEMENT_CHEK_STATE_UNCHECKED);  // Состояние элемента управления, имеющего флажок проверки
   this.SetProperty(CANV_ELEMENT_PROP_AUTOCHECK,true);                        // Автоматическое изменение состояния флажка при его выборе
   //---
   this.SetProperty(CANV_ELEMENT_PROP_CHECK_BACKGROUND_COLOR,CLR_DEF_CHECK_BACK_COLOR);            // Цвет фона флажка проверки элемента управления
   this.SetProperty(CANV_ELEMENT_PROP_CHECK_BACKGROUND_COLOR_OPACITY,CLR_DEF_CHECK_BACK_OPACITY);  // Непрозрачность цвета фона флажка проверки элемента управления
   this.SetProperty(CANV_ELEMENT_PROP_CHECK_BACKGROUND_COLOR_MOUSE_DOWN,CLR_DEF_CHECK_BACK_MOUSE_DOWN);// Цвет фона флажка проверки элемента управления при нажатии мышки на элемент управления
   this.SetProperty(CANV_ELEMENT_PROP_CHECK_BACKGROUND_COLOR_MOUSE_OVER,CLR_DEF_CHECK_BACK_MOUSE_OVER);// Цвет фона флажка проверки элемента управления при наведении мышки на элемент управления
   this.SetProperty(CANV_ELEMENT_PROP_CHECK_FORE_COLOR,CLR_DEF_CHECK_BORDER_COLOR);                // Цвет рамки флажка проверки элемента управления
   this.SetProperty(CANV_ELEMENT_PROP_CHECK_FORE_COLOR_OPACITY,CLR_DEF_CHECK_BORDER_OPACITY);      // Непрозрачность цвета рамки флажка проверки элемента управления
   this.SetProperty(CANV_ELEMENT_PROP_CHECK_FORE_COLOR_MOUSE_DOWN,CLR_DEF_CHECK_BORDER_MOUSE_DOWN);// Цвет рамки флажка проверки элемента управления при нажатии мышки на элемент управления
   this.SetProperty(CANV_ELEMENT_PROP_CHECK_FORE_COLOR_MOUSE_OVER,CLR_DEF_CHECK_BORDER_MOUSE_OVER);// Цвет рамки флажка проверки элемента управления при наведении мышки на элемент управления
   this.SetProperty(CANV_ELEMENT_PROP_CHECK_FLAG_COLOR,CLR_DEF_CHECK_FLAG_COLOR);                  // Цвет флажка проверки элемента управления
   this.SetProperty(CANV_ELEMENT_PROP_CHECK_FLAG_COLOR_OPACITY,CLR_DEF_CHECK_FLAG_OPACITY);        // Непрозрачность цвета флажка проверки элемента управления
   this.SetProperty(CANV_ELEMENT_PROP_CHECK_FLAG_COLOR_MOUSE_DOWN,CLR_DEF_CHECK_FLAG_MOUSE_DOWN);  // Цвет флажка проверки элемента управления при нажатии мышки на элемент управления
   this.SetProperty(CANV_ELEMENT_PROP_CHECK_FLAG_COLOR_MOUSE_OVER,CLR_DEF_CHECK_FLAG_MOUSE_OVER);  // Цвет флажка проверки элемента управления при наведении мышки на элемент управления
   this.SetProperty(CANV_ELEMENT_PROP_FORE_COLOR,CLR_DEF_FORE_COLOR);                              // Цвет текста по умолчанию для всех объектов элемента управления
   this.SetProperty(CANV_ELEMENT_PROP_FORE_COLOR_OPACITY,CLR_DEF_FORE_COLOR_OPACITY);              // Непрозрачность цвета текста по умолчанию для всех объектов элемента управления
   this.SetProperty(CANV_ELEMENT_PROP_FORE_COLOR_MOUSE_DOWN,CLR_DEF_FORE_COLOR_MOUSE_DOWN);        // Цвет текста элемента управления по умолчанию при нажатии мышки на элемент управления
   this.SetProperty(CANV_ELEMENT_PROP_FORE_COLOR_MOUSE_OVER,CLR_DEF_FORE_COLOR_MOUSE_OVER);        // Цвет текста элемента управления по умолчанию при наведении мышки на элемент управления
   this.SetProperty(CANV_ELEMENT_PROP_FORE_COLOR_STATE_ON,CLR_DEF_FORE_COLOR);                     // Цвет текста элемента управления в состоянии "включено"
   this.SetProperty(CANV_ELEMENT_PROP_FORE_COLOR_STATE_ON_MOUSE_DOWN,CLR_DEF_FORE_COLOR_MOUSE_DOWN);// Цвет текста элемента управления по умолчанию в состоянии "включено" при нажатии мышки на элемент управления
   this.SetProperty(CANV_ELEMENT_PROP_FORE_COLOR_STATE_ON_MOUSE_OVER,CLR_DEF_FORE_COLOR_MOUSE_OVER);// Цвет текста элемента управления по умолчанию в состоянии "включено" при наведении мышки на элемент управления
   this.SetProperty(CANV_ELEMENT_PROP_BACKGROUND_COLOR_MOUSE_DOWN,this.BackgroundColor());         // Цвет фона элемента управления при нажатии мышки на элемент управления
   this.SetProperty(CANV_ELEMENT_PROP_BACKGROUND_COLOR_MOUSE_OVER,this.BackgroundColor());         // Цвет фона элемента управления при наведении мышки на элемент управления
   this.SetProperty(CANV_ELEMENT_PROP_BACKGROUND_COLOR_STATE_ON,CLR_DEF_CONTROL_STD_BACK_COLOR_ON);// Цвет фона элемента управления в состоянии "включено"
   this.SetProperty(CANV_ELEMENT_PROP_BACKGROUND_COLOR_STATE_ON_MOUSE_DOWN,CLR_DEF_CONTROL_STD_BACK_DOWN_ON);// Цвет фона элемента управления в состоянии "включено" при нажатии мышки на элемент управления
   this.SetProperty(CANV_ELEMENT_PROP_BACKGROUND_COLOR_STATE_ON_MOUSE_OVER,CLR_DEF_CONTROL_STD_BACK_OVER_ON);// Цвет фона элемента управления в состоянии "включено" при наведении мышки на элемент управления
   this.SetProperty(CANV_ELEMENT_PROP_BORDER_COLOR_MOUSE_DOWN,CLR_DEF_BORDER_MOUSE_DOWN);          // Цвет рамки элемента управления при нажатии мышки на элемент управления
   this.SetProperty(CANV_ELEMENT_PROP_BORDER_COLOR_MOUSE_OVER,CLR_DEF_BORDER_MOUSE_OVER);          // Цвет рамки элемента управления при наведении мышки на элемент управления
   this.SetProperty(CANV_ELEMENT_PROP_BUTTON_TOGGLE,false);                                        // Флаг "Переключатель" элемента управления, имеющего кнопку
   this.SetProperty(CANV_ELEMENT_PROP_BUTTON_STATE,false);                                         // Состояние элемента управления "Переключатель", имеющего кнопку
   this.SetProperty(CANV_ELEMENT_PROP_BUTTON_GROUP,false);                                         // Флаг группы кнопки
   this.SetProperty(CANV_ELEMENT_PROP_LIST_BOX_MULTI_COLUMN,false);                                // Горизонтальное отображение столбцов в элементе управления ListBox
   this.SetProperty(CANV_ELEMENT_PROP_LIST_BOX_COLUMN_WIDTH,0);                                    // Ширина каждого столбца элемента управления ListBox
   this.SetProperty(CANV_ELEMENT_PROP_TAB_MULTILINE,false);                                        // Несколько рядов вкладок в элементе управления TabControl
   this.SetProperty(CANV_ELEMENT_PROP_TAB_ALIGNMENT,CANV_ELEMENT_ALIGNMENT_TOP);                   // Местоположение вкладок внутри элемента управления
   this.SetProperty(CANV_ELEMENT_PROP_ALIGNMENT,CANV_ELEMENT_ALIGNMENT_TOP);                       // Местоположение объекта внутри элемента управления
   this.SetProperty(CANV_ELEMENT_PROP_TEXT,"");                                                    // Текст графического элемента
   this.SetProperty(CANV_ELEMENT_PROP_DESCRIPTION,descript);                                       // Описание графического элемента
   this.SetProperty(CANV_ELEMENT_PROP_SPLIT_CONTAINER_FIXED_PANEL,0);                              // Панель, сохраняющая свои размеры при изменении размера контейнера
   this.SetProperty(CANV_ELEMENT_PROP_SPLIT_CONTAINER_SPLITTER_FIXED,true);                        // Флаг перемещаемости разделителя
   this.SetProperty(CANV_ELEMENT_PROP_SPLIT_CONTAINER_SPLITTER_DISTANCE,50);                       // Расстояние от края до разделителя
   this.SetProperty(CANV_ELEMENT_PROP_SPLIT_CONTAINER_SPLITTER_WIDTH,4);                           // Толщина разделителя
   this.SetProperty(CANV_ELEMENT_PROP_SPLIT_CONTAINER_SPLITTER_ORIENTATION,0);                     // Расположение разделителя
   this.SetProperty(CANV_ELEMENT_PROP_SPLIT_CONTAINER_PANEL1_COLLAPSED,false);                     // Флаг свёрнутости панели 1
   this.SetProperty(CANV_ELEMENT_PROP_SPLIT_CONTAINER_PANEL1_MIN_SIZE,25);                         // Минимальный размер панели 1
   this.SetProperty(CANV_ELEMENT_PROP_SPLIT_CONTAINER_PANEL2_COLLAPSED,false);                     // Флаг свёрнутости панели 1
   this.SetProperty(CANV_ELEMENT_PROP_SPLIT_CONTAINER_PANEL2_MIN_SIZE,25);                         // Минимальный размер панели 2
   this.SetProperty(CANV_ELEMENT_PROP_TOOLTIP_INITIAL_DELAY,500);                                  // Задержка отображения подсказки
   this.SetProperty(CANV_ELEMENT_PROP_TOOLTIP_AUTO_POP_DELAY,5000);                                // Длительность отображения подсказки
   this.SetProperty(CANV_ELEMENT_PROP_TOOLTIP_RESHOW_DELAY,100);                                   // Задержка отображения новой подсказки одного элемента
   this.SetProperty(CANV_ELEMENT_PROP_TOOLTIP_SHOW_ALWAYS,false);                                  // Отображать подсказку в неактивном окне
   this.SetProperty(CANV_ELEMENT_PROP_TOOLTIP_ICON,CANV_ELEMENT_TOOLTIP_ICON_NONE);                // Значок, отображаемый в подсказке
   this.SetProperty(CANV_ELEMENT_PROP_TOOLTIP_IS_BALLOON,false);                                   // Подсказка в форме "облачка"
   this.SetProperty(CANV_ELEMENT_PROP_TOOLTIP_USE_FADING,true);                                    // Угасание при отображении и скрытии подсказки
   this.SetProperty(CANV_ELEMENT_PROP_TOOLTIP_TITLE,"");                                           // Заголовок Tooltip для элемента
   this.SetProperty(CANV_ELEMENT_PROP_TOOLTIP_TEXT,"");                                            // Текст Tooltip для элемента
   this.SetProperty(CANV_ELEMENT_PROP_GROUP,0);                               // Группа, к которой принадлежит графический элемент
   this.SetProperty(CANV_ELEMENT_PROP_TAB_SIZE_MODE,CANV_ELEMENT_TAB_SIZE_MODE_NORMAL);// Режим установки размера вкладок
   this.SetProperty(CANV_ELEMENT_PROP_TAB_PAGE_NUMBER,0);                     // Порядковый номер вкладки
   this.SetProperty(CANV_ELEMENT_PROP_TAB_PAGE_ROW,0);                        // Номер ряда вкладки
   this.SetProperty(CANV_ELEMENT_PROP_TAB_PAGE_COLUMN,0);                     // Номер столбца вкладки
   this.SetProperty(CANV_ELEMENT_PROP_PROGRESS_BAR_MAXIMUM,100);              // Верхняя граница диапазона, в котором действует ProgressBar
   this.SetProperty(CANV_ELEMENT_PROP_PROGRESS_BAR_MINIMUM,0);                // Нижняя граница диапазона, в котором действует ProgressBar
   this.SetProperty(CANV_ELEMENT_PROP_PROGRESS_BAR_STEP,10);                  // Величина приращения значения ProgressBar для его перерисовки
   this.SetProperty(CANV_ELEMENT_PROP_PROGRESS_BAR_STYLE,CANV_ELEMENT_PROGRESS_BAR_STYLE_CONTINUOUS); // Стиль элемента ProgressBar
   this.SetProperty(CANV_ELEMENT_PROP_PROGRESS_BAR_VALUE,50);                 // Текущее значение элемента ProgressBar в диапазоне от Min до Max
   this.SetProperty(CANV_ELEMENT_PROP_PROGRESS_BAR_MARQUEE_ANIM_SPEED,10);    // Скорость анимации полосы прогресса при стиле Marquee
  }
//+------------------------------------------------------------------+

Сюда мы просто перенесли удалённые строки из конструкторов класса. Значения свойств, которые в разных конструкторах были разными, теперь передаются и устанавливаются в свойства объекта через формальные параметры метода.


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

//+------------------------------------------------------------------+
//| Создаёт структуру объекта                                        |
//+------------------------------------------------------------------+
bool CGCnvElement::ObjectToStruct(void)
  {
//--- Сохранение целочисленных свойств
   this.m_struct_obj.id=(int)this.GetProperty(CANV_ELEMENT_PROP_ID);                               // Идентификатор элемента
   this.m_struct_obj.type=(int)this.GetProperty(CANV_ELEMENT_PROP_TYPE);                           // Тип графического элемента
   this.m_struct_obj.belong=(int)this.GetProperty(CANV_ELEMENT_PROP_BELONG);                       // Принадлежность графического элемента
   this.m_struct_obj.number=(int)this.GetProperty(CANV_ELEMENT_PROP_NUM);                          // Номер элемента в списке

   //---...
   //---...

   this.m_struct_obj.border_right_area_width=(int)this.GetProperty(CANV_ELEMENT_PROP_BORDER_RIGHT_AREA_WIDTH);    // Ширина области правой грани
   this.m_struct_obj.border_top_area_width=(int)this.GetProperty(CANV_ELEMENT_PROP_BORDER_TOP_AREA_WIDTH);        // Ширина области верхней грани
   //---
   this.m_struct_obj.tooltip_initial_delay=this.GetProperty(CANV_ELEMENT_PROP_TOOLTIP_INITIAL_DELAY);    // Задержка отображения подсказки
   this.m_struct_obj.tooltip_auto_pop_delay=this.GetProperty(CANV_ELEMENT_PROP_TOOLTIP_AUTO_POP_DELAY);  // Длительность отображения подсказки
   this.m_struct_obj.tooltip_reshow_delay=this.GetProperty(CANV_ELEMENT_PROP_TOOLTIP_RESHOW_DELAY);      // Задержка отображения новой подсказки одного элемента
   this.m_struct_obj.tooltip_show_always=this.GetProperty(CANV_ELEMENT_PROP_TOOLTIP_SHOW_ALWAYS);        // Отображать подсказку в неактивном окне
   this.m_struct_obj.tooltip_icon=(int)this.GetProperty(CANV_ELEMENT_PROP_TOOLTIP_ICON);                 // Значок, отображаемый в подсказке
   this.m_struct_obj.tooltip_is_balloon=(bool)this.GetProperty(CANV_ELEMENT_PROP_TOOLTIP_IS_BALLOON);    // Подсказка в форме "облачка"
   this.m_struct_obj.tooltip_use_fading=(bool)this.GetProperty(CANV_ELEMENT_PROP_TOOLTIP_USE_FADING);    // Угасание при отображении и скрытии подсказки
      //---
   this.m_struct_obj.group=(int)this.GetProperty(CANV_ELEMENT_PROP_GROUP);                               // Группа, к которой принадлежит графический элемент
   this.m_struct_obj.tab_size_mode=(int)this.GetProperty(CANV_ELEMENT_PROP_TAB_SIZE_MODE);               // Режим установки размера вкладок
   this.m_struct_obj.tab_page_number=(int)this.GetProperty(CANV_ELEMENT_PROP_TAB_PAGE_NUMBER);           // Порядковый номер вкладки
   this.m_struct_obj.tab_page_row=(int)this.GetProperty(CANV_ELEMENT_PROP_TAB_PAGE_ROW);                 // Номер ряда вкладки
   this.m_struct_obj.tab_page_column=(int)this.GetProperty(CANV_ELEMENT_PROP_TAB_PAGE_COLUMN);           // Номер столбца вкладки
   this.m_struct_obj.progress_bar_maximum=(int)this.GetProperty(CANV_ELEMENT_PROP_PROGRESS_BAR_MAXIMUM); // Верхняя граница диапазона, в котором действует ProgressBar
   this.m_struct_obj.progress_bar_minimum=(int)this.GetProperty(CANV_ELEMENT_PROP_PROGRESS_BAR_MINIMUM); // Нижняя граница диапазона, в котором действует ProgressBar
   this.m_struct_obj.progress_bar_step=(int)this.GetProperty(CANV_ELEMENT_PROP_PROGRESS_BAR_STEP);       // Величина приращения значения ProgressBar для его перерисовки
   this.m_struct_obj.progress_bar_style=(int)this.GetProperty(CANV_ELEMENT_PROP_PROGRESS_BAR_STYLE);     // Стиль элемента ProgressBar
   this.m_struct_obj.progress_bar_value=(int)this.GetProperty(CANV_ELEMENT_PROP_PROGRESS_BAR_VALUE);     // Текущее значение элемента ProgressBar в диапазоне от Min до Max
   this.m_struct_obj.progress_bar_marquee_speed=(int)this.GetProperty(CANV_ELEMENT_PROP_PROGRESS_BAR_MARQUEE_ANIM_SPEED);// Скорость анимации полосы прогресса при стиле Marquee
//--- Сохранение вещественных свойств

//--- Сохранение строковых свойств
   ::StringToCharArray(this.GetProperty(CANV_ELEMENT_PROP_NAME_OBJ),this.m_struct_obj.name_obj);   // Имя объекта-графического элемента
   ::StringToCharArray(this.GetProperty(CANV_ELEMENT_PROP_NAME_RES),this.m_struct_obj.name_res);   // Имя графического ресурса
   ::StringToCharArray(this.GetProperty(CANV_ELEMENT_PROP_TEXT),this.m_struct_obj.text);           // Текст графического элемента
   ::StringToCharArray(this.GetProperty(CANV_ELEMENT_PROP_DESCRIPTION),this.m_struct_obj.descript);// Описание графического элемента
   ::StringToCharArray(this.GetProperty(CANV_ELEMENT_PROP_TOOLTIP_TITLE),this.m_struct_obj.tooltip_title);// Заголовок Tooltip для элемента
   ::StringToCharArray(this.GetProperty(CANV_ELEMENT_PROP_TOOLTIP_TEXT),this.m_struct_obj.tooltip_text);  // Текст Tooltip для элемента
   //--- Сохранение структуры в uchar-массив
   ::ResetLastError();
   if(!::StructToCharArray(this.m_struct_obj,this.m_uchar_array))
     {
      CMessage::ToLog(DFUN,MSG_LIB_SYS_FAILED_SAVE_OBJ_STRUCT_TO_UARRAY,true);
      return false;
     }
   return true;
  }
//+------------------------------------------------------------------+


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

//+------------------------------------------------------------------+
//| Создаёт объект из структуры                                      |
//+------------------------------------------------------------------+
void CGCnvElement::StructToObject(void)
  {
//--- Сохранение целочисленных свойств
   this.SetProperty(CANV_ELEMENT_PROP_ID,this.m_struct_obj.id);                                    // Идентификатор элемента
   this.SetProperty(CANV_ELEMENT_PROP_TYPE,this.m_struct_obj.type);                                // Тип графического элемента
   this.SetProperty(CANV_ELEMENT_PROP_BELONG,this.m_struct_obj.belong);                            // Принадлежность графического элемента
   this.SetProperty(CANV_ELEMENT_PROP_NUM,this.m_struct_obj.number);                               // Номер элемента в списке

   //---...
   //---...

   this.SetProperty(CANV_ELEMENT_PROP_BORDER_RIGHT_AREA_WIDTH,this.m_struct_obj.border_right_area_width);      // Ширина области правой грани
   this.SetProperty(CANV_ELEMENT_PROP_BORDER_TOP_AREA_WIDTH,this.m_struct_obj.border_top_area_width);          // Ширина области верхней грани
   this.SetProperty(CANV_ELEMENT_PROP_TOOLTIP_INITIAL_DELAY,this.m_struct_obj.tooltip_initial_delay);             // Задержка отображения подсказки
   this.SetProperty(CANV_ELEMENT_PROP_TOOLTIP_AUTO_POP_DELAY,this.m_struct_obj.tooltip_auto_pop_delay);// Длительность отображения подсказки
   this.SetProperty(CANV_ELEMENT_PROP_TOOLTIP_RESHOW_DELAY,this.m_struct_obj.tooltip_reshow_delay);// Задержка отображения новой подсказки одного элемента
   this.SetProperty(CANV_ELEMENT_PROP_TOOLTIP_SHOW_ALWAYS,this.m_struct_obj.tooltip_show_always);// Отображать подсказку в неактивном окне
   this.SetProperty(CANV_ELEMENT_PROP_TOOLTIP_ICON,this.m_struct_obj.tooltip_icon);              // Значок, отображаемый в подсказке
   this.SetProperty(CANV_ELEMENT_PROP_TOOLTIP_IS_BALLOON,this.m_struct_obj.tooltip_is_balloon);  // Подсказка в форме "облачка"
   this.SetProperty(CANV_ELEMENT_PROP_TOOLTIP_USE_FADING,this.m_struct_obj.tooltip_use_fading);  // Угасание при отображении и скрытии подсказки
   this.SetProperty(CANV_ELEMENT_PROP_GROUP,this.m_struct_obj.group);                            // Группа, к которой принадлежит графический элемент
   this.SetProperty(CANV_ELEMENT_PROP_TAB_SIZE_MODE,this.m_struct_obj.tab_size_mode);            // Режим установки размера вкладок
   this.SetProperty(CANV_ELEMENT_PROP_TAB_PAGE_NUMBER,this.m_struct_obj.tab_page_number);        // Порядковый номер вкладки
   this.SetProperty(CANV_ELEMENT_PROP_TAB_PAGE_ROW,this.m_struct_obj.tab_page_row);              // Номер ряда вкладки
   this.SetProperty(CANV_ELEMENT_PROP_TAB_PAGE_COLUMN,this.m_struct_obj.tab_page_column);        // Номер столбца вкладки
   this.SetProperty(CANV_ELEMENT_PROP_PROGRESS_BAR_MAXIMUM,this.m_struct_obj.progress_bar_maximum);// Верхняя граница диапазона, в котором действует ProgressBar
   this.SetProperty(CANV_ELEMENT_PROP_PROGRESS_BAR_MINIMUM,this.m_struct_obj.progress_bar_minimum);// Нижняя граница диапазона, в котором действует ProgressBar
   this.SetProperty(CANV_ELEMENT_PROP_PROGRESS_BAR_STEP,this.m_struct_obj.progress_bar_step);    // Величина приращения значения ProgressBar для его перерисовки
   this.SetProperty(CANV_ELEMENT_PROP_PROGRESS_BAR_STYLE,this.m_struct_obj.progress_bar_style);  // Стиль элемента ProgressBar
   this.SetProperty(CANV_ELEMENT_PROP_PROGRESS_BAR_VALUE,this.m_struct_obj.progress_bar_value);  // Текущее значение элемента ProgressBar в диапазоне от Min до Max
   this.SetProperty(CANV_ELEMENT_PROP_PROGRESS_BAR_MARQUEE_ANIM_SPEED,this.m_struct_obj.progress_bar_marquee_speed);  // Скорость анимации полосы прогресса при стиле Marquee
//--- Сохранение вещественных свойств

//--- Сохранение строковых свойств
   this.SetProperty(CANV_ELEMENT_PROP_NAME_OBJ,::CharArrayToString(this.m_struct_obj.name_obj));   // Имя объекта-графического элемента
   this.SetProperty(CANV_ELEMENT_PROP_NAME_RES,::CharArrayToString(this.m_struct_obj.name_res));   // Имя графического ресурса
   this.SetProperty(CANV_ELEMENT_PROP_TEXT,::CharArrayToString(this.m_struct_obj.text));           // Текст графического элемента
   this.SetProperty(CANV_ELEMENT_PROP_DESCRIPTION,::CharArrayToString(this.m_struct_obj.descript));// Описание графического элемента
   this.SetProperty(CANV_ELEMENT_PROP_TOOLTIP_TITLE,::CharArrayToString(this.m_struct_obj.tooltip_title));// Заголовок Tooltip для элемента
   this.SetProperty(CANV_ELEMENT_PROP_TOOLTIP_TEXT,::CharArrayToString(this.m_struct_obj.tooltip_text));  // Текст Tooltip для элемента
  }
//+------------------------------------------------------------------+

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


Блик, пробегающий по полосе прогресса, может иметь форму параллелепипеда. Это скошенный четырёхугольник, вершины верхней грани которого смещены на 6 пикселей относительно вершин нижней грани. Для небольшого по высоте объекта такого смещения вполне достаточно, чтобы объект выглядел достаточно скошенным. Но при увеличении его высоты, скос, заданный жёстко в шесть пикселей, становится малоразличим. Для исправления ситуации нам нужно сделать скос относительной величиной — чем выше объект, тем сильнее скос. А значит, для задания размера смещения вершин будем использовать высоту объекта — тогда визуально скос будет под 45 градусов.

В файле \MQL5\Include\DoEasy\Objects\Graph\WForms\GlareObj.mqh, в методе, рисующем форму блика объекта в виде параллелограмма, исправим инициализацию параметров координат вершин. Вместо цифры 6 припишем значение высоты графического объекта:

//+------------------------------------------------------------------+
//| Рисует форму блика объекта в виде параллелограмма                |
//+------------------------------------------------------------------+
void CGlareObj::DrawFigureParallelogram(void)
  {
   int array_x[]={this.Height(),this.Width()-1,this.Width()-1-this.Height(),0};
   int array_y[]={0,0,this.Height()-1,this.Height()-1};
   CGCnvElement::DrawPolygonFill(array_x,array_y,this.m_color,this.OpacityDraw());
   CGCnvElement::Update();
  }
//+------------------------------------------------------------------+

Теперь вершины будут смещаться на значение высоты объекта и всегда скос будет примерно под 45 градусов, вне зависимости от высоты объекта.


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

В файле \MQL5\Include\DoEasy\Objects\Graph\WForms\Helpers\BarProgressBar.mqh в приватную секцию добавим переменные для хранения параметров сегментов и объявим метод для сегментации фона объекта:

//+------------------------------------------------------------------+
//| Класс объекта BarProgressBar элемента управления ProgressBar     |
//+------------------------------------------------------------------+
class CBarProgressBar : public CWinFormBase
  {
private:
   int               m_segment_s;                                 // Начало отсчёта сегментов
   int               m_segment_x;                                 // Координата X последнего сегмента
   int               m_segment_w;                                 // Ширина сегмента
   int               m_segment_d;                                 // Расстояние между сегмнтами
//--- Сегментирует фон
   void              Segmentation(void);
//--- (1) Устанавливает, (2) возвращает задержку перед отображением эффекта
   void              SetShowDelay(const long delay)               { this.SetProperty(CANV_ELEMENT_PROP_TOOLTIP_RESHOW_DELAY,delay);             }
   ulong             ShowDelay(void)                              { return this.GetProperty(CANV_ELEMENT_PROP_TOOLTIP_RESHOW_DELAY);            }
//--- Инициализирует свойства
   void              Initialize(void);
protected:
//--- Защищённый конструктор с указанием типа объекта, идентификатора чарта и подокна
                     CBarProgressBar(const ENUM_GRAPH_ELEMENT_TYPE type,
                                     CGCnvElement *main_obj,CGCnvElement *base_obj,
                                     const long chart_id,
                                     const int subwindow,
                                     const string descript,
                                     const int x,
                                     const int y,
                                     const int w,
                                     const int h);
public:

В объявленных переменных будут записаны начальные параметры, от которых можно рассчитывать расположение сегментов. Ширина сегмента всегда будет иметь значение 3/4 от высоты, а дистанция между сегментами будет рассчитываться от полученной ширины сегмента. Эти данные будут записываться при инициализации объекта, и далее их можно будет использовать для расчёта места расположения любого сегмента по его номеру. Так как мы рисуем не сами сегменты, а пробелы между ними, то началом отсчёта будет являться ширина первого сегмента, отсчитанная либо от нуля, либо от единицы. Если высота полосы прогресса больше трёх пикселей, то по краю объекта должно быть нарисовано пустое пространство размером в один пиксель со всех сторон. Это отделит сегменты от внешнего края полосы, сделав их визуально как бы самостоятельными единицами. В этом случае отступ первого сегмента должен начинаться с единицы — это пустое пространство в один пиксель от края объекта до первого сегмента. Если высота полосы прогресса три пикселя и меньше, то пустое пространство рисовать не нужно — оно займёт всю полезную площадь объекта, на котором рисуются сегменты. В этом случае отступ до начала первого сегмента должен равняться нулю.

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

public:
//--- Устанавливает (1) скорость анимации при стиле Marquee, (2) стиль отображения, (3) величину приращения, (4) текущее начение элемента ProgressBar
   void              SetMarqueeAnimationSpeed(const int value)    { this.SetProperty(CANV_ELEMENT_PROP_PROGRESS_BAR_MARQUEE_ANIM_SPEED,value);  }
   void              SetStyle(const ENUM_CANV_ELEMENT_PROGRESS_BAR_STYLE style) { this.SetProperty(CANV_ELEMENT_PROP_PROGRESS_BAR_STYLE,style); }
   void              SetStep(const int value)                     { this.SetProperty(CANV_ELEMENT_PROP_PROGRESS_BAR_STEP,value);                }
   void              SetValue(const int value)                    { this.SetProperty(CANV_ELEMENT_PROP_PROGRESS_BAR_VALUE,value);               }
   
//--- Возвращает стиль отображения
   ENUM_CANV_ELEMENT_PROGRESS_BAR_STYLE Style(void) const
                       { return (ENUM_CANV_ELEMENT_PROGRESS_BAR_STYLE)this.GetProperty(CANV_ELEMENT_PROP_PROGRESS_BAR_STYLE); }

//--- Возвращает (1) координату X последнего сегмента, (2) ширину сегмента, (3) расстояние между сегментами, (4) начало отсчёта сегментов
   int               SegmentX(void)                         const { return this.m_segment_x; }
   int               SegmentWidth(void)                     const { return this.m_segment_w; }
   int               SegmentDistance(void)                  const { return this.m_segment_d; }
   int               SegmentStart(void)                     const { return this.m_segment_s; }
   
//--- Рассчитывает (1) ширину сегмента, (2) расстояние между сегментами
   int               CalculateSegmentWidth(void);
   int               CalculateSegmentDistance(const int width);

//--- Поддерживаемые свойства объекта (1) целочисленные, (2) вещественные, (3) строковые
   virtual bool      SupportProperty(ENUM_CANV_ELEMENT_PROP_INTEGER property) { return true; }
   virtual bool      SupportProperty(ENUM_CANV_ELEMENT_PROP_DOUBLE property)  { return true; }
   virtual bool      SupportProperty(ENUM_CANV_ELEMENT_PROP_STRING property)  { return true; }

//--- Очищает элемент с заполнением его цветом и непрозрачностью
   virtual void      Erase(const color colour,const uchar opacity,const bool redraw=false);
//--- Очищает элемент заливкой градиентом
   virtual void      Erase(color &colors[],const uchar opacity,const bool vgradient,const bool cycle,const bool redraw=false);
   
//--- Конструктор
                     CBarProgressBar(CGCnvElement *main_obj,CGCnvElement *base_obj,
                                     const long chart_id,
                                     const int subwindow,
                                     const string descript,
                                     const int x,
                                     const int y,
                                     const int w,
                                     const int h);
//--- Таймер
   virtual void      OnTimer(void);
  };
//+------------------------------------------------------------------+

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

В методе инициализации свойств объекта впишем значения по умолчанию для переменных, хранящих параметры сегментации. Координата X последнего сегмента и начало отсчёта будут равны нулю, а ширина сегмента и дистанция между ними будут сразу рассчитаны:

//+------------------------------------------------------------------+
//| Инициализирует свойства                                          |
//+------------------------------------------------------------------+
void CBarProgressBar::Initialize(void)
  {
   this.SetPaddingAll(0);
   this.SetMarginAll(0);
   this.SetBorderSizeAll(0);
   this.SetBackgroundColor(CLR_DEF_CONTROL_PROGRESS_BAR_BAR_COLOR,true);
   this.SetBorderColor(CLR_DEF_CONTROL_PROGRESS_BAR_BAR_COLOR,true);
   this.SetForeColor(CLR_DEF_CONTROL_PROGRESS_BAR_FORE_COLOR,true);
   this.SetShowDelay(2000);
   this.m_segment_x=0;
   this.m_segment_s=0;
   this.m_segment_w=this.CalculateSegmentWidth();
   this.m_segment_d=this.CalculateSegmentDistance(this.m_segment_w);
  }
//+------------------------------------------------------------------+


Рассмотрим подробнее объявленные методы.

Метод, очищающий элемент с заполнением его цветом и непрозрачностью:

//+------------------------------------------------------------------+
//| Очищает элемент с заполнением его цветом и непрозрачностью       |
//+------------------------------------------------------------------+
void CBarProgressBar::Erase(const color colour,const uchar opacity,const bool redraw=false)
  {
//--- Закрашиваем элемент с указанным цветом и флагом необходимости перерисовки
   CGCnvElement::EraseNoCrop(colour,opacity,false);
//--- Сегментируем фон
   this.Segmentation();
//--- Если у объекта есть рамка - рисуем её
   if(this.BorderStyle()!=FRAME_STYLE_NONE)
      this.DrawFormFrame(this.BorderSizeTop(),this.BorderSizeBottom(),this.BorderSizeLeft(),this.BorderSizeRight(),this.BorderColor(),this.Opacity(),this.BorderStyle());
//--- Обрезаем лишнее и обновляем элемент с указанным флагом необходимости перерисовки
   this.Crop();
   this.Update(redraw);
  }
//+------------------------------------------------------------------+

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

Метод, очищающий элемент заливкой градиентом:

//+------------------------------------------------------------------+
//| Очищает элемент заливкой градиентом                              |
//+------------------------------------------------------------------+
void CBarProgressBar::Erase(color &colors[],const uchar opacity,const bool vgradient,const bool cycle,const bool redraw=false)
  {
//--- Закрашиваем элемент с указанным массивом цветов и флагом необходимости перерисовки
   CGCnvElement::EraseNoCrop(colors,opacity,vgradient,cycle,false);
//--- Сегментируем фон
   this.Segmentation();
//--- Если у объекта есть рамка - рисуем её
   if(this.BorderStyle()!=FRAME_STYLE_NONE)
      this.DrawFormFrame(this.BorderSizeTop(),this.BorderSizeBottom(),this.BorderSizeLeft(),this.BorderSizeRight(),this.BorderColor(),this.Opacity(),this.BorderStyle());
//--- Обрезаем лишнее и обновляем элемент с указанным флагом необходимости перерисовки
   this.Crop();
   this.Update(redraw);
  }
//+------------------------------------------------------------------+

Логика метода идентична логике вышерассмотренного метода за исключением того, что в метод передаётся не единственный цвет, а массив цветов градиентной заливки и флаг её направления — вертикальная/горизонтальная и циклическая.


Метод, рассчитывающий ширину сегмента:

//+------------------------------------------------------------------+
//| Рассчитывает ширину сегмента                                     |
//+------------------------------------------------------------------+
int CBarProgressBar::CalculateSegmentWidth(void)
  {
   int w=(int)::ceil((this.Height()-2)/1.75);
   return(w>3 ? w : 3);
  }
//+------------------------------------------------------------------+

Ширина любого сегмента должна составлять 2/3 от его высоты. Этот метод рассчитывает ширину сегмента в зависимости от высоты объекта-полосы прогресса. Берём высоту объекта минус два пикселя. Чаще высота полосы прогресса будет больше трёх пикселей, а значит — с каждой стороны полосы прогресса по его периметру будет нарисовано свободное пространство в один пиксель, что уменьшит видимую высоту сегмента на два пикселя (один сверху и один снизу). Полученную высоту делим на 1.75, что даст соотношение сторон 3/4. Если полученная ширина меньше трёх пикселей, то ширина будет равна трём пикселям, так как слишком маленькие по высоте сегменты плохо смотрятся с шириной в 1-2 пикселя.


Метод, рассчитывающий расстояние между сегментами:

//+------------------------------------------------------------------+
//| Рассчитывает расстояние между сегментами                         |
//+------------------------------------------------------------------+
int CBarProgressBar::CalculateSegmentDistance(const int width)
  {
   int d=(int)::ceil(width/6);
   return(d<1 ? 1 : d);
  }
//+------------------------------------------------------------------+

Здесь всё просто: В метод передаётся ширина, от которой нужно рассчитать отступ. Расстояние между сегментами должно быть в шесть раз меньше ширины блока, но не менее одного пикселя.


Метод, сегментирующий фон:

//+------------------------------------------------------------------+
//| Сегментирует фон                                                 |
//+------------------------------------------------------------------+
void CBarProgressBar::Segmentation(void)
  {
//--- Если стиль отрисовки не "Сегмантированные блоки" - уходим
   if(this.Style()!=CANV_ELEMENT_PROGRESS_BAR_STYLE_BLOCKS)
      return;
//--- Обнуляем координату X сегмента
   this.m_segment_x=0;
//--- Получаем ширину блока как 3/4 его высоты
   int w=this.SegmentWidth();
//--- Получаем дистанцию между сегментами (в шесть раз меньше ширины блока)
   int d=this.SegmentDistance();
//--- Получаем начало отсчёта
   this.m_segment_s=w+(this.Height()>3 ? 1 : 0);
//--- В цикле от начала отсчёта до ширины полосы прогресса с шагом в ширину блока + отступ между сегментами
   for(int i=this.SegmentStart();i<this.Width();i+=w+d)
     {
      //--- рисуем пустой полностью прозрачный прямоугольник (стираем фон элемента)
      this.DrawRectangleFill(i,0,i+d-1,this.Height()-1,CLR_CANV_NULL,0);
      //--- Запоминаем координату X сегмента
      this.m_segment_x=i;
     }
//--- Если высота линии прогресса больше трёх пикселей рисуем по периметру полностью прозрачную рамку
   if(this.Height()>3)
      this.DrawRectangle(0,0,this.Width()-1,this.Height()-1,CLR_CANV_NULL,0);
  }
//+------------------------------------------------------------------+

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


В объект ProgressBar добавим возможность включать описание, отображаемое на полосе прогресса. По умолчанию описание не будет отображаться, но, чтобы его включить, достаточно просто определить его текст. Описание будет построено объектом CLabel, постоянно присоединённым к объекту. Естественно, самостоятельно можно будет создать сколько угодно таких объектов и разместить их в нужных местах. Но объект-описание полосы прогресса будет создаваться по умолчанию при создании ProgressBar, и к нему будет сделан упрощённый доступ при помощи специальных методов.

В файле класса CProgressBar \MQL5\Include\DoEasy\Objects\Graph\WForms\Common Controls\ProgressBar.mqh, удалим из приватной секции переменную для хранения количества пропускаемых шагов, так как надобности в ней не оказалось:

//+------------------------------------------------------------------+
//| Класс объекта ArrowLeftRightBox элементов управления WForms      |
//+------------------------------------------------------------------+
class CProgressBar : public CContainer
  {
private:
   int               m_progress_bar_max;  // Максимальная ширина полосы прогресса
   int               m_value_by_max;      // Значение Value относительно Maximum
   int               m_steps_skipped;     // Количество пропускаемых шагов увеличения ширины полосы прогресса

//--- Создаёт новый графический объект


Там же, в приватной секции объявим переменные для хранения свойств текстовой метки с описанием полосы прогресса:

//+------------------------------------------------------------------+
//| Класс объекта ArrowLeftRightBox элементов управления WForms      |
//+------------------------------------------------------------------+
class CProgressBar : public CContainer
  {
private:
   int               m_progress_bar_max;           // Максимальная ширина полосы прогресса
   int               m_value_by_max;               // Значение Value относительно Maximum
   int               m_progress_bar_text_x;        // Координата X текстовой метки с описанием полосы прогресса
   int               m_progress_bar_text_y;        // Координата Y текстовой метки с описанием полосы прогресса
   color             m_progress_bar_text_color;    // Цвет текста с описанием полосы прогресса
   uchar             m_progress_bar_text_opacity;  // Непрозрачность текста с описанием полосы прогресса
   string            m_progress_bar_text;          // Текст с описанием полосы прогресса
   ENUM_FRAME_ANCHOR m_progress_bar_text_anchor;   // Способ привязки текста с описанием полосы прогресса
//--- Создаёт новый графический объект

Переменные потребуются нам для установки и возврата свойств объекта-текстовой метки, выступающей в качестве описания полосы прогресса.


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

В публичную секцию класса добавим методы, возвращающие значение Value в текстовом представлении и значение Value в процентном отношении, представленные числовым и текстовыми значениями:

//--- (1) Устанавливает, (2) возвращает текущее значение полосы прогресса в диапазоне от Min до Max как число, (3) как текст
   void              SetValue(const int value);
   int               Value(void)                         const { return (int)this.GetProperty(CANV_ELEMENT_PROP_PROGRESS_BAR_VALUE);   }
   string            ValueDescription(void)              const { return (string)this.Value();                                          }
//--- Возвращает текущее значение полосы прогресса в диапазоне от Min до Max в процентном отношении (1) числом, (2) текстом
   double            ValuePercent(void) const;
   string            ValuePercentDescription(void)       const { return ::DoubleToString(this.ValuePercent(),2)+"%";                   }
   
//--- (1) Устанавливает, (2) возвращает верхнюю границу диапазона, в котором действует ProgressBar


Для упрощённого доступа к объекту-блику, в публичную секцию класса объявим методы для установки его свойств, добавим методы для получения указателей на создаваемые прикреплённые объекты, и объявим методы для работы с объектом-описанием полосы прогресса:

//--- (1) Устанавливает, (2) возвращает нижнюю границу диапазона, в котором действует ProgressBar
   void              SetMinimum(const int value)               { this.SetProperty(CANV_ELEMENT_PROP_PROGRESS_BAR_MINIMUM,value);       }
   int               Minimum(void)                       const { return (int)this.GetProperty(CANV_ELEMENT_PROP_PROGRESS_BAR_MINIMUM); }
   
//--- Устанавливает для объекта-блика (1) стиль, (2) непрозрачность, (3) цвет
   void              SetGlareStyle(const ENUM_CANV_ELEMENT_VISUAL_EFF_STYLE style);
   void              SetGlareOpacity(const uchar opacity);
   void              SetGlareColor(const color clr);

//--- Возвращает указатель на (1) объект-полосу прогресса, (2) объект-блик, (3) объект-текстовую метку с описанием полосы прогресса
   CBarProgressBar  *GetProgressBar(void)                { return this.GetElementByType(GRAPH_ELEMENT_TYPE_WF_BAR_PROGRESS_BAR,0);     }
   CGlareObj        *GetGlareObj(void)                   { return this.GetElementByType(GRAPH_ELEMENT_TYPE_WF_GLARE_OBJ,0);            }
   CLabel           *GetProgressDescriptionObj(void)     { return this.GetElementByType(GRAPH_ELEMENT_TYPE_WF_LABEL,0);                }

//--- Устанавливает в текстовую метку полосы прогресса (1) текст, (2) цвет,
//--- (3) непрозрачность, (4) X, (5) Y координаты, (6) шрифт, (7) размер, (8) флаги шрифта
   void              SetBarDescriptionText(const string text,const bool redraw=false);
   void              SetBarDescriptionColor(const color clr,const bool redraw=false,const bool set_init_color=false);
   void              SetBarDescriptionOpacity(const uchar opacity,const bool redraw=false);
   void              SetBarDescriptionX(const int x,const bool redraw=false);
   void              SetBarDescriptionY(const int y,const bool redraw=false);
   void              SetBarDescriptionFontName(const string font,const bool redraw=false);
   void              SetBarDescriptionFontSize(const int size,const bool relative=false,const bool redraw=false);
   void              SetBarDescriptionFontFlags(const uint flags,const bool redraw=false);
   
//--- (1) скрывает, (2) отображает текстовую метку полосы прогресса
   void              HideBarDescription(void);
   void              ShowBarDescription(void);
     
//--- Сбрасывает значения полосы прогресса к установленному минимальному


В конструкторе класса инициализируем объявленные переменные значениями по умолчанию:

//+------------------------------------------------------------------+
//| Инициализирует свойства элемента                                 |
//+------------------------------------------------------------------+
void CProgressBar::Initialize(void)
  {
   this.SetBorderSizeAll(1);
   this.SetBorderStyle(FRAME_STYLE_SIMPLE);
   this.SetBackgroundColor(CLR_DEF_CONTROL_PROGRESS_BAR_BACK_COLOR,true);
   this.SetBorderColor(CLR_DEF_CONTROL_PROGRESS_BAR_BORDER_COLOR,true);
   this.SetForeColor(CLR_DEF_CONTROL_PROGRESS_BAR_FORE_COLOR,true);
   this.SetMarqueeAnimationSpeed(10);
   this.SetMaximum(100);
   this.SetMinimum(0);
   this.SetValue(50);
   this.SetStep(10);
   this.SetStyle(CANV_ELEMENT_PROGRESS_BAR_STYLE_CONTINUOUS);
   this.m_progress_bar_max=this.Width()-this.BorderSizeLeft()-this.BorderSizeRight();
   this.m_value_by_max=this.Value()*100/this.Maximum();
   this.m_progress_bar_text="";
   this.m_progress_bar_text_x=1;
   this.m_progress_bar_text_y=0;
   this.m_progress_bar_text_color=CLR_DEF_CONTROL_PROGRESS_BAR_FORE_COLOR;
   this.m_progress_bar_text_opacity=255;
   this.m_progress_bar_text_anchor=FRAME_ANCHOR_LEFT_TOP;
  }
//+------------------------------------------------------------------+


В методе, создающем объект-полосу прогресса, создаётся и объект-блик. Значит, туда же добавим и создание объекта-описания полосы прогресса:

//+------------------------------------------------------------------+
//| Создаёт объект-полосу прогресса                                  |
//+------------------------------------------------------------------+
void CProgressBar::CreateProgressBar(void)
  {
//--- Устанавливаем длину полосы прогресса, равной значению Value() объекта
//--- Высота полосы прогресса устанавливается равной высоте объекта минус размеры рамки сверху и снизу
   int w=this.CalculateProgressBarWidth();
   int h=this.Height()-this.BorderSizeTop()-this.BorderSizeBottom();
   if(h<1)
      h=1;
//--- Создаём  объект-полосу прогресса
   this.CreateNewElement(GRAPH_ELEMENT_TYPE_WF_BAR_PROGRESS_BAR,0,0,w,h,clrNONE,255,false,false);
//--- Создаём описание полосы прогресса, получаем объект-текстовую метку и устанавливаем параметры по умолчанию
   this.CreateNewElement(GRAPH_ELEMENT_TYPE_WF_LABEL,1,0,this.Width()-2,this.Height()-2,clrNONE,0,false,false);
   CLabel *obj=this.GetElementByType(GRAPH_ELEMENT_TYPE_WF_LABEL,0);
   if(obj!=NULL)
     {
      obj.SetText(this.m_progress_bar_text);
      obj.SetFontName(DEF_FONT);
      obj.SetFontSize(DEF_FONT_SIZE);
      obj.SetTextAnchor(FRAME_ANCHOR_LEFT_TOP);
      obj.SetTextAlign(ANCHOR_LEFT_UPPER);
      this.HideBarDescription();
     }
//--- Создаём объект-блик
   this.CreateNewElement(GRAPH_ELEMENT_TYPE_WF_GLARE_OBJ,0,0,w,h,CLR_CANV_NULL,0,true,false);
//--- Добавляем текущий объект CProgressBar в список активных элементов коллекции
   this.AddObjToListActiveElements();
  }
//+------------------------------------------------------------------+

Сразу же после создания текстовой метки, получаем указатель на этот объект и устанавливаем ему некоторые свойства: текст, наименование шрифта, размер шрифта, способ привязки и выравнивание текста, а затем скрываем созданный объект. Остальные параметры объекту можно будет получить позже, получив указатель на этот объект при помощи метода GetProgressDescriptionObj(), а затем уже полученному объекту установить нужные свойства.
Если мы попытаемся создать объект-полосу прогресса с высотой менее одного пикселя, то ничего не выйдет — объект нельзя создать с нулевым размером. Поэтому введём проверку на нулевой размер и скорректируем его в случае, если это так.


В методе, создающем новый графический объект, добавим блок кода для создания объекта-текстовой метки:

//+------------------------------------------------------------------+
//| Создаёт новый графический объект                                 |
//+------------------------------------------------------------------+
CGCnvElement *CProgressBar::CreateNewGObject(const ENUM_GRAPH_ELEMENT_TYPE type,
                                             const int obj_num,
                                             const string descript,
                                             const int x,
                                             const int y,
                                             const int w,
                                             const int h,
                                             const color colour,
                                             const uchar opacity,
                                             const bool movable,
                                             const bool activity)
  {
   CGCnvElement *element=NULL;
   switch(type)
     {
      case GRAPH_ELEMENT_TYPE_WF_BAR_PROGRESS_BAR  :
         element=new CBarProgressBar(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h);
        break;
      case GRAPH_ELEMENT_TYPE_WF_GLARE_OBJ         :
         element=new CGlareObj(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h);
        break;
      case GRAPH_ELEMENT_TYPE_WF_LABEL             :
         element=new CLabel(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h);
        break;
      default:
        break;
     }
   if(element==NULL)
      ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_FAILED_CREATE_ELM_OBJ),this.TypeElementDescription(type));
   return element;
  }
//+------------------------------------------------------------------+

Теперь объект ProgressBar может создавать прикреплённые объекты с типом "Полоса прогресса", "Блик" и "Текстовая метка".


Объект "Индикатор прогресса" (ProgressBar) является активным элементом управления, обрабатываемым в таймере. В данный момент в таймере обрабатывается блик, пробегающий по полосе прогресса. Но так же, в таймере, нам нужно обрабатывать и полосу прогресса, отображаемую в стиле "Marquee". В этом режиме полоса прогресса имеет фиксированную ширину, равную половине ширины всего объекта, и постоянно прокручивается внутри объекта ProgressBar, тем самым показывая ход процесса, количество итераций которого заранее не известно.

Добавим в обработчик таймера объекта такую обработку полосы прогресса:

//+------------------------------------------------------------------+
//| Таймер                                                           |
//+------------------------------------------------------------------+
void CProgressBar::OnTimer(void)
  {
   CBarProgressBar *bar=this.GetProgressBar();
   if(bar!=NULL)
     {
      if(bar.Style()==CANV_ELEMENT_PROGRESS_BAR_STYLE_MARQUEE)
        {
         int x=bar.CoordX()+8;
         if(x>this.RightEdge())
            x=this.CoordX()-bar.Width();
         bar.Move(x,bar.CoordY());
         bar.Redraw(true);
        }
      else
         bar.OnTimer();
     }
  }
//+------------------------------------------------------------------+

Здесь: если стиль отображения полосы прогресса "Непрерывная прокрутка", то получаем координату X, на которую необходимо сместить объект. На данный момент мы к текущей координате X прибавляем смещение 8 пикселей и сдвигаем полосу прогресса на новую координату по горизонтали. Предварительно проверяем выход объекта-полосы прогресса полностью за пределы своего контейнера справа — в этом случае устанавливаем его координату X так, чтобы объект находился на величину своей ширину слева за левым краем своего контейнера. Метод Redraw() кроме перерисовки объекта выполняет ещё одну функцию — он обрезает выступающие за края контейнера части объекта. Таким образом у нас создан полный цикл перемещения полосы прогресса внутри своего контейнера, и при этом выступающие за края части обрезаются.
Если же объект имеет другой стиль отрисовки, то вызываем таймер объекта-полосы прогресса для вывода объекта-блика.


Так как у нас теперь есть стиль отрисовки полосы прогресса как "Сегментированные блоки", то нужно доработать метод, устанавливающий значение полосы прогресса. На данный момент этот метод после установки значения сразу же изменяет размер объекта в соответствии с новым значением. Но, так как, сегментированные блоки должны отрисовываться не попиксельно, а поблочно, то нам нужно понять сколько блоков может уместиться на установленную ширину полосы прогресса. Если ширины достаточно для рисования нового сегмента, то у объекта изменяется его ширина, уже установленная в его свойство. Если же ширины не хватит (текущий рисуемый сегмент окажется не полным, обрезанным), то нужно из установленной ширины объекта вычесть ширину одного сегмента и установить для полосы прогресса именно такую ширину — тогда будет отрисован лишь предпоследний сегмент со всеми предыдущими. Таким образом, ширина объекта всегда будет изменяться только тогда, когда все сегменты могут быть полностью нарисованы без их обрезания, что даст нам поблочное изменение видимой ширины полосы прогресса. При этом значение Value всегда будет тем, которое передано в объект. Т.е., мы только визуально изменяем дискретно ширину объекта, а значение Value в нём всегда то, которое для него установлено.

Внесём описанные выше доработки в метод, устанавливающий текущее значение полосы прогресса:

//+------------------------------------------------------------------+
//| Устанавливает текущее значение полосы прогресса                  |
//+------------------------------------------------------------------+
void CProgressBar::SetValue(const int value)
  {
//--- Корректируем переданное в метод значение value и устанавливаем его в свойство объекта
   int v=(value<this.Minimum() ? this.Minimum() : value>this.Maximum() ? this.Maximum() : value);
   this.SetProperty(CANV_ELEMENT_PROP_PROGRESS_BAR_VALUE,v);
//--- Получаем объект-полосу прогресса
   CBarProgressBar *bar=this.GetProgressBar();
   if(bar!=NULL)
     {
      //--- Устанавливаем для полосы прогресса значение value
      bar.SetValue(v);
      //--- Рассчитываем ширину объекта-полосы прогресса
      int w=this.CalculateProgressBarWidth();
      //--- Если рассчитанное значение ширины больше максимально-возможной - делаем ширину максимальной
      if(w>this.m_progress_bar_max)
         w=this.m_progress_bar_max;
      //--- Если значение ширины меньше 1, то
      if(w<1)
        {
         //--- скрываем полосу прогресса и перерисовываем график для немедленного отображения изменений
         bar.Hide();
         ::ChartRedraw(bar.ChartID());
        }
      //--- Если значение ширины не меньше 1
      else
        {
         //--- Если полоса прогресса скрыта - отображаем её и
         if(!bar.IsVisible())
            bar.Show();
         //--- Если стиль полосы прогресса "Непрерывная линия" - изменяем ширину объекта на рассчитанное значение
         if(this.Style()==CANV_ELEMENT_PROGRESS_BAR_STYLE_CONTINUOUS)
            bar.Resize(w,bar.Height(),true);
         //--- Иначе, если стиль полосы прогресса "Сегментированные блоки"
         else if(this.Style()==CANV_ELEMENT_PROGRESS_BAR_STYLE_BLOCKS)
           {
            //--- Ширина сегмента вместе с отступом
            int wd=bar.SegmentWidth()+bar.SegmentDistance();
            //--- Количество сегментов, умещающихся по ширине полосы прогресса
            int num=w/(wd>0 ? wd : 1);
            //--- Координата X последнего сегмента, рассчитанная от начала отсчёта сегментов
            int wx=bar.SegmentStart()+num*wd;
            //--- Если рассчитанная ширина полосы прогресса меньше координаты последнего сегмента, и это не последний сегмент,
            //--- устанавливаем ширину полосы прогресса равной координате предпоследнего сегмента
            if(w<wx-bar.SegmentDistance() && w<this.m_progress_bar_max)
               w=wx-wd+bar.SegmentDistance();
            //--- Если рассчитанная ширина полосы прогресса меньше координаты последнего сегмента, или это не последний сегмент
            if(w<wx-bar.SegmentDistance() || w==this.m_progress_bar_max)
               //--- изменяем размер полосы прогресса в соответствии с полученной и скорректированной шириной
               bar.Resize(w,bar.Height(),true);
           }
        }
      //--- Если текст описания полосы прогресса задан,
      if(this.m_progress_bar_text!="")
        {
         //--- получаем объект-описание полосы прогресса и выводим его на передний план
         CLabel *obj=this.GetProgressDescriptionObj();
         if(obj!=NULL)
            obj.BringToTop();
        }
     }
  }
//+------------------------------------------------------------------+

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


Метод, рассчитывающий ширину полосы прогресса, нужно доработать таким образом, чтобы он возвращал половину размера элемента управления ProgressBar в случае, если для полосы прогресса задан стиль "Непрерывная прокрутка":

//+------------------------------------------------------------------+
//| Рассчитывает ширину полосы прогресса                             |
//+------------------------------------------------------------------+
int CProgressBar::CalculateProgressBarWidth(void)   

  {
   this.m_value_by_max=this.Value()*100/this.Maximum();
   return(this.Style()==CANV_ELEMENT_PROGRESS_BAR_STYLE_MARQUEE ? this.m_progress_bar_max/2 : this.m_progress_bar_max*this.m_value_by_max/100);
  }
//+------------------------------------------------------------------+

Теперь метод будет возвращать верные размеры полосы прокрутки при любом её стиле отображения.


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

//+------------------------------------------------------------------+
//| Устанавливает новую ширину                                       |
//+------------------------------------------------------------------+
bool CProgressBar::SetWidth(const int width)
  {
   if(!CGCnvElement::SetWidth(width))
      return false;
   this.m_progress_bar_max=this.Width()-this.BorderSizeLeft()-this.BorderSizeRight();
   CBarProgressBar *bar=this.GetProgressBar();
   if(bar==NULL)
      return false;
   int w=this.CalculateProgressBarWidth();
   bar.SetWidth(w);
   CLabel *lbl=this.GetProgressDescriptionObj();
   if(lbl!=NULL)
      lbl.SetWidth(w);
   return true;
  }
//+------------------------------------------------------------------+

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

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

Метод, устанавливающий для объекта-блика стиль:

//+------------------------------------------------------------------+
//| Устанавливает для объекта-блика стиль                            |
//+------------------------------------------------------------------+
void CProgressBar::SetGlareStyle(const ENUM_CANV_ELEMENT_VISUAL_EFF_STYLE style)
  {
   CGlareObj *obj=this.GetGlareObj();
   if(obj==NULL)
      return;
   obj.SetVisualEffectStyle(style);
  }
//+------------------------------------------------------------------+

Здесь: получаем указатель на объект-блик и устанавливаем стиль в полученный объект.


Метод, устанавливающий для объекта-блика непрозрачность:

//+------------------------------------------------------------------+
//| Устанавливает для объекта-блика непрозрачность                   |
//+------------------------------------------------------------------+
void CProgressBar::SetGlareOpacity(const uchar opacity)
  {
   CGlareObj *obj=this.GetGlareObj();
   if(obj==NULL)
      return;
   obj.SetOpacity(opacity);
  }
//+------------------------------------------------------------------+

Получаем указатель на объект-блик и устанавливаем значение непрозрачности в полученный объект.


Метод, устанавливающий для объекта-блика цвет:

//+------------------------------------------------------------------+
//| Устанавливает для объекта-блика цвет                             |
//+------------------------------------------------------------------+
void CProgressBar::SetGlareColor(const color clr)
  {
   CGlareObj *obj=this.GetGlareObj();
   if(obj==NULL)
      return;
   obj.SetColor(clr);
  }
//+------------------------------------------------------------------+

Получаем указатель на объект-блик и устанавливаем значение цвета, переданное в метод, в полученный объект.


Метод, устанавливающий в текстовую метку полосы прогресса текст:

//+------------------------------------------------------------------+
//| Устанавливает в текстовую метку полосы прогресса текст           |
//+------------------------------------------------------------------+
void CProgressBar::SetBarDescriptionText(const string text,const bool redraw=false)
  {
   CLabel *obj=this.GetProgressDescriptionObj();
   if(obj==NULL)
      return;
   this.m_progress_bar_text=text;
   obj.SetText(text);
   obj.Erase(false);
   obj.Text(this.m_progress_bar_text_x,this.m_progress_bar_text_y,this.m_progress_bar_text,this.m_progress_bar_text_color,this.m_progress_bar_text_opacity,this.m_progress_bar_text_anchor);
   obj.Update(redraw);
  }
//+------------------------------------------------------------------+

Здесь: получаем указатель на объект-текстовую метку. Устанавливаем в переменную m_progress_bar_text переданный в метод текст, и далее этот текст устанавливаем в объект. Так как текст сразу же будет выводиться на объекте, то во-избежание накладывания одного текста на другой, фон и всё, что на нём было нарисовано нужно стереть. Далее выводим текст и обновляем элемент.


Метод, устанавливающий в текстовую метку полосы прогресса цвет текста:

//+------------------------------------------------------------------+
//| Устанавливает в текстовую метку полосы прогресса цвет текста     |
//+------------------------------------------------------------------+
void CProgressBar::SetBarDescriptionColor(const color clr,const bool redraw=false,const bool set_init_color=false)
  {
   CLabel *obj=this.GetProgressDescriptionObj();
   if(obj==NULL)
      return;
   this.m_progress_bar_text_color=clr;
   obj.SetForeColor(clr,false);
   obj.Erase(false);
   obj.Text(this.m_progress_bar_text_x,this.m_progress_bar_text_y,this.m_progress_bar_text,this.m_progress_bar_text_color,this.m_progress_bar_text_opacity,this.m_progress_bar_text_anchor);
   obj.Update(redraw);
  }
//+------------------------------------------------------------------+

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


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

//+------------------------------------------------------------------+
//| Устанавливает в текстовую метку полосы прогресса непрозрачность  |
//+------------------------------------------------------------------+
void CProgressBar::SetBarDescriptionOpacity(const uchar opacity,const bool redraw=false)
  {
   CLabel *obj=this.GetProgressDescriptionObj();
   if(obj==NULL)
      return;
   this.m_progress_bar_text_opacity=opacity;
   obj.Erase(false);
   obj.Text(this.m_progress_bar_text_x,this.m_progress_bar_text_y,this.m_progress_bar_text,this.m_progress_bar_text_color,this.m_progress_bar_text_opacity,this.m_progress_bar_text_anchor);
   obj.Update(redraw);
  }
//+------------------------------------------------------------------+

Логика метода идентична вышерассмотренным, но в соответствующую переменную и объект устанавливаем значение непрозрачности.


Метод, устанавливающий в текстовую метку полосы прогресса координату X:

//+------------------------------------------------------------------+
//| Устанавливает в текстовую метку полосы прогресса координату X    |
//+------------------------------------------------------------------+
void CProgressBar::SetBarDescriptionX(const int x,const bool redraw=false)
  {
   CLabel *obj=this.GetProgressDescriptionObj();
   if(obj==NULL)
      return;
   this.m_progress_bar_text_x=x;
   obj.Erase(false);
   obj.Text(this.m_progress_bar_text_x,this.m_progress_bar_text_y,this.m_progress_bar_text,this.m_progress_bar_text_color,this.m_progress_bar_text_opacity,this.m_progress_bar_text_anchor);
   obj.Update(redraw);
  }
//+------------------------------------------------------------------+


Метод, устанавливающий в текстовую метку полосы прогресса координату Y:

//+------------------------------------------------------------------+
//| Устанавливает в текстовую метку полосы прогресса координату Y    |
//+------------------------------------------------------------------+
void CProgressBar::SetBarDescriptionY(const int y,const bool redraw=false)
  {
   CLabel *obj=this.GetProgressDescriptionObj();
   if(obj==NULL)
      return;
   this.m_progress_bar_text_y=y;
   obj.Erase(false);
   obj.Text(this.m_progress_bar_text_x,this.m_progress_bar_text_y,this.m_progress_bar_text,this.m_progress_bar_text_color,this.m_progress_bar_text_opacity,this.m_progress_bar_text_anchor);
   obj.Update(redraw);
  }
//+------------------------------------------------------------------+


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

//+------------------------------------------------------------------+
//| Устанавливает в текстовую метку полосы прогресса шрифт           |
//+------------------------------------------------------------------+
void CProgressBar::SetBarDescriptionFontName(const string font,const bool redraw=false)
  {
   CLabel *obj=this.GetProgressDescriptionObj();
   if(obj==NULL)
      return;
   obj.SetFontName(font);
   obj.Erase(false);
   obj.Text(this.m_progress_bar_text_x,this.m_progress_bar_text_y,this.m_progress_bar_text,this.m_progress_bar_text_color,this.m_progress_bar_text_opacity,this.m_progress_bar_text_anchor);
   obj.Update(redraw);
  }
//+------------------------------------------------------------------+


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

//+------------------------------------------------------------------+
//| Устанавливает в текстовую метку полосы прогресса размер шрифта   |
//+------------------------------------------------------------------+
void CProgressBar::SetBarDescriptionFontSize(const int size,const bool relative=false,const bool redraw=false)
  {
   CLabel *obj=this.GetProgressDescriptionObj();
   if(obj==NULL)
      return;
   obj.SetFontSize(size,relative);
   obj.Erase(false);
   obj.Text(this.m_progress_bar_text_x,this.m_progress_bar_text_y,this.m_progress_bar_text,this.m_progress_bar_text_color,this.m_progress_bar_text_opacity,this.m_progress_bar_text_anchor);
   obj.Update(redraw);
  }
//+------------------------------------------------------------------+


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

//+------------------------------------------------------------------+
//| Устанавливает в текстовую метку полосы прогресса флаги шрифта    |
//+------------------------------------------------------------------+
void CProgressBar::SetBarDescriptionFontFlags(const uint flags,const bool redraw=false)
  {
   CLabel *obj=this.GetProgressDescriptionObj();
   if(obj==NULL)
      return;
   obj.SetFontFlags(flags);
   obj.Erase(false);
   obj.Text(this.m_progress_bar_text_x,this.m_progress_bar_text_y,this.m_progress_bar_text,this.m_progress_bar_text_color,this.m_progress_bar_text_opacity,this.m_progress_bar_text_anchor);
   obj.Update(redraw);
  }
//+------------------------------------------------------------------+

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


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

Метод, скрывающий текстовую метку полосы прогресса:

//+------------------------------------------------------------------+
//| Скрывает текстовую метку полосы прогресса                        |
//+------------------------------------------------------------------+
void CProgressBar::HideBarDescription(void)
  {
   CLabel *obj=this.GetProgressDescriptionObj();
   if(obj==NULL)
      return;
   obj.SetDisplayed(false);
   obj.Hide();
  }
//+------------------------------------------------------------------+

Здесь: получаем объект-текстовую метку с описанием полосы прогресса, устанавливаем для него флаг неотображения и скрываем объект.


Метод, отображающий текстовую метку полосы прогресса:

//+------------------------------------------------------------------+
//| Отображает текстовую метку полосы прогресса                      |
//+------------------------------------------------------------------+
void CProgressBar::ShowBarDescription(void)
  {
   CLabel *obj=this.GetProgressDescriptionObj();
   if(obj==NULL)
      return;
   obj.SetDisplayed(true);
   obj.Show();
  }
//+------------------------------------------------------------------+

Здесь: получаем указатель на объект-текстовую метку с описанием полосы прогресса, устанавливаем ему флаг отображения и показываем объект.


Метод, возвращающий текущее значение полосы прогресса в диапазоне от Min до Max в процентном отношении:

//+------------------------------------------------------------------+
//| Возвращает текущее значение полосы прогресса                     |
//| в диапазоне от Min до Max в процентном отношении                 |
//+------------------------------------------------------------------+
double CProgressBar::ValuePercent(void) const
  {
   double range=this.Maximum()-this.Minimum();
   return(this.Value()*100.0/(range>0 ? range : 1));
  }
//+------------------------------------------------------------------+

Если нам необходимо получить значение Value полосы прогресса в процентах диапазона от Min до Max, то этот метод рассчитает и вернёт значение того, сколько процентов из заданного диапазона уже обработано. За 100% принимается разница максимального и минимального значений, установленные в объекте ProgressBar.

Проверим что у нас получилось.


Тестирование

Для теста возьмём советник из прошлой статьи и сохраним его в новой папке \MQL5\Experts\TestDoEasy\Part128\ под новым именем TestDoEasy128.mq5.

Создадим перечисление стилей полосы прогресса для английской версии компиляции и версии компиляции на языке пользователя:

//--- enumerations by compilation language
#ifdef COMPILE_EN
enum ENUM_AUTO_SIZE_MODE
  {
   AUTO_SIZE_MODE_GROW=CANV_ELEMENT_AUTO_SIZE_MODE_GROW,                               // Grow
   AUTO_SIZE_MODE_GROW_SHRINK=CANV_ELEMENT_AUTO_SIZE_MODE_GROW_SHRINK                  // Grow and Shrink
  };
enum ENUM_BORDER_STYLE
  {
   BORDER_STYLE_NONE=FRAME_STYLE_NONE,                                                 // None
   BORDER_STYLE_SIMPLE=FRAME_STYLE_SIMPLE,                                             // Simple
   BORDER_STYLE_FLAT=FRAME_STYLE_FLAT,                                                 // Flat
   BORDER_STYLE_BEVEL=FRAME_STYLE_BEVEL,                                               // Embossed (bevel)
   BORDER_STYLE_STAMP=FRAME_STYLE_STAMP,                                               // Embossed (stamp)
  };
enum ENUM_CHEK_STATE
  {
   CHEK_STATE_UNCHECKED=CANV_ELEMENT_CHEK_STATE_UNCHECKED,                             // Unchecked
   CHEK_STATE_CHECKED=CANV_ELEMENT_CHEK_STATE_CHECKED,                                 // Checked
   CHEK_STATE_INDETERMINATE=CANV_ELEMENT_CHEK_STATE_INDETERMINATE,                     // Indeterminate
  };
enum ENUM_ELEMENT_ALIGNMENT
  {
   ELEMENT_ALIGNMENT_TOP=CANV_ELEMENT_ALIGNMENT_TOP,                                   // Top
   ELEMENT_ALIGNMENT_BOTTOM=CANV_ELEMENT_ALIGNMENT_BOTTOM,                             // Bottom
   ELEMENT_ALIGNMENT_LEFT=CANV_ELEMENT_ALIGNMENT_LEFT,                                 // Left
   ELEMENT_ALIGNMENT_RIGHT=CANV_ELEMENT_ALIGNMENT_RIGHT,                               // Right
  };
enum ENUM_ELEMENT_TAB_SIZE_MODE
  {
   ELEMENT_TAB_SIZE_MODE_NORMAL=CANV_ELEMENT_TAB_SIZE_MODE_NORMAL,                     // Fit to tab title text width
   ELEMENT_TAB_SIZE_MODE_FIXED=CANV_ELEMENT_TAB_SIZE_MODE_FIXED,                       // Fixed size
   ELEMENT_TAB_SIZE_MODE_FILL=CANV_ELEMENT_TAB_SIZE_MODE_FILL,                         // Fit TabControl Size
  };
enum ENUM_ELEMENT_PROGRESS_BAR_STYLE
  {
   ELEMENT_PROGRESS_BAR_STYLE_BLOCKS=CANV_ELEMENT_PROGRESS_BAR_STYLE_BLOCKS,           // Blocks
   ELEMENT_PROGRESS_BAR_STYLE_CONTINUOUS=CANV_ELEMENT_PROGRESS_BAR_STYLE_CONTINUOUS,   // Continuous
   ELEMENT_PROGRESS_BAR_STYLE_MARQUEE=CANV_ELEMENT_PROGRESS_BAR_STYLE_MARQUEE,         // Marquee
  };  
#else 
enum ENUM_AUTO_SIZE_MODE
  {
   AUTO_SIZE_MODE_GROW=CANV_ELEMENT_AUTO_SIZE_MODE_GROW,                               // Только увеличение
   AUTO_SIZE_MODE_GROW_SHRINK=CANV_ELEMENT_AUTO_SIZE_MODE_GROW_SHRINK                  // Увеличение и уменьшение
  };
enum ENUM_BORDER_STYLE
  {
   BORDER_STYLE_NONE=FRAME_STYLE_NONE,                                                 // Нет рамки
   BORDER_STYLE_SIMPLE=FRAME_STYLE_SIMPLE,                                             // Простая рамка
   BORDER_STYLE_FLAT=FRAME_STYLE_FLAT,                                                 // Плоская рамка
   BORDER_STYLE_BEVEL=FRAME_STYLE_BEVEL,                                               // Рельефная (выпуклая)
   BORDER_STYLE_STAMP=FRAME_STYLE_STAMP,                                               // Рельефная (вдавленная)
  };
enum ENUM_CHEK_STATE
  {
   CHEK_STATE_UNCHECKED=CANV_ELEMENT_CHEK_STATE_UNCHECKED,                             // Не установлен
   CHEK_STATE_CHECKED=CANV_ELEMENT_CHEK_STATE_CHECKED,                                 // Установлен
   CHEK_STATE_INDETERMINATE=CANV_ELEMENT_CHEK_STATE_INDETERMINATE,                     // Неопределённый
  };
enum ENUM_ELEMENT_ALIGNMENT
  {
   ELEMENT_ALIGNMENT_TOP=CANV_ELEMENT_ALIGNMENT_TOP,                                   // Сверху
   ELEMENT_ALIGNMENT_BOTTOM=CANV_ELEMENT_ALIGNMENT_BOTTOM,                             // Снизу
   ELEMENT_ALIGNMENT_LEFT=CANV_ELEMENT_ALIGNMENT_LEFT,                                 // Слева
   ELEMENT_ALIGNMENT_RIGHT=CANV_ELEMENT_ALIGNMENT_RIGHT,                               // Справа
  };
enum ENUM_ELEMENT_TAB_SIZE_MODE
  {
   ELEMENT_TAB_SIZE_MODE_NORMAL=CANV_ELEMENT_TAB_SIZE_MODE_NORMAL,                     // По ширине текста заголовка вкладки
   ELEMENT_TAB_SIZE_MODE_FIXED=CANV_ELEMENT_TAB_SIZE_MODE_FIXED,                       // Фиксированный размер
   ELEMENT_TAB_SIZE_MODE_FILL=CANV_ELEMENT_TAB_SIZE_MODE_FILL,                         // По размеру элемента управления TabControl
  };
enum ENUM_ELEMENT_PROGRESS_BAR_STYLE
  {
   ELEMENT_PROGRESS_BAR_STYLE_BLOCKS=CANV_ELEMENT_PROGRESS_BAR_STYLE_BLOCKS,           // Сегментированные блоки
   ELEMENT_PROGRESS_BAR_STYLE_CONTINUOUS=CANV_ELEMENT_PROGRESS_BAR_STYLE_CONTINUOUS,   // Непрерывная полоса
   ELEMENT_PROGRESS_BAR_STYLE_MARQUEE=CANV_ELEMENT_PROGRESS_BAR_STYLE_MARQUEE,         // Непрерывная прокрутка
  };  
#endif 
//--- input parameters


Во входные параметры добавим два новых параметра — стиль полосы прогресса и флаг вывода значений полосы прогресса в процентах:

//--- input parameters
sinput   bool                          InpMovable           =  true;                   // Panel Movable flag
sinput   ENUM_INPUT_YES_NO             InpAutoSize          =  INPUT_YES;              // Panel Autosize
sinput   ENUM_AUTO_SIZE_MODE           InpAutoSizeMode      =  AUTO_SIZE_MODE_GROW;    // Panel Autosize mode
sinput   ENUM_BORDER_STYLE             InpFrameStyle        =  BORDER_STYLE_SIMPLE;    // Label border style
sinput   ENUM_ANCHOR_POINT             InpTextAlign         =  ANCHOR_CENTER;          // Label text align
sinput   ENUM_INPUT_YES_NO             InpTextAutoSize      =  INPUT_NO;               // Label autosize
sinput   ENUM_ANCHOR_POINT             InpCheckAlign        =  ANCHOR_LEFT;            // Check flag align
sinput   ENUM_ANCHOR_POINT             InpCheckTextAlign    =  ANCHOR_LEFT;            // Check label text align
sinput   ENUM_CHEK_STATE               InpCheckState        =  CHEK_STATE_UNCHECKED;   // Check flag state
sinput   ENUM_INPUT_YES_NO             InpCheckAutoSize     =  INPUT_YES;              // CheckBox autosize
sinput   ENUM_BORDER_STYLE             InpCheckFrameStyle   =  BORDER_STYLE_NONE;      // CheckBox border style
sinput   ENUM_ANCHOR_POINT             InpButtonTextAlign   =  ANCHOR_CENTER;          // Button text align
sinput   ENUM_INPUT_YES_NO             InpButtonAutoSize    =  INPUT_YES;              // Button autosize
sinput   ENUM_AUTO_SIZE_MODE           InpButtonAutoSizeMode=  AUTO_SIZE_MODE_GROW;    // Button Autosize mode
sinput   ENUM_BORDER_STYLE             InpButtonFrameStyle  =  BORDER_STYLE_NONE;      // Button border style
sinput   bool                          InpButtonToggle      =  true ;                  // Button toggle flag
sinput   bool                          InpButtListMSelect   =  false;                  // ButtonListBox Button MultiSelect flag
sinput   bool                          InpListBoxMColumn    =  true;                   // ListBox MultiColumn flag
sinput   bool                          InpTabCtrlMultiline  =  false;                   // Tab Control Multiline flag
sinput   ENUM_ELEMENT_ALIGNMENT        InpHeaderAlignment   =  ELEMENT_ALIGNMENT_TOP;  // TabHeader Alignment
sinput   ENUM_ELEMENT_TAB_SIZE_MODE    InpTabPageSizeMode   =  ELEMENT_TAB_SIZE_MODE_FILL; // TabHeader Size Mode
sinput   int                           InpTabControlX       =  10;                     // TabControl X coord
sinput   int                           InpTabControlY       =  20;                     // TabControl Y coord
sinput   ENUM_CANV_ELEMENT_TOOLTIP_ICON InpTooltipIcon      =  CANV_ELEMENT_TOOLTIP_ICON_NONE;  // Tooltip Icon
sinput   string                        InpTooltipTitle      =  "";                     // Tooltip Title
sinput   ENUM_ELEMENT_PROGRESS_BAR_STYLE InpProgressBarStyle=  ELEMENT_PROGRESS_BAR_STYLE_BLOCKS;  // Progress Bar Style
sinput   bool                          InpProgressBarPercent=  false;                  // Show progress bar values as a percentage
//--- global variables


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

                     //--- Если это первая вкладка и вторая панель
                     if(n==0 && j==1)
                       {
                        //--- Создадим на ней элемент управления ProgressBar
                        if(split_container.CreateNewElement(j,GRAPH_ELEMENT_TYPE_WF_PROGRESS_BAR,4,4,100,12,clrNONE,255,false,false))
                          {
                           CProgressBar *progress_bar=split_container.GetPanelElementByType(j,GRAPH_ELEMENT_TYPE_WF_PROGRESS_BAR,0);
                           if(progress_bar!=NULL)
                             {
                              //--- Установим стиль полосы прогресса, заданный в настройках советника
                              progress_bar.SetStyle((ENUM_CANV_ELEMENT_PROGRESS_BAR_STYLE)InpProgressBarStyle);
                              //--- Установим параметры для описания полосы прогресса
                              progress_bar.SetBarDescriptionText("Progress Bar ");
                              progress_bar.SetBarDescriptionColor(panel.BackgroundColor());
                              progress_bar.SetBarDescriptionOpacity(255);
                              progress_bar.SetBarDescriptionY(-2);
                             }
                          }
                       }


В цикле отображения и перерисовки всех панелей отобразим описание при стиле полосы прогресса "Непрерывная линия" и на каждой итерации цикла приращения значения Value полосы прогресса будем выводить в описани значения Value в зависимости от настроек — в процентах или в завершённых шагах. По окончании цикла запишем в описание полосы прогресса сообщение о завершении цикла приращения изменённым на стиль Bold шрифтом.
Для установки параметров объекта-блика будем использовать методы быстрого доступа к этому объекту:

//--- Отобразим и перерисуем все созданные панели
   for(int i=0;i<FORMS_TOTAL;i++)
     {
      //--- Получаем объект-панель
      pnl=engine.GetWFPanel("WinForms Panel"+(string)i);
      if(pnl!=NULL)
        {
         //--- отображаем и перерисовываем панель
         pnl.Show();
         pnl.Redraw(true);
         //--- Получаем с панели объект TabControl
         CTabControl *tc=pnl.GetElementByType(GRAPH_ELEMENT_TYPE_WF_TAB_CONTROL,0);
         //--- С первой вкладки объекта TabControl получаем объект SplitContainer
         CSplitContainer *sc=tc.GetTabElementByType(0,GRAPH_ELEMENT_TYPE_WF_SPLIT_CONTAINER,0);
         //--- С объекта SplitContainer получаем его вторую панель
         CSplitContainerPanel *scp=sc.GetPanel(1);
         //--- С полученной панели получаем объект ProgressBar
         CProgressBar *pb=scp.GetElementByType(GRAPH_ELEMENT_TYPE_WF_PROGRESS_BAR,0);
         //--- Ожидаем 1/10 секунды
         Sleep(100);
         //--- Получаем ширину объекта ProgressBar
         int w=pb.Width();
         //--- В цикле с задержкой 1/50 увеличиваем ширину ProgressBar на 180 пикселей
         for(int n=0;n<180;n++)
           {
            Sleep(20);
            pb.Resize(w+n,pb.Height(),true);
           }
         //--- Устанавливаем значения для PerformStep объекта ProgressBar
         pb.SetValuesForProcessing(0,350,1,0);
         //--- Сбрасываем полосу прогресса объекта ProgressBar в минимальное положение
         pb.ResetProgressBar();
         //--- Если стиль полосы прогресса "Непрерывная линия" - отображаем описание полосы прогресса
         if(pb.Style()==CANV_ELEMENT_PROGRESS_BAR_STYLE_CONTINUOUS)
            pb.ShowBarDescription();
         //--- Ожидаем 1/5 секунды
         Sleep(200);
         //--- Если стиль полосы прогресса не "Непрерывная прокрутка"
         if(pb.Style()!=CANV_ELEMENT_PROGRESS_BAR_STYLE_MARQUEE)
           {
            //--- В цикле от минимального до максимального значения ProgressBar
            for(int n=0;n<=pb.Maximum();n++)
              {
               //--- вызываем метод увеличения полосы прогресса на заданный шаг с ожиданием в 1/5 секунды
               pb.PerformStep();
               //--- Записываем в описание полосы прогресса количество пройденных шагов
               pb.SetBarDescriptionText("Progress Bar, pass: "+(InpProgressBarPercent ? pb.ValuePercentDescription() : pb.ValueDescription()));
               Sleep(20);
              }
           }
         //--- Ожидаем 1/2 секунды, устанавливаем для описания тип шрифта Bold и пишем на полосе прогресса сообщение о выполнении
         Sleep(500);
         pb.SetBarDescriptionFontFlags(FW_BOLD);
         pb.SetBarDescriptionText("Progress Bar: Done");
         //--- Устанавливаем для объекта-блика тип - прямоугольник, непрозрачность 40, цвет - белый
         pb.SetGlareStyle(CANV_ELEMENT_VISUAL_EFF_STYLE_RECTANGLE);
         pb.SetGlareOpacity(40);
         pb.SetGlareColor(clrWhite);
        }
     }
        
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+


Скомпилируем советник и запустим его на графике:


Как видим все заявленные режимы нормально работают.


Что дальше

В следующей статье начнём разработку элемента управления TrackBar

К содержанию

*Статьи этой серии:

 
DoEasy. Элементы управления (Часть 26): Дорабатываем WinForms-объект "ToolTip" и начинаем разработку индикатора выполнения "ProgressBar"
DoEasy. Элементы управления (Часть 27): Продолжаем работу над WinForms-объектом "ProgressBar"