DoEasy. 控件 (第 26 部分): 完成 ToolTip(工具提示)WinForms 对象,并转移至 ProgressBar(进度条)开发
内容
概述
在上一篇文章中,我启动 ToolTip(工具提示)控件的开发。 当我们将鼠标悬停在对象上方时,工具提示应在短暂停顿后出现。 而之前显示的工具提示应隐藏。 如果我们在工具提示出现后将光标停留在相同位置,那么它应该在一段时间后消失。 在上一篇文章中,鼠标悬停在分配给它的对象上后,我令工具提示立即出现。 在本文中,我将继续研究工具提示的行为,并令其与 MS Visual Studio 中编译的程序的行为更加一致。 将鼠标悬停在对象上方时,应暂停,之后工具提示才会平滑地显示在屏幕上。 如果我们从对象中删除光标,则工具提示将被隐藏。 将光标停留在对象上方时,工具提示也会在一段时间后平滑消失。 为了实现此行为,我们需要用到为图形元素集合创建的计时器。 每个对象都将定义一个虚拟计时器事件处理程序。 计时器的实现应在那些需要专门处理的对象类中执行。
为了让函数库明白哪些对象应该在计时器中处理,哪些不应该处理,我将创建一个活动对象列表。 它将包含需要在计时器中处理的对象指针。 这样可以更轻松地跟踪我们需要处理的对象,并避免我们在循环中不断扫描所有图形元素。 循环仅针对被添加到列表中的对象。 这种方式令图形界面的动态化成为可能。 对象不仅会响应与鼠标的交互,还会按照为它们指定的动画序列来达到视觉效果。 以这种方式,我们可以轻松地创建和使用普通的动画图标。 只需创建一个带有动画帧的对象,并将其放置在活动函数库对象列表中就足够了,而在此类对象的计时器事件处理程序中,我们简单地交替显示在对象构造期间创建的动画帧。 今天,我就为工具提示控件创建一个计时器事件处理程序。 此处理程序将实现工具提示的上述行为。 由于此行为意味着与鼠标的交互,因此我们将在图形元素集合的事件处理程序和集合计时器中操控它们。
完成工具提示 WinForms 对象之后,我就会着手开发进度条控件。 我只创建对象的静态版本 — 它的属性和外观。 其操作将在下一篇文章中实现。
改进库类
在 \MQL5\Include\DoEasy\Defines.mqh 中,为画布上的图形元素添加计时器参数:
//--- Parameters of the graphical objects collection timer #define COLLECTION_GRAPH_OBJ_PAUSE (250) // Graphical objects collection timer pause in milliseconds #define COLLECTION_GRAPH_OBJ_COUNTER_STEP (16) // Graphical objects timer counter increment #define COLLECTION_GRAPH_OBJ_COUNTER_ID (10) // Graphical objects timer counter ID //--- Parameters of the timer for the collection of graphical elements on canvas #define COLLECTION_GRAPH_ELM_PAUSE (16) // Graphical elements collection timer pause in milliseconds #define COLLECTION_GRAPH_ELM_COUNTER_STEP (16) // Graphical elements timer counter increment #define COLLECTION_GRAPH_ELM_COUNTER_ID (11) // Graphical elements timer counter ID //--- Collection list IDs
每个函数库对象集合都有自己的计时器处理程序,其中拥有自己的一组参数 — 暂停、计时器计数器增量、及其 ID。 我已在此为新计时器添加了完全相同的参数。
为新的进度条控件添加默认值:
#define CLR_DEF_CONTROL_HINT_BACK_COLOR (C'0xFF,0xFF,0xE1') // Hint control background color #define CLR_DEF_CONTROL_HINT_BORDER_COLOR (C'0x76,0x76,0x76') // Hint control frame color #define CLR_DEF_CONTROL_HINT_FORE_COLOR (C'0x5A,0x5A,0x5A') // Hint control text color #define CLR_DEF_CONTROL_PROGRESS_BAR_BACK_COLOR (C'0xF0,0xF0,0xF0') // ProgressBar control background color #define CLR_DEF_CONTROL_PROGRESS_BAR_BORDER_COLOR (C'0xBC,0xBC,0xBC') // ProgressBar control frame color #define CLR_DEF_CONTROL_PROGRESS_BAR_FORE_COLOR (C'0x00,0x78,0xD7') // ProgressBar control text color #define CLR_DEF_CONTROL_PROGRESS_BAR_BAR_COLOR (C'0x06,0xB0,0x25') // ProgressBar control progress line color
某些图形控件需要支持其视觉效果的元素。 例如,进度线是沿条状运行的眩光,如同它在 Windows 中的实现。 实现眩光的对象将叠加在条形图的顶部,并沿其移动。 我们需要在函数库对象类型列表中设置新的对象类型:
//+------------------------------------------------+ //| List of library object types | //+------------------------------------------------+ enum ENUM_OBJECT_DE_TYPE { //--- Graphics OBJECT_DE_TYPE_GBASE = COLLECTION_ID_LIST_END+1, // "Base object of all library graphical objects" object type OBJECT_DE_TYPE_GELEMENT, // "Graphical element" object type OBJECT_DE_TYPE_GFORM, // Form object type OBJECT_DE_TYPE_GFORM_CONTROL, // "Form for managing pivot points of graphical object" object type OBJECT_DE_TYPE_GSHADOW, // Shadow object type OBJECT_DE_TYPE_GGLARE, // Glare object type //--- WinForms OBJECT_DE_TYPE_GWF_BASE, // WinForms Base object type (base abstract WinForms object) OBJECT_DE_TYPE_GWF_CONTAINER, // WinForms container object type OBJECT_DE_TYPE_GWF_COMMON, // WinForms standard control object type OBJECT_DE_TYPE_GWF_HELPER, // WinForms auxiliary control object type //--- Animation
将画布上的新图形元素类型添加到图形元素类型列表当中:
//+------------------------------------------------+ //| 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_GLARE_OBJ, // Glare 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 GRAPH_ELEMENT_TYPE_WF_PROGRESS_BAR, // Windows Forms ProgressBar //--- 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 GRAPH_ELEMENT_TYPE_WF_BAR_PROGRESS_BAR, // Windows Forms BarProgressBar }; //+------------------------------------------------------------------+
控件(此处为工具提示)在与鼠标交互时可以具有多种状态。 由于对象是动画,因此应描述其行为和状态。 它也许正等待淡入开始、处于淡入状态、等待淡出开始、处于淡出状态、或者也许处于正常状态。
我们在新控件显示状态列表中描述其状态:
//+------------------------------------------------+ //| 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 }; //+------------------------------------------------+ //| List of control display states | //+------------------------------------------------+ enum ENUM_CANV_ELEMENT_DISPLAY_STATE { CANV_ELEMENT_DISPLAY_STATE_NORMAL, // Normal CANV_ELEMENT_DISPLAY_STATE_WAITING_FADE_IN, // Wait for fading in CANV_ELEMENT_DISPLAY_STATE_PROCESS_FADE_IN, // Fading in CANV_ELEMENT_DISPLAY_STATE_COMPLETED_FADE_IN, // Fading in end CANV_ELEMENT_DISPLAY_STATE_WAITING_FADE_OUT, // Wait for fading out CANV_ELEMENT_DISPLAY_STATE_PROCESS_FADE_OUT, // Fading out CANV_ELEMENT_DISPLAY_STATE_COMPLETED_FADE_OUT, // Fading out end CANV_ELEMENT_DISPLAY_STATE_COMPLETED, // Complete processing }; //+------------------------------------------------------------------+
此类状态亦可应用于其它控件。 以后可以为它们创建动画序列。 此外,对象不必淡入或淡出。 对象淡入的描述可以等同于展开下拉列表,而逐渐淡出(衰减)的状态 — 等同于折叠状态。 不过,我们以后始终能扩展此列表。
进度条可以有三种进度绘制样式。 我们在枚举中列出它们:
//+------------------------------------------------+ //| List of ProgressBar element styles | //+------------------------------------------------+ enum ENUM_CANV_ELEMENT_PROGRESS_BAR_STYLE { CANV_ELEMENT_PROGRESS_BAR_STYLE_BLOCKS, // Segmented blocks CANV_ELEMENT_PROGRESS_BAR_STYLE_CONTINUOUS, // Continuous bar CANV_ELEMENT_PROGRESS_BAR_STYLE_MARQUEE, // Continuous scrolling }; //+------------------------------------------------------------------+ //| Integer properties of the graphical element on the canvas | //+------------------------------------------------------------------+
我在此只实现连续进度条。
如果为对象实现了动画显示,则应在其属性中保存和获取其状态。
我们将新的图形元素属性添加到画布上图形元素的整数型属性枚举之中,并将整数型属性的总数从 129 增加到 138:
//+------------------------------------------------------------------+ //| 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_BORDER_TOP_AREA_WIDTH, // Upper edge area width CANV_ELEMENT_PROP_DISPLAYED, // Non-hidden control display flag CANV_ELEMENT_PROP_DISPLAY_STATE, // Control display state CANV_ELEMENT_PROP_DISPLAY_DURATION, // Control display duration CANV_ELEMENT_PROP_GROUP, // Group the graphical element belongs to CANV_ELEMENT_PROP_ZORDER, // Priority of a graphical object for receiving the event of clicking on a chart //---... //---... CANV_ELEMENT_PROP_TOOLTIP_IS_BALLOON, // Tooltip in the form of a "cloud" CANV_ELEMENT_PROP_TOOLTIP_USE_FADING, // Fade when showing/hiding a tooltip CANV_ELEMENT_PROP_PROGRESS_BAR_MAXIMUM, // The upper bound of the range ProgressBar operates in CANV_ELEMENT_PROP_PROGRESS_BAR_MINIMUM, // The lower bound of the range ProgressBar operates in CANV_ELEMENT_PROP_PROGRESS_BAR_STEP, // ProgressBar increment needed to redraw it CANV_ELEMENT_PROP_PROGRESS_BAR_STYLE, // ProgressBar style CANV_ELEMENT_PROP_PROGRESS_BAR_VALUE, // Current ProgressBar value from Min to Max CANV_ELEMENT_PROP_PROGRESS_BAR_MARQUEE_ANIM_SPEED, // Progress bar animation speed in case of Marquee style }; #define CANV_ELEMENT_PROP_INTEGER_TOTAL (138) // Total number of integer properties #define CANV_ELEMENT_PROP_INTEGER_SKIP (0) // Number of integer properties not used in sorting //+------------------------------------------------------------------+
将新属性添加到画布上图形元素可能的排序条件枚举之中:
//+------------------------------------------------------------------+ //| Possible sorting criteria of graphical elements on the canvas | //+------------------------------------------------------------------+ #define FIRST_CANV_ELEMENT_DBL_PROP (CANV_ELEMENT_PROP_INTEGER_TOTAL-CANV_ELEMENT_PROP_INTEGER_SKIP) #define FIRST_CANV_ELEMENT_STR_PROP (CANV_ELEMENT_PROP_INTEGER_TOTAL-CANV_ELEMENT_PROP_INTEGER_SKIP+CANV_ELEMENT_PROP_DOUBLE_TOTAL-CANV_ELEMENT_PROP_DOUBLE_SKIP) enum ENUM_SORT_CANV_ELEMENT_MODE { //--- Sort by integer properties SORT_BY_CANV_ELEMENT_ID = 0, // Sort by element ID SORT_BY_CANV_ELEMENT_TYPE, // Sort by graphical element type //---... //---... SORT_BY_CANV_ELEMENT_BORDER_TOP_AREA_WIDTH, // Sort by upper edge area width SORT_BY_CANV_ELEMENT_DISPLAYED, // Sort by non-hidden control display flag SORT_BY_CANV_ELEMENT_DISPLAY_STATE, // Sort by control display state SORT_BY_CANV_ELEMENT_DISPLAY_DURATION, // Sort by control display duration SORT_BY_CANV_ELEMENT_GROUP, // Sort by a group the graphical element belongs to SORT_BY_CANV_ELEMENT_ZORDER, // Sort by the priority of a graphical object for receiving the event of clicking on a chart //---... //---... SORT_BY_CANV_ELEMENT_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_CANV_ELEMENT_PROGRESS_BAR_MAXIMUM, // Sort by the upper bound of the range ProgressBar operates in SORT_BY_CANV_ELEMENT_PROGRESS_BAR_MINIMUM, // Sort by the lower bound of the range ProgressBar operates in SORT_BY_CANV_ELEMENT_PROGRESS_BAR_STEP, // Sort by ProgressBar increment needed to redraw it SORT_BY_CANV_ELEMENT_PROGRESS_BAR_STYLE, // Sort by ProgressBar style SORT_BY_CANV_ELEMENT_PROGRESS_BAR_VALUE, // Sort by the current ProgressBar value from Min to Max SORT_BY_CANV_ELEMENT_PROGRESS_BAR_MARQUEE_ANIM_SPEED, // Sort by progress bar animation speed in case of Marquee style //--- 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 element header SORT_BY_CANV_ELEMENT_TOOLTIP_TEXT, // Sort by Tooltip element text }; //+------------------------------------------------------------------+
现在我们就能够按新属性针对所有图形元素进行排序和选择。
在 \MQL5\Include\DoEasy\Data.mqh 里,添加新的消息索引:
MSG_LIB_TEXT_CHEK_STATE_UNCHECKED, // Unchecked MSG_LIB_TEXT_CHEK_STATE_CHECKED, // Checked MSG_LIB_TEXT_CHEK_STATE_INDETERMINATE, // Undefined MSG_LIB_TEXT_ICON_NONE, // None MSG_LIB_TEXT_ICON_INFO, // Info MSG_LIB_TEXT_ICON_WARNING, // Warning MSG_LIB_TEXT_ICON_ERROR, // Error MSG_LIB_TEXT_ICON_USER, // User MSG_LIB_TEXT_STYLE_BLOCKS, // Segmented blocks MSG_LIB_TEXT_STYLE_CONTINUOUS, // Continuous bar MSG_LIB_TEXT_STYLE_MARQUEE, // Continuous scrolling MSG_LIB_TEXT_SUNDAY, // Sunday MSG_LIB_TEXT_MONDAY, // Monday
...
MSG_GRAPH_ELEMENT_TYPE_ELEMENT, // Element MSG_GRAPH_ELEMENT_TYPE_SHADOW_OBJ, // Shadow object MSG_GRAPH_ELEMENT_TYPE_GLARE_OBJ, // Glare object MSG_GRAPH_ELEMENT_TYPE_FORM, // Form MSG_GRAPH_ELEMENT_TYPE_WINDOW, // Window
...
MSG_GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_DOWN, // HintMoveLeft control MSG_GRAPH_ELEMENT_TYPE_WF_TOOLTIP, // ToolTip control MSG_GRAPH_ELEMENT_TYPE_WF_BAR_PROGRESS_BAR, // BarProgressBar control MSG_GRAPH_ELEMENT_TYPE_WF_PROGRESS_BAR, // ProgressBar control //--- MSG_GRAPH_ELEMENT_TYPE_WF_WRONG_TYPE_PASSED, // Incorrect control type MSG_GRAPH_ELEMENT_TYPE_WF_CANT_ADD_2_TOOLTIP, // Unable to add two or more ToolTips to the same 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
...
//--- CGraphElementsCollection MSG_GRAPH_OBJ_FAILED_GET_ADDED_OBJ_LIST, // Failed to get the list of newly added objects MSG_GRAPH_OBJ_FAILED_GET_ACTIVE_OBJ_LIST, // Failed to get the list of active elements MSG_GRAPH_OBJ_FAILED_GET_OBJECT_NAMES, // Failed to get object names MSG_GRAPH_OBJ_FAILED_DETACH_OBJ_FROM_LIST, // Failed to remove a graphical object from the list
...
MSG_CANV_ELEMENT_PROP_BORDER_TOP_AREA_WIDTH, // Upper edge area width MSG_CANV_ELEMENT_PROP_DISPLAYED, // Non-hidden control display flag MSG_CANV_ELEMENT_PROP_DISPLAY_STATE, // Control display state MSG_CANV_ELEMENT_PROP_DISPLAY_DURATION, // Control display duration MSG_CANV_ELEMENT_PROP_ENABLED, // Element availability flag MSG_CANV_ELEMENT_PROP_FORE_COLOR, // Default text color for all control objects
...
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 MSG_CANV_ELEMENT_PROP_PROGRESS_BAR_MAXIMUM, // The upper bound of the range ProgressBar operates in MSG_CANV_ELEMENT_PROP_PROGRESS_BAR_MINIMUM, // The lower bound of the range ProgressBar operates in MSG_CANV_ELEMENT_PROP_PROGRESS_BAR_STEP, // ProgressBar increment needed to redraw it MSG_CANV_ELEMENT_PROP_PROGRESS_BAR_STYLE, // ProgressBar style MSG_CANV_ELEMENT_PROP_PROGRESS_BAR_VALUE, // Current ProgressBar value from Min to Max MSG_CANV_ELEMENT_PROP_PROGRESS_BAR_MARQUEE_ANIM_SPEED,// Progress bar animation speed in case of Marquee style //--- 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 }; //+------------------------------------------------------------------+
及与新增索引相对应的消息:
{"Не установлен","Unchecked"}, {"Установлен","Checked"}, {"Неопределённый","Indeterminate"}, {"None","None"}, {"Info","Info"}, {"Warning","Warning"}, {"Error","Error"}, {"Пользовательский","User"}, {"Сегментированные блоки","Blocks"}, {"Непрерывная полоса","Continuous"}, {"Непрерывная прокрутка","Marquee"}, {"Воскресение","Sunday"}, {"Понедельник","Monday"},
...
{"Элемент","Element"}, {"Объект тени","Shadow object"}, {"Объект блика","Glare object"}, {"Форма","Form"}, {"Окно","Window"},
...
{"Элемент управления \"HintMoveDown\"","Control element \"HintMoveDown\""}, {"Элемент управления \"ToolTip\"","Control element \"ToolTip\""}, {"Элемент управления BarProgressBar","Control element \"BarProgressBar\""}, {"Элемент управления ProgressBar","Control element \"ProgressBar\""}, {"Передан не правильный тип элемента управления","Wrong control type passed"}, {"Нельзя к одному элементу управления добавить два и более ToolTip","Can't add two or more ToolTips to one control"}, {"Графический объект принадлежит программе","The graphic object belongs to the program"}, {"Графический объект не принадлежит программе","The graphic object does not belong to the program"},
...
//--- CGraphElementsCollection {"Не удалось получить список вновь добавленных объектов","Failed to get the list of newly added objects"}, {"Не удалось получить список активных элементов","Failed to get list of active elements"}, {"Не удалось получить имена объектов","Failed to get object names"}, {"Не удалось изъять графический объект из списка","Failed to detach graphic object from the list"},
...
{"Ширина области верхней грани","Width of the top border area"}, {"Флаг отображения не скрытого элемента управления","Flag that sets the display of a non-hidden control"}, {"Состояние отображения элемента управления","Display state of the control"}, {"Продолжительность процесса отображения элемента управления","Duration of the process of displaying the control"}, {"Флаг доступности элемента","Element Availability Flag"}, {"Цвет текста по умолчанию для всех объектов элемента управления","Default text color for all objects in the control"},
...
{"Подсказка в форме \"облачка\"","Tooltip as \"Balloon\""}, {"Угасание при отображении и скрытии подсказки","Tooltip uses fading"}, {"Верхняя граница диапазона, в котором действует ProgressBar","Upper bound of the range in which the ProgressBar operates"}, {"Нижняя граница диапазона, в котором действует ProgressBar","Lower bound of the range in which the ProgressBar operates"}, {"Величина приращения значения ProgressBar для его перерисовки","Increment value of the ProgressBar to redraw it"}, {"Стиль элемента ProgressBar","Progress Bar element style"}, {"Текущее начение элемента ProgressBar в диапазоне от Min до Max","Current value of the ProgressBar in the range from Min to Max"}, {"Скорость анимации полосы прогресса при стиле Marquee","Marquee style progress bar animation speed"}, //--- 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"}, }; //+---------------------------------------------------------------------+
创建对象集合时,我们使用一个列表类,该类派生自 CObject 类及其后代实例指针的动态数组。 CArrayObj 类拥有 Detach() 方法,其从列表中检索指针,并返回获取的指针。 从列表中提取指针之后,我们不需要再用到指针。
因此,在 \MQL5\Include\DoEasy\Collections\ListObj.mqh 的派生类中,创建从列表中删除指针,且无需返回指针的方法:
//+------------------------------------------------------------------+ //| ListObj.mqh | //| Copyright 2019, MetaQuotes Software Corp. | //| https://mql5.com/en/users/artmedia70 | //+------------------------------------------------------------------+ #property copyright "Copyright 2019, MetaQuotes Software Corp." #property link "https://mql5.com/en/users/artmedia70" #property version "1.00" //+------------------------------------------------+ //| Include files | //+------------------------------------------------+ #include <Arrays\ArrayObj.mqh> //+------------------------------------------------+ //| Class of collection lists | //+------------------------------------------------+ class CListObj : public CArrayObj { private: int m_type; // List type public: bool DetachElement(const int index) { CObject *obj=CArrayObj::Detach(index); if(obj==NULL) return false; obj=NULL; return true; } void Type(const int type) { this.m_type=type; } virtual int Type(void) const { return(this.m_type); } CListObj() { this.m_type=0x7778; } }; //+------------------------------------------------------------------+
在此,我们简单地从列表中提取指针。 如果执行失败,则返回 false。
成功提取后,重置生成的指针,并返回 true。
我们稍微修改一下 \MQL5\Include\DoEasy\Services\Pause.mqh 中的暂停对象类。
添加返回暂停倒计时开始的公开方法,以所传递的自系统启动以来经历的毫秒数表示:
//--- Return (1) the time passed from the countdown start in milliseconds, (2) waiting completion flag, //--- (3) pause countdown start time, (4) pause in milliseconds, (5) countdown start ulong Passed(void) const { return this.TickCount()-this.m_start; } bool IsCompleted(void) const { return this.Passed()>this.m_wait_msc; } ulong TimeBegin(void) const { return this.m_time_begin; } ulong TimeWait(void) const { return this.m_wait_msc; } ulong CountdownStart(void) const { return this.m_start; }
在计时器中处理控件时,我们需要该方法。
由于我添加了新的图形元素整数型属性,我们需要在图形元素的基准对象中初始化它们,并在对象结构里添加新的字段。
在 \MQL5\Include\DoEasy\Objects\Graph\GCnvElement.mqh 中,将新字段加入结构:
private: int m_shift_coord_x; // Offset of the X coordinate relative to the base object int m_shift_coord_y; // Offset of the Y coordinate relative to the base object struct SData { //--- Object integer properties int id; // Element ID int type; // Graphical element type //---... //---... int visible_area_h; // Visibility scope height bool displayed; // Non-hidden control display flag int display_state; // Control display state long display_duration; // Control display duration int split_container_fixed_panel; // Panel that retains its size when the container is resized bool split_container_splitter_fixed; // Separator moveability flag //---... //---... //---... //---... int border_right_area_width; // Right edge area width int border_top_area_width; // Upper edge area width //--- Object real properties //--- Object string properties uchar name_obj[64]; // Graphical element object name uchar name_res[64]; // Graphical resource name uchar text[256]; // Graphical element text uchar descript[256]; // Graphical element description }; SData m_struct_obj; // Object structure uchar m_uchar_array[]; // uchar array of the object structure
我们需要对象的结构,以便正确将对象保存到文件中,以及随后恢复它。
在访问对象属性的简化方法模块中,设置处理这些新属性的方法:
//--- (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); } //--- (1) Set and (2) return the control display status void SetDisplayState(const ENUM_CANV_ELEMENT_DISPLAY_STATE state) { this.SetProperty(CANV_ELEMENT_PROP_DISPLAY_STATE,state); } ENUM_CANV_ELEMENT_DISPLAY_STATE DisplayState(void) const { return (ENUM_CANV_ELEMENT_DISPLAY_STATE)this.GetProperty(CANV_ELEMENT_PROP_DISPLAY_STATE); } //--- (1) Set and (2) return the control display duration void SetDisplayDuration(const long value) { this.SetProperty(CANV_ELEMENT_PROP_DISPLAY_DURATION,value); } long DisplayDuration(void) const { return this.GetProperty(CANV_ELEMENT_PROP_DISPLAY_DURATION); } //--- (1) Set and (2) return the graphical element type void SetTypeElement(const ENUM_GRAPH_ELEMENT_TYPE type) { CGBaseObj::SetTypeElement(type); this.SetProperty(CANV_ELEMENT_PROP_TYPE,type); } ENUM_GRAPH_ELEMENT_TYPE TypeGraphElement(void) const { return (ENUM_GRAPH_ELEMENT_TYPE)this.GetProperty(CANV_ELEMENT_PROP_TYPE); }
属性 setter 赋值方法把传递给它们的值设置到对象属性当中,并从方法返回对象属性以前的设置值。
在两个构造函数中设置新属性的默认值:
//+------------------------------------------------------------------+ //| 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_VISIBLE_AREA_HEIGHT,h); // Visibility scope height this.SetProperty(CANV_ELEMENT_PROP_DISPLAYED,true); // Non-hidden control display flag this.SetProperty(CANV_ELEMENT_PROP_DISPLAY_STATE,CANV_ELEMENT_DISPLAY_STATE_NORMAL);// Control display state this.SetProperty(CANV_ELEMENT_PROP_DISPLAY_DURATION,DEF_CONTROL_PROCESS_DURATION); // Control display duration this.SetProperty(CANV_ELEMENT_PROP_CONTROL_AREA_X,0); // Control area X coordinate this.SetProperty(CANV_ELEMENT_PROP_CONTROL_AREA_Y,0); // Control area Y coordinate //---... //---... this.SetProperty(CANV_ELEMENT_PROP_CONTROL_AREA_HEIGHT,0); // Control area height this.SetProperty(CANV_ELEMENT_PROP_SCROLL_AREA_X_RIGHT,0); // Right scroll area X coordinate 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_VISIBLE_AREA_HEIGHT,h); // Visibility scope height this.SetProperty(CANV_ELEMENT_PROP_DISPLAYED,true); // Non-hidden control display flag this.SetProperty(CANV_ELEMENT_PROP_DISPLAY_STATE,CANV_ELEMENT_DISPLAY_STATE_NORMAL);// Control display state this.SetProperty(CANV_ELEMENT_PROP_DISPLAY_DURATION,DEF_CONTROL_PROCESS_DURATION); // Control display duration this.SetProperty(CANV_ELEMENT_PROP_CONTROL_AREA_X,0); // Control area X coordinate this.SetProperty(CANV_ELEMENT_PROP_CONTROL_AREA_Y,0); // Control area Y coordinate //---... //---... this.SetProperty(CANV_ELEMENT_PROP_CONTROL_AREA_HEIGHT,0); // Control area height this.SetProperty(CANV_ELEMENT_PROP_SCROLL_AREA_X_RIGHT,0); // Right scroll area X coordinate 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()); } } //+------------------------------------------------------------------+
在创建对象结构的方法中,添加将对象属性值写入到结构字段:
//+------------------------------------------------+ //| Create the object structure | //+------------------------------------------------+ bool CGCnvElement::ObjectToStruct(void) { //--- Save integer properties this.m_struct_obj.id=(int)this.GetProperty(CANV_ELEMENT_PROP_ID); // Element ID this.m_struct_obj.type=(int)this.GetProperty(CANV_ELEMENT_PROP_TYPE); // Graphical element type //---... //---... this.m_struct_obj.visible_area_h=(int)this.GetProperty(CANV_ELEMENT_PROP_VISIBLE_AREA_HEIGHT); // Visibility scope height this.m_struct_obj.displayed=(bool)this.GetProperty(CANV_ELEMENT_PROP_DISPLAYED); // Flag for displaying a non-hidden control this.m_struct_obj.display_state=(int)this.GetProperty(CANV_ELEMENT_PROP_DISPLAY_STATE); // Control display state this.m_struct_obj.display_duration=this.GetProperty(CANV_ELEMENT_PROP_DISPLAY_DURATION); // Control display duration this.m_struct_obj.zorder=this.GetProperty(CANV_ELEMENT_PROP_ZORDER); // Priority of a graphical object for receiving the on-chart mouse click event this.m_struct_obj.enabled=(bool)this.GetProperty(CANV_ELEMENT_PROP_ENABLED); // Element availability flag //---... //---... this.m_struct_obj.fore_color=(color)this.GetProperty(CANV_ELEMENT_PROP_FORE_COLOR); // Default text color for all control objects this.m_struct_obj.fore_color_opacity=(uchar)this.GetProperty(CANV_ELEMENT_PROP_FORE_COLOR_OPACITY); // Opacity of the default text color for all control objects //---... //---... this.m_struct_obj.border_right_area_width=(int)this.GetProperty(CANV_ELEMENT_PROP_BORDER_RIGHT_AREA_WIDTH); // Right edge area width this.m_struct_obj.border_top_area_width=(int)this.GetProperty(CANV_ELEMENT_PROP_BORDER_TOP_AREA_WIDTH); // Top edge area width //--- Save real properties //--- Save string properties ::StringToCharArray(this.GetProperty(CANV_ELEMENT_PROP_NAME_OBJ),this.m_struct_obj.name_obj); // Graphical element object name ::StringToCharArray(this.GetProperty(CANV_ELEMENT_PROP_NAME_RES),this.m_struct_obj.name_res); // Graphical resource name ::StringToCharArray(this.GetProperty(CANV_ELEMENT_PROP_TEXT),this.m_struct_obj.text); // Graphical element text ::StringToCharArray(this.GetProperty(CANV_ELEMENT_PROP_DESCRIPTION),this.m_struct_obj.descript);// Graphical element description //--- Save the structure to the uchar array ::ResetLastError(); if(!::StructToCharArray(this.m_struct_obj,this.m_uchar_array)) { CMessage::ToLog(DFUN,MSG_LIB_SYS_FAILED_SAVE_OBJ_STRUCT_TO_UARRAY,true); return false; } return true; } //+------------------------------------------------------------------+
在创建对象结构的方法中,实现从结构字段赋值到对象属性:
//+------------------------------------------------+ //| Create the object from the structure | //+------------------------------------------------+ void CGCnvElement::StructToObject(void) { //--- Save integer properties this.SetProperty(CANV_ELEMENT_PROP_ID,this.m_struct_obj.id); // Element ID this.SetProperty(CANV_ELEMENT_PROP_TYPE,this.m_struct_obj.type); // Graphical element type //---... //---... this.SetProperty(CANV_ELEMENT_PROP_VISIBLE_AREA_HEIGHT,this.m_struct_obj.visible_area_h); // Visibility scope height this.SetProperty(CANV_ELEMENT_PROP_DISPLAYED,this.m_struct_obj.displayed); // Non-hidden control display flag this.SetProperty(CANV_ELEMENT_PROP_DISPLAY_STATE,this.m_struct_obj.display_state); // Control display state this.SetProperty(CANV_ELEMENT_PROP_DISPLAY_DURATION,this.m_struct_obj.display_duration); // Control display duration this.SetProperty(CANV_ELEMENT_PROP_ZORDER,this.m_struct_obj.zorder); // Priority of a graphical object for receiving the event of clicking on a chart this.SetProperty(CANV_ELEMENT_PROP_ENABLED,this.m_struct_obj.enabled); // Element availability flag //---... //---... this.SetProperty(CANV_ELEMENT_PROP_BACKGROUND_COLOR_OPACITY,this.m_struct_obj.background_color_opacity); // Element opacity this.SetProperty(CANV_ELEMENT_PROP_BACKGROUND_COLOR_MOUSE_DOWN,this.m_struct_obj.background_color_mouse_down); // Control background color when clicking on the control //---... //---... this.SetProperty(CANV_ELEMENT_PROP_BORDER_BOTTOM_AREA_WIDTH,this.m_struct_obj.border_bottom_area_width); // Bottom edge area width this.SetProperty(CANV_ELEMENT_PROP_BORDER_RIGHT_AREA_WIDTH,this.m_struct_obj.border_right_area_width); // Right edge area width this.SetProperty(CANV_ELEMENT_PROP_BORDER_TOP_AREA_WIDTH,this.m_struct_obj.border_top_area_width); // Top edge area width //--- Save real properties //--- Save string properties this.SetProperty(CANV_ELEMENT_PROP_NAME_OBJ,::CharArrayToString(this.m_struct_obj.name_obj)); // Graphical element object name this.SetProperty(CANV_ELEMENT_PROP_NAME_RES,::CharArrayToString(this.m_struct_obj.name_res)); // Graphical resource name this.SetProperty(CANV_ELEMENT_PROP_TEXT,::CharArrayToString(this.m_struct_obj.text)); // Graphical element text this.SetProperty(CANV_ELEMENT_PROP_DESCRIPTION,::CharArrayToString(this.m_struct_obj.descript));// Graphical element description } //+------------------------------------------------------------------+
在 \MQL5\Include\DoEasy\Objects\Graph\GBaseObj.mqh 所有函数库图形对象的基准对象类中,将返回新对象类型的描述添加到返回图形元素类型描述的方法之中:
//+------------------------------------------------------------------+ //| Return the description of the graphical element type | //+------------------------------------------------------------------+ string CGBaseObj::TypeElementDescription(const ENUM_GRAPH_ELEMENT_TYPE type) { return ( type==GRAPH_ELEMENT_TYPE_STANDARD ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_STANDARD) : type==GRAPH_ELEMENT_TYPE_STANDARD_EXTENDED ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_STANDARD_EXTENDED) : type==GRAPH_ELEMENT_TYPE_ELEMENT ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_ELEMENT) : type==GRAPH_ELEMENT_TYPE_SHADOW_OBJ ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_SHADOW_OBJ) : type==GRAPH_ELEMENT_TYPE_GLARE_OBJ ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_GLARE_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) : type==GRAPH_ELEMENT_TYPE_WF_PROGRESS_BAR ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_PROGRESS_BAR) : //--- 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) : type==GRAPH_ELEMENT_TYPE_WF_BAR_PROGRESS_BAR ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_BAR_PROGRESS_BAR) : "Unknown" ); } //+------------------------------------------------------------------+
我已声明了所有这些对象,但我稍后再创建它们 — 在完成 ToolTip 对象类之后。
为了操控图形元素,我们需要创建一个计时器事件处理程序。 由于所有图形元素都继承自窗体对象类,故我将在该类中声明虚拟计时器处理程序。
在 \MQL5\Include\DoEasy\Objects\Graph\Form.mqh 的公开部分中,编写一个虚拟计时器事件处理程序:
//--- Event handler virtual void OnChartEvent(const int id,const long& lparam,const double& dparam,const string& sparam); //--- Mouse event handler virtual void OnMouseEvent(const int id,const long& lparam,const double& dparam,const string& sparam); //--- Last mouse event handler virtual void OnMouseEventPostProcessing(void); //--- Timer virtual void OnTimer(void) { return; }
该处理程序未执行任何操作。 在需要处理计时器事件的类中重新定义它。
声明返回附加到元素的工具提示对象数量的方法:
//--- Add a new attached element bool AddNewElement(CGCnvElement *obj,const int x,const int y); //--- (1) Bind the ToolTip object to the element bool AddTooltip(CForm *tooltip); //--- Return (1) the number of ToolTip objects, (2) bound ToolTip object, (3) by description int ToolTipTotal(void); CForm *GetTooltip(void); CForm *GetTooltipByDescription(const string descript); //--- Set the text for Tooltip virtual void SetTooltipText(const string text);
一个工具提示对象只能被分配给一个图形对象。 但我们可以将若干个工具提示附加到一个对象,并将创建的工具提示分配给附于它的其它对象。 允许这样做是因为并非所有函数库对象都可以将工具提示对象附于自身。 但我们始终可以在对象附于的容器中创建工具提示,然后再将工具提示分配给这些对象。 该方法允许我们查找创建并附于容器的工具提示对象的数量。
我们来优化将指定的工具提示对象绑定到对象的方法。
添加其它无效类型,并且检查附加的工具提示数量是否大于零:
//+------------------------------------------------------------------+ //| Assign the specified ToolTip object to an 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) { CMessage::ToLog(DFUN,MSG_GRAPH_ELM_COLLECTION_ERR_EMPTY_OBJECT); return false; } //--- If a pointer to an object whose type is not equal to Tooltip is passed, report an error and return 'false' if(tooltip.TypeGraphElement()!=GRAPH_ELEMENT_TYPE_WF_TOOLTIP) { CMessage::ToLog(DFUN,MSG_GRAPH_ELEMENT_TYPE_WF_WRONG_TYPE_PASSED); 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 the list of attached objects already contains the Tooltip object, report this to the log and return 'false' if(this.ToolTipTotal()>0) { ::Print(DFUN,CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_CANT_ADD_2_TOOLTIP)); 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(),this.CoordY())) { 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。
该方法返回工具提示对象数量:
//+------------------------------------------------+ //| Return the number of ToolTip objects | //+------------------------------------------------+ int CForm::ToolTipTotal(void) { int res=0; for(int i=this.ElementsTotal()-1;i>WRONG_VALUE;i--) { CGCnvElement *obj=this.GetElement(i); if(obj!=NULL && obj.TypeGraphElement()==GRAPH_ELEMENT_TYPE_WF_TOOLTIP) res++; } return res; } //+------------------------------------------------------------------+
如果此对象是一个容器,则可将任意数量的工具提示对象附于该容器。 该方法返回其编号。 按附于对象的总数循环,获取下一个对象。 如果对象类型是工具提示,则增加对象计数器(res 变量)。 在循环结束时,返回设置在 res 中的计算结果。
图形元素集合计时器每 16 毫秒更新一次。 为了令工具提示淡入或淡出,譬如说一秒,我们需要取 1000 毫秒除以计时器更新周期。 结果就是,我们需要大约每 1000/16=62.5 毫秒更改一次对象的不透明度。
在 \MQL5\Include\DoEasy\Objects\Graph\WForms\Common Controls\ToolTip.mqh 中,声明变量存储透明度更改的步数:
//+------------------------------------------------------------------+ //| Class of the base Hint object of the WForms controls | //+------------------------------------------------------------------+ class CToolTip : public CHintBase { private: int m_step; // Transparency change step //--- Adjust a tooltip size according to a text size void CorrectSizeByTexts(void); protected:
在类的公开部分中,声明虚拟计时器事件处理程序:
//--- 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); //--- Timer virtual void OnTimer(void); }; //+------------------------------------------------------------------+
在每个构造函数中,根据淡入/淡出持续时间的默认值,和图形元素集合计时器的计数器步数,计算存储透明度更改步数变量的值:
//+------------------------------------------------+ //| 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(); this.m_step=(int)::floor(DEF_CONTROL_PROCESS_DURATION/COLLECTION_GRAPH_ELM_COUNTER_STEP); } //+------------------------------------------------------------------+ //| 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(); this.m_step=(int)::floor(DEF_CONTROL_PROCESS_DURATION/COLLECTION_GRAPH_ELM_COUNTER_STEP); } //+------------------------------------------------------------------+
在变量初始化方法中,输入更改工具提示对象状态延迟的默认值:
//+------------------------------------------------+ //| 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(DEF_CONTROL_TOOLTIP_INITIAL_DELAY); this.SetAutoPopDelay(DEF_CONTROL_TOOLTIP_AUTO_POP_DELAY); this.SetReshowDelay(DEF_CONTROL_TOOLTIP_RESHOW_DELAY); 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(); } //+------------------------------------------------------------------+
在显示工具提示的方法中,将对象显示命令移动到末尾,并删除整个对象的彻底重绘:
//+------------------------------------------------+ //| 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); } //+------------------------------------------------------------------+
现在,方法如下所示:
//+------------------------------------------------+ //| 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; //--- 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(); } //--- Display the object CGCnvElement::Show(); } //+------------------------------------------------------------------+
这种改进略微减轻了元素在逐渐改变其不透明度时令人不快的“闪烁”。
在工具提示方法中,在末尾添加更新修改后的画布:
//+------------------------------------------------+ //| 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()); this.Update(); } //+------------------------------------------------------------------+
当把不透明度改为零(对象完全透明)时,我注意到抗绘制锯齿基元方法的奇怪行为 — 它们对透明度值的反应很差劲。 对于非常低的不透明度值,此类方法绘制的线条在不透明度值设置为完全相同的背景对象上对比过度。 添加画布更新指示线是减少这种烦人效果的尝试之一。 但是,它尚未见效...
计时器事件处理程序:
//+------------------------------------------------+ //| Timer | //+------------------------------------------------+ void CToolTip::OnTimer(void) { //--- If the object is in the normal state (hidden) if(this.DisplayState()==CANV_ELEMENT_DISPLAY_STATE_NORMAL) { //--- set the state of waiting for the object to fade in //--- set the waiting duration and set the countdown time this.SetDisplayState(CANV_ELEMENT_DISPLAY_STATE_WAITING_FADE_IN); this.m_pause.SetWaitingMSC(this.InitialDelay()); this.m_pause.SetTimeBegin(); return; } //--- If the object is in the state of waiting for fading in if(this.DisplayState()==CANV_ELEMENT_DISPLAY_STATE_WAITING_FADE_IN) { //--- If the waiting time has not yet passed, leave if(this.m_pause.Passed()<this.InitialDelay()) return; //--- Set the state of the object fading in and //--- the process start countdown time this.SetDisplayState(CANV_ELEMENT_DISPLAY_STATE_PROCESS_FADE_IN); this.m_pause.SetTimeBegin(); return; } //--- If the object is in the state of fading in if(this.DisplayState()==CANV_ELEMENT_DISPLAY_STATE_PROCESS_FADE_IN) { //--- If the object is not completely opaque yet if(this.Opacity()<255) { //--- set the display flag and show the object, //--- calculate the required opacity value at this timer step, //--- set the calculated opacity to the properties and redraw the object this.SetDisplayed(true); if(this.Opacity()==0) this.Show(); uchar value=(this.Opacity()+(uchar)this.m_step<255 ? this.Opacity()+(uchar)this.m_step : 255); this.SetOpacity(value); this.Redraw(true); return; } //--- Set the end state of fading in this.SetDisplayState(CANV_ELEMENT_DISPLAY_STATE_COMPLETED_FADE_IN); } //--- If the object is in the state of completion of fading in if(this.DisplayState()==CANV_ELEMENT_DISPLAY_STATE_COMPLETED_FADE_IN) { //--- set the state of waiting for object fading out, //--- set the waiting duration and set the countdown time this.SetDisplayState(CANV_ELEMENT_DISPLAY_STATE_WAITING_FADE_OUT); this.m_pause.SetWaitingMSC(this.AutoPopDelay()); this.m_pause.SetTimeBegin(); return; } //--- If the object is in the state of waiting for fading out if(this.DisplayState()==CANV_ELEMENT_DISPLAY_STATE_WAITING_FADE_OUT) { //--- If the waiting time has not yet passed, leave if(this.m_pause.Passed()<this.AutoPopDelay()) return; //--- Set the state of the object fading out and //--- the process start countdown time this.SetDisplayState(CANV_ELEMENT_DISPLAY_STATE_PROCESS_FADE_OUT); this.m_pause.SetTimeBegin(); return; } //--- If the object is in the state of fading out if(this.DisplayState()==CANV_ELEMENT_DISPLAY_STATE_PROCESS_FADE_OUT) { //--- If the object is not completely transparent yet if(this.Opacity()>0) { //--- set the display flag, //--- calculate the required opacity value at this timer step, //--- set the calculated opacity to the properties and redraw the object //this.SetDisplayed(true); uchar value=(this.Opacity()-(uchar)this.m_step>0 ? this.Opacity()-(uchar)this.m_step : 0); this.SetOpacity(value); this.Redraw(true); return; } //--- Set the end state of fading out, //--- set the properties to full transparency and redraw the object this.SetDisplayState(CANV_ELEMENT_DISPLAY_STATE_COMPLETED_FADE_OUT); this.SetOpacity(0); this.Redraw(true); } //--- If the object is in the state of completion of fading out if(this.DisplayState()==CANV_ELEMENT_DISPLAY_STATE_COMPLETED_FADE_OUT) { //--- Set the flag for not displaying, set the full transparency, //--- hide the object and set the state of the completion of the entire cycle of changes this.SetDisplayed(false); this.SetOpacity(0); this.Hide(); this.SetDisplayState(CANV_ELEMENT_DISPLAY_STATE_COMPLETED); } } //+------------------------------------------------------------------+
代码注释中完整描述了方法逻辑。 每次对象计时器到期时都会调用处理程序。 在计时器的每次迭代中,我们需要检查对象的状态,及其等待/状态更改计数器。
首先,对象的常规状态予以保存。 在此状态下,工具提示完全隐藏。 如果对象处于这种状态,那么首先它在开始淡入之前应等待一段时间。 因此,若看到对象是正常状态,我们则为其预置等待淡入的状态,开始等待时间的倒计时,并离开方法。
在下一次迭代中,我们看到一个新状态(等待淡入),并相应地检查计数器。 如果等待时间已到,则将对象设置为淡入淡出状态,并持续到下一次方法调用。 在下一次运行时,我们就已经处于对象淡入的过程。 在此,我们需要检查对象的不透明度,并更改步数增加它。 这在计时器每次迭代的操作中都会发生。 一旦对象变得完全不透明,我们就为它设置一个新的状态 — 等待平滑的淡出过程。 毕竟,大约五秒钟后,出现的物体也应该平滑消隐。
其淡出的过程与淡入的过程相同。 在完整周期结束时,为对象设置更改周期结束状态。 如果我们立即为其设置回正常状态,那么整个循环就会重新开始,因为对象的正常状态是启动此循环的起始条件。 仅当从计时器中的需处理对象列表中删除对象指针时,才应将其写入对象。 这是在图形元素集合类中完成的。 看到对象完整周期完成状态,该方法从处理列表中删除对象指针,并将对象本身设置为正常状态。 待下次我们将光标悬停在对象上方时,它回被再次置于列表当中,以便在计时器中进行处理。 以同样方式,如果光标在对象区域移动,而其正准备处理,甚至正处于处理期间,则对象指针将从列表中删除。
渐渐地,我们正在接近创建一个新的进度条 WinForms对象。 针对此控件,我们需要创建眩光(Glare) 辅助对象。 该类型对象作为一些 GUI 元素的直观装饰,而在进度条对象中,眩光应沿着进度条运动。 概念如下:绘制所需形状和尺寸的白色光斑,并从其中心到边缘递次模糊。 眩光对象将与阴影对象一样是半透明。 于此,我们得出一个事实,即我们需要从阴影对象继承这样一个对象,从而借助其模糊方法,同时保持自定义渲染。 为了正常地从阴影对象继承,我们需要为其添加一个受保护的构造函数,其中我们将指明所创建对象的类型。
我们在 \MQL5\Include\DoEasy\Objects\Graph\ShadowObj.mqh 中更改阴影对象类。
我们把高斯模糊方法和权重系数数组从私密部分转移到类的受保护部分。 受保护的构造函数也在那里声明。 在类的公开部分中,编写一个虚拟方法,返回按对象维护实数型属性的标志:
//+------------------------------------------------+ //| Shadows object class | //+------------------------------------------------+ class CShadowObj : public CGCnvElement { private: color m_color; // Shadow color uchar m_opacity; // Shadow opacity uchar m_blur; // Blur //--- Draw the object shadow form void DrawShadowFigureRect(const int w,const int h); protected: //--- Gaussian blur bool GaussianBlur(const uint radius); //--- Return the array of weight ratios bool GetQuadratureWeights(const double mu0,const int n,double &weights[]); //--- Protected constructor with object type, chart ID and subwindow CShadowObj(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: //--- Constructor indicating the main and base objects, chart ID and subwindow CShadowObj(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); //--- Supported object properties (1) integer, (2) real and (3) string ones virtual bool SupportProperty(ENUM_CANV_ELEMENT_PROP_INTEGER property) { return true; } virtual bool SupportProperty(ENUM_CANV_ELEMENT_PROP_DOUBLE property) { return true; } virtual bool SupportProperty(ENUM_CANV_ELEMENT_PROP_STRING property) { return true; } //--- Draw an object shadow
我将逐步加入方法,这些方法为所有图形元素返回维护对象属性的标志,对于函数库来说这些都是标准方法,我将来也需要。 在此,我加上这样的方法,只是为了避免在该类回到这个问题。
受保护的构造函数:
//+------------------------------------------------+ //| Protected constructor with an object type, | //| chart ID and subwindow | //+------------------------------------------------+ CShadowObj::CShadowObj(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) : CGCnvElement(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_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); } //+------------------------------------------------------------------+
在正式构造函数参数中,把欲创建对象的类型传递给初始化代码中的父类。 这是在函数库的所有对象中完成的。 此处的其它所有,都与参数化构造函数中的完全相同。
在参数化构造函数中,删除末尾导致阴影在创建后立即绘制的代码:
//+------------------------------------------------------------------+ //| Constructor indicating the main and base objects, | //| chart ID and subwindow | //+------------------------------------------------------------------+ CShadowObj::CShadowObj(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) : CGCnvElement(GRAPH_ELEMENT_TYPE_SHADOW_OBJ,main_obj,base_obj,chart_id,subwindow,descript,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(); } //+------------------------------------------------------------------+
该段代码导致创建 GUI 元素时阴影对象的行为不正确。 首先,一个阴影出现在一个空图表上,然后才构建GUI 的外观。 现在,阴影则不会先出现。
进度条(ProgressBar)控件
首先,我们在 GlareObj.mqh 文件的 \MQL5\Include\DoEasy\Objects\Graph\WForms\ 中创建一个辅助眩光对象。 由于我目前不需要这个对象,所以我暂时不去研究它。 它与阴影对象类完全相似,只是绘制的眩光颜色设置为白色:
//+------------------------------------------------------------------+ //| GlareObj.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 "..\ShadowObj.mqh" //+------------------------------------------------+ //| Glare object class | //+------------------------------------------------+ class CGlareObj : public CShadowObj { private: color m_color; // Glare color uchar m_opacity; // Glare opacity uchar m_blur; // Blur //--- Draw the object glare form void DrawGlareFigure(const int w,const int h); void DrawGlareFigureRect(const int w,const int h); protected: //--- Protected constructor with object type, chart ID and subwindow CGlareObj(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: //--- Constructor indicating the main and base objects, chart ID and subwindow CGlareObj(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); //--- Supported object properties (1) integer, (2) real and (3) string ones virtual bool SupportProperty(ENUM_CANV_ELEMENT_PROP_INTEGER property) { return true; } virtual bool SupportProperty(ENUM_CANV_ELEMENT_PROP_DOUBLE property) { return true; } virtual bool SupportProperty(ENUM_CANV_ELEMENT_PROP_STRING property) { return true; } //--- Draw the object glare void Draw(const int shift_x,const int shift_y,const uchar blur_value,const bool redraw); }; //+------------------------------------------------+ //| Protected constructor with an object type, | //| chart ID and subwindow | //+------------------------------------------------+ CGlareObj::CGlareObj(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) : CShadowObj(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_GGLARE; CGCnvElement::SetBackgroundColor(clrNONE,true); CGCnvElement::SetOpacity(0); CGCnvElement::SetActive(false); this.SetOpacityDraw(CLR_DEF_SHADOW_OPACITY); this.SetBlur(DEF_SHADOW_BLUR); this.m_color=clrWhite; this.m_shadow=false; this.SetVisibleFlag(false,false); } //+------------------------------------------------------------------+ //| Constructor indicating the main and base objects, | //| chart ID and subwindow | //+------------------------------------------------------------------+ CGlareObj::CGlareObj(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) : CShadowObj(GRAPH_ELEMENT_TYPE_GLARE_OBJ,main_obj,base_obj,chart_id,subwindow,descript,x,y,w,h) { this.m_type=OBJECT_DE_TYPE_GGLARE; CGCnvElement::SetBackgroundColor(clrNONE,true); CGCnvElement::SetOpacity(0); CGCnvElement::SetActive(false); this.SetOpacityDraw(CLR_DEF_SHADOW_OPACITY); this.SetBlur(DEF_SHADOW_BLUR); this.m_color=clrWhite; this.m_shadow=false; this.SetVisibleFlag(false,false); } //+------------------------------------------------+ //| Draw the object glare | //+------------------------------------------------+ void CGlareObj::Draw(const int shift_x,const int shift_y,const uchar blur_value,const bool redraw) { if(!this.IsVisible()) return; //--- Set the glare offset values along the X and Y axes to variables this.SetCoordXRelative(shift_x); this.SetCoordYRelative(shift_y); //--- Calculate the height and width of the drawn rectangle int w=this.Width()-OUTER_AREA_SIZE*2; int h=this.Height()-OUTER_AREA_SIZE*2; //--- Draw a filled rectangle with calculated dimensions this.DrawGlareFigure(w,h); //--- Calculate the blur radius, which cannot exceed a quarter of the OUTER_AREA_SIZE constant this.m_blur=(blur_value>OUTER_AREA_SIZE/4 ? OUTER_AREA_SIZE/4 : blur_value); //--- If failed to blur the shape, exit the method (GaussianBlur() displays the error on the journal) if(!this.GaussianBlur(this.m_blur)) return; //--- Shift the glare object by X/Y offsets specified in the method arguments and update the canvas CGCnvElement::Move(this.CoordX()+this.CoordXRelative(),this.CoordY()+this.CoordYRelative(),false); CGCnvElement::Update(redraw); } //+------------------------------------------------+ //| Draw the object glare form | //+------------------------------------------------+ void CGlareObj::DrawGlareFigure(const int w,const int h) { this.DrawGlareFigureRect(w,h); } //+------------------------------------------------+ //| Draw the rectangle object glare form | //+------------------------------------------------+ void CGlareObj::DrawGlareFigureRect(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(); } //+------------------------------------------------------------------+
我将在下一篇文章中对该类进行所有必要的修改。 至于现在,我们先保持原样。
进度条控件将由两个对象组成 — 水印和进度条。 参考底图将代表控制元素本身。 将来可以在其上放置其它元素,并且进度条将由一个单独的辅助对象表示,其属性从父对象更改。
在 \MQL5\Include\DoEasy\Objects\Graph\WForms\Helpers\ 函数库文件中,创建含有 CBarProgressBar 类的新 BarProgressBar.mqh 文件。 该类应派生自所有函数库 WinForms 对象的基准对象。 基准对象文件应包含在所创建类文件当中:
//+------------------------------------------------------------------+ //| BarProgressBar.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 "..\WinFormBase.mqh" //+------------------------------------------------------------------+ //| BarProgressBar object class of the ProgressBar control | //+------------------------------------------------------------------+ class CBarProgressBar : public CWinFormBase { }
在类的受保护部分中,声明在进度条上显示眩光对象的方法,以及受保护的构造函数。 在公开部分中,编写设置和返回对象属性值的方法、用于维护属性的虚拟方法、参数构造函数、和类计时器事件处理程序:
//+------------------------------------------------+ //| Include files | //+------------------------------------------------+ #include "..\WinFormBase.mqh" //+------------------------------------------------------------------+ //| BarProgressBar object class of the ProgressBar control | //+------------------------------------------------------------------+ class CBarProgressBar : public CWinFormBase { private: protected: //--- Display the glare virtual void DrawGlare(void); //--- Protected constructor with object type, chart ID and subwindow CBarProgressBar(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: //--- Set the (1) animation speed in case of Marquee style, (2) style, (3) increment value, (4) current value of the ProgressBar control void SetMarqueeAnimationSpeed(const int value) { this.SetProperty(CANV_ELEMENT_PROP_PROGRESS_BAR_MARQUEE_ANIM_SPEED,value); } void SetStyle(const ENUM_CANV_ELEMENT_PROGRESS_BAR_STYLE style) { this.SetProperty(CANV_ELEMENT_PROP_PROGRESS_BAR_STYLE,style); } void SetStep(const int value) { this.SetProperty(CANV_ELEMENT_PROP_PROGRESS_BAR_STEP,value); } void SetValue(const int value) { this.SetProperty(CANV_ELEMENT_PROP_PROGRESS_BAR_VALUE,value); } //--- Supported object properties (1) integer, (2) real and (3) string ones virtual bool SupportProperty(ENUM_CANV_ELEMENT_PROP_INTEGER property) { return true; } virtual bool SupportProperty(ENUM_CANV_ELEMENT_PROP_DOUBLE property) { return true; } virtual bool SupportProperty(ENUM_CANV_ELEMENT_PROP_STRING property) { return true; } //--- Constructor CBarProgressBar(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); //--- Timer virtual void OnTimer(void); }; //+------------------------------------------------------------------+
我们来仔细查看所声明的方法。
受保护的构造函数:
//+------------------------------------------------+ //| Protected constructor with an object type, | //| chart ID and subwindow | //+------------------------------------------------+ CBarProgressBar::CBarProgressBar(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) : CWinFormBase(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_HELPER; this.SetPaddingAll(0); this.SetMarginAll(0); this.SetBorderSizeAll(0); this.SetBackgroundColor(CLR_DEF_CONTROL_PROGRESS_BAR_BAR_COLOR,true); this.SetBorderColor(CLR_DEF_CONTROL_PROGRESS_BAR_BAR_COLOR,true); this.SetForeColor(CLR_DEF_CONTROL_PROGRESS_BAR_FORE_COLOR,true); } //+------------------------------------------------------------------+
构造函数接收在初始化代码中传递给父类的已创建图形元素的类型。 函数库图形对象类型设置为“辅助”,对象无边框,并设置缺省颜色。
参数型构造函数:
//+------------------------------------------------------------------+ //| Constructor indicating the main and base objects, | //| chart ID and subwindow | //+------------------------------------------------------------------+ CBarProgressBar::CBarProgressBar(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) : CWinFormBase(GRAPH_ELEMENT_TYPE_WF_BAR_PROGRESS_BAR,main_obj,base_obj,chart_id,subwindow,descript,x,y,w,h) { this.SetTypeElement(GRAPH_ELEMENT_TYPE_WF_BAR_PROGRESS_BAR); this.m_type=OBJECT_DE_TYPE_GWF_HELPER; this.SetPaddingAll(0); this.SetMarginAll(0); this.SetBorderSizeAll(0); this.SetBackgroundColor(CLR_DEF_CONTROL_PROGRESS_BAR_BAR_COLOR,true); this.SetBorderColor(CLR_DEF_CONTROL_PROGRESS_BAR_BAR_COLOR,true); this.SetForeColor(CLR_DEF_CONTROL_PROGRESS_BAR_FORE_COLOR,true); } //+------------------------------------------------------------------+
此处的所有内容都与受保护的构造函数中的完全相同,但对象类型未在形式参数中传递。 取而代之,它在初始化代码中硬编码。
绘制眩光的方法现在将为空,因为眩光对象尚未准备就绪。 计划在下一篇文章中创建和调试它:
//+------------------------------------------------+ //| Draw a glare | //+------------------------------------------------+ void CBarProgressBar::DrawGlare(void) { } //+------------------------------------------------------------------+
在计时器事件处理程序中,临时设置从 GetTickCount() 函数返回的值,确保该对象落入需由函数库计时器中处理的已分配活动对象列表之中:
//+------------------------------------------------+ //| Timer | //+------------------------------------------------+ void CBarProgressBar::OnTimer(void) { Comment(DFUN,GetTickCount()); } //+------------------------------------------------------------------+
在开始创建进度条控件之前,我需要确保图形元素对象可以在函数库计时器中独立处理。
概念如下:在计时器中独立处理的每个图形元素都将被视为活动元素。 为了处理活动元素,我们需创建一个列表,其中包含此类对象的指针。 此列表将始终在主对象中创建,相应地,在图形元素的集合类中可见。 在集合类计时器中,我们将遍历所有主窗体对象,获取指向活动元素的指针,并调用它们的计时器事件处理程序。 如此,我们就不会被迫在主对象中查找每个图形元素,而是立即创建主对象中的元素列表,并仅处理此类元素。
在 \MQL5\Include\DoEasy\Objects\Graph\WForms\WinFormBase.mqh 所有 WinForms 函数库对象的基准对象类文件中,添加包含眩光对象类的文件,并在受保护部分中声明指向活动元素列表的指针:
//+------------------------------------------------------------------+ //| WinFormBase.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 "GlareObj.mqh" #include "..\Form.mqh" #include "..\..\..\Services\Select.mqh" //+------------------------------------------------+ //| Form object class | //+------------------------------------------------+ class CWinFormBase : public CForm { protected: CArrayObj *m_list_active_elements; // Pointer to the list of active elements color m_fore_color_init; // Initial color of the control text color m_fore_state_on_color_init; // Initial color of the control text when the control is "ON" private: //--- Return the font flags uint GetFontFlags(void); public:
在此处包含眩光对象文件,我们就能够在函数库的许多图形元素中创建和使用它。
在类的公开部分中,声明处理活动元素列表的方法:
public: //--- Draw a frame virtual void DrawFrame(void){} //--- Return by type the (1) list, (2) the number of bound controls, the bound control (3) by index in the list, (4) by name CArrayObj *GetListElementsByType(const ENUM_GRAPH_ELEMENT_TYPE type); int ElementsTotalByType(const ENUM_GRAPH_ELEMENT_TYPE type); CGCnvElement *GetElementByType(const ENUM_GRAPH_ELEMENT_TYPE type,const int index); CGCnvElement *GetElementByName(const string name); //--- Return the list of active elements of (1) the current, (2) main object CArrayObj *GetListActiveElements(void) { return this.m_list_active_elements; } CArrayObj *GetListMainActiveElements(void); //--- Return the number of active elements of the main object int ListMainActiveElementsTotal(void); //--- Return the element from the list of active elements of the main object by index CWinFormBase *GetActiveElement(const int index); //--- Return the index of the specified object in the list of active elements of the main object int IndexActiveElements(CWinFormBase *obj); //--- Return the flag of the object presence by name in the list of active elements bool IsPresentObjInListActiveElements(string name_obj); //--- Add (1) the specified and (2) the current object to the list of active elements bool AddObjToListActiveElements(CWinFormBase *obj); bool AddObjToListActiveElements(void); //--- Remove (1) the specified and (2) the current object from the list of active elements bool DetachObjFromListActiveElements(CWinFormBase *obj); bool DetachObjFromListActiveElements(void); //--- Clear the element filling it with color and opacity virtual void Erase(const color colour,const uchar opacity,const bool redraw=false);
加入类的析构函数:
public: //--- Constructor CWinFormBase(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); //--- Destructor ~CWinFormBase(void) { if(this.m_list_active_elements!=NULL) { this.m_list_active_elements.Clear(); delete this.m_list_active_elements; } } //--- (1) Set and (2) return the default text color of all panel objects
如果创建了活动元素列表,则清除它,并删除所创建列表对象。
在类构造函数中,编写创建活动元素的列表对象:
//+------------------------------------------------+ //| Protected constructor with an object type, | //| chart ID and subwindow | //+------------------------------------------------+ CWinFormBase::CWinFormBase(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) : CForm(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_BASE; //--- Initialize all variables this.SetText(""); this.SetForeColor(CLR_DEF_FORE_COLOR,true); this.SetForeStateOnColor(this.ForeColor(),true); this.SetForeStateOnColorMouseDown(this.ForeColor()); this.SetForeStateOnColorMouseOver(this.ForeColor()); this.SetForeColorOpacity(CLR_DEF_FORE_COLOR_OPACITY); this.SetFontBoldType(FW_TYPE_NORMAL); this.SetMarginAll(0); this.SetPaddingAll(0); this.SetBorderSizeAll(0); this.SetDockMode(CANV_ELEMENT_DOCK_MODE_NONE,false); this.SetBorderStyle(FRAME_STYLE_NONE); this.SetAutoSize(false,false); CForm::SetCoordXInit(x); CForm::SetCoordYInit(y); CForm::SetWidthInit(w); CForm::SetHeightInit(h); this.m_shadow=false; this.m_gradient_v=true; this.m_gradient_c=false; this.m_list_active_elements=new CArrayObj(); } //+------------------------------------------------------------------+ //| Constructor indicating the main and base objects, | //| chart ID and subwindow | //+------------------------------------------------------------------+ CWinFormBase::CWinFormBase(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) : CForm(GRAPH_ELEMENT_TYPE_WF_BASE,main_obj,base_obj,chart_id,subwindow,descript,x,y,w,h) { //--- Set the graphical element and library object types as a base WinForms object this.SetTypeElement(GRAPH_ELEMENT_TYPE_WF_BASE); this.m_type=OBJECT_DE_TYPE_GWF_BASE; //--- Initialize all variables this.SetText(""); this.SetForeColor(CLR_DEF_FORE_COLOR,true); this.SetForeStateOnColor(this.ForeColor(),true); this.SetForeStateOnColorMouseDown(this.ForeColor()); this.SetForeStateOnColorMouseOver(this.ForeColor()); this.SetForeColorOpacity(CLR_DEF_FORE_COLOR_OPACITY); this.SetFontBoldType(FW_TYPE_NORMAL); this.SetMarginAll(0); this.SetPaddingAll(0); this.SetBorderSizeAll(0); this.SetDockMode(CANV_ELEMENT_DOCK_MODE_NONE,false); this.SetBorderStyle(FRAME_STYLE_NONE); this.SetAutoSize(false,false); CForm::SetCoordXInit(x); CForm::SetCoordYInit(y); CForm::SetWidthInit(w); CForm::SetHeightInit(h); this.m_shadow=false; this.m_gradient_v=true; this.m_gradient_c=false; this.m_list_active_elements=new CArrayObj(); } //+------------------------------------------------------------------+
在返回元素整数型属性说明的方法中,编写代码模块,返回新对象属性的说明:
//+------------------------------------------------------------------+ //| 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_DISPLAYED ? CMessage::Text(MSG_CANV_ELEMENT_PROP_DISPLAYED)+ (only_prop ? "" : !this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(string)this.GetProperty(property) ) : property==CANV_ELEMENT_PROP_DISPLAY_STATE ? CMessage::Text(MSG_CANV_ELEMENT_PROP_DISPLAY_STATE)+ (only_prop ? "" : !this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(string)this.GetProperty(property) ) : property==CANV_ELEMENT_PROP_DISPLAY_DURATION ? CMessage::Text(MSG_CANV_ELEMENT_PROP_DISPLAY_DURATION)+ (only_prop ? "" : !this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(string)this.GetProperty(property) ) : property==CANV_ELEMENT_PROP_GROUP ? CMessage::Text(MSG_GRAPH_OBJ_PROP_GROUP)+ (only_prop ? "" : !this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(string)this.GetProperty(property) ) : //---... //---... property==CANV_ELEMENT_PROP_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) ) : property==CANV_ELEMENT_PROP_PROGRESS_BAR_MAXIMUM ? CMessage::Text(MSG_CANV_ELEMENT_PROP_PROGRESS_BAR_MAXIMUM)+ (only_prop ? "" : !this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(string)this.GetProperty(property) ) : property==CANV_ELEMENT_PROP_PROGRESS_BAR_MINIMUM ? CMessage::Text(MSG_CANV_ELEMENT_PROP_PROGRESS_BAR_MINIMUM)+ (only_prop ? "" : !this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(string)this.GetProperty(property) ) : property==CANV_ELEMENT_PROP_PROGRESS_BAR_STEP ? CMessage::Text(MSG_CANV_ELEMENT_PROP_PROGRESS_BAR_STEP)+ (only_prop ? "" : !this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(string)this.GetProperty(property) ) : property==CANV_ELEMENT_PROP_PROGRESS_BAR_STYLE ? CMessage::Text(MSG_CANV_ELEMENT_PROP_PROGRESS_BAR_STYLE)+ (only_prop ? "" : !this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(string)this.GetProperty(property) ) : property==CANV_ELEMENT_PROP_PROGRESS_BAR_VALUE ? CMessage::Text(MSG_CANV_ELEMENT_PROP_PROGRESS_BAR_VALUE)+ (only_prop ? "" : !this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(string)this.GetProperty(property) ) : property==CANV_ELEMENT_PROP_PROGRESS_BAR_MARQUEE_ANIM_SPEED ? CMessage::Text(MSG_CANV_ELEMENT_PROP_PROGRESS_BAR_MARQUEE_ANIM_SPEED)+ (only_prop ? "" : !this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(string)this.GetProperty(property) ) : "" ); } //+------------------------------------------------------------------+
该方法返回主对象的活动元素列表:
//+------------------------------------------------------------------+ //| Return the list of active elements of the main object | //+------------------------------------------------------------------+ CArrayObj *CWinFormBase::GetListMainActiveElements(void) { CWinFormBase *main=this.GetMain(); if(main==NULL) main=this.GetObject(); CArrayObj *list=main.GetListActiveElements(); if(list==NULL) { CMessage::ToLog(DFUN,MSG_GRAPH_OBJ_FAILED_GET_ACTIVE_OBJ_LIST); .return NULL; } return list; } //+------------------------------------------------------------------+
在此,我们得到指向主对象的指针。 如果返回值为 NULL,则代表这是主对象。 从主对象获取活动元素的列表。 如果获取列表失败,则通知,并返回 NULL。 否则,返回指向列表的指针。
该方法返回主对象的活动元素数量:
//+------------------------------------------------------------------+ //| Return the number of active elements of the main object | //+------------------------------------------------------------------+ int CWinFormBase::ListMainActiveElementsTotal(void) { return(this.GetListMainActiveElements()!=NULL ? this.GetListMainActiveElements().Total() : 0); } //+------------------------------------------------------------------+
如果已获取指向主对象列表的指针,则返回列表中的元素数量。 否则,返回零。
该方法返回主对象的活动元素列表中指定对象的索引:
//+------------------------------------------------------------------+ //| Return the specified object index | //| in the list of active elements of the main object | //+------------------------------------------------------------------+ int CWinFormBase::IndexActiveElements(CWinFormBase *obj) { CArrayObj *list=this.GetListMainActiveElements(); if(list==NULL) return WRONG_VALUE; for(int i=0;i<this.ListMainActiveElementsTotal();i++) { CWinFormBase *elm=list.At(i); if(elm!=NULL && elm.Name()==obj.Name()) return i; } return WRONG_VALUE; } //+------------------------------------------------------------------+
该方法接收指向需查找其索引的对象指针。 从主对象获取活动元素的列表。 根据收到的列表循环,获取下一个对象。 如果其名称等于传递给方法的对象名称,则返回循环索引。 在循环结束时,返回 -1,这意味着未找到该对象。
该方法按索引从主对象的活动元素列表中返回元素:
//+------------------------------------------------+ //| Return an element from the list of active | //| elements of the main object by index | //+------------------------------------------------+ CWinFormBase *CWinFormBase::GetActiveElement(const int index) { CArrayObj *list=this.GetListMainActiveElements(); return(list!=NULL ? list.At(index) : NULL); } //+------------------------------------------------------------------+
该方法接收需从列表中返回的对象索引。 我们从主对象获取活动元素的列表,并按索引返回指向对象的指针。 如果无法获取列表,该方法将返回 NULL。
该方法从主对象活动元素列表中按名称返回对象存在的标志:
//+------------------------------------------------------------------+ //| Return the flag of an object presence by name | //| in the list of active elements of the main object | //+------------------------------------------------------------------+ bool CWinFormBase::IsPresentObjInListActiveElements(string name_obj) { CArrayObj *list=this.GetListMainActiveElements(); if(list==NULL) return false; for(int i=list.Total()-1;i>WRONG_VALUE;i--) { CWinFormBase *obj=list.At(i); if(obj!=NULL && obj.Name()==name_obj) return true; } return false; } //+------------------------------------------------------------------+
该方法接收我们需在列表中查找其存在的对象名称。 从主对象获取活动元素的列表。 循环遍历获取的列表,获取下一个对象。 如果其名称与传递给方法的名称匹配,则返回 true。 直至循环完毕,返回 false。 意即对象不在列表中。
该方法将指定对象添加到主对象的活动元素列表之中:
//+------------------------------------------------------------------+ //| Add the specified object to the list | //| of active elements of the main object | //+------------------------------------------------------------------+ bool CWinFormBase::AddObjToListActiveElements(CWinFormBase *obj) { CArrayObj *list=this.GetListMainActiveElements(); if(list==NULL) return false; if(obj==NULL || this.IsPresentObjInListActiveElements(obj.Name())) return false; return list.Add(obj); } //+------------------------------------------------------------------+
获取主对象的活动元素列表。 如果列表中已有同名对象,则返回 false。 否则,返回将对象添加到列表的结果。
该方法将当前对象添加到主对象的活动元素列表之中:
//+------------------------------------------------+ //| Add the current object to the list of | //| active elements of the main object | //+------------------------------------------------+ bool CWinFormBase::AddObjToListActiveElements(void) { return this.AddObjToListActiveElements(this.GetObject()); } //+------------------------------------------------------------------+
返回调用上述方法的结果。 指定当前对象为要添加到列表中的对象。
该方法从主对象的活动元素列表中删除指定对象:
//+------------------------------------------------+ //| Remove the specified object from the list of | //| active elements of the main object | //+------------------------------------------------+ bool CWinFormBase::DetachObjFromListActiveElements(CWinFormBase *obj) { CArrayObj *list=this.GetListMainActiveElements(); if(list==NULL) return false; int index=this.IndexActiveElements(obj); if(index==WRONG_VALUE) return false; CWinFormBase *elm=list.Detach(index); if(elm==NULL) return false; elm=NULL; return true; } //+------------------------------------------------------------------+
获取指向主对象的活动元素列表的指针。 对象指针传递给该方法,获取列表中对应对象的索引。 获取从列表中删除的对象指针。 如果删除对象失败,则返回 false. 否则,重置指针,并返回 true。
该方法从主对象的活动元素列表中删除当前对象:
//+------------------------------------------------+ //| Remove the current object from the list of | //| active elements of the main object | //+------------------------------------------------+ bool CWinFormBase::DetachObjFromListActiveElements(void) { return this.DetachObjFromListActiveElements(this.GetObject()); } //+------------------------------------------------------------------+
返回调用上述方法的结果。 指定当前对象为要删除的对象。
我们来创建进度条控件。
在 MQL5\Include\DoEasy\Objects\Graph\WForms\Common Controls\ 中,创建一个 CProgressBar 类的新文件 ProgressBar.mqh。 由于为了扩展对象的功能,我们计划令其拥有附加其它控件的能力,故此该类应派生自容器对象类。 容器类文件应与进度条类文件一起包含在所创建对象的文件当中:
//+------------------------------------------------------------------+ //| ProgressBar.mqh | //| Copyright 2022, MetaQuotes Ltd. | //| https://mql5.com/en/users/artmedia70 | //+------------------------------------------------------------------+ #property copyright "Copyright 2022, MetaQuotes Ltd." #property link "https://mql5.com/en/users/artmedia70" #property version "1.00" #property strict // Necessary for mql4 //+------------------------------------------------+ //| Include files | //+------------------------------------------------+ #include "..\Containers\Container.mqh" #include "..\Helpers\BarProgressBar.mqh" //+------------------------------------------------------------------+ //| ArrowLeftRightBox object class of WForms controls | //+------------------------------------------------------------------+ class CProgressBar : public CContainer { }
在类的私密部分,声明创建新图形对象和进度条的方法,以及类初始化方法:
//+------------------------------------------------------------------+ //| ArrowLeftRightBox object class of WForms controls | //+------------------------------------------------------------------+ class CProgressBar : public CContainer { private: //--- Create a new graphical object virtual CGCnvElement *CreateNewGObject(const ENUM_GRAPH_ELEMENT_TYPE type, const int element_num, const string descript, const int x, const int y, const int w, const int h, const color colour, const uchar opacity, const bool movable, const bool activity); //--- Create the progress bar object void CreateProgressBar(void); //--- Initialize the element properties void Initialize(void); protected:
在类的受保护部分中,声明受保护的构造函数:
protected: //--- Protected constructor with object type, chart ID and subwindow CProgressBar(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 animation speed of the progress bar in case of the Marquee style void SetMarqueeAnimationSpeed(const int value) { this.SetProperty(CANV_ELEMENT_PROP_PROGRESS_BAR_MARQUEE_ANIM_SPEED,value); CBarProgressBar *bar=this.GetProgressBar(); if(bar!=NULL) bar.SetMarqueeAnimationSpeed(value); } int MarqueeAnimationSpeed(void) const { return (int)this.GetProperty(CANV_ELEMENT_PROP_PROGRESS_BAR_MARQUEE_ANIM_SPEED); } //--- (1) Set and (2) return the progress bar style void SetStyle(const ENUM_CANV_ELEMENT_PROGRESS_BAR_STYLE style) { this.SetProperty(CANV_ELEMENT_PROP_PROGRESS_BAR_STYLE,style); CBarProgressBar *bar=this.GetProgressBar(); if(bar!=NULL) bar.SetStyle(style); } ENUM_CANV_ELEMENT_PROGRESS_BAR_STYLE Style(void) const { return (ENUM_CANV_ELEMENT_PROGRESS_BAR_STYLE)this.GetProperty(CANV_ELEMENT_PROP_PROGRESS_BAR_STYLE); } //--- (1) Set and (2) return the progress bar increment to redraw it void SetStep(const int value) { this.SetProperty(CANV_ELEMENT_PROP_PROGRESS_BAR_STEP,value); CBarProgressBar *bar=this.GetProgressBar(); if(bar!=NULL) bar.SetStep(value); } int Step(void) const { return (int)this.GetProperty(CANV_ELEMENT_PROP_PROGRESS_BAR_STEP); } //--- (1) Set and (2) return the current value of the progress bar in the range from Min to Max void SetValue(const int value) { this.SetProperty(CANV_ELEMENT_PROP_PROGRESS_BAR_VALUE,value); CBarProgressBar *bar=this.GetProgressBar(); if(bar!=NULL) bar.SetValue(value); } int Value(void) const { return (int)this.GetProperty(CANV_ELEMENT_PROP_PROGRESS_BAR_VALUE); } //--- (1) Set and (2) return the upper bound of the ProgressBar operating range void SetMaximum(const int value) { this.SetProperty(CANV_ELEMENT_PROP_PROGRESS_BAR_MAXIMUM,value); } int Maximum(void) const { return (int)this.GetProperty(CANV_ELEMENT_PROP_PROGRESS_BAR_MAXIMUM); } //--- (1) Set and (2) return the lower bound of the ProgressBar operating range void SetMinimum(const int value) { this.SetProperty(CANV_ELEMENT_PROP_PROGRESS_BAR_MINIMUM,value); } int Minimum(void) const { return (int)this.GetProperty(CANV_ELEMENT_PROP_PROGRESS_BAR_MINIMUM); } //--- Return the pointer to the progress bar object CBarProgressBar *GetProgressBar(void) { return this.GetElementByType(GRAPH_ELEMENT_TYPE_WF_BAR_PROGRESS_BAR,0); } //--- Supported object properties (1) integer, (2) real and (3) string ones virtual bool SupportProperty(ENUM_CANV_ELEMENT_PROP_INTEGER property) { return true; } virtual bool SupportProperty(ENUM_CANV_ELEMENT_PROP_DOUBLE property) { return true; } virtual bool SupportProperty(ENUM_CANV_ELEMENT_PROP_STRING property) { return true; } //--- Constructor CProgressBar(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); //--- Timer virtual void OnTimer(void); }; //+------------------------------------------------------------------+
设置属性后,某些设置对象属性的方法会为进度条对象设置相应的属性。
我们来仔细查看所声明的方法。
受保护的构造函数:
//+------------------------------------------------+ //| Protected constructor with an object type, | //| chart ID and subwindow | //+------------------------------------------------+ CProgressBar::CProgressBar(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) : CContainer(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(); this.CreateProgressBar(); } //+------------------------------------------------------------------+
该方法接收所创建对象的类型,该类型在初始化代码中设置到父类。 函数库图形对象类型设置为“标准控件”。 然后调用对象属性初始化方法,和创建进度条对象的方法。
参数型构造函数:
//+------------------------------------------------------------------+ //| Constructor indicating the main and base objects, | //| chart ID and subwindow | //+------------------------------------------------------------------+ CProgressBar::CProgressBar(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) : CContainer(GRAPH_ELEMENT_TYPE_WF_PROGRESS_BAR,main_obj,base_obj,chart_id,subwindow,descript,x,y,w,h) { this.SetTypeElement(GRAPH_ELEMENT_TYPE_WF_PROGRESS_BAR); this.m_type=OBJECT_DE_TYPE_GWF_COMMON; this.Initialize(); this.CreateProgressBar(); } //+------------------------------------------------------------------+
此处,控件的类型在初始化代码中硬编码。
元素属性初始化方法:
//+------------------------------------------------+ //| Initialize the element properties | //+------------------------------------------------+ void CProgressBar::Initialize(void) { this.SetBorderSizeAll(1); this.SetBorderStyle(FRAME_STYLE_SIMPLE); this.SetBackgroundColor(CLR_DEF_CONTROL_PROGRESS_BAR_BACK_COLOR,true); this.SetBorderColor(CLR_DEF_CONTROL_PROGRESS_BAR_BORDER_COLOR,true); this.SetForeColor(CLR_DEF_CONTROL_PROGRESS_BAR_FORE_COLOR,true); this.SetMarqueeAnimationSpeed(10); this.SetMaximum(100); this.SetMinimum(0); this.SetStep(10); this.SetStyle(CANV_ELEMENT_PROGRESS_BAR_STYLE_CONTINUOUS); this.SetValue(this.Width()/2); } //+------------------------------------------------------------------+
对象边框设置为每侧一个像素,边框类型为简单,对象颜色和其它默认属性一并设置。 进度条的长度设置为对象宽度的一半。
该方法创建进度栏对象:
//+------------------------------------------------+ //| Create the progress bar object | //+------------------------------------------------+ void CProgressBar::CreateProgressBar(void) { //--- Set the length of the progress bar equal to the object Value() //--- The height of the progress bar is equal to the height of the object minus the top and bottom frame sizes int w=this.Value(); int h=this.Height()-this.BorderSizeTop()-this.BorderSizeBottom(); //--- Create the progress bar object this.CreateNewElement(GRAPH_ELEMENT_TYPE_WF_BAR_PROGRESS_BAR,0,0,w,h,clrNONE,255,false,false); //--- Add the created CProgressBar object to the list of active elements of the collection if(this.AddObjToListActiveElements()) { //--- To perform the check, get this element from the list, display its description and the number of active elements in the list CProgressBar *progress_bar=this.GetActiveElement(this.IndexActiveElements(this.GetObject())); if(progress_bar!=NULL) Print(DFUN_ERR_LINE,"CProgressBar: ",progress_bar.TypeElementDescription(),", ListMainActiveElementsTotal=",this.ListMainActiveElementsTotal()); } } //+------------------------------------------------------------------+
方法逻辑在清单中均有注释。 设置进度条的高度,如此令对象完全适配容器,同时容器边框不会与它重叠。 创建进度条后,将整个对象添加到活动元素列表当中。 在这种情况下,它将进入函数库图形元素计时器的事件处理程序,并将在其中进行处理。 相应地,在对象计时器中,我们可令功能独立于用户工作。 在这个对象中,我将在下一篇文章中处理的所述的视觉效果。
创建新图形对象的虚拟方法:
//+------------------------------------------------+ //| Create a new graphical object | //+------------------------------------------------+ CGCnvElement *CProgressBar::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_BAR_PROGRESS_BAR : element=new CBarProgressBar(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h); break; case GRAPH_ELEMENT_TYPE_GLARE_OBJ : element=new CGlareObj(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; } //+------------------------------------------------------------------+
为了令容器对象在自身内部创建附加对象,有一个虚拟方法可创建这些对象。 对于每个控件,在其中创建的对象列表可以不同。 此对象提供创建进度条对象和眩光对象的功能。 至于目前,此处还不需要创建对象。
计时器事件处理程序:
//+------------------------------------------------+ //| Timer | //+------------------------------------------------+ void CProgressBar::OnTimer(void) { CBarProgressBar *bar=this.GetProgressBar(); if(bar!=NULL) bar.OnTimer(); } //+------------------------------------------------------------------+
在此,我们获取指向进度条对象的指针,并调用其计时器事件处理程序。 由于眩光应该完全沿着进度条运行,因此我将在 CBarProgressBar 类对象的计时器中实现该行为。 出于这个原因,这里也会调用它的计时器,在图表上以注释的形式写上 GetTickCount() 的输出值。
在 \MQL5\Include\DoEasy\Objects\Graph\WForms\Containers\Container.mqh 基准容器对象类文件中,即在设置绑定对象参数的方法中,添加为创建的 BarProgressBar 和进度栏对象设置参数的代码模块:
//+------------------------------------------------+ //| 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 case GRAPH_ELEMENT_TYPE_WF_CONTAINER : case GRAPH_ELEMENT_TYPE_WF_PANEL : case GRAPH_ELEMENT_TYPE_WF_GROUPBOX : obj.SetBorderColor(obj.BackgroundColor(),true); break; //--- For "Label", "CheckBox" and "RadioButton" 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; //--- For BarProgressBar WinForms object case GRAPH_ELEMENT_TYPE_WF_BAR_PROGRESS_BAR : obj.SetBackgroundColor(CLR_DEF_CONTROL_PROGRESS_BAR_BAR_COLOR,true); obj.SetBorderColor(CLR_DEF_CONTROL_PROGRESS_BAR_BAR_COLOR,true); obj.SetForeColor(CLR_DEF_CONTROL_PROGRESS_BAR_FORE_COLOR,true); obj.SetBorderStyle(FRAME_STYLE_NONE); break; //--- For ProgressBar WinForms object case GRAPH_ELEMENT_TYPE_WF_PROGRESS_BAR : obj.SetBackgroundColor(CLR_DEF_CONTROL_PROGRESS_BAR_BACK_COLOR,true); obj.SetBorderColor(CLR_DEF_CONTROL_PROGRESS_BAR_BORDER_COLOR,true); obj.SetForeColor(CLR_DEF_CONTROL_PROGRESS_BAR_FORE_COLOR,true); obj.SetBorderStyle(FRAME_STYLE_SIMPLE); break; default: break; } obj.Crop(); } //+------------------------------------------------------------------+
简单地取这些类的构造函数中设置的默认值。
现在,我们需要令每个容器对象拥有快速将工具提示对象附于当前元素和指定元素的能力。 这样就可更轻松地将工具提示附于容器的所需对象。
在 \MQL5\Include\DoEasy\Objects\Graph\WForms\Containers\Panel.mqh 中,添加包含进度条控件文件:
//+------------------------------------------------------------------+ //| Panel.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 "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" #include "..\..\WForms\Common Controls\ProgressBar.mqh" //+------------------------------------------------------------------+
在公开部分中,声明创建和附加工具提示对象的两种方法:
virtual void SetPaddingAll(const uint value) { this.SetPaddingLeft(value); this.SetPaddingTop(value); this.SetPaddingRight(value); this.SetPaddingBottom(value); } //--- Create and attach the ToolTip object (1) to the current and (2) to the specified control CToolTip *SetToolTip(const string tooltip_description,const string tooltip_title,const string tooltip_text,ENUM_CANV_ELEMENT_TOOLTIP_ICON tooltip_ico); CToolTip *SetToolTipTo(CForm *element,const string tooltip_description,const string tooltip_title,const string tooltip_text,ENUM_CANV_ELEMENT_TOOLTIP_ICON tooltip_ico);
在创建新图形对象的方法中,添加一段创建进度条控件的代码:
//+------------------------------------------------+ //| 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; case GRAPH_ELEMENT_TYPE_WF_PROGRESS_BAR : element=new CProgressBar(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; } //+------------------------------------------------------------------+
该方法创建工具提示对象,并将其附于当前元素:
//+------------------------------------------------------------------+ //| Create and attach the ToolTip object to the current element | //+------------------------------------------------------------------+ CToolTip *CPanel::SetToolTip(const string tooltip_description,const string tooltip_title,const string tooltip_text,ENUM_CANV_ELEMENT_TOOLTIP_ICON tooltip_ico) { //--- Create a new tooltip object this.CreateNewElement(GRAPH_ELEMENT_TYPE_WF_TOOLTIP,0,0,10,10,clrNONE,0,false,false); //--- Get the list of all created ToolTip objects CArrayObj *list=this.GetListElementsByType(GRAPH_ELEMENT_TYPE_WF_TOOLTIP); if(list==NULL || list.Total()==0) .return NULL; //--- Get the last element in the list of ToolTip objects (the last one created) CToolTip *tooltip=list.At(list.Total()-1); //--- If the object is received if(tooltip!=NULL) { //--- set the description, icon, title and tooltip text for the object tooltip.SetDescription(tooltip_description); tooltip.SetIcon(tooltip_ico); tooltip.SetTitle(tooltip_title); tooltip.SetTooltipText(tooltip_text); } //--- Return the ToolTip object return tooltip; } //+------------------------------------------------------------------+
代码注释中完整描述了方法逻辑。 所有必需的数据都传递给创建工具提示对象的方法。 当创建对象之后,它会自动附于当前对象。 接着,我们从工具提示对象列表中获取最新的工具提示对象,并把传递给方法的参数设置给它。
该方法创建工具提示对象,并将其附于指定元素:
//+------------------------------------------------------------------+ //| Create and attach the ToolTip object to the specified element | //+------------------------------------------------------------------+ CToolTip *CPanel::SetToolTipTo(CForm *element,const string tooltip_description,const string tooltip_title,const string tooltip_text,ENUM_CANV_ELEMENT_TOOLTIP_ICON tooltip_ico) { CToolTip *tooltip=this.SetToolTip(tooltip_description,tooltip_title,tooltip_text,tooltip_ico); if(tooltip==NULL) .return NULL; if(element.AddTooltip(tooltip)) return tooltip; .return NULL; } //+------------------------------------------------------------------+
除了创建工具提示对象所需的参数外,该方法还接收创建的工具提示所附于的控件。 使用上述方法,将创建一个新的工具提示对象,并将其附于方法参数中指定的控件。 如果工具提示成功附加,则返回指向所创建对象的指针。 否则,返回 NULL。
在所有容器类文件中,对附加工具提示对象和创建进度条控件进行了相同的改进
TabControl.mqh, TabField.mqh, SplitContainer.mqh, SplitContainerPanel.mqh 和 GroupBox.mqh。 我不会在此处研究这些变化。
我们改进位于 \MQL5\Include\DoEasy\Collections\GraphElementsCollection.mqh 里的图形元素集合类。
我们看看应该如何处理工具提示对象。 这些对象与鼠标交互,并根据交互结果在图形元素集合的计时器中进行处理。 光标下的工具提示对象将添加到交互对象列表之中。 此列表应在计时器中处理。 在这种情况下,还应处理以前位于光标下的对象。 但由于过去的对象不需要淡出动画,因此从集合事件处理程序调用的方法会处理它 — 对象应该只是隐藏,并应为它设置非显示标志。 这种逻辑与活动对象的逻辑不同,活动对象的逻辑最初放置在活动元素列表中,并由其中的计时器不断处理。
在集合类的私有部分中,声明要处理的对象列表:
SDataPivotPoint m_data_pivot_point[]; // Pivot point data structure array CArrayObj m_list_charts_control; // List of chart management objects CListObj m_list_all_canv_elm_obj; // List of all graphical elements on canvas CListObj m_list_elm_to_process; // List of graphical elements on canvas for processing CListObj m_list_all_graph_obj; // List of all graphical objects CArrayObj m_list_deleted_obj; // List of removed graphical objects CMouseState m_mouse; // "Mouse status" class object
此外,声明处理在游标下的当前和过去对象的方法,以及处理对象列表的方法:
//--- Reset all interaction flags for all forms except the specified one void ResetAllInteractionExeptOne(CGCnvElement *form); //--- Post-processing of the former active form under the cursor void FormPostProcessing(CForm *form,const int id, const long &lparam, const double &dparam, const string &sparam); //--- Processing (1) the current and (2) previous ToolTip element void TooltipCurrProcessing(CForm *tooltip,CForm *base); void TooltipPrevProcessing(CForm *tooltip); //--- Add the element (1) to the collection list and (2) to the list for processing bool AddCanvElmToCollection(CGCnvElement *element); bool AddCanvElmToProcessList(CForm *element); //--- Get an element by name from the list for handling CForm *GetCanvElementFromProcessList(CForm *element); //--- Return the element index by name in the list for handling int CanvElementIndexInProcessList(CForm *element) const; //--- Add the element to the collectionl ist or return the existing one ENUM_ADD_OBJ_RET_CODE AddOrGetCanvElmToCollection(CGCnvElement *element,int &id); //--- Return the graphical elemnt index in the collection list int GetIndexGraphElement(const long chart_id,const string name);
在公开部分中,编写返回需处理的对象列表的方法:
public: //--- Return itself CGraphElementsCollection *GetObject(void) { return &this; } //--- Return the full collection list of standard graphical objects "as is" CArrayObj *GetListGraphObj(void) { return &this.m_list_all_graph_obj; } //--- Return (1) the complete collection list of graphical elements on canvas "as is", (2) the list of elements to be handled CArrayObj *GetListCanvElm(void) { return &this.m_list_all_canv_elm_obj;} CArrayObj *GetListCanvElmToProcess(void) { return &this.m_list_elm_to_process; } //--- Return the list of graphical elements by a selected (1) integer, (2) real and (3) string properties meeting the compared criterion CArrayObj *GetList(ENUM_CANV_ELEMENT_PROP_INTEGER property,long value,ENUM_COMPARER_TYPE mode=EQUAL) { return CSelect::ByGraphCanvElementProperty(this.GetListCanvElm(),property,value,mode); } CArrayObj *GetList(ENUM_CANV_ELEMENT_PROP_DOUBLE property,double value,ENUM_COMPARER_TYPE mode=EQUAL) { return CSelect::ByGraphCanvElementProperty(this.GetListCanvElm(),property,value,mode); } CArrayObj *GetList(ENUM_CANV_ELEMENT_PROP_STRING property,string value,ENUM_COMPARER_TYPE mode=EQUAL) { return CSelect::ByGraphCanvElementProperty(this.GetListCanvElm(),property,value,mode); }
并声明集合计时器事件处理程序:
//--- (1) Event handlers, (2) timer, (3) deinitialization void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam); void OnTimer(); void OnDeinit(void);
在类构造函数中,清除列表,并为其设置排序列表标志:
//+------------------------------------------------+ //| Constructor | //+------------------------------------------------+ CGraphElementsCollection::CGraphElementsCollection() { this.m_type=COLLECTION_GRAPH_OBJ_ID; this.m_name_prefix=this.m_name_program+"_"; ::ChartSetInteger(::ChartID(),CHART_EVENT_MOUSE_MOVE,true); ::ChartSetInteger(::ChartID(),CHART_EVENT_MOUSE_WHEEL,true); this.m_list_all_graph_obj.Type(COLLECTION_GRAPH_OBJ_ID); this.m_list_all_graph_obj.Sort(SORT_BY_CANV_ELEMENT_ID); this.m_list_all_graph_obj.Clear(); this.m_list_charts_control.Sort(); this.m_list_charts_control.Clear(); this.m_total_objects=0; this.m_is_graph_obj_event=false; this.m_list_deleted_obj.Clear(); this.m_list_deleted_obj.Sort(); this.m_list_all_canv_elm_obj.Clear(); this.m_list_all_canv_elm_obj.Sort(); this.m_list_elm_to_process.Clear(); this.m_list_elm_to_process.Sort(); } //+------------------------------------------------------------------+
该方法将画布上的图形元素添加到列表中以便进行后续处理:
//+------------------------------------------------------------------+ //| Add a graphical element on canvas to the list for handling | //+------------------------------------------------------------------+ bool CGraphElementsCollection::AddCanvElmToProcessList(CForm *element) { if(this.GetCanvElementFromProcessList(element)!=NULL) return false; if(!this.m_list_elm_to_process.Add(element)) { CMessage::ToLog(DFUN+element.TypeElementDescription()+element.Name()+": ",MSG_LIB_SYS_FAILED_OBJ_ADD_TO_LIST); return false; } return true; } //+------------------------------------------------------------------+
获取指向要处理的对象列表的指针。 如果传递对象失败,则在日志中通知,并返回 false。 若是成功添加后,则返回 true。
该方法从列表中按名称接收元素,并进行处理:
//+------------------------------------------------------------------+ //| Get an element by name from the list for handling | //+------------------------------------------------------------------+ CForm *CGraphElementsCollection::GetCanvElementFromProcessList(CForm *element) { for(int i=this.m_list_elm_to_process.Total()-1;i>WRONG_VALUE;i--) { CForm *obj=this.m_list_elm_to_process.At(i); if(obj==NULL) continue; if(obj.Name()==element.Name()) return obj; } .return NULL; } //+------------------------------------------------------------------+
依据要处理的对象列表循环,获取下一个对象。 如果其名称与传递给方法的对象名称相匹配,则返回指向找到对象的指针。 循环完成后,返回 NULL。 意即未发现对象。
该方法在列表中按名称返回元素的索引,以便进行后续处理:
//+------------------------------------------------------------------+ //| Return the index of an element by name in the list for handling | //+------------------------------------------------------------------+ int CGraphElementsCollection::CanvElementIndexInProcessList(CForm *element) const { for(int i=this.m_list_elm_to_process.Total()-1;i>WRONG_VALUE;i--) { CForm *obj=this.m_list_elm_to_process.At(i); if(obj==NULL) continue; if(obj.Name()==element.Name()) return i; } return WRONG_VALUE; } //+------------------------------------------------------------------+依据要处理的对象列表循环,获取下一个对象。 如果其名称与传递给该方法的对象名称匹配,则返回循环索引。 在循环结束时,返回 -1,这意味着未找到该对象。
该方法处理当前工具提示元素:
//+------------------------------------------------+ //| Handle the current ToolTip element | //+------------------------------------------------+ void CGraphElementsCollection::TooltipCurrProcessing(CForm *tooltip,CForm *base) { //--- If at least one empty object is passed, leave if(tooltip==NULL || base==NULL) return; //--- 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 shifted to the received cursor coordinates if(tooltip.Move(x,y)) { //--- Set new relative tooltip coordinates tooltip.SetCoordXRelative(tooltip.CoordX()-base.CoordX()); tooltip.SetCoordYRelative(tooltip.CoordY()-base.CoordY()); } } //+------------------------------------------------------------------+
代码注释中完整描述了方法逻辑。 该方法将工具提示对象移动到光标坐标,并在对象超出屏幕边缘时调整接收的坐标。
该方法处理上一个工具提示元素:
//+------------------------------------------------+ //| Handle the previous ToolTip element | //+------------------------------------------------+ void CGraphElementsCollection::TooltipPrevProcessing(CForm *tooltip) { //--- If an empty object is passed, leave if(tooltip==NULL) return; //--- Set the object non-display flag, make it completely transparent and hide it tooltip.SetDisplayed(false); tooltip.SetOpacity(0,false); tooltip.SetDisplayState(CANV_ELEMENT_DISPLAY_STATE_NORMAL); tooltip.Hide(); //--- Get the index of the object in the list int index=this.CanvElementIndexInProcessList(tooltip); //--- If the index is received, remove the object from the list if(index>WRONG_VALUE) this.m_list_elm_to_process.DetachElement(index); } //+------------------------------------------------------------------+
此处,一切都非常透明。 上一个对象当然已不在光标之下。 这意味着应立即隐藏它,并应从要处理的对象列表中删除它的指针。
如果在上一篇文章中测试的智能系统在图表上启动,并且图表周期更改为另一个图表周期,则程序将会以严重错误结束。 发生这种情况是因为指向窗体对象的指针被声明为静态:
//--- Declare static variables for the active form and status flags static CForm *form=NULL;
这意味着当更改图表的周期时,数据将保留在其中,NULL 检查将产生否定结果,表示对象有效:
//--- In case of the mouse movement event if(id==CHARTEVENT_MOUSE_MOVE) { //--- If the cursor is above the form if(form!=NULL) { //--- If the move flag is set
指针中的数据集将不再引用此对象的内存区域。 故此,当访问内存中的错误位置时,程序会终止。 修复错误很容易:检查指针的有效性,而不是其值:
//--- In case of the mouse movement event if(id==CHARTEVENT_MOUSE_MOVE) { //--- If the cursor is above the form if(::CheckPointer(form)!=POINTER_INVALID) { //--- If the move flag is set if(move) {
处理以前和当前工具提示的方式按如下实现:
//--- 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 && tooltip.TypeGraphElement()==GRAPH_ELEMENT_TYPE_WF_TOOLTIP) { //--- If the previous ToolTip object exists if(::CheckPointer(tooltip_prev)!=POINTER_INVALID && tooltip_prev.TypeGraphElement()==GRAPH_ELEMENT_TYPE_WF_TOOLTIP) { //--- If the name of the previous ToolTip is not equal to the name of the current one, hide the previous ToolTip object if(tooltip_prev.Name()!=tooltip.Name()) this.TooltipPrevProcessing(tooltip_prev); } //--- Get the base object set in ToolTip CForm *base=tooltip.GetBase(); //--- If the base object is received //--- 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!=NULL && base.Name()==form.Name()) { //--- Add ToolTip to the handling list, shift it to the cursor coordinates //--- and write the current ToolTip to the variable as the previous one if(this.AddCanvElmToProcessList(tooltip)) { this.TooltipCurrProcessing(tooltip,base); tooltip_prev=tooltip; } } } //--- If there is no current ToolTip object, but the previous ToolTip exists, hide the previous ToolTip object else if(::CheckPointer(tooltip_prev)!=POINTER_INVALID && tooltip_prev.TypeGraphElement()==GRAPH_ELEMENT_TYPE_WF_TOOLTIP) this.TooltipPrevProcessing(tooltip_prev); //--- 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); }
所有逻辑在代码的注释中都有足够详细的讲述。 简而言之,如果对象在光标下方,则将其添加到列表中进行处理,并且其坐标将移动到光标坐标。 如果对象不在光标下方,则会将其从列表中删除,并隐藏。
集合计时器事件处理程序:
//+------------------------------------------------+ //| Timer | //+------------------------------------------------+ void CGraphElementsCollection::OnTimer(void) { //--- Handle the elements under the cursor //--- In the loop by the list of objects for handling for(int i=this.m_list_elm_to_process.Total()-1;i>WRONG_VALUE;i--) { //--- get the next object from the list CForm *obj=this.m_list_elm_to_process.At(i); if(obj==NULL) continue; //--- If the state of the object is the full loop is completed if(obj.DisplayState()==CANV_ELEMENT_DISPLAY_STATE_COMPLETED) { //--- remove the pointer to the object from the list and go to the next iteration this.m_list_elm_to_process.DetachElement(i); continue; } //--- Call the object timer event handler obj.OnTimer(); } //--- Work with active elements of the collection //--- In the loop by the collection list of graphical elements for(int i=0;i<this.m_list_all_canv_elm_obj.Total();i++) { //--- Get the next object from the list CWinFormBase *obj=this.m_list_all_canv_elm_obj.At(i); if(obj==NULL) continue; //--- Get the list of active elements from the object CArrayObj *list=obj.GetListMainActiveElements(); if(list==NULL || list.Total()==0) continue; //--- In the loop by the list of active elements for(int j=0;j<list.Total();j++) { //--- get the next object and CWinFormBase *elm=list.At(j); if(elm==NULL) continue; //--- call the object timer event handler elm.OnTimer(); } } } //+------------------------------------------------------------------+
处理程序逻辑在代码的注释中进行了完整讲述。 其在两个循环中工作 — 要处理的对象列表(工具提示),和活动元素列表(具有可视动画效果的对象)
在 \MQL5\Include\DoEasy\Engine.mqh 的 CEngine 函数库的主对象中,创建函数库图形元素的计时器,和计时器事件处理程序。
在私密部分中,声明处理程序:
//--- Handling events of (1) orders, deals and positions, (2) accounts, (3) graphical objects and (4) canvas elements void TradeEventsControl(void); void AccountEventsControl(void); void GraphObjEventsControl(void); void GraphElmEventsControl(void); //--- (1) Working with a symbol collection and (2) symbol list events in the market watch window
在类构造函数中,创建另一个计时器 — 图形元素集合计时器:
//+------------------------------------------------+ //| CEngine constructor | //+------------------------------------------------+ CEngine::CEngine() : m_first_start(true), m_last_trade_event(TRADE_EVENT_NO_EVENT), m_last_account_event(WRONG_VALUE), m_last_symbol_event(WRONG_VALUE), m_global_error(ERR_SUCCESS) { this.m_is_hedge=#ifdef __MQL4__ true #else bool(::AccountInfoInteger(ACCOUNT_MARGIN_MODE)==ACCOUNT_MARGIN_MODE_RETAIL_HEDGING) #endif; this.m_is_tester=::MQLInfoInteger(MQL_TESTER); this.m_program_type=(ENUM_PROGRAM_TYPE)::MQLInfoInteger(MQL_PROGRAM_TYPE); this.m_name_program=::MQLInfoString(MQL_PROGRAM_NAME); this.m_name_prefix=this.m_name_program+"_"; this.m_list_counters.Sort(); this.m_list_counters.Clear(); this.CreateCounter(COLLECTION_ORD_COUNTER_ID,COLLECTION_ORD_COUNTER_STEP,COLLECTION_ORD_PAUSE); this.CreateCounter(COLLECTION_ACC_COUNTER_ID,COLLECTION_ACC_COUNTER_STEP,COLLECTION_ACC_PAUSE); this.CreateCounter(COLLECTION_SYM_COUNTER_ID1,COLLECTION_SYM_COUNTER_STEP1,COLLECTION_SYM_PAUSE1); this.CreateCounter(COLLECTION_SYM_COUNTER_ID2,COLLECTION_SYM_COUNTER_STEP2,COLLECTION_SYM_PAUSE2); this.CreateCounter(COLLECTION_REQ_COUNTER_ID,COLLECTION_REQ_COUNTER_STEP,COLLECTION_REQ_PAUSE); this.CreateCounter(COLLECTION_TS_COUNTER_ID,COLLECTION_TS_COUNTER_STEP,COLLECTION_TS_PAUSE); this.CreateCounter(COLLECTION_IND_TS_COUNTER_ID,COLLECTION_IND_TS_COUNTER_STEP,COLLECTION_IND_TS_PAUSE); this.CreateCounter(COLLECTION_TICKS_COUNTER_ID,COLLECTION_TICKS_COUNTER_STEP,COLLECTION_TICKS_PAUSE); this.CreateCounter(COLLECTION_CHARTS_COUNTER_ID,COLLECTION_CHARTS_COUNTER_STEP,COLLECTION_CHARTS_PAUSE); this.CreateCounter(COLLECTION_GRAPH_OBJ_COUNTER_ID,COLLECTION_GRAPH_OBJ_COUNTER_STEP,COLLECTION_GRAPH_OBJ_PAUSE); this.CreateCounter(COLLECTION_GRAPH_ELM_COUNTER_ID,COLLECTION_GRAPH_ELM_COUNTER_STEP,COLLECTION_GRAPH_ELM_PAUSE); ::ResetLastError(); #ifdef __MQL5__ if(!::EventSetMillisecondTimer(TIMER_FREQUENCY)) { ::Print(DFUN_ERR_LINE,CMessage::Text(MSG_LIB_SYS_FAILED_CREATE_TIMER),(string)::GetLastError()); this.m_global_error=::GetLastError(); } //---__MQL4__ #else if(!this.IsTester() && !::EventSetMillisecondTimer(TIMER_FREQUENCY)) { ::Print(DFUN_ERR_LINE,CMessage::Text(MSG_LIB_SYS_FAILED_CREATE_TIMER),(string)::GetLastError()); this.m_global_error=::GetLastError(); } #endif //--- } //+------------------------------------------------------------------+
在计时器事件处理程序中,添加处理图形元素计时器的代码模块:
//+------------------------------------------------+ //| CEngine timer | //+------------------------------------------------+ void CEngine::OnTimer(SDataCalculate &data_calculate) { //--- If this is not a tester, work with collection events by timer if(!this.IsTester()) { //--- Timer of the collections of historical orders and deals, as well as of market orders and positions int index=this.CounterIndex(COLLECTION_ORD_COUNTER_ID); CTimerCounter* cnt1=this.m_list_counters.At(index); if(cnt1!=NULL) { //--- If unpaused, work with the order, deal and position collections events if(cnt1.IsTimeDone()) this.TradeEventsControl(); } //--- Account collection timer index=this.CounterIndex(COLLECTION_ACC_COUNTER_ID); CTimerCounter* cnt2=this.m_list_counters.At(index); if(cnt2!=NULL) { //--- If unpaused, work with the account collection events if(cnt2.IsTimeDone()) this.AccountEventsControl(); } //--- Timer 1 of the symbol collection (updating symbol quote data in the collection) index=this.CounterIndex(COLLECTION_SYM_COUNTER_ID1); CTimerCounter* cnt3=this.m_list_counters.At(index); if(cnt3!=NULL) { //--- If the pause is over, update quote data of all symbols in the collection if(cnt3.IsTimeDone()) this.m_symbols.RefreshRates(); } //--- Timer 2 of the symbol collection (updating all data of all symbols in the collection and tracking symbl and symbol search events in the market watch window) index=this.CounterIndex(COLLECTION_SYM_COUNTER_ID2); CTimerCounter* cnt4=this.m_list_counters.At(index); if(cnt4!=NULL) { //--- If the pause is over if(cnt4.IsTimeDone()) { //--- update data and work with events of all symbols in the collection this.SymbolEventsControl(); //--- When working with the market watch list, check the market watch window events if(this.m_symbols.ModeSymbolsList()==SYMBOLS_MODE_MARKET_WATCH) this.MarketWatchEventsControl(); } } //--- Trading class timer index=this.CounterIndex(COLLECTION_REQ_COUNTER_ID); CTimerCounter* cnt5=this.m_list_counters.At(index); if(cnt5!=NULL) { //--- If unpaused, work with the list of pending requests if(cnt5.IsTimeDone()) this.m_trading.OnTimer(); } //--- Timeseries collection timer index=this.CounterIndex(COLLECTION_TS_COUNTER_ID); CTimerCounter* cnt6=this.m_list_counters.At(index); if(cnt6!=NULL) { //--- If the pause is over, work with the timeseries list (update all except the current one) if(cnt6.IsTimeDone()) this.SeriesRefreshAllExceptCurrent(data_calculate); } //--- Timer of timeseries collection of indicator buffer data index=this.CounterIndex(COLLECTION_IND_TS_COUNTER_ID); CTimerCounter* cnt7=this.m_list_counters.At(index); if(cnt7!=NULL) { //--- If the pause is over, work with the timeseries list of indicator data (update all except for the current one) if(cnt7.IsTimeDone()) this.IndicatorSeriesRefreshAll(); } //--- Tick series collection timer index=this.CounterIndex(COLLECTION_TICKS_COUNTER_ID); CTimerCounter* cnt8=this.m_list_counters.At(index); if(cnt8!=NULL) { //--- If the pause is over, work with the tick series list (update all except the current one) if(cnt8.IsTimeDone()) this.TickSeriesRefreshAllExceptCurrent(); } //--- Chart collection timer index=this.CounterIndex(COLLECTION_CHARTS_COUNTER_ID); CTimerCounter* cnt9=this.m_list_counters.At(index); if(cnt9!=NULL) { //--- If unpaused, work with the chart list if(cnt9.IsTimeDone()) this.ChartsRefreshAll(); } //--- Graphical objects collection timer index=this.CounterIndex(COLLECTION_GRAPH_OBJ_COUNTER_ID); CTimerCounter* cnt10=this.m_list_counters.At(index); if(cnt10!=NULL) { //--- If unpaused, work with the list of graphical objects if(cnt10.IsTimeDone()) this.GraphObjEventsControl(); } //--- The timer for the collection of graphical elements on the canvas index=this.CounterIndex(COLLECTION_GRAPH_ELM_COUNTER_ID); CTimerCounter* cnt11=this.m_list_counters.At(index); if(cnt11!=NULL) { //--- If the pause is over, work with the list of graphical elements on the canvas if(cnt11.IsTimeDone()) this.GraphElmEventsControl(); } } //--- If this is a tester, work with collection events by tick else { //--- work with events of collections of orders, deals and positions by tick this.TradeEventsControl(); //--- work with events of collections of accounts by tick this.AccountEventsControl(); //--- update quote data of all collection symbols by tick this.m_symbols.RefreshRates(); //--- work with events of all symbols in the collection by tick this.SymbolEventsControl(); //--- work with the list of pending orders by tick this.m_trading.OnTimer(); //--- work with the timeseries list by tick this.SeriesRefresh(data_calculate); //--- work with the timeseries list of indicator buffers by tick this.IndicatorSeriesRefreshAll(); //--- work with the list of tick series by tick this.TickSeriesRefreshAll(); //--- work with the list of graphical objects by tick this.GraphObjEventsControl(); //--- work with the list of graphical elements on canvas by tick this.GraphElmEventsControl(); } } //+------------------------------------------------------------------+
画布上图形元素的事件处理方法:
//+------------------------------------------------------------------+ //| Event handling method for graphical elements on canvas | //+------------------------------------------------------------------+ void CEngine::GraphElmEventsControl(void) { this.m_graph_objects.OnTimer(); } //+------------------------------------------------------------------+
一旦图形元素集合的暂停计时器结束,就会调用此方法,该方法又调用图形元素集合类的计时器处理程序。 默认调用将每 16 毫秒发生一次(计时器暂停)。
一切都准备好,可以进行测试了。
测试
为了执行测试,我将延用上一篇文章中的 EA,并将其保存在 \MQL5\Experts\TestDoEasy\Part126\ 中,命名为 TestDoEasy126.mq5。
在 OnInit() EA 处理程序中,在 SplitContainer 控件的 TabControl 的第一个选项卡的第二个面板上,创建进度条控件:
//--- On each of the control panels... for(int j=0;j<2;j++) { //--- Get the panel by loop index CSplitContainerPanel *panel=split_container.GetPanel(j); if(panel==NULL) continue; //--- set its description for the panel panel.SetDescription(TextByLanguage("Панель","Panel")+string(j+1)); //--- If this is the first tab and the second panel if(n==0 && j==1) { //--- Create the ProgressBar control on it if(split_container.CreateNewElement(j,GRAPH_ELEMENT_TYPE_WF_PROGRESS_BAR,4,4,100,12,clrNONE,255,false,false)) { CProgressBar *progress_bar=split_container.GetPanelElementByType(j,GRAPH_ELEMENT_TYPE_WF_PROGRESS_BAR,0); if(progress_bar!=NULL) { Print(DFUN,progress_bar.TypeElementDescription()," ",progress_bar.Name()); } } } //--- ...create a text label with the panel name if(split_container.CreateNewElement(j,GRAPH_ELEMENT_TYPE_WF_LABEL,4,4,panel.Width()-8,panel.Height()-8,clrDodgerBlue,255,true,false)) { CLabel *label=split_container.GetPanelElementByType(j,GRAPH_ELEMENT_TYPE_WF_LABEL,0); if(label==NULL) continue; label.SetTextAlign(ANCHOR_CENTER); label.SetText(panel.Description()); } }
若要确保已创建对象,获取指向该对象的指针,并将其说明发送到日志。
编译 EA,并在图表上启动它:
当前文章声明的功能运行良好。 我们可以在图表注释中看到一个不断变化的数字。 这是来进度条控件的计时器中的数据。
下一步是什么?
在下一篇文章中,我将继续处理进度条对象。
当前 MQL5 版本的函数库、测试 EA、和图表事件控制指标的所有文件均附于文后。
*该系列的前几篇文章:
DoEasy. 控件 (第 20 部分): SplitContainer WinForms 对象
DoEasy. 控件 (第 21 部分): SplitContainer 控件 面板隔板
DoEasy. 控件 (第 22 部分): SplitContainer。 修改已创建对象的属性
DoEasy. 控件 (第 23 部分): 改进 TabControl 和 SplitContainer WinForms 对象
DoEasy. 控件 (第 24 部分): 提示(Hint)辅助 WinForms 对象
DoEasy. 控件 (第 25 部分): 工具提示(Tooltip) WinForms 对象
本文由MetaQuotes Ltd译自俄文
原文地址: https://www.mql5.com/ru/articles/11732