English Русский Español Deutsch 日本語 Português
DoEasy 函数库中的其他类(第七十一部分):图表对象集合事件

DoEasy 函数库中的其他类(第七十一部分):图表对象集合事件

MetaTrader 5示例 | 12 七月 2021, 10:38
961 0
Artyom Trishkin
Artyom Trishkin

内容


概述

在上一篇文章中,我介绍了一些图表对象属性和相关对象的自动更新 — 打开一个新的/关闭一个现有的品种图表(图表对象)、在图表对象中添加新的/关闭一个现有的指标窗口、以及在图表窗口中添加新的/删除/更改现有的指标。

在本文中,我将为图表窗口中的图表对象、图表窗口对象和指标对象创建事件功能。 此功能将允许我们在上述对象事件注册时,发送自定义事件至控制程序图表。

请记住,操控图表是通过手动图表控制实现的,即当用户手动更改图表,或利用他们自己的程序控制元素时,操作会一次一个对象逐一执行。 在计时器单次跳动时,一次性针对若干个对象进行编程更改可能会导致所发生事件的错误定义。 最好仅记录最后一个事件。 最糟糕的是,变化的对象将被错误地定义。 我当然会在编写代码时考虑到这种可能性,并尽可能插入代码,允许批量更改对象参数。 不过,我不打算微调和测试程序化并发处理若干个图表元素。 如果来自函数库的用户这样的需求,我再进行返工任务。


改进库类

我们向函数库中添加新消息。 在 \MQL5\Include\DoEasy\Data.mqh 里,加入新的消息索引:

   MSG_CHART_OBJ_TEMPLATE_SAVED,                      // Chart template saved
   MSG_CHART_OBJ_TEMPLATE_APPLIED,                    // Template applied to chart
   
   MSG_CHART_OBJ_INDICATOR_ADDED,                     // Indicator added
   MSG_CHART_OBJ_INDICATOR_REMOVED,                   // Indicator removed
   MSG_CHART_OBJ_INDICATOR_CHANGED,                   // Indicator changed
   MSG_CHART_OBJ_WINDOW_ADDED,                        // Subwindow added
   MSG_CHART_OBJ_WINDOW_REMOVED,                      // Subwindow removed

//--- CChartObjCollection
   MSG_CHART_COLLECTION_TEXT_CHART_COLLECTION,        // Chart collection
   MSG_CHART_COLLECTION_ERR_FAILED_CREATE_CHART_OBJ,  // Failed to create a new chart object
   MSG_CHART_COLLECTION_ERR_FAILED_ADD_CHART,         // Failed to add a chart object to the collection
   MSG_CHART_COLLECTION_ERR_CHARTS_MAX,               // Cannot open new chart. Number of open charts at maximum
   
   MSG_CHART_COLLECTION_CHART_OPENED,                 // Chart opened
   MSG_CHART_COLLECTION_CHART_CLOSED,                 // Chart closed
  
  };
//+------------------------------------------------------------------+

与新添加的索引相对应的消息文本:

   {"Шаблон графика сохранён","Chart template saved"},
   {"Шаблон применён к графику","Template applied to the chart"},
   
   {"Добавлен индикатор","Added indicator"},
   {"Удалён индикатор","Removed indicator"},
   {"Изменён индикатор","Changed indicator"},
   
   {"Добавлено подокно","Added subwindow"},
   {"Удалено подокно","Removed subwindow"},
   
//--- CChartObjCollection
   {"Коллекция чартов","Chart collection"},
   {"Не удалось создать новый объект-чарт","Failed to create new chart object"},
   {"Не удалось добавить объект-чарт в коллекцию","Failed to add chart object to collection"},
   {"Нельзя открыть новый график, так как количество открытых графиков уже максимальное","You cannot open a new chart, since the number of open charts is already maximum"},
   
   {"Открыт график","Open chart"},
   {"Закрыт график","Closed chart"},
   
  };
//+---------------------------------------------------------------------+


在本文中,我们将处理一些图表事件。 为了跟踪它们,并指定确定的事件,请在 \MQL5\Include\DoEasy\Defines.mqh 中创建可能的新图表事件枚举:

//+------------------------------------------------------------------+
//| Data for working with charts                                     |
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| List of possible chart events                                    |
//+------------------------------------------------------------------+
enum ENUM_CHART_OBJ_EVENT
  {
   CHART_OBJ_EVENT_NO_EVENT = SIGNAL_MQL5_EVENTS_NEXT_CODE, // No event
   CHART_OBJ_EVENT_CHART_OPEN,                        // "New chart opening" event
   CHART_OBJ_EVENT_CHART_CLOSE,                       // "Chart closure" event
   CHART_OBJ_EVENT_CHART_WND_ADD,                     // "Adding a new window on the chart" event
   CHART_OBJ_EVENT_CHART_WND_DEL,                     // "Removing a window from the chart" event
   CHART_OBJ_EVENT_CHART_WND_IND_ADD,                 // "Adding a new indicator to the chart window" event
   CHART_OBJ_EVENT_CHART_WND_IND_DEL,                 // "Removing an indicator from the chart window" event
   CHART_OBJ_EVENT_CHART_WND_IND_CHANGE,              // "Changing indicator parameters in the chart window" event
  };
#define CHART_OBJ_EVENTS_NEXT_CODE  (CHART_OBJ_EVENT_CHART_WND_IND_CHANGE+1)  // The code of the next event after the last chart event code
//+------------------------------------------------------------------+

当注册已在枚举中指定的图表事件时,会将自定义事件发送到程序图表。 此自定义事件会包含所发生事件的类型,对应枚举中的相应常量。 接下来,程序会分析事件代码,并进行相应的处理。

在实现事件处理时,我遇到了一个意想不到的挑战。 事实证明,为已删除的图表子窗口及其内部指标定义索引极其困难。 为了能更便捷地定义它们,我决定在图表窗口中为指标对象实现一个新属性 — 它所在窗口的索​​引。

所有已删除的图表、图表窗口和相应的指标(描述它们的对象)都将存储在特殊的列表之中,从而令我们能随时接收以前删除的对象。 这便是我们能够从中获取指标窗口索引的对象(如果指标已被删除)。

将图表窗口中指标对象的新属性常量添加到图表对象整数型属性的枚举之中:

//+------------------------------------------------------------------+
//| Chart integer property                                           |
//+------------------------------------------------------------------+
enum ENUM_CHART_PROP_INTEGER
  {
   CHART_PROP_ID = 0,                                 // Chart ID
   CHART_PROP_TIMEFRAME,                              // Chart timeframe
   CHART_PROP_SHOW,                                   // Price chart drawing
   CHART_PROP_IS_OBJECT,                              // Chart object (OBJ_CHART) identification attribute
   CHART_PROP_BRING_TO_TOP,                           // Show chart above all others
   CHART_PROP_CONTEXT_MENU,                           // Enable/disable access to the context menu using the right click 
   CHART_PROP_CROSSHAIR_TOOL,                         // Enable/disable access to the Crosshair tool using the middle click
   CHART_PROP_MOUSE_SCROLL,                           // Scroll the chart horizontally using the left mouse button
   CHART_PROP_EVENT_MOUSE_WHEEL,                      // Send messages about mouse wheel events (CHARTEVENT_MOUSE_WHEEL) to all MQL5 programs on a chart
   CHART_PROP_EVENT_MOUSE_MOVE,                       // Send messages about mouse button click and movement events (CHARTEVENT_MOUSE_MOVE) to all MQL5 programs on a chart
   CHART_PROP_EVENT_OBJECT_CREATE,                    // Send messages about the graphical object creation event (CHARTEVENT_OBJECT_CREATE) to all MQL5 programs on a chart
   CHART_PROP_EVENT_OBJECT_DELETE,                    // Send messages about the graphical object destruction event (CHARTEVENT_OBJECT_DELETE) to all MQL5 programs on a chart
   CHART_PROP_MODE,                                   // Type of the chart (candlesticks, bars or line (ENUM_CHART_MODE))
   CHART_PROP_FOREGROUND,                             // Price chart in the foreground
   CHART_PROP_SHIFT,                                  // Mode of shift of the price chart from the right border
   CHART_PROP_AUTOSCROLL,                             // The mode of automatic shift to the right border of the chart
   CHART_PROP_KEYBOARD_CONTROL,                       // Allow managing the chart using a keyboard
   CHART_PROP_QUICK_NAVIGATION,                       // Allow the chart to intercept Space and Enter key strokes to activate the quick navigation bar
   CHART_PROP_SCALE,                                  // Scale
   CHART_PROP_SCALEFIX,                               // Fixed scale mode
   CHART_PROP_SCALEFIX_11,                            // 1:1 scale mode
   CHART_PROP_SCALE_PT_PER_BAR,                       // The mode of specifying the scale in points per bar
   CHART_PROP_SHOW_TICKER,                            // Display a symbol ticker in the upper left corner
   CHART_PROP_SHOW_OHLC,                              // Display OHLC values in the upper left corner
   CHART_PROP_SHOW_BID_LINE,                          // Display Bid value as a horizontal line on the chart
   CHART_PROP_SHOW_ASK_LINE,                          // Display Ask value as a horizontal line on a chart
   CHART_PROP_SHOW_LAST_LINE,                         // Display Last value as a horizontal line on a chart
   CHART_PROP_SHOW_PERIOD_SEP,                        // Display vertical separators between adjacent periods
   CHART_PROP_SHOW_GRID,                              // Display a grid on the chart
   CHART_PROP_SHOW_VOLUMES,                           // Display volumes on a chart
   CHART_PROP_SHOW_OBJECT_DESCR,                      // Display text descriptions of objects
   CHART_PROP_VISIBLE_BARS,                           // Number of bars on a chart that are available for display
   CHART_PROP_WINDOWS_TOTAL,                          // The total number of chart windows including indicator subwindows
   CHART_PROP_WINDOW_HANDLE,                          // Chart window handle
   CHART_PROP_WINDOW_YDISTANCE,                       // Distance in Y axis pixels between the upper frame of the indicator subwindow and the upper frame of the chart main window
   CHART_PROP_FIRST_VISIBLE_BAR,                      // Number of the first visible bar on the chart
   CHART_PROP_WIDTH_IN_BARS,                          // Width of the chart in bars
   CHART_PROP_WIDTH_IN_PIXELS,                        // Width of the chart in pixels
   CHART_PROP_HEIGHT_IN_PIXELS,                       // Height of the chart in pixels
   CHART_PROP_COLOR_BACKGROUND,                       // Color of background of the chart
   CHART_PROP_COLOR_FOREGROUND,                       // Color of axes, scale and OHLC line
   CHART_PROP_COLOR_GRID,                             // Grid color
   CHART_PROP_COLOR_VOLUME,                           // Color of volumes and position opening levels
   CHART_PROP_COLOR_CHART_UP,                         // Color for the up bar, shadows and body borders of bull candlesticks
   CHART_PROP_COLOR_CHART_DOWN,                       // Color of down bar, its shadow and border of body of the bullish candlestick
   CHART_PROP_COLOR_CHART_LINE,                       // Color of the chart line and the Doji candlesticks
   CHART_PROP_COLOR_CANDLE_BULL,                      // Color of body of a bullish candlestick
   CHART_PROP_COLOR_CANDLE_BEAR,                      // Color of body of a bearish candlestick
   CHART_PROP_COLOR_BID,                              // Color of the Bid price line
   CHART_PROP_COLOR_ASK,                              // Color of the Ask price line
   CHART_PROP_COLOR_LAST,                             // Color of the last performed deal's price line (Last)
   CHART_PROP_COLOR_STOP_LEVEL,                       // Color of stop order levels (Stop Loss and Take Profit)
   CHART_PROP_SHOW_TRADE_LEVELS,                      // Display trade levels on the chart (levels of open positions, Stop Loss, Take Profit and pending orders)
   CHART_PROP_DRAG_TRADE_LEVELS,                      // Enable the ability to drag trading levels on a chart using mouse
   CHART_PROP_SHOW_DATE_SCALE,                        // Display the time scale on a chart
   CHART_PROP_SHOW_PRICE_SCALE,                       // Display a price scale on a chart
   CHART_PROP_SHOW_ONE_CLICK,                         // Display the quick trading panel on the chart
   CHART_PROP_IS_MAXIMIZED,                           // Chart window maximized
   CHART_PROP_IS_MINIMIZED,                           // Chart window minimized
   CHART_PROP_IS_DOCKED,                              // Chart window docked
   CHART_PROP_FLOAT_LEFT,                             // Left coordinate of the undocked chart window relative to the virtual screen
   CHART_PROP_FLOAT_TOP,                              // Upper coordinate of the undocked chart window relative to the virtual screen
   CHART_PROP_FLOAT_RIGHT,                            // Right coordinate of the undocked chart window relative to the virtual screen
   CHART_PROP_FLOAT_BOTTOM,                           // Bottom coordinate of the undocked chart window relative to the virtual screen
   //--- CWndInd
   CHART_PROP_WINDOW_IND_HANDLE,                      // Indicator handle in the chart window
   CHART_PROP_WINDOW_IND_INDEX,                       // Indicator index in the chart window
   CHART_PROP_WINDOW_NUM,                             // Chart window index
  };
#define CHART_PROP_INTEGER_TOTAL (67)                 // Total number of integer properties
#define CHART_PROP_INTEGER_SKIP  (0)                  // Number of integer DOM properties not used in sorting
//+------------------------------------------------------------------+

由于整数型属性的数量增加,将它们的数量从 66 增加到 67

将按图表窗口索引排序添加到图表对象排序标准的枚举当中:

//+------------------------------------------------------------------+
//| Possible chart sorting criteria                                  |
//+------------------------------------------------------------------+
#define FIRST_CHART_DBL_PROP  (CHART_PROP_INTEGER_TOTAL-CHART_PROP_INTEGER_SKIP)
#define FIRST_CHART_STR_PROP  (CHART_PROP_INTEGER_TOTAL-CHART_PROP_INTEGER_SKIP+CHART_PROP_DOUBLE_TOTAL-CHART_PROP_DOUBLE_SKIP)
enum ENUM_SORT_CHART_MODE
  {
//--- Sort by integer properties
   SORT_BY_CHART_ID = 0,                              // Sort by chart ID
   SORT_BY_CHART_TIMEFRAME,                           // Sort by chart timeframe
   SORT_BY_CHART_SHOW,                                // Sort by the price chart drawing attribute
   SORT_BY_CHART_IS_OBJECT,                           // Sort by chart object (OBJ_CHART) identification attribute
   SORT_BY_CHART_BRING_TO_TOP,                        // Sort by the flag of displaying a chart above all others
   SORT_BY_CHART_CONTEXT_MENU,                        // Sort by the flag of enabling/disabling access to the context menu using the right click
   SORT_BY_CHART_CROSSHAIR_TOO,                       // Sort by the flag of enabling/disabling access to the Crosshair tool using the middle click
   SORT_BY_CHART_MOUSE_SCROLL,                        // Sort by the flag of scrolling the chart horizontally using the left mouse button
   SORT_BY_CHART_EVENT_MOUSE_WHEEL,                   // Sort by the flag of sending messages about mouse wheel events to all MQL5 programs on a chart
   SORT_BY_CHART_EVENT_MOUSE_MOVE,                    // Sort by the flag of sending messages about mouse button click and movement events to all MQL5 programs on a chart
   SORT_BY_CHART_EVENT_OBJECT_CREATE,                 // Sort by the flag of sending messages about the graphical object creation event to all MQL5 programs on a chart
   SORT_BY_CHART_EVENT_OBJECT_DELETE,                 // Sort by the flag of sending messages about the graphical object destruction event to all MQL5 programs on a chart
   SORT_BY_CHART_MODE,                                // Sort by chart type
   SORT_BY_CHART_FOREGROUND,                          // Sort by the "Price chart in the foreground" flag
   SORT_BY_CHART_SHIFT,                               // Sort by the "Mode of shift of the price chart from the right border" flag
   SORT_BY_CHART_AUTOSCROLL,                          // Sort by the "The mode of automatic shift to the right border of the chart" flag
   SORT_BY_CHART_KEYBOARD_CONTROL,                    // Sort by the flag allowing the chart management using a keyboard
   SORT_BY_CHART_QUICK_NAVIGATION,                    // Sort by the flag allowing the chart to intercept Space and Enter key strokes to activate the quick navigation bar
   SORT_BY_CHART_SCALE,                               // Sort by scale
   SORT_BY_CHART_SCALEFIX,                            // Sort by the fixed scale flag
   SORT_BY_CHART_SCALEFIX_11,                         // Sort by the 1:1 scale flag
   SORT_BY_CHART_SCALE_PT_PER_BAR,                    // Sort by the flag of specifying the scale in points per bar
   SORT_BY_CHART_SHOW_TICKER,                         // Sort by the flag displaying a symbol ticker in the upper left corner
   SORT_BY_CHART_SHOW_OHLC,                           // Sort by the flag displaying OHLC values in the upper left corner
   SORT_BY_CHART_SHOW_BID_LINE,                       // Sort by the flag displaying Bid value as a horizontal line on the chart
   SORT_BY_CHART_SHOW_ASK_LINE,                       // Sort by the flag displaying Ask value as a horizontal line on the chart
   SORT_BY_CHART_SHOW_LAST_LINE,                      // Sort by the flag displaying Last value as a horizontal line on the chart
   SORT_BY_CHART_SHOW_PERIOD_SEP,                     // Sort by the flag displaying vertical separators between adjacent periods
   SORT_BY_CHART_SHOW_GRID,                           // Sort by the flag of displaying a grid on the chart
   SORT_BY_CHART_SHOW_VOLUMES,                        // Sort by the mode of displaying volumes on a chart
   SORT_BY_CHART_SHOW_OBJECT_DESCR,                   // Sort by the flag of displaying object text descriptions
   SORT_BY_CHART_VISIBLE_BARS,                        // Sort by the number of bars on a chart that are available for display
   SORT_BY_CHART_WINDOWS_TOTAL,                       // Sort by the total number of chart windows including indicator subwindows
   SORT_BY_CHART_WINDOW_HANDLE,                       // Sort by the chart handle
   SORT_BY_CHART_WINDOW_YDISTANCE,                    // Sort by the distance in Y axis pixels between the upper frame of the indicator subwindow and the upper frame of the chart main window
   SORT_BY_CHART_FIRST_VISIBLE_BAR,                   // Sort by the number of the first visible bar on the chart
   SORT_BY_CHART_WIDTH_IN_BARS,                       // Sort by the width of the chart in bars
   SORT_BY_CHART_WIDTH_IN_PIXELS,                     // Sort by the width of the chart in pixels
   SORT_BY_CHART_HEIGHT_IN_PIXELS,                    // Sort by the height of the chart in pixels
   SORT_BY_CHART_COLOR_BACKGROUND,                    // Sort by the color of the chart background
   SORT_BY_CHART_COLOR_FOREGROUND,                    // Sort by color of axes, scale and OHLC line
   SORT_BY_CHART_COLOR_GRID,                          // Sort by grid color
   SORT_BY_CHART_COLOR_VOLUME,                        // Sort by the color of volumes and position opening levels
   SORT_BY_CHART_COLOR_CHART_UP,                      // Sort by the color for the up bar, shadows and body borders of bull candlesticks
   SORT_BY_CHART_COLOR_CHART_DOWN,                    // Sort by the color of down bar, its shadow and border of body of the bullish candlestick
   SORT_BY_CHART_COLOR_CHART_LINE,                    // Sort by the color of the chart line and the Doji candlesticks
   SORT_BY_CHART_COLOR_CANDLE_BULL,                   // Sort by the color of a bullish candlestick body
   SORT_BY_CHART_COLOR_CANDLE_BEAR,                   // Sort by the color of a bearish candlestick body
   SORT_BY_CHART_COLOR_BID,                           // Sort by the color of the Bid price line
   SORT_BY_CHART_COLOR_ASK,                           // Sort by the color of the Ask price line
   SORT_BY_CHART_COLOR_LAST,                          // Sort by the color of the last performed deal's price line (Last)
   SORT_BY_CHART_COLOR_STOP_LEVEL,                    // Sort by the color of stop order levels (Stop Loss and Take Profit)
   SORT_BY_CHART_SHOW_TRADE_LEVELS,                   // Sort by the flag of displaying trading levels on the chart
   SORT_BY_CHART_DRAG_TRADE_LEVELS,                   // Sort by the flag enabling the ability to drag trading levels on a chart using mouse
   SORT_BY_CHART_SHOW_DATE_SCALE,                     // Sort by the flag of displaying the time scale on the chart
   SORT_BY_CHART_SHOW_PRICE_SCALE,                    // Sort by the flag of displaying the price scale on the chart
   SORT_BY_CHART_SHOW_ONE_CLICK,                      // Sort by the flag of displaying the quick trading panel on the chart
   SORT_BY_CHART_IS_MAXIMIZED,                        // Sort by the "Chart window maximized" flag
   SORT_BY_CHART_IS_MINIMIZED,                        // Sort by the "Chart window minimized" flag
   SORT_BY_CHART_IS_DOCKED,                           // Sort by the "Chart window docked" flag
   SORT_BY_CHART_FLOAT_LEFT,                          // Sort by the left coordinate of the undocked chart window relative to the virtual screen
   SORT_BY_CHART_FLOAT_TOP,                           // Sort by the upper coordinate of the undocked chart window relative to the virtual screen
   SORT_BY_CHART_FLOAT_RIGHT,                         // Sort by the right coordinate of the undocked chart window relative to the virtual screen
   SORT_BY_CHART_FLOAT_BOTTOM,                        // Sort by the bottom coordinate of the undocked chart window relative to the virtual screen
   SORT_BY_CHART_WINDOW_IND_HANDLE,                   // Sort by the indicator handle in the chart window
   SORT_BY_CHART_WINDOW_IND_INDEX,                    // Sort by the indicator index in the chart window
   SORT_BY_CHART_WINDOW_NUM,                          // Sort by chart window index
//--- Sort by real properties
   SORT_BY_CHART_SHIFT_SIZE = FIRST_CHART_DBL_PROP,   // Sort by the shift size of the zero bar from the right border in %
   SORT_BY_CHART_FIXED_POSITION,                      // Sort by the chart fixed position from the left border in %
   SORT_BY_CHART_FIXED_MAX,                           // Sort by the fixed chart maximum
   SORT_BY_CHART_FIXED_MIN,                           // Sort by the fixed chart minimum
   SORT_BY_CHART_POINTS_PER_BAR,                      // Sort by the scale value in points per bar
   SORT_BY_CHART_PRICE_MIN,                           // Sort by the chart minimum
   SORT_BY_CHART_PRICE_MAX,                           // Sort by the chart maximum
//--- Sort by string properties
   SORT_BY_CHART_COMMENT = FIRST_CHART_STR_PROP,      // Sort by a comment text on the chart
   SORT_BY_CHART_EXPERT_NAME,                         // Sort by a name of an EA launched on the chart
   SORT_BY_CHART_SCRIPT_NAME,                         // Sort by a name of a script launched on the chart
   SORT_BY_CHART_WINDOW_IND_NAME,                     // Sort by a name of an indicator launched in the chart window
   SORT_BY_CHART_SYMBOL,                              // Sort by chart symbol
  };
//+------------------------------------------------------------------+


如上所述,我将使用特殊列表来存储已删除指标、窗口和图表的对象副本。 我们将需要访问这些列表里的每个图表对象、其窗口和属于这些窗口的指标。 为了避免从某个已删除对象需要数据时访问在自定义列表里存储的每个对象,在图表对象集合类(该类提供对所有图表对象的访问)中声明所有这些列表,而指向列表的指针会被传递给其他对象(图表、图表窗口、图表窗口所含的指标)。 因此,存储在集合中的每个对象都可以访问所有列表。

在集合对象中使用这些列表强加了一些限制,而不是集合对象本身。 我们不能删除该列表中的对象,当在所有集合对象内部工作时重置列表,除了集合本身,也不能以任何方式修改指向列表的指针。 然而,如果我们牢记使用指向列表的指针的这个特性(实际上是在只读模式下应用它),那么安排从不同对象访问列表就变得简单了,因为将指针传递给列表对象就足以读取其内容。

我们改进图表窗口中的指标对象类文件和图表窗口对象文件(这两个类都位于 \MQL5\Include\DoEasy\Objects\Chart\ChartWnd.mqh)。

在类的私密部分 CWndInd 中,声明用于存储指标所在子窗口索引的变量,而在类的公开部分中,编写设置返回所有对象属性的方法(以前,我们已可以设置单个属性 — 窗口列表中的指标索引):

//+------------------------------------------------------------------+
//| Chart window indicator object class                              |
//+------------------------------------------------------------------+
class CWndInd : public CObject
  {
private:
   long              m_chart_id;                         // Chart ID
   string            m_name;                             // Indicator short name
   int               m_index;                            // indicator index in the list
   int               m_window_num;                       // Indicator subwindow index
   int               m_handle;                           // Indicator handle
public:
//--- Return itself
   CWndInd          *GetObject(void)                     { return &this;               }
//--- Return (1) indicator name, (2) index in the list, (3) indicator handle and (4) subwindow index
   string            Name(void)                    const { return this.m_name;         }
   int               Index(void)                   const { return this.m_index;        }
   int               Handle(void)                  const { return this.m_handle;       }
   int               WindowNum(void)               const { return this.m_window_num;   }
//--- Set (1) subwindow name, (2) window index on the chart, (3) handle, (4) index
   void              SetName(const string name)          { this.m_name=name;           }
   void              SetIndex(const int index)           { this.m_index=index;         }
   void              SetHandle(const int handle)         { this.m_handle=handle;       }
   void              SetWindowNum(const int win_num)     { this.m_window_num=win_num;  }
   
//--- Display the description of object properties in the journal (dash=true - hyphen before the description, false - description only)
   void              Print(const bool dash=false)        { ::Print((dash ? "- " : "")+this.Header());                      }
//--- Return the object short name
   string            Header(void)                  const { return CMessage::Text(MSG_CHART_OBJ_INDICATOR)+" "+this.Name(); }
   
//--- Compare CWndInd objects with each other by the specified property
   virtual int       Compare(const CObject *node,const int mode=0) const;
   
//--- Constructors
                     CWndInd(void){;}
                     CWndInd(const int handle,const string name,const int index,const int win_num) : m_handle(handle),
                                                                                                     m_name(name),
                                                                                                     m_index(index),
                                                                                                     m_window_num(win_num) {}
  };
//+------------------------------------------------------------------+

此外,传递指标所在的图表子窗口索引,并将传递的数值分配给参数型构造函数的相应变量

现在,创建这样的对象时,我们应该额外指定含有所创建对象的指标的子窗口索引。 故此,我们就能够在移除指标和窗口后依然能找到含有指标的窗口。 这将令我们更轻易查找已经不存在的窗口索引,从而了解随窗口被删除的指标原本包含在哪些窗口里,且它们的对象位于已删除图表对象的列表当中。

我们也对图表窗口对象进行修改。 在类的私密部分,声明指向窗口中已变更指标列表的指针并从中删除指标,以及声明存储窗口所属图表品种的变量。 声明从图表对象列表中的窗口返回表示指标存在标志的方法返回列表中存在,但在图表窗口上不存在于的指标对象的方法,以及检查在窗口中存在的指标参数变化的方法

//+------------------------------------------------------------------+
//| Chart window object class                                        |
//+------------------------------------------------------------------+
class CChartWnd : public CBaseObjExt
  {
private:
   CArrayObj         m_list_ind;                                        // Indicator list
   CArrayObj        *m_list_ind_del;                                    // Pointer to the list of indicators removed from the indicator window
   CArrayObj        *m_list_ind_param;                                  // Pointer to the list of changed indicators
   int               m_window_num;                                      // Subwindow index
   int               m_wnd_coord_x;                                     // The X coordinate for the time on the chart in the window
   int               m_wnd_coord_y;                                     // The Y coordinate for the price on the chart in the window
   string            m_symbol;                                          // Symbol of a chart the window belongs to
//--- Return the flag indicating the presence of an indicator (1) from the list in the window and (2) from the window in the list
   bool              IsPresentInWindow(const CWndInd *ind);
   bool              IsPresentInList(const string name);
//--- Return the indicator object present in the list but not present on the chart
   CWndInd          *GetMissingInd(void);
//--- Remove indicators not present in the window from the list
   void              IndicatorsDelete(void);
//--- Add new indicators to the list
   void              IndicatorsAdd(void);
//--- Check the changes of the parameters of existing indicators
   void              IndicatorsChangeCheck(void);
   
public:

现在,该类继承自所有函数库对象的扩展基类,其提供了事件功能,可轻松处理每个这阳的对象。

在类的公开部分,即在返回表示对象支持指定属性标志的方法中,添加另一个需支持的属性 — 图表品种将返回字符串型属性描述的方法实现移到类主体之外 (将来再研究):

public:
//--- Return itself
   CChartWnd        *GetObject(void)                                    { return &this;            }

//--- Return the flag of the object supporting this property
   virtual bool      SupportProperty(ENUM_CHART_PROP_INTEGER property)  { return(property==CHART_PROP_WINDOW_YDISTANCE || property==CHART_PROP_HEIGHT_IN_PIXELS ? true : false); }
   virtual bool      SupportProperty(ENUM_CHART_PROP_DOUBLE property)   { return false; }
   virtual bool      SupportProperty(ENUM_CHART_PROP_STRING property)   { return (property==CHART_PROP_WINDOW_IND_NAME || property==CHART_PROP_SYMBOL ? true : false);  }

//--- Get description of (1) integer, (2) real and (3) string properties
   string            GetPropertyDescription(ENUM_CHART_PROP_INTEGER property);
   string            GetPropertyDescription(ENUM_CHART_PROP_DOUBLE property)  { return CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED);        }
   string            GetPropertyDescription(ENUM_CHART_PROP_STRING property);

在类的公开部分,声明创建图表事件并将其发送到控制程序图表的方法。 类的参数型构造函数现在将接收图表品种名称,和指向从窗口已删除/已变化指标列表的指针。 还有,声明类的析构函数

//--- Return the object short name
   virtual string    Header(void);
   
//--- Create and send the chart event to the control program chart
   void              SendEvent(ENUM_CHART_OBJ_EVENT event);
  
//--- Compare CChartWnd objects by a specified property (to sort the list by an MQL5 signal object)
   virtual int       Compare(const CObject *node,const int mode=0) const;
//--- Compare CChartWnd objects by all properties (to search for equal MQL5 signal objects)
   bool              IsEqual(CChartWnd* compared_obj) const;
   
//--- Constructors
                     CChartWnd(void){;}
                     CChartWnd(const long chart_id,const int wnd_num,const string symbol,CArrayObj *list_ind_del,CArrayObj *list_ind_param);
//--- Destructor
                    ~CChartWnd(void);

接着,添加其他必要的方法,其目的已在清单注释中说明:

//--- Return (1) the subwindow index, (2) the number of indicators attached to the window and (3) the name of a symbol chart
   int               WindowNum(void)                              const { return this.m_window_num;                                       }
   int               IndicatorsTotal(void)                        const { return this.m_list_ind.Total();                                 }
   string            Symbol(void)                                 const { return m_symbol;}
//--- Set (1) the subwindow index and (2) the chart symbol
   void              SetWindowNum(const int num)                        { this.m_window_num=num;                                          }
   void              SetSymbol(const string symbol)                     { this.m_symbol=symbol;                                           }
   
//--- Return (1) the indicator list, the window indicator object from the list by (2) index in the list and (3) by handle
   CArrayObj        *GetIndicatorsList(void)                            { return &this.m_list_ind;                                        }
   CWndInd          *GetIndicatorByIndex(const int index);
   CWndInd          *GetIndicatorByHandle(const int handle);
//--- Return (1) the last one added to the window, (2) the last one removed from the window and (3) the changed indicator
   CWndInd          *GetLastAddedIndicator(void)                        { return this.m_list_ind.At(this.m_list_ind.Total()-1);           }
   CWndInd          *GetLastDeletedIndicator(void)                      { return this.m_list_ind_del.At(this.m_list_ind_del.Total()-1);   }
   CWndInd          *GetLastChangedIndicator(void)                      { return this.m_list_ind_param.At(this.m_list_ind_param.Total()-1);}


我们来详细研究新方法和改进后的方法实现。

在参数型类构造函数的初始化清单中,将传入的参数值赋给 m_symbol,而在类主体中,将指针(传递给方法)的值赋给指向列表的指针变量

//+------------------------------------------------------------------+
//| Parametric constructor                                           |
//+------------------------------------------------------------------+
CChartWnd::CChartWnd(const long chart_id,const int wnd_num,const string symbol,CArrayObj *list_ind_del,CArrayObj *list_ind_param) : m_window_num(wnd_num),
                                                                                                                                    m_symbol(symbol),
                                                                                                                                    m_wnd_coord_x(0),
                                                                                                                                    m_wnd_coord_y(0)
  {
   this.m_list_ind_del=list_ind_del;
   this.m_list_ind_param=list_ind_param;
   CBaseObj::SetChartID(chart_id);
   this.IndicatorsListCreate();
  }
//+------------------------------------------------------------------+

在上一篇文章中(即,为附加在窗口上的指标提供数据的方法中),指标句柄取自指标列表。 读取指标数据后,立即释放句柄。 结果就是,每次都为同一指标创建一个新的句柄(由于我对帮助信息的错误理解 — 指标句柄应该只在确定不再需要时才会释放,即程序操作完成时,而非在句柄接收数据后立即释放,因为指标可能被程序以后再次使用)。 我将在本文中修复它 — 所有指标的句柄都会在类的析构函数中被释放。

类的析构函数:

//+------------------------------------------------------------------+
//| Destructor                                                       |
//+------------------------------------------------------------------+
CChartWnd::~CChartWnd(void)
  {
   int total=this.m_list_ind.Total();
   for(int i=total-1;i>WRONG_VALUE;i--)
     {
      CWndInd *ind=this.m_list_ind.At(i);
      if(ind==NULL)
         continue;
      ::IndicatorRelease(ind.Handle());
      this.m_list_ind.Delete(i);
     }
  }
//+------------------------------------------------------------------+

此处,循环遍历窗口指标对象列表获取下一个对象释放在设置在对象属性中的指标句柄,并移除对象本身

按指定属性比较两个对象的虚拟方法接收进行比较的窗口索引图表品种

//+------------------------------------------------------------------+
//| Compare CChartWnd objects with each other by a specified property|
//+------------------------------------------------------------------+
int CChartWnd::Compare(const CObject *node,const int mode=0) const
  {
   const CChartWnd *obj_compared=node;
   if(mode==CHART_PROP_WINDOW_YDISTANCE)
      return(this.YDistance()>obj_compared.YDistance() ? 1 : this.YDistance()<obj_compared.YDistance() ? -1 : 0);
   else if(mode==CHART_PROP_HEIGHT_IN_PIXELS)
      return(this.HeightInPixels()>obj_compared.HeightInPixels() ? 1 : this.HeightInPixels()<obj_compared.HeightInPixels() ? -1 : 0);
   else if(mode==CHART_PROP_WINDOW_NUM)
      return(this.WindowNum()>obj_compared.WindowNum() ? 1 : this.WindowNum()<obj_compared.WindowNum() ? -1 : 0);
   else if(mode==CHART_PROP_SYMBOL)
      return(this.Symbol()==obj_compared.Symbol() ? 0 : this.Symbol()>obj_compared.Symbol() ? 1 : -1);
   return -1;
  }
//+------------------------------------------------------------------+

在列表中搜索对象和排序所必需参数:图表品种和新实现的对象属性 — 窗口索引。

返回对象字符串型属性描述方法的实现被移到类主体之外:

//+------------------------------------------------------------------+
//| Return description of object's string property                   |
//+------------------------------------------------------------------+
string CChartWnd::GetPropertyDescription(ENUM_CHART_PROP_STRING property)
  {
   return
     (
      property==CHART_PROP_SYMBOL  ?  CMessage::Text(MSG_LIB_PROP_SYMBOL)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+this.Symbol()
         )  :
      ""
     );
  }
//+------------------------------------------------------------------+

就像所有函数库对象的所有类似方法,在此我们创建需传递给方法的属性文本描述:
如果这是 “Chart symbol” 属性则返回其字符串描述若为任何其他属性则返回空字符串
如果属性带有 “property not supported” 标志则返回内容相同的字符串

在为附加到窗口的指标创建列表的方法中删除释放当前选定指标句柄的代码(原因已在上面讨论过):

      //--- get and save the indicator handle by its short name
      int handle=::ChartIndicatorGet(this.m_chart_id,this.m_window_num,name);
      //--- Free the indicator handle
      ::IndicatorRelease(handle);
      //--- Create the new indicator object in the chart window

这段代码,我们在图表窗口中创建一个新指标对象,接收将当前窗口索引,并传递给类的构造函数

//+------------------------------------------------------------------+
//| Create the list of indicators attached to the window             |
//+------------------------------------------------------------------+
void CChartWnd::IndicatorsListCreate(void)
  {
   //--- Clear the indicator lists
   this.m_list_ind.Clear();
   //--- Get the total number of indicators in the window
   int total=::ChartIndicatorsTotal(this.m_chart_id,this.m_window_num);
   //--- In the loop by the number of indicators,
   for(int i=0;i<total;i++)
     {
      //--- obtain and save the short indicator name,
      string name=::ChartIndicatorName(this.m_chart_id,this.m_window_num,i);
      //--- get and save the indicator handle by its short name
      int handle=::ChartIndicatorGet(this.m_chart_id,this.m_window_num,name);
      //--- Create the new indicator object in the chart window
      CWndInd *ind=new CWndInd(handle,name,i,this.WindowNum());
      if(ind==NULL)
         continue;
      //--- set the sorted list flag to the list
      this.m_list_ind.Sort();
      //--- If failed to add the object to the list, remove it
      if(!this.m_list_ind.Add(ind))
         delete ind;
     }
  }
//+------------------------------------------------------------------+

现在图表窗口中的每个指标对象都会“知道”它所在的窗口。

将新指标添加到列表的方法也同样如此完成:

删除代码

      int handle=::ChartIndicatorGet(this.m_chart_id,this.m_window_num,name);
      //--- Free the indicator handle
      ::IndicatorRelease(handle);
      //--- Create the new indicator object in the chart window

并在图表窗口中创建新指标对象时添加传递窗口索引

//+------------------------------------------------------------------+
//| Add new indicators to the list                                   |
//+------------------------------------------------------------------+
void CChartWnd::IndicatorsAdd(void)
  {
   //--- Get the total number of indicators in the window
   int total=::ChartIndicatorsTotal(this.m_chart_id,this.m_window_num);
   //--- In the loop by the number of indicators,
   for(int i=0;i<total;i++)
     {
      //--- obtain and save the short indicator name,
      string name=::ChartIndicatorName(this.m_chart_id,this.m_window_num,i);
      //--- get and save the indicator handle by its short name
      int handle=::ChartIndicatorGet(this.m_chart_id,this.m_window_num,name);
      //--- Create the new indicator object in the chart window
      CWndInd *ind=new CWndInd(handle,name,i,this.WindowNum());
      if(ind==NULL)
         continue;
      //--- set the sorted list flag to the list
      this.m_list_ind.Sort();
      //--- If the object is already in the list or an attempt to add it to the list failed, remove it
      if(this.m_list_ind.Search(ind)>WRONG_VALUE || !this.m_list_ind.Add(ind))
         delete ind;
     }
  }
//+------------------------------------------------------------------+

从窗口列表中返回指标存在标志的方法中也要剔除代码

      int handle=::ChartIndicatorGet(this.m_chart_id,this.m_window_num,name);
      ::IndicatorRelease(handle);

现在,我们也不在此方法中释放指标句柄:

//+--------------------------------------------------------------------------------------+
//| Return the flag indicating the presence of an indicator from the list in the window  |
//+--------------------------------------------------------------------------------------+
bool CChartWnd::IsPresentInWindow(const CWndInd *ind)
  {
   int total=::ChartIndicatorsTotal(this.m_chart_id,this.m_window_num);
   for(int i=0;i<total;i++)
     {
      string name=::ChartIndicatorName(this.m_chart_id,this.m_window_num,i);
      int handle=::ChartIndicatorGet(this.m_chart_id,this.m_window_num,name);
      if(ind.Name()==name && ind.Handle()==handle)
         return true;
     }
   return false;
  }
//+------------------------------------------------------------------+

该方法检查现有指标参数的变化:

//+------------------------------------------------------------------+
//| Check the changes of the parameters of existing indicators       |
//+------------------------------------------------------------------+
void CChartWnd::IndicatorsChangeCheck(void)
  {
   //--- Get the total number of indicators in the window
   int total=::ChartIndicatorsTotal(this.m_chart_id,this.m_window_num);
   //--- In the loop by all window indicators,
   for(int i=0;i<total;i++)
     {
      //--- get the indicator name and get its handle by a name
      string name=::ChartIndicatorName(this.m_chart_id,this.m_window_num,i);
      int handle=::ChartIndicatorGet(this.m_chart_id,this.m_window_num,name);
      //--- If the indicator with such a name is present in the object indicator list, move on to the next one
      if(this.IsPresentInList(name))
         continue;
      //--- Get the indicator object present in the list but not present in the window
      CWndInd *ind=this.GetMissingInd();
      if(ind==NULL)
         continue;
      //--- If the indicator and the detected object have the same index, this is the indicator with changed parameters
      if(ind.Index()==i)
        {
         //--- Create a new indicator object based on the detected indicator object,
         CWndInd *changed=new CWndInd(ind.Handle(),ind.Name(),ind.Index(),ind.WindowNum());
         if(changed==NULL)
            continue;
         //--- set the sorted list flag to the list of changed indicators
         this.m_list_ind_param.Sort();
         //--- If failed to add a newly created indicator object to the list of changed indicators,
         //--- remove the created object and move on to the next indicator in the window
         if(!this.m_list_ind_param.Add(changed))
           {
            delete changed;
            continue;
           }
         //--- Set the new parameters for the detected "lost" indicator - short name and handle
         ind.SetName(name);
         ind.SetHandle(handle);
         //--- and call the method of sending a custom event to the control program chart
         this.SendEvent(CHART_OBJ_EVENT_CHART_WND_IND_CHANGE);
        }
     }
  }
//+------------------------------------------------------------------+

整个方法逻辑在其清单中有完整的描述。 图表窗口中的指标由它们的简称来辨识。 如果一个指标参数有变更,则应更改其短名(正确制作的自定义指标就是这种情况,而标准指标会考虑此功能)。 因此,此处搜索已变更指标是基于这样一个事实:窗口中已变更指标的索引保持不变,但其短名不同了。 如此,如果我们检测到一个指标存在于窗口对象列表中,但不在客户端图表窗口中,我们需要检查索引是否匹配 — 如果指标和检测到的对象具有相同的索引(删除指标时,窗口中其他指标的索引也受影响),那么这个就是我们正在寻找的参数发生变化的指标。

由于从窗口中删除的指标将被存储在一个特殊列表中(当处理事件时,进行后续搜索),我们需要改进从列表中删除窗口中不存在指标的方法:

//+------------------------------------------------------------------+
//| Remove indicators not present in the window from the list        |
//+------------------------------------------------------------------+
void CChartWnd::IndicatorsDelete(void)
  {
   //--- In the loop by the list of window indicator objects,
   int total=this.m_list_ind.Total();
   for(int i=total-1;i>WRONG_VALUE;i--)
     {
      //--- get the next indicator object
      CWndInd *ind=this.m_list_ind.At(i);
      if(ind==NULL)
         continue;
      //--- If such an indicator is present in the chart window, move on to the next object in the list
      if(this.IsPresentInWindow(ind))
         continue;
      //--- Create a copy of a removed indicator
      CWndInd *ind_del=new CWndInd(ind.Handle(),ind.Name(),ind.Index(),ind.WindowNum());
      if(ind_del==NULL)
         continue;
      //--- If failed to place a created object to the list of indicators removed from the window,
      //--- remove it and go to the next object in the list
      if(!this.m_list_ind_del.Add(ind_del))
        {
         delete ind_del;
         continue;
        }
      //--- Remove the indicator, which was deleted from the window, from the list
      this.m_list_ind.Delete(i);
     }
  }
//+------------------------------------------------------------------+

方法逻辑在代码清单中已有详述。 我相信,它不需要特别的解释。 如果您有任何疑问,请随时在下面的评论中提问。

该方法返回列表中窗口里存在指标的标志:

//+-----------------------------------------------------------------------------+
//| Return the flag of the presence of an indicator from the window in the list |
//+-----------------------------------------------------------------------------+
bool CChartWnd::IsPresentInList(const string name)
  {
   CWndInd *ind=new CWndInd();
   if(ind==NULL)
      return false;
   ind.SetName(name);
   this.m_list_ind.Sort(SORT_BY_CHART_WINDOW_IND_NAME);
   int index=this.m_list_ind.Search(ind);
   delete ind;
   return(index>WRONG_VALUE);
  }
//+------------------------------------------------------------------+

该方法允许使用短名称来定义窗口对象列表中相应指标对象的存在。
在此,我们创建一个临时指标对象 ,并把传递给方法的短名称分配给它为指标对象列表设置按指标名称排序标志,并获取列表中匹配该名称的指标的索引。 确保删除临时对象,并返回列表中检测到的指标索引超过 -1 的指示标志(如果找到匹配名称的指标,则其索引肯定超过 -1)。

该方法返回一个存在于列表中,但不存在于图表窗口上的指标对象:

//+------------------------------------------------------------------+
//| Return the indicator object present in the list                  |
//| but not on the chart                                             |
//+------------------------------------------------------------------+
CWndInd *CChartWnd::GetMissingInd(void)
  {
   for(int i=0;i<this.m_list_ind.Total();i++)
     {
      CWndInd *ind=this.m_list_ind.At(i);
      if(!this.IsPresentInWindow(ind))
         return ind;
     }
   return NULL;
  }
//+------------------------------------------------------------------+

此处,循环遍历列表中所有指标对象获取下一个指标对象如果图表上没有此类指标,则返回指向检测到的指标对象的指针否则,返回 NULL

该方法依据图表窗口列表中的指标索引,返回对象列表中的指标对象:

//+------------------------------------------------------------------+
//| Return the indicator object by the index in the window list      |
//+------------------------------------------------------------------+
CWndInd *CChartWnd::GetIndicatorByIndex(const int index)
  {
   CWndInd *ind=new CWndInd();
   if(ind==NULL)
      return NULL;
   ind.SetIndex(index);
   this.m_list_ind.Sort(SORT_BY_CHART_WINDOW_IND_INDEX);
   int n=this.m_list_ind.Search(ind);
   delete ind;
   return this.m_list_ind.At(n);
  }
//+------------------------------------------------------------------+

在此,我们创建一个临时指标对象,并将传递给该方法的索引分配给该它为将图表上的指标对象列表设置按指标索引排序的标志,并获取图表窗口列表与窗口对象列表里都存在的指标索引。 确保 删除临时对象,并返回依据指标对象列表中指定索引处的对象指针
如果未找到对象,则 Search() 方法返回 -1,若在此类索引值的情况下,At() 方法返回 NULL。 故此,如果在图标窗口里指定索引的指标对象在列表中不存在,则该方法返回指向列表中检测到的对象的指针,或 NULL

该方法依据句柄从列表中返回窗口指标对象:

//+------------------------------------------------------------------+
//| Return the window indicator object from the list by handle       |
//+------------------------------------------------------------------+
CWndInd *CChartWnd::GetIndicatorByHandle(const int handle)
  {
   CWndInd *ind=new CWndInd();
   if(ind==NULL)
      return NULL;
   ind.SetHandle(handle);
   this.m_list_ind.Sort(SORT_BY_CHART_WINDOW_IND_HANDLE);
   int index=this.m_list_ind.Search(ind);
   delete ind;
   return this.m_list_ind.At(index);
  }
//+------------------------------------------------------------------+

该方法与上面研究过的方法雷同,除了它接收所需指标对象的句柄值。 故此,列表按 “indicator handle” 属性排序,如此列表搜索则按该对象属性执行。

改进为附加到窗口的指标更新数据的方法,以便在其参数数量或参数本身发生任何变化时将自定义事件发送到控制程序图表:

//+------------------------------------------------------------------+
//| Update data on attached indicators                               |
//+------------------------------------------------------------------+
void CChartWnd::Refresh(void)
  {
   //--- Calculate the change of the indicator number in the "now and during the previous check" window
   int change=::ChartIndicatorsTotal(this.m_chart_id,this.m_window_num)-this.m_list_ind.Total();
   //--- If there is no change in the number of indicators in the window,
   if(change==0)
     {
      //--- check the change of parameters of all indicators and exit
      this.IndicatorsChangeCheck();
      return;
     }
   //--- If indicators are added
   if(change>0)
     {
      //--- Call the method of adding new indicators to the list
      this.IndicatorsAdd();
      //--- In the loop by the number of indicators added to the window,
      for(int i=0;i<change;i++)
        {
         //--- get the new indicator in the list by the index calculated from the end of the list
         int index=this.m_list_ind.Total()-(1+i);
         //--- and if failed to obtain the object, move on to the next one
         CWndInd *ind=this.m_list_ind.At(index);
         if(ind==NULL)
            continue;
         //--- call the method of sending an event to the control program chart
         this.SendEvent(CHART_OBJ_EVENT_CHART_WND_IND_ADD);
        }
     }
   //--- If there are removed indicators
   if(change<0)
     {
      //--- Call the method of removing unnecessary indicators from the list
      this.IndicatorsDelete();
      //--- In the loop by the number of indicators removed from the window,
      for(int i=0;i<-change;i++)
        {
         //--- get a new removed indicator in the list of removed indicators by index calculated from the end of the list
         int index=this.m_list_ind_del.Total()-(1+i);
         //--- and if failed to obtain the object, move on to the next one
         CWndInd *ind=this.m_list_ind_del.At(index);
         if(ind==NULL)
            continue;
         //--- call the method of sending an event to the control program chart
         this.SendEvent(CHART_OBJ_EVENT_CHART_WND_IND_DEL);
        }
     }
  }
//+------------------------------------------------------------------+

方法逻辑已在代码清单中进行了说明。 我应该注意到,在事件处理的当前实现中不需要循环遍历指标列表中新添加(或删除)的指标对象。 现在可以立即调用发送事件的方法。 但是,若我们需要重新编排这个类,从而可以在单次计时器跳动时正确跟踪多个指标的变化,那么可能需要循环搜索新添加到列表中的指标对象。 以程序化方式实现比之手动要容易得多。 目前,我正在实现处理图表上的手动更改。 因此,我现在还不需要这样的循环,尽管它们可能会在未来的开发中有所用处。

该方法创建图表窗口事件,并将其发送到控制程序图表:

//+------------------------------------------------------------------+
//| Create and send a chart window event                             |
//| to the control program chart                                     |
//+------------------------------------------------------------------+
void CChartWnd::SendEvent(ENUM_CHART_OBJ_EVENT event)
  {
   //--- If an indicator is added
   if(event==CHART_OBJ_EVENT_CHART_WND_IND_ADD)
     {
      //--- Get the last indicator object added to the list
      CWndInd *ind=this.GetLastAddedIndicator();
      if(ind==NULL)
         return;
      //--- Send the CHART_OBJ_EVENT_CHART_WND_IND_ADD event to the control program chart
      //--- pass the chart ID to lparam,
      //--- pass the chart window index to dparam,
      //--- pass the short name of the added indicator to sparam
      ::EventChartCustom(this.m_chart_id_main,(ushort)event,this.m_chart_id,this.WindowNum(),ind.Name());
     }
   //--- If the indicator is removed
   else if(event==CHART_OBJ_EVENT_CHART_WND_IND_DEL)
     {
      //--- Get the last indicator object added to the list of removed indicators
      CWndInd *ind=this.GetLastDeletedIndicator();
      if(ind==NULL)
         return;
      //--- Send the CHART_OBJ_EVENT_CHART_WND_IND_DEL event to the control program chart
      //--- pass the chart ID to lparam,
      //--- pass the chart window index to dparam,
      //--- pass the short name of a deleted indicator to sparam
      ::EventChartCustom(this.m_chart_id_main,(ushort)event,this.m_chart_id,this.WindowNum(),ind.Name());
     }
   //--- If the indicator has changed
   else if(event==CHART_OBJ_EVENT_CHART_WND_IND_CHANGE)
     {
      //--- Get the last indicator object added to the list of changed indicators
      CWndInd *ind=this.GetLastChangedIndicator();
      if(ind==NULL)
         return;
      //--- Send the CHART_OBJ_EVENT_CHART_WND_IND_CHANGE event to the control program chart
      //--- pass the chart ID to lparam,
      //--- pass the chart window index to dparam,
      //--- pass the short name of a changed indicator to sparam
      ::EventChartCustom(this.m_chart_id_main,(ushort)event,this.m_chart_id,this.WindowNum(),ind.Name());
     }
  }
//+------------------------------------------------------------------+

整个方法逻辑在其清单中有完整的描述。 如果您有任何疑问,请随时在下面的评论中提问。

现在我们来改进 \MQL5\Include\DoEasy\Objects\Chart\ChartObj.mqh 中的图表对象类。

与图表窗口对象类相似,令该类成为所有函数库对象的扩展对象类的衍生后代,而在类的私密部分,声明指向已删除图表窗口、已删除和已变更指标的指针,以及重新创建图表窗口的方法

//+------------------------------------------------------------------+
//| Chart object class                                               |
//+------------------------------------------------------------------+
class CChartObj : public CBaseObjExt
  {
private:
   CArrayObj         m_list_wnd;                                  // List of chart window objects
   CArrayObj        *m_list_wnd_del;                              // Pointer to the list of chart window objects
   CArrayObj        *m_list_ind_del;                              // Pointer to the list of indicators removed from the indicator window
   CArrayObj        *m_list_ind_param;                            // Pointer to the list of changed indicators
   long              m_long_prop[CHART_PROP_INTEGER_TOTAL];       // Integer properties
   double            m_double_prop[CHART_PROP_DOUBLE_TOTAL];      // Real properties
   string            m_string_prop[CHART_PROP_STRING_TOTAL];      // String properties
   int               m_digits;                                    // Symbol's Digits()
   datetime          m_wnd_time_x;                                // Time for X coordinate on the windowed chart
   double            m_wnd_price_y;                               // Price for Y coordinate on the windowed chart
   
//--- Return the index of the array the (1) double and (2) string properties are actually located at
   int               IndexProp(ENUM_CHART_PROP_DOUBLE property)   const { return(int)property-CHART_PROP_INTEGER_TOTAL;                         }
   int               IndexProp(ENUM_CHART_PROP_STRING property)   const { return(int)property-CHART_PROP_INTEGER_TOTAL-CHART_PROP_DOUBLE_TOTAL; }

//--- The methods of setting parameter flags 
   bool              SetShowFlag(const string source,const bool flag,const bool redraw=false);
   bool              SetBringToTopFlag(const string source,const bool flag,const bool redraw=false);
   bool              SetContextMenuFlag(const string source,const bool flag,const bool redraw=false);
   bool              SetCrosshairToolFlag(const string source,const bool flag,const bool redraw=false);
   bool              SetMouseScrollFlag(const string source,const bool flag,const bool redraw=false);
   bool              SetEventMouseWhellFlag(const string source,const bool flag,const bool redraw=false);
   bool              SetEventMouseMoveFlag(const string source,const bool flag,const bool redraw=false);
   bool              SetEventObjectCreateFlag(const string source,const bool flag,const bool redraw=false);
   bool              SetEventObjectDeleteFlag(const string source,const bool flag,const bool redraw=false);
   bool              SetForegroundFlag(const string source,const bool flag,const bool redraw=false);
   bool              SetShiftFlag(const string source,const bool flag,const bool redraw=false);
   bool              SetAutoscrollFlag(const string source,const bool flag,const bool redraw=false);
   bool              SetKeyboardControlFlag(const string source,const bool flag,const bool redraw=false);
   bool              SetQuickNavigationFlag(const string source,const bool flag,const bool redraw=false);
   bool              SetScaleFixFlag(const string source,const bool flag,const bool redraw=false);
   bool              SetScaleFix11Flag(const string source,const bool flag,const bool redraw=false);
   bool              SetScalePTPerBarFlag(const string source,const bool flag,const bool redraw=false);
   bool              SetShowTickerFlag(const string source,const bool flag,const bool redraw=false);
   bool              SetShowOHLCFlag(const string source,const bool flag,const bool redraw=false);
   bool              SetShowBidLineFlag(const string source,const bool flag,const bool redraw=false);
   bool              SetShowAskLineFlag(const string source,const bool flag,const bool redraw=false);
   bool              SetShowLastLineFlag(const string source,const bool flag,const bool redraw=false);
   bool              SetShowPeriodSeparatorsFlag(const string source,const bool flag,const bool redraw=false);
   bool              SetShowGridFlag(const string source,const bool flag,const bool redraw=false);
   bool              SetShowObjectDescriptionsFlag(const string source,const bool flag,const bool redraw=false);
   bool              SetShowTradeLevelsFlag(const string source,const bool flag,const bool redraw=false);
   bool              SetDragTradeLevelsFlag(const string source,const bool flag,const bool redraw=false);
   bool              SetShowDateScaleFlag(const string source,const bool flag,const bool redraw=false);
   bool              SetShowPriceScaleFlag(const string source,const bool flag,const bool redraw=false);
   bool              SetShowOneClickPanelFlag(const string source,const bool flag,const bool redraw=false);
   bool              SetDockedFlag(const string source,const bool flag,const bool redraw=false);

//--- The methods of setting property values
   bool              SetMode(const string source,const ENUM_CHART_MODE mode,const bool redraw=false);
   bool              SetScale(const string source,const int scale,const bool redraw=false);
   bool              SetModeVolume(const string source,const ENUM_CHART_VOLUME_MODE mode,const bool redraw=false);
   void              SetVisibleBars(void);
   void              SetWindowsTotal(void);
   void              SetFirstVisibleBars(void);
   void              SetWidthInBars(void);
   void              SetWidthInPixels(void);
   void              SetMaximizedFlag(void);
   void              SetMinimizedFlag(void);
   void              SetExpertName(void);
   void              SetScriptName(void);
   
//--- (1) Create, (2) check and re-create the chart window list
   void              CreateWindowsList(void);
   void              RecreateWindowsList(const int change);
//--- Add an extension to the screenshot file if it is missing
   string            FileNameWithExtention(const string filename);
   
public:

在类的公开部分,编写声明操控类事件的新方法。
将指向新列表的指针传递给参数型构造函数:

public:
//--- Set object's (1) integer, (2) real and (3) string properties
   void              SetProperty(ENUM_CHART_PROP_INTEGER property,long value)    { this.m_long_prop[property]=value;                      }
   void              SetProperty(ENUM_CHART_PROP_DOUBLE property,double value)   { this.m_double_prop[this.IndexProp(property)]=value;    }
   void              SetProperty(ENUM_CHART_PROP_STRING property,string value)   { this.m_string_prop[this.IndexProp(property)]=value;    }
//--- Return object’s (1) integer, (2) real and (3) string property from the properties array
   long              GetProperty(ENUM_CHART_PROP_INTEGER property)         const { return this.m_long_prop[property];                     }
   double            GetProperty(ENUM_CHART_PROP_DOUBLE property)          const { return this.m_double_prop[this.IndexProp(property)];   }
   string            GetProperty(ENUM_CHART_PROP_STRING property)          const { return this.m_string_prop[this.IndexProp(property)];   }
//--- Return (1) itself, (2) the window object list and (3) the list of removed window objects
   CChartObj        *GetObject(void)                                             { return &this;               }
   CArrayObj        *GetList(void)                                               { return &this.m_list_wnd;    }

//--- Return the last (1) added (removed) chart window
   CChartWnd        *GetLastAddedWindow(void)                                    { return this.m_list_wnd.At(this.m_list_wnd.Total()-1);           }
   CChartWnd        *GetLastDeletedWindow(void)                                  { return this.m_list_wnd_del.At(this.m_list_wnd_del.Total()-1);   }
   
//--- Return (1) the last one added to the window, (2) the last one removed from the window and (3) the changed indicator,
   CWndInd          *GetLastAddedIndicator(const int win_num);
   CWndInd          *GetLastDeletedIndicator(void)                               { return this.m_list_ind_del.At(this.m_list_ind_del.Total()-1);   }
   CWndInd          *GetLastChangedIndicator(void)                               { return this.m_list_ind_param.At(this.m_list_ind_param.Total()-1);}
//--- Return the indicator by index from the specified chart window
   CWndInd          *GetIndicator(const int win_num,const int ind_index);
   
//--- Return the flag of the object supporting this property
   virtual bool      SupportProperty(ENUM_CHART_PROP_INTEGER property)           { return (property!=CHART_PROP_WINDOW_YDISTANCE ? true : false);  }
   virtual bool      SupportProperty(ENUM_CHART_PROP_DOUBLE property)            { return true; }
   virtual bool      SupportProperty(ENUM_CHART_PROP_STRING property)            { return true; }

//--- Get description of (1) integer, (2) real and (3) string properties
   string            GetPropertyDescription(ENUM_CHART_PROP_INTEGER property);
   string            GetPropertyDescription(ENUM_CHART_PROP_DOUBLE property);
   string            GetPropertyDescription(ENUM_CHART_PROP_STRING property);

//--- Display the description of object properties in the journal (full_prop=true - all properties, false - supported ones only)
   void              Print(const bool full_prop=false);
//--- Display a short description of the object in the journal
   virtual void      PrintShort(const bool dash=false);
//--- Return the object short name
   virtual string    Header(void);
   
//--- Create and send the chart event to the control program chart
   void              SendEvent(ENUM_CHART_OBJ_EVENT event);
  
//--- Compare CChartObj objects by a specified property (to sort the list by a specified chart object property)
   virtual int       Compare(const CObject *node,const int mode=0) const;
//--- Compare CChartObj objects by all properties (to search for equal chart objects)
   bool              IsEqual(CChartObj* compared_obj) const;
   
//--- Update the chart object and its list of indicator windows
   void              Refresh(void);
   
//--- Constructors
                     CChartObj(){;}
                     CChartObj(const long chart_id,CArrayObj *list_wnd_del,CArrayObj *list_ind_del,CArrayObj *list_ind_param);
//+------------------------------------------------------------------+ 

我们来研究新增和改进的类方法。

在参数型构造函数中在存储指向列表对象指针的变量里,接收传递给方法的参数里指向它们的指针

//+------------------------------------------------------------------+
//| Parametric constructor                                           |
//+------------------------------------------------------------------+
CChartObj::CChartObj(const long chart_id,CArrayObj *list_wnd_del,CArrayObj *list_ind_del,CArrayObj *list_ind_param) : m_wnd_time_x(0),m_wnd_price_y(0)
  {
   this.m_list_wnd_del=list_wnd_del;
   this.m_list_ind_del=list_ind_del;
   this.m_list_ind_param=list_ind_param;
//--- Set chart ID to the base object

在构造函数清单的末尾,为为已删除图表对象窗口的列表设置按图表窗口索引排序的标志

   this.m_digits=(int)::SymbolInfoInteger(this.Symbol(),SYMBOL_DIGITS);
   this.m_list_wnd_del.Sort(SORT_BY_CHART_WINDOW_NUM);
   this.CreateWindowsList();
  }
//+------------------------------------------------------------------+

该方法返回添加到窗口的最后一个指标:

//+------------------------------------------------------------------+
//| Return the last indicator added to the window                    |
//+------------------------------------------------------------------+
CWndInd *CChartObj::GetLastAddedIndicator(const int win_num)
  {
   CChartWnd *wnd=this.GetWindowByNum(win_num);
   return(wnd!=NULL ? wnd.GetLastAddedIndicator() : NULL);
  }
//+------------------------------------------------------------------+

该方法接收图表的窗口索引,我们希望据其获取最后添加的指标。 调用 GetWindowByNum() 方法获取所需的图表窗口。 依序,从该图表窗口获取最后添加的指标如果获取图表窗口对象失败,则返回 NULL。 请记住,GetLastAddedIndicator() 图表窗口对象方法也可能返回 NULL

该方法从指定图表窗口按索引返回指标:

//+------------------------------------------------------------------+
//| Return the indicator by index from the specified chart window    |
//+------------------------------------------------------------------+
CWndInd *CChartObj::GetIndicator(const int win_num,const int ind_index)
  {
   CChartWnd *wnd=this.GetWindowByNum(win_num);
   return(wnd!=NULL ? wnd.GetIndicatorByIndex(ind_index) : NULL);
  }
//+------------------------------------------------------------------+

该方法接收图表窗口索引,我们希望从中获取最后添加的指标,以及窗口列表中的指标索引
调用 GetWindowByNum() 方法获取所需的图表窗口 ,并依据其在图表窗口中的索引获取指标如果获取图表窗口对象失败,则返回 NULL。 请记住,GetIndicatorByIndex() 图表窗口对象方法也可能返回 NULL

在更新图表对象及其窗口列表的方法中,将 CreateWindowsList() 方法替换为新方法 RecreateWindowsList()

//+------------------------------------------------------------------+
//| Update the chart object and its window list                      |
//+------------------------------------------------------------------+
void CChartObj::Refresh(void)
  {
   for(int i=0;i<this.m_list_wnd.Total();i++)
     {
      CChartWnd *wnd=this.m_list_wnd.At(i);
      if(wnd==NULL)
         continue;
      wnd.Refresh();
     }
   int change=(int)::ChartGetInteger(this.m_chart_id,CHART_WINDOWS_TOTAL)-this.WindowsTotal();
   if(change==0)
      return;
   this.RecreateWindowsList(change);
  }
//+------------------------------------------------------------------+

创建图表窗口列表的方法 CreateWindowsList() 仅在启动程序时构建窗口。 更新图表对象时将调用重新构建变更窗口的方法(研究如下)。

现在,当创建一个新的图表窗口对象时,我们需要将图表品种名称,和指向列表的指针传递给它。 如此,我们在创建图表窗口列表的方法中实现在新建图表窗口对象时将它们传递给图表窗口对象类构造函数

//+------------------------------------------------------------------+
//| Create the list of chart windows                                 |
//+------------------------------------------------------------------+
void CChartObj::CreateWindowsList(void)
  {
   //--- Clear the chart window list
   this.m_list_wnd.Clear();
   //--- Get the total number of chart windows from the environment
   int total=(int)::ChartGetInteger(this.m_chart_id,CHART_WINDOWS_TOTAL);
   //--- In the loop by the total number of windows
   for(int i=0;i<total;i++)
     {
      //--- Create a new chart window object
      CChartWnd *wnd=new CChartWnd(this.m_chart_id,i,this.Symbol(),this.m_list_ind_del,this.m_list_ind_param);
      if(wnd==NULL)
         continue;
      //--- If the window index exceeds 0 (not the main chart window) and it still has no indicator,
      //--- remove the newly created chart window object and go to the next loop iteration
      if(wnd.WindowNum()!=0 && wnd.IndicatorsTotal()==0)
        {
         delete wnd;
         continue;
        }
      //--- If the object was not added to the list, remove that object
      this.m_list_wnd.Sort();
      if(!this.m_list_wnd.Add(wnd))
         delete wnd;
     }
   //--- If the number of objects in the list corresponds to the number of windows on the chart,
   //--- write that value to the chart object property
   //--- If the number of objects in the list does not correspond to the number of windows on the chart,
   //--- write the number of objects in the list to the chart object property.
   int value=int(this.m_list_wnd.Total()==total ? total : this.m_list_wnd.Total());
   this.SetProperty(CHART_PROP_WINDOWS_TOTAL,value);
  }
//+------------------------------------------------------------------+

该方法检查图表窗口的变化,并重新创建其列表:

//+------------------------------------------------------------------+
//| Check and re-create the chart window list                        |
//+------------------------------------------------------------------+
void CChartObj::RecreateWindowsList(const int change)
  {
//--- If the window is removed
   if(change<0)
     {
      //--- If the chart has only one window, this means we have only the main chart with no subwindows,
      //--- while the change in the number of chart windows indicates the removal of a symbol chart in the terminal.
      //--- This situation is handled in the collection class of chart objects - leave the method
      if(this.WindowsTotal()==1)
         return;
      //--- Get the last removed indicator from the list of removed indicators
      CWndInd *ind=this.m_list_ind_del.At(this.m_list_ind_del.Total()-1);
      //--- If managed to get the indicator,
      if(ind!=NULL)
        {
         //--- create a new chart window object
         CChartWnd *wnd=new CChartWnd();
         if(wnd!=NULL)
           {
            //--- Set the subwindow index from the last removed indicator object,
            //--- ID and the chart object symbol name for a new object
            wnd.SetWindowNum(ind.WindowNum());
            wnd.SetChartID(this.ID());
            wnd.SetSymbol(this.Symbol());
            //--- If failed to add the created object to the list of removed chart window objects, remove it
            if(!this.m_list_wnd_del.Add(wnd))
               delete wnd;
           }
        }
      //--- Call the method of sending an event to the control program chart and re-create the chart window list
      this.SendEvent(CHART_OBJ_EVENT_CHART_WND_DEL);
      this.CreateWindowsList();
      return;
     }
//--- If there are no changes, leave
   else if(change==0)
      return;

//--- If a window is added
   //--- Get the total number of chart windows from the environment
   int total=(int)::ChartGetInteger(this.m_chart_id,CHART_WINDOWS_TOTAL);
   //--- In the loop by the total number of windows
   for(int i=0;i<total;i++)
     {
      //--- Create a new chart window object
      CChartWnd *wnd=new CChartWnd(this.m_chart_id,i,this.Symbol(),this.m_list_ind_del,this.m_list_ind_param);
      if(wnd==NULL)
         continue;
      this.m_list_wnd.Sort(SORT_BY_CHART_WINDOW_NUM);
      //--- If the window index exceeds 0 (not the main chart window) and it still has no indicator,
      //--- or such a window is already present in the list or the window object is not added to the list
      //--- remove the newly created chart window object and go to the next loop iteration
      if((wnd.WindowNum()!=0 && wnd.IndicatorsTotal()==0) || this.m_list_wnd.Search(wnd)>WRONG_VALUE || !this.m_list_wnd.Add(wnd))
        {
         delete wnd;
         continue;
        }
      //--- If added the window, call the method of sending an event to the control program chart
      this.SendEvent(CHART_OBJ_EVENT_CHART_WND_ADD);
     }
   //--- If the number of objects in the list corresponds to the number of windows on the chart,
   //--- write that value to the chart object property
   //--- If the number of objects in the list does not correspond to the number of windows on the chart,
   //--- write the number of objects in the list to the chart object property.
   int value=int(this.m_list_wnd.Total()==total ? total : this.m_list_wnd.Total());
   this.SetProperty(CHART_PROP_WINDOWS_TOTAL,value);
  }
//+------------------------------------------------------------------+

该方法创建图表事件,然后发送给控制程序图表:

//+------------------------------------------------------------------+
//| Create and send a chart event                                    |
//| to the control program chart                                     |
//+------------------------------------------------------------------+
void CChartObj::SendEvent(ENUM_CHART_OBJ_EVENT event)
  {
   //--- If a window is added
   if(event==CHART_OBJ_EVENT_CHART_WND_ADD)
     {
      //--- Get the last chart window object added to the list
      CChartWnd *wnd=this.GetLastAddedWindow();
      if(wnd==NULL)
         return;
      //--- Send the CHART_OBJ_EVENT_CHART_WND_ADD event to the control program chart
      //--- pass the chart ID to lparam,
      //--- pass the chart window index to dparam,
      //--- pass the chart symbol to sparam
      ::EventChartCustom(this.m_chart_id_main,(ushort)event,this.m_chart_id,wnd.WindowNum(),this.Symbol());
     }
   //--- If the window is removed
   else if(event==CHART_OBJ_EVENT_CHART_WND_DEL)
     {
      //--- Get the last chart window object added to the list of removed windows
      CChartWnd *wnd=this.GetLastDeletedWindow();
      if(wnd==NULL)
         return;
      //--- Send the CHART_OBJ_EVENT_CHART_WND_DEL event to the control program chart
      //--- pass the chart ID to lparam,
      //--- pass the chart window index to dparam,
      //--- pass the chart symbol to sparam
      ::EventChartCustom(this.m_chart_id_main,(ushort)event,this.m_chart_id,wnd.WindowNum(),this.Symbol());
     }
  }
//+------------------------------------------------------------------+

后两种方法的整个逻辑在它们的清单中进行了完整的说明,务须解释。

如果您对这些方法有任何疑问,请随时在评论中提出。

改进 \MQL5\Include\DoEasy\Collections\ChartObjCollection.mqh 中的图表对象集合类。

在该类的私密部分声明列表对象,这些对象的指针会被传递到图表窗口,和图表窗口中的指标对象:

//+------------------------------------------------------------------+
//| MQL5 signal object collection                                    |
//+------------------------------------------------------------------+
class CChartObjCollection : public CBaseObj
  {
private:
   CListObj                m_list;                                   // List of chart objects
   CListObj                m_list_del;                               // List of deleted chart objects
   CArrayObj               m_list_wnd_del;                           // List of deleted chart window objects
   CArrayObj               m_list_ind_del;                           // List of indicators removed from the indicator window
   CArrayObj               m_list_ind_param;                         // List of changed indicators
   int                     m_charts_total_prev;                      // Previous number of charts in the terminal
   //--- Return the number of charts in the terminal
   int                     ChartsTotal(void) const;
   //--- Return the flag indicating the existence of (1) a chart object and (2) a chart
   bool                    IsPresentChartObj(const long chart_id);
   bool                    IsPresentChart(const long chart_id);
   //--- Create a new chart object and add it to the list
   bool                    CreateNewChartObj(const long chart_id,const string source);
   //--- Find the missing chart object, create it and add it to the collection list
   bool                    FindAndCreateMissingChartObj(void);
   //--- Find a chart object not present in the terminal and remove it from the list
   void                    FindAndDeleteExcessChartObj(void);
public:

在此我们声明了 CArrayObj 对象,替代指向列表的指针,我们将在其中存储所有已删除的图表对象。

在类的公开部分声明操控图表对象事件所需的新方法

public:
//--- Return (1) itself, (2) chart object collection list, (3) the list of deleted chart objects, 
//--- the list (4) of deleted window objects, (5) deleted and (6) changed indicators
   CChartObjCollection    *GetObject(void)                                 { return &this;                  }
   CArrayObj              *GetList(void)                                   { return &this.m_list;           }
   CArrayObj              *GetListDeletedCharts(void)                      { return &this.m_list_del;       }
   CArrayObj              *GetListDeletedWindows(void)                     { return &this.m_list_wnd_del;   }
   CArrayObj              *GetListDeletedIndicators(void)                  { return &this.m_list_ind_del;   }
   CArrayObj              *GetListChangedIndicators(void)                  { return &this.m_list_ind_param; }
   //--- Return the list by selected (1) integer, (2) real and (3) string properties meeting the compared criterion
   CArrayObj              *GetList(ENUM_CHART_PROP_INTEGER property,long value,ENUM_COMPARER_TYPE mode=EQUAL)  { return CSelect::ByChartProperty(this.GetList(),property,value,mode);  }
   CArrayObj              *GetList(ENUM_CHART_PROP_DOUBLE property,double value,ENUM_COMPARER_TYPE mode=EQUAL) { return CSelect::ByChartProperty(this.GetList(),property,value,mode);  }
   CArrayObj              *GetList(ENUM_CHART_PROP_STRING property,string value,ENUM_COMPARER_TYPE mode=EQUAL) { return CSelect::ByChartProperty(this.GetList(),property,value,mode);  }
//--- Return the number of chart objects in the list
   int                     DataTotal(void)                           const { return this.m_list.Total();    }
//--- Display (1) the complete and (2) short collection description in the journal
   void                    Print(void);
   void                    PrintShort(void);
//--- Constructor
                           CChartObjCollection();

//--- Return the list of chart objects by (1) symbol and (2) timeframe
   CArrayObj              *GetChartsList(const string symbol)              { return this.GetList(CHART_PROP_SYMBOL,symbol,EQUAL);      }
   CArrayObj              *GetChartsList(const ENUM_TIMEFRAMES timeframe)  { return this.GetList(CHART_PROP_TIMEFRAME,timeframe,EQUAL);}
//--- Return the pointer to the chart object (1) by ID and (2) by an index in the list
   CChartObj              *GetChart(const long id);
   CChartObj              *GetChart(const int index)                       { return this.m_list.At(index);                             }
//--- Return (1) the last added chart and (2) the last removed chart
   CChartObj              *GetLastAddedChart(void)                         { return this.m_list.At(this.m_list.Total()-1);             }
   CChartObj              *GetLastDeletedChart(void)                       { return this.m_list_del.At(this.m_list_del.Total()-1);     }
   
//--- Return (1) the last added window on the chart by chart ID and (2) the last removed chart window
   CChartWnd              *GetLastAddedChartWindow(const long chart_id); 
   CChartWnd              *GetLastDeletedChartWindow(void)                 { return this.m_list_wnd_del.At(this.m_list_wnd_del.Total()-1);}
   
//--- Return (1) the last one added to the specified window of the specified chart, (2) the last one removed from the window and (3) the changed indicator
   CWndInd                *GetLastAddedIndicator(const long chart_id,const int win_num);
   CWndInd                *GetLastDeletedIndicator(void)                   { return this.m_list_ind_del.At(this.m_list_ind_del.Total()-1);   }
   CWndInd                *GetLastChangedIndicator(void)                   { return this.m_list_ind_param.At(this.m_list_ind_param.Total()-1);}
//--- Return the indicator by index from the specified window of the specified chart
   CWndInd                *GetIndicator(const long chart_id,const int win_num,const int ind_index);

//--- Return the chart ID with the program
   long                    GetMainChartID(void)                      const { return CBaseObj::GetMainChartID();                        }
//--- Create the collection list of chart objects
   bool                    CreateCollection(void);
//--- Update (1) the chart object collection list and (2) the specified chart object
   void                    Refresh(void);
   void                    Refresh(const long chart_id);

//--- (1) Open a new chart with the specified symbol and period, (2) close the specified chart
   bool                    Open(const string symbol,const ENUM_TIMEFRAMES timeframe);
   bool                    Close(const long chart_id);
   
//--- Create and send the chart event to the control program chart
   void                    SendEvent(ENUM_CHART_OBJ_EVENT event);
  
  };
//+------------------------------------------------------------------+

我们来研究新方法的实现,和现有方法的改进。

在类的构造函数中清除新列表,并为它们设置列表排序标志

//+------------------------------------------------------------------+
//| Constructor                                                      |
//+------------------------------------------------------------------+
CChartObjCollection::CChartObjCollection()
  {
   this.m_list.Clear();
   this.m_list.Sort();
   this.m_list_ind_del.Clear();
   this.m_list_ind_del.Sort();
   this.m_list_ind_param.Clear();
   this.m_list_ind_param.Sort();
   this.m_list_wnd_del.Clear();
   this.m_list_wnd_del.Sort();
   this.m_list_del.Clear();
   this.m_list_del.Sort();
   this.m_list.Type(COLLECTION_CHARTS_ID);
   this.m_charts_total_prev=this.ChartsTotal();
  }
//+------------------------------------------------------------------+

在声明图表对象集合列表的方法中增加调用发送事件至控制程序图表的方法

//+------------------------------------------------------------------+
//| Update the collection list of chart objects                      |
//+------------------------------------------------------------------+
void CChartObjCollection::Refresh(void)
  {
   //--- In the loop by the number of chart objects in the list,
   for(int i=0;i<this.m_list.Total();i++)
     {
      //--- get the next chart object and
      CChartObj *chart=this.m_list.At(i);
      if(chart==NULL)
         continue;
      //--- update it
      chart.Refresh();
     }
   //--- Get the number of open charts in the terminal and
   int charts_total=this.ChartsTotal();
   //--- calculate the difference between the number of open charts in the terminal
   //--- and chart objects in the collection list
   int change=charts_total-this.m_list.Total();
   //--- If there are no changes, leave
   if(change==0)
      return;
   //--- If a chart is added in the terminal
   if(change>0)
     {
      //--- Find the missing chart object, create and add it to the collection list
      this.FindAndCreateMissingChartObj();
      //--- Get the current chart and return to it since
      //--- adding a new chart switches the focus to it
      CChartObj *chart=this.GetChart(GetMainChartID());
      if(chart!=NULL)
         chart.SetBringToTopON(true);
      for(int i=0;i<change;i++)
        {
         chart=m_list.At(m_list.Total()-(1+i));
         if(chart==NULL)
            continue;
         this.SendEvent(CHART_OBJ_EVENT_CHART_OPEN);
        }
     }
   //--- If a chart is removed in the terminal
   else if(change<0)
     {
      //--- Find an extra chart object in the collection list and remove it from the list
      this.FindAndDeleteExcessChartObj();
      for(int i=0;i<-change;i++)
        {
         CChartObj *chart=this.m_list_del.At(this.m_list_del.Total()-(1+i));
         if(chart==NULL)
            continue;
         this.SendEvent(CHART_OBJ_EVENT_CHART_CLOSE);
        }
     }
  }
//+------------------------------------------------------------------+

调用 SendEvent() 方法的代码块,与之前研究的图表窗口对象类的代码块雷同,且在实现时也考虑了未来可能的改进。 不需要循环来处理终端图表中的手动更改 — 我们可以立即调用发送事件的方法。

在创建新图表对象并将其添加到列表的方法中,目前可在创建新图表对象时,可以传递指向新列表的指针。 我们来加入这些修改:

//+------------------------------------------------------------------+
//| Create a new chart object and add it to the list                 |
//+------------------------------------------------------------------+
bool CChartObjCollection::CreateNewChartObj(const long chart_id,const string source)
  {
   ::ResetLastError();
   CChartObj *chart_obj=new CChartObj(chart_id,this.GetListDeletedWindows(),this.GetListDeletedIndicators(),this.GetListChangedIndicators());
   if(chart_obj==NULL)
     {
      CMessage::ToLog(source,MSG_CHART_COLLECTION_ERR_FAILED_CREATE_CHART_OBJ,true);
      return false;
     }
   this.m_list.Sort(SORT_BY_CHART_ID);
   if(!this.m_list.InsertSort(chart_obj))
     {
      CMessage::ToLog(source,MSG_CHART_COLLECTION_ERR_FAILED_ADD_CHART,true);
      delete chart_obj;
      return false;
     }
   return true;
  }
//+------------------------------------------------------------------+

该方法依据图表 ID 将最后添加的窗口返回给图表:

//+------------------------------------------------------------------+
//| Return the last added window to the chart by ID                  |
//+------------------------------------------------------------------+
CChartWnd* CChartObjCollection::GetLastAddedChartWindow(const long chart_id)
  {
   CChartObj *chart=this.GetChart(chart_id);
   if(chart==NULL)
      return NULL;
   CArrayObj *list=chart.GetList();
   return(list!=NULL ? list.At(list.Total()-1) : NULL);
  }
//+------------------------------------------------------------------+

该方法接收我们想要获取的最后添加至窗口的图表 ID
依据传递给方法的 ID 获取图表对象,并提取其窗口列表
最后添加的窗口总是位于列表的末尾返回图表窗口列表末尾的对象,如果失败,则返回 NULL

该方法返回指定图表的指定窗口的最后一个添加指标:

//+------------------------------------------------------------------+
//| Return the last indicator added                                  |
//| to the specified window of the specified chart                   |
//+------------------------------------------------------------------+
CWndInd* CChartObjCollection::GetLastAddedIndicator(const long chart_id,const int win_num)
  {
   CChartObj *chart=this.GetChart(chart_id);
   return(chart!=NULL ? chart.GetLastAddedIndicator(win_num) : NULL);
  }
//+------------------------------------------------------------------+

该方法接收图表 ID ,和我们想要获取的最后添加指标的子窗口编号
依据传递给方法的 ID 获取图表对象,并调用 GetLastAddedIndicator() 图表对象方法返回指向添加到指定图表窗口的最后一个指标的指针。 如果失败,返回 NULL

该方法从指定图表的指定窗口按索引返回指标:

//+------------------------------------------------------------------+
//| Return the indicator by index                                    |
//| from the specified window of the specified chart                 |
//+------------------------------------------------------------------+
CWndInd* CChartObjCollection::GetIndicator(const long chart_id,const int win_num,const int ind_index)
  {
   CChartObj *chart=this.GetChart(chart_id);
   return(chart!=NULL ? chart.GetIndicator(win_num,ind_index) : NULL);
  }
//+------------------------------------------------------------------+

该方法接收我们想要获取的图表 ID子窗口索引、和指标索引
依据传递给方法的 ID 获取图表对象
,并调用 GetIndicator() 图表对象方法从指定的图表窗口返回指标的指针。 如果失败,返回 NULL

从列表中查找并删除终端中不存在的图表对象的方法中添加代码块将检测到的图表对象放置到已删除图表列表之中:

//+-----------------------------------------------------------------------------+
//|Find a chart object not present in the terminal and remove it from the list  |
//+-----------------------------------------------------------------------------+
void CChartObjCollection::FindAndDeleteExcessChartObj(void)
  {
   for(int i=this.m_list.Total()-1;i>WRONG_VALUE;i--)
     {
      CChartObj *chart=this.m_list.At(i);
      if(chart==NULL)
         continue;
      if(!this.IsPresentChart(chart.ID()))
        {
         chart=this.m_list.Detach(i);
         if(chart!=NULL)
           {
            if(!this.m_list_del.Add(chart))
               this.m_list.Delete(i);
           }
        }
     }
  }
//+------------------------------------------------------------------+

在此,如果从列表中获取的图表对象不存在于客户端则调用 Detach() 标准库方法 将对象从列表中删除,并将其添加到已删除图表列表当中如果未能将删除的对象放入新列表请将其删除,从而避免内存泄漏。

该方法创建图表事件,然后发送给控制程序图表:

//+------------------------------------------------------------------+
//| Create and send a chart event                                    |
//| to the control program chart                                     |
//+------------------------------------------------------------------+
void CChartObjCollection::SendEvent(ENUM_CHART_OBJ_EVENT event)
  {
   //--- If a chart is added
   if(event==CHART_OBJ_EVENT_CHART_OPEN)
     {
      //--- Get the last chart object added to the list
      CChartObj *chart=this.GetLastAddedChart();
      if(chart==NULL)
         return;
      //--- Send the CHART_OBJ_EVENT_CHART_OPEN event to the control program chart
      //--- pass the chart ID to lparam,
      //--- pass the chart timeframe to dparam,
      //--- pass the chart symbol to sparam
      ::EventChartCustom(this.m_chart_id_main,(ushort)event,chart.ID(),chart.Timeframe(),chart.Symbol());
     }
   //--- If a chart is removed
   else if(event==CHART_OBJ_EVENT_CHART_CLOSE)
     {
      //--- Get the last chart object added to the list of removed charts
      CChartObj *chart=this.GetLastDeletedChart();
      if(chart==NULL)
         return;
      //--- Send the CHART_OBJ_EVENT_CHART_CLOSE event to the control program chart
      //--- pass the chart ID to lparam,
      //--- pass the chart timeframe to dparam,
      //--- pass the chart symbol to sparam
      ::EventChartCustom(this.m_chart_id_main,(ushort)event,chart.ID(),chart.Timeframe(),chart.Symbol());
     }
  }
//+------------------------------------------------------------------+

方法逻辑已在清单中有所详述。 我相信,它务须解释。


跟踪图表事件

我已创建了一些跟踪图表事件的功能。 现在我们需要为控制程序提供访问权限。 为达此目标,我将使用 CEngine 库主类。 它应该拥有新方法,为我在本文中实现的图表对象集合类方法提供访问。 由于我们已经拥有了上一篇文章中讲述的更新图表对象集合列表的功能,因此在控制程序中跟踪事件的一切均已准备就绪。 我们只需要在测试 EA 里实现处理来自函数库的传入事件(即来自图表对象集合类的事件)。

该函数库还不能跟踪所有图表属性、它们的窗口和指标的变化。 然而,由于所有对象都是函数库基准对象扩展类的衍生后代(自动赋予其后代事件功能),那么我们所要做的就是添加管理对象属性的方法。 我将把它留至下一篇文章。 现在,我们将新功能与“外部世界”联系起来,并测试我在本文中实现的所有内容。

在函数库主对象类的 \MQL5\Include\DoEasy\Engine.mqh 文件中,添加访问图表对象集合新方法的方法

//--- Return the list of chart objects by (1) symbol and (2) timeframe
   CArrayObj           *GetListCharts(const string symbol)                             { return this.m_charts.GetChartsList(symbol);         }
   CArrayObj           *GetListCharts(const ENUM_TIMEFRAMES timeframe)                 { return this.m_charts.GetChartsList(timeframe);      }
//--- Return the list of removed (1) chart objects, (2) chart windows, (3) indicators in the chart window and (4) changed indicators in the chart window
   CArrayObj           *GetListChartsClosed(void)                                      { return this.m_charts.GetListDeletedCharts();        }
   CArrayObj           *GetListChartWindowsDeleted(void)                               { return this.m_charts.GetListDeletedWindows();       }
   CArrayObj           *GetListChartWindowsIndicatorsDeleted(void)                     { return this.m_charts.GetListDeletedIndicators();    }
   CArrayObj           *GetListChartWindowsIndicatorsChanged(void)                     { return this.m_charts.GetListChangedIndicators();    }

//--- Return (1) the specified chart object and (2) the chart object with the program
   CChartObj           *ChartGetChartObj(const long chart_id)                          { return this.m_charts.GetChart(chart_id);            }
   CChartObj           *ChartGetMainChart(void)                                        { return this.m_charts.GetChart(this.m_charts.GetMainChartID());}
//--- Reutrn the chart object of the last (1) open and (2) closed chart
   CChartObj           *ChartGetLastOpenedChart(void)                                  { return this.m_charts.GetLastAddedChart();           }
   CChartObj           *ChartGetLastClosedChart(void)                                  { return this.m_charts.GetLastDeletedChart();         }

//--- Return the object (1) of the last added window of the specified chart and (2) the last removed chart window
   CChartWnd           *ChartGetLastAddedChartWindow(const long chart_id)              { return this.m_charts.GetLastAddedChartWindow(chart_id);}
   CChartWnd           *ChartGetLastDeletedChartWindow(void)                           { return this.m_charts.GetLastDeletedChartWindow();   }
   
//--- Return (1) the last one added to the specified window of the specified chart, (2) the last one removed from the window and (3) the changed indicator
   CWndInd             *ChartGetLastAddedIndicator(const long id,const int win)        { return m_charts.GetLastAddedIndicator(id,win);      }
   CWndInd             *ChartGetLastDeletedIndicator(void)                             { return this.m_charts.GetLastDeletedIndicator();     }
   CWndInd             *ChartGetLastChangedIndicator(void)                             { return this.m_charts.GetLastChangedIndicator();     }
//--- Return the indicator by index from the specified window of the specified chart
   CWndInd             *ChartGetIndicator(const long chart_id,const int win_num,const int ind_index)
                          {
                           return m_charts.GetIndicator(chart_id,win_num,ind_index);
                          }

//--- Return the number of charts in the collection list
   int                  ChartsTotal(void)                                              { return this.m_charts.DataTotal();                   }

所有新添加的方法都会返回调用相应图表对象集合方法的结果。

测试

为了执行测试,我将借用上一篇文章中的 EA,并将其保存在 \MQL5\Experts\TestDoEasy\Part71\ 中,命名为 TestDoEasyPart71.mq5

我们需要做的全部就是将处理新事件的代码添加到 OnDoEasyEvent() 函数库事件处理程序之中。
研究完整的函数代码并无意义。 它很大,且需要把来自不同函数库对象的事件拆分为单独的处理程序。 我稍后会处理这个问题。
现在我们来研究将要添加到 OnDoEasyEvent() EA 函数的代码块

//--- Handling timeseries events
   else if(idx>SERIES_EVENTS_NO_EVENT && idx<SERIES_EVENTS_NEXT_CODE)
     {
      //--- "New bar" event
      if(idx==SERIES_EVENTS_NEW_BAR)
        {
         Print(TextByLanguage("Новый бар на ","New Bar on "),sparam," ",TimeframeDescription((ENUM_TIMEFRAMES)dparam),": ",TimeToString(lparam));
        }
     }
     
//--- Handle chart events
   else if(idx>CHART_OBJ_EVENT_NO_EVENT && idx<CHART_OBJ_EVENTS_NEXT_CODE)
     {
      //--- "New chart opening" event
      if(idx==CHART_OBJ_EVENT_CHART_OPEN)
        {
         //::EventChartCustom(this.m_chart_id_main,(ushort)event,chart.ID(),chart.Timeframe(),chart.Symbol());
         CChartObj *chart=engine.ChartGetLastOpenedChart();
         if(chart!=NULL)
           {
            string symbol=sparam;
            long chart_id=lparam;
            ENUM_TIMEFRAMES timeframe=(ENUM_TIMEFRAMES)dparam;
            string header=symbol+" "+TimeframeDescription(timeframe)+", ID "+(string)chart_id;
            Print(DFUN,CMessage::Text(MSG_CHART_COLLECTION_CHART_OPENED),": ",header);
           }
        }
      //--- "Chart closure" event
      if(idx==CHART_OBJ_EVENT_CHART_CLOSE)
        {
         //::EventChartCustom(this.m_chart_id_main,(ushort)event,chart.ID(),chart.Timeframe(),chart.Symbol());
         CChartObj *chart=engine.ChartGetLastClosedChart();
         if(chart!=NULL)
           {
            string symbol=sparam;
            long   chart_id=lparam;
            ENUM_TIMEFRAMES timeframe=(ENUM_TIMEFRAMES)dparam;
            string header=symbol+" "+TimeframeDescription(timeframe)+", ID "+(string)chart_id;
            Print(DFUN,CMessage::Text(MSG_CHART_COLLECTION_CHART_CLOSED),": ",header);
           }
        }
      //--- "Adding a new window on the chart" event
      if(idx==CHART_OBJ_EVENT_CHART_WND_ADD)
        {
         //::EventChartCustom(this.m_chart_id_main,(ushort)event,this.m_chart_id,wnd.WindowNum(),this.Symbol());
         ENUM_TIMEFRAMES timeframe=WRONG_VALUE;
         string ind_name="";
         string symbol=sparam;
         long   chart_id=lparam;
         int    win_num=(int)dparam;
         string header=symbol+" "+TimeframeDescription(timeframe)+", ID "+(string)chart_id+": ";
         
         CChartObj *chart=engine.ChartGetLastOpenedChart();
         if(chart!=NULL)
           {
            timeframe=chart.Timeframe();
            CChartWnd *wnd=engine.ChartGetLastAddedChartWindow(chart.ID());
            if(wnd!=NULL)
              {
               CWndInd *ind=wnd.GetLastAddedIndicator();
               if(ind!=NULL)
                  ind_name=ind.Name();
              }
           }
         Print(DFUN,header,CMessage::Text(MSG_CHART_OBJ_WINDOW_ADDED)," ",(string)win_num," ",ind_name);
        }
      //--- "Removing a window from the chart" event
      if(idx==CHART_OBJ_EVENT_CHART_WND_DEL)
        {
         //::EventChartCustom(this.m_chart_id_main,(ushort)event,this.m_chart_id,wnd.WindowNum(),this.Symbol());
         CChartWnd *wnd=engine.ChartGetLastDeletedChartWindow();
         ENUM_TIMEFRAMES timeframe=WRONG_VALUE;
         string symbol=sparam;
         long   chart_id=lparam;
         int    win_num=(int)dparam;
         string header=symbol+" "+TimeframeDescription(timeframe)+", ID "+(string)chart_id+": ";
         Print(DFUN,header,CMessage::Text(MSG_CHART_OBJ_WINDOW_REMOVED)," ",(string)win_num);
        }
      //--- "Adding a new indicator to the chart window" event
      if(idx==CHART_OBJ_EVENT_CHART_WND_IND_ADD)
        {
         //::EventChartCustom(this.m_chart_id_main,(ushort)event,this.m_chart_id,this.WindowNum(),ind.Name());
         ENUM_TIMEFRAMES timeframe=WRONG_VALUE;
         string ind_name=sparam;
         string symbol=NULL;
         long   chart_id=lparam;
         int    win_num=(int)dparam;
         string header=NULL;
         
         CWndInd *ind=engine.ChartGetLastAddedIndicator(chart_id,win_num);
         if(ind!=NULL)
           {
            CChartObj *chart=engine.ChartGetChartObj(chart_id);
            if(chart!=NULL)
              {
               symbol=chart.Symbol();
               timeframe=chart.Timeframe();
               CChartWnd *wnd=chart.GetWindowByNum(win_num);
               if(wnd!=NULL)
                  header=wnd.Header();
              }
           }
         Print(DFUN,symbol," ",TimeframeDescription(timeframe),", ID ",chart_id,", ",header,": ",CMessage::Text(MSG_CHART_OBJ_INDICATOR_ADDED)," ",ind_name);
        }
      //--- "Removing an indicator from the chart window" event
      if(idx==CHART_OBJ_EVENT_CHART_WND_IND_DEL)
        {
         //::EventChartCustom(this.m_chart_id_main,(ushort)event,this.m_chart_id,this.WindowNum(),ind.Name());
         ENUM_TIMEFRAMES timeframe=WRONG_VALUE;
         string ind_name=sparam;
         string symbol=NULL;
         long   chart_id=lparam;
         int    win_num=(int)dparam;
         string header=NULL;
         
         CWndInd *ind=engine.ChartGetLastDeletedIndicator();
         if(ind!=NULL)
           {
            CChartObj *chart=engine.ChartGetChartObj(chart_id);
            if(chart!=NULL)
              {
               symbol=chart.Symbol();
               timeframe=chart.Timeframe();
               CChartWnd *wnd=chart.GetWindowByNum(win_num);
               if(wnd!=NULL)
                  header=wnd.Header();
              }
           }
         Print(DFUN,symbol," ",TimeframeDescription(timeframe),", ID ",chart_id,", ",header,": ",CMessage::Text(MSG_CHART_OBJ_INDICATOR_REMOVED)," ",ind_name);
        }
      //--- "Changing indicator parameters in the chart window" event
      if(idx==CHART_OBJ_EVENT_CHART_WND_IND_CHANGE)
        {
         //::EventChartCustom(this.m_chart_id_main,(ushort)event,this.m_chart_id,this.WindowNum(),ind.Name());
         ENUM_TIMEFRAMES timeframe=WRONG_VALUE;
         string ind_name=sparam;
         string symbol=NULL;
         long   chart_id=lparam;
         int    win_num=(int)dparam;
         string header=NULL;
         
         CWndInd *ind=NULL;
         CWndInd *ind_changed=engine.ChartGetLastChangedIndicator();
         if(ind_changed!=NULL)
           {
            ind=engine.ChartGetIndicator(chart_id,win_num,ind_changed.Index());
            if(ind!=NULL)
              {
               CChartObj *chart=engine.ChartGetChartObj(chart_id);
               if(chart!=NULL)
                 {
                  symbol=chart.Symbol();
                  timeframe=chart.Timeframe();
                  CChartWnd *wnd=chart.GetWindowByNum(win_num);
                  if(wnd!=NULL)
                     header=wnd.Header();
                 }
              }
           }
         Print(DFUN,symbol," ",TimeframeDescription(timeframe),", ID ",chart_id,", ",header,": ",CMessage::Text(MSG_CHART_OBJ_INDICATOR_CHANGED)," ",ind_name," >>> ",ind.Name());
        }
     }
     
//--- Handling trading events

来自图表对象集合的每个事件都在注释里有一个从函数库类发送该事件的示例
此示例清晰地展示了我们得到的三个处理程序参数(lparam, dparam sparam)的每个数据类型。 该数据用于在函数库中搜索必要的对象。 在创建于日志中显示的公共消息时,它也用作基础。 我不会在测试 EA 中实现来自图表对象集合其他事件的处理。 这个例子足以理解如何根据自己的需要处理传入的事件。

编译 EA,并在品种图表上启动它。

打开一个新的品种图表 — 从 OnDoEasyEvent() 处理程序中获取以下日志消息:

OnDoEasyEvent: Open chart: AUDNZD H4, ID 131733844391938634

在打开的图表里添加任意振荡器的新窗口 — 从 OnDoEasyEvent() 处理程序获取以下日志消息:

OnDoEasyEvent: AUDNZD H1, ID 131733844391938634: Added subwindow 1 Momentum(14)

将主窗口中绘制的任意指标添加到打开的图表中 — 从 OnDoEasyEvent() 处理程序获取以下日志消息:

OnDoEasyEvent: AUDNZD H4, ID 131733844391938634, Main chart window: Added indicator AMA(14,2,30)

更改振荡器参数 — 从 OnDoEasyEvent() 处理程序获取以下日志消息:

OnDoEasyEvent: AUDNZD H4, ID 131733844391938634, Chart subwindow 1: Changed indicator Momentum(14) >>> Momentum(20)

更改主窗口中的指标参数 — 从 OnDoEasyEvent() 处理程序获取以下日志消息:

OnDoEasyEvent: AUDNZD H4, ID 131733844391938634, Main chart window: Changed indicator AMA(14,2,30) >>> AMA(20,2,30)

删除振荡器窗口 — 从 OnDoEasyEvent() 处理程序中获取以下两个日志消息:

OnDoEasyEvent: AUDNZD H4, ID 131733844391938634: Removed indicator Momentum(20)
OnDoEasyEvent: AUDNZD H1, ID 131733844391938634: Removed subwindow 1

从主窗口中删除指标 — 从 OnDoEasyEvent() 处理程序中获取以下日志消息:

OnDoEasyEvent: AUDNZD H4, ID 131733844391938634, Main chart window: Removed indicator AMA(20,2,30)

关闭之前打开的图表窗口 — 从 OnDoEasyEvent() 处理程序获取以下日志消息:

OnDoEasyEvent: Closed chart: AUDNZD H4, ID 131733844391938634

正如我们所见,所有事件都被正确处理,并发送到控制程序。

下一步是什么?

在下一篇文章中,我将实现自动跟踪变化,并管理所有图表对象的属性。

以下是该函数库当前版本的所有文件,以及 MQL5 的测试 EA 文件,供您测试和下载。
请您在评论中留下问题和建议。

返回内容目录

*该系列的前几篇文章:

DoEasy 函数库中的其他类(第六十七部分):图表对象类
DoEasy 函数库中的其他类(第六十八部分):图表窗口对象类和图表窗口中的指标对象类
DoEasy 函数库中的其他类(第六十九部分):图表对象集合类
DoEasy 函数库中的其他类(第七十部分):扩展功能并自动更新图表对象集合

本文由MetaQuotes Ltd译自俄文
原文地址: https://www.mql5.com/ru/articles/9360

附加的文件 |
MQL5.zip (3962.05 KB)
来自专业程序员的提示(第二部分):在智能交易系统、脚本和外部程序之间存储和交换参数 来自专业程序员的提示(第二部分):在智能交易系统、脚本和外部程序之间存储和交换参数
这些来自专业程序员关于方法、技术和辅助工具的一些提示,可令编程变得更轻松。 我们将讨论终端重启(关闭)后如何恢复参数。 所有示例都是来自我的 Cayman 项目的真实工作代码片段。
DoEasy 库中的其他类(第七十部分):扩展功能并自动更新图表对象集合 DoEasy 库中的其他类(第七十部分):扩展功能并自动更新图表对象集合
在本文中,我将扩展图表对象的功能,并编排图表导航、创建屏幕截图、以及为图表保存和应用模板。 此外,我还将实现图表对象集合、其窗口和其内指标的自动更新。
DoEasy 库中的其他类(第七十二部分):跟踪并记录集合中的图表对象参数 DoEasy 库中的其他类(第七十二部分):跟踪并记录集合中的图表对象参数
在本文中,我将完成图表对象类及其集合的操控。 我还将实现图表属性及其窗口变化的自动跟踪,以及把新参数保存到对象属性。 如此修订允许在未来实现整个图表集合的事件功能。
掉期利率(第一部分):锁定与合成仓位 掉期利率(第一部分):锁定与合成仓位
在本文中,我将尝试扩展掉期利率交易方法的经典概念。 我将解释为什么我会得出这样的结论,即这个概念值得特别关注,绝对推荐研究。