Ir para conteúdo
Fórum Script Brasil
  • 0

Consulta E Cadastro De Dados - [1] Tela "combo"


Micheus

Pergunta

Pessoal, este simples tutorial tem como objetivo apresentar uma das inúmeras formas de manipular dados de uma tabela. Utilizei Paradox com TTable, para simplificar a implementação. O projeto é composto por um Form (CadTesteDBEdit) e um Datamodule (DMCadastro). Entretanto, os conceitos mostrados nele podem ser aproveitados para o uso de quaquer banco e/ou componentes de acesso. (download)

Dos recursos explorados

- Monitoração da mensagem WM_MOUSEWHEEL (rodinha do mouse) para movimentação no DBGrid;

- Utilização do evento OnStateChange do TDataSource para implementação de botões sensiveis ao estado do dataset;

- Concentração da manipulação da tabela em um único evento a ser utilizado pelos botões que executarão a ação;

- Implementação do evento OnCalcFields, demonstrando sua aplicação;

O lay-out proposto para o form, na ordem

- 01 TEdit para aplicação da função localizar (edLocalizar);

- 01 TPanel para conter os TDBEdits e viabilizar o bloqueio da edição dos campos (Panel1);

- 01 TDBGrid para listar os dados da tabela (DBGrid1);

- 01 TDataSource para viabilizar a nonitoração do status da tabela - no form;

- 03 TButton para inplementação dos botões de estado, onde serão disponibilizadas as funções Novo, Alterar, Excluir, Gravar e Cancelar (btnNovo, btnAlterar, btnExcluir);

- 01 TButton para fechar o form (btnFechar);

A relevância na ordem dos componentes (Tab Order)

1º - edLocalizar

2º - DBGrid1

3º - Panel1

4º - btnNovo

5º - btnAlterar

6º - btnExcluir

7º - btnFechar

nesta ordem, no modo consulta, ao teclar TAB no edLocalizar você estará posicionado no DBGrid1, podendo facilmento movimentar-se no grid ou simplesmente acionando um dos botões; Já no modo edição, ao sair do Panel1, o foco passará automaticamente para o botão btnGravar, facilitando neste processo.

Propriedades diversas alteradas em design-time

1) DBGrid1

- Options: dgRowSelect = True; dgAlwaysShowSelection = True; Selecionando toda a linha e automaticamente bloqueando a edição no DBGrid1

- ReadOnly = False; Evitando a exclusão no DBGrid1: CTRL+D

2) Panel1

- Enabled = False; Evitando o acesso inicial aos componentes TDBEdit

3) DBEdit1 (código auto-increment), edComissao (valor fixo) DBEdit4 (campo calculado), - apenas visualização

- TabStop = False

- ReadOnly = True

No datamodule teremos

- 01 TTable para acessar os dados da tabela Teste.DB (TabTeste);

- 01 TDataSource que será utilizado pelo DBGrid1 para mostrar os dados;

Breve descrição do programa

- O programa manipula uma tabela simples, onde existem 3 campos: Codigo, Descrição e Valor. Não é muito realista, mas este não é o foco.

- Para exemplificação do uso de campos calculados, eu defini (internamente - fixo) uma variável que conterá um percentual de comissão (PcComissao) que será utilizado para calcular o valor da comissão para cada item na tabela. Os campos calculados são ótimos para situações em que precisamos mostrar uma informação que não vem diretamente da tabela. Podem ser dados fixos, cálculos ou vindos de outra consulta/tabela (neste caso, similar a um lookup).

- A função localizar implementada, tenta seguir o modelo que costumamos ver no Excel, onde vamos digitando o texto, o grid é posicionado e o texto digitado é complementado com o resto do texto encontrado, ficando este selecionado. Para realizar esta função, serão manipulados os eventos OnChange e OnKeyPress do campo edLocalizar.

- A concentração da manipulação da tabela, pelos botões, é feita através de definição única do evento OnClick do botão btnNovo, o qual é associado ao respectivo evento dos botões btnAlterar e btnExcluir. Para viabilizar este procedimento, também são atribuídos os valores 1, 2 e 3 a propriedade TAG dos referidos botões.

Vamos ao código

Form Principal:

- O procedimento AppEventsMessage foi declarado na sessão private do form CadTesteDBEdit e tem como objetivo simular o uso das teclas "para cima" e "para baixo" quando a rodinha do mouse for movimentada nestes sentidos.

- Este evento (como implementada sua chamada) deve ser implementado/atribuído à Application.OnMessage apenas no form principal da aplicação (não repetir em cada form).

procedure TCadTesteDBEdit.AppEventsMessage(var Msg: TMsg; var Handled: Boolean);
var
  Sentido: SmallInt;
begin
  if Msg.message = WM_MOUSEWHEEL then
  begin
    Msg.message := WM_KEYDOWN;
    Msg.lParam := 0;
    Sentido := HiWord(Msg.wParam);
    if Sentido > 0 then
      Msg.wParam := VK_UP
    else
      Msg.wParam := VK_DOWN;
  end;
end;
- Como mensionei antes, e sendo este nosso único form (o principal), é neste ponto que iremos inicializar o nosso manipulador de eventos. - O percentual de comissão é fixo, então, para mostrá-lo em nosso form, devemos atribuí-lo ao EdComissao utilizando a função de formatação FormatFloat. Poderíamos, também, ter criado um campo calculado para manter este valor e utilizá-lo para apresentação e os cálculos. - O datamodule é criado apenas no momento em que é utilizado, então este é o momento.
procedure TCadTesteDBEdit.FormCreate(Sender: TObject);
begin
 // declara um tratador de eventos para a aplicação
 // com o objetivo de tratar o evento MouseWheel
  Application.OnMessage := AppEventsMessage;
  DMCadastro := TDMCadastro.Create(Self);
  EdComissao.Text := FormatFloat('##0.0', DMCadastro.PcComissao);
end;
- Da mesma forma, liberamos o datamodule quando não mais o utilizamos
procedure TCadTesteDBEdit.FormDestroy(Sender: TObject);
begin
  DMCadastro.Free;
  DMCadastro := Nil;
// ou simplesmente: FreeAndNil(DMCadastro);  // não existe no D3
end;
- O evento OnStateChange do TDataSource (DSTesteLocal) ocorre sempre que o dataset ligado a ele muda de estado. É observando esta mudança que iremos manipular a habilitação e caption dos botões utilizados pelo usuário para manipular a tabela. Visando tormar este procedimento o mais genérico possível (uso de copiar/colar), resolvir fazer o type-cast de Sender (sempre que o evento é chamado pelo componente, Sender será um ponteiro para ele próprio). Como é possível notar, btnNovo estará habilitado sempre que o dataset estiver em modo browse. A partir do momento ele passar para o estado de iserção (se clicado em btnNovo) ou edição (se clicado em btnAlterar), este botão estará desabilitado. Já os botões btnAlterar e btnExcluir, só estarão desabilitados quando a tabela estiver vazia. Na habilitação é verificado se a tabela não está no modo inserção, também, porque ser for utilizado Append ou um Insert no último registro, EOF retornará True e os botões ficariam incorretamente desabilitados. Aproveitamos este evento para também permitir a manipulação dos TDBEdit's através da habilitação do Panel1 onde eles se encontram. Observe que quando estamos no modo inserção/edição, os botões btnAlterar e btnExcluir, passam a ter a função Gravar e Cancelar.
procedure TCadTesteDBEdit.DSTesteLocalStateChange(Sender: TObject);
begin
  with Sender as TDataSource do
  begin
    BtnNovo.Enabled := DataSet.State = dsBrowse;
    BtnAlterar.Enabled := not DataSet.EOF or // true sempre que houver dados na tabela e falso se for dada um append
                         (DataSet.State = dsInsert);
    BtnExcluir.Enabled := not DataSet.EOF or
                         (DataSet.State = dsInsert);
    Panel1.Enabled := DataSet.State in [dsInsert, dsEdit];
    if DataSet.State in [dsInsert, dsEdit] then
    begin
      BtnAlterar.Caption := 'Gravar';
      BtnExcluir.Caption := 'Cancelar';
    end else
    begin
      BtnAlterar.Caption := 'Alterar';
      BtnExcluir.Caption := 'Excluir';
    end;
  end;
end;
- Como mensionado anteriormente, utilizamos um único evento OnClick para os botões btnNovo, btnAlterar e btnExcluir. Também foi considerada a utilização da propriedade TAG para identificar-mos a correta função dos botões, mas poderíamos, também, apenas comparar Sender com os botões (btnNovo, ...) através de if..then..else. Assim, utilizando também o princípio da generalização, trabalharemos como o DataSet ligado ao DBGrid1, não importando componente utilizado e nome do mesmo.
procedure TCadTesteDBEdit.BtnNovoClick(Sender: TObject);
begin
  with DBGrid1.DataSource do
  case (Sender as TButton).Tag of
    1 : // Incluir
      begin
        DataSet.Append;
        DBEdit2.SetFocus;
      end;
    2 : // Alterar/Gravar
      begin
        if DataSet.State in [dsInsert, dsEdit] then  // Opção Gravar
        begin
          DataSet.Post;
          DBGrid1.SetFocus;
        end else  // Opção Alterar
        begin
          DataSet.Edit;
          DBEdit2.SetFocus;
        end;
      end;
    3 : // Excluir/Cancelar
      begin
        if DataSet.State in [dsInsert, dsEdit] then  // Opção Cancelar
        begin
          if MessageDlg('Deseja cancelar a inclusão/edição do registro ?',
                        mtConfirmation, [mbYes, mbNo], 0) = mrYes then
          begin
            DataSet.Cancel;
            DBGrid1.SetFocus;
          end else
            DBEdit2.SetFocus;
        end else  // Opção Excluir
          if MessageDlg('Confirma a exclusão do registro ?',
                        mtWarning, [mbYes, mbNo], 0) = mrYes then
            DataSet.Delete;
      end;
  end;
end;
- Para simular, no edLocaliza, o efeito da busca em que o texto digitado é completado com o conteúdo da linha selecionada no DBGrid1 (quando compatível), utilizamos as propriedades SelStart e SelLength. Assim, se localizando um texto compatível com o digitado (text +%), inicialmente salvamos a posição do cursor (ele mudará ao atribuirmos novo valor a Text), evitamos uma indesejável recursividade desabilitando o evento OnChange ao atribuir-lhe nil. Em seguida atribuimos o valor localizado, definimos o início da seleção para a posição salva e seu tamanho para o comprimento do texto menos a posição salva. Podemos então habilitar novamente o evento OnChange
procedure TCadTesteDBEdit.EdLocalizaChange(Sender: TObject);
var
  CaretPos :Integer;
begin
  with DMCadastro do
  if TabTeste.Locate('Descricao', EdLocaliza.Text, [loPartialKey, loCaseInsensitive]) then
  begin
    CaretPos := EdLocaliza.SelStart;
    EdLocaliza.OnChange := nil;
    EdLocaliza.Text := TabTesteDescricao.AsString;
    EdLocaliza.SelStart := CaretPos;
    EdLocaliza.SelLength := Length(EdLocaliza.Text) -CaretPos;
    EdLocaliza.OnChange := EdLocalizaChange;
  end;
end;
Entretanto, quando temos um texto selecionado nestas condições, ao teclar BACKSPACE, apenas a seleção é apagada - não o caracter antes dela, como desejaríamos. Então, para resolver este problema, escrevemos no evento OnKeyPress do edLocaliza, o código abaixo. Também desabilitamos o evento OnChange porque, novamente, iremos alterar o conteúdo de Text - vamos corrigir o "bug" citado anteriormente. Após corrigido - copiamos o conteúdo desde o início de Text até o início da seleção menos uma posição atual, habilitamos novamente o evento OnChange e forçamos a tentativa de posicionamento na consulta, baseado no novo conteúdo de Text
procedure TCadTesteDBEdit.EdLocalizaKeyPress(Sender: TObject; var Key: Char);
var
  CaretPos :Integer;
begin
  if Key = #8 then
  begin
    EdLocaliza.OnChange := nil;
    CaretPos := EdLocaliza.SelStart -1;
    EdLocaliza.Text :=Copy(EdLocaliza.Text, 1, CaretPos);
    EdLocaliza.SelStart := CaretPos;
    EdLocaliza.OnChange := EdLocalizaChange;
    EdLocalizaChange(Sender);
    Key := #0;
  end
end;
DataModule: - Temos apenas um TTable e um DataSource "apontando" para o mesmo. - O campo calculado (VlComissao) é criado através do editor de campos (Fields Editor...), acessado através de um duplo-click no componente. Teclando CTRL+N ou acessando menu popup, New Field..., definimos o nome (VlComissao), o tipo de dados (Float) e o tipo do campo (Calculated). Após clicar em OK temos nosso campo na tabela, definimos então a máscara DisplayFormat e o Label. Utilizaremos o evento OnCalcFields justamente para atribuirmos a VlComissao (criado como Calculated) o valor do cálculo da comissao sobre o campo Valor
procedure TDMCadastro.TabTesteCalcFields(DataSet: TDataSet);
begin
  TabTesteVlComissao.Value := TabTesteValor.AsFloat *PcComissao /100;
end;

Link para o comentário
Compartilhar em outros sites

0 respostass a esta questão

Posts Recomendados

Até agora não há respostas para essa pergunta

Participe da discussão

Você pode postar agora e se registrar depois. Se você já tem uma conta, acesse agora para postar com sua conta.

Visitante
Responder esta pergunta...

×   Você colou conteúdo com formatação.   Remover formatação

  Apenas 75 emoticons são permitidos.

×   Seu link foi incorporado automaticamente.   Exibir como um link em vez disso

×   Seu conteúdo anterior foi restaurado.   Limpar Editor

×   Você não pode colar imagens diretamente. Carregar ou inserir imagens do URL.



  • Estatísticas dos Fóruns

    • Tópicos
      152,3k
    • Posts
      652,4k
×
×
  • Criar Novo...