![DoEasy - コントロール(第22部):SplitContainer - 作成したオブジェクトのプロパティを変更する](https://c.mql5.com/2/49/DoEasy_22_SplitContainer_600x314.jpg)
DoEasy - コントロール(第22部):SplitContainer - 作成したオブジェクトのプロパティを変更する
内容
概念
ライブラリ内のSplitContainerコントロールは、デフォルト値で作成されています。オブジェクトを作成した後、そのプロパティを変更することはできますが、外観は変わりません。これを防ぐには、オブジェクトのプロパティを変更した後、そのプロパティの新しい値で再描画します。
今回の記事は比較的小さくなります。コントロールのプロパティを設定するためのメソッドを確定し、そのプロパティへのすべての変更が直ちにその外観を変更できるようにする予定です。
ライブラリクラスの改善
\MQL5\Include\DoEasy\Defines.mqhにある、WinFormsコントロールで可能なイベントのリストに、2つの新しいイベントを追加します。
//+------------------------------------------------------------------+ //| List of possible WinForms control events | //+------------------------------------------------------------------+ enum ENUM_WF_CONTROL_EVENT { WF_CONTROL_EVENT_NO_EVENT = GRAPH_OBJ_EVENTS_NEXT_CODE,// No event WF_CONTROL_EVENT_CLICK, // "Click on the control" event WF_CONTROL_EVENT_CLICK_CANCEL, // "Canceling the click on the control" event WF_CONTROL_EVENT_MOVING, // "Control relocation" event WF_CONTROL_EVENT_STOP_MOVING, // "Control relocation stop" event WF_CONTROL_EVENT_TAB_SELECT, // "TabControl tab selection" event WF_CONTROL_EVENT_CLICK_SCROLL_LEFT, // "Clicking the control left button" event WF_CONTROL_EVENT_CLICK_SCROLL_RIGHT, // "Clicking the control right button" event WF_CONTROL_EVENT_CLICK_SCROLL_UP, // "Clicking the control up button" event WF_CONTROL_EVENT_CLICK_SCROLL_DOWN, // "Clicking the control down button" event }; #define WF_CONTROL_EVENTS_NEXT_CODE (WF_CONTROL_EVENT_CLICK_SCROLL_DOWN+1) // The code of the next event after the last graphical element event code //+------------------------------------------------------------------+
いくつかのコントロールはその動きに反応し、プログラムまたは操作する親コントロールにイベントを送信する必要があります。例えば、スクロール領域のスライダーがマウスに捕捉されて移動した場合、その移動に関するイベントと、移動した値を送信する必要があります。同様に、SplitContainerコントロールの区切りもその動きに反応するようにします。
現在の実装では、マウスカーソルを乗せると外観が変化します。移動すると、親要素にイベントが送られ、親要素はこのイベントに反応してパネルのサイズを変更します。その後、この動作を確定させると、マウスを合わせたときに、オブジェクトの輪郭が点線で描かれた長方形になります。マウスで捕獲すると、ハッチングされた領域で埋め尽くされます。マウスを離すと(移動が完了すると)、区切りは元の姿に戻ります。
このようなイベント(コントロールの移動と移動完了)に正しく、時間通りに対応するために、2つの新しいイベントを追加しました。リストから一番最後のイベントWF_CONTROL_EVENT_SPLITTER_MOVEを削除しました。すべてのコントロールに対して普遍的であるWF_CONTROL_EVENT_MOVINGイベントと置き換えることができるからです。リストの最後のイベントがWF_CONTROL_EVENT_CLICK_SCROLL_DOWN イベントなので、次のイベントコード値の計算にそれを入力します。
前回のEAを実行し、メインパネルの移動を開始すると、SplitContainerコントロールにその区切りが表示されなくなります。マウスカーソルを合わせるまで非表示になるはずなので、これは正しいですが、区切りオブジェクトを一度でも移動させると、それ以降はメインパネルのどのような動きでもこの隠されたコントロールが表示されるようになります。
これは、なかなか見つからなかったロジックエラーですが、その解決策を手に入れました。BringToTop()メソッドは、まずオブジェクトを非表示にしてから表示することで、オブジェクトを前景に出すように動作します。このメソッドでは、要素の描画の必要性を示すフラグの状態が考慮されていないため、非表示になっていた区切りオブジェクトが見えるようになります。
\MQL5\Include\DoEasy\Objects\Graph\Form.mqhにあるオブジェクトを上位に設定するメソッドに、その要素の描画の必要性を示すフラグの確認を追加しました。オブジェクトを表示しない場合は、このメソッドを単に終了します。
//+------------------------------------------------------------------+ //| Set the object above all the rest | //+------------------------------------------------------------------+ void CForm::BringToTop(void) { //--- If the object should not be displayed, leave if(!this.Displayed()) return; //--- If the shadow usage flag is set if(this.m_shadow) { //--- If the shadow object is created, move it to the foreground if(this.m_shadow_obj!=NULL) this.m_shadow_obj.BringToTop(); } //--- Move the object to the foreground (the object is located above the shadow) CGCnvElement::BringToTop(); //--- In the loop by all bound objects, int total=this.m_list_elements.Total(); for(int i=0;i<total;i++) { //--- get the next object from the list CGCnvElement *obj=this.m_list_elements.At(i); if(obj==NULL) continue; //--- and bring it to the foreground if the object should be displayed if(!obj.Displayed()) continue; obj.BringToTop(); } } //+------------------------------------------------------------------+
接続されているすべてのオブジェクトのリストに同じ確認を追加します。オブジェクトがフォアグラウンドにならないように、描画必要フラグが無効になっているオブジェクトはスキップします。
最後のマウスイベントのハンドラで、オブジェクト描画必要フラグを確認するようにします。
//+------------------------------------------------------------------+ //| Last mouse event handler | //+------------------------------------------------------------------+ void CForm::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 //---... //---...
以前は、非表示および非アクティブのオブジェクトに対して最後のマウスイベントが処理されませんでした。現状では、表示しないオブジェクトは処理されていません。
\MQL5\Include\DoEasy\Objects\Graph\WForms\Helpers\SplitContainerPanel.mqhの SplitContainerコントロールパネルオブジェクトクラスで、要素をクリアするメソッドにオブジェクト描画要否フラグの確認を追加しました。
//+------------------------------------------------------------------+ //| Clear the element filling it with color and opacity | //+------------------------------------------------------------------+ void CSplitContainerPanel::Erase(const color colour,const uchar opacity,const bool redraw=false) { //--- If the element should not be displayed (hidden inside another control), leave if(!this.Displayed()) return; //--- 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(); //--- Update the element having the specified redrawing flag this.Crop(); this.Update(redraw); } //+------------------------------------------------------------------+ //| Clear the element with a gradient fill | //+------------------------------------------------------------------+ void CSplitContainerPanel::Erase(color &colors[],const uchar opacity,const bool vgradient,const bool cycle,const bool redraw=false) { //--- If the element should not be displayed (hidden inside another control), leave if(!this.Displayed()) return; //--- 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(); //--- Update the element having the specified redrawing flag this.Crop(); this.Update(redraw); } //+------------------------------------------------------------------+
これらの確認は、SplitContainerコントロールでパネルが非表示(折りたたみ)になっている場合に、意図せずに表示されることを防ぐ役割も果たします。
SplitContainerコントロールの区切りに固定区切りフラグが設定されている場合、マウスと一切連動しないようにします。従って、マウスカーソルがパネルに乗った(カーソルが区切りを離れてパネルに入った)場合、固定区切りの場合はそのようなイベントを処理する必要はありません。
「カーソルがアクティブ領域内にあり、マウスボタンがクリックされていない」イベントのハンドラで、固定区切りフラグを確認するようにしました。
//+------------------------------------------------------------------+ //| '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; //--- 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(); } } //+------------------------------------------------------------------+
このような確認は、実は、私たちが不必要な行動をしないように制限しているだけなのです。最初から非表示になっている場合は、区切りオブジェクトを取得して非表示にする必要はありません。
すべての主要な変更と改善は、SplitContainer WinForms オブジェクトの\MQL5\Include\DoEasy\Objects\Graph\WForms\Containers\SplitContainer.mqhファイルでおこなわれます。
コントロールパネルを折りたたみ/展開するための新しいメソッドをクラスのprivateセクションで宣言します。
//--- Set the panel parameters bool SetsPanelParams(void); //--- (1) Collapse and (2) expand the panel 1 void CollapsePanel1(void); void ExpandPanel1(void); //--- (1) Collapse and (2) expand the panel 2 void CollapsePanel2(void); void ExpandPanel2(void); public:
宣言されたメソッドをクラス本体の外側に実装します。
折りたたまれたパネルは非表示になり、その表示必要性プロパティはfalseに設定されます。
//+------------------------------------------------------------------+ //| Collapse the panel 1 | //+------------------------------------------------------------------+ void CSplitContainer::CollapsePanel1(void) { CSplitContainerPanel *panel=this.GetPanel1(); if(panel==NULL) return; panel.SetDisplayed(false); panel.Hide(); } //+------------------------------------------------------------------+ //| Collapse the panel 2 | //+------------------------------------------------------------------+ void CSplitContainer::CollapsePanel2(void) { CSplitContainerPanel *panel=this.GetPanel2(); if(panel==NULL) return; panel.SetDisplayed(false); panel.Hide(); } //+------------------------------------------------------------------+
展開されたパネルでは、表示必要性を設定し、パネルを表示して前景に持ってきます。
//+------------------------------------------------------------------+ //| Expand the panel 1 | //+------------------------------------------------------------------+ void CSplitContainer::ExpandPanel1(void) { CSplitContainerPanel *panel=this.GetPanel1(); if(panel==NULL) return; panel.SetDisplayed(true); panel.Show(); panel.BringToTop(); } //+------------------------------------------------------------------+ //| Expand the panel 2 | //+------------------------------------------------------------------+ void CSplitContainer::ExpandPanel2(void) { CSplitContainerPanel *panel=this.GetPanel2(); if(panel==NULL) return; panel.SetDisplayed(true); panel.Show(); panel.BringToTop(); } //+------------------------------------------------------------------+
ライブラリのオブジェクトにプロパティを設定するメソッドには、単にプロパティに値を設定するものと、グラフィカルオブジェクトに設定するのと一緒にプロパティに値を設定するものがあります。クラスメソッドに同じ機能を実装しましょう。オブジェクトのプロパティに単に値を書き込むか、値をプロパティに書き込んだ後にオブジェクト全体を再描画します。これは、プロパティを変更すると、SplitContainerコントロールの外観が変更されるためです。
オブジェクトプロパティに値を設定する必要性を示すフラグをメソッドの仮パラメータに追加し、メソッドの実装をクラス本体の外に移動してここで削除します。
//--- (1) set and (2) return the separator distance from the edge void SetSplitterDistance(const int value,const bool only_prop); int SplitterDistance(void) const { return (int)this.GetProperty(CANV_ELEMENT_PROP_SPLIT_CONTAINER_SPLITTER_DISTANCE); } //--- (1) set and (2) return the separator non-removability flag void SetSplitterFixed(const bool flag) { this.SetProperty(CANV_ELEMENT_PROP_SPLIT_CONTAINER_SPLITTER_FIXED,flag); } bool SplitterFixed(void) const { return (bool)this.GetProperty(CANV_ELEMENT_PROP_SPLIT_CONTAINER_SPLITTER_FIXED); } //--- (1) set and (2) return the separator width void SetSplitterWidth(const int value,const bool only_prop); int SplitterWidth(void) const { return (int)this.GetProperty(CANV_ELEMENT_PROP_SPLIT_CONTAINER_SPLITTER_WIDTH); } //--- (1) set and (2) return the separator location void SetSplitterOrientation(const ENUM_CANV_ELEMENT_SPLITTER_ORIENTATION value,const bool only_prop); ENUM_CANV_ELEMENT_SPLITTER_ORIENTATION SplitterOrientation(void) const { return(ENUM_CANV_ELEMENT_SPLITTER_ORIENTATION)this.GetProperty(CANV_ELEMENT_PROP_SPLIT_CONTAINER_SPLITTER_ORIENTATION); }
クラスオブジェクトが独立して区切り領域からのマウスカーソルの除去を処理するためには、最後のマウスイベントのための仮想ハンドラを追加する必要があります。クラスのpublicセクションで宣言してみましょう。
//--- Event handler virtual void OnChartEvent(const int id,const long& lparam,const double& dparam,const string& sparam); //--- '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); //--- Last mouse event handler virtual void OnMouseEventPostProcessing(void); //--- Constructor CSplitContainer(const long chart_id, const int subwindow, const string descript, const int x, const int y, const int w, const int h); }; //+------------------------------------------------------------------+
クラスのコンストラクタで、固定区切りのプロパティのデフォルト値をmovableに設定します。
//+------------------------------------------------------------------+ //| Constructor indicating the chart and subwindow ID | //+------------------------------------------------------------------+ CSplitContainer::CSplitContainer(const long chart_id, const int subwindow, const string descript, const int x, const int y, const int w, const int h) : CContainer(GRAPH_ELEMENT_TYPE_WF_SPLIT_CONTAINER,chart_id,subwindow,descript,x,y,w,h) { this.SetTypeElement(GRAPH_ELEMENT_TYPE_WF_SPLIT_CONTAINER); this.m_type=OBJECT_DE_TYPE_GWF_CONTAINER; this.SetBorderSizeAll(0); this.SetBorderStyle(FRAME_STYLE_NONE); this.SetPaddingAll(0); this.SetMarginAll(3); this.SetOpacity(0,true); this.SetBackgroundColor(CLR_CANV_NULL,true); this.SetBackgroundColorMouseDown(CLR_CANV_NULL); this.SetBackgroundColorMouseOver(CLR_CANV_NULL); this.SetBorderColor(CLR_CANV_NULL,true); this.SetBorderColorMouseDown(CLR_CANV_NULL); this.SetBorderColorMouseOver(CLR_CANV_NULL); this.SetForeColor(CLR_DEF_FORE_COLOR,true); this.SetSplitterFixed(false); this.CreatePanels(); } //+------------------------------------------------------------------+
オブジェクト作成後、区切りはデフォルトで移動可能になります。区切りを後で移動不可にする場合は、同じ方法でプロパティをtrueに設定するだけです。
パネルパラメータを設定するメソッドを変更しました。
非表示パネルのサイズと同じ大きさが、すべての非表示パネルに設定されることになります。パネルは、同時に表示されたまま、そのコンテナのサイズ、つまりSplitContainerオブジェクトのサイズ全体と同じになります。
//+------------------------------------------------------------------+ //| Set the panel parameters | //+------------------------------------------------------------------+ bool CSplitContainer::SetsPanelParams(void) { switch(this.SplitterOrientation()) { //--- The separator is positioned vertically case CANV_ELEMENT_SPLITTER_ORIENTATION_VERTICAL : //--- If both panels are not collapsed, if(!this.Panel1Collapsed() && !this.Panel2Collapsed()) { //--- set the panel1 coordinates and size this.m_panel1_x=0; this.m_panel1_y=0; this.m_panel1_w=this.SplitterDistance(); this.m_panel1_h=this.Height(); //--- set the panel2 coordinates and size this.m_panel2_x=this.SplitterDistance()+this.SplitterWidth(); this.m_panel2_y=0; this.m_panel2_w=this.Width()-this.m_panel2_x; this.m_panel2_h=this.Height(); //--- write separator coordinates and size this.m_splitter_x=this.SplitterDistance(); this.m_splitter_y=0; this.m_splitter_w=this.SplitterWidth(); this.m_splitter_h=this.Height(); } //--- If panel1 or panel2 is collapsed, else { //--- write the coordinates and sizes of panel1 and panel2 this.m_panel2_x=this.m_panel1_x=0; this.m_panel2_y=this.m_panel1_y=0; this.m_panel2_w=this.m_panel1_w=this.Width(); this.m_panel2_h=this.m_panel1_h=this.Height(); //--- write separator coordinates and size this.m_splitter_x=0; this.m_splitter_y=0; this.m_splitter_w=this.SplitterWidth(); this.m_splitter_h=this.Height(); } break; //--- The separator is located horizontally case CANV_ELEMENT_SPLITTER_ORIENTATION_HORISONTAL : //--- If both panels are not collapsed, if(!this.Panel1Collapsed() && !this.Panel2Collapsed()) { //--- set the panel1 coordinates and size this.m_panel1_x=0; this.m_panel1_y=0; this.m_panel1_w=this.Width(); this.m_panel1_h=this.SplitterDistance(); //--- set the panel2 coordinates and size this.m_panel2_x=0; this.m_panel2_y=this.SplitterDistance()+this.SplitterWidth(); this.m_panel2_w=this.Width(); this.m_panel2_h=this.Height()-this.m_panel2_y; //--- write separator coordinates and size this.m_splitter_x=0; this.m_splitter_y=this.SplitterDistance(); this.m_splitter_w=this.Width(); this.m_splitter_h=this.SplitterWidth(); } //--- If panel1 or panel2 is collapsed, else { //--- write the coordinates and sizes of panel1 and panel2 this.m_panel2_x=this.m_panel1_x=0; this.m_panel2_y=this.m_panel1_y=0; this.m_panel2_w=this.m_panel1_w=this.Width(); this.m_panel2_h=this.m_panel1_h=this.Height(); //--- write separator coordinates and size this.m_splitter_x=0; this.m_splitter_y=0; this.m_splitter_w=this.Width(); this.m_splitter_h=this.SplitterWidth(); } break; default: return false; break; } //--- Set the coordinates and sizes of the control area equal to the properties set by the separator this.SetProperty(CANV_ELEMENT_PROP_CONTROL_AREA_X,this.m_splitter_x); this.SetProperty(CANV_ELEMENT_PROP_CONTROL_AREA_Y,this.m_splitter_y); this.SetProperty(CANV_ELEMENT_PROP_CONTROL_AREA_WIDTH,this.m_splitter_w); this.SetProperty(CANV_ELEMENT_PROP_CONTROL_AREA_HEIGHT,this.m_splitter_h); return true; } //+------------------------------------------------------------------+
以下は、パネル1の折りたたみフラグを設定するメソッドです。
//+------------------------------------------------------------------+ //| Set the flag of collapsed panel 1 | //+------------------------------------------------------------------+ void CSplitContainer::SetPanel1Collapsed(const int flag) { //--- Set the flag, passed to the method, to the object property this.SetProperty(CANV_ELEMENT_PROP_SPLIT_CONTAINER_PANEL1_COLLAPSED,flag); CSplitContainerPanel *p1=this.GetPanel1(); CSplitContainerPanel *p2=this.GetPanel2(); if(p1==NULL || p2==NULL) return; //--- Set the parameters of the panels and the separator this.SetsPanelParams(); //--- If panel1 should be collapsed if(this.Panel1Collapsed()) { //--- If panel1 is shifted to new coordinates and its size is changed, hide panel1 if(p1.Move(this.CoordX()+this.m_panel1_x,this.CoordY()+this.m_panel1_y) && p1.Resize(this.m_panel1_w,this.m_panel1_h,false)) { p1.SetCoordXRelative(p1.CoordX()-this.CoordX()); p1.SetCoordYRelative(p1.CoordY()-this.CoordY()); this.CollapsePanel1(); } //--- set the expanded flag for panel2 this.SetProperty(CANV_ELEMENT_PROP_SPLIT_CONTAINER_PANEL2_COLLAPSED,false); //--- If panel2 is shifted to new coordinates and its size is changed, display panel2 if(p2.Move(this.CoordX()+this.m_panel2_x,this.CoordY()+this.m_panel2_y) && p2.Resize(this.m_panel2_w,this.m_panel2_h,true)) { p2.SetCoordXRelative(p2.CoordX()-this.CoordX()); p2.SetCoordYRelative(p2.CoordY()-this.CoordY()); this.ExpandPanel2(); } } //--- If panel1 should be expanded, else { //--- set the collapsed flag for panel2 this.SetProperty(CANV_ELEMENT_PROP_SPLIT_CONTAINER_PANEL2_COLLAPSED,true); //--- If panel2 is shifted to new coordinates and its size is changed, hide panel2 if(p2.Move(this.CoordX()+this.m_panel2_x,this.CoordY()+this.m_panel2_y) && p2.Resize(this.m_panel2_w,this.m_panel2_h,false)) { p2.SetCoordXRelative(p2.CoordX()-this.CoordX()); p2.SetCoordYRelative(p2.CoordY()-this.CoordY()); this.CollapsePanel2(); } //--- If panel1 is shifted to new coordinates and its size is changed, display panel1 if(p1.Move(this.CoordX()+this.m_panel1_x,this.CoordY()+this.m_panel1_y) && p1.Resize(this.m_panel1_w,this.m_panel1_h,true)) { p1.SetCoordXRelative(p1.CoordX()-this.CoordX()); p1.SetCoordYRelative(p1.CoordY()-this.CoordY()); this.ExpandPanel1(); } } } //+------------------------------------------------------------------+
メソッドのロジックは、コードにコメントされています。メソッドに渡されたフラグの値に応じて、パネル1を非表示にし(メソッドに渡されたフラグはtrueに等しい)、パネル2をコンテナのフルサイズに拡大して表示します。メソッドにfalseを渡すと、パネル2が非表示になり、パネル1はコンテナのフルサイズに拡大されます。
以下は、パネル2の折りたたみフラグを設定するメソッドです。
//+------------------------------------------------------------------+ //| Set the flag of collapsed panel 2 | //+------------------------------------------------------------------+ void CSplitContainer::SetPanel2Collapsed(const int flag) { //--- Set the flag, passed to the method, to the object property this.SetProperty(CANV_ELEMENT_PROP_SPLIT_CONTAINER_PANEL2_COLLAPSED,flag); CSplitContainerPanel *p1=this.GetPanel1(); CSplitContainerPanel *p2=this.GetPanel2(); if(p1==NULL || p2==NULL) return; //--- Set the parameters of the panels and the separator this.SetsPanelParams(); //--- If panel2 should be collapsed, if(this.Panel2Collapsed()) { //--- If panel2 is shifted to new coordinates and its size is changed, hide panel2 if(p2.Move(this.CoordX()+this.m_panel2_x,this.CoordY()+this.m_panel2_y) && p2.Resize(this.m_panel2_w,this.m_panel2_h,false)) { p2.SetCoordXRelative(p2.CoordX()-this.CoordX()); p2.SetCoordYRelative(p2.CoordY()-this.CoordY()); this.CollapsePanel2(); } //--- set the expanded flag for panel1 this.SetProperty(CANV_ELEMENT_PROP_SPLIT_CONTAINER_PANEL1_COLLAPSED,false); //--- If panel1 is shifted to new coordinates and its size is changed, display panel1 if(p1.Move(this.CoordX()+this.m_panel1_x,this.CoordY()+this.m_panel1_y) && p1.Resize(this.m_panel1_w,this.m_panel1_h,true)) { p1.SetCoordXRelative(p1.CoordX()-this.CoordX()); p1.SetCoordYRelative(p1.CoordY()-this.CoordY()); this.ExpandPanel1(); } } //--- If panel2 should be expanded, else { //--- set the collapsed flag for panel1 this.SetProperty(CANV_ELEMENT_PROP_SPLIT_CONTAINER_PANEL1_COLLAPSED,true); //--- If panel1 is shifted to new coordinates and its size is changed, hide panel1 if(p1.Move(this.CoordX()+this.m_panel1_x,this.CoordY()+this.m_panel1_y) && p1.Resize(this.m_panel1_w,this.m_panel1_h,false)) { p1.SetCoordXRelative(p1.CoordX()-this.CoordX()); p1.SetCoordYRelative(p1.CoordY()-this.CoordY()); this.CollapsePanel1(); } //--- If panel2 is shifted to new coordinates and its size is changed, display panel2 if(p2.Move(this.CoordX()+this.m_panel2_x,this.CoordY()+this.m_panel2_y) && p2.Resize(this.m_panel2_w,this.m_panel2_h,true)) { p2.SetCoordXRelative(p2.CoordX()-this.CoordX()); p2.SetCoordYRelative(p2.CoordY()-this.CoordY()); this.ExpandPanel2(); } } } //+------------------------------------------------------------------+
ロジックは上記メソッドと同様ですが、フラグはパネル2に対して設定され、パネル1はパネル2の折りたたみ/展開に応じて非表示/表示になります。
以下は、区切りのエッジからの距離を設定するメソッドです。
//+------------------------------------------------------------------+ //| Set the separator distance from the edge | //+------------------------------------------------------------------+ void CSplitContainer::SetSplitterDistance(const int value,const bool only_prop) { //--- Set the value, passed to the method, to the object property this.SetProperty(CANV_ELEMENT_PROP_SPLIT_CONTAINER_SPLITTER_DISTANCE,value); //--- Depending on the direction of the separator (vertical or horizontal), //--- set the values to the coordinates of the object control area switch(this.SplitterOrientation()) { case CANV_ELEMENT_SPLITTER_ORIENTATION_VERTICAL : this.SetProperty(CANV_ELEMENT_PROP_CONTROL_AREA_X,this.SplitterDistance()); this.SetProperty(CANV_ELEMENT_PROP_CONTROL_AREA_Y,0); break; //---CANV_ELEMENT_SPLITTER_ORIENTATION_HORISONTAL default: this.SetProperty(CANV_ELEMENT_PROP_CONTROL_AREA_X,0); this.SetProperty(CANV_ELEMENT_PROP_CONTROL_AREA_Y,this.SplitterDistance()); break; } //--- If only setting the property, leave if(only_prop) return; //--- 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 parameters of the panels and the separator this.SetsPanelParams(); //--- If the size of the separator object has been successfully changed if(sp.Resize(this.m_splitter_w,this.m_splitter_h,false)) { //--- Shift the separator if(sp.Move(this.CoordX()+this.m_splitter_x,this.CoordY()+this.m_splitter_y)) { //--- Set new relative separator coordinates sp.SetCoordXRelative(sp.CoordX()-this.CoordX()); sp.SetCoordYRelative(sp.CoordY()-this.CoordY()); //--- 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()); } } } } } } //+------------------------------------------------------------------+
渡されたonly_propフラグ (プロパティのみを設定) がfalseの場合、パネルと区切りの新しい座標とサイズを設定し、「区切り距離」プロパティに新しい値を設定することとは別に、 新しい区切り距離値に従ってパネルを再構築する必要があります。
以下は、区切りの厚みを設定するメソッドです。
//+------------------------------------------------------------------+ //| Set the separator width | //+------------------------------------------------------------------+ void CSplitContainer::SetSplitterWidth(const int value,const bool only_prop) { //--- Set the value, passed to the method, to the object property this.SetProperty(CANV_ELEMENT_PROP_SPLIT_CONTAINER_SPLITTER_WIDTH,value); //--- Depending on the direction of the separator (vertical or horizontal), //--- set the values to the object control area width and height switch(this.SplitterOrientation()) { case CANV_ELEMENT_SPLITTER_ORIENTATION_VERTICAL : this.SetProperty(CANV_ELEMENT_PROP_CONTROL_AREA_WIDTH,this.SplitterWidth()); this.SetProperty(CANV_ELEMENT_PROP_CONTROL_AREA_HEIGHT,this.Height()); break; //---CANV_ELEMENT_SPLITTER_ORIENTATION_HORISONTAL default: this.SetProperty(CANV_ELEMENT_PROP_CONTROL_AREA_WIDTH,this.Width()); this.SetProperty(CANV_ELEMENT_PROP_CONTROL_AREA_HEIGHT,this.SplitterWidth()); break; } //--- If only setting the property, leave if(only_prop) return; //--- 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 parameters of the panels and the separator this.SetsPanelParams(); //--- If the size of the separator object has been successfully changed if(sp.Resize(this.m_splitter_w,this.m_splitter_h,false)) { //--- If the separator is shifted to new coordinates if(sp.Move(this.CoordX()+this.m_splitter_x,this.CoordY()+this.m_splitter_y)) { //--- Set new relative separator coordinates sp.SetCoordXRelative(sp.CoordX()-this.CoordX()); sp.SetCoordYRelative(sp.CoordY()-this.CoordY()); //--- 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()); } } } } } } //+------------------------------------------------------------------+
このメソッドの改良のロジックは、上で検討したメソッドと似ています。
以下は、区切りの位置を設定するメソッドです。
//+------------------------------------------------------------------+ //| 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 only setting the property, leave if(only_prop) return; //--- 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 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); } } //+------------------------------------------------------------------+
このメソッドの論理は、上記のメソッドと同一です。まず、メソッドに渡された値をオブジェクトのプロパティに設定します。only_propフラグがfalseの場合、パネルと区切りのパラメータを設定 し、設定されたサイズと座標のプロパティに応じて、パネルと区切りの位置を再配置します。
オブジェクトのプロパティを変更するメソッドは、パネルと区切りの位置をすぐに再構築できるようになったので、イベントハンドラは少し短くなりました。
//+------------------------------------------------------------------+ //| 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; //--- 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; } //--- 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); } } } //+------------------------------------------------------------------+区切りが固定されている場合、要素の移動イベントは処理されないため、この値は確認され、区切りが固定されている場合はハンドラを終了します。SetSplitterDistance()メソッドは、ここで、区切り座標の新しい値を設定してオブジェクトのパネルを再構築するという2つの機能を実行します。このメソッドの呼び出し時にonly_propフラグがfalseとして指定されているからです。
「カーソルがアクティブ領域内にあり、マウスボタンがクリックされていない」イベントのハンドラは、区切りオブジェクトへのポインタを受け取り、ハッチングで表示します。SplitContainerコントロールの区切りが固定されている場合は、区切りオブジェクトを表示しないようにします。
そこで、ハンドラの冒頭で、区切りが固定されている場合はこのメソッドを終了するための確認を追加します。
//+------------------------------------------------------------------+ //| '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; //--- 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); } } //+------------------------------------------------------------------+
すべてのグラフィック要素において、オブジェクトに対する最後のマウスイベントが何であったかを常に確認することができます。これを実現するために、フォームオブジェクトクラスには仮想のOnMouseEventPostProcessing()メソッドがあり、親クラスのメソッドのロジックが最後のマウスイベントの処理に適していない場合、派生クラスでオーバーライドすることが可能です。このクラスでやったことは、まさにこれです。
以下は、最後のマウスイベントハンドラです。
//+------------------------------------------------------------------+ //| 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_SPLITTER_AREA_NOT_PRESSED || this.MouseEventLast()==MOUSE_EVENT_INSIDE_SPLITTER_AREA_PRESSED || this.MouseEventLast()==MOUSE_EVENT_INSIDE_SPLITTER_AREA_WHEEL || this.MouseEventLast()==MOUSE_EVENT_NO_EVENT) { //--- 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(); 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_SPLITTER_AREA_NOT_PRESSED: case MOUSE_FORM_STATE_INSIDE_SPLITTER_AREA_PRESSED : case MOUSE_FORM_STATE_INSIDE_SPLITTER_AREA_WHEEL : break; //--- MOUSE_EVENT_NO_EVENT default: break; } } //+------------------------------------------------------------------+
最後のイベントがオブジェクト領域からマウスカーソルが取り除かれた場合、区切りオブジェクトへのポインタを取得し、非表示フラグを設定し、区切りを非表示にして、現在のマウス状態を前のイベントに設定します。
これで、SplitContainerコントロールからマウスを離すと、その区切りオブジェクトが非表示になります。後で、マウスカーソルを区切り領域から移動させる処理を追加して、SplitContainerコントロールのパネルオブジェクトでおこなわないようにします。
グラフィック要素コレクションクラスの \MQL5\Include\DoEasy\Collections\GraphElementsCollection.mqhファイルで、FormPostProcessing()メソッドに、表示しないオブジェクトもハンドラで処理しないようにするためのオブジェクト非表示フラグの確認を追加します。
//+------------------------------------------------------------------+ //| Post-processing of the former active form under the cursor | //+------------------------------------------------------------------+ void CGraphElementsCollection::FormPostProcessing(CForm *form,const int id, const long &lparam, const double &dparam, const string &sparam) { //--- Get the main object the form is attached to CForm *main=form.GetMain(); if(main==NULL) main=form; //--- Get all the elements attached to the form CArrayObj *list=main.GetListElements(); if(list==NULL) return; //--- In the loop by the list of received elements int total=list.Total(); for(int i=0;i<total;i++) { //--- get the pointer to the object CForm *obj=list.At(i); //--- if failed to get the pointer, move on to the next one in the list if(obj==NULL || !obj.IsVisible() || !obj.Enabled() || !obj.Displayed()) continue; obj.OnMouseEventPostProcessing(); //--- Create the list of interaction objects and get their number int count=obj.CreateListInteractObj(); //--- In the loop by the obtained list for(int j=0;j<count;j++) { //--- get the next object CWinFormBase *elm=obj.GetInteractForm(j); if(elm==NULL || !elm.IsVisible() || !elm.Enabled() || !elm.Displayed()) continue; if(elm.TypeGraphElement()==GRAPH_ELEMENT_TYPE_WF_TAB_CONTROL) { CTabControl *tab_ctrl=elm; CForm *selected=tab_ctrl.SelectedTabPage(); if(selected!=NULL) elm=selected; } //--- determine the location of the cursor relative to the object //--- and call the mouse event handling method for the object elm.MouseFormState(id,lparam,dparam,sparam); elm.OnMouseEventPostProcessing(); } } ::ChartRedraw(main.ChartID()); } //+------------------------------------------------------------------+
改善は今のところこれですべてです。結果を確認してみましょう。
検証
テストを実行するには、前の記事のEAを\MQL5\Experts\TestDoEasy\Part122\でTestDoEasy122.mq5として保存します。
EAでは、TabControlが構築されたパネルを作成します。最初の5つのタブにそれぞれSplitContainerコントロールを作成しましょう。区切りは、偶数インデックスでは垂直、奇数インデックスでは水平になります。3番目のタブで、区切りを固定にし、4番目と5番目のタブで、それぞれ、パネル2とパネル1を折りたたみます。
OnInit() EAハンドラは、次のようになります。
//+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- Set EA global variables ArrayResize(array_clr,2); // Array of gradient filling colors array_clr[0]=C'26,100,128'; // Original ≈Dark-azure color array_clr[1]=C'35,133,169'; // Lightened original color //--- Create the array with the current symbol and set it to be used in the library string array[1]={Symbol()}; engine.SetUsedSymbols(array); //--- Create the timeseries object for the current symbol and period, and show its description in the journal engine.SeriesCreate(Symbol(),Period()); engine.GetTimeSeriesCollection().PrintShort(false); // Short descriptions //--- Create the required number of WinForms Panel objects CPanel *pnl=NULL; for(int i=0;i<1;i++) { pnl=engine.CreateWFPanel("WinForms Panel"+(string)i,(i==0 ? 50 : 70),(i==0 ? 50 : 70),410,200,array_clr,200,true,true,false,-1,FRAME_STYLE_BEVEL,true,false); if(pnl!=NULL) { pnl.Hide(); Print(DFUN,"Panel description: ",pnl.Description(),", Type and name: ",pnl.TypeElementDescription()," ",pnl.Name()); //--- Set Padding to 4 pnl.SetPaddingAll(3); //--- Set the flags of relocation, auto resizing and auto changing mode from the inputs pnl.SetMovable(InpMovable); pnl.SetAutoSize(InpAutoSize,false); pnl.SetAutoSizeMode((ENUM_CANV_ELEMENT_AUTO_SIZE_MODE)InpAutoSizeMode,false); //--- Create TabControl pnl.CreateNewElement(GRAPH_ELEMENT_TYPE_WF_TAB_CONTROL,InpTabControlX,InpTabControlY,pnl.Width()-30,pnl.Height()-40,clrNONE,255,true,false); CTabControl *tc=pnl.GetElementByType(GRAPH_ELEMENT_TYPE_WF_TAB_CONTROL,0); if(tc!=NULL) { tc.SetTabSizeMode((ENUM_CANV_ELEMENT_TAB_SIZE_MODE)InpTabPageSizeMode); tc.SetAlignment((ENUM_CANV_ELEMENT_ALIGNMENT)InpHeaderAlignment); tc.SetMultiline(InpTabCtrlMultiline); tc.SetHeaderPadding(6,0); tc.CreateTabPages(15,0,56,20,TextByLanguage("Вкладка","TabPage")); //--- Create a text label with a tab description on each tab for(int j=0;j<tc.TabPages();j++) { tc.CreateNewElement(j,GRAPH_ELEMENT_TYPE_WF_LABEL,322,120,80,20,clrDodgerBlue,255,true,false); CLabel *label=tc.GetTabElement(j,0); if(label==NULL) continue; //--- If this is the very first tab, then there will be no text label.SetText(j<5 ? "" : "TabPage"+string(j+1)); } for(int n=0;n<5;n++) { //--- Create a SplitContainer control on each tab tc.CreateNewElement(n,GRAPH_ELEMENT_TYPE_WF_SPLIT_CONTAINER,10,10,tc.Width()-22,tc.GetTabField(0).Height()-22,clrNONE,255,true,false); //--- Get the SplitContainer control from each tab CSplitContainer *split_container=tc.GetTabElementByType(n,GRAPH_ELEMENT_TYPE_WF_SPLIT_CONTAINER,0); if(split_container!=NULL) { //--- The separator will be vertical for each even tab and horizontal for each odd one split_container.SetSplitterOrientation(n%2==0 ? CANV_ELEMENT_SPLITTER_ORIENTATION_VERTICAL : CANV_ELEMENT_SPLITTER_ORIENTATION_HORISONTAL,true); //--- The separator distance on each tab will be 50 pixels split_container.SetSplitterDistance(50,true); //--- The width of the separator on each subsequent tab will increase by 2 pixels split_container.SetSplitterWidth(4+2*n,false); //--- Make a fixed separator for the tab with index 2, and a movable one for the rest split_container.SetSplitterFixed(n==2 ? true : false); //--- For a tab with index 3, the second panel will be in a collapsed state (only the first one is visible) if(n==3) split_container.SetPanel2Collapsed(true); //--- For a tab with index 4, the first panel will be in a collapsed state (only the second one is visible) if(n==4) split_container.SetPanel1Collapsed(true); //--- On each of the control panels... for(int j=0;j<2;j++) { CSplitContainerPanel *panel=split_container.GetPanel(j); if(panel==NULL) continue; //--- ...create a text label with the panel name if(split_container.CreateNewElement(j,GRAPH_ELEMENT_TYPE_WF_LABEL,4,4,panel.Width()-8,panel.Height()-8,clrDodgerBlue,255,true,false)) { CLabel *label=split_container.GetPanelElementByType(j,GRAPH_ELEMENT_TYPE_WF_LABEL,0); if(label==NULL) continue; label.SetTextAlign(ANCHOR_CENTER); label.SetText(TextByLanguage("Панель","Panel")+string(j+1)); } } } } } } } //--- Display and redraw all created panels for(int i=0;i<1;i++) { pnl=engine.GetWFPanelByName("Panel"+(string)i); if(pnl!=NULL) { pnl.Show(); pnl.Redraw(true); } } //--- return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+
ハンドラのロジックは、コード中に完全にコメントされています。
SplitContainerコントロールを構築するためのタブの数によるループで、各タブに新しいオブジェクトを作成します。作成後、そのポインタを取得し、新しいパラメータを設定します。
EAをコンパイルし、チャート上で起動します。
ご覧のように、オブジェクトの作成後に設定された新しいプロパティはすべて、その外観を正しく変更します。
欠点は、マウスカーソルを離した後に区切りオブジェクトを隠すというファジーなトリガーが見受けられられることですが、MS Visual Studioの動作ロジックにより近いものにするために、動作や表示ロジックを変更することにします。これによって、この問題を掘り下げることができるようになります。
次の段階
次回は、SplitContainerコントロールの続きです。
連載のこれまでの記事
DoEasy - コントロール(第13部):WinFormsオブジェクトとマウスの相互作用を最適化し、TabControlWinFormsオブジェクトの開発を開始
DoEasy - コントロール(第14部):グラフィック要素に名前を付けるための新しいアルゴリズム。TabControlWinFormsオブジェクトの継続作業
DoEasy - コントロール(第15部):TabControlWinFormsオブジェクト—複数行のタブヘッダー、タブ処理メソッド
DoEasy - コントロール(第16部):TabControlWinFormsオブジェクト—複数行のタブヘッダー、コンテナに合わせてヘッダーをストレッチ
DoEasy - コントロール(第17部):非表示のオブジェクト部分のトリミング、補助矢印ボタンのWinFormsオブジェクト
DoEasy - コントロール(第18部):TabControlでタブをスクロールする機能
DoEasy - コントロール(第19部):TabControl、WinFormsオブジェクトイベントでのタブのスクロール
DoEasy - コントロール(第20部):SplitContainerWinFormsオブジェクト
DoEasy - コントロール(第21部):SplitContainerコントロール。パネル区切り
MetaQuotes Ltdによってロシア語から翻訳されました。
元の記事: https://www.mql5.com/ru/articles/11601
![マウンテンチャートとアイスバーグチャート](https://c.mql5.com/2/48/UI_CCanvas.png)
![Frames Analyzerツールによるタイムトレード間隔の魔法](https://c.mql5.com/2/50/Frames_Analyzer_avatar.png)
![DoEasy - コントロール(第23部):TabControlおよびSplitContainer WinFormsオブジェクトの改善](https://c.mql5.com/2/50/MQL5-avatar-doeasy-library-2.png)
![DoEasy-コントロール(第21部):SplitContainerコントロール。パネルセパレータ](https://c.mql5.com/2/49/MQL5-avatar-doeasy-library-2__3.png)
![MQL5 - MetaTrader 5クライアントターミナルにビルトインされたトレードストラテジーの言語](https://c.mql5.com/i/registerlandings/logo-2.png)
- 無料取引アプリ
- 8千を超えるシグナルをコピー
- 金融ニュースで金融マーケットを探索