DoEasy. 控件 (第 12 部分): 基准列表对象、ListBox 和 ButtonListBox WinForms 对象
内容
概述
在本文中,我将继续针对函数库中 WinForms 对象的工作。 在上一篇文章中,我创建了 CheckedListBox 对象,它本质上是一个 CheckBox 对象的列表。 但由于我们将进一步创建更多不同的 WinForms 对象列表,因此现在创建 WinForms 对象的基准对象列表的类,并在其基础上创建所有其它类是合理的。 该类将包含处理 WinForms 对象列表,以及随后绑定到控件的各种数据(MS Visual Studio 中的数据绑定)的主要功能,这些数据是为了在行中显示元素列表。 这将允许显示来源于函数库本身、终端、以及其数据库等环境的完全不同的数据,当然,也可以经由这些列表访问它们。
基于该类,我将创建 ListBox WinForms 对象,这是一个显示特定数据集合的简单列表。 目前,该列表仅将显示在其构造期间所创建的列表项的名称。 这些项允许与鼠标交互(在悬停和选择时更改背景颜色)。 该对象尚无任何实际用途(它只是该对象未来某项功能的可视化显示),由于为了创建 WinForms 对象的事件功能,我们仍然需要创建一定数量的它们,以便判定需要传递给事件处理程序的数据。 创建的不同对象越多,我们对必要的数据及其结构就有更多认知,这是正确创建 WinForms 对象的事件功能所必需的。
此外,我将创建按钮列表对象。 由于 ListBox(其行)完全基于 CButton 对象类而创建,因此创建额外一个对象,显示其列表中的一组按钮是非常合乎逻辑的(类似于在其列表中显示 CheckBox 对象的 CheckedListBox 对象)。 MS Visual Studio 中的标准控件列表不包含此类对象,因此它将作为一次实验。
改进库类
客户端最后一次更新之后,函数库的交易类 CTrading 终止工作了 — 从继承类访问其 OpenPosition() 和 PlaceOrder() 私密方法已被禁止。
因此,在 \MQL5\Include\DoEasy\Trading.mqh 中,在类的受保护部分设置这些方法:
//--- Return the error handling method ENUM_ERROR_CODE_PROCESSING_METHOD ResultProccessingMethod(const uint result_code); //--- Correct errors ENUM_ERROR_CODE_PROCESSING_METHOD RequestErrorsCorrecting(MqlTradeRequest &request,const ENUM_ORDER_TYPE order_type,const uint spread_multiplier,CSymbol *symbol_obj,CTradeObj *trade_obj); //--- (1) Open a position, (2) place a pending order protected: template<typename SL,typename TP> bool OpenPosition(const ENUM_POSITION_TYPE type, const double volume, const string symbol, const ulong magic=ULONG_MAX, const SL sl=0, const TP tp=0, const string comment=NULL, const ulong deviation=ULONG_MAX, const ENUM_ORDER_TYPE_FILLING type_filling=WRONG_VALUE); template<typename PR,typename PL,typename SL,typename TP> bool PlaceOrder( const ENUM_ORDER_TYPE order_type, const double volume, const string symbol, const PR price, const PL price_limit=0, const SL sl=0, const TP tp=0, const ulong magic=ULONG_MAX, const string comment=NULL, const datetime expiration=0, const ENUM_ORDER_TYPE_TIME type_time=WRONG_VALUE, const ENUM_ORDER_TYPE_FILLING type_filling=WRONG_VALUE); private: //--- Return the request object index in the list by (1) ID, //--- (2) order ticket, (3) position ticket in the request int GetIndexPendingRequestByID(const uchar id); int GetIndexPendingRequestByOrder(const ulong ticket); int GetIndexPendingRequestByPosition(const ulong ticket); public:
在此,我已在私密部分中创建了受保护的部分,然后返回私密部分。
在 \MQL5\Include\DoEasy\Defines.mqh 中,把三个新的类型加入到图形元素类型的枚举当中:
//+------------------------------------------------------------------+ //| 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_ELEMENTS_LIST_BOX, // Base list object of Windows Forms elements GRAPH_ELEMENT_TYPE_WF_LIST_BOX, // Windows Forms ListBox GRAPH_ELEMENT_TYPE_WF_CHECKED_LIST_BOX, // Windows Forms CheckedListBox GRAPH_ELEMENT_TYPE_WF_BUTTON_LIST_BOX, // Windows Forms ButtonListBox }; //+------------------------------------------------------------------+
在画布上图形元素的整数型属性列表的最后,添加两个新属性,并将整数型属性的数量从 83 增加到 85:
//+------------------------------------------------------------------+ //| 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_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 CANV_ELEMENT_PROP_LIST_BOX_MULTI_COLUMN, // Horizontal display of columns in the ListBox control CANV_ELEMENT_PROP_LIST_BOX_COLUMN_WIDTH, // Width of each ListBox control column }; #define CANV_ELEMENT_PROP_INTEGER_TOTAL (85) // Total number of integer properties #define CANV_ELEMENT_PROP_INTEGER_SKIP (0) // Number of integer properties not used in sorting //+------------------------------------------------------------------+
将这两个新属性添加到画布上图形元素的可能排序条件的枚举当中:
//+------------------------------------------------------------------+ //| 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_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_CANV_ELEMENT_LIST_BOX_MULTI_COLUMN, // Sort by horizontal column display flag in the ListBox control SORT_BY_CANV_ELEMENT_LIST_BOX_COLUMN_WIDTH, // Sort by the width of each ListBox control column //--- 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 }; //+------------------------------------------------------------------+
现在,我们能够按这两个新属性选择和排序对象。 事实上,在处理图形对象来构建 GUI 时,并没有特别的需要(我尚未见过依据这些属性作为选项进行搜索的)。 不过,我们把 GUI 对象的所有属性无遗漏地输入到这些列表只中,以便我们可以直接在图表窗口中为程序构建 GUI 来创建和处理图形 shell。 这就是您需要获取和更改图形元素的所有属性的位置。
在 \MQL5\Include\DoEasy\Data.mqh 里,加入新的函数库消息索引,重命名索引项 MSG_CHECKED_LIST_ERR_FAILED_CREATE_CHECK_BOX_OBJ,并删除索引项 MSG_CHECKED_LIST_ERR_FAILED_GET_CHECK_BOX_OBJ:
//--- WinForms standard MSG_GRAPH_ELEMENT_TYPE_WF_COMMON_BASE, // WinForms base standard control MSG_GRAPH_ELEMENT_TYPE_WF_LABEL, // Label control MSG_GRAPH_ELEMENT_TYPE_WF_CHECKBOX, // CheckBox control MSG_GRAPH_ELEMENT_TYPE_WF_RADIOBUTTON, // RadioButton control MSG_GRAPH_ELEMENT_TYPE_WF_BUTTON, // Button control MSG_GRAPH_ELEMENT_TYPE_WF_ELEMENTS_LIST_BOX, // Base list object of Windows Forms elements MSG_GRAPH_ELEMENT_TYPE_WF_LIST_BOX, // ListBox control MSG_GRAPH_ELEMENT_TYPE_WF_CHECKED_LIST_BOX, // CheckedListBox control MSG_GRAPH_ELEMENT_TYPE_WF_BUTTON_LIST_BOX, // ButtonListBox control MSG_GRAPH_OBJ_BELONG_PROGRAM, // Graphical object belongs to a program MSG_GRAPH_OBJ_BELONG_NO_PROGRAM, // Graphical object does not belong to a program
...
//--- 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 //--- ElementsListBox MSG_ELM_LIST_ERR_FAILED_GET_GRAPH_ELEMENT_OBJ, // Failed to get a graphical element //--- CButtonListBox MSG_BUTT_LIST_ERR_FAILED_SET_GROUP_BUTTON, // Failed to set the group for the button with the index MSG_BUTT_LIST_ERR_FAILED_SET_TOGGLE_BUTTON, // Failed to set the Toggle flag to the button with the index //--- Integer properties of graphical elements
...
MSG_CANV_ELEMENT_PROP_CHECK_FLAG_COLOR_MOUSE_DOWN, // Color of control checkbox when clicking on the control MSG_CANV_ELEMENT_PROP_CHECK_FLAG_COLOR_MOUSE_OVER, // Color of control checkbox when hovering the mouse over the control MSG_CANV_ELEMENT_PROP_LIST_BOX_MULTI_COLUMN, // Horizontal display of columns in the ListBox control MSG_CANV_ELEMENT_PROP_LIST_BOX_COLUMN_WIDTH, // Width of each ListBox control column //--- Real properties of graphical elements //--- String properties of graphical elements MSG_CANV_ELEMENT_PROP_NAME_OBJ, // Graphical element object name MSG_CANV_ELEMENT_PROP_NAME_RES, // Graphical resource name MSG_CANV_ELEMENT_PROP_TEXT, // Graphical element text }; //+------------------------------------------------------------------+
以及与新添加的索引对应的文本消息:
//--- WinForms standard {"Базовый стандартный элемент управления WinForms","Basic Standard WinForms Control"}, {"Элемент управления \"Label\"","Control element \"Label\""}, {"Элемент управления \"CheckBox\"","Control element \"CheckBox\""}, {"Элемент управления \"RadioButton\"","Control element \"RadioButton\""}, {"Элемент управления \"Button\"","Control element \"Button\""}, {"Базовый объект-список Windows Forms элементов","Basic Windows Forms List Object"}, {"Элемент управления \"ListBox\"","Control element \"ListBox\""}, {"Элемент управления \"CheckedListBox\"","Control element \"CheckedListBox\""}, {"Элемент управления \"ButtonListBox\"","Control element \"ButtonListBox\""}, {"Графический объект принадлежит программе","The graphic object belongs to the program"}, {"Графический объект не принадлежит программе","The graphic object does not belong to the program"},
...
//--- CPanel {"Не удалось создать объект-подложку","Failed to create underlay object"}, {"Ошибка. Создаваемый объект должен иметь тип WinForms Base или быть его наследником","Error. The object being created must be of type WinForms Base or be derived from it"}, //--- ElementsListBox {"Не удалось получить графический элемент ","Failed to get graphic element "}, //--- CButtonListBox {"Не удалось установить группу кнопке с индексом ","Failed to set group for button with index "}, {"Не удалось установить флаг \"Переключатель\" кнопке с индексом ","Failed to set the \"Toggle\" flag on the button with index "}, //--- Integer properties of graphical elements
...
{"Цвет флажка проверки элемента управления при нажатии мышки на элемент управления","Control Checkbox Colorl when the mouse is pressed on the control"}, {"Цвет флажка проверки элемента управления при наведении мышки на элемент управления","Control Checkbox Colorl when hovering the mouse over the control"}, {"Горизонтальное отображение столбцов в элементе управления ListBox","Display columns horizontally in a ListBox control"}, {"Ширина каждого столбца элемента управления ListBox","The width of each column of the ListBox control"}, //--- String properties of graphical elements {"Имя объекта-графического элемента","The name of the graphic element object"}, {"Имя графического ресурса","Image resource name"}, {"Текст графического элемента","Text of the graphic element"}, }; //+---------------------------------------------------------------------+
我们需要返回指定类型的图形元素的描述。 目前,函数库的基准图形对象类拥有 TypeElementDescription() 方法,该方法返回当前图形对象类型的描述,换言之,是其自身类型的描述。 但是我们需要依据方法输入参数中指定的图形元素返回其描述。 因此,我们来这样做:往现有方法里添加一个形式参数,我们将在其中传递对象类型,并实现一个重载方法,返回当前对象类型。
在 \MQL5\Include\DoEasy\Objects\Graph\GBaseObj.mqh 中进行必要的修改。
在公开部分中,声明一个新的重载方法,其中我们传递一个对象类型,而之前的方法会将当前对象类型传递给新方法:
//--- Return the graphical object type (ENUM_OBJECT) calculated from the object type (ENUM_OBJECT_DE_TYPE) passed to the method ENUM_OBJECT GraphObjectType(const ENUM_OBJECT_DE_TYPE obj_type) const { return ENUM_OBJECT(obj_type-OBJECT_DE_TYPE_GSTD_OBJ-1); } //--- Return the description of the type of the graphical object (1) type, (2, 3) element, (4) affiliation and (5) species string TypeGraphObjectDescription(void); string TypeElementDescription(const ENUM_GRAPH_ELEMENT_TYPE type); string TypeElementDescription(void); string BelongDescription(void); string SpeciesDescription(void);
在类主体之外完成这两种方法。
在带有形式参数的方法中,添加根据指定类型返回函数库图形元素新类型的描述:
//+------------------------------------------------------------------+ //| Return the description of the graphical element type | //+------------------------------------------------------------------+ string CGBaseObj::TypeElementDescription(const ENUM_GRAPH_ELEMENT_TYPE type) { return ( type==GRAPH_ELEMENT_TYPE_STANDARD ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_STANDARD) : type==GRAPH_ELEMENT_TYPE_STANDARD_EXTENDED ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_STANDARD_EXTENDED) : type==GRAPH_ELEMENT_TYPE_ELEMENT ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_ELEMENT) : type==GRAPH_ELEMENT_TYPE_SHADOW_OBJ ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_SHADOW_OBJ) : type==GRAPH_ELEMENT_TYPE_FORM ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_FORM) : type==GRAPH_ELEMENT_TYPE_WINDOW ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WINDOW) : //--- WinForms type==GRAPH_ELEMENT_TYPE_WF_UNDERLAY ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_UNDERLAY) : type==GRAPH_ELEMENT_TYPE_WF_BASE ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_BASE) : //--- Containers type==GRAPH_ELEMENT_TYPE_WF_CONTAINER ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_CONTAINER) : type==GRAPH_ELEMENT_TYPE_WF_GROUPBOX ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_GROUPBOX) : type==GRAPH_ELEMENT_TYPE_WF_PANEL ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_PANEL) : //--- Standard controls type==GRAPH_ELEMENT_TYPE_WF_COMMON_BASE ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_COMMON_BASE) : type==GRAPH_ELEMENT_TYPE_WF_LABEL ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_LABEL) : type==GRAPH_ELEMENT_TYPE_WF_CHECKBOX ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_CHECKBOX) : type==GRAPH_ELEMENT_TYPE_WF_RADIOBUTTON ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_RADIOBUTTON) : type==GRAPH_ELEMENT_TYPE_WF_BUTTON ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_BUTTON) : type==GRAPH_ELEMENT_TYPE_WF_ELEMENTS_LIST_BOX ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_ELEMENTS_LIST_BOX) : type==GRAPH_ELEMENT_TYPE_WF_LIST_BOX ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_LIST_BOX) : type==GRAPH_ELEMENT_TYPE_WF_CHECKED_LIST_BOX ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_CHECKED_LIST_BOX) : type==GRAPH_ELEMENT_TYPE_WF_BUTTON_LIST_BOX ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_BUTTON_LIST_BOX) : "Unknown" ); } //+------------------------------------------------------------------+
以前的方法,返回当前对象的类型,现在则依据所传递的当前对象类型返回调用其重载方法的结果:
//+------------------------------------------------------------------+ //| Return the description of the graphical element type | //+------------------------------------------------------------------+ string CGBaseObj::TypeElementDescription(void) { return this.TypeElementDescription(this.TypeGraphElement()); } //+------------------------------------------------------------------+
由于在群组中按钮的操作直接取决于按钮属于哪个群组,因此实现将群组索引以文本消息的形式输出到日志,来控制按钮群组的正确选择。
在 \MQL5\Include\DoEasy\Objects\Graph\WForms\Common Controls\Button.mqh 中,即在“光标处于活动区域内,单击鼠标左键”事件处理程序中,添加处理群组中操作的按钮,并将按钮群组的索引写入日志数据的输出字符串:
//+------------------------------------------------------------------+ //| 'The cursor is inside the active area, | //| left mouse button released | //+------------------------------------------------------------------+ void CButton::MouseActiveAreaReleasedHandler(const int id,const long& lparam,const double& dparam,const string& sparam) { //--- The mouse button released outside the element means refusal to interact with the element if(lparam<this.CoordX() || lparam>this.RightEdge() || dparam<this.CoordY() || dparam>this.BottomEdge()) { //--- If this is a simple button, set the initial background color if(!this.Toggle()) this.SetBackgroundColor(this.BackgroundColorInit(),false); //--- If this is the toggle button, set the initial color depending on whether the button is pressed or not else this.SetBackgroundColor(!this.State() ? this.BackgroundColorInit() : this.BackgroundColorToggleONInit(),false); //--- Set the initial frame color this.SetBorderColor(this.BorderColorInit(),false); //--- Send the test message to the journal Print(DFUN_ERR_LINE,TextByLanguage("Отмена","Cancel")); } //--- The mouse button released within the element means a click on the control else { //--- If this is a simple button, set the color for "The cursor is over the active area" status if(!this.Toggle()) this.SetBackgroundColor(this.BackgroundColorMouseOver(),false); //--- If this is the toggle button, else { //--- if the button does not work in the group, set its state to the opposite, if(!this.GroupButtonFlag()) this.SetState(!this.State()); //--- if the button is not pressed yet, set it to the pressed state else if(!this.State()) this.SetState(true); //--- set the background color for "The cursor is over the active area" status depending on whether the button is clicked or not this.SetBackgroundColor(this.State() ? this.BackgroundColorToggleONMouseOver() : this.BackgroundColorMouseOver(),false); } //--- Send the test message to the journal Print(DFUN_ERR_LINE,TextByLanguage("Щелчок","Click"),", this.State()=",this.State(),", ID=",this.ID(),", Group=",this.Group()); //--- Set the frame color for "The cursor is over the active area" status this.SetBorderColor(this.BorderColorMouseOver(),false); } //--- Redraw the object this.Redraw(false); } //+------------------------------------------------------------------+
我们有若干种按钮切换的操作模式。 单个切换按钮既可以按下,亦或释放。 如果有若干个切换按钮位于同一容器中,并且属于相同的群组,则它们的操作方式是按下其中一个按钮就会导致该群组中的其余按钮释放。 如果我们再次按下已经按下的按钮,则它就会被释放。
如果我们为每个群组按钮添加一个群组按钮标志,且每个按钮都属于相同的群组,那么这些按钮的操作方式会略有不同。 类似地,按下一个按钮会导致释放其余按钮,但按下已按下的按钮则它不会被释放。 如此,在这种情况下,将始终有一个按钮被按下。
记录群组编号是一条调试消息。 调试以后会被删除,但为了检查操作的有效性,我们有时需要查看所点击按钮所属的群组。
重命名返回群组按钮标志的 GroupButton() 方法,明确该方法只返回标志,而不是群组编号:
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 GroupButtonFlag(void) const { return (bool)this.GetProperty(CANV_ELEMENT_PROP_BUTTON_GROUP); } //--- (1,2) Set and (3) return the main background color for the 'enabled' status void SetBackgroundColorToggleON(const color colour,const bool set_init_color)
WinForms 对象列表的基类
WinForms 对象列表类拥有类似的功能,因此建议创建一个基准对象,从而其衍生后代都能拥有共用功能,其余对象将从该基准对象继承,并在其子类中创建自己固有的独特功能。
在 \MQL5\Include\DoEasy\Objects\Graph\WForms\Common Controls\ 函数库文件夹里,创建一个含 CElementsListBox 类的新文件 ElementsListBox.mqh。
该类应继承自基准容器类,且在其中应包含类文件:
//+------------------------------------------------------------------+ //| ElementsListBox.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\Container.mqh" //+------------------------------------------------------------------+ //| Class of the base object of the WForms control list | //+------------------------------------------------------------------+ class CElementsListBox : public CContainer { }
在类的私密部分中,声明一个方法,返回列表中下一个已创建对象的坐标。 在受保护的部分中,声明一个方法,依据指定数量创建指定的 WinForms 对象。 在类的公开部分中,声明一个参数化构造函数,和设置和返回在列表中放置对象的权限的方法,以及设置每列宽度的方法:
//+------------------------------------------------------------------+ //| Class of the base object of the WForms control list | //+------------------------------------------------------------------+ class CElementsListBox : public CContainer { private: //--- Return the coordinates of the next object placed in the list void GetCoordsObj(CWinFormBase *obj,int &x,int &y); protected: //--- Create the specified number of specified WinForms objects void CreateElements(ENUM_GRAPH_ELEMENT_TYPE element_type, const int count, const int x, const int y, const int w, const int h, uint new_column_width=0, const bool autosize=true); public: //--- Constructor CElementsListBox(const long chart_id, const int subwindow, const string name, const int x, const int y, const int w, const int h); //--- (1) Set and (2) return the flag of horizontal display of columns void SetMultiColumn(const bool flag) { this.SetProperty(CANV_ELEMENT_PROP_LIST_BOX_MULTI_COLUMN,flag); } bool MultiColumn(void) const { return (bool)this.GetProperty(CANV_ELEMENT_PROP_LIST_BOX_MULTI_COLUMN); } //--- (1) Set and (2) return the width of each column void SetColumnWidth(const uint value) { this.SetProperty(CANV_ELEMENT_PROP_LIST_BOX_COLUMN_WIDTH,value); } uint ColumnWidth(void) const { return (uint)this.GetProperty(CANV_ELEMENT_PROP_LIST_BOX_COLUMN_WIDTH); } }; //+------------------------------------------------------------------+
我们来仔细查看所声明的方法。
在类构造函数中,指定图形元素的类型,和函数库的图形对象的类型,为简单边框设置一个像素的宽度,设置对象边框和内部文本的默认颜色,禁用按列放置列表,并将列宽指定为零:
//+------------------------------------------------------------------+ //| Constructor indicating the chart and subwindow ID | //+------------------------------------------------------------------+ CElementsListBox::CElementsListBox(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_ELEMENTS_LIST_BOX); CGCnvElement::SetProperty(CANV_ELEMENT_PROP_TYPE,GRAPH_ELEMENT_TYPE_WF_ELEMENTS_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); this.SetMultiColumn(false); this.SetColumnWidth(0); } //+------------------------------------------------------------------+
该方法依据指定数量创建确定的 WinForms 对象:
//+------------------------------------------------------------------+ //| Create the specified number of certain WinForms objects | //+------------------------------------------------------------------+ void CElementsListBox::CreateElements(ENUM_GRAPH_ELEMENT_TYPE element_type, const int count, const int x, const int y, const int w, const int h, uint new_column_width=0, const bool autosize=true) { //--- Set the width of columns if the value greater than zero has been passed if(new_column_width>0) { if(this.ColumnWidth()!=new_column_width) this.SetColumnWidth(new_column_width); } //--- Create a pointer to the created WinFormBase object CWinFormBase *obj=NULL; //--- In the loop through the specified number of objects for(int i=0;i<count;i++) { //--- Get the coordinates of the created object int coord_x=x, coord_y=y; this.GetCoordsObj(obj,coord_x,coord_y); //--- If the object could not be created, send the appropriate message to the log and move on to the next one if(!this.CreateNewElement(element_type,coord_x,coord_y,w,h,clrNONE,255,true,false)) { ::Print(DFUN,MSG_LIB_SYS_FAILED_CREATE_ELM_OBJ,this.TypeElementDescription(element_type)); continue; } //--- Get the created object from the list by the loop index obj=this.GetElement(i); //--- If the object could not be obtained, send the appropriate message to the log and move on to the next one if(obj==NULL) { ::Print(DFUN,MSG_ELM_LIST_ERR_FAILED_GET_GRAPH_ELEMENT_OBJ,this.TypeElementDescription(element_type)); continue; } //--- Set the frame size of the created object to zero obj.SetBorderSizeAll(0); //--- Set the opacity of the base object and the default background color obj.SetOpacity(this.Opacity()); obj.SetBackgroundColor(this.BackgroundColor(),true); obj.SetBackgroundColorMouseOver(CLR_DEF_CONTROL_STD_MOUSE_OVER); obj.SetBackgroundColorMouseDown(CLR_DEF_CONTROL_STD_MOUSE_DOWN); } //--- If the flag of auto resizing the base object is passed to the method, //--- set the auto resize mode to "increase and decrease" if(autosize) this.SetAutoSizeMode(CANV_ELEMENT_AUTO_SIZE_MODE_GROW_SHRINK,false); } //+------------------------------------------------------------------+
方法逻辑已在代码注释中讲述。 我相信,一切都很清晰明了。 创建附着对象时,面板必须能够自动调整大小,从而令面板的尺寸与所创建元素对齐。 某些类不需要这样做,这就是为什么引入了一个标志的原因,它指示是否需要将面板的尺寸与其内容对齐。
该方法返回放置在列表中的下一个对象的坐标:
//+------------------------------------------------------------------+ //| Return the coordinates of the next object placed in the list | //+------------------------------------------------------------------+ void CElementsListBox::GetCoordsObj(CWinFormBase *obj,int &x,int &y) { //--- Save the coordinates passed to the method in the variables int coord_x=x; int coord_y=y; //--- If the flag of using multiple columns is not set, if(!this.MultiColumn()) { //--- set the X coordinate the same as the one passed to the method, //--- set the Y coordinate for the first object in the list to be equal to the one passed to the method, //--- set the rest 4 pixels lower than the bottom edge of the previous object located above. //--- After setting the coordinates to the variables, leave the method x=coord_x; y=(obj==NULL ? coord_y : obj.BottomEdgeRelative()+4); return; } //--- If multiple columns can be used //--- If this is the first object in the list, if(obj==NULL) { //--- set the coordinates the same as those passed to the method and leave x=coord_x; y=coord_y; return; } //--- If this is not the first object in the list //--- If (the bottom border of the previous object + 4 pixels) is below the bottom border of the ListBox panel (the next object will go beyond the borders), if(obj.BottomEdge()+4>this.BottomEdge()) { //--- If the columns width is zero, then the X coordinate of the created object will be the right border of the previous object + 6 pixels //--- Otherwise, if the width of the columns is greater than zero, then the X coordinate of the created object will be the X coordinate of the previous one + the column width //--- The Y coordinate will be the value passed to the method (start placing objects in a new column) x=(this.ColumnWidth()==0 ? obj.RightEdgeRelative()+6 : int(obj.CoordXRelative()+this.ColumnWidth())); y=coord_y; } //--- If the created object is placed within the ListBox panel, else { //--- the X coordinate of the created object will be the offset of the previous one from the panel edge minus the width of its frame, //--- the Y coordinate will be the lower border of the previous object located above plus 4 pixels x=obj.CoordXRelative()-this.BorderSizeLeft(); y=obj.BottomEdgeRelative()+4; } } //+------------------------------------------------------------------+
于此,该方法的逻辑在代码的注释中已有详细说明。 该方法根据列表中第一个对象所对应的初始坐标,以及列表中已定位的前一个对象的坐标,来计算它们下一个对象的坐标。 此外,如果设置了允许对象可在若干列中排列的标志,则该方法将计算下一个创建对象是否适合面板区域(仅参考其位置坐标,而非整个对象)。 如果一个对象(其 Y 坐标)适合面板,则把它构建在前一个对象之下。 如果坐标超出面板,则在初始 Y 坐标处,相对于前一个对象的右侧,与右边框之间构建该对象。 这意味着开始构造新的一列。 对象在面板内定位后,调用当前对象的方法,根据其尺寸调整内部内容。 因此,所有位置不准确的最低对象都将予以纠正。 事实上,由于列表中一个对象的高度可能与另一个对象不同,因此形成的坐标会不精准(底部对象的 Y 坐标在面板内部,而其其余部分在限制之外),故稍后我们能够在列表中放置不同的对象。 因此,与其计算未来对象的尺寸,并考虑它是否会完全落入面板区域,以及从对象底边到面板底边的距离是否正确,我们简单地调整面板尺寸令其与内部已创建内容相适合更容易一些。
鉴于我们现在有一个 WinForms 列表对象的基准对象,我们需要修改已创建的 CheckedListBox 列表对象。
我们来改进在 \MQL5\Include\DoEasy\Objects\Graph\WForms\Common Controls\CheckedListBox.mqh 里的对象类。
取代在类文件里包含面板对象类,
#include "..\Containers\Panel.mqh"
用新创建的类文件替代。 相应地,我们现在将从其继承:
//+------------------------------------------------------------------+ //| 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 "ElementsListBox.mqh" //+------------------------------------------------------------------+ //| CheckedListBox object class of the WForms controls | //+------------------------------------------------------------------+ class CCheckedListBox : public CElementsListBox {
在声明的依据指定数量创建 CheckBox 对象的方法中,添加指定创建对象宽度和新列宽值的形式参数:
public: //--- Create the specified number of CheckBox objects void CreateCheckBox(const int count,const int width,const int new_column_width=0); //--- Constructor
在类构造函数中,即在其初始化清单中,将参数传递给新的父类:
//+------------------------------------------------------------------+ //| 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) : CElementsListBox(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); } //+------------------------------------------------------------------+
依据指定数量创建 CheckBox 对象的方法现已重新设计,因为新的父类已提供了按指定类型和指定数量创建对象的方法:
//+------------------------------------------------------------------+ //| Create the specified number of CheckBox objects | //+------------------------------------------------------------------+ void CCheckedListBox::CreateCheckBox(const int count,const int width,const int new_column_width=0) { //--- Create a pointer to the CheckBox object CCheckBox *obj=NULL; //--- Create the specified number of CheckBox objects CElementsListBox::CreateElements(GRAPH_ELEMENT_TYPE_WF_CHECKBOX,count,2,2,width,DEF_CHECK_SIZE+1,new_column_width); //--- In the loop by the created number of objects for(int i=0;i<this.ElementsTotal();i++) { //--- Get the created object from the list by the loop index obj=this.GetElement(i); //--- If the object could not be obtained, send the appropriate message to the log and move on to the next one if(obj==NULL) { ::Print(DFUN,MSG_ELM_LIST_ERR_FAILED_GET_GRAPH_ELEMENT_OBJ,this.TypeElementDescription(GRAPH_ELEMENT_TYPE_WF_CHECKBOX)); continue; } //--- Set the left center alignment of the checkbox and the text obj.SetCheckAlign(ANCHOR_LEFT); obj.SetTextAlign(ANCHOR_LEFT); //--- Set the object text obj.SetText("CheckBox"+string(i+1)); } } //+------------------------------------------------------------------+
因此,首先创建对象列表,然后依据已创建对象数量进行循环,为每个对象指定必要的参数。
该方法变得更简洁,更具可读性。
ListBox 和 ButtonListBox WinForms 对象类
我们开始开发新的 ListBox WinForms 对象类。
该对象是一个简单的文本列表,您可以在其中选择任何项目。 由于我们需要列表的行能够与鼠标交互,而文本标签对象(CLabel 库的类)原本不具备这样的功能,因此用按钮对象的类来显示列表是合乎逻辑的。 当鼠标悬停在它们之上时,它们可以做出反应,并可被选中(若按下按钮)。
为了按钮看起来像文本列表项,我们需要把它们的边界颜色与背景颜色匹配。 在这种情况下,按钮与背景融合,只有上面的文本可见。 若光标悬停在按钮区域(在文本上可视)时,文本的背景颜色会发生变化。 若单击文本(按钮)时,则它将被选中(按下按钮)。
为了令由按钮创建的列表的行为类似于 MS Visual Studio 中的 ListBox,我们需要将列表的所有按钮包含在一个群组之中(为它们设置群组标志),并启用它们的切换(具有两种状态能力 — 开/关)。 每个按钮(列表行)都有一个与面板的群组编号相对应的群组索引,而为每个列表按钮设置的群组标志则不允许取消已选择的列表项。
在 \MQL5\Include\DoEasy\Objects\Graph\WForms\Common Controls\ 函数库文件夹里,创建一个 CListBox 类的新文件 ListBox.mqh。
该类应派生自列表对象的基类,其文件应包含在创建的类文件当中:
//+------------------------------------------------------------------+ //| ListBox.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 "ElementsListBox.mqh" //+------------------------------------------------------------------+ //| Class of the base object of the WForms control list | //+------------------------------------------------------------------+ class CListBox : public CElementsListBox { }
在类的私密部分,我们将声明一个创建新图形对象的虚拟方法;而在公开部分,声明创建列表和参数化构造函数的方法:
//+------------------------------------------------------------------+ //| Class of the base object of the WForms control list | //+------------------------------------------------------------------+ class CListBox : public CElementsListBox { 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 a list from the specified number of rows (Label objects) void CreateList(const int line_count); //--- Constructor CListBox(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 | //+------------------------------------------------------------------+ CListBox::CListBox(const long chart_id, const int subwindow, const string name, const int x, const int y, const int w, const int h) : CElementsListBox(chart_id,subwindow,name,x,y,w,h) { CGBaseObj::SetTypeElement(GRAPH_ELEMENT_TYPE_WF_LIST_BOX); CGCnvElement::SetProperty(CANV_ELEMENT_PROP_TYPE,GRAPH_ELEMENT_TYPE_WF_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); this.SetMultiColumn(false); this.SetColumnWidth(0); } //+------------------------------------------------------------------+
该方法按指定的行数创建列表:
//+--------------------------------------------------------------------+ //| Create the list from the specified number of rows (Button objects) | //+--------------------------------------------------------------------+ void CListBox::CreateList(const int count) { //--- Create the pointer to the Button object CButton *obj=NULL; //--- Create the specified number of Button objects CElementsListBox::CreateElements(GRAPH_ELEMENT_TYPE_WF_BUTTON,count,2,2,this.Width()-4,12,0,false); //--- In the loop by the created number of objects for(int i=0;i<this.ElementsTotal();i++) { //--- Get the created object from the list by the loop index obj=this.GetElement(i); //--- If the object could not be obtained, send the appropriate message to the log and move on to the next one if(obj==NULL) { ::Print(DFUN,MSG_ELM_LIST_ERR_FAILED_GET_GRAPH_ELEMENT_OBJ,this.TypeElementDescription(GRAPH_ELEMENT_TYPE_WF_BUTTON)); continue; } //--- Set left center text alignment obj.SetTextAlign(ANCHOR_LEFT); //--- Set the object text obj.SetFontSize(8); obj.SetText("ListBoxItem"+string(i+1)); //--- Set the frame colors equal to the background colors and the flag of the toggle button obj.SetBorderColor(obj.BackgroundColor(),true); obj.SetBorderColorMouseDown(obj.BackgroundColorMouseDown()); obj.SetBorderColorMouseOver(obj.BackgroundColorMouseOver()); obj.SetToggleFlag(true); obj.SetGroupButtonFlag(true); } } //+------------------------------------------------------------------+
该方法与上面修订后的 CheckedListBox 方法雷同。 它将成行创建按钮对象,并为它们设置边框颜色,以便与背景色融合。 为每个创建的按钮设置切换按钮标志,和以群组方式操作的按钮标志。 每个按钮的群组索引继承自放置它的面板。
创建新图形对象的虚拟方法:
//+------------------------------------------------------------------+ //| Create a new graphical object | //+------------------------------------------------------------------+ CGCnvElement *CListBox::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 Button object CGCnvElement *element=new CButton(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; } //+------------------------------------------------------------------+
按钮对象是在方法中创建的,并为其设置了最小参数 — 重定位标志和对象的相对坐标。
在此阶段,这就是该类所需的全部工作。 稍后,我将扩展列表对象类,提供所需的获取所选对象和发送消息的功能。
现在我们来创建一个按钮对象的列表对象类。 这样的对象将把在面板上创建的按钮组合到一起。 按钮可以分配到不同的群组,可以为它们设置群组按钮标志,来配合它们属性的其它参数。 如此这般,就可以在一个对象(在一个面板上)中创建不同的按钮群组。
在 \MQL5\Include\DoEasy\Objects\Graph\WForms\Common Controls\ 函数库文件夹里,创建一个CButtonListBox 类的新文件 ButtonListBox.mqh。
该类应继承自基准列表对象类,其文件也应包含在创建的类文件之中:
//+------------------------------------------------------------------+ //| ButtonListBox.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 "ElementsListBox.mqh" //+------------------------------------------------------------------+ //| ButtonListBox object class of WForms controls | //+------------------------------------------------------------------+ class CButtonListBox : public CElementsListBox { }
在类的私密部分中,声明创建新图形对象的方法。 在公开部分中,声明依据指定数量创建按钮的方法、参数化构造函数、以及处理创建在面板上的按钮的方法:
//+------------------------------------------------------------------+ //| ButtonListBox object class of WForms controls | //+------------------------------------------------------------------+ class CButtonListBox : public CElementsListBox { 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 CreateButton(const int count,const int width,const int height,const int new_column_width=0); //--- Constructor CButtonListBox(const long chart_id, const int subwindow, const string name, const int x, const int y, const int w, const int h); //--- (1) Set and (2) return the group of the button specified by index void SetButtonGroup(const int index,const int group); int ButtonGroup(const int index); //--- (1) Set and (2) return the flag of the group button specified by the button index void SetButtonGroupFlag(const int index,const bool flag); bool ButtonGroupFlag(const int index); //--- Sets the specified button "multiselect" mode void SetMultiSelect(const bool flag); //--- (1) Set and (2) return the "Toggle button" flag of the button specified by index void SetButtonToggle(const int index,const bool flag); bool ButtonToggle(const int index); //--- Set the "Toggle button" flag for all buttons of the object void SetToggle(const bool flag); }; //+------------------------------------------------------------------+
我们来仔细查看所声明的方法。
在参数化构造函数中,指定图形元素类型、函数库对象类型,并设置面板边框、其颜色和样式的默认值,以及面板上对象文本的颜色:
//+------------------------------------------------------------------+ //| Constructor indicating the chart and subwindow ID | //+------------------------------------------------------------------+ CButtonListBox::CButtonListBox(const long chart_id, const int subwindow, const string name, const int x, const int y, const int w, const int h) : CElementsListBox(chart_id,subwindow,name,x,y,w,h) { CGBaseObj::SetTypeElement(GRAPH_ELEMENT_TYPE_WF_BUTTON_LIST_BOX); CGCnvElement::SetProperty(CANV_ELEMENT_PROP_TYPE,GRAPH_ELEMENT_TYPE_WF_BUTTON_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); } //+------------------------------------------------------------------+
依据指定数量创建 Button 对象的方法:
//+------------------------------------------------------------------+ //| Create the specified number of Button objects | //+------------------------------------------------------------------+ void CButtonListBox::CreateButton(const int count,const int width,const int height,const int new_column_width=0) { //--- Create the pointer to the Button object CButton *obj=NULL; //--- Create the specified number of Button objects CElementsListBox::CreateElements(GRAPH_ELEMENT_TYPE_WF_BUTTON,count,2,2,width,height,new_column_width); //--- In the loop by the created number of objects for(int i=0;i<this.ElementsTotal();i++) { //--- Get the created object from the list by the loop index obj=this.GetElement(i); //--- If the object could not be obtained, send the appropriate message to the log and move on to the next one if(obj==NULL) { ::Print(DFUN,MSG_ELM_LIST_ERR_FAILED_GET_GRAPH_ELEMENT_OBJ,this.TypeElementDescription(GRAPH_ELEMENT_TYPE_WF_BUTTON)); continue; } //--- Set left center text alignment obj.SetTextAlign(ANCHOR_CENTER); //--- Set the object text obj.SetText("Button"+string(i+1)); } } //+------------------------------------------------------------------+
该方法与上述列表对象的类似方法雷同。 首先,创建指定数量的按钮对象。 然后,依据创建的对象数在循环中逐一为它们设置默认值。
创建新图形对象的虚拟方法:
//+------------------------------------------------------------------+ //| Create a new graphical object | //+------------------------------------------------------------------+ CGCnvElement *CButtonListBox::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 CButton object CGCnvElement *element=new CButton(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; } //+------------------------------------------------------------------+
此处,一切都与列表对象类的类似方法完全相同。
该方法按索引为指定的按钮设置群组:
//+------------------------------------------------------------------+ //| Set the group of the button specified by index | //+------------------------------------------------------------------+ void CButtonListBox::SetButtonGroup(const int index,const int group) { CButton *butt=this.GetElement(index); if(butt==NULL) { ::Print(DFUN,CMessage::Text(MSG_BUTT_LIST_ERR_FAILED_SET_GROUP_BUTTON),(string)index); return; } butt.SetGroup(group); } //+------------------------------------------------------------------+
按指定索引从列表中获取对象。 如果获取对象失败,则在日志中发出通知,并退出该方法。
为所获对象设置传递而来的群组索引的方法。
该方法返回指定索引的按钮群组:
//+------------------------------------------------------------------+ //| Return the group of the button specified by index | //+------------------------------------------------------------------+ int CButtonListBox::ButtonGroup(const int index) { CButton *butt=this.GetElement(index); return(butt!=NULL ? butt.Group() : WRONG_VALUE); } //+------------------------------------------------------------------+
按索引从列表中获取对象。 如果得到对象,则获取其群组,否则返回 -1。
该方法按指定索引设置群组按钮标志:
//+------------------------------------------------------------------+ //| Set the flag of the group button specified by the button index | //+------------------------------------------------------------------+ void CButtonListBox::SetButtonGroupFlag(const int index,const bool flag) { CButton *butt=this.GetElement(index); if(butt==NULL) { ::Print(DFUN,CMessage::Text(MSG_BUTT_LIST_ERR_FAILED_SET_GROUP_BUTTON),(string)index); return; } butt.SetGroupButtonFlag(flag); } //+------------------------------------------------------------------+
按指定索引从列表中获取对象。 如果获取对象失败,则在日志中发出通知,并离开该方法。
把传递给方法的标志设置到获取的对象。
该方法返回由按钮索引指定的群组按钮的标志:
//+------------------------------------------------------------------+ //| Return the flag of the group button specified by the button index| //+------------------------------------------------------------------+ bool CButtonListBox::ButtonGroupFlag(const int index) { CButton *butt=this.GetElement(index); return(butt!=NULL ? butt.GroupButtonFlag() : false); } //+------------------------------------------------------------------+
按指定索引从列表中获取对象。 如果得到对象,则返回群组按钮标志,否则返回 false。
该方法设置按钮的“多选”模式:
//+------------------------------------------------------------------+ //| Set the "multiselect" mode of the buttons | //+------------------------------------------------------------------+ void CButtonListBox::SetMultiSelect(const bool flag) { int group=this.Group()+(flag ? 1 : 0); for(int i=0;i<this.ElementsTotal();i++) this.SetButtonGroup(i,group+(flag ? i : 0)); } //+------------------------------------------------------------------+
该方法允许放置在面板上的按钮能够彼此独立工作,如此每个按钮都可以独立于面板上的其它按钮按下/释放。 为此,每个按钮都应有自己的群组。
首先,如果我们想允许多选按钮,则将第一个按钮群组的初始值设置为面板群组加 1,而按钮若应相互依赖,则将设置为 0。 接下来,在循环中遍历所有创建的按钮 ,即可为每个后续按钮设置一个新群组,计算面板群组编号加上循环索引,这意味着每个按钮都有一个等于列表中按钮位置 + 1 的群组;或是面板群组编号加零,这意味着所有按钮都有一个等于面板群组的群组。该方法为指定索引的按钮设置“切换按钮”标志:
//+------------------------------------------------------------------+ //| Set the "Toggle button" flag | //| button specified by index | //+------------------------------------------------------------------+ void CButtonListBox::SetButtonToggle(const int index,const bool flag) { CButton *butt=this.GetElement(index); if(butt==NULL) { ::Print(DFUN,CMessage::Text(MSG_BUTT_LIST_ERR_FAILED_SET_TOGGLE_BUTTON),(string)index); return; } butt.SetToggleFlag(flag); } //+------------------------------------------------------------------+
按指定索引从列表中获取按钮。 如果获取按钮失败,则在日志中发出通知,并退出该方法。
把传递到方法的标志设置给获取的按钮。
该方法返回指定索引处按钮的“切换按钮”标志:
//+------------------------------------------------------------------+ //| Return the "Toggle button" flag | //| button specified by index | //+------------------------------------------------------------------+ bool CButtonListBox::ButtonToggle(const int index) { CButton *butt=this.GetElement(index); return(butt!=NULL ? butt.Toggle() : false); } //+------------------------------------------------------------------+
按指定索引从列表中获取按钮。 如果收到按钮,则返回其切换按钮标志,否则返回 false。
该方法为所有的按钮对象设置“切换按钮”标志:
//+------------------------------------------------------------------+ //| Set the "Toggle button" flag to all buttons of the object | //+------------------------------------------------------------------+ void CButtonListBox::SetToggle(const bool flag) { for(int i=0;i<this.ElementsTotal();i++) this.SetButtonToggle(i,flag); } //+------------------------------------------------------------------+
在循环中遍历所有列表对象,调用上面研究过的 SetButtonToggle() 方法为每个下一个按钮设置指定的标志。
我已经为本文创建了规划中的所有对象。
现在我们需要确保函数库“知道”它们,如此这般,我们才可以从我们的程序里创建它们。
我们在 \MQL5\Include\DoEasy\Objects\Graph\WForms\Containers\Container.mqh 基准容器对象中进行一些小的改进。 稍后,我们需要将之前创建的对象添加到容器中,而非在其中创建新对象。 为了简化该操作,我们需要将创建新附加对象的方法拆分为两个 — 其一创建一个新对象,另一个则为新对象设置一些默认属性。 当添加一个对象时,我们不会创建一个新对象,而是将指定的对象添加到列表之中,并在必要时修改其参数。
在类的受保护部分中,声明新方法,为所创建的对象设置参数:
protected: //--- Adjust the element size to fit its content bool AutoSizeProcess(const bool redraw); //--- Set parameters for the attached object void SetObjParams(CWinFormBase *obj,const color colour); public:
我们在类主体之外编写其实现:
//+------------------------------------------------------------------+ //| Set parameters for the attached object | //+------------------------------------------------------------------+ void CContainer::SetObjParams(CWinFormBase *obj,const color colour) { //--- Set the text color of the 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 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 "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 "ListBox", "CheckedListBox" and "ButtonListBox" WinForms object case GRAPH_ELEMENT_TYPE_WF_LIST_BOX : case GRAPH_ELEMENT_TYPE_WF_CHECKED_LIST_BOX : case GRAPH_ELEMENT_TYPE_WF_BUTTON_LIST_BOX : //--- set the object text color as a container text color depending on the one passed to the method: //--- set the background color depending on the one passed to the method: //--- either the default standard control background color, or the one passed to the method. //--- The frame color is set equal to the text color obj.SetBackgroundColor(colour==clrNONE ? CLR_DEF_CONTROL_STD_BACK_COLOR : colour,true); obj.SetBorderColor(CLR_DEF_BORDER_COLOR,true); obj.SetForeColor(CLR_DEF_FORE_COLOR,true); break; default: break; } } //+------------------------------------------------------------------+
在此,我们简单地从 CreateNewElement() 方法中移动了设置对象参数的代码模块。 将指针传递给所创建对象,并将传递给 CreateNewElement() 方法的颜色值,一并传递给该方法。 此处,我还添加了新对象的处理,这与之前创建的 CheckedListBox 对象处理方式类似。 因此,我不必编写任何额外的东西 — 我只是指出这些对象的处理方式类似于 CheckedListBox 对象的一个“开关”情况。
修订后的创建新绑定元素的方法:
//+------------------------------------------------------------------+ //| Create a new attached element | //+------------------------------------------------------------------+ bool CContainer::CreateNewElement(const ENUM_GRAPH_ELEMENT_TYPE element_type, const int x, const int y, const int w, const int h, const color colour, const uchar opacity, const bool activity, const bool redraw) { //--- If the object type is less than the base WinForms object if(element_type<GRAPH_ELEMENT_TYPE_WF_BASE) { //--- report the error and return 'false' CMessage::ToLog(DFUN,MSG_PANEL_OBJECT_ERR_OBJ_MUST_BE_WFBASE); return false; } //--- If failed to create a new graphical element, return 'false' CWinFormBase *obj=CForm::CreateAndAddNewElement(element_type,x,y,w,h,colour,opacity,activity); if(obj==NULL) return false; //--- Set parameters for the created object this.SetObjParams(obj,colour); //--- If 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; } //+------------------------------------------------------------------+
现在我们调用新方法,替代移到新方法的代码模块。 相应地,代码变得更简洁、更简单、更清晰,现在我们可以调用该一分为二的方法把已经创建的对象附加到列表当中。
改进 \MQL5\Include\DoEasy\Objects\Graph\WForms\Containers\Panel.mqh 中的面板对象类。
将今天创建的新对象文件添加到包含文件列表之中:
//+------------------------------------------------------------------+ //| Include files | //+------------------------------------------------------------------+ #include "Container.mqh" #include "GroupBox.mqh" #include "..\..\WForms\Common Controls\ListBox.mqh" #include "..\..\WForms\Common Controls\CheckedListBox.mqh" #include "..\..\WForms\Common Controls\ButtonListBox.mqh" //+------------------------------------------------------------------+
将创建新对象的代码模块添加到创建新图形对象的方法当中:
//+------------------------------------------------------------------+ //| 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_LIST_BOX : element=new CCheckedListBox(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; case GRAPH_ELEMENT_TYPE_WF_BUTTON_LIST_BOX : element=new CButtonListBox(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; } //+------------------------------------------------------------------+
在 \MQL5\Include\DoEasy\Objects\Graph\WForms\Containers\GroupBox.mqh 中的 GroupBox 容器对象类里,添加了与有关创建新对象的相同修订。 我不会在此详述它们。 您可以在文后附加的文件中查看所有更改。
将 ButtonListBox 对象类文件包含到图形元素集合类的 \MQL5\Include\DoEasy\Collections\GraphElementsCollection.mqh 里:
//+------------------------------------------------------------------+ //| 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\WForms\Common Controls\ButtonListBox.mqh" #include "..\Objects\Graph\Standard\GStdVLineObj.mqh"
之后,在本文中创建的所有其它对象,均可在基于函数库创建的程序中可见。
这些都是今天规划的对象和改进。
测试
为了执行测试,我们取用来自上一篇文章中的 EA,并将其保存在 \MQL5\Experts\TestDoEasy\Part112\,命名为 TstDE112.mq5。
在第二群组对象 GroupBox2 中,创建新的 ButtonListBox 和 ListBox 列表对象。 容器中的最后一个对象位置坐标将取决于 CheckedListBox 和 ButtonListBox 对象的外观。 如果它们启用了可在若干列中构建列表的标志,则 ListBox 对象将位于下方,否则位于前两列的右侧。
还要检查群组按钮的操作 — 它们在不同群组中操作的可能性,以及再次按下按钮时释放按钮的能力。
在 EA 输入中,加入两个新的参数:
//--- input parameters sinput bool InpMovable = true; // Panel Movable flag sinput ENUM_INPUT_YES_NO InpAutoSize = INPUT_YES; // Panel Autosize sinput ENUM_AUTO_SIZE_MODE InpAutoSizeMode = AUTO_SIZE_MODE_GROW; // Panel Autosize mode sinput ENUM_BORDER_STYLE InpFrameStyle = BORDER_STYLE_SIMPLE; // Label border style sinput ENUM_ANCHOR_POINT InpTextAlign = ANCHOR_CENTER; // Label text align sinput ENUM_INPUT_YES_NO InpTextAutoSize = INPUT_NO; // Label autosize sinput ENUM_ANCHOR_POINT InpCheckAlign = ANCHOR_LEFT; // Check flag align sinput ENUM_ANCHOR_POINT InpCheckTextAlign = ANCHOR_LEFT; // Check label text align sinput ENUM_CHEK_STATE InpCheckState = CHEK_STATE_UNCHECKED; // Check flag state sinput ENUM_INPUT_YES_NO InpCheckAutoSize = INPUT_YES; // CheckBox autosize sinput ENUM_BORDER_STYLE InpCheckFrameStyle = BORDER_STYLE_NONE; // CheckBox border style sinput ENUM_ANCHOR_POINT InpButtonTextAlign = ANCHOR_CENTER; // Button text align sinput ENUM_INPUT_YES_NO InpButtonAutoSize = INPUT_YES; // Button autosize sinput ENUM_AUTO_SIZE_MODE InpButtonAutoSizeMode= AUTO_SIZE_MODE_GROW; // Button Autosize mode sinput ENUM_BORDER_STYLE InpButtonFrameStyle = BORDER_STYLE_NONE; // Button border style sinput bool InpButtonToggle = false; // Button toggle flag sinput bool InpListBoxMColumn = false; // ListBox MultiColumn flag sinput bool InpButtListMSelect = false; // ButtonListBox Button MultiSelect flag //--- global variables CEngine engine; color array_clr[]; //+------------------------------------------------------------------+
第一个参数指示可创建由若干列组成的列表(如果所有对象都不适合面板的高度),第二个参数设置群组中按钮可多选的可能性。
将创建新对象的代码模块添加到 EA 的 OnInit() 处理程序当中(仅显示部分代码):
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,160,20,clrNONE,255,true,false); //--- get the pointer to the CheckedListBox object by its index in the list of bound objects of the CheckBox type CCheckedListBox *clbox=gbox2.GetElementByType(GRAPH_ELEMENT_TYPE_WF_CHECKED_LIST_BOX,0); //--- If CheckedListBox is created and the pointer to it is received if(clbox!=NULL) { clbox.SetMultiColumn(InpListBoxMColumn); clbox.SetColumnWidth(0); clbox.CreateCheckBox(4,66); } //--- Create the ButtonListBox object gbox2.CreateNewElement(GRAPH_ELEMENT_TYPE_WF_BUTTON_LIST_BOX,4,clbox.BottomEdgeRelative()+6,160,30,clrNONE,255,true,false); //--- get the pointer to the ButtonListBox object by its index in the list of attached objects of the Button type CButtonListBox *blbox=gbox2.GetElementByType(GRAPH_ELEMENT_TYPE_WF_BUTTON_LIST_BOX,0); //--- If ButtonListBox is created and the pointer to it is received if(blbox!=NULL) { blbox.SetMultiColumn(InpListBoxMColumn); blbox.SetColumnWidth(0); blbox.CreateButton(4,66,16); blbox.SetMultiSelect(InpButtListMSelect); blbox.SetToggle(InpButtonToggle); for(int i=0;i<blbox.ElementsTotal();i++) { blbox.SetButtonGroup(i,(i % 2==0 ? blbox.Group()+1 : blbox.Group()+2)); blbox.SetButtonGroupFlag(i,(i % 2==0 ? true : false)); } } //--- Create the ListBox object int lbx=4; int lby=blbox.BottomEdgeRelative()+6; int lbw=146; if(!InpListBoxMColumn) { lbx=blbox.RightEdgeRelative()+6; lby=14; lbw=100; } gbox2.CreateNewElement(GRAPH_ELEMENT_TYPE_WF_LIST_BOX,lbx,lby,lbw,70,clrNONE,255,true,false); //--- get the pointer to the ListBox object by its index in the list of attached objects of Button type CListBox *lbox=gbox2.GetElementByType(GRAPH_ELEMENT_TYPE_WF_LIST_BOX,0); //--- If ListBox has been created and the pointer to it has been received if(lbox!=NULL) { lbox.CreateList(4); } } } //--- Redraw all objects according to their hierarchy pnl.Redraw(true); } //--- return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+
在此,我们在 GroupBox2 对象中创建了两个新的列表对象。 如果它们被创建成功,那么我们在每个对象中创建四个对象。
EA 的 OnInit() 处理程序的完整代码可在文后附加的文件中找到。
编译 EA,并在图表上启动它:
在此,我们可看到顶部两个 ButtonListBox 按钮的操作方式与底部两个略有不同。 这取决于设置的标志。 在第一种情况下,无法禁用按钮再次被按下。 我们能通过按第二个按钮来禁用另一个按钮。 在第二种情况下,可以通过单击第二个按钮,以及再次按下已启用的按钮来禁用该按钮。 这会受到群组按钮标志的影响。 如果已设置,因为它们在群组中操作,则按钮会完全相互依赖。
列表对象工作正常。 但是外观还有很多不足之处。 在 MS Visual Studio 中,列表更加紧凑,对象彼此更靠近。 但在此处,我们仍然无法做到这样,因为如果您将对象彼此靠近放置,那么在与鼠标交互时,对象背景颜色的变化并不总能正常工作。 一旦我们找到并修复了这个问题,我们就能调整所创建对象的外观。
下一步是什么?
在下一篇文章中,我将继续研究基于该函数库的 GUI 程序的图形元素。
*该系列的前几篇文章:
DoEasy. 控件 (第 1 部分): 第一步
DoEasy. 控件 (第 2 部分): 操控 CPanel 类
DoEasy. 控件 (第 3 部分): 创建绑定控件
DoEasy. 控件 (第 4 部分): 面板控件,Padding(填充)和 Dock(驻靠)参数
DoEasy. 控件 (第 5 部分): 基准 WinForms 对象,面板控件,AutoSize 参数
DoEasy. 控件 (第 6 部分): 面板控件,自动调整容器大小来适应内部内容
DoEasy. 控件 (第 7 部分): 文本标签控件
DoEasy. 控件 (第 8 部分): 基准 WinForms 对象类别,GroupBox 和 CheckBox 控件
DoEasy. 控件 (第 9 部分): 重新编排 WinForms 对象方法、RadioButton 和 Button 控件
DoEasy. 控件 (第 10 部分): WinForms 对象 — 动画界面
DoEasy. 控件 (第 11 部分): WinForms 对象 — 群组,CheckedListBox WinForms 对象
本文由MetaQuotes Ltd译自俄文
原文地址: https://www.mql5.com/ru/articles/11228