Descargar MetaTrader 5

Interfaces gráficas V: Control "Lista" (Capítulo 2)

23 junio 2016, 09:20
Anatoli Kazharski
0
503

Í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 cada artículo de la serie se muestra la lista completa de los capítulos con los enlaces. Además, se puede 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.  

En el primer capítulo de la quinta parte de la serie hemos desarrollado las clases para la creación de los controles como la barra de desplazamiento vertical y horizontal. En este artículo vamos a aplicarlas en la práctica. Esta vez diseñaremos la clase para la creación del control “Lista”, y la barra de desplazamiento vertical será su parte integrante. Además, mostraremos la implementación del mecanismo para el desplazamiento automático de la lista al mantener pulsados los botones de la barra de desplazamiento. Al final, probaremos todos eso en un ejemplo real de la aplicación MQL.

 


Control “Lista”

El control de la interfaz gráfica “Lista” ofrece al usuario la posibilidad de elegir entre varias opciones. El número total de los elementos de la lista y el número de los elementos de su parte visible puede ser diferente, cuando el número total es muy grande y no cabe en el área de trabajo asignada de la interfaz. En estos casos se utiliza la barra de desplazamiento. 

Nosotros vamos a componer la lista de varios objetos primitivos y un elemento incluido. Son los siguientes:

  1. Fondo de la lista.
  2. Arrays de los elementos de la lista.
  3. Control “Barra de desplazamiento vertical”.



Fig. 1. Partes integrantes del control “Lista”.

 

A continuación, vamos a analizar el desarrollo de la clase para la creación del control “Lista”.

 


Desarrollo de la clase para la creación del control

Para la creación del control y su incorporación en la librería, es necesario crear el archivo con la clase del control (CListView) -en nuestro caso es ListView.mqh- e incluirlo en el archivo WndContainer.mqh:

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

La clase CListView,  igual que las demás clases de los controles que han sido analizados antes en otros artículos de esta serie, tiene un conjunto estándar de los métodos. Para usar la barra de desplazamiento en este control, el archivo Scrolls.mqh tiene que estar incluido en el archivo ListView.mqh

//+------------------------------------------------------------------+
//|                                                     ListView.mqh |
//|                        Copyright 2015, MetaQuotes Software Corp. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
#include "Element.mqh"
#include "Window.mqh"
#include "Scrolls.mqh"
//+------------------------------------------------------------------+
//| Clase para crear la lista                                        |
//+------------------------------------------------------------------+
class CListView : public CElement
  {
private:
   //--- Puntero al formulario al que está adjuntado el control
   CWindow          *m_wnd;
   //---
public:
                     CListView(void);
                    ~CListView(void);
   //--- (1) 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);
  };
//+------------------------------------------------------------------+
//| Constructor                                                      |
//+------------------------------------------------------------------+
CListView::CListView(void)
  {
//--- Guardamos el nombre de la clase del control en la clase base
   CElement::ClassName(CLASS_NAME);
  }
//+------------------------------------------------------------------+
//| Destructor                                                       |
//+------------------------------------------------------------------+
CListView::~CListView(void)
  {
  }

Vamos a necesitar los métodos para establecer las propiedades de los objetos primitivos (los que van a componer la lista) antes de su creación.

  • Alto de los elementos de la lista.
  • Color del marco del fondo de la lista.
  • Color del fondo de los elementos en diferentes estados.
  • Colores del texto de los elementos en diferentes estados.

Los valores predefinidos de las propiedades mencionadas se establecen en el constructor de la clase.

class CListView : public CElement
  {
private:
   //--- Propiedades del fondo de la lista
   int               m_area_zorder;
   color             m_area_border_color;
   //--- Propiedades de los elementos de la lista
   int               m_item_zorder;
   int               m_item_y_size;
   color             m_item_color;
   color             m_item_color_hover;
   color             m_item_color_selected;
   color             m_item_text_color;
   color             m_item_text_color_hover;
   color             m_item_text_color_selected;
   //---
public:
   //--- Alto del elemento
   void              ItemYSize(const int y_size)                         { m_item_y_size=y_size;             }
   //--- Color del marco del fondo
   void              AreaBorderColor(const color clr)                    { m_area_border_color=clr;          }
   //--- Colores de los elementos de la lista en diferentes estados
   void              ItemColor(const color clr)                          { m_item_color=clr;                 }
   void              ItemColorHover(const color clr)                     { m_item_color_hover=clr;           }
   void              ItemColorSelected(const color clr)                  { m_item_color_selected=clr;        }
   void              ItemTextColor(const color clr)                      { m_item_text_color=clr;            }
   void              ItemTextColorHover(const color clr)                 { m_item_text_color_hover=clr;      }
   void              ItemTextColorSelected(const color clr)              { m_item_text_color_selected=clr;   }
  };
//+------------------------------------------------------------------+
//| Constructor                                                      |
//+------------------------------------------------------------------+
CListView::CListView(void) : m_item_y_size(18),
                             m_area_border_color(C'235,235,235'),
                             m_item_color(clrWhite),
                             m_item_color_hover(C'240,240,240'),
                             m_item_color_selected(C'51,153,255'),
                             m_item_text_color(clrBlack),
                             m_item_text_color_hover(clrBlack),
                             m_item_text_color_selected(clrWhite)
  {
//--- Establecemos las prioridades para el clic izquierdo del ratón
   m_area_zorder =1;
   m_item_zorder =2;
  }

Entre los métodos de la creación de los objetos de la lista hay tres métodos privados y uno público. Para crear los elementos de la lista, es necesario declarar el array de las instancias de la clase tipo CEdit, mediante el cual se puede crear los objetos gráficos tipo OBJ_EDIT (campo de edición). 

class CListView : public CElement
  {
private:
   //--- Objetos para crear la lista
   CRectLabel        m_area;
   CEdit             m_items[];
   CScrollV          m_scrollv;
   //---
public:
   //--- Métodos para crear la lista
   bool              CreateListView(const long chart_id,const int window,const int x,const int y);
   //---
private:
   bool              CreateArea(void);
   bool              CreateList(void);
   bool              CreateScrollV(void);
  };

Por defecto, el tamaño de la lista y de su parte visible es igual a dos elementos, ya que no tiene sentido crear la lista que se compone sólo de un elemento. Para establecer el tamaño de la lista y de su parte visible, vamos a crear los métodos CListView::ListSize() y CListView::VisibleListSize() con la comprobación del número de los elementos no menos de dos.  

class CListView : public CElement
  {
private:
   //--- Array de los valores de la lista
   string            m_value_items[];
   //--- Tamaño de la lista y de su parte visible
   int               m_items_total;
   int               m_visible_items_total;
   //---
public:
   //--- Devuelve (1) el tamaño de la lista y (2) de su parte visible
   int               ItemsTotal(void)                              const { return(m_items_total);            }
   int               VisibleItemsTotal(void)                       const { return(m_visible_items_total);    }
   //--- Establecer (1) el tamaño de la lista y (2) de su parte visible
   void              ListSize(const int items_total);
   void              VisibleListSize(const int visible_items_total);
  };
//+------------------------------------------------------------------+
//| Constructor                                                      |
//+------------------------------------------------------------------+
CListView::CListView(void) : m_items_total(2),
                             m_visible_items_total(2)
  {
//--- Establecemos el tamaño de la lista y de su parte visible
   ListSize(m_items_total);
   VisibleListSize(m_visible_items_total);
  }
//+------------------------------------------------------------------+
//| Establece el tamaño de la lista                                      |
//+------------------------------------------------------------------+
void CListView::ListSize(const int items_total)
  {
//--- No hay sentido hacer la lista menos de dos elementos
   m_items_total=(items_total<2) ? 2 : items_total;
   ::ArrayResize(m_value_items,m_items_total);
  }
//+------------------------------------------------------------------+
//| Establece el tamaño de la parte visible de la lista                        |
//+------------------------------------------------------------------+
void CListView::VisibleListSize(const int visible_items_total)
  {
//--- No hay sentido hacer la lista menos de dos elementos
   m_visible_items_total=(visible_items_total<2) ? 2 : visible_items_total;
   ::ArrayResize(m_items,m_visible_items_total);
  }

Vamos a necesitar los métodos correspondientes para guardar y obtener el índice y el texto del elemento seleccionado en la lista. Por defecto, queda seleccionado el primer elemento de la lista. Si hay que seleccionar otro elemento después de creación de la lista, utilice el método CListView::SelectedItemIndex(). Tendrá que especificar el índice del elemento antes de creación de la lista y después de que haya sido definido el número de sus elementos.

class CListView : public CElement
  {
private:
   //--- (1) Índice y (2) texto del elemento seleccionado
   int               m_selected_item_index;
   string            m_selected_item_text;
   //---
public:
   //--- Devuelve/guarda (1) el índice y (2) texto del elemento seleccionado en la lista
   void              SelectedItemIndex(const int index);
   int               SelectedItemIndex(void)                       const { return(m_selected_item_index);    }
   void              SelectedItemText(const string text)                 { m_selected_item_text=text;        }
   string            SelectedItemText(void)                        const { return(m_selected_item_text);     }
  };
//+------------------------------------------------------------------+
//| Constructor                                                      |
//+------------------------------------------------------------------+
CListView::CListView(void) : m_selected_item_index(0),
                             m_selected_item_text("")
  {
//--- ...
  }
//+------------------------------------------------------------------+
//| Guarda el índice                                               |
//+------------------------------------------------------------------+
void CListView::SelectedItemIndex(const int index)
  {
//--- Corrección en caso de salir fuera del diapasón
   m_selected_item_index=(index>=m_items_total)? m_items_total-1 : (index<0)? 0 : index;
  }

Después de la creación de la lista, así como en el momento de seleccionar el elemento, hay que resaltarlo con un color diferente. Para eso escribiremos el método CListView::HighlightSelectedItem(). Al principio de este método se encuentra la comprobación del estado actual de la barra de desplazamiento de la lista. Si se encuentra en modo activo (en modo de desplazamiento del deslizador), el programa sale del método. Si la comprobación ha pasado con éxito, luego obtenemos la posición actual del deslizador en la lista. El valor obtenido será el inicial para el contador del ciclo mediante el cual podremos determinar qué elemento hay que seleccionar.

class CListView : public CElement
  {
public:
   //--- Resaltar elemento seleccionado
   void              HighlightSelectedItem(void);
  };
//+------------------------------------------------------------------+
//| Resalta elemento seleccionado                                         |
//+------------------------------------------------------------------+
void CListView::HighlightSelectedItem(void)
  {
//--- Salimos si la barra de desplazamiento se encuentra en modo activo
   if(m_scrollv.ScrollState())
      return;
//--- Obtenemos la posición actual del deslizador de la barra de desplazamiento
   int v=m_scrollv.CurrentPos();
//--- Repasamos la parte visible de la lista en el ciclo
   for(int r=0; r<m_visible_items_total; r++)
     {
      //--- Si estamos dentro del diapasón de la lista
      if(v>=0 && v<m_items_total)
        {
         //--- Cambio del color del fondo y color del texto
         m_items[r].BackColor((m_selected_item_index==v) ? m_item_color_selected : m_item_color);
         m_items[r].Color((m_selected_item_index==v) ? m_item_text_color_selected : m_item_text_color);
         //--- Aumentamos el contador
         v++;
        }
     }
  }

En el momento de creación de los elementos de la lista en el método CListView::CreateList(), sus coordenadas y el ancho se calculan de tal manera que ellos no cubran el marco del fondo de la lista. El ancho de los elementos se calcula tomando en cuenta el hecho de la presencia de la barra de desplazamiento dentro de la lista. Todos los elementos tras el primero se colocan uno encima del otro con solapamiento de un píxel. Es necesario para excluir las separaciones de dos píxeles que serán visibles al seleccionar (resaltar) los elementos cuando el cursor se coloca encima de ellos. Vamos a tomarlo en consideración a la hora de calcular el ancho del fondo de la lista y la barra de desplazamiento. Después de crear todos los elementos, al final del método se resalta el elemento y se guarda su texto

//+------------------------------------------------------------------+
//| Crea los elementos de la lista                                            |
//+------------------------------------------------------------------+
bool CListView::CreateList(void)
  {
//--- Coordenadas
   int x =CElement::X()+1;
   int y =0;
//--- Cálculo del ancho de los elementos de la lista
   int w=(m_items_total>m_visible_items_total) ? CElement::XSize()-m_scrollv.ScrollWidth() : CElement::XSize()-2;
//---
   for(int i=0; i<m_visible_items_total; i++)
     {
      //--- Formación del nombre del objeto
      string name=CElement::ProgramName()+"_listview_edit_"+(string)i+"__"+(string)CElement::Id();
      //--- Cálculo de la coordenada Y
      y=(i>0) ? y+m_item_y_size-1 : CElement::Y()+1;
      //--- Creación del objeto
      if(!m_items[i].Create(m_chart_id,name,m_subwin,x,y,w,m_item_y_size))
         return(false);
      //--- Establecemos las propiedades
      m_items[i].Description(m_value_items[i]);
      m_items[i].TextAlign(m_align_mode);
      m_items[i].Font(FONT);
      m_items[i].FontSize(FONT_SIZE);
      m_items[i].Color(m_item_text_color);
      m_items[i].BackColor(m_item_color);
      m_items[i].BorderColor(m_item_color);
      m_items[i].Corner(m_corner);
      m_items[i].Anchor(m_anchor);
      m_items[i].Selectable(false);
      m_items[i].Z_Order(m_item_zorder);
      m_items[i].ReadOnly(true);
      m_items[i].Tooltip("\n");
      //--- Coordenadas
      m_items[i].X(x);
      m_items[i].Y(y);
      //--- Tamaños
      m_items[i].XSize(w);
      m_items[i].YSize(m_item_y_size);
      //--- Márgenes desde el punto extremo del panel
      m_items[i].XGap(x-m_wnd.X());
      m_items[i].YGap(y-m_wnd.Y());
      //--- Guardamos el puntero del objeto
      CElement::AddToArray(m_items[i]);
     }
//--- Resaltar el elemento seleccionado
   HighlightSelectedItem();
//--- Guardamos el texto del elemento seleccionado
   m_selected_item_text=m_value_items[m_selected_item_index];
   return(true);
  }

Al crear la barra de desplazamiento en el método CListView::CreateScrollV(), al principio se comprueba la relación entre el número de elementos de la lista y en su parte visible. Si resulta que el número total de elementos en la lista es menor o igual al número de elementos en su parte visible, no tiene sentido seguir adelante y el programa sale del método. Luego (1) se guarda el puntero al formulario, (2) se calculan las coordenadas y (3) se establecen las propiedades. El identificador del control para la barra de desplazamiento tiene que ser el mismo que tiene el el control cuya parte está formando. Los modos del control desplegable también tienen que coincidir.

//+------------------------------------------------------------------+
//| Crea el Scroll vertical                                      |
//+------------------------------------------------------------------+
bool CListView::CreateScrollV(void)
  {
//--- Si el número de elementos supera el tamaño de la lista,
//    creamos el Scroll vertical
   if(m_items_total<=m_visible_items_total)
      return(true);
//--- Guardar el puntero del formulario
   m_scrollv.WindowPointer(m_wnd);
//--- Coordenadas
   int x=CElement::X()+m_area.X_Size()-m_scrollv.ScrollWidth();
   int y=CElement::Y();
//--- Establecemos las propiedades
   m_scrollv.Id(CElement::Id());
   m_scrollv.XSize(m_scrollv.ScrollWidth());
   m_scrollv.YSize(CElement::YSize());
   m_scrollv.AreaBorderColor(m_area_border_color);
   m_scrollv.IsDropdown(CElement::IsDropdown());
//--- Crear la barra de desplazamiento
   if(!m_scrollv.CreateScroll(m_chart_id,m_subwin,x,y,m_items_total,m_visible_items_total))
      return(false);
//---
   return(true);
  }

El tamaño de la lista por el eje Y se calcula en el método principal (público) para la creación de la lista CListView::CreateListView(). Como ya hemos mencionado antes, durante el cálculo del tamaño hay que tener en cuenta que los elementos de la lista se solapan en un píxel. También vamos a recordar que el array de los elementos debe encontrarse estrictamente dentro del fondo de la lista para no cubrir su marco.

//+------------------------------------------------------------------+
//| Crea la lista                                                  |
//+------------------------------------------------------------------+
bool CListView::CreateListView(const long chart_id,const int window,const int x,const int y)
  {
//--- Salir si no hay puntero al formulario
   if(::CheckPointer(m_wnd)==POINTER_INVALID)
     {
      ::Print(__FUNCTION__," > Antes de crear la lista, hay que pasar a la clase "
              "el puntero al formulario: CListView::WindowPointer(CWindow &object)");
      return(false);
     }
//--- Inicialización de variables
   m_id       =m_wnd.LastId()+1;
   m_chart_id =chart_id;
   m_subwin   =window;
   m_x        =x;
   m_y        =y;
   m_y_size   =m_item_y_size*m_visible_items_total-(m_visible_items_total-1)+2;
//--- Márgenes desde el punto extremo
   CElement::XGap(m_x-m_wnd.X());
   CElement::YGap(m_y-m_wnd.Y());
//--- Crear el botón
   if(!CreateArea())
      return(false);
   if(!CreateList())
      return(false);
   if(!CreateScrollV())
      return(false);
//--- Ocultar el elemento si es la ventana de diálogo o está minimizada
   if(m_wnd.WindowType()==W_DIALOG || m_wnd.IsMinimized())
      Hide();
//---
   return(true);
  }

Vamos a dar al usuario de la librería la posibilidad de decidir por sí mismo si necesita resaltar los elementos al situar el cursor sobre ellos. Por defecto, será establecido el modo cuando el resalto está desactivado. Como una propiedad adicional ajustable, vamos a crear el modo para establecer la alineación del texto: (1) por la izquierda, (2) por la derecha, (3) centrado. Por defecto, la alineación será por la izquierda. 

class CListView : public CElement
  {
private:
   //--- Modo del resalto al apuntar con el cursor
   bool              m_lights_hover;
   //--- Modo de alineación del texto dentro de la lista
   ENUM_ALIGN_MODE   m_align_mode;
   //---
public:
   //--- (1) Modo del resalto de los elementos al apuntar con el cursor, (2) modo de alineación del texto
   void              LightsHover(const bool state)                       { m_lights_hover=state;             }
   void              TextAlign(const ENUM_ALIGN_MODE align_mode)         { m_align_mode=align_mode;          }
  };
//+------------------------------------------------------------------+
//| Constructor                                                      |
//+------------------------------------------------------------------+
CListView::CListView(void) : m_lights_hover(false),
                             m_align_mode(ALIGN_LEFT)
  {
//--- ...
  }

Antes de crear la lista hay que inicializar el array de datos que debe contener. Para eso creamos el método CListView::ValueToList(), donde va a realizarse la comprobación del tamaño del array y la corrección del índice en caso de salir fuera del rango del array.

class CListView : public CElement
  {
public:
   //--- Establecer el valor en la lista según el índice especificado de la fila
   void              ValueToList(const int item_index,const string value);
  };
//+------------------------------------------------------------------+
//| Guarda el valor devuelto en la lista según el índice especificado     |
//+------------------------------------------------------------------+
void CListView::ValueToList(const int item_index,const string value)
  {
   int array_size=::ArraySize(m_value_items);
//--- Si el menú contextual no tiene elementos, avisar sobre ello
   if(array_size<1)
     {
      ::Print(__FUNCTION__," > ¡La llamada a este método debe realizarse "
              "cuando en la lista hay por lo menos un elemento!
     }
//--- Corrección en caso de salir fuera del diapasón
   int i=(item_index>=array_size)? array_size-1 : (item_index <0)? 0 : item_index;
//--- Guardamos el valor en la lista
   m_value_items[i]=value;
  }

A continuación, vamos a probar la colocación de la lista con la barra de desplazamiento vertical, y luego vamos a añadirle poco a poco todos los métodos necesarios para su manejo. 



Prueba de colocación de la lista

Puesto que el archivo ListView.mqh ya está incluido en la librería, la clase del control “Lista” (CListView) ya está disponible para el uso en la clase personalizada. Pero antes de probar la colocación de la lista, hay que introducir algunas adiciones en la clase CWndContainer. El Control “Lista” es compuesto, por eso hay que asegurar la adición del puntero de la barra de desplazamiento a la base de los controles. 

Para eso escribiremos el método CWndContainer::AddListViewElements(). Al principio de este método se encuentra la comprobación del nombre de la clase. Si resulta que no se trata de la lista, el programa saldrá del método. Luego aumentamos el tamaño del array común de punteros, recibimos el puntero con el tipo del control necesario y lo guardamos.

class CWndContainer
  {
private:
  //--- Guarda los punteros a los objetos de la lista en la base
   bool              AddListViewElements(const int window_index,CElement &object);
  };
//+------------------------------------------------------------------+
//| Guarda los punteros a los objetos de la lista en la base                     |
//+------------------------------------------------------------------+
bool CWndContainer::AddListViewElements(const int window_index,CElement &object)
  {
//--- Salimos si no es una lista
   if(object.ClassName()!="CListView")
      return(false);
//--- Obtenemos el puntero a la lista
   CListView *lv=::GetPointer(object);
//--- Aumentar el array de controles
   int size=::ArraySize(m_wnd[window_index].m_elements);
   ::ArrayResize(m_wnd[window_index].m_elements,size+1);
//--- Obtenemos el puntero de la barra de desplazamiento
   CScrollV *sv=lv.GetScrollVPointer();
//--- Guardamos el control en la base
   m_wnd[window_index].m_elements[size]=sv;
   return(true);
  }

La llamada al método CWndContainer::AddListViewElements() se realiza en el método principal público para añadir los controles a la base: 

//+------------------------------------------------------------------+
//| Añade el puntero al array de controles                           | 
//+------------------------------------------------------------------+
void CWndContainer::AddToElementsArray(const int window_index,CElement &object)
  {
//--- Si en la base no hay formularios para los controles
//--- Si se solicita el formulario que no existe
//--- Añadimos al array común de controles
//--- Añadir los objetos del control al array común de objetos
//--- Recordamos id del último control en todos los formularios
//--- Aumentamos el contador de los identificadores de controles

//--- Guarda los punteros a los objetos del menú contextual en la base
//--- Guarda los punteros a los objetos del menú principal en la base
//--- Guarda los punteros a los objetos del botón de división en la base
//--- Guarda los punteros a los objetos de las descripciones emergentes en la base
//--- Guarda los punteros a los objetos de la lista en la base
   if(AddListViewElements(window_index,object))
      return;
  }

Para la prueba vamos a usar el EA de la parte anterior (4) de la serie. Dejaremos dentro el menú principal con su menú contextual y la barra de estado. Todos los demás controles tienen que ser eliminados. Creamos la instancia de la clase en la clase personalizada, declaramos el método para la creación del control y determinamos los márgenes desde el punto extremos del formulario:

class CProgram : public CWndEvents
  {
private:
   //--- Lista
   CListView         m_listview1;
   //--- 
private:
   //--- Lista
#define LISTVIEW1_GAP_X       (2)
#define LISTVIEW1_GAP_Y       (43)
   bool              CreateListView1(void);
  };

Abajo se muestra el código del método de la creación de la lista. En el ejemplo la lista tendrá 20 elementos. La parte visible de la lista va a componerse de 10 elementos. Activamos el modo para resaltar los elementos al situar el cursor sobre ellos. Seleccionamos el sexto (5) elemento de la lista. Como se trata de un ejemplo, vamos a llenar la lista con el texto «SYMBOL» con el número de orden del elemento

//+------------------------------------------------------------------+
//| Crea la lista 1                                                 |
//+------------------------------------------------------------------+
bool CProgram::CreateListView1(void)
  {
//--- Tamaño de la lista
#define ITEMS_TOTAL1 20
//--- Guardamos el puntero a la ventana
   m_listview1.WindowPointer(m_window1);
//--- Coordenadas
   int x=m_window1.X()+LISTVIEW1_GAP_X;
   int y=m_window1.Y()+LISTVIEW1_GAP_Y;
//--- Establecemos las propiedades antes de la creación
   m_listview1.XSize(100);
   m_listview1.LightsHover(true);
   m_listview1.ListSize(ITEMS_TOTAL1);
   m_listview1.VisibleListSize(10);
   m_listview1.AreaBorderColor(clrDarkGray);
   m_listview1.SelectedItemIndex(5);
//--- Obtenemos el puntero de la barra de desplazamiento
   CScrollV *sv=m_listview1.GetScrollVPointer();
//--- Propiedades del scroll
   sv.ThumbBorderColor(C'190,190,190');
   sv.ThumbBorderColorHover(C'180,180,180');
   sv.ThumbBorderColorPressed(C'160,160,160');
//--- Llenamos la lista con los datos
   for(int r=0; r<ITEMS_TOTAL1; r++)
      m_listview1.ValueToList(r,"SYMBOL "+string(r));
//--- Creamos la lista
   if(!m_listview1.CreateListView(m_chart_id,m_subwin,x,y))
      return(false);
//--- Añadimos el puntero al control a la base
   CWndContainer::AddToElementsArray(0,m_listview1);
   return(true);
  }

La llamada al método de la creación de la lista debe realizarse en el método principal de la creación de la interfaz gráfica: 

//+------------------------------------------------------------------+
//| 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
//--- Lista
   if(!CreateListView1())
      return(false);
//--- Redibujar el gráfico
   m_chart.Redraw();
   return(true);
  }

Ahora ya se puede compilar el código y cargar el programa en el gráfico. Si ha hecho todo bien, en el gráfico aparece el formulario que se muestra a continuación:

 Fig. 2. Prueba del control “Lista”.

Fig. 2. Prueba del control “Lista”.

 

Se ve bastante bien, pero en este momento no se puede manejar la lista, ninguno de sus objetos va a reaccionar al cursor del ratón. A continuación, pasamos al desarrollo de los métodos que permitirán hacerlo.

 


Métodos para la gestión del control

Para empezar, vamos a crear el método que permite cambiar el color de los elementos al situar el cursor sobre ellos. Luego, vamos a necesitar el método que permite recuperar los colores predefinidos.

class CListView : public CElement
  {
public:
   //--- (1) Resetear el color del elemento de la lista, (2) cambio del color de los elementos al situar el cursor
   void              ResetItemsColor(void);
   void              ChangeItemsColor(const int x,const int y);
  };

En el método CListView::ResetItemsColor() se resetean los colores de todos los elementos, a excepción del seleccionado. Al principio del método, se identifica la posición en la que se encuentra el deslizador de la barra de deslizamiento. Este valor se recibe en la variable que va a utilizarse en el ciclo como contador para la identificación del elemento seleccionado.

//+------------------------------------------------------------------+
//| Resetear colores de elementos de la lista                                       |
//+------------------------------------------------------------------+
void CListView::ResetItemsColor(void)
  {
//--- Obtenemos la posición actual del deslizador de la barra de desplazamiento
   int v=m_scrollv.CurrentPos();
//--- Repasamos la parte visible de la lista en el ciclo
   for(int i=0; i<m_visible_items_total; i++)
     {
      //--- Aumentamos el contador si estamos dentro del diapasón de la lista
      if(v>=0 && v<m_items_total)
         v++;
      //--- Omitimos el elemento seleccionado
      if(m_selected_item_index==v-1)
         continue;
      //--- Establecer el color (fondo, texto)
      m_items[i].BackColor(m_item_color);
      m_items[i].Color(m_item_text_color);
     }
  }

Al principio del método CListView::ChangeItemsColor() hay que realizar varias comprobaciones. El programa saldrá del método en las siguientes situaciones:

  • si el modo para resaltar los elementos al situar el cursor sobre ellos no está activado;
  • si se utiliza la barra de desplazamiento;
  • si el elemento no es desplegable y el formulario está bloqueado.

A continuación, igual como en muchos otros métodos de esta clase, la variable recibe la posición actual de la barra de deslizamiento. Luego, esta variable se utiliza para identificar en el ciclo el elemento seleccionado con el fin de omitirlo, porque no es necesario cambiar su color. El método recibe las coordenadas del cursor que se utilizan para identificar en el ciclo sobre qué elemento exactamente se sitúa el cursor. Para más detalles CListView::ChangeItemsColor() véase el código de abajo. 

//+------------------------------------------------------------------+
//| Cambio del color de la línea de la lista al situar el cursor encima              |
//+------------------------------------------------------------------+
void CListView::ChangeItemsColor(const int x,const int y)
  {
//--- Salimos si el resalto está desactivado al apuntar con el cursor o el srolling está activo
   if(!m_lights_hover || m_scrollv.ScrollState())
      return;
//--- Salimos si el control no es desplegable y el formulario está bloqueado
   if(!CElement::IsDropdown() && m_wnd.IsLocked())
      return;
//--- Obtenemos la posición actual del deslizador de la barra de desplazamiento
   int v=m_scrollv.CurrentPos();
//--- Buscamos el elemento sobre el que se sitúa el cursor y lo resaltamos
   for(int i=0; i<m_visible_items_total; i++)
     {
      //--- Aumentamos el contador si estamos dentro del diapasón de la lista
      if(v>=0 && v<m_items_total)
         v++;
      //--- Omitimos el elemento seleccionado
      if(m_selected_item_index==v-1)
         continue;
      //--- Si el cursor se encuentra sobre este elemento, lo resaltamos
      if(x>m_items[i].X() && x<m_items[i].X2() && y>m_items[i].Y() && y<m_items[i].Y2())
        {
         m_items[i].BackColor(m_item_color_hover);
         m_items[i].Color(m_item_text_color_hover);
        }
      //--- Si el cursor no se encuentra sobre este elemento, pues ponemos el color correspondiente a este estado
      else
        {
         m_items[i].BackColor(m_item_color);
         m_items[i].Color(m_item_text_color);
        }
     }
  }

Ahora tenemos que usar el método CListView::ChangeItemsColor() en el manejador de eventos de la clase CListView:

//+------------------------------------------------------------------+
//| Manejador de eventos                                               |
//+------------------------------------------------------------------+
void CListView::OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam)
  {
   if(id==CHARTEVENT_MOUSE_MOVE)
     {
      //--- Lista ocultada
      if(!CElement::IsVisible())
         return;
      //--- Coordenadas
      int x=(int)lparam;
      int y=(int)dparam;
      //--- Cambia el color de las líneas de la lista al situar el cursor encima
      ChangeItemsColor(x,y);
      return;
     }
  }

Si compilamos el programa para las pruebas ahora, entonces al situar el cursor sobre los elementos de la lista, su color va a cambiar (ver la imagen de abajo):

Fig. 3. Prueba del cambio del color de los elementos al situar el cursor encima. 

Fig. 3. Prueba del cambio del color de los elementos al situar el cursor encima.

Para desplazar la lista respecto al deslizador de la barra de desplazamiento, escribiremos el método CListView::ShiftList(). Este método también empieza con el guardado de la posición actual del deslizador en una variable. Luego, igual que en el método anterior, vamos a usarla en el ciclo como el contador para identificar el elemento seleccionado en la lista, así como para el desplazamiento de datos (ver el código de abajo). 

class CListView : public CElement
  {
public:
   //--- Desplazamiento de la lista
   void              ShiftList(void);
  };
//+------------------------------------------------------------------+
//| Mueve la lista respecto a la barra de desplazamiento                    |
//+------------------------------------------------------------------+
void CListView::ShiftList(void)
  {
//--- Obtenemos la posición actual del deslizador de la barra de desplazamiento
   int v=m_scrollv.CurrentPos();
//--- Repasamos la parte visible de la lista en el ciclo
   for(int i=0; i<m_visible_items_total; i++)
     {
      //--- Si estamos dentro del diapasón de la lista
      if(v>=0 && v<m_items_total)
        {
         //--- Desplazamiento del fondo, color del fondo y color del texto
         m_items[i].Description(m_value_items[v]);
         m_items[i].BackColor((m_selected_item_index==v) ? m_item_color_selected : m_item_color);
         m_items[i].Color((m_selected_item_index==v) ? m_item_text_color_selected : m_item_text_color);
         //--- Aumentamos el contador
         v++;
        }
     }
  }

Hay que llamar al método CListView::ShiftList() en el manejador CListView::OnEvent() con la condición de que el método de la barra de desplazamiento CScrollV::ScrollBarControl() devuelva el valor true. O sea, eso va a significar que el manejo del deslizador de la barra ha sido activado.

class CListView : public CElement
  {
private:
   //--- Estado del botón izquierdo del ratón (pulsado/suelto)
   bool              m_mouse_state;
  };
//+------------------------------------------------------------------+
//| Manejador de eventos                                               |
//+------------------------------------------------------------------+
void CListView::OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam)
  {
   if(id==CHARTEVENT_MOUSE_MOVE)
     {
      //--- Lista ocultada
      if(!CElement::IsVisible())
         return;
       //--- Coordenadas y el estado del botón izquierdo del ratón
      int x=(int)lparam;
      int y=(int)dparam;
      m_mouse_state=(bool)int(sparam);
      //--- Comprobación del foco sobre la lista
      CElement::MouseFocus(x>CElement::X() && x<CElement::X2() && 
                           y>CElement::Y() && y<CElement::Y2());
      //--- Desplazamos la lista si el manejo del deslizador está activado
      if(m_scrollv.ScrollBarControl(x,y,m_mouse_state))
         ShiftList();
      //--- Cambia el color de las líneas de la lista al situar el cursor encima
      ChangeItemsColor(x,y);
      return;
     }
  }

Ahora después de compilar el programa, podremos manejar la lista mediante el deslizador de la barra de desplazamiento (ver la imagen de abajo). Todo está implementado de tal manera que incluso si el cursor sale fuera del marco del deslizador, tras haber sido apretado con el botón izquierdo del ratón y el manejo haya sido pasado a la barra de desplazamiento, todavía se podrá mover el deslizador.

 Fig.4. Manejo de la lista mediante el deslizador de la barra de desplazamiento.

Fig. 4. Manejo de la lista mediante el deslizador de la barra de desplazamiento.

 

Necesitamos un método para identificar el clic sobre uno de los elementos de la lista. Vamos a crear este método privado y llamarlo CListView::OnClickListItem(). Además, necesitaremos el método privado CListView::IdFromObjectName() para extraer el identificador del elemento desde el nombre del objeto que ya ha sido mostrado varias veces en otras clase de otros controles de la librería.

A parte de eso, también necesitaremos el identificador único del evento del clic en el elemento de la lista (ON_CLICK_LIST_ITEM). Vamos a añadirlo al archivo Defines.mqh

//+------------------------------------------------------------------+
//|                                                      Defines.mqh |
//|                        Copyright 2015, MetaQuotes Software Corp. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
#define ON_CLICK_LIST_ITEM        (16) // Selección del elemento en la lista

En el inicio del método CListView::OnClickListItem() se encuentran las comprobaciones del nombre e identificador del objeto que ha sido pulsado. Luego, a la variable local se le asigna la posición actual del deslizador. Esta variable luego se utiliza como contador en el ciclo para identificar el índice y el texto del elemento. Al final del método se envía el mensaje con (1) el identificador del evento ON_CLICK_LIST_ITEM, (2) identificador del control y (3) el texto del elemento actual seleccionado. 

class CListView : public CElement
  {
private:
   //--- Procesamiento del clic en un elemento de la lista
   bool              OnClickListItem(const string clicked_object);
   //--- Obtener el identificador desde el nombre del elemento de la lista
   int               IdFromObjectName(const string object_name);
  };
//+------------------------------------------------------------------+
//| Procesamiento del clic en un elemento de la lista                               |
//+------------------------------------------------------------------+
bool CListView::OnClickListItem(const string clicked_object)
  {
//--- Salimos si el clic ha sido hecho fuera del elemento del menú
   if(::StringFind(clicked_object,CElement::ProgramName()+"_listview_edit_",0)<0)
      return(false);
//--- Obtenemos el identificador e índice desde el nombre del objeto
   int id=IdFromObjectName(clicked_object);
//--- Salir si el identificador no coincide
   if(id!=CElement::Id())
      return(false);
//--- Obtenemos la posición actual del deslizador de la barra de desplazamiento
   int v=m_scrollv.CurrentPos();
//--- Repasamos la parte visible de la lista
   for(int i=0; i<m_visible_items_total; i++)
     {
      //--- Si ha sido seleccionado este elemento de la lista
      if(m_items[i].Name()==clicked_object)
        {
         m_selected_item_index =v;
         m_selected_item_text  =m_value_items[v];
        }
      //--- Si estamos dentro del diapasón de la lista
      if(v>=0 && v<m_items_total)
         //--- Aumentamos el contador
         v++;
     }
//--- Enviamos el mensaje sobre ello
   ::EventChartCustom(m_chart_id,ON_CLICK_LIST_ITEM,CElement::Id(),0,m_selected_item_text);
   return(true);
  }

Ahora tenemos todos los métodos necesarios para manejar la lista. Nos queda sólo colocar el código dentro del manejador del evento de la lista CListView::OnEvent() tal como se muestra en código de abajo Al pulsar uno de los elementos de la lista, se llama el método CListView::HighlightSelectedItem(). Aquí mismo se monitorea el clic en los botones de la barra de desplazamiento. Si uno de sus botones ha sido pulsado, la lista se desplaza respecto a la posición actual del deslizador.

//+------------------------------------------------------------------+
//| Manejador de eventos                                               |
//+------------------------------------------------------------------+
void CListView::OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam)
  {
//--- Procesamiento del clic en los objetos
   if(id==CHARTEVENT_OBJECT_CLICK)
     {
      //--- Si un elemento de la lista ha sido pulsado
      if(OnClickListItem(sparam))
        {
        //--- Resaltar la línea
         HighlightSelectedItem();
         return;
        }
       //--- Si han sido pulsados los botones de la barra de desplazamiento de la lista
      if(m_scrollv.OnClickScrollInc(sparam) || m_scrollv.OnClickScrollDec(sparam))
        {
         //--- Mueve la lista respecto a la barra de desplazamiento
         ShiftList();
         return;
        }
     }
  }


 

Avance/retroceso rápido de la lista

Hemos terminado el desarrollo del mínimo necesario para el manejo de la lista. Pero no hay lugar para pocas posibilidades. Por eso vamos a crear otro método privado más. Nos permitirá realizar el avance/retroceso rápido de la lista manteniendo pulsado uno de los botones de la barra de desplazamiento.

Hay que hacer que haya un pequeño retardo antes de que empiece el avance/retroceso rápido de la lista después de que apretemos el botón izquierdo del ratón sobre los botones de la barra de desplazamiento. Si no lo hacemos, el avance/retroceso va a activarse inmediatamente, incluyendo los momentos cuando sólo hay que hacer un clic en el botón con el fin de desplazar la lista sólo a un elemento. En el archivo Defines.mqh hay que añadir el identificador SPIN_DELAY_MSC con el valor -450. Es decir, el retardo será de 450 milisegundos. 

//+------------------------------------------------------------------+
//|                                                      Defines.mqh |
//|                        Copyright 2015, MetaQuotes Software Corp. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
//--- Retardo antes de la activación del contador (milisegundos)
#define SPIN_DELAY_MSC (-450)

Abajo se muestra el código del método CListView::FastSwitching(). Además, hay que declarar el campo m_timer_counter que va a utilizarse como el contador del temporizador. En el inicio del método CListView::FastSwitching() se comprueba el foco sobre la lista. Si no hay foco, entonces el programa sale del método. Luego, si el botón del ratón está suelto, al contador se le asigna el valor del retardo (en nuestro caso -450 ms), y aquí todo se termina. Si el botón del ratón esta apretado, el valor del contador se aumenta al paso del temporizador establecido en la librería (en este caso 16 ms). Luego hay una condición que impide que el programa siga trabajando hasta que el valor del contador sea igual o más de cero. En cuanto esta condición se cumpla, se comprueba el estado de los botones de la barra de desplazamiento. Dependiendo del botón que se mantiene pulsado, se llama el método para imitar el clic en el botón, y luego la lista se desplaza avanzando o retrocediendo de acuerdo con la posición actual del deslizador. 

class CListView : public CElement
  {
private:
   //--- Contador del deslizador para avanzar/retroceder la lista
   int               m_timer_counter;
private:
   //--- Avance/retroceso rápido de la lista
   void              FastSwitching(void);
  };
//+------------------------------------------------------------------+
//| Avance/retroceso rápido de la barra de desplazamiento                            |
//+------------------------------------------------------------------+
void CListView::FastSwitching(void)
  {
//--- Salimos si no hay foco
   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;
      //--- Si la barra de desplazamiento arriba
      if(m_scrollv.ScrollIncState())
         m_scrollv.OnClickScrollInc(m_scrollv.ScrollIncName());
      //--- Si la barra de desplazamiento abajo
      else if(m_scrollv.ScrollDecState())
         m_scrollv.OnClickScrollDec(m_scrollv.ScrollDecName());
      //--- Desplaza la lista
      ShiftList();
     }
  }

La llamada al método CListView::FastSwitching() se realiza en el temporizador CListView::OnEventTimer(), tal como se muestra abajo. Si la lista es un control desplegable, no hay comprobaciones adicionales. De lo contrario, hay que comprobar si el formulario está bloqueado o no en este momento. 

//+------------------------------------------------------------------+
//| Temporizador                                                           |
//+------------------------------------------------------------------+
void CListView::OnEventTimer(void)
  {
//--- Si el control es desplegable
   if(CElement::IsDropdown())
       //--- Avance/retroceso de la lista
      FastSwitching();
 //--- Si el control no es desplegable, tomamos en cuenta la disponibilidad del formulario en este momento
   else
     {
      //--- Seguimos el avance de la lista sólo si el formulario no está bloqueado
      if(!m_wnd.IsLocked())
         FastSwitching();
     }
  }

Tenemos preparados todos los métodos para la gestión de la lista. Ahora podemos probar todo eso. Vamos a añadir otras dos listas al que ya hemos hecho antes:

Fig. 5. Prueba de tres listas en la interfaz gráfica. 

Fig. 5. Prueba de tres listas en la interfaz gráfica.

Vamos a recibir los mensajes de las listas (CProgram) en el manejador de eventos de la clase personalizada: 

//+------------------------------------------------------------------+
//| Manejador de eventos                                               |
//+------------------------------------------------------------------+
void CProgram::OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam)
  {
//--- Evento del clic en un elemento de la lista
   if(id==CHARTEVENT_CUSTOM+ON_CLICK_LIST_ITEM)
     {
      if(lparam==m_listview1.Id())
         ::Print(__FUNCTION__," > Es el mensaje de la primera lista > id: ",id,"; lparam: ",lparam,"; dparam: ",dparam,"; sparam: ",sparam);
      else if(lparam==m_listview2.Id())
         ::Print(__FUNCTION__," > Es el mensaje de la segunda lista > id: ",id,"; lparam: ",lparam,"; dparam: ",dparam,"; sparam: ",sparam);
      else if(lparam==m_listview3.Id())
         ::Print(__FUNCTION__," > Es el mensaje de la tercera lista > id: ",id,"; lparam: ",lparam,"; dparam: ",dparam,"; sparam: ",sparam);
     }
  }

Cuando se pulsan los elementos del menú, en el registro se muestran los mensajes como se muestra: 

2016.01.16 13:02:00.085 TestLibrary (GBPUSD,D1) CProgram::OnEvent > Es el mensaje de la primera lista > id: 1016; lparam: 7; dparam: 0.0; sparam: SYMBOL 11
2016.01.16 13:01:59.056 TestLibrary (GBPUSD,D1) CProgram::OnEvent > Es el mensaje de la tercera lista > id: 1016; lparam: 9; dparam: 0.0; sparam: SYMBOL 12
2016.01.16 13:01:58.479 TestLibrary (GBPUSD,D1) CProgram::OnEvent > Es el mensaje de la sgunda lista > id: 1016; lparam: 8; dparam: 0.0; sparam: SYMBOL 9
2016.01.16 13:01:57.868 TestLibrary (GBPUSD,D1) CProgram::OnEvent > Es el mensaje de la tercera lista > id: 1016; lparam: 9; dparam: 0.0; sparam: SYMBOL 19
2016.01.16 13:01:56.854 TestLibrary (GBPUSD,D1) CProgram::OnEvent > Es el mensaje de la segunda lista > id: 1016; lparam: 8; dparam: 0.0; sparam: SYMBOL 4
2016.01.16 13:01:56.136 TestLibrary (GBPUSD,D1) CProgram::OnEvent > Es el mensaje de la primera lista > id: 1016; lparam: 7; dparam: 0.0; sparam: SYMBOL 9
2016.01.16 13:01:55.433 TestLibrary (GBPUSD,D1) CProgram::OnEvent > Es el mensaje de la primera lista > id: 1016; lparam: 7; dparam: 0.0; sparam: SYMBOL 14

 


Conclusión

En este artículo hemos analizado el control compuesto “Lista”. Además, hemos visto un ejemplo del uso de la barra de desplazamiento vertical. En el siguiente artículo (el tercer capítulo de la quinta parte) vamos a hablar de otro control compuesto, “Lista combinada” (combobox). 

Más abajo puede descargar el material de la quinta 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 quinta parte:

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

Archivos adjuntos |
Por dónde comenzar a crear un robot comercial para la Bolsa de Moscú MOEX Por dónde comenzar a crear un robot comercial para la Bolsa de Moscú MOEX

Muchos tráders de la Bolsa de Moscú querrían automatizar sus algoritmos comerciales, pero no saben por dónde empezar. El lenguaje MQL5 propone no solo un conjunto enorme de funciones comerciales, sino también clases preparadas, que facilitan al máximo los primeros pasos en el trading automático.

¿Cómo copiar señales con la ayuda de un asesor según sus propias normas? ¿Cómo copiar señales con la ayuda de un asesor según sus propias normas?

Al suscribirse a una señal puede darse la situación siguiente: su cuenta comercial tiene un apalancamiento de 1:100, el proveedor tiene un apalancamiento de 1:500 y comercia con un lote mínimo, y sus balances comerciales son prácticamente iguales, además, el coeficiente de copiado es del 10% al 15%. En este artículo hablaremos de cómo aumentar el coeficiente de copiado en ese caso.

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 "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)

Este artículo empieza la sexta parte de la serie sobre el desarrollo de la librería para la creación de las interfaces gráficas en los terminales MetaTrader. En el primer capítulo hablaremos sobre los siguientes controles: “casilla de verificación”, “campo de edición” y los tipos combinados de estos controles.