English Русский 中文 Español Deutsch Português
DoEasyライブラリのグラフィックス(第91部): 標準グラフィカルオブジェクトのイベントオブジェクト名変更履歴

DoEasyライブラリのグラフィックス(第91部): 標準グラフィカルオブジェクトのイベントオブジェクト名変更履歴

MetaTrader 5 | 21 2月 2022, 15:32
354 0
Artyom Trishkin
Artyom Trishkin

目次


概念

ライブラリではすでに、標準グラフィカルオブジェクトで発生するイベントを定義できます。本稿では、ライブラリベースのプログラムで作業しているユーザーが、イベントを受信したときに変更されたプロパティと量を正確に定義できる機能を実装したいと思います。考え方は単純で、イベントIDとイベントが発生したオブジェクトの名前、変更されたグラフィカルオブジェクトが配置されているチャートのID、値が変更されたプロパティの名前を渡すことに基づいています。チャートからグラフィカルオブジェクトを削除すると、これらの削除されたグラフィカルオブジェクトを説明するクラスのオブジェクトが削除済みオブジェクトのリストに配置されます。これにより、プログラムでどのオブジェクトが削除されたかを定義し、そのすべてのプロパティを受け取ったり、誤って削除されたオブジェクトを復元したりできるようになります。

削除されたグラフィカルオブジェクトを保存するという概念を変更されたオブジェクトプロパティを保存することにまで拡張すると、オブジェクトの変更履歴全体を再現するという魅力的な機能を作成できるようになります。つまり、連続して変更されたすべてのオブジェクトプロパティのリストがあれば、オブジェクトが現在より前に持っていた状態を常に再現できます。したがって、チャートに対して異なるオブジェクトの位置を「設定」し、後でそれらを順番に(または順番なしで)再現することができます。これにより、次の記事で検討する複合グラフィカルオブジェクトを含む、独自の「メモリ」を備えたテクニカル分析ツールを作成できるようになります。

ここではこの概念を完全に紹介するつもりはありませんが、例として「オブジェクト名」プロパティの変更を使用してその機能をテストします。以降の記事では、すべてのオブジェクトプロパティを記録および再現する機能を徐々に作成していきます。

この概念は、操作ログに以前の名前と現在の名前を表示するために、どのグラフィカルオブジェクト名が変更されたかをプログラムに通知する必要性から生まれました。ライブラリとライブラリベースのグラフィカルオブジェクトの機能を拡張するのは良いことだと思いました。それで、現在のコンセプトを思いついたのです。


ライブラリクラスの改善

いつものように、まず、\MQL5\Include\DoEasy\Data.mqhファイルに新しいライブラリメッセージを実装します。

新しいメッセージインデックスを追加します

//--- CGraphElementsCollection
   MSG_GRAPH_OBJ_FAILED_GET_ADDED_OBJ_LIST,           // Failed to get the list of newly added objects
   MSG_GRAPH_OBJ_FAILED_DETACH_OBJ_FROM_LIST,         // Failed to remove a graphical object from the list
   MSG_GRAPH_OBJ_FAILED_ADD_OBJ_TO_DEL_LIST,          // Failed to set a graphical object to the list of removed objects
   MSG_GRAPH_OBJ_FAILED_ADD_OBJ_TO_RNM_LIST,          // Failed to set a graphical object to the list of renamed objects
   
   MSG_GRAPH_OBJ_CREATE_EVN_CTRL_INDICATOR,           // Indicator for controlling and sending events created
   MSG_GRAPH_OBJ_FAILED_CREATE_EVN_CTRL_INDICATOR,    // Failed to create the indicator for controlling and sending events
   MSG_GRAPH_OBJ_ADDED_EVN_CTRL_INDICATOR,            // Indicator for controlling and sending events successfully added to the chart
   MSG_GRAPH_OBJ_FAILED_ADD_EVN_CTRL_INDICATOR,       // Failed to add the indicator for controlling and sending events
   MSG_GRAPH_OBJ_ALREADY_EXIST_EVN_CTRL_INDICATOR,    // Indicator for controlling and sending events is already present on the chart
   MSG_GRAPH_OBJ_CLOSED_CHARTS,                       // Chart windows closed:
   MSG_GRAPH_OBJ_OBJECTS_ON_CLOSED_CHARTS,            // Objects removed together with charts:
   MSG_GRAPH_OBJ_FAILED_CREATE_EVN_OBJ,               // Failed to create the event object for a graphical object
   MSG_GRAPH_OBJ_FAILED_ADD_EVN_OBJ,                  // Failed to add the event object to the list
   MSG_GRAPH_OBJ_GRAPH_OBJ_PROP_CHANGE_HISTORY,       // Property change history: 
   
   MSG_GRAPH_OBJ_EVN_GRAPH_OBJ_CREATE,                // New graphical object created
   MSG_GRAPH_OBJ_EVN_GRAPH_OBJ_CHANGE,                // Changed the graphical object property
   MSG_GRAPH_OBJ_EVN_GRAPH_OBJ_RENAME,                // Graphical object renamed
   MSG_GRAPH_OBJ_EVN_GRAPH_OBJ_DELETE,                // Graphical object removed
   MSG_GRAPH_OBJ_EVN_GRAPH_OBJ_DEL_CHART,             // Graphical object removed together with the chart
   
  };
//+------------------------------------------------------------------+

また、新しく追加されたインデックスに対応するテキストも追加します。

//--- CGraphElementsCollection
   {"Не удалось получить список вновь добавленных объектов","Failed to get the list of newly added objects"},
   {"Не удалось изъять графический объект из списка","Failed to detach graphic object from the list"},
   {"Не удалось поместить графический объект в список удалённых объектов","Failed to place graphic object in the list of deleted objects"},
   {"Не удалось поместить графический объект в список переименованных объектов","Failed to place graphic object in the list of renamed objects"},
   
   {"Создан индикатор контроля и отправки событий","An indicator for monitoring and sending events has been created"},
   {"Не удалось создать индикатор контроля и отправки событий","Failed to create indicator for monitoring and sending events"},
   {"Индикатор контроля и отправки событий успешно добавлен на график ","The indicator for monitoring and sending events has been successfully added to the chart "},
   {"Не удалось добавить индикатор контроля и отправки событий на график ","Failed to add the indicator of monitoring and sending events to the chart "},
   {"Индикатор контроля и отправки событий уже есть на графике","The indicator for monitoring and sending events is already on the chart"},
   {"Закрыто окон графиков: ","Closed chart windows: "},
   {"С ними удалено объектов: ","Objects removed with them: "},
   {"Не удалось создать объект-событие для графического объекта","Failed to create event object for graphic object"},
   {"Не удалось добавить объект-событие в список","Failed to add event object to list"},
   {"История изменения свойства: ","Property change history: "},
   
   {"Создан новый графический объект","New graphic object created"},
   {"Изменено свойство графического объекта","Changed graphic object property"},
   {"Графический объект переименован","Graphic object renamed"},
   {"Удалён графический объект","Graphic object deleted"},
   {"Графический объект удалён вместе с графиком","The graphic object has been removed along with the chart"},
   
  };
//+---------------------------------------------------------------------+


\MQL5\Include\DoEasy\Objects\Graph\Standard\GStdGraphObj.mqhにある抽象標準グラフィカルオブジェクトクラスのpublicセクションで、プロパティオブジェクトへのポインタを返すための新しいメソッドを追加して、操作ログ内のグラフィカルオブジェクトの名前変更の履歴を表示するメソッドを宣言します。

//--- Return (1) itself and (2) the properties
   CGStdGraphObj    *GetObject(void)                                       { return &this;      }
   CProperties      *Properties(void)                                      { return this.Prop;  }
//--- Return the flag of the object supporting this property
   virtual bool      SupportProperty(ENUM_GRAPH_OBJ_PROP_INTEGER property) { return true;       }
   virtual bool      SupportProperty(ENUM_GRAPH_OBJ_PROP_DOUBLE property)  { return true;       }
   virtual bool      SupportProperty(ENUM_GRAPH_OBJ_PROP_STRING property)  { return true;       }

//--- Get description of (1) integer, (2) real and (3) string properties
   string            GetPropertyDescription(ENUM_GRAPH_OBJ_PROP_INTEGER property);
   string            GetPropertyDescription(ENUM_GRAPH_OBJ_PROP_DOUBLE property);
   string            GetPropertyDescription(ENUM_GRAPH_OBJ_PROP_STRING property);

//--- Return the description of the graphical object anchor point position
   virtual string    AnchorDescription(void)                                  const { return (string)this.GetProperty(GRAPH_OBJ_PROP_ANCHOR,0); }

//--- Display the description of the object properties in the journal (full_prop=true - all properties, false - supported ones only - implemented in descendant classes)
   virtual void      Print(const bool full_prop=false,const bool dash=false);
//--- Display a short description of the object in the journal
   virtual void      PrintShort(const bool dash=false,const bool symbol=false);
//--- Return the object short name
   virtual string    Header(const bool symbol=false);
//--- Display the history of renaming the object to the journal
   void              PrintRenameHistory(void);

//--- Return the description of the (1) (ENUM_OBJECT) graphical object type
   virtual string    TypeDescription(void)                                    const { return CMessage::Text(MSG_GRAPH_STD_OBJ_ANY);          }


オブジェクトの名前を変更する場合、以前の名前は事前にどこにも設定されていなければ知ることができません。オブジェクトの名前変更を定義するメカニズムはすでにあります。グラフィカルオブジェクトコレクションは、標準グラフィカルオブジェクトクラスのオブジェクトを、チャート上の各物理オブジェクトのパラメータの説明とともに格納します。オブジェクトの名前変更イベントが定義されるとすぐに、適切なクラスオブジェクトでその名前を変更します。名前を変更する前に、オブジェクトの以前の名前を知って履歴のために保存することができます。残りのプロパティでも同じことができます。

グラフィカルオブジェクトクラスのオブジェクトは多次元動的配列に基づいていていつでもどの次元でも必要な配列サイズを設定できるため、以前のオブジェクト名をオブジェクトプロパティに直接保存することにしました。これを使ってみましょう。グラフィカルオブジェクトの名前を変更するたびに、オブジェクト名プロパティ配列を増やし、その前の名前を配列の最後に入力します。したがって、オブジェクト名プロパティ配列のセル0には実際の名前が格納され、配列の他のセルには以前の名前が格納されます。この場合、オブジェクトの名前変更の履歴全体を常に知ることができます。他のプロパティについても同じことを行うと、その状態を記述して保存できる興味深いグラフィカルオブジェクトを受け取ることができます。オブジェクトに設定されたパラメータを読み取るために必要なセルを指定することで、そのすべての状態をすばやく再現できます。これは、メモリを使用した分析ツールの作成に役立ちます。

オブジェクト名以外のプロパティは後で改善されます。ここでは、オブジェクトプロパティの置き換えの履歴を保存するという概念を確認するだけにします。次に、オブジェクトのプロパティ(たとえば、そのレベル)とレベル変更の履歴を格納するために使用できるように、オブジェクトのプロパティを格納する多次元配列の構造を少し変更します。現在、レベルはNameオブジェクトのプロパティ値の変更履歴と同じ配列セルに格納されています。プロパティの衝突を避けるために、多次元プロパティ配列の構造を少し変更する必要があります。これは次の記事で行います。

クラスのpublicセクションで、オブジェクトの以前の名前を設定するためのメソッドを設定します。

//--- Compare CGStdGraphObj objects by a specified property (to sort the list by an object property)
   virtual int       Compare(const CObject *node,const int mode=0) const;
//--- Compare CGStdGraphObj objects with each other by all properties (to search for equal objects)
   bool              IsEqual(CGStdGraphObj* compared_req) const;
   
//--- Set the object previous name
   bool              SetNamePrev(const string name)
                       {
                        if(!this.Prop.SetSizeRange(GRAPH_OBJ_PROP_NAME,this.Prop.CurrSize(GRAPH_OBJ_PROP_NAME)+1))
                           return false;
                        this.SetProperty(GRAPH_OBJ_PROP_NAME,this.Prop.CurrSize(GRAPH_OBJ_PROP_NAME)-1,name);
                        return true;
                       }
  
//--- Default constructor
                     CGStdGraphObj(){ this.m_type=OBJECT_DE_TYPE_GSTD_OBJ; m_group=WRONG_VALUE; }
//--- Destructor
                    ~CGStdGraphObj()
                       {
                        if(this.Prop!=NULL)
                           delete this.Prop;
                       }
protected:

メソッドはオブジェクトの以前の名前を受け取り、名前を含むプロパティ配列のサイズは1増加します。一方、新しい配列セルはメソッドに渡されたグラフィカルオブジェクトの以前の名前を受け取り、 trueが返されます。配列サイズの変更や名前の設定に失敗した場合は、適切な操作ログメッセージが表示され、falseが返されます。

クラス本体外に、操作ログにオブジェクトの名前変更履歴を表示するメソッドを実装します。

//+------------------------------------------------------------------+
//| Display the history of renaming the object to the journal        |
//+------------------------------------------------------------------+
void CGStdGraphObj::PrintRenameHistory(void)
  {
   ::Print(DFUN,CMessage::Text(MSG_GRAPH_OBJ_GRAPH_OBJ_PROP_CHANGE_HISTORY),this.GetPropertyDescription(GRAPH_OBJ_PROP_NAME),":");
   int size=this.Properties().CurrSize(GRAPH_OBJ_PROP_NAME);
   ::Print(DFUN,"0: ",this.GetProperty(GRAPH_OBJ_PROP_NAME,0));
   for(int i=size-1;i>0;i--)
      ::Print(DFUN,size-i,": ",this.GetProperty(GRAPH_OBJ_PROP_NAME,i));
  }
//+------------------------------------------------------------------+

ここでは、新しいオブジェクト名のヘッダーが最初に表示され、次にインデックス0の現在の名前が表示されます。
次に、インデックス1を含む配列セルまでの逆ループで、名前変更履歴を格納するオブジェクト名配列セルに追加されたNameオブジェクトプロパティの説明をループインデックス によって表示します。現在のオブジェクト名は常にセル0に格納され、前のオブジェクト名は最後のセルに格納されるため、オブジェクトの名前変更シーケンスインデックスを正しく表示するためにループインデックスを使用するのではなくこの値を計算します。

たとえば、3回名前が変更されたオブジェクトに対して次のエントリが表示されます。

CGStdGraphObj::PrintRenameHistory: Property change history: Name: "M30 Vertical Line 24264_3":
CGStdGraphObj::PrintRenameHistory: 0: M30 Vertical Line 24264_3
CGStdGraphObj::PrintRenameHistory: 1: M30 Vertical Line 24264_2
CGStdGraphObj::PrintRenameHistory: 2: M30 Vertical Line 24264_1
CGStdGraphObj::PrintRenameHistory: 3: M30 Vertical Line 24264

ここでは、グラフィカルオブジェクトの名前を変更するシーケンスを明確に確認できます。(おそらく、シーケンス番号を変更して、0が常にオブジェクトの初期名に対応し、最後のインデックスが現在の名前に対応するようにします)。


グラフィカルオブジェクトイベントの追跡を完了するための主な作業は、グラフィカルオブジェクトコレクションクラスで実行されます。

\MQL5\Include\DoEasy\Collections\GraphElementsCollection.mqhを開き、必要な変更を加えます。

オブジェクトを追加するイベントを処理する現在の実装では、ターミナルリストから最後に追加されたオブジェクトを取得します。これは、単一の新しいオブジェクトがチャートに追加された場合にのみ機能します。複数のオブジェクトを同時に追加しても、ライブラリに1つのイベントが表示されます。つまり、複数のオブジェクトから1つのオブジェクトをチャートに追加します。これを回避するには、1つのオブジェクトがチャートに追加されているかどうかを確認し、追加されている場合は、最後に追加されたオブジェクトを取得します。複数のオブジェクトが同時に追加された場合は、すべてのチャートオブジェクトをループして各オブジェクトを選択し、その追加に関するイベントを送信します。

ただし、この構造を少し変更します。まず、追加したすべてのオブジェクトのリストを作成します。イベントはそのリストから制御プログラムチャートに送信されるようになります。新しく追加されたオブジェクトは、開いているチャートごとにあるCChartObjectsControlチャート管理クラスオブジェクトのRefresh()メソッドで確認されます。メソッドでは、追加されたオブジェクトごとにクラスオブジェクトを作成し、次にそれらをリストに追加して、このループによってリスト内の制御プログラムにオブジェクトの作成に関するメッセージを送信します。ライブラリイベントハンドラは、プログラムから呼び出されます。メッセージが到着した必要なチャート管理オブジェクトを受け取り、標準グラフィカルオブジェクトのクラスオブジェクトを取得して、コレクションリストに配置します。

改善後、チャート管理オブジェクトクラスのRefresh()メソッドは次のようになります。

//+------------------------------------------------------------------+
//| CChartObjectsControl: Check objects on a chart                   |
//+------------------------------------------------------------------+
void CChartObjectsControl::Refresh(void)
  {
//--- Clear the list of newly added objects
   this.m_list_new_graph_obj.Clear();
//--- Calculate the number of new objects on the chart
   this.m_total_objects=::ObjectsTotal(this.ChartID());
   this.m_delta_graph_obj=this.m_total_objects-this.m_last_objects;
   //--- If an object is added to the chart
   if(this.m_delta_graph_obj>0)
     {
      //--- Create the list of added graphical objects
      for(int i=0;i<this.m_delta_graph_obj;i++)
        {
         //--- Get the name of the last added object (if a single new object is added),
         //--- or a name from the terminal object list by index (if several objects have been added)
         string name=(this.m_delta_graph_obj==1 ? this.LastAddedGraphObjName() : ::ObjectName(this.m_chart_id,i));
         //--- Handle only non-programmatically created objects
         if(name==NULL || ::StringFind(name,this.m_name_program)>WRONG_VALUE)
            continue;
         //--- Create the object of the graphical object class corresponding to the added graphical object type
         ENUM_OBJECT type=(ENUM_OBJECT)::ObjectGetInteger(this.ChartID(),name,OBJPROP_TYPE);
         ENUM_OBJECT_DE_TYPE obj_type=ENUM_OBJECT_DE_TYPE(type+OBJECT_DE_TYPE_GSTD_OBJ+1);
         CGStdGraphObj *obj=this.CreateNewGraphObj(type,name);
         //--- If failed to create an object, inform of that and move on to the new iteration
         if(obj==NULL)
           {
            CMessage::ToLog(DFUN,MSG_GRAPH_STD_OBJ_ERR_FAILED_CREATE_CLASS_OBJ);
            continue;
           }
         //--- Set the object affiliation and add the created object to the list of new objects
         obj.SetBelong(GRAPH_OBJ_BELONG_NO_PROGRAM); 
         //--- If failed to add the object to the list, inform of that, remove the object and move on to the next iteration
         if(!this.m_list_new_graph_obj.Add(obj))
           {
            CMessage::ToLog(DFUN_ERR_LINE,MSG_LIB_SYS_FAILED_OBJ_ADD_TO_LIST);
            delete obj;
            continue;
           }
        }
      //--- Send events to the control program chart from the created list
      for(int i=0;i<this.m_list_new_graph_obj.Total();i++)
        {
         CGStdGraphObj *obj=this.m_list_new_graph_obj.At(i);
         if(obj==NULL)
            continue;
         //--- Send an event to the control program chart
         ::EventChartCustom(this.m_chart_id_main,GRAPH_OBJ_EVENT_CREATE,this.ChartID(),obj.TimeCreate(),obj.Name());
        }
     }
//--- save the index of the last added graphical object and the difference with the last check
   this.m_last_objects=this.m_total_objects;
   this.m_is_graph_obj_event=(bool)this.m_delta_graph_obj;
  }
//+------------------------------------------------------------------+

メソッドのロジックはコードのコメントで完全に説明されています。EventChartCustom()関数のdparamパラメータは、グラフィカルオブジェクトの作成時間を渡します。これは、以前に削除された復元されたチャート上にあるオブジェクトを識別するために必要です。つまり、開いているチャートにグラフィカルオブジェクトを設定し、そのチャートを削除して復元すると、削除時に設定されたグラフィカルオブジェクトも復元されます。このようなオブジェクトの作成時間はゼロです。後でこの時間を使用して、銘柄チャートと一緒に復元されたグラフィカルオブジェクトを識別できます。

チャートにチャート管理クラスのイベント制御指標を追加するメソッドでは、追加する前に、そのような指標がチャートにまだ存在しないことを確認します。

//+------------------------------------------------------------------+
//|CChartObjectsControl: Add the event control indicator to the chart|
//+------------------------------------------------------------------+
bool CChartObjectsControl::AddEventControlInd(void)
  {
   if(this.m_handle_ind==INVALID_HANDLE)
      return false;
   ::ResetLastError();
   string shortname="EventSend_From#"+(string)this.ChartID()+"_To#"+(string)this.m_chart_id_main;
   int total=::ChartIndicatorsTotal(this.ChartID(),0);
   for(int i=0;i<total;i++)
      if(::ChartIndicatorName(this.ChartID(),0,i)==shortname)
        {
         CMessage::ToLog(DFUN,MSG_GRAPH_OBJ_ALREADY_EXIST_EVN_CTRL_INDICATOR);
         return true;
        }
   return ::ChartIndicatorAdd(this.ChartID(),0,this.m_handle_ind);
  }
//+------------------------------------------------------------------+

ここではまず、チャート上の指標の検索に使用される短い指標名を作成します。次に、ループで、次のチャートIDを取得します。取得した指標の短縮名が必要な指標と一致する場合は、そのような指標がチャートにすでに存在することを通知してtrueを返します。ループが完了したら、チャートに指標を追加した結果を返します

グラフィカルオブジェクトコレクションクラスのprivateセクションで、削除されたグラフィカルオブジェクトのリストを宣言し、オブジェクトを検索するオーバーロードされたメソッドを宣言(コレクションには存在するがチャートには存在しない)します。検出されたオブジェクトへのポインタとは別に、リスト内のオブジェクトインデックスも返します。削除されたオブジェクトを削除済みグラフィカルオブジェクトのリストに再配置するメソッドを宣言します

//+------------------------------------------------------------------+
//| Collection of graphical objects                                  |
//+------------------------------------------------------------------+
#resource "\\"+PATH_TO_EVENT_CTRL_IND;          // Indicator for controlling graphical object events packed into the program resources
class CGraphElementsCollection : public CBaseObj
  {
private:
   CArrayObj         m_list_charts_control;     // List of chart management objects
   CListObj          m_list_all_canv_elm_obj;   // List of all graphical elements on canvas
   CListObj          m_list_all_graph_obj;      // List of all graphical objects
   CArrayObj         m_list_deleted_obj;        // List of removed graphical objects
   bool              m_is_graph_obj_event;      // Event flag in the list of graphical objects
   int               m_total_objects;           // Number of graphical objects
   int               m_delta_graph_obj;         // Difference in the number of graphical objects compared to the previous check
   
//--- Return the flag indicating the graphical element class object presence in the collection list of graphical elements
   bool              IsPresentGraphElmInList(const int id,const ENUM_GRAPH_ELEMENT_TYPE type_obj);
//--- Return the flag indicating the presence of the graphical object class in the graphical object collection list
   bool              IsPresentGraphObjInList(const long chart_id,const string name);
//--- Return the flag indicating the presence of a graphical object on a chart by name
   bool              IsPresentGraphObjOnChart(const long chart_id,const string name);
//--- Return the pointer to the object of managing objects of the specified chart
   CChartObjectsControl *GetChartObjectCtrlObj(const long chart_id);
//--- Create a new object of managing graphical objects of a specified chart and add it to the list
   CChartObjectsControl *CreateChartObjectCtrlObj(const long chart_id);
//--- Update the list of graphical objects by chart ID
   CChartObjectsControl *RefreshByChartID(const long chart_id);
//--- Check if the chart window is present
   bool              IsPresentChartWindow(const long chart_id);
//--- Handle removing the chart window
   void              RefreshForExtraObjects(void);
//--- Return the first free ID of the graphical (1) object and (2) element on canvas
   long              GetFreeGraphObjID(bool program_object);
   long              GetFreeCanvElmID(void);
//--- Add a graphical object to the collection
   bool              AddGraphObjToCollection(const string source,CChartObjectsControl *obj_control);
//--- Find an object present in the collection but not on a chart
   CGStdGraphObj    *FindMissingObj(const long chart_id);
   CGStdGraphObj    *FindMissingObj(const long chart_id,int &index);
//--- Find the graphical object present on a chart but not in the collection
   string            FindExtraObj(const long chart_id);
//--- Remove the graphical object class object from the graphical object collection list: (1) specified object, (2) by chart ID
   bool              DeleteGraphObjFromList(CGStdGraphObj *obj);
   void              DeleteGraphObjectsFromList(const long chart_id);
//--- Move the graphical object class object to the list of removed graphical objects: (1) specified object, (2) by index
   bool              MoveGraphObjToDeletedObjList(CGStdGraphObj *obj);
   bool              MoveGraphObjToDeletedObjList(const int index);
//--- Move all objects by chart ID to the list of removed graphical objects
   void              MoveGraphObjectsToDeletedObjList(const long chart_id);
//--- Remove the object of managing charts from the list
   bool              DeleteGraphObjCtrlObjFromList(CChartObjectsControl *obj);
//--- Create a new standard graphical object, return an object name
   bool              CreateNewStdGraphObject(const long chart_id,
                                             const string name,
                                             const ENUM_OBJECT type,
                                             const int subwindow,
                                             const datetime time1,
                                             const double price1,
                                             const datetime time2=0,
                                             const double price2=0,
                                             const datetime time3=0,
                                             const double price3=0,
                                             const datetime time4=0,
                                             const double price4=0,
                                             const datetime time5=0,
                                             const double price5=0);
public:


クラスのpublicセクションで、選択したプロパティによって削除されたグラフィカルオブジェクトのリストを返すメソッドを記述し、削除されたグラフィカルオブジェクトへのポインタを返すメソッドを宣言し、削除されたグラフィカルオブジェクトのリストを返すメソッド、最後に削除されたグラフィカルオブジェクトへのポインタを返すメソッド、グラフィカルオブジェクトのプロパティ配列のサイズを返すメソッドを記述します。
最後に、操作ログでオブジェクトの名前変更の履歴を表示するメソッドを宣言します

public:
//--- Return itself
   CGraphElementsCollection *GetObject(void)                                                             { return &this;                        }
//--- Return the full collection list of standard graphical objects "as is"
   CArrayObj        *GetListGraphObj(void)                                                               { return &this.m_list_all_graph_obj;   }
//--- Return the full collection list of graphical elements on canvas "as is"
   CArrayObj        *GetListCanvElm(void)                                                                { return &this.m_list_all_canv_elm_obj;}
//--- Return the list of graphical elements by a selected (1) integer, (2) real and (3) string properties meeting the compared criterion
   CArrayObj        *GetList(ENUM_CANV_ELEMENT_PROP_INTEGER property,long value,ENUM_COMPARER_TYPE mode=EQUAL)             { return CSelect::ByGraphCanvElementProperty(this.GetListCanvElm(),property,value,mode);           }
   CArrayObj        *GetList(ENUM_CANV_ELEMENT_PROP_DOUBLE property,double value,ENUM_COMPARER_TYPE mode=EQUAL)            { return CSelect::ByGraphCanvElementProperty(this.GetListCanvElm(),property,value,mode);           }
   CArrayObj        *GetList(ENUM_CANV_ELEMENT_PROP_STRING property,string value,ENUM_COMPARER_TYPE mode=EQUAL)            { return CSelect::ByGraphCanvElementProperty(this.GetListCanvElm(),property,value,mode);           }
//--- Return the list of existing graphical objects by a selected (1) integer, (2) real and (3) string properties meeting the compared criterion
   CArrayObj        *GetList(ENUM_GRAPH_OBJ_PROP_INTEGER property,int index,long value,ENUM_COMPARER_TYPE mode=EQUAL)      { return CSelect::ByGraphicStdObjectProperty(this.GetListGraphObj(),property,index,value,mode);    }
   CArrayObj        *GetList(ENUM_GRAPH_OBJ_PROP_DOUBLE property,int index,double value,ENUM_COMPARER_TYPE mode=EQUAL)     { return CSelect::ByGraphicStdObjectProperty(this.GetListGraphObj(),property,index,value,mode);    }
   CArrayObj        *GetList(ENUM_GRAPH_OBJ_PROP_STRING property,int index,string value,ENUM_COMPARER_TYPE mode=EQUAL)     { return CSelect::ByGraphicStdObjectProperty(this.GetListGraphObj(),property,index,value,mode);    }
//--- Return the list of removed graphical objects by a selected (1) integer, (2) real and (3) string properties meeting the compared criterion
   CArrayObj        *GetListDel(ENUM_GRAPH_OBJ_PROP_INTEGER property,int index,long value,ENUM_COMPARER_TYPE mode=EQUAL)   { return CSelect::ByGraphicStdObjectProperty(this.GetListDeletedObj(),property,index,value,mode);  }
   CArrayObj        *GetListDel(ENUM_GRAPH_OBJ_PROP_DOUBLE property,int index,double value,ENUM_COMPARER_TYPE mode=EQUAL)  { return CSelect::ByGraphicStdObjectProperty(this.GetListDeletedObj(),property,index,value,mode);  }
   CArrayObj        *GetListDel(ENUM_GRAPH_OBJ_PROP_STRING property,int index,string value,ENUM_COMPARER_TYPE mode=EQUAL)  { return CSelect::ByGraphicStdObjectProperty(this.GetListDeletedObj(),property,index,value,mode);  }
//--- Return the number of new graphical objects, (3) the flag of the occurred change in the list of graphical objects
   int               NewObjects(void)   const                                                            { return this.m_delta_graph_obj;       }
   bool              IsEvent(void) const                                                                 { return this.m_is_graph_obj_event;    }
//--- Return an (1) existing and (2) removed graphical object by chart name and ID
   CGStdGraphObj    *GetStdGraphObject(const string name,const long chart_id);
   CGStdGraphObj    *GetStdDelGraphObject(const string name,const long chart_id);
//--- Return the list of (1) chart management objects and (2) removed graphical objects
   CArrayObj        *GetListChartsControl(void)                                                          { return &this.m_list_charts_control;  }
   CArrayObj        *GetListDeletedObj(void)                                                             { return &this.m_list_deleted_obj;     }
//--- 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);
                       }
   
//--- Constructor
                     CGraphElementsCollection();
//--- Display the description of the object properties in the journal (full_prop=true - all properties, false - supported ones only - implemented in descendant classes)
   virtual void      Print(const bool full_prop=false,const bool dash=false);
//--- Display a short description of the object in the journal
   virtual void      PrintShort(const bool dash=false,const bool symbol=false);
//--- Display the history of renaming the object to the journal
   void              PrintRenameHistory(const string name,const long chart_id);

//--- Create the list of chart management objects and return the number of charts
   int               CreateChartControlList(void);
//--- Update the list of (1) all graphical objects, (2) on the specified chart, fill in the data on the number of new ones and set the event flag
   void              Refresh(void);
   void              Refresh(const long chart_id);
//--- Event handler
   void              OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam);

private:


クラスコンストラクタで、削除されたグラフィカルオブジェクトのリストをクリアし、並べ替えリストフラグを設定します。

//+------------------------------------------------------------------+
//| Constructor                                                      |
//+------------------------------------------------------------------+
CGraphElementsCollection::CGraphElementsCollection()
  {
   this.m_type=COLLECTION_GRAPH_OBJ_ID;
   ::ChartSetInteger(::ChartID(),CHART_EVENT_MOUSE_MOVE,true);
   ::ChartSetInteger(::ChartID(),CHART_EVENT_MOUSE_WHEEL,true);
   this.m_list_all_graph_obj.Type(COLLECTION_GRAPH_OBJ_ID);
   this.m_list_all_graph_obj.Sort(SORT_BY_CANV_ELEMENT_ID);
   this.m_list_all_graph_obj.Clear();
   this.m_list_charts_control.Sort();
   this.m_list_charts_control.Clear();
   this.m_total_objects=0;
   this.m_is_graph_obj_event=false;
   this.m_list_deleted_obj.Clear();
   this.m_list_deleted_obj.Sort();
  }
//+------------------------------------------------------------------+


グラフィカルオブジェクトコレクションクラスのRefresh()メソッドで、チャートからグラフィカルオブジェクトを削除するイベントを処理するときに、削除されたオブジェクトが1つだけ見つかりました。次に、削除された各オブジェクトを見つけて、削除されたオブジェクトの数だけループ内の制御プログラムチャートに適切なイベントを送信する必要があります。これは、グラフィカルオブジェクトの削除を処理する改善されたブロックで行われます

//+------------------------------------------------------------------+
//| Update the list of all graphical objects                         |
//+------------------------------------------------------------------+
void CGraphElementsCollection::Refresh(void)
  {
   this.RefreshForExtraObjects();
//--- Declare variables to search for charts
   long chart_id=0;
   int i=0;
//--- In the loop by all open charts in the terminal (no more than 100)
   while(i<CHARTS_MAX)
     {
      //--- Get the chart ID
      chart_id=::ChartNext(chart_id);
      if(chart_id<0)
         break;
      //--- Get the pointer to the object for managing graphical objects
      //--- and update the list of graphical objects by chart ID
      CChartObjectsControl *obj_ctrl=this.RefreshByChartID(chart_id);
      //--- If failed to get the pointer, move on to the next chart
      if(obj_ctrl==NULL)
         continue;
      //--- If the number of objects on the chart changes
      if(obj_ctrl.IsEvent())
        {
         //--- If a graphical object is added to the chart
         if(obj_ctrl.Delta()>0)
           {
            //--- Get the list of added graphical objects and move them to the collection list
            //--- (if failed to move the object to the collection, move on to the next object)
            if(!this.AddGraphObjToCollection(DFUN_ERR_LINE,obj_ctrl))
               continue;
           }
         //--- If the graphical object has been removed
         else if(obj_ctrl.Delta()<0)
           {
            int index=WRONG_VALUE;
            //--- In the loop by the number of removed graphical objects
            for(int j=0;j<-obj_ctrl.Delta();j++)
              {
               // Find an extra object in the list
               CGStdGraphObj *obj=this.FindMissingObj(chart_id,index);
               if(obj!=NULL)
                 {
                  //--- Get the removed object parameters
                  long   lparam=obj.ChartID();
                  string sparam=obj.Name();
                  double dparam=(double)obj.TimeCreate();
                  //--- Move the graphical object class object to the list of removed objects
                  //--- and send the event to the control program chart
                  if(this.MoveGraphObjToDeletedObjList(index))
                     ::EventChartCustom(this.m_chart_id_main,GRAPH_OBJ_EVENT_DELETE,lparam,dparam,sparam);
                 }
              }
           }
        }
      //--- Increase the loop index
      i++;
     }
  }
//+------------------------------------------------------------------+


以前、チャートウィンドウの削除を処理するメソッドでチャートウィンドウと一緒に削除されたグラフィカルオブジェクトを説明するクラスのオブジェクトを削除しましたそれらを削除されたグラフィカルオブジェクトのリストに移動します。後に、このリストを使用して、チャートで削除された各オブジェクトのすべてのプロパティを取得できるようになります。

//+------------------------------------------------------------------+
//| Handle removing the chart window                                 |
//+------------------------------------------------------------------+
void CGraphElementsCollection::RefreshForExtraObjects(void)
  {
   for(int i=this.m_list_charts_control.Total()-1;i>WRONG_VALUE;i--)
     {
      CChartObjectsControl *obj_ctrl=this.m_list_charts_control.At(i);
      if(obj_ctrl==NULL)
         continue;
      if(!this.IsPresentChartWindow(obj_ctrl.ChartID()))
        {
         long chart_id=obj_ctrl.ChartID();
         string chart_symb=obj_ctrl.Symbol();
         int total_ctrl=this.m_list_charts_control.Total();
         this.DeleteGraphObjCtrlObjFromList(obj_ctrl);
         int total_obj=this.m_list_all_graph_obj.Total();
         this.MoveGraphObjectsToDeletedObjList(chart_id);
         int del_ctrl=total_ctrl-this.m_list_charts_control.Total();
         int del_obj=total_obj-this.m_list_all_graph_obj.Total();
         ::EventChartCustom(this.m_chart_id_main,GRAPH_OBJ_EVENT_DEL_CHART,chart_id,del_obj,chart_symb);
        }
     }
  }
//+------------------------------------------------------------------+


コレクションにあるが、グラフにはないメソッドで、オブジェクトへのポインタとリスト内のそのインデックスを返します。

//+------------------------------------------------------------------+
//|Find an object present in the collection but not on a chart       |
//| Return the pointer to the object and its index in the list.      |
//+------------------------------------------------------------------+
CGStdGraphObj *CGraphElementsCollection::FindMissingObj(const long chart_id,int &index)
  {
   CArrayObj *list=CSelect::ByGraphicStdObjectProperty(this.GetListGraphObj(),GRAPH_OBJ_PROP_CHART_ID,0,chart_id,EQUAL);
   if(list==NULL)
      return NULL;
   index=WRONG_VALUE;
   for(int i=0;i<list.Total();i++)
     {
      CGStdGraphObj *obj=list.At(i);
      if(obj==NULL)
         continue;
      if(!this.IsPresentGraphObjOnChart(obj.ChartID(),obj.Name()))
        {
         index=i;
         return obj;
        }
     }
   return NULL;
  }
//+------------------------------------------------------------------+

このメソッドは、以前に実装された同じ名前のメソッドのオーバーロードです。その「パートナー」とは異なり、検出されたオブジェクトへのポインタに加えて、このメソッドはそのインデックスも返します。これは、リストのインデックスによって検出されたオブジェクトにアクセスするときに必要になる場合があります。


以下は、チャートIDですべてのオブジェクトを削除済みグラフィカルオブジェクトのリストに移動するメソッドです。

//+------------------------------------------------------------------+
//| Move all objects (by chart ID)                                   |
//| to the list of removed graphical objects                         |
//+------------------------------------------------------------------+
void CGraphElementsCollection::MoveGraphObjectsToDeletedObjList(const long chart_id)
  {
   CArrayObj *list=GetList(GRAPH_OBJ_PROP_CHART_ID,0,chart_id,EQUAL);
   if(list==NULL)
      return;
   for(int i=list.Total()-1;i>WRONG_VALUE;i--)
     {
      CGStdGraphObj *obj=list.At(i);
      if(obj==NULL)
         continue;
      this.MoveGraphObjToDeletedObjList(obj);
     }
  }
//+------------------------------------------------------------------+

このメソッドは、指定されたチャートIDを持つすべてのオブジェクトを削除済みオブジェクトのリストに移動します。
ここでは指定されたチャートIDを持つオブジェクトのリストを取得します。また、指定されたリストによるループで、次のオブジェクトを取得 、以下で検討するメソッドを使用して、削除済みグラフィカルオブジェクトのリストに移動します


以下は、削除済みオブジェクトのリストにインデックスでグラフィカルオブジェクトクラスオブジェクトを移動するメソッドです。

//+------------------------------------------------------------------+
//| Move the class object of the graphical object by index           |
//| to the list of removed graphical objects                         |
//+------------------------------------------------------------------+
bool CGraphElementsCollection::MoveGraphObjToDeletedObjList(const int index)
  {
   CGStdGraphObj *obj=this.m_list_all_graph_obj.Detach(index);
   if(obj==NULL)
     {
      CMessage::ToLog(DFUN,MSG_GRAPH_OBJ_FAILED_DETACH_OBJ_FROM_LIST);
      return false;
     }
   if(!this.m_list_deleted_obj.Add(obj))
     {
      CMessage::ToLog(DFUN,MSG_GRAPH_OBJ_FAILED_ADD_OBJ_TO_DEL_LIST);
      delete obj;
      return false;
     }
   return true;
  }
//+------------------------------------------------------------------+

メソッドは、インデックスがメソッドに渡されたオブジェクトを、コレクションリストから削除済みオブジェクトのリストに移動します。
ここで、指定されたインデックスによってリストからオブジェクトを取得しますオブジェクトの取得に失敗した場合は、そのことを通知してfalseを返します
削除済みオブジェクトのリストにオブジェクトを配置できなかった場合は、そのことを通知し、オブジェクトを削除してfalseを返します
その結果、trueを返します

このメソッドは指定されたグラフィカルオブジェクトクラスオブジェクトを削除済みグラフィカルオブジェクトのリストに移動します

//+------------------------------------------------------------------+
//| Move the specified graphical object class object                 |
//| to the list of removed graphical objects                         |
//+------------------------------------------------------------------+
bool CGraphElementsCollection::MoveGraphObjToDeletedObjList(CGStdGraphObj *obj)
  {
   this.m_list_all_graph_obj.Sort();
   int index=this.m_list_all_graph_obj.Search(obj);
   return this.MoveGraphObjToDeletedObjList(index);
  }
//+------------------------------------------------------------------+

このメソッドは、ポインタで指定されたオブジェクトを、削除済みグラフィカルオブジェクトのリストに移動します。
ここでは、並べ替えリストフラグをコレクションリストに設定し、Search()メソッドを使用してリスト内のオブジェクトインデックスを取得します。
上記のリスト内のインデックスでオブジェクトを移動するメソッドを使用する場合、削除済みグラフィカルオブジェクトのリストにオブジェクトを移動します

以下は、削除されたグラフィカルオブジェクトをチャート名とIDで返すメソッドです。

//+------------------------------------------------------------------+
//| Return a removed graphical object                                |
//| by chart name and ID                                             |
//+------------------------------------------------------------------+
CGStdGraphObj *CGraphElementsCollection::GetStdDelGraphObject(const string name,const long chart_id)
  {
   CArrayObj *list=this.GetListDel(GRAPH_OBJ_PROP_CHART_ID,0,chart_id);
   list=CSelect::ByGraphicStdObjectProperty(list,GRAPH_OBJ_PROP_NAME,0,name,EQUAL);
   return(list!=NULL && list.Total()>0 ? list.At(0) : NULL);
  }
//+------------------------------------------------------------------+

オブジェクト名とチャートIDがメソッドに渡されます。次に、チャートIDでオブジェクトのリストを受け取ります取得したリストから、名前によってオブジェクトのリストを受け取ります(そのようなオブジェクトは1つだけである必要があります)。リストが受信され、そのサイズがゼロを超える場合は、インデックス0にあるリスト内の単一のオブジェクトへのポインタを返しますそれ以外の場合はオブジェクトの取得に失敗したので、NULLを返します

イベントハンドラから、グラフィカルオブジェクトイベントの処理を担当するコードブロックを削除します。それをEAイベントハンドラに移動します。

//+------------------------------------------------------------------+
//| Event handler                                                    |
//+------------------------------------------------------------------+
void CGraphElementsCollection::OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
  {
   CGStdGraphObj *obj=NULL;
   ushort idx=ushort(id-CHARTEVENT_CUSTOM);
   if(id==CHARTEVENT_OBJECT_CHANGE  || id==CHARTEVENT_OBJECT_DRAG    || id==CHARTEVENT_OBJECT_CLICK   ||
      idx==CHARTEVENT_OBJECT_CHANGE || idx==CHARTEVENT_OBJECT_DRAG   || idx==CHARTEVENT_OBJECT_CLICK)
     {
      //--- Calculate the chart ID
      //--- If the event ID corresponds to an event from the current chart, the chart ID is received from ChartID
      //--- If the event ID corresponds to a user event, the chart ID is received from lparam
      //--- Otherwise, the chart ID is assigned to -1
      long param=(id==CHARTEVENT_OBJECT_CLICK ? ::ChartID() : idx==CHARTEVENT_OBJECT_CLICK ? lparam : WRONG_VALUE);
      long chart_id=(param==WRONG_VALUE ? (lparam==0 ? ::ChartID() : lparam) : param);
      //--- Get the object, whose properties were changed or which was relocated,
      //--- from the collection list by its name set in sparam
      obj=this.GetStdGraphObject(sparam,chart_id);
      //--- If failed to get the object by its name, it is not on the list,
      //--- which means its name has been changed
      if(obj==NULL)
        {
         //--- Let's search the list for the object that is not on the chart
         obj=this.FindMissingObj(chart_id);
         //--- If failed to find the object here as well, exit
         if(obj==NULL)
            return;
         //--- Get the name of the renamed graphical object on the chart, which is not in the collection list
         string name_new=this.FindExtraObj(chart_id);
         //--- Send an event with the old name of an object to the control program chart and
         //--- set a new name for the collection list object, which does not correspond to any graphical object on the chart
         ::EventChartCustom(this.m_chart_id_main,GRAPH_OBJ_EVENT_RENAME,obj.ChartID(),0,obj.Name());
         obj.SetName(name_new);
        }
      //--- Update the properties of the obtained object
      //--- and check their change
      obj.PropertiesRefresh();
      obj.PropertiesCheckChanged();
     }
//--- Handle standard graphical object events
   if(idx>GRAPH_OBJ_EVENT_NO_EVENT && idx<GRAPH_OBJ_EVENTS_NEXT_CODE)
     {
      //--- Depending on the event type, display an appropriate message in the journal
      switch(idx)
        {
         case GRAPH_OBJ_EVENT_CREATE   :
           ::Print(DFUN,CMessage::Text(MSG_GRAPH_OBJ_EVN_GRAPH_OBJ_CREATE));
           obj=this.GetStdGraphObject(sparam,lparam);
           if(obj!=NULL)
              obj.PrintShort();
           break;
         case GRAPH_OBJ_EVENT_CHANGE   :
           ::Print(DFUN,CMessage::Text(MSG_GRAPH_OBJ_EVN_GRAPH_OBJ_CHANGE));
           obj=this.GetStdGraphObject(sparam,lparam);
           if(obj!=NULL)
              obj.PrintShort();
           break;
         case GRAPH_OBJ_EVENT_RENAME   :
           ::Print(DFUN,CMessage::Text(MSG_GRAPH_OBJ_EVN_GRAPH_OBJ_RENAME));
           obj=this.GetStdGraphObject(sparam,lparam);
           if(obj!=NULL)
              obj.PrintShort();
           break;
         case GRAPH_OBJ_EVENT_DELETE   :
           ::Print(DFUN,CMessage::Text(MSG_GRAPH_OBJ_EVN_GRAPH_OBJ_DELETE));
           break;
         case GRAPH_OBJ_EVENT_DEL_CHART:
           ::Print(DFUN,CMessage::Text(MSG_GRAPH_OBJ_EVN_GRAPH_OBJ_DEL_CHART),": ChartID: ",lparam,", ChartSymbol: ",sparam);
           break;
         default:
           break;
        }
     }
  }
//+------------------------------------------------------------------+

ハンドラ自体で、グラフィカルオブジェクトの名前変更メッセージを送信するロジックを修正します。オブジェクトに新しい名前が設定されている場合イベントを制御プログラムチャートに送信します

//+------------------------------------------------------------------+
//| Event handler                                                    |
//+------------------------------------------------------------------+
void CGraphElementsCollection::OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
  {
   CGStdGraphObj *obj=NULL;
   ushort idx=ushort(id-CHARTEVENT_CUSTOM);
   if(id==CHARTEVENT_OBJECT_CHANGE  || id==CHARTEVENT_OBJECT_DRAG    || id==CHARTEVENT_OBJECT_CLICK   ||
      idx==CHARTEVENT_OBJECT_CHANGE || idx==CHARTEVENT_OBJECT_DRAG   || idx==CHARTEVENT_OBJECT_CLICK)
     {
      //--- Calculate the chart ID
      //--- If the event ID corresponds to an event from the current chart, the chart ID is received from ChartID
      //--- If the event ID corresponds to a user event, the chart ID is received from lparam
      //--- Otherwise, the chart ID is assigned to -1
      long param=(id==CHARTEVENT_OBJECT_CLICK ? ::ChartID() : idx==CHARTEVENT_OBJECT_CLICK ? lparam : WRONG_VALUE);
      long chart_id=(param==WRONG_VALUE ? (lparam==0 ? ::ChartID() : lparam) : param);
      //--- Get the object, whose properties were changed or which was relocated,
      //--- from the collection list by its name set in sparam
      obj=this.GetStdGraphObject(sparam,chart_id);
      //--- If failed to get the object by its name, it is not on the list,
      //--- which means its name has been changed
      if(obj==NULL)
        {
         //--- Let's search the list for the object that is not on the chart
         obj=this.FindMissingObj(chart_id);
         //--- If failed to find the object here as well, exit
         if(obj==NULL)
            return;
         //--- Get the name of the renamed graphical object on the chart, which is not in the collection list
         string name_new=this.FindExtraObj(chart_id);
         //--- set a new name for the collection list object, which does not correspond to any graphical object on the chart,
         //--- and send an event with the new name of the object to the control program chart
         if(obj.SetNamePrev(obj.Name()) && obj.SetName(name_new))
            ::EventChartCustom(this.m_chart_id_main,GRAPH_OBJ_EVENT_RENAME,obj.ChartID(),obj.TimeCreate(),obj.Name());
        }
      //--- Update the properties of the obtained object
      //--- and check their change
      obj.PropertiesRefresh();
      obj.PropertiesCheckChanged();
     }
  }
//+------------------------------------------------------------------+


以下は、操作ログにオブジェクトの名前変更履歴を表示するメソッドです。

//+------------------------------------------------------------------+
//| Display the history of renaming the object to the journal        |
//+------------------------------------------------------------------+
void CGraphElementsCollection::PrintRenameHistory(const string name,const long chart_id)
  {
   CGStdGraphObj *obj=this.GetStdGraphObject(name,chart_id);
   if(obj==NULL)
      return;
   obj.PrintRenameHistory();
  }
//+------------------------------------------------------------------+

メソッドは、グラフィカルオブジェクト名とそれが配置されているチャートのIDを受け取ります。
名前とチャートIDによってオブジェクトへのポインタを取得し、以前に検討された抽象標準グラフィカルオブジェクトクラスのPrintRenameHistory()メソッドを使用して、操作ログ内のオブジェクトの名前変更履歴を表示するメソッドを呼び出します

CEngineライブラリのメインクラスにいくつかのメソッドを追加して、プログラムでの作業をより便利にしましょう。

\MQL5\Include\DoEasy\Engine.mqhを開き、削除されたグラフィカルオブジェクトのリストを返すメソッド削除されたグラフィカルオブジェクトの数を返すメソッド指定されたプロパティ配列のサイズを返すメソッドを追加します。

//--- Launch the new pause countdown
   void                 Pause(const ulong pause_msc,const datetime time_start=0)
                          {
                           this.PauseSetWaitingMSC(pause_msc);
                           this.PauseSetTimeBegin(time_start*1000);
                           while(!this.PauseIsCompleted() && !::IsStopped()){}
                          }

//--- Return the (1) collection of graphical objects and (2) the list of removed objects
   CGraphElementsCollection *GetGraphicObjCollection(void)              { return &this.m_graph_objects;                       }
   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);
                          }
   
//--- Fill in the array with IDs of the charts opened in the terminal

これらのメソッドは、グラフィカルオブジェクトコレクションクラスの同名のメソッドによって提供される結果を返します。ここではこれ以上の詳細は必要ないと思います。


テスト

テストを実行するには、前の記事のEAを使用して、\MQL5\Experts\TestDoEasy\Part91\にTestDoEasyPart91.mq5として保存します。

必要なのは、グラフィカルオブジェクトコレクションクラスイベントのハンドラから削除されたグラフィカルオブジェクトイベントを処理するためのコードブロックをEAのOnChartEvent()ハンドラに追加することです。

//+------------------------------------------------------------------+
//| 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(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
           //--- and display the short description of a newly created object to the journal
           obj=engine.GetGraphicObjCollection().GetStdGraphObject(sparam,lparam);
           if(obj!=NULL)
             {
              obj.PrintShort();
             }
           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_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をコンパイルし、チャート上で起動します。


ご覧のとおり、オブジェクトの名前変更はその「メモリ」に保存されます。

グラフィカルオブジェクトのパッケージ削除も正しく処理されます。


以前に削除されたチャートウィンドウが復元された後、チャートと一緒に削除されたすべてのグラフィカルオブジェクトがグラフィカルオブジェクトコレクションリストに正しく復元されるわけではないので、注意してください。オブジェクトがスキップされる理由はまだわかりませんが、間違いなく修正します。

次の段階

次の記事では、グラフィカルオブジェクトイベントの作業を継続し、オブジェクトプロパティの変更履歴をオブジェクトプロパティに保存する機能の実装を開始します。

現在のライブラリバージョン、テストEA、およびMQL5のチャートイベントコントロール指標のすべてのファイルが、テストおよびダウンロードできるように以下に添付されています。質問、コメント、提案はコメント欄にお願いします。

目次に戻る

*連載のこれまでの記事:

DoEasyライブラリのグラフィックス(第86部): グラフィカルオブジェクトコレクション - プロパティ変更の管理
DoEasyライブラリのグラフィックス(第87部): グラフィカルオブジェクトコレクション - プロパティ変更の管理
DoEasyライブラリのグラフィックス(第88部): グラフィカルオブジェクトコレクション - 動的に変化するオブジェクトのプロパティを格納するための2次元動的配列
DoEasyライブラリのグラフィックス(第89部): 抽象標準グラフィカルオブジェクトのプログラミング基本機能
DoEasyライブラリのグラフィックス(第90部): 標準グラフィカルオブジェクトのイベント基本機能

MetaQuotes Ltdによってロシア語から翻訳されました。
元の記事: https://www.mql5.com/ru/articles/10184

添付されたファイル |
MQL5.zip (4181.64 KB)
一からの取引エキスパートアドバイザーの開発 一からの取引エキスパートアドバイザーの開発
この記事では、最小限のプログラミングで自動売買ロボットを開発する方法について説明します。
同事線の例で示されているローソク足パターン認識の改善 同事線の例で示されているローソク足パターン認識の改善
通常より多くのローソク足パターンを見つけるにはどうしたらよいでしょうか。ローソク足パターンの単純さの背後には、深刻な欠点もあります。これは、最新の取引自動化ツールの大幅に強化された機能を使用することで解消できます。
DoEasyライブラリのグラフィックス(第92部): 標準グラフィカルオブジェクトのメモリクラスオブジェクトプロパティの変更履歴 DoEasyライブラリのグラフィックス(第92部): 標準グラフィカルオブジェクトのメモリクラスオブジェクトプロパティの変更履歴
本稿では標準のグラフィカルオブジェクトメモリのクラスを作成して、プロパティが変更されたときにオブジェクトの状態を保存できるようにします。これにより、以前のグラフィカルオブジェクトの状態に戻ることができるようになります。
MQL5の行列とベクトル MQL5の行列とベクトル
特別な「matrix」と「vector」データ型を使用すると、数学表記に非常に近いコードを作成することができます。行列とベクトルのメソッドを使用すると、計算でネストされたループを作成したり配列で正しいインデックスを作成したりする必要がなくなるため、複雑なプログラムの開発における信頼性と速度が向上します。