English Русский 中文 Español Deutsch Português
preview
DoEasy-コントロール(第19部):TabControl、WinFormsオブジェクトイベントでのタブのスクロール

DoEasy-コントロール(第19部):TabControl、WinFormsオブジェクトイベントでのタブのスクロール

MetaTrader 5 | 10 1月 2023, 15:00
65 0
Artyom Trishkin
Artyom Trishkin

内容


概念

前回の記事で、部分的に非表示になっているヘッダーが選択されたときにタブヘッダーバーをスクロールするテストをおこないました。左に移動して、完全に表示されます。ここでは、作成した機能に基づいて、ヘッダーバーを左右および上下に移動するメソッドを実装します。ヘッダーを上下にスクロールする場合、見出し行がどこ(左または右)にあるかが重要になります。これらのメソッドはすべて、部分的に非表示のヘッダーを選択して全体を表示するときと、スクロールボタン(上、下、左、右)が押されたときに呼び出されます。どのボタンとどのコントロールが押されたかを理解するために、イベントモデルを使用します。ボタンが押されると、イベントが送信され、ライブラリがインターセプトして処理し、要素のイベントハンドラに送信します。押されたボタンは、コントロール内でさらに処理するために接続されています。このモデルを使用して、他のコントロールを操作します。

現時点では、メインおよび基本のグラフィック要素の名前がsparamパラメータでイベントハンドラに送信されます。メインコントロールは、イベントが発生したオブジェクトを特徴とするコントロールで、ここでは基本コントロールと見なされます。ただし、この基本コントロールが複合コントロールであり、さらにいくつかのコントロール(ボタンなど)が関連付けられていて、それらのコントロールでイベントが既に発生している場合は、メインオブジェクトに直接接続されていないため、基本要素を見つけることができません。これを回避するには、sparamパラメータで、メインオブジェクトと基本オブジェクトの名前に加えて、イベントが発生したオブジェクトの名前を渡します。

したがって、メインオブジェクトの入力データ、基本オブジェクトの入力データ、それに関連付けられた要素の1つでイベントが発生した場合、およびイベントが発生した要素の名前が含まれます。ボタンをクリックしたコントロールを定義するために(特殊なケース)、この基本オブジェクトのタイプをdparamパラメータで送信します。したがって、基本オブジェクトのタイプがわかれば、メインオブジェクト内のすべてのコントロールのリストが取得され、そのタイプがdparamに記録されます。次に、これらすべてのオブジェクトのループで、sparamで最後に渡された名前で関連付けられたオブジェクトを探します。これは、イベント(コントロールのクリック)が発生したオブジェクトです。

現時点では、そのような構造は、より複雑なオブジェクトを処理するための汎用性という意味で、あまり信頼性がないように思えますが、この段階では、ライブラリ開発には十分です。相互にオブジェクトをより洗練された入れ子にした、より複雑なコントロールを作成すると、ライブラリ内のそのようなオブジェクトのイベントを正確かつ普遍的に識別する必要がどのようにあるかの明確な実用的な例が得られます(「単純なものから複雑なものへ」原理を思い出してください)。

現在、ライブラリではコントロールを表示することも非表示にすることもできます。要素を表示または非表示にするには、メイン要素または基本要素のみを非表示にするだけで十分です。それに関連付けられているすべての要素は、それに応じて表示または非表示にされます。ただし、メインオブジェクトに関係なく表示されるオブジェクトをコントロールに含める必要があることに留意すると(それらの可視性はコントロール自体の内部で設定される)、そのようなオブジェクトが非表示になり、その親要素が表示される場合、そのようなオブジェクトは表示されてはいけません。このような状況では、基本コントロールからこれらのオブジェクトの可視性を管理する必要があります。この可能性を実装するには、グラフィック要素のもう1つのプロパティ、つまり表示フラグを導入する必要があります。次に、メインオブジェクトが非表示になってから表示された場合、表示フラグがクリアされたコントロールは、基本コントロールから明示的に表示されるまで非表示のままになります。

さて、理論は十分なので、仕事に取り掛かりましょう...


ライブラリクラスの改善

すべては将来を念頭に置いておこなっているため、スクロールコントロールボタン用の独自のイベントIDは作成しません。ボタンを使用する新しいコントロール(スクロールバー、ドロップダウンリストなど)をさらに見据えて作成します。つまり、スクロールコントロールまたはポップアップコントロールをクリックする一般的なイベントをいくつか作成しましょう。

\MQL5\Include\DoEasy\Defines.mqhにある、可能なWinFormsコントロールイベントのリストで、新しい列挙定数を追加します

//+------------------------------------------------------------------+
//| List of possible WinForms control events                         |
//+------------------------------------------------------------------+
enum ENUM_WF_CONTROL_EVENT
  {
   WF_CONTROL_EVENT_NO_EVENT = GRAPH_OBJ_EVENTS_NEXT_CODE,// No event
   WF_CONTROL_EVENT_CLICK,                            // "Click on the control" event
   WF_CONTROL_EVENT_CLICK_CANCEL,                     // "Canceling the click on the control" event
   WF_CONTROL_EVENT_TAB_SELECT,                       // "TabControl tab selection" event
   WF_CONTROL_EVENT_CLICK_SCROLL_LEFT,                // "Clicking the control left button" event
   WF_CONTROL_EVENT_CLICK_SCROLL_RIGHT,               // "Clicking the control right button" event
   WF_CONTROL_EVENT_CLICK_SCROLL_UP,                  // "Clicking the control up button" event
   WF_CONTROL_EVENT_CLICK_SCROLL_DOWN,                // "Clicking the control down button" event
  };
#define WF_CONTROL_EVENTS_NEXT_CODE (WF_CONTROL_EVENT_TAB_SELECT+1)  // The code of the next event after the last graphical element event code
//+------------------------------------------------------------------+

ここでは、念のため、コントロールのクリックをキャンセルするためのイベントIDを作成し(場合によっては、そのようなイベントを処理できる場合もある)、コントロールの外観を管理するボタンや操作を可能にするボタンを持つコントロールの一般的なイベントを追加しました。

グラフィック要素の整数プロパティの列挙で新しいプロパティを追加し、オブジェクトの整数プロパティの数を96から97に増やします

//+------------------------------------------------------------------+
//| Integer properties of the graphical element on the canvas        |
//+------------------------------------------------------------------+
enum ENUM_CANV_ELEMENT_PROP_INTEGER
  {
   CANV_ELEMENT_PROP_ID = 0,                          // Element ID
   CANV_ELEMENT_PROP_TYPE,                            // Graphical element type

   //---...
   //---...

   CANV_ELEMENT_PROP_VISIBLE_AREA_WIDTH,              // Visibility scope width
   CANV_ELEMENT_PROP_VISIBLE_AREA_HEIGHT,             // Visibility scope height
   CANV_ELEMENT_PROP_DISPLAYED,                       // Non-hidden control display flag
   CANV_ELEMENT_PROP_GROUP,                           // Group the graphical element belongs to
   CANV_ELEMENT_PROP_ZORDER,                          // Priority of a graphical object for receiving the event of clicking on a chart

   //---...
   //---...

   CANV_ELEMENT_PROP_TAB_PAGE_COLUMN,                 // Tab column index
   CANV_ELEMENT_PROP_ALIGNMENT,                       // Location of an object inside the control
   
  };
#define CANV_ELEMENT_PROP_INTEGER_TOTAL (97)          // Total number of integer properties
#define CANV_ELEMENT_PROP_INTEGER_SKIP  (0)           // Number of integer properties not used in sorting
//+------------------------------------------------------------------+

コントロールが非表示になっていないが非表示のコントロールを表示するためのフラグがクリアされている(false)場合、コントロールは表示されません。つまり、メインコントロールが表示されている場合、このフラグがクリアされているその子孫は、このフラグをtrueに設定してShow()メソッドを呼び出して強制的に表示するまで非表示のままになります。


キャンバス上のグラフィック要素を並べ替えられる基準の列挙に新しいプロパティを追加します。

//+------------------------------------------------------------------+
//| Possible sorting criteria of graphical elements on the canvas    |
//+------------------------------------------------------------------+
#define FIRST_CANV_ELEMENT_DBL_PROP  (CANV_ELEMENT_PROP_INTEGER_TOTAL-CANV_ELEMENT_PROP_INTEGER_SKIP)
#define FIRST_CANV_ELEMENT_STR_PROP  (CANV_ELEMENT_PROP_INTEGER_TOTAL-CANV_ELEMENT_PROP_INTEGER_SKIP+CANV_ELEMENT_PROP_DOUBLE_TOTAL-CANV_ELEMENT_PROP_DOUBLE_SKIP)
enum ENUM_SORT_CANV_ELEMENT_MODE
  {
//--- Sort by integer properties
   SORT_BY_CANV_ELEMENT_ID = 0,                       // Sort by element ID
   SORT_BY_CANV_ELEMENT_TYPE,                         // Sort by graphical element type

   //---...
   //---...

   SORT_BY_CANV_ELEMENT_VISIBLE_AREA_WIDTH,           // Sort by visibility scope width
   SORT_BY_CANV_ELEMENT_VISIBLE_AREA_HEIGHT,          // Sort by visibility scope height
   SORT_BY_CANV_ELEMENT_DISPLAYED,                    // Sort by non-hidden control display flag
   SORT_BY_CANV_ELEMENT_GROUP,                        // Sort by a group the graphical element belongs to
   SORT_BY_CANV_ELEMENT_ZORDER,                       // Sort by the priority of a graphical object for receiving the event of clicking on a chart

   //---...
   //---...

   SORT_BY_CANV_ELEMENT_TAB_PAGE_COLUMN,              // Sort by tab column index
   SORT_BY_CANV_ELEMENT_ALIGNMENT,                    // Sort by the location of the object inside the control
//--- Sort by real properties

//--- Sort by string properties
   SORT_BY_CANV_ELEMENT_NAME_OBJ = FIRST_CANV_ELEMENT_STR_PROP,// Sort by an element object name
   SORT_BY_CANV_ELEMENT_NAME_RES,                     // Sort by the graphical resource name
   SORT_BY_CANV_ELEMENT_TEXT,                         // Sort by graphical element text
   SORT_BY_CANV_ELEMENT_DESCRIPTION,                  // Sort by graphical element description
  };
//+------------------------------------------------------------------+

これで、この新しいプロパティによってグラフィック要素オブジェクトのリストを選択して並べ替えることができるようになります。


\MQL5\Include\DoEasy\Data.mqhに、ライブラリの新しいメッセージインデックスを追加します

   MSG_LIB_SYS_REQUEST_OUTSIDE_ARRAY,                 // Request outside the array
   MSG_LIB_SYS_FAILED_CONV_GRAPH_OBJ_COORDS_TO_XY,    // Failed to convert graphical object coordinates to screen ones
   MSG_LIB_SYS_FAILED_CONV_TIMEPRICE_COORDS_TO_XY,    // Failed to convert time/price coordinates to screen ones
   MSG_LIB_SYS_FAILED_ENQUEUE_EVENT,                  // Failed to put the event in the chart event queue

...

   MSG_CANV_ELEMENT_PROP_VISIBLE_AREA_WIDTH,          // Visibility scope width
   MSG_CANV_ELEMENT_PROP_VISIBLE_AREA_HEIGHT,         // Visibility scope height
   MSG_CANV_ELEMENT_PROP_DISPLAYED,                   // Non-hidden control display flag
   MSG_CANV_ELEMENT_PROP_ENABLED,                     // Element availability flag
   MSG_CANV_ELEMENT_PROP_FORE_COLOR,                  // Default text color for all control objects

新しく追加したインデックスに対応するメッセージテキストも追加します。

   {"Запрос за пределами массива","Data requested outside the array"},
   {"Не удалось преобразовать координаты графического объекта в экранные","Failed to convert graphics object coordinates to screen coordinates"},
   {"Не удалось преобразовать координаты время/цена в экранные","Failed to convert time/price coordinates to screen coordinates"},
   {"Не удалось поставить событие в очередь событий графика","Failed to put event in chart event queue"},

...

   {"Ширина области видимости","Width of object visibility area"},
   {"Высота области видимости","Height of object visibility area"},
   {"Флаг отображения не скрытого элемента управления","Flag that sets the display of a non-hidden control"},
   {"Флаг доступности элемента","Element Availability Flag"},
   {"Цвет текста по умолчанию для всех объектов элемента управления","Default text color for all objects in the control"},


\MQL5\Include\DoEasy\Objects\Graph\GCnvElement.mqhのグラフィック要素オブジェクトクラスを改善しましょう。

オブジェクト構造体新しく追加されたプロパティを追加します。

private:
   int               m_shift_coord_x;                          // Offset of the X coordinate relative to the base object
   int               m_shift_coord_y;                          // Offset of the Y coordinate relative to the base object
   struct SData
     {         
      //--- Object integer properties
      int            id;                                       // Element ID
      int            type;                                     // Graphical element type
               
      //---... 
      //---... 
               
      int            visible_area_w;                           // Visibility scope width
      int            visible_area_h;                           // Visibility scope height
      bool           displayed;                                // Non-hidden control display flag
      //--- Object real properties
               
      //--- Object string properties
      uchar          name_obj[64];                             // Graphical element object name
      uchar          name_res[64];                             // Graphical resource name
      uchar          text[256];                                // Graphical element text
      uchar          descript[256];                            // Graphical element description
     };        
   SData             m_struct_obj;                             // Object structure
   uchar             m_uchar_array[];                          // uchar array of the object structure

ファイルからオブジェクトを保存および復元するために、オブジェクトのすべてのプロパティがオブジェクトの構造体に設定されます。

オブジェクトプロパティへのアクセスを簡素化するために、オブジェクト表示プロパティを設定および取得するための2つの新しいメソッドをメソッドのブロックに追加します。

//+------------------------------------------------------------------+
//| Methods of simplified access to object properties                |
//+------------------------------------------------------------------+
//--- Set the (1) X, (2) Y coordinates, (3) element width, (4) height, (5) right (6) and bottom edge,
   virtual bool      SetCoordX(const int coord_x);
   virtual bool      SetCoordY(const int coord_y);
   virtual bool      SetWidth(const int width);
   virtual bool      SetHeight(const int height);
   void              SetRightEdge(void)                        { this.SetProperty(CANV_ELEMENT_PROP_RIGHT,this.RightEdge());           }
   void              SetBottomEdge(void)                       { this.SetProperty(CANV_ELEMENT_PROP_BOTTOM,this.BottomEdge());         }
//--- Set the shift of the (1) left, (2) top, (3) right, (4) bottom edge of the active area relative to the element,
//--- (5) all shifts of the active area edges relative to the element, (6) opacity
   void              SetActiveAreaLeftShift(const int value)   { this.SetProperty(CANV_ELEMENT_PROP_ACT_SHIFT_LEFT,fabs(value));       }
   void              SetActiveAreaRightShift(const int value)  { this.SetProperty(CANV_ELEMENT_PROP_ACT_SHIFT_RIGHT,fabs(value));      }
   void              SetActiveAreaTopShift(const int value)    { this.SetProperty(CANV_ELEMENT_PROP_ACT_SHIFT_TOP,fabs(value));        }
   void              SetActiveAreaBottomShift(const int value) { this.SetProperty(CANV_ELEMENT_PROP_ACT_SHIFT_BOTTOM,fabs(value));     }
   void              SetActiveAreaShift(const int left_shift,const int bottom_shift,const int right_shift,const int top_shift);
   void              SetOpacity(const uchar value,const bool redraw=false);
   
//--- (1) Set and (2) return the flag for displaying a non-hidden control
   void              SetDisplayed(const bool flag)             { this.SetProperty(CANV_ELEMENT_PROP_DISPLAYED,flag);                   }
   bool              Displayed(void)                           { return (bool)this.GetProperty(CANV_ELEMENT_PROP_DISPLAYED);           }

//--- (1) Set and (2) return the graphical element type
   void              SetTypeElement(const ENUM_GRAPH_ELEMENT_TYPE type)
                       {
                        CGBaseObj::SetTypeElement(type);
                        this.SetProperty(CANV_ELEMENT_PROP_TYPE,type);
                       }
   ENUM_GRAPH_ELEMENT_TYPE TypeGraphElement(void)  const { return (ENUM_GRAPH_ELEMENT_TYPE)this.GetProperty(CANV_ELEMENT_PROP_TYPE);   }

メソッドは、渡されたフラグをオブジェクトプロパティに書き込み、そこに設定された値を返すだけです。


両方のクラスコンストラクタで新しいプロパティに既定値を追加します

//+------------------------------------------------------------------+
//| Parametric constructor                                           |
//+------------------------------------------------------------------+
CGCnvElement::CGCnvElement(const ENUM_GRAPH_ELEMENT_TYPE element_type,
                           const int      element_id,
                           const int      element_num,
                           const long     chart_id,
                           const int      wnd_num,
                           const string   descript,
                           const int      x,
                           const int      y,
                           const int      w,
                           const int      h,
                           const color    colour,
                           const uchar    opacity,
                           const bool     movable=true,
                           const bool     activity=true,
                           const bool     redraw=false) : m_shadow(false)
  {
   this.SetTypeElement(element_type);
   this.m_type=OBJECT_DE_TYPE_GELEMENT; 
   this.m_element_main=NULL;
   this.m_element_base=NULL;
   this.m_chart_color_bg=(color)::ChartGetInteger((chart_id==NULL ? ::ChartID() : chart_id),CHART_COLOR_BACKGROUND);
   this.m_name=this.CreateNameGraphElement(element_type);
   this.m_chart_id=(chart_id==NULL || chart_id==0 ? ::ChartID() : chart_id);
   this.m_subwindow=wnd_num;
   this.SetFont(DEF_FONT,DEF_FONT_SIZE);
   this.m_text_anchor=0;
   this.m_text_x=0;
   this.m_text_y=0;
   this.SetBackgroundColor(colour,true);
   this.SetOpacity(opacity);
   this.m_shift_coord_x=0;
   this.m_shift_coord_y=0;
   if(::ArrayResize(this.m_array_colors_bg,1)==1)
      this.m_array_colors_bg[0]=this.BackgroundColor();
   if(::ArrayResize(this.m_array_colors_bg_dwn,1)==1)
      this.m_array_colors_bg_dwn[0]=this.BackgroundColor();
   if(::ArrayResize(this.m_array_colors_bg_ovr,1)==1)
      this.m_array_colors_bg_ovr[0]=this.BackgroundColor();
   if(this.Create(chart_id,wnd_num,x,y,w,h,redraw))
     {
      this.SetProperty(CANV_ELEMENT_PROP_NAME_RES,this.m_canvas.ResourceName()); // Graphical resource name
      this.SetProperty(CANV_ELEMENT_PROP_CHART_ID,CGBaseObj::ChartID());         // Chart ID

      //---...
      //---...

      this.SetProperty(CANV_ELEMENT_PROP_VISIBLE_AREA_WIDTH,w);                  // Visibility scope width
      this.SetProperty(CANV_ELEMENT_PROP_VISIBLE_AREA_HEIGHT,h);                 // Visibility scope height
      this.SetProperty(CANV_ELEMENT_PROP_DISPLAYED,true);                        // Non-hidden control display flag
      //---
      this.SetProperty(CANV_ELEMENT_PROP_BELONG,ENUM_GRAPH_OBJ_BELONG::GRAPH_OBJ_BELONG_PROGRAM);  // Graphical element affiliation
      this.SetProperty(CANV_ELEMENT_PROP_ZORDER,0);                              // Priority of a graphical object for receiving the event of clicking on a chart
      this.SetProperty(CANV_ELEMENT_PROP_BOLD_TYPE,FW_NORMAL);                   // Font width type

      //---...
      //---...

      this.SetProperty(CANV_ELEMENT_PROP_TEXT,"");                                                    // Graphical element text
      this.SetProperty(CANV_ELEMENT_PROP_DESCRIPTION,descript);                                       // Graphical element description
      this.SetVisibleFlag(false,false);
     }
   else
     {
      ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_FAILED_CREATE_ELM_OBJ),"\"",this.TypeElementDescription(element_type),"\" ",this.NameObj());
     }
  }
//+------------------------------------------------------------------+
//| Protected constructor                                            |
//+------------------------------------------------------------------+
CGCnvElement::CGCnvElement(const ENUM_GRAPH_ELEMENT_TYPE element_type,
                           const long    chart_id,
                           const int     wnd_num,
                           const string  descript,
                           const int     x,
                           const int     y,
                           const int     w,
                           const int     h) : m_shadow(false)
  {
   this.m_type=OBJECT_DE_TYPE_GELEMENT; 
   this.m_element_main=NULL;
   this.m_element_base=NULL;
   this.m_chart_color_bg=(color)::ChartGetInteger((chart_id==NULL ? ::ChartID() : chart_id),CHART_COLOR_BACKGROUND);
   this.m_name=this.CreateNameGraphElement(element_type);
   this.m_chart_id=(chart_id==NULL || chart_id==0 ? ::ChartID() : chart_id);
   this.m_subwindow=wnd_num;
   this.m_type_element=element_type;
   this.SetFont(DEF_FONT,DEF_FONT_SIZE);
   this.m_text_anchor=0;
   this.m_text_x=0;
   this.m_text_y=0;
   this.SetBackgroundColor(CLR_CANV_NULL,true);
   this.SetOpacity(0);
   this.m_shift_coord_x=0;
   this.m_shift_coord_y=0;
   if(::ArrayResize(this.m_array_colors_bg,1)==1)
      this.m_array_colors_bg[0]=this.BackgroundColor();
   if(::ArrayResize(this.m_array_colors_bg_dwn,1)==1)
      this.m_array_colors_bg_dwn[0]=this.BackgroundColor();
   if(::ArrayResize(this.m_array_colors_bg_ovr,1)==1)
      this.m_array_colors_bg_ovr[0]=this.BackgroundColor();
   if(this.Create(chart_id,wnd_num,x,y,w,h,false))
     {
      this.SetProperty(CANV_ELEMENT_PROP_NAME_RES,this.m_canvas.ResourceName()); // Graphical resource name
      this.SetProperty(CANV_ELEMENT_PROP_CHART_ID,CGBaseObj::ChartID());         // Chart ID

      //---...
      //---...

      this.SetProperty(CANV_ELEMENT_PROP_VISIBLE_AREA_WIDTH,w);                  // Visibility scope width
      this.SetProperty(CANV_ELEMENT_PROP_VISIBLE_AREA_HEIGHT,h);                 // Visibility scope height
      this.SetProperty(CANV_ELEMENT_PROP_DISPLAYED,true);                        // Non-hidden control display flag
      //---
      this.SetProperty(CANV_ELEMENT_PROP_BELONG,ENUM_GRAPH_OBJ_BELONG::GRAPH_OBJ_BELONG_PROGRAM);  // Graphical element affiliation
      this.SetProperty(CANV_ELEMENT_PROP_ZORDER,0);                              // Priority of a graphical object for receiving the event of clicking on a chart
      this.SetProperty(CANV_ELEMENT_PROP_BOLD_TYPE,FW_NORMAL);                   // Font width type

      //---...
      //---...

      this.SetProperty(CANV_ELEMENT_PROP_TEXT,"");                                                    // Graphical element text
      this.SetProperty(CANV_ELEMENT_PROP_DESCRIPTION,descript);                                       // Graphical element description
      this.SetVisibleFlag(false,false);
     }
   else
     {
      ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_FAILED_CREATE_ELM_OBJ),"\"",this.TypeElementDescription(element_type),"\" ",this.NameObj());
     }
  }
//+------------------------------------------------------------------+

デフォルトでは、オブジェクトは、基本オブジェクトまたはメインオブジェクトの可視性がオンになっているときに可視で表示されるように設定されています。オブジェクトの可視性を手動で制御するモードを設定するには、フラグの値をfalseに設定する必要があります。この場合、基本オブジェクトまたはメインオブジェクトが非表示になってから表示された場合、現在のオブジェクトは引き続き非表示の状態のままであり、それを表示するには、Displayedプロパティをtrueに設定し、オブジェクトのShow()メソッドを呼び出します。


オブジェクト構造体を作成するメソッドで、オブジェクトプロパティ値を適切な構造体フィールドに設定します

//+------------------------------------------------------------------+
//| Create the object structure                                      |
//+------------------------------------------------------------------+
bool CGCnvElement::ObjectToStruct(void)
  {
//--- Save integer properties
   this.m_struct_obj.id=(int)this.GetProperty(CANV_ELEMENT_PROP_ID);                               // Element ID
   this.m_struct_obj.type=(int)this.GetProperty(CANV_ELEMENT_PROP_TYPE);                           // Graphical element type
   //---...
   //---...
   this.m_struct_obj.belong=(int)this.GetProperty(CANV_ELEMENT_PROP_BELONG);                       // Graphical element affiliation
   this.m_struct_obj.number=(int)this.GetProperty(CANV_ELEMENT_PROP_NUM);                          // Element ID in the list
   //---...
   //---...
   this.m_struct_obj.visible_area_x=(int)this.GetProperty(CANV_ELEMENT_PROP_VISIBLE_AREA_X);       // Visibility scope X coordinate
   this.m_struct_obj.visible_area_y=(int)this.GetProperty(CANV_ELEMENT_PROP_VISIBLE_AREA_Y);       // Visibility scope Y coordinate
   //---...
   //---...
   this.m_struct_obj.visible_area_w=(int)this.GetProperty(CANV_ELEMENT_PROP_VISIBLE_AREA_WIDTH);   // Visibility scope width
   this.m_struct_obj.visible_area_h=(int)this.GetProperty(CANV_ELEMENT_PROP_VISIBLE_AREA_HEIGHT);  // Visibility scope height
   this.m_struct_obj.displayed=(bool)this.GetProperty(CANV_ELEMENT_PROP_DISPLAYED);                // Flag for displaying a non-hidden control
   this.m_struct_obj.zorder=this.GetProperty(CANV_ELEMENT_PROP_ZORDER);                            // Priority of a graphical object for receiving the on-chart mouse click event
   this.m_struct_obj.enabled=(bool)this.GetProperty(CANV_ELEMENT_PROP_ENABLED);                    // Element availability flag
   this.m_struct_obj.fore_color=(color)this.GetProperty(CANV_ELEMENT_PROP_FORE_COLOR);             // Default text color for all control objects
   //---...
   //---...
   this.m_struct_obj.fore_color_opacity=(uchar)this.GetProperty(CANV_ELEMENT_PROP_FORE_COLOR_OPACITY); // Opacity of the default text color for all control objects
   this.m_struct_obj.background_color=(color)this.GetProperty(CANV_ELEMENT_PROP_BACKGROUND_COLOR); // Element background color

   this.m_struct_obj.tab_alignment=(int)this.GetProperty(CANV_ELEMENT_PROP_TAB_ALIGNMENT);                              // Location of tabs inside the control
   this.m_struct_obj.alignment=(int)this.GetProperty(CANV_ELEMENT_PROP_ALIGNMENT);                                      // Location of an object inside the control
//--- Save real properties

//--- Save string properties
   ::StringToCharArray(this.GetProperty(CANV_ELEMENT_PROP_NAME_OBJ),this.m_struct_obj.name_obj);   // Graphical element object name
   ::StringToCharArray(this.GetProperty(CANV_ELEMENT_PROP_NAME_RES),this.m_struct_obj.name_res);   // Graphical resource name
   ::StringToCharArray(this.GetProperty(CANV_ELEMENT_PROP_TEXT),this.m_struct_obj.text);           // Graphical element text
   ::StringToCharArray(this.GetProperty(CANV_ELEMENT_PROP_DESCRIPTION),this.m_struct_obj.descript);// Graphical element description
   //--- Save the structure to the uchar array
   ::ResetLastError();
   if(!::StructToCharArray(this.m_struct_obj,this.m_uchar_array))
     {
      CMessage::ToLog(DFUN,MSG_LIB_SYS_FAILED_SAVE_OBJ_STRUCT_TO_UARRAY,true);
      return false;
     }
   return true;
  }
//+------------------------------------------------------------------+


構造体からオブジェクトを作成するメソッドで対応する構造体フィールドから新しいオブジェクトプロパティの値を設定します

//+------------------------------------------------------------------+
//| Create the object from the structure                             |
//+------------------------------------------------------------------+
void CGCnvElement::StructToObject(void)
  {
//--- Save integer properties
   this.SetProperty(CANV_ELEMENT_PROP_ID,this.m_struct_obj.id);                                    // Element ID
   this.SetProperty(CANV_ELEMENT_PROP_TYPE,this.m_struct_obj.type);                                // Graphical element type
   //---...
   //---...
   this.SetProperty(CANV_ELEMENT_PROP_BELONG,this.m_struct_obj.belong);                            // Graphical element affiliation
   this.SetProperty(CANV_ELEMENT_PROP_NUM,this.m_struct_obj.number);                               // Element index in the list
   //---...
   //---...
   this.SetProperty(CANV_ELEMENT_PROP_VISIBLE_AREA_HEIGHT,this.m_struct_obj.visible_area_h);       // Visibility scope height
   this.SetProperty(CANV_ELEMENT_PROP_DISPLAYED,this.m_struct_obj.displayed);                      // Non-hidden control display flag
   this.SetProperty(CANV_ELEMENT_PROP_ZORDER,this.m_struct_obj.zorder);                            // Priority of a graphical object for receiving the event of clicking on a chart
   this.SetProperty(CANV_ELEMENT_PROP_ENABLED,this.m_struct_obj.enabled);                          // Element availability flag
   //---...
   //---...
   this.SetProperty(CANV_ELEMENT_PROP_FORE_COLOR,this.m_struct_obj.fore_color);                    // Default text color for all control objects
   this.SetProperty(CANV_ELEMENT_PROP_FORE_COLOR_OPACITY,this.m_struct_obj.fore_color_opacity);    // Opacity of the default text color for all control objects

   this.SetProperty(CANV_ELEMENT_PROP_TAB_ALIGNMENT,this.m_struct_obj.tab_alignment);                             // Location of tabs inside the control
   this.SetProperty(CANV_ELEMENT_PROP_ALIGNMENT,this.m_struct_obj.alignment);                                     // Location of an object inside the control
//--- Save real properties

//--- Save string properties
   this.SetProperty(CANV_ELEMENT_PROP_NAME_OBJ,::CharArrayToString(this.m_struct_obj.name_obj));   // Graphical element object name
   this.SetProperty(CANV_ELEMENT_PROP_NAME_RES,::CharArrayToString(this.m_struct_obj.name_res));   // Graphical resource name
   this.SetProperty(CANV_ELEMENT_PROP_TEXT,::CharArrayToString(this.m_struct_obj.text));           // Graphical element text
   this.SetProperty(CANV_ELEMENT_PROP_DESCRIPTION,::CharArrayToString(this.m_struct_obj.descript));// Graphical element description
  }
//+------------------------------------------------------------------+


\MQL5\Include\DoEasy\Objects\Graph\WForms\WinFormBase.mqhにある基本WinFormsオブジェクトのクラスの整数要素プロパティの説明を返すメソッドに、新しいオブジェクトプロパティ:の説明を返すコードブロックを追加します

//+------------------------------------------------------------------+
//| Return the description of the control integer property           |
//+------------------------------------------------------------------+
string CWinFormBase::GetPropertyDescription(ENUM_CANV_ELEMENT_PROP_INTEGER property,bool only_prop=false)
  {
   return
     (
      property==CANV_ELEMENT_PROP_ID                           ?  CMessage::Text(MSG_CANV_ELEMENT_PROP_ID)+
         (only_prop ? "" : !this.SupportProperty(property)     ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)this.GetProperty(property)
         )  :

      //---...
      //---...

      property==CANV_ELEMENT_PROP_VISIBLE_AREA_HEIGHT          ?  CMessage::Text(MSG_CANV_ELEMENT_PROP_VISIBLE_AREA_HEIGHT)+
         (only_prop ? "" : !this.SupportProperty(property)     ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)this.GetProperty(property)
         )  :
      property==CANV_ELEMENT_PROP_DISPLAYED                    ?  CMessage::Text(MSG_CANV_ELEMENT_PROP_DISPLAYED)+
         (only_prop ? "" : !this.SupportProperty(property)     ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)this.GetProperty(property)
         )  :
      property==CANV_ELEMENT_PROP_GROUP                        ?  CMessage::Text(MSG_GRAPH_OBJ_PROP_GROUP)+
         (only_prop ? "" : !this.SupportProperty(property)     ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)this.GetProperty(property)
         )  :

      //---...
      //---...

      property==CANV_ELEMENT_PROP_ALIGNMENT                    ?  CMessage::Text(MSG_CANV_ELEMENT_PROP_ALIGNMENT)+
         (only_prop ? "" : !this.SupportProperty(property)     ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+AlignmentDescription((ENUM_CANV_ELEMENT_ALIGNMENT)this.GetProperty(property))
         )  :
      ""
     );
  }
//+------------------------------------------------------------------+

プロパティがメソッドに渡されると、only_propフラグに応じて、単純なプロパティ名(only_prop=true)、またはプロパティに設定された値(only_prop=false)とともに、適切なテキストメッセージが作成されます。


すべてのコントロールは、内部使用と、GUIで発生するイベントについてプログラムに通知するための両方について、何らかの方法でイベント機能を使用します。ユーザーインタラクションのメインクラスはフォームオブジェクトクラスです。これはマウスインタラクション機能を実装し、すべてのWinFormsライブラリオブジェクトの基本クラスはそれから継承されます。同じクラスのグラフィック要素にメッセージを送信するメソッドを作成しましょう。

\MQL5\Include\DoEasy\Objects\Graph\Form.mqhフォームオブジェクトクラスファイルのprotectedセクションで、メッセージを送信するメソッドを宣言します
このメソッドは、派生オブジェクトに対してオーバーライドする必要がある場合に備えて仮想になります。

//--- 'The cursor is inside the window scrolling area, a mouse button is clicked (any)' event handler
   virtual void      MouseScrollAreaPressedHandler(const int id,const long& lparam,const double& dparam,const string& sparam);
//--- 'The cursor is inside the window scrolling area, the mouse wheel is being scrolled' event handler
   virtual void      MouseScrollAreaWhellHandler(const int id,const long& lparam,const double& dparam,const string& sparam);

//--- Send a message about the event
   virtual bool      SendEvent(const long chart_id,const ushort event_id);

public:


フォームを表示するメソッドで、オブジェクト表示フラグを確認するようにします(新しいグラフィック要素のプロパティを確認)。

//+------------------------------------------------------------------+
//| Show the form                                                    |
//+------------------------------------------------------------------+
void CForm::Show(void)
  {
//--- If the element should not be displayed (hidden inside another control), leave
   if(!this.Displayed())
      return;
//--- If the object has a shadow, display it
   if(this.m_shadow_obj!=NULL)
      this.m_shadow_obj.Show();
//--- Display the main form
   CGCnvElement::Show();
//--- In the loop by all bound graphical objects,
   for(int i=0;i<this.m_list_elements.Total();i++)
     {
      //--- get the next graphical element
      CGCnvElement *element=this.m_list_elements.At(i);
      if(element==NULL)
         continue;
      //--- and display it
      element.Show();
     }
//--- Update the form
   CGCnvElement::Update();
  }
//+------------------------------------------------------------------+

CForm型以上のオブジェクトに対してこのメソッドを呼び出すと、オブジェクト表示フラグが最初に確認され、フラグが設定されていない(手動の可視性制御が有効になっている)場合は、すぐにメソッドを終了します。


クラス本体の外にイベントメッセージを送信するメソッドを実装しましょう。

//+------------------------------------------------------------------+
//| Send a message about the event                                   |
//+------------------------------------------------------------------+
bool CForm::SendEvent(const long chart_id,const ushort event_id)
  {
   //--- Create the event:
   //--- Get the base and main objects
   CGCnvElement *base=this.GetBase();
   CGCnvElement *main=this.GetMain();
   //--- find the names of the main and base objects
   string name_main=(main!=NULL ? main.Name() : this.IsMain() ? this.Name() : "Lost name of object");
   string name_base=(base!=NULL ? base.Name() : "Lost name of object");
   ENUM_GRAPH_ELEMENT_TYPE base_base_type=(base!=NULL ? base.GetBase().TypeGraphElement() : this.TypeGraphElement());
   //--- pass the object ID in the event 'long' parameter
   //--- pass the object type in the event 'double' parameter
   //--- in the event 'string' parameter, pass the names of the main, base and current objects separated by ";"
   long lp=this.ID();
   double dp=base_base_type;
   string sp=::StringSubstr(name_main,::StringLen(this.NamePrefix()))+";"+
             ::StringSubstr(name_base,::StringLen(this.NamePrefix()))+";"+
             ::StringSubstr(this.Name(),::StringLen(this.NamePrefix()));
   //--- Send the event of clicking on the control to the control program chart
   bool res=true;
   ::ResetLastError();
   res=::EventChartCustom(chart_id,event_id,lp,dp,sp);
   if(res)
      return true;
   ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_FAILED_ENQUEUE_EVENT),". ",CMessage::Text(MSG_LIB_SYS_ERROR),": ",CMessage::Text(::GetLastError()));
   return false;
  }
//+------------------------------------------------------------------+

ここで、メインオブジェクトと基本オブジェクトへのポインタを取得し、それらの名前を受け取ります。メインオブジェクトへのポインタがNULLの場合、これがメインオブジェクトである可能性が最も高くなります。これをおこなうには、これがtrueかどうかを確認します。trueの場合は、現在のオブジェクトの名前を使用します。何らかの理由でポインタが受信されない場合は、「Lost name of object」文字列を使用して管理します。

次に、現在のオブジェクトの基本オブジェクトがバインドされている基本オブジェクトの型を見つけて(つまり、基本オブジェクトからその基本オブジェクトを取得し、その後にその型を取得する)、イベントメッセージで送信され、受信したすべてのデータを、渡された変数に書き込む必要があります。lparamでは現在のオブジェクトIDを送信し、dparamでは現在のオブジェクトの基本オブジェクトがバインドされている基本オブジェクトのタイプを送信し、'sparam'では3つのオブジェクト(メイン、基本)の名前を含む文字列を渡します。およびcurrent)は「;」で区切られています。イベントを受信すると、このデータを使用して、イベントメッセージがどのオブジェクトから来たのかを正確に判断できます。

現時点では、このロジックはイベントを生成したオブジェクトを特定するのに十分ですが、ネストのより深い階層を持つより複雑なコントロールを作成するときにはオブジェクトのお互いのネスト全体を相互に追跡できないため、後で変更します。

次に、イベントメッセージの送信をWinFormsオブジェクトのイベントハンドラに追加しましょう。

\MQL5\Include\DoEasy\Objects\Graph\WForms\CommonControls\Button.mqhボタンオブジェクトクラスファイル、つまり「カーソルがアクティブ領域内にあり、マウスの左ボタンがクリックされた」イベントハンドラに、メッセージ:送信イベントを追加します

//+------------------------------------------------------------------+
//| 'The cursor is inside the active area,                           |
//| left mouse button released                                       |
//+------------------------------------------------------------------+
void CButton::MouseActiveAreaReleasedHandler(const int id,const long& lparam,const double& dparam,const string& sparam)
  {
//--- The mouse button released outside the element means refusal to interact with the element
   if(lparam<this.CoordX() || lparam>this.RightEdge() || dparam<this.CoordY() || dparam>this.BottomEdge())
     {
      //--- If this is a simple button, set the initial background and text color
      if(!this.Toggle())
        {
         this.SetBackgroundColor(this.BackgroundColorInit(),false);
         this.SetForeColor(this.ForeColorInit(),false);
        }
      //--- If this is the toggle button, set the initial background and text color depending on whether the button is pressed or not
      else
        {
         this.SetBackgroundColor(!this.State() ? this.BackgroundColorInit() : this.BackgroundStateOnColorInit(),false);
         this.SetForeColor(!this.State() ? this.ForeColorInit() : this.ForeStateOnColorInit(),false);
        }
      //--- Set the initial frame color
      this.SetBorderColor(this.BorderColorInit(),false);
      //--- Send the event:
      this.SendEvent(::ChartID(),WF_CONTROL_EVENT_CLICK_CANCEL);
      //--- Send the test message to the journal
      Print(DFUN_ERR_LINE,TextByLanguage("Отмена","Cancel"));
     }
//--- The mouse button released within the element means a  click on the control
   else
     {
      //--- If this is a simple button, set the background and text color for "The cursor is over the active area" status
      if(!this.Toggle())
        {
         this.SetBackgroundColor(this.BackgroundColorMouseOver(),false);
         this.SetForeColor(this.ForeColorMouseOver(),false);
        }
      //--- If this is the toggle button,
      else
        {
         //--- if the button does not work in the group, set its state to the opposite,
         if(!this.GroupButtonFlag())
            this.SetState(!this.State());
         //--- if the button is not pressed yet, set it to the pressed state
         else if(!this.State())
            this.SetState(true);
         //--- set the background and text color for "The cursor is over the active area" status depending on whether the button is clicked or not
         this.SetBackgroundColor(this.State() ? this.BackgroundStateOnColorMouseOver() : this.BackgroundColorMouseOver(),false);
         this.SetForeColor(this.State() ? this.ForeStateOnColorMouseOver() : this.ForeColorMouseOver(),false);
        }
      
      //--- Send the event:
      this.SendEvent(::ChartID(),WF_CONTROL_EVENT_CLICK);
      //--- Send the test message to the journal
      Print(DFUN_ERR_LINE,TextByLanguage("Щелчок","Click"),", this.State()=",this.State(),", ID=",this.ID(),", Group=",this.Group());
      //--- Set the frame color for "The cursor is over the active area" status
      this.SetBorderColor(this.BorderColorMouseOver(),false);
     }
//--- Redraw the object
   this.Redraw(false);
  }
//+------------------------------------------------------------------+


\MQL5\Include\DoEasy\Objects\Graph\WForms\CommonControls\RadioButton.mqh

//+------------------------------------------------------------------+
//| 'The cursor is inside the active area,                           |
//| left mouse button released                                       |
//+------------------------------------------------------------------+
void CRadioButton::MouseActiveAreaReleasedHandler(const int id,const long& lparam,const double& dparam,const string& sparam)
  {
//--- The mouse button released outside the element means refusal to interact with the element
   if(lparam<this.CoordX() || lparam>this.RightEdge() || dparam<this.CoordY() || dparam>this.BottomEdge())
     {
      this.SetCheckBackgroundColor(this.BackgroundColorInit(),false);
      this.SetCheckBorderColor(this.CheckBorderColorInit(),false);
      this.SetCheckFlagColor(this.CheckFlagColorInit(),false);
      //--- Send the event:
      this.SendEvent(::ChartID(),WF_CONTROL_EVENT_CLICK_CANCEL);
      //--- Send a test entry to the journal
      Print(DFUN_ERR_LINE,TextByLanguage("Отмена","Cancel"));
     }
//--- The mouse button released within the element means a  click on the control
   else
     {
      this.SetCheckBackgroundColor(this.CheckBackgroundColorMouseOver(),false);
      this.SetCheckBorderColor(this.CheckBorderColorMouseOver(),false);
      this.SetCheckFlagColor(this.CheckFlagColorInit(),false);
      if(!this.Checked())
         this.SetChecked(true);
      //--- Send the event:
      this.SendEvent(::ChartID(),WF_CONTROL_EVENT_CLICK);
      //--- Send a test entry to the journal
      Print(DFUN_ERR_LINE,TextByLanguage("Щелчок","Click"),", this.Checked()=",this.Checked(),", ID=",this.ID(),", Group=",this.Group());
     }
   this.Redraw(false);
  }
//+------------------------------------------------------------------+


\MQL5\Include\DoEasy\Objects\Graph\WForms\CommonControls\CheckBox.mqh

//+------------------------------------------------------------------+
//| 'The cursor is inside the active area,                           |
//| left mouse button released                                       |
//+------------------------------------------------------------------+
void CCheckBox::MouseActiveAreaReleasedHandler(const int id,const long& lparam,const double& dparam,const string& sparam)
  {
//--- The mouse button released outside the element means refusal to interact with the element
   if(lparam<this.CoordX() || lparam>this.RightEdge() || dparam<this.CoordY() || dparam>this.BottomEdge())
     {
      this.SetCheckBackgroundColor(this.CheckBackgroundColorInit(),false);
      this.SetCheckBorderColor(this.CheckBorderColorInit(),false);
      this.SetCheckFlagColor(this.CheckFlagColorInit(),false);
      this.SetBackgroundColor(this.BackgroundColorInit(),false);
      //--- Send the event:
      this.SendEvent(::ChartID(),WF_CONTROL_EVENT_CLICK_CANCEL);
      //--- Send a test entry to the journal
      Print(DFUN_ERR_LINE,TextByLanguage("Отмена","Cancel"));
     }
//--- The mouse button released within the element means a  click on the control
   else
     {
      this.SetCheckBackgroundColor(this.CheckBackgroundColorMouseOver(),false);
      this.SetCheckBorderColor(this.CheckBorderColorMouseOver(),false);
      this.SetCheckFlagColor(this.CheckFlagColorInit(),false);
      this.SetBackgroundColor(this.BackgroundColorMouseOver(),false);
      this.SetChecked(!this.Checked());
      //--- Send the event:
      this.SendEvent(::ChartID(),WF_CONTROL_EVENT_CLICK);
      //--- Send a test entry to the journal
      Print(DFUN_ERR_LINE,TextByLanguage("Щелчок","Click"),", this.Checked()=",this.Checked(),", ID=",this.ID());
     }
   this.Redraw(false);
  }
//+------------------------------------------------------------------+


前回の記事を公開した後、左右と上下の矢印を持つボタンオブジェクトの2つのクラスファイルが単独でコンパイルされなくなったことに気付きました。これらはライブラリの一部としてのみコンパイルできます(Engine.mqhライブラリのメインファイルをコンパイルするとき)が、それ自体ではコンパイルできません。これは正しくありません。これを修正するには、これらのオブジェクトのファイルにインクルードされるファイルのリストを変更する必要があります。
以前は、パネルオブジェクトファイルがインクルードされていました

//+------------------------------------------------------------------+
//| Include files                                                    |
//+------------------------------------------------------------------+
#include "..\Containers\Panel.mqh"
//+------------------------------------------------------------------+

これらのクラスで使用する必要があるファイルのみをインクルードするようにします。

上下矢印のボタンオブジェクトファイル:\MQL5\Include\DoEasy\Objects\Graph\WForms\Helpers\ArrowUpDownBox.mqh

//+------------------------------------------------------------------+
//|                                               ArrowUpDownBox.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 "..\Containers\Container.mqh"
#include "..\Helpers\ArrowUpButton.mqh"
#include "..\Helpers\ArrowDownButton.mqh"
//+------------------------------------------------------------------+


左右矢印を持つボタンオブジェクトファイル:\MQL5\Include\DoEasy\Objects\Graph\WForms\Helpers\ArrowLeftRightBox.mqh

//+------------------------------------------------------------------+
//|                                            ArrowLeftRightBox.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 "..\Containers\Container.mqh"
#include "..\Helpers\ArrowLeftButton.mqh"
#include "..\Helpers\ArrowRightButton.mqh"
//+------------------------------------------------------------------+

これで、両方のファイルが独立してライブラリの一部として正常にコンパイルされます。


\MQL5\Include\DoEasy\Objects\Graph\WForms\Helpers\TabHeader.mqhで、TabControlのタブヘッダーオブジェクトのクラスを完成させましょう。

「カーソルがアクティブ領域内にあり、マウスの左ボタンがクリックされた」イベントハンドラから、イベントを作成するためのコードブロックを削除します

      //--- Send the test message to the journal
      Print(DFUN_ERR_LINE,TextByLanguage("Щелчок","Click"),", this.State()=",this.State(),", ID=",this.ID(),", Group=",this.Group());
      //--- Create the event:
      //--- Get the base and main objects
      CWinFormBase *base=this.GetBase();
      CWinFormBase *main=this.GetMain();
      //--- in the 'long' event parameter, pass a string, while in the 'double' parameter, the tab header location column
      long lp=this.Row();
      double dp=this.Column();
      //--- in the 'string' parameter of the event, pass the names of the main and base objects separated by ";"
      string name_main=(main!=NULL ? main.Name() : "");
      string name_base=(base!=NULL ? base.Name() : "");
      string sp=name_main+";"+name_base;
      //--- Send the tab selection event to the chart of the control program
      ::EventChartCustom(::ChartID(),WF_CONTROL_EVENT_TAB_SELECT,lp,dp,sp);
      //--- Set the frame color for "The cursor is over the active area" status
      this.SetBorderColor(this.BorderColorMouseOver(),false);
     }
  }
//+------------------------------------------------------------------+

イベントを作成して送信するメソッドができたので、使ってみましょう

//+------------------------------------------------------------------+
//| 'The cursor is inside the active area,                           |
//| left mouse button released                                       |
//+------------------------------------------------------------------+
void CTabHeader::MouseActiveAreaReleasedHandler(const int id,const long& lparam,const double& dparam,const string& sparam)
  {
//--- The mouse button released outside the element means refusal to interact with the element
   if(lparam<this.CoordX() || lparam>this.RightEdge() || dparam<this.CoordY() || dparam>this.BottomEdge())
     {
      //--- If this is a simple button, set the initial background and text color
      if(!this.Toggle())
        {
         this.SetBackgroundColor(this.BackgroundColorInit(),false);
         this.SetForeColor(this.ForeColorInit(),false);
        }
      //--- If this is the toggle button, set the initial background and text color depending on whether the button is pressed or not
      else
        {
         this.SetBackgroundColor(!this.State() ? this.BackgroundColorInit() : this.BackgroundStateOnColorInit(),false);
         this.SetForeColor(!this.State() ? this.ForeColorInit() : this.ForeStateOnColorInit(),false);
        }
      //--- Set the initial frame color
      this.SetBorderColor(this.BorderColorInit(),false);
      //--- Send the event:
      this.SendEvent(::ChartID(),WF_CONTROL_EVENT_CLICK_CANCEL);
      //--- Send the test message to the journal
      Print(DFUN_ERR_LINE,TextByLanguage("Отмена","Cancel"));
     }
//--- The mouse button released within the element means a  click on the control
   else
     {
      //--- If this is a simple button, set the background and text color for "The cursor is over the active area" status
      if(!this.Toggle())
        {
         this.SetBackgroundColor(this.BackgroundColorMouseOver(),false);
         this.SetForeColor(this.ForeColorMouseOver(),false);
        }
      //--- If this is the toggle button,
      else
        {
         //--- if the button does not work in the group, set its state to the opposite,
         if(!this.GroupButtonFlag())
            this.SetState(!this.State());
         //--- if the button is not pressed yet, set it to the pressed state
         else if(!this.State())
            this.SetState(true);
         //--- set the background and text color for "The cursor is over the active area" status depending on whether the button is clicked or not
         this.SetBackgroundColor(this.State() ? this.BackgroundStateOnColorMouseOver() : this.BackgroundColorMouseOver(),false);
         this.SetForeColor(this.State() ? this.ForeStateOnColorMouseOver() : this.ForeColorMouseOver(),false);
         
         //--- Get the field object corresponding to the header
         CWinFormBase *field=this.GetFieldObj();
         if(field!=NULL)
           {
            //--- Display the field, bring it to the front, draw a frame and crop the excess
            field.Show();
            field.BringToTop();
            field.DrawFrame();
            field.Crop();
           }
        }
      //--- Send the event:
      this.SendEvent(::ChartID(),WF_CONTROL_EVENT_TAB_SELECT);
      //--- Send the test message to the journal
      Print(DFUN_ERR_LINE,TextByLanguage("Щелчок","Click"),", this.State()=",this.State(),", ID=",this.ID(),", Group=",this.Group());
      //--- Set the frame color for "The cursor is over the active area" status
      this.SetBorderColor(this.BorderColorMouseOver(),false);
     }
//--- Redraw an object and a chart
   this.Redraw(true);
  }
//+------------------------------------------------------------------+


たとえば、ヘッダーバーを左にスクロールすると、一番左のヘッダーがコントロールの外に移動し、その右側のヘッダーが代わりに配置されます。ヘッダーの初期座標はコンテナの左端の右に2ピクセルだけシフトされているため、左端を超えるヘッダーは、この2ピクセルの領域で表示されたままになります。コンテナ領域の端で切り取られ、その内側に要素が表示されます。

左端を超えたヘッダーのこの薄い可視部分を非表示にするには、接続されたオブジェクトが表示されるコンテナ領域のサイズをわずかに調整する必要があります。さらに、ヘッダーが選択されているかどうかも考慮する必要があります。これは、選択されたヘッダーのサイズが両側で2ピクセルずつ大きくなるためです。つまり、端にあるオブジェクトに応じて、オブジェクトが表示されるコンテナの領域のサイズを動的に変更する必要があります。選択した場合、サイズは変更されません。それ以外の場合は、2ピクセル縮小されます。

計算された長方形の可視範囲によって輪郭が描かれた画像をトリミングするメソッドでコンテナの可視範囲のサイズを調整し、取得した値を可視領域の端に適用するようにします。

//+------------------------------------------------------------------+
//| Crop the image outlined by the calculated                        |
//| rectangular visibility scope                                     |
//+------------------------------------------------------------------+
void CTabHeader::Crop(void)
  {
//--- Get the pointer to the base object
   CGCnvElement *base=this.GetBase();
//--- If the object does not have a base object it is attached to, then there is no need to crop the hidden areas - leave
   if(base==NULL)
      return;
//--- Set the initial coordinates and size of the visibility scope to the entire object
   int vis_x=0;
   int vis_y=0;
   int vis_w=this.Width();
   int vis_h=this.Height();
//--- Set the size of the top, bottom, left and right areas that go beyond the container
   int crop_top=0;
   int crop_bottom=0;
   int crop_left=0;
   int crop_right=0;
//--- Get the additional size, by which to crop the titles when the arrow buttons are visible
   int add_size_lr=(this.IsVisibleLeftRightBox() ? this.m_arr_butt_lr_size : 0);
   int add_size_ud=(this.IsVisibleUpDownBox()    ? this.m_arr_butt_ud_size : 0);
   int dec_size_vis=(this.State() ? 0 : 2);
//--- Calculate the boundaries of the container area, inside which the object is fully visible
   int top=fmax(base.CoordY()+(int)base.GetProperty(CANV_ELEMENT_PROP_BORDER_SIZE_TOP),base.CoordYVisibleArea())+dec_size_vis+(this.Alignment()==CANV_ELEMENT_ALIGNMENT_LEFT ? add_size_ud : 0);
   int bottom=fmin(base.BottomEdge()-(int)base.GetProperty(CANV_ELEMENT_PROP_BORDER_SIZE_BOTTOM),base.BottomEdgeVisibleArea()+1)-dec_size_vis-(this.Alignment()==CANV_ELEMENT_ALIGNMENT_RIGHT ? add_size_ud : 0);
   int left=fmax(base.CoordX()+(int)base.GetProperty(CANV_ELEMENT_PROP_BORDER_SIZE_LEFT),base.CoordXVisibleArea())+dec_size_vis;
   int right=fmin(base.RightEdge()-(int)base.GetProperty(CANV_ELEMENT_PROP_BORDER_SIZE_RIGHT),base.RightEdgeVisibleArea()+1)-add_size_lr;
//--- Calculate the values of the top, bottom, left and right areas, at which the object goes beyond
//--- the boundaries of the container area, inside which the object is fully visible
   crop_top=this.CoordY()-top;
   if(crop_top<0)
      vis_y=-crop_top;
   crop_bottom=bottom-this.BottomEdge()-1;
   if(crop_bottom<0)
      vis_h=this.Height()+crop_bottom-vis_y;
   crop_left=this.CoordX()-left;
   if(crop_left<0)
      vis_x=-crop_left;
   crop_right=right-this.RightEdge()-1;
   if(crop_right<0)
      vis_w=this.Width()+crop_right-vis_x;
//--- If there are areas that need to be hidden, call the cropping method with the calculated size of the object visibility scope
   if(crop_top<0 || crop_bottom<0 || crop_left<0 || crop_right<0)
      this.Crop(vis_x,vis_y,vis_w,vis_h);
  }
//+------------------------------------------------------------------+

これで、ヘッダーがコンテナを超えると、サイズが2ピクセルの狭いセクションが表示されなくなりました。


TabControl\MQL5\Include\DoEasy\Objects\Graph\WForms\Containers\TabControl.mqhWinFormsオブジェクトクラスファイルのprivateセクションで、新しいメソッドを宣言します

//--- Return the list of (1) headers, (2) tab fields, the pointer to the (3) up-down and (4) left-right button objects
   CArrayObj        *GetListHeaders(void)          { return this.GetListElementsByType(GRAPH_ELEMENT_TYPE_WF_TAB_HEADER);        }
   CArrayObj        *GetListFields(void)           { return this.GetListElementsByType(GRAPH_ELEMENT_TYPE_WF_TAB_FIELD);         }
   CArrowUpDownBox  *GetArrUpDownBox(void)         { return this.GetElementByType(GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTONS_UD_BOX,0); }
   CArrowLeftRightBox *GetArrLeftRightBox(void)    { return this.GetElementByType(GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTONS_LR_BOX,0); }
   
//--- Return the pointer to the (1) last and (2) first visible tab header
   CTabHeader       *GetLastHeader(void)           { return this.GetTabHeader(this.TabPages()-1);                                }
   CTabHeader       *GetFirstVisibleHeader(void);
//--- Set the tab as selected
   void              SetSelected(const int index);
//--- Set the tab as released
   void              SetUnselected(const int index);
//--- Set the number of a selected tab
   void              SetSelectedTabPageNum(const int value) { this.SetProperty(CANV_ELEMENT_PROP_TAB_PAGE_NUMBER,value);         }
//--- Arrange the tab headers according to the set modes
   void              ArrangeTabHeaders(void);
//--- Arrange the tab headers at the (1) top, (2) bottom, (3) left and (4) right
   void              ArrangeTabHeadersTop(void);
   void              ArrangeTabHeadersBottom(void);
   void              ArrangeTabHeadersLeft(void);
   void              ArrangeTabHeadersRight(void);
//--- Stretch tab headers by control size
   void              StretchHeaders(void);
//--- Stretch tab headers by (1) control width and height when positioned on the (2) left and (3) right
   void              StretchHeadersByWidth(void);
   void              StretchHeadersByHeightLeft(void);
   void              StretchHeadersByHeightRight(void);
//--- Scroll the header row (1) to the left, (2) to the right, (3) up when headers are on the left, (4) down, (3) up, (4) down
   void              ScrollHeadersRowToLeft(void);
   void              ScrollHeadersRowToRight(void);
//--- Scroll the row of headers when they are located on the left (1) up, (2) down
   void              ScrollHeadersRowLeftToUp(void);
   void              ScrollHeadersRowLeftToDown(void);
//--- Scroll the row of headers when they are located on the right (1) up, (2) down
   void              ScrollHeadersRowRightToUp(void);
   void              ScrollHeadersRowRightToDown(void);
public:

publicメソッドを削除します

//--- Show the control
   virtual void      Show(void);
//--- Shift the header row
   void              ShiftHeadersRow(const int selected);
//--- Event handler
   virtual void      OnChartEvent(const int id,const long& lparam,const double& dparam,const string& sparam);

//--- Constructor

このpublicメソッドは前回の記事で作成しました。部分的に非表示のヘッダーがクリックされると、ヘッダーバーを左に移動していましたが、これは、上記で宣言されたメソッドによっておこなわれるようになります。さらに、これらのメソッドは、部分的に非表示になっているヘッダーのクリックと、ヘッダーバーのスクロールを管理するボタンのクリックの両方を処理します。

宣言された各メソッドは、ヘッダーバーを特定の方向にスクロールするように設計されています。

  • ヘッダーが上または下にある場合 - 左右にスクロールする2つのメソッド
  • ヘッダーが左側にある場合 - 左にスクロールするメソッドと右にスクロールするメソッドの2つ 
  • ヘッダーが右側にある場合 - 左にスクロールするメソッドと右にスクロールするメソッドの2つ

指定された数のタブを作成するメソッドで左右と上下の矢印を持つボタンオブジェクトを作成する場合、これらのオブジェクトのメインオブジェクトと基本オブジェクトおよびこれらのオブジェクト内の各矢印ボタンオブジェクトを指定する必要があります。そうしないと、ボタンがクリックされてイベントメッセージを作成するときに、これらのオブジェクトを見つけることができなくなります。

//+------------------------------------------------------------------+
//| Create the specified number of tabs                              |
//+------------------------------------------------------------------+
bool CTabControl::CreateTabPages(const int total,const int selected_page,const int tab_w=0,const int tab_h=0,const string header_text="")
  {
//--- Calculate the size and initial coordinates of the tab title
   int w=(tab_w==0 ? this.ItemWidth()  : tab_w);
   int h=(tab_h==0 ? this.ItemHeight() : tab_h);

//--- In the loop by the number of tabs

//---...
//---...

//--- Create left-right and up-down button objects
   this.CreateNewElement(GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTONS_LR_BOX,this.Width()-32,0,15,15,clrNONE,255,this.Active(),false);
   this.CreateNewElement(GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTONS_UD_BOX,0,this.Height()-32,15,15,clrNONE,255,this.Active(),false);
//--- 
   CArrowLeftRightBox *box_lr=this.GetArrLeftRightBox();
   if(box_lr!=NULL)
     {
      this.SetVisibleLeftRightBox(false);
      this.SetSizeLeftRightBox(box_lr.Width());
      box_lr.SetMain(this.GetMain());
      box_lr.SetBase(this.GetObject());
      box_lr.SetBorderStyle(FRAME_STYLE_NONE);
      box_lr.SetBackgroundColor(CLR_CANV_NULL,true);
      box_lr.SetOpacity(0);
      box_lr.Hide();
      CArrowLeftButton *lb=box_lr.GetArrowLeftButton();
      if(lb!=NULL)
        {
         lb.SetMain(this.GetMain());
         lb.SetBase(box_lr);
        }
      CArrowRightButton *rb=box_lr.GetArrowRightButton();
      if(rb!=NULL)
        {
         rb.SetMain(this.GetMain());
         rb.SetBase(box_lr);
        }
     }
//---
   CArrowUpDownBox *box_ud=this.GetArrUpDownBox();
   if(box_ud!=NULL)
     {
      this.SetVisibleUpDownBox(false);
      this.SetSizeUpDownBox(box_ud.Height());
      box_ud.SetMain(this.GetMain());
      box_ud.SetBase(this.GetObject());
      box_ud.SetBorderStyle(FRAME_STYLE_NONE);
      box_ud.SetBackgroundColor(CLR_CANV_NULL,true);
      box_ud.SetOpacity(0);
      box_ud.Hide();
      CArrowDownButton *db=box_ud.GetArrowDownButton();
      if(db!=NULL)
        {
         db.SetMain(this.GetMain());
         db.SetBase(box_ud);
        }
      CArrowUpButton *ub=box_ud.GetArrowUpButton();
      if(ub!=NULL)
        {
         ub.SetMain(this.GetMain());
         ub.SetBase(box_ud);
        }
     }

//--- Arrange all titles in accordance with the specified display modes and select the specified tab
   this.ArrangeTabHeaders();
   this.Select(selected_page,true);
   return true;
  }
//+------------------------------------------------------------------+

左右と上下の矢印でボタンオブジェクトを作成した後、作成されたオブジェクトへのポインタを取得し、メインオブジェクトと基本オブジェクトを設定します。受け取ったオブジェクトから、その矢印ボタンオブジェクトを取得し、それぞれのメインオブジェクトと基本オブジェクトを指定します。

コントロールを表示するメソッドにオブジェクト表示フラグの確認を追加します

//+------------------------------------------------------------------+
//| Show the control                                                 |
//+------------------------------------------------------------------+
void CTabControl::Show(void)
  {
//--- If the element should not be displayed (hidden inside another control), leave
   if(!this.Displayed())
      return;
//--- Get the list of all tab headers
   CArrayObj *list=this.GetListHeaders();
   if(list==NULL)
      return;
//--- If the object has a shadow, display it
   if(this.m_shadow_obj!=NULL)
      this.m_shadow_obj.Show();
//--- Display the container
   CGCnvElement::Show();
//--- Move all elements of the object to the foreground
   this.BringToTop();
  }
//+------------------------------------------------------------------+

オブジェクトに対して手動表示管理モードが有効になっている場合は、メソッドを終了します。


以下は、最初に表示されるヘッダーへのポインタを返すメソッドです。

//+------------------------------------------------------------------+
//| Return the pointer to the first visible header                   |
//+------------------------------------------------------------------+
CTabHeader *CTabControl::GetFirstVisibleHeader(void)
  {
   for(int i=0;i<this.TabPages();i++)
     {
      CTabHeader *obj=this.GetTabHeader(i);
      if(obj==NULL)
         continue;
      switch(this.Alignment())
        {
         case CANV_ELEMENT_ALIGNMENT_TOP     :
         case CANV_ELEMENT_ALIGNMENT_BOTTOM  :
           if(obj.CoordX()==this.CoordXWorkspace()+(obj.State() ? 0 : 2))
              return obj;
           break;
         case CANV_ELEMENT_ALIGNMENT_LEFT  :
           if(obj.BottomEdge()==this.BottomEdgeWorkspace()+(obj.State() ? 0 : -2))
              return obj;
           break;
         case CANV_ELEMENT_ALIGNMENT_RIGHT  :
           if(obj.CoordY()==this.CoordYWorkspace()+(obj.State() ? 0 : 2))
              return obj;
           break;
         default:
           break;
        }
     }
   return NULL;
  }
//+------------------------------------------------------------------+

最初に表示されるヘッダーは、ヘッダーが上部/下部にある場合は左側、ヘッダーが左側にある場合は下部、ヘッダーがコントロールの右側にある場合は上部です。この極端なヘッダーを見つけるには、オブジェクトのすべてのヘッダーをループして、ヘッダー行の位置に従ってその位置の座標を確認する必要があります。上部に配置するには、ヘッダーをコンテナの開始X座標に配置する必要があります。この場合、タイトルが選択されていない場合、その初期座標は右に2ピクセルシフトされます。状況は、ヘッダーバーの別の場所でも同様です。
ループ内のメソッドは、ヘッダーバーの位置に応じて、オブジェクトの座標とコンテナの座標の一致を探し、見つかったオブジェクトへのポインタを返します。ヘッダーが見つからない場合、メソッドはNULLを返します。


以下は、ヘッダーバーを左にスクロールするメソッド:です。

//+------------------------------------------------------------------+
//| Scroll the header bar to the left                                |
//+------------------------------------------------------------------+
void CTabControl::ScrollHeadersRowToLeft(void)
  {
//--- If there are multiline headers, leave
   if(this.Multiline())
      return;
//--- Declare the variables and get the index of the selected tab
   int shift=0;
   int correct_size=0;
   int selected=this.SelectedTabPageNum();
//--- Get the first visible header
   CTabHeader *first=this.GetFirstVisibleHeader();
   if(first==NULL)
      return;
//--- If the first visible header is selected, set the size adjustment value
   if(first.PageNumber()==selected)
      correct_size=4;
//--- Get the pointer to the very last header
   CTabHeader *last=this.GetLastHeader();
   if(last==NULL)
      return;
//--- If the last heading is fully visible, leave since the shift of all headers to the left is completed
   if(last.RightEdge()<=this.RightEdgeWorkspace())
      return;
//--- Get the shift size
   shift=first.Width()-correct_size;
//--- In the loop by all headers
   for(int i=0;i<this.TabPages();i++)
     {
      //--- get the next header
      CTabHeader *header=this.GetTabHeader(i);
      if(header==NULL)
         continue;
      //--- and, if the header is successfully shifted to the left by 'shift' value,
      if(header.Move(header.CoordX()-shift,header.CoordY()))
        {
         //--- save its new relative coordinates
         header.SetCoordXRelative(header.CoordX()-this.CoordX());
         header.SetCoordYRelative(header.CoordY()-this.CoordY());
         //--- If the title has gone beyond the left edge,
         int x=(i==selected ? 0 : 2);
         if(header.CoordX()-x<this.CoordXWorkspace())
           {
            //--- crop and hide it
            header.Crop();
            header.Hide();
            //--- Get the selected header
            CTabHeader *header_selected=this.GetTabHeader(selected);
            if(header_selected==NULL)
               continue;
            //--- Get the tab field corresponding to the selected header
            CTabField *field_selected=header_selected.GetFieldObj();
            if(field_selected==NULL)
               continue;
            //--- Draw the field frame
            field_selected.DrawFrame();
            field_selected.Update();
           }
         //--- If the header fits the visible area of the control,
         else
           {
            //--- display and redraw it
            header.Show();
            header.Redraw(false);
            //--- Get the tab field corresponding to the header
            CTabField *field=header.GetFieldObj();
            if(field==NULL)
               continue;
            //--- If this is a selected header,
            if(i==selected)
              {
               //--- Draw the field frame
               field.DrawFrame();
               field.Update();
              }
           }
        }
     }
//--- Get the selected header
   CTabHeader *obj=this.GetTabHeader(selected);
//--- If the header is placed in the visible part of the control, bring it to the foreground
   if(obj!=NULL && obj.CoordX()>=this.CoordXWorkspace() && obj.RightEdge()<=this.RightEdgeWorkspace())
      obj.BringToTop();
//--- Redraw the chart to display changes immediately
   ::ChartRedraw(this.ChartID());
  }
//+------------------------------------------------------------------+


以下は、ヘッダーバーを右にスクロールするメソッドです。

//+------------------------------------------------------------------+
//| Scroll the header bar to the right                               |
//+------------------------------------------------------------------+
void CTabControl::ScrollHeadersRowToRight(void)
  {
//--- If there are multiline headers, leave
   if(this.Multiline())
      return;
//--- Declare the variables and get the index of the selected tab
   int shift=0;
   int correct_size=0;
   int selected=this.SelectedTabPageNum();
//--- Get the first visible header
   CTabHeader *first=this.GetFirstVisibleHeader();
   if(first==NULL)
      return;
//--- Get the header located before the first visible one
   CTabHeader *prev=this.GetTabHeader(first.PageNumber()-1);
//--- If there is no such header, leave since the shift of all headers to the right is completed
   if(prev==NULL)
      return;
//--- If the header is selected, specify the size adjustment value
   if(prev.PageNumber()==selected)
      correct_size=4;
//--- Get the shift size
   shift=prev.Width()-correct_size;
//--- In the loop by all headers
   for(int i=0;i<this.TabPages();i++)
     {
      //--- get the next header
      CTabHeader *header=this.GetTabHeader(i);
      if(header==NULL)
         continue;
      //--- and, if the header is successfully shifted to the right by 'shift' value,
      if(header.Move(header.CoordX()+shift,header.CoordY()))
        {
         //--- save its new relative coordinates
         header.SetCoordXRelative(header.CoordX()-this.CoordX());
         header.SetCoordYRelative(header.CoordY()-this.CoordY());
         //--- If the title goes beyond the left edge,
         int x=(i==selected ? 0 : 2);
         if(header.CoordX()-x<this.CoordXWorkspace())
           {
            //--- crop and hide it
            header.Crop();
            header.Hide();
           }
         //--- If the header fits the visible area of the control,
         else
           {
            //--- display and redraw it
            header.Show();
            header.Redraw(false);
            //--- Get the tab field corresponding to the header
            CTabField *field=header.GetFieldObj();
            if(field==NULL)
               continue;
            //--- If this is a selected header,
            if(i==selected)
              {
               //--- Draw the field frame
               field.DrawFrame();
               field.Update();
              }
           }
        }
     }
//--- Get the selected header
   CTabHeader *obj=this.GetTabHeader(selected);
//--- If the header is placed in the visible part of the control, bring it to the foreground
   if(obj!=NULL && obj.CoordX()>=this.CoordXWorkspace() && obj.RightEdge()<=this.RightEdgeWorkspace())
      obj.BringToTop();
//--- Redraw the chart to display changes immediately
   ::ChartRedraw(this.ChartID());
  }
//+------------------------------------------------------------------+


以下は、ヘッダーが左側にあるときにヘッダー行を上にスクロールするメソッドです。

//+------------------------------------------------------------------+
//| Scroll the header row up when the headers are on the left        |
//+------------------------------------------------------------------+
void CTabControl::ScrollHeadersRowLeftToUp(void)
  {
//--- If there are multiline headers, leave
   if(this.Multiline())
      return;
//--- Declare the variables and get the index of the selected tab
   int shift=0;
   int correct_size=0;
   int selected=this.SelectedTabPageNum();
//--- Get the first visible header
   CTabHeader *first=this.GetFirstVisibleHeader();
   if(first==NULL)
      return;
//--- Get the header located before the first visible one
   CTabHeader *prev=this.GetTabHeader(first.PageNumber()-1);
//--- If there is no such header, leave since the shift of all headers upwards is completed
   if(prev==NULL)
      return;
//--- If the header is selected, specify the size adjustment value
   if(prev.PageNumber()==selected)
      correct_size=4;
//--- Get the shift size
   shift=prev.Height()-correct_size;
//--- In the loop by all headers
   for(int i=0;i<this.TabPages();i++)
     {
      //--- get the next header
      CTabHeader *header=this.GetTabHeader(i);
      if(header==NULL)
         continue;
      //--- and, if the header is successfully shifted upwards by 'shift' value,
      if(header.Move(header.CoordX(),header.CoordY()-shift))
        {
         //--- save its new relative coordinates
         header.SetCoordXRelative(header.CoordX()-this.CoordX());
         header.SetCoordYRelative(header.CoordY()-this.CoordY());
         //--- If the header goes beyond the lower edge,
         int x=(i==selected ? 0 : 2);
         if(header.BottomEdge()+x>this.BottomEdgeWorkspace())
           {
            //--- crop and hide it
            header.Crop();
            header.Hide();
           }
         //--- If the header fits the visible area of the control,
         else
           {
            //--- display and redraw it
            header.Show();
            header.Redraw(false);
            //--- Get the tab field corresponding to the header
            CTabField *field=header.GetFieldObj();
            if(field==NULL)
               continue;
            //--- If this is a selected header,
            if(i==selected)
              {
               //--- Draw the field frame
               field.DrawFrame();
               field.Update();
              }
           }
        }
     }
//--- Get the selected header
   CTabHeader *obj=this.GetTabHeader(selected);
//--- If the header is placed in the visible part of the control, bring it to the foreground
   if(obj!=NULL && obj.CoordY()>=this.CoordYWorkspace() && obj.BottomEdge()<=this.BottomEdgeWorkspace())
      obj.BringToTop();
//--- Redraw the chart to display changes immediately
   ::ChartRedraw(this.ChartID());
  }
//+------------------------------------------------------------------+


以下は、ヘッダーが左側にあるときにヘッダー行を下にスクロールするメソッドです。

//+------------------------------------------------------------------+
//| Scroll the header row down when the headers are on the left      |
//+------------------------------------------------------------------+
void CTabControl::ScrollHeadersRowLeftToDown(void)
  {
//--- If there are multiline headers, leave
   if(this.Multiline())
      return;
//--- Declare the variables and get the index of the selected tab
   int shift=0;
   int correct_size=0;
   int selected=this.SelectedTabPageNum();
//--- Get the first visible header
   CTabHeader *first=this.GetFirstVisibleHeader();
   if(first==NULL)
      return;
//--- If the first visible header is selected, set the size adjustment value
   if(first.PageNumber()==selected)
      correct_size=4;
//--- Get the pointer to the very last header
   CTabHeader *last=this.GetLastHeader();
   if(last==NULL)
      return;
//--- If the last heading is fully visible, leave since the shift of all headers downwards is completed
   if(last.CoordY()>=this.CoordYWorkspace())
      return;
//--- Get the shift size
   shift=first.Height()-correct_size;
//--- In the loop by all headers
   for(int i=0;i<this.TabPages();i++)
     {
      //--- get the next header
      CTabHeader *header=this.GetTabHeader(i);
      if(header==NULL)
         continue;
      //--- and, if the header is successfully shifted downwards by 'shift' value,
      if(header.Move(header.CoordX(),header.CoordY()+shift))
        {
         //--- save its new relative coordinates
         header.SetCoordXRelative(header.CoordX()-this.CoordX());
         header.SetCoordYRelative(header.CoordY()-this.CoordY());
         //--- If the header has gone beyond the lower edge,
         int x=(i==selected ? 0 : 2);
         if(header.BottomEdge()-x>this.BottomEdgeWorkspace())
           {
            //--- crop and hide it
            header.Crop();
            header.Hide();
            //--- Get the selected header
            CTabHeader *header_selected=this.GetTabHeader(selected);
            if(header_selected==NULL)
               continue;
            //--- Get the tab field corresponding to the selected header
            CTabField *field_selected=header_selected.GetFieldObj();
            if(field_selected==NULL)
               continue;
            //--- Draw the field frame
            field_selected.DrawFrame();
            field_selected.Update();
           }
         //--- If the header fits the visible area of the control,
         else
           {
            //--- display and redraw it
            header.Show();
            header.Redraw(false);
            //--- Get the tab field corresponding to the header
            CTabField *field=header.GetFieldObj();
            if(field==NULL)
               continue;
            //--- If this is a selected header,
            if(i==selected)
              {
               //--- Draw the field frame
               field.DrawFrame();
               field.Update();
              }
           }
        }
     }
//--- Get the selected header
   CTabHeader *obj=this.GetTabHeader(selected);
//--- If the header is placed in the visible part of the control, bring it to the foreground
   if(obj!=NULL && obj.CoordY()>=this.CoordYWorkspace() && obj.BottomEdge()<=this.BottomEdgeWorkspace())
      obj.BringToTop();
//--- Redraw the chart to display changes immediately
   ::ChartRedraw(this.ChartID());
  }
//+------------------------------------------------------------------+


以下は、ヘッダーが右側にあるときにヘッダー行を上にスクロールするメソッドです。

//+------------------------------------------------------------------+
//| Scroll the header row up when the headers are on the right       |
//+------------------------------------------------------------------+
void CTabControl::ScrollHeadersRowRightToUp(void)
  {
//--- If there are multiline headers, leave
   if(this.Multiline())
      return;
//--- Declare the variables and get the index of the selected tab
   int shift=0;
   int correct_size=0;
   int selected=this.SelectedTabPageNum();
//--- Get the first visible header
   CTabHeader *first=this.GetFirstVisibleHeader();
   if(first==NULL)
      return;
//--- If the first visible header is selected, set the size adjustment value
   if(first.PageNumber()==selected)
      correct_size=4;
//--- Get the pointer to the very last header
   CTabHeader *last=this.GetLastHeader();
   if(last==NULL)
      return;
//--- If the last heading is fully visible, leave since the shift of all headers upwards is completed
   if(last.BottomEdge()<=this.BottomEdgeWorkspace())
      return;
//--- Get the shift size
   shift=first.Height()-correct_size;
//--- In the loop by all headers
   for(int i=0;i<this.TabPages();i++)
     {
      //--- get the next header
      CTabHeader *header=this.GetTabHeader(i);
      if(header==NULL)
         continue;
      //--- and, if the header is successfully shifted upwards by 'shift' value,
      if(header.Move(header.CoordX(),header.CoordY()-shift))
        {
         //--- save its new relative coordinates
         header.SetCoordXRelative(header.CoordX()-this.CoordX());
         header.SetCoordYRelative(header.CoordY()-this.CoordY());
         //--- If the header has gone beyond the upper edge,
         int x=(i==selected ? 0 : 2);
         if(header.CoordY()-x<this.CoordYWorkspace())
           {
            //--- crop and hide it
            header.Crop();
            header.Hide();
            //--- Get the selected header
            CTabHeader *header_selected=this.GetTabHeader(selected);
            if(header_selected==NULL)
               continue;
            //--- Get the tab field corresponding to the selected header
            CTabField *field_selected=header_selected.GetFieldObj();
            if(field_selected==NULL)
               continue;
            //--- Draw the field frame
            field_selected.DrawFrame();
            field_selected.Update();
           }
         //--- If the header fits the visible area of the control,
         else
           {
            //--- display and redraw it
            header.Show();
            header.Redraw(false);
            //--- Get the tab field corresponding to the header
            CTabField *field=header.GetFieldObj();
            if(field==NULL)
               continue;
            //--- If this is a selected header,
            if(i==selected)
              {
               //--- Draw the field frame
               field.DrawFrame();
               field.Update();
              }
           }
        }
     }
//--- Get the selected header
   CTabHeader *obj=this.GetTabHeader(selected);
//--- If the header is placed in the visible part of the control, bring it to the foreground
   if(obj!=NULL && obj.CoordY()>=this.CoordYWorkspace() && obj.BottomEdge()<=this.BottomEdgeWorkspace())
      obj.BringToTop();
//--- Redraw the chart to display changes immediately
   ::ChartRedraw(this.ChartID());
  }
//+------------------------------------------------------------------+


以下は、ヘッダーが右側にあるときにヘッダー行を下にスクロールするメソッドです。

//+------------------------------------------------------------------+
//| Scroll the header row down when the headers are on the right     |
//+------------------------------------------------------------------+
void CTabControl::ScrollHeadersRowRightToDown(void)
  {
//--- If there are multiline headers, leave
   if(this.Multiline())
      return;
//--- Declare the variables and get the index of the selected tab
   int shift=0;
   int correct_size=0;
   int selected=this.SelectedTabPageNum();
//--- Get the first visible header
   CTabHeader *first=this.GetFirstVisibleHeader();
   if(first==NULL)
      return;
//--- Get the header located before the first visible one
   CTabHeader *prev=this.GetTabHeader(first.PageNumber()-1);
//--- If there is no such header, leave since the shift of all headers downwards is completed
   if(prev==NULL)
      return;
//--- If the header is selected, specify the size adjustment value
   if(prev.PageNumber()==selected)
      correct_size=4;
//--- Get the shift size
   shift=prev.Height()-correct_size;
//--- In the loop by all headers
   for(int i=0;i<this.TabPages();i++)
     {
      //--- get the next header
      CTabHeader *header=this.GetTabHeader(i);
      if(header==NULL)
         continue;
      //--- and, if the header is successfully shifted downwards by 'shift' value,
      if(header.Move(header.CoordX(),header.CoordY()+shift))
        {
         //--- save its new relative coordinates
         header.SetCoordXRelative(header.CoordX()-this.CoordX());
         header.SetCoordYRelative(header.CoordY()-this.CoordY());
         //--- If the title goes beyond the upper edge
         int x=(i==selected ? 0 : 2);
         if(header.CoordY()-x<this.CoordYWorkspace())
           {
            //--- crop and hide it
            header.Crop();
            header.Hide();
           }
         //--- If the header fits the visible area of the control,
         else
           {
            //--- display and redraw it
            header.Show();
            header.Redraw(false);
            //--- Get the tab field corresponding to the header
            CTabField *field=header.GetFieldObj();
            if(field==NULL)
               continue;
            //--- If this is a selected header,
            if(i==selected)
              {
               //--- Draw the field frame
               field.DrawFrame();
               field.Update();
              }
           }
        }
     }
//--- Get the selected header
   CTabHeader *obj=this.GetTabHeader(selected);
//--- If the header is placed in the visible part of the control, bring it to the foreground
   if(obj!=NULL && obj.CoordY()>=this.CoordYWorkspace() && obj.BottomEdge()<=this.BottomEdgeWorkspace())
      obj.BringToTop();
//--- Redraw the chart to display changes immediately
   ::ChartRedraw(this.ChartID());
  }
//+------------------------------------------------------------------+

ヘッダー行をスクロールするためのすべてのメソッドのロジックは、メソッドコードに完全に記述されています。それらはすべて互いに同一であり、シフト計算と可視範囲に関してわずかに異なるだけです。メソッドに追加の説明が必要ないことを願っています。質問がある場合は、下のコメント欄でお気軽にお問い合わせください。

以下はイベントハンドラです。

//+------------------------------------------------------------------+
//| Event handler                                                    |
//+------------------------------------------------------------------+
void CTabControl::OnChartEvent(const int id,const long &lparam,const double &dparam,const string &sparam)
  {
//--- Adjust subwindow Y shift
   CGCnvElement::OnChartEvent(id,lparam,dparam,sparam);

//--- If the tab is selected
   if(id==WF_CONTROL_EVENT_TAB_SELECT)
     {
      //--- Get the header of the selected tab
      CTabHeader *header=this.GetTabHeader(this.SelectedTabPageNum());
      if(header==NULL)
         return;
      //--- Depending on the location of the header row
      switch(this.Alignment())
        {
         //--- Headers at the top/bottom
         case CANV_ELEMENT_ALIGNMENT_TOP     :
         case CANV_ELEMENT_ALIGNMENT_BOTTOM  :
            //--- If the header is cropped, shift the header row to the left
            if(header.RightEdge()>this.RightEdgeWorkspace())
               this.ScrollHeadersRowToLeft();
            break;
         //--- Headers on the left
         case CANV_ELEMENT_ALIGNMENT_LEFT    :
            //--- If the header is cropped, shift the header row downwards
            if(header.CoordY()<this.CoordYWorkspace())
               this.ScrollHeadersRowLeftToDown();
            break;
         //--- Headers on the right
         case CANV_ELEMENT_ALIGNMENT_RIGHT   :
            //--- If the header is cropped, shift the header row upwards
            Print(DFUN,"header.BottomEdge=",header.BottomEdge(),", this.BottomEdgeWorkspace=",this.BottomEdgeWorkspace());
            if(header.BottomEdge()>this.BottomEdgeWorkspace())
               this.ScrollHeadersRowRightToUp();
            break;
         default:
           break;
        }
      
     }

//--- When clicking on any header row scroll button
   if(id>=WF_CONTROL_EVENT_CLICK_SCROLL_LEFT && id<=WF_CONTROL_EVENT_CLICK_SCROLL_DOWN)
     {
      //--- Get the header of the last tab
      CTabHeader *header=this.GetTabHeader(this.GetListHeaders().Total()-1);
      if(header==NULL)
         return;
      int hidden=0;
      
      //--- When clicking on the left arrow header row scroll button
      if(id==WF_CONTROL_EVENT_CLICK_SCROLL_LEFT)
         this.ScrollHeadersRowToRight();
      
      //--- When clicking on the right arrow header row scroll button
      if(id==WF_CONTROL_EVENT_CLICK_SCROLL_RIGHT)
         this.ScrollHeadersRowToLeft();
      
      //--- When clicking on the down arrow header row scroll button
      if(id==WF_CONTROL_EVENT_CLICK_SCROLL_DOWN)
        {
         //--- Depending on the location of the header row
         switch(this.Alignment())
           {
            //--- scroll the headings upwards using the appropriate method
            case CANV_ELEMENT_ALIGNMENT_LEFT    :  this.ScrollHeadersRowLeftToUp();    break;
            case CANV_ELEMENT_ALIGNMENT_RIGHT   :  this.ScrollHeadersRowRightToUp();   break;
            default: break;
           }
        }
      
      //--- When clicking on the up arrow header row scroll button
      if(id==WF_CONTROL_EVENT_CLICK_SCROLL_UP)
        {
         //--- Depending on the location of the header row
         switch(this.Alignment())
           {
            //--- scroll the headings downwards using the appropriate method
            case CANV_ELEMENT_ALIGNMENT_LEFT    :  this.ScrollHeadersRowLeftToDown();  break;
            case CANV_ELEMENT_ALIGNMENT_RIGHT   :  this.ScrollHeadersRowRightToDown(); break;
            default: break;
           }
        }
     }
  }
//+------------------------------------------------------------------+

上記のメソッドを使用して、各イベント(部分的に非表示のタブヘッダーを選択するか、ヘッダーバーをスクロールするためのボタンをクリックする)を処理します。通常、ヘッダーバーの位置とボタンまたはヘッダーのクリックイベントに応じて、適切なメソッドを呼び出してヘッダーバーをスクロールします。

\MQL5\Include\DoEasy\Collections\GraphElementsCollection.mqhのグラフィック要素のコレクションクラスのイベントハンドラで、WinFormsオブジェクトから受け取ったイベントを正しく処理する必要があります。これを実現するには、「sparam」文字列パラメータから3つの名前を取得し、基本オブジェクトを見つけて、そこからイベントを生成したオブジェクトを取得する必要があります。見つかったオブジェクトがTabControlに属している場合は、イベントIDを送信してTabControlイベントハンドラを呼び出します。

//+------------------------------------------------------------------+
//| Event handler                                                    |
//+------------------------------------------------------------------+
void CGraphElementsCollection::OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
  {
   CGStdGraphObj *obj_std=NULL;  // Pointer to the standard graphical object
   CGCnvElement  *obj_cnv=NULL;  // Pointer to the graphical element object on canvas
   ushort idx=ushort(id-CHARTEVENT_CUSTOM);

//--- Processing WinForms control events
   if(idx>WF_CONTROL_EVENT_NO_EVENT && idx<WF_CONTROL_EVENTS_NEXT_CODE)
     {
      //--- Declare the array of names and enter the names of three objects, set in 'sparam' and separated by ";", into it
      string array[];
      if(::StringSplit(sparam,::StringGetCharacter(";",0),array)!=3)
        {
         CMessage::ToLog(MSG_GRAPH_OBJ_FAILED_GET_OBJECT_NAMES);
         return;
        }
      //--- Get the main object by name
      CWinFormBase *main=this.GetCanvElement(array[0]);
      if(main==NULL)
         return;
      //--- Get the base object, inside which the event has occurred, from the main object by name
      CWinFormBase *base=main.GetElementByName(array[1]);
      CWinFormBase *base_elm=NULL;
      //--- If there is no element with the same name, then this is the base object of the event element bound to the base one - look for it in the list
      if(base==NULL)
        {
         //--- Get the list of all elements bound to the main object with the type set in the 'dparam' parameter
         CArrayObj *list_obj=CSelect::ByGraphCanvElementProperty(main.GetListElements(),CANV_ELEMENT_PROP_TYPE,(long)dparam,EQUAL);
         if(list_obj==NULL || list_obj.Total()==0)
            return;
         //--- In the loop by the obtained list
         for(int i=0;i<list_obj.Total();i++)
           {
            //--- get the next object
            base_elm=list_obj.At(i);
            if(base_elm==NULL)
               continue;
            //--- If the base object is found, get the bound object from it by name from array[1]
            base=base_elm.GetElementByName(array[1]);
            if(base!=NULL)
               break;
           }
        }
      //--- If failed to find the object here, exit
      if(base==NULL)
         return;
      //--- From the found base object, get the object the event occurred from by name
      CWinFormBase *object=base.GetElementByName(array[2]);
      if(object==NULL)
         return;

      //+------------------------------------------------------------------+
      //|  Clicking the control                                            |
      //+------------------------------------------------------------------+
      if(idx==WF_CONTROL_EVENT_CLICK)
        {
         //--- If TabControl type is set in dparam
         if((ENUM_GRAPH_ELEMENT_TYPE)dparam==GRAPH_ELEMENT_TYPE_WF_TAB_CONTROL)
           {
            //--- Set the event type depending on the element type that generated the event
            int event_id=
              (object.TypeGraphElement()==GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_LEFT   ?  WF_CONTROL_EVENT_CLICK_SCROLL_LEFT  :
               object.TypeGraphElement()==GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_RIGHT  ?  WF_CONTROL_EVENT_CLICK_SCROLL_RIGHT :
               object.TypeGraphElement()==GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_UP     ?  WF_CONTROL_EVENT_CLICK_SCROLL_UP    :
               object.TypeGraphElement()==GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_DOWN   ?  WF_CONTROL_EVENT_CLICK_SCROLL_DOWN  :
               WF_CONTROL_EVENT_NO_EVENT);
            //--- If the base control is received, call its event handler
            if(base_elm!=NULL)
               base_elm.OnChartEvent(event_id,lparam,dparam,sparam);
           }
        }

      //+------------------------------------------------------------------+
      //|  Selecting the TabControl tab                                    |
      //+------------------------------------------------------------------+
      if(idx==WF_CONTROL_EVENT_TAB_SELECT)
        {
         if(base!=NULL)
            base.OnChartEvent(idx,lparam,dparam,sparam);
        }
     }
//--- Handle the events of renaming and clicking a standard graphical object
   if(id==CHARTEVENT_OBJECT_CHANGE  || id==CHARTEVENT_OBJECT_DRAG    || id==CHARTEVENT_OBJECT_CLICK   ||
      idx==CHARTEVENT_OBJECT_CHANGE || idx==CHARTEVENT_OBJECT_DRAG   || idx==CHARTEVENT_OBJECT_CLICK)
     {
      //--- Calculate the chart ID

      //---...
      //---...

ロジック全体がコードで記述されているので、それ以上の説明は必要ありません。

これで、すべてのテストの準備が整いました。


検証

テストを実行するには、前の記事のEAを\MQL5\Experts\TestDoEasy\Part119\TestDoEasy119.mq5として保存します。

以前のバージョンとの唯一の違いは、TabControlに15個のタブを実装したことです。

         //--- Create TabControl
         pnl.CreateNewElement(GRAPH_ELEMENT_TYPE_WF_TAB_CONTROL,InpTabControlX,InpTabControlY,pnl.Width()-30,pnl.Height()-40,clrNONE,255,true,false);
         CTabControl *tc=pnl.GetElementByType(GRAPH_ELEMENT_TYPE_WF_TAB_CONTROL,0);
         if(tc!=NULL)
           {
            tc.SetTabSizeMode((ENUM_CANV_ELEMENT_TAB_SIZE_MODE)InpTabPageSizeMode);
            tc.SetAlignment((ENUM_CANV_ELEMENT_ALIGNMENT)InpHeaderAlignment);
            tc.SetMultiline(InpTabCtrlMultiline);
            tc.SetHeaderPadding(6,0);
            tc.CreateTabPages(15,0,56,20,TextByLanguage("Вкладка","TabPage"));
            //--- Create a text label with a tab description on each tab
            for(int j=0;j<tc.TabPages();j++)
              {
               tc.CreateNewElement(j,GRAPH_ELEMENT_TYPE_WF_LABEL,60,20,80,20,clrDodgerBlue,255,true,false);
               CLabel *label=tc.GetTabElement(j,0);
               if(label==NULL)
                  continue;
               label.SetText("TabPage"+string(j+1));
              }
           }

11個のタブを残すこともできましたが、パフォーマンスをテストし、いくつかの「バグ」を検索するために、タブの数を増やしました。したがって、この数値は、選択したヘッダーを両側のコンテナから移動する際のデバッグとトラブルシューティングの結果に過ぎません。

EAをコンパイルし、チャート上で起動します。


ご覧のとおり、すべてが想定どおりに機能します。

ただし、2つの欠点があります。非表示のタブヘッダー領域にカーソルを合わせると、この領域に表示されているかのようにヘッダーの色が変化します。これが、表示領域のサイズが変更されたときに、コントロールのアクティブ領域のサイズが変更されない理由です。これを修正するには、表示されている領域に合わせてアクティブ領域を計算してサイズを変更する必要があります。

2つ目の欠点は、選択したヘッダーをコンテナの外に移動してパネルを移動すると、非表示のヘッダーの2ピクセルが表示されることです。これは、スコープ計算用のタブのサイズ変更に関係しています。これは、選択したヘッダーが両側で2ピクセルずつサイズが大きくなるためです。これを修正するには、タブヘッダーオブジェクト内の隣接するヘッダーのサイズを取得する方法を見つける必要があります。これに基づいて、表示領域のサイズが計算されます。

これについては、新しいWinFormsオブジェクトの開発と併せて、後続の記事で扱います。


次の段階

次の記事では、SplitContainerWinFormsオブジェクトの開発を開始します。

現在のライブラリバージョン、テストEA、およびMQL5のチャートイベントコントロール指標のすべてのファイルが、テストおよびダウンロードできるように以下に接続されています。質問や提案はコメント欄にお願いします。

目次に戻る

連載のこれまでの記事

 
DoEasy.コントロール(第13部):WinFormsオブジェクトとマウスの相互作用を最適化し、TabControlWinFormsオブジェクトの開発を開始
DoEasy.コントロール(第14部):グラフィック要素に名前を付けるための新しいアルゴリズム。TabControlWinFormsオブジェクトの継続作業
DoEasy.コントロール(第15部):TabControlWinFormsオブジェクト—複数行のタブヘッダー、タブ処理メソッド 
DoEasy.コントロール(第16部):TabControlWinFormsオブジェクト—複数行のタブヘッダー、コンテナに合わせてヘッダーをストレッチ
DoEasy.コントロール(第17部):非表示のオブジェクト部分のトリミング、補助矢印ボタンのWinFormsオブジェクト
DoEasy.コントロール(第18部):TabControlでタブをスクロールする機能

MetaQuotes Ltdによってロシア語から翻訳されました。
元の記事: https://www.mql5.com/ru/articles/11490

添付されたファイル |
MQL5.zip (4458.6 KB)
知っておくべきMQL5ウィザードのテクニック(第03回):シャノンのエントロピー 知っておくべきMQL5ウィザードのテクニック(第03回):シャノンのエントロピー
今日のトレーダーは哲学者であり、ほとんどの場合、新しいアイデアを探し、試し、変更するか破棄するかを選択します。これは、かなりの労力を要する探索的プロセスです。この連載では、MQL5ウィザードがトレーダーの主力であるべきであることを示します。
ニューラルネットワークが簡単に(第30部):遺伝的アルゴリズム ニューラルネットワークが簡単に(第30部):遺伝的アルゴリズム
今日はちょっと変わった学習法を紹介したいと思います。ダーウィンの進化論からの借用と言えます。先に述べた手法よりも制御性は劣るでしょうが、非差別的なモデルの訓練が可能です。
EAを用いたリスクとキャピタルの管理 EAを用いたリスクとキャピタルの管理
この記事では、バックテストレポートでは見えないこと、自動売買ソフトを使用する際の注意点、エキスパートアドバイザー(EA)を使用している場合の資金管理、自動売買をおこなっている場合に取引活動を続けるために大きな損失をカバーする方法について説明します。
一からの取引エキスパートアドバイザーの開発(第31部):未来に向かって(IV) 一からの取引エキスパートアドバイザーの開発(第31部):未来に向かって(IV)
引き続きEAから分離した部分を取り除きます。本連載は今回で最終回です。そして、最後に取り除くのがサウンドシステムです。この連載をご覧になっていない方には、少し分かりにくいかもしれません。