English Русский 中文 Español Deutsch 日本語
Interfaces Gráficas I: Formulário para os Controles (Capítulo 2)

Interfaces Gráficas I: Formulário para os Controles (Capítulo 2)

MetaTrader 5Exemplos | 21 julho 2016, 10:04
2 141 0
Anatoli Kazharski
Anatoli Kazharski

Conteúdo

 

Introdução

Este artigo é a continuação da primeira parte da série sobre as interfaces gráficas. O primeiro artigo Interfaces gráficas I: Preparação da Estrutura da Biblioteca (Capítulo 1) considera em detalhes a finalidade desta biblioteca. A lista completa dos links para os artigos da primeira parte se encontram no final de cada capítulo. Lá, você também pode encontrar e baixar a versão completa da biblioteca, no estágio de desenvolvimento atual. Os arquivos devem estar localizados nas mesmas pastas que o arquivo baixado.

No capítulo anterior nós discutimos a estrutura da biblioteca para a criação das interfaces gráficas. Nós criamos (1) as classes derivadas para os objetos primitivos, (2) uma classe base para todos os controles, (3) as classes principais para armazenar os ponteiros do controle e a gestão desses controles no manipulador comum de eventos.

Neste artigo, nós vamos criar o primeiro e o principal elemento da interface gráfica — o formulário para os controles. Vários controles podem ser anexados a este formulário, podendo ser de qualquer lugar e qualquer combinação. O formulário será móvel e todos os controles ligados a ele serão deslocados da mesma maneira.

 

A Classe do Formulário para os Controles

Como discutido anteriormente, a descrição da classe CElement já continha alguns métodos virtuais, que serão únicos para cada controle. Vamos colocar as suas duplicatas na classe CWindow e em toda descrição futura de outros controles que deverão fazer o mesmo. O código para esses métodos será considerado após a criação dos métodos para a construção de um formulário (janela) para os controles.

//+------------------------------------------------------------------+
//|                                                       Window.mqh |
//|                        Copyright 2015, MetaQuotes Software Corp. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
#include "Element.mqh"
//+------------------------------------------------------------------+
//| Classe para a criação do formulário para os controles            |
//+------------------------------------------------------------------+
class CWindow : public CElement
  {
public:
                     CWindow(void);
                    ~CWindow(void);
   //--- Manipulador de eventos do gráfico
   virtual void      OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam);
   //--- Timer
   virtual void      OnEventTimer(void);
   //--- Mover o controle
   virtual void      Moving(const int x,const int y);
   //--- Exibir, ocultar, resetar, remover
   virtual void      Show(void);
   virtual void      Hide(void);
   virtual void      Reset(void);
   virtual void      Delete(void);
   //--- Definir, resetar as prioridades para o clique esquerdo do mouse
   virtual void      SetZorders(void);
   virtual void      ResetZorders(void);
  };
//+------------------------------------------------------------------+
//| Construtor                                                       |
//+------------------------------------------------------------------+
CWindow::CWindow(void)
  {
  }
//+------------------------------------------------------------------+
//| Destrutor                                                        |
//+------------------------------------------------------------------+
CWindow::~CWindow(void)
  {
  }
//+------------------------------------------------------------------+

Na classe CWindow e em algumas outras classes, serão utilizados muitos modos de diferentes tipos para variadas ações. As listas dos identificadores das constantes nomeadas para todos os modos e tipos de dados serão armazenadas no arquivo separado Enums.mqh. Inclua-o no arquivo Objects.mqh, assim, todas os modos e tipos de enumerações estarão disponíveis em todos os arquivos de biblioteca:

//+------------------------------------------------------------------+
//|                                                      Objects.mqh |
//|                        Copyright 2015, MetaQuotes Software Corp. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
#include "Enums.mqh"
#include "Defines.mqh"
#include <ChartObjects\ChartObjectsBmpControls.mqh>
#include <ChartObjects\ChartObjectsTxtControls.mqh>

Quais partes que constituirão a janela que nós vamos criar?

  1. Fundo. Todos os controles estarão localizados nesta área.
  2. Cabeçalho. Esta parte permite mover a janela que contém os controles de interface listados abaixo.
  3. Ícone. Atributos adicionais para a identificação visual.
  4. Legenda. Nome da janela.
  5. O botão de "Dica". Pressionando este botão, é habilitado o modo de exibição de dicas para os controles que esta funcionalidade está presente.
  6. Botão para minimizar/maximizar a janela.
  7. Botão para fechar a janela.

Fig. 1. Partes que compõem o formulário para os controles

Fig. 1. Partes que compõem o formulário para os controles

Será implementado o modo multi-janela na biblioteca da interface gráfica, que nós estamos desenvolvendo. Um aplicativo pode ter apenas uma janela. No entanto, quando um programa possui muitas opções, não havendo possibilidade de colocá-las em um único formulário, é necessário adicionar mais um formulário. Ela pode ser usada para algumas configurações comuns da aplicação, manual do usuário, uma janela para abrir/salvar o arquivo e até mesmo uma paleta para selecionar uma cor. Assim, ideias variadas podem exigir janelas adicionais. Normalmente, elas são chamadas de "janelas de diálogo".

A classe CWindow deve apresentam uma oportunidade para definir o tipo da janela, isto é, qual janela é a principal do aplicativo e qual dela é a de diálogo. Durante o processo de desenvolvimento, pode acontecer de surgir algumas ideias sobre novos tipos de janelas, mas no momento, vamos criar uma enumeração no arquivo Enums.mqh contendo apenas dois tipos para a janela (1) principal e (2) de diálogo:

//+------------------------------------------------------------------+
//| Enumeração dos tipos de janelas                                  |
//+------------------------------------------------------------------+
enum ENUM_WINDOW_TYPE
  {
   W_MAIN   =0,
   W_DIALOG =1
  };

O fundo e o cabeçalho serão criados a partir do objeto primitivo "Rótulo Retangular" (Rectangle Label). Para isso, nós temos a classe CRectLabel. Para a legenda, nós vamos usar a classe CLabel, com o qual o objeto "Rótulo de texto" (Text Label) poderá ser criado. Para o ícone da janela e os botões listados acima, é necessário o objeto "Rótulo Gráfico" (Bitmap Label), e para isso nós já temos a classe CBmpLabel.

Se o arquivo Element.mqh já está incluído, então, todas estas classes, que foram definidas no arquivo Object.mqh estarão disponíveis para uso. Vamos criar as respectivas instâncias para cada controle da janela no corpo da classe CWindow e todos os métodos necessários para a criação de uma janela:

class CWindow : public CElement
  {
private:
   //--- Objetos para a criação de um formulário
   CRectLabel        m_bg;
   CRectLabel        m_caption_bg;
   CBmpLabel         m_icon;
   CLabel            m_label;
   CBmpLabel         m_button_tooltip;
   CBmpLabel         m_button_unroll;
   CBmpLabel         m_button_rollup;
   CBmpLabel         m_button_close;
   //---
public:
   //--- Métodos para a criação de uma janela
   bool              CreateWindow(const long chart_id,const int window,const string caption_text,const int x,const int y);
   //---
private:
   bool              CreateBackground(void);
   bool              CreateCaption(void);
   bool              CreateIcon(void);
   bool              CreateLabel(void);
   bool              CreateButtonClose(void);
   bool              CreateButtonRollUp(void);
   bool              CreateButtonUnroll(void);
   bool              CreateButtonTooltip(void);
  };

Antes de começarmos a preencher esses métodos, nós temos que preparar algumas funcionalidades na classe CWndContainer onde os arrays armazenarão os ponteiros para todos os controles de interface e suas componentes. Nós também precisamos fazer o seguinte passo no desenvolvimento da estrutura da biblioteca e preparar o programa para testes.

Incluir o arquivo Window.mqh com a classe CWindow no arquivo WndContainer.mqh com a classe CWndContainer. Nós vamos incluir aqui também todas as outras classes com os controles.

//+------------------------------------------------------------------+
//|                                                 WndContainer.mqh |
//|                        Copyright 2015, MetaQuotes Software Corp. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
#include "Window.mqh"
// ...
// Nós vamos incluir aqui também todas as outras classes com os controles
// ...

Desta forma, elas estarão disponíveis para uso na classe CProgram, que está na classe onde a interface do usuário da aplicação MQL será criada. Será possível criar e utilizar os membros da classe com estes tipos de dados nas classes CWndContainer e CWndEvents. Por exemplo, se depois da inclusão do arquivo Window.mqh, um array dinâmico de ponteiros do tipo CWindow for criado (ele não é necessário para o trabalho), não haverá quaisquer problemas e nenhuma mensagem de erro será exibida.

//+------------------------------------------------------------------+
//|                                                 WndContainer.mqh |
//|                        Copyright 2015, MetaQuotes Software Corp. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
#include "Window.mqh"
//+------------------------------------------------------------------+
//| Classe para armazenar todos os objetos da interface              |
//+------------------------------------------------------------------+
class CWndContainer
  {
protected:
   //--- Array da janela
   CWindow          *m_windows[];
  };

No entanto, se o arquivo Window.mqh não foi incluído, então, você receberia uma mensagem de erro durante a criação de um membro da classe com o tipo CWindow, como na imagem abaixo quando você tentar compilar o arquivo:

Fig. 2. Mensagem na compilação notificando que o tipo especificado está ausente

Fig. 2. Mensagem na compilação notificando que o tipo especificado está ausente

Junto com o array de janela dinâmica, nós vamos precisar de uma estrutura de arrays dinâmicos de ponteiros para todos os controles (CElement) e arrays de todos os componentes dos objetos (CChartObject). Como um exemplo dessa estrutura, vamos criar um array dinâmico de tamanho igual ao do array de janela (m_window[]). Como resultado, nós vamos receber um array de arrays de ponteiro para os controles para cada forma (veja o código abaixo).

Por favor, note que o array m_objects[] é declarado com o tipo CChartObject. Na compilação não haverá quaisquer erros já que este tipo de objeto já está presente na estrutura da biblioteca em desenvolvimento e está incluída no arquivo Objects.mqh.

class CWndContainer
  {
protected:
   //--- Estrutura de arrays de controle
   struct WindowElements
     {
      //--- Array comum de todos os objetos
      CChartObject     *m_objects[];
      //--- Array comum de todos os controles
      CElement         *m_elements[];
     };
   //--- Array dos arrays de controles para cada janela
   WindowElements    m_wnd[];
  };

Esta não é uma lista completa de arrays dinâmicos da estrutura WindowElements. Esta lista será atualizada assim que outros controles forem criados.

Para obter o tamanho dos arrays, é necessário utilizar os métodos adequados. O número de objetos de todos os controles e o número de controles para cada janela pode ser obtido passando o índice da janela (window_index) como um parâmetro.

class CWndContainer
  {
public:
   //--- Número de janelas na interface
   int               WindowsTotal(void) { return(::ArraySize(m_windows)); }
   //--- Número de objetos de todos os controles
   int               ObjectsElementsTotal(const int window_index);
   //--- Número de controles
   int               ElementsTotal(const int window_index);
  };

Devemos verificar se o tamanho do array não foi excedido. Muitas vezes, uma frase repetida pode ser escrito como uma substituição de macro, adicionando no início uma substituição de macro predefinida que contém o nome da função (__FUNCTION__). Vamos escrever tal substituição de macro (PREVENTING_OUT_OF_RANGE) e adicionar isto para o arquivo Defines.mqh:

//+------------------------------------------------------------------+
//|                                                      Defines.mqh |
//|                        Copyright 2015, MetaQuotes Software Corp. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
//--- Prevenção de exceder o tamanho do array
#define PREVENTING_OUT_OF_RANGE __FUNCTION__," > Prevenção de exceder o tamanho do array."

Agora, será conveniente usá-lo em todas as funções onde há a verificação se o tamanho do array não foi excedido:

//+------------------------------------------------------------------+
//| Retorna o número de objetos pelo índice da janela específica     |
//+------------------------------------------------------------------+
int CWndContainer::ObjectsElementsTotal(const int window_index)
  {
   if(window_index>=::ArraySize(m_wnd))
     {
      ::Print(PREVENTING_OUT_OF_RANGE);
      return(WRONG_VALUE);
     }
//---
   return(::ArraySize(m_wnd[window_index].m_objects));
  }
//+------------------------------------------------------------------+
//| Retorna o número de controles pelo índice da janela específica   |
//+------------------------------------------------------------------+
int CWndContainer::ElementsTotal(const int window_index)
  {
   if(window_index>=::ArraySize(m_wnd))
     {
      ::Print(PREVENTING_OUT_OF_RANGE);
      return(WRONG_VALUE);
     }
//---
   return(::ArraySize(m_wnd[window_index].m_elements));
  }

Agora, imagine que você é um usuário desta biblioteca e vai para a classe CProgram do EA criado anteriormente para testes. Vamos supor que nós precisamos criar um painel de negociação. Para isso, nós criamos o método CreateTradePanel() na classe CProgram. Não se esqueça que, para salvar espaço no artigo, nós só desenvolvemos em cima do que já foi feito antes e, à medida que avançamos, nós discutimos apenas o que foi adicionado nas diferentes classes.

class CProgram : public CWndEvents
  {
public:
   //--- Cria um painel de negociação
   bool              CreateTradePanel(void);
  };
//+------------------------------------------------------------------+
//| Cria um painel de negociação                                     |
//+------------------------------------------------------------------+
bool CProgram::CreateTradePanel(void)
  {
//--- Criação de um formulário para os controles
// ...
//--- Criando os controles
// ...
//---
   ::ChartRedraw();
   return(true);
  }

O corpo deste método está vazio, mas em breve ele será preenchido com a funcionalidade necessária. A partir dos comentários no corpo do método, fica evidente que a janela para os controles será a primeira a ser criada. Nós precisamos de um método em que as propriedades da janela criada podem ser especificadas. Como nós já incluímos o arquivo Window.mqh no arquivo WndContainer.mqh, agora a classe CWindow estará disponível na classe CProgram. Portanto, nós vamos criar uma instância dessa classe e também um método para criar uma janela para os controles CreateWindow():

class CProgram : public CWndEvents
  {
private:
   //--- Formulário para os controles
   CWindow           m_window;
   //---
private:
   //--- Criação de um formulário
   bool              CreateWindow(const string caption_text);
  };

Esta é hora para responder as seguintes perguntas:

  1. Como os ponteiros para os controles e seus objetos encontram o seu caminho para os arrays, que nós havíamos criado anteriormente na classe CWndContainer?
  2. Como será definido o identificador de cada controle de interface?

Já que a criação de uma interface leva uma sequência determinada (estrita), o processo de desenvolvimento da interface deve ser organizado de tal forma para que o desenvolvedor não se encontre na situação em que ele não sabe o que fazer caso a sequência não seja seguida exatamente. É por isso que a cada ação da biblioteca do usuário será controlada pelo motor da biblioteca. Vamos organizá-la de modo que é impossível criar um formulário para os controles enquanto o ponteiro para ele não se encontra na base dos ponteiros na classe CWndContainer. Um controle não será criado até que um ponteiro para o formulário seja anexado a ele, que não está armazenada em sua classe.

Vamos criar o método AddWindow() para a adição de um ponteiro de janela para a base dos controles de interface na classe CWndContainer. Aqui também será realizado (1) o armazenamento de um ponteiro no array de controles, (2) o armazenamento dos ponteiros de objetos do controle no array comum de ponteiros de objeto, para o qual vamos precisar do método AddToObjectsArray(), e também (3) definir o identificador. Além disso, é necessário (4) armazenar o último identificador na janela de propriedades, uma vez que será necessário definir os identificadores para cada controle dentro de suas classes. Isso será possível porque, como mencionado no parágrafo anterior, cada controle terá um ponteiro para a janela que ele está anexado.

Vamos começar com a criação do método LastId() na classe CWindow para armazenar e obter o identificador do último controle de interface criado:

class CWindow : public CElement
  {
private:
   //--- Identificador do último controle
   int               m_last_id;
   //---
public:
   //--- Métodos para armazenar e obter o ID do último controle criado
   void              LastId(const int id)                                    { m_last_id=id;                       }
   int               LastId(void)                                      const { return(m_last_id);                  }
  };

Vamos criar o resto dos métodos na classe CWndContainer, que será utilizado no método AddWindow(). Cada controle terá um nome de classe único, e, portanto, o método AddToObjectsArray() será um modelo já que os objetos de diferentes controles serão passados ​​para ele. Neste método, nós vamos iterar sobre o array de objetos de controle adicionando em turno um ponteiro para cada um deles no array de base. Para isso, nós vamos precisar de mais um método. Vamos nomeá-lo de AddToArray(). Como apenas os objetos de um tipo (CChartObject) serão passados para este método, não há necessidade de fazer um modelo.

class CWndContainer
  {
protected:
   //--- Adicionando ponteiros de objetos de controle para o array comum
   template<typename T>
   void              AddToObjectsArray(const int window_index,T &object);
   //--- Adiciona um ponteiro de objeto para um array
   void              AddToArray(const int window_index,CChartObject &object);
  };
//+------------------------------------------------------------------+
//| Adiciona ponteiros de objetos de controle para um array comum    |
//+------------------------------------------------------------------+
template<typename T>
void CWndContainer::AddToObjectsArray(const int window_index,T &object)
  {
   int total=object.ObjectsElementTotal();
   for(int i=0; i<total; i++)
      AddToArray(window_index,object.Object(i));
  }
//+------------------------------------------------------------------+
//| Adiciona um ponteiro de objeto para um array                     |
//+------------------------------------------------------------------+
void CWndContainer::AddToArray(const int window_index,CChartObject &object)
  {
   int size=::ArraySize(m_wnd[window_index].m_objects);
   ::ArrayResize(m_wnd[window_index].m_objects,size+1);
   m_wnd[window_index].m_objects[size]=::GetPointer(object);
  }

Agora, o método AddWindow() pode ser criado. Aqui, nós também precisaremos de um contador de controle (m_counter_element_id). O valor dessa variável deve ser aumentado cada vez, depois de adicionar outro controle para a base.

class CWndContainer
  {
private:
   //--- Contador de Controle
   int               m_counter_element_id;
   //---
protected:
   //--- Adiciona um ponteiro da janela para a base de controles de interface
   void              AddWindow(CWindow &object);
  };
//+------------------------------------------------------------------+
//| Adiciona um ponteiro de janela para a base de interface dos controles |
//+------------------------------------------------------------------+
void CWndContainer::AddWindow(CWindow &object)
  {
   int windows_total=::ArraySize(m_windows);
//--- Se não há quaisquer janelas, zere o contador de controle
   if(windows_total<1)
      m_counter_element_id=0;
//--- Adiciona o ponteiro para o array da janela
   ::ArrayResize(m_wnd,windows_total+1);
   ::ArrayResize(m_windows,windows_total+1);
   m_windows[windows_total]=::GetPointer(object);
//--- Adiciona o ponteiro para o array comum de controles
   int elements_total=::ArraySize(m_wnd[windows_total].m_elements);
   ::ArrayResize(m_wnd[windows_total].m_elements,elements_total+1);
   m_wnd[windows_total].m_elements[elements_total]=::GetPointer(object);
//--- Adiciona objetos de controle para o array comum de objetos
   AddToObjectsArray(windows_total,object);
//--- Define o identificador e armazena o id do último controle
   m_windows[windows_total].Id(m_counter_element_id);
   m_windows[windows_total].LastId(m_counter_element_id);
//--- Aumenta o contador de identificadores de controle
   m_counter_element_id++;
  }

A partir de agora, cada vez que uma nova janela de classe personalizada for criada na aplicação MQL (no artigo é CProgram), é necessário adicioná-lo à base usando o método AddWindow().

Em seguida, os métodos para a criação da janela declarados anteriormente têm que ser implementados na classe CWindow. Para isso, nós precisaremos de variáveis ​​adicionais e métodos relacionados com o tipo, aparência da janela e também os modos que da janela, que poderá ser definido pelo usuário ou dependendo do tipo da aplicação em MQL. Eles estão listados abaixo, acompanhados por uma breve descrição:

  1. Métodos para definir e obter o estado da janela (minimizado/maximizado).
  2. Métodos para definir e obter o tipo de janela (principal/caixa de diálogo).
  3. Os métodos para definir o modo de minimizar a janela, considerando o tipo de aplicação MQL quando ele está sendo criado (EA, indicador). Para isso, nós precisamos de métodos que nos permitam gerenciar o tamanho da janela, se este é um indicador não localizado na janela principal do gráfico.
  4. Os métodos para definir a cor de cada objeto da janela pelo utilizador.
  5. Método para a definição do rótulo janela.
  6. Método para definir o rótulo janela por padrão, caso o usuário não tenha feita isso.
  7. Identificação dos limites da área de captura no cabeçalho da janela.
  8. Constantes para o recuo da borda do botão da direita da janela.
  9. Mostrando o botão para o modo de exibição de dicas de ferramentas.
  10. Variáveis ​​de prioridades para o clique esquerdo do mouse para cada objeto de janela.

Adicionar tudo o que foi listado acima para a classe CWindow. Constantes para os recuos da borda do botão da direita do formulário será adicionado no início do arquivo:

//+------------------------------------------------------------------+
//|                                                       Window.mqh |
//|                        Copyright 2015, MetaQuotes Software Corp. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
#include "Element.mqh"
//--- Reuso do botão da borda direita da janela
#define CLOSE_BUTTON_OFFSET   (20)
#define ROLL_BUTTON_OFFSET    (36)
#define TOOLTIP_BUTTON_OFFSET (53)

Cria variáveis ​​mencionadas e métodos no corpo da classe:

//+------------------------------------------------------------------+
//| Classe para a criação do formulário para os controles            |
//+------------------------------------------------------------------+
class CWindow : public CElement
  {
private:
   //--- Identificador do último controle
   int               m_last_id;
   //--- Estado de uma janela minimizada
   bool              m_is_minimized;
   // --- Tipo de janela
   ENUM_WINDOW_TYPE  m_window_type;
   //--- Modo de de definição da altura da sub-janela (para os indicadores)
   bool              m_height_subwindow_mode;
   //--- Modo de minimizar o formulário na sub-janela do indicador
   bool              m_rollup_subwindow_mode;
   //--- Altura da sub-janela do indicador
   int               m_subwindow_height;
   //--- Propriedades do fundo
   int               m_bg_zorder;
   color             m_bg_color;
   int               m_bg_full_height;
   //--- Propriedades do cabeçalho
   int               m_caption_zorder;
   string            m_caption_text;
   int               m_caption_height;
   color             m_caption_bg_color;
   color             m_caption_bg_color_hover;
   color             m_caption_bg_color_off;
   color             m_caption_color_bg_array[];
   //--- Propriedades dos botões
   int               m_button_zorder;
   //--- Cor do quadro do formulário (fundo, cabeçalho)
   color             m_border_color;
   //--- Ícone do formulário
   string            m_icon_file;
   //--- Presença do botão para o modo de exibição das dicas
   bool              m_tooltips_button;
   //--- Para identificar os limites da área de captura no cabeçalho da janela
   int               m_right_limit;
   //---
public:
   // --- Tipo de janela
   ENUM_WINDOW_TYPE  WindowType(void)                                  const { return(m_window_type);              }
   void              WindowType(const ENUM_WINDOW_TYPE flag)                 { m_window_type=flag;                 }
   //--- Ícone Padrão
   string            DefaultIcon(void);
   //--- (1) ícone personalizado da janela, (2) Botão utilizar como referência, (3) limitação da área de captura do cabeçalho
   void              IconFile(const string file_path)                        { m_icon_file=file_path;              }
   void              UseTooltipsButton(void)                                 { m_tooltips_button=true;             }
   void              RightLimit(const int value)                             { m_right_limit=value;                }
   //--- Estado de uma janela minimizada
   bool              IsMinimized(void)                                 const { return(m_is_minimized);             }
   void              IsMinimized(const bool flag)                            { m_is_minimized=flag;                }
   //--- Propriedades do cabeçalho
   void              CaptionText(const string text);
   string            CaptionText(void)                                 const { return(m_caption_text);             }
   void              CaptionHeight(const int height)                         { m_caption_height=height;            }
   int               CaptionHeight(void)                               const { return(m_caption_height);           }
   void              CaptionBgColor(const color clr)                         { m_caption_bg_color=clr;             }
   color             CaptionBgColor(void)                              const { return(m_caption_bg_color);         }
   void              CaptionBgColorHover(const color clr)                    { m_caption_bg_color_hover=clr;       }
   color             CaptionBgColorHover(void)                         const { return(m_caption_bg_color_hover);   }
   void              CaptionBgColorOff(const color clr)                      { m_caption_bg_color_off=clr;         }
   //--- Propriedades de janela
   void              WindowBgColor(const color clr)                          { m_bg_color=clr;                     }
   color             WindowBgColor(void)                                     { return(m_bg_color);                 }
   void              WindowBorderColor(const color clr)                      { m_border_color=clr;                 }
   color             WindowBorderColor(void)                                 { return(m_border_color);             }
   //--- Modos das sub-janela do indicador
   void              RollUpSubwindowMode(const bool flag,const bool height_mode);
   //--- Mudando a altura da sub-janela do indicador
   void              ChangeSubwindowHeight(const int height);
  };

Inicialização de variáveis ​​no construtor da classe pelos valores padrão. No corpo do construtor nós também armazenamos o nome da classe do controle de interface e definimos uma sequência estrita de prioridades para a esquerda clicando no mouse.

//+------------------------------------------------------------------+
//| Construtor                                                       |
//+------------------------------------------------------------------+
CWindow::CWindow(void) : m_last_id(0),
                         m_subwindow_height(0),
                         m_rollup_subwindow_mode(false),
                         m_height_subwindow_mode(false),
                         m_is_minimized(false),
                         m_tooltips_button(false),
                         m_window_type(W_MAIN),
                         m_icon_file(""),
                         m_right_limit(20),
                         m_caption_height(20),
                         m_caption_bg_color(C'88,157,255'),
                         m_caption_bg_color_off(clrSilver),
                         m_caption_bg_color_hover(C'118,177,255'),
                         m_bg_color(C'15,15,15'),
                         m_border_color(clrLightGray)

  {
//--- Armazena o nome da classe de controle na classe base
   CElement::ClassName(CLASS_NAME);
//--- Define uma sequência rigorosa de prioridades
   m_bg_zorder      =0;
   m_caption_zorder =1;
   m_button_zorder  =2;
  }

A implementação dos métodos destacados em amarelo no código acima é apresentada abaixo. Se as variáveis ​​e métodos da classe base são necessários, então, como boas práticas de programação, é melhor prefixar eles com o nome da classe e dois pontos duplos (NomeDaClasse::). Desta forma, o código se torna mais fácil para estudar e mais rápido para se lembrar caso você não tenha visto ele por um tempo. O nome da variável m_subwindow_height será definida automaticamente quando a janela for criada.

//+------------------------------------------------------------------+
//| Modo de minimizar a sub-janela do indicador                      |
//+------------------------------------------------------------------+
void CWindow::RollUpSubwindowMode(const bool rollup_mode=false,const bool height_mode=false)
  {
   if(CElement::m_program_type!=PROGRAM_INDICATOR)
      return;
//---
   m_rollup_subwindow_mode =rollup_mode;
   m_height_subwindow_mode =height_mode;
//---
   if(m_height_subwindow_mode)
      ChangeSubwindowHeight(m_subwindow_height);
  }

Nós devemos voltar para o método CWindow::RollUpSubwindowMode() em um dos capítulos seguintes, quando estaremos a considerar os exemplos práticos sobre o gráfico no terminal. Desta forma, ficará mais claro o que cada um dos modos sugeridos representa.

O método CWindow::ChangeSubwindowHeight() permite alterar a altura da sub-janela do indicador, se o programa é de tal modo:

//+------------------------------------------------------------------+
//| Altera a altura da sub-janela do indicador                       |
//+------------------------------------------------------------------+
void CWindow::ChangeSubwindowHeight(const int height)
  {
   if(CElement::m_subwin<1 || CElement::m_program_type!=PROGRAM_INDICATOR)
      return;
//---
   if(height>0)
      ::IndicatorSetInteger(INDICATOR_HEIGHT,height);
  }

 

Métodos para a Criação de um Formulário

Para todas as partes componentes da janela serem visíveis após a criação, elas devem ser definidas em uma sequência estrita (camada após camada):

  1. Fundo.
  2. Cabeçalho.
  3. Controles de cabeçalho.

No início do método CWindow::CreateWindow() onde todas as partes componentes da janela serão criados, organizaremos uma verificação se o ponteiro para esta janela está armazenado na base dos controles. Se isto não for feito, então, o identificador da janela terá o valor de WRONG_VALUE. Nesse caso, a janela não será criada e a função retornará false, imprimindo uma mensagem na aba Diário sobre qual ação deve ser tomada. Se tudo estiver em ordem, então as variáveis ​​da classe atual e base serão inicializadas pelos valores passados por parâmetro. Entre essas variáveis ​​nós temos:

  • Identificador do gráfico.
  • Número da janela do gráfico.
  • Nome da janela (texto no cabeçalho).
  • Coordenadas para definir a janela (x, y).
  • Variável duplicada da altura do formulário (m_bg_full_height). Será usado para mudar os tamanhos dos formulários. A altura do formulário é definida antes do método de criação de um formulário ser aplicado (teremos exemplos mais tarde).

O identificador gráfico e o número da janela do gráfico são definidos no construtor da classe CWndEvents. Ao criar os controles na classe personalizada (CWndEvents::CProgram), esses mesmos valores são passados ​​para os métodos de sua criação.

Em seguida, seguem os métodos para a criação de todos os objetos do formulário. Se qualquer um dos objetos não forem criados, então, a função retornará false. Depois de instanciar todos os objetos, dependendo do tipo do programa e do modo definido, uma verificação deve ser efetuada caso o tamanho da janela tiver que ser ajustado (se este for um indicador numa janela separada gráfico). Anteriormente, eu mencionei a variável m_subwindow_height, que é inicializada quando a janela é criada. É precisamente aqui que essa inicialização acontece. Ele está destacado em amarelo no código abaixo.

E, por fim, se foi estabelecido que esta janela será uma janela de diálogo, isso deve ser ocultado. Alguma explicação é necessária aqui. Se a interface de programa contém mais do que uma janela, então, estas janelas serão anexadas ao gráfico durante o carregamento do programa. Assim, ao chamar uma ou outra janela de diálogo, o usuário não precisará ficar criando e removendo as janelas de diálogo. Elas estarão todas presentes no gráfico, elas estarão apenas no modo oculto. Quando uma janela de diálogo for chamada, ela será visível e quando ela for fechada, ela será oculta, mas não removida.

Tudo o que eu disse acima está refletido no código abaixo:

//+------------------------------------------------------------------+
//| Cria o formulário para os controles                              |
//+------------------------------------------------------------------+
bool CWindow::CreateWindow(const long chart_id,const int subwin,const string caption_text,const int x,const int y)
  {
   if(CElement::Id()==WRONG_VALUE)
     {
      ::Print(__FUNCTION__," > Antes de criar uma janela, o ponteiro é para ser passado "
            "para o array de janela usando o método CWndContainer::AddWindow(CWindow &object).");
      return(false);
     }
//--- Inicialização das variáveis
   m_chart_id       =chart_id;
   m_subwin         =subwin;
   m_caption_text   =caption_text;
   m_x              =x;
   m_y              =y;
   m_bg_full_height =m_y_size;
//--- Criação de todos os objetos da janela
   if(!CreateBackground())
      return(false);
   if(!CreateCaption())
      return(false);
   if(!CreateLabel())
      return(false);
   if(!CreateIcon())
      return(false);
   if(!CreateButtonClose())
      return(false);
   if(!CreateButtonRollUp())
      return(false);
   if(!CreateButtonUnroll())
      return(false);
   if(!CreateButtonTooltip())
      return(false);
//--- Se este programa é um indicador
   if(CElement::ProgramType()==PROGRAM_INDICATOR)
     {
      //--- Se foi definido o modo de altura da sub-janela
      if(m_height_subwindow_mode)
        {
         m_subwindow_height=m_bg_full_height+3;
         ChangeSubwindowHeight(m_subwindow_height);
        }
     }
//--- Oculta a janela, se for uma janela de diálogo
   if(m_window_type==W_DIALOG)
      Hide();
//---
   return(true);
  }

A função virtual Hide() é usada para ocultar os objetos de controle. No código acima, ele está destacado em verde. Antes, na classe CWindow, nós consideramos apenas a sua declaração. No seu código, todos os objetos da janela estão ocultos em todos os tempos gráficos e a variável m_is_visible da classe base recebe o estado false.

//+------------------------------------------------------------------+
//| Ocultar a janela                                                 |
//+------------------------------------------------------------------+
void CWindow::Hide(void)
  {
//--- Oculta todos os objetos
   for(int i=0; i<ObjectsElementTotal(); i++)
      Object(i).Timeframes(OBJ_NO_PERIODS);
//--- Estado de visibilidade
   CElement::IsVisible(false);
  }

Se você tiver introduzido todas as alterações acima, não se apresse para compilar o arquivo com a classe CWindow já que você receberá uma mensagem de erro (veja a imagem abaixo). Isso acontecerá, pois, todos os métodos de criação de partes da janela, no momento, não possuem conteúdo. Agora, vamos dedicar algum tempo para a sua implementação.

Fig. 3. Mensagem sobre a ausência de implementação dos métodos

Fig. 3. Mensagem sobre a ausência de implementação dos métodos

Vamos começar com o plano de fundo e usar o método CWindow::CreateBackground(). No início de cada método relacionado com a criação do objeto, nós devemos formar o nome para este objeto. Ele será composto por várias partes:

  • Nome do programa.
  • Pertencente ao controle ("window").
  • Pertencente a uma parte do controle ("bg").
  • Identificador do controle.

As partes serão separadas com o sinal de sublinhado «_». O nome do programa é salvo na classe base CElement na lista de inicialização do construtor. O identificador do controle é obtido no momento da adição da janela para a base de controle.

Então, vamos realizar a verificação do tamanho do fundo da janela e seu ajuste, que vai depender se a janela está minimizada ou maximizada. Vamos olhar para frente. Quando o tempo gráfico ou o símbolo do gráfico for alterado, todos os objetos serão movidos para as funções de desinicialização do programa. Na função do programa de inicialização, eles serão restaurados de acordo com os valores atuais dos campos da classe (variáveis).

Após o ajustamento do tamanho do fundo, a configuração do objeto é realizada, seguido pela definição das propriedades. No final do método, o ponteiro de objeto é salvo no array da classe base. O código da função CWindow::CreateBackground() é apresentada a seguir:

//+------------------------------------------------------------------+
//| Cria o fundo da janela                                           |
//+------------------------------------------------------------------+
bool CWindow::CreateBackground(void)
  {
//--- Formação do nome da janela
   string name=CElement::ProgramName()+"_window_bg_"+(string)CElement::Id();
//--- O tamanho da janela depende do seu estado (minimizado/maximizado)
   int y_size=0;
   if(m_is_minimized)
     {
      y_size=m_caption_height;
      CElement::YSize(m_caption_height);
     }
   else
     {
      y_size=m_bg_full_height;
      CElement::YSize(m_bg_full_height);
     }
//--- Ajusta o fundo da janela
   if(!m_bg.Create(m_chart_id,name,m_subwin,m_x,m_y,m_x_size,y_size))
      return(false);
//--- Define as propriedades
   m_bg.BackColor(m_bg_color);
   m_bg.Color(m_border_color);
   m_bg.BorderType(BORDER_FLAT);
   m_bg.Corner(m_corner);
   m_bg.Selectable(false);
   m_bg.Z_Order(m_bg_zorder);
   m_bg.Tooltip("\n");
//--- Armazena o ponteiro de objeto
   CElement::AddToArray(m_bg);
   return(true);
  }

No método de criação do cabeçalho da janela, além das ações básicas semelhantes à forma como ele foi implementado no método de criação do fundo da janela, é necessário salvar as coordenadas e tamanhos do objeto primitivo da classe CRectLabel, que está localizada no arquivo Objects.mqh. Os valores desses campos serão atualizados quando a janela for deslocada (e, portanto, as suas coordenadas mudam) e quando seus tamanhos mudarem. Se estes valores são estáticos, será impossível controlar o cursor sobre o cabeçalho. Esta regra será aplicável a todos os outros objetos em todos os outros controles.

Uma outra diferença reside na capacidade do cabeçalho de reagir com o cursor do mouse, quando ele está na área da janela ou quando o cursor sair da área da janela. Para isso, o método InitColorArray() foi criado na classe CElement.

Código do método CElement::createCaption():

//+------------------------------------------------------------------+
//| Cria o cabeçalho da janela                                       |
//+------------------------------------------------------------------+
bool CWindow::CreateCaption(void)
  {
   string name=CElement::ProgramName()+"_window_caption_"+(string)CElement::Id();
//--- Define o cabeçalho da janela
   if(!m_caption_bg.Create(m_chart_id,name,m_subwin,m_x,m_y,m_x_size,m_caption_height))
      return(false);
//--- Define as propriedades
   m_caption_bg.BackColor(m_caption_bg_color);
   m_caption_bg.Color(m_border_color);
   m_caption_bg.BorderType(BORDER_FLAT);
   m_caption_bg.Corner(m_corner);
   m_caption_bg.Selectable(false);
   m_caption_bg.Z_Order(m_caption_zorder);
   m_caption_bg.Tooltip("\n");
//--- Armazena as coordenadas
   m_caption_bg.X(m_x);
   m_caption_bg.Y(m_y);
//--- Armazena os tamanhos (em objeto)
   m_caption_bg.XSize(m_caption_bg.X_Size());
   m_caption_bg.YSize(m_caption_bg.Y_Size());
//--- Inicializar o array do gradiente
   CElement::InitColorArray(m_caption_bg_color,m_caption_bg_color_hover,m_caption_color_bg_array);
//--- Armazena o ponteiro de objeto
   CElement::AddToArray(m_caption_bg);
   return(true);
  }

O rótulo do programa na janela principal da interface será representada por padrão pelos ícones previamente preparados. Qual delas que será refletida dependerá do tipo de programa a ser desenvolvido (ENUM_PROGRAM_TYPE). Todos os ícones do rótulo podem ser baixados no final do artigo. Para usar em programas, os ícones dos rótulos que não são controles clicáveis ​​devem ser colocados na pasta <data folder>\MQLX\Images\EasyAndFastGUI\Icons\bmp16. O nome bmp16 significa que a pasta contém os ícones de tamanho 16X16 píxeis. Se a sua coleção de ícones da biblioteca for constituída por aqueles que são 24X24 em tamanho, então, a pasta será chamada de bmp24 etc.

Ícones padrões que são usados ​​por padrão para a criação de controles, devem ser colocados na pasta <data folder>\MQLX\Images\EasyAndFastGUI\Controls.

Para a identificação automática do rótulo da janela principal do programa, o método DefaultIcon() foi declarado na classe CWindow. Abaixo está o código desse método:

//+------------------------------------------------------------------+
//| Identificando o rótulo padrão                                    |
//+------------------------------------------------------------------+
string CWindow::DefaultIcon(void)
  {
   string path="Images\\EasyAndFastGUI\\Icons\\bmp16\\advisor.bmp";
//---
   switch(CElement::ProgramType())
     {
      case PROGRAM_SCRIPT:
        {
         path="Images\\EasyAndFastGUI\\Icons\\bmp16\\script.bmp";
         break;
        }
      case PROGRAM_EXPERT:
        {
         path="Images\\EasyAndFastGUI\\Icons\\bmp16\\advisor.bmp";
         break;
        }
      case PROGRAM_INDICATOR:
        {
         path="Images\\EasyAndFastGUI\\Icons\\bmp16\\indicator.bmp";
         break;
        }
     }
//---
   return(path);
  }

Deve-se notar que somente os comandos de interface relacionados com a visualização de dados podem ser utilizados em scripts. A explicação aqui é que os scripts não fornecem as funcionalidades necessárias para utilizar as funções para rastrear eventos.

Se o ícone da janela deve ser redefinido, o método CWindow::IconFile() poderá ser utilizado. Os exemplos deste e de outros métodos serão considerados quando nós testarmos as interfaces com base nesta biblioteca no gráfico do terminal.

Os recursos com ícones para diferentes controles serão incluídos (#resource) ao lado do método (fora do corpo do método), onde eles serão usados (destaque em amarelo). O rótulo terá sempre um estado (um ícone) e, portanto, um caminho para uma única versão será especificado nos métodos dos objetos primitivos CChartObjectBmpLabel::BmpFileOn() e CChartObjectBmpLabel::BmpFileOff().

Todas as coordenadas dos objetos (exceto o cabeçalho da janela) de todos os controles de cada janela são calculadas em relação às coordenadas (canto superior esquerdo) dessa janela nas variáveis ​​locais deste método. Consequentemente, todos os recortes do objeto em relação às coordenadas da janela são armazenados nos campos correspondentes da classe do objeto primitivo no arquivo Objects.mqh. Eles são obrigados a não calcular as coordenadas novamente quando a janela for realocada. Isso significa que é necessário apenas uma operação para a atualização das coordenadas de cada objeto de todos os controlos, assim como no momento de ajuste do objeto. No código abaixo, a ausência de recortes do ponto da borda da janela está destacada em verde.

//+------------------------------------------------------------------+
//| Cria o rótulo do programa                                        |
//+------------------------------------------------------------------+
//--- Ícones (por padrão) que simboliza o tipo do programa
#resource "\\Images\\EasyAndFastGUI\\Icons\\bmp16\\advisor.bmp"
#resource "\\Images\\EasyAndFastGUI\\Icons\\bmp16\\indicator.bmp"
#resource "\\Images\\EasyAndFastGUI\\Icons\\bmp16\\script.bmp"
//---
bool CWindow::CreateIcon(void)
  {
   string name=CElement::ProgramName()+"_window_icon_"+(string)CElement::Id();
//--- Coordenadas do objeto
   int x=m_x+5;
   int y=m_y+2;
//--- Define o ícone da janela
   if(!m_icon.Create(m_chart_id,name,m_subwin,x,y))
      return(false);
//--- Ícone padrão, se não foi especificado pelo usuário
   if(m_icon_file=="")
      m_icon_file=DefaultIcon();
//--- Define as propriedades
   m_icon.BmpFileOn("::"+m_icon_file);
   m_icon.BmpFileOff("::"+m_icon_file);
   m_icon.Corner(m_corner);
   m_icon.Selectable(false);
   m_icon.Z_Order(m_button_zorder);
   m_icon.Tooltip("\n");
//--- Armazena as coordenadas
   m_icon.X(x);
   m_icon.Y(y);   
//--- Recortes do ponto da borda
   m_icon.XGap(x-m_x);
   m_icon.YGap(y-m_y);
//--- Armazena os tamanhos (em objeto)
   m_icon.XSize(m_icon.X_Size());
   m_icon.YSize(m_icon.Y_Size());
//--- Adiciona os objetos para o array do grupo
   CElement::AddToArray(m_icon);
   return(true);
  }

Como o código do método para a criação de um rótulo de texto do cabeçalho CWindow::createLabel() não possui peculiaridades diferentes do que foi descrito no método para a criação do rótulo do programa, nós não vamos discutir isso aqui para economizar espaço no artigo. Você pode encontrar o seu código no arquivo Window.mqh no final deste artigo.

Agora, nós vamos discutir os botões que serão localizados no cabeçalho da janela. Todos esses botões terão regras que os deixarão diferente dos métodos descritos acima:

  • Alterando a aparência quando o cursor está pairando sobre ele. É por isso que dois ícones serão carregados para esses botões. Uma será usado para o estado ON quando o cursor está sobre o botão. O segundo será utilizado para o estado OFF quando o cursor está fora da área do botão.
  • Os botões não serão definidos para scripts.
  • Para as janelas de diálogo, estará disponível um único botão, que é para fechar a janela.
  • A área de captura no cabeçalho para mover a janela será calculada dependendo da quantidade de botões que foram definidos.

Para a funcionalidade de minimizar e maximizar a janela, é necessário usar dois botões. Eles estarão localizados um por cima do outro e a visibilidade de cada um deles vai depender de qual estado a janela se encontra. Quando a janela é maximizada, então, o botão para minimizar a janela será visível e vice-versa.

Em todo o resto, estes botões não são diferentes de outros métodos para a criação de partes de um formulário de controle. Maiores detalhes do código dos métodos CreateButtonClose(), CreateButtonRollUp(), CreateButtonUnroll() e CreateButtonTooltip() podem ser encontrados no arquivo Window.mqh anexado no final do artigo.

Os ícones para todos os botões que serão utilizados nos exemplos abaixo, também podem ser baixados no final do artigo. Você pode usar os seus próprios ícones. Nesse caso, tenha em mente que em caso dos nomes de seus arquivos serem diferentes dos sugeridos neste artigo, os mesmos nomes dos arquivos BMP como no código em anexo devem ser armazenados ou o caminho para eles devem ser alterados no código.

 

Anexar um Formulário no Gráfico

Finalmente, estamos na fase em que podemos testar uma parte do que tem sido feito e ver o resultado. Vamos dar uma olhada como o controle de formulário se parece no gráfico. A funcionalidade para criação deste formulário foi criada acima.

Nós vamos continuar a trabalhar no arquivo de testes, que foi criado anteriormente. Lá nós paramos a criação de um método comum para a criação da interface CProgram::CreateTradePanel() e o método para a criação do formulário para os controles CProgram::CreateWindow(). Abaixo está o código para a criação de um formulário. Por favor, note como a cor de fundo e o cabeçalho do formulário pode ser redefinido.

//+------------------------------------------------------------------+
//| Cria o formulário para os controles                              |
//+------------------------------------------------------------------+
bool CProgram::CreateWindow(const string caption_text)
  {
//--- Adiciona um ponteiro de janela para o array da janela
   CWndContainer::AddWindow(m_window);
//--- Propriedades
   m_window.XSize(200);
   m_window.YSize(200);
   m_window.WindowBgColor(clrWhiteSmoke);
   m_window.WindowBorderColor(clrLightSteelBlue);
   m_window.CaptionBgColor(clrLightSteelBlue);
   m_window.CaptionBgColorHover(C'200,210,225');
//--- Criação de um formulário
   if(!m_window.CreateWindow(m_chart_id,m_subwin,caption_text,1,1))
      return(false);
//---
   return(true);
  }

No método comum CProgram::CreateTradePanel() chame o método CProgram::CreateWindow() , tendo passado um texto para o cabeçalho da janela como único parâmetro:

//+------------------------------------------------------------------+
//| Cria um painel de negociação                                     |
//+------------------------------------------------------------------+
bool CProgram::CreateTradePanel(void)
  {
//--- Criação de um formulário para os controles
   if(!CreateWindow("EXPERT PANEL"))
      return(false);
//--- Criando os controles
// ...
//---
   ::ChartRedraw();
   return(true);
  }

Agora, nós só temos que adicionar uma chamada do método CProgram::CreateTradePanel() no arquivo principal do programa na função OnInit(). No caso de haver um erro ao criar a interface do programa, imprima uma mensagem correspondente na aba Expert e termine o trabalho:

//+------------------------------------------------------------------+
//| Função de inicialização do Expert                                |
//+------------------------------------------------------------------+
int OnInit(void)
  {
   program.OnInitEvent();
//--- Define um painel de negociação
   if(!program.CreateTradePanel())
     {
      ::Print(__FUNCTION__," > Falha na criação de uma interface gráfica!");
      return(INIT_FAILED);
     }
//--- Inicialização bem sucedida
   return(INIT_SUCCEEDED);
  }

Compile o código e carregue o programa para o gráfico. Se tudo estiver correto, você verá um formulário no gráfico como na imagem abaixo:

Fig. 4. O primeiro teste para anexar o formulário ao gráfico

Fig. 4. O primeiro teste para anexar o formulário ao gráfico

Se você esquecer de adicionar um ponteiro de janela para a base do ponteiro ao desenvolver uma aplicação MQL, você ainda será capaz de compilar o código. No entanto, quando você tentar carregar o programa para o gráfico, você verá que o programa será imediatamente apagado e a seguinte mensagem será impressa no registro da aba dos "Expert Advisors" (painel "Caixa de Ferramentas"):

2015.09.23 19:26:10.398 TestLibrary (EURUSD,D1) OnInit > Falha ao criar a interface gráfica!
2015.09.23 19:26:10.398 TestLibrary (EURUSD,D1) CWindow::CreateWindow > Antes de criar uma janela, o ponteiro deve ser guardado na base: CWndContainer::AddWindow(CWindow &object).

Exemplos mais detalhados sobre a utilização dos métodos da classe CWindow em um indicador e script, serão mostrados nos capítulos seguintes (artigos) da série.

 

Conclusão

A estrutura atual da biblioteca que está sendo desenvolvida está ilustrada no diagrama abaixo:

Fig. 5. Adicionando o controle principal da interface e um formulário para os controles do projeto.

Fig. 5. Adicionando o controle principal da interface e um formulário para os controles do projeto

Todas as classes relativas aos controles de interface, derivados de sua classe base CElement estão localizados no retângulo grande com uma seta espessa de cor azul. Todos os elementos da interface serão incluídos no arquivo com a classe CWndContainer.

Você pode baixar para o seu computador todo o material da primeira parte da série para você testá-lo. Se você tiver dúvidas sobre a utilização do material a partir desses arquivos, você poderá consultar a descrição detalhada do desenvolvimento da biblioteca em um dos artigos da lista abaixo ou fazer sua pergunta nos comentários deste artigo.

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

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

Arquivos anexados |
Interfaces Gráficas I: Animação na Interface Gráfica (Capítulo 3) Interfaces Gráficas I: Animação na Interface Gráfica (Capítulo 3)
No artigo anterior, nós começamos a desenvolver uma classe de formulário para os controles. Neste artigo, nós vamos continuar a desenvolver esta classe e preenchê-la com os métodos para mover um formulário sobre a área do gráfico. Em seguida, integraremos este componente da interface para o núcleo da biblioteca. Além disso, nós vamos garantir que os controles do formulário mudem de cor quando o cursor do mouse estiver pairando sobre eles.
Expert Advisor Universal: O Modelo de Evento e o Protótipo da Estratégia de Negociação (Parte 2) Expert Advisor Universal: O Modelo de Evento e o Protótipo da Estratégia de Negociação (Parte 2)
Este artigo continua a série de publicações do modelo universal de um Expert Advisor. Esta parte descreve em detalhes o modelo de eventos original, baseado no processamento de dados centralizado e considera a estrutura da classe base CStrategy.
Expert Advisor Universal: Negociação em Grupo e Gestão de uma Carteira de Estratégias (Parte 4) Expert Advisor Universal: Negociação em Grupo e Gestão de uma Carteira de Estratégias (Parte 4)
Na última parte da série de artigos sobre o mecanismo de negociação CStrategy, vamos considerar a operação simultânea de vários algoritmos de negociação, aprenderemos a carregar estratégias de arquivos XML, e apresentaremos um painel simples para selecionar Expert Advisors partir de um único módulo executável e gerenciar os seus modos de negociação.
Interfaces Gráficas I: Preparação da Estrutura da Biblioteca (Capítulo 1) Interfaces Gráficas I: Preparação da Estrutura da Biblioteca (Capítulo 1)
Este artigo é o começo de uma outra série sobre o desenvolvimento de interfaces gráficas. Atualmente, não há uma única biblioteca de código que permite a criação rápida e fácil de interfaces gráficas de alta qualidade dentro das aplicações em MQL. Me refiro as interfaces gráficas que estamos acostumados nos sistemas operacionais comuns.