English Русский Español Deutsch 日本語 Português
preview
DoEasy. 控件 (第 25 部分): Tooltip WinForms 对象

DoEasy. 控件 (第 25 部分): Tooltip WinForms 对象

MetaTrader 5示例 | 24 二月 2023, 09:37
621 0
Artyom Trishkin
Artyom Trishkin

内容


概述

当鼠标悬停在控件上方时,一段时间后会显示带有说明的工具提示。 自然而然地,并非每个元素都有工具提示,但每个图形对象都有设置它的能力。 我们为函数库的图形元素创建这样的可能性。

并非函数库中的每个图形元素都“知道”其它图形元素的存在,但所有这些对象都可以将托管元素附着于自身。 附着于图形元素的对象可能包括父元素,且其一无所知。 由于附着元素的控件都放置在图形元素的集合类当中,因此各种类型的控件的可视性没有问题 — 集合类知道有关它们的所有信息。

此外,如果我们以 MS Visual Studio 为例,则创建的 ToolTip 对象在窗体对象中可见,并且可分配给绑定到此窗体的对象。 我们将按以下方式执行此操作:那些需要看到工具提示对象的类对象都能创建它,且所有其它对象也能够附着于以前创建的此类对象。 为此,它们不需要“知道”它,因为 ArrayObj 列表接受从标准库的基类派生的任何对象。

换言之:在容器对象中(能够创建其它图形对象,并将其附着于自身),我们可以创建工具提示控件,并指示将其分配给哪个元素。 该元素拥有指向 ToolTip 对象的指针,而在所分配的 ToolTip 对象中设置所针对的对象。

典型情况,当光标悬停在控件上方一段时间后工具提示出现。 如果为一个对象分配了若干个提示,并且第一个提示已经出现,则当光标移到此种对象上方时,其余的工具提示将几乎无延迟地出现。 此行为会作为以后文章的主题。 在此,我只创建 ToolTip 控件,并能够把它分配给图形元素。

除了新控件的开发之外,在此我还会添加绘制新图形基元的功能,包括标准图标,以及左右和上下箭头... 换言之,我开始慢慢在函数库中添加绘制预定义图像的功能,并能够“按原样”使用它们。 此类图标可以在 ToolTip 控件,和我之前已创建的其它元素中使用。 稍后,我将在函数库的所有图形元素中添加使用新图形基元的能力。


改进库类

在 \MQL5\Include\DoEasy\Defines.mqh 中,将新的图形元素类型添加到图形元素类型列表之中:

//+-------------------------------------------------+
//| The list of graphical element types             |
//+-------------------------------------------------+
enum ENUM_GRAPH_ELEMENT_TYPE
  {
   GRAPH_ELEMENT_TYPE_STANDARD,                       // Standard graphical object
   GRAPH_ELEMENT_TYPE_STANDARD_EXTENDED,              // Extended standard graphical object
   GRAPH_ELEMENT_TYPE_SHADOW_OBJ,                     // Shadow object
   GRAPH_ELEMENT_TYPE_ELEMENT,                        // Element
   GRAPH_ELEMENT_TYPE_FORM,                           // Form
   GRAPH_ELEMENT_TYPE_WINDOW,                         // Window
   //--- WinForms
   GRAPH_ELEMENT_TYPE_WF_UNDERLAY,                    // Panel object underlay
   GRAPH_ELEMENT_TYPE_WF_BASE,                        // Windows Forms Base
   //--- 'Container' object types are to be set below
   GRAPH_ELEMENT_TYPE_WF_CONTAINER,                   // Windows Forms container base object
   GRAPH_ELEMENT_TYPE_WF_PANEL,                       // Windows Forms Panel
   GRAPH_ELEMENT_TYPE_WF_GROUPBOX,                    // Windows Forms GroupBox
   GRAPH_ELEMENT_TYPE_WF_TAB_CONTROL,                 // Windows Forms TabControl
   GRAPH_ELEMENT_TYPE_WF_SPLIT_CONTAINER,             // Windows Forms SplitContainer
   //--- '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_TOOLTIP,                     // Windows Forms ToolTip
   //--- Auxiliary elements of WinForms objects
   GRAPH_ELEMENT_TYPE_WF_LIST_BOX_ITEM,               // Windows Forms ListBoxItem
   GRAPH_ELEMENT_TYPE_WF_TAB_HEADER,                  // Windows Forms TabHeader
   GRAPH_ELEMENT_TYPE_WF_TAB_FIELD,                   // Windows Forms TabField
   GRAPH_ELEMENT_TYPE_WF_SPLIT_CONTAINER_PANEL,       // Windows Forms SplitContainerPanel
   GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON,                // Windows Forms ArrowButton
   GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_UP,             // Windows Forms UpArrowButton
   GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_DOWN,           // Windows Forms DownArrowButton
   GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_LEFT,           // Windows Forms LeftArrowButton
   GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_RIGHT,          // Windows Forms RightArrowButton
   GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTONS_UD_BOX,        // Windows Forms UpDownArrowButtonsBox
   GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTONS_LR_BOX,        // Windows Forms LeftRightArrowButtonsBox
   GRAPH_ELEMENT_TYPE_WF_SPLITTER,                    // Windows Forms Splitter
   GRAPH_ELEMENT_TYPE_WF_HINT_BASE,                   // Windows Forms HintBase
   GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_LEFT,              // Windows Forms HintMoveLeft
   GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_RIGHT,             // Windows Forms HintMoveRight
   GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_UP,                // Windows Forms HintMoveUp
   GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_DOWN,              // Windows Forms HintMoveDown
  };
//+------------------------------------------------------------------+


为了能够绘制一些标准图像,诸如信息警告错误、和一些其它图标,为对应的图标类型创建枚举

//+-------------------------------------------------+
//| Separator location in Split Container           |
//+-------------------------------------------------+
enum ENUM_CANV_ELEMENT_SPLITTER_ORIENTATION
  {
   CANV_ELEMENT_SPLITTER_ORIENTATION_VERTICAL,        // Vertical
   CANV_ELEMENT_SPLITTER_ORIENTATION_HORISONTAL,      // Horizontal
  };
//+-------------------------------------------------+
//| The list of predefined icons                    |
//+-------------------------------------------------+
enum ENUM_CANV_ELEMENT_TOOLTIP_ICON
  {
   CANV_ELEMENT_TOOLTIP_ICON_NONE,                    // None
   CANV_ELEMENT_TOOLTIP_ICON_INFO,                    // Info
   CANV_ELEMENT_TOOLTIP_ICON_WARNING,                 // Warning
   CANV_ELEMENT_TOOLTIP_ICON_ERROR,                   // Error
   CANV_ELEMENT_TOOLTIP_ICON_USER,                    // User
  };
//+-----------------------------------------------------------+
//| Integer properties of the graphical element on the canvas |
//+-----------------------------------------------------------+

在此,我将输入我将在函数库中创建的新图标名称。 然后,我们就可以简单地从列表中选择要在控件上绘制的图标类型。 稍后将实现绘制来自列表中每种特定类型图标的方法。


新控件将需要新属性。

将新属性添加到整数型图形元素属性列表之中,并将其总数从 122 增加到 129

//+------------------------------------------------------------------+
//| Integer properties of the graphical element on the canvas        |
//+------------------------------------------------------------------+
enum ENUM_CANV_ELEMENT_PROP_INTEGER
  {
   CANV_ELEMENT_PROP_ID = 0,                          // Element ID
   CANV_ELEMENT_PROP_TYPE,                            // Graphical element type

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

   CANV_ELEMENT_PROP_SPLIT_CONTAINER_PANEL2_COLLAPSED,// Flag for collapsed panel 2
   CANV_ELEMENT_PROP_SPLIT_CONTAINER_PANEL2_MIN_SIZE, // Panel 2 minimum size
   CANV_ELEMENT_PROP_TOOLTIP_INITIAL_DELAY,           // Tooltip display delay
   CANV_ELEMENT_PROP_TOOLTIP_AUTO_POP_DELAY,          // Tooltip display duration
   CANV_ELEMENT_PROP_TOOLTIP_RESHOW_DELAY,            // One element new tooltip display delay
   CANV_ELEMENT_PROP_TOOLTIP_SHOW_ALWAYS,             // Display a tooltip in inactive window
   CANV_ELEMENT_PROP_TOOLTIP_ICON,                    // Icon displayed in tooltip
   CANV_ELEMENT_PROP_TOOLTIP_IS_BALLOON,              // Tooltip in the form of a "cloud"
   CANV_ELEMENT_PROP_TOOLTIP_USE_FADING,              // Fade when showing/hiding a tooltip
  };
#define CANV_ELEMENT_PROP_INTEGER_TOTAL (129)         // Total number of integer properties
#define CANV_ELEMENT_PROP_INTEGER_SKIP  (0)           // Number of integer properties not used in sorting
//+------------------------------------------------------------------+


将两个新属性添加到字符串型属性列表之中,并将其总数增加到 6

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


将新属性添加到画布上图形元素的可能排序准则列表中:

//+------------------------------------------------------------------+
//| Possible sorting criteria of graphical elements on the canvas    |
//+------------------------------------------------------------------+
#define FIRST_CANV_ELEMENT_DBL_PROP  (CANV_ELEMENT_PROP_INTEGER_TOTAL-CANV_ELEMENT_PROP_INTEGER_SKIP)
#define FIRST_CANV_ELEMENT_STR_PROP  (CANV_ELEMENT_PROP_INTEGER_TOTAL-CANV_ELEMENT_PROP_INTEGER_SKIP+CANV_ELEMENT_PROP_DOUBLE_TOTAL-CANV_ELEMENT_PROP_DOUBLE_SKIP)
enum ENUM_SORT_CANV_ELEMENT_MODE
  {
//--- Sort by integer properties
   SORT_BY_CANV_ELEMENT_ID = 0,                       // Sort by element ID
   SORT_BY_CANV_ELEMENT_TYPE,                         // Sort by graphical element type

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

   SORT_BY_CANV_ELEMENT_SPLIT_CONTAINER_PANEL2_COLLAPSED,// Sort by flag for collapsed panel 2
   SORT_BY_CANV_ELEMENT_SPLIT_CONTAINER_PANEL2_MIN_SIZE, // Sort by panel 2 minimum size
   SORT_BY_CANV_ELEMENT_TOOLTIP_INITIAL_DELAY,        // Sort by tooltip display delay
   SORT_BY_CANV_ELEMENT_TOOLTIP_AUTO_POP_DELAY,       // Sort by tooltip display duration
   SORT_BY_CANV_ELEMENT_TOOLTIP_RESHOW_DELAY,         // Sort by one element new tooltip display delay
   SORT_BY_CANV_ELEMENT_TOOLTIP_SHOW_ALWAYS,          // Sort by a tooltip in inactive window
   SORT_BY_CANV_ELEMENT_TOOLTIP_ICON,                 // Sort by icon displayed in a tooltip
   SORT_BY_CANV_ELEMENT_TOOLTIP_IS_BALLOON,           // Sort by a cloud tooltip flag
   SORT_BY_CANV_ELEMENT_TOOLTIP_USE_FADING,           // Sort by the flag of fading when showing/hiding a tooltip
//--- Sort by real properties

//--- Sort by string properties
   SORT_BY_CANV_ELEMENT_NAME_OBJ = FIRST_CANV_ELEMENT_STR_PROP,// Sort by an element object name
   SORT_BY_CANV_ELEMENT_NAME_RES,                     // Sort by the graphical resource name
   SORT_BY_CANV_ELEMENT_TEXT,                         // Sort by graphical element text
   SORT_BY_CANV_ELEMENT_DESCRIPTION,                  // Sort by graphical element description
   SORT_BY_CANV_ELEMENT_TOOLTIP_HEADER,               // Sort by ToolTip header for an element
   SORT_BY_CANV_ELEMENT_TOOLTIP_TEXT,                 // Sort by ToolTip text for an element
  };
//+------------------------------------------------------------------+

现在,我们就能按新属性对图形元素进行排序和选择。


在 \MQL5\Include\DoEasy\Data.mqh 里,添加新的消息索引:

//--- CForm
   MSG_FORM_OBJECT_TEXT_NO_SHADOW_OBJ_FIRST_CREATE_IT,// No shadow object. Create it using the CreateShadowObj() method
   MSG_FORM_OBJECT_ERR_FAILED_CREATE_SHADOW_OBJ,      // Failed to create new shadow object
   MSG_FORM_OBJECT_ERR_FAILED_CREATE_PC_OBJ,          // Failed to create new pixel copier object
   MSG_FORM_OBJECT_PC_OBJ_ALREADY_IN_LIST,            // Pixel copier object with ID already present in the list 
   MSG_FORM_OBJECT_PC_OBJ_NOT_EXIST_LIST,             // No pixel copier object with ID in the list 
   MSG_FORM_OBJECT_ERR_NOT_INTENDED,                  // The method is not meant for creating such an object: 
   MSG_FORM_TOOLTIP_OBJ_ALREADY_EXISTS,               // ToolTip object already exists

//--- CFrame

...

   MSG_GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_UP,            // HintMoveLeft control
   MSG_GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_DOWN,          // HintMoveLeft control
   MSG_GRAPH_ELEMENT_TYPE_WF_TOOLTIP,                 // ToolTip control
   MSG_GRAPH_OBJ_BELONG_PROGRAM,                      // Graphical object belongs to a program
   MSG_GRAPH_OBJ_BELONG_NO_PROGRAM,                   // Graphical object does not belong to a program

...

   MSG_CANV_ELEMENT_PROP_SPLIT_CONTAINER_PANEL2_COLLAPSED,  // Flag for collapsed panel 1
   MSG_CANV_ELEMENT_PROP_SPLIT_CONTAINER_PANEL2_MIN_SIZE,   // Panel 2 minimum size
   MSG_CANV_ELEMENT_PROP_TOOLTIP_INITIAL_DELAY,       // Tooltip display delay
   MSG_CANV_ELEMENT_PROP_TOOLTIP_AUTO_POP_DELAY,      // Tooltip display duration
   MSG_CANV_ELEMENT_PROP_TOOLTIP_RESHOW_DELAY,        // One element new tooltip display delay
   MSG_CANV_ELEMENT_PROP_TOOLTIP_SHOW_ALWAYS,         // Display a tooltip in inactive window
   MSG_CANV_ELEMENT_PROP_TOOLTIP_ICON,                // Icon displayed in a tooltip
   MSG_CANV_ELEMENT_PROP_TOOLTIP_IS_BALLOON,          // Tooltip in the form of a "cloud"
   MSG_CANV_ELEMENT_PROP_TOOLTIP_USE_FADING,          // Fade when showing/hiding a tooltip
   
//--- 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
   MSG_CANV_ELEMENT_PROP_DESCRIPTION,                 // Graphical element description
   MSG_CANV_ELEMENT_PROP_TOOLTIP_TITLE,               // Element tooltip title
   MSG_CANV_ELEMENT_PROP_TOOLTIP_TEXT,                // Element tooltip text
  };
//+------------------------------------------------------------------+

以及与新添加的索引对应的文本消息

//--- CForm
   {"Отсутствует объект тени. Необходимо сначала его создать при помощи метода CreateShadowObj()","There is no shadow object. You must first create it using the CreateShadowObj () method"},
   {"Не удалось создать новый объект для тени","Failed to create new object for shadow"},
   {"Не удалось создать новый объект-копировщик пикселей","Failed to create new pixel copier object"},
   {"В списке уже есть объект-копировщик пикселей с идентификатором ","There is already a pixel copier object in the list with ID "},
   {"В списке нет объекта-копировщика пикселей с идентификатором ","No pixel copier object with ID "},
   {"Метод не предназначен для создания такого объекта: ","The method is not intended to create such an object: "},
   {"Объект ToolTip уже существует","ToolTip object already exists"},
   
//--- CFrame

...

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

...

   {"Флаг свёрнутости панели 2","Flag to indicate that panel 2 is collapsed"},
   {"Минимальный размер панели 2","Min size of Panel 2"},
   {"Задержка отображения подсказки","Tooltip initial delay"},
   {"Длительность отображения подсказки","Tooltip autopoop delay"},
   {"Задержка отображения новой подсказки одного элемента","Tooltip reshow delay"},
   {"Отображать подсказку в неактивном окне","Tooltip show always"},
   {"Значок, отображаемый в подсказке","Tooltip icon"},
   {"Подсказка в форме \"облачка\"","Tooltip as \"Balloon\""},
   {"Угасание при отображении и скрытии подсказки","Tooltip uses fading"},

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


为了获得图形元素的描述,基准图形函数库对象的 \MQL5\Include\DoEasy\Objects\Graph\GBaseObj.mqh 文件含有 TypeElementDescription() 方法。 在其中添加返回新控件的说明

//+------------------------------------------------------------------+
//| 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_CONTROL            ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_TAB_CONTROL)           :
      type==GRAPH_ELEMENT_TYPE_WF_SPLIT_CONTAINER        ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_SPLIT_CONTAINER)       :
      //--- Standard controls
      type==GRAPH_ELEMENT_TYPE_WF_COMMON_BASE            ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_COMMON_BASE)           :
      type==GRAPH_ELEMENT_TYPE_WF_LABEL                  ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_LABEL)                 :
      type==GRAPH_ELEMENT_TYPE_WF_CHECKBOX               ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_CHECKBOX)              :
      type==GRAPH_ELEMENT_TYPE_WF_RADIOBUTTON            ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_RADIOBUTTON)           :
      type==GRAPH_ELEMENT_TYPE_WF_BUTTON                 ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_BUTTON)                :
      type==GRAPH_ELEMENT_TYPE_WF_ELEMENTS_LIST_BOX      ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_ELEMENTS_LIST_BOX)     :
      type==GRAPH_ELEMENT_TYPE_WF_LIST_BOX               ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_LIST_BOX)              :
      type==GRAPH_ELEMENT_TYPE_WF_LIST_BOX_ITEM          ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_LIST_BOX_ITEM)         :
      type==GRAPH_ELEMENT_TYPE_WF_CHECKED_LIST_BOX       ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_CHECKED_LIST_BOX)      :
      type==GRAPH_ELEMENT_TYPE_WF_BUTTON_LIST_BOX        ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_BUTTON_LIST_BOX)       :
      type==GRAPH_ELEMENT_TYPE_WF_TOOLTIP                ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_TOOLTIP)               :
      //--- Auxiliary control objects
      type==GRAPH_ELEMENT_TYPE_WF_TAB_HEADER             ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_TAB_HEADER)            :
      type==GRAPH_ELEMENT_TYPE_WF_TAB_FIELD              ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_TAB_FIELD)             :
      type==GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON           ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON)          :
      type==GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_UP        ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_UP)       :
      type==GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_DOWN      ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_DOWN)     :
      type==GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_LEFT      ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_LEFT)     :
      type==GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_RIGHT     ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_RIGHT)    :
      type==GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTONS_UD_BOX   ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTONS_UD_BOX)  :
      type==GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTONS_LR_BOX   ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTONS_LR_BOX)  :
      type==GRAPH_ELEMENT_TYPE_WF_SPLIT_CONTAINER_PANEL  ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_SPLIT_CONTAINER_PANEL) :
      type==GRAPH_ELEMENT_TYPE_WF_SPLITTER               ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_SPLITTER)              :
      type==GRAPH_ELEMENT_TYPE_WF_HINT_BASE              ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_HINT_BASE)             :
      type==GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_LEFT         ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_LEFT)        :
      type==GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_RIGHT        ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_RIGHT)       :
      type==GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_UP           ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_UP)          :
      type==GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_DOWN         ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_DOWN)        :
      "Unknown"
     );
  }  
//+------------------------------------------------------------------+

该方法接收对象的类型,据其获取其描述。 取决于所传递类型,根据我们上面添加到 Data.mqh 文件的消息索引显示文本说明。

以前,我们在创建附着图形元素时手动指定图形元素的基准对象。

另外,在上一篇文章中,我摆脱了这样做的需要,并删除了在对象里写入指向主对象和基准对象指针的方法。 然而,实践表明,我们仍然需要这样的方法。 为了在新工具提示对象里写入其对应的元素,我们需要一个方法来写入指向基准对象的指针。

在 \MQL5\Include\DoEasy\Objects\Graph\GCnvElement.mqh 的公开部分中,再次编写这样的方法

public:
//--- (1) Set and (2) return the X coordinate shift relative to the base object
   void              SetCoordXRelative(const int value)                                { this.m_shift_coord_x=value;                }
   int               CoordXRelative(void)                                        const { return this.m_shift_coord_x;               }
//--- (1) Set and (2) return the Y coordinate shift relative to the base object
   void              SetCoordYRelative(const int value)                                { this.m_shift_coord_y=value;                }
   int               CoordYRelative(void)                                        const { return this.m_shift_coord_y;               }
//--- Set the pointer to the parent element within related objects of the current group
   void              SetBase(CGCnvElement *base)                                       { this.m_element_base=base;                  }
   
//--- Event handler
   virtual void      OnChartEvent(const int id,const long& lparam,const double& dparam,const string& sparam);

该方法接收指向分配给 m_element_base 变量的基准对象指针。 因此,为了指示针对哪个对象创建工具提示,当鼠标光标悬停在该工具提示上方时,我们将写入指向触发工具提示的元素指针。

设置元素不透明度,和指定元素显示标志的方法都是虚拟的,因为我们需要在继承的类中重新定义它们。 另外,添加设置和返回工具提示文本的方法

//--- Set the shift of the (1) left, (2) top, (3) right, (4) bottom edge of the active area relative to the element,
//--- (5) all shifts of the active area edges relative to the element, (6) opacity
   void              SetActiveAreaLeftShift(const int value)   { this.SetProperty(CANV_ELEMENT_PROP_ACT_SHIFT_LEFT,fabs(value));       }
   void              SetActiveAreaRightShift(const int value)  { this.SetProperty(CANV_ELEMENT_PROP_ACT_SHIFT_RIGHT,fabs(value));      }
   void              SetActiveAreaTopShift(const int value)    { this.SetProperty(CANV_ELEMENT_PROP_ACT_SHIFT_TOP,fabs(value));        }
   void              SetActiveAreaBottomShift(const int value) { this.SetProperty(CANV_ELEMENT_PROP_ACT_SHIFT_BOTTOM,fabs(value));     }
   void              SetActiveAreaShift(const int left_shift,const int bottom_shift,const int right_shift,const int top_shift);
   virtual void      SetOpacity(const uchar value,const bool redraw=false);
   
//--- (1) Set and (2) return the Tooltip text
   virtual void      SetTooltipText(const string text)         { this.SetProperty(CANV_ELEMENT_PROP_TOOLTIP_TEXT,text);                }
   virtual string    TooltipText(void)                         { return this.GetProperty(CANV_ELEMENT_PROP_TOOLTIP_TEXT);              }
   
//--- (1) Set and (2) return the flag for displaying a non-hidden control
   virtual void      SetDisplayed(const bool flag)             { this.SetProperty(CANV_ELEMENT_PROP_DISPLAYED,flag);                   }
   bool              Displayed(void)                     const { return (bool)this.GetProperty(CANV_ELEMENT_PROP_DISPLAYED);           }


构造函数主体包含绘制各种图形基元的方法清单。 在清单的末尾,声明绘制预定义“标准”图像的新方法

//--- Return coordinate offsets relative to the rectangle anchor point by size
   void              GetShiftXYbySize(const int width,               // Rectangle size by width
                                      const int height,              //Rectangle size by height
                                      const ENUM_FRAME_ANCHOR anchor,// Rectangle anchor point, relative to which the offsets are calculated
                                      int &shift_x,                  // X coordinate of the rectangle upper left corner
                                      int &shift_y);                 // Y coordinate of the rectangle upper left corner
                                      
//+-------------------------------------------------+
//| Methods for drawing predefined standard images  |
//+-------------------------------------------------+
//--- Draw the Info icon
   void              DrawIconInfo(const int coord_x,const int coord_y,const uchar opacity);
//--- Draw the Warning icon
   void              DrawIconWarning(const int coord_x,const int coord_y,const uchar opacity);
//--- Draw the Error icon
   void              DrawIconError(const int coord_x,const int coord_y,const uchar opacity);
//--- Draw the left arrow
   void              DrawArrowLeft(const int coord_x,const int coord_y,const color clr,const uchar opacity);
//--- Draw the right arrow
   void              DrawArrowRight(const int coord_x,const int coord_y,const color clr,const uchar opacity);
//--- Draw the up arrow
   void              DrawArrowUp(const int coord_x,const int coord_y,const color clr,const uchar opacity);
//--- Draw the down arrow
   void              DrawArrowDown(const int coord_x,const int coord_y,const color clr,const uchar opacity);
  };
//+------------------------------------------------------------------+


在这两个类构造函数中,为图形元素新属性编写设置默认值

//+-------------------------------------------------+
//| Parametric constructor                          |
//+-------------------------------------------------+
CGCnvElement::CGCnvElement(const ENUM_GRAPH_ELEMENT_TYPE element_type,
                           CGCnvElement *main_obj,CGCnvElement *base_obj,
                           const int      element_id,
                           const int      element_num,
                           const long     chart_id,
                           const int      wnd_num,
                           const string   descript,
                           const int      x,
                           const int      y,
                           const int      w,
                           const int      h,
                           const color    colour,
                           const uchar    opacity,
                           const bool     movable=true,
                           const bool     activity=true,
                           const bool     redraw=false) : m_shadow(false)
  {
   this.SetTypeElement(element_type);
   this.m_type=OBJECT_DE_TYPE_GELEMENT; 
   this.m_element_main=main_obj;
   this.m_element_base=base_obj;
   this.m_chart_color_bg=(color)::ChartGetInteger((chart_id==NULL ? ::ChartID() : chart_id),CHART_COLOR_BACKGROUND);
   this.m_name=this.CreateNameGraphElement(element_type);
   this.m_chart_id=(chart_id==NULL || chart_id==0 ? ::ChartID() : chart_id);
   this.m_subwindow=wnd_num;
   this.SetFont(DEF_FONT,DEF_FONT_SIZE);
   this.m_text_anchor=0;
   this.m_text_x=0;
   this.m_text_y=0;
   this.SetBackgroundColor(colour,true);
   this.SetOpacity(opacity);
   this.m_shift_coord_x=0;
   this.m_shift_coord_y=0;
   if(::ArrayResize(this.m_array_colors_bg,1)==1)
      this.m_array_colors_bg[0]=this.BackgroundColor();
   if(::ArrayResize(this.m_array_colors_bg_dwn,1)==1)
      this.m_array_colors_bg_dwn[0]=this.BackgroundColor();
   if(::ArrayResize(this.m_array_colors_bg_ovr,1)==1)
      this.m_array_colors_bg_ovr[0]=this.BackgroundColor();
   if(this.Create(chart_id,wnd_num,x,y,w,h,redraw))
     {
      this.SetProperty(CANV_ELEMENT_PROP_NAME_RES,this.m_canvas.ResourceName()); // Graphical resource name
      this.SetProperty(CANV_ELEMENT_PROP_CHART_ID,CGBaseObj::ChartID());         // Chart ID

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

      this.SetProperty(CANV_ELEMENT_PROP_SPLIT_CONTAINER_PANEL2_COLLAPSED,false);                     // Flag for collapsed panel 1
      this.SetProperty(CANV_ELEMENT_PROP_SPLIT_CONTAINER_PANEL2_MIN_SIZE,25);                         // Panel 2 minimum size
      this.SetProperty(CANV_ELEMENT_PROP_TOOLTIP_INITIAL_DELAY,500);                                  // Tooltip display delay
      this.SetProperty(CANV_ELEMENT_PROP_TOOLTIP_AUTO_POP_DELAY,5000);                                // Tooltip display duration
      this.SetProperty(CANV_ELEMENT_PROP_TOOLTIP_RESHOW_DELAY,100);                                   // One element new tooltip display delay
      this.SetProperty(CANV_ELEMENT_PROP_TOOLTIP_SHOW_ALWAYS,false);                                  // Display a tooltip in inactive window
      this.SetProperty(CANV_ELEMENT_PROP_TOOLTIP_ICON,CANV_ELEMENT_TOOLTIP_ICON_NONE);                // Icon displayed in a tooltip
      this.SetProperty(CANV_ELEMENT_PROP_TOOLTIP_IS_BALLOON,false);                                   // Tooltip in the form of a "cloud"
      this.SetProperty(CANV_ELEMENT_PROP_TOOLTIP_USE_FADING,true);                                    // Fade when showing/hiding a tooltip
      this.SetProperty(CANV_ELEMENT_PROP_TOOLTIP_TITLE,"");                                           // Tooltip title for the element
      this.SetProperty(CANV_ELEMENT_PROP_TOOLTIP_TEXT,"");                                            // Tooltip text for the element
      this.SetVisibleFlag(false,false);
     }
   else
     {
      ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_FAILED_CREATE_ELM_OBJ),"\"",this.TypeElementDescription(element_type),"\" ",this.NameObj());
     }
  }
//+------------------------------------------------------------------+
//| Protected constructor                                            |
//+------------------------------------------------------------------+
CGCnvElement::CGCnvElement(const ENUM_GRAPH_ELEMENT_TYPE element_type,
                           CGCnvElement *main_obj,CGCnvElement *base_obj,
                           const long    chart_id,
                           const int     wnd_num,
                           const string  descript,
                           const int     x,
                           const int     y,
                           const int     w,
                           const int     h) : m_shadow(false)
  {
   this.m_type=OBJECT_DE_TYPE_GELEMENT; 
   this.m_element_main=main_obj;
   this.m_element_base=base_obj;
   this.m_chart_color_bg=(color)::ChartGetInteger((chart_id==NULL ? ::ChartID() : chart_id),CHART_COLOR_BACKGROUND);
   this.m_name=this.CreateNameGraphElement(element_type);
   this.m_chart_id=(chart_id==NULL || chart_id==0 ? ::ChartID() : chart_id);
   this.m_subwindow=wnd_num;
   this.m_type_element=element_type;
   this.SetFont(DEF_FONT,DEF_FONT_SIZE);
   this.m_text_anchor=0;
   this.m_text_x=0;
   this.m_text_y=0;
   this.SetBackgroundColor(CLR_CANV_NULL,true);
   this.SetOpacity(0);
   this.m_shift_coord_x=0;
   this.m_shift_coord_y=0;
   if(::ArrayResize(this.m_array_colors_bg,1)==1)
      this.m_array_colors_bg[0]=this.BackgroundColor();
   if(::ArrayResize(this.m_array_colors_bg_dwn,1)==1)
      this.m_array_colors_bg_dwn[0]=this.BackgroundColor();
   if(::ArrayResize(this.m_array_colors_bg_ovr,1)==1)
      this.m_array_colors_bg_ovr[0]=this.BackgroundColor();
   if(this.Create(chart_id,wnd_num,x,y,w,h,false))
     {
      this.SetProperty(CANV_ELEMENT_PROP_NAME_RES,this.m_canvas.ResourceName()); // Graphical resource name
      this.SetProperty(CANV_ELEMENT_PROP_CHART_ID,CGBaseObj::ChartID());         // Chart ID

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

      this.SetProperty(CANV_ELEMENT_PROP_SPLIT_CONTAINER_PANEL1_MIN_SIZE,25);                         // Panel 1 minimum size
      this.SetProperty(CANV_ELEMENT_PROP_SPLIT_CONTAINER_PANEL2_COLLAPSED,false);                     // Flag for collapsed panel 1
      this.SetProperty(CANV_ELEMENT_PROP_SPLIT_CONTAINER_PANEL2_MIN_SIZE,25);                         // Panel 2 minimum size
      this.SetProperty(CANV_ELEMENT_PROP_TOOLTIP_INITIAL_DELAY,500);                                  // Tooltip display delay
      this.SetProperty(CANV_ELEMENT_PROP_TOOLTIP_AUTO_POP_DELAY,5000);                                // Tooltip display duration
      this.SetProperty(CANV_ELEMENT_PROP_TOOLTIP_RESHOW_DELAY,100);                                   // One element new tooltip display delay
      this.SetProperty(CANV_ELEMENT_PROP_TOOLTIP_SHOW_ALWAYS,false);                                  // Display a tooltip in inactive window
      this.SetProperty(CANV_ELEMENT_PROP_TOOLTIP_ICON,CANV_ELEMENT_TOOLTIP_ICON_NONE);                // Icon displayed in a tooltip
      this.SetProperty(CANV_ELEMENT_PROP_TOOLTIP_IS_BALLOON,false);                                   // Tooltip in the form of a "cloud"
      this.SetProperty(CANV_ELEMENT_PROP_TOOLTIP_USE_FADING,true);                                    // Fade when showing/hiding a tooltip
      this.SetProperty(CANV_ELEMENT_PROP_TOOLTIP_TITLE,"");                                           // Tooltip title for the element
      this.SetProperty(CANV_ELEMENT_PROP_TOOLTIP_TEXT,"");                                            // Tooltip text for the element
      this.SetVisibleFlag(false,false);
     }
   else
     {
      ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_FAILED_CREATE_ELM_OBJ),"\"",this.TypeElementDescription(element_type),"\" ",this.NameObj());
     }
  }
//+------------------------------------------------------------------+


在类主体之外,编写绘制预定义图像的方法。

该方法绘制信息图标:

//+-------------------------------------------------+
//| Draw the Info icon                              |
//+-------------------------------------------------+
void CGCnvElement::DrawIconInfo(const int coord_x,const int coord_y,const uchar opacity)
  {
   int x=coord_x+8;
   int y=coord_y+8;
   this.DrawCircleFill(x,y,7,C'0x00,0x77,0xD7',opacity);
   this.DrawCircleWu(x,y,7.5,C'0x00,0x3D,0x8C',opacity);
   this.DrawRectangle(x,y-5,x+1,y-4, C'0xFF,0xFF,0xFF',opacity);
   this.DrawRectangle(x,y-2,x+1,y+4,C'0xFF,0xFF,0xFF',opacity);
  }
//+------------------------------------------------------------------+

勾勒图像轮廓的矩形左上角的坐标将传递给该方法。 接下来,计算圆心的坐标,绘制一个填充圆,同时在上面绘制一个经由 Wu 方法平滑的圆。 绘制圆圈完毕后,绘制 i 图标。 它由两个矩形组成,故此第一个矩形绘制一个 2x2 点,第二个绘制一条 2x6 垂直线。 不幸的是,由于在绘制此类线条时应用了奇怪的抗锯齿算法,我们无法绘制具有指定宽度(LineThick)的平滑线。 故此,我们只需绘制一个两像素宽的矩形,并按所需高度绘制两像素线条。 在该方法中,所有颜色都是预定义的,如此图像看起来就像在 MS Visual Studio 中那样的标准。


该方法绘制警告图标:

//+-------------------------------------------------+
//| Draw the Warning icon                           |
//+-------------------------------------------------+
void CGCnvElement::DrawIconWarning(const int coord_x,const int coord_y,const uchar opacity)
  {
   int x=coord_x+8;
   int y=coord_y+1;
   this.DrawTriangleFill(x,y,x+8,y+14,x-8,y+14,C'0xFC,0xE1,0x00',opacity);
   this.DrawTriangleWu(x,y,x+8,y+14,x-7,y+14,C'0xFF,0xB9,0x00',opacity);
   this.DrawRectangle(x,y+5,x+1,y+9,  C'0x00,0x00,0x00',opacity);
   this.DrawRectangle(x,y+11,x+1,y+12,C'0x00,0x00,0x00',opacity);
  }
//+------------------------------------------------------------------+

勾勒图像轮廓的矩形左上角的坐标将传递给该方法。 接下来,计算三角形上顶点的坐标,绘制一个填充三角形,同时在顶部绘制一个经由 Wu 方法平滑的三角形。 与第一个填充三角形相比,第二个三角形顶点的 X 坐标向右移动了一个像素。 这样做是为了令顶点在视觉上更粗壮,因为三角形内的图标应是两个像素宽,且它不能精确地定位在三角形的中心。 故此,我们将三角形的顶点“扩散”两个像素,这令图标在视觉处于居中的位置。 绘制三角形后,绘制 i 图标。 它由两个矩形组成,如此第一个矩形绘制 2x6 垂直线,第二个矩形绘制 2x2 点。

该方法绘制出错图标:

//+-------------------------------------------------+
//| Draw the Error icon                             |
//+-------------------------------------------------+
void CGCnvElement::DrawIconError(const int coord_x,const int coord_y,const uchar opacity)
  {
   int x=coord_x+8;
   int y=coord_y+8;
   this.DrawCircleFill(x,y,7,C'0xF0,0x39,0x16',opacity);
   this.DrawCircleWu(x,y,7.5,C'0xA5,0x25,0x12',opacity);
   this.DrawLineWu(x-3,y-3,x+3,y+3,C'0xFF,0xFF,0xFF',opacity);
   this.DrawLineWu(x+3,y-3,x-3,y+3,C'0xFF,0xFF,0xFF',opacity);
  }
//+------------------------------------------------------------------+

该方法绘制两个圆圈 — 其一填充颜色,而平滑的圆圈则勾边。 在中心,有两条平滑的线条形成 X 图标。 与其它图标一样,颜色是预定义的,与 MS Visual Studio 匹配。


该方法绘制左、右、上、下箭头:

//+-------------------------------------------------+
//| Draw the left arrow                             |
//+-------------------------------------------------+
void CGCnvElement::DrawArrowLeft(const int coord_x,const int coord_y,const color clr,const uchar opacity)
  {
   int x=coord_x;
   int y=coord_y+5;
   this.DrawTriangleFill(x,y,x+3,y-3,x+3,y+3,clr,opacity);
   this.DrawTriangleWu(x,y,x+3,y-3,x+3,y+3,clr,opacity);
  }
//+-------------------------------------------------+
//| Draw the right arrow                            |
//+-------------------------------------------------+
void CGCnvElement::DrawArrowRight(const int coord_x,const int coord_y,const color clr,const uchar opacity)
  {
   int x=coord_x;
   int y=coord_y+5;
   this.DrawTriangleFill(x+3,y,x,y+3,x,y-3,clr,opacity);
   this.DrawTriangleWu(x+3,y,x,y+3,x,y-3,clr,opacity);
  }
//+-------------------------------------------------+
//| Draw the up arrow                               |
//+-------------------------------------------------+
void CGCnvElement::DrawArrowUp(const int coord_x,const int coord_y,const color clr,const uchar opacity)
  {
   int x=coord_x+5;
   int y=coord_y;
   this.DrawTriangleFill(x,y,x+3,y+3,x-3,y+3,clr,opacity);
   this.DrawTriangleWu(x,y,x+3,y+3,x-3,y+3,clr,opacity);
  }
//+-------------------------------------------------+
//| Draw the down arrow                             |
//+-------------------------------------------------+
void CGCnvElement::DrawArrowDown(const int coord_x,const int coord_y,const color clr,const uchar opacity)
  {
   int x=coord_x+5;
   int y=coord_y+3;
   this.DrawTriangleFill(x,y,x+3,y-3,x-3,y-3,clr,opacity);
   this.DrawTriangleWu(x,y,x+3,y-3,x-3,y-3,clr,opacity);
  }
//+------------------------------------------------------------------+

勾边矩形左上角的坐标、以及绘制箭头的颜色、和不透明度都将传递给所有方法。 接下来,计算三角形初始顶点的坐标,并绘制一个填充三角形,同时在其顶部绘制由 Wu 方法平滑的三角形。 对于所绘制三角形的每个后续顶点,计算出的坐标位置是距其第一个顶点的偏移量。 指定颜色和不透明度,如此即可根据在图形元素上绘制的箭头状态更改所绘制三角形的外观。 例如,对于非活动元素,箭头应为灰色。 一般来说,与图标相比,这里有更多的选择。

阴影对象始终与另一个对象一起存在。 绘制阴影的背景不透明度应始终为零 — 对象应始终透明。 与此同时,在这个基板上绘制的阴影也应该具有一定程度的透明度,因为如果阴影完全覆盖它所投射的对象,那么它看起来会很奇怪。 因此,我们为阴影对象引入绘制阴影的不透明度的概念。 调用 SetOpacity() 方法时,此值应更改,令背景在任何时间都始终透明。 相同的方法也适用于参考底图,它设置精确的不透明度值。 故此,我们添加新方法SetOpacityDraw(绘制阴影的不透明度),并结合这两个方法。 调用它们时,背景的不透明度将始终设置为零,对于绘制阴影的颜色,应在方法中指定数值。

在 \MQL5\Include\DoEasy\Objects\Graph\ShadowObj.mqh 里,重命名 SetOpacity() 和 Opacity() 方法,并声明 SetOpacity() 虚拟方法

//--- Draw an object shadow
   void              Draw(const int shift_x,const int shift_y,const uchar blur_value,const bool redraw);
//--- Set the element opacity
   virtual void      SetOpacity(const uchar value,const bool redraw=false);

//+------------------------------------------------------------------+
//| Methods of simplified access to object properties                |
//+------------------------------------------------------------------+
//--- (1) Set and (2) return the shadow color
   void              SetColor(const color colour)                             { this.m_color=colour;     }
   color             Color(void)                                        const { return this.m_color;     }
//--- (1) Set and (2) return the drawn shadow opacity
   void              SetOpacityDraw(const uchar opacity)                      { this.m_opacity=opacity;  }
   uchar             OpacityDraw(void)                                  const { return this.m_opacity;   }
//--- (1) Set and (2) return the shadow blur
   void              SetBlur(const uchar blur)                                { this.m_blur=blur;        }
   uchar             Blur(void)                                         const { return this.m_blur;      }
  };
//+------------------------------------------------------------------+


在类构造函数中,设置绘制阴影的不透明度,及其模糊度的默认值

//+------------------------------------------------------------------+
//| Protected constructor with an object type,                       |
//| chart ID and subwindow                                           |
//+------------------------------------------------------------------+
CShadowObj::CShadowObj(CGCnvElement *main_obj,CGCnvElement *base_obj,
                       const long chart_id,
                       const int subwindow,
                       const string name,
                       const int x,
                       const int y,
                       const int w,
                       const int h) : CGCnvElement(GRAPH_ELEMENT_TYPE_SHADOW_OBJ,main_obj,base_obj,chart_id,subwindow,name,x,y,w,h)
  {
   this.m_type=OBJECT_DE_TYPE_GSHADOW; 
   CGCnvElement::SetBackgroundColor(clrNONE,true);
   CGCnvElement::SetOpacity(0);
   CGCnvElement::SetActive(false);
   this.SetOpacityDraw(CLR_DEF_SHADOW_OPACITY);
   this.SetBlur(DEF_SHADOW_BLUR);
   color gray=CGCnvElement::ChangeColorSaturation(this.ChartBackgroundColor(),-100);
   this.m_color=CGCnvElement::ChangeColorLightness(gray,-50);
   this.m_shadow=false;
   this.SetVisibleFlag(false,false);
   CGCnvElement::Erase();
  }
//+------------------------------------------------------------------+


为了在绘制对象阴影形状的方法中设置不透明度,所用值是设置给绘制阴影的不透明度值

//+------------------------------------------------------------------+
//| Draw the object shadow form                                      |
//+------------------------------------------------------------------+
void CShadowObj::DrawShadowFigureRect(const int w,const int h)
  {
   CGCnvElement::DrawRectangleFill(OUTER_AREA_SIZE,OUTER_AREA_SIZE,OUTER_AREA_SIZE+w-1,OUTER_AREA_SIZE+h-1,this.m_color,this.OpacityDraw());
   CGCnvElement::Update();
  }
//+------------------------------------------------------------------+


我们编写一个新的虚拟方法,设置元素的不透明度:

//+------------------------------------------------------------------+
//| Set the element opacity                                          |
//+------------------------------------------------------------------+
void CShadowObj::SetOpacity(const uchar value,const bool redraw=false)
  {
   CGCnvElement::SetOpacity(0,false);
   this.SetOpacityDraw(value>(uchar)CLR_DEF_SHADOW_OPACITY ? (uchar)CLR_DEF_SHADOW_OPACITY : value);
   this.m_canvas.Update(redraw);
  }
//+------------------------------------------------------------------+

调用该方法时,首先设置完整的阴影底图透明度接着,取传递给方法的值设置为绘制阴影的不透明度。 在这种情况下,如果该值超过为函数库设置的默认阴影不透明度值,则取该值。 这样做是为了防止出现完全覆盖底层对象的不透明阴影。

在 \MQL5\Include\DoEasy\Objects\Graph\WForms\WinFormBase.mqh 中,更改重绘对象方法的逻辑。 以前,对象的阴影总是在呈现时才绘制的。 如果重绘标志被重置,则阴影只会被擦除。 这可能会导致对象的阴影消失。 我们在检查重绘标志时执行所有操作:

//+-------------------------------------------------+
//| Redraw the object                               |
//+-------------------------------------------------+
void CWinFormBase::Redraw(bool redraw)
  {
//--- If the object type is less than the "Base WinForms object" or the object is not to be displayed, exit
   if(this.TypeGraphElement()<GRAPH_ELEMENT_TYPE_WF_BASE || !this.Displayed())
      return;
//--- Get the "Shadow" object
   CShadowObj *shadow=this.GetShadowObj();
//--- If the redraw flag is set,
   if(redraw)
     {
      //--- completely redraw the object and save its new initial look
      this.Erase(this.m_array_colors_bg,this.Opacity(),this.m_gradient_v,this.m_gradient_c,redraw);
      this.Done();
      //--- If the object has a shadow and the "Shadow" object exists, redraw it
      if(this.IsShadow() && shadow!=NULL)
        {
         //--- remove the previously drawn shadow,
         shadow.Erase();
         //--- save the relative shadow coordinates,
         int x=shadow.CoordXRelative();
         int y=shadow.CoordYRelative();
         //--- redraw the shadow,
         shadow.Draw(0,0,shadow.Blur(),redraw);
         //--- restore relative shadow coordinates
         shadow.SetCoordXRelative(x);
         shadow.SetCoordYRelative(y);
        }
     }
//--- otherwise, remove the object
   else
      this.Erase();
//--- Redraw all bound objects with the redraw flag
   for(int i=0;i<this.ElementsTotal();i++)
     {
      CWinFormBase *element=this.GetElement(i);
      if(element==NULL)
         continue;
      if(redraw)
         element.Redraw(redraw);
     }
//--- If the redraw flag is set and if this is the main object the rest are bound to,
//--- redraw the chart to display changes immediately
   if(this.IsMain() && redraw)
      ::ChartRedraw(this.ChartID());
  }
//+------------------------------------------------------------------+


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

//+------------------------------------------------------------------+
//| Return the description of the control integer property           |
//+------------------------------------------------------------------+
string CWinFormBase::GetPropertyDescription(ENUM_CANV_ELEMENT_PROP_INTEGER property,bool only_prop=false)
  {
   return
     (
      property==CANV_ELEMENT_PROP_ID                           ?  CMessage::Text(MSG_CANV_ELEMENT_PROP_ID)+
         (only_prop ? "" : !this.SupportProperty(property)     ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)this.GetProperty(property)
         )  :

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

      property==CANV_ELEMENT_PROP_SPLIT_CONTAINER_PANEL2_MIN_SIZE ?  CMessage::Text(MSG_CANV_ELEMENT_PROP_SPLIT_CONTAINER_PANEL2_MIN_SIZE)+
         (only_prop ? "" : !this.SupportProperty(property)     ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)this.GetProperty(property)
         )  :
      property==CANV_ELEMENT_PROP_TOOLTIP_INITIAL_DELAY ?  CMessage::Text(MSG_CANV_ELEMENT_PROP_TOOLTIP_INITIAL_DELAY)+
         (only_prop ? "" : !this.SupportProperty(property)     ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)this.GetProperty(property)
         )  :
      property==CANV_ELEMENT_PROP_TOOLTIP_AUTO_POP_DELAY ?  CMessage::Text(MSG_CANV_ELEMENT_PROP_TOOLTIP_AUTO_POP_DELAY)+
         (only_prop ? "" : !this.SupportProperty(property)     ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)this.GetProperty(property)
         )  :
      property==CANV_ELEMENT_PROP_TOOLTIP_RESHOW_DELAY ?  CMessage::Text(MSG_CANV_ELEMENT_PROP_TOOLTIP_RESHOW_DELAY)+
         (only_prop ? "" : !this.SupportProperty(property)     ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)this.GetProperty(property)
         )  :
      property==CANV_ELEMENT_PROP_TOOLTIP_SHOW_ALWAYS ?  CMessage::Text(MSG_CANV_ELEMENT_PROP_TOOLTIP_SHOW_ALWAYS)+
         (only_prop ? "" : !this.SupportProperty(property)     ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)this.GetProperty(property)
         )  :
      property==CANV_ELEMENT_PROP_TOOLTIP_ICON ?  CMessage::Text(MSG_CANV_ELEMENT_PROP_TOOLTIP_ICON)+
         (only_prop ? "" : !this.SupportProperty(property)     ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)this.GetProperty(property)
         )  :
      property==CANV_ELEMENT_PROP_TOOLTIP_IS_BALLOON ?  CMessage::Text(MSG_CANV_ELEMENT_PROP_TOOLTIP_IS_BALLOON)+
         (only_prop ? "" : !this.SupportProperty(property)     ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)this.GetProperty(property)
         )  :
      property==CANV_ELEMENT_PROP_TOOLTIP_USE_FADING ?  CMessage::Text(MSG_CANV_ELEMENT_PROP_TOOLTIP_USE_FADING)+
         (only_prop ? "" : !this.SupportProperty(property)     ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)this.GetProperty(property)
         )  :
      ""
     );
  }
//+------------------------------------------------------------------+


在返回元素字符串型属性说明的方法中,也要添加返回新字符串型属性说明的方法

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


由于我们现在已有了绘制标准箭头的方法,我们在 DrawArrow() 方法中添加它们的调用,以便在箭头按钮对象类中绘制箭头。

在 \MQL5\Include\DoEasy\Objects\Graph\WForms\Helpers\ArrowLeftButton.mqh 中:

//+-------------------------------------------------+
//| Draw the arrow                                  |
//+-------------------------------------------------+
void CArrowLeftButton::DrawArrow(void)
  {
   CGCnvElement::DrawArrowLeft(5,2,this.ArrowColor(),this.Opacity());
  }
//+------------------------------------------------------------------+


在 \MQL5\Include\DoEasy\Objects\Graph\WForms\Helpers\ArrowRightButton.mqh 中:

//+-------------------------------------------------+
//| Draw the arrow                                  |
//+-------------------------------------------------+
void CArrowRightButton::DrawArrow(void)
  {
   CGCnvElement::DrawArrowRight(6,2,this.ArrowColor(),this.Opacity());
  }
//+------------------------------------------------------------------+


在 \MQL5\Include\DoEasy\Objects\Graph\WForms\Helpers\ArrowUpButton.mqh 中:

//+-------------------------------------------------+
//| Draw the arrow                                  |
//+-------------------------------------------------+
void CArrowUpButton::DrawArrow(void)
  {
   CGCnvElement::DrawArrowUp(2,5,this.ArrowColor(),this.Opacity());
  }
//+------------------------------------------------------------------+


在 \MQL5\Include\DoEasy\Objects\Graph\WForms\Helpers\ArrowDownButton.mqh 中:

//+-------------------------------------------------+
//| Draw the arrow                                  |
//+-------------------------------------------------+
void CArrowDownButton::DrawArrow(void)
  {
   CGCnvElement::DrawArrowDown(2,6,this.ArrowColor(),this.Opacity());
  }
//+------------------------------------------------------------------+

如您所见,在每个类中,我们简单地调用来自基准图形元素类中的绘制所需箭头的方法,对应于类对象的用途。 如果类创建左箭头按钮,则调用左箭头绘制方法;对于右箭头按钮,调用右箭头绘制方法,依此类推。

我们开始开发工具提示对象。


ToolTip WinForms 对象类

在 \MQL5\Include\DoEasy\Objects\Graph\WForms\Common Controls\ 函数库文件夹,创建一个 CToolTip 类的新文件 ToolTip.mqh。 该类应派生自提示对象基类,而其文件应包含在创建的类文件之中

//+------------------------------------------------------------------+
//|                                                      ToolTip.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 "..\Helpers\HintBase.mqh"
//+------------------------------------------------------------------+
//| Class of the base Hint object of the WForms controls             |
//+------------------------------------------------------------------+
class CToolTip : public CHintBase
  {
  }


在类的私密部分中,声明方法,根据为其设置的文本,调整工具提示尺寸。 在类的受保护部分中,实现绘制不同类型的图标,并声明受保护类构造函数的方法:

class CToolTip : public CHintBase
  {
private:
//--- Adjust a tooltip size according to a text size
   void              CorrectSizeByTexts(void);
protected:
   //--- Draw (1) tooltip, (2) icon, (3) Info, (4) Warning, (5) Error and (6) User
   virtual void      DrawHint(const int shift);
   void              DrawIcon(void);
   void              DrawIconInfo(void)                  { CGCnvElement::DrawIconInfo(3,1,this.Opacity());                       }
   void              DrawIconWarning(void)               { CGCnvElement::DrawIconWarning(3,1,this.Opacity());                    }
   void              DrawIconError(void)                 { CGCnvElement::DrawIconError(3,1,this.Opacity());                      }
   virtual void      DrawIconUser(void)                  { return; }
//--- Protected constructor with object type, chart ID and subwindow
                     CToolTip(const ENUM_GRAPH_ELEMENT_TYPE type,
                              CGCnvElement *main_obj,CGCnvElement *base_obj,
                              const long chart_id,
                              const int subwindow,
                              const string descript,
                              const int x,
                              const int y,
                              const int w,
                              const int h);
public:

绘制信息、警告和出错图标的方法只是调用上面实现的基准图形元素类的相应方法绘制自定义图标的虚拟方法不执行任何操作。 它应在继承类中重新定义,以防我们需要独立更改此对象的功能。

在类的公开部分中,针对我今天为此对象添加的属性,实现设置和返回数值的方法。 我们声明参数化类构造函数,以及用于显示、重绘、清除、绘制边框、和初始化对象的方法:

public:
//--- (1) Set and (2) return the tooltip display delay
   void              SetInitialDelay(const long delay)   { this.SetProperty(CANV_ELEMENT_PROP_TOOLTIP_INITIAL_DELAY,delay);      }
   long              InitialDelay(void)                  { return this.GetProperty(CANV_ELEMENT_PROP_TOOLTIP_INITIAL_DELAY);     }
   
//--- (1) Set and (2) return the tooltip display duration
   void              SetAutoPopDelay(const long delay)   { this.SetProperty(CANV_ELEMENT_PROP_TOOLTIP_AUTO_POP_DELAY,delay);     }
   long              AutoPopDelay(void)                  { return this.GetProperty(CANV_ELEMENT_PROP_TOOLTIP_AUTO_POP_DELAY);    }
   
//--- (1) Set and (2) return the delay in a display of a new tooltip of one element
   void              SetReshowDelay(const long delay)    { this.SetProperty(CANV_ELEMENT_PROP_TOOLTIP_RESHOW_DELAY,delay);       }
   long              ReshowDelay(void)                   { return this.GetProperty(CANV_ELEMENT_PROP_TOOLTIP_RESHOW_DELAY);      }
   
//--- (1) Set and (2) return the flag for displaying a tooltip in an inactive window
   void              SetShowAlways(const bool flag)      { this.SetProperty(CANV_ELEMENT_PROP_TOOLTIP_SHOW_ALWAYS,flag);         }
   bool              ShowAlways(void)                    { return (bool)this.GetProperty(CANV_ELEMENT_PROP_TOOLTIP_SHOW_ALWAYS); }
   
//--- (1) Set and (2) return the type of the icon displayed in a tooltip
   void              SetIcon(const ENUM_CANV_ELEMENT_TOOLTIP_ICON ico)
                       { this.SetProperty(CANV_ELEMENT_PROP_TOOLTIP_ICON,ico);   }
   ENUM_CANV_ELEMENT_TOOLTIP_ICON Icon(void)
                       { return (ENUM_CANV_ELEMENT_TOOLTIP_ICON)this.GetProperty(CANV_ELEMENT_PROP_TOOLTIP_ICON); }
   
//--- (1) Set and (2) return the cloud tooltip flag
   void              SetBalloon(const bool flag)         { this.SetProperty(CANV_ELEMENT_PROP_TOOLTIP_IS_BALLOON,flag);          }
   bool              Balloon(void)                       { return (bool)this.GetProperty(CANV_ELEMENT_PROP_TOOLTIP_IS_BALLOON);  }
   
//--- (1) Set and (2) return the flag of fading when showing/hiding a tooltip
   void              SetUseFading(const bool flag)       { this.SetProperty(CANV_ELEMENT_PROP_TOOLTIP_USE_FADING,flag);          }
   bool              UseFading(void)                     { return (bool)this.GetProperty(CANV_ELEMENT_PROP_TOOLTIP_USE_FADING);  }
   
//--- (1) Set and (2) return the Tooltip title
   void              SetTitle(const string header);
   string            Title(void)                         { return this.GetProperty(CANV_ELEMENT_PROP_TOOLTIP_TITLE);             }
   
//--- (1,2) Set the Tooltip text
   virtual void      SetTooltipText(const string text);
   virtual void      SetText(const string text)          { this.SetTooltipText(text);                                            }
   
//--- Constructor
                     CToolTip(CGCnvElement *main_obj,CGCnvElement *base_obj,
                              const long chart_id,
                              const int subwindow,
                              const string descript,
                              const int x,
                              const int y,
                              const int w,
                              const int h);
//--- Display the element
   virtual void      Show(void);
//--- Redraw the object
   virtual void      Redraw(bool redraw);
//--- Clear the element filling it with color and opacity
   virtual void      Erase(const color colour,const uchar opacity,const bool redraw=false);
//--- Clear the element with a gradient fill
   virtual void      Erase(color &colors[],const uchar opacity,const bool vgradient,const bool cycle,const bool redraw=false);
//--- Draw the hint frame
   virtual void      DrawFrame(void);
//--- Initialize the variables
   virtual void      Initialize(void);
  };
//+------------------------------------------------------------------+

我们仔细看看声明的类方法。

指定对象类型、图表 ID 和子窗口的受保护构造函数:

//+------------------------------------------------------------------+
//| Protected constructor with an object type,                       |
//| chart ID and subwindow                                           |
//+------------------------------------------------------------------+
CToolTip::CToolTip(const ENUM_GRAPH_ELEMENT_TYPE type,
                   CGCnvElement *main_obj,CGCnvElement *base_obj,
                   const long chart_id,
                   const int subwindow,
                   const string descript,
                   const int x,
                   const int y,
                   const int w,
                   const int h) : CHintBase(type,main_obj,base_obj,chart_id,subwindow,descript,x,y,w,h)
  {
//--- Set the specified graphical element type for the object and assign the library object type to the current object
   this.SetTypeElement(type);
   this.m_type=OBJECT_DE_TYPE_GWF_COMMON;
   this.Initialize();
  }
//+------------------------------------------------------------------+

构造函数接收所创建图形元素的类型,以及在初始化清单中需设置到父类的其它对象参数。 在类主体中,设置传递给方法的图形元素类型,和函数库图形对象的类型调用设置所有默认对象参数的方法

参数型构造函数:

//+------------------------------------------------------------------+
//| Constructor indicating the main and base objects,                |
//| chart ID and subwindow                                           |
//+------------------------------------------------------------------+
CToolTip::CToolTip(CGCnvElement *main_obj,CGCnvElement *base_obj,
                   const long chart_id,
                   const int subwindow,
                   const string descript,
                   const int x,
                   const int y,
                   const int w,
                   const int h) : CHintBase(GRAPH_ELEMENT_TYPE_WF_TOOLTIP,main_obj,base_obj,chart_id,subwindow,descript,x,y,w,h)
  {
   this.SetTypeElement(GRAPH_ELEMENT_TYPE_WF_TOOLTIP);
   this.m_type=OBJECT_DE_TYPE_GWF_COMMON;
   this.Initialize();
  }
//+------------------------------------------------------------------+

这里一切都类似于受保护的构造函数,但图形元素类型被硬编码为 ToolTip


变量初始化方法:

//+-------------------------------------------------+
//| Initialize the variables                        |
//+-------------------------------------------------+
void CToolTip::Initialize(void)
  {
   this.SetBackgroundColor(CLR_DEF_CONTROL_HINT_BACK_COLOR,true);
   this.SetBorderColor(CLR_DEF_CONTROL_HINT_BORDER_COLOR,true);
   this.SetForeColor(CLR_DEF_CONTROL_HINT_FORE_COLOR,true);
   this.SetDisplayed(false);
   this.SetBorderSizeAll(1);
   this.SetBorderStyle(FRAME_STYLE_SIMPLE);
   this.SetShadow(true);
   this.DrawShadow(2,2,CLR_DEF_SHADOW_COLOR,CLR_DEF_SHADOW_OPACITY,DEF_SHADOW_BLUR);
   this.SetOpacity(255,false);
   this.SetTitle("");
   this.SetTooltipText("");
   this.SetInitialDelay(500);
   this.SetAutoPopDelay(5000);
   this.SetReshowDelay(100);
   this.SetShowAlways(false);
   this.SetIcon(CANV_ELEMENT_TOOLTIP_ICON_NONE);
   this.SetBalloon(false);
   this.SetUseFading(true);
   this.SetTextAlign(ANCHOR_LEFT_UPPER);
   this.SetTextAnchor(FRAME_ANCHOR_LEFT_TOP);
   this.SetFont(DEF_FONT,DEF_FONT_SIZE,FW_NORMAL);
   this.SetDisplayed(false);
   this.Hide();
  }
//+------------------------------------------------------------------+

工具提示对象在创建后应立即具有的所有属性,均在此处指定。


该方法重绘对象:

//+-------------------------------------------------+
//| Redraw the object                               |
//+-------------------------------------------------+
void CToolTip::Redraw(bool redraw)
  {
//--- If the element should not be displayed (hidden inside another control), leave
   if(!this.Displayed() || this.Text()=="")
      return;
//--- Get the "Shadow" object
   CShadowObj *shadow=this.GetShadowObj();
//--- If the redraw flag is set,
   if(redraw)
     {
      //--- completely redraw the object and save its new initial look
      this.Erase(this.m_array_colors_bg,this.Opacity(),this.m_gradient_v,this.m_gradient_c,true);
      this.Done();
      //--- If the object has a shadow and the "Shadow" object exists, redraw it
      if(this.IsShadow() && shadow!=NULL)
        {
         //--- remove the previously drawn shadow,
         shadow.Erase();
         //--- save the relative shadow coordinates,
         int x=shadow.CoordXRelative();
         int y=shadow.CoordYRelative();
         //--- redraw the shadow,
         shadow.Draw(0,0,shadow.Blur(),true);
         //--- restore relative shadow coordinates
         shadow.SetCoordXRelative(x);
         shadow.SetCoordYRelative(y);
        }
     }
//--- otherwise, erase the object and its shadow
   else
     {
      CGCnvElement::Erase();
      if(this.IsShadow() && shadow!=NULL)
         shadow.Erase();
     }
  }
//+------------------------------------------------------------------+

方法逻辑已在代码注释中讲述。 此处的所有内容几乎与基准 WinForms 对象 CWinFormBase 类中同名的方法完全相同,除了重绘绑定对象和图表。


该方法取背景色绘制元素来清除元素:

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

这些方法与其它函数库图形元素的同名方法雷同,特别是与父对象的方法。 如果此处不需要进一步的修改,则会从类中删除它们。

该方法绘制元素边框:

//+-------------------------------------------------+
//| Draw the element border                         |
//+-------------------------------------------------+
void CToolTip::DrawFrame(void)
  {
//--- If the element should not be displayed (hidden inside another control), leave
   if(!this.Displayed() || this.Text()=="")
      return;
//--- Draw a rectangle along the object edges
   this.DrawRectangle(0,0,this.Width()-1,this.Height()-1,this.BorderColor(),this.Opacity());
  }
//+------------------------------------------------------------------+

该方法的逻辑在代码中已有注释。 这里与父类的方法的区别仅在于检查是否需要显示元素。


该方法显示元素:

//+-------------------------------------------------+
//| Show the element                                |
//+-------------------------------------------------+
void CToolTip::Show(void)
  {
//--- If the element should not be displayed (hidden inside another control), leave
   if(!this.Displayed() || this.TooltipText()=="")
      return;
//--- Display the object
   CGCnvElement::Show();
//--- Get the "Shadow" object
   CShadowObj *shadow=this.GetShadowObj();
//--- If the object has a shadow and the "Shadow" object exists, display the shadow
   if(this.IsShadow() && shadow!=NULL)
     {
      shadow.Show();
      this.BringToTop();
     }
//--- Redraw the object
   this.Redraw(true);
  }
//+------------------------------------------------------------------+

该方法检查对象是否有阴影,若有,则还需显示它。 与此同时,在显示阴影后,对象本身应移动到前景,因为在显示阴影后,对象将在其下方。


该方法设置工具提示文本:

//+-------------------------------------------------+
//| Set the Tooltip text                            |
//+-------------------------------------------------+
void CToolTip::SetTooltipText(const string text)
  {
   CGCnvElement::SetTooltipText(text);
   CWinFormBase::SetText(text);
   this.CorrectSizeByTexts();
  }
//+------------------------------------------------------------------+

在此,工具提示文本首先设置到对象的新属性,然后将相同的文本也设置到 “object text” 属性,因为这些属性对于工具提示对象是相同的。 接下来,根据为其设置的标题和工具提示文本,调用调整对象尺寸的方法。


该方法设置工具提示的标题文本:

//+-------------------------------------------------+
//| Set the title text for Tooltip                  |
//+-------------------------------------------------+
void CToolTip::SetTitle(const string header)
  {
   this.SetProperty(CANV_ELEMENT_PROP_TOOLTIP_TITLE,header);
   this.CorrectSizeByTexts();
  }
//+------------------------------------------------------------------+

此处我们先把传递给方法的文本设置到对象的新字符串属性,然后调用对象尺寸调整方法。


该方法根据文本大小调整工具提示尺寸:

//+-------------------------------------------------+
//| Adjust tooltip size to fit texts                |
//+-------------------------------------------------+
void CToolTip::CorrectSizeByTexts(void)
  {
//--- If the tooltip text is not set for the object, leave
   if(this.TooltipText()=="" || this.TooltipText()==NULL)
      return;
//--- Declare variables for the width and height of the object
   int w=this.Width();
   int h=this.Height();
//--- Declare variables for the width and height of the title and tooltip texts
   int w1=0;
   int h1=0;
   int w2=w;
   int h2=h;
//--- If the header text is set, get and adjust its size
   if(this.Title()!="" && this.Title()!=NULL)
     {
      this.TextSize(this.Title(),w1,h1);
      if(w1<6)
         w1=6;
      if(h1<19)
         h1=19;
     }
//--- If the tooltip text is set, get and adjust its size
   this.TextSize(this.Text(),w2,h2);
   if(w2<6)
      w2=6;
   if(h2<19)
      h2=19;
//--- Calculate the total size of the tooltip
   w=fmax(w1,w2);
   h=h1+h2;
//--- Set the size of the object in accordance with the calculated ones
   this.Resize(w+12+(this.Icon()>CANV_ELEMENT_TOOLTIP_ICON_NONE && this.Icon()<=CANV_ELEMENT_TOOLTIP_ICON_USER ? 16 : 0),h,false);
  }
//+------------------------------------------------------------------+

方法逻辑已在代码注释中讲述。 简而言之,如果没有为对象设置工具提示文本,则不会显示工具提示。 无论对象是否设置了标题文本。 如果未显示工具提示,则无需更改其尺寸。 因此,在这种情况下,我们只需离开该方法即可。 如果设置了工具提示文本,那么我们需要获取标题和工具提示文本的尺寸。 如果生成的文本宽度小于 6,则对象的宽度将等于 6;如果高度小于 19,则对象的高度应为 19。 接下来,我们定义两个文本的最大宽度,并将其作为对象的宽度。 对象的高度等于两个文本的高度之和。 当调整尺寸时,我们要考虑从六个像素的左边缘开始的文本缩进。 相应地,右侧的缩进也应等于 12 个像素。 因此,我们在计算宽度时加上 16 个像素,并检查图标存在的条件。 如果需绘制图标,则图标的宽度要加上对象的 16 像素宽度。 结果就是,我们得到了对象的正确尺寸,其中含有两段文本 — 标题、描述和图标(如果有),看起来很和谐。


该方法绘制提示:

//+------------------------------------------------------------------+
//| Draw a hint                                                      |
//+------------------------------------------------------------------+
void CToolTip::DrawHint(const int shift)
  {
   int y=3;
   int x=6+(this.Icon()>CANV_ELEMENT_TOOLTIP_ICON_NONE ? 16 : 0);
   this.DrawIcon();
   if(this.Title()!="" && this.Title()!=NULL)
     {
      this.SetFont(DEF_FONT,DEF_FONT_SIZE,FW_BLACK);
      this.Text(x,y,this.Title(),this.ForeColor(),this.Opacity(),this.TextAnchor());
      this.SetFont(DEF_FONT,DEF_FONT_SIZE,FW_NORMAL);
      y+=this.TextHeight(this.Title())+4;
     }
   this.Text(x,y,this.Text(),this.ForeColor(),this.Opacity(),this.TextAnchor());
  }
//+------------------------------------------------------------------+

将文本的垂直缩进设置为 16 个像素,将水平缩进设置为 4 个像素。 如果要绘制图标,则将其 16 像素的宽度加到水平缩进中。 如果工具提示应具有标题,则将字体设置为粗体显示标题文本将字体宽度恢复为正常,并将标题文本的高度加上 4 个像素作为垂直坐标。 完成后,显示工具提示文本。 如果没有标题,则工具提示文本将显示在初始 Y 坐标处;如果有标题,则显示在计算出的 Y 坐标处。


绘制图标的方法:

//+-------------------------------------------------+
//| Draw an icon                                    |
//+-------------------------------------------------+
void CToolTip::DrawIcon(void)
  {
   switch(this.Icon())
     {
      case CANV_ELEMENT_TOOLTIP_ICON_INFO    :  this.DrawIconInfo();    break;
      case CANV_ELEMENT_TOOLTIP_ICON_WARNING :  this.DrawIconWarning(); break;
      case CANV_ELEMENT_TOOLTIP_ICON_ERROR   :  this.DrawIconError();   break;
      case CANV_ELEMENT_TOOLTIP_ICON_USER    :  this.DrawIconUser();    break;
      //--- Icon None
      default: break;
     }
  }
//+------------------------------------------------------------------+

取决于为对象设置的图标类型,我们调用绘制相应图像的方法。

对于目前,这就是这个类操作所需要的全部内容。 将来,我们会修改这个类,从而获得其操作所需的结果。

我们优化库类的其余部分,以便能够使用新对象。


在 \MQL5\Include\DoEasy\Objects\Graph\Form.mqh 窗体对象类中,即在公开部分中,声明设置显示非隐藏控件的标志,和设置元素不透明度的虚拟方法

//--- Update the coordinates (shift the canvas)
   virtual bool      Move(const int x,const int y,const bool redraw=false);
//--- Set the priority of a graphical object for receiving the event of clicking on a chart
   virtual bool      SetZorder(const long value,const bool only_prop);
//--- Set the object above all
   virtual void      BringToTop(void);
//--- Set the flag for displaying a non-hidden control
   virtual void      SetDisplayed(const bool flag);
//--- Set the element opacity
   virtual void      SetOpacity(const uchar value,const bool redraw=false);

//--- Event handler
   virtual void      OnChartEvent(const int id,const long& lparam,const double& dparam,const string& sparam);


在公开部分中,声明处理附加的工具提示对象,以及设置其文本的方法

//--- Add a new attached element
   bool              AddNewElement(CGCnvElement *obj,const int x,const int y);

//--- (1) Attach, return (2) the attached ToolTip object, (3) by description
   bool              AddTooltip(CForm *tooltip);
   CForm            *GetTooltip(void);
   CForm            *GetTooltipByDescription(const string descript);
//--- Set the text for Tooltip
   virtual void      SetTooltipText(const string text);

//--- Draw an object shadow
   void              DrawShadow(const int shift_x,const int shift_y,const color colour,const uchar opacity=127,const uchar blur=DEF_SHADOW_BLUR);


我们来修复创建新图形对象的方法:

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

如果未创建对象,则会在日志里显示记录。 接下来,调用未创建对象的指针,这可能会导致严重错误。 这就是为什么所创建对象设置参数的模块要包含在 else {} 中的原因

每个 WinForms 对象都应该能够把以前创建的工具提示对象的指针附加到自身。 为了令对象“理解”此类工具提示已附加于它,它应该依据工具提示对象中设置的说明来区分它们。 因此,我们就能够在容器对象中创建若干个工具提示对象,并为每个对象设置唯一的描述。 然后,我们可以获取指向附加于所创建工具提示对象的任何其它对象的指针,并通过其描述将所需的工具提示分配给该对象。 为此,每个 WinForms 对象都应该有方法,允许我们从外部附加具有 CForm 类型的对象及其后代。 我已经在窗体对象类中声明了这样的方法。 我们研究它们的实现。


该方法创建附加的 ToolTip 对象:

//+-------------------------------------------------+
//| Create the attached ToolTip object              |
//+-------------------------------------------------+
bool CForm::AddTooltip(CForm *tooltip)
  {
//--- If the pointer to an empty object is passed or the object type is not equal to Tooltip, report an error and return 'false'
   if(tooltip==NULL || tooltip.TypeGraphElement()!=GRAPH_ELEMENT_TYPE_WF_TOOLTIP)
     {
      CMessage::ToLog(DFUN,MSG_GRAPH_ELM_COLLECTION_ERR_EMPTY_OBJECT);
      return false;
     }
//--- If the list of attached objects already contains the Tooltip object with the same description as the object passed to the method - 
//--- inform of that in the journal and return 'false'
   if(this.GetTooltipByDescription(tooltip.Description())!=NULL)
     {
      ::Print(DFUN,this.TypeElementDescription()+": ",CMessage::Text(MSG_FORM_TOOLTIP_OBJ_ALREADY_EXISTS),": ",tooltip.Name(),", Description: \"",tooltip.Description(),"\"");
      return false;
     }
//--- If it was not possible to add the Tooltip object to the list of attached objects, report an error and return 'false'
   if(!this.m_list_elements.Add(tooltip))
     {
      ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_FAILED_OBJ_ADD_TO_LIST),": ",tooltip.NameObj());
      return false;
     }
//--- If the coordinates of the object added to the list are changed, set the Tooltip relative coordinates
   if(tooltip.Move(this.CoordX()+1,this.CoordY()+1))
     {
      tooltip.SetCoordXRelative(tooltip.CoordX()-this.CoordX());
      tooltip.SetCoordYRelative(tooltip.CoordY()-this.CoordY());
     }
//--- Set this object as the base object for the Tooltip object and return 'true'
   tooltip.SetBase(this.GetObject());
   return true;
  }
//+------------------------------------------------------------------+

代码注释中完整描述了方法逻辑。 把指向工具提示对象的指针传递给该方法。 如果已附加到该对象的此类型对象具有完全相同的描述,则我们认为这是相同的工具提示。 报告错误并返回 false。 接下来,我们将所附工具提示对象的指针传递给方法,即已附加对象的列表。 在这种情况下,对象本身可能不知道有关 CToolTip 类型的任何信息,但由于此类型是 CForm 类的继承者,因此应将其传递给具有 CForm 类型的方法。 接下来,在处理弹出对象的集合类中,我们可以从列表中获取此对象,并像处理 CToolTip 对象一样。 在方法的末尾,指向当前对象的指针将写入工具提示对象,从而它便成为工具提示对象的基础。 由于工具提示对象可以在其它对象中创建,然后附着于第三个对象,因此有关基准对象的记录,有助于我们判定将其分配给哪个对象。 基准对象是工具提示所分配的目的对象。


该方法返回附着的工具提示对象:

//+-------------------------------------------------+
//| Return the attached ToolTip object              |
//+-------------------------------------------------+
CForm *CForm::GetTooltip(void)
  {
   CForm *obj=NULL;
   for(int i=this.ElementsTotal()-1;i>WRONG_VALUE;i--)
     {
      obj=this.GetElement(i);
      if(obj!=NULL && obj.TypeGraphElement()==GRAPH_ELEMENT_TYPE_WF_TOOLTIP)
         break;
     }
   return obj;
  }
//+------------------------------------------------------------------+

循环遍历附着对象,我们依据循环索引获取下一个对象。 如果此对象不为空,且为 CToolTip 类型,则中断循环。 在循环结尾处,返回存储在 obj 变量中的指针。

可以为一个对象分配多个具有不同描述的工具提示。 该方法按说明返回附着的工具提示对象,从而获取指向所需工具提示对象的指针:

//+------------------------------------------------------------------+
//| Return the attached ToolTip object by description                |
//+------------------------------------------------------------------+
CForm *CForm::GetTooltipByDescription(const string descript)
  {
   CForm *obj=NULL;
   for(int i=this.ElementsTotal()-1;i>WRONG_VALUE;i--)
     {
      obj=this.GetElement(i);
      if(obj!=NULL && obj.TypeGraphElement()==GRAPH_ELEMENT_TYPE_WF_TOOLTIP && obj.Description()==descript)
         break;
     }
   return obj;
  }
//+------------------------------------------------------------------+

该方法接收说明文本,即需要按该文本查找工具提示对象。 循环遍历附加对象列表,我们得到下一个对象。 如果它不为空,其类型为 CToolTip,且描述等于所需的描述,则中断循环。 在循环结尾处,返回指向找到的(或空的)对象指针。

该方法设置工具提示文本:

//+-------------------------------------------------+
//| Set the Tooltip text                            |
//+-------------------------------------------------+
void CForm::SetTooltipText(const string text)
  {
   CGCnvElement::SetTooltipText(text);
   CForm *tooltip=this.GetTooltip();
   if(tooltip!=NULL)
      tooltip.SetTooltipText(text);
  }
//+------------------------------------------------------------------+

首先,我们将工具提示文本设置为此对象。 然后,我们获取指向附加工具提示对象的指针,并设置传递给该方法的工具提示文本。 如果对象只有一个工具提示,则该方法将为此对象和分配给它的工具提示设置相同的文本。

该虚拟方法设置显示非隐藏控件的标志:

//+------------------------------------------------------------------+
//| Set the flag for displaying a non-hidden control                 |
//+------------------------------------------------------------------+
void CForm::SetDisplayed(const bool flag)
  {
//--- Set the display flag for the object
   CGCnvElement::SetDisplayed(flag);
//--- If the shadow use flag is set and the shadow object has been created,
//--- set the display flag for the shadow object
   if(this.m_shadow && this.m_shadow_obj!=NULL)
      this.m_shadow_obj.SetDisplayed(flag);
  }
//+------------------------------------------------------------------+

如果对象可以拥有投射到其它对象上的阴影,则不仅需要为对象本身设置显示标志,还需要为阴影对象设置显示标志。 否则,无论什么样的对象,阴影都会“过自己的生活”。 因此,这里我们首先为对象本身设置显示标志。 之后,如果标志有阴影,亦需为其阴影设置。


该虚拟方法设置元素不透明度:

//+------------------------------------------------------------------+
//| Set the element opacity                                          |
//+------------------------------------------------------------------+
void CForm::SetOpacity(const uchar value,const bool redraw=false)
  {
//--- Set the object opacity value
   CGCnvElement::SetOpacity(value,redraw);
//--- If the shadow use flag is set and the shadow object has been created,
//--- set the opacity value for the shadow object
   if(this.m_shadow && this.m_shadow_obj!=NULL)
      this.m_shadow_obj.SetOpacity(value,redraw);
  }
//+------------------------------------------------------------------+

此处的一切都相同。 首先,我们为对象本身设置不透明度值。 如果存在阴影对象,亦为其设置不透明度值。


若要操控新控件,我们需要将其文件连接到窗体对象的类文件。

在 \MQL5\Include\DoEasy\Objects\Graph\WForms\Containers\Panel.mqh 中,包含 CToolTip 类文件

//+------------------------------------------------------------------+
//| Include files                                                    |
//+------------------------------------------------------------------+
#include "Container.mqh"
#include "..\Helpers\TabField.mqh"
#include "..\Helpers\ArrowUpButton.mqh"
#include "..\Helpers\ArrowDownButton.mqh"
#include "..\Helpers\ArrowLeftButton.mqh"
#include "..\Helpers\ArrowRightButton.mqh"
#include "..\Helpers\ArrowUpDownBox.mqh"
#include "..\Helpers\ArrowLeftRightBox.mqh"
#include "..\Helpers\HintMoveLeft.mqh"
#include "..\Helpers\HintMoveRight.mqh"
#include "..\Helpers\HintMoveUp.mqh"
#include "..\Helpers\HintMoveDown.mqh"
#include "GroupBox.mqh"
#include "TabControl.mqh"
#include "SplitContainer.mqh"
#include "..\..\WForms\Common Controls\ListBox.mqh"
#include "..\..\WForms\Common Controls\CheckedListBox.mqh"
#include "..\..\WForms\Common Controls\ButtonListBox.mqh"
#include "..\..\WForms\Common Controls\ToolTip.mqh"
//+------------------------------------------------------------------+
//| Panel object class of WForms controls                            |
//+------------------------------------------------------------------+


在创建新图形对象的方法中,添加创建新工具提示对象的代码

//+------------------------------------------------------------------+
//| Create a new graphical object                                    |
//+------------------------------------------------------------------+
CGCnvElement *CPanel::CreateNewGObject(const ENUM_GRAPH_ELEMENT_TYPE type,
                                       const int obj_num,
                                       const string descript,
                                       const int x,
                                       const int y,
                                       const int w,
                                       const int h,
                                       const color colour,
                                       const uchar opacity,
                                       const bool movable,
                                       const bool activity)
  {
   CGCnvElement *element=NULL;
   switch(type)
     {
      case GRAPH_ELEMENT_TYPE_ELEMENT                 : element=new CGCnvElement(type,this.GetMain(),this.GetObject(),this.ID(),obj_num,this.ChartID(),this.SubWindow(),descript,x,y,w,h,colour,opacity,movable,activity); break;
      case GRAPH_ELEMENT_TYPE_FORM                    : element=new CForm(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h);               break;
      case GRAPH_ELEMENT_TYPE_WF_CONTAINER            : element=new CContainer(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h);          break;
      case GRAPH_ELEMENT_TYPE_WF_GROUPBOX             : element=new CGroupBox(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h);           break;
      case GRAPH_ELEMENT_TYPE_WF_PANEL                : element=new CPanel(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h);              break;
      case GRAPH_ELEMENT_TYPE_WF_LABEL                : element=new CLabel(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h);              break;
      case GRAPH_ELEMENT_TYPE_WF_CHECKBOX             : element=new CCheckBox(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h);           break;
      case GRAPH_ELEMENT_TYPE_WF_RADIOBUTTON          : element=new CRadioButton(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h);        break;
      case GRAPH_ELEMENT_TYPE_WF_BUTTON               : element=new CButton(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h);             break;
      case GRAPH_ELEMENT_TYPE_WF_LIST_BOX             : element=new CListBox(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h);            break;
      case GRAPH_ELEMENT_TYPE_WF_LIST_BOX_ITEM        : element=new CListBoxItem(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h);        break;
      case GRAPH_ELEMENT_TYPE_WF_CHECKED_LIST_BOX     : element=new CCheckedListBox(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h);     break;
      case GRAPH_ELEMENT_TYPE_WF_BUTTON_LIST_BOX      : element=new CButtonListBox(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h);      break;
      case GRAPH_ELEMENT_TYPE_WF_TAB_HEADER           : element=new CTabHeader(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h);          break;
      case GRAPH_ELEMENT_TYPE_WF_TAB_FIELD            : element=new CTabField(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h);           break;
      case GRAPH_ELEMENT_TYPE_WF_TAB_CONTROL          : element=new CTabControl(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h);         break;
      case GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON         : element=new CArrowButton(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h);        break;
      case GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_UP      : element=new CArrowUpButton(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h);      break;
      case GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_DOWN    : element=new CArrowDownButton(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h);    break;
      case GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_LEFT    : element=new CArrowLeftButton(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h);    break;
      case GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_RIGHT   : element=new CArrowRightButton(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h);   break;
      case GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTONS_UD_BOX : element=new CArrowUpDownBox(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h);     break;
      case GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTONS_LR_BOX : element=new CArrowLeftRightBox(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h);  break;
      case GRAPH_ELEMENT_TYPE_WF_SPLIT_CONTAINER      : element=new CSplitContainer(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h);     break;
      case GRAPH_ELEMENT_TYPE_WF_SPLITTER             : element=new CSplitter(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h);           break;
      case GRAPH_ELEMENT_TYPE_WF_HINT_BASE            : element=new CHintBase(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h);           break;
      case GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_LEFT       : element=new CHintMoveLeft(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h);       break;
      case GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_RIGHT      : element=new CHintMoveRight(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h);      break;
      case GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_UP         : element=new CHintMoveUp(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h);         break;
      case GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_DOWN       : element=new CHintMoveDown(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h);       break;
      case GRAPH_ELEMENT_TYPE_WF_TOOLTIP              : element=new CToolTip(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h);            break;
      default  : break;
     }
   if(element==NULL)
      ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_FAILED_CREATE_ELM_OBJ),this.TypeElementDescription(type));
   return element;
  }
//+------------------------------------------------------------------+


在 \MQL5\Include\DoEasy\Objects\Graph\WForms\Containers\Container.mqh 中,即在为附加对象创建参数的方法中,添加为新创建的工具提示对象设置参数的代码模块

//+-------------------------------------------------+
//| 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_SPLIT_CONTAINER)
      obj.SetGroup(this.Group());
//--- Depending on the object type
   switch(obj.TypeGraphElement())
     {
      //--- For the Container, Panel and GroupBox WinForms objects

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

      //--- For ToolTip WinForms object
      case GRAPH_ELEMENT_TYPE_WF_TOOLTIP              :
        obj.SetBackgroundColor(CLR_DEF_CONTROL_HINT_BACK_COLOR,true);
        obj.SetBorderColor(CLR_DEF_CONTROL_HINT_BORDER_COLOR,true);
        obj.SetForeColor(CLR_DEF_CONTROL_HINT_FORE_COLOR,true);
        obj.SetBorderStyle(FRAME_STYLE_SIMPLE);
        obj.SetOpacity(0,false);
        obj.SetDisplayed(false);
        obj.Hide();
        break;
      default:
        break;
     }
   obj.Crop();
  }
//+------------------------------------------------------------------+


在 \MQL5\Include\DoEasy\Objects\Graph\WForms\Containers\GroupBox.mqh GroupBox 对象文件中,即在其创建新图形对象的方法中,添加创建工具提示对象的代码

//+-------------------------------------------------+
//| Create a new graphical object                   |
//+-------------------------------------------------+
CGCnvElement *CGroupBox::CreateNewGObject(const ENUM_GRAPH_ELEMENT_TYPE type,
                                          const int obj_num,
                                          const string descript,
                                          const int x,
                                          const int y,
                                          const int w,
                                          const int h,
                                          const color colour,
                                          const uchar opacity,
                                          const bool movable,
                                          const bool activity)
  {
   CGCnvElement *element=NULL;
   switch(type)
     {
      case GRAPH_ELEMENT_TYPE_ELEMENT                 : element=new CGCnvElement(type,this.GetMain(),this.GetObject(),this.ID(),obj_num,this.ChartID(),this.SubWindow(),descript,x,y,w,h,colour,opacity,movable,activity); break;
      case GRAPH_ELEMENT_TYPE_FORM                    : element=new CForm(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h);               break;

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

      case GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_UP         : element=new CHintMoveUp(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h);         break;
      case GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_DOWN       : element=new CHintMoveDown(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h);       break;
      case GRAPH_ELEMENT_TYPE_WF_TOOLTIP              : element=new CToolTip(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h);            break;
      default  : break;
     }
   if(element==NULL)
      ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_FAILED_CREATE_ELM_OBJ),this.TypeElementDescription(type));
   return element;
  }
//+------------------------------------------------------------------+


在 \MQL5\Include\DoEasy\Objects\Graph\WForms\Containers\TabControl.mqh TabControl 对象类文件中,我们需要添加滚动标题栏的箭头按钮对象的方法。 如果没有指向这些辅助对象的指针,我们将无法为它们分配工具提示。

在类的私密部分中,声明接收指向按钮对象指针的方法

//--- Return the list of (1) headers, (2) tab fields, the pointer to the (3) up-down and (4) left-right button objects
   CArrayObj        *GetListHeaders(void)          { return this.GetListElementsByType(GRAPH_ELEMENT_TYPE_WF_TAB_HEADER);        }
   CArrayObj        *GetListFields(void)           { return this.GetListElementsByType(GRAPH_ELEMENT_TYPE_WF_TAB_FIELD);         }
   CArrowUpDownBox  *GetArrUpDownBox(void)         { return this.GetElementByType(GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTONS_UD_BOX,0); }
   CArrowLeftRightBox *GetArrLeftRightBox(void)    { return this.GetElementByType(GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTONS_LR_BOX,0); }

//--- Return the pointer to the (1) up, (2) down, (3) left and (4) right arrow button
   CArrowUpButton   *GetArrowUpButton(void);
   CArrowDownButton *GetArrowDownButton(void);
   CArrowLeftButton *GetArrowLeftButton(void);
   CArrowRightButton*GetArrowRightButton(void);
   
//--- Return the pointer to the (1) last and (2) first visible tab header
   CTabHeader       *GetLastHeader(void)           { return this.GetTabHeader(this.TabPages()-1);                                }
   CTabHeader       *GetFirstVisibleHeader(void);
//--- Set the tab as selected


在公开部分中,声明为按钮对象的箭头设置工具提示的方法

//--- Set the tab specified by index to selected/not selected
   void              Select(const int index,const bool flag);
//--- Returns the (1) index, (2) the pointer to the selected tab
   int               SelectedTabPageNum(void)      const { return (int)this.GetProperty(CANV_ELEMENT_PROP_TAB_PAGE_NUMBER);}
   CWinFormBase     *SelectedTabPage(void)               { return this.GetTabField(this.SelectedTabPageNum());             }
   
//--- Add Tooltip to the (1) up, (2) down, (3) left and (4) right arrow button
   bool              AddTooltipToArrowUpButton(CForm *tooltip);
   bool              AddTooltipToArrowDownButton(CForm *tooltip);
   bool              AddTooltipToArrowLeftButton(CForm *tooltip);
   bool              AddTooltipToArrowRightButton(CForm *tooltip);
   
//--- Set the object above all
   virtual void      BringToTop(void);
//--- Show the control
   virtual void      Show(void);
//--- Event handler


在创建新图形对象的方法中,添加创建工具提示对象的代码

//+-------------------------------------------------+
//| Create a new graphical object                   |
//+-------------------------------------------------+
CGCnvElement *CTabControl::CreateNewGObject(const ENUM_GRAPH_ELEMENT_TYPE type,
                                         const int obj_num,
                                         const string descript,
                                         const int x,
                                         const int y,
                                         const int w,
                                         const int h,
                                         const color colour,
                                         const uchar opacity,
                                         const bool movable,
                                         const bool activity)
  {
   CGCnvElement *element=NULL;
   switch(type)
     {
      case GRAPH_ELEMENT_TYPE_WF_TAB_HEADER           : element=new CTabHeader(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h);          break;
      case GRAPH_ELEMENT_TYPE_WF_TAB_FIELD            : element=new CTabField(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h);           break;
      case GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTONS_UD_BOX : element=new CArrowUpDownBox(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h);     break;
      case GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTONS_LR_BOX : element=new CArrowLeftRightBox(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h);  break;
      case GRAPH_ELEMENT_TYPE_WF_TOOLTIP              : element=new CToolTip(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h);            break;
      default  : break;
     }
   if(element==NULL)
      ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_FAILED_CREATE_ELM_OBJ),this.TypeElementDescription(type));
   return element;
  }
//+------------------------------------------------------------------+


该方法返回箭头按钮的指针:

//+-------------------------------------------------+
//| Return the pointer to the up arrow button       |
//+-------------------------------------------------+
CArrowUpButton *CTabControl::GetArrowUpButton(void)
  {
   CArrowUpDownBox *box=this.GetArrUpDownBox();
   return(box!=NULL ? box.GetArrowUpButton() : NULL);
  }
//+-------------------------------------------------+
//| Return the pointer to the down arrow button     |
//+-------------------------------------------------+
CArrowDownButton *CTabControl::GetArrowDownButton(void)
  {
   CArrowUpDownBox *box=this.GetArrUpDownBox();
   return(box!=NULL ? box.GetArrowDownButton() : NULL);
  }
//+-------------------------------------------------+
//| Return the pointer to the left arrow button     |
//+-------------------------------------------------+
CArrowLeftButton *CTabControl::GetArrowLeftButton(void)
  {
   CArrowLeftRightBox *box=this.GetArrLeftRightBox();
   return(box!=NULL ? box.GetArrowLeftButton() : NULL);
  }
//+-------------------------------------------------+
//| Return the pointer to the right arrow button    |
//+-------------------------------------------------+
CArrowRightButton *CTabControl::GetArrowRightButton(void)
  {
   CArrowLeftRightBox *box=this.GetArrLeftRightBox();
   return(box!=NULL ? box.GetArrowRightButton() : NULL);
  }
//+------------------------------------------------------------------+

所有方法都相同。 首先,我们获取包含一对左右或上下按钮的对应的对象指针。 从这些对象中,我们接收并返回指向所需按钮的指针 — 向上、向下、向左或向右。


该方法为向上、向下、向左和向右箭头按钮添加工具提示对象:

//+-------------------------------------------------+
//| Add ToolTip to the up arrow button              |
//+-------------------------------------------------+
bool CTabControl::AddTooltipToArrowUpButton(CForm *tooltip)
  {
   CArrowUpButton *butt=this.GetArrowUpButton();
   return(butt!=NULL ? butt.AddTooltip(tooltip) : false);
  }
//+-------------------------------------------------+
//| Add ToolTip to the down arrow button            |
//+-------------------------------------------------+
bool CTabControl::AddTooltipToArrowDownButton(CForm *tooltip)
  {
   CArrowDownButton *butt=this.GetArrowDownButton();
   return(butt!=NULL ? butt.AddTooltip(tooltip) : false);
  }
//+-------------------------------------------------+
//| Add ToolTip to the left arrow button            |
//+-------------------------------------------------+
bool CTabControl::AddTooltipToArrowLeftButton(CForm *tooltip)
  {
   CArrowLeftButton *butt=this.GetArrowLeftButton();
   return(butt!=NULL ? butt.AddTooltip(tooltip) : false);
  }
//+-------------------------------------------------+
//| Add ToolTip to the right arrow button           |
//+-------------------------------------------------+
bool CTabControl::AddTooltipToArrowRightButton(CForm *tooltip)
  {
   CArrowRightButton *butt=this.GetArrowRightButton();
   return(butt!=NULL ? butt.AddTooltip(tooltip) : false);
  }
//+------------------------------------------------------------------+

这些方法彼此雷同。 首先,调用上述方法获取指向相应按钮的指针,然后设置指针指向工具提示对象,传递给方法,赋给接收的对象。 成功接收到指向按钮对象的指针,并将指针添加到工具提示对象后,该方法返回 true,否则返回 — false


在 \MQL5\Include\DoEasy\Objects\Graph\WForms\Helpers\TabField.mqh TabField 对象类文件中,添加创建新工具提示对象的代码到创建新图形对象的方法当中:

//+-------------------------------------------------+
//| Create a new graphical object                   |
//+-------------------------------------------------+
CGCnvElement *CTabField::CreateNewGObject(const ENUM_GRAPH_ELEMENT_TYPE type,
                                          const int obj_num,
                                          const string descript,
                                          const int x,
                                          const int y,
                                          const int w,
                                          const int h,
                                          const color colour,
                                          const uchar opacity,
                                          const bool movable,
                                          const bool activity)
  {
   CGCnvElement *element=NULL;
   switch(type)
     {
      case GRAPH_ELEMENT_TYPE_ELEMENT                 : element=new CGCnvElement(type,this.GetMain(),this.GetObject(),this.ID(),obj_num,this.ChartID(),this.SubWindow(),descript,x,y,w,h,colour,opacity,movable,activity); break;
      case GRAPH_ELEMENT_TYPE_FORM                    : element=new CForm(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h);               break;

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

      case GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_UP         : element=new CHintMoveUp(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h);         break;
      case GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_DOWN       : element=new CHintMoveDown(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h);       break;
      case GRAPH_ELEMENT_TYPE_WF_TOOLTIP              : element=new CToolTip(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h);            break;
      default  : break;
     }
   if(element==NULL)
      ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_FAILED_CREATE_ELM_OBJ),this.TypeElementDescription(type));
   return element;
  }
//+------------------------------------------------------------------+


在 \MQL5\Include\DoEasy\Objects\Graph\WForms\Containers\SplitContainer.mqh SplitContainer 对象类文件中,声明为隔板对象设置工具提示的方法:

//--- Return the pointer to the separator
   CSplitter        *GetSplitter(void)                         { return this.GetElementByType(GRAPH_ELEMENT_TYPE_WF_SPLITTER,0);                      }
//--- Return a pointer to the (1) "Left shift", (1) "Right shift", (1) "Up shift" and (1) "Down shift" hint objects
   CHintMoveLeft    *GetHintMoveLeft(void)                     { return this.GetElementByType(GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_LEFT,0);                }
   CHintMoveRight   *GetHintMoveRight(void)                    { return this.GetElementByType(GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_RIGHT,0);               }
   CHintMoveUp      *GetHintMoveUp(void)                       { return this.GetElementByType(GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_UP,0);                  }
   CHintMoveDown    *GetHintMoveDown(void)                     { return this.GetElementByType(GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_DOWN,0);                }
//--- Add Tooltip to the separator
   bool              AddTooltipToSplitter(CForm *tooltip);

//--- (1) set and (2) return the minimum possible size of the panel 1 and 2


在创建新图形对象的方法中,添加创建新工具提示对象的代码

//+------------------------------------------------------------------+
//| Create a new graphical object                                    |
//+------------------------------------------------------------------+
CGCnvElement *CSplitContainer::CreateNewGObject(const ENUM_GRAPH_ELEMENT_TYPE type,
                                                const int obj_num,
                                                const string descript,
                                                const int x,
                                                const int y,
                                                const int w,
                                                const int h,
                                                const color colour,
                                                const uchar opacity,
                                                const bool movable,
                                                const bool activity)
  {
   CGCnvElement *element=NULL;
   switch(type)
     {
      case GRAPH_ELEMENT_TYPE_WF_SPLIT_CONTAINER_PANEL: element=new CSplitContainerPanel(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h);break;
      case GRAPH_ELEMENT_TYPE_WF_SPLITTER             : element=new CSplitter(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h);           break;
      case GRAPH_ELEMENT_TYPE_WF_HINT_BASE            : element=new CHintBase(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h);           break;
      case GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_LEFT       : element=new CHintMoveLeft(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h);       break;
      case GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_RIGHT      : element=new CHintMoveRight(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h);      break;
      case GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_UP         : element=new CHintMoveUp(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h);         break;
      case GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_DOWN       : element=new CHintMoveDown(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h);       break;
      case GRAPH_ELEMENT_TYPE_WF_TOOLTIP              : element=new CToolTip(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h);            break;
      default  : break;
     }
   if(element==NULL)
      ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_FAILED_CREATE_ELM_OBJ),this.TypeElementDescription(type));
   return element;
  }
//+------------------------------------------------------------------+


该方法为隔板添加工具提示:

//+-------------------------------------------------+
//| Add ToolTip to the separator                    |
//+-------------------------------------------------+
bool CSplitContainer::AddTooltipToSplitter(CForm *tooltip)
  {
   CSplitter *obj=this.GetSplitter();
   return(obj!=NULL ? obj.AddTooltip(tooltip) : false);
  }
//+------------------------------------------------------------------+

在此,我们获取指向隔板对象的指针,将指向工具提示对象的指针添加到隔板对象,并返回结果。


在 \MQL5\Include\DoEasy\Objects\Graph\WForms\Helpers\SplitContainerPanel.mqh SplitContainerPanel 对象类文件中,即在创建新图形对象的方法中,添加创建新工具提示对象的代码

//+-------------------------------------------------+
//| Create a new graphical object                   |
//+-------------------------------------------------+
CGCnvElement *CSplitContainerPanel::CreateNewGObject(const ENUM_GRAPH_ELEMENT_TYPE type,
                                                     const int obj_num,
                                                     const string descript,
                                                     const int x,
                                                     const int y,
                                                     const int w,
                                                     const int h,
                                                     const color colour,
                                                     const uchar opacity,
                                                     const bool movable,
                                                     const bool activity)
  {
   CGCnvElement *element=NULL;
   switch(type)
     {
      case GRAPH_ELEMENT_TYPE_ELEMENT                 : element=new CGCnvElement(type,this.GetMain(),this.GetObject(),this.ID(),obj_num,this.ChartID(),this.SubWindow(),descript,x,y,w,h,colour,opacity,movable,activity); break;
      case GRAPH_ELEMENT_TYPE_FORM                    : element=new CForm(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h);               break;

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

      case GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_UP         : element=new CHintMoveUp(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h);         break;
      case GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_DOWN       : element=new CHintMoveDown(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h);       break;
      case GRAPH_ELEMENT_TYPE_WF_TOOLTIP              : element=new CToolTip(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h);            break;
      default  : break;
     }
   if(element==NULL)
      ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_FAILED_CREATE_ELM_OBJ),this.TypeElementDescription(type));
   return element;
  }
//+------------------------------------------------------------------+

现在,在每个容器对象中,我们可以创建一个新的工具提示对象,并将其分配给任何已有的图形元素。

为了处理工具提示对象,我们需要对图形元素集合类的 \MQL5\Include\DoEasy\Collections\GraphElementsCollection.mqh 进行修改。

在类的私密部分中,声明处理工具提示对象的方法

//--- 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);
//--- Handle Tooltip elements
   void              TooltipProcessing(CForm *form,CForm *tooltip,CForm *tooltip_prev);
//--- Add the element to the collection list
   bool AddCanvElmToCollection(CGCnvElement *element);


在搜索交互对象的方法中,添加跳过工具提示对象

//+-------------------------------------------------+
//| Search for interaction objects                  |
//+-------------------------------------------------+
CForm *CGraphElementsCollection::SearchInteractObj(CForm *form,const int id,const long &lparam,const double &dparam,const string &sparam)
  {
//--- If a non-empty pointer is passed
   if(form!=NULL)
     {
      //--- Create the list of interaction objects
      int total=form.CreateListInteractObj();
      //--- In the loop by the created list
      for(int i=total-1;i>WRONG_VALUE;i--)
        {
         //--- get the next form object
         CForm *obj=form.GetInteractForm(i);
         //--- If the object is received, but is not visible, or not active, or should not be displayed, skip it
         if(obj==NULL || !obj.IsVisible() || !obj.Enabled() || !obj.Displayed())
            continue;
         
         //--- If this is Tooltip, skip such an object for now
         if(obj.TypeGraphElement()==GRAPH_ELEMENT_TYPE_WF_TOOLTIP)
           {
            continue;
           }
         
         //--- If the form object is TabControl, return the selected tab under the cursor
         if(obj.TypeGraphElement()==GRAPH_ELEMENT_TYPE_WF_TAB_CONTROL)
           {
            //---...
            //---...

当我们寻找在光标下的图形元素,并遇到工具提示对象时,我们需要在此处跳过它,从而避免处理它。 当活动工具提示对象允许与鼠标交互时,则检查此类对象是否可与鼠标交互,替代跳过。 而在此时,我们于此跳过这些对象。


该方法处理工具提示元素:

//+-------------------------------------------------+
//| Handle ToolTip elements                         |
//+-------------------------------------------------+
void CGraphElementsCollection::TooltipProcessing(CForm *form,CForm *tooltip,CForm *tooltip_prev)
  {
//--- Get the chart width and height
   int w=(int)::ChartGetInteger(tooltip.ChartID(),CHART_WIDTH_IN_PIXELS,tooltip.SubWindow());
   int h=(int)::ChartGetInteger(tooltip.ChartID(),CHART_HEIGHT_IN_PIXELS,tooltip.SubWindow());
//--- Get cursor coordinates
   int x=this.m_mouse.CoordX();
   int y=this.m_mouse.CoordY();
//--- If at the current X coordinate (cursor) the tooltip goes beyond the right edge of the chart, adjust the X coordinate
   if(x+tooltip.Width()>w)
      x=w-tooltip.Width();
//--- If at the current Y coordinate (cursor) the tooltip goes beyond the bottom edge of the chart, adjust the Y coordinate
   if(y+tooltip.Height()>h)
      y=h-tooltip.Height();
//--- If the tooltip object is hidden
   if(!tooltip.IsVisible())
     {
      //--- If the tooltip object is shifted to the received cursor coordinates
      if(tooltip.Move(x,y))
        {
         //--- Get the object the tooltip object is assigned to
         CForm *base=tooltip.GetBase();
         //--- If the object is received, set new relative tooltip coordinates
         if(base!=NULL)
           {
            tooltip.SetCoordXRelative(tooltip.CoordX()-base.CoordX());
            tooltip.SetCoordYRelative(tooltip.CoordY()-base.CoordY());
           }
        }
     }
//--- Set the tooltip drawing flag, as well as full opacity, and display the object
   tooltip.SetDisplayed(true);
   tooltip.SetOpacity(255);
   tooltip.Show();
//--- If the previous tooltip object exists and its name is not equal to the name of the current object (it is the same object)
   if(tooltip_prev!=NULL && tooltip_prev.Name()!=tooltip.Name())
     {
      //--- Set the flag disabling the display, make the object completely transparent and hide it
      tooltip_prev.SetDisplayed(false);
      tooltip.SetOpacity(0);
      tooltip_prev.Hide();
     }
  }
//+------------------------------------------------------------------+

代码注释中完整描述了方法逻辑。 当我们获取光标下的对象时,我们检查是否曾为其设置了工具提示对象。 如果有,那么我们调用此方法来处理工具提示对象。 找到工具提示时,我们将对象本身传递给方法、指向对象工具提示的指针,以及指向以前处理过的工具提示对象的指针。


将以下代码模块插入到类的 OnChartEvent() 事件处理程序之中,即插入到处理对象移动标志删除情况的片段(光标在对象上,但尚未按下任何按钮):

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

            //--- If the move flag is disabled
            else
              {
               //--- Get the ToolTip object assigned to the form and declare the previous ToolTip
               CForm *tooltip=form.GetTooltip();
               static CForm *tooltip_prev=NULL;
               //--- If the ToolTip object is received
               if(tooltip!=NULL)
                 {
                  //--- and if its type is indeed ToolTip (to avoid incorrect casting, which we sometimes get if we do not check the type)
                  if(tooltip.TypeGraphElement()==GRAPH_ELEMENT_TYPE_WF_TOOLTIP)
                    {
                     //--- Get the base object set in ToolTip
                     CForm *base=tooltip.GetBase();
                     //--- If the base object is received
                     if(base!=NULL)
                       {
                        //--- and if the name of the base object is the same as the name of the current form (the ToolTip is bound to the form)
                        if(base.Name()==form.Name())
                          {
                           //--- Handle the received and previous ToolTip and write the current ToolTip to the variable as the previous one
                           this.TooltipProcessing(form,tooltip,tooltip_prev);
                           tooltip_prev=tooltip;
                          }
                       }
                    }
                 }
               //--- If there is no ToolTip attached to the object
               else
                 {
                  //--- If the previous ToolTip exists
                  if(tooltip_prev!=NULL)
                    {
                     //--- and if its type is indeed ToolTip (to avoid incorrect casting, which we sometimes get if we do not check the type)
                     if(tooltip_prev.TypeGraphElement()==GRAPH_ELEMENT_TYPE_WF_TOOLTIP)
                       {
                        //--- Set the object non-display flag, make it completely transparent and hide it
                        tooltip_prev.SetDisplayed(false);
                        tooltip_prev.SetOpacity(0,false);
                        tooltip_prev.Hide();
                       }
                    }
                 }
               //--- 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);
              }
           }
        }
      
      //--- If the cursor is not above the form
      if(form==NULL)
        {


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

注释中讲述了此代码模块的逻辑。 十分有趣的是,在终端更新后,逻辑条件,同时检查非NULL,和某些对象属性,并不总能正确处理。 故此,此处可以写在一段代码里的条件,被划分为括号 { } 中的若干模块。 我也许犯了错,但无论如何,在进一步开发工具提示对象类时,此代码模块将被重新设计。 在这种情况下,代码只允许我们找到设置工具提示的对象,并显示此工具提示,同时隐藏前一个工具提示。 换句话说,显示工具提示既没有延迟,也没有平滑的外观。 我将在后续文章中实现所有这些。

这些就是我现在需要做的一切。 我们测试一下结果。


测试

为了执行测试,我将取用上一篇文章中的 EA,并将其保存在 \MQL5\Experts\TestDoEasy\Part125\ 中,命名为 TestDoEasy125.mq5

往 EA 设置里添加两个新变量,来指定工具提示中使用的图标类型,并设置工具提示标题文本

//--- input parameters
sinput   bool                          InpMovable           =  true;                   // Panel Movable flag
sinput   ENUM_INPUT_YES_NO             InpAutoSize          =  INPUT_YES;              // Panel Autosize
sinput   ENUM_AUTO_SIZE_MODE           InpAutoSizeMode      =  AUTO_SIZE_MODE_GROW;    // Panel Autosize mode
sinput   ENUM_BORDER_STYLE             InpFrameStyle        =  BORDER_STYLE_SIMPLE;    // Label border style
sinput   ENUM_ANCHOR_POINT             InpTextAlign         =  ANCHOR_CENTER;          // Label text align
sinput   ENUM_INPUT_YES_NO             InpTextAutoSize      =  INPUT_NO;               // Label autosize
sinput   ENUM_ANCHOR_POINT             InpCheckAlign        =  ANCHOR_LEFT;            // Check flag align
sinput   ENUM_ANCHOR_POINT             InpCheckTextAlign    =  ANCHOR_LEFT;            // Check label text align
sinput   ENUM_CHEK_STATE               InpCheckState        =  CHEK_STATE_UNCHECKED;   // Check flag state
sinput   ENUM_INPUT_YES_NO             InpCheckAutoSize     =  INPUT_YES;              // CheckBox autosize
sinput   ENUM_BORDER_STYLE             InpCheckFrameStyle   =  BORDER_STYLE_NONE;      // CheckBox border style
sinput   ENUM_ANCHOR_POINT             InpButtonTextAlign   =  ANCHOR_CENTER;          // Button text align
sinput   ENUM_INPUT_YES_NO             InpButtonAutoSize    =  INPUT_YES;              // Button autosize
sinput   ENUM_AUTO_SIZE_MODE           InpButtonAutoSizeMode=  AUTO_SIZE_MODE_GROW;    // Button Autosize mode
sinput   ENUM_BORDER_STYLE             InpButtonFrameStyle  =  BORDER_STYLE_NONE;      // Button border style
sinput   bool                          InpButtonToggle      =  true ;                  // Button toggle flag
sinput   bool                          InpButtListMSelect   =  false;                  // ButtonListBox Button MultiSelect flag
sinput   bool                          InpListBoxMColumn    =  true;                   // ListBox MultiColumn flag
sinput   bool                          InpTabCtrlMultiline  =  false;                   // Tab Control Multiline flag
sinput   ENUM_ELEMENT_ALIGNMENT        InpHeaderAlignment   =  ELEMENT_ALIGNMENT_TOP;  // TabHeader Alignment
sinput   ENUM_ELEMENT_TAB_SIZE_MODE    InpTabPageSizeMode   =  ELEMENT_TAB_SIZE_MODE_FILL; // TabHeader Size Mode
sinput   int                           InpTabControlX       =  10;                     // TabControl X coord
sinput   int                           InpTabControlY       =  20;                     // TabControl Y coord
sinput   ENUM_CANV_ELEMENT_TOOLTIP_ICON InpTooltipIcon      =  CANV_ELEMENT_TOOLTIP_ICON_NONE;  // Tooltip Icon
sinput   string                        InpTooltipTitle      =  "";                     // Tooltip Title
//--- global variables


创建 TabControl 后,实现两个工具提示对象,并将它们分配给向左和向右滚动标题栏的两个按钮

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

         //--- Create TabControl
         pnl.CreateNewElement(GRAPH_ELEMENT_TYPE_WF_TAB_CONTROL,InpTabControlX,InpTabControlY,pnl.Width()-30,pnl.Height()-40,clrNONE,255,true,false);
         CTabControl *tc=pnl.GetElementByType(GRAPH_ELEMENT_TYPE_WF_TAB_CONTROL,0);
         if(tc!=NULL)
           {
            tc.SetTabSizeMode((ENUM_CANV_ELEMENT_TAB_SIZE_MODE)InpTabPageSizeMode);
            tc.SetAlignment((ENUM_CANV_ELEMENT_ALIGNMENT)InpHeaderAlignment);
            tc.SetMultiline(InpTabCtrlMultiline);
            tc.SetHeaderPadding(6,0);
            tc.CreateTabPages(15,0,56,20,TextByLanguage("Вкладка","TabPage"));
            
            //--- Create Tooltip for the Left button
            tc.CreateNewElement(GRAPH_ELEMENT_TYPE_WF_TOOLTIP,0,0,10,10,clrNONE,0,false,false);
            CToolTip *tooltip=tc.GetElementByType(GRAPH_ELEMENT_TYPE_WF_TOOLTIP,0);
            if(tooltip!=NULL)
              {
               tooltip.SetDescription("Left Button Tooltip");
               tooltip.SetIcon(InpTooltipIcon);
               tooltip.SetTitle(InpTooltipTitle);
               tooltip.SetTooltipText(TextByLanguage("Нажмите для прокрутки заголовков вправо","Click to scroll headings to the right"));
               tc.AddTooltipToArrowLeftButton(tooltip);
              }
            //--- Create Tooltip for the Right button
            tc.CreateNewElement(GRAPH_ELEMENT_TYPE_WF_TOOLTIP,0,0,10,10,clrNONE,0,false,false);
            tooltip=tc.GetElementByType(GRAPH_ELEMENT_TYPE_WF_TOOLTIP,1);
            if(tooltip!=NULL)
              {
               tooltip.SetDescription("Right Button Tooltip");
               tooltip.SetIcon(ENUM_CANV_ELEMENT_TOOLTIP_ICON(InpTooltipIcon+1));
               tooltip.SetTitle(InpTooltipTitle);
               tooltip.SetTooltipText(TextByLanguage("Нажмите для прокрутки заголовков влево","Click to scroll headings to the left"));
               tc.AddTooltipToArrowRightButton(tooltip);
              }
              
            //--- Create a text label with a tab description on each tab
            for(int j=0;j<tc.TabPages();j++)
              {
                //---...
                //---...

如您所见,创建 ToolTip 对象后,我们会立即为其分配说明、显示的图标、标题文本和工具提示,并将其添加到相应的按钮中。 对于第二个工具提示对象,将值指定为比设置中指定的图标多一个的图标,这样就可以同时查看两个图标。

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


将鼠标指针悬停在箭头按钮上方时,会立即显示工具提示。 每个对象都有它们自己的图标。 工具提示不会脱离屏幕 — 它们的坐标已正确调整。 现在,我们看到在箭头按钮上方绘制的新箭头作为标准图形基元绘制。 很自然,工具提示对象的行为是不正确的。 它们的正确行为将在以后实现。 该测试仅用于评估对象的外观。


下一步是什么?

在下一篇文章中,我将继续处理操控提示对象。

当前 MQL5 版本的函数库、测试 EA、和图表事件控制指标的所有文件均附于文后。

返回内容目录

*该系列的前几篇文章:

 
DoEasy. 控件 (第 20 部分): SplitContainer WinForms 对象
DoEasy. 控件 (第 21 部分): SplitContainer 控件 面板隔板
DoEasy. 控件 (第 22 部分): SplitContainer。 修改已创建对象的属性
DoEasy. 控件 (第 23 部分): 改进 TabControl 和 SplitContainer WinForms 对象
DoEasy. 控件 (第 24 部分): 提示(Hint)辅助 WinForms 对象

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

附加的文件 |
MQL5.zip (4557.73 KB)
数据科学与机器学习(第 09 部分):K-最近邻算法(KNN) 数据科学与机器学习(第 09 部分):K-最近邻算法(KNN)
这是一种惰性算法,它不是基于训练数据集学习,而是以存储数据集替代,并在给定新样本时立即采取行动。 尽管它很简单,但它能用于各种实际应用。
创建一个行情卷播面板:改进版 创建一个行情卷播面板:改进版
您如何看待复查我们的行情卷播面板基本版的主意? 我们改进面板要做的第一件事就是能够添加图像,例如资产徽标或其它图像,从而用户可以迅速、轻松地识别所示品种。
您应该知道的 MQL5 向导技术(第 04 部分):线性判别分析 您应该知道的 MQL5 向导技术(第 04 部分):线性判别分析
今天的交易者都是哲学家,几乎总是在寻找新的想法,尝试提炼它们,选择修改或丢弃它们:一个探索性的过程,肯定会花费相当的勤奋程度。 这些系列文章将提出 MQL5 向导应该是交易者在此领域努力的中流砥柱。
创建一个行情卷播面板:基本版 创建一个行情卷播面板:基本版
在此,我将展示如何创建价格播报屏幕,它通常在交易所滚动显示报价。 我会只用 MQL5 来做到这一点,无需复杂的外部编程。