DoEasy - コントロール(第33部):垂直スクロールバー
内容
概念
前回、ライブラリのグラフィカル要素について説明しましたが、フォームに接続されたオブジェクトが親フォームの左端、右端、または両端からはみ出した場合にオブジェクトに表示される水平スクロールバーを作成しました。ここでは、水平スクロールバーオブジェクトをベースに垂直スクロールバーを作成します。オブジェクトがフォームの上、下、または両側からはみ出した場合、フォームに表示されます。
横スクロールバーオブジェクトのコピーを作り、それを縦にするのはとても簡単な作業なので、この記事は比較的短く、どちらかというと概要に近くなります。このスクロールバーは、後でWindowsフォームスタイルで後続のコントロールを開発するときに必要になります。縦スクロールバーは、基本的にはかなり前に開発されたものですが、小さなエラーというか、脱落のために記事の公開が遅れ、グラフィック要素との相互作用時に非常に不快なアーティファクトが発生し、それが目に見えないオブジェクト部分の絶え間ない「点滅」という形で現れました。これは、オブジェクトの更新が制御されずに早まってしまい、その後、オブジェクトが親のサイズにトリミングされてしまったことが原因でした。この「点滅」は次のようにして現れました。まず、オブジェクトは完全にレンダリングされ、チャート上に表示されました。その後、親オブジェクトのフォームのサイズに合わせてトリミングしました。このような状況では通常そうであるように、解決策は単純であることが判明しました。再描画によって早期更新を削除することです。しかし、再描画がおこなわれた場所を見つけるのに多くの時間を要しました。現在、このバグは発見され修正されているので、安心してライブラリの開発を続けることができます。
ライブラリクラスの改善
まずは、その後のライブラリ改良で必要となる便利な関数やメソッドを追加します。
ある出来事が起こったバーの開始時間を知りたいことがあります。ローソク足が開いた瞬間にイベントが発生したのであれば、時刻の特定に問題はありませんが、ローソク足の始値と終値の間にイベントが発生した場合、このイベントの発生時刻を使って、指定したチャート期間におけるローソク足の始値を計算することができます。もちろん、標準的な関数を使用して、イベント時間をバーインデックスに変換し、最終的にバーインデックスを使用して、必要なチャート期間で目的のローソク足の開始時間を取得することができます...しかし、これにはCPUの時間がかかる。もし実行速度が重要であれば、現実のローソク足の中で発生したイベントであれば、やはり計算を使った方が良くなります。
こちらのロシア語フォーラムには有用なスレッドがあり、リソースユーザーがこの種の興味深いコードを共有しています。提案されたアルゴリズムを利用して、ライブラリ関数を書いてみましょう。
\MQL5\Include\DoEasy\Services\DELib.mqhライブラリファイルの最後に次の関数を実装します。
//+---------------------------------------------------------------------------------+ //| Get the opening time of the virtual bar based on input time and | //| timeframe, regardless of the existence of a real bar. | //| It counts correctly only till 28.02.2100 | //| It is not a replacement for iBarShift!!! It does not depend on the bar history. | //| https://www.mql5.com/ru/forum/170952/page234#comment_50523898 | //+---------------------------------------------------------------------------------+ datetime GetStartTimeOfBarFast(const ENUM_TIMEFRAMES timeframe, const datetime time) { ENUM_TIMEFRAMES tf=(timeframe==PERIOD_CURRENT ? _Period : timeframe); int ts=0; if(tf<PERIOD_MN1) { ushort i_tf=ushort(tf); uchar _i=uchar(i_tf>>14); int n=i_tf & 0x0FFF; ts=(_i==0 ? n*60 : _i==1 ? n*60*60 : 60*60*24*7); } if(tf<PERIOD_W1) return time-time % ts; if(tf==PERIOD_W1) return time-(time+4*24*60*60) % ts; else // Period MN1 { static int dm[12] = {0,31,61,92,122,153,184, 214, 245, 275, 306, 337}; static int last_days = 0; static datetime last_result = 0; int days = int(time/(24*60*60)); if(last_days!=days) { last_days = days; int d1 = (days+306+365)%1461; int y = d1/365; datetime t1 = time - time % (24*60*60) - d1*24*60*60; int m = 0; if(d1==1460) { m=11; y--; }; int d = d1-y*365+1; if(d!=31) if(d==276) m = 9; else m = int(d/30.68); if(m<0 || m>11) return WRONG_VALUE; last_result = t1+y*365*24*60*60+dm[m]*24*60*60; } return last_result; } } //+------------------------------------------------------------------+
アルゴリズム分析については、上記リンク先のフォーラムを参照してください。その後、この関数を使用することで、バーの開始時間を常に見つけることができます。同時に、計算速度が重要視される場面で、十分に高速でない関数に頼る必要もなくなります。
グラフィカルオブジェクトを扱う際、状況に応じてグラフィカルオブジェクトの色を変える必要がある場合があります。もちろん、標準色のリストにある色を使用することもできますが、それだけでは十分でないことが多いです。例えば、あるオブジェクト、例えば中間色のグレーがあるとします。状況によっては、少しトーンを変えることもあります。ある場合はやや赤みがかった色に、またある場合はやや緑がかった色に変更します。つまり、この場合、標準セットから色を適用するのではなく、色の1つまたは別の成分の彩度をわずかに加えるだけでよいのです。
そのためには、\MQL5\Include\DoEasy\Objects\Graph\GCnvElement.mqhグラフィカル要素オブジェクトファイルで次のメソッドを宣言します。
//--- Change the lightness of (1) ARGB and (2) COLOR by a specified amount uint ChangeColorLightness(const uint clr,const double change_value); color ChangeColorLightness(const color colour,const double change_value); //--- Change the saturation of (1) ARGB and (2) COLOR by a specified amount uint ChangeColorSaturation(const uint clr,const double change_value); color ChangeColorSaturation(const color colour,const double change_value); //--- Changes the color component of RGB-Color color ChangeRGBComponents(color clr,const uchar R,const uchar G,const uchar B);
クラス本体の外側で実装しましょう。
//+------------------------------------------------------------------+ //| Change the color component of RGB-Color | //+------------------------------------------------------------------+ color CGCnvElement::ChangeRGBComponents(color clr,const uchar R,const uchar G,const uchar B) { double r=CColors::GetR(clr)+R; if(r>255) r=255; double g=CColors::GetG(clr)+G; if(g>255) g=255; double b=CColors::GetB(clr)+B; if(b>255) b=255; return CColors::RGBToColor(r,g,b); } //+------------------------------------------------------------------+ //| Save the image to the array | //+------------------------------------------------------------------+
すべてが簡単です。メソッドに渡された色の構成要素のうち、変更が必要なものをそれぞれ取得し、メソッドに渡された対応する値を結果の構成要素の値に追加します。いずれかの値が255より大きい場合は、255に調整します。その結果、CColorライブラリクラスのRGBToColorメソッドを使用して、計算された新しいコンポーネントで構成される色を返します。
同じファイルに、グラフィック要素の可視領域の座標と寸法を設定するメソッドがあるります。
//--- Set relative coordinates and size of the visible area void SetVisibleArea(const int x,const int y,const int w,const int h) { this.SetVisibleAreaX(x,false); this.SetVisibleAreaY(y,false); this.SetVisibleAreaWidth(w,false); this.SetVisibleAreaHeight(h,false); }
さらに、グラフィカル要素オブジェクトのプロパティのみ、またはプロパティと物理オブジェクトの両方において、可視性のスコープをどのように設定するかを個別に指定できる機能を追加しましょう。これをおこなうには、単純に別の入力変数を追加し、それに対応して、スコープをオブジェクトサイズ全体に設定するメソッド呼び出しを修正します。
//--- Set relative coordinates and size of the visible area void SetVisibleArea(const int x,const int y,const int w,const int h,const bool only_prop) { this.SetVisibleAreaX(x,only_prop); this.SetVisibleAreaY(y,only_prop); this.SetVisibleAreaWidth(w,only_prop); this.SetVisibleAreaHeight(h,only_prop); } //--- Sets the size of the visible area equal to the entire object void ResetVisibleArea(void) { this.SetVisibleArea(0,0,this.Width(),this.Height(),false); }
実装では、要素をクリアして色と不透明度で塗りつぶし、トリミングはせずにフラグでチャートを更新するため、オブジェクトの更新ロジックを少し変更する必要があります。以前は、チャートの再描画フラグが設定されているかどうかにかかわらず、オブジェクトは常にここで更新されていました。
//+------------------------------------------------------------------+ //| Clear the element filling it with color and opacity | //| without cropping and with the chart update by flag | //+------------------------------------------------------------------+ void CGCnvElement::EraseNoCrop(const color colour,const uchar opacity,const bool redraw=false) { color arr[1]; arr[0]=colour; this.SaveColorsBG(arr); this.m_canvas.Erase(::ColorToARGB(colour,opacity)); this.Update(redraw); }
ここでチャート再描画フラグを持つUpdate()メソッドは、常に、EraseNoCrop()メソッドのパラメータで指定された色でオブジェクトが完全に塗り替えられた後に更新されます。したがって、再描画フラグに関係なく、オブジェクトは常に更新されました(適用された変更が表示された)。再描画フラグは変更表示時間にのみ影響し、即時(フラグがtrueに設定されている場合)か、ティック到着時またはチャート更新時(フラグがfalseに設定されている場合)のどちらかです。このメソッドはオブジェクト全体を完全に再配色するので、チャート上にいつでもフルサイズで表示することができます。このオブジェクトが親オブジェクトのサイズにトリミングされるべきであった場合、不可視部分のトリミングは常にこのメソッドを呼び出した後に実行されます。これが、この再描画はオブジェクトの不可視部分に不快な「点滅」を引き起こしました。
すべて解決しました。オブジェクトの見えない部分の点滅はなくなりました。
//+------------------------------------------------------------------+ //| Clear the element filling it with color and opacity | //| without cropping and with the chart update by flag | //+------------------------------------------------------------------+ void CGCnvElement::EraseNoCrop(const color colour,const uchar opacity,const bool redraw=false) { color arr[1]; arr[0]=colour; this.SaveColorsBG(arr); this.m_canvas.Erase(::ColorToARGB(colour,opacity)); if(redraw) this.Update(redraw); }
ここでは、再描画フラグがセットされたときのみオブジェクトが更新されます。オブジェクトが切り取られる必要が絶対にない場合、フラグをセットしたメソッドを呼び出すと、その新しい外観が直ちにチャートに表示されます。オブジェクトをトリミングする必要がある場合は、まずフラグがクリアされた状態でこのメソッドが呼び出され、次にCrop()メソッドが呼び出され、隠れた部分がトリミングされ、フラグに基づいて再描画されたチャートでオブジェクトの外観が更新されます。ライブラリのグラフィカル要素のさらなる発展を妨げたのは、この論理的誤りでした。このエラーは解消されました。
次に、\MQL5\Include\DoEasy\Objects\Graph\WForms\Helpers\BarProgressBar.mqhのタイマーハンドラーで、必要なフラグを指定して SetVisibleArea()メソッドの呼び出しを修正します。
//--- ... //--- ... //--- If the object is in the normal state (hidden) if(glare.DisplayState()==CANV_ELEMENT_DISPLAY_STATE_NORMAL) { //--- set the state of waiting for fading in to the object (in our case, waiting for a shift along the progress bar), //--- set the waiting duration and set the countdown time glare.SetDisplayState(CANV_ELEMENT_DISPLAY_STATE_WAITING_FADE_IN); this.m_pause.SetWaitingMSC(this.ShowDelay()); this.m_pause.SetTimeBegin(); //--- If the right edge of the glare object is to the right of the left edge of the progress bar object if(glare.RightEdge()>=this.CoordX()) { //--- Hide the glare object and move it beyond the right edge of the progress bar glare.Hide(); if(glare.Move(this.CoordX()-glare.Width(),this.CoordY())) { //--- Set the relative coordinates of the glare object glare.SetCoordXRelative(glare.CoordX()-this.CoordX()); glare.SetCoordYRelative(glare.CoordY()-this.CoordY()); //--- and its visibility scope equal to the entire object glare.SetVisibleArea(0,0,glare.Width(),glare.Height(),false); } } return; } //--- ... //--- ...
ほとんどすべてのライブラリオブジェクトには、グラフィカルオブジェクトフォームを動的に作成できるグラフィカルコントロールオブジェクトが含まれています。標準的なグラフィカルオブジェクトを作成するためのメソッドを追加しましょう。グラフィカルオブジェクト管理オブジェクトクラス \MQL5\Include\DoEasy\Objects\Graph\GraphElmControl.mqhのpublicセクションで、トレンドラインと矢印を描画する新しいメソッドを宣言します。
public: //--- Return itself CGraphElmControl *GetObject(void) { return &this; } //--- Set a type of the object the graphics is constructed for void SetTypeNode(const int type_node) { this.m_type_node=type_node; } //--- Create a form object CForm *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 *CreateForm(const int form_id,const int wnd,const string name,const int x,const int y,const int w,const int h); CForm *CreateForm(const int form_id,const string name,const int x,const int y,const int w,const int h); //--- Creates the trend line standard graphical object bool CreateTrendLine(const long chart_id,const string name,const int subwindow, const datetime time1,const double price1, const datetime time2,const double price2, color clr,int width=1,ENUM_LINE_STYLE style=STYLE_SOLID); bool CreateTrendLine(const string name,const int subwindow, const datetime time1,const double price1, const datetime time2,const double price2, color clr,int width=1,ENUM_LINE_STYLE style=STYLE_SOLID); bool CreateTrendLine(const string name, const datetime time1,const double price1, const datetime time2,const double price2, color clr,int width=1,ENUM_LINE_STYLE style=STYLE_SOLID); //--- Create the arrow standard graphical object bool CreateArrow(const long chart_id,const string name,const int subwindow, const datetime time1,const double price1, color clr,uchar arrow_code,int width=1); bool CreateArrow(const string name,const int subwindow, const datetime time1,const double price1, color clr,uchar arrow_code,int width=1); bool CreateArrow(const string name, const datetime time1,const double price1, color clr,uchar arrow_code,int width=1); //--- Constructors CGraphElmControl(){ this.m_type=OBJECT_DE_TYPE_GELEMENT_CONTROL; } CGraphElmControl(int type_node); };
privateセクションで、標準的なグラフィカルオブジェクトの一般的なパラメータを設定するメソッドを宣言します。
//+------------------------------------------------------------------+ //| Class for managing graphical elements | //+------------------------------------------------------------------+ class CGraphElmControl : public CObject { private: int m_type; // Object type int m_type_node; // Type of the object the graphics is constructed for //--- Set general parameters for standard graphical objects void SetCommonParamsStdGraphObj(const long chart_id,const string name); public: //--- Return itself CGraphElmControl *GetObject(void) { return &this; }
新しく作成された各オブジェクトには、デフォルトでいくつかのプロパティが割り当てられているはずです。このプロパティの意味は、作成されたすべてのグラフィカルオブジェクトについて例外なく同じです。オブジェクトはすべてのチャートオブジェクトのリストで非表示になり、選択されておらず、マウスで選択できず、すべての時間枠に表示される必要があります。これらは、SetCommonParamsStdGraphObjメソッドによって設定されるプロパティで、クラス本体の外部で実装されます。
//+------------------------------------------------------------------+ //|Set general parameters for standard graphical objects | //+------------------------------------------------------------------+ void CGraphElmControl::SetCommonParamsStdGraphObj(const long chart_id,const string name) { ::ObjectSetInteger(chart_id,name,OBJPROP_HIDDEN,true); ::ObjectSetInteger(chart_id,name,OBJPROP_SELECTED,false); ::ObjectSetInteger(chart_id,name,OBJPROP_SELECTABLE,false); ::ObjectSetInteger(chart_id,name,OBJPROP_TIMEFRAMES,OBJ_ALL_PERIODS); }
また、グラフィカルオブジェクトを生成するメソッドを、クラス本体の外に実装します。
//+------------------------------------------------------------------+ //| Create the trend line standard graphical object | //| on a specified chart in a specified subwindow | //+------------------------------------------------------------------+ bool CGraphElmControl::CreateTrendLine(const long chart_id,const string name,const int subwindow, const datetime time1,const double price1, const datetime time2,const double price2, color clr,int width=1,ENUM_LINE_STYLE style=STYLE_SOLID) { if(!CreateNewStdGraphObject(chart_id,name,OBJ_TREND,subwindow,time1,price1,time2,price2)) { ::Print(DFUN,CMessage::Text(MSG_GRAPH_STD_OBJ_ERR_FAILED_CREATE_STD_GRAPH_OBJ),": ",StdGraphObjectTypeDescription(OBJ_TREND)); return false; } this.SetCommonParamsStdGraphObj(chart_id,name); ::ObjectSetInteger(chart_id,name,OBJPROP_COLOR,clr); ::ObjectSetInteger(chart_id,name,OBJPROP_WIDTH,width); ::ObjectSetInteger(chart_id,name,OBJPROP_STYLE,style); return true; } //+------------------------------------------------------------------+ //| Create the trend line standard graphical object | //| on the current chart in a specified subwindow | //+------------------------------------------------------------------+ bool CGraphElmControl::CreateTrendLine(const string name,const int subwindow, const datetime time1,const double price1, const datetime time2,const double price2, color clr,int width=1,ENUM_LINE_STYLE style=STYLE_SOLID) { return this.CreateTrendLine(::ChartID(),name,subwindow,time1,price1,time2,price2,clr,width,style); } //+------------------------------------------------------------------+ //| Create the trend line standard graphical object | //| on the current chart in the main window | //+------------------------------------------------------------------+ bool CGraphElmControl::CreateTrendLine(const string name, const datetime time1,const double price1, const datetime time2,const double price2, color clr,int width=1,ENUM_LINE_STYLE style=STYLE_SOLID) { return this.CreateTrendLine(::ChartID(),name,0,time1,price1,time2,price2,clr,width,style); } //+------------------------------------------------------------------+ //| Create the arrow standard graphical object | //| on a specified chart in a specified subwindow | //+------------------------------------------------------------------+ bool CGraphElmControl::CreateArrow(const long chart_id,const string name,const int subwindow, const datetime time1,const double price1, color clr,uchar arrow_code,int width=1) { if(!CreateNewStdGraphObject(chart_id,name,OBJ_ARROW,subwindow,time1,price1)) { ::Print(DFUN,CMessage::Text(MSG_GRAPH_STD_OBJ_ERR_FAILED_CREATE_STD_GRAPH_OBJ),": ",StdGraphObjectTypeDescription(OBJ_ARROW)); return false; } this.SetCommonParamsStdGraphObj(chart_id,name); ::ObjectSetInteger(chart_id,name,OBJPROP_COLOR,clr); ::ObjectSetInteger(chart_id,name,OBJPROP_WIDTH,width); ::ObjectSetInteger(chart_id,name,OBJPROP_ARROWCODE,arrow_code); return true; } //+------------------------------------------------------------------+ //| Create the arrow standard graphical object | //| on the current chart in a specified subwindow | //+------------------------------------------------------------------+ bool CGraphElmControl::CreateArrow(const string name,const int subwindow, const datetime time1,const double price1, color clr,uchar arrow_code,int width=1) { return this.CreateArrow(::ChartID(),name,subwindow,time1,price1,clr,arrow_code,width); } //+------------------------------------------------------------------+ //| Create the arrow standard graphical object | //| on the current chart in the main window | //+------------------------------------------------------------------+ bool CGraphElmControl::CreateArrow(const string name, const datetime time1,const double price1, color clr,uchar arrow_code,int width=1) { return this.CreateArrow(::ChartID(),name,0,time1,price1,clr,arrow_code,width); }
グラフィカルオブジェクト管理クラスのオブジェクトのインスタンスは、CBaseObjライブラリ基本オブジェクトクラスから継承された各ライブラリオブジェクトに含まれます。グラフィカルオブジェクト管理オブジェクトは、このようなオブジェクトを作成するメソッドを持っています。オブジェクトクラスからグラフィカルオブジェクトを作成するためには、基となるオブジェクトクラスにグラフィカルオブジェクトを作成するメソッドを記述する必要があります。これにより、アプリケーションにおけるグラフィックの開発が簡素化されます。基本的には、まず目的のオブジェクトへのポインタを取得し、次にそのグラフィカルオブジェクト管理オブジェクトへのポインタを取得し、そのメソッドにアクセスしてグラフィカルオブジェクトを作成します。ただし、それは長い道のりです。オブジェクトへのポインタを取得し、そのメソッドを使ってグラフィカルオブジェクトを作成し、その中で上記のチェーン全体を実行する方が簡単で便利で速くなります。
゙\MQL5\Include\DoEasy\Objects\BaseObj.mqhライブラリベースオブジェクトファイルのpublicセクションのグラフィカルオブジェクトを扱うセクションで、トレンドラインと矢印オブジェク トを作成するための新しいメソッドを宣言します。
//+------------------------------------------------------------------+ //| Methods for handling graphical elements | //+------------------------------------------------------------------+ //--- Create a form object on a specified chart in a specified subwindow CForm *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) { return this.m_graph_elm.CreateForm(form_id,chart_id,wnd,name,x,y,w,h); } //--- Create a form object on the current chart in a specified subwindow CForm *CreateForm(const int form_id,const int wnd,const string name,const int x,const int y,const int w,const int h) { return this.m_graph_elm.CreateForm(form_id,wnd,name,x,y,w,h); } //--- Create the form object on the current chart in the main window CForm *CreateForm(const int form_id,const string name,const int x,const int y,const int w,const int h) { return this.m_graph_elm.CreateForm(form_id,name,x,y,w,h); } //--- Create a standard graphical trend line object in the specified subwindow of the specified chart bool CreateTrendLine(const long chart_id,const string name,const int subwindow, const datetime time1,const double price1, const datetime time2,const double price2, color clr,int width=1,ENUM_LINE_STYLE style=STYLE_SOLID) { return this.m_graph_elm.CreateTrendLine(chart_id,name,subwindow,time1,price1,time2,price2,clr,width,style); } //--- Create a standard graphical trend line object in the specified subwindow of the current chart bool CreateTrendLine(const string name,const int subwindow, const datetime time1,const double price1, const datetime time2,const double price2, color clr,int width=1,ENUM_LINE_STYLE style=STYLE_SOLID) { return this.m_graph_elm.CreateTrendLine(::ChartID(),name,subwindow,time1,price1,time2,price2,clr,width,style);} //--- Create a standard graphical trend line object in the main window of the current chart bool CreateTrendLine(const string name, const datetime time1,const double price1, const datetime time2,const double price2, color clr,int width=1,ENUM_LINE_STYLE style=STYLE_SOLID) { return this.m_graph_elm.CreateTrendLine(::ChartID(),name,0,time1,price1,time2,price2,clr,width,style); } //--- Create a standard arrow graphical object in the specified subwindow of the specified chart bool CreateArrow(const long chart_id,const string name,const int subwindow, const datetime time1,const double price1, color clr,uchar arrow_code,int width=1) { return this.m_graph_elm.CreateArrow(chart_id,name,subwindow,time1,price1,clr,arrow_code,width); } //--- Create a standard arrow graphical object in the specified subwindow of the current chart bool CreateArrow(const string name,const int subwindow, const datetime time1,const double price1, color clr,uchar arrow_code,int width=1) { return this.m_graph_elm.CreateArrow(::ChartID(),name,subwindow,time1,price1,clr,arrow_code,width); } //--- Create a standard arrow graphical object in the main window of the current chart bool CreateArrow(const string name, const datetime time1,const double price1, color clr,uchar arrow_code,int width=1) { return this.m_graph_elm.CreateArrow(::ChartID(),name,0,time1,price1,clr,arrow_code,width); } //--- Constructor
グラフィックスコントロールオブジェクトの適切なメソッドは、メソッドの中で呼び出されるだけです。将来的には、他の標準的なグラフィカルオブジェクトを作成するメソッドを追加する予定です。当面は、これらのグラフィカルオブジェクトで十分でしょう。
垂直スクロールバーオブジェクトを作り始めましょう。
スクロールバーグラブエリアとは、マウスで掴んでスクロールバー内で動かすことのできるスライダーのことで、それによってコントロールされるエリアを動かすことができます。カーソルがスクロールバー内にある状態で、マウスホイールをある方向にスクロールさせると、対応するスクロールコントロールボタン(スクロールバーの端にある矢印ボタン)にクリックイベントが発生します。水平スクロールバーのためにマウスホイールがスクロールされたとき、右矢印と左矢印ボタンをクリックしたときのイベントはすでに生成されています。次に、垂直スクロールバースライダーの上下矢印ボタンをクリックするイベント生成を追加する必要があります。
\MQL5\Include\DoEasy\Objects\Graph\WForms\Helpers\ScrollBarThumb.mqhファイル、つまり「カーソルがアクティブ領域内にあり、マウス ホイールがスクロールされている」イベント ハンドラで、次の変更をおこないます。
//+------------------------------------------------------------------+ //| 'The cursor is inside the active area, | //| the mouse wheel is being scrolled | //+------------------------------------------------------------------+ void CScrollBarThumb::MouseActiveAreaWhellHandler(const int id,const long& lparam,const double& dparam,const string& sparam) { CWinFormBase *base=this.GetBase(); if(base==NULL) return; base.BringToTop(); ENUM_WF_CONTROL_EVENT evn=WF_CONTROL_EVENT_NO_EVENT; switch(base.TypeGraphElement()) { case GRAPH_ELEMENT_TYPE_WF_SCROLL_BAR_HORISONTAL: evn=(dparam>0 ? WF_CONTROL_EVENT_CLICK_SCROLL_LEFT : dparam<0 ? WF_CONTROL_EVENT_CLICK_SCROLL_RIGHT : WF_CONTROL_EVENT_NO_EVENT); break; case GRAPH_ELEMENT_TYPE_WF_SCROLL_BAR_VERTICAL : evn=(dparam>0 ? WF_CONTROL_EVENT_CLICK_SCROLL_UP : dparam<0 ? WF_CONTROL_EVENT_CLICK_SCROLL_DOWN : WF_CONTROL_EVENT_NO_EVENT); break; default : break; } base.OnChartEvent(evn,lparam,dparam,sparam); ::ChartRedraw(base.ChartID()); }
どのオブジェクトがキャプチャエリアの基本オブジェクト(水平または垂直スクロールバー)であるかによって、基本オブジェクトのイベントハンドラが呼び出され、対応するイベントコードが渡されます。左矢印ボタンまたは右矢印ボタンのマウスクリックか、上矢印ボタンまたは下矢印ボタンのマウスクリックです。
縦スクロールバーオブジェクトを作成するには、横スクロールバーオブジェクトクラスファイル\MQL5\Include\DoEasy\Objects\Graph\WForms\Helpers\ScrollBarHorisontal.mqhを \MQL5\Include\DoEasy\Objects\Graph\WForms\Helpers\ScrollBarVertical.mqh として保存します。新しいクラスは、同じクラスに基づいて作成されるので、いくつかの計算を置き換えるだけで大丈夫です(計算の「left/right」の代わりに「top/bottom」を使用するなど)。すべての変更点を説明する意味はありません。このようなオブジェクトの作成については、適切な記事をお読みください。ここでは、すでに修正されたクラスファイル全体を見てみましょう。
//+------------------------------------------------------------------+ //| ScrollBarVertical.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 "ScrollBarThumb.mqh" #include "ArrowDownButton.mqh" #include "ArrowUpButton.mqh" #include "ScrollBar.mqh" //+------------------------------------------------------------------+ //| CScrollBarVertical object class of WForms controls | //+------------------------------------------------------------------+ class CScrollBarVertical : public CScrollBar { private: //--- Create the ArrowButton objects virtual void CreateArrowButtons(const int width,const int height); //--- Calculate the distance of the capture area (slider) int CalculateThumbAreaDistance(const int thumb_size); protected: //--- Protected constructor with object type, chart ID and subwindow CScrollBarVertical(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); //--- 'The cursor is inside the active area, the mouse wheel is being scrolled' event handler virtual void MouseActiveAreaWhellHandler(const int id,const long& lparam,const double& dparam,const string& sparam); public: //--- Supported object properties (1) integer, (2) real and (3) string ones virtual bool SupportProperty(ENUM_CANV_ELEMENT_PROP_INTEGER property) { return true; } virtual bool SupportProperty(ENUM_CANV_ELEMENT_PROP_DOUBLE property) { return true; } virtual bool SupportProperty(ENUM_CANV_ELEMENT_PROP_STRING property) { return true; } //--- Return the button with the (1) up, (2) down arrow CArrowUpButton *GetArrowButtonUp(void) { return this.GetElementByType(GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_UP,0); } CArrowDownButton *GetArrowButtonDown(void) { return this.GetElementByType(GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_DOWN,0); } //--- Return the size of the slider working area int BarWorkAreaSize(void); //--- Return the coordinate of the beginning of the slider working area int BarWorkAreaCoord(void); //--- Set the new size virtual bool Resize(const int w,const int h,const bool redraw); //--- Calculate and set the parameters of the capture area (slider) int SetThumbParams(void); //--- Constructor CScrollBarVertical(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); //--- Timer virtual void OnTimer(void); //--- Event handler virtual void OnChartEvent(const int id,const long& lparam,const double& dparam,const string& sparam); }; //+------------------------------------------------------------------+ //| Protected constructor with an object type, | //| chart ID and subwindow | //+------------------------------------------------------------------+ CScrollBarVertical::CScrollBarVertical(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) : CScrollBar(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.CreateThumbArea(); } //+------------------------------------------------------------------+ //| Constructor indicating the main and base objects, | //| chart ID and subwindow | //+------------------------------------------------------------------+ CScrollBarVertical::CScrollBarVertical(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) : CScrollBar(GRAPH_ELEMENT_TYPE_WF_SCROLL_BAR_VERTICAL,main_obj,base_obj,chart_id,subwindow,descript,x,y,w,h) { this.SetTypeElement(GRAPH_ELEMENT_TYPE_WF_SCROLL_BAR_VERTICAL); this.CreateThumbArea(); } //+------------------------------------------------------------------+ //| Create the ArrowButton objects | //+------------------------------------------------------------------+ void CScrollBarVertical::CreateArrowButtons(const int width,const int height) { //--- Set the size of the buttons equal to the width of the scrollbar without the size of its frame int size=this.Thickness()-this.BorderSizeLeft()-this.BorderSizeRight(); //--- Create the buttons with up and down arrows and the area capture object. The arrow size is set to 2 this.CreateNewElement(GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_UP, 0,0,size,size,this.BackgroundColor(),255,true,false); this.CreateNewElement(GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_DOWN,0,this.Height()-height,size,size,this.BackgroundColor(),255,true,false); this.CreateNewElement(GRAPH_ELEMENT_TYPE_WF_SCROLL_BAR_THUMB,0,this.Height()/2-height,size,30,CLR_DEF_CONTROL_SCROLL_BAR_THUMB_COLOR,255,true,false); this.SetArrowSize(2); //--- Get the pointer to the up arrow button and set the colors of its various states for it CArrowUpButton *bu=this.GetArrowButtonUp(); if(bu!=NULL) { bu.SetBackgroundColor(CLR_DEF_CONTROL_SCROLL_BAR_BUTT_COLOR,true); bu.SetBackgroundColorMouseDown(CLR_DEF_CONTROL_SCROLL_BAR_BUTT_MOUSE_DOWN); bu.SetBackgroundColorMouseOver(CLR_DEF_CONTROL_SCROLL_BAR_BUTT_MOUSE_OVER); bu.SetForeColor(CLR_DEF_CONTROL_SCROLL_BAR_BUTT_FORE_COLOR,true); bu.SetForeColorMouseDown(CLR_DEF_CONTROL_SCROLL_BAR_BUTT_FORE_MOUSE_DOWN); bu.SetForeColorMouseOver(CLR_DEF_CONTROL_SCROLL_BAR_BUTT_FORE_MOUSE_OVER); } //--- Get the pointer to the down arrow button and set the colors of its various states for it CArrowDownButton *bd=this.GetArrowButtonDown(); if(bd!=NULL) { bd.SetBackgroundColor(CLR_DEF_CONTROL_SCROLL_BAR_BUTT_COLOR,true); bd.SetBackgroundColorMouseDown(CLR_DEF_CONTROL_SCROLL_BAR_BUTT_MOUSE_DOWN); bd.SetBackgroundColorMouseOver(CLR_DEF_CONTROL_SCROLL_BAR_BUTT_MOUSE_OVER); bd.SetForeColor(CLR_DEF_CONTROL_SCROLL_BAR_BUTT_FORE_COLOR,true); bd.SetForeColorMouseDown(CLR_DEF_CONTROL_SCROLL_BAR_BUTT_FORE_MOUSE_DOWN); bd.SetForeColorMouseOver(CLR_DEF_CONTROL_SCROLL_BAR_BUTT_FORE_MOUSE_OVER); } //--- Get the pointer to the capture area object and set the colors of its various states for it CScrollBarThumb *th=this.GetThumb(); if(th!=NULL) { th.SetBackgroundColor(CLR_DEF_CONTROL_SCROLL_BAR_THUMB_COLOR,true); th.SetBorderColor(CLR_DEF_CONTROL_SCROLL_BAR_THUMB_BORDER_COLOR,true); th.SetBackgroundColorMouseDown(CLR_DEF_CONTROL_SCROLL_BAR_THUMB_MOUSE_DOWN); th.SetBackgroundColorMouseOver(CLR_DEF_CONTROL_SCROLL_BAR_THUMB_MOUSE_OVER); th.SetForeColor(CLR_DEF_CONTROL_SCROLL_BAR_THUMB_FORE_COLOR,true); th.SetForeColorMouseDown(CLR_DEF_CONTROL_SCROLL_BAR_THUMB_FORE_MOUSE_DOWN); th.SetForeColorMouseOver(CLR_DEF_CONTROL_SCROLL_BAR_THUMB_FORE_MOUSE_OVER); } } //+------------------------------------------------------------------+ //| Set the new size | //+------------------------------------------------------------------+ bool CScrollBarVertical::Resize(const int w,const int h,const bool redraw) { //--- If failed to change the object size, return 'false' if(!CWinFormBase::Resize(w,h,redraw)) return false; //--- Get the button object with the down arrow CArrowDownButton *bd=this.GetArrowButtonDown(); //--- If the button is not received, return 'false' if(bd==NULL) return false; //--- Move the button to the bottom edge of the scrollbar if(bd.Move(bd.CoordX(),this.BottomEdge()-this.BorderSizeBottom()-bd.Height())) { //--- Set new relative coordinates for the button bd.SetCoordXRelative(bd.CoordX()-this.CoordX()); bd.SetCoordYRelative(bd.CoordY()-this.CoordY()); } //--- Set the slider parameters this.SetThumbParams(); //--- Successful return true; } //+------------------------------------------------------------------+ //| Calculate and set the parameters of the capture area (slider) | //+------------------------------------------------------------------+ int CScrollBarVertical::SetThumbParams(void) { //--- Get the base object CWinFormBase *base=this.GetBase(); if(base==NULL) return 0; //--- Get the capture area object (slider) CScrollBarThumb *thumb=this.GetThumb(); if(thumb==NULL) return 0; //--- Get the height size of the visible part inside the container int base_h=base.HeightWorkspace(); //--- Calculate the total height of all attached objects int objs_h=base_h+base.OversizeTop()+base.OversizeBottom(); //--- Calculate the relative size of the visible part window double px=(double)base_h/double(objs_h!=0 ? objs_h : 1); //--- Calculate and adjust the size of the slider relative to the height of its workspace (not less than the minimum size) int thumb_size=(int)::floor(this.BarWorkAreaSize()*px); if(thumb_size<DEF_CONTROL_SCROLL_BAR_THUMB_SIZE_MIN) thumb_size=DEF_CONTROL_SCROLL_BAR_THUMB_SIZE_MIN; if(thumb_size>this.BarWorkAreaSize()) thumb_size=this.BarWorkAreaSize(); //--- Calculate the coordinate of the slider and change its size to match the previously calculated one int thumb_y=this.CalculateThumbAreaDistance(thumb_size); if(!thumb.Resize(thumb.Width(),thumb_size,true)) return 0; //--- Shift the slider by the calculated Y coordinate if(thumb.Move(thumb.CoordX(),this.BarWorkAreaCoord()+thumb_y)) { thumb.SetCoordXRelative(thumb.CoordX()-this.CoordX()); thumb.SetCoordYRelative(thumb.CoordY()-this.CoordY()); } //--- Return the calculated slider size return thumb_size; } //+------------------------------------------------------------------+ //| Calculate the distance of the capture area (slider) | //+------------------------------------------------------------------+ int CScrollBarVertical::CalculateThumbAreaDistance(const int thumb_size) { CWinFormBase *base=this.GetBase(); if(base==NULL) return 0; double x=(double)thumb_size/(double)base.HeightWorkspace(); return (int)::ceil((double)base.OversizeTop()*x); } //+------------------------------------------------------------------+ //| Return the size of the slider working area | //+------------------------------------------------------------------+ int CScrollBarVertical::BarWorkAreaSize(void) { CArrowUpButton *bu=this.GetArrowButtonUp(); CArrowDownButton *bd=this.GetArrowButtonDown(); int y1=(bu!=NULL ? bu.BottomEdge() : this.CoordY()+this.BorderSizeTop()); int y2=(bd!=NULL ? bd.CoordY() : this.BottomEdge()-this.BorderSizeBottom()); return(y2-y1); } //+------------------------------------------------------------------+ //| Return the coordinate of the beginning of the slider working area| //+------------------------------------------------------------------+ int CScrollBarVertical::BarWorkAreaCoord(void) { CArrowUpButton *bu=this.GetArrowButtonUp(); return(bu!=NULL ? bu.BottomEdge() : this.CoordY()+this.BorderSizeTop()); } //+------------------------------------------------------------------+ //| Timer | //+------------------------------------------------------------------+ void CScrollBarVertical::OnTimer(void) { } //+------------------------------------------------------------------+ //| Event handler | //+------------------------------------------------------------------+ void CScrollBarVertical::OnChartEvent(const int id,const long &lparam,const double &dparam,const string &sparam) { //--- Adjust subwindow Y shift CGCnvElement::OnChartEvent(id,lparam,dparam,sparam); //--- Get the pointers to control objects of the scrollbar CArrowUpButton *buttu=this.GetArrowButtonUp(); CArrowDownButton *buttd=this.GetArrowButtonDown(); CScrollBarThumb *thumb=this.GetThumb(); if(buttu==NULL || buttd==NULL || thumb==NULL) return; //--- If the event ID is an object movement if(id==WF_CONTROL_EVENT_MOVING) { //--- Move the scrollbar to the foreground this.BringToTop(); //--- Declare the variables for the coordinates of the capture area int x=(int)lparam; int y=(int)dparam; //--- Set the X coordinate equal to the X coordinate of the control element x=this.CoordX()+this.BorderSizeLeft(); //--- Adjust the Y coordinate so that the capture area does not go beyond the control, taking into account the arrow buttons if(y<buttu.BottomEdge()) y=buttu.BottomEdge(); if(y>buttd.CoordY()-thumb.Height()) y=buttd.CoordY()-thumb.Height(); //--- If the capture area object is shifted by the calculated coordinates if(thumb.Move(x,y,true)) { //--- set the object relative coordinates thumb.SetCoordXRelative(thumb.CoordX()-this.CoordX()); thumb.SetCoordYRelative(thumb.CoordY()-this.CoordY()); } //--- Get the pointer to the base object CWinFormBase *base=this.GetBase(); if(base!=NULL) { //--- Check if the content goes beyond the container base.CheckForOversize(); //--- Calculate the distance the slider is from the upper border of the scrollbar (from the bottom side of the upper arrow button) int distance=thumb.CoordY()-buttu.BottomEdge(); //--- Declare a variable that stores the distance value before the slider shift static int distance_last=distance; //--- Declare a variable that stores the value in screen pixels the slider was shifted by int shift_value=0; //--- If the values of the past and current distances are not equal (the slider is shifted), if(distance!=distance_last) { //--- calculate the value the slider is shifted by shift_value=distance_last-distance; //--- and enter the new distance into the value of the previous distance for the next calculation distance_last=distance; } //--- Get the largest and smallest coordinates of the lower and upper sides of the base object content int cntt_d=(int)base.GetMaxLongPropFromDependent(CANV_ELEMENT_PROP_BOTTOM); int cntt_u=(int)base.GetMinLongPropFromDependent(CANV_ELEMENT_PROP_COORD_Y); //--- Get the coordinate offset of the upper side of the base object content //--- relative to the initial coordinate of the base object working area int extu=base.CoordYWorkspace()-cntt_u; //--- Calculate the relative value of the desired coordinate, //--- where the contents of the base object, shifted by the slider, should be located double y=(double)this.HeightWorkspace()*(double)distance/double(thumb.Height()!=0 ? thumb.Height() : DBL_MIN); //--- Calculate the required shift value of the base object content along the above calculated coordinate int shift_need=extu-(int)::round(y); //--- If the slider is shifted upwards (positive shift value) if(shift_value>0) { if(cntt_u+shift_need<=base.CoordYWorkspace()) base.ShiftDependentObj(0,shift_need); } //--- If the slider is shifted downwards (negative shift value) if(shift_value<0) { if(cntt_d-shift_need>=base.BottomEdgeWorkspace()) base.ShiftDependentObj(0,shift_need); } ::ChartRedraw(this.ChartID()); } } //--- If any scroll button is clicked if(id==WF_CONTROL_EVENT_CLICK_SCROLL_UP || id==WF_CONTROL_EVENT_CLICK_SCROLL_DOWN) { //--- Move the scrollbar to the foreground this.BringToTop(); //--- Get the base object CWinFormBase *base=this.GetBase(); if(base==NULL) return; //--- Calculate how much each side of the content of the base object goes beyond its borders base.CheckForOversize(); //--- Get the largest and smallest coordinates of the lower and upper sides of the base object content int cntt_d=(int)base.GetMaxLongPropFromDependent(CANV_ELEMENT_PROP_BOTTOM); int cntt_u=(int)base.GetMinLongPropFromDependent(CANV_ELEMENT_PROP_COORD_Y); //--- Set the number of pixels, by which the content of the base object should be shifted int shift=(sparam!="" ? DEF_CONTROL_SCROLL_BAR_SCROLL_STEP_CLICK : DEF_CONTROL_SCROLL_BAR_SCROLL_STEP_WHELL); //--- If the up button is clicked if(id==WF_CONTROL_EVENT_CLICK_SCROLL_UP) { if(cntt_u+shift<=base.CoordYWorkspace()) base.ShiftDependentObj(0,shift); } //--- If the down button is clicked if(id==WF_CONTROL_EVENT_CLICK_SCROLL_DOWN) { if(cntt_d-shift>=base.BottomEdgeWorkspace()) base.ShiftDependentObj(0,-shift); } //--- Calculate the width and coordinates of the slider this.SetThumbParams(); } } //+------------------------------------------------------------------+ //| 'The cursor is inside the active area, | //| the mouse wheel is being scrolled | //+------------------------------------------------------------------+ void CScrollBarVertical::MouseActiveAreaWhellHandler(const int id,const long& lparam,const double& dparam,const string& sparam) { ENUM_WF_CONTROL_EVENT evn=(dparam>0 ? WF_CONTROL_EVENT_CLICK_SCROLL_UP : dparam<0 ? WF_CONTROL_EVENT_CLICK_SCROLL_DOWN : WF_CONTROL_EVENT_NO_EVENT); this.OnChartEvent(evn,lparam,dparam,sparam); ::ChartRedraw(this.ChartID()); } //+------------------------------------------------------------------+
このファイルにすでに変更が加えられた後、子オブジェクトがアタッチされ、親オブジェクトの上、下、または両側から一度にはみ出したオブジェクトには、垂直スクロールバーが表示されます。
子オブジェクトのコンテナである親オブジェクトにスクロールバーを表示させるには、そのオブジェクトにアタッチされたオブジェクトを作成するときに、そのオブジェクトがコンテナの境界を越えているかどうかを確認する必要があります。この確認は、水平スクロールバーを表示するためにすでに実装されています。ここで、コンテナオブジェクトクラスを変更して、コンテナにアタッチされたオブジェクトがその境界を左右にはみ出した場合に、両方のスクロールバーが表示されるようにする必要があります。
\MQL5\Include\DoEasy\Objects\Graph\WForms\Containers\Container.mqhファイルを開き、添付オブジェク ト作成メソッドに必要な確認とスクロールバーの表示を追加します。
//+------------------------------------------------------------------+ //| Create a new attached element | //+------------------------------------------------------------------+ bool CContainer::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) { //--- If the object type is less than the base WinForms object if(element_type<GRAPH_ELEMENT_TYPE_WF_BASE) { //--- report the error and return 'false' CMessage::ToLog(DFUN,MSG_PANEL_OBJECT_ERR_OBJ_MUST_BE_WFBASE); return false; } //--- If failed to create a new graphical element, return 'false' CWinFormBase *obj=CForm::CreateAndAddNewElement(element_type,x,y,w,h,colour,opacity,activity); if(obj==NULL) return false; //--- Set parameters for the created object this.SetObjParams(obj,colour); //--- If there are bound objects if(this.ElementsTotal()>0) { //--- If the panel has auto resize enabled, call the auto resize method if(this.AutoSize()) this.AutoSizeProcess(redraw); //--- If auto resize is disabled, determine whether scrollbars should be displayed else { if(this.CheckForOversize()) { //--- If the attached objects go beyond the visibility window to the left or right if(this.OversizeLeft()>0 || this.OversizeRight()>0) { CScrollBarHorisontal *sbh=this.GetScrollBarHorisontal(); if(sbh!=NULL) { sbh.SetThumbParams(); sbh.SetDisplayed(true); sbh.Show(); } } //--- If the attached objects go beyond the visibility window from above or below if(this.OversizeTop()>0 || this.OversizeBottom()>0) { CScrollBarVertical *sbv=this.GetScrollBarVertical(); if(sbv!=NULL) { sbv.SetThumbParams(); sbv.SetDisplayed(true); sbv.Show(); } } } } } //--- Crop the created object along the edges of the visible part of the container obj.Crop(); //--- return 'true' return true; }
ここで、EraseNoCrop()オブジェクトをトリミングせずに再描画するメソッドを呼び出すいくつかのクラスを修正する必要があります。メソッド内でオブジェクトが更新されないように、falseを設定する必要があります。
\MQL5\Include\DoEasy\Objects\Graph\WForms\Common Controls\Button.mqh、 \MQL5\Include\DoEasy\Objects\Graph\WForms\Common Controls\CheckBox.mqh、 \MQL5\Include\DoEasy\Objects\Graph\WForms\Common Controls\Label.mqhの3つのライブラリファイルで、3つのオブジェクトのRedraw()メソッドに変更が必要です。
すべての変化はfalseフラグの設定に集約されます。
//+------------------------------------------------------------------+ //| Redraw the object | //+------------------------------------------------------------------+ void CButton::Redraw(bool redraw) { //--- Fill the object with the background color this.EraseNoCrop(this.BackgroundColor(),this.Opacity(),false); //--- Declare the variables for X and Y coordinates and set their values depending on the text alignment int x=0,y=0; CLabel::SetTextParamsByAlign(x,y); //--- Draw the text within the set coordinates of the object and the binding point of the text, and update the object this.Text(x,y,this.Text(),this.ForeColor(),this.ForeColorOpacity(),this.TextAnchor()); this.Crop(); this.Update(redraw); } //+------------------------------------------------------------------+ //| Redraw the object | //+------------------------------------------------------------------+ void CCheckBox::Redraw(bool redraw) { //--- Fill the object with the background color having full transparency this.EraseNoCrop(this.BackgroundColor(),this.Opacity(),false); //--- Set corrected text coordinates relative to the checkbox this.SetCorrectTextCoords(); //--- Draw the text and checkbox within the set coordinates of the object and the binding point, and update the object this.Text(this.m_text_x,this.m_text_y,this.Text(),this.ForeColor(),this.ForeColorOpacity(),this.TextAnchor()); this.ShowControlFlag(this.CheckState()); this.Crop(); this.Update(redraw); } //+------------------------------------------------------------------+ //| Redraw the object | //+------------------------------------------------------------------+ void CLabel::Redraw(bool redraw) { //--- Fill the object with the background color having full transparency this.EraseNoCrop(this.BackgroundColor(),0,false); //--- Declare the variables for X and Y coordinates and set their values depending on the text alignment int x=0,y=0; this.SetTextParamsByAlign(x,y); //--- Draw the text within the set coordinates of the object and the binding point of the text, and update the object this.Text(x,y,this.Text(),this.ForeColor(),this.ForeColorOpacity(),this.TextAnchor()); this.Crop(); this.Update(redraw); }
ロジックは以下の通りです。まず、オブジェクト全体が色で塗りつぶされ、更新フラグはリセットされます。つまり、変更はチャートに表示されません。その後、指定されたパラメータでテキストが描画されます。次に、オブジェクトは(必要であれば)可視領域の端に沿って切り取られ、完了すると、オブジェクトの更新メソッドが呼び出され、オブジェクトの表現に加えられた変更が、特にその親オブジェクトとチャート全体に表示されます。
検証
テストをおこなうために、前回のEAを使用し、 \MT5\MQL5\Experts\TestDoEasy\Part133\にTestDoEasy133.mq5として保存します。
パネルに付属するボタンオブジェクトを作成する場合、その親よりも縦方向の寸法が大きくなるように寸法を変更する必要があります。言い換えれば、親パネルの幅に完全に収まるようにしながら、上下の端からはみ出すようにします。
//--- Create the required number of WinForms Panel objects CPanel *pnl=NULL; for(int i=0;i<FORMS_TOTAL;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(); //--- 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); //--- pnl.CreateNewElement(GRAPH_ELEMENT_TYPE_WF_BUTTON,10,-40,pnl.WidthWorkspace()-30,pnl.HeightWorkspace()+50,clrNONE,255,true,false); CButton *btn=pnl.GetElementByType(GRAPH_ELEMENT_TYPE_WF_BUTTON,0); btn.SetText("123456789012345678901234567890"); pnl.CreateNewElement(GRAPH_ELEMENT_TYPE_WF_LABEL,40,20,60,20,clrDodgerBlue,255,false,false); CLabel *lbl=pnl.GetElementByType(GRAPH_ELEMENT_TYPE_WF_LABEL,0); lbl.SetText("LABEL");
それだけです。その他の変更は必要ありません。
EAをコンパイルしてチャート上で起動し、あらかじめPanel AutosizeにNoを指定しておきます。
垂直スクロールバーは、前回の記事で実装した水平スクロールバーとまったく同じように動作することがわかります。
次の段階
次回は、DoEasyライブラリのグラフィカルコントロールを作成するために、コンテナオブジェクト上の両方のスクロールバーを接続し、他のコントロールを作成します。
すべてのファイルは記事に添付されているので、ご自分で勉強したりテストしたりすることができます。
MetaQuotes Ltdによってロシア語から翻訳されました。
元の記事: https://www.mql5.com/ru/articles/14278
- 無料取引アプリ
- 8千を超えるシグナルをコピー
- 金融ニュースで金融マーケットを探索