![Cómo crear un panel gráfico de cualquier nivel de complejidad](https://c.mql5.com/2/68/graphic-panel_600x314.jpg)
Cómo crear un panel gráfico de cualquier nivel de complejidad
Contenido
- Introducción
- Creamos un panel basado en CAppDialog
- Qué sabe hacer AppWindow
- Estructura del objeto CAppDialog
- Esquema de herencia de los objetos
- Dónde se ubican las constantes básicas para crear objetos y cómo redefinirlos usando #undef
- Cómo agregar nuevos controles: dos botones
- Cómo tiene lugar el desplazamiento de los controles anidados y su dibujado
- Añadimos un grupo de elementos a CAppDialog utilizando la clase CDialog
- Cómo redefinir el comportamiento de los elementos estándar
- Cómo leer las macros integradas del tipo de procesamiento de eventos
- Intente crear el panel por sí mismo, ¡es sencillo!
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:
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.
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:
- Dónde se ubican las constantes básicas para crear objetos y cómo redefinirlos usando #undef
- Resumen sobre la construcción de 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 Ltd.
Artículo original: https://www.mql5.com/ru/articles/4503
![Enrique Enguix Enrique Enguix](https://c.mql5.com/avatar/2024/8/66d331d4-3aa9.png)
![Simulación de patrones de parejas de divisas: Uso y perspectivas para el trading real. Parte IV](https://c.mql5.com/2/31/LOGO.png)
![Construimos el indicador Zigzag usando osciladores. Ejemplo de ejecución de la tarea técnica](https://c.mql5.com/2/31/Avatar_ZigZag__1.png)
![Neuroredes profundas (Parte VI). Conjunto de clasificadores de redes neuronales: bagging](https://c.mql5.com/2/48/Deep_Neural_Networks_06.png)
![Aplicando el método de Monte Carlo para optimizar estrategias comerciales](https://c.mql5.com/2/32/Monte_Carlo.png)
![MQL5 - Lenguaje de estrategias comerciales para el terminal de cliente MetaTrader 5](https://c.mql5.com/i/registerlandings/logo-2.png)
- Aplicaciones de trading gratuitas
- 8 000+ señales para copiar
- Noticias económicas para analizar los mercados financieros
Usted acepta la política del sitio web y las condiciones de uso