
DoEasy. Controles (Parte 26): Apurando o objeto WinForms "ToolTip" e desenvolvendo o "ProgressBar".
Conteúdo
- Ideia
- Modificando as classes da biblioteca
- Elemento de controle da barra de progresso
- Teste
- O que virá a seguir?
Ideia
Neste artigo, prosseguiremos a construção do controle ToolTip. Quando o cursor é movido sobre um objeto, a ponta da ferramenta deve surgir após uma breve pausa e qualquer outra dica anterior deve ser escondida. Se o cursor permanecer sobre o objeto, a dica deve sumir após um certo período de tempo. No artigo anterior, alcançamos o objetivo de fazer com que a ponta da ferramenta aparecesse instantaneamente ao posicionar o cursor sobre o objeto designado. Agora, vamos aperfeiçoar o comportamento da ponta da ferramenta para ficar mais alinhado com o comportamento padrão dos binários compilados do MS Visual Studio. Quando o cursor é colocado sobre um objeto, haverá uma pausa antes da ponta da ferramenta surgir suavemente na tela. Se o cursor é retirado do objeto, a ponta da ferramenta será escondida. Se o cursor é deixado sobre o objeto, a ponta da ferramenta desaparecerá após um tempo. Para implementar este comportamento, precisamos usar o temporizador já criado para os elementos gráficos da coleção. Cada objeto terá seu manipulador virtual para eventos do temporizador. Este temporizador deve ser implementado diretamente nas classes de objetos que precisam ser processados.
Para aprimorar a eficiência da biblioteca, criaremos uma lista de objetos ativos que indicarão quais objetos devem ser processados no temporizador. Isso facilita o rastreamento dos objetos desejados e evita a varredura constante de todos os elementos gráficos. O loop será executado apenas para objetos adicionados à lista, possibilitando que a GUI se torne mais dinâmica. Além de reagirem à interação do mouse, os objetos também utilizarão as animações definidas para efeitos visuais. Até mesmo ícones animados simples podem ser facilmente criados, basta criar um objeto com quadros de animação e adicioná-los à lista de objetos ativos. No manipulador de eventos temporizados deste objeto, basta alternar os quadros de animação previamente criados. Hoje, teremos o foco em criar o manipulador de eventos temporizados para o controle ToolTip. Este manipulador implementará o comportamento de ponta de ferramenta descrito anteriormente, envolvendo a interação com o mouse. Portanto, tanto o manipulador de eventos do elemento gráfico quanto o temporizador serão tratados.
Uma vez que o objeto ToolTip WinForms tenha sido finalizado, procedermos à criação do controle ProgressBar (controle "barra de progresso" do Windows). Vamos criar apenas uma versão estática do objeto - suas propriedades e aparência. Chegaremos ao fundo da questão no próximo artigo.
Modificando as classes da biblioteca
No arquivo \MQL5\Include\DoEasy\Defines.mqh adicionamos parâmetros de temporizador para elementos gráficos na tela:
//--- 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
Cada coleção de objetos de biblioteca tem seu próprio manipulador de temporizador, com seus respetivos parâmetros, como pausa, incremento do contador e identificador. Aqui adicionamos os mesmos parâmetros para o novo temporizador.
Para o novo controle ProgressBar, acrescentamos valores padrão:
#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
Antecipando o que virá, alguns controles gráficos terão efeitos visuais. Por exemplo, para a linha de progresso, faremos um brilho ao longo da mesma, conforme implementado no Windows. O objeto que aplica o brilho será colocado no topo da linha e se moverá ao longo dela. Para tal objeto, é necessário especificar seu tipo na lista de tipos de objetos da biblioteca:
//+------------------------------------------------------------------+ //| 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
E vamos adicionar novos tipos de elementos gráficos à lista de tipos de elementos gráficos na tela:
//+------------------------------------------------------------------+ //| 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 }; //+------------------------------------------------------------------+
Neste artigo o controle ToolTip pode ter vários estados ao interagir com o mouse. Como o objeto é animado, seu comportamento e estado devem ser descritos de alguma forma. Pode esperar antes de aparecer, aparecer, aguardar antes de desaparecer, estar em estado de atenuação ou estar em seu estado normal.
Vamos descrever seus estados em uma nova lista de estados de exibição de controle:
//+------------------------------------------------------------------+ //| 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 }; //+------------------------------------------------------------------+
Este tipo de estados também pode ser aplicado a outros controles para os quais é possível criar posteriormente sequências de animação. Não necessariamente o objeto tem que aparecer ou desaparecer gradualmente. O aparecimento gradual de um objeto pode ser equiparado ao desdobramento de uma lista suspensa, e o estado de desaparecimento gradual (fading out) pode ser equiparado à minimização de um objeto. No entanto, podemos sempre acrescentar a esta lista mais tarde.
A barra de progresso pode ter três estilos de linhas. Vamos descrevê-las em uma enumeração:
//+------------------------------------------------------------------+ //| 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 | //+------------------------------------------------------------------+
Hoje faremos apenas um estilo, que será a uma faixa contínua.
O estado do objeto que tem exibição animada deve ser armazenado e recuperado das propriedades do objeto.
Adicionamos as novas propriedades do elemento gráfico à enumeração de propriedades inteiras do mesmo na tela e aumentamos o número total de propriedades inteiras de 129 para 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 //+------------------------------------------------------------------+
Adicionaremos novas propriedades à lista de critérios para ordenar os elementos gráficos na tela:
//+------------------------------------------------------------------+ //| 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 }; //+------------------------------------------------------------------+
Agora poderemos selecionar, filtrar e ordenar elementos gráficos por novas propriedades.
No arquivo \MQL5\Include\DoEasy\Data.mqh, adicionamos os índices das novas mensagens:
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 }; //+------------------------------------------------------------------+
e as mensagens de texto correspondentes aos índices recém-adicionados:
{"Не установлен","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"}, }; //+---------------------------------------------------------------------+
Ao criar coleções de objetos, usamos uma classe de lista derivada de uma classe de matriz dinâmica de ponteiros para instâncias da classe CObject e seus descendentes. A classe CArrayObj tem um método Detach() que extrai um ponteiro para um objeto de uma lista e retorna o ponteiro resultante. Nem sempre precisamos usar mais um ponteiro quando o extraímos de uma lista.
Assim, na classe derivada no arquivo \MQL5\Include\DoEasy\Collections\ListObj.mqh criaremos um método que extrai um ponteiro da lista sem devolvê-lo:
//+------------------------------------------------------------------+ //| 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; } }; //+------------------------------------------------------------------+
Isto é simples: extraímos o ponteiro da lista e, se isso falhar, retornamos false.
Se a extração for bem sucedida , zeramos o ponteiro resultante e retornamos true.
Vamos modificar ligeiramente a classe do objeto de pausa no arquivo \MQL5\Include\DoEasy\Services\Pause.mqh.
Vamos adicionar um método público que retorna o início da contagem regressiva da pausa em milissegundos desde a inicialização do sistema:
//--- 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; }
Precisamos desse método ao fazer os controles no temporizador.
Como adicionamos novas propriedades inteiras ao elemento gráfico, precisamos inicializá-las no objeto base do elemento gráfico e adicionar novos campos de estrutura de objeto.
No arquivo \MQL5\Include\DoEasy\Objects\Graph\GCnvElement.mqh, adicionamos novos campos à estrutura:
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
Precisamos da estrutura do objeto a fim de salvar e recuperar corretamente o objeto.
No bloco de métodos simplificados de acesso de propriedade de objeto , escreveremos métodos para lidar com estas novas propriedades:
//--- (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); }
Os métodos de definição de propriedade escrevem os valores recebidos por eles nas propriedades do objeto, e os métodos de retorno devolvem valores previamente definidos a partir das propriedades do objeto.
Em ambos os construtores, definimos os valores padrão para as novas propriedades:
//+------------------------------------------------------------------+ //| 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()); } } //+------------------------------------------------------------------+
No método que cria a estrutura do objeto, escrevemos os valores das propriedades do objeto nos campos da estrutura:
//+------------------------------------------------------------------+ //| 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; } //+------------------------------------------------------------------+
No método que cria um objeto a partir de uma estrutura, escrevemos valores para as propriedades do objeto a partir dos campos da estrutura:
//+------------------------------------------------------------------+ //| 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 } //+------------------------------------------------------------------+
No arquivo \MQL5\Include\DoEasy\Objects\Graph\GBaseObj.mqh da classe do objeto base de todos os objetos gráficos da biblioteca, escrevemos o retorno da descrição dos novos tipos de objetos no método que retorna a descrição do tipo de elemento gráfico:
//+------------------------------------------------------------------+ //| 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" ); } //+------------------------------------------------------------------+
Todos esses objetos já foram declarados, mas iremos criá-los mais tarde, após finalizarmos a classe do objeto ToolTip.
Para trabalhar com gráficos, precisamos criar um manipulador de eventos com temporizador. Como todos os elementos gráficos são herdados da classe objeto-forma, é nesta classe que declararemos um manipulador virtual de temporizador.
Na seção pública do arquivo \MQL5\Include\DoEasy\Objects\Graph\GCnvElement.mqh, escrevemos novamente o seguinte método:
//--- 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; }
Este manipulador não faz nada aqui, ele deve ser restaurado nas classes em que os eventos do temporizador devem ser tratados.
Declaramos um método que devolve o número de objetos ToolTip anexados ao elemento:
//--- 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);
Apenas um objeto dica de ferramenta pode ser atribuído a um objeto gráfico. Mas vários objetos ToolTip podem ser anexados a um objeto, e outros objetos anexados a ele podem ser atribuídos às dicas de ferramenta criadas. Isto se deve ao fato de que nem todos os objetos da biblioteca podem ser anexados aos objetos ToolTip. Mas podemos sempre criar dicas de ferramenta no contêiner ao qual os objetos são anexados, e depois atribui-las a esses objetos. Este método servirá para saber o número de objetos ToolTip criados e anexados ao contêiner.
Vamos modificar o método que atribui o ToolTip ao objeto.
Adicionamos verificações adicionais de o tipo incorreto e de número de dicas anexas maior que zero:
//+------------------------------------------------------------------+ //| 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; } //+------------------------------------------------------------------+
Se erroneamente tentarmos anexar um tipo diferente de objeto em vez de um objeto de dica, o método irá relatar um erro. Se um ToolTip já estiver anexado ao objeto, uma mensagem de erro também será exibida e o método retornará false.
Método que devolve o número de objetos ToolTip:
//+------------------------------------------------------------------+ //| 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; } //+------------------------------------------------------------------+
Se este objeto for um contêiner, qualquer número de objetos dica de ferramenta pode ser anexado a ele. O método retorna a quantidade de objetos existente. No loop, usamos número total de objetos anexados para, assim, obter o próximo objeto. Se o tipo de objeto for uma dica de ferramenta, aumentamos o contador de objetos (variável res). No final do loop, retornamos o resultado da contagem registrado na variável res.
O temporizador da coleção de elementos gráficos é atualizado uma vez a cada 16 milissegundos, e para fazer com que a dica de ferramenta apareça ou se desvaneça por um segundo, precisamos de 1000 milissegundos divididos pelo período de atualização do temporizador. Como resultado, teremos que mudar a opacidade do objeto aproximadamente a cada 1000/16=62,5 milissegundos.
No arquivo \MQL5\Include\DoEasy\Objects\Graph\WForms\Common Controls\ToolTip.mqh declaramos a variável para armazenar o incremento de transparência:
//+------------------------------------------------------------------+ //| 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:
Na seção pública da classe, declaramos um manipulador de eventos de temporizador virtual:
//--- 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); }; //+------------------------------------------------------------------+
E em cada um dos construtores, vamos calcular o valor da variável que armazena o incremento de transparência, com base nos valores padrão para a duração da surgimiento/desvanecimento e o incremento do contador para o temporizador da coleção de elementos gráficos:
//+------------------------------------------------------------------+ //| 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); } //+------------------------------------------------------------------+
No método de inicialização de variáveis, escrevemos os valores padrão para os atrasos de mudança de estado dos objetos dica da ferramenta:
//+------------------------------------------------------------------+ //| 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(); } //+------------------------------------------------------------------+
No método que exibe a dica da ferramenta, movemos o comando de exibição do objeto até o fim e removemos o redesenho completo do objeto inteiro:
//+------------------------------------------------------------------+ //| 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); } //+------------------------------------------------------------------+
O método fica assim:
//+------------------------------------------------------------------+ //| 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(); } //+------------------------------------------------------------------+
Este ajuste reduz ligeiramente o 'piscar' desagradável do elemento à medida que sua opacidade muda gradualmente.
No método que desenha a dica de ferramenta, no final adicionamos uma atualização da tela modificada:
//+------------------------------------------------------------------+ //| 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(); } //+------------------------------------------------------------------+
Quando mudo a opacidade para zero (o objeto é completamente transparente), noto um comportamento estranho nos métodos que desenham as primitivas com suavização - eles não reagem bem ao valor da transparência. Quando o valor da opacidade é muito baixo, as linhas traçadas com estes métodos se destacam muito contra um objeto que tem o mesmo valor de opacidade. A adição de uma linha de atualização de tela é uma tentativa de reduzir este efeito desagradável. Entretanto, enquanto não estiver funcionando...
Manipulador de eventos do temporizador:
//+------------------------------------------------------------------+ //| 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); } } //+------------------------------------------------------------------+
A lógica do método é descrita nos comentários ao código. Cada vez que o temporizador do objeto é acionado, este manipulador é chamado. A cada iteração do temporizador, precisamos verificar o estado do objeto e seus contadores de espera ou de mudança de estado.
Primeiramente, o estado normal do objeto é registrado. Nesse estado, a dica de ferramenta está completamente oculta. Se o objeto se encontra neste estado, ele deve esperar um determinado tempo antes de iniciar seu processo de exibição suave. Portanto, quando o objeto está no estado normal, é estabelecido um período de espera para que a dica de ferramenta surja gradualmente e, após esse tempo, a contagem regressiva do temporizador é iniciada e o método é encerrado.
Na próxima etapa, o objeto está no estado de espera pelo processo de surgimento suave e verificamos o contador. Se o tempo de espera terminar, mudamos o estado do objeto para surgimento suave e prosseguimos. Durante o processo de surgimento suave, aumentamos gradualmente a opacidade do objeto em cada iteração do temporizador até que ele se torne completamente opaco. Em seguida, mudamos o estado do objeto para espera pelo processo de desvanecimento suave, já que é importante que o objeto desvaneça suavemente após cerca de cinco segundos.
O processo de desvanecimento suave é idêntico ao processo de surgimento suave. Ao final do laço, o estado de término de laço de mudanças é registrado no objeto. Se o estado normal for registrado imediatamente, o laço inteiro será reiniciado, pois o estado normal do objeto é justamente a condição de partida para o início do laço. E isso só deve ser registrado no objeto quando o ponteiro para ele for removido da lista de objetos que precisam ser processados no temporizador. Isso é feito na classe coleção de elementos gráficos. Quando o objeto apresenta o estado de término de laço completo, o método remove o ponteiro para o objeto da lista de processamento e estabelece seu estado normal. Na próxima vez que o cursor apontar para o objeto, ele será colocado de volta na lista de processamento no temporizador. Da mesma forma, o ponteiro para o objeto é removido da lista se o cursor for retirado da área do objeto, já pronto para o laço de processamento, ou mesmo durante este ciclo.
Gradualmente, estamos chegando à criação do novo objeto WinForms ProgressBar. Para esse controle, precisaremos criar um objeto auxiliar chamado "Brilho". Objetos deste tipo serão usados para a aparência visual de alguns elementos da GUI, e no objeto ProgressBar, o brilho deve percorrer a linha de progresso. A concepção será a seguinte: desenhamos uma mancha branca na forma e tamanho necessários e a espalhamos do centro para as bordas. O objeto-brilho será tão semi-transparente quanto o objeto-sombra. E aqui chegamos ao fato de que precisamos herdar esse objeto da sombra para usar seus métodos de desvanecimento e fazer a renderização por conta própria. Para herdar corretamente da sombra, precisamos adicionar um construtor protegido a ele, no qual especificaremos o tipo de objeto a ser criado.
No arquivo \MQL5\Include\DoEasy\Objects\Graph\ShadowObj.mqh fazemos alterações na classe do objeto sombra.
Movemos os métodos de borrão gaussiano e a matriz de coeficientes de ponderação da seção privada para a seção protegida da classe, e declaramos um construtor protegido na mesma seção. Na seção pública da classe, escrevemos um método virtual que devolva o sinalizador para manter o objeto como uma propriedade real:
//+------------------------------------------------------------------+ //| 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
Aos poucos adicionaremos métodos que retornam sinalizadores que indicam o objeto suporta as propriedades a todos os elementos gráficos, já que estes são métodos padrão de biblioteca que precisaremos mais tarde. Acrescentamos aqui tal método simplesmente para garantir que não voltaremos a esta questão nesta classe novamente.
Construtor protegido:
//+------------------------------------------------------------------+ //| 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); } //+------------------------------------------------------------------+
Nos parâmetros formais do construtor, é passado o tipo do objeto criado, que é passado na linha de inicialização para o construtor da classe pai. Isto é feito em todos os objetos da biblioteca. Tudo o resto aqui é feito exatamente da mesma forma que no construtor paramétrico.
No construtor paramétrico, apagamos a linha no final que faz com que a sombra seja desenhada assim que ela é criada:
//+------------------------------------------------------------------+ //| 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(); } //+------------------------------------------------------------------+
A presença desta linha causou um comportamento incorreto do objeto sombra durante a criação de elementos GUI - primeiro a sombra aparecia em um gráfico em branco e depois a aparência da GUI era construída. Agora a sombra não aparecerá primeiro.
Controle ProgressBar
Primeiro, criamos um objeto auxiliar brilho na pasta \MQL5\Include\DoEasy\Objects\Graph\WForms\ no arquivo GlareObj.mqh. Não precisaremos deste objeto hoje, ele será feito inteiramente à imagem da classe do objeto sombra. Apenas a cor do brilho que está sendo desenhado será branca:
//+------------------------------------------------------------------+ //| 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(); } //+------------------------------------------------------------------+
No próximo artigo, faremos todas as mudanças necessárias nesta classe. Por enquanto, vamos deixar as coisas como estão.
O controle da barra de progresso consistirá em dois objetos, a base e a linha de progresso. A base será o próprio controle. A barra de progresso será um objeto auxiliar separado com propriedades próprias que podem ser alteradas em relação ao objeto principal.
Na pasta \MQL5\Include\DoEasy\Objects\Graph\WForms\Helpers\, criamos um novo arquivo Splitter.mqh da classe CSplitter. A classe deve ser herdada do objeto base de todos os objetos WinForms da biblioteca, cujo arquivo deve ser anexado ao arquivo da classe que está sendo criada:
//+------------------------------------------------------------------+ //| 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 { }
Na seção protegida da classe, vamos declarar um método que exibe o objeto brilho na barra de progresso e um construtor protegido. Na seção pública, escrevemos métodos que definem e retornam valores das propriedades do objeto, métodos virtuais para manter propriedades, construtor paramétrico e manipulador de eventos do temporizador de classe:
//+------------------------------------------------------------------+ //| 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); }; //+------------------------------------------------------------------+
Vamos dar uma olhada nos métodos declarados.
Construtor protegido:
//+------------------------------------------------------------------+ //| 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); } //+------------------------------------------------------------------+
O tipo de elemento gráfico a ser criado é passado para o construtor, que é passado para a classe pai na linha de inicialização. O tipo de objeto gráfico da biblioteca é definido como "auxiliar", o objeto não tem moldura, e são definidas as cores padrão.
Construtor paramétrico:
//+------------------------------------------------------------------+ //| 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); } //+------------------------------------------------------------------+
Isto é exatamente o mesmo que o construtor protegido, mas o tipo do objeto não é passado em parâmetros formais, mas é codificado de forma forçada na linha de inicialização.
O método que põe o brilho estará vazio por enquanto, já que o objeto brilho ainda não está pronto, e sua criação e depuração está programada para o próximo artigo:
//+------------------------------------------------------------------+ //| Draw a glare | //+------------------------------------------------------------------+ void CBarProgressBar::DrawGlare(void) { } //+------------------------------------------------------------------+
No gerenciador de eventos do temporizador, simplesmente mostraremos o valor retornado a partir de GetTickCount() por enquanto, apenas para garantir que este objeto seja incluído na lista de objetos ativos designados ao gerenciador de eventos do temporizador da biblioteca:
//+------------------------------------------------------------------+ //| Timer | //+------------------------------------------------------------------+ void CBarProgressBar::OnTimer(void) { Comment(DFUN,GetTickCount()); } //+------------------------------------------------------------------+
Antes de começarmos a criar o controle ProgressBar, precisamos ter certeza de que os objetos elementos gráficos têm a capacidade de se processarem a si mesmos no temporizador da biblioteca.
A ideia é a seguinte: cada um dos elementos gráficos que têm processamento independente planejado no temporizador será considerado um elemento ativo. Para gerenciar esses elementos ativos, vamos criar uma lista que armazenará as referências a esses objetos. Essa lista será sempre criada no objeto principal e, portanto, será acessível na classe coleção de elementos gráficos. Na classe coleção de temporizadores, percorreremos todos os objetos forma principais, obteremos as referências aos elementos ativos e chamaremos seus manipuladores de eventos de temporizador. Dessa forma, não precisaremos procurar cada elemento gráfico no objeto principal, mas criaremos imediatamente uma lista com esses elementos no objeto principal e os processaremos.
No arquivo \MQL5\Include\DoEasy\Objects\Graph\WForms\WinFormBase.mqh do objeto base de todos os objetos WinForms da biblioteca, escreveremos o arquivo de inclusão da classe de objetos brilho e na seção protegida declararemos um ponteiro para a lista de elementos ativos:
//+------------------------------------------------------------------+ //| 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:
Aqui a inclusão do arquivo de objeto brilho nos dará a capacidade de criá-lo e usá-lo em muitos dos elementos gráficos da biblioteca.
Na seção pública da classe, declaramos métodos para trabalhar com a lista de elementos ativos:
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);
Vamos adicionar o destruidor da classe:
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
Se a lista de elementos ativos tiver sido criada, limpamos a lista e excluímos o objeto lista criado.
Nos construtores da classes, vamos escrever a criação do objetos lista de elementos ativos:
//+------------------------------------------------------------------+ //| 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(); } //+------------------------------------------------------------------+
No método que retorna a descrição da propriedade inteira do elemento, escrevemos blocos de código para retornar as descrições das novas propriedades do objeto WInForms:
//+------------------------------------------------------------------+ //| 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) ) : "" ); } //+------------------------------------------------------------------+
Método que devolve uma lista de elementos ativos do objeto principal:
//+------------------------------------------------------------------+ //| 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; } //+------------------------------------------------------------------+
Aqui: obtemos um ponteiro para o objeto principal. Se NULL for devolvido é porque é o principal. Obtemos a lista de elementos ativos a partir do objeto principal. Se o painel não puder ser obtido, notificamos isso e retornamos false. Caso contrário, retornamos um ponteiro para lista.
Método que devolve uma lista de elementos ativos do objeto principal:
//+------------------------------------------------------------------+ //| Return the number of active elements of the main object | //+------------------------------------------------------------------+ int CWinFormBase::ListMainActiveElementsTotal(void) { return(this.GetListMainActiveElements()!=NULL ? this.GetListMainActiveElements().Total() : 0); } //+------------------------------------------------------------------+
Se o ponteiro para a lista do objeto principal for recuperado, o número de elementos da lista será devolvido, caso contrário, zero.
Método que retorna o índice do objeto especificado na lista de elementos ativos do objeto principal:
//+------------------------------------------------------------------+ //| 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; } //+------------------------------------------------------------------+
O método recebe o ponteiro para o objeto cujo índice devemos encontrar. Obtemos a lista de elementos ativos a partir do objeto principal. Com um laço, obtemos o próximo objeto, e se seu nome for igual ao nome do objeto passado para o método, o índice do laço é retornado. No final do laço ciclo retornamos -1, o que significa objeto não encontrado.
Método que retorna um elemento da lista de elementos ativos do objeto principal por índice:
//+------------------------------------------------------------------+ //| 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); } //+------------------------------------------------------------------+
O índice do objeto a ser devolvido da lista é passado para o método. Usamos o índice para obter a lista de elementos ativos do objeto principal e retornamos um ponteiro para o objeto. Se a lista não puder ser obtida, o método retornará NULL.
Método que retorna o sinalizador de presença de objeto segundo o nome na lista de elementos ativos do objeto principal:
//+------------------------------------------------------------------+ //| 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; } //+------------------------------------------------------------------+
O método recebe o nome do objeto cuja presença na lista deve ser conhecida. Obtemos a lista de elementos ativos a partir do objeto principal. Com um laço, obtemos o próximo objeto, e se seu nome for igual ao nome do objeto passado para o método, o índice do laço é retornado (true). Ao final do laço, retornamos false, o que indica que o objeto não está na lista.
Método que adiciona o objeto especificado à lista de elementos ativos do objeto principal:
//+------------------------------------------------------------------+ //| 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); } //+------------------------------------------------------------------+
Obtemos a lista de elementos ativos a partir do objeto principal. Se um objeto com este nome já existe na lista, devolvemos false. Caso contrário, devolvemos o resultado da adição do objeto à lista.
Método que adiciona o objeto especificado à lista de elementos ativos do objeto principal:
//+------------------------------------------------------------------+ //| Add the current object to the list of | //| active elements of the main object | //+------------------------------------------------------------------+ bool CWinFormBase::AddObjToListActiveElements(void) { return this.AddObjToListActiveElements(this.GetObject()); } //+------------------------------------------------------------------+
Retornamos o resultado da chamada do método acima. Especificamos o objeto atual como o objeto a ser adicionado à lista.
Método que remove o objeto especificado da lista de elementos ativos do objeto principal:
//+------------------------------------------------------------------+ //| 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; } //+------------------------------------------------------------------+
Recebemos um ponteiro para a lista de elementos ativos no objeto principal. Obtemos o índice do objeto na lista, cujo ponteiro foi passado para o método. Obtemos um ponteiro para o objeto removido da lista. Se o objeto não pôde ser removido, devolvemos false. Caso contrário, zeramos o ponteiro e retornamos true.
Método que remove o objeto especificado a partir da lista de elementos ativos do objeto principal:
//+------------------------------------------------------------------+ //| Remove the current object from the list of | //| active elements of the main object | //+------------------------------------------------------------------+ bool CWinFormBase::DetachObjFromListActiveElements(void) { return this.DetachObjFromListActiveElements(this.GetObject()); } //+------------------------------------------------------------------+
Retornamos o resultado da chamada do método acima. O objeto a ser retirado é o objeto atual.
Criando o controle ProgressBar.
No diretório da biblioteca \MQL5\Include\DoEasy\Objects\Graph\WForms\Common Controls\, criamos um novo arquivo ProgressBar.mqh da classe CProgressBar. Como planejamos tornar possível anexar outros controles ao objeto para ampliar sua funcionalidade, a classe deve ser herdada da classe do objeto contêiner. O arquivo da classe contêiner, juntamente com o arquivo de classe da barra de progresso, deve ser anexado ao arquivo objeto que está sendo criado:
//+------------------------------------------------------------------+ //| 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 { }
Na seção privada da classe, vamos declarar métodos para a criação de um novo objeto gráfico e barra de progresso, bem como um método para a inicialização da classe:
//+------------------------------------------------------------------+ //| 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:
Na seção protegida da classe, declaramos um construtor protegido:
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:
Na seção pública da classe, vamos declarar métodos para definir e recuperar propriedades do objeto, um método para obter um ponteiro para o objeto barra de progresso, métodos que retornam sinalizadores de suporte de propriedades, um construtor paramétrico e um manipulador de eventos do temporizador:
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); }; //+------------------------------------------------------------------+
Alguns métodos de configuração de propriedades do objeto, após a definição de uma propriedade, definem a mesma propriedade no objeto barra de progresso correspondente.
Vamos dar uma olhada nos métodos declarados.
Construtor protegido:
//+------------------------------------------------------------------+ //| 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(); } //+------------------------------------------------------------------+
O tipo de objeto a ser criado é passado para o método, que é definido para a classe pai na linha de inicialização. O tipo de objeto gráfico da biblioteca é definido como "controle padrão". O método de inicialização da propriedade do objeto e o método que cria o objeto barra de progresso são então chamados.
Construtor paramétrico:
//+------------------------------------------------------------------+ //| 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(); } //+------------------------------------------------------------------+
Aqui, o tipo de controle é codificado de forma rígida na linha de inicialização.
Método de inicialização das propriedades do elemento:
//+------------------------------------------------------------------+ //| 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); } //+------------------------------------------------------------------+
O objeto tem uma borda de 1 pixel de largura em cada lado, com uma aparência lisa. As cores e outras características são definidas como padrão. O comprimento da barra de progresso é ajustado para a metade da largura do objeto.
Método que cria o objeto barra de progresso:
//+------------------------------------------------------------------+ //| 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()); } } //+------------------------------------------------------------------+
A lógica do método é comentada em sua listagem. A altura da barra de progresso é estabelecida de forma que ela se ajuste completamente no contêiner sem ser ocultada pelos contornos do mesmo. Depois de criarmos a barra de progresso, adicionamos todo o objeto à lista de elementos ativos. Dessa forma, ele será incluído no gerenciador de eventos do temporizador para elementos gráficos da biblioteca e será processado ali. Desta maneira, no temporizador desse objeto, podemos criar funcionalidades que não dependam do usuário. Nesse objeto, será um efeito visual, que será abordado na próxima seção.
Método virtual que cria um novo objeto gráfico:
//+------------------------------------------------------------------+ //| 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; } //+------------------------------------------------------------------+
Para que um objeto contêiner possa criar objetos anexos dentro de si, existe um método virtual no qual esses objetos são criados. Para cada controle, a lista de objetos criados nele pode ser diferente. Este objeto permite criar um objeto barra de progresso e um objeto brilho. Por enquanto, não é necessário criar mais objetos aqui.
Manipulador de eventos do temporizador:
//+------------------------------------------------------------------+ //| Timer | //+------------------------------------------------------------------+ void CProgressBar::OnTimer(void) { CBarProgressBar *bar=this.GetProgressBar(); if(bar!=NULL) bar.OnTimer(); } //+------------------------------------------------------------------+
Aqui: obtemos um ponteiro para o objeto barra de progresso e chamamos seu manipulador de eventos do temporizador. Como o brilho deve correr exatamente ao longo da barra de progresso, vamos implementá-lo no temporizador do objeto da classe CBarProgressBar. E é por isso que seu temporizador é chamado aqui, seu valor GetTickCount() é escrito como um comentário sobre o gráfico por enquanto.
No arquivo \MQL5\Include\DoEasy\Objects\Graph\WForms\Containers\Container.mqh, no método que define os parâmetros do objeto anexado, escreveremos um bloco de código para definir os parâmetros do objeto dica de ferramenta recém-criado:
//+------------------------------------------------------------------+ //| 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(); } //+------------------------------------------------------------------+
Basta definir os valores padrão definidos nos construtores dessas classes.
Agora precisamos tornar possível que cada objeto contêiner possa rapidamente anexar objetos dica de ferramentas ao elemento atual e ao elemento especificado. Isto facilitará a anexação de dicas de ferramentas aos objetos desejados anexados aos contêineres.
No arquivo \MQL5\Include\DoEasy\Objects\Graph\WForms\Containers\Panel.mqh, adicionamos a inclusão do arquivo do controle ProgressBar:
//+------------------------------------------------------------------+ //| 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" //+------------------------------------------------------------------+
Na seção pública, declaramos dois métodos para criar e anexar objetos dica de ferramenta:
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);
No método que cria um novo objeto gráfico, vamos adicionar uma linha de código para criar um novo controle ProgressBar:
//+------------------------------------------------------------------+ //| 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; } //+------------------------------------------------------------------+
Método que cria e anexa um objeto ToolTip ao elemento atual:
//+------------------------------------------------------------------+ //| 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; } //+------------------------------------------------------------------+
A lógica do método é descrita nos comentários ao código. Todos os dados necessários para criar um objeto dica de ferramenta são passados para o método. Quando um objeto é criado, ele é automaticamente anexado ao objeto atual. Em seguida, recuperamos o objeto ToolTip mais recente da lista de objetos dica de ferramenta e configuramos os parâmetros passados para o método.
Método que cria e anexa um objeto ToolTip ao elemento atual:
//+------------------------------------------------------------------+ //| 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; } //+------------------------------------------------------------------+
Além dos parâmetros necessários para criar o objeto dica de ferramenta, o controle ao qual o ToolTip criado deve ser anexado é passado para o método. O método acima cria um novo objeto dica de ferramenta e o anexa ao controle especificado nos parâmetros do método. Se o ToolTip for anexado com sucesso, o ponteiro é devolvido ao objeto criado. Caso contrário, retornamos NULL.
Refinamentos idênticos para anexar objetos dica de ferramenta e criar controles da barra de progresso são feitos em todos os arquivos de classes contêineres:
TabControl.mqh, TabField.mqh, SplitContainer.mqh, SplitContainerPanel.mqh e GroupBox.mqh. Não vamos considerar estas mudanças aqui.
Vamos alterar a classe-coleção de objetos gráficos no arquivo \MQL5\Include\DoEasy\Collections\GraphElementsCollection.mqh.
Vejamos como os objetos dica de ferramenta devem ser manuseados. Estes objetos interagem com o mouse e, dependendo da interação, são manipulados no temporizador da coleção de elementos gráficos. O objeto ToolTip que está abaixo do cursor é adicionado à lista de objetos de interação. Esta lista deve ser processada em um temporizador. Qualquer objeto passado que estivesse sob o cursor também deve ser processado. Mas como o objeto passado não precisa de uma animação desbotada, ele será manipulado por um método chamado pelo manipulador de eventos da coleção, porque o objeto deve ser simplesmente escondido e o sinalizador de não exibição deve ser marcado. Esta lógica é diferente daquela dos objetos ativos, que são inicialmente colocados na lista de elementos ativos e permanentemente processados pelo temporizador da lista.
Na seção privada da classe coleção, declaramos a lista de objetos a serem processados:
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
Lá também declararemos métodos para lidar com os objetos atuais e passados sob o cursor, e métodos para lidar com a lista de objetos a serem manuseados:
//--- 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);
Na seção pública, escrevemos um método que devolva a lista de objetos a serem processados:
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); }
e declaramos um manipulador de eventos com temporizador de coleção:
//--- (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);
No construtor da classe, limpamos a lista e definimos um sinalizador de lista ordenada:
//+------------------------------------------------------------------+ //| 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(); } //+------------------------------------------------------------------+
Método que adiciona um elemento gráfico na tela à coleção:
//+------------------------------------------------------------------+ //| 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; } //+------------------------------------------------------------------+
Obtemos um ponteiro para a lista de objetos a serem processados. Se ele não acrescentar um objeto passado ao método, nós reportamos isso ao log e retornamos false. Se adicionado com sucesso, retornamos true.
Método que usa o nome para recuperar o elemento de uma lista de processamento:
//+------------------------------------------------------------------+ //| 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; } //+------------------------------------------------------------------+
Em um laço sobre a lista de objetos a serem processados, obtemos o próximo objeto e se seu nome corresponder ao nome do objeto passado para o método, devolvemos o ponteiro para o objeto encontrado. No final do laço, retornamos NULL - objeto não encontrado.
Método que retorna o índice de elemento por nome na lista a ser processada:
//+------------------------------------------------------------------+ //| 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; } //+------------------------------------------------------------------+Em um laço sobre a lista de objetos a serem processados, obtemos o próximo objeto e se seu nome corresponder ao nome do objeto passado para o método, devolvemos o ponteiro para o objeto encontrado. No final do laço retornamos -1, o que significa objeto não encontrado.
Método para processar o elemento ToolTip atual:
//+------------------------------------------------------------------+ //| 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()); } } //+------------------------------------------------------------------+
A lógica do método é descrita nos comentários ao código. O método desloca o objeto dica da ferramenta pelas coordenadas do cursor e corrige as coordenadas resultantes se o objeto se estender para além da borda da tela.
Método para processar o elemento ToolTip atual:
//+------------------------------------------------------------------+ //| 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); } //+------------------------------------------------------------------+
Aqui também é bastante transparente. O objeto passado não está mais sob o cursor. Isto significa que ele deve ser escondido imediatamente e um ponteiro para ele removido da lista de objetos a serem processados.
Se você executar o EA testado no último artigo em um gráfico e mudar o período gráfico para outro, o programa será encerrado com um erro crítico. Isso acontece porque o ponteiro para objeto forma é declarado como estático:
//--- Declare static variables for the active form and status flags static CForm *form=NULL;
Isto significa que quando o período gráfico é alterado, os dados permanecerão nele e a verificação de NULL dará um resultado negativo, dizendo que o objeto é válido:
//--- 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
Os dados registrados no ponteiro não se irão referir à área de memória daquele objeto. É por isso que o programa encerra ao acessar o local errado da memória. O erro é fácil de corrigir: vamos verificar a validade do ponteiro, não o seu valor:
//--- 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) {
O manuseio das dicas de ferramentas agora é feito assim:
//--- 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); }
A lógica é explicada em detalhes nos comentários do código. Em resumo: se um objeto estiver sob o cursor, ele é adicionado à lista a ser processada e suas coordenadas são deslocadas com base nas coordenadas do cursor. Se o objeto não estiver sob o cursor, ele é removido da lista e escondido.
Manipulador de eventos do temporizador da coleção:
//+------------------------------------------------------------------+ //| 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(); } } } //+------------------------------------------------------------------+
A lógica do manipulador é totalmente explicada nos comentários do código. Trabalhamos em dois laços, um sobre a lista de objetos a serem processados (ToolTip) e outro sobre a lista de elementos ativos (objetos com efeitos de animação)
No objeto principal da biblioteca CEngine, no arquivo \MQL5\Include\DoEasy\Engine.mqh é necessário criar um temporizador para os elementos gráficos da biblioteca e um manipulador de eventos do temporizador.
Na seção privada, declaramos um manipulador:
//--- 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
No construtor da classe, criamos outro temporizador, que é o temporizador da coleção de elementos gráficos:
//+------------------------------------------------------------------+ //| 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 //--- } //+------------------------------------------------------------------+
No manipulador de eventos do temporizador, escrevemos os blocos de código do manipulador do temporizador do elemento gráfico:
//+------------------------------------------------------------------+ //| 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(); } } //+------------------------------------------------------------------+
Método para processar eventos de elementos gráficos na tela:
//+------------------------------------------------------------------+ //| Event handling method for graphical elements on canvas | //+------------------------------------------------------------------+ void CEngine::GraphElmEventsControl(void) { this.m_graph_objects.OnTimer(); } //+------------------------------------------------------------------+
Assim que a pausa do temporizador da coleção de elementos gráficos terminar, este método será chamado, o qual, por sua vez, chama o manipulador do temporizador da classe coleção de elementos gráficos. A chamada padrão ocorrerá a cada 16 milissegundos (pausa do temporizador).
Agora estamos prontos para o teste.
Teste
Para testar, vamos pegar o Expert Advisor do artigo anterior e salvá-lo na nova pasta \MQL5\Experts\TestDoEasy\Part126\ com o novo nome TestDoEasy126.mq5.
No manipulador OnInit(), na primeira aba do TabControl no controle SplitContainer, criamos um controle barra de progresso em seu segundo painel:
//--- 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()); } }
Para verificar se o objeto foi criado, obtemos um ponteiro para ele e exibimos sua descrição no log.
Compilamos o Expert Advisor e o iniciamos no gráfico:
Bem, a funcionalidade anunciada até agora está funcionando satisfatoriamente. O gráfico nos comentários mostra um número em constante mudança, esta é a saída do temporizador da barra de progresso do controle ProgressBar.
O que virá a seguir?
No próximo artigo, continuaremos trabalhando no objeto ProgressBar.
Todos os arquivos da versão atual da biblioteca, os arquivos do EA de teste e o indicador do controle de eventos dos gráficos para MQL5 estão anexados abaixo.
*Artigos desta série:
DoEasy. Controles (Parte 20): Objeto WinForms SplitContainer
DoEasy. Controles (Parte 21): Controle SplitContainer. Separador de painéis
DoEasy. Controles (Parte 22): SplitContainer. Alterando as propriedades do objeto criado
DoEasy. Controles (Parte 23): Apurando os objetos WinForms TabControl e SplitContainer
DoEasy. Controles (Parte 24): Objeto WinForms dica
DoEasy. Controles (Parte 25): Objeto WinForms Tooltip
Traduzido do russo pela MetaQuotes Ltd.
Artigo original: https://www.mql5.com/ru/articles/11732





- Aplicativos de negociação gratuitos
- 8 000+ sinais para cópia
- Notícias econômicas para análise dos mercados financeiros
Você concorda com a política do site e com os termos de uso