Interfaces gráficas III: Grupos de botones simples y multifuncionales (Capítulo 2)

Anatoli Kazharski | 26 abril, 2016

Í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 tercera pare de la serie se trataba de los botones simples y botones multifuncionales. En el segundo capítulo hablaremos de los grupos de botones interconectados.  Se utilizan para crear los elementos en las aplicaciones que permiten al usuario seleccionar una opción de varias disponibles en el conjunto (grupo).

 


Desarrollando la clase para la creación de los grupos de botones simples

El grupo de botones simples representa un array de objetos gráficos tipo OBJ_BUTTON. La diferencia característica de estos controles es que sólo un botón del grupo puede estar presionado al mismo tiempo. En este momento, hay dos maneras para crear la clase de este control:

  1. crear el grupo usando el control ya implementad tipo CSimpleButton;
  2. crear el grupo de los objetos primitivos tipo CButton.

La segunda opción es más sencilla porque no hace falta crear el método adicional para que cada control tipo CSimpleButton entre en la base de los punteros. Por eso vamos a utilizar esta opción. 

Creamos el archivo ButtonsGroup.mqh con la clase CButtonsGroup en la carpeta de todos los controles (Controls) y lo incluimos en el archivo WndContainer.mqh. La clase debe contener los métodos virtuales estándar y los punteros al formulario, como ya ha sido mostrado varias veces para todos los demás controles desarrollados anteriormente. Por eso no vamos a mostrarlos aquí otra vez, e iremos directamente a la descripción de las propiedades y los métodos para su construcción. 

Algunas propiedades serán comunes para cada botón dentro del grupo, pero también habrán aquellas que son únicas. Vamos a especificar estos dos grupos de propiedades referentes a la apariencia de los botones.

Propiedades comunes de los botones:

  • alto;
  • color de botones bloqueados;
  • color de los marcos en el modo disponible y bloqueado;
  • colores del texto en estados diferentes;
  • prioridad para el clic izquierdo del ratón.

Propiedades únicas de los botones:

  • estado del botón (pulsado/suelto);
  • márgenes desde el punto extremo del formulario;
  • texto;
  • ancho;
  • colores del botón en diferentes estados;
  • gradientes para los botones.

Los márgenes para cada botón permiten organizar su ubicación en cualquier secuencia:

Fig. 1. Ejemplos de organización de botones dentro del grupo.

Fig. 1. Ejemplos de organización de botones dentro del grupo.


Declaramos los arrays dinámicos para los objetos tipo CButton y las propiedades únicas de los botones: 

//+------------------------------------------------------------------+
//| Clase para la creación del grupo de botones simples                         |
//+------------------------------------------------------------------+
class CButtonsGroup : public CElement
  {
private:
   //--- Objetos para crear el botón
   CButton           m_buttons[];
   //--- Gradientes de los botones
   struct ButtonsGradients
     {
      color             m_buttons_color_array[];
     };
   ButtonsGradients  m_buttons_total[];
   //--- Propiedades de botones:
       Arrays para las propiedades únicas de los botones:
   bool              m_buttons_state[];
   int               m_buttons_x_gap[];
   int               m_buttons_y_gap[];
   string            m_buttons_text[];
   int               m_buttons_width[];
   color             m_buttons_color[];
   color             m_buttons_color_hover[];
   color             m_buttons_color_pressed[];
   //--- Alto de botones
   int               m_button_y_size;
   //--- Color de botones bloqueados
   color             m_back_color_off;
   //--- Color del marco en el estado activo y bloqueado
   color             m_border_color;
   color             m_border_color_off;
   //--- Colores del texto
   color             m_text_color;
   color             m_text_color_off;
   color             m_text_color_pressed;
   //--- Prioridad para el clic izquierdo del ratón
   int               m_buttons_zorder;
   //---
public:
   //--- Número de botones
   int               ButtonsTotal(void)                       const { return(::ArraySize(m_buttons));  }
   //--- (1) Alto de botones
   void              ButtonYSize(const int y_size)                  { m_button_y_size=y_size;          }
   //--- (1) Colores del fondo del botón bloqueado y del marco ((2) disponible/(3) bloqueado)
   void              BackColorOff(const color clr)                  { m_back_color_off=clr;            }
   void              BorderColor(const color clr)                   { m_border_color=clr;              }
   void              BorderColorOff(const color clr)                { m_border_color_off=clr;          }
   //--- Colores del texto
   void              TextColor(const color clr)                     { m_text_color=clr;                }
   void              TextColorOff(const color clr)                  { m_text_color_off=clr;            }
   void              TextColorPressed(const color clr)              { m_text_color_pressed=clr;        }
  };

El tamaño de los arrays dinámicos será definido en el momento de la formación del grupo de botones antes de su creación (colocación en el gráfico). Con cada llamada al método CButtonsGroup::AddButton(), los array se aumentan a un elemento que se rellena con parámetros pasados.

class CButtonsGroup : public CElement
  {
public:
   //--- Añade un botón con propiedades especificadas antes de la creación
   void              AddButton(const int x_gap,const int y_gap,const string text,const int width,
                               const color button_color,const color button_color_hover,const color button_color_pressed);
  };
//+------------------------------------------------------------------+
//| Añade el botón                                                 |
//+------------------------------------------------------------------+
void CButtonsGroup::AddButton(const int x_gap,const int y_gap,const string text,const int width,
                              const color button_color,const color button_color_hover,const color pressed_button_color)
  {
//--- Aumentamos el tamaño del array a un elemento
   int array_size=::ArraySize(m_buttons);
   ::ArrayResize(m_buttons,array_size+1);
   ::ArrayResize(m_buttons_total,array_size+1);
   ::ArrayResize(m_buttons_state,array_size+1);
   ::ArrayResize(m_buttons_x_gap,array_size+1);
   ::ArrayResize(m_buttons_y_gap,array_size+1);
   ::ArrayResize(m_buttons_text,array_size+1);
   ::ArrayResize(m_buttons_width,array_size+1);
   ::ArrayResize(m_buttons_color,array_size+1);
   ::ArrayResize(m_buttons_color_hover,array_size+1);
   ::ArrayResize(m_buttons_color_pressed,array_size+1);
//--- Guardamos los valores de los parámetros pasados
   m_buttons_x_gap[array_size]         =x_gap;
   m_buttons_y_gap[array_size]         =y_gap;
   m_buttons_text[array_size]          =text;
   m_buttons_width[array_size]         =width;
   m_buttons_color[array_size]         =button_color;
   m_buttons_color_hover[array_size]   =button_color_hover;
   m_buttons_color_pressed[array_size] =pressed_button_color;
   m_buttons_state[array_size]         =false;
  }

Cuando se crea un grupo de botones, el proceso de la creación de la interfaz gráfica será detenido y en el diario aparecerá el mensaje correspondiente, si antes de eso no ha sido añadido ningún botón mediante el método CButtonsGroup::AddButton(). Los botones se crean en el ciclo, tal como se muestra en la versión reducida del método CButtonsGroup::CreateButtons() en el código de abajo. 

class CButtonsGroup : public CElement
  {
public:
   //--- Métodos para crear el botón
   bool              CreateButtonsGroup(const long chart_id,const int subwin,const int x,const int y);
   //---
private:
   bool              CreateButtons(void);
  };
//+------------------------------------------------------------------+
//| Crea los botones                                                   |
//+------------------------------------------------------------------+
bool CButtonsGroup::CreateButtons(void)
  {
//--- Coordenadas
   int l_x =m_x;
   int l_y =m_y;
//--- Obtenemos el número de botones
   int buttons_total=ButtonsTotal();
//--- Si en el grupo no hay botones, avisar sobre ello
   if(buttons_total<1)
     {
      ::Print(__FUNCTION__," > ¡La llamada a este método debe realizarse "
              "cuando en el grupo hay por lo menos un botón! Utilice el método CButtonsGroup::AddButton()");
      return(false);
     }
//--- Creamos el número especificado de botones
   for(int i=0; i<buttons_total; i++)
     {
      //--- Formación del nombre del objeto
      //--- Cálculo de coordenadas
      //--- Establecemos el botón
      //--- Establecemos las propiedades
      //--- Guardamos los márgenes desde el punto extremo del panel, coordenadas y tamaños
      //--- Inicialización del array del gradiente
      //--- Guardamos el puntero del objeto
     }
//---
   return(true);
  }

Hagamos dos modos para este tipo de botones. Para establecer el modo, añadimos un campo especial y el método a la clase (véase el código de abajo). Por defecto, se establece el valor false, y eso va a significar que el modo está activado permitiendo que todos los botones del grupo pueden estar sueltos. Si se establece el valor true, por lo menos un botón estará pulsado en el grupo.

class CButtonsGroup : public CElement
  {
public:
   //--- Modo de botones de opción
   bool              m_radio_buttons_mode;
   //---
public:
   //--- Establecer el modo de botones de opción
   void              RadioButtonsMode(const bool flag)              { m_radio_buttons_mode=flag;       }
  };

Igual que en cualquier otro control, esta clase debe tener un método para bloquear el control. Para este tipo del grupo de botones, es importante asegurar que cuando se desbloquea el botón pulsado anteriormente, él recupere la apariencia de su estado

class CButtonsGroup : public CElement
  {
private:
   //--- Disponible/bloqueado
   bool              m_buttons_group_state;
   //---
public:
   //--- Estado general del grupo de botones (disponible/bloqueado)
   bool              ButtonsGroupState(void)                  const { return(m_buttons_group_state);   }
   void              ButtonsGroupState(const bool state);
  };
//+------------------------------------------------------------------+
//| Cambio del estado de botones                                       |
//+------------------------------------------------------------------+
void CButtonsGroup::ButtonsGroupState(const bool state)
  {
   m_buttons_group_state=state;
//---
   int buttons_total=ButtonsTotal();
   for(int i=0; i<buttons_total; i++)
     {
      m_buttons[i].State(false);
      m_buttons[i].Color((state)? m_text_color : m_text_color_off);
      m_buttons[i].BackColor((state)? m_buttons_color[i]: m_back_color_off);
      m_buttons[i].BorderColor((state)? m_border_color : m_border_color_off);
     }
//--- Pulsar el botón si estaba pulsado antes del bloqueo
   if(m_buttons_group_state)
     {
      if(m_selected_button_index!=WRONG_VALUE)
        {
         m_buttons_state[m_selected_button_index]=true;
         m_buttons[m_selected_button_index].Color(m_text_color_pressed);
         m_buttons[m_selected_button_index].BackColor(m_buttons_color_pressed[m_selected_button_index]);
        }
     }
  }

Necesitamos un método para conmutar el botón al pulsarlo. Además, vamos a necesitar los campos y los métodos para almacenar y obtener el texto e índice del botón resaltado.

Al principio del método para la conmutación del estado del botón (véase el código de abajo) hay una comprobación del número de botones en el grupo. Si resulta que no hay ningún botón en absoluto, en el diario se mostrará el mensaje correspondiente. Además, el programa no va a abandonar el método. El programa seguirá adelante y en algún momento se enfrentará con el error del exceso del diapasón del array m_buttons_state[]. Es decir, el desarrollador de la aplicación tiene que hacerlo todo correctamente desde el principio (añadir por lo menos un botón en el grupo, para que no surja este error). Y después de la comprobación, si en el grupo hay por lo menos un botón, el programa ajustará el índice pasado en caso de haberse excedido el diapasón del array. Luego, se realiza el cambio del estado del botón según el índice especificado.

Después de eso, todos los botones, salvo el botón pulsado, se sueltan (se establece el color correspondiente) en el ciclo, tomando en cuenta el modo del grupo. En otras palabras, si está activado el modo “botones de opción” cuando por lo menos un botón tiene que estar pulsado siempre, entonces durante cada iteración se comprueba la condición de la coincidencia del índice pasado y el índice actual del ciclo.

Si está activado el modo cuando todos los botones pueden estar sueltos, aparte de la comprobación del índice se comprueba el estado actual del botón. Si la condición se ha cumplido, entonces independientemente del modo se pone la bandera que hay un botón pulsado. Según esta bandera, al final del método, se establecen los valores correspondientes en los campos de la clase donde tiene que guardarse el texto e índice del botón resaltado. Si no hay ningún botón pulsado, se establecen los valores vacíos ("" y WRONG_VALUE). 

class CButtonsGroup : public CElement
  {
private:
   //--- (1) Texto e (2) índice del botón seleccionado
   string            m_selected_button_text;
   int               m_selected_button_index;
   //---
public:
   //--- Devuelve el (1) texto e (2) índice del botón seleccionado
   string            SelectedButtonText(void)                 const { return(m_selected_button_text);  }
   int               SelectedButtonIndex(void)                const { return(m_selected_button_index); }
   //--- Conmuta el botón según el índice especificado
   void              SelectionButton(const int index);
  };
//+------------------------------------------------------------------+
//| Conmuta el botón según el índice especificado                         |
//+------------------------------------------------------------------+
void CButtonsGroup::SelectionButton(const int index)
  {
//--- Para comprobar la existencia del botón pulsado en el grupo
   bool check_pressed_button=false;
//--- Obtenemos el número de botones
   int buttons_total=ButtonsTotal();
//--- Si en el grupo no hay botones, avisar sobre ello
   if(buttons_total<1)
     {
      ::Print(__FUNCTION__," > ¡La llamada a este método debe realizarse "
              "cuando en el grupo hay por lo menos un botón! Utilice el método CButtonsGroup::AddButton()");
     }
//--- Corregir el valor del índice si excede el rango
   int correct_index=(index>=buttons_total)? buttons_total-1 : (index<0)? 0 : index;
//--- Cambiar el estado del botón por el contrario
   m_buttons_state[correct_index]=(m_buttons_state[correct_index])? false : true;
//--- Recorremos en el ciclo el grupo de botones
   for(int i=0; i<buttons_total; i++)
     {
      //--- Dependiendo del modo, se realiza la comprobación correspondiente
      bool condition=(m_radio_buttons_mode)? (i==correct_index) : (i==correct_index && m_buttons_state[i]);
      //--- Si la condición no se ha cumplido, hagamos el botón pulsado
      if(condition)
        {
         if(m_radio_buttons_mode)
            m_buttons_state[i]=true;
         //--- Hay un botón pulsado
         check_pressed_button=true;
         //--- Establecer los colores
         m_buttons[i].Color(m_text_color_pressed);
         m_buttons[i].BackColor(m_buttons_color_pressed[i]);
         CElement::InitColorArray(m_buttons_color_pressed[i],m_buttons_color_pressed[i],m_buttons_total[i].m_buttons_color_array);
        }
      //--- Si la condición no se ha cumplido, hagamos el botón suelto
      else
        {
         //--- Establecer el estado desactivado y los colores
         m_buttons_state[i]=false;
         m_buttons[i].Color(m_text_color);
         m_buttons[i].BackColor(m_buttons_color[i]);
         CElement::InitColorArray(m_buttons_color[i],m_buttons_color_hover[i],m_buttons_total[i].m_buttons_color_array);
        }
      //--- Poner a cero el estado normal del botón
      m_buttons[i].State(false);
     }
//--- Si hay un botón pulsado, guardamos su texto e índice
   m_selected_button_text  =(check_pressed_button) ? m_buttons[correct_index].Description() : "";
   m_selected_button_index =(check_pressed_button) ? correct_index : WRONG_VALUE;
  }

Para el procesamiento del clic en el grupo de botones, creamos el método CButtonsGroup::OnClickButton(). Igual que en muchos otros métodos homónimos considerados antes, aquí se realizan las siguientes comprobaciones:

  • por el nombre;
  • por el identificador. Para extraer el identificador desde el nombre del objeto, se utiliza el método CButtonsGroup::IdFromObjectName(). El código del método es similar a los métodos homónimos considerados antes en otras clases de los controles, por eso no vamos a mostrarlo aquí;
  • por el estado actual (disponible/bloqueado).

Si todas las comprobaciones han sido pasadas y el programa no ha salido del método, entonces se realiza la conmutación del botón en el grupo a través del método considerado antes CButtonsGroup::SelectionButton(). Al final de este método, el mensaje con los datos del botón pulsado se envía al flujo de los eventos. Este mensaje puede ser recibido en el manejador de la clase personalizada.

class CButtonsGroup : public CElement
  {
private:
   //--- Procesamiento del clic en el botón
   bool              OnClickButton(const string clicked_object);
   //--- Obtener el identificador desde el nombre del botón
   int               IdFromObjectName(const string object_name);
  };
//+------------------------------------------------------------------+
//| Manejador del evento del gráfico                                       |
//+------------------------------------------------------------------+
void CButtonsGroup::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)
     {
      if(OnClickButton(sparam))
         return;
     }
  }
//+------------------------------------------------------------------+
//| Clic en el botón en el grupo                                       |
//+------------------------------------------------------------------+
bool CButtonsGroup::OnClickButton(const string pressed_object)
  {
//--- Salimos si el clic ha sido hecho fuera del elemento del menú
   if(::StringFind(pressed_object,CElement::ProgramName()+"_buttons_",0)<0)
      return(false);
//--- Obtenemos el identificador desde el nombre del objeto
   int id=IdFromObjectName(pressed_object);
//--- Salimos si los identificadores no coinciden
   if(id!=CElement::Id())
      return(false);
//--- Para comprobar el índice
   int check_index=WRONG_VALUE;
//--- Comprobamos si ha tenido lugar el clic en uno de los botones de este grupo
   int buttons_total=ButtonsTotal();
//--- Salir si los botones están bloqueados
   if(!m_buttons_group_state)
     {
      for(int i=0; i<buttons_total; i++)
         m_buttons[i].State(false);
      //---
      return(false);
     }
//--- Si el clic ha tenido lugar, guardamos el índice
   for(int i=0; i<buttons_total; i++)
     {
      if(m_buttons[i].Name()==pressed_object)
        {
         check_index=i;
         break;
        }
     }
//--- Salimos si el botón no ha sido pulsado en este grupo
   if(check_index==WRONG_VALUE)
      return(false);
//--- Conmutar el botón
   SelectionButton(check_index);
//--- Enviar la señal sobre ello
   ::EventChartCustom(m_chart_id,ON_CLICK_BUTTON,CElement::Id(),m_selected_button_index,m_selected_button_text);
   return(true);
  }

Ahora tenemos que configurar la reacción de los botones del grupo al movimiento del cursor encima de ellos, y al clic izquierdo del ratón. Para eso escribiremos el método CButtonsGroup::CheckPressedOverButton() que va a llamarse en el manejador del control durante el procesamiento del evento CHARTEVENT_MOUSE_MOVE. Antes de su llamada se realizan algunas comprobaciones: (1) si el control está visible, (2) si está disponible, (3) si el formulario está disponible, (4) si el botón izquierdo del ratón está pulsado.

class CButtonsGroup : public CElement
  {
private:
   //--- Comprobando si el botón izquierdo del ratón está pulsado sobre los botones del grupo
   void              CheckPressedOverButton(void);
  };
//+------------------------------------------------------------------+
//| Manejador del evento del gráfico                                       |
//+------------------------------------------------------------------+
void CButtonsGroup::OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam)
  {
//--- Procesamiento del evento del desplazamiento del cursor
   if(id==CHARTEVENT_MOUSE_MOVE)
     {
      //--- Salir si el control está ocultado
      if(!CElement::IsVisible())
         return;
      //--- Salir si los botones están bloqueados
      if(!m_buttons_group_state)
         return;
      //--- Definimos el foco
      int x=(int)lparam;
      int y=(int)dparam;
      int buttons_total=ButtonsTotal();
      for(int i=0; i<buttons_total; i++)
        {
         m_buttons[i].MouseFocus(x>m_buttons[i].X() && x<m_buttons[i].X2() && 
                                 y>m_buttons[i].Y() && y<m_buttons[i].Y2());
        }
      //--- Salir si el formulario está bloqueado
      if(m_wnd.IsLocked())
         return;
      //--- Salir si el botón del ratón no está pulsado
      if(sparam!="1")
         return;
      //--- Comprobando si el botón izquierdo del ratón está pulsado sobre los botones del grupo
      CheckPressedOverButton();
      return;
     }
  }
//+------------------------------------------------------------------+
//| Comprobando si el botón izquierdo del ratón está pulsado sobre los botones del grupo           |
//+------------------------------------------------------------------+
void CButtonsGroup::CheckPressedOverButton(void)
  {
   int buttons_total=ButtonsTotal();
//--- Establecer el color en función de la posición del botón izquierdo pulsado
   for(int i=0; i<buttons_total; i++)
     {
      //--- Si hay foco, el color del botón pulsado
      if(m_buttons[i].MouseFocus())
         m_buttons[i].BackColor(m_buttons_color_pressed[i]);
      //--- Si no hay foco, entonces...
      else
        {
         //--- ...si el botón del grupo no está pulsado, asignar el color del fondo
         if(!m_buttons_state[i])
            m_buttons[i].BackColor(m_buttons_color[i]);
        }
     }
  }

Todo está listo para probar el grupo de botones simples en la aplicación de prueba. Hagamos una copia del EA que hemos probado antes. Eliminemos de ahí todos los controles, salvo el menú principal con sus menús contextuales. Luego en este artículo vamos a usar este programa para testear diferentes grupos de botones.

Creamos la instancia de la clase del grupo de botones CButtonsGroup en la clase personalizada. Declaramos el método CProgram::CreateButtonsGroup1() para su creación y los márgenes desde el punto extremo del formulario:

class CProgram : public CWndEvents
  {
private:
   //--- Grupo de botones simples
   CButtonsGroup     m_buttons_group1;
   //---
private:
   //--- Grupo de botones simples
#define BUTTONS_GROUP1_GAP_X (7)
#define BUTTONS_GROUP1_GAP_Y (50)
   bool              CreateButtonsGroup1(void);
  };

Creamos el grupo de cuatro botones. Los colocamos horizontalmente. Como ejemplo, dos botones serán rojos, otros dos, azules. La implementación del método CProgram::CreateButtonsGroup1() se muestra en el código de abajo. Si necesita que uno de los botones esté resaltado inmediatamente después de la creación del grupo, utilice el método público CButtonsGroup::SelectionButton()

//+------------------------------------------------------------------+
//| Crea un grupo de botones simples                                |
//+------------------------------------------------------------------+
bool CProgram::CreateButtonsGroup1(void)
  {
//--- Guardamos el puntero a la ventana
   m_buttons_group1.WindowPointer(m_window);
//--- Coordenadas
   int x =m_window.X()+BUTTONS_GROUP1_GAP_X;
   int y =m_window.Y()+BUTTONS_GROUP1_GAP_Y;
//--- Propiedades
   int    buttons_x_gap[]         ={0,72,144,216};
   string buttons_text[]          ={"BUTTON 1","BUTTON 2","BUTTON 3","BUTTON 4"};
   int    buttons_width[]         ={70,70,70,70};
   color  buttons_color[]         ={C'195,0,0',C'195,0,0',clrRoyalBlue,clrRoyalBlue};
   color  buttons_color_hover[]   ={C'255,51,51',C'255,51,51',C'85,170,255',C'85,170,255'};
   color  buttons_color_pressed[] ={C'135,0,0',C'135,0,0',C'50,100,135',C'50,100,135'};
//--- Establecemos las propiedades
   m_buttons_group1.TextColor(clrWhite);
   m_buttons_group1.TextColorPressed(clrGold);
//--- Añadimos cuatro botones al grupo
   for(int i=0; i<4; i++)
      m_buttons_group1.AddButton(buttons_x_gap[i],0,buttons_text[i],buttons_width[i],
                                 buttons_color[i],buttons_color_hover[i],buttons_color_pressed[i]);
//--- Crear grupo de botones
   if(!m_buttons_group1.CreateButtonsGroup(m_chart_id,m_subwin,x,y))
      return(false);
//--- Seleccionamos el segundo botón del grupo
   m_buttons_group1.SelectionButton(1);
//--- Añadimos el objeto al array común de los grupos de objetos
   CWndContainer::AddToElementsArray(0,m_buttons_group1);
   return(true);
  }

Después de compilar los archivos y cargar el programa en el gráfico, verá el resultado que se muestra en la captura de pantalla de abajo:

Fig. 2. Prueba del control “Grupo de botones simples”.

Fig. 2. Prueba del control “Grupo de botones simples”.


Pues, con el primer grupo de botones está todo claro. Al final del artículo, se puede descargar la versión hecha de la clase CButtonsGroup. El siguiente control que vamos a analizar es el grupo de botones de opción. 

 


Desarrollando la clase para la creación de los grupos de botones de opción

Creamos el archivo RadioButtons.mqh con la clase CRadioButtons, que debe contener los métodos virtuales estándar y los miembros de la clase para guardar y obtener el puntero al formulario. Puede ver los ejemplos en las clase de otros controles más arriba. Incluimos el archivo RadioButtons.mqh en la librería (WndContainer.mqh).

Cada botón de opción va a componerse de tres objetos primitivos:

  1. fondo;
  2. icono;
  3. etiqueta de texto.


Fig. 3. Partes integrantes de los botones de opción.


A diferencia del grupo de botones simples, el grupo de botones de opción va a tener sólo un modo, porque en este tipo del control no puede haber situación cuando todos los botones estén sueltos al mismo tiempo. Algún botón del grupo siempre estará pulsado. El color del fondo del grupo suele ser el mismo que el color del fondo del formulario al que está adjuntado este grupo. En realidad, se utiliza para identificar el foco y seguir el clic en el botón de opción (prioridad superior). En esta versión, se puede ajustar el cambio del color sólo para la etiqueta de texto cuando el cursor entra en el área del fondo del botón y sale de ahí. Las listas de las propiedades únicas y comunes del botón de opción se diferencian de las listas de estas propiedades para el botón simple (véase las listas de abajo).

Propiedades comunes:

  • Color del fondo y del marco;
  • Colores del texto;
  • Alto;
  • Iconos del botón en el estado (1) activo, (2) desactivado y (3) bloqueado;
  • Prioridad para el clic izquierdo del ratón.

Propiedades únicas:

  • Texto;
  • Ancho;
  • Estado. Sólo un botón del grupo puede permanecer pulsado;
  • Márgenes. Igual que con los botones simples, la organización de los botones de opción puede ser cualquiera (mosaico, horizontalmente, verticalmente, etc.);
  • Gradientes para las etiquetas de texto.


A continuación, se muestra cómo se ve todo eso en el código de la clase CRadioButtons. La redimensión de los arrays de propiedades únicas y el llenado con los valores se realiza a través del método público CRadioButtons::AddButton() antes de la creación del control en la clase personalizada. Si no se ha añadido ningún botón, la creación de la interfaz gráfica del programa será interrumpido. Ya lo hemos mostrado en el ejemplo del desarrollo de la clase para la creación del grupo de botones simples, por eso no vamos a repetirnos.

//+------------------------------------------------------------------+
//| Clase para la creación del grupo de botones de opción                           |
//+------------------------------------------------------------------+
class CRadioButtons : public CElement
  {
private:
   //--- Gradientes de las etiquetas de texto
   struct LabelsGradients
     {
      color             m_labels_color_array[];
     };
   LabelsGradients   m_labels_total[];
   //--- Propiedades de botones:
   //    (1) Color y (2) prioridad del fondo para el clic izquierdo del ratón
   color             m_area_color;
   int               m_area_zorder;
   Arrays para las propiedades únicas de los botones
   bool              m_buttons_state[];
   int               m_buttons_x_gap[];
   int               m_buttons_y_gap[];
   int               m_buttons_width[];
   string            m_buttons_text[];
   //--- Alto de botones
   int               m_button_y_size;
   //--- Iconos del botón en el estado activo, desactivado y bloqueado
   string            m_icon_file_on;
   string            m_icon_file_off;
   string            m_icon_file_on_locked;
   string            m_icon_file_off_locked;
   //--- Colores del texto
   color             m_text_color;
   color             m_text_color_off;
   color             m_text_color_hover;
   //--- Prioridad para el clic izquierdo del ratón
   int               m_buttons_zorder;
   //---
public:
   //--- Establecer los iconos para el botón en el estado activo, desactivado y bloqueado
   void              IconFileOn(const string file_path)           { m_icon_file_on=file_path;         }
   void              IconFileOff(const string file_path)          { m_icon_file_off=file_path;        }
   void              IconFileOnLocked(const string file_path)     { m_icon_file_on_locked=file_path;  }
   void              IconFileOffLocked(const string file_path)    { m_icon_file_off_locked=file_path; }
   //--- (1) Colores del fondo, (2) colores del texto
   void              AreaColor(const color clr)                   { m_area_color=clr;                 }
   void              TextColor(const color clr)                   { m_text_color=clr;                 }
   void              TextColorOff(const color clr)                { m_text_color_off=clr;             }
   void              TextColorHover(const color clr)              { m_text_color_hover=clr;           }

   //--- Añade un botón con propiedades especificadas antes de la creación
   void              AddButton(const int x_gap,const int y_gap,const string text,const int width);
  };

Luego, hay que crear los arrays de las instancias de las clases de los objetos primitivos y los métodos para su creación. Igual que el grupo de botones simples, los botones de opción van a crearse en el ciclo. Pero en este caso, vamos a colocar el ciclo en el método principal, y el índice que va a participar en la formación del nombre de cada objeto lo vamos a pasar a los métodos de la creación de estos objetos.

class CRadioButtons : public CElement
  {
private:
   //--- Objetos para crear el botón
   CRectLabel        m_area[];
   CBmpLabel         m_icon[];
   CLabel            m_label[];
   //---
public:
   //--- Métodos para crear el botón
   bool              CreateRadioButtons(const long chart_id,const int window,const int x,const int y);
   //---
private:
   bool              CreateArea(const int index);
   bool              CreateRadio(const int index);
   bool              CreateLabel(const int index);
   //---
public:
   //--- Número de botones
   int               RadioButtonsTotal(void)                const { return(::ArraySize(m_icon));      }
  };
//+------------------------------------------------------------------+
//| Crea el grupo de objetos Botones                                   |
//+------------------------------------------------------------------+
bool CRadioButtons::CreateRadioButtons(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 el grupo de botones de opción, hay que pasar a la clase "
              "el puntero al formuñario: CButtonsGroup::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;
//--- Obtenemos el número de botones en el grupo
   int radio_buttons_total=RadioButtonsTotal();
//--- Si en el grupo no hay botones, avisar sobre ello
   if(radio_buttons_total<1)
     {
      ::Print(__FUNCTION__," > ¡La llamada a este método debe realizarse "
              "cuando en el grupo hay por lo menos un botón! Utilice el método CRadioButtons::AddButton()");
      return(false);
     }
//--- Establecemos el grupos de botones
   for(int i=0; i<radio_buttons_total; i++)
     {
      CreateArea(i);
      CreateRadio(i);
      CreateLabel(i);
      //--- Puesta a cero del foco
      m_area[i].MouseFocus(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);
  }

Esta clase también tienen que contener los métodos para el cambio del estado del control (disponible/bloqueado) y para la conmutación del botón según el índice especificado. Aquí incluso son más sencillos que en la clase CButtonsGroup porque el grupo de botones de opción tiene sólo un modo. Usted puede estudiar el código de estos métodos en los archivos adjuntos al artículo. Aparte de eso, hacen falta los métodos para obtener el texto e índice del botón seleccionado, igual que en las clase para los botones simples.

class CButtonsGroup : public CElement
  {
private:
   //--- (1) Texto e (2) índice del botón seleccionado
   string            m_selected_button_text;
   int               m_selected_button_index;
   //--- Disponible/bloqueado
   bool              m_buttons_group_state;
   //---
public:
   //--- Estado general del grupo de botones (disponible/bloqueado)
   bool              ButtonsGroupState(void)                  const { return(m_buttons_group_state);   }
   void              ButtonsGroupState(const bool state);
   //--- Devuelve el (1) texto e (2) índice del botón seleccionado
   string            SelectedButtonText(void)                 const { return(m_selected_button_text);  }
   int               SelectedButtonIndex(void)                const { return(m_selected_button_index); }
   //--- Conmuta el botón según el índice especificado
   void              SelectionButton(const int index);
  };

Para el envío de los mensajes sobre el clic en los controles que visualmente aparentan a la etiqueta de texto, nosotros vamos a aplicar el nuevo identificador ON_CLICK_LABEL. Vamos a añadirlo al archivo Defines.mqh:

#define ON_CLICK_LABEL            (10) //Clic en la etiqueta de texto

La implementación del método para el procesamiento del clic en los botones del grupo se muestra a continuación. Después de pasar todas las comprobaciones: (1) por la pertenencia al tipo del control, (2) por el identificador y (3) disponibilidad, luego se identifica en el ciclo el índice del botón pulsado según el nombre exacto. Si el botón del grupo ha sido pulsado y este botón no está seleccionado en este momento, se realiza la conmutación y el envío del mensaje que puede ser recibido en la clase personalizada.

class CButtonsGroup : public CElement
  {
private:
   //--- Procesamiento del clic en el botón
   bool              OnClickButton(const string pressed_object);
   //--- Obtener el identificador desde el nombre del botón de opción
   int               IdFromObjectName(const string object_name);
  };
//+------------------------------------------------------------------+
//| Manejador del evento del gráfico                                       |
//+------------------------------------------------------------------+
void CRadioButtons::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)
     {
      //--- Conmutar el botón
      if(OnClickButton(sparam))
         return;
     }
  }
//+------------------------------------------------------------------+
//| Clic en el botón de opción                                          |
//+------------------------------------------------------------------+
bool CRadioButtons::OnClickButton(const string pressed_object)
  {
//--- Salimos si el clic ha sido hecho fuera del elemento del menú
   if(::StringFind(pressed_object,CElement::ProgramName()+"_radio_area_",0)<0)
      return(false);
//--- Obtenemos el identificador e índice desde el nombre del objeto
   int id=IdFromObjectName(pressed_object);
//--- Salimos si el clic no ha sido hecho en el elemento al que este menú contextual está vinculado
   if(id!=CElement::Id())
      return(false);
//--- Para comprobar el índice
   int check_index=WRONG_VALUE;
//--- Salir si los botones están bloqueados
   if(!m_radio_buttons_state)
      return(false);
//--- Si el clic ha tenido lugar, guardamos el índice
   int radio_buttons_total=RadioButtonsTotal();
   for(int i=0; i<radio_buttons_total; i++)
     {
      if(m_area[i].Name()==pressed_object)
        {
         check_index=i;
         break;
        }
     }
//--- Salimos si el botón no ha sido pulsado en este grupo o
//    si este botón ya está seleccionado
   if(check_index==WRONG_VALUE || check_index==m_selected_button_index)
      return(false);
//--- Conmutar el botón
   SelectionRadioButton(check_index);
//--- Enviar la señal sobre ello
   ::EventChartCustom(m_chart_id,ON_CLICK_LABEL,CElement::Id(),check_index,m_selected_button_text);
   return(true);
  }

Todo está preparado para las pruebas. Añadimos cuatro grupos de botones de opción (CRadioButtons) y un grupo de botones simples (CButtonsGroup) en el EA que hemos utilizado par probar antes el grupo de botones simples. 

class CProgram : public CWndEvents
  {
private:
   //--- Grupo de botones de opción 1
   CRadioButtons     m_radio_buttons1;
   //--- Grupo de botones de opción 2
   CButtonsGroup     m_buttons_group2;
   //--- Grupos de botones de opción 2,3,4
   CRadioButtons     m_radio_buttons2;
   CRadioButtons     m_radio_buttons3;
   CRadioButtons     m_radio_buttons4;
   //---
private:
   //--- Grupo de botones de opción 1
#define RADIO_BUTTONS1_GAP_X     (7)
#define RADIO_BUTTONS1_GAP_Y     (75)
   bool              CreateRadioButtons1();
   //--- Grupo de botones de opción 2
#define BUTTONS_GROUP2_GAP_X     (7)
#define BUTTONS_GROUP2_GAP_Y     (100)
   bool              CreateButtonsGroup2(void);
   //--- Grupos de botones de opción 2,3,4
#define RADIO_BUTTONS2_GAP_X     (7)
#define RADIO_BUTTONS2_GAP_Y     (125)
   bool              CreateRadioButtons2();
#define RADIO_BUTTONS3_GAP_X     (105)
#define RADIO_BUTTONS3_GAP_Y     (125)
   bool              CreateRadioButtons3();
#define RADIO_BUTTONS4_GAP_X     (203)
#define RADIO_BUTTONS4_GAP_Y     (125)
   bool              CreateRadioButtons4();
  };
//+------------------------------------------------------------------+
//| Crea el panel de trading                                          |
//+------------------------------------------------------------------+
bool CProgram::CreateTradePanel(void)
  {
//--- Creación del formulario para los controles
//--- Creación de controles:
//    Menú principal
//--- Menús contextuales
//--- Grupo de botones de opción 1
//--- Grupo de botones de opción 1
   if(!CreateRadioButtons1())
      return(false);
//--- Grupo de botones de simples 2
   if(!CreateButtonsGroup2())
      return(false);
   //--- Grupos de botones de opción 2,3,4
   if(!CreateRadioButtons2())
      return(false);
   if(!CreateRadioButtons3())
      return(false);
   if(!CreateRadioButtons4())
      return(false);
//--- Redibujar el gráfico
   m_chart.Redraw();
   return(true);
  }

Como ejemplo, mostraremos la implementación del método de uno de ellos. Los demás pueden diferenciarse sólo con los valores de sus parámetros. 

//+------------------------------------------------------------------+
//| Crea el grupo de botones de opción 1                                    |
//+------------------------------------------------------------------+
bool CProgram::CreateRadioButtons1(void)
  {
//--- Pasar el objeto del panel
   m_radio_buttons1.WindowPointer(m_window);
//--- Coordenadas
   int x =m_window.X()+RADIO_BUTTONS1_GAP_X;
   int y =m_window.Y()+RADIO_BUTTONS1_GAP_Y;
//--- Propiedades
   int    buttons_x_offset[] ={0,98,196};
   int    buttons_y_offset[] ={0,0,0};
   string buttons_text[]     ={"Radio Button 1","Radio Button 2","Radio Button 3"};
   int    buttons_width[]    ={92,92,92};
//---
   for(int i=0; i<3; i++)
      m_radio_buttons1.AddButton(buttons_x_offset[i],buttons_y_offset[i],buttons_text[i],buttons_width[i]);
//--- Crear grupo de botones
   if(!m_radio_buttons1.CreateRadioButtons(m_chart_id,m_subwin,x,y))
      return(false);
//--- Seleccionamos el segundo botón del grupo
   m_radio_buttons1.SelectedRadioButton(1);
//--- Bloqueamos los botones de opción
   m_radio_buttons1.RadioButtonsState(false);
//--- Añadimos el objeto al array común de los grupos de objetos
   CWndContainer::AddToElementsArray(0,m_radio_buttons1);
   return(true);
  }

Hagamos dos botones en el grupo adicional del tipo CButtonsGroup. Para la demostración, hagamos que el segundo botón de este grupo bloquee los primeros grupos de botones simples y de opción y el primer botón los haga disponibles.

//+------------------------------------------------------------------+
//| Manejador de eventos                                               |
//+------------------------------------------------------------------+
void CProgram::OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam)
  {
 //--- Evento del clic en el botón
   if(id==CHARTEVENT_CUSTOM+ON_CLICK_BUTTON)
     {
      ::Print("id: ",id,"; lparam: ",lparam,"; dparam: ",dparam,"; sparam: ",sparam);
      //--- Si el identificador del segundo grupo de botones simples y 
      //    el texto del botón seleccionado en este grupo coincide con el parámetro string del mensaje
      if(lparam==m_buttons_group2.Id() && sparam==m_buttons_group2.SelectedButtonText())
        {
         //--- Si es el índice del primer botón, desbloqueamos los controles especificados
         if((int)dparam==0)
           {
            m_buttons_group1.ButtonsGroupState(true);
            m_radio_buttons1.RadioButtonsState(true);
           }
         //--- Si es el índice del segundo botón, bloqueamos los controles especificados
         else
           {
            m_buttons_group1.ButtonsGroupState(false);
            m_radio_buttons1.RadioButtonsState(false);
           }
        }
      return;
     }     
  }

Compile todos los archivos e inicie el programa en el gráfico. En esta fase del desarrollo, debemos obtener el resultado como en la captura de pantalla de abajo:

Fig. 4. Prueba del control “Botones de opción”.

Fig. 4. Prueba del control “Botones de opción”.


Hemos terminado el desarrollo de la clase para la creación del “Grupos de botones de opción”. Se puede descargar la versión completa de los archivos adjuntos al artículo. 

 


Desarrollando la clase para la creación de los grupos de botones con imágenes

Antes hemos creado la clase CIconButton para la creación del control “Botón con imagen”. Ahora vamos a implementar el control que permite crear el grupo de estos botones. En la carpeta Controls, donde se ubican otros archivos creados anteriormente con las clases de los controles, creamos el archivo IconButtonsGroup.mqh con la clase CIconButtonsGroup en la que inmediatamente se puede declarar e implementar los métodos virtuales estándar. Incluya este archivo en la librería (archivo WndContainer.mqh).

Esta vez no vamos a describirlo todo detalladamente porque, en realidad, la clase CIconButtonsGroup es la composición de todos los métodos que ya han sido analizados en las clases CButtonsGroup y CRadioButtons. Igual que en la clase del grupo de los botones de opción (CRadioButtons), los objetos de los que se compone cada botón del grupo van a crearse en el ciclo usando los métodos privados del método principal de la creación del control. El único parámetro de estos métodos es el índice que va a utilizarse en la formación del nombre de los objetos gráficos. Al situar el cursor sobre los botones de este grupo, se puede configurar el cambio del texto y del fondo. 

Los métodos que se refieren al procesamiento de eventos ya han sido descritos en el artículo. En este tipo del grupo de botones son prácticamente iguales, por eso mostraremos sólo el contenido de la clase CIconButtonsGroup (véase el código de abajo En los archivos adjuntos se puede ver la implementación de los métodos que están fuera del cuerpo de la clase.

//+------------------------------------------------------------------+
//| Clase para la creación del grupo de botones con imágenes                    |
//+------------------------------------------------------------------+
class CIconButtonsGroup : public CElement
  {
private:
   //--- Puntero al formulario al que está adjuntado el control
   CWindow          *m_wnd;
   //--- Objetos para crear el botón
   CButton           m_buttons[];
   CBmpLabel         m_icons[];
   CLabel            m_labels[];
   //--- Gradientes de las etiquetas de texto
   struct IconButtonsGradients
     {
      color             m_back_color_array[];
      color             m_label_color_array[];
     };
   IconButtonsGradients   m_icon_buttons_total[];
   //--- Propiedades de botones:
       Arrays para las propiedades únicas de los botones:
   bool              m_buttons_state[];
   int               m_buttons_x_gap[];
   int               m_buttons_y_gap[];
   string            m_buttons_text[];
   int               m_buttons_width[];
   string            m_icon_file_on[];
   string            m_icon_file_off[];
   //--- Alto de botones
   int               m_buttons_y_size;
   //--- Color del fondo en diferentes modos
   color             m_back_color;
   color             m_back_color_off;
   color             m_back_color_hover;
   color             m_back_color_pressed;
   //--- Color del marco
   color             m_border_color;
   color             m_border_color_off;
   //--- Márgenes del icono
   int               m_icon_x_gap;
   int               m_icon_y_gap;
   //--- Texto y márgenes de la etiqueta de texto
   int               m_label_x_gap;
   int               m_label_y_gap;
   //--- Color de la etiqueta de texto en diferentes modos
   color             m_label_color;
   color             m_label_color_off;
   color             m_label_color_hover;
   color             m_label_color_pressed;
   //--- (1) Texto e (2) índice del botón seleccionado
   string            m_selected_button_text;
   int               m_selected_button_index;
   //--- Prioridad general para los objetos no clicables
   int               m_zorder;
   //--- Prioridad para el clic izquierdo del ratón
   int               m_buttons_zorder;
   //--- Disponible/bloqueado
   bool              m_icon_buttons_state;
   //---
public:
                     CIconButtonsGroup(void);
                    ~CIconButtonsGroup(void);
   //--- Métodos para crear el botón
   bool              CreateIconButtonsGroup(const long chart_id,const int window,const int x,const int y);
   //---
private:
   bool              CreateButton(const int index);
   bool              CreateIcon(const int index);
   bool              CreateLabel(const int index);
   //---
public:
    //--- (1) Guarda el puntero del formulario, (2) alto de botones, (3) número de botones,
   //    (4) estado general del botón (disponible/bloqueado)
   void              WindowPointer(CWindow &object)               { m_wnd=::GetPointer(object);      }
   void              ButtonsYSize(const int y_size)               { m_buttons_y_size=y_size;         }
   int               IconButtonsTotal(void)                 const { return(::ArraySize(m_icons));    }
   bool              IconButtonsState(void)                 const { return(m_icon_buttons_state);    }
   void              IconButtonsState(const bool state);
   //--- Colores del fondo del botón
   void              BackColor(const color clr)                   { m_back_color=clr;                }
   void              BackColorOff(const color clr)                { m_back_color_off=clr;            }
   void              BackColorHover(const color clr)              { m_back_color_hover=clr;          }
   void              BackColorPressed(const color clr)            { m_back_color_pressed=clr;        }
   //--- Márgenes del icono
   void              IconXGap(const int x_gap)                    { m_icon_x_gap=x_gap;              }
   void              IconYGap(const int y_gap)                    { m_icon_y_gap=y_gap;              }
   //--- Márgenes de la etiqueta de texto
   void              LabelXGap(const int x_gap)                   { m_label_x_gap=x_gap;             }
   void              LabelYGap(const int y_gap)                   { m_label_y_gap=y_gap;             }
   //--- Devuelve el (1) texto e (2) índice del botón seleccionado
   string            SelectedButtonText(void)               const { return(m_selected_button_text);  }
   int               SelectedButtonIndex(void)              const { return(m_selected_button_index); }
   //--- Conmuta el botón de opción según el índice especificado
   void              SelectedRadioButton(const int index);

   //--- Añade un botón con propiedades especificadas antes de la creación
   void              AddButton(const int x_gap,const int y_gap,const string text,
                               const int width,const string icon_file_on,const string icon_file_off);
   //--- Cambio del color
   void              ChangeObjectsColor(void);
   //---
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) poner a cero las prioridades para el clic izquierdo del ratón
   virtual void      SetZorders(void);
   virtual void      ResetZorders(void);
   //---
private:
   //--- Procesamiento del clic en el botón
   bool              OnClickButton(const string pressed_object);
   //--- Comprobando si el botón izquierdo del ratón está pulsado sobre los botones del grupo
   void              CheckPressedOverButton(void);
   //--- Obtener el identificador desde el nombre del botón de opción
   int               IdFromObjectName(const string object_name);
  };

A continuación, vamos a probar este control. Como ejemplo, vamos a crear un grupo de botones con imágenes en el formulario del mismo EA en el que hemos probado los grupos del tipoCButtonsGroup y CRadioButtons. Para eso, en la clase personalizada hay que insertar las líneas del código de abajo.

class CProgram : public CWndEvents
  {
private:
   //--- Grupo de botones con imágenes 1
   CIconButtonsGroup m_icon_buttons_group1;
   //---
private:
   //--- Grupo de botones con imágenes 1
#define IBUTTONS_GROUP1_GAP_X    (7)
#define IBUTTONS_GROUP1_GAP_Y    (190)
   bool              CreateIconButtonsGroup1(void);
  };
//+------------------------------------------------------------------+
//| Crea el panel de trading                                          |
//+------------------------------------------------------------------+
bool CProgram::CreateTradePanel(void)
  {
//--- Creación del formulario para los controles
//--- Creación de controles:
//    Menú principal
//--- Menús contextuales
//--- Grupo de botones de opción 1
//--- Grupo de botones de opción 1
//--- Grupo de botones de simples 2
//--- Grupos de botones de opción 2,3,4
//--- Grupo de botones con imágenes 1
   if(!CreateIconButtonsGroup1())
      return(false);
//--- Redibujar el gráfico
   m_chart.Redraw();
   return(true);
  }

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

Fig. 5. Prueba del grupo de botones con imágenes.

Fig. 5. Prueba del grupo de botones con imágenes.


Hemos terminado el desarrollo del control “Grupo de botones con imágenes”. Se puede descargar la versión completa de los archivos adjuntos al artículo.

 


Conclusión

Hemos terminado la tercera 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 esta fase del desarrollo, el esquema de la librería tiene el siguiente aspecto:

Fig. 6. Estructura de la librería en la fase actual del desarrollo.

Fig. 6. Estructura de la librería en la fase actual del desarrollo


En la siguiente (cuarta) parte continuaremos el desarrollo de la librería y hablaremos de los siguientes temas: 

  • Modo de ventanas múltiples
  • Sistema de gestión de las prioridades para el clic izquierdo del ratón sobre los objetos gráficos
  • Elementos informativos de la interfaz “Barra de estado” y “Descripciones emergentes”

Más abajo se puede descargar los archivos comprimidos con los ficheros de la librería en esta fase del desarrollo, imágenes y ficheros de los programas examinados en el artículo para realizar las pruebas en los terminales  MetaTrader. 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 tercera parte: