English Русский 中文 Español Deutsch 日本語 한국어 Français Italiano Türkçe
Controles gráficos personalizados. Parte 3. Formas

Controles gráficos personalizados. Parte 3. Formas

MetaTrader 5Exemplos | 21 janeiro 2014, 13:21
2 025 0
Dmitry Fedoseev
Dmitry Fedoseev

Introdução

O primeiro artigo "Criando um controle simples" tratava sobre os princípios da criação de controle gráfico e fornecia um exemplo passo a passo demonstrando a criação de um controle simples. O artigo de continuação "Biblioteca de controles" estabelece um conjunto de controles prontos. Há ainda outro componente muito importante da interface gráfica - a forma.

A forma representa uma parte retangular especialmente projetada da tela onde os controles são exibidos. Além disso, a forma é também um contentor que permite gerenciar simultaneamente todos os controles que contém: ocultar, exibir e mover todos juntos.

O artigo de fechamento estabelecerá a criação de formas e seu uso em combinação com controles.

As classes para trabalhar com as formas foram adicionadas ao arquivo IncGUI.mqh utilizado nos artigos anteriores (o novo nome do arquivo é IncGUI_v3.mqh). Além das classes para trabalhar com formas, algumas classes a mais foram adicionadas ao arquivo, as classes CHMenu (Menu horizontal) e CVMenu (Menu vertical) foram atualizadas e alguns erros foram consertados.

Classes adicionadas:

  • Classe CFrame - Armação. A classe é idêntica à armação criada pelo método Frame da classe CWorkPiece.
  • Classe CButton - Botão. Um botão comum (OBJ_BUTTON).
  • Classe CLabel - Etiqueta. Fora exibir a etiqueta, como é feito pelo objeto gráfico "Etiqueta" (OBJ_LABEL), esta classe permite exibir um texto em algumas linhas onde as linhas são separadas pelo símbolo "\n".

Como resultado por adicionar novas classes, a classe CColorSchemes foi modificada da seguinte forma: as cores para os novos controles foram adicionadas.

Mudanças nas classes CHMenu e CVMenu: as mudanças aplicadas permitem criar menus de dois níveis com guias suspensas. Isso será considerado mais detalhadamente na seção "Criando o menu principal" abaixo.

Erros: uma correção no método Frame() da classe CWorkPiece - um número de subjanela para uma etiqueta de retângulo não pode ser definido. Classe CListMS - Os métodos Selected() e SetSelected() - o tamanho do array agora é verificado, do contrário, era impossível usar uma lista vazia.

1. Formas

A forma é baseada em objetos gráficos "Etiqueta retângulo" OBJ_RECTANGLE_LABEL com uso de diversos botões OBJ_BUTTON. Visualmente, a forma representa um retângulo (Fig. 1) com uma barra no parte de cima da forma, onde o nome da forma e os botões de controle são exibidos.

Um botão movendo a forma (com a imagem de uma mão) é situado à esquerda, os botões de minimizar (com a imagem de um retângulo) e de fechar (com a imagem de um x) estão situados à direita.

Fig. 1. Forma

Fig. 1. Forma

Para mover a forma, pressione o botão mover (o botão ficará na posição pressionada) e clique em qualquer lugar no gráfico para onde a forma deve ser movida. Como resultado, o botão mover será solto e a forma se moverá para o local indicado (seu canto esquerdo será situado no ponto clicado).

Onde o local indicado está na extremidade do gráfico e a forma deve ser posicionada do lado de fora da borda do gráfico, a posição da forma será ajustada de modo que seja completamente visível no gráfico.

É possível criar diversos tipos de formas:

  • Tipo 0 (Fig. 1);
  • Tipo 1 - com os botões adicionais "Cancelar" e "Aplicar" (Fig. 2);
  • Tipo 2 - com o botão adicional "Fechar" (Fig. 3);

Fig. 2. Forma de tipo 1 (com botões

Fig. 2. Forma de tipo 1 (com botões "Cancelar" e "Aplicar")

Fig. 3. Forma de tipo 2 (com botão

Fig. 3. Forma de tipo 2 (com botão "Fechar")

Meios programáticos de trabalho com formas são representados por duas classes: CFormBase e CFormTemplate.

A classe CFormBase é uma classe básica enquanto a classe CFormTemplate é uma subclasse da classe CFormBase. Em efeito e princípio (incluindo o princípio de aplicação), a classe CFormBase é exatamente o mesmo controle que outros controles descritos anteriormente.

A classe CFormBase tem o método Init() para preparação de controle, os métodos SetPos() e Show() para exibição de uma forma (criação de objetos gráficos representando uma forma), o método Hide() para ocultar, o método Refresh() para atualizar o display, o método Event() para a manipulação de evento e outros métodos como em todos os outros controles.

A aplicação de uma forma, assim como o uso de controles, começa com uma chamada do método Init(), seguido pela configuração de uma posição com a utilização do método SetPos() e possibilitando a exibição pelo método Show() ou, caso contrário, a exibição pode ser possibilitada utilizando apenas o método Show() com parâmetros, e uma forma pode ser oculta usando o método Hide().

Uma chamada do método Event() é adicionada à função OnChartEvent() para garantir à forma a capacidade de controle (mover, minimizar, fechar, etc.). Onde é necessário garantir a operação da forma em uma subjanela, o método SetSubWindows() é usado e chamado logo após a chamada do método Init(), assim como a função OnChartEvent() sob o evento CHARTEVENT_CHART_CHANGE. Tudo é igual como nos outros controles.

Embora os controles padrão sejam independentes (eles não precisam ser associados com outros controles), a forma implica na existência de outros controles para garantir a exibição/ocultação simultânea dos controles situados na forma junto com a ocultação/exibição da forma. Assim, deve ser garantida a operação sincronizada dos métodos Init(), Show() e Hide() da forma e dos controles relacionados.

Certamente é possível apenas chamar (ou seja, quando chamar o método Show() da forma) os métodos Show() de todos os controles relacionadas à forma, mas quando diversas formas são utilizadas no programa, tal abordagem resultará em um código que é desestruturado e inconveniente para entender, depurar e atualizar.

Uma pessoa pode fazer uma cópia da classe da forma para cada forma e adicionar o código de trabalho com controles; porém, isso resultará novamente no código desestruturado (onde o código da forma não será separado do código relacionado à operação de controle), duplicação excessiva do código, onde apenas parte do código encarregado do design e funcionalidade da forma será duplicada; tudo isso complicará a aplicação de modificações a quaisquer funções da forma devido a precisar de mudanças em toda cópia de classe.

A divisão em classe básica e subclasses ajuda a resolver o problema de criação de diversas formas, sincronização de exibição da forma e controles relacionados e separação clara do código relacionado a diferentes formas. Além de resolver os problemas básicos, o uso de classe e subclasse permitirá atualização futura da classe básica sem ter que fazer nenhuma modificação no trabalho já concluído.

Para sincronizar a chamada dos métodos Init(), Show() e Hide() dos controles e forma, a classe CFormBase tem algumas diferenças da classe de controle padrão: alguns métodos virtuais em uma seção protegida:

virtual void MainProperties() {}
virtual void OnInitEvent() {}
virtual void OnShowEvent() {}
virtual void OnHideEvent() {}
virtual void EventsHandler(const int id, const long& lparam, const double& dparam, const string& sparam){}
virtual void OnWindowChangeEvent(int aSubWindow) {}
virtual bool OnCancelEvent() {return(true);}
virtual bool OnApplyEvent()  {return(true);} 

Estes métodos são virtuais o que significa que o redirecionamento aos métodos reais correspondentes da subclasse CFormTemplate é realizado através deles.

A subclasse CFormTemplate não é usada por si só, é um template. Para criar uma nova forma, faça uma cópia da classe CFormTemplate com um nome único (apenas copie o código da classe CFormTemplate, renomeie e repita o mesmo para cada forma). A cópia de subclasses permite a separação de código relacionado a diferentes formas.

Os métodos virtuais são situados na seção protegida e, assim, serão inacessíveis para uma chamada que nem ao menos é necessária devido aos métodos já chamados da classe base sob diferentes eventos de forma - você deve apenas adicionar um código para operação dos controles correspondentes a estes eventos.

O gráfico abaixo mostra a interação entre as funções do Expert Advisor e os métodos de classe de controle padrão. (Fig. 4) e como uma comparação a interação com os métodos de classe de forma (Fig. 5).

Fig. 4. Interação entre as funções do Expert Advisor e os métodos de controle
Fig. 4. Interação entre as funções do Expert Advisor e os métodos de controle

Fig. 5. Interação entre as funções do Expert Advisor e os métodos de classe de forma
Fig. 5. Interação entre as funções do Expert Advisor e os métodos de classe de forma

Vamos rever o propósito de cada método detalhadamente:

  • Os métodos MainProperties() e OnInitEvent() são chamados do método Init() da forma. O método MainProperties() define a aparência da forma (desde tipo, presença de movimento, botões minimizar e fechar). O método OnInitEvent() chama os métodos Init() de todos os controles e define os valores padrão para os controles.

  • O método OnShowEvent() é chamado do método Show(); o código de chamada dos métodos Show() de todos os controles é adicionado a ele.

  • O método OnHideEvent() é chamado do método Hide(); o código de chamada dos métodos Hide() de todos os controles é adicionado a ele.

  • O método EventsHandler() é equivalente à função OnChartEvent() do Expert Advisor. O método Event() da forma é chamado da função OnChartEvent() do Expert Advisor e todos os eventos de gráfico são simplesmente redirecionados para este método. O código de chamada dos métodos Event() de todos os controles e a manipulação do evento correspondente são adicionados ao EventsHandler().

  • O método OnWindowChangeEvent() é chamado do método SetSubWindow() ao mudar uma subjanela. O código de chamada dos métodos SetSubWindow() de todos os controles é adicionado a ele. O método possui um parâmetro que transporta um novo número de subjanela a ser definido em todos os controles.

  • O método OnCancelEvent() é executado com o fechamento da forma (ao clicar no botão com uma imagem cruzada, botões "Cancelar" ou "Fechar"). Uma pessoa pode adicionar algum código de verificação no fechamento da forma, por exemplo, uma chamada de uma janela de confirmação para certificar que a forma realmente precise ser fechada. Se o método retornar verdadeiro, o código fechando a forma será concluído e a forma fechará; se o método retornar falso, a execução do código que fecha a forma será interrompida e permanecerá aberta.

  • O método OnApplyEvent() é executado ao clicar no botão "Aplicar" da forma. Aqui (como no método OnCancelEvent()) uma pessoa pode executar alguma verificação e ver se o método retorna verdadeiro ou falso, permitindo ou não o fechamento da forma. Este método pode também conter um código para salvar os valores de controle, de modo que que os valores de controle no próximo início do Expert Advisor sejam os mesmos que antes do programa ser fechado. E para este propósito, os valores salvos devem ser carregados no método OnInitEvent() e definido aos controles.

2. Um procedimento passo a passo para criação de uma nova forma

Um procedimento passo a passo para criação de uma nova forma é da seguinte forma:

1. Incluir um arquivo IncGUI_v3.mqh.

#include 
2. Copie o código da classe CFormTemplate do arquivo IncGUI_v3.mqh para um arquivo do seu Expert Advisor (a subclasse está no final do arquivo) e renomeie (no exemplo de uso da biblioteca, a subclasse se chama CForm).

class CFormTemplate: public CFormBase{
   public:
   protected: 
      void MainProperties()
     {
        m_Name        = "Form";       // Form name. Os nomes de todos os controles desta forma devem começar com isso.
        m_Width       = 200;           // Form width
        m_Height      = 150;           // Form height
        m_Type        = 1;             // Form type: 0 - without buttons, 1 - with "Apply" and "Cancel" buttons, 2 - with the "Close" button
        m_Caption     = "FormCaption"; // Form caption
        m_Movable     = true;          // Movable form (the button with a hand image is displayed in the top left corner)
        m_Resizable   =  true;         // Form minimizing/maximizing allowed (the button with a rectangle image 
                                           // is displayed in the top right corner)
        m_CloseButton = true;          // Form closing allowed (the button with a cross image is displayed in the top right corner)
     }
      void OnInitEvent() {} 
      void OnShowEvent(int aLeft, int aTop) {}
      void OnHideEvent() {}
      void OnWindowChangeEvent(int aSubWindow) {}
      void EventsHandler(const int id,const long& lparam,const double& dparam,const string& sparam){}
      bool OnApplyEvent()  {return(true);}
      bool OnCancelEvent() {return(true);}
};

É possível criar um arquivo incluso separado para o código de forma, incluir no Expert Advisor, copiar o código da subclasse CFormTemplate e fazer a codificação restante relacionada com a operação da forma neste arquivo.

Desenhar uma analogia com o uso de um editor visual, esta etapa seria bastante similar a clicar no botão para criar um novo segmento de forma, em que um novo arquivo de forma com funções de vários eventos podendo aparecer no projeto.

3. Definir as principais propriedades da forma. Esta etapa é executada no método MainProperties() atribuindo valores às variáveis declaradas na classe de base. Os comentários detalhados sobre os propósitos de todas as variáveis são fornecidos no template. Você pode notar a similaridade desta etapa com o uso da janela de propriedades da forma em um editor visual (por exemplo, VBA no Excel).

void MainProperties()
{
   m_Name         =  "Form";        // Form name. Os nomes de todos os controles desta forma devem começar com este.
   m_Width        =  200;           // Form width
   m_Height       =  150;           // Form height
   m_Type         =  1;             // Form type: 0 - without buttons, 1 - with "Apply" and "Cancel" buttons, 2 - with the "Close" button
   m_Caption      =  "FormCaption";   // Form caption
   m_Movable      =  true;          // Movable form (the button with a hand image is displayed in the top left corner)
   m_Resizable    =  true;          // Form minimizing/maximizing allowed (the button with a rectangle image 
                                    // is displayed in the top right corner)
   m_CloseButton = true;            // Form closing allowed (the button with a cross image is displayed in the top right corner)
}

Uma pessoa pode notar a diferença da forma a partir de outros controles - o nome da forma é definido no método MainProperties() em vez de ser transmitido como parâmetro do método Init().

O trabalho principal na forma é feito em uma subclasse; cada subclasse da forma é declarada apenas uma vez, então, não há a necessidade de especificar diferentes nomes ao chamar o método Init() de uma subclasse da forma.

O único parâmetro que pode ser transmitido ao método Init() é o parâmetro State, mas mesmo este não é obrigatório. O valor do parâmetro State determina o estado inicial da forma quando exibido: maximizado ou minimizado. Se o valor for 1, a forma é maximizada (Fig. 1, 2, 3), se o valor for 2, a forma é minimizada (Fig. 6).

Fig. 6. Forma minimizada

Fig. 6. Forma minimizada

4. Declarar uma classe.

CForm frm;

5. Faça etapas padrão para usar o controle: chame o método Init() da forma a partir da função OnInit() do Expert Advisor, chame o método Deinit() da função OnDeinit(), chame o método Event() da função OnChartEvent(), e, onde necessário, chame o método SetSubWindow() sob o evento CHARTEVENT_CHART_CHANGE.

A forma deve depois ser visível no gráfico e responder ao clique dos botões mover, minimizar, fechar, etc.

Depois continuamos adicionando os controles à forma.

3. Um procedimento passo a passo para adicionar controles à forma

  1. Declarando os controles. Se você precisar garantir acesso aos métodos de controles fora da forma, as classes de controle devem ser declaradas na seção pública se todo o trabalho puder ser feito dentro da classe de forma, as classes de controle devem ser declaradas na seção protegida.

  2. Declarando as variáveis para restaurar os valores de controle. Um passo opcional. Se você for utilizar a forma com os botões "Cancelar" e "Aplicar", uma variável deve ser adicionada a cada controle para armazenar o valor e o controle que tinha na abertura da forma a fim de restaurar o valor se um usuário mudar o valor de controle, mas pressionar o botão "Cancelar".

  3. Chamando os métodos Int() de todos os controles. É executado no método OnInitEvent().

  4. Carregando os dados salvos. Um passo opcional. É executado no método OnInitEvent(). A etapa é usada quando necessário para os controles reterem seus valores após reiniciar o Expert Advisor, terminal ou computador. O salvamento dos dados é realizado na etapa 12.

  5. Definindo os valores padrão. Nesta etapa, todos os controles são definidos em valores que serão vistos nos controles na primeira abertura da forma após iniciar o Expert Advisor (até seus valores serem mudados por um usuário). Se os dados forem carregados na etapa 4, os dados carregados são usados. Além disso, a preparação dos controles também é realizada nesta etapa, por exemplo, os itens de menu e lista são adicionados também aqui.

  6. O chamado dos métodos Show() (versão com parâmetros) para todos os controles é executado no método OnShowEvent(). O método OnShowEvent() também possui dois parâmetros (int aLeft, int aTOP) que leva as coordenadas do canto esquerdo superior do espaço de trabalho da forma. As posições de controle são definidas sujeitas aos valores destas variáveis (os valores devem ser adicionados).

  7. Uma chamada do método Hide() para todos os controles da forma é executada no método OnHideEvent().

  8. Chamando o método SetSubWindow(). Um passo opcional. Se for necessário exibir a forma em uma subjanela, o método SetSubWindow() é chamado para todos os controles. É executado no método OnWindowChange() que tem um parâmetro (int aSubWindow) que conduz um novo número de subjanela.

  9. Chamando os métodos Event() de todos os controles. É executado no método EventsHandler().

  10. Manipulando eventos de controle. Um passo opcional. Se for necessário garantir a interação entre os controles em seus eventos, é nesta etapa que os eventos são manipulados e as ações apropriadas são executadas. É executado no método EventsHandler().

  11. Aplicando novos valores. Uma etapa opcional que é executada utilizando o botão "Aplicar". É executada no método OnApplyEvent(). Nesta etapa, os valores de controle são verificados por precisão; se quaisquer valores incorretos forem encontrados, o usuário deve ser notificado, o método deve retornar falso e a forma permanecerá aberta. Se os valores estiverem corretos, deve ser retornado verdadeiro para a forma fechar.

  12. Salvamento de dados. Um passo opcional. É executado no método OnApllyEvent() utilizando o botão "Apply". Os dados podem ser salvos (dependendo dos objetivos e propósitos) em um arquivo, variáveis globais, objetos invisíveis no gráfico, etc.

  13. Verificar o método OnCancelEvent(). Um passo opcional. Se for retornado falso do método OnCancelEvent(), a forma permanecerá aberta, ou seja, o botão para fechar não responderá. Para que a forma feche, deve ser retornado verdadeiro do método.

Um exemplo de trabalho com formas pode ser encontrado no arquivo eIncGUI_v3_Test_Form.mq5 em anexo. A forma principal com diferentes controles abre na inicialização (Fig. 7).

Observe que a forma não tem um botão para fechar; esta é a forma principal e não deve ser feita para desaparecer do gráfico, caso contrário, o Expert Advisor terá que ser reiniciado para a forma reaparecer.

Fig. 7. Forma principal do exemplo eIncGUI_v3_Test_Form.mq5 com uma guia de menu principal para abrir

Fig. 7. Forma principal do exemplo eIncGUI_v3_Test_Form.mq5 com uma guia de menu principal para abrir

Usando os comando do menu principal, você pode abrir as outras duas variáveis da forma.

Menu principal - Tipos de forma - Tipo 1 abre uma forma de tipo 1 (com botões "Cancel" e "Apply").
Menu principal - Tipos de forma - Tipo 2 abre uma forma de tipo 2 (com o botão "Close").

Ambas formas requerem confirmação no fechamento (feito usando a função MessageBox()). A forma de tipo 1 possui uma caixa de entrada; um valor de entrada é verificado clicando no botão "Apply". A forma de tipo 2 possui um novo controle "Label" (classe CLabel) e alguns botões (classe CButton) para testá-la.

Agora, como mencionado na introdução, vamos rever as mudanças nas classes CHMenu e CVMenu como exemplificado criando o menu principal.

Primeiro de tudo, gostaria de dizer que apenas menus de dois níveis podem ser criados: uma barra horizontal (baseada na classe CHMenu) e guias (baseadas na classe CVMenu). A princípio, é possível criar ainda mais etiquetas, mas o processo será muito complicado e trabalhoso, e é por isso que não é recomendado e não será mais considerado.

Na realidade, a criação do menu principal para a forma não foi nem ao menos planejada, e as classes CHMenu e CVMenu foram destinadas a serem usadas individualmente e independente uma da outra para possibilitar/desativar várias funções e instrumentos. Porém, uma pequena melhoria das classes CHMenu e CVMenu tornou possível obter o menu principal, o que é apropriado o suficiente para a maioria dos casos.

Para criar um menu multinível e completo, uma abordagem um pouco diferente deve ser aplicada (criação de uma estrutura de dados em árvore), o que pode servir como assunto para um artigo completo separado, por isso vamos nos ater aos meios existentes.

Vamos rever primeiro todas as mudanças e correções aplicadas às classes CHMenu e CVMenu.

Na classe CVMenu, uma resposta ao clicar o objeto gráfico "Etiqueta" foi fixada e é usada para exibir o símbolo do tick. Consequentemente, houveram mudanças na operação dos métodos LastClickedX(), LastClickedY() e LastClickedQuarter(). Ao clicar nas etiquetas, os valores agora são retornados como no caso das caixas de texto. Correções similares foram aplicadas à classe CHMenu e métodos LastClickedX(), LastClickedY(), LastClickedQuarter() e LastClickedW().

Ambas as classes foram atualizadas adicionando os métodos SolvePosX() e SolvePosY(), que são planejados para calcular coordenadas de um objeto gráfico exibido no comando de menu. A largura do objeto exibido é transmitida para o método SolvePosX() e a altura do objeto exibido é transmitida para o método SolvePosY() que retorna a coordenada X- ou Y- do objeto exibido respectivamente. Agora não há mais a necessidade de usar os métodos LastClickedX(), LastClickedY(), LastClickedQuarter() e LastClickedW().

Ambas as classes foram atualizadas adicionando métodos LastClickedName1() e LastClickedName2(). Estes métodos retornam os nomes dos objetos gráficos formando o último item de menu clicado. LastClickedName1() retorna o nome da caixa de texto, LastClickedName2() retorna o nome da etiqueta para o símbolo do tick.

Os métodos ToggleNameAdd() e ToggleNamesClear() foram adicionados ao CVMenu. O método ToggleNameAdd() é usado para criar uma lista de nomes "toggle", o método NamesClear() é usado para limpar esta lista. Ao usar o método ToggleNameAdd() (adicionando um nome à lista), o menu fechará automaticamente com qualquer evento de gráfico, exceto para os eventos dos objetos gráficos formando o menu e os objetos gráficos em que os nomes foram adicionados pelo método ToggleNameAdd(). O menu retorna para o modo operacional normal quando a lista é limpa pelo método ToggleNameClear().

A operação do menu de dois níveis é da seguinte forma: quando o evento de clique no item do menu horizontal é definido pelos métodos LastClickedName1() e LastClickedName2(), obtemos os nomes dos objetos deste item, que são depois transmitidos para ToggleNameAdd() do menu vertical.

Após isso, o menu vertical será oculto com qualquer evento do gráfico, exceto para o menu vertical e um item horizontal (por meio do qual o menu vertical abriu).

Se um usuário selecionou o menu vertical, o menu vertical deve ser fechado e as ações correspondentes a este item devem ser executadas. Se um usuário clicou repetidamente no mesmo item de menu horizontal (por meio do qual o menu vertical abriu), o menu vertical deve ser oculto.

Vamos rever um exemplo da criação de menu.

O menu principal possui três itens, então, precisamos de três menus verticais. Declare uma classe do menu horizontal e três classes de menu vertical na seção pública da subclasse da forma (o exemplo abaixo mostra exatamente da mesma forma que no exemplo eIncGUI_v3_Test_Form.mq5):

class CForm: public CFormBase{
public:
   CHMenu m_hm;
   CVMenu m_vm1;
   CVMenu m_vm2;
   CVMenu m_vm3; 

Inicie as classes de menu no método OnInitEvent() e adicione itens de menu:

// Horizontal menu
m_hm.Init(m_Name+"_HM",m_Width,2);
// Adding horizontal menu items
m_hm.AddItem("Form types");
m_hm.AddItem("Item-2");
m_hm.AddItem("Item-3");
// Vertical menu 1
m_vm1.Init(m_Name+"_VM1",70,10); 
// Adding items to vertical menu 1
m_vm1.AddItem("Type-1");
m_vm1.AddItem("Type-2");
// Vertical menu 2
m_vm2.Init(m_Name+"_VM2",70,3);
// Adding items to vertical menu 2
m_vm2.AddItem("Item-2-1");
m_vm2.AddItem("Item-2-2");
m_vm2.AddItem("Item-2-3"); 
m_vm2.AddItem("Item-2-4");
m_vm2.AddItem("Item-2-5"); 
// Vertical menu 3
m_vm3.Init(m_Name+"_VM3",70,3);
// Adding items to vertical menu 3
m_vm3.AddItem("Item-3-1");
m_vm3.AddItem("Item-3-2");
m_vm3.AddItem("Item-3-3"); 
m_vm3.AddItem("Item-3-4");
m_vm3.AddItem("Item-3-5"); 

Oculte o menu no método OnHideEvent():

void OnHideEvent()
{
   m_hm.Hide(); 
   m_vm1.Hide(); 
   m_vm2.Hide(); 
   m_vm3.Hide(); 
}

Exiba o menu horizontal no método OnShowEvent():

void OnShowEvent(int aLeft,int aTop)
{
    m_hm.Show(aLeft,aTop); 
}

Finalmente, o trabalho principal no método EventsHandler().

Chame os métodos Event() de todos os menus.

void EventsHandler(const int id,const long& lparam,const double& dparam,const string& sparam)
{
    int m_event0=m_hm.Event(id,lparam,dparam,sparam);
    int m_event1=m_vm1.Event(id,lparam,dparam,sparam);
    int m_event2=m_vm2.Event(id,lparam,dparam,sparam);
    int m_event3=m_vm3.Event(id,lparam,dparam,sparam); 

Dependendo do valor m_event0 (índice de item de menu horizontal), trabalhe com o menu vertical adequado (por exemplo, com o item 0 e menu vertical 1):

if(m_event0==0)
{ // Clicking item 0
   if(m_vm1.Visible())
   { 
      m_vm1.Hide(); // If the vertical menu is open, close it
   }
   else{ // If the vertical menu is closed
      m_vm1.ToggleNamesClear(); // Clear the list of "toggle" names
      // Add to the list the names of the graphical objects forming the item 
      // of the horizontal menu which led to the last event 
      // of the horizontal menu 
      m_vm1.ToggleNameAdd(m_hm.LastClickedName1()); 
      m_vm1.ToggleNameAdd(m_hm.LastClickedName2());
      // Display the vertical menu
      m_vm1.Show(m_hm.SolvePosLeft(m_vm1.Width()),m_hm.SolvePosTop(m_vm1.Height())); 
   }
}

Tais ações devem ser realizadas para todos os itens de menu horizontal abrindo o menu vertical. Se o evento do menu vertical ocorrer, feche o menu.

Dependendo do índice do item clicado, faça o seguinte:

if(m_event1>=0)
{ // Vertical menu event 1
   m_vm1.Hide(); // Hide the menu
      if(m_event1==0)
      { // Clicking item 0
        //...
      }
      if(m_event1==1)
      { // Clicking item 1
        //...
      }
}

Tais ações devem ser realizadas para todos os menus verticais e todos seus itens.

A criação do menu principal agora está concluída.

Conclusão

Este artigo finaliza a série de artigos dedicados à criação de uma interface gráfica.

Como resultado de nosso trabalho, temos um arsenal bastante considerável de meios para uma rápida criação de uma interface gráfica fácil de usar e multifuncional. Isso é muito consistente com requerimentos existentes para uma interface gráfica:

  • Usando o framework na forma de título e uma forma que pode ser minimizada/maximizada, movida;
  • Áreas claramente designadas do código relacionando uma forma a outra;
  • Exibir/ocultar os controles é sincronizado com ocultar/exibir a forma.

Todos os controles e a forma têm um design em comum e esquema de cores que pode ser rapidamente alterado.

Vamos repetir o procedimento para criar uma forma.

Um breve procedimento para criação de uma forma:

  1. Incluir um arquivo IncGUI_v3.mqh.

  2. Fazer uma cópia da classe CFormTemplate com um nome único e declarar esta classe.

  3. Definir as propriedades da forma no método MainProperties() da cópia da subclasse.

  4. Chamar os métodos Init(), Hide() e Event() da forma das funções OnInit(), OnDeinit() e OnChartEvent() do Expert Advisor, respectivamente. Chamar o método Show() da função OnInit() do Expert Advisor ou conforme necessário.

  5. Trabalhar com controles. Declarar as classes de controle na cópia da subclasse. Chamar os métodos Init(), Show(), Hide() e Event() dos controles dos métodos OnInitEvent(), OnShowEvent(), OnHideEvent() e EventHandler() da cópia da subclasse.

Apêndice

Lista de arquivos anexados:

  • IncGUI_v3.mqh - um arquivo incluso contendo todas as classes para criar uma interface gráfica. O arquivo deve ser posicionado no diretório MQL5/Include do Diretório de dados do terminal.
  • eIncGUI_v3_Test_Form.mq5 - um exemplo de trabalho com formas. O arquivo deve ser posicionado no diretório MQL5/Experts do Diretório de dados do terminal.
  • IncGUIv3mqh.chm - documentação para o arquivo IncGUI_v3.mqh.

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

Arquivos anexados |
Crie seus próprios painéis gráficos no MQL5 Crie seus próprios painéis gráficos no MQL5
A usabilidade do programa MQL5 é determinada tanto por sua rica funcionalidade como pela interface de usuário gráfica elaborada. A percepção visual, algumas vezes, é mais importante do que uma operação rápida e estável. Aqui está um guia passo-a-passo para você mesmo criar painéis de exibição com base nas classes da Biblioteca padrão.
Controles gráficos personalizados. Parte 2. Biblioteca de controles Controles gráficos personalizados. Parte 2. Biblioteca de controles
O segundo artigo da série "Controles gráficos personalizados" apresenta uma biblioteca para manusear os principais problemas que surgem da interação entre um programa (Expert Advisor, script, indicador) e um usuário. A biblioteca contém um grande número de classes (CInputBox, CSpinInputBox, CCheckBox, CRadioGroup, CVSсrollBar, CHSсrollBar, CList, CListMS, CComBox, CHMenu, CVMenu, CHProgress, CDialer, CDialerInputBox, CTable) e exemplos de seu uso.
Os Fundamentos da programação orientada a objetos Os Fundamentos da programação orientada a objetos
Você não precisa saber o que são polimorfismo, encapsulação, etc. tudo sobre o uso da programação orientada a objeto (OOP)... você pode simplesmente usar estes recursos. Este artigo cobre o básico de OOP com exemplos práticos.
Controles gráficos personalizados. Parte 1: criando um controle simples Controles gráficos personalizados. Parte 1: criando um controle simples
Este artigo cobre os princípios gerais para desenvolvimento de controles gráficos. Vamos preparar ferramentas para um trabalho rápido e conveniente com objetos gráficos, analisar um exemplo de criação de um simples controle para inserir texto ou dados numéricos, bem como os meios para usá-los.