English Русский 中文 Español Deutsch Português
preview
DoEasy - コントロール(第3部):バインドされたコントロールの作成

DoEasy - コントロール(第3部):バインドされたコントロールの作成

MetaTrader 5 | 8 8月 2022, 14:52
128 0
Artyom Trishkin
Artyom Trishkin

内容


概念

本稿では、グラフィック要素にバインドされたグラフィックコントロールを開発するための機能を作成することを検討します。Panelコントロールがあるとします。要素自体は単に他のコントロールを格納できるコンテナです。パネルを移動すると、それにバインドされているすべてのコントロールオブジェクトも移動します。パネルは、GUI要素をグループ化して配置するための基本オブジェクトです。ターミナルにはビジュアルGUIコンストラクタがないため、そのような要素の構築はプログラマーの責任になります。このライブラリでは、必要なグラフィック要素の作成順序を指定するだけでパネルに配置できるため、このようなGUI要素を簡単に開発できます。さらに、プログラムで要素を作成してパネルに追加する機能もあります。

別の要素の中に要素を作成する方法のワークピースがすでにあるので、本稿では必要なメソッドの開発を続けます。これらのメソッドを使用すると、バインドされたグラフィック要素をパネルから直接新規作成し、それをGUIプログラムの独立した部分として処理できます。次に、パネルに作成および接続されたそのような各要素は、それ自体の中に他の要素を作成することもできます。このような機能を備えた最小単位はフォームクラスオブジェクトです。

さらに、影を持つことができるオブジェクトに適用するとまだいくつかのロジックエラーが発生するため、グラフィック要素の影オブジェクトを少しいじります。たとえば、影はオブジェクトに重ねて表示されてその上に影を投影するオブジェクトが配置される必要がある場合に、影がチャートの上部にのみ描画されます。


ライブラリクラスの改善

\MQL5\Include\DoEasy\Defines.mqhは、一部のライブラリオブジェクトプロパティのデフォルト値を指定するためのマクロ置換を備えています。

キャンバスパラメータブロックで、マクロ置換CLR_FORE_COLORCLR_DEF_FORE_COLORに名前変更し 、グラフィック要素オブジェクトの非透明性のデフォルト値影オブジェクトプロパティのその他のいくつかのデフォルト値を追加します。

//--- Canvas parameters
#define PAUSE_FOR_CANV_UPDATE          (16)                       // Canvas update frequency
#define CLR_CANV_NULL                  (0x00FFFFFF)               // Zero for the canvas with the alpha channel
#define CLR_DEF_FORE_COLOR             (C'0x2D,0x43,0x48')        // Default color for texts of objects on canvas
#define CLR_DEF_OPACITY                (200)                      // Default color non-transparency for canvas objects
#define CLR_DEF_SHADOW_COLOR           (C'0x6B,0x6B,0x6B')        // Default color for canvas object shadows
#define CLR_DEF_SHADOW_OPACITY         (127)                      // Default color non-transparency for canvas objects
#define DEF_SHADOW_BLUR                (4)                        // Default blur for canvas object shadows
#define DEF_FONT                       ("Calibri")                // Default font
#define DEF_FONT_SIZE                  (8)                        // Default font 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
//--- Graphical object parameters

これらの値は、グラフィック要素を作成するためのメソッドでライブラリによって使用されます。デフォルト値は作成後にいつでも変更できます。

定数は、グラフィック要素タイプの列挙に論理的に配置されていません。

//+------------------------------------------------------------------+
//| 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_ELEMENT,                        // Element
   GRAPH_ELEMENT_TYPE_SHADOW_OBJ,                     // Shadow object
   GRAPH_ELEMENT_TYPE_FORM,                           // Form
   GRAPH_ELEMENT_TYPE_WINDOW,                         // Window
   //--- WinForms
   GRAPH_ELEMENT_TYPE_PANEL,                          // Windows Forms Panel
  };
//+------------------------------------------------------------------+

影オブジェクト定数はグラフィック要素オブジェクト定数の後にあります。これは、一部のメソッドでオブジェクトタイプを制限するにはあまり実用的ではありません。たとえば、すべてのGUI要素オブジェクトを処理する必要がある場合、要素タイプ以上のオブジェクトのみを処理するように指定できます。この場合、影オブジェクトもメソッドによって処理されます。これを回避し、列挙定数値でオブジェクトグループを選択できるようにするには、GUI要素オブジェクトが継承階層に従って配置されるように指定された定数を交換します。

//+------------------------------------------------------------------+
//| 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_SHADOW_OBJ,                     // Shadow object
   GRAPH_ELEMENT_TYPE_ELEMENT,                        // Element
   GRAPH_ELEMENT_TYPE_FORM,                           // Form
   GRAPH_ELEMENT_TYPE_WINDOW,                         // Window
   //--- WinForms
   GRAPH_ELEMENT_TYPE_PANEL,                          // Windows Forms Panel
  };
//+------------------------------------------------------------------+

これで、ライブラリメソッドで処理する必要のあるオブジェクトをすばやく選択できるようになります。


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

//--- CForm
   MSG_FORM_OBJECT_TEXT_NO_SHADOW_OBJ_FIRST_CREATE_IT,// No shadow object. Create it using the CreateShadowObj() method
   MSG_FORM_OBJECT_ERR_FAILED_CREATE_SHADOW_OBJ,      // Failed to create new shadow object
   MSG_FORM_OBJECT_ERR_FAILED_CREATE_PC_OBJ,          // Failed to create new pixel copier object
   MSG_FORM_OBJECT_PC_OBJ_ALREADY_IN_LIST,            // Pixel copier object with ID already present in the list 
   MSG_FORM_OBJECT_PC_OBJ_NOT_EXIST_LIST,             // No pixel copier object with ID in the list 
   MSG_FORM_OBJECT_ERR_NOT_INTENDED,                  // The method is not meant for creating such an object: 

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

//--- CForm
   {"Отсутствует объект тени. Необходимо сначала его создать при помощи метода CreateShadowObj()","There is no shadow object. You must first create it using the CreateShadowObj () method"},
   {"Не удалось создать новый объект для тени","Failed to create new object for shadow"},
   {"Не удалось создать новый объект-копировщик пикселей","Failed to create new pixel copier object"},
   {"В списке уже есть объект-копировщик пикселей с идентификатором ","There is already a pixel copier object in the list with ID "},
   {"В списке нет объекта-копировщика пикселей с идентификатором ","No pixel copier object with ID "},
   {"Метод не предназначен для создания такого объекта: ","The method is not intended to create such an object: "},


すべてのGUI要素は異なるライブラリオブジェクトから作成されています。最小のものはグラフィック要素オブジェクトです。継承階層内の後続の各オブジェクトには、親オブジェクトには存在しない新しい機能が追加されます。したがって、親オブジェクトの一部のメソッドの機能は、子オブジェクトで完全に実装するには不十分です。このようなメソッドを仮想化し、子孫オブジェクトに必要な機能を追加する必要があります。

GUI要素で構成される各オブジェクトは、接続されている要素を「認識」している必要があります。これにより、たとえば、基本オブジェクトのプロパティをその配置に使用できます。これをおこなうには、要素オブジェクトクラスの基本オブジェクトへのポインタを導入します(すべてのGUI要素の親であるのはこのオブジェクトであるため、ポインタをその中に配置するのが論理的です)。

この段階で、グラフィカルインターフェイスを構成するすべてのオブジェクトはチャート画面の座標にバインドされ、それらの座標はチャートの左上隅から数えられます。ただし、あるオブジェクトを別のオブジェクトの内側に配置する必要がある場合は、座標系を基本オブジェクトに移動するのが論理的です。ここで、原点はチャートではなく基本グラフィック要素の左上隅になります。これをおこなうには、相対座標の概念を導入し、基本オブジェクト(オブジェクトが接続されているオブジェクト)を基準にしたオブジェクトの座標を返す新しいメソッドを追加しましょう。次に、オブジェクトを配置するために、チャートに沿って基本グラフィック要素を移動するときに常に新しい配置座標を計算するのではなく、基本オブジェクトの左上隅を基準にしたシフトを指定するだけです。

\MQL5\Include\DoEasy\Objects\Graph\GCnvElement.mqhのprotectedセクションで、グラフィック要素が接続され ている基本オブジェクトへのポインタを宣言 し、privateセクションで、基本オブジェクトに対する座標シフトを格納するための変数を宣言します。

//+------------------------------------------------------------------+
//| Class of the graphical element object                            |
//+------------------------------------------------------------------+
class CGCnvElement : public CGBaseObj
  {
protected:
   CGCnvElement     *m_element_base;                           // Pointer to the parent element
   CCanvas           m_canvas;                                 // CCanvas class object
   CPause            m_pause;                                  // Pause class object
   bool              m_shadow;                                 // Shadow presence
   color             m_chart_color_bg;                         // Chart background color
   uint              m_duplicate_res[];                        // Array for storing resource data copy

//--- Create (1) the object structure and (2) the object from the structure
   virtual bool      ObjectToStruct(void);
   virtual void      StructToObject(void);
   
private:
   int               m_shift_coord_x;                          // Offset of the X coordinate relative to the base object
   int               m_shift_coord_y;                          // Offset of the Y coordinate relative to the base object
   struct SData

Publicセクションで、基本オブジェクトへのポインタを設定して返すためのメソッドを指定します

//--- Create the element
   bool              Create(const long chart_id,
                            const int wnd_num,
                            const string name,
                            const int x,
                            const int y,
                            const int w,
                            const int h,
                            const color colour,
                            const uchar opacity,
                            const bool redraw=false);

//--- (1) Set and (2) return the pointer to the parent control
   void              SetBase(CGCnvElement *element)                                    { this.m_element_base=element;               }
   CGCnvElement     *GetBase(void)                                                     { return this.m_element_base;                }
//--- Return the pointer to a canvas object
   CCanvas          *GetCanvasObj(void)                                                { return &this.m_canvas;                     }

Move()メソッドは仮想化されます

//--- Return the size of the graphical resource copy array
   uint              DuplicateResArraySize(void)                                       { return ::ArraySize(this.m_duplicate_res);  }
   
//--- Update the coordinates (shift the canvas)
   virtual bool      Move(const int x,const int y,const bool redraw=false);

//--- Save an image to the array
   bool              ImageCopy(const string source,uint &array[]);

グラフィック要素オブジェクトは、オブジェクト自体を接続できない場合でも別のオブジェクトに接続される可能性があるため、Move()メソッドによって処理される再配置に変更を加える必要はありません。同時に、その子孫オブジェクトにはすでに他のグラフィック要素が接続されている可能性がありますが、これらの要素へのポインタはリストに配置されます。つまり、Move()メソッドはそれにバインドされたオブジェクトのリスト全体も処理する必要があります。ここで、オブジェクトごとに個別のMove()メソッドが呼び出されます。このため、このメソッドは仮想であり、継承されたオブジェクトごとに個別に実装されます。

publicセクションで、相対オブジェクト座標を設定するメソッドと返すメソッドを指定します

protected:
//--- Protected constructor
                     CGCnvElement(const ENUM_GRAPH_ELEMENT_TYPE element_type,
                                  const long    chart_id,
                                  const int     wnd_num,
                                  const string  name,
                                  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


デフォルトのコンストラクタで、ポインタの初期化を基本オブジェクトに設定し、相対オブジェクト座標のシフトを設定します。

//--- Default constructor/Destructor
                     CGCnvElement() : m_shadow(false),m_chart_color_bg((color)::ChartGetInteger(::ChartID(),CHART_COLOR_BACKGROUND))
                        { this.m_type=OBJECT_DE_TYPE_GELEMENT; this.m_element_base=NULL; this.m_shift_coord_x=0; this.m_shift_coord_y=0; }
                    ~CGCnvElement()
                        { this.m_canvas.Destroy();             }

基本オブジェクトへのポインタがNULLの場合、オブジェクトは他のグラフィック要素に接続されていません。

オブジェクトプロパティへの簡単なアクセスのためのメソッドのブロックで、オブジェクトの右端と下端の相対座標を返すためのメソッドを設定します

//--- Return (1) the background color, (2) the opacity, coordinate (3) of the right and (4) bottom element edge
   color             ColorBackground(void)               const { return this.m_color_bg;                                               }
   uchar             Opacity(void)                       const { return this.m_opacity;                                                }
   int               RightEdge(void)                     const { return this.CoordX()+this.m_canvas.Width();                           }
   int               BottomEdge(void)                    const { return this.CoordY()+this.m_canvas.Height();                          }
//--- Return the relative coordinate of the (1) right and (2) bottom element edge
   int               RightEdgeRelative(void)             const { return this.CoordXRelative()+this.m_canvas.Width();                   }
   int               BottomEdgeRelative(void)            const { return this.CoordYRelative()+this.m_canvas.Height();                  }
//--- Return the (1) X, (2) Y coordinates, (3) element width and (4) height,

BringToTop()メソッドが仮想化されます

//--- Set the object above all
   virtual void      BringToTop(void)                          { CGBaseObj::SetVisible(false,false); CGBaseObj::SetVisible(true,false);}
//--- (1) Show and (2) hide the element
   virtual void      Show(void)                                { CGBaseObj::SetVisible(true,false);                                    }
   virtual void      Hide(void)                                { CGBaseObj::SetVisible(false,false);                                   }

このメソッドが仮想化される理由は、上記と同じです。オブジェクトにバインドされたすべてのグラフィック要素は、何よりも基本オブジェクトを設定するときにも処理する必要があります。さもないと、前景に表示されているオブジェクトにバインドされているすべての要素が視覚的に消えてしまいます。また、バインドされた順序で基本オブジェクトの上に移動する必要があります。これは、接続されたオブジェクトのリストを持つ子孫オブジェクトのBringToTop()仮想メソッドで処理されます。

パラメトリックコンストラクタとprotectedコンストラクタで、基本オブジェクトへのポインタ相対座標シフトを初期化します。

//+------------------------------------------------------------------+
//| Parametric constructor                                           |
//+------------------------------------------------------------------+
CGCnvElement::CGCnvElement(const ENUM_GRAPH_ELEMENT_TYPE element_type,
                           const int      element_id,
                           const int      element_num,
                           const long     chart_id,
                           const int      wnd_num,
                           const string   name,
                           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.m_type=OBJECT_DE_TYPE_GELEMENT; 
   this.m_element_base=NULL;
   this.m_chart_color_bg=(color)::ChartGetInteger((chart_id==NULL ? ::ChartID() : chart_id),CHART_COLOR_BACKGROUND);
   this.m_name=(::StringFind(name,this.m_name_prefix)<0 ? this.m_name_prefix : "")+name;
   this.m_chart_id=(chart_id==NULL || chart_id==0 ? ::ChartID() : chart_id);
   this.m_subwindow=wnd_num;
   this.m_type_element=element_type;
   this.SetFont(DEF_FONT,DEF_FONT_SIZE);
   this.m_text_anchor=0;
   this.m_text_x=0;
   this.m_text_y=0;
   this.m_color_bg=colour;
   this.m_opacity=opacity;
   this.m_shift_coord_x=0;
   this.m_shift_coord_y=0;
   if(this.Create(chart_id,wnd_num,this.m_name,x,y,w,h,colour,opacity,redraw))
     {

protectedコンストラクタ内のポインタと変数の初期化は、同じ方法で実装されます。


フォームオブジェクトは、影オブジェクトを備える可能性のある最小単位です。静止したオブジェクトのためではありましたが、すでに使用しています。移動可能なインターフェイスオブジェクトに影の使用を割り当てると、グラフィックオブジェクトを移動すると、すぐに、影がそのまま残るという欠点が目立ちます。したがって、最初に変数を追加して、それをキャストするオブジェクトに対する影のシフトを格納し、次に、移動するオブジェクトに影を追従させる必要があります。ただし、影オブジェクトはグラフィック要素の下にあるため、最初に影オブジェクトを移動し、次にグラフィック要素を移動して、最後にグラフを更新する必要があります。この場合、オブジェクトが再配置して表示されます。それによって投影される影は、オブジェクトに従います。

さらに、オブジェクトをクリックすると、オブジェクトが前景に表示され、その影もチャート上の他のすべてのオブジェクトよりも高くなり、移動したオブジェクトの下のチャートにある他のグラフィック要素の上に描画されます。景

\MQL5\Include\DoEasy\Objects\Graph\ShadowObj.mqhのprivateセクションで、影をキャストするグラフィカルオブジェクトに相対した影のシフトを格納するための変数を宣言します

//+------------------------------------------------------------------+
//| Shadows object class                                             |
//+------------------------------------------------------------------+
class CShadowObj : public CGCnvElement
  {
private:
   color             m_color_shadow;                  // Shadow color
   uchar             m_opacity_shadow;                // Shadow opacity
   int               m_offset_x;                      // Shadow X axis shift
   int               m_offset_y;                      // Shadow Y axis shift

Publicセクションで、オブジェクトに相対した影シフト値を返すメソッドを記述します

//--- Supported object properties (1) integer and (2) string ones
   virtual bool      SupportProperty(ENUM_CANV_ELEMENT_PROP_INTEGER property) { return true; }
   virtual bool      SupportProperty(ENUM_CANV_ELEMENT_PROP_STRING property)  { return true; }
   
//--- Return the shadow shift by (1) X and (2) Y
   int               OffsetX(void)                                      const { return this.m_offset_x;        }
   int               OffsetY(void)                                      const { return this.m_offset_y;        }

//--- Draw an object shadow

クラスコンストラクタで変数値を初期化します

//+------------------------------------------------------------------+
//| Constructor                                                      |
//+------------------------------------------------------------------+
CShadowObj::CShadowObj(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,chart_id,subwindow,name,x,y,w,h)
  {
   this.m_type=OBJECT_DE_TYPE_GSHADOW; 
   CGCnvElement::SetColorBackground(clrNONE);
   CGCnvElement::SetOpacity(0);
   CGCnvElement::SetActive(false);
   this.m_opacity_shadow=127;
   color gray=CGCnvElement::ChangeColorSaturation(ChartColorBackground(),-100);
   this.m_color_shadow=CGCnvElement::ChangeColorLightness(gray,-50);
   this.m_shadow=false;
   this.m_visible=true;
   this.m_offset_x=0;
   this.m_offset_y=0;
   CGCnvElement::Erase();
  }
//+------------------------------------------------------------------+


影オブジェクトの描画中に、メソッド引数で渡されたシフト値を新しい変数に追加します

//+------------------------------------------------------------------+
//| Draw the object shadow                                           |
//+------------------------------------------------------------------+
void CShadowObj::DrawShadow(const int shift_x,const int shift_y,const uchar blur_value)
  {
//--- Set the shadow shift values to the variables by X and Y axes
   this.m_offset_x=shift_x;
   this.m_offset_y=shift_y;
//--- Calculate the height and width of the drawn rectangle
   int w=this.Width()-OUTER_AREA_SIZE*2;
   int h=this.Height()-OUTER_AREA_SIZE*2;
//--- Draw a filled rectangle with calculated dimensions
   this.DrawShadowFigureRect(w,h);
//--- Calculate the blur radius, which cannot exceed a quarter of the OUTER_AREA_SIZE constant
   int radius=(blur_value>OUTER_AREA_SIZE/4 ? OUTER_AREA_SIZE/4 : blur_value);
//--- If failed to blur the shape, exit the method (GaussianBlur() displays the error on the journal)
   if(!this.GaussianBlur(radius))
      return;
//--- Shift the shadow object by X/Y offsets specified in the method arguments and update the canvas
   CGCnvElement::Move(this.CoordX()+this.m_offset_x,this.CoordY()+this.m_offset_y);
   CGCnvElement::Update();
  }
//+------------------------------------------------------------------+

影を描いたら、指定した値だけシフトします。XおよびYシフト値を新しい変数に保存したことを除いて、メソッドはほとんど変更されていません。影オブジェクトは以前はメソッド引数で渡された値でシフトされていましたが、新しい変数で設定された値にシフトされるようになりました。これは同じことですが、影オブジェクトを作成および描画するときに指定されたシフトの値が得られます。これは、影オブジェクトを再配置するときに必要になります。


フォームオブジェクトファイル( \MQL5\Include\DoEasy\Objects\Graph\Form.mqh)で、CreateNameDependentObject()メソッドとCreateShadowObj()メソッドをクラスのprivateセクションからprotectedセクションに移動します。

protected:
   int               m_frame_width_left;                       // Form frame width to the left
   int               m_frame_width_right;                      // Form frame width to the right
   int               m_frame_width_top;                        // Form frame width at the top
   int               m_frame_width_bottom;                     // Form frame width at the bottom
//--- Initialize the variables
   void              Initialize(void);
   void              Deinitialize(void);
//--- Create a shadow object
   void              CreateShadowObj(const color colour,const uchar opacity);
//--- Return the name of the dependent object
   string            CreateNameDependentObject(const string base_name)  const
                       { return ::StringSubstr(this.NameObj(),::StringLen(::MQLInfoString(MQL_PROGRAM_NAME))+1)+"_"+base_name;   }
   
public:

これらのメソッドは、子孫クラスオブジェクトで使用できる必要があるため、クラスのpriotectedセクションにあります。

子孫オブジェクトをいくらか改良する必要があるため、新しいグラフィカルオブジェクトを作成するprivateメソッドは、仮想化されます。バインドされたオブジェクトを作成するときに、そのオブジェクトの座標を返す仮想メソッドを宣言します

//--- Create a new graphical object
   virtual CGCnvElement *CreateNewGObject(const ENUM_GRAPH_ELEMENT_TYPE type,
                                          const int element_num,
                                          const string name,
                                          const int x,
                                          const int y,
                                          const int w,
                                          const int h,
                                          const color colour,
                                          const uchar opacity,
                                          const bool movable,
                                          const bool activity);
//--- Return the initial coordinates of a bound object
   virtual void      GetCoords(int &x,int &y);

バインドされたオブジェクトを新規作成するメソッドは、フォームオブジェクトクラス内にあります。フォームオブジェクトは、グラフィック要素オブジェクトクラスから継承されています。したがって、これら2つのクラス(要素とフォーム)はフォームクラスオブジェクトに既知であり検知されており、ここで作成できます。ただし、WinFormsオブジェクトの開発プロセスで実行するウィンドウ、パネル、その他のクラスオブジェクトなど、他のコントロールを作成できなければなりませんが、これらはこのクラスには見えません。したがって、それぞれがクラス内に表示されるコントロールを作成するためのコードを含むCreateNewGObject()仮想メソッドを備えています。

クラスのpublicセクションで、グラフィック要素オブジェクトクラスで仮想化した仮想メソッドを宣言します

public:
//--- Return (1) the mouse status relative to the form, as well as (2) X and (3) Y coordinate of the cursor
   ENUM_MOUSE_FORM_STATE MouseFormState(const int id,const long lparam,const double dparam,const string sparam);
   int               MouseCursorX(void)            const { return this.m_mouse.CoordX();     }
   int               MouseCursorY(void)            const { return this.m_mouse.CoordY();     }
//--- Set the flags of mouse scrolling, context menu and the crosshairs tool for the chart
   void              SetChartTools(const bool flag);
//--- (1) Set and (2) return the shift of X and Y coordinates relative to the cursor
   void              SetOffsetX(const int value)         { this.m_offset_x=value;            }
   void              SetOffsetY(const int value)         { this.m_offset_y=value;            }
   int               OffsetX(void)                 const { return this.m_offset_x;           }
   int               OffsetY(void)                 const { return this.m_offset_y;           }

//--- Update the coordinates (shift the canvas)
   virtual bool      Move(const int x,const int y,const bool redraw=false);
//--- Set the priority of a graphical object for receiving the event of clicking on a chart
   virtual bool      SetZorder(const long value,const bool only_prop);
//--- Set the object above all
   virtual void      BringToTop(void);
   
//--- Event handler

フォームオブジェクトクラスに必要なハンドラ(オブジェクトにバインドされた他のコントロールのシフトを処理する)を持つメソッドの実装を以下に追加します。

影オブジェクトを返すメソッドは、以前にはエラーのためにCGCnvElementグラフィック要素オブジェクトタイプを返していましたが、影オブジェクトクラスで指定されたメソッドが使用できないままであることを除いて、これは重要ではありません。
メソッドが影オブジェクトタイプを返すように修正し、バインドされたコントロールの数とバインドさ れたオブジェクトのリスト内の インデックスによるコントロールを返す2つのメソッドを追加します。

//--- Return (1) the list of attached objects and (2) the shadow object
   CForm            *GetObject(void)                                          { return &this;                  }
   CArrayObj        *GetListElements(void)                                    { return &this.m_list_elements;  }
   CShadowObj       *GetShadowObj(void)                                       { return this.m_shadow_obj;      }
//--- Return the pointer to (1) the animation object, the list of (2) text and (3) rectangular animation frames
   CAnimations      *GetAnimationsObj(void)                                   { return this.m_animations;      }
   CArrayObj        *GetListFramesText(void)
                       { return(this.m_animations!=NULL ? this.m_animations.GetListFramesText() : NULL);       }
   CArrayObj        *GetListFramesQuad(void)
                       { return(this.m_animations!=NULL ? this.m_animations.GetListFramesQuad() : NULL);       }

//--- Return the (1) number of bound elements and (2) the bound element by the index in the list
   int               ElementsTotal(void)                       const { return this.m_list_elements.Total();    }
   CGCnvElement     *GetElement(const int index)                     { return this.m_list_elements.At(index);  }
   
//--- Set the form (1) color scheme and (2) style


新しいバインドされた要素を作成するメソッドの引数を変更します。以前には、作成された要素のインデックスと移動可能性フラグが他のプロパティに加えて渡されていました。インデックスは自動的に選択されるため、バインドされた要素には移動可能性は必要ありませんが(オブジェクトは接続されている基本オブジェクトのすべての再配置を継承するため)、インデックスではなく、作成されたオブジェクトのタイプを渡し、移動可能性はフラグはメソッド引数から削除されます。メソッド宣言は次のようになります。

//--- Create a new attached element
   bool              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);
//--- Add a new attached element


オブジェクトの影を描画するメソッドで、以前に渡されていた値4の代わりに、影ブラーのデフォルト値を追加します

//--- Add a new attached element
   bool              AddNewElement(CGCnvElement *obj,const int x,const int y);

//--- Draw an object shadow
   void              DrawShadow(const int shift_x,const int shift_y,const color colour,const uchar opacity=127,const uchar blur=DEF_SHADOW_BLUR);

//--- Draw the form frame


新しいグラフィックオブジェクトを作成するメソッドは以前はCGCnvElementグラフィック要素タイプのオブジェクトのみを作成していました。
作成されたオブジェクトがメソッド引数で渡されたものと同じタイプになるようにメソッドを改善します

//+------------------------------------------------------------------+
//| Create a new graphical object                                    |
//+------------------------------------------------------------------+
CGCnvElement *CForm::CreateNewGObject(const ENUM_GRAPH_ELEMENT_TYPE type,
                                      const int obj_num,
                                      const string obj_name,
                                      const int x,
                                      const int y,
                                      const int w,
                                      const int h,
                                      const color colour,
                                      const uchar opacity,
                                      const bool movable,
                                      const bool activity)
  {
   string name=this.CreateNameDependentObject(obj_name);
   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.ID(),obj_num,this.ChartID(),this.SubWindow(),name,x,y,w,h,colour,opacity,movable,activity);
        break;
      //--- create a form object
      case GRAPH_ELEMENT_TYPE_FORM :
         element=new CForm(this.ChartID(),this.SubWindow(),name,x,y,w,h);
        break;
      default:
        break;
     }
   if(element==NULL)
      ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_FAILED_CREATE_ELM_OBJ),": ",name);
   return element;
  }
//+------------------------------------------------------------------+


バインドされた要素を新規作成するメソッドは大幅に変更されました。

//+------------------------------------------------------------------+
//| 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)
  {
//--- 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 false;
     }
//--- Specify the element index in the list
   int num=this.m_list_elements.Total()+1;
//--- Create a graphical element name
   string ns=(::StringLen((string)num)<2 ? ::IntegerToString(num,2,'0') : (string)num);
   string name="Elm"+ns;
//--- Get the initial 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,name,elm_x,elm_y,w,h,colour,opacity,false,activity);
   if(obj==NULL)
      return false;
//--- and add it to the list of bound graphical elements
   if(!this.AddNewElement(obj,elm_x,elm_y))
     {
      delete obj;
      return false;
     }
//--- Set the minimum properties for a bound graphical element
   obj.SetBase(this.GetObject());
   obj.SetCoordXRelative(x);
   obj.SetCoordYRelative(y);
   obj.SetZorder(this.Zorder(),false);
//--- Draw an added object and return 'true'
   obj.Erase(colour,opacity,true);
   return true;
  }
//+------------------------------------------------------------------+

ロジック全体は、コードのコメントで説明されています。グラフィカルオブジェクト名を作成するときは、リスト内の基本オブジェクト名+「_Elm」文字列+オブジェクト番号を使用します。オブジェクト番号には次のロジックを使用します。オブジェクト番号が1〜9の範囲内にある場合は、ゼロを追加しいます。01、02、03、.., .., 08、09.10以上の場合は何も追加しません。このメソッドはオブジェクト名のみを作成しますが、CreateNameDependentObject()メソッドを呼び出すと、基本オブジェクトの名前がCreateNewGObject()メソッド、つまり最初の文字列に追加されます。

新しい接続要素をリストに追加するメソッド も少し変更されました。今のところ、このメソッドは、追加されているオブジェクトと同じ名前のリスト内のオブジェクトを検索するためのループを適用します

//+------------------------------------------------------------------+
//| Add a new attached element                                       |
//+------------------------------------------------------------------+
bool CForm::AddNewElement(CGCnvElement *obj,const int x,const int y)
  {
   if(obj==NULL)
      return false;
   for(int i=0;i<this.m_list_elements.Total();i++)
     {
      CGCnvElement *elm=this.m_list_elements.At(i);
      if(elm==NULL)
         continue;
      if(elm.Name()==obj.Name())
        {
         ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_OBJ_ALREADY_IN_LIST),": ",obj.NameObj());
         return false;
        }
     }
   if(!this.m_list_elements.Add(obj))
     {
      ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_FAILED_OBJ_ADD_TO_LIST),": ",obj.NameObj());
      return false;
     }
   return true;
  }
//+------------------------------------------------------------------+

以前の検索

   this.m_list_elements.Sort(SORT_BY_CANV_ELEMENT_NAME_OBJ);
   int index=this.m_list_elements.Search(obj);
   if(index>WRONG_VALUE)
     {
      ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_OBJ_ALREADY_IN_LIST),": ",obj.NameObj());
      return false;
     }

まだはっきりしない理由で動作しません。バインドされたオブジェクトのリストでメソッドに渡された名前のオブジェクトの存在が常に返され、この動作は正しくありません。これについては後で扱います。検索はループに置き換えられました。

以下は、バインドされたオブジェクトの初期座標を返すメソッドです。

//+------------------------------------------------------------------+
//| Return the initial coordinates of a bound object                 |
//+------------------------------------------------------------------+
void CForm::GetCoords(int &x,int &y)
  {
   x=this.CoordX()+this.m_frame_width_left+x;
   y=this.CoordY()+this.m_frame_width_top+y;
  }
//+------------------------------------------------------------------+

このメソッドは、リンクによって渡された相対座標の値を、X 座標の場合は左側に、Y 座標の場合は上部にあるフレームの幅に合わせて調整された値に戻します。オブジェクトにフレームがある場合、追加された オブジェクトはその上に配置しないでください。たとえば0などの座標が渡されると、フレームの幅だけシフトされます。言い換えれば、フレームの幅が3の場合、すべての座標がその量だけシフトされます。

T以下は、要素の座標を更新する仮想メソッドです。

//+------------------------------------------------------------------+
//| Update the coordinate elements                                   |
//+------------------------------------------------------------------+
bool CForm::Move(const int x,const int y,const bool redraw=false)
  {
   CGCnvElement *base=this.GetBase();
   bool res=true;
//--- If the element is not movable and is a base object, leave
   if(!this.Movable() && base==NULL)
      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)
     {
      if(this.m_shadow_obj==NULL || !this.m_shadow_obj.Move(x-OUTER_AREA_SIZE+this.m_shadow_obj.OffsetX(),y-OUTER_AREA_SIZE+this.m_shadow_obj.OffsetY(),false))
         return false;
     }
//--- If failed to set new values into graphical object properties, return 'false'
   if(!this.SetCoordX(x) || !this.SetCoordY(y))
      return false;
//--- In the loop by all bound objects,
   for(int i=0;i<this.m_list_elements.Total();i++)
     {
      //--- get the next object and shift it
      CGCnvElement *obj=m_list_elements.At(i);
      if(obj==NULL)
         continue;
      if(!obj.Move(x+this.m_frame_width_left+obj.CoordXRelative(),y+this.m_frame_width_top+obj.CoordYRelative(),false))
         return false;
     }
   //--- If the update flag is set and this is a base object, redraw the chart.
   if(redraw && base==NULL)
      ::ChartRedraw(this.ChartID());
   //--- Return 'true'
   return true;
  }
//+------------------------------------------------------------------+

メソッドはCGCnvElement親オブジェクトクラスから取得され 、バインドされたオブジェクトのリストによるループが追加されています。オブジェクトごとに同じメソッドが呼び出されますため、すべてのオブジェクトは、チェーン順に基本オブジェクトの後にシフトされます。
メソッドが呼び出される各オブジェクトのチャートが更新されないようにするために、基本オブジェクトのチェックを追加しました(他の基本オブジェクトにバインドされていないため、基本オブジェクトへのポインタはNULLです)。これは、チャートがループの完了時に1回のみ更新されることを意味します。

以下は、チャートをクリックするイベントを受信するためのグラフィックオブジェクトの優先度を設定するメソッドです

//+------------------------------------------------------------------+
//| Set the priority of a graphical object                           |
//| for receiving the event of clicking on a chart                   |
//+------------------------------------------------------------------+
bool CForm::SetZorder(const long value,const bool only_prop)
  {
   if(!CGCnvElement::SetZorder(value,only_prop))
      return false;
   if(this.m_shadow)
     {
      if(this.m_shadow_obj==NULL || !this.m_shadow_obj.SetZorder(value,only_prop))
         return false;
     }
   int total=this.m_list_elements.Total();
   for(int i=0;i<total;i++)
     {
      CGCnvElement *obj=this.m_list_elements.At(i);
      if(obj==NULL)
         continue;
      if(!obj.SetZorder(value,only_prop))
         return false;
     }
   return true;
  }
//+------------------------------------------------------------------+

上記のメソッドと同様に、ここでは、同じメソッドが呼び出されるすべての接続されたオブジェクトによる反復処理を追加しました

以下は、他のすべての上にオブジェクトを設定するメソッドです。

//+------------------------------------------------------------------+
//| Set the object above all the rest                                |
//+------------------------------------------------------------------+
void CForm::BringToTop(void)
  {
//--- If the shadow usage flag is set
   if(this.m_shadow)
     {
      //--- If the shadow object is created, move it to the foreground
      if(this.m_shadow_obj!=NULL)
         this.m_shadow_obj.BringToTop();
     }
//--- Move the object to the foreground (the object is located above the shadow)
   CGCnvElement::BringToTop();
//--- In the loop by all bound objects,
   int total=this.m_list_elements.Total();
   for(int i=0;i<total;i++)
     {
      //--- get the next object from the list
      CGCnvElement *obj=this.m_list_elements.At(i);
      if(obj==NULL)
         continue;
      //--- and move it to the foreground
      obj.BringToTop();
     }
  }
//+------------------------------------------------------------------+

メソッドのロジックは、コードのコメントで説明されています。オブジェクトに影がある場合は、その影を最初のものの前景に移動して、すべてのチャートオブジェクトの上に配置する必要があります。次に、オブジェクト自体が前景に移動され、影の上に配置されます。次に、バインドされたすべてのオブジェクトを反復処理しで、バインドされているオブジェクトの上にオブジェクトを移動します。


\MQL5\Include\DoEasy\Objects\Graph\WForms\Containers\Panel.mqhのパネルオブジェクトクラスを改善します。

クラスのprivateセクションで、親クラスに実装した2つの仮想メソッドを宣言します

//+------------------------------------------------------------------+
//| Panel object class of WForms controls                            |
//+------------------------------------------------------------------+
class CPanel : public CForm
  {
private:
   color             m_fore_color;                                   // Default text color for all panel objects
   ENUM_FW_TYPE      m_bold_type;                                    // Font width type
   ENUM_FRAME_STYLE  m_border_style;                                 // Panel frame style
   bool              m_autoscroll;                                   // Auto scrollbar flag
   int               m_autoscroll_margin[2];                         // Array of fields around the control during an auto scroll
   bool              m_autosize;                                     // Flag of the element auto resizing depending on the content
   ENUM_CANV_ELEMENT_AUTO_SIZE_MODE m_autosize_mode;                 // Mode of the element auto resizing depending on the content
   ENUM_CANV_ELEMENT_DOCK_MODE m_dock_mode;                          // Mode of binding element borders to the container
   int               m_margin[4];                                    // Array of gaps of all sides between the fields of the current and adjacent controls
   int               m_padding[4];                                   // Array of gaps of all sides inside controls
//--- Return the font flags
   uint              GetFontFlags(void);
//--- Create a new graphical object
   virtual CGCnvElement *CreateNewGObject(const ENUM_GRAPH_ELEMENT_TYPE type,
                                          const int element_num,
                                          const string name,
                                          const int x,
                                          const int y,
                                          const int w,
                                          const int h,
                                          const color colour,
                                          const uchar opacity,
                                          const bool movable,
                                          const bool activity);
//--- Return the initial coordinates of a bound object
   virtual void      GetCoords(int &x,int &y);

public:

クラスに新しいグラフィックオブジェクトを作成するメソッドでは、同じタイプのクラスに表示されるため、CPanelオブジェクトの作成を追加できます。GetCoords()メソッドは、一時的に親クラスと同じ実装になります。おそらく、クラスを改善するときに変更します。

パネルの各側面のパディング値を設定するメソッドを改良してみましょう。

//--- Set the gap (1) to the left, (2) at the top, (3) to the right, (4) at the bottom and (5) on all sides inside the control
   void              PaddingLeft(const uint value)
                       {
                        this.m_padding[0]=((int)value<this.m_frame_width_left ? this.m_frame_width_left : (int)value);
                       }
   void              PaddingTop(const uint value)
                       {
                        this.m_padding[1]=((int)value<this.m_frame_width_top ? this.m_frame_width_top : (int)value);
                       }
   void              PaddingRight(const uint value)
                       {
                        this.m_padding[2]=((int)value<this.m_frame_width_right ? this.m_frame_width_right : (int)value);
                       }
   void              PaddingBottom(const uint value)
                       {
                        this.m_padding[3]=((int)value<this.m_frame_width_bottom ? this.m_frame_width_bottom : (int)value);
                       }
   void              PaddingAll(const uint value)
                       {
                        this.PaddingLeft(value); this.PaddingTop(value); this.PaddingRight(value); this.PaddingBottom(value);
                       }
//--- Return the gap (1) to the left, (2) at the top, (3) to the right and (4) at the bottom between the fields inside the control

ここで、パディング値がパネルフレーム幅よりも小さい場合値はフレームの対応する辺の幅に等しくなります


デフォルトのコンストラクタで、グラフィカルオブジェクトタイプの設定を「パネル」としてオブジェクトプロパティに追加します

                     CPanel(const string name) : CForm(::ChartID(),0,name,0,0,0,0)
                       {
                        CGBaseObj::SetTypeElement(GRAPH_ELEMENT_TYPE_PANEL);
                        CGCnvElement::SetProperty(CANV_ELEMENT_PROP_TYPE,GRAPH_ELEMENT_TYPE_PANEL);
                        this.m_type=OBJECT_DE_TYPE_GWF_PANEL; 
                        this.m_fore_color=CLR_DEF_FORE_COLOR;
                        this.m_bold_type=FW_TYPE_NORMAL;
                        this.MarginAll(3);
                        this.PaddingAll(0);
                        this.Initialize();
                       }
//--- Destructor

同じ文字列が残りのすべてのクラスコンストラクタに追加されます。文字列がない場合、親クラスコンストラクタによって登録されたオブジェクトタイプは「Form」として設定されます。そのため、これらのコンストラクタにプロパティの強制設定を「Panel」として追加しました。
これは実際にはクラスロジックの欠陥です。後でこれを修正する方法を見ていきます...

以前に名前が変更されたCLR_FORE_COLORマクロ置換の名前は、すべてのクラスコンストラクタでCLR_DEF_FORE_COLORに置き換えられました。

以下は、新しいグラフィックオブジェクトを作成するメソッドです。

//+------------------------------------------------------------------+
//| Create a new graphical object                                    |
//+------------------------------------------------------------------+
CGCnvElement *CPanel::CreateNewGObject(const ENUM_GRAPH_ELEMENT_TYPE type,
                                       const int obj_num,
                                       const string obj_name,
                                       const int x,
                                       const int y,
                                       const int w,
                                       const int h,
                                       const color colour,
                                       const uchar opacity,
                                       const bool movable,
                                       const bool activity)
  {
   string name=this.CreateNameDependentObject(obj_name);
   CGCnvElement *element=NULL;
   switch(type)
     {
      case GRAPH_ELEMENT_TYPE_ELEMENT :
         element=new CGCnvElement(type,this.ID(),obj_num,this.ChartID(),this.SubWindow(),name,x,y,w,h,colour,opacity,movable,activity);
        break;
      case GRAPH_ELEMENT_TYPE_FORM :
         element=new CForm(this.ChartID(),this.SubWindow(),name,x,y,w,h);
        break;
      case GRAPH_ELEMENT_TYPE_PANEL :
         element=new CPanel(this.ChartID(),this.SubWindow(),name,x,y,w,h);
        break;
      default:
        break;
     }
   if(element==NULL)
      ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_FAILED_CREATE_ELM_OBJ),": ",name);
   return element;
  }
//+------------------------------------------------------------------+

このメソッドでは、上記で検討した同じ名前の親オブジェクトメソッドのロジックを繰り返します。ただし、ここでは、同じタイプのクラスであるため、このようなオブジェクトタイプがクラスで認識されているため、さらに別の「Panel」グラフィカルコントロールオブジェクトの作成を追加 しました。
後で追加される他のクラスでは、仮想メソッドは、それらのタイプに対応するグラフィック要素を作成する文字列を備えることになります。

以下は、バインドされたオブジェクトの初期座標を返すメソッドです。

//+------------------------------------------------------------------+
//| Return the initial coordinates of a bound object                 |
//+------------------------------------------------------------------+
void CPanel::GetCoords(int &x,int &y)
  {
   x=this.CoordX()+this.FrameWidthLeft()+x;
   y=this.CoordY()+this.FrameWidthTop()+y;
  }
//+------------------------------------------------------------------+

このメソッドでは、ロジックは親オブジェクトの同名メソッドと同じです。違いは、ここでは、privateセクションに隠されていて子孫クラスでは使用できない変数の値ではなく、親クラスのpublicメソッドを使用して境界線の幅の値にアクセスすることです。


グラフィック要素のコレクションクラス(\MQL5\Include\DoEasy\Collections\GraphElementsCollection.mqh)に、グラフィックコントロールにすばやくアクセスするためのメソッドを追加します

//--- Return the list of graphical elements by element type
   CArrayObj        *GetListCanvElementByType(const ENUM_GRAPH_ELEMENT_TYPE type)
                       {
                        return CSelect::ByGraphCanvElementProperty(this.GetListCanvElm(),CANV_ELEMENT_PROP_TYPE,type,EQUAL);
                       }

//--- ...
                
//--- Return the graphical element by chart ID and name
   CGCnvElement     *GetCanvElement(const long chart_id,const string name)
                       {
                        CArrayObj *list=this.GetListCanvElementByName(chart_id,name);
                        return(list!=NULL ? list.At(0) : NULL);
                       }
//--- Return the graphical element by chart and object IDs
   CGCnvElement     *GetCanvElement(const long chart_id,const int element_id)
                       {
                        CArrayObj *list=this.GetListCanvElementByID(chart_id,element_id);
                        return(list!=NULL ? list.At(0) : NULL);
                       }

//--- Constructor
                     CGraphElementsCollection();

そのようなメソッドの背後にある論理はすでに何度も考察しました。これは、指定されたプロパティによる通常の並べ替えです。

指定したチャートとサブウィンドウのキャンバス上にグラフィックオブジェクトWinFormsPanelオブジェクトを作成するメソッドで、影オブジェクトを作成・描画するようにします。
メソッド引数で影の存在が指定されている場合

//--- Create graphical object WinForms Panel object on canvas on a specified chart and subwindow
   int               CreatePanel(const long chart_id,
                                 const int subwindow,
                                 const string name,
                                 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 int  frame_width=-1,
                                 ENUM_FRAME_STYLE frame_style=FRAME_STYLE_BEVEL,
                                 const bool shadow=false,
                                 const bool redraw=false)
                       {
                        int id=this.m_list_all_canv_elm_obj.Total();
                        CPanel *obj=new CPanel(chart_id,subwindow,name,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.SetColorBackground(clr);
                        obj.SetColorFrame(clr);
                        obj.SetOpacity(opacity,false);
                        //--- Draw the shadow drawing flag
                        obj.SetShadow(shadow);
                        if(shadow)
                          {
                           //--- Calculate the shadow color as the chart background color converted to the monochrome one
                           //--- and darken the monochrome color by 20 units
                           color clrS=obj.ChangeColorLightness(obj.ChangeColorSaturation(obj.ColorBackground(),-100),-20);
                           //--- Draw the form shadow with the right-downwards offset from the form by three pixels along all axes
                           //--- Set the shadow opacity to the default value, while the blur radius is equal to 4
                           obj.DrawShadow(3,3,clrS,CLR_DEF_SHADOW_OPACITY,DEF_SHADOW_BLUR);
                          }
                        obj.DrawRectangle(0,0,obj.Width()-1,obj.Height()-1,obj.ColorFrame(),obj.Opacity());
                        obj.Erase(clr,opacity,redraw);
                        if(frame_width>0)
                           obj.FrameWidthAll(frame_width);
                        obj.SetActiveAreaShift(obj.FrameWidthLeft(),obj.FrameWidthBottom(),obj.FrameWidthRight(),obj.FrameWidthTop());
                        obj.DrawFormFrame(obj.FrameWidthTop(),obj.FrameWidthBottom(),obj.FrameWidthLeft(),obj.FrameWidthRight(),obj.ColorFrame(),obj.Opacity(),frame_style);
                        obj.Done();
                        return obj.ID();
                       }

さまざまな塗りつぶしタイプ(垂直方向と水平方向のグラデーション、および周期的な垂直方向と水平方向のグラデーション)でパネルオブジェクトを作成するメソッドを追加します。ここでは、そのようなメソッドの1つを検討します。残りのメソッドでは、CGCnvElementクラスのErase()メソッドに渡されるフラグの組み合わせのみが異なるためです。

//--- Create a WinForms Panel object graphical object on canvas on a specified chart and subwindow with the vertical gradient filling
   int               CreatePanelVGradient(const long chart_id,
                                          const int subwindow,
                                          const string name,
                                          const int x,
                                          const int y,
                                          const int w,
                                          const int h,
                                          color &clr[],
                                          const uchar opacity,
                                          const bool movable,
                                          const bool activity,
                                          const int  frame_width=-1,
                                          ENUM_FRAME_STYLE frame_style=FRAME_STYLE_BEVEL,
                                          const bool shadow=false,
                                          const bool redraw=false)
                       {
                        int id=this.m_list_all_canv_elm_obj.Total();
                        CPanel *obj=new CPanel(chart_id,subwindow,name,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.SetColorBackground(clr[0]);
                        obj.SetColorFrame(clr[0]);
                        obj.SetOpacity(opacity,false);
                        //--- Draw the shadow drawing flag
                        obj.SetShadow(shadow);
                        if(shadow)
                          {
                           //--- Calculate the shadow color as the chart background color converted to the monochrome one
                           //--- and darken the monochrome color by 20 units
                           color clrS=obj.ChangeColorLightness(obj.ChangeColorSaturation(obj.ColorBackground(),-100),-20);
                           //--- Draw the form shadow with the right-downwards offset from the form by three pixels along all axes
                           //--- Set the shadow opacity to the default value, while the blur radius is equal to 4
                           obj.DrawShadow(3,3,clrS,CLR_DEF_SHADOW_OPACITY,DEF_SHADOW_BLUR);
                          }
                        obj.DrawRectangle(0,0,obj.Width()-1,obj.Height()-1,obj.ColorFrame(),obj.Opacity());
                        obj.Erase(clr,opacity,true,false,redraw);
                        if(frame_width>0)
                           obj.FrameWidthAll(frame_width);
                        obj.SetActiveAreaShift(obj.FrameWidthLeft(),obj.FrameWidthBottom(),obj.FrameWidthRight(),obj.FrameWidthTop());
                        obj.DrawFormFrame(obj.FrameWidthTop(),obj.FrameWidthBottom(),obj.FrameWidthLeft(),obj.FrameWidthRight(),obj.ColorFrame(),obj.Opacity(),frame_style);
                        obj.Done();
                        return obj.ID();
                       }
//--- ...

  };
//+------------------------------------------------------------------+
//| Constructor                                                      |
//+------------------------------------------------------------------+

Erase()メソッドに渡されるフラグを除いて、メソッドは互いにほとんど同じです。最初のメソッドとは異なり、これらのメソッドは、パネルの塗りつぶしの色を示す変数ではなく、グラデーションの塗りつぶしに使用される色の配列を取得します。

オブジェクトの長方形の領域にペイントするために使用されるCGCnvElement親クラスのErase()メソッドを使用すると、単一の色でペイントするか、さまざまなグラデーション塗りつぶしメソッドを使用できます。これらは、以前にグラフィック要素オブジェクトクラスを作成するときに考慮しました。

記事に添付されているファイルで、これらすべてのメソッドに精通することができます。

カスタムプログラムからコントロールを簡単に作成して処理できるようにするには、CEngineライブラリのメインクラス、つまり\MQL5\Include\DoEasy\Engine.mqhのpublicセクションで 、これらのオブジェクトにすばやくアクセスするためのメソッドを設定します。オブジェクト名のプレフィックスをクラスのprivateセクションに格納するための変数を追加します

   ENUM_PROGRAM_TYPE    m_program_type;                  // Program type
   string               m_name_program;                  // Program name
   string               m_name_prefix;                   // Object name prefix
//--- Return the counter index by id
   int                  CounterIndex(const int id) const;

...

//--- Return the list of graphical elements by chart ID and object name
   CArrayObj           *GetListCanvElementByName(const long chart_id,const string name)
                          {
                           return this.m_graph_objects.GetListCanvElementByName(chart_id,name);
                          }
//--- Return the list of graphical elements by object type
   CArrayObj           *GetListCanvElementByType(const ENUM_GRAPH_ELEMENT_TYPE type)
                          {
                           return this.m_graph_objects.GetListCanvElementByType(type);
                          }
   
//--- Return the graphical element by chart ID and object name
   CGCnvElement        *GetCanvElementByName(const long chart_id,const string name)
                          {
                           return this.m_graph_objects.GetCanvElement(chart_id,name);
                          }
//--- Return the graphical element by chart and object IDs
   CGCnvElement        *GetCanvElementByID(const long chart_id,const int element_id)
                          {
                           return this.m_graph_objects.GetCanvElement(chart_id,element_id);
                          }
   
//--- Return the WForm Element object by object name on the current chart
   CGCnvElement        *GetWFElement(const string name)
                          {
                           string nm=(::StringFind(name,this.m_name_prefix)<0 ? this.m_name_prefix : "")+name;
                           CArrayObj *list=GetListCanvElementByType(GRAPH_ELEMENT_TYPE_ELEMENT);
                           list=CSelect::ByGraphCanvElementProperty(list,CANV_ELEMENT_PROP_CHART_ID,::ChartID(),EQUAL);
                           list=CSelect::ByGraphCanvElementProperty(list,CANV_ELEMENT_PROP_NAME_OBJ,nm,EQUAL);
                           return(list!=NULL ? list.At(0) : NULL);
                          }
//--- Return the WForm Element object by chart ID and object name
   CGCnvElement        *GetWFElement(const long chart_id,const string name)
                          {
                           string nm=(::StringFind(name,this.m_name_prefix)<0 ? this.m_name_prefix : "")+name;
                           CArrayObj *list=GetListCanvElementByType(GRAPH_ELEMENT_TYPE_ELEMENT);
                           list=CSelect::ByGraphCanvElementProperty(list,CANV_ELEMENT_PROP_CHART_ID,chart_id,EQUAL);
                           list=CSelect::ByGraphCanvElementProperty(list,CANV_ELEMENT_PROP_NAME_OBJ,nm,EQUAL);
                           return(list!=NULL ? list.At(0) : NULL);
                          }
//--- Return the WForm Element object by object ID
   CGCnvElement        *GetWFElement(const int element_id)
                          {
                           CArrayObj *list=GetListCanvElementByType(GRAPH_ELEMENT_TYPE_ELEMENT);
                           list=CSelect::ByGraphCanvElementProperty(list,CANV_ELEMENT_PROP_ID,element_id,EQUAL);
                           return(list!=NULL ? list.At(0) : NULL);
                          }
   
//--- Return the WForm Form object by object name on the current chart
   CForm               *GetWFForm(const string name)
                          {
                           string nm=(::StringFind(name,this.m_name_prefix)<0 ? this.m_name_prefix : "")+name;
                           CArrayObj *list=GetListCanvElementByType(GRAPH_ELEMENT_TYPE_FORM);
                           list=CSelect::ByGraphCanvElementProperty(list,CANV_ELEMENT_PROP_CHART_ID,::ChartID(),EQUAL);
                           list=CSelect::ByGraphCanvElementProperty(list,CANV_ELEMENT_PROP_NAME_OBJ,nm,EQUAL);
                           return(list!=NULL ? list.At(0) : NULL);
                          }
//--- Return the WForm Form object by chart ID and object name
   CForm               *GetWFForm(const long chart_id,const string name)
                          {
                           string nm=(::StringFind(name,this.m_name_prefix)<0 ? this.m_name_prefix : "")+name;
                           CArrayObj *list=GetListCanvElementByType(GRAPH_ELEMENT_TYPE_FORM);
                           list=CSelect::ByGraphCanvElementProperty(list,CANV_ELEMENT_PROP_CHART_ID,chart_id,EQUAL);
                           list=CSelect::ByGraphCanvElementProperty(list,CANV_ELEMENT_PROP_NAME_OBJ,nm,EQUAL);
                           return(list!=NULL ? list.At(0) : NULL);
                          }
//--- Return the WForm Element object by object ID
   CForm               *GetWFForm(const int element_id)
                          {
                           CArrayObj *list=GetListCanvElementByType(GRAPH_ELEMENT_TYPE_FORM);
                           list=CSelect::ByGraphCanvElementProperty(list,CANV_ELEMENT_PROP_ID,element_id,EQUAL);
                           return(list!=NULL ? list.At(0) : NULL);
                          }
   
//--- Return the WForm Panel object by object name on the current chart
   CPanel              *GetWFPanel(const string name)
                          {
                           string nm=(::StringFind(name,this.m_name_prefix)<0 ? this.m_name_prefix : "")+name;
                           CArrayObj *list=GetListCanvElementByType(GRAPH_ELEMENT_TYPE_PANEL);
                           list=CSelect::ByGraphCanvElementProperty(list,CANV_ELEMENT_PROP_CHART_ID,::ChartID(),EQUAL);
                           list=CSelect::ByGraphCanvElementProperty(list,CANV_ELEMENT_PROP_NAME_OBJ,nm,EQUAL);
                           return(list!=NULL ? list.At(0) : NULL);
                          }
//--- Return the WForm Panel object by chart ID and object name
   CPanel              *GetWFPanel(const long chart_id,const string name)
                          {
                           string nm=(::StringFind(name,this.m_name_prefix)<0 ? this.m_name_prefix : "")+name;
                           CArrayObj *list=GetListCanvElementByType(GRAPH_ELEMENT_TYPE_PANEL);
                           list=CSelect::ByGraphCanvElementProperty(list,CANV_ELEMENT_PROP_CHART_ID,chart_id,EQUAL);
                           list=CSelect::ByGraphCanvElementProperty(list,CANV_ELEMENT_PROP_NAME_OBJ,nm,EQUAL);
                           return(list!=NULL ? list.At(0) : NULL);
                          }
//--- Return the WForm Panel object by object ID
   CPanel              *GetWFPanel(const int element_id)
                          {
                           CArrayObj *list=GetListCanvElementByType(GRAPH_ELEMENT_TYPE_PANEL);
                           list=CSelect::ByGraphCanvElementProperty(list,CANV_ELEMENT_PROP_ID,element_id,EQUAL);
                           return(list!=NULL ? list.At(0) : NULL);
                          }

これらすべてのメソッドの背後にあるロジックは、指定されたオブジェクトプロパティによるリストの単純な並べ替えで構成されています。何度も考察したため、問題ないはずです。質問がある場合は、コメント欄でお気軽にお問い合わせください。

WinForm Elementオブジェクトを作成するメソッドを書いてみましょう。

//--- Create the WinForm Element object
   CGCnvElement        *CreateWFElement(const long chart_id,
                                        const int subwindow,
                                        const string name,
                                        const int x,
                                        const int y,
                                        const int w,
                                        const int h,
                                        color &clr[],
                                        const uchar opacity,
                                        const bool v_gradient=true,
                                        const bool c_gradient=false,
                                        const bool redraw=false)
                          {
                           //--- Get the created object ID
                           int obj_id=
                             (
                              //--- In case of a vertical gradient:
                              v_gradient ?
                                (
                                 //--- if not a cyclic gradient, create an object with the vertical gradient filling
                                 !c_gradient ? this.m_graph_objects.CreateElementVGradient(chart_id,subwindow,name,x,y,w,h,clr,opacity,false,true,redraw) :
                                 //--- otherwise, create an object with the cyclic vertical gradient filling
                                 this.m_graph_objects.CreateElementVGradientCicle(chart_id,subwindow,name,x,y,w,h,clr,opacity,false,true,redraw)
                                ) :
                              //--- If this is not a vertical gradient:
                              !v_gradient ?
                                (
                                 //--- if not a cyclic gradient, create an object with the horizontal gradient filling
                                 !c_gradient ? this.m_graph_objects.CreateElementHGradient(chart_id,subwindow,name,x,y,w,h,clr,opacity,false,true,redraw) :
                                 //--- otherwise, create an object with the cyclic horizontal gradient filling
                                 this.m_graph_objects.CreateElementHGradientCicle(chart_id,subwindow,name,x,y,w,h,clr,opacity,false,true,redraw)
                                ) :
                              WRONG_VALUE
                             );
                           //--- return the pointer to an object by its ID
                           return this.GetWFElement(obj_id);
                          }

塗りつぶしの種類(垂直または水平のグラデーション、循環かどうか)に応じて、上記のグラフィック要素コレクションクラスで実装したグラフィック要素オブジェクトを作成するために適切なメソッドを呼び出します。

このメソッドを使用して、現在のチャートと指定されたサブウィンドウ、およびメインウィンドウの現在のチャートにグラフィック要素オブジェクトを作成するためのメソッドを記述します

//--- Create the WinForm Element object in the specified subwindow on the current chart
   CGCnvElement        *CreateWFElement(const int subwindow,
                                        const string name,
                                        const int x,
                                        const int y,
                                        const int w,
                                        const int h,
                                        color &clr[],
                                        const uchar opacity,
                                        const bool v_gradient=true,
                                        const bool c_gradient=false,
                                        const bool redraw=false)
                          {
                           return this.CreateWFElement(::ChartID(),subwindow,name,x,y,w,h,clr,opacity,v_gradient,c_gradient,redraw);
                          }
//--- Create the WinForm Element object in the main window of the current chart
   CGCnvElement        *CreateWFElement(const string name,
                                        const int x,
                                        const int y,
                                        const int w,
                                        const int h,
                                        color &clr[],
                                        const uchar opacity,
                                        const bool v_gradient=true,
                                        const bool c_gradient=false,
                                        const bool redraw=false)
                          {
                           return this.CreateWFElement(::ChartID(),0,name,x,y,w,h,clr,opacity,v_gradient,c_gradient,redraw);
                          }

メソッドは、必要なチャートとサブウィンドウIDを示すグラフィック要素を作成するメソッドを呼び出した結果を返します。

WinForm Formオブジェクトを作成するためのメソッドは、同様の方法で設定されます。

//--- Create the WinForm Form object
   CForm               *CreateWFForm(const long chart_id,
                                     const int subwindow,
                                     const string name,
                                     const int x,
                                     const int y,
                                     const int w,
                                     const int h,
                                     color &clr[],
                                     const uchar opacity,
                                     const bool movable,
                                     const bool v_gradient=true,
                                     const bool c_gradient=false,
                                     const bool shadow=false,
                                     const bool redraw=false)
                          {
                           int obj_id=
                             (
                              v_gradient ?
                                (
                                 !c_gradient ? this.m_graph_objects.CreateFormVGradient(chart_id,subwindow,name,x,y,w,h,clr,opacity,movable,true,shadow,redraw) :
                                 this.m_graph_objects.CreateFormVGradientCicle(chart_id,subwindow,name,x,y,w,h,clr,opacity,movable,true,shadow,redraw)
                                ) :
                              !v_gradient ?
                                (
                                 !c_gradient ? this.m_graph_objects.CreateFormVGradient(chart_id,subwindow,name,x,y,w,h,clr,opacity,movable,true,shadow,redraw) :
                                 this.m_graph_objects.CreateFormVGradientCicle(chart_id,subwindow,name,x,y,w,h,clr,opacity,movable,true,shadow,redraw)
                                ) :
                              WRONG_VALUE
                             );
                           return this.GetWFForm(obj_id);
                          }
//--- Create the WinForm Form object in the specified subwindow on the current chart
   CForm               *CreateWFForm(const int subwindow,
                                     const string name,
                                     const int x,
                                     const int y,
                                     const int w,
                                     const int h,
                                     color &clr[],
                                     const uchar opacity,
                                     const bool movable,
                                     const bool v_gradient=true,
                                     const bool c_gradient=false,
                                     const bool shadow=false,
                                     const bool redraw=false)
                          {
                           return this.CreateWFForm(::ChartID(),subwindow,name,x,y,w,h,clr,opacity,movable,v_gradient,c_gradient,shadow,redraw);
                          }
//--- Create the WinForm Form object in the main window of the current chart
   CForm               *CreateWFForm(const string name,
                                     const int x,
                                     const int y,
                                     const int w,
                                     const int h,
                                     color &clr[],
                                     const uchar opacity,
                                     const bool movable,
                                     const bool v_gradient=true,
                                     const bool c_gradient=false,
                                     const bool shadow=false,
                                     const bool redraw=false)
                          {
                           return this.CreateWFForm(::ChartID(),0,name,x,y,w,h,clr,opacity,movable,v_gradient,c_gradient,shadow,redraw);
                          }

ここではすべてがまったく同じです。1つのメソッドは指定された塗りつぶしでフォームオブジェクトを作成し、他の2つのメソッドは目的のチャートとサブウィンドウIDを指定して最初のメソッドを呼び出した結果を返します。

WinForm Panelオブジェクトを作成するためのメソッドは、まったく同じ方法で設定されます。

//--- Create the WinForm Panel object
   CForm               *CreateWFPanel(const long chart_id,
                                      const int subwindow,
                                      const string name,
                                      const int x,
                                      const int y,
                                      const int w,
                                      const int h,
                                      color &clr[],
                                      const uchar opacity,
                                      const bool movable,
                                      const bool v_gradient=true,
                                      const bool c_gradient=false,
                                      const int frame_width=-1,
                                      const ENUM_FRAME_STYLE frame_style=FRAME_STYLE_BEVEL,
                                      const bool shadow=false,
                                      const bool redraw=false)
                          {
                           int obj_id=
                             (
                              v_gradient ?
                                (
                                 !c_gradient ? this.m_graph_objects.CreatePanelVGradient(chart_id,subwindow,name,x,y,w,h,clr,opacity,movable,true,frame_width,frame_style,shadow,redraw) :
                                 this.m_graph_objects.CreatePanelVGradientCicle(chart_id,subwindow,name,x,y,w,h,clr,opacity,movable,true,frame_width,frame_style,shadow,redraw)
                                ) :
                              !v_gradient ?
                                (
                                 !c_gradient ? this.m_graph_objects.CreatePanelHGradient(chart_id,subwindow,name,x,y,w,h,clr,opacity,movable,true,frame_width,frame_style,shadow,redraw) :
                                 this.m_graph_objects.CreatePanelHGradientCicle(chart_id,subwindow,name,x,y,w,h,clr,opacity,movable,true,frame_width,frame_style,shadow,redraw)
                                ) :
                              WRONG_VALUE
                             );
                           return this.GetWFPanel(obj_id);
                          }
//--- Create the WinForm Panel object in the specified subwindow on the current chart
   CForm               *CreateWFPanel(const int subwindow,
                                      const string name,
                                      const int x,
                                      const int y,
                                      const int w,
                                      const int h,
                                      color &clr[],
                                      const uchar opacity,
                                      const bool movable,
                                      const bool v_gradient=true,
                                      const bool c_gradient=false,
                                      const int frame_width=-1,
                                      const ENUM_FRAME_STYLE frame_style=FRAME_STYLE_BEVEL,
                                      const bool shadow=false,
                                      const bool redraw=false)
                          {
                           return this.CreateWFPanel(::ChartID(),subwindow,name,x,y,w,h,clr,opacity,movable,v_gradient,c_gradient,frame_width,frame_style,shadow,redraw);
                          }
//--- Create the WinForm Panel object in the main window of the current chart
   CForm               *CreateWFPanel(const string name,
                                      const int x,
                                      const int y,
                                      const int w,
                                      const int h,
                                      color &clr[],
                                      const uchar opacity,
                                      const bool movable,
                                      const bool v_gradient=true,
                                      const bool c_gradient=false,
                                      const int frame_width=-1,
                                      const ENUM_FRAME_STYLE frame_style=FRAME_STYLE_BEVEL,
                                      const bool shadow=false,
                                      const bool redraw=false)
                          {
                           return this.CreateWFPanel(::ChartID(),0,name,x,y,w,h,clr,opacity,movable,v_gradient,c_gradient,frame_width,frame_style,shadow,redraw);
                          }

これらのメソッドは、独立した分析のために残しておきます。それらはすべて上記で説明したものと同じです。

クラスコンストラクタで 、グラフィカルオブジェクト名のプレフィックスを作成します

//+------------------------------------------------------------------+
//| CEngine constructor                                              |
//+------------------------------------------------------------------+
CEngine::CEngine() : m_first_start(true),
                     m_last_trade_event(TRADE_EVENT_NO_EVENT),
                     m_last_account_event(WRONG_VALUE),
                     m_last_symbol_event(WRONG_VALUE),
                     m_global_error(ERR_SUCCESS)
  {
   this.m_is_hedge=#ifdef __MQL4__ true #else bool(::AccountInfoInteger(ACCOUNT_MARGIN_MODE)==ACCOUNT_MARGIN_MODE_RETAIL_HEDGING) #endif;
   this.m_is_tester=::MQLInfoInteger(MQL_TESTER);
   this.m_program_type=(ENUM_PROGRAM_TYPE)::MQLInfoInteger(MQL_PROGRAM_TYPE);
   this.m_name_program=::MQLInfoString(MQL_PROGRAM_NAME);
   this.m_name_prefix=this.m_name_program+"_";
   
//--- ...

//--- ...

  }

お気づきかもしれませんが、m_name変数はその目的をより正確に示すためにm_name_program に名前変更されました。


検証

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

以前のEAと比較して、コントロールの作成を簡素化しました。CEngineクラスは、作成されたオブジェクトへのポインタを作成および同時に取得するためのメソッドを備えています。パネルオブジェクトを作成したら、それにバインドされた5つのフォームオブジェクトを作成しましょう

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- Set EA global variables
   ArrayResize(array_clr,2);        // Array of gradient filling colors
   array_clr[0]=C'26,100,128';      // Original ≈Dark-azure color
   array_clr[1]=C'35,133,169';      // Lightened original color
//--- Create the array with the current symbol and set it to be used in the library
   string array[1]={Symbol()};
   engine.SetUsedSymbols(array);
   //--- Create the timeseries object for the current symbol and period, and show its description in the journal
   engine.SeriesCreate(Symbol(),Period());
   engine.GetTimeSeriesCollection().PrintShort(false); // Short descriptions
//--- Create form objects
   string name="";
   int obj_id=WRONG_VALUE;
   CArrayObj *list=NULL;
   CForm *form=NULL;
   for(int i=0;i<FORMS_TOTAL;i++)
     {
      form=engine.CreateWFForm("Form_0"+string(i+1),30,(form==NULL ? 100 : form.BottomEdge()+20),100,30,array_clr,245,true);
      if(form==NULL)
         continue;
      //--- Set ZOrder to zero, display the text describing the gradient type and update the form
      //--- Text parameters: the text coordinates and the anchor point in the form center
      //--- Create a new text animation frame with the ID of 0 and display the text on the form
      form.SetZorder(0,false);
      form.TextOnBG(0,form.TypeElementDescription()+": ID "+(string)form.ID()+", ZD "+(string)form.Zorder(),form.Width()/2,form.Height()/2,FRAME_ANCHOR_CENTER,C'211,233,149',255,true,false);
     }
//--- Create four graphical elements
   CGCnvElement *elm=NULL;
   array_clr[0]=C'0x65,0xA4,0xA9';
   array_clr[1]=C'0x48,0x75,0xA2';
//--- Vertical gradient
   elm=engine.CreateWFElement("CElmVG",form.RightEdge()+20,20,200,50,array_clr,127,true);
   if(elm!=NULL)
     {
      elm.SetFontSize(10);
      elm.Text(elm.Width()/2,elm.Height()/2,elm.TypeElementDescription()+": ID "+(string)elm.ID()+", ZD "+(string)elm.Zorder(),C'0xDB,0xEE,0xF2',elm.Opacity(),FRAME_ANCHOR_CENTER);
      elm.Update();
     }
//--- Vertical cyclic gradient
   elm=engine.CreateWFElement("CElmVGC",form.RightEdge()+20,80,200,50,array_clr,127,true,true);
   if(elm!=NULL)
     {
      elm.SetFontSize(10);
      elm.Text(elm.Width()/2,elm.Height()/2,elm.TypeElementDescription()+": ID "+(string)elm.ID()+", ZD "+(string)elm.Zorder(),C'0xDB,0xEE,0xF2',elm.Opacity(),FRAME_ANCHOR_CENTER);
      elm.Update();
     }
//--- Horizontal gradient
   elm=engine.CreateWFElement("CElmHG",form.RightEdge()+20,140,200,50,array_clr,127,false,false);
   if(elm!=NULL)
     {
      elm.SetFontSize(10);
      elm.Text(elm.Width()/2,elm.Height()/2,elm.TypeElementDescription()+": ID "+(string)elm.ID()+", ZD "+(string)elm.Zorder(),C'0xDB,0xEE,0xF2',elm.Opacity(),FRAME_ANCHOR_CENTER);
      elm.Update();
     }
//--- Horizontal cyclic gradient
   elm=engine.CreateWFElement("CElmHGC",form.RightEdge()+20,200,200,50,array_clr,127,false,true);
   if(elm!=NULL)
     {
      elm.SetFontSize(10);
      elm.Text(elm.Width()/2,elm.Height()/2,elm.TypeElementDescription()+": ID "+(string)elm.ID()+", ZD "+(string)elm.Zorder(),C'0xDB,0xEE,0xF2',elm.Opacity(),FRAME_ANCHOR_CENTER);
      elm.Update();
     }
//--- Create WinForms Panel object
   CPanel *pnl=NULL;
   pnl=engine.CreateWFPanel("WFPanel",elm.RightEdge()+20,50,230,150,array_clr,200,true,true,false,-1,FRAME_STYLE_BEVEL,true);
   if(pnl!=NULL)
     {
      pnl.FontDrawStyle(FONT_STYLE_NORMAL);
      pnl.Bold(true);
      pnl.SetFontSize(10);
      pnl.TextOnBG(0,pnl.TypeElementDescription()+": ID "+(string)pnl.ID()+", ZD "+(string)pnl.Zorder(),pnl.Width()/2,pnl.Height()/2,FRAME_ANCHOR_CENTER,C'211,233,149',pnl.Opacity());
      //--- In the loop, create five bound form objects located horizontally on top of the panel indented by 4 pixels from each other
      CGCnvElement *obj=NULL;
      for(int i=0;i<5;i++)
        {
         //--- create a form object with calculated coordinates along the X axis 
         pnl.CreateNewElement(GRAPH_ELEMENT_TYPE_FORM,(obj==NULL ? 3 : obj.RightEdgeRelative()+4),3,40,30,C'0xCD,0xDA,0xD7',200,true);
         //--- To control the creation of bound objects,
         //--- get the pointer to the bound object by the loop index
         obj=pnl.GetElement(i);
         //--- take the pointer to the base object from the obtained object
         CPanel *p=obj.GetBase();
         //--- and display the name of a created bound object and the name of its base object in the journal
         Print
           (
            TextByLanguage("Объект ","Object "),obj.TypeElementDescription()," ",obj.Name(),
            TextByLanguage(" привязан к объекту "," is attached to object "),p.TypeElementDescription()," ",p.Name()
           );
        }
      pnl.Update(true);
     }
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+

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


ご覧のとおり、パネルに接続されているすべてのオブジェクトが正常に作成されています。パネルオブジェクトの影は、他のチャートオブジェクトの上に配置され、作成されたオブジェクトの後に従います。垂直線やその他の標準のグラフィックオブジェクトを作成する場合、固定のグラフィック要素を除くすべてのコントロールは、新しく作成されたグラフィックオブジェクトの上に残ります。

パネルに接続されたコントロールを作成すると、操作ログには、バインドされた各オブジェクトに基本オブジェクトへのポインタがあるかどうかを示すエントリが表示されます。

Object Form TestDoEasyPart103_WFPanel_Elm01 is attached to object Control element "Panel" TestDoEasyPart103_WFPanel
Object Form TestDoEasyPart103_WFPanel_Elm02 is attached to object Control element "Panel" TestDoEasyPart103_WFPanel
Object Form TestDoEasyPart103_WFPanel_Elm03 is attached to object Control element "Panel" TestDoEasyPart103_WFPanel
Object Form TestDoEasyPart103_WFPanel_Elm04 is attached to object Control element "Panel" TestDoEasyPart103_WFPanel
Object Form TestDoEasyPart103_WFPanel_Elm05 is attached to object Control element "Panel" TestDoEasyPart103_WFPanel


次の段階

次の記事では、Panelコントロールの機能に関する作業を続けます。

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

目次に戻る

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

DoEasyコントロール(第1部):最初のステップ
DoEasyコントロール(第2部):CPanelクラスでの作業


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

添付されたファイル |
MQL5.zip (4337.26 KB)
ADXによる取引システムの設計方法を学ぶ ADXによる取引システムの設計方法を学ぶ
今回は、最も人気のある指標を使って取引システムを設計する連載の続きとして、ADX (Average Directional Index)指標についてお話します。この指標を理解するために詳しく学び、簡単な戦略でその使い方を学びます。深く学ぶことで、より多くの洞察得ることができ、それをよりよく活用することができるのです。
ストキャスティクスによる取引システムの設計方法を学ぶ ストキャスティクスによる取引システムの設計方法を学ぶ
この記事では、学習シリーズを継続します。今回は、基本的な知識の新しいブロックを構築するために、最も人気があり、便利な指標の1つであるストキャスティックスオシレータ指標を使用して取引システムを設計する方法を学びます。
DoEasy - コントロール(第4部):パネルコントロールとPadding and Dockパラメータ DoEasy - コントロール(第4部):パネルコントロールとPadding and Dockパラメータ
今回は、Paddingパラメータ(要素の四辺の内部インデント/マージン)とDockパラメータ(コンテナ内のオブジェクトの配置方法)の扱いを実装します。
データサイエンスと機械学習(第02回):ロジスティック回帰 データサイエンスと機械学習(第02回):ロジスティック回帰
データ分類は、アルゴトレーダーとプログラマーにとって非常に重要なものです。この記事では、「はい」と「いいえ」、上と下、買いと売りを識別するのに役立つ可能性のある分類ロジスティックアルゴリズムの1つに焦点を当てます。