Cómo crear un panel gráfico de cualquier nivel de complejidad

6 junio 2018, 16:06
Vladimir Karputov
0
3 215

Contenido

Introducción

Hasta el momento, la mayoría de programadores que escriben indicadores y asesores para la plataforma MetaTrader 5 apenas han usado las capacidades para crear interfaces gráficas en sus aplicaciones.  La razón principal de este hecho, en nuestra opinión, es que las clases Paneles y diálogos de la Biblioteca estándar contienen solo una descripción seca de los métodos. Sí, para muchos controles gráficos en la guía de ayuda se dan ejemplos de código con comentarios. Pero sin una comprensión completa del dispositivo y la ideología implementada en ellos, no podremos crear nuestros propios paneles.

Hemos intentado descubrir qué y cómo está construido en ellos, y queremos compartir con otros desarrolladores los conocimientos obtenidos. Hemos empezado por una sencilla aplicación que crea un panel gráfico basado en la clase CAppDialog:, y después hemos ido introduciendo paulatinamente correcciones en ella y analizado los resultados obtenidos.

Leyendo este artículo, podrá aprender todo lo que necesita sobre la clase CAppDialog: cómo crear un panel y qué conjunto mínimo de funciones necesita escribir, además de cómo agregar elementos adicionales (por ejemplo, botones). También veremos de qué objetos está compuesto el panel y en qué orden se crean. Asimismo, mostraremos qué constantes se usan al crear el panel y cómo cambiarlas. Tampoco estará de más aprender un poco de información sobre cómo romper rápidamente cualquier panel.


Creamos un panel basado en CAppDialog

Para empezar, vamos a ver alguna información general. 

La clase CAppDialog es la clase del elemento de control combinado "Ventana de diálogo de la aplicación". La clase CAppDialog une visualemnte grupos de elementos heterogéneos relacionados funcionalmente en los límites de un programa MQL5.

Aquí tenemos el código mínimo para crear el panel:

//+------------------------------------------------------------------+
//|                                              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;
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- create application dialog
   if(!AppWindow.Create(0,"AppWindow",0,20,20,360,324))
      return(INIT_FAILED);
//--- run application
   AppWindow.Run();
//--- succeed
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//--- destroy dialog
   AppWindow.Destroy(reason);
  }
//+------------------------------------------------------------------+
//| Expert chart event function                                      |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,         // event ID  
                  const long& lparam,   // event parameter of the long type
                  const double& dparam, // event parameter of the double type
                  const string& sparam) // event parameter of the string type
  {
   AppWindow.ChartEvent(id,lparam,dparam,sparam);
  }
//+------------------------------------------------------------------+

El resultado del funcionamiento del asesor "LearnCAppDialog.mq5" es el panel de control creado:

CAppDialog panel

El asesor "LearnCAppDialog.mq5" contiene el conjunto mínimo de comandos necesario para crear un panel y que este funcione. Bien, vamos paso a paso:

  • declaramos a nivel global del programael ejemplar de la clase CAppDialog:
#include <Controls\Dialog.mqh>

CAppDialog AppWindow;
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
  • creamos el panel AppWindow e iniciamos el funcionamiento del panel:
int OnInit()
  {
//--- create application dialog
   if(!AppWindow.Create(0,"AppWindow",0,20,20,360,324))
      return(INIT_FAILED);
//--- run application
   AppWindow.Run();
//--- succeed
   return(INIT_SUCCEEDED);
  }
  • transferimos el evento ChartEvent a panel& AppWindow :
//+------------------------------------------------------------------+
//| Expert chart event function                                      |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,         // event ID  
                  const long& lparam,   // event parameter of the long type
                  const double& dparam, // event parameter of the double type
                  const string& sparam) // event parameter of the string type
  {
   AppWindow.ChartEvent(id,lparam,dparam,sparam);
  }

y, en último lugar, algo muy importante:

  • destruimos el elemento de control: llamamos al método Destroy
void OnDeinit(const int reason)
  {
//--- destroy dialog
   AppWindow.Destroy(reason);
  }

Si no destruimos el panel, cada vez que cambie el marco temporal o cambie un símbolo, obtendremos un montón de elementos restantes del panel anterior.


Qué sabe hacer AppWindow

En teoría, un panel basado en la clase CAppDialog puede procesar eventos de este tipo: 

//+------------------------------------------------------------------+
//| Events                                                           |
//+------------------------------------------------------------------+
#define ON_CLICK                (0)   // clicking on control event
#define ON_DBL_CLICK            (1)   // double clicking on control event
#define ON_SHOW                 (2)   // showing control event
#define ON_HIDE                 (3)   // hiding control event
#define ON_CHANGE               (4)   // changing control event
#define ON_START_EDIT           (5)   // start of editing event
#define ON_END_EDIT             (6)   // end of editing event
#define ON_SCROLL_INC           (7)   // increment of scrollbar event
#define ON_SCROLL_DEC           (8)   // decrement of scrollbar event
#define ON_MOUSE_FOCUS_SET      (9)   // the "mouse cursor entered the control" event
#define ON_MOUSE_FOCUS_KILL     (10)  // the "mouse cursor exited the control" event
#define ON_DRAG_START           (11)  // the "control dragging start" event
#define ON_DRAG_PROCESS         (12)  // the "control is being dragged" event
#define ON_DRAG_END             (13)  // the "control dragging end" event
#define ON_BRING_TO_TOP         (14)  // the "mouse events priority increase" event
#define ON_APP_CLOSE            (100) // "closing the application" event

Estos eventos se han tomado del bloque Events del archivo [data folder]\MQL5\Include\Controls\Defines.mqh. Se trata del clic, el doble clic, el inicio y el final de la edición, la obtención de foco, el arrastre (inicio, proceso y finalización) y la muestra y la ocultación del panel. Podrá encontrar ejemplos sobre cómo trabajar con estos eventos en los ejemplos de la sección Paneles y ventanas de diálogo. Por ejemplo, en el ejemplo  CRadioGroup se da el procesamiento del evento "ON_CHANGE", y en el ejemplo CScrollV, el procesamiento de los eventos "ON_SCROLL_INC" y "ON_SCROLL_DEC".


Estructura del objeto CAppDialog

Si iniciamos el asesor "LearnCAppDialog.mq5" en un gráfico vacío, entonces al pulsar "Ctrl"+"B" y clicar en el botón "Todos" podremos ver todos los objetos de los que consta el panel:


El orden de creación y superposición de objetos de la sección Paneles y diálogos de la Biblioteca estándar tiene el aspecto siguiente. Primero creamos el objeto "Border", dentro de sus límites se añade el fondo del panel en forma de objeto "Back", y en el fondo se superpone el área de cliente "ClientBack", dentro de la cual se pueden encontrar los subcontroles. A la zona superior del panel se añaden el objeto "Caption" con el nombre del panel y dos botones de control.

Descomposición del panel AppWindow en controles gráficos

Si representamos el proceso en forma de esquema, estos objetos se crean y organizan en el siguiente orden:

El objeto "Border" es OBJ_RECTANGLE_LABEL, al cual (por defecto para todos los paneles) se le asigna el color blanco para el marco. Es decir, la misión del objeto "Border" es puramente estética: representar un marco blanco, mientras que el cuerpo del objeto "Border", en este caso, quedará oculto por el objeto "Back".



Esquema de herencia de los objetos

A primera vista, en la sección Paneles y ventanas de diálogo hay demasiadas clases con relaciones extensas y estructura de herencia. En realidad, la jerarquía es muy simple, y si entiende en qué consiste el objeto CAppDialog y cómo se crea, entonces no deberían surgir problemas con la comprensión del resto de clases. Aquí tenemos el esquema de herencia de todas las clases de la Biblioteca estándar:


En el asesor "LearnCAppDialog.mq5" el panel AppWindow consta de seis objetos, cada uno de los cuales ejecuta su propia tarea.


El panel basado en CAppDialog puede ser creado tanto a partir de un experto, como a partir de un indicador. En este caso, además, la creación del panel se diferencia dependiendo de qué tipo de programa (experto o indicador) crea el panel y en qué subventana funciona el programa:

  • si el programa es un experto (el tipo de programa iniciado es PROGRAM_EXPERT), entonces el panel se crea SOLO en la ventana principal (número de ventana "0") y solo con la ayuda del método CAppDialog::CreateExpert;
  • si el programa es un indicador (tipo de programa iniciado PROGRAM_INDICATOR), entonces se comprueba el número de la ventana en la que se ha iniciado el programa:
    • si se trata de la ventana principal (número de ventana "0"), entonces el panel se crea con la ayuda del método CAppDialog::CreateIndicator;
    • si se trata de una subventana, el panel se crea con la ayuda del método CAppDialog::CreateExpert.

La peculiaridad del método CAppDialog::CreateIndicator reside en que al crear el panel, automáticamente:

  • se ajusta a la anchura de la ventana;
  • ajusta a sí mismo la altura de la ventana.

Ejemplo del panel-indicador [data folder]\MQL5\Indicators\Examples\Panels\SimplePanel\SimplePanel.mq5 después de crearlo y después de minimizarlo:


"CreateExpert" crea un panel en la ventana principal (número de ventana igual a "0"), y se presupone que el programa que crea el panel es un asesor.

Existe una excepción a estas normas: podemos crear un panel a partir de un indicador en la ventana principal. En este caso, se aplicará el método de creación del panel "CreateIndicator".


Dónde se ubican las constantes básicas para crear objetos y cómo redefinirlos usando #undef

El código se implementará en el asesor "AppWindowEditDefine.mq5".

Las constantes principales del panel y los elementos de control se ubican en el archivo [data folder]\MQL5\Include\Controls\Defines.mqh, que se incluye en la clase CWnd:

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

Recordemos la jerarquía de herencia:

  • CWnd
    • CWndContainer
      • CDialog
        • CAppDialog

Nos interesa especialmente este grupo de constantes:

//+------------------------------------------------------------------+
//| Drawing styles and colors                                        |
//+------------------------------------------------------------------+
//--- common
#define CONTROLS_FONT_NAME                  "Trebuchet MS"
#define CONTROLS_FONT_SIZE                  (10)
//--- Text
#define CONTROLS_COLOR_TEXT                 C'0x3B,0x29,0x28'
#define CONTROLS_COLOR_TEXT_SEL             White
#define CONTROLS_COLOR_BG                   White
#define CONTROLS_COLOR_BG_SEL               C'0x33,0x99,0xFF'
//--- Button
#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'
//--- Label
#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'
//--- Scrolls
#define CONTROLS_SCROLL_COLOR_BG            C'0xEC,0xEC,0xEC'
#define CONTROLS_SCROLL_COLOR_BORDER        C'0xD3,0xD3,0xD3'
//--- Client
#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'
//--- Dialog
#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 modificar esta macrosustitución es mejor aplicar la directiva #undef:

La directiva #undef sirve para cancelar una macrosustitución declarada anteriormente.

De esta forma, se obtiene el siguiente algoritmo: cancelamos la macro declarada anteriormente; declaramos una macro de nuevo, solo que con el parámetro modificado. Para ello, debemos ejecutar el siguiente truco: incluir el archivo "Defines.mqh" ANTES de "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 "Control Panels and Dialogs. Demonstration class CBmpButton"
#include <Controls\Defines.mqh>

después de incluir "Defines.mqh", cancelamos las macros:

#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 

escribimos los 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';

y, lo más interesante: declaramos de nuevo las macros, pero esta vez sustituimos los parámetros de entrada como 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>

Ejemplo de funcionamiento:



Resumen sobre la construcción de CAppDialog

Nuestro panel es un objeto de la clase CAppDialog. Ha heredado de la clase CWndContainer el método ControlsTotal (número de elementos de control en el contenedor). Esto nos da la posibilidad de evitar todos los elementos de control del panel en el ciclo y hacer algo con ellos. Estos elementos se declaran en el área private de la clase padre CDialog:

//+------------------------------------------------------------------+
//| Class CDialog                                                    |
//| Usage: base class to create dialog boxes                         |
//|             and indicator panels                                 |
//+------------------------------------------------------------------+
class CDialog : public CWndContainer
  {
private:
   //--- dependent controls
   CPanel            m_white_border;        // the "white border" object
   CPanel            m_background;          // the background object
   CEdit             m_caption;             // the window title object
   CBmpButton        m_button_close;        // the "Close" button object
   CWndClient        m_client_area;         // the client area object

protected:

Además, si damos una pasada con el depurador, podremos ver cómo se crean estos objetos:

//+------------------------------------------------------------------+
//| Create a control                                                 |
//+------------------------------------------------------------------+
bool CDialog::Create(const long chart,const string name,const int subwin,const int x1,const int y1,const int x2,const int y2)
  {
//--- call method of parent class
   if(!CWndContainer::Create(chart,name,subwin,x1,y1,x2,y2))
      return(false);
//--- create dependent controls
   if(!m_panel_flag && !CreateWhiteBorder())
      return(false);
   if(!CreateBackground())
      return(false);
   if(!CreateCaption())
      return(false);
   if(!CreateButtonClose())
      return(false);
   if(!CreateClientArea())
      return(false);

y, lo más importante, qué nombres se les asignan: m_white_border -> "29437Border", m_background -> "29437Back", m_caption -> "29437Caption", m_button_close -> "29437Close", m_client_area -> "29437Client". En estos nombres, la cifra "29437" es el indentificador del panel para el momento de su vida útil.

De esta forma, es posible cambiar algunas propiedades para los elementos del panel. Por ejemplo, vamos a modificar el color para los objetos "m_client_area" y "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;
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- create application dialog
   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);
      //--- color 
      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);
//--- run application
   AppWindow.Run();
//--- succeed
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//--- destroy dialog
   AppWindow.Destroy(reason);
  }
//+------------------------------------------------------------------+
//| Expert chart event function                                      |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,         // event ID  
                  const long& lparam,   // event parameter of the long type
                  const double& dparam, // event parameter of the double type
                  const string& sparam) // event parameter of the string type
  {
   AppWindow.ChartEvent(id,lparam,dparam,sparam);
  }
//+------------------------------------------------------------------+

Preste atención a la línea: en ella se llama al método CWndContainer::Delete que elimina un elemento del grupo (contenedor). Después de que el elemento "m_client_area" haya sido eliminado del grupo, al desplazar el panel, el comando de desplazamiento no se transmitirá al objeto  "m_client_area". El área de cliente se quedará donde está:


Sin embargo, al cerrar el panel, el elemento "m_client_area" será eliminado del gráfico junto con otros elementos suyos.

Ahora vamos a ver el mismo ejemplo, pero, en lugar del método CWndContainer::Delete, aplicaremos el método CWndContainer::Destroy: eliminaremos el 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;
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- create application dialog
   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);
      //--- color 
      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();
//--- run application
   AppWindow.Run();
//--- succeed
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//--- destroy dialog
   AppWindow.Destroy(reason);
  }
//+------------------------------------------------------------------+
//| Expert chart event function                                      |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,         // event ID  
                  const long& lparam,   // event parameter of the long type
                  const double& dparam, // event parameter of the double type
                  const string& sparam) // event parameter of the string type
  {
   AppWindow.ChartEvent(id,lparam,dparam,sparam);
  }
//+------------------------------------------------------------------+

Este es el aspecto que tiene: 5 segundos de sueño después de crear el panel, tras lo cual el área de cliente es eliminada:



Cómo agregar nuevos controles: dos botones

Modificamos el asesor del apartado "Creamos un panel basado en CAppDialog": añadimos al panel dos botones basados en la clase CButton y lo guardamos con el nombre "AppWindowTwoButtons.mq5". Antes de añadir los botones propiamente dichos (como se hace al proyectar cualquier panel), deberemos imaginar de forma preliminar qué parámetros tendrán botón y dónde se ubicarán. Supongamos que el dibujo de más abajo es el propio panel con botones que queremos construir: 


Aquí:

  • "TOP" — es el margen con respecto al límite superior del área de cliente (la constante "INDENT_TOP" será la responsable de este tamaño);
  • "LEFT" — es el margen con respecto al límite izquierdo del área de cliente (la constante "INDENT_LEFT" será la responsable de este tamaño);
  • "HEIGHT" — es la altura del botón (la constante "BUTTON_HEIGHT" será la responsable de este tamaño);
  • "WIDTH" — es la anchura del botón (la constante "BUTTON_WIDTH" será la responsable de este tamaño).

Asimismo, necesitaremos una constante más: el margen horizontal mínimo entre los elementos de control. Lo llamaremos "CONTROLS_GAP_X". 

Para usar la clase CButton, primero deberemos incluir esta clase:

//+------------------------------------------------------------------+
//|                                          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 "Control Panels and Dialogs. Demonstration class CButton"
#include <Controls\Dialog.mqh>
#include <Controls\Button.mqh>

A continuación, asignaremos las constantes de tamaño y ubicación de los botones:

//+------------------------------------------------------------------+
//| defines                                                          |
//+------------------------------------------------------------------+
//--- indents and gaps
#define INDENT_LEFT                         (11)      // indent from left (with allowance for border width)
#define INDENT_TOP                          (11)      // indent from top (with allowance for border width)
#define CONTROLS_GAP_X                      (5)       // gap by X coordinate
//--- for buttons
#define BUTTON_WIDTH                        (100)     // size by X coordinate
#define BUTTON_HEIGHT                       (20)      // size by Y coordinate
//---

Declaramos a nivel global del programa dos ejemplares de la clase CButton:

#define BUTTON_HEIGHT                       (20)      // size by Y coordinate
//---
CAppDialog           AppWindow;
CButton              m_button1;                       // the button object
CButton              m_button2;                       // the button object
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()

La declaración de botones a nivel global del programa supone un rasgo de mal estilo, ya que estos ejemplares (y, en consecuencia, sus métodos) serán visibles desde cualquier lugar en el sitio del asesor. Pero ahora esto se ha hecho de manera consciente, para reducir el volumen de código

OnInit() no cambiará apenas, se añadirán las llamadas y comprobaciones de los resultados de la creación de los botones:

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- create application dialog
   if(!AppWindow.Create(0,"AppWindow with Two Buttons",0,40,40,380,344))
      return(INIT_FAILED);
//--- create dependent controls
   if(!CreateButton1())
      return(false);
   if(!CreateButton2())
      return(false);
//--- run application
   AppWindow.Run();
//--- succeed
   return(INIT_SUCCEEDED);
  }


Usando el ejemplo "CreateButton1()", podemos detenernos con más detalle en el proceso de creación y VINCULACIÓN del botón al panel.

De la clase CButton usaremos el método "Create", encargado de la creación de botones:


 y el método "Text", con el que ubicamos el rótulo en el botón (además, el método "Text" se hereda de la clase CWndObj) :


En esta etapa se crea el botón, pero existe por separado del panel. Para combinarlos, debemos ejecutar el método CDialog::Add, que sirve para añadir un botón al área de cliente del panel:

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

Y ahora, veamos el código de creación del botón al completo:

//+------------------------------------------------------------------+
//| Create the "Button1" button                                      |
//+------------------------------------------------------------------+
bool CreateButton1(void)
  {
//--- coordinates
   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);
//--- succeed
   return(true);
  }

No olivemos que en OnDeinit() debemos eliminar el panel, y en OnChartEvent(), transmitir todos los eventos al formulario:

//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//---
   Comment("");
//--- destroy dialog
   AppWindow.Destroy(reason);
  }
//+------------------------------------------------------------------+
//| Expert chart event function                                      |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,         // event ID  
                  const long& lparam,   // event parameter of the long type
                  const double& dparam, // event parameter of the double type
                  const string& sparam) // event parameter of the string type
  {
   AppWindow.ChartEvent(id,lparam,dparam,sparam);
  }
//+------------------------------------------------------------------+
//| Create the "Button1" button                                      |
//+------------------------------------------------------------------+

Cómo tiene lugar el desplazamiento de los controles anidados y su dibujado

Recordemos que el panel AppWindow es un objeto de la clase CAppDialog, que es heredera de CDialog. A su vez, CDialog se hereda de CWndContainer:

La clase CWndContainer es la clase básica para el grupo de elementos de control de la Biblioteca estándar.

Es decir, la clase padre CWndContainer controla el desplazamiento de todo el grupo de elementos de control que entra en el panel.

El desplazamiento de todos los elementos de control del panel se realiza en "CWndContainer::Shift" en el ciclo.

//+------------------------------------------------------------------+
//| Relative movement of the controls group                          |
//+------------------------------------------------------------------+
bool CWndContainer::Shift(const int dx,const int dy)
  {
//--- call of the method of the parent class
   if(!CWnd::Shift(dx,dy))
      return(false);
//--- loop by elements of group
   int total=m_controls.Total();
   for(int i=0;i<total;i++)
     {
      CWnd *control=Control(i);
      //--- check of pointer
      if(control==NULL)
         continue;
      //--- move the group item
      control.Shift(dx,dy);
     }
//--- succeed
   return(true);
  }

Usando como ejemplo el panel CBmpButton de la guía de ayuda (en nuestro caso, este ejemplo está en la carpeta \MQL5\Experts\MyExp\Help\With the Panel. EN\ControlsBmpButton.mq5).

Al método "CWndContainer::Shift" llegamos así:



Añadimos un grupo de elementos a CAppDialog utilizando la clase CDialog

Más arriba se ha mostrado un ejemplo de panel con dos botones. ¿Recuerda que dijimos allí que la declaración de los botones a nivel global del programa no es un buen ejemplo? Vamos a analizar un ejemplo "más correcto": ubicaremos el código completo de creación del panel y los botones en la clase-heredero CAppDialog. Ejemplo de creación del panel: "AppWindowTwoButtonsClass.mq5".

La clase "CAppWindowTwoButtons", heredera de CAppDialog, contiene los siguientes métodos:

 Creación
 Create  Crea el principal elemento de control: el panel
 CreateButton1  Crea el elemento de control subordinado: botón #1
 CreateButton2  Crea el elemento de control subordinado: botón #2

Código "AppWindowTwoButtonsClass.mq5": se ha resaltado con color el código que ahora se encuentra en la clase:

//+------------------------------------------------------------------+
//|                                     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 "Control Panels and Dialogs. Demonstration class CButton"
#include <Controls\Dialog.mqh>
#include <Controls\Button.mqh>
//+------------------------------------------------------------------+
//| defines                                                          |
//+------------------------------------------------------------------+
//--- indents and gaps
#define INDENT_LEFT                         (11)      // indent from left (with allowance for border width)
#define INDENT_TOP                          (11)      // indent from top (with allowance for border width)
#define CONTROLS_GAP_X                      (5)       // gap by X coordinate
//--- for buttons
#define BUTTON_WIDTH                        (100)     // size by X coordinate
#define BUTTON_HEIGHT                       (20)      // size by Y coordinate
//---
//+------------------------------------------------------------------+
//| Class CAppWindowTwoButtons                                       |
//| Usage: main dialog of the Controls application                   |
//+------------------------------------------------------------------+
class CAppWindowTwoButtons : public CAppDialog
  {
private:
   CButton           m_button1;                       // the button object
   CButton           m_button2;                       // the button object

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:
   //--- create dependent controls
   bool              CreateButton1(void);
   bool              CreateButton2(void);

  };
//+------------------------------------------------------------------+
//| Constructor                                                      |
//+------------------------------------------------------------------+
CAppWindowTwoButtons::CAppWindowTwoButtons(void)
  {
  }
//+------------------------------------------------------------------+
//| Destructor                                                       |
//+------------------------------------------------------------------+
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);
//--- create dependent controls
   if(!CreateButton1())
      return(false);
   if(!CreateButton2())
      return(false);
//--- succeed
   return(true);
  }
//+------------------------------------------------------------------+
//| Global Variable                                                  |
//+------------------------------------------------------------------+
CAppWindowTwoButtons ExtDialog;
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- create application dialog
   if(!ExtDialog.Create(0,"AppWindowClass with Two Buttons",0,40,40,380,344))
      return(INIT_FAILED);
//--- run application
   ExtDialog.Run();
//--- succeed
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//---
   Comment("");
//--- destroy dialog
   ExtDialog.Destroy(reason);
  }
//+------------------------------------------------------------------+
//| Expert chart event function                                      |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,         // event ID  
                  const long& lparam,   // event parameter of the long type
                  const double& dparam, // event parameter of the double type
                  const string& sparam) // event parameter of the string type
  {
   ExtDialog.ChartEvent(id,lparam,dparam,sparam);
  }
//+------------------------------------------------------------------+
//| Create the "Button1" button                                      |
//+------------------------------------------------------------------+
bool CAppWindowTwoButtons::CreateButton1(void)
  {
//--- coordinates
   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);
//--- succeed
   return(true);
  }
//+------------------------------------------------------------------+
//| Create the "Button2"                                             |
//+------------------------------------------------------------------+
bool CAppWindowTwoButtons::CreateButton2(void)
  {
//--- coordinates
   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);
//--- succeed
   return(true);
  }
//+------------------------------------------------------------------+

Usando el ejemplo "AppWindowTwoButtonsClass.mq5", vamos a analizar el algoritmo de creación del panel y los elementos de control. Todas las acciones tienen lugar en la función CAppWindowTwoButtons::Create.

  • Creamos el propio panel:
   if(!CAppDialog::Create(chart,name,subwin,x1,y1,x2,y2))
      return(false);
  • Creamos los elementos de control subordinados:
//--- create dependent controls
   if(!CreateButton1())
      return(false);
   if(!CreateButton2())
      return(false);
  • Y lo más importante: tras crear un botón, este todavía no es un elemento subordinado de nuestro panel, sino que existe por sí mismo. Para que sea uno de los elementos subordinados del panel, deberemos llamar al método Add (se llama al método CDialog::Add, el elemento de control se añade a la zona de cliente según el índice/enlace)
...
   if(!Add(m_button1))
      return(false);
...
   if(!Add(m_button2))
      return(false);
...

Después de la adición, el elemento de control se convierte en un panel subordinado: todos los eventos ahora se distribuirán de manera centralizada, desde el panel hasta los controles subordinados.

Cómo redefinir el comportamiento de los elementos estándar

Si minimizamos el panel, este se posicionará siempre en las coordenadas (10;10). En este caso, el panel minimizado estará parcialmente superpuesto a los botones de comercio rápido:

Vamos a corregir dicho posicionamiento, teniendo en cuenta al mismo tiempo si se ha expandido el panel de comercio rápido. Para ello, debemos redefinir el método padre CAppDialog::Minimize. Usando como base el código "AppWindowTwoButtons.mq5" del capítulo "Añadimos un grupo de elementos a CAppDialog utilizando la clase CDialog", creamos el ejemplo "AppWindowCorrectMinimization.mq5"

Cambios: declaramos el método "Minimize":

protected:
   //--- create dependent controls
   bool              CreateButton1(void);
   bool              CreateButton2(void);
   //--- override the parent method
   virtual void      Minimize(void);

  };

y escribimos el cuerpo del método:

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void CAppWindowCorrectMinimization::Minimize(void)
  {
//--- variable para obtener el panel de comercio rápido
   long one_click_visible=-1;  // 0 - no hay panel de comercio rápido 
   if(!ChartGetInteger(m_chart_id,CHART_SHOW_ONE_CLICK,0,one_click_visible))
     {
      //--- mostramos el mensaje de error en el registro "Expertos"
      Print(__FUNCTION__+", Error Code = ",GetLastError());
     }
//--- margen mínimo para el panel de aplicación minimizado
   int min_y_indent=28;
   if(one_click_visible)
      min_y_indent=100;  // margen, si el comercio rápido se muestra en el gráfico
//--- obtenemos el margen actual para el panel de aplicación 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;
//--- calculamos el nuevo margen mínimo desde la parte superior para el panel de aplicación minimizado
   if(m_min_rect.top!=min_y_indent)
     {
      m_min_rect.top=min_y_indent;
      //--- desplazamos asimismo el límite inferior del icono minimizado
      m_min_rect.bottom=m_min_rect.top+height;
     }
//--- ahora podemos llamar al método de la clase básica
   CAppDialog::Minimize();
  }


Cómo leer las macros integradas del tipo de procesamiento de eventos

El panel puede procesar los siguientes tipos de eventos (tomado de [date folder]\MQL5\Include\Controls\Defines.mqh" en el bloque "Events")

//+------------------------------------------------------------------+
//| Events                                                           |
//+------------------------------------------------------------------+
#define ON_CLICK                (0)   // clicking on control event
#define ON_DBL_CLICK            (1)   // double clicking on control event
#define ON_SHOW                 (2)   // showing control event
#define ON_HIDE                 (3)   // hiding control event
#define ON_CHANGE               (4)   // changing control event
#define ON_START_EDIT           (5)   // start of editing event
#define ON_END_EDIT             (6)   // end of editing event
#define ON_SCROLL_INC           (7)   // increment of scrollbar event
#define ON_SCROLL_DEC           (8)   // decrement of scrollbar event
#define ON_MOUSE_FOCUS_SET      (9)   // the "mouse cursor entered the control" event
#define ON_MOUSE_FOCUS_KILL     (10)  // the "mouse cursor exited the control" event
#define ON_DRAG_START           (11)  // the "control dragging start" event
#define ON_DRAG_PROCESS         (12)  // the "control is being dragged" event
#define ON_DRAG_END             (13)  // the "control dragging end" event
#define ON_BRING_TO_TOP         (14)  // the "mouse events priority increase" event
#define ON_APP_CLOSE            (100) // "closing the application" event

Estos eventos se procesan en el método CAppDialog::OnEvent. Para facilitar la percepción visual de varios tipos de eventos en [date folder]\MQL5\Include\Controls\Defines.mqh" en el bloque "Macro of event handling map" se describen varias macros:

//+------------------------------------------------------------------+
//| Macro of event handling map                                      |
//+------------------------------------------------------------------+
#define INTERNAL_EVENT                           (-1)
//--- beginning of map
#define EVENT_MAP_BEGIN(class_name)              bool class_name::OnEvent(const int id,const long& lparam,const double& dparam,const string& sparam) {
//--- end of map
#define EVENT_MAP_END(parent_class_name)         return(parent_class_name::OnEvent(id,lparam,dparam,sparam)); }
//--- event handling by numeric ID
#define ON_EVENT(event,control,handler)          if(id==(event+CHARTEVENT_CUSTOM) && lparam==control.Id()) { handler(); return(true); }
//--- event handling by numeric ID by pointer of control
#define ON_EVENT_PTR(event,control,handler)      if(control!=NULL && id==(event+CHARTEVENT_CUSTOM) && lparam==control.Id()) { handler(); return(true); }
//--- event handling without ID analysis
#define ON_NO_ID_EVENT(event,handler)            if(id==(event+CHARTEVENT_CUSTOM)) { return(handler()); }
//--- event handling by row ID
#define ON_NAMED_EVENT(event,control,handler)    if(id==(event+CHARTEVENT_CUSTOM) && sparam==control.Name()) { handler(); return(true); }
//--- handling of indexed event
#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)); }
//--- handling of external event
#define ON_EXTERNAL_EVENT(event,handler)         if(id==(event+CHARTEVENT_CUSTOM)) { handler(lparam,dparam,sparam); return(true); }

Gracias a las macros del bloque "Events" y del bloque "Macro of event handling map", el método OnEvent en el panel adopta el siguiente aspecto: 

//+------------------------------------------------------------------+
//| Event Handling                                                   |
//+------------------------------------------------------------------+
EVENT_MAP_BEGIN(CControlsDialog)
ON_EVENT(ON_CLICK,m_bmpbutton1,OnClickBmpButton1)
ON_EVENT(ON_CLICK,m_bmpbutton2,OnClickBmpButton2)
EVENT_MAP_END(CAppDialog)

Recordemos que este código ha sido tomado de la guía de ayuda CBmpButton, y aquí "CControlsDialog" es un ejemplar de la clase CAppDialog, un panel en forma de clase. 

Teniendo en cuenta las macros del bloque "Macro of event handling map", el método OnEvent será así:

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)); }

y después de aplicar el estabilizador:

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));
  }

El código resultante se puede leer así: si se recibe un evento de clic personalizado en el elemento "m_bmpbutton1", entonces se llamará el método OnClickBmpButton1(). Si se recibe un evento de clic personalizado en el elemento "m_bmpbutton2", se llamará el evento OnClickBmpButton2().

Ejemplo de procesamiento de eventos

Usando como base "AppWindowTwoButtonsClass.mq5", crearemos "AppWindowTwoButtonsClasssEvents.mq5" añadiendo los procesamientos de eventos de clic en un botón.

El primer paso es declarar OnEvent, y también OnClickButton1 y OnClickButton2.

//+------------------------------------------------------------------+
//| Class CControlsDialog                                            |
//| Usage: main dialog of the Controls application                   |
//+------------------------------------------------------------------+
class CAppWindowTwoButtons : public CAppDialog
  {
private:
   CButton           m_button1;                       // the button object
   CButton           m_button2;                       // the button object

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);
   //--- chart event handler
   virtual bool      OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam);

protected:
   //--- create dependent controls
   bool              CreateButton1(void);
   bool              CreateButton2(void);
   //--- handlers of the dependent controls events
   void              OnClickButton1(void);
   void              OnClickButton2(void);

  };

Segundo paso: el método OnEvent, que gracias a las macros del bloque "Events" y del bloque "Macro of event handling map" del archivo [date folder]\MQL5\Include\Controls\Defines.mqh" adopta el aspecto siguiente: 

protected:
   //--- create dependent controls
   bool              CreateButton1(void);
   bool              CreateButton2(void);
   //--- handlers of the dependent controls events
   void              OnClickButton1(void);
   void              OnClickButton2(void);

  };
//+------------------------------------------------------------------+
//| Event Handling                                                   |
//+------------------------------------------------------------------+
EVENT_MAP_BEGIN(CAppWindowTwoButtons)
ON_EVENT(ON_CLICK,m_button1,OnClickButton1)
ON_EVENT(ON_CLICK,m_button2,OnClickButton2)
EVENT_MAP_END(CAppDialog)
//+------------------------------------------------------------------+
//| Constructor                                                      |
//+------------------------------------------------------------------+

Ahora hay que escribir el cuerpo de las funciones OnClickButton1 y OnClickButton2. Imaginemos que el clic sobre el botón 1 es responsable de la apertura de la posición BUY, y el clic sobre el botón 2, del cierre de posición. 

Para ello, en primer lugar, cambiaremos el rótulo sobre los botones (los cambios, naturalmente, suceden en CreateButton1 y CreateButton2):

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

Ahora vamos a decidir qué clases debemos incluir: para comerciar, necesitamos CTrade, para trabajar con las pocisiones, CPositionInfo, y con la ayuda de CAccountInfo, obtendremos el tipo de cuenta comercial:

#property description "Control Panels and Dialogs. Demonstration class CButton"
#include <Controls\Dialog.mqh>
#include <Controls\Button.mqh>
#include <Trade\PositionInfo.mqh>
#include <Trade\Trade.mqh>
#include <Trade\AccountInfo.mqh>
//+------------------------------------------------------------------+
//| defines                                                          |

Para trabajar con estas clases, declararemos en la sección protected de nuestro panel los ejemplares de estas clases:

class CAppWindowTwoButtons : public CAppDialog
  {
protected:
   CPositionInfo     m_position;                      // trade position object
   CTrade            m_trade;                         // trading object
   CAccountInfo      m_account;                       // account info wrapper

private:
   CButton           m_button1;                       // the button object

Métodos de procesamiento de clics:

//+------------------------------------------------------------------+
//| Event handler                                                    |
//+------------------------------------------------------------------+
void CAppWindowTwoButtons::OnClickButton1(void)
  {
   if(m_account.TradeMode()==ACCOUNT_TRADE_MODE_DEMO)
      m_trade.Buy(1.0);
  }
//+------------------------------------------------------------------+
//| Event handler                                                    |
//+------------------------------------------------------------------+
void CAppWindowTwoButtons::OnClickButton2(void)
  {
   if(m_account.TradeMode()==ACCOUNT_TRADE_MODE_DEMO)
      for(int i=PositionsTotal()-1;i>=0;i--) // returns the number of current positions
         if(m_position.SelectByIndex(i)) // selects the position by index for further access to its properties
            if(m_position.Symbol()==Symbol())
               m_trade.PositionClose(m_position.Ticket()); // close a position by the specified symbol
  }

Ahora el panel, al trabajar en una cuenta demo, se convierte en un panel comercial: el clic en el primer botón abre una posición BUY, y el clic en el segundo botón, cierra todas las posiciones.


Intente crear el panel por sí mismo, ¡es sencillo!

En el artículo se ha presentado el esquema general de herencia de las clases de la sección Paneles y ventanas de diálogo. Usando como ejemplo la clase CAppDialog, hemos mostrado cómo crear y administrar cualquier panel gráfico basado en la Biblioteca estándar. Además, hemos analizado cómo se puede obtener acceso a las propiedades de cualquier objeto gráfico de los que consta un panel basado en CAppDialog. Exactamente de la misma forma podrá trabajar con cualquier descendiente de la clase CWnd.

Para comprender rápidamente cómo están construidos los objetos gráficos, en el artículo se muestran varios métodos no estándar para cambiar las propiedades de los controles internos del panel, basados ​​en CAppDialog:

Espero que los ejemplos mostrados ayuden al lector a la hora de crear paneles basados en CAppDialog. Asimismo, recomendamos estudiar los ejemplos de creación de los elementos de control mostrados en el apartado Paneles y ventanas de diálogo.

Nombre del archivo Comentarios
 LearnCAppDialog.mq5  Código mínimo del panel basado en la clase CAppDialog
 AppWindowEditDefine.mq5  Asesor-panel que redefine las constantes de Defines.mqh
 LearnCAppDialog_1.mq5  Cambia el color para los objetos "m_client_area" y "m_background"
 LearnCAppDialog_2.mq5  En lugar del método CWndContainer::Delete, aplicaremos el método CWndContainer::Destroy, eliminación del objeto "m_client_area"
 AppWindowTwoButtons.mq5  Panel con dos botones añadidos
 AppWindowTwoButtonsClass.mq5  Panel con los dos botones añadidos, en forma de clase
 AppWindowCorrectMinimization.mq5  Ejemplo de corrección del posicionamiento del panel por defecto
 AppWindowTwoButtonsClasssEvents.mq5  Panel con dos botones añadidos, en forma de clase. Procesamiento de eventos de los botones


Traducción del ruso hecha por MetaQuotes Software Corp.
Artículo original: https://www.mql5.com/ru/articles/4503

Archivos adjuntos |
MQL5.zip (13.66 KB)
Construimos el indicador Zigzag usando osciladores. Ejemplo de ejecución de la tarea técnica Construimos el indicador Zigzag usando osciladores. Ejemplo de ejecución de la tarea técnica

En este artículo, se demuestra el desarrollo del indicador ZigZag de acuerdo con uno de los ejemplos de la tareas descrito en el artículo «Cómo crear una Tarea Técnica al encargar un indicador». El indicador se construye por los extremos que se definen a través del oscilador. En el indicador está prevista la posibilidad de usar uno de cinco osciladores a elegir: WPR, CCI, Chaikin, RSI, Stochastic Oscillator.

Aplicando el método de Monte Carlo para optimizar estrategias comerciales Aplicando el método de Monte Carlo para optimizar estrategias comerciales

Antes de iniciar un robot en la cuenta comercial, habitualmente lo probamos y optimizamos usando el historial de las cotizaciones. Pues, aquí surge una pregunta razonable, ¿cómo nos pueden ayudar los resultados anteriores en el historial en el futuro? En este artículo, se muestra la aplicación del método de Monte Carlo para construir sus propios criterios de optimización de las estrategias comerciales. Aparte de eso, se consideran los criterios de la estabilidad del Asesor Experto.

Simulación de patrones de parejas de divisas: Uso y perspectivas para el trading real. Parte IV Simulación de patrones de parejas de divisas: Uso y perspectivas para el trading real. Parte IV

Con este artículo terminamos la serie sobre el trading con las cestas de parejas de divisas. En este artículo, vamos a simular el último patrón y discutir sobre el uso de la metodología completa en el trading real. Han sido consideradas la entrada y la salida del mercado, la búsqueda y el análisis de los patrones, y la aplicación de los indicadores combinados.

Neuroredes profundas (Parte VI). Conjunto de clasificadores de redes neuronales: bagging Neuroredes profundas (Parte VI). Conjunto de clasificadores de redes neuronales: bagging

Vamos a ver los métodos de construcción y entrenamiento de conjuntos de redes neuronales con la estructura bagging. También vamos a definir las peculiaridades de la optimización de los hiperparámetros de los clasificadores de redes neuronales individuales que componen el conjunto. Asimismo, compararemos la calidad de la red neuronal optimizada obtenida en el artículo anterior de la serie, y el conjunto creado de redes neuronales. Para finalizar, analizaremos las diferentes opciones para mejorar aún más la calidad de clasificación del conjunto.