Published by Joao Morais on 31 Aug 2008 at 11:08 am
Dica para gerenciamento de formulários
Ao meu ver, uma parte bem mal feita 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
class procedure Execute(var AForm; AModal: Boolean = False);
end;
a implementação deste método 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;
agora falta associar nil à variável pública do form no momento em que ele for destruído. Pode-se escolher dois eventos, o Destroy ou o Close. Vou implementar no Destroy, e a implementação do Close fica a encargo do programador, se ele vai usar o Action como caFree ou se ele vai destruir o form manualmente:
procedure TBaseForm.FormDestroy(Sender: TObject);
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;
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.
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.