English Русский 中文 Español Deutsch Português
preview
DoEasyライブラリのグラフィックス(第99部):単一のコントロールポイントを使用した拡張グラフィックオブジェクトの移動

DoEasyライブラリのグラフィックス(第99部):単一のコントロールポイントを使用した拡張グラフィックオブジェクトの移動

MetaTrader 5 | 10 6月 2022, 09:30
185 0
Artyom Trishkin
Artyom Trishkin

内容


概念

前の記事では、コントロールフォームを使用して拡張グラフィックオブジェクトのピボットポイントを移動する機能を実装しました。ただし、そのようなグラフィックオブジェクト全体を移動する機能はまだありません。標準グラフィックオブジェクトは、その中心点を移動することによって完全に移動できます。同様に、ここでは、グラフィックオブジェクトを管理するための単一の中心点を作成し、その点を移動することで、グラフィックオブジェクト全体(ピボットポイントではなく)を移動できるようにします。テストを実行するためには、両端に価格ラベルオブジェクトを備えたトレンドラインで構成される複合グラフィックオブジェクトを選択しました。これを念頭に置いて、作業全体は、端を再配置するための2つのピボットポイントとグラフィックオブジェクト全体を移動するための1つの中心点(オブジェクトの端を変更するための2つの点と、オブジェクトを移動するための中心点)を持つグラフィックオブジェクトに対しておこなわれます。後で、3つ以上のコントロールポイントを持つグラフィックオブジェクトのコントロールポイントを使用して同じフォームを作成します。

さらに、基本的なロジックをわかりやすくするために、グラフィックオブジェクトのピボットポイントを個別のメソッドに分割するという観点から、画面座標を計算するためのコードを少し最適化します。結局のところ、メインの計算ブロックにそのようなメソッドのかさばったコード全体を設定して理解するのが難しくなるよりも、特定の値を返すメソッドを呼び出すコードを読む方がはるかに簡単です(一方、内部には別のメソッドがあって、何かを計算します)。

実装されたすべてが常に意図したとおりに機能するわけではありません。本稿の目的は、必要な結果を得るためのコードの開発と作成のプロセスを説明することです。「最終的にすべてがどうなるか」についての味気ない記事を読むよりも、機能の計画から実装までのほとんどすべてをおこなう方がはるかに興味深いと思います。

画面座標を取得するChartTimePriceToXY()関数が返すのはチャートの表示部分の座標のみなので、チャートの制限外のラインポイントの画面座標を計算することはできません。表示されているチャートの左側を超えて配置されているスクリーンタイムピクセルのX座標を要求すると、この関数は常に0を返すため、複合グラフィックオブジェクトを画面に沿って移動し、その左側の部分が画面の左側の境界を超えた場合、オブジェクトの左側のピボットポイントはチャートピクセルの座標0のままになります。これによって、グラフィックオブジェクトが歪みます。グラフィックオブジェクトの右側とチャート画面の右側(および上部と下部)にも同じことが当てはまります。したがって、チャートの表示部分を超えた移動に対する制限を複合グラフィックオブジェクトに導入します。これにより、移動時にグラフィックオブジェクトのいずれかの側面が画面の端に「ぶつかった」場合に、グラフィックオブジェクトの歪みを防ぐことができます。


ライブラリクラスの改善

拡張グラフィックオブジェクトのピボットポイントを管理するためのコントロールポイントを表示するために使用されるオブジェクトフォームは、ライブラリオブジェクトの中で重要なオブジェクトであるのにグラフィックオブジェクトコレクションに含まれていないため、このようなフォームの新しいタイプを定義する必要があります。すべての重要なライブラリオブジェクトには独自のライブラリオブジェクト型の名前があるため、現在アクティブなオブジェクトを定義できます。ライブラリの拡張グラフィックオブジェクト内で、ピボットポイントを管理するためのフォームオブジェクトのタイプを定義しましょう。

\MQL5\Include\DoEasy\Defines.mqhで、ライブラリオブジェクト型の列挙に新しい型を追加します。

//+------------------------------------------------------------------+
//| List of library object types                                     |
//+------------------------------------------------------------------+
enum ENUM_OBJECT_DE_TYPE
  {
//--- Graphics
   OBJECT_DE_TYPE_GBASE =  COLLECTION_ID_LIST_END+1,              // "Base object of all library graphical objects" object type
   OBJECT_DE_TYPE_GELEMENT,                                       // "Graphical element" object type
   OBJECT_DE_TYPE_GFORM,                                          // Form object type
   OBJECT_DE_TYPE_GFORM_CONTROL,                                  // "Form for managing pivot points of graphical object" object type
   OBJECT_DE_TYPE_GSHADOW,                                        // Shadow object type
//--- Animation
   OBJECT_DE_TYPE_GFRAME,                                         // "Single animation frame" object type
   OBJECT_DE_TYPE_GFRAME_TEXT,                                    // "Single text animation frame" object type
   OBJECT_DE_TYPE_GFRAME_QUAD,                                    // "Single rectangular animation frame" object type
   OBJECT_DE_TYPE_GFRAME_GEOMETRY,                                // "Single geometric animation frame" object type
   OBJECT_DE_TYPE_GANIMATIONS,                                    // "Animations" object type
//--- Managing graphical objects
   ...
   ...
   ...
  }


\MQL5\Include\DoEasy\Data.mqhに、ライブラリの新しいメッセージインデックスを追加します

   MSG_LIB_SYS_REQUEST_OUTSIDE_LONG_ARRAY,            // Request outside long array
   MSG_LIB_SYS_REQUEST_OUTSIDE_DOUBLE_ARRAY,          // Request outside double array
   MSG_LIB_SYS_REQUEST_OUTSIDE_STRING_ARRAY,          // Request outside string array
   MSG_LIB_SYS_REQUEST_OUTSIDE_ARRAY,                 // Request outside the array
   MSG_LIB_SYS_FAILED_CONV_GRAPH_OBJ_COORDS_TO_XY,    // Failed to convert graphical object coordinates to screen ones
   MSG_LIB_SYS_FAILED_CONV_TIMEPRICE_COORDS_TO_XY,    // Failed to convert time/price coordinates to screen ones

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

   {"Запрос за пределами long-массива","Data requested outside the long-array"},
   {"Запрос за пределами double-массива","Data requested outside the double-array"},
   {"Запрос за пределами string-массива","Data requested outside the string-array"},
   {"Запрос за пределами массива","Data requested outside the array"},
   {"Не удалось преобразовать координаты графического объекта в экранные","Failed to convert graphics object coordinates to screen coordinates"},
   {"Не удалось преобразовать координаты время/цена в экранные","Failed to convert time/price coordinates to screen coordinates"},


グラフィックオブジェクトを移動する機能を開発しているときに時間/価格座標を画面座標に変換するエラーが検出された場合、そのようなエラーはロジックエラーの検索からチェーンを除外するために報告されます。

ChartTimePriceToXY() 関数は、座標変換エラーを引き起こす可能性がありますが、 \MQL5\Include\DoEasy\Objects\Chart\ChartWnd.mqhのチャートウィンドウオブジェクトクラスでも使用されています。操作ログでの座標変換エラーメッセージの表示をTimePriceToXY()メソッドに追加しましょう。

//+------------------------------------------------------------------+
//| Convert chart coordinates from the time/price representation     |
//| to X and Y coordinates                                           |
//+------------------------------------------------------------------+
bool CChartWnd::TimePriceToXY(const datetime time,const double price)
  {
   ::ResetLastError();
   if(!::ChartTimePriceToXY(this.m_chart_id,this.WindowNum(),time,price,this.m_wnd_coord_x,this.m_wnd_coord_y))
     {
      CMessage::ToLog(DFUN,MSG_LIB_SYS_FAILED_CONV_TIMEPRICE_COORDS_TO_XY);
      CMessage::ToLog(DFUN,::GetLastError(),true);
      return false;
     }
   return true;
  }
//+------------------------------------------------------------------+

まず、「時間/価格座標を画面座標に変換できませんでした」というメッセージを表示 し、続いて エラーの説明とエラーコードを表示します。


拡張された標準グラフィックオブジェクトのピボットポイントを管理するためのコントロールポイントの新しいライブラリオブジェクト型を宣言したので、フォームオブジェクトクラスから継承されたそのようなオブジェクトのクラスを作成します。クラス内には、そのようなオブジェクトの処理を簡素化するためのいくつかの変数とメソッドを追加します。

\MQL5\Include\DoEasy\Objects\Graph\Extend\CGStdGraphObjExtToolkit.mqhにある拡張標準グラフィックオブジェクトのツールキットに設定します。

//+------------------------------------------------------------------+
//|                                      CGStdGraphObjExtToolkit.mqh |
//|                                  Copyright 2022, MetaQuotes Ltd. |
//|                             https://mql5.com/en/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2022, MetaQuotes Ltd."
#property link      "https://mql5.com/en/users/artmedia70"
#property version   "1.00"
#property strict    // Necessary for mql4
//+------------------------------------------------------------------+
//| Include files                                                    |
//+------------------------------------------------------------------+
#include "..\..\Graph\Form.mqh"
//+------------------------------------------------------------------+
//| Class of the form for managing pivot points of a graphical object|
//+------------------------------------------------------------------+
class CFormControl : public CForm
  {
private:
   bool              m_drawn;                   // Flag indicating that the pivot point is drawn on the form
   int               m_pivot_point;             // Pivot point managed by the form
public:
//--- (1) Return and (2) set the drawn point flag
   bool              IsControlAlreadyDrawn(void)               const { return this.m_drawn;                       }
   void              SetControlPointDrawnFlag(const bool flag)       { this.m_drawn=flag;                         }
//--- (1) Return and (2) set the pivot point managed by the form
   int               GraphObjPivotPoint(void)                  const { return this.m_pivot_point;                 }
   void              SetGraphObjPivotPoint(const int index)          { this.m_pivot_point=index;                  }
//--- Constructor
                     CFormControl(void)                              { this.m_type=OBJECT_DE_TYPE_GFORM_CONTROL;  }
                     CFormControl(const long chart_id,const int subwindow,const string name,const int pivot_point,const int x,const int y,const int w,const int h) :
                        CForm(chart_id,subwindow,name,x,y,w,h)
                          {
                           this.m_type=OBJECT_DE_TYPE_GFORM_CONTROL;
                           this.m_pivot_point=pivot_point;
                          }
  };
//+------------------------------------------------------------------+
//| Extended standard graphical                                      |
//| object toolkit class                                             |
//+------------------------------------------------------------------+
class CGStdGraphObjExtToolkit : public CObject

m_drawn privateクラスメンバー変数は、ポイントがフォームにすでに描画されていることを通知するフラグを格納するためのものです。なぜそのような変数が必要なのでしょうか。グラフィックオブジェクトのコントロールポイントを管理するためにコントロールフォームのアクティブゾーンからマウスカーソルを削除した場合、フォームに描画されたポイントを削除する必要があります。現在、マウスカーソルがフォームのアクティブな領域に合わせられていない場合、描画されたポイントはそのようなすべてのフォームで常に削除されます。フォームの再描画フラグを最初に確認できるのに、なぜこのようにすべてのフォームを絶えず再描画することでシステムに負担をかける必要があるのでしょうか。フラグがポイントが描画または削除されたことを通知します。将来的には、そのようなポイントを描画するための視覚効果を(他のものに加えて)開発する予定です。したがって、視覚効果ハンドラを実行した直後にフラグを設定する方が、描画はすでに完了していることを定義するよりも優れています。

クラスのprivateメンバー変数m_pivot_pointは、フォームによって管理されるピボットポイントインデックスを格納するためのものです。グラフィックオブジェクトにはいくつかのコントロールポイントがあります。たとえば、トレンドラインには3つのポイントがあります。ラインの終点の位置を個別に変更するためのラインの2つの終点と、オブジェクト全体を移動するための中心点です。フォームオブジェクトに格納されているインデックスは、ラインのピボットポイントのインデックス(0と1 - 線の端に沿った点、2 - 中心点)に対応します。他のグラフィックオブジェクトのコントロールポイントは完全に異なる場合がありますが、すべてのインデックスは、オブジェクト全体を移動するためのオブジェクトピボットポイント+追加の1つ(すべての場合ではありませんが、これについては後続の記事で説明します)に対応します。

クラスのpublicメソッドは上記の変数の値を設定/返すために使用されます。クラスには2つのコンストラクタもあります。デフォルトのコンストラクタでは、本稿で追加された新しいOBJECT_DE_TYPE_GFORM_CONTROLのタイプ がオブジェクトタイプに設定されます。
パラメトリックコンストラクタは、親クラスコンストラクタに渡されるすべての値に加えて、作成されたフォームによって管理されるグラフィックオブジェクトのピボットポイントのインデックスである追加の変数を渡します。

これで、CGStdGraphObjExtToolkitクラスのすべてのピボットポイントコントロールフォームがCFormControl型になるため、CFormフォームオブジェクトの型をCFormControlに置き換えグラフィックオブジェクトのピボットポイントを管理するためのコントロールフォームを処理するための新しいメソッドを追加する必要があります。

//+------------------------------------------------------------------+
//| Extended standard graphical                                      |
//| object toolkit class                                             |
//+------------------------------------------------------------------+
class CGStdGraphObjExtToolkit : public CObject
  {
private:
   long              m_base_chart_id;           // Base graphical object chart ID
   int               m_base_subwindow;          // Base graphical object chart subwindow
   ENUM_OBJECT       m_base_type;               // Base object type
   string            m_base_name;               // Base object name
   int               m_base_pivots;             // Number of base object reference points
   datetime          m_base_time[];             // Time array of base object reference points
   double            m_base_price[];            // Price array of base object reference points
   int               m_base_x;                  // Base object X coordinate
   int               m_base_y;                  // Base object Y coordinate
   int               m_ctrl_form_size;          // Size of forms for managing reference points
   int               m_shift;                   // Shift coordinates for adjusting the form location
   CArrayObj         m_list_forms;              // List of form objects for managing reference points
//--- Create a form object on a base object reference point
   CFormControl     *CreateNewControlPointForm(const int index);
//--- Return X and Y (1) screen coordinates of the specified reference point of the graphical object
   bool              GetControlPointCoordXY(const int index,int &x,int &y);
//--- Set the parameters of a form object for managing pivot points
   void              SetControlFormParams(CFormControl *form,const int index);
public:
//--- Set the parameters of the base object of a composite graphical object
   void              SetBaseObj(const ENUM_OBJECT base_type,const string base_name,
                                const long base_chart_id,const int base_subwindow,
                                const int base_pivots,const int ctrl_form_size,
                                const int base_x,const int base_y,
                                const datetime &base_time[],const double &base_price[]);
//--- Set the base object (1) time, (2) price, (3) time and price coordinates
   void              SetBaseObjTime(const datetime time,const int index);
   void              SetBaseObjPrice(const double price,const int index);
   void              SetBaseObjTimePrice(const datetime time,const double price,const int index);
//--- Set the base object (1) X, (2) Y, (3) X and Y screen coordinates
   void              SetBaseObjCoordX(const int value)                        { this.m_base_x=value;                          }
   void              SetBaseObjCoordY(const int value)                        { this.m_base_y=value;                          }
   void              SetBaseObjCoordXY(const int value_x,const int value_y)   { this.m_base_x=value_x; this.m_base_y=value_y; }
//--- (1) Set and (2) return the size of the form of pivot point management control points
   void              SetControlFormSize(const int size);
   int               GetControlFormSize(void)                           const { return this.m_ctrl_form_size;                 }
//--- Return the pointer to the pivot point form by (1) index and (2) name
   CFormControl     *GetControlPointForm(const int index)                     { return this.m_list_forms.At(index);           }
   CFormControl     *GetControlPointForm(const string name,int &index);
//--- Return the number of (1) base object pivot points and (2) newly created form objects for managing control points
   int               GetNumPivotsBaseObj(void)                          const { return this.m_base_pivots;                    }
   int               GetNumControlPointForms(void)                      const { return this.m_list_forms.Total();             }
//--- Create form objects on the base object pivot points
   bool              CreateAllControlPointForm(void);
//--- (1) Draw a control point on the form, (2) draw a control point on the form and delete it on all other forms
   void              DrawControlPoint(CFormControl *form,const uchar opacity,const color clr);
   void              DrawOneControlPoint(CFormControl *form,const uchar opacity=255,const color clr=CTRL_POINT_COLOR);
//--- (1) Draw using a default color, (remove) a control point on the form
   void              DrawControlPoint(CFormControl *form)                     { this.DrawControlPoint(form,255,CTRL_POINT_COLOR);}
   void              ClearControlPoint(CFormControl *form)                    { this.DrawControlPoint(form,0,CTRL_POINT_COLOR);  }
//--- Remove all form objects from the list
   void              DeleteAllControlPointForm(void);
   
//--- Event handler
   void              OnChartEvent(const int id,const long& lparam,const double& dparam,const string& sparam);

//--- Constructor/destructor
                     CGStdGraphObjExtToolkit(const ENUM_OBJECT base_type,const string base_name,
                                             const long base_chart_id,const int base_subwindow,
                                             const int base_pivots,const int ctrl_form_size,
                                             const int base_x,const int base_y,
                                             const datetime &base_time[],const double &base_price[])
                       {
                        this.m_list_forms.Clear();
                        this.SetBaseObj(base_type,base_name,base_chart_id,base_subwindow,base_pivots,ctrl_form_size,base_x,base_y,base_time,base_price);
                        this.CreateAllControlPointForm();
                       }
                     CGStdGraphObjExtToolkit(){;}
                    ~CGStdGraphObjExtToolkit(){;}
  };
//+------------------------------------------------------------------+


グラフィックオブジェクトの指定されたピボットポイントのX座標とY座標を画面座標で返すGetControlPointCoordXY()メソッドを改善します。

以前は、このメソッドは指定されたグラフィックオブジェクトのピボットポイントの計算された座標を返すだけでしたが、グラフィックオブジェクトのピボットポイントの数と中央のピボットポイントの位置が異なる可能性があることを考慮する必要があります。switch()でさまざまな型のオブジェクトの計算を行いましょう。さらに、取得するピボットポイントの座標(オブジェクトの端に沿って配置されている座標または中央の座標)を考慮する必要があります。メソッドに渡されるピボットポイントのインデックスがグラフィックオブジェクトのピボットポイントの総数よりも少ない場合、ピボットポイントの座標が要求されます。それ以外の場合は、中央のピボットポイントの座標が要求されます。

今のところ、端に2つのピボットポイントと1つの中心点を持つグラフィックオブジェクトに対してのみ、X座標とY座標の受信を実装します。

//+------------------------------------------------------------------+
//| Return the X and Y coordinates of the specified pivot point      |
//| of the graphical object in screen coordinates                    |
//+------------------------------------------------------------------+
bool CGStdGraphObjExtToolkit::GetControlPointCoordXY(const int index,int &x,int &y)
  {
//--- Declare form objects, from which we are to receive their screen coordinates
   CFormControl *form0=NULL, *form1=NULL;
//--- Set X and Y to zero - these values will be received in case of a failure
   x=0;
   y=0;
//--- Depending on the graphical object type
   switch(this.m_base_type)
     {
      //--- Objects drawn using screen coordinates
      case OBJ_LABEL             :
      case OBJ_BUTTON            :
      case OBJ_BITMAP_LABEL      :
      case OBJ_EDIT              :
      case OBJ_RECTANGLE_LABEL   :
      case OBJ_CHART             :
      case OBJ_EVENT             :
        //--- Write object screen coordinates and return 'true'
        x=this.m_base_x;
        y=this.m_base_y;
        return true;
      
      //--- Lines (vertical and horizontal)
      case OBJ_VLINE             : break;
      case OBJ_HLINE             : break;
      //--- Lines
      case OBJ_TREND             :
      case OBJ_TRENDBYANGLE      :
      case OBJ_CYCLES            :
      case OBJ_ARROWED_LINE      :
      //--- Channels
      case OBJ_CHANNEL           :
      case OBJ_STDDEVCHANNEL     :
      case OBJ_REGRESSION        :
      //--- Gann
      case OBJ_GANNLINE          :
      case OBJ_GANNGRID          :
      //--- Fibo
      case OBJ_FIBO              :
      case OBJ_FIBOTIMES         :
      case OBJ_FIBOFAN           :
      case OBJ_FIBOARC           :
      case OBJ_FIBOCHANNEL       :
      case OBJ_EXPANSION         :
        //--- Calculate coordinates for forms on the line pivot points
        if(index<this.m_base_pivots)
           return(::ChartTimePriceToXY(this.m_base_chart_id,this.m_base_subwindow,this.m_base_time[index],this.m_base_price[index],x,y) ? true : false);
        //--- Calculate the coordinates for the central form located between the line pivot points
        else
          {
           form0=this.GetControlPointForm(0);
           form1=this.GetControlPointForm(1);
           if(form0==NULL || form1==NULL)
              return false;
           x=(form0.CoordX()+this.m_shift+form1.CoordX()+this.m_shift)/2;
           y=(form0.CoordY()+this.m_shift+form1.CoordY()+this.m_shift)/2;
           return true;
          }

      //--- Channels
      case OBJ_PITCHFORK         : break;
      //--- Gann
      case OBJ_GANNFAN           : break;
      //--- Elliott
      case OBJ_ELLIOTWAVE5       : break;
      case OBJ_ELLIOTWAVE3       : break;
      //--- Shapes
      case OBJ_RECTANGLE         : break;
      case OBJ_TRIANGLE          : break;
      case OBJ_ELLIPSE           : break;
      //--- Arrows
      case OBJ_ARROW_THUMB_UP    : break;
      case OBJ_ARROW_THUMB_DOWN  : break;
      case OBJ_ARROW_UP          : break;
      case OBJ_ARROW_DOWN        : break;
      case OBJ_ARROW_STOP        : break;
      case OBJ_ARROW_CHECK       : break;
      case OBJ_ARROW_LEFT_PRICE  : break;
      case OBJ_ARROW_RIGHT_PRICE : break;
      case OBJ_ARROW_BUY         : break;
      case OBJ_ARROW_SELL        : break;
      case OBJ_ARROW             : break;
      //--- Graphical objects with time/price coordinates
      case OBJ_TEXT              : break;
      case OBJ_BITMAP            : break;
      //---
      default                    : break;
     }
   return false;
  }
//+------------------------------------------------------------------+

ピボットポイントの計算は、ラインピボットポイント座標の m_base_timeおよびm_base_price配列で設定された値を使用して実行されます。中心点の座標を計算するには、線の端のピボットポイントに接続されたフォームオブジェクトの座標を使用します。座標計算が成功した場合、メソッドはすぐにtrueを返します。それ以外の場合はすべて、false を返すか、breakを適用してswitchcaseのコードの実行を停止し、メソッドの最後に移動してfalseを返します。

ピボットポイントフォームへのポインタを名前で返すメソッドで、CFormをCFormControlに置き換えます

//+------------------------------------------------------------------+
//| Return the pointer to the pivot point form by name               |
//+------------------------------------------------------------------+
CFormControl *CGStdGraphObjExtToolkit::GetControlPointForm(const string name,int &index)
  {
   index=WRONG_VALUE;
   for(int i=0;i<this.m_list_forms.Total();i++)
     {
      CFormControl *form=this.m_list_forms.At(i);
      if(form==NULL)
         continue;
      if(form.Name()==name)
        {
         index=i;
         return form;
        }
     }
   return NULL;
  }
//+------------------------------------------------------------------+

基本オブジェクトのピボットポイントにフォームオブジェクトを作成するメソッドで、CFormをCFormControlに置き換え正常に作成されたフォームオブジェクトのパラメーターを設定します

//+------------------------------------------------------------------+
//| Create a form object on a base object reference point            |
//+------------------------------------------------------------------+
CFormControl *CGStdGraphObjExtToolkit::CreateNewControlPointForm(const int index)
  {
   string name=this.m_base_name+"_CP_"+(index<this.m_base_pivots ? (string)index : "X");
   CFormControl *form=this.GetControlPointForm(index);
   if(form!=NULL)
      return NULL;
   int x=0, y=0;
   if(!this.GetControlPointCoordXY(index,x,y))
      return NULL;
   form=new CFormControl(this.m_base_chart_id,this.m_base_subwindow,name,index,x-this.m_shift,y-this.m_shift,this.GetControlFormSize(),this.GetControlFormSize());
//--- Set all the necessary properties for the created form object
   if(form!=NULL)
      this.SetControlFormParams(form,index);
   return form;
  }
//+------------------------------------------------------------------+


基本オブジェクトのピボットポイントにフォームオブジェクトを作成するメソッドでは 、CFormをCFormControlに置き換え、作成されたフォームオブジェクトのパラメータを設定するための文字列を削除します。これは、上記のメソッドでオブジェクトを作成するときにパラメータがすぐに設定されるためです。

//+------------------------------------------------------------------+
//| Create form objects on the base object pivot points              |
//+------------------------------------------------------------------+
bool CGStdGraphObjExtToolkit::CreateAllControlPointForm(void)
  {
   bool res=true;
//--- In the loop by the number of base object pivot points
   for(int i=0;i<=this.m_base_pivots;i++)
     {
      //--- Create a new form object on the current pivot point corresponding to the loop index
      CFormControl *form=this.CreateNewControlPointForm(i);
      //--- If failed to create the form, inform of that and add 'false' to the final result
      if(form==NULL)
        {
         CMessage::ToLog(DFUN,MSG_GRAPH_OBJ_EXT_FAILED_CREATE_CTRL_POINT_FORM);
         res &=false;
        }
      //--- If failed to add the form to the list, inform of that, remove the created form and add 'false' to the final result
      if(!this.m_list_forms.Add(form))
        {
         CMessage::ToLog(DFUN,MSG_LIB_SYS_FAILED_OBJ_ADD_TO_LIST);
         delete form;
         res &=false;
        }
     }
//--- Redraw the chart for displaying changes (if successful) and return the final result
   if(res)
      ::ChartRedraw(this.m_base_chart_id);
   return res;
  }
//+------------------------------------------------------------------+

これで、基本オブジェクトのピボットポイントの数に1を加えた数のループが実行されます。つまり、作成されたフォームの数が、グラフィックオブジェクトのピボットポイントの数を1つ上回っています。最後のフォームは中心のもので、グラフィックオブジェクト全体を移動するためのものです。

以下は、ピボットポイントを管理するためのフォームオブジェクトのパラメータを設定するメソッドです。

//+------------------------------------------------------------------+
//| Set the parameters of a form object for managing pivot points    |
//+------------------------------------------------------------------+
void CGStdGraphObjExtToolkit::SetControlFormParams(CFormControl *form,const int index)
  {
   form.SetBelong(GRAPH_OBJ_BELONG_PROGRAM);                // Object is created programmatically
   form.SetActive(true);                                    // Form object is active
   form.SetMovable(true);                                   // Movable object
   int x=(int)::floor((form.Width()-CTRL_POINT_RADIUS*2)/2);// Shift the active area from the form edge
   form.SetActiveAreaShift(x,x,x,x);                        // Object active area is located at the center of the form, its size is equal to two CTRL_POINT_RADIUS values
   form.SetFlagSelected(false,false);                       // Object is not selected
   form.SetFlagSelectable(false,false);                     // Object cannot be selected by mouse
   form.Erase(CLR_CANV_NULL,0);                             // Fill in the form with transparent color and set the full transparency
   //form.DrawRectangle(0,0,form.Width()-1,form.Height()-1,clrSilver);    // Draw an outlining rectangle for visual display of the form location
   //form.DrawRectangle(x,x,form.Width()-x-1,form.Height()-x-1,clrSilver);// Draw an outlining rectangle for visual display of the form active area location
   form.SetID(index+1);                                     // Set the form ID
   form.SetControlPointDrawnFlag(false);                    // Set the flag that the pivot point is not drawn on the form
   form.Done();                                             // Save the initial form object state (its appearance)
  }
//+------------------------------------------------------------------+

ここには、上記で検討したメソッドから転送されたコードの文字列があります。それに、フォームに描かれたポイントとフォームIDのフラグがあります

フォームにコントロールポイントを描画する方法では、計算の重複を避けるために、フォームの中心の計算を別の文字列に再配置します。メソッドが完了したら、フォームに描画ポイントのフラグを設定します

//+------------------------------------------------------------------+
//| Draw a control point on the form                                 |
//+------------------------------------------------------------------+
void CGStdGraphObjExtToolkit::DrawControlPoint(CFormControl *form,const uchar opacity,const color clr)
  {
   if(form==NULL)
      return;
   int c=int(::floor(form.Width()/2));                      // Form center (coordinates)
   form.DrawCircle(c,c,CTRL_POINT_RADIUS,clr,opacity);      // Draw a circle in the form center
   form.DrawCircleFill(c,c,2,clr,opacity);                  // Draw a circle in the form center
   form.SetControlPointDrawnFlag(opacity>0 ? true : false); // Set the flag that the pivot point is drawn on the form
  }
//+------------------------------------------------------------------+


現在、グラフィックオブジェクトのピボットポイントを管理するためにフォームにマウスを合わせると、その上にポイントが表示され、カーソルがフォームを離れた後にのみ削除されます。ただし、オブジェクトのすべてのコントロールポイントを近づけて、グラフィックオブジェクトの端に作成されたフォームと中央のフォームが互いに重なり始めると、1つのフォームから遠ざけたカーソルが近くにありる別のフォームに移動します。したがって、すべてのオブジェクトフォームのすべてのポイントが表示されるように作成することができます。

フォームをつかんで動かし始めると、オブジェクトのピボットポイントも動き始めます。エラーが原因で表示されているフォームは、再配置が開始された前の場所に残ります。この動作は正しくありません。したがって、1つのグラフィックオブジェクトフォームオブジェクトにポイントを描画すると同時に同じオブジェクトの他のフォームオブジェクトのポイントを削除するメソッドが必要です。

以下は、フォームにコントロールポイントを描画し、残りのすべてのフォームからコントロールポイントを削除するメソッドです。

//+------------------------------------------------------------------+
//| Draw a control point on the form,                                |
//| remove it on all other forms                                     |
//+------------------------------------------------------------------+
void CGStdGraphObjExtToolkit::DrawOneControlPoint(CFormControl *form,const uchar opacity=255,const color clr=CTRL_POINT_COLOR)
  {
   this.DrawControlPoint(form,opacity,clr);
   for(int i=0;i<this.GetNumControlPointForms();i++)
     {
      CFormControl *ctrl=this.GetControlPointForm(i);
      if(ctrl==NULL || ctrl.ID()==form.ID())
         continue;
      this.ClearControlPoint(ctrl);
     }
  }
//+------------------------------------------------------------------+

ここで、メソッドはカーソルが合わせられているフォームへのポインタを受け取りますフォームに点を描きます。次に、すべてのオブジェクトフォームによるループで、フォームを選択し、フォームがメソッドに渡されていない場合は、フォームからポイントを削除します。

イベントハンドラでCFormフォーム型をCFormControlに置き換えます

//+------------------------------------------------------------------+
//| Event handler                                                    |
//+------------------------------------------------------------------+
void CGStdGraphObjExtToolkit::OnChartEvent(const int id,const long& lparam,const double& dparam,const string& sparam)
  {
   if(id==CHARTEVENT_CHART_CHANGE)
     {
      for(int i=0;i<this.m_list_forms.Total();i++)
        {
         CFormControl *form=this.m_list_forms.At(i);
         if(form==NULL)
            continue;
         int x=0, y=0;
         if(!this.GetControlPointCoordXY(i,x,y))
            continue;
         form.SetCoordX(x-this.m_shift);
         form.SetCoordY(y-this.m_shift);
         form.Update();
        }
      ::ChartRedraw(this.m_base_chart_id);
     }
  }
//+------------------------------------------------------------------+


\MQL5\Include\DoEasy\Objects\Graph\Standard\GStdGraphObj.mqhにある抽象標準グラフィックオブジェクトのクラスでメソッドコードの最適化を改善してみましょう。さまざまなメソッドに同様のコードフラグメントがあるため、このようなコードブロックを個別のメソッドとして実装して必要に応じて呼び出して、コードを読みやすくするのが道理にかなっています。

クラスのpublicprivateセクションで、繰り返しコードフラグメントを含む新しいメソッドを宣言します。

//--- Return (1) the list of dependent objects, (2) dependent graphical object by index and (3) the number of dependent objects
   CArrayObj        *GetListDependentObj(void)        { return &this.m_list;           }
   CGStdGraphObj    *GetDependentObj(const int index) { return this.m_list.At(index);  }
   int               GetNumDependentObj(void)         { return this.m_list.Total();    }
//--- Return the name of the dependent object by index
   string            NameDependent(const int index);
//--- Add the dependent graphical object to the list
   bool              AddDependentObj(CGStdGraphObj *obj);
//--- Change X and Y coordinates of the current and all dependent objects
   bool              ChangeCoordsExtendedObj(const int x,const int y,const int modifier,bool redraw=false);
//--- Set X and Y coordinates into the appropriate pivot points of a specified subordinate object
   bool              SetCoordsXYtoDependentObj(CGStdGraphObj *dependent_obj);
   
//--- Return the pivot point data object
   CLinkedPivotPoint*GetLinkedPivotPoint(void)        { return &this.m_linked_pivots;  }

...

private:
//--- Set the X coordinate (1) from the specified property of the base object to the specified subordinate object, (2) from the base object
   void              SetCoordXToDependentObj(CGStdGraphObj *obj,const int prop_from,const int modifier_from,const int modifier_to);
   void              SetCoordXFromBaseObj(const int prop_from,const int modifier_from,const int modifier_to);
//--- Set the Y coordinate (1) from the specified property of the base object to the specified subordinate object, (2) from the base object
   void              SetCoordYToDependentObj(CGStdGraphObj *obj,const int prop_from,const int modifier_from,const int modifier_to);
   void              SetCoordYFromBaseObj(const int prop_from,const int modifier_from,const int modifier_to);
//--- Set X and Y coordinates into the appropriate pivot point of a specified subordinate object
   void              SetCoordsXYtoDependentObj(CGStdGraphObj *dependent_obj,CLinkedPivotPoint *pivot_point,const int index);
//--- Set the (1) integer, (2) real and (3) string property to the specified subordinate property
   void              SetDependentINT(CGStdGraphObj *obj,const ENUM_GRAPH_OBJ_PROP_INTEGER prop,const long value,const int modifier);
   void              SetDependentDBL(CGStdGraphObj *obj,const ENUM_GRAPH_OBJ_PROP_DOUBLE prop,const double value,const int modifier);
   void              SetDependentSTR(CGStdGraphObj *obj,const ENUM_GRAPH_OBJ_PROP_STRING prop,const string value,const int modifier);

public:
//--- Event handler


オブジェクトプロパティの変更をチェックするメソッドで、指定されたコードブロックを削除します (コードは別のメソッドに移動されます)。

      //--- If subordinate objects are attached to the base one (in a composite graphical object)
      if(this.m_list.Total()>0)
        {
         //--- In the loop by the number of added graphical objects,
         for(int i=0;i<this.m_list.Total();i++)
           {
            //--- get the next graphical object,
            CGStdGraphObj *dep=m_list.At(i);
            if(dep==NULL)
               continue;
            //--- get the data object of its pivot points,
            CLinkedPivotPoint *pp=dep.GetLinkedPivotPoint();
            if(pp==NULL)
               continue;
            //--- get the number of coordinate points the object is attached to
            int num=pp.GetNumLinkedCoords();
            //--- In the loop by the object coordinate points,
            for(int j=0;j<num;j++)
              {
               //--- get the number of coordinate points of the base object for setting the X coordinate
               int numx=pp.GetBasePivotsNumX(j);
               //--- In the loop by each coordinate point for setting the X coordinate,
               for(int nx=0;nx<numx;nx++)
                 {
                  //--- get the property for setting the X coordinate, its modifier
                  //--- and set it in the object selected as the current one in the main loop
                  int prop_from=pp.GetPropertyX(j,nx);
                  int modifier_from=pp.GetPropertyModifierX(j,nx);
                  this.SetCoordXToDependentObj(dep,prop_from,modifier_from,nx);
                 }
               //--- Get the number of coordinate points of the base object for setting the Y coordinate
               int numy=pp.GetBasePivotsNumY(j);
               //--- In the loop by each coordinate point for setting the Y coordinate,
               for(int ny=0;ny<numy;ny++)
                 {
                  //--- get the property for setting the Y coordinate, its modifier
                  //--- and set it in the object selected as the current one in the main loop
                  int prop_from=pp.GetPropertyY(j,ny);
                  int modifier_from=pp.GetPropertyModifierY(j,ny);
                  this.SetCoordYToDependentObj(dep,prop_from,modifier_from,ny);
                 }
              }
            dep.PropertiesCopyToPrevData();
           }
         //--- Move reference control points to new coordinates
         if(ExtToolkit!=NULL)
           {
            for(int i=0;i<this.Pivots();i++)
              {
               ExtToolkit.SetBaseObjTimePrice(this.Time(i),this.Price(i),i);
              }
            ExtToolkit.SetBaseObjCoordXY(this.XDistance(),this.YDistance());
            long   lparam=0;
            double dparam=0;
            string sparam="";
            ExtToolkit.OnChartEvent(CHARTEVENT_CHART_CHANGE,lparam,dparam,sparam);
           }
         //--- Upon completion of the loop of handling all bound objects, redraw the chart to display all the changes
         ::ChartRedraw(m_chart_id);
        }

削除されたブロックの代わりに新しいメソッドの呼び出しを追加します。

      //--- If subordinate objects are attached to the base one (in a composite graphical object)
      if(this.m_list.Total()>0)
        {
         //--- In the loop by the number of added graphical objects,
         for(int i=0;i<this.m_list.Total();i++)
           {
            //--- get the next graphical object,
            CGStdGraphObj *dep=m_list.At(i);
            if(dep==NULL)
               continue;
            //--- Set X and Y coordinates to all pivot points of a subordinate object and
            //--- save the current properties of a subordinate graphical object as the previous ones
            if(this.SetCoordsXYtoDependentObj(dep))
               dep.PropertiesCopyToPrevData();
           }
         //--- Move reference control points to new coordinates
         if(this.ExtToolkit!=NULL)
           {
            for(int i=0;i<this.Pivots();i++)
              {
               this.ExtToolkit.SetBaseObjTimePrice(this.Time(i),this.Price(i),i);
              }
            this.ExtToolkit.SetBaseObjCoordXY(this.XDistance(),this.YDistance());
            long   lparam=0;
            double dparam=0;
            string sparam="";
            this.ExtToolkit.OnChartEvent(CHARTEVENT_CHART_CHANGE,lparam,dparam,sparam);
           }
         //--- Upon completion of the loop of handling all bound objects, redraw the chart to display all the changes
         ::ChartRedraw(m_chart_id);
        }


現在のポイント削除ロジックでは、カーソルがどのフォームにも設定されていない場合にはそのような各フォームが常に再描画されるため(これは最適ではなく、リソースを大量に消費します)、ポイントを実際に削除する必要があり、拡張された標準グラフィックオブジェクトのコントロールポイントを管理するためのフォームを再描画するメソッドにポイントがまだ存在する場合にのみ、ポイント削除フォームを再描画する必要があることを確認するチェックを実装しましょう。また、フォームオブジェクトタイプを新しいものに置き換えます

//+------------------------------------------------------------------+
//| Redraw the form for managing a control point                     |
//| of an extended standard graphical object                         |
//+------------------------------------------------------------------+
void CGStdGraphObj::RedrawControlPointForms(const uchar opacity,const color clr)
  {
//--- Leave if the object has no toolkit of an extended standard graphical object
   if(this.ExtToolkit==NULL)
      return;
//--- Get the number of pivot point management forms
   int total_form=this.GetNumControlPointForms();
//--- In the loop by the number of pivot point management forms
   for(int i=0;i<total_form;i++)
     {
      //--- get the next form object
      CFormControl *form=this.ExtToolkit.GetControlPointForm(i);
      if(form==NULL)
         continue;
      //--- Draw a point and a circle with a specified non-transparency and color
      //--- If a point should be completely transparent (deleted)
      //--- and the form still has the point, delete the point,
      if(opacity==0 && form.IsControlAlreadyDrawn())
         this.ExtToolkit.DrawControlPoint(form,0,clr);
      //--- otherwise, draw the point with a specified non-transparency and color
      else
         this.ExtToolkit.DrawControlPoint(form,opacity,clr);
     }
   
//--- Get the total number of bound graphical objects
   int total_dep=this.GetNumDependentObj();
//--- In the loop by all bound graphical objects,
   for(int i=0;i<total_dep;i++)
     {
      //--- get the next graphical object from the list
      CGStdGraphObj *dep=this.GetDependentObj(i);
      if(dep==NULL)
         continue;
      //--- call the method for it
      dep.RedrawControlPointForms(opacity,clr);
     }
  }
//+------------------------------------------------------------------+

ポイントの削除は、ポイントを実際に削除する必要があり(ポイントの非透明度がゼロに設定されている)、かつポイントがまだ存在している(描画ポイントフラグが設定されている)場合にのみ実行されるようになりました。

また、新しいメソッドの呼び出しで置き換えられるコードセグメントを削除することにより、現在のオブジェクトとすべての依存オブジェクトのX座標とY座標を変更するメソッドを作り直してみましょう。

//+----------------------------------------------------------------------+
//| Change X and Y coordinates of the current and all dependent objects  |
//+----------------------------------------------------------------------+
bool CGStdGraphObj::ChangeCoordsExtendedObj(const int x,const int y,const int modifier,bool redraw=false)
  {
//--- Set new coordinates for the pivot point specified in 'modifier'
   if(!this.SetTimePrice(x,y,modifier))
      return false;
//--- If the object is not a composite graphical object
//--- or if subordinate graphical objects are not attached to the object,
//--- there is nothing else to do here, return 'true'
   if(this.ExtToolkit==NULL || this.m_list.Total()==0)
      return true;
//--- Get the graphical object bound to the 'modifier' point
   CGStdGraphObj *dep=this.GetDependentObj(modifier);
   if(dep==NULL)
      return false;
//--- Get the object of pivot point data of the bound graphical object
   CLinkedPivotPoint *pp=dep.GetLinkedPivotPoint();
   if(pp==NULL)
      return false;
//--- get the number of coordinate points the object is attached to
   int num=pp.GetNumLinkedCoords();
//--- In the loop by the object coordinate points,
   for(int j=0;j<num;j++)
     {
      //--- get the number of coordinate points of the base object for setting the X coordinate
      int numx=pp.GetBasePivotsNumX(j);
      //--- In the loop by each coordinate point for setting the X coordinate,
      for(int nx=0;nx<numx;nx++)
        {
         //--- get the property for setting the X coordinate, its modifier
         //--- and set it to the subordinate graphical object attached to the 'modifier' point
         int prop_from=pp.GetPropertyX(j,nx);
         int modifier_from=pp.GetPropertyModifierX(j,nx);
         this.SetCoordXToDependentObj(dep,prop_from,modifier_from,nx);
        }
      //--- Get the number of coordinate points of the base object for setting the Y coordinate
      int numy=pp.GetBasePivotsNumY(j);
      //--- In the loop by each coordinate point for setting the Y coordinate,
      for(int ny=0;ny<numy;ny++)
        {
         //--- get the property for setting the Y coordinate, its modifier
         //--- and set it to the subordinate graphical object attached to the 'modifier' point
         int prop_from=pp.GetPropertyY(j,ny);
         int modifier_from=pp.GetPropertyModifierY(j,ny);
         this.SetCoordYToDependentObj(dep,prop_from,modifier_from,ny);
        }
     }
//--- Save the current properties of a subordinate graphical object as the previous ones
   dep.PropertiesCopyToPrevData();
//--- Move a reference control point to new coordinates
   this.ExtToolkit.SetBaseObjTimePrice(this.Time(modifier),this.Price(modifier),modifier);
   this.ExtToolkit.SetBaseObjCoordXY(this.XDistance(),this.YDistance());
//--- If the flag is active, redraw the chart
   if(redraw)
      ::ChartRedraw(m_chart_id);
//--- All is successful - return 'true'
   return true;
  }
//+------------------------------------------------------------------+

これで、メソッドははるかに簡潔になります。

//+----------------------------------------------------------------------+
//| Change X and Y coordinates of the current and all dependent objects  |
//+----------------------------------------------------------------------+
bool CGStdGraphObj::ChangeCoordsExtendedObj(const int x,const int y,const int modifier,bool redraw=false)
  {
//--- Set new coordinates for the pivot point specified in 'modifier'
   if(!this.SetTimePrice(x,y,modifier))
      return false;
//--- If the object is a composite graphical object,
//--- and subordinate graphical objects are attached to the object
   if(this.ExtToolkit!=NULL && this.m_list.Total()>0)
     {
      //--- Get the graphical object bound to the 'modifier' point
      CGStdGraphObj *dep=this.GetDependentObj(modifier);
      if(dep==NULL)
         return false;
      //--- Set X and Y coordinates to all pivot points of a subordinate object and
      //--- save the current properties of a subordinate graphical object as the previous ones
      if(this.SetCoordsXYtoDependentObj(dep))
         dep.PropertiesCopyToPrevData();
     }
//--- Move a reference control point to new coordinates
   this.ExtToolkit.SetBaseObjTimePrice(this.Time(modifier),this.Price(modifier),modifier);
   this.ExtToolkit.SetBaseObjCoordXY(this.XDistance(),this.YDistance());
//--- If the flag is active, redraw the chart
   if(redraw)
      ::ChartRedraw(m_chart_id);
//--- All is successful - return 'true'
   return true;
  }
//+------------------------------------------------------------------+


X座標とY座標を指定した従属オブジェクトの適切なピボットポイントに設定するメソッドを実装します。

//+------------------------------------------------------------------+
//| Set X and Y coordinates to the associated pivot point            |
//| of a specified subordinate object by index                       |
//+------------------------------------------------------------------+
void CGStdGraphObj::SetCoordsXYtoDependentObj(CGStdGraphObj *dependent_obj,CLinkedPivotPoint *pivot_point,const int index)
  {
//--- get the number of coordinate points of the base object for setting the X coordinate
   int numx=pivot_point.GetBasePivotsNumX(index);
//--- In the loop by each coordinate point for setting the X coordinate,
   for(int nx=0;nx<numx;nx++)
     {
      //--- get the property for setting the X coordinate, its modifier
      //--- and set it to the subordinate graphical object attached to the 'index' point
      int prop_from=pivot_point.GetPropertyX(index,nx);
      int modifier_from=pivot_point.GetPropertyModifierX(index,nx);
      this.SetCoordXToDependentObj(dependent_obj,prop_from,modifier_from,nx);
     }
//--- Get the number of coordinate points of the base object for setting the Y coordinate
   int numy=pivot_point.GetBasePivotsNumY(index);
   //--- In the loop by each coordinate point for setting the Y coordinate,
   for(int ny=0;ny<numy;ny++)
     {
      //--- get the property for setting the Y coordinate, its modifier
      //--- and set it to the subordinate graphical object attached to the 'index' point
      int prop_from=pivot_point.GetPropertyY(index,ny);
      int modifier_from=pivot_point.GetPropertyModifierY(index,ny);
      this.SetCoordYToDependentObj(dependent_obj,prop_from,modifier_from,ny);
     }
  }
//+------------------------------------------------------------------+

実際、これらはまさにクラスメソッドから削除されたコードブロックです。コードロジックは以前の記事で検討しており、コードコメントにも説明されているので、説明は必要ないと思います。

指定された従属オブジェクトの関連するピボットポイントにX座標とY座標を設定するメソッドを実装します

//+------------------------------------------------------------------+
//| Set X and Y coordinates to associated pivot points               |
//| of the specified subordinate object                              |
//+------------------------------------------------------------------+
bool CGStdGraphObj::SetCoordsXYtoDependentObj(CGStdGraphObj *dependent_obj)
  {
//--- Get the object of pivot point data of the bound graphical object
   CLinkedPivotPoint *pp=dependent_obj.GetLinkedPivotPoint();
   if(pp==NULL)
      return false;
//--- get the number of coordinate points the object is attached to
   int num=pp.GetNumLinkedCoords();
//--- In the loop by the object coordinate points,
//--- set X and Y to all pivot points of a subordinate object
   for(int j=0;j<num;j++)
      this.SetCoordsXYtoDependentObj(dependent_obj,pp,j);
   return true;
  }
//+------------------------------------------------------------------+

このメソッドでは、従属オブジェクトのすべてのピボットポイントに座標を設定できます。他のグラフィックオブジェクトが複合グラフィックオブジェクトに追加された場合、メソッドはそれらに指定された座標を設定します。


\MQL5\Include\DoEasy\Collections\GraphElementsCollection.mqhでグラフィカル要素のコレクションクラスを改善しましょう。

ChartTimePriceToXY()標準関数はXとYの2つの座標を同時に返すため、それらを格納するためにprivateセクションに構造体を作成します。この構造体では座標に加えて、中心点を基準にした座標シフトも格納します。グラフィックオブジェクトには複数のピボットポイントがある場合があるため、グラフィックオブジェクトの各ピボットポイントの座標を保存するために、作成された構造体型で配列を宣言します。この場合、各配列セルは、「時間/価格」座標から変換されたXおよびY画面座標と、グラフィックオブジェクトの中心点に対するピボットポイント座標のシフトを特徴とします。

クラスの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:
   //--- Pivot point data structure
   struct SDataPivotPoint
     {
      public:
         int         X;                         // Pivot point X coordinate
         int         Y;                         // Pivot point Y coordinate
         int         ShiftX;                    // Pivot point X coordinate shift from the central one
         int         ShiftY;                    // Pivot point Y coordinate shift from the central one
     };
   SDataPivotPoint   m_data_pivot_point[];      // Pivot point data structure array
   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
   CMouseState       m_mouse;                   // "Mouse status" class object
   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

クラスのprivateセクションで、グラフィックオブジェクトの各ピボットポイントの画面座標を返すメソッドを宣言します

private:
//--- 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);
//--- Set the flags of scrolling the chart with the mouse, context menu and crosshairs tool for the specified chart
   void              SetChartTools(const long chart_id,const bool flag);
//--- Return the screen coordinates of each pivot point of the graphical object
   bool              GetPivotPointCoordsAll(CGStdGraphObj *obj,SDataPivotPoint &array_pivots[]);
public:

クラス本体の外にメソッドを実装します。

//+------------------------------------------------------------------+
//| Return screen coordinates                                        |
//| of each graphical object pivot point                             |
//+------------------------------------------------------------------+
bool CGraphElementsCollection::GetPivotPointCoordsAll(CGStdGraphObj *obj,SDataPivotPoint &array_pivots[])
  {
//--- If failed to increase the array of structures to match the number of pivot points,
//--- inform of that in the journal and return 'false'
   if(::ArrayResize(array_pivots,obj.Pivots())!=obj.Pivots())
     {
      CMessage::ToLog(DFUN,MSG_LIB_SYS_FAILED_ARRAY_RESIZE);
      return false;
     }
//--- In the loop by the number of graphical object pivot points
   for(int i=0;i<obj.Pivots();i++)
     {
      //--- Convert the object coordinates into screen ones. If failed, inform of that and return 'false'
      if(!::ChartTimePriceToXY(obj.ChartID(),obj.SubWindow(),obj.Time(i),obj.Price(i),array_pivots[i].X,array_pivots[i].Y))
       {
        CMessage::ToLog(DFUN,MSG_LIB_SYS_FAILED_CONV_GRAPH_OBJ_COORDS_TO_XY);
        return false;
       }
     }
//--- Depending on the graphical object type
   switch(obj.TypeGraphObject())
     {
      //--- One pivot point in screen coordinates
      case OBJ_LABEL             :
      case OBJ_BUTTON            :
      case OBJ_BITMAP_LABEL      :
      case OBJ_EDIT              :
      case OBJ_RECTANGLE_LABEL   :
      case OBJ_CHART             : break;
      
      //--- One pivot point (price only)
      case OBJ_HLINE             : break;
      
      //--- One pivot point (time only)
      case OBJ_VLINE             : break;
      case OBJ_EVENT             : break;
      
      //--- Two pivot points and a central one
      //--- Lines
      case OBJ_TREND             :
      case OBJ_TRENDBYANGLE      :
      case OBJ_CYCLES            :
      case OBJ_ARROWED_LINE      :
      //--- Channels
      case OBJ_CHANNEL           :
      case OBJ_STDDEVCHANNEL     :
      case OBJ_REGRESSION        :
      //--- Gann
      case OBJ_GANNLINE          :
      case OBJ_GANNGRID          :
      //--- Fibo
      case OBJ_FIBO              :
      case OBJ_FIBOTIMES         :
      case OBJ_FIBOFAN           :
      case OBJ_FIBOARC           :
      case OBJ_FIBOCHANNEL       :
      case OBJ_EXPANSION         :
        //--- Calculate the shifts of all pivot points from the central one and write them to the structure array
        array_pivots[0].ShiftX=(array_pivots[1].X-array_pivots[0].X)/2; 
        array_pivots[0].ShiftY=(array_pivots[1].Y-array_pivots[0].Y)/2;
        array_pivots[1].ShiftX=(array_pivots[0].X-array_pivots[1].X)/2;
        array_pivots[1].ShiftY=(array_pivots[0].Y-array_pivots[1].Y)/2;
        return true;

      //--- Channels
      case OBJ_PITCHFORK         : break;
      //--- Gann
      case OBJ_GANNFAN           : break;
      //--- Elliott
      case OBJ_ELLIOTWAVE5       : break;
      case OBJ_ELLIOTWAVE3       : break;
      //--- Shapes
      case OBJ_RECTANGLE         : break;
      case OBJ_TRIANGLE          : break;
      case OBJ_ELLIPSE           : break;
      //--- Arrows
      case OBJ_ARROW_THUMB_UP    : break;
      case OBJ_ARROW_THUMB_DOWN  : break;
      case OBJ_ARROW_UP          : break;
      case OBJ_ARROW_DOWN        : break;
      case OBJ_ARROW_STOP        : break;
      case OBJ_ARROW_CHECK       : break;
      case OBJ_ARROW_LEFT_PRICE  : break;
      case OBJ_ARROW_RIGHT_PRICE : break;
      case OBJ_ARROW_BUY         : break;
      case OBJ_ARROW_SELL        : break;
      case OBJ_ARROW             : break;
      //--- Graphical objects with time/price coordinates
      case OBJ_TEXT              : break;
      case OBJ_BITMAP            : break;
      //---
      default: break;
     }
   return false;
  }
//+------------------------------------------------------------------+

今のところ、2つのピボットポイントと1つの中央のピボットポイントを持つグラフィックオブジェクトの画面座標のみが構造体に設定されています。

メソッドは、ピボットポイント座標を構造体配列に設定する必要があるグラフィックオブジェクトへのポインタを受け取ります。このオブジェクトは、リンクによってメソッドに渡されます。座標変換が成功した場合、メソッドは、グラフィックオブジェクトの各ピボットポイントの画面座標を含む完全に塗りつぶされた構造体配列とともにtrueを返します。失敗した場合、メソッドはfalseを返します。

クラスイベントハンドラでは、オブジェクト管理フォームシフトを処理して、これが中心点である場合にオブジェクト全体が移動されるようにする必要があります。これを実現するには、(オブジェクトをドラッグするために使用される)中央のエッジフォームに対するエッジフォームのシフトを計算し、計算されて構造体に設定されたシフト値によって両方のピボットポイントを再配置する必要があるため、そのすべてのピボットポイントは、マウスによってドラッグされた中央のピボットポイントと同じ値だけシフトします。

中央のコントロールポイント(フォーム)移動イベントのそのような処理を追加しましょう

//+------------------------------------------------------------------+
//| Event handler                                                    |
//+------------------------------------------------------------------+
void CGraphElementsCollection::OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
  {
   CGStdGraphObj *obj_std=NULL;  // Pointer to the standard graphical object
   CGCnvElement  *obj_cnv=NULL;  // Pointer to the graphical element object on canvas
   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_std=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_std==NULL)
        {
         //--- Let's search the list for the object that is not on the chart
         obj_std=this.FindMissingObj(chart_id);
         //--- If failed to find the object here as well, exit
         if(obj_std==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_std.SetNamePrev(obj_std.Name()) && obj_std.SetName(name_new))
            ::EventChartCustom(this.m_chart_id_main,GRAPH_OBJ_EVENT_RENAME,obj_std.ChartID(),obj_std.TimeCreate(),obj_std.Name());
        }
      //--- Update the properties of the obtained object
      //--- and check their change
      obj_std.PropertiesRefresh();
      obj_std.PropertiesCheckChanged();
     }

//--- Handle standard graphical object events in the collection list
   for(int i=0;i<this.m_list_all_graph_obj.Total();i++)
     {
      //--- Get the next graphical object and
      obj_std=this.m_list_all_graph_obj.At(i);
      if(obj_std==NULL)
         continue;
      //--- call its event handler
      obj_std.OnChartEvent((id<CHARTEVENT_CUSTOM ? id : idx),lparam,dparam,sparam);
     }

//--- Handle chart changes for extended standard objects
   if(id==CHARTEVENT_CHART_CHANGE || idx==CHARTEVENT_CHART_CHANGE)
     {
      CArrayObj *list=this.GetListStdGraphObjectExt();
      if(list!=NULL)
        {
         for(int i=0;i<list.Total();i++)
           {
            obj_std=list.At(i);
            if(obj_std==NULL)
               continue;
            obj_std.OnChartEvent(CHARTEVENT_CHART_CHANGE,lparam,dparam,sparam);
           }
        }
     }
//--- Handling mouse events of graphical objects on canvas
//--- If the event is not a chart change
   else
     {
      //--- Check whether the mouse button is pressed
      bool pressed=(this.m_mouse.ButtonKeyState(id,lparam,dparam,sparam)==MOUSE_BUTT_KEY_STATE_LEFT ? true : false);
      ENUM_MOUSE_FORM_STATE mouse_state=MOUSE_FORM_STATE_NONE;
      //--- Declare static variables for the active form and status flags
      static CForm *form=NULL;
      static bool pressed_chart=false;
      static bool pressed_form=false;
      static bool move=false;
      //--- Declare static variables for the index of the form for managing an extended standard graphical object and its ID
      static int  form_index=WRONG_VALUE;
      static long graph_obj_id=WRONG_VALUE;
      
      //--- If the button is not pressed on the chart and the movement flag is not set, get the form, above which the cursor is located
      if(!pressed_chart && !move)
         form=this.GetFormUnderCursor(id,lparam,dparam,sparam,mouse_state,graph_obj_id,form_index);
      
      //--- If the button is not pressed, reset all flags and enable the chart tools 
      if(!pressed)
        {
         pressed_chart=false;
         pressed_form=false;
         move=false;
         this.SetChartTools(::ChartID(),true);
        }
      
      //--- If this is a mouse movement event and the movement flag is active, move the form, above which the cursor is located (if the pointer to it is valid)
      if(id==CHARTEVENT_MOUSE_MOVE && move)
        {
         if(form!=NULL)
           {
            //--- calculate the cursor movement relative to the form coordinate origin
            int x=this.m_mouse.CoordX()-form.OffsetX();
            int y=this.m_mouse.CoordY()-form.OffsetY();
            //--- get the width and height of the chart the form is located at
            int chart_width=(int)::ChartGetInteger(form.ChartID(),CHART_WIDTH_IN_PIXELS,form.SubWindow());
            int chart_height=(int)::ChartGetInteger(form.ChartID(),CHART_HEIGHT_IN_PIXELS,form.SubWindow());
            //--- If the form is not within an extended standard graphical object
            if(form_index==WRONG_VALUE)
              {
               //--- Adjust the calculated form coordinates if the form is out of the chart range
               if(x<0) x=0;
               if(x>chart_width-form.Width()) x=chart_width-form.Width();
               if(y<0) y=0;
               if(y>chart_height-form.Height()) y=chart_height-form.Height();
               //--- If the one-click trading panel is not present on the chart,
               if(!::ChartGetInteger(form.ChartID(),CHART_SHOW_ONE_CLICK))
                 {
                  //--- calculate the form coordinates so that the form does not overlap with the one-click trading panel button
                  if(y<17 && x<41)
                     y=17;
                 }
               //--- If the one-click trading panel is on the chart,
               else
                 {
                  //--- calculate the form coordinates so that the form does not overlap with the one-click trading panel
                  if(y<80 && x<192)
                     y=80;
                 }
              }
            //--- If the form is included into the extended standard graphical object
            else
              {
               if(graph_obj_id>WRONG_VALUE)
                 {
                  //--- Get the list of objects by object ID (there should be one object)
                  CArrayObj *list_ext=CSelect::ByGraphicStdObjectProperty(GetListStdGraphObjectExt(),GRAPH_OBJ_PROP_ID,0,graph_obj_id,EQUAL);
                  //--- If managed to obtain the list and it is not empty,
                  if(list_ext!=NULL && list_ext.Total()>0)
                    {
                     //--- get the graphical object from the list
                     CGStdGraphObj *ext=list_ext.At(0);
                     //--- If the pointer to the object has been received,
                     if(ext!=NULL)
                       {
                        //--- get the object type
                        ENUM_OBJECT type=ext.GraphObjectType();
                        //--- If the object is built using screen coordinates, set the coordinates to the object
                        if(type==OBJ_LABEL || type==OBJ_BUTTON || type==OBJ_BITMAP_LABEL || type==OBJ_EDIT || type==OBJ_RECTANGLE_LABEL)
                          {
                           ext.SetXDistance(x);
                           ext.SetYDistance(y);
                          }
                        //--- otherwise, if the object is built based on time/price coordinates,
                        else
                          {
                           //--- calculate the coordinate shift
                           int shift=(int)::ceil(form.Width()/2)+1;
                           //--- If the form is located on one of the graphical object pivot points,
                           if(form_index<ext.Pivots())
                             {
                              //--- limit the form coordinates so that they do not move beyond the chart borders
                              if(x+shift<0)
                                 x=-shift;
                              if(x+shift>chart_width)
                                 x=chart_width-shift;
                              if(y+shift<0)
                                 y=-shift;
                              if(y+shift>chart_height)
                                 y=chart_height-shift;
                              //--- set the calculated coordinates to the object
                              ext.ChangeCoordsExtendedObj(x+shift,y+shift,form_index);
                             }
                           //--- If the form is central for managing all pivot points of a graphical object
                           else
                             {
                              //--- Get screen coordinates of all object pivot points and write them to the m_data_pivot_point structure
                              if(this.GetPivotPointCoordsAll(ext,m_data_pivot_point))
                                {
                                 //--- In the loop by the number of object pivot points,
                                 for(int i=0;i<(int)this.m_data_pivot_point.Size();i++)
                                   {
                                    //--- limit the screen coordinates of the current pivot point so that they do not move beyond the chart borders
                                    if(x+shift-this.m_data_pivot_point[i].ShiftX<0)
                                       x=-shift+m_data_pivot_point[i].ShiftX;
                                    if(x+shift+this.m_data_pivot_point[i].ShiftX>chart_width)
                                       x=chart_width-shift-this.m_data_pivot_point[i].ShiftX;
                                    if(y+shift+this.m_data_pivot_point[i].ShiftY<0)
                                       y=-shift-this.m_data_pivot_point[i].ShiftY;
                                    if(y+shift-this.m_data_pivot_point[i].ShiftY>chart_height)
                                       y=chart_height-shift+this.m_data_pivot_point[i].ShiftY;
                                    //--- set the calculated coordinates to the current object pivot point
                                    ext.ChangeCoordsExtendedObj(x+shift-this.m_data_pivot_point[i].ShiftX,y+shift-this.m_data_pivot_point[i].ShiftY,i);
                                   }
                                }
                             }
                          }
                       }
                    }
                 }
              }
            //--- Move the form by the obtained coordinates
            form.Move(x,y,true);
           }
        }
   
      //--- Display debugging comments on the chart
      Comment
        (
         (form!=NULL ? form.Name()+":" : ""),"\n",
         EnumToString((ENUM_CHART_EVENT)id),"\n",
         EnumToString(this.m_mouse.ButtonKeyState(id,lparam,dparam,sparam)),
         "\n",EnumToString(mouse_state),
         "\npressed=",pressed,", move=",move,(form!=NULL ? ", Interaction="+(string)form.Interaction() : ""),
         "\npressed_chart=",pressed_chart,", pressed_form=",pressed_form,
         "\nform_index=",form_index,", graph_obj_id=",graph_obj_id
        );
      
      //--- If the cursor is not above the form
      if(form==NULL)
        {
         //--- If the mouse button is pressed
         if(pressed)
           {
            //--- If the button is still pressed and held on the form, exit
            if(pressed_form)
              {
               return;
              }
            //--- If the button hold flag is not enabled yet, set the flags and enable chart tools
            if(!pressed_chart)
              {
               pressed_chart=true;  // Button is held on the chart
               pressed_form=false;  // Cursor is not above the form
               move=false;          // movement disabled
               this.SetChartTools(::ChartID(),true);
              }
           }
         //--- If the mouse button is not pressed
         else
           {
            //--- Get the list of extended standard graphical objects
            CArrayObj *list_ext=GetListStdGraphObjectExt();
            //--- In the loop by all extended graphical objects,
            int total=list_ext.Total();
            for(int i=0;i<total;i++)
              {
               //--- get the next graphical object
               CGStdGraphObj *obj=list_ext.At(i);
               if(obj==NULL)
                  continue;
               //--- and redraw it without a point and a circle
               obj.RedrawControlPointForms(0,CTRL_POINT_COLOR);
              }
           }
        }
      //--- If the cursor is above the form
      else
        {
         //--- If the button is still pressed and held on the chart, exit
         if(pressed_chart)
           {
            return;
           }
         
         //--- If the flag of holding the button on the form is not set yet
         if(!pressed_form)
           {
            pressed_chart=false;    // The button is not pressed on the chart
            this.SetChartTools(::ChartID(),false);
            
            //--- 'The cursor is inside the form, no mouse buttons are clicked' event handler
            if(mouse_state==MOUSE_FORM_STATE_INSIDE_FORM_NOT_PRESSED)
              {
               //--- If the cursor is above the form for managing the pivot point of an extended graphical object,
               if(graph_obj_id>WRONG_VALUE)
                 {
                  //--- get the object by its ID and by the chart ID
                  CGStdGraphObj *graph_obj=this.GetStdGraphObjectExt(graph_obj_id,form.ChartID());
                  if(graph_obj!=NULL)
                    {
                     //--- Get the toolkit of an extended standard graphical object
                     CGStdGraphObjExtToolkit *toolkit=graph_obj.GetExtToolkit();
                     if(toolkit!=NULL)
                       {
                        //--- Draw a point with a circle on the form and delete it on all other forms
                        toolkit.DrawOneControlPoint(form);
                       }
                    }
                 }
              }
            //--- 'The cursor is inside the form, a mouse button is clicked (any)' event handler
            if(mouse_state==MOUSE_FORM_STATE_INSIDE_FORM_PRESSED)
              {
               this.SetChartTools(::ChartID(),false);
               //--- If the flag of holding the form is not set yet
               if(!pressed_form)
                 {
                  pressed_form=true;      // set the flag of pressing on the form
                  pressed_chart=false;    // disable the flag of pressing on the form
                 }
              }
            //--- 'The cursor is inside the form, the mouse wheel is being scrolled' event handler workpiece
            if(mouse_state==MOUSE_FORM_STATE_INSIDE_FORM_WHEEL)
              {
               
              }
            
            
            //--- 'The cursor is inside the active area, the mouse buttons are not clicked' event handler
            if(mouse_state==MOUSE_FORM_STATE_INSIDE_ACTIVE_AREA_NOT_PRESSED)
              {
               //--- Set the cursor shift relative to the form initial coordinates
               form.SetOffsetX(this.m_mouse.CoordX()-form.CoordX());
               form.SetOffsetY(this.m_mouse.CoordY()-form.CoordY());
               //--- If the cursor is above the active area of the form for managing the pivot point of an extended graphical object,
               if(graph_obj_id>WRONG_VALUE)
                 {
                  //--- get the object by its ID and by the chart ID
                  CGStdGraphObj *graph_obj=this.GetStdGraphObjectExt(graph_obj_id,form.ChartID());
                  if(graph_obj!=NULL)
                    {
                     //--- Get the toolkit of an extended standard graphical object
                     CGStdGraphObjExtToolkit *toolkit=graph_obj.GetExtToolkit();
                     if(toolkit!=NULL)
                       {
                        //--- Draw a point with a circle on the form and delete it on all other forms
                        toolkit.DrawOneControlPoint(form);
                       }
                    }
                 }
              }
            //--- 'The cursor is inside the active area,  any mouse button is clicked' event handler
            if(mouse_state==MOUSE_FORM_STATE_INSIDE_ACTIVE_AREA_PRESSED && !move)
              {
               pressed_form=true;   // the flag of holding the mouse button on the form
               //--- If the left mouse button is pressed
               if(this.m_mouse.IsPressedButtonLeft())
                 {
                  //--- Set flags and form parameters
                  move=true;                                            // movement flag
                  form.SetInteraction(true);                            // flag of the form interaction with the environment
                  form.BringToTop();                                    // form on the background - above all others
                  this.ResetAllInteractionExeptOne(form);               // Reset interaction flags for all forms except the current one
                  form.SetOffsetX(this.m_mouse.CoordX()-form.CoordX()); // Cursor shift relative to the X coordinate
                  form.SetOffsetY(this.m_mouse.CoordY()-form.CoordY()); // Cursor shift relative to the Y coordinate
                 }
              }
            //--- 'The cursor is inside the active area, the mouse wheel is being scrolled' event handler workpiece
            if(mouse_state==MOUSE_FORM_STATE_INSIDE_ACTIVE_AREA_WHEEL)
              {
               
              }
            
            
            //--- 'The cursor is inside the window scrolling area, no mouse buttons are clicked' event handler workpiece
            if(mouse_state==MOUSE_FORM_STATE_INSIDE_SCROLL_AREA_NOT_PRESSED)
              {
               
              }
            //--- 'The cursor is inside the window scrolling area, a mouse button is clicked (any)' event handler workpiece
            if(mouse_state==MOUSE_FORM_STATE_INSIDE_SCROLL_AREA_PRESSED)
              {
               
              }
            //--- 'The cursor is inside the window scrolling area, the mouse wheel is being scrolled' event handler workpiece
            if(mouse_state==MOUSE_FORM_STATE_INSIDE_SCROLL_AREA_WHEEL)
              {
               
              }
           }
        }
     }
  }
//+------------------------------------------------------------------+

中央管理フォームを移動する新しいハンドラとは別に、カーソルの下にあるオブジェクトフォームにポイントを描画するメソッドを呼び出し、グラフィックオブジェクトの他のフォームでこれらのポイントを削除するようにしました。これにより、上記のように、複数のフォームオブジェクトが互いの近くに合って重なり合っている場合に、それらのオブジェクトに同時に点が描画されるのを防ぐことができます。

これで、新しい概念をテストする準備が整いました。


検証

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

EAを変更する必要はありません。現時点では、すべての変更はライブラリクラスでのみおこなわれます。

EAをコンパイルし、チャート上で起動します。


ご覧のとおり、作成されたフォームで複合グラフィックオブジェクトを移動すると、チャートの制限を超えるピボットポイントに関連するすべての制限が正しく機能します。ただし、ピボットポイントの位置を初期位置に対して「逆」にすると、ピボットポイントがチャートの制限を超えた場合にオブジェクトの「構成」が歪んでしまいます。これは、ピボットポイントがチャートの右、左、上、または下の端を超えることについての制限と依存関係の計算が正しくないことを意味します。
ピボットポイントのシフトは中央点を基準にして計算されるため、これは驚くべきことではありません。つまり、1つめのポイントには正のシフトがあり、2番目のポイントには負のシフトがあります。中央のピボットポイントを基準にしてピボットポイントの位置を変更すると、制限の計算にエラーが発生します。これは次の記事で修正します。


次の段階

次の記事では、複合グラフィックオブジェクトの作業を続けます。

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

目次に戻る

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

DoEasyライブラリのグラフィックス(第93部):複合グラフィックオブジェクトを作成するための機能の準備
DoEasyライブラリのグラフィックス(第94部):複合グラフィックオブジェクトの移動と削除
DoEasyライブラリのグラフィックス(第95部):複合グラフィックオブジェクトコントロール
DoEasyライブラリのグラフィックス(第96部):フォームオブジェクトのグラフィックとマウスイベントの処理
DoEasyライブラリのグラフィックス(第97部):フォームオブジェクトの移動の独立した処理
DoEasyライブラリのグラフィックス(第98部):拡張された標準グラフィックオブジェクトのピボットポイントの移動

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

添付されたファイル |
MQL5.zip (4372.39 KB)
RSIによる取引システムの設計方法を学ぶ RSIによる取引システムの設計方法を学ぶ
今回は、取引の世界で最も人気があり、一般的に使用されている指標の1つであるRSIを紹介します。この指標を使用した取引システムの設計方法を学びます。
DoEasyライブラリのグラフィックス(第98部):拡張された標準グラフィカルオブジェクトのピボットポイントの移動 DoEasyライブラリのグラフィックス(第98部):拡張された標準グラフィカルオブジェクトのピボットポイントの移動
本稿では、拡張された標準グラフィカルオブジェクトの開発を継続し、グラフィカルオブジェクトのピボットポイントの座標を管理するためのコントロールポイントを使用して、複合グラフィカルオブジェクトのピボットポイントを移動する機能を作成します。
単一チャート上の複数インジケータ(第06部):MetaTrader 5をRADシステムに変える(II) 単一チャート上の複数インジケータ(第06部):MetaTrader 5をRADシステムに変える(II)
前回の記事では、MetaTrader 5のオブジェクトを使ってChart Tradeを作成し、プラットフォームをRADシステムに変える方法を紹介しました。このシステムは非常によく機能しており、読者の多くは、提案されたシステムの機能を拡張できるようなライブラリを作成することをお考えになったのではないでしょうか。これに基づいて、より直感的で使い勝手の良いEAを開発することも可能でしょう。
モメンタムによるトレーディングシステムの設計方法を学ぶ モメンタムによるトレーディングシステムの設計方法を学ぶ
前回は、価格の方向性であるトレンドを見極めることの重要性について述べました。この記事では、最も重要な概念と指標の1つであるモメンタム指標を紹介します。このモメンタム指標に基づいたトレーディングシステムの設計方法を紹介します。