
DoEasy - コントロール(第3部):バインドされたコントロールの作成
内容
概念
本稿では、グラフィック要素にバインドされたグラフィックコントロールを開発するための機能を作成することを検討します。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コントロールの機能に関する作業を続けます。
**連載のこれまでの記事:
DoEasyコントロール(第1部):最初のステップ
DoEasyコントロール(第2部):CPanelクラスでの作業
MetaQuotes Ltdによってロシア語から翻訳されました。
元の記事: https://www.mql5.com/ru/articles/10733





- 無料取引アプリ
- 8千を超えるシグナルをコピー
- 金融ニュースで金融マーケットを探索