Published by Joao Morais on 31 Aug 2008 at 11:08 am
Dica para gerenciamento de formulários
Ao meu ver, uma parte mal elaborada do Delphi/Lazarus é o gerenciamento de formulários da aplicação. Tanto o fato de usar uma variável pública independente para controlar a instância quanto a isca ‘jogue tudo no form’ trazem problemas. A primeira abordagem exige que o programador, ou crie os formulários no início da aplicação e não os destrua mais, ou exige que ele cuide para não deixar a variável apontar para um objeto destruído. A segunda abordagem, entre outros problemas, exige que a primeira funcione perfeitamente para que ocorra comunicação entre formulários.
Quanto a segunda abordagem não há muito o que fazer. Ou convive-se com a limitação ou escreve-se um novo framework para contorná-la. Agora, para manter a variável pública atualizada, o programador pode beneficiar-se de pequenos truques como o que eu vou citar aqui.
A minha sugestão para manter a variável pública atualizada é passá-la para um método de classe. Este método cuida de inicializar o form caso necessário, e cuida de atualizar a variável quando o form for destruído. Para isto, implemente o seguinte método na classe base dos formulários, ou seja, aquela classe a partir da qual todos os forms irão herdar:
TBaseForm = class(TForm)
public
destructor Destroy; override;
class procedure Execute(var AForm; AModal: Boolean = False);
end;
a implementação dos métodos fica assim:
class procedure TBaseForm.Execute(var AForm; AModal: Boolean);
var
VIndex: Integer;
begin
if not Assigned(_Forms) then
begin
_Forms := TStringList.Create;
_Forms.Sorted := True;
end;
if not _Forms.Find(ClassName, VIndex) then
VIndex := _Forms.Add(ClassName);
if TForm(AForm) = nil then
Application.CreateForm(Self, AForm);
_Forms.Objects[VIndex] := TObject(@AForm);
if AModal then
TForm(AForm).ShowModal
else
TForm(AForm).Show;
end;
e associar nil à variável pública do form no momento em que ele for destruído:
destructor TBaseForm.Destroy;
type
PForm = ^TForm;
var
VIndex: Integer;
begin
if Assigned(_Forms) and _Forms.Find(ClassName, VIndex) and
(_Forms.Objects[VIndex] <> nil) then
begin
PForm(_Forms.Objects[VIndex])^ := nil;
_Forms.Objects[VIndex] := nil;
end;
inherited;
end;
Por fim, falta declarar a StringList e destruí-la no encerramento da aplicação:
implementation var _Forms: TStringList = nil; ... initialization finalization _Forms.Free; end.
Pronto. Agora basta criar novos forms que descendam deste e chamar TSeuForm.Execute(SeuForm). Quando o form for destruído, a variável pública irá apontar para nil.
10 Responses to “Dica para gerenciamento de formulários”
Leave a Reply
You must be logged in to post a comment.
Rodrigo Palhano on 07 Oct 2008 at 11:48 pm #
Eu nunca uso a variavel global de forms que o delphi gera. A primeira coisa que eu faço é apagá-la quando crio uma nova unit, então esse problema simplesmente deixa de existir.
se vc usar showmodal fica mais simples
With Tform1.Create (nil) do
begin
ShowModal;
Release;
end;
ou se naum usar showmodal vc pode criar uma referencia em um field no form que chama o form filho
neste caso para limpar a referencia invalida do field vc poderia escutar o onclose do form filho.
o uso destas variaveis globais que o delph encoraja promovem acoplamento.
Joao Morais on 08 Oct 2008 at 8:07 am #
Eu concordo com você, e eu também apago estas variáveis pois o mvp do pressobjects cuida das instâncias por mim.
E além do acoplamento, que vai ocorrer de qualquer forma ao acessar a classe do form, o modelo proposto e a forma que o delphi encoraja o uso dos forms imaginam uma única instância de cada form de cada vez.
No entanto tenho visto pessoas procurarem meios de automatizar a atualização desta variável global, e é este o objetivo do artigo.
Rodrigo Palhano on 08 Oct 2008 at 8:52 am #
Outra abordagem seria utilizar o mecanismo de notificação já presente na classe TComponent
procedure Notification(AComponent: TComponent; Operation: TOperation); override;
para remover a referencia do field que aponte para o sub-form.
Joao Morais on 12 Oct 2008 at 10:31 am #
Que não seria mais eficiente do que sobrecarregar o destructor. E o maior problema ainda permaneceria: como localizar a variável que aponta para a instância do form a fim de setá-la para nil?
Erich Nascimento on 22 Nov 2008 at 9:23 pm #
Cara, essa dica me serviu muito bem.
Estou reestruturando minhas aplicações que são SDI para MDI.
Ontem tive uma ideia de criar uma classe base da qual seriam herdados todos os meus formularios child. A merda ocorria exatamente neste ponto. Na hora em que fechava o formulario filho, não conseguia setar a variavel de instancia para nil a partir dessa classe pai.
Tentei de varias formas, fiquei umas 3 horas googando e naaada. Relaxei, tomei uma Pepsi, e encontrei seu blog. Show cara, serviu perfeitamente esta técnica para mim.
Obrigado.
Jaserises on 15 Feb 2009 at 7:19 pm #
Your web page does not correctly work in safari browser
Joao Morais on 16 Feb 2009 at 6:21 pm #
Known issue. Thanks.
Thiago on 26 May 2009 at 10:25 am #
Bom dia, o meu problema com gerenciamento de Formulários acontece da seguinte forma:
Eu tenho um formulario de Busca, e um Formulario de Cadastro, No meu formulario de busca estão os componentes de Persistencia (TSqlQuery, TDataSetProvider, TClientDataSet) que serão utilizados, tanto para buscar os registros, quanto para fazer a inserção e edição dos mesmos. O Formulario de Cadastro não possue componentes de Persistencia, ou seja, Todos os “DbEdits” que estão na tela de cadastro, estão referenciados aos componentes de persistencia do Formulario de Busca.
O Problema Ocorre no seguinte caso : Eu tenho um Formulario de Busca Instanciado. Eu quero criar outra Instancia deste mesmo Formulario Busca, o qual eu realizarei alterações no componente de Persistencia, e ao criar um Formulario de Cadastro, eu necessito que este esteja referenciada com essa instancia alterada, e não a primeira instancia que eu já o tinha criada… Em outras Palavras …. Os tais Componentes “DbEdits” presentes na tela de Cadastro estão ligados aos componentes que eu não alterei, isto quer dizer q o Fomulario de Cadastro que eu tenho esta vinculado a primeira instancia do Formulario de Busca, e não a Instancia que eu alterei os componentes…
A duvida é, como referenciar o Formulario de Cadastro a segunda instancia do Formulario de Busca e não a primeira?
Silvio Clécio on 16 Feb 2010 at 11:30 pm #
João meu caro, estou sempre visitando seu blog e relendo esses artigos, pois são ótimos, os melhores.
Uma coisa, só uma pequena correção no segundo bloco de código, colocar o “= False” no “Execute(var AForm; AModal: Boolean)”, para ficar igual a declaração.
Silvio Clécio on 07 Apr 2010 at 4:09 am #
João, esquece o ultimo comentário que fiz, executa normal aqui no Lazarus
Quem quiser calar os hints chatos do FPC:
(…)
var
VIndex: Integer;
begin
VIndex := 0;
if not Assigned(_Forms) then
begin
(…)
João, detona esse cara do “vigara medicine”, que spam fdp!