Clique para saber mais...
  Home     Download     Produtos / Cursos     Revista     Vídeo Aulas     Fórum     Contato   Clique aqui para logar | 20 de Julho de 2026
  Login

Codinome
Senha
Salvar informações

 Esqueci minha senha
 Novo Cadastro

  Usuários
34 Usuários Online

  Revista ActiveDelphi
 Assine Já!
 Edições
 Sobre a Revista

  Conteúdo
 Apostilas
 Artigos
 Componentes
 Dicas
 News
 Programas / Exemplos
 Vídeo Aulas

  Serviços
 Active News
 Fórum
 Produtos / Cursos

  Outros
 Colunistas
 Contato
 Top 10

  Publicidade

  [Artigos]  Controlando os estados de uma tabela
Publicado por ActiveDelphi : Quinta, Dezembro 23, 2004 - 12:22 GMT-3 (6009 leituras)
Comentários comentar   Enviar esta notícia a um amigo Enviar para um amigo   Versão para Impressão Versão para impressão
Administrador O que voce vai encontrar neste artigo:
- Uma função genérica para controlar a edição de registros;
- Criar um TTABLE em tempo de execução;
- Validar duplicação de chave;
- Utilizar os eventos OnDataChange e OnStateChange do componenteTDatasource;
- Controlar os estados de uma tabela;
- Pesquisa de campo chave;
- Permitir ou não o fechamento do form;

Verificar o estado de uma tabela pode ser muito interessante para o programador em geral, alguns estados podem ser de grande valia para determinar se alguma atitude deve ser tomada pelo programa.
 
Abaixo esta uma tabela com todos os estados possíveis para o componente TTable.
 
Estado | Descrição
------------------------------------------------------------------
dsInactive | Tabela não esta aberta
dsBrowse | Esta em navegação
dsEdit | Registro esta em edição
dsInsert | Inserindo um novo registro
dsSetKey | Estabelecendo valores para os campos chaves do arquivo, em uma operação de faixas ou pesquisa
dsCalcFields | Obtendo valores para os campos calculados
dsUpdateNew | Reservado para uso interno
dsUpdateOld | Reservado para uso interno
dsFilter | Processando o evento OnFilterRecord
------------------------------------------------------------------
 
Estes estados podem ser críticos em situações como edição de registros já existentes ou inclusão de novos registros.
 

· dsInactive indica que a tabela não esta aberta, mas também pode indicar que um processo de abertura não foi bem sucedido.
· dsEdit indica que um registro que já existia na tabela esta agora sofrendo uma alteração, em conjunto com a propriedade Modified podemos montar uma rotina de tratamento muito completa para um possível cancelamento da edição.
· dsInsert indica que estamos trabalhando com um novo registro, não importa se este registro tenha sido criado com Append ou com Insert.
· dsSetKey indica que estamos informando uma chave para pesquisa.

Estes provavelmente devem ser os mais utilizados...
 
Abaixo um exemplo de como os estados da tabela devem ser controlados.

Nesta última revisão foram incluídos os procedimentos necessários para executar uma pesquisa dentro do índice atualmente em uso na tabela que é por código.
 
O código deste projeto está bem mais coerente com as reais necessidades de um form de edição. Os aspectos novos que vamos ver são muito portáveis, podendo ser utilizados em qualquer projeto no dia a dia.
 
O primeiro procedimento do form apresenta uma característica das units em Object Pascal que ainda não exploramos, declarar um procedimento local não diretamente ligado ao Form em que está sendo inserido.
 
Na declaração a função não tem o seu nome precedido pelo nome do form e tão pouco está incluída na declaração de procedimentos do form.
 
Este tipo de procedimento/função será visível dentro da unit que está sendo colocada e deve ser posicionada antes de qualquer referencia a ela, para que o compilador a reconheça sem problemas, é o primeiro passo para a criação de uma biblioteca.
 
Outro item a ser observado é como se passa uma Table como parâmetro para uma função, e como esta função pode manipular de forma transparente esta Table sem que se tenha a menor idéia de onde ela tenha vindo.
 
Foi necessário tratar a situação de verificação do estado de uma Table, e conseqüentemente a atitude a ser tomada, dentro de uma função para evitar a duplicação de código idêntico em pontos distintos do projeto, quando o usuário tenta fechar o form estando em edição ou inserção, quando o usuário seleciona o botão "Cancela" ou ainda quando a tabela esta esperando a entrada da chave para pesquisa.
 

{--------------------------------------------------------------}
{Uma função local }
{--------------------------------------------------------------}
 
{----------}
procedure CheckState(ATable: TTable);
{----------}
var
  strMessage : String[25];
  wrdMessageResult : word;
begin
  ATable.UpdateRecord; { Atualiza a tabela sem gravar }
  if ( ATable.Modified ) and
  ( ATable.State <> dsSetKey ) then
  begin
    if ATable.State = dsEdit then
      strMessage := 'Editando registro, ';
    if ATable.State = dsInsert then
      strMessage := 'Inserindo registro, ';
 
    wrdMessageResult := MessageDlg(strMessage+ 'as informações que ainda não '+ 'foram salvas serão perdidas!'+#10+ 'Salva as informações ?', mtConfirmation, [mbYes,mbNo,mbCancel],0);
    case wrdMessageResult of
      mrCancel: Abort;
      mrYes : ATable.Post;
      mrNo : ATable.Cancel;
    end;
  end
  else
    ATable.Cancel;
end;
 

As três respostas mais comuns, para Cancelar a operação em curso é utilizado o procedimento Abort, caso a resposta seja mrYes então os dados da tabela são gravados e se for mrNo a edição ou inserção será cancelada.
 
Na linha que utilizamos a função MessageDlg a linha de mensagem que foi passada continha um caractere ASCII(10)para forçar a quebra de linha.

 

 

wrdMessageResult := MessageDlg(strMessage+ 'as informações que ainda não '+ 'foram salvas serão perdidas!'+#10+  'Salva as informações ?', mtConfirmation, [mbYes,mbNo,mbCancel],0);
 
Fica mais fácil se você controlar a quebra de linhas quando a mensagem a ser informada ocupar mais de uma linha, normalmente a quebra oferecida pelo Delphi deixa muito a desejar.
 
O procedimento associado ao evento OnClick do botão "Cancela" utiliza o procedimento CheckState, muito pratico,já que esta lá vamos usar ...

procedure TForm1.btnCancelaClick(Sender: TObject);
begin
  CheckState(tblCadcli);
end;
 
Evento OnDataChange do Datasource. 
Utilizamos este evento para determinar quando houve uma movimentação da tabela,assim é possível ligar ou desligar os botões de movimentação para o usuário.
 
procedure TForm1.dtsCadcliDataChange(Sender: TObject; Field: TField);
var
  localTable : TTable;
begin
  if Field <> nil then
    exit;
  localTable := ((sender as TDatasource).Dataset as TTable); 
  btnNext.enabled := not localTable.eof;
  btnLast.enabled := not localTable.eof;
  btnPrior.enabled := not localTable.bof;
  btnFirst.enabled := not localTable.bof;
end;
 

Evento OnStateChange do Datasource.
 
Este parte do código foi bem alterada, primeiro as mensagens ao usuário foram melhoradas e os botões estão sendo controlados apenas quando realmente o estado da tabela se altera assim como a disponibilidade ou não dos TDBEdit's envolvidos. 
Quando em edição o TDBEdit para edição do código torna-se indisponível para impedir a alteração do campo chave. 
Também neste evento são executadas as alterações necessárias na interface para que o usuário possa entrar com a chave para pesquisa.

procedure TForm1.dtsCadcliStateChange(Sender: TObject);
var
 localState : TDataSetState;
begin 
  localState := (Sender as TDatasource).State;
  btnPesquisa.enabled := (LocalState in [dsBrowse, dsSetKey]);
  { Botão de pesquisa }
  btnGrava.enabled := not(LocalState in [dsBrowse, dsSetKey]);
  btnCancela.enabled := not(LocalState in [dsBrowse]);
  btnNovo.enabled := (LocalState = dsBrowse);
  btnEdita.enabled := (LocalState = dsBrowse);
 { Controla a mensagem do display }
  case localState of
  dsBrowse: begin
                     btnPesquisa.Caption:= 'Pesquisa';
                     edtCODIGO.color := clWindow;
                     Display.Font.Color := clBlue;
                     Display.Caption := 'Navegando';
                   end;
  dsEdit : begin
                Display.Font.Color := clRed;
                Display.Caption := 'Editando registro';
              end;
  dsInsert: begin
                  Display.Font.Color := clRed;
                  Display.Caption := 'Inserindo registro';
                end;
  dsSetKey: begin
                     btnPesquisa.Caption:= 'Executa ...';
                     edtCODIGO.color := clYellow;
                     Display.Font.Color := clRed;
                     Display.Caption := 'Pesquisando registro, ESC Cancela';
                  end;
  end;
 
{ O campo de código só pode ser acessado em inclusão }
edtCodigo.Enabled := LocalState in [dsInsert, dsSetKey];
 
if LocalState in [dsInsert, dsSetKey] then
  if edtCodigo.Canfocus then
    edtCodigo.SetFocus;
 
if LocalState = dsEdit then
  if edtNome.CanFocus then
    edtNome.SetFocus;
end;
 
O botão de pesquisa foi incluido na interface e dispara o seguinte procedimento...
 
procedure TForm1.btnPesquisaClick(Sender: TObject);
var
  strChave : string;
begin
  with tblCadCli do
  case State of
    dsBrowse : SetKey;
    dsSetKey : begin
    strChave := edtCodigo.Text; { Guarda a chave para pesquisa aproximada }
    if Trim( strChave ) = '' then
    begin
      cancel;
      exit;

    end;
 
  if not GotoKey then
    if MessageDlg('Não foi encontrada a chave '+strChave+' !'+#13+  'Utiliza o registro mais próximo à chave ?', mtConfirmation,[mbYes, mbNO],0)  = mrYes then
    begin
      SetKey;
      FieldByName('CODIGO').AsString := strChave;
      GotoNearest;
    end;
  end;
end;
end;
 

Caso a chave informada não seja encontrada no arquivo o usuário é questionado se deseja parar no registro mais próximo.
 
O Evento OnCloseQuery do form é disparado imediatamente antes de se fechar o form e é a sua ultima chance de impedir este fechamento.
 
No projeto de exemplo onCloseQuery chama o procedimento CheckState quando a tabela do form estiver em estado de edição ou inserção.
 
Para impedir que o form seja fechado pode-se utilizar a variável CanClose, passada ao procedimento com "VAR", se for atribuído o valor false a esta variável o form não será fechado, ou gerando uma exception que no nosso caso é gerada dentro de CheckState se o usuário escolher o botão "Cancel".


procedure TForm1.FormCloseQuery(Sender: TObject; var CanClose: Boolean);
begin
  if (tblCadcli.State in [dsEdit, dsInsert]) then
    CheckState(tblCadcli);
end;

Abaixo segue o código completo da unit...
 
==================================================================
 
unit Unit1;
 
interface
 
uses
SysUtils, Windows, Messages, Classes, Graphics,
StdCtrls, Forms, DBCtrls, DB, Mask, Buttons, DBTables, ExtCtrls,
Controls;
 
type
TForm1 = class(TForm)
Panel1: TPanel;
dtsCadcli: TDataSource;
tblCadcli: TTable;
btnFirst: TSpeedButton;
btnPrior: TSpeedButton;
btnNext: TSpeedButton;
btnLast: TSpeedButton;
btnNovo: TButton;
btnEdita: TButton;
btnGrava: TButton;
btnCancela: TButton;
Panel2: TPanel;
Label1: TLabel;
edtCODIGO: TDBEdit;
edtNOME: TDBEdit;
edtENDERECO: TDBEdit;
edtCONTATO: TDBEdit;
edtTELEFONE: TDBEdit;
Display: TLabel;
btnPesquisa: TSpeedButton;
procedure FormCreate(Sender: TObject);
procedure btnFirstClick(Sender: TObject);
procedure btnPriorClick(Sender: TObject);
procedure btnNextClick(Sender: TObject);
procedure btnLastClick(Sender: TObject);
procedure btnNovoClick(Sender: TObject);
procedure btnEditaClick(Sender: TObject);
procedure btnGravaClick(Sender: TObject);
procedure btnCancelaClick(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure dtsCadcliDataChange(Sender: TObject; Field: TField);
procedure FormCloseQuery(Sender: TObject; var CanClose: Boolean);
procedure dtsCadcliStateChange(Sender: TObject);
procedure btnPesquisaClick(Sender: TObject);
procedure edtCODIGOKeyPress(Sender: TObject; var Key: Char);
procedure tblCadcliBeforeOpen(DataSet: TDataSet);
private
{ private declarations }
public
{ public declarations }
end;
 
var
Form1: TForm1;
 
implementation
 
{$R *.DFM}
 
uses Dialogs;
{Uma função local }
 
procedure CheckState(ATable: TTable);
var
  strMessage : String[25];
  wrdMessageResult : word;
begin
  ATable.UpdateRecord; { Atualiza a tabela sem gravar }
  if ( ATable.Modified ) and   ( ATable.State <> dsSetKey ) then
  begin
    if ATable.State = dsEdit then
      strMessage := 'Editando registro, ';
    if ATable.State = dsInsert then
      strMessage := 'Inserindo registro, ';
 
  wrdMessageResult := MessageDlg(strMessage+ 'as informações que ainda não '+ 'foram salvas serão perdidas!'+#10+ 'Salva as informações ?', mtConfirmation, [mbYes,mbNo,mbCancel],0);
  case wrdMessageResult of
     mrCancel: Abort;
     mrYes : ATable.Post;
     mrNo : ATable.Cancel;
   end;
   end
   else
      ATable.Cancel;
   end;
 
{--------------------------------------------------------------}
{FormCreate, FormDestroy }
 
procedure TForm1.FormCreate(Sender: TObject);
begin
  tblCadcli.Open;
end;
 
procedure TForm1.FormDestroy(Sender: TObject);
begin
  tblCadcli.Close;
end;
 
{--------------------------------------------------------------}
{Movimentação (First,Prior,Next,Last) } 

procedure TForm1.btnFirstClick(Sender: TObject);
begin
  tblCadcli.First;
end;
 
procedure TForm1.btnPriorClick(Sender: TObject);
begin
  tblCadcli.Prior;
end;
 
procedure TForm1.btnNextClick(Sender: TObject);
begin
  tblCadcli.Next;
end;
 
procedure TForm1.btnLastClick(Sender: TObject);
begin
  tblCadcli.Last;
end;
 
{Operações (Novo,Edita,Grava,Cancela) }
 
procedure TForm1.btnNovoClick(Sender: TObject);
begin
  tblCadcli.Append;
end;
 
procedure TForm1.btnEditaClick(Sender: TObject);
begin
  tblCadcli.Edit;
end;
 
procedure TForm1.btnCancelaClick(Sender: TObject);
begin
  CheckState(tblCadcli);
end;
 
procedure TForm1.btnGravaClick(Sender: TObject);
var
  tblAux : TTable;
  bolKeyViolation : Boolean;
begin 
  if tblCadcli.State = dsInsert then
  begin
    { cria uma nova TTable em tempo de execução }
    tblAux := TTable.create(self); 
    with tblAux do
    begin
      DataBaseName := tblCadcli.DataBaseName;
      TableName := tblCadcli.TableName;
      ReadOnly := true;
      Active := true;   
      SetKey;
      FieldByName('Codigo').AsString := edtCodigo.text;
      bolKeyViolation := GotoKey;
      Close;
      Free;
      if bolKeyViolation then
        raise Exception.Create('O codigo '+edtCodigo.text+' já existe no arquivo !');
    end;
  end;
  tblCadcli.Post;
end;
 
{--------------------------------------------------------------}
{OnDataChange }
{OnStateChange }
 
procedure TForm1.dtsCadcliDataChange(Sender: TObject; Field: TField);
var
  localTable : TTable;
begin
  if Field <> nil then
    exit;
  localTable := ((sender as TDatasource).Dataset as TTable); 
  btnNext.enabled := not localTable.eof;
  btnLast.enabled := not localTable.eof;   
  btnPrior.enabled := not localTable.bof;
  btnFirst.enabled := not localTable.bof;
end;
 
procedure TForm1.dtsCadcliStateChange(Sender: TObject);
var
  localState : TDataSetState;
begin
 
  localState := (Sender as TDatasource).State;
  btnPesquisa.enabled := (LocalState in [dsBrowse, dsSetKey]); { Botão de pesquisa }
  btnGrava.enabled := not(LocalState in [dsBrowse, dsSetKey]);
  btnCancela.enabled := not(LocalState in [dsBrowse]);
  btnNovo.enabled := (LocalState = dsBrowse);
  btnEdita.enabled := (LocalState = dsBrowse);
 
{ Controla a mensagem do display }
  case localState of
  dsBrowse: begin
                     btnPesquisa.Caption:= 'Pesquisa';
                     edtCODIGO.color := clWindow;
                     Display.Font.Color := clBlue;
                     Display.Caption := 'Navegando';
                   end;
  dsEdit : begin
                 Display.Font.Color := clRed;
                 Display.Caption := 'Editando registro';
               end;
  dsInsert: begin
                  Display.Font.Color := clRed;
                  Display.Caption := 'Inserindo registro';
                end;
  dsSetKey: begin
                     btnPesquisa.Caption:= 'Executa ...';
                     edtCODIGO.color := clYellow;
                     Display.Font.Color := clRed;
                     Display.Caption := 'Pesquisando registro, ESC Cancela';
                   end;
  end;
 
 { O campo de codigo só pode ser acessado em inclusão }
  edtCodigo.Enabled := LocalState in [dsInsert, dsSetKey];
 
  if LocalState in [dsInsert, dsSetKey] then
    if edtCodigo.Canfocus then
      edtCodigo.SetFocus;
  
   if LocalState = dsEdit then
    if edtNome.CanFocus then
      edtNome.SetFocus;
  end;
 
procedure TForm1.FormCloseQuery(Sender: TObject; var CanClose: Boolean);
begin
  if (tblCadcli.State in [dsEdit, dsInsert]) then
  CheckState(tblCadcli);
end;
 
procedure TForm1.btnPesquisaClick(Sender: TObject);
var
  strChave : string;
begin
  with tblCadCli do
  case State of
  dsBrowse : SetKey;
  dsSetKey : begin
  strChave := edtCodigo.Text; { Guarda a chave para pesquisa aproximada }
  if Trim( strChave ) = '' then
  begin
    cancel;
    exit;
  end;
 
  if not GotoKey then
    if MessageDlg('Não foi encontrada a chave '+strChave+' !'+#13+  'Utiliza o registro mais próximo à chave ?', mtConfirmation,[mbYes, mbNO],0) = mrYes then
    begin
      SetKey;
      FieldByName('CODIGO').AsString := strChave;
      GotoNearest;
    end;
  end;
end;
end;
 
procedure TForm1.edtCODIGOKeyPress(Sender: TObject; var Key: Char);
begin
  if tblCadcli.State = dsSetKey then
  case key of
    #13 : btnPesquisa.click;
    #27 : btnCancela.Click;
  end;
end;
 
procedure TForm1.tblCadcliBeforeOpen(DataSet: TDataSet);
begin
  { Pega o arquivo do diretório da aplicação }
  (Dataset as TTable).DataBaseName:=ExtractFilePath(Application.ExeName);
end;
 
end.



Observação: Paulo Henrique Altino Dias - phdias@netsite.com.br


Comentários Comentários
   Ordem:  
Comentários pertencem aos seus respectivos autores. Não somos responsáveis pelo seus conteúdos.
  Edição 112

Revista ActiveDelphi

  50 Programas Fontes


  Produtos

Conheça Nossos Produtos

Copyright© 2001-2016 – Active Delphi – Todos os direitos reservados