Controles gráficos personalizados. Parte 3. Formularios

Dmitry Fedoseev | 24 diciembre, 2013

 

Introducción

El primer artículo "Creando un control simple" trata sobre los principios para la creación de un control gráfico y constituye un ejemplo donde se describe paso a paso la creación de un control simple. El siguiente artículo "la librería de control" expone un conjunto de controles ya diseñados. Este es otro componente muy importante de la interfaz gráfica, el formulario.

El formulario representa una parte especial de la pantalla en la que se muestran los controles. Además, el formulario es también un contenedor que nos permite gestionar todos los controles simultáneamente para ocultarlos, mostrarlos y moverlos todos a la vez.

El último artículo trata sobre la creación de formularios y su uso junto con los controles.

Las clases para el trabajo con formularios se han añadido al archivo IncGUI.mqh que hemos usado en artículos anteriores (su nuevo nombre es IncGUI_v3.mqh). Además de las clases para el trabajo con formularios, se han añadido algunas clases más al archivo, y las clases CHMenu (Menú horizontal) y CVMenu (Menú vertical) se han actualizado y se han resuelto algunos errores.

Clases añadidas:

Como resultado de añadir nuevas clases, se ha modificado la clase CColorSchemes al haberse añadido colores para el nuevo control.

Cambios en las clases CHmenu y CVMenu: los cambios aplicados permiten crear menús de dos niveles con menús desplegables. Esto será tratado con mayor detalle en la sección "Creando el menú principal" más adelante.

Errores: una corrección en el método Frame() de la clase CWorkPiece, ya que no se podría establecer un número de subventana para una etiqueta rectangular. Clase CListMS - Métodos Selected() y SetSelected() - ahora, se ha comprobado el tamaño de la matriz, ya que de otra forma hubiera sido imposible usar una lista vacía.

 

1. Formularios

El formulario está basado en los objetos gráficos "etiqueta rectangular" OBJ_RECTANGLE_LABEL con el uso de varios botones OBJ_BUTTON. Visualmente, el formulario representa un rectángulo (Fig.1) con una barra en su parte superior donde se muestran el nombre del formulario y los botones del control.

Un botón para mover el formulario (con la apariencia de una mano) se encuentra en la parte izquierda, y un botón de minimizado (con la imagen de un rectángulo) junto con un botón para el cierre (con apariencia de una cruz) se encuentran situados a la derecha.

Fig. 1. Formulario

Fig. 1. Formulario

Para mover el formulario, presione el botón mover (el botón se cambiará a la posición de arrastre) y haga clic en cualquier lugar del gráfico al que desee mover el formulario. Como resultado, se liberará el botón mover y podrá desplazar el formulario al lugar deseado (podrá situar su esquina superior derecha en el lugar donde haga clic).

Podrá ajustar la posición del formulario en el lugar donde éste debe situarse en el exterior del borde del gráfico, de forma que el formulario sea completamente visible en el gráfico.

Es posible crear varios tipos de formularios:

Fig. 2. Formulario de tipo 1 (con botones "Cancelar" y "Aplicar")

Fig. 2. Formulario de tipo 1 (con botones "Cancelar" y "Aplicar")

Fig. 3. Formulario de tipo 2 (con el botón "Cerrar")

Fig. 3. Formulario de tipo 2 (con el botón "Cerrar")

Los recursos de programación para el trabajo con formularios están representados por dos clases: CFormBase y CFormTemplate.

La clase CFormBase es una clase básica mientras que CFormTemplate es una subclase de CFormBase. En lo que respecta al efecto y los principios (incluyendo el principio de aplicación), las clase CFormBase es exactamente el mismo control que hemos visto previamente.

La clase CFormBase dispone del método Init() para la preparación del control, de los métodos SetPos() y Show() para mostrar en pantalla un formulario (creación de objetos gráficos que representan un formulario), el método Hide() para ocultar, el método Refresh() para actualizar la representación en pantalla, el método Event() para la gestión de eventos y otros métodos, tal y como ocurre con los demás controles.

La aplicación de un formulario y el uso de controles comienza con la llamada al método Init(), seguida por el establecimiento de una posición usando el método SetPost() y habilitando la representación en pantalla a través del método Show() o bien utilizando el método Show() con parámetros. Finalmente, puede ocultarse un formulario a través del método Hide().

Se añade una llamada del método Event() a la función OnChartEvent() para asegurar la posibilidad de control del formulario (movimiento, minimizado, cierre, etc.). Cuando se requiere asegurar la operación del formulario en una subventana se usa el método SetSubWindow() que es llamado justo después de llamar al método Init() así como en la función OnChartEvent() sobre el evento CHARTEVENT_CHART_CHANGE. Todo es igual que en otros controles.

Mientras que los controles estándar son independientes (no tienen que asociarse con otros controles) el formulario implica la existencia de una asociación obligatoria con otros controles para asegurar la representación/ocultación simultánea de los controles situados en el formulario con la representación/ocultación del formulario mismo. De esta forma, debe asegurarse un operación sincronizada de los métodos Init(), Show() y Hide() del formulario y los controles relacionados.

Se podría llamar (p. ej, llamando al método Show() del formulario) a los métodos Show() de todos los controles relativos al formulario, pero cuando se usan varios formularios en el programa tal enfoque puede dar como resultado un código desestructurado y poco adecuado para su comprensión, compilado y actualización.

Se puede hacer una copia de la clase formulario para cada formulario y añadir código con controles ya incluidos. Sin embargo esto también dará como resultado un código desestructurado (donde el código del formulario no estará separado del código relacionado con la operación del control) y una excesiva duplicación del código donde solo será duplicada la parte del código encargada del diseño y funcionalidad del formulario. Todo ello va a complicar la realización de modificaciones a cualquier función del formulario ya que requerirá que se realicen cambios en cada copia de la clase.

La división en una clase básica y una subclase ayuda a resolver el problema de crear varios formularios, a la sincronización de la representación en pantalla del formulario y controles relacionados, así como a una clara separación del código relacionado con los diferentes controles. Además de resolver los problemas básicos, el uso de clases y subclases permitirá la actualización futura de la clase básica sin tener que realizar ningún cambio en el trabajo que ya se haya realizado.

Para sincronizar las llamadas a los métodos Init(), Show() y Hide() a los controles y al formulario, la clase CFormBase presenta algunas diferencias con respecto a la clase de control estándar: algunos métodos virtuales en una sección protegida:

virtual void MainProperties() {}
virtual void OnInitEvent() {}
virtual void OnShowEvent() {}
virtual void OnHideEvent() {}
virtual void EventsHandler(const int id, const long& lparam, const double& dparam, const string& sparam){}
virtual void OnWindowChangeEvent(int aSubWindow) {}
virtual bool OnCancelEvent() {return(true);}
virtual bool OnApplyEvent()  {return(true);} 

Estos métodos son de tipo virtual, lo que significa que a través de ellos se realiza la redirección hacia los correspondientes métodos reales de la subclase CFormTemplate.

La subclase CFormTemplate no es usada como tal, sino que es una plantilla. Para crear un nuevo formulario, haga una copia de la clase CFormTemplate con un único nombre (copie el código de la clase CFormTemplate, renómbrelo y repita lo mismo para cada formulario). El copiado de subclases permite la separación del código correspondiente a las distintas clases.

Los métodos virtual se encuentran en la sección protegida y por tanto serán inaccesibles a una llamada, algo que no es necesario ya que los métodos son llamados ya por la clase básica en distintos eventos de formulario. Solo debe añadir el código correspondiente a estos eventos para el funcionamiento de los controles.

El gráfico a continuación muestra la interacción entre las funciones del asesor experto y los métodos de clase de control estándar (Fig. 4) y como comparación la interacción con los métodos de clase del formulario (Fig. 5).

Fig. 4. La interacción entre las funciones del Expert Advisor y los métodos del control
Fig. 4. La interacción entre las funciones del asesor experto y los métodos del control

Fig. 5. La interacción entre las funciones del Expert Advisor y los métodos de la clase del formulario
Fig. 5. La interacción entre las funciones del asesor experto y los métodos de la clase del formulario

Vamos a revisar la finalidad de cada método en detalle:

 

2. Un procedimiento paso a paso para crear un nuevo formulario

A continuación se muestra un procedimiento paso a paso para crear un nuevo formulario:

1. Include IncGUI_v3.mqh file.

#include <IncGUI_v3.mqh>
2. Copie el código de la clase CFormTemplate del archivo IncGUI_v3.mqh a un archivo de su asesor experto (la subclase está al final del archivo) y renómbrelo (en el ejemplo de la librería use la subclase llamada CForm). 

 

class CFormTemplate: public CFormBase{
   public:
   protegido      void MainProperties()
     {
        m_Name        = "Form";        // Form name. The names of all the controls of this form should start with it.
        m_Width       = 200;           // Form width
        m_Height      = 150;           // Form height
        m_Type        = 1;             // Form type: 0 - without buttons, 1 - with "Apply" and "Cancel" buttons, 2 - with the "Close" button
        m_Caption     = "FormCaption"; // Form caption
        m_Movable     = true;          // Movable form (the button with a hand image is displayed in the top left corner)
        m_Resizable   =  true;         // Form minimizing/maximizing allowed (the button with a rectangle image
                                       // is displayed in the top right corner)
        m_CloseButton = true;          // Form closing allowed (the button with a cross image is displayed in the top right corner)
     }
      void OnInitEvent() {} 
      void OnShowEvent(int aLeft, int aTop) {}
      void OnHideEvent() {}
      void OnWindowChangeEvent(int aSubWindow) {}
      void EventsHandler(const int id,const long& lparam,const double& dparam,const string& sparam){}
      bool OnApplyEvent()  {return(true);}
      bool OnCancelEvent() {return(true);}
};

Es posible crear un archivo include para el código del formulario, incluirlo en el asesor experto, copiar el código de la subclase CFormTemplate en él y realizar el resto del código para el funcionamiento del formulario en este archivo.

De forma análoga al uso de un editor visual, este paso sería muy similar a hacer clic en el botón para crear un nuevo formulario al que le seguiría la aparición de un nuevo archivo de formulario con funciones de varios eventos de formulario en el proyecto.

3. Establecer las propiedades principales del formulario. Este paso se ejecuta en el método MainProperties() asignando valores a las variables declaradas en la clase básica. En la plantilla se suministran comentarios detallados sobre la finalidad de todas las variables. Puede apreciar la similitud de este paso con el uso de la ventana de propiedades del formulario en un editor visual (por ejemplo, VBA en Excel).

void MainProperties()
{
   m_Name         =  "Form";        // Form name. The names of all the controls of this form should start with this one.
   m_Width        =  200;           // Form width
   m_Height       =  150;           // Form height
   m_Type         =  1;             // Form type: 0 - without buttons, 1 - with "Apply" and "Cancel" buttons, 2 - with the "Close" button 
   m_Caption      =  "FormCaption"; // Form caption
   m_Movable      =  true;          // Movable form (the button with a hand image is displayed in the top left corner)
   m_Resizable    =  true;          // Form minimizing/maximizing allowed (the button with a rectangle image
                                    // is displayed in the top right corner)
   m_CloseButton = true;            // Form closing allowed (the button with a cross image is displayed in the top right corner)
}

El formulario presenta otra diferencia con respecto a los demás controles ya que su nombre se establece en el método MainProperties() en lugar de transmitirse como parámetro del método Init().

La función principal del formulario se realiza en una subclase. Cada subclase del formulario solo se declara una vez por lo que no es necesario especificar nombres diferentes al llamar al método Init() de una subclase del formulario.

El único parámetro que puede enviarse al método Init() es el parámetro State, pero incluso este no es obligatorio. El valor del parámetro State determina el estado inicial del formulario cuando se muestra en pantalla: maximizado o minimizado. Si el valor es 1, el formulario es maximizado (Fig. 1, 2, 3) y si el valor es 2 el formulario es minimizado (Fig.6)

Fig. 6. Formulario minimizado

Fig. 6. Formulario minimizado

4. Declara una clase

Formulario CForm;

5. Siga los pasos habituales para usar el control: llame al método Init() del formulario desde la función Oninit() del asesor experto, llame al método Deinit() de la función OnDeinit() y llame, cuando sea necesario, al método SetSubWindow() en el evento CHARTEVENT_CHART_CHANGE.

Posteriormente, el formulario debe ser visible en el gráfico y responder a los clics de ratón para moverse, minimizarse, botones de cierre, etc.

A continuación procedemos a añadir los controles al formulario.

 

3. Un procedimiento paso a paso para añadir los controles al formulario.

  1. Declarando los controles. Si necesita garantizar el acceso a los métodos del control fuera del formulario, las clases del control deben declararse en la sección pública. Si se puede realizar todo el trabajo dentro de la clase del formulario, las clases del control deben declararse en la sección protegida.

  2. Declarando las variables para recuperar los valores del control. Un paso opcional. Si va a usar el formulario con los botones "Cancelar" y "Aplicar", debe añadirse una variable a cada control para guardar el valor que este tenía al abrir el formulario, y así recuperar el valor si un usuario cambia el valor del control pero pulsa el botón "Cancelar".

  3. Llamando a los métodos Init() de todos los controles. Se ejecuta en el método OnInitEvent().

  4. Cargando los datos guardados. Un paso opcional. Se ejecuta en el método OnInitEvent(). Este paso se utiliza cuando es preciso que los controles conserven sus valores después de reiniciar el asesor experto, el terminal o el ordenador. El guardado de los datos se realiza en el paso 12.

  5. Estableciendo los parámetros por defecto. En este paso, todos los controles se han establecido a valores que podrán observarse en los controles cuando se produzca la primera apertura del formulario tras iniciar el asesor experto (hasta que sus valores sean cambiados por un usuario). Si los datos se cargaron en el paso 4, se usan los datos cargados. La preparación posterior de los controles se realiza también en este paso, por ejemplo, los elementos de menú y de las listas también se añaden aquí.

  6. La llamada a los métodos Show() (versión con parámetros) para todos los controles es ejecutada en el método OnShowEvent(). El método OnShowEvent() tiene dos parámetros (int aLeft, int aTop) que que envía las coordenadas de la esquina superior izquierda del espacio de trabajo al formulario. Las posiciones del control se establecen en función de los valores de estas variables (se deben añadir los valores).

  7. En el método OnHideEvent() se produce una llamada del método Hide() a todos los controles del formulario.

  8. Llamar al método SetSubWindow(). Un paso opcional. Si es necesario mostrar en pantalla el formulario en un subventana, se llama al método SetSubWindow() en todos los controles. Se ejecuta en el método OnWindowChangeEvent(), que tiene un parámetro (int aSubWindow) que envía un nuevo número de subventana.

  9. Llamar a los métodos Event() de todos los controles. Esto se ejecuta en el método EventsHandler().

  10. Gestionando los eventos del control. Un paso opcional. Si se requiere garantizar una interacción entre los controles y sus eventos, es en este paso donde los eventos son gestionados y donde se realizan las acciones apropiadas. Esto se ejecuta en el método EventsHandler().

  11. Aplicando nuevos valores. Este es un paso opcional que se ejecuta mediante el botón "Aplicar". Se ejecuta en el método OnApplyEvent(). En este paso, se comprueba la exactitud de los valores del control. Si se encuentra algún valor incorrecto, se debe notificar al usuario, el método debe devolver un valor falso y el formulario debe permanecer abierto. Si los valores son correctos, debe devolverse un valor verdadero para poder cerrar el formulario.

  12. Guardado de los datos. Un paso opcional. Se ejecuta en el método OnApplyEvent() a través del botón "Aplicar". Los datos pueden guardarse (dependiendo de los objetivos y la finalidad) en un archivo, en variables globales, objetos invisibles en el gráfico, etc.

  13. Comprobación en el método OnCancelEvent(). Un paso opcional. Si se devuelve falso en el método OnCancelEvent(), el formulario permanecerá abierto, es decir, el botón cerrar no responderá. Para que el formulario se cierre, el método debe devolver un valor verdadero.

Puede ver un ejemplo del trabajo con formularios en el archivo eIncGUI_v3_Test_Form.mq5. El formulario principal con diferentes controles se abre al inicio (Fig. 7).

Observe que el formulario no tiene un botón de cierre. Este es el formulario principal y no debe desaparecer del gráfico, ya que de lo contrario el asesor experto tendrá que iniciarse de nuevo para que el formulario vuelva a aparecer.

Fig. 7. Formulario principal del ejemplo eIncGUI_v3_Test_Form.mq5 con una pestaña de menú principal abierto.

Fig. 7. Formulario principal del ejemplo eIncGUI_v3_Test_Form.mq5 con una pestaña de menú principal abierto.

Usando los comandos del menú principal podemos abrir las otras dos variantes del formulario.

Menú principal - Tipos de formularios - El tipo 1 abre un formulario del tipo 1 (con los botones "Cancelar" y "Aplicar").
Menú principal - Tipos de formularios - El tipo 2 abre un formulario del tipo 2 (con los el botón "Cerrar").

Ambos formularios requieren una confirmación al cerrarse (lo que se realiza usando la función MessageBox()). El formulario de tipo 1 tiene un cuadro de entrada y se comprueba un valor de entrada haciendo clic en el botón "Aplicar". El formulario de tipo 2 tiene una nueva etiqueta del control (la clase CLable) y algunos botones (la clase CButton) para hacer pruebas.

Ahora, como hemos mencionado en la introducción, vamos a revisar los cambios en las clases CHMenu y CVMenu como hemos visto al crear el menú principal.

 

En primer lugar, me gustaría decir que solo se pueden crear menús de dos niveles: una barra horizontal (basada en la clase de CHMenu) y pestañas (basadas en la clase CVMenu). En principio, es posible crear aun más niveles pero el proceso sería muy complicado y laborioso, por lo que no se recomienda y no lo vamos a considerar en adelante.

De hecho, la creación de un menú principal para el formulario ni siquiera estaba pensado, y se suponía que las clases CHMenu y CVMenu debían usarse de forma individual e independientemente la una de la otra, para habilitar/deshabilitar varias funciones e instrumentos. Sin embargo, una ligera mejora de las clases CHMenu y CVMenu permite obtener el menú principal adecuado para la mayoría de los casos.

Para crear un completo de menú multinivel, debe usarse un enfoque algo diferente (la creación de una estructura de datos en forma de árbol) que puede ser tema de otro artículo, por lo que nos limitaremos aquí a los medios existentes.

Primero vamos a revisar todos los cambios y correcciones aplicadas a las clases CHMenu y CVMenu.

En la clase CVMenu, se ha fijado una respuesta al clic sobre el objeto gráfico "Label", que es usada para mostrar en pantalla el tick del símbolo. Como consecuencia, se han producido cambios en el funcionamiento de los métodos LastClickedX(), LastClickedY() y LastClickedQuarter(). Al hacer clic, los valores de las etiquetas son ahora devueltos como ocurre con los cuadros de texto. Se han realizado correcciones similares en la clase CHMenu y en los métodos LastClickedX(), LastClickedY(), LastClickedQuarter() y LastClickedW().

Ambas clases han sido actualizadas añadiendo los métodos SolvePosX() y SolvePosY(), que tienen por finalidad calcular las coordenadas de un objeto gráfico mostrado en el comando de menú. La anchura del objeto mostrado en pantalla se transmite al método SolvePosX() y la altura del mismo se envía al método SolvePosY() que devuelve la coordenada X o Y del objeto mostrado, respectivamente. Ahora ya no es necesario usar los métodos LastClickedX(), LastClickedY(), LastClickedQuarter() y LastClickedW().

Ambas clases han sido actualizadas añadiendo los métodos LastClickedName1() y LastClickedName2(). Los métodos devuelven los nombres de los objetos gráficos que forman el último elemento de menú sobre el que se ha hecho clic. LastClickedName1() devuelve el nombre del cuadro de texto y LastClickedName2() devuelve el nombre de la etiqueta para el símbolo del tick.

Los métodos ToggleNameAdd() y ToggleNamesClear() se han añadido a CVMenu. El método ToggleNameAdd() se usa para crear una lista de nombres de "cambio" y ToggleNamesClear() se usa para vaciar esta lista. Al usar el método ToggleNameAdd() (añadiendo un nombre a la lista), el menú se cerrará automáticamente con cualquier evento del gráfico, excepto para aquellos eventos de los objetos gráficos que forman el menú y los objetos gráficos cuyos nombres se añadieron al método ToggleNameAdd(). El menú vuelve a su modo de funcionamiento normal cuando la lista es vaciada por el método ToggleNamesClear().

El funcionamiento del menú de dos niveles es el siguiente: cuando el evento de hacer clic en el menú horizontal se define por los métodos LastClickedName1() y LastClickedName2(), obtenemos los nombres de los objetos de este elemento que posteriormente son trasmitidos al método ToggleNameAdd() del menú vertical.

Según esto, se ocultará el menú vertical al ocurrir cualquier evento del gráfico, excepto aquellos eventos del menú vertical y un elemento del menú horizontal (a través del cual se abrió el menú vertical).

Si un usuario elige un elemento del menú vertical, este menú debe cerrarse y se deben ejecutar las acciones correspondientes a este elemento. Si un usuario hace clic repetidamente en el mismo elemento del menú horizontal (a través del cual se abrió el menú vertical), el menú vertical debe ocultarse.

Vamos a ver un ejemplo de creación del menú.

El menú principal tiene tres elementos y, por tanto, necesitamos tres menús verticales. Declare una clase de menú horizontal y tres clases de menú vertical en la sección pública de la subclase del formulario (el ejemplo siguiente muestra esto exactamente de la misma forma que en el ejemplo de eIncGUI_v3_Test_Form.mq5):

class CForm: public CFormBase{
public:
   CHMenu m_hm;
   CVMenu m_vm1;
   CVMenu m_vm2;
   CVMenu m_vm3; 

Inicializa las clases de menú en el método OnInitEvent() y añade elementos de menú:

// Menú horizontal 
m_hm.Init(m_Name+"_HM",m_Width,2);
// Añadiendo elementos del menú horizontal
m_hm.AddItem("Form types");
m_hm.AddItem("Item-2");
m_hm.AddItem("Item-3");
// Menú vertical 1
m_vm1.Init(m_Name+"_VM1",70,10); 
// Añadiendo elementos al menú vertical 1
m_vm1.AddItem("Type-1");
m_vm1.AddItem("Type-2");
// Menú vertical 2
m_vm2.Init(m_Name+"_VM2",70,3);
// Añadiendo elementos al menú 2
m_vm2.AddItem("Item-2-1");
m_vm2.AddItem("Item-2-2");
m_vm2.AddItem("Item-2-3");
m_vm2.AddItem("Item-2-4");
m_vm2.AddItem("Item-2-5"); 
// Menú vertical 3
m_vm3.Init(m_Name+"_VM3",70,3);
// Añadiendo elementos al menú verrtical 3
m_vm3.AddItem("Item-3-1");
m_vm3.AddItem("Item-3-2");
m_vm3.AddItem("Item-3-3");
m_vm3.AddItem("Item-3-4");
m_vm3.AddItem("Item-3-5"); 

Oculta el menú en el método OnHideEvent():

void OnHideEvent()
{
   m_hm.Hide(); 
   m_vm1.Hide();
   m_vm2.Hide(); 
   m_vm3.Hide(); 
}

Muestra en pantalla el menú en el método OnShowEvent():

void OnShowEvent(int aLeft,int aTop)
{
    m_hm.Show(aLeft,aTop); 
}

Finalmente, el trabajo principal en el método EventsHandler().

Llamar a los métodos Event() de todos los menús:

void EventsHandler(const int id,const long& lparam,const double& dparam,const string& sparam)
{
    int m_event0=m_hm.Event(id,lparam,dparam,sparam);
    int m_event1=m_vm1.Event(id,lparam,dparam,sparam);
    int m_event2=m_vm2.Event(id,lparam,dparam,sparam);
    int m_event3=m_vm3.Event(id,lparam,dparam,sparam);

Dependiendo del valor de m_event0 (índice del elemento del menú horizontal), trabaja con el menú vertical adecuado (p. ej., elemento 0 y menú vertical 1):

if(m_event0==0)
{ // Clicking item 0
   if(m_vm1.Visible())
   { 
      m_vm1.Hide(); // If the vertical menu is open, close it
   }
   else{ // If the vertical menu is closed
      m_vm1.ToggleNamesClear(); // Clear the list of "toggle" names
      // Add to the list the names of the graphical objects forming the item  
      // of the horizontal menu which led to the last event 
      // of the horizontal menu 
      m_vm1.ToggleNameAdd(m_hm.LastClickedName1()); 
      m_vm1.ToggleNameAdd(m_hm.LastClickedName2());
      // Display the vertical menu
      m_vm1.Show(m_hm.SolvePosLeft(m_vm1.Width()),m_hm.SolvePosTop(m_vm1.Height())); 
   }
}

Estas acciones pueden realizarse para elementos del menú horizontal abriendo el menú vertical. Si ha ocurrido el evento del menú vertical, cierra el menú.

Dependiendo del índice del elemento en el que se hace clic, proceda como se muestra a continuación:

if(m_event1>=0)
{ // Vertical menu event 1
   m_vm1.Hide(); // Hide the menu
      if(m_event1==0)
      { // Clicking item 0
        //...
      }
      if(m_event1==1)
      { // Clicking item 1
        //...
      }
}

Estas acciones pueden realizarse para todos los menús verticales y todos sus elementos.

Ya hemos completado la creación de un menú vertical.

 

Conclusión

Este artículo es el último de una serie de artículos dedicados a la creación de una interfaz gráfica.

Como resultado de nuestro trabajo, tenemos ya un gran arsenal de medios para crear rápidamente una interfaz gráfica multifuncional y fácil de usar. Esto es muy coherente con los requisitos de una interfaz gráfica:

Todos los controles y el formulario tienen un diseño y esquema de color común que puede cambiarse rápidamente.

Vamos a repetir el procedimiento para crear un formulario.

Un procedimiento breve para crear un formulario:

  1. Include IncGUI_v3.mqh file.

  2. Hacer una copia de la clase CFormTemplate con un único nombre y declarar esta clase.

  3. Establecer las propiedades del formulario en el método MainProperties() de la copia de la subclase.

  4. Llamar a los métodos Init(), Hide() e Event() del formulario desde las funciones OnInit(), OnDeinit() y OnChartEvent() del asesor experto, respectivamente. Llamar al método Show() desde la función OnInit() del asesor experto o cuando sea necesario.

  5. Trabajar con los controles. Declarar las clases del control en la copia de la subclase. Llamar a los métodos Init(), Show(), Hide() y Event() de los controles desde los métodos OnInitEvent(), OnShowEvent(), OnHideEvent() y EventHandler() de la copia de la subclase.

 

Apéndice

Lista de archivos adjuntos: