
DoEasy - コントロール(第5部):WinForms基本オブジェクト、Panelコントロール、AutoSizeパラメータ
内容
概念
パネルオブジェクトのAutoSizeとAutoSizeModeプロパティを実装する前に、すべてのライブラリWinFormsオブジェクトの基本クラスを作成することにします。このようなオブジェクトのプロパティの多くは互いに継承されるので、作業中のオブジェクトのパネルに固有のプロパティは、他のWinFormsオブジェクトにも同様に使用することができます。各オブジェクトに同じようなプロパティを設定しなくてよいように、このタイプの他のオブジェクトが継承されるベースとなるWinFormsオブジェクトを作成することにします。基本オブジェクト自体は、マウスとのインタラクションが実装されているフォームオブジェクトクラスから継承されます。
パネル内にあるオブジェクトのDockプロパティ(前回の記事で考察)が有効な場合、そのオブジェクトはコンテナの境界線に「くっつきます」。コンテナの境界はDockModeプロパティで指定されます。この場合、パネル内に配置された後続の各オブジェクトが、そのコンテナ(パネル)の側面に対するバインドが、パネル内に配置された前のオブジェクトと同じであれば、コンテナの指定した側面ではなく、前のオブジェクトの最も近い側面に接続されることになります。したがって、パネル内に配置され、例えばコンテナの左端にバインドされたオブジェクトは、左から右へ一列に並べられることになります。パネルのAutoSizeモードが有効な場合、コンテナの中に一列に並んだすべてのオブジェクトがコンテナからはみ出さないように、コンテナの幅が自動的に広げられます。パネルの端からはみ出したオブジェクトをコンテナの中に入れても、同じように動作するはずです。パネルのAutoSizeモードが有効な場合、オブジェクトがその限界を超えないように、パネルの側面のサイズを調整する必要があります。
同時に、オブジェクトがコンテナの一辺に取り付けられているか、コンテナ自体がAutoSizeプロパティを有効にしているかどうかの違いもあります。
AutoSizeを有効にしてコンテナ内に配置されたすべてのオブジェクトは、コンテナに付属するオブジェクトのリストで、オブジェクトのシリアル番号で指定された優先順位によって配置されます。これにより、コンテナ内のオブジェクトの位置をあらかじめ決めておくことができます。コンテナのサイズはバインドされているすべての要素の合計サイズに自動的に調整されます。これについては、次回以降の記事で考えていきます。今日は、新しいWinForms基本オブジェクトを作成し、パネルから直接それに接続された要素を作成するときにAutosizeプロパティを実装するタスクに従って、ライブラリクラスを改善します。
指定された作業に加えて、パネル内のグラフィカル要素の構築を少し最適化しましょう。まず、パネルに付属するすべての要素を通し番号とバインド値に従って配置し(Dock)、自動サイズ プロパティが設定されていて本当に必要な場合は、内部コンテンツに合わせてパネルのサイズを変更します。まず、すべての要素をパネル内に「仮想的に」配置する必要があります。次に、パネルのサイズを変更する必要があるかどうかを確認し、必要に応じて変更します。パネル内にあるすべての要素を再描画するのはその後です。その代わりに、要素をすぐに描画すると、サイズとバインド点の変更がリアルタイムに表示され、パネル周辺にさまざまなレンダリングアーチファクトが発生します。したがって、まず要素を正しい順序で並べ、必要に応じてサイズを変更し(要素のバインド方法とバインドされた要素のリスト内の番号に依存)、次にパネルのサイズをどのくらい変更する必要があるか計算します。これがすべて終わったら、パネルとその中にある要素を新しい順序で再描画します。
ライブラリクラスの改善
ここでは新しいライブラリオブジェクトを実装するので、そのタイプをライブラリオブジェクトのタイプのリストに追加する必要があります。また、パネルアンダーレイオブジェクトにアクセスする際には、オブジェクトのタイプを知る必要があります。
\MQL5\Include\DoEasy\Defines.mqhのライブラリオブジェクトタイプのリストに、新しいタイプ(WinForms基本オブジェクト)を入力します。
//+------------------------------------------------------------------+ //| List of library object types | //+------------------------------------------------------------------+ enum ENUM_OBJECT_DE_TYPE { //--- Graphics OBJECT_DE_TYPE_GBASE = COLLECTION_ID_LIST_END+1, // "Base object of all library graphical objects" object type OBJECT_DE_TYPE_GELEMENT, // "Graphical element" object type OBJECT_DE_TYPE_GFORM, // Form object type OBJECT_DE_TYPE_GFORM_CONTROL, // "Form for managing pivot points of graphical object" object type OBJECT_DE_TYPE_GSHADOW, // Shadow object type //--- WinForms OBJECT_DE_TYPE_GWF_BASE, // WinForms Base object type (base abstract WinForms object) OBJECT_DE_TYPE_GWF_PANEL, // WinForms Panel object type //--- Animation OBJECT_DE_TYPE_GFRAME, // "Single animation frame" object type //--- ... //--- ... }
グラフィカル要素のタイプのリストに、グラフィカルアンダーレイ要素とWinForms基本オブジェクトの2つの新しいタイプを追加します。また、GRAPH_ELEMENT_TYPE_PANELマクロ置換をGRAPH_ELEMENT_TYPE_WF_PANELに名前変更します。
//+------------------------------------------------------------------+ //| 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_WF_UNDERLAY, // Panel object underlay GRAPH_ELEMENT_TYPE_WF_BASE, // Windows Forms Base GRAPH_ELEMENT_TYPE_WF_PANEL, // Windows Forms Panel }; //+------------------------------------------------------------------+
これらのタイプによって、現在どのオブジェクトが選択されているか、そしてこれが必要なタイプである場合にそれを処理する方法がわかります。
\MQL5\Include\DoEasy\Data.mqhに、ライブラリの新しいメッセージインデックスを追加します。
MSG_GRAPH_ELEMENT_TYPE_WINDOW, // Window MSG_GRAPH_ELEMENT_TYPE_WF_UNDERLAY, // Underlay of the Panel WinForms control object MSG_GRAPH_ELEMENT_TYPE_WF_BASE, // WinForms base control MSG_GRAPH_ELEMENT_TYPE_WF_PANEL, // Panel control
また、新しく追加されたインデックスに対応するテキストも追加します。
{"Окно","Window"}, {"Подложка объекта-элемента управления WinForms \"Панель\"","Underlay object-control WinForms \"Panel\""}, {"Базовый элемент управления WinForms","Base WinForms control"}, {"Элемент управления \"Panel\"","Control element \"Panel\""},
すべてのライブラリグラフィカルオブジェクトは、CGBaseObjライブラリ基本グラフィカルオブジェクトクラスから派生しています。このクラスには、任意のライブラリグラフィックオブジェクトを処理するためのすべての基本メソッドが含まれています。\MQL5\Include\DoEasy\Objects\Graph\GBaseObj.mqhのグラフィカル要素タイプの説明を返すメソッドで、2つの新しいタイプのライブラリグラフィカル要素の説明を表示するようにします。
//+------------------------------------------------------------------+ //| Return the description of the graphical element type | //+------------------------------------------------------------------+ string CGBaseObj::TypeElementDescription(void) { return ( this.TypeGraphElement()==GRAPH_ELEMENT_TYPE_STANDARD ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_STANDARD) : this.TypeGraphElement()==GRAPH_ELEMENT_TYPE_STANDARD_EXTENDED ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_STANDARD_EXTENDED) : this.TypeGraphElement()==GRAPH_ELEMENT_TYPE_ELEMENT ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_ELEMENT) : this.TypeGraphElement()==GRAPH_ELEMENT_TYPE_SHADOW_OBJ ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_SHADOW_OBJ) : this.TypeGraphElement()==GRAPH_ELEMENT_TYPE_FORM ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_FORM) : this.TypeGraphElement()==GRAPH_ELEMENT_TYPE_WINDOW ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WINDOW) : //--- this.TypeGraphElement()==GRAPH_ELEMENT_TYPE_WF_UNDERLAY ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_UNDERLAY) : this.TypeGraphElement()==GRAPH_ELEMENT_TYPE_WF_BASE ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_BASE) : this.TypeGraphElement()==GRAPH_ELEMENT_TYPE_WF_PANEL ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_PANEL) : "Unknown" ); } //+------------------------------------------------------------------+
ライブラリグラフィックの残りのCanvasオブジェクトが派生しているキャンバス上のグラフィック要素オブジェクトのクラスで、いくつかのメソッドを仮想化することにしましょう。このクラスは\MQL5\Include\DoEasy\Objects\Graph\GCnvElement.mqhにあります。
クラスの一部のメソッドの実装が、そのクラスの子孫オブジェクトに不適切であることがわかるかもしれません。仮想化すれば、子孫クラスでこれらのメソッドを変更して、クラスで作成したメソッドと異なって実装できるようになります。
//+------------------------------------------------------------------+ //| Methods of simplified access to object properties | //+------------------------------------------------------------------+ //--- Set the (1) X, (2) Y coordinates, (3) element width, (4) height, (5) right (6) and bottom edge, virtual bool SetCoordX(const int coord_x); virtual bool SetCoordY(const int coord_y); virtual bool SetWidth(const int width); virtual bool SetHeight(const int height); void SetRightEdge(void) { this.SetProperty(CANV_ELEMENT_PROP_RIGHT,this.RightEdge()); } void SetBottomEdge(void) { this.SetProperty(CANV_ELEMENT_PROP_BOTTOM,this.BottomEdge()); }
...
//+------------------------------------------------------------------+ //| The methods of filling, clearing and updating raster data | //+------------------------------------------------------------------+ //--- Clear the element filling it with color and opacity virtual void Erase(const color colour,const uchar opacity,const bool redraw=false); //--- Clear the element with a gradient fill virtual void Erase(color &colors[],const uchar opacity,const bool vgradient,const bool cycle,const bool redraw=false); //--- Clear the element completely virtual void Erase(const bool redraw=false); //--- Update the element void Update(const bool redraw=false) { this.m_canvas.Update(redraw); }
これらのメソッドの異なる実装が必要な子孫クラスでは、まったく同じ仮想メソッドを書きますが、ここで表示されているものとは異なる独自の実装を持ちます。親クラスの仮想メソッドにアクセスする際に呼び出されるのは、まさにその子孫クラスの仮想メソッドです。
現在改良中のいくつかのメソッドに、グラフィカルオブジェクトの再描画の必要性を示すフラグを追加する必要があります。これにより、リストから各オブジェクトを常に再描画するのではなく、まずリスト内のすべてのオブジェクトを処理する(例えば、各オブジェクトのサイズを変更して新しい場所に移動する)ように、オブジェクトリストの処理を最適化することができます。リスト全体の処理が終了したら、単に各オブジェクトを新しい座標または新しいサイズで再描画します。これは、寸法や座標を変更した直後にリスト内の各オブジェクトを再描画するよりも視覚的に高速になります。
影オブジェクトクラスファイル(\MQL5\Include\DoEasy\Objects\Graph\ShadowObj.mqh)で、変数名とメソッド名から「shadow」を削除します。ここでは冗長だと思います。
影のパラメータは同じですが、新しいサイズで完全に再描画する必要があるため、影のぼかし半径を格納する別の変数、新しい変数にアクセスする2つのメソッド、影オブジェクト全体を再描画する必要があるというフラグを影の再描画メソッドに追加します。
//+------------------------------------------------------------------+ //| Shadows object class | //+------------------------------------------------------------------+ class CShadowObj : public CGCnvElement { private: color m_color; // Shadow color uchar m_opacity; // Shadow opacity uchar m_blur; // Blur //--- Gaussian blur bool GaussianBlur(const uint radius); //--- Return the array of weight ratios bool GetQuadratureWeights(const double mu0,const int n,double &weights[]); //--- Draw the object shadow form void DrawShadowFigureRect(const int w,const int h); public: //--- Constructor CShadowObj(const long chart_id, const int subwindow, const string name, const int x, const int y, const int w, const int h); //--- Supported object properties (1) integer and (2) string ones virtual bool SupportProperty(ENUM_CANV_ELEMENT_PROP_INTEGER property) { return true; } virtual bool SupportProperty(ENUM_CANV_ELEMENT_PROP_STRING property) { return true; } //--- Draw an object shadow void Draw(const int shift_x,const int shift_y,const uchar blur_value,const bool redraw); //+------------------------------------------------------------------+ //| Methods of simplified access to object properties | //+------------------------------------------------------------------+ //--- (1) Set and (2) return the shadow color void SetColor(const color colour) { this.m_color=colour; } color Color(void) const { return this.m_color; } //--- (1) Set and (2) return the shadow opacity void SetOpacity(const uchar opacity) { this.m_opacity=opacity; } uchar Opacity(void) const { return this.m_opacity; } //--- (1) Set and (2) return the shadow blur void SetBlur(const uchar blur) { this.m_blur=blur; } uchar Blur(void) const { return this.m_blur; } }; //+------------------------------------------------------------------+
クラスのコンストラクタで、ライブラリのDefines.mqhファイルのCLR_DEF_SHADOW_OPACITYマクロ置換で指定されたデフォルトの影の不透明度を追加し、同じファイルからDEF_SHADOW_BLUR マクロ置換を使ってデフォルトの影のぼかしを指定します。
//+------------------------------------------------------------------+ //| 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=CLR_DEF_SHADOW_OPACITY; this.m_blur=DEF_SHADOW_BLUR; color gray=CGCnvElement::ChangeColorSaturation(ChartColorBackground(),-100); this.m_color=CGCnvElement::ChangeColorLightness(gray,-50); this.m_shadow=false; this.m_visible=true; CGCnvElement::Erase(); } //+------------------------------------------------------------------+
影オブジェクトを描画するメソッドの実装において、影の再描画フラグを明確に指定するようにしました。ローカル変数「radius」の代わりに、新しい変数m_blurを使うことにします。これにより、後に影オブジェクトを元々描画されていたパラメータで再描画するために影のぼかしの値を保存することができます。
//+------------------------------------------------------------------+ //| Draw the object shadow | //+------------------------------------------------------------------+ void CShadowObj::Draw(const int shift_x,const int shift_y,const uchar blur_value,const bool redraw) { //--- Set the shadow shift values to the variables by X and Y axes this.SetCoordXRelative(shift_x); this.SetCoordYRelative(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 this.m_blur=(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(this.m_blur)) return; //--- Shift the shadow object by X/Y offsets specified in the method arguments and update the canvas CGCnvElement::Move(this.CoordX()+this.CoordXRelative(),this.CoordY()+this.CoordYRelative(),redraw); CGCnvElement::Update(redraw); } //+------------------------------------------------------------------+
少し後にすべてのライブラリWinFormsオブジェクトの基本オブジェクトを実装するつもりなので、実装されたWinFormsオブジェクトクラスとその親クラスのいくつかの変数とメソッドは、新しい基本クラスかその親であるCFormクラスに渡される必要があります。すべての変数とメソッドは、必要とされる継承階層のそれらのクラスのレベルで利用可能なままになります。
例えば、オブジェクトを作成すると、その座標とサイズはCPanelのクラス変数に格納されます。他のWinFormsオブジェクトでも同じデータが必要になるため、それらを\MQL5\Include\DoEasy\Objects\Graph\Form.mqhのオブジェクトの親クラスに移動します。
一見すると、これらの変数やメソッドをWinFormsオブジェクトの基本クラスに移動させるのが論理的でしょう。しかし、そのデータは、クラスがすべてのWinFormsオブジェクトの基本オブジェクトのクラスの親であるフォームオブジェクトのために有用であることが判明することもあります。これらの変数をそこに移動させましょう。
//+------------------------------------------------------------------+ //| Form object class | //+------------------------------------------------------------------+ class CForm : public CGCnvElement { private: CArrayObj m_list_elements; // List of attached elements CAnimations *m_animations; // Pointer to the animation object CShadowObj *m_shadow_obj; // Pointer to the shadow object CMouseState m_mouse; // "Mouse status" class object ENUM_MOUSE_FORM_STATE m_mouse_form_state; // Mouse status relative to the form ushort m_mouse_state_flags; // Mouse status flags color m_color_frame; // Form frame color int m_offset_x; // Offset of the X coordinate relative to the cursor int m_offset_y; // Offset of the Y coordinate relative to the cursor int m_init_x; // Newly created form X coordinate int m_init_y; // Newly created form Y coordinate int m_init_w; // Newly created form width int m_init_h; // Newly created form height //--- Reset the array size of (1) text, (2) rectangular and (3) geometric animation frames void ResetArrayFrameT(void); void ResetArrayFrameQ(void); void ResetArrayFrameG(void); //--- Create a new graphical object
CreateNewElement()メソッドを2つに分割して、コードを最適化しましょう。さらにもう1つ、新しい要素を作成してリストに追加するCreateAndAddNewElement()メソッドを作成します。クラスのprotectedセクションでメソッドを宣言します。初期座標とオブジェクトサイズを格納する変数を扱うメソッドを、CPanelクラスからpublicセクションに移動します。
protected: CArrayObj m_list_tmp; // List for storing the pointers 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; } //--- Update coordinates of bound objects virtual bool MoveDependentObj(const int x,const int y,const bool redraw=false); //--- Create a new bound element and add it to the list of bound objects CGCnvElement *CreateAndAddNewElement(const ENUM_GRAPH_ELEMENT_TYPE element_type, CGCnvElement *main, const int x, const int y, const int w, const int h, const color colour, const uchar opacity, const bool activity); public: //--- Return the initial (1) X and (2) Y coordinate, (3) form width and (4) height int GetCoordXInit(void) const { return this.m_init_x; } int GetCoordYInit(void) const { return this.m_init_y; } int GetWidthInit(void) const { return this.m_init_w; } int GetHeightInit(void) const { return this.m_init_h; } //--- Set the initial (1) X and (2) Y coordinate, (3) form width and (4) height void SetCoordXInit(const int value) { this.m_init_x=value; } void SetCoordYInit(const int value) { this.m_init_y=value; } void SetWidthInit(const int value) { this.m_init_w=value; } void SetHeightInit(const int value) { this.m_init_h=value; } //--- 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(); }
CreateNewElement()メソッドを仮想化し、新たに生成したオブジェクトのレンダリングが必要であることを示すフラグを追加します。
//--- Create a new attached element virtual bool CreateNewElement(const ENUM_GRAPH_ELEMENT_TYPE element_type, CGCnvElement *main, const int x, const int y, const int w, const int h, const color colour, const uchar opacity, const bool activity, const bool redraw); //--- Add a new attached element bool AddNewElement(CGCnvElement *obj,const int x,const int y);
継承した各クラスに独自の実装を持つ同じメソッドを作ることができるようにこのメソッドを仮想化しました。オブジェクト再描画フラグは、作成の直後にオブジェクトを1つずつ表示するのを避けるために必要です。その代わり、まずすべてのオブジェクトを作成し、次に作成したパネルのサイズを調整し、最後にすべてをレンダリングするようにします。変更されたパネルサイズを表示する際に、ループ内で同時に作成されたすべてのオブジェクトから後続の各オブジェクトを作成する際に生じる視覚的なアーティファクトを回避することができるため、これははるかに高速になります。
オブジェクトプロパティへの簡単なアクセスのためのメソッドのブロックで、「Shadow blur」プロパティを処理するための2つの新しいメソッドを設定します。
//+------------------------------------------------------------------+ //| Methods of simplified access to object properties | //+------------------------------------------------------------------+ //--- (1) Set and (2) get the form frame color void SetColorFrame(const color colour) { this.m_color_frame=colour; } color ColorFrame(void) const { return this.m_color_frame; } //--- (1) Set and (2) return the form shadow color void SetColorShadow(const color colour); color ColorShadow(void) const; //--- (1) Set and (2) return the form shadow opacity void SetOpacityShadow(const uchar opacity); uchar OpacityShadow(void) const; //--- (1) Set and (2) return the form shadow blur void SetBlurShadow(const uchar blur); uchar BlurShadow(void) const; }; //+------------------------------------------------------------------+
プロパティ初期化メソッドを呼び出した後、オブジェクトの初期サイズと座標を格納するプロパティの初期化メソッドをすべてのパラメトリックコンストラクタに呼び出すようにします。
//+------------------------------------------------------------------+ //| Constructor indicating the chart and subwindow ID | //+------------------------------------------------------------------+ CForm::CForm(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_FORM,chart_id,subwindow,name,x,y,w,h) { this.m_type=OBJECT_DE_TYPE_GFORM; this.Initialize(); this.SetCoordXInit(x); this.SetCoordYInit(y); this.SetWidthInit(w); this.SetHeightInit(h); } //+------------------------------------------------------------------+ //| Current chart constructor specifying the subwindow | //+------------------------------------------------------------------+ CForm::CForm(const int subwindow, const string name, const int x, const int y, const int w, const int h) : CGCnvElement(GRAPH_ELEMENT_TYPE_FORM,::ChartID(),subwindow,name,x,y,w,h) { this.m_type=OBJECT_DE_TYPE_GFORM; this.Initialize(); this.SetCoordXInit(x); this.SetCoordYInit(y); this.SetWidthInit(w); this.SetHeightInit(h); } //+------------------------------------------------------------------+ //| Constructor on the current chart in the main chart window | //+------------------------------------------------------------------+ CForm::CForm(const string name, const int x, const int y, const int w, const int h) : CGCnvElement(GRAPH_ELEMENT_TYPE_FORM,::ChartID(),0,name,x,y,w,h) { this.m_type=OBJECT_DE_TYPE_GFORM; this.Initialize(); this.SetCoordXInit(x); this.SetCoordYInit(y); this.SetWidthInit(w); this.SetHeightInit(h); } //+------------------------------------------------------------------+
初期化メソッド自体で、これらの値にゼロを設定します。
//+------------------------------------------------------------------+ //| Initialize the variables | //+------------------------------------------------------------------+ void CForm::Initialize(void) { this.m_list_elements.Clear(); this.m_list_elements.Sort(); this.m_list_tmp.Clear(); this.m_list_tmp.Sort(); this.m_shadow_obj=NULL; this.m_shadow=false; this.m_frame_width_right=DEF_FRAME_WIDTH_SIZE; this.m_frame_width_left=DEF_FRAME_WIDTH_SIZE; this.m_frame_width_top=DEF_FRAME_WIDTH_SIZE; this.m_frame_width_bottom=DEF_FRAME_WIDTH_SIZE; this.m_gradient_v=true; this.m_gradient_c=false; this.m_mouse_state_flags=0; this.m_offset_x=0; this.m_offset_y=0; this.m_init_x=0; this.m_init_y=0; this.m_init_w=0; this.m_init_h=0; CGCnvElement::SetInteraction(false); this.m_animations=new CAnimations(CGCnvElement::GetObject()); this.m_list_tmp.Add(m_animations); } //+------------------------------------------------------------------+
以下は、新しい接続済み要素を作成し、接続済みオブジェクトのリストに追加するメソッドです。
//+------------------------------------------------------------------+ //| Create a new attached element | //| and add it to the list of bound objects | //+------------------------------------------------------------------+ CGCnvElement *CForm::CreateAndAddNewElement(const ENUM_GRAPH_ELEMENT_TYPE element_type, CGCnvElement *main, const int x, const int y, const int w, const int h, const color colour, const uchar opacity, const bool activity) { //--- If the type of a created graphical element is less than the "element", inform of that and return 'false' if(element_type<GRAPH_ELEMENT_TYPE_ELEMENT) { ::Print(DFUN,CMessage::Text(MSG_FORM_OBJECT_ERR_NOT_INTENDED),::StringSubstr(::EnumToString(element_type),19)); return NULL; } //--- Specify the element index in the list int num=this.m_list_elements.Total(); //--- Create a graphical element name string ns=(::StringLen((string)num)<2 ? ::IntegerToString(num,2,'0') : (string)num); string name="Elm"+ns; //--- Get the screen coordinates of the object relative to the coordinate system of the base object int elm_x=x; int elm_y=y; this.GetCoords(elm_x,elm_y); //--- Create a new graphical element CGCnvElement *obj=this.CreateNewGObject(element_type,num,name,elm_x,elm_y,w,h,colour,opacity,false,activity); if(obj==NULL) return NULL; //--- and add it to the list of bound graphical elements if(!this.AddNewElement(obj,elm_x,elm_y)) { delete obj; return NULL; } //--- Set the minimum properties for a bound graphical element obj.SetColorBackground(colour); obj.SetOpacity(opacity); obj.SetActive(activity); obj.SetMain(main); obj.SetBase(this.GetObject()); obj.SetID(this.ID()); obj.SetNumber(num); obj.SetCoordXRelative(x); obj.SetCoordYRelative(y); obj.SetZorder(this.Zorder(),false); obj.SetCoordXRelativeInit(x); obj.SetCoordYRelativeInit(y); return obj; } //+------------------------------------------------------------------+
このメソッドのロジックは、基本的にCreateNewElement()メソッドのロジックを繰り返していますが、作成した要素を無条件にレンダリングすることはありません。このメソッドは、正常に作成されたグラフィック要素へのポインタを返すだけです。要素の作成またはリストへの追加時にエラーが発生した場合はNULLを返します。ここでは細かいミスも修正しました。バインドされたオブジェクトのリストに、各オブジェクトのインデックスが表示されるようになりました。
接続済み要素を新規作成するメソッドの見た目が変わりました。新しいオブジェクトを追加してリストに追加するコード全体が、上で検討したメソッドに移動して、ここで呼び出されるようになりました。このメソッドは、オブジェクトが生成されないかリストに追加されない場合はfalseを返し、そうでない場合はオブジェクトはその物理的なレンダリングの必要性を示すフラグでレンダリングされ、このメソッドはtrueを返します。
//+------------------------------------------------------------------+ //| Create a new attached element | //+------------------------------------------------------------------+ bool CForm::CreateNewElement(const ENUM_GRAPH_ELEMENT_TYPE element_type, CGCnvElement *main, const int x, const int y, const int w, const int h, const color colour, const uchar opacity, const bool activity, const bool redraw) { //--- Create a new graphical element CGCnvElement *obj=this.CreateAndAddNewElement(element_type,main,x,y,w,h,colour,opacity,activity); //--- If the object has been created, draw the added object and return 'true' if(obj==NULL) return false; obj.Erase(colour,opacity,redraw); return true; } //+------------------------------------------------------------------+
影を描画するメソッドでは、影オブジェクトクラスのDraw() メソッドに影を描画する必要性を示すフラグを渡すようにしました。
//+------------------------------------------------------------------+ //| Draw the shadow | //+------------------------------------------------------------------+ void CForm::DrawShadow(const int shift_x,const int shift_y,const color colour,const uchar opacity=127,const uchar blur=DEF_SHADOW_BLUR) { //--- If the shadow flag is disabled, exit if(!this.m_shadow) return; //--- If there is no shadow object, create it if(this.m_shadow_obj==NULL) this.CreateShadowObj(colour,opacity); //--- If the shadow object exists, draw the shadow on it, //--- set the shadow object visibility flag and //--- move the form object to the foreground if(this.m_shadow_obj!=NULL) { this.m_shadow_obj.Draw(shift_x,shift_y,blur,true); this.m_shadow_obj.SetVisible(true,false); this.BringToTop(); } } //+------------------------------------------------------------------+
ここでは、影を描画するために常にtrueを渡すことにします。影が不要な場合は、これは呼び出し側のメソッドで条件演算子によりチェックされます。これは、ぼかされていない影の四角形のレンダリングを制御し、この四角形のぼかしを使用する前に影が描画されているかどうかを追跡し、関連する他の落とし穴を見つけるよりも簡単です。その代わり、単に影をレンダリングする必要があるかどうかをチェックして、メソッドを呼び出す方がはるかに簡単です。
以下は、フォームの影のぼかしを設定するメソッドです。
//+------------------------------------------------------------------+ //| Set the form shadow blur | //+------------------------------------------------------------------+ void CForm::SetBlurShadow(const uchar blur) { if(this.m_shadow_obj==NULL) { ::Print(DFUN,CMessage::Text(MSG_FORM_OBJECT_TEXT_NO_SHADOW_OBJ_FIRST_CREATE_IT)); return; } this.m_shadow_obj.SetBlur(blur); } //+------------------------------------------------------------------+
影オブジェクトが存在しない場合、影オブジェクトを先に作成することを示すメッセージを表示します。それ以外の場合は、影オブジェクトにぼかし量を設定するメソッドが呼び出されます。
以下は、フォームの影のぼかしを返すメソッドです。
//+------------------------------------------------------------------+ //| Return the form shadow blur | //+------------------------------------------------------------------+ uchar CForm::BlurShadow(void) const { if(this.m_shadow_obj==NULL) { ::Print(DFUN,CMessage::Text(MSG_FORM_OBJECT_TEXT_NO_SHADOW_OBJ_FIRST_CREATE_IT)); return 0; } return this.m_shadow_obj.Blur(); } //+------------------------------------------------------------------+
影オブジェクトが存在しない場合、影オブジェクトを先に作成することを示すメッセージを表示します。それ以外の場合は、影オブジェクトのぼかし量を設定するためのメソッドを返します。
上記の変更点とは別に、このクラスにはロジックに影響を与えない小さな改良が加えられていますが、ここでは説明しません。すべての変更は以下に添付されているファイルでご覧になれます。
WinFormsの基本オブジェクトクラス
\MQL5\Include\DoEasy\Objects\Graph\WForms\で、CWinFormBaseクラスのWinFormBase.mqhを新規作成します。このクラスは、CFormフォームオブジェクトクラスを継承している必要があります。基本クラスがファイル内で見えるようにするために、フォームオブジェクトクラスファイルをそれにインクルードします。
//+------------------------------------------------------------------+ //| WinFormBase.mqh | //| Copyright 2021, MetaQuotes Ltd. | //| https://mql5.com/ja/users/artmedia70 | //+------------------------------------------------------------------+ #property copyright "Copyright 2021, MetaQuotes Ltd." #property link "https://mql5.com/ja/users/artmedia70" #property version "1.00" #property strict // Necessary for mql4 //+------------------------------------------------------------------+ //| Include files | //+------------------------------------------------------------------+ #include "..\Form.mqh" //+------------------------------------------------------------------+ //| Form object class | //+------------------------------------------------------------------+ class CWinFormBase : public CForm { }
それらを処理するための変数とメソッドをPanel WinFormsオブジェクト クラスファイル( \MQL5\Include\DoEasy\Objects\Graph\WForms\Containers\Panel.mqh)からこのクラスに移動します。パネルオブジェクトに実装したすべての変数とメソッドを格納するためのものです。もちろん、新しいものも追加される予定です。
クラスのprotectedセクションに以下の変数を配置します。
//+------------------------------------------------------------------+ //| Form object class | //+------------------------------------------------------------------+ class CWinFormBase : public CForm { protected: color m_fore_color; // Default text color for all control objects ENUM_FW_TYPE m_bold_type; // Font width type ENUM_FRAME_STYLE m_border_style; // Control frame style bool m_autosize; // Flag of the element auto resizing depending on the content ENUM_CANV_ELEMENT_DOCK_MODE m_dock_mode; // Mode of binding control 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 private:
Privateセクションに、フォントフラグを返すメソッドを配置します。
private: //--- Return the font flags uint GetFontFlags(void); public:
Publicセクションで、要素をクリアし、再描画し、サイズを変更するための仮想メソッド、およびクラス コンストラクタを宣言し、そのようなメソッドをCPanelクラス ファイルから移動します。
public: //--- Clear the element filling it with color and opacity virtual void Erase(const color colour,const uchar opacity,const bool redraw=false); //--- Clear the element with a gradient fill virtual void Erase(color &colors[],const uchar opacity,const bool vgradient,const bool cycle,const bool redraw=false); //--- Clear the element completely virtual void Erase(const bool redraw=false); //--- Redraw the object virtual void Redraw(bool redraw); //--- Set the new size for the (1) current object and (2) the object specified by index virtual bool Resize(const int w,const int h,const bool redraw); virtual bool Resize(const int index,const int w,const int h,const bool redraw); //--- Constructors CWinFormBase(const long chart_id, const int subwindow, const string name, const int x, const int y, const int w, const int h); CWinFormBase(const string name) : CForm(::ChartID(),0,name,0,0,0,0) { this.m_type=OBJECT_DE_TYPE_GWF_BASE; } //--- (1) Set and (2) return the default text color of all panel objects void SetForeColor(const color clr) { this.m_fore_color=clr; } color ForeColor(void) const { return this.m_fore_color; } //--- (1) Set and (2) return the Bold font flag void SetBold(const bool flag); bool Bold(void); //--- (1) Set and (2) return the Italic font flag void SetItalic(const bool flag); bool Italic(void); //--- (1) Set and (2) return the Strikeout font flag void SetStrikeout(const bool flag); bool Strikeout(void); //--- (1) Set and (2) return the Underline font flag void SetUnderline(const bool flag); bool Underline(void); //--- (1) Set and (2) return the font style void SetFontDrawStyle(ENUM_FONT_STYLE style); ENUM_FONT_STYLE FontDrawStyle(void); //--- (1) Set and (2) return the font width type void SetFontBoldType(ENUM_FW_TYPE type); ENUM_FW_TYPE FontBoldType(void) const { return this.m_bold_type; } //--- (1) Set and (2) return the frame style void SetBorderStyle(const ENUM_FRAME_STYLE style) { this.m_border_style=style; } ENUM_FRAME_STYLE BorderStyle(void) const { return this.m_border_style; } //--- (1) Set and (2) return the flag of the element auto resizing depending on the content virtual void SetAutoSize(const bool flag,const bool redraw) { this.m_autosize=flag; } bool AutoSize(void) { return this.m_autosize; } //--- (1) Set and (2) return the mode of binding element borders to the container virtual void SetDockMode(const ENUM_CANV_ELEMENT_DOCK_MODE mode,const bool redraw) { this.m_dock_mode=mode; } ENUM_CANV_ELEMENT_DOCK_MODE DockMode(void) const { return this.m_dock_mode; } //--- Set the gap (1) to the left, (2) at the top, (3) to the right, (4) at the bottom and (5) on all sides between the fields of this and another control void SetMarginLeft(const int value) { this.m_margin[0]=value; } void SetMarginTop(const int value) { this.m_margin[1]=value; } void SetMarginRight(const int value) { this.m_margin[2]=value; } void SetMarginBottom(const int value) { this.m_margin[3]=value; } void SetMarginAll(const int value) { this.SetMarginLeft(value); this.SetMarginTop(value); this.SetMarginRight(value); this.SetMarginBottom(value); } //--- Return the gap (1) to the left, (2) at the top, (3) to the right and (4) at the bottom between the fields of this and another control int MarginLeft(void) const { return this.m_margin[0]; } int MarginTop(void) const { return this.m_margin[1]; } int MarginRight(void) const { return this.m_margin[2]; } int MarginBottom(void) const { return this.m_margin[3]; } //--- 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 virtual void SetPaddingLeft(const uint value) { this.m_padding[0]=((int)value<this.m_frame_width_left ? this.m_frame_width_left : (int)value); } virtual void SetPaddingTop(const uint value) { this.m_padding[1]=((int)value<this.m_frame_width_top ? this.m_frame_width_top : (int)value); } virtual void SetPaddingRight(const uint value) { this.m_padding[2]=((int)value<this.m_frame_width_right ? this.m_frame_width_right : (int)value); } virtual void SetPaddingBottom(const uint value) { this.m_padding[3]=((int)value<this.m_frame_width_bottom ? this.m_frame_width_bottom : (int)value); } virtual void SetPaddingAll(const uint value) { this.SetPaddingLeft(value); this.SetPaddingTop(value); this.SetPaddingRight(value); this.SetPaddingBottom(value); } //--- Set the width of the element frame (1) to the left, (2) at the top, (3) to the right and (4) at the bottom virtual void SetFrameWidthLeft(const uint value) { this.m_frame_width_left=(int)value; } virtual void SetFrameWidthTop(const uint value) { this.m_frame_width_top=(int)value; } virtual void SetFrameWidthRight(const uint value) { this.m_frame_width_right=(int)value; } virtual void SetFrameWidthBottom(const uint value) { this.m_frame_width_bottom=(int)value;} virtual void SetFrameWidthAll(const uint value) { this.SetFrameWidthLeft(value); this.SetFrameWidthTop(value); this.SetFrameWidthRight(value); this.SetFrameWidthBottom(value); } //--- Return the width of the element frame (1) to the left, (2) at the top, (3) to the right and (4) at the bottom int FrameWidthLeft(void) const { return this.m_frame_width_left; } int FrameWidthTop(void) const { return this.m_frame_width_top; } int FrameWidthRight(void) const { return this.m_frame_width_right; } int FrameWidthBottom(void) const { return this.m_frame_width_bottom; } //--- 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 int PaddingLeft(void) const { return this.m_padding[0]; } int PaddingTop(void) const { return this.m_padding[1]; } int PaddingRight(void) const { return this.m_padding[2]; } int PaddingBottom(void) const { return this.m_padding[3]; } }; //+------------------------------------------------------------------+
転送されたメソッドの多くは仮想化されており、必要に応じて継承クラスで再定義することが可能です。プロパティを設定するすべてのメソッドで、名前に「Set」接頭辞が付くようになりました。これは、メソッドの所属を明確に示しています。
パラメトリックコンストラクタで、グラフィカル要素とライブラリオブジェクトのタイプ、およびデフォルトのプロパティ値 またはパラメータで渡された値を設定します。
//+------------------------------------------------------------------+ //| Constructor | //+------------------------------------------------------------------+ CWinFormBase::CWinFormBase(const long chart_id, const int subwindow, const string name, const int x, const int y, const int w, const int h) : CForm(chart_id,subwindow,name,x,y,w,h) { //--- Set the graphical element and library object types as a base WinForms object CGBaseObj::SetTypeElement(GRAPH_ELEMENT_TYPE_WF_BASE); CGCnvElement::SetProperty(CANV_ELEMENT_PROP_TYPE,GRAPH_ELEMENT_TYPE_WF_BASE); this.m_type=OBJECT_DE_TYPE_GWF_BASE; //--- Initialize all variables this.m_fore_color=CLR_DEF_FORE_COLOR; this.m_bold_type=FW_TYPE_NORMAL; this.SetMarginAll(0); this.SetPaddingAll(0); this.SetDockMode(CANV_ELEMENT_DOCK_MODE_NONE,false); this.SetBorderStyle(FRAME_STYLE_NONE); this.SetAutoSize(false,false); CForm::SetCoordXInit(x); CForm::SetCoordYInit(y); CForm::SetWidthInit(w); CForm::SetHeightInit(h); this.m_shadow=false; this.m_frame_width_right=0; this.m_frame_width_left=0; this.m_frame_width_top=0; this.m_frame_width_bottom=0; this.m_gradient_v=true; this.m_gradient_c=false; } //+------------------------------------------------------------------+
以下は、CPanelクラスから移動したメソッドです。
//+------------------------------------------------------------------+ //| Return the font flags | //+------------------------------------------------------------------+ uint CWinFormBase::GetFontFlags(void) { string name; int size; uint flags; uint angle; CGCnvElement::GetFont(name,size,flags,angle); return flags; } //+------------------------------------------------------------------+ //| Set the Bold font flag | //+------------------------------------------------------------------+ void CWinFormBase::SetBold(const bool flag) { uint flags=this.GetFontFlags(); if(flag) { this.m_bold_type=FW_TYPE_BOLD; CGCnvElement::SetFontFlags(flags | FW_BOLD); } else this.m_bold_type=FW_TYPE_NORMAL; } //+------------------------------------------------------------------+ //| Return the Bold font flag | //+------------------------------------------------------------------+ bool CWinFormBase::Bold(void) { uint flags=this.GetFontFlags(); return(flags &FW_BOLD)==FW_BOLD; } //+------------------------------------------------------------------+ //| Set the Italic font flag | //+------------------------------------------------------------------+ void CWinFormBase::SetItalic(const bool flag) { uint flags=this.GetFontFlags(); if(flag) CGCnvElement::SetFontFlags(flags | FONT_ITALIC); } //+------------------------------------------------------------------+ //| Return the Italic font flag | //+------------------------------------------------------------------+ bool CWinFormBase::Italic(void) { uint flags=this.GetFontFlags(); return(flags &FONT_ITALIC)==FONT_ITALIC; } //+------------------------------------------------------------------+ //| Set the Strikeout font flag | //+------------------------------------------------------------------+ void CWinFormBase::SetStrikeout(const bool flag) { uint flags=this.GetFontFlags(); if(flag) CGCnvElement::SetFontFlags(flags | FONT_STRIKEOUT); } //+------------------------------------------------------------------+ //| Return the Strikeout font flag | //+------------------------------------------------------------------+ bool CWinFormBase::Strikeout(void) { uint flags=this.GetFontFlags(); return(flags &FONT_STRIKEOUT)==FONT_STRIKEOUT; } //+------------------------------------------------------------------+ //| Set the Underline font flag | //+------------------------------------------------------------------+ void CWinFormBase::SetUnderline(const bool flag) { uint flags=this.GetFontFlags(); if(flag) CGCnvElement::SetFontFlags(flags | FONT_UNDERLINE); } //+------------------------------------------------------------------+ //| Return the Underline font flag | //+------------------------------------------------------------------+ bool CWinFormBase::Underline(void) { uint flags=this.GetFontFlags(); return(flags &FONT_UNDERLINE)==FONT_UNDERLINE; } //+------------------------------------------------------------------+ //| Set the font style | //+------------------------------------------------------------------+ void CWinFormBase::SetFontDrawStyle(ENUM_FONT_STYLE style) { switch(style) { case FONT_STYLE_ITALIC : this.SetItalic(true); break; case FONT_STYLE_UNDERLINE : this.SetUnderline(true); break; case FONT_STYLE_STRIKEOUT : this.SetStrikeout(true); break; default: break; } } //+------------------------------------------------------------------+ //| Return the font style | //+------------------------------------------------------------------+ ENUM_FONT_STYLE CWinFormBase::FontDrawStyle(void) { return ( this.Italic() ? FONT_STYLE_ITALIC : this.Underline() ? FONT_STYLE_UNDERLINE : this.Strikeout() ? FONT_STYLE_UNDERLINE : FONT_STYLE_NORMAL ); } //+------------------------------------------------------------------+ //| Set the font width type | //+------------------------------------------------------------------+ void CWinFormBase::SetFontBoldType(ENUM_FW_TYPE type) { this.m_bold_type=type; uint flags=this.GetFontFlags(); switch(type) { case FW_TYPE_DONTCARE : CGCnvElement::SetFontFlags(flags | FW_DONTCARE); break; case FW_TYPE_THIN : CGCnvElement::SetFontFlags(flags | FW_THIN); break; case FW_TYPE_EXTRALIGHT : CGCnvElement::SetFontFlags(flags | FW_EXTRALIGHT); break; case FW_TYPE_ULTRALIGHT : CGCnvElement::SetFontFlags(flags | FW_ULTRALIGHT); break; case FW_TYPE_LIGHT : CGCnvElement::SetFontFlags(flags | FW_LIGHT); break; case FW_TYPE_REGULAR : CGCnvElement::SetFontFlags(flags | FW_REGULAR); break; case FW_TYPE_MEDIUM : CGCnvElement::SetFontFlags(flags | FW_MEDIUM); break; case FW_TYPE_SEMIBOLD : CGCnvElement::SetFontFlags(flags | FW_SEMIBOLD); break; case FW_TYPE_DEMIBOLD : CGCnvElement::SetFontFlags(flags | FW_DEMIBOLD); break; case FW_TYPE_BOLD : CGCnvElement::SetFontFlags(flags | FW_BOLD); break; case FW_TYPE_EXTRABOLD : CGCnvElement::SetFontFlags(flags | FW_EXTRABOLD); break; case FW_TYPE_ULTRABOLD : CGCnvElement::SetFontFlags(flags | FW_ULTRABOLD); break; case FW_TYPE_HEAVY : CGCnvElement::SetFontFlags(flags | FW_HEAVY); break; case FW_TYPE_BLACK : CGCnvElement::SetFontFlags(flags | FW_BLACK); break; default : CGCnvElement::SetFontFlags(flags | FW_NORMAL); break; } } //+------------------------------------------------------------------+
これらのメソッドについては、これまでのパネルオブジェクトの作成に関する記事で検討してきました。
以下は、要素をクリア(色で塗りつぶす)する仮想メソッドです。
//+------------------------------------------------------------------+ //| Clear the element filling it with color and opacity | //+------------------------------------------------------------------+ void CWinFormBase::Erase(const color colour,const uchar opacity,const bool redraw=false) { //--- Fill the element having the specified color and the redrawing flag CGCnvElement::Erase(colour,opacity,redraw); //--- If the object has a frame, draw it if(this.BorderStyle()!=FRAME_STYLE_NONE && redraw) this.DrawFormFrame(this.FrameWidthTop(),this.FrameWidthBottom(),this.FrameWidthLeft(),this.FrameWidthRight(),this.ColorFrame(),this.Opacity(),this.BorderStyle()); //--- Update the element having the specified redrawing flag this.Update(redraw); } //+------------------------------------------------------------------+ //| Clear the element with a gradient fill | //+------------------------------------------------------------------+ void CWinFormBase::Erase(color &colors[],const uchar opacity,const bool vgradient,const bool cycle,const bool redraw=false) { //--- Fill the element having the specified color array and the redrawing flag CGCnvElement::Erase(colors,opacity,vgradient,cycle,redraw); //--- If the object has a frame, draw it if(this.BorderStyle()!=FRAME_STYLE_NONE && redraw) this.DrawFormFrame(this.FrameWidthTop(),this.FrameWidthBottom(),this.FrameWidthLeft(),this.FrameWidthRight(),this.ColorFrame(),this.Opacity(),this.BorderStyle()); //--- Update the element having the specified redrawing flag this.Update(redraw); } //+------------------------------------------------------------------+ //| Clear the element completely | //+------------------------------------------------------------------+ void CWinFormBase::Erase(const bool redraw=false) { //--- Fully clear the element with the redrawing flag CGCnvElement::Erase(redraw); } //+------------------------------------------------------------------+
これらの仮想メソッドは、同じ親クラスのメソッドをオーバーライドします。ここでは、まず親クラスが呼び出され、要素に色が塗られます。その後、オブジェクトフレームフラグがチェックされます。フレームが存在するはずでオブジェクト再描画フラグが設定されている場合、フレームが描画されます。次に、オブジェクトが更新されます。
以下は、現在のオブジェクトの新しいサイズを設定するメソッドです。
//+------------------------------------------------------------------+ //| Set the new size for the current object | //+------------------------------------------------------------------+ bool CWinFormBase::Resize(const int w,const int h,const bool redraw) { //--- If the object width and height are equal to the passed ones, return 'true' if(this.Width()==w && this.Height()==h) return true; //--- Declare the variable with the property change result bool res=true; //--- Save the panel initial size int prev_w=this.Width(); int prev_h=this.Height(); //--- Set the property change result to the 'res' variable //--- (if the property value is not equal to the passed value) if(this.Width()!=w) res &=this.SetWidth(w); if(this.Height()!=h) res &=this.SetHeight(h); if(!res) return false; //--- Calculate the value, by which the size should be changed int excess_w=this.Width()-prev_w; int excess_h=this.Height()-prev_h; //--- Get the "Shadow" object CShadowObj *shadow=this.GetShadowObj(); //--- If the object has a shadow and the "Shadow" object has been received, if(this.IsShadow() && shadow!=NULL) { //--- save shadow shifts by X and Y, int x=shadow.CoordXRelative(); int y=shadow.CoordYRelative(); //--- set the shadow new width and height res &=shadow.SetWidth(shadow.Width()+excess_w); res &=shadow.SetHeight(shadow.Height()+excess_h); if(!res) return false; //--- If there is no need to redraw, remove the shadow if(!redraw) shadow.Erase(); //--- Save the previously set shadow shift values relative to the panel shadow.SetCoordXRelative(x); shadow.SetCoordYRelative(y); } //--- Redraw the element with new size if(redraw) this.Redraw(true); //--- All is successful - return 'true' return true; } //+------------------------------------------------------------------+
メソッドのロジックは、コードコメントで詳しく説明されています。簡単に言うと、既存のオブジェクトのサイズがメソッドに渡された場合、何も変更する必要はなく、すぐにtrueを返します。メソッドに渡されたサイズがオブジェクトが現在持っているサイズと一致しない場合は、変更します。影がある場合はそのサイズを変更して、redrawフラグが設定されている場合はオブジェクト全体を再描画します。つまり、redrawフラグが設定されていない場合、オブジェクトのサイズは変更されますが、オブジェクト自体は再描画されません。これは、別のオブジェクトにバインドされた複数のオブジェクトの再描画を高速化するために必要です。すべてのバインドされたオブジェクトは、サイズ変更の後で再描画する必要があります。
インデックスで指定されたオブジェクトの新しいサイズを設定するメソッドは、インデックスで指定されたオブジェクトの新しいサイズを設定します。
//+------------------------------------------------------------------+ //| Set the new size for the object specified by object index | //+------------------------------------------------------------------+ bool CWinFormBase::Resize(const int index,const int w,const int h,const bool redraw) { CWinFormBase *obj=this.GetElement(index); return(obj!=NULL ? obj.Resize(w,h,redraw) : false); } //+------------------------------------------------------------------+
このメソッドは、サイズを変更する必要があるバインドされたオブジェクトリスト内のオブジェクトインデックスを受け取ります(サイズもこのメソッドに渡されます)。
リストのインデックスでオブジェクトを取得し、上記のResize()メソッドを呼び出した結果を返します。
以下は、オブジェクトを再描画するメソッドです。
//+------------------------------------------------------------------+ //| Redraw the object | //+------------------------------------------------------------------+ void CWinFormBase::Redraw(bool redraw) { //--- If the object type is less than the "Base WinForms object", exit if(this.TypeGraphElement()<GRAPH_ELEMENT_TYPE_WF_BASE) return; //--- Get the "Shadow" object CShadowObj *shadow=this.GetShadowObj(); //--- If the object has a shadow and the "Shadow" object exists, redraw it if(this.IsShadow() && shadow!=NULL) { //--- remove the previously drawn shadow, shadow.Erase(); //--- save the relative shadow coordinates, int x=shadow.CoordXRelative(); int y=shadow.CoordYRelative(); //--- redraw the shadow, if(redraw) shadow.Draw(0,0,shadow.Blur(),redraw); //--- restore relative shadow coordinates shadow.SetCoordXRelative(x); shadow.SetCoordYRelative(y); } //--- If the redraw flag is set, if(redraw) { //--- completely redraw the object and save its new initial look this.Erase(this.m_array_colors_bg,this.Opacity(),this.m_gradient_v,this.m_gradient_c,redraw); this.Done(); } //--- otherwise, remove the object else this.Erase(); //--- Redraw all bound objects with the redraw flag for(int i=0;i<this.ElementsTotal();i++) { CWinFormBase *element=this.GetElement(i); if(element==NULL) continue; element.Redraw(redraw); } //--- If the redraw flag is set and if this is the main object the rest are bound to, //--- redraw the chart to display changes immediately if(redraw && this.GetMain()==NULL) ::ChartRedraw(this.ChartID()); } //+------------------------------------------------------------------+
メソッドのロジックは、オブジェクトのサイズを変更するメソッドと同様です。次に、再描画フラグが設定されている場合は、オブジェクトを完全に再描画して、現在の外観を「参照」オブジェクトとして設定します。再描画フラグが設定されていない場合は、描画された影オブジェクトとオブジェクト自体を削除します。次に、接続されているオブジェクトのリストを反復処理して、redrawフラグに従ってそれぞれを再描画します(すべてを削除または描画)。
最後に、redrawフラグが設定され、かつそのフラグがバインドされたオブジェクトの全階層でメインである場合(メインオブジェクトを持たず、その GetMain()メソッドがNULLを返す)、実装した変更を直ちに表示するためにチャートを更新します。
すべてのライブラリWinFormsオブジェクトの基本クラスが出来上がりました。
次に、\MQL5\Include\DoEasy\Objects\Graph\WForms\Containers\Panel.mqhのパネルオブジェクトクラスを改善します。
パネルにバインドされているオブジェクトにDockプロパティが設定されている場合、これらのオブジェクトは、バインドされているオブジェクトのリスト内のインデックス順に自動的に配置され、ルールに従って、バインドタイプに設定される必要があります。つまり、Dockが左側にある場合、オブジェクトの高さは、オブジェクトが接続されているパネルの全高に引き伸ばされ、その左座標は、パネルのアンダーレイの左端(オブジェクトがリストの最初のものである場合)またはバインドされたオブジェクトのリストから前のオブジェクトの右端のいずれかになります。このルールは、リストの各オブジェクトに適用されます。そのほか、パネルの自動サイズ変更フラグやサイズ変更変更モード(増加だけまたは増減)も考慮する必要があります。
本稿では、パネル自動サイズ変更モードが無効の場合にのみ、バインドされたオブジェクトのDockプロパティを扱うように実装します。バインドされたオブジェクトのリストから現在のオブジェクトがバインドしているオブジェクトの端を定義するために、端の数だけ4つのオブジェクトを作成します(上下左右)。Dockプロパティが設定された各オブジェクトは、これらのオブジェクトのいずれかに適合し、リストの残りのオブジェクトは、どのオブジェクトにバインドすべきかの座標を「知る」ことができます(同様にDockプロパティが設定されていれば、リスト内の前のオブジェクトの端に接続されるからです)。
すべてのWinFormsオブジェクトはWinForms基本オブジェクトから継承されているため、インクルードされているフォームオブジェクトファイル
//+------------------------------------------------------------------+ //| Panel.mqh | //| Copyright 2021, MetaQuotes Ltd. | //| https://mql5.com/ja/users/artmedia70 | //+------------------------------------------------------------------+ #property copyright "Copyright 2021, MetaQuotes Ltd." #property link "https://mql5.com/ja/users/artmedia70" #property version "1.00" #property strict // Necessary for mql4 //+------------------------------------------------------------------+ //| Include files | //+------------------------------------------------------------------+ #include "..\..\Form.mqh" //+------------------------------------------------------------------+ //| Panel object class of WForms controls | //+------------------------------------------------------------------+ class CPanel : public CForm
の代わりに、WinForms基本オブジェクトのファイルをインクルードします。
//+------------------------------------------------------------------+ //| Panel.mqh | //| Copyright 2021, MetaQuotes Ltd. | //| https://mql5.com/ja/users/artmedia70 | //+------------------------------------------------------------------+ #property copyright "Copyright 2021, MetaQuotes Ltd." #property link "https://mql5.com/ja/users/artmedia70" #property version "1.00" #property strict // Necessary for mql4 //+------------------------------------------------------------------+ //| Include files | //+------------------------------------------------------------------+ #include "..\..\WForms\WinFormBase.mqh" //+------------------------------------------------------------------+ //| Panel object class of WForms controls | //+------------------------------------------------------------------+
クラスのprivateセクションで、Dockオブジェクトをバインドするために必要な座標のオブジェクトへのポインタを格納するために、4つのオブジェクトへのポインタを宣言します。
class CPanel : public CWinFormBase { private: CGCnvElement *m_obj_top; // Pointer to the object whose coordinates the current upper object is bound to CGCnvElement *m_obj_bottom; // Pointer to the object whose coordinates the current bottom object is bound to CGCnvElement *m_obj_left; // Pointer to the object whose coordinates the current left object is bound to CGCnvElement *m_obj_right; // Pointer to the object whose coordinates the current right object is bound to CGCnvElement *m_underlay; // Underlay for placing elements bool m_autoscroll; // Auto scrollbar flag int m_autoscroll_margin[2]; // Array of fields around the control during an auto scroll ENUM_CANV_ELEMENT_AUTO_SIZE_MODE m_autosize_mode; // Mode of the element auto resizing depending on the content //--- 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); //--- Create the underlay object bool CreateUnderlayObj(void); //--- Set the underlay as a coordinate system zero void SetUnderlayAsBase(void); protected:
SetDockingToContainer()メソッドの代わりに、SetUnderlayAsBase()メソッドを宣言して、アンダーレイオブジェクトを上で宣言した4つのポインタすべてに設定します。オブジェクトを作成した直後に、そのアンダーレイが最初のバインドされるオブジェクトのバインド座標原点として使用されます。Dockオブジェクトが配置されると、すでに必要な端にバインドされている適切なオブジェクトへのポインタが変数に設定されます。しかし、アンダーレイは最初のうちはバインドオブジェクトとして機能します。
クラスのprotectedセクションには、すべてのDockオブジェクトがバインドされているオブジェクトを超える最大値を返す2つのメソッドを宣言し、格納するオブジェクトに合わせてコンテナのサイズを自動調整するメソッドを宣言し、コンテナにバインドされているオブジェクトのリストからDockオブジェクトが使用する端のオブジェクトへのポインタを返す4つのメソッドを記述します。
protected: //--- Return the maximum value of Dock object borders going beyond the container by width and (2) height int GetExcessMaxX(void); int GetExcessMaxY(void); //--- Set (1) X, (2) Y coordinate, (3) width, (4) height and (5) all underlay parameters bool SetCoordXUnderlay(const int value) { return(this.m_underlay!=NULL ? this.m_underlay.SetCoordX(value) : false); } bool SetCoordYUnderlay(const int value) { return(this.m_underlay!=NULL ? this.m_underlay.SetCoordY(value) : false); } bool SetWidthUnderlay(const int value) { return(this.m_underlay!=NULL ? this.m_underlay.SetWidth(value) : false); } bool SetHeightUnderlay(const int value) { return(this.m_underlay!=NULL ? this.m_underlay.SetHeight(value) : false); } bool SetUnderlayParams(void); //--- Return the underlay (1) X, (2) Y coordinate, (3) width and (4) height int GetCoordXUnderlay(void) const { return(this.m_underlay!=NULL ? this.m_underlay.CoordX() : 0); } int GetCoordYUnderlay(void) const { return(this.m_underlay!=NULL ? this.m_underlay.CoordY() : 0); } int GetWidthUnderlay(void) const { return(this.m_underlay!=NULL ? this.m_underlay.Width() : 0); } int GetHeightUnderlay(void) const { return(this.m_underlay!=NULL ? this.m_underlay.Height() : 0); } //--- Return the underlay (1) X and (2) Y coordinate relative to the panel int GetCoordXUnderlayRelative(void) const { return(this.m_underlay!=NULL ? this.m_underlay.CoordXRelative() : 0); } int GetCoordYUnderlayRelative(void) const { return(this.m_underlay!=NULL ? this.m_underlay.CoordYRelative() : 0); } //--- Return the object whose coordinates the current (1) top, (2) bottom, (3) left or (4) right object is bound to CGCnvElement *GetTopObj(void) { return this.m_obj_top; } CGCnvElement *GetBottomObj(void) { return this.m_obj_bottom; } CGCnvElement *GetLeftObj(void) { return this.m_obj_left; } CGCnvElement *GetRightObj(void) { return this.m_obj_right; } //--- Adjust the element size to fit its content bool AutoSizeProcess(const bool redraw); public:
残りの変数とメソッドは、すでにWinFormsオブジェクトの基本クラスに移動されています。このような変更点については、本稿に添付されたファイルからご確認ください。
移動したメソッド、調整と新しいメソッドをすべて考慮すると、クラスのpublicセクションは次のようになります。
public: //--- Return the (1) underlay and (2) the object the current object is bound to CGCnvElement *GetUnderlay(void) { return this.m_underlay; } //--- Update the coordinates (shift the canvas) virtual bool Move(const int x,const int y,const bool redraw=false); //--- Set the (1) X, (2) Y coordinates, (3) element width and (4) height virtual bool SetCoordX(const int coord_x); virtual bool SetCoordY(const int coord_y); virtual bool SetWidth(const int width); virtual bool SetHeight(const int height); //--- Create a new attached element virtual bool CreateNewElement(const ENUM_GRAPH_ELEMENT_TYPE element_type, CGCnvElement *main, const int x, const int y, const int w, const int h, const color colour, const uchar opacity, const bool activity, const bool redraw); //--- Redraw the object virtual void Redraw(bool redraw); //--- Update the element void Update(const bool redraw=false) { this.m_canvas.Update(redraw); } //--- Reset the size of all bound objects to the initial ones bool ResetSizeAllToInit(void); //--- Place bound objects in the order of their Dock binding bool ArrangeObjects(const bool redraw); //--- (1) Set and (2) return the auto scrollbar flag void SetAutoScroll(const bool flag) { this.m_autoscroll=flag; } bool AutoScroll(void) { return this.m_autoscroll; } //--- Set the (1) field width, (2) height, (3) the height of all fields around the control during auto scrolling void SetAutoScrollMarginWidth(const int value) { this.m_autoscroll_margin[0]=value; } void SetAutoScrollMarginHeight(const int value) { this.m_autoscroll_margin[1]=value; } void SetAutoScrollMarginAll(const int value) { this.SetAutoScrollMarginWidth(value); this.SetAutoScrollMarginHeight(value); } //--- Return the (1) field width and (2) height around the control during auto scrolling int AutoScrollMarginWidth(void) const { return this.m_autoscroll_margin[0]; } int AutoScrollMarginHeight(void) const { return this.m_autoscroll_margin[1]; } //--- (1) Set the flag of the element auto resizing depending on the content virtual void SetAutoSize(const bool flag,const bool redraw) { bool prev=this.AutoSize(); if(prev==flag) return; CWinFormBase::SetAutoSize(flag,redraw); if(prev!=this.AutoSize() && this.ElementsTotal()>0) this.AutoSizeProcess(redraw); } //--- (1) Set and (2) return the mode of the element auto resizing depending on the content void SetAutoSizeMode(const ENUM_CANV_ELEMENT_AUTO_SIZE_MODE mode,const bool redraw) { ENUM_CANV_ELEMENT_AUTO_SIZE_MODE prev=this.AutoSizeMode(); if(prev==mode) return; this.m_autosize_mode=mode; if(prev!=this.AutoSizeMode() && this.ElementsTotal()>0) this.AutoSizeProcess(redraw); } ENUM_CANV_ELEMENT_AUTO_SIZE_MODE AutoSizeMode(void) const { return this.m_autosize_mode; } //--- (1) Set and (2) return the mode of binding element borders to the container virtual void SetDockMode(const ENUM_CANV_ELEMENT_DOCK_MODE mode,const bool redraw) { if(this.DockMode()==mode) return; CWinFormBase::SetDockMode(mode,redraw); CPanel *base=this.GetBase(); if(base!=NULL) base.ArrangeObjects(redraw); } //--- 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 virtual void SetPaddingLeft(const uint value) { CWinFormBase::SetPaddingLeft(value); if(this.m_underlay!=NULL) { //--- Set the underlay shift along the X axis this.m_underlay.SetCoordXRelative(this.PaddingLeft()); //--- Set the X coordinate and the underlay width this.SetCoordXUnderlay(this.CoordX()+this.PaddingLeft()); this.SetWidthUnderlay(this.Width()-this.PaddingLeft()-this.PaddingRight()); } } virtual void SetPaddingTop(const uint value) { CWinFormBase::SetPaddingTop(value); if(this.m_underlay!=NULL) { //--- Set the underlay shift along the Y axis this.m_underlay.SetCoordYRelative(this.PaddingTop()); //--- Set the Y coordinate and underlay height this.SetCoordYUnderlay(this.CoordY()+this.PaddingTop()); this.SetHeightUnderlay(this.Height()-this.PaddingTop()-this.PaddingBottom()); } } virtual void SetPaddingRight(const uint value) { CWinFormBase::SetPaddingRight(value); if(this.m_underlay!=NULL) { //--- Set the underlay width this.SetWidthUnderlay(this.Width()-this.PaddingLeft()-this.PaddingRight()); } } virtual void SetPaddingBottom(const uint value) { CWinFormBase::SetPaddingBottom(value); if(this.m_underlay!=NULL) { //--- Set the underlay height this.SetHeightUnderlay(this.Height()-this.PaddingTop()-this.PaddingBottom()); } } virtual void SetPaddingAll(const uint value) { this.SetPaddingLeft(value); this.SetPaddingTop(value); this.SetPaddingRight(value); this.SetPaddingBottom(value); } //--- Set the width of the form frame (1) to the left, (2) at the top, (3) to the right, (4) at the bottom and (5) on all sides of the control virtual void SetFrameWidthLeft(const uint value) { this.m_frame_width_left=(int)value; if(this.PaddingLeft()<this.FrameWidthLeft()) this.SetPaddingLeft(this.FrameWidthLeft()); } virtual void SetFrameWidthTop(const uint value) { this.m_frame_width_top=(int)value; if(this.PaddingTop()<this.FrameWidthTop()) this.SetPaddingTop(this.FrameWidthTop()); } virtual void SetFrameWidthRight(const uint value) { this.m_frame_width_right=(int)value; if(this.PaddingRight()<this.FrameWidthRight()) this.SetPaddingRight(this.FrameWidthRight()); } virtual void SetFrameWidthBottom(const uint value) { this.m_frame_width_bottom=(int)value; if(this.PaddingBottom()<this.FrameWidthBottom()) this.SetPaddingBottom(this.FrameWidthBottom()); } virtual void SetFrameWidthAll(const uint value) { this.SetFrameWidthLeft(value); this.SetFrameWidthTop(value); this.SetFrameWidthRight(value); this.SetFrameWidthBottom(value); } //--- Constructors CPanel(const long chart_id, const int subwindow, const string name, const int x, const int y, const int w, const int h); CPanel(const string name) : CWinFormBase(::ChartID(),0,name,0,0,0,0) { CGBaseObj::SetTypeElement(GRAPH_ELEMENT_TYPE_WF_PANEL); CGCnvElement::SetProperty(CANV_ELEMENT_PROP_TYPE,GRAPH_ELEMENT_TYPE_WF_PANEL); this.m_type=OBJECT_DE_TYPE_GWF_PANEL; this.SetForeColor(CLR_DEF_FORE_COLOR); this.SetFontBoldType(FW_TYPE_NORMAL); this.SetMarginAll(3); this.SetPaddingAll(0); this.SetDockMode(CANV_ELEMENT_DOCK_MODE_NONE,false); this.SetBorderStyle(FRAME_STYLE_BEVEL); this.SetAutoScroll(false); this.SetAutoScrollMarginAll(0); this.SetAutoSize(false,false); this.SetAutoSizeMode(CANV_ELEMENT_AUTO_SIZE_MODE_GROW,false); this.Initialize(); if(this.CreateUnderlayObj()) this.SetUnderlayAsBase(); } //--- Destructor ~CPanel(); }; //+------------------------------------------------------------------+
すべてのSetメソッドの名前に「Set」の接頭辞が付きました。
自動サイズ変更、自動サイズ変更モードを設定するメソッド、およびDockオブジェクトをコンテナにバインドするメソッドは、まず、プロパティを設定するための基本クラスのメソッドを呼び出してから、コンテナのサイズを変更するメソッドを呼び出します。
virtual void SetAutoSize(const bool flag,const bool redraw) { bool prev=this.AutoSize(); if(prev==flag) return; CWinFormBase::SetAutoSize(flag,redraw); if(prev!=this.AutoSize() && this.ElementsTotal()>0) this.AutoSizeProcess(redraw); }
コンテナは、少なくとも1つのオブジェクトがコンテナにバインドされている場合にのみ、サイズを変更する必要があります。
その他の同様のメソッドも同様のロジックで作られています。ご自分でお調べください。
質問がある場合は、下のコメント欄でお気軽にお問い合わせください。
アンダーレイの全パラメータを設定するメソッドで、「アンダーレイ 」オブジェクトタイプを指定します。
//+------------------------------------------------------------------+ //| Set all underlay parameters | //+------------------------------------------------------------------+ bool CPanel::SetUnderlayParams(void) { //--- Set the object type this.m_underlay.SetTypeElement(GRAPH_ELEMENT_TYPE_WF_UNDERLAY); //--- Set the underlay shift values to the variables by X and Y axes this.m_underlay.SetCoordXRelative(this.PaddingLeft()); this.m_underlay.SetCoordYRelative(this.PaddingTop()); //--- Set the underlay coordinates and size bool res=true; res &=this.SetCoordXUnderlay(this.CoordX()+this.PaddingLeft()); res &=this.SetCoordYUnderlay(this.CoordY()+this.PaddingTop()); res &=this.SetWidthUnderlay(this.Width()-this.PaddingLeft()-this.PaddingRight()); res &=this.SetHeightUnderlay(this.Height()-this.PaddingTop()-this.PaddingBottom()); return res; } //+------------------------------------------------------------------+
Dockオブジェクトをコンテナ内に配置する際、選択したオブジェクトが「アンダーレイ 」タイプであることを知る必要があります。バインドされたオブジェクトのリストから現在のオブジェクトを接続するために端が使用されるべきオブジェクトがアンダーレイである場合、例えば左側のバインドエッジはアンダーレイのX座標となります。アンダーレイではなく、他のDockオブジェクトの場合は、そのDockオブジェクトの右端がバインドエッジとみなされます。
パネルの座標や大きさを設定するメソッドでは、まず基本オブジェクトのプロパティを設定するメソッドを呼び出します。次に、アンダーレイオブジェクトの座標とサイズを変更します。
//+------------------------------------------------------------------+ //| Set the panel X coordinate | //+------------------------------------------------------------------+ bool CPanel::SetCoordX(const int coord_x) { if(!CGCnvElement::SetCoordX(coord_x)) return false; return(this.m_underlay!=NULL ? this.SetCoordXUnderlay(coord_x+this.PaddingLeft()) : true); } //+------------------------------------------------------------------+ //| Set the panel Y coordinate | //+------------------------------------------------------------------+ bool CPanel::SetCoordY(const int coord_y) { if(!CGCnvElement::SetCoordY(coord_y)) return false; return(this.m_underlay!=NULL ? this.SetCoordYUnderlay(coord_y+this.PaddingTop()) : true); } //+------------------------------------------------------------------+ //| Set the panel width | //+------------------------------------------------------------------+ bool CPanel::SetWidth(const int width) { if(!CGCnvElement::SetWidth(width)) return false; return(this.m_underlay!=NULL ? this.SetWidthUnderlay(this.Width()-this.PaddingLeft()-this.PaddingRight()) : true); } //+------------------------------------------------------------------+ //| Set the panel height | //+------------------------------------------------------------------+ bool CPanel::SetHeight(const int height) { if(!CGCnvElement::SetHeight(height)) return false; return(this.m_underlay!=NULL ? this.SetHeightUnderlay(this.Height()-this.PaddingTop()-this.PaddingBottom()) : true); } //+------------------------------------------------------------------+
以下は、バインドされているすべてのオブジェクトのサイズを初期値に戻すメソッドです。
//+------------------------------------------------------------------+ //| Reset the size of all bound objects to the initial ones | //+------------------------------------------------------------------+ bool CPanel::ResetSizeAllToInit(void) { bool res=true; for(int i=0;i<this.ElementsTotal();i++) { CPanel *obj=this.GetElement(i); if(obj==NULL) { res &=false; continue; } res &=obj.Resize(i,obj.GetWidthInit(),obj.GetHeightInit()); } return res; } //+------------------------------------------------------------------+
コンテナにバインドされたすべてのオブジェクトを反復処理して、次のオブジェクトを取得し、オブジェクトのサイズを初期サイズに変更した結果をres変数に設定します。反復処理が終了すると、サイズ変更結果の合計がres変数に設定されます。少なくとも1つのオブジェクトのサイズ変更がエラーとなった場合、res変数にfalse、そうでない場合はtrueが設定されます。
以下は、接続要素を新規作成するメソッドです。
//+------------------------------------------------------------------+ //| Create a new attached element | //+------------------------------------------------------------------+ bool CPanel::CreateNewElement(const ENUM_GRAPH_ELEMENT_TYPE element_type, CGCnvElement *main, const int x, const int y, const int w, const int h, const color colour, const uchar opacity, const bool activity, const bool redraw) { //--- If failed to create a new graphical element, return 'false' CGCnvElement *obj=CForm::CreateAndAddNewElement(element_type,main,x,y,w,h,colour,opacity,activity); if(obj==NULL) return false; //--- If the object type is a base WinForms object and higher, if(obj.TypeGraphElement()>=GRAPH_ELEMENT_TYPE_WF_BASE) { //--- declare the pointer with the base WinForms object type and assign the pointer to the newly created object to it, //--- set the frame color equal to the background color CWinFormBase *wf=obj; wf.SetColorFrame(wf.ColorBackground()); } //--- If the panel has auto resize enabled and features bound objects, call the resize method if(this.AutoSize() && this.ElementsTotal()>0) this.AutoSizeProcess(redraw); //--- Redraw the panel and all added objects, and return 'true' this.Redraw(redraw); return true; } //+------------------------------------------------------------------+
メソッドのロジックはコードコメントに記載されており、非常にシンプルです。
以下は、Dockオブジェクトの境界がコンテナから横方向にはみ出した場合の最大値を返すメソッドです。
//+------------------------------------------------------------------+ //| Return the maximum value of Dock object borders | //| going beyond the container by width | //+------------------------------------------------------------------+ int CPanel::GetExcessMaxX(void) { int value=0; for(int i=0;i<this.ElementsTotal();i++) { CWinFormBase *element=this.GetElement(i); if(element==NULL) continue; if(element.RightEdge()>value) value=element.RightEdge(); } return value-this.m_underlay.RightEdge(); } //+------------------------------------------------------------------+
以下は、Dock オブジェクトの境界がコンテナから縦方向にはみ出した場合の最大値を返すメソッドです。
//+------------------------------------------------------------------+ //| Return the maximum value of Dock object borders | //| going beyond the container by height | //+------------------------------------------------------------------+ int CPanel::GetExcessMaxY(void) { int value=0; for(int i=0;i<this.ElementsTotal();i++) { CWinFormBase *element=this.GetElement(i); if(element==NULL) continue; if(element.BottomEdge()>value) value=element.BottomEdge(); } return value-this.m_underlay.BottomEdge(); } //+------------------------------------------------------------------+
どちらの方式もロジックは同じです。コンテナに束縛されたすべてのオブジェクトによるループの中で、次のオブジェクトを取得し、その右(下)エッジがvalue変数に設定された値を超えていれば、そのエッジの値がその変数に代入されます。ループが終了すると、value変数に全オブジェクトの右(下)端の最大値と、見つかった値と右(下)端のアンダーレイオブジェクトの境界線が返されるのが特徴です。このようにして、コンテナのサイズを変更するために必要なピクセル単位の正負の値を得ることができます。
以下は、バインドされたオブジェクトをDockバインドの順番に配置するメソッドです。
//+------------------------------------------------------------------+ //| Place bound objects in the order of their Dock binding | //+------------------------------------------------------------------+ bool CPanel::ArrangeObjects(const bool redraw) { CWinFormBase *prev=NULL, *obj=NULL; //--- If auto resizing mode is enabled if(this.AutoSize()) { //--- In the loop by all bound objects, for(int i=0;i<this.ElementsTotal();i++) { //--- Get the previous element from the list prev=this.GetElement(i-1); //--- If there is no previous element, set the underlay as a previous element if(prev==NULL) this.SetUnderlayAsBase(); //--- Get the current element obj=GetElement(i); //--- If the object has not been received or its type is less than the base WinForms object or it has no underlay, move on if(obj==NULL || obj.TypeGraphElement()<GRAPH_ELEMENT_TYPE_WF_BASE || this.m_underlay==NULL) continue; //--- Depending on the current object binding mode... //--- Top if(obj.DockMode()==CANV_ELEMENT_DOCK_MODE_TOP) { } //--- Bottom if(obj.DockMode()==CANV_ELEMENT_DOCK_MODE_BOTTOM) { } //--- Left if(obj.DockMode()==CANV_ELEMENT_DOCK_MODE_LEFT) { } //--- Right if(obj.DockMode()==CANV_ELEMENT_DOCK_MODE_RIGHT) { } //--- Binding with filling if(obj.DockMode()==CANV_ELEMENT_DOCK_MODE_FILL) { } //--- No binding if(obj.DockMode()==CANV_ELEMENT_DOCK_MODE_NONE) { } } this.Resize(this.GetWidthInit(),this.GetHeightInit(),false); } //--- If auto resizing mode disabled else { //--- In the loop by all bound objects, for(int i=0;i<this.ElementsTotal();i++) { //--- Get the current and previous elements from the list obj=this.GetElement(i); prev=this.GetElement(i-1); //--- If there is no previous element, set the underlay as a previous element if(prev==NULL) this.SetUnderlayAsBase(); //--- If the object has not been received or its type is less than the base WinForms object or it has no underlay, move on if(obj==NULL || obj.TypeGraphElement()<GRAPH_ELEMENT_TYPE_WF_BASE || this.m_underlay==NULL) continue; int x=0, y=0; // Object binding coordinates //--- Depending on the current object binding mode... //--- Top if(obj.DockMode()==CANV_ELEMENT_DOCK_MODE_TOP) { //--- If failed to change the object size (for the entire underlay width and by the initial object height), move on to the next one if(!obj.Resize(this.GetWidthUnderlay(),obj.GetHeightInit(),false)) continue; //--- Get the pointer to the object at the top whose edges are used to bind the current one CGCnvElement *coord_base=this.GetTopObj(); //--- Get the object binding coordinates x=this.GetCoordXUnderlay(); y=(coord_base.TypeGraphElement()==GRAPH_ELEMENT_TYPE_WF_UNDERLAY ? coord_base.CoordY() : coord_base.BottomEdge()+1); //--- If failed to move the object to the obtained coordinates, move on to the next one if(!obj.Move(x,y,false)) continue; //--- Set the current object as the top one whose edges will be used to bind the next one this.m_obj_top=obj; } //--- Bottom if(obj.DockMode()==CANV_ELEMENT_DOCK_MODE_BOTTOM) { //--- If failed to change the object size (for the entire underlay width and by the initial object height), move on to the next one if(!obj.Resize(this.GetWidthUnderlay(),obj.GetHeightInit(),false)) continue; //--- Get the pointer to the object at the bottom whose edges are used to bind the current one CGCnvElement *coord_base=this.GetBottomObj(); //--- Get the object binding coordinates x=this.GetCoordXUnderlay(); y=(coord_base.TypeGraphElement()==GRAPH_ELEMENT_TYPE_WF_UNDERLAY ? coord_base.BottomEdge()-obj.Height()-1 : coord_base.CoordY()-obj.Height()-1); //--- If failed to move the object to the obtained coordinates, move on to the next one if(!obj.Move(x,y,false)) continue; //--- Set the current object as the bottom one whose edges will be used to bind the next one this.m_obj_bottom=obj; } //--- Left if(obj.DockMode()==CANV_ELEMENT_DOCK_MODE_LEFT) { //--- If failed to change the object size (for the initial object width and the entire underlay height), move on to the next one if(!obj.Resize(obj.GetWidthInit(),this.GetHeightUnderlay(),false)) continue; //--- Get the pointer to the object at the left whose edges are used to bind the current one CGCnvElement *coord_base=this.GetLeftObj(); //--- Get the object binding coordinates x=(coord_base.TypeGraphElement()==GRAPH_ELEMENT_TYPE_WF_UNDERLAY ? coord_base.CoordX() : coord_base.RightEdge()+1); y=this.GetCoordYUnderlay(); //--- If failed to move the object to the obtained coordinates, move on to the next one if(!obj.Move(x,y,false)) continue; //--- Set the current object as the left one whose edges will be used to bind the next one this.m_obj_left=obj; } //--- Right if(obj.DockMode()==CANV_ELEMENT_DOCK_MODE_RIGHT) { //--- If failed to change the object size (for the initial object width and the entire underlay height), move on to the next one if(!obj.Resize(obj.GetWidthInit(),this.GetHeightUnderlay(),false)) continue; //--- Get the pointer to the object at the right whose edges are used to bind the current one CGCnvElement *coord_base=this.GetRightObj(); //--- Get the object binding coordinates x=(coord_base.TypeGraphElement()==GRAPH_ELEMENT_TYPE_WF_UNDERLAY ? m_underlay.RightEdge()-obj.Width() : coord_base.CoordX()-obj.Width()-1); y=this.GetCoordYUnderlay(); //--- If failed to move the object to the obtained coordinates, move on to the next one if(!obj.Move(x,y,false)) continue; //--- Set the current object as the right one whose edges will be used to bind the next one this.m_obj_right=obj; } //--- Binding with filling if(obj.DockMode()==CANV_ELEMENT_DOCK_MODE_FILL) { //--- If failed to change the object size (for the entire underlay width and height), move on to the next one if(!obj.Resize(this.GetWidthUnderlay(),this.GetHeightUnderlay(),false)) continue; //--- Set the underlay as a binding object this.SetUnderlayAsBase(); //--- Get the object binding coordinates x=this.GetLeftObj().CoordX(); y=this.GetTopObj().CoordY(); //--- If failed to move the object to the obtained coordinates, move on to the next one if(!obj.Move(x,y,false)) continue; } //--- No binding if(obj.DockMode()==CANV_ELEMENT_DOCK_MODE_NONE) { //--- Reset the object size obj.Resize(obj.GetWidthInit(),obj.GetHeightInit(),false); //--- Get the initial object location coordinates x=this.GetCoordXUnderlay()+obj.CoordXRelativeInit(); y=this.GetCoordYUnderlay()+obj.CoordYRelativeInit(); //--- If failed to move the object to the obtained coordinates, move on to the next one if(!obj.Move(x,y,false)) continue; } //--- Calculate and set the relative object coordinates obj.SetCoordXRelative(x-this.m_underlay.CoordX()); obj.SetCoordYRelative(y-this.m_underlay.CoordY()); } } //--- Redraw the object with the redraw flag and return 'true' this.Redraw(redraw); return true; } //+------------------------------------------------------------------+
メソッドのロジックは、コードコメントで詳しく説明されています。自動サイズ変更フラグを持つパネルの場合、自動サイズ変更なしのパネルで実装したものとは異なるロジックになるため、コンテナ内のオブジェクトはまだ配置しません(ハンドラに置き換えるスタブのみが存在)。
以下は、バインドされたDockオブジェクトの座標系ゼロとしてオーバーレイを設定するメソッドです。
//+------------------------------------------------------------------+ //| Set the underlay as a coordinate system zero | //+------------------------------------------------------------------+ void CPanel::SetUnderlayAsBase(void) { this.m_obj_left=this.m_underlay; this.m_obj_right=this.m_underlay; this.m_obj_top=this.m_underlay; this.m_obj_bottom=this.m_underlay; } //+------------------------------------------------------------------+
4つのバインドオブジェクトすべてにアンダーレイへのポインタを割り当てます。
以下は、内容に合わせて要素の大きさを調整するメソッドです。
//+------------------------------------------------------------------+ //| Adjust the element size to fit its content | //+------------------------------------------------------------------+ bool CPanel::AutoSizeProcess(const bool redraw) { //--- Get values along X and Y axes, by which the panel size is to be changed int excess_w=this.GetExcessMaxX(); int excess_h=this.GetExcessMaxY(); //--- If failed to change the size, return 'true' if(excess_w==0 && excess_h==0) return true; //--- If failed to change the panel size, return the result of its adjustment return ( //--- if only a size increase this.AutoSizeMode()==CANV_ELEMENT_AUTO_SIZE_MODE_GROW ? this.Resize(this.Width()+(excess_w>0 ? excess_w : 0),this.Height()+(excess_h>0 ? excess_h : 0),redraw) : //--- if both increase and decrease this.Resize(this.Width()+(excess_w!=0 ? excess_w : 0),this.Height()+(excess_h!=0 ? excess_h : 0),redraw) ); } //+------------------------------------------------------------------+
メソッドのロジックは、コードのコメントで説明されています。簡単に説明すると、バインドされたオブジェクトがアンダーレイの境界を越えるようなX軸とY軸に沿った最大値を取得します。正の値は、オブジェクトがアンダーレイからはみ出したことを示し、負の値は、アンダーレイが大きすぎるため縮小できることを示します。また、自動サイズ変更モード(増加のみまたは増減)を考慮してパネルサイズを変更した結果を返します。
グラフィカル要素コレクションクラスの\MQL5\Include\DoEasy\Collections\GraphElementsCollection.mqhファイルで、パネル作成メソッドの BorderStyle() と FrameWidthAll() プロパティを設定するメソッドの名前を新しい名前に置き換える必要があり ます。SetBorderStyle()およびSetFrameWidthAll()を使用します。添付ファイルでは、メソッド名が既に変更されています。
\MQL5\Include\DoEasy\Engine.mqhのWForm Panelオブジェクトを返すメソッドにおいて、GRAPH_ELEMENT_TYPE_PANELマクロ置換をGRAPH_ELEMENT_TYPE_WF_PANELに名前変更します。
//--- 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_WF_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_WF_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_WF_PANEL); list=CSelect::ByGraphCanvElementProperty(list,CANV_ELEMENT_PROP_ID,element_id,EQUAL); return(list!=NULL ? list.At(0) : NULL); } //--- Create the WinForm Element object
本稿で計画した変更と改善はこれですべてです。
検証
テストを実行するには、前の記事のEAを使用して、\MQL5\Experts\TestDoEasy\Part105\にTestDoEasyPart105.mq5として保存します。
パネルの内容に合わせるために、自動サイズ変更モードと同様に、パネル自動サイズ変更フラグを追加してみましょう。
また、オブジェクトを配置するための新しいキーをコンテナ内に追加します。Qを押すと、パネル内にあるすべてのオブジェクトが、バインドモードに従って配置されます。バインドモードの数に応じてこのようなオブジェクトを6個用意します。
//+------------------------------------------------------------------+ //| Control borders bound to the container | //+------------------------------------------------------------------+ enum ENUM_CANV_ELEMENT_DOCK_MODE { CANV_ELEMENT_DOCK_MODE_NONE, // Attached to the specified coordinates, size does not change CANV_ELEMENT_DOCK_MODE_TOP, // Attaching to the top and stretching along the container width CANV_ELEMENT_DOCK_MODE_BOTTOM, // Attaching to the bottom and stretching along the container width CANV_ELEMENT_DOCK_MODE_LEFT, // Attaching to the left and stretching along the container height CANV_ELEMENT_DOCK_MODE_RIGHT, // Attaching to the right and stretching along the container height CANV_ELEMENT_DOCK_MODE_FILL, // Stretching along the entire container width and height }; //+------------------------------------------------------------------+
したがって、これらのオブジェクトは、パネルオブジェクトのリスト内の番号に対応するバインドモードをそれぞれ受け取ることになります。リストの最初のオブジェクトはバインドなし(CANV_ELEMENT_DOCK_MODE_NONE)、2番目はCANV_ELEMENT_DOCK_MODE_TOPモード、3番目はMODE_BOTTOM、などになります。
グローバルエリアでは、Qキーのマクロ置換、英語とユーザーの国の言語での入力の列挙を追加し、新しい入力も追加します。
//+------------------------------------------------------------------+ //| TestDoEasyPart105.mq5 | //| Copyright 2021, MetaQuotes Ltd. | //| https://mql5.com/ja/users/artmedia70 | //+------------------------------------------------------------------+ #property copyright "Copyright 2021, MetaQuotes Ltd." #property link "https://mql5.com/ja/users/artmedia70" #property version "1.00" //--- includes #include <DoEasy\Engine.mqh> //--- defines #define FORMS_TOTAL (3) // Number of created forms #define START_X (4) // Initial X coordinate of the shape #define START_Y (4) // Initial Y coordinate of the shape #define KEY_LEFT (65) // (A) Left #define KEY_RIGHT (68) // (D) Right #define KEY_UP (87) // (W) Up #define KEY_DOWN (88) // (X) Down #define KEY_FILL (83) // (S) Filling #define KEY_ORIGIN (90) // (Z) Default #define KEY_INDEX (81) // (Q) By index //--- enumerations by compilation language #ifdef COMPILE_EN enum ENUM_AUTO_SIZE_MODE { AUTO_SIZE_MODE_GROW=CANV_ELEMENT_AUTO_SIZE_MODE_GROW, // Grow AUTO_SIZE_MODE_GROW_SHRINK=CANV_ELEMENT_AUTO_SIZE_MODE_GROW_SHRINK // Grow and Shrink }; #else enum ENUM_AUTO_SIZE_MODE { AUTO_SIZE_MODE_GROW=CANV_ELEMENT_AUTO_SIZE_MODE_GROW, // Increase only AUTO_SIZE_MODE_GROW_SHRINK=CANV_ELEMENT_AUTO_SIZE_MODE_GROW_SHRINK // Increase and decrease }; #endif //--- input parameters sinput bool InpMovable = true; // Movable forms flag sinput ENUM_INPUT_YES_NO InpAutoSize = INPUT_YES; // Autosize sinput ENUM_AUTO_SIZE_MODE InpAutoSizeMode = AUTO_SIZE_MODE_GROW; // Autosize Mode //--- global variables CEngine engine; color array_clr[]; //+------------------------------------------------------------------+
OnInit()ハンドラから、フォームや要素を作成するコードを削除します。1コマの作成のみを残します。パネル自体のループの中で、falseredrawフラグを持つ6つのバインドされたパネルオブジェクトを作成します。この場合、パネルの高さは、その中に作られるすべてのオブジェクトの高さの合計よりもわずかに低くなり、逆に幅は広くなります。このように、パネルの中に組み込んだオブジェクトの大きさに合わせて、パネルが調整されます。
//+------------------------------------------------------------------+ //| 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 WinForms Panel object CPanel *pnl=NULL; pnl=engine.CreateWFPanel("WFPanel",50,50,230,150,array_clr,200,true,true,false,-1,FRAME_STYLE_BEVEL,true); if(pnl!=NULL) { //--- Set Padding to 4 pnl.SetPaddingAll(4); //--- Set the flags of relocation, auto resizing and auto changing mode from the inputs pnl.SetMovable(InpMovable); pnl.SetAutoSize(InpAutoSize,false); pnl.SetAutoSizeMode((ENUM_CANV_ELEMENT_AUTO_SIZE_MODE)InpAutoSizeMode,false); //--- In the loop, create 6 bound panel objects for(int i=0;i<6;i++) { //--- create the panel object with coordinates along the X axis in the center and 10 along the Y axis, the width of 80 and the height of 50 CPanel *prev=pnl.GetElement(i-1); int xb=0, yb=0; int x=(i<3 ? (prev==NULL ? xb : prev.CoordXRelative()) : xb+prev.Width()+20); int y=(i<3 ? (prev==NULL ? yb : prev.BottomEdgeRelative()+16) : (i==3 ? yb : prev.BottomEdgeRelative()+16)); pnl.CreateNewElement(GRAPH_ELEMENT_TYPE_WF_PANEL,pnl,x,y,80,40,C'0xCD,0xDA,0xD7',200,true,false); } pnl.Redraw(true); } //--- return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+
パネル内にすべての要素を作成した後、パネル全体を作成した要素と共に完全に再描画します(trueredrawフラグ)。
OnChartEvent()イベントハンドラ、すなわちキーストローク処理ブロックに、Qキー押下ハンドラを追加します。
//--- If a key is pressed if(id==CHARTEVENT_KEYDOWN) { CPanel *panel=engine.GetWFPanel(0); if(panel!=NULL && (lparam==KEY_UP || lparam==KEY_DOWN || lparam==KEY_LEFT || lparam==KEY_RIGHT || lparam==KEY_FILL || lparam==KEY_ORIGIN || lparam==KEY_INDEX)) { for(int i=0;i<panel.ElementsTotal();i++) { CPanel *obj=panel.GetElement(i); if(obj!=NULL) { if(lparam==KEY_UP) obj.SetDockMode(CANV_ELEMENT_DOCK_MODE_TOP,false); else if(lparam==KEY_DOWN) obj.SetDockMode(CANV_ELEMENT_DOCK_MODE_BOTTOM,false); else if(lparam==KEY_LEFT) obj.SetDockMode(CANV_ELEMENT_DOCK_MODE_LEFT,false); else if(lparam==KEY_RIGHT) obj.SetDockMode(CANV_ELEMENT_DOCK_MODE_RIGHT,false); else if(lparam==KEY_FILL) obj.SetDockMode(CANV_ELEMENT_DOCK_MODE_FILL,false); else if(lparam==KEY_ORIGIN) obj.SetDockMode(CANV_ELEMENT_DOCK_MODE_NONE,false); else if(lparam==KEY_INDEX) { obj.SetDockMode((ENUM_CANV_ELEMENT_DOCK_MODE)i,true); Sleep(i>0 ? 500 : 0); } } } panel.Redraw(true); } }
バインドモードに従ってオブジェクトがどのように移動していくかを明確にするために、オブジェクトが新しい座標に移動するたびに0.5秒の休止を挿入します。
EAをコンパイルし、チャート上で起動します。
ご覧の通り、オブジェクトは各パネル面に正しくバインドされています。Qを押すと、それぞれのオブジェクトが適切なパネル面に接続されます。パネルの自動サイズ変更モードを変更すると、オートサイズ設定モードに合わせて内部コンテンツに適応します。
次の段階
次回は、WinFormsのオブジェクトについての説明を続けます。
**連載のこれまでの記事:
DoEasyコントロール(第1部):最初のステップ
DoEasyコントロール(第2部):CPanelクラスでの作業
DoEasyコントロール(第3部):バインドされたコントロールの作成
DoEasyコントロール(第4部):パネルコントロール、Padding、Dockパラメータ
MetaQuotes Ltdによってロシア語から翻訳されました。
元の記事: https://www.mql5.com/ru/articles/10794





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