
Delphi@Brasil Magazine - Outubro 97
Tópico do mês : Como fazer o ENTER trabalhar da mesma forma que os meus sistemas feitos com ferramentas baseadas na interface de caractere, ou seja mudar de campo todas as vezes em que ele for teclado ?
- Inicialmente, crie um novo projeto no Delphi. Vá até a aba Standard da palheta de componentes, escolha a caixa de edição e coloque três delas no formulário e também um botão. Clique em cima da aba Additional escolha um StringGrid e coloque-o no Form. Vá até a aba Data Controls e clique em cima do DBGrid e coloque-o dentro do Form., a tela terá o aspecto aproximado da figura I.
Figura I
- Clique uma vez em cima do formulário da aplicação e tecle F11 para que a janela do Object Inspector surja. A seguir verifique na aba de propriedades do mesmo a propriedade KeyPreview, caso esteja em False coloque-a em True ( o valor padrão desta propriedade é False ).
- Com isto estamos solicitando à API do Windows que ao processar os eventos relativos à manipulação de teclado, ( nunca é demais lembrar que este controle é feito quando nosso sistema possui o foco, ou seja, quando está ativo) primeiro verifique se o nosso aplicativo não deseja manipular ele próprio que ações devem ser executadas sobrepondo-se às ações do Windows.
- O simples ato de configurar a propriedade KeyPreview para True não resolve em nada o problema ( você não achou que não iria digitar código hein ? ), novamente clicamos em cima do formulário e teclamos F11 , ao surgir a tela do Object Inspector tecle na aba Events, localize o evento OnKeyPress e dê um duplo clique em cima do mesmo. Ao surgir o código relativo ao evento FormKeyPress digite o seguinte trecho de código ( apenas o que está em negrito...) :
procedure TForm1.FormKeyPress( Sender : TObject; Var Key : Char);
begin
if (Key = Chr(VK_RETURN)) AND (Self.ActiveControl IS TEdit) then
Perform( WM_NEXTDLGCTL, 0, 0);
end;
- Notar que key é um parâmetro ( lembra-se da diferença entre argumento e paramêtro ?) recebido pelo método KeyPress que é passado como argumento de referência ( seu valor pode ser alterado na função que o recebe, o mesmo efeito que símbolo @ tinha antes de uma variável no clipper ), portanto já podemos notar de cara um benefício que este método pode nos trazer no tratamento de teclas, nós podemos comparar o que foi digitado e se assim desejarmos alterar o mesmo, porém no caso do ENTER não podemos simplesmente substituí-lo pela tecla TAB pois desta maneira só retornariámos um caractere ASCII correspondente à tecla TAB e não a ação que a mesma desencadeia.
- A primeira coisa que fazemos é tomarmos conhecimento que realmente foi teclada a tecla ENTER com a comparação Key = Chr(VK_RETURN) ( VK_RETURN é uma constante definida pela API do Windows e o VK vêm de Virtual Key) fazendo uso da função Chr já que a constante da API só nós dá o valor ASCII da tecla. Caso seja verdadeiro passamos à segunda comparação que já não é tão óbvia.
- ActiveControl é uma variável pública proveniente de cada Form ( ou objeto que possua um HandleWindow para ser mais preciso ) que nos informa a cada instante o tipo de componente que possui o foco da aplicação atualmente. Você deve estar se perguntando qual a função da palavra Self antes do ActiveControl, na realidade ela não é obrigatória, porém se você não domina totalmente a orientação a objetos eu aconselho a usar esta notação, pois no futuro você começará a entender mais facilmente o sentido de cada conceito da OOP. Self vêm de o próprio, neste caso o próprio diz respeito ao objeto possuidor de todos os objetos na janela ou seja o Form. Isso significa que também poderíamos escrever Form1.ActiveControl que teria o mesmo efeito. Note que Self significa o mesmo que Form porque estamos lidando com um método ( OnKeyPress ) que pertence a Form, por este motivo a chamada do método se faz na forma TForm1.FormKeyPress, que pode ser entendido ( mas não escrito ) como Self.FormKeyPress já que quem possui o método é o Form.
- A palavra IS ( verbo ser, flexionado para É no inglês ) significa que estamos fazendo uma tipagem em tempo de execução para verificarmos se o controle ativo é do tipo TEdit( esta tipagem é chamada de RTTI, Run Time Type Information ou Informação de Tipo em Tempo de Execução ) . O IS nos fornece TRUE caso seja do tipo TEdit e FALSE caso contrário. Caso tenhamos satisfeito as duas condições finalmente executamos a parte interna do IF onde aparece uma função declarada com o nome de Perform. Esta função pertence à API do Windows, e ela nos fornece meios de enviar mensagens à pilha de eventos do Windows e a partir disso executar algumas ações. A ação que desejamos vêm na forma de uma constante definida também pela API de nome WM_NEXTDLGCTL ( que vêm de Windows Move to Next Dialog Control ), ou seja solicitamos ao Windows que passe ao próximo controle que esteja na ordem de tabulação do formulário. Os outro dois parâmetros são valores do tipo longint que para outras ações passam a ter sentido como fornecer ao windows uma nova posição X,Y para o cursor do windows, porém no nosso caso específico esses parâmetros não são usados.
- Notar a importância da propriedade TabOrder de cada componente, pois o Perform vai se basear nesta ordem para achar o próximo controle. Notar também que alguns controles ( notadamente os que possuem HandleWindow ) só necessitam de um TabOrder porém como eles próprios recebem outros componentes, estes componente internos têm uma nova sequência de TabOrder iniciando de 0 novamente. Por exemplo : Digamos que temos um Form com duas caixas TEdit, de TabOrders 0 e 1 respectivamente, e logo após um painel que possui mais duas caixas TEdit. Este painel vai ter a TabOrder 2, porém suas caixas TEdit possuirão as TabOrder 0 e 1 novamente já que agora elas são possuidas por um TPanel e não mais pelo Form.
- Agora vamos supor que estamos editando dados dentro de um componente TStringGrid e queremos que ele responda ao ENTER da mesma maneira que as planilhas eletrônicas respondem. Ao nosso evento OnKeyPress basta adicionarmos :
procedure TForm1.FormKeyPress( Sender : TObject; Var Key : Char);
begin
if (Key = chr(VK_RETURN)) AND (Self.ActiveControl IS TEdit) then
perform( WM_NEXTDLGCTL, 0, 0);
if (Key = Chr(VK_RETURN)) AND (Self.ActiveControl IS TStringGrid) then
begin
with TStringGrid(Self.ActiveControl) do
if Col < ColCount - 1 then
Col := Col + 1
else
begin
Row := Row + 1;
Col := 0;
end;
end;
end;
- Como já falamos sobre a verificação do tipo anteriormente vamos entrar direto na parte interna do if. Note como usamos novamente a RTTI. Já que temos aboluta certeza de que nosso ActiveControl é um tipo TStringGrid podemos fazer esta tipagem em tempo de execução, que estaremos apontando para o objeto da mesma forma que fariámos colocando o nome do mesmo como por exemplo em : with StringGrid1 do. Após isto verificamos a coluna atual do cursor que nos é dado pela propriedade Col em StringGrid.Col. Caso ele seja menor que o número total de colunas, fornecido por ColCount - 1 ( já que o número de colunas começa por 0 ), nós adicionamos uma coluna ao valor atual da propriedade coluna. Caso a coluna seja maior que o número de colunas então nós somamos uma linha ao valor da propriedade Row de StringGrid e colocamos o cursor na coluna 0 para iniciar a digitação da próxima linha.
- Agora suponhamos que ao invés de uma StringGrid estivermos usando um DBGrid, apesar de ser parente direto de StringGrid, nos podemos usar de outro método para atingir o mesmo objetivo. Usamos o contador de campos invés do contador de colunas para compararmos a posição atual do cursor como no exemplo :
procedure TForm1.FormKeyPress( Sender : TObject; Var Key : Char);
begin
if (Key = chr(VK_RETURN)) AND (Self.ActiveControl IS TEdit) then
perform( WM_NEXTDLGCTL, 0, 0);
if (Key = Chr(VK_RETURN)) AND (Self.ActiveControl IS TStringGrid) then
begin
with TStringGrid(Self.ActiveControl) do
if Col < ColCount - 1 then
Col := Col + 1
else
begin
Row := Row + 1;
Col := 0;
end;
end;
if (Key = Chr(VK_RETURN)) AND (Self.ActiveControl IS TDBGrid) then
begin
if TDBGrid(ActiveControl).SelectedIndex < TDBGrid(ActiveControl).FieldCount - 1 then
TDBGrid(ActiveControl).SelectedIndex := TDBGrid(ActiveControl).SelectedIndex+1
end;
end;
- Para finalizarmos mais uma dica super útil. Para nós Brasileiros quando digitamos números no teclado númerico, o ponto simplesmente perde a função já que nossa notação numerica têm como marcador de decimal a vírgula. Com a ajuda deste método simplesmente nós rastreamos se a tecla digitada foi o '.', caso tenha sido nos retornamos com a ajuda do argumento Key o caracter ',' desta forma :
procedure TForm1.FormKeyPress( Sender : TObject; Var Key : Char);
begin
if (Key = chr(VK_RETURN)) AND (Self.ActiveControl IS TEdit) then
perform( WM_NEXTDLGCTL, 0, 0);
if (Key = Chr(VK_RETURN)) AND (Self.ActiveControl IS TStringGrid) then
begin
with TStringGrid(Self.ActiveControl) do
if Col < ColCount - 1 then
Col := Col + 1
else
begin
Row := Row + 1;
Col := 0;
end;
end;
if (Key = Chr(VK_RETURN)) AND (Self.ActiveControl IS TDBGrid) then
begin
if TDBGrid(ActiveControl).SelectedIndex < TDBGrid(ActiveControl).FieldCount - 1 then
TDBGrid(ActiveControl).SelectedIndex := TDBGrid(ActiveControl).SelectedIndex+1
end;
if (Key = '.') then
if (Self.ActiveControl IS TDBGrid) OR (Self.ActiveControl IS TStringGrid) then
Key := ',';
end;
- Notar que no exemplo só estamos substuindo o ponto por vírgula nos DBGrid's e nos StringGrid's já que dentro de caixa de textos fica perigoso nós fazermos esta substituição pois caso tenhamos uma campo 'Nome' por exemplo e o usuário digitar o seu nome seguido por uma inicial com um ponto este ponto será substituido por uma vírgula, sendo este comportamento indesejável.
- No caso de estarmos programando uma aplicação de banco de dados, a única alteração seria em vez de utilizarmos os tipo TEdit para os campos usaríamos TDBEdit, porém o funcionamento básico continuaria o mesmo.
- Em alguns computadores o uso desta técnica provoca um ruído ao presionarmos a tecla ENTER, caso deseje retirar este ruído basta apenas antes de chamarmos a API do Windows através do Perform acrecentarmos a linha Key := #0;. Com isto indicamos que nenhuma tecla foi pressionada, apenas deve deslocar-se para o próximo controle no formulário.
- Bom amigos, por enquanto isto é tudo, esperamos que as informações aqui contidas tenha lhe sido de grande utilidade. Venho reforçar o pedido para que se desejarem entrem em contato conosco por meio de [email protected].
- No mês que vêm lhes darei a dica de como traduzir todas as caixas de dialógo padrão do Delphi substituindo o Yes, No, Cancel, Error etc... por palavras em português legível. Até a próxima....
Fúlvio C. Albuquerque
Editor Responsável