English Русский 中文 Español Deutsch Português
preview
DoEasy-コントロール(第24部):ヒント補助WinFormsオブジェクト

DoEasy-コントロール(第24部):ヒント補助WinFormsオブジェクト

MetaTrader 5 | 13 1月 2023, 10:31
225 0
Artyom Trishkin
Artyom Trishkin

内容


概念

ライブラリでは、多くのグラフィック要素が何らかの形で互いに関連しています。各コントロールには、その基本オブジェクトと、関連するオブジェクトの階層全体のメインの親オブジェクトへのポインタがあります。現在の実装では新規に作成されたグラフィック要素に対してこれらのオブジェクトを指定しており、その親オブジェクトを「失う」原因となる欠陥があります。オブジェクトを作成した後にこれらを指定してはいますが、これが必ずしも結果に結びつくとは限りません。エラーのある場所を見つけるのは、かなり複雑です。そこで、リレーションシップの階層で親オブジェクトを指定するロジックを変更することにしました。すべてのグラフィック要素のコンストラクタに、親オブジェクトへのポインタを追加するつもりです。したがって、グラフィカルオブジェクトを作成する瞬間にこれらを直接指定することになり、次のバインドコントロールを作成した後に毎回親オブジェクトを指定する手間を省くことができます。独立した(どの要素にも接続されないオブジェクトとして作成された)コントロールについては、親オブジェクトへのポインタとしてNULL値を渡すことにします。

このような手直しにより、すべてのオブジェクトが親を「認識」しているわけではないというバグを探す手間が省け、将来的に新しいライブラリコントロールの作成とデバッグが容易になります。

グラフィック要素オブジェクトのコンストラクタを仕上げるのに加えて、いくつかの新しいWinFormsオブジェクトを作成します。Windowsアプリケーションでコントロールを操作していると、マウスカーソルがオブジェクトの領域に入ったときに見た目が変わることに気がつくかもしれません。この変化は、この領域との相互作用によって、例えば、オブジェクトの外観が変化する可能性があることを示します。また、この領域でドラッグ&ドロップ機能を使うなど、何らかのアクションを実行できることを示す場合もあります。

残念ながら、MQL5言語ではマウスカーソルの外観を変更することはできませんが、どうにかしてライブラリの利用者に、コントロールとの相互作用の可能性を知らせる必要があります。私の解決策は、マウスカーソルによる操作が可能な場所では、補助的なグラフィックオブジェクトを表示することです。これらのオブジェクトの外観は、コントロールのこの領域で実行可能なアクションを示します。

このような補助的な要素をヒントオブジェクトと呼ぶことにします。すべてのヒントオブジェクトについて、基本的な機能を持つ基本オブジェクトを作成することにします。それは、それぞれの目的に応じて、具体的なオブジェクトを作るために使われます。

今回は、基本となるヒントオブジェクトとその派生物、つまり区切りを上下左右に移動させる可能性を示すオブジェクトを作成することにします。マウスカーソルがSplitContainerコントロールの区切り領域上にあると、カーソルの横にそのようなオブジェクトが表示され、区切りが左右や上下に移動できることを示すようになります。

将来的には、今日以降に作成されたヒントオブジェクトは、新しいコントロールの一部として使用されることになるでしょう。また、このようなオブジェクトを使って、すでに作成されたコントロールに追加される新しい機能を示すこともできます。例は、グラフィックオブジェクトのサイズを変更する可能性です。


ライブラリクラスの改善

ヒントオブジェクトに色と大きさを設定する必要があります。\MQL5\Include\DoEasy\Defines.mqhで定義しましょう。

#define CLR_DEF_CONTROL_SPLIT_CONTAINER_BACK_COLOR    (C'0xF0,0xF0,0xF0')  // SplitContainer control background color
#define CLR_DEF_CONTROL_SPLIT_CONTAINER_MOUSE_DOWN    (C'0xF0,0xF0,0xF0')  // Color of SplitContainer control background when clicking on the control
#define CLR_DEF_CONTROL_SPLIT_CONTAINER_MOUSE_OVER    (C'0xF0,0xF0,0xF0')  // Color of SplitContainer control background when hovering the mouse over the control
#define CLR_DEF_CONTROL_SPLIT_CONTAINER_BORDER_COLOR  (C'0x65,0x65,0x65')  // SplitContainer control frame color

#define CLR_DEF_CONTROL_HINT_BACK_COLOR               (C'0xFF,0xFF,0xE1')  // Hint control background color
#define CLR_DEF_CONTROL_HINT_BORDER_COLOR             (C'0x76,0x76,0x76')  // Hint control frame color
#define CLR_DEF_CONTROL_HINT_FORE_COLOR               (C'0x5A,0x5A,0x5A')  // Hint control text color

#define DEF_CONTROL_LIST_MARGIN_X      (1)                        // Gap between columns in ListBox controls
#define DEF_CONTROL_LIST_MARGIN_Y      (0)                        // Gap between rows in ListBox controls

#define DEF_FONT                       ("Calibri")                // Default font
#define DEF_FONT_SIZE                  (8)                        // Default font size
#define DEF_CHECK_SIZE                 (12)                       // Verification flag default size
#define DEF_ARROW_BUTTON_SIZE          (15)                       // Default arrow button size
#define OUTER_AREA_SIZE                (16)                       // Size of one side of the outer area around the form workspace
#define DEF_FRAME_WIDTH_SIZE           (3)                        // Default form/panel/window frame width
#define DEF_HINT_ICON_SIZE             (11)                       // Hint object side size
//--- Graphical object parameters
#define PROGRAM_OBJ_MAX_ID             (10000)                    // Maximum value of an ID of a graphical object belonging to a program
#define CTRL_POINT_RADIUS              (5)                        // Radius of the control point on the form for managing graphical object pivot points
#define CTRL_POINT_COLOR               (clrDodgerBlue)            // Radius of the control point on the form for managing graphical object pivot points
#define CTRL_FORM_SIZE                 (40)                       // Size of the control point form for managing graphical object pivot points
//+------------------------------------------------------------------+


ここで作成する新しいオブジェクトタイプを、グラフィック要素タイプのリストに追加します。

//+------------------------------------------------------------------+
//| The list of graphical element types                              |
//+------------------------------------------------------------------+
enum ENUM_GRAPH_ELEMENT_TYPE
  {
   GRAPH_ELEMENT_TYPE_STANDARD,                       // Standard graphical object
   GRAPH_ELEMENT_TYPE_STANDARD_EXTENDED,              // Extended standard graphical object

   //---...
   //---...

   GRAPH_ELEMENT_TYPE_WF_CHECKED_LIST_BOX,            // Windows Forms CheckedListBox
   GRAPH_ELEMENT_TYPE_WF_BUTTON_LIST_BOX,             // Windows Forms ButtonListBox
   //--- Auxiliary elements of WinForms objects
   GRAPH_ELEMENT_TYPE_WF_LIST_BOX_ITEM,               // Windows Forms ListBoxItem
   GRAPH_ELEMENT_TYPE_WF_TAB_HEADER,                  // Windows Forms TabHeader
   GRAPH_ELEMENT_TYPE_WF_TAB_FIELD,                   // Windows Forms TabField
   GRAPH_ELEMENT_TYPE_WF_SPLIT_CONTAINER_PANEL,       // Windows Forms SplitContainerPanel
   GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON,                // Windows Forms ArrowButton
   GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_UP,             // Windows Forms UpArrowButton
   GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_DOWN,           // Windows Forms DownArrowButton
   GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_LEFT,           // Windows Forms LeftArrowButton
   GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_RIGHT,          // Windows Forms RightArrowButton
   GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTONS_UD_BOX,        // Windows Forms UpDownArrowButtonsBox
   GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTONS_LR_BOX,        // Windows Forms LeftRightArrowButtonsBox
   GRAPH_ELEMENT_TYPE_WF_SPLITTER,                    // Windows Forms Splitter
   GRAPH_ELEMENT_TYPE_WF_HINT_BASE,                   // Windows Forms HintBase
   GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_LEFT,              // Windows Forms HintMoveLeft
   GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_RIGHT,             // Windows Forms HintMoveRight
   GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_UP,                // Windows Forms HintMoveUp
   GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_DOWN,              // Windows Forms HintMoveDown
  };
//+------------------------------------------------------------------+


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

   MSG_GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTONS_UD_BOX,    // UpDownArrowBox control
   MSG_GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTONS_LR_BOX,    // LeftRightArrowBox control
   MSG_GRAPH_ELEMENT_TYPE_WF_HINT_BASE,               // HintBase control
   MSG_GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_LEFT,          // HintMoveLeft control
   MSG_GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_RIGHT,         // HintMoveLeft control
   MSG_GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_UP,            // HintMoveLeft control
   MSG_GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_DOWN,          // HintMoveLeft control
   MSG_GRAPH_OBJ_BELONG_PROGRAM,                      // Graphical object belongs to a program
   MSG_GRAPH_OBJ_BELONG_NO_PROGRAM,                   // Graphical object does not belong to a program

新しく追加したインデックスに対応するメッセージテキストも追加します。

   {"Элемент управления \"UpDownArrowBox\"","Control element \"UpDownArrowBox\""},
   {"Элемент управления \"LeftRightArrowBox\"","Control element \"LeftRightArrowBox\""},
   {"Элемент управления \"HintBase\"","Control element \"HintBase\""},
   {"Элемент управления \"HintMoveLeft\"","Control element \"HintMoveLeft\""},
   {"Элемент управления \"HintMoveRight\"","Control element \"HintMoveRight\""},
   {"Элемент управления \"HintMoveUp\"","Control element \"HintMoveUp\""},
   {"Элемент управления \"HintMoveDown\"","Control element \"HintMoveDown\""},
   {"Графический объект принадлежит программе","The graphic object belongs to the program"},
   {"Графический объект не принадлежит программе","The graphic object does not belong to the program"},


ここで新たに作成されるオブジェクトの説明を表示することができるようになりました。グラフィカルオブジェクトの説明を表示するためには、\MQL5\Include\DoEasy\Objects\Graph\GBaseObj.mqhのメソッドを使用します。そのメソッドに、要素タイプ別に必要な文字列の戻り値を追加します。

//+------------------------------------------------------------------+
//| Return the description of the graphical element type             |
//+------------------------------------------------------------------+
string CGBaseObj::TypeElementDescription(const ENUM_GRAPH_ELEMENT_TYPE type)
  {
   return
     (
      type==GRAPH_ELEMENT_TYPE_STANDARD                  ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_STANDARD)                 :
      type==GRAPH_ELEMENT_TYPE_STANDARD_EXTENDED         ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_STANDARD_EXTENDED)        :
      type==GRAPH_ELEMENT_TYPE_ELEMENT                   ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_ELEMENT)                  :
      type==GRAPH_ELEMENT_TYPE_SHADOW_OBJ                ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_SHADOW_OBJ)               :
      type==GRAPH_ELEMENT_TYPE_FORM                      ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_FORM)                     :
      type==GRAPH_ELEMENT_TYPE_WINDOW                    ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WINDOW)                   :
      //--- WinForms
      type==GRAPH_ELEMENT_TYPE_WF_UNDERLAY               ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_UNDERLAY)              :
      type==GRAPH_ELEMENT_TYPE_WF_BASE                   ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_BASE)                  :
      //--- Containers
      type==GRAPH_ELEMENT_TYPE_WF_CONTAINER              ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_CONTAINER)             :
      type==GRAPH_ELEMENT_TYPE_WF_GROUPBOX               ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_GROUPBOX)              :
      type==GRAPH_ELEMENT_TYPE_WF_PANEL                  ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_PANEL)                 :
      type==GRAPH_ELEMENT_TYPE_WF_TAB_CONTROL            ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_TAB_CONTROL)           :
      type==GRAPH_ELEMENT_TYPE_WF_SPLIT_CONTAINER        ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_SPLIT_CONTAINER)       :
      //--- Standard controls
      type==GRAPH_ELEMENT_TYPE_WF_COMMON_BASE            ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_COMMON_BASE)           :
      type==GRAPH_ELEMENT_TYPE_WF_LABEL                  ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_LABEL)                 :
      type==GRAPH_ELEMENT_TYPE_WF_CHECKBOX               ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_CHECKBOX)              :
      type==GRAPH_ELEMENT_TYPE_WF_RADIOBUTTON            ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_RADIOBUTTON)           :
      type==GRAPH_ELEMENT_TYPE_WF_BUTTON                 ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_BUTTON)                :
      type==GRAPH_ELEMENT_TYPE_WF_ELEMENTS_LIST_BOX      ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_ELEMENTS_LIST_BOX)     :
      type==GRAPH_ELEMENT_TYPE_WF_LIST_BOX               ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_LIST_BOX)              :
      type==GRAPH_ELEMENT_TYPE_WF_LIST_BOX_ITEM          ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_LIST_BOX_ITEM)         :
      type==GRAPH_ELEMENT_TYPE_WF_CHECKED_LIST_BOX       ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_CHECKED_LIST_BOX)      :
      type==GRAPH_ELEMENT_TYPE_WF_BUTTON_LIST_BOX        ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_BUTTON_LIST_BOX)       :
      //--- Auxiliary control objects
      type==GRAPH_ELEMENT_TYPE_WF_TAB_HEADER             ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_TAB_HEADER)            :
      type==GRAPH_ELEMENT_TYPE_WF_TAB_FIELD              ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_TAB_FIELD)             :
      type==GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON           ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON)          :
      type==GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_UP        ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_UP)       :
      type==GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_DOWN      ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_DOWN)     :
      type==GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_LEFT      ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_LEFT)     :
      type==GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_RIGHT     ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_RIGHT)    :
      type==GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTONS_UD_BOX   ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTONS_UD_BOX)  :
      type==GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTONS_LR_BOX   ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTONS_LR_BOX)  :
      type==GRAPH_ELEMENT_TYPE_WF_SPLIT_CONTAINER_PANEL  ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_SPLIT_CONTAINER_PANEL) :
      type==GRAPH_ELEMENT_TYPE_WF_SPLITTER               ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_SPLITTER)              :
      type==GRAPH_ELEMENT_TYPE_WF_HINT_BASE              ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_HINT_BASE)             :
      type==GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_LEFT         ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_LEFT)        :
      type==GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_RIGHT        ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_RIGHT)       :
      type==GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_UP           ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_UP)          :
      type==GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_DOWN         ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_DOWN)        :
      "Unknown"
     );
  }  
//+------------------------------------------------------------------+


グラフィック要素オブジェクトのクラスで、親オブジェクトを指定し、受け取るという主な変更点を実装します。作成されたグラフィック要素の関係階層の基本オブジェクトとメインオブジェクトへのポインタは、クラスコンストラクタに送られることになっています。

\MQL5\Include\DoEasy\Objects\Graph\GCnvElement.mqhで、親オブジェクトを設定するメソッドを削除して、ポインタを返すメソッドのみを残しますIsBase()メソッドはIsDependent()に名称変更されました

//--- (1) Set and (2) return the initial shift of the (1) X and (2) Y coordinate relative to the base object
   void              SetCoordXRelativeInit(const int value)                            { this.m_init_relative_x=value;              }
   void              SetCoordYRelativeInit(const int value)                            { this.m_init_relative_y=value;              }
   int               CoordXRelativeInit(void)                                    const { return this.m_init_relative_x;             }
   int               CoordYRelativeInit(void)                                    const { return this.m_init_relative_y;             }
   
//--- Return the pointer to the parent element within related objects of the current group
   CGCnvElement     *GetBase(void)                                                     { return this.m_element_base;                }
//--- Return the pointer to the parent element within all groups of related objects
   CGCnvElement     *GetMain(void)     { return(this.m_element_main==NULL ? this.GetObject() : this.m_element_main);                }

//--- Return the flag indicating that the object is (1) main, (2) base
   bool              IsMain(void)                                                      { return this.m_element_main==NULL;          }
   bool              IsDependent(void)                                                 { return this.m_element_base!=NULL;          }
//--- Get the (1) main and (2) base object ID
   int               GetMainID(void)
                       {
                        if(this.IsMain())
                           return this.ID();
                        CGCnvElement *main=this.GetMain();
                        return(main!=NULL ? main.ID() : WRONG_VALUE);
                       }
   int               GetBaseID(void)
                       {
                        if(!this.IsDependent())
                           return this.ID();
                        CGCnvElement *base=this.GetBase();
                        return(base!=NULL ? base.ID() : WRONG_VALUE);
                       }

IsBaseメソッドの名前は直接的に、このメソッドが他のオブジェクトの基本オブジェクトであるかどうかを確認することを示唆しますが、これは完全に正しいとは言えません。ポイントは、変数m_element_baseの値がNULLに等しい場合、そのオブジェクトは基本となるものを持たない、つまり他のオブジェクトに接続されていないことを示すということです。ただし、他のオブジェクトの基本オブジェクトとして機能すること(接続オブジェクトとしてその内部に作成された場合)もしないこと(グラフィック要素が接続されていない場合)もあります。

したがって、このような方法で質問する(Is Base)のは誤りであることがわかります。m_element_baseNULLならば、そのオブジェクトは他のオブジェクトに接続されていない(つまり独立している)ことになり、そうでなければ、そのオブジェクトは親の基本オブジェクトに依存していることになります。そこで、IsBaseメソッドの名前をIsDependentに変更し、オブジェクトの依存関係を定義するロジックを変更しました。m_element_baseNULLに等しくないことを示すフラグが返されます。
それに伴い、基本オブジェクトの確認も変更されました。このメソッドのように、オブジェクト独立性フラグを確認します

   int               GetBaseID(void)
                       {
                        if(!this.IsDependent())
                           return this.ID();
                        CGCnvElement *base=this.GetBase();
                        return(base!=NULL ? base.ID() : WRONG_VALUE);
                       }

オブジェクトが他にバインドされていない場合は、そのIDが返されます。バインドされている場合は、基本オブジェクトのIDが返されます。


コンストラクタ(protectedとpublicパラメトリック)の宣言では、新しい形式変数を書いて、生成されたオブジェクトがバインドされるメインオブジェクト基本オブジェクトへのポインタを渡すことにします。

protected:
//--- Protected constructor
                     CGCnvElement(const ENUM_GRAPH_ELEMENT_TYPE element_type,
                                  CGCnvElement *main_obj,CGCnvElement *base_obj,
                                  const long    chart_id,
                                  const int     wnd_num,
                                  const string  descript,
                                  const int     x,
                                  const int     y,
                                  const int     w,
                                  const int     h);
public:
//--- (1) Set and (2) return the X coordinate shift relative to the base object
   void              SetCoordXRelative(const int value)                                { this.m_shift_coord_x=value;                }
   int               CoordXRelative(void)                                        const { return this.m_shift_coord_x;               }
//--- (1) Set and (2) return the Y coordinate shift relative to the base object
   void              SetCoordYRelative(const int value)                                { this.m_shift_coord_y=value;                }
   int               CoordYRelative(void)                                        const { return this.m_shift_coord_y;               }
   
//--- Event handler
   virtual void      OnChartEvent(const int id,const long& lparam,const double& dparam,const string& sparam);
//--- Parametric constructor
                     CGCnvElement(const ENUM_GRAPH_ELEMENT_TYPE element_type,
                                  CGCnvElement *main_obj,CGCnvElement *base_obj,
                                  const int     element_id,
                                  const int     element_num,
                                  const long    chart_id,
                                  const int     wnd_num,
                                  const string  descript,
                                  const int     x,
                                  const int     y,
                                  const int     w,
                                  const int     h,
                                  const color   colour,
                                  const uchar   opacity,
                                  const bool    movable=true,
                                  const bool    activity=true,
                                  const bool    redraw=false);
//--- Default constructor
                     CGCnvElement() : m_shadow(false),m_chart_color_bg((color)::ChartGetInteger(::ChartID(),CHART_COLOR_BACKGROUND))
                        {
                         this.m_type=OBJECT_DE_TYPE_GELEMENT;
                         this.m_element_main=NULL;
                         this.m_element_base=NULL;
                         this.m_shift_coord_x=0;
                         this.m_shift_coord_y=0;
                        }
//--- Destructor
                    ~CGCnvElement()
                        { this.m_canvas.Destroy();             }
     
//+------------------------------------------------------------------+

オブジェクトを作成する際には、その2つの親オブジェクトへのポインタをコンストラクタに渡す必要があるのは間違いないでしょう。オブジェクトが独立して作成された場合は、ポインタの代わりにNULLを入力します。これにより、新しいグラフィック要素を作成した後に、これらのオブジェクトを指定するための追加アクションを完全に省略することができます。

前回は、コンストラクタで親オブジェクトのデフォルト値を指定しました。

   this.m_element_main=NULL;
   this.m_element_base=NULL;

ここで再び、仮パラメータで渡された値を追加します。

//+------------------------------------------------------------------+
//| Parametric constructor                                           |
//+------------------------------------------------------------------+
CGCnvElement::CGCnvElement(const ENUM_GRAPH_ELEMENT_TYPE element_type,
                           CGCnvElement *main_obj,CGCnvElement *base_obj,
                           const int      element_id,
                           const int      element_num,
                           const long     chart_id,
                           const int      wnd_num,
                           const string   descript,
                           const int      x,
                           const int      y,
                           const int      w,
                           const int      h,
                           const color    colour,
                           const uchar    opacity,
                           const bool     movable=true,
                           const bool     activity=true,
                           const bool     redraw=false) : m_shadow(false)
  {
   this.SetTypeElement(element_type);
   this.m_type=OBJECT_DE_TYPE_GELEMENT; 
   this.m_element_main=main_obj;
   this.m_element_base=base_obj;
   this.m_chart_color_bg=(color)::ChartGetInteger((chart_id==NULL ? ::ChartID() : chart_id),CHART_COLOR_BACKGROUND);
   this.m_name=this.CreateNameGraphElement(element_type);
   //---...
   //---...

これで、作成された各オブジェクトは、すぐにその親オブジェクトを「知る」ことができるようになりました。このグラフィック要素が他に接続されていない場合、両方の変数がNULL値を持ち、オブジェクト自体がメインで独立していることを示します。


計算された矩形の可視領域で画像を切り出すメソッドに、オブジェクトが親から独立していることを確認する機能が追加されました。

//+------------------------------------------------------------------+
//| Crop the image outlined by the calculated                        |
//| rectangular visibility scope                                     |
//+------------------------------------------------------------------+
void CGCnvElement::Crop(void)
  {
//--- Get the pointer to the base object
   CGCnvElement *base=this.GetBase();
//--- If the object does not have a base object it is attached to, then there is no need to crop the hidden areas - leave
   if(!this.IsDependent())
      return;
//--- Set the initial coordinates and size of the visibility scope to the entire object
//---...
//---...


影オブジェクトクラスのファイル( \MQL5\Include\DoEasy\Objects\Graph\ShadowObj.mqh)で、メインオブジェクト基本オブジェクトへのポインタを渡すようにします。

//--- Draw the object shadow form
   void              DrawShadowFigureRect(const int w,const int h);

public:
//--- Constructor indicating the main and base objects, chart ID and subwindow
                     CShadowObj(CGCnvElement *main_obj,CGCnvElement *base_obj,
                                const long chart_id,
                                const int subwindow,
                                const string name,
                                const int x,
                                const int y,
                                const int w,
                                const int h);

//--- Supported object properties (1) integer and (2) string ones


コンストラクタを実装する際に、メインオブジェクトと基本オブジェクトへのポインタを親オブジェクトに渡します。

//+------------------------------------------------------------------+
//| Protected constructor with an object type,                       |
//| chart ID and subwindow                                           |
//+------------------------------------------------------------------+
CShadowObj::CShadowObj(CGCnvElement *main_obj,CGCnvElement *base_obj,
                       const long chart_id,
                       const int subwindow,
                       const string name,
                       const int x,
                       const int y,
                       const int w,
                       const int h) : CGCnvElement(GRAPH_ELEMENT_TYPE_SHADOW_OBJ,main_obj,base_obj,chart_id,subwindow,name,x,y,w,h)
  {
   this.m_type=OBJECT_DE_TYPE_GSHADOW; 
   CGCnvElement::SetBackgroundColor(clrNONE,true);
   CGCnvElement::SetOpacity(0);
   CGCnvElement::SetActive(false);
   this.m_opacity=CLR_DEF_SHADOW_OPACITY;
   this.m_blur=DEF_SHADOW_BLUR;
   color gray=CGCnvElement::ChangeColorSaturation(this.ChartBackgroundColor(),-100);
   this.m_color=CGCnvElement::ChangeColorLightness(gray,-50);
   this.m_shadow=false;
   this.SetVisibleFlag(false,false);
   CGCnvElement::Erase();
  }
//+------------------------------------------------------------------+

任意のオブジェクトに対して影オブジェクトを作成する場合、影オブジェクトコンストラクタは、影が作成されるオブジェクトへのポインタを受け取ります(または、そのために作成されます)。したがって、このコンストラクタでは、メインオブジェクトと基本オブジェクトへのポインタがCGCnvElement基本オブジェクトコンストラクタの適切な変数に渡されることになります。このように、すべての子クラスのコンストラクタを通じて、継承階層全体の親オブジェクトへのポインタを連鎖的に渡しています。こうすれば、作成したコントロールのメインオブジェクトと基本オブジェクトが、作成時に常に正確に指定されていることを確認することができます。


フォームオブジェクトクラスのファイル(\MQL5\Include\DoEasy\Objects\Graph\Form.mqh)のすべてのコンストラクタで、メインオブジェクト基本オブジェクトへのポインタを渡すようにします。

protected:
//--- Protected constructor with object type, chart ID and subwindow
                     CForm(const ENUM_GRAPH_ELEMENT_TYPE type,
                           CGCnvElement *main_obj,CGCnvElement *base_obj,
                           const long chart_id,
                           const int subwindow,
                           const string descript,
                           const int x,
                           const int y,
                           const int w,
                           const int h);
public:                     
//--- Constructors
                     CForm(CGCnvElement *main_obj,CGCnvElement *base_obj,
                           const long chart_id,
                           const int subwindow,
                           const string descript,
                           const int x,
                           const int y,
                           const int w,
                           const int h);
                     CForm() { this.m_type=OBJECT_DE_TYPE_GFORM; this.Initialize(); }
//--- Destructor
                    ~CForm();

...

//+------------------------------------------------------------------+
//| Protected constructor with an object type,                       |
//| chart ID and subwindow                                           |
//+------------------------------------------------------------------+
CForm::CForm(const ENUM_GRAPH_ELEMENT_TYPE type,
             CGCnvElement *main_obj,CGCnvElement *base_obj,
             const long chart_id,
             const int subwindow,
             const string descript,
             const int x,
             const int y,
             const int w,
             const int h) : CGCnvElement(type,main_obj,base_obj,chart_id,subwindow,descript,x,y,w,h)
  {
   this.m_type=OBJECT_DE_TYPE_GFORM; 
   this.Initialize();
   this.SetCoordXInit(x);
   this.SetCoordYInit(y);
   this.SetWidthInit(w);
   this.SetHeightInit(h);
  }
//+------------------------------------------------------------------+
//| Constructor indicating the main and base objects,                |
//| chart ID and subwindow                                           |
//+------------------------------------------------------------------+
CForm::CForm(CGCnvElement *main_obj,CGCnvElement *base_obj,
             const long chart_id,
             const int subwindow,
             const string descript,
             const int x,
             const int y,
             const int w,
             const int h) : CGCnvElement(GRAPH_ELEMENT_TYPE_FORM,main_obj,base_obj,chart_id,subwindow,descript,x,y,w,h)
  {
   this.m_type=OBJECT_DE_TYPE_GFORM; 
   this.Initialize();
   this.SetCoordXInit(x);
   this.SetCoordYInit(y);
   this.SetWidthInit(w);
   this.SetHeightInit(h);
  }
//+------------------------------------------------------------------+


グラフィカルオブジェクトを新規作成するメソッドで、メインオブジェクト基本オブジェクトへのポインタを渡すようにします。

//+------------------------------------------------------------------+
//| Create a new graphical object                                    |
//+------------------------------------------------------------------+
CGCnvElement *CForm::CreateNewGObject(const ENUM_GRAPH_ELEMENT_TYPE type,
                                      const int obj_num,
                                      const string descript,
                                      const int x,
                                      const int y,
                                      const int w,
                                      const int h,
                                      const color colour,
                                      const uchar opacity,
                                      const bool movable,
                                      const bool activity)
  {
   CGCnvElement *element=NULL;
   //--- Depending on the created object type,
   switch(type)
     {
      //--- create a graphical element object
      case GRAPH_ELEMENT_TYPE_ELEMENT :
         element=new CGCnvElement(type,this.GetMain(),this.GetObject(),this.ID(),obj_num,this.ChartID(),this.SubWindow(),descript,x,y,w,h,colour,opacity,movable,activity);
        break;
      //--- create a form object
      case GRAPH_ELEMENT_TYPE_FORM :
         element=new CForm(type,this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h);
        break;
      default:
        break;
     }
   if(element==NULL)
      ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_FAILED_CREATE_ELM_OBJ),this.TypeElementDescription(type));
   element.SetMovable(movable);
   element.SetCoordXRelative(element.CoordX()-this.CoordX());
   element.SetCoordYRelative(element.CoordY()-this.CoordY());
   return element;
  }
//+------------------------------------------------------------------+

これらの文字列には、基本オブジェクトにバインドされた要素が生成されるため、GetMain()メソッドの返す値はメインオブジェクトへのポインタとして渡され、GetObject()メソッドの返す値は基本オブジェクトへのポインタとして渡されます。メインオブジェクトへのポインタは、このオブジェクトの生成時にすでに登録されています。このポインタを、新しく生成されたバインドオブジェクトのコンストラクタに送ります。したがって、リンクチェーンの階層全体のメインオブジェクトは常に同じもの、つまりこのチェーンの一番最初のものであり、このオブジェクトへのポインタが基本オブジェクトへのポインタとして渡されます。よって、新たに生成されるバウンド要素の基本オブジェクトへのポインタは、このオブジェクトを指す必要があります。


CreateAndAddNewElement()およびCreateNewElement()メソッドからメインオブジェクトと基本オブジェクトを削除します。これらのメソッドはすでにグラフィック要素オブジェクトクラスから削除され、クラスのコンストラクタでメインオブジェクトと基本オブジェクトへのポインタが渡されるようになっているためです。

//+------------------------------------------------------------------+
//| Create a new attached element                                    |
//| and add it to the list of bound objects                          |
//+------------------------------------------------------------------+
CGCnvElement *CForm::CreateAndAddNewElement(const ENUM_GRAPH_ELEMENT_TYPE element_type,
                                            const int x,
                                            const int y,
                                            const int w,
                                            const int h,
                                            const color colour,
                                            const uchar opacity,
                                            const bool activity)
  {
//--- If the type of a created graphical element is less than the "element", inform of that and return 'false'
   if(element_type<GRAPH_ELEMENT_TYPE_ELEMENT)
     {
      ::Print(DFUN,CMessage::Text(MSG_FORM_OBJECT_ERR_NOT_INTENDED),::StringSubstr(::EnumToString(element_type),19));
      .return NULL;
     }
//--- Specify the element index in the list
   int num=this.m_list_elements.Total();
//--- Create a description of the default graphical element
   string descript=TypeGraphElementAsString(element_type);
//--- Get the screen coordinates of the object relative to the coordinate system of the base object
   int elm_x=x;
   int elm_y=y;
   this.GetCoords(elm_x,elm_y);
//--- Create a new graphical element
   CGCnvElement *obj=this.CreateNewGObject(element_type,num,descript,elm_x,elm_y,w,h,colour,opacity,false,activity);
   if(obj==NULL)
      .return NULL;
//--- and add it to the list of bound graphical elements
   if(!this.AddNewElement(obj,elm_x,elm_y))
     {
      delete obj;
      .return NULL;
     }
//--- Set the minimum properties for a bound graphical element
   obj.SetBackgroundColor(colour,true);
   obj.SetOpacity(opacity);
   obj.SetActive(activity);
   obj.SetMain(this.GetMain()==NULL ? this.GetObject() : this.GetMain());
   obj.SetBase(this.GetObject());
   obj.SetID(this.GetMaxIDAll()+1);
   obj.SetNumber(num);
   obj.SetCoordXRelative(obj.CoordX()-this.CoordX());
   obj.SetCoordYRelative(obj.CoordY()-this.CoordY());
   obj.SetZorder(this.Zorder(),false);
   obj.SetCoordXRelativeInit(obj.CoordXRelative());
   obj.SetCoordYRelativeInit(obj.CoordYRelative());
   obj.SetVisibleFlag(this.IsVisible(),false);
   obj.SetActive(this.Active());
   obj.SetEnabled(this.Enabled());
   return obj;
  }
//+------------------------------------------------------------------+

...

//+------------------------------------------------------------------+
//| Create a new attached element                                    |
//+------------------------------------------------------------------+
bool CForm::CreateNewElement(const ENUM_GRAPH_ELEMENT_TYPE element_type,
                             const int x,
                             const int y,
                             const int w,
                             const int h,
                             const color colour,
                             const uchar opacity,
                             const bool activity,
                             const bool redraw)
  {
//--- Create a new graphical element
   CGCnvElement *obj=this.CreateAndAddNewElement(element_type,x,y,w,h,colour,opacity,activity);
//--- If the object has been created, draw the added object and return 'true'
   if(obj==NULL)
      return false;
   obj.SetMain(this.GetMain()==NULL ? this.GetObject() : this.GetMain());
   obj.SetBase(this.GetBase());
   obj.Erase(colour,opacity,redraw);
   return true;
  }
//+------------------------------------------------------------------+


影オブジェクトを作成するメソッドで、影オブジェクトクラスのコンストラクタにメインオブジェクト基本オブジェクトへのポインタを渡します。

//+------------------------------------------------------------------+
//| Create the shadow object                                         |
//+------------------------------------------------------------------+
void CForm::CreateShadowObj(const color colour,const uchar opacity)
  {
//--- If the shadow flag is disabled or the shadow object already exists, exit
   if(!this.m_shadow || this.m_shadow_obj!=NULL)
      return;
//--- Calculate the shadow object coordinates according to the offset from the top and left
   int x=this.CoordX()-OUTER_AREA_SIZE;
   int y=this.CoordY()-OUTER_AREA_SIZE;
//--- Calculate the width and height in accordance with the top, bottom, left and right offsets
   int w=this.Width()+OUTER_AREA_SIZE*2;
   int h=this.Height()+OUTER_AREA_SIZE*2;
//--- Create a new shadow object and set the pointer to it in the variable
   this.m_shadow_obj=new CShadowObj(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),this.NameObj()+"Shadow",x,y,w,h);
   if(this.m_shadow_obj==NULL)
     {
      ::Print(DFUN,CMessage::Text(MSG_FORM_OBJECT_ERR_FAILED_CREATE_SHADOW_OBJ));
      return;
     }
   this.m_list_tmp.Add(this.m_shadow_obj);
//--- Set the properties for the created shadow object
   this.m_shadow_obj.SetID(this.ID());
   this.m_shadow_obj.SetNumber(-1);
   this.m_shadow_obj.SetOpacity(opacity);
   this.m_shadow_obj.SetColor(colour);
   this.m_shadow_obj.SetMovable(this.Movable());
   this.m_shadow_obj.SetActive(false);
   this.m_shadow_obj.SetVisibleFlag(false,false);
//--- Move the form object to the foreground
   this.BringToTop();
  }
//+------------------------------------------------------------------+

ここでのポインタの受け渡しのロジックは、上記と全く同じで、このオブジェクトに設定されたメインオブジェクトを取得して送信し、このオブジェクトを基本として指定します。

要素の座標を更新するメソッドでは、オブジェクト独立フラグこれがメインオブジェクトであることを示すフラグを確認します。

//+------------------------------------------------------------------+
//| Update the coordinate elements                                   |
//+------------------------------------------------------------------+
bool CForm::Move(const int x,const int y,const bool redraw=false)
  {
//--- Get the pointers to the base and main objects in the bound objects hierarchy, as well as the shadow object
   CGCnvElement *base=this.GetBase();
   CGCnvElement *main=this.GetMain();
   bool res=true;
//--- If the element is not movable and it is an independent object, leave
   if(!this.IsDependent() && !this.Movable())
      return false;
//--- If the object has a shadow and we failed to set new coordinate values to the properties of the shadow object, return 'false'
   if(this.m_shadow && this.m_shadow_obj!=NULL)
     {
      if(!this.m_shadow_obj.Move(x-OUTER_AREA_SIZE+this.m_shadow_obj.CoordXRelative(),y-OUTER_AREA_SIZE+this.m_shadow_obj.CoordYRelative(),false))
         return false;
     }
//--- If failed to set new values into graphical object properties, return 'false'
   if(!this.SetCoordX(x) || !this.SetCoordY(y))
      return false;
//--- Shift all bound objects
   if(!this.MoveDependentObj(x,y,false))
      return false;
   //--- If the update flag is set, and this is the main object, redraw the chart.
   if(this.IsMain() && redraw)
      ::ChartRedraw(this.ChartID());
   //--- Return 'true'
   return true;
  }
//+------------------------------------------------------------------+


グラフィック要素管理クラスのファイル(\MQL5\Include\DoEasy\Objects\Graph\GraphElmControl.mqh)にある、指定されたサブウィンドウの指定されたチャートにフォームオブジェクトを作成するメソッドに、メインオブジェクトと基本オブジェクトの指定またはその指定がないことを示す記述を追加します。

//+----------------------------------------------------------------------+
//| Create the form object on a specified chart in a specified subwindow |
//+----------------------------------------------------------------------+
CForm *CGraphElmControl::CreateForm(const int form_id,const long chart_id,const int wnd,const string name,const int x,const int y,const int w,const int h)
  {
   CForm *form=new CForm(NULL,NULL,chart_id,wnd,name,x,y,w,h);
   if(form==NULL)
      .return NULL;
   form.SetID(form_id);
   form.SetNumber(0);
   return form;
  }
//+------------------------------------------------------------------+

ここでは、フォームオブジェクトのコンストラクタに、メインオブジェクトと基本オブジェクトへのポインタとしてNULL値を渡しています。これは、作成されたオブジェクトが他のオブジェクトと結びつかず、それ自身がメインオブジェクトであることを意味します。


また、\MQL5\Include\DoEasy\Objects\Graph\Extend\CGStdGraphObjExtToolkit.mqhにある、GraphicObjectのアンカーポイントを管理するフォームのコンストラクタクラスの初期化文字列でNULL値を指定します。

//+------------------------------------------------------------------+
//| 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(GRAPH_ELEMENT_TYPE_FORM,NULL,NULL,chart_id,subwindow,name,x,y,w,h)
                          {
                           this.m_type=OBJECT_DE_TYPE_GFORM_CONTROL;
                           this.m_pivot_point=pivot_point;
                          }
  };
//+------------------------------------------------------------------+


すべてのWinFormsライブラリオブジェクトの基本オブジェクトクラスのファイル(MQL5\Include\DoEasy\Objects\Graph\WForms\WinFormBase.mqh)にあるクラスのコンストラクタでメインオブジェクト基本オブジェクトへのポインタを渡すようにします。

protected:
//--- Protected constructor with object type, chart ID and subwindow
                     CWinFormBase(const ENUM_GRAPH_ELEMENT_TYPE type,
                                  CGCnvElement *main_obj,CGCnvElement *base_obj,
                                  const long chart_id,
                                  const int subwindow,
                                  const string descript,
                                  const int x,
                                  const int y,
                                  const int w,
                                  const int h);
public:
//--- Constructors
                     CWinFormBase(CGCnvElement *main_obj,CGCnvElement *base_obj,
                                  const long chart_id,
                                  const int subwindow,
                                  const string descript,
                                  const int x,
                                  const int y,
                                  const int w,
                                  const int h);
                       
//--- (1) Set and (2) return the default text color of all panel objects

...

//+------------------------------------------------------------------+
//| Protected constructor with an object type,                       |
//| chart ID and subwindow                                           |
//+------------------------------------------------------------------+
CWinFormBase::CWinFormBase(const ENUM_GRAPH_ELEMENT_TYPE type,
                           CGCnvElement *main_obj,CGCnvElement *base_obj,
                           const long chart_id,
                           const int subwindow,
                           const string descript,
                           const int x,
                           const int y,
                           const int w,
                           const int h) : CForm(type,main_obj,base_obj,chart_id,subwindow,descript,x,y,w,h)
  {
//--- Set the specified graphical element type for the object and assign the library object type to the current object
   this.SetTypeElement(type);
   this.m_type=OBJECT_DE_TYPE_GWF_BASE; 
//--- Initialize all variables
   this.SetText("");
   this.SetForeColor(CLR_DEF_FORE_COLOR,true);
   this.SetForeStateOnColor(this.ForeColor(),true);
   this.SetForeStateOnColorMouseDown(this.ForeColor());
   this.SetForeStateOnColorMouseOver(this.ForeColor());
   this.SetForeColorOpacity(CLR_DEF_FORE_COLOR_OPACITY);
   this.SetFontBoldType(FW_TYPE_NORMAL);
   this.SetMarginAll(0);
   this.SetPaddingAll(0);
   this.SetBorderSizeAll(0);
   this.SetDockMode(CANV_ELEMENT_DOCK_MODE_NONE,false);
   this.SetBorderStyle(FRAME_STYLE_NONE);
   this.SetAutoSize(false,false);
   CForm::SetCoordXInit(x);
   CForm::SetCoordYInit(y);
   CForm::SetWidthInit(w);
   CForm::SetHeightInit(h);
   this.m_shadow=false;
   this.m_gradient_v=true;
   this.m_gradient_c=false;
  }
//+------------------------------------------------------------------+
//| Constructor indicating the main and base objects,                |
//| chart ID and subwindow                                           |
//+------------------------------------------------------------------+
CWinFormBase::CWinFormBase(CGCnvElement *main_obj,CGCnvElement *base_obj,
                           const long chart_id,
                           const int subwindow,
                           const string descript,
                           const int x,
                           const int y,
                           const int w,
                           const int h) : CForm(GRAPH_ELEMENT_TYPE_WF_BASE,main_obj,base_obj,chart_id,subwindow,descript,x,y,w,h)
  {
//--- Set the graphical element and library object types as a base WinForms object
   this.SetTypeElement(GRAPH_ELEMENT_TYPE_WF_BASE);
   this.m_type=OBJECT_DE_TYPE_GWF_BASE; 
//--- Initialize all variables
   this.SetText("");
   this.SetForeColor(CLR_DEF_FORE_COLOR,true);
   this.SetForeStateOnColor(this.ForeColor(),true);
   this.SetForeStateOnColorMouseDown(this.ForeColor());
   this.SetForeStateOnColorMouseOver(this.ForeColor());
   this.SetForeColorOpacity(CLR_DEF_FORE_COLOR_OPACITY);
   this.SetFontBoldType(FW_TYPE_NORMAL);
   this.SetMarginAll(0);
   this.SetPaddingAll(0);
   this.SetBorderSizeAll(0);
   this.SetDockMode(CANV_ELEMENT_DOCK_MODE_NONE,false);
   this.SetBorderStyle(FRAME_STYLE_NONE);
   this.SetAutoSize(false,false);
   CForm::SetCoordXInit(x);
   CForm::SetCoordYInit(y);
   CForm::SetWidthInit(w);
   CForm::SetHeightInit(h);
   this.m_shadow=false;
   this.m_gradient_v=true;
   this.m_gradient_c=false;
  }
//+------------------------------------------------------------------+


オブジェクトを再描画するメソッドで、これがメインオブジェクトであることの確認を変更します

//+------------------------------------------------------------------+
//| Redraw the object                                                |
//+------------------------------------------------------------------+
void CWinFormBase::Redraw(bool redraw)
  {
//--- If the object type is less than the "Base WinForms object" or the object is not to be displayed, exit
   if(this.TypeGraphElement()<GRAPH_ELEMENT_TYPE_WF_BASE || !this.Displayed())
      return;
//--- Get the "Shadow" object

//---...
//---...

//--- If the redraw flag is set and if this is the main object the rest are bound to,
//--- redraw the chart to display changes immediately
   if(this.IsMain() && redraw)
      ::ChartRedraw(this.ChartID());
  }
//+------------------------------------------------------------------+

以前は、ここで以下のような方法で確認をおこなっていました

if(redraw && this.GetMain()==NULL)

要するに同じことなのですが、せっかくメソッドがあるのだから、使ってみることにします。

クラスのコンストラクタとオブジェクトがメインまたは基本であることの確認に対する上記の改良は、すべてのWinFormsライブラリオブジェクトの絶対にすべてのクラスですでに実行されています。新しいオブジェクトの場合、これがデフォルトの動作となります。ここでは、記事スペースの節約のため、これ以上の改良点については説明しません。

次のクラスファイルのヘッダでは、メインオブジェクトと基本オブジェクトへのポインタを渡すようにコンストラクタが改良されただけです。

CommonBase.mqhButton.mqhElementsListBox.mqhRadioButton.mqhArrowButton.mqhArrowLeftButton.mqhArrowRightButton.mqhArrowUpButton.mqhArrowDownButton.mqhListBoxItem.mqhTabHeader.mqh

しかし、コンストラクタの改良に加えて、新しいグラフィカルオブジェクトを作成するためのメソッドに変更が加えられたクラスファイルもあります。ここでは、作成したオブジェクトのコンストラクタにメインオブジェクトと基本オブジェクトへのポインタを渡すように改良しています。

//+------------------------------------------------------------------+
//| Create a new graphical object                                    |
//+------------------------------------------------------------------+
CGCnvElement *CButtonListBox::CreateNewGObject(const ENUM_GRAPH_ELEMENT_TYPE type,
                                               const int obj_num,
                                               const string descript,
                                               const int x,
                                               const int y,
                                               const int w,
                                               const int h,
                                               const color colour,
                                               const uchar opacity,
                                               const bool movable,
                                               const bool activity)
  {
//--- create the CButton object
   CGCnvElement *element=new CButton(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h);
   if(element==NULL)
      ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_FAILED_CREATE_ELM_OBJ),this.TypeElementDescription(type));
//--- set the object relocation flag and relative coordinates
   element.SetMovable(movable);
   element.SetCoordXRelative(element.CoordX()-this.CoordX());
   element.SetCoordYRelative(element.CoordY()-this.CoordY());
   return element;
  }
//+------------------------------------------------------------------+


新しいグラフィカルオブジェクトを作成するメソッドについても、(コンストラクタの改良に加えて)ファイル内で同じまたは類似の改良が施されています。

ButtonListBox.mqhCheckedListBox.mqhListBox.mqhArrowLeftRightBox.mqhArrowUpDownBox.mqh

それでは、新しい補助ヒントオブジェクトを作成してみましょう。


ヒント補助オブジェクトとその派生物

このようなオブジェクトを構築する概念は、ほとんどのライブラリのオブジェクトを構築する概念と何ら変わりはないでしょう。ここには基本オブジェクトがあり、その子孫は特定のヒントオブジェクトの必要な機能を洗練して実装することになります。


\MQL5\Include\DoEasy\Objects\Graph\WForms\で、CWinFormBaseクラスのWinFormBase.mqhを新規作成します。このクラスは、すべてのWinFormsオブジェクトの基本クラスであるCWinFormBaseから派生させる必要があり、そのファイルは作成されたクラスのファイルにインクルードする必要があります。

//+------------------------------------------------------------------+
//|                                                     HintBase.mqh |
//|                                  Copyright 2022, MetaQuotes Ltd. |
//|                             https://mql5.com/ja/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2022, MetaQuotes Ltd."
#property link      "https://mql5.com/ja/users/artmedia70"
#property version   "1.00"
#property strict    // Necessary for mql4
//+------------------------------------------------------------------+
//| Include files                                                    |
//+------------------------------------------------------------------+
#include "..\WinFormBase.mqh"
//+------------------------------------------------------------------+
//| Class of the base Hint object of the WForms controls             |
//+------------------------------------------------------------------+
class CHintBase : public CWinFormBase
  {
  }


protectedセクションで、ヒントを描画するための仮想メソッドを書き、protectedコンストラクタを宣言します。クラスのpublicセクションには、ヒントの色を設定したり返したりするメソッドを書き、パラメトリックコンストラクタとオブジェクトの表示、再描画、クリアのための仮想メソッド、およびオブジェクトの枠を描画するメソッドを宣言することにします。

//+------------------------------------------------------------------+
//| Class of the base Hint object of the WForms controls             |
//+------------------------------------------------------------------+
class CHintBase : public CWinFormBase
  {
private:

protected:
   //--- Draw a hint
   virtual void      DrawHint(const int shift) {return;}
//--- Protected constructor with object type, chart ID and subwindow
                     CHintBase(const ENUM_GRAPH_ELEMENT_TYPE type,
                               CGCnvElement *main_obj,CGCnvElement *base_obj,
                               const long chart_id,
                               const int subwindow,
                               const string descript,
                               const int x,
                               const int y,
                               const int w,
                               const int h);
public:
//--- (1) Set and (2) return the hint color
   void              SetHintColor(const color clr)       { this.SetForeColor(clr,false);  }
   color             HintColor(void)               const { return this.ForeColor();       }
//--- Constructor
                     CHintBase(CGCnvElement *main_obj,CGCnvElement *base_obj,
                               const long chart_id,
                               const int subwindow,
                               const string descript,
                               const int x,
                               const int y,
                               const int w,
                               const int h);
//--- Display the element
   virtual void      Show(void);
//--- Redraw the object
   virtual void      Redraw(bool redraw);
//--- Clear the element filling it with color and opacity
   virtual void      Erase(const color colour,const uchar opacity,const bool redraw=false);
//--- Clear the element with a gradient fill
   virtual void      Erase(color &colors[],const uchar opacity,const bool vgradient,const bool cycle,const bool redraw=false);
//--- Draw the hint frame
   virtual void      DrawFrame(void);
  };
//+------------------------------------------------------------------+

ヒントの色を設定するメソッドと返すメソッドは、実際にはオブジェクトのテキストの色を設定し返します(ForeColor())。

宣言されたメソッドについて詳しく考えてみましょう。

以下は、Protectedコンストラクタです。

//+------------------------------------------------------------------+
//| Protected constructor with an object type,                       |
//| chart ID and subwindow                                           |
//+------------------------------------------------------------------+
CHintBase::CHintBase(const ENUM_GRAPH_ELEMENT_TYPE type,
                     CGCnvElement *main_obj,CGCnvElement *base_obj,
                     const long chart_id,
                     const int subwindow,
                     const string descript,
                     const int x,
                     const int y,
                     const int w,
                     const int h) : CWinFormBase(type,main_obj,base_obj,chart_id,subwindow,descript,x,y,w,h)
  {
//--- Set the specified graphical element type for the object and assign the library object type to the current object
   this.SetTypeElement(type);
   this.m_type=OBJECT_DE_TYPE_GWF_HELPER;
   this.SetPaddingAll(0);
   this.SetMarginAll(0);
   this.SetBorderSizeAll(0);
  }
//+------------------------------------------------------------------+

作成されたオブジェクトの型、メインオブジェクトと基本オブジェクトへのポインタ、および作成されたオブジェクトの残りの基本パラメータは、このようなすべてのコンストラクタの標準としてコンストラクタに渡されています。初期化文字列では、親クラスのコンストラクタは、メソッドに渡されたオブジェクトの型、メインオブジェクトと基本オブジェクトへのポインタ、コンストラクタの仮パラメータで渡された他のプロパティを取得します。クラス本体では、メソッドに渡される要素の型、補助オブジェクトとしてライブラリのグラフィカルオブジェクトの型を設定し、さらにPadding、Margin、BorderSizeの値を0に設定します。

以下は、パラメトリックコンストラクタです。

//+------------------------------------------------------------------+
//| Constructor indicating the main and base objects,                |
//| chart ID and subwindow                                           |
//+------------------------------------------------------------------+
CHintBase::CHintBase(CGCnvElement *main_obj,CGCnvElement *base_obj,
                     const long chart_id,
                     const int subwindow,
                     const string descript,
                     const int x,
                     const int y,
                     const int w,
                     const int h) : CWinFormBase(GRAPH_ELEMENT_TYPE_WF_HINT_BASE,main_obj,base_obj,chart_id,subwindow,descript,x,y,w,h)
  {
   this.SetTypeElement(GRAPH_ELEMENT_TYPE_WF_HINT_BASE);
   this.m_type=OBJECT_DE_TYPE_GWF_HELPER;
   this.SetPaddingAll(0);
   this.SetMarginAll(0);
   this.SetBorderSizeAll(0);
  }
//+------------------------------------------------------------------+

ここでは、ライブラリのすべてのWinFormsオブジェクトに対して現在おこなっているのと同様に、メインオブジェクトと基本オブジェクトへのポインタと、オブジェクトを作成するための他の標準的なパラメータをコンストラクタに渡します。初期化文字列の中で、作成されるオブジェクトの型を基本ヒントオブジェクトとして親オブジェクトに渡します。メインオブジェクトと基本オブジェクトへのポインタと、コンストラクタに渡された他のパラメータを渡します。コンストラクタの本体で、作成されたオブジェクトの型を基本ヒントオブジェクトとして指定し、補助オブジェクトとしてライブラリのグラフィカルオブジェクトの型を指定します。


以下は、オブジェクトを再描画するメソッドです。

//+------------------------------------------------------------------+
//| Redraw the object                                                |
//+------------------------------------------------------------------+
void CHintBase::Redraw(bool redraw)
  {
//--- Fill the object with background color having transparency
   this.Erase(this.BackgroundColor(),this.Opacity(),true);
  }
//+------------------------------------------------------------------+

ここでは、オブジェクトに背景色と不透明度を設定した上で、オブジェクトをクリアするメソッドを呼び出すだけです。


以下は、色と不透明度を指定して要素をクリアするメソッドです。

//+------------------------------------------------------------------+
//| Clear the element filling it with color and opacity              |
//+------------------------------------------------------------------+
void CHintBase::Erase(const color colour,const uchar opacity,const bool redraw=false)
  {
//--- Fill the element having the specified color and the redrawing flag
   CGCnvElement::EraseNoCrop(colour,opacity,false);
//--- If the object has a frame, draw it
   if(this.BorderStyle()!=FRAME_STYLE_NONE)
      this.DrawFrame();
//--- Draw a hint
   this.DrawHint(1);
//--- Update the element having the specified redrawing flag
   this.Update(redraw);
  }
//+------------------------------------------------------------------+

メソッドのロジックはコードのコメントで完全に説明されています。


以下は、グラデーションで塗りつぶされた要素をクリアするメソッドです。

//+------------------------------------------------------------------+
//| Clear the element with a gradient fill                           |
//+------------------------------------------------------------------+
void CHintBase::Erase(color &colors[],const uchar opacity,const bool vgradient,const bool cycle,const bool redraw=false)
  {
//--- Fill the element having the specified color array and the redrawing flag
   CGCnvElement::EraseNoCrop(colors,opacity,vgradient,cycle,false);
//--- If the object has a frame, draw it
   if(this.BorderStyle()!=FRAME_STYLE_NONE)
      this.DrawFrame();
//--- Draw a hint
   this.DrawHint(1);
//--- Update the element having the specified redrawing flag
   this.Update(redraw);
  }
//+------------------------------------------------------------------+

このメソッドも前バージョンと同様に、背景を色の配列から選んで塗りつぶすとともに、枠とヒントを描画します。


以下は、要素の境界線を描画するメソッドです。

//+------------------------------------------------------------------+
//| Draw the element border                                          |
//+------------------------------------------------------------------+
void CHintBase::DrawFrame(void)
  {
   this.DrawRectangle(0,0,this.Width()-1,this.Height()-1,this.BorderColor(),this.Opacity());
  }
//+------------------------------------------------------------------+

ここでは、DrawRectangleメソッドを使って、オブジェクトの端に枠の色と不透明度を設定した枠を描いています。


以下は、要素を示すメソッドです。

//+------------------------------------------------------------------+
//| Show the element                                                 |
//+------------------------------------------------------------------+
void CHintBase::Show(void)
  {
//--- If the element should not be displayed (hidden inside another control), leave
   if(!this.Displayed())
      return;
//--- If the object has a shadow, display it
   if(this.m_shadow_obj!=NULL)
      this.m_shadow_obj.Show();
//--- Display the main form
   CGCnvElement::Show();
   this.Redraw(false);
//--- In the loop by all bound graphical objects,
   for(int i=0;i<this.m_list_elements.Total();i++)
     {
      //--- get the next graphical element
      CGCnvElement *element=this.m_list_elements.At(i);
      if(element==NULL || !element.Displayed())
         continue;
      //--- and display it
      element.Show();
     }
  }
//+------------------------------------------------------------------+

メソッドのロジックはコードのコメントで完全に説明されています。


他のすべてのヒントオブジェクトは、このオブジェクトを基に作成されます。今日は、区切りを左右上下にシフトする可能性を示唆するオブジェクトを作成します。適切な矢印が表示されます。

左にシフトする可能性についてのヒントオブジェクトを作成してみましょう。

\MQL5\Include\DoEasy\Objects\Graph\WForms\Helpers\で、CHintMoveLeftクラスのHintMoveLeft.mqhファイルを新規に作成します。クラスはヒントオブジェクト基本クラスから派生する,ので、そのファイルを作成されたクラスファイルにインクルードする必要があります

//+------------------------------------------------------------------+
//|                                                 HintMoveLeft.mqh |
//|                                  Copyright 2022, MetaQuotes Ltd. |
//|                             https://mql5.com/ja/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2022, MetaQuotes Ltd."
#property link      "https://mql5.com/ja/users/artmedia70"
#property version   "1.00"
#property strict    // Necessary for mql4
//+------------------------------------------------------------------+
//| Include files                                                    |
//+------------------------------------------------------------------+
#include "HintBase.mqh"
//+------------------------------------------------------------------+
//| HintMoveLeft base object class of the WForms controls            |
//+------------------------------------------------------------------+
class CHintMoveLeft : public CHintBase
  {
  }


クラスのprotectedセクションで、親クラスのこのメソッドをオーバーライドするヒント描画用の仮想メソッドを宣言し、protectedクラスのコンストラクタを宣言します。publicセクションで、「カーソルがアクティブ領域内にあり、マウスボタンがクリックされていない」イベントのハンドラおよびパラメトリックコンストラクタを宣言します。

//+------------------------------------------------------------------+
//| HintMoveLeft object class of the WForms controls                 |
//+------------------------------------------------------------------+
class CHintMoveLeft : public CHintBase
  {

protected:
   //--- Draw a hint
   virtual void      DrawHint(const int shift);
//--- Protected constructor with object type, chart ID and subwindow
                     CHintMoveLeft(const ENUM_GRAPH_ELEMENT_TYPE type,
                                   CGCnvElement *main_obj,CGCnvElement *base_obj,
                                   const long chart_id,
                                   const int subwindow,
                                   const string descript,
                                   const int x,
                                   const int y,
                                   const int w,
                                   const int h);
public:
//--- 'The cursor is inside the active area, the mouse buttons are not clicked' event handler
   virtual void      MouseActiveAreaNotPressedHandler(const int id,const long& lparam,const double& dparam,const string& sparam);
//--- Constructor
                     CHintMoveLeft(CGCnvElement *main_obj,CGCnvElement *base_obj,
                                   const long chart_id,
                                   const int subwindow,
                                   const string descript,
                                   const int x,
                                   const int y,
                                   const int w,
                                   const int h);
  };
//+------------------------------------------------------------------+


宣言されたメソッドについて詳しく考えてみましょう。

以下は、Protectedコンストラクタです。

//+------------------------------------------------------------------+
//| Protected constructor with an object type,                       |
//| chart ID and subwindow                                           |
//+------------------------------------------------------------------+
CHintMoveLeft::CHintMoveLeft(const ENUM_GRAPH_ELEMENT_TYPE type,
                             CGCnvElement *main_obj,CGCnvElement *base_obj,
                             const long chart_id,
                             const int subwindow,
                             const string descript,
                             const int x,
                             const int y,
                             const int w,
                             const int h) : CHintBase(type,main_obj,base_obj,chart_id,subwindow,descript,x,y,w,h)
  {
//--- Set the specified graphical element type for the object and assign the library object type to the current object
   this.SetTypeElement(type);
  }
//+------------------------------------------------------------------+

作成されたオブジェクトの型、メインオブジェクトと基本オブジェクトへのポインタ、その他新しいグラフィック要素の作成に必要なパラメータがコンストラクタに渡されます。初期化文字列では、作成されたオブジェクトの型と、コンストラクタ変数で指定された他のすべてのプロパティが、親クラスのコンストラクタに渡されます。クラス本体で、コンストラクタの正式なパラメータに渡される生成されるオブジェクトの型を指定します。


以下は、パラメトリックコンストラクタです。

//+------------------------------------------------------------------+
//| Constructor indicating the main and base objects,                |
//| chart ID and subwindow                                           |
//+------------------------------------------------------------------+
CHintMoveLeft::CHintMoveLeft(CGCnvElement *main_obj,CGCnvElement *base_obj,
                             const long chart_id,
                             const int subwindow,
                             const string descript,
                             const int x,
                             const int y,
                             const int w,
                             const int h) : CHintBase(GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_LEFT,main_obj,base_obj,chart_id,subwindow,descript,x,y,w,h)
  {
   this.SetTypeElement(GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_LEFT);
  }
//+------------------------------------------------------------------+

作成されるオブジェクトの型は仮パラメータとして渡されず、代わりにコードにハードコードされていることを除いて、ここでのすべてはprotectedコンストラクタとまったく同じです。


以下は、ヒントを描くメソッドです。

//+------------------------------------------------------------------+
//| Draw a hint                                                      |
//+------------------------------------------------------------------+
void CHintMoveLeft::DrawHint(const int shift)
  {
   int w=this.Width();
   int h=this.Height();
   int middle=int(h*0.5);
   this.DrawRectangleFill(w-2,0,w-1,h-1,this.HintColor(),255);
   this.DrawTriangleFill(shift,middle,shift+3,middle-3,shift+3,middle+3,this.HintColor(),255);
   this.DrawLine(shift+3,middle,w-3,middle,this.HintColor(),255);
  }
//+------------------------------------------------------------------+

ここでは、オブジェクト全体の幅と高さを取得し、矢印を構築するための値をカウントする中心線を計算します。オブジェクトの右側に幅2ピクセルの塗りつぶされた縦長の長方形を描きます。メソッドに渡されたshift値と中心線から数えた角度を持つ三角形を描きます。最後に、メソッドに渡されたshift値によってインデントされた左端から、オブジェクトの右側にある描画済みの縦長長方形まで、水平方向の中心線を描画します。こうして、オブジェクトの右側にベースがある左矢印ができます。この場合、矢印の長さはメソッドに渡されたshift値に依存します。この変数の値が高いほど、矢印は短くなります。

SplitContainerコントロールの区切り領域にマウスカーソルを合わせると、点線の四角形が表示されます。また、区切りの配置(縦か横か)によって、ここに左右または上下の矢印が2つ表示されるはずです。マウスカーソルが区切り領域から離れると、点線の長方形と矢印が消えるはずです。これは、SplitContainerオブジェクトのパネルの「カーソルがアクティブ領域内にある」イベントのハンドラで発生します。ただし、カーソルが補助オブジェクトの領域に入った場合、パネルハンドラは呼び出されません。そこで、補助オブジェクトにもそのようなハンドラを作る必要があります。このような仮想ハンドラはすべてフォームオブジェクトクラスで宣言され、派生クラスでオーバーライドされるべきものです。ここで再定義しましょう。

以下は、「カーソルがアクティブ領域内にあり、マウスボタンがクリックされていない」イベントのハンドラです。

//+------------------------------------------------------------------+
//| 'The cursor is inside the active area,                           |
//| no mouse buttons are clicked' event handler                      |
//+------------------------------------------------------------------+
void CHintMoveLeft::MouseActiveAreaNotPressedHandler(const int id,const long& lparam,const double& dparam,const string& sparam)
  {
//--- Set the flag for not displaying the object and hide it
   this.SetDisplayed(false);
   this.Hide();
//--- Get the pointer to the base object
   CWinFormBase *base=this.GetBase();
   if(base==NULL)
      return;
//--- From the base object, get the pointers to the "right, up and down shift" hint objects,
//--- set the object non-display flag for them and hide the controls
   CWinFormBase *hint_mr=base.GetElementByType(GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_RIGHT,0);
   if(hint_mr!=NULL)
     {
      hint_mr.SetDisplayed(false);
      hint_mr.Hide();
     }
   CWinFormBase *hint_mu=base.GetElementByType(GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_UP,0);
   if(hint_mu!=NULL)
     {
      hint_mu.SetDisplayed(false);
      hint_mu.Hide();
     }
   CWinFormBase *hint_md=base.GetElementByType(GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_DOWN,0);
   if(hint_md!=NULL)
     {
      hint_md.SetDisplayed(false);
      hint_md.Hide();
     }
  }
//+------------------------------------------------------------------+

メソッドロジック全体は、コードへのコメントで説明されています。つまり、カーソルがオブジェクトのアクティブ領域に入ると、すぐにこのハンドラが起動します。まず、このオブジェクトをその中で非表示にします。そして、ヒントオブジェクトが接続している基本オブジェクトへのポインタを取得します。このメソッドでは、右、上、下の矢印でヒントを得ることができます。左の矢印はこのオブジェクトではすでに非表示になっています。要求されたオブジェクトへのポインタが有効であれば、これらのオブジェクトも非表示にします。したがって、カーソルがこのオブジェクトの領域に入ると、自分自身と他のヒントオブジェクトを非表示にすることができます。


右にシフトする可能性を示唆するオブジェクトを作ってみましょう。

\MQL5\Include\DoEasy\Objects\Graph\WForms\Helpers\で、CHintMoveRightクラスのHintMoveRight.mqhファイルを新規に作成します。クラスはヒントオブジェクト基本クラスから派生する,ので、そのファイルを作成されたクラスファイルにインクルードする必要があります

//+------------------------------------------------------------------+
//|                                                HintMoveRight.mqh |
//|                                  Copyright 2022, MetaQuotes Ltd. |
//|                             https://mql5.com/ja/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2022, MetaQuotes Ltd."
#property link      "https://mql5.com/ja/users/artmedia70"
#property version   "1.00"
#property strict    // Necessary for mql4
//+------------------------------------------------------------------+
//| Include files                                                    |
//+------------------------------------------------------------------+
#include "HintBase.mqh"
//+------------------------------------------------------------------+
//| Class of the base HintMoveRight object of the WForms controls    |
//+------------------------------------------------------------------+
class CHintMoveRight : public CHintBase
  {
  }

ヒントを描画するメソッドとマウスイベントのハンドラ以外は、上のクラスと同じです。

//+------------------------------------------------------------------+
//| Class of the HintMoveRight object of the WForms controls         |
//+------------------------------------------------------------------+
class CHintMoveRight : public CHintBase
  {

protected:
   //--- Draw a hint
   virtual void      DrawHint(const int shift);
//--- Protected constructor with object type, chart ID and subwindow
                     CHintMoveRight(const ENUM_GRAPH_ELEMENT_TYPE type,
                                    CGCnvElement *main_obj,CGCnvElement *base_obj,
                                    const long chart_id,
                                    const int subwindow,
                                    const string descript,
                                    const int x,
                                    const int y,
                                    const int w,
                                    const int h);
public:
//--- 'The cursor is inside the active area, the mouse buttons are not clicked' event handler
   virtual void      MouseActiveAreaNotPressedHandler(const int id,const long& lparam,const double& dparam,const string& sparam);
//--- Constructor
                     CHintMoveRight(CGCnvElement *main_obj,CGCnvElement *base_obj,
                                    const long chart_id,
                                    const int subwindow,
                                    const string descript,
                                    const int x,
                                    const int y,
                                    const int w,
                                    const int h);
  };
//+------------------------------------------------------------------+
//| Protected constructor with an object type,                       |
//| chart ID and subwindow                                           |
//+------------------------------------------------------------------+
CHintMoveRight::CHintMoveRight(const ENUM_GRAPH_ELEMENT_TYPE type,
                               CGCnvElement *main_obj,CGCnvElement *base_obj,
                               const long chart_id,
                               const int subwindow,
                               const string descript,
                               const int x,
                               const int y,
                               const int w,
                               const int h) : CHintBase(type,main_obj,base_obj,chart_id,subwindow,descript,x,y,w,h)
  {
//--- Set the specified graphical element type for the object and assign the library object type to the current object
   this.SetTypeElement(type);
  }
//+------------------------------------------------------------------+
//| Constructor indicating the main and base objects,                |
//| chart ID and subwindow                                           |
//+------------------------------------------------------------------+
CHintMoveRight::CHintMoveRight(CGCnvElement *main_obj,CGCnvElement *base_obj,
                              const long chart_id,
                              const int subwindow,
                              const string descript,
                              const int x,
                              const int y,
                              const int w,
                              const int h) : CHintBase(GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_RIGHT,main_obj,base_obj,chart_id,subwindow,descript,x,y,w,h)
  {
   this.SetTypeElement(GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_RIGHT);
  }
//+------------------------------------------------------------------+


以下は、ヒントを描くメソッドです。

//+------------------------------------------------------------------+
//| Draw a hint                                                      |
//+------------------------------------------------------------------+
void CHintMoveRight::DrawHint(const int shift)
  {
   int w=this.Width();
   int h=this.Height();
   int middle=int(h*0.5);
   this.DrawRectangleFill(0,0,1,h-1,this.HintColor(),255);
   this.DrawTriangleFill(shift+8,middle,shift+8-3,middle+3,shift+8-3,middle-3,this.HintColor(),255);
   this.DrawLine(2,middle,shift+4,middle,this.HintColor(),255);
  }
//+------------------------------------------------------------------+

ここでは、オブジェクトの左側に縦の長方形を描き、shift値に8(線の長さ)を加えた値から矢印の三角形を描き、さらに、描いた基本の長方形から始まり、描いた三角形で終わる水平の中心線を描いています。shiftを大きくすると、矢印の長さが長くなります。

以下は、「カーソルがアクティブ領域内にあり、マウスボタンがクリックされていない」イベントのハンドラです。

//+------------------------------------------------------------------+
//| 'The cursor is inside the active area,                           |
//| no mouse buttons are clicked' event handler                      |
//+------------------------------------------------------------------+
void CHintMoveRight::MouseActiveAreaNotPressedHandler(const int id,const long& lparam,const double& dparam,const string& sparam)
  {
//--- Set the flag for not displaying the object and hide it
   this.SetDisplayed(false);
   this.Hide();
//--- Get the pointer to the base object
   CWinFormBase *base=this.GetBase();
   if(base==NULL)
      return;
//--- From the base object, get the pointers to the "left, up and down shift" hint objects,
//--- set the object non-display flag for them and hide the controls
   CWinFormBase *hint_ml=base.GetElementByType(GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_LEFT,0);
   if(hint_ml!=NULL)
     {
      hint_ml.SetDisplayed(false);
      hint_ml.Hide();
     }
   CWinFormBase *hint_mu=base.GetElementByType(GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_UP,0);
   if(hint_mu!=NULL)
     {
      hint_mu.SetDisplayed(false);
      hint_mu.Hide();
     }
   CWinFormBase *hint_md=base.GetElementByType(GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_DOWN,0);
   if(hint_md!=NULL)
     {
      hint_md.SetDisplayed(false);
      hint_md.Hide();
     }
  }
//+------------------------------------------------------------------+

このメソッドのロジックは、前のクラスのこのメソッドのロジックと同じです。唯一の違いは、ここではこのオブジェクトが右矢印であることです。このメソッドでは、このオブジェクトを非表示にした後、左、上、下矢印を持つヒントオブジェクトへのポインタを取得します。


上下の矢印を持つヒントオブジェクトの他の2つのクラスは、そのまま考えます。それらのロジックは、上で説明した2つのクラスと同じで、繰り返すことに意味はありません。

以下は、上にシフトする可能性を示す補助オブジェクトのクラスです。

//+------------------------------------------------------------------+
//|                                                   HintMoveUp.mqh |
//|                                  Copyright 2022, MetaQuotes Ltd. |
//|                             https://mql5.com/ja/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2022, MetaQuotes Ltd."
#property link      "https://mql5.com/ja/users/artmedia70"
#property version   "1.00"
#property strict    // Necessary for mql4
//+------------------------------------------------------------------+
//| Include files                                                    |
//+------------------------------------------------------------------+
#include "HintBase.mqh"
//+------------------------------------------------------------------+
//| HintMoveUp object class of WForms controls                       |
//+------------------------------------------------------------------+
class CHintMoveUp : public CHintBase
  {

protected:
   //--- Draw a hint
   virtual void      DrawHint(const int shift);
//--- Protected constructor with object type, chart ID and subwindow
                     CHintMoveUp(const ENUM_GRAPH_ELEMENT_TYPE type,
                                 CGCnvElement *main_obj,CGCnvElement *base_obj,
                                 const long chart_id,
                                 const int subwindow,
                                 const string descript,
                                 const int x,
                                 const int y,
                                 const int w,
                                 const int h);
public:
//--- 'The cursor is inside the active area, the mouse buttons are not clicked' event handler
   virtual void      MouseActiveAreaNotPressedHandler(const int id,const long& lparam,const double& dparam,const string& sparam);
//--- Constructor
                     CHintMoveUp(CGCnvElement *main_obj,CGCnvElement *base_obj,
                                 const long chart_id,
                                 const int subwindow,
                                 const string descript,
                                 const int x,
                                 const int y,
                                 const int w,
                                 const int h);
  };
//+------------------------------------------------------------------+
//| Protected constructor with an object type,                       |
//| chart ID and subwindow                                           |
//+------------------------------------------------------------------+
CHintMoveUp::CHintMoveUp(const ENUM_GRAPH_ELEMENT_TYPE type,
                         CGCnvElement *main_obj,CGCnvElement *base_obj,
                         const long chart_id,
                         const int subwindow,
                         const string descript,
                         const int x,
                         const int y,
                         const int w,
                         const int h) : CHintBase(type,main_obj,base_obj,chart_id,subwindow,descript,x,y,w,h)
  {
//--- Set the specified graphical element type for the object and assign the library object type to the current object
   this.SetTypeElement(type);
  }
//+------------------------------------------------------------------+
//| Constructor indicating the main and base objects,                |
//| chart ID and subwindow                                           |
//+------------------------------------------------------------------+
CHintMoveUp::CHintMoveUp(CGCnvElement *main_obj,CGCnvElement *base_obj,
                         const long chart_id,
                         const int subwindow,
                         const string descript,
                         const int x,
                         const int y,
                         const int w,
                         const int h) : CHintBase(GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_UP,main_obj,base_obj,chart_id,subwindow,descript,x,y,w,h)
  {
   this.SetTypeElement(GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_UP);
  }
//+------------------------------------------------------------------+
//| Draw a hint                                                      |
//+------------------------------------------------------------------+
void CHintMoveUp::DrawHint(const int shift)
  {
   int w=this.Width();
   int h=this.Height();
   int middle=int(w*0.5);
   this.DrawRectangleFill(0,h-2,w-1,h-1,this.HintColor(),255);
   this.DrawTriangleFill(middle,shift,middle+3,shift+3,middle-3,shift+3,this.HintColor(),255);
   this.DrawLine(middle,shift+4,middle,h-3,this.HintColor(),255);
  }
//+------------------------------------------------------------------+
//| 'The cursor is inside the active area,                           |
//| no mouse buttons are clicked' event handler                      |
//+------------------------------------------------------------------+
void CHintMoveUp::MouseActiveAreaNotPressedHandler(const int id,const long& lparam,const double& dparam,const string& sparam)
  {
//--- Set the flag for not displaying the object and hide it
   this.SetDisplayed(false);
   this.Hide();
//--- Get the pointer to the base object
   CWinFormBase *base=this.GetBase();
   if(base==NULL)
      return;
//--- From the base object, get the pointers to the "down, left and right shift" hint objects,
//--- set the object non-display flag for them and hide the controls
   CWinFormBase *hint_md=base.GetElementByType(GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_DOWN,0);
   if(hint_md!=NULL)
     {
      hint_md.SetDisplayed(false);
      hint_md.Hide();
     }
   CWinFormBase *hint_ml=base.GetElementByType(GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_LEFT,0);
   if(hint_ml!=NULL)
     {
      hint_ml.SetDisplayed(false);
      hint_ml.Hide();
     }
   CWinFormBase *hint_mr=base.GetElementByType(GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_RIGHT,0);
   if(hint_mr!=NULL)
     {
      hint_mr.SetDisplayed(false);
      hint_mr.Hide();
     }
  }
//+------------------------------------------------------------------+


以下は、下にシフトする可能性を示す補助オブジェクトのクラスです。

//+------------------------------------------------------------------+
//|                                                 HintMoveDown.mqh |
//|                                  Copyright 2022, MetaQuotes Ltd. |
//|                             https://mql5.com/ja/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2022, MetaQuotes Ltd."
#property link      "https://mql5.com/ja/users/artmedia70"
#property version   "1.00"
#property strict    // Necessary for mql4
//+------------------------------------------------------------------+
//| Include files                                                    |
//+------------------------------------------------------------------+
#include "HintBase.mqh"
//+------------------------------------------------------------------+
//| HintMoveDown object class of WForms controls                     |
//+------------------------------------------------------------------+
class CHintMoveDown : public CHintBase
  {

protected:
   //--- Draw a hint
   virtual void      DrawHint(const int shift);
//--- Protected constructor with object type, chart ID and subwindow
                     CHintMoveDown(const ENUM_GRAPH_ELEMENT_TYPE type,
                                   CGCnvElement *main_obj,CGCnvElement *base_obj,
                                   const long chart_id,
                                   const int subwindow,
                                   const string descript,
                                   const int x,
                                   const int y,
                                   const int w,
                                   const int h);
public:
//--- 'The cursor is inside the active area, the mouse buttons are not clicked' event handler
   virtual void      MouseActiveAreaNotPressedHandler(const int id,const long& lparam,const double& dparam,const string& sparam);
//--- Constructor
                     CHintMoveDown(CGCnvElement *main_obj,CGCnvElement *base_obj,
                                   const long chart_id,
                                   const int subwindow,
                                   const string descript,
                                   const int x,
                                   const int y,
                                   const int w,
                                   const int h);
  };
//+------------------------------------------------------------------+
//| Protected constructor with an object type,                       |
//| chart ID and subwindow                                           |
//+------------------------------------------------------------------+
CHintMoveDown::CHintMoveDown(const ENUM_GRAPH_ELEMENT_TYPE type,
                             CGCnvElement *main_obj,CGCnvElement *base_obj,
                             const long chart_id,
                             const int subwindow,
                             const string descript,
                             const int x,
                             const int y,
                             const int w,
                             const int h) : CHintBase(type,main_obj,base_obj,chart_id,subwindow,descript,x,y,w,h)
  {
//--- Set the specified graphical element type for the object and assign the library object type to the current object
   this.SetTypeElement(type);
  }
//+------------------------------------------------------------------+
//| Constructor indicating the main and base objects,                |
//| chart ID and subwindow                                           |
//+------------------------------------------------------------------+
CHintMoveDown::CHintMoveDown(CGCnvElement *main_obj,CGCnvElement *base_obj,
                             const long chart_id,
                             const int subwindow,
                             const string descript,
                             const int x,
                             const int y,
                             const int w,
                             const int h) : CHintBase(GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_DOWN,main_obj,base_obj,chart_id,subwindow,descript,x,y,w,h)
  {
   this.SetTypeElement(GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_DOWN);
  }
//+------------------------------------------------------------------+
//| Draw a hint                                                      |
//+------------------------------------------------------------------+
void CHintMoveDown::DrawHint(const int shift)
  {
   int w=this.Width();
   int h=this.Height();
   int middle=int(w*0.5);
   this.DrawRectangleFill(0,0,w-1,1,this.HintColor(),255);
   this.DrawTriangleFill(middle,shift+8,middle-3,shift+8-3,middle+3,shift+8-3,this.HintColor(),255);
   this.DrawLine(middle,2,middle,shift+8-4,this.HintColor(),255);
  }
//+------------------------------------------------------------------+
//| 'The cursor is inside the active area,                           |
//| no mouse buttons are clicked' event handler                      |
//+------------------------------------------------------------------+
void CHintMoveDown::MouseActiveAreaNotPressedHandler(const int id,const long& lparam,const double& dparam,const string& sparam)
  {
//--- Set the flag for not displaying the object and hide it
   this.SetDisplayed(false);
   this.Hide();
//--- Get the pointer to the base object
   CWinFormBase *base=this.GetBase();
   if(base==NULL)
      return;
//--- From the base object, get the pointers to the "up, left and right shift" hint objects,
//--- set the object non-display flag for them and hide the controls
   CWinFormBase *hint_mu=base.GetElementByType(GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_UP,0);
   if(hint_mu!=NULL)
     {
      hint_mu.SetDisplayed(false);
      hint_mu.Hide();
     }
   CWinFormBase *hint_ml=base.GetElementByType(GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_LEFT,0);
   if(hint_ml!=NULL)
     {
      hint_ml.SetDisplayed(false);
      hint_ml.Hide();
     }
   CWinFormBase *hint_mr=base.GetElementByType(GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_RIGHT,0);
   if(hint_mr!=NULL)
     {
      hint_mr.SetDisplayed(false);
      hint_mr.Hide();
     }
  }
//+------------------------------------------------------------------+

左矢印のオブジェクトは右矢印のオブジェクトと一緒に、上矢印のオブジェクトは下矢印のオブジェクトと一緒に、というようにです。ツールチップの描画メソッドでオフセットを指定すると、あるオブジェクトでは矢印が減少し、逆のオブジェクトでは増加するのはこのためです。このように、ペアで動作する2つのオブジェクトの矢印を相互に変位させたアニメーションツールチップを簡単に作成することができます。左のオブジェクトの矢印が減少すれば、右のオブジェクトの矢印は増加します。ヒント描画メソッドに同じシフト量を渡してループさせることで、左右の矢印を一定にシフトさせ、ヒントをアニメーション化させることができます。


すべてのコンテナオブジェクトクラスにおいて、これらの新しいオブジェクトを作成する機能を追加する必要があります。

基本コンテナオブジェクトクラスのファイル(\MQL5\Include\DoEasy\Objects\Graph\WForms\Containers\Container.mqh)にある、追加されたオブジェクトのパラメータを設定するメソッドで、メインオブジェクトと基本オブジェクトを設定する文字列を削除します。

//+------------------------------------------------------------------+
//| Set parameters for the attached object                           |
//+------------------------------------------------------------------+
void CContainer::SetObjParams(CWinFormBase *obj,const color colour)
  {
   obj.SetMain(this.IsMain() ? this.GetObject() : this.GetMain());
   obj.SetBase(this.GetObject());
//--- Set the text color of the object to be the same as that of the base container
   obj.SetForeColor(this.ForeColor(),true);

最後に、新規作成するオブジェクトの最小限のパラメータを設定するようにします。

//---...
//---...
      //--- For the "ArrowButton" WinForms object
      case GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON         :
      case GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_UP      :
      case GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_DOWN    :
      case GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_LEFT    :
      case GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_RIGHT   :
        obj.SetBorderColor(CLR_DEF_CONTROL_TAB_HEAD_BORDER_COLOR,true);
        obj.SetBorderStyle(FRAME_STYLE_SIMPLE);
        break;
      //--- For "Hint" WinForms object
      case GRAPH_ELEMENT_TYPE_WF_HINT_BASE            :
        obj.SetBackgroundColor(CLR_CANV_NULL,true);
        obj.SetBorderColor(CLR_CANV_NULL,true);
        obj.SetForeColor(CLR_CANV_NULL,true);
        obj.SetOpacity(0,false);
        obj.SetBorderStyle(FRAME_STYLE_NONE);
        break;
      //--- For "HintMoveLeft", "HintMoveRight", "HintMoveUp" and "HintMoveDown" WinForms object 
      case GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_LEFT       :
      case GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_RIGHT      :
      case GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_UP         :
      case GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_DOWN       :
        obj.SetBackgroundColor(CLR_CANV_NULL,true);
        obj.SetBorderColor(CLR_CANV_NULL,true);
        obj.SetForeColor(CLR_DEF_CONTROL_HINT_FORE_COLOR,true);
        obj.SetOpacity(0,false);
        obj.SetBorderStyle(FRAME_STYLE_NONE);
        break;
      default:
        break;
     }
   obj.Crop();
  }
//+------------------------------------------------------------------+


パネルオブジェクトクラスのファイル(\MQL5\Include\DoEasy\Objects\Graph\WForms\Containers\Panel.mqh)にあるincludedfilesのリストに、新規作成されたクラスのファイルを追加します

//+------------------------------------------------------------------+
//|                                                        Panel.mqh |
//|                                  Copyright 2022, MetaQuotes Ltd. |
//|                             https://mql5.com/ja/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2022, MetaQuotes Ltd."
#property link      "https://mql5.com/ja/users/artmedia70"
#property version   "1.00"
#property strict    // Necessary for mql4
//+------------------------------------------------------------------+
//| Include files                                                    |
//+------------------------------------------------------------------+
#include "Container.mqh"
#include "..\Helpers\TabField.mqh"
#include "..\Helpers\ArrowUpButton.mqh"
#include "..\Helpers\ArrowDownButton.mqh"
#include "..\Helpers\ArrowLeftButton.mqh"
#include "..\Helpers\ArrowRightButton.mqh"
#include "..\Helpers\ArrowUpDownBox.mqh"
#include "..\Helpers\ArrowLeftRightBox.mqh"
#include "..\Helpers\HintMoveLeft.mqh"
#include "..\Helpers\HintMoveRight.mqh"
#include "..\Helpers\HintMoveUp.mqh"
#include "..\Helpers\HintMoveDown.mqh"
#include "GroupBox.mqh"
#include "TabControl.mqh"
#include "SplitContainer.mqh"
#include "..\..\WForms\Common Controls\ListBox.mqh"
#include "..\..\WForms\Common Controls\CheckedListBox.mqh"
#include "..\..\WForms\Common Controls\ButtonListBox.mqh"
//+------------------------------------------------------------------+

これで、これらのオブジェクトは、すべてのライブラリコンテナオブジェクトで作成できるようになります。

WinFormsオブジェクトのすべてのクラスのコンストラクタは、すでにメインオブジェクトと基本オブジェクトを指定するように変更されており、ここではこれらの変更をさらに検討しないことに留意してください。

グラフィカルオブジェクトを新規作成するメソッドに、新しいライブラリオブジェクトを実装するために上記で作成した文字列を追加します

//+------------------------------------------------------------------+
//| Create a new graphical object                                    |
//+------------------------------------------------------------------+
CGCnvElement *CPanel::CreateNewGObject(const ENUM_GRAPH_ELEMENT_TYPE type,
                                       const int obj_num,
                                       const string descript,
                                       const int x,
                                       const int y,
                                       const int w,
                                       const int h,
                                       const color colour,
                                       const uchar opacity,
                                       const bool movable,
                                       const bool activity)
  {
   CGCnvElement *element=NULL;
   switch(type)
     {
      case GRAPH_ELEMENT_TYPE_ELEMENT                 : element=new CGCnvElement(type,this.GetMain(),this.GetObject(),this.ID(),obj_num,this.ChartID(),this.SubWindow(),descript,x,y,w,h,colour,opacity,movable,activity); break;
      case GRAPH_ELEMENT_TYPE_FORM                    : element=new CForm(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h);               break;
      case GRAPH_ELEMENT_TYPE_WF_CONTAINER            : element=new CContainer(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h);          break;
      case GRAPH_ELEMENT_TYPE_WF_GROUPBOX             : element=new CGroupBox(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h);           break;
      case GRAPH_ELEMENT_TYPE_WF_PANEL                : element=new CPanel(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h);              break;
      case GRAPH_ELEMENT_TYPE_WF_LABEL                : element=new CLabel(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h);              break;
      case GRAPH_ELEMENT_TYPE_WF_CHECKBOX             : element=new CCheckBox(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h);           break;
      case GRAPH_ELEMENT_TYPE_WF_RADIOBUTTON          : element=new CRadioButton(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h);        break;
      case GRAPH_ELEMENT_TYPE_WF_BUTTON               : element=new CButton(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h);             break;
      case GRAPH_ELEMENT_TYPE_WF_LIST_BOX             : element=new CListBox(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h);            break;
      case GRAPH_ELEMENT_TYPE_WF_LIST_BOX_ITEM        : element=new CListBoxItem(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h);        break;
      case GRAPH_ELEMENT_TYPE_WF_CHECKED_LIST_BOX     : element=new CCheckedListBox(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h);     break;
      case GRAPH_ELEMENT_TYPE_WF_BUTTON_LIST_BOX      : element=new CButtonListBox(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h);      break;
      case GRAPH_ELEMENT_TYPE_WF_TAB_HEADER           : element=new CTabHeader(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h);          break;
      case GRAPH_ELEMENT_TYPE_WF_TAB_FIELD            : element=new CTabField(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h);           break;
      case GRAPH_ELEMENT_TYPE_WF_TAB_CONTROL          : element=new CTabControl(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h);         break;
      case GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON         : element=new CArrowButton(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h);        break;
      case GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_UP      : element=new CArrowUpButton(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h);      break;
      case GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_DOWN    : element=new CArrowDownButton(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h);    break;
      case GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_LEFT    : element=new CArrowLeftButton(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h);    break;
      case GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_RIGHT   : element=new CArrowRightButton(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h);   break;
      case GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTONS_UD_BOX : element=new CArrowUpDownBox(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h);     break;
      case GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTONS_LR_BOX : element=new CArrowLeftRightBox(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h);  break;
      case GRAPH_ELEMENT_TYPE_WF_SPLIT_CONTAINER      : element=new CSplitContainer(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h);     break;
      case GRAPH_ELEMENT_TYPE_WF_SPLITTER             : element=new CSplitter(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h);           break;
      case GRAPH_ELEMENT_TYPE_WF_HINT_BASE            : element=new CHintBase(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h);           break;
      case GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_LEFT       : element=new CHintMoveLeft(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h);       break;
      case GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_RIGHT      : element=new CHintMoveRight(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h);      break;
      case GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_UP         : element=new CHintMoveUp(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h);         break;
      case GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_DOWN       : element=new CHintMoveDown(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h);       break;
      default  : break;
     }
   if(element==NULL)
      ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_FAILED_CREATE_ELM_OBJ),this.TypeElementDescription(type));
   return element;
  }
//+------------------------------------------------------------------+

このメソッドに渡されたオブジェクトの種類によって、対応するクラスの新しいオブジェクトが作成され、そのコンストラクタがメインオブジェクトと基本オブジェクトへのポインタを受け取るようになります。

アンダーレイオブジェクトを作成するメソッドでは、作成されたオブジェクトのメインオブジェクトと基本オブジェクトを指定します。

//+------------------------------------------------------------------+
//| Create the underlay object                                       |
//+------------------------------------------------------------------+
bool CPanel::CreateUnderlayObj(void)
  {
   this.m_underlay=new CGCnvElement(GRAPH_ELEMENT_TYPE_WF_UNDERLAY,this.GetMain(),this.GetObject(),this.ID(),this.Number(),
                                    this.ChartID(),this.SubWindow(),this.NameObj()+"Underlay",
                                    this.CoordXWorkspace(),this.CoordYWorkspace(),this.WidthWorkspace(),this.HeightWorkspace(),
                                    CLR_CANV_NULL,0,false,false);
   if(m_underlay==NULL)
     {
      CMessage::ToLog(DFUN,MSG_PANEL_OBJECT_ERR_FAILED_CREATE_UNDERLAY_OBJ);
      return false;
     }
   if(!this.m_list_tmp.Add(this.m_underlay))
     {
      CMessage::ToLog(DFUN,MSG_LIB_SYS_FAILED_OBJ_ADD_TO_LIST);
      delete this.m_underlay;
      return false;
     }
   this.SetUnderlayParams();
   return true;
  }
//+------------------------------------------------------------------+


GroupBoxコントロールクラスの\MQL5\Include\DoEasy\Objects\Graph\WForms\Containers\GroupBox.mqhファイルにある、グラフィカルオブジェクトを新規作成するメソッドで、上記のパネルオブジェクトのクラスと同じ改善を追加します。

//+------------------------------------------------------------------+
//| Create a new graphical object                                    |
//+------------------------------------------------------------------+
CGCnvElement *CGroupBox::CreateNewGObject(const ENUM_GRAPH_ELEMENT_TYPE type,
                                          const int obj_num,
                                          const string descript,
                                          const int x,
                                          const int y,
                                          const int w,
                                          const int h,
                                          const color colour,
                                          const uchar opacity,
                                          const bool movable,
                                          const bool activity)
  {
   CGCnvElement *element=NULL;
   switch(type)
     {
      case GRAPH_ELEMENT_TYPE_ELEMENT                 : element=new CGCnvElement(type,this.GetMain(),this.GetObject(),this.ID(),obj_num,this.ChartID(),this.SubWindow(),descript,x,y,w,h,colour,opacity,movable,activity); break;
      case GRAPH_ELEMENT_TYPE_FORM                    : element=new CForm(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h);               break;
      case GRAPH_ELEMENT_TYPE_WF_CONTAINER            : element=new CContainer(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h);          break;
      case GRAPH_ELEMENT_TYPE_WF_GROUPBOX             : element=new CGroupBox(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h);           break;
      case GRAPH_ELEMENT_TYPE_WF_PANEL                : element=new CPanel(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h);              break;
      case GRAPH_ELEMENT_TYPE_WF_LABEL                : element=new CLabel(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h);              break;
      case GRAPH_ELEMENT_TYPE_WF_CHECKBOX             : element=new CCheckBox(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h);           break;
      case GRAPH_ELEMENT_TYPE_WF_RADIOBUTTON          : element=new CRadioButton(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h);        break;
      case GRAPH_ELEMENT_TYPE_WF_BUTTON               : element=new CButton(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h);             break;
      case GRAPH_ELEMENT_TYPE_WF_LIST_BOX             : element=new CListBox(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h);            break;
      case GRAPH_ELEMENT_TYPE_WF_LIST_BOX_ITEM        : element=new CListBoxItem(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h);        break;
      case GRAPH_ELEMENT_TYPE_WF_CHECKED_LIST_BOX     : element=new CCheckedListBox(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h);     break;
      case GRAPH_ELEMENT_TYPE_WF_BUTTON_LIST_BOX      : element=new CButtonListBox(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h);      break;
      case GRAPH_ELEMENT_TYPE_WF_TAB_HEADER           : element=new CTabHeader(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h);          break;
      case GRAPH_ELEMENT_TYPE_WF_TAB_FIELD            : element=new CTabField(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h);           break;
      case GRAPH_ELEMENT_TYPE_WF_TAB_CONTROL          : element=new CTabControl(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h);         break;
      case GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON         : element=new CArrowButton(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h);        break;
      case GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_UP      : element=new CArrowUpButton(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h);      break;
      case GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_DOWN    : element=new CArrowDownButton(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h);    break;
      case GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_LEFT    : element=new CArrowLeftButton(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h);    break;
      case GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_RIGHT   : element=new CArrowRightButton(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h);   break;
      case GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTONS_UD_BOX : element=new CArrowUpDownBox(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h);     break;
      case GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTONS_LR_BOX : element=new CArrowLeftRightBox(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h);  break;
      case GRAPH_ELEMENT_TYPE_WF_SPLIT_CONTAINER      : element=new CSplitContainer(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h);     break;
      case GRAPH_ELEMENT_TYPE_WF_SPLITTER             : element=new CSplitter(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h);           break;
      case GRAPH_ELEMENT_TYPE_WF_HINT_BASE            : element=new CHintBase(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h);           break;
      case GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_LEFT       : element=new CHintMoveLeft(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h);       break;
      case GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_RIGHT      : element=new CHintMoveRight(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h);      break;
      case GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_UP         : element=new CHintMoveUp(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h);         break;
      case GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_DOWN       : element=new CHintMoveDown(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h);       break;
      default  : break;
     }
   if(element==NULL)
      ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_FAILED_CREATE_ELM_OBJ),this.TypeElementDescription(type));
   return element;
  }
//+------------------------------------------------------------------+

このメソッドと同じ改良が、TabControl.mqhTabField.mqhSplitContainerPanel.mqhファイル内の他のすべてのクラスのコンテナオブジェクトに施されています。これらのメソッドをさらに洗練させることについては、ここでは検討しません。


TabControlクラスのファイル(\MQL5\Include\DoEasy\Objects\Graph\WForms\Containers\TabControl.mqh)にあるCreateTabPages()メソッドで、指定された数のタブを作成し、タブヘッダーオブジェクトのメインオブジェクトと基本オブジェクトをインストールするすべての文字列を削除します

      header.SetMain(this.IsMain() ? this.GetObject() : this.GetMain());
      header.SetBase(this.GetObject());

タブフィールドオブジェクト用

      field.SetMain(this.IsMain() ? this.GetObject() : this.GetMain());
      field.SetBase(this.GetObject());

右左上下ボタンオブジェクト用

//--- Create the left-right button object
   this.CreateNewElement(GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTONS_LR_BOX,this.Width()-32,0,15,15,clrNONE,255,this.Active(),false);
//--- Get the pointer to a newly created object
   CArrowLeftRightBox *box_lr=this.GetArrLeftRightBox();
   if(box_lr!=NULL)
     {
      this.SetVisibleLeftRightBox(false);
      this.SetSizeLeftRightBox(box_lr.Width());
      box_lr.SetMain(this.IsMain() ? this.GetObject() : this.GetMain());
      box_lr.SetBase(this.GetObject());
      box_lr.SetID(this.GetMaxIDAll());
      box_lr.SetBorderStyle(FRAME_STYLE_NONE);
      box_lr.SetBackgroundColor(CLR_CANV_NULL,true);
      box_lr.SetOpacity(0);
      box_lr.Hide();
      CArrowLeftButton *lb=box_lr.GetArrowLeftButton();
      if(lb!=NULL)
        {
         lb.SetMain(this.IsMain() ? this.GetObject() : this.GetMain());
         lb.SetBase(box_lr);
         lb.SetID(this.GetMaxIDAll());
        }
      CArrowRightButton *rb=box_lr.GetArrowRightButton();
      if(rb!=NULL)
        {
         rb.SetMain(this.IsMain() ? this.GetObject() : this.GetMain());
         rb.SetBase(box_lr);
         rb.SetID(this.GetMaxIDAll());
        }
     }
//--- Create the up-down button object
   this.CreateNewElement(GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTONS_UD_BOX,0,this.Height()-32,15,15,clrNONE,255,this.Active(),false);
//--- Get the pointer to a newly created object
   CArrowUpDownBox *box_ud=this.GetArrUpDownBox();
   if(box_ud!=NULL)
     {
      this.SetVisibleUpDownBox(false);
      this.SetSizeUpDownBox(box_ud.Height());
      box_ud.SetMain(this.IsMain() ? this.GetObject() : this.GetMain());
      box_ud.SetBase(this.GetObject());
      box_ud.SetID(this.GetMaxIDAll());
      box_ud.SetBorderStyle(FRAME_STYLE_NONE);
      box_ud.SetBackgroundColor(CLR_CANV_NULL,true);
      box_ud.SetOpacity(0);
      box_ud.Hide();
      CArrowDownButton *db=box_ud.GetArrowDownButton();
      if(db!=NULL)
        {
         db.SetMain(this.IsMain() ? this.GetObject() : this.GetMain());
         db.SetBase(box_ud);
         db.SetID(this.GetMaxIDAll());
        }
      CArrowUpButton *ub=box_ud.GetArrowUpButton();
      if(ub!=NULL)
        {
         ub.SetMain(this.IsMain() ? this.GetObject() : this.GetMain());
         ub.SetBase(box_ud);
         ub.SetID(this.GetMaxIDAll());
        }
     }
//--- Arrange all titles in accordance with the specified display modes and select the specified tab
   this.ArrangeTabHeaders();
   this.Select(selected_page,true);
   return true;
  }
//+------------------------------------------------------------------+


\MQL5\Include\DoEasy\Objects\Graph\WForms\Containers\SplitContainer.mqhでSplitContainerコントロールクラスを改良してみましょう。

クラスのpublicセクションに、ヒントオブジェクトへのポインタを返すメソッドを記述します

//--- Return the pointer to the separator
   CSplitter        *GetSplitter(void)                         { return this.GetElementByType(GRAPH_ELEMENT_TYPE_WF_SPLITTER,0);                      }
//--- Return a pointer to the (1) "Left shift", (1) "Right shift", (1) "Up shift" and (1) "Down shift" hint objects
   CHintMoveLeft    *GetHintMoveLeft(void)                     { return this.GetElementByType(GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_LEFT,0);                }
   CHintMoveRight   *GetHintMoveRight(void)                    { return this.GetElementByType(GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_RIGHT,0);               }
   CHintMoveUp      *GetHintMoveUp(void)                       { return this.GetElementByType(GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_UP,0);                  }
   CHintMoveDown    *GetHintMoveDown(void)                     { return this.GetElementByType(GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_DOWN,0);                }

//--- (1) set and (2) return the minimum possible size of the panel 1 and 2


パネルを作成するメソッドで、作成されたパネルにメインオブジェクトと基本オブジェクトを設定し、区切りオブジェクトに設定するループを削除します。

         return;
      for(int i=0;i<2;i++)
        {
         CSplitContainerPanel *panel=this.GetPanel(i);
         if(panel==NULL)
            continue;
         panel.SetMain(this.IsMain() ? this.GetObject() : this.GetMain());
         panel.SetBase(this.GetObject());
        }
      //---
      if(!this.CreateNewElement(GRAPH_ELEMENT_TYPE_WF_SPLITTER,this.m_splitter_x,this.m_splitter_y,this.m_splitter_w,this.m_splitter_h,clrNONE,255,true,false))
         return;
      CSplitter *splitter=this.GetSplitter();
      if(splitter!=NULL)
        {
         splitter.SetMain(this.IsMain() ? this.GetObject() : this.GetMain());
         splitter.SetBase(this.GetObject());
         splitter.SetMovable(true);
         splitter.SetDisplayed(false);
         splitter.Hide();
        }


また、4つのヒントオブジェクトを作成し、そのパラメータをメソッドに設定するようにします。

//+------------------------------------------------------------------+
//| Create the panels                                                |
//+------------------------------------------------------------------+
void CSplitContainer::CreatePanels(void)
  {
   this.m_list_elements.Clear();
   if(this.SetsPanelParams())
     {
      //--- Create two panels
      if(!this.CreateNewElement(GRAPH_ELEMENT_TYPE_WF_SPLIT_CONTAINER_PANEL,this.m_panel1_x,this.m_panel1_y,this.m_panel1_w,this.m_panel1_h,clrNONE,255,true,false))
         return;
      if(!this.CreateNewElement(GRAPH_ELEMENT_TYPE_WF_SPLIT_CONTAINER_PANEL,this.m_panel2_x,this.m_panel2_y,this.m_panel2_w,this.m_panel2_h,clrNONE,255,true,false))
         return;
      //--- Create a separator object
      if(!this.CreateNewElement(GRAPH_ELEMENT_TYPE_WF_SPLITTER,this.m_splitter_x,this.m_splitter_y,this.m_splitter_w,this.m_splitter_h,clrNONE,255,true,false))
         return;
      CSplitter *splitter=this.GetSplitter();
      if(splitter!=NULL)
        {
         splitter.SetProperty(CANV_ELEMENT_PROP_SPLIT_CONTAINER_SPLITTER_ORIENTATION,this.SplitterOrientation());
         splitter.SetMovable(true);
         splitter.SetDisplayed(false);
         splitter.Hide();
        }
      //--- Create the HintMoveLeft object
      if(!this.CreateNewElement(GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_LEFT,this.m_splitter_x-DEF_HINT_ICON_SIZE,this.m_splitter_y,DEF_HINT_ICON_SIZE,DEF_HINT_ICON_SIZE,clrNONE,255,true,false))
         return;
      CHintMoveLeft *hint_ml=this.GetHintMoveLeft();
      if(hint_ml!=NULL)
        {
         hint_ml.SetMovable(false);
         hint_ml.SetDisplayed(false);
         hint_ml.Hide();
        }
      //--- Create the HintMoveRight object
      if(!this.CreateNewElement(GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_RIGHT,this.m_splitter_x+this.m_splitter_w,this.m_splitter_y,DEF_HINT_ICON_SIZE,DEF_HINT_ICON_SIZE,clrNONE,255,true,false))
         return;
      CHintMoveRight *hint_mr=this.GetHintMoveRight();
      if(hint_mr!=NULL)
        {
         hint_mr.SetMovable(false);
         hint_mr.SetDisplayed(false);
         hint_mr.Hide();
        }
      //--- Create the HintMoveUp object
      if(!this.CreateNewElement(GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_UP,this.m_splitter_x,this.m_splitter_y-DEF_HINT_ICON_SIZE,DEF_HINT_ICON_SIZE,DEF_HINT_ICON_SIZE,clrNONE,255,true,false))
         return;
      CHintMoveUp *hint_mu=this.GetHintMoveUp();
      if(hint_mu!=NULL)
        {
         hint_mu.SetMovable(false);
         hint_mu.SetDisplayed(false);
         hint_mu.Hide();
        }
      //--- Create the HintMoveDown object
      if(!this.CreateNewElement(GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_DOWN,this.m_splitter_x,this.m_splitter_y+this.m_splitter_h,DEF_HINT_ICON_SIZE,DEF_HINT_ICON_SIZE,clrNONE,255,true,false))
         return;
      CHintMoveDown *hint_md=this.GetHintMoveDown();
      if(hint_md!=NULL)
        {
         hint_md.SetMovable(false);
         hint_md.SetDisplayed(false);
         hint_md.Hide();
        }
     }
  }
//+------------------------------------------------------------------+

ヒントオブジェクトが作成されている場合、そのオブジェクトに対するマウス操作を無効化し、不可視フラグを設定し、作成された要素を非表示にします。


区切りの位置を設定するメソッドを作成したら、区切りオブジェクトのプロパティにメソッドに渡されたを追加して、区切りオブジェクトから直接オブジェクトの向き(垂直/水平)を検出できるようにします。

//+------------------------------------------------------------------+
//| set the separator location                                       |
//+------------------------------------------------------------------+
void CSplitContainer::SetSplitterOrientation(const ENUM_CANV_ELEMENT_SPLITTER_ORIENTATION value,const bool only_prop)
  {
   this.SetProperty(CANV_ELEMENT_PROP_SPLIT_CONTAINER_SPLITTER_ORIENTATION,value);
//--- If there are no panels or separator, leave
   CSplitContainerPanel *p1=this.GetPanel1();
   CSplitContainerPanel *p2=this.GetPanel2();
   CSplitter *sp=this.GetSplitter();
   if(p1==NULL || p2==NULL || sp==NULL)
      return;
//--- Set the orientation property for the separator object
   sp.SetProperty(CANV_ELEMENT_PROP_SPLIT_CONTAINER_SPLITTER_ORIENTATION,value);
//--- If only setting the property, leave
   if(only_prop)
      return;
//--- Set the parameters of the panels and the separator
   this.SetsPanelParams();
//--- If panel 1 is resized successfully
   if(p1.Resize(this.m_panel1_w,this.m_panel1_h,true))
     {
      //--- If panel 2 coordinates are changed to new ones
      if(p2.Move(this.CoordX()+this.m_panel2_x,this.CoordY()+this.m_panel2_y,true))
        {
         //--- if panel 2 has been successfully resized,
         if(p2.Resize(this.m_panel2_w,this.m_panel2_h,true))
           {
            //--- set new relative coordinates of panel 2
            p2.SetCoordXRelative(p2.CoordX()-this.CoordX());
            p2.SetCoordYRelative(p2.CoordY()-this.CoordY());
           }
        }
      //--- If the size of the separator object has been successfully changed, 
      //--- set new values of separator coordinates
      if(sp.Resize(this.m_splitter_w,this.m_splitter_h,false))
         this.SetSplitterDistance(this.SplitterDistance(),true);
     }
  }
//+------------------------------------------------------------------+

これで、このクラスの外部からデリミタへのポインタを受け取ったとき、常にその位置を知ることができ、将来的に便利なものになります。


区切りの移動を処理するイベントハンドラで、利用可能なすべての可視ヒントオブジェクトを非表示にします。

//+------------------------------------------------------------------+
//| Event handler                                                    |
//+------------------------------------------------------------------+
void CSplitContainer::OnChartEvent(const int id,const long &lparam,const double &dparam,const string &sparam)
  {
//--- Adjust subwindow Y shift
   CGCnvElement::OnChartEvent(id,lparam,dparam,sparam);
//--- If the event ID is moving the separator
   if(id==WF_CONTROL_EVENT_MOVING)
     {
      //--- Get the pointer to the separator object
      CSplitter *splitter=this.GetSplitter();
      if(splitter==NULL || this.SplitterFixed())
         return;
      //--- Get the pointers to hint objects
      CHintMoveLeft *hint_ml=this.GetHintMoveLeft();
      CHintMoveRight *hint_mr=this.GetHintMoveRight();
      CHintMoveUp *hint_mu=this.GetHintMoveUp();
      CHintMoveDown *hint_md=this.GetHintMoveDown();
      if(hint_ml==NULL || hint_mr==NULL || hint_mu==NULL || hint_md==NULL)
         return;
      
      //--- Disable the display of hints and hide them
      hint_ml.SetDisplayed(false);
      hint_ml.Hide();
      hint_mr.SetDisplayed(false);
      hint_mr.Hide();
      hint_mu.SetDisplayed(false);
      hint_mu.Hide();
      hint_md.SetDisplayed(false);
      hint_md.Hide();
         
      //--- Declare the variables for separator coordinates
      int x=(int)lparam;
      int y=(int)dparam;
      //--- Depending on the separator direction,
      switch(this.SplitterOrientation())
        {
         //--- vertical position
         case CANV_ELEMENT_SPLITTER_ORIENTATION_VERTICAL :
           //--- Set the Y coordinate equal to the Y coordinate of the control element
           y=this.CoordY();
           //--- Adjust the X coordinate so that the separator does not go beyond the control element
           //--- taking into account the resulting minimum width of the panels
           if(x<this.CoordX()+this.Panel1MinSize())
              x=this.CoordX()+this.Panel1MinSize();
           if(x>this.CoordX()+this.Width()-this.Panel2MinSize()-this.SplitterWidth())
              x=this.CoordX()+this.Width()-this.Panel2MinSize()-this.SplitterWidth();
           break;
         //---CANV_ELEMENT_SPLITTER_ORIENTATION_HORISONTAL
         //--- horizontal position of the separator
         default:
           //--- Set the X coordinate equal to the X coordinate of the control element
           x=this.CoordX();
           //--- Adjust the Y coordinate so that the separator does not go beyond the control element
           //--- taking into account the resulting minimum height of the panels
           if(y<this.CoordY()+this.Panel1MinSize())
              y=this.CoordY()+this.Panel1MinSize();
           if(y>this.CoordY()+this.Height()-this.Panel2MinSize()-this.SplitterWidth())
              y=this.CoordY()+this.Height()-this.Panel2MinSize()-this.SplitterWidth();
           break;
        }
      //--- Draw an empty rectangle
      this.DrawRectangleEmpty();
      //--- If the separator is shifted by the calculated coordinates,
      if(splitter.Move(x,y,true))
        {
         //--- set the separator relative coordinates
         splitter.SetCoordXRelative(splitter.CoordX()-this.CoordX());
         splitter.SetCoordYRelative(splitter.CoordY()-this.CoordY());
         //--- Depending on the direction of the separator, set its new coordinates
         this.SetSplitterDistance(!this.SplitterOrientation() ? splitter.CoordX()-this.CoordX() : splitter.CoordY()-this.CoordY(),false);
        }
     }
  }
//+------------------------------------------------------------------+

このメソッドは、区切りオブジェクトの移動イベントを処理します。パネルのサイズと位置を調整する前に、すべての補助オブジェクトをここで非表示にします。そのため、区切り部分にカーソルを合わせると、区切りーの移動可能な方向に関するヒントが表示されます。マウスで区切りをキャプチャして動かし始めると、ヒントが非表示になるようになっています。

同様に、カーソルが区切り領域から外れた場合、ヒントを非表示にする必要があります。

「カーソルがアクティブ領域内にあり、マウスボタンがクリックされていない」イベントのハンドラで、点線の四角形を削除してヒントオブジェクトを非表示にするようにします。

//+------------------------------------------------------------------+
//| 'The cursor is inside the active area,                           |
//| no mouse buttons are clicked' event handler                      |
//+------------------------------------------------------------------+
void CSplitContainer::MouseActiveAreaNotPressedHandler(const int id,const long& lparam,const double& dparam,const string& sparam)
  {
//--- If the separator is non-movable, leave
   if(this.SplitterFixed())
      return;
//--- Draw an empty rectangle in the control area
   this.DrawRectangleEmpty();
//--- Get the pointer to the separator
   CSplitter *splitter=this.GetSplitter();
   if(splitter==NULL)
     {
      ::Print(DFUN,CMessage::Text(MSG_ELM_LIST_ERR_FAILED_GET_GRAPH_ELEMENT_OBJ),": ",this.TypeElementDescription(GRAPH_ELEMENT_TYPE_WF_SPLITTER));
      return;
     }
//--- If the separator is not displayed
   if(!splitter.Displayed())
     {
      //--- Enable the display of the separator, show and redraw it
      splitter.SetDisplayed(true);
      splitter.Show();
      splitter.Redraw(true);
     }
//--- Get the pointer to hint objects
   CHintMoveLeft *hint_ml=this.GetHintMoveLeft();
   CHintMoveRight *hint_mr=this.GetHintMoveRight();
   CHintMoveUp *hint_mu=this.GetHintMoveUp();
   CHintMoveDown *hint_md=this.GetHintMoveDown();
   if(hint_ml==NULL || hint_mr==NULL || hint_mu==NULL || hint_md==NULL)
      return;
   hint_ml.SetDisplayed(false);
   hint_ml.Hide();
   hint_mr.SetDisplayed(false);
   hint_mr.Hide();
   hint_mu.SetDisplayed(false);
   hint_mu.Hide();
   hint_md.SetDisplayed(false);
   hint_md.Hide();
  }
//+------------------------------------------------------------------+

カーソルが区切り領域(オブジェクトのコントロール領域にある)から外れると同時に、カーソルはオブジェクトのアクティブ領域に入ります。このイベントのハンドラは、まず区切り領域の輪郭を描く点線の矩形を削除し、次に表示されているヒントをすべて非表示にします。


カーソルが区切りのあるコントロール領域に入るとすぐに、区切りの向きによって左右または上下に移動する可能性があることを示すヒントが表示されるはずです。

「カーソルがコントロール領域内にあり、マウスボタンがクリックされていない」イベントのハンドラ内に、説明した機能を追加します。

//+------------------------------------------------------------------+
//| The cursor is inside the control area,                           |
//| no mouse buttons are clicked' event handler                      |
//+------------------------------------------------------------------+
void CSplitContainer::MouseControlAreaNotPressedHandler(const int id,const long &lparam,const double &dparam,const string &sparam)
  {
//--- If the separator is non-movable, leave
   if(this.SplitterFixed())
      return;
//--- Draw an empty rectangle in the control area
   this.DrawRectangleEmpty();
//--- Draw a dotted rectangle in the control area
   this.DrawRectangleDotted();
//--- Get the pointer to the separator
   CSplitter *splitter=this.GetSplitter();
   if(splitter==NULL)
     {
      ::Print(DFUN,CMessage::Text(MSG_ELM_LIST_ERR_FAILED_GET_GRAPH_ELEMENT_OBJ),": ",this.TypeElementDescription(GRAPH_ELEMENT_TYPE_WF_SPLITTER));
      return;
     }
//--- If the separator is not displayed
   if(!splitter.Displayed())
     {
      //--- Enable the display of the separator and show it
      splitter.SetDisplayed(true);
      splitter.Erase(true);
      splitter.Show();
     }
//--- Get the pointers to hint objects
   CHintMoveLeft *hint_ml=this.GetHintMoveLeft();
   CHintMoveRight *hint_mr=this.GetHintMoveRight();
   CHintMoveUp *hint_mu=this.GetHintMoveUp();
   CHintMoveDown *hint_md=this.GetHintMoveDown();
   if(hint_ml==NULL || hint_mr==NULL || hint_mu==NULL || hint_md==NULL)
      return;
//--- Get cursor coordinates
   int x=this.m_mouse.CoordX()-this.CoordX();
   int y=this.m_mouse.CoordY()-this.CoordY();
//--- Depending on the separator direction,
   switch(this.SplitterOrientation())
     {
      //--- vertical position
      case CANV_ELEMENT_SPLITTER_ORIENTATION_VERTICAL :
        //--- Set and adjust the coordinates of the "left shift" hint object
        x=this.CoordX()+this.m_splitter_x-hint_ml.Width();//-1;
        y+=this.CoordY()-hint_ml.Height()-1;
        if(y<this.CoordY()+this.m_splitter_y)
           y=this.CoordY()+this.m_splitter_y;
        //--- Shift the hint object to the calculated coordinates, set its visibility flag and display the object
        if(hint_ml.Move(x,y))
          {
           hint_ml.SetCoordXRelative(x-this.CoordX());
           hint_ml.SetCoordYRelative(y-this.CoordY());
           hint_ml.SetDisplayed(true);
           hint_ml.Show();
          }
        //--- Set and adjust the coordinates of the "right shift" hint object
        x=this.CoordX()+this.m_splitter_x+this.m_splitter_w;//+1;
        //--- Shift the hint object to the calculated coordinates, set its visibility flag and display the object
        if(hint_mr.Move(x,y))
          {
           hint_mr.SetCoordXRelative(x-this.CoordX());
           hint_mr.SetCoordYRelative(y-this.CoordY());
           hint_mr.SetDisplayed(true);
           hint_mr.Show();
          }
        break;
      //---CANV_ELEMENT_SPLITTER_ORIENTATION_HORISONTAL
      //--- horizontal position of the separator
      default:
        //--- Set and adjust the coordinates of the "up shift" hint object
        y=this.CoordY()+this.m_splitter_y-hint_mu.Height();//-1;
        x+=this.CoordX()-hint_mu.Width()-1;
        if(x<this.CoordX()+this.m_splitter_x)
           x=this.CoordX()+this.m_splitter_x;
        //--- Shift the hint object to the calculated coordinates, set its visibility flag and display the object
        if(hint_mu.Move(x,y))
          {
           hint_mu.SetCoordXRelative(x-this.CoordX());
           hint_mu.SetCoordYRelative(y-this.CoordY());
           hint_mu.SetDisplayed(true);
           hint_mu.Show();
          }
        //--- Set and adjust the coordinates of the "down shift" hint object
        y=this.CoordY()+this.m_splitter_y+this.m_splitter_h;//+1;
        //--- Shift the hint object to the calculated coordinates, set its visibility flag and display the object
        if(hint_md.Move(x,y))
          {
           hint_md.SetCoordXRelative(x-this.CoordX());
           hint_md.SetCoordYRelative(y-this.CoordY());
           hint_md.SetDisplayed(true);
           hint_md.Show();
          }
        break;
     }
  }
//+------------------------------------------------------------------+

追加されたコードブロックのロジックは、メソッド一覧でコメントされています。ヒントオブジェクトへのポインタを取得し、カーソルの上に位置するように移動し、ヒントオブジェクトを表示します。

最後のマウスイベントのハンドラで、最後のイベントがフォームの外側またはアクティブ領域内にあった場合ヒントオブジェクトを非表示にします

//+------------------------------------------------------------------+
//| Last mouse event handler                                         |
//+------------------------------------------------------------------+
void CSplitContainer::OnMouseEventPostProcessing(void)
  {
   if(!this.IsVisible() || !this.Enabled() || !this.Displayed())
      return;
   ENUM_MOUSE_FORM_STATE state=this.GetMouseState();
   switch(state)
     {
      //--- The cursor is outside the form, the mouse buttons are not clicked
      //--- The cursor is outside the form, any mouse button is clicked
      //--- The cursor is outside the form, the mouse wheel is being scrolled
      case MOUSE_FORM_STATE_OUTSIDE_FORM_NOT_PRESSED        :
      case MOUSE_FORM_STATE_OUTSIDE_FORM_PRESSED            :
      case MOUSE_FORM_STATE_OUTSIDE_FORM_WHEEL              :
      case MOUSE_FORM_STATE_NONE                            :
        if(this.MouseEventLast()==MOUSE_EVENT_INSIDE_ACTIVE_AREA_NOT_PRESSED  ||
           this.MouseEventLast()==MOUSE_EVENT_INSIDE_FORM_NOT_PRESSED         ||
           this.MouseEventLast()==MOUSE_EVENT_OUTSIDE_FORM_NOT_PRESSED        ||
           this.MouseEventLast()==MOUSE_EVENT_INSIDE_CONTROL_AREA_NOT_PRESSED ||
           this.MouseEventLast()==MOUSE_EVENT_INSIDE_CONTROL_AREA_PRESSED     ||
           this.MouseEventLast()==MOUSE_EVENT_INSIDE_CONTROL_AREA_WHEEL       ||
           this.MouseEventLast()==MOUSE_EVENT_NO_EVENT)
          {
            //--- Draw an empty rectangle in the control area
            this.DrawRectangleEmpty();
            //--- Get the pointer to the separator
            CSplitter *splitter=this.GetSplitter();
            if(splitter==NULL)
              {
               ::Print(DFUN,CMessage::Text(MSG_ELM_LIST_ERR_FAILED_GET_GRAPH_ELEMENT_OBJ),": ",this.TypeElementDescription(GRAPH_ELEMENT_TYPE_WF_SPLITTER));
               return;
              }
            splitter.SetDisplayed(false);
            splitter.Hide();
            //--- Get the pointers to hint objects
            CHintMoveLeft *hint_ml=this.GetHintMoveLeft();
            CHintMoveRight *hint_mr=this.GetHintMoveRight();
            CHintMoveUp *hint_mu=this.GetHintMoveUp();
            CHintMoveDown *hint_md=this.GetHintMoveDown();
            if(hint_ml==NULL || hint_mr==NULL || hint_mu==NULL || hint_md==NULL)
               return;
            //--- If the hide object is displayed, disable its display and hide it
            if(hint_ml.Displayed())
              {
               hint_ml.SetDisplayed(false);
               hint_ml.Hide();
              }
            if(hint_mr.Displayed())
              {
               hint_mr.SetDisplayed(false);
               hint_mr.Hide();
              }
            if(hint_mu.Displayed())
              {
               hint_mu.SetDisplayed(false);
               hint_mu.Hide();
              }
            if(hint_md.Displayed())
              {
               hint_md.SetDisplayed(false);
               hint_md.Hide();
              }
            //--- Set the current mouse state as the last one
            this.m_mouse_event_last=ENUM_MOUSE_EVENT(state+MOUSE_EVENT_NO_EVENT);
          }
        break;
      //--- The cursor is inside the form, the mouse buttons are not clicked
      //--- The cursor is inside the form, any mouse button is clicked
      //--- The cursor is inside the form, the mouse wheel is being scrolled
      //--- The cursor is inside the active area, the mouse buttons are not clicked
      //--- The cursor is inside the active area, any mouse button is clicked
      //--- The cursor is inside the active area, the mouse wheel is being scrolled
      //--- The cursor is inside the active area, left mouse button is released
      //--- The cursor is within the window scrolling area, the mouse buttons are not clicked
      //--- The cursor is within the window scrolling area, any mouse button is clicked
      //--- The cursor is within the window scrolling area, the mouse wheel is being scrolled
      //--- The cursor is within the window resizing area, the mouse buttons are not clicked
      //--- The cursor is within the window resizing area, the mouse button (any) is clicked
      //--- The cursor is within the window resizing area, the mouse wheel is being scrolled
      //--- The cursor is within the window resizing area, the mouse buttons are not clicked
      //--- The cursor is within the window resizing area, the mouse button (any) is clicked
      //--- The cursor is within the window separator area, the mouse wheel is being scrolled
      case MOUSE_FORM_STATE_INSIDE_FORM_NOT_PRESSED         :
      case MOUSE_FORM_STATE_INSIDE_FORM_PRESSED             :
      case MOUSE_FORM_STATE_INSIDE_FORM_WHEEL               :
      case MOUSE_FORM_STATE_INSIDE_ACTIVE_AREA_NOT_PRESSED  :
      case MOUSE_FORM_STATE_INSIDE_ACTIVE_AREA_PRESSED      :
      case MOUSE_FORM_STATE_INSIDE_ACTIVE_AREA_WHEEL        :
      case MOUSE_FORM_STATE_INSIDE_ACTIVE_AREA_RELEASED     :
      case MOUSE_FORM_STATE_INSIDE_SCROLL_AREA_NOT_PRESSED  :
      case MOUSE_FORM_STATE_INSIDE_SCROLL_AREA_PRESSED      :
      case MOUSE_FORM_STATE_INSIDE_SCROLL_AREA_WHEEL        :
      case MOUSE_FORM_STATE_INSIDE_RESIZE_AREA_NOT_PRESSED  :
      case MOUSE_FORM_STATE_INSIDE_RESIZE_AREA_PRESSED      :
      case MOUSE_FORM_STATE_INSIDE_RESIZE_AREA_WHEEL        :
      case MOUSE_FORM_STATE_INSIDE_CONTROL_AREA_NOT_PRESSED:
      case MOUSE_FORM_STATE_INSIDE_CONTROL_AREA_PRESSED    :
      case MOUSE_FORM_STATE_INSIDE_CONTROL_AREA_WHEEL      :
        break;
      //--- MOUSE_EVENT_NO_EVENT
      default:
        break;
     }
  }
//+------------------------------------------------------------------+


カーソルがSplitContainerコントロールのパネルに入ると同時に、区切りがあるコントロール領域を超えたことを示します。同時に、何らかの理由でCSplitContainerクラスのこのようなイベントのハンドラが起動されなかった場合、そのような状況はパネルオブジェクトのクラスで処理される必要があります。\MQL5\Include\DoEasy\Objects\Graph\WForms\Helpers\SplitContainerPanel.mqhファイルの「カーソルがアクティブ領域にあり、マウスボタンがクリックされていない」イベントのハンドラでヒントオブジェクトを非表示にするためのコードを追加します。

//+------------------------------------------------------------------+
//| 'The cursor is inside the active area,                           |
//| no mouse buttons are clicked' event handler                      |
//+------------------------------------------------------------------+
void CSplitContainerPanel::MouseActiveAreaNotPressedHandler(const int id,const long& lparam,const double& dparam,const string& sparam)
  {
//--- Get the pointer to the base object
   CSplitContainer *base=this.GetBase();
//--- If the base object is not received, or the separator is non-movable, leave
   if(base==NULL || base.SplitterFixed())
      return;
//--- Draw an empty rectangle in the base object control area
   base.DrawRectangleEmpty();
//--- Get the pointer to the separator object from the base object
   CSplitter *splitter=base.GetSplitter();
   if(splitter==NULL)
     {
      ::Print(DFUN,CMessage::Text(MSG_ELM_LIST_ERR_FAILED_GET_GRAPH_ELEMENT_OBJ),": ",this.TypeElementDescription(GRAPH_ELEMENT_TYPE_WF_SPLITTER));
      return;
     }
//--- If the separator is displayed
   if(splitter.Displayed())
     {
      //--- Disable the display of the separator and hide it
      splitter.SetDisplayed(false);
      splitter.Hide();
     }
//--- Get the pointer to the hint objects from the base object
   CHintMoveLeft *hint_ml=base.GetHintMoveLeft();
   CHintMoveRight *hint_mr=base.GetHintMoveRight();
   CHintMoveUp *hint_mu=base.GetHintMoveUp();
   CHintMoveDown *hint_md=base.GetHintMoveDown();
   if(hint_ml==NULL || hint_mr==NULL || hint_mu==NULL || hint_md==NULL)
      return;
//--- If the hide object is displayed, disable its display and hide it
   if(hint_ml.Displayed())
     {
      hint_ml.SetDisplayed(false);
      hint_ml.Hide();
     }
   if(hint_mr.Displayed())
     {
      hint_mr.SetDisplayed(false);
      hint_mr.Hide();
     }
   if(hint_mu.Displayed())
     {
      hint_mu.SetDisplayed(false);
      hint_mu.Hide();
     }
   if(hint_md.Displayed())
     {
      hint_md.SetDisplayed(false);
      hint_md.Hide();
     }
  }
//+------------------------------------------------------------------+

このメソッドのロジックは、上記のハンドラ内のロジックと同じです。


カーソルが区切りオブジェクトにあるとき、カーソルの後にヒントオブジェクトを移動させる必要があります。区切り上のカーソルの動きは、区切りオブジェクトのクラスで追跡することができます。そのためには、カーソルが区切りのアクティブ領域上に来たときのイベントのハンドラをクラスに追加し、ヒントオブジェクトへのポインタを取得して、カーソルの後に区切り領域だけに動きを限定して移動させる必要があります。

\MQL5\Include\DoEasy\Objects\Graph\WForms\Helpers\Splitter.mqhで、仮想イベントハンドラを宣言します

//--- Clear the element completely
   virtual void      Erase(const bool redraw=false) { CWinFormBase::Erase(redraw);  }
//--- 'The cursor is inside the active area, the mouse buttons are not clicked' event handler
   virtual void      MouseActiveAreaNotPressedHandler(const int id,const long& lparam,const double& dparam,const string& sparam);
//--- 'The cursor is inside the active area, a mouse button is clicked (any)' event handler
   virtual void      MouseActiveAreaPressedHandler(const int id,const long& lparam,const double& dparam,const string& sparam);
//--- 'The cursor is inside the active area, the left mouse button is clicked' event handler
   virtual void      MouseActiveAreaReleasedHandler(const int id,const long& lparam,const double& dparam,const string& sparam);
  };
//+------------------------------------------------------------------+


クラス本体の外側で実装しましょう。

//+------------------------------------------------------------------+
//| 'The cursor is inside the active area,                           |
//| no mouse buttons are clicked' event handler                      |
//+------------------------------------------------------------------+
void CSplitter::MouseActiveAreaNotPressedHandler(const int id,const long& lparam,const double& dparam,const string& sparam)
  {
//--- Get the pointer to the base object
   CWinFormBase *base=this.GetBase();
   if(base==NULL)
      return;
//--- Declare the pointers to hint objects
   CWinFormBase *hint_ml=NULL;
   CWinFormBase *hint_mr=NULL;
   CWinFormBase *hint_mu=NULL;
   CWinFormBase *hint_md=NULL;
//--- Get cursor coordinates
   int x=this.m_mouse.CoordX();
   int y=this.m_mouse.CoordY();
//--- Depending on the separator direction,
   switch((int)this.GetProperty(CANV_ELEMENT_PROP_SPLIT_CONTAINER_SPLITTER_ORIENTATION))
     {
      //--- vertical position
      case CANV_ELEMENT_SPLITTER_ORIENTATION_VERTICAL :
         //--- From the base object, get the pointer to the "left shift" and "right shift" hint objects
         hint_ml=base.GetElementByType(GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_LEFT,0);
         hint_mr=base.GetElementByType(GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_RIGHT,0);
         if(hint_ml==NULL || hint_mr==NULL)
            return;
         //--- Shift hints following the cursor
         hint_ml.Move(hint_ml.CoordX(),(y-hint_ml.Height()<this.CoordY() ? this.CoordY() : y-hint_ml.Height()));
         hint_mr.Move(hint_mr.CoordX(),(y-hint_mr.Height()<this.CoordY() ? this.CoordY() : y-hint_mr.Height()),true);
        break;
      //---CANV_ELEMENT_SPLITTER_ORIENTATION_HORISONTAL
      //--- horizontal position of the separator
      default:
         //--- From the base object, get the pointer to the "shift up" and "shift down" hint objects
         hint_mu=base.GetElementByType(GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_UP,0);
         hint_md=base.GetElementByType(GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_DOWN,0);
         if(hint_mu==NULL || hint_md==NULL)
            return;
         //--- Shift hints following the cursor
         hint_mu.Move((x-hint_mu.Width()<this.CoordX() ? this.CoordX() : x-hint_mu.Width()),hint_mu.CoordY());
         hint_md.Move((x-hint_md.Width()<this.CoordX() ? this.CoordX() : x-hint_md.Width()),hint_md.CoordY(),true);
        break;
     }
  }
//+------------------------------------------------------------------+

メソッドのロジックはコードのコメントで完全に説明されています。なお、ヒントオブジェクトの移動制限は、算出した座標をオブジェクトシフトメソッドに渡す際に、直接実装されます


グラフィック要素コレクションクラスのファイル(\MQL5\Include\DoEasy\Collections\GraphElementsCollection.mqh)で、すべてのWinFormsオブジェクト作成メソッドでメインオブジェクトと基本オブジェクトへのポインタを渡すように設定します。

//--- Create a graphical element object on canvas on a specified chart and subwindow
   int               CreateElement(const long chart_id,
                                   const int subwindow,
                                   const string descript,
                                   const int x,
                                   const int y,
                                   const int w,
                                   const int h,
                                   const color clr,
                                   const uchar opacity,
                                   const bool movable,
                                   const bool activity,
                                   const bool redraw=false)
                       {
                        int id=this.GetMaxID()+1;
                        CGCnvElement *obj=new CGCnvElement(GRAPH_ELEMENT_TYPE_ELEMENT,NULL,NULL,id,0,chart_id,subwindow,descript,x,y,w,h,clr,opacity,movable,activity,redraw);
                        ENUM_ADD_OBJ_RET_CODE res=this.AddOrGetCanvElmToCollection(obj,id);
                        if(res==ADD_OBJ_RET_CODE_ERROR)
                           return WRONG_VALUE;
                        if(res==ADD_OBJ_RET_CODE_EXIST)
                           obj.SetID(id);
                        obj.Erase(clr,opacity,redraw);
                        return obj.ID();
                       }

ここでは、NULLがメインオブジェクトと基本オブジェクトとして設定されています。なぜなら、このクラスから(他に結びついたものではなく)独立したオブジェクトを作成するからです。そのため、当初はメインで独立したオブジェクトとなります。

このようなメソッドはクラス内に多数存在しますが、変更点はすべて同じです。したがって、ここでそのすべてを説明する必要はありません。その中のポインタと後続のWinFormsオブジェクトの受け渡しは、上記のメソッドとは少し異なるので、フォームオブジェクトを作成するいくつかのメソッドのうちの1つだけを考えてみましょう。

//--- Create a graphical form object on canvas on a specified chart and subwindow
   int               CreateForm(const long chart_id,
                                const int subwindow,
                                const string descript,
                                const int x,
                                const int y,
                                const int w,
                                const int h,
                                const color clr,
                                const uchar opacity,
                                const bool movable,
                                const bool activity,
                                const bool shadow=false,
                                const bool redraw=false)
                       {
                        int id=this.GetMaxID()+1;
                        CForm *obj=new CForm(NULL,NULL,chart_id,subwindow,descript,x,y,w,h);
                        ENUM_ADD_OBJ_RET_CODE res=this.AddOrGetCanvElmToCollection(obj,id);
                        if(res==ADD_OBJ_RET_CODE_ERROR)
                           return WRONG_VALUE;
                        obj.SetID(id);
                        obj.SetActive(activity);
                        obj.SetMovable(movable);
                        obj.SetBackgroundColor(clr,true);
                        obj.SetBorderColor(clr,true);
                        obj.SetOpacity(opacity,false);
                        obj.SetShadow(shadow);
                        obj.DrawRectangle(0,0,obj.Width()-1,obj.Height()-1,obj.BorderColor(),obj.Opacity());
                        obj.Done();
                        obj.Erase(clr,opacity,redraw);
                        return obj.ID();
                       }

ここでは、メインオブジェクトと基本オブジェクトを指定する前に、オブジェクトの型は渡しません。これは作成されたクラスのコンストラクタに既に登録されています。

その他の変更はすべて同じで、クラスファイルですでにおこなわれています。結果をテストしてみましょう。


検証

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

EAに変更はありません。コンパイルしてチャート上で起動してみましょう。


実装された機能は正常に動作します。


次の段階

次の記事では、TabControlに取り組みます。

現在のライブラリバージョン、テストEA、およびMQL5のチャートイベントコントロール指標のすべてのファイルが、テストおよびダウンロードできるように以下に接続されています。

目次に戻る

連載のこれまでの記事

 
DoEasy.コントロール(第20部):SplitContainerWinFormsオブジェクト
DoEasy.コントロール(第21部):SplitContainerコントロール。パネル区切りー
DoEasy.コントロール(第22部):SplitContainer。作成したオブジェクトのプロパティを変更する
DoEasy.コントロール(第23部):TabControlとSplitContainerのWinFormsオブジェクトの改善

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

添付されたファイル |
MQL5.zip (4496.43 KB)
データサイエンスと機械学習(第08回)::簡単なMQL5でのK平均法 データサイエンスと機械学習(第08回)::簡単なMQL5でのK平均法
データサイエンティストやトレーダーにとってデータマイニングは非常に重要です。多くの場合、データは私たちが思っているほど単純ではありません。人間の目は、データセット内のささいな基本パターンと関係を理解できません。k平均法アルゴリズムがその助けになるかもしれません。調べてみましょう...
母集団最適化アルゴリズム:粒子群(PSO) 母集団最適化アルゴリズム:粒子群(PSO)
この記事では、一般的な粒子群最適化(PSO)アルゴリズムについて検討します。以前は、収束、収束率、安定性、スケーラビリティなどの最適化アルゴリズムの重要な特性について説明し、テストスタンドを開発し、最も単純なRNGアルゴリズムを検討しました。
適応型インジケーター 適応型インジケーター
この記事では、適応型インジケーターを作成するためのいくつかの可能なアプローチを検討します。適応型インジケーターは、入力信号と出力信号の値の間のフィードバックの存在によって特徴付けられます。このフィードバックにより、インジケーターは金融時系列値の最適な処理に個別に適応できるようになります。
DoEasy - コントロール(第23部):TabControlおよびSplitContainer WinFormsオブジェクトの改善 DoEasy - コントロール(第23部):TabControlおよびSplitContainer WinFormsオブジェクトの改善
今回は、WinFormsオブジェクトの作業領域の境界線に関連する新しいマウスイベントを追加し、TabControlとSplitContainerコントロールの機能に関するいくつかの欠点を修正することにします。