Interfaces Gráficas VI: Os Controles Caixa de Seleção, Campo de Edição e seus Tipos Combinados (Capítulo 1)
Conteúdo
- Introdução
- O Controle Caixa de Seleção
- Desenvolvimento de uma Classe para a Criação do Controle Caixa de Seleção
- Teste do Controle Caixa de Seleção
- O Controle Campo de Edição
- Desenvolvimento de uma Classe para a Criação do Controle Campo de Edição
- Teste do Controle Campo de Edição
- Outros Controles com as Caixas de Seleção
- 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.
A sexta parte da série consistirá de dois capítulos. No primeiro capítulo, nós vamos criar quatro controles:
- Caixa de seleção (checkbox)
- Campo de Edição
- Campo de edição com a caixa de seleção
- Lista combinada com a caixa de seleção
Nós vamos discutir apenas a caixa de seleção e o campo de edição já que a combinação dos dois não irá conter nada que nós não vimos antes.
O segundo artigo será dedicado aos seguintes controles:
- Deslizante
- Deslizante duplo
O Controle Caixa de Seleção
O controle caixa de seleção é designado para o gerenciamento dos parâmetros que podem ter apenas dois estados. Um botão com dois ícones é usado para identificar o estado atual do parâmetro para o qual o controle está vinculado. O ícone com o símbolo de verificação significa que o parâmetro está habilitado (on). O ícone sem o símbolo de verificação significa que o parâmetro está desativado (off). Perto do botão é possível encontrar uma breve descrição do parâmetro.
Este elemento será composto por três objetos gráficos. Eles são:
- Fundo
- Ícone (botão)
- Rótulo de texto
Fig. 1. Partes integrantes do controle caixa de seleção.
Vamos dar uma olhada mais a fundo na classe deste controle.
Desenvolvimento de uma Classe para a Criação do Controle Caixa de Seleção
Na terceira parte desta série, nós falamos sobre um controle semelhante - um botão com ícone e sua classe CIconButton (Veja o artigo Interfaces Gráficas III: Botões simples e Multifuncionais (Capítulo 1)). O controle caixa de seleção é semelhante à um botão com ícone no modo de dois estados. A única diferença aqui é que o botão em diferentes estados (on/off) possui uma cor de fundo e de texto diferente (se for definida), e no caso de uma caixa de seleção, apenas a cor do ícone e de texto que se alteram (se for definida). A cor de fundo da caixa de seleção será a mesma que a de fundo da janela ao qual ele está anexado. O fundo aqui será usado como a área para identificar o cursor do mouse em relação aos limites desta área e para identificar também o pressionamento do botão esquerdo do mouse sobre o controle. O controle caixa de seleção é mais simples do que um botão com ícone, já que ele tem menos propriedades que o usuário possa definir.
Crie o arquivo CheckBox.mqh e inclua-o no arquivo WndContainer.mqh da biblioteca:
//+------------------------------------------------------------------+ //| WndContainer.mqh | //| Copyright 2015, MetaQuotes Software Corp. | //| http://www.mql5.com | //+------------------------------------------------------------------+ #include "CheckBox.mqh"
Cria a classe CCheckBox com os métodos padrão para todos os elementos da biblioteca no arquivo CheckBox.mqh:
//+------------------------------------------------------------------+ //| CheckBox.mqh | //| Copyright 2015, MetaQuotes Software Corp. | //| http://www.mql5.com | //+------------------------------------------------------------------+ #include "Element.mqh" #include "Window.mqh" //+------------------------------------------------------------------+ //| Classe para criar a caixa de seleção | //+------------------------------------------------------------------+ class CCheckBox : public CElement { private: //--- Ponteiro para o formulário ao qual o elemento está anexado CWindow *m_wnd; //--- public: CCheckBox(void); ~CCheckBox(void); //--- 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); //--- Resetar a cor virtual void ResetColors(void); };
Antes do controle ser criado, o usuário será capaz de utilizar os métodos para a instalação das propriedades listadas abaixo:
- Fundo do controle
- Ícones para a caixa de seleção nos estados ativo e bloqueado
- Margens para o rótulo de texto
- Cores do rótulo de texto em diferentes estados
Haverá quatro ícones para o botão caixa de seleção - dois ícones para o estado ativo e dois para o estado bloqueado. Os ícones para todos os estados estão disponíveis no final deste artigo, mas você poderá utilizar os seus ícones personalizados.
class CCheckBox : public CElement { private: //--- Cor de fundo da caixa de seleção color m_area_color; //--- Ícones da caixa de seleção nos estados ativo e bloqueado string m_check_bmp_file_on; string m_check_bmp_file_off; string m_check_bmp_file_on_locked; string m_check_bmp_file_off_locked; //--- Texto da caixa de seleção string m_label_text; //--- Margens do rótulo de texto int m_label_x_gap; int m_label_y_gap; //--- Cores do rótulo de texto em diferentes estados color m_label_color; color m_label_color_off; color m_label_color_hover; color m_label_color_locked; color m_label_color_array[]; //--- Prioridades do clique do botão esquerdo do mouse int m_zorder; int m_area_zorder; //--- public: //--- Definir os rótulos para o botão nos estados ativo e bloqueado void CheckFileOn(const string file_path) { m_check_bmp_file_on=file_path; } void CheckFileOff(const string file_path) { m_check_bmp_file_off=file_path; } void CheckFileOnLocked(const string file_path) { m_check_bmp_file_on_locked=file_path; } void CheckFileOffLocked(const string file_path) { m_check_bmp_file_off_locked=file_path; } //--- (1) cor de fundo, (2) margens do rótulo de texto void AreaColor(const color clr) { m_area_color=clr; } void LabelXGap(const int x_gap) { m_label_x_gap=x_gap; } void LabelYGap(const int y_gap) { m_label_y_gap=y_gap; } //--- Cor do texto em diferentes estados void LabelColor(const color clr) { m_label_color=clr; } void LabelColorOff(const color clr) { m_label_color_off=clr; } void LabelColorHover(const color clr) { m_label_color_hover=clr; } void LabelColorLocked(const color clr) { m_label_color_locked=clr; } //--- Descrição da caixa de seleção string LabelText(void) const { return(m_label.Description()); } };
Para criar o controle de caixa de seleção, nós vamos precisar de três métodos privados e um público:
class CCheckBox : public CElement { private: //--- Objetos para a criação da caixa de seleção CRectLabel m_area; CBmpLabel m_check; CLabel m_label; //--- public: //--- Métodos para criar uma caixa de seleção bool CreateCheckBox(const long chart_id,const int subwin,const string text,const int x,const int y); //--- private: bool CreateArea(void); bool CreateCheck(void); bool CreateLabel(void); };
Vamos criar os métodos CCheckBox::CheckButtonState() e CCheckBox::CheckBoxState() para gerenciar o estado da caixa de seleção:
class CCheckBox : public CElement { private: //--- Estado do botão da caixa de seleção bool m_check_button_state; //--- Estado da caixa de seleção(disponível/bloqueado) bool m_checkbox_state; //--- public: //--- Retorna/define o estado da caixa de seleção bool CheckBoxState(void) const { return(m_checkbox_state); } void CheckBoxState(const bool state); //--- Retorna/define o estado do botão da caixa de seleção bool CheckButtonState(void) const { return(m_check.State()); } void CheckButtonState(const bool state); };
Para bloquear/desbloquear o controle, use o método CCheckBox::CheckBoxState():
//+------------------------------------------------------------------+ //| Define o estado do controle | //+------------------------------------------------------------------+ void CCheckBox::CheckBoxState(const bool state) { //--- Estado do Controle m_checkbox_state=state; //--- Ícone m_check.BmpFileOn((state)? "::"+m_check_bmp_file_on : "::"+m_check_bmp_file_on_locked); m_check.BmpFileOff((state)? "::"+m_check_bmp_file_off : "::"+m_check_bmp_file_off_locked); //--- Cor do rótulo de texto m_label.Color((state)? m_label_color : m_label_color_locked); }
Para definir o estado do botão caixa de seleção, utilize o método CCheckBox::CheckButtonState():
//+------------------------------------------------------------------+ //| Define o estado do botão da caixa de seleção | //+------------------------------------------------------------------+ void CCheckBox::CheckButtonState(const bool state) { //--- Sai, se o controle está bloqueado if(!m_checkbox_state) return; //--- Define o estado do botão m_check.State(state); m_check_button_state=state; //--- Alterar as cores de acordo com o estado atual m_label.Color((state)? m_label_color : m_label_color_off); CElement::InitColorArray((state)? m_label_color : m_label_color_off,m_label_color_hover,m_label_color_array); }
Nós só temos que criar um método para lidar com o clique sobre o controle de caixa de seleção. Este método será chamado no manipulador principal do controle pelo evento CCheckBox::OnEvent() com o identificador CHARTEVENT_OBJECT_CLICK. Vamos chamá-lo de CCheckBox::OnClickLabel(). No início deste método, há uma verificação se o clique foi sobre a área de controle (no fundo). Para evitar confusão sobre qual objeto do controle que o clique deve exatamente levar em conta, o fundo possui uma prioridade para o clique do botão esquerdo do mouse no valor igual a 1 enquanto que os outros objetos tem o valor igual a 0. Portanto, mesmo que o clique foi quando o cursor estava sobre um botão da caixa de seleção ou de um rótulo de texto, ele será tratado como um clique no fundo do controle, que é o objeto com a maior prioridade nesta área. Então, nós vamos usar o método CCheckBox::CheckButtonState() para definir o estado oposto no botão caixa de seleção. No final do método, é enviado um evento personalizado com (1) o identificador do evento ON_CLICK_LABEL, (2) o identificador do elemento e (3) a descrição do elemento.
class CCheckBox : public CElement { private: //--- Manipulação do clique sobre o elemento bool OnClickLabel(const string clicked_object); }; //+------------------------------------------------------------------+ //| Manipulador de evento | //+------------------------------------------------------------------+ void CCheckBox::OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam) { //--- Manipula o clique do botão esquerdo do mouse sobre o objeto if(id==CHARTEVENT_OBJECT_CLICK) { //--- Clique na caixa de seleção if(OnClickLabel(sparam)) return; } } //+------------------------------------------------------------------+ //| Clique no rótulo do elemento | //+------------------------------------------------------------------+ bool CCheckBox::OnClickLabel(const string clicked_object) { //--- Sai se o nome for diferente if(m_area.Name()!=clicked_object) return(false); //--- Sai, se o controle está bloqueado if(!m_checkbox_state) return(false); //--- Muda para o estado oposto CheckButtonState(!m_check.State()); //--- O cursor do mouse está atualmente sobre o elemento m_label.Color(m_label_color_hover); //--- Envia uma mensagem sobre ele ::EventChartCustom(m_chart_id,ON_CLICK_LABEL,CElement::Id(),0,m_label.Description()); return(true); }
Teste do Controle Caixa de Seleção
Todos os métodos da classe CCheckBox estão prontos para que nós possamos testar o funcionamento deste controle. O arquivo com a classe deste controle já deve estar incluída na biblioteca. O EA para testes pode ser copiado do artigo anterior. Remova todos os controles dele, exceto o menu principal e a barra de estado. Crie duas caixas de seleção para o teste. Certifique-se de que a segunda caixa de seleção está bloqueada por padrão no momento de carregar o programa ao gráfico. A primeira caixa de seleção estará disponível para interação com o usuário, porém, no estado desativado. Habilitando a primeira caixa de seleção, será um sinal para desbloquear a segunda caixa de seleção. O mesmo irá funcionar para a situação oposta - desativando a primeira caixa de seleção será um sinal para bloquear a segunda caixa de seleção.
Na classe personalizada CProgram crie duas instâncias da classe CCheckBox e declare dois métodos para a criação das caixas de seleção e especifique as margens da borda do formulário em que eles serão anexados:
//+------------------------------------------------------------------+ //| Classe para a criação de um aplicativo | //+------------------------------------------------------------------+ class CProgram : public CWndEvents { private: //--- Caixas de seleção CCheckBox m_checkbox1; CCheckBox m_checkbox2; //--- private: //--- Caixas de seleção #define CHECKBOX1_GAP_X (7) #define CHECKBOX1_GAP_Y (50) bool CreateCheckBox1(const string text); #define CHECKBOX2_GAP_X (30) #define CHECKBOX2_GAP_Y (75) bool CreateCheckBox2(const string text); };
A única diferença na implementação do método será que a disponibilidade da segunda caixa de seleção irá depender do estado da primeira:
//+------------------------------------------------------------------+ //| Cria a caixa de seleção 2 | //+------------------------------------------------------------------+ bool CProgram::CreateCheckBox2(string text) { //--- Passa o objeto do painel m_checkbox2.WindowPointer(m_window1); //--- Coordenadas int x=m_window1.X()+CHECKBOX2_GAP_X; int y=m_window1.Y()+CHECKBOX2_GAP_Y; //--- Define as propriedades antes da criação m_checkbox2.XSize(90); m_checkbox2.YSize(18); m_checkbox2.AreaColor(clrWhiteSmoke); m_checkbox2.LabelColor(clrBlack); m_checkbox2.LabelColorOff(clrBlack); m_checkbox2.LabelColorLocked(clrSilver); //--- Cria o controle if(!m_checkbox2.CreateCheckBox(m_chart_id,m_subwin,text,x,y)) return(false); //--- A disponibilidade irá depender do estado atual da primeira caixa de seleção m_checkbox2.CheckBoxState(m_checkbox1.CheckButtonState()); //--- Adiciona o objeto para o array comum dos grupos de objetos CWndContainer::AddToElementsArray(0,m_checkbox2); return(true); }
Coloque as chamadas do método para a criação da caixas de seleção no método principal da criação da interface gráfica. Uma versão resumida deste código é apresentado a seguir:
//+------------------------------------------------------------------+ //| 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 //--- Caixas de seleção if(!CreateCheckBox1("Checkbox 1")) return(false); if(!CreateCheckBox2("Checkbox 2")) return(false); //--- Redesenha o gráfico m_chart.Redraw(); return(true); }
Nós vamos receber e processar uma mensagem com o identificador ON_CLICK_LABEL no manipulador de eventos da classe CProgram. A disponibilidade da segunda caixa de seleção será definido pelo estado da primeira como é mostrado no código abaixo:
//+------------------------------------------------------------------+ //| Manipulador de eventos | //+------------------------------------------------------------------+ void CProgram::OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam) { //--- O evento clique na caixa de seleção if(id==CHARTEVENT_CUSTOM+ON_CLICK_LABEL) { ::Print(__FUNCTION__," > id: ",id,"; lparam: ",lparam,"; dparam: ",dparam,"; sparam: ",sparam); //--- Se o clique foi na primeira caixa de seleção if(lparam==m_checkbox1.Id()) { //--- Define o estado da segunda caixa de seleção m_checkbox2.CheckBoxState(m_checkbox1.CheckButtonState()); } } }
Agora, compile os arquivos e carregue o EA ao gráfico. O resultado deve ser como na imagem abaixo:
Fig. 2. Testando o controle caixa de seleção.
A imagem mostra que a segunda caixa de seleção está bloqueada. A indicação aqui é a cor do texto e o ícone do botão correspondente a este estado (as cores desvanecidas). Tudo está funcionando bem e nós vamos avançar para o desenvolvimento de uma classe para a criação do controle campo de edição.
O Controle Campo de Edição
O controle campo de edição é usado para definir um valor numérico no campo de texto. O valor pode ser inserido manualmente ou com a ajuda dos botões de rolagem. Os valores máximos e mínimos (limites), bem como o passo da rolagem podem ser definidos pelo usuário. Se o botão com a seta para cima/baixo é pressionado uma vez, então o valor será aumentado/diminuído no passo especificado nas configurações. Se a seta de cima para baixo é pressionada e mantida para baixo, o valor no campo de edição será alterado rapidamente. A rolagem irá parar, tendo atingido o máximo ou mínimo permitido.
Este controle será composto por cinco objetos gráficos. Eles são:
- Fundo
- Rótulo de texto
- Campo de Edição
- Dois botões para a rolagem dos valores no controle campo de edição
Fig. 3. Partes integrantes do controle campo de edição.
Na próxima parte do artigo, nós vamos escrever uma classe para a criação deste controle de interface.
Desenvolvimento de uma Classe para a Criação do Controle Campo de Edição
Semelhante ao controle caixa de seleção, o controle campo de edição é simples, não sendo composto. Em outras palavras, ele é constituído por objetos simples e não contêm quaisquer outros controles. É por isso que não há necessidade de adições no arquivo WndContainer.mqh. Nós precisamos incluir apenas o arquivo com a classe do controle campo de edição.
Agora, crie o arquivo SpinEdit.mqh e inclua-o no arquivo WndContainer.mqh:
//+------------------------------------------------------------------+ //| WndContainer.mqh | //| Copyright 2015, MetaQuotes Software Corp. | //| http://www.mql5.com | //+------------------------------------------------------------------+ #include "SpinEdit.mqh"
No arquivo SpinEdit.mqh, crie a classe CSpinEdit com os métodos padrão para todos os elementos da biblioteca como na descrição da classe CCheckBox no início deste artigo. É por isso que nós vamos diretamente para a descrição das propriedades do controle campo de edição.
Abaixo está a lista de todas as propriedades do controle campo de edição que estará disponível para o usuário definir.
- Cor de fundo
- Texto da descrição do campo de edição
- Margens do rótulo de texto
- Cores do texto em diferentes estados
- Tamanho do campo de edição
- Margem do campo de edição do lado direito do elemento
- Cores do campo de edição em estados diferentes
- Cores do texto do campo de edição em diferentes estados
- Cores do quadro do campo de edição em diferentes estados
- Rótulos das chaves nos estados ativo e bloqueado
- Margens do botão (do lado direito)
- Modo de resetar o valor para a mínima
- Valor mínimo e máximo
- Passo para alterar o valor do campo de edição usando os botões
- Modo de alinhamento de texto
- Número de casas decimais
O código a seguir apresenta os campos e métodos da classe para definir as propriedades do controle listadas acima:
//+------------------------------------------------------------------+ //| Classe para a criação do campo de edição | //+------------------------------------------------------------------+ class CSpinEdit : public CElement { private: //--- Cor de fundo do controle color m_area_color; //--- Texto da descrição do campo de edição string m_label_text; //--- Margens do rótulo de texto int m_label_x_gap; int m_label_y_gap; //--- Cor do texto em diferentes estados color m_label_color; color m_label_color_hover; color m_label_color_locked; color m_label_color_array[]; //--- Tamanho do campo de edição int m_edit_x_size; int m_edit_y_size; //--- Margem do campo de edição do lado direito do elemento int m_edit_x_gap; //--- Cores do texto do campo de edição em diferentes estados color m_edit_color; color m_edit_color_locked; color m_edit_text_color; color m_edit_text_color_locked; color m_edit_text_color_highlight; //--- Cores do quadro do campo de edição em diferentes estados color m_edit_border_color; color m_edit_border_color_hover; color m_edit_border_color_locked; color m_edit_border_color_array[]; //--- Rótulos das chaves nos estados ativo e bloqueado string m_inc_bmp_file_on; string m_inc_bmp_file_off; string m_inc_bmp_file_locked; string m_dec_bmp_file_on; string m_dec_bmp_file_off; string m_dec_bmp_file_locked; //--- Margens do botão (do lado direito) int m_inc_x_gap; int m_inc_y_gap; int m_dec_x_gap; int m_dec_y_gap; //--- Modo de resetar o valor para a mínima bool m_reset_mode; //--- Valor mínimo e máximo double m_min_value; double m_max_value; //--- Passo para alterar o valor do campo de edição double m_step_value; //--- Modo de alinhamento de texto ENUM_ALIGN_MODE m_align_mode; //--- Número de casas decimais int m_digits; //--- public: //--- (1) Cor de fundo, (2) o texto da descrição do campo de edição, (3) margens do rótulo de texto void AreaColor(const color clr) { m_area_color=clr; } string LabelText(void) const { return(m_label.Description()); } void LabelXGap(const int x_gap) { m_label_x_gap=x_gap; } void LabelYGap(const int y_gap) { m_label_y_gap=y_gap; } //--- Cores do rótulo de texto em diferentes estados void LabelColor(const color clr) { m_label_color=clr; } void LabelColorHover(const color clr) { m_label_color_hover=clr; } void LabelColorLocked(const color clr) { m_label_color_locked=clr; } //--- (1) Tamanho do campo de edição, (2) Margem do campo de edição do lado direito void EditXSize(const int x_size) { m_edit_x_size=x_size; } void EditYSize(const int y_size) { m_edit_y_size=y_size; } void EditXGap(const int x_gap) { m_edit_x_gap=x_gap; } //--- Cores do campo de edição em estados diferentes void EditColor(const color clr) { m_edit_color=clr; } void EditColorLocked(const color clr) { m_edit_color_locked=clr; } //--- Cores do texto do campo de edição em diferentes estados void EditTextColor(const color clr) { m_edit_text_color=clr; } void EditTextColorLocked(const color clr) { m_edit_text_color_locked=clr; } void EditTextColorHighlight(const color clr) { m_edit_text_color_highlight=clr; } //--- Cores do quadro do campo de edição em diferentes estados void EditBorderColor(const color clr) { m_edit_border_color=clr; } void EditBorderColorHover(const color clr) { m_edit_border_color_hover=clr; } void EditBorderColorLocked(const color clr) { m_edit_border_color_locked=clr; } //--- Definir os rótulos para o botão nos estados ativo e bloqueado void IncFileOn(const string file_path) { m_inc_bmp_file_on=file_path; } void IncFileOff(const string file_path) { m_inc_bmp_file_off=file_path; } void IncFileLocked(const string file_path) { m_inc_bmp_file_locked=file_path; } void DecFileOn(const string file_path) { m_dec_bmp_file_on=file_path; } void DecFileOff(const string file_path) { m_dec_bmp_file_off=file_path; } void DecFileLocked(const string file_path) { m_dec_bmp_file_locked=file_path; } //--- Margem dos botões do campo de edição void IncXGap(const int x_gap) { m_inc_x_gap=x_gap; } void IncYGap(const int y_gap) { m_inc_y_gap=y_gap; } void DecXGap(const int x_gap) { m_dec_x_gap=x_gap; } void DecYGap(const int y_gap) { m_dec_y_gap=y_gap; } //--- Modo de reset quando acontece o clique sobre o rótulo de texto bool ResetMode(void) { return(m_reset_mode); } void ResetMode(const bool mode) { m_reset_mode=mode; } //--- Valor mínimo double MinValue(void) const { return(m_min_value); } void MinValue(const double value) { m_min_value=value; } //--- Valor máximo double MaxValue(void) const { return(m_max_value); } void MaxValue(const double value) { m_max_value=value; } //--- O passo do valor double StepValue(void) const { return(m_step_value); } void StepValue(const double value) { m_step_value=(value<=0)? 1 : value; } //--- (1) Número de casas decimais, (2) o modo de alinhamento do texto void SetDigits(const int digits) { m_digits=::fabs(digits); } void AlignMode(ENUM_ALIGN_MODE mode) { m_align_mode=mode; } };
Para criar o controle campo de edição, nós vamos precisar de cinco métodos privados para criar objetos gráficos primitivos e um método público, que serão chamados na classe personalizada quando a interface gráfica for criada. Os ícones para os botões de rolagem podem ser baixados no final deste artigo. Eles são usados por padrão, mas você poderá utilizar os seus ícones personalizados.
class CSpinEdit : public CElement { private: //--- Objetos para a criação do campo de edição CRectLabel m_area; CLabel m_label; CEdit m_edit; CBmpLabel m_spin_inc; CBmpLabel m_spin_dec; //--- public: //--- Métodos para criar o campo de edição bool CreateSpinEdit(const long chart_id,const int subwin,const string label_text,const int x,const int y); //--- private: bool CreateArea(void); bool CreateLabel(void); bool CreateEdit(void); bool CreateSpinInc(void); bool CreateSpinDec(void); };
Agora, nós estamos vamos discutir os modos e métodos de gestão dos estados e propriedades do controle após a sua criação. Vamos começar com o método CSpinEdit::SetValue() para ajustar e o armazenar o valor do controle campo de edição e também o método auxiliar CSpinEdit::HighlightLimit() para o destaque (realce) do texto no campo de edição quando os limites mínimos e máximos são ultrapassados.
No início do método CSpinEdit::SetValue(), há um ajuste considerando o passo para mudar o valor. Então, siga as verificações para ultrapassar os limites definidos (máximas/mínimas). Se os limites forem ultrapassados, então, um valor correspondente à verificação é definida e o método CSpinEdit::HighlightLimit() é chamado para destacar o texto. Se depois de todas as verificações e ajustes o valor se alterar em relação ao que foi armazenado, o novo valor substitui o antigo.
O método CSpinEdit::HighlightLimit() é bastante simples. Ali, a cor do realce é definido em primeiro lugar. É vermelho por padrão, podendo ser redefinido pelo usuário. Depois disso, há uma pausa de 100 milissegundos. Isto é suficiente para notar uma mudança temporária da cor. A cor do texto inicial é definida no final.
Abaixo está o código dos métodos descritos:
class CSpinEdit : public CElement { private: //--- Valor atual do campo de edição double m_edit_value; //--- public: //--- Retorna e define o valor do campo de edição double GetValue(void) const { return(m_edit_value); } bool SetValue(const double value); //--- Piscar quando o limite for atingido void HighlightLimit(void); }; //+------------------------------------------------------------------+ //| Verifica o valor atual | //+------------------------------------------------------------------+ bool CSpinEdit::SetValue(double value) { //--- Para o ajuste double corrected_value =0.0; //--- Ajustar considerando o passo corrected_value=::MathRound(value/m_step_value)*m_step_value; //--- Verifica a mínima/máxima if(corrected_value<m_min_value) { //--- Define o valor mínimo corrected_value=m_min_value; //--- Define o estado para On (ligado) m_spin_dec.State(true); //--- Pisca para indicar que o limite foi atingido HighlightLimit(); } if(corrected_value>m_max_value) { //--- Define o valor máximo corrected_value=m_max_value; //--- Define o estado para On (ligado) m_spin_inc.State(true); //--- Pisca para indicar que o limite foi atingido HighlightLimit(); } //--- Se o valor tiver sido alterado if(m_edit_value!=corrected_value) { m_edit_value=corrected_value; m_edit.Color(m_edit_text_color); return(true); } //--- Valor inalterado return(false); } //+------------------------------------------------------------------+ //| Destacando o limite | //+------------------------------------------------------------------+ void CSpinEdit::HighlightLimit(void) { //--- Muda a cor do texto temporariamente m_edit.Color(m_edit_text_color_highlight); //--- Atualiza ::ChartRedraw(); //--- Atrasa antes de voltar para a cor inicial ::Sleep(100); //--- Muda a cor do texto para o inicial m_edit.Color(m_edit_text_color); }
O método CSpinEdit::SetValue() é designado apenas para ajustar o valor no caso do limite estabelecido for excedido. O método CSpinEdit::ChangeValue() é usado para alterar o valor do campo de edição:
class CSpinEdit : public CElement { private: //--- A alteração do valor no campo de edição void ChangeValue(const double value); }; //+------------------------------------------------------------------+ //| A alteração do valor no campo de edição | //+------------------------------------------------------------------+ void CSpinEdit::ChangeValue(const double value) { //--- Verifica, ajusta e armazena o novo valor SetValue(value); //--- Define o novo valor no campo de edição m_edit.Description(::DoubleToString(GetValue(),m_digits)); }
Semelhante aos outros elementos, vamos precisar de um método para gerenciar a disponibilidade do controle de edição, como mostrado no código abaixo:
class CSpinEdit : public CElement { private: //--- Estado da caixa de seleção(disponível/bloqueado) bool m_spin_edit_state; //--- public: //--- Retorna/define o estado da disponibilidade do campo de edição bool SpinEditState(void) const { return(m_spin_edit_state); } void SpinEditState(const bool state); }; //+------------------------------------------------------------------+ //| Define o estado do controle | //+------------------------------------------------------------------+ void CSpinEdit::SpinEditState(const bool state) { m_spin_edit_state=state; //--- Cor do rótulo de texto m_label.Color((state)? m_label_color : m_label_color_locked); //--- Cor do campo de edição m_edit.Color((state)? m_edit_text_color : m_edit_text_color_locked); m_edit.BackColor((state)? m_edit_color : m_edit_color_locked); m_edit.BorderColor((state)? m_edit_border_color : m_edit_border_color_locked); //--- Ícones das chaves m_spin_inc.BmpFileOn((state)? "::"+m_inc_bmp_file_on : "::"+m_inc_bmp_file_locked); m_spin_dec.BmpFileOn((state)? "::"+m_dec_bmp_file_on : "::"+m_dec_bmp_file_locked); //--- Define em relação ao estado atual if(!m_spin_edit_state) { //--- Prioridades m_edit.Z_Order(-1); m_spin_inc.Z_Order(-1); m_spin_dec.Z_Order(-1); //--- Edita no modo somente leitura m_edit.ReadOnly(true); } else { //--- Prioridades m_edit.Z_Order(m_edit_zorder); m_spin_inc.Z_Order(m_spin_zorder); m_spin_dec.Z_Order(m_spin_zorder); //--- O controle campo de edição no modo de edição m_edit.ReadOnly(false); } }
Agora, nós vamos considerar os métodos para lidar com os eventos do controle campo de edição. O clique em cima do rótulo de texto irá gerar um evento personalizado com (1) o identificador de evento ON_CLICK_LABEL, (2) o identificador de controle, (3) o índice de controle e (4) a descrição do rótulo de texto. O modo de resetar o valor no campo de edição para o seu mínimo foi mencionado anteriormente. Este modo é desativado por padrão (false). Ele pode ser ativado com o método CSpinEdit::ResetMode(). Para isso, você só precisa definir como true em seu único argumento. Se o modo para resetar o valor estiver ativado, então, ao pressionar o rótulo de texto, será chamado o método para definir o valor mínimo no campo de edição.
O código do método CSpinEdit::OnClickLabel() para lidar com o clique sobre o rótulo de texto será como é mostrado abaixo:
class CSpinEdit : public CElement { private: //--- Manipulando o pressionamento do rótulo de texto bool OnClickLabel(const string clicked_object); }; //+------------------------------------------------------------------+ //| Clique no rótulo do elemento | //+------------------------------------------------------------------+ bool CSpinEdit::OnClickLabel(const string clicked_object) { //--- Sai se o nome for diferente if(m_area.Name()!=clicked_object) return(false); //--- Se o modo de resetar o valor está habilitado if(m_reset_mode) { //--- Define o valor mínimo ChangeValue(MinValue()); } //--- Envia uma mensagem sobre ele ::EventChartCustom(m_chart_id,ON_CLICK_LABEL,CElement::Id(),CElement::Index(),m_label.Description()); return(true); }
O usuário pode alterar o valor no controle campo de edição, inserindo o novo valor manualmente ou usando os botões de incremento e decremento. Nós vamos precisar de novos identificadores de eventos personalizados. Adicione-os no arquivo Defines.mqh:
//+------------------------------------------------------------------+ //| Defines.mqh | //| Copyright 2015, MetaQuotes Software Corp. | //| http://www.mql5.com | //+------------------------------------------------------------------+ #define ON_END_EDIT (18) // Edição final do valor no campo de edição #define ON_CLICK_INC (19) // Altera o contador para cima #define ON_CLICK_DEC (20) // Altera o contador para baixo
Para lidar com a entrada manual do novo valor, nós vamos escrever o método CSpinEdit::OnEndEdit(). Ele será chamado no manipulador comum CSpinEdit::OnEvent() ao receber um evento com o identificador CHARTEVENT_OBJECT_ENDEDIT. O novo valor pode ser ajustado se necessário. No final do método, um evento personalizado será gerado com (1) o identificador de evento ON_END_EDIT, (2) o identificador do elemento, (3) o índice do elemento e (4) a descrição do rótulo de texto, como é mostrado no código abaixo.
class CSpinEdit : public CElement { private: //--- Manipulação do valor inserido no campo de edição bool OnEndEdit(const string edited_object); }; //+------------------------------------------------------------------+ //| Manipulação do valor inserido no campo de edição | //+------------------------------------------------------------------+ bool CSpinEdit::OnEndEdit(const string edited_object) { //--- Sai se o nome for diferente if(m_edit.Name()!=edited_object) return(false); //--- Obter o valor inserido double entered_value=::StringToDouble(m_edit.Description()); //--- Verifica, ajusta e armazena o novo valor ChangeValue(entered_value); //--- Envia uma mensagem sobre ele ::EventChartCustom(m_chart_id,ON_END_EDIT,CElement::Id(),CElement::Index(),m_label.Description()); return(true); }
Para lidar com o pressionamento sobre os botões de incremento e decremento, nós precisamos criar dois métodos distintos, o CSpinEdit::OnClickSpinInc() e o CSpinEdit::OnClickSpinDec(). Eles são bem simples. Depois de um objeto gráfico ser pressionado, há uma verificação pelo seu nome, se for um botão de nosso controle. Em seguida, nós obtemos o valor atual no campo de edição e aumentamos/diminuímos o seu passo definido nas propriedades do controle. No final dos métodos, um evento personalizado é gerado com (1) o identificador de evento ON_CLICK_INC/ON_CLICK_DEC, (2) o identificador do elemento, (3) o índice do elemento e (4) a descrição do rótulo de texto, como é mostrado no código abaixo.
class CSpinEdit : public CElement { private: //--- Manipulação do clique do botão do campo de edição bool OnClickSpinInc(const string clicked_object); bool OnClickSpinDec(const string clicked_object); }; //+------------------------------------------------------------------+ //| Pressionando na chave de incremento | //+------------------------------------------------------------------+ bool CSpinEdit::OnClickSpinInc(const string clicked_object) { //--- Sai se o nome for diferente if(m_spin_inc.Name()!=clicked_object) return(false); //--- Obtém o valor atual double value=GetValue(); //--- Aumenta em um passo e verifica se excedeu o limite ChangeValue(value+m_step_value); //--- Define o estado para On (ligado) m_spin_inc.State(true); //--- Envia uma mensagem sobre ele ::EventChartCustom(m_chart_id,ON_CLICK_INC,CElement::Id(),CElement::Index(),m_label.Description()); return(true); } //+------------------------------------------------------------------+ //| Pressionando na chave de decremento | //+------------------------------------------------------------------+ bool CSpinEdit::OnClickSpinDec(const string clicked_object) { //--- Sai se o nome for diferente if(m_spin_dec.Name()!=clicked_object) return(false); //--- Obtém o valor atual double value=GetValue(); //--- Diminui em um passo e verifica se excedeu o limite ChangeValue(value-m_step_value); //--- Define o estado para On (ligado) m_spin_dec.State(true); //--- Envia uma mensagem sobre ele ::EventChartCustom(m_chart_id,ON_CLICK_DEC,CElement::Id(),CElement::Index(),m_label.Description()); return(true); }
Todos estes métodos de manipulação devem ser chamados no método principal para o tratamento de eventos, como é mostrado no código abaixo. O acesso ao corpo de métodos de manipulação é controlado pela disponibilidade atual do controle.
//+------------------------------------------------------------------+ //| Manipulador de evento | //+------------------------------------------------------------------+ void CSpinEdit::OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam) { //--- Manipula o clique do botão esquerdo do mouse sobre o objeto if(id==CHARTEVENT_OBJECT_CLICK) { //--- Sai, se o controle está bloqueado if(!m_spin_edit_state) return; //--- Manipulando o pressionamento do rótulo de texto if(OnClickLabel(sparam)) return; //--- Manipulação do clique do botão do campo de edição if(OnClickSpinInc(sparam)) return; if(OnClickSpinDec(sparam)) return; //--- return; } //--- Manipulando a alteração do valor no evento do campo de edição if(id==CHARTEVENT_OBJECT_ENDEDIT) { //--- Sai, se o controle está bloqueado if(!m_spin_edit_state) return; //--- Manipula o valor de entrada if(OnEndEdit(sparam)) return; } }
Para uma rolagem mais rápida dos valores quando o botão esquerdo do mouse é pressionado sobre os botões chaves do controle, crie o método CSpinEdit::FastSwitching() que será chamado no timer do controle. Nós discutimos esse método quando falamos sobre a classe CListView para a criar a lista com uma barra de rolagem. Considerando que esse método foi necessário para a rolagem da lista, aqui ele vai ter uma função de aumentar ou diminuir os valores do campo de edição. Mais detalhes do código CSpinEdit::FastSwitching() é apresentado abaixo.
class CSpinEdit : public CElement { private: //--- Rolagem rápida dos valores do campo de edição void FastSwitching(void); }; //+------------------------------------------------------------------+ //| Timer | //+------------------------------------------------------------------+ void CSpinEdit::OnEventTimer(void) { //--- Se o elemento é suspenso if(CElement::IsDropdown()) { ChangeObjectsColor(); FastSwitching(); } else { //--- Acompanha a mudança da cor e a rolagem mais rápida dos valores // se o formulário não está bloqueado if(!m_wnd.IsLocked()) { ChangeObjectsColor(); FastSwitching(); } } } //+------------------------------------------------------------------+ //| Rolagem rápida dos valores do campo de edição | //+------------------------------------------------------------------+ void CSpinEdit::FastSwitching(void) { //--- Sai, se o foco não está no controle 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; //--- Obtém o valor atual no campo de edição double current_value=::StringToDouble(m_edit.Description()); //--- Se aumentar if(m_spin_inc.State()) SetValue(current_value+m_step_value); //--- Se diminuir else if(m_spin_dec.State()) SetValue(current_value-m_step_value); //--- Altera o valor se o botão chaveado ainda estiver pressionado para baixo if(m_spin_inc.State() || m_spin_dec.State()) m_edit.Description(::DoubleToString(GetValue(),m_digits)); } }
Teste do Controle Campo de Edição
Todos os métodos do controle campo de edição foram implementadas. Vamos testá-lo no programa que nós preparamos antes. Na classe personalizada CProgram do aplicativo, crie uma instância da classe CSpinEdit e declare um método para criar o controle campo de edição.
//+------------------------------------------------------------------+ //| Classe para a criação de um aplicativo | //+------------------------------------------------------------------+ class CProgram : public CWndEvents { private: //--- Campos de Edição CSpinEdit m_spin_edit1; //--- private: //--- Campos de Edição #define SPINEDIT1_GAP_X (150) #define SPINEDIT1_GAP_Y (75) bool CreateSpinEdit1(const string text); };
Vamos fazer a primeira caixa de seleção para gerenciar a disponibilidade desse controle, da mesma maneira que foi feito em relação à segunda caixa de seleção. Portanto, após este controle ser criado, sua disponibilidade dependerá do estado da primeira caixa de seleção como é mostrado no código abaixo.
//+------------------------------------------------------------------+ //| Cria o campo de edição 1 | //+------------------------------------------------------------------+ bool CProgram::CreateSpinEdit1(string text) { //--- Armazena o ponteiro da janela m_spin_edit1.WindowPointer(m_window1); //--- Coordenadas int x=m_window1.X()+SPINEDIT1_GAP_X; int y=m_window1.Y()+SPINEDIT1_GAP_Y; //--- Valor double v=(m_spin_edit1.GetValue()==WRONG_VALUE) ? 4 : m_spin_edit1.GetValue(); //--- Define as propriedades antes da criação m_spin_edit1.XSize(150); m_spin_edit1.YSize(18); m_spin_edit1.EditXSize(76); m_spin_edit1.MaxValue(1000); m_spin_edit1.MinValue(-1000); m_spin_edit1.StepValue(1); m_spin_edit1.SetDigits(0); m_spin_edit1.SetValue(v); m_spin_edit1.ResetMode(true); m_spin_edit1.AreaColor(clrWhiteSmoke); m_spin_edit1.LabelColor(clrBlack); m_spin_edit1.LabelColorLocked(clrSilver); m_spin_edit1.EditColorLocked(clrWhiteSmoke); m_spin_edit1.EditTextColor(clrBlack); m_spin_edit1.EditTextColorLocked(clrSilver); m_spin_edit1.EditBorderColor(clrSilver); m_spin_edit1.EditBorderColorLocked(clrSilver); //--- Cria o controle if(!m_spin_edit1.CreateSpinEdit(m_chart_id,m_subwin,text,x,y)) return(false); //--- A disponibilidade irá depender do estado atual da primeira caixa de seleção m_spin_edit1.SpinEditState(m_checkbox1.CheckButtonState()); //--- Adiciona o objeto para o array comum dos grupos de objetos CWndContainer::AddToElementsArray(0,m_spin_edit1); return(true); }
Semelhante aos outros controles, o método CProgram::CreateSpinEdit1() deve ser chamado no método principal para criar a interface gráfica do programa. Abaixo está uma versão resumida do método:
//+------------------------------------------------------------------+ //| 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 //--- Caixas de seleção //--- Campos de Edição if(!CreateSpinEdit1("Spin Edit 1:")) return(false); //--- Redesenha o gráfico m_chart.Redraw(); return(true); }
No manipulador de eventos CProgram::OnEvent(), adicione o código para o testar a escuta dos eventos a partir dos controles campo de edição e também especifique que o primeiro campo de edição depende do estado da primeira caixa de seleção:
//+------------------------------------------------------------------+ //| Manipulador de eventos | //+------------------------------------------------------------------+ void CProgram::OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam) { //--- O evento clique no rótulo de texto if(id==CHARTEVENT_CUSTOM+ON_CLICK_LABEL) { ::Print(__FUNCTION__," > id: ",id,"; lparam: ",lparam,"; dparam: ",dparam,"; sparam: ",sparam); //--- Se o clique foi na primeira caixa de seleção if(lparam==m_checkbox1.Id()) { //--- Define o estado para a segunda caixa de seleção e o primeiro campo de edição m_checkbox2.CheckBoxState(m_checkbox1.CheckButtonState()); m_spin_edit1.SpinEditState(m_checkbox1.CheckButtonState()); } } //--- O evento fim do valor de entrada no campo de edição if(id==CHARTEVENT_CUSTOM+ON_END_EDIT) { ::Print(__FUNCTION__," > id: ",id,"; lparam: ",lparam,"; dparam: ",dparam,"; sparam: ",sparam); } //--- O evento clique dos botões chaveados do campo de edição if(id==CHARTEVENT_CUSTOM+ON_CLICK_INC || id==CHARTEVENT_CUSTOM+ON_CLICK_DEC) { ::Print(__FUNCTION__," > id: ",id,"; lparam: ",lparam,"; dparam: ",dparam,"; sparam: ",sparam); } }
Compile o programa e carregue-o ao gráfico. Você pode ver que a interface gráfica da aplicação está igual a imagem abaixo:
Fig. 4. Testando o controle campo de edição.
Outros Controles com as Caixas de Seleção
Foi mencionado no início deste artigo, que além dos controles da caixa de seleção e campo de edição, nós poderíamos considerar o campo de edição com caixa de seleção e a lista combinada. O campo de edição com o caixa de seleção é uma versão estendida da classe CSpinEdit, enriquecida com campos e métodos da classe CCheckBox que consideramos neste artigo.
Fig. 5. Partes integrantes do controle campo de edição com a caixa de seleção.
A caixa de seleção com a lista combinada é uma combinação semelhante às classes CComboBox e CCheckBox:
Fig. 6. Partes integrantes do controle caixa de seleção com a lista combinada.
Você pode encontrar a implementação do CCheckBoxEdit (campo de edição com caixa de seleção) e o CCheckComboBox (caixa de seleção com a lista combinada) nos arquivos anexados neste artigo para estudar por conta própria. Como o controle do tipo CCheckComboBox contém uma lista suspensa, as adições relevantes precisam ser introduzidas no arquivo WndContainer.mqh semelhante aos outros elementos que possuem partes suspensas. Neste caso, o ponteiro da lista suspensa deve entrar no array privado de ponteiros m_drop_lists[]. Uma descrição detalhada de como fazer isso pode ser encontrada no artigo Interfaces Gráficas V: O Controle ComboBox (Capítulo 3).
Por uma questão de exemplo, nós vamos adicionar o aplicativo de teste com esses controles para que você possa ver como ele funciona. Vamos adicionar duas caixas de seleção do tipo CCheckBox e um do CCheckBoxEdit e CCheckComboBox. A disponibilidade do controle do tipo CCheckBoxEdit vai depender do estado da terceira caixa de seleção e a disponibilidade do controle do tipo CCheckComboBox vai depender do estado da quarta lista combinada.
Fig. 7. Testando os controles de tipos mistos.
Conclusão
Neste artigo, nós desenvolvemos os controles amplamente utilizados em muitas interfaces gráficas em diferentes ambientes: caixa de seleção, campo de edição, campo de edição com caixa de seleção e a lista combinada com a caixa de seleção. No segundo capítulo, nós vamos desenvolver os controles deslizante e deslizante duplo.
- Interfaces Gráficas VI: Os Controles Caixa de Seleção, Campo de Edição e seus Tipos Combinados (Capítulo 1)
- Interfaces Gráficas VI: Os Controles Deslizante e Deslizante Duplo (Capítulo 2)
Traduzido do russo pela MetaQuotes Ltd.
Artigo original: https://www.mql5.com/ru/articles/2466
- 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