English Русский 中文 Deutsch 日本語 Português
Interfaces gráficas VI: Controles "Casilla de verificación", "Campo de edición" y sus tipos combinados (Capítulo 1)

Interfaces gráficas VI: Controles "Casilla de verificación", "Campo de edición" y sus tipos combinados (Capítulo 1)

MetaTrader 5Ejemplos | 27 junio 2016, 17:12
1 041 0
Anatoli Kazharski
Anatoli Kazharski

Índice



Introducción

El primer artículo de la serie nos cuenta con más detalles para qué sirve esta librería: Interfaces gráficas I: Preparación de la estructura de la librería (Capítulo 1). Al final de los artículos de cada parte se puede encontrar la lista de los capítulos con los enlaces, así como descargar la versión completa de la librería en la fase actual del desarrollo del proyecto. Es necesario colocar los ficheros en los mismos directorios, tal como están ubicados en el archivo.

La sexta parte de la serie  va a incluir dos capítulos. En la primera crearemos cuatro controles:

  • Casilla de verificación (checkbox)
  • Campo de edición
  • Campo de edición con checkbox
  • Lista combinada (combobox) con casilla de verificación (checkbox)

En este artículo, de estos cuatro controles nosotros vamos a describir sólo “casilla de verificación” y “campo de edición”, porque “campo de edición con checkbox” y “combobox con checkbox” no van a contener nada nuevo de lo que no hemos hablado antes. 

En el segundo capítulo hablaremos de los siguientes controles:

  • Slider
  • Slider doble

 


Control “Casilla de verificación”

El control “Casilla de verificación” o “Checkbox” está destinado para manejar los parámetros que pueden tener sólo dos estados. El botón con dos iconos se utiliza para identificar el estado actual del parámetro con el que está vinculado el control. El icono con el signo “marca de verificación” significa el estado “activado” (on). El icono sin el signo “marca de verificación” significa el estado “desactivado” (off). Junto con el botón se encuentra la descripción breve del parámetro. 

Vamos a utilizar tres objetos gráficos para diseñar este control. Son los siguientes:

  1. Fondo
  2. Icono (botón)
  3. Etiqueta de texto


Fig. 1. Partes integrantes del control “Casilla de verificación”.

Vamos a ver cómo está organizada la clase de este control.

 


Desarrollo de la clase para la creación del control “Casilla de verificación”

En la tercera parte de esta serie ya hemos considerado un control parecido: “Botón con imagen”, clase CIconButton  (véase el artículo Interfaces gráficas III: Botones simples y multifuncionales (Capítulo 1). El control “Checkbox” parece al botón con imagen en el modo con dos estados. La única diferencia es que el botón en diferentes estados (on/off) tiene el color del fondo y del texto diferente (si está establecido), y en caso del checkbox, se cambia sólo el color de la imagen y del texto (si está establecido). E color del fondo del checkbox suele ser el mismo que tiene el fondo de la ventana a la que se adjunta el control. Aquí el fondo va a utilizarse como el área para identificar la posición del cursor respecto a los bordes de esta área, así como para identificar el clic izquierdo en el control. Al mismo tiempo, el control “checkbox” es incluso más sencillo que el control “botón con imagen” porque posee menos propiedades que puede establecer el usuario.

Creamos el archivo CheckBox.mqh y lo incluimos en la librería (archivo WndContainer.mqh):

//+------------------------------------------------------------------+
//|                                                 WndContainer.mqh |
//|                        Copyright 2015, MetaQuotes Software Corp. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
#include "CheckBox.mqh"

Creamos la clase CCheckBox con los métodos estándar para todos los controles de la librería en el archivo CheckBox.mqh:

//+------------------------------------------------------------------+
//|                                                     CheckBox.mqh |
//|                        Copyright 2015, MetaQuotes Software Corp. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
#include "Element.mqh"
#include "Window.mqh"
//+------------------------------------------------------------------+
//| Clase para crear checkbox                                    |
//+------------------------------------------------------------------+
class CCheckBox : public CElement
  {
private:
   //--- Puntero al formulario al que está adjuntado el control
   CWindow          *m_wnd;
   //---
public:
                     CCheckBox(void);
                    ~CCheckBox(void);
   //--- Guarda el puntero del formulario
   void              WindowPointer(CWindow &object)                 { m_wnd=::GetPointer(object);            }
   //---
public:
   //--- Manejador de eventos del gráfico
   virtual void      OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam);
   //--- Temporizador
   virtual void      OnEventTimer(void);
   //--- Desplazamiento del control
   virtual void      Moving(const int x,const int y);
   //--- (1) Mostrar, (2) ocultar, (3) resetear, (4) eliminar
   virtual void      Show(void);
   virtual void      Hide(void);
   virtual void      Reset(void);
   virtual void      Delete(void);
   //--- (1) Establecer, (2) resetear las prioridades para el clic izquierdo del ratón
   virtual void      SetZorders(void);
   virtual void      ResetZorders(void);
   //--- Resetear color
   virtual void      ResetColors(void);
  };

Antes de la creación del control, el usuario tendrá disponibles los métodos para establecer las propiedades listadas a continuación:

  • Fondo del control
  • Iconos del checkbox en el estado activo y bloqueado
  • Márgenes para la etiqueta de texto
  • Colores de las etiquetas de texto en diferentes estados

Habrá cuatro imágenes para el botón del checkbox: dos imágenes para el estado activo del control, y otras dos para el estado bloqueado. Al final del artículo se puede descargar las imágenes ya hechas para todos los estados, pero cada uno puede usar sus propias imágenes.

class CCheckBox : public CElement
  {
private:
   //--- Color del fondo del checkbox
   color             m_area_color;
   //--- Iconos del checkbox en el estado activo y bloqueado
   string            m_check_bmp_file_on;
   string            m_check_bmp_file_off;
   string            m_check_bmp_file_on_locked;
   string            m_check_bmp_file_off_locked;
   //--- Texto del checkbox
   string            m_label_text;
   //--- Márgenes de la etiqueta de texto
   int               m_label_x_gap;
   int               m_label_y_gap;
   //--- Colores de la etiqueta de texto en diferentes estados
   color             m_label_color;
   color             m_label_color_off;
   color             m_label_color_hover;
   color             m_label_color_locked;
   color             m_label_color_array[];
   //--- Prioridades para el clic izquierdo del ratón
   int               m_zorder;
   int               m_area_zorder;
   //---
public:
   //--- Establecer los iconos para el botón en el estado activo y bloqueado
   void              CheckFileOn(const string file_path)            { m_check_bmp_file_on=file_path;         }
   void              CheckFileOff(const string file_path)           { m_check_bmp_file_off=file_path;        }
   void              CheckFileOnLocked(const string file_path)      { m_check_bmp_file_on_locked=file_path;  }
   void              CheckFileOffLocked(const string file_path)     { m_check_bmp_file_off_locked=file_path; }
   //--- (1) Color del fondo, (2) márgenes de la etiqueta de texto
   void              AreaColor(const color clr)                     { m_area_color=clr;                      }
   void              LabelXGap(const int x_gap)                     { m_label_x_gap=x_gap;                   }
   void              LabelYGap(const int y_gap)                     { m_label_y_gap=y_gap;                   }
   //--- Colores del texto en estados diferentes
   void              LabelColor(const color clr)                    { m_label_color=clr;                     }
   void              LabelColorOff(const color clr)                 { m_label_color_off=clr;                 }
   void              LabelColorHover(const color clr)               { m_label_color_hover=clr;               }
   void              LabelColorLocked(const color clr)              { m_label_color_locked=clr;              }
   //--- Descripción del checkbox
   string            LabelText(void)                          const { return(m_label.Description());         }
  };

Para crear el control “” checkbox vamos a necesitar tres métodos privados (private) y uno principal público (public):

class CCheckBox : public CElement
  {
private:
   //--- Objetos para crear el checkbox
   CRectLabel        m_area;
   CBmpLabel         m_check;
   CLabel            m_label;
   //---
public:
   //--- Métodos para crear el checkbox
   bool              CreateCheckBox(const long chart_id,const int subwin,const string text,const int x,const int y);
   //---
private:
   bool              CreateArea(void);
   bool              CreateCheck(void);
   bool              CreateLabel(void);
  };

Para gestionar el estado del checkbox, vamos a crear los métodos CCheckBox::CheckButtonState() y CCheckBox::CheckBoxState():

class CCheckBox : public CElement
  {
private:
   //--- Estado del botón del checkbox
   bool              m_check_button_state;
   //--- Estado del control (disponible/bloqueado)
   bool              m_checkbox_state;
   //---
public:
   //--- Devolver/establecer el estado del checkbox
   bool              CheckBoxState(void)                      const { return(m_checkbox_state);              }
   void              CheckBoxState(const bool state);
   //--- Devolver/establecer el estado del botón del checkbox
   bool              CheckButtonState(void)                   const { return(m_check.State());               }
   void              CheckButtonState(const bool state);
  };

Para bloquear/desbloquear el control hay que usar el método CCheckBox::CheckBoxState():

//+------------------------------------------------------------------+
//| Establecer el estado del control                                     |
//+------------------------------------------------------------------+
void CCheckBox::CheckBoxState(const bool state)
  {
//--- Estado del control
   m_checkbox_state=state;
//--- Imagen
   m_check.BmpFileOn((state)? "::"+m_check_bmp_file_on : "::"+m_check_bmp_file_on_locked);
   m_check.BmpFileOff((state)? "::"+m_check_bmp_file_off : "::"+m_check_bmp_file_off_locked);
//--- Color de la etiqueta de texto
   m_label.Color((state)? m_label_color : m_label_color_locked);
  }

Para establecer el estado del botón del checkbox, hay que usar el método CCheckBox::CheckButtonState(): 

//+------------------------------------------------------------------+
//| Establecer el estado del botón del checkbox                             |
//+------------------------------------------------------------------+
void CCheckBox::CheckButtonState(const bool state)
  {
//--- Salir si el control está bloqueado
   if(!m_checkbox_state)
      return;
//--- Establecemos el estado para el botón
   m_check.State(state);
   m_check_button_state=state;
//--- Cambiamos el color de acuerdo con el estado
   m_label.Color((state)? m_label_color : m_label_color_off);
   CElement::InitColorArray((state)? m_label_color : m_label_color_off,m_label_color_hover,m_label_color_array);
  }

Nos queda sólo crear el método para procesar el clic en el control “checkbox”. Este método va a invocarse en el manejador principal del control CCheckBox::OnEvent() por el evento con el identificador CHARTEVENT_OBJECT_CLICK. Este método va a llamarse CCheckBox::OnClickLabel(). En el principio de este método, se comprueba si ha tenido lugar el clic en el área del control (fondo). Para evitar confusiones en cuanto al objeto del control que ha sido pulsado, el valor de la prioridad del clic izquierdo para el fondo es igual a 1, para los demás es 0. Por eso, incluso si el clic ha sido hecho cuando el cursor se encuentra sobre el botón del checkbox o sobre la etiqueta de texto, va a considerarse que el clic se ha hecho en el fondo del control, es decir, en el objeto cuya prioridad es más alta en esta área. Luego, usamos el método CCheckBox::CheckButtonState() para establecer el estado opuesto al botón del checkbox. Al final del método, se envía el evento personalizado con (1) el identificador del evento ON_CLICK_LABEL, (2) identificador del control y (3) la descripción del control.

class CCheckBox : public CElement
  {
private:
  //--- Procesamiento del clic en el control
   bool              OnClickLabel(const string clicked_object);
  };
//+------------------------------------------------------------------+
//| Manejo de eventos                                                |
//+------------------------------------------------------------------+
void CCheckBox::OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam)
  {
//--- Procesamiento del evento del clic izquierdo en el objeto
   if(id==CHARTEVENT_OBJECT_CLICK)
     {
      //--- Clic en el checkbox
      if(OnClickLabel(sparam))
         return;
     }
  }
//+------------------------------------------------------------------+
//| Clic en el encabezado del control                                    |
//+------------------------------------------------------------------+
bool CCheckBox::OnClickLabel(const string clicked_object)
  {
//--- Salimos si el nombre del objeto no coincide
   if(m_area.Name()!=clicked_object)
      return(false);
//--- Salir si el control está bloqueado
   if(!m_checkbox_state)
      return(false);
//--- Cambiar al modo opuesto
   CheckButtonState(!m_check.State());
//--- Ahora el curso está sobre el control
   m_label.Color(m_label_color_hover);
//--- Enviamos el mensaje sobre ello
   ::EventChartCustom(m_chart_id,ON_CLICK_LABEL,CElement::Id(),0,m_label.Description());
   return(true);
  }

 

 


Prueba del control “Casilla de verificación”

Todos los métodos de la clase CCheckBox ya están listos, y ahora podemos probar cómo funciona este control. El archivo con la clase de este control ya tienen que estar incluido en la liibrería. Se puede copiar el Asesor Experto para las pruebas desde el artículo anterior. Eliminamos de ahí todos los controles, dejando sólo el menú principal y la barra de estado. Luego, creamos dos casillas de verificación (checkbox) para la prueba. Hagamos que el segundo checkbox esté bloqueado por defecto en el momento de la carga del programa en el gráfico. El primer checkbox estará disponible para la interacción con el usuario, pero en el estado desactivado. La activación del primer checkbox será como señal para el desbloqueo del segundo checkbox. Y al revés, la desactivación del primer checkbox será como señal para el bloqueo del segundo checkbox.

Hay que crear dos instancias de la clase CCheckBox en la clase personalizada CProgram, y declarar dos métodos para la creación de los checkbox indicando los márgenes desde el punto extremos del formulario al que estarán adjuntados: 

//+------------------------------------------------------------------+
//| Clase para crear la aplicación                                     |
//+------------------------------------------------------------------+
class CProgram : public CWndEvents
  {
private:
   //--- Checkboxs
Casilla de verificación (  checkbox        ).
   CCheckBox         m_checkbox2;
   //---
private:
   //--- Checkboxs
#define CHECKBOX1_GAP_X       (7)
#define CHECKBOX1_GAP_Y       (50)
   bool              CreateCheckBox1(const string text);
#define CHECKBOX2_GAP_X       (30)
#define CHECKBOX2_GAP_Y       (75)
   bool              CreateCheckBox2(const string text);
  };

La única diferencia en la implementación de los métodos consiste en que la disponibilidad del segundo checkbox va a depender del estado del primer checkbox:

//+------------------------------------------------------------------+
//| Crea el combobox 2                                                 |
//+------------------------------------------------------------------+
bool CProgram::CreateCheckBox2(string text)
  {
//--- Pasar el objeto del panel
   m_checkbox2.WindowPointer(m_window1);
//--- Coordenadas
   int x=m_window1.X()+CHECKBOX2_GAP_X;
   int y=m_window1.Y()+CHECKBOX2_GAP_Y;
//--- Establecemos las propiedades antes de la creación
   m_checkbox2.XSize(90);
   m_checkbox2.YSize(18);
   m_checkbox2.AreaColor(clrWhiteSmoke);
   m_checkbox2.LabelColor(clrBlack);
   m_checkbox2.LabelColorOff(clrBlack);
   m_checkbox2.LabelColorLocked(clrSilver);
//--- Creamos el control
   if(!m_checkbox2.CreateCheckBox(m_chart_id,m_subwin,text,x,y))
      return(false);
//--- La disponibilidad va a depender del estado actual del primer checkbox
   m_checkbox2.CheckBoxState(m_checkbox1.CheckButtonState()); 
//--- Añadimos el objeto al array común de los grupos de objetos
   CWndContainer::AddToElementsArray(0,m_checkbox2);
   return(true);
  }

La llamada a los métodos de la creación de checkboxs debe ubicarse en el método principal de la creación de la interfaz gráfica. En el código de abajo se muestra la versión reducida de este método:

//+------------------------------------------------------------------+
//| Crea el panel de trading                                          |
//+------------------------------------------------------------------+
bool CProgram::CreateTradePanel(void)
  {
//--- Creación del formulario 1 para los controles
//--- Creación de controles:
//    Menú principal
//--- Menús contextuales
//--- Creación de la barra de estado

 //--- Checkboxs
   if(!CreateCheckBox1("Checkbox 1"))
      return(false);
   if(!CreateCheckBox2("Checkbox 2"))
      return(false);
//--- Redibujar el gráfico
   m_chart.Redraw();
   return(true);
  }

Vamos a recibir y procesar los mensajes con el identificador ON_CLICK_LABEL en el manejador de eventos de la clase personalizada CProgram. La disponibilidad del segundo checkbox va a definirse por el estado del primer checkbox (véase el código de abajo):

//+------------------------------------------------------------------+
//| Manejador de eventos                                               |
//+------------------------------------------------------------------+
void CProgram::OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam)
  {
//--- Evento del clic en el checkbox
   if(id==CHARTEVENT_CUSTOM+ON_CLICK_LABEL)
     {
      ::Print(__FUNCTION__," > id: ",id,"; lparam: ",lparam,"; dparam: ",dparam,"; sparam: ",sparam);
      //--- Si el clic ha sido en el primer checkbox
      if(lparam==m_checkbox1.Id())
        {
         //--- Establecer el estado para el segundo checkbox
         m_checkbox2.CheckBoxState(m_checkbox1.CheckButtonState());
        }
     }
  }

Compile los archivos e inicie el EA en el gráfico. Debe obtener el resultado como en la captura de pantalla de abajo:

Fig. 2. Prueba del control “Checkbox”.

Fig. 2. Prueba del control “Checkbox”.


La captura de pantalla muestra que el segundo checkbox está bloqueado. Podemos definirlo por el color del texto y el imagen del botón correspondiente a este estado (colores descoloridos). Todo funciona perfectamente, y a continuación vamos a dedicarnos al desarrollo de la clase para la creación del control “campo de edición” 

 


Control “Campo de edición”

El control “campo de edición” se utiliza para establecer un valor numérico en el cuadro de texto. Se puede hacerlo manualmente, o bien utilizar los botones de desplazamiento (scrolling). El usuario de la librería puede establecer por sí mismo los valores mínimos y máximos (limitaciones), así como el paso para el scrolling. Si se pulsa el botón con flecha arriba/abajo, entonces el valor será aumentado/reducido de acuerdo con el paso especificado en los ajustes. Si el botón arriba/abajo se mantiene apretado, el valor en el campo de edición va a cambiarse en modo acelerado. Al llegar al máximo/mínimo permitido, el desplazamiento del valor se detiene.

Vamos a utilizar cinco objetos gráficos para diseñar este control. Son los siguientes:

  1. Fondo
  2. Etiqueta de texto
  3. Campo de edición
  4. Dos botones para el desplazamiento de los valores en el campo de edición


Fig. 3. Partes integrantes del control “Campo de edición”.


A continuación, vamos a empezar el desarrollo de la clase para la creación de este control de la interfaz.

 


Desarrollo de la clase para la creación del control “Campo de edición”

Igual que el control “Casilla de verificación», el control “Campo de edición» es simple y no compuesto. En otras palabras, se compone de objetos simples, pero no incluye otros controles. Por eso no habrá que introducir ningunas adiciones en el archivo WndContainer.mqh. Será suficiente sólo incluir el archivo con la clase del control “campo de edición”.

Ahora creamos el archivo SpinEdit.mqh y lo incluimos en el archivo WndContainer.mqh:

//+------------------------------------------------------------------+
//|                                                 WndContainer.mqh |
//|                        Copyright 2015, MetaQuotes Software Corp. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
#include "SpinEdit.mqh"

Creamos la clase CSpinEdit con los métodos estándar para todos los controles de la librería en el archivo SpinEdit.mqh, igual que ha sido mostrado antes en este artículo en la descripción de la clase CCheckBox. Por eso pasaremos directamente a la descripción de las propiedades del control “campo de edición”. 

Abajo se muestra la lista de todas las propiedades del control “campo de edición” que estarán disponibles al usuario para su configuración.

  • Color del fondo
  • Texto de la descripción del campo de edición
  • Márgenes de la etiqueta de texto
  • Colores del texto en estados diferentes
  • Tamaños del campo de edición
  • Margen del campo de edición desde el lado derecho del control
  • Colores del campo de edición en diferentes estados
  • Colores del texto del campo de edición en diferentes estados
  • Colores del marco del campo de edición en diferentes estados
  • Iconos de conmutadores en el estado activo y bloqueado
  • Márgenes de los botones (desde el lado derecho)
  • Modo de reseteo de valores hasta el mínimo
  • Valor mínimo y máximo
  • Paso para el cambio del valor en el campo de edición usando los botones
  • Modo de alineación del texto
  • Número de dígitos después de la coma

En el código de abajo se puede ver los campos y métodos de la clase para la configuración de las propiedades del control arriba mencionadas:

//+------------------------------------------------------------------+
//| Clase para crear campo de edición                                    |
//+------------------------------------------------------------------+
class CSpinEdit : public CElement
  {
private:
   //--- Color del fondo del control
   color             m_area_color;
   //--- Texto de la descripción del campo de edición
   string            m_label_text;
   //--- Márgenes de la etiqueta de texto
   int               m_label_x_gap;
   int               m_label_y_gap;
   //--- Colores del texto en estados diferentes
   color             m_label_color;
   color             m_label_color_hover;
   color             m_label_color_locked;
   color             m_label_color_array[];
   //--- Tamaños del campo de edición
   int               m_edit_x_size;
   int               m_edit_y_size;
   //--- Margen del campo de edición desde el lado derecho
   int               m_edit_x_gap;
   //--- Colores del campo de edición y del texto en diferentes estados
   color             m_edit_color;
   color             m_edit_color_locked;
   color             m_edit_text_color;
   color             m_edit_text_color_locked;
   color             m_edit_text_color_highlight;
   //--- Colores del marco del campo de edición en diferentes estados
   color             m_edit_border_color;
   color             m_edit_border_color_hover;
   color             m_edit_border_color_locked;
   color             m_edit_border_color_array[];
   //--- Iconos de conmutadores en el estado activo y bloqueado
   string            m_inc_bmp_file_on;
   string            m_inc_bmp_file_off;
   string            m_inc_bmp_file_locked;
   string            m_dec_bmp_file_on;
   string            m_dec_bmp_file_off;
   string            m_dec_bmp_file_locked;
   //--- Márgenes de los botones (desde el lado derecho)
   int               m_inc_x_gap;
   int               m_inc_y_gap;
   int               m_dec_x_gap;
   int               m_dec_y_gap;
   //--- Modo de reseteo de valores hasta el mínimo
   bool              m_reset_mode;
   //--- Valor mínimo/máximo
   double            m_min_value;
   double            m_max_value;
   //--- Paso para el cambio del valor en el campo de edición
   double            m_step_value;
   //--- Modo de alineación del texto
   ENUM_ALIGN_MODE   m_align_mode;
   //--- Número de dígitos después de la coma
   int               m_digits;
   //---
public:
   //--- (1) Color del fondo, (2) texto de la descripción del campo de edición, (3) márgenes de la etiqueta de texto
   void              AreaColor(const color clr)                     { m_area_color=clr;                   }
   string            LabelText(void)                          const { return(m_label.Description());      }
   void              LabelXGap(const int x_gap)                     { m_label_x_gap=x_gap;                }
   void              LabelYGap(const int y_gap)                     { m_label_y_gap=y_gap;                }
   //--- Colores de la etiqueta de texto en diferentes estados
   void              LabelColor(const color clr)                    { m_label_color=clr;                  }
   void              LabelColorHover(const color clr)               { m_label_color_hover=clr;            }
   void              LabelColorLocked(const color clr)              { m_label_color_locked=clr;           }
   //--- (1) Tamaños del campo de edición, (2) margen del campo de edición desde el lado derecho
   void              EditXSize(const int x_size)                    { m_edit_x_size=x_size;               }
   void              EditYSize(const int y_size)                    { m_edit_y_size=y_size;               }
   void              EditXGap(const int x_gap)                      { m_edit_x_gap=x_gap;                 }
   //--- Colores del campo de edición en diferentes estados
   void              EditColor(const color clr)                     { m_edit_color=clr;                   }
   void              EditColorLocked(const color clr)               { m_edit_color_locked=clr;            }
   //--- Colores del texto del campo de edición en diferentes estados
   void              EditTextColor(const color clr)                 { m_edit_text_color=clr;              }
   void              EditTextColorLocked(const color clr)           { m_edit_text_color_locked=clr;       }
   void              EditTextColorHighlight(const color clr)        { m_edit_text_color_highlight=clr;    }
   //--- Colores del marco del campo de edición en diferentes estados
   void              EditBorderColor(const color clr)               { m_edit_border_color=clr;            }
   void              EditBorderColorHover(const color clr)          { m_edit_border_color_hover=clr;      }
   void              EditBorderColorLocked(const color clr)         { m_edit_border_color_locked=clr;     }
   //--- Establecer los iconos para el botón en el estado activo y bloqueado
   void              IncFileOn(const string file_path)              { m_inc_bmp_file_on=file_path;        }
   void              IncFileOff(const string file_path)             { m_inc_bmp_file_off=file_path;       }
   void              IncFileLocked(const string file_path)          { m_inc_bmp_file_locked=file_path;    }
   void              DecFileOn(const string file_path)              { m_dec_bmp_file_on=file_path;        }
   void              DecFileOff(const string file_path)             { m_dec_bmp_file_off=file_path;       }
   void              DecFileLocked(const string file_path)          { m_dec_bmp_file_locked=file_path;    }
   //--- Márgenes para los botones del campo de edición
   void              IncXGap(const int x_gap)                       { m_inc_x_gap=x_gap;                  }
   void              IncYGap(const int y_gap)                       { m_inc_y_gap=y_gap;                  }
   void              DecXGap(const int x_gap)                       { m_dec_x_gap=x_gap;                  }
   void              DecYGap(const int y_gap)                       { m_dec_y_gap=y_gap;                  }
   //--- Modo del reseteo al hacer clic en la etiqueta de texto
   bool              ResetMode(void)                                { return(m_reset_mode);               }
   void              ResetMode(const bool mode)                     { m_reset_mode=mode;                  }
   //--- Valor mínimo
   double            MinValue(void)                           const { return(m_min_value);                }
   void              MinValue(const double value)                   { m_min_value=value;                  }
   //--- Valor máximo
   double            MaxValue(void)                           const { return(m_max_value);                }
   void              MaxValue(const double value)                   { m_max_value=value;                  }
   //--- Paso del valor
   double            StepValue(void)                          const { return(m_step_value);               }
   void              StepValue(const double value)                  { m_step_value=(value<=0)? 1 : value; }
   //--- (1) Número de dígitos después de la coma, (2) modo de alineación del texto
   void              SetDigits(const int digits)                    { m_digits=::fabs(digits);            }
   void              AlignMode(ENUM_ALIGN_MODE mode)                { m_align_mode=mode;                  }
  };

Para diseñar el control “campo de edición”, vamos a necesitar cinco métodos privados (private) para la creación de los objetos primitivos, y un método principal público (public) que va a invocarse en la clase personalizada durante la creación de la interfaz gráfica. Al final del artículo se puede descargar las imágenes para los botones de desplazamiento. Por defecto, se utilizan estas imágenes, pero si desean, pueden reemplazarlas por las suyas.

class CSpinEdit : public CElement
  {
private:
   //--- Objetos para crear el campo de edición
   CRectLabel        m_area;
   CLabel            m_label;
   CEdit             m_edit;
   CBmpLabel         m_spin_inc;
   CBmpLabel         m_spin_dec;
   //---
public:
   //--- Métodos para crear el campo de edición
   bool              CreateSpinEdit(const long chart_id,const int subwin,const string label_text,const int x,const int y);
   //---
private:
   bool              CreateArea(void);
   bool              CreateLabel(void);
   bool              CreateEdit(void);
   bool              CreateSpinInc(void);
   bool              CreateSpinDec(void);
  };

Ahora vamos a discutir los modos y los métodos que se usan para manejar el estado y las propiedades del control después de su creación. Empezamos con el método CSpinEdit::SetValue() para corregir y guardar el valor para el campo de edición, así como con el método adicional CSpinEdit::HighlightLimit(), que se usa para resaltar (parpadeo) el texto en edición cuando se excede el límite mínimo y el máximo.

Al principio del método CSpinEdit::SetValue() se realiza la corrección tomando en cuenta el paso para el cambio del valor. Luego se hacen las comprobaciones del exceso de los límites establecidos (máximo/mínimo). Si se detecta el exceso de esos límites, se establece el valor correspondiente a la comprobación y se llama al método CSpinEdit::HighlightLimit() para resaltar el texto. Luego, si después de todas las comprobaciones y correcciones resulta que el valor se ha cambiado en comparación con el que ha sido guardado antes, se guarda el valor nuevo.

El método CSpinEdit::HighlightLimit() es bastante sencillo. Primero ahí se establece el color del parpadeo que puede ser redefinido por el usuario (rojo por defecto). Después de eso se mantiene una pausa de 100 milisegundos. Será suficiente para que el cambio temporal del color sea notable. Al final, se establece el color inicial.

Se puede encontrar más detalles sobre estos métodos descritos en el código de abajo: 

class CSpinEdit : public CElement
  {
private:
   //--- Valor actual en el campo de edición
   double            m_edit_value;
   //---
public:
   //--- Devolver y establecer el valor del campo de edición
   double            GetValue(void)                           const { return(m_edit_value);               }
   bool              SetValue(const double value);
   //--- Parpadeo al alcanzar el límite
   void              HighlightLimit(void);
  };
//+------------------------------------------------------------------+
//| Comprobación del valor actual                                       |
//+------------------------------------------------------------------+
bool CSpinEdit::SetValue(double value)
  {
//--- Para la corrección
   double corrected_value =0.0;
//--- Corregimos tomando en cuenta el paso
   corrected_value=::MathRound(value/m_step_value)*m_step_value;
//--- Comprobación del mínimo/máximo
   if(corrected_value<m_min_value)
     {
       //--- Establecer valor mínimo
      corrected_value=m_min_value;
      //--- Establecemos el estado On
      m_spin_dec.State(true);
      //--- Parpadear avisando sobre el alcance del límite
      HighlightLimit();
     }
   if(corrected_value>m_max_value)
     {
       //--- Establecer valor máximo
      corrected_value=m_max_value;
      //--- Establecemos el estado On
      m_spin_inc.State(true);
      //--- Parpadear avisando sobre el alcance del límite
      HighlightLimit();
     }
//--- Si el valor ha sido cambiado
   if(m_edit_value!=corrected_value)
     {
      m_edit_value=corrected_value;
      m_edit.Color(m_edit_text_color);
      return(true);
     }
//--- Valor sin cambios
   return(false);
  }
//+------------------------------------------------------------------+
//| Resaltar el límite                                             |
//+------------------------------------------------------------------+
void CSpinEdit::HighlightLimit(void)
  {
//--- Cambiar temporalmente el color del texto
   m_edit.Color(m_edit_text_color_highlight);
//--- Actualizar
   ::ChartRedraw();
//--- Retardo antes de volver al color inicial
   ::Sleep(100);
//--- Cambiar el color del texto por el color inicial
   m_edit.Color(m_edit_text_color);
  }

El método CSpinEdit::SetValue() está destinado sólo para el ajuste del valor en caso de exceder el límite establecido, mientras que para el cambio del valor en el campo de edición, hay que usar el método CSpinEdit:: ChangeValue():

class CSpinEdit : public CElement
  {
private:
   //--- Cambio del valor en el campo de edición
   void              ChangeValue(const double value);
  };
//+------------------------------------------------------------------+
//| Cambio del valor en el campo de edición                                  |
//+------------------------------------------------------------------+
void CSpinEdit::ChangeValue(const double value)
  {
//--- Comprobamos, ajustamos y guardamos el valor nuevo
   SetValue(value);
//--- Establecemos el valor nuevo en el campo de edición
   m_edit.Description(::DoubleToString(GetValue(),m_digits));
  }

Igual que en los demás controles, vamos a necesitar el método para manejar la disponibilidad del control “campo de edición” (véase el código de abajo):

class CSpinEdit : public CElement
  {
private:
   //--- Estado del control (disponible/bloqueado)
   bool              m_spin_edit_state;
   //---
public:
   //--- Devolver/establecer el estado de disponibilidad del campo de edición
   bool              SpinEditState(void)                      const { return(m_spin_edit_state);          }
   void              SpinEditState(const bool state);
  };
//+------------------------------------------------------------------+
//| Establecer el estado del control                                     |
//+------------------------------------------------------------------+
void CSpinEdit::SpinEditState(const bool state)
  {
   m_spin_edit_state=state;
//--- Color de la etiqueta de texto
   m_label.Color((state)? m_label_color : m_label_color_locked);
//--- Color del campo de edición
   m_edit.Color((state)? m_edit_text_color : m_edit_text_color_locked);
   m_edit.BackColor((state)? m_edit_color : m_edit_color_locked);
   m_edit.BorderColor((state)? m_edit_border_color : m_edit_border_color_locked);
//--- Imagen de conmutadores
   m_spin_inc.BmpFileOn((state)? "::"+m_inc_bmp_file_on : "::"+m_inc_bmp_file_locked);
   m_spin_dec.BmpFileOn((state)? "::"+m_dec_bmp_file_on : "::"+m_dec_bmp_file_locked);
//--- Ajuste respecto el estado actual
   if(!m_spin_edit_state)
     {
      //--- Prioridades
      m_edit.Z_Order(-1);
      m_spin_inc.Z_Order(-1);
      m_spin_dec.Z_Order(-1);
      //--- Campo de edición en el modo “Sólo lectura”
      m_edit.ReadOnly(true);
     }
   else
     {
      //--- Prioridades
      m_edit.Z_Order(m_edit_zorder);
      m_spin_inc.Z_Order(m_spin_zorder);
      m_spin_dec.Z_Order(m_spin_zorder);
      //--- Campo de edición en modo de edición
      m_edit.ReadOnly(false);
     }
  }

Ahora hablaremos de los métodos para procesar los eventos del control “campo de edición”. Al pulsar la etiqueta de texto, va a generarse el evento personalizado con (1) el identificador del evento ON_CLICK_LABEL, (2) identificador del control, (3) índice del control y (4) la descripción de la etiqueta de texto. Antes, ya hemos mencionado el modo para resetear el valor en el campo de edición hasta el mínimo. Por defecto, este modo está desactivado (false). Se activa a través del método CSpinEdit::ResetMode(): ponga el valor true en su único argumento. Si el modo para el reseteo del valor está activado, el clic en la etiqueta de texto va a llamar al método para establecer el valor mínimo en el campo de edición.

Teniendo en cuenta todo lo arriba mencionado, el código del método CSpinEdit::OnClickLabel() para el procesamiento del clic en la etiqueta de texto será como se muestra a continuación:

class CSpinEdit : public CElement
  {
private:
  //--- Procesamiento del clic en la etiqueta de texto
   bool              OnClickLabel(const string clicked_object);
  };
//+------------------------------------------------------------------+
//| Clic en el encabezado del control                                    |
//+------------------------------------------------------------------+
bool CSpinEdit::OnClickLabel(const string clicked_object)
  {
//--- Salimos si el nombre del objeto no coincide
   if(m_area.Name()!=clicked_object)
      return(false);
//--- Si el modo de reseteo está activado
   if(m_reset_mode)
     {
       //--- Establecemos el valor mínimo
      ChangeValue(MinValue());
     }
//--- Enviamos el mensaje sobre ello
   ::EventChartCustom(m_chart_id,ON_CLICK_LABEL,CElement::Id(),CElement::Index(),m_label.Description());
   return(true);
  }

El usuario puede modificar el valor en el campo de edición introduciendo un valor nuevo manualmente o usando los botones conmutadores del incremento y decremento. Necesitaremos nuevos identificadores de eventos personalizados. Vamos a añadirlos al archivo Defines.mqh:

//+------------------------------------------------------------------+
//|                                                      Defines.mqh |
//|                        Copyright 2015, MetaQuotes Software Corp. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
#define ON_END_EDIT               (18) // Fin de edición del valor en el campo de edición
#define ON_CLICK_INC              (19) // Cambio del contador hacia arriba
#define ON_CLICK_DEC              (20) // Cambio del contador hacia abajo

Para procesar la entrada de un nuevo valor manualmente, escribiremos el método CSpinEdit::OnEndEdit(). Va a invocarse en el manejador común CSpinEdit::OnEvent() cuando llega el evento con el identificador CHARTEVENT_OBJECT_ENDEDIT. Cuando se introduce un valor nuevo, se corrige en caso de necesidad. Al final del método va a generarse el evento personalizado con (1) el identificador del evento ON_END_EDIT, (2) identificador del control, (3) índice del control y (4) la descripción de la etiqueta de texto (véase el código de abajo). 

class CSpinEdit : public CElement
  {
private:
   //--- Procesamiento de la entrada del valor en el campo de edición
   bool              OnEndEdit(const string edited_object);
  };
//+------------------------------------------------------------------+
//| Procesamiento de la entrada del valor en el campo de edición                            |
//+------------------------------------------------------------------+
bool CSpinEdit::OnEndEdit(const string edited_object)
  {
//--- Salimos si el nombre del objeto no coincide
   if(m_edit.Name()!=edited_object)
      return(false);
//--- Obtenemos el valor introducido
   double entered_value=::StringToDouble(m_edit.Description());
//--- Comprobamos, ajustamos y guardamos el valor nuevo
   ChangeValue(entered_value);
//--- Enviamos el mensaje sobre ello
   ::EventChartCustom(m_chart_id,ON_END_EDIT,CElement::Id(),CElement::Index(),m_label.Description());
   return(true);
  }

Para el procesamiento del clic en los botones de incremento y decremento, hay que crear dos métodos separados CSpinEdit::OnClickSpinInc() y CSpinEdit::OnClickSpinDec(). Ahí todo es muy simple. Después del clic en el objeto gráfico, por su nombre se determina si es el botón de nuestro control. Luego obtenemos el valor actual en el campo de edición y lo aumentamos/reducimos por el paso establecido en las propiedades del control. Al final de los métodos se genera el evento personalizado con (1) el identificador del evento ON_CLICK_INC/ON_CLICK_DEC, (2) identificador del control, (3) índice del control y (4) la descripción de la etiqueta de texto (véase el código de abajo).

class CSpinEdit : public CElement
  {
private:
   //--- Procesamiento del clic en el botón del campo de edición
   bool              OnClickSpinInc(const string clicked_object);
   bool              OnClickSpinDec(const string clicked_object);
  };
//+------------------------------------------------------------------+
//| Clic en el conmutador del incremento                              |
//+------------------------------------------------------------------+
bool CSpinEdit::OnClickSpinInc(const string clicked_object)
  {
//--- Salimos si el nombre del objeto no coincide
   if(m_spin_inc.Name()!=clicked_object)
      return(false);
//--- Obtenemos el valor actual
   double value=GetValue();
//--- Aumentamos por un paso y comprobamos para el exceso del límite
   ChangeValue(value+m_step_value);
//--- Establecemos el estado On
   m_spin_inc.State(true);
//--- Enviamos el mensaje sobre ello
   ::EventChartCustom(m_chart_id,ON_CLICK_INC,CElement::Id(),CElement::Index(),m_label.Description());
   return(true);
  }
//+------------------------------------------------------------------+
//| Clic en el conmutador del decremento                              |
//+------------------------------------------------------------------+
bool CSpinEdit::OnClickSpinDec(const string clicked_object)
  {
//--- Salimos si el nombre del objeto no coincide
   if(m_spin_dec.Name()!=clicked_object)
      return(false);
//--- Obtenemos el valor actual
   double value=GetValue();
//--- Reducimos por un paso y comprobamos para el exceso del límite
   ChangeValue(value-m_step_value);
//--- Establecemos el estado On
   m_spin_dec.State(true);
//--- Enviamos el mensaje sobre ello
   ::EventChartCustom(m_chart_id,ON_CLICK_DEC,CElement::Id(),CElement::Index(),m_label.Description());
   return(true);
  }

Todos los métodos manejadores deben ser llamados en el método principal de procesamiento de eventos (véase el código de abajo). El acceso al cuerpo de los métodos manejadores se controla con la disponibilidad del control en este momento

//+------------------------------------------------------------------+
//| Manejo de eventos                                                |
//+------------------------------------------------------------------+
void CSpinEdit::OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam)
  {
//--- Procesamiento del evento del clic izquierdo en el objeto
   if(id==CHARTEVENT_OBJECT_CLICK)
     {
      //--- Salir si el control está bloqueado
      if(!m_spin_edit_state)
        return;
      //--- Procesamiento del clic en la etiqueta de texto
      if(OnClickLabel(sparam))
         return;
       //--- Procesamiento del clic en los botones del campo de edición
      if(OnClickSpinInc(sparam))
         return;
      if(OnClickSpinDec(sparam))
         return;
      //---
      return;
     }
//--- Procesamiento del evento del cambio del valor en el campo de edición
   if(id==CHARTEVENT_OBJECT_ENDEDIT)
     {
      //--- Salir si el control está bloqueado
      if(!m_spin_edit_state)
        return;
       //--- Procesamiento de la entrada del valor
      if(OnEndEdit(sparam))
         return;
     }
  }

Para el avance/retroceso rápido de los valores, cuando el botón izquierdo se mantiene pulsado sobre los botones conmutadores del control, vamos a crear el método especial CSpinEdit::FastSwitching() que debe invocarse en el temporizador del control. Antes ya discutimos un método parecido cuando hablabamos sobre la clase CListView para la creación de las listas con la barra de desplazamiento. Si ahí este método es necesario para el scrolling de la lista, aquí va a servir para aumentar o reducir el valor en el campo de edición. Para más detalles CSpinEdit::FastSwitching() véase el código de abajo. 

class CSpinEdit : public CElement
  {
private:
   //--- Avance/retroceso rápido de los valores en el campo de edición
   void              FastSwitching(void);
  };
//+------------------------------------------------------------------+
//| Temporizador                                                           |
//+------------------------------------------------------------------+
void CSpinEdit::OnEventTimer(void)
  {
//--- Si el control es desplegable
   if(CElement::IsDropdown())
     {
      ChangeObjectsColor();
      FastSwitching();
     }
   else
     {
      //--- Seguimos el cambio del color y el avance/retroceso de valores, 
          //--- sólo si el formulario no está bloqueado
      if(!m_wnd.IsLocked())
        {
         ChangeObjectsColor();
         FastSwitching();
        }
     }
  }
//+------------------------------------------------------------------+
//| Avance/retroceso rápido de valores en el campo de edición                        |
//+------------------------------------------------------------------+
void CSpinEdit::FastSwitching(void)
  {
//--- Salimos si no hay foco sobre el control
   if(!CElement::MouseFocus())
      return;
//--- Volvemos el contador al estado inicial si el botón del ratón está suelto
   if(!m_mouse_state)
      m_timer_counter=SPIN_DELAY_MSC;
//--- Si el botón del ratón está pulsado
   else
     {
      //--- Aumentamos el contador al intervalo establecido
      m_timer_counter+=TIMER_STEP_MSC;
      //--- Salimos si es menos de cero
      if(m_timer_counter<0)
         return;
       //--- Obtenemos el valor actual en el campo de edición
      double current_value=::StringToDouble(m_edit.Description());
      //--- Si se aumenta
      if(m_spin_inc.State())
         SetValue(current_value+m_step_value);
      //--- Si se reduce
      else if(m_spin_dec.State())
         SetValue(current_value-m_step_value);
      //--- Cambiamos el valor si el botón conmutador todavía se mantiene pulsado
      if(m_spin_inc.State() || m_spin_dec.State())
         m_edit.Description(::DoubleToString(GetValue(),m_digits));
     }
  }

 

 


Prueba del control “campo de edición”

Todos los métodos del control “Campo de edición” ya están implementados. Ahora vamos a testearlo en el programa que tenemos preparado para ello. Creamos la instancia de la clase CSpinEdit en la clase personalizada de la aplicación CProgram y declaramos el método para la creación del control “campo de edición”.

//+------------------------------------------------------------------+
//| Clase para crear la aplicación                                     |
//+------------------------------------------------------------------+
class CProgram : public CWndEvents
  {
private:
  //--- Campos de edición
   CSpinEdit         m_spin_edit1;
   //---
private:
  //--- Campos de edición
#define SPINEDIT1_GAP_X       (150)
#define SPINEDIT1_GAP_Y       (75)
   bool              CreateSpinEdit1(const string text);
  };

Hagamos que el primer checkbox gestione la disponibilidad de este control igual que lo hemos hecho respecto al segundo checkbox. Por eso, una vez creado el control, su disponibilidad va a depender del estado actual del primer checkbox (código de abajo).

//+------------------------------------------------------------------+
//| Crea el campo de edición 1                                             |
//+------------------------------------------------------------------+
bool CProgram::CreateSpinEdit1(string text)
  {
//--- Guardamos el puntero a la ventana
   m_spin_edit1.WindowPointer(m_window1);
//--- Coordenadas
   int x=m_window1.X()+SPINEDIT1_GAP_X;
   int y=m_window1.Y()+SPINEDIT1_GAP_Y;
//--- Valor
   double v=(m_spin_edit1.GetValue()==WRONG_VALUE) ? 4 : m_spin_edit1.GetValue();
//--- Establecemos las propiedades antes de la creación
   m_spin_edit1.XSize(150);
   m_spin_edit1.YSize(18);
   m_spin_edit1.EditXSize(76);
   m_spin_edit1.MaxValue(1000);
   m_spin_edit1.MinValue(-1000);
   m_spin_edit1.StepValue(1);
   m_spin_edit1.SetDigits(0);
   m_spin_edit1.SetValue(v);
   m_spin_edit1.ResetMode(true);
   m_spin_edit1.AreaColor(clrWhiteSmoke);
   m_spin_edit1.LabelColor(clrBlack);
   m_spin_edit1.LabelColorLocked(clrSilver);
   m_spin_edit1.EditColorLocked(clrWhiteSmoke);
   m_spin_edit1.EditTextColor(clrBlack);
   m_spin_edit1.EditTextColorLocked(clrSilver);
   m_spin_edit1.EditBorderColor(clrSilver);
   m_spin_edit1.EditBorderColorLocked(clrSilver);
//--- Creamos el control
   if(!m_spin_edit1.CreateSpinEdit(m_chart_id,m_subwin,text,x,y))
      return(false);
//--- La disponibilidad va a depender del estado actual del primer checkbox
   m_spin_edit1.SpinEditState(m_checkbox1.CheckButtonState());
//--- Añadimos el objeto al array común de los grupos de objetos
   CWndContainer::AddToElementsArray(0,m_spin_edit1);
   return(true);
  }

Igual que los demás controles, el método CProgram::CreateSpinEdit1() debe ser llamado en el método principal de la creación de la interfaz gráfica del programa. En el código de abajo se muestra la versión reducida de este método:

//+------------------------------------------------------------------+
//| Crea el panel de trading                                          |
//+------------------------------------------------------------------+
bool CProgram::CreateTradePanel(void)
  {
//--- Creación del formulario 1 para los controles
//--- Creación de controles:
//    Menú principal
//--- Menús contextuales
//--- Creación de la barra de estado
//--- Casillas de verificación (checkbox)
//--- Campos de edición
   if(!CreateSpinEdit1("Spin Edit 1:"))
      return(false);
//--- Redibujar el gráfico
   m_chart.Redraw();
   return(true);
  }

En el manejador de eventos CProgram::OnEvent(), añadimos el código para la prueba de la escucha de los mensajes de los campos de edición, también indicamos que el primer campo de edición depende ahora del estado del primer checkbox

//+------------------------------------------------------------------+
//| Manejador de eventos                                               |
//+------------------------------------------------------------------+
void CProgram::OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam)
  {
//--- Evento del clic en la etiqueta de texto
   if(id==CHARTEVENT_CUSTOM+ON_CLICK_LABEL)
     {
      ::Print(__FUNCTION__," > id: ",id,"; lparam: ",lparam,"; dparam: ",dparam,"; sparam: ",sparam);
      //--- Si el clic ha sido en el primer checkbox
      if(lparam==m_checkbox1.Id())
        {
         //--- Establecer el estado para el segundo checkbox y el primer campo de edición
         m_checkbox2.CheckBoxState(m_checkbox1.CheckButtonState());
         m_spin_edit1.SpinEditState(m_checkbox1.CheckButtonState());
        }
     }
 //--- Evento del fin de la entrada del valor en el campo de edición
   if(id==CHARTEVENT_CUSTOM+ON_END_EDIT)
     {
      ::Print(__FUNCTION__," > id: ",id,"; lparam: ",lparam,"; dparam: ",dparam,"; sparam: ",sparam);
     }
//--- Eventos del clic en los botones conmutadores del campo de edición
   if(id==CHARTEVENT_CUSTOM+ON_CLICK_INC || id==CHARTEVENT_CUSTOM+ON_CLICK_DEC)
     {
      ::Print(__FUNCTION__," > id: ",id,"; lparam: ",lparam,"; dparam: ",dparam,"; sparam: ",sparam);
     }
  }

Compile el programa y cárguelo en el gráfico. En la captura de pantalla se muestra el aspecto de la interfaz gráfica de la aplicación en este momento:

Fig. 4. Prueba del control “Campo de edición”.

Fig. 4. Prueba del control “Campo de edición”.

 


Otros controles con las casillas de verificación

Al principio del artículo hemos mencionado que aparte de los controles “casilla de verificación” o (“checkbox”) y “campo de edición” serán considerados otros dos controles: “campo de edición con checkbox” y “lista combinada (combobox) con checkbox”. El campo de edición con checkbox es la versión ampliada de la clase CSpinEdit completada con los campos y métodos de la clase CCheckBox, que han sido considerados en este artículo. 

 Fig. 5. Partes integrantes del control “Campo de edición con checkbox”.

Fig. 5. Partes integrantes del control “Campo de edición con checkbox”.


Combobox con checkbox es la combinación similar de las clases CComboBox y CCheckBox:

 

Fig. 6. Partes integrantes del control “Combobox con checkbox”.


Puede encontrar la implementación de las clases CCheckBoxEdit (campo de edición con checkbox) y CCheckComboBox (combobox con checkbox) en los archivos adjuntos al artículo para su estudio independiente. Puesto que en el control tipo CCheckComboBox contiene la lista desplegable, hay que introducir en el archivo WndContainer.mqh las adiciones correspondientes, igual que ha sido hecho con otros controles que contienen las partes desplegables. En este caso, hay que hacer que el puntero a la lista desplegable se encuentre en el array privado de los punteros m_drop_lists[]. Puede encontrar la descripción detallada de cómo hacerlo en el artículo Interfaces gráficas V - Control “Lista combinada” (Capítulo 3).

Como ejemplo, completaremos la aplicación de prueba con estos controles para que pueda ver cómo funciona eso. Añadiremos dos checkbox tipo CCheckBox uno de CCheckBoxEdit y CCheckComboBox. La disponibilidad del control tipo CCheckBoxEdit va a depender del estado del tercer checkbox, la disponibilidad del control tipo CCheckComboBox va a depender del estado del cuarto checkbox.

 

Fig. 7. Prueba de controles de tipos combinados.

 


Conclusión

En este artículo hemos desarrollado los controles bastante comunes que se encuentran a menudo en muchas interfaces gráficas en diferentes entornos: “checkbox”, “campo de edición”, “campo de edición con checkbox” y “combobox con checkbox”. En el segundo capítulo de la sexta parte nos ocuparemos del desarrollo de los controles “Slider” y “Slider doble”. 

Más abajo puede descargar el material de la sexta parte de la serie para poder probar cómo funciona todo eso. Si le surgen algunas preguntas sobre el uso del material de estos archivos, puede dirigirse a la descripción detallada del proceso de desarrollo de la librería en uno de los artículos listados más abajo, o bien hacer su pregunta en los comentarios para el artículo.

Lista de artículos (capítulos) de la sexta parte:

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

Archivos adjuntos |
Experto comercial universal: trabajando con órdenes pendientes y cobertura (parte 5) Experto comercial universal: trabajando con órdenes pendientes y cobertura (parte 5)
Este artículo continúa la presentación a los lectores del motor comercial CStrategy. A petición de multitud de usuarios, se han añadido funciones de trabajo con órdenes pendientes al motor comercial. Asimismo, las últimas versiones de MetaTrader 5 han comenzado a dar soporte a cuentas con cobertura. Ahora CStrategy también da soporte a las mismas. En el artículo se da una descripción detallada de un algoritmo para trabajar con órdenes pendientes, así como de los principios de funcionamiento de CStrategy con las cuentas con cobertura.
Interfaces gráficas V: Control "Lista combinada" (Capítulo 3) Interfaces gráficas V: Control "Lista combinada" (Capítulo 3)
En dos primeros capítulos de la quinta parte sobre las interfaces gráficas hemos desarrollado las clases para crear la barra de desplazamiento y la lista. En este capítulo vamos a hablar de la clase para la creación del control llamado “Lista combinada”. Éste también es un control compuesto que incluye los controles analizados en dos primeros capítulos de la quinta parte.
Interfaces gráficas VI: Controles "Slider" y "Slider doble" (Capítulo 2) Interfaces gráficas VI: Controles "Slider" y "Slider doble" (Capítulo 2)
En el artículo anterior nuestra librería ha sido completada con cuatro controles bastante frecuentes en las interfaces gráficas: “checkbox”, “campo de edición”, “campo de edición con checkbox” y “combobox con checkbox”. El segundo capítulo de la sexta parte estará dedicado a los controles como Slider y Slider doble.
Cómo mejorar el simulador de estrategias para optimizar indicadores usando ejemplos de los mercados de tendencia y flat Cómo mejorar el simulador de estrategias para optimizar indicadores usando ejemplos de los mercados de tendencia y flat
Al comerciar con diferentes estrategias a veces se requiere determinar si el mercado se encuentra en tendencia o en flat. Con este objetivo se desarrollan multitud de indicadores. ¿Pero cómo evaluar si el indicador cumple o no con la tarea indicada? ¿Cómo aclarar cuál es el diapasón medio del estado del flat o de la tendencia para definir nuestros stops y objetivos? En este artículo se propone usar para ello el simulador de estrategias, demostrando al mismo tiempo que no solo sirve para la optimización de robots para determinadas necesidades. Como indicador de prueba vamos a usar a nuestro viejo conocido ADX.