English Русский 中文 Español Deutsch 日本語
preview
Como criar um painel gráfico de qualquer nível de complexidade

Como criar um painel gráfico de qualquer nível de complexidade

MetaTrader 5Exemplos | 17 maio 2018, 08:56
12 127 2
Vladimir Karputov
Vladimir Karputov

Índice

Introdução

Mesmo agora, a maioria dos programadores, que desenvolvem indicadores e Expert Advisors para a plataforma MetaTrader 5, não utilizam dos recursos de criação de interface gráfica disponíveis em seus aplicativos. Eu acredito que isso é porque as classes dos Painéis e Diálogos da Biblioteca Padrão fornecem apenas uma breve descrição técnica dos métodos. A referência da linguagem fornece exemplos de código com comentários para muitos controles gráficos. Mas você não pode começar a criar seus próprios painéis sem uma compreensão completa de sua estrutura e ideia.

Eu tentei entender como os painéis estão dispostos. Agora, eu quero compartilhar o conhecimento obtido com os outros desenvolvedores. Eu comecei com um aplicativo simples, que cria um painel gráfico baseado na classe CAppDialog. Então, eu modifiquei em etapas e analisei os resultados obtidos.

O artigo fornece todos os detalhes necessários para a operação da classe CAppDialog: como criar um painel, qual conjunto mínimo necessário de funções é necessário e como adicionar elementos adicionais (como botões). Nós vamos analisar os objetos que consistem o painel e a ordem em que eles devem ser criados. Também mostrarei quais constantes são usadas na criação de um painel e como alterá-las.


Criando um painel baseado na CAppDialog

Nós vamos começar com algumas informações básicas. 

CAppDialog é uma classe do controle combinado " Caixa de Diálogo do Aplicativo" (Application Dialog). A classe CAppDialog une visualmente os grupos de elementos dissimilares funcionalmente conectados em um aplicativo MQL5.

O código mínimo, que cria um painel, é exibido abaixo:

//+------------------------------------------------------------------+
//|                                              LearnCAppDialog.mq5 |
//|                        Copyright 2018, MetaQuotes Software Corp. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2018, MetaQuotes Software Corp."
#property link      "https://www.mql5.com"
#property version   "1.00"
#include <Controls\Dialog.mqh>

CAppDialog AppWindow;
//+------------------------------------------------------------------+
//| Função de inicialização do Expert                                |
//+------------------------------------------------------------------+
int OnInit()
  {
//---Cria o diálogo do aplicativo
   if(!AppWindow.Create(0,"AppWindow",0,20,20,360,324))
      return(INIT_FAILED);
//---Execução da aplicação
   AppWindow.Run();
//--- sucesso
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Função de desinicialização do Expert                             |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//--- destrói o diálogo
   AppWindow.Destroy(reason);
  }
//+------------------------------------------------------------------+
//| Função de evento do gráfico do Expert                            |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,         // ID do evento  
                  const long& lparam,   // parâmetro do evento do tipo long
                  const double& dparam, // parâmetro do evento do tipo double
                  const string& sparam) // parâmetro do evento do tipo string
  {
   AppWindow.ChartEvent(id,lparam,dparam,sparam);
  }
//+------------------------------------------------------------------+


O resultado da execução do Expert Advisor LearnCAppDialog.mq5 é o painel de controle criado:

Painel CAppDialog

O Expert Advisor LearnCAppDialog.mq5 contém um conjunto mínimo de comandos necessários para criar um painel e para sua operação. Siga os seguintes passos:

  • Declare uma instância da classe CAppDialog em nível global do programa:
#include <Controls\Dialog.mqh>

CAppDialog AppWindow;
//+------------------------------------------------------------------+
//| Função de inicialização do Expert                                |
//+------------------------------------------------------------------+

  • Criação do painel AppWindow e lançamento do painel:
int OnInit()
  {
//---Cria o diálogo do aplicativo
   if(!AppWindow.Create(0,"AppWindow",0,20,20,360,324))
      return(INIT_FAILED);
//---Execução da aplicação
   AppWindow.Run();
//--- sucesso
   return(INIT_SUCCEEDED);
  }

  • Passando os eventos ChartEvent para o Painel AppWindow :
//+------------------------------------------------------------------+
//| Função de evento do gráfico do Expert                            |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,         // ID do evento  
                  const long& lparam,   // parâmetro do evento do tipo long
                  const double& dparam, // parâmetro do evento do tipo double
                  const string& sparam) // parâmetro do evento do tipo string
  {
   AppWindow.ChartEvent(id,lparam,dparam,sparam);
  }

E o último passo extremamente importante:

  • Destruição do controle chamando o método Destroy
void OnDeinit(const int reason)
  {
//--- destrói o diálogo
   AppWindow.Destroy(reason);
  }

Se não fornecermos a destruição do painel, cada alteração do período gráfico ou símbolo levará à adição de novos elementos em cima dos existentes.


O que a AppWindow pode fazer

Um painel baseado em CAppDialog pode, teoricamente, processar os seguintes eventos: 

//+------------------------------------------------------------------+
//| Eventos                                                          |
//+------------------------------------------------------------------+
#define ON_CLICK                (0)   // evento de clique no controle
#define ON_DBL_CLICK            (1)   // evento de clique duplo no controle
#define ON_SHOW                 (2)   // evento de exibir o controle
#define ON_HIDE                 (3)   // evento de ocultar o controle
#define ON_CHANGE               (4)   // evento de alteração do controle
#define ON_START_EDIT           (5)   // evento de início da edição
#define ON_END_EDIT             (6)   // evento do fim da edição
#define ON_SCROLL_INC           (7)   // evento de incremento da barra de rolagem
#define ON_SCROLL_DEC           (8)   // evento de decremento da barra de rolagem
#define ON_MOUSE_FOCUS_SET      (9)   // evento de "cursor do mouse entrou no controle"
#define ON_MOUSE_FOCUS_KILL     (10)  // evento de "cursor do mouse saiu do controle"
#define ON_DRAG_START           (11)  // evento de "início do arraste do controle"
#define ON_DRAG_PROCESS         (12)  // evento de "o controle está sendo arrastado"
#define ON_DRAG_END             (13)  // evento de "fim do arraste do controle"
#define ON_BRING_TO_TOP         (14)  // evento "aumento da prioridade dos eventos do mouse"
#define ON_APP_CLOSE            (100) // evento "encerramento da aplicação"

Esses eventos estão incluídos no bloco do arquivo Events [pasta de dados]\MQL5\Include\Controls\Defines.mqh. Assim, os eventos incluem um clique, um clique duplo, início e término da edição, foco, arraste (início, processo e término), exibição e ocultação do painel. Os exemplos de trabalho com esses eventos são fornecidos nos exemplos da seção Painéis e Caixas de Diálogos. O evento ON_CHANGE é tratado no exemplo CRadioGroup, o ON_SCROLL_INC e ON_SCROLL_DEC são tratados em CScrollV.


A estrutura do objeto CAppDialog

Inicie o Expert Advisor LearnCAppDialog.mq5 em um gráfico vazio, pressione Ctrl+B e clique em "Lista tudo" para ver todos os objetos que consiste o painel:


Os objetos da seção Painéis e Caixa de Diálogos da Biblioteca Padrão são criadas e aplicadas na seguinte ordem. Um objeto "Border" é criado primeiro, dentro dele é adicionado o plano de fundo do painel como um objeto "Back". Em seguida, a área do cliente "ClientBack" é aplicada sobre o plano de fundo. Os Controles filhos podem ser adicionados dentro da área do cliente. O objeto Caption, que contém o nome do painel e dois botões de controle são adicionados à parte superior do painel.

Controles gráficos no painel AppWindow

O processo pode ser representado esquematicamente para ver a ordem de criação desses objetos:

O objeto Border é um OBJ_RECTANGLE_LABEL com uma borda de cor branca (padrão para todos os painéis). Então o objeto Border é usado para fins puramente estéticos: ele exibe uma borda branca, enquanto o corpo do objeto Border será oculto atrás do objeto Back.



O esquema da herança de objetos

Pode parecer que a seção Painéis e Caixa de Diálogos tem muitas classes com estrutura de herança e relações extensas. Mas a hierarquia é muito simples. Então, se você entender o que a CAppDialog consiste e como ela é criada, entender outras classes também será fácil. Aqui está o esquema de herança de todas as classes da Biblioteca Padrão:


O painel AppWindow no Expert Advisor LearnCAppDialog.mq5 consiste em seis objetos, na qual cada um executa sua tarefa específica.


Um painel baseado na CAppDialog pode ser criado a partir de um Expert Advisor ou de um indicador. No entanto, a criação do painel pode variar dependendo do tipo de programa (Expert Advisor ou indicador) que cria o painel e a subjanela na qual o programa está sendo executado:

  • Se o programa for um Expert Advisor (o tipo do programa em execução é PROGRAM_EXPERT), então o painel é criado SOMENTE na janela principal (o índice da janela é "0"), somente usando o método CAppDialog::CreateExpert.
  • Se o programa é um indicador (o tipo do programa em execução é PROGRAM_INDICATOR), então, é verificado o número da janela, na qual o programa está sendo executado:
    • se for a janela principal (o número da janela é 0), o painel é criado usando o método CAppDialog::CreateIndicator
    • se for uma subjanela, o painel é criado usando o método CAppDialog::CreateExpert

A característica específica do método CAppDialog::CreateIndicator é que o painel realiza de forma automática durante a criação o seguinte:

  • é ajustado para a largura da janela
  • ajusta a altura da janela para caber no painel

Um exemplo do painel de indicadores [pasta de dados]\MQL5\Indicators\Examples\Panels\SimplePanel\SimplePanel.mq5 após a criação e minimização:


O CreateExpert cria um painel na janela principal (o número da janela é 0) e implica que o programa que cria o painel é um Expert Advisor.

Há uma exceção a essas regras: o painel pode ser criado na janela principal a partir de um indicador. Neste caso, o método CreateIndicator para a criação de painéis será aplicado.


Onde encontrar as principais constantes para a criação dos objetos e como redefini-los usando a diretiva #undef

O código será implementado no Expert Advisor AppWindowEditDefine.mq5.

As constantes básicas do painel e seus controles estão localizadas no arquivo [pasta de dados]\MQL5\Include\Controls\Defines.mqh, que está conectado na classe CWnd:

//+------------------------------------------------------------------+
//|                                                          Wnd.mqh |
//|                   Copyright 2009-2017, MetaQuotes Software Corp. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
#include "Rect.mqh"
#include "Defines.mqh"
#include <Object.mqh>
class CDragWnd;

A hierarquia de herança é a seguinte:

  • CWnd
    • CWndContainer
      • CDialog
        • CAppDialog

Estamos especialmente interessados ​​no seguinte grupo de constantes:

//+------------------------------------------------------------------+
//| Estilos de desenho e cores                                       |
//+------------------------------------------------------------------+
//--- comum
#define CONTROLS_FONT_NAME                  "Trebuchet MS"
#define CONTROLS_FONT_SIZE                  (10)
//--- Texto
#define CONTROLS_COLOR_TEXT                 C'0x3B,0x29,0x28'
#define CONTROLS_COLOR_TEXT_SEL             Branco
#define CONTROLS_COLOR_BG                   Branco
#define CONTROLS_COLOR_BG_SEL               C'0x33,0x99,0xFF'
//--- Botão
#define CONTROLS_BUTTON_COLOR               C'0x3B,0x29,0x28'
#define CONTROLS_BUTTON_COLOR_BG            C'0xDD,0xE2,0xEB'
#define CONTROLS_BUTTON_COLOR_BORDER        C'0xB2,0xC3,0xCF'
//--- Rótulo
#define CONTROLS_LABEL_COLOR                C'0x3B,0x29,0x28'
//--- Edit
#define CONTROLS_EDIT_COLOR                 C'0x3B,0x29,0x28'
#define CONTROLS_EDIT_COLOR_BG              White
#define CONTROLS_EDIT_COLOR_BORDER          C'0xB2,0xC3,0xCF'
//--- Rolamento
#define CONTROLS_SCROLL_COLOR_BG            C'0xEC,0xEC,0xEC'
#define CONTROLS_SCROLL_COLOR_BORDER        C'0xD3,0xD3,0xD3'
//--- Cliente
#define CONTROLS_CLIENT_COLOR_BG            C'0xDE,0xDE,0xDE'
#define CONTROLS_CLIENT_COLOR_BORDER        C'0x2C,0x2C,0x2C'
//--- ListView
#define CONTROLS_LISTITEM_COLOR_TEXT        C'0x3B,0x29,0x28'
#define CONTROLS_LISTITEM_COLOR_TEXT_SEL    White
#define CONTROLS_LISTITEM_COLOR_BG          White
#define CONTROLS_LISTITEM_COLOR_BG_SEL      C'0x33,0x99,0xFF'
#define CONTROLS_LIST_COLOR_BG              White
#define CONTROLS_LIST_COLOR_BORDER          C'0xB2,0xC3,0xCF'
//--- CheckGroup
#define CONTROLS_CHECKGROUP_COLOR_BG        C'0xF7,0xF7,0xF7'
#define CONTROLS_CHECKGROUP_COLOR_BORDER    C'0xB2,0xC3,0xCF'
//--- RadioGroup
#define CONTROLS_RADIOGROUP_COLOR_BG        C'0xF7,0xF7,0xF7'
#define CONTROLS_RADIOGROUP_COLOR_BORDER    C'0xB2,0xC3,0xCF'
//--- Caixa de Diálogo
#define CONTROLS_DIALOG_COLOR_BORDER_LIGHT  White
#define CONTROLS_DIALOG_COLOR_BORDER_DARK   C'0xB6,0xB6,0xB6'
#define CONTROLS_DIALOG_COLOR_BG            C'0xF0,0xF0,0xF0'
#define CONTROLS_DIALOG_COLOR_CAPTION_TEXT  C'0x28,0x29,0x3B'
#define CONTROLS_DIALOG_COLOR_CLIENT_BG     C'0xF7,0xF7,0xF7'
#define CONTROLS_DIALOG_COLOR_CLIENT_BORDER C'0xC8,0xC8,0xC8'

Para alterar essas substituições de macro, use a directiva #undef:

A diretiva #undef é usada para cancelar uma macro previamente declarada.

Então, nós temos o seguinte algoritmo: cancelar a macro previamente declarada; em seguida, declarar novamente a macro com um parâmetro alterado. Nós devemos fazer o seguinte truque para isso: conectar o arquivo Define.mqh ANTES da Dialog.mqh:

//+------------------------------------------------------------------+
//|                                          AppWindowEditDefine.mq5 |
//|                        Copyright 2018, MetaQuotes Software Corp. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2018, MetaQuotes Software Corp."
#property link      "http://www.mql5.com"
#property version   "1.001"
#property description "Painéis de Controle e Caixas de Diálogos. Demonstração da classe CBmpButton"
#include <Controls\Defines.mqh>


cancela macros após a conexão "Defines.mqh":

#undef CONTROLS_FONT_NAME 
#undef CONTROLS_FONT_SIZE 

#undef CONTROLS_BUTTON_COLOR
#undef CONTROLS_BUTTON_COLOR_BG
#undef CONTROLS_BUTTON_COLOR_BORDER

#undef CONTROLS_DIALOG_COLOR_BORDER_LIGHT
#undef CONTROLS_DIALOG_COLOR_BORDER_DARK
#undef CONTROLS_DIALOG_COLOR_BG
#undef CONTROLS_DIALOG_COLOR_CAPTION_TEXT
#undef CONTROLS_DIALOG_COLOR_CLIENT_BG
#undef CONTROLS_DIALOG_COLOR_CLIENT_BORDER 

Escreve parâmetros de entrada:

input string   font_name                  = "Trebuchet MS";
input int      font_size                  = 10;

input color    button_color               = C'0x3B,0x29,0x28';
input color    button_color_bg            = C'0xDD,0xE2,0xEB';
input color    button_color_border        = C'0xB2,0xC3,0xCF';

input color    dialog_color_border_light  = White;
input color    dialog_color_border_dark   = C'0xB6,0xB6,0xB6';
input color    dialog_color_bg            = C'0xF0,0xF0,0xF0';
input color    dialog_color_caption_text  = C'0x28,0x29,0x3B';
input color    dialog_color_client_bg     = C'0xF7,0xF7,0xF7';
input color    dialog_color_client_border = C'0xC8,0xC8,0xC8';

A parte mais interessante: novamente, nós declaramos as macros e, desta vez, usamos os parâmetros de entrada para os seus valores:

#define CONTROLS_FONT_NAME                font_name
#define CONTROLS_FONT_SIZE                font_size

#define CONTROLS_BUTTON_COLOR             button_color
#define CONTROLS_BUTTON_COLOR_BG          button_color_bg
#define CONTROLS_BUTTON_COLOR_BORDER      button_color_border

#define CONTROLS_DIALOG_COLOR_BORDER_LIGHT dialog_color_border_light
#define CONTROLS_DIALOG_COLOR_BORDER_DARK dialog_color_border_dark
#define CONTROLS_DIALOG_COLOR_BG          dialog_color_bg
#define CONTROLS_DIALOG_COLOR_CAPTION_TEXT dialog_color_caption_text
#define CONTROLS_DIALOG_COLOR_CLIENT_BG   dialog_color_client_bg
#define CONTROLS_DIALOG_COLOR_CLIENT_BORDER dialog_color_client_border

#include <Controls\Dialog.mqh>
#include <Controls\BmpButton.mqh>

Exemplo:



Resumo da CAppDialog

Nosso painel é o objeto da classe CAppDialog. Ele herdou o método ControlsTotal (o número de controles no container) da classe CWndContainer. Portanto, nós podemos passar por todos os controles do painel e aplicar algumas ações a eles. Esses elementos são declarados na área private da classe pai CDialog:

//+------------------------------------------------------------------+
//| Classe CDialog                                                   |
//| Uso: classe base para a criação das caixas de diálogo            |
//|             e paineis indicadores                                |
//+------------------------------------------------------------------+
class CDialog : public CWndContainer
  {
private:
   //--- controles dependentes
   CPanel            m_white_border;        // o objeto "borda branca"
   CPanel            m_background;          // o objeto plano de fundo
   CEdit             m_caption;             // o objeto título da janela
   CBmpButton        m_button_close;        // o objeto botão "Close"
   CWndClient        m_client_area;         // o objeto área do cliente

protected:

O depurador permite ver como esses objetos são criados:

//+------------------------------------------------------------------+
//| Criação de um controle                                           |
//+------------------------------------------------------------------+
bool CDialog::Create(const long chart,const string name,const int subwin,const int x1,const int y1,const int x2,const int y2)
  {
//--- método de chamada da classe pai
   if(!CWndContainer::Create(chart,name,subwin,x1,y1,x2,y2))
      return(false);
//--- cria os controles dependentes
   if(!m_panel_flag && !CreateWhiteBorder())
      return(false);
   if(!CreateBackground())
      return(false);
   if(!CreateCaption())
      return(false);
   if(!CreateButtonClose())
      return(false);
   if(!CreateClientArea())
      return(false);

e também como os nomes são atribuídos a eles: m_white_border -> "29437Border", m_background -> "29437Back", m_caption -> "29437Caption", m_button_close -> "29437Close", m_client_area -> "29437Client". Nestes nomes, o número de "29437" é o identificador do painel para a sua vida útil.

Assim, nós podemos alterar algumas propriedades dos elementos do painel. Por exemplo, nós podemos alterar a cor dos objetos m_client_area e m_background:

//+------------------------------------------------------------------+
//|                                            LearnCAppDialog_1.mq5 |
//|                        Copyright 2018, MetaQuotes Software Corp. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2018, MetaQuotes Software Corp."
#property link      "https://www.mql5.com"
#property version   "1.00"
#include <Controls\Dialog.mqh>

CAppDialog AppWindow;
//+------------------------------------------------------------------+
//| Função de inicialização do Expert                                |
//+------------------------------------------------------------------+
int OnInit()
  {
//---Cria o diálogo do aplicativo
   if(!AppWindow.Create(0,"AppWindow",0,20,20,360,324))
      return(INIT_FAILED);
//---
   int total=AppWindow.ControlsTotal();
   CWndClient*myclient;
   for(int i=0;i<total;i++)
     {
      CWnd*obj=AppWindow.Control(i);
      string name=obj.Name();
      PrintFormat("%d is %s",i,name);
      //--- cor 
      if(StringFind(name,"Client")>0)
        {
         CWndClient *client=(CWndClient*)obj;
         client.ColorBackground(clrRed);
         myclient=client;
         Print("client.ColorBackground(clrRed);");
         ChartRedraw();
        }
      //---
      if(StringFind(name,"Back")>0)
        {
         CPanel *panel=(CPanel*) obj;
         panel.ColorBackground(clrGreen);
         Print("panel.ColorBackground(clrGreen);");
         ChartRedraw();
        }
     }
   AppWindow.Delete(myclient);
//---Execução da aplicação
   AppWindow.Run();
//--- sucesso
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Função de desinicialização do Expert                             |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//--- destrói o diálogo
   AppWindow.Destroy(reason);
  }
//+------------------------------------------------------------------+
//| Função de evento do gráfico do Expert                            |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,         // ID do evento  
                  const long& lparam,   // parâmetro do evento do tipo long
                  const double& dparam, // parâmetro do evento do tipo double
                  const string& sparam) // parâmetro do evento do tipo string
  {
   AppWindow.ChartEvent(id,lparam,dparam,sparam);
  }
//+------------------------------------------------------------------+


Preste atenção para a linha: ela contém a chamada do método CWndContainer::Delete, que exclui um elemento do grupo (container). Depois do elemento m_client_area ser excluído do grupo, um comando apropriado não será passado para o objeto m_client_area no caso de você tentar mover o painel. A área do cliente permanecerá em sua posição:


No entanto, quando você fecha o painel, o elemento m_client_area será excluído do gráfico junto com os outros elementos.

No exemplo a seguir, em vez de método CWndContainer::Delete nós usamos o CWndContainer::Destroy, que destrói o objeto m_client_area:

//+------------------------------------------------------------------+
//|                                            LearnCAppDialog_2.mq5 |
//|                        Copyright 2018, MetaQuotes Software Corp. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2018, MetaQuotes Software Corp."
#property link      "https://www.mql5.com"
#property version   "1.00"
#include <Controls\Dialog.mqh>

CAppDialog AppWindow;
//+------------------------------------------------------------------+
//| Função de inicialização do Expert                                |
//+------------------------------------------------------------------+
int OnInit()
  {
//---Cria o diálogo do aplicativo
   if(!AppWindow.Create(0,"AppWindow",0,20,20,360,324))
      return(INIT_FAILED);
//---
   int total=AppWindow.ControlsTotal();
   CWndClient*myclient;
   for(int i=0;i<total;i++)
     {
      CWnd*obj=AppWindow.Control(i);
      string name=obj.Name();
      PrintFormat("%d is %s",i,name);
      //--- cor 
      if(StringFind(name,"Client")>0)
        {
         CWndClient *client=(CWndClient*)obj;
         client.ColorBackground(clrRed);
         myclient=client;
         Print("client.ColorBackground(clrRed);");
         ChartRedraw();
        }
      //---
      if(StringFind(name,"Back")>0)
        {
         CPanel *panel=(CPanel*) obj;
         panel.ColorBackground(clrGreen);
         Print("panel.ColorBackground(clrGreen);");
         ChartRedraw();
        }
     }
   Sleep(5000);
   myclient.Destroy();
//---Execução da aplicação
   AppWindow.Run();
//--- sucesso
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Função de desinicialização do Expert                             |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//--- destrói o diálogo
   AppWindow.Destroy(reason);
  }
//+------------------------------------------------------------------+
//| Função de evento do gráfico do Expert                            |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,         // ID do evento  
                  const long& lparam,   // parâmetro do evento do tipo long
                  const double& dparam, // parâmetro do evento do tipo double
                  const string& sparam) // parâmetro do evento do tipo string
  {
   AppWindow.ChartEvent(id,lparam,dparam,sparam);
  }
//+------------------------------------------------------------------+


Veja como ele funciona: após uma pausa de 5 segundos após a criação do painel, a área do cliente é destruída:



Como adicionar novos controles: dois botões

Vamos modificar o EA da seção "Criando um painel baseado na CAppDialog", adicionando ao painel dois botões baseados na classe CButton e salvá-lo como AppWindowTwoButtons.mq5. Antes de adicionar os botões (semelhante ao desenho de qualquer painel), você deve primeiro pensar em seu tamanho e localização. Suponha que a figura abaixo mostra o painel com botões que nós queremos criar: 


Onde:

  • TOP é a distância da borda superior da área do cliente (definida pela constante INDENT_TOP)
  • LEFT é a distância da borda esquerda da área do cliente (definida pela constante INDENT_LEFT)
  • HEIGHT é a altura do botão (definida pela constante BUTTON_HEIGHT)
  • WIDTH é a largura do botão (definida pela constante BUTTON_WIDTH)

Outra constante que nós precisamos é o recuo horizontal mínimo entre os controles. Vamos chamá-lo de "CONTROLS_GAP_X". 

Para usar a classe CButton, nós precisamos conectá-la primeiro:

//+------------------------------------------------------------------+
//|                                          AppWindowTwoButtons.mq5 |
//|                        Copyright 2018, MetaQuotes Software Corp. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2018, MetaQuotes Software Corp."
#property link      "http://www.mql5.com"
#property version   "1.001"
#property description "Painéis de Controle e Caixas de Diálogos. Classe de demonstração da CButton"
#include <Controls\Dialog.mqh>
#include <Controls\Button.mqh>

Em seguida, nós adicionamos as constantes do tamanho e localização dos botões:

//+------------------------------------------------------------------+
//| defines                                                          |
//+------------------------------------------------------------------+
//--- recuos e gaps
#define INDENT_LEFT                         (11)      // recuo da esquerda (com permissão para a largura da borda)
#define INDENT_TOP                          (11)      // recuo da parte superior (com permissão para a largura da borda)
#define CONTROLS_GAP_X                      (5)       // gap pela coordenada X
//--- para os botões
#define BUTTON_WIDTH                        (100)     // tamanho pela coordenada X
#define BUTTON_HEIGHT                       (20)      // tamanho pela coordenada Y
//---

Declarando duas instâncias da classe CButton no nível global do programa:

#define BUTTON_HEIGHT                       (20)      // tamanho pela coordenada Y
//---
CAppDialog           AppWindow;
CButton              m_button1;                       // o objeto botão
CButton              m_button2;                       // o objeto botão
//+------------------------------------------------------------------+
//| Função de inicialização do Expert                                |
//+------------------------------------------------------------------+
int OnInit()

A declaração de botões em um nível global é um estilo ruim, porque essas instâncias (e, portanto, seus métodos) serão vistos de qualquer lugar no Expert Advisor. No entanto, eu fiz isso aqui deliberadamente para reduzir o tamanho do código.

A OnInit() irá se alterar um pouco: nós adicionamos as chamadas e verificação de resultados da criação do botão:

//+------------------------------------------------------------------+
//| Função de inicialização do Expert                                |
//+------------------------------------------------------------------+
int OnInit()
  {
//---Cria o diálogo do aplicativo
   if(!AppWindow.Create(0,"AppWindow with Two Buttons",0,40,40,380,344))
      return(INIT_FAILED);
//--- cria os controles dependentes
   if(!CreateButton1())
      return(false);
   if(!CreateButton2())
      return(false);
//---Execução da aplicação
   AppWindow.Run();
//--- sucesso
   return(INIT_SUCCEEDED);
  }


Vamos analisar a CreateButton1() para ver em detalhes o processo de criação de botões e vinculação a um painel.

Nós usaremos os seguintes métodos da classe CButton: Create para a criação do botão:


 e Text para adicionar um texto ao botão (o método Text é herdado da classe CWndObj):


O botão é criado neste estágio, mas existe separadamente do painel. Para ligá-los, precisamos executar o método CDialog::Add, que adiciona o botão à área do cliente do painel:

   if(!AppWindow.Add(m_button1))
      return(false);
//--- sucesso
   return(true);
  }

Aqui está o código completo de criação do botão:

//+------------------------------------------------------------------+
//| Cria o botão "Button1"                                           |
//+------------------------------------------------------------------+
bool CreateButton1(void)
  {
//--- coordenadas
   int x1=INDENT_LEFT;        // x1            = 11  pixels
   int y1=INDENT_TOP;         // y1            = 11  pixels
   int x2=x1+BUTTON_WIDTH;    // x2 = 11 + 100 = 111 pixels
   int y2=y1+BUTTON_HEIGHT;   // y2 = 11 + 20  = 32  pixels
//--- create
   if(!m_button1.Create(0,"Button1",0,x1,y1,x2,y2))
      return(false);
   if(!m_button1.Text("Button1"))
      return(false);
   if(!AppWindow.Add(m_button1))
      return(false);
//--- sucesso
   return(true);
  }

Não se esqueça que nós precisamos destruir o painel em OnDeinit() e passar todos os eventos para um formulário em OnChartEvent():

//+------------------------------------------------------------------+
//| Função de desinicialização do Expert                             |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//---
   Comment("");
//--- destrói o diálogo
   AppWindow.Destroy(reason);
  }
//+------------------------------------------------------------------+
//| Função de evento do gráfico do Expert                            |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,         // ID do evento  
                  const long& lparam,   // parâmetro do evento do tipo long
                  const double& dparam, // parâmetro do evento do tipo double
                  const string& sparam) // parâmetro do evento do tipo string
  {
   AppWindow.ChartEvent(id,lparam,dparam,sparam);
  }
//+------------------------------------------------------------------+
//| Cria o botão "Button1"                                           |
//+------------------------------------------------------------------+


Como os controles aninhados são movidos e desenhados

Lembre-se, o painel AppWindow é o objeto da classe CAppDialog, que é o filho da CDialog. A CDialog em si é derivada da CWndContainer:

A CWndContainer é uma classe base para um grupo de controles da Biblioteca Padrão.

Assim, a classe pai CWndContainer controla o movimento de todo o grupo de controles, que estão incluídos no painel.

O movimento de todos os controles do painel é realizado em um loop no CWndContainer::Shift.

//+------------------------------------------------------------------+
//| Movimento relativo do grupo de controle                          |
//+------------------------------------------------------------------+
bool CWndContainer::Shift(const int dx,const int dy)
  {
//--- chamada do método da classe pai
   if(!CWnd::Shift(dx,dy))
      return(false);
//--- loop por elementos do grupo
   int total=m_controls.Total();
   for(int i=0;i<total;i++)
     {
      CWnd *control=Control(i);
      //--- checagem do ponteiro
      if(control==NULL)
         continue;
      //--- move o item do grupo
      control.Shift(dx,dy);
     }
//--- sucesso
   return(true);
  }

Nós usamos um exemplo da referência - CBmpButton (localizado em \MQL5\Experts\MyExp\Help\With the Panel. EN\ControlsBmpButton.mq5).

Acessando o método CWndContainer::Shift:



Adicionando a CAppDialog ao grupo de controles via CDialog

Acima está um exemplo de um painel com dois botões. Lembre-se, eu mencionei que declarar botões em nível global não é um bom exemplo? Aqui está um exemplo mais correto: o código inteiro para criar o painel e os botões é colocado na classe derivada da CAppDialog. Um exemplo de criação de painéis é mostrado no AppWindowTwoButtonsClass.mq5.

CAppWindowTwoButtons é filho da CAppDialog e contém os seguintes métodos:

 Creation
 Create  Criação do controle principal: o painel
 CreateButton1  Criação do controle dependente: botão #1
 CreateButton2  Criação do controle dependente: botão #2

Código do AppWindowTwoButtonsClass.mq5: o código, que agora está na classe, está destacado com cor:

//+------------------------------------------------------------------+
//|                                     AppWindowTwoButtonsClass.mq5 |
//|                        Copyright 2018, MetaQuotes Software Corp. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2018, MetaQuotes Software Corp."
#property link      "http://www.mql5.com"
#property version   "1.000"
#property description "Painéis de Controle e Caixas de Diálogos. Classe de demonstração da CButton"
#include <Controls\Dialog.mqh>
#include <Controls\Button.mqh>
//+------------------------------------------------------------------+
//| defines                                                          |
//+------------------------------------------------------------------+
//--- recuos e gaps
#define INDENT_LEFT                         (11)      // recuo da esquerda (com permissão para a largura da borda)
#define INDENT_TOP                          (11)      // recuo da parte superior (com permissão para a largura da borda)
#define CONTROLS_GAP_X                      (5)       // gap pela coordenada X
//--- para os botões
#define BUTTON_WIDTH                        (100)     // tamanho pela coordenada X
#define BUTTON_HEIGHT                       (20)      // tamanho pela coordenada Y
//---
//+------------------------------------------------------------------+
//| Classe CAppWindowTwoButtons                                      |
//| Uso: caixa de diálogo principal do aplicativo Controls           |
//+------------------------------------------------------------------+
class CAppWindowTwoButtons : public CAppDialog
  {
private:
   CButton           m_button1;                       // o objeto button
   CButton           m_button2;                       // o objeto button

public:
                     CAppWindowTwoButtons(void);
                    ~CAppWindowTwoButtons(void);
   //--- create
   virtual bool      Create(const long chart,const string name,const int subwin,const int x1,const int y1,const int x2,const int y2);

protected:
   //--- cria os controles dependentes
   bool              CreateButton1(void);
   bool              CreateButton2(void);

  };
//+------------------------------------------------------------------+
//| Construtor                                                       |
//+------------------------------------------------------------------+
CAppWindowTwoButtons::CAppWindowTwoButtons(void)
  {
  }
//+------------------------------------------------------------------+
//| Destrutor                                                        |
//+------------------------------------------------------------------+
CAppWindowTwoButtons::~CAppWindowTwoButtons(void)
  {
  }
//+------------------------------------------------------------------+
//| Create                                                           |
//+------------------------------------------------------------------+
bool CAppWindowTwoButtons::Create(const long chart,const string name,const int subwin,const int x1,const int y1,const int x2,const int y2)
  {
   if(!CAppDialog::Create(chart,name,subwin,x1,y1,x2,y2))
      return(false);
//--- cria os controles dependentes
   if(!CreateButton1())
      return(false);
   if(!CreateButton2())
      return(false);
//--- sucesso
   return(true);
  }
//+------------------------------------------------------------------+
//| Variável Global                                                  |
//+------------------------------------------------------------------+
CAppWindowTwoButtons ExtDialog;
//+------------------------------------------------------------------+
//| Função de inicialização do Expert                                |
//+------------------------------------------------------------------+
int OnInit()
  {
//---Cria o diálogo do aplicativo
   if(!ExtDialog.Create(0,"AppWindowClass with Two Buttons",0,40,40,380,344))
      return(INIT_FAILED);
//---Execução da aplicação
   ExtDialog.Run();
//--- sucesso
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Função de desinicialização do Expert                             |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//---
   Comment("");
//--- destrói o diálogo
   ExtDialog.Destroy(reason);
  }
//+------------------------------------------------------------------+
//| Função de evento do gráfico do Expert                            |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,         // ID do evento  
                  const long& lparam,   // parâmetro do evento do tipo long
                  const double& dparam, // parâmetro do evento do tipo double
                  const string& sparam) // parâmetro do evento do tipo string
  {
   ExtDialog.ChartEvent(id,lparam,dparam,sparam);
  }
//+------------------------------------------------------------------+
//| Cria o botão "Button1"                                           |
//+------------------------------------------------------------------+
bool CAppWindowTwoButtons::CreateButton1(void)
  {
//--- coordenadas
   int x1=INDENT_LEFT;        // x1            = 11  pixels
   int y1=INDENT_TOP;         // y1            = 11  pixels
   int x2=x1+BUTTON_WIDTH;    // x2 = 11 + 100 = 111 pixels
   int y2=y1+BUTTON_HEIGHT;   // y2 = 11 + 20  = 32  pixels
//--- create
   if(!m_button1.Create(0,"Button1",0,x1,y1,x2,y2))
      return(false);
   if(!m_button1.Text("Button1"))
      return(false);
   if(!Add(m_button1))
      return(false);
//--- sucesso
   return(true);
  }
//+------------------------------------------------------------------+
//| Cria o "Button2"                                                 |
//+------------------------------------------------------------------+
bool CAppWindowTwoButtons::CreateButton2(void)
  {
//--- coordenadas
   int x1=INDENT_LEFT+2*(BUTTON_WIDTH+CONTROLS_GAP_X);   // x1 = 11  + 2 * (100 + 5) = 221 pixels
   int y1=INDENT_TOP;                                    // y1                       = 11  pixels
   int x2=x1+BUTTON_WIDTH;                               // x2 = 221 + 100           = 321 pixels
   int y2=y1+BUTTON_HEIGHT;                              // y2 = 11  + 20            = 31  pixels
//--- create
   if(!m_button2.Create(0,"Button2",0,x1,y1,x2,y2))
      return(false);
   if(!m_button2.Text("Button2"))
      return(false);
   if(!Add(m_button2))
      return(false);
//--- sucesso
   return(true);
  }
//+------------------------------------------------------------------+

Vamos ver o algoritmo para a criação de um painel e controles baseados no exemplo do AppWindowTwoButtonsClass.mq5. Todas as ações são executadas no método CAppWindowTwoButtons::Create.

  • Criação do painel:
   if(!CAppDialog::Create(chart,name,subwin,x1,y1,x2,y2))
      return(false);

  • Criação de controles dependentes:
//--- cria os controles dependentes
   if(!CreateButton1())
      return(false);
   if(!CreateButton2())
      return(false);

  • O momento mais importante é que quando o botão é criado, ele é um elemento independente de nosso painel. Para torná-lo um dos elementos dependentes do painel, nós devemos chamar o método Add (o CDialog::Add adiciona um controle à área do cliente no ponteiro/referência especificado)
...
   if(!Add(m_button1))
      return(false);
...
   if(!Add(m_button2))
      return(false);
...

Depois disso, o controle se torna um elemento dependente do painel: todos os eventos são distribuídos centralmente do painel para os controles dependentes.

Como sobrescrever o comportamento dos controles padrão

Se você minimizar o painel, ele será posicionado na coordenada (10;10). O painel minimizado é parcialmente sobreposto com o painel de negociação com um clique:

Vamos corrigir esse posicionamento e adicionar uma verificação se o painel de negociação com um clique está maximizado. Para isso, nós precisamos substituir o método pai CAppDialog::Minimize. Vamos criar outro exemplo: o AppWindowCorrectMinimization.mq5 com base no código do AppWindowTwoButtons.mq5 da seção "Adicionando a CAppDialog ao grupo de controles via CDialog".

Alterações: declarando o método Minimiza:

protected:
   //--- cria os controles dependentes
   bool              CreateButton1(void);
   bool              CreateButton2(void);
   //--- sobreposição do método pai
   virtual void      Minimize(void);

  };

e escrita do corpo do método:

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void CAppWindowCorrectMinimization::Minimize(void)
  {
//--- uma variável para verificar o painel de negociação com um clique
   long one_click_visible=-1;  // 0 - não há um painel de negociação com um clique
   if(!ChartGetInteger(m_chart_id,CHART_SHOW_ONE_CLICK,0,one_click_visible))
     {
      //--- exibe a mensagem de erro no log do Expert
      Print(__FUNCTION__+", Error Code = ",GetLastError());
     }
//--- o recuo mínimo para um painel minimizado
   int min_y_indent=28;
   if(one_click_visible)
      min_y_indent=100;  // use este recuo se houver um painel de negociação com um clique no gráfico
//--- obtendo o recuo atual para o painel minimizado
   int current_y_top=m_min_rect.top;
   int current_y_bottom=m_min_rect.bottom;
   int height=current_y_bottom-current_y_top;
//--- calculando o recuo mínimo do topo para um painel minimizado do aplicativo
   if(m_min_rect.top!=min_y_indent)
     {
      m_min_rect.top=min_y_indent;
      //--- deslocando a borda inferior do ícone minimizado
      m_min_rect.bottom=m_min_rect.top+height;
     }
//--- Agora nós podemos chamar o método da classe base
   CAppDialog::Minimize();
  }



Como ler as macros internas do tipo de processamento de evento

O painel pode lidar com os seguintes tipos de eventos (usados ​​em data folder]\MQL5\Include\Controls\Defines.mqh em "Events")

//+------------------------------------------------------------------+
//| Eventos                                                          |
//+------------------------------------------------------------------+
#define ON_CLICK                (0)   // evento de clique no controle
#define ON_DBL_CLICK            (1)   // evento de clique duplo no controle
#define ON_SHOW                 (2)   // evento de exibir o controle
#define ON_HIDE                 (3)   // evento de ocultar o controle
#define ON_CHANGE               (4)   // evento de alteração do controle
#define ON_START_EDIT           (5)   // evento de início da edição
#define ON_END_EDIT             (6)   // evento do fim da edição
#define ON_SCROLL_INC           (7)   // evento de incremento da barra de rolagem
#define ON_SCROLL_DEC           (8)   // evento de decremento da barra de rolagem
#define ON_MOUSE_FOCUS_SET      (9)   // evento de "cursor do mouse entrou no controle"
#define ON_MOUSE_FOCUS_KILL     (10)  // evento de "cursor do mouse saiu do controle"
#define ON_DRAG_START           (11)  // evento de "início do arraste do controle"
#define ON_DRAG_PROCESS         (12)  // evento de "o controle está sendo arrastado"
#define ON_DRAG_END             (13)  // evento de "fim do arraste do controle"
#define ON_BRING_TO_TOP         (14)  // evento "aumento da prioridade dos eventos do mouse"
#define ON_APP_CLOSE            (100) // evento "encerramento da aplicação"

Esses eventos são tratados no método CAppDialog::OnEvent. Para uma melhor percepção visual de diferentes tipos de eventos, várias macros são descritas em [data folder]\MQL5\Include\Controls\Defines.mqh no bloco "Macro of event handling map":

//+------------------------------------------------------------------+
//| Macro do mapa de manipulação de eventos                          |
//+------------------------------------------------------------------+
#define INTERNAL_EVENT                           (-1)
//--- início do mapa
#define EVENT_MAP_BEGIN(class_name)              bool class_name::OnEvent(const int id,const long& lparam,const double& dparam,const string& sparam) {
//--- fim do mapa
#define EVENT_MAP_END(parent_class_name)         return(parent_class_name::OnEvent(id,lparam,dparam,sparam)); }
//--- manipulação de eventos pelo ID numérico
#define ON_EVENT(event,control,handler)          if(id==(event+CHARTEVENT_CUSTOM) && lparam==control.Id()) { handler(); return(true); }
//--- manipulação de eventos pelo ID numérico pelo ponteiro de controle
#define ON_EVENT_PTR(event,control,handler)      if(control!=NULL && id==(event+CHARTEVENT_CUSTOM) && lparam==control.Id()) { handler(); return(true); }
//--- manipulação de eventos sem análise do ID
#define ON_NO_ID_EVENT(event,handler)            if(id==(event+CHARTEVENT_CUSTOM)) { return(handler()); }
//--- manipulação de eventos pelo ID da linha
#define ON_NAMED_EVENT(event,control,handler)    if(id==(event+CHARTEVENT_CUSTOM) && sparam==control.Name()) { handler(); return(true); }
//--- manipulação de evento indexado
#define ON_INDEXED_EVENT(event,controls,handler) { int total=ArraySize(controls); for(int i=0;i<total;i++) if(id==(event+CHARTEVENT_CUSTOM) && lparam==controls[i].Id()) return(handler(i)); }
//--- manipulação de evento externo
#define ON_EXTERNAL_EVENT(event,handler)         if(id==(event+CHARTEVENT_CUSTOM)) { handler(lparam,dparam,sparam); return(true); }

As macros dos blocos "Events" e "Macro of event handling map" fazem com que o método OnEvent fique assim: 

//+------------------------------------------------------------------+
//| Manipulação de Evento                                            |
//+------------------------------------------------------------------+
EVENT_MAP_BEGIN(CControlsDialog)
ON_EVENT(ON_CLICK,m_bmpbutton1,OnClickBmpButton1)
ON_EVENT(ON_CLICK,m_bmpbutton2,OnClickBmpButton2)
EVENT_MAP_END(CAppDialog)

Este é o código da referência CBmpButton e aqui a CControlsDialog é a instância da classe CAppDialog, que é um painel na forma de uma classe. 

Levando em conta as macros da "Macro do mapa de manipulação de eventos", o OnEvent terá a seguinte aparência:

bool CControlsDialog::OnEvent(const int id,const long& lparam,const double& dparam,const string& sparam) {
if(id==(ON_CLICK+CHARTEVENT_CUSTOM) && lparam==m_bmpbutton1.Id()) { OnClickBmpButton1(); return(true); }
if(id==(ON_CLICK+CHARTEVENT_CUSTOM) && lparam==m_bmpbutton2.Id()) { OnClickBmpButton2(); return(true); }
return(CAppDialog::OnEvent(id,lparam,dparam,sparam)); }

após aplicar o modelador:

bool CControlsDialog::OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam)
  {
   if(id==(ON_CLICK+CHARTEVENT_CUSTOM) && lparam==m_bmpbutton1.Id())
     {
      OnClickBmpButton1();
      return(true);
     }
   if(id==(ON_CLICK+CHARTEVENT_CUSTOM) && lparam==m_bmpbutton2.Id())
     {
      OnClickBmpButton2();
      return(true);
     }
   return(CAppDialog::OnEvent(id,lparam,dparam,sparam));
  }

O código resultante pode ser lido da seguinte forma: se um evento personalizado de um clique no elemento m_bmpbutton1 for recebido, o método OnClickBmpButton1() será chamado. Se um evento customizado de um clique em m_bmpbutton2 for recebido, então o OnClickBmpButton2() será chamado.

Exemplo de manipulação de evento

Nós usamos o AppWindowTwoButtonsClass.mq5 como base e criamos o AppWindowTwoButtonsClasssEvents.mq5 através da adição de manipuladores de eventos de clique de botão.

O primeiro passo é declarar a OnEvent, assim como a OnClickButton1 e OnClickButton2.

//+------------------------------------------------------------------+
//| Classe CControlsDialog                                           |
//| Uso: caixa de diálogo principal do aplicativo Controls           |
//+------------------------------------------------------------------+
class CAppWindowTwoButtons : public CAppDialog
  {
private:
   CButton           m_button1;                       // o objeto button
   CButton           m_button2;                       // o objeto button

public:
                     CAppWindowTwoButtons(void);
                    ~CAppWindowTwoButtons(void);
   //--- create
   virtual bool      Create(const long chart,const string name,const int subwin,const int x1,const int y1,const int x2,const int y2);
   //--- manipulador de eventos do gráfico
   virtual bool      OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam);

protected:
   //--- cria os controles dependentes
   bool              CreateButton1(void);
   bool              CreateButton2(void);
   //--- manipuladores dos eventos de controles dependentes
   void              OnClickButton1(void);
   void              OnClickButton2(void);

  };

Passo 2: o método OnEvent, que possui o seguinte formato devido ao uso de macros da "Events" e "Macro of event handling map" do arquivo em [data folder]\MQL5\Include\Controls\Defines.mqh

protected:
   //--- cria os controles dependentes
   bool              CreateButton1(void);
   bool              CreateButton2(void);
   //--- manipuladores dos eventos de controles dependentes
   void              OnClickButton1(void);
   void              OnClickButton2(void);

  };
//+------------------------------------------------------------------+
//| Manipulação de Evento                                            |
//+------------------------------------------------------------------+
EVENT_MAP_BEGIN(CAppWindowTwoButtons)
ON_EVENT(ON_CLICK,m_button1,OnClickButton1)
ON_EVENT(ON_CLICK,m_button2,OnClickButton2)
EVENT_MAP_END(CAppDialog)
//+------------------------------------------------------------------+
//| Construtor                                                       |
//+------------------------------------------------------------------+

Agora nós precisamos escrever os corpos da OnClickButton1 e OnClickButton2. Um clique no botão 1 abrirá uma posição de COMPRA e um clique no botão 2 fechará a posição. 

Então, vamos alterar o texto nos botões primeiro (as mudanças são implementadas em CreateButton1 e CreateButton2):

...
   if(!m_button1.Text("Open BUY"))
      return(false);
...
...
   if(!m_button2.Text("Close"))
      return(false);
...

Agora, vamos determinar as classes, que precisamos conectar: a classe CTrade é necessária para a negociação, a CPositionInfo é necessária para trabalhar com posições, e o tipo de conta de negociação é obtido na CAccountInfo:

#property description "Painéis de Controle e Caixas de Diálogos. Classe de demonstração da CButton"
#include <Controls\Dialog.mqh>
#include <Controls\Button.mqh>
#include <Trade\PositionInfo.mqh>
#include <Trade\Trade.mqh>
#include <Trade\AccountInfo.mqh>
//+------------------------------------------------------------------+
//| defines                                                          |

Para poder trabalhar com essas classes, nós precisamos declarar as instâncias dessas classes na seção protegida deste painel:

class CAppWindowTwoButtons : public CAppDialog
  {
protected:
   CPositionInfo     m_position;                      // objeto da posição da negocição
   CTrade            m_trade;                         // objeto de negociação
   CAccountInfo      m_account;                       // wrapper de informações da conta

private:
   CButton           m_button1;                       // o objeto button

Clique nos métodos de tratamento:

//+------------------------------------------------------------------+
//| Manipulador de eventos                                           |
//+------------------------------------------------------------------+
void CAppWindowTwoButtons::OnClickButton1(void)
  {
   if(m_account.TradeMode()==ACCOUNT_TRADE_MODE_DEMO)
      m_trade.Buy(1.0);
  }
//+------------------------------------------------------------------+
//| Manipulador de eventos                                           |
//+------------------------------------------------------------------+
void CAppWindowTwoButtons::OnClickButton2(void)
  {
   if(m_account.TradeMode()==ACCOUNT_TRADE_MODE_DEMO)
      for(int i=PositionsTotal()-1;i>=0;i--) // retorna o número de posições atuais
         if(m_position.SelectByIndex(i)) // seleciona a posição pelo índice para acesso adicional às suas propriedades
            if(m_position.Symbol()==Symbol())
               m_trade.PositionClose(m_position.Ticket()); // encerra uma posição pelo símbolo especificado
  }

Agora, o painel em uma conta de demonstração funciona como um painel de negociação: um clique no primeiro botão abre uma posição de COMPRA e um clique no segundo botão encerra todas as posições.


Criação do seu próprio painel — é fácil!

O artigo apresenta um esquema geral da herança de classes da seção Painéis e Caixas de Diálogos. A criação e gerenciamento de qualquer painel gráfico baseado na Biblioteca Padrão é mostrado no exemplo da classe CAppDialog. Além disso, o exemplo mostra como acessar as propriedades de qualquer objeto gráfico incluído em um painel, que é baseado na CAppDialog. Da mesma forma, você pode trabalhar com qualquer filho da classe CWnd.

Além disso, o artigo fornece alguns métodos que não é padrão para alterar as propriedades dos controles do painel interno com base na CAppDialog. Esses métodos ajudam a entender como os objetos gráficos operam:

Espero que esses exemplos ajudem você a criar seus próprios painéis com base na CAppDialog. Eu também recomendo estudar alguns exemplos de criação de controles da seção Painéis e Caixas de Diálogos.

Nome do arquivoComentário
 LearnCAppDialog.mq5 O código mínimo de um painel baseado na CAppDialog
 AppWindowEditDefine.mq5 Um Expert Advisor em forma de painel, que redefine as constantes do Defines.mqh
 LearnCAppDialog_1.mq5 Altera a cor dos objetos "m_client_area" e "m_background"
 LearnCAppDialog_2.mq5 Em vez do CWndContainer::Delete, nós aplicamos a CWndContainer::Destroy para destruir o objeto "m_client_area"
 AppWindowTwoButtons.mq5 Um painel com dois botões nele
 AppWindowTwoButtonsClass.mq5 Um painel com dois botões como uma classe
 AppWindowCorrectMinimization.mq5 Um exemplo de posicionamento padrão de um painel
 AppWindowTwoButtonsClasssEvents.mq5 Um painel com dois botões como uma classe. Manipulando eventos de botão


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

Arquivos anexados |
MQL5.zip (14.12 KB)
Últimos Comentários | Ir para discussão (2)
Marcio Pereira
Marcio Pereira | 13 ago 2019 em 06:39

Muito Bom artigo.

Gostaria de saber como posso fazer para remover o botão de fechar e fixar ele ao fundo?

Vladimir Karputov
Vladimir Karputov | 13 ago 2019 em 08:06
Marcio Pereira :

Muito Bom artigo.

Gostaria de saber como posso fazer para remover o botão de fechar e fixar ele ao fundo?

Eu não vou dizer imediatamente. Eu nunca tive o pensamento de virar tudo de cabeça para baixo :)

Construímos um ZigZag de osciladores. Exemplo de execução do trabalho segundo os termos de referência Construímos um ZigZag de osciladores. Exemplo de execução do trabalho segundo os termos de referência
O artigo apresenta a criação do indicador ZigZag de acordo com os termos de referência para um dos exercícios descritos no artigo "Como criar uma especificação de requisitos para solicitar um indicador". O indicador é construído com base em extremos determinados com a ajuda de um oscilador. O indicador suporta o uso de um dos seguintes osciladores: WPR, CCI, Chaikin, RSI, Stochastic Oscillator.
Redes Neurais Profundas (Parte V). Otimização Bayesiana de hiperparâmetros de uma DNN Redes Neurais Profundas (Parte V). Otimização Bayesiana de hiperparâmetros de uma DNN
O artigo considera a possibilidade de aplicar a otimização Bayesiana para os hiperparâmetros das redes neurais profundas, obtidas por diversas variantes de treinamento. É realizado a comparação da qualidade de classificação de uma DNN com os hiperparâmetros ótimos em diferentes variantes de treinamento. O nível de eficácia dos hiperparâmetros ótimos da DNN foi verificado nos testes fora da amostra (forward tests). As direções possíveis para melhorar a qualidade da classificação foram determinadas.
Visualização dos resultados de otimização pelo critério selecionado Visualização dos resultados de otimização pelo critério selecionado
No artigo, continuamos a desenvolver o aplicativo MQL para trabalhar com resultados de otimização que foi iniciado em artigos anteriores. Desta vez, veremos um exemplo em que podemos gerar uma tabela de melhores resultados após a otimização de parâmetros, especificando através da interface gráfica outro critério.
Trabalhemos com os resultados da otimização através da interface gráfica do usuário Trabalhemos com os resultados da otimização através da interface gráfica do usuário
Continuamos a desenvolver o tópico sobre o processamento e análise de resultados de otimização. Desta vez, a tarefa é selecionar os 100 melhores resultados de otimização e exibi-los na tabela da GUI. Vamos fazer com que o usuário, selecionando uma série na tabela de resultados de otimização, receba um gráfico multissímbolo de saldo e rebaixamento, em gráficos separados.