English Русский 中文 Español Deutsch Português
preview
DoEasy-コントロール(第11部):WinFormsオブジェクト—グループ、CheckedListBox WinFormsオブジェクト

DoEasy-コントロール(第11部):WinFormsオブジェクト—グループ、CheckedListBox WinFormsオブジェクト

MetaTrader 5 | 31 10月 2022, 10:01
88 0
Artyom Trishkin
Artyom Trishkin

内容


概念

単一のコンテナに結合されたWinFormsオブジェクトは、単一のグループに結合された一連のオブジェクトになります。GroupBoxオブジェクトに結合されているか、パネルに結合されているかに関係なく、コンテナは、すべてのオブジェクトを1つのグループに結合するエンティティです。オブジェクトは、このグループの特定のルールに従って動作し始めます。たとえば、RadioButtonオブジェクトは単独ではほとんど役に立ちません。選択されていない状態で作成された場合、そのチェックボックスはクリックするとアクティブになり、再度チェックを外すことはできません。ボックスのチェックを外すには、同じタイプの別のオブジェクトを選択する必要があります。ここに違いがあります。同じコンテナ内で別のRadioButtonオブジェクトを選択すると、チェックボックスは最初のオブジェクトでは無効になり、クリックされた2番目のオブジェクトではアクティブになります。別のコンテナに結合されたRadioButtonを選択しようとすると、最初のグループで最初に選択されたオブジェクトのチェックボックスが無効になりません。これは、異なるコンテナの異なるオブジェクトグループを扱っているためです。

しかし、互いに独立して動作する2つのRadioButtonオブジェクトのセットを同じコンテナに入れたい場合はどうなるでしょうか。結局のところ、それらはコンテナ(グループのインデックスを継承する必要があります)によって1つのオブジェクトのセットに結合され、コンテナから受け取った共通のグループインデックスに従って動作します。このようなオブジェクトの複数の独立した作業セットを1つのコンテナに作成するために、オブジェクトグループの概念を紹介します。

グループインデックス1を持つコンテナ内に6つのRadioButtonオブジェクトのセットを作成すると、それらすべてにグループインデックス1が割り当てられます。それらはすべて、コンテナのグループインデックスによってリンクされ、それに応じて機能します。6つのRadioButtonオブジェクトのいずれかをクリックすると、残りの5つの選択が解除されます。

しかし、グループ2をグループ1の4つのオブジェクトに割り当て、残りの2つのオブジェクトがグループ3を形成する場合、2つのオブジェクトサブグループ2と3がグループ1のコンテナ内に作成されます。したがって、これらの新しいグループはそれぞれ、そのグループのオブジェクトと連動してのみ機能します。

これにより、1つのコンテナ内にさまざまなサブグループを作成し、独自のインデックスの下で独自のグループに結合できるようになり、メインのグループ内にグループ用の新しいコンテナを作成する必要がなくなります。

したがって、2つのトグルボタンを1つのグループに結合することにより、2つのボタンのスイッチに変換します。この場合、最初のボタンを押すと2番目のボタンが解放され、その逆も同様です。複数のトグルボタンを1つのグループにまとめて、すべてのボタンを押していない状態にするか、1つだけ押した状態にして残りを離すことができるようにすることができるということです。

また、一部のサーバーではターミナル名が標準のものと異なる場合があるため、アカウントオブジェクトクラスを改善します。通常、ターミナルの名前を要求すると、サーバーは「MetaTrader 5」という文字列を返しますが、証券会社はターミナル名を変更する文字列に別の何かを追加することがあります。したがって、サーバータイプを定義する代わりに、ターミナル名で「MetaTrader 5」の部分文字列を探すのが合理的です。


ライブラリクラスの改善

\MQL5\Include\DoEasy\Defines.mqhのグラフィック要素タイプの列挙に新しいWinFormsオブジェクトタイプを追加します

//+------------------------------------------------------------------+
//| 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
   //--- '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_CHECKED_LIST_BOX,            // Windows Forms CheckedListBox
  };
//+------------------------------------------------------------------+



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

//+------------------------------------------------------------------+
//| Integer properties of the graphical element on the canvas        |
//+------------------------------------------------------------------+
enum ENUM_CANV_ELEMENT_PROP_INTEGER
  {
   CANV_ELEMENT_PROP_ID = 0,                          // Element ID
   CANV_ELEMENT_PROP_TYPE,                            // Graphical element type
   //---...
   //---...
   CANV_ELEMENT_PROP_ACT_BOTTOM,                      // Bottom border of the element active area
   CANV_ELEMENT_PROP_GROUP,                           // Group the graphical element belongs to
   CANV_ELEMENT_PROP_ZORDER,                          // Priority of a graphical object for receiving the event of clicking on a chart
   //---...
   //---...
   CANV_ELEMENT_PROP_BUTTON_TOGGLE,                   // Toggle flag of the control featuring a button
   CANV_ELEMENT_PROP_BUTTON_GROUP,                    // Button group flag
   CANV_ELEMENT_PROP_BUTTON_STATE,                    // Status of the Toggle control featuring a button
   //---...
   //---...
   CANV_ELEMENT_PROP_CHECK_FLAG_COLOR_MOUSE_DOWN,     // Color of control checkbox when clicking on the control
   CANV_ELEMENT_PROP_CHECK_FLAG_COLOR_MOUSE_OVER,     // Color of control checkbox when hovering the mouse over the control
   
  };
#define CANV_ELEMENT_PROP_INTEGER_TOTAL (83)          // Total number of integer properties
#define CANV_ELEMENT_PROP_INTEGER_SKIP  (0)           // Number of integer properties not used in sorting
//+------------------------------------------------------------------+


グラフィック要素が属するグループはグループインデックスであり、ボタングループフラグは、ボタンが複数のトグルボタンの一部として機能することを示すフラグです。3つのボタンで1つのトグルボタンオブジェクトを構成する場合、それぞれにグループボタンフラグを設定し、同じグループに含める必要があります。


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

//+------------------------------------------------------------------+
//| 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_ACT_BOTTOM,                   // Sort by the bottom border of the element active area
   SORT_BY_CANV_ELEMENT_GROUP,                        // Sort by a group the graphical element belongs to
   SORT_BY_CANV_ELEMENT_ZORDER,                       // Sort by the priority of a graphical object for receiving the event of clicking on a chart
   //---...
   //---...
   SORT_BY_CANV_ELEMENT_BUTTON_TOGGLE,                // Sort by the Toggle flag of the control featuring a button
   SORT_BY_CANV_ELEMENT_BUTTON_GROUP,                 // Sort by button group flag
   SORT_BY_CANV_ELEMENT_BUTTON_STATE,                 // Sort by the status of the Toggle control featuring a button
   SORT_BY_CANV_ELEMENT_CHECK_BACKGROUND_COLOR,       // Sort by color of control checkbox background
   SORT_BY_CANV_ELEMENT_CHECK_BACKGROUND_COLOR_OPACITY,   // Sort by opacity of control checkbox background color
   SORT_BY_CANV_ELEMENT_CHECK_BACKGROUND_COLOR_MOUSE_DOWN,// Sort by color of control checkbox background when clicking on the control
   SORT_BY_CANV_ELEMENT_CHECK_BACKGROUND_COLOR_MOUSE_OVER,// Sort by color of control checkbox background when hovering the mouse over the control
   SORT_BY_CANV_ELEMENT_CHECK_FORE_COLOR,             // Sort by color of control checkbox frame
   SORT_BY_CANV_ELEMENT_CHECK_FORE_COLOR_OPACITY,     // Sort by opacity of control checkbox frame color
   SORT_BY_CANV_ELEMENT_CHECK_FORE_COLOR_MOUSE_DOWN,  // Sort by color of control checkbox frame when clicking on the control
   SORT_BY_CANV_ELEMENT_CHECK_FORE_COLOR_MOUSE_OVER,  // Sort by color of control checkbox frame when hovering the mouse over the control
   SORT_BY_CANV_ELEMENT_CHECK_FLAG_COLOR,             // Sort by color of control checkbox
   SORT_BY_CANV_ELEMENT_CHECK_FLAG_COLOR_OPACITY,     // Sort by opacity of control checkbox color
   SORT_BY_CANV_ELEMENT_CHECK_FLAG_COLOR_MOUSE_DOWN,  // Sort by color of control checkbox when clicking on the control
   SORT_BY_CANV_ELEMENT_CHECK_FLAG_COLOR_MOUSE_OVER,  // Sort by color of control checkbox when hovering the mouse over 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
  };
//+------------------------------------------------------------------+


これで、新しいプロパティでグラフィック要素を並べ替えて選択できるようになります。


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

//--- CPanel
   MSG_PANEL_OBJECT_ERR_FAILED_CREATE_UNDERLAY_OBJ,   // Failed to create the underlay object
   MSG_PANEL_OBJECT_ERR_OBJ_MUST_BE_WFBASE,           // Error. The created object should be of WinForms Base type or be derived from it

//--- CCheckedListBox
   MSG_CHECKED_LIST_ERR_FAILED_CREATE_CHECK_BOX_OBJ,  // Failed to create the CheckBox object
   MSG_CHECKED_LIST_ERR_FAILED_GET_CHECK_BOX_OBJ,     // Failed to get the CheckBox object from the object list

//--- Integer properties of graphical elements


...

   MSG_CANV_ELEMENT_PROP_AUTOCHECK,                   // Auto change flag status when it is selected
   MSG_CANV_ELEMENT_PROP_BUTTON_TOGGLE,               // Toggle flag of the control featuring a button
   MSG_CANV_ELEMENT_PROP_BUTTON_GROUP,                // Button group flag
   MSG_CANV_ELEMENT_PROP_BUTTON_STATE,                // Status of the Toggle control featuring a button
   MSG_CANV_ELEMENT_PROP_CHECK_BACKGROUND_COLOR,      // Color of control checkbox background


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

//--- CPanel
   {"Не удалось создать объект-подложку","Failed to create underlay object"},
   {"Ошибка. Создаваемый объект должен иметь тип WinForms Base или быть его наследником","Error. The object being created must be of type WinForms Base or be derived from it"},

//--- CCheckedListBox
   {"Не удалось создать объект CheckBox","Failed to create CheckBox object"},
   {"Не удалось получить объект CheckBox из списка объектов","Failed to get CheckBox object from list of objects"},

//--- Integer properties of graphical elements


...

   {"Автоматическое изменение состояния флажка при его выборе","Automatically change the state of the checkbox when it is selected"},
   {"Флаг \"Переключатель\" элемента управления, имеющего кнопку","\"Button-toggle\" flag of a control"},
   {"Флаг группы кнопки","Button group flag"},
   {"Состояние элемента управления \"Переключатель\", имеющего кнопку","The \"Toggle-button\" control state"},
   {"Цвет фона флажка проверки элемента управления","The background color of the control's validation checkbox"},



\MQL5\Include\DoEasy\Objects\Accounts\Account.mqhアカウントオブジェクトクラスファイルのクラスコンストラクタのサーバータイプ定義を少し変更します。前に述べたように、ターミナル名文字列を取得する代わりに、ターミナル名文字列内で「MetaTrader 5」部分文字列を検索します。

//+------------------------------------------------------------------+
//| Constructor                                                      |
//+------------------------------------------------------------------+
CAccount::CAccount(void)
  {
   this.m_type=OBJECT_DE_TYPE_ACCOUNT;
//--- Initialize control data
   this.SetControlDataArraySizeLong(ACCOUNT_PROP_INTEGER_TOTAL);
   this.SetControlDataArraySizeDouble(ACCOUNT_PROP_DOUBLE_TOTAL);
   this.ResetChangesParams();
   this.ResetControlsParams();
  
//--- Save integer properties
   this.m_long_prop[ACCOUNT_PROP_LOGIN]                              = ::AccountInfoInteger(ACCOUNT_LOGIN);
   this.m_long_prop[ACCOUNT_PROP_TRADE_MODE]                         = ::AccountInfoInteger(ACCOUNT_TRADE_MODE);
   this.m_long_prop[ACCOUNT_PROP_LEVERAGE]                           = ::AccountInfoInteger(ACCOUNT_LEVERAGE);
   this.m_long_prop[ACCOUNT_PROP_LIMIT_ORDERS]                       = ::AccountInfoInteger(ACCOUNT_LIMIT_ORDERS);
   this.m_long_prop[ACCOUNT_PROP_MARGIN_SO_MODE]                     = ::AccountInfoInteger(ACCOUNT_MARGIN_SO_MODE);
   this.m_long_prop[ACCOUNT_PROP_TRADE_ALLOWED]                      = ::AccountInfoInteger(ACCOUNT_TRADE_ALLOWED);
   this.m_long_prop[ACCOUNT_PROP_TRADE_EXPERT]                       = ::AccountInfoInteger(ACCOUNT_TRADE_EXPERT);
   this.m_long_prop[ACCOUNT_PROP_MARGIN_MODE]                        = #ifdef __MQL5__::AccountInfoInteger(ACCOUNT_MARGIN_MODE) #else ACCOUNT_MARGIN_MODE_RETAIL_HEDGING #endif ;
   this.m_long_prop[ACCOUNT_PROP_CURRENCY_DIGITS]                    = #ifdef __MQL5__::AccountInfoInteger(ACCOUNT_CURRENCY_DIGITS) #else 2 #endif ;
   this.m_long_prop[ACCOUNT_PROP_SERVER_TYPE]                        = (::StringFind(::TerminalInfoString(TERMINAL_NAME),"MetaTrader 5")>WRONG_VALUE ? 5 : 4);
   this.m_long_prop[ACCOUNT_PROP_FIFO_CLOSE]                         = (#ifdef __MQL5__::TerminalInfoInteger(TERMINAL_BUILD)<2155 ? false : ::AccountInfoInteger(ACCOUNT_FIFO_CLOSE) #else false #endif );
   this.m_long_prop[ACCOUNT_PROP_HEDGE_ALLOWED]                      = (#ifdef __MQL5__::TerminalInfoInteger(TERMINAL_BUILD)<3245 ? false : ::AccountInfoInteger(ACCOUNT_HEDGE_ALLOWED) #else false #endif );
   
//--- Save real properties
   this.m_double_prop[this.IndexProp(ACCOUNT_PROP_BALANCE)]          = ::AccountInfoDouble(ACCOUNT_BALANCE);
   this.m_double_prop[this.IndexProp(ACCOUNT_PROP_CREDIT)]           = ::AccountInfoDouble(ACCOUNT_CREDIT);
   this.m_double_prop[this.IndexProp(ACCOUNT_PROP_PROFIT)]           = ::AccountInfoDouble(ACCOUNT_PROFIT);
   this.m_double_prop[this.IndexProp(ACCOUNT_PROP_EQUITY)]           = ::AccountInfoDouble(ACCOUNT_EQUITY);
   this.m_double_prop[this.IndexProp(ACCOUNT_PROP_MARGIN)]           = ::AccountInfoDouble(ACCOUNT_MARGIN);
   this.m_double_prop[this.IndexProp(ACCOUNT_PROP_MARGIN_FREE)]      = ::AccountInfoDouble(ACCOUNT_MARGIN_FREE);
   this.m_double_prop[this.IndexProp(ACCOUNT_PROP_MARGIN_LEVEL)]     = ::AccountInfoDouble(ACCOUNT_MARGIN_LEVEL);
   this.m_double_prop[this.IndexProp(ACCOUNT_PROP_MARGIN_SO_CALL)]   = ::AccountInfoDouble(ACCOUNT_MARGIN_SO_CALL);
   this.m_double_prop[this.IndexProp(ACCOUNT_PROP_MARGIN_SO_SO)]     = ::AccountInfoDouble(ACCOUNT_MARGIN_SO_SO);
   this.m_double_prop[this.IndexProp(ACCOUNT_PROP_MARGIN_INITIAL)]   = ::AccountInfoDouble(ACCOUNT_MARGIN_INITIAL);
   this.m_double_prop[this.IndexProp(ACCOUNT_PROP_MARGIN_MAINTENANCE)]=::AccountInfoDouble(ACCOUNT_MARGIN_MAINTENANCE);
   this.m_double_prop[this.IndexProp(ACCOUNT_PROP_ASSETS)]           = ::AccountInfoDouble(ACCOUNT_ASSETS);
   this.m_double_prop[this.IndexProp(ACCOUNT_PROP_LIABILITIES)]      = ::AccountInfoDouble(ACCOUNT_LIABILITIES);
   this.m_double_prop[this.IndexProp(ACCOUNT_PROP_COMMISSION_BLOCKED)]=::AccountInfoDouble(ACCOUNT_COMMISSION_BLOCKED);
   
//--- Save string properties
   this.m_string_prop[this.IndexProp(ACCOUNT_PROP_NAME)]             = ::AccountInfoString(ACCOUNT_NAME);
   this.m_string_prop[this.IndexProp(ACCOUNT_PROP_SERVER)]           = ::AccountInfoString(ACCOUNT_SERVER);
   this.m_string_prop[this.IndexProp(ACCOUNT_PROP_CURRENCY)]         = ::AccountInfoString(ACCOUNT_CURRENCY);
   this.m_string_prop[this.IndexProp(ACCOUNT_PROP_COMPANY)]          = ::AccountInfoString(ACCOUNT_COMPANY);

//--- Account object name, object and account type (MetaTrader 5 or 4)
   this.m_name=CMessage::Text(MSG_LIB_PROP_ACCOUNT)+" "+(string)this.Login()+": "+this.Name()+" ("+this.Company()+")";
   this.m_type=COLLECTION_ACCOUNT_ID;
   this.m_type_server=(uchar)this.m_long_prop[ACCOUNT_PROP_SERVER_TYPE];

//--- Filling in the current account data
   for(int i=0;i<ACCOUNT_PROP_INTEGER_TOTAL;i++)
      this.m_long_prop_event[i][3]=this.m_long_prop[i];
   for(int i=0;i<ACCOUNT_PROP_DOUBLE_TOTAL;i++)
      this.m_double_prop_event[i][3]=this.m_double_prop[i];

//--- Update the base object data and search for changes
   CBaseObjExt::Refresh();
  }
//+-------------------------------------------------------------------+


上記で受け取った値(5または4)をm_type_server変数に追加します。以前は、「MetaTrader 5」の値のターミナル名をチェックした結果を受け取り、エラーが発生することがありました。これで、アカウントプロパティに既に設定されている値を受け取るようになります。


ライブラリ基本グラフィカルオブジェクトクラスの\MQL5\Include\DoEasy\Objects\Graph\GBaseObj.mqhファイルで、オブジェクトグループ仮想の設定および取得するメソッドを作成します。

//--- Set the values of the class variables
   void              SetObjectID(const long value)             { this.m_object_id=value;              }
   void              SetBelong(const ENUM_GRAPH_OBJ_BELONG belong){ this.m_belong=belong;             }
   void              SetTypeGraphObject(const ENUM_OBJECT obj) { this.m_type_graph_obj=obj;           }
   void              SetTypeElement(const ENUM_GRAPH_ELEMENT_TYPE type) { this.m_type_element=type;   }
   void              SetSpecies(const ENUM_GRAPH_OBJ_SPECIES species){ this.m_species=species;        }
   virtual void      SetGroup(const int value)                 { this.m_group=value;                  }
   void              SetName(const string name)                { this.m_name=name;                    }
   void              SetDigits(const int value)                { this.m_digits=value;                 }


...

   virtual long      Zorder(void)                        const { return this.m_zorder;             }
   int               SubWindow(void)                     const { return this.m_subwindow;          }
   int               ShiftY(void)                        const { return this.m_shift_y;            }
   int               VisibleOnTimeframes(void)           const { return this.m_timeframes_visible; }
   int               Digits(void)                        const { return this.m_digits;             }
   virtual int       Group(void)                         const { return this.m_group;              }
   bool              IsBack(void)                        const { return this.m_back;               }
   bool              IsSelected(void)                    const { return this.m_selected;           }
   bool              IsSelectable(void)                  const { return this.m_selectable;         }
   bool              IsHidden(void)                      const { return this.m_hidden;             }
   bool              IsVisible(void)                     const { return this.m_visible;            }


継承されたクラスでそれらを再定義する必要があります。


GUIのグラフィック要素は、相互に関連する位置の共通の階層によって相互接続されています。従属オブジェクトが結合されているオブジェクトは、関連オブジェクトのチェーンの基本オブジェクトとして機能します。次に、従属オブジェクトは、それらに関連付けられたオブジェクトの独自のチェーンを持つことができますが、基本オブジェクトは、別のオブジェクトに結合されたオブジェクトのチェーン内のリンクになることができます。この場合、メインオブジェクトは、従属オブジェクトを持ち、他のオブジェクトに結合されていないオブジェクトと見なされます。これは、すべての下位オブジェクトの接続の階層全体の祖先です。通常、このようなオブジェクトは、Windowsではウィンドウ、C#ではフォームです。ここでも「ウィンドウ」になります。これは、「フォーム」の定義が、マウスを操作する機能を実装するグラフィック要素によって既に占められており、このオブジェクトが基本WinFormsオブジェクトの親であるためです。

グラフィック要素クラスのMQL5\Include\DoEasy\Objects\Graph\GCnvElement.mqhファイルで基本およびメインオブジェクトIDを返すメソッドを実装します。

//--- Return the flag indicating that the object is (1) main, (2) base
   bool              IsMain(void)                                                      { return this.GetMain()==NULL;               }
   bool              IsBase(void)                                                      { return this.GetBase()==NULL;               }
//--- Get the (1) main and (2) base object ID
   int               GetMainID(void)
                       {
                        if(this.IsMain())
                           return this.ID();
                        CGCnvElement *main=this.GetMain();
                        return(main!=NULL ? main.ID() : WRONG_VALUE);
                       }
   int               GetBaseID(void)
                       {
                        if(this.IsBase())
                           return this.ID();
                        CGCnvElement *base=this.GetBase();
                        return(base!=NULL ? base.ID() : WRONG_VALUE);
                       }
//--- Return the pointer to a canvas object


基本オブジェクトIDの取得を使用しましょう。オブジェクトが既に基本(下位オブジェクトを含む)である場合は、そのIDを返します。それ以外の場合は、基本オブジェクトへのポインタを取得します(これらのポインタは各従属オブジェクトに設定されます)。ポインタを受け取った場合は、基本オブジェクトIDを返しますそれ以外の場合は、-1を返します(error — failed to get object)。
メインオブジェクトIDを取得するためのメソッドロジックは同じです。


グラフィック要素グループを取得および設定するための仮想メソッドを記述します。

//--- Priority of a graphical object for receiving the event of clicking on a chart
   virtual long      Zorder(void)                        const { return this.GetProperty(CANV_ELEMENT_PROP_ZORDER);                    }
   virtual bool      SetZorder(const long value,const bool only_prop)
                       {
                        if(!CGBaseObj::SetZorder(value,only_prop))
                           return false;
                        this.SetProperty(CANV_ELEMENT_PROP_ZORDER,value);
                        return true;
                       }
//--- 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);
                       }
//+------------------------------------------------------------------+


Group()メソッドは、オブジェクトの「グループ」プロパティに設定された値を返します

「グループ」プロパティを設定するメソッドでは、まずメソッドに渡す親オブジェクトの値を設定します。次に、グラフィック要素のプロパティに設定します


コレクション内の各グラフィック要素には、独自の一意のIDが必要です。これにより、IDでオブジェクトを直接参照できるようになり(オブジェクトがプログラムに格納されている場合)、すべてのオブジェクトをループして検索する必要がなくなります。現在、一意のIDは、プログラムから直接作成されたグラフィック要素にのみ割り当てられます。すでに作成されたオブジェクト(新しいオブジェクトの作成元のオブジェクトに結合されているオブジェクト)から新しい結合されたオブジェクトを(プログラムによっても)作成すると、新しく作成された従属オブジェクトは作成元のオブジェクトのIDを受け取ります。この状況では、下位のリストで指定されたオブジェクトの番号を持つ基本1のIDによって、このオブジェクトを取得できます。

もちろん、このアプローチも機能しますが、ライブラリに基づいて作成されたプログラムでの作業を簡素化するために、一意のIDを検索し、新しく作成された従属グラフィック要素に割り当てます。したがって、各グラフィック要素に一意のIDを割り当てて、アクセスできるようにします。第2に、現時点ではまだ作業メソッドが実装されています。つまり、基本オブジェクトを参照し、要素インデックスによってそのリストから必要な要素を取得します。オブジェクトへのポインタを取得する2つの方法は、1つよりも確実に優れています。特に、一意のIDで要素にアクセスするより高速な方法があるためです。

フォームオブジェクトクラスの\MQL5\Include\DoEasy\Objects\Graph\Form.mqhファイルのpublicセクションで最大オブジェクトプロパティ値とIDを検索するための4つのメソッドを宣言します

//--- Return (1) itself, the list of (2) attached objects, (3) the list of interaction objects and (4) shadow object
   CForm            *GetObject(void)                                          { return &this;                  }
   CArrayObj        *GetListElements(void)                                    { return &this.m_list_elements;  }
   CArrayObj        *GetListInteractObj(void)                                 { return &this.m_list_interact;  }
   CShadowObj       *GetShadowObj(void)                                       { return this.m_shadow_obj;      }
//--- Return the maximum value (1) of the specified integer property and (2) ID from all objects bound to the base one
   long              GetMaxLongPropForm(const ENUM_CANV_ELEMENT_PROP_INTEGER prop);
   int               GetMaxIDForm(void);
//--- Return the maximum value (1) of the specified integer property and (2) ID from the entire hierarchy of related objects
   long              GetMaxLongPropAll(const ENUM_CANV_ELEMENT_PROP_INTEGER prop);
   int               GetMaxIDAll(void);
//--- Return the pointer to (1) the animation object, the list of (2) text and (3) rectangular animation frames


パラメータを持つメソッドは、指定されたプロパティの見つかった最大値を返しますが、パラメータのないメソッドは、グラフィック要素IDの見つかった最大値を返します。


新しい接続された要素を作成し、それを結合されたオブジェクトのリストに追加するためのメソッドCreateAndAddNewElement()で、新しく作成されたオブジェクトのIDの設定を変更します。

以前は、新しいオブジェクトが作成されたオブジェクトのIDを設定しました。

   obj.SetID(this.ID());


ここで、IDとしてメインオブジェクトから始まる下位オブジェクトの階層全体から見つかった最大IDを設定し、取得した値に1を追加します。つまり、新しいオブジェクトには、コレクション内のすべてのオブジェクトのすべてのIDの最大値が含まれます。

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

//--- ...
//--- Set the minimum properties for a bound graphical element
   obj.SetBackgroundColor(colour,true);
   obj.SetOpacity(opacity);
   obj.SetActive(activity);
   obj.SetMain(this.GetMain()==NULL ? this.GetObject() : this.GetMain());
   obj.SetBase(this.GetObject());
   obj.SetID(this.GetMaxIDAll()+1);
   obj.SetNumber(num);
   obj.SetCoordXRelative(obj.CoordX()-this.CoordX());
   obj.SetCoordYRelative(obj.CoordY()-this.CoordY());
   obj.SetZorder(this.Zorder(),false);
   obj.SetCoordXRelativeInit(obj.CoordXRelative());
   obj.SetCoordYRelativeInit(obj.CoordYRelative());
   return obj;
  }
//+------------------------------------------------------------------+



カーソルがグラフィック要素の領域から離れた後、オブジェクトの背景、テキスト、およびフレームの色をデフォルト値に戻すために、このイベントのハンドラをオンにします。要素の領域にカーソルを合わせると、グラフィック要素のアクティビティが視覚的に表示されます。複数のテスト中に、色が常に正しく復元されないことがあるのに気付きました。場合によっては、これらのオブジェクトが配置されているフォームにカーソルを再配置して、オブジェクトの色を元の色にリセットする必要があります。ライブラリオブジェクトのビジュアルコンポーネントの開発により、このような省略を取り除きます。グラフィック要素からカーソルを移動する最後のマウスイベントハンドラ処理にさらに別の条件を追加しましょう。

//+------------------------------------------------------------------+
//| Last mouse event handler                                         |
//+------------------------------------------------------------------+
void CForm::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.BackgroundColorInit(),false);
           this.SetBorderColor(this.BorderColorInit(),false);
           this.m_mouse_event_last=ENUM_MOUSE_EVENT(state+MOUSE_EVENT_NO_EVENT);
           this.Redraw(true);
          }
        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;
     }
  }
//+------------------------------------------------------------------+


ここで、カーソルがオブジェクトのアクティブ領域にあったときの状況だけでなく、カーソルがオブジェクトの領域にあったときの状況も処理します。要素およびカーソルは、アクティブなゾーンからフォームを超える前に、非アクティブなゾーンに入りますが、グラフィック要素内にあります。この状況も考慮されるようになりました。


以下は、基本オブジェクトに従属するすべてのオブジェクトの指定された整数プロパティの最大値を返すメソッドです。

//+------------------------------------------------------------------+
//| Return the maximum value of the specified integer                |
//| property from all objects subordinate to the base one            |
//+------------------------------------------------------------------+
long CForm::GetMaxLongPropForm(const ENUM_CANV_ELEMENT_PROP_INTEGER prop)
  {
//--- Get the pointer to the base object
   CForm *base=this.GetBase();
//--- If the base object is received, then set the property value of the specified property, otherwise the value is equal to -1
   long property=(base!=NULL ? base.GetProperty(prop) : WRONG_VALUE);
//--- If the received value is greater than -1
   if(property>WRONG_VALUE)
     {
      //--- In the loop through the list of bound objects
      for(int i=0;i<this.ElementsTotal();i++)
        {
         //--- get the next object
         CForm *elm=this.GetElement(i);
         if(elm==NULL)
            continue;
         //--- If the property value of the received object is greater than the value set in the property,
         //--- set the property value of the current object to 'property'
         if(elm.GetProperty(prop)>property)
            property=elm.GetProperty(prop);
         //--- Get the maximum property value from objects bound to the current one
         long prop_form=elm.GetMaxLongPropForm(prop);
         //--- If the received value is greater than the 'property' value,
         //--- set the received value to 'property'
         if(prop_form>property)
            property=prop_form;
        }
     }
//--- Return the found maximum property value
   return property;
  }
//+------------------------------------------------------------------+


メソッドのロジックは、コードコメントで詳しく説明されています。このメソッドは、フォームに結合されているすべてのオブジェクトの値が0以上(負でない)である、指定された整数プロパティの最大値を検索します。この場合、基本オブジェクトのプロパティの値が考慮され、そこから検索が開始されます。すべての従属オブジェクトは基本オブジェクトから次々と作成されるため、基本に直接接続されているオブジェクトからプロパティの最大値を逃して、基本から離れたオブジェクトから検索を開始するという状況はあり得ません。階層の観点から1を基本にします。
ただし、基本オブジェクトの階層全体を正確に検索する必要がある場合は、そのようなメソッドを簡単に作成できます。


以下は、基本オブジェクトに結合されたすべてのオブジェクトからグラフィック要素IDの最大値を返すメソッドです。

//+------------------------------------------------------------------+
//| Returns the maximum value of an ID                               |
//| from all bound objects                                           |
//+------------------------------------------------------------------+
int CForm::GetMaxIDForm(void)
  {
   return (int)this.GetMaxLongPropForm(CANV_ELEMENT_PROP_ID);
  }
//+------------------------------------------------------------------+


このメソッドは、検索のために「オブジェクトID」プロパティが渡された上記のメソッドの結果を返すだけです。


以下は、関連オブジェクトの階層全体から、指定された整数プロパティの最大値を返すメソッドです。

//+------------------------------------------------------------------+
//| Return the maximum value of the specified integer                |
//| property from the entire hierarchy of related objects            |
//+------------------------------------------------------------------+
long CForm::GetMaxLongPropAll(const ENUM_CANV_ELEMENT_PROP_INTEGER prop)
  {
//--- Get the pointer to the main object
   CForm *main=(this.IsMain() ? this.GetObject() : this.GetMain());
//--- If the main object is obtained, then set the value of the specified property to 'property', otherwise the value is equal to -1
   long property=(main!=NULL ? main.GetProperty(prop) : WRONG_VALUE);
//--- If the received value is greater than -1
   if(property>WRONG_VALUE)
     {
      //--- In the loop through the list of objects bound to the main object
      for(int i=0;i<main.ElementsTotal();i++)
        {
         //--- get the next object
         CForm *elm=main.GetElement(i);
         if(elm==NULL)
            continue;
         //--- Get the maximum value of the property from the entire hierarchy of objects subordinate to the current one in the loop
         long prop_form=elm.GetMaxLongPropForm(prop);
         //--- If the received value is greater than the 'property' value,
         //--- set the received value to 'property'
         if(prop_form>property)
            property=prop_form;
        }
     }
//--- Return the found maximum property value
   return property;
  }
//+------------------------------------------------------------------+


メソッドのロジックはコードのコメントで詳しく説明されており、基本オブジェクトにアタッチされたオブジェクトの最大プロパティを見つける方法に似ています。最初の方法とは異なり、この方法では、メインオブジェクト(関連オブジェクトの階層の祖先)から検索を開始し、関連オブジェクトの階層チェーン全体のすべてのオブジェクトのリストを調べます。その結果、下位オブジェクトの階層全体で最大のプロパティ値が得られます。


以下は、関連オブジェクトの階層全体からIDの最大値を返すメソッドです。

//+------------------------------------------------------------------+
//| Returns the maximum value of an ID                               |
//| from the entire hierarchy of related objects                     |
//+------------------------------------------------------------------+
int CForm::GetMaxIDAll(void)
  {
   return (int)this.GetMaxLongPropAll(CANV_ELEMENT_PROP_ID);
  }
//+------------------------------------------------------------------+

このメソッドは、上記のメソッドを呼び出した結果を返します。このメソッドのパラメータでは、最大値を検索するために「ID」プロパティが渡されます。


グラフィック要素のプロパティの説明は、\MQL5\Include\DoEasy\Objects\Graph\WForms\WinFormBase.mqhの基本WinFormsオブジェクトのクラスに実装されています。

GetPropertyDescription()メソッドで、2つの新しいグラフィック要素プロパティの説明を返すコードブロックを追加します。

      property==CANV_ELEMENT_PROP_ACT_BOTTOM                   ?  CMessage::Text(MSG_CANV_ELEMENT_PROP_ACT_BOTTOM)+
         (only_prop ? "" : !this.SupportProperty(property)     ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)this.GetProperty(property)
         )  :
      property==CANV_ELEMENT_PROP_GROUP                        ?  CMessage::Text(MSG_GRAPH_OBJ_PROP_GROUP)+
         (only_prop ? "" : !this.SupportProperty(property)     ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)this.GetProperty(property)
         )  :
      property==CANV_ELEMENT_PROP_ZORDER                       ?  CMessage::Text(MSG_GRAPH_OBJ_PROP_ZORDER)+
         (only_prop ? "" : !this.SupportProperty(property)     ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)this.GetProperty(property)
         )  :


...

      property==CANV_ELEMENT_PROP_BUTTON_TOGGLE                ?  CMessage::Text(MSG_CANV_ELEMENT_PROP_BUTTON_TOGGLE)+
         (only_prop ? "" : !this.SupportProperty(property)     ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)(bool)this.GetProperty(property)
         )  :
      property==CANV_ELEMENT_PROP_BUTTON_GROUP                 ?  CMessage::Text(MSG_CANV_ELEMENT_PROP_BUTTON_GROUP)+
         (only_prop ? "" : !this.SupportProperty(property)     ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)(bool)this.GetProperty(property)
         )  :
      property==CANV_ELEMENT_PROP_BUTTON_STATE                 ?  CMessage::Text(MSG_CANV_ELEMENT_PROP_BUTTON_STATE)+
         (only_prop ? "" : !this.SupportProperty(property)     ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)(bool)this.GetProperty(property)
         )  :


このメソッドは、すべてのグラフィック要素のプロパティの説明を正しく返すようになりました。


RadioButtonWinFormsオブジェクトは、このタイプの他のオブジェクトとと連動してのみ正しく機能します。さらに、これらのオブジェクトは、同じコンテナに結合されるか、同じグループ値を持つ(同じオブジェクトグループの一部である)必要があります。これらのオブジェクトのいずれかがクリックされると、そのチェックボックスが選択され(以前に選択されていなかった場合)、このグループの他のすべてのオブジェクトのチェックボックスがオフになります。すでに選択されているオブジェクトをもう一度クリックしても、そのフラグはクリアされません。

\MQL5\Include\DoEasy\Objects\Graph\WForms\CommonControls\RadioButton.mqhのRadioButtonオブジェクトクラスを改善しましょう。

クラスのprivateセクションで、同じグループのすべてのオブジェクトに対してメソッド設定の「未選択」状態を宣言します。publicセクションで、指定されたオブジェクトとそのチェックボックスのステータスを設定するメソッドを実装します。

//+------------------------------------------------------------------+
//| CheckBox object class of the WForms controls                     |
//+------------------------------------------------------------------+
class CRadioButton : public CCheckBox
  {
private:
//--- Set the state of the checkbox as "not selected" for all RadioButtons of the same group in the container
   void              UncheckOtherAll(void);
protected:
//--- Displays the checkbox for the specified state
   virtual void      ShowControlFlag(const ENUM_CANV_ELEMENT_CHEK_STATE state);

//--- '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 checkbox status
   virtual void      SetChecked(const bool flag)
                       {
                        this.SetProperty(CANV_ELEMENT_PROP_CHECKED,flag);
                        this.SetCheckState((ENUM_CANV_ELEMENT_CHEK_STATE)flag);
                        if(this.Checked())
                           this.UncheckOtherAll();
                       }
//--- Constructor


チェックボックスのステータス設定メソッドで、メソッドに渡された値をそのプロパティに実装し、選択状態(選択されているかどうか)を設定します。さらに、オブジェクトの状態が「選択済み」の場合は、メソッドを呼び出します。このメソッドでは、このグループの他のすべての類似オブジェクトが「未選択」に設定され、チェックボックスが無効になります。


「カーソルがアクティブ領域内にあり、マウスの左ボタンがクリックされた」イベントハンドラに、オブジェクトのステータスを確認するコードブロックを追加します。 選択されていない場合はオブジェクトを「選択済み」に設定するメソッドを呼び出します

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


ここでは、ログにデバッグエントリが表示され、イベント、要素の状態(選択されている/選択されていない)、そのID、およびグループインデックスが示されます。後で、このエントリを削除し、ライブラリと制御プログラムにメッセージを送信するように置き換えます。


以下は、コンテナ内の同じグループのすべてのRadioButtonオブジェクトに対して、チェックボックスの状態を「未選択」に設定するメソッドです。

//+------------------------------------------------------------------+
//| Sets the state of the checkbox to "not selected"                 |
//| for all RadioButton objects of the same group in the container   |
//+------------------------------------------------------------------+
void CRadioButton::UncheckOtherAll(void)
  {
//--- Get the pointer to the base object
   CWinFormBase *base=this.GetBase();
   if(base==NULL)
      return;
//--- From the base object, get a list of all objects of the RadioButton type
   CArrayObj *list=base.GetListElementsByType(GRAPH_ELEMENT_TYPE_WF_RADIOBUTTON);
//--- Select all objects from the received list, except for the given one (the names of the selected objects are not equal to the name of this one)
   list=CSelect::ByGraphCanvElementProperty(list,CANV_ELEMENT_PROP_NAME_OBJ,this.Name(),NO_EQUAL);
//--- From the received list, select only those objects whose group index matches the group of the current one
   list=CSelect::ByGraphCanvElementProperty(list,CANV_ELEMENT_PROP_GROUP,this.Group(),EQUAL);
//--- If the list of objects is received,
   if(list!=NULL)
     {
      //--- in the loop through all objects in the list
      for(int i=0;i<list.Total();i++)
        {
         //--- get the next object,
         CRadioButton *obj=list.At(i);
         if(obj==NULL)
            continue;
         //--- set its state to "not selected"
         obj.SetChecked(false);
         //--- Redraw the object to display an unselected checkbox
         obj.Redraw(false);
        }
     }
  }
//+------------------------------------------------------------------+

コードの各行はコメントされています。メソッドのロジックに問題がないことを願っています。つまり、コンテナに結合されたすべてのRadioButtonオブジェクトのリストを取得する必要があります。それらはすべて同じグループに属している必要があり、リストにはメソッドが呼び出されたオブジェクトが含まれていてはなりません(結局のところ、これはマウスでクリックされて選択されたオブジェクトなので、選択フラグを削除する必要はありません)。結果のリストをループし、各オブジェクトを未選択状態に設定し、ボックスのチェックを外します。変更を反映するためにオブジェクトが再描画されます。


\MQL5\Include\DoEasy\Objects\Graph\WForms\CommonControls\Button.mqhボタンオブジェクトクラスファイルで、同様の改善をおこない、ボタンをクリック可能にするだけでなく、有効化/無効化状態にすることができます。したがって、それらをグループに割り当てることができます。グループでは、1つのグループによって接続されたボタンが連携して機能します。

クラスのprivateセクションで同じグループのすべてのボタンに「解放済み」ステータスを設定するメソッドを宣言します

//+------------------------------------------------------------------+
//| Label object class of WForms controls                            |
//+------------------------------------------------------------------+
class CButton : public CLabel
  {
private:
   int               m_text_x;                                 // Text X coordinate
   int               m_text_y;                                 // Text Y coordinate
   color             m_array_colors_bg_tgl[];                  // Array of element background colors for the 'enabled' state
   color             m_array_colors_bg_tgl_dwn[];              // Array of control background colors for the 'enabled' state when clicking on the control
   color             m_array_colors_bg_tgl_ovr[];              // Array of control background colors for the 'enabled' state when hovering the mouse over the control
   color             m_array_colors_bg_tgl_init[];             // Array of initial element background colors for the 'enabled' state
//--- Set the button state as "released" for all Buttons of the same group in the container
   void              UnpressOtherAll(void);
protected:



クラスのpublicセクションで、ボタンのステータスを設定するメソッドを変更し、他のボタンオブジェクトとグループで動作するボタンのフラグを設定して返すメソッドを実装します。

//--- (1) Set and (2) return the Toggle control status
   void              SetState(const bool flag)
                       {
                        this.SetProperty(CANV_ELEMENT_PROP_BUTTON_STATE,flag);
                        if(this.State())
                          {
                           this.UnpressOtherAll();
                          }
                       }
   bool              State(void)                         const { return (bool)this.GetProperty(CANV_ELEMENT_PROP_BUTTON_STATE);                                }
   
//--- (1) Set and (2) return the group flag
   void              SetGroupButtonFlag(const bool flag)       { this.SetProperty(CANV_ELEMENT_PROP_BUTTON_GROUP,flag);                                        }
   bool              GroupButton(void)                   const { return (bool)this.GetProperty(CANV_ELEMENT_PROP_BUTTON_GROUP);                                }


ボタンの状態を設定するメソッドでは、最初に状態がオブジェクトプロパティに設定され、状態が「押された」の場合は、同じグループの残りのボタンの状態を「解放済み」に設定するメソッドを呼び出します。


最後のマウスイベントハンドラで、上記で検討した同じ名前のフォームオブジェクトクラスメソッドに似た、さらに別の条件のチェックを追加します。

//+------------------------------------------------------------------+
//| Last mouse event handler                                         |
//+------------------------------------------------------------------+
void CButton::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.BackgroundColorToggleON() : this.BackgroundColorInit(),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;
     }
  }
//+------------------------------------------------------------------+



以下は、コンテナ内の同じグループのすべてのButtonオブジェクトに対して、ボタンの状態を「解放済み」に設定するメソッドです。

//+------------------------------------------------------------------+
//| Sets the state of the button to "released"                       |
//| for all Buttons of the same group in the container               |
//+------------------------------------------------------------------+
void CButton::UnpressOtherAll(void)
  {
//--- Get the pointer to the base object
   CWinFormBase *base=this.GetBase();
   if(base==NULL)
      return;
//--- Get the list of all objects of the Button type from the base object
   CArrayObj *list=base.GetListElementsByType(GRAPH_ELEMENT_TYPE_WF_BUTTON);
//--- Select all objects from the received list, except for the given one (the names of the selected objects are not equal to the name of this one)
   list=CSelect::ByGraphCanvElementProperty(list,CANV_ELEMENT_PROP_NAME_OBJ,this.Name(),NO_EQUAL);
//--- From the received list, select only those objects whose group index matches the group of the current one
   list=CSelect::ByGraphCanvElementProperty(list,CANV_ELEMENT_PROP_GROUP,this.Group(),EQUAL);
//--- If the list of objects is received,
   if(list!=NULL)
     {
      //--- in the loop through all objects in the list
      for(int i=0;i<list.Total();i++)
        {
         //--- get the next object,
         CButton *obj=list.At(i);
         if(obj==NULL)
            continue;
         //--- set the button status to "released",
         obj.SetState(false);
         //--- set the background color to the original one (the cursor is on another button outside this one)
         obj.SetBackgroundColor(obj.BackgroundColorInit(),false);
         //--- Redraw the object to display the changes
         obj.Redraw(false);
        }
     }
  }
//+------------------------------------------------------------------+


メソッドのロジックは、RadioButtonオブジェクトクラスのメソッドと同じです。コードのコメントで完全に説明されており、説明が必要ないことを願っています。


通常の状態では、カーソルをその上に置くと、CheckBoxWinFormsオブジェクトは背景、チェックボックス、およびチェックボックス領域のフレームの色を変更します。オブジェクト自体の背景は変更されません(透明)。ただし、そのようなオブジェクトがグループにまとめられている場合(次に来るオブジェクトの場合のように)、カーソルをオブジェクトの上に置くと、その背景色も変わります。CheckBoxオブジェクトを使用してCheckBoxオブジェクトのオブジェクトリストを作成するには、\MQL5\Include\DoEasy\Objects\Graph\WForms\CommonControls\CheckBox.mqhでこのオブジェクトを変更します。

RadioButton子オブジェクトと同様に、チェックボックスのステータスを設定するメソッドを仮想にします。

//--- (1) Set and (2) return the checkbox status
   virtual void      SetChecked(const bool flag)
                       {
                        this.SetProperty(CANV_ELEMENT_PROP_CHECKED,flag);
                        if((bool)this.CheckState()!=flag)
                           this.SetCheckState((ENUM_CANV_ELEMENT_CHEK_STATE)flag);
                       }
   bool              Checked(void)                             const { return (bool)this.GetProperty(CANV_ELEMENT_PROP_CHECKED);                            }



クラスコンストラクタでオブジェクトの背景色を完全な不透明度に設定し、アクティブ領域のシフトをそれぞれの側で1ピクセルずつ設定します

//+------------------------------------------------------------------+
//| Constructor                                                      |
//+------------------------------------------------------------------+
CCheckBox::CCheckBox(const long chart_id,
                     const int subwindow,
                     const string name,
                     const int x,
                     const int y,
                     const int w,
                     const int h) : CLabel(chart_id,subwindow,name,x,y,w,h)
  {
   CGBaseObj::SetTypeElement(GRAPH_ELEMENT_TYPE_WF_CHECKBOX);
   CGCnvElement::SetProperty(CANV_ELEMENT_PROP_TYPE,GRAPH_ELEMENT_TYPE_WF_CHECKBOX);
   this.m_type=OBJECT_DE_TYPE_GWF_COMMON;
   this.SetCoordX(x);
   this.SetCoordY(y);
   this.SetCheckWidth(DEF_CHECK_SIZE);
   this.SetCheckHeight(DEF_CHECK_SIZE);
   this.SetWidth(w);
   this.SetHeight(h);
   this.Initialize();
   this.SetOpacity(0);
   this.SetForeColorMouseDown(CLR_DEF_FORE_COLOR_MOUSE_DOWN);
   this.SetForeColorMouseOver(CLR_DEF_FORE_COLOR_MOUSE_OVER);
   this.SetCheckBackgroundColor(CLR_DEF_CHECK_BACK_COLOR,true);
   this.SetCheckBackgroundColorMouseDown(CLR_DEF_CHECK_BACK_MOUSE_DOWN);
   this.SetCheckBackgroundColorMouseOver(CLR_DEF_CHECK_BACK_MOUSE_OVER);
   this.SetCheckBorderColor(CLR_DEF_CHECK_BORDER_COLOR,true);
   this.SetCheckBorderColorMouseDown(CLR_DEF_CHECK_BORDER_MOUSE_DOWN);
   this.SetCheckBorderColorMouseOver(CLR_DEF_CHECK_BORDER_MOUSE_OVER);
   this.SetCheckFlagColor(CLR_DEF_CHECK_FLAG_COLOR,true);
   this.SetCheckFlagColorMouseDown(CLR_DEF_CHECK_FLAG_MOUSE_DOWN);
   this.SetCheckFlagColorMouseOver(CLR_DEF_CHECK_FLAG_MOUSE_OVER);
   this.SetWidthInit(this.Width());
   this.SetHeightInit(this.Height());
   this.SetCoordXInit(x);
   this.SetCoordYInit(y);
   this.SetTextAlign(ANCHOR_LEFT);
   this.SetActiveAreaShift(1,1,1,1);
   this.m_text_x=0;
   this.m_text_y=0;
   this.m_check_x=0;
   this.m_check_y=0;
   this.Redraw(false);
  }
//+------------------------------------------------------------------+


ここでは、基本オブジェクトの不透明度を持つオブジェクトをさらに作成する必要があるため、背景を完全な不透明度に設定します。このオブジェクトの作成時に通常の状態で常に不透明度を設定することを避けるために、コンストラクタで明示的に設定します。アクティブ領域を両側で1ピクセルずらすことは、隣接するCheckBoxオブジェクト間のギャップを広げようとする試みであり、カーソルが1つのオブジェクトから離れたときに、すぐに次のオブジェクトにカーソルを合わせることなく、そのオブジェクトに「アクセス」する時間ができるようにします。カーソルが近くのオブジェクトにすぐにヒットすることなく基本オブジェクトを通過するようにします。これはすべて、カーソルを遠ざけた後、近くのオブジェクトがデフォルトの背景色に戻らない場合があるという問題の解決策を探した結果です。ただし、そのような解決策が常に役立つとは限りません。まだ、問題を徹底的に把握して修正する時間を見つけなければなりません。

オブジェクトを再描画するメソッドで、完全な不透明度(値0)を設定する代わりに、オブジェクトプロパティで指定された不透明度の値を指定するようになりました

//+------------------------------------------------------------------+
//| Redraw the object                                                |
//+------------------------------------------------------------------+
void CCheckBox::Redraw(bool redraw)
  {
//--- Fill the object with the background color having full transparency
   this.Erase(this.BackgroundColor(),this.Opacity(),true);
//--- Set corrected text coordinates relative to the checkbox
   this.SetCorrectTextCoords();
//--- Draw the text and checkbox within the set coordinates of the object and the binding point, and update the object 
   this.Text(this.m_text_x,this.m_text_y,this.Text(),this.ForeColor(),this.ForeColorOpacity(),this.TextAnchor());
   this.ShowControlFlag(this.CheckState());
   this.Update(redraw);
  }
//+------------------------------------------------------------------+


これにより、マウスをオブジェクトに合わせたときに背景色を使用して変更できます。背景が不透明な場合(以前のように)、もちろん、背景色の変更は表示できません。


オブジェクトの背景色の変更を必要とするすべてのマウスイベントハンドラに必要な変更を実装します。

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



最後のマウスイベントハンドラに、おなじみのステータスの確認を追加します

//+------------------------------------------------------------------+
//| Last mouse event handler                                         |
//+------------------------------------------------------------------+
void CCheckBox::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.BackgroundColorInit(),false);
           this.SetBorderColor(this.BorderColorInit(),false);
           this.SetCheckBackgroundColor(this.CheckBackgroundColorInit(),false);
           this.SetCheckBorderColor(this.CheckBorderColorInit(),false);
           this.SetCheckFlagColor(this.CheckFlagColorInit(),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;
     }
  }
//+------------------------------------------------------------------+

これで、新しいオブジェクトの作成を開始できます。


CheckedListBox WinFormsオブジェクト

このWinFormsオブジェクトは、CheckBoxオブジェクトのリストを含むパネルです。リストオブジェクトにカーソルを合わせると、チェックボックス、その背景、およびチェックボックス領域の境界線の色とともに背景色が変化します。リストのオブジェクトは、上下に並べて配置することも、複数のピースを列に並べて配置することもできます。今日は、オブジェクトの縦方向の配置のみをおこないます。

\MQL5\Include\DoEasy\Objects\Graph\WForms\CommonControls\ライブラリディレクトリで、CCheckedListBoxクラスの新しいファイルCheckedListBox.mqhを作成します。

このクラスは基本コンテナオブジェクトから継承されます。CContainerクラスとCCheckBoxクラスの両方を「表示」するには、必要なすべてのクラスファイルが既に表示されているパネルオブジェクトクラスファイルをインクルードします。

//+------------------------------------------------------------------+
//|                                               CheckedListBox.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 "..\Containers\Panel.mqh"
//+------------------------------------------------------------------+
//| CCheckedListBox object Class of the WForms controls              |
//+------------------------------------------------------------------+
class CCheckedListBox : public CContainer
  {
  }


privateセクションでは新しいグラフィカルオブジェクトを作成するための仮想メソッドを宣言し、publicセクションでは、指定された数のCheckBoxオブジェクトで構成されるリストを作成するメソッドパラメトリックコンストラクタを宣言します。

//+------------------------------------------------------------------+
//| CCheckedListBox object Class of the WForms controls              |
//+------------------------------------------------------------------+
class CCheckedListBox : public CContainer
  {
private:
//--- Create a new graphical object
   virtual CGCnvElement *CreateNewGObject(const ENUM_GRAPH_ELEMENT_TYPE type,
                                          const int element_num,
                                          const string name,
                                          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:
//--- Create the specified number of CheckBox objects
   void              CreateCheckBox(const int count);
//--- Constructor
                     CCheckedListBox(const long chart_id,
                                     const int subwindow,
                                     const string name,
                                     const int x,
                                     const int y,
                                     const int w,
                                     const int h);
  };
//+------------------------------------------------------------------+



以下は、パラメトリックコンストラクタです。

//+------------------------------------------------------------------+
//| Constructor indicating the chart and subwindow ID                |
//+------------------------------------------------------------------+
CCheckedListBox::CCheckedListBox(const long chart_id,
                                 const int subwindow,
                                 const string name,
                                 const int x,
                                 const int y,
                                 const int w,
                                 const int h) : CContainer(chart_id,subwindow,name,x,y,w,h)
  {
   CGBaseObj::SetTypeElement(GRAPH_ELEMENT_TYPE_WF_CHECKED_LIST_BOX);
   CGCnvElement::SetProperty(CANV_ELEMENT_PROP_TYPE,GRAPH_ELEMENT_TYPE_WF_CHECKED_LIST_BOX);
   this.m_type=OBJECT_DE_TYPE_GWF_COMMON;
   this.SetBorderSizeAll(1);
   this.SetBorderStyle(FRAME_STYLE_SIMPLE);
   this.SetBorderColor(CLR_DEF_BORDER_COLOR,true);
   this.SetForeColor(CLR_DEF_FORE_COLOR,true);
  }
//+------------------------------------------------------------------+


オブジェクトのWinFormsオブジェクトタイプライブラリグラフィカルオブジェクトタイプを設定します。次に、対象フレームのサイズを1ピクセルに設定します。フレームタイプはsimpleです。また、ライブラリ内のグラフィカルオブジェクトのデフォルトの枠とテキストの色を設定します。


以下は、メインパネルに指定された数のCheckBoxオブジェクトを作成するメソッドです。

//+------------------------------------------------------------------+
//| Create the specified number of CheckBox objects                  |
//+------------------------------------------------------------------+
void CCheckedListBox::CreateCheckBox(const int count)
  {
//--- Create a pointer to the CheckBox object
   CCheckBox *cbox=NULL;
//--- In the loop through the specified number of objects
   for(int i=0;i<count;i++)
     {
      //--- Set the coordinates and dimensions of the created object
      int x=this.BorderSizeLeft()+1;
      int y=(cbox==NULL ? this.BorderSizeTop()+1 : cbox.BottomEdgeRelative()+4);
      int w=this.Width()-this.BorderSizeLeft()-this.BorderSizeRight();
      int h=DEF_CHECK_SIZE+1;
      //--- If the object could not be created, display a message in the log
      if(!this.CreateNewElement(GRAPH_ELEMENT_TYPE_WF_CHECKBOX,x,y,w,h,clrNONE,255,true,false))
         CMessage::ToLog(DFUN,MSG_CHECKED_LIST_ERR_FAILED_CREATE_CHECK_BOX_OBJ);
      //--- Get the created object from the list by the loop index
      cbox=this.GetElement(i);
      //--- If the object could not be obtained, display a message in the log
      if(cbox==NULL)
         CMessage::ToLog(DFUN,MSG_CHECKED_LIST_ERR_FAILED_GET_CHECK_BOX_OBJ);
      //--- Set the frame size to zero
      cbox.SetBorderSizeAll(0);
      //--- Set the left center alignment of the checkbox and the text
      cbox.SetCheckAlign(ANCHOR_LEFT);
      cbox.SetTextAlign(ANCHOR_LEFT);
      //--- Set the object text
      cbox.SetText("CheckBox"+string(i+1));
      //--- Set the opacity of the base object and the default background color
      cbox.SetOpacity(this.Opacity());
      cbox.SetBackgroundColor(this.BackgroundColor(),true);
      cbox.SetBackgroundColorMouseOver(CLR_DEF_CONTROL_STD_MOUSE_OVER);
      cbox.SetBackgroundColorMouseDown(CLR_DEF_CONTROL_STD_MOUSE_DOWN);
     }
//--- For the base object, set the auto resizing mode as "increase and decrease"
//--- and set the auto resize flag
   this.SetAutoSizeMode(CANV_ELEMENT_AUTO_SIZE_MODE_GROW_SHRINK,false);
   this.SetAutoSize(true,false);
  }
//+------------------------------------------------------------------+


メソッドロジックは、コード内の詳細なコメントを特徴としています。つまり、パネル上に作成するために必要な数のCheckBoxオブジェクトがメソッドに渡されます。指定された数のオブジェクトのループで、それらを作成し、それらに必要なプロパティを設定します。CheckBoxオブジェクトを作成するループが完了したら、パネルの自動サイズ変更モードを設定して、作成されたすべてのオブジェクトの合計サイズに調整できるようにします。また、パネルの自動サイズ変更フラグを設定します。これにより、パネルは、作成されたオブジェクトに合わせてサイズ変更されるようになります。

以下は、新しいグラフィカルオブジェクトを作成する仮想メソッドです。

//+------------------------------------------------------------------+
//| Create a new graphical object                                    |
//+------------------------------------------------------------------+
CGCnvElement *CCheckedListBox::CreateNewGObject(const ENUM_GRAPH_ELEMENT_TYPE type,
                                                const int obj_num,
                                                const string obj_name,
                                                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);
//--- create the CheckBox object
   CGCnvElement *element=new CCheckBox(this.ChartID(),this.SubWindow(),name,x,y,w,h);
   if(element==NULL)
      ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_FAILED_CREATE_ELM_OBJ),": ",name);
//--- set the object relocation flag and relative coordinates
   element.SetMovable(movable);
   element.SetCoordXRelative(element.CoordX()-this.CoordX());
   element.SetCoordYRelative(element.CoordY()-this.CoordY());
   return element;
  }
//+------------------------------------------------------------------+


オブジェクトの親の1つは、マウスを操作するための機能を備えたフォームオブジェクトのクラスであり、このオブジェクトのクラスは表示されないため、ここでCFormクラスの仮想メソッドをオーバーライドして、新しいグラフィカルオブジェクトを作成する必要があります。このメソッドではメソッドに渡されるオブジェクトの型を確認する必要はありません。作成されるオブジェクトの型が正確にわかっているためです。この型はCheckBoxで、ここで作成し、最小値(再配置フラグと相対座標)を設定します。

クラスの他のすべてのメソッドは、すでにその親クラスにあります。

当然、後でオブジェクトクラスを改良して、その追加機能を実装します。

ここで、このようなオブジェクトを作成できるように、すべてのコンテナクラスにこのタイプのオブジェクトの処理を追加する必要があります。

パネルオブジェクトクラスの\MQL5\Include\DoEasy\Objects\Graph\WForms\Containers\Panel.mqhファイルに、新しく作成されたクラスのファイルをインクルードします

//+------------------------------------------------------------------+
//| Include files                                                    |
//+------------------------------------------------------------------+
#include "Container.mqh"
#include "GroupBox.mqh"
#include "..\..\WForms\Common Controls\CheckedListBox.mqh"
//+------------------------------------------------------------------+
//| Panel object class of WForms controls                            |
//+------------------------------------------------------------------+


これで、この新しいクラスがライブラリのすべてのコンテナクラスに表示されるようになります。


新しいグラフィカルオブジェクトを作成するメソッドで、新しいライブラリオブジェクトの型の処理を追加します

//+------------------------------------------------------------------+
//| Create a new graphical object                                    |
//+------------------------------------------------------------------+
CGCnvElement *CPanel::CreateNewGObject(const ENUM_GRAPH_ELEMENT_TYPE type,
                                       const int obj_num,
                                       const string obj_name,
                                       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;
   switch(type)
     {
      case GRAPH_ELEMENT_TYPE_ELEMENT :
         element=new CGCnvElement(type,this.ID(),obj_num,this.ChartID(),this.SubWindow(),name,x,y,w,h,colour,opacity,movable,activity);
        break;
      case GRAPH_ELEMENT_TYPE_FORM :
         element=new CForm(this.ChartID(),this.SubWindow(),name,x,y,w,h);
        break;
      case GRAPH_ELEMENT_TYPE_WF_CONTAINER         :
         element=new CContainer(this.ChartID(),this.SubWindow(),name,x,y,w,h);
        break;
      case GRAPH_ELEMENT_TYPE_WF_GROUPBOX          :
         element=new CGroupBox(this.ChartID(),this.SubWindow(),name,x,y,w,h);
        break;
      case GRAPH_ELEMENT_TYPE_WF_PANEL             :
         element=new CPanel(this.ChartID(),this.SubWindow(),name,x,y,w,h);
        break;
      case GRAPH_ELEMENT_TYPE_WF_LABEL             :
         element=new CLabel(this.ChartID(),this.SubWindow(),name,x,y,w,h);
        break;
      case GRAPH_ELEMENT_TYPE_WF_CHECKBOX          :
         element=new CCheckBox(this.ChartID(),this.SubWindow(),name,x,y,w,h);
        break;
      case GRAPH_ELEMENT_TYPE_WF_RADIOBUTTON       :
         element=new CRadioButton(this.ChartID(),this.SubWindow(),name,x,y,w,h);
        break;
      case GRAPH_ELEMENT_TYPE_WF_BUTTON            :
         element=new CButton(this.ChartID(),this.SubWindow(),name,x,y,w,h);
        break;
      case GRAPH_ELEMENT_TYPE_WF_CHECKED_LIST_BOX  :
         element=new CCheckedListBox(this.ChartID(),this.SubWindow(),name,x,y,w,h);
        break;
      default:
        break;
     }
   if(element==NULL)
      ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_FAILED_CREATE_ELM_OBJ),": ",name);
   return element;
  }
//+------------------------------------------------------------------+


ここでは、CCheckedListBoxクラスの新しいオブジェクトを作成するだけです。

基本コンテナオブジェクトクラスの\MQL5\Include\DoEasy\Objects\Graph\WForms\Containers\Container.mqhファイル、つまり新しい結合された要素を作成するメソッドで、基本オブジェクトが持つグループと同様のグループを設定します。ただし、作成されたオブジェクトがコンテナオブジェクトでない場合のみです透明な背景色とその完全な透明度の設定を追加し、「テキストラベル」、「CheckBox」、「RadioButton」WinFormsオブジェクトのCheckedListBox WinFormsオブジェクトの処理を追加します。

//+------------------------------------------------------------------+
//| Create a new attached element                                    |
//+------------------------------------------------------------------+
bool CContainer::CreateNewElement(const ENUM_GRAPH_ELEMENT_TYPE element_type,
                                  const int x,
                                  const int y,
                                  const int w,
                                  const int h,
                                  const color colour,
                                  const uchar opacity,
                                  const bool activity,
                                  const bool redraw)
  {
//--- If the object type is less than the base WinForms object
   if(element_type<GRAPH_ELEMENT_TYPE_WF_BASE)
     {
      //--- report the error and return 'false'
      CMessage::ToLog(DFUN,MSG_PANEL_OBJECT_ERR_OBJ_MUST_BE_WFBASE);
      return false;
     }
//--- If failed to create a new graphical element, return 'false'
   CWinFormBase *obj=CForm::CreateAndAddNewElement(element_type,x,y,w,h,colour,opacity,activity);
   if(obj==NULL)
      return false;
//--- Set the text color of the created object to be the same as that of the base container
   obj.SetForeColor(this.ForeColor(),true);
//--- If the created object is not a container, set the same group for it as the one for its base object
   if(obj.TypeGraphElement()<GRAPH_ELEMENT_TYPE_WF_CONTAINER || obj.TypeGraphElement()>GRAPH_ELEMENT_TYPE_WF_GROUPBOX)
      obj.SetGroup(this.Group());
//--- Depending on the created object type,
   switch(obj.TypeGraphElement())
     {
      //--- For the Container, Panel and GroupBox WinForms objects
      case GRAPH_ELEMENT_TYPE_WF_CONTAINER         :
      case GRAPH_ELEMENT_TYPE_WF_PANEL             :
      case GRAPH_ELEMENT_TYPE_WF_GROUPBOX          :
        //--- set the frame color equal to the background color 
        obj.SetBorderColor(obj.BackgroundColor(),true);
        break;
      //--- For the Text 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 WinForms object
      case GRAPH_ELEMENT_TYPE_WF_BUTTON            :
        //--- 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 the CheckedListBox WinForms object
      case GRAPH_ELEMENT_TYPE_WF_CHECKED_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;
      default:
        break;
     }
//--- If the panel has auto resize enabled and features bound objects, call the resize method
   if(this.AutoSize() && this.ElementsTotal()>0)
      this.AutoSizeProcess(redraw);
//--- Redraw the panel and all added objects, and return 'true'
   this.Redraw(redraw);
   return true;
  }
//+------------------------------------------------------------------+



GroupBoxオブジェクトクラスの\MQL5\Include\DoEasy\Objects\Graph\WForms\Containers\GroupBox.mqhファイル、つまり新しいグラフィカルオブジェクトを作成するメソッドに、CheckedListBoxオブジェクトの新しいタイプの処理を追加します

//+------------------------------------------------------------------+
//| Create a new graphical object                                    |
//+------------------------------------------------------------------+
CGCnvElement *CGroupBox::CreateNewGObject(const ENUM_GRAPH_ELEMENT_TYPE type,
                                          const int obj_num,
                                          const string obj_name,
                                          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;
   switch(type)
     {
      case GRAPH_ELEMENT_TYPE_ELEMENT              :
         element=new CGCnvElement(type,this.ID(),obj_num,this.ChartID(),this.SubWindow(),name,x,y,w,h,colour,opacity,movable,activity);
        break;
      case GRAPH_ELEMENT_TYPE_FORM                 :
         element=new CForm(this.ChartID(),this.SubWindow(),name,x,y,w,h);
        break;
      case GRAPH_ELEMENT_TYPE_WF_CONTAINER         :
         element=new CContainer(this.ChartID(),this.SubWindow(),name,x,y,w,h);
        break;
      case GRAPH_ELEMENT_TYPE_WF_GROUPBOX          :
         element=new CGroupBox(this.ChartID(),this.SubWindow(),name,x,y,w,h);
        break;
      case GRAPH_ELEMENT_TYPE_WF_PANEL             :
         element=new CPanel(this.ChartID(),this.SubWindow(),name,x,y,w,h);
        break;
      case GRAPH_ELEMENT_TYPE_WF_LABEL             :
         element=new CLabel(this.ChartID(),this.SubWindow(),name,x,y,w,h);
        break;
      case GRAPH_ELEMENT_TYPE_WF_CHECKBOX          :
         element=new CCheckBox(this.ChartID(),this.SubWindow(),name,x,y,w,h);
        break;
      case GRAPH_ELEMENT_TYPE_WF_RADIOBUTTON       :
         element=new CRadioButton(this.ChartID(),this.SubWindow(),name,x,y,w,h);
        break;
      case GRAPH_ELEMENT_TYPE_WF_BUTTON            :
         element=new CButton(this.ChartID(),this.SubWindow(),name,x,y,w,h);
        break;
      case GRAPH_ELEMENT_TYPE_WF_CHECKED_LIST_BOX  :
         element=new CCheckedListBox(this.ChartID(),this.SubWindow(),name,x,y,w,h);
        break;
      default:
        break;
     }
   if(element==NULL)
      ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_FAILED_CREATE_ELM_OBJ),": ",name);
   return element;
  }
//+------------------------------------------------------------------+



Initialize()変数の初期化メソッドの最後に、デフォルトのグループ値の設定を追加します

   this.SetEnabled(true);
   this.SetVisible(true,false);
//--- Set the default group value
   this.SetGroup((int)this.GetMaxLongPropAll(CANV_ELEMENT_PROP_GROUP)+1);
  }
//+------------------------------------------------------------------+


ここでは、ライブラリグラフィック要素のコレクション内のすべてのオブジェクトから指定されたプロパティの最大値を返す、前に検討したメソッドを呼び出します。「グループ」プロパティを目的のパラメータとして指定し、結果の値に1を追加します。これにより、新しいグループの値が最大に設定されます。他のコンテナオブジェクトと区別する一意のグループを持つことが重要です。


\MQL5\Include\DoEasy\Collections\GraphElementsCollection.mqhグラフィック要素コレクションクラスファイルで、CheckedListBoxオブジェクトクラスのファイルをインクルードファイルのリストに追加します。

//+------------------------------------------------------------------+
//| Include files                                                    |
//+------------------------------------------------------------------+
#include "ListObj.mqh"
#include "..\Services\Select.mqh"
#include "..\Objects\Graph\WForms\Containers\GroupBox.mqh"
#include "..\Objects\Graph\WForms\Containers\Panel.mqh"
#include "..\Objects\Graph\WForms\Common Controls\CheckedListBox.mqh"
#include "..\Objects\Graph\Standard\GStdVLineObj.mqh"
#include "..\Objects\Graph\Standard\GStdHLineObj.mqh"



privateクラスセクションで、すべてのコレクションのグラフィック要素から最大IDを返すメソッドを宣言します。

//--- Return the screen coordinates of each pivot point of the graphical object
   bool              GetPivotPointCoordsAll(CGStdGraphObj *obj,SDataPivotPoint &array_pivots[]);
//--- Return the maximum ID from all graphical elements of the collection
   int               GetMaxID(void);
   
public:



すべてのグラフィック要素作成メソッドで、コレクション内のグラフィック要素の総数をIDとして設定する文字列を置き換えます。

int id=this.m_list_all_canv_elm_obj.Total();

すべてのコレクションのグラフィック要素から最大のIDに1を加えた文字列を割り当てます

//--- Create a graphical element object on canvas on a specified chart and subwindow
   int               CreateElement(const long chart_id,
                                   const int subwindow,
                                   const string name,
                                   const int x,
                                   const int y,
                                   const int w,
                                   const int h,
                                   const color clr,
                                   const uchar opacity,
                                   const bool movable,
                                   const bool activity,
                                   const bool redraw=false)
                       {
                        int id=this.GetMaxID()+1;
                        CGCnvElement *obj=new CGCnvElement(GRAPH_ELEMENT_TYPE_ELEMENT,id,0,chart_id,subwindow,name,x,y,w,h,clr,opacity,movable,activity,redraw);
                        ENUM_ADD_OBJ_RET_CODE res=this.AddOrGetCanvElmToCollection(obj,id);
                        if(res==ADD_OBJ_RET_CODE_ERROR)
                           return WRONG_VALUE;
                        if(res==ADD_OBJ_RET_CODE_EXIST)
                           obj.SetID(id);
                        obj.Erase(clr,opacity,redraw);
                        return obj.ID();
                       }
//--- Create a graphical element object on canvas on a specified chart and subwindow with the vertical gradient filling


このような変更は、グラフィック要素を作成するすべてのメソッドでおこなわれています。ここでは提示しません。このような変更はすべて、記事に添付されているライブラリファイルで確認できます。

カーソルの下にある以前のアクティブフォームを処理するメソッドで、現在のループオブジェクトのマウスイベントハンドラの呼び出しを追加して、オブジェクトがまだ非アクティブオブジェクトのリストにない場合に、カーソルがそこから離れたときにその処理をスキップしないようにします。しかし、実際にはすでに非アクティブです。

//+------------------------------------------------------------------+
//| Post-processing of the former active form under the cursor       |
//+------------------------------------------------------------------+
void CGraphElementsCollection::FormPostProcessing(void)
  {
 //--- Get all the elements of the CForm type and above
   CArrayObj *list=GetList(CANV_ELEMENT_PROP_TYPE,GRAPH_ELEMENT_TYPE_FORM,EQUAL_OR_MORE);
   if(list==NULL)
      return;
   //--- In the loop by the list of received elements
   int total=list.Total();
   for(int i=0;i<total;i++)
     {
      //--- get the pointer to the object
      CForm *obj=list.At(i);
      //--- if failed to get the pointer, move on to the next one in the list
      if(obj==NULL)
         continue;
      obj.OnMouseEventPostProcessing();
      //--- Create the list of interaction objects and get their number
      int count=obj.CreateListInteractObj();
      //--- In the loop by the obtained list
      for(int j=0;j<count;j++)
        {
         //--- get the next object
         CForm *elm=obj.GetInteractForm(j);
         if(elm==NULL)
            continue;
         //--- and call its method of handling mouse events
         elm.OnMouseEventPostProcessing();
        }
     }
  }
//+------------------------------------------------------------------+



マウスの右ボタンとプログラムのGUIオブジェクトとの相互作用の処理を実装するために、OnChartEvent()イベントハンドラでマウスの右ボタンを押したままにするためのチェックを追加します。

//--- Handling mouse events of graphical objects on canvas
//--- If the event is not a chart change
   else
     {
      //--- Check whether the mouse button is clicked
      ENUM_MOUSE_BUTT_KEY_STATE butt_state=this.m_mouse.ButtonKeyState(id,lparam,dparam,sparam);
      bool pressed=(butt_state==MOUSE_BUTT_KEY_STATE_LEFT || butt_state==MOUSE_BUTT_KEY_STATE_RIGHT ? true : false);
      ENUM_MOUSE_FORM_STATE mouse_state=MOUSE_FORM_STATE_NONE;
      //--- Declare static variables for the active form and status flags


マウスボタンの状態は変数に設定されるため、後でこの値が必要になった場合に、ボタンの状態を読み取るメソッドを再度呼び出す必要はありません。


以下は、コレクション内のすべてのグラフィック要素から最大IDを返すメソッドです。

//+------------------------------------------------------------------+
//| Return the maximum ID                                            |
//| of all graphical elements of the collection                      |
//+------------------------------------------------------------------+
int CGraphElementsCollection::GetMaxID(void)
  {
   int index=CSelect::FindGraphCanvElementMax(this.GetListCanvElm(),CANV_ELEMENT_PROP_ID);
   CGCnvElement *obj=this.m_list_all_canv_elm_obj.At(index);
   return(obj!=NULL ? obj.ID() : WRONG_VALUE);
  }
//+------------------------------------------------------------------+

ここでは、ID値が最も高いコレクションリスト内のオブジェクトのインデックスを取得します。受信したインデックスによってオブジェクトへのポインタを取得し、オブジェクトへのポインタが受信された場合はオブジェクトIDを返します。その他の場合は-1を返します。グラフィック要素のコレクションが空であるか、ポインタの取得中にエラーが発生しました。


検証

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

メインパネルのサイズを大きくし、最初のコンテナオブジェクトにCheckBoxオブジェクトと4つのグループ値2のRadioButtonオブジェクトを配置します。また、3つのボタンオブジェクトを配置しますが、そのうち2つはグループ2で3番目は、そのコンテナオブジェクトグループからデフォルトで継承されたグループ1に属します。ボタンの下に、グループ値が3のRadioButtonオブジェクトをさらに2つ配置します。したがって、コンテナには、グループ2の4つのRadioButtonオブジェクト、グループ3の2つのRadioButtonオブジェクト、および3つのボタン(グループ2の2つとグループ1の1つ)があります。

最初のGroupBoxコンテナの右側に、同じタイプの別のコンテナを配置し、その中に新しいCheckedListBoxオブジェクトを作成します。このオブジェクトは、4つのCheckBoxオブジェクトを作成するために使用されます。同じコンテナの異なるグループに配置されたすべてのオブジェクトは、個別のオブジェクトセットとして機能する必要があります。オブジェクトとマウスの相互作用のビジュアルコンポーネント全体がうまく機能するはずです。

OnInit()EAハンドラに、すべてのGUI要素を作成するための次のコードブロックを配置します。

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- Set EA global variables
   ArrayResize(array_clr,2);        // Array of gradient filling colors
   array_clr[0]=C'26,100,128';      // Original ≈Dark-azure color
   array_clr[1]=C'35,133,169';      // Lightened original color
//--- Create the array with the current symbol and set it to be used in the library
   string array[1]={Symbol()};
   engine.SetUsedSymbols(array);
   //--- Create the timeseries object for the current symbol and period, and show its description in the journal
   engine.SeriesCreate(Symbol(),Period());
   engine.GetTimeSeriesCollection().PrintShort(false); // Short descriptions

//--- Create WinForms Panel object
   CPanel *pnl=NULL;
   pnl=engine.CreateWFPanel("WFPanel",50,50,400,200,array_clr,200,true,true,false,-1,FRAME_STYLE_BEVEL,true,false);
   if(pnl!=NULL)
     {
      //--- Set Padding to 4
      pnl.SetPaddingAll(4);
      //--- Set the flags of relocation, auto resizing and auto changing mode from the inputs
      pnl.SetMovable(InpMovable);
      pnl.SetAutoSize(InpAutoSize,false);
      pnl.SetAutoSizeMode((ENUM_CANV_ELEMENT_AUTO_SIZE_MODE)InpAutoSizeMode,false);
      //--- In the loop, create 2 bound panel objects
      CPanel *obj=NULL;
      for(int i=0;i<2;i++)
        {
         //--- create the panel object with calculated coordinates, width of 90 and height of 40
         CPanel *prev=pnl.GetElement(i-1);
         int xb=0, yb=0;
         int x=(prev==NULL ? xb : xb+prev.Width()+20);
         int y=0;
         if(pnl.CreateNewElement(GRAPH_ELEMENT_TYPE_WF_PANEL,x,y,90,40,C'0xCD,0xDA,0xD7',200,true,false))
           {
            obj=pnl.GetElement(i);
            if(obj==NULL)
               continue;
            obj.SetBorderSizeAll(3);
            obj.SetBorderStyle(FRAME_STYLE_BEVEL);
            obj.SetBackgroundColor(obj.ChangeColorLightness(obj.BackgroundColor(),4*i),true);
            obj.SetForeColor(clrRed,true);
            //--- Calculate the width and height of the future text label object
            int w=obj.Width()-obj.BorderSizeLeft()-obj.BorderSizeRight();
            int h=obj.Height()-obj.BorderSizeTop()-obj.BorderSizeBottom();
            //--- Create a text label object
            obj.CreateNewElement(GRAPH_ELEMENT_TYPE_WF_LABEL,0,0,w,h,clrNONE,255,false,false);
            //--- Get the pointer to a newly created object
            CLabel *lbl=obj.GetElement(0);
            if(lbl!=NULL)
              {
               //--- If the object has an even or zero index in the list, set the default text color for it
               if(i % 2==0)
                  lbl.SetForeColor(CLR_DEF_FORE_COLOR,true);
               //--- If the object index in the list is odd, set the object opacity to 127
               else
                  lbl.SetForeColorOpacity(127);
               //--- Set the font Black width type and
               //--- specify the text alignment from the EA settings
               lbl.SetFontBoldType(FW_TYPE_BLACK);
               lbl.SetTextAlign(InpTextAlign);
               lbl.SetAutoSize((bool)InpTextAutoSize,false);
               //--- For an object with an even or zero index, specify the Bid price for the text, otherwise - the Ask price of the symbol 
               lbl.SetText(GetPrice(i % 2==0 ? SYMBOL_BID : SYMBOL_ASK));
               //--- Set the frame width, type and color for a text label and update the modified object
               lbl.SetBorderSizeAll(1);
               lbl.SetBorderStyle((ENUM_FRAME_STYLE)InpFrameStyle);
               lbl.SetBorderColor(CLR_DEF_BORDER_COLOR,true);
               lbl.Update(true);
              }
           }
        }
      //--- Create the WinForms GroupBox1 object
      CGroupBox *gbox1=NULL;
      //--- The indent from the attached panels by 6 pixels will be the Y coordinate of GrotupBox1
      int w=pnl.GetUnderlay().Width();
      int y=obj.BottomEdgeRelative()+6;
      //--- If the attached GroupBox object is created
      if(pnl.CreateNewElement(GRAPH_ELEMENT_TYPE_WF_GROUPBOX,0,y,200,150,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
         gbox1=pnl.GetElementByType(GRAPH_ELEMENT_TYPE_WF_GROUPBOX,0);
         if(gbox1!=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
            gbox1.SetBorderStyle(FRAME_STYLE_STAMP);
            gbox1.SetBorderColor(pnl.BackgroundColor(),true);
            gbox1.SetForeColor(gbox1.ChangeColorLightness(obj.BackgroundColor(),-1),true);
            gbox1.SetText("GroupBox1");
            //--- Create the CheckBox object
            gbox1.CreateNewElement(GRAPH_ELEMENT_TYPE_WF_CHECKBOX,2,10,50,20,clrNONE,255,true,false);
            //--- get the pointer to the CheckBox object by its index in the list of bound CheckBox type objects
            CCheckBox *cbox=gbox1.GetElementByType(GRAPH_ELEMENT_TYPE_WF_CHECKBOX,0);
            //--- If CheckBox is created and the pointer to it is received
            if(cbox!=NULL)
              {
               //--- Set the CheckBox parameters from the EA inputs
               cbox.SetAutoSize((bool)InpCheckAutoSize,false);
               cbox.SetCheckAlign(InpCheckAlign);
               cbox.SetTextAlign(InpCheckTextAlign);
               //--- Set the displayed text, frame style and color, as well as checkbox status
               cbox.SetText("CheckBox");
               cbox.SetBorderStyle((ENUM_FRAME_STYLE)InpCheckFrameStyle);
               cbox.SetBorderColor(CLR_DEF_BORDER_COLOR,true);
               cbox.SetChecked(true);
               cbox.SetCheckState((ENUM_CANV_ELEMENT_CHEK_STATE)InpCheckState);
              }
            //--- Create 4 RadioButton WinForms objects
            CRadioButton *rbtn=NULL;
            for(int i=0;i<4;i++)
              {
               //--- Create the RadioButton object
               int yrb=(rbtn==NULL ? cbox.BottomEdgeRelative() : rbtn.BottomEdgeRelative());
               gbox1.CreateNewElement(GRAPH_ELEMENT_TYPE_WF_RADIOBUTTON,2,yrb+4,50,20,clrNONE,255,true,false);
               //--- get the pointer to the RadioButton object by its index in the list of bound RadioButton type objects
               rbtn=gbox1.GetElementByType(GRAPH_ELEMENT_TYPE_WF_RADIOBUTTON,i);
               //--- If RadioButton1 is created and the pointer to it is received
               if(rbtn!=NULL)
                 {
                  //--- Set the RadioButton parameters from the EA inputs
                  rbtn.SetAutoSize((bool)InpCheckAutoSize,false);
                  rbtn.SetCheckAlign(InpCheckAlign);
                  rbtn.SetTextAlign(InpCheckTextAlign);
                  //--- Set the displayed text, frame style and color, as well as checkbox status
                  rbtn.SetText("RadioButton"+string(i+1));
                  rbtn.SetBorderStyle((ENUM_FRAME_STYLE)InpCheckFrameStyle);
                  rbtn.SetBorderColor(CLR_DEF_BORDER_COLOR,true);
                  rbtn.SetChecked(!i);
                  rbtn.SetGroup(2);
                 }
              }
            //--- Create 3 Button WinForms objects
            CButton *butt=NULL;
            for(int i=0;i<3;i++)
              {
               //--- Create the Button object
               int ybtn=(butt==NULL ? 12 : butt.BottomEdgeRelative()+4);
               gbox1.CreateNewElement(GRAPH_ELEMENT_TYPE_WF_BUTTON,(int)fmax(rbtn.RightEdgeRelative(),cbox.RightEdgeRelative())+20,ybtn,78,18,clrNONE,255,true,false);
               //--- get the pointer to the Button object by its index in the list of bound Button type objects
               butt=gbox1.GetElementByType(GRAPH_ELEMENT_TYPE_WF_BUTTON,i);
               //--- If Button is created and the pointer to it is received
               if(butt!=NULL)
                 {
                  //--- Set the Button parameters from the EA inputs
                  butt.SetAutoSize((bool)InpButtonAutoSize,false);
                  butt.SetAutoSizeMode((ENUM_CANV_ELEMENT_AUTO_SIZE_MODE)InpButtonAutoSizeMode,false);
                  butt.SetTextAlign(InpButtonTextAlign);
                  //--- Set the text color, as well as frame style and color
                  butt.SetForeColor(butt.ChangeColorLightness(CLR_DEF_FORE_COLOR,2),true);
                  butt.SetBorderStyle((ENUM_FRAME_STYLE)InpButtonFrameStyle);
                  butt.SetBorderColor(butt.ChangeColorLightness(butt.BackgroundColor(),-10),true);
                  //--- Set the 'toggle' mode depending on the settings
                  butt.SetToggleFlag(InpButtonToggle);
                  //--- Set the displayed text on the button depending on the 'toggle' flag
                  string txt="Button"+string(i+1);
                  if(butt.Toggle())
                     butt.SetText("Toggle-"+txt);
                  else
                     butt.SetText(txt);
                  if(i<2)
                    {
                     butt.SetGroup(2);
                     if(butt.Toggle())
                       {
                        butt.SetBackgroundColorMouseOver(butt.ChangeColorLightness(butt.BackgroundColor(),-5));
                        butt.SetBackgroundColorMouseDown(butt.ChangeColorLightness(butt.BackgroundColor(),-10));
                        butt.SetBackgroundColorToggleON(C'0xE2,0xC5,0xB1',true);
                        butt.SetBackgroundColorToggleONMouseOver(butt.ChangeColorLightness(butt.BackgroundColorToggleON(),-5));
                        butt.SetBackgroundColorToggleONMouseDown(butt.ChangeColorLightness(butt.BackgroundColorToggleON(),-10));
                       }
                    }
                 }
              }
            //--- Create 2 RadioButton WinForms objects
            rbtn=NULL;
            for(int i=0;i<2;i++)
              {
               //--- Create the RadioButton object
               int yrb=(rbtn==NULL ? butt.BottomEdgeRelative() : rbtn.BottomEdgeRelative());
               gbox1.CreateNewElement(GRAPH_ELEMENT_TYPE_WF_RADIOBUTTON,butt.CoordXRelative()-4,yrb+3,50,20,clrNONE,255,true,false);
               //--- get the pointer to the RadioButton object by its index in the list of bound RadioButton type objects
               rbtn=gbox1.GetElementByType(GRAPH_ELEMENT_TYPE_WF_RADIOBUTTON,i+4);
               //--- If RadioButton1 is created and the pointer to it is received
               if(rbtn!=NULL)
                 {
                  //--- Set the RadioButton parameters from the EA inputs
                  rbtn.SetAutoSize((bool)InpCheckAutoSize,false);
                  rbtn.SetCheckAlign(InpCheckAlign);
                  rbtn.SetTextAlign(InpCheckTextAlign);
                  //--- Set the displayed text, frame style and color, as well as checkbox status
                  rbtn.SetText("RadioButton"+string(i+5));
                  rbtn.SetBorderStyle((ENUM_FRAME_STYLE)InpCheckFrameStyle);
                  rbtn.SetBorderColor(CLR_DEF_BORDER_COLOR,true);
                  rbtn.SetChecked(!i);
                  rbtn.SetGroup(3);
                 }
              }
           }
        }
      //--- Create the GroupBox2 WinForms object
      CGroupBox *gbox2=NULL;
      //--- The indent from the attached panels by 6 pixels will be the Y coordinate of GrotupBox2
      w=gbox1.Width()-1;
      int x=gbox1.RightEdgeRelative()+1;
      int h=gbox1.BottomEdgeRelative()-6;
      //--- 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 CheckedListBox object
            gbox2.CreateNewElement(GRAPH_ELEMENT_TYPE_WF_CHECKED_LIST_BOX,4,12,80,80,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.CreateCheckBox(4);
              }
           }
        }
      //--- Redraw all objects according to their hierarchy
      pnl.Redraw(true);
     }
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+


メソッドは、コードコメントで十分に詳細に説明されています。ご自分で分析できます。質問がある場合は、下のコメント欄でお気軽にお問い合わせください。

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


宣言された機能が正しく動作することがわかります。異なるグループに配置された同じコンテナの類似オブジェクトは、正しく機能します。各グループのオブジェクトは独立した単位であり、別のグループの同じタイプのオブジェクトには影響しません。グループに結合された2つのトグルボタンは正しく機能し、独立して機能する3番目のボタンには影響しません。現在の状態のCheckedListBoxオブジェクトも適切に機能します。ビジュアルコンポーネント全体が正常に動作します(これは、マウスとそのボタンの以前の状態を処理するときに色を変更するときに、将来的にエラーが発生することを排除するものではありません)。今後のグラフィック要素の開発中に、考えられるすべてのエラーを見つけて修正していきます。


次の段階

次回の記事では、ライブラリーに基づいて作成されたGUIプログラムのグラフィック要素に関する作業を続けます。

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

目次に戻る

**連載のこれまでの記事:

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



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

添付されたファイル |
MQL5.zip (4447.61 KB)
標準偏差による取引システムの設計方法を学ぶ 標準偏差による取引システムの設計方法を学ぶ
これは、MetaTrader 5取引プラットフォームで最も人気のあるテクニカル指標による取引システムの設計方法に関する連載の新しい記事です。この新しい記事では、標準偏差指標による取引システムの設計方法を学びます。
価格変動モデルとその主な規定(第2回)。価格場の確率的発展方程式と観測されたランダムウォークの発生 価格変動モデルとその主な規定(第2回)。価格場の確率的発展方程式と観測されたランダムウォークの発生
この記事では、確率的な価格場の発展方程式と、今後の価格高騰の基準について考察しています。また、チャート上での価格値の本質と、そのランダムウォークが発生するメカニズムも明らかにします。
一からの取引エキスパートアドバイザーの開発(第24部):システムの堅牢性の提供(I) 一からの取引エキスパートアドバイザーの開発(第24部):システムの堅牢性の提供(I)
この記事では、堅牢で安全な使用を保証するために、システムの信頼性を高めます。望ましい堅牢性を実現する方法の1つは、コードを可能な限り再利用して、常にさまざまな場合にテストされるようにすることです。しかし、これは方法の1つにすぎません。もう1つは、OOPを使用することです。
ニューラルネットワークの実験(第2回):スマートなニューラルネットワークの最適化 ニューラルネットワークの実験(第2回):スマートなニューラルネットワークの最適化
この記事では、実験と非標準的なアプローチを使用して、収益性の高い取引システムを開発し、ニューラルネットワークがトレーダーに役立つかどうかを確認します。ニューラルネットワークを取引に活用するための自給自足ツールとしてMetaTrader 5を使用します。