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
- 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;
Pergunta
Micheus
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).
- 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. - Da mesma forma, liberamos o datamodule quando não mais o utilizamos - 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. - 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. - 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 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 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 ValorLink para o comentário
Compartilhar em outros sites
0 respostass a esta questão
Posts Recomendados
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.