Interfaces Gráficas V: O Controle Lista (Capítulo 2)
Conteúdo
- Introdução
- O Controle Lista
- Desenvolvimento da Classe para a criação do Elemento
- Testando a Configuração da Lista
- Métodos para Gerenciar o Elemento
- Avanço Rápido da Lista
- Conclusão
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:
- Fundo da lista.
- Array de elementos da lista.
- 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.
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.
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.
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.
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:
- Interfaces Gráficas V: A Barra de Rolagem Vertical e Horizontal (Capítulo 1)
- Interfaces Gráficas V: O Controle Lista (Capítulo 2)
- Interfaces Gráficas V: O Controle Combobox (Capítulo 3)
Traduzido do russo pela MetaQuotes Ltd.
Artigo original: https://www.mql5.com/ru/articles/2380
- Aplicativos de negociação gratuitos
- 8 000+ sinais para cópia
- Notícias econômicas para análise dos mercados financeiros
Você concorda com a política do site e com os termos de uso