DoEasy 函数库中的图形(第九十二部分):标准图形对象记忆类。 对象属性变更历史记录

Artyom Trishkin | 29 三月, 2022

内容


概述

我们已经测试了图形对象属性重命名的历史的实现。 实际上,上一篇文章中创建的功能,已经允许我们定义图形对象重命名的整个序列。 很难预测它能有多大用处。 不过,考虑到重命名过程中会删除以前的对象,并创建一个拥有另一个名称的新对象,故此,掌握图表上过往存在的对象也许会很有用处。 如果我们考虑到存储对象整个变更历史的可能性,那么我们将在任何图形对象中掌控其所有状态,包括以前的名称。 因此,这个对象可令我们知道它以前的名字。 通过匹配所需的名称及其存储在记忆中的属性,我们可以轻松地恢复图表上的对象。


我不打算讨论它会在哪里以及如何派上用场,但它肯定是技术分析能用到的另一个工具。 例如,对象名称可用于标记图形对象构建的日期、周、月、或任何其它时间区间。 当新的时间片段到达时,旧对象将被重命名,以便其名称与新的时间片段吻合,且在图表上重建对象。 相应地,它的所有属性都将存储在其记忆中(我将在本文中实现这种功能),包括它的重命名历史。 如果我们要用图形对象在图表上为某个东西作标记,并要每天重建该对象,则可为该对象重命名匹配的新日期。 当手动滚动图表时,我们可以发现一根可见柱线的时间,并从其记忆中“提取”图形对象状态。 该状态应与滚动图表上的柱线时间相对应,并将其属性应用到当前对象。 如此,我们将能够创建一个智能对象,该对象能根据图表上可见柱线的时间自行改变其状态。

故此,上述示例将令我们在一个交易周内,根据每天的当时市场状态调整图形对象属性。 在每个周末,我们可以简单地将图表向回滚动,图形对象将在图表的可视区域显示过往的每个交易日。 由于对象有自己的记忆,因此每次对象属性变更时,它都会保存所有变更。 如果图表拥有一个由函数库控制的 EA,那么某个图形对象应该能针对图表一天的可视区域状态制作“快照”,并将这些参数应用于自身;滚动图表可作为触发更改图形对象属性的条件,它能显示在交易周内所做的所有修改。

我们为什么需要这个? 我相信,这个功能对于分析一个交易周是很方便的。 此外,这只是一个浮现在脑海,并立即被我捕捉到的似乎很有用的例子。


改进库类

标准图形对象属性的枚举有一个 Group 属性。 它当前用于指定图形对象所属的分组:


然而,如果我们还记得,其它函数库对象也有它们的分组,并且它们被用来根据某些属性对对象进行排序,所以为图形对象建立相同的顺序也是合理的。 当前的 Group 属性将重命名为 Species,而新的 Group 属性将用于按对象的某些属性对对象进行排序。 为了测试我在这里创建的功能,我将把分组 #1 分配给图表上创建的图形对象。 分组中的所有对象在修改时都会保存其状态。

在 \MQL5\Include\DoEasy\Defines.mqh 里,替换 "Graphical object group" 枚举:

//+------------------------------------------------------------------+
//| The list of graphical element types                              |
//+------------------------------------------------------------------+
enum ENUM_GRAPH_ELEMENT_TYPE
  {
   GRAPH_ELEMENT_TYPE_STANDARD,                       // Standard graphical object
   GRAPH_ELEMENT_TYPE_ELEMENT,                        // Element
   GRAPH_ELEMENT_TYPE_SHADOW_OBJ,                     // Shadow object
   GRAPH_ELEMENT_TYPE_FORM,                           // Form
   GRAPH_ELEMENT_TYPE_WINDOW,                         // Window
  };
//+------------------------------------------------------------------+
//| Graphical object group                                           |
//+------------------------------------------------------------------+
enum ENUM_GRAPH_OBJ_GROUP
  {
   GRAPH_OBJ_GROUP_LINES,                             // Lines
   GRAPH_OBJ_GROUP_CHANNELS,                          // Channels
   GRAPH_OBJ_GROUP_GANN,                              // Gann
   GRAPH_OBJ_GROUP_FIBO,                              // Fibo
   GRAPH_OBJ_GROUP_ELLIOTT,                           // Elliott
   GRAPH_OBJ_GROUP_SHAPES,                            // Shapes
   GRAPH_OBJ_GROUP_ARROWS,                            // Arrows
   GRAPH_OBJ_GROUP_GRAPHICAL,                         // Graphical objects
  };
//+------------------------------------------------------------------+
//| Integer properties of a standard graphical object                |
//+------------------------------------------------------------------+

新枚举是 "Graphical object species":

//+------------------------------------------------------------------+
//| Graphical object species                                         |
//+------------------------------------------------------------------+
enum ENUM_GRAPH_OBJ_SPECIES
  {
   GRAPH_OBJ_SPECIES_LINES,                             // Lines
   GRAPH_OBJ_SPECIES_CHANNELS,                          // Channels
   GRAPH_OBJ_SPECIES_GANN,                              // Gann
   GRAPH_OBJ_SPECIES_FIBO,                              // Fibo
   GRAPH_OBJ_SPECIES_ELLIOTT,                           // Elliott
   GRAPH_OBJ_SPECIES_SHAPES,                            // Shapes
   GRAPH_OBJ_SPECIES_ARROWS,                            // Arrows
   GRAPH_OBJ_SPECIES_GRAPHICAL,                         // Graphical objects
  };
//+------------------------------------------------------------------+

在标准图形对象的整数型属性枚举中,替换 Group 属性

   //--- Additional properties
   GRAPH_OBJ_PROP_ID = 0,                             // Object ID
   GRAPH_OBJ_PROP_TYPE,                               // Graphical object type (ENUM_OBJECT)
   GRAPH_OBJ_PROP_ELEMENT_TYPE,                       // Graphical element type (ENUM_GRAPH_ELEMENT_TYPE)
   GRAPH_OBJ_PROP_GROUP,                              // Graphical object group (ENUM_GRAPH_OBJ_GROUP)
   GRAPH_OBJ_PROP_BELONG,                             // Graphical object affiliation
   GRAPH_OBJ_PROP_CHART_ID,                           // Chart ID
   GRAPH_OBJ_PROP_WND_NUM,                            // Chart subwindow index
   GRAPH_OBJ_PROP_NUM,                                // Object index in the list

新属性为 Species,并加入两个新参数 — 存储历史记录变更的标志对象分组:

//+------------------------------------------------------------------+
//| Integer properties of a standard graphical object                |
//+------------------------------------------------------------------+
enum ENUM_GRAPH_OBJ_PROP_INTEGER
  {
   //--- Additional properties
   GRAPH_OBJ_PROP_ID = 0,                             // Object ID
   GRAPH_OBJ_PROP_TYPE,                               // Graphical object type (ENUM_OBJECT)
   GRAPH_OBJ_PROP_ELEMENT_TYPE,                       // Graphical element type (ENUM_GRAPH_ELEMENT_TYPE)
   GRAPH_OBJ_PROP_SPECIES,                            // Graphical object species (ENUM_GRAPH_OBJ_SPECIES)
   GRAPH_OBJ_PROP_BELONG,                             // Graphical object affiliation
   GRAPH_OBJ_PROP_CHART_ID,                           // Chart ID
   GRAPH_OBJ_PROP_WND_NUM,                            // Chart subwindow index
   GRAPH_OBJ_PROP_NUM,                                // Object index in the list
   GRAPH_OBJ_PROP_CHANGE_HISTORY,                     // Flag of storing the change history
   GRAPH_OBJ_PROP_GROUP,                              // Group of objects the graphical object belongs to
   //--- Common properties of all graphical objects

默认情况下,图形对象并不会存储属性变更历史记录。 因此,我实现了存储标志的属性,该标志指明图形对象是否记录了其变更历史。 如上所述,我已将 Group 属性重命名为 Species,并创建了新的 Group 属性,存储按特定属性排序的对象组的索引。

由于我们在整数型图形对象属性的枚举中添加了两个新属性,故需指定它们的新数量(54 替换 52):

#define GRAPH_OBJ_PROP_INTEGER_TOTAL (54)             // Total number of integer properties
#define GRAPH_OBJ_PROP_INTEGER_SKIP  (0)              // Number of integer properties not used in sorting
//+------------------------------------------------------------------+
//| Real properties of a standard graphical object                   |
//+------------------------------------------------------------------+

在可能的图形对象排序标准的枚举中添加新属性

//+------------------------------------------------------------------+
//| Possible sorting criteria of graphical objects                   |
//+------------------------------------------------------------------+
#define FIRST_GRAPH_OBJ_DBL_PROP  (GRAPH_OBJ_PROP_INTEGER_TOTAL-GRAPH_OBJ_PROP_INTEGER_SKIP)
#define FIRST_GRAPH_OBJ_STR_PROP  (GRAPH_OBJ_PROP_INTEGER_TOTAL-GRAPH_OBJ_PROP_INTEGER_SKIP+GRAPH_OBJ_PROP_DOUBLE_TOTAL-GRAPH_OBJ_PROP_DOUBLE_SKIP)
enum ENUM_SORT_GRAPH_OBJ_MODE
  {
//--- Sort by integer properties
   SORT_BY_GRAPH_OBJ_ID = 0,                             // Sort by object ID
   SORT_BY_GRAPH_OBJ_TYPE,                               // Sort by object type
   SORT_BY_GRAPH_OBJ_ELEMENT_TYPE,                       // Sort by graphical element type
   SORT_BY_GRAPH_OBJ_SPECIES,                            // Sort by a graphical object species
   SORT_BY_GRAPH_OBJ_BELONG,                             //  Sort by a graphical element affiliation
   SORT_BY_GRAPH_OBJ_CHART_ID,                           // Sort by chart ID
   SORT_BY_GRAPH_OBJ_WND_NUM,                            // Sort by chart subwindow index
   SORT_BY_GRAPH_OBJ_NUM,                                // Sort by object index in the list
   SORT_BY_GRAPH_OBJ_CHANGE_HISTORY,                     // Sort by the flag of storing the change history
   SORT_BY_GRAPH_OBJ_GROUP,                              // Sort by the group of objects the graphical object belongs to
   SORT_BY_GRAPH_OBJ_CREATETIME,                         // Sort by object creation time
   SORT_BY_GRAPH_OBJ_TIMEFRAMES,                         // Sort by object visibility on timeframes
   SORT_BY_GRAPH_OBJ_BACK,                               // Sort by the "Background object" property
   SORT_BY_GRAPH_OBJ_ZORDER,                             // Sort by the priority of a graphical object for receiving the event of clicking on a chart
   SORT_BY_GRAPH_OBJ_HIDDEN,                             // Sort by a disabling display of the name of a graphical object in the terminal object list
   SORT_BY_GRAPH_OBJ_SELECTED,                           // Sort by the "Object selection" property
   SORT_BY_GRAPH_OBJ_SELECTABLE,                         // Sort by the "Object availability" property
   SORT_BY_GRAPH_OBJ_TIME,                               // Sort by time coordinate
   SORT_BY_GRAPH_OBJ_COLOR,                              // Sort by color
   SORT_BY_GRAPH_OBJ_STYLE,                              // Sort by style
   SORT_BY_GRAPH_OBJ_WIDTH,                              // Sort by line width
   SORT_BY_GRAPH_OBJ_FILL,                               // Sort by the "Object color filling" property
   SORT_BY_GRAPH_OBJ_READONLY,                           // Sort by the ability to edit text in the Edit object
   SORT_BY_GRAPH_OBJ_LEVELS,                             // Sort by number of levels
   SORT_BY_GRAPH_OBJ_LEVELCOLOR,                         // Sort by line level color
   SORT_BY_GRAPH_OBJ_LEVELSTYLE,                         // Sort by line level style
   SORT_BY_GRAPH_OBJ_LEVELWIDTH,                         // Sort by line level width
   SORT_BY_GRAPH_OBJ_ALIGN,                              // Sort by the "Horizontal text alignment in the Entry field" property
   SORT_BY_GRAPH_OBJ_FONTSIZE,                           // Sort by font size
   SORT_BY_GRAPH_OBJ_RAY_LEFT,                           // Sort by "Ray goes to the left" property
   SORT_BY_GRAPH_OBJ_RAY_RIGHT,                          // Sort by "Ray goes to the right" property
   SORT_BY_GRAPH_OBJ_RAY,                                // Sort by the "Vertical line goes through all windows of a chart" property
   SORT_BY_GRAPH_OBJ_ELLIPSE,                            // Sort by the "Display the full ellipse of the Fibonacci Arc object" property
   SORT_BY_GRAPH_OBJ_ARROWCODE,                          // Sort by an arrow code for the Arrow object
   SORT_BY_GRAPH_OBJ_ANCHOR,                             // Sort by the position of a binding point of a graphical object
   SORT_BY_GRAPH_OBJ_XDISTANCE,                          // Sort by a distance from the base corner along the X axis in pixels
   SORT_BY_GRAPH_OBJ_YDISTANCE,                          // Sort by a distance from the base corner along the Y axis in pixels
   SORT_BY_GRAPH_OBJ_DIRECTION,                          // Sort by the "Gann object trend" property
   SORT_BY_GRAPH_OBJ_DEGREE,                             // Sort by the "Elliott wave marking level" property
   SORT_BY_GRAPH_OBJ_DRAWLINES,                          // Sort by the "Display lines for Elliott wave marking" property
   SORT_BY_GRAPH_OBJ_STATE,                              // Sort by button state (pressed/released)
   SORT_BY_GRAPH_OBJ_OBJ_CHART_ID,                       // Sort by Chart object ID.
   SORT_BY_GRAPH_OBJ_CHART_OBJ_PERIOD,                   // Sort by Chart object period
   SORT_BY_GRAPH_OBJ_CHART_OBJ_DATE_SCALE,               // Sort by time scale display flag for the Chart object
   SORT_BY_GRAPH_OBJ_CHART_OBJ_PRICE_SCALE,              // Sort by price scale display flag for the Chart object
   SORT_BY_GRAPH_OBJ_CHART_OBJ_CHART_SCALE,              // Sort by Chart object scale
   SORT_BY_GRAPH_OBJ_XSIZE,                              // Sort by Object width along the X axis in pixels
   SORT_BY_GRAPH_OBJ_YSIZE,                              // Sort by object height along the Y axis in pixels
   SORT_BY_GRAPH_OBJ_XOFFSET,                            // Sort by X coordinate of the upper-left corner of the visibility area
   SORT_BY_GRAPH_OBJ_YOFFSET,                            // Sort by Y coordinate of the upper-left corner of the visibility area
   SORT_BY_GRAPH_OBJ_BGCOLOR,                            // Sort by background color for OBJ_EDIT, OBJ_BUTTON and OBJ_RECTANGLE_LABEL
   SORT_BY_GRAPH_OBJ_CORNER,                             // Sort by chart corner for binding a graphical object
   SORT_BY_GRAPH_OBJ_BORDER_TYPE,                        // Sort by border type for the "Rectangle border" object
   SORT_BY_GRAPH_OBJ_BORDER_COLOR,                       // Sort by frame color for the OBJ_EDIT and OBJ_BUTTON objects
//--- Sort by real properties
   SORT_BY_GRAPH_OBJ_PRICE = FIRST_GRAPH_OBJ_DBL_PROP,   // Sort by price coordinate
   SORT_BY_GRAPH_OBJ_LEVELVALUE,                         // Sort by level value
   SORT_BY_GRAPH_OBJ_SCALE,                              // Sort by scale (property of Gann objects and Fibonacci Arcs objects)
   SORT_BY_GRAPH_OBJ_ANGLE,                              // Sort by angle
   SORT_BY_GRAPH_OBJ_DEVIATION,                          // Sort by a deviation of the standard deviation channel
//--- Sort by string properties
   SORT_BY_GRAPH_OBJ_NAME = FIRST_GRAPH_OBJ_STR_PROP,    // Sort by object name
   SORT_BY_GRAPH_OBJ_TEXT,                               // Sort by object description
   SORT_BY_GRAPH_OBJ_TOOLTIP,                            // Sort by tooltip text
   SORT_BY_GRAPH_OBJ_LEVELTEXT,                          // Sort by level description
   SORT_BY_GRAPH_OBJ_FONT,                               // Sort by font
   SORT_BY_GRAPH_OBJ_BMPFILE,                            // Sort by BMP file name for the "Bitmap Level" object
   SORT_BY_GRAPH_OBJ_CHART_OBJ_SYMBOL,                   // Sort by Chart object period symbol
  };
//+------------------------------------------------------------------+


在 \MQL5\Include\DoEasy\Data.mqh 里,添加新的消息索引,并修复枚举常量的名称:

//--- GStdGraphObj
   MSG_GRAPH_STD_OBJ_ERR_FAILED_CREATE_CLASS_OBJ,     // Failed to create the class object for a graphical object 
   MSG_GRAPH_STD_OBJ_ERR_FAILED_CREATE_STD_GRAPH_OBJ, // Failed to create a graphical object 
   MSG_GRAPH_STD_OBJ_ERR_NOT_FIND_SUBWINDOW,          // Failed to find the chart subwindow
   MSG_GRAPH_STD_OBJ_ERR_FAILED_CREATE_SNAPSHOT,      // Failed to create a snapshot of the graphical object change history
   MSG_GRAPH_STD_OBJ_SUCCESS_CREATE_SNAPSHOT,         // Created a snapshot of the graphical object change history

...

   MSG_GRAPH_OBJ_PROP_ID,                             // Object ID
   MSG_GRAPH_OBJ_PROP_TYPE,                           // Graphical object type (ENUM_OBJECT)
   MSG_GRAPH_OBJ_PROP_ELEMENT_TYPE,                   // Graphical element type (ENUM_GRAPH_ELEMENT_TYPE)
   MSG_GRAPH_OBJ_PROP_BELONG,                         // Graphical object affiliation
   MSG_GRAPH_OBJ_PROP_CHART_ID,                       // Chart ID
   MSG_GRAPH_OBJ_PROP_WND_NUM,                        // Chart subwindow index
   MSG_GRAPH_OBJ_PROP_CHANGE_MEMORY,                  // Change history
   MSG_GRAPH_OBJ_PROP_CREATETIME,                     // Creation time
   MSG_GRAPH_OBJ_PROP_TIMEFRAMES,                     // Object visibility on timeframes

...

   MSG_GRAPH_OBJ_PROP_SPECIES,                        // Graphical object species
   MSG_GRAPH_OBJ_PROP_SPECIES_LINES,                  // Lines
   MSG_GRAPH_OBJ_PROP_SPECIES_CHANNELS,               // Channels
   MSG_GRAPH_OBJ_PROP_SPECIES_GANN,                   // Gann
   MSG_GRAPH_OBJ_PROP_SPECIES_FIBO,                   // Fibo
   MSG_GRAPH_OBJ_PROP_SPECIES_ELLIOTT,                // Elliott
   MSG_GRAPH_OBJ_PROP_SPECIES_SHAPES,                 // Shapes
   MSG_GRAPH_OBJ_PROP_SPECIES_ARROWS,                 // Arrows
   MSG_GRAPH_OBJ_PROP_SPECIES_GRAPHICAL,              // Graphical objects
   MSG_GRAPH_OBJ_PROP_GROUP,                          // Group of objects
   MSG_GRAPH_OBJ_TEXT_CLICK_COORD,                    // (Chart click coordinate)
   MSG_GRAPH_OBJ_TEXT_ANCHOR_TOP,                     // Arrow anchor point is located at the top
   MSG_GRAPH_OBJ_TEXT_ANCHOR_BOTTOM,                  // Arrow anchor point is located at the bottom

...

//--- CDataPropObj
   MSG_DATA_PROP_OBJ_OUT_OF_PROP_RANGE,               // Passed property is out of object property range
   MSG_GRAPH_OBJ_FAILED_CREATE_NEW_HIST_OBJ,          // Failed to create an object of the graphical object change history
   MSG_GRAPH_OBJ_FAILED_ADD_OBJ_TO_HIST_LIST,         // Failed to add the change history object to the list
   MSG_GRAPH_OBJ_FAILED_GET_HIST_OBJ,                 // Failed to receive the change history object
   MSG_GRAPH_OBJ_FAILED_INC_ARRAY_SIZE,               // Failed to increase the array size

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

//--- GStdGraphObj
   {"Не удалось создать объект класса для графического объекта ","Failed to create class object for graphic object"},
   {"Не удалось создать графический объект ","Failed to create graphic object "},
   {"Не удалось найти подокно графика","Could not find chart subwindow"},
   {"Не удалось создать снимок истории изменений графического объекта","Failed to create a snapshot of the change history of a graphic object"},
   {"Создан снимок истории изменений графического объекта","A snapshot of the history of changes to a graphical object has been created"},

...

   {"Идентификатор объекта","Object ID"},
   {"Тип объекта","Object type"},
   {"Тип графического элемента","Graphic element type"},
   {"Принадлежность объекта","Object belongs to"},
   {"Идентификатор графика объекта","Object chart ID"},
   {"Номер подокна графика","Chart subwindow number"},
   {"История изменений","Change history"},
   {"Время создания","Time of creation"},
   {"Видимость объекта на таймфреймах","Visibility of an object at timeframes"},

...

   {"Вид графического объекта","Graphic object species"},
   {"Линии","Lines"},
   {"Каналы","Channels"},
   {"Ганн","Gann"},
   {"Фибоначчи","Fibonacci"},
   {"Эллиотт","Elliott"},
   {"Фигуры","Shapes"},
   {"Стрелки","Arrows"},
   {"Графические объекты","Graphical"},
   {"Группа объектов","Object group"},

...

//--- CDataPropObj
   {"Переданное свойство находится за пределами диапазона свойств объекта","The passed property is outside the range of the object's properties"},
   {"Не удалось создать объект истории изменений графического объекта","Failed to create a graphical object change history object"},
   {"Не удалось добавить объект истории изменений в список","Failed to add change history object to the list"},
   {"Не удалось получить объект истории изменений","Failed to get change history object"},
   {"Не удалось увеличить размер массива","Failed to increase array size"},


我们需要针对 \MQL5\Include\DoEasy\Objects\Graph\Standard\GStdArrowBuyObj.mqh 作为示例)中的抽象标准图形对象派生的所有对象进行修改。

在类构造函数的初始化清单中,修复指示图形对象种类的枚举常量的名称

   //--- Constructor
                     CGStdArrowBuyObj(const long chart_id,const string name) : 
                        CGStdGraphObj(OBJECT_DE_TYPE_GSTD_ARROW_BUY,GRAPH_OBJ_BELONG_NO_PROGRAM,GRAPH_OBJ_SPECIES_ARROWS,chart_id,1,name)
                          {
                           //--- Specify the object property
                           CGStdGraphObj::SetProperty(GRAPH_OBJ_PROP_ANCHOR,0,ANCHOR_TOP);
                          }

在其它文件中,还会有其它类型的图形对象。 但在所有地方 _GROUP_ 都应替换为 _SPECIES_。

在对象返回所支持整数型属性标志的方法中,添加 “Flag of saving object change history(保存对象变更历史标志)”属性

//+------------------------------------------------------------------+
//| Return 'true' if an object supports a passed                     |
//| integer property, otherwise return 'false'                       |
//+------------------------------------------------------------------+
bool CGStdArrowBuyObj::SupportProperty(ENUM_GRAPH_OBJ_PROP_INTEGER property)
  {
   switch((int)property)
     {
      //--- Supported properties
      case GRAPH_OBJ_PROP_ID           :
      case GRAPH_OBJ_PROP_TYPE         :
      case GRAPH_OBJ_PROP_ELEMENT_TYPE : 
      case GRAPH_OBJ_PROP_GROUP        : 
      case GRAPH_OBJ_PROP_BELONG       :
      case GRAPH_OBJ_PROP_CHART_ID     :
      case GRAPH_OBJ_PROP_WND_NUM      :
      case GRAPH_OBJ_PROP_NUM          :
      case GRAPH_OBJ_PROP_CREATETIME   :
      case GRAPH_OBJ_PROP_CHANGE_HISTORY:
      case GRAPH_OBJ_PROP_TIMEFRAMES   :
      case GRAPH_OBJ_PROP_BACK         :
      case GRAPH_OBJ_PROP_ZORDER       :
      case GRAPH_OBJ_PROP_HIDDEN       :
      case GRAPH_OBJ_PROP_SELECTED     :
      case GRAPH_OBJ_PROP_SELECTABLE   :
      case GRAPH_OBJ_PROP_TIME         :
      case GRAPH_OBJ_PROP_COLOR        :
      case GRAPH_OBJ_PROP_STYLE        :
      case GRAPH_OBJ_PROP_WIDTH        :
      case GRAPH_OBJ_PROP_ANCHOR       : return true;
      //--- Other properties are not supported
      //--- Default is 'false'
      default: break;
     }
   return false;
  }
//+------------------------------------------------------------------+

在指定文件夹中进行了所有修改。 它们与已研究过的雷同,所以我不打算再复述它们。 您可在文后的附件中找到它们。

在所有函数库图形对象的基准图形对象文件 \MQL5\Include\DoEasy\objects\Graph\GBaseObj.mqh 中,添加存储图形对象种类和分组的变量:

//+------------------------------------------------------------------+
//| Class of the base object of the library graphical objects        |
//+------------------------------------------------------------------+
class CGBaseObj : public CObject
  {
protected:
   CArrayObj         m_list_events;                      // Object event list
   ENUM_OBJECT       m_type_graph_obj;                   // Graphical object type
   ENUM_GRAPH_ELEMENT_TYPE m_type_element;               // Graphical element type
   ENUM_GRAPH_OBJ_BELONG m_belong;                       // Program affiliation
   ENUM_GRAPH_OBJ_SPECIES m_species;                     // Graphical object species
   string            m_name_prefix;                      // Object name prefix
   string            m_name;                             // Object name
   long              m_chart_id;                         // Object chart ID
   long              m_object_id;                        // Object ID
   long              m_zorder;                           // Priority of a graphical object for receiving the mouse click event
   int               m_subwindow;                        // Subwindow index
   int               m_shift_y;                          // Subwindow Y coordinate shift
   int               m_type;                             // Object type
   int               m_timeframes_visible;               // Visibility of an object on timeframes (a set of flags)
   int               m_digits;                           // Number of decimal places in a quote
   int               m_group;                            // Graphical object group
   bool              m_visible;                          // Object visibility
   bool              m_back;                             // "Background object" flag
   bool              m_selected;                         // "Object selection" flag
   bool              m_selectable;                       // "Object availability" flag
   bool              m_hidden;                           // "Disable displaying the name of a graphical object in the terminal object list" flag
   datetime          m_create_time;                      // Object creation time

以及设置返回这些变量值的方法:

public:
//--- Set the values of the class variables
   void              SetObjectID(const long value)             { this.m_object_id=value;              }
   void              SetBelong(const ENUM_GRAPH_OBJ_BELONG belong){ this.m_belong=belong;             }
   void              SetTypeGraphObject(const ENUM_OBJECT obj) { this.m_type_graph_obj=obj;           }
   void              SetTypeElement(const ENUM_GRAPH_ELEMENT_TYPE type) { this.m_type_element=type;   }
   void              SetSpecies(const ENUM_GRAPH_OBJ_SPECIES species){ this.m_species=species;        }
   void              SetGroup(const int group)                 { this.m_group=group;                  }
   void              SetName(const string name)                { this.m_name=name;                    }
   void              SetChartID(const long chart_id)           { this.m_chart_id=chart_id;            }
   void              SetDigits(const int value)                { this.m_digits=value;                 }

...

//--- Return the values of class variables
   ENUM_GRAPH_ELEMENT_TYPE TypeGraphElement(void)        const { return this.m_type_element;       }
   ENUM_GRAPH_OBJ_BELONG   Belong(void)                  const { return this.m_belong;             }
   ENUM_GRAPH_OBJ_SPECIES  Species(void)                 const { return this.m_species;            }
   ENUM_OBJECT       TypeGraphObject(void)               const { return this.m_type_graph_obj;     }
   datetime          TimeCreate(void)                    const { return this.m_create_time;        }
   string            Name(void)                          const { return this.m_name;               }
   long              ChartID(void)                       const { return this.m_chart_id;           }
   long              ObjectID(void)                      const { return this.m_object_id;          }
   long              Zorder(void)                        const { return this.m_zorder;             }
   int               SubWindow(void)                     const { return this.m_subwindow;          }
   int               ShiftY(void)                        const { return this.m_shift_y;            }
   int               VisibleOnTimeframes(void)           const { return this.m_timeframes_visible; }
   int               Digits(void)                        const { return this.m_digits;             }
   int               Group(void)                         const { return this.m_group;              }
   bool              IsBack(void)                        const { return this.m_back;               }
   bool              IsSelected(void)                    const { return this.m_selected;           }
   bool              IsSelectable(void)                  const { return this.m_selectable;         }
   bool              IsHidden(void)                      const { return this.m_hidden;             }
   bool              IsVisible(void)                     const { return this.m_visible;            }

将之前返回图形对象描述的方法重命名为返回图形对象种类描述的方法

//--- Return the description of the type of the graphical object (1) type, (2) element, (3) affiliation and (4) species
string               TypeGraphObjectDescription(void);
string               TypeElementDescription(void);
string               BelongDescription(void);
string               SpeciesDescription(void);


并根据对象物种枚举常量的新名称修复其实现:

//+------------------------------------------------------------------+
//| Return the description of the graphical object group             |
//+------------------------------------------------------------------+
string CGBaseObj::SpeciesDescription(void)
  {
   return
     (
      this.Species()==GRAPH_OBJ_SPECIES_LINES      ?  CMessage::Text(MSG_GRAPH_OBJ_PROP_SPECIES_LINES)      :
      this.Species()==GRAPH_OBJ_SPECIES_CHANNELS   ?  CMessage::Text(MSG_GRAPH_OBJ_PROP_SPECIES_CHANNELS)   :
      this.Species()==GRAPH_OBJ_SPECIES_GANN       ?  CMessage::Text(MSG_GRAPH_OBJ_PROP_SPECIES_GANN)       :
      this.Species()==GRAPH_OBJ_SPECIES_FIBO       ?  CMessage::Text(MSG_GRAPH_OBJ_PROP_SPECIES_FIBO)       :
      this.Species()==GRAPH_OBJ_SPECIES_ELLIOTT    ?  CMessage::Text(MSG_GRAPH_OBJ_PROP_SPECIES_ELLIOTT)    :
      this.Species()==GRAPH_OBJ_SPECIES_SHAPES     ?  CMessage::Text(MSG_GRAPH_OBJ_PROP_SPECIES_SHAPES)     :
      this.Species()==GRAPH_OBJ_SPECIES_ARROWS     ?  CMessage::Text(MSG_GRAPH_OBJ_PROP_SPECIES_ARROWS)     :
      this.Species()==GRAPH_OBJ_SPECIES_GRAPHICAL  ?  CMessage::Text(MSG_GRAPH_OBJ_PROP_SPECIES_GRAPHICAL)  :
      "Unknown"
     );
  }

在类构造函数中,将默认对象分组设置为 0,这对应于省缺:

//+------------------------------------------------------------------+
//| Constructor                                                      |
//+------------------------------------------------------------------+
CGBaseObj::CGBaseObj() : m_shift_y(0),m_visible(false), m_name_prefix(::MQLInfoString(MQL_PROGRAM_NAME)+"_"),m_belong(GRAPH_OBJ_BELONG_PROGRAM)
  {
   this.m_list_events.Clear();                  // Clear the event list
   this.m_list_events.Sort();                   // Sorted list flag
   this.m_type=OBJECT_DE_TYPE_GBASE;            // Object type
   this.m_type_graph_obj=WRONG_VALUE;           // Graphical object type
   this.m_type_element=WRONG_VALUE;             // Graphical object type
   this.m_belong=WRONG_VALUE;                   // Program/terminal affiliation
   this.m_group=0;                              // Graphical object group
   this.m_name_prefix="";                       // Object name prefix
   this.m_name="";                              // Object name
   this.m_chart_id=0;                           // Object chart ID
   this.m_object_id=0;                          // Object ID
   this.m_zorder=0;                             // Priority of a graphical object for receiving the mouse click event
   this.m_subwindow=0;                          // Subwindow index
   this.m_shift_y=0;                            // Subwindow Y coordinate shift
   this.m_timeframes_visible=OBJ_ALL_PERIODS;   // Visibility of an object on timeframes (a set of flags)
   this.m_visible=true;                         // Object visibility
   this.m_back=false;                           // "Background object" flag
   this.m_selected=false;                       // "Object selection" flag
   this.m_selectable=false;                     // "Object availability" flag
   this.m_hidden=true;                          // "Disable displaying the name of a graphical object in the terminal object list" flag
   this.m_create_time=0;                        // Object creation time
  }
//+------------------------------------------------------------------+


标准图形对象记忆类

图形对象记忆类是一个对象列表,在任何图形对象属性(整数型、实数型和字符串型)发生变化时,这些对象都含有全部图形对象属性(整数型、实数型和字符串型)。 为什么不只保存变化的属性? 仅仅知道哪些属性已经变化是不够的。 我们还应该能设置对象的所有属性,从而获得其状态。 因此,我们将存储其属性的完整快照。 在此情况下,我们就能够把所有这些属性从对象记忆复制到其真实属性,而无需计算从记忆中获取哪些属性,以及从其当前状态获取哪些属性。

为了实现已变更对象属性的快照类,我将使用 CDATA PropObj 对象属性类。 但是,由于我们需要知道和考虑一些附加参数(变更时间、品种、及其小数位),所以变更对象属性快照类应从图形对象属性对象类继承。

图形对象记忆类包含已变更属性快照对象的列表,并提供处理这些列表和其中所包含对象的访问权限。

这两个类都位于 \MQL5\Include\DoEasy\Services\Properties.mqh 对象属性类文件当中。

在其中包括函数库服务函数的文件:

//+------------------------------------------------------------------+
//|                                                   Properties.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 "DELib.mqh"
#include "XDimArray.mqh"
//+------------------------------------------------------------------+

在对象属性类的公开部分,添加返回整数型、实数型和字符串型对象属性数的方法:

//--- Return the number of (1) integer, (2) real and (3) string parameters
   int               TotalLong(void)   const { return this.m_total_int; }
   int               TotalDouble(void) const { return this.m_total_dbl; }
   int               TotalString(void) const { return this.m_total_str; }

//--- Constructor

这些方法对于设置历史变更类对象的属性数量非常有用。

对象属性类之后是对象变更属性快照类:

//+------------------------------------------------------------------+
//| Object changed property snapshot class                           |
//+------------------------------------------------------------------+
class CChangedProps : public CDataPropObj
  {
private:
  long               m_time_change;                         // Property modification time
  string             m_symbol;                              // Chart window symbol
  int                m_digits;                              // Symbol's Digits
public:
//--- Set the (1) change time value, (2) symbol and (3) symbol's Digits
   void              SetTimeChanged(const long time)        { this.m_time_change=time;                   }
   void              SetSymbol(const string symbol)         { this.m_symbol=symbol;                      }
   void              SetDigits(const int digits)            { this.m_digits=digits;                      }
//--- Return the (1) change time value, (2) change time, (3) symbol and (4) symbol's Digits
   long              TimeChanged(void)                const { return this.m_time_change;                 }
   string            TimeChangedToString(void)        const { return TimeMSCtoString(this.m_time_change);}
   string            Symbol(void)                     const { return this.m_symbol;                      }
   int               Digits(void)                     const { return this.m_digits;                      }
//--- Constructor/destructor
                     CChangedProps (const int prop_total_integer,const int prop_total_double,const int prop_total_string,const long time_changed) : 
                        CDataPropObj(prop_total_integer,prop_total_double,prop_total_string) { this.m_time_change=time_changed;}
                    ~CChangedProps (void){;}
  };
//+------------------------------------------------------------------+

正如我们所见,该类是从图形对象属性对象类派生而来的。 故此,它拥有其父类的所有属性,以及类中的其它属性集合。 在此,我们只有对象修改时间(以毫秒为单位)、图形对象变更的图表品种,以及对象价格属性中正确显示品种小数位的长度。
在类构造函数中,传递整数型、实数型和字符串型属性的数量,及其变更时间(以毫秒为单位)。

如此,我们可以创建图形对象参数的副本,并将其放在参数变更历史类对象的列表当中。 该类在同一个文件中编写 — 就在对象变更属性快照类的正下方:

//+------------------------------------------------------------------+
//| Class of the history of graphical object property changes        |
//+------------------------------------------------------------------+
class CChangeHistory
  {
private:
  CArrayObj          m_list_changes;                  // List of the property change history
public:
//--- Return (1) the pointer to the property change history object and (2) the number of changes
   CChangedProps    *GetChangedPropsObj(const string source,const int index)
                       {
                        CChangedProps *props=this.m_list_changes.At(index<0 ? 0 : index);
                        if(props==NULL)
                           CMessage::ToLog(source,MSG_GRAPH_OBJ_FAILED_GET_HIST_OBJ);
                        return props;
                       }
   int               TotalChanges(void)               { return this.m_list_changes.Total();  }
//--- Create a new object of the graphical object property change history
   bool              CreateNewElement(CDataPropObj *element,const long time_change)
                       {
                        //--- Create a new object of the graphical object property snapshot
                        CChangedProps *obj=new CChangedProps(element.TotalLong(),element.TotalDouble(),element.TotalString(),time_change);
                        //--- If failed to create an object, inform of that and return 'false'
                        if(obj==NULL)
                          {
                           CMessage::ToLog(DFUN,MSG_GRAPH_OBJ_FAILED_CREATE_NEW_HIST_OBJ);
                           return false;
                          }
                        //--- If failed to add the object to the list, inform of that, remove the object and return 'false'
                        if(!this.m_list_changes.Add(obj))
                          {
                           CMessage::ToLog(DFUN,MSG_GRAPH_OBJ_FAILED_ADD_OBJ_TO_HIST_LIST);
                           delete obj;
                           return false;
                          }
                        //--- Get the ID of the chart the graphical object is located on
                        long chart_id=element.GetLong(GRAPH_OBJ_PROP_CHART_ID,0);
                        //--- Set a chart symbol and symbol's Digits for the graphical object property snapshot object
                        obj.SetSymbol(::ChartSymbol(chart_id));
                        obj.SetDigits((int)::SymbolInfoInteger(obj.Symbol(),SYMBOL_DIGITS));
                        //--- Copy all integer properties
                        for(int i=0;i<element.TotalLong();i++)
                          {
                           int total=element.Long().Size(i);
                           if(obj.SetSizeRange(i,total))
                             {
                              for(int r=0;r<total;r++)
                                 obj.Long().Set(i,r,element.Long().Get(i,r));
                             }
                           else
                              CMessage::ToLog(DFUN,MSG_GRAPH_OBJ_FAILED_INC_ARRAY_SIZE);
                          }
                        //--- Copy all real properties
                        for(int i=0;i<element.TotalDouble();i++)
                          {
                           int total=element.Double().Size(i);
                           if(obj.Double().SetSizeRange(i,total))
                             {
                              for(int r=0;r<total;r++)
                                 obj.Double().Set(i,r,element.Double().Get(i,r));
                             }
                           else
                              CMessage::ToLog(DFUN,MSG_GRAPH_OBJ_FAILED_INC_ARRAY_SIZE);
                          }
                        //--- Copy all string properties
                        for(int i=0;i<element.TotalString();i++)
                          {
                           int total=element.String().Size(i);
                           if(obj.String().SetSizeRange(i,total))
                             {
                              for(int r=0;r<total;r++)
                                 obj.String().Set(i,r,element.String().Get(i,r));
                             }
                           else
                              CMessage::ToLog(DFUN,MSG_GRAPH_OBJ_FAILED_INC_ARRAY_SIZE);
                          }
                        return true;
                       }
//--- Return by index in the list of the graphical object change history object
//--- the value from the specified index of the (1) long, (2) double and (3) string array
   long              GetLong(const int time_index,const ENUM_GRAPH_OBJ_PROP_INTEGER prop,const int index)
                       {
                        CChangedProps *properties=this.GetChangedPropsObj(DFUN,time_index);
                        if(properties==NULL)
                           return 0;
                        return properties.GetLong(prop,index);
                       }
   double            GetDouble(const int time_index,const ENUM_GRAPH_OBJ_PROP_DOUBLE prop,const int index)
                       {
                        CChangedProps *properties=this.GetChangedPropsObj(DFUN,time_index);
                        if(properties==NULL)
                           return 0;
                        return properties.GetDouble(prop,index);
                       }
   string            GetString(const int time_index,const ENUM_GRAPH_OBJ_PROP_STRING prop,const int index)
                       {
                        CChangedProps *properties=this.GetChangedPropsObj(DFUN,time_index);
                        if(properties==NULL)
                           return "";
                        return properties.GetString(prop,index);
                       }
//--- Constructor/destructor
                     CChangeHistory(void){;}
                    ~CChangeHistory(void){;}
  };
//+------------------------------------------------------------------+

该类也很简单。 它提供了一个列表,该列表将包含所有图形对象的变更,其对象由图形对象变更属性快照类表述。

为已变更属性创建新快照的方法接收当前(已变更)图形对象属性,和变更时间(以毫秒为单位)。 创建一个新的属性快照对象,并将其添加到列表当中。 进而,还要为对象设置其它参数。 然后, 把所有已变更图形对象的所有属性传递给该方法,并通过三个循环复制到所创建的对象。

因此,在更改每个图形对象时,会为其属性创建副本,并将其添加到列表之中。 因此,我们可以获得指向任何已保存属性对象的指针,并可随时将其用于程序。

所有的类方法都是雷同。 它们的逻辑相当透明:从列表中获取所需的属性对象,并从中返回所请求的属性。

您可以在下面的评论区中咨询有关这些方法的所有问题。 我相信,在此讲述它们并无什么意义。 在以前的文章中曾反复研究过这种方法的逻辑。

当前和以前的属性类数据接收指向已变更历史对象的指针,和返回图形对象变更次数的方法。 在类构造函数中,创建一个新的变更历史对象且需在析构函数中删除它

//+------------------------------------------------------------------+
//| Data class of the current and previous properties                |
//+------------------------------------------------------------------+
class CProperties : public CObject
  {
private:
   CArrayObj         m_list;  // List for storing the pointers to property objects
public:
   CDataPropObj     *Curr;    // Pointer to the current properties object
   CDataPropObj     *Prev;    // Pointer to the previous properties object
   CChangeHistory   *History; // Pointer to the change history object
//--- Set the array size ('size') in the specified dimension ('range')
   bool              SetSizeRange(const int range,const int size)
                       {
                        return(this.Curr.SetSizeRange(range,size) && this.Prev.SetSizeRange(range,size) ? true : false);
                       }
//--- Return the size of the specified array of the (1) current and (2) previous first dimension data
   int               CurrSize(const int range)  const { return Curr.Size(range); }
   int               PrevSize(const int range)  const { return Prev.Size(range); }
//--- Copy the current data to the previous one
   void              CurrentToPrevious(void)
                       {
                        //--- Copy all integer properties
                        for(int i=0;i<this.Curr.Long().Total();i++)
                           for(int r=0;r<this.Curr.Long().Size(i);r++)
                              this.Prev.Long().Set(i,r,this.Curr.Long().Get(i,r));
                        //--- Copy all real properties
                        for(int i=0;i<this.Curr.Double().Total();i++)
                           for(int r=0;r<this.Curr.Double().Size(i);r++)
                              this.Prev.Double().Set(i,r,this.Curr.Double().Get(i,r));
                        //--- Copy all string properties
                        for(int i=0;i<this.Curr.String().Total();i++)
                           for(int r=0;r<this.Curr.String().Size(i);r++)
                              this.Prev.String().Set(i,r,this.Curr.String().Get(i,r));
                       }
//--- Return the amount of graphical object changes since the start of recording them
   int               TotalChanges(void)   { return this.History.TotalChanges();  }
                       
//--- Constructor
                     CProperties(const int prop_int_total,const int prop_double_total,const int prop_string_total)
                       {
                        //--- Create new objects of the current and previous properties
                        this.Curr=new CDataPropObj(prop_int_total,prop_double_total,prop_string_total);
                        this.Prev=new CDataPropObj(prop_int_total,prop_double_total,prop_string_total);
                        //--- Add newly created objects to the list
                        this.m_list.Add(this.Curr);
                        this.m_list.Add(this.Prev);
                        //--- Create the change history object
                        this.History=new CChangeHistory();
                       }
//--- Destructor
                    ~CProperties()
                       {
                        this.m_list.Clear();
                        this.m_list.Shutdown();
                        if(this.History!=NULL)
                           delete this.History;
                       }
  };
//+------------------------------------------------------------------+

现在,每个图形对象都将在其属性中存储属性变更列表。

为了与图形对象变改历史进行交互,在 \MQL5\Include\DoEasy\Objects\Graph\Standard\GStdGraphObj.mqh 文件里(也就是说,在抽象标准图形对象类中),在对象属性中加入返回指向变更历史列表指针的方法

public:
//--- Set object's (1) integer, (2) real and (3) string properties
   void              SetProperty(ENUM_GRAPH_OBJ_PROP_INTEGER property,int index,long value)     { this.Prop.Curr.SetLong(property,index,value);    }
   void              SetProperty(ENUM_GRAPH_OBJ_PROP_DOUBLE property,int index,double value)    { this.Prop.Curr.SetDouble(property,index,value);  }
   void              SetProperty(ENUM_GRAPH_OBJ_PROP_STRING property,int index,string value)    { this.Prop.Curr.SetString(property,index,value);  }
//--- Return object’s (1) integer, (2) real and (3) string property from the properties array
   long              GetProperty(ENUM_GRAPH_OBJ_PROP_INTEGER property,int index)          const { return this.Prop.Curr.GetLong(property,index);   }
   double            GetProperty(ENUM_GRAPH_OBJ_PROP_DOUBLE property,int index)           const { return this.Prop.Curr.GetDouble(property,index); }
   string            GetProperty(ENUM_GRAPH_OBJ_PROP_STRING property,int index)           const { return this.Prop.Curr.GetString(property,index); }
//--- Set object's previous (1) integer, (2) real and (3) string properties
   void              SetPropertyPrev(ENUM_GRAPH_OBJ_PROP_INTEGER property,int index,long value) { this.Prop.Prev.SetLong(property,index,value);    }
   void              SetPropertyPrev(ENUM_GRAPH_OBJ_PROP_DOUBLE property,int index,double value){ this.Prop.Prev.SetDouble(property,index,value);  }
   void              SetPropertyPrev(ENUM_GRAPH_OBJ_PROP_STRING property,int index,string value){ this.Prop.Prev.SetString(property,index,value);  }
//--- Return object’s (1) integer, (2) real and (3) string property from the previous properties array
   long              GetPropertyPrev(ENUM_GRAPH_OBJ_PROP_INTEGER property,int index)      const { return this.Prop.Prev.GetLong(property,index);   }
   double            GetPropertyPrev(ENUM_GRAPH_OBJ_PROP_DOUBLE property,int index)       const { return this.Prop.Prev.GetDouble(property,index); }
   string            GetPropertyPrev(ENUM_GRAPH_OBJ_PROP_STRING property,int index)       const { return this.Prop.Prev.GetString(property,index); }
   
//--- Return (1) itself, (2) properties and (3) the change history
   CGStdGraphObj    *GetObject(void)                                       { return &this;            }
   CProperties      *Properties(void)                                      { return this.Prop;        }
   CChangeHistory   *History(void)                                         { return this.Prop.History;}
   
//--- Return the flag of the object supporting this property

在类的默认构造函数中,以种类指示替代对象分组指示。 在闭合参数构造函数中,传递对象种类,而非分组:

//--- Default constructor
                     CGStdGraphObj(){ this.m_type=OBJECT_DE_TYPE_GSTD_OBJ; this.m_species=WRONG_VALUE; }
//--- Destructor
                    ~CGStdGraphObj()
                       {
                        if(this.Prop!=NULL)
                           delete this.Prop;
                       }
protected:
//--- Protected parametric constructor
                     CGStdGraphObj(const ENUM_OBJECT_DE_TYPE obj_type,
                                   const ENUM_GRAPH_OBJ_BELONG belong,
                                   const ENUM_GRAPH_OBJ_SPECIES species,
                                   const long chart_id, const int pivots,
                                   const string name);

在简化访问和设置图形对象属性的方法模块中,添加设置和返回存储变更历史标志,和图形对象分组的方法:

public:
//+--------------------------------------------------------------------+ 
//|Methods of simplified access and setting graphical object properties|
//+--------------------------------------------------------------------+
//--- Object index in the list
   int               Number(void)                  const { return (int)this.GetProperty(GRAPH_OBJ_PROP_NUM,0);                            }
   void              SetNumber(const int number)         { this.SetProperty(GRAPH_OBJ_PROP_NUM,0,number);                                 }
//--- Flag of storing the change history
   bool              AllowChangeHistory(void)      const { return (bool)this.GetProperty(GRAPH_OBJ_PROP_CHANGE_HISTORY,0);                }
   void              SetAllowChangeMemory(const bool flag){ this.SetProperty(GRAPH_OBJ_PROP_CHANGE_HISTORY,0,flag);                       }
//--- Object ID
   long              ObjectID(void)                const { return this.GetProperty(GRAPH_OBJ_PROP_ID,0);                                  }
   void              SetObjectID(const long obj_id)
                       {
                        CGBaseObj::SetObjectID(obj_id);
                        this.SetProperty(GRAPH_OBJ_PROP_ID,0,obj_id);
                        this.SetPropertyPrev(GRAPH_OBJ_PROP_ID,0,obj_id);
                       }
//--- Graphical object type
   ENUM_OBJECT       GraphObjectType(void)         const { return (ENUM_OBJECT)this.GetProperty(GRAPH_OBJ_PROP_TYPE,0);                   }
   void              SetGraphObjectType(const ENUM_OBJECT obj_type)
                       {
                        CGBaseObj::SetTypeGraphObject(obj_type);
                        this.SetProperty(GRAPH_OBJ_PROP_TYPE,0,obj_type);
                       }
//--- Graphical element type
   ENUM_GRAPH_ELEMENT_TYPE GraphElementType(void)  const { return (ENUM_GRAPH_ELEMENT_TYPE)this.GetProperty(GRAPH_OBJ_PROP_ELEMENT_TYPE,0);}
   void              SetGraphElementType(const ENUM_GRAPH_ELEMENT_TYPE elm_type)
                       {
                        CGBaseObj::SetTypeElement(elm_type);
                        this.SetProperty(GRAPH_OBJ_PROP_ELEMENT_TYPE,0,elm_type);
                       }
//--- Graphical object affiliation
   ENUM_GRAPH_OBJ_BELONG Belong(void)              const { return (ENUM_GRAPH_OBJ_BELONG)this.GetProperty(GRAPH_OBJ_PROP_BELONG,0);       }
   void              SetBelong(const ENUM_GRAPH_OBJ_BELONG belong)
                       {
                        CGBaseObj::SetBelong(belong);
                        this.SetProperty(GRAPH_OBJ_PROP_BELONG,0,belong);
                       }
//--- Group of graphical objects
   int               Group(void)                   const { return (int)this.GetProperty(GRAPH_OBJ_PROP_GROUP,0);                          }
   void              SetGroup(const int group)
                       {
                        CGBaseObj::SetGroup(group);
                        this.SetProperty(GRAPH_OBJ_PROP_GROUP,0,group);
                       }
//--- Chart ID

默认情况下,图表上的每个图形对象都不会记录其变更历史。 若要开始执行此操作,需调用方法设置允许记录变更历史的标志。 图形对象分组包括图表上的各种图形对象,它们组合成一个分组,可用于选择它们,来执行必要的操作。

添加处理对象变更历史记录的方法:

//--- Re-write all graphical object properties
   void              PropertiesRefresh(void);
//--- Check object property changes
   void              PropertiesCheckChanged(void);
//--- Copy the current data to the previous one
   void              PropertiesCopyToPrevData(void);
//--- Return (1) the number of property changes in history specified (2) by the property index, (3) the last and (4) the first changed object
   int               HistoryChangesTotal(void)                          { return this.History().TotalChanges();                                          }
   CChangedProps    *GetHistoryChangedProps(const string source,const int index) { return this.History().GetChangedPropsObj(source,index);               }
   CChangedProps    *GetHistoryChangedPropsLast(const string source)    { return this.History().GetChangedPropsObj(source,this.HistoryChangesTotal()-1); }
   CChangedProps    *GetHistoryChangedPropsFirst(const string source)   { return this.History().GetChangedPropsObj(source,0);                            }
//--- Using the specified index in the list of change history objects, return
//--- the specified value of (1) integer, (2) real and (3) string property
   long              HistoryChangedObjGetLong(const int time_index,const ENUM_GRAPH_OBJ_PROP_INTEGER prop,const int prop_index)
                       {
                        CChangedProps *obj=this.GetHistoryChangedProps(DFUN,time_index);
                        return(obj!=NULL ? obj.GetLong(prop,prop_index) : 0);
                       }
   double            HistoryChangedObjGetDouble(const int time_index,const ENUM_GRAPH_OBJ_PROP_DOUBLE prop,const int prop_index)
                       {
                        CChangedProps *obj=this.GetHistoryChangedProps(DFUN,time_index);
                        return(obj!=NULL ? obj.GetDouble(prop,prop_index) : 0);
                       }
   string            HistoryChangedObjGetString(const int time_index,const ENUM_GRAPH_OBJ_PROP_STRING prop,const int prop_index)
                       {
                        CChangedProps *obj=this.GetHistoryChangedProps(DFUN,time_index);
                        return(obj!=NULL ? obj.GetString(prop,prop_index) : "ERROR");
                       }
//--- Return (1) a symbol, (2) symbol's Digits and (3) the time of changing the change history object
   string            HistoryChangedObjSymbol(const int time_index)
                       {
                        CChangedProps *obj=this.GetHistoryChangedProps(DFUN,time_index);
                        return(obj!=NULL ? obj.Symbol() : "ERROR");
                       }
   int               HistoryChangedObjDigits(const int time_index)
                       {
                        CChangedProps *obj=this.GetHistoryChangedProps(DFUN,time_index);
                        return(obj!=NULL ? obj.Digits() : 0);
                       }
   long              HistoryChangedObjTimeChanged(const int time_index)
                       {
                        CChangedProps *obj=this.GetHistoryChangedProps(DFUN,time_index);
                        return(obj!=NULL ? obj.TimeChanged() : 0);
                       }
   string            HistoryChangedObjTimeChangedToString(const int time_index)
                       {
                        CChangedProps *obj=this.GetHistoryChangedProps(DFUN,time_index);
                        return(obj!=NULL ? obj.TimeChangedToString() : "ERROR");
                       }
//--- Set object parameters from the specified history snapshot
   bool              SetPropertiesFromHistory(const int time_index)
                       {
                        CChangedProps *obj=this.GetHistoryChangedProps(DFUN,time_index);
                        if(obj==NULL)
                           return false;
                        int begin=0, end=GRAPH_OBJ_PROP_INTEGER_TOTAL;
                        for(int i=begin; i<end; i++)
                          {
                           ENUM_GRAPH_OBJ_PROP_INTEGER prop=(ENUM_GRAPH_OBJ_PROP_INTEGER)i;
                           for(int j=0;j<this.Prop.CurrSize(prop);j++)
                              if(this.GetProperty(prop,j)!=obj.GetLong(prop,j))
                                 this.SetHistoryINT(prop,obj.GetLong(prop,j),j);
                          }
                        begin=end; end+=GRAPH_OBJ_PROP_DOUBLE_TOTAL;
                        for(int i=begin; i<end; i++)
                          {
                           ENUM_GRAPH_OBJ_PROP_DOUBLE prop=(ENUM_GRAPH_OBJ_PROP_DOUBLE)i;
                           for(int j=0;j<this.Prop.CurrSize(prop);j++)
                             {
                              if(this.GetProperty(prop,j)!=obj.GetDouble(prop,j))
                                 this.SetHistoryDBL(prop,obj.GetDouble(prop,j),j);
                             }
                          }
                        begin=end; end+=GRAPH_OBJ_PROP_STRING_TOTAL;
                        for(int i=begin; i<end; i++)
                          {
                           ENUM_GRAPH_OBJ_PROP_STRING prop=(ENUM_GRAPH_OBJ_PROP_STRING)i;
                           for(int j=0;j<this.Prop.CurrSize(prop);j++)
                              if(this.GetProperty(prop,j)!=obj.GetString(prop,j))
                                 this.SetHistorySTR(prop,obj.GetString(prop,j),j);
                          }
                        return true;
                       }

几乎所有方法都调用图形对象变更历史类的同名方法,并返回结果。

从指定的历史快照里设置图形对象属性的方法,通过索引接收对象,并在三重循环中调用 SetHistoryINT(),SetHistoryDBL() 和 SetHistorySTR() 方法(如下所述),将历史快照对象的所有属性设置到图形对象。

将方法添加到类的私密部分:

private:
//--- Get and save (1) integer, (2) real and (3) string properties
   void              GetAndSaveINT(void);
   void              GetAndSaveDBL(void);
   void              GetAndSaveSTR(void);
   
//--- Create a new object of the graphical object change history
   bool              CreateNewChangeHistoryObj(const bool first)
                       {
                        bool res=true;
                        if(first)
                           res &=this.History().CreateNewElement(this.Prop.Prev,this.GetMarketWatchTime());
                        res &=this.History().CreateNewElement(this.Prop.Curr,this.GetMarketWatchTime());
                        if(!res)
                           CMessage::ToLog(DFUN,MSG_GRAPH_STD_OBJ_ERR_FAILED_CREATE_SNAPSHOT);
                        return res;
                       }
//--- Set (1) integer, (2) real and (3) string property values from the change history
   void              SetHistoryINT(const ENUM_GRAPH_OBJ_PROP_INTEGER prop,const long value,const int modifier);
   void              SetHistoryDBL(const ENUM_GRAPH_OBJ_PROP_DOUBLE prop,const double value,const int modifier);
   void              SetHistorySTR(const ENUM_GRAPH_OBJ_PROP_STRING prop,const string value,const int modifier);
//--- Return the time of the last symbol tick
   long              GetSymbolTime(const string symbol)
                       {
                        MqlTick tick;
                        return(::SymbolInfoTick(symbol,tick) ? tick.time_msc : 0);
                       }
//--- Return the time of the last Market Watch tick
   long              GetMarketWatchTime(void)
                       {
                        long res=0;
                        for(int i=::SymbolsTotal(true)-1;i>WRONG_VALUE;i--)
                          {
                           const long time=this.GetSymbolTime(::SymbolName(i,true));
                           if(time>res)
                              res=time;
                          }
                        return res;
                       }   
  };
//+------------------------------------------------------------------+

创建图形对象变更历史新对象的方法传递初次图形对象变更的标志
如果设置了标志,则这是第一次变更。 首先,我们需要保存历史记录中以前的图形对象状态(在属性变更之前)。 接下来,将当前对象状态写入历史记录。 如果未设置任何标志,则在变更历史记录中保存当前对象状态:

//--- Create a new object of the graphical object change history
   bool              CreateNewChangeHistoryObj(const bool first)
                       {
                        bool res=true;
                        if(first)
                           res &=this.History().CreateNewElement(this.Prop.Prev,this.GetMarketWatchTime());
                        res &=this.History().CreateNewElement(this.Prop.Curr,this.GetMarketWatchTime());
                        if(!res)
                           CMessage::ToLog(DFUN,MSG_GRAPH_STD_OBJ_ERR_FAILED_CREATE_SNAPSHOT);
                        return res;
                       }

把调用方法的结果添加到生成的 res 变量中,且仅当任何被调用方法返回 false 时才等于 false。 结果就是,返回变量值。

返回上次市场观察即时报价时间的方法,迭代市场观察窗口中的所有品种,以毫秒为单位读取当前时间,并比较每个品种的时间从而返回最新时间

//--- Return the time of the last Market Watch tick
   long              GetMarketWatchTime(void)
                       {
                        long res=0;
                        for(int i=::SymbolsTotal(true)-1;i>WRONG_VALUE;i--)
                          {
                           const long time=this.GetSymbolTime(::SymbolName(i,true));
                           if(time>res)
                              res=time;
                          }
                        return res;
                       }   

在类的受保护参数化构造函数中,传递图形对象种类(替代分组),并为对象设置所有新属性

//+------------------------------------------------------------------+
//| Protected parametric constructor                                 |
//+------------------------------------------------------------------+
CGStdGraphObj::CGStdGraphObj(const ENUM_OBJECT_DE_TYPE obj_type,
                             const ENUM_GRAPH_OBJ_BELONG belong,
                             const ENUM_GRAPH_OBJ_SPECIES species,
                             const long chart_id,const int pivots,
                             const string name)
  {
   //--- Create the property object with the default values
   this.Prop=new CProperties(GRAPH_OBJ_PROP_INTEGER_TOTAL,GRAPH_OBJ_PROP_DOUBLE_TOTAL,GRAPH_OBJ_PROP_STRING_TOTAL);
   
//--- Set the number of pivot points and object levels
   this.m_pivots=pivots;
   int levels=(int)::ObjectGetInteger(chart_id,name,OBJPROP_LEVELS);

//--- Set the property array dimensionalities according to the number of pivot points and levels
   this.Prop.SetSizeRange(GRAPH_OBJ_PROP_TIME,this.m_pivots);
   this.Prop.SetSizeRange(GRAPH_OBJ_PROP_PRICE,this.m_pivots);
   this.Prop.SetSizeRange(GRAPH_OBJ_PROP_LEVELCOLOR,levels);
   this.Prop.SetSizeRange(GRAPH_OBJ_PROP_LEVELSTYLE,levels);
   this.Prop.SetSizeRange(GRAPH_OBJ_PROP_LEVELWIDTH,levels);
   this.Prop.SetSizeRange(GRAPH_OBJ_PROP_LEVELVALUE,levels);
   this.Prop.SetSizeRange(GRAPH_OBJ_PROP_LEVELTEXT,levels);
   this.Prop.SetSizeRange(GRAPH_OBJ_PROP_BMPFILE,2);
   
//--- Set the object (1) type, type of graphical (2) object, (3) element, (4) subwindow affiliation and (5) index, as well as (6) chart symbol Digits
   this.m_type=obj_type;
   this.SetName(name);
   CGBaseObj::SetChartID(chart_id);
   CGBaseObj::SetTypeGraphObject(CGBaseObj::GraphObjectType(obj_type));
   CGBaseObj::SetTypeElement(GRAPH_ELEMENT_TYPE_STANDARD);
   CGBaseObj::SetBelong(belong);
   CGBaseObj::SetSpecies(species);
   CGBaseObj::SetSubwindow(chart_id,name);
   CGBaseObj::SetDigits((int)::SymbolInfoInteger(::ChartSymbol(chart_id),SYMBOL_DIGITS));
   
//--- Save the integer properties inherent in all graphical objects but not present in the current one
   this.SetProperty(GRAPH_OBJ_PROP_CHART_ID,0,CGBaseObj::ChartID());                // Chart ID
   this.SetProperty(GRAPH_OBJ_PROP_WND_NUM,0,CGBaseObj::SubWindow());               // Chart subwindow index
   this.SetProperty(GRAPH_OBJ_PROP_TYPE,0,CGBaseObj::TypeGraphObject());            // Graphical object type (ENUM_OBJECT)
   this.SetProperty(GRAPH_OBJ_PROP_ELEMENT_TYPE,0,CGBaseObj::TypeGraphElement());   // Graphical element type (ENUM_GRAPH_ELEMENT_TYPE)
   this.SetProperty(GRAPH_OBJ_PROP_BELONG,0,CGBaseObj::Belong());                   // Graphical object affiliation
   this.SetProperty(GRAPH_OBJ_PROP_SPECIES,0,CGBaseObj::Species());                 // Graphical object species
   this.SetProperty(GRAPH_OBJ_PROP_GROUP,0,0);                                      // Graphical object group
   this.SetProperty(GRAPH_OBJ_PROP_ID,0,0);                                         // Object ID
   this.SetProperty(GRAPH_OBJ_PROP_NUM,0,0);                                        // Object index in the list
   this.SetProperty(GRAPH_OBJ_PROP_CHANGE_HISTORY,0,false);                         // Flag of storing the change history<
   
//--- Save the properties inherent in all graphical objects and present in a graphical object
   this.PropertiesRefresh();
   
//--- Save basic properties in the parent object
   this.m_create_time=(datetime)this.GetProperty(GRAPH_OBJ_PROP_CREATETIME,0);
   this.m_back=(bool)this.GetProperty(GRAPH_OBJ_PROP_BACK,0);
   this.m_selected=(bool)this.GetProperty(GRAPH_OBJ_PROP_SELECTED,0);
   this.m_selectable=(bool)this.GetProperty(GRAPH_OBJ_PROP_SELECTABLE,0);
   this.m_hidden=(bool)this.GetProperty(GRAPH_OBJ_PROP_HIDDEN,0);

//--- Save the current properties to the previous ones
   this.PropertiesCopyToPrevData();
  }
//+-------------------------------------------------------------------+


返回对象整数型属性描述的方法将接收新对象属性的描述

//+------------------------------------------------------------------+
//| Return description of object's integer property                  |
//+------------------------------------------------------------------+
string CGStdGraphObj::GetPropertyDescription(ENUM_GRAPH_OBJ_PROP_INTEGER property)
  {
   return
     (
      property==GRAPH_OBJ_PROP_ID         ?  CMessage::Text(MSG_GRAPH_OBJ_PROP_ID)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)this.GetProperty(property,0)
         )  :
      property==GRAPH_OBJ_PROP_TYPE       ?  CMessage::Text(MSG_GRAPH_OBJ_PROP_TYPE)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+this.TypeDescription()
         )  :
      property==GRAPH_OBJ_PROP_ELEMENT_TYPE  ?  CMessage::Text(MSG_GRAPH_OBJ_PROP_ELEMENT_TYPE)+
         (!this.SupportProperty(property)    ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+CGBaseObj::TypeElementDescription()
         )  :
      property==GRAPH_OBJ_PROP_SPECIES    ?  CMessage::Text(MSG_GRAPH_OBJ_PROP_SPECIES)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+CGBaseObj::SpeciesDescription()
         )  :
      property==GRAPH_OBJ_PROP_GROUP    ?  CMessage::Text(MSG_GRAPH_OBJ_PROP_GROUP)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(CGBaseObj::Group()>0 ? (string)this.GetProperty(property,0) : CMessage::Text(MSG_LIB_PROP_EMPTY))
         )  :
      property==GRAPH_OBJ_PROP_BELONG     ?  CMessage::Text(MSG_GRAPH_OBJ_PROP_BELONG)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+CGBaseObj::BelongDescription()
         )  :
      property==GRAPH_OBJ_PROP_CHART_ID   ?  CMessage::Text(MSG_GRAPH_OBJ_PROP_CHART_ID)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)this.GetProperty(property,0)
         )  :
      property==GRAPH_OBJ_PROP_WND_NUM    ?  CMessage::Text(MSG_GRAPH_OBJ_PROP_WND_NUM)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)this.GetProperty(property,0)
         )  :
      property==GRAPH_OBJ_PROP_CHANGE_HISTORY ?  CMessage::Text(MSG_GRAPH_OBJ_PROP_CHANGE_MEMORY)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(this.GetProperty(property,0) ? CMessage::Text(MSG_LIB_TEXT_YES) : CMessage::Text(MSG_LIB_TEXT_NO))
         )  :
      property==GRAPH_OBJ_PROP_CREATETIME   ?  CMessage::Text(MSG_GRAPH_OBJ_PROP_CREATETIME)+
         (!this.SupportProperty(property)    ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+::TimeToString(this.GetProperty(property,0),TIME_DATE|TIME_MINUTES|TIME_SECONDS)
         )  :
      property==GRAPH_OBJ_PROP_TIMEFRAMES ?  CMessage::Text(MSG_GRAPH_OBJ_PROP_TIMEFRAMES)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+this.VisibleOnTimeframeDescription()
         )  :
      property==GRAPH_OBJ_PROP_BACK       ?  CMessage::Text(MSG_GRAPH_OBJ_PROP_BACK)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(this.IsBack() ? CMessage::Text(MSG_LIB_TEXT_YES) : CMessage::Text(MSG_LIB_TEXT_NO))
         )  :
      property==GRAPH_OBJ_PROP_ZORDER     ?  CMessage::Text(MSG_GRAPH_OBJ_PROP_ZORDER)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)this.GetProperty(property,0)
         )  :
      property==GRAPH_OBJ_PROP_HIDDEN     ?  CMessage::Text(MSG_GRAPH_OBJ_PROP_HIDDEN)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(this.IsHidden() ? CMessage::Text(MSG_LIB_TEXT_YES) : CMessage::Text(MSG_LIB_TEXT_NO))
         )  :
      property==GRAPH_OBJ_PROP_SELECTED   ?  CMessage::Text(MSG_GRAPH_OBJ_PROP_SELECTED)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(this.IsSelected() ? CMessage::Text(MSG_LIB_TEXT_YES) : CMessage::Text(MSG_LIB_TEXT_NO))
         )  :
      property==GRAPH_OBJ_PROP_SELECTABLE ?  CMessage::Text(MSG_GRAPH_OBJ_PROP_SELECTABLE)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(this.IsSelectable() ? CMessage::Text(MSG_LIB_TEXT_YES) : CMessage::Text(MSG_LIB_TEXT_NO))
         )  :
      property==GRAPH_OBJ_PROP_NUM        ?  CMessage::Text(MSG_GRAPH_OBJ_PROP_NUM)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)this.GetProperty(property,0)
         )  :
      property==GRAPH_OBJ_PROP_TIME       ?  CMessage::Text(MSG_GRAPH_OBJ_PROP_TIME)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+"\n"+this.TimesDescription()
         )  :
      property==GRAPH_OBJ_PROP_COLOR      ?  CMessage::Text(MSG_GRAPH_OBJ_PROP_COLOR)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+::ColorToString((color)this.GetProperty(property,0),true)
         )  :
      property==GRAPH_OBJ_PROP_STYLE      ?  CMessage::Text(MSG_GRAPH_OBJ_PROP_STYLE)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+LineStyleDescription((ENUM_LINE_STYLE)this.GetProperty(property,0))
         )  :
      property==GRAPH_OBJ_PROP_WIDTH     ?  CMessage::Text(MSG_GRAPH_OBJ_PROP_WIDTH)+
         (!this.SupportProperty(property)    ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)this.GetProperty(property,0)
         )  :
      property==GRAPH_OBJ_PROP_FILL       ?  CMessage::Text(MSG_GRAPH_OBJ_PROP_FILL)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(this.GetProperty(property,0) ? CMessage::Text(MSG_LIB_TEXT_YES) : CMessage::Text(MSG_LIB_TEXT_NO))
         )  :
      property==GRAPH_OBJ_PROP_READONLY   ?  CMessage::Text(MSG_GRAPH_OBJ_PROP_READONLY)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(this.GetProperty(property,0) ? CMessage::Text(MSG_LIB_TEXT_YES) : CMessage::Text(MSG_LIB_TEXT_NO))
         )  :
      property==GRAPH_OBJ_PROP_LEVELS     ?  CMessage::Text(MSG_GRAPH_OBJ_PROP_LEVELS)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)this.GetProperty(property,0)
         )  :
      property==GRAPH_OBJ_PROP_LEVELCOLOR ?  CMessage::Text(MSG_GRAPH_OBJ_PROP_LEVELCOLOR)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ":\n"+this.LevelsColorDescription()
         )  :
      property==GRAPH_OBJ_PROP_LEVELSTYLE ?  CMessage::Text(MSG_GRAPH_OBJ_PROP_LEVELSTYLE)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ":\n"+this.LevelsStyleDescription()
         )  :
      property==GRAPH_OBJ_PROP_LEVELWIDTH ?  CMessage::Text(MSG_GRAPH_OBJ_PROP_LEVELWIDTH)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ":\n"+this.LevelsWidthDescription()
         )  :
      property==GRAPH_OBJ_PROP_ALIGN      ?  CMessage::Text(MSG_GRAPH_OBJ_PROP_ALIGN)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+AlignModeDescription((ENUM_ALIGN_MODE)this.GetProperty(property,0))
         )  :
      property==GRAPH_OBJ_PROP_FONTSIZE   ?  CMessage::Text(MSG_GRAPH_OBJ_PROP_FONTSIZE)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)this.GetProperty(property,0)
         )  :
      property==GRAPH_OBJ_PROP_RAY_LEFT   ?  CMessage::Text(MSG_GRAPH_OBJ_PROP_RAY_LEFT)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(this.GetProperty(property,0) ? CMessage::Text(MSG_LIB_TEXT_YES) : CMessage::Text(MSG_LIB_TEXT_NO))
         )  :
      property==GRAPH_OBJ_PROP_RAY_RIGHT  ?  CMessage::Text(MSG_GRAPH_OBJ_PROP_RAY_RIGHT)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(this.GetProperty(property,0) ? CMessage::Text(MSG_LIB_TEXT_YES) : CMessage::Text(MSG_LIB_TEXT_NO))
         )  :
      property==GRAPH_OBJ_PROP_RAY        ?  CMessage::Text(MSG_GRAPH_OBJ_PROP_RAY)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(this.GetProperty(property,0) ? CMessage::Text(MSG_LIB_TEXT_YES) : CMessage::Text(MSG_LIB_TEXT_NO))
         )  :
      property==GRAPH_OBJ_PROP_ELLIPSE    ?  CMessage::Text(MSG_GRAPH_OBJ_PROP_ELLIPSE)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(this.GetProperty(property,0) ? CMessage::Text(MSG_LIB_TEXT_YES) : CMessage::Text(MSG_LIB_TEXT_NO))
         )  :
      property==GRAPH_OBJ_PROP_ARROWCODE  ?  CMessage::Text(MSG_GRAPH_OBJ_PROP_ARROWCODE)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)this.GetProperty(property,0)
         )  :
      property==GRAPH_OBJ_PROP_ANCHOR     ?  CMessage::Text(MSG_GRAPH_OBJ_PROP_ANCHOR)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+this.AnchorDescription()
         )  :
      property==GRAPH_OBJ_PROP_XDISTANCE  ?  CMessage::Text(MSG_GRAPH_OBJ_PROP_XDISTANCE)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)this.GetProperty(property,0)
         )  :
      property==GRAPH_OBJ_PROP_YDISTANCE  ?  CMessage::Text(MSG_GRAPH_OBJ_PROP_YDISTANCE)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)this.GetProperty(property,0)
         )  :
      property==GRAPH_OBJ_PROP_DIRECTION  ?  CMessage::Text(MSG_GRAPH_OBJ_PROP_DIRECTION)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+GannDirectDescription((ENUM_GANN_DIRECTION)this.GetProperty(property,0))
         )  :
      property==GRAPH_OBJ_PROP_DEGREE     ?  CMessage::Text(MSG_GRAPH_OBJ_PROP_DEGREE)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+ElliotWaveDegreeDescription((ENUM_ELLIOT_WAVE_DEGREE)this.GetProperty(property,0))
         )  :
      property==GRAPH_OBJ_PROP_DRAWLINES  ?  CMessage::Text(MSG_GRAPH_OBJ_PROP_DRAWLINES)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(this.GetProperty(property,0) ? CMessage::Text(MSG_LIB_TEXT_YES) : CMessage::Text(MSG_LIB_TEXT_NO))
         )  :
      property==GRAPH_OBJ_PROP_STATE      ?  CMessage::Text(MSG_GRAPH_OBJ_PROP_STATE)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(this.GetProperty(property,0) ? CMessage::Text(MSG_LIB_TEXT_BUTTON_STATE_PRESSED) : CMessage::Text(MSG_LIB_TEXT_BUTTON_STATE_DEPRESSED))
         )  :
      property==GRAPH_OBJ_PROP_CHART_OBJ_CHART_ID  ?  CMessage::Text(MSG_CHART_OBJ_ID)+
         (!this.SupportProperty(property)    ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)this.GetProperty(property,0)
         )  :
      property==GRAPH_OBJ_PROP_CHART_OBJ_PERIOD ?  CMessage::Text(MSG_GRAPH_OBJ_PROP_CHART_OBJ_PERIOD)+
         (!this.SupportProperty(property)       ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+TimeframeDescription((ENUM_TIMEFRAMES)this.GetProperty(property,0))
         )  :
      property==GRAPH_OBJ_PROP_CHART_OBJ_DATE_SCALE   ?  CMessage::Text(MSG_GRAPH_OBJ_PROP_CHART_OBJ_DATE_SCALE)+
         (!this.SupportProperty(property)             ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(this.GetProperty(property,0) ? CMessage::Text(MSG_LIB_TEXT_YES) : CMessage::Text(MSG_LIB_TEXT_NO))
         )  :
      property==GRAPH_OBJ_PROP_CHART_OBJ_PRICE_SCALE  ?  CMessage::Text(MSG_GRAPH_OBJ_PROP_CHART_OBJ_PRICE_SCALE)+
         (!this.SupportProperty(property)             ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(this.GetProperty(property,0) ? CMessage::Text(MSG_LIB_TEXT_YES) : CMessage::Text(MSG_LIB_TEXT_NO))
         )  :
      property==GRAPH_OBJ_PROP_CHART_OBJ_CHART_SCALE  ?  CMessage::Text(MSG_GRAPH_OBJ_PROP_CHART_OBJ_CHART_SCALE)+
         (!this.SupportProperty(property)             ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)this.GetProperty(property,0)
         )  :
      property==GRAPH_OBJ_PROP_XSIZE      ?  CMessage::Text(MSG_GRAPH_OBJ_PROP_XSIZE)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)this.GetProperty(property,0)
         )  :
      property==GRAPH_OBJ_PROP_YSIZE      ?  CMessage::Text(MSG_GRAPH_OBJ_PROP_YSIZE)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)this.GetProperty(property,0)
         )  :
      property==GRAPH_OBJ_PROP_XOFFSET    ?  CMessage::Text(MSG_GRAPH_OBJ_PROP_XOFFSET)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)this.GetProperty(property,0)
         )  :
      property==GRAPH_OBJ_PROP_YOFFSET    ?  CMessage::Text(MSG_GRAPH_OBJ_PROP_YOFFSET)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)this.GetProperty(property,0)
         )  :
      property==GRAPH_OBJ_PROP_BGCOLOR    ?  CMessage::Text(MSG_GRAPH_OBJ_PROP_BGCOLOR)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+::ColorToString((color)this.GetProperty(property,0),true)
         )  :
      property==GRAPH_OBJ_PROP_CORNER     ?  CMessage::Text(MSG_GRAPH_OBJ_PROP_CORNER)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+BaseCornerDescription((ENUM_BASE_CORNER)this.GetProperty(property,0))
         )  :
      property==GRAPH_OBJ_PROP_BORDER_TYPE   ?  CMessage::Text(MSG_GRAPH_OBJ_PROP_BORDER_TYPE)+
         (!this.SupportProperty(property)    ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+BorderTypeDescription((ENUM_BORDER_TYPE)this.GetProperty(property,0))
         )  :
      property==GRAPH_OBJ_PROP_BORDER_COLOR  ?  CMessage::Text(MSG_GRAPH_OBJ_PROP_BORDER_COLOR)+
         (!this.SupportProperty(property)    ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+::ColorToString((color)this.GetProperty(property,0),true)
         )  :
      ""
     );
  }
//+------------------------------------------------------------------+

在检查对象属性变更的方法中,添加创建图形对象变改历史新对象的代码模块前提是设置了允许记录历史变更的标志

//+------------------------------------------------------------------+
//| Check object property changes                                    |
//+------------------------------------------------------------------+
void CGStdGraphObj::PropertiesCheckChanged(void)
  {
   CGBaseObj::ClearEventsList();
   bool changed=false;
   int begin=0, end=GRAPH_OBJ_PROP_INTEGER_TOTAL;
   for(int i=begin; i<end; i++)
     {
      ENUM_GRAPH_OBJ_PROP_INTEGER prop=(ENUM_GRAPH_OBJ_PROP_INTEGER)i;
      if(!this.SupportProperty(prop)) continue;
      for(int j=0;j<Prop.CurrSize(prop);j++)
        {
         if(this.GetProperty(prop,j)!=this.GetPropertyPrev(prop,j))
           {
            changed=true;
            this.CreateAndAddNewEvent(GRAPH_OBJ_EVENT_CHANGE,this.ChartID(),prop,this.Name());
           }
        }
     }

   begin=end; end+=GRAPH_OBJ_PROP_DOUBLE_TOTAL;
   for(int i=begin; i<end; i++)
     {
      ENUM_GRAPH_OBJ_PROP_DOUBLE prop=(ENUM_GRAPH_OBJ_PROP_DOUBLE)i;
      if(!this.SupportProperty(prop)) continue;
      for(int j=0;j<Prop.CurrSize(prop);j++)
        {
         if(this.GetProperty(prop,j)!=this.GetPropertyPrev(prop,j))
           {
            changed=true;
            this.CreateAndAddNewEvent(GRAPH_OBJ_EVENT_CHANGE,this.ChartID(),prop,this.Name());
           }
        }
     }

   begin=end; end+=GRAPH_OBJ_PROP_STRING_TOTAL;
   for(int i=begin; i<end; i++)
     {
      ENUM_GRAPH_OBJ_PROP_STRING prop=(ENUM_GRAPH_OBJ_PROP_STRING)i;
      if(!this.SupportProperty(prop)) continue;
      for(int j=0;j<Prop.CurrSize(prop);j++)
        {
         if(this.GetProperty(prop,j)!=this.GetPropertyPrev(prop,j) && prop!=GRAPH_OBJ_PROP_NAME)
           {
            changed=true;
            this.CreateAndAddNewEvent(GRAPH_OBJ_EVENT_CHANGE,this.ChartID(),prop,this.Name());
           }
        }
     }
   if(changed)
     {
      for(int i=0;i<this.m_list_events.Total();i++)
        {
         CGBaseEvent *event=this.m_list_events.At(i);
         if(event==NULL)
            continue;
         ::EventChartCustom(::ChartID(),event.ID(),event.Lparam(),event.Dparam(),event.Sparam());
        }
      if(this.AllowChangeHistory())
        {
         int total=HistoryChangesTotal();
         if(this.CreateNewChangeHistoryObj(total<1))
            ::Print
              (
               DFUN,CMessage::Text(MSG_GRAPH_STD_OBJ_SUCCESS_CREATE_SNAPSHOT)," #",(total==0 ? "0-1" : (string)total),
               ": ",this.HistoryChangedObjTimeChangedToString(total-1)
              );
        }
      this.PropertiesCopyToPrevData();
     }
  }
//+------------------------------------------------------------------+

此处,我们得到已变更图形对象的数量,并将它们传递给创建对象属性新快照对象的方法,该方法的形式为 bool 标志(如果 'total' 小于 1,传递的值为 true,这意味着这是图形对象的首次变更)。 如果创建了对象,并将其添加到变更列表中,则在日志中一同显示相应的消息与变改索引。 如果这是首次变更,则消息包含 “0-1”,这意味着同时创建了两个对象(0 — 变更其属性前的图形对象状态,1 — 对象当前状态)。

从图形对象的变更历史记录中设置整数型实数型字符串型属性值的方法:

//+------------------------------------------------------------------+
//| Set integer property values from the change history              |
//| for the graphical object                                         |
//+------------------------------------------------------------------+
void CGStdGraphObj::SetHistoryINT(const ENUM_GRAPH_OBJ_PROP_INTEGER prop,const long value,const int modifier)
  {
   switch(prop)
     {
      case GRAPH_OBJ_PROP_TIMEFRAMES            :  this.SetVisibleOnTimeframes((int)value,false);        break;  // Object visibility on timeframes
      case GRAPH_OBJ_PROP_BACK                  :  this.SetFlagBack(value,false);                        break;   // Background object
      case GRAPH_OBJ_PROP_ZORDER                :  this.SetZorder(value,false);                          break;   // Priority of a graphical object for receiving the event of clicking on a chart
      case GRAPH_OBJ_PROP_HIDDEN                :  this.SetFlagHidden(value,false);                      break;   // Disable displaying the name of a graphical object in the terminal object list
      case GRAPH_OBJ_PROP_SELECTED              :  this.SetFlagSelected(value,false);                    break;   // Object selection
      case GRAPH_OBJ_PROP_SELECTABLE            :  this.SetFlagSelectable(value,false);                  break;   // Object availability
      case GRAPH_OBJ_PROP_TIME                  :  this.SetTime(value,modifier);                         break;   // Time coordinate
      case GRAPH_OBJ_PROP_COLOR                 :  this.SetColor((color)value);                          break;   // Color
      case GRAPH_OBJ_PROP_STYLE                 :  this.SetStyle((ENUM_LINE_STYLE)value);                break;   // Style
      case GRAPH_OBJ_PROP_WIDTH                 :  this.SetWidth((int)value);                            break;   // Line width
      case GRAPH_OBJ_PROP_FILL                  :  this.SetFlagFill(value);                              break;   // Filling an object with color
      case GRAPH_OBJ_PROP_READONLY              :  this.SetFlagReadOnly(value);                          break;   // Ability to edit text in the Edit object
      case GRAPH_OBJ_PROP_LEVELS                :  this.SetLevels((int)value);                           break;   // Number of levels
      case GRAPH_OBJ_PROP_LEVELCOLOR            :  this.SetLevelColor((color)value,modifier);            break;   // Level line color
      case GRAPH_OBJ_PROP_LEVELSTYLE            :  this.SetLevelStyle((ENUM_LINE_STYLE)value,modifier);  break;   // Level line style
      case GRAPH_OBJ_PROP_LEVELWIDTH            :  this.SetLevelWidth((int)value,modifier);              break;   // Level line width
      case GRAPH_OBJ_PROP_ALIGN                 :  this.SetAlign((ENUM_ALIGN_MODE)value);                break;   // Horizontal text alignment in the Edit object (OBJ_EDIT)
      case GRAPH_OBJ_PROP_FONTSIZE              :  this.SetFontSize((int)value);                         break;   // Font size
      case GRAPH_OBJ_PROP_RAY_LEFT              :  this.SetFlagRayLeft(value);                           break;   // Ray goes to the left
      case GRAPH_OBJ_PROP_RAY_RIGHT             :  this.SetFlagRayRight(value);                          break;   // Ray goes to the right
      case GRAPH_OBJ_PROP_RAY                   :  this.SetFlagRay(value);                               break;   // Vertical line goes through all windows of a chart
      case GRAPH_OBJ_PROP_ELLIPSE               :  this.SetFlagEllipse(value);                           break;   // Display the full ellipse of the Fibonacci Arc object
      case GRAPH_OBJ_PROP_ARROWCODE             :  this.SetArrowCode((uchar)value);                      break;   // Arrow code for the Arrow object
      case GRAPH_OBJ_PROP_ANCHOR                :  this.SetAnchor((int)value);                           break;   // Position of the binding point of the graphical object
      case GRAPH_OBJ_PROP_XDISTANCE             :  this.SetXDistance((int)value);                        break;   // Distance from the base corner along the X axis in pixels
      case GRAPH_OBJ_PROP_YDISTANCE             :  this.SetYDistance((int)value);                        break;   // Distance from the base corner along the Y axis in pixels
      case GRAPH_OBJ_PROP_DIRECTION             :  this.SetDirection((ENUM_GANN_DIRECTION)value);        break;   // Gann object trend
      case GRAPH_OBJ_PROP_DEGREE                :  this.SetDegree((ENUM_ELLIOT_WAVE_DEGREE)value);       break;   // Elliott wave markup level
      case GRAPH_OBJ_PROP_DRAWLINES             :  this.SetFlagDrawLines(value);                         break;   // Display lines for Elliott wave markup
      case GRAPH_OBJ_PROP_STATE                 :  this.SetFlagState(value);                             break;   // Button state (pressed/released)
      case GRAPH_OBJ_PROP_CHART_OBJ_CHART_ID    :  this.SetChartObjChartID(value);                       break;   // Chart object ID (OBJ_CHART)
      case GRAPH_OBJ_PROP_CHART_OBJ_PERIOD      :  this.SetChartObjPeriod((ENUM_TIMEFRAMES)value);       break;   // Chart object period
      case GRAPH_OBJ_PROP_CHART_OBJ_DATE_SCALE  :  this.SetChartObjChartScale((int)value);               break;   // Time scale display flag for the Chart object
      case GRAPH_OBJ_PROP_CHART_OBJ_PRICE_SCALE :  this.SetFlagChartObjPriceScale(value);                break;   // Price scale display flag for the Chart object
      case GRAPH_OBJ_PROP_CHART_OBJ_CHART_SCALE :  this.SetFlagChartObjDateScale(value);                 break;   // Chart object scale
      case GRAPH_OBJ_PROP_XSIZE                 :  this.SetXSize((int)value);                            break;   // Object distance along the X axis in pixels
      case GRAPH_OBJ_PROP_YSIZE                 :  this.SetYSize((int)value);                            break;   // Object height along the Y axis in pixels
      case GRAPH_OBJ_PROP_XOFFSET               :  this.SetXOffset((int)value);                          break;   // X coordinate of the upper-left corner of the visibility area
      case GRAPH_OBJ_PROP_YOFFSET               :  this.SetYOffset((int)value);                          break;   // Y coordinate of the upper-left corner of the visibility area
      case GRAPH_OBJ_PROP_BGCOLOR               :  this.SetBGColor((color)value);                        break;   // Background color for OBJ_EDIT, OBJ_BUTTON, OBJ_RECTANGLE_LABEL
      case GRAPH_OBJ_PROP_CORNER                :  this.SetCorner((ENUM_BASE_CORNER)value);              break;   // Chart corner for binding a graphical object
      case GRAPH_OBJ_PROP_BORDER_TYPE           :  this.SetBorderType((ENUM_BORDER_TYPE)value);          break;   // Border type for "Rectangle border"
      case GRAPH_OBJ_PROP_BORDER_COLOR          :  this.SetBorderColor((color)value);                    break;   // Border color for the OBJ_EDIT and OBJ_BUTTON objects
      case GRAPH_OBJ_PROP_ID                    :  // Object ID
      case GRAPH_OBJ_PROP_TYPE                  :  // Graphical object type (ENUM_OBJECT)
      case GRAPH_OBJ_PROP_ELEMENT_TYPE          :  // Graphical element type (ENUM_GRAPH_ELEMENT_TYPE)
      case GRAPH_OBJ_PROP_SPECIES               :  // Graphical object species (ENUM_GRAPH_OBJ_SPECIES)
      case GRAPH_OBJ_PROP_GROUP                 :  // Graphical object group
      case GRAPH_OBJ_PROP_BELONG                :  // Graphical object affiliation
      case GRAPH_OBJ_PROP_CHART_ID              :  // Chart ID
      case GRAPH_OBJ_PROP_WND_NUM               :  // Chart subwindow index
      case GRAPH_OBJ_PROP_NUM                   :  // Object index in the list
      case GRAPH_OBJ_PROP_CHANGE_HISTORY        :  // Flag of storing the change history
      case GRAPH_OBJ_PROP_CREATETIME            :  // Object creation time
      default  : break;
     }
  }
//+------------------------------------------------------------------+
//| Set real property values from the change history                 |
//| for the graphical object                                         |
//+------------------------------------------------------------------+
void CGStdGraphObj::SetHistoryDBL(const ENUM_GRAPH_OBJ_PROP_DOUBLE prop,const double value,const int modifier)
  {
   switch(prop)
     {
      case GRAPH_OBJ_PROP_PRICE                 : this.SetPrice(value,modifier);          break;   // Price coordinate
      case GRAPH_OBJ_PROP_LEVELVALUE            : this.SetLevelValue(value,modifier);     break;   // Level value
      case GRAPH_OBJ_PROP_SCALE                 : this.SetScale(value);                   break;   // Scale (property of Gann objects and Fibonacci Arcs objects)
      case GRAPH_OBJ_PROP_ANGLE                 : this.SetAngle(value);                   break;   // Angle
      case GRAPH_OBJ_PROP_DEVIATION             : this.SetDeviation(value);               break;   // Deviation of the standard deviation channel
      default: break;
     }
  }
//+------------------------------------------------------------------+
//| Set string property values from the change history               |
//| for the graphical object                                         |
//+------------------------------------------------------------------+
void CGStdGraphObj::SetHistorySTR(const ENUM_GRAPH_OBJ_PROP_STRING prop,const string value,const int modifier)
  {
   switch(prop)
     {
      case GRAPH_OBJ_PROP_TEXT                  : this.SetText(value);                    break;   // Object description (the text contained in the object)
      case GRAPH_OBJ_PROP_TOOLTIP               : this.SetTooltip(value);                 break;   // Tooltip text
      case GRAPH_OBJ_PROP_LEVELTEXT             : this.SetLevelText(value,modifier);      break;   // Level description
      case GRAPH_OBJ_PROP_FONT                  : this.SetFont(value);                    break;   // Font
      case GRAPH_OBJ_PROP_BMPFILE               : this.SetBMPFile(value,modifier);        break;   // BMP file name for the "Bitmap Level" object
      case GRAPH_OBJ_PROP_CHART_OBJ_SYMBOL      : this.SetChartObjSymbol(value);          break;   // Chart object symbol
      case GRAPH_OBJ_PROP_NAME                  : // Object name
      default :  break;
     }
  }
//+------------------------------------------------------------------+

此处,根据传递给方法的属性,在 “switch” 操作符中选择用于设置类对象和图形对象中的对象属性值的相应方法。 属性变更历史对象不需要的属性没有对应的 case 处理程序,因此代码执行直达 default 操作符,并于break 操作符处结束。

在图形元素集合类 \MQL5\Include\DoEasy\Collections\GraphElementsCollection.mqh 中,添加按图表 ID 和分组返回对象列表的方法:

//--- Return (1) the last removed graphical object and (2) the array size of graphical object properties
   CGStdGraphObj    *GetLastDeletedGraphObj(void)                 const { return this.m_list_deleted_obj.At(this.m_list_deleted_obj.Total()-1); }
   int               GetSizeProperty(const string name,const long chart_id,const int prop)
                       {
                        CGStdGraphObj *obj=this.GetStdGraphObject(name,chart_id);
                        return(obj!=NULL ? obj.Properties().CurrSize(prop) : 0);
                       }
//--- Return the list of objects by chart ID and group
   CArrayObj        *GetListStdGraphObjByGroup(const long chart_id,const int group)
                       {
                        CArrayObj *list=GetList(GRAPH_OBJ_PROP_CHART_ID,0,chart_id,EQUAL);
                        return CSelect::ByGraphicStdObjectProperty(list,GRAPH_OBJ_PROP_GROUP,0,group,EQUAL);
                       }
   
//--- Constructor

方法按照图表 ID 接收所有图形对象的列表从获取的列表中返回含有指定分组值的对象列表

在我们的程序中运用这种方法,我们可以得到图形对象的列表,并为它们指定一个分组,从而能以我们需要的方式处理这些对象。

为了在基于函数库的程序中获取整个变更历史数据,我们需要在 \MQL5\Include\DoEasy\Engine.mqh 中的 CEngine 函数库主对象中进行修改 。

添加返回现有图形对象列表的方法,和按图表名称和 ID 返回标准图形对象对象类指针的方法

//--- Return the (1) collection of graphical objects, the list of (2) existing and (3) removed graphical objects
   CGraphElementsCollection *GetGraphicObjCollection(void)              { return &this.m_graph_objects;                       }
   CArrayObj           *GetListStdGraphObj(void)                        { return this.m_graph_objects.GetListGraphObj();      }
   CArrayObj           *GetListDeletedObj(void)                         { return this.m_graph_objects.GetListDeletedObj();    }
//--- Return (1) the number of removed graphical objects and (2) the size of the property array
   int                  TotalDeletedGraphObjects(void)                  { return this.GetListDeletedObj().Total();            }
   int                  GraphGetSizeProperty(const string name,const long chart_id,const int prop)
                          {
                           return this.m_graph_objects.GetSizeProperty(name,chart_id,prop);
                          }
//--- Return the class of the object of the standard graphical object by chart name and ID
   CGStdGraphObj       *GraphGetStdGraphObject(const string name,const long chart_id)
                          {
                           return this.m_graph_objects.GetStdGraphObject(name,chart_id);
                          }
   
//--- Fill in the array with IDs of the charts opened in the terminal

这些方法返回调用图形元素集合类的同名方法的结果。

这些都是测试处理图形对象变更历史所必需的函数库类的修改和改进。


测试

为了执行测试,我们借助上一篇文章中的 EA,并将其保存到 \MQL5\Experts\TestDoEasy\Part92\,命名为 TestDoEasyPart92.mq5

要为图表上每个新创建的图形对象设置允许保存变更历史的标志,并创建分组 1。 因此,把图表中的所有图形对象都归类到一个分组,且允许它们写入变更历史。 接下来,更改每个图形对象。 所有更改都将写入其记忆类。

设置查看变更历史记录的关键字。

通过按键,我们将看到图形对象在每次变更时如何接收其所有属性。

针对指定按钮设置宏替换:

//+------------------------------------------------------------------+
//|                                             TestDoEasyPart92.mq5 |
//|                                  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"
//--- includes
#include <DoEasy\Engine.mqh>
//--- defines
#define        FORMS_TOTAL (4)   // Number of created forms
#define        START_X     (4)   // Initial X coordinate of the shape
#define        START_Y     (4)   // Initial Y coordinate of the shape
#define KEY_LEFT           (188) // Left
#define KEY_RIGHT          (190) // Right
#define KEY_ORIGIN         (191) // Initial properties
//--- input parameters
sinput   bool              InpMovable     =  true;          // Movable forms flag
sinput   ENUM_INPUT_YES_NO InpUseColorBG  =  INPUT_YES;     // Use chart background color to calculate shadow color
sinput   color             InpColorForm3  =  clrCadetBlue;  // Third form shadow color (if not background color) 
//--- global variables
CEngine        engine;
CArrayObj      list_forms;  
color          array_clr[];
//+------------------------------------------------------------------+

在事件处理程序中,添加处理击键的代码模块。 在处理图形对象创建事件的块中,添加允许保存更改历史记录的标志分组 1

//+------------------------------------------------------------------+
//| ChartEvent function                                              |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,
                  const long &lparam,
                  const double &dparam,
                  const string &sparam)
  {
//--- If working in the tester, exit
   if(MQLInfoInteger(MQL_TESTER))
      return;
//--- If the mouse is moved
   /*
   if(id==CHARTEVENT_MOUSE_MOVE)
     {
      CForm *form=NULL;
      datetime time=0;
      double price=0;
      int wnd=0;
      
      //--- If Ctrl is not pressed,
      if(!IsCtrlKeyPressed())
        {
         //--- clear the list of created form objects, allow scrolling a chart with the mouse and show the context menu
         list_forms.Clear();
         ChartSetInteger(ChartID(),CHART_MOUSE_SCROLL,true);
         ChartSetInteger(ChartID(),CHART_CONTEXT_MENU,true);
         return;
        }
      
      //--- If X and Y chart coordinates are successfully converted into time and price,
      if(ChartXYToTimePrice(ChartID(),(int)lparam,(int)dparam,wnd,time,price))
        {
         //--- get the bar index the cursor is hovered over
         int index=iBarShift(Symbol(),PERIOD_CURRENT,time);
         if(index==WRONG_VALUE)
            return;
         
         //--- Get the bar index by index
         CBar *bar=engine.SeriesGetBar(Symbol(),Period(),index);
         if(bar==NULL)
            return;
         
         //--- Convert the coordinates of a chart from the time/price representation of the bar object to the X and Y coordinates
         int x=(int)lparam,y=(int)dparam;
         if(!ChartTimePriceToXY(ChartID(),0,bar.Time(),(bar.Open()+bar.Close())/2.0,x,y))
            return;
         
         //--- Disable moving a chart with the mouse and showing the context menu
         ChartSetInteger(ChartID(),CHART_MOUSE_SCROLL,false);
         ChartSetInteger(ChartID(),CHART_CONTEXT_MENU,false);
         
         //--- Create the form object name and hide all objects except one having such a name
         string name="FormBar_"+(string)index;
         HideFormAllExceptOne(name);
         
         //--- If the form object with such a name does not exist yet,
         if(!IsPresentForm(name))
           {
            //--- create a new form object
            form=bar.CreateForm(index,name,x,y,114,16);   
            if(form==NULL)
               return;
            
            //--- Set activity and unmoveability flags for the form
            form.SetActive(true);
            form.SetMovable(false);
            //--- Set the opacity of 200
            form.SetOpacity(200);
            //--- The form background color is set as the first color from the color array
            form.SetColorBackground(array_clr[0]);
            //--- Form outlining frame color
            form.SetColorFrame(C'47,70,59');
            //--- Draw the shadow drawing flag
            form.SetShadow(true);
            //--- Calculate the shadow color as the chart background color converted to the monochrome one
            color clrS=form.ChangeColorSaturation(form.ColorBackground(),-100);
            //--- If the settings specify the usage of the chart background color, replace the monochrome color with 20 units
            //--- Otherwise, use the color specified in the settings for drawing the shadow
            color clr=(InpUseColorBG ? form.ChangeColorLightness(clrS,-20) : InpColorForm3);
            //--- Draw the form shadow with the right-downwards offset from the form by three pixels along all axes
            //--- Set the shadow opacity to 200, while the blur radius is equal to 4
            form.DrawShadow(2,2,clr,200,3);
            //--- Fill the form background with a vertical gradient
            form.Erase(array_clr,form.Opacity());
            //--- Draw an outlining rectangle at the edges of the form
            form.DrawRectangle(0,0,form.Width()-1,form.Height()-1,form.ColorFrame(),form.Opacity());
            //--- If failed to add the form object to the list, remove the form and exit the handler
            if(!list_forms.Add(form))
              {
               delete form;
               return;
              }
            //--- Capture the form appearance
            form.Done();
           }
         //--- If the form object exists,
         if(form!=NULL)
           {
            //--- draw a text with the bar type description on it and show the form. The description corresponds to the mouse cursor position
            form.TextOnBG(0,bar.BodyTypeDescription(),form.Width()/2,form.Height()/2-1,FRAME_ANCHOR_CENTER,C'7,28,21');
            form.Show();
           }
         //--- Re-draw the chart
         ChartRedraw();
        }
     }
   */
   //--- If a key is pressed
   if(id==CHARTEVENT_KEYDOWN)
     {
      //---Declare the index of the current graphical object change history object
      static int index=0;
      //--- Get the list of all graphical objects with the specified group index (1)
      CArrayObj *list=engine.GetListStdGraphObj();
      list=CSelect::ByGraphicStdObjectProperty(list,GRAPH_OBJ_PROP_GROUP,0,1,EQUAL);
      if(list==NULL || list.Total()==0)
         return;
      //--- If "/" is pressed
      if(lparam==KEY_ORIGIN)
        {
         //--- Set the index 0 in the list of the graphical object change history object
         index=0;
         //--- In the loop by the number of group 1 objects on the chart
         for(int i=0;i<list.Total();i++)
           {
            //--- Get the next object from the list and set the initial properties from the change history to it
            CGStdGraphObj *obj=list.At(i);
            if(obj==NULL)
               continue;
            obj.SetPropertiesFromHistory(index);
           }
        }
      //--- If "." is pressed
      if(lparam==KEY_RIGHT)
        {
         //--- Declare the variables to search for the maximum number of changes of all group 1 graphical objects
         int change_max=0, changes_total=0;
         //--- Increase the object index in the list of the graphical object change history
         index++;
         //--- In the loop by the number of group 1 objects on the chart
         for(int i=0;i<list.Total();i++)
           {
            //--- Get the next object from the list
            CGStdGraphObj *obj=list.At(i);
            if(obj==NULL)
               continue;
            //--- Calculate the maximum number of changes of all group 1 graphical objects
            changes_total=obj.HistoryChangesTotal();
            if(changes_total>change_max)
               change_max=changes_total;
            //--- Set the properties (by 'index' from the list) from the change history
            obj.SetPropertiesFromHistory(index>obj.HistoryChangesTotal()-1 ? obj.HistoryChangesTotal()-1 : index);
           }
         //--- If the change history object index exceeds the maximum number of changes of all objects,
         //--- set the index equal to the maximum amount of changes of all graphical objects
         if(index>change_max-1)
            index=change_max-1;
        }
      //--- If "," is pressed
      if(lparam==KEY_LEFT)
        {
         //--- Decrease the object index in the list of the graphical object change history
         index--;
         //--- If the index is less than zero, set it to 0
         if(index<0)
            index=0;
         //--- In the loop by the number of group 1 objects on the chart
         for(int i=0;i<list.Total();i++)
           {
            //--- Get the next object from the list
            CGStdGraphObj *obj=list.At(i);
            if(obj==NULL)
               continue;
            //--- Set the properties (by 'index' from the list) from the change history
            obj.SetPropertiesFromHistory(index);
           }
        }
      //--- Re-draw the chart for displaying changes in graphical objects
      ChartRedraw();
     }
   if(id==CHARTEVENT_CLICK)
     {
      if(!IsCtrlKeyPressed())
         return;
      datetime time=0;
      double price=0;
      int sw=0;
      if(ChartXYToTimePrice(ChartID(),(int)lparam,(int)dparam,sw,time,price))
        {
         long array[];
         engine.GraphGetArrayChartsID(array);
         for(int i=0;i<ArraySize(array);i++)
            engine.CreateLineVertical(array[i],"LineVertical",0,time);
        }
     }
   engine.GetGraphicObjCollection().OnChartEvent(id,lparam,dparam,sparam);

//--- Handle standard graphical object events
   ushort idx=ushort(id-CHARTEVENT_CUSTOM);
   CGStdGraphObj *obj=NULL;
   if(idx>GRAPH_OBJ_EVENT_NO_EVENT && idx<GRAPH_OBJ_EVENTS_NEXT_CODE)
     {
      CChartObjectsControl *chart_ctrl=NULL;
      int end=0;
      string evn="";
      //--- Depending on the event type, display an appropriate message in the journal
      switch(idx)
        {
         //--- Graphical object creation event
         case GRAPH_OBJ_EVENT_CREATE   :
           //--- Display the message about creating a new graphical object
           Print(DFUN,CMessage::Text(MSG_GRAPH_OBJ_EVN_GRAPH_OBJ_CREATE),":");
           //--- Get the pointer to the object by chart name and ID passed to sparam and lparam, respectively
           //--- display the short description of a newly created object to the journal and set the flag of storing the change history
           obj=engine.GraphGetStdGraphObject(sparam,lparam);
           if(obj!=NULL)
             {
              obj.PrintShort();
              obj.SetAllowChangeMemory(true);
              obj.SetGroup(1);
             }
           break;
         //--- Event of changing the graphical object property
         case GRAPH_OBJ_EVENT_CHANGE   :
           //--- Display the message about changing the graphical object property
           Print(DFUN,CMessage::Text(MSG_GRAPH_OBJ_EVN_GRAPH_OBJ_CHANGE),":");
           //--- Get the pointer to the object by chart name and ID passed to sparam and lparam, respectively
           obj=engine.GetGraphicObjCollection().GetStdGraphObject(sparam,lparam);
           if(obj!=NULL)
             {
              //--- Display a short description of the changed object in the journal
              obj.PrintShort();
              //--- calculate the code of the changed property passed to dparam and get the property description
              if(dparam<GRAPH_OBJ_PROP_INTEGER_TOTAL)
                 evn=obj.GetPropertyDescription((ENUM_GRAPH_OBJ_PROP_INTEGER)dparam);
              else if(dparam<GRAPH_OBJ_PROP_INTEGER_TOTAL+GRAPH_OBJ_PROP_DOUBLE_TOTAL)
                 evn=obj.GetPropertyDescription((ENUM_GRAPH_OBJ_PROP_DOUBLE)dparam);
              else
                 evn=obj.GetPropertyDescription((ENUM_GRAPH_OBJ_PROP_STRING)dparam);
              //--- Display the description of the graphical object's changed property in the journal
              Print(DFUN,evn);
             }
           break;
         //--- Graphical object renaming event
         case GRAPH_OBJ_EVENT_RENAME   :
           //--- Display the message about renaming the graphical object
           Print(DFUN,CMessage::Text(MSG_GRAPH_OBJ_EVN_GRAPH_OBJ_RENAME));
           //--- Get the pointer to the object by chart name and ID passed to sparam and lparam, respectively
           obj=engine.GetGraphicObjCollection().GetStdGraphObject(sparam,lparam);
           if(obj!=NULL)
             {
              //--- Display the previous and new object name, as well as its entire renaming history, in the journal
              Print(DFUN,obj.GetProperty(GRAPH_OBJ_PROP_NAME,obj.Properties().CurrSize(GRAPH_OBJ_PROP_NAME)-1)," >>> ",obj.GetProperty(GRAPH_OBJ_PROP_NAME,0));
              obj.PrintRenameHistory();
             }
           break;
         //--- Graphical object deletion event
         case GRAPH_OBJ_EVENT_DELETE   :
           //--- Display the message about removing the graphical object
           Print(DFUN,CMessage::Text(MSG_GRAPH_OBJ_EVN_GRAPH_OBJ_DELETE),":");
           //--- Get the pointer to the removed object by chart name and ID passed to sparam and lparam, respectively
           //--- and display a short description of the removed object in the journal
           obj=engine.GetGraphicObjCollection().GetStdDelGraphObject(sparam,lparam);
           if(obj!=NULL)
             {
              obj.PrintShort();
             }
           break;
         //--- Event of removing the graphical object together with the chart window
         case GRAPH_OBJ_EVENT_DEL_CHART:
           //--- Display the message about removing graphical objects together with the chart window, whose ID and symbol are passed to lparam and sparam
           Print(DFUN,CMessage::Text(MSG_GRAPH_OBJ_EVN_GRAPH_OBJ_DEL_CHART),": #",lparam,", ",sparam,":");
           //--- Calculate the end value for the loop by the list of removed graphical objects
           end=engine.TotalDeletedGraphObjects()-(int)dparam;
           if(end<0)
              end=0;
           //--- In the loop from the end of the removed graphical objects list up to the value calculated in the 'end' variable,
           for(int i=engine.TotalDeletedGraphObjects()-1;i>=end;i--)
             {
              //--- get the next removed graphical object from the list
              obj=engine.GetListDeletedObj().At(i);
              if(obj==NULL)
                 continue;
              //--- and display its brief description in the journal
              obj.PrintShort();
             }
           break;
         //---
         default:
           break;
        }
     }
  }
//+------------------------------------------------------------------+

处理击键的整个逻辑在代码的注释中均有描述。

编译 EA,并在图表上启动它。 添加图形对象,修改其属性,然后按 “/” — 对象会提取首次修改之前的数值。 按下 "." 和 "," — 对象将采用与属性变更历史记录列表相对应的属性和外观:


 

下一步是什么?

在下一篇文章中,我将着手开发复合图形对象。

以下是 MQL5 的当前函数库版本、测试 EA,和图表事件控制指标的所有文件,供您测试和下载。 在评论中留下您的问题、意见和建议。

返回内容目录

*该系列的前几篇文章:

DoEasy 函数库中的图形(第八十九部分):标准图形对象编程。 基本功能
DoEasy 函数库中的图形(第九十部分):标准图形对象事件。 基本功能
DoEasy 函数库中的图形(第九十一部分):标准图形对象事件。 对象名称变更历史记录