English Русский 中文 Español Deutsch 日本語
Interfaces Gráficas II: O Elemento de Menu (Capítulo 1)

Interfaces Gráficas II: O Elemento de Menu (Capítulo 1)

MetaTrader 5Exemplos | 10 agosto 2016, 10:45
1 183 0
Anatoli Kazharski
Anatoli Kazharski

Conteúdo


Introdução

Nos capítulos da primeira parte, nós discutimos em detalhes os processos de desenvolvimento da estrutura principal da biblioteca para a criação das interfaces gráficas. Lá, nós também criamos o elemento principal da interface - o formulário para os controles. O primeiro artigo Interfaces gráficas I: Preparação da Estrutura da Biblioteca (Capítulo 1) considera em detalhes a finalidade desta biblioteca. A lista completa dos links para os artigos da primeira parte se encontram no final de cada capítulo. Lá, você também pode encontrar e baixar a versão completa da biblioteca, no estágio de desenvolvimento atual. Os arquivos devem estar localizados nas mesmas pastas que o arquivo baixado.

Algumas classes, no estágio de desenvolvimento atual, ainda estão incompletas. Conforme a biblioteca for incrementada com os novos controles, algumas adições terão que ser introduzidas na classe CWndContainer, que é a base para armazenar os ponteiros para os elementos e objetos que fazem parte dele. Será necessário introduzirmos também algumas adições na classe CWndEvents. Isto é por causa que os eventos do gráfico e os eventos gerados pelos controles serão tratados aqui.

A versão atual da classe de formulário CWindow para os controles também não é definitiva. O modo multi-janela mencionado nos artigos anteriores ainda não foi implementado. Nós vamos entender este modo na quarta parte desta série. Juntando a isso, será demonstrado a criação de um menu de contexto. Embora esta seja uma parte do menu principal, ela pode ser parte de alguns outros controles que serão considerados em outros artigos desta série.

Recomenda-se a realização consistente de todas as ações para um melhor entendimento do material. Por questões de economia de espaço, o código dos métodos pertencendo ao mesmo tipo e repetidos em diferentes classes será omitido. Se você se deparar com tais métodos, você deve se dirigir aos arquivos anexados neste artigo para procurar o código exigido e, então, continuar estudando o material deste artigo.


O Menu Principal do Programa

É difícil encontrar um programa que não possui um menu principal. Os terminais MetaTrader também possuem este elemento de interface (veja a imagem abaixo). Geralmente, o menu está localizado na parte superior esquerda da janela do programa, consistindo de vários elementos. Um clique com o botão esquerdo do mouse em um elemento de menu traz uma lista suspensa com as opções do programa.

Fig. 1. Menu principal no terminal MetaTrader 5

Fig. 1. Menu principal no terminal MetaTrader 5

Esta lista suspensa é chamada de menu de contexto, podendo conter vários tipos de elementos. Vamos estudar cada um deles em detalhes:

  • O elemento botão. Este é o elemento mais simples do menu de contexto. Normalmente, um clique esquerdo neste elemento, faz abrir uma janela com uma funcionalidade estendida para a configuração de um programa ou uma janela contendo algumas informações. Lá, também pode haver funções muito simples. Eles mudam algo na aparência da interface do programa após o elemento botão ser clicado.
  • Um elemento com dois estados do tipo caixa de seleção. Este elemento pode ser usado para ativar algum processo ou abrir (tornar visível) alguma parte da interface do programa. Quando isso acontece, este elemento muda sua aparência e mostra ao usuário do aplicativo qual estado ele se encontra.
  • Um grupo de elementos. Neste grupo apenas um elemento pode ser habilitado. Este tipo de controle é chamado de botão do tipo radio ou botão switch. Neste artigo, nós vamos chamá-lo de elemento de rádio.
  • Um elemento para a chamada do menu de contexto. O menu de contexto que foi chamado do arquivo principal do programa pode ter elementos contendo outro menu de contexto. Após clicar neste elemento, o menu de contexto será exibido à direita da mesma.

O editor de código MetaEditor também contém o menu principal:

Fig. 2. Menu principal no editor de código MetaEditor.

Fig. 2. Menu principal no editor de código MetaEditor

Agora, nós precisamos identificar quais classes são necessárias para compor tal elemento de interface complexo. É claro que reunir tudo em uma única classe é impraticável já que seria muito difícil estudar e realizar manutenções em tal classe. Portanto, faz sentido realizar tudo de maneira que todo o complexo seja montado a partir de várias partes simples. Vamos decidir quais serão essas partes.

O menu principal e menu de contexto consistem de vários itens. A mesma classe pode ser usada para estes elementos em ambos os tipos de menu. Um menu de contexto, muitas vezes contém uma linha de separação, que serve para distinguir os elementos do menu em categorias. Portanto, nós podemos ver que é necessário, pelo menos, quatro classes de código para criar tais elementos de interface como:

  1. O elemento de menu. Para criar este elemento, nós vamos desenvolver a classe CMenuItem.
  2. A classe CSeparateLine será criada para a linha de separação.
  3. O menu de contexto. A classe CContextMenu. Este elemento de interface irá ser montado a partir dos objetos da classe CMenuItem.
  4. O menu principal. A classe CMenuBar. Da mesma forma que no menu de contexto, as partes constituintes serão os elementos do menu (CMenuItem).

Nós definimos as principais tarefas. A partir do que foi dito acima, ficou claro que a parte mais importante para a criação do menu principal e o de contexto é o elemento de menu. Portanto, nós vamos continuar com a criação da classe CMenuItem.


Desenvolvimento da Classe para a Criação do Elemento de Menu

Na pasta Controls, onde todos os outros arquivos da biblioteca estão localizados, crie o arquivo MenuItem.mqh com a classe derivada CMenuItem. Você pode declarar os métodos virtuais padrões para todos os controles de maneira imediata. A classe base para isso é a classe CElement, que foi discutida em detalhes no artigo anterior. Por enquanto, nós vamos deixar esses métodos sem sua implementação já que mais tarde eu gostaria de destacar uma peculiaridade interessante do compilador.

//+------------------------------------------------------------------+
//|                                                     MenuItem.mqh |
//|                        Copyright 2015, MetaQuotes Software Corp. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
#include "Element.mqh"
//+------------------------------------------------------------------+
//| Classe para criar o elemento de menu                             |
//+------------------------------------------------------------------+
class CMenuItem : public CElement
  {
   //---
public:
                     CMenuItem(void);
                    ~CMenuItem(void);  
   //---
public:
   //--- Manipulador de eventos do gráfico
   virtual void      OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam);
   //--- Timer
   virtual void      OnEventTimer(void);
   //--- Elemento móvel
   virtual void      Moving(const int x,const int y);
   //--- Exibir, ocultar, resetar, remover
   virtual void      Show(void);
   virtual void      Hide(void);
   virtual void      Reset(void);
   virtual void      Delete(void);
   //--- Definir, resetar as prioridades para o clique esquerdo do mouse
   virtual void      SetZorders(void);
   virtual void      ResetZorders(void);  
  };
//+------------------------------------------------------------------+
//| Construtor                                                       |
//+------------------------------------------------------------------+
CMenuItem::CMenuItem(void)
  {
  }
//+------------------------------------------------------------------+
//| Destrutor                                                        |
//+------------------------------------------------------------------+
CMenuItem::~CMenuItem(void)
  {
  }  
//+------------------------------------------------------------------+

Quais objetos gráficos serão utilizados para compor o elemento de menu? No menu principal, estes são geralmente títulos que mudam suas cores de fundo e/ou as cores da fonte quando o cursor está pairando sobre eles. Geralmente, o menu de contexto tem um ícone. Se um elemento contém o seu próprio menu de contexto, então, na parte direita da área dele há uma seta apontando para a direita, indicando para o usuário que há um menu anexado. Isto significa que um elemento de menu pode ser de vários tipos diferentes, dependendo a quem ele pertence e que tarefa que se destina a cumprir. Vamos enumerar todas as suas possíveis partes constituintes:

  1. Fundo.
  2. Rótulo.
  3. Legenda.
  4. Indicação da presença do menu de contexto.

Fig. 3. Partes integrantes do controle elementos de menu.

Fig. 3. Partes integrantes do controle elementos de menu.

Adicione instâncias de classe de objetos gráficos, que estão disponíveis no arquivo Element.mqh e declarações de métodos para a criação de objetos gráficos para a classe CMenuItem. No momento de anexar o elemento de menu para o gráfico, o número de índice do elemento de menu tem de ser passado para o método. Este número irá ser utilizada para formar os nomes dos objetos gráficos, que serão usados ​​para a composição do elemento de menu. Isto também será utilizado para a identificação na lista de elementos ou no momento do clique sobre um deles.

class CMenuItem : public CElement
  {
private:
   //--- Objetos para a criação de um elemento de menu
   CRectLabel        m_area;
   CBmpLabel         m_icon;
   CLabel            m_label;
   CBmpLabel         m_arrow;
   //---
public:
   //--- Métodos para a criação do elemento de menu
   bool              CreateMenuItem(const long chart_id,const int window,const int index_number,const string label_text,const int x,const int y);
   //---
private:
   bool              CreateArea(void);
   bool              CreateIcon(void);
   bool              CreateLabel(void);
   bool              CreateArrow(void);
   //---
  };

Como um elemento de menu pode ser de vários tipos diferentes, é necessário haver a opção para especificar o tipo a que ele pertence antes de sua criação. Nós vamos precisar de uma enumeração dos tipos de elementos de menu (ENUM_TYPE_MENU_ITEM).

Adicione isso ao arquivo Enums.mqh onde todas as enumerações da biblioteca são armazenadas. A enumeração ENUM_TYPE_MENU_ITEM deve ter as opções que foram discutidas anteriormente:

  • MI_SIMPLE — elemento simples.
  • MI_HAS_CONTEXT_MENU — elemento que contém um menu de contexto.
  • MI_CHECKBOX — o elemento caixa de seleção.
  • MI_RADIOBUTTON — elemento que pertence ao grupo dos botões de radio.
//+------------------------------------------------------------------+
//| Enumeração dos tipos de elementos de menu                        |
//+------------------------------------------------------------------+
enum ENUM_TYPE_MENU_ITEM
  {
   MI_SIMPLE           =0,
   MI_HAS_CONTEXT_MENU =1,
   MI_CHECKBOX         =2,
   MI_RADIOBUTTON      =3
  };

Adicione o campo e os métodos correspondentes para configurar e obter o tipo de elemento de menu para a classe CMenuItem :

class CMenuItem : public CElement
  {
private:
   //--- Propriedades do elemento de menu
   ENUM_TYPE_MENU_ITEM m_type_menu_item;
   //---
public:
   //--- Configurar e obter o tipo
   void              TypeMenuItem(const ENUM_TYPE_MENU_ITEM type)   { m_type_menu_item=type;                 }
   ENUM_TYPE_MENU_ITEM TypeMenuItem(void)                     const { return(m_type_menu_item);              }
   //---
  };

O tipo MI_SIMPLE , que é um elemento de menu simples, será definido por padrão:

//+------------------------------------------------------------------+
//| Construtor                                                       |
//+------------------------------------------------------------------+
CMenuItem::CMenuItem(void) : m_type_menu_item(MI_SIMPLE)
  {
  }

Os métodos para definir a aparência de cada objeto gráfico de um elemento de menu devem ser criados da mesma maneira que foi feito no artigo anterior para o formulário. Estes métodos possuem algumas propriedades que são únicas. Vamos listar tudo o que será necessário para esse controle:

  1. Alterar a cor do fundo.
  2. A cor de fundo quando um elemento de menu não está disponível, ou seja, está bloqueado devido à impossibilidade de utilizar a função do elemento neste momento.
  3. Alterar a cor de fundo do quadro.
  4. A prioridade para o clique esquerdo do mouse deve ser generalizada e igual a zero para todos os objetos, com exceção do plano de fundo já que é sobre ele que será monitorado o clique.
  5. Redefinição das imagens para os ícones e a indicação da presença do menu de contexto.
  6. Métodos para gerenciar as propriedades do rótulo de texto, tais como:
    • margens partindo da coordenada zero do fundo do elemento de menu;
    • cor da fonte;
    • cor do texto quando o cursor está pairando sobre ele;
    • cor do texto quando o elemento está bloqueado.
  7. Variáveis ​​para acompanhar o estado de um elemento de menu:
    • estado geral (disponível/bloqueado);
    • estado da caixa de seleção;
    • estado do botão de radio;
    • estado do menu de contexto, se ele for anexado a este elemento.
  8. Identificador para um grupo de elementos de botão de radio. Isto é necessário já que um menu de contexto pode conter vários grupos de elementos de botão de radio. O identificador permitirá entender a qual grupo o botão de radio pertence exatamente.
  9. Número de índice de um elemento de menu.

Adicione tudo o que foi listado acima para a classe CMenuItem:

class CMenuItem : public CElement
  {
private:
   //--- Propriedades do fundo
   int               m_area_zorder;
   color             m_area_border_color;
   color             m_area_color;
   color             m_area_color_off;
   color             m_area_color_hover;
   //--- Propriedades do rótulo
   string            m_icon_file_on;
   string            m_icon_file_off;
   //--- Propriedades do rótulo de texto
   string            m_label_text;
   int               m_label_x_gap;
   int               m_label_y_gap;
   color             m_label_color;
   color             m_label_color_off;
   color             m_label_color_hover;
   //--- Propriedades do indício do menu de contexto
   string            m_right_arrow_file_on;
   string            m_right_arrow_file_off;
   //--- Prioridade Geral para o clique
   int               m_zorder;
   //--- Disponível/bloqueado
   bool              m_item_state;
   //--- Estado da caixa de seleção
   bool              m_checkbox_state;
   //--- Estado do botão de rádio e seu identificador
   bool              m_radiobutton_state;
   int               m_radiobutton_id;
   //--- Estado do menu de contexto
   bool              m_context_menu_state;
   //---
public:
   //--- Métodos do fundo
   void              AreaBackColor(const color clr)                 { m_area_color=clr;                      }
   void              AreaBackColorOff(const color clr)              { m_area_color_off=clr;                  }
   void              AreaBorderColor(const color clr)               { m_area_border_color=clr;               }
   //--- Métodos do rótulo
   void              IconFileOn(const string file_path)             { m_icon_file_on=file_path;              }
   void              IconFileOff(const string file_path)            { m_icon_file_off=file_path;             }
   //--- Métodos do rótulo de texto
   string            LabelText(void)                          const { return(m_label.Description());         }
   void              LabelXGap(const int x_gap)                     { m_label_x_gap=x_gap;                   }
   void              LabelYGap(const int y_gap)                     { m_label_y_gap=y_gap;                   }
   void              LabelColor(const color clr)                    { m_label_color=clr;                     }
   void              LabelColorOff(const color clr)                 { m_label_color_off=clr;                 }
   void              LabelColorHover(const color clr)               { m_label_color_hover=clr;               }
   //--- Métodos para indicar a presença do menu de contexto
   void              RightArrowFileOn(const string file_path)       { m_right_arrow_file_on=file_path;       }
   void              RightArrowFileOff(const string file_path)      { m_right_arrow_file_off=file_path;      }
   //--- (1) Estado comum do elemento e (2) o elemento de caixa de seleção
   void              ItemState(const bool state);
   bool              ItemState(void)                          const { return(m_item_state);                  }
   void              CheckBoxState(const bool flag)                 { m_checkbox_state=flag;                 }
   bool              CheckBoxState(void)                      const { return(m_checkbox_state);              }
   //--- Identificador do elemento radio
   void              RadioButtonID(const int id)                    { m_radiobutton_id=id;                   }
   int               RadioButtonID(void)                      const { return(m_radiobutton_id);              }
   //--- Estado do elemento radio
   void              RadioButtonState(const bool flag)              { m_radiobutton_state=flag;              }
   bool              RadioButtonState(void)                   const { return(m_radiobutton_state);           }
   //--- Estado do menu de contexto anexado a este elemento
   bool              ContextMenuState(void)                   const { return(m_context_menu_state);          }
   void              ContextMenuState(const bool flag)              { m_context_menu_state=flag;             }
   //---
  };

No artigo anterior, foi explicado que a biblioteca será estruturada de uma forma para que seus usuários não se encontram em uma situação onde eles não sabem o que fazer. A sequência de ações pode ser esquecida ao longo do tempo. Antes de criar um controle, tem de ser passado o ponteiro ao formulário em que ele será anexado. Se isso não for feito, o programa não irá terminar de anexar todos os controles ao formulário e imprimir uma mensagem relevante sobre ele nos registros. Esta mensagem deve ser formada de uma forma clara sobre o qual controle o usuário cometeu um erro no curso de suas ações.

Faça a inclusão do arquivo que contém a classe do formulário (CWindow) e declare um ponteiro com o tipo do formulário. Para armazenar o ponteiro ao formulário na classe de cada controle, nós vamos precisar de um método correspondente. Vamos nomeá-lo de WindowPointer(). Como parâmetro único, ele recebe como referência o objeto do tipo CWindow. Sua tarefa é de armazenar o ponteiro para o objeto passado. A função GetPointer() pode ser usada para obter um ponteiro do objeto. O mesmo terá de ser feito na classe de cada controle que nós vamos criar.

//+------------------------------------------------------------------+
//|                                                     MenuItem.mqh |
//|                        Copyright 2015, MetaQuotes Software Corp. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
#include "Element.mqh"
#include "Window.mqh"
//+------------------------------------------------------------------+
//| Classe para criar o elemento de menu                             |
//+------------------------------------------------------------------+
class CMenuItem : public CElement
  {
private:
   //--- Ponteiro para o formulário ao qual o controle será anexado
   CWindow          *m_wnd;
   //---
public:
   //--- Armazena o ponteiro do formulário passado
   void              WindowPointer(CWindow &object)                 { m_wnd=::GetPointer(object);            }
   //---
  };

Agora, vamos considerar o método CMenuItem::CreateMenuItem() para a criação de um elemento de menu. No início do método, nós vamos verificar a validade do ponteiro do formulário que o elemento deve ser anexado para utilizar a função CheckPointer(). Se o ponteiro não é válido, então o programa irá imprimir uma mensagem para o registro e deixará o método, retornando false. Se o ponteiro estiver presente, então as variáveis ​​dos elementos são inicializadas.

No artigo anterior, nós estabelecemos que cada elemento de interface deve ter o seu próprio identificador e analisado em detalhes como é que ele será formado. Para evitar repetições, eu vou lembrá-lo brevemente como isso acontece.

No momento da criação da formulário principal, quando um ponteiro para ele é adicionado ao elemento base (a classe CWndContainer) o identificador é definido pelo contador dos elementos de interface do método CWndContainer::AddWindow(). O valor do contador é armazenado nas classes do formulário sob a adição de cada elemento para a base. Como o ponteiro ao formulário é obrigatório em cada elemento, o número do identificador do último controle está disponível para cada novo elemento de interface criado através do ponteiro ao formulário. Antes de criar um elemento, o identificador será formado, como é mostrado no código abaixo (destacado em amarelo.

Em seguida, as margens do ponto extremo do formulário são calculadas e armazenadas para o elemento. O acesso às coordenadas do formulário está disponível a partir do ponteiro de formulário, existindo a possibilidade de calcular a distância a partir deles. As mesmas margens maneira serão calculadas para cada objeto de um elemento. Depois disso, todos os objetos do elemento são criados e uma verificação de estado de janela é levada a cabo no final. Se a janela é minimizada, então o elemento tem de ser escondido.

//+------------------------------------------------------------------+
//| Cria o elemento de menu                                          |
//+------------------------------------------------------------------+
bool CMenuItem::CreateMenuItem(const long chart_id,const int subwin,const int index_number,const string label_text,const int x,const int y)
  {
//--- Sai, se não houver um ponteiro para o formulário
   if(::CheckPointer(m_wnd)==POINTER_INVALID)
     {
      ::Print(__FUNCTION__," > Antes de criar o elemento de menu, a classe deve ser passada.  "
            "pelo ponteiro da janela: CMenuItem::WindowPointer(CWindow &object)");
      return(false);
     }
//--- Inicialização das variáveis
   m_id           =m_wnd.LastId()+1;
   m_index        =index_number;
   m_chart_id     =chart_id;
   m_subwin       =subwin;
   m_label_text   =label_text;
   m_x            =x;
   m_y            =y;
//--- Margens do ponto extremo
   CElement::XGap(m_x-m_wnd.X());
   CElement::YGap(m_y-m_wnd.Y());
//--- Criando o elemento de menu
   if(!CreateArea())
      return(false);
   if(!CreateIcon())
      return(false);
   if(!CreateLabel())
      return(false);
   if(!CreateArrow())
      return(false);
//--- Se a janela está minimizada, oculte o elemento após sua criação
   if(m_wnd.IsMinimized())
      Hide();
//---
   return(true);
  }

No método CMenuItem::Hide() de esconder o elemento, todos os objetos tem de ser ocultos, algumas variáveis ​​zeradas e as cores redefinidas:

//+------------------------------------------------------------------+
//| Oculta o elemento de menu                                        |
//+------------------------------------------------------------------+
void CMenuItem::Hide(void)
  {
//--- Sai se o elemento está oculto
   if(!CElement::m_is_visible)
      return;
//--- Oculta todos os objetos
   for(int i=0; i<ObjectsElementTotal(); i++)
      Object(i).Timeframes(OBJ_NO_PERIODS);
//--- Zerando as variáveis
   m_context_menu_state=false;
   CElement::m_is_visible=false;
   CElement::MouseFocus(false);
//--- Reseta a cor
   m_area.BackColor(m_area_color);
   m_arrow.State(false);
  }

A elaboração do nome para os objetos gráficos do elemento de menu será um pouco mais complexa que na classe de formulário CWindow. Se um elemento de menu é criado como um elemento separado, como um elemento de menu independente, que não faz parte do menu principal ou de contexto (exemplos a seguir), então não haverá qualquer problema já que este terá um identificador único. Se este elemento for criado em um grupo, onde existem outros objetos semelhantes, então, apenas um identificador não é suficiente. Isto deve-se pelo fato dos nomes de todos os objetos gráficos do mesmo tipo serem o mesmo e, como resultado apenas um elemento será visível no gráfico.

Quando um elemento de menu é criado, o número do índice deste elemento será passado para o método CMenuItem::CreateMenuItem(). Em seguida, ele será armazenado no campo da classe m_index_number, que mais tarde será utilizado para formar o nome de cada objeto gráfico do elemento. Abaixo estão as partes integrantes do nome do objeto do elemento de menu:

  • Nome do programa.
  • Pertencente ao elemento.
  • Pertencente a uma parte do elemento.
  • Número de índice do elemento.
  • Identificador do elemento (ID).

Os métodos de criação de um fundo, um rótulo de texto e da indicação da presença do menu de contexto não possuem nenhuma diferença importante em relação as apresentadas na classe CWindow e, portanto, você pode estudar o seu código no arquivo MenuItem.mqh anexado neste artigo. Aqui, nós vamos demonstrar o método CMenuItem::CreateIcon() para a criação de um ícone como exemplo. Neste método, o tipo de elemento de menu é verificado e, dependendo do resultado, um ícone relevante é definido.

Elementos de menu simples (MI_SIMPLE) e os que contêm um menu de contexto (MI_HAS_CONTEXT_MENU) não pode ter um ícone de modo algum. Se eles não são definidos pelo usuário, então o programa sai do método retornando true, já que este não é um erro e um ícone não é necessário. Se os elementos de menu do tipo caixa de seleção ou botão de rádio não foram definidos, utiliza-se seus tipos padrões. Os ícones utilizados no código da biblioteca estão anexados neste artigo.

O código do método CMenuItem::CreateIcon():

//+------------------------------------------------------------------+
//| Cria o elemento rótulo                                           |
//+------------------------------------------------------------------+
#resource "\\Images\\Controls\\CheckBoxOn_min_gray.bmp"
#resource "\\Images\\Controls\\CheckBoxOn_min_white.bmp"
//---
bool CMenuItem::CreateIcon(void)
  {
//--- Se este é um elemento simples ou um que contém um menu de contexto
   if(m_type_menu_item==MI_SIMPLE || m_type_menu_item==MI_HAS_CONTEXT_MENU)
     {
      //--- Se o rótulo não é exigido (ícone não está definido), retorna "true"
      if(m_icon_file_on=="" || m_icon_file_off=="")
         return(true);
     }
//--- Se este é uma caixa de seleção
   else if(m_type_menu_item==MI_CHECKBOX)
     {
      //--- Se o ícone não foi definido, define o seu padrão
      if(m_icon_file_on=="")
         m_icon_file_on="Images\\Controls\\CheckBoxOn_min_white.bmp";
      if(m_icon_file_off=="")
         m_icon_file_off="Images\\Controls\\CheckBoxOn_min_gray.bmp";
     }
//--- Se este é um elemento de botão de rádio     
   else if(m_type_menu_item==MI_RADIOBUTTON)
     {
      //--- Se o ícone não foi definido, define o seu padrão
      if(m_icon_file_on=="")
         m_icon_file_on="Images\\Controls\\CheckBoxOn_min_white.bmp";
      if(m_icon_file_off=="")
         m_icon_file_off="Images\\Controls\\CheckBoxOn_min_gray.bmp";
     }
//--- Elaborando o nome do objeto
   string name=CElement::ProgramName()+"_menuitem_icon_"+(string)CElement::Index()+"__"+(string)CElement::Id();
//--- Coordenadas do objeto
   int x =m_x+7;
   int y =m_y+4;
//--- Define o rótulo
   if(!m_icon.Create(m_chart_id,name,m_subwin,x,y))
      return(false);
//--- Define as propriedades
   m_icon.BmpFileOn("::"+m_icon_file_on);
   m_icon.BmpFileOff("::"+m_icon_file_off);
   m_icon.State(m_item_state);
   m_icon.Corner(m_corner);
   m_icon.GetInteger(OBJPROP_ANCHOR,m_anchor);
   m_icon.Selectable(false);
   m_icon.Z_Order(m_zorder);
   m_icon.Tooltip("\n");
//--- Margens do ponto extremo
   m_icon.XGap(x-m_wnd.X());
   m_icon.YGap(y-m_wnd.Y());
//--- Armazena o ponteiro de objeto
   CElement::AddToArray(m_icon);
   return(true);
  }

O método CMenuItem::Hide() para ocultar o elemento foi mostrado anteriormente. Agora, nós vamos implementar o método CMenuItem::Show() que fará com que o elemento seja visível. No caso de o elemento de menu ser do tipo caixa de seleção ou botão de rádio, este método tem de considerar o seu estado atual (ativado/desativado):

//+------------------------------------------------------------------+
//| Faz com que o elemento de menu seja visível                      |
//+------------------------------------------------------------------+
void CMenuItem::Show(void)
  {
//--- Retorna, se o elemento já for visível
   if(CElement::m_is_visible)
      return;
//--- Faz com que todos os objetos sejam visíveis
   for(int i=0; i<ObjectsElementTotal(); i++)
      Object(i).Timeframes(OBJ_ALL_PERIODS);
//--- Se este é uma caixa de seleção, verifica o seu estado
   if(m_type_menu_item==MI_CHECKBOX)
      m_icon.Timeframes((m_checkbox_state)? OBJ_ALL_PERIODS : OBJ_NO_PERIODS);
//--- Se este é um botão de rádio, verifica o seu estado
   else if(m_type_menu_item==MI_RADIOBUTTON)
      m_icon.Timeframes((m_radiobutton_state)? OBJ_ALL_PERIODS : OBJ_NO_PERIODS);
//--- Zerando as variáveis
   CElement::m_is_visible=true;
   CElement::MouseFocus(false);
  }

Quando todos os métodos para a criação do elemento de interface forem implementados, será possível realizar os testes de anexar a interface ao gráfico. Faça a inclusão da classe CMenuItem no arquivo WndContainer.mqh para que ela esteja disponível para uso:

//+------------------------------------------------------------------+
//|                                                 WndContainer.mqh |
//|                        Copyright 2015, MetaQuotes Software Corp. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
#include "Window.mqh"
#include "MenuItem.mqh"

Nós podemos usar o EA que testamos no artigo anterior. Na classe CProgram , crie uma instância da classe CMenuItem e do método CProgram::CreateMenuItem1() para a criação do elemento de menu. As margens do ponto da borda do formulário para cada elemento criado serão mantidas acessível em macros. Quando há um grande número de elementos, esta é uma maneira mais conveniente e rápida para ajustar a sua posição em relação aos outros do que mudar de uma implementação de método para outro.

//+------------------------------------------------------------------+
//| Classe para a criação de um aplicativo                           |
//+------------------------------------------------------------------+
class CProgram : public CWndEvents
  {
private:
   //--- Janela
   CWindow           m_window;
   //--- Elemento de menu
   CMenuItem         m_menu_item1;
public:
                     CProgram();
                    ~CProgram();
   //--- Inicialização/desinicialização
   void              OnInitEvent(void);
   void              OnDeinitEvent(const int reason);
   //--- Timer
   void              OnTimerEvent(void);
   //---
protected:
   //--- Manipulador de eventos do gráfico
   virtual void      OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam);
   //---
public:
   //--- Cria o painel de negociação
   bool              CreateTradePanel(void);
   //---
private:
//--- Criação de um formulário
   bool              CreateWindow(const string text);
//--- Criando o elemento de menu
#define MENU_ITEM1_GAP_X (6)
#define MENU_ITEM1_GAP_Y (25)
   bool              CreateMenuItem1(const string item_text);
  };

Para visualizar como se parece um elemento com um conjunto completo de recursos, vamos criar um elemento de menu com um ícone, que contém um menu de contexto para realizar um teste. Nós teremos dois ícones - coloridos e incolores. O ícone incolor será usado quando o elemento estiver bloqueado (indisponível).

Segue abaixo o código do método CProgram::CreateMenuItem1(). Os resources (ícones) incluídos estão disponíveis no final deste artigo. No início do método, o ponteiro do formulário na qual o elemento será anexado, é armazenado no elemento da classe. Em seguida, as coordenadas são calculadas, as propriedades necessárias do elemento são definidas e o elemento de menu é anexado ao gráfico.

//+------------------------------------------------------------------+
//| Cria o elemento de menu                                          |
//+------------------------------------------------------------------+
#resource "\\Images\\Controls\\bar_chart.bmp"
#resource "\\Images\\Controls\\bar_chart_colorless.bmp"
//---
bool CProgram::CreateMenuItem1(string item_text)
  {
//--- Armazena o ponteiro da janela
   m_menu_item1.WindowPointer(m_window);
//--- Coordenadas  
   int x=m_window.X()+MENU_ITEM1_GAP_X;
   int y=m_window.Y()+MENU_ITEM1_GAP_Y;
//--- Configura as propriedades antes da criação
   m_menu_item1.XSize(193);
   m_menu_item1.YSize(24);
   m_menu_item1.TypeMenuItem(MI_HAS_CONTEXT_MENU);
   m_menu_item1.IconFileOn("Images\\Controls\\bar_chart.bmp");
   m_menu_item1.IconFileOff("Images\\Controls\\bar_chart_colorless.bmp");
   m_menu_item1.LabelColor(clrWhite);
   m_menu_item1.LabelColorHover(clrWhite);
//--- Criando o elemento de menu
   if(!m_menu_item1.CreateMenuItem(m_chart_id,m_subwin,0,item_text,x,y))
      return(false);
//---
   return(true);
  }

Agora, é possível adicionar a chamada do método para a criação do elemento de menu no método CProgram::CreateTradePanel():

//+------------------------------------------------------------------+
//| Cria o painel de negociação                                      |
//+------------------------------------------------------------------+
bool CProgram::CreateTradePanel(void)
  {
//--- Criação de um formulário para os controles
   if(!CreateWindow("EXPERT PANEL"))
      return(false);
//--- Criação dos controles:
//    Elemento de menu
   if(!CreateMenuItem1("Menu item"))
      return(false);
//--- Redesenho do gráfico
   m_chart.Redraw();
   return(true);
  }

Um leitor atento terá uma pergunta: "Como o ponteiro do elemento chegará na base?". Esta é uma boa pergunta já que isso ainda não foi implementado e o ponteiro do elemento ainda não foi armazenado na base. Anteriormente, eu mencionei que iria demonstrar uma peculiaridade interessante do compilador. Agora, tudo está pronto para isso. Atualmente, os métodos para gerenciar o elemento e manipular os eventos não estão implementados na classe CMenuItem e não há ponteiro na base. A compilação do arquivo com a classe CMenuItem não indica erro algum. Tente compilar o arquivo do EA e você receberá uma mensagem de erro especificando os métodos para os quais a sua implementação é necessária (veja a imagem abaixo).

Fig. 4. Mensagem sobre a falta de implementação do método

Fig. 4. Mensagem sobre a falta de implementação do método

Quando o arquivo foi compilado com a classe CMenuItem, nenhum erro foi encontrado pois no nível atual de desenvolvimento desta classe, não foi chamado nenhum método sem sua implementação. O elemento base,no momento, tem apenas o ponteiro do formulário. Parece que a chamada dos métodos que você vê na imagem acima foi realizada apenas para o formulário (CWindow) na classe CWndEvents nos loops dos métoos CheckElementsEvents(), MovingWindow(), CheckElementsEventsTimer() e Destroy(). Os ponteiros de elemento são armazenados no array do tipo CElement, o que significa que a chamada é realizada através da classe base CElement, onde estes métodos são declarados como virtual.

Uma vez que no arquivo WndContainer.mqh, na base estão incluídos dois arquivos com os elementos Window.mqh e MenuItem.mqh e suas classes são derivadas a partir da classe CElement, o compilador identifica todas as classes derivadas dele e exige a implementação dos métodos chamados, mesmo se um deles não tiver uma chamada direta. Vamos criar os métodos necessários na classe CMenuItem. Nós também vamos criar um método que nos permitirá adicionar ponteiros de controle para a base na classe CWndContainer.

Nesta fase, é possível adicionar o método CMenuItem::ChangeObjectsColor() para alterar a cor dos objetos do elemento quando o cursor do mouse estiver pairando sobre ele. Neste método, o tipo e o estado do elemento de menu (disponível/bloqueado) precisa ser considerado. No início deste método, deve haver uma verificação se este elemento contém um menu de contexto e se o mesmo está habilitado no momento. A razão é que quando um menu de contexto é habilitado, a gestão é passada para ele. Além disso, a cor do elemento que foi chamado deve ser gravada.

Mais tarde, nós também precisaremos de um método para "resetar" a cor do foco no elemento de menu, por isso vamos criar o método CMenuItem::ResetColors().

class CMenuItem : public CElement
  {
public:
   //--- Alteração da cor dos objetos de controle
   void              ChangeObjectsColor(void);
   //--- Resetar a cor
   void              ResetColors(void);
   //---
  };
//+------------------------------------------------------------------+
//| Alterar a cor do objeto quando o cursor estiver pairando sobre ele|
//+------------------------------------------------------------------+
void CMenuItem::ChangeObjectsColor(void)
  {
//--- Retorna, se esse elemento tem um menu de contexto e ele está habilitado
   if(m_type_menu_item==MI_HAS_CONTEXT_MENU && m_context_menu_state)
      return;
//--- Bloco de código para elementos simples e os que contêm um menu de contexto
   if(m_type_menu_item==MI_HAS_CONTEXT_MENU || m_type_menu_item==MI_SIMPLE)
     {
      //--- Se houver um foco
      if(CElement::MouseFocus())
        {
         m_icon.State(m_item_state);
         m_area.BackColor((m_item_state)? m_area_color_hover : m_area_color_off);
         m_label.Color((m_item_state)? m_label_color_hover : m_label_color_off);
         if(m_item_state)
            m_arrow.State(true);
        }
      //--- Se não houver um foco
      else
        {
         m_arrow.State(false);
         m_area.BackColor(m_area_color);
         m_label.Color((m_item_state)? m_label_color : m_label_color_off);
        }
     }
//--- Código do bloco para os elementos de caixa de seleção e botão de radio
   else if(m_type_menu_item==MI_CHECKBOX || m_type_menu_item==MI_RADIOBUTTON)
     {
      m_icon.State(CElement::MouseFocus());
      m_area.BackColor((CElement::MouseFocus())? m_area_color_hover : m_area_color);
      m_label.Color((CElement::MouseFocus())? m_label_color_hover : m_label_color);
     }
  }
//+------------------------------------------------------------------+
//| Reseta a cor do elemento                                         |
//+------------------------------------------------------------------+
void CMenuItem::ResetColors(void)
  {
   CElement::MouseFocus(false);
   m_area.BackColor(m_area_color);
   m_label.Color(m_label_color);
  }

Os métodos para mover e remover os objetos são implementados de uma forma semelhante ao que foi feito na classe CWindow e, portanto, não há nenhum ponto em repeti-los aqui. Você pode encontrar o seu código nos arquivos anexados a este artigo. Por isso, adicione o mínimo de código necessário (veja o código abaixo) para os métodos CMenuItem::OnEvent() e CMenuItem::OnEventTimer() e compile os arquivos da biblioteca e o EA. Agora, não haverá quaisquer erros que relatam a necessidade da implementação do método.

//+------------------------------------------------------------------+
//| Manipulador de eventos                                           |
//+------------------------------------------------------------------+
void CMenuItem::OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam)
  {
   if(id==CHARTEVENT_MOUSE_MOVE)
     {
      //--- Se o controle não está oculto
      if(!CElement::m_is_visible)
         return;
      //--- Identificador do foco
      int x=(int)lparam;
      int y=(int)dparam;
      CElement::MouseFocus(x>X() && x<X2() && y>Y() && y<Y2());
      return;
     }  
  }
//+------------------------------------------------------------------+
//| Timer                                                            |
//+------------------------------------------------------------------+
void CMenuItem::OnEventTimer(void)
  {
//--- Alterando a cor dos objetos do formulário
   ChangeObjectsColor();
  }


Teste da colocação de um Elemento de Menu

Quando você carrega o EA para o gráfico, você verá um formulário com o elemento de menu, como na imagem abaixo. Você não pode visualizá-lo aqui já que a cor padrão de fundo do elemento coincide com a cor de fundo do formulário. Você já pode usar a funcionalidade da classe para mudar muitas propriedades dos objetos - é necessário tanto o formulário como o elemento de menu.

Fig. 5. Teste de anexar o elemento de menu ao gráfico.

Fig. 5. Teste de anexar o elemento de menu ao gráfico

O elemento é anexado ao gráfico, mas se você tentar mover o formulário, o elemento irá ficar no mesmo local e se você passar o cursor sobre ele, sua aparência não irá mudar. Vamos criar um método para adicionar ponteiros de elemento para a base. As propriedades de todos os elementos podem ser gerenciadas através da chamada de cada uma delas em um loop.

Vamos chamar este método de CWndContainer::AddToElementsArray(). Ele terá dois parâmetros: (1) Número do índice do formulário, a qual o elemento supostamente será anexado (2) elemento do objeto, um ponteiro que deverá ser guardado na base. No início deste método, haverá uma verificação se a base contém um ou mais formulários. Se não houver nenhum, então, será impresso uma mensagem no registro dizendo que antes de tentar anexar o elemento ao formulário, ele tem que ser adicionado primeiramente à base. Em seguida, haverá uma verificação se o tamanho do array não excedeu.

Se não há problemas, então (1) o ponteiro é adicionado ao array de formulário a qual ele está anexado, (2) os objetos do elemento são adicionados ao array comum de objetos, (3) o último identificador deste elemento é armazenado em todos os formulários e (4) o contador de elementos é incrementado de um. Esta não é a versão final do método, nós vamos voltar a isso mais tarde. O código da versão atual é apresentado a seguir:

//+------------------------------------------------------------------+
//| Adiciona um ponteiro ao array de elemento                        |
//+------------------------------------------------------------------+
void CWndContainer::AddToElementsArray(const int window_index,CElement &object)
  {
//--- Se a base não contém formulários para os controles
   if(ArraySize(m_windows)<1)
     {
      Print(__FUNCTION__," > Antes de criar um controle, crie um formulário  "
            "e adicione-o a base usando o método CWndContainer::AddWindow(CWindow &object).");
      return;
     }
//--- Se houver uma solicitação para uma formulário não-existente
   if(window_index>=ArraySize(m_windows))
     {
      Print(PREVENTING_OUT_OF_RANGE," window_index: ",window_index,"; ArraySize(m_windows): ",ArraySize(m_windows));
      return;
     }
//--- Adiciona ao array comum de elementos
   int size=ArraySize(m_wnd[window_index].m_elements);
   ArrayResize(m_wnd[window_index].m_elements,size+1);
   m_wnd[window_index].m_elements[size]=GetPointer(object);
//--- Adiciona os objetos do elemento para o array comum de objetos
   AddToObjectsArray(window_index,object);
//--- Armazena o ID do último elemento em todos os formulários
   int windows_total=ArraySize(m_windows);
   for(int w=0; w<windows_total; w++)
      m_windows[w].LastId(m_counter_element_id);
//--- Aumenta o contador de identificadores do elemento
   m_counter_element_id++;
  }

A versão atual do método CWndContainer::AddToElementsArray() já é suficiente para testar o EA, que começamos a trabalhar anteriormente. No final do método CProgram::CreateMenuItem(), após a criação de um elemento, adicione uma linha de código, como mostrado na versão curta do método abaixo.

//+------------------------------------------------------------------+
//| Cria o elemento de menu                                          |
//+------------------------------------------------------------------+
bool CProgram::CreateMenuItem(string item_text)
  {
//--- Armazena o ponteiro da janela
//--- Coordenadas  
//--- Configura as propriedades antes da criação
//--- Criando o elemento de menu
//--- Adiciona o ponteiro do elemento para a base
   CWndContainer::AddToElementsArray(0,m_menu_item);
   return(true);
  }

Se os arquivos onde as mudanças foram introduzidas são compilados e o EA é carregado para o gráfico, então, quando o formulário é deslocado, o elemento de menu estará se movendo junto com ele e todos os seus objetos irão mudar sua aparência quando o cursor do mouse estiver sobre ele:

Fig. 6. Teste do elemento de menu como uma parte da interface gráfica

Fig. 6. Teste do elemento de menu como uma parte da interface gráfica


Melhorando as Classes Principais da Biblioteca

Se o formulário for minimizado agora, o elemento de menu não será oculto, como esperado. Como podemos implementar o ocultamento dos elementos anexados ao formulário? Atualmente, a gestão dos eventos do gráfico está organizada na classe CWndEvents, que tem acesso à base de todos os elementos de sua própria classe base, a CWndContainer. Existe alguma indicação de que um botão de minimizar ou maximizar foi pressionado? Para tais casos, a MQL possui a função EventChartCustom() que pode gerar eventos personalizados.

Agora, quando o botão para minimizar o formulário for clicado, o programa irá acompanhar o evento CHARTEVENT_OBJECT_CLICK no manipulador OnEvent() da classe CWindow e, tendo verificado o valor do parâmetro de string (sparam) deste evento junto do nome do objeto gráfico, o método CWindow::RollUp() será chamado. Este é o momento em que uma mensagem poderá ser enviada para a fila do fluxo de evento e ser recebida no manipulador da classe CWndEvents. Como a classe CWndEvents tem acesso a todos os elementos, o método CElement::Hide() de cada elemento pode ser chamado no loop. O mesmo deve ser feito para o evento de maximização do formulário, fazendo com que todos os elementos do formulário sejam visíveis usando os seus métodos CElement::Show().

Cada evento personalizado requer seu identificador exclusivo. Adicione os identificadores para a minimização/maximização do formulário para o arquivo Defines.mqh:

//--- Eventos
#define ON_WINDOW_UNROLL          (1) // Maximização do formulário
#define ON_WINDOW_ROLLUP          (2) // Minimização do formulário

No final da chamada dos métodos CWindow::RollUp() e CWindow::Unroll() a função EventChartCustom() passou por elas:

  1. O identificador do gráfico.
  2. O identificador de evento personalizado.
  3. O identificador do elemento como o terceiro parâmetro (lparam).
  4. O número da sub-janela do gráfico e onde o programa está localizado como quarto parâmetro (dparam).

O terceiro e o quarto parâmetro são necessários para a verificação adicional no caso deste evento ser enviado por um outro elemento ou outro programa.

Abaixo encontramos as versões curtas dos métodos CWindow::RollUp() e CWindow::Unroll() contendo apenas o que tem de ser adicionado a eles (Todos os comentários foram mantidos):

//+------------------------------------------------------------------+
//| Minimiza a janela                                                |
//+------------------------------------------------------------------+
void CWindow::RollUp(void)
  {
//--- Altera o botão
//--- Define e armazena o tamanho
//--- Desabilita o botão
//--- Minimiza o estado do formulário
//--- Se este é um indicador com uma altura definida e com a sub-janela no modo minimizado,
//    define o tamanho do indicador da sub-janela
//--- Envia uma mensagem sobre ele
   ::EventChartCustom(m_chart_id,ON_WINDOW_ROLLUP,CElement::Id(),m_subwin,"");
  }
//+------------------------------------------------------------------+
//| Maximiza a janela                                                |
//+------------------------------------------------------------------+
void CWindow::Unroll(void)
  {
//--- Altera o botão
//--- Define e armazena o tamanho
//--- Desabilita o botão
//--- Maximiza o estado do formulário
//--- Se este é um indicador com uma altura definida e com a sub-janela no modo minimizado,
//    define o tamanho do indicador da sub-janela
//--- Envia uma mensagem sobre ele
   ::EventChartCustom(m_chart_id,ON_WINDOW_UNROLL,CElement::Id(),m_subwin,"");
  }

Agora, nós precisamos criar os métodos na classe CWndEvents, que irá manipular esses eventos personalizados. Vamos nomeá-los da mesma forma que os macros para os identificadores. Abaixo está o código para a declaração e implementação dos métodos na classe CWndEvents:

class CWndEvents : public CWndContainer
  {
private:
   //--- Minimizando/maximizando o formulário
   bool              OnWindowRollUp(void);
   bool              OnWindowUnroll(void);
   //---
  };
//+------------------------------------------------------------------+
//| Evento ON_WINDOW_ROLLUP                                          |
//+------------------------------------------------------------------+
bool CWndEvents::OnWindowRollUp(void)
  {
//--- Se o sinal for para minimizar o formulário
   if(m_id!=CHARTEVENT_CUSTOM+ON_WINDOW_ROLLUP)
      return(false);
//--- Se o identificador da janela e o número da sub-janela coincidirem
   if(m_lparam==m_windows[0].Id() && (int)m_dparam==m_subwin)
     {
      int elements_total=CWndContainer::ElementsTotal(0);
      for(int e=0; e<elements_total; e++)
        {
         //--- Esconder todos os elementos, exceto o formulário
         if(m_wnd[0].m_elements[e].ClassName()!="CWindow")
            m_wnd[0].m_elements[e].Hide();
        }
     }
//---
   return(true);
  }
//+------------------------------------------------------------------+
//| Evento ON_WINDOW_UNROLL                                          |
//+------------------------------------------------------------------+
bool CWndEvents::OnWindowUnroll(void)
  {
//--- Se o sinal for para maximizar o formulário
   if(m_id!=CHARTEVENT_CUSTOM+ON_WINDOW_UNROLL)
      return(false);
//--- Se o identificador da janela e o número da sub-janela coincidirem
   if(m_lparam==m_windows[0].Id() && (int)m_dparam==m_subwin)
     {
      int elements_total=CWndContainer::ElementsTotal(0);
      for(int e=0; e<elements_total; e++)
        {
         //--- Faça todos os elementos visíveis, exceto o formulário e a
         //    lista suspensa
         if(m_wnd[0].m_elements[e].ClassName()!="CWindow")
            if(!m_wnd[0].m_elements[e].IsDropdown())
               m_wnd[0].m_elements[e].Show();
        }
     }
//---
   return(true);
  }

O método CWindow::Show() ainda não foi implementado na classe do formulário. Como este método será chamado em todos os elementos em um loop, a sua implementação é obrigatória, mesmo se uma condição não permite que o programa chegue a classe CWindow, como é mostrado no código acima.

Código do método CWindow::Show():

//+------------------------------------------------------------------+
//| Exibe a janela                                                   |
//+------------------------------------------------------------------+
void CWindow::Show(void)
  {
//--- Faz com que todos os objetos sejam visíveis
   for(int i=0; i<ObjectsElementTotal(); i++)
      Object(i).Timeframes(OBJ_ALL_PERIODS);
//--- Estado de visibilidade
   CElement::m_is_visible=true;
//--- Zera o foco
   CElement::MouseFocus(false);
   m_button_close.MouseFocus(false);
   m_button_close.State(false);
  }

Nós vamos chamar esses métodos no método CWndEvents::ChartEventCustom():

//+------------------------------------------------------------------+
//| Evento CHARTEVENT_CUSTOM                                         |
//+------------------------------------------------------------------+
void CWndEvents::ChartEventCustom(void)
  {
//--- Se o sinal for para minimizar o formulário
   if(OnWindowRollUp())
      return;
//--- Se o sinal for para maximizar o formulário
   if(OnWindowUnroll())
      return;
  }

A partir de agora, todos os métodos que manipulam os eventos personalizados estarão localizados no método CWndEvents::ChartEventCustom().

Por sua vez, a chamada do CWndEvents::ChartEventCustom() tem de ser colocado no método CWndEvents::ChartEvent() antes dos métodos onde os eventos do gráfico são tratados:

//+------------------------------------------------------------------+
//| Manipulação de eventos do programa                               |
//+------------------------------------------------------------------+
void CWndEvents::ChartEvent(const int id,const long &lparam,const double &dparam,const string &sparam)
  {
//--- Retorna, se o array está vazio
   if(CWndContainer::WindowsTotal()<1)
      return;
//--- Inicialização dos campos dos parâmetros de evento
   InitChartEventsParams(id,lparam,dparam,sparam);
//--- Evento personalizado
   ChartEventCustom();
//--- Eventos de verificação dos elementos de interface
   CheckElementsEvents();
//--- Evento do movimento do mouse
   ChartEventMouseMove();
//--- O evento de alteração das propriedades do gráfico
   ChartEventChartChange();
  }

Depois de compilar os arquivos que sofreram alterações e o arquivo principal do programa, carregue-o para o gráfico. Agora, quando o formulário for minimizado, o elemento de menu será oculto e se tornará visível quando o formulário for maximizado.

Nós completamos o desenvolvimento da parte principal da classe CMenuItem. Agora, nós só temos que configurar o manipulador de eventos desse controle. Nós vamos voltar a isto depois de termos implementado a classe para a criação de um menu de contexto para que todas as alterações possam ser testadas de forma consistente e completa. Antes disso, nós vamos desenvolver um outro elemento de interface, que é uma parte de um menu de contexto - a linha de separação.


Conclusão

Neste artigo, discutimos em pormenor o processo de criação do controle do elemento de menu. Nós também introduzimos as adições necessárias à classe de formulário para os controles (CWindow) e classe principal de manipulação de eventos (CWndEvents). No próximo artigo nós vamos criar as classes para criar uma linha de separação e um menu de contexto.

Você pode buscar e baixar os arquivos da biblioteca no atual estágio de desenvolvimento, as imagens e os arquivos dos programas discutidos no artigo (o EA, indicadores e o script) podem ser baixados para testes nos terminais MetaTrader 4 e MetaTrader 5. Se você tiver dúvidas sobre a utilização do material a partir desses arquivos, você poderá consultar a descrição detalhada do desenvolvimento da biblioteca em um dos artigos da lista abaixo ou fazer sua pergunta nos comentários deste artigo.

Lista de artigos (capítulos) da segunda parte:

  • Interfaces Gráficas II: O Elemento de Menu (Capítulo 1)
  • Interfaces Gráficas II: Os Elementos Linha de Separação e o Menu de Contexto (Capítulo 2)
  • Interfaces Gráficas II: Configuração dos manipuladores de eventos da Biblioteca (Capítulo 3)
  • Interfaces Gráficas II: O Elemento Menu Principal (Capítulo 4)

Traduzido do russo pela MetaQuotes Ltd.
Artigo original: https://www.mql5.com/ru/articles/2200

Arquivos anexados |
Interfaces Gráficas II: Os Elementos Linha de Separação e o Menu de Contexto (Capítulo 2) Interfaces Gráficas II: Os Elementos Linha de Separação e o Menu de Contexto (Capítulo 2)
Neste artigo, nós vamos criar o elemento linha de separação. Será possível usá-lo não só como um elemento de interface independentes, mas também como uma parte de diversos outros elementos. Depois disso, nós teremos todos os recursos necessários para o desenvolvimento da classe do menu de contexto, que também serão considerados neste artigo em detalhe. Acrescentando, nós vamos apresentar todos os incrementos necessários à classe, que é a base para armazenar os ponteiros para todos os elementos da interface gráfica da aplicação.
Como criar bots para Telegram em MQL5 Como criar bots para Telegram em MQL5
Este artigo contém instruções passo-a-passo para a criação de bots para o Telegram em MQL5. Esta informação pode ser útil aos usuários que desejam sincronizar o seu robô de negociação a um dispositivo móvel. Existem exemplos de bots no artigo que fornecem sinais de negociação, busca de informações em sites, enviam informações sobre o balanço da conta, cotações e imagens de gráficos ao seu telefone celular.
Que testes deve passar o robô de negociação antes da publicação no Mercado Que testes deve passar o robô de negociação antes da publicação no Mercado
Todos os produtos do Mercado, antes de serem publicados, passam uma revisão preliminar obrigatória para garantir um único padrão de qualidade. Neste artigo, vamos falar sobre os erros mais comuns que os desenvolvedores cometem ao trabalhar com os seus indicadores técnicos e robôs de negociação. Além disso, mostraremos como testar por si mesmo o seu produto antes de enviá-lo para o Mercado.
Lógica difusa na negociação via MQL4 Lógica difusa na negociação via MQL4
Este artigo apresenta exemplos que tratam de como os recursos MQL4 aplicam a teoria de conjuntos difusos na negociação. Além disso, descreve o desenvolvimento de indicador e Expert Advisor, usando a biblioteca FuzzyNet para MQL4.