English Русский Español Deutsch Português
preview
DoEasy - コントロール(第33部):垂直スクロールバー

DoEasy - コントロール(第33部):垂直スクロールバー

MetaTrader 5 | 12 8月 2024, 13:02
16 0
Artyom Trishkin
Artyom Trishkin

内容


概念

前回、ライブラリのグラフィカル要素について説明しましたが、フォームに接続されたオブジェクトが親フォームの左端、右端、または両端からはみ出した場合にオブジェクトに表示される水平スクロールバーを作成しました。ここでは、水平スクロールバーオブジェクトをベースに垂直スクロールバーを作成します。オブジェクトがフォームの上、下、または両側からはみ出した場合、フォームに表示されます。

横スクロールバーオブジェクトのコピーを作り、それを縦にするのはとても簡単な作業なので、この記事は比較的短く、どちらかというと概要に近くなります。このスクロールバーは、後で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

添付されたファイル |
MQL5.zip (5257.78 KB)
母集団最適化アルゴリズム:極値から抜け出す力(第II部) 母集団最適化アルゴリズム:極値から抜け出す力(第II部)
母集団の多様性が低いときに効率的に極小値を脱出して最大値に到達する能力という観点から、母集団最適化アルゴリズムの挙動を調べることを目的とした実験を続けます。研究結果が提供されます。
ニューラルネットワークが簡単に(第77回):Cross-Covariance Transformer (XCiT) ニューラルネットワークが簡単に(第77回):Cross-Covariance Transformer (XCiT)
モデルでは、しばしば様々なAttentionアルゴリズムを使用します。そして、おそらく最もよく使用するのがTransformerです。Transformerの主な欠点はリソースを必要とすることです。この記事では、品質を損なうことなく計算コストを削減する新しいアルゴリズムについて考察します。
多通貨エキスパートアドバイザーの開発(第4回):仮想注文の保留と状況の保存 多通貨エキスパートアドバイザーの開発(第4回):仮想注文の保留と状況の保存
多通貨EAの開発を始めてから、すでに一定の成果を上げ、コードの改良を何度か繰り返すことができました。ただし、EAは保留中注文を扱うことができず、端末の再起動後に動作を再開することができませんでした。これらの機能を追加しましょう。
ニューラルネットワークが簡単に(第76回):Multi-future Transformerで多様な相互作用パターンを探る ニューラルネットワークが簡単に(第76回):Multi-future Transformerで多様な相互作用パターンを探る
この記事では、今後の値動きを予測するというトピックを続けます。Multi-future Transformerのアーキテクチャーをお見せします。その主なアイデアは、未来のマルチモーダル分布をいくつかのユニモーダル分布に分解することで、シーンのエージェント間の相互作用のさまざまなモデルを効果的にシミュレートすることができるというものです。