Como criar um painel gráfico de qualquer nível de complexidade
Índice
- Introdução
- Criando um painel baseado na CAppDialog
- O que a AppWindow pode fazer
- A estrutura do objeto CAppDialog
- O esquema da herança de objetos
- Onde encontrar as principais constantes para a criação dos objetos e como redefini-los usando a diretiva #undef
- Como adicionar novos controles: dois botões
- Como os controles aninhados são movidos e desenhados
- Adicionando a CAppDialog ao grupo de controles via CDialog
- Como sobrescrever o comportamento dos controles padrão
- Como ler as macros internas do tipo de processamento de evento
- Criação do seu próprio painel — é fácil!
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:
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.
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:
- Onde as principais constantes para a criação de objetos podem ser encontradas e como redefini-las usando a diretiva #undef
- Resumo da CAppDialog
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 arquivo | Comentá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
- Aplicativos de negociação gratuitos
- 8 000+ sinais para cópia
- Notícias econômicas para análise dos mercados financeiros
Você concorda com a política do site e com os termos de uso
Muito Bom artigo.
Gostaria de saber como posso fazer para remover o botão de fechar e fixar ele ao fundo?
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 :)