
DoEasy. Steuerung (Teil 26): Fertigstellung des WinForms-Objekts ToolTip und Weiterführung der Entwicklung der ProgressBar
Inhalt
Konzept
Im letzten Artikel habe ich mit der Entwicklung des Steuerelements SplitContainer begonnen. Der Tooltip sollte nach einer kurzen Pause erscheinen, wenn sich der Mauszeiger über ein Objekt bewegt. Ein zuvor angezeigter Tooltip sollte ausgeblendet werden. Wenn wir den Cursor nach dem Erscheinen des Tooltips an der gleichen Stelle belassen, sollte er nach einer gewissen Zeit verschwinden. Im vorigen Artikel habe ich dafür gesorgt, dass der Tooltip sofort erscheint, wenn man den Mauszeiger über das Objekt bewegt, dem sie zugewiesen wurde. Im aktuellen Artikel werde ich weiter an dem Verhalten der QuickInfo arbeiten und es konsistenter mit dem Verhalten von Programmen machen, die in MS Visual Studio kompiliert wurden. Wenn sich der Mauszeiger über ein Objekt bewegt, sollte erst nach einer Pause der Tooltip ohne Unterbrechung auf dem Bildschirm erscheinen. Wenn sich der Cursor vom Objekt entfernt, wird der Tooltip ausgeblendet. Wenn der Mauszeiger auf dem Objekt stehen bleibt, verschwindet der Tooltip nach einiger Zeit wieder. Um dieses Verhalten zu implementieren, müssen wir den Timer verwenden, der für die grafischen Elemente der Kollektion erstellt wurde. Für jedes Objekt wird eine virtuelle Ereignisbehandlung der Zeit, ein Timer, definiert. Die Implementierung des Timers sollte speziell in den Klassen derjenigen Objekte erfolgen, die in ihm verarbeitet werden sollen.
Damit die Bibliothek versteht, welche Objekte im Timer verarbeitet werden sollen und welche nicht, werde ich eine Liste der aktiven Objekte erstellen. Sie enthält die Zeiger auf die Objekte, die im Timer verarbeitet werden müssen. Dadurch wird es einfacher, den Überblick über die zu verarbeitenden Objekte zu behalten, und wir müssen nicht ständig alle grafischen Elemente in einer Schleife überprüfen. Die Schleife soll sich nur auf die der Liste hinzugefügten Objekte beziehen. Auf diese Weise wird es möglich sein, die grafische Schnittstelle zu animieren. Die Objekte reagieren nicht nur auf die Interaktion mit der Maus, sondern verwenden auch die für sie festgelegte Animationssequenz für visuelle Effekte. Auf diese Weise lassen sich auch gewöhnliche animierte Icons leicht erstellen und verwenden. Es reicht aus, ein Objekt mit Animationsbildern zu erstellen und es in die Liste der aktiven Bibliotheksobjekte aufzunehmen. In der Ereignisbehandlung des Timers eines solchen Objekts werden wir einfach die Animationsbilder, die bei der Erstellung des Objekts erstellt wurden, abwechselnd anzeigen. Heute werde ich einen Timer für das ToolTip-Steuerelement erstellen. Dieser Timer implementiert das oben beschriebene Verhalten des Tooltips. Da dieses Verhalten eine Interaktion mit der Maus impliziert, werden wir sowohl in der Ereignisbehandlung der Kollektion grafischer Elemente als auch im Timer der Kollektion mit ihnen arbeiten.
Nach der Fertigstellung des WinForms-Objekts ToolTip werde ich mit der Entwicklung des ProgressBar-Steuerelements beginnen. Ich werde nur eine statische Version des Objekts erstellen - seine Eigenschaften und sein Aussehen. Seine Funktionsweise wird im nächsten Artikel erläutert.
Verbesserung der Bibliotheksklassen
In \MQL5\Include\DoEasy\Defines.mqh fügen wir die Zeitgeberparameter für grafische Elemente auf der Leinwand hinzu:
//--- 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
Jede Kollektion von Bibliotheksobjekten hat ihren eigenen Timer mit einem eigenen Satz von Parametern - Pause, Erhöhung des Timer-Zählers und seiner ID. Ich habe hier genau die gleichen Parameter für den neuen Timer hinzugefügt.
Fügen wir die Standardwerte für das neue ProgressBar-Steuerelement hinzu:
#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
Für einige grafische Kontrollen sind die Elemente der visuellen Effekte erforderlich. Zum Beispiel soll die Fortschrittsanzeige eine Blende haben, die entlang des Balkens verläuft, wie sie in Windows implementiert ist. Das Objekt, das die Blende umsetzt, wird über den Balken gelegt und bewegt sich an ihm entlang. Wir müssen den Objekttyp in der Liste der Bibliotheksobjekttypen festlegen:
//+------------------------------------------------+ //| 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
Fügen wir die neuen Typen von Grafikelementen auf der Leinwand zur Liste der Grafikelementtypen hinzu:
//+------------------------------------------------+ //| 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 }; //+------------------------------------------------------------------+
Ein Steuerelement (hier ToolTip) kann mehrere Zustände haben, wenn es mit der Maus interagiert. Da das Objekt animiert ist, sollten sein Verhalten und sein Zustand beschrieben werden. Er kann auf den Beginn des Einblendens warten, sich im Einblendzustand befinden, auf den Beginn des Ausblendens warten, sich im Ausblendzustand befinden oder sich im Normalzustand befinden.
Beschreiben wir seine Zustände in der neuen Liste der Kontrollanzeigezustände:
//+------------------------------------------------+ //| 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 }; //+------------------------------------------------------------------+
Solche Zustände können auf andere Kontrollen angewendet werden. Für sie können später Animationssequenzen erstellt werden. Außerdem ist es nicht notwendig, dass ein Objekt ein- oder ausgeblendet wird. Die Beschreibung eines Objekts, das eingeblendet wird, kann mit der Erweiterung der Dropdown-Liste gleichgesetzt werden, während der Zustand des allmählichen Ausblendens (Dämpfung) - mit dem Zustand zusammengeklappt gleichgesetzt werden kann. Wir können diese Liste jedoch später jederzeit erweitern.
Die ProgressBar kann drei Zeichenstile des Fortschrittsbalkens haben. Wir listen sie in der Enumeration auf:
//+------------------------------------------------+ //| 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 | //+------------------------------------------------------------------+
Ich werde hier nur den durchgehenden Balken implementieren.
Wenn für ein Objekt eine animierte Anzeige implementiert ist, sollte sein Zustand gespeichert und über seine Eigenschaften abgerufen werden.
Fügen wir die neuen Eigenschaften des grafischen Elements zur Aufzählung der ganzzahligen Eigenschaften des grafischen Elements auf der Leinwand hinzu und erhöhen wir die Gesamtzahl der ganzzahligen Eigenschaften von 129 auf 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 //+------------------------------------------------------------------+
Wir sortieren nach neuer Eigenschaft zur Enumeration möglicher Kriterien zum Sortieren von grafischen Elementen auf der Leinwand hinzufügen:
//+------------------------------------------------------------------+ //| 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 }; //+------------------------------------------------------------------+
Jetzt können wir alle grafischen Elemente nach neuen Eigenschaften sortieren und auswählen.
In \MQL5\Include\DoEasy\Data.mqh wurden neuen Nachrichtenindizes hinzugefügt:
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 }; //+------------------------------------------------------------------+
und die Textnachrichten, die den neu hinzugefügten Indizes entsprechen:
{"Не установлен","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"}, }; //+---------------------------------------------------------------------+
Bei der Erstellung von ObjektKollektionen verwenden wir die Klasse der Liste, die von der Klasse des dynamischen Arrays von Zeigern auf Instanzen der Klasse CObject und ihrer Nachkommen. Die Klasse CArrayObj verfügt über die Methode Detach(), die den Zeiger aus der Liste abruft und den erhaltenen Zeiger zurückgibt. Wir brauchen den Zeiger nicht immer zu verwenden, nachdem wir ihn aus der Liste extrahiert haben.
Daher erstellen wir in der abgeleiteten Klasse in \MQL5\Include\DoEasy\Collections\ListObj.mqh die Methode, die den Zeiger aus der Liste entfernt, ohne ihn zurückzugeben:
//+------------------------------------------------------------------+ //| 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; } }; //+------------------------------------------------------------------+
Hier wird einfach der Zeiger aus der Liste abgerufen. Wenn dies nicht gelingt, wird false zurückgegeben.
Bei erfolgreichem Entfernen wird der resultierende Zeiger zurückgesetzt und true zurückgegeben.
Lassen Sie uns die Klasse des Pausenobjekts in \MQL5\Include\DoEasy\Services\Pause.mqh leicht abändern.
Wir fügen die öffentliche Methode hinzu, die den Beginn des Pausen-Countdowns in der Anzahl der Millisekunden zurückgibt, die seit dem Start des Systems vergangen sind:
//--- 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; }
Wir benötigen die Methode, wenn wir Steuerelemente im Timer behandeln.
Da ich neue Integer-Eigenschaften des grafischen Elements hinzugefügt habe, müssen wir sie im Basisobjekt des grafischen Elements initialisieren und neue Felder der Objektstruktur hinzufügen.
In \MQL5\Include\DoEasy\Objects\Graph\GCnvElement.mqh fügen wir die neue Eigenschaften zu der Objektstruktur hinzu:
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
Wir benötigen die Struktur des Objekts, um das Objekt korrekt in einer Datei zu speichern und anschließend wiederherzustellen.
Im Methodenblock für den vereinfachten Zugriff auf die Objekteigenschaften legen wir die Methoden für die Behandlung dieser neuen Eigenschaften fest:
//--- (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); }
Die Set-Methoden der Eigenschaften setzen die Werte, die ihnen in den Objekteigenschaften übergeben werden, während die Return-Methoden die zuvor gesetzten Werte aus den Objekteigenschaften zurückgeben.
Legen wir die Standardwerte für die neuen Eigenschaften in beiden Konstruktoren fest:
//+------------------------------------------------------------------+ //| 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()); } } //+------------------------------------------------------------------+
Fügen wir in der Methode, die die Objektstruktur erstellt, das Schreiben der Objekteigenschaften zu den Strukturfeldern hinzu:
//+------------------------------------------------+ //| 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; } //+------------------------------------------------------------------+
In der Methode, die ein Objekt aus einer Struktur erstellt, implementieren wir das Setzen von Werten in den Objekteigenschaften aus den Strukturfeldern:
//+------------------------------------------------+ //| 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 } //+------------------------------------------------------------------+
In \MQL5\Include\DoEasy\Objects\Graph\GBaseObj.mqh der Basisobjektklasse aller grafischen Bibliotheksobjekte, fügen wir die Rückgabe der Beschreibung neuer Objekttypen der Methode hinzu, die die Beschreibung des grafischen Elementtyps zurückgibt:
//+------------------------------------------------------------------+ //| 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" ); } //+------------------------------------------------------------------+
Ich habe alle diese Objekte bereits deklariert, aber ich werde sie etwas später erstellen - nach der Fertigstellung der ToolTip-Objektklasse.
Um mit grafischen Elementen zu arbeiten, müssen wir einen Ereignisbehandlung in Form des Timers erstellen. Da alle grafischen Elemente von der Klasse der Formularobjekte geerbt werden, wird in dieser Klasse der virtuelle Timer deklariert.
Schreiben wir einen virtuellen Timer im öffentlichen Abschnitt in \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; }
Der Timer tut nichts. Er muss in den Klassen, in denen die Ereignisse des Timers behandelt werden sollen, neu definiert werden.
Wir deklarieren die Methode, die die Anzahl der an das Element angehängten ToolTip-Objekte zurückgibt:
//--- 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);
Einem grafischen Objekt kann nur ein Tooltip-Objekt zugewiesen werden. Wir können jedoch mehrere ToolTips an ein Objekt anhängen und die erstellten ToolTips anderen Objekten zuweisen, die mit dem Objekt verbunden sind. Dies geschieht, weil nicht alle Bibliotheksobjekte ToolTip-Objekte an sich selbst anhängen können. Wir können jedoch jederzeit Tooltips in dem Container erstellen, an den Objekte angehängt sind, und diesen Objekten dann Tooltips zuweisen. Die Methode ermöglicht es uns, die Anzahl der erstellten und an den Container angehängten ToolTip-Objekte zu ermitteln.
Verfeinern wir nun die Methode, die das angegebene ToolTip-Objekt an ein Objekt bindet.
Wir fügen zusätzlicher Prüfungen auf ungültigen Typ hinzu und auf die Anzahl der angehängten Tooltips, die größer als Null ist:
//+------------------------------------------------------------------+ //| 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; } //+------------------------------------------------------------------+
Wenn versehentlich versucht wird, ein Objekt eines anderen Typs anstelle eines Tooltip-Objekts anzuhängen, wird die Methode einen Fehler melden. Wenn ToolTip bereits an das Objekt angehängt ist, wird ebenfalls eine Fehlermeldung angezeigt und die Methode gibt false zurück.
Die Methode gibt die Anzahl der ToolTip-Objekte zurück:
//+------------------------------------------------+ //| 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; } //+------------------------------------------------------------------+
Handelt es sich bei diesem Objekt um einen Container, so kann eine beliebige Anzahl von Tooltip-Objekten an ihn angehängt werden. Die Methode gibt ihre Nummer zurück. Ermittelt das nächste Objekt in der Schleife anhand der Gesamtzahl der angehängten Objekte. Wenn es sich bei dem Objekttyp um einen Tooltip handelt, erhöhen wir den Objektzähler (die Variable res). Am Ende der Schleife geben wir das Ergebnis der Berechnung zurück, das in res.
Der Timer für die Kollektion grafischer Elemente wird alle 16 Millisekunden aktualisiert. Um die QuickInfo innerhalb von z. B. einer Sekunde ein- oder auszublenden, müssen wir 1000 Millisekunden durch den Aktualisierungszeitraum des Timers teilen. Folglich müssen wir die Deckkraft des Objekts etwa alle 1000/16=62,5 Millisekunden ändern.
In \MQL5\Include\DoEasy\Objects\Graph\WForms\Common Controls\ToolTip.mqh deklarieren wir die Variable zum Speichern des Transparenzänderungsschritts:
//+------------------------------------------------------------------+ //| 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:
Im öffentlichen Abschnitt der Klasse deklarieren wir den virtuellen Timer:
//--- 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); }; //+------------------------------------------------------------------+
In jedem der Konstruktoren berechnen wir den Wert der Variablen, die den Transparenzänderungsschritt speichert, auf der Grundlage der Standardwerte für die Ein-/Ausblenddauer und den Schritt des Timerzählers der GrafikelementKollektion:
//+------------------------------------------------+ //| 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); } //+------------------------------------------------------------------+
In der Variableninitialisierungsmethode die Standardwerte für die Verzögerungen geben wir beim Ändern der Zustände von Tooltip-Objekten ein:
//+------------------------------------------------+ //| 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(); } //+------------------------------------------------------------------+
In der Methode, die den Tooltip anzeigt, verschieben wir den Befehl zur Anzeige des Objekts an das Ende und entfernen das komplette Neuzeichnen des gesamten Objekts:
//+------------------------------------------------+ //| 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); } //+------------------------------------------------------------------+
Die Methode sieht nun wie folgt aus:
//+------------------------------------------------+ //| 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(); } //+------------------------------------------------------------------+
Durch diese Verfeinerung wird das unangenehme „Blinken“ des Elements bei der schrittweisen Änderung seiner Deckkraft etwas reduziert.
Fügen wir in der Tooltip-Methode ganz am Ende eine Aktualisierung der geänderten Leinwand hinzu:
//+------------------------------------------------+ //| 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(); } //+------------------------------------------------------------------+
Beim Ändern der Deckkraft auf Null (das Objekt ist vollständig transparent), bemerkte ich ein seltsames Verhalten von Methoden, die Primitive mit Anti-Aliasing zeichnen - sie reagieren schlecht auf den Transparenzwert. Bei einem sehr niedrigen Deckkraftwert heben sich die mit solchen Methoden gezeichneten Linien zu stark vom Hintergrund eines Objekts ab, das auf genau denselben Deckkraftwert eingestellt ist. Das Hinzufügen einer Zeile zur Aktualisierung der Leinwand ist einer der Versuche, diesen störenden Effekt zu verringern. Es funktioniert jedoch noch nicht...
Ereignisbehandlung durch den Timer:
//+------------------------------------------------+ //| 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); } } //+------------------------------------------------------------------+
Die Methodenlogik wird in den Codekommentaren beschrieben. Der Handler wird jedes Mal aufgerufen, wenn der Objekttimer abläuft. Bei jeder Iteration des Zeitgebers müssen wir den Zustand des Objekts und seine Warte-/Zustandsänderungszähler überprüfen.
Zunächst wird der normale Zustand des Objekts gespeichert. In diesem Zustand ist der Tooltip vollständig ausgeblendet. Wenn sich das Objekt in diesem Zustand befindet, sollte es zunächst einige Zeit warten, bevor es mit der Einblendung beginnt. Wenn wir also den normalen Zustand des Objekts sehen, schreiben wir ihm den Zustand des Wartens auf die Einblendung vor, starten den Countdown der Wartezeit und verlassen die Methode.
Bei der nächsten Iteration sehen wir einen neuen Zustand (wir warten auf die Einblendung) und überprüfen dementsprechend den Zähler. Wenn die Wartezeit abgelaufen ist, versetzen wir das Objekt in den Einblendungszustand und warten bis zum nächsten Methodenaufruf. Beim nächsten Durchlauf beginnen wir bereits mit dem Prozess der Einblendung des Objekts. Hier müssen wir die Deckkraft des Objekts überprüfen und sie um den Änderungsschritt erhöhen. Dies geschieht bei jeder Iteration der Timer-Operation. Sobald das Objekt vollständig undurchsichtig wird, setzen wir es in einen neuen Zustand - und warten auf den sanften Ausblendungsprozess. Schließlich sollte das Objekt, das nach etwa fünf Sekunden erscheint, auch wieder verschwinden.
Der Prozess des Ausblendens ist identisch mit dem des Einblendens. Am Ende des vollständigen Zyklus wird der Zustand des Änderungszyklusendes auf das Objekt gesetzt. Wenn wir ihm sofort einen normalen Zustand zuweisen, wird der gesamte Zyklus neu gestartet, da der normale Zustand des Objekts als Startbedingung für den Beginn dieses Zyklus dient. Er sollte erst dann in das Objekt geschrieben werden, wenn der Zeiger auf das Objekt aus der Liste der im Zeitgeber zu behandelnden Objekte entfernt wird. Dies geschieht in der Kollektionsklasse der grafischen Elemente. Wenn das Objekt den Status ‚Zyklus vollständig durchlaufen‘ hat, entfernt die Methode den Zeiger auf das Objekt aus der Bearbeitungsliste und versetzt das Objekt selbst in seinen normalen Zustand. Wenn wir das nächste Mal mit dem Mauszeiger über ein Objekt fahren, wird es wieder in die Liste zur Bearbeitung im Timer aufgenommen. Auf die gleiche Weise wird der Objektzeiger aus der Liste entfernt, wenn der Cursor aus dem Bereich des Objekts bewegt wird, der für den Bearbeitungszyklus bereit ist, oder sogar während des Zyklus.
Allmählich nähern wir uns der Erstellung des neuen WinForms-Objekts der ProgressBar . Für dieses Steuerelement müssen wir das Hilfsobjekt Glare (Blende) erstellen. Die Objekte dieses Typs dienen dazu, einige GUI-Elemente visuell zu dekorieren, während im ProgressBar-Objekt die Blende entlang des Fortschrittsbalkens verlaufen soll. Das Konzept sieht folgendermaßen aus: Wir zeichnen einen weißen Fleck in der gewünschten Form und Größe und verwischen ihn von der Mitte zu den Rändern hin. Das Objekt der Blende ist ebenso halbtransparent wie das Schattenobjekt. Hier müssen wir ein solches Objekt vom Schattenobjekt ableiten, um dessen Weichzeichnermethoden nutzen zu können und gleichzeitig das nutzerdefinierte Rendering beizubehalten. Um normalerweise vom Schattenobjekt zu erben, müssen wir ihm einen geschützten Konstruktor hinzufügen, in dem wir den Typ des erstellten Objekts angeben.
Lassen Sie uns Änderungen an der Klasse des Schattenobjekts in \MQL5\Include\DoEasy\Objects\Graph\ShadowObj.mqh vornehmen.
Wir werden die Gaußschen Unschärfemethoden und das Array der Gewichtskoeffizienten vom privaten in den geschützten Bereich der Klasse verschieben. Der geschützte Konstruktor wird dort ebenfalls deklariert. Im öffentlichen Abschnitt der Klasse schreiben wir die virtuelle Methode, die das Flag für die Pflege einer realen Eigenschaft durch ein Objekt zurückgibt:
//+------------------------------------------------+ //| 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
Ich werde nach und nach allen grafischen Elementen Methoden hinzufügen, die die Flags für die Pflege von Eigenschaften durch ein Objekt zurückgeben, da dies Standardmethoden für die Bibliothek sind, die ich in Zukunft benötigen werde. Hier habe ich eine solche Methode hinzugefügt, um zu vermeiden, dass dieses Thema in dieser Klasse wieder auftaucht.
Geschützter Konstruktor:
//+------------------------------------------------+ //| 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); } //+------------------------------------------------------------------+
In den formalen Konstruktorparametern übergeben wir den Typ des erstellten Objekts, der im Initialisierungsstring an den Konstruktor der übergeordneten Klasse übergeben wird. Dies geschieht in allen Objekten der Bibliothek. Alles andere wird hier genau so gemacht wie im parametrischen Konstruktor.
Im parametrischen Konstruktor entfernen wir die Zeichenkette am Ende, die bewirkt, dass der Schatten sofort nach seiner Erstellung gezeichnet wird:
//+------------------------------------------------------------------+ //| 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(); } //+------------------------------------------------------------------+
Die Zeichenfolge verursachte ein falsches Verhalten des Schattenobjekts beim Erstellen von GUI-Elementen. Zunächst erschien ein Schatten auf einem leeren Diagramm, und dann wurde das Erscheinungsbild der grafischen Nutzeroberfläche erstellt. Jetzt wird der Schatten nicht mehr zuerst erscheinen.
Steuerelement ProgressBar
Erstellen wir zunächst ein zusätzliches Objekt der Blende in der Datei \MQL5\Include\DoEasy\Objects\Graph\WForms\GlareObj.mqh. Da ich dieses Objekt derzeit nicht benötige, werde ich es nicht berücksichtigen. Sie ist der Klasse der Schattenobjekte völlig ähnlich, mit dem Unterschied, dass die Farbe eines gezeichneten Schattens auf Weiß gesetzt ist:
//+------------------------------------------------------------------+ //| 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(); } //+------------------------------------------------------------------+
Ich werde alle notwendigen Änderungen in dieser Klasse im nächsten Artikel vornehmen. Lassen wir es vorerst so, wie es ist.
Das ProgressBar-Steuerelement besteht aus zwei Objekten - einem Hintergrund und einem Fortschrittsbalken. Der Hintergrund stellt das eigentliche Steuerelement dar. In Zukunft wird es möglich sein, zusätzliche Elemente darauf zu platzieren, und der Fortschrittsbalken wird durch ein separates Hilfsobjekt dargestellt, dessen Eigenschaften sich vom übergeordneten Objekt unterscheiden.
Erstellen wir in der Bibliotheksdatei \MQL5\Include\DoEasy\Objects\Graph\WForms\Helpers\ die neue Datei BarProgressBar.mqh für die Klasse CBarProgressBar. Die Klasse sollte vom Basisobjekt aller WinForms-Objekte der Bibliothek abgeleitet sein. Die Panel-Objektdatei sollte in die erstellte Klassendatei aufgenommen werden.
//+------------------------------------------------------------------+ //| 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 { }
Im geschützten Abschnitt der Klasse deklarieren wir die Methode, die die Blende auf dem Fortschrittsbalken anzeigt, sowie den geschützten Konstruktor. In den öffentlichen Abschnitt schreiben wir die Methoden, die Werte von Objekteigenschaften setzen und zurückgeben, virtuelle Methoden zur Verwaltung von Eigenschaften, den parametrischen Konstruktor und den Ereignisbehandler für den Klassentimer:
//+------------------------------------------------+ //| 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); }; //+------------------------------------------------------------------+
Schauen wir uns die angegebenen Methoden genauer an.
Geschützter Konstruktor:
//+------------------------------------------------+ //| 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); } //+------------------------------------------------------------------+
Der Konstruktor erhält den Typ des erstellten grafischen Elements und andere Objektparameter, die in der Initialisierungszeichenfolge auf die übergeordnete Klasse eingestellt sind. Der grafische Objekttyp der Bibliothek ist auf „Hilfsobjekt“ eingestellt, das Objekt hat keinen Rahmen und es sind Standardfarben eingestellt.
Der parametrische Konstruktor:
//+------------------------------------------------------------------+ //| 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); } //+------------------------------------------------------------------+
Hier ist alles genau so wie im geschützten Konstruktor, aber der Objekttyp wird nicht in formalen Parametern übergeben. Stattdessen ist sie in der Initialisierungszeichenfolge fest codiert.
Die Methode, die eine Blende zeichnet, wird vorerst leer sein, da die Blende noch nicht fertig ist. Seine Erstellung und Fehlersuche sind für den nächsten Artikel vorgesehen:
//+------------------------------------------------+ //| Draw a glare | //+------------------------------------------------+ void CBarProgressBar::DrawGlare(void) { } //+------------------------------------------------------------------+
Im Timer setzen wir vorübergehend den von der Funktion GetTickCount() zurückgegebenen Wert, um sicherzustellen, dass das Objekt in die Liste der aktiven Objekte fällt, die für die Verarbeitung im Bibliotheks-Timer zugewiesen sind:
//+------------------------------------------------+ //| Timer | //+------------------------------------------------+ void CBarProgressBar::OnTimer(void) { Comment(DFUN,GetTickCount()); } //+------------------------------------------------------------------+
Bevor ich mit der Erstellung des ProgressBar-Steuerelements beginne, muss ich sicherstellen, dass die Objekte der grafischen Elemente unabhängig voneinander im Bibliotheks-Timer verarbeitet werden können.
Das Konzept sieht folgendermaßen aus: Jedes der grafischen Elemente, die unabhängig voneinander im Timer bearbeitet werden, wird als aktives Element betrachtet. Um aktive Elemente zu behandeln, erstellen wir eine Liste, die Zeiger auf solche Objekte enthält. Diese Liste wird immer im Hauptobjekt erstellt und ist dementsprechend in der Sammelklasse der grafischen Elemente sichtbar. Im Timer der Kollektionsklasse werden wir eine Schleife durch alle Hauptformularobjekte ziehen, Zeiger auf aktive Elemente erhalten und deren Ereignisbehandlung im Timer aufrufen. So sind wir nicht gezwungen, jedes grafische Element im Hauptobjekt zu suchen, sondern erstellen sofort eine Liste der Elemente im Hauptobjekt und verarbeiten nur diese.
In der Datei \MQL5\Include\DoEasy\Objects\Graph\WForms\WinFormBase.mqh der Basisobjektklasse aller WinForms-Bibliotheksobjekte fügen wir die Datei der Objektklasse der Blende hinzu und deklarieren den Zeiger auf die Liste der aktiven Elemente im geschützten Abschnitt:
//+------------------------------------------------------------------+ //| 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:
Die Einbindung einer Blenden-Objektdatei ermöglicht es uns, diese in vielen grafischen Elementen der Bibliothek zu erstellen und zu verwenden.
Im öffentlichen Teil der Klasse deklarieren wir die Methoden für die Arbeit mit der Liste der aktiven Elemente:
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);
Wir fügen den Destruktor der Klasse hinzu :
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
Wenn die Liste der aktiven Elemente erstellt wurde, leeren wir sie und löschen das erstellte Listenobjekt.
In die Konstruktoren der Klasse schreiben wir die Erstellung des Listenobjekts der aktiven Elemente:
//+------------------------------------------------+ //| 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(); } //+------------------------------------------------------------------+
Schreiben wir in der Methode, die die Beschreibung der Integer-Eigenschaft des Elements zurückgibt, Codeblöcke, um die Beschreibung der neuen Objekteigenschaften zurückzugeben:
//+------------------------------------------------------------------+ //| 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) ) : "" ); } //+------------------------------------------------------------------+
Die Methode, die die Liste der aktiven Elemente des Hauptobjekts zurückgibt:
//+------------------------------------------------------------------+ //| 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; } //+------------------------------------------------------------------+
Hier erhalten wir den Zeiger auf das Hauptobjekt. Wenn NULL zurückgegeben wird, ist dieses Objekt das Hauptobjekt. Wir holen uns die Liste der aktiven Elemente aus dem Hauptobjekt. Wenn es nicht gelingt, den Zeiger zu erhalten, wird dies mitgeteilt und NULL zurückgegeben. Andernfalls wird der Zeiger auf die Liste zurückgegeben.
Die Methode, die die Anzahl der aktiven Elemente des Hauptobjekts zurückgibt:
//+------------------------------------------------------------------+ //| Return the number of active elements of the main object | //+------------------------------------------------------------------+ int CWinFormBase::ListMainActiveElementsTotal(void) { return(this.GetListMainActiveElements()!=NULL ? this.GetListMainActiveElements().Total() : 0); } //+------------------------------------------------------------------+
Wenn der Zeiger auf die Liste des Hauptobjekts erhalten wurde, wird die Anzahl der Elemente in der Liste zurückgegeben. Andernfalls wird true zurückgegeben.
Die Methode, die den Index des angegebenen Objekts in der Liste der aktiven Elemente des Hauptobjekts zurückgibt:
//+------------------------------------------------------------------+ //| 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; } //+------------------------------------------------------------------+
Die Methode erhält den Zeiger auf das Objekt, dessen Index gesucht werden soll. Holt die Liste der aktiven Elemente aus dem Hauptobjekt. In der Schleife wird anhand der erhaltenen Liste das nächste Objekt ausgewählt. Wenn sein Name gleich dem Namen des an die Methode übergebenen Objekts ist, wird der Schleifenindex zurückgegeben. Am Ende der Schleife wird -1 zurückgegeben, was bedeutet, dass das Objekt nicht gefunden wurde.
Die Methode, die ein Element aus der Liste der aktiven Elemente des Hauptobjekts nach Index zurückgibt:
//+------------------------------------------------+ //| 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); } //+------------------------------------------------------------------+
Die Methode erhält den Index eines Objekts, das aus der Liste zurückgegeben werden soll. Wir holen die Liste der aktiven Elemente aus dem Hauptobjekt und geben den Zeiger auf das Objekt nach Index zurück. Wenn die Liste nicht ermittelt werden konnte, gibt die Methode NULL zurück.
Die Methode, die das Flag für das Vorhandensein eines Objekts nach Namen in der Liste der aktiven Elemente des Hauptobjekts zurückgibt:
//+------------------------------------------------------------------+ //| 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; } //+------------------------------------------------------------------+
Die Methode empfängt den Namen des Objekts, dessen Vorhandensein in der Liste wir herausfinden müssen. Holt die Liste der aktiven Elemente aus dem Hauptobjekt. In der Schleife durch die erhaltene Liste, das nächste Objekt zu erhalten. Wenn der Name mit dem an die Methode übergebenen Namen übereinstimmt, wird true zurückgegeben. Nach Beendigung der Schleife wird -1 zurückgegeben. Das Objekt ist nicht in der Liste enthalten.
Die Methode, die das angegebene Objekt in die Liste der aktiven Elemente des Hauptobjekts aufnimmt:
//+------------------------------------------------------------------+ //| 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); } //+------------------------------------------------------------------+
Ermittelt die Liste der aktiven Elemente des Hauptobjekts. Wenn ein Objekt mit demselben Namen bereits in der Liste enthalten ist, wird false zurückgegeben. Andernfalls wird das Ergebnis des Hinzufügens des Objekts zur Liste zurückgegeben.
Die Methode, die das aktuelle Objekt in die Liste der aktiven Elemente des Hauptobjekts aufnimmt:
//+------------------------------------------------+ //| Add the current object to the list of | //| active elements of the main object | //+------------------------------------------------+ bool CWinFormBase::AddObjToListActiveElements(void) { return this.AddObjToListActiveElements(this.GetObject()); } //+------------------------------------------------------------------+
Gibt das Ergebnis des Aufrufs der obigen Methode zurück. Das aktuelle Objekt wird als ein Objekt angegeben, das der Liste hinzugefügt werden soll.
Die Methode, die das angegebene Objekt aus der Liste der aktiven Elemente des Hauptobjekts entfernt:
//+------------------------------------------------+ //| 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; } //+------------------------------------------------------------------+
Ermitteln des Zeigers auf die Liste der aktiven Elemente des Hauptobjekts. Ermitteln des Index des Objekts in der Liste, dessen Zeiger an die Methode übergeben wird. Ermitteln des Zeigers auf das aus der Liste entfernte Objekt. Wenn das Objekt nicht entfernt werden kann, wird false zurückgegeben. Andernfalls wird der Zeiger zurückgesetzt und true zurückgegeben.
Die Methode, die das aktuelle Objekt aus der Liste der aktiven Elemente des Hauptobjekts entfernt:
//+------------------------------------------------+ //| Remove the current object from the list of | //| active elements of the main object | //+------------------------------------------------+ bool CWinFormBase::DetachObjFromListActiveElements(void) { return this.DetachObjFromListActiveElements(this.GetObject()); } //+------------------------------------------------------------------+
Gibt das Ergebnis des Aufrufs der obigen Methode zurück. Wir geben das aktuelle Objekt als das zu entfernende Objekt an.
Erstellen wir nun das Steuerelement der ProgressBar.
In MQL5\Include\DoEasy\Objects\Graph\WForms\CommonControls\ erstellen wir die neue Datei ProgressBar.mqh mit der Klasse CProgressBar. Um die Funktionsweise des Objekts zu erweitern, soll die Klasse von der Klasse des Container-Objekts abgeleitet werden, damit andere Steuerelemente an das Objekt angehängt werden können. Die Container-Klassendatei sollte zusammen mit der Fortschrittsbalken-Klassendatei in die Datei des erstellten Objekts aufgenommen werden:
//+------------------------------------------------------------------+ //| 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 { }
Deklarieren wir im privaten Abschnitt der Klasse die Methoden zur Erstellung eines neuen grafischen Objekts und des Fortschrittsbalkens sowie die Initialisierungsmethode der Klasse:
//+------------------------------------------------------------------+ //| 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:
Im geschützten Abschnitt der Klasse deklarieren wir einen geschützten Konstruktor:
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:
Im öffentlichen Teil der Klasse deklarieren wir die Methoden zum Setzen und Abrufen von Objekteigenschaften, die Methode zum Abrufen des Zeigers auf das Fortschrittsbalkenobjekt, die Methoden, die die Flags für die Pflege der Objekteigenschaften zurückgeben, den parametrischen Konstruktor und den Timer:
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); }; //+------------------------------------------------------------------+
Nach dem Setzen der Eigenschaft setzen einige Methoden zum Setzen der Objekteigenschaften diese auf die entsprechende Eigenschaft des Fortschrittsbalkenobjekts.
Schauen wir uns die angegebenen Methoden genauer an.
Geschützter Konstruktor:
//+------------------------------------------------+ //| 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(); } //+------------------------------------------------------------------+
Die Methode empfängt den Typ des erstellten Objekts, der in der Initialisierungszeichenfolge auf die übergeordnete Klasse gesetzt ist. Der Objekttyp der Bibliotheksgrafik ist auf „Standardsteuerung“ eingestellt. Dann werden die Methode zur Initialisierung der Objekteigenschaften und die Methode zur Erstellung des Fortschrittsbalkenobjekts aufgerufen.
Der parametrische Konstruktor:
//+------------------------------------------------------------------+ //| 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(); } //+------------------------------------------------------------------+
Hier ist der Typ des Steuerelements im Initialisierungsstring fest codiert.
Die Methode zur Initialisierung der Elementeigenschaften:
//+------------------------------------------------+ //| 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); } //+------------------------------------------------------------------+
Der Objektrahmen ist auf ein Pixel auf jeder Seite eingestellt, der Rahmentyp ist einfach, die Objektfarben und andere Standardeigenschaften sind ebenfalls eingestellt. Die Länge des Fortschrittsbalkens wird auf die Hälfte der Breite des Objekts festgelegt.
Die Methode, die das Fortschrittsbalkenobjekt erstellt:
//+------------------------------------------------+ //| 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()); } } //+------------------------------------------------------------------+
Die Methodenlogik enthält Kommentare in der Auflistung. Die Höhe des Fortschrittsbalkens wird so eingestellt, dass das Objekt vollständig in den Container passt und gleichzeitig der Containerrahmen nicht von ihm überlappt wird. Nachdem wir den Fortschrittsbalken erstellt haben, fügen wir das gesamte Objekt zur Liste der aktiven Elemente hinzu. In diesem Fall wird es in der >Ereignisbehandlung des Timers der grafischen Bibliothekselemente gelangen und dort verarbeitet werden. Dementsprechend können wir in dem Objekt Timer, die Funktionalität, die unabhängig vom Nutzer funktioniert. Bei diesem Objekt wird dies der visuelle Effekt sein, mit dem ich mich im nächsten Artikel befassen werde.
Die virtuelle Methode zur Erstellung eines neuen grafischen Objekts:
//+------------------------------------------------+ //| 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; } //+------------------------------------------------------------------+
Damit das Containerobjekt angehängte Objekte in sich selbst erstellen kann, gibt es eine virtuelle Methode, in der diese Objekte erstellt werden. Für jedes Steuerelement kann die Liste der darin erstellten Objekte unterschiedlich sein. Dieses Objekt bietet die Möglichkeit, das Fortschrittsbalken-Objekt und das Blendenobjekt zu erstellen. Im Moment müssen hier keine weiteren Objekte erstellt werden.
Ereignisbehandlung durch den Timer:
//+------------------------------------------------+ //| Timer | //+------------------------------------------------+ void CProgressBar::OnTimer(void) { CBarProgressBar *bar=this.GetProgressBar(); if(bar!=NULL) bar.OnTimer(); } //+------------------------------------------------------------------+
Hier erhalten wir den Zeiger auf das Fortschrittsbalkenobjekt und rufen dessen Timer-Ereignisbehandlung auf. Da die Blende genau entlang des Fortschrittsbalkens verlaufen soll, werde ich dieses Verhalten im Timer des Klassenobjekts CBarProgressBar implementieren. Aus diesem Grund wird sein Timer hier aufgerufen, wobei die Ausgabe des GetTickCount()-Werts auf dem Diagramm in Form eines Kommentars geschrieben wird.
In der Datei \MQL5\Include\DoEasy\Objects\Graph\WForms\Containers\Container.mqh der Container-Objektklasse, und zwar in ihrer Methode, die die Parameter für das gebundene Objekt festlegt, fügen wir den Codeblock zum Festlegen der Parameter eines neu erstellten Trennobjekts hinzu:
//+------------------------------------------------+ //| 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(); } //+------------------------------------------------------------------+
Wir verwenden einfach die Standardwerte, die in den Konstruktoren dieser Klassen festgelegt sind.
Nun müssen wir in jedem Containerobjekt die Möglichkeit schaffen, Tooltip-Objekte schnell an das aktuelle und an die angegebenen Elemente anzuhängen. Dadurch wird es einfacher, den gewünschten Objekten, die an Containern angebracht sind, Tooltips zuzuordnen.
Fügen wir in \MQL5\Include\DoEasy\Objects\Graph\WForms\Containers\Panel.mqh die Einbindung der ProgressBar-Steuerungsdatei hinzu:
//+------------------------------------------------------------------+ //| 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" //+------------------------------------------------------------------+
Im öffentlichen Abschnitt deklarieren wir zwei Methoden zum Erstellen und Anhängen von Tooltip-Objekten:
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);
In der Methode, die ein neues grafisches Objekt erstellt, erstellen wir eine Zeichenfolge für die Erstellung eines neuen Steuerelements hinzu:
//+------------------------------------------------+ //| 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; } //+------------------------------------------------------------------+
Die Methode, die das ToolTip-Objekt erstellt und an das aktuelle Element anhängt:
//+------------------------------------------------------------------+ //| 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; } //+------------------------------------------------------------------+
Die Methodenlogik wird in den Codekommentaren beschrieben. Alle erforderlichen Daten werden an die Methode übergeben, um das Tooltip-Objekt zu erstellen. Wenn das Objekt erstellt wird, wird es automatisch mit dem aktuellen Objekt verbunden. Als Nächstes holen wir das neueste ToolTip-Objekt aus der Liste der ToolTip-Objekte und setzen die Parameter, die der Methode übergeben wurden, darauf.
Die Methode, die das ToolTip-Objekt erstellt und an das angegebene Element anhängt:
//+------------------------------------------------------------------+ //| 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; } //+------------------------------------------------------------------+
Zusätzlich zu den Parametern, die zum Erstellen eines Tooltip-Objekts erforderlich sind, erhält die Methode das Steuerelement, an das der erstellte ToolTip angehängt werden soll. Mit der obigen Methode wird ein neues Tooltip-Objekt erstellt und an das Steuerelement, das in den Methodenparametern angegeben wurde, angehängt. Wenn der ToolTip erfolgreich angehängt wurde, wird der Zeiger auf das erstellte Objekt zurückgegeben, andernfalls wird NULL ZURÜCKGEGEBEN.
Identische Verbesserungen beim Anhängen von Tooltip-Objekten und beim Erstellen von ProgressBar-Steuerelementen wurden in allen Containerklassendateien vorgenommen:
TabControl.mqh, TabField.mqh, SplitContainer.mqh, SplitContainerPanel.mqh und GroupBox.mqh. Ich werde diese Änderungen hier nicht berücksichtigen.
Jetzt müssen wir die Kollektionsklasse der grafischen Elemente \MQL5\Include\DoEasy\Collections\GraphElementsCollection.mqh verbessern.
Schauen wir uns an, wie Tooltip-Objekte behandelt werden sollten. Diese Objekte interagieren mit der Maus, und je nach Ergebnis der Interaktion werden sie im Zeitgeber der Kollektion grafischer Elemente verarbeitet. Das ToolTip-Objekt unter dem Cursor wird in die Liste der Interaktionsobjekte aufgenommen. Diese Liste sollte im Timer abgearbeitet werden. In diesem Fall sollte auch das vorherige Objekt, das sich zuvor unter dem Cursor befand, bearbeitet werden. Da das vergangene Objekt jedoch keine Ausblendanimation benötigt, wird die vom Ereignisbehandler der Kollektion aufgerufene Methode für seine Verarbeitung sorgen - das Objekt sollte einfach ausgeblendet und das Nicht-Anzeigen-Flag für es gesetzt werden. Diese Logik unterscheidet sich von der Logik aktiver Objekte, die zunächst in die Liste der aktiven Elemente aufgenommen werden und dort von einem Zeitgeber ständig abgearbeitet werden.
Im privaten Abschnitt der Sammelklasse deklarieren wir die Liste der zu verarbeitenden Objekte:
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
Deklarieren wir auch die Methoden zur Verarbeitung der aktuellen und vergangenen Objekte unter dem Cursor und die Methoden zur Verarbeitung der Liste der zu behandelnden Objekte:
//--- 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);
Im öffentlichen Abschnitt schreiben wir die Methode, die eine Liste der zu behandelnden Objekte zurückgibt:
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); }
und deklarieren die Ereignisbehandlung des Timers der Kollektion:
//--- (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);
Im Klassenkonstruktor löschen wir die Liste und setzen das Flag für die sortierte Liste auf die Liste:
//+------------------------------------------------+ //| 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(); } //+------------------------------------------------------------------+
Die Methode, die das grafische Element auf der Leinwand zur Kollektion hinzufügt:
//+------------------------------------------------------------------+ //| 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; } //+------------------------------------------------------------------+
Wir holen den Zeiger auf die Liste der zu behandelnden Objekte. Wenn das Objekt, das an die Methode übergeben wurde, nicht übergeben werden konnte, wird dies im Protokoll vermerkt und false zurückgegeben. Bei erfolgreicher Addition wird true zurückgegeben.
Die Methode, die ein Element nach Namen aus der Liste zur Bearbeitung erhält:
//+------------------------------------------------------------------+ //| 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; } //+------------------------------------------------------------------+
In der Schleife durch die Liste der zu bearbeitenden Objekte, das nächste Objekt holen. Wenn sein Name mit dem Namen des Objekts übereinstimmt, das an die Methode übergeben wurde, wird der Zeiger auf das gefundene Objekt zurückgegeben. Nach Abschluss der Schleife wird NULL zurückgegeben. Kein Objekt gefunden.
Die Methode, die den Index eines Elements nach Name in der Liste zur Bearbeitung zurückgibt:
//+------------------------------------------------------------------+ //| 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; } //+------------------------------------------------------------------+In der Schleife durch die Liste der zu bearbeitenden Objekte, das nächste Objekt holen. Wenn sein Name mit dem Namen des an die Methode übergebenen Objekts übereinstimmt, wird der Schleifenindex zurückgegeben. Am Ende der Schleife wird -1 zurückgegeben, was bedeutet, dass das Objekt nicht gefunden wurde.
Die Bearbeitungsmethode für das aktuelle ToolTip-Element:
//+------------------------------------------------+ //| 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()); } } //+------------------------------------------------------------------+
Die Methodenlogik wird in den Codekommentaren beschrieben. Die Methode verschiebt das Tooltip-Objekt zu den Cursor-Koordinaten und passt die empfangenen Koordinaten an, wenn das Objekt über den Rand des Bildschirms hinausgeht.
Die Methode zur Behandlung des vorherigen ToolTip-Elements:
//+------------------------------------------------+ //| 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); } //+------------------------------------------------------------------+
Auch hier ist alles sehr transparent. Das vorherige Objekt befindet sich natürlich nicht mehr unter dem Cursor. Das bedeutet, dass es sofort ausgeblendet und der Zeiger darauf aus der Liste der zu behandelnden Objekte entfernt werden sollte.
Wenn der im letzten Artikel getestete Expert Advisor auf einem Chart gestartet wird und die Chart-Periode auf eine andere geändert wird, wird das Programm mit einem kritischen Fehler beendet. Dies geschieht, weil der Zeiger auf das Formularobjekt als statisch deklariert ist:
//--- Declare static variables for the active form and status flags static CForm *form=NULL;
Das bedeutet, dass beim Ändern des Zeitrahmens des Charts die Daten darin erhalten bleiben und die Prüfung auf NULL ein negatives Ergebnis liefert, was besagt, dass das Objekt gültig ist:
//--- 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
Der Datensatz im Zeiger bezieht sich nicht mehr auf den Speicherbereich von diesem Objekt. Dies führt zum Abbruch des Programms, wenn auf die falsche Stelle im Speicher zugegriffen wird. Die Behebung des Fehlers ist einfach: Wir überprüfen die Gültigkeit des Zeigers, nicht seinen Wert:
//--- 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) {
Die Handhabung der vorherigen und aktuellen Tooltips ist nun wie folgt implementiert:
//--- 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); }
Die gesamte Logik ist in den Code-Kommentaren ausreichend detailliert beschrieben. Kurz gesagt, wenn sich das Objekt unter dem Cursor befindet, wird es der Liste zur Bearbeitung hinzugefügt und seine Koordinaten werden auf die Cursor-Koordinaten verschoben. Befindet sich das Objekt nicht unter dem Cursor, so wird es aus der Liste entfernt und ausgeblendet.
Der Timer der Kollektion:
//+------------------------------------------------+ //| 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(); } } } //+------------------------------------------------------------------+
Die Logik der Timer ist in den Kommentaren zum Code ausführlich beschrieben. Wir arbeiten mit zwei Schleifen - die Liste der zu bearbeitenden Objekte (ToolTip) und die Liste der aktiven Elemente (Objekte mit visuellen Animationseffekten)
Im Hauptobjekt der CEngine-Bibliothek in \MQL5\Include\DoEasy\Engine.mqh erstellen wir den Timer der grafischen Elemente der Bibliothek und den Timer für Ereignisse.
Im privaten Abschnitt deklarieren wir die Ereignisbehandlung:
//--- 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
Im Klassenkonstruktor erstellen wir einen weiteren Timer - den Timer für die Kollektion grafischer Elemente:
//+------------------------------------------------+ //| 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 //--- } //+------------------------------------------------------------------+
Im Timer der Ereignisbehandlung ergänzen wir die Codeblöcke für die Handhabung des Timers von grafischen Elementen:
//+------------------------------------------------+ //| 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(); } } //+------------------------------------------------------------------+
Die Ereignisbehandlungsmethode für grafische Elemente auf der Leinwand (canvas):
//+------------------------------------------------------------------+ //| Event handling method for graphical elements on canvas | //+------------------------------------------------------------------+ void CEngine::GraphElmEventsControl(void) { this.m_graph_objects.OnTimer(); } //+------------------------------------------------------------------+
Sobald der Pausentimer der Kollektion grafischer Elemente endet, wird diese Methode aufgerufen, die wiederum den Timer der Kollektionsklasse der grafischen Elemente aufruft. Der Standardaufruf erfolgt alle 16 Millisekunden (Timer-Intervall).
Alles ist bereit für einen Test.
Test
Um den Test durchzuführen, verwende ich den EA aus dem vorherigen Artikel und speichere ihn in \MQL5\Experts\TestDoEasy\Part126\ als TestDoEasy126.mq5.
In OnInit() des EAs erstellen wir auf dem zweiten Panel der ersten Registerkarte des TabControls im SplitContainer-Steuerelement das ProgressBar-Steuerelement:
//--- 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()); } }
Um sicherzugehen, dass das Objekt erstellt wurde, holen wir uns den Zeiger auf das Objekt und senden Sie seine Beschreibung an das Protokoll.
Kompilieren Sie den EA und starten Sie ihn auf einem Chart:
Die für den aktuellen Artikel angegebene Funktionsweisen funktioniert gut. In den Kommentaren zum Chart ist eine sich ständig ändernde Zahl zu sehen. Dies sind die Daten aus dem Timer des Fortschrittsbalkens des ProgressBar-Steuerelements.
Was kommt als Nächstes?
Im nächsten Artikel werde ich die Arbeit am ToolTip-Objekt fortsetzen.
Alle Dateien der aktuellen Bibliotheksversion, des Test-EA und des Chartereignis-Kontrollindikators für MQL5 sind unten angehängt, damit Sie sie testen und herunterladen können.
*Vorherige Artikel in dieser Reihe:
DoEasy. Steuerung (Teil 20): Das WinForms-Objekt SplitContainer
DoEasy. Steuerung (Teil 21): SplitContainer-Steuerung. Paneel-Trennlinie
DoEasy. Steuerung (Teil 22): SplitContainer. Ändern der Eigenschaften des erstellten Objekts
DoEasy. Steuerung (Teil 23): Verbesserung der WinForms-Objekte TabControl und SplitContainer
DoEasy. Steuerung (Teil 24): Hinweis auf WinForms-Hilfsobjekt
DoEasy. Steuerung (Teil 25): Tooltip WinForms-Objekt
Übersetzt aus dem Russischen von MetaQuotes Ltd.
Originalartikel: https://www.mql5.com/ru/articles/11732





- Freie Handelsapplikationen
- Über 8.000 Signale zum Kopieren
- Wirtschaftsnachrichten für die Lage an den Finanzmärkte
Sie stimmen der Website-Richtlinie und den Nutzungsbedingungen zu.