
DoEasy-コントロール(第24部):ヒント補助WinFormsオブジェクト
内容
概念
ライブラリでは、多くのグラフィック要素が何らかの形で互いに関連しています。各コントロールには、その基本オブジェクトと、関連するオブジェクトの階層全体のメインの親オブジェクトへのポインタがあります。現在の実装では新規に作成されたグラフィック要素に対してこれらのオブジェクトを指定しており、その親オブジェクトを「失う」原因となる欠陥があります。オブジェクトを作成した後にこれらを指定してはいますが、これが必ずしも結果に結びつくとは限りません。エラーのある場所を見つけるのは、かなり複雑です。そこで、リレーションシップの階層で親オブジェクトを指定するロジックを変更することにしました。すべてのグラフィック要素のコンストラクタに、親オブジェクトへのポインタを追加するつもりです。したがって、グラフィカルオブジェクトを作成する瞬間にこれらを直接指定することになり、次のバインドコントロールを作成した後に毎回親オブジェクトを指定する手間を省くことができます。独立した(どの要素にも接続されないオブジェクトとして作成された)コントロールについては、親オブジェクトへのポインタとしてNULL値を渡すことにします。
このような手直しにより、すべてのオブジェクトが親を「認識」しているわけではないというバグを探す手間が省け、将来的に新しいライブラリコントロールの作成とデバッグが容易になります。
グラフィック要素オブジェクトのコンストラクタを仕上げるのに加えて、いくつかの新しいWinFormsオブジェクトを作成します。Windowsアプリケーションでコントロールを操作していると、マウスカーソルがオブジェクトの領域に入ったときに見た目が変わることに気がつくかもしれません。この変化は、この領域との相互作用によって、例えば、オブジェクトの外観が変化する可能性があることを示します。また、この領域でドラッグ&ドロップ機能を使うなど、何らかのアクションを実行できることを示す場合もあります。
残念ながら、MQL5言語ではマウスカーソルの外観を変更することはできませんが、どうにかしてライブラリの利用者に、コントロールとの相互作用の可能性を知らせる必要があります。私の解決策は、マウスカーソルによる操作が可能な場所では、補助的なグラフィックオブジェクトを表示することです。これらのオブジェクトの外観は、コントロールのこの領域で実行可能なアクションを示します。
このような補助的な要素をヒントオブジェクトと呼ぶことにします。すべてのヒントオブジェクトについて、基本的な機能を持つ基本オブジェクトを作成することにします。それは、それぞれの目的に応じて、具体的なオブジェクトを作るために使われます。
今回は、基本となるヒントオブジェクトとその派生物、つまり区切りを上下左右に移動させる可能性を示すオブジェクトを作成することにします。マウスカーソルがSplitContainerコントロールの区切り領域上にあると、カーソルの横にそのようなオブジェクトが表示され、区切りが左右や上下に移動できることを示すようになります。
将来的には、今日以降に作成されたヒントオブジェクトは、新しいコントロールの一部として使用されることになるでしょう。また、このようなオブジェクトを使って、すでに作成されたコントロールに追加される新しい機能を示すこともできます。例は、グラフィックオブジェクトのサイズを変更する可能性です。
ライブラリクラスの改善
ヒントオブジェクトに色と大きさを設定する必要があります。\MQL5\Include\DoEasy\Defines.mqhで定義しましょう。
#define CLR_DEF_CONTROL_SPLIT_CONTAINER_BACK_COLOR (C'0xF0,0xF0,0xF0') // SplitContainer control background color #define CLR_DEF_CONTROL_SPLIT_CONTAINER_MOUSE_DOWN (C'0xF0,0xF0,0xF0') // Color of SplitContainer control background when clicking on the control #define CLR_DEF_CONTROL_SPLIT_CONTAINER_MOUSE_OVER (C'0xF0,0xF0,0xF0') // Color of SplitContainer control background when hovering the mouse over the control #define CLR_DEF_CONTROL_SPLIT_CONTAINER_BORDER_COLOR (C'0x65,0x65,0x65') // SplitContainer control frame color #define CLR_DEF_CONTROL_HINT_BACK_COLOR (C'0xFF,0xFF,0xE1') // Hint control background color #define CLR_DEF_CONTROL_HINT_BORDER_COLOR (C'0x76,0x76,0x76') // Hint control frame color #define CLR_DEF_CONTROL_HINT_FORE_COLOR (C'0x5A,0x5A,0x5A') // Hint control text color #define DEF_CONTROL_LIST_MARGIN_X (1) // Gap between columns in ListBox controls #define DEF_CONTROL_LIST_MARGIN_Y (0) // Gap between rows in ListBox controls #define DEF_FONT ("Calibri") // Default font #define DEF_FONT_SIZE (8) // Default font size #define DEF_CHECK_SIZE (12) // Verification flag default size #define DEF_ARROW_BUTTON_SIZE (15) // Default arrow button size #define OUTER_AREA_SIZE (16) // Size of one side of the outer area around the form workspace #define DEF_FRAME_WIDTH_SIZE (3) // Default form/panel/window frame width #define DEF_HINT_ICON_SIZE (11) // Hint object side size //--- Graphical object parameters #define PROGRAM_OBJ_MAX_ID (10000) // Maximum value of an ID of a graphical object belonging to a program #define CTRL_POINT_RADIUS (5) // Radius of the control point on the form for managing graphical object pivot points #define CTRL_POINT_COLOR (clrDodgerBlue) // Radius of the control point on the form for managing graphical object pivot points #define CTRL_FORM_SIZE (40) // Size of the control point form for managing graphical object pivot points //+------------------------------------------------------------------+
ここで作成する新しいオブジェクトタイプを、グラフィック要素タイプのリストに追加します。
//+------------------------------------------------------------------+ //| The list of graphical element types | //+------------------------------------------------------------------+ enum ENUM_GRAPH_ELEMENT_TYPE { GRAPH_ELEMENT_TYPE_STANDARD, // Standard graphical object GRAPH_ELEMENT_TYPE_STANDARD_EXTENDED, // Extended standard graphical object //---... //---... GRAPH_ELEMENT_TYPE_WF_CHECKED_LIST_BOX, // Windows Forms CheckedListBox GRAPH_ELEMENT_TYPE_WF_BUTTON_LIST_BOX, // Windows Forms ButtonListBox //--- Auxiliary elements of WinForms objects GRAPH_ELEMENT_TYPE_WF_LIST_BOX_ITEM, // Windows Forms ListBoxItem GRAPH_ELEMENT_TYPE_WF_TAB_HEADER, // Windows Forms TabHeader GRAPH_ELEMENT_TYPE_WF_TAB_FIELD, // Windows Forms TabField GRAPH_ELEMENT_TYPE_WF_SPLIT_CONTAINER_PANEL, // Windows Forms SplitContainerPanel GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON, // Windows Forms ArrowButton GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_UP, // Windows Forms UpArrowButton GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_DOWN, // Windows Forms DownArrowButton GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_LEFT, // Windows Forms LeftArrowButton GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_RIGHT, // Windows Forms RightArrowButton GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTONS_UD_BOX, // Windows Forms UpDownArrowButtonsBox GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTONS_LR_BOX, // Windows Forms LeftRightArrowButtonsBox GRAPH_ELEMENT_TYPE_WF_SPLITTER, // Windows Forms Splitter GRAPH_ELEMENT_TYPE_WF_HINT_BASE, // Windows Forms HintBase GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_LEFT, // Windows Forms HintMoveLeft GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_RIGHT, // Windows Forms HintMoveRight GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_UP, // Windows Forms HintMoveUp GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_DOWN, // Windows Forms HintMoveDown }; //+------------------------------------------------------------------+
\MQL5\Include\DoEasy\Data.mqhに、ライブラリの新しいメッセージインデックスを追加します。
MSG_GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTONS_UD_BOX, // UpDownArrowBox control MSG_GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTONS_LR_BOX, // LeftRightArrowBox control MSG_GRAPH_ELEMENT_TYPE_WF_HINT_BASE, // HintBase control MSG_GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_LEFT, // HintMoveLeft control MSG_GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_RIGHT, // HintMoveLeft control MSG_GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_UP, // HintMoveLeft control MSG_GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_DOWN, // HintMoveLeft control MSG_GRAPH_OBJ_BELONG_PROGRAM, // Graphical object belongs to a program MSG_GRAPH_OBJ_BELONG_NO_PROGRAM, // Graphical object does not belong to a program
新しく追加したインデックスに対応するメッセージテキストも追加します。
{"Элемент управления \"UpDownArrowBox\"","Control element \"UpDownArrowBox\""}, {"Элемент управления \"LeftRightArrowBox\"","Control element \"LeftRightArrowBox\""}, {"Элемент управления \"HintBase\"","Control element \"HintBase\""}, {"Элемент управления \"HintMoveLeft\"","Control element \"HintMoveLeft\""}, {"Элемент управления \"HintMoveRight\"","Control element \"HintMoveRight\""}, {"Элемент управления \"HintMoveUp\"","Control element \"HintMoveUp\""}, {"Элемент управления \"HintMoveDown\"","Control element \"HintMoveDown\""}, {"Графический объект принадлежит программе","The graphic object belongs to the program"}, {"Графический объект не принадлежит программе","The graphic object does not belong to the program"},
ここで新たに作成されるオブジェクトの説明を表示することができるようになりました。グラフィカルオブジェクトの説明を表示するためには、\MQL5\Include\DoEasy\Objects\Graph\GBaseObj.mqhのメソッドを使用します。そのメソッドに、要素タイプ別に必要な文字列の戻り値を追加します。
//+------------------------------------------------------------------+ //| Return the description of the graphical element type | //+------------------------------------------------------------------+ string CGBaseObj::TypeElementDescription(const ENUM_GRAPH_ELEMENT_TYPE type) { return ( type==GRAPH_ELEMENT_TYPE_STANDARD ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_STANDARD) : type==GRAPH_ELEMENT_TYPE_STANDARD_EXTENDED ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_STANDARD_EXTENDED) : type==GRAPH_ELEMENT_TYPE_ELEMENT ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_ELEMENT) : type==GRAPH_ELEMENT_TYPE_SHADOW_OBJ ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_SHADOW_OBJ) : type==GRAPH_ELEMENT_TYPE_FORM ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_FORM) : type==GRAPH_ELEMENT_TYPE_WINDOW ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WINDOW) : //--- WinForms type==GRAPH_ELEMENT_TYPE_WF_UNDERLAY ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_UNDERLAY) : type==GRAPH_ELEMENT_TYPE_WF_BASE ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_BASE) : //--- Containers type==GRAPH_ELEMENT_TYPE_WF_CONTAINER ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_CONTAINER) : type==GRAPH_ELEMENT_TYPE_WF_GROUPBOX ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_GROUPBOX) : type==GRAPH_ELEMENT_TYPE_WF_PANEL ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_PANEL) : type==GRAPH_ELEMENT_TYPE_WF_TAB_CONTROL ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_TAB_CONTROL) : type==GRAPH_ELEMENT_TYPE_WF_SPLIT_CONTAINER ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_SPLIT_CONTAINER) : //--- Standard controls type==GRAPH_ELEMENT_TYPE_WF_COMMON_BASE ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_COMMON_BASE) : type==GRAPH_ELEMENT_TYPE_WF_LABEL ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_LABEL) : type==GRAPH_ELEMENT_TYPE_WF_CHECKBOX ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_CHECKBOX) : type==GRAPH_ELEMENT_TYPE_WF_RADIOBUTTON ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_RADIOBUTTON) : type==GRAPH_ELEMENT_TYPE_WF_BUTTON ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_BUTTON) : type==GRAPH_ELEMENT_TYPE_WF_ELEMENTS_LIST_BOX ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_ELEMENTS_LIST_BOX) : type==GRAPH_ELEMENT_TYPE_WF_LIST_BOX ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_LIST_BOX) : type==GRAPH_ELEMENT_TYPE_WF_LIST_BOX_ITEM ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_LIST_BOX_ITEM) : type==GRAPH_ELEMENT_TYPE_WF_CHECKED_LIST_BOX ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_CHECKED_LIST_BOX) : type==GRAPH_ELEMENT_TYPE_WF_BUTTON_LIST_BOX ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_BUTTON_LIST_BOX) : //--- Auxiliary control objects type==GRAPH_ELEMENT_TYPE_WF_TAB_HEADER ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_TAB_HEADER) : type==GRAPH_ELEMENT_TYPE_WF_TAB_FIELD ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_TAB_FIELD) : type==GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON) : type==GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_UP ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_UP) : type==GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_DOWN ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_DOWN) : type==GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_LEFT ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_LEFT) : type==GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_RIGHT ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_RIGHT) : type==GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTONS_UD_BOX ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTONS_UD_BOX) : type==GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTONS_LR_BOX ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTONS_LR_BOX) : type==GRAPH_ELEMENT_TYPE_WF_SPLIT_CONTAINER_PANEL ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_SPLIT_CONTAINER_PANEL) : type==GRAPH_ELEMENT_TYPE_WF_SPLITTER ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_SPLITTER) : type==GRAPH_ELEMENT_TYPE_WF_HINT_BASE ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_HINT_BASE) : type==GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_LEFT ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_LEFT) : type==GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_RIGHT ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_RIGHT) : type==GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_UP ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_UP) : type==GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_DOWN ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_DOWN) : "Unknown" ); } //+------------------------------------------------------------------+
グラフィック要素オブジェクトのクラスで、親オブジェクトを指定し、受け取るという主な変更点を実装します。作成されたグラフィック要素の関係階層の基本オブジェクトとメインオブジェクトへのポインタは、クラスコンストラクタに送られることになっています。
\MQL5\Include\DoEasy\Objects\Graph\GCnvElement.mqhで、親オブジェクトを設定するメソッドを削除して、ポインタを返すメソッドのみを残します。IsBase()メソッドはIsDependent()に名称変更されました。
//--- (1) Set and (2) return the initial shift of the (1) X and (2) Y coordinate relative to the base object void SetCoordXRelativeInit(const int value) { this.m_init_relative_x=value; } void SetCoordYRelativeInit(const int value) { this.m_init_relative_y=value; } int CoordXRelativeInit(void) const { return this.m_init_relative_x; } int CoordYRelativeInit(void) const { return this.m_init_relative_y; } //--- Return the pointer to the parent element within related objects of the current group CGCnvElement *GetBase(void) { return this.m_element_base; } //--- Return the pointer to the parent element within all groups of related objects CGCnvElement *GetMain(void) { return(this.m_element_main==NULL ? this.GetObject() : this.m_element_main); } //--- Return the flag indicating that the object is (1) main, (2) base bool IsMain(void) { return this.m_element_main==NULL; } bool IsDependent(void) { return this.m_element_base!=NULL; } //--- Get the (1) main and (2) base object ID int GetMainID(void) { if(this.IsMain()) return this.ID(); CGCnvElement *main=this.GetMain(); return(main!=NULL ? main.ID() : WRONG_VALUE); } int GetBaseID(void) { if(!this.IsDependent()) return this.ID(); CGCnvElement *base=this.GetBase(); return(base!=NULL ? base.ID() : WRONG_VALUE); }
IsBaseメソッドの名前は直接的に、このメソッドが他のオブジェクトの基本オブジェクトであるかどうかを確認することを示唆しますが、これは完全に正しいとは言えません。ポイントは、変数m_element_baseの値がNULLに等しい場合、そのオブジェクトは基本となるものを持たない、つまり他のオブジェクトに接続されていないことを示すということです。ただし、他のオブジェクトの基本オブジェクトとして機能すること(接続オブジェクトとしてその内部に作成された場合)もしないこと(グラフィック要素が接続されていない場合)もあります。
したがって、このような方法で質問する(Is Base)のは誤りであることがわかります。m_element_baseがNULLならば、そのオブジェクトは他のオブジェクトに接続されていない(つまり独立している)ことになり、そうでなければ、そのオブジェクトは親の基本オブジェクトに依存していることになります。そこで、IsBaseメソッドの名前をIsDependentに変更し、オブジェクトの依存関係を定義するロジックを変更しました。m_element_baseがNULLに等しくないことを示すフラグが返されます。
それに伴い、基本オブジェクトの確認も変更されました。このメソッドのように、オブジェクト独立性フラグを確認します。
int GetBaseID(void) { if(!this.IsDependent()) return this.ID(); CGCnvElement *base=this.GetBase(); return(base!=NULL ? base.ID() : WRONG_VALUE); }
オブジェクトが他にバインドされていない場合は、そのIDが返されます。バインドされている場合は、基本オブジェクトのIDが返されます。
コンストラクタ(protectedとpublicパラメトリック)の宣言では、新しい形式変数を書いて、生成されたオブジェクトがバインドされるメインオブジェクトと基本オブジェクトへのポインタを渡すことにします。
protected: //--- Protected constructor CGCnvElement(const ENUM_GRAPH_ELEMENT_TYPE element_type, CGCnvElement *main_obj,CGCnvElement *base_obj, const long chart_id, const int wnd_num, const string descript, const int x, const int y, const int w, const int h); public: //--- (1) Set and (2) return the X coordinate shift relative to the base object void SetCoordXRelative(const int value) { this.m_shift_coord_x=value; } int CoordXRelative(void) const { return this.m_shift_coord_x; } //--- (1) Set and (2) return the Y coordinate shift relative to the base object void SetCoordYRelative(const int value) { this.m_shift_coord_y=value; } int CoordYRelative(void) const { return this.m_shift_coord_y; } //--- Event handler virtual void OnChartEvent(const int id,const long& lparam,const double& dparam,const string& sparam); //--- Parametric constructor CGCnvElement(const ENUM_GRAPH_ELEMENT_TYPE element_type, CGCnvElement *main_obj,CGCnvElement *base_obj, const int element_id, const int element_num, const long chart_id, const int wnd_num, const string descript, const int x, const int y, const int w, const int h, const color colour, const uchar opacity, const bool movable=true, const bool activity=true, const bool redraw=false); //--- Default constructor CGCnvElement() : m_shadow(false),m_chart_color_bg((color)::ChartGetInteger(::ChartID(),CHART_COLOR_BACKGROUND)) { this.m_type=OBJECT_DE_TYPE_GELEMENT; this.m_element_main=NULL; this.m_element_base=NULL; this.m_shift_coord_x=0; this.m_shift_coord_y=0; } //--- Destructor ~CGCnvElement() { this.m_canvas.Destroy(); } //+------------------------------------------------------------------+
オブジェクトを作成する際には、その2つの親オブジェクトへのポインタをコンストラクタに渡す必要があるのは間違いないでしょう。オブジェクトが独立して作成された場合は、ポインタの代わりにNULLを入力します。これにより、新しいグラフィック要素を作成した後に、これらのオブジェクトを指定するための追加アクションを完全に省略することができます。
前回は、コンストラクタで親オブジェクトのデフォルト値を指定しました。
this.m_element_main=NULL; this.m_element_base=NULL;
ここで再び、仮パラメータで渡された値を追加します。
//+------------------------------------------------------------------+ //| Parametric constructor | //+------------------------------------------------------------------+ CGCnvElement::CGCnvElement(const ENUM_GRAPH_ELEMENT_TYPE element_type, CGCnvElement *main_obj,CGCnvElement *base_obj, const int element_id, const int element_num, const long chart_id, const int wnd_num, const string descript, const int x, const int y, const int w, const int h, const color colour, const uchar opacity, const bool movable=true, const bool activity=true, const bool redraw=false) : m_shadow(false) { this.SetTypeElement(element_type); this.m_type=OBJECT_DE_TYPE_GELEMENT; this.m_element_main=main_obj; this.m_element_base=base_obj; this.m_chart_color_bg=(color)::ChartGetInteger((chart_id==NULL ? ::ChartID() : chart_id),CHART_COLOR_BACKGROUND); this.m_name=this.CreateNameGraphElement(element_type); //---... //---...
これで、作成された各オブジェクトは、すぐにその親オブジェクトを「知る」ことができるようになりました。このグラフィック要素が他に接続されていない場合、両方の変数がNULL値を持ち、オブジェクト自体がメインで独立していることを示します。
計算された矩形の可視領域で画像を切り出すメソッドに、オブジェクトが親から独立していることを確認する機能が追加されました。
//+------------------------------------------------------------------+ //| Crop the image outlined by the calculated | //| rectangular visibility scope | //+------------------------------------------------------------------+ void CGCnvElement::Crop(void) { //--- Get the pointer to the base object CGCnvElement *base=this.GetBase(); //--- If the object does not have a base object it is attached to, then there is no need to crop the hidden areas - leave if(!this.IsDependent()) return; //--- Set the initial coordinates and size of the visibility scope to the entire object //---... //---...
影オブジェクトクラスのファイル( \MQL5\Include\DoEasy\Objects\Graph\ShadowObj.mqh)で、メインオブジェクトと基本オブジェクトへのポインタを渡すようにします。
//--- Draw the object shadow form void DrawShadowFigureRect(const int w,const int h); public: //--- Constructor indicating the main and base objects, chart ID and subwindow CShadowObj(CGCnvElement *main_obj,CGCnvElement *base_obj, const long chart_id, const int subwindow, const string name, const int x, const int y, const int w, const int h); //--- Supported object properties (1) integer and (2) string ones
コンストラクタを実装する際に、メインオブジェクトと基本オブジェクトへのポインタを親オブジェクトに渡します。
//+------------------------------------------------------------------+ //| Protected constructor with an object type, | //| chart ID and subwindow | //+------------------------------------------------------------------+ CShadowObj::CShadowObj(CGCnvElement *main_obj,CGCnvElement *base_obj, const long chart_id, const int subwindow, const string name, const int x, const int y, const int w, const int h) : CGCnvElement(GRAPH_ELEMENT_TYPE_SHADOW_OBJ,main_obj,base_obj,chart_id,subwindow,name,x,y,w,h) { this.m_type=OBJECT_DE_TYPE_GSHADOW; CGCnvElement::SetBackgroundColor(clrNONE,true); CGCnvElement::SetOpacity(0); CGCnvElement::SetActive(false); this.m_opacity=CLR_DEF_SHADOW_OPACITY; this.m_blur=DEF_SHADOW_BLUR; color gray=CGCnvElement::ChangeColorSaturation(this.ChartBackgroundColor(),-100); this.m_color=CGCnvElement::ChangeColorLightness(gray,-50); this.m_shadow=false; this.SetVisibleFlag(false,false); CGCnvElement::Erase(); } //+------------------------------------------------------------------+
任意のオブジェクトに対して影オブジェクトを作成する場合、影オブジェクトコンストラクタは、影が作成されるオブジェクトへのポインタを受け取ります(または、そのために作成されます)。したがって、このコンストラクタでは、メインオブジェクトと基本オブジェクトへのポインタがCGCnvElement基本オブジェクトコンストラクタの適切な変数に渡されることになります。このように、すべての子クラスのコンストラクタを通じて、継承階層全体の親オブジェクトへのポインタを連鎖的に渡しています。こうすれば、作成したコントロールのメインオブジェクトと基本オブジェクトが、作成時に常に正確に指定されていることを確認することができます。
フォームオブジェクトクラスのファイル(\MQL5\Include\DoEasy\Objects\Graph\Form.mqh)のすべてのコンストラクタで、メインオブジェクトと基本オブジェクトへのポインタを渡すようにします。
protected: //--- Protected constructor with object type, chart ID and subwindow CForm(const ENUM_GRAPH_ELEMENT_TYPE type, CGCnvElement *main_obj,CGCnvElement *base_obj, const long chart_id, const int subwindow, const string descript, const int x, const int y, const int w, const int h); public: //--- Constructors CForm(CGCnvElement *main_obj,CGCnvElement *base_obj, const long chart_id, const int subwindow, const string descript, const int x, const int y, const int w, const int h); CForm() { this.m_type=OBJECT_DE_TYPE_GFORM; this.Initialize(); } //--- Destructor ~CForm();
...
//+------------------------------------------------------------------+ //| Protected constructor with an object type, | //| chart ID and subwindow | //+------------------------------------------------------------------+ CForm::CForm(const ENUM_GRAPH_ELEMENT_TYPE type, CGCnvElement *main_obj,CGCnvElement *base_obj, const long chart_id, const int subwindow, const string descript, const int x, const int y, const int w, const int h) : CGCnvElement(type,main_obj,base_obj,chart_id,subwindow,descript,x,y,w,h) { this.m_type=OBJECT_DE_TYPE_GFORM; this.Initialize(); this.SetCoordXInit(x); this.SetCoordYInit(y); this.SetWidthInit(w); this.SetHeightInit(h); } //+------------------------------------------------------------------+ //| Constructor indicating the main and base objects, | //| chart ID and subwindow | //+------------------------------------------------------------------+ CForm::CForm(CGCnvElement *main_obj,CGCnvElement *base_obj, const long chart_id, const int subwindow, const string descript, const int x, const int y, const int w, const int h) : CGCnvElement(GRAPH_ELEMENT_TYPE_FORM,main_obj,base_obj,chart_id,subwindow,descript,x,y,w,h) { this.m_type=OBJECT_DE_TYPE_GFORM; this.Initialize(); this.SetCoordXInit(x); this.SetCoordYInit(y); this.SetWidthInit(w); this.SetHeightInit(h); } //+------------------------------------------------------------------+
グラフィカルオブジェクトを新規作成するメソッドで、メインオブジェクトと基本オブジェクトへのポインタを渡すようにします。
//+------------------------------------------------------------------+ //| Create a new graphical object | //+------------------------------------------------------------------+ CGCnvElement *CForm::CreateNewGObject(const ENUM_GRAPH_ELEMENT_TYPE type, const int obj_num, const string descript, const int x, const int y, const int w, const int h, const color colour, const uchar opacity, const bool movable, const bool activity) { CGCnvElement *element=NULL; //--- Depending on the created object type, switch(type) { //--- create a graphical element object case GRAPH_ELEMENT_TYPE_ELEMENT : element=new CGCnvElement(type,this.GetMain(),this.GetObject(),this.ID(),obj_num,this.ChartID(),this.SubWindow(),descript,x,y,w,h,colour,opacity,movable,activity); break; //--- create a form object case GRAPH_ELEMENT_TYPE_FORM : element=new CForm(type,this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h); break; default: break; } if(element==NULL) ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_FAILED_CREATE_ELM_OBJ),this.TypeElementDescription(type)); element.SetMovable(movable); element.SetCoordXRelative(element.CoordX()-this.CoordX()); element.SetCoordYRelative(element.CoordY()-this.CoordY()); return element; } //+------------------------------------------------------------------+
これらの文字列には、基本オブジェクトにバインドされた要素が生成されるため、GetMain()メソッドの返す値はメインオブジェクトへのポインタとして渡され、GetObject()メソッドの返す値は基本オブジェクトへのポインタとして渡されます。メインオブジェクトへのポインタは、このオブジェクトの生成時にすでに登録されています。このポインタを、新しく生成されたバインドオブジェクトのコンストラクタに送ります。したがって、リンクチェーンの階層全体のメインオブジェクトは常に同じもの、つまりこのチェーンの一番最初のものであり、このオブジェクトへのポインタが基本オブジェクトへのポインタとして渡されます。よって、新たに生成されるバウンド要素の基本オブジェクトへのポインタは、このオブジェクトを指す必要があります。
CreateAndAddNewElement()およびCreateNewElement()メソッドからメインオブジェクトと基本オブジェクトを削除します。これらのメソッドはすでにグラフィック要素オブジェクトクラスから削除され、クラスのコンストラクタでメインオブジェクトと基本オブジェクトへのポインタが渡されるようになっているためです。
//+------------------------------------------------------------------+ //| Create a new attached element | //| and add it to the list of bound objects | //+------------------------------------------------------------------+ CGCnvElement *CForm::CreateAndAddNewElement(const ENUM_GRAPH_ELEMENT_TYPE element_type, const int x, const int y, const int w, const int h, const color colour, const uchar opacity, const bool activity) { //--- If the type of a created graphical element is less than the "element", inform of that and return 'false' if(element_type<GRAPH_ELEMENT_TYPE_ELEMENT) { ::Print(DFUN,CMessage::Text(MSG_FORM_OBJECT_ERR_NOT_INTENDED),::StringSubstr(::EnumToString(element_type),19)); .return NULL; } //--- Specify the element index in the list int num=this.m_list_elements.Total(); //--- Create a description of the default graphical element string descript=TypeGraphElementAsString(element_type); //--- Get the screen coordinates of the object relative to the coordinate system of the base object int elm_x=x; int elm_y=y; this.GetCoords(elm_x,elm_y); //--- Create a new graphical element CGCnvElement *obj=this.CreateNewGObject(element_type,num,descript,elm_x,elm_y,w,h,colour,opacity,false,activity); if(obj==NULL) .return NULL; //--- and add it to the list of bound graphical elements if(!this.AddNewElement(obj,elm_x,elm_y)) { delete obj; .return NULL; } //--- Set the minimum properties for a bound graphical element obj.SetBackgroundColor(colour,true); obj.SetOpacity(opacity); obj.SetActive(activity); obj.SetMain(this.GetMain()==NULL ? this.GetObject() : this.GetMain()); obj.SetBase(this.GetObject()); obj.SetID(this.GetMaxIDAll()+1); obj.SetNumber(num); obj.SetCoordXRelative(obj.CoordX()-this.CoordX()); obj.SetCoordYRelative(obj.CoordY()-this.CoordY()); obj.SetZorder(this.Zorder(),false); obj.SetCoordXRelativeInit(obj.CoordXRelative()); obj.SetCoordYRelativeInit(obj.CoordYRelative()); obj.SetVisibleFlag(this.IsVisible(),false); obj.SetActive(this.Active()); obj.SetEnabled(this.Enabled()); return obj; } //+------------------------------------------------------------------+
...
//+------------------------------------------------------------------+ //| Create a new attached element | //+------------------------------------------------------------------+ bool CForm::CreateNewElement(const ENUM_GRAPH_ELEMENT_TYPE element_type, const int x, const int y, const int w, const int h, const color colour, const uchar opacity, const bool activity, const bool redraw) { //--- Create a new graphical element CGCnvElement *obj=this.CreateAndAddNewElement(element_type,x,y,w,h,colour,opacity,activity); //--- If the object has been created, draw the added object and return 'true' if(obj==NULL) return false; obj.SetMain(this.GetMain()==NULL ? this.GetObject() : this.GetMain()); obj.SetBase(this.GetBase()); obj.Erase(colour,opacity,redraw); return true; } //+------------------------------------------------------------------+
影オブジェクトを作成するメソッドで、影オブジェクトクラスのコンストラクタにメインオブジェクトと基本オブジェクトへのポインタを渡します。
//+------------------------------------------------------------------+ //| Create the shadow object | //+------------------------------------------------------------------+ void CForm::CreateShadowObj(const color colour,const uchar opacity) { //--- If the shadow flag is disabled or the shadow object already exists, exit if(!this.m_shadow || this.m_shadow_obj!=NULL) return; //--- Calculate the shadow object coordinates according to the offset from the top and left int x=this.CoordX()-OUTER_AREA_SIZE; int y=this.CoordY()-OUTER_AREA_SIZE; //--- Calculate the width and height in accordance with the top, bottom, left and right offsets int w=this.Width()+OUTER_AREA_SIZE*2; int h=this.Height()+OUTER_AREA_SIZE*2; //--- Create a new shadow object and set the pointer to it in the variable this.m_shadow_obj=new CShadowObj(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),this.NameObj()+"Shadow",x,y,w,h); if(this.m_shadow_obj==NULL) { ::Print(DFUN,CMessage::Text(MSG_FORM_OBJECT_ERR_FAILED_CREATE_SHADOW_OBJ)); return; } this.m_list_tmp.Add(this.m_shadow_obj); //--- Set the properties for the created shadow object this.m_shadow_obj.SetID(this.ID()); this.m_shadow_obj.SetNumber(-1); this.m_shadow_obj.SetOpacity(opacity); this.m_shadow_obj.SetColor(colour); this.m_shadow_obj.SetMovable(this.Movable()); this.m_shadow_obj.SetActive(false); this.m_shadow_obj.SetVisibleFlag(false,false); //--- Move the form object to the foreground this.BringToTop(); } //+------------------------------------------------------------------+
ここでのポインタの受け渡しのロジックは、上記と全く同じで、このオブジェクトに設定されたメインオブジェクトを取得して送信し、このオブジェクトを基本として指定します。
要素の座標を更新するメソッドでは、オブジェクト独立フラグとこれがメインオブジェクトであることを示すフラグを確認します。
//+------------------------------------------------------------------+ //| Update the coordinate elements | //+------------------------------------------------------------------+ bool CForm::Move(const int x,const int y,const bool redraw=false) { //--- Get the pointers to the base and main objects in the bound objects hierarchy, as well as the shadow object CGCnvElement *base=this.GetBase(); CGCnvElement *main=this.GetMain(); bool res=true; //--- If the element is not movable and it is an independent object, leave if(!this.IsDependent() && !this.Movable()) return false; //--- If the object has a shadow and we failed to set new coordinate values to the properties of the shadow object, return 'false' if(this.m_shadow && this.m_shadow_obj!=NULL) { if(!this.m_shadow_obj.Move(x-OUTER_AREA_SIZE+this.m_shadow_obj.CoordXRelative(),y-OUTER_AREA_SIZE+this.m_shadow_obj.CoordYRelative(),false)) return false; } //--- If failed to set new values into graphical object properties, return 'false' if(!this.SetCoordX(x) || !this.SetCoordY(y)) return false; //--- Shift all bound objects if(!this.MoveDependentObj(x,y,false)) return false; //--- If the update flag is set, and this is the main object, redraw the chart. if(this.IsMain() && redraw) ::ChartRedraw(this.ChartID()); //--- Return 'true' return true; } //+------------------------------------------------------------------+
グラフィック要素管理クラスのファイル(\MQL5\Include\DoEasy\Objects\Graph\GraphElmControl.mqh)にある、指定されたサブウィンドウの指定されたチャートにフォームオブジェクトを作成するメソッドに、メインオブジェクトと基本オブジェクトの指定またはその指定がないことを示す記述を追加します。
//+----------------------------------------------------------------------+ //| Create the form object on a specified chart in a specified subwindow | //+----------------------------------------------------------------------+ CForm *CGraphElmControl::CreateForm(const int form_id,const long chart_id,const int wnd,const string name,const int x,const int y,const int w,const int h) { CForm *form=new CForm(NULL,NULL,chart_id,wnd,name,x,y,w,h); if(form==NULL) .return NULL; form.SetID(form_id); form.SetNumber(0); return form; } //+------------------------------------------------------------------+
ここでは、フォームオブジェクトのコンストラクタに、メインオブジェクトと基本オブジェクトへのポインタとしてNULL値を渡しています。これは、作成されたオブジェクトが他のオブジェクトと結びつかず、それ自身がメインオブジェクトであることを意味します。
また、\MQL5\Include\DoEasy\Objects\Graph\Extend\CGStdGraphObjExtToolkit.mqhにある、GraphicObjectのアンカーポイントを管理するフォームのコンストラクタクラスの初期化文字列でNULL値を指定します。
//+------------------------------------------------------------------+ //| Class of the form for managing pivot points of a graphical object| //+------------------------------------------------------------------+ class CFormControl : public CForm { private: bool m_drawn; // Flag indicating that the pivot point is drawn on the form int m_pivot_point; // Pivot point managed by the form public: //--- (1) Return and (2) set the drawn point flag bool IsControlAlreadyDrawn(void) const { return this.m_drawn; } void SetControlPointDrawnFlag(const bool flag) { this.m_drawn=flag; } //--- (1) Return and (2) set the pivot point managed by the form int GraphObjPivotPoint(void) const { return this.m_pivot_point; } void SetGraphObjPivotPoint(const int index) { this.m_pivot_point=index; } //--- Constructor CFormControl(void) { this.m_type=OBJECT_DE_TYPE_GFORM_CONTROL; } CFormControl(const long chart_id,const int subwindow,const string name,const int pivot_point,const int x,const int y,const int w,const int h) : CForm(GRAPH_ELEMENT_TYPE_FORM,NULL,NULL,chart_id,subwindow,name,x,y,w,h) { this.m_type=OBJECT_DE_TYPE_GFORM_CONTROL; this.m_pivot_point=pivot_point; } }; //+------------------------------------------------------------------+
すべてのWinFormsライブラリオブジェクトの基本オブジェクトクラスのファイル(MQL5\Include\DoEasy\Objects\Graph\WForms\WinFormBase.mqh)にあるクラスのコンストラクタでメインオブジェクトと基本オブジェクトへのポインタを渡すようにします。
protected: //--- Protected constructor with object type, chart ID and subwindow CWinFormBase(const ENUM_GRAPH_ELEMENT_TYPE type, CGCnvElement *main_obj,CGCnvElement *base_obj, const long chart_id, const int subwindow, const string descript, const int x, const int y, const int w, const int h); public: //--- Constructors CWinFormBase(CGCnvElement *main_obj,CGCnvElement *base_obj, const long chart_id, const int subwindow, const string descript, const int x, const int y, const int w, const int h); //--- (1) Set and (2) return the default text color of all panel objects
...
//+------------------------------------------------------------------+ //| Protected constructor with an object type, | //| chart ID and subwindow | //+------------------------------------------------------------------+ CWinFormBase::CWinFormBase(const ENUM_GRAPH_ELEMENT_TYPE type, CGCnvElement *main_obj,CGCnvElement *base_obj, const long chart_id, const int subwindow, const string descript, const int x, const int y, const int w, const int h) : CForm(type,main_obj,base_obj,chart_id,subwindow,descript,x,y,w,h) { //--- Set the specified graphical element type for the object and assign the library object type to the current object this.SetTypeElement(type); this.m_type=OBJECT_DE_TYPE_GWF_BASE; //--- Initialize all variables this.SetText(""); this.SetForeColor(CLR_DEF_FORE_COLOR,true); this.SetForeStateOnColor(this.ForeColor(),true); this.SetForeStateOnColorMouseDown(this.ForeColor()); this.SetForeStateOnColorMouseOver(this.ForeColor()); this.SetForeColorOpacity(CLR_DEF_FORE_COLOR_OPACITY); this.SetFontBoldType(FW_TYPE_NORMAL); this.SetMarginAll(0); this.SetPaddingAll(0); this.SetBorderSizeAll(0); this.SetDockMode(CANV_ELEMENT_DOCK_MODE_NONE,false); this.SetBorderStyle(FRAME_STYLE_NONE); this.SetAutoSize(false,false); CForm::SetCoordXInit(x); CForm::SetCoordYInit(y); CForm::SetWidthInit(w); CForm::SetHeightInit(h); this.m_shadow=false; this.m_gradient_v=true; this.m_gradient_c=false; } //+------------------------------------------------------------------+ //| Constructor indicating the main and base objects, | //| chart ID and subwindow | //+------------------------------------------------------------------+ CWinFormBase::CWinFormBase(CGCnvElement *main_obj,CGCnvElement *base_obj, const long chart_id, const int subwindow, const string descript, const int x, const int y, const int w, const int h) : CForm(GRAPH_ELEMENT_TYPE_WF_BASE,main_obj,base_obj,chart_id,subwindow,descript,x,y,w,h) { //--- Set the graphical element and library object types as a base WinForms object this.SetTypeElement(GRAPH_ELEMENT_TYPE_WF_BASE); this.m_type=OBJECT_DE_TYPE_GWF_BASE; //--- Initialize all variables this.SetText(""); this.SetForeColor(CLR_DEF_FORE_COLOR,true); this.SetForeStateOnColor(this.ForeColor(),true); this.SetForeStateOnColorMouseDown(this.ForeColor()); this.SetForeStateOnColorMouseOver(this.ForeColor()); this.SetForeColorOpacity(CLR_DEF_FORE_COLOR_OPACITY); this.SetFontBoldType(FW_TYPE_NORMAL); this.SetMarginAll(0); this.SetPaddingAll(0); this.SetBorderSizeAll(0); this.SetDockMode(CANV_ELEMENT_DOCK_MODE_NONE,false); this.SetBorderStyle(FRAME_STYLE_NONE); this.SetAutoSize(false,false); CForm::SetCoordXInit(x); CForm::SetCoordYInit(y); CForm::SetWidthInit(w); CForm::SetHeightInit(h); this.m_shadow=false; this.m_gradient_v=true; this.m_gradient_c=false; } //+------------------------------------------------------------------+
オブジェクトを再描画するメソッドで、これがメインオブジェクトであることの確認を変更します。
//+------------------------------------------------------------------+ //| Redraw the object | //+------------------------------------------------------------------+ void CWinFormBase::Redraw(bool redraw) { //--- If the object type is less than the "Base WinForms object" or the object is not to be displayed, exit if(this.TypeGraphElement()<GRAPH_ELEMENT_TYPE_WF_BASE || !this.Displayed()) return; //--- Get the "Shadow" object //---... //---... //--- If the redraw flag is set and if this is the main object the rest are bound to, //--- redraw the chart to display changes immediately if(this.IsMain() && redraw) ::ChartRedraw(this.ChartID()); } //+------------------------------------------------------------------+
以前は、ここで以下のような方法で確認をおこなっていました。
if(redraw && this.GetMain()==NULL)
要するに同じことなのですが、せっかくメソッドがあるのだから、使ってみることにします。
クラスのコンストラクタとオブジェクトがメインまたは基本であることの確認に対する上記の改良は、すべてのWinFormsライブラリオブジェクトの絶対にすべてのクラスですでに実行されています。新しいオブジェクトの場合、これがデフォルトの動作となります。ここでは、記事スペースの節約のため、これ以上の改良点については説明しません。
次のクラスファイルのヘッダでは、メインオブジェクトと基本オブジェクトへのポインタを渡すようにコンストラクタが改良されただけです。
CommonBase.mqh、Button.mqh、ElementsListBox.mqh、RadioButton.mqh、ArrowButton.mqh、ArrowLeftButton.mqh、ArrowRightButton.mqh、ArrowUpButton.mqh、ArrowDownButton.mqh、ListBoxItem.mqh、TabHeader.mqh。
しかし、コンストラクタの改良に加えて、新しいグラフィカルオブジェクトを作成するためのメソッドに変更が加えられたクラスファイルもあります。ここでは、作成したオブジェクトのコンストラクタにメインオブジェクトと基本オブジェクトへのポインタを渡すように改良しています。
//+------------------------------------------------------------------+ //| Create a new graphical object | //+------------------------------------------------------------------+ CGCnvElement *CButtonListBox::CreateNewGObject(const ENUM_GRAPH_ELEMENT_TYPE type, const int obj_num, const string descript, const int x, const int y, const int w, const int h, const color colour, const uchar opacity, const bool movable, const bool activity) { //--- create the CButton object CGCnvElement *element=new CButton(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h); if(element==NULL) ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_FAILED_CREATE_ELM_OBJ),this.TypeElementDescription(type)); //--- set the object relocation flag and relative coordinates element.SetMovable(movable); element.SetCoordXRelative(element.CoordX()-this.CoordX()); element.SetCoordYRelative(element.CoordY()-this.CoordY()); return element; } //+------------------------------------------------------------------+
新しいグラフィカルオブジェクトを作成するメソッドについても、(コンストラクタの改良に加えて)ファイル内で同じまたは類似の改良が施されています。
ButtonListBox.mqh、CheckedListBox.mqh、ListBox.mqh、ArrowLeftRightBox.mqh、ArrowUpDownBox.mqh。
それでは、新しい補助ヒントオブジェクトを作成してみましょう。
ヒント補助オブジェクトとその派生物
このようなオブジェクトを構築する概念は、ほとんどのライブラリのオブジェクトを構築する概念と何ら変わりはないでしょう。ここには基本オブジェクトがあり、その子孫は特定のヒントオブジェクトの必要な機能を洗練して実装することになります。
\MQL5\Include\DoEasy\Objects\Graph\WForms\で、CWinFormBaseクラスのWinFormBase.mqhを新規作成します。このクラスは、すべてのWinFormsオブジェクトの基本クラスであるCWinFormBaseから派生させる必要があり、そのファイルは作成されたクラスのファイルにインクルードする必要があります。
//+------------------------------------------------------------------+ //| HintBase.mqh | //| Copyright 2022, MetaQuotes Ltd. | //| https://mql5.com/ja/users/artmedia70 | //+------------------------------------------------------------------+ #property copyright "Copyright 2022, MetaQuotes Ltd." #property link "https://mql5.com/ja/users/artmedia70" #property version "1.00" #property strict // Necessary for mql4 //+------------------------------------------------------------------+ //| Include files | //+------------------------------------------------------------------+ #include "..\WinFormBase.mqh" //+------------------------------------------------------------------+ //| Class of the base Hint object of the WForms controls | //+------------------------------------------------------------------+ class CHintBase : public CWinFormBase { }
protectedセクションで、ヒントを描画するための仮想メソッドを書き、protectedコンストラクタを宣言します。クラスのpublicセクションには、ヒントの色を設定したり返したりするメソッドを書き、パラメトリックコンストラクタとオブジェクトの表示、再描画、クリアのための仮想メソッド、およびオブジェクトの枠を描画するメソッドを宣言することにします。
//+------------------------------------------------------------------+ //| Class of the base Hint object of the WForms controls | //+------------------------------------------------------------------+ class CHintBase : public CWinFormBase { private: protected: //--- Draw a hint virtual void DrawHint(const int shift) {return;} //--- Protected constructor with object type, chart ID and subwindow CHintBase(const ENUM_GRAPH_ELEMENT_TYPE type, CGCnvElement *main_obj,CGCnvElement *base_obj, const long chart_id, const int subwindow, const string descript, const int x, const int y, const int w, const int h); public: //--- (1) Set and (2) return the hint color void SetHintColor(const color clr) { this.SetForeColor(clr,false); } color HintColor(void) const { return this.ForeColor(); } //--- Constructor CHintBase(CGCnvElement *main_obj,CGCnvElement *base_obj, const long chart_id, const int subwindow, const string descript, const int x, const int y, const int w, const int h); //--- Display the element virtual void Show(void); //--- Redraw the object virtual void Redraw(bool redraw); //--- Clear the element filling it with color and opacity virtual void Erase(const color colour,const uchar opacity,const bool redraw=false); //--- Clear the element with a gradient fill virtual void Erase(color &colors[],const uchar opacity,const bool vgradient,const bool cycle,const bool redraw=false); //--- Draw the hint frame virtual void DrawFrame(void); }; //+------------------------------------------------------------------+
ヒントの色を設定するメソッドと返すメソッドは、実際にはオブジェクトのテキストの色を設定し返します(ForeColor())。
宣言されたメソッドについて詳しく考えてみましょう。
以下は、Protectedコンストラクタです。
//+------------------------------------------------------------------+ //| Protected constructor with an object type, | //| chart ID and subwindow | //+------------------------------------------------------------------+ CHintBase::CHintBase(const ENUM_GRAPH_ELEMENT_TYPE type, CGCnvElement *main_obj,CGCnvElement *base_obj, const long chart_id, const int subwindow, const string descript, const int x, const int y, const int w, const int h) : CWinFormBase(type,main_obj,base_obj,chart_id,subwindow,descript,x,y,w,h) { //--- Set the specified graphical element type for the object and assign the library object type to the current object this.SetTypeElement(type); this.m_type=OBJECT_DE_TYPE_GWF_HELPER; this.SetPaddingAll(0); this.SetMarginAll(0); this.SetBorderSizeAll(0); } //+------------------------------------------------------------------+
作成されたオブジェクトの型、メインオブジェクトと基本オブジェクトへのポインタ、および作成されたオブジェクトの残りの基本パラメータは、このようなすべてのコンストラクタの標準としてコンストラクタに渡されています。初期化文字列では、親クラスのコンストラクタは、メソッドに渡されたオブジェクトの型、メインオブジェクトと基本オブジェクトへのポインタ、コンストラクタの仮パラメータで渡された他のプロパティを取得します。クラス本体では、メソッドに渡される要素の型、補助オブジェクトとしてライブラリのグラフィカルオブジェクトの型を設定し、さらにPadding、Margin、BorderSizeの値を0に設定します。
以下は、パラメトリックコンストラクタです。
//+------------------------------------------------------------------+ //| Constructor indicating the main and base objects, | //| chart ID and subwindow | //+------------------------------------------------------------------+ CHintBase::CHintBase(CGCnvElement *main_obj,CGCnvElement *base_obj, const long chart_id, const int subwindow, const string descript, const int x, const int y, const int w, const int h) : CWinFormBase(GRAPH_ELEMENT_TYPE_WF_HINT_BASE,main_obj,base_obj,chart_id,subwindow,descript,x,y,w,h) { this.SetTypeElement(GRAPH_ELEMENT_TYPE_WF_HINT_BASE); this.m_type=OBJECT_DE_TYPE_GWF_HELPER; this.SetPaddingAll(0); this.SetMarginAll(0); this.SetBorderSizeAll(0); } //+------------------------------------------------------------------+
ここでは、ライブラリのすべてのWinFormsオブジェクトに対して現在おこなっているのと同様に、メインオブジェクトと基本オブジェクトへのポインタと、オブジェクトを作成するための他の標準的なパラメータをコンストラクタに渡します。初期化文字列の中で、作成されるオブジェクトの型を基本ヒントオブジェクトとして親オブジェクトに渡します。メインオブジェクトと基本オブジェクトへのポインタと、コンストラクタに渡された他のパラメータを渡します。コンストラクタの本体で、作成されたオブジェクトの型を基本ヒントオブジェクトとして指定し、補助オブジェクトとしてライブラリのグラフィカルオブジェクトの型を指定します。
以下は、オブジェクトを再描画するメソッドです。
//+------------------------------------------------------------------+ //| Redraw the object | //+------------------------------------------------------------------+ void CHintBase::Redraw(bool redraw) { //--- Fill the object with background color having transparency this.Erase(this.BackgroundColor(),this.Opacity(),true); } //+------------------------------------------------------------------+
ここでは、オブジェクトに背景色と不透明度を設定した上で、オブジェクトをクリアするメソッドを呼び出すだけです。
以下は、色と不透明度を指定して要素をクリアするメソッドです。
//+------------------------------------------------------------------+ //| Clear the element filling it with color and opacity | //+------------------------------------------------------------------+ void CHintBase::Erase(const color colour,const uchar opacity,const bool redraw=false) { //--- Fill the element having the specified color and the redrawing flag CGCnvElement::EraseNoCrop(colour,opacity,false); //--- If the object has a frame, draw it if(this.BorderStyle()!=FRAME_STYLE_NONE) this.DrawFrame(); //--- Draw a hint this.DrawHint(1); //--- Update the element having the specified redrawing flag this.Update(redraw); } //+------------------------------------------------------------------+
メソッドのロジックはコードのコメントで完全に説明されています。
以下は、グラデーションで塗りつぶされた要素をクリアするメソッドです。
//+------------------------------------------------------------------+ //| Clear the element with a gradient fill | //+------------------------------------------------------------------+ void CHintBase::Erase(color &colors[],const uchar opacity,const bool vgradient,const bool cycle,const bool redraw=false) { //--- Fill the element having the specified color array and the redrawing flag CGCnvElement::EraseNoCrop(colors,opacity,vgradient,cycle,false); //--- If the object has a frame, draw it if(this.BorderStyle()!=FRAME_STYLE_NONE) this.DrawFrame(); //--- Draw a hint this.DrawHint(1); //--- Update the element having the specified redrawing flag this.Update(redraw); } //+------------------------------------------------------------------+
このメソッドも前バージョンと同様に、背景を色の配列から選んで塗りつぶすとともに、枠とヒントを描画します。
以下は、要素の境界線を描画するメソッドです。
//+------------------------------------------------------------------+ //| Draw the element border | //+------------------------------------------------------------------+ void CHintBase::DrawFrame(void) { this.DrawRectangle(0,0,this.Width()-1,this.Height()-1,this.BorderColor(),this.Opacity()); } //+------------------------------------------------------------------+
ここでは、DrawRectangleメソッドを使って、オブジェクトの端に枠の色と不透明度を設定した枠を描いています。
以下は、要素を示すメソッドです。
//+------------------------------------------------------------------+ //| Show the element | //+------------------------------------------------------------------+ void CHintBase::Show(void) { //--- If the element should not be displayed (hidden inside another control), leave if(!this.Displayed()) return; //--- If the object has a shadow, display it if(this.m_shadow_obj!=NULL) this.m_shadow_obj.Show(); //--- Display the main form CGCnvElement::Show(); this.Redraw(false); //--- In the loop by all bound graphical objects, for(int i=0;i<this.m_list_elements.Total();i++) { //--- get the next graphical element CGCnvElement *element=this.m_list_elements.At(i); if(element==NULL || !element.Displayed()) continue; //--- and display it element.Show(); } } //+------------------------------------------------------------------+
メソッドのロジックはコードのコメントで完全に説明されています。
他のすべてのヒントオブジェクトは、このオブジェクトを基に作成されます。今日は、区切りを左右上下にシフトする可能性を示唆するオブジェクトを作成します。適切な矢印が表示されます。
左にシフトする可能性についてのヒントオブジェクトを作成してみましょう。
\MQL5\Include\DoEasy\Objects\Graph\WForms\Helpers\で、CHintMoveLeftクラスのHintMoveLeft.mqhファイルを新規に作成します。クラスはヒントオブジェクト基本クラスから派生する,ので、そのファイルを作成されたクラスファイルにインクルードする必要があります。
//+------------------------------------------------------------------+ //| HintMoveLeft.mqh | //| Copyright 2022, MetaQuotes Ltd. | //| https://mql5.com/ja/users/artmedia70 | //+------------------------------------------------------------------+ #property copyright "Copyright 2022, MetaQuotes Ltd." #property link "https://mql5.com/ja/users/artmedia70" #property version "1.00" #property strict // Necessary for mql4 //+------------------------------------------------------------------+ //| Include files | //+------------------------------------------------------------------+ #include "HintBase.mqh" //+------------------------------------------------------------------+ //| HintMoveLeft base object class of the WForms controls | //+------------------------------------------------------------------+ class CHintMoveLeft : public CHintBase { }
クラスのprotectedセクションで、親クラスのこのメソッドをオーバーライドするヒント描画用の仮想メソッドを宣言し、protectedクラスのコンストラクタを宣言します。publicセクションで、「カーソルがアクティブ領域内にあり、マウスボタンがクリックされていない」イベントのハンドラおよびパラメトリックコンストラクタを宣言します。
//+------------------------------------------------------------------+ //| HintMoveLeft object class of the WForms controls | //+------------------------------------------------------------------+ class CHintMoveLeft : public CHintBase { protected: //--- Draw a hint virtual void DrawHint(const int shift); //--- Protected constructor with object type, chart ID and subwindow CHintMoveLeft(const ENUM_GRAPH_ELEMENT_TYPE type, CGCnvElement *main_obj,CGCnvElement *base_obj, const long chart_id, const int subwindow, const string descript, const int x, const int y, const int w, const int h); public: //--- 'The cursor is inside the active area, the mouse buttons are not clicked' event handler virtual void MouseActiveAreaNotPressedHandler(const int id,const long& lparam,const double& dparam,const string& sparam); //--- Constructor CHintMoveLeft(CGCnvElement *main_obj,CGCnvElement *base_obj, const long chart_id, const int subwindow, const string descript, const int x, const int y, const int w, const int h); }; //+------------------------------------------------------------------+
宣言されたメソッドについて詳しく考えてみましょう。
以下は、Protectedコンストラクタです。
//+------------------------------------------------------------------+ //| Protected constructor with an object type, | //| chart ID and subwindow | //+------------------------------------------------------------------+ CHintMoveLeft::CHintMoveLeft(const ENUM_GRAPH_ELEMENT_TYPE type, CGCnvElement *main_obj,CGCnvElement *base_obj, const long chart_id, const int subwindow, const string descript, const int x, const int y, const int w, const int h) : CHintBase(type,main_obj,base_obj,chart_id,subwindow,descript,x,y,w,h) { //--- Set the specified graphical element type for the object and assign the library object type to the current object this.SetTypeElement(type); } //+------------------------------------------------------------------+
作成されたオブジェクトの型、メインオブジェクトと基本オブジェクトへのポインタ、その他新しいグラフィック要素の作成に必要なパラメータがコンストラクタに渡されます。初期化文字列では、作成されたオブジェクトの型と、コンストラクタ変数で指定された他のすべてのプロパティが、親クラスのコンストラクタに渡されます。クラス本体で、コンストラクタの正式なパラメータに渡される生成されるオブジェクトの型を指定します。
以下は、パラメトリックコンストラクタです。
//+------------------------------------------------------------------+ //| Constructor indicating the main and base objects, | //| chart ID and subwindow | //+------------------------------------------------------------------+ CHintMoveLeft::CHintMoveLeft(CGCnvElement *main_obj,CGCnvElement *base_obj, const long chart_id, const int subwindow, const string descript, const int x, const int y, const int w, const int h) : CHintBase(GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_LEFT,main_obj,base_obj,chart_id,subwindow,descript,x,y,w,h) { this.SetTypeElement(GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_LEFT); } //+------------------------------------------------------------------+
作成されるオブジェクトの型は仮パラメータとして渡されず、代わりにコードにハードコードされていることを除いて、ここでのすべてはprotectedコンストラクタとまったく同じです。
以下は、ヒントを描くメソッドです。
//+------------------------------------------------------------------+ //| Draw a hint | //+------------------------------------------------------------------+ void CHintMoveLeft::DrawHint(const int shift) { int w=this.Width(); int h=this.Height(); int middle=int(h*0.5); this.DrawRectangleFill(w-2,0,w-1,h-1,this.HintColor(),255); this.DrawTriangleFill(shift,middle,shift+3,middle-3,shift+3,middle+3,this.HintColor(),255); this.DrawLine(shift+3,middle,w-3,middle,this.HintColor(),255); } //+------------------------------------------------------------------+
ここでは、オブジェクト全体の幅と高さを取得し、矢印を構築するための値をカウントする中心線を計算します。オブジェクトの右側に幅2ピクセルの塗りつぶされた縦長の長方形を描きます。メソッドに渡されたshift値と中心線から数えた角度を持つ三角形を描きます。最後に、メソッドに渡されたshift値によってインデントされた左端から、オブジェクトの右側にある描画済みの縦長長方形まで、水平方向の中心線を描画します。こうして、オブジェクトの右側にベースがある左矢印ができます。この場合、矢印の長さはメソッドに渡されたshift値に依存します。この変数の値が高いほど、矢印は短くなります。
SplitContainerコントロールの区切り領域にマウスカーソルを合わせると、点線の四角形が表示されます。また、区切りの配置(縦か横か)によって、ここに左右または上下の矢印が2つ表示されるはずです。マウスカーソルが区切り領域から離れると、点線の長方形と矢印が消えるはずです。これは、SplitContainerオブジェクトのパネルの「カーソルがアクティブ領域内にある」イベントのハンドラで発生します。ただし、カーソルが補助オブジェクトの領域に入った場合、パネルハンドラは呼び出されません。そこで、補助オブジェクトにもそのようなハンドラを作る必要があります。このような仮想ハンドラはすべてフォームオブジェクトクラスで宣言され、派生クラスでオーバーライドされるべきものです。ここで再定義しましょう。
以下は、「カーソルがアクティブ領域内にあり、マウスボタンがクリックされていない」イベントのハンドラです。
//+------------------------------------------------------------------+ //| 'The cursor is inside the active area, | //| no mouse buttons are clicked' event handler | //+------------------------------------------------------------------+ void CHintMoveLeft::MouseActiveAreaNotPressedHandler(const int id,const long& lparam,const double& dparam,const string& sparam) { //--- Set the flag for not displaying the object and hide it this.SetDisplayed(false); this.Hide(); //--- Get the pointer to the base object CWinFormBase *base=this.GetBase(); if(base==NULL) return; //--- From the base object, get the pointers to the "right, up and down shift" hint objects, //--- set the object non-display flag for them and hide the controls CWinFormBase *hint_mr=base.GetElementByType(GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_RIGHT,0); if(hint_mr!=NULL) { hint_mr.SetDisplayed(false); hint_mr.Hide(); } CWinFormBase *hint_mu=base.GetElementByType(GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_UP,0); if(hint_mu!=NULL) { hint_mu.SetDisplayed(false); hint_mu.Hide(); } CWinFormBase *hint_md=base.GetElementByType(GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_DOWN,0); if(hint_md!=NULL) { hint_md.SetDisplayed(false); hint_md.Hide(); } } //+------------------------------------------------------------------+
メソッドロジック全体は、コードへのコメントで説明されています。つまり、カーソルがオブジェクトのアクティブ領域に入ると、すぐにこのハンドラが起動します。まず、このオブジェクトをその中で非表示にします。そして、ヒントオブジェクトが接続している基本オブジェクトへのポインタを取得します。このメソッドでは、右、上、下の矢印でヒントを得ることができます。左の矢印はこのオブジェクトではすでに非表示になっています。要求されたオブジェクトへのポインタが有効であれば、これらのオブジェクトも非表示にします。したがって、カーソルがこのオブジェクトの領域に入ると、自分自身と他のヒントオブジェクトを非表示にすることができます。
右にシフトする可能性を示唆するオブジェクトを作ってみましょう。
\MQL5\Include\DoEasy\Objects\Graph\WForms\Helpers\で、CHintMoveRightクラスのHintMoveRight.mqhファイルを新規に作成します。クラスはヒントオブジェクト基本クラスから派生する,ので、そのファイルを作成されたクラスファイルにインクルードする必要があります。
//+------------------------------------------------------------------+ //| HintMoveRight.mqh | //| Copyright 2022, MetaQuotes Ltd. | //| https://mql5.com/ja/users/artmedia70 | //+------------------------------------------------------------------+ #property copyright "Copyright 2022, MetaQuotes Ltd." #property link "https://mql5.com/ja/users/artmedia70" #property version "1.00" #property strict // Necessary for mql4 //+------------------------------------------------------------------+ //| Include files | //+------------------------------------------------------------------+ #include "HintBase.mqh" //+------------------------------------------------------------------+ //| Class of the base HintMoveRight object of the WForms controls | //+------------------------------------------------------------------+ class CHintMoveRight : public CHintBase { }
ヒントを描画するメソッドとマウスイベントのハンドラ以外は、上のクラスと同じです。
//+------------------------------------------------------------------+ //| Class of the HintMoveRight object of the WForms controls | //+------------------------------------------------------------------+ class CHintMoveRight : public CHintBase { protected: //--- Draw a hint virtual void DrawHint(const int shift); //--- Protected constructor with object type, chart ID and subwindow CHintMoveRight(const ENUM_GRAPH_ELEMENT_TYPE type, CGCnvElement *main_obj,CGCnvElement *base_obj, const long chart_id, const int subwindow, const string descript, const int x, const int y, const int w, const int h); public: //--- 'The cursor is inside the active area, the mouse buttons are not clicked' event handler virtual void MouseActiveAreaNotPressedHandler(const int id,const long& lparam,const double& dparam,const string& sparam); //--- Constructor CHintMoveRight(CGCnvElement *main_obj,CGCnvElement *base_obj, const long chart_id, const int subwindow, const string descript, const int x, const int y, const int w, const int h); }; //+------------------------------------------------------------------+ //| Protected constructor with an object type, | //| chart ID and subwindow | //+------------------------------------------------------------------+ CHintMoveRight::CHintMoveRight(const ENUM_GRAPH_ELEMENT_TYPE type, CGCnvElement *main_obj,CGCnvElement *base_obj, const long chart_id, const int subwindow, const string descript, const int x, const int y, const int w, const int h) : CHintBase(type,main_obj,base_obj,chart_id,subwindow,descript,x,y,w,h) { //--- Set the specified graphical element type for the object and assign the library object type to the current object this.SetTypeElement(type); } //+------------------------------------------------------------------+ //| Constructor indicating the main and base objects, | //| chart ID and subwindow | //+------------------------------------------------------------------+ CHintMoveRight::CHintMoveRight(CGCnvElement *main_obj,CGCnvElement *base_obj, const long chart_id, const int subwindow, const string descript, const int x, const int y, const int w, const int h) : CHintBase(GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_RIGHT,main_obj,base_obj,chart_id,subwindow,descript,x,y,w,h) { this.SetTypeElement(GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_RIGHT); } //+------------------------------------------------------------------+
以下は、ヒントを描くメソッドです。
//+------------------------------------------------------------------+ //| Draw a hint | //+------------------------------------------------------------------+ void CHintMoveRight::DrawHint(const int shift) { int w=this.Width(); int h=this.Height(); int middle=int(h*0.5); this.DrawRectangleFill(0,0,1,h-1,this.HintColor(),255); this.DrawTriangleFill(shift+8,middle,shift+8-3,middle+3,shift+8-3,middle-3,this.HintColor(),255); this.DrawLine(2,middle,shift+4,middle,this.HintColor(),255); } //+------------------------------------------------------------------+
ここでは、オブジェクトの左側に縦の長方形を描き、shift値に8(線の長さ)を加えた値から矢印の三角形を描き、さらに、描いた基本の長方形から始まり、描いた三角形で終わる水平の中心線を描いています。shiftを大きくすると、矢印の長さが長くなります。
以下は、「カーソルがアクティブ領域内にあり、マウスボタンがクリックされていない」イベントのハンドラです。
//+------------------------------------------------------------------+ //| 'The cursor is inside the active area, | //| no mouse buttons are clicked' event handler | //+------------------------------------------------------------------+ void CHintMoveRight::MouseActiveAreaNotPressedHandler(const int id,const long& lparam,const double& dparam,const string& sparam) { //--- Set the flag for not displaying the object and hide it this.SetDisplayed(false); this.Hide(); //--- Get the pointer to the base object CWinFormBase *base=this.GetBase(); if(base==NULL) return; //--- From the base object, get the pointers to the "left, up and down shift" hint objects, //--- set the object non-display flag for them and hide the controls CWinFormBase *hint_ml=base.GetElementByType(GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_LEFT,0); if(hint_ml!=NULL) { hint_ml.SetDisplayed(false); hint_ml.Hide(); } CWinFormBase *hint_mu=base.GetElementByType(GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_UP,0); if(hint_mu!=NULL) { hint_mu.SetDisplayed(false); hint_mu.Hide(); } CWinFormBase *hint_md=base.GetElementByType(GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_DOWN,0); if(hint_md!=NULL) { hint_md.SetDisplayed(false); hint_md.Hide(); } } //+------------------------------------------------------------------+
このメソッドのロジックは、前のクラスのこのメソッドのロジックと同じです。唯一の違いは、ここではこのオブジェクトが右矢印であることです。このメソッドでは、このオブジェクトを非表示にした後、左、上、下矢印を持つヒントオブジェクトへのポインタを取得します。
上下の矢印を持つヒントオブジェクトの他の2つのクラスは、そのまま考えます。それらのロジックは、上で説明した2つのクラスと同じで、繰り返すことに意味はありません。
以下は、上にシフトする可能性を示す補助オブジェクトのクラスです。
//+------------------------------------------------------------------+ //| HintMoveUp.mqh | //| Copyright 2022, MetaQuotes Ltd. | //| https://mql5.com/ja/users/artmedia70 | //+------------------------------------------------------------------+ #property copyright "Copyright 2022, MetaQuotes Ltd." #property link "https://mql5.com/ja/users/artmedia70" #property version "1.00" #property strict // Necessary for mql4 //+------------------------------------------------------------------+ //| Include files | //+------------------------------------------------------------------+ #include "HintBase.mqh" //+------------------------------------------------------------------+ //| HintMoveUp object class of WForms controls | //+------------------------------------------------------------------+ class CHintMoveUp : public CHintBase { protected: //--- Draw a hint virtual void DrawHint(const int shift); //--- Protected constructor with object type, chart ID and subwindow CHintMoveUp(const ENUM_GRAPH_ELEMENT_TYPE type, CGCnvElement *main_obj,CGCnvElement *base_obj, const long chart_id, const int subwindow, const string descript, const int x, const int y, const int w, const int h); public: //--- 'The cursor is inside the active area, the mouse buttons are not clicked' event handler virtual void MouseActiveAreaNotPressedHandler(const int id,const long& lparam,const double& dparam,const string& sparam); //--- Constructor CHintMoveUp(CGCnvElement *main_obj,CGCnvElement *base_obj, const long chart_id, const int subwindow, const string descript, const int x, const int y, const int w, const int h); }; //+------------------------------------------------------------------+ //| Protected constructor with an object type, | //| chart ID and subwindow | //+------------------------------------------------------------------+ CHintMoveUp::CHintMoveUp(const ENUM_GRAPH_ELEMENT_TYPE type, CGCnvElement *main_obj,CGCnvElement *base_obj, const long chart_id, const int subwindow, const string descript, const int x, const int y, const int w, const int h) : CHintBase(type,main_obj,base_obj,chart_id,subwindow,descript,x,y,w,h) { //--- Set the specified graphical element type for the object and assign the library object type to the current object this.SetTypeElement(type); } //+------------------------------------------------------------------+ //| Constructor indicating the main and base objects, | //| chart ID and subwindow | //+------------------------------------------------------------------+ CHintMoveUp::CHintMoveUp(CGCnvElement *main_obj,CGCnvElement *base_obj, const long chart_id, const int subwindow, const string descript, const int x, const int y, const int w, const int h) : CHintBase(GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_UP,main_obj,base_obj,chart_id,subwindow,descript,x,y,w,h) { this.SetTypeElement(GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_UP); } //+------------------------------------------------------------------+ //| Draw a hint | //+------------------------------------------------------------------+ void CHintMoveUp::DrawHint(const int shift) { int w=this.Width(); int h=this.Height(); int middle=int(w*0.5); this.DrawRectangleFill(0,h-2,w-1,h-1,this.HintColor(),255); this.DrawTriangleFill(middle,shift,middle+3,shift+3,middle-3,shift+3,this.HintColor(),255); this.DrawLine(middle,shift+4,middle,h-3,this.HintColor(),255); } //+------------------------------------------------------------------+ //| 'The cursor is inside the active area, | //| no mouse buttons are clicked' event handler | //+------------------------------------------------------------------+ void CHintMoveUp::MouseActiveAreaNotPressedHandler(const int id,const long& lparam,const double& dparam,const string& sparam) { //--- Set the flag for not displaying the object and hide it this.SetDisplayed(false); this.Hide(); //--- Get the pointer to the base object CWinFormBase *base=this.GetBase(); if(base==NULL) return; //--- From the base object, get the pointers to the "down, left and right shift" hint objects, //--- set the object non-display flag for them and hide the controls CWinFormBase *hint_md=base.GetElementByType(GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_DOWN,0); if(hint_md!=NULL) { hint_md.SetDisplayed(false); hint_md.Hide(); } CWinFormBase *hint_ml=base.GetElementByType(GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_LEFT,0); if(hint_ml!=NULL) { hint_ml.SetDisplayed(false); hint_ml.Hide(); } CWinFormBase *hint_mr=base.GetElementByType(GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_RIGHT,0); if(hint_mr!=NULL) { hint_mr.SetDisplayed(false); hint_mr.Hide(); } } //+------------------------------------------------------------------+
以下は、下にシフトする可能性を示す補助オブジェクトのクラスです。
//+------------------------------------------------------------------+ //| HintMoveDown.mqh | //| Copyright 2022, MetaQuotes Ltd. | //| https://mql5.com/ja/users/artmedia70 | //+------------------------------------------------------------------+ #property copyright "Copyright 2022, MetaQuotes Ltd." #property link "https://mql5.com/ja/users/artmedia70" #property version "1.00" #property strict // Necessary for mql4 //+------------------------------------------------------------------+ //| Include files | //+------------------------------------------------------------------+ #include "HintBase.mqh" //+------------------------------------------------------------------+ //| HintMoveDown object class of WForms controls | //+------------------------------------------------------------------+ class CHintMoveDown : public CHintBase { protected: //--- Draw a hint virtual void DrawHint(const int shift); //--- Protected constructor with object type, chart ID and subwindow CHintMoveDown(const ENUM_GRAPH_ELEMENT_TYPE type, CGCnvElement *main_obj,CGCnvElement *base_obj, const long chart_id, const int subwindow, const string descript, const int x, const int y, const int w, const int h); public: //--- 'The cursor is inside the active area, the mouse buttons are not clicked' event handler virtual void MouseActiveAreaNotPressedHandler(const int id,const long& lparam,const double& dparam,const string& sparam); //--- Constructor CHintMoveDown(CGCnvElement *main_obj,CGCnvElement *base_obj, const long chart_id, const int subwindow, const string descript, const int x, const int y, const int w, const int h); }; //+------------------------------------------------------------------+ //| Protected constructor with an object type, | //| chart ID and subwindow | //+------------------------------------------------------------------+ CHintMoveDown::CHintMoveDown(const ENUM_GRAPH_ELEMENT_TYPE type, CGCnvElement *main_obj,CGCnvElement *base_obj, const long chart_id, const int subwindow, const string descript, const int x, const int y, const int w, const int h) : CHintBase(type,main_obj,base_obj,chart_id,subwindow,descript,x,y,w,h) { //--- Set the specified graphical element type for the object and assign the library object type to the current object this.SetTypeElement(type); } //+------------------------------------------------------------------+ //| Constructor indicating the main and base objects, | //| chart ID and subwindow | //+------------------------------------------------------------------+ CHintMoveDown::CHintMoveDown(CGCnvElement *main_obj,CGCnvElement *base_obj, const long chart_id, const int subwindow, const string descript, const int x, const int y, const int w, const int h) : CHintBase(GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_DOWN,main_obj,base_obj,chart_id,subwindow,descript,x,y,w,h) { this.SetTypeElement(GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_DOWN); } //+------------------------------------------------------------------+ //| Draw a hint | //+------------------------------------------------------------------+ void CHintMoveDown::DrawHint(const int shift) { int w=this.Width(); int h=this.Height(); int middle=int(w*0.5); this.DrawRectangleFill(0,0,w-1,1,this.HintColor(),255); this.DrawTriangleFill(middle,shift+8,middle-3,shift+8-3,middle+3,shift+8-3,this.HintColor(),255); this.DrawLine(middle,2,middle,shift+8-4,this.HintColor(),255); } //+------------------------------------------------------------------+ //| 'The cursor is inside the active area, | //| no mouse buttons are clicked' event handler | //+------------------------------------------------------------------+ void CHintMoveDown::MouseActiveAreaNotPressedHandler(const int id,const long& lparam,const double& dparam,const string& sparam) { //--- Set the flag for not displaying the object and hide it this.SetDisplayed(false); this.Hide(); //--- Get the pointer to the base object CWinFormBase *base=this.GetBase(); if(base==NULL) return; //--- From the base object, get the pointers to the "up, left and right shift" hint objects, //--- set the object non-display flag for them and hide the controls CWinFormBase *hint_mu=base.GetElementByType(GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_UP,0); if(hint_mu!=NULL) { hint_mu.SetDisplayed(false); hint_mu.Hide(); } CWinFormBase *hint_ml=base.GetElementByType(GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_LEFT,0); if(hint_ml!=NULL) { hint_ml.SetDisplayed(false); hint_ml.Hide(); } CWinFormBase *hint_mr=base.GetElementByType(GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_RIGHT,0); if(hint_mr!=NULL) { hint_mr.SetDisplayed(false); hint_mr.Hide(); } } //+------------------------------------------------------------------+
左矢印のオブジェクトは右矢印のオブジェクトと一緒に、上矢印のオブジェクトは下矢印のオブジェクトと一緒に、というようにです。ツールチップの描画メソッドでオフセットを指定すると、あるオブジェクトでは矢印が減少し、逆のオブジェクトでは増加するのはこのためです。このように、ペアで動作する2つのオブジェクトの矢印を相互に変位させたアニメーションツールチップを簡単に作成することができます。左のオブジェクトの矢印が減少すれば、右のオブジェクトの矢印は増加します。ヒント描画メソッドに同じシフト量を渡してループさせることで、左右の矢印を一定にシフトさせ、ヒントをアニメーション化させることができます。
すべてのコンテナオブジェクトクラスにおいて、これらの新しいオブジェクトを作成する機能を追加する必要があります。
基本コンテナオブジェクトクラスのファイル(\MQL5\Include\DoEasy\Objects\Graph\WForms\Containers\Container.mqh)にある、追加されたオブジェクトのパラメータを設定するメソッドで、メインオブジェクトと基本オブジェクトを設定する文字列を削除します。
//+------------------------------------------------------------------+ //| Set parameters for the attached object | //+------------------------------------------------------------------+ void CContainer::SetObjParams(CWinFormBase *obj,const color colour) { obj.SetMain(this.IsMain() ? this.GetObject() : this.GetMain()); obj.SetBase(this.GetObject()); //--- Set the text color of the object to be the same as that of the base container obj.SetForeColor(this.ForeColor(),true);
最後に、新規作成するオブジェクトの最小限のパラメータを設定するようにします。
//---... //---... //--- For the "ArrowButton" WinForms object case GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON : case GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_UP : case GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_DOWN : case GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_LEFT : case GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_RIGHT : obj.SetBorderColor(CLR_DEF_CONTROL_TAB_HEAD_BORDER_COLOR,true); obj.SetBorderStyle(FRAME_STYLE_SIMPLE); break; //--- For "Hint" WinForms object case GRAPH_ELEMENT_TYPE_WF_HINT_BASE : obj.SetBackgroundColor(CLR_CANV_NULL,true); obj.SetBorderColor(CLR_CANV_NULL,true); obj.SetForeColor(CLR_CANV_NULL,true); obj.SetOpacity(0,false); obj.SetBorderStyle(FRAME_STYLE_NONE); break; //--- For "HintMoveLeft", "HintMoveRight", "HintMoveUp" and "HintMoveDown" WinForms object case GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_LEFT : case GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_RIGHT : case GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_UP : case GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_DOWN : obj.SetBackgroundColor(CLR_CANV_NULL,true); obj.SetBorderColor(CLR_CANV_NULL,true); obj.SetForeColor(CLR_DEF_CONTROL_HINT_FORE_COLOR,true); obj.SetOpacity(0,false); obj.SetBorderStyle(FRAME_STYLE_NONE); break; default: break; } obj.Crop(); } //+------------------------------------------------------------------+
パネルオブジェクトクラスのファイル(\MQL5\Include\DoEasy\Objects\Graph\WForms\Containers\Panel.mqh)にあるincludedfilesのリストに、新規作成されたクラスのファイルを追加します。
//+------------------------------------------------------------------+ //| Panel.mqh | //| Copyright 2022, MetaQuotes Ltd. | //| https://mql5.com/ja/users/artmedia70 | //+------------------------------------------------------------------+ #property copyright "Copyright 2022, MetaQuotes Ltd." #property link "https://mql5.com/ja/users/artmedia70" #property version "1.00" #property strict // Necessary for mql4 //+------------------------------------------------------------------+ //| Include files | //+------------------------------------------------------------------+ #include "Container.mqh" #include "..\Helpers\TabField.mqh" #include "..\Helpers\ArrowUpButton.mqh" #include "..\Helpers\ArrowDownButton.mqh" #include "..\Helpers\ArrowLeftButton.mqh" #include "..\Helpers\ArrowRightButton.mqh" #include "..\Helpers\ArrowUpDownBox.mqh" #include "..\Helpers\ArrowLeftRightBox.mqh" #include "..\Helpers\HintMoveLeft.mqh" #include "..\Helpers\HintMoveRight.mqh" #include "..\Helpers\HintMoveUp.mqh" #include "..\Helpers\HintMoveDown.mqh" #include "GroupBox.mqh" #include "TabControl.mqh" #include "SplitContainer.mqh" #include "..\..\WForms\Common Controls\ListBox.mqh" #include "..\..\WForms\Common Controls\CheckedListBox.mqh" #include "..\..\WForms\Common Controls\ButtonListBox.mqh" //+------------------------------------------------------------------+
これで、これらのオブジェクトは、すべてのライブラリコンテナオブジェクトで作成できるようになります。
WinFormsオブジェクトのすべてのクラスのコンストラクタは、すでにメインオブジェクトと基本オブジェクトを指定するように変更されており、ここではこれらの変更をさらに検討しないことに留意してください。
グラフィカルオブジェクトを新規作成するメソッドに、新しいライブラリオブジェクトを実装するために上記で作成した文字列を追加します。
//+------------------------------------------------------------------+ //| Create a new graphical object | //+------------------------------------------------------------------+ CGCnvElement *CPanel::CreateNewGObject(const ENUM_GRAPH_ELEMENT_TYPE type, const int obj_num, const string descript, const int x, const int y, const int w, const int h, const color colour, const uchar opacity, const bool movable, const bool activity) { CGCnvElement *element=NULL; switch(type) { case GRAPH_ELEMENT_TYPE_ELEMENT : element=new CGCnvElement(type,this.GetMain(),this.GetObject(),this.ID(),obj_num,this.ChartID(),this.SubWindow(),descript,x,y,w,h,colour,opacity,movable,activity); break; case GRAPH_ELEMENT_TYPE_FORM : element=new CForm(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h); break; case GRAPH_ELEMENT_TYPE_WF_CONTAINER : element=new CContainer(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h); break; case GRAPH_ELEMENT_TYPE_WF_GROUPBOX : element=new CGroupBox(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h); break; case GRAPH_ELEMENT_TYPE_WF_PANEL : element=new CPanel(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h); break; case GRAPH_ELEMENT_TYPE_WF_LABEL : element=new CLabel(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h); break; case GRAPH_ELEMENT_TYPE_WF_CHECKBOX : element=new CCheckBox(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h); break; case GRAPH_ELEMENT_TYPE_WF_RADIOBUTTON : element=new CRadioButton(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h); break; case GRAPH_ELEMENT_TYPE_WF_BUTTON : element=new CButton(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h); break; case GRAPH_ELEMENT_TYPE_WF_LIST_BOX : element=new CListBox(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h); break; case GRAPH_ELEMENT_TYPE_WF_LIST_BOX_ITEM : element=new CListBoxItem(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h); break; case GRAPH_ELEMENT_TYPE_WF_CHECKED_LIST_BOX : element=new CCheckedListBox(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h); break; case GRAPH_ELEMENT_TYPE_WF_BUTTON_LIST_BOX : element=new CButtonListBox(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h); break; case GRAPH_ELEMENT_TYPE_WF_TAB_HEADER : element=new CTabHeader(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h); break; case GRAPH_ELEMENT_TYPE_WF_TAB_FIELD : element=new CTabField(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h); break; case GRAPH_ELEMENT_TYPE_WF_TAB_CONTROL : element=new CTabControl(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h); break; case GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON : element=new CArrowButton(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h); break; case GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_UP : element=new CArrowUpButton(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h); break; case GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_DOWN : element=new CArrowDownButton(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h); break; case GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_LEFT : element=new CArrowLeftButton(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h); break; case GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_RIGHT : element=new CArrowRightButton(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h); break; case GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTONS_UD_BOX : element=new CArrowUpDownBox(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h); break; case GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTONS_LR_BOX : element=new CArrowLeftRightBox(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h); break; case GRAPH_ELEMENT_TYPE_WF_SPLIT_CONTAINER : element=new CSplitContainer(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h); break; case GRAPH_ELEMENT_TYPE_WF_SPLITTER : element=new CSplitter(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h); break; case GRAPH_ELEMENT_TYPE_WF_HINT_BASE : element=new CHintBase(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h); break; case GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_LEFT : element=new CHintMoveLeft(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h); break; case GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_RIGHT : element=new CHintMoveRight(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h); break; case GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_UP : element=new CHintMoveUp(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h); break; case GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_DOWN : element=new CHintMoveDown(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h); break; default : break; } if(element==NULL) ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_FAILED_CREATE_ELM_OBJ),this.TypeElementDescription(type)); return element; } //+------------------------------------------------------------------+
このメソッドに渡されたオブジェクトの種類によって、対応するクラスの新しいオブジェクトが作成され、そのコンストラクタがメインオブジェクトと基本オブジェクトへのポインタを受け取るようになります。
アンダーレイオブジェクトを作成するメソッドでは、作成されたオブジェクトのメインオブジェクトと基本オブジェクトを指定します。
//+------------------------------------------------------------------+ //| Create the underlay object | //+------------------------------------------------------------------+ bool CPanel::CreateUnderlayObj(void) { this.m_underlay=new CGCnvElement(GRAPH_ELEMENT_TYPE_WF_UNDERLAY,this.GetMain(),this.GetObject(),this.ID(),this.Number(), this.ChartID(),this.SubWindow(),this.NameObj()+"Underlay", this.CoordXWorkspace(),this.CoordYWorkspace(),this.WidthWorkspace(),this.HeightWorkspace(), CLR_CANV_NULL,0,false,false); if(m_underlay==NULL) { CMessage::ToLog(DFUN,MSG_PANEL_OBJECT_ERR_FAILED_CREATE_UNDERLAY_OBJ); return false; } if(!this.m_list_tmp.Add(this.m_underlay)) { CMessage::ToLog(DFUN,MSG_LIB_SYS_FAILED_OBJ_ADD_TO_LIST); delete this.m_underlay; return false; } this.SetUnderlayParams(); return true; } //+------------------------------------------------------------------+
GroupBoxコントロールクラスの\MQL5\Include\DoEasy\Objects\Graph\WForms\Containers\GroupBox.mqhファイルにある、グラフィカルオブジェクトを新規作成するメソッドで、上記のパネルオブジェクトのクラスと同じ改善を追加します。
//+------------------------------------------------------------------+ //| Create a new graphical object | //+------------------------------------------------------------------+ CGCnvElement *CGroupBox::CreateNewGObject(const ENUM_GRAPH_ELEMENT_TYPE type, const int obj_num, const string descript, const int x, const int y, const int w, const int h, const color colour, const uchar opacity, const bool movable, const bool activity) { CGCnvElement *element=NULL; switch(type) { case GRAPH_ELEMENT_TYPE_ELEMENT : element=new CGCnvElement(type,this.GetMain(),this.GetObject(),this.ID(),obj_num,this.ChartID(),this.SubWindow(),descript,x,y,w,h,colour,opacity,movable,activity); break; case GRAPH_ELEMENT_TYPE_FORM : element=new CForm(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h); break; case GRAPH_ELEMENT_TYPE_WF_CONTAINER : element=new CContainer(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h); break; case GRAPH_ELEMENT_TYPE_WF_GROUPBOX : element=new CGroupBox(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h); break; case GRAPH_ELEMENT_TYPE_WF_PANEL : element=new CPanel(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h); break; case GRAPH_ELEMENT_TYPE_WF_LABEL : element=new CLabel(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h); break; case GRAPH_ELEMENT_TYPE_WF_CHECKBOX : element=new CCheckBox(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h); break; case GRAPH_ELEMENT_TYPE_WF_RADIOBUTTON : element=new CRadioButton(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h); break; case GRAPH_ELEMENT_TYPE_WF_BUTTON : element=new CButton(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h); break; case GRAPH_ELEMENT_TYPE_WF_LIST_BOX : element=new CListBox(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h); break; case GRAPH_ELEMENT_TYPE_WF_LIST_BOX_ITEM : element=new CListBoxItem(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h); break; case GRAPH_ELEMENT_TYPE_WF_CHECKED_LIST_BOX : element=new CCheckedListBox(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h); break; case GRAPH_ELEMENT_TYPE_WF_BUTTON_LIST_BOX : element=new CButtonListBox(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h); break; case GRAPH_ELEMENT_TYPE_WF_TAB_HEADER : element=new CTabHeader(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h); break; case GRAPH_ELEMENT_TYPE_WF_TAB_FIELD : element=new CTabField(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h); break; case GRAPH_ELEMENT_TYPE_WF_TAB_CONTROL : element=new CTabControl(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h); break; case GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON : element=new CArrowButton(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h); break; case GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_UP : element=new CArrowUpButton(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h); break; case GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_DOWN : element=new CArrowDownButton(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h); break; case GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_LEFT : element=new CArrowLeftButton(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h); break; case GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_RIGHT : element=new CArrowRightButton(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h); break; case GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTONS_UD_BOX : element=new CArrowUpDownBox(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h); break; case GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTONS_LR_BOX : element=new CArrowLeftRightBox(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h); break; case GRAPH_ELEMENT_TYPE_WF_SPLIT_CONTAINER : element=new CSplitContainer(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h); break; case GRAPH_ELEMENT_TYPE_WF_SPLITTER : element=new CSplitter(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h); break; case GRAPH_ELEMENT_TYPE_WF_HINT_BASE : element=new CHintBase(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h); break; case GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_LEFT : element=new CHintMoveLeft(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h); break; case GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_RIGHT : element=new CHintMoveRight(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h); break; case GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_UP : element=new CHintMoveUp(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h); break; case GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_DOWN : element=new CHintMoveDown(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h); break; default : break; } if(element==NULL) ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_FAILED_CREATE_ELM_OBJ),this.TypeElementDescription(type)); return element; } //+------------------------------------------------------------------+
このメソッドと同じ改良が、TabControl.mqh、TabField.mqh、SplitContainerPanel.mqhファイル内の他のすべてのクラスのコンテナオブジェクトに施されています。これらのメソッドをさらに洗練させることについては、ここでは検討しません。
TabControlクラスのファイル(\MQL5\Include\DoEasy\Objects\Graph\WForms\Containers\TabControl.mqh)にあるCreateTabPages()メソッドで、指定された数のタブを作成し、タブヘッダーオブジェクトのメインオブジェクトと基本オブジェクトをインストールするすべての文字列を削除します。
header.SetMain(this.IsMain() ? this.GetObject() : this.GetMain());
header.SetBase(this.GetObject());
タブフィールドオブジェクト用
field.SetMain(this.IsMain() ? this.GetObject() : this.GetMain());
field.SetBase(this.GetObject());
右左上下ボタンオブジェクト用
//--- Create the left-right button object this.CreateNewElement(GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTONS_LR_BOX,this.Width()-32,0,15,15,clrNONE,255,this.Active(),false); //--- Get the pointer to a newly created object CArrowLeftRightBox *box_lr=this.GetArrLeftRightBox(); if(box_lr!=NULL) { this.SetVisibleLeftRightBox(false); this.SetSizeLeftRightBox(box_lr.Width()); box_lr.SetMain(this.IsMain() ? this.GetObject() : this.GetMain()); box_lr.SetBase(this.GetObject()); box_lr.SetID(this.GetMaxIDAll()); box_lr.SetBorderStyle(FRAME_STYLE_NONE); box_lr.SetBackgroundColor(CLR_CANV_NULL,true); box_lr.SetOpacity(0); box_lr.Hide(); CArrowLeftButton *lb=box_lr.GetArrowLeftButton(); if(lb!=NULL) { lb.SetMain(this.IsMain() ? this.GetObject() : this.GetMain()); lb.SetBase(box_lr); lb.SetID(this.GetMaxIDAll()); } CArrowRightButton *rb=box_lr.GetArrowRightButton(); if(rb!=NULL) { rb.SetMain(this.IsMain() ? this.GetObject() : this.GetMain()); rb.SetBase(box_lr); rb.SetID(this.GetMaxIDAll()); } } //--- Create the up-down button object this.CreateNewElement(GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTONS_UD_BOX,0,this.Height()-32,15,15,clrNONE,255,this.Active(),false); //--- Get the pointer to a newly created object CArrowUpDownBox *box_ud=this.GetArrUpDownBox(); if(box_ud!=NULL) { this.SetVisibleUpDownBox(false); this.SetSizeUpDownBox(box_ud.Height()); box_ud.SetMain(this.IsMain() ? this.GetObject() : this.GetMain()); box_ud.SetBase(this.GetObject()); box_ud.SetID(this.GetMaxIDAll()); box_ud.SetBorderStyle(FRAME_STYLE_NONE); box_ud.SetBackgroundColor(CLR_CANV_NULL,true); box_ud.SetOpacity(0); box_ud.Hide(); CArrowDownButton *db=box_ud.GetArrowDownButton(); if(db!=NULL) { db.SetMain(this.IsMain() ? this.GetObject() : this.GetMain()); db.SetBase(box_ud); db.SetID(this.GetMaxIDAll()); } CArrowUpButton *ub=box_ud.GetArrowUpButton(); if(ub!=NULL) { ub.SetMain(this.IsMain() ? this.GetObject() : this.GetMain()); ub.SetBase(box_ud); ub.SetID(this.GetMaxIDAll()); } } //--- Arrange all titles in accordance with the specified display modes and select the specified tab this.ArrangeTabHeaders(); this.Select(selected_page,true); return true; } //+------------------------------------------------------------------+
\MQL5\Include\DoEasy\Objects\Graph\WForms\Containers\SplitContainer.mqhでSplitContainerコントロールクラスを改良してみましょう。
クラスのpublicセクションに、ヒントオブジェクトへのポインタを返すメソッドを記述します。
//--- Return the pointer to the separator CSplitter *GetSplitter(void) { return this.GetElementByType(GRAPH_ELEMENT_TYPE_WF_SPLITTER,0); } //--- Return a pointer to the (1) "Left shift", (1) "Right shift", (1) "Up shift" and (1) "Down shift" hint objects CHintMoveLeft *GetHintMoveLeft(void) { return this.GetElementByType(GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_LEFT,0); } CHintMoveRight *GetHintMoveRight(void) { return this.GetElementByType(GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_RIGHT,0); } CHintMoveUp *GetHintMoveUp(void) { return this.GetElementByType(GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_UP,0); } CHintMoveDown *GetHintMoveDown(void) { return this.GetElementByType(GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_DOWN,0); } //--- (1) set and (2) return the minimum possible size of the panel 1 and 2
パネルを作成するメソッドで、作成されたパネルにメインオブジェクトと基本オブジェクトを設定し、区切りオブジェクトに設定するループを削除します。
return; for(int i=0;i<2;i++) { CSplitContainerPanel *panel=this.GetPanel(i); if(panel==NULL) continue; panel.SetMain(this.IsMain() ? this.GetObject() : this.GetMain()); panel.SetBase(this.GetObject()); } //--- if(!this.CreateNewElement(GRAPH_ELEMENT_TYPE_WF_SPLITTER,this.m_splitter_x,this.m_splitter_y,this.m_splitter_w,this.m_splitter_h,clrNONE,255,true,false)) return; CSplitter *splitter=this.GetSplitter(); if(splitter!=NULL) { splitter.SetMain(this.IsMain() ? this.GetObject() : this.GetMain()); splitter.SetBase(this.GetObject()); splitter.SetMovable(true); splitter.SetDisplayed(false); splitter.Hide(); }
また、4つのヒントオブジェクトを作成し、そのパラメータをメソッドに設定するようにします。
//+------------------------------------------------------------------+ //| Create the panels | //+------------------------------------------------------------------+ void CSplitContainer::CreatePanels(void) { this.m_list_elements.Clear(); if(this.SetsPanelParams()) { //--- Create two panels if(!this.CreateNewElement(GRAPH_ELEMENT_TYPE_WF_SPLIT_CONTAINER_PANEL,this.m_panel1_x,this.m_panel1_y,this.m_panel1_w,this.m_panel1_h,clrNONE,255,true,false)) return; if(!this.CreateNewElement(GRAPH_ELEMENT_TYPE_WF_SPLIT_CONTAINER_PANEL,this.m_panel2_x,this.m_panel2_y,this.m_panel2_w,this.m_panel2_h,clrNONE,255,true,false)) return; //--- Create a separator object if(!this.CreateNewElement(GRAPH_ELEMENT_TYPE_WF_SPLITTER,this.m_splitter_x,this.m_splitter_y,this.m_splitter_w,this.m_splitter_h,clrNONE,255,true,false)) return; CSplitter *splitter=this.GetSplitter(); if(splitter!=NULL) { splitter.SetProperty(CANV_ELEMENT_PROP_SPLIT_CONTAINER_SPLITTER_ORIENTATION,this.SplitterOrientation()); splitter.SetMovable(true); splitter.SetDisplayed(false); splitter.Hide(); } //--- Create the HintMoveLeft object if(!this.CreateNewElement(GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_LEFT,this.m_splitter_x-DEF_HINT_ICON_SIZE,this.m_splitter_y,DEF_HINT_ICON_SIZE,DEF_HINT_ICON_SIZE,clrNONE,255,true,false)) return; CHintMoveLeft *hint_ml=this.GetHintMoveLeft(); if(hint_ml!=NULL) { hint_ml.SetMovable(false); hint_ml.SetDisplayed(false); hint_ml.Hide(); } //--- Create the HintMoveRight object if(!this.CreateNewElement(GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_RIGHT,this.m_splitter_x+this.m_splitter_w,this.m_splitter_y,DEF_HINT_ICON_SIZE,DEF_HINT_ICON_SIZE,clrNONE,255,true,false)) return; CHintMoveRight *hint_mr=this.GetHintMoveRight(); if(hint_mr!=NULL) { hint_mr.SetMovable(false); hint_mr.SetDisplayed(false); hint_mr.Hide(); } //--- Create the HintMoveUp object if(!this.CreateNewElement(GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_UP,this.m_splitter_x,this.m_splitter_y-DEF_HINT_ICON_SIZE,DEF_HINT_ICON_SIZE,DEF_HINT_ICON_SIZE,clrNONE,255,true,false)) return; CHintMoveUp *hint_mu=this.GetHintMoveUp(); if(hint_mu!=NULL) { hint_mu.SetMovable(false); hint_mu.SetDisplayed(false); hint_mu.Hide(); } //--- Create the HintMoveDown object if(!this.CreateNewElement(GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_DOWN,this.m_splitter_x,this.m_splitter_y+this.m_splitter_h,DEF_HINT_ICON_SIZE,DEF_HINT_ICON_SIZE,clrNONE,255,true,false)) return; CHintMoveDown *hint_md=this.GetHintMoveDown(); if(hint_md!=NULL) { hint_md.SetMovable(false); hint_md.SetDisplayed(false); hint_md.Hide(); } } } //+------------------------------------------------------------------+
ヒントオブジェクトが作成されている場合、そのオブジェクトに対するマウス操作を無効化し、不可視フラグを設定し、作成された要素を非表示にします。
区切りの位置を設定するメソッドを作成したら、区切りオブジェクトのプロパティにメソッドに渡された値を追加して、区切りオブジェクトから直接オブジェクトの向き(垂直/水平)を検出できるようにします。
//+------------------------------------------------------------------+ //| set the separator location | //+------------------------------------------------------------------+ void CSplitContainer::SetSplitterOrientation(const ENUM_CANV_ELEMENT_SPLITTER_ORIENTATION value,const bool only_prop) { this.SetProperty(CANV_ELEMENT_PROP_SPLIT_CONTAINER_SPLITTER_ORIENTATION,value); //--- If there are no panels or separator, leave CSplitContainerPanel *p1=this.GetPanel1(); CSplitContainerPanel *p2=this.GetPanel2(); CSplitter *sp=this.GetSplitter(); if(p1==NULL || p2==NULL || sp==NULL) return; //--- Set the orientation property for the separator object sp.SetProperty(CANV_ELEMENT_PROP_SPLIT_CONTAINER_SPLITTER_ORIENTATION,value); //--- If only setting the property, leave if(only_prop) return; //--- Set the parameters of the panels and the separator this.SetsPanelParams(); //--- If panel 1 is resized successfully if(p1.Resize(this.m_panel1_w,this.m_panel1_h,true)) { //--- If panel 2 coordinates are changed to new ones if(p2.Move(this.CoordX()+this.m_panel2_x,this.CoordY()+this.m_panel2_y,true)) { //--- if panel 2 has been successfully resized, if(p2.Resize(this.m_panel2_w,this.m_panel2_h,true)) { //--- set new relative coordinates of panel 2 p2.SetCoordXRelative(p2.CoordX()-this.CoordX()); p2.SetCoordYRelative(p2.CoordY()-this.CoordY()); } } //--- If the size of the separator object has been successfully changed, //--- set new values of separator coordinates if(sp.Resize(this.m_splitter_w,this.m_splitter_h,false)) this.SetSplitterDistance(this.SplitterDistance(),true); } } //+------------------------------------------------------------------+
これで、このクラスの外部からデリミタへのポインタを受け取ったとき、常にその位置を知ることができ、将来的に便利なものになります。
区切りの移動を処理するイベントハンドラで、利用可能なすべての可視ヒントオブジェクトを非表示にします。
//+------------------------------------------------------------------+ //| Event handler | //+------------------------------------------------------------------+ void CSplitContainer::OnChartEvent(const int id,const long &lparam,const double &dparam,const string &sparam) { //--- Adjust subwindow Y shift CGCnvElement::OnChartEvent(id,lparam,dparam,sparam); //--- If the event ID is moving the separator if(id==WF_CONTROL_EVENT_MOVING) { //--- Get the pointer to the separator object CSplitter *splitter=this.GetSplitter(); if(splitter==NULL || this.SplitterFixed()) return; //--- Get the pointers to hint objects CHintMoveLeft *hint_ml=this.GetHintMoveLeft(); CHintMoveRight *hint_mr=this.GetHintMoveRight(); CHintMoveUp *hint_mu=this.GetHintMoveUp(); CHintMoveDown *hint_md=this.GetHintMoveDown(); if(hint_ml==NULL || hint_mr==NULL || hint_mu==NULL || hint_md==NULL) return; //--- Disable the display of hints and hide them hint_ml.SetDisplayed(false); hint_ml.Hide(); hint_mr.SetDisplayed(false); hint_mr.Hide(); hint_mu.SetDisplayed(false); hint_mu.Hide(); hint_md.SetDisplayed(false); hint_md.Hide(); //--- Declare the variables for separator coordinates int x=(int)lparam; int y=(int)dparam; //--- Depending on the separator direction, switch(this.SplitterOrientation()) { //--- vertical position case CANV_ELEMENT_SPLITTER_ORIENTATION_VERTICAL : //--- Set the Y coordinate equal to the Y coordinate of the control element y=this.CoordY(); //--- Adjust the X coordinate so that the separator does not go beyond the control element //--- taking into account the resulting minimum width of the panels if(x<this.CoordX()+this.Panel1MinSize()) x=this.CoordX()+this.Panel1MinSize(); if(x>this.CoordX()+this.Width()-this.Panel2MinSize()-this.SplitterWidth()) x=this.CoordX()+this.Width()-this.Panel2MinSize()-this.SplitterWidth(); break; //---CANV_ELEMENT_SPLITTER_ORIENTATION_HORISONTAL //--- horizontal position of the separator default: //--- Set the X coordinate equal to the X coordinate of the control element x=this.CoordX(); //--- Adjust the Y coordinate so that the separator does not go beyond the control element //--- taking into account the resulting minimum height of the panels if(y<this.CoordY()+this.Panel1MinSize()) y=this.CoordY()+this.Panel1MinSize(); if(y>this.CoordY()+this.Height()-this.Panel2MinSize()-this.SplitterWidth()) y=this.CoordY()+this.Height()-this.Panel2MinSize()-this.SplitterWidth(); break; } //--- Draw an empty rectangle this.DrawRectangleEmpty(); //--- If the separator is shifted by the calculated coordinates, if(splitter.Move(x,y,true)) { //--- set the separator relative coordinates splitter.SetCoordXRelative(splitter.CoordX()-this.CoordX()); splitter.SetCoordYRelative(splitter.CoordY()-this.CoordY()); //--- Depending on the direction of the separator, set its new coordinates this.SetSplitterDistance(!this.SplitterOrientation() ? splitter.CoordX()-this.CoordX() : splitter.CoordY()-this.CoordY(),false); } } } //+------------------------------------------------------------------+
このメソッドは、区切りオブジェクトの移動イベントを処理します。パネルのサイズと位置を調整する前に、すべての補助オブジェクトをここで非表示にします。そのため、区切り部分にカーソルを合わせると、区切りーの移動可能な方向に関するヒントが表示されます。マウスで区切りをキャプチャして動かし始めると、ヒントが非表示になるようになっています。
同様に、カーソルが区切り領域から外れた場合、ヒントを非表示にする必要があります。
「カーソルがアクティブ領域内にあり、マウスボタンがクリックされていない」イベントのハンドラで、点線の四角形を削除してヒントオブジェクトを非表示にするようにします。
//+------------------------------------------------------------------+ //| 'The cursor is inside the active area, | //| no mouse buttons are clicked' event handler | //+------------------------------------------------------------------+ void CSplitContainer::MouseActiveAreaNotPressedHandler(const int id,const long& lparam,const double& dparam,const string& sparam) { //--- If the separator is non-movable, leave if(this.SplitterFixed()) return; //--- Draw an empty rectangle in the control area this.DrawRectangleEmpty(); //--- Get the pointer to the separator CSplitter *splitter=this.GetSplitter(); if(splitter==NULL) { ::Print(DFUN,CMessage::Text(MSG_ELM_LIST_ERR_FAILED_GET_GRAPH_ELEMENT_OBJ),": ",this.TypeElementDescription(GRAPH_ELEMENT_TYPE_WF_SPLITTER)); return; } //--- If the separator is not displayed if(!splitter.Displayed()) { //--- Enable the display of the separator, show and redraw it splitter.SetDisplayed(true); splitter.Show(); splitter.Redraw(true); } //--- Get the pointer to hint objects CHintMoveLeft *hint_ml=this.GetHintMoveLeft(); CHintMoveRight *hint_mr=this.GetHintMoveRight(); CHintMoveUp *hint_mu=this.GetHintMoveUp(); CHintMoveDown *hint_md=this.GetHintMoveDown(); if(hint_ml==NULL || hint_mr==NULL || hint_mu==NULL || hint_md==NULL) return; hint_ml.SetDisplayed(false); hint_ml.Hide(); hint_mr.SetDisplayed(false); hint_mr.Hide(); hint_mu.SetDisplayed(false); hint_mu.Hide(); hint_md.SetDisplayed(false); hint_md.Hide(); } //+------------------------------------------------------------------+
カーソルが区切り領域(オブジェクトのコントロール領域にある)から外れると同時に、カーソルはオブジェクトのアクティブ領域に入ります。このイベントのハンドラは、まず区切り領域の輪郭を描く点線の矩形を削除し、次に表示されているヒントをすべて非表示にします。
カーソルが区切りのあるコントロール領域に入るとすぐに、区切りの向きによって左右または上下に移動する可能性があることを示すヒントが表示されるはずです。
「カーソルがコントロール領域内にあり、マウスボタンがクリックされていない」イベントのハンドラ内に、説明した機能を追加します。
//+------------------------------------------------------------------+ //| The cursor is inside the control area, | //| no mouse buttons are clicked' event handler | //+------------------------------------------------------------------+ void CSplitContainer::MouseControlAreaNotPressedHandler(const int id,const long &lparam,const double &dparam,const string &sparam) { //--- If the separator is non-movable, leave if(this.SplitterFixed()) return; //--- Draw an empty rectangle in the control area this.DrawRectangleEmpty(); //--- Draw a dotted rectangle in the control area this.DrawRectangleDotted(); //--- Get the pointer to the separator CSplitter *splitter=this.GetSplitter(); if(splitter==NULL) { ::Print(DFUN,CMessage::Text(MSG_ELM_LIST_ERR_FAILED_GET_GRAPH_ELEMENT_OBJ),": ",this.TypeElementDescription(GRAPH_ELEMENT_TYPE_WF_SPLITTER)); return; } //--- If the separator is not displayed if(!splitter.Displayed()) { //--- Enable the display of the separator and show it splitter.SetDisplayed(true); splitter.Erase(true); splitter.Show(); } //--- Get the pointers to hint objects CHintMoveLeft *hint_ml=this.GetHintMoveLeft(); CHintMoveRight *hint_mr=this.GetHintMoveRight(); CHintMoveUp *hint_mu=this.GetHintMoveUp(); CHintMoveDown *hint_md=this.GetHintMoveDown(); if(hint_ml==NULL || hint_mr==NULL || hint_mu==NULL || hint_md==NULL) return; //--- Get cursor coordinates int x=this.m_mouse.CoordX()-this.CoordX(); int y=this.m_mouse.CoordY()-this.CoordY(); //--- Depending on the separator direction, switch(this.SplitterOrientation()) { //--- vertical position case CANV_ELEMENT_SPLITTER_ORIENTATION_VERTICAL : //--- Set and adjust the coordinates of the "left shift" hint object x=this.CoordX()+this.m_splitter_x-hint_ml.Width();//-1; y+=this.CoordY()-hint_ml.Height()-1; if(y<this.CoordY()+this.m_splitter_y) y=this.CoordY()+this.m_splitter_y; //--- Shift the hint object to the calculated coordinates, set its visibility flag and display the object if(hint_ml.Move(x,y)) { hint_ml.SetCoordXRelative(x-this.CoordX()); hint_ml.SetCoordYRelative(y-this.CoordY()); hint_ml.SetDisplayed(true); hint_ml.Show(); } //--- Set and adjust the coordinates of the "right shift" hint object x=this.CoordX()+this.m_splitter_x+this.m_splitter_w;//+1; //--- Shift the hint object to the calculated coordinates, set its visibility flag and display the object if(hint_mr.Move(x,y)) { hint_mr.SetCoordXRelative(x-this.CoordX()); hint_mr.SetCoordYRelative(y-this.CoordY()); hint_mr.SetDisplayed(true); hint_mr.Show(); } break; //---CANV_ELEMENT_SPLITTER_ORIENTATION_HORISONTAL //--- horizontal position of the separator default: //--- Set and adjust the coordinates of the "up shift" hint object y=this.CoordY()+this.m_splitter_y-hint_mu.Height();//-1; x+=this.CoordX()-hint_mu.Width()-1; if(x<this.CoordX()+this.m_splitter_x) x=this.CoordX()+this.m_splitter_x; //--- Shift the hint object to the calculated coordinates, set its visibility flag and display the object if(hint_mu.Move(x,y)) { hint_mu.SetCoordXRelative(x-this.CoordX()); hint_mu.SetCoordYRelative(y-this.CoordY()); hint_mu.SetDisplayed(true); hint_mu.Show(); } //--- Set and adjust the coordinates of the "down shift" hint object y=this.CoordY()+this.m_splitter_y+this.m_splitter_h;//+1; //--- Shift the hint object to the calculated coordinates, set its visibility flag and display the object if(hint_md.Move(x,y)) { hint_md.SetCoordXRelative(x-this.CoordX()); hint_md.SetCoordYRelative(y-this.CoordY()); hint_md.SetDisplayed(true); hint_md.Show(); } break; } } //+------------------------------------------------------------------+
追加されたコードブロックのロジックは、メソッド一覧でコメントされています。ヒントオブジェクトへのポインタを取得し、カーソルの上に位置するように移動し、ヒントオブジェクトを表示します。
最後のマウスイベントのハンドラで、最後のイベントがフォームの外側またはアクティブ領域内にあった場合にヒントオブジェクトを非表示にします。
//+------------------------------------------------------------------+ //| Last mouse event handler | //+------------------------------------------------------------------+ void CSplitContainer::OnMouseEventPostProcessing(void) { if(!this.IsVisible() || !this.Enabled() || !this.Displayed()) return; ENUM_MOUSE_FORM_STATE state=this.GetMouseState(); switch(state) { //--- The cursor is outside the form, the mouse buttons are not clicked //--- The cursor is outside the form, any mouse button is clicked //--- The cursor is outside the form, the mouse wheel is being scrolled case MOUSE_FORM_STATE_OUTSIDE_FORM_NOT_PRESSED : case MOUSE_FORM_STATE_OUTSIDE_FORM_PRESSED : case MOUSE_FORM_STATE_OUTSIDE_FORM_WHEEL : case MOUSE_FORM_STATE_NONE : if(this.MouseEventLast()==MOUSE_EVENT_INSIDE_ACTIVE_AREA_NOT_PRESSED || this.MouseEventLast()==MOUSE_EVENT_INSIDE_FORM_NOT_PRESSED || this.MouseEventLast()==MOUSE_EVENT_OUTSIDE_FORM_NOT_PRESSED || this.MouseEventLast()==MOUSE_EVENT_INSIDE_CONTROL_AREA_NOT_PRESSED || this.MouseEventLast()==MOUSE_EVENT_INSIDE_CONTROL_AREA_PRESSED || this.MouseEventLast()==MOUSE_EVENT_INSIDE_CONTROL_AREA_WHEEL || this.MouseEventLast()==MOUSE_EVENT_NO_EVENT) { //--- Draw an empty rectangle in the control area this.DrawRectangleEmpty(); //--- Get the pointer to the separator CSplitter *splitter=this.GetSplitter(); if(splitter==NULL) { ::Print(DFUN,CMessage::Text(MSG_ELM_LIST_ERR_FAILED_GET_GRAPH_ELEMENT_OBJ),": ",this.TypeElementDescription(GRAPH_ELEMENT_TYPE_WF_SPLITTER)); return; } splitter.SetDisplayed(false); splitter.Hide(); //--- Get the pointers to hint objects CHintMoveLeft *hint_ml=this.GetHintMoveLeft(); CHintMoveRight *hint_mr=this.GetHintMoveRight(); CHintMoveUp *hint_mu=this.GetHintMoveUp(); CHintMoveDown *hint_md=this.GetHintMoveDown(); if(hint_ml==NULL || hint_mr==NULL || hint_mu==NULL || hint_md==NULL) return; //--- If the hide object is displayed, disable its display and hide it if(hint_ml.Displayed()) { hint_ml.SetDisplayed(false); hint_ml.Hide(); } if(hint_mr.Displayed()) { hint_mr.SetDisplayed(false); hint_mr.Hide(); } if(hint_mu.Displayed()) { hint_mu.SetDisplayed(false); hint_mu.Hide(); } if(hint_md.Displayed()) { hint_md.SetDisplayed(false); hint_md.Hide(); } //--- Set the current mouse state as the last one this.m_mouse_event_last=ENUM_MOUSE_EVENT(state+MOUSE_EVENT_NO_EVENT); } break; //--- The cursor is inside the form, the mouse buttons are not clicked //--- The cursor is inside the form, any mouse button is clicked //--- The cursor is inside the form, the mouse wheel is being scrolled //--- The cursor is inside the active area, the mouse buttons are not clicked //--- The cursor is inside the active area, any mouse button is clicked //--- The cursor is inside the active area, the mouse wheel is being scrolled //--- The cursor is inside the active area, left mouse button is released //--- The cursor is within the window scrolling area, the mouse buttons are not clicked //--- The cursor is within the window scrolling area, any mouse button is clicked //--- The cursor is within the window scrolling area, the mouse wheel is being scrolled //--- The cursor is within the window resizing area, the mouse buttons are not clicked //--- The cursor is within the window resizing area, the mouse button (any) is clicked //--- The cursor is within the window resizing area, the mouse wheel is being scrolled //--- The cursor is within the window resizing area, the mouse buttons are not clicked //--- The cursor is within the window resizing area, the mouse button (any) is clicked //--- The cursor is within the window separator area, the mouse wheel is being scrolled case MOUSE_FORM_STATE_INSIDE_FORM_NOT_PRESSED : case MOUSE_FORM_STATE_INSIDE_FORM_PRESSED : case MOUSE_FORM_STATE_INSIDE_FORM_WHEEL : case MOUSE_FORM_STATE_INSIDE_ACTIVE_AREA_NOT_PRESSED : case MOUSE_FORM_STATE_INSIDE_ACTIVE_AREA_PRESSED : case MOUSE_FORM_STATE_INSIDE_ACTIVE_AREA_WHEEL : case MOUSE_FORM_STATE_INSIDE_ACTIVE_AREA_RELEASED : case MOUSE_FORM_STATE_INSIDE_SCROLL_AREA_NOT_PRESSED : case MOUSE_FORM_STATE_INSIDE_SCROLL_AREA_PRESSED : case MOUSE_FORM_STATE_INSIDE_SCROLL_AREA_WHEEL : case MOUSE_FORM_STATE_INSIDE_RESIZE_AREA_NOT_PRESSED : case MOUSE_FORM_STATE_INSIDE_RESIZE_AREA_PRESSED : case MOUSE_FORM_STATE_INSIDE_RESIZE_AREA_WHEEL : case MOUSE_FORM_STATE_INSIDE_CONTROL_AREA_NOT_PRESSED: case MOUSE_FORM_STATE_INSIDE_CONTROL_AREA_PRESSED : case MOUSE_FORM_STATE_INSIDE_CONTROL_AREA_WHEEL : break; //--- MOUSE_EVENT_NO_EVENT default: break; } } //+------------------------------------------------------------------+
カーソルがSplitContainerコントロールのパネルに入ると同時に、区切りがあるコントロール領域を超えたことを示します。同時に、何らかの理由でCSplitContainerクラスのこのようなイベントのハンドラが起動されなかった場合、そのような状況はパネルオブジェクトのクラスで処理される必要があります。\MQL5\Include\DoEasy\Objects\Graph\WForms\Helpers\SplitContainerPanel.mqhファイルの「カーソルがアクティブ領域にあり、マウスボタンがクリックされていない」イベントのハンドラでヒントオブジェクトを非表示にするためのコードを追加します。
//+------------------------------------------------------------------+ //| 'The cursor is inside the active area, | //| no mouse buttons are clicked' event handler | //+------------------------------------------------------------------+ void CSplitContainerPanel::MouseActiveAreaNotPressedHandler(const int id,const long& lparam,const double& dparam,const string& sparam) { //--- Get the pointer to the base object CSplitContainer *base=this.GetBase(); //--- If the base object is not received, or the separator is non-movable, leave if(base==NULL || base.SplitterFixed()) return; //--- Draw an empty rectangle in the base object control area base.DrawRectangleEmpty(); //--- Get the pointer to the separator object from the base object CSplitter *splitter=base.GetSplitter(); if(splitter==NULL) { ::Print(DFUN,CMessage::Text(MSG_ELM_LIST_ERR_FAILED_GET_GRAPH_ELEMENT_OBJ),": ",this.TypeElementDescription(GRAPH_ELEMENT_TYPE_WF_SPLITTER)); return; } //--- If the separator is displayed if(splitter.Displayed()) { //--- Disable the display of the separator and hide it splitter.SetDisplayed(false); splitter.Hide(); } //--- Get the pointer to the hint objects from the base object CHintMoveLeft *hint_ml=base.GetHintMoveLeft(); CHintMoveRight *hint_mr=base.GetHintMoveRight(); CHintMoveUp *hint_mu=base.GetHintMoveUp(); CHintMoveDown *hint_md=base.GetHintMoveDown(); if(hint_ml==NULL || hint_mr==NULL || hint_mu==NULL || hint_md==NULL) return; //--- If the hide object is displayed, disable its display and hide it if(hint_ml.Displayed()) { hint_ml.SetDisplayed(false); hint_ml.Hide(); } if(hint_mr.Displayed()) { hint_mr.SetDisplayed(false); hint_mr.Hide(); } if(hint_mu.Displayed()) { hint_mu.SetDisplayed(false); hint_mu.Hide(); } if(hint_md.Displayed()) { hint_md.SetDisplayed(false); hint_md.Hide(); } } //+------------------------------------------------------------------+
このメソッドのロジックは、上記のハンドラ内のロジックと同じです。
カーソルが区切りオブジェクトにあるとき、カーソルの後にヒントオブジェクトを移動させる必要があります。区切り上のカーソルの動きは、区切りオブジェクトのクラスで追跡することができます。そのためには、カーソルが区切りのアクティブ領域上に来たときのイベントのハンドラをクラスに追加し、ヒントオブジェクトへのポインタを取得して、カーソルの後に区切り領域だけに動きを限定して移動させる必要があります。
\MQL5\Include\DoEasy\Objects\Graph\WForms\Helpers\Splitter.mqhで、仮想イベントハンドラを宣言します。
//--- Clear the element completely virtual void Erase(const bool redraw=false) { CWinFormBase::Erase(redraw); } //--- 'The cursor is inside the active area, the mouse buttons are not clicked' event handler virtual void MouseActiveAreaNotPressedHandler(const int id,const long& lparam,const double& dparam,const string& sparam); //--- 'The cursor is inside the active area, a mouse button is clicked (any)' event handler virtual void MouseActiveAreaPressedHandler(const int id,const long& lparam,const double& dparam,const string& sparam); //--- 'The cursor is inside the active area, the left mouse button is clicked' event handler virtual void MouseActiveAreaReleasedHandler(const int id,const long& lparam,const double& dparam,const string& sparam); }; //+------------------------------------------------------------------+
クラス本体の外側で実装しましょう。
//+------------------------------------------------------------------+ //| 'The cursor is inside the active area, | //| no mouse buttons are clicked' event handler | //+------------------------------------------------------------------+ void CSplitter::MouseActiveAreaNotPressedHandler(const int id,const long& lparam,const double& dparam,const string& sparam) { //--- Get the pointer to the base object CWinFormBase *base=this.GetBase(); if(base==NULL) return; //--- Declare the pointers to hint objects CWinFormBase *hint_ml=NULL; CWinFormBase *hint_mr=NULL; CWinFormBase *hint_mu=NULL; CWinFormBase *hint_md=NULL; //--- Get cursor coordinates int x=this.m_mouse.CoordX(); int y=this.m_mouse.CoordY(); //--- Depending on the separator direction, switch((int)this.GetProperty(CANV_ELEMENT_PROP_SPLIT_CONTAINER_SPLITTER_ORIENTATION)) { //--- vertical position case CANV_ELEMENT_SPLITTER_ORIENTATION_VERTICAL : //--- From the base object, get the pointer to the "left shift" and "right shift" hint objects hint_ml=base.GetElementByType(GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_LEFT,0); hint_mr=base.GetElementByType(GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_RIGHT,0); if(hint_ml==NULL || hint_mr==NULL) return; //--- Shift hints following the cursor hint_ml.Move(hint_ml.CoordX(),(y-hint_ml.Height()<this.CoordY() ? this.CoordY() : y-hint_ml.Height())); hint_mr.Move(hint_mr.CoordX(),(y-hint_mr.Height()<this.CoordY() ? this.CoordY() : y-hint_mr.Height()),true); break; //---CANV_ELEMENT_SPLITTER_ORIENTATION_HORISONTAL //--- horizontal position of the separator default: //--- From the base object, get the pointer to the "shift up" and "shift down" hint objects hint_mu=base.GetElementByType(GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_UP,0); hint_md=base.GetElementByType(GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_DOWN,0); if(hint_mu==NULL || hint_md==NULL) return; //--- Shift hints following the cursor hint_mu.Move((x-hint_mu.Width()<this.CoordX() ? this.CoordX() : x-hint_mu.Width()),hint_mu.CoordY()); hint_md.Move((x-hint_md.Width()<this.CoordX() ? this.CoordX() : x-hint_md.Width()),hint_md.CoordY(),true); break; } } //+------------------------------------------------------------------+
メソッドのロジックはコードのコメントで完全に説明されています。なお、ヒントオブジェクトの移動制限は、算出した座標をオブジェクトシフトメソッドに渡す際に、直接実装されます。
グラフィック要素コレクションクラスのファイル(\MQL5\Include\DoEasy\Collections\GraphElementsCollection.mqh)で、すべてのWinFormsオブジェクト作成メソッドでメインオブジェクトと基本オブジェクトへのポインタを渡すように設定します。
//--- Create a graphical element object on canvas on a specified chart and subwindow int CreateElement(const long chart_id, const int subwindow, const string descript, const int x, const int y, const int w, const int h, const color clr, const uchar opacity, const bool movable, const bool activity, const bool redraw=false) { int id=this.GetMaxID()+1; CGCnvElement *obj=new CGCnvElement(GRAPH_ELEMENT_TYPE_ELEMENT,NULL,NULL,id,0,chart_id,subwindow,descript,x,y,w,h,clr,opacity,movable,activity,redraw); ENUM_ADD_OBJ_RET_CODE res=this.AddOrGetCanvElmToCollection(obj,id); if(res==ADD_OBJ_RET_CODE_ERROR) return WRONG_VALUE; if(res==ADD_OBJ_RET_CODE_EXIST) obj.SetID(id); obj.Erase(clr,opacity,redraw); return obj.ID(); }
ここでは、NULLがメインオブジェクトと基本オブジェクトとして設定されています。なぜなら、このクラスから(他に結びついたものではなく)独立したオブジェクトを作成するからです。そのため、当初はメインで独立したオブジェクトとなります。
このようなメソッドはクラス内に多数存在しますが、変更点はすべて同じです。したがって、ここでそのすべてを説明する必要はありません。その中のポインタと後続のWinFormsオブジェクトの受け渡しは、上記のメソッドとは少し異なるので、フォームオブジェクトを作成するいくつかのメソッドのうちの1つだけを考えてみましょう。
//--- Create a graphical form object on canvas on a specified chart and subwindow int CreateForm(const long chart_id, const int subwindow, const string descript, const int x, const int y, const int w, const int h, const color clr, const uchar opacity, const bool movable, const bool activity, const bool shadow=false, const bool redraw=false) { int id=this.GetMaxID()+1; CForm *obj=new CForm(NULL,NULL,chart_id,subwindow,descript,x,y,w,h); ENUM_ADD_OBJ_RET_CODE res=this.AddOrGetCanvElmToCollection(obj,id); if(res==ADD_OBJ_RET_CODE_ERROR) return WRONG_VALUE; obj.SetID(id); obj.SetActive(activity); obj.SetMovable(movable); obj.SetBackgroundColor(clr,true); obj.SetBorderColor(clr,true); obj.SetOpacity(opacity,false); obj.SetShadow(shadow); obj.DrawRectangle(0,0,obj.Width()-1,obj.Height()-1,obj.BorderColor(),obj.Opacity()); obj.Done(); obj.Erase(clr,opacity,redraw); return obj.ID(); }
ここでは、メインオブジェクトと基本オブジェクトを指定する前に、オブジェクトの型は渡しません。これは作成されたクラスのコンストラクタに既に登録されています。
その他の変更はすべて同じで、クラスファイルですでにおこなわれています。結果をテストしてみましょう。
検証
テストを実行するには、前の記事のEAを\MQL5\Experts\TestDoEasy\Part124\TestDoEasy124.mq5として保存します。
EAに変更はありません。コンパイルしてチャート上で起動してみましょう。
実装された機能は正常に動作します。
次の段階
次の記事では、TabControlに取り組みます。
現在のライブラリバージョン、テストEA、およびMQL5のチャートイベントコントロール指標のすべてのファイルが、テストおよびダウンロードできるように以下に接続されています。
連載のこれまでの記事
DoEasy.コントロール(第20部):SplitContainerWinFormsオブジェクト
DoEasy.コントロール(第21部):SplitContainerコントロール。パネル区切りー
DoEasy.コントロール(第22部):SplitContainer。作成したオブジェクトのプロパティを変更する
DoEasy.コントロール(第23部):TabControlとSplitContainerのWinFormsオブジェクトの改善
MetaQuotes Ltdによってロシア語から翻訳されました。
元の記事: https://www.mql5.com/ru/articles/11661





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