Графика в библиотеке DoEasy (Часть 82): Рефакторинг объектов библиотеки и коллекция графических объектов

Artyom Trishkin | 11 сентября, 2021

Содержание


Концепция

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

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

И вот тут нас настигла необходимость доработать все объекты библиотеки. Нам необходимо сделать так, чтобы каждый объект имел свой уникальный идентификатор — Type(), по которому мы сможем определить что это за объект. Дело в том, что каждый объект библиотеки должен уметь создавать графические объекты, а графический объект должен знать, "кто" его создал. И вот после создания графического объекта любым объектом библиотеки, графический объект должен знать, кто его создал, иметь указатель на своего родителя, а родитель должен знать свои порождённые графические объекты и тоже иметь на них указатели.

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

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

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


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

Начнём доработку с внесения новых макроподстановок и перечислений в файле \MQL5\Include\DoEasy\Defines.mqh. Нам необходимо добавить типы всех объектов библиотеки — чтобы эти значения записывать в свойство "тип" объекта сразу при его создании.

Но сначала добавим макроподстановки для указания параметров таймера коллекции графических объектов в конец списка параметров таймеров уже имеющихся коллекций:

//--- Параметры таймера коллекции чартов
#define COLLECTION_CHARTS_PAUSE        (500)                      // Пауза таймера коллекции чартов в миллисекундах
#define COLLECTION_CHARTS_COUNTER_STEP (16)                       // Шаг приращения счётчика таймера чартов
#define COLLECTION_CHARTS_COUNTER_ID   (9)                        // Идентификатор счётчика таймера чартов
//--- Параметры таймера коллекции графических объектов
#define COLLECTION_GRAPH_OBJ_PAUSE        (250)                   // Пауза таймера коллекции графических объектов в миллисекундах
#define COLLECTION_GRAPH_OBJ_COUNTER_STEP (16)                    // Шаг приращения счётчика таймера графических объектов
#define COLLECTION_GRAPH_OBJ_COUNTER_ID   (10)                    // Идентификатор счётчика таймера графических объектов

//--- Идентификаторы списков коллекций


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

//--- Идентификаторы списков коллекций
#define COLLECTION_HISTORY_ID          (0x777A)                   // Идентификатор списка исторической коллекции
#define COLLECTION_MARKET_ID           (0x777B)                   // Идентификатор списка рыночной коллекции
#define COLLECTION_EVENTS_ID           (0x777C)                   // Идентификатор списка коллекции событий
#define COLLECTION_ACCOUNT_ID          (0x777D)                   // Идентификатор списка коллекции аккаунтов
#define COLLECTION_SYMBOLS_ID          (0x777E)                   // Идентификатор списка коллекции символов
#define COLLECTION_SERIES_ID           (0x777F)                   // Идентификатор списка коллекции таймсерий
#define COLLECTION_BUFFERS_ID          (0x7780)                   // Идентификатор списка коллекции индикаторных буферов
#define COLLECTION_INDICATORS_ID       (0x7781)                   // Идентификатор списка коллекции индикаторов
#define COLLECTION_INDICATORS_DATA_ID  (0x7782)                   // Идентификатор списка коллекции индикаторных данных
#define COLLECTION_TICKSERIES_ID       (0x7783)                   // Идентификатор списка коллекции тиковых серий
#define COLLECTION_MBOOKSERIES_ID      (0x7784)                   // Идентификатор списка коллекции серий стаканов цен
#define COLLECTION_MQL5_SIGNALS_ID     (0x7785)                   // Идентификатор списка коллекции mql5-сигналов
#define COLLECTION_CHARTS_ID           (0x7786)                   // Идентификатор списка коллекции чартов
#define COLLECTION_CHART_WND_ID        (0x7787)                   // Идентификатор списка окон чартов
#define COLLECTION_GRAPH_OBJ_ID        (0x7788)                   // Идентификатор списка коллекции графических объектов

#define COLLECTION_ID_LIST_END         (COLLECTION_GRAPH_OBJ_ID)  // Конец списка идентификаторов коллекций

//--- Идентификаторы типов отложенных запросов

Именно значение этой метки + 1 у нас будет значением для первой константы перечисления типов объектов библиотеки, которое мы сейчас добавим:

//--- Параметры канваса
#define PAUSE_FOR_CANV_UPDATE          (16)                       // Частота обновления канваса
#define NULL_COLOR                     (0x00FFFFFF)               // Ноль для канваса с альфа-каналом
#define OUTER_AREA_SIZE                (16)                       // Размер одной стороны внешней области вокруг рабочего пространства
//+------------------------------------------------------------------+
//| Перечисления                                                     |
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| Список типов объектов библиотеки                                 |
//+------------------------------------------------------------------+
enum ENUM_OBJECT_DE_TYPE
  {
//--- Графика
   OBJECT_DE_TYPE_GBASE =  COLLECTION_ID_LIST_END+1,              // Тип объекта "Базовый объект всех графических объектов библиотеки"
   OBJECT_DE_TYPE_GELEMENT,                                       // Тип объекта "Графический элемент"
   OBJECT_DE_TYPE_GFORM,                                          // Тип объекта "Форма"
   OBJECT_DE_TYPE_GSHADOW,                                        // Тип объекта "Тень"
//--- Анимация
   OBJECT_DE_TYPE_GFRAME,                                         // Тип объекта "Один кадр анимации"
   OBJECT_DE_TYPE_GFRAME_TEXT,                                    // Тип объекта "Один кадр текстовой анимации"
   OBJECT_DE_TYPE_GFRAME_QUAD,                                    // Тип объекта "Один кадр прямоугольной анимации"
   OBJECT_DE_TYPE_GFRAME_GEOMETRY,                                // Тип объекта "Один кадр геометрической анимации"
   OBJECT_DE_TYPE_GANIMATIONS,                                    // Тип объекта "Анимации"
//--- Управление графическими объектами
   OBJECT_DE_TYPE_GELEMENT_CONTROL,                               // Тип объекта "Управление графическими элементами"
//--- Стандартные графические объекты
   OBJECT_DE_TYPE_GSTD_VLINE              =  OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_VLINE,             // Тип объекта "Вертикальная линия
   OBJECT_DE_TYPE_GSTD_HLINE              =  OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_HLINE,             // Тип объекта "Горизонтальная линия
   OBJECT_DE_TYPE_GSTD_TREND              =  OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_TREND,             // Тип объекта "Трендовая линия
   OBJECT_DE_TYPE_GSTD_TRENDBYANGLE       =  OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_TRENDBYANGLE,      // Тип объекта "Трендовая линия по углу
   OBJECT_DE_TYPE_GSTD_CYCLES             =  OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_CYCLES,            // Тип объекта "Циклические линии
   OBJECT_DE_TYPE_GSTD_ARROWED_LINE       =  OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_ARROWED_LINE,      // Тип объекта "Линия со стрелкой"
   OBJECT_DE_TYPE_GSTD_CHANNEL            =  OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_CHANNEL,           // Тип объекта "Равноудаленный канал
   OBJECT_DE_TYPE_GSTD_STDDEVCHANNEL      =  OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_STDDEVCHANNEL,     // Тип объекта "Канал стандартного отклонения
   OBJECT_DE_TYPE_GSTD_REGRESSION         =  OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_REGRESSION,        // Тип объекта "Канал на линейной регрессии
   OBJECT_DE_TYPE_GSTD_PITCHFORK          =  OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_PITCHFORK,         // Тип объекта "Вилы Эндрюса
   OBJECT_DE_TYPE_GSTD_GANNLINE           =  OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_GANNLINE,          // Тип объекта "Линия Ганна
   OBJECT_DE_TYPE_GSTD_GANNFAN            =  OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_GANNFAN,           // Тип объекта "Веер Ганна
   OBJECT_DE_TYPE_GSTD_GANNGRID           =  OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_GANNGRID,          // Тип объекта "Сетка Ганна
   OBJECT_DE_TYPE_GSTD_FIBO               =  OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_FIBO,              // Тип объекта "Уровни Фибоначчи
   OBJECT_DE_TYPE_GSTD_FIBOTIMES          =  OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_FIBOTIMES,         // Тип объекта "Временные зоны Фибоначчи
   OBJECT_DE_TYPE_GSTD_FIBOFAN            =  OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_FIBOFAN,           // Тип объекта "Веер Фибоначчи
   OBJECT_DE_TYPE_GSTD_FIBOARC            =  OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_FIBOARC,           // Тип объекта "Дуги Фибоначчи
   OBJECT_DE_TYPE_GSTD_FIBOCHANNEL        =  OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_FIBOCHANNEL,       // Тип объекта "Канал Фибоначчи
   OBJECT_DE_TYPE_GSTD_EXPANSION          =  OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_EXPANSION,         // Тип объекта "Расширение Фибоначчи
   OBJECT_DE_TYPE_GSTD_ELLIOTWAVE5        =  OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_ELLIOTWAVE5,       // Тип объекта "5-волновка Эллиота
   OBJECT_DE_TYPE_GSTD_ELLIOTWAVE3        =  OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_ELLIOTWAVE3,       // Тип объекта "3-волновка Эллиота
   OBJECT_DE_TYPE_GSTD_RECTANGLE          =  OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_RECTANGLE,         // Тип объекта "Прямоугольник
   OBJECT_DE_TYPE_GSTD_TRIANGLE           =  OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_TRIANGLE,          // Тип объекта "Треугольник
   OBJECT_DE_TYPE_GSTD_ELLIPSE            =  OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_ELLIPSE,           // Тип объекта "Эллипс
   OBJECT_DE_TYPE_GSTD_ARROW_THUMB_UP     =  OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_ARROW_THUMB_UP,    // Тип объекта "Знак "Хорошо" (большой палец вверх)
   OBJECT_DE_TYPE_GSTD_ARROW_THUMB_DOWN   =  OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_ARROW_THUMB_DOWN,  // Тип объекта "Знак "Плохо" (большой палец вниз)
   OBJECT_DE_TYPE_GSTD_ARROW_UP           =  OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_ARROW_UP,          // Тип объекта "Знак "Стрелка вверх"
   OBJECT_DE_TYPE_GSTD_ARROW_DOWN         =  OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_ARROW_DOWN,        // Тип объекта "Знак "Стрелка вниз"
   OBJECT_DE_TYPE_GSTD_ARROW_STOP         =  OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_ARROW_STOP,        // Тип объекта "Знак "Стоп"
   OBJECT_DE_TYPE_GSTD_ARROW_CHECK        =  OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_ARROW_CHECK,       // Тип объекта "Знак "Птичка" (галка)
   OBJECT_DE_TYPE_GSTD_ARROW_LEFT_PRICE   =  OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_ARROW_LEFT_PRICE,  // Тип объекта "Левая ценовая метка
   OBJECT_DE_TYPE_GSTD_ARROW_RIGHT_PRICE  =  OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_ARROW_RIGHT_PRICE, // Тип объекта "Правая ценовая метка
   OBJECT_DE_TYPE_GSTD_ARROW_BUY          =  OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_ARROW_BUY,         // Тип объекта "Знак "Buy"
   OBJECT_DE_TYPE_GSTD_ARROW_SELL         =  OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_ARROW_SELL,        // Тип объекта "Знак "Sell"
   OBJECT_DE_TYPE_GSTD_ARROW              =  OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_ARROW,             // Тип объекта "Стрелка"
   OBJECT_DE_TYPE_GSTD_TEXT               =  OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_TEXT,              // Тип объекта "Текст"
   OBJECT_DE_TYPE_GSTD_LABEL              =  OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_LABEL,             // Тип объекта "Текстовая метка"
   OBJECT_DE_TYPE_GSTD_BUTTON             =  OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_BUTTON,            // Тип объекта "Кнопка"
   OBJECT_DE_TYPE_GSTD_CHART              =  OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_CHART,             // Тип объекта "График"
   OBJECT_DE_TYPE_GSTD_BITMAP             =  OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_BITMAP,            // Тип объекта "Рисунок"
   OBJECT_DE_TYPE_GSTD_BITMAP_LABEL       =  OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_BITMAP_LABEL,      // Тип объекта "Графическая метка"
   OBJECT_DE_TYPE_GSTD_EDIT               =  OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_EDIT,              // Тип объекта "Поле ввода"
   OBJECT_DE_TYPE_GSTD_EVENT              =  OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_EVENT,             // Тип объекта "Событие, соответствующий событию в экономическом календаре"
   OBJECT_DE_TYPE_GSTD_RECTANGLE_LABEL    =  OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_RECTANGLE_LABEL,   // Тип объекта "Прямоугольная метка для создания и оформления пользовательского графического интерфейса"
   
//--- Объекты
   OBJECT_DE_TYPE_BASE  =  OBJECT_DE_TYPE_GSTD_RECTANGLE_LABEL+1, // Базовый объект всех объектов библиотеки
   OBJECT_DE_TYPE_BASE_EXT,                                       // Расширенный базовый объект всех объектов библиотеки
   
   OBJECT_DE_TYPE_ACCOUNT,                                        // Тип объекта "Аккаунт"
   OBJECT_DE_TYPE_BOOK_ORDER,                                     // Тип объекта "Заявка стакана цен"
   OBJECT_DE_TYPE_BOOK_BUY,                                       // Тип объекта "Заявка на покупку в стакане"
   OBJECT_DE_TYPE_BOOK_BUY_MARKET,                                // Тип объекта "Заявка на покупку по рыночной цене в стакане"
   OBJECT_DE_TYPE_BOOK_SELL,                                      // Тип объекта "Заявка на продажу в стакане"
   OBJECT_DE_TYPE_BOOK_SELL_MARKET,                               // Тип объекта "Заявка на продажу по рыночной цене в стакане"
   OBJECT_DE_TYPE_BOOK_SNAPSHOT,                                  // Тип объекта "Снимок стакана цен"
   OBJECT_DE_TYPE_BOOK_SERIES,                                    // Тип объекта "Серия снимков стакана цен"
   
   OBJECT_DE_TYPE_CHART,                                          // Тип объекта "Чарт"
   OBJECT_DE_TYPE_CHART_WND,                                      // Тип объекта "Окно графика"
   OBJECT_DE_TYPE_CHART_WND_IND,                                  // Тип объекта "Индикатор окна графика"
   
   OBJECT_DE_TYPE_EVENT,                                          // Тип объекта "Событие"
   OBJECT_DE_TYPE_EVENT_BALANCE,                                  // Тип объекта "Событие балансовой операции"
   OBJECT_DE_TYPE_EVENT_MODIFY,                                   // Тип объекта "Событие модификации отложенного ордера или позиции"
   OBJECT_DE_TYPE_EVENT_ORDER_PLASED,                             // Тип объекта "Событие установки отложенного ордера"
   OBJECT_DE_TYPE_EVENT_ORDER_REMOVED,                            // Тип объекта "Событие удаления отложенного ордера"
   OBJECT_DE_TYPE_EVENT_POSITION_CLOSE,                           // Тип объекта "Событие закрытия позиции"
   OBJECT_DE_TYPE_EVENT_POSITION_OPEN,                            // Тип объекта "Событие открытия позиции"
   
   OBJECT_DE_TYPE_IND_BUFFER,                                     // Тип объекта "Индикаторный буфер"
   OBJECT_DE_TYPE_IND_BUFFER_ARROW,                               // Тип объекта "Буфер отрисовки стрелками"
   OBJECT_DE_TYPE_IND_BUFFER_BAR,                                 // Тип объекта "<Буфер баров"
   OBJECT_DE_TYPE_IND_BUFFER_CALCULATE,                           // Тип объекта "Расчётный буфер"
   OBJECT_DE_TYPE_IND_BUFFER_CANDLE,                              // Тип объекта "Буфер свечей"
   OBJECT_DE_TYPE_IND_BUFFER_FILLING,                             // Тип объекта "Буфер заливки"
   OBJECT_DE_TYPE_IND_BUFFER_HISTOGRAMM,                          // Тип объекта "Буфер гистограммы"
   OBJECT_DE_TYPE_IND_BUFFER_HISTOGRAMM2,                         // Тип объекта "Буфер гистограммы2"
   OBJECT_DE_TYPE_IND_BUFFER_LINE,                                // Тип объекта "Буфер линии"
   OBJECT_DE_TYPE_IND_BUFFER_SECTION,                             // Тип объекта "Буфер секции"
   OBJECT_DE_TYPE_IND_BUFFER_ZIGZAG,                              // Тип объекта "Буфер зигзага"
   OBJECT_DE_TYPE_INDICATOR,                                      // Тип объекта "Индикатор"
   OBJECT_DE_TYPE_IND_DATA,                                       // Тип объекта "Данные индикатора"
   OBJECT_DE_TYPE_IND_DATA_LIST,                                  // Тип объекта "Список данных индикатора"
   
   OBJECT_DE_TYPE_IND_AC,                                         // Тип объекта "Индикатор Accelerator Oscillator"
   OBJECT_DE_TYPE_IND_AD,                                         // Тип объекта "Индикатор Accumulation/Distribution"
   OBJECT_DE_TYPE_IND_ADX,                                        // Тип объекта "Индикатор Average Directional Index"
   OBJECT_DE_TYPE_IND_ADXW,                                       // Тип объекта "Индикатор ADX by Welles Wilder"
   OBJECT_DE_TYPE_IND_ALLIGATOR,                                  // Тип объекта "Индикатор Alligator"
   OBJECT_DE_TYPE_IND_AMA,                                        // Тип объекта "Индикатор Adaptive Moving Average"
   OBJECT_DE_TYPE_IND_AO,                                         // Тип объекта "Индикатор Awesome Oscillator"
   OBJECT_DE_TYPE_IND_ATR,                                        // Тип объекта "Индикатор Average True Range"
   OBJECT_DE_TYPE_IND_BANDS,                                      // Тип объекта "Индикатор Bollinger Bands®"
   OBJECT_DE_TYPE_IND_BEARS,                                      // Тип объекта "Индикатор Bears Power"
   OBJECT_DE_TYPE_IND_BULLS,                                      // Тип объекта "Индикатор Bulls Power"
   OBJECT_DE_TYPE_IND_BWMFI,                                      // Тип объекта "Индикатор Market Facilitation Index"
   OBJECT_DE_TYPE_IND_CCI,                                        // Тип объекта "Индикатор Commodity Channel Index"
   OBJECT_DE_TYPE_IND_CHAIKIN,                                    // Тип объекта "Индикатор Chaikin Oscillator"
   OBJECT_DE_TYPE_IND_CUSTOM,                                     // Тип объекта "Пользовательский индикатор"
   OBJECT_DE_TYPE_IND_DEMA,                                       // Тип объекта "Индикатор Double Exponential Moving Average"
   OBJECT_DE_TYPE_IND_DEMARKER,                                   // Тип объекта "Индикатор DeMarker"
   OBJECT_DE_TYPE_IND_ENVELOPES,                                  // Тип объекта "Индикатор Envelopes"
   OBJECT_DE_TYPE_IND_FORCE,                                      // Тип объекта "Индикатор Force Index"
   OBJECT_DE_TYPE_IND_FRACTALS,                                   // Тип объекта "Индикатор Fractals"
   OBJECT_DE_TYPE_IND_FRAMA,                                      // Тип объекта "Индикатор Fractal Adaptive Moving Average"
   OBJECT_DE_TYPE_IND_GATOR,                                      // Тип объекта "Индикатор Gator Oscillator"
   OBJECT_DE_TYPE_IND_ICHIMOKU,                                   // Тип объекта "Индикатор Ichimoku Kinko Hyo"
   OBJECT_DE_TYPE_IND_MA,                                         // Тип объекта "Индикатор Moving Average"
   OBJECT_DE_TYPE_IND_MACD,                                       // Тип объекта "Индикатор Moving Average Convergence/Divergence"
   OBJECT_DE_TYPE_IND_MFI,                                        // Тип объекта "Индикатор Money Flow Index"
   OBJECT_DE_TYPE_IND_MOMENTUM,                                   // Тип объекта "Индикатор Momentum"
   OBJECT_DE_TYPE_IND_OBV,                                        // Тип объекта "Индикатор On Balance Volume"
   OBJECT_DE_TYPE_IND_OSMA,                                       // Тип объекта "Индикатор Moving Average of Oscillator"
   OBJECT_DE_TYPE_IND_RSI,                                        // Тип объекта "Индикатор Relative Strength Index"
   OBJECT_DE_TYPE_IND_RVI,                                        // Тип объекта "Индикатор Relative Vigor Index"
   OBJECT_DE_TYPE_IND_SAR,                                        // Тип объекта "Индикатор Parabolic SAR"
   OBJECT_DE_TYPE_IND_STDEV,                                      // Тип объекта "Индикатор Standard Deviation"
   OBJECT_DE_TYPE_IND_STOCH,                                      // Тип объекта "Индикатор Stochastic Oscillator"
   OBJECT_DE_TYPE_IND_TEMA,                                       // Тип объекта "Индикатор Triple Exponential Moving Average"
   OBJECT_DE_TYPE_IND_TRIX,                                       // Тип объекта "Индикатор Triple Exponential Moving Averages Oscillator"
   OBJECT_DE_TYPE_IND_VIDYA,                                      // Тип объекта "Индикатор Variable Index Dynamic Average"
   OBJECT_DE_TYPE_IND_VOLUMES,                                    // Тип объекта "Индикатор Volumes"
   OBJECT_DE_TYPE_IND_WPR,                                        // Тип объекта "Индикатор Williams' Percent Range"
   
   OBJECT_DE_TYPE_MQL5_SIGNAL,                                    // Тип объекта "mql5-сигнал"
   
   OBJECT_DE_TYPE_ORDER_DEAL_POSITION,                            // Тип объекта "Ордер/Сделка/Позиция"
   OBJECT_DE_TYPE_HISTORY_BALANCE,                                // Тип объекта "Историческая балансовая операция"
   OBJECT_DE_TYPE_HISTORY_DEAL,                                   // Тип объекта "Историческая сделка"
   OBJECT_DE_TYPE_HISTORY_ORDER_MARKET,                           // Тип объекта "Исторический маркет-ордер"
   OBJECT_DE_TYPE_HISTORY_ORDER_PENDING,                          // Тип объекта "Исторический удалённый отложенный ордер"
   OBJECT_DE_TYPE_MARKET_ORDER,                                   // Тип объекта "Рыночный маркет-ордер"
   OBJECT_DE_TYPE_MARKET_PENDING,                                 // Тип объекта "Рыночный отложенный ордер"
   OBJECT_DE_TYPE_MARKET_POSITION,                                // Тип объекта "Рыночная позиция"
   
   OBJECT_DE_TYPE_PENDING_REQUEST,                                // Тип объекта "Отложенный торговый запрос"
   OBJECT_DE_TYPE_PENDING_REQUEST_POSITION_OPEN,                  // Тип объекта "Отложенный запрос на открытие позиции"
   OBJECT_DE_TYPE_PENDING_REQUEST_POSITION_CLOSE,                 // Тип объекта "Отложенный запрос на закрытие позиции"
   OBJECT_DE_TYPE_PENDING_REQUEST_POSITION_SLTP,                  // Тип объекта "Отложенный запрос на модификацию стоп-приказов позиции"
   OBJECT_DE_TYPE_PENDING_REQUEST_ORDER_PLACE,                    // Тип объекта "Отложенный запрос на установку отложенного ордера"
   OBJECT_DE_TYPE_PENDING_REQUEST_ORDER_REMOVE,                   // Тип объекта "Отложенный запрос на удаление отложенного ордера"
   OBJECT_DE_TYPE_PENDING_REQUEST_ORDER_MODIFY,                   // Тип объекта "Отложенный запрос на мидификацию параметров отложенного ордера"
   
   OBJECT_DE_TYPE_SERIES_BAR,                                     // Тип объекта "Бар"
   OBJECT_DE_TYPE_SERIES_PERIOD,                                  // Тип объекта "Таймсерия периода"
   OBJECT_DE_TYPE_SERIES_SYMBOL,                                  // Тип объекта "Таймсерии символа"
   
   OBJECT_DE_TYPE_SYMBOL,                                         // Тип объекта "Символ"
   OBJECT_DE_TYPE_SYMBOL_BONDS,                                   // Тип объекта "Символ облигация"
   OBJECT_DE_TYPE_SYMBOL_CFD,                                     // Тип объекта "CFD символ (Контракт на разницу цен)"
   OBJECT_DE_TYPE_SYMBOL_COLLATERAL,                              // Тип объекта "Символ неторгуемый актив"
   OBJECT_DE_TYPE_SYMBOL_COMMODITY,                               // Тип объекта "Товарный символ"
   OBJECT_DE_TYPE_SYMBOL_COMMON,                                  // Тип объекта "Символ общей группы"
   OBJECT_DE_TYPE_SYMBOL_CRYPTO,                                  // Тип объекта "Криптовалютный символ"
   OBJECT_DE_TYPE_SYMBOL_CUSTOM,                                  // Тип объекта "Пользовательский символ"
   OBJECT_DE_TYPE_SYMBOL_EXCHANGE,                                // Тип объекта "Биржевой символ"
   OBJECT_DE_TYPE_SYMBOL_FUTURES,                                 // Тип объекта "Символ фьючерс"
   OBJECT_DE_TYPE_SYMBOL_FX,                                      // Тип объекта "Форекс символ"
   OBJECT_DE_TYPE_SYMBOL_FX_EXOTIC,                               // Тип объекта "Форекс символ экзотик"
   OBJECT_DE_TYPE_SYMBOL_FX_MAJOR,                                // Тип объекта "Форекс символ мажор"
   OBJECT_DE_TYPE_SYMBOL_FX_MINOR,                                // Тип объекта "Форекс символ минор"
   OBJECT_DE_TYPE_SYMBOL_FX_RUB,                                  // Тип объекта "Форекс символ рубль"
   OBJECT_DE_TYPE_SYMBOL_INDEX,                                   // Тип объекта "Символ индекс"
   OBJECT_DE_TYPE_SYMBOL_INDICATIVE,                              // Тип объекта "Символ индикатив"
   OBJECT_DE_TYPE_SYMBOL_METALL,                                  // Тип объекта "Символ металл"
   OBJECT_DE_TYPE_SYMBOL_OPTION,                                  // Тип объекта "Символ опцион"
   OBJECT_DE_TYPE_SYMBOL_STOCKS,                                  // Тип объекта "Символ ценная бумага"
   
   OBJECT_DE_TYPE_TICK,                                           // Тип объекта "Тик"
   OBJECT_DE_TYPE_NEW_TICK,                                       // Тип объекта "Новый тик"
   OBJECT_DE_TYPE_TICKSERIES,                                     // Тип объекта "Серия тиковых данных"
   
   OBJECT_DE_TYPE_TRADE,                                          // Тип объекта "Торговый объект"
  };

//+------------------------------------------------------------------+
//| Данные для поиска и сортировки                                   |
//+------------------------------------------------------------------+

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

//--- Управление графическими объектами
   OBJECT_DE_TYPE_GELEMENT_CONTROL,                               // Тип объекта "Управление графическими элементами"
//--- Стандартные графические объекты
   OBJECT_DE_TYPE_GSTD_VLINE              =  OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_VLINE,             // Тип объекта "Вертикальная линия
   OBJECT_DE_TYPE_GSTD_HLINE              =  OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_HLINE,             // Тип объекта "Горизонтальная линия
   OBJECT_DE_TYPE_GSTD_TREND              =  OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_TREND,             // Тип объекта "Трендовая линия
   OBJECT_DE_TYPE_GSTD_TRENDBYANGLE       =  OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_TRENDBYANGLE,      // Тип объекта "Трендовая линия по углу
   OBJECT_DE_TYPE_GSTD_CYCLES             =  OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_CYCLES,            // Тип объекта "Циклические линии
   OBJECT_DE_TYPE_GSTD_ARROWED_LINE       =  OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_ARROWED_LINE,      // Тип объекта "Линия со стрелкой"
   OBJECT_DE_TYPE_GSTD_CHANNEL            =  OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_CHANNEL,           // Тип объекта "Равноудаленный канал
   OBJECT_DE_TYPE_GSTD_STDDEVCHANNEL      =  OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_STDDEVCHANNEL,     // Тип объекта "Канал стандартного отклонения
   OBJECT_DE_TYPE_GSTD_REGRESSION         =  OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_REGRESSION,        // Тип объекта "Канал на линейной регрессии
   OBJECT_DE_TYPE_GSTD_PITCHFORK          =  OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_PITCHFORK,         // Тип объекта "Вилы Эндрюса
   OBJECT_DE_TYPE_GSTD_GANNLINE           =  OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_GANNLINE,          // Тип объекта "Линия Ганна
   OBJECT_DE_TYPE_GSTD_GANNFAN            =  OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_GANNFAN,           // Тип объекта "Веер Ганна
   OBJECT_DE_TYPE_GSTD_GANNGRID           =  OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_GANNGRID,          // Тип объекта "Сетка Ганна
   OBJECT_DE_TYPE_GSTD_FIBO               =  OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_FIBO,              // Тип объекта "Уровни Фибоначчи
   OBJECT_DE_TYPE_GSTD_FIBOTIMES          =  OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_FIBOTIMES,         // Тип объекта "Временные зоны Фибоначчи
   OBJECT_DE_TYPE_GSTD_FIBOFAN            =  OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_FIBOFAN,           // Тип объекта "Веер Фибоначчи
   OBJECT_DE_TYPE_GSTD_FIBOARC            =  OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_FIBOARC,           // Тип объекта "Дуги Фибоначчи
   OBJECT_DE_TYPE_GSTD_FIBOCHANNEL        =  OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_FIBOCHANNEL,       // Тип объекта "Канал Фибоначчи
   OBJECT_DE_TYPE_GSTD_EXPANSION          =  OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_EXPANSION,         // Тип объекта "Расширение Фибоначчи
   OBJECT_DE_TYPE_GSTD_ELLIOTWAVE5        =  OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_ELLIOTWAVE5,       // Тип объекта "5-волновка Эллиота
   OBJECT_DE_TYPE_GSTD_ELLIOTWAVE3        =  OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_ELLIOTWAVE3,       // Тип объекта "3-волновка Эллиота
   OBJECT_DE_TYPE_GSTD_RECTANGLE          =  OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_RECTANGLE,         // Тип объекта "Прямоугольник
   OBJECT_DE_TYPE_GSTD_TRIANGLE           =  OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_TRIANGLE,          // Тип объекта "Треугольник
   OBJECT_DE_TYPE_GSTD_ELLIPSE            =  OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_ELLIPSE,           // Тип объекта "Эллипс
   OBJECT_DE_TYPE_GSTD_ARROW_THUMB_UP     =  OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_ARROW_THUMB_UP,    // Тип объекта "Знак "Хорошо" (большой палец вверх)
   OBJECT_DE_TYPE_GSTD_ARROW_THUMB_DOWN   =  OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_ARROW_THUMB_DOWN,  // Тип объекта "Знак "Плохо" (большой палец вниз)
   OBJECT_DE_TYPE_GSTD_ARROW_UP           =  OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_ARROW_UP,          // Тип объекта "Знак "Стрелка вверх"
   OBJECT_DE_TYPE_GSTD_ARROW_DOWN         =  OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_ARROW_DOWN,        // Тип объекта "Знак "Стрелка вниз"
   OBJECT_DE_TYPE_GSTD_ARROW_STOP         =  OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_ARROW_STOP,        // Тип объекта "Знак "Стоп"
   OBJECT_DE_TYPE_GSTD_ARROW_CHECK        =  OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_ARROW_CHECK,       // Тип объекта "Знак "Птичка" (галка)
   OBJECT_DE_TYPE_GSTD_ARROW_LEFT_PRICE   =  OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_ARROW_LEFT_PRICE,  // Тип объекта "Левая ценовая метка
   OBJECT_DE_TYPE_GSTD_ARROW_RIGHT_PRICE  =  OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_ARROW_RIGHT_PRICE, // Тип объекта "Правая ценовая метка
   OBJECT_DE_TYPE_GSTD_ARROW_BUY          =  OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_ARROW_BUY,         // Тип объекта "Знак "Buy"
   OBJECT_DE_TYPE_GSTD_ARROW_SELL         =  OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_ARROW_SELL,        // Тип объекта "Знак "Sell"
   OBJECT_DE_TYPE_GSTD_ARROW              =  OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_ARROW,             // Тип объекта "Стрелка"
   OBJECT_DE_TYPE_GSTD_TEXT               =  OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_TEXT,              // Тип объекта "Текст"
   OBJECT_DE_TYPE_GSTD_LABEL              =  OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_LABEL,             // Тип объекта "Текстовая метка"
   OBJECT_DE_TYPE_GSTD_BUTTON             =  OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_BUTTON,            // Тип объекта "Кнопка"
   OBJECT_DE_TYPE_GSTD_CHART              =  OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_CHART,             // Тип объекта "График"
   OBJECT_DE_TYPE_GSTD_BITMAP             =  OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_BITMAP,            // Тип объекта "Рисунок"
   OBJECT_DE_TYPE_GSTD_BITMAP_LABEL       =  OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_BITMAP_LABEL,      // Тип объекта "Графическая метка"
   OBJECT_DE_TYPE_GSTD_EDIT               =  OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_EDIT,              // Тип объекта "Поле ввода"
   OBJECT_DE_TYPE_GSTD_EVENT              =  OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_EVENT,             // Тип объекта "Событие, соответствующий событию в экономическом календаре"
   OBJECT_DE_TYPE_GSTD_RECTANGLE_LABEL    =  OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_RECTANGLE_LABEL,   // Тип объекта "Прямоугольная метка для создания и оформления пользовательского графического интерфейса"
   
//--- Объекты
   OBJECT_DE_TYPE_BASE  =  OBJECT_DE_TYPE_GSTD_RECTANGLE_LABEL+1, // Базовый объект всех объектов библиотеки

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

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

//+------------------------------------------------------------------+
//| Список принадлежностей графических объектов                      |
//+------------------------------------------------------------------+
enum ENUM_GRAPH_OBJ_BELONG
  {
   GRAPH_OBJ_BELONG_PROGRAM,                          // Графический объект принадлежит программе
   GRAPH_OBJ_BELONG_TERMINAL,                         // Графический объект не принадлежит программе
  };
//+------------------------------------------------------------------+
//| Список типов графических элементов                               |
//+------------------------------------------------------------------+
enum ENUM_GRAPH_ELEMENT_TYPE
  {
   GRAPH_ELEMENT_TYPE_ELEMENT,                        // Элемент
   GRAPH_ELEMENT_TYPE_SHADOW_OBJ,                     // Объект тени
   GRAPH_ELEMENT_TYPE_FORM,                           // Форма
   GRAPH_ELEMENT_TYPE_WINDOW,                         // Окно
  };
//+------------------------------------------------------------------+
//| Целочисленные свойства графического элемента на канвасе          |
//+------------------------------------------------------------------+
enum ENUM_CANV_ELEMENT_PROP_INTEGER
  {
   CANV_ELEMENT_PROP_ID = 0,                          // Идентификатор элемента
   CANV_ELEMENT_PROP_TYPE,                            // Тип графического элемента
   CANV_ELEMENT_PROP_BELONG,                          // Принадлежность графического элемента
   CANV_ELEMENT_PROP_NUM,                             // Номер элемента в списке
   CANV_ELEMENT_PROP_CHART_ID,                        // Идентификатор графика
   CANV_ELEMENT_PROP_WND_NUM,                         // Номер подокна графика
   CANV_ELEMENT_PROP_COORD_X,                         // X-координата формы на графике
   CANV_ELEMENT_PROP_COORD_Y,                         // Y-координата формы на графике
   CANV_ELEMENT_PROP_WIDTH,                           // Ширина элемента
   CANV_ELEMENT_PROP_HEIGHT,                          // Высота элемента
   CANV_ELEMENT_PROP_RIGHT,                           // Правая граница элемента
   CANV_ELEMENT_PROP_BOTTOM,                          // Нижняя граница элемента
   CANV_ELEMENT_PROP_ACT_SHIFT_LEFT,                  // Отступ активной зоны от левого края элемента
   CANV_ELEMENT_PROP_ACT_SHIFT_TOP,                   // Отступ активной зоны от верхнего края элемента
   CANV_ELEMENT_PROP_ACT_SHIFT_RIGHT,                 // Отступ активной зоны от правого края элемента
   CANV_ELEMENT_PROP_ACT_SHIFT_BOTTOM,                // Отступ активной зоны от нижнего края элемента
   CANV_ELEMENT_PROP_MOVABLE,                         // Флаг перемещаемости элемента
   CANV_ELEMENT_PROP_ACTIVE,                          // Флаг активности элемента
   CANV_ELEMENT_PROP_COORD_ACT_X,                     // X-координата активной зоны элемента
   CANV_ELEMENT_PROP_COORD_ACT_Y,                     // Y-координата активной зоны элемента
   CANV_ELEMENT_PROP_ACT_RIGHT,                       // Правая граница активной зоны элемента
   CANV_ELEMENT_PROP_ACT_BOTTOM,                      // Нижняя граница активной зоны элемента
  };
#define CANV_ELEMENT_PROP_INTEGER_TOTAL (22)          // Общее количество целочисленных свойств
#define CANV_ELEMENT_PROP_INTEGER_SKIP  (0)           // Количество неиспользуемых в сортировке целочисленных свойств
//+------------------------------------------------------------------+

Соответственно, раз мы вписали новое свойство, то должны и увеличить общее количество этих свойств (с 21 до 22), а также нам нужно добавить сортировку по этому свойству в перечисление возможных критериев сортировки графических объектов:

//+------------------------------------------------------------------+
//| Возможные критерии сортировки графических элементов на канвасе   |
//+------------------------------------------------------------------+
#define FIRST_CANV_ELEMENT_DBL_PROP  (CANV_ELEMENT_PROP_INTEGER_TOTAL-CANV_ELEMENT_PROP_INTEGER_SKIP)
#define FIRST_CANV_ELEMENT_STR_PROP  (CANV_ELEMENT_PROP_INTEGER_TOTAL-CANV_ELEMENT_PROP_INTEGER_SKIP+CANV_ELEMENT_PROP_DOUBLE_TOTAL-CANV_ELEMENT_PROP_DOUBLE_SKIP)
enum ENUM_SORT_CANV_ELEMENT_MODE
  {
//--- Сортировка по целочисленным свойствам
   SORT_BY_CANV_ELEMENT_ID = 0,                       // Сортировать по идентификатору элемента
   SORT_BY_CANV_ELEMENT_TYPE,                         // Сортировать по типу графического элемента
   SORT_BY_CANV_ELEMENT_BELONG,                       // Сортировать по принадлежности графического элемента
   SORT_BY_CANV_ELEMENT_NUM,                          // Сортировать по номеру формы в списке
   SORT_BY_CANV_ELEMENT_CHART_ID,                     // Сортировать по идентификатору графика
   SORT_BY_CANV_ELEMENT_WND_NUM,                      // Сортировать по номеру окна графика
   SORT_BY_CANV_ELEMENT_COORD_X,                      // Сортировать по X-координате элемента на графике
   SORT_BY_CANV_ELEMENT_COORD_Y,                      // Сортировать по Y-координате элемента на графике
   SORT_BY_CANV_ELEMENT_WIDTH,                        // Сортировать по ширине элемента
   SORT_BY_CANV_ELEMENT_HEIGHT,                       // Сортировать по высоте элемента
   SORT_BY_CANV_ELEMENT_RIGHT,                        // Сортировать по правой границе элемента
   SORT_BY_CANV_ELEMENT_BOTTOM,                       // Сортировать по нижней границе элемента
   SORT_BY_CANV_ELEMENT_ACT_SHIFT_LEFT,               // Сортировать по отступу активной зоны от левого края элемента
   SORT_BY_CANV_ELEMENT_ACT_SHIFT_TOP,                // Сортировать по отступу активной зоны от верхнего края элемента
   SORT_BY_CANV_ELEMENT_ACT_SHIFT_RIGHT,              // Сортировать по отступу активной зоны от правого края элемента
   SORT_BY_CANV_ELEMENT_ACT_SHIFT_BOTTOM,             // Сортировать по отступу активной зоны от нижнего края элемента
   SORT_BY_CANV_ELEMENT_MOVABLE,                      // Сортировать по флагу перемещаемости элемента
   SORT_BY_CANV_ELEMENT_ACTIVE,                       // Сортировать по флагу активности элемента
   SORT_BY_CANV_ELEMENT_COORD_ACT_X,                  // Сортировать по X-координате активной зоны элемента
   SORT_BY_CANV_ELEMENT_COORD_ACT_Y,                  // Сортировать по Y-координате активной зоны элемента
   SORT_BY_CANV_ELEMENT_ACT_RIGHT,                    // Сортировать по правой границе активной зоны элемента
   SORT_BY_CANV_ELEMENT_ACT_BOTTOM,                   // Сортировать по нижней границе активной зоны элемента
//--- Сортировка по вещественным свойствам

//--- Сортировка по строковым свойствам
   SORT_BY_CANV_ELEMENT_NAME_OBJ = FIRST_CANV_ELEMENT_STR_PROP,// Сортировать по имени объекта-элемента
   SORT_BY_CANV_ELEMENT_NAME_RES,                     // Сортировать по имени графического ресурса
  };
//+------------------------------------------------------------------+


В файле \MQL5\Include\DoEasy\Data.mqh добавим индексы новых сообщений библиотеки для коллекции графических объектов:

//--- CShadowObj
   MSG_SHADOW_OBJ_IMG_SMALL_BLUR_LARGE,               // Ошибка! Размер изображения очень маленький или очень большое размытие

//--- CGraphElementsCollection
   MSG_CHART_OBJ_COLLECTION_ERR_OBJ_ALREADY_EXISTS,   // Ошибка. Уже существует объект управления чартами с идентификатором чарта 
   MSG_CHART_OBJ_COLLECTION_ERR_FAILED_CREATE_CTRL_OBJ,// Не удалось создать объект управления чартами с идентификатором чарта 
   
  };
//+------------------------------------------------------------------+

и тексты сообщений, соответствующие вновь добавленным индексам:

//--- CShadowObj
   {"Ошибка! Размер изображения очень маленький или очень большое размытие","Error! Image size is very small or very large blur"},
      
//--- CGraphElementsCollection
   {"Ошибка. Уже существует объект управления чартами с идентификатором чарта ","Error. A chart control object already exists with chart id "},
   {"Не удалось создать объект управления чартами с идентификатором чарта ","Failed to create chart control object with chart id "},

  };
//+---------------------------------------------------------------------+


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

Многие объекты библиотеки являются наследниками базового объекта всех объектов библиотеки, и в нём уже есть переменная m_type, которая хранит значение типа объекта, и есть виртуальный метод Type(), возвращающий установленный в переменную тип объекта. Соответственно, нам достаточно будет в конструкторах его наследников указать значение переменной m_type, соответствующее типу объекта.

Так как мы ввели понятие принадлежности объекта, то определять эту принадлежность мы будем по наличию в имени графического объекта наименования программы. Для этого в базовом объекте всех объектов библиотеки в файле \MQL5\Include\DoEasy\Objects\BaseObj.mqh создадим в защищённой секции класса новую переменную для хранения имени программы:

//+------------------------------------------------------------------+
//| Класс базового объекта для всех объектов библиотеки              |
//+------------------------------------------------------------------+
class CBaseObj : public CObject
  {
protected:
   CGraphElmControl  m_graph_elm;                              // Экземпляр класса управления графическими элементами
   ENUM_LOG_LEVEL    m_log_level;                              // Уровень логирования
   ENUM_PROGRAM_TYPE m_program;                                // Тип программы
   bool              m_first_start;                            // Флаг первого запуска
   bool              m_use_sound;                              // Флаг проигрывания установленного объекту звука
   bool              m_available;                              // Флаг использования объекта-наследника в программе
   int               m_global_error;                           // Код глобальной ошибки
   long              m_chart_id_main;                          // Идентификатор графика управляющей программы
   long              m_chart_id;                               // Идентификатор графика
   string            m_name_program;                           // Имя программы
   string            m_name;                                   // Наименование объекта
   string            m_folder_name;                            // Имя папки хранения объектов-наследников CBaseObj
   string            m_sound_name;                             // Имя звукового файла объекта
   int               m_type;                                   // Тип объекта (соответствует типу объекта из перечисления ENUM_OBJECT_DE_TYPE)

public:

А в конструкторе класса укажем имя программы и тип объекта как базовый объект:

//--- Конструктор
                     CBaseObj() : m_program((ENUM_PROGRAM_TYPE)::MQLInfoInteger(MQL_PROGRAM_TYPE)),
                                  m_name_program(::MQLInfoString(MQL_PROGRAM_NAME)),
                                  m_global_error(ERR_SUCCESS),
                                  m_log_level(LOG_LEVEL_ERROR_MSG),
                                  m_chart_id_main(::ChartID()),
                                  m_chart_id(::ChartID()),
                                  m_folder_name(DIRECTORY),
                                  m_sound_name(""),
                                  m_name(__FUNCTION__),
                                  m_type(OBJECT_DE_TYPE_BASE),
                                  m_use_sound(false),
                                  m_available(true),
                                  m_first_start(true) {}
  };
//+------------------------------------------------------------------+

В этом же файле, вслед за классом базового объекта у нас написан и класс расширенного базового объекта для всех объектов библиотеки. Укажем в его конструкторе тип этого объекта как базовый-расширенный:

//+------------------------------------------------------------------+
//| Конструктор                                                      |
//+------------------------------------------------------------------+
CBaseObjExt::CBaseObjExt() : m_hash_sum(0),m_hash_sum_prev(0),
                             m_is_event(false),m_event_code(WRONG_VALUE),
                             m_long_prop_total(0),
                             m_double_prop_total(0)
  {
   this.m_type=OBJECT_DE_TYPE_BASE_EXT;
   ::ArrayResize(this.m_long_prop_event,0,100);
   ::ArrayResize(this.m_double_prop_event,0,100);
   ::ArrayResize(this.m_long_prop_event_prev,0,100);
   ::ArrayResize(this.m_double_prop_event_prev,0,100);
   ::ZeroMemory(this.m_tick);
   this.m_digits_currency=(#ifdef __MQL5__ (int)::AccountInfoInteger(ACCOUNT_CURRENCY_DIGITS) #else 2 #endif);
   this.m_list_events.Clear();
   this.m_list_events.Sort();
   this.m_list_events_base.Clear();
   this.m_list_events_base.Sort();
  }
//+------------------------------------------------------------------+

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

Для объекта-аккаунта в файле \MQL5\Include\DoEasy\Objects\Accounts\Account.mqh это будет выглядеть так (целиком конструктор):

//+------------------------------------------------------------------+
//| Конструктор                                                      |
//+------------------------------------------------------------------+
CAccount::CAccount(void)
  {
   this.m_type=OBJECT_DE_TYPE_ACCOUNT;
//--- Инициализация контролируемых данных
   this.SetControlDataArraySizeLong(ACCOUNT_PROP_INTEGER_TOTAL);
   this.SetControlDataArraySizeDouble(ACCOUNT_PROP_DOUBLE_TOTAL);
   this.ResetChangesParams();
   this.ResetControlsParams();
  
//--- Сохранение целочисленных свойств
   this.m_long_prop[ACCOUNT_PROP_LOGIN]                              = ::AccountInfoInteger(ACCOUNT_LOGIN);
   this.m_long_prop[ACCOUNT_PROP_TRADE_MODE]                         = ::AccountInfoInteger(ACCOUNT_TRADE_MODE);
   this.m_long_prop[ACCOUNT_PROP_LEVERAGE]                           = ::AccountInfoInteger(ACCOUNT_LEVERAGE);
   this.m_long_prop[ACCOUNT_PROP_LIMIT_ORDERS]                       = ::AccountInfoInteger(ACCOUNT_LIMIT_ORDERS);
   this.m_long_prop[ACCOUNT_PROP_MARGIN_SO_MODE]                     = ::AccountInfoInteger(ACCOUNT_MARGIN_SO_MODE);
   this.m_long_prop[ACCOUNT_PROP_TRADE_ALLOWED]                      = ::AccountInfoInteger(ACCOUNT_TRADE_ALLOWED);
   this.m_long_prop[ACCOUNT_PROP_TRADE_EXPERT]                       = ::AccountInfoInteger(ACCOUNT_TRADE_EXPERT);
   this.m_long_prop[ACCOUNT_PROP_MARGIN_MODE]                        = #ifdef __MQL5__::AccountInfoInteger(ACCOUNT_MARGIN_MODE) #else ACCOUNT_MARGIN_MODE_RETAIL_HEDGING #endif ;
   this.m_long_prop[ACCOUNT_PROP_CURRENCY_DIGITS]                    = #ifdef __MQL5__::AccountInfoInteger(ACCOUNT_CURRENCY_DIGITS) #else 2 #endif ;
   this.m_long_prop[ACCOUNT_PROP_SERVER_TYPE]                        = (::TerminalInfoString(TERMINAL_NAME)=="MetaTrader 5" ? 5 : 4);
   this.m_long_prop[ACCOUNT_PROP_FIFO_CLOSE]                         = (#ifdef __MQL5__::TerminalInfoInteger(TERMINAL_BUILD)<2155 ? false : ::AccountInfoInteger(ACCOUNT_FIFO_CLOSE) #else false #endif );
   
//--- Сохранение вещественных свойств
   this.m_double_prop[this.IndexProp(ACCOUNT_PROP_BALANCE)]          = ::AccountInfoDouble(ACCOUNT_BALANCE);
   this.m_double_prop[this.IndexProp(ACCOUNT_PROP_CREDIT)]           = ::AccountInfoDouble(ACCOUNT_CREDIT);
   this.m_double_prop[this.IndexProp(ACCOUNT_PROP_PROFIT)]           = ::AccountInfoDouble(ACCOUNT_PROFIT);
   this.m_double_prop[this.IndexProp(ACCOUNT_PROP_EQUITY)]           = ::AccountInfoDouble(ACCOUNT_EQUITY);
   this.m_double_prop[this.IndexProp(ACCOUNT_PROP_MARGIN)]           = ::AccountInfoDouble(ACCOUNT_MARGIN);
   this.m_double_prop[this.IndexProp(ACCOUNT_PROP_MARGIN_FREE)]      = ::AccountInfoDouble(ACCOUNT_MARGIN_FREE);
   this.m_double_prop[this.IndexProp(ACCOUNT_PROP_MARGIN_LEVEL)]     = ::AccountInfoDouble(ACCOUNT_MARGIN_LEVEL);
   this.m_double_prop[this.IndexProp(ACCOUNT_PROP_MARGIN_SO_CALL)]   = ::AccountInfoDouble(ACCOUNT_MARGIN_SO_CALL);
   this.m_double_prop[this.IndexProp(ACCOUNT_PROP_MARGIN_SO_SO)]     = ::AccountInfoDouble(ACCOUNT_MARGIN_SO_SO);
   this.m_double_prop[this.IndexProp(ACCOUNT_PROP_MARGIN_INITIAL)]   = ::AccountInfoDouble(ACCOUNT_MARGIN_INITIAL);
   this.m_double_prop[this.IndexProp(ACCOUNT_PROP_MARGIN_MAINTENANCE)]=::AccountInfoDouble(ACCOUNT_MARGIN_MAINTENANCE);
   this.m_double_prop[this.IndexProp(ACCOUNT_PROP_ASSETS)]           = ::AccountInfoDouble(ACCOUNT_ASSETS);
   this.m_double_prop[this.IndexProp(ACCOUNT_PROP_LIABILITIES)]      = ::AccountInfoDouble(ACCOUNT_LIABILITIES);
   this.m_double_prop[this.IndexProp(ACCOUNT_PROP_COMMISSION_BLOCKED)]=::AccountInfoDouble(ACCOUNT_COMMISSION_BLOCKED);
   
//--- Сохранение строковых свойств
   this.m_string_prop[this.IndexProp(ACCOUNT_PROP_NAME)]             = ::AccountInfoString(ACCOUNT_NAME);
   this.m_string_prop[this.IndexProp(ACCOUNT_PROP_SERVER)]           = ::AccountInfoString(ACCOUNT_SERVER);
   this.m_string_prop[this.IndexProp(ACCOUNT_PROP_CURRENCY)]         = ::AccountInfoString(ACCOUNT_CURRENCY);
   this.m_string_prop[this.IndexProp(ACCOUNT_PROP_COMPANY)]          = ::AccountInfoString(ACCOUNT_COMPANY);

//--- Имя объекта-аккаунта, тип объекта и тип аккаунта (MetaTrader 5 или 4)
   this.m_name=CMessage::Text(MSG_LIB_PROP_ACCOUNT)+" "+(string)this.Login()+": "+this.Name()+" ("+this.Company()+")";
   this.m_type=COLLECTION_ACCOUNT_ID;
   this.m_type_server=(::TerminalInfoString(TERMINAL_NAME)=="MetaTrader 5" ? 5 : 4);

//--- Заполнение текущих данных аккаунта
   for(int i=0;i<ACCOUNT_PROP_INTEGER_TOTAL;i++)
      this.m_long_prop_event[i][3]=this.m_long_prop[i];
   for(int i=0;i<ACCOUNT_PROP_DOUBLE_TOTAL;i++)
      this.m_double_prop_event[i][3]=this.m_double_prop[i];

//--- Обновление данных в базовом объекте и поиск изменений
   CBaseObjExt::Refresh();
  }
//+-------------------------------------------------------------------+

Как видим всё, что нам нужно — это задать защищённой переменной m_type нужный тип объекта. В данном случае — это тип "аккаунт". Запись в эту переменную нового значение типа объекта, которая объявлена в классе базового объекта (и в нём же ей присвоен тип "базовый объект"), переопределит тип объекта с "базового" на "аккаунт". И теперь виртуальный метод Type(), возвращающий значение переменной m_type и так же реализованный в базовом объекте, будет возвращать значение переменной, переопределённое в конструкторе класса объекта-аккаунта.

Класс абстрактной заявки стакана цен в файле \MQL5\Include\DoEasy\Objects\Book\MarketBookOrd.mqh имеет два конструктора — по умолчанию и параметрический. В обоих конструкторах пропишем тип объекта.

В конструкторе по умолчанию:

//--- Сравнивает объекты CMarketBookOrd между собой по всем свойствам (для поиска равных объектов-запросов)
   bool              IsEqual(CMarketBookOrd* compared_req) const;
   
//--- Конструктор по умолчанию
                     CMarketBookOrd(){ this.m_type=OBJECT_DE_TYPE_BOOK_ORDER; }
protected:
//--- Защищённый параметрический конструктор
                     CMarketBookOrd(const ENUM_MBOOK_ORD_STATUS status,const MqlBookInfo &book_info,const string symbol);
                     
public:
//+------------------------------------------------------------------+ 
//|Методы упрощённого доступа к свойствам объекта-запроса стакана цен|
//+------------------------------------------------------------------+

и в параметрическом:

//+------------------------------------------------------------------+
//| Защищённый параметрический конструктор                           |
//+------------------------------------------------------------------+
CMarketBookOrd::CMarketBookOrd(const ENUM_MBOOK_ORD_STATUS status,const MqlBookInfo &book_info,const string symbol)
  {
   this.m_type=OBJECT_DE_TYPE_BOOK_ORDER;
//--- Сохраняем Digits символа
   this.m_digits=(int)::SymbolInfoInteger(symbol,SYMBOL_DIGITS);
//--- Сохраняем целочисленные свойства объекта
   this.SetProperty(MBOOK_ORD_PROP_STATUS,status);
   this.SetProperty(MBOOK_ORD_PROP_TYPE,book_info.type);
   this.SetProperty(MBOOK_ORD_PROP_VOLUME,book_info.volume);
//--- Сохраняем вещественные свойства объекта
   this.SetProperty(MBOOK_ORD_PROP_PRICE,book_info.price);
   this.SetProperty(MBOOK_ORD_PROP_VOLUME_REAL,book_info.volume_real);
//--- Сохраняем дополнительные свойства объекта
   this.SetProperty(MBOOK_ORD_PROP_SYMBOL,(symbol==NULL || symbol=="" ? ::Symbol() : symbol));
//--- Время заявки отсутствует в параметрах, и оно учитывается в классе снимка стакана цен. Обнуляем время
   this.SetProperty(MBOOK_ORD_PROP_TIME_MSC,0);
  }
//+------------------------------------------------------------------+

В наследниках класса абстрактной заявки стакана цен пропишем соответствующие типы объектов.

Заявка на покупку в стакане в файле \MQL5\Include\DoEasy\Objects\Book\MarketBookBuy.mqh:

//+------------------------------------------------------------------+
//| Заявка на покупку в стакане                                      |
//+------------------------------------------------------------------+
class CMarketBookBuy : public CMarketBookOrd
  {
private:

public:
   //--- Конструктор
                     CMarketBookBuy(const string symbol,const MqlBookInfo &book_info) :
                        CMarketBookOrd(MBOOK_ORD_STATUS_BUY,book_info,symbol) { this.m_type=OBJECT_DE_TYPE_BOOK_BUY; }
   //--- Поддерживаемые свойства ордера (1) вещественные, (2) целочисленные
   virtual bool      SupportProperty(ENUM_MBOOK_ORD_PROP_DOUBLE property);
   virtual bool      SupportProperty(ENUM_MBOOK_ORD_PROP_INTEGER property);
//--- Возвращает краткое наименование объекта
   virtual string    Header(const bool symbol=false);
//--- Возвращает описание типа (ENUM_BOOK_TYPE) заявки
   virtual string    TypeDescription(void);
  };
//+------------------------------------------------------------------+

Заявка на покупку по рыночной цене в стакане в файле \MQL5\Include\DoEasy\Objects\Book\MarketBookBuyMarket.mqh:

//+------------------------------------------------------------------+
//| Заявка на покупку по рыночной цене в стакане                     |
//+------------------------------------------------------------------+
class CMarketBookBuyMarket : public CMarketBookOrd
  {
private:

public:
   //--- Конструктор
                     CMarketBookBuyMarket(const string symbol,const MqlBookInfo &book_info) :
                        CMarketBookOrd(MBOOK_ORD_STATUS_BUY,book_info,symbol) { this.m_type=OBJECT_DE_TYPE_BOOK_BUY_MARKET; }
   //--- Поддерживаемые свойства ордера (1) вещественные, (2) целочисленные
   virtual bool      SupportProperty(ENUM_MBOOK_ORD_PROP_DOUBLE property);
   virtual bool      SupportProperty(ENUM_MBOOK_ORD_PROP_INTEGER property);
//--- Возвращает краткое наименование объекта
   virtual string    Header(const bool symbol=false);
//--- Возвращает описание типа (ENUM_BOOK_TYPE) заявки
   virtual string    TypeDescription(void);
  };
//+------------------------------------------------------------------+

Заявка на продажу в стакане в файле \MQL5\Include\DoEasy\Objects\Book\MarketBookSell.mqh:

//+------------------------------------------------------------------+
//| Заявка на продажу в стакане                                      |
//+------------------------------------------------------------------+
class CMarketBookSell : public CMarketBookOrd
  {
private:

public:
   //--- Конструктор
                     CMarketBookSell(const string symbol,const MqlBookInfo &book_info) :
                        CMarketBookOrd(MBOOK_ORD_STATUS_SELL,book_info,symbol) { this.m_type=OBJECT_DE_TYPE_BOOK_SELL; }
   //--- Поддерживаемые свойства ордера (1) вещественные, (2) целочисленные
   virtual bool      SupportProperty(ENUM_MBOOK_ORD_PROP_DOUBLE property);
   virtual bool      SupportProperty(ENUM_MBOOK_ORD_PROP_INTEGER property);
//--- Возвращает краткое наименование объекта
   virtual string    Header(const bool symbol=false);
//--- Возвращает описание типа (ENUM_BOOK_TYPE) заявки
   virtual string    TypeDescription(void);
  };
//+------------------------------------------------------------------+

Заявка на продажу по рыночной цене в стакане в файле \MQL5\Include\DoEasy\Objects\Book\MarketBookSellMarket.mqh:

//+------------------------------------------------------------------+
//| Заявка на продажу по рыночной цене в стакане                     |
//+------------------------------------------------------------------+
class CMarketBookSellMarket : public CMarketBookOrd
  {
private:

public:
   //--- Конструктор
                     CMarketBookSellMarket(const string symbol,const MqlBookInfo &book_info) :
                        CMarketBookOrd(MBOOK_ORD_STATUS_SELL,book_info,symbol) { this.m_type=OBJECT_DE_TYPE_BOOK_SELL_MARKET; }
   //--- Поддерживаемые свойства ордера (1) вещественные, (2) целочисленные
   virtual bool      SupportProperty(ENUM_MBOOK_ORD_PROP_DOUBLE property);
   virtual bool      SupportProperty(ENUM_MBOOK_ORD_PROP_INTEGER property);
//--- Возвращает краткое наименование объекта
   virtual string    Header(const bool symbol=false);
//--- Возвращает описание типа (ENUM_BOOK_TYPE) заявки
   virtual string    TypeDescription(void);
  };
//+------------------------------------------------------------------+

Класс "Снимок стакана цен" в файле \MQL5\Include\DoEasy\Objects\Book\MarketBookSnapshot.mqh имеет два конструктора — по умолчанию и параметрический. Соответственно, здесь нам нужно в обоих конструкторах указать тип объекта.

В конструкторе по умолчанию:

//--- Возвращает наименование снимка стакана цен
   string            Header(void);
//--- Выводит в журнал (1) описание, (2) краткое описание снимка стакана цен
   virtual void      Print(const bool full_prop=false,const bool dash=false);
   virtual void      PrintShort(const bool dash=false,const bool symbol=false);

//--- Конструкторы
                     CMBookSnapshot(){ this.m_type=OBJECT_DE_TYPE_BOOK_SNAPSHOT; }
                     CMBookSnapshot(const string symbol,const long time,MqlBookInfo &book_array[]);
//+------------------------------------------------------------------+ 
//|Методы упрощённого доступа к свойствам объекта-снимка стакана цен |
//+------------------------------------------------------------------+

и в параметрическом:

//+------------------------------------------------------------------+
//| Параметрический конструктор                                      |
//+------------------------------------------------------------------+
CMBookSnapshot::CMBookSnapshot(const string symbol,const long time,MqlBookInfo &book_array[]) : m_time(time)
  {
   this.m_type=OBJECT_DE_TYPE_BOOK_SNAPSHOT;
   //--- Устанавливаем символ
   this.SetSymbol(symbol);
   //--- Очищаем список
   this.m_list.Clear();
   //--- В цикле по массиву структур
   int total=::ArraySize(book_array);
   this.m_volume_buy=this.m_volume_sell=0;
   this.m_volume_buy_real=this.m_volume_sell_real=0;
   for(int i=0;i<total;i++)
     {
      //--- Создаём объекты-заявки текущего снимка стакана цен в зависимости от типа заявки
      CMarketBookOrd *mbook_ord=NULL;
      switch(book_array[i].type)
        {
         case BOOK_TYPE_BUY         : mbook_ord=new CMarketBookBuy(this.m_symbol,book_array[i]);         break;
         case BOOK_TYPE_SELL        : mbook_ord=new CMarketBookSell(this.m_symbol,book_array[i]);        break;
         case BOOK_TYPE_BUY_MARKET  : mbook_ord=new CMarketBookBuyMarket(this.m_symbol,book_array[i]);   break;
         case BOOK_TYPE_SELL_MARKET : mbook_ord=new CMarketBookSellMarket(this.m_symbol,book_array[i]);  break;
         default: break;
        }
      if(mbook_ord==NULL)
         continue;
      //--- Устанавливаем заявке время снимка стакана цен
      mbook_ord.SetTime(this.m_time);

      //--- Ставим списку флаг сортированного списка (по значению цены) и добавляем в него текущий объект-заявку
      //--- Если не удалось добавить объект в список заявок стакана цен - удаляем объект-заявку
      this.m_list.Sort(SORT_BY_MBOOK_ORD_PRICE);
      if(!this.m_list.InsertSort(mbook_ord))
         delete mbook_ord;
      //--- Если объект-заявка успешно добавлен в список заявок снимка стакана - дополняем общие объёмы снимка
      else
        {
         switch(mbook_ord.TypeOrd())
           {
            case BOOK_TYPE_BUY         : 
              this.m_volume_buy+=mbook_ord.Volume(); 
              this.m_volume_buy_real+=mbook_ord.VolumeReal();
              break;
            case BOOK_TYPE_SELL        : 
              this.m_volume_sell+=mbook_ord.Volume(); 
              this.m_volume_sell_real+=mbook_ord.VolumeReal();
              break;
            case BOOK_TYPE_BUY_MARKET  : 
              this.m_volume_buy+=mbook_ord.Volume(); 
              this.m_volume_buy_real+=mbook_ord.VolumeReal();
              break;
            case BOOK_TYPE_SELL_MARKET : 
              this.m_volume_buy+=mbook_ord.Volume(); 
              this.m_volume_buy_real+=mbook_ord.VolumeReal();
              break;
            default: break;
           }
        }
     }
  }
//+------------------------------------------------------------------+

Класс "Серия снимков стакана цен" также имеет два конструктора, и в обоих пропишем тип объекта.

По умолчанию:

//--- Выводит в журнал (1) описание, (2) краткое описание серии снимков стакана цен
   virtual void      Print(const bool full_prop=false,const bool dash=false);
   virtual void      PrintShort(const bool dash=false,const bool symbol=false);

//--- Конструкторы
                     CMBookSeries(){ this.m_type=OBJECT_DE_TYPE_BOOK_SERIES; }
                     CMBookSeries(const string symbol,const uint required=0);

//+------------------------------------------------------------------+ 
//| Методы работы с объектами и доступа к их свойствам               |
//+------------------------------------------------------------------+

и в параметрическом:

//+------------------------------------------------------------------+
//| Параметрический конструктор                                      |
//+------------------------------------------------------------------+
CMBookSeries::CMBookSeries(const string symbol,const uint required=0) : m_symbol(symbol)
  {
   this.m_type=OBJECT_DE_TYPE_BOOK_SERIES;
   this.m_list.Clear();
   this.m_list.Sort(SORT_BY_MBOOK_ORD_TIME_MSC);
   this.SetRequiredUsedDays(required);
  }
//+------------------------------------------------------------------+


Класс абстрактного события в файле \MQL5\Include\DoEasy\Objects\Events\Event.mqh не является наследником ни базового объекта, ни расширенного базового объекта всех объектов библиотеки. Соответственно, в нём нет переменной m_type, нет виртуального метода Type(), возвращающего значение этой переменной (такой метод есть в базовом объекте CObject, от которого унаследован данный класс, но он возвращает 0 и должен быть переопределён в наследниках). Значит — нам необходимо добавить переменную и метод и прописать в конструкторах класса нужный тип для созданной переменной:

//+------------------------------------------------------------------+
//| Класс абстрактного события                                       |
//+------------------------------------------------------------------+
class CEvent : public CObject
  {
private:
   int               m_event_code;                                   // Код события
//--- Возвращает индекс массива, по которому фактически расположено (1) double-свойство и (2) string-свойство события
   int               IndexProp(ENUM_EVENT_PROP_DOUBLE property)const { return(int)property-EVENT_PROP_INTEGER_TOTAL;                         }
   int               IndexProp(ENUM_EVENT_PROP_STRING property)const { return(int)property-EVENT_PROP_INTEGER_TOTAL-EVENT_PROP_DOUBLE_TOTAL; }
protected:
   ENUM_TRADE_EVENT  m_trade_event;                                  // Торговое событие
   bool              m_is_hedge;                                     // Флаг хедж-счёта
   long              m_chart_id_main;                                // Идентификатор графика управляющей программы
   int               m_type;                                         // Тип объекта
   int               m_digits;                                       // Digits() символа
   int               m_digits_acc;                                   // Количество знаков после запятой для валюты счета
   long              m_long_prop[EVENT_PROP_INTEGER_TOTAL];          // Целочисленные свойства события
   double            m_double_prop[EVENT_PROP_DOUBLE_TOTAL];         // Вещественные свойства события
   string            m_string_prop[EVENT_PROP_STRING_TOTAL];         // Строковые свойства события
//--- возвращает факт наличия флага в торговом событии
   bool              IsPresentEventFlag(const int event_code)  const { return (this.m_event_code & event_code)==event_code;                  }
//--- Возвращает (1) заданный магический номер, идентификатор (2) первой группы, (3) второй группы, (4) отложенного запроса из значения магика
   ushort            GetMagicID(void)                          const { return ushort(this.Magic() & 0xFFFF);                                 }
   uchar             GetGroupID1(void)                         const { return uchar(this.Magic()>>16) & 0x0F;                                }
   uchar             GetGroupID2(void)                         const { return uchar((this.Magic()>>16) & 0xF0)>>4;                           }
   uchar             GetPendReqID(void)                        const { return uchar(this.Magic()>>24) & 0xFF;                                }
//--- Защищённый параметрический конструктор
                     CEvent(const ENUM_EVENT_STATUS event_status,const int event_code,const ulong ticket);
public:
//--- Конструктор по умолчанию
                     CEvent(void){ this.m_type=OBJECT_DE_TYPE_EVENT; }
 
//--- Устанавливает (1) целочисленное, (2) вещественное и (3) строковое свойство события
   void              SetProperty(ENUM_EVENT_PROP_INTEGER property,long value) { this.m_long_prop[property]=value;                            }
   void              SetProperty(ENUM_EVENT_PROP_DOUBLE property,double value){ this.m_double_prop[this.IndexProp(property)]=value;          }
   void              SetProperty(ENUM_EVENT_PROP_STRING property,string value){ this.m_string_prop[this.IndexProp(property)]=value;          }
//--- Возвращает из массива свойств (1) целочисленное, (2) вещественное и (3) строковое свойство события
   long              GetProperty(ENUM_EVENT_PROP_INTEGER property)      const { return this.m_long_prop[property];                           }
   double            GetProperty(ENUM_EVENT_PROP_DOUBLE property)       const { return this.m_double_prop[this.IndexProp(property)];         }
   string            GetProperty(ENUM_EVENT_PROP_STRING property)       const { return this.m_string_prop[this.IndexProp(property)];         }

//--- Возвращает флаг поддержания событием данного свойства
   virtual bool      SupportProperty(ENUM_EVENT_PROP_INTEGER property)        { return true;       }
   virtual bool      SupportProperty(ENUM_EVENT_PROP_DOUBLE property)         { return true;       }
   virtual bool      SupportProperty(ENUM_EVENT_PROP_STRING property)         { return true;       }
//--- Возвращает тип объекта
   virtual int       Type(void)                                         const { return this.m_type;}

//--- Расшифровывает код события и устанавливает торговое событие, (2) возвращает торговое событие
   void              SetTypeEvent(void);
   ENUM_TRADE_EVENT  TradeEvent(void)                                   const { return this.m_trade_event;                                   }
//--- Отправляет событие на график (реализация в потомках класса)
   virtual void      SendEvent(void) {;}

//--- Сравнивает объекты CEvent между собой по заданному свойству (для сортировки списков по указанному свойству объекта-события)
   virtual int       Compare(const CObject *node,const int mode=0) const;
//--- Сравнивает объекты CEvent между собой по всем свойствам (для поиска равных объектов-событий)
   bool              IsEqual(CEvent* compared_event);
//+------------------------------------------------------------------+
//| Методы упрощённого доступа к свойствам объекта-события           |
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| Конструктор                                                      |
//+------------------------------------------------------------------+
CEvent::CEvent(const ENUM_EVENT_STATUS event_status,const int event_code,const ulong ticket) : m_event_code(event_code),m_digits(0)
  {
   this.m_type=OBJECT_DE_TYPE_EVENT;
   this.m_long_prop[EVENT_PROP_STATUS_EVENT]       =  event_status;
   this.m_long_prop[EVENT_PROP_TICKET_ORDER_EVENT] =  (long)ticket;
   this.m_is_hedge=#ifdef __MQL4__ true #else bool(::AccountInfoInteger(ACCOUNT_MARGIN_MODE)==ACCOUNT_MARGIN_MODE_RETAIL_HEDGING) #endif;
   this.m_digits_acc=#ifdef __MQL4__ 2 #else (int)::AccountInfoInteger(ACCOUNT_CURRENCY_DIGITS) #endif;
   this.m_chart_id_main=::ChartID();
  }
//+------------------------------------------------------------------+

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

Класс события балансовой операции в файле \MQL5\Include\DoEasy\Objects\Events\EventBalanceOperation.mqh:

//+------------------------------------------------------------------+
//| Событие балансовой операции                                      |
//+------------------------------------------------------------------+
class CEventBalanceOperation : public CEvent
  {
public:
//--- Конструктор
                     CEventBalanceOperation(const int event_code,const ulong ticket=0) : CEvent(EVENT_STATUS_BALANCE,event_code,ticket)
                       { this.m_type=OBJECT_DE_TYPE_EVENT_BALANCE; }
//--- Поддерживаемые свойства ордера (1) вещественные, (2) целочисленные
   virtual bool      SupportProperty(ENUM_EVENT_PROP_INTEGER property);
   virtual bool      SupportProperty(ENUM_EVENT_PROP_DOUBLE property);
   virtual bool      SupportProperty(ENUM_EVENT_PROP_STRING property);
//--- (1) Выводит в журнал краткое сообщение о событии, (2) Отправляет событие на график
   virtual void      PrintShort(void);
   virtual void      SendEvent(void);
  };
//+------------------------------------------------------------------+

Класс события модификации отложенного ордера или позиции в файле \MQL5\Include\DoEasy\Objects\Events\EventModify.mqh:

//+------------------------------------------------------------------+
//| Событие модификации отложенного ордера или позиции               |
//+------------------------------------------------------------------+
class CEventModify : public CEvent
  {
private:
   double            m_price;                               // Цена, отправляемая в событие
//--- Создаёт и возвращает краткое сообщение события
   string            EventsMessage(void);
public:
//--- Конструктор
                     CEventModify(const int event_code,const ulong ticket=0) : CEvent(EVENT_STATUS_MODIFY,event_code,ticket),m_price(0)
                       { this.m_type=OBJECT_DE_TYPE_EVENT_MODIFY; }
//--- Поддерживаемые свойства ордера (1) вещественные, (2) целочисленные
   virtual bool      SupportProperty(ENUM_EVENT_PROP_INTEGER property);
   virtual bool      SupportProperty(ENUM_EVENT_PROP_DOUBLE property);
//--- (1) Выводит в журнал краткое сообщение о событии, (2) Отправляет событие на график
   virtual void      PrintShort(void);
   virtual void      SendEvent(void);
  };
//+------------------------------------------------------------------+

Класс события установки отложенного ордера в файле \MQL5\Include\DoEasy\Objects\Events\EventOrderPlaced.mqh:

//+------------------------------------------------------------------+
//| Событие установки отложенного ордера                             |
//+------------------------------------------------------------------+
class CEventOrderPlased : public CEvent
  {
public:
//--- Конструктор
                     CEventOrderPlased(const int event_code,const ulong ticket=0) : CEvent(EVENT_STATUS_MARKET_PENDING,event_code,ticket)
                       { this.m_type=OBJECT_DE_TYPE_EVENT_ORDER_PLASED; }
//--- Поддерживаемые свойства ордера (1) вещественные, (2) целочисленные
   virtual bool      SupportProperty(ENUM_EVENT_PROP_INTEGER property);
   virtual bool      SupportProperty(ENUM_EVENT_PROP_DOUBLE property);
//--- (1) Выводит в журнал краткое сообщение о событии, (2) Отправляет событие на график
   virtual void      PrintShort(void);
   virtual void      SendEvent(void);
  };
//+------------------------------------------------------------------+

Класс события удаления отложенного ордера в файле \MQL5\Include\DoEasy\Objects\Events\EventOrderRemoved.mqh:

//+------------------------------------------------------------------+
//| Событие удаления отложенного ордера                              |
//+------------------------------------------------------------------+
class CEventOrderRemoved : public CEvent
  {
public:
//--- Конструктор
                     CEventOrderRemoved(const int event_code,const ulong ticket=0) : CEvent(EVENT_STATUS_HISTORY_PENDING,event_code,ticket)
                       { this.m_type=OBJECT_DE_TYPE_EVENT_ORDER_REMOVED; }
//--- Поддерживаемые свойства ордера (1) вещественные, (2) целочисленные
   virtual bool      SupportProperty(ENUM_EVENT_PROP_INTEGER property);
   virtual bool      SupportProperty(ENUM_EVENT_PROP_DOUBLE property);
//--- (1) Выводит в журнал краткое сообщение о событии, (2) Отправляет событие на график
   virtual void      PrintShort(void);
   virtual void      SendEvent(void);
  };
//+------------------------------------------------------------------+

Класс события закрытия позиции в файле \MQL5\Include\DoEasy\Objects\Events\EventPositionClose.mqh:

//+------------------------------------------------------------------+
//| Событие закрытия позиции                                         |
//+------------------------------------------------------------------+
class CEventPositionClose : public CEvent
  {
private:
//--- Создаёт и возвращает краткое сообщение события
   string            EventsMessage(void);  
public:
//--- Конструктор
                     CEventPositionClose(const int event_code,const ulong ticket=0) : CEvent(EVENT_STATUS_HISTORY_POSITION,event_code,ticket)
                       { this.m_type=OBJECT_DE_TYPE_EVENT_POSITION_CLOSE; }
//--- Поддерживаемые свойства ордера (1) вещественные, (2) целочисленные
   virtual bool      SupportProperty(ENUM_EVENT_PROP_INTEGER property);
   virtual bool      SupportProperty(ENUM_EVENT_PROP_DOUBLE property);
//--- (1) Выводит в журнал краткое сообщение о событии, (2) Отправляет событие на график
   virtual void      PrintShort(void);
   virtual void      SendEvent(void);
  };
//+------------------------------------------------------------------+

Класс события открытия позиции в файле \MQL5\Include\DoEasy\Objects\Events\EventPositionOpen.mqh:

//+------------------------------------------------------------------+
//| Событие открытия позиции                                         |
//+------------------------------------------------------------------+
class CEventPositionOpen : public CEvent
  {
private:
//--- Создаёт и возвращает краткое сообщение события
   string            EventsMessage(void);  
public:
//--- Конструктор
                     CEventPositionOpen(const int event_code,const ulong ticket=0) : CEvent(EVENT_STATUS_MARKET_POSITION,event_code,ticket)
                       { this.m_type=OBJECT_DE_TYPE_EVENT_POSITION_OPEN; }
//--- Поддерживаемые свойства ордера (1) вещественные, (2) целочисленные
   virtual bool      SupportProperty(ENUM_EVENT_PROP_INTEGER property);
   virtual bool      SupportProperty(ENUM_EVENT_PROP_DOUBLE property);
//--- (1) Выводит в журнал краткое сообщение о событии, (2) Отправляет событие на график
   virtual void      PrintShort(void);
   virtual void      SendEvent(void);
  };
//+------------------------------------------------------------------+


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

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

Вот перечень доработанных классов в каталоге библиотеки \MQL5\Include\DoEasy\Objects:

Все эти файлы можно посмотреть и изучить внесённые в них изменения в прикреплённых к статье файлах.

Мы упустили из перечня изменённых файлов один класс — это класс базового объекта всех графических объектов библиотеки в файле \MQL5\Include\DoEasy\Objects\Graph\GBaseObj.mqh. В него, помимо указания типа объекта, было добавлено свойство "Принадлежность программе/терминалу" — это нам необходимо для понимания, какой графический объект был создан программой, работающей под управлением библиотеки, а какой добавлен на график вручную в терминале:

//+------------------------------------------------------------------+
//|                                                     GBaseObj.mqh |
//|                                  Copyright 2021, MetaQuotes Ltd. |
//|                             https://mql5.com/ru/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2021, MetaQuotes Ltd."
#property link      "https://mql5.com/ru/users/artmedia70"
#property version   "1.00"
#property strict    // Нужно для mql4
//+------------------------------------------------------------------+
//| Включаемые файлы                                                 |
//+------------------------------------------------------------------+
#include "..\..\Services\DELib.mqh"
#include <Graphics\Graphic.mqh>
//+------------------------------------------------------------------+
//| Класс базового объекта графических объектов библиотеки           |
//+------------------------------------------------------------------+
class CGBaseObj : public CObject
  {
private:
   
protected:
   string            m_name_prefix;                      // Префикс имени объекта
   string            m_name;                             // Имя объекта
   long              m_chart_id;                         // Идентификатор графика
   int               m_subwindow;                        // Номер подокна
   int               m_shift_y;                          // Смещение координаты Y для подокна
   int               m_type;                             // Тип объекта
   bool              m_visible;                          // Видимость объекта
   ENUM_GRAPH_OBJ_BELONG m_belong;                       // Принадлежность программе/терминалу
   
//--- Создаёт (1) структуру объекта, (2) объект из структуры
   virtual bool      ObjectToStruct(void)                      { return true; }
   virtual void      StructToObject(void){;}

public:
//--- Возврат значений переменных класса
   string            Name(void)                          const { return this.m_name;      }
   long              ChartID(void)                       const { return this.m_chart_id;  }
   int               SubWindow(void)                     const { return this.m_subwindow; }
   ENUM_GRAPH_OBJ_BELONG Belong(void)                    const { return this.m_belong;    }
   
//--- (1) Устанавливает, (2) возвращает видимость объекта
   void              SetVisible(const bool flag)   
                       { 
                        long value=(flag ? OBJ_ALL_PERIODS : 0);
                        if(::ObjectSetInteger(this.m_chart_id,this.m_name,OBJPROP_TIMEFRAMES,value))
                           this.m_visible=flag;
                       }
   bool              IsVisible(void)                     const { return this.m_visible;   }

//--- Виртуальный метод, возвращающий тип объекта
   virtual int       Type(void)                          const { return this.m_type;      }
//--- Устанавливает принадлежность
   void              SetBelong(const ENUM_GRAPH_OBJ_BELONG belong){ this.m_belong=belong; }

//--- Конструктор/деструктор
                     CGBaseObj();
                    ~CGBaseObj();
  };
//+------------------------------------------------------------------+
//| Конструктор                                                      |
//+------------------------------------------------------------------+
CGBaseObj::CGBaseObj() : m_shift_y(0),m_visible(false), m_name_prefix(::MQLInfoString(MQL_PROGRAM_NAME)+"_"),m_belong(GRAPH_OBJ_BELONG_PROGRAM)
  {
   this.m_type=OBJECT_DE_TYPE_GBASE;
  }
//+------------------------------------------------------------------+
//| Деструктор                                                       |
//+------------------------------------------------------------------+
CGBaseObj::~CGBaseObj()
  {
  }
//+------------------------------------------------------------------+


Доработки, аналогичные внесённым в файлы классов объектов библиотеки, были внесены и в файлы классов коллекций объектов библиотеки в каталоге \MQL5\Include\DoEasy\Collections\.

Вот перечень файлов доработанных классов коллекций объектов:

AccountsCollection.mqh, BookSeriesCollection.mqh, BuffersCollection.mqh, ChartObjCollection.mqh, EventsCollection.mqh, HistoryCollection.mqh, IndicatorsCollection.mqh, MarketCollection.mqh, MQLSignalsCollection.mqh, ResourceCollection.mqh, SymbolsCollection.mqh, TickSeriesCollection.mqh, TimeSeriesCollection.mqh.

Эти файлы можно так же посмотреть и изучить внесённые в них изменения в прикреплённых к статье файлах.

Доработка классов библиотеки завершена.


Класс-коллекция графических объектов

В прошлой статье мы сделали заготовку класса-коллекции графических объектов библиотеки (файл \MQL5\Include\DoEasy\Collections\GraphElementsCollection.mqh). Сегодня продолжим её развитие.

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

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

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

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

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

Пропишем такой класс прямо в файле класса-коллекции графических объектов \MQL5\Include\DoEasy\Collections\GraphElementsCollection.mqh:

//+------------------------------------------------------------------+
//|                                      GraphElementsCollection.mqh |
//|                                  Copyright 2021, MetaQuotes Ltd. |
//|                             https://mql5.com/ru/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2021, MetaQuotes Ltd."
#property link      "https://mql5.com/ru/users/artmedia70"
#property version   "1.00"
//+------------------------------------------------------------------+
//| Включаемые файлы                                                 |
//+------------------------------------------------------------------+
#include "ListObj.mqh"
#include "..\Services\Select.mqh"
#include "..\Objects\Graph\Form.mqh"
//+------------------------------------------------------------------+
//| Класс управления объектами чарта                                 |
//+------------------------------------------------------------------+
class CChartObjectsControl : public CObject
  {
private:
   ENUM_TIMEFRAMES   m_chart_timeframe;         // Период графика
   long              m_chart_id;                // Идентификатор графика
   string            m_chart_symbol;            // Символ графика
   bool              m_is_graph_obj_event;      // Флаг события в списке графических объектов
   int               m_total_objects;           // Количество графических объектов
   int               m_last_objects;            // Количество графических объектов на прошлой проверке
   int               m_index_object;            // Индекс последнего добавленного графического объекта в коллекцию из списка объектов терминала
   int               m_delta_graph_obj;         // Разница в количестве графических объектов по сравнению с прошлой проверкой
public:
//--- Возврат значений переменных
   ENUM_TIMEFRAMES   Timeframe(void)                           const { return this.m_chart_timeframe;    }
   long              ChartID(void)                             const { return this.m_chart_id;           }
   string            Symbol(void)                              const { return this.m_chart_symbol;       }
   bool              IsEvent(void)                             const { return this.m_is_graph_obj_event; }
   int               TotalObjects(void)                        const { return this.m_total_objects;      }
   int               Delta(void)                               const { return this.m_delta_graph_obj;    }
//--- Проверяет объекты на чарте
   void              Refresh(void);
//--- Конструкторы
                     CChartObjectsControl(void)
                       { 
                        this.m_chart_id=::ChartID();
                        this.m_chart_timeframe=(ENUM_TIMEFRAMES)::ChartPeriod(this.m_chart_id);
                        this.m_chart_symbol=::ChartSymbol(this.m_chart_id);
                        this.m_is_graph_obj_event=false;
                        this.m_total_objects=0;
                        this.m_last_objects=0;
                        this.m_index_object=0;
                        this.m_delta_graph_obj=0;
                       }
                     CChartObjectsControl(const long chart_id)
                       {
                        this.m_chart_id=chart_id;
                        this.m_chart_timeframe=(ENUM_TIMEFRAMES)::ChartPeriod(this.m_chart_id);
                        this.m_chart_symbol=::ChartSymbol(this.m_chart_id);
                        this.m_is_graph_obj_event=false;
                        this.m_total_objects=0;
                        this.m_last_objects=0;
                        this.m_index_object=0;
                        this.m_delta_graph_obj=0;
                       }
                     
//--- Сравнивает объекты CChartObjectsControl между собой по идентификатору графика (для сортировки списка по свойству объекта)
   virtual int       Compare(const CObject *node,const int mode=0) const
                       {
                        const CChartObjectsControl *obj_compared=node;
                        return(this.ChartID()>obj_compared.ChartID() ? 1 : this.ChartID()<obj_compared.ChartID() ? -1 : 0);
                       }
  };
//+------------------------------------------------------------------+
//| CChartObjectsControl Проверяет объекты на чарте                  |
//+------------------------------------------------------------------+
void CChartObjectsControl::Refresh(void)
  {
//--- Графические объекты на чарте
   this.m_total_objects=::ObjectsTotal(this.ChartID());
   int i=this.m_index_object;
   int delta=this.m_total_objects-this.m_last_objects;
   
//--- Если количество объектов изменилось
   if(delta!=0)
     {
      //--- Создадим строку и выведем её в журнал с указание идентификатора графика, его символа и периода
      string txt=", "+(delta>0 ? "Added: " : "Deleted: ")+(string)fabs(delta)+" obj";
      Print(DFUN,"ChartID=",this.ChartID(),", ",this.Symbol(),", ",TimeframeDescription(this.Timeframe()),txt);
     }

//--- сохранение индекса последнего добавленного ордера и разницы по сравнению с прошлой проверкой
   this.m_delta_graph_obj=i-this.m_index_object;
   this.m_index_object=i;
   this.m_last_objects=this.m_total_objects;
   this.m_is_graph_obj_event=(bool)this.m_delta_graph_obj;
  }
//+------------------------------------------------------------------+

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

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

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

Добавим в ранее созданный класс CGraphElementsCollection новые переменные и методы.

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

//+------------------------------------------------------------------+
//| Коллекция графических объектов                                   |
//+------------------------------------------------------------------+
class CGraphElementsCollection : public CBaseObj
  {
private:
   CArrayObj         m_list_charts_control;     // Список объектов управления чартами
   CListObj          m_list_all_graph_obj;      // Список всех графических объектов
   bool              m_is_graph_obj_event;      // Флаг события в списке графических объектов
   int               m_total_objects;           // Количество графических объектов
   int               m_delta_graph_obj;         // Разница в количестве графических объектов по сравнению с прошлой проверкой
   
//--- Возвращает флаг наличия объекта-графического элемента в списке графических объектов
   bool              IsPresentGraphElmInList(const int id,const ENUM_GRAPH_ELEMENT_TYPE type_obj);

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

//--- Возвращает флаг наличия объекта-графического элемента в списке графических объектов
   bool              IsPresentGraphElmInList(const int id,const ENUM_GRAPH_ELEMENT_TYPE type_obj);
//--- Возвращает указатель на объект управления объектами указанного чарта
   CChartObjectsControl *GetChartObjectCtrlObj(const long chart_id);
//--- Создаёт новый объект управления графическими объектами указанного чарта и добавляет его в список
   CChartObjectsControl *CreateChartObjectCtrlObj(const long chart_id);
//--- Обновляет список графических объектов по идентификатору чарта
   void              RefreshByChartID(const long chart_id);

public:

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

public:
//--- Возвращает себя
   CGraphElementsCollection *GetObject(void)                                                             { return &this;                        }
   //--- Возвращает полный список-коллекцию "как есть"
   CArrayObj        *GetList(void)                                                                       { return &this.m_list_all_graph_obj;   }
   //--- Возвращает список по выбранному (1) целочисленному, (2) вещественному и (3) строковому свойству, удовлетворяющему сравниваемому критерию
   CArrayObj        *GetList(ENUM_CANV_ELEMENT_PROP_INTEGER property,long value,ENUM_COMPARER_TYPE mode=EQUAL)  { return CSelect::ByGraphCanvElementProperty(this.GetList(),property,value,mode);  }
   CArrayObj        *GetList(ENUM_CANV_ELEMENT_PROP_DOUBLE property,double value,ENUM_COMPARER_TYPE mode=EQUAL) { return CSelect::ByGraphCanvElementProperty(this.GetList(),property,value,mode);  }
   CArrayObj        *GetList(ENUM_CANV_ELEMENT_PROP_STRING property,string value,ENUM_COMPARER_TYPE mode=EQUAL) { return CSelect::ByGraphCanvElementProperty(this.GetList(),property,value,mode);  }
   //--- Возвращает количество новых графических объектов, (3) флаг произошедшего изменения в списке графических объектов
   int               NewObjects(void)   const                                                            { return this.m_delta_graph_obj;       }
   bool              IsEvent(void) const                                                                 { return this.m_is_graph_obj_event;    }
   //--- Конструктор
                     CGraphElementsCollection();
//--- Выводит в журнал описание свойств объекта (full_prop=true - все свойства, false - только поддерживаемые - реализуется в наследниках класса)
   virtual void      Print(const bool full_prop=false,const bool dash=false);
//--- Выводит в журнал краткое описание объекта
   virtual void      PrintShort(const bool dash=false,const bool symbol=false);

//--- Создаёт список объектов управления чартами, возвращает количество чартов
   int               CreateChartControlList(void);
//--- Обновляет список (1) всех графических объектов, (2) на указанном чарте, заполняет данные о количестве новых и устанавливает флаг события
   void              Refresh(void);
   void              Refresh(const long chart_id);
  };
//+------------------------------------------------------------------+

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

//+------------------------------------------------------------------+
//| Конструктор                                                      |
//+------------------------------------------------------------------+
CGraphElementsCollection::CGraphElementsCollection()
  {
   this.m_type=COLLECTION_GRAPH_OBJ_ID;
   ::ChartSetInteger(::ChartID(),CHART_EVENT_MOUSE_MOVE,true);
   ::ChartSetInteger(::ChartID(),CHART_EVENT_MOUSE_WHEEL,true);
   this.m_list_all_graph_obj.Type(COLLECTION_GRAPH_OBJ_ID);
   this.m_list_all_graph_obj.Sort(SORT_BY_CANV_ELEMENT_ID);
   this.m_list_all_graph_obj.Clear();
   this.m_list_charts_control.Sort();
   this.m_list_charts_control.Clear();
   this.m_total_objects=0;
   this.m_is_graph_obj_event=false;
  }
//+------------------------------------------------------------------+

Рассмотрим реализацию объявленных методов.

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

//+------------------------------------------------------------------+
//| Создаёт новый объект управления графическими объектами           |
//| указанного чарта и добавляет его в список                        |
//+------------------------------------------------------------------+
CChartObjectsControl *CGraphElementsCollection::CreateChartObjectCtrlObj(const long chart_id)
  {
//--- Создаём новый объект управления объектами графика по идентификатору
   CChartObjectsControl *obj=new CChartObjectsControl(chart_id);
//--- Еслиобъект не создан - сообщаем об ошибке и возвращаем NULL
   if(obj==NULL)
     {
      ::Print(DFUN,CMessage::Text(MSG_CHART_OBJ_COLLECTION_ERR_FAILED_CREATE_CTRL_OBJ),(string)chart_id);
      return NULL;
     }
//--- Если объект не удалось добавить в список - сообщаем об ошибке, удаляем объект и возвращаем NULL
   if(!this.m_list_charts_control.Add(obj))
     {
      CMessage::ToLog(DFUN,MSG_LIB_SYS_FAILED_OBJ_ADD_TO_LIST);
      delete obj;
      return NULL;
     }
//--- Возвращаем указатель на созданный и добавленный в список объект
   return obj;
  }
//+------------------------------------------------------------------+

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

//+------------------------------------------------------------------+
//| Возвращает указатель на объект                                   |
//| управления объектами указанного чарта                            |
//+------------------------------------------------------------------+
CChartObjectsControl *CGraphElementsCollection::GetChartObjectCtrlObj(const long chart_id)
  {
//--- В цикле во общему количеству объектов в списке
   for(int i=0;i<this.m_list_charts_control.Total();i++)
     {
      //--- Получаем указатель на очередной объект
      CChartObjectsControl *obj=this.m_list_charts_control.At(i);
      //--- Если указатель получить не удалось - переходим к следующему
      if(obj==NULL)
         continue;
      //--- Если идентификатор графика объекта равен искомому - возвращаем указатель на объект в списке
      if(obj.ChartID()==chart_id)
         return obj;
     }
//--- Не нашли объект - возвращаем NULL
   return NULL;
  }
//+------------------------------------------------------------------+

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

//+------------------------------------------------------------------+
//| Создаёт список объектов управления чартами                       |
//+------------------------------------------------------------------+
int CGraphElementsCollection::CreateChartControlList(void)
  {
   //--- Очистим список объектов управления чартами и установим ему флаг сортированного списка
   this.m_list_charts_control.Clear();
   this.m_list_charts_control.Sort();
   //--- Объявим переменные для поиска графиков
   long chart_id=0;
   int i=0;
//--- В цикле по всем открытым графикам терминала (не более 100)
   while(i<CHARTS_MAX)
     {
      //--- Получим идентификатор графика
      chart_id=::ChartNext(chart_id);
      if(chart_id<0)
         break;
      //--- Создаём объект управления объектами чарта на основании идентификатора текущего графика в цикле и добавим его в список
      CChartObjectsControl *chart_control=new CChartObjectsControl(chart_id);
      if(chart_control==NULL)
         continue;
      //--- Если такой объект уже есть в списке - сообщаем об этом, удаляем объект и идём к следующему графику
      if(this.m_list_charts_control.Search(chart_control)>WRONG_VALUE)
        {
         ::Print(DFUN,CMessage::Text(MSG_CHART_OBJ_COLLECTION_ERR_OBJ_ALREADY_EXISTS),(string)chart_id);
         delete chart_control;
         continue;
        }
      //--- Если не удалось добавить объект в список - сообщаем об этом, удаляем объект и идём к следующему графику
      if(!this.m_list_charts_control.Add(chart_control))
        {
         CMessage::ToLog(DFUN_ERR_LINE,MSG_LIB_SYS_FAILED_OBJ_ADD_TO_LIST);
         delete chart_control;
         continue;
        }
      //--- Увеличиваем индекс цикла
      i++;
     }
   //--- Успешно заполнили список - возвращаем количество его элементов
   return this.m_list_charts_control.Total();
  }
//+------------------------------------------------------------------+

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

//+------------------------------------------------------------------+
//| Обновляет список графических объектов по идентификатору чарта    |
//+------------------------------------------------------------------+
void CGraphElementsCollection::RefreshByChartID(const long chart_id)
  {
//--- Получаем указатель на объект управления графическими объектами
   CChartObjectsControl *obj=GetChartObjectCtrlObj(chart_id);
//--- Если такого объекта нет в списке - создаём новый и добавляем его в список
   if(obj==NULL)
      obj=this.CreateChartObjectCtrlObj(chart_id);
//--- Если указатель на объект валидный - обновляем список графических объектов на указанном графике
   if(obj!=NULL)
      obj.Refresh();
  }
//+------------------------------------------------------------------+

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

//+------------------------------------------------------------------+
//| Обновляет список графических объектов на указанном чарте         |
//+------------------------------------------------------------------+
void CGraphElementsCollection::Refresh(const long chart_id)
  {
//--- Получаем указатель на объект управления графическими объектами
   CChartObjectsControl *obj=GetChartObjectCtrlObj(chart_id);
//--- Если цказатель получить не удалось - уходим из метода
   if(obj==NULL)
      return;
//--- Обновляем список графических объектов на указанном графике
   obj.Refresh();
  }
//+------------------------------------------------------------------+

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

//+------------------------------------------------------------------+
//| Обновляет список всех графических объектов                       |
//+------------------------------------------------------------------+
void CGraphElementsCollection::Refresh(void)
  {
//--- Объявим переменные для поиска графиков
   long chart_id=0;
   int i=0;
//--- В цикле по всем открытым графикам терминала (не более 100)
   while(i<CHARTS_MAX)
     {
      //--- Получим идентификатор графика
      chart_id=::ChartNext(chart_id);
      if(chart_id<0)
         break;
      //--- Обновляем список графических объектов по идентификатору чарта
      this.RefreshByChartID(chart_id);
      //--- Увеличиваем индекс цикла
      i++;
     }
  }
//+------------------------------------------------------------------+

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

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

В файле \MQL5\Include\DoEasy\Engine.mqh в приватной секции класса объявим экземпляр класса-коллекции графических объектов, переменную-флаг события в списке графических объектов и объявим метод контроля за событиями графических объектов:

//+------------------------------------------------------------------+
//| Класс-основа библиотеки                                          |
//+------------------------------------------------------------------+
class CEngine
  {
private:
   CHistoryCollection   m_history;                       // Коллекция исторических ордеров и сделок
   CMarketCollection    m_market;                        // Коллекция рыночных ордеров и сделок
   CEventsCollection    m_events;                        // Коллекция событий
   CAccountsCollection  m_accounts;                      // Коллекция аккаунтов
   CSymbolsCollection   m_symbols;                       // Коллекция символов
   CTimeSeriesCollection m_time_series;                  // Коллекция таймсерий
   CBuffersCollection   m_buffers;                       // Коллекция индикаторных буферов
   CIndicatorsCollection m_indicators;                   // Коллекция индикаторов
   CTickSeriesCollection m_tick_series;                  // Коллекция тиковых серий
   CMBookSeriesCollection m_book_series;                 // Коллекция серий стаканов цен
   CMQLSignalsCollection m_signals_mql5;                 // Коллекция сигналов сервиса сигналов mql5.com
   CChartObjCollection  m_charts;                        // Коллекция чартов
   CGraphElementsCollection m_graph_objects;             // Коллекция графических объектов
   CResourceCollection  m_resource;                      // Список ресурсов
   CTradingControl      m_trading;                       // Объект управления торговлей
   CPause               m_pause;                         // Объект "Пауза"
   CArrayObj            m_list_counters;                 // Список счётчиков таймера
   int                  m_global_error;                  // Код глобальной ошибки
   bool                 m_first_start;                   // Флаг первого запуска
   bool                 m_is_hedge;                      // Флаг хедж-счёта
   bool                 m_is_tester;                     // Флаг работы в тестере
   bool                 m_is_market_trade_event;         // Флаг торгового события на счёте
   bool                 m_is_history_trade_event;        // Флаг торгового события в истории счёта
   bool                 m_is_account_event;              // Флаг события изменения аккаунта
   bool                 m_is_symbol_event;               // Флаг события изменения свойств символа
   bool                 m_is_graph_obj_event;            // Флаг события в списке графических объектов
   ENUM_TRADE_EVENT     m_last_trade_event;              // Последнее торговое событие на счёте
   int                  m_last_account_event;            // Последнее событие в свойствах счёта
   int                  m_last_symbol_event;             // Последнее событие в свойствах символа
   ENUM_PROGRAM_TYPE    m_program;                       // Тип программы
   string               m_name;                          // Имя программы
//--- Возвращает индекс счётчика по id
   int                  CounterIndex(const int id) const;
//--- Возвращает флаг первого запуска
   bool                 IsFirstStart(void);
//--- Работа с событиями (1) ордеров, сделок и позиций, (2) аккаунтов, (3) графических объектов
   void                 TradeEventsControl(void);
   void                 AccountEventsControl(void);
   void                 GraphObjEventsControl(void);
//--- (1) Работа с коллекцией символов и (2) событиями списка символов в окне обзора рынка
   void                 SymbolEventsControl(void);
   void                 MarketWatchEventsControl(void);
//--- Возвращает последний (1) рыночный отложенный ордер, (2) маркет-ордер, (3) последнюю позицию, (4) позицию по тикету
   COrder              *GetLastMarketPending(void);
   COrder              *GetLastMarketOrder(void);
   COrder              *GetLastPosition(void);
   COrder              *GetPosition(const ulong ticket);
//--- Возвращает последний (1) удалённый отложенный ордер, (2) исторический маркет-ордер, (3) исторический ордер (маркет или отложенный) по его тикету
   COrder              *GetLastHistoryPending(void);
   COrder              *GetLastHistoryOrder(void);
   COrder              *GetHistoryOrder(const ulong ticket);
//--- Возвращает (1) первый и (2) последний исторический маркет-ордер из списка всех ордеров позиции, (3) последнюю сделку
   COrder              *GetFirstOrderPosition(const ulong position_id);
   COrder              *GetLastOrderPosition(const ulong position_id);
   COrder              *GetLastDeal(void);
//--- Извлекает нужное ushort-число из упакованного long-значения
   ushort               LongToUshortFromByte(const long source_value,const uchar index) const;
   
public:

В конструкторе класса создадим счётчик таймера коллекции графических объектов:

//+------------------------------------------------------------------+
//| CEngine конструктор                                              |
//+------------------------------------------------------------------+
CEngine::CEngine() : m_first_start(true),
                     m_last_trade_event(TRADE_EVENT_NO_EVENT),
                     m_last_account_event(WRONG_VALUE),
                     m_last_symbol_event(WRONG_VALUE),
                     m_global_error(ERR_SUCCESS)
  {
   this.m_is_hedge=#ifdef __MQL4__ true #else bool(::AccountInfoInteger(ACCOUNT_MARGIN_MODE)==ACCOUNT_MARGIN_MODE_RETAIL_HEDGING) #endif;
   this.m_is_tester=::MQLInfoInteger(MQL_TESTER);
   this.m_program=(ENUM_PROGRAM_TYPE)::MQLInfoInteger(MQL_PROGRAM_TYPE);
   this.m_name=::MQLInfoString(MQL_PROGRAM_NAME);
   
   this.m_list_counters.Sort();
   this.m_list_counters.Clear();
   this.CreateCounter(COLLECTION_ORD_COUNTER_ID,COLLECTION_ORD_COUNTER_STEP,COLLECTION_ORD_PAUSE);
   this.CreateCounter(COLLECTION_ACC_COUNTER_ID,COLLECTION_ACC_COUNTER_STEP,COLLECTION_ACC_PAUSE);
   this.CreateCounter(COLLECTION_SYM_COUNTER_ID1,COLLECTION_SYM_COUNTER_STEP1,COLLECTION_SYM_PAUSE1);
   this.CreateCounter(COLLECTION_SYM_COUNTER_ID2,COLLECTION_SYM_COUNTER_STEP2,COLLECTION_SYM_PAUSE2);
   this.CreateCounter(COLLECTION_REQ_COUNTER_ID,COLLECTION_REQ_COUNTER_STEP,COLLECTION_REQ_PAUSE);
   this.CreateCounter(COLLECTION_TS_COUNTER_ID,COLLECTION_TS_COUNTER_STEP,COLLECTION_TS_PAUSE);
   this.CreateCounter(COLLECTION_IND_TS_COUNTER_ID,COLLECTION_IND_TS_COUNTER_STEP,COLLECTION_IND_TS_PAUSE);
   this.CreateCounter(COLLECTION_TICKS_COUNTER_ID,COLLECTION_TICKS_COUNTER_STEP,COLLECTION_TICKS_PAUSE);
   this.CreateCounter(COLLECTION_CHARTS_COUNTER_ID,COLLECTION_CHARTS_COUNTER_STEP,COLLECTION_CHARTS_PAUSE);
   this.CreateCounter(COLLECTION_GRAPH_OBJ_COUNTER_ID,COLLECTION_GRAPH_OBJ_COUNTER_STEP,COLLECTION_GRAPH_OBJ_PAUSE);
   
   ::ResetLastError();
   #ifdef __MQL5__
      if(!::EventSetMillisecondTimer(TIMER_FREQUENCY))
        {
         ::Print(DFUN_ERR_LINE,CMessage::Text(MSG_LIB_SYS_FAILED_CREATE_TIMER),(string)::GetLastError());
         this.m_global_error=::GetLastError();
        }
   //---__MQL4__
   #else 
      if(!this.IsTester() && !::EventSetMillisecondTimer(TIMER_FREQUENCY))
        {
         ::Print(DFUN_ERR_LINE,CMessage::Text(MSG_LIB_SYS_FAILED_CREATE_TIMER),(string)::GetLastError());
         this.m_global_error=::GetLastError();
        }
   #endif 
   //---
  }
//+------------------------------------------------------------------+

В таймере класса впишем обработку таймера коллекции графических объектов:

//+------------------------------------------------------------------+
//| CEngine таймер                                                   |
//+------------------------------------------------------------------+
void CEngine::OnTimer(SDataCalculate &data_calculate)
  {
//--- Если это не тестер - работаем с событиями коллекций по таймеру
   if(!this.IsTester())
     {
   //--- Таймер коллекций исторических ордеров и сделок и рыночных ордеров и позиций
      int index=this.CounterIndex(COLLECTION_ORD_COUNTER_ID);
      CTimerCounter* cnt1=this.m_list_counters.At(index);
      if(cnt1!=NULL)
        {
         //--- Если пауза завершилась - работаем с событиями коллекций ордеров, сделок и позиций
         if(cnt1.IsTimeDone())
            this.TradeEventsControl();
        }
   //--- Таймер коллекции аккаунтов
      index=this.CounterIndex(COLLECTION_ACC_COUNTER_ID);
      CTimerCounter* cnt2=this.m_list_counters.At(index);
      if(cnt2!=NULL)
        {
         //--- Если пауза завершилась - работаем с событиями коллекции аккаунтов
         if(cnt2.IsTimeDone())
            this.AccountEventsControl();
        }
   //--- Таймер1 коллекции символов (обновление котировочных данных символов в коллекции)
      index=this.CounterIndex(COLLECTION_SYM_COUNTER_ID1);
      CTimerCounter* cnt3=this.m_list_counters.At(index);
      if(cnt3!=NULL)
        {
         //--- Если пауза завершилась - обновляем котировочные данные всех символов в коллекции
         if(cnt3.IsTimeDone())
            this.m_symbols.RefreshRates();
        }
   //--- Таймер2 коллекции символов (обновление всех данных всех символов в коллекции и отслеживание событий символов и списка символов в окне обзора рынка)
      index=this.CounterIndex(COLLECTION_SYM_COUNTER_ID2);
      CTimerCounter* cnt4=this.m_list_counters.At(index);
      if(cnt4!=NULL)
        {
         //--- Если пауза завершилась
         if(cnt4.IsTimeDone())
           {
            //--- обновляем данные и работаем с событиями всех символов в коллекции
            this.SymbolEventsControl();
            //--- Если работаем со списком из обзора рынка - проверяем события окна обзора рынка
            if(this.m_symbols.ModeSymbolsList()==SYMBOLS_MODE_MARKET_WATCH)
               this.MarketWatchEventsControl();
           }
        }
   //--- Таймер торгового класса
      index=this.CounterIndex(COLLECTION_REQ_COUNTER_ID);
      CTimerCounter* cnt5=this.m_list_counters.At(index);
      if(cnt5!=NULL)
        {
         //--- Если пауза завершилась - работаем со списком отложенных запросов
         if(cnt5.IsTimeDone())
            this.m_trading.OnTimer();
        }
   //--- Таймер коллекции таймсерий
      index=this.CounterIndex(COLLECTION_TS_COUNTER_ID);
      CTimerCounter* cnt6=this.m_list_counters.At(index);
      if(cnt6!=NULL)
        {
         //--- Если пауза завершилась - работаем со списком таймсерий (обновляем все кроме текущей)
         if(cnt6.IsTimeDone())
            this.SeriesRefreshAllExceptCurrent(data_calculate);
        }
        
   //--- Таймер коллекции таймсерий данных индикаторных буферов
      index=this.CounterIndex(COLLECTION_IND_TS_COUNTER_ID);
      CTimerCounter* cnt7=this.m_list_counters.At(index);
      if(cnt7!=NULL)
        {
         //--- Если пауза завершилась - работаем со списком таймсерий индикаторных данных (обновляем все кроме текущей)
         if(cnt7.IsTimeDone()) 
            this.IndicatorSeriesRefreshAll();
        }
   //--- Таймер коллекции тиковых серий
      index=this.CounterIndex(COLLECTION_TICKS_COUNTER_ID);
      CTimerCounter* cnt8=this.m_list_counters.At(index);
      if(cnt8!=NULL)
        {
         //--- Если пауза завершилась - работаем со списком тиковых серий (обновляем все кроме текущей)
         if(cnt8.IsTimeDone())
            this.TickSeriesRefreshAllExceptCurrent();
        }
   //--- Таймер коллекции чартов
      index=this.CounterIndex(COLLECTION_CHARTS_COUNTER_ID);
      CTimerCounter* cnt9=this.m_list_counters.At(index);
      if(cnt9!=NULL)
        {
         //--- Если пауза завершилась - работаем со списком чартов
         if(cnt9.IsTimeDone())
            this.ChartsRefreshAll();
        }
        
   //--- Таймер коллекции графических объектов
      index=this.CounterIndex(COLLECTION_GRAPH_OBJ_COUNTER_ID);
      CTimerCounter* cnt10=this.m_list_counters.At(index);
      if(cnt10!=NULL)
        {
         //--- Если пауза завершилась - работаем со списком графических объектов
         if(cnt10.IsTimeDone())
            this.GraphObjEventsControl();
        }
        
     }
//--- Если тестер - работаем с событиями коллекций по тику
   else
     {
      //--- работаем с событиями коллекций ордеров, сделок и позиций по тику
      this.TradeEventsControl();
      //--- работаем с событиями коллекции аккаунтов по тику
      this.AccountEventsControl();
      //--- обновляем котировочные данные всех символов в коллекции по тику
      this.m_symbols.RefreshRates();
      //--- работаем с событиями всех символов в коллекции по тику
      this.SymbolEventsControl();
      //--- работаем со списком отложенных запросов по тику
      this.m_trading.OnTimer();
      //--- работаем со списком таймсерий по тику
      this.SeriesRefresh(data_calculate);
      //--- работаем со списком таймсерий индикаторных буферов по тику
      this.IndicatorSeriesRefreshAll();
      //--- работаем со списком тиковых серий по тику
      this.TickSeriesRefreshAll();
      //--- работаем со списком графических объектов по тику
      this.GraphObjEventsControl();
     }
  }
//+------------------------------------------------------------------+

За пределами тела класса напишем реализацию метода проверки событий графических объектов:

//+------------------------------------------------------------------+
//| Проверка событий графических объектов                            |
//+------------------------------------------------------------------+
void CEngine::GraphObjEventsControl(void)
  {
//--- Проверка изменений в списке графических объектов и установка флага их событий
   this.m_graph_objects.Refresh();
   this.m_is_graph_obj_event=this.m_graph_objects.IsEvent();
//--- Если есть изменения в списке графических объектов
   if(this.m_is_graph_obj_event)
     {
      Print(DFUN,"Graph obj is event. NewObjects: ",m_graph_objects.NewObjects());
      //--- Получаем последнее событие изменения свойств графических объектов
      // ...
     }
  }
//+------------------------------------------------------------------+

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

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


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

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

Здесь нам потребуется внести совсем немного изменений.

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

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {

  }
//+------------------------------------------------------------------+
//| Timer function                                                   |
//+------------------------------------------------------------------+
void OnTimer()
  {
//--- Запускаем таймер библиотеки только не в тестере
   if(!MQLInfoInteger(MQL_TESTER))
      engine.OnTimer(rates_data);
  }
//+------------------------------------------------------------------+

В обработчике OnChartEvent() добавим запрет на вызов контекстного меню правой кнопки мышки в случае, если нажата клавиша Ctrl, и разрешение в случае, если клавиша отпущена — при её удержании мы создаём графические элементы из объекта-бара с описанием типа этого бара (это делали в прошлой статье), и немного увеличим ширину создаваемого объекта — чтобы в него полностью помещалось длинное описание бара ("Свеча с нулевым телом"):

//+------------------------------------------------------------------+
//| ChartEvent function                                              |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,
                  const long &lparam,
                  const double &dparam,
                  const string &sparam)
  {
//--- Если работа в тестере - выход
   if(MQLInfoInteger(MQL_TESTER))
      return;
//--- Если перемещаем мышь
   if(id==CHARTEVENT_MOUSE_MOVE)
     {
      CForm *form=NULL;
      datetime time=0;
      double price=0;
      int wnd=0;
      
      //--- Если клавиша Ctrl не нажата
      if(!IsCtrlKeyPressed())
        {
         //--- очищаем список созданных объектов-форм и разрешаем прокручивать график мышкой и показывать контекстное меню
         list_forms.Clear();
         ChartSetInteger(ChartID(),CHART_MOUSE_SCROLL,true);
         ChartSetInteger(ChartID(),CHART_CONTEXT_MENU,true);
         return;
        }
      
      //--- Если координаты  X и Y графика успешно преобразованы в значения время и цена
      if(ChartXYToTimePrice(ChartID(),(int)lparam,(int)dparam,wnd,time,price))
        {
         //--- получим индекс бара, над которым находится курсор
         int index=iBarShift(Symbol(),PERIOD_CURRENT,time);
         if(index==WRONG_VALUE)
            return;
         
         //--- Получим объект-бар по индексу
         CBar *bar=engine.SeriesGetBar(Symbol(),Period(),index);
         if(bar==NULL)
            return;
         
         //--- Преобразуем координаты графика из представления время/цена объекта-бара в координаты по оси X и Y
         int x=(int)lparam,y=(int)dparam;
         if(!ChartTimePriceToXY(ChartID(),0,bar.Time(),(bar.Open()+bar.Close())/2.0,x,y))
            return;
         
         //--- Запретим перемещать график мышкой и показывать контекстное меню
         ChartSetInteger(ChartID(),CHART_MOUSE_SCROLL,false);
         ChartSetInteger(ChartID(),CHART_CONTEXT_MENU,false);
         
         //--- Создадим имя объекта-формы и скроем все объекты кроме одного с таким именем
         string name="FormBar_"+(string)index;
         HideFormAllExceptOne(name);
         
         //--- Если ещё нет объекта-формы с таким именем
         if(!IsPresentForm(name))
           {
            //--- создадим новый объект-форму
            form=bar.CreateForm(index,name,x,y,114,16);   
            if(form==NULL)
               return;
            
            //--- Установим форме флаги активности и неперемещаемости
            form.SetActive(true);
            form.SetMovable(false);
            //--- Установим непрозрачность 200
            form.SetOpacity(200);
            //--- Цвет фона формы зададим как первый цвет из массива цветов
            form.SetColorBackground(array_clr[0]);
            //--- Цвет очерчивающей рамки формы
            form.SetColorFrame(C'47,70,59');
            //--- Установим флаг рисования тени
            form.SetShadow(true);
            //--- Рассчитаем цвет тени как цвет фона графика, преобразованный в монохромный
            color clrS=form.ChangeColorSaturation(form.ColorBackground(),-100);
            //--- Если в настройках задано использовать цвет фона графика, то затемним монохромный цвет на 20 единиц
            //--- Иначе - будем использовать для рисования тени заданный в настройках цвет
            color clr=(InpUseColorBG ? form.ChangeColorLightness(clrS,-20) : InpColorForm3);
            //--- Нарисуем тень формы со смещением от формы вправо-вниз на три пикселя по всем осям
            //--- Непрозрачность тени при этом установим равной 200, а радиус размытия равный 4
            form.DrawShadow(2,2,clr,200,3);
            //--- Зальём фон формы вертикальным градиентом
            form.Erase(array_clr,form.Opacity());
            //--- Нарисуем очерчивающий прямоугольник по краям формы
            form.DrawRectangle(0,0,form.Width()-1,form.Height()-1,form.ColorFrame(),form.Opacity());
            //--- Если объект-форму не удалось добавить в список - удаляем форму и уходим из обработчика
            if(!list_forms.Add(form))
              {
               delete form;
               return;
              }
            //--- Зафиксируем внешний вид формы
            form.Done();
           }
         //--- Если объект-форма существует
         if(form!=NULL)
           {
            //--- нарисуем на нём текст с описанием типа бара, соответствующего положению курсора мышки и покажем форму
            form.TextOnBG(0,bar.BodyTypeDescription(),form.Width()/2,form.Height()/2-1,FRAME_ANCHOR_CENTER,C'7,28,21');
            form.Show();
           }
         //--- Перерисуем чарт
         ChartRedraw();
        }
     }
  }
//+------------------------------------------------------------------+

И это все доработки, которые нам нужно было сделать в советнике.

Запустим его на графике одного символа (при этом открытых графиков должно быть больше одного), добавим графические объекты на каждый из графиков — в журнале будут выводиться об этом сообщения. Затем на каждом графике нажмём клавишу Delete — это удалит все выделенные графические объекты. И об этом также будут выведены сообщения в журнал:



Что дальше

В следующей статье продолжим работу над коллекцией графических объектов.

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

Ниже прикреплены все файлы текущей версии библиотеки и файл тестового советника для MQL5. Их можно скачать и протестировать всё самостоятельно.
При возникновении вопросов, замечаний и пожеланий, вы можете озвучить их в комментариях к статье.

К содержанию

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

Графика в библиотеке DoEasy (Часть 73): Объект-форма графического элемента
Графика в библиотеке DoEasy (Часть 74): Базовый графический элемент на основе класса CCanvas
Графика в библиотеке DoEasy (Часть 75): Методы работы с примитивами и текстом в базовом графическом элементе
Графика в библиотеке DoEasy (Часть 76): Объект Форма и предопределённые цветовые темы
Графика в библиотеке DoEasy (Часть 77): Класс объекта Тень
Графика в библиотеке DoEasy (Часть 78): Принципы анимации в библиотеке. Нарезка изображений
Графика в библиотеке DoEasy (Часть 79): Класс объекта "Кадр анимации" и его объекты-наследники
Графика в библиотеке DoEasy (Часть 80): Класс объекта "Кадр геометрической анимации"
Графика в библиотеке DoEasy (Часть 81): Интегрируем графику в объекты библиотеки