English Русский 中文 Español Deutsch Português
preview
DoEasy-コントロール(第14部):グラフィック要素に名前を付けるための新しいアルゴリズム。TabControl WinFormsオブジェクトへの作業の継続

DoEasy-コントロール(第14部):グラフィック要素に名前を付けるための新しいアルゴリズム。TabControl WinFormsオブジェクトへの作業の継続

MetaTrader 5 | 18 11月 2022, 10:48
180 0
Artyom Trishkin
Artyom Trishkin

内容


概念

前回は、TabControl WinFormsオブジェクトを開発する際に、グラフィック要素の名前の長さに制限があって本格的なオブジェクトの作成ができないという状況に陥ってしまいました。親に含まれる各子グラフィカル要素の名前は、関連するすべてのグラフィカルコントロールの階層全体を含む親要素への参照を特徴としていました。このチェーンでは後続のオブジェクトの名前は前のオブジェクトの名前よりも長くなっていたため、グラフィカルリソース名の長さの上限の63文字に達してしまっていました。今日はこの問題を解消するために、異なるグラフィック要素命名アルゴリズムを実装します。同じタイプの新規オブジェクトは、その名前に、プログラム名、グラフィック要素のタイプ名、GUI要素の構築時にプログラムで作成されるこのタイプの既存の要素の数を含むようになります。

例えば、この記事のテストプログラムのGUI要素を作成する場合、次のようなグラフィック要素のリストができました(すべての構成要素の最初の部分しか見えませんが、受け入れられた概念を理解するのには十分です)。


これで、コントロールを作成する際に、オブジェクトのネストに関する制約を受けることがなくなります。グラフィック要素の名前に階層を表示するのではなく、ただ、プログラム名とコントロールタイプと要素のインデックスを使用します。

関連するオブジェクトのチェーンの階層におけるグラフィック要素のおおよその位置は、その名前から理解することはできませんが、現在、名前の長さには制限を設けていません。どのようなオブジェクトなのかをなんとなく理解できるようにするために、グラフィック要素の文字列プロパティに新しいプロパティ「グラフィック要素の説明」を追加することにします。これにより、グラフィック要素の目的を理解し、プログラム内でどのようにアクセスできるのかという問題が明確になります。例えば、トグルボタンのグラフィック要素を作成した後、その説明に「取引の方向を切り替えるためのボタン」というように入力するのです。この記述により、プログラム中のこのコントロール要素を直接参照することができ、以前のようにMyProgram_Elm00_Elm01_Elm00のような「曖昧な」名前で参照するよりはるかに良くなります。

グラフィック要素に名前を付けるための新しいアルゴリズムを作るほか、TabControlの開発も継続します。すなわち、タブヘッダーを記述したTabHeaderオブジェクトを作成します。このオブジェクトは、他の類似のオブジェクト(他のタブオブジェクトのヘッダー)とグループで動作できるようにしなければなりません。この要素は選択されるとサイズが少し大きくなります。同時に、TabControl上のすべてのタブのタイトルのセットの位置(上下左右)を考慮し、その位置によって、オブジェクトの正しい場所にのみフレームを描画するようにする必要があります。例えば、タブヘッダーオブジェクトがTabControlの上部にある場合、タブタイトルの輪郭を描くフレームは、左、上、右の3辺にのみ描画されるようにする必要があります。タブのヘッダーの下側がタブフィールドに接触し、その上にこのタブのオブジェクトが配置されることになります。タブタイトルとタブフィールドが分離することなく一体となるように、接触の場所には境界があってはなりません。

今日は、説明したタブヘッダーの位置に応じて境界を区切る処理を、タブヘッダーオブジェクトに対してのみ実装してみます。次回は、タブフィールドの境界線の描画と、その他のコントロールの配置を扱う予定です。


ライブラリクラスの改善

例えば、ListBoxはコレクション(アイテム)の描画に改良されたButtonを使用するなど、いくつかのコントロールは既存のコントロールを利用しています。これを実装するには、Button要素から派生した新しいオブジェクトを作成し、必要な機能を追加する必要があります。このオブジェクトと他のいくつかの類似のオブジェクトは、それらのフォルダではなく、コントロールカテゴリのルートディレクトリに配置される補助オブジェクトの別のカテゴリに入れた方が良いでしょう。

\MQL5\Include\DoEasy\Defines.mqhにあるグラフィック要素のタイプの列挙に、新しいタイプのTabControlコンテナオブジェクトを追加し、新しいカテゴリにListBoxItemとTabHeaderの2つの補助コントロールを追加しました。

//+------------------------------------------------------------------+
//| The list of graphical element types                              |
//+------------------------------------------------------------------+
enum ENUM_GRAPH_ELEMENT_TYPE
  {
   GRAPH_ELEMENT_TYPE_STANDARD,                       // Standard graphical object
   GRAPH_ELEMENT_TYPE_STANDARD_EXTENDED,              // Extended standard graphical object
   GRAPH_ELEMENT_TYPE_SHADOW_OBJ,                     // Shadow object
   GRAPH_ELEMENT_TYPE_ELEMENT,                        // Element
   GRAPH_ELEMENT_TYPE_FORM,                           // Form
   GRAPH_ELEMENT_TYPE_WINDOW,                         // Window
   //--- WinForms
   GRAPH_ELEMENT_TYPE_WF_UNDERLAY,                    // Panel object underlay
   GRAPH_ELEMENT_TYPE_WF_BASE,                        // Windows Forms Base
   //--- 'Container' object types are to be set below
   GRAPH_ELEMENT_TYPE_WF_CONTAINER,                   // Windows Forms container base object
   GRAPH_ELEMENT_TYPE_WF_PANEL,                       // Windows Forms Panel
   GRAPH_ELEMENT_TYPE_WF_GROUPBOX,                    // Windows Forms GroupBox
   GRAPH_ELEMENT_TYPE_WF_TAB_CONTROL,                 // Windows Forms TabControl
   //--- 'Standard control' object types are to be set below
   GRAPH_ELEMENT_TYPE_WF_COMMON_BASE,                 // Windows Forms base standard control
   GRAPH_ELEMENT_TYPE_WF_LABEL,                       // Windows Forms Label
   GRAPH_ELEMENT_TYPE_WF_BUTTON,                      // Windows Forms Button
   GRAPH_ELEMENT_TYPE_WF_CHECKBOX,                    // Windows Forms CheckBox
   GRAPH_ELEMENT_TYPE_WF_RADIOBUTTON,                 // Windows Forms RadioButton
   GRAPH_ELEMENT_TYPE_WF_ELEMENTS_LIST_BOX,           // Base list object of Windows Forms elements
   GRAPH_ELEMENT_TYPE_WF_LIST_BOX,                    // Windows Forms ListBox
   GRAPH_ELEMENT_TYPE_WF_CHECKED_LIST_BOX,            // Windows Forms CheckedListBox
   GRAPH_ELEMENT_TYPE_WF_BUTTON_LIST_BOX,             // Windows Forms ButtonListBox
   //--- Auxiliary elements of WinForms objects
   GRAPH_ELEMENT_TYPE_WF_LIST_BOX_ITEM,               // Windows Forms ListBoxItem
   GRAPH_ELEMENT_TYPE_WF_TAB_HEADER,                  // Windows Forms TabHeader
  };
//+------------------------------------------------------------------+


キャンバスに基づいたグラフィック要素の文字列プロパティの列挙に新しいプロパティであるグラフィック要素の説明を追加し、文字列プロパティの合計数を3から4に増やしました

//+------------------------------------------------------------------+
//| String properties of the graphical element on the canvas         |
//+------------------------------------------------------------------+
enum ENUM_CANV_ELEMENT_PROP_STRING
  {
   CANV_ELEMENT_PROP_NAME_OBJ = (CANV_ELEMENT_PROP_INTEGER_TOTAL+CANV_ELEMENT_PROP_DOUBLE_TOTAL), // Graphical element object name
   CANV_ELEMENT_PROP_NAME_RES,                        // Graphical resource name
   CANV_ELEMENT_PROP_TEXT,                            // Graphical element text
   CANV_ELEMENT_PROP_DESCRIPTION,                     // Graphical element description
  };
#define CANV_ELEMENT_PROP_STRING_TOTAL  (4)           // Total number of string properties
//+------------------------------------------------------------------+


キャンバス上のグラフィック要素を並び替える可能な基準のリストの一番最後に新しいプロパティによる並び替えを追加しました。

//+------------------------------------------------------------------+
//| 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_TAB_ALIGNMENT,                // Sort by the location of tabs inside the control
   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で、新しいメッセージインデックスを追加し、不要なメッセージインデックスを削除します。

//--- WinForms standard
   MSG_GRAPH_ELEMENT_TYPE_WF_COMMON_BASE,             // WinForms base standard control
   MSG_GRAPH_ELEMENT_TYPE_WF_LABEL,                   // Label control
   MSG_GRAPH_ELEMENT_TYPE_WF_CHECKBOX,                // CheckBox control
   MSG_GRAPH_ELEMENT_TYPE_WF_RADIOBUTTON,             // RadioButton control
   MSG_GRAPH_ELEMENT_TYPE_WF_BUTTON,                  // Button control
   MSG_GRAPH_ELEMENT_TYPE_WF_ELEMENTS_LIST_BOX,       // Base list object of Windows Forms elements
   MSG_GRAPH_ELEMENT_TYPE_WF_LIST_BOX,                // ListBox control
   MSG_GRAPH_ELEMENT_TYPE_WF_LIST_BOX_ITEM,           // ListBox control collection object
   MSG_GRAPH_ELEMENT_TYPE_WF_CHECKED_LIST_BOX,        // CheckedListBox control
   MSG_GRAPH_ELEMENT_TYPE_WF_BUTTON_LIST_BOX,         // ButtonListBox control
   MSG_GRAPH_ELEMENT_TYPE_WF_TAB_HEADER,              // Tab header
   MSG_GRAPH_ELEMENT_TYPE_WF_TAB_PAGE,                // TabPage control
   MSG_GRAPH_ELEMENT_TYPE_WF_TAB_CONTROL,             // TabControl
   MSG_GRAPH_OBJ_BELONG_PROGRAM,                      // Graphical object belongs to a program
   MSG_GRAPH_OBJ_BELONG_NO_PROGRAM,                   // Graphical object does not belong to a program

...

//--- String properties of graphical elements
   MSG_CANV_ELEMENT_PROP_NAME_OBJ,                    // Graphical element object name
   MSG_CANV_ELEMENT_PROP_NAME_RES,                    // Graphical resource name
   MSG_CANV_ELEMENT_PROP_TEXT,                        // Graphical element text
   MSG_CANV_ELEMENT_PROP_DESCRIPTION,                 // Graphical element description
  };
//+------------------------------------------------------------------+


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

//--- WinForms standard
   {"Базовый стандартный элемент управления WinForms","Basic Standard WinForms Control"},
   {"Элемент управления \"Label\"","Control element \"Label\""},
   {"Элемент управления \"CheckBox\"","Control element \"CheckBox\""},
   {"Элемент управления \"RadioButton\"","Control element \"RadioButton\""},
   {"Элемент управления \"Button\"","Control element \"Button\""},
   {"Базовый объект-список Windows Forms элементов","Basic Windows Forms List Object"},
   {"Элемент управления \"ListBox\"","Control element \"ListBox\""},
   {"Объект коллекции элемента управления ListBox","Collection object of the ListBox control"},
   {"Элемент управления \"CheckedListBox\"","Control element \"CheckedListBox\""},
   {"Элемент управления \"ButtonListBox\"","Control element \"ButtonListBox\""},
   {"Заголовок вкладки","Tab header"},
                                                                                             
   {"Элемент управления \"TabControl\"","Control element \"TabControl\""},
   {"Графический объект принадлежит программе","The graphic object belongs to the program"},
   {"Графический объект не принадлежит программе","The graphic object does not belong to the program"},

...

//--- String properties of graphical elements
   {"Имя объекта-графического элемента","The name of the graphic element object"},
   {"Имя графического ресурса","Image resource name"},
   {"Текст графического элемента","Text of the graphic element"},
   {"Описание графического элемента","Description of the graphic element"},
  };
//+---------------------------------------------------------------------+


サービス関数の\MQL5\Include\DoEasy\Services\DELib.mqhファイルに、その後ライブラリで使用するためにグラフィック要素タイプの説明を作成して返す関数を実装します。

//+------------------------------------------------------------------+
//| Return the graphical object type as string                       |
//+------------------------------------------------------------------+
string TypeGraphElementAsString(const ENUM_GRAPH_ELEMENT_TYPE type)
  {
   ushort array[];
   int total=StringToShortArray(StringSubstr(::EnumToString(type),18),array);
   for(int i=0;i<total-1;i++)
     {
      if(array[i]==95)
        {
         i+=1;
         continue;
        }
      else
         array[i]+=0x20;
     }
   string txt=ShortArrayToString(array);
   StringReplace(txt,"_Wf_Base","WFBase");
   StringReplace(txt,"_Wf_","");
   StringReplace(txt,"_Obj","");
   StringReplace(txt,"_","");
   StringReplace(txt,"Groupbox","GroupBox");
   return txt;
  }
//+------------------------------------------------------------------+

アルゴリズムは以下の通りです。説明を得るために必要なグラフィック要素タイプを関数に渡します。次に、

int total=StringToShortArray(StringSubstr(EnumToString(type),18),array);

で、文字列の中にある型列挙定数の名前から抽出された部分文字列の文字数を取得します。

GRAPH_ELEMENT_TYPE_WF_CHECKED_LIST_BOX定数を例にとって見てみましょう。

列挙定数をGRAPH_ELEMENT_TYPE_WF_CHECKED_LIST_BOXに変換します。

EnumToString(type)

得られたGRAPH_ELEMENT_TYPE_WF_CHECKED_LIST_BOXから、文字18から始まる_WF_CHECKED_LIST_BOXを抽出します。

StringSubstr(EnumToString(type),18)

取得した文字列「_WF_CHECKED_LIST_BOXが1文字ずつushort配列にコピーされて、コピーした文字数が受け取られます

int total=StringToShortArray(StringSubstr(EnumToString(type),18),array);

その結果、array[]には、_WF_CHECKED_LIST_BOXの各文字シンボルのコードが格納されます。


ここで、それぞれの「_」の後を大文字のままにして、他の文字はすべて小文字にする必要があります。

これは、文字配列によるループの中でおこないます。

   for(int i=0;i<total-1;i++)
     {
      if(array[i]==95)
        {
         i+=1;
         continue;
        }
      else
         array[i]+=0x20;
     }

文字列と配列の最初の文字は「_」です。配列の中から文字コード(95)を見つけたら、すぐにその次のシンボルにループインデックスを設定します。

文字列_WF_CHECKED_LIST_BOXでの色付きの文字です
配列の次の文字コードにループインデックスを設定した後、次の反復処理に移るため、変更しない文字をスキップします。else演算子で、配列の文字コードに32を加えて小文字にします。

したがって、ループ全体が終了すると、配列には文字列「_Wf_Checked_List_Box」の文字コードが格納されるので、これを文字列に変換します。

string txt=ShortArrayToString(array);

次に、指定された文字列の出現箇所を、得られた文字列の中の必要なものに置き換えるだけで、最終的な行を返します

   StringReplace(txt,"_Wf_Base","WFBase");
   StringReplace(txt,"_Wf_","");
   StringReplace(txt,"_Obj","");
   StringReplace(txt,"_","");
   StringReplace(txt,"Groupbox","GroupBox");
   return txt;

この新しい関数を使って、グラフィック要素タイプからファイル名を取得することにします。


基本グラフィカルオブジェクトの\MQL5\Include\DoEasy\Objects\Graph\GBaseObj.mqhファイルにあるグラフィック要素のタイプの記述を返すメソッドで、新しいタイプを追加して不要なタイプを削除します。

//+------------------------------------------------------------------+
//| Return the description of the graphical element type             |
//+------------------------------------------------------------------+
string CGBaseObj::TypeElementDescription(const ENUM_GRAPH_ELEMENT_TYPE type)
  {
   return
     (
      type==GRAPH_ELEMENT_TYPE_STANDARD               ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_STANDARD)              :
      type==GRAPH_ELEMENT_TYPE_STANDARD_EXTENDED      ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_STANDARD_EXTENDED)     :
      type==GRAPH_ELEMENT_TYPE_ELEMENT                ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_ELEMENT)               :
      type==GRAPH_ELEMENT_TYPE_SHADOW_OBJ             ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_SHADOW_OBJ)            :
      type==GRAPH_ELEMENT_TYPE_FORM                   ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_FORM)                  :
      type==GRAPH_ELEMENT_TYPE_WINDOW                 ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WINDOW)                :
      //--- WinForms
      type==GRAPH_ELEMENT_TYPE_WF_UNDERLAY            ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_UNDERLAY)           :
      type==GRAPH_ELEMENT_TYPE_WF_BASE                ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_BASE)               :
      //--- Containers
      type==GRAPH_ELEMENT_TYPE_WF_CONTAINER           ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_CONTAINER)          :
      type==GRAPH_ELEMENT_TYPE_WF_GROUPBOX            ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_GROUPBOX)           :
      type==GRAPH_ELEMENT_TYPE_WF_PANEL               ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_PANEL)              :
      type==GRAPH_ELEMENT_TYPE_WF_TAB_HEADER          ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_TAB_HEADER)         :
      type==GRAPH_ELEMENT_TYPE_WF_TAB_PAGE            ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_TAB_PAGE)           :
      type==GRAPH_ELEMENT_TYPE_WF_TAB_CONTROL         ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_TAB_CONTROL)        :
      //--- Standard controls
      type==GRAPH_ELEMENT_TYPE_WF_COMMON_BASE         ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_COMMON_BASE)        :
      type==GRAPH_ELEMENT_TYPE_WF_LABEL               ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_LABEL)              :
      type==GRAPH_ELEMENT_TYPE_WF_CHECKBOX            ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_CHECKBOX)           :
      type==GRAPH_ELEMENT_TYPE_WF_RADIOBUTTON         ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_RADIOBUTTON)        :
      type==GRAPH_ELEMENT_TYPE_WF_BUTTON              ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_BUTTON)             :
      type==GRAPH_ELEMENT_TYPE_WF_ELEMENTS_LIST_BOX   ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_ELEMENTS_LIST_BOX)  :
      type==GRAPH_ELEMENT_TYPE_WF_LIST_BOX            ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_LIST_BOX)           :
      type==GRAPH_ELEMENT_TYPE_WF_LIST_BOX_ITEM       ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_LIST_BOX_ITEM)      :
      type==GRAPH_ELEMENT_TYPE_WF_CHECKED_LIST_BOX    ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_CHECKED_LIST_BOX)   :
      type==GRAPH_ELEMENT_TYPE_WF_BUTTON_LIST_BOX     ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_BUTTON_LIST_BOX)    :
      "Unknown"
     );
  }  
//+------------------------------------------------------------------+


オブジェクトの名前を取得するには、上記の関数が必要です。この関数は、作成されたグラフィック要素のタイプに応じた名前を返します。ただし、これだけでは、本格的なオブジェクト名を生成することはできません。関数から受け取った文字列に、同じタイプの既に存在するグラフィック要素(銘柄チャートとそのサブウィンドウに存在する)の数を追加する必要があります。

MQL5\Include\DoEasy\Objects\Graph\GCnvElement.mqhにあるグラフィック要素オブジェクトクラスのprotectedセクションに、タイプ別のグラフィック要素数を返すメソッドと、タイプ別のグラフィック要素名を生成して返すメソッドを宣言します。

//--- Create (1) the object structure and (2) the object from the structure
   virtual bool      ObjectToStruct(void);
   virtual void      StructToObject(void);
//--- Copy the color array to the specified background color array
   void              CopyArraysColors(color &array_dst[],const color &array_src[],const string source);
   
//--- Return the number of graphical elements by type
   int               GetNumGraphElements(const ENUM_GRAPH_ELEMENT_TYPE type) const;
//--- Create and return the graphical element name by its type
   string            CreateNameGraphElement(const ENUM_GRAPH_ELEMENT_TYPE type);
   
private:

メソッドの実装については、もう少し後で考えることにします。

privateセクションで、オブジェクト構造体に新しいフィールドを追加します。

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
      //---...
      //---...
      bool           button_toggle;                            // Toggle flag of the control featuring a button
      bool           button_state;                             // Status of the Toggle control featuring a button
      bool           button_group_flag;                        // Button group flag
      bool           multicolumn;                              // Horizontal display of columns in the ListBox control
      int            column_width;                             // Width of each ListBox control column
      bool           tab_multiline;                            // Several lines of tabs in TabControl
      int            tab_alignment;                            // Location of tabs inside the control
      int            alignment;                                // Location of the object inside the control
      //--- 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

オブジェクトをファイルに正しく保存し、そこから読み出すために必要です。これらは、今回の記事かそれ以前に追加したがここに書くのを忘れていたグラフィック要素の新しいプロパティです。

新しいグラフィック要素を作成するメソッドの宣言から、作成されたオブジェクトの名前がメソッドに渡された仮パラメータを削除します。

//--- Create the element
   bool              Create(const long chart_id,
                            const int wnd_num,
                            const string name,
                            const int x,
                            const int y,
                            const int w,
                            const int h,
                            const bool redraw=false);

これで、オブジェクトの名前はメソッドに渡されなくなり、そのタイプに基づいてメソッド内に作成されるようになります。

name変数は、すでに以前に実装されたすべてのグラフィック要素オブジェクトクラス、すなわちすべてのそれらのコンストラクタの仮パラメータでdescriptに置き換えられています。例えば、現在のファイルでは次のようになっています。

protected:
//--- Protected constructor
                     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);
public:
//--- (1) Set and (2) return the X coordinate shift relative to the base object
   void              SetCoordXRelative(const int value)                                { this.m_shift_coord_x=value;                }
   int               CoordXRelative(void)                                        const { return this.m_shift_coord_x;               }
//--- (1) Set and (2) return the Y coordinate shift relative to the base object
   void              SetCoordYRelative(const int value)                                { this.m_shift_coord_y=value;                }
   int               CoordYRelative(void)                                        const { return this.m_shift_coord_y;               }
   
//--- Event handler
   virtual void      OnChartEvent(const int id,const long& lparam,const double& dparam,const string& sparam);
//--- Parametric constructor
                     CGCnvElement(const ENUM_GRAPH_ELEMENT_TYPE element_type,
                                  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);

ここでは、作成したオブジェクトの名前をクラスのコンストラクタに渡さないようにします。ライブラリは、新しいオブジェクトに対して、そのタイプに基づいた新しい名前を作成します。したがって、コンストラクタにオブジェクトの名前を渡す代わりに、自分自身で割り当てることのできる説明を渡して、この説明を使って作成されたオブジェクトを参照するようにします。このような変更は、すでにすべてのWinFormsオブジェクトのすべてのクラスに対しておこなわれているので、ここではこれ以上検討しません。これらは記事に添付されたファイルに記載されています。

グラフィック要素タイプの設定も変更されました。以前は、WinFormsの各オブジェクトクラスのコンストラクタで、要素タイプを2回設定していました。まず、ライブラリグラフィック要素の基本オブジェクト(そのm_type_element変数)にタイプを設定しました。

void              SetTypeElement(const ENUM_GRAPH_ELEMENT_TYPE type) { this.m_type_element=type;   }

2番目の文字列はオブジェクトのプロパティに同じタイプを設定していました。

オブジェクトタイプを変数プロパティの両方の値に一度に設定する(そして返す)ためのpublicメソッドを作って、これを単純化してみましょう。

//--- 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 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);   }
   
//--- Set the main background color

ここで、各WinFormsオブジェクトクラスのコンストラクタで、プロパティを設定するためのメソッドを呼び出す1つの文字列を設定します。このメソッドは、両方の親クラスにプロパティを設定します。


グラフィック要素の説明をプロパティに返す、または設定するための2つのメソッドを追加します。

//--- Graphical object group
   virtual int       Group(void)                         const { return (int)this.GetProperty(CANV_ELEMENT_PROP_GROUP);                }
   virtual void      SetGroup(const int value)
                       {
                        CGBaseObj::SetGroup(value);
                        this.SetProperty(CANV_ELEMENT_PROP_GROUP,value);
                       }
//--- Graphical element description
   string            Description(void)                   const { return this.GetProperty(CANV_ELEMENT_PROP_DESCRIPTION);               }
   void              SetDescription(const string descr)        { this.SetProperty(CANV_ELEMENT_PROP_DESCRIPTION,descr);                }
   
//+------------------------------------------------------------------+
//| The methods of receiving raster data                             |
//+------------------------------------------------------------------+


両方のクラスのコンストラクタに、グラフィック要素のタイプを設定するメソッドを追加し、そのタイプによって要素名を作成するメソッドを呼び出しグラフィック要素の新しいプロパティを設定するようにします。

//+------------------------------------------------------------------+
//| 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_CHECK_STATE,CANV_ELEMENT_CHEK_STATE_UNCHECKED);  // Status of a control having a checkbox
      this.SetProperty(CANV_ELEMENT_PROP_AUTOCHECK,true);                        // Auto change flag status when it is selected
      
      //---...
      //---...

      this.SetProperty(CANV_ELEMENT_PROP_BUTTON_TOGGLE,false);                                        // Toggle flag of the control featuring a button
      this.SetProperty(CANV_ELEMENT_PROP_BUTTON_STATE,false);                                         // Status of the Toggle control featuring a button
      this.SetProperty(CANV_ELEMENT_PROP_BUTTON_GROUP,false);                                         // Button group flag
      this.SetProperty(CANV_ELEMENT_PROP_LIST_BOX_MULTI_COLUMN,false);                                // Horizontal display of columns in the ListBox control
      this.SetProperty(CANV_ELEMENT_PROP_LIST_BOX_COLUMN_WIDTH,0);                                    // Width of each ListBox control column
      this.SetProperty(CANV_ELEMENT_PROP_TAB_MULTILINE,false);                                        // Several lines of tabs in TabControl
      this.SetProperty(CANV_ELEMENT_PROP_TAB_ALIGNMENT,CANV_ELEMENT_ALIGNMENT_TOP);                   // Location of tabs inside the control
      this.SetProperty(CANV_ELEMENT_PROP_ALIGNMENT,CANV_ELEMENT_ALIGNMENT_TOP);                       // Location of an object inside the control
      this.SetProperty(CANV_ELEMENT_PROP_TEXT,"");                                                    // Graphical element text
      this.SetProperty(CANV_ELEMENT_PROP_DESCRIPTION,descript);                                       // Graphical element description
     }
   else
     {
      ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_FAILED_CREATE_ELM_OBJ),"\"",this.TypeElementDescription(element_type),"\" ",this.NameObj());
     }
  }
//+------------------------------------------------------------------+


オブジェクト構造体を作成するメソッドで、構造体フィールドに新しいプロパティを保存するようにします。

//+------------------------------------------------------------------+
//| 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.button_toggle=(bool)this.GetProperty(CANV_ELEMENT_PROP_BUTTON_TOGGLE);                             // Toggle flag of the control featuring a button
   this.m_struct_obj.button_state=(bool)this.GetProperty(CANV_ELEMENT_PROP_BUTTON_STATE);                               // Status of the Toggle control featuring a button
   
   this.m_struct_obj.button_group_flag=(bool)this.GetProperty(CANV_ELEMENT_PROP_BUTTON_GROUP);                          // Button group flag
   this.m_struct_obj.multicolumn=(bool)this.GetProperty(CANV_ELEMENT_PROP_LIST_BOX_MULTI_COLUMN);                       // Horizontal display of columns in the ListBox control
   this.m_struct_obj.column_width=(int)this.GetProperty(CANV_ELEMENT_PROP_LIST_BOX_COLUMN_WIDTH);                       // Width of each ListBox control column
   this.m_struct_obj.tab_multiline=(bool)this.GetProperty(CANV_ELEMENT_PROP_TAB_MULTILINE);                             // Several lines of tabs in TabControl
   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_BUTTON_TOGGLE,this.m_struct_obj.button_toggle);                             // Toggle flag of the control featuring a button
   this.SetProperty(CANV_ELEMENT_PROP_BUTTON_STATE,this.m_struct_obj.button_state);                               // Status of the Toggle control featuring a button

   this.SetProperty(CANV_ELEMENT_PROP_BUTTON_GROUP,this.m_struct_obj.button_group_flag);                          // Button group flag
   this.SetProperty(CANV_ELEMENT_PROP_LIST_BOX_MULTI_COLUMN,this.m_struct_obj.multicolumn);                       // Horizontal display of columns in the ListBox control
   this.SetProperty(CANV_ELEMENT_PROP_LIST_BOX_COLUMN_WIDTH,this.m_struct_obj.column_width);                      // Width of each ListBox control column
   this.SetProperty(CANV_ELEMENT_PROP_TAB_MULTILINE,this.m_struct_obj.tab_multiline);                             // Several lines of tabs in TabControl
   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
  }
//+------------------------------------------------------------------+


現在では、チャートオブジェクトにバインドされたCCanvasクラスのグラフィカルリソース作成するメソッドのパラメータとして渡された作成オブジェクトの名前を渡すことはありません。その代わり先に設定したオブジェクト名を渡します。

//+------------------------------------------------------------------+
//| Create the graphical element object                              |
//+------------------------------------------------------------------+
bool CGCnvElement::Create(const long chart_id,     // Chart ID
                          const int wnd_num,       // Chart subwindow
                          const int x,             // X coordinate
                          const int y,             // Y coordinate
                          const int w,             // Width
                          const int h,             // Height
                          const bool redraw=false) // Flag indicating the need to redraw
                         
  {
   ::ResetLastError();
   if(this.m_canvas.CreateBitmapLabel((chart_id==NULL ? ::ChartID() : chart_id),wnd_num,this.m_name,x,y,w,h,COLOR_FORMAT_ARGB_NORMALIZE))
     {
      this.Erase(CLR_CANV_NULL);
      this.m_canvas.Update(redraw);
      this.m_shift_y=(int)::ChartGetInteger((chart_id==NULL ? ::ChartID() : chart_id),CHART_WINDOW_YDISTANCE,wnd_num);
      return true;
     }
   CMessage::ToLog(DFUN_ERR_LINE,::GetLastError(),true);
   return false;
  }
//+------------------------------------------------------------------+


以下は、タイプ別にグラフィック要素の数を返すメソッドです。

//+------------------------------------------------------------------+
//| Return the number of graphical elements by type                  |
//+------------------------------------------------------------------+
int CGCnvElement::GetNumGraphElements(const ENUM_GRAPH_ELEMENT_TYPE type) const
  {
//--- Declare a variable with the number of graphical objects and
//--- get the total number of graphical objects on the chart and the subwindow where the graphical element is created
   int n=0, total=::ObjectsTotal(this.ChartID(),this.SubWindow());
//--- Create the name of a graphical object by its type
   string name=TypeGraphElementAsString(type);
//--- In the loop by all chart and subwindow objects,
   for(int i=0;i<total;i++)
     {
      //--- get the name of the next object
      string name_obj=::ObjectName(this.ChartID(),i,this.SubWindow());
      //--- if the object name does not contain the set prefix of the names of the library graphical objects, move on - this is not the object we are looking for
      if(::StringFind(name_obj,this.NamePrefix())==WRONG_VALUE)
         continue;
      //--- If the name of a graphical object selected in the loop has a substring with the created object name by its type,
      //--- then there is a graphical object of this type - increase the counter of objects of this type
      if(::StringFind(name_obj,name)>0)
         n++;
     }
//--- Return the number of found objects by their type
   return n;
  }
//+------------------------------------------------------------------+

メソッドの各行は詳細にコメントされているので、メソッドのロジックは明確になっているはずです。要するに、このタイプの別の要素を作成する必要があるチャートとサブウィンドウ上に、指定したタイプのグラフィカルオブジェクトがいくつあるかを調べる必要があります。すでに適切な関数が追加されているので、タイプによる名前を作成することができます。オブジェクトのタイプ別に名前を作成し、チャートとそのサブウィンドウ上のすべてのグラフィカルオブジェクトを反復処理して、そのタイプ別に作成したグラフィカルオブジェクトの名前を持つ部分文字列を名前に含むオブジェクトを探します。そのような名前が見つかったら、このタイプのオブジェクトはすでに存在しているので、カウンタを増やす必要があります。その結果、ループの最後には、必要なタイプのオブジェクトが何個見つかったかがわかるので、それを返します。


以下は、グラフィック要素のタイプに基づいて、その名前を作成し、返すメソッドです。

//+------------------------------------------------------------------+
//| Create and return the graphical element name by its type         |
//+------------------------------------------------------------------+
string CGCnvElement::CreateNameGraphElement(const ENUM_GRAPH_ELEMENT_TYPE type)
  {
   return this.NamePrefix()+TypeGraphElementAsString(type)+(string)this.GetNumGraphElements(type);
  }
//+------------------------------------------------------------------+

このメソッドは、名前が作成されるべきオブジェクトのタイプを受け取ります。
次に、ライブラリグラフィカルオブジェクトのプレフィックス(「Program_name」+「_」)は、そのタイプによってオブジェクト名、このタイプのオブジェクトの数を受け取ります。
メソッドから結果が返されます。

これらのメソッドは両方とも、そのタイプによってグラフィカルオブジェクトの名前を取得するためのメソッドを呼び出すため、2回呼び出されるメソッドを1回削除することで最適化できます。これは次回以降におこなう予定です(「単純から複雑へ」の原則を念頭に)。


作成されたオブジェクトのタイプを正しく示すためには、このタイプがWinFormsオブジェクトの親クラスの1つであり、これらのオブジェクトが作成されるCGCnvElementグラフィック要素クラスにどのように「到達」するのかを理解する必要があります。作成されたグラフィック要素のタイプをこのクラスに「伝える」必要があります。そこから継承したクラスのすべてのコンストラクタで、子孫クラスが属するタイプが明示的に指定されるようになったため、常にCGCnvElementクラスの次の継承階層にあるクラスで指定されているタイプのみを作成することになります。これがフォームオブジェクトクラスです。

CFormクラスから遠い継承階層にある作成オブジェクトのタイプを送信するにはどうしたらよいでしょうか。答えは明らかです: そのようなクラスはそれぞれもう1つのコンストラクタを持つ必要があります。コンストラクタは、作成されるオブジェクトのタイプを明確に示すものではありません (現在おこなわれているように) が、初期化リストのコンストラクタ変数を介して親クラスに渡されます。このようなコンストラクタは、継承されたクラスでのみ動作するようにprotectedにする必要があります。外部からのアクセスは禁止します。このようなコンストラクタは、各WinFormsオブジェクトに対して既に作成されています。互いに継承する場合、子クラスの型は親クラスに引き継がれます。これは、CGCnvElementオブジェクトまでのオブジェクト階層の連鎖全体に沿って発生し、必要なグラフィック要素は、全体の継承連鎖に沿ってその親に「到達」したタイプで作成されることになります。

すべてのWinFormsオブジェクトクラスのファイルの、グラフィック要素を作成するためのコンストラクタとメソッドで、仮パラメータ「name」はすでに「descript」に名前変更されています。この問題に戻ってきたりそれぞれのWinFormsオブジェクトに対してすでにおこなわれた同じ変更を記述したりしなくて済むように、このことについて再度念を押しておきます。

\MQL5\Include\DoEasy\Objects\Graph\Form.mqhフォームオブジェクトファイルで、依存オブジェクトの名前を返す不要なメソッドの宣言を削除します(すべての名前はCGCnvElementクラスで自動生成されます)。

//--- Create a shadow object
   void              CreateShadowObj(const color colour,const uchar opacity);
//--- Return the name of the dependent object
   string            CreateNameDependentObject(const string base_name)  const
                       { return ::StringSubstr(this.NameObj(),::StringLen(::MQLInfoString(MQL_PROGRAM_NAME))+1)+"_"+base_name;   }
//--- Update coordinates of bound objects
   virtual bool      MoveDependentObj(const int x,const int y,const bool redraw=false);

また、このメソッドの実装コードをクラスから削除します。


すべてのクラスコンストラクタの前に新しいprotectedコンストラクタを宣言します

//--- Last mouse event handler
   virtual void      OnMouseEventPostProcessing(void);

protected:
//--- Protected constructor with object type, chart ID and subwindow
                     CForm(const ENUM_GRAPH_ELEMENT_TYPE type,
                           const long chart_id,
                           const int subwindow,
                           const string descript,
                           const int x,
                           const int y,
                           const int w,
                           const int h);
public:                     
//--- Constructors

他のコンストラクタとは異なり、このコンストラクタには仮パラメータがあり、それによって生成されるオブジェクトのタイプが指定されます。

クラス本体の外側で実装しましょう。

//+------------------------------------------------------------------+
//| Protected constructor with an object type,                       |
//| chart ID and subwindow                                           |
//+------------------------------------------------------------------+
CForm::CForm(const ENUM_GRAPH_ELEMENT_TYPE type,
             const long chart_id,
             const int subwindow,
             const string descript,
             const int x,
             const int y,
             const int w,
             const int h) : CGCnvElement(type,chart_id,subwindow,descript,x,y,w,h)
  {
   this.m_type=OBJECT_DE_TYPE_GFORM; 
   this.Initialize();
   this.SetCoordXInit(x);
   this.SetCoordYInit(y);
   this.SetWidthInit(w);
   this.SetHeightInit(h);
  }
//+------------------------------------------------------------------+

publicコンストラクタとは異なり、コンストラクタの初期化リストでの(親クラスのコンストラクタ)は、作成されたオブジェクトのタイプを受け取り、そのタイプは継承されたクラスからコンストラクタに渡されることになります。

このように、各WinFormsオブジェクトに同様のprotectedコンストラクタを作成することで、任意の子孫クラスのタイプがCGCnvElement親クラスに渡され、その中でオブジェクトが継承されたオブジェクトの階層全体のチェーンに沿って得られたタイプで作成されることができます。

新しいグラフィカルオブジェクトを作成するメソッドから、依存オブジェクト名を作成する文字列を削除し、新しいオブジェクトを作成するときに名前の代わりにメソッドの仮パラメータで渡されるdescriptパラメータを渡すようにしました。

//+------------------------------------------------------------------+
//| Create a new graphical object                                    |
//+------------------------------------------------------------------+
CGCnvElement *CForm::CreateNewGObject(const ENUM_GRAPH_ELEMENT_TYPE type,
                                      const int obj_num,
                                      const string descript,
                                      const int x,
                                      const int y,
                                      const int w,
                                      const int h,
                                      const color colour,
                                      const uchar opacity,
                                      const bool movable,
                                      const bool activity)
  {
   string name=this.CreateNameDependentObject(obj_name);
   CGCnvElement *element=NULL;
   //--- Depending on the created object type,
   switch(type)
     {
      //--- create a graphical element object
      case GRAPH_ELEMENT_TYPE_ELEMENT :
         element=new CGCnvElement(type,this.ID(),obj_num,this.ChartID(),this.SubWindow(),descript,x,y,w,h,colour,opacity,movable,activity);
        break;
      //--- create a form object
      case GRAPH_ELEMENT_TYPE_FORM :
         element=new CForm(type,this.ChartID(),this.SubWindow(),descript,x,y,w,h);
        break;
      default:
        break;
     }
   if(element==NULL)
      ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_FAILED_CREATE_ELM_OBJ),this.TypeElementDescription(type));
   element.SetMovable(movable);
   element.SetCoordXRelative(element.CoordX()-this.CoordX());
   element.SetCoordYRelative(element.CoordY()-this.CoordY());
   return element;
  }
//+------------------------------------------------------------------+

このような変更は、WinFormsオブジェクトの他のクラスのすべての類似したメソッドに対しておこなわれました。ここではそれ以上の考察はおこないません。全ては記事に添付されたファイルに記載されています。

新しい接続要素を作成し、接続オブジェクトのリストに追加するメソッドで、グラフィカルオブジェクトの名前を作成するための文字列の代わりに、

//--- Create a graphical element name
   string ns=(::StringLen((string)num)<2 ? ::IntegerToString(num,2,'0') : (string)num);
   string name="Elm"+ns;

デフォルトのオブジェクト説明テキストを作成し、新しいグラフィカルオブジェクトを作成するメソッドに渡すようしました。

//+------------------------------------------------------------------+
//| Create a new attached element                                    |
//| and add it to the list of bound objects                          |
//+------------------------------------------------------------------+
CGCnvElement *CForm::CreateAndAddNewElement(const ENUM_GRAPH_ELEMENT_TYPE element_type,
                                            const int x,
                                            const int y,
                                            const int w,
                                            const int h,
                                            const color colour,
                                            const uchar opacity,
                                            const bool activity)
  {
//--- If the type of a created graphical element is less than the "element", inform of that and return 'false'
   if(element_type<GRAPH_ELEMENT_TYPE_ELEMENT)
     {
      ::Print(DFUN,CMessage::Text(MSG_FORM_OBJECT_ERR_NOT_INTENDED),::StringSubstr(::EnumToString(element_type),19));
      return NULL;
     }
//--- Specify the element index in the list
   int num=this.m_list_elements.Total();
//--- Create a description of the default graphical element
   string descript=TypeGraphElementAsString(element_type);
//--- Get the screen coordinates of the object relative to the coordinate system of the base object
   int elm_x=x;
   int elm_y=y;
   this.GetCoords(elm_x,elm_y);
//--- Create a new graphical element
   CGCnvElement *obj=this.CreateNewGObject(element_type,num,descript,elm_x,elm_y,w,h,colour,opacity,false,activity);
   if(obj==NULL)
      return NULL;
//--- and add it to the list of bound graphical elements

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

   return obj;
  }
//+------------------------------------------------------------------+


シャドウオブジェクトを作成するメソッドでは、名前の代わりにオブジェクトのデフォルトの説明を追加します。

//+------------------------------------------------------------------+
//| Create the shadow object                                         |
//+------------------------------------------------------------------+
void CForm::CreateShadowObj(const color colour,const uchar opacity)
  {
//--- If the shadow flag is disabled or the shadow object already exists, exit
   if(!this.m_shadow || this.m_shadow_obj!=NULL)
      return;

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

//--- Create a new shadow object and set the pointer to it in the variable
   this.m_shadow_obj=new CShadowObj(this.ChartID(),this.SubWindow(),this.NameObj()+"Shadow",x,y,w,h);
   if(this.m_shadow_obj==NULL)
     {
      ::Print(DFUN,CMessage::Text(MSG_FORM_OBJECT_ERR_FAILED_CREATE_SHADOW_OBJ));
      return;
     }
   this.m_list_tmp.Add(this.m_shadow_obj);
//--- Set the properties for the created shadow object
   
   //---...
   //---...
   //---...

//--- Move the form object to the foreground
   this.BringToTop();
  }
//+------------------------------------------------------------------+

デフォルトのオブジェクトの説明を自動的に作成するすべてのメソッドでは、SetDescription()メソッドを使用してプログラムからこの説明をいつでも変更することができます。


WinFormsオブジェクト基本クラスの \MQL5\Include\DoEasy\Objects\Graph\WForms\WinFormBase.mqhファイルにおいて、protectedコンストラクタを宣言し、不要なpublicを削除します。

protected:
//--- Protected constructor with object type, chart ID and subwindow
                     CWinFormBase(const ENUM_GRAPH_ELEMENT_TYPE type,
                                  const long chart_id,
                                  const int subwindow,
                                  const string descript,
                                  const int x,
                                  const int y,
                                  const int w,
                                  const int h);
public:
//--- Constructors
                     CWinFormBase(const long chart_id,
                                  const int subwindow,
                                  const string descript,
                                  const int x,
                                  const int y,
                                  const int w,
                                  const int h);
                     CWinFormBase(const string name) : CForm(::ChartID(),0,name,0,0,0,0)
                       {
                        this.m_type=OBJECT_DE_TYPE_GWF_BASE; 
                       }

//--- (1) Set and (2) return the default text color of all panel objects


protectedコンストラクタの実装は、publicパラメトリックコンストラクタの実装をほぼ完全に再現しています。

//+------------------------------------------------------------------+
//| Protected constructor with an object type,                       |
//| chart ID and subwindow                                           |
//+------------------------------------------------------------------+
CWinFormBase::CWinFormBase(const ENUM_GRAPH_ELEMENT_TYPE type,
                           const long chart_id,
                           const int subwindow,
                           const string descript,
                           const int x,
                           const int y,
                           const int w,
                           const int h) : CForm(type,chart_id,subwindow,descript,x,y,w,h)
  {
//--- Set the specified graphical element type for the object and assign the library object type to the current object
   this.SetTypeElement(type);
   this.m_type=OBJECT_DE_TYPE_GWF_BASE; 
//--- Initialize all variables
   this.SetText("");
   this.SetForeColor(CLR_DEF_FORE_COLOR,true);
   this.SetForeStateOnColor(this.ForeColor(),true);
   this.SetForeStateOnColorMouseDown(this.ForeColor());
   this.SetForeStateOnColorMouseOver(this.ForeColor());
   this.SetForeColorOpacity(CLR_DEF_FORE_COLOR_OPACITY);
   this.SetFontBoldType(FW_TYPE_NORMAL);
   this.SetMarginAll(0);
   this.SetPaddingAll(0);
   this.SetBorderSizeAll(0);
   this.SetDockMode(CANV_ELEMENT_DOCK_MODE_NONE,false);
   this.SetBorderStyle(FRAME_STYLE_NONE);
   this.SetAutoSize(false,false);
   CForm::SetCoordXInit(x);
   CForm::SetCoordYInit(y);
   CForm::SetWidthInit(w);
   CForm::SetHeightInit(h);
   this.m_shadow=false;
   this.m_gradient_v=true;
   this.m_gradient_c=false;
  }
//+------------------------------------------------------------------+

両者の唯一の違いは、ここではコンストラクタの仮パラメータで設定したタイプを親クラスのコンストラクタに渡している点です。プロパティにオブジェクトタイプを設定する文字列は、新しいメソッドを呼び出すことでおこなわれるようになりました。同じ文字列がpublicコンストラクタに書き込まれるようになりました。

//--- Set the specified graphical element type for the object and assign the library object type to the current object
   this.SetTypeElement(type);
   this.m_type=OBJECT_DE_TYPE_GWF_BASE; 
//--- Initialize all variables


同じ機能があった2つを置き換えました。

//--- Set the graphical element and library object types as a base WinForms object
   CGBaseObj::SetTypeElement(GRAPH_ELEMENT_TYPE_WF_BASE);
   CGCnvElement::SetProperty(CANV_ELEMENT_PROP_TYPE,GRAPH_ELEMENT_TYPE_WF_BASE);
   this.m_type=OBJECT_DE_TYPE_GWF_BASE; 
//--- Initialize all variables

同様の改良は、すべてのWinFormsオブジェクトのすべてのクラスに対しておこなわれました。これ以上検討することはありません。


要素の文字列プロパティの説明を返すメソッドで、新しいプロパティの説明を返すようにします。

//+------------------------------------------------------------------+
//| Return the description of the control string property            |
//+------------------------------------------------------------------+
string CWinFormBase::GetPropertyDescription(ENUM_CANV_ELEMENT_PROP_STRING property,bool only_prop=false)
  {
   return
     (
      property==CANV_ELEMENT_PROP_NAME_OBJ      ?  CMessage::Text(MSG_CANV_ELEMENT_PROP_NAME_OBJ)+": \""+this.GetProperty(property)+"\""     :
      property==CANV_ELEMENT_PROP_NAME_RES      ?  CMessage::Text(MSG_CANV_ELEMENT_PROP_NAME_RES)+": \""+this.GetProperty(property)+"\""     :
      property==CANV_ELEMENT_PROP_TEXT          ?  CMessage::Text(MSG_CANV_ELEMENT_PROP_TEXT)+": \""+this.GetProperty(property)+"\""         :
      property==CANV_ELEMENT_PROP_DESCRIPTION   ?  CMessage::Text(MSG_CANV_ELEMENT_PROP_DESCRIPTION)+": \""+this.GetProperty(property)+"\""  :
      ""
     );
  }
//+------------------------------------------------------------------+


上記の改善はすべて、ファイル内のWinFormsオブジェクトのクラスでおこなわれます。

\MQL5\Include\DoEasy\Objects\Graph\WForms\Common Controls\CommonBase.mqh
\MQL5\Include\DoEasy\Objects\Graph\WForms\Common Controls\Label.mqh
\MQL5\Include\DoEasy\Objects\Graph\WForms\Common Controls\CheckBox.mqh
\MQL5\Include\DoEasy\Objects\Graph\WForms\Common Controls\RadioButton.mqh
\황MQL5↩Include﹑DoEasy﹑Objects﹑Graph﹑WFormsCommonControls엽CheckedListBox.mqh
\MQL5\Include\DoEasy\Objects\Graph\WForms\Common Controls\ButtonListBox.mqh

WinFormsオブジェクトに共通する改善点ですが、派生クラスで再定義する必要があるため、\MQL5\Include\DoEasy\Objects\Graph\WForms\Common Controls\Button.mqhボタンオブジェクトファイルに、ステータスを仮想化するメソッドを実装します。

   bool              Toggle(void)                        const { return (bool)this.GetProperty(CANV_ELEMENT_PROP_BUTTON_TOGGLE);                               }
//--- (1) Set and (2) return the Toggle control status
   virtual void      SetState(const bool flag)
                       {
                        this.SetProperty(CANV_ELEMENT_PROP_BUTTON_STATE,flag);
                        if(this.State())
                          {
                           this.SetBackgroundColor(this.BackgroundStateOnColor(),false);
                           this.SetForeColor(this.ForeStateOnColor(),false);
                           this.UnpressOtherAll();
                          }
                        else
                          {
                           this.SetBackgroundColor(this.BackgroundColorInit(),false);
                           this.SetForeColor(this.ForeColorInit(),false);
                           this.SetBorderColor(this.BorderColorInit(),false);
                          }
                       }
   bool              State(void)                         const { return (bool)this.GetProperty(CANV_ELEMENT_PROP_BUTTON_STATE);                                }

 

ListBoxオブジェクトクラスで文字列を表示するために、ボタンオブジェクトを使用していますが、左寄せにしたときに、常にボタンの左端にテキストが押されるような条件でボタンにテキストを表示するには、テキストを右にずらす文字数を示すパラメータを導入する必要があります。
ボタンオブジェクトには、そのようなプロパティはありません。そこで、ListBoxItem補助オブジェクトを作成します。

\MQL5\Include\DoEasy\Objects\Graph\WForms\ライブラリフォルダで、CListBoxクラスの新しいListBox.mqhファイルを作成します。クラスはリストオブジェクトの基本クラスから派生するため、その ファイルは作成されたクラスファイルにインクルードされます。

//+------------------------------------------------------------------+
//|                                                  ListBoxItem.mqh |
//|                                  Copyright 2022, MetaQuotes Ltd. |
//|                             https://mql5.com/en/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2022, MetaQuotes Ltd."
#property link      "https://mql5.com/en/users/artmedia70"
#property version   "1.00"
#property strict    // Necessary for mql4
//+------------------------------------------------------------------+
//| Include files                                                    |
//+------------------------------------------------------------------+
#include "Common Controls\Button.mqh"
//+------------------------------------------------------------------+
//| Label object class of WForms controls                            |
//+------------------------------------------------------------------+
class CListBoxItem : public CButton
  {
  }


クラスのprivateセクションで、テキストをシフトさせる文字数を格納する変数シフト文字列が格納される文字列変数を宣言します。クラスのprotectedセクションにprotectedコンストラクタを宣言し、クラス変数を操作するメソッドとパラメトリックコンストラクタはpublicセクションで宣言することにしましょう。

//+------------------------------------------------------------------+
//| Label object class of WForms controls                            |
//+------------------------------------------------------------------+
class CListBoxItem : public CButton
  {
private:
   uchar             m_text_shift;                          // Element text shift to the right from the left edge in characters
   string            m_shift_space;                         // Shift string
protected:
//--- Protected constructor with object type, chart ID and subwindow
                     CListBoxItem(const ENUM_GRAPH_ELEMENT_TYPE type,
                                  const long chart_id,
                                  const int subwindow,
                                  const string descript,
                                  const int x,
                                  const int y,
                                  const int w,
                                  const int h);
public:
//--- Return the element text shift to the right from the left edge in characters
   uchar             TextShift(void)                        const { return this.m_text_shift;   }
//--- (1) Create and (2) return the string consisting of the number of shift characters
   void              SetTextShiftSpace(const uchar value);
   string            GetTextShiftSpace(void)                const { return this.m_shift_space;  }
//--- Set the element text
   virtual void      SetText(const string text);
//--- Constructor
                     CListBoxItem(const long chart_id,
                                  const int subwindow,
                                  const string descript,
                                  const int x,
                                  const int y,
                                  const int w,
                                  const int h);
  };
//+------------------------------------------------------------------+


以下はクラスコンストラクタです。

//+------------------------------------------------------------------+
//| Protected constructor with an object type,                       |
//| chart ID and subwindow                                           |
//+------------------------------------------------------------------+
CListBoxItem::CListBoxItem(const ENUM_GRAPH_ELEMENT_TYPE type,
                           const long chart_id,
                           const int subwindow,
                           const string descript,
                           const int x,
                           const int y,
                           const int w,
                           const int h) : CButton(type,chart_id,subwindow,descript,x,y,w,h)
  {
//--- Set the specified graphical element type for the object and assign the library object type to the current object
   this.SetTypeElement(type);
   this.m_type=OBJECT_DE_TYPE_GWF_COMMON;
   this.SetTextAlign(ANCHOR_LEFT);
   this.SetTextShiftSpace(1);
  }
//+------------------------------------------------------------------+
//| Constructor                                                      |
//+------------------------------------------------------------------+
CListBoxItem::CListBoxItem(const long chart_id,
                           const int subwindow,
                           const string descript,
                           const int x,
                           const int y,
                           const int w,
                           const int h) : CButton(GRAPH_ELEMENT_TYPE_WF_LIST_BOX_ITEM,chart_id,subwindow,descript,x,y,w,h)
  {
   this.SetTypeElement(GRAPH_ELEMENT_TYPE_WF_LIST_BOX_ITEM);
   this.m_type=OBJECT_DE_TYPE_GWF_COMMON;
   this.SetTextAlign(ANCHOR_LEFT);
   this.SetTextShiftSpace(1);
  }
//+------------------------------------------------------------------+

どちらのコンストラクタもほとんど同じですが、protectedコンストラクタではオブジェクトタイプが仮パラメータとして渡され、このタイプが親クラスのコンストラクタに渡されるのに対し、パラメトリックコンストラクタではオブジェクトタイプがListBoxItemとして正確に設定される点が異なります。これで、ライブラリのすべてのWinFormsオブジェクトがこのように配置されるようになりました。各コンストラクタで、テキストを1文字分右へずらすよう設定します。


以下は、シフト文字で構成される文字列を作成するメソッドです。

//+------------------------------------------------------------------+
//| Create a string consisting of the number of shift characters     |
//+------------------------------------------------------------------+
void CListBoxItem::SetTextShiftSpace(const uchar value)
  {
   this.m_text_shift=value;
   this.m_shift_space="";
   switch(this.TextAlign())
     {
      case ANCHOR_LEFT        :
      case ANCHOR_LEFT_LOWER  :
      case ANCHOR_LEFT_UPPER  :
        for(int i=0;i<(int)this.m_text_shift;i++)
           this.m_shift_space+=" ";
        break;
      default:
        break;
     }
  }
//+------------------------------------------------------------------+

このメソッドは、文字列をシフトさせる文字数を受け取ります。シフト文字列の初期値は、後から設定します。テキストが左端から始まる場合、テキストの配置に応じて、ループの繰り返しごとにオフセット行に1文字分のスペースを追加します。ループの最後には、文字列に必要な数の空白文字が含まれることになります。


以下は、要素のテキストを設定するメソッドです。

//+------------------------------------------------------------------+
//| Set the element text                                             |
//+------------------------------------------------------------------+
void CListBoxItem::SetText(const string text)
  {
   this.SetProperty(CANV_ELEMENT_PROP_TEXT,this.GetTextShiftSpace()+text);
  }
//+------------------------------------------------------------------+

ここで、オブジェクトのプロパティは、上で述べたSetTextShiftSpace()メソッドで設定された数のスペースを持つオブジェクトのテキストを受け取ります。


\MQL5\Include\DoEasy\Objects\Graph\WForms\Common Controls\ListBox.mqhファイルのListBoxオブジェクトクラスのprivateセクションに、要素オブジェクトコレクション(上記ListBoxItemオブジェクトクラス)のテキストシフトを格納する変数を追加します。publicセクションで、新しい変数を扱うメソッドを宣言し、protectedセクションで、protectedコンストラクタを宣言します。

//+------------------------------------------------------------------+
//| ListBox object class of the WForms controls                      |
//+------------------------------------------------------------------+
class CListBox : public CElementsListBox
  {
private:
   uchar             m_text_shift;                       // ListBoxItem elements text shift to the right from the left edge in characters
//--- Create a new graphical object
   virtual CGCnvElement *CreateNewGObject(const ENUM_GRAPH_ELEMENT_TYPE type,
                                          const int element_num,
                                          const string descript,
                                          const int x,
                                          const int y,
                                          const int w,
                                          const int h,
                                          const color colour,
                                          const uchar opacity,
                                          const bool movable,
                                          const bool activity);
public:
//--- (1) Set and (2) return the element text shift to the right from the left edge in characters
   void              SetTextShift(const uchar value);
   uchar             TextShift(void)                  const { return this.m_text_shift;   }
//--- Create a list from the specified number of rows (Label objects)
   void              CreateList(const int line_count,const int new_column_width=0,const bool autosize=true);
   
protected:
//--- Protected constructor with object type, chart ID and subwindow
                     CListBox(const ENUM_GRAPH_ELEMENT_TYPE type,
                              const long chart_id,
                              const int subwindow,
                              const string descript,
                              const int x,
                              const int y,
                              const int w,
                              const int h);
public:
//--- Constructor
                     CListBox(const long chart_id,
                              const int subwindow,
                              const string descript,
                              const int x,
                              const int y,
                              const int w,
                              const int h);
  };
//+------------------------------------------------------------------+

コンストラクタは、他のクラスの既に考慮されたオブジェクトと同じように作られ、確定されます。

指定した数の文字列のリスト(ListBoxItem)を作成するメソッドを改良してみましょう。

次に、CButtonクラスのオブジェクトの代わりに、新しいクラスCListBoxItemを使用して、この新しいクラスのオブジェクトを作成します。オブジェクトを作成したら、すぐにそのテキストシフトを作成し、作成したコレクション要素のデフォルトテキストを設定します。

//+------------------------------------------------------------------+
//| Create the list from the specified number of rows (ListBoxItem)  |
//+------------------------------------------------------------------+
void CListBox::CreateList(const int count,const int new_column_width=0,const bool autosize=true)
  {
//--- Create the pointer to the CListBoxItem object
   CListBoxItem *obj=NULL;
//--- Calculate the width of the created object depending on the specified column width
   int width=(new_column_width>0 ? new_column_width : this.Width()-this.BorderSizeLeft()-this.BorderSizeRight());
//--- Create the specified number of ListBoxItem objects
   CElementsListBox::CreateElements(GRAPH_ELEMENT_TYPE_WF_LIST_BOX_ITEM,count,0,0,width,15,new_column_width,autosize);
//--- In the loop by the created number of objects
   for(int i=0;i<this.ElementsTotal();i++)
     {
      //--- Get the created object from the list by the loop index
      obj=this.GetElement(i);
      //--- If the object could not be obtained, send the appropriate message to the log and move on to the next one
      if(obj==NULL)
        {
         ::Print(DFUN,MSG_ELM_LIST_ERR_FAILED_GET_GRAPH_ELEMENT_OBJ,": ",this.TypeElementDescription(GRAPH_ELEMENT_TYPE_WF_LIST_BOX_ITEM));
         continue;
        }
      //--- Set left center text alignment
      obj.SetTextAlign(ANCHOR_LEFT);
      obj.SetTextShiftSpace(this.TextShift());
      //--- Set the object text
      obj.SetFontSize(8);
      obj.SetText(TypeGraphElementAsString(obj.TypeGraphElement())+string(i+1));
      //--- Set the background, text and frame color
      obj.SetBackgroundStateOnColor(clrDodgerBlue,true);
      obj.SetBackgroundStateOnColorMouseOver(obj.ChangeColorLightness(obj.BackgroundStateOnColor(),-5));
      obj.SetBackgroundStateOnColorMouseDown(obj.ChangeColorLightness(obj.BackgroundStateOnColor(),-10));
      obj.SetForeStateOnColor(this.BackgroundColor(),true);
      obj.SetForeStateOnColorMouseOver(obj.ChangeColorLightness(obj.ForeStateOnColor(),-5));
      obj.SetForeStateOnColorMouseDown(obj.ChangeColorLightness(obj.ForeStateOnColor(),-10));
      obj.SetBorderColor(obj.BackgroundColor(),true);
      obj.SetBorderColorMouseDown(obj.BackgroundColorMouseDown());
      obj.SetBorderColorMouseOver(obj.BackgroundColorMouseOver());
      //--- Set the flags of the toggle and group buttons
      obj.SetToggleFlag(true);
      obj.SetGroupButtonFlag(true);
     }
//--- If the flag of auto resizing the base object is passed to the method,
//--- set the auto resize mode to "increase and decrease"
   if(autosize)
      this.SetAutoSizeMode(CANV_ELEMENT_AUTO_SIZE_MODE_GROW_SHRINK,false);
  }
//+------------------------------------------------------------------+


以下は、要素テキストの左端から右端へのシフトを文字数で設定するメソッドです。

//+------------------------------------------------------------------+
//| Set the element text shift                                       |
//| to the right from the left edge in characters                    |
//+------------------------------------------------------------------+
void CListBox::SetTextShift(const uchar value)
  {
   this.m_text_shift=value;
   for(int i=0;i<this.ElementsTotal();i++)
     {
      CListBoxItem *obj=this.GetElement(i);
      if(obj==NULL || obj.TextShift()==value)
         continue;
      obj.SetTextShiftSpace(value);
      obj.SetText(obj.Text());
      obj.Update(false);
     }
  }
//+------------------------------------------------------------------+

ここでは、メソッドに渡されたシフト文字数を変数に設定しています。そして、バインドされたオブジェクトのリストをループして、次のオブジェクトを取得し、上で述べたSetText()オブジェクトメソッドを使用して、そのオブジェクトにシフトを伴うテキストを設定します。

MQL5\Include\DoEasy\Objects\Graph\WForms\Containers\Container.mqhの基本コンテナオブジェクトのクラスで、重複するコンストラクタを削除します。

                     CContainer(const string name) : CWinFormBase(::ChartID(),0,name,0,0,0,0)
                       {
                        CGBaseObj::SetTypeElement(GRAPH_ELEMENT_TYPE_WF_CONTAINER);
                        CGCnvElement::SetProperty(CANV_ELEMENT_PROP_TYPE,GRAPH_ELEMENT_TYPE_WF_CONTAINER);
                        this.m_type=OBJECT_DE_TYPE_GWF_CONTAINER; 
                        this.SetForeColor(CLR_DEF_FORE_COLOR,true);
                        this.SetFontBoldType(FW_TYPE_NORMAL);
                        this.SetMarginAll(3);
                        this.SetPaddingAll(0);
                        this.SetDockMode(CANV_ELEMENT_DOCK_MODE_NONE,false);
                        this.SetBorderStyle(FRAME_STYLE_NONE);
                        this.SetAutoScroll(false,false);
                        this.SetAutoScrollMarginAll(0);
                        this.SetAutoSize(false,false);
                        this.SetAutoSizeMode(CANV_ELEMENT_AUTO_SIZE_MODE_GROW,false);
                        this.Initialize();
                       }
//--- Destructor
                    ~CContainer();
  };
//+------------------------------------------------------------------+


protectedセクションでprotectedコンストラクタを宣言します。

protected:
//--- Protected constructor with object type, chart ID and subwindow
                     CContainer(const ENUM_GRAPH_ELEMENT_TYPE type,
                                const long chart_id,
                                const int subwindow,
                                const string descript,
                                const int x,
                                const int y,
                                const int w,
                                const int h);
public:
//--- Constructor
                     CContainer(const long chart_id,
                                const int subwindow,
                                const string descript,
                                const int x,
                                const int y,
                                const int w,
                                const int h);


protectedコンストラクタの実装は、以前に他のクラスで追加されたすべてのprotectedコンストラクタと同じです。


接続されたオブジェクトにパラメータを設定するメソッドに、ButtonやTabHeaderオブジェクトで作ったのと同様ListBoxItemオブジェクトの処理を追加します。

      //--- For "Label", "CheckBox" and "RadioButton" WinForms objects
      case GRAPH_ELEMENT_TYPE_WF_LABEL             :
      case GRAPH_ELEMENT_TYPE_WF_CHECKBOX          :
      case GRAPH_ELEMENT_TYPE_WF_RADIOBUTTON       :
        //--- set the object text color depending on the one passed to the method:
        //--- either the container text color, or the one passed to the method.
        //--- The frame color is set equal to the text color
        //--- Set the background color to transparent
        obj.SetForeColor(colour==clrNONE ? this.ForeColor() : colour,true);
        obj.SetBorderColor(obj.ForeColor(),true);
        obj.SetBackgroundColor(CLR_CANV_NULL,true);
        obj.SetOpacity(0,false);
        break;
      //--- For the Button, TabHeader and ListBoxItem WinForms objects
      case GRAPH_ELEMENT_TYPE_WF_BUTTON            :
      case GRAPH_ELEMENT_TYPE_WF_TAB_HEADER        :
      case GRAPH_ELEMENT_TYPE_WF_LIST_BOX_ITEM     :
        //--- set the object text color as a container text color depending on the one passed to the method:
        //--- set the background color depending on the one passed to the method:
        //--- either the default standard control background color, or the one passed to the method.
        //--- The frame color is set equal to the text color
        obj.SetForeColor(this.ForeColor(),true);
        obj.SetBackgroundColor(colour==clrNONE ? CLR_DEF_CONTROL_STD_BACK_COLOR : colour,true);
        obj.SetBorderColor(obj.ForeColor(),true);
        obj.SetBorderStyle(FRAME_STYLE_SIMPLE);
        break;
      //--- For "ListBox", "CheckedListBox" and "ButtonListBox" WinForms object
      case GRAPH_ELEMENT_TYPE_WF_LIST_BOX          :
      case GRAPH_ELEMENT_TYPE_WF_CHECKED_LIST_BOX  :
      case GRAPH_ELEMENT_TYPE_WF_BUTTON_LIST_BOX   :
        //--- set the object text color as a container text color depending on the one passed to the method:
        //--- set the background color depending on the one passed to the method:
        //--- either the default standard control background color, or the one passed to the method.
        //--- The frame color is set equal to the text color
        obj.SetBackgroundColor(colour==clrNONE ? CLR_DEF_CONTROL_STD_BACK_COLOR : colour,true);
        obj.SetBorderColor(CLR_DEF_BORDER_COLOR,true);
        obj.SetForeColor(CLR_DEF_FORE_COLOR,true);
        break;


無くなったTabPageオブジェクトの処理をメソッドから削除します。

        break;
      case GRAPH_ELEMENT_TYPE_WF_TAB_PAGE          :
        //--- set the object text color as a container text color depending on the one passed to the method:
        //--- set the background color depending on the one passed to the method:
        //--- either the default standard control background color, or the one passed to the method.
        //--- The frame color is set equal to the text color
        obj.SetBackgroundColor(colour==clrNONE ? CLR_DEF_CONTROL_TAB_PAGE_BACK_COLOR : colour,true);
        obj.SetBackgroundColorMouseDown(CLR_DEF_CONTROL_TAB_PAGE_MOUSE_DOWN);
        obj.SetBackgroundColorMouseOver(CLR_DEF_CONTROL_TAB_PAGE_MOUSE_OVER);
        obj.SetBorderColor(CLR_DEF_CONTROL_TAB_PAGE_BORDER_COLOR,true);
        obj.SetBorderColorMouseDown(CLR_DEF_CONTROL_TAB_PAGE_BORDER_MOUSE_DOWN);
        obj.SetBorderColorMouseOver(CLR_DEF_CONTROL_TAB_PAGE_BORDER_MOUSE_OVER);
        obj.SetForeColor(CLR_DEF_FORE_COLOR,true);
        obj.SetOpacity(CLR_DEF_CONTROL_TAB_PAGE_OPACITY);
        obj.SetBorderSizeAll(1);
        obj.SetBorderStyle(FRAME_STYLE_NONE);
        break;
      case GRAPH_ELEMENT_TYPE_WF_TAB_CONTROL       :
  


\MQL5\Include\DoEasy\Objects\Graph\WForms\Common Controls\ElementsListBox.mqhの基本コントロールリストオブジェクトクラスにCListBoxItemクラスファイルをインクルードします。

//+------------------------------------------------------------------+
//| Include files                                                    |
//+------------------------------------------------------------------+
#include "..\Containers\Container.mqh"
#include "..\ListBoxItem.mqh"
//+------------------------------------------------------------------+
//| Class of the base object of the WForms control list              |
//+------------------------------------------------------------------+

これで、コントロールリストコレクションのクラスは、ライブラリの他のオブジェクトでも利用できるようになります。

他のオブジェクトと同様に、protectedコンストラクタを宣言します。

protected:
//--- Create the specified number of specified WinForms objects
   void              CreateElements(ENUM_GRAPH_ELEMENT_TYPE element_type,
                                    const int count,
                                    const int x,
                                    const int y,
                                    const int w,
                                    const int h,
                                    uint new_column_width=0,
                                    const bool autosize=true);
                                    
//--- Protected constructor with object type, chart ID and subwindow
                     CElementsListBox(const ENUM_GRAPH_ELEMENT_TYPE type,
                                      const long chart_id,
                                      const int subwindow,
                                      const string descript,
                                      const int x,
                                      const int y,
                                      const int w,
                                      const int h);
public:
//--- Constructor

protectedコンストラクタの実装とパラメトリックコンストラクタの改良は、他のWinFormsオブジェクトと同じです。


TabHeaderクラス - TabControlオブジェクトのタブヘッダー

TabControlタブのタイトルは、ボタンオブジェクトを元に作成されます。タブのタイトルは、トグルボタンと同様に、有効(そのタブが選択されている)か無効(別のタブが選択されている)かのどちらかにする必要があります。すべてのタイトルは、グループとして動作します。グループボタンと同じように、1つのタブが選択されていれば、残りは解除されるはずですが、すべてのタブが解放されるような事態はあってはなりません。1つのタブは選択したままにしておきます。

ボタンを使って実現します。ただし、タブヘッダーと違って、ボタンは状態によってサイズを変えることができません。選択されたタブには、離されたタブより少し大きなタイトルが表示されます。タブタイトルは、上下左右の4方向からコントロールに配置することができます。従って、タブフィールドに接する側からはフレームを描かないようにします。つまり、ボタンを再描画するためのメソッドを新たに作る必要があるのです。このメソッドでは、コントロール上のタイトルの位置に合わせてフレームが描画されます。さらに、ボタンが押されたときに、ボタンが大きくなり、新しい座標に移動して、常にタブフィールドに押されたままになるように処理する新しいメソッドを実装する必要があります。

\MQL5\Include\DoEasy\Objects\Graph\WForms\ライブラリフォルダで、TabHeaderクラスのTabHeader.mqhファイルを新規に作成します。クラスはボタンオブジェクトクラスから派生するので、その ファイルを作成されたクラスファイルにインクルードする必要があります。

//+------------------------------------------------------------------+
//|                                                    TabHeader.mqh |
//|                                  Copyright 2022, MetaQuotes Ltd. |
//|                             https://mql5.com/en/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2022, MetaQuotes Ltd."
#property link      "https://mql5.com/en/users/artmedia70"
#property version   "1.00"
#property strict    // Necessary for mql4
//+------------------------------------------------------------------+
//| Include files                                                    |
//+------------------------------------------------------------------+
#include "Common Controls\Button.mqh"
//+------------------------------------------------------------------+
//| TabHeader object class of WForms TabControl                      |
//+------------------------------------------------------------------+
class CTabHeader : public CButton
  {
  }


private、protected、publicの各クラスセクションに、クラス操作に必要な変数とメソッドを宣言します。

//+------------------------------------------------------------------+
//| TabHeader object class of WForms TabControl                      |
//+------------------------------------------------------------------+
class CTabHeader : public CButton
  {
private:
   int               m_width_off;                        // Object width in the released state
   int               m_height_off;                       // Object height in the released state
   int               m_width_on;                         // Object width in the selected state
   int               m_height_on;                        // Object height in the selected state
   int               m_col;                              // Header column index
   int               m_row;                              // Header row index
//--- Sets the width, height and shift of the element depending on the state
   void              SetWH(void);
//--- Adjust the size and location of the element depending on the state
   void              WHProcessStateOn(void);
   void              WHProcessStateOff(void);
//--- Draw the element frame depending on the location
   void              DrawFrame(void);
   
protected:
//--- 'The cursor is inside the active area, the left mouse button is clicked' event handler
   virtual void      MouseActiveAreaReleasedHandler(const int id,const long& lparam,const double& dparam,const string& sparam);

public:
//--- Set the control size in the (1) released and (2) selected state
   bool              SetSizeOff(void)  { return(CGCnvElement::SetWidth(this.m_width_off) && CGCnvElement::SetHeight(this.m_height_off) ? true : false);  }
   bool              SetSizeOn(void)   { return(CGCnvElement::SetWidth(this.m_width_on) && CGCnvElement::SetHeight(this.m_height_on)   ? true : false);  }
//--- Sets the size of the control element
   void              SetWidthOff(const int value)                                            { this.m_width_off=value;  }
   void              SetHeightOff(const int value)                                           { this.m_height_off=value; }
   void              SetWidthOn(const int value)                                             { this.m_width_on=value;   }
   void              SetHeightOn(const int value)                                            { this.m_height_on=value;  }
//--- Returns the control size
   int               WidthOff(void)                                                    const { return this.m_width_off; }
   int               HeightOff(void)                                                   const { return this.m_height_off;}
   int               WidthOn(void)                                                     const { return this.m_width_on;  }
   int               HeightOn(void)                                                    const { return this.m_height_on; }
//--- (1) Set and (2) return the index of the tab title row
   void              SetRow(const int value)                                                 { this.m_row=value;        }
   int               Row(void)                                                         const { return this.m_row;       }
//--- (1) Set and (2) return the index of the tab title column
   void              SetColumn(const int value)                                              { this.m_col=value;        }
   int               Column(void)                                                      const { return this.m_col;       }
//--- Set the tab location
   void              SetTabLocation(const int index,const int row,const int col)
                       {
                        this.SetRow(row);
                        this.SetColumn(col);
                       }
//--- (1) Sets and (2) return the location of the object on the control
   void              SetAlignment(const ENUM_CANV_ELEMENT_ALIGNMENT alignment)
                       {
                        this.SetProperty(CANV_ELEMENT_PROP_TAB_ALIGNMENT,alignment);
                        if(this.Alignment()==CANV_ELEMENT_ALIGNMENT_TOP)
                           this.SetBorderSize(1,1,1,0);
                        if(this.Alignment()==CANV_ELEMENT_ALIGNMENT_BOTTOM)
                           this.SetBorderSize(1,0,1,1);
                        if(this.Alignment()==CANV_ELEMENT_ALIGNMENT_LEFT)
                           this.SetBorderSize(1,1,0,1);
                        if(this.Alignment()==CANV_ELEMENT_ALIGNMENT_RIGHT)
                           this.SetBorderSize(0,1,1,1);
                       }
   ENUM_CANV_ELEMENT_ALIGNMENT Alignment(void)  const { return (ENUM_CANV_ELEMENT_ALIGNMENT)this.GetProperty(CANV_ELEMENT_PROP_TAB_ALIGNMENT);  }
   

//--- Sets the state of the control
   virtual void      SetState(const bool flag);

//--- Clear the element filling it with color and opacity
   virtual void      Erase(const color colour,const uchar opacity,const bool redraw=false);
//--- Clear the element with a gradient fill
   virtual void      Erase(color &colors[],const uchar opacity,const bool vgradient,const bool cycle,const bool redraw=false);

//--- Last mouse event handler
   virtual void      OnMouseEventPostProcessing(void);

protected:
//--- Protected constructor with object type, chart ID and subwindow
                     CTabHeader(const ENUM_GRAPH_ELEMENT_TYPE type,
                                const long chart_id,
                                const int subwindow,
                                const string descript,
                                const int x,
                                const int y,
                                const int w,
                                const int h);
public:
//--- Constructor
                     CTabHeader(const long chart_id,
                             const int subwindow,
                             const string descript,
                             const int x,
                             const int y,
                             const int w,
                             const int h);
  };
//+------------------------------------------------------------------+

変数とメソッドの説明は、かなり自明です。ではもっと詳しくみていきます。

クラスコンストラクタ - protectedパラメトリック。実装はほとんど同じで、論理も他のライブラリオブジェクトとまったく同じです。protectedコンストラクタは指定したタイプを親クラスのコンストラクタに渡し、パラメトリックコンストラクタはそのオブジェクトタイプを親クラスのコンストラクタに渡しています。

//+------------------------------------------------------------------+
//| Protected constructor with an object type,                       |
//| chart ID and subwindow                                           |
//+------------------------------------------------------------------+
CTabHeader::CTabHeader(const ENUM_GRAPH_ELEMENT_TYPE type,
                       const long chart_id,
                       const int subwindow,
                       const string descript,
                       const int x,
                       const int y,
                       const int w,
                       const int h) : CButton(type,chart_id,subwindow,descript,x,y,w,h)
  {
   this.SetTypeElement(GRAPH_ELEMENT_TYPE_WF_TAB_HEADER);
   this.m_type=OBJECT_DE_TYPE_GWF_COMMON;
   this.SetAlignment(CANV_ELEMENT_ALIGNMENT_TOP);
   this.SetToggleFlag(true);
   this.SetGroupButtonFlag(true);
   this.SetText(TypeGraphElementAsString(this.TypeGraphElement()));
   this.SetForeColor(CLR_DEF_FORE_COLOR,true);
   this.SetOpacity(CLR_DEF_CONTROL_TAB_HEAD_OPACITY,true);
   this.SetBackgroundColor(CLR_DEF_CONTROL_TAB_HEAD_BACK_COLOR,true);
   this.SetBackgroundColorMouseDown(CLR_DEF_CONTROL_TAB_HEAD_MOUSE_DOWN);
   this.SetBackgroundColorMouseOver(CLR_DEF_CONTROL_TAB_HEAD_MOUSE_OVER);
   this.SetBackgroundStateOnColor(CLR_DEF_CONTROL_TAB_HEAD_BACK_COLOR_ON,true);
   this.SetBackgroundStateOnColorMouseDown(CLR_DEF_CONTROL_TAB_HEAD_BACK_DOWN_ON);
   this.SetBackgroundStateOnColorMouseOver(CLR_DEF_CONTROL_TAB_HEAD_BACK_OVER_ON);
   this.SetBorderStyle(FRAME_STYLE_SIMPLE);
   this.SetBorderColor(CLR_DEF_CONTROL_TAB_HEAD_BORDER_COLOR,true);
   this.SetBorderColorMouseDown(CLR_DEF_CONTROL_TAB_HEAD_BORDER_MOUSE_DOWN);
   this.SetBorderColorMouseOver(CLR_DEF_CONTROL_TAB_HEAD_BORDER_MOUSE_OVER);
   this.SetWidthOff(this.Width());
   this.SetHeightOff(this.Height());
   this.SetWidthOn(this.Width()+4);
   this.SetHeightOn(this.Height()+2);
   this.SetState(false);
  }
//+------------------------------------------------------------------+
//| Constructor                                                      |
//+------------------------------------------------------------------+
CTabHeader::CTabHeader(const long chart_id,
                       const int subwindow,
                       const string descript,
                       const int x,
                       const int y,
                       const int w,
                       const int h) : CButton(GRAPH_ELEMENT_TYPE_WF_TAB_HEADER,chart_id,subwindow,descript,x,y,w,h)
  {
   this.SetTypeElement(GRAPH_ELEMENT_TYPE_WF_TAB_HEADER);
   this.m_type=OBJECT_DE_TYPE_GWF_COMMON;
   this.SetAlignment(CANV_ELEMENT_ALIGNMENT_TOP);
   this.SetToggleFlag(true);
   this.SetGroupButtonFlag(true);
   this.SetText(TypeGraphElementAsString(this.TypeGraphElement()));
   this.SetForeColor(CLR_DEF_FORE_COLOR,true);
   this.SetOpacity(CLR_DEF_CONTROL_TAB_HEAD_OPACITY,true);
   this.SetBackgroundColor(CLR_DEF_CONTROL_TAB_HEAD_BACK_COLOR,true);
   this.SetBackgroundColorMouseDown(CLR_DEF_CONTROL_TAB_HEAD_MOUSE_DOWN);
   this.SetBackgroundColorMouseOver(CLR_DEF_CONTROL_TAB_HEAD_MOUSE_OVER);
   this.SetBackgroundStateOnColor(CLR_DEF_CONTROL_TAB_HEAD_BACK_COLOR_ON,true);
   this.SetBackgroundStateOnColorMouseDown(CLR_DEF_CONTROL_TAB_HEAD_BACK_DOWN_ON);
   this.SetBackgroundStateOnColorMouseOver(CLR_DEF_CONTROL_TAB_HEAD_BACK_OVER_ON);
   this.SetBorderStyle(FRAME_STYLE_SIMPLE);
   this.SetBorderColor(CLR_DEF_CONTROL_TAB_HEAD_BORDER_COLOR,true);
   this.SetBorderColorMouseDown(CLR_DEF_CONTROL_TAB_HEAD_BORDER_MOUSE_DOWN);
   this.SetBorderColorMouseOver(CLR_DEF_CONTROL_TAB_HEAD_BORDER_MOUSE_OVER);
   this.SetWidthOff(this.Width());
   this.SetHeightOff(this.Height());
   this.SetWidthOn(this.Width()+4);
   this.SetHeightOn(this.Height()+2);
   this.SetState(false);
  }
//+------------------------------------------------------------------+

コンストラクタの本体では、さまざまな状態におけるオブジェクトの色のデフォルト値を設定します。トグルボタンとグループ内で動作しているボタンのフラグが即座に設定されます。押された状態では、フィールドに隣接するタブを除く各方向に、2ピクセルずつサイズが大きくなります。

以下は、コントロールの状態を設定するメソッドです。

//+------------------------------------------------------------------+
//| Set the state of the control                                     |
//+------------------------------------------------------------------+
void CTabHeader::SetState(const bool flag)
  {
   bool state=this.State();
   CButton::SetState(flag);
   if(state!=flag)
      this.SetWH();
  }
//+------------------------------------------------------------------+

まず、現在の状態を保存し、次に状態を設定するための親クラスのメソッドを呼び出します。以前の状態と現在の状態が異なる場合、タブタイトルサイズを変更するメソッドを呼び出します。


以下は、要素の状態に応じて、要素の幅、高さ、シフトを設定するメソッドです。

//+------------------------------------------------------------------+
//| Set the element width, height and shift                          |
//| depending on its state                                           |
//+------------------------------------------------------------------+
void CTabHeader::SetWH(void)
  {
   if(this.State())
      this.WHProcessStateOn();
   else
      this.WHProcessStateOff();
  }
//+------------------------------------------------------------------+

状態がenabledであれば、enabled状態用のサイズと位置を変更するメソッドを呼び、そうでなければ、disabled状態用のサイズと位置を変更するメソッドを呼び出します。


以下は、selected状態にある要素の大きさや位置を、その位置によって調整するメソッドです。

//+------------------------------------------------------------------+
//| Adjust the element size and location                             |
//| in the "selected" state depending on its location                |
//+------------------------------------------------------------------+
void CTabHeader::WHProcessStateOn(void)
  {
//--- If failed to get a new size, leave
   if(!this.SetSizeOn())
      return;
//--- Depending on the title location,
   switch(this.Alignment())
     {
      case CANV_ELEMENT_ALIGNMENT_TOP :
        //--- move it where necessary and set new relative coordinates
        if(this.Move(this.CoordX()-2,this.CoordY()-2))
          {
           this.SetCoordXRelative(this.CoordXRelative()-2);
           this.SetCoordYRelative(this.CoordYRelative()-2);
          }
        break;
      case CANV_ELEMENT_ALIGNMENT_BOTTOM :
        //--- move it where necessary and set new relative coordinates
        if(this.Move(this.CoordX()-2,this.CoordY()))
          {
           this.SetCoordXRelative(this.CoordXRelative()-2);
           this.SetCoordYRelative(this.CoordYRelative());
          }
        break;
      default:
        break;
     }
   this.Update(false);
  }
//+------------------------------------------------------------------+

メソッドのロジックは、コードにコメントされています。ヘッダーの位置(今のところ上と下の2カ所だけ処理されています)に応じて、ヘッダーの大きさを変え、新しい座標に移動し、タブフィールドに接触しているその端を所定の位置に保ちます。視覚的には、フィールドに隣接する側を除いて、各方向に2ピクセルずつ拡大されます。


以下は、released状態の要素の大きさや位置を、その位置によって調整するメソッドです。

//+------------------------------------------------------------------+
//| Adjust the element size and location                             |
//| in the "released" state depending on its location                |
//+------------------------------------------------------------------+
void CTabHeader::WHProcessStateOff(void)
  {
//--- If failed to get a new size, leave
   if(!this.SetSizeOff())
      return;
//--- Depending on the title location,
   switch(this.Alignment())
     {
      case CANV_ELEMENT_ALIGNMENT_TOP :
        //--- move it where necessary and set new relative coordinates
        if(this.Move(this.CoordX()+2,this.CoordY()+2))
          {
           this.SetCoordXRelative(this.CoordXRelative()+2);
           this.SetCoordYRelative(this.CoordYRelative()+2);
          }
        break;
      case CANV_ELEMENT_ALIGNMENT_BOTTOM :
        //--- move it where necessary and set new relative coordinates
        if(this.Move(this.CoordX()+2,this.CoordY()))
          {
           this.SetCoordXRelative(this.CoordXRelative()+2);
           this.SetCoordYRelative(this.CoordYRelative());
          }
        break;
      default:
        break;
     }
   this.Update(false);
  }
//+------------------------------------------------------------------+

ヘッダーの位置(今のところ上と下の2カ所だけ処理されています)に応じて、ヘッダーの大きさを変え、新しい座標に移動し、タブフィールドに接触しているその端を所定の位置に保ちます。視覚的には、フィールドに隣接する側を除いて、各方向に2ピクセルずつ縮小されています。


以下は、場所によって要素フレームを描画するメソッドです。

//+------------------------------------------------------------------+
//| Draw the element frame depending on the location                 |
//+------------------------------------------------------------------+
void CTabHeader::DrawFrame(void)
  {
//--- Set initial coordinates
   int x1=0;
   int x2=this.Width()-1;
   int y1=0;
   int y2=this.Height()-1;
//--- Depending on the position of the header, draw a frame
//--- so that the edge of the drawn frame adjacent to the field goes beyond the object
//--- thus, visually the edge will not be drawn on the adjacent side
   switch(this.Alignment())
     {
      case CANV_ELEMENT_ALIGNMENT_TOP     :
        this.DrawRectangle(x1,y1,x2,y2+1,this.BorderColor(),this.Opacity());
        break;
      case CANV_ELEMENT_ALIGNMENT_BOTTOM  :
        this.DrawRectangle(x1,y1-1,x2,y2,this.BorderColor(),this.Opacity());
        break;
      case CANV_ELEMENT_ALIGNMENT_LEFT    :
        this.DrawRectangle(x1,y1,x2+1,y2,this.BorderColor(),this.Opacity());
        break;
      case CANV_ELEMENT_ALIGNMENT_RIGHT   :
        this.DrawRectangle(x1-1,y1,x2,y2,this.BorderColor(),this.Opacity());
        break;
      default:
        break;
     }
  }
//+------------------------------------------------------------------+

メソッドのロジックは、コードにコメントされています。オブジェクトに矩形を描くとき、一方の辺の座標がオブジェクトからはみ出るように描くと、この辺には何も描かれません。ここではこれが使われています。タブフィールドに隣接してタイトルを表示すべき場所では、あえてオブジェクトをはみ出す座標を示し、この辺にはフレームを描画しないようにします。


以下は、色と不透明度を指定して要素をクリアするメソッドです。

//+------------------------------------------------------------------+
//| Clear the element filling it with color and opacity              |
//+------------------------------------------------------------------+
void CTabHeader::Erase(const color colour,const uchar opacity,const bool redraw=false)
  {
//--- Fill the element having the specified color and the redrawing flag
   CGCnvElement::Erase(colour,opacity,redraw);
//--- If the object has a frame, draw it
   if(this.BorderStyle()!=FRAME_STYLE_NONE && redraw)
      this.DrawFrame();
//--- Update the element having the specified redrawing flag
   this.Update(redraw);
  }
//+------------------------------------------------------------------+

仮想メソッドは、親オブジェクトのメソッドをオーバーライドし、オブジェクト内にフレームが描画されます。ここでは、上記のメソッドを呼び出して、オブジェクトの3辺にだけフレームを描画しています。


以下は、グラデーションで塗りつぶされた要素をクリアするメソッドです。

//+------------------------------------------------------------------+
//| Clear the element with a gradient fill                           |
//+------------------------------------------------------------------+
void CTabHeader::Erase(color &colors[],const uchar opacity,const bool vgradient,const bool cycle,const bool redraw=false)
  {
//--- Fill the element having the specified color array and the redrawing flag
   CGCnvElement::Erase(colors,opacity,vgradient,cycle,redraw);
//--- If the object has a frame, draw it
   if(this.BorderStyle()!=FRAME_STYLE_NONE && redraw)
      this.DrawFrame();
//--- Update the element having the specified redrawing flag
   this.Update(redraw);
  }
//+------------------------------------------------------------------+

このメソッドは上で検討したものと同じですが、メソッドに渡された色の配列からグラデーションカラーで背景を塗りつぶします。


以下は、「カーソルがアクティブ領域内にあり、マウスの左ボタンがクリックされた」イベントのハンドラです。

//+------------------------------------------------------------------+
//| '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 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 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);
  }
//+------------------------------------------------------------------+


以下は、最後のマウスイベントハンドラです。

//+------------------------------------------------------------------+
//| Last mouse event handler                                         |
//+------------------------------------------------------------------+
void CTabHeader::OnMouseEventPostProcessing(void)
  {
   ENUM_MOUSE_FORM_STATE state=GetMouseState();
   switch(state)
     {
      //--- The cursor is outside the form, the mouse buttons are not clicked
      //--- The cursor is outside the form, any mouse button is clicked
      //--- The cursor is outside the form, the mouse wheel is being scrolled
      case MOUSE_FORM_STATE_OUTSIDE_FORM_NOT_PRESSED :
      case MOUSE_FORM_STATE_OUTSIDE_FORM_PRESSED     :
      case MOUSE_FORM_STATE_OUTSIDE_FORM_WHEEL       :
        if(this.MouseEventLast()==MOUSE_EVENT_INSIDE_ACTIVE_AREA_NOT_PRESSED || this.MouseEventLast()==MOUSE_EVENT_INSIDE_FORM_NOT_PRESSED)
          {
           this.SetBackgroundColor(this.State() ? this.BackgroundStateOnColor() : this.BackgroundColorInit(),false);
           this.SetForeColor(this.State() ? this.ForeStateOnColor() : this.ForeColorInit(),false);
           this.SetBorderColor(this.BorderColorInit(),false);
           this.m_mouse_event_last=ENUM_MOUSE_EVENT(state+MOUSE_EVENT_NO_EVENT);
           this.Redraw(false);
          }
        break;

      //--- The cursor is inside the form, the mouse buttons are not clicked
      //--- The cursor is inside the form, any mouse button is clicked
      //--- The cursor is inside the form, the mouse wheel is being scrolled
      //--- The cursor is inside the active area, the mouse buttons are not clicked
      //--- The cursor is inside the active area, any mouse button is clicked
      //--- The cursor is inside the active area, the mouse wheel is being scrolled
      //--- The cursor is inside the active area, left mouse button is released
      //--- The cursor is within the window scrolling area, the mouse buttons are not clicked
      //--- The cursor is within the window scrolling area, any mouse button is clicked
      //--- The cursor is within the window scrolling area, the mouse wheel is being scrolled
      case MOUSE_FORM_STATE_INSIDE_FORM_NOT_PRESSED :
      case MOUSE_FORM_STATE_INSIDE_FORM_PRESSED :
      case MOUSE_FORM_STATE_INSIDE_FORM_WHEEL :
      case MOUSE_FORM_STATE_INSIDE_ACTIVE_AREA_NOT_PRESSED :
      case MOUSE_FORM_STATE_INSIDE_ACTIVE_AREA_PRESSED :
      case MOUSE_FORM_STATE_INSIDE_ACTIVE_AREA_WHEEL :
      case MOUSE_FORM_STATE_INSIDE_ACTIVE_AREA_RELEASED :
      case MOUSE_FORM_STATE_INSIDE_SCROLL_AREA_NOT_PRESSED :
      case MOUSE_FORM_STATE_INSIDE_SCROLL_AREA_PRESSED :
      case MOUSE_FORM_STATE_INSIDE_SCROLL_AREA_WHEEL :
        break;
      //--- MOUSE_EVENT_NO_EVENT
      default:
        break;
     }
  }
//+------------------------------------------------------------------+

どちらのメソッドも親クラスのメソッドと同じです。
これらをここに移動したのは、TabControlオブジェクトのさらなる開発中に再定義される可能性があるためです。


TabControlクラス - 開発の再開

前回は、TabControlの開発を始めたものの、作成したグラフィカルオブジェクトの名前の長さ制限に遭遇しました。ライブラリのグラフィック要素に名前を付けるための新しいアルゴリズムを作成し終わったため、\MQL5\Include\DoEasy\Objects\Graph\WForms\Containers\TabControl.mqhで作業を継続することにしましょう。

タブタイトルオブジェクトクラスファイルをコントロールファイルにインクルードします。

//+------------------------------------------------------------------+
//| Include files                                                    |
//+------------------------------------------------------------------+
#include "Container.mqh"
#include "GroupBox.mqh"
#include "..\TabHeader.mqh"
//+------------------------------------------------------------------+


クラスのprivateセクションに、インデックスで指定されたタブの状態を設定するための2つのメソッドを宣言します。

private:
   int                  m_item_width;                 // Fixed width of tab titles
   int                  m_item_height;                // Fixed height of tab titles
//--- Create a new graphical object
   virtual CGCnvElement *CreateNewGObject(const ENUM_GRAPH_ELEMENT_TYPE type,
                                          const int element_num,
                                          const string descript,
                                          const int x,
                                          const int y,
                                          const int w,
                                          const int h,
                                          const color colour,
                                          const uchar opacity,
                                          const bool movable,
                                          const bool activity);
//--- Set the tab as selected
   void              SetSelected(const int index);
//--- Set the tab as released
   void              SetUnselected(const int index);
   
public:


CreateTabPage()をCreateTabPages()に改名し、bool戻り値型に合わせ、仮パラメータのセットを変更し、インデックスによるヘッダーとタブフィールドへのポインタを返す2つのメソッドを追加します。

public:
//--- Create the specified number of tabs
   bool              CreateTabPages(const int total,const int selected_page,const int tab_w=0,const int tab_h=0,const string header_text="");
   
//--- Return a pointer to the (1) title and (2) tab field
   CTabHeader       *GetHeader(const int index)          { return this.GetElementByType(GRAPH_ELEMENT_TYPE_WF_TAB_HEADER,index); }
   CContainer       *GetField(const int index)           { return this.GetElementByType(GRAPH_ELEMENT_TYPE_WF_CONTAINER,index);  }


SetAlignment()メソッドを実装し、オブジェクトプロパティに値を設定するだけでなく、コントロール内に作成されたすべてのタブタイトルに値を設定するようにします。

//--- (1) Set and (2) return the location of tabs on the control
   void              SetAlignment(const ENUM_CANV_ELEMENT_ALIGNMENT alignment)
                       {
                        this.SetProperty(CANV_ELEMENT_PROP_TAB_ALIGNMENT,alignment);
                        CArrayObj *list=this.GetListElementsByType(GRAPH_ELEMENT_TYPE_WF_TAB_HEADER);
                        if(list==NULL)
                           return;
                        for(int i=0;i<list.Total();i++)
                          {
                           CTabHeader *header=list.At(i);
                           if(header==NULL)
                              continue;
                           header.SetAlignment(alignment);
                          }
                       }

まず、オブジェクトのプロパティに値を設定し、作成されたすべてのタブタイトルのリストを取得します。各オブジェクトの結果リストを通過するループに同じ値を設定します。


新たに3つのpublicメソッドを宣言します。

//--- Set a fixed tab size
   void              SetItemSize(const int w,const int h)
                       {
                        if(this.ItemWidth()!=w)
                           this.SetItemWidth(w);
                        if(this.ItemHeight()!=h)
                           this.SetItemHeight(h);
                       }
//--- Set the tab as selected/released
   void              Select(const int index,const bool flag);

//--- Set the title text (1) of the specified tab and (2) by index
   void              SetHeaderText(CTabHeader *header,const string text);
   void              SetHeaderText(const int index,const string text);

//--- Constructor


クラスのコンストラクタで、テキスト色以外のすべての色を透明に設定し、すべてのWinFormsオブジェクトに変更を実装します。

//+------------------------------------------------------------------+
//| Constructor indicating the chart and subwindow ID                |
//+------------------------------------------------------------------+
CTabControl::CTabControl(const long chart_id,
                   const int subwindow,
                   const string descript,
                   const int x,
                   const int y,
                   const int w,
                   const int h) : CContainer(GRAPH_ELEMENT_TYPE_WF_TAB_CONTROL,chart_id,subwindow,descript,x,y,w,h)
  {
   this.SetTypeElement(GRAPH_ELEMENT_TYPE_WF_TAB_CONTROL);
   this.m_type=OBJECT_DE_TYPE_GWF_CONTAINER;
   this.SetBorderSizeAll(0);
   this.SetBorderStyle(FRAME_STYLE_NONE);
   this.SetOpacity(0,true);
   this.SetBackgroundColor(CLR_CANV_NULL,true);
   this.SetBackgroundColorMouseDown(CLR_CANV_NULL);
   this.SetBackgroundColorMouseOver(CLR_CANV_NULL);
   this.SetBorderColor(CLR_CANV_NULL,true);
   this.SetBorderColorMouseDown(CLR_CANV_NULL);
   this.SetBorderColorMouseOver(CLR_CANV_NULL);
   this.SetForeColor(CLR_DEF_FORE_COLOR,true);
   this.SetAlignment(CANV_ELEMENT_ALIGNMENT_TOP);
   this.SetItemSize(58,18);
  }
//+------------------------------------------------------------------+

このオブジェクトは、その中に作成されるタブのコンテナとして機能する必要があります。それに、それらのタブを管理する必要があります。そのため、完全に透明ですが、その上に文字を表示する機能は残っています。


以下は、指定された数のタブを作成するメソッドです。

//+------------------------------------------------------------------+
//| 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
   CTabHeader *header=NULL;
   for(int i=0;i<total;i++)
     {
      //--- Depending on the location of tab titles, set their initial coordinates
      int header_x=2;
      int header_y=0;
      if(this.Alignment()==CANV_ELEMENT_ALIGNMENT_TOP)
         header_y=0;
      if(this.Alignment()==CANV_ELEMENT_ALIGNMENT_BOTTOM)
         header_y=this.Height()-h;
      //--- Set the current X coordinate
      header_x=(header==NULL ? header_x : header.RightEdgeRelative());
      //--- Create the TabHeader object
      if(!this.CreateNewElement(GRAPH_ELEMENT_TYPE_WF_TAB_HEADER,header_x,header_y,w,h,clrNONE,255,this.Active(),false))
        {
         ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_FAILED_CREATE_ELM_OBJ),this.TypeElementDescription(GRAPH_ELEMENT_TYPE_WF_TAB_HEADER),string(i+1));
         return false;
        }
      header=this.GetElementByType(GRAPH_ELEMENT_TYPE_WF_TAB_HEADER,i);
      if(header==NULL)
        {
         ::Print(DFUN,CMessage::Text(MSG_ELM_LIST_ERR_FAILED_GET_GRAPH_ELEMENT_OBJ),this.TypeElementDescription(GRAPH_ELEMENT_TYPE_WF_TAB_HEADER),string(i+1));
         return false;
        }
      header.SetBackgroundColor(CLR_DEF_CONTROL_TAB_HEAD_BACK_COLOR,true);
      header.SetBackgroundColorMouseDown(CLR_DEF_CONTROL_TAB_HEAD_MOUSE_DOWN);
      header.SetBackgroundColorMouseOver(CLR_DEF_CONTROL_TAB_HEAD_MOUSE_OVER);
      header.SetBackgroundStateOnColor(CLR_DEF_CONTROL_TAB_HEAD_BACK_COLOR_ON,true);
      header.SetBackgroundStateOnColorMouseDown(CLR_DEF_CONTROL_TAB_HEAD_BACK_DOWN_ON);
      header.SetBackgroundStateOnColorMouseOver(CLR_DEF_CONTROL_TAB_HEAD_BACK_OVER_ON);
      header.SetBorderStyle(FRAME_STYLE_SIMPLE);
      header.SetBorderColor(CLR_DEF_CONTROL_TAB_HEAD_BORDER_COLOR,true);
      header.SetBorderColorMouseDown(CLR_DEF_CONTROL_TAB_HEAD_BORDER_MOUSE_DOWN);
      header.SetBorderColorMouseOver(CLR_DEF_CONTROL_TAB_HEAD_BORDER_MOUSE_OVER);
      header.SetAlignment(this.Alignment());
      if(header_text!="" && header_text!=NULL)
         this.SetHeaderText(header,header_text+string(i+1));
      
      //--- Depending on the location of the tab headers, set the initial coordinates of the tab fields
      int field_x=0;
      int field_y=0;
      int field_h=this.Height()-header.Height();
      if(this.Alignment()==CANV_ELEMENT_ALIGNMENT_TOP)
         field_y=header.BottomEdgeRelative();
      if(this.Alignment()==CANV_ELEMENT_ALIGNMENT_BOTTOM)
         field_y=0;
      //--- Create the Container object (tab field)
      CContainer *field=NULL;
      if(!this.CreateNewElement(GRAPH_ELEMENT_TYPE_WF_CONTAINER,field_x,field_y,this.Width(),field_h,clrNONE,255,true,false))
        {
         ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_FAILED_CREATE_ELM_OBJ),this.TypeElementDescription(GRAPH_ELEMENT_TYPE_WF_CONTAINER),string(i+1));
         return false;
        }
      field=this.GetElementByType(GRAPH_ELEMENT_TYPE_WF_CONTAINER,i);
      if(field==NULL)
        {
         ::Print(DFUN,CMessage::Text(MSG_ELM_LIST_ERR_FAILED_GET_GRAPH_ELEMENT_OBJ),this.TypeElementDescription(GRAPH_ELEMENT_TYPE_WF_CONTAINER),string(i+1));
         return false;
        }
      field.SetBorderSizeAll(1);
      field.SetBorderStyle(FRAME_STYLE_SIMPLE);
      field.SetOpacity(CLR_DEF_CONTROL_TAB_PAGE_OPACITY,true);
      field.SetBackgroundColor(CLR_DEF_CONTROL_TAB_PAGE_BACK_COLOR,true);
      field.SetBackgroundColorMouseDown(CLR_DEF_CONTROL_TAB_PAGE_MOUSE_DOWN);
      field.SetBackgroundColorMouseOver(CLR_DEF_CONTROL_TAB_PAGE_MOUSE_OVER);
      field.SetBorderColor(CLR_DEF_CONTROL_TAB_PAGE_BORDER_COLOR,true);
      field.SetBorderColorMouseDown(CLR_DEF_CONTROL_TAB_PAGE_BORDER_MOUSE_DOWN);
      field.SetBorderColorMouseOver(CLR_DEF_CONTROL_TAB_PAGE_BORDER_MOUSE_OVER);
      field.SetForeColor(CLR_DEF_FORE_COLOR,true);
      field.Hide();
   //---
     }
   this.Select(selected_page,true);
   return true;
  }
//+------------------------------------------------------------------+

タブは、タブタイトルとタブフィールドの2つのオブジェクトから構成されています。タイトル(ボタン)はアクティブなタブを選択するために使用され、フィールドはその上に他のコントロールを配置するために使用されます。ボタンは常に表示され、タブフィールドはアクティブなものを除き、常に非表示のままであるべきです。

ここでは、まずループ内で、タブのタイトルとなるボタンを作成し、そのデフォルト値を設定しています。ボタンを作成した後のループの同じ繰り返しで、タブフィールドのコンテナを作成し、そのデフォルト値も設定します。タブフィールドの座標とサイズは、コントロール内のヘッダーの位置に依存します。タブオブジェクトの作成ループが終了したら、入力に設定されているタブを選択状態に指定します。


以下は、タブをselectedとして設定するメソッドです。

//+------------------------------------------------------------------+
//| Set the tab as selected                                          |
//+------------------------------------------------------------------+
void CTabControl::SetSelected(const int index)
  {
   CTabHeader *header=this.GetElementByType(GRAPH_ELEMENT_TYPE_WF_TAB_HEADER,index);
   CContainer *field=this.GetElementByType(GRAPH_ELEMENT_TYPE_WF_CONTAINER,index);
   if(header==NULL || field==NULL)
      return;
   field.Show();
   field.BringToTop();
   header.SetState(true);
   header.BringToTop();
  }
//+------------------------------------------------------------------+

タイトルとタブフィールドへのポインタをインデックスで取得します。
フィールドを表示し、前景に出します

選択したタイトルを設定し、前景に出します


以下は、タブをreleasedに設定するメソッドです。

//+------------------------------------------------------------------+
//| Select the tab as released                                       |
//+------------------------------------------------------------------+
void CTabControl::SetUnselected(const int index)
  {
   CTabHeader *header=this.GetElementByType(GRAPH_ELEMENT_TYPE_WF_TAB_HEADER,index);
   CContainer *field=this.GetElementByType(GRAPH_ELEMENT_TYPE_WF_CONTAINER,index);
   if(header==NULL || field==NULL)
      return;
   field.Hide();
   header.SetState(false);
  }
//+------------------------------------------------------------------+

タイトルとタブフィールドへのポインタをインデックスで取得します。
フィールドを非表示にして
ヘッダをreleasedに設定します


以下は、タブのselected/releasedを設定するメソッドです。

//+------------------------------------------------------------------+
//| Set the tab as selected/released                                 |
//+------------------------------------------------------------------+
void CTabControl::Select(const int index,const bool flag)
  {
   if(flag)
      this.SetSelected(index);
   else
      this.SetUnselected(index);
  }
//+------------------------------------------------------------------+

タブインデックスとフラグがメソッドに渡され、フラグの値に応じて、前述した2つのメソッドのいずれかを呼び出します。


以下は、指定されたタブのタイトルテキストを設定するメソッドです。

//+------------------------------------------------------------------+
//| Set the title text of the specified tab                          |
//+------------------------------------------------------------------+
void CTabControl::SetHeaderText(CTabHeader *header,const string text)
  {
   if(header==NULL)
      return;
   header.SetText(text);
  }
//+------------------------------------------------------------------+

このメソッドは、メソッドに渡されたテキストがセットされるオブジェクトへのポインタを受け取ります。


以下は、タブタイトルテキストをインデックス単位で設定するメソッドです。

//+------------------------------------------------------------------+
//| Set the tab title text by index                                  |
//+------------------------------------------------------------------+
void CTabControl::SetHeaderText(const int index,const string text)
  {
   CTabHeader *header=this.GetElementByType(GRAPH_ELEMENT_TYPE_WF_TAB_HEADER,index);
   this.SetHeaderText(header,text);
  }
//+------------------------------------------------------------------+

このメソッドは、テキストが設定されるべきタブタイトルのインデックスを受け取ります。
インデックスでタブタイトル型のオブジェクトを取得し、上記のメソッドを呼び出して、指定したオブジェクトにテキストが設定します。


新しいグラフィカルオブジェクトを作成するメソッドで、ListBoxItemオブジェクトの作成を追加し、今はないTabPageオブジェクトを作成するブロックを削除します。

//+------------------------------------------------------------------+
//| Create a new graphical object                                    |
//+------------------------------------------------------------------+
CGCnvElement *CTabControl::CreateNewGObject(const ENUM_GRAPH_ELEMENT_TYPE type,
                                         const int obj_num,
                                         const string descript,
                                         const int x,
                                         const int y,
                                         const int w,
                                         const int h,
                                         const color colour,
                                         const uchar opacity,
                                         const bool movable,
                                         const bool activity)
  {

   CGCnvElement *element=NULL;
   switch(type)
     {
      case GRAPH_ELEMENT_TYPE_ELEMENT              :
         element=new CGCnvElement(type,this.ID(),obj_num,this.ChartID(),this.SubWindow(),descript,x,y,w,h,colour,opacity,movable,activity);
        break;
      case GRAPH_ELEMENT_TYPE_FORM                 :
         element=new CForm(this.ChartID(),this.SubWindow(),descript,x,y,w,h);
        break;
      case GRAPH_ELEMENT_TYPE_WF_CONTAINER         :
         element=new CContainer(this.ChartID(),this.SubWindow(),descript,x,y,w,h);
        break;
      case GRAPH_ELEMENT_TYPE_WF_GROUPBOX          :
         element=new CGroupBox(this.ChartID(),this.SubWindow(),descript,x,y,w,h);
        break;
      case GRAPH_ELEMENT_TYPE_WF_PANEL             :
         element=new CPanel(this.ChartID(),this.SubWindow(),descript,x,y,w,h);
        break;
      case GRAPH_ELEMENT_TYPE_WF_LABEL             :
         element=new CLabel(this.ChartID(),this.SubWindow(),descript,x,y,w,h);
        break;
      case GRAPH_ELEMENT_TYPE_WF_CHECKBOX          :
         element=new CCheckBox(this.ChartID(),this.SubWindow(),descript,x,y,w,h);
        break;
      case GRAPH_ELEMENT_TYPE_WF_RADIOBUTTON       :
         element=new CRadioButton(this.ChartID(),this.SubWindow(),descript,x,y,w,h);
        break;
      case GRAPH_ELEMENT_TYPE_WF_BUTTON            :
         element=new CButton(this.ChartID(),this.SubWindow(),descript,x,y,w,h);
        break;
      case GRAPH_ELEMENT_TYPE_WF_LIST_BOX          :
         element=new CListBox(this.ChartID(),this.SubWindow(),descript,x,y,w,h);
        break;
      case GRAPH_ELEMENT_TYPE_WF_LIST_BOX_ITEM     :
         element=new CListBoxItem(this.ChartID(),this.SubWindow(),descript,x,y,w,h);
        break;
      case GRAPH_ELEMENT_TYPE_WF_CHECKED_LIST_BOX  :
         element=new CCheckedListBox(this.ChartID(),this.SubWindow(),descript,x,y,w,h);
        break;
      case GRAPH_ELEMENT_TYPE_WF_BUTTON_LIST_BOX   :
         element=new CButtonListBox(this.ChartID(),this.SubWindow(),descript,x,y,w,h);
        break;
      case GRAPH_ELEMENT_TYPE_WF_TAB_HEADER        :
         element=new CTabHeader(this.ChartID(),this.SubWindow(),descript,x,y,w,h);
        break;
                                                                                     
                                                                                     
                                                                                     
      case GRAPH_ELEMENT_TYPE_WF_TAB_CONTROL       :
         element=new CTabControl(this.ChartID(),this.SubWindow(),descript,x,y,w,h);
        break;
      default:
        break;
     }
   if(element==NULL)
      ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_FAILED_CREATE_ELM_OBJ),this.TypeElementDescription(type));
   return element;
  }
//+------------------------------------------------------------------+

ここで、各オブジェクト作成ブロックのメソッドに、(名前ではなく)オブジェクトの説明を渡します


また、WinFormsオブジェクトと同様に、\MQL5\Include\DoEasy\Objects\Graph\WForms\Containers\Panel.mqh Panelオブジェクトクラスと\MQL5\Include\DoEasy\Objects\Graph\WForms\Containers\GroupBox.mqh GroupBoxオブジェクトの改良をおこないました。上述したような、新しいprotectedコンストラクタの追加、不要なパラメトリックコンストラクタの削除、CreateNewGObject()virtualメソッドの改良です。

現在の記事で計画している改善点はこれですべてです。


検証

テストを実行するには、前の記事のEAを使用するので、\MQL5\Experts\TestDoEasy\Part114\TstDE114.mp5として保存します。

EAの設定で列挙定数の説明を2つの言語で表示するには、英語の言語とロシア語の列挙を追加で作成してコンパイルします。設定に、タブのタイトルの位置(上か下か)を指定する列挙型の新しい変数を追加します(右左はまだ実装されていません)。

//--- enumerations by compilation language
#ifdef COMPILE_EN
enum ENUM_AUTO_SIZE_MODE
  {
   AUTO_SIZE_MODE_GROW=CANV_ELEMENT_AUTO_SIZE_MODE_GROW,                // Grow
   AUTO_SIZE_MODE_GROW_SHRINK=CANV_ELEMENT_AUTO_SIZE_MODE_GROW_SHRINK   // Grow and Shrink
  };
enum ENUM_BORDER_STYLE
  {
   BORDER_STYLE_NONE=FRAME_STYLE_NONE,                                  // None
   BORDER_STYLE_SIMPLE=FRAME_STYLE_SIMPLE,                              // Simple
   BORDER_STYLE_FLAT=FRAME_STYLE_FLAT,                                  // Flat
   BORDER_STYLE_BEVEL=FRAME_STYLE_BEVEL,                                // Embossed (bevel)
   BORDER_STYLE_STAMP=FRAME_STYLE_STAMP,                                // Embossed (stamp)
  };
enum ENUM_CHEK_STATE
  {
   CHEK_STATE_UNCHECKED=CANV_ELEMENT_CHEK_STATE_UNCHECKED,              // Unchecked
   CHEK_STATE_CHECKED=CANV_ELEMENT_CHEK_STATE_CHECKED,                  // Checked
   CHEK_STATE_INDETERMINATE=CANV_ELEMENT_CHEK_STATE_INDETERMINATE,      // Indeterminate
  };
enum ENUM_ELEMENT_ALIGNMENT
  {
   ELEMENT_ALIGNMENT_TOP=CANV_ELEMENT_ALIGNMENT_TOP,                    // Top
   ELEMENT_ALIGNMENT_BOTTOM=CANV_ELEMENT_ALIGNMENT_BOTTOM,              // Bottom
   ELEMENT_ALIGNMENT_LEFT=CANV_ELEMENT_ALIGNMENT_LEFT,                  // Left
   ELEMENT_ALIGNMENT_RIGHT=CANV_ELEMENT_ALIGNMENT_RIGHT,                // Right
  };
#else 
enum ENUM_AUTO_SIZE_MODE
  {
   AUTO_SIZE_MODE_GROW=CANV_ELEMENT_AUTO_SIZE_MODE_GROW,                // Increase only
   AUTO_SIZE_MODE_GROW_SHRINK=CANV_ELEMENT_AUTO_SIZE_MODE_GROW_SHRINK   // Increase and decrease
  };
enum ENUM_BORDER_STYLE
  {
   BORDER_STYLE_NONE=FRAME_STYLE_NONE,                                  // No frame
   BORDER_STYLE_SIMPLE=FRAME_STYLE_SIMPLE,                              // Simple frame
   BORDER_STYLE_FLAT=FRAME_STYLE_FLAT,                                  // Flat frame
   BORDER_STYLE_BEVEL=FRAME_STYLE_BEVEL,                                // Embossed (convex)
   BORDER_STYLE_STAMP=FRAME_STYLE_STAMP,                                // Embossed (concave)
  };
enum ENUM_CHEK_STATE
  {
   CHEK_STATE_UNCHECKED=CANV_ELEMENT_CHEK_STATE_UNCHECKED,              // Unchecked
   CHEK_STATE_CHECKED=CANV_ELEMENT_CHEK_STATE_CHECKED,                  // Checked
   CHEK_STATE_INDETERMINATE=CANV_ELEMENT_CHEK_STATE_INDETERMINATE,      // Undefined
  };
enum ENUM_ELEMENT_ALIGNMENT
  {
   ELEMENT_ALIGNMENT_TOP=CANV_ELEMENT_ALIGNMENT_TOP,                    // Top
   ELEMENT_ALIGNMENT_BOTTOM=CANV_ELEMENT_ALIGNMENT_BOTTOM,              // Bottom
   ELEMENT_ALIGNMENT_LEFT=CANV_ELEMENT_ALIGNMENT_LEFT,                  // Left
   ELEMENT_ALIGNMENT_RIGHT=CANV_ELEMENT_ALIGNMENT_RIGHT,                // Right
  };
#endif 
//--- input parameters
sinput   bool                          InpMovable           =  true;                   // Panel Movable flag
sinput   ENUM_INPUT_YES_NO             InpAutoSize          =  INPUT_YES;              // Panel Autosize
sinput   ENUM_AUTO_SIZE_MODE           InpAutoSizeMode      =  AUTO_SIZE_MODE_GROW;    // Panel Autosize mode
sinput   ENUM_BORDER_STYLE             InpFrameStyle        =  BORDER_STYLE_SIMPLE;    // Label border style
sinput   ENUM_ANCHOR_POINT             InpTextAlign         =  ANCHOR_CENTER;          // Label text align
sinput   ENUM_INPUT_YES_NO             InpTextAutoSize      =  INPUT_NO;               // Label autosize
sinput   ENUM_ANCHOR_POINT             InpCheckAlign        =  ANCHOR_LEFT;            // Check flag align
sinput   ENUM_ANCHOR_POINT             InpCheckTextAlign    =  ANCHOR_LEFT;            // Check label text align
sinput   ENUM_CHEK_STATE               InpCheckState        =  CHEK_STATE_UNCHECKED;   // Check flag state
sinput   ENUM_INPUT_YES_NO             InpCheckAutoSize     =  INPUT_YES;              // CheckBox autosize
sinput   ENUM_BORDER_STYLE             InpCheckFrameStyle   =  BORDER_STYLE_NONE;      // CheckBox border style
sinput   ENUM_ANCHOR_POINT             InpButtonTextAlign   =  ANCHOR_CENTER;          // Button text align
sinput   ENUM_INPUT_YES_NO             InpButtonAutoSize    =  INPUT_YES;              // Button autosize
sinput   ENUM_AUTO_SIZE_MODE           InpButtonAutoSizeMode=  AUTO_SIZE_MODE_GROW;    // Button Autosize mode
sinput   ENUM_BORDER_STYLE             InpButtonFrameStyle  =  BORDER_STYLE_NONE;      // Button border style
sinput   bool                          InpButtonToggle      =  false;                  // Button toggle flag
//sinput   bool                          InpListBoxMColumn    =  false;                  // ListBox MultiColumn flag
sinput   bool                          InpButtListMSelect   =  false;                  // ButtonListBox Button MultiSelect flag
sinput   ENUM_ELEMENT_ALIGNMENT        InpHeaderAlignment   =  ELEMENT_ALIGNMENT_TOP;  // TabHeader Alignment
//--- global variables
CEngine        engine;
color          array_clr[];
//+------------------------------------------------------------------+


OnInit()ハンドラ、すなわちグラフィカルオブジェクト作成ブロックの中で、前回の記事のTabControlオブジェクト作成ブロック(後でそこに配置します)とListBoxオブジェクト作成コード(将来のTabControlオブジェクトのタブに配置します)をコメントアウトします。ButtonListBoxオブジェクトを作成するコードブロックの後に、3つのタブを持つTabControlオブジェクトを作成するコードブロックを配置し、そのうちの1つを最初に選択するようにします。

      //--- If the attached GroupBox object is created
      if(pnl.CreateNewElement(GRAPH_ELEMENT_TYPE_WF_GROUPBOX,x,2,w,h,C'0x91,0xAA,0xAE',0,true,false))
        {
         //--- get the pointer to the GroupBox object by its index in the list of bound GroupBox type objects
         gbox2=pnl.GetElementByType(GRAPH_ELEMENT_TYPE_WF_GROUPBOX,1);
         if(gbox2!=NULL)
           {
            //--- set the "indented frame" type, the frame color matches the main panel background color,
            //--- while the text color is the background color of the last attached panel darkened by 1
            gbox2.SetBorderStyle(FRAME_STYLE_STAMP);
            gbox2.SetBorderColor(pnl.BackgroundColor(),true);
            gbox2.SetForeColor(gbox2.ChangeColorLightness(obj.BackgroundColor(),-1),true);
            gbox2.SetText("GroupBox2");
            
            //--- Create the TabControl object
//            gbox2.CreateNewElement(GRAPH_ELEMENT_TYPE_WF_TAB_CONTROL,4,12,gbox2.Width()-12,gbox2.Height()-20,clrNONE,255,true,false);
//            //--- get the pointer to the TabControl object by its index in the list of bound objects of the TabControl type
//            CTabControl *tctrl=gbox2.GetElementByType(GRAPH_ELEMENT_TYPE_WF_TAB_CONTROL,0);
//            if(tctrl!=NULL)
//              {
//               //--- get the pointer to the Container object by its index in the list of bound objects of the Container type
//               CContainer *page=tctrl.GetElementByType(GRAPH_ELEMENT_TYPE_WF_CONTAINER,0);
//               if(page!=NULL)
//                 {
//                  // Here we will create objects attached to the specified tab of the TabControl object
//                  // Unfortunately, in the current state of creating the names of graphical objects of the library,
//                  // their further creation is limited by the number of characters in the resource name in the CCanvas class
//                 }
//               
//              }
            ///*
            //--- Create the CheckedListBox object
            gbox2.CreateNewElement(GRAPH_ELEMENT_TYPE_WF_CHECKED_LIST_BOX,4,12,160,20,clrNONE,255,true,false);
            //--- get the pointer to the CheckedListBox object by its index in the list of bound objects of the CheckBox type
            CCheckedListBox *clbox=gbox2.GetElementByType(GRAPH_ELEMENT_TYPE_WF_CHECKED_LIST_BOX,0);
            //--- If CheckedListBox is created and the pointer to it is received
            if(clbox!=NULL)
              {
               clbox.SetMultiColumn(true);
               clbox.SetColumnWidth(0);
               clbox.CreateCheckBox(4,66);
              }
            //--- Create the ButtonListBox object
            gbox2.CreateNewElement(GRAPH_ELEMENT_TYPE_WF_BUTTON_LIST_BOX,4,clbox.BottomEdgeRelative()+6,160,30,clrNONE,255,true,false);
            //--- get the pointer to the ButtonListBox object by its index in the list of attached objects of the Button type
            CButtonListBox *blbox=gbox2.GetElementByType(GRAPH_ELEMENT_TYPE_WF_BUTTON_LIST_BOX,0);
            //--- If ButtonListBox is created and the pointer to it is received
            if(blbox!=NULL)
              {
               blbox.SetMultiColumn(true);
               blbox.SetColumnWidth(0);
               blbox.CreateButton(4,66,16);
               blbox.SetMultiSelect(InpButtListMSelect);
               blbox.SetToggle(InpButtonToggle);
               for(int i=0;i<blbox.ElementsTotal();i++)
                 {
                  blbox.SetButtonGroup(i,(i % 2==0 ? blbox.Group()+1 : blbox.Group()+2));
                  blbox.SetButtonGroupFlag(i,(i % 2==0 ? true : false));
                 }
              }
            
            int lbx=6;
            int lby=blbox.BottomEdgeRelative()+6;
            int lbw=180;
            //--- Create the TabControl object
            gbox2.CreateNewElement(GRAPH_ELEMENT_TYPE_WF_TAB_CONTROL,lbx,lby,lbw,78,clrNONE,255,true,false);
            //--- get the pointer to the TabControl object by its index in the list of bound objects of the TabControl type
            CTabControl *tab_ctrl=gbox2.GetElementByType(GRAPH_ELEMENT_TYPE_WF_TAB_CONTROL,0);
            //--- If TabControl is created and the pointer to it is received
            if(tab_ctrl!=NULL)
              {
               //--- Set the location of the tab titles on the element and the tab text
               tab_ctrl.SetAlignment((ENUM_CANV_ELEMENT_ALIGNMENT)InpHeaderAlignment);
               tab_ctrl.CreateTabPages(3,0,56,16,TextByLanguage("Вкладка","TabPage"));
              }
                        
            //--- Create the ListBox object
            //int lbx=4;
            //int lby=blbox.BottomEdgeRelative()+6;
            //int lbw=146;
            //if(!InpListBoxMColumn)
            //  {
            //   lbx=blbox.RightEdgeRelative()+6;
            //   lby=14;
            //   lbw=100;
            //  }
            //gbox2.CreateNewElement(GRAPH_ELEMENT_TYPE_WF_LIST_BOX,lbx,lby,lbw,60,clrNONE,255,true,false);
            ////--- get the pointer to the ListBox object by its index in the list of attached objects of ListBox type
            //CListBox *lbox=gbox2.GetElementByType(GRAPH_ELEMENT_TYPE_WF_LIST_BOX,0);
            ////--- If ListBox has been created and the pointer to it has been received
            //if(lbox!=NULL)
            //  {
            //   lbox.SetMultiColumn(true);
            //   lbox.CreateList(8,68);
            //  }
            //*/
           }
        }

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


前回の記事のEAに比べ、タブがにぎやかになりましたね。タブタイトルはコンテナの上にも下にも配置できますが、他の要素がマウス操作でうまく動作する場合、タブタイトルが目立って「点滅」するという欠点もあります。これについては後で扱います。結局、タイトルとタブ欄の間の線にカーソルを合わせました。その線はないはずです。これについては、すでに記事の中で触れています。次回は、タブ(タブ本体とそのタイトル)が一体となるようにします。


次の段階

次回の記事では、WinFormsオブジェクトの開発を続けます。

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

目次に戻る

連載のこれまでの記事

DoEasyコントロール(第1部):最初のステップ
DoEasyコントロール(第2部):CPanelクラスでの作業
DoEasyコントロール(第3部):結合されたコントロールの作成
DoEasyコントロール(第4部):パネルコントロール、Padding、Dockパラメータ
DoEasyコントロール(第5部):WinForms基本オブジェクト、Panelコントロール、AutoSizeパラメータ
DoEasyコントロール(第6部):パネルコントロール、内部コンテンツに合わせたコンテナサイズの自動変更
DoEasy.コントロール(第7部):テキストラベルコントロール
DoEasy.コントロール(第8部):カテゴリ(GroupBoxおよびCheckBoxのコントロール)による基本WinFormsオブジェクト
DoEasy.コントロール(第9部):WinFormsオブジェクトメソッド、RadioButtonおよびButtonコントロールの再配置
DoEasy.コントロール(第10部):WinFormsオブジェクト—インターフェイスのアニメーション化
DoEasy.コントロール(第11部):WinFormsオブジェクト—グループ、CheckedListBoxWinFormsオブジェクト
DoEasy.コントロール(第12部):基本リストオブジェクト、ListBoxおよびButtonListBoxWinFormsオブジェクト
DoEasy.コントロール(第13部):WinFormsオブジェクトのマウスによる操作の最適化、TabControlWinFormsオブジェクトの開発開始



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

添付されたファイル |
MQL5.zip (4422.74 KB)
Bears Powerによる取引システムの設計方法を学ぶ Bears Powerによる取引システムの設計方法を学ぶ
最も人気のあるテクニカル指標によって取引システムを設計する方法を学ぶ連載の新しい記事へようこそ。この新しい記事では、Bears Power(ベアーパワー)テクニカル指標によって取引システムを設計する方法を学びます。
データサイエンスと機械学習—ニューラルネットワーク(第01回):フィードフォワードニューラルネットワークの解明 データサイエンスと機械学習—ニューラルネットワーク(第01回):フィードフォワードニューラルネットワークの解明
ニューラルネットワークの背後にある操作全体は、多くの人に気に入られていますが、ほとんどの人に理解されていません。この記事では、フィードフォワード型の多層知覚の密室の背後にあるすべてを平易な言葉で説明しようとします。
ニューラルネットワークが簡単に(第24部):転移学習用ツールの改善 ニューラルネットワークが簡単に(第24部):転移学習用ツールの改善
前回の記事では、ニューラルネットワークのアーキテクチャを作成および編集するためのツールを作成しました。今日はこのツールでの作業を続けて、より使いやすくします。これは、私たちのトピックから一歩離れていると思われるかもしれませんが、うまく整理されたワークスペースは、結果を達成する上で重要な役割を果たすと思われないでしょうか。
ニューラルネットワークが簡単に(第23部):転移学習用ツールの構築 ニューラルネットワークが簡単に(第23部):転移学習用ツールの構築
転移学習については当連載ですでに何度も言及していますが、これはただの言及でした。この記事では、このギャップを埋めて、転移学習の詳しい調査を提案します。