English Русский Español Deutsch 日本語 Português
preview
DoEasy. 控件 (第 13 部分): 优化 WinForms 对象与鼠标的交互,启动开发 TabControl WinForms 对象

DoEasy. 控件 (第 13 部分): 优化 WinForms 对象与鼠标的交互,启动开发 TabControl WinForms 对象

MetaTrader 5示例 | 11 十一月 2022, 13:43
648 0
Artyom Trishkin
Artyom Trishkin

内容


概述

用于创建 GUI 控件的函数库图形对象目前有一个缺点:将鼠标悬停在某些对象上时,它们会改变其外观,但当您将光标移离对象区域时,它并不总能将其恢复到原始状态。 如果两个对象彼此靠得很近,就会发生这种情况。 即使光标远离对象的方向也会对此产生影响。 如果移动是从下到上,或从左到右,则对象能正确响应光标。 在相对方向的情况下,当光标移离对象区域时,图形对象不会恢复到其原始状态。 不过,一旦我们将物体放置在彼此稍远的距离,它们的行为就开始正常。

此问题阻碍我们创建某些对象。 我们在放置它们的组件时,其间距必须与 MS Visual Studio 控件集当中类似对象相当。 通过分析这种行为,我得出的结论是,为了令对象将其外观改回原始外观,它应该首先进入图形对象所在的窗体区域。 这会迫使它们彼此间隔一定距离(至少 4 像素)。

我将在本文中修复此问题。 此外,我还将优化图形对象与鼠标交互时的颜色切换。 此外,我将着手开发 TabControl 图形对象,这是一组选项卡,您可以在其上放置各种 WinForms 对象。 不幸的是,在开发此控件的过程中,我在规划函数库结构时碰到了一个同样严重的错误,即创建图形对象名称的逻辑。 目前,我们的图形对象名称由程序名称,和面板的名称,以及后缀 “_Elm00”、“_Elm01”、“_Elm02” 组成。

例如,如果程序称为 “Program”,且在图表上创建了三个空面板对象,则这三个面板的名称分别是 “Program_WFPanel1”、“Program_WFPanel2” 和 “Program_WFPanel3”。 在此情况下,面板对象的名称由函数库的用户给出,该用户依据其程序所创建面板来命名。 在我们的例子中,这些是 WFPanel1,WFPanel2 和 WFPanel3。 如果我们将另一个对象附着到第一个面板,函数库将自动为其创建一个名称,它将如下所示:Program_WFPanel1_Elm00。

如果您在某个附着元素中再创建附着元素,则名称将变得更长:Program_WFPanel1_Elm00_Elm00。 如果我们在第一个附着元素上创建另一个元素,那么它的名称将是 WFPanel1_Elm00_Elm01。 创建一个新的之后,名称将为 WFPanel1_Elm00_Elm02,依此类推。 相应地,如果我们在每个附着对象上再创建新的附着对象,则名称将变得更长,因为所有附加对象的整个层次结构都被编写在其内。 图形资源的名称不应超过 63 个字符。 在 CCanvas 标准库类中,创建一个图形资源,其名称是用户指定的(函数库生成的),加上自系统启动以来的毫秒数,再加上一个介于 0 到 32767 之间的伪随机整数(在 Create 方法中):

m_rcname="::"+name+(string)ChartID()+(string)(GetTickCount()+MathRand());

如此,由于程序已指定了由函数库生成的对象名称,那么剩余的可用字符空间就非常有限了。

因此,先前采用的构造函数库图形元素名称的概念变得不可行。 我在开发 TabControl 图形元素时就碰到了这个问题。 在其选项卡上,已不再可能附着任何其它图形元素。 若是图形资源的名称变得太长,CCanvas 类的 Create() 方法就会返回 false

在本文中,我将准备创建 TabControl WinForm 对象的必要基础,先创建空白类,待日后完善,故不会在此处进一步开发它们。 取而代之,我将从“即兴工具”创建此控件的布局,我会用该布局来检查此对象未来所需的功能和外观。 在下一篇文章中,我将实现一种新机制,为函数库图形元素命名,并采用此处准备就绪的类,以及从开发布局中获得的经验,着手制作成熟的 TabControl WinForms 对象。


改进库类

将鼠标指针悬停在 TabPage 对象上方,或单击选项卡标题时,对象元素的行为应符合某种方式 — 有些元素应更改其颜色,而选项卡的标题大小应略有增加,表明它已被选中。 对象元素的边框接收自己的颜色,对应于选择对象,或光标悬停在对象上方时对象的状态。 对于所有这些可能的状态,我们定义了宏替换,这些宏替换将存储对象组件不同模式的默认颜色值。

在 \MQL5\Include\DoEasy\Defines.mqh 中,设置以下宏替换:

#define CLR_DEF_CONTROL_STD_BACK_COLOR_ON (C'0xC9,0xDE,0xD0')     // Background color of standard controls which are on
#define CLR_DEF_CONTROL_STD_BACK_DOWN_ON (C'0xA6,0xC8,0xB0')      // Color of standard control background when clicking on the control when it is on
#define CLR_DEF_CONTROL_STD_BACK_OVER_ON (C'0xB8,0xD3,0xC0')      // Color of standard control background when hovering the mouse over the control when it is on

#define CLR_DEF_CONTROL_TAB_BACK_COLOR (CLR_CANV_NULL)            // TabControl background color
#define CLR_DEF_CONTROL_TAB_MOUSE_DOWN (CLR_CANV_NULL)            // Color of TabControl background when clicking on the control
#define CLR_DEF_CONTROL_TAB_MOUSE_OVER (CLR_CANV_NULL)            // Color of TabControl background when hovering the mouse over the control
#define CLR_DEF_CONTROL_TAB_OPACITY    (0)                        // TabControl background opacity

#define CLR_DEF_CONTROL_TAB_BACK_COLOR_ON (CLR_CANV_NULL)         // Enabled TabControl background color
#define CLR_DEF_CONTROL_TAB_BACK_DOWN_ON (CLR_CANV_NULL)          // Color of enabled TabControl background when clicking on the control
#define CLR_DEF_CONTROL_TAB_BACK_OVER_ON (CLR_CANV_NULL)          // Color of enabled TabControl background when hovering the mouse over the control

#define CLR_DEF_CONTROL_TAB_BORDER_COLOR (CLR_CANV_NULL)          // TabControl frame color
#define CLR_DEF_CONTROL_TAB_BORDER_MOUSE_DOWN (CLR_CANV_NULL)     // Color of TabControl frame when clicking on the control
#define CLR_DEF_CONTROL_TAB_BORDER_MOUSE_OVER (CLR_CANV_NULL)     // Color of TabControl frame when hovering the mouse over the control

#define CLR_DEF_CONTROL_TAB_BORDER_COLOR_ON (CLR_CANV_NULL)       // Enabled TabControl frame color
#define CLR_DEF_CONTROL_TAB_BORDER_DOWN_ON (CLR_CANV_NULL)        // Color of enabled TabControl frame when clicking on the control
#define CLR_DEF_CONTROL_TAB_BORDER_OVER_ON (CLR_CANV_NULL)        // Color of enabled TabControl frame when hovering the mouse over the control

#define CLR_DEF_CONTROL_TAB_PAGE_BACK_COLOR (C'0xFF,0xFF,0xFF')   // TabPage control background color
#define CLR_DEF_CONTROL_TAB_PAGE_MOUSE_DOWN (C'0xFF,0xFF,0xFF')   // Color of TabPage control background when clicking on the control
#define CLR_DEF_CONTROL_TAB_PAGE_MOUSE_OVER (C'0xFF,0xFF,0xFF')   // Color of TabPage control background when hovering the mouse over the control
#define CLR_DEF_CONTROL_TAB_PAGE_OPACITY    (255)                 // TabPage background opacity

#define CLR_DEF_CONTROL_TAB_PAGE_BACK_COLOR_ON (C'0xFF,0xFF,0xFF')// Color of the enabled TabPage control background
#define CLR_DEF_CONTROL_TAB_PAGE_BACK_DOWN_ON (C'0xFF,0xFF,0xFF') // Color of the enabled TabPage control background when clicking on the control
#define CLR_DEF_CONTROL_TAB_PAGE_BACK_OVER_ON (C'0xFF,0xFF,0xFF') // Color of the enabled TabPage control background when hovering the mouse over the control

#define CLR_DEF_CONTROL_TAB_PAGE_BORDER_COLOR (C'0xDD,0xDD,0xDD') // TabPage control frame color
#define CLR_DEF_CONTROL_TAB_PAGE_BORDER_MOUSE_DOWN (C'0xDD,0xDD,0xDD') // Color of TabPage control background frame when clicking on the control
#define CLR_DEF_CONTROL_TAB_PAGE_BORDER_MOUSE_OVER (C'0xDD,0xDD,0xDD') // Color of TabPage control background frame when hovering the mouse over the control

#define CLR_DEF_CONTROL_TAB_PAGE_BORDER_COLOR_ON (C'0xDD,0xDD,0xDD')// Color of the enabled TabPage control frame
#define CLR_DEF_CONTROL_TAB_PAGE_BORDER_DOWN_ON (C'0xDD,0xDD,0xDD') // Color of the enabled TabPage control frame when clicking on the control
#define CLR_DEF_CONTROL_TAB_PAGE_BORDER_OVER_ON (C'0xDD,0xDD,0xDD') // Color of the enabled TabPage control frame when hovering the mouse over the control

#define CLR_DEF_CONTROL_TAB_HEAD_BACK_COLOR (C'0xF0,0xF0,0xF0')   // TabPage control header background color
#define CLR_DEF_CONTROL_TAB_HEAD_MOUSE_DOWN (C'0xF0,0xF0,0xF0')   // Color of TabPage control header background when clicking on the control
#define CLR_DEF_CONTROL_TAB_HEAD_MOUSE_OVER (C'0xF0,0xF0,0xF0')   // Color of TabPage control header background when hovering the mouse over the control
#define CLR_DEF_CONTROL_TAB_HEAD_OPACITY    (255)                 // TabPage header background opacity

#define CLR_DEF_CONTROL_TAB_HEAD_BACK_COLOR_ON (C'0xFF,0xFF,0xFF')// Color of the enabled TabPage control header background
#define CLR_DEF_CONTROL_TAB_HEAD_BACK_DOWN_ON (C'0xFF,0xFF,0xFF') // Color of the enabled TabPage control header background when clicking on the control
#define CLR_DEF_CONTROL_TAB_HEAD_BACK_OVER_ON (C'0xFF,0xFF,0xFF') // Color of the enabled TabPage control header background when clicking on the control

#define CLR_DEF_CONTROL_TAB_HEAD_BORDER_COLOR (C'0xD9,0xD9,0xD9')   // TabPage control header frame color
#define CLR_DEF_CONTROL_TAB_HEAD_BORDER_MOUSE_DOWN (C'0xD9,0xD9,0xD9') // Color of TabPage control header frame when clicking on the control
#define CLR_DEF_CONTROL_TAB_HEAD_BORDER_MOUSE_OVER (C'0xD9,0xD9,0xD9') // Color of TabPage control header frame when hovering the mouse over the control

#define CLR_DEF_CONTROL_TAB_HEAD_BORDER_COLOR_ON (C'0xDD,0xDD,0xDD')// Color of the enabled TabPage control header frame
#define CLR_DEF_CONTROL_TAB_HEAD_BORDER_DOWN_ON (C'0xDD,0xDD,0xDD') // Color of the enabled TabPage control header frame when clicking on the control
#define CLR_DEF_CONTROL_TAB_HEAD_BORDER_OVER_ON (C'0xDD,0xDD,0xDD') // Color of the enabled TabPage control header frame when hovering the mouse over the control

#define DEF_CONTROL_LIST_MARGIN_X      (1)                        // Gap between columns in ListBox controls
#define DEF_CONTROL_LIST_MARGIN_Y      (0)                        // Gap between rows in ListBox controls

#define DEF_FONT                       ("Calibri")                // Default font
#define DEF_FONT_SIZE                  (8)                        // Default font size
#define DEF_CHECK_SIZE                 (12)                       // Verification flag default size
#define OUTER_AREA_SIZE                (16)                       // Size of one side of the outer area around the form workspace
#define DEF_FRAME_WIDTH_SIZE           (3)                        // Default form/panel/window frame width
//--- Graphical object parameters

在此,我们有两个宏替换定义了 ListBox 控件的行间距。 以前,我们被迫采用 4 像素高和 6 像素宽。 现在,在修复了与鼠标交互时元素外观变更的错误之后,我们可以指定行间高度和行间宽度的最小间距。


在图形元素类型的枚举中添加三个新类型:

//+------------------------------------------------------------------+
//| 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
   GRAPH_ELEMENT_TYPE_WF_TAB_HEADER,                  // Windows Forms TabHeader
   GRAPH_ELEMENT_TYPE_WF_TAB_PAGE,                    // Windows Forms TabPage
   GRAPH_ELEMENT_TYPE_WF_TAB_CONTROL,                 // Windows Forms TabControl
  };
//+------------------------------------------------------------------+

我们需要依靠这些图形元素的新类型来指定 WinForms TabControl 对象本身或其组件的类型 — 选项卡标题(TabHeader)、或选项卡(TabPage)。 换句话说,对象将由一组选项卡组成,选项卡将包含一个面板,其中将放置附着的对象,以及面板的标题,单击该标题则相应的选项卡处于活动状态。

TabControl 图形元素的选项卡标题可以放置在四个位置:右、左、上、和下。
我们创建一个新枚举来指定其位置:

//+------------------------------------------------------------------+
//| Control flag status                                              |
//+------------------------------------------------------------------+
enum ENUM_CANV_ELEMENT_CHEK_STATE
  {
   CANV_ELEMENT_CHEK_STATE_UNCHECKED,                 // Unchecked
   CANV_ELEMENT_CHEK_STATE_CHECKED,                   // Checked
   CANV_ELEMENT_CHEK_STATE_INDETERMINATE,             // Undefined
  };
//+------------------------------------------------------------------+
//| Location of an object inside a control                           |
//+------------------------------------------------------------------+
enum ENUM_CANV_ELEMENT_ALIGNMENT
  {
   CANV_ELEMENT_ALIGNMENT_TOP,                        // Top
   CANV_ELEMENT_ALIGNMENT_BOTTOM,                     // Bottom
   CANV_ELEMENT_ALIGNMENT_LEFT,                       // Left
   CANV_ELEMENT_ALIGNMENT_RIGHT,                      // Right
  };
//+------------------------------------------------------------------+
//| Integer properties of the graphical element on the canvas        |
//+------------------------------------------------------------------+


在创建常量名称时,之前有一个小的不准确之处:我们的按钮可以是切换按钮,但按钮“按下/释放”的状态不是切换状态。 最好在枚举常量的名称中将按下的状态重命名为 StateOn。

因此,所有曾提及的按下状态的颜色枚举(以前称为 “COLOR_TOGGLE”)现在已重命名为 “COLOR_STATE_ON”。 枚举常量名称的所有更正已经在函数库中完成了,此处我们仅提一提它们以供参考。
以下是曾讨论过的,在画布上的图形元素的整数型属性枚举文件中的一些常量重命名的示例

   CANV_ELEMENT_PROP_FORE_COLOR_MOUSE_DOWN,           // Default control text color when clicking on the control
   CANV_ELEMENT_PROP_FORE_COLOR_MOUSE_OVER,           // Default control text color when hovering the mouse over the control
   CANV_ELEMENT_PROP_FORE_COLOR_STATE_ON,             // Text color of the control which is on
   CANV_ELEMENT_PROP_FORE_COLOR_STATE_ON_MOUSE_DOWN,  // Default control text color when clicking on the control which is on
   CANV_ELEMENT_PROP_FORE_COLOR_STATE_ON_MOUSE_OVER,  // Default control text color when hovering the mouse over the control which is on
   CANV_ELEMENT_PROP_BACKGROUND_COLOR,                // Control background color
   CANV_ELEMENT_PROP_BACKGROUND_COLOR_OPACITY,        // Opacity of control background color
   CANV_ELEMENT_PROP_BACKGROUND_COLOR_MOUSE_DOWN,     // Control background color when clicking on the control
   CANV_ELEMENT_PROP_BACKGROUND_COLOR_MOUSE_OVER,     // Control background color when hovering the mouse over the control
   CANV_ELEMENT_PROP_BACKGROUND_COLOR_STATE_ON,       // Background color of the control which is on
   CANV_ELEMENT_PROP_BACKGROUND_COLOR_STATE_ON_MOUSE_DOWN,// Control background color when clicking on the control which is on
   CANV_ELEMENT_PROP_BACKGROUND_COLOR_STATE_ON_MOUSE_OVER,// Control background color when hovering the mouse over control which is on
   CANV_ELEMENT_PROP_BOLD_TYPE,                       // Font width type
   CANV_ELEMENT_PROP_BORDER_STYLE,                    // Control frame style


在同一枚举文件的末尾,添加三个新的整数型属性,并将整数型对象属性的数量从 85 增加到 88

   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
   CANV_ELEMENT_PROP_TAB_MULTILINE,                   // Several lines of tabs in TabControl
   CANV_ELEMENT_PROP_TAB_ALIGNMENT,                   // Location of tabs inside the control
   CANV_ELEMENT_PROP_ALIGNMENT,                       // Location of an object inside the control
   
  };
#define CANV_ELEMENT_PROP_INTEGER_TOTAL (88)          // Total number of integer properties
#define CANV_ELEMENT_PROP_INTEGER_SKIP  (0)           // Number of integer properties not used in sorting
//+------------------------------------------------------------------+


在画布上图形元素可能的排序条件列表末尾添加三个新属性

   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_CANV_ELEMENT_TAB_MULTILINE,                // Sort by the flag of several rows of tabs in TabControl
   SORT_BY_CANV_ELEMENT_TAB_ALIGNMENT,                // Sort by the location of tabs inside the control
   SORT_BY_CANV_ELEMENT_ALIGNMENT,                    // Sort by the location of the object inside the control
//--- Sort by real properties

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

现在,我们就能够依据三个新属性来对图形元素进行排序和选择。


在 \MQL5\Include\DoEasy\Data.mqh 中,加入新的消息函数索引:

   MSG_LIB_TEXT_BUTTON_STATE_PRESSED,                 // Pressed
   MSG_LIB_TEXT_BUTTON_STATE_DEPRESSED,               // Released
   
   MSG_LIB_TEXT_TOP,                                  // Top
   MSG_LIB_TEXT_BOTTOM,                               // Bottom
   MSG_LIB_TEXT_LEFT,                                 // Left
   MSG_LIB_TEXT_RIGHT,                                // Right
   
   MSG_LIB_TEXT_CORNER_LEFT_UPPER,                    // Center of coordinates at the upper left corner of the chart
   MSG_LIB_TEXT_CORNER_LEFT_LOWER,                    // Center of coordinates at the lower left corner of the chart
   MSG_LIB_TEXT_CORNER_RIGHT_LOWER,                   // Center of coordinates at the lower right corner of the chart
   MSG_LIB_TEXT_CORNER_RIGHT_UPPER,                   // Center of coordinates at the upper right corner of the chart

...

   MSG_GRAPH_ELEMENT_TYPE_WF_CHECKED_LIST_BOX,        // CheckedListBox control
   MSG_GRAPH_ELEMENT_TYPE_WF_BUTTON_LIST_BOX,         // ButtonListBox control
   MSG_GRAPH_ELEMENT_TYPE_WF_TAB_HEADER,              // Tab header
   MSG_GRAPH_ELEMENT_TYPE_WF_TAB_PAGE,                // TabPage control
   MSG_GRAPH_ELEMENT_TYPE_WF_TAB_CONTROL,             // TabControl
   MSG_GRAPH_OBJ_BELONG_PROGRAM,                      // Graphical object belongs to a program
   MSG_GRAPH_OBJ_BELONG_NO_PROGRAM,                   // Graphical object does not belong to a program

..

   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
   MSG_CANV_ELEMENT_PROP_TAB_MULTILINE,               // Several lines of tabs in the control
   MSG_CANV_ELEMENT_PROP_TAB_ALIGNMENT,               // Location of tabs inside the control
   MSG_CANV_ELEMENT_PROP_ALIGNMENT,                   // Location of an object inside the control
//--- 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

  };
//+------------------------------------------------------------------+

与新增索引相对应的消息

   {"Нажата","Pressed"},
   {"Отжата","Depressed"},
   
   {"Сверху","Top"},
   {"Снизу","Bottom"},
   {"Слева","Left"},
   {"Справа","Right"},
   
   {"Центр координат в левом верхнем углу графика","Center of coordinates is in the upper left corner of the chart"},
   {"Центр координат в левом нижнем углу графика","Center of coordinates is in the lower left corner of the chart"},
   {"Центр координат в правом нижнем углу графика","Center of coordinates is in the lower right corner of the chart"},
   {"Центр координат в правом верхнем углу графика","Center of coordinates is in the upper right corner of the chart"},

...

   {"Элемент управления \"CheckedListBox\"","Control element \"CheckedListBox\""},
   {"Элемент управления \"ButtonListBox\"","Control element \"ButtonListBox\""},
   {"Заголовок вкладки","Tab header"},
   {"Элемент управления \"TabPage\"","Control element \"TabPage\""},
   {"Элемент управления \"TabControl\"","Control element \"TabControl\""},
   
   {"Графический объект принадлежит программе","The graphic object belongs to the program"},
   {"Графический объект не принадлежит программе","The graphic object does not belong to the program"},

..

   {"Горизонтальное отображение столбцов в элементе управления ListBox","Display columns horizontally in a ListBox control"},
   {"Ширина каждого столбца элемента управления ListBox","The width of each column of the ListBox control"},
   {"Несколько рядов вкладок в элементе управления","Multiple rows of tabs in a control"},
   {"Местоположение вкладок внутри элемента управления","Location of tabs inside the control"},
   {"Местоположение объекта внутри элемента управления","Location of the object inside the control"},
   
//--- String properties of graphical elements
   {"Имя объекта-графического элемента","The name of the graphic element object"},
   {"Имя графического ресурса","Image resource name"},
   {"Текст графического элемента","Text of the graphic element"},

  };
//+---------------------------------------------------------------------+


我添加了图形元素的新类型及其描述。 如此,现在我们能够访问方法中的文本消息索引,该方法返回 \MQL5\Include\DoEasy\Objects\Graph\GBaseObj.mqh 中基准图形对象类的图形元素类型说明:

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


TabControl 的选项卡标题可以位于图形对象的四个边侧上。 此外,稍后我们可能会加进其它对象,为此有必要指定它们在其容器中的位置。 故此,我们创建一个公开函数,返回 \MQL5\Include\DoEasy\Services\DELib.mqh 中图形元素位置边侧的说明:

//+------------------------------------------------------------------+
//| Return the description                                           |
//| of the object location inside the control                        |
//+------------------------------------------------------------------+
string AlignmentDescription(const ENUM_CANV_ELEMENT_ALIGNMENT alignment)
  {
   switch(alignment)
     {
      case CANV_ELEMENT_ALIGNMENT_TOP     :  return CMessage::Text(MSG_LIB_TEXT_TOP);     break;
      case CANV_ELEMENT_ALIGNMENT_BOTTOM  :  return CMessage::Text(MSG_LIB_TEXT_BOTTOM);  break;
      case CANV_ELEMENT_ALIGNMENT_LEFT    :  return CMessage::Text(MSG_LIB_TEXT_LEFT);    break;
      case CANV_ELEMENT_ALIGNMENT_RIGHT   :  return CMessage::Text(MSG_LIB_TEXT_RIGHT);   break;
      default                             :  return "Unknown"; break;
     }
  }
//+------------------------------------------------------------------+
//| Return the flag of displaying the graphical                      |
//| object on a specified chart timeframe                            |
//+------------------------------------------------------------------+

取决于所传递的容器内对象位置的类型,返回相应的文本消息。


现在我们略微谈谈有关优化的话题。

将光标悬停在对象上时,该对象可以更改其颜色。 目前,更改颜色的方法会立即起作用。 颜色也许已是您需要变更到的颜色,这并不重要。 相应地,在这种情况下,我们重涂一次同样颜色会令系统负载过度。 为避免这种情况,我们需要在更改颜色之前检查颜色。 如果对象的指定颜色与我们想要变更的颜色完全相同,我们不需要做任何事情,直接退出方法即可。

再 \MQL5\Include\DoEasy\Objects\Graph\GCnvElement.mqh 里,即在颜色设置方法中,完成宣称的改进

//--- Set the main background color
   void              SetBackgroundColor(const color colour,const bool set_init_color)
                       {
                        if(this.BackgroundColor()==colour)
                           return;
                        this.SetProperty(CANV_ELEMENT_PROP_BACKGROUND_COLOR,colour);
                        color arr[1];
                        arr[0]=colour;
                        this.SaveColorsBG(arr);
                        if(set_init_color)
                           this.SetBackgroundColorInit(this.BackgroundColor());
                       }
   void              SetBackgroundColors(color &colors[],const bool set_init_colors)
                       {
                        if(::ArrayCompare(colors,this.m_array_colors_bg)==0)
                           return;
                        this.SaveColorsBG(colors);
                        this.SetProperty(CANV_ELEMENT_PROP_BACKGROUND_COLOR,this.m_array_colors_bg[0]);
                        if(set_init_colors)
                           this.SetBackgroundColorsInit(colors);
                       }
//--- Set the background color when clicking on the control
   void              SetBackgroundColorMouseDown(const color colour)
                       {
                        if(this.BackgroundColorMouseDown()==colour)
                           return;
                        this.SetProperty(CANV_ELEMENT_PROP_BACKGROUND_COLOR_MOUSE_DOWN,colour);
                        color arr[1];
                        arr[0]=colour;
                        this.SaveColorsBGMouseDown(arr);
                       }
   void              SetBackgroundColorsMouseDown(color &colors[])
                       {
                        if(::ArrayCompare(colors,this.m_array_colors_bg_dwn)==0)
                           return;
                        this.SaveColorsBGMouseDown(colors);
                        this.SetProperty(CANV_ELEMENT_PROP_BACKGROUND_COLOR_MOUSE_DOWN,this.m_array_colors_bg_dwn[0]);
                       }
//--- Set the background color when hovering the mouse over control
   void              SetBackgroundColorMouseOver(const color colour)
                       {
                        if(this.BackgroundColorMouseOver()==colour)
                           return;
                        this.SetProperty(CANV_ELEMENT_PROP_BACKGROUND_COLOR_MOUSE_OVER,colour);
                        color arr[1];
                        arr[0]=colour;
                        this.SaveColorsBGMouseOver(arr);
                       }
   void              SetBackgroundColorsMouseOver(color &colors[])
                       {
                        if(::ArrayCompare(colors,this.m_array_colors_bg_ovr)==0)
                           return;
                        this.SaveColorsBGMouseOver(colors);
                        this.SetProperty(CANV_ELEMENT_PROP_BACKGROUND_COLOR_MOUSE_OVER,this.m_array_colors_bg_ovr[0]);
                       }
//--- Set the initial main background color

在调用的涂色方法中,我们将对象的颜色与传递给该方法的颜色进行比较。 在这些方法中都会应用到颜色数组,我们需要比较两个数组是否等价。 我们调用 ArrayCompare() 函数来执行此操作,如果数组比较结果相等,则返回零。

在受保护的构造函数的最后,在创建要显示的错误字符串中添加类的名称和所创建对象的类型

   else
     {
      ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_FAILED_CREATE_ELM_OBJ),"\"",this.TypeElementDescription(element_type),"\" ",this.m_name);
     }
  }
//+------------------------------------------------------------------+
//| Destructor                                                       |
//+------------------------------------------------------------------+

以前,在调试期间,若是对象尚未创建,则会将含有其名称的相应记录发送到日志。 这还不够,因为不清楚消息具体来自哪个类,以及它是什么类型的对象。 现在,它就能更容易查找错误消息来自的正确类。

但与此同时,指定的对象类型并不总是正确的,因为几乎所有图形元素都有一个复杂的继承层次结构,并且它们的类型随着其层次结构中所有构造函数的成功处理而变化,从最简单的一个开始,到最后一个结束,在其中会为所创建对象指示正确的类型。 但这里也有积极的一面。 知道我们正在创建什么类型的对象,并查看创建它时在哪个阶段被错误终止,我们已知我们应在哪里(在哪个类中)查找错误,因为导致构造函数错误终止的类型现在会显示在日志里。


我们已有在不同类别的不同控件中处理图形元素颜色的方法,这很合理 — 如果对象没有需要更改颜色的属性,则无需为其内缺少的属性创建处理方法。 与对象不同,其衍生后代已含有此属性,并能够处理它。 因此,我们实现了处理已支持属性的方法。

在 \MQL5\Include\DoEasy\Objects\Graph\Form.mqh 窗体对象文件中的颜色处理方法里添加上述修改

//+------------------------------------------------------------------+
//| Methods of simplified access to object properties                |
//+------------------------------------------------------------------+
//--- (1) Set and (2) return the control frame color
   void              SetBorderColor(const color colour,const bool set_init_color)
                       {
                        if(this.BorderColor()==colour)
                           return;
                        this.SetProperty(CANV_ELEMENT_PROP_BORDER_COLOR,colour);
                        if(set_init_color)
                           this.SetBorderColorInit(colour);
                       }
   color             BorderColor(void)                            const { return (color)this.GetProperty(CANV_ELEMENT_PROP_BORDER_COLOR);             }
//--- (1) Set and (2) return the control frame color when clicking the control
   void              SetBorderColorMouseDown(const color colour)
                       {
                        if(this.BorderColorMouseDown()==colour)
                           return;
                        this.SetProperty(CANV_ELEMENT_PROP_BORDER_COLOR_MOUSE_DOWN,colour);
                       }
   color             BorderColorMouseDown(void)                   const { return (color)this.GetProperty(CANV_ELEMENT_PROP_BORDER_COLOR_MOUSE_DOWN);  }
//--- (1) Set and (2) return the control frame color when hovering the mouse over the control
   void              SetBorderColorMouseOver(const color colour)
                       {
                        if(this.BorderColorMouseOver()==colour)
                           return;
                        this.SetProperty(CANV_ELEMENT_PROP_BORDER_COLOR_MOUSE_OVER,colour);
                       }
   color             BorderColorMouseOver(void)                   const { return (color)this.GetProperty(CANV_ELEMENT_PROP_BORDER_COLOR_MOUSE_OVER);  }


在绘制表单边框的方法中,以前默认绘制简单边框。 但如果将 FRAME_STYLE_NONE(无边框)边框样式传递给该方法,则该方法仍要绘制一个简单边框。 在许多对象中,调用此方法之前会检查边框的类型,并且仅当对象具有边框时才调用该方法。 但依然是为了考虑到将来可能的遗漏,我们实现了默认边框缺失,而绘制简单边框则根据 “switch” 运算符选择性执行:

//+------------------------------------------------------------------+
//| Draw the form frame                                              |
//+------------------------------------------------------------------+
void CForm::DrawFormFrame(const int wd_top,              // Frame upper segment width
                          const int wd_bottom,           // Frame lower segment width
                          const int wd_left,             // Frame left segment width
                          const int wd_right,            // Frame right segment width
                          const color colour,            // Frame color
                          const uchar opacity,           // Frame opacity
                          const ENUM_FRAME_STYLE style)  // Frame style
  {
//--- Depending on the passed frame style
   switch(style)
     {
      //--- draw a dimensional (convex) frame
      case FRAME_STYLE_BEVEL  : this.DrawFrameBevel(0,0,this.Width(),this.Height(),wd_top,wd_bottom,wd_left,wd_right,colour,opacity);   break;
      //--- draw a dimensional (concave) frame
      case FRAME_STYLE_STAMP  : this.DrawFrameStamp(0,0,this.Width(),this.Height(),wd_top,wd_bottom,wd_left,wd_right,colour,opacity);   break;
      //--- draw a flat frame
      case FRAME_STYLE_FLAT   : this.DrawFrameFlat(0,0,this.Width(),this.Height(),wd_top,wd_bottom,wd_left,wd_right,colour,opacity);    break;
      //--- draw a simple frame
      case FRAME_STYLE_SIMPLE : this.DrawFrameSimple(0,0,this.Width(),this.Height(),wd_top,wd_bottom,wd_left,wd_right,colour,opacity);  break;
      //---FRAME_STYLE_NONE
      default                 : break;
     }
  }
//+------------------------------------------------------------------+


在最后一个鼠标事件的处理程序中,添加以下条件:最后一个鼠标事件是未定义状态,而当前状态是按钮超出窗体之外未按下,且状态未定义

//+------------------------------------------------------------------+
//| Last mouse event handler                                         |
//+------------------------------------------------------------------+
void CForm::OnMouseEventPostProcessing(void)
  {
   ENUM_MOUSE_FORM_STATE state=this.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              :
      case MOUSE_FORM_STATE_NONE                            :
        if(this.MouseEventLast()==MOUSE_EVENT_INSIDE_ACTIVE_AREA_NOT_PRESSED  || 
           this.MouseEventLast()==MOUSE_EVENT_INSIDE_FORM_NOT_PRESSED         || 
           this.MouseEventLast()==MOUSE_EVENT_OUTSIDE_FORM_NOT_PRESSED        ||
           this.MouseEventLast()==MOUSE_EVENT_NO_EVENT)
          {
           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(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;
     }
  }
//+------------------------------------------------------------------+

这种状况还需要处理把对象颜色重置为正常状态。 但这种方法中,最重要的事情是优化与鼠标的交互,事实在于对象每次都要重绘。 换言之,当颜色变更时,对象会用新颜色彻底重绘。 相应地,附着在它之上的所有对象也要被重绘,这就导致图形界面在视觉上会明显的“闪烁”。 现在,重绘已被删除,对象只需更改其颜色,而无需强迫彻底重绘它们。 与此同时,若颜色已等同于应更改的颜色,则保持不变即可。 我们简单地退出颜色变更方法而已。 我们已在之前设置了这些更改。

现在,图形元素与光标的交互操作正常。 我不排除进一步的改进,以此来优化和纠正未来发现的缺点。


如上所述,一些处理对象颜色的方法处于支持这些属性的对象类当中。 而与光标交互时,处理文本颜色的方法位于 WinForms 对象的基准对象类之中。 在此,我们将最终敲定操控对象文本方法的优化。

在 \MQL5\Include\DoEasy\Objects\Graph\WForms\WinFormBase.mqh 基准 WinForms 对象类中,即其受保护部分,声明在对象“启用”状态下存储初始文本颜色的变量

//+------------------------------------------------------------------+
//| Form object class                                                |
//+------------------------------------------------------------------+
class CWinFormBase : public CForm
  {
protected:
   color             m_fore_color_init;                        // Initial color of the control text
   color             m_fore_state_on_color_init;               // Initial color of the control text when the control is "ON"
private:


我们改进优化颜色处理的方法,就像我们上面所做的那样:

//--- (1) Set and (2) return the default text color of all panel objects
   void              SetForeColor(const color clr,const bool set_init_color)
                       {
                        if(this.ForeColor()==clr)
                           return;
                        this.SetProperty(CANV_ELEMENT_PROP_FORE_COLOR,clr);
                        if(set_init_color)
                           this.SetForeColorInit(clr);
                       }
   color             ForeColor(void)                           const { return (color)this.GetProperty(CANV_ELEMENT_PROP_FORE_COLOR);                     }
//--- (1) Set and (2) return the initial default text color of all panel objects
   void              SetForeColorInit(const color clr)               { this.m_fore_color_init=clr;                                                       }
   color             ForeColorInit(void)                       const { return (color)this.m_fore_color_init;                                             }
//--- (1) Set and (2) return the default text color opacity of all panel objects
   void              SetForeColorOpacity(const uchar value)
                       {
                        if(this.ForeColorOpacity()==value)
                           return;
                        this.SetProperty(CANV_ELEMENT_PROP_FORE_COLOR_OPACITY,value);
                       }
   uchar             ForeColorOpacity(void)                    const { return (uchar)this.GetProperty(CANV_ELEMENT_PROP_FORE_COLOR_OPACITY);             }
//--- (1) Set and (2) return the control text color when clicking the control
   void              SetForeColorMouseDown(const color clr)
                       {
                        if(this.ForeColorMouseDown()==clr)
                           return;
                        this.SetProperty(CANV_ELEMENT_PROP_FORE_COLOR_MOUSE_DOWN,clr);
                       }
   color             ForeColorMouseDown(void)                  const { return (color)this.GetProperty(CANV_ELEMENT_PROP_FORE_COLOR_MOUSE_DOWN);          }
//--- (1) Set and (2) return the control text color when hovering the mouse over the control
   void              SetForeColorMouseOver(const color clr)
                       {
                        if(this.ForeColorMouseOver()==clr)
                           return;
                        this.SetProperty(CANV_ELEMENT_PROP_FORE_COLOR_MOUSE_OVER,clr);
                       }
   color             ForeColorMouseOver(void)                  const { return (color)this.GetProperty(CANV_ELEMENT_PROP_FORE_COLOR_MOUSE_OVER);          }


添加方法,当启用对象与鼠标光标交互时更改文本颜色:

   color             ForeColorMouseOver(void)                  const { return (color)this.GetProperty(CANV_ELEMENT_PROP_FORE_COLOR_MOUSE_OVER);          }
   
//--- (1) Set and (2) return the initial enabled default text color of all panel objects
   void              SetForeStateOnColorInit(const color clr)        { this.m_fore_state_on_color_init=clr;                                              }
   color             ForeStateOnColorInit(void)                const { return (color)this.m_fore_state_on_color_init;                                    }
//--- (1) Set and (2) return the main text color for the "enabled" status
   void              SetForeStateOnColor(const color colour,const bool set_init_color)
                       {
                        if(this.ForeStateOnColor()==colour)
                           return;
                        this.SetProperty(CANV_ELEMENT_PROP_FORE_COLOR_STATE_ON,colour);
                        if(set_init_color)
                           this.SetForeStateOnColorInit(colour);
                       }
   color             ForeStateOnColor(void)                    const { return (color)this.GetProperty(CANV_ELEMENT_PROP_FORE_COLOR_STATE_ON);            }
   
//--- (1) Set and (2) return the text color when clicking on the control for the "enabled" status
   void              SetForeStateOnColorMouseDown(const color colour)
                       {
                        if(this.ForeStateOnColorMouseDown()==colour)
                           return;
                        this.SetProperty(CANV_ELEMENT_PROP_FORE_COLOR_STATE_ON_MOUSE_DOWN,colour);
                       }
   color             ForeStateOnColorMouseDown(void)           const { return (color)this.GetProperty(CANV_ELEMENT_PROP_FORE_COLOR_STATE_ON_MOUSE_DOWN); }
                       
//--- (1) Set and (2) return the text color when hovering the mouse over the control for the "enabled" status
   void              SetForeStateOnColorMouseOver(const color colour)
                       {
                        if(this.ForeStateOnColorMouseOver()==colour)
                           return;
                        this.SetProperty(CANV_ELEMENT_PROP_FORE_COLOR_STATE_ON_MOUSE_OVER,colour);
                       }
   color             ForeStateOnColorMouseOver(void)           const { return (color)this.GetProperty(CANV_ELEMENT_PROP_FORE_COLOR_STATE_ON_MOUSE_OVER); }
   
//--- (1) Set and (2) return the element text

现在,我们可在与鼠标交互时,以及当对象处于不同状态时更改文本的颜色,就像改变它的其它颜色(背景和边框)一样。

在类构造函数中,添加设置启用的对象文本颜色

//+------------------------------------------------------------------+
//| Constructor                                                      |
//+------------------------------------------------------------------+
CWinFormBase::CWinFormBase(const long chart_id,
                           const int subwindow,
                           const string name,
                           const int x,
                           const int y,
                           const int w,
                           const int h) : CForm(chart_id,subwindow,name,x,y,w,h)
  {
//--- Set the graphical element and library object types as a base WinForms object
   CGBaseObj::SetTypeElement(GRAPH_ELEMENT_TYPE_WF_BASE);
   CGCnvElement::SetProperty(CANV_ELEMENT_PROP_TYPE,GRAPH_ELEMENT_TYPE_WF_BASE);
   this.m_type=OBJECT_DE_TYPE_GWF_BASE; 
//--- Initialize all variables
   this.SetText("");
   this.SetForeColor(CLR_DEF_FORE_COLOR,true);
   this.SetForeStateOnColor(this.ForeColor(),true);
   this.SetForeStateOnColorMouseDown(this.ForeColor());
   this.SetForeStateOnColorMouseOver(this.ForeColor());
   this.SetForeColorOpacity(CLR_DEF_FORE_COLOR_OPACITY);
   this.SetFontBoldType(FW_TYPE_NORMAL);
   this.SetMarginAll(0);
   this.SetPaddingAll(0);
   this.SetBorderSizeAll(0);
   this.SetDockMode(CANV_ELEMENT_DOCK_MODE_NONE,false);
   this.SetBorderStyle(FRAME_STYLE_NONE);
   this.SetAutoSize(false,false);
   CForm::SetCoordXInit(x);
   CForm::SetCoordYInit(y);
   CForm::SetWidthInit(w);
   CForm::SetHeightInit(h);
   this.m_shadow=false;
   this.m_gradient_v=true;
   this.m_gradient_c=false;
  }
//+------------------------------------------------------------------+

默认情况下,当您将鼠标悬停,并单击对象时,处于“启用”状态的文本颜色不会更改 — 它们等于对象正常状态下的文本颜色。 在派生类的对象中,可以更改这些颜色,从而直观地示意对象与鼠标的交互。


在返回元素整数型属性说明的方法的末尾,添加返回新图形元素属性说明的代码模块

      property==CANV_ELEMENT_PROP_CHECK_FLAG_COLOR_MOUSE_OVER  ?  CMessage::Text(MSG_CANV_ELEMENT_PROP_CHECK_FLAG_COLOR_MOUSE_OVER)+
         (only_prop ? "" : !this.SupportProperty(property)     ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+::ColorToString((color)this.GetProperty(property),true)
         )  :
      property==CANV_ELEMENT_PROP_LIST_BOX_MULTI_COLUMN  ?  CMessage::Text(MSG_CANV_ELEMENT_PROP_LIST_BOX_MULTI_COLUMN)+
         (only_prop ? "" : !this.SupportProperty(property)     ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)this.GetProperty(property)
         )  :
      property==CANV_ELEMENT_PROP_LIST_BOX_COLUMN_WIDTH  ?  CMessage::Text(MSG_CANV_ELEMENT_PROP_LIST_BOX_COLUMN_WIDTH)+
         (only_prop ? "" : !this.SupportProperty(property)     ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)this.GetProperty(property)
         )  :
      property==CANV_ELEMENT_PROP_TAB_MULTILINE                ?  CMessage::Text(MSG_CANV_ELEMENT_PROP_TAB_MULTILINE)+
         (only_prop ? "" : !this.SupportProperty(property)     ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)this.GetProperty(property)
         )  :
      property==CANV_ELEMENT_PROP_TAB_ALIGNMENT                ?  CMessage::Text(MSG_CANV_ELEMENT_PROP_TAB_ALIGNMENT)+
         (only_prop ? "" : !this.SupportProperty(property)     ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+AlignmentDescription((ENUM_CANV_ELEMENT_ALIGNMENT)this.GetProperty(property))
         )  :
      property==CANV_ELEMENT_PROP_ALIGNMENT                    ?  CMessage::Text(MSG_CANV_ELEMENT_PROP_ALIGNMENT)+
         (only_prop ? "" : !this.SupportProperty(property)     ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+AlignmentDescription((ENUM_CANV_ELEMENT_ALIGNMENT)this.GetProperty(property))
         )  :
      ""
     );
  }
//+------------------------------------------------------------------+

我理应在上一篇文章中添加前两个模块。 不过迟到总比没有好...


在 CheckBox WinForms 对象中,到目前为止,我们已根据其字段内的硬编码坐标绘制了一个复选框。 这其实是不正确的,因为当调整字段大小时,复选框将依照相同的硬编码坐标拉伸或压缩。 如此,我们不应采用依靠从复选框边缘缩进的坐标(以像素为单位),而应采用相对坐标作为字段大小的百分比。 我们来修复这个问题。

在 \MQL5\Include\DoEasy\Objects\Graph\WForms\Common Controls\CheckBox.mqh 中,即在显示指定状态的复选框方法中,我们将计算复选框在“选中”和“未定义”状态下的相对坐标。 我将以实数值进行计算,然后将它们简化为整数值,并将坐标传递给 CCanvas 类的原本绘图方法

//+------------------------------------------------------------------+
//| Display the checkbox for the specified state                     |
//+------------------------------------------------------------------+
void CCheckBox::ShowControlFlag(const ENUM_CANV_ELEMENT_CHEK_STATE state)
  {
//--- Draw a filled rectangle of the selection checkbox area
   this.DrawRectangleFill(this.m_check_x,this.m_check_y,this.m_check_x+this.CheckWidth(),this.m_check_y+this.CheckHeight(),this.CheckBackgroundColor(),this.CheckBackgroundColorOpacity());
//--- Draw the rectangle of checkbox boundaries
   this.DrawRectangle(this.m_check_x,this.m_check_y,this.m_check_x+this.CheckWidth(),this.m_check_y+this.CheckHeight(),this.CheckBorderColor(),this.CheckBorderColorOpacity());
//--- Create X and Y coordinate arrays for drawing a polyline
   double x=(double)this.m_check_x;
   double y=(double)this.m_check_y;
   double w=(double)this.m_check_w;
   double h=(double)this.m_check_h;
//--- Calculate coordinates as double values and write them to arrays as integers
   int array_x[]={int(x+w*0.2), int(x+w*0.45), int(x+w*0.85), int(x+w*0.45)};
   int array_y[]={int(y+h*0.5), int(y+h*0.6),  int(y+h*0.3),  int(y+h*0.75)};
//--- Depending on the checkbox status passed to the method
   switch(state)
     {
      //--- Checked box
      case CANV_ELEMENT_CHEK_STATE_CHECKED :
        //--- First, draw a filled polygon inside the checkbox borders,
        //--- as well as a smoothed polygon in the form of a checkmark on top of it
        this.DrawPolygonFill(array_x,array_y,this.CheckFlagColor(),this.CheckFlagColorOpacity());
        this.DrawPolygonAA(array_x,array_y,this.CheckFlagColor(),this.CheckFlagColorOpacity());
        break;
      //--- Undefined state
      case CANV_ELEMENT_CHEK_STATE_INDETERMINATE :
        //--- Draw a filled rectangle inside the checkbox boundaries
        this.DrawRectangleFill(int(x+w*0.3),int(y+h*0.3),int(x+w*0.7),int(y+h*0.7),this.CheckFlagColor(),this.CheckFlagColorOpacity());
        break;
      //--- Unchecked checkbox
      default:
        break;
     }
  }
//+------------------------------------------------------------------+

现在,当复选框字段的大小更改时,复选标记将能正确显示。

我们针对 \MQL5\Include\DoEasy\Objects\Graph\WForms\Common Controls\Button.mqh 中的按钮对象类进行改进。

在设置“启用”状态颜色的方法声明中,为“启用”状态添加三个正式颜色参数:

//--- Set the colors for the 'enabled' status
   void              SetStateOnColors(const color back,
                                      const color back_down,
                                      const color back_over,
                                      const color fore,
                                      const color fore_down,
                                      const color fore_over,
                                      const bool set_init_color);

在类的实体之外,即在方法的实现代码之中,添加把所传递颜色设置到对象属性之中

//+------------------------------------------------------------------+
//| Set the colors for the toggle element 'enabled' status           |
//+------------------------------------------------------------------+
void CButton::SetStateOnColors(const color back,
                               const color back_down,
                               const color back_over,
                               const color fore,
                               const color fore_down,
                               const color fore_over,
                               const bool set_init_color)
  {
   this.SetBackgroundStateOnColor(back,set_init_color);
   this.SetBackgroundStateOnColorMouseDown(back_down);
   this.SetBackgroundStateOnColorMouseOver(back_over);
   this.SetForeStateOnColor(fore,set_init_color);
   this.SetForeStateOnColorMouseDown(fore_down);
   this.SetForeStateOnColorMouseOver(fore_over);
  }
//+------------------------------------------------------------------+


在设置控件 Toggle 标志的方法中,添加传递文本颜色,对应已启用对象与鼠标光标交互时的不同状态

//--- (1) Set and (2) return the control Toggle flag
   void              SetToggleFlag(const bool flag)
                       {
                        this.SetProperty(CANV_ELEMENT_PROP_BUTTON_TOGGLE,flag);
                        if(this.Toggle())
                           this.SetStateOnColors
                             (
                              this.BackgroundStateOnColor(),this.BackgroundStateOnColorMouseDown(),this.BackgroundStateOnColorMouseOver(),
                              this.ForeStateOnColor(),this.ForeStateOnColorMouseDown(),this.ForeStateOnColorMouseOver(),true
                             );
                       }

在设置控件 Toggle 状态的方法中,取决于按钮状态添加设置对象属性颜色,:

//--- (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.SetBackgroundColor(this.BackgroundStateOnColor(),false);
                           this.SetForeColor(this.ForeStateOnColor(),false);
                           this.UnpressOtherAll();
                          }
                        else
                          {
                           this.SetBackgroundColor(this.BackgroundColorInit(),false);
                           this.SetForeColor(this.ForeColorInit(),false);
                           this.SetBorderColor(this.BorderColorInit(),false);
                          }
                       }

名称中包含 “ColorToggleON” 字符串的所有方法都已被重命名。 该字符串已替换为 “StateOnColor”,而所有枚举常量都已相应地重命名。 例如,以下是为“已启用”状态对象设置主要背景颜色的方法:

   void              SetBackgroundStateOnColor(const color colour,const bool set_init_color)
                       {
                        this.SetProperty(CANV_ELEMENT_PROP_BACKGROUND_COLOR_STATE_ON,colour);
                        color arr[1];
                        arr[0]=colour;
                        this.CopyArraysColors(this.m_array_colors_bg_tgl,arr,DFUN);
                        if(set_init_color)
                           this.CopyArraysColors(this.m_array_colors_bg_tgl_init,arr,DFUN);
                       }

在类构造函数中,依据鼠标交互的三种状态,为已启用按钮添加设置颜色

//+------------------------------------------------------------------+
//| Constructor                                                      |
//+------------------------------------------------------------------+
CButton::CButton(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_BUTTON);
   CGCnvElement::SetProperty(CANV_ELEMENT_PROP_TYPE,GRAPH_ELEMENT_TYPE_WF_BUTTON);
   this.m_type=OBJECT_DE_TYPE_GWF_COMMON;
   this.SetCoordX(x);
   this.SetCoordY(y);
   this.SetWidth(w);
   this.SetHeight(h);
   this.Initialize();
   this.SetBackgroundColor(CLR_DEF_CONTROL_STD_BACK_COLOR,true);
   this.SetBackgroundColorMouseDown(CLR_DEF_CONTROL_STD_MOUSE_DOWN);
   this.SetBackgroundColorMouseOver(CLR_DEF_CONTROL_STD_MOUSE_OVER);
   this.SetBackgroundStateOnColor(CLR_DEF_CONTROL_STD_BACK_COLOR_ON,true);
   this.SetBackgroundStateOnColorMouseDown(CLR_DEF_CONTROL_STD_BACK_DOWN_ON);
   this.SetBackgroundStateOnColorMouseOver(CLR_DEF_CONTROL_STD_BACK_OVER_ON);
   this.SetOpacity(CLR_DEF_CONTROL_STD_OPACITY);
   this.SetTextAlign(ANCHOR_CENTER);
   this.SetMarginAll(3);
   this.SetWidthInit(this.Width());
   this.SetHeightInit(this.Height());
   this.SetCoordXInit(x);
   this.SetCoordYInit(y);
   this.SetToggleFlag(false);
   this.SetState(false);
   this.Redraw(false);
  }
//+------------------------------------------------------------------+

在鼠标光标事件处理程序中,根据交互状态,添加设置文本颜色

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

在最后一个鼠标事件处理程序中,根据对象文本颜色的状态(启用/禁用),添加恢复对象文本颜色

//+------------------------------------------------------------------+
//| Last mouse event handler                                         |
//+------------------------------------------------------------------+
void 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.BackgroundStateOnColor() : this.BackgroundColorInit(),false);
           this.SetForeColor(this.State() ? this.ForeStateOnColor() : this.ForeColorInit(),false);
           this.SetBorderColor(this.BorderColorInit(),false);
           this.m_mouse_event_last=ENUM_MOUSE_EVENT(state+MOUSE_EVENT_NO_EVENT);
           this.Redraw(false);
          }
        break;

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

在将容器中同一群组的所有按钮的按钮状态设置为“已释放”的方法中,将文本和边框的颜色设置为其原始值:

//+------------------------------------------------------------------+
//| 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);
         obj.SetForeColor(obj.ForeColorInit(),false);
         obj.SetBorderColor(obj.BorderColorInit(),false);
         //--- Redraw the object to display the changes
         obj.Redraw(false);
        }
     }
  }
//+------------------------------------------------------------------+

现在,按钮就能够根据与鼠标光标交互时按钮的状态(按下/释放)更改其上文本的显示颜色。

在 \MQL5\Include\DoEasy\Objects\Graph\WForms\Common Controls\ElementsListBox.mqh 中的 ElementsListBox 对象类里,返回列表中下一个对象所处坐标的方法里,我们现在能够用先前创建的宏替换中设置的默认值,来替换对象硬编码的以像素为单位的缩进。 默认值现在作为最小值。 目前,无论对象之间的距离如何,颜色都会正确更改:

//+------------------------------------------------------------------+
//| 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()+DEF_CONTROL_LIST_MARGIN_Y);
      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()+DEF_CONTROL_LIST_MARGIN_Y>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()+DEF_CONTROL_LIST_MARGIN_X : 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()+DEF_CONTROL_LIST_MARGIN_Y+(this.TypeGraphElement()==GRAPH_ELEMENT_TYPE_WF_BUTTON_LIST_BOX ? 2 : 0);
     }
  }
//+------------------------------------------------------------------+

在 \MQL5\Include\DoEasy\Objects\Graph\WForms\Common Controls\ListBox.mqh 的 ListBox WinForms 对象类中的创建由多个字符串组成的列表的方法声明里,加入新的指定列宽的正式参数以及容器适配其内容的自动调整大小标志

public:
//--- Create a list from the specified number of rows (Label objects)
   void              CreateList(const int line_count,const int new_column_width=0,const bool autosize=true);
//--- Constructor

在方法的实现代码中,根据传递给方法的列宽计算所创建字符串的宽度,并为所创建对象的“已启用”状态添加设置颜色

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

现在,仅当在方法输入中设置了标志时,容器才会更改其大小。

我们针对 \MQL5\Include\DoEasy\Objects\Graph\WForms\Common Controls\CheckedListBox.mqh 中的 CheckedListBox 对象类进行类似的改进。

在依据指定数量创建 CheckBox 对象的方法声明中,添加新的形式参数 — 容器为适应其内容自动调整大小的标志:

public:
//--- Create the specified number of CheckBox objects
   void              CreateCheckBox(const int count,const int width,const int new_column_width=0,const bool autosize=true);
//--- Constructor

在方法实现代码中,将标志传递给依据指定数量创建元素的方法。 最后,添加检查相同的标志,启动容器自动调整大小,从而适应在其中创建的新对象的数量:

//+------------------------------------------------------------------+
//| Create the specified number of CheckBox objects                  |
//+------------------------------------------------------------------+
void CCheckedListBox::CreateCheckBox(const int count,const int width,const int new_column_width=0,const bool autosize=true)
  {
//--- 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,new_column_width,autosize);
//--- In the loop by the created number of objects
   for(int i=0;i<this.ElementsTotal();i++)
     {
      //--- Get the created object from the list by the loop index
      obj=this.GetElement(i);
      //--- If the object could not be obtained, send the appropriate message to the log and move on to the next one
      if(obj==NULL)
        {
         ::Print(DFUN,MSG_ELM_LIST_ERR_FAILED_GET_GRAPH_ELEMENT_OBJ,this.TypeElementDescription(GRAPH_ELEMENT_TYPE_WF_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));
     }
//--- 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);
  }
//+------------------------------------------------------------------+

在创建新图形对象的方法中,将传递给该方法的已创建对象的高度基础上再加三个像素,以便令 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+3);
   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\ButtonListBox.mqh 中的 ButtonListBox 对象类,以类似的方式进行了改进。

此外还添加了自动调整容器大小以适合其内容的标志

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,const bool autosize=true);
//--- Constructor

在方法实现代码中,将标志传递给对象创建方法,并检查是否需要自动调整容器大小,从而适应所创建的内容:

//+------------------------------------------------------------------+
//| 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,const bool autosize=true)
  {
//--- 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,autosize);
//--- In the loop by the created number of objects
   for(int i=0;i<this.ElementsTotal();i++)
     {
      //--- Get the created object from the list by the loop index
      obj=this.GetElement(i);
      //--- If the object could not be obtained, send the appropriate message to the log and move on to the next one
      if(obj==NULL)
        {
         ::Print(DFUN,MSG_ELM_LIST_ERR_FAILED_GET_GRAPH_ELEMENT_OBJ,this.TypeElementDescription(GRAPH_ELEMENT_TYPE_WF_BUTTON));
         continue;
        }
      //--- Set left center text alignment
      obj.SetTextAlign(ANCHOR_CENTER);
      //--- Set the object text
      obj.SetText("Button"+string(i+1));
     }
//--- 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);
  }
//+------------------------------------------------------------------+

TabControl WinForms 对象布局

在函数库中创建图形元素时,最初采用的命名概念阻碍了我们创建复杂的复合图形对象,且不能将新图形对象附着到不断增长的嵌套层次结构中已创建的对象之上(稍后修复)。 由此,在此我只创建 TabControl 的布局,以便理解在应用为图形元素创建名称的新概念之后,我们如何实现它。

TabControl 对象应由一个面板构成,该面板将承载由按钮和面板组成的控件选项卡(TabPage 对象)。 按钮(选项卡标题 - TabHeader)将激活选项卡,而面板则显示应位于其上的对象。 若一个选项卡被激活时,将显示其面板,其按钮尺寸略大于面板隐藏时的选项卡未激活状态的按钮。

由于选卡页眉(TabHeader 对象)应该像按钮一样工作,同时能够增大尺寸,并且其外观与 Button 控件略有不同,因此,此对象应是 Button 控件的衍生后代,并实现了额外功能。

Tab 对象(TabPage) 应该是容器对象,因为它需要同时含有标题和按钮,然后还允许其它对象附着到其中。 最有可能的是,标题按钮将附着到 Panel 对象,而加载元素可被放置在面板本身上面。

TabControl 对象应该是选项卡对象附着到面板对象。

然而,这都是一个理论。 在实践中,由于我们受到嵌套对象数量的限制,并且我们无法创建一个完善的对象,然后将其它对象附着到它之上,因此我们将自行限制去创建 TabHeader 和 TabPage 对象的空白类,而对象本身(或者更确切地说是它的布局原型)是由容器对象创建的。 它还附加了三个按钮,和三个容纳可视化 TabControl 外观的容器。

该对象将是空的、且静态的,但是,按钮会响应鼠标单击、以及鼠标悬停在它们上方。 但按下按钮时,活动选项卡的尺寸不会增加,非活动选项卡的尺寸也不会减小,因为我们的按钮仍然没有此类功能。 我将从下一篇文章开始实现所有这些,需在创建一个为函数库图形元素命名的新概念之后。

在 \MQL5\Include\DoEasy\Objects\Graph\WForms\Containers\ 中,创建内含 TabControl 类的 TabControl.mqh 文件。

在其中包含类操作所需的文件

//+------------------------------------------------------------------+
//|                                                   TabControl.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"
#include "..\Containers\GroupBox.mqh"
//+------------------------------------------------------------------+

在下面添加假体类。 它们将作为两个空白的类,用于创建选项卡标题和选项卡本身:

//+------------------------------------------------------------------+
//| Include files                                                    |
//+------------------------------------------------------------------+
#include "..\Containers\Container.mqh"
#include "..\Containers\GroupBox.mqh"
//+------------------------------------------------------------------+
//| TabHeader object class of WForms TabControl                      |
//+------------------------------------------------------------------+
class CTabHeader : public CButton
  {
private:

protected:

public:

//--- Constructor
                     CTabHeader(const long chart_id,
                             const int subwindow,
                             const string name,
                             const int x,
                             const int y,
                             const int w,
                             const int h);
  };
//+------------------------------------------------------------------+
//| CTabHeader::Constructor                                          |
//+------------------------------------------------------------------+
CTabHeader::CTabHeader(const long chart_id,
                       const int subwindow,
                       const string name,
                       const int x,
                       const int y,
                       const int w,
                       const int h) : CButton(chart_id,subwindow,name,x,y,w,h)
  {
   CGBaseObj::SetTypeElement(GRAPH_ELEMENT_TYPE_WF_TAB_HEADER);
   CGCnvElement::SetProperty(CANV_ELEMENT_PROP_TYPE,GRAPH_ELEMENT_TYPE_WF_TAB_HEADER);
   this.m_type=OBJECT_DE_TYPE_GWF_COMMON;
  }
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| TabPage object class of WForms TabControl                        |
//+------------------------------------------------------------------+
class CTabPage : public CContainer
  {
private:

public:

//--- Constructor
                     CTabPage(const long chart_id,
                              const int subwindow,
                              const string name,
                              const int x,
                              const int y,
                              const int w,
                              const int h);
  };
//+------------------------------------------------------------------+
//| CTabPage::Constructor indicating the chart and subwindow ID      |
//+------------------------------------------------------------------+
CTabPage::CTabPage(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_TAB_PAGE);
   CGCnvElement::SetProperty(CANV_ELEMENT_PROP_TYPE,GRAPH_ELEMENT_TYPE_WF_TAB_PAGE);
   this.m_type=OBJECT_DE_TYPE_GWF_CONTAINER;
  }
//+------------------------------------------------------------------+

由于该类只实现了最低限度的参数化构造函数,这些构造函数仅指示图形元素的类型,和函数库图形对象的类型,因此这里没什么要研究的。 我会在下一篇文章中实现这些类。

接下来,声明继承自容器对象类的 TabControl WinForms 对象类,并在其中放置处理该类的变量和方法的声明

//+------------------------------------------------------------------+
//| TabControl object class of WForms controls                       |
//+------------------------------------------------------------------+
class CTabControl : public CContainer
  {
private:
   int                  m_item_width;                 // Fixed width of tab titles
   int                  m_item_height;                // Fixed height of tab titles
//--- Create a new graphical object
   virtual CGCnvElement *CreateNewGObject(const ENUM_GRAPH_ELEMENT_TYPE type,
                                          const int element_num,
                                          const string 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 TabPage objects
   void              CreateTabPage(const int count,const int width,const int height,const int tab_state=1);

//--- (1) Set and (2) return the location of tab headers on the control
   void              SetAlignment(const ENUM_CANV_ELEMENT_ALIGNMENT alignment)   { this.SetProperty(CANV_ELEMENT_PROP_TAB_ALIGNMENT,alignment); }
   ENUM_CANV_ELEMENT_ALIGNMENT Alignment(void)  const { return (ENUM_CANV_ELEMENT_ALIGNMENT)this.GetProperty(CANV_ELEMENT_PROP_TAB_ALIGNMENT);  }
//--- (1) Set and (2) return the flag allowing multiple rows of tab headers on the control
   void              SetMultiline(const bool flag)    { this.SetProperty(CANV_ELEMENT_PROP_TAB_MULTILINE,flag);                                 }
   bool              Multiline(void)            const { return (bool)this.GetProperty(CANV_ELEMENT_PROP_TAB_MULTILINE);                         }
//--- (1) Set and (2) return the fixed width of tab headers
   void              SetItemWidth(const int value)    { this.m_item_width=value;    }
   int               ItemWidth(void)            const { return this.m_item_width;   }
//--- (1) Set and (2) return the fixed height of tab headers
   void              SetItemHeight(const int value)   { this.m_item_height=value;   }
   int               ItemHeight(void)           const { return this.m_item_height;  }
//--- Set the fixed size of tab headers
   void              SetItemSize(const int w,const int h)
                       {
                        if(this.ItemWidth()!=w)
                           this.SetItemWidth(w);
                        if(this.ItemHeight()!=h)
                           this.SetItemHeight(h);
                       }
//--- Constructor
                     CTabControl(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                |
//+------------------------------------------------------------------+
CTabControl::CTabControl(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_TAB_CONTROL);
   CGCnvElement::SetProperty(CANV_ELEMENT_PROP_TYPE,GRAPH_ELEMENT_TYPE_WF_TAB_CONTROL);
   this.SetID(this.GetMaxIDAll()+1);
   this.m_type=OBJECT_DE_TYPE_GWF_CONTAINER;
   this.SetBorderSizeAll(0);
   this.SetBorderStyle(FRAME_STYLE_NONE);
   this.SetOpacity(CLR_DEF_CONTROL_TAB_OPACITY,true);
   this.SetBackgroundColor(CLR_DEF_CONTROL_TAB_BACK_COLOR,true);
   this.SetBackgroundColorMouseDown(CLR_DEF_CONTROL_TAB_MOUSE_DOWN);
   this.SetBackgroundColorMouseOver(CLR_DEF_CONTROL_TAB_MOUSE_OVER);
   this.SetBorderColor(CLR_DEF_CONTROL_TAB_BORDER_COLOR,true);
   this.SetBorderColorMouseDown(CLR_DEF_CONTROL_TAB_BORDER_MOUSE_DOWN);
   this.SetBorderColorMouseOver(CLR_DEF_CONTROL_TAB_BORDER_MOUSE_OVER);
   this.SetForeColor(CLR_DEF_FORE_COLOR,true);
   this.SetMultiline(false);
   this.SetAlignment(CANV_ELEMENT_ALIGNMENT_TOP);
   this.SetItemSize(58,20);
   this.CreateTabPage(3,this.Width(),this.Height()-this.ItemHeight(),0);
  }
//+------------------------------------------------------------------+

在构造器代码的末尾,调用方法创建三个选项卡

创建新图形对象的私密虚拟方法:

//+------------------------------------------------------------------+
//| Create a new graphical object                                    |
//+------------------------------------------------------------------+
CGCnvElement *CTabControl::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 CListBox(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;
      case GRAPH_ELEMENT_TYPE_WF_TAB_HEADER       :
         element=new CTabHeader(this.ChartID(),this.SubWindow(),name,x,y,w,h);
        break;
      case GRAPH_ELEMENT_TYPE_WF_TAB_PAGE       :
         element=new CTabPage(this.ChartID(),this.SubWindow(),name,x,y,w,h);
        break;
      case GRAPH_ELEMENT_TYPE_WF_TAB_CONTROL       :
         element=new CTabControl(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;
  }
//+------------------------------------------------------------------+

该方法与在其它容器对象类中创建图形元素的方法完全相同,并且简单地根据传递给该方法的类型创建新的图形元素。

依据指定数量创建 TabPage 对象的方法:

//+------------------------------------------------------------------+
//| Create the specified number of TabPage objects                   |
//+------------------------------------------------------------------+
void CTabControl::CreateTabPage(const int count,const int width,const int height,const int tab_state=1)
  {
//--- Create the pointer to the Button and Container objects
   CButton *header=NULL;
   CContainer *tab=NULL;
//--- Create the specified number of TabPage objects
   for(int i=0;i<count;i++)
     {
      //--- Set the initial tab coordinates
      int x=this.BorderSizeLeft()+2;
      int y=this.BorderSizeTop()+2;
      //--- Create the button object as a tab header
      if(!CContainer::CreateNewElement(GRAPH_ELEMENT_TYPE_WF_BUTTON,x+(this.ItemWidth()-4)*i,y,this.ItemWidth()-4,this.ItemHeight()-2,clrNONE,255,true,false))
        {
         ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_FAILED_CREATE_ELM_OBJ),this.TypeElementDescription(GRAPH_ELEMENT_TYPE_WF_BUTTON));
         continue;
        }
      //--- Get the created button from the list of created objects
      header=this.GetElementByType(GRAPH_ELEMENT_TYPE_WF_BUTTON,i);
      if(header==NULL)
        {
         ::Print(DFUN,CMessage::Text(MSG_ELM_LIST_ERR_FAILED_GET_GRAPH_ELEMENT_OBJ),this.TypeElementDescription(GRAPH_ELEMENT_TYPE_WF_BUTTON));
         continue;
        }
      //--- Set the header default property values
      header.SetID(this.GetMaxIDAll()+1);
      header.SetToggleFlag(true);
      header.SetGroupButtonFlag(true);
      header.SetText("TabPage"+string(i+1));
      header.SetTextAlign(ANCHOR_CENTER);
      header.SetOpacity(CLR_DEF_CONTROL_TAB_HEAD_OPACITY,true);
      header.SetBackgroundColor(CLR_DEF_CONTROL_TAB_HEAD_BACK_COLOR,true);
      header.SetBackgroundColorMouseDown(CLR_DEF_CONTROL_TAB_HEAD_MOUSE_DOWN);
      header.SetBackgroundColorMouseOver(CLR_DEF_CONTROL_TAB_HEAD_MOUSE_OVER);
      header.SetBackgroundStateOnColor(CLR_DEF_CONTROL_TAB_HEAD_BACK_COLOR_ON,true);
      header.SetBackgroundStateOnColorMouseDown(CLR_DEF_CONTROL_TAB_HEAD_BACK_DOWN_ON);
      header.SetBackgroundStateOnColorMouseOver(CLR_DEF_CONTROL_TAB_HEAD_BACK_OVER_ON);
      header.SetBorderColor(CLR_DEF_CONTROL_TAB_HEAD_BORDER_COLOR,true);
      header.SetBorderColorMouseDown(CLR_DEF_CONTROL_TAB_HEAD_BORDER_MOUSE_DOWN);
      header.SetBorderColorMouseOver(CLR_DEF_CONTROL_TAB_HEAD_BORDER_MOUSE_OVER);
      header.SetForeColor(CLR_DEF_FORE_COLOR,true);
      if(this.Alignment()==CANV_ELEMENT_ALIGNMENT_TOP)
         header.SetBorderSize(1,1,1,0);
      if(this.Alignment()==CANV_ELEMENT_ALIGNMENT_BOTTOM)
         header.SetBorderSize(1,0,1,1);
      if(this.Alignment()==CANV_ELEMENT_ALIGNMENT_LEFT)
         header.SetBorderSize(1,1,0,1);
      if(this.Alignment()==CANV_ELEMENT_ALIGNMENT_RIGHT)
         header.SetBorderSize(0,1,1,1);
      
      //--- Create a container object as a tab field attached objects are to be located on
      if(!CContainer::CreateNewElement(GRAPH_ELEMENT_TYPE_WF_CONTAINER,x-2,header.BottomEdgeRelative(),width,height,clrNONE,255,true,false))
        {
         ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_FAILED_CREATE_ELM_OBJ),this.TypeElementDescription(GRAPH_ELEMENT_TYPE_WF_CONTAINER));
         continue;
        }
      //--- Get the tab from the list of created objects
      tab=this.GetElementByType(GRAPH_ELEMENT_TYPE_WF_CONTAINER,i);
      if(tab==NULL)
        {
         ::Print(DFUN,CMessage::Text(MSG_ELM_LIST_ERR_FAILED_GET_GRAPH_ELEMENT_OBJ),this.TypeElementDescription(GRAPH_ELEMENT_TYPE_WF_CONTAINER));
         continue;
        }
      //--- Set the default property values for the created tab
      tab.SetID(this.GetMaxIDAll()+1);
      tab.SetBorderSizeAll(1);
      tab.SetOpacity(CLR_DEF_CONTROL_TAB_PAGE_OPACITY,true);
      tab.SetBackgroundColor(CLR_DEF_CONTROL_TAB_PAGE_BACK_COLOR,true);
      tab.SetBackgroundColorMouseDown(CLR_DEF_CONTROL_TAB_PAGE_MOUSE_DOWN);
      tab.SetBackgroundColorMouseOver(CLR_DEF_CONTROL_TAB_PAGE_MOUSE_OVER);
      tab.SetBorderColor(CLR_DEF_CONTROL_TAB_PAGE_BORDER_COLOR,true);
      tab.SetBorderColorMouseDown(CLR_DEF_CONTROL_TAB_PAGE_BORDER_MOUSE_DOWN);
      tab.SetBorderColorMouseOver(CLR_DEF_CONTROL_TAB_PAGE_BORDER_MOUSE_OVER);
      tab.SetForeColor(CLR_DEF_FORE_COLOR,true);
      tab.Hide();
     }
   //--- Get the title and tab from the list by the index of the active tab
   header=this.GetElementByType(GRAPH_ELEMENT_TYPE_WF_BUTTON,tab_state);
   tab=this.GetElementByType(GRAPH_ELEMENT_TYPE_WF_CONTAINER,tab_state);
   //--- If the pointers to objects have been received
   if(header!=NULL && tab!=NULL)
     {
      //--- Display the tab
      tab.Show();
      //--- Move the title to the front and set new sizes for it
      header.BringToTop();
      header.SetState(true);
      header.SetWidth(this.ItemWidth());
      header.SetHeight(this.ItemHeight());
      //--- Shift the title to new coordinates, since the size has become slightly larger,
      //--- and set new relative coordinates for the header
      header.Move(header.CoordX()-2,header.CoordY()-2);
      header.SetCoordXRelative(header.CoordXRelative()-2);
      header.SetCoordYRelative(header.CoordYRelative()-2);
      header.Update(true);
     }
  }
//+------------------------------------------------------------------+

鉴于这是一种临时方法,仅用于测试创建选项卡的概念,因此我们不会过多研究它,因为在下一篇文章中它就会经历相当大的变化。 简言之,在循环中创建指定数量的按钮对象。 按钮对象的尺寸略小于活动选项卡标题所需的尺寸。 然后创建一个容器对象作为选项卡框,保存附着到该选项卡的对象,且该对象将立即隐藏。 创建之后,两个对象都会收到其属性的默认值,并且活动选项卡的标题(其编号在方法的输入中指示)变为活动状态 — 按钮接收按下状态,其尺寸增加到属性中指定的大小,并将其带到前台,同时显示选项卡字段。

这就是我们现在显示 TabControl 布局所需的全部内容。

为了在容器对象中创建 TabControl 控件,我们需对容器对象类加以修改。

在 \MQL5\Include\DoEasy\Objects\Graph\WForms\Containers\Container.mqh 容器对象类中,即为附着对象设置参数的方法中,添加为所创建的 TabHeaderTabPage、和 TabControl 对象设置属性值:

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

在 \MQL5\Include\DoEasy\Objects\Graph\WForms\Containers\Panel.mqh 面板对象类文件中,包含 TabControl 对象文件:

//+------------------------------------------------------------------+
//| Include files                                                    |
//+------------------------------------------------------------------+
#include "Container.mqh"
#include "GroupBox.mqh"
#include "TabControl.mqh"
#include "..\..\WForms\Common Controls\ListBox.mqh"
#include "..\..\WForms\Common Controls\CheckedListBox.mqh"
#include "..\..\WForms\Common Controls\ButtonListBox.mqh"
//+------------------------------------------------------------------+

现在,该类对于所有容器对象类均可见。

在创建新图形对象的方法中,添加创建 TabControl 类对象

//+------------------------------------------------------------------+
//| 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 CListBox(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;
      case GRAPH_ELEMENT_TYPE_WF_TAB_HEADER        :
         element=new CTabHeader(this.ChartID(),this.SubWindow(),name,x,y,w,h);
        break;
      case GRAPH_ELEMENT_TYPE_WF_TAB_PAGE          :
         element=new CTabPage(this.ChartID(),this.SubWindow(),name,x,y,w,h);
        break;
      case GRAPH_ELEMENT_TYPE_WF_TAB_CONTROL       :
         element=new CTabControl(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 对象类中,添加创建 TabControl 类对象,它类似于创建新图形对象的方法所完成的工作:

//+------------------------------------------------------------------+
//| 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_LIST_BOX  :
         element=new CListBox(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;
      case GRAPH_ELEMENT_TYPE_WF_TAB_HEADER        :
         element=new CTabHeader(this.ChartID(),this.SubWindow(),name,x,y,w,h);
        break;
      case GRAPH_ELEMENT_TYPE_WF_TAB_PAGE          :
         element=new CTabPage(this.ChartID(),this.SubWindow(),name,x,y,w,h);
        break;
      case GRAPH_ELEMENT_TYPE_WF_TAB_CONTROL       :
         element=new CTabControl(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\Collections\GraphElementsCollection.mqh 图形元素集合类中,即针对光标下方以前的活动窗体处理完毕后的方法中,实现将指向当前窗体(光标所在的位置)的指针作为新的形式参数传递到方法中以及事件处理程序参数

//--- Reset all interaction flags for all forms except the specified one
   void              ResetAllInteractionExeptOne(CGCnvElement *form);
//--- Post-processing of the former active form under the cursor
   void              FormPostProcessing(CForm *form,const int id, const long &lparam, const double &dparam, const string &sparam);
//--- Add the element to the collection list

在方法本身中,获取窗体附着到的主对象。 然后获取附着到窗体的所有对象的列表。 接下来,在循环中遍历获得的列表,获取每个下一个附着对象。 此外,请务必调用判定鼠标光标相对于对象位置的方法。 这是对象在将光标移开后没有改变其颜色的主要原因。 在某些情况下,此状态未在对象中指定,并且无法处理:

//+------------------------------------------------------------------+
//| Post-processing of the former active form under the cursor       |
//+------------------------------------------------------------------+
void CGraphElementsCollection::FormPostProcessing(CForm *form,const int id, const long &lparam, const double &dparam, const string &sparam)
  {
//--- Get the main object the form is attached to
   CForm *main=form.GetMain();
   if(main==NULL)
      main=form;
//--- Get all the elements attached to the form
   CArrayObj *list=main.GetListElements();
   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
         CWinFormBase *elm=obj.GetInteractForm(j);
         if(elm==NULL)
            continue;
         //--- determine the location of the cursor relative to the object 
         //--- and call the mouse event handling method for the object
         elm.MouseFormState(id,lparam,dparam,sparam);
         elm.OnMouseEventPostProcessing();
        }
     }
   ::ChartRedraw(main.ChartID());
  }
//+------------------------------------------------------------------+

循环完毕后,更新对象图表。 经过此优调后,对象与鼠标应能正确交互。

在事件处理程序中,指向当前窗体的指针和处理程序的参数值,现在都会传递给该方法:

            else
              {
               //--- The undefined mouse status in mouse_state means releasing the left button
               //--- Assign the new mouse status to the variable
               if(mouse_state==MOUSE_FORM_STATE_NONE)
                  mouse_state=MOUSE_FORM_STATE_INSIDE_ACTIVE_AREA_RELEASED;
               //--- Handle moving the cursor mouse away from the graphical element
               this.FormPostProcessing(form,id,lparam,dparam,sparam);
              }

一切都准备好,可以进行测试了。


测试

为了执行测试,我们取用来自上一篇文章中的 EA,并将其保存到 \MQL5\Experts\TestDoEasy\Part113\ 中,命名为 TstDE113.mq5

无需在第二个 GroupBox 控件上创建 CheckedListBox、ButtonListBox 和 ListBox 对象,取而代之,创建 TabControl 对象

      //--- If the attached GroupBox object is created
      if(pnl.CreateNewElement(GRAPH_ELEMENT_TYPE_WF_GROUPBOX,x,2,w,h,C'0x91,0xAA,0xAE',0,true,false))
        {
         //--- get the pointer to the GroupBox object by its index in the list of bound GroupBox type objects
         gbox2=pnl.GetElementByType(GRAPH_ELEMENT_TYPE_WF_GROUPBOX,1);
         if(gbox2!=NULL)
           {
            //--- set the "indented frame" type, the frame color matches the main panel background color,
            //--- while the text color is the background color of the last attached panel darkened by 1
            gbox2.SetBorderStyle(FRAME_STYLE_STAMP);
            gbox2.SetBorderColor(pnl.BackgroundColor(),true);
            gbox2.SetForeColor(gbox2.ChangeColorLightness(obj.BackgroundColor(),-1),true);
            gbox2.SetText("GroupBox2");
            
            //--- Create the TabControl object
            gbox2.CreateNewElement(GRAPH_ELEMENT_TYPE_WF_TAB_CONTROL,4,12,gbox2.Width()-12,gbox2.Height()-20,clrNONE,255,true,false);
            //--- get the pointer to the TabControl object by its index in the list of bound objects of the TabControl type
            CTabControl *tctrl=gbox2.GetElementByType(GRAPH_ELEMENT_TYPE_WF_TAB_CONTROL,0);
            if(tctrl!=NULL)
              {
               //--- get the pointer to the Container object by its index in the list of bound objects of the Container type
               CContainer *page=tctrl.GetElementByType(GRAPH_ELEMENT_TYPE_WF_CONTAINER,0);
               if(page!=NULL)
                 {
                  // Here we will create objects attached to the specified tab of the TabControl object
                  // Unfortunately, in the current state of creating the names of graphical objects of the library,
                  // their further creation is limited by the number of characters in the resource name in the CCanvas class
                 }
               
              }
            /*
            //--- Create the CheckedListBox object
            //---...
            //---...
           }

在此,我们简单地创建一个 CTabControl 类的对象。 之后我们无法针对它做任何事情,因为当尝试将任何其它对象附着其上时,函数库都会报错,因为图形资源名称超过了规定长度。 我将在下一篇文章中解决此问题。

编译 EA,并在图表上启动它:


在所创建面板的左侧,我们看到,对象与鼠标交互均处理正确。 在右边,我们可以看到未来 TabControl 控件的布局。 它展示出第一个选项卡处于活动状态,并且标题的尺寸略大于非活动选项卡的标题尺寸。 选项卡能响应鼠标交互,更准确地说,是响应标题区域中存在的光标,及按下按钮。 还没有其它功能,现在此处也不需要。 我只创建了一个纯粹的控件原型。 我将在以下文章中应对其内容。


下一步是什么?

在下一篇文章中,我将创建一个为函数库图形对象命名的新算法,并继续开发 TabControl WinForms 对象。

以下是 MQL5 的当前函数库版本、测试 EA,和图表事件控制指标的所有文件,供您测试和下载。 在评论中留下您的问题、意见和建议。

返回内容目录

*该系列的前几篇文章:

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 对象
DoEasy. 控件 (第 12 部分): 基准列表对象、ListBox 和 ButtonListBox WinForms 对象



本文由MetaQuotes Ltd译自俄文
原文地址: https://www.mql5.com/ru/articles/11260

附加的文件 |
MQL5.zip (4412.44 KB)
利用对象轻松制作复杂指标 利用对象轻松制作复杂指标
本文提供了一种创建复杂指标的方法,同时还避免了在处置多个作图板、缓冲区、和/或组合来自多个来源的数据时出现的问题。
CCI 指标。 三个变换步骤 CCI 指标。 三个变换步骤
在本文中,我将针对 CCI 进行额外的修改,从而影响该指标的逻辑。 进而,我们就能够在主图表窗口中看到它。
学习如何基于强力指数(Force Index)设计交易系统 学习如何基于强力指数(Force Index)设计交易系统
欢迎阅读我们系列中的新篇章,有关如何基于最流行的技术指标设计交易系统。 在本文中,我们将学习一个新的技术指标,以及如何运用强力指数(Force Index)指标创建交易系统。
学习如何基于柴金(Chaikin)振荡器设计交易系统 学习如何基于柴金(Chaikin)振荡器设计交易系统
欢迎阅读我们系列的新篇章,学习如何基于最流行的技术指标设计交易系统。 通读这篇新文章,我们将学习如何基于柴金(Chaikin)振荡器指标设计交易系统。