请 [注册] 或 [登录]  | 返回主站

量化交易吧 /  数理科学 帖子:3351378 新帖:65

DoEasy 库中的其他类(第六十八部分):图表窗口对象类和图表窗口中的指标对象类

吃瓜群众小王发表于:6 月 14 日 19:00回复(1)

内容

  • 概述
  • 带有指标对象的图表窗口类
  • 测试
  • 下一步是什么?

概述

在上一篇文章中,我已启动开发图表对象类,为其准备第一个版本。 该对象描述了一个附带所有参数的终端图表。 它允许管理其属性 — 获取设置窗口大小和图表元素显示参数。
在我们的例子中,一个图表也许会有若干个窗口,而这些窗口也许会包含指标。 窗口有自己的大小,而现在我们的图表对象能偶简单地返回位于指定子窗口之上的参数,并管控其高度。 一个子窗口(就像主图表窗口一样)也许包含不同数量的指标。 我们应可想到这能够在访问图表对象时请求位于图表上的所需窗口。 使用所获窗口对象,我们就能够请求其指标列表,获取必要指标的句柄,从而可进一步处理。

今天我将创建两个对象 — 图表窗口中的指标对象,它定义一些指标参数用于识别;还有一个图表窗口对象,它存储其大小和附于其上的指标列表(图表窗口中的指标对象)。 我在上一篇文章中启动开发的图表对象,带有附于其上的窗口对象列表(包括主图表窗口)。

将来,这样的层次结构将极大地促进我们操控众多图表及其包含指标列表的子窗口的能力。 不幸的是,现在创建上一篇文章中宣布的图表对象的集合类还为时过早。 首先,我们需要完成所有主要图表对象的改进。 这就是我即将在此要做的事。


带有指标对象的图表窗口类

首先,我们将所有必要的消息添加到函数库当中。
在 \MQL5\Include\DoEasy\Data.mqh 里,添加新消息的索引

   MSG_CHART_OBJ_CHART_WINDOW,                        // Main chart window
   MSG_CHART_OBJ_CHART_SUBWINDOW,                     // Chart subwindow
   MSG_CHART_OBJ_CHART_SUBWINDOWS_NUM,                // Subwindows
   MSG_CHART_OBJ_INDICATORS_MW_NAME_LIST,             // Indicators in the main chart window
   MSG_CHART_OBJ_INDICATORS_SW_NAME_LIST,             // Indicators in the chart window
   MSG_CHART_OBJ_INDICATOR,                           // Indicator
   MSG_CHART_OBJ_INDICATORS_TOTAL,                    // Indicators
   MSG_CHART_OBJ_WINDOW_N,                            // Window
   MSG_CHART_OBJ_INDICATORS_NONE,                     // No indicators
  
  };
//+------------------------------------------------------------------+

以及与新添加的索引对应的消息文本

   {"Главное окно графика","Main chart window"},
   {"Подокно графика","Chart subwindow"},
   {"Подокон","Subwindows"},
   {"Индикаторы в главном окне графика","Indicators in the main chart window"},
   {"Индикаторы в окне графика","Indicators in the chart window"},
   {"Индикатор","Indicator"},
   {"Индикаторов","Indicators total"},
   {"Окно","Window"},
   {"Отсутствуют","No indicators"},
   
  };
//+---------------------------------------------------------------------+

而在为图表对象属性创建整数型枚举时,我故意跳过了三个固有的属性,不仅是主窗口对象,且涉及所有图表子窗口:

  • 子窗口可见性,
  • 指标子窗口上框与图表主窗口上框之间的 Y 轴像素距离,
  • 以像素为单位的图表高度。

这些是图表窗口对象的主要属性(除了我们即将定义的来自图表对象的子窗口可见性)。 图表窗口列表将出现在每个图表对象里,且每个对象的这些属性都有自己的数值。 每个窗口对象都拥有附于其上窗口的指标列表。 故此,我们需要在整数型和字符串型图表对象属性的枚举中设置额外的常量。

在 \MQL5\Include\DoEasy\Defines.mqh 中,取消先前注释掉的定义,并注释属性 — 这些是图表窗口对象属性,并添加新属性 — 图表窗口中的指标对象

//+------------------------------------------------------------------+
//| 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_IS_VISIBLE,                      // Subwindow visibility
   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
  };
#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
//+------------------------------------------------------------------+
//| Chart real properties                                            |
//+------------------------------------------------------------------+
enum ENUM_CHART_PROP_DOUBLE
  {
   CHART_PROP_SHIFT_SIZE = CHART_PROP_INTEGER_TOTAL,  // Shift size of the zero bar from the right border in %
   CHART_PROP_FIXED_POSITION,                         // Chart fixed position from the left border in %
   CHART_PROP_FIXED_MAX,                              // Chart fixed maximum
   CHART_PROP_FIXED_MIN,                              // Chart fixed minimum
   CHART_PROP_POINTS_PER_BAR,                         // Scale in points per bar
   CHART_PROP_PRICE_MIN,                              // Chart minimum
   CHART_PROP_PRICE_MAX,                              // Chart maximum
  };
#define CHART_PROP_DOUBLE_TOTAL  (7)                  // Total number of real properties
#define CHART_PROP_DOUBLE_SKIP   (0)                  // Number of real properties not used in sorting
//+------------------------------------------------------------------+
//| Chart string properties                                          |
//+------------------------------------------------------------------+
enum ENUM_CHART_PROP_STRING
  {
   CHART_PROP_COMMENT = (CHART_PROP_INTEGER_TOTAL+CHART_PROP_DOUBLE_TOTAL), // Chart comment text
   CHART_PROP_EXPERT_NAME,                            // Name of an EA launched on the chart
   CHART_PROP_SCRIPT_NAME,                            // Name of a script launched on the chart
   CHART_PROP_INDICATOR_NAME,                         // Name of an indicator launched on the chart
   CHART_PROP_SYMBOL,                                 // Chart symbol
  };
#define CHART_PROP_STRING_TOTAL  (5)                  // Total number of string properties
//+------------------------------------------------------------------+

我们用新的常量修改枚举属性的数量 — 整数型属性的数量从 62 增加到 67,而字符串型的数量从 4 增加到 5

在可能的图表对象排序条件枚举中,为新添加的属性加入对应的新条件

//+------------------------------------------------------------------+
//| 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_SHOW = 0,                            // 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_IS_VISIBLE,                   // Sort by the subwindow visibility flag
   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 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_INDICATOR_NAME,                      // Sort by a name of an indicator launched on the chart
   SORT_BY_CHART_SYMBOL,                              // Sort by chart symbol
  };
//+------------------------------------------------------------------+

在第一个版本的图表对象中,条件列表不正确,因为注释掉的整数型图表属性已添加到排序条件中,但没有按品种名称排序。 我已在当前文章中修复了这个问题。

现在我需要创建两个类 — 图表窗口中指标对象的类,和图表窗口对象的类。 我们把它们一次性加入到一个文件之中。

在 \MQL5\Include\DoEasy\Objects\Chart\ 里,创建含 CWndInd (图标窗口内指标) 和 CChartWnd (图标窗口) 类的新文件 ChartWnd.mqh

CWndInd 类应继承自 CObject 标准库的基类,而 CChartWnd 类 — 来自所有 CBaseObj 函数库对象的基准对象。

许多不同的指标均可附加到图表窗口。 图表窗口对象应该知晓它们,从而我们始终能够从图表窗口获取所需指标的句柄,并操控它。 我们不需要太多不同的参数来识别指标。 指标句柄、其简称和指标所附图表窗口的索引就足够了。 故此,图表窗口中的指标对象类将是最简单的一个,并将继承自标准库的基准对象,从而我们能够把所有这些对象添加到指向 CArrayObj 对象指针的列表中,这些指标均位于图表对象里。

我们在新创建的 ChartWnd.mqh 文件中为新的类编写代码:

//+------------------------------------------------------------------+
//|                                                     ChartWnd.mqh |
//|                                  Copyright 2021, MetaQuotes Ltd. |
//|                             https://mql5.com/en/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2021, MetaQuotes Ltd."
#property link      "https://mql5.com/en/users/artmedia70"
#property version   "1.00"
#property strict    // Necessary for mql4
//+------------------------------------------------------------------+
//| Include files                                                    |
//+------------------------------------------------------------------+
#include "..\..\Objects\BaseObj.mqh"
//+------------------------------------------------------------------+
//| 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;                            // Window index on the chart
   int               m_handle;                           // Indicator handle
public:
//--- Return itself
   CWndInd          *GetObject(void)                     { return &this;         }
//--- Return (1) indicator name, (2) window index and (3) indicator handle
   string            Name(void)                    const { return this.m_name;   }
   int               Index(void)                   const { return this.m_index;  }
   int               Handle(void)                  const { return this.m_handle; }
   
//--- 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) : m_handle(handle),m_name(name),m_index(index) {}
  };
//+------------------------------------------------------------------+
//| Compare CWndInd objects with each other by the specified property|
//+------------------------------------------------------------------+
int CWndInd::Compare(const CObject *node,const int mode=0) const
  {
   const CWndInd *obj_compared=node;
   if(mode==CHART_PROP_WINDOW_IND_HANDLE) return(this.Handle()>obj_compared.Handle() ? 1 : this.Handle()<obj_compared.Handle() ? -1 : 0);
   else if(mode==CHART_PROP_WINDOW_IND_INDEX) return(this.Index()>obj_compared.Index() ? 1 : this.Index()<obj_compared.Index() ? -1 : 0);
   return(this.Name()==obj_compared.Name() ? 0 : this.Name()<obj_compared.Name() ? -1 : 1);
  }
//+------------------------------------------------------------------+

这是图表窗口中的整个指标对象类。
私密部分提供了用于存储的类成员变量:

  • 指标窗口所在图表的 ID,
  • 指标短名(在图表窗口中,终端依据其短名识别指标),
  • 指标所在图表窗口的索引(0 — 主图表窗口,1 及以上 — 图表子窗口),
  • 指标句柄。

该数据足以在图表窗口对象中存储附加到窗口的所有指标的列表。 列表本身包含的对象,允许我们查找所需指标,并返回其句柄,可供进一步操作。

返回上述变量数值的公开方法是不言自明的。 我们来研究一些其他的类方法。

返回指标对象短名的方法:

//--- Return the object short name
   string            Header(void)                  const { return CMessage::Text(MSG_CHART_OBJ_INDICATOR)+" "+this.Name(); }

简单地返回指标标题 + 指标短名。

在日志中显示指标对象属性描述的方法:

//--- 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());                      }

由于图表窗口中也许有若干个指标,它们将以带有标题的列表形式显示。 为了令日志中的列表更具视觉吸引力,我将在指标名称前使用连字符。 方法输入代表是否必须显示连字符。

我们有两个构造函数:默认和参数型构造函数。 默认构造函数也许有助于在窗口中创建“空虚”指标对象,而当图表窗口对象类中创建指标列表时,参数型构造函数作为主要的类构造函数。

参数型构造函数接收指标句柄、其短名、和指标所在子窗口的索引

//--- Constructors
                     CWndInd(void);
                     CWndInd(const int handle,const string name,const int index) : m_handle(handle),m_name(name),m_index(index) {}

传递给该方法的所有参数值都会立即分配到其初始化列表中的类变量

依据指定属性比较图表窗口指标对象的方法:

//+------------------------------------------------------------------+
//| Compare CWndInd objects with each other by the specified property|
//+------------------------------------------------------------------+
int CWndInd::Compare(const CObject *node,const int mode=0) const
  {
   const CWndInd *obj_compared=node;
   if(mode==CHART_PROP_WINDOW_IND_HANDLE) return(this.Handle()>obj_compared.Handle() ? 1 : this.Handle()<obj_compared.Handle() ? -1 : 0);
   else if(mode==CHART_PROP_WINDOW_IND_INDEX) return(this.Index()>obj_compared.Index() ? 1 : this.Index()<obj_compared.Index() ? -1 : 0);
   return(this.Name()==obj_compared.Name() ? 0 : this.Name()<obj_compared.Name() ? -1 : 1);
  }
//+------------------------------------------------------------------+

我们没有针对此对象自定义属性枚举。 其所有属性都出现在图表对象属性的枚举当中。 因此,该方法也许能接收任何图表对象属性。 不过,仅当模式 收到的属性为“指标句柄”,或“图表窗口索引”时,才与所传递的属性执行比较。 在任何其他情况下,依据指标短名执行比较

比较方法是针对所有函数库对象的标准方法:如果当前对象的参数值超过比较对象的值,则返回 1。 如果小于,则返回 -1,否则返回 0。

我们来启动开发图表窗口对象类。

我会继续开发代码,并在包含图表窗口中指标对象类的文件 (ChartWnd.mqh) 里编写图表窗口对象类。 该类应该派生自所有 CBaseObj 库对象的基准对象

//+------------------------------------------------------------------+
//| Chart window object class                                        |
//+------------------------------------------------------------------+
class CChartWnd : public CBaseObj
  {
  }

类的私密部分将包含该窗口中指标对象的指针列表、对象描述的子窗口索引,以及安置类操作的附加方法

//+------------------------------------------------------------------+
//| Chart window object class                                        |
//+------------------------------------------------------------------+
class CChartWnd : public CBaseObj
  {
private:
   CArrayObj         m_list_ind;                                        // Indicator list
   int               m_window_num;                                      // Subwindow index
//--- Return the flag indicating the presence of an indicator from the list in the window
   bool              IsPresentInWindow(const CWndInd *ind);
//--- Remove indicators not present in the window from the list
   void              IndicatorsDelete(void);
//--- Add new indicators to the list
   void              IndicatorsAdd(void);
//--- Set a subwindow index
   void              SetWindowNum(const int num)                        { this.m_window_num=num;   }
public:

类的公开部分提供了标准方法(设置属性值的方法除外 — 因为对象没有自定义属性枚举列表,所以我们只返回那些属于图表窗口的图表对象属性的所需值)。 这些方法在之前的文章中已被多次探讨过。
此外,该类还提供了设置和返回窗口属性值的方法,以及操控该类的方法。 我将在下面更细致地研究。

//+------------------------------------------------------------------+
//| Chart window object class                                        |
//+------------------------------------------------------------------+
class CChartWnd : public CBaseObj
  {
private:
   CArrayObj         m_list_ind;                                        // Indicator list
   int               m_window_num;                                      // Subwindow index
//--- Return the flag indicating the presence of an indicator from the list in the window
   bool              IsPresentInWindow(const CWndInd *ind);
//--- Remove indicators not present in the window from the list
   void              IndicatorsDelete(void);
//--- Add new indicators to the list
   void              IndicatorsAdd(void);
//--- Set a subwindow index
   void              SetWindowNum(const int num)                        { this.m_window_num=num;   }
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_INDICATOR_NAME ? 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 CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED);  }

//--- 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);
   
//--- 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);

//--- Return the distance in pixels between the window borders
   int               YDistance(void)                              const { return (int)::ChartGetInteger(this.m_chart_id,CHART_WINDOW_YDISTANCE,this.m_window_num);}
//--- (1) Return and (2) set the window height in pixels
   int               HeightInPixels(void)                         const { return (int)::ChartGetInteger(this.m_chart_id,CHART_HEIGHT_IN_PIXELS,this.m_window_num);}
   bool              SetHeightInPixels(const int value,const bool redraw=false);
//--- Return (1) the subwindow index and (2) the number of indicators attached to the window
   int               WindowNum(void)                              const { return this.m_window_num;}
   int               IndicatorsTotal(void)                        const { return this.m_list_ind.Total();   }
   
//--- Return (1) the indicator list and (2) the window indicator object from the list by index
   CArrayObj        *GetIndicatorsList(void)                            { return &this.m_list_ind;          }
   CWndInd          *GetIndicator(const int index)                      { return this.m_list_ind.At(index); }
   
//--- Display the description of indicators attached to the chart window in the journal
   void              PrintIndicators(const bool dash=false);
//--- Display the description of the window parameters in the journal
   void              PrintParameters(const bool dash=false);
   
//--- Create the list of indicators attached to the window
   void              IndicatorsListCreate(void);
//--- Update data on attached indicators
   void              Refresh(void);
   
  };
//+------------------------------------------------------------------+

该方法返回窗口边框之间的距离(以像素为单位):

//--- Return the distance in pixels between the window borders
   int YDistance(void) const { return (int)::ChartGetInteger(this.m_chart_id,CHART_WINDOW_YDISTANCE,this.m_window_num);}

由于 CHART_WINDOW_YDISTANCE 图表属性是只读的,故此处没有为其设置数值的方法。 该方法简单地返回特定图表子窗口的属性值。

该方法返回以像素为单位的窗口高度:

int HeightInPixels(void) const { return (int)::ChartGetInteger(this.m_chart_id,CHART_HEIGHT_IN_PIXELS,this.m_window_num);}

该方法的操作方式与上述方法类似,并返回 m_window_num 变量中指定窗口索引处的属性值。

该方法以像素为单位设置窗口高度 (在类主体中声明,但在类主体外部实现):

//+------------------------------------------------------------------+
//| Set the window height in pixels                                  |
//+------------------------------------------------------------------+
bool CChartWnd::SetHeightInPixels(const int value,const bool redraw=false)
  {
   ::ResetLastError();
   if(!::ChartSetInteger(this.m_chart_id,CHART_HEIGHT_IN_PIXELS,this.m_window_num,value))
     {
      CMessage::ToLog(DFUN,::GetLastError(),true);
      return false;
     }
   if(redraw)
      ::ChartRedraw(this.m_chart_id);
   return true;
  }
//+------------------------------------------------------------------+

该方法类似于上一篇文章中研讨过的设置图表对象属性值的方法。 该方法接收设置所需的数值,然后尝试调用 ChartSetInteger() 函数将其分配给窗口。 如果图表变更事件并未排队,则发通知并返回 false。 如果事件成功排队,则激活重绘标志,初步重绘图表并返回 true。 强制图表重绘是必要的,从而避免等待任何图表事件(报价到达、大小更改、鼠标点击等)即可刷新显示,其可替代重新绘制图表与查看结果。

依据指定属性比较图表窗口对象的方法:

//+------------------------------------------------------------------+
//| 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);
   return -1;
  }
//+------------------------------------------------------------------+

就像上面研讨的图表窗口指标类中的比较方法一样,我们只比较图表对象属性枚举中指定的一些属性:

  • 指标子窗口上框与主图表窗口上框之间的 Y 轴像素距离,
  • 图表高度以像素为单位,
  • 在所有其他情况下,返回 -1

该方法依据所有属性比较图表窗口对象

//+------------------------------------------------------------------+
//| Compare the CChartWnd objects by all properties                  |
//+------------------------------------------------------------------+
bool CChartWnd::IsEqual(CChartWnd *compared_obj) const
  {
   return(this.YDistance()!=compared_obj.YDistance() || this.HeightInPixels()!=compared_obj.HeightInPixels() ? false : true);
  }
//+------------------------------------------------------------------+

在此,如果所比较对象的两个属性中至少有一个返回不等,则返回 false — 对象不相等。 否则,返回 true — 对象相同。

该方法返回整数型属性描述:

//+------------------------------------------------------------------+
//| Return description of object's integer property                  |
//+------------------------------------------------------------------+
string CChartWnd::GetPropertyDescription(ENUM_CHART_PROP_INTEGER property)
  {
   return
     (
      property==CHART_PROP_WINDOW_YDISTANCE  ?  CMessage::Text(MSG_CHART_OBJ_WINDOW_YDISTANCE)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)this.YDistance()
         )  :
      property==CHART_PROP_HEIGHT_IN_PIXELS  ?  CMessage::Text(MSG_CHART_OBJ_HEIGHT_IN_PIXELS)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)this.HeightInPixels()
         )  :
      ""
     );
  }
//+------------------------------------------------------------------+

根据传递给方法的图表窗口对象整数型属性,创建并返回含有描述的字符串。

在日志中显示对象属性的方法:

//+------------------------------------------------------------------+
//| Display object properties in the journal                         |
//+------------------------------------------------------------------+
void CChartWnd::Print(const bool full_prop=false)
  {
   ::Print("============= ",CMessage::Text(MSG_LIB_PARAMS_LIST_BEG)," (",this.Header(),") =============");
   int beg=0, end=CHART_PROP_INTEGER_TOTAL;
   for(int i=beg; i<end; i++)
     {
      ENUM_CHART_PROP_INTEGER prop=(ENUM_CHART_PROP_INTEGER)i;
      if(prop!=CHART_PROP_WINDOW_YDISTANCE && prop!=CHART_PROP_HEIGHT_IN_PIXELS) continue;
      if(!full_prop && !this.SupportProperty(prop)) continue;
      ::Print(this.GetPropertyDescription(prop));
     }
   ::Print("------");
   beg=end; end+=CHART_PROP_DOUBLE_TOTAL;
   for(int i=beg; i<end; i++)
     {
      //ENUM_CHART_PROP_DOUBLE prop=(ENUM_CHART_PROP_DOUBLE)i;
      //if(!full_prop && !this.SupportProperty(prop)) continue;
      //::Print(this.GetPropertyDescription(prop));
     }
   beg=end; end+=CHART_PROP_STRING_TOTAL;
   for(int i=beg; i<end; i++)
     {
      ENUM_CHART_PROP_STRING prop=(ENUM_CHART_PROP_STRING)i;
      if(prop==CHART_PROP_INDICATOR_NAME)
        {
         this.PrintIndicators();
         continue;
        }
      if(!full_prop && !this.SupportProperty(prop)) continue;
      ::Print(this.GetPropertyDescription(prop));
     }
   ::Print("============= ",CMessage::Text(MSG_LIB_PARAMS_LIST_END)," (",this.Header(),") =============\n");
  }
//+------------------------------------------------------------------+

遍历所有图表对象属性的三重循环中,接收下一个属性,并在日志中显示其描述。 由于图表窗口对象不含有其属性的自定义枚举列表,我们需要只显示图表窗口对象中固有的图表对象属性 — 整数型和字符串型。 该对象没有实际型属性,这意味着它们无法显示。 我已为每个循环的开始和结束设置了一个严格的框架...... 但这样的解决方案对于后续修改图表对象属性并非最佳,因为我不得不回到对象,并编辑每个循环的开始和结束值。 因此,我简单地针对实数型属性进行空循环(也许这只是暂时的,直到将来图表窗口对象需要实数型属性为止)。 如此,在属性数量发生任何变化时,每个循环的开始和结束始终是正确的。

该方法返回图表窗口对象的简述:

//+------------------------------------------------------------------+
//| Return the object short name                                     |
//+------------------------------------------------------------------+
string CChartWnd::Header(void)
  {
   return(this.m_window_num==0 ? CMessage::Text(MSG_CHART_OBJ_CHART_WINDOW) : (string)this.WindowNum()+" "+CMessage::Text(MSG_CHART_OBJ_CHART_SUBWINDOW));
  }
//+------------------------------------------------------------------+

该方法检查子窗口索引。 如果这是主图表窗口(索引 0),则返回“主图表窗口”字符串。 如果这是主图表子窗口,则返回子窗口索引 + “图表子窗口”字符串。

在日志中显示图表窗口对象描述的方法:

//+------------------------------------------------------------------+
//| Display a short description of the object in the journal         |
//+------------------------------------------------------------------+
void CChartWnd::PrintShort(const bool dash=false)
  {
   ::Print((dash ? "- " : ""),this.Header()," ID: ",(string)this.GetChartID(),", ",CMessage::Text(MSG_CHART_OBJ_INDICATORS_TOTAL),": ",this.IndicatorsTotal());
  }
//+------------------------------------------------------------------+

在此,我们创建了一个字符串,其中包含一个简短的对象名称、图表 ID,和窗口附加的指标数量。 如果该方法收到指示在对象描述(破折号)之前需显示连字符的标志,则在创建的字符串之前显示连字符。

该方法在日志中显示附加到窗口的所有指标的描述:

//+------------------------------------------------------------------+
//| Display the description of indicators attached to the window     |
//+------------------------------------------------------------------+
void CChartWnd::PrintIndicators(const bool dash=false)
  {
   string header=
     (
      this.WindowNum()==0 ? CMessage::Text(MSG_CHART_OBJ_INDICATORS_MW_NAME_LIST) : 
      CMessage::Text(MSG_CHART_OBJ_INDICATORS_SW_NAME_LIST)+" "+(string)this.WindowNum()
     );
   ::Print(header,":");
   int total=this.IndicatorsTotal();
   if(total==0)
      ::Print("- ",CMessage::Text(MSG_CHART_OBJ_INDICATORS_NONE));
   else for(int i=0;i<total;i++)
     {
      CWndInd *ind=this.m_list_ind.At(i);
      if(ind==NULL)
         continue;
      ind.Print(dash);
     }
  }
//+------------------------------------------------------------------+

首先,创建一个与窗口匹配的标题,并显示在日志中
如果这是主图表窗口,标题文本应该是 “Indicators in the main chart window”,否则标题文本将是 “Indicators in the chart window” + 窗口索引。
接下来,定义附加到窗口的指标数量如果没有指标,则显示 “No indicators”
否则,遍历所有指标列表的循环中,获取图表窗口中的下一个指标对象,并打印其数据

在日志中显示窗口参数描述的方法:

//+------------------------------------------------------------------+
//| Display the description of the window parameters in the journal  |
//+------------------------------------------------------------------+
void CChartWnd::PrintParameters(const bool dash=false)
  {
   string header=
     (
      this.WindowNum()==0 ? CMessage::Text(MSG_CHART_OBJ_CHART_WINDOW) : 
      CMessage::Text(MSG_CHART_OBJ_CHART_SUBWINDOW)+" "+(string)this.WindowNum()
     );
   ::Print((dash ? " " : ""),header,":");
   if(this.WindowNum()>0)
      ::Print((dash ? " - " : ""),GetPropertyDescription(CHART_PROP_WINDOW_YDISTANCE));
   ::Print((dash ? " - " : ""),GetPropertyDescription(CHART_PROP_HEIGHT_IN_PIXELS));
  }
//+------------------------------------------------------------------+

首先,创建一个与窗口匹配的标题,并显示在日志中
如果这是主图表窗口,标题文本应该是 “Main chart window”,否则,标题文本将是 “Chart subwindow” + 窗口索引。
如果这是一个图表子窗口(其索引超过零),则显示指标子窗口上边框和主图表窗口上边框之间的垂直(Y 轴)像素距离(对于主窗口,它始终为 0,且不会显示)。 接下来,在日志中显示第二个对象属性(图表的像素高度)

为附加到窗口的指标创建列表的方法:

//+------------------------------------------------------------------+
//| Create the list of indicators attached to the window             |
//+------------------------------------------------------------------+
void CChartWnd::IndicatorsListCreate(void)
  {
   //--- Clear the list of indicators
   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);
      //--- Free the indicator handle
      ::IndicatorRelease(handle);
      //--- Create the new indicator object in the chart window
      CWndInd *ind=new CWndInd(handle,name,i);
      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;
     }
  }
//+------------------------------------------------------------------+

该方法已在代码注释中详述。 当获取窗口附加指标列表时,我们依据其短名调用 ChartIndicatorGet() 获取指标句柄,如此给我们强加了一些“责任”。 终端会跟踪每个指标的使用情况。 每收到一个句柄,正在使用指标的内部计数器就会增加。 如果我们程序中已经不需要某些指标,但我们未释放其句柄,那么以后就不可能再捕捉到“丢失”的句柄。 因此,我在获得所有必要的指标数据后立即释放句柄,从而减少指标占用内部计数器。

该方法往列表中添加新指标:

//+------------------------------------------------------------------+
//| 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);
      //--- Release the indicator handle
      ::IndicatorRelease(handle);
      //--- Create the new indicator object in the chart window
      CWndInd *ind=new CWndInd(handle,name,i);
      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;
     }
  }
//+------------------------------------------------------------------+

方法逻辑与上面研讨的雷同。 代码中也有注释。 唯一的区别是该列表最初并未在方法中清理。 当往列表中添加一个指标时,首先检查列表中是否存在这个指标如果已经存在,则指标对象将被删除

为了能够同步窗口中的指标列表与其实际数量(毕竟,指标可以添加到窗口中,也可以从图表窗口中删除),我们需要将它们在窗口中的数量与列表中的数量进行比较。 我们将在后续文章中完成这一点。
不过,我于此将实现从终端窗口列表中返回指标存在的方法

//+--------------------------------------------------------------------------------------+
//| 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);
      ::IndicatorRelease(handle);
      if(ind.Name()==name && ind.Handle()==handle)
         return true;
     }
   return false;
  }
//+------------------------------------------------------------------+

该方法接收图表窗口中指标对象的指针,并应检查其是否实际存在。 接下来,在按图表窗口中指标总数的循环中获取下一个指标的名称获取其句柄,并立即释放如果当前指标的短名称和句柄与检查对象的短名称和句柄匹配,则返回 true。 该指标仍存在于图表窗口之中直至循环完毕,返回 false — 未找到匹配项,这意味着图表窗口中没有这个指标。

如果列表里有,而图表上没有的指标,则应将其从列表中删除。
该方法从列表中删除窗口中不存在的指标:

//+------------------------------------------------------------------+
//| Remove indicators not present in the window from the list        |
//+------------------------------------------------------------------+
void CChartWnd::IndicatorsDelete(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(!this.IsPresentInWindow(ind))
         this.m_list_ind.Delete(i);
     }
  }
//+------------------------------------------------------------------+

在此,遍历指标对象列表的循环中按循环索引获取下一个指标对象,并检查其在实际图表窗口中是否存在。 如果它不存在,则使用 Delete() 方法从列表中删除指向它的指针

稍后,在实现图表集合类时,我们将需要跟踪所有图表对象的子窗口中运行的指标状态。 如果图表子窗口中实际指标数量与其在图表窗口对象列表中的描述不匹配,我们需要从列表中删除不存在的指标,或在缺失时添加新指标。

为了实现这一点,我们创建更新图表窗口附加指标数据的方法:

//+------------------------------------------------------------------+
//| Update data on attached indicators                               |
//+------------------------------------------------------------------+
void CChartWnd::Refresh(void)
  {
   this.IndicatorsDelete();
   this.IndicatorsAdd();
  }
//+------------------------------------------------------------------+

在此,我们首先检测实际图表窗口中不存在的指标,并将它们从列表中删除。 接下来,我们调用上述方法搜索并添加出现在窗口中,但在列表中不曾出现的新指标

最后,我们来研讨 参数型类构造函数:

//+------------------------------------------------------------------+
//| Parametric constructor                                           |
//+------------------------------------------------------------------+
CChartWnd::CChartWnd(const long chart_id,const int wnd_num) : m_window_num(wnd_num)
  {
   CBaseObj::SetChartID(chart_id);
   this.IndicatorsListCreate();
  }
//+------------------------------------------------------------------+

在此,我们首先在 CBaseObj 父对象中设置图表 ID。 然后我们创建图表窗口附加的指标列表图表子窗口索引在构造函数初始化列表中设置

图表窗口与图表窗口对象中附加指标对象类的创建至此完毕。
可在下面附带的文件里查看位于同一个文件中的两个类的完整清单。

由于我们现在有了描述图表窗口及其子窗口的对象,是时候改进 \MQL5\Include\DoEasy\Objects\Chart\ChartObj.mqh 中的 CChartObj 图表对象类了。 现在它将拥有、其主窗口中附加的所有子窗口的列表。 为了获取窗口属性,我们需要引用指向上面创建的所需图表窗口对象的指针。 从获取的 window 对象中,我们可以获取附加到其上的所有指标的列表。 反之,从这些指标中,我们可以接收一个所需指标的句柄来操控它。

首先,我们在图表对象文件里包含窗口中的窗口对象和附加于窗口之上的指标文件,并声明列表对象,我们将在其中存储指向所有图表对象窗口的指针

//+------------------------------------------------------------------+
//|                                                     ChartObj.mqh |
//|                                  Copyright 2021, MetaQuotes Ltd. |
//|                             https://mql5.com/en/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2021, MetaQuotes Ltd."
#property link      "https://mql5.com/en/users/artmedia70"
#property version   "1.00"
#property strict    // Necessary for mql4
//+------------------------------------------------------------------+
//| Include files                                                    |
//+------------------------------------------------------------------+
#include "..\..\Objects\BaseObj.mqh"
#include "ChartWnd.mqh"
//+------------------------------------------------------------------+
//| Chart object class                                               |
//+------------------------------------------------------------------+
class CChartObj : public CBaseObj
  {
private:
   CArrayObj         m_list_wnd;                                  // List of chart window objects
   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()

在类的私密部分设置属性值的所有方法清单中,添加设置图表窗口可见性的方法

//--- 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              SetVisible(void);
   void              SetFirstVisibleBars(void);
   void              SetWidthInBars(void);
   void              SetWidthInPixels(void);
   void              SetMaximizedFlag(void);
   void              SetMinimizedFlag(void);
   void              SetExpertName(void);
   void              SetScriptName(void);

public:

由于“指标子窗口上框与图表主窗口上框之间的 Y 轴像素距离”属性不适用于主窗口(那里始终为 0),我们来更改返回值,预防出现 CHART_PROP_WINDOW_YDISTANCE 属性这种情况,方法返回对象支持整数型属性的标志:

//--- 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; }

如果传递给方法的属性不等于 CHART_PROP_WINDOW_YDISTANCE,则返回 true否则 – false

在简化访问对象属性的方法清单中,添加返回窗口可见性的方法

//--- Return the total number of chart windows including indicator subwindows
   int               WindowsTotal(void)                              const { return (int)this.GetProperty(CHART_PROP_WINDOWS_TOTAL);      }

//--- Return the window visibility
   bool              Visible(void)                                   const { return (bool)this.GetProperty(CHART_PROP_WINDOW_IS_VISIBLE); }

//--- Return the chart window handle
   int               Handle(void)                                    const { return (int)this.GetProperty(CHART_PROP_WINDOW_HANDLE);      } 

此处的方法只返回主图表窗口的属性。

返回指标子窗口上框与图表主窗口上框之间 Y 轴像素距离的方法,以及返回和设置指定图表高度(以像素为单位)的方法均已被修改。 现在,我们需要找到设置和返回这些属性所需的窗口对象,以便在对象属性中设置或获取这些数值。 稍后将研讨这些方法的实现。

//--- Return the name of a script launched on the chart
   string            ScriptName(void)                                const { return this.GetProperty(CHART_PROP_SCRIPT_NAME);             }

//--- Return the distance in Y axis pixels between the upper frame of the indicator subwindow and the upper frame of the chart main window
   int               WindowYDistance(const int sub_window)           const;
   
//--- (1) Return and (2) set the height of the specified chart in pixels
   int               WindowHeightInPixels(const int sub_window)      const;
   bool              SetWindowHeightInPixels(const int height,const int sub_window,const bool redraw=false);
   
//--- Return the specified subwindow visibility

在类主体的最底部,声明额外的方法,接收所需窗口对象,并在日志中显示所有图表子窗口的属性,以及附加到指定图表窗口的所有指标的数据:

//--- Emulate a tick (chart updates - similar to the terminal Refresh command)
   void              EmulateTick(void)                                     { ::ChartSetSymbolPeriod(this.ID(),this.Symbol(),this.Timeframe());}

//--- Return the chart window specified by index
   CChartWnd        *GetWindowByIndex(const int index)               const { return this.m_list_wnd.At(index); }
//--- Return the window object by its subwindow index
   CChartWnd        *GetWindowByNum(const int win_num)               const;
   
//--- Display data of all indicators of all chart windows in the journal
   void              PrintWndIndicators(void);
//--- Display the properties of all chart windows in the journal
   void              PrintWndParameters(void);

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

在参数型类构造函数中r设置父类对象中的图表 ID,并为图表对象指定三个主窗口属性 (这实际上并非必要,因为主窗口对象现在位于所有图表窗口的列表中,但由于对象具有此类属性,我将用正确的数据填充它们)。 在该方法的末尾,将属于图表的所有窗口对象添加到所有图表窗口的清单之中

//+------------------------------------------------------------------+
//| Parametric constructor                                           |
//+------------------------------------------------------------------+
CChartObj::CChartObj(const long chart_id)
  {
//--- Set chart ID to the base object
   CBaseObj::SetChartID(chart_id);
//--- Set integer properties
   this.SetProperty(CHART_PROP_ID,chart_id);                                                             // Chart ID
   this.SetProperty(CHART_PROP_TIMEFRAME,::ChartPeriod(this.ID()));                                      // Chart timeframe
   this.SetProperty(CHART_PROP_SHOW,::ChartGetInteger(this.ID(),CHART_SHOW));                            // Price chart drawing attribute
   this.SetProperty(CHART_PROP_IS_OBJECT,::ChartGetInteger(this.ID(),CHART_IS_OBJECT));                  // Chart object identification attribute
   this.SetProperty(CHART_PROP_BRING_TO_TOP,false);                                                      // Show chart above all others
   this.SetProperty(CHART_PROP_CONTEXT_MENU,::ChartGetInteger(this.ID(),CHART_CONTEXT_MENU));            // Access to the context menu using the right click
   this.SetProperty(CHART_PROP_CROSSHAIR_TOOL,::ChartGetInteger(this.ID(),CHART_CROSSHAIR_TOOL));        // Access the Crosshair tool by pressing the middle mouse button
   this.SetProperty(CHART_PROP_MOUSE_SCROLL,::ChartGetInteger(this.ID(),CHART_MOUSE_SCROLL));            // Scroll the chart horizontally using the left mouse button
   this.SetProperty(CHART_PROP_EVENT_MOUSE_WHEEL,::ChartGetInteger(this.ID(),CHART_EVENT_MOUSE_WHEEL));  // Send messages about mouse wheel events to all MQL5 programs on a chart
   this.SetProperty(CHART_PROP_EVENT_MOUSE_MOVE,::ChartGetInteger(this.ID(),CHART_EVENT_MOUSE_MOVE));    // Send messages about mouse button click and movement events to all MQL5 programs on a chart
   this.SetProperty(CHART_PROP_EVENT_OBJECT_CREATE,::ChartGetInteger(this.ID(),CHART_EVENT_OBJECT_CREATE)); // Send messages about the graphical object creation event to all MQL5 programs on a chart
   this.SetProperty(CHART_PROP_EVENT_OBJECT_DELETE,::ChartGetInteger(this.ID(),CHART_EVENT_OBJECT_DELETE)); // Send messages about the graphical object destruction event to all MQL5 programs on a chart
   this.SetProperty(CHART_PROP_MODE,::ChartGetInteger(this.ID(),CHART_MODE));                            // Type of the chart (candlesticks, bars or line)
   this.SetProperty(CHART_PROP_FOREGROUND,::ChartGetInteger(this.ID(),CHART_FOREGROUND));                // Price chart in the foreground
   this.SetProperty(CHART_PROP_SHIFT,::ChartGetInteger(this.ID(),CHART_SHIFT));                          // Mode of shift of the price chart from the right border
   this.SetProperty(CHART_PROP_AUTOSCROLL,::ChartGetInteger(this.ID(),CHART_AUTOSCROLL));                // The mode of automatic shift to the right border of the chart
   this.SetProperty(CHART_PROP_KEYBOARD_CONTROL,::ChartGetInteger(this.ID(),CHART_KEYBOARD_CONTROL));    // Allow managing the chart using a keyboard
   this.SetProperty(CHART_PROP_QUICK_NAVIGATION,::ChartGetInteger(this.ID(),CHART_QUICK_NAVIGATION));    // Allow the chart to intercept Space and Enter key strokes to activate the quick navigation bar
   this.SetProperty(CHART_PROP_SCALE,::ChartGetInteger(this.ID(),CHART_SCALE));                          // Scale
   this.SetProperty(CHART_PROP_SCALEFIX,::ChartGetInteger(this.ID(),CHART_SCALEFIX));                    // Fixed scale mode
   this.SetProperty(CHART_PROP_SCALEFIX_11,::ChartGetInteger(this.ID(),CHART_SCALEFIX_11));              // 1:1 scale mode
   this.SetProperty(CHART_PROP_SCALE_PT_PER_BAR,::ChartGetInteger(this.ID(),CHART_SCALE_PT_PER_BAR));    // Mode for specifying the scale in points per bar
   this.SetProperty(CHART_PROP_SHOW_TICKER,::ChartGetInteger(this.ID(),CHART_SHOW_TICKER));              // Display a symbol ticker in the upper left corner
   this.SetProperty(CHART_PROP_SHOW_OHLC,::ChartGetInteger(this.ID(),CHART_SHOW_OHLC));                  // Display OHLC values in the upper left corner
   this.SetProperty(CHART_PROP_SHOW_BID_LINE,::ChartGetInteger(this.ID(),CHART_SHOW_BID_LINE));          // Display Bid value as a horizontal line on the chart
   this.SetProperty(CHART_PROP_SHOW_ASK_LINE,::ChartGetInteger(this.ID(),CHART_SHOW_ASK_LINE));          // Display Ask value as a horizontal line on the chart
   this.SetProperty(CHART_PROP_SHOW_LAST_LINE,::ChartGetInteger(this.ID(),CHART_SHOW_LAST_LINE));        // Display Last value as a horizontal line on the chart
   this.SetProperty(CHART_PROP_SHOW_PERIOD_SEP,::ChartGetInteger(this.ID(),CHART_SHOW_PERIOD_SEP));      // Display vertical separators between adjacent periods
   this.SetProperty(CHART_PROP_SHOW_GRID,::ChartGetInteger(this.ID(),CHART_SHOW_GRID));                  // Display the chart grid
   this.SetProperty(CHART_PROP_SHOW_VOLUMES,::ChartGetInteger(this.ID(),CHART_SHOW_VOLUMES));            // Display volumes on the chart
   this.SetProperty(CHART_PROP_SHOW_OBJECT_DESCR,::ChartGetInteger(this.ID(),CHART_SHOW_OBJECT_DESCR));  // Display text descriptions of the objects
   this.SetProperty(CHART_PROP_VISIBLE_BARS,::ChartGetInteger(this.ID(),CHART_VISIBLE_BARS));            // Number of bars on a chart that are available for display
   this.SetProperty(CHART_PROP_WINDOWS_TOTAL,::ChartGetInteger(this.ID(),CHART_WINDOWS_TOTAL));          // The total number of chart windows including indicator subwindows
   this.SetProperty(CHART_PROP_WINDOW_IS_VISIBLE,::ChartGetInteger(this.ID(),CHART_WINDOW_IS_VISIBLE,0));// Window visibility
   this.SetProperty(CHART_PROP_WINDOW_HANDLE,::ChartGetInteger(this.ID(),CHART_WINDOW_HANDLE));          // Chart window handle
   this.SetProperty(CHART_PROP_WINDOW_YDISTANCE,::ChartGetInteger(this.ID(),CHART_WINDOW_YDISTANCE,0));  // Distance in Y axis pixels between the upper frame of the indicator subwindow and the upper frame of the chart main window
   this.SetProperty(CHART_PROP_FIRST_VISIBLE_BAR,::ChartGetInteger(this.ID(),CHART_FIRST_VISIBLE_BAR));  // Number of the first visible bar on the chart
   this.SetProperty(CHART_PROP_WIDTH_IN_BARS,::ChartGetInteger(this.ID(),CHART_WIDTH_IN_BARS));          // Chart width in bars
   this.SetProperty(CHART_PROP_WIDTH_IN_PIXELS,::ChartGetInteger(this.ID(),CHART_WIDTH_IN_PIXELS));      // Chart width in pixels
   this.SetProperty(CHART_PROP_HEIGHT_IN_PIXELS,::ChartGetInteger(this.ID(),CHART_HEIGHT_IN_PIXELS,0));  // Chart height in pixels
   this.SetProperty(CHART_PROP_COLOR_BACKGROUND,::ChartGetInteger(this.ID(),CHART_COLOR_BACKGROUND));    // Chart background color
   this.SetProperty(CHART_PROP_COLOR_FOREGROUND,::ChartGetInteger(this.ID(),CHART_COLOR_FOREGROUND));    // Color of axes, scale and OHLC line
   this.SetProperty(CHART_PROP_COLOR_GRID,::ChartGetInteger(this.ID(),CHART_COLOR_GRID));                // Grid color
   this.SetProperty(CHART_PROP_COLOR_VOLUME,::ChartGetInteger(this.ID(),CHART_COLOR_VOLUME));            // Color of volumes and position opening levels
   this.SetProperty(CHART_PROP_COLOR_CHART_UP,::ChartGetInteger(this.ID(),CHART_COLOR_CHART_UP));        // Color for the up bar, shadows and body borders of bullish candlesticks
   this.SetProperty(CHART_PROP_COLOR_CHART_DOWN,::ChartGetInteger(this.ID(),CHART_COLOR_CHART_DOWN));    // Color for the down bar, shadows and body borders of bearish candlesticks
   this.SetProperty(CHART_PROP_COLOR_CHART_LINE,::ChartGetInteger(this.ID(),CHART_COLOR_CHART_LINE));    // Color of the chart line and the Doji candlesticks
   this.SetProperty(CHART_PROP_COLOR_CANDLE_BULL,::ChartGetInteger(this.ID(),CHART_COLOR_CANDLE_BULL));  // Color of the bullish candle body
   this.SetProperty(CHART_PROP_COLOR_CANDLE_BEAR,::ChartGetInteger(this.ID(),CHART_COLOR_CANDLE_BEAR));  // Color of the bearish candle body
   this.SetProperty(CHART_PROP_COLOR_BID,::ChartGetInteger(this.ID(),CHART_COLOR_BID));                  // Bid price line color
   this.SetProperty(CHART_PROP_COLOR_ASK,::ChartGetInteger(this.ID(),CHART_COLOR_ASK));                  // Ask price line color
   this.SetProperty(CHART_PROP_COLOR_LAST,::ChartGetInteger(this.ID(),CHART_COLOR_LAST));                // Color of the last performed deal's price line (Last)
   this.SetProperty(CHART_PROP_COLOR_STOP_LEVEL,::ChartGetInteger(this.ID(),CHART_COLOR_STOP_LEVEL));    // Color of stop order levels (Stop Loss and Take Profit)
   this.SetProperty(CHART_PROP_SHOW_TRADE_LEVELS,::ChartGetInteger(this.ID(),CHART_SHOW_TRADE_LEVELS));  // Display trade levels on the chart (levels of open positions, Stop Loss, Take Profit and pending orders)
   this.SetProperty(CHART_PROP_DRAG_TRADE_LEVELS,::ChartGetInteger(this.ID(),CHART_DRAG_TRADE_LEVELS));  // Enable the ability to drag trading levels on a chart using mouse
   this.SetProperty(CHART_PROP_SHOW_DATE_SCALE,::ChartGetInteger(this.ID(),CHART_SHOW_DATE_SCALE));      // Display the time scale on the chart
   this.SetProperty(CHART_PROP_SHOW_PRICE_SCALE,::ChartGetInteger(this.ID(),CHART_SHOW_PRICE_SCALE));    // Display the price scale on the chart
   this.SetProperty(CHART_PROP_SHOW_ONE_CLICK,::ChartGetInteger(this.ID(),CHART_SHOW_ONE_CLICK));        // Display the quick trading panel on the chart
   this.SetProperty(CHART_PROP_IS_MAXIMIZED,::ChartGetInteger(this.ID(),CHART_IS_MAXIMIZED));            // Chart window maximized
   this.SetProperty(CHART_PROP_IS_MINIMIZED,::ChartGetInteger(this.ID(),CHART_IS_MINIMIZED));            // Chart window minimized
   this.SetProperty(CHART_PROP_IS_DOCKED,::ChartGetInteger(this.ID(),CHART_IS_DOCKED));                  // Chart window docked
   this.SetProperty(CHART_PROP_FLOAT_LEFT,::ChartGetInteger(this.ID(),CHART_FLOAT_LEFT));                // Left coordinate of the undocked chart window relative to the virtual screen
   this.SetProperty(CHART_PROP_FLOAT_TOP,::ChartGetInteger(this.ID(),CHART_FLOAT_TOP));                  // Upper coordinate of the undocked chart window relative to the virtual screen
   this.SetProperty(CHART_PROP_FLOAT_RIGHT,::ChartGetInteger(this.ID(),CHART_FLOAT_RIGHT));              // Right coordinate of the undocked chart window relative to the virtual screen
   this.SetProperty(CHART_PROP_FLOAT_BOTTOM,::ChartGetInteger(this.ID(),CHART_FLOAT_BOTTOM));            // Bottom coordinate of the undocked chart window relative to the virtual screen
//--- Set real properties
   this.SetProperty(CHART_PROP_SHIFT_SIZE,::ChartGetDouble(this.ID(),CHART_SHIFT_SIZE));                 // Shift size of the zero bar from the right border in %
   this.SetProperty(CHART_PROP_FIXED_POSITION,::ChartGetDouble(this.ID(),CHART_FIXED_POSITION));         // Chart fixed position from the left border in %
   this.SetProperty(CHART_PROP_FIXED_MAX,::ChartGetDouble(this.ID(),CHART_FIXED_MAX));                   // Fixed chart maximum
   this.SetProperty(CHART_PROP_FIXED_MIN,::ChartGetDouble(this.ID(),CHART_FIXED_MIN));                   // Fixed chart minimum
   this.SetProperty(CHART_PROP_POINTS_PER_BAR,::ChartGetDouble(this.ID(),CHART_POINTS_PER_BAR));         // Scale in points per bar
   this.SetProperty(CHART_PROP_PRICE_MIN,::ChartGetDouble(this.ID(),CHART_PRICE_MIN));                   // Chart minimum
   this.SetProperty(CHART_PROP_PRICE_MAX,::ChartGetDouble(this.ID(),CHART_PRICE_MAX));                   // Chart maximum
//--- Set string properties
   this.SetProperty(CHART_PROP_COMMENT,::ChartGetString(this.ID(),CHART_COMMENT));                       // Comment text on the chart
   this.SetProperty(CHART_PROP_EXPERT_NAME,::ChartGetString(this.ID(),CHART_EXPERT_NAME));               // name of an EA launched on the chart
   this.SetProperty(CHART_PROP_SCRIPT_NAME,::ChartGetString(this.ID(),CHART_SCRIPT_NAME));               // name of a script launched on the chart
   this.SetProperty(CHART_PROP_SYMBOL,::ChartSymbol(this.ID()));                                         // Chart symbol
   
   this.m_digits=(int)::SymbolInfoInteger(this.Symbol(),SYMBOL_DIGITS);
   int total=this.WindowsTotal();
   for(int i=0;i<total;i++)
     {
      CChartWnd *wnd=new CChartWnd(m_chart_id,i);
      if(wnd==NULL)
         continue;
      m_list_wnd.Sort();
      if(!m_list_wnd.Add(wnd))
         delete wnd;
     }
  }
//+------------------------------------------------------------------+

为了创建图表窗口列表,获取所有图表窗口的总数。 在遍历所有窗口的循环中,创建一个新的图表窗口对象,并将其添加到列表中。

在上一篇文章中开发的比较两个图表对象的方法中犯了一个逻辑错误:每个图表都有一个唯一的图表 ID 和窗口句柄。 因此,若比较这两个属性,总是返回 false。 然而,该方法的目标是确保图表匹配,而比较它们的 ID 和句柄将始终显示它们不相同。
我们在比较时跳过这两个属性,即可解决这个问题

//+------------------------------------------------------------------+
//| Compare the CChartObj objects by all properties                  |
//+------------------------------------------------------------------+
bool CChartObj::IsEqual(CChartObj *compared_obj) const
  {
   int beg=0, end=CHART_PROP_INTEGER_TOTAL;
   for(int i=beg; i<end; i++)
     {
      ENUM_CHART_PROP_INTEGER prop=(ENUM_CHART_PROP_INTEGER)i;
      if(prop==CHART_PROP_ID || prop==CHART_PROP_WINDOW_HANDLE) continue;
      if(this.GetProperty(prop)!=compared_obj.GetProperty(prop)) return false;
     }
   beg=end; end+=CHART_PROP_DOUBLE_TOTAL;
   for(int i=beg; i<end; i++)
     {
      ENUM_CHART_PROP_DOUBLE prop=(ENUM_CHART_PROP_DOUBLE)i;
      if(this.GetProperty(prop)!=compared_obj.GetProperty(prop)) return false; 
     }
   beg=end; end+=CHART_PROP_STRING_TOTAL;
   for(int i=beg; i<end; i++)
     {
      ENUM_CHART_PROP_STRING prop=(ENUM_CHART_PROP_STRING)i;
      if(this.GetProperty(prop)!=compared_obj.GetProperty(prop)) return false; 
     }
   return true;
  }
//+------------------------------------------------------------------+

在返回整数型对象属性描述的方法中,为三个新加属性加入返回描述:

//+------------------------------------------------------------------+
//| Return description of object's integer property                  |
//+------------------------------------------------------------------+
string CChartObj::GetPropertyDescription(ENUM_CHART_PROP_INTEGER property)
  {
   return
     (
      property==CHART_PROP_ID     ?  CMessage::Text(MSG_CHART_OBJ_ID)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)this.GetProperty(property)
         )  :
      property==CHART_PROP_TIMEFRAME      ?  CMessage::Text(MSG_LIB_TEXT_BAR_PERIOD)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+TimeframeDescription((ENUM_TIMEFRAMES)this.GetProperty(property))
         )  :
      property==CHART_PROP_SHOW   ?  CMessage::Text(MSG_CHART_OBJ_SHOW)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(this.GetProperty(property) ? CMessage::Text(MSG_LIB_TEXT_YES) : CMessage::Text(MSG_LIB_TEXT_NO))
         )  :
      property==CHART_PROP_IS_OBJECT      ?  CMessage::Text(MSG_CHART_OBJ_IS_OBJECT)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(this.GetProperty(property) ? CMessage::Text(MSG_LIB_TEXT_YES) : CMessage::Text(MSG_LIB_TEXT_NO))
         )  :
      property==CHART_PROP_BRING_TO_TOP   ?  CMessage::Text(MSG_CHART_OBJ_BRING_TO_TOP)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(this.GetProperty(property) ? CMessage::Text(MSG_LIB_TEXT_YES) : CMessage::Text(MSG_LIB_TEXT_NO))
         )  :
      property==CHART_PROP_CONTEXT_MENU   ?  CMessage::Text(MSG_CHART_OBJ_CONTEXT_MENU)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(this.GetProperty(property) ? CMessage::Text(MSG_LIB_TEXT_YES) : CMessage::Text(MSG_LIB_TEXT_NO))
         )  :
      property==CHART_PROP_CROSSHAIR_TOOL ?  CMessage::Text(MSG_CHART_OBJ_CROSSHAIR_TOOL)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(this.GetProperty(property) ? CMessage::Text(MSG_LIB_TEXT_YES) : CMessage::Text(MSG_LIB_TEXT_NO))
         )  :
      property==CHART_PROP_MOUSE_SCROLL   ?  CMessage::Text(MSG_CHART_OBJ_MOUSE_SCROLL)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(this.GetProperty(property) ? CMessage::Text(MSG_LIB_TEXT_YES) : CMessage::Text(MSG_LIB_TEXT_NO))
         )  :
      property==CHART_PROP_EVENT_MOUSE_WHEEL ?  CMessage::Text(MSG_CHART_OBJ_EVENT_MOUSE_WHEEL)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(this.GetProperty(property) ? CMessage::Text(MSG_LIB_TEXT_YES) : CMessage::Text(MSG_LIB_TEXT_NO))
         )  :
      property==CHART_PROP_EVENT_MOUSE_MOVE  ?  CMessage::Text(MSG_CHART_OBJ_EVENT_MOUSE_MOVE)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(this.GetProperty(property) ? CMessage::Text(MSG_LIB_TEXT_YES) : CMessage::Text(MSG_LIB_TEXT_NO))
         )  :
      property==CHART_PROP_EVENT_OBJECT_CREATE  ?  CMessage::Text(MSG_CHART_OBJ_EVENT_OBJECT_CREATE)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(this.GetProperty(property) ? CMessage::Text(MSG_LIB_TEXT_YES) : CMessage::Text(MSG_LIB_TEXT_NO))
         )  :
      property==CHART_PROP_EVENT_OBJECT_DELETE  ?  CMessage::Text(MSG_CHART_OBJ_EVENT_OBJECT_DELETE)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(this.GetProperty(property) ? CMessage::Text(MSG_LIB_TEXT_YES) : CMessage::Text(MSG_LIB_TEXT_NO))
         )  :
      property==CHART_PROP_MODE           ?  CMessage::Text(MSG_CHART_OBJ_MODE)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+ChartModeDescription((ENUM_CHART_MODE)this.GetProperty(property))
         )  :
      property==CHART_PROP_FOREGROUND     ?  CMessage::Text(MSG_CHART_OBJ_FOREGROUND)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(this.GetProperty(property) ? CMessage::Text(MSG_LIB_TEXT_YES) : CMessage::Text(MSG_LIB_TEXT_NO))
         )  :
      property==CHART_PROP_SHIFT        ?  CMessage::Text(MSG_CHART_OBJ_SHIFT)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(this.GetProperty(property) ? CMessage::Text(MSG_LIB_TEXT_YES) : CMessage::Text(MSG_LIB_TEXT_NO))
         )  :
      property==CHART_PROP_AUTOSCROLL        ?  CMessage::Text(MSG_CHART_OBJ_AUTOSCROLL)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(this.GetProperty(property) ? CMessage::Text(MSG_LIB_TEXT_YES) : CMessage::Text(MSG_LIB_TEXT_NO))
         )  :
      property==CHART_PROP_KEYBOARD_CONTROL        ?  CMessage::Text(MSG_CHART_OBJ_KEYBOARD_CONTROL)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(this.GetProperty(property) ? CMessage::Text(MSG_LIB_TEXT_YES) : CMessage::Text(MSG_LIB_TEXT_NO))
         )  :
      property==CHART_PROP_QUICK_NAVIGATION        ?  CMessage::Text(MSG_CHART_OBJ_QUICK_NAVIGATION)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(this.GetProperty(property) ? CMessage::Text(MSG_LIB_TEXT_YES) : CMessage::Text(MSG_LIB_TEXT_NO))
         )  :
      property==CHART_PROP_SCALE        ?  CMessage::Text(MSG_CHART_OBJ_SCALE)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)this.GetProperty(property)
         )  :
      property==CHART_PROP_SCALEFIX        ?  CMessage::Text(MSG_CHART_OBJ_SCALEFIX)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(this.GetProperty(property) ? CMessage::Text(MSG_LIB_TEXT_YES) : CMessage::Text(MSG_LIB_TEXT_NO))
         )  :
      property==CHART_PROP_SCALEFIX_11        ?  CMessage::Text(MSG_CHART_OBJ_SCALEFIX_11)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(this.GetProperty(property) ? CMessage::Text(MSG_LIB_TEXT_YES) : CMessage::Text(MSG_LIB_TEXT_NO))
         )  :
      property==CHART_PROP_SCALE_PT_PER_BAR  ?  CMessage::Text(MSG_CHART_OBJ_SCALE_PT_PER_BAR)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(this.GetProperty(property) ? CMessage::Text(MSG_LIB_TEXT_YES) : CMessage::Text(MSG_LIB_TEXT_NO))
         )  :
      property==CHART_PROP_SHOW_TICKER        ?  CMessage::Text(MSG_CHART_OBJ_SHOW_TICKER)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(this.GetProperty(property) ? CMessage::Text(MSG_LIB_TEXT_YES) : CMessage::Text(MSG_LIB_TEXT_NO))
         )  :
      property==CHART_PROP_SHOW_OHLC        ?  CMessage::Text(MSG_CHART_OBJ_SHOW_OHLC)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(this.GetProperty(property) ? CMessage::Text(MSG_LIB_TEXT_YES) : CMessage::Text(MSG_LIB_TEXT_NO))
         )  :
      property==CHART_PROP_SHOW_BID_LINE  ?  CMessage::Text(MSG_CHART_OBJ_SHOW_BID_LINE)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(this.GetProperty(property) ? CMessage::Text(MSG_LIB_TEXT_YES) : CMessage::Text(MSG_LIB_TEXT_NO))
         )  :
      property==CHART_PROP_SHOW_ASK_LINE  ?  CMessage::Text(MSG_CHART_OBJ_SHOW_ASK_LINE)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(this.GetProperty(property) ? CMessage::Text(MSG_LIB_TEXT_YES) : CMessage::Text(MSG_LIB_TEXT_NO))
         )  :
      property==CHART_PROP_SHOW_LAST_LINE ?  CMessage::Text(MSG_CHART_OBJ_SHOW_LAST_LINE)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(this.GetProperty(property) ? CMessage::Text(MSG_LIB_TEXT_YES) : CMessage::Text(MSG_LIB_TEXT_NO))
         )  :
      property==CHART_PROP_SHOW_PERIOD_SEP   ?  CMessage::Text(MSG_CHART_OBJ_SHOW_PERIOD_SEP)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(this.GetProperty(property) ? CMessage::Text(MSG_LIB_TEXT_YES) : CMessage::Text(MSG_LIB_TEXT_NO))
         )  :
      property==CHART_PROP_SHOW_GRID        ?  CMessage::Text(MSG_CHART_OBJ_SHOW_GRID)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(this.GetProperty(property) ? CMessage::Text(MSG_LIB_TEXT_YES) : CMessage::Text(MSG_LIB_TEXT_NO))
         )  :
      property==CHART_PROP_SHOW_VOLUMES   ?  CMessage::Text(MSG_CHART_OBJ_SHOW_VOLUMES)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+ChartModeVolumeDescription((ENUM_CHART_VOLUME_MODE)this.GetProperty(property))
         )  :
      property==CHART_PROP_SHOW_OBJECT_DESCR ?  CMessage::Text(MSG_CHART_OBJ_SHOW_OBJECT_DESCR)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(this.GetProperty(property) ? CMessage::Text(MSG_LIB_TEXT_YES) : CMessage::Text(MSG_LIB_TEXT_NO))
         )  :
      property==CHART_PROP_VISIBLE_BARS   ?  CMessage::Text(MSG_CHART_OBJ_VISIBLE_BARS)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)this.GetProperty(property)
         )  :
      property==CHART_PROP_WINDOWS_TOTAL  ?  CMessage::Text(MSG_CHART_OBJ_WINDOWS_TOTAL)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)this.GetProperty(property)
         )  :
      property==CHART_PROP_WINDOW_IS_VISIBLE  ?  CMessage::Text(MSG_CHART_OBJ_WINDOW_IS_VISIBLE)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(this.GetProperty(property) ? CMessage::Text(MSG_LIB_TEXT_YES) : CMessage::Text(MSG_LIB_TEXT_NO))
         )  :
      property==CHART_PROP_WINDOW_HANDLE  ?  CMessage::Text(MSG_CHART_OBJ_WINDOW_HANDLE)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)this.GetProperty(property)
         )  :
      property==CHART_PROP_WINDOW_YDISTANCE  ?  CMessage::Text(MSG_CHART_OBJ_WINDOW_YDISTANCE)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)this.GetProperty(property)
         )  :
      property==CHART_PROP_FIRST_VISIBLE_BAR ?  CMessage::Text(MSG_CHART_OBJ_FIRST_VISIBLE_BAR)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)this.GetProperty(property)
         )  :
      property==CHART_PROP_WIDTH_IN_BARS  ?  CMessage::Text(MSG_CHART_OBJ_WIDTH_IN_BARS)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)this.GetProperty(property)
         )  :
      property==CHART_PROP_WIDTH_IN_PIXELS   ?  CMessage::Text(MSG_CHART_OBJ_WIDTH_IN_PIXELS)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)this.GetProperty(property)
         )  :
      property==CHART_PROP_HEIGHT_IN_PIXELS   ?  CMessage::Text(MSG_CHART_OBJ_HEIGHT_IN_PIXELS)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)this.GetProperty(property)
         )  :
      property==CHART_PROP_COLOR_BACKGROUND        ?  CMessage::Text(MSG_CHART_OBJ_COLOR_BACKGROUND)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+::ColorToString((color)this.GetProperty(property),true)
         )  :
      property==CHART_PROP_COLOR_FOREGROUND  ?  CMessage::Text(MSG_CHART_OBJ_COLOR_FOREGROUND)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+::ColorToString((color)this.GetProperty(property),true)
         )  :
      property==CHART_PROP_COLOR_GRID     ?  CMessage::Text(MSG_CHART_OBJ_COLOR_GRID)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+::ColorToString((color)this.GetProperty(property),true)
         )  :
      property==CHART_PROP_COLOR_VOLUME   ?  CMessage::Text(MSG_CHART_OBJ_COLOR_VOLUME)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+::ColorToString((color)this.GetProperty(property),true)
         )  :
      property==CHART_PROP_COLOR_CHART_UP ?  CMessage::Text(MSG_CHART_OBJ_COLOR_CHART_UP)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+::ColorToString((color)this.GetProperty(property),true)
         )  :
      property==CHART_PROP_COLOR_CHART_DOWN  ?  CMessage::Text(MSG_CHART_OBJ_COLOR_CHART_DOWN)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+::ColorToString((color)this.GetProperty(property),true)
         )  :
      property==CHART_PROP_COLOR_CHART_LINE  ?  CMessage::Text(MSG_CHART_OBJ_COLOR_CHART_LINE)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+::ColorToString((color)this.GetProperty(property),true)
         )  :
      property==CHART_PROP_COLOR_CANDLE_BULL ?  CMessage::Text(MSG_CHART_OBJ_COLOR_CANDLE_BULL)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+::ColorToString((color)this.GetProperty(property),true)
         )  :
      property==CHART_PROP_COLOR_CANDLE_BEAR ?  CMessage::Text(MSG_CHART_OBJ_COLOR_CANDLE_BEAR)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+::ColorToString((color)this.GetProperty(property),true)
         )  :
      property==CHART_PROP_COLOR_BID        ?  CMessage::Text(MSG_CHART_OBJ_COLOR_BID)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+::ColorToString((color)this.GetProperty(property),true)
         )  :
      property==CHART_PROP_COLOR_ASK        ?  CMessage::Text(MSG_CHART_OBJ_COLOR_ASK)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+::ColorToString((color)this.GetProperty(property),true)
         )  :
      property==CHART_PROP_COLOR_LAST        ?  CMessage::Text(MSG_CHART_OBJ_COLOR_LAST)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+::ColorToString((color)this.GetProperty(property),true)
         )  :
      property==CHART_PROP_COLOR_STOP_LEVEL  ?  CMessage::Text(MSG_CHART_OBJ_COLOR_STOP_LEVEL)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+::ColorToString((color)this.GetProperty(property),true)
         )  :
      property==CHART_PROP_SHOW_TRADE_LEVELS ?  CMessage::Text(MSG_CHART_OBJ_SHOW_TRADE_LEVELS)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(this.GetProperty(property) ? CMessage::Text(MSG_LIB_TEXT_YES) : CMessage::Text(MSG_LIB_TEXT_NO))
         )  :
      property==CHART_PROP_DRAG_TRADE_LEVELS ?  CMessage::Text(MSG_CHART_OBJ_DRAG_TRADE_LEVELS)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(this.GetProperty(property) ? CMessage::Text(MSG_LIB_TEXT_YES) : CMessage::Text(MSG_LIB_TEXT_NO))
         )  :
      property==CHART_PROP_SHOW_DATE_SCALE   ?  CMessage::Text(MSG_CHART_OBJ_SHOW_DATE_SCALE)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(this.GetProperty(property) ? CMessage::Text(MSG_LIB_TEXT_YES) : CMessage::Text(MSG_LIB_TEXT_NO))
         )  :
      property==CHART_PROP_SHOW_PRICE_SCALE  ?  CMessage::Text(MSG_CHART_OBJ_SHOW_PRICE_SCALE)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(this.GetProperty(property) ? CMessage::Text(MSG_LIB_TEXT_YES) : CMessage::Text(MSG_LIB_TEXT_NO))
         )  :
      property==CHART_PROP_SHOW_ONE_CLICK ?  CMessage::Text(MSG_CHART_OBJ_SHOW_ONE_CLICK)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(this.GetProperty(property) ? CMessage::Text(MSG_LIB_TEXT_YES) : CMessage::Text(MSG_LIB_TEXT_NO))
         )  :
      property==CHART_PROP_IS_MAXIMIZED   ?  CMessage::Text(MSG_CHART_OBJ_IS_MAXIMIZED)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(this.GetProperty(property) ? CMessage::Text(MSG_LIB_TEXT_YES) : CMessage::Text(MSG_LIB_TEXT_NO))
         )  :
      property==CHART_PROP_IS_MINIMIZED   ?  CMessage::Text(MSG_CHART_OBJ_IS_MINIMIZED)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(this.GetProperty(property) ? CMessage::Text(MSG_LIB_TEXT_YES) : CMessage::Text(MSG_LIB_TEXT_NO))
         )  :
      property==CHART_PROP_IS_DOCKED      ?  CMessage::Text(MSG_CHART_OBJ_IS_DOCKED)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(this.GetProperty(property) ? CMessage::Text(MSG_LIB_TEXT_YES) : CMessage::Text(MSG_LIB_TEXT_NO))
         )  :
      property==CHART_PROP_FLOAT_LEFT     ?  CMessage::Text(MSG_CHART_OBJ_FLOAT_LEFT)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)this.GetProperty(property)
         )  :
      property==CHART_PROP_FLOAT_TOP      ?  CMessage::Text(MSG_CHART_OBJ_FLOAT_TOP)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)this.GetProperty(property)
         )  :
      property==CHART_PROP_FLOAT_RIGHT    ?  CMessage::Text(MSG_CHART_OBJ_FLOAT_RIGHT)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)this.GetProperty(property)
         )  :
      property==CHART_PROP_FLOAT_BOTTOM   ?  CMessage::Text(MSG_CHART_OBJ_FLOAT_BOTTOM)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)this.GetProperty(property)
         )  :
      ""
     );
  }
//+------------------------------------------------------------------+

添加的代码模块与所有其他模块雷同 — 根据传递给方法的属性,创建并返回含有其描述和数值的字符串。

我们来改进在日志中显示所有对象属性的方法:

//+------------------------------------------------------------------+
//| Display object properties in the journal                         |
//+------------------------------------------------------------------+
void CChartObj::Print(const bool full_prop=false)
  {
   ::Print("============= ",CMessage::Text(MSG_LIB_PARAMS_LIST_BEG)," (",this.Header(),") =============");
   int beg=0, end=CHART_PROP_INTEGER_TOTAL;
   for(int i=beg; i<end; i++)
     {
      ENUM_CHART_PROP_INTEGER prop=(ENUM_CHART_PROP_INTEGER)i;
      if(!full_prop && !this.SupportProperty(prop)) continue;
      if(prop==CHART_PROP_WINDOW_IND_HANDLE || prop==CHART_PROP_WINDOW_IND_INDEX) continue;
      if(prop==CHART_PROP_HEIGHT_IN_PIXELS)
        {
         this.PrintWndParameters();
         continue;
        }
      ::Print(this.GetPropertyDescription(prop));
     }
   ::Print("------");
   beg=end; end+=CHART_PROP_DOUBLE_TOTAL;
   for(int i=beg; i<end; i++)
     {
      ENUM_CHART_PROP_DOUBLE prop=(ENUM_CHART_PROP_DOUBLE)i;
      if(!full_prop && !this.SupportProperty(prop)) continue;
      ::Print(this.GetPropertyDescription(prop));
     }
   ::Print("------");
   beg=end; end+=CHART_PROP_STRING_TOTAL;
   for(int i=beg; i<end; i++)
     {
      ENUM_CHART_PROP_STRING prop=(ENUM_CHART_PROP_STRING)i;
      if(!full_prop && !this.SupportProperty(prop)) continue;
      if(prop==CHART_PROP_INDICATOR_NAME)
        {
         this.PrintWndIndicators();
         continue;
        }
      ::Print(this.GetPropertyDescription(prop));
     }
   ::Print("============= ",CMessage::Text(MSG_LIB_PARAMS_LIST_END)," (",this.Header(),") =============\n");
  }
//+------------------------------------------------------------------+

如果循环含有“指标句柄”和“窗口中的指标索引”属性,则会跳过它们。 它们与图表对象无关。
若是“图表高度像素”属性的情况下,将调用显示所有图表窗口属性描述的方法
若是“窗口中的指标名称”属性的情况下,将调用显示所有图表窗口附加的所有指标的描述的方法。 我们稍后会研讨这些方法。

显示对象简述的方法也得到了改进。
现在,它还显示图表窗口里子窗口的数量(如果存在)或通知没有子窗口

//+------------------------------------------------------------------+
//| Display a short description of the object in the journal         |
//+------------------------------------------------------------------+
void CChartObj::PrintShort(const bool dash=false)
  {
   ::Print
     (
      (dash ? "- " : ""),this.Header()," ID: ",(string)this.ID(),", HWND: ",(string)this.Handle(),
      ", ",CMessage::Text(MSG_CHART_OBJ_CHART_SUBWINDOWS_NUM),": ",(this.WindowsTotal()>1 ? string(this.WindowsTotal()-1) : CMessage::Text(MSG_LIB_TEXT_NO))
     );
  }
//+------------------------------------------------------------------+

日志中显示所有图表窗口所有指标数据的方法:

//+-------------------------------------------------------------------+
//| Display data of all indicators of all chart windows in the journal|
//+-------------------------------------------------------------------+
void CChartObj::PrintWndIndicators(void)
  {
   for(int i=0;i<this.WindowsTotal();i++)
     {
      CChartWnd *wnd=m_list_wnd.At(i);
      if(wnd==NULL)
         continue;
      wnd.PrintIndicators(true);
     }
  }
//+------------------------------------------------------------------+

此处,在遍历所有图表窗口对象的循环中获取下一个图表窗口对象,并在日志中显示窗口附加的所有指标的描述。 该方法接收 true 标志,以便在指标描述之前显示连字符。

在日志中显示所有图表窗口属性的方法:

//+------------------------------------------------------------------+
//| Display the properties of all chart windows in the journal       |
//+------------------------------------------------------------------+
void CChartObj::PrintWndParameters(void)
  {
   for(int i=0;i<this.WindowsTotal();i++)
     {
      CChartWnd *wnd=m_list_wnd.At(i);
      if(wnd==NULL)
         continue;
      wnd.PrintParameters(true);
     }
  }
//+------------------------------------------------------------------+

此处,在遍历所有图表窗口对象的循环中获取下一个图表窗口对象,并在日志中显示其参数的描述。 该方法接收 true,以便在窗口描述之前显示连字符。

依据子窗口索引返回窗口对象的方法:

//+------------------------------------------------------------------+
//| Return the window object by its subwindow index                  |
//+------------------------------------------------------------------+
CChartWnd *CChartObj::GetWindowByNum(const int win_num) const
  {
   int total=m_list_wnd.Total();
   for(int i=0;i<total;i++)
     {
      CChartWnd *wnd=m_list_wnd.At(i);
      if(wnd==NULL)
         continue;
      if(wnd.WindowNum()==win_num)
         return wnd;
     }
   return NULL;
  }
//+------------------------------------------------------------------+

此处,按图表窗口对象总数的循环中得到下一个窗口对象如果其索引与传递给该方法的索引匹配,则返回列表中找到的对象指针直至循环完毕后,返回 NULL — 对象未找到。

为图表对象设置“子窗口可见性”属性的方法:

//+------------------------------------------------------------------+
//| Set the "Subwindow visibility" property                          |
//+------------------------------------------------------------------+
void CChartObj::SetVisible(void)
  {
   this.SetProperty(CHART_PROP_WINDOW_IS_VISIBLE,::ChartGetInteger(this.ID(),CHART_WINDOW_IS_VISIBLE,0));
  }
//+------------------------------------------------------------------+

在此,只需添加主图表窗口的相应只读属性。

返回指标子窗口上框与主窗口上框之间的 Y 轴像素距离的方法:

//+-----------------------------------------------------------------------------------+
//| Return the distance in pixels by Y axis between                                   |
//| the upper frame of the indicator subwindow and the upper frame of the main window |
//+-----------------------------------------------------------------------------------+
int CChartObj::WindowYDistance(const int sub_window) const
  {
   CChartWnd *wnd=GetWindowByNum(sub_window);
   return(wnd!=NULL ? wnd.YDistance() : WRONG_VALUE);
  }
//+------------------------------------------------------------------+

这里,我们按其子窗口索引接收图表窗口对象,并从获取的对象中返回属性值
如果未收到对象,则返回 -1

返回指定图表高度像素的方法:

//+------------------------------------------------------------------+
//| Return the height of the specified chart in pixels               |
//+------------------------------------------------------------------+
int CChartObj::WindowHeightInPixels(const int sub_window) const
  {
   CChartWnd *wnd=GetWindowByNum(sub_window);
   return(wnd!=NULL ? wnd.HeightInPixels() : WRONG_VALUE);
  }
//+------------------------------------------------------------------+

这里,我们按其子窗口索引接收图表窗口对象,并从获取的对象中返回属性值
如果未收到对象,则返回 -1

为指定图表设置高度像素的方法:

//+------------------------------------------------------------------+
//| Set the height of the specified chart in pixels                  |
//+------------------------------------------------------------------+
bool CChartObj::SetWindowHeightInPixels(const int height,const int sub_window,const bool redraw=false)
  {
   CChartWnd *wnd=GetWindowByNum(sub_window);
   return(wnd!=NULL ? wnd.SetHeightInPixels(height,redraw) : false);
  }
//+------------------------------------------------------------------+

此处,我们按其子窗口索引接收图表窗口对象,并返回为获取对象设置相应属性的结果
如果未收到对象,则返回 false

把我在上一篇文章中添加的旧方法删除:

//+------------------------------------------------------------------+
//| Set the height of the specified chart in pixels                  |
//+------------------------------------------------------------------+
bool CChartObj::SetWindowHeightInPixels(const int height,const int sub_window,const bool redraw=false)
  {
   ::ResetLastError();
   if(!::ChartSetInteger(this.ID(),CHART_HEIGHT_IN_PIXELS,sub_window,height))
     {
      CMessage::ToLog(DFUN,::GetLastError(),true);
      return false;
     }
   if(redraw)
      ::ChartRedraw(this.ID());
   return true;
  }
//+------------------------------------------------------------------+

图表对象类的改进到此完毕。


测试

为了检查创建对象的性能,简单地打开任意三个图表。 在加载 EA 的图表上加入分形指标 + 添加指标窗口,例如,包含另一个指标的 DeMarker,譬如基于 DeMarker 数据的 AMA。
在第二个图表上,我将放置随机振荡器窗口;而第三个窗口将取消驻靠 (Alt+D 浮动小窗口):


我将在日志中显示所有三个图表对象的简述,以及 EA 所在的当前图表的完整描述。

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

EA 几乎保持不变。 我们所要做的全部只是将逻辑添加到 OnTick() 处理程序当中

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
//--- Handle the NewTick event in the library
   engine.OnTick(rates_data);

//--- If working in the tester
   if(MQLInfoInteger(MQL_TESTER))
     {
      engine.OnTimer(rates_data);   // Working in the timer
      PressButtonsControl();        // Button pressing control
      engine.EventsHandling();      // Working with events
     }

//--- If the trailing flag is set
   if(trailing_on)
     {
      TrailingPositions();          // Trailing positions
      TrailingOrders();             // Trailing pending orders
     }
   
//--- If it is the first launch
   static bool done=false;
   if(!done)
     {
      //--- Create the list object for storing chart objects
      CArrayObj *list=new CArrayObj();
      if(list==NULL)
         return;
      //--- Declare the variables and get the first chart ID
      long currChart,prevChart=ChartFirst(); 
      int i=0; 
      //--- Create the chart object and add it to the list
      CChartObj *chart_first=new CChartObj(prevChart);
      list.Add(chart_first);
      //--- In the loop by the total number of terminal charts (not more than 100)
      while(i<CHARTS_MAX)
        { 
         //--- based on the previous one, get the new chart
         currChart=ChartNext(prevChart);
         //--- When reaching the end of the chart list, complete the loop
         if(currChart<0) break;
         //--- Create the chart object based on the current chart ID in the loop and add it to the list
         CChartObj *chart=new CChartObj(currChart);
         list.Add(chart);
         //--- remember the current chart ID for ChartNext() and increase the loop counter
         prevChart=currChart;
         i++;
        }
      Print("");
      //--- From the filled list in the loop, receive the next chart object and display its short description
      int total=list.Total();
      for(int j=0;j<total;j++)
        {
         CChartObj *chart_obj=list.At(j);
         if(chart_obj!=NULL)
            chart_obj.PrintShort();
        }
      Print("");
      //--- Display the full description of the current chart: in the loop by all objects of the created list
      for(int j=0;j<total;j++)
        {
         //--- get the next chart object and
         CChartObj *chart_obj=list.At(j);
         //--- if its symbol matches the current chart symbol, display its full description in the journal
         if(chart_obj!=NULL && chart_obj.Symbol()==Symbol())
            chart_obj.Print();
        }
      //--- Destroy the list of chart objects
      delete list;
      done=true;
     }
//---
  }
//+------------------------------------------------------------------+

编译 EA,并在图表上启动它,同时初步创建本章节开头提到的所需终端环境。

日志显示所有三个打开的​​图表的简述:

Main chart window EURUSD H4 ID: 131733844391938630, HWND: 5179646, Subwindows: 1
Main chart window AUDUSD H4 ID: 131733844391938634, HWND: 3672036, Subwindows: 1
Main chart window GBPUSD H4 ID: 131733844391938633, HWND: 3473910, Subwindows: No

以及当前图表的完整描述。 当前文章中开发的类,会在日志中顺序显示窗口对象属性字符串和附于其上的指标属性字符串

============= The beginning of the parameter list (Main chart window EURUSD H4) =============
Chart ID: 131733844391938630
Timeframe: H4
Drawing attributes of a price chart: Yes
Object "Chart": No
Chart on top of other charts: No
Accessing the context menu by pressing the right mouse button: Yes
Accessing the "Crosshair tool" by pressing the middle mouse button: Yes
Scrolling the chart horizontally using the left mouse button: Yes
Sending messages about mouse wheel events to all mql5 programs on a chart: No
Send notifications of mouse move and mouse click events to all mql5 programs on a chart: No
Send a notification of an event of new object creation to all mql5-programs on a chart: No
Send a notification of an event of object deletion to all mql5-programs on a chart: No
Chart type: Display as Japanese candlesticks
Price chart in the foreground: No
Price chart indent from the right border: Yes
Automatic moving to the right border of the chart: Yes
Managing the chart using a keyboard: Yes
Allowed to intercept Space and Enter key presses on the chart to activate the quick navigation bar: Yes
Scale: 2
Fixed scale mode: No
Scale 1:1 mode: No
Scale to be specified in points per bar: No
Display a symbol ticker in the upper left corner: Yes
Display OHLC values in the upper left corner: Yes
Display Bid values as a horizontal line in a chart: Yes
Display Ask values as a horizontal line in a chart: Yes
Display Last values as a horizontal line in a chart: No
Display vertical separators between adjacent periods: No
Display grid in the chart: No
Display volume in the chart: Trade volumes
Display textual descriptions of objects: Yes
The number of bars on the chart that can be displayed: 137
The total number of chart windows, including indicator subwindows: 2
Visibility of subwindow: Yes
Chart window handle: 5179646
Number of the first visible bar in the chart: 136
Chart width in bars: 168
Chart width in pixels: 670
 Main chart window:
 - Chart height in pixels: 301
 Chart subwindow 1:
 - The distance between the upper frame of the indicator subwindow and the upper frame of the main chart window: 303
 - Chart height in pixels: 13
Chart background color: clrWhite
Color of axes, scales and OHLC line: clrBlack
Grid color: clrSilver
Color of volumes and position opening levels: clrGreen
Color for the up bar, shadows and body borders of bull candlesticks: clrBlack
Color for the down bar, shadows and body borders of bear candlesticks: clrBlack
Line chart color and color of "Doji" Japanese candlesticks: clrBlack
Body color of a bull candlestick: clrWhite
Body color of a bear candlestick: clrBlack
Bid price level color: clrLightSkyBlue
Ask price level color: clrCoral
Line color of the last executed deal price (Last): clrSilver
Color of stop order levels (Stop Loss and Take Profit): clrOrangeRed
Displaying trade levels in the chart (levels of open positions, Stop Loss, Take Profit and pending orders): Yes
Permission to drag trading levels on a chart with a mouse: Yes
Showing the time scale on a chart: Yes
Showing the price scale on a chart: Yes
Showing the "One click trading" panel on a chart: No
Chart window is maximized: Yes
Chart window is minimized: No
The chart window is docked: Yes
The left coordinate of the undocked chart window relative to the virtual screen: 0
The top coordinate of the undocked chart window relative to the virtual screen: 0
The right coordinate of the undocked chart window relative to the virtual screen: 0
The bottom coordinate of the undocked chart window relative to the virtual screen: 0
------
The size of the zero bar indent from the right border in percents: 18.93
Chart fixed position from the left border in percent value: 0.00
Fixed  chart maximum: 1.22620
Fixed  chart minimum : 1.17940
Scale in points per bar: 1.00
Chart minimum: 1.17940
Chart maximum: 1.22620
------
Text of a comment in a chart: ""
The name of the Expert Advisor running on the chart: "TestDoEasyPart68"
The name of the script running on the chart: ""
Indicators in the main chart window:
- Indicator Fractals
Indicators in the chart window 1:
- Indicator DeM(14)
- Indicator AMA(14,2,30)
Symbol: "EURUSD"
============= End of the parameter list (Main chart window EURUSD H4) =============


下一步是什么?

在下一篇文章中,我将着手开发图表对象集合类。

以下是该函数库当前版本的所有文件,以及 MQL5 的测试 EA 文件,供您测试和下载。
我不建议在您的实际操作中运用当前状态的图表对象,因为它们会得到进一步修改。
请您在评论中留下问题和建议。

返回内容目录

*该系列的前几篇文章:

DoEasy 函数库中的价格(第六十二部分):实时更新即时报价序列,为操控市场深度做准备
DoEasy 函数库中的价格(第六十三部分):市场深度及其抽象请求类
DoEasy 函数库中的价格(第六十四部分):市场深度,DOM 快照类和快照序列对象
DoEasy 函数库中的价格(第六十五部分):市场深度集合并操控 MQL5.com 信号的类
DoEasy 函数库中的其他类(第六十六部分):MQL5.com 信号集合类
DoEasy 函数库中的其他类(第六十七部分):图表对象类

全部回复

0/140

达人推荐

量化课程

    移动端课程