English Русский 中文 Español Deutsch 日本語
Interfaces Gráficas V: O Controle Lista (Capítulo 2)

Interfaces Gráficas V: O Controle Lista (Capítulo 2)

MetaTrader 5Exemplos | 15 setembro 2016, 17:15
1 046 0
Anatoli Kazharski
Anatoli Kazharski

Conteúdo

 


Introdução

O primeiro artigo Interfaces gráficas I: Preparação da Estrutura da Biblioteca (Capítulo 1) considera em detalhes a finalidade desta biblioteca. Você irã encontrar uma lista de artigos com os links no final de cada capítulo. Lá, você também pode encontrar e baixar a versão completa da biblioteca, no estágio de desenvolvimento atual. Os arquivos devem estar localizados nas mesmas pastas que o arquivo baixado.   

No capítulo anterior, nós escrevemos as classes para criar a barra de rolagem vertical e horizontal. Neste capítulo, nós vamos implementá-las. Nós vamos escrever uma classe para criar o controle lista, sendo que a barra de rolagem vertical será sua parte integrante. Nós também vamos demonstrar a implementação do mecanismo de rolagem automática da lista ao segurar o botão de rolagem para baixo. No final, nós vamos testar tudo usando um exemplo real de uma aplicação MQL.

 


O Controle Lista

O elemento da interface gráfica lista oferece ao usuário uma escolha de várias opções. O número total dos elementos da lista e o número de elementos na sua parte visível pode ser diferente quando o número total é muito grande e não se encaixa a parte de trabalho destacada da interface. Em tais casos, é utilizado uma barra de rolagem. 

Nós vamos compor uma lista de vários objetos primitivos e um elemento de inclusão. Eles são:

  1. Fundo da lista.
  2. Array de elementos da lista.
  3. O controle da barra de rolagem vertical.



Fig. 1. Partes integrantes do elemento lista.

 

Abaixo, nós vamos considerar o desenvolvimento da classe para a criação do elemento lista.

 


Desenvolvimento da Classe para a criação do Elemento

Para criar o elemento e incorpora-lo à biblioteca em desenvolvimento, nós precisamos criar um arquivo com a classe CListView do elemento. No nosso caso, é a ListView.mqh. Então, nós precisamos incluí-los no arquivo WndContainer.mqh:

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

Semelhante às classes de controles que foram discutidas nos artigos anteriores desta série, a classe CListView possui um conjunto padrão de métodos. Para usar uma barra de rolagem neste elemento, o arquivo Scrolls.mqh deve ser incluído no arquivo ListView.mqh

//+------------------------------------------------------------------+
//|                                                     ListView.mqh |
//|                        Copyright 2015, MetaQuotes Software Corp. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
#include "Element.mqh"
#include "Window.mqh"
#include "Scrolls.mqh"
//+------------------------------------------------------------------+
//| Classe para criar uma lista                                      |
//+------------------------------------------------------------------+
class CListView : public CElement
  {
private:
   //--- Ponteiro para o formulário ao qual o elemento está anexado
   CWindow          *m_wnd;
   //---
public:
                     CListView(void);
                    ~CListView(void);
   //--- (1) Armazena o ponteiro do formulário
   void              WindowPointer(CWindow &object)                      { m_wnd=::GetPointer(object);       }
   //---
public:
   //--- Manipulador de eventos do gráfico
   virtual void      OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam);
   //--- Timer
   virtual void      OnEventTimer(void);
   //--- Move o elemento
   virtual void      Moving(const int x,const int y);
   //--- (1) Exibe, (2) oculta, (3) reseta, (4) remove
   virtual void      Show(void);
   virtual void      Hide(void);
   virtual void      Reset(void);
   virtual void      Delete(void);
   //--- (1) Definir (2), resetar as prioridades para o clique esquerdo do mouse
   virtual void      SetZorders(void);
   virtual void      ResetZorders(void);
  };
//+------------------------------------------------------------------+
//| Construtor                                                       |
//+------------------------------------------------------------------+
CListView::CListView(void)
  {
//--- Armazena o nome da classe do elemento na classe base
   CElement::ClassName(CLASS_NAME);
  }
//+------------------------------------------------------------------+
//| Destrutor                                                        |
//+------------------------------------------------------------------+
CListView::~CListView(void)
  {
  }

Os métodos para definir as propriedades dos objetos primitivos (partes componentes da lista) serão exigidos antes de sua criação.

  • Altura dos elementos da lista.
  • Cor do quadro do fundo da lista.
  • Cor de fundo do elemento em diferentes estados.
  • Cor do texto do elemento em diferentes estados.

Os valores das propriedades acima são definidos no construtor da classe. 

class CListView : public CElement
  {
private:
   //--- Propriedades do fundo da lista
   int               m_area_zorder;
   color             m_area_border_color;
   //--- Propriedades dos elementos da 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:
   //--- Altura do elemento
   void              ItemYSize(const int y_size)                         { m_item_y_size=y_size;             }
   //--- Cor do quadro de fundo
   void              AreaBorderColor(const color clr)                    { m_area_border_color=clr;          }
   //--- Cor dos elementos da lista em estados diferentes
   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;   }
  };
//+------------------------------------------------------------------+
//| Construtor                                                       |
//+------------------------------------------------------------------+
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)
  {
//--- Define as prioridades do botão esquerdo do mouse
   m_area_zorder =1;
   m_item_zorder =2;
  }

Há três métodos privados e um público para criar os objetos da lista. Um array de instâncias de classe do tipo CEdit deve ser declarado para a criação dos elementos da lista. Este array pode ser utilizado para a criação de objetos gráficos do tipo OBJ_EDIT (campo de entrada). 

class CListView : public CElement
  {
private:
   //--- Objetos para a criação da lista
   CRectLabel        m_area;
   CEdit             m_items[];
   CScrollV          m_scrollv;
   //---
public:
   //--- Métodos para a criação da 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);
  };

O tamanho padrão de exibição da lista e a sua parte visível é igual a dois elementos, já que não há nenhuma razão em criar uma lista que contenha apenas um elemento. Para definir o tamanho da exibição da lista e a sua parte visível, nós vamos criar o método CListView::ListSize() e o CListView::VisibleListSize() com a verificação da quantidade de elementos para que não seja inferior a dois.  

class CListView : public CElement
  {
private:
   //--- Array dos valores da lista
   string            m_value_items[];
   //--- Tamanho da lista e a sua parte visível
   int               m_items_total;
   int               m_visible_items_total;
   //---
public:
   //--- Retorna o tamanho da (1) lista e (2) a sua parte visível
   int               ItemsTotal(void)                              const { return(m_items_total);            }
   int               VisibleItemsTotal(void)                       const { return(m_visible_items_total);    }
   //--- Define o tamanho da (1) lista e (2) a sua parte visível
   void              ListSize(const int items_total);
   void              VisibleListSize(const int visible_items_total);
  };
//+------------------------------------------------------------------+
//| Construtor                                                       |
//+------------------------------------------------------------------+
CListView::CListView(void) : m_items_total(2),
                             m_visible_items_total(2)
  {
//--- Ajusta o tamanho da exibição da lista e a sua parte visível
   ListSize(m_items_total);
   VisibleListSize(m_visible_items_total);
  }
//+------------------------------------------------------------------+
//| Define o tamanho da lista                                        |
//+------------------------------------------------------------------+
void CListView::ListSize(const int items_total)
  {
//--- Não há razão em exibir uma lista inferior a dois itens
   m_items_total=(items_total<2) ? 2 : items_total;
   ::ArrayResize(m_value_items,m_items_total);
  }
//+------------------------------------------------------------------+
//| Define o tamanho da parte visível da lista                       |
//+------------------------------------------------------------------+
void CListView::VisibleListSize(const int visible_items_total)
  {
//--- Não há razão em exibir uma lista inferior a dois itens
   m_visible_items_total=(visible_items_total<2) ? 2 : visible_items_total;
   ::ArrayResize(m_items,m_visible_items_total);
  }

Os métodos relevantes são necessários para armazenar e obter o índice e o texto do elemento destacado da lista. O primeiro elemento da lista será destacado por padrão. Se outro item tiver que ser destacado após a lista ter sido criada, use o método CListView::SelectedItemIndex(). Você precisará especificar o índice do elemento antes da criação da lista e após a quantidade de elementos ter sido definida.

class CListView : public CElement
  {
private:
   //--- (1) Índice e (2) o texto do botão em destaque
   int               m_selected_item_index;
   string            m_selected_item_text;
   //---
public:
   //--- Retorna/Armazena (1) o índice e (2) o texto no elemento destacado da 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);     }
  };
//+------------------------------------------------------------------+
//| Construtor                                                       |
//+------------------------------------------------------------------+
CListView::CListView(void) : m_selected_item_index(0),
                             m_selected_item_text("")
  {
//--- ...
  }
//+------------------------------------------------------------------+
//| Armazena o índice                                                |
//+------------------------------------------------------------------+
void CListView::SelectedItemIndex(const int index)
  {
//--- Ajuste no caso do tamanho exceder
   m_selected_item_index=(index>=m_items_total)? m_items_total-1 : (index<0)? 0 : index;
  }

Após a lista ser criada, no momento de selecionar um elemento, ele deve se destacar com uma cor diferente. Vamos escrever o método CListView::HighlightSelectedItem() para isso. No início deste método, há uma verificação para o estado atual da barra de rolagem. Se ela está ativa e o deslizador pode mover-se, então, o programa sai do método. Se a verificação foi aprovada, então, obtém a posição atual do deslizador na lista. O valor obtido será utilizado como partida para o contador no loop. Com a ajuda do loop nós podemos identificar qual o elemento para ser destacado.

class CListView : public CElement
  {
public:
   //--- Destacando o elemento selecionado
   void              HighlightSelectedItem(void);
  };
//+------------------------------------------------------------------+
//| Destacando o elemento selecionado                                |
//+------------------------------------------------------------------+
void CListView::HighlightSelectedItem(void)
  {
//--- Sai, se a barra de rolagem está ativa
   if(m_scrollv.ScrollState())
      return;
//--- Obtém a posição atual do deslizador da barra de rolagem
   int v=m_scrollv.CurrentPos();
//--- Itera sobre a parte visível da lista
   for(int r=0; r<m_visible_items_total; r++)
     {
      //--- Se dentro do intervalo da lista
      if(v>=0 && v<m_items_total)
        {
         //--- Altera a cor de fundo e a cor do 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);
         //--- Aumenta o contador
         v++;
        }
     }
  }

Quando os elementos da lista são criados no método CListView::CreateList(), as coordenadas e a largura são calculadas de modo que eles não obstruam o quadro de fundo da lista. A largura dos elementos é calculada considerando se a lista terá uma barra de rolagem ou não. Todos os elementos abaixo do primeiro da lista são definidos um em cima do outro com a sobreposição de um pixel. Isso é necessário para excluir as lacunas em dois pixels que será visível quando os itens estiverem destacados devido o cursor do mouse estar sobre eles. Nós precisamos levar isso em consideração no cálculo da altura do fundo da lista e a barra de rolagem. Após a criação de todos os elementos, no final do método o elemento selecionado é destacado e seu texto é armazenado

//+------------------------------------------------------------------+
//| Cria os elementos da lista                                       |
//+------------------------------------------------------------------+
bool CListView::CreateList(void)
  {
//--- Coordenadas
   int x =CElement::X()+1;
   int y =0;
//--- Calcula a largura dos elementos da 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++)
     {
      //--- Elaborando o nome do objeto
      string name=CElement::ProgramName()+"_listview_edit_"+(string)i+"__"+(string)CElement::Id();
      //--- Calcular a coordenada Y
      y=(i>0) ? y+m_item_y_size-1 : CElement::Y()+1;
      //--- Cria um objeto
      if(!m_items[i].Create(m_chart_id,name,m_subwin,x,y,w,m_item_y_size))
         return(false);
      //--- Define as propriedades
      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);
      //--- Tamanho
      m_items[i].XSize(w);
      m_items[i].YSize(m_item_y_size);
      //--- Margens da borda do painel
      m_items[i].XGap(x-m_wnd.X());
      m_items[i].YGap(y-m_wnd.Y());
      //--- Armazena o ponteiro de objeto
      CElement::AddToArray(m_items[i]);
     }
//--- Destacando o elemento selecionado
   HighlightSelectedItem();
//--- Armazena o texto do elemento selecionado
   m_selected_item_text=m_value_items[m_selected_item_index];
   return(true);
  }

Ao criar uma barra de rolagem no método CListView::CreateScrollV(), no início é verificado a razão entre o número de elementos de toda a lista para a sua parte visível. Se o número total de elementos for menor ou igual ao número de elementos na sua parte visível, então não há nenhum ponto em continuar, dessa forma o programa deixa o método. Em seguida, (1) o ponteiro do formulário é armazenado, (2) as coordenadas são calculadas e (3) as propriedades são definidas. O identificador do elemento para a barra de rolagem deve ser o mesmo que o seu elemento pai. Os modos do elemento suspenso devem ser iguais também.

//+------------------------------------------------------------------+
//| Cria a barra de rolagem vertical                                 |
//+------------------------------------------------------------------+
bool CListView::CreateScrollV(void)
  {
//--- Se o número de elementos for maior que o tamanho da lista, então
//    define a barra de rolagem vertical
   if(m_items_total<=m_visible_items_total)
      return(true);
//--- Armazena o ponteiro do formulário
   m_scrollv.WindowPointer(m_wnd);
//--- Coordenadas
   int x=CElement::X()+m_area.X_Size()-m_scrollv.ScrollWidth();
   int y=CElement::Y();
//--- Define as propriedades
   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());
//--- Cria a barra de rolagem
   if(!m_scrollv.CreateScroll(m_chart_id,m_subwin,x,y,m_items_total,m_visible_items_total))
      return(false);
//---
   return(true);
  }

O tamanho ao longo do eixo Y é calculado no método principal CListView::CreateListView() para criar a lista. Como mencionado acima, quando o tamanho é calculado, a sobreposição de um pixel dos elementos da lista deve ser considerada. Por favor, também tenha em mente que o array de elementos deve estar localizado estritamente dentro do fundo da lista para que ela não obstrua seu quadro. 

//+------------------------------------------------------------------+
//| Cria a lista                                                     |
//+------------------------------------------------------------------+
bool CListView::CreateListView(const long chart_id,const int window,const int x,const int y)
  {
//--- Retorna se não há nenhum ponteiro do formulário
   if(::CheckPointer(m_wnd)==POINTER_INVALID)
     {
      ::Print(__FUNCTION__," > Antes de criar a lista, a classe deve ser passada "
              "o ponteiro do formulário: CScroll::WindowPointer(CWindow &object)");
      return(false);
     }
//--- Inicialização das variáveis
   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;
//--- Margens da borda
   CElement::XGap(m_x-m_wnd.X());
   CElement::YGap(m_y-m_wnd.Y());
//--- Cria um botão
   if(!CreateArea())
      return(false);
   if(!CreateList())
      return(false);
   if(!CreateScrollV())
      return(false);
//--- Oculta o elemento se ela for uma janela de diálogo ou está minimizada
   if(m_wnd.WindowType()==W_DIALOG || m_wnd.IsMinimized())
      Hide();
//---
   return(true);
  }

O usuário da biblioteca vai decidir se ele ou ela precisa que os elementos sejam destacados quando o cursor do mouse estiver sobre eles. O destaque será desativado por padrão. Como uma propriedade adicional ajustável, vamos criar um método para definir o alinhamento do texto (1) da margem esquerda, (2) para a margem direita ou (3) para o centro. Por padrão, o texto será alinhado à margem esquerda. 

class CListView : public CElement
  {
private:
   //--- Modo de destacar quando o cursor está pairando sobre
   bool              m_lights_hover;
   //--- Modo de alinhamento do texto na lista
   ENUM_ALIGN_MODE   m_align_mode;
   //---
public:
   //--- (1) Modo de destacar os elementos ao passar o cursor, (2) o alinhamento do texto
   void              LightsHover(const bool state)                       { m_lights_hover=state;             }
   void              TextAlign(const ENUM_ALIGN_MODE align_mode)         { m_align_mode=align_mode;          }
  };
//+------------------------------------------------------------------+
//| Construtor                                                       |
//+------------------------------------------------------------------+
CListView::CListView(void) : m_lights_hover(false),
                             m_align_mode(ALIGN_LEFT)
  {
//--- ...
  }

Antes de criar a lista, o array de dados deve ser inicializado. Para isso, crie o método CListView::ValueToList() em que a verificação do tamanho do array e o ajuste do índice será realizado em caso de exceder o tamanho do array.  

class CListView : public CElement
  {
public:
   //--- Definindo o valor na lista pelo índice especificado da fila
   void              ValueToList(const int item_index,const string value);
  };
//+------------------------------------------------------------------+
//| Armazena o valor passado da lista pelo índice especificado       |
//+------------------------------------------------------------------+
void CListView::ValueToList(const int item_index,const string value)
  {
   int array_size=::ArraySize(m_value_items);
//--- Se não houver um elemento no menu de contexto, registra
   if(array_size<1)
     {
      ::Print(__FUNCTION__," > Este método era para ser chamado, "
              "quando a lista tiver, pelo menos, um elemento!");
     }
//--- Ajuste no caso do tamanho exceder
   int i=(item_index>=array_size)? array_size-1 : (item_index <0)? 0 : item_index;
//--- Armazena o valor da lista
   m_value_items[i]=value;
  }

Agora, nós vamos testar a configuração da lista com uma barra de rolagem vertical e, em seguida, vamos adicionar gradualmente todos os métodos necessários para a sua gestão. 



Testando a Configuração da Lista

Assim que o arquivo ListView.mqh já estiver incluído na biblioteca, a classe do elemento de exibição de lista (CListView) já estará disponível para o usuário na classe personalizada. Antes de testarmos a incorporação da lista, é necessário introduzir algumas adições na classe CWndContainer. A lista é um elemento composto e, portanto, devemos assegurar que o ponteiro da barra de rolagem seja adicionado para ao elemento base. 

Para isso, vamos escrever o método CWndContainer::AddListViewElements(). Uma verificação para o nome da classe se dará no início do método. Se o elemento acabar por não ser uma lista, o programa deixará o método. Então, depois de ter aumentado o tamanho do array comum de ponteiros e ter obtido o ponteiro com o tipo de elemento certo, armazene-o.

class CWndContainer
  {
private:
   //--- Armazena os ponteiros para a lista de objetos na base
   bool              AddListViewElements(const int window_index,CElement &object);
  };
//+------------------------------------------------------------------+
//| Armazena os ponteiros para a lista de objetos na base            |
//+------------------------------------------------------------------+
bool CWndContainer::AddListViewElements(const int window_index,CElement &object)
  {
//--- Sai, se isso não for uma lista
   if(object.ClassName()!="CListView")
      return(false);
//--- Obtém o ponteiro de exibição da lista
   CListView *lv=::GetPointer(object);
//--- Incrementa o array de elementos
   int size=::ArraySize(m_wnd[window_index].m_elements);
   ::ArrayResize(m_wnd[window_index].m_elements,size+1);
//--- Obtém o ponteiro da barra de rolagem
   CScrollV *sv=lv.GetScrollVPointer();
//--- Armazena o elemento e os objetos na base
   m_wnd[window_index].m_elements[size]=sv;
   return(true);
  }

O método CWndContainer::AddListViewElements() é chamado no método público principal para adicionar os elementos à base: 

//+------------------------------------------------------------------+
//| Adiciona o ponteiro ao array de elemento                         |
//+------------------------------------------------------------------+
void CWndContainer::AddToElementsArray(const int window_index,CElement &object)
  {
//--- Se a base não contém formulários para os controles
/ --- Se a solicitação for para um formulário que não existe
//--- Adiciona ao array comum de elementos
//--- Adiciona os objetos do elemento para o array comum de objetos
//--- Armazena o ID do último elemento em todos os formulários
//--- Aumenta o contador de identificadores de elemento

//--- Armazena os ponteiros para os objetos do menu de contexto na base
//--- Armazena os ponteiros para os objetos do menu principal na base
//--- Armazena os ponteiros aos elementos do botão de divisão na base
//--- Armazena os ponteiros aos objetos dicas de contexto na base
//--- Armazena os ponteiros para a lista de objetos na base
   if(AddListViewElements(window_index,object))
      return;
  }

Vamos utilizar o EA da parte anterior da série para a realização dos testes. Nós vamos deixar apenas o seu menu principal, juntamente com os menus de contexto e a barra de estado, outros elementos devem ser eliminados. Crie uma instância de classe na classe personalizada, declare o método para criar um elemento e defina as margens da borda do formulário: 

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

Abaixo está o código do método para a criação de uma lista. A lista irá conter vinte elementos. Apenas dez elementos serão visíveis. Nós vamos permitir o destaque dos elementos quando o cursor do mouse estiver sobre eles. Selecione o sexto (5) elemento da lista. Como isto é apenas um exemplo, preencha a lista com o texto «SYMBOL» com o número do elemento

//+------------------------------------------------------------------+
//| Cria a lista 1                                                   |
//+------------------------------------------------------------------+
bool CProgram::CreateListView1(void)
  {
//--- Tamanho da lista
#define ITEMS_TOTAL1 20
//--- Armazena o ponteiro da janela
   m_listview1.WindowPointer(m_window1);
//--- Coordenadas
   int x=m_window1.X()+LISTVIEW1_GAP_X;
   int y=m_window1.Y()+LISTVIEW1_GAP_Y;
//--- Define as propriedades antes da criação
   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);
//--- Obtém o ponteiro da barra de rolagem
   CScrollV *sv=m_listview1.GetScrollVPointer();
//--- Propriedades da barra de rolagem
   sv.ThumbBorderColor(C'190,190,190');
   sv.ThumbBorderColorHover(C'180,180,180');
   sv.ThumbBorderColorPressed(C'160,160,160');
//--- Preenche a lista com os dados
   for(int r=0; r<ITEMS_TOTAL1; r++)
      m_listview1.ValueToList(r,"SYMBOL "+string(r));
//--- Cria a lista
   if(!m_listview1.CreateListView(m_chart_id,m_subwin,x,y))
      return(false);
//--- Adiciona o ponteiro do elemento para a base
   CWndContainer::AddToElementsArray(0,m_listview1);
   return(true);
  }

O método para criar uma lista deve ser chamado no método principal para a criação da interface gráfica: 

//+------------------------------------------------------------------+
//| Cria o painel de negociação                                      |
//+------------------------------------------------------------------+
bool CProgram::CreateTradePanel(void)
  {
//--- Criação do formulário 1 para os controles
//--- Criação dos controles:
//    Menu principal
//--- Menus de contexto
//--- Criando a barra de status
//--- Lista
   if(!CreateListView1())
      return(false);
//--- Redesenha o gráfico
   m_chart.Redraw();
   return(true);
  }

Agora, o código pode ser compilado e o programa poderá ser carregado ao gráfico. Se tudo for feito corretamente, você verá o resultado como na imagem abaixo:

 Fig. 2. Teste da criação do elemento da lista.

Fig. 2. Teste da criação do elemento da lista.

 

Aparentemente está tudo bem, mas atualmente esta lista não pode ser gerenciada e seus objetos não reagirão ao cursor do mouse. Na próxima parte do artigo, nós vamos escrever os métodos que nos permitam gerir a lista.

 


Métodos para Gerenciar o Elemento

Inicialmente, nós vamos criar o método que irá permitir que os elementos mudem de cor quando o cursor do mouse estiver sobre eles. Então, nós vamos precisar de um método que facilitará a restauração das cores padrão.

class CListView : public CElement
  {
public:
   //--- (1) Resetando a cor dos elementos da lista, (2) a mudança da cor dos elementos da lista quando o cursor estiver sobre eles
   void              ResetItemsColor(void);
   void              ChangeItemsColor(const int x,const int y);
  };

O reset das cores de todos os elementos exceto aquele selecionado é realizado pelo método CListView::ResetItemsColor(). No início do método, a posição do deslizador é identificada. Este valor é recebido na variável que será usada posteriormente em um loop como um contador para a identificação do elemento selecionado.

//+------------------------------------------------------------------+
//| Resetando a cor dos elementos da lista                           |
//+------------------------------------------------------------------+
void CListView::ResetItemsColor(void)
  {
//--- Obtém a posição atual do deslizador da barra de rolagem
   int v=m_scrollv.CurrentPos();
//--- Itera sobre a parte visível da lista
   for(int i=0; i<m_visible_items_total; i++)
     {
      //--- Aumenta o contador se o tamanho da lista não foi excedido
      if(v>=0 && v<m_items_total)
         v++;
      //--- Pula o elemento selecionado
      if(m_selected_item_index==v-1)
         continue;
      //--- Define a cor (fundo, texto)
      m_items[i].BackColor(m_item_color);
      m_items[i].Color(m_item_text_color);
     }
  }

Várias verificações devem ser passadas no início do método CListView::ChangeItemsColor(). O programa irá deixar o método nos seguintes casos:

  • se estiver desativado o destaque dos elementos quando o cursor estiver sobre eles;
  • se a barra de rolagem estiver sendo usada;
  • se o elemento não for do tipo suspenso e a formulário estiver bloqueado.

Em seguida, semelhante a muitos métodos desta classe, nós obtemos a posição atual da barra de rolagem na variável. Esta variável irá ser utilizada para identificar o artigo selecionado no loop, assim, ele é ignorado já que a sua cor não necessita de mudança. As coordenadas do cursor serão passadas para o método. Estas coordenadas permitirão identificar em um loop qual elemento o mouse está situado. O código mais detalhado do método CListView::ChangeItemsColor() é apresentado abaixo. 

//+------------------------------------------------------------------+
//--- Altera a cor dos elementos da lista quando o cursor estiver sobre eles |
//+------------------------------------------------------------------+
void CListView::ChangeItemsColor(const int x,const int y)
  {
//--- Sai se estiver desativado o destaque dos elementos quando o cursor estiver sobre eles ou a barra de rolagem está ativa
   if(!m_lights_hover || m_scrollv.ScrollState())
      return;
//--- Sai, se não for um elemento suspenso e o formulário está bloqueado
   if(!CElement::IsDropdown() && m_wnd.IsLocked())
      return;
//--- Obtém a posição atual do deslizador da barra de rolagem
   int v=m_scrollv.CurrentPos();
//--- Identifica qual elemento o cursor está e destaca-o
   for(int i=0; i<m_visible_items_total; i++)
     {
      //--- Aumenta o contador se o tamanho da lista não foi excedido
      if(v>=0 && v<m_items_total)
         v++;
      //--- Pula o elemento selecionado
      if(m_selected_item_index==v-1)
         continue;
      //--- Se o cursor está sobre este elemento, destaca-o
      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);
        }
      //--- Se o cursor não está sobre este elemento, atribui a cor adequada ao seu estado
      else
        {
         m_items[i].BackColor(m_item_color);
         m_items[i].Color(m_item_text_color);
        }
     }
  }

Agora, nós precisamos usar o método CListView::ChangeItemsColor() no manipulador de eventos da classe CListView:

//+------------------------------------------------------------------+
//| Manipulador de eventos                                           |
//+------------------------------------------------------------------+
void CListView::OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam)
  {
   if(id==CHARTEVENT_MOUSE_MOVE)
     {
      //--- Lista oculta
      if(!CElement::IsVisible())
         return;
      //--- Coordenadas
      int x=(int)lparam;
      int y=(int)dparam;
      //--- Muda a cor dos elementos da lista quando o cursor estiver sobre ele
      ChangeItemsColor(x,y);
      return;
     }
  }

Se nós compilarmos o programa de testes, então, quando o cursor do mouse passar sobre os elementos da lista, sua cor será alterada, como é mostrado na imagem abaixo:

Fig. 3. Teste de mudar a cor dos elementos da lista quando o cursor do mouse estiver sobre eles. 

Fig. 3. Teste de mudar a cor dos elementos da lista quando o cursor do mouse estiver sobre eles.

Para permitir que a lista mova juntamente com o deslizador da barra de rolagem, nós vamos escrever o método CListView::ShiftList(). Este método também começa com o armazenamento da posição atual do cursor na variável. Semelhante ao método anterior, ele será utilizado como um contador no loop para armazenar o elemento selecionado na lista e também para mover os dados. Veja o código abaixo. 

class CListView : public CElement
  {
public:
   //--- Rolando a lista
   void              ShiftList(void);
  };
//+------------------------------------------------------------------+
//| Move a lista ao longo da barra de rolagem                        |
//+------------------------------------------------------------------+
void CListView::ShiftList(void)
  {
//--- Obtém a posição atual do deslizador da barra de rolagem
   int v=m_scrollv.CurrentPos();
//--- Itera sobre a parte visível da lista
   for(int i=0; i<m_visible_items_total; i++)
     {
      //--- Se dentro do intervalo da lista
      if(v>=0 && v<m_items_total)
        {
         //--- Move o texto, a cor de fundo e a cor do 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);
         //--- Aumenta o contador
         v++;
        }
     }
  }

O método CListView::ShiftList() deve ser chamado no manipulador CListView::OnEvent() desde que o método CScrollV::ScrollBarControl() da barra de rolagem retorne true. Isso significa que o gerenciamento do deslizador foi habilitado.

class CListView : public CElement
  {
private:
   //--- Estado do botão esquerdo do mouse (pressionado/liberado)
   bool              m_mouse_state;
  };
//+------------------------------------------------------------------+
//| Manipulador de eventos                                           |
//+------------------------------------------------------------------+
void CListView::OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam)
  {
   if(id==CHARTEVENT_MOUSE_MOVE)
     {
      //--- Lista oculta
      if(!CElement::IsVisible())
         return;
      //--- Coordenadas e o estado do botão esquerdo do mouse
      int x=(int)lparam;
      int y=(int)dparam;
      m_mouse_state=(bool)int(sparam);
      //--- Verificando o foco sobre a lista
      CElement::MouseFocus(x>CElement::X() && x<CElement::X2() && 
                           y>CElement::Y() && y<CElement::Y2());
      //--- Mova a lista se a gestão do deslizador for habilitada
      if(m_scrollv.ScrollBarControl(x,y,m_mouse_state))
         ShiftList();
      //--- Muda a cor dos elementos da lista quando o cursor estiver sobre ele
      ChangeItemsColor(x,y);
      return;
     }
  }

Após a compilação do programa, a lista pode ser gerida pelo deslizador da barra de rolagem, como é mostrado na imagem abaixo. Tudo foi implementado de tal forma que, mesmo se o cursor deixar os limites do deslizador após o botão esquerdo do mouse ter sido pressionado sobre ele, a gestão será passada para a barra de rolagem o deslizador estará se movendo.

 Fig. 4. Gerenciando a lista usando o deslizador da barra de rolagem.

Fig. 4. Gerenciando a lista usando o deslizador da barra de rolagem.

 

Agora, nós precisamos de um método para identificar o pressionamento em um dos elementos da lista. Vamos criar um método privado e chamá-lo de CListView::OnClickListItem(). É necessário também, o método privado CListView::IdFromObjectName() para extrair e identificador o elemento do nome do objeto que foi mostrado nas classes de outros elementos da nossa biblioteca.

Além disso, nós vamos precisar de um identificador único de clique no elemento da lista (ON_CLICK_LIST_ITEM). Adicione-o ao arquivo Defines.mqh

//+------------------------------------------------------------------+
//|                                                      Defines.mqh |
//|                        Copyright 2015, MetaQuotes Software Corp. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
#define ON_CLICK_LIST_ITEM        (16) // Selecionando o elemento da lista

No início do método CListView::OnClickListItem(), há verificações para o nome e o identificador do objeto que foi clicado. Em seguida, a variável local é atribuída a posição atual do deslizador. Esta variável é utilizada como um contador no loop para identificar o índice e o texto do elemento. No final do método, uma mensagem é enviada contendo (1) o identificador do evento ON_CLICK_LIST_ITEM, (2) o elemento identificador e (3) o texto do elemento atual selecionado. 

class CListView : public CElement
  {
private:
   //--- Lidando com o pressionamento da lista de elementos
   bool              OnClickListItem(const string clicked_object);
   //--- Obtendo o identificador do nome da lista de elementos
   int               IdFromObjectName(const string object_name);
  };
//+------------------------------------------------------------------+
//| Lidando com o pressionamento da lista de elementos               |
//+------------------------------------------------------------------+
bool CListView::OnClickListItem(const string clicked_object)
  {
//--- Sai, se o pressionamento não foi no elemento de menu
   if(::StringFind(clicked_object,CElement::ProgramName()+"_listview_edit_",0)<0)
      return(false);
//--- Obtém o identificador e o índice a partir do nome do objeto
   int id=IdFromObjectName(clicked_object);
//--- Retorna, se o tipo definido não corresponder
   if(id!=CElement::Id())
      return(false);
//--- Obtém a posição atual do deslizador da barra de rolagem
   int v=m_scrollv.CurrentPos();
//--- Vai para a parte visível da lista
   for(int i=0; i<m_visible_items_total; i++)
     {
      //--- Se esta lista de elementos foi selecionada
      if(m_items[i].Name()==clicked_object)
        {
         m_selected_item_index =v;
         m_selected_item_text  =m_value_items[v];
        }
      //--- Se dentro do intervalo da lista
      if(v>=0 && v<m_items_total)
         //--- Aumenta o contador
         v++;
     }
//--- Envia uma mensagem sobre ele
   ::EventChartCustom(m_chart_id,ON_CLICK_LIST_ITEM,CElement::Id(),0,m_selected_item_text);
   return(true);
  }

Agora, quando nós temos todos os métodos necessários para gerenciar a lista, nós apenas temos que colocar o código no manipulador de eventos CListView::OnEvent() da lista, como é mostrado no código abaixo. Pressionando em um dos elementos da lista, o método CListView::HighlightSelectedItem() é chamado. Neste método, faz se o monitoramento dos botões pressionados da barra de rolagem. Se em um de seus botões foi pressionado, então a lista é movida em relação à posição atual do deslizador. 

//+------------------------------------------------------------------+
//| Manipulador de eventos                                           |
//+------------------------------------------------------------------+
void CListView::OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam)
  {
//--- Manipulando o pressionamento de objetos
   if(id==CHARTEVENT_OBJECT_CLICK)
     {
      //--- Se o pressionamento foi sobre os elementos da lista
      if(OnClickListItem(sparam))
        {
         //--- Destacando o elemento
         HighlightSelectedItem();
         return;
        }
      //--- Se o pressionamento foi sobre os botões da barra de rolagem
      if(m_scrollv.OnClickScrollInc(sparam) || m_scrollv.OnClickScrollDec(sparam))
        {
         //--- Move a lista junto a barra de rolagem
         ShiftList();
         return;
        }
     }
  }


 

Avanço Rápido da Lista

Nós completamos o desenvolvimento do mínimo exigido para gerenciar a lista. Nós podemos estender sua capacidade. Vamos criar mais um método privado para o avanço rápido da lista, por pressionar o botão do mouse e segura-lo para baixo em um dos botões da barra de rolagem.

Nós precisamos garantir que, quando o botão esquerdo do mouse estiver pressionado sobre os botões da barra de rolagem, haja um pequeno atraso antes do avanço rápido da lista. Se isso não for feito, o avanço rápido será realizado de imediato, o que não é adequado para o momento em que o botão foi clicado apenas uma vez na lista para ser movido apenas por um elemento. O identificador SPIN_DELAY_MSC deve ser adicionado no arquivo Defines.mqh com o valor de -450. Isto significa que o atraso será de 450 milissegundos. 

//+------------------------------------------------------------------+
//|                                                      Defines.mqh |
//|                        Copyright 2015, MetaQuotes Software Corp. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
//--- Atraso antes de ativar o avanço rápido do contador (milissegundos)
#define SPIN_DELAY_MSC (-450)

Abaixo está o código do método CListView::FastSwitching(). Nós também precisamos declarar o campo m_timer_counter que será utilizado como o timer do contador. No início do método CListView::FastSwitching(), nós vamos verificar o foco na lista. Se não houver nenhum foco, o programa sairá do método. Então, se o botão do mouse for liberado, é atribuído ao contador o valor do atraso (no nosso caso, é de -450ms). Se o botão do mouse foi pressionado, o valor do contador é aumentado pelo passo do timer definido na biblioteca (neste caso, é de 16ms). Em seguida, há uma condição que impede que o programa funcione até que o valor do contador não seja igual ou maior que zero. Assim que esta condição forem satisfeitas, o estado dos botões da barra de rolagem será marcado. Dependendo qual botão foi pressionado para baixo, o método para imitar o pressionando do botão é chamado. Em seguida, a lista é movida de acordo com a posição atual do deslizador da barra de rolagem. 

class CListView : public CElement
  {
private:
   //--- Timer do contador para o avanço rápido da lista
   int               m_timer_counter;
private:
   //--- Avanço rápido da lista
   void              FastSwitching(void);
  };
//+------------------------------------------------------------------+
//| Avanço rápido da barra de rolagem                                |
//+------------------------------------------------------------------+
void CListView::FastSwitching(void)
  {
//--- Sai, se não houver foco na lista
   if(!CElement::MouseFocus())
      return;
//--- Reseta o contador para o valor inicial quando o botão do mouse for liberado
   if(!m_mouse_state)
      m_timer_counter=SPIN_DELAY_MSC;
//--- Se o botão do mouse é pressionado para baixo
   else
     {
      //--- Aumenta o contador pelo passo definido
      m_timer_counter+=TIMER_STEP_MSC;
      //--- Sai, se menor que zero
      if(m_timer_counter<0)
         return;
      //--- Se a rolagem é para cima
      if(m_scrollv.ScrollIncState())
         m_scrollv.OnClickScrollInc(m_scrollv.ScrollIncName());
      //--- Se a rolagem é para baixo
      else if(m_scrollv.ScrollDecState())
         m_scrollv.OnClickScrollDec(m_scrollv.ScrollDecName());
      //--- Move a lista
      ShiftList();
     }
  }

O método CListView::FastSwitching() deve ser chamado dentro do timer CListView::OnEventTimer() como é mostrado abaixo. Se a lista for um elemento do tipo suspenso, verificações adicionais não serão necessárias. Caso contrário, é preciso verificar se o formulário está atualmente bloqueado. 

//+------------------------------------------------------------------+
//| Timer                                                            |
//+------------------------------------------------------------------+
void CListView::OnEventTimer(void)
  {
//--- Se este é um elemento suspenso
   if(CElement::IsDropdown())
      //--- Avanço rápido da lista
      FastSwitching();
//--- Se este não é um elemento suspenso, leva em consideração a disponibilidade atual do formulário
   else
     {
      //--- Acompanha o avanço rápido da lista apenas se o formulário não estiver bloqueado
      if(!m_wnd.IsLocked())
         FastSwitching();
     }
  }

Agora, todos os métodos para gerenciar a lista estão prontos. Nós podemos testá-los. Vamos adicionar mais duas listas com o que criamos anteriormente:

Fig. 5. Testando três listas na interface gráfica. 

Fig. 5. Testando três listas na interface gráfica.

Nós vamos receber as mensagens das listas no manipulador de eventos da classe personalizada (CProgram): 

//+------------------------------------------------------------------+
//| Manipulador de eventos                                           |
//+------------------------------------------------------------------+
void CProgram::OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam)
  {
//--- Evento de pressionamento na lista de elementos
   if(id==CHARTEVENT_CUSTOM+ON_CLICK_LIST_ITEM)
     {
      if(lparam==m_listview1.Id())
         ::Print(__FUNCTION__," > Esta mensagem é da primeira lista > id: ",id,"; lparam: ",lparam,"; dparam: ",dparam,"; sparam: ",sparam);
      else if(lparam==m_listview2.Id())
         ::Print(__FUNCTION__," > Esta mensagem é da segunda lista > id: ",id,"; lparam: ",lparam,"; dparam: ",dparam,"; sparam: ",sparam);
      else if(lparam==m_listview3.Id())
         ::Print(__FUNCTION__," > Esta mensagem é da terceira lista > id: ",id,"; lparam: ",lparam,"; dparam: ",dparam,"; sparam: ",sparam);
     }
  }

Quando os elementos da lista são pressionados, as mensagens serão impressas no registro como é mostrado abaixo: 

2016.01.16 13:02:00.085 TestLibrary (GBPUSD,D1) CProgram::OnEvent > Esta mensagem é da primeira lista > id: 1016; lparam: 7; dparam: 0.0; sparam: SYMBOL 11
2016.01.16 13:01:59.056 TestLibrary (GBPUSD,D1) CProgram::OnEvent > Esta mensagem é da terceira lista > id: 1016; lparam: 9; dparam: 0.0; sparam: SYMBOL 12
2016.01.16 13:01:58.479 TestLibrary (GBPUSD,D1) CProgram::OnEvent > Esta mensagem é da segunda lista > id: 1016; lparam: 8; dparam: 0.0; sparam: SYMBOL 9
2016.01.16 13:01:57.868 TestLibrary (GBPUSD,D1) CProgram::OnEvent > Esta mensagem é da terceira lista > id: 1016; lparam: 9; dparam: 0.0; sparam: SYMBOL 19
2016.01.16 13:01:56.854 TestLibrary (GBPUSD,D1) CProgram::OnEvent > Esta mensagem é da segunda lista > id: 1016; lparam: 8; dparam: 0.0; sparam: SYMBOL 4
2016.01.16 13:01:56.136 TestLibrary (GBPUSD,D1) CProgram::OnEvent > Esta mensagem é da primeira lista > id: 1016; lparam: 7; dparam: 0.0; sparam: SYMBOL 9
2016.01.16 13:01:55.433 TestLibrary (GBPUSD,D1) CProgram::OnEvent > Esta mensagem é da primeira lista > id: 1016; lparam: 7; dparam: 0.0; sparam: SYMBOL 14

 


Conclusão

Neste artigo, nós consideramos o controle composto lista. Nós também demonstramos como uma barra de rolagem vertical pode ser usada. No próximo artigo, nós vamos falar sobre outro controle composto - o combobox

Você pode baixar o material da parte V e testar o seu funcionamento. Se você tiver dúvidas sobre a utilização do material a partir desses arquivos, você poderá consultar a descrição detalhada do desenvolvimento da biblioteca em um dos artigos da lista abaixo ou fazer sua pergunta nos comentários deste artigo.

Lista de artigos da quinta parte:

Traduzido do russo pela MetaQuotes Ltd.
Artigo original: https://www.mql5.com/ru/articles/2380

Arquivos anexados |
Proteção contra falsos positivos do robô de negociação Proteção contra falsos positivos do robô de negociação
A rentabilidade dos sistemas de negociação é determinada não só pela lógica e precisão da dinâmica dos instrumentos financeiros, mas também pela qualidade do algoritmo de execução dessa lógica. Os falsos positivos são uma manifestação característica da má execução da lógica fundamental do robô de negociação. Neste artigo trataremos várias opções para resolver esse problema.
Módulo de sinais de negociação utilizando o sistema Bill Williams Módulo de sinais de negociação utilizando o sistema Bill Williams
O artigo descreve as regras do sistema de negociação Bill Williams, o procedimento da aplicação de um módulo MQL5 desenvolvido com o objetivo de procurar e marcar padrões deste sistema no gráfico, as negociações automatizadas de acordo com os padrões encontrados e por fim, apresenta os resultados dos testes em vários instrumentos de negociação.
Criação de estratégias de negociação manuais usando lógica fuzzy Criação de estratégias de negociação manuais usando lógica fuzzy
No artigo é considerada a possibilidade de melhorar as estratégias de negociação manuais usando a teoria dos conjuntos difusos (fuzzy). Como exemplo, é descrito passo a passo o motor de busca de estratégias e a seleção de seus parâmetros, o uso de lógica fuzzy para diluir os critérios demasiado formais de entrada no mercado. Assim, Depois da modificação da estratégia, nós obtemos condições flexíveis de abertura de posição que respondem melhor à situação de mercado.
Avaliação da eficácia dos sistemas de negociação pela análise de seus componentes Avaliação da eficácia dos sistemas de negociação pela análise de seus componentes
Este artigo explora a eficácia dos sistemas de negociação complexos pela análise da eficiência de seus componentes individuais. Qualquer análise é um dos componentes-chave de sucesso em negociação nos mercados financeiros, seja gráfica, com base em indicadores, ou qualquer outro. Portanto, de certa maneira, este arquivo é uma pesquisa sobre alguns sistemas de negociação simples e independentes, no qual podemos analisar a sua eficácia em conjunto com a utilidade da aplicação.