![DoEasy-コントロール(第14部):グラフィック要素に名前を付けるための新しいアルゴリズム。TabControl WinFormsオブジェクトへの作業の継続](https://c.mql5.com/2/49/doeasy_014_600x314.jpg)
DoEasy-コントロール(第14部):グラフィック要素に名前を付けるための新しいアルゴリズム。TabControl WinFormsオブジェクトへの作業の継続
内容
概念
前回は、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オブジェクトの開発を続けます。
連載のこれまでの記事
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
![Bears Powerによる取引システムの設計方法を学ぶ](https://c.mql5.com/2/48/why-and-how__3.png)
![データサイエンスと機械学習—ニューラルネットワーク(第01回):フィードフォワードニューラルネットワークの解明](https://c.mql5.com/2/48/forward_neural_network.png)
![ニューラルネットワークが簡単に(第24部):転移学習用ツールの改善](https://c.mql5.com/2/48/Neural_networks_made_easy_024.png)
![ニューラルネットワークが簡単に(第23部):転移学習用ツールの構築](https://c.mql5.com/2/48/Neural_networks_made_easy_023.png)
![MQL5 - MetaTrader 5クライアントターミナルにビルトインされたトレードストラテジーの言語](https://c.mql5.com/i/registerlandings/logo-2.png)
- 無料取引アプリ
- 8千を超えるシグナルをコピー
- 金融ニュースで金融マーケットを探索