Delphi Form Consulta - Error Acess Violation
08/01/2021
0
Estou com um probleminha bem simples, vamos lá!
Tenho 1 formulário de consulta(Cidade) que é usado por vários outros formulários de cadastro ex: Funcionário, cliente e até mesmo cidade.
Na minha Grid do formulário de consulta(cidade) no evento onkeypress, coloquei para que quando o usuário pressionasse ENTER, o formulário de cadastro recebe as informações e em seguida o formulário de consulta fecha.
Ao chamar o formulário de consulta pelo cadastro de funcionário por exemplo, tenho a seguinte mensagem de erro: Access violation at address 004074d2 in module 'UNILITE.exe'. Write of address 00000000.
Código:
procedure TFrmConsulCidade.DBGridConsulCidadeKeyPress(Sender: TObject;
var Key: Char);
var
nome_cid, cep : string;
begin
nome_cid := FrmCadasCidade.FDQryCidade.FieldByName('NOME_CID').AsString;
cep := FrmCadasCidade.FDQryCidade.FieldByName('CEP').AsString;
if key = #13 then
begin
FrmConsulCidade.Close;
end;
begin
if key = #13 then
begin
FrmCadastroFunc.FDQryFunc.FieldByName('CIDADE').AsString := nome_cid;
FrmCadastroFunc.FDQryFunc.FieldByName('CEP').AsString := cep;
FrmConsulCidade.Close;
end;
begin
if key = #13 then
begin
FrmCadCliente.FDQryCadCliente.FieldByName('CIDADE').AsString := nome_cid;
FrmCadCliente.FDQryCadCliente.FieldByName('CEP').AsString := cep;
FrmConsulCidade.Close;
end;
end;
end;
end;
Usei o seguinte método antes do código nos meus botões de chamada do formulário de consulta(Cidade): Application.CreateForm(TFrmConsulCidade, FrmConsulCidade);
Sei o motivo do erro, no qual estou tentando acessar um formulário que ainda não foi criado. Sei também que é normal programadores passar por este tipo de erro, ainda mais iniciante como eu.
Qualquer ajuda é bem vida!!
Obrigado!!
Jefferson
Posts
08/01/2021
Emerson Nascimento
isso é abstração: quem chamou não precisa entender o que está sendo executado em quem foi chamado; quem foi chamado não precisa saber quem o chamou.
você não está tratando isto no teu código. você está devolvendo o retorno para todas as telas, estando elas criadas ou não, por isso o ACCESS VIOLATION.
mude a forma como faz a consulta.
a tua tela de consulta não deve fazer referência a nenhuma tela que a utiliza.
retire da uses da consulta todas as referências às telas de cadastro.
daí, na tela de consulta, crie uma classe para os dados de retorno.
no caso:
type TRetCidade: record Nome: string; CEP: string; // cep??? end;
type TFrmConsulCidade = class(TForm) public { Public declarations } class function Abre: TRetCidade; // função que deverá ser chamada onde se pretender utilizar a consulta de cidades end; var FrmConsulCidade: TFrmConsulCidade; {*.res} implementation class function TFrmConsulCidade.Abre: TRetCidade; var FrmConsulta: TFrmConsulCidade; begin FrmConsulta := TFrmConsulCidade.Create(nil); if FrmConsulta.ShowModal = mrOk then // se finalizou com Ok begin Result.Nome := FrmConsulta.DBGridConsulCidade.DataSource.Dataset.FieldByName('CIDADE').AsString; Result.CEP := FrmConsulta.DBGridConsulCidade.DataSource.Dataset.FieldByName('CEP').AsString; end else begin Result.Nome := EmptyStr; Result.CEP := EmptyStr; end; FreeAndNil(FrmConsulta); // finaliza o objeto e libera a memória utilizada end;
procedure TFrmConsulCidade.DBGridConsulCidadeKeyPress(Sender: TObject; var Key: Char); begin if key = #13 then ModalResult := mkOk; // fecha a tela end;
agora acrescente a unit da consulta na uses de todos os formulários que pretendem chamar a consulta de cidades (no teu caso, já deve estar acrescentada).
daí, para chamar a consulta, faça assim (supondo que está na tela de cadastro de funcionários):
var RetCidade: UnitDaTelaDeConsultaDeCidades.TRetCidade; begin RetCidade := TFrmConsulCidade.Abre; // abre a tela de consulta. if RetCidade.Nome <> EmptyStr then // quer dizer que a tela de consulta foi 'confirmada' begin FDQryFunc.FieldByName('CIDADE').AsString := RetCidade.Nome; FDQryFunc.FieldByName('CEP').AsString := RetCidade.CEP; end; end;
basta fazer este procedimento em todas as telas que fazem consulta ao cadastro de cidades, efeutando as alterações pertinentes (tabelas/datasets, campos e o que mais for necessário).
desta forma apresentada, ao criar uma nova tela que precise consultar uma cidade, não será necessário alterar a tela de consulta (como era necessário da forma como você desenvolveu).
OBS.: pode haver algum erro de sintaxe porque não tenho Delphi instalado. fiz tudo diretamente aqui no fórum.
10/01/2021
Jefferson
Não conseguir Emerson, Tenho um alerta de erro ai inserir a classe e a função.
Meu código completo:
unit UnitConsulCidade;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Data.DB, FireDAC.Stan.Intf,
FireDAC.Stan.Option, FireDAC.Stan.Param, FireDAC.Stan.Error, FireDAC.DatS,
FireDAC.Phys.Intf, FireDAC.DApt.Intf, FireDAC.Stan.Async, FireDAC.DApt,
FireDAC.Comp.DataSet, FireDAC.Comp.Client, Vcl.Imaging.pngimage, Vcl.ExtCtrls,
Vcl.StdCtrls, Vcl.Buttons, Vcl.Grids, Vcl.DBGrids;
type
TFrmConsulCidade = class(TForm)
Panel1: TPanel;
Panel2: TPanel;
DBGridConsulCidade: TDBGrid;
SpeedButton1: TSpeedButton;
EdtConsultaCidade: TEdit;
Image1: TImage;
Label1: TLabel;
procedure SpeedButton1Click(Sender: TObject);
procedure DBGridConsulCidadeDrawColumnCell(Sender: TObject; const Rect: TRect;
DataCol: Integer; Column: TColumn; State: TGridDrawState);
procedure EdtConsultaCidadeChange(Sender: TObject);
procedure DBGridConsulCidadeKeyPress(Sender: TObject; var Key: Char);
procedure DBGridConsulCidadeDblClick(Sender: TObject);
procedure FormShow(Sender: TObject);
procedure EdtConsultaCidadeKeyPress(Sender: TObject; var Key: Char);
private
{ Private declarations }
nome_cid, cep : string;
public
{ Public declarations }
end;
var
FrmConsulCidade: TFrmConsulCidade;
implementation
{$R *.dfm}
uses UnitDataModule, UnitCadCidade, UnitCadastroFuncionario, UnitCadCliente;
procedure TFrmConsulCidade.DBGridConsulCidadeDblClick(Sender: TObject);
begin
FrmConsulCidade.Close;
end;
procedure TFrmConsulCidade.DBGridConsulCidadeDrawColumnCell(Sender: TObject;
const Rect: TRect; DataCol: Integer; Column: TColumn; State: TGridDrawState);
begin
if not odd(FrmCadasCidade.FDQryCidade.RecNo) then
if not (gdSelected in State) then
begin
DBGridConsulCidade.Canvas.Brush.Color := clGradientInactiveCaption;
DBGridConsulCidade.Canvas.FillRect(Rect);
DBGridConsulCidade.DefaultDrawDataCell(rect,Column.Field,state);
end;
end;
procedure TFrmConsulCidade.DBGridConsulCidadeKeyPress(Sender: TObject;
var Key: Char);
begin
nome_cid := FrmCadasCidade.FDQryCidade.FieldByName('NOME_CID').AsString;
cep := FrmCadasCidade.FDQryCidade.FieldByName('CEP').AsString;
if key = #13 then
begin
FrmConsulCidade.Close;
end;
begin
if key = #13 then
begin
FrmCadastroFunc.FDQryFunc.FieldByName('CIDADE').AsString := nome_cid;
FrmCadastroFunc.FDQryFunc.FieldByName('CEP').AsString := cep;
FrmConsulCidade.Close;
end;
begin
if key = #13 then
begin
FrmCadCliente.FDQryCadCliente.FieldByName('CIDADE').AsString := nome_cid;
FrmCadCliente.FDQryCadCliente.FieldByName('CEP').AsString := cep;
FrmConsulCidade.Close;
end;
end;
end;
end;
procedure TFrmConsulCidade.EdtConsultaCidadeChange(Sender: TObject);
begin
FrmCadasCidade.FDQryCidade.close;
FrmCadasCidade.FDQryCidade.sql.clear;
FrmCadasCidade.FDQryCidade.sql.add('select * from cidade where nome_cid like ''%'+ EdtConsultaCidade.text+'%''');
FrmCadasCidade.FDQryCidade.open;
end;
procedure TFrmConsulCidade.EdtConsultaCidadeKeyPress(Sender: TObject;
var Key: Char);
begin
if key = #13 then
begin
DBGridConsulCidade.SetFocus;
end;
end;
procedure TFrmConsulCidade.FormShow(Sender: TObject);
begin
EdtConsultaCidade.SetFocus;
end;
procedure TFrmConsulCidade.SpeedButton1Click(Sender: TObject);
begin
FrmCadasCidade.FDQryCidade.Active := true;
end;
end.
10/01/2021
Jefferson
Obs: Formulário de consulta(cidade) usa o data source do formulário cadastro cidade.
11/01/2021
Emerson Nascimento
unit UnitConsulCidade; interface uses Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Data.DB, FireDAC.Stan.Intf, FireDAC.Stan.Option, FireDAC.Stan.Param, FireDAC.Stan.Error, FireDAC.DatS, FireDAC.Phys.Intf, FireDAC.DApt.Intf, FireDAC.Stan.Async, FireDAC.DApt, FireDAC.Comp.DataSet, FireDAC.Comp.Client, Vcl.Imaging.pngimage, Vcl.ExtCtrls, Vcl.StdCtrls, Vcl.Buttons, Vcl.Grids, Vcl.DBGrids; type TRetCidade: record Nome: string; CEP: string; end; TFrmConsulCidade = class(TForm) FNome_cid: string; // retorna o nome da cidade FCEP: string; // retorna o CEP da cidade Panel1: TPanel; Panel2: TPanel; DBGridConsulCidade: TDBGrid; SpeedButton1: TSpeedButton; EdtConsultaCidade: TEdit; Image1: TImage; Label1: TLabel; procedure SpeedButton1Click(Sender: TObject); procedure DBGridConsulCidadeDrawColumnCell(Sender: TObject; const Rect: TRect; DataCol: Integer; Column: TColumn; State: TGridDrawState); procedure EdtConsultaCidadeChange(Sender: TObject); procedure DBGridConsulCidadeKeyPress(Sender: TObject; var Key: Char); procedure DBGridConsulCidadeDblClick(Sender: TObject); procedure FormShow(Sender: TObject); procedure FormCreate(Sender: TObject); procedure EdtConsultaCidadeKeyPress(Sender: TObject; var Key: Char); private { Private declarations } public { Public declarations } class function Abre: TRetCidade; // função que deverá ser chamada onde se pretender utilizar a consulta de cidades end; // não precisa declarar a variável abaixo. todo o tratamento será feito pelo médoto Abre (class function Abre) //var // FrmConsulCidade: TFrmConsulCidade; implementation {$R *.dfm} // abaixo deveria ter somente a UnitDataModule, porque o dataset deveria estar na própria tela de consulta // ** a tela de consulta precisa ser auto-suficiente, precisando somente do datamodulo ** uses UnitDataModule, UnitCadCidade; class function TFrmConsulCidade.Abre: TRetCidade; var FrmConsulta: TFrmConsulCidade; begin FrmConsulta := TFrmConsulCidade.Create(nil); if FrmConsulta.ShowModal = mrOk then // se finalizou com Ok begin Result.Nome := FrmConsulta.FNome_Cid; Result.CEP := FrmConsulta.FCEP; end else begin Result.Nome := EmptyStr; Result.CEP := EmptyStr; end; FreeAndNil(FrmConsulta); // finaliza o objeto e libera a memória utilizada end; procedure TFrmConsulCidade.FormCreate(Sender: TObject); begin // inicializa os campos de retorno com conteúdo vazio FNome_cid := EmptyStr; FCep := EmptyStr; end; procedure TFrmConsulCidade.DBGridConsulCidadeDblClick(Sender: TObject); begin // FrmConsulCidade.Close; ModalResult := mrCancel; // fecha a tela com Cancel - poderia ser com Ok end; procedure TFrmConsulCidade.DBGridConsulCidadeDrawColumnCell(Sender: TObject; const Rect: TRect; DataCol: Integer; Column: TColumn; State: TGridDrawState); begin if not odd(FrmCadasCidade.FDQryCidade.RecNo) then if not (gdSelected in State) then begin DBGridConsulCidade.Canvas.Brush.Color := clGradientInactiveCaption; DBGridConsulCidade.Canvas.FillRect(Rect); DBGridConsulCidade.DefaultDrawDataCell(rect,Column.Field,state); end; end; procedure TFrmConsulCidade.DBGridConsulCidadeKeyPress(Sender: TObject; var Key: Char); begin if key = #13 then begin // o dataset deveria estar na própria tela de consulta, e não usar o dataset de outra tela FNome_cid := FrmCadasCidade.FDQryCidade.FieldByName('NOME_CID').AsString; FCEP := FrmCadasCidade.FDQryCidade.FieldByName('CEP').AsString; ModalResult := mrOk; // fecha a tela com Ok end; end; procedure TFrmConsulCidade.EdtConsultaCidadeChange(Sender: TObject); begin // o dataset deveria estar na própria tela de consulta, e não usar o dataset de outra tela FrmCadasCidade.FDQryCidade.close; FrmCadasCidade.FDQryCidade.sql.clear; FrmCadasCidade.FDQryCidade.sql.add('select * from cidade where nome_cid like ''%'+ EdtConsultaCidade.text+'%'''); FrmCadasCidade.FDQryCidade.open; end; procedure TFrmConsulCidade.EdtConsultaCidadeKeyPress(Sender: TObject; var Key: Char); begin if key = #13 then DBGridConsulCidade.SetFocus; end; procedure TFrmConsulCidade.FormShow(Sender: TObject); begin EdtConsultaCidade.SetFocus; end; procedure TFrmConsulCidade.SpeedButton1Click(Sender: TObject); begin // o dataset deveria estar na própria tela de consulta, e não usar o dataset de outra tela FrmCadasCidade.FDQryCidade.Open; end; end.
o uso desta tela deveria ser assim, a partir do cadastro de funcionários:
(precisa colocar UnitConsulCidade na uses do cadastro de funcionários)
procedure TFrmCadastroFunc.ButtonConsultaCidadeClick(Sender: TObject); var RetCidade: UnitConsulCidade.TRetCidade; begin RetCidade := TFrmConsulCidade.Abre; // abre a tela de consulta. if RetCidade.Nome <> EmptyStr then // quer dizer que a tela de consulta foi 'confirmada' begin FDQryFunc.FieldByName('CIDADE').AsString := RetCidade.Nome; FDQryFunc.FieldByName('CEP').AsString := RetCidade.CEP; end; end;
12/01/2021
Jefferson
Tive que colocar na cadeia de variável (Private) as variáveis (FNome_cid: string, FCEP: string), pois estava dando erro ao copilar o projeto.
E no evento Double click da grid da tela de consulta coloque pra executar a mesma função do evento onkeypress, pois quero que o usuário utilize as duas opções.
Estou analisando cada linha do código, ainda sou leigo, mais através de pesquisas estou entendendo melhor.
E vou rever a questão sobre o data set está ligado na tela de consulta, deve ser uma ótima ideia, obrigado pelo conselho.
Código:
unit UnitConsulCidade;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Data.DB, FireDAC.Stan.Intf,
FireDAC.Stan.Option, FireDAC.Stan.Param, FireDAC.Stan.Error, FireDAC.DatS,
FireDAC.Phys.Intf, FireDAC.DApt.Intf, FireDAC.Stan.Async, FireDAC.DApt,
FireDAC.Comp.DataSet, FireDAC.Comp.Client, Vcl.Imaging.pngimage, Vcl.ExtCtrls,
Vcl.StdCtrls, Vcl.Buttons, Vcl.Grids, Vcl.DBGrids;
type
TRetCidade = record
Nome: string;
CEP: string;
end;
TFrmConsulCidade = class(TForm)
Panel1: TPanel;
Panel2: TPanel;
DBGridConsulCidade: TDBGrid;
SpeedButton1: TSpeedButton;
EdtConsultaCidade: TEdit;
Image1: TImage;
Label1: TLabel;
procedure SpeedButton1Click(Sender: TObject);
procedure DBGridConsulCidadeDrawColumnCell(Sender: TObject; const Rect: TRect;
DataCol: Integer; Column: TColumn; State: TGridDrawState);
procedure EdtConsultaCidadeChange(Sender: TObject);
procedure DBGridConsulCidadeKeyPress(Sender: TObject; var Key: Char);
procedure DBGridConsulCidadeDblClick(Sender: TObject);
procedure FormShow(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure EdtConsultaCidadeKeyPress(Sender: TObject; var Key: Char);
private
{ Private declarations }
FNome_cid: string; // retorna o nome da cidade
FCEP: string; // retorna o CEP da cidade
public
{ Public declarations }
class function Abre: TRetCidade; // função que deverá ser chamada onde se pretender utilizar a consulta de cidades
end;
// não precisa declarar a variável abaixo. todo o tratamento será feito pelo médoto Abre (class function Abre)
//var
// FrmConsulCidade: TFrmConsulCidade;
implementation
{$R *.dfm}
// abaixo deveria ter somente a UnitDataModule, porque o dataset deveria estar na própria tela de consulta
// ** a tela de consulta precisa ser auto-suficiente, precisando somente do datamodulo **
uses
UnitDataModule, UnitCadCidade;
class function TFrmConsulCidade.Abre: TRetCidade;
var
FrmConsulta: TFrmConsulCidade;
begin
FrmConsulta := TFrmConsulCidade.Create(nil);
if FrmConsulta.ShowModal = mrOk then // se finalizou com Ok
begin
Result.Nome := FrmConsulta.FNome_Cid;
Result.CEP := FrmConsulta.FCEP;
end
else
begin
Result.Nome := EmptyStr;
Result.CEP := EmptyStr;
end;
FreeAndNil(FrmConsulta); // finaliza o objeto e libera a memória utilizada
end;
procedure TFrmConsulCidade.FormCreate(Sender: TObject);
begin
// inicializa os campos de retorno com conteúdo vazio
FNome_cid := EmptyStr;
FCep := EmptyStr;
end;
procedure TFrmConsulCidade.DBGridConsulCidadeDblClick(Sender: TObject);
begin
// o dataset deveria estar na própria tela de consulta, e não usar o dataset de outra tela
FNome_cid := FrmCadasCidade.FDQryCidade.FieldByName('NOME_CID').AsString;
FCEP := FrmCadasCidade.FDQryCidade.FieldByName('CEP').AsString;
ModalResult := mrOk; // fecha a tela com Ok
end;
procedure TFrmConsulCidade.DBGridConsulCidadeDrawColumnCell(Sender: TObject;
const Rect: TRect; DataCol: Integer; Column: TColumn; State: TGridDrawState);
begin
if not odd(FrmCadasCidade.FDQryCidade.RecNo) then
if not (gdSelected in State) then
begin
DBGridConsulCidade.Canvas.Brush.Color := clGradientInactiveCaption;
DBGridConsulCidade.Canvas.FillRect(Rect);
DBGridConsulCidade.DefaultDrawDataCell(rect,Column.Field,state);
end;
end;
procedure TFrmConsulCidade.DBGridConsulCidadeKeyPress(Sender: TObject;
var Key: Char);
begin
if key = #13 then
begin
// o dataset deveria estar na própria tela de consulta, e não usar o dataset de outra tela
FNome_cid := FrmCadasCidade.FDQryCidade.FieldByName('NOME_CID').AsString;
FCEP := FrmCadasCidade.FDQryCidade.FieldByName('CEP').AsString;
ModalResult := mrOk; // fecha a tela com Ok
end;
end;
procedure TFrmConsulCidade.EdtConsultaCidadeChange(Sender: TObject);
begin
// o dataset deveria estar na própria tela de consulta, e não usar o dataset de outra tela
FrmCadasCidade.FDQryCidade.close;
FrmCadasCidade.FDQryCidade.sql.clear;
FrmCadasCidade.FDQryCidade.sql.add('select * from cidade where nome_cid like ''%'+ EdtConsultaCidade.text+'%''');
FrmCadasCidade.FDQryCidade.open;
end;
procedure TFrmConsulCidade.EdtConsultaCidadeKeyPress(Sender: TObject;
var Key: Char);
begin
if key = #13 then
DBGridConsulCidade.SetFocus;
end;
procedure TFrmConsulCidade.FormShow(Sender: TObject);
begin
EdtConsultaCidade.SetFocus;
end;
procedure TFrmConsulCidade.SpeedButton1Click(Sender: TObject);
begin
// o dataset deveria estar na própria tela de consulta, e não usar o dataset de outra tela
FrmCadasCidade.FDQryCidade.Open;
end;
end.
Cara você me ajudou demais, Muito obrigado!!!
Que Deus te pague...
12/01/2021
Emerson Nascimento
procedure TFrmConsulCidade.DBGridConsulCidadeKeyPress(Sender: TObject; var Key: Char); begin if key = #13 then DBGridConsulCidadeDblClick(Sender); // chama o evento DblClick end;
12/01/2021
Emerson Nascimento
Basta copiar o FDQryCidade - e seu datasource, se for o caso - para a tela de consulta. Daí fazer as ligações de grades e/ou dbedits (componentes data-aware) nesse novo conjunto dataset/datasource (terão até os mesmos nomes).
Com tudo pronto, basta apagar todas as referências à FrmCadasCidade da tela de consulta de cidades, inclusive a declaração na uses.
14/01/2021
Jefferson
14/01/2021
Emerson Nascimento
procedure TFrmConsulCidade.DBGridConsulCidadeKeyPress(Sender: TObject; var Key: Char); begin if key = #13 then begin DBGridConsulCidadeDblClick(Sender); // chama o evento DblClick key = #0; // limpa a tecla, para que a ação não seja propagada end; end;
14/01/2021
Jefferson
procedure TFrmCadastroFunc.DBEditCidFuncKeyPress(Sender: TObject;
var Key: Char);
var
RetCidade: UnitConsulCidade.TRetCidade;
begin
RetCidade := TFrmConsulCidade.Abre; // abre a tela de consulta.
if key = # 13 then
begin
RetCidade.Nome <> EmptyStr then // quer dizer que a tela de consulta foi 'confirmada'
FDQryFunc.FieldByName('CIDADE').AsString := RetCidade.Nome;
FDQryFunc.FieldByName('CEP').AsString := RetCidade.CEP;
DBEditCepFunc.SetFocus;
end;
end;
Só exemplo, sei que a logica está errada.
No caso assim que eu pressionasse enter no dbedit o fomulário de consulta será chamado.
14/01/2021
Jefferson
procedure TFrmCadastroFunc.DBEditCidFuncKeyPress(Sender: TObject;
var Key: Char);
begin
if key = #13 then
begin
SbConsultaCidadFunc(sender);
end;
end;
No formulário também tem um botão que ao ser clicado possibilita que o usuário faça a chamada da tela de consulta.
E no botão contém o código no qual você me passou, não sei se é correto, mais enfim, está funcionando.
Clique aqui para fazer login e interagir na Comunidade :)