Jump to content
Fórum Script Brasil
  • 0

Criando formulários em tempo de execução


Duduh_Capixaba

Question

Olá a todos!

Estou criando formulários em tempo de execução da seguinte forma: busco em minha base de dados o nome

do formulário a ser criado e coloco em uma variável String. Abaixo está o código que estou usando.

procedure TForm1.Button1Click(Sender: TObject);
var
  vcls_form: TPersistentClass;
  vobj_form: TForm;
  nome_form: String;

begin
  RegisterClass(TfrmTela1);
  RegisterClass(TfrmTela2);

  nome_form := tblFORM.FieldByName('DESCRICAO').AsString;
  vcls_form := GetClass(nome_form);

  if (Assigned(vcls_form)) and (vcls_form.InheritsFrom(TForm)) then
  begin
    vobj_form := TFormClass(vcls_form).Create(Self);
    vobj_form.Show;
  end;
end;

Com a String preenchida com o nome do form consigo criar o objeto sem problemas, mas com um porém:

preciso registrar cada classe antes do comando GetClass, ou seja, cada nome de formulário existente no projeto.

No meu teste registrei dois formulários: TfrmTela1 e TfrmTela2, mas em meu projeto tenho mais de 700 forms.

Não gostaria de escrever essas 700 linhas, e nem ter que adicionar mais uma linha quando um novo form for

adicionado ao projeto.

Alguém teria uma outra idéia de como criar um obeto, passando como parâmetro uma variável String, sem ser

necessário registrar classe por classe?

Link to comment
Share on other sites

7 answers to this question

Recommended Posts

  • 0

Olá Jhonas!

Primeiramente obrigado pela resposta. Bem, eu rodei o exemplo CLASSREF, que é muito interessante.

Já havia visto um exemplo parecido com esse, onde um formulário é criado antes dos outros objetos,

código cedido pelo Micheus no forum da DevMedia.

Os dois códigos me deram uma boa noção do que fazer, mas ainda não consegui chegar a uma solução.

Deixe-me explicar melhor o problema:

Tenho um projeto com vários formulários criados em modo de design. TfrmTela1, TfrmTela2, TfrmTela3,

TfrmTela4... TfrmTela700. Esses formulários NÃO são criados na inicialização do aplicativo. Eles são criados

à medida que são chamados no projeto, e depois liberados da memória quando fechados. Ex.:

O cliente clica no menu pra chamar o formulário 1:

Application.CreateForm(TfrmTela1, frmTela1);
  frmTela1.Show;

Quando ele fecha o formulário 1, o mesmo é liberado com um "Action := caFree". Ok! Tudo perfeito. Agora

vamos à minha idéia...

O que eu quero é o seguinte: passar para o código "Application.CreateForm(TfrmTela1, frmTela1)" parâmetros

STRING. No caso, ficaria: Application.CreateForm('TfrmTela1', 'frmTela1').

Por quê isso? Porque eu tenho em minha base de dados o nome de cada formulário do projeto. Sendo assim

eu criaria uma procedure para criar os formulários buscando seu nome na base de dados.

Eu até consegui esse feito usando o código que coloquei no primeiro post. Mas esse código tem um porém:

eu preciso usar o comando "RegisterClass() e UnregisterClass()" em cada formulário, ou seja, teria que passar

form por form (mais de 700) incluindo essas duas linhas de comando com o nome do formulário. Ou ainda, no

formulário principal do projeto, registrar todos os forms de uma vez só e em seguida desregistrar, tendo que

digitar mais de 1500 linhas de código. E ainda por cima ter que incluir duas novas linhas no form principal a cada

novo form inserido no projeto.

Gostaria de uma solução sem ter que utilizar o GetClass(), RegisterClass() e UnregisterClass(). Se tiver uma luz

de como fazer isso me avise.

Abraço

Edited by Duduh_Capixaba
Link to comment
Share on other sites

  • 0
Tenho um projeto com vários formulários criados em modo de design. TfrmTela1, TfrmTela2, TfrmTela3, TfrmTela4... TfrmTela700. Esses formulários NÃO são criados na inicialização do aplicativo. Eles são criados

à medida que são chamados no projeto, e depois liberados da memória quando fechados.

Observar que este procedimento está apenas manipulando as Instâncias e não as Classes de objetos definidas no projeto.

É importante o entendimento sobre o que é Instância e Classe em OO (Orientação a Objetos).

del.0201.gif

Na esquerda estão as instâncias da classe apresentada à direita. ref.: site

O que eu quero é o seguinte: passar para o código "Application.CreateForm(TfrmTela1, frmTela1)" parâmetros

STRING. No caso, ficaria: Application.CreateForm('TfrmTela1', 'frmTela1').

Isto é impossível!

Entenda que o método CreateForm recebe como parâmetro dois endereços de memória. O primeiro se refere à definição da classe e o segundo à variável (passada por referência) que acomodará uma instância da referida classe.

Assim, para criar uma instância de qualquer classe, nos termos que você gostaria, seria realmente necessário o uso de GetClass ou FindClass (métodos para os quais você passa como parâmetro o nome da classe (previamente registrada) e obtem sua "definição").

Eu até consegui esse feito usando o código que coloquei no primeiro post. Mas esse código tem um porém:

eu preciso usar o comando "RegisterClass() e UnregisterClass()" em cada formulário, ou seja, teria que passar

form por form (mais de 700) incluindo essas duas linhas de comando com o nome do formulário.

Você não precisa utilizar o UnregisterClass. No caso de a classe já ter sido registrada, uma nova chamada à RegisterClass não irá gerar problema algum - ela é suficientemente inteligente para isto. Está lá na documentação (help):

If the class is already registered, RegisterClass
does nothing
.
If a different class with the same name is already registered, RegisterClass raises an EFilerError exception.

Deve ser lembrado que um programa em Delphi é compilado e não interpretado. O compilador Delphi otimiza o código de modo que uma variável declarada que não seja utilizada é removida do código final (executável) e o mesmo ocorre com as classes ("definições").

Se voce tem adiconado ao seu projeto o arquivo que define um form (que é uma classe descendente de TForm), mas não faz uso explicitamente dele em lugar algum (tipo: CadProduto := TCadProduto.Create(self) ou Application.CreateForm(TCadProduto, CadProduto)), o compilador irá gerar o código final sem qualquer referência à esta classe de formulário, pois você não a está utilzando. Assim, ainda que você não queira criar uma instância explicitamente, mas queira criá-la dinamicamente da forma como foi colocada aqui neste tópico, então você tem que dizer para o compilador incluir a definição desta classe (do seu form) em seu executável. Somente desta forma ele terá condições de criar uma instância deste form em tempo de execução - conhecendo ele - por isto a necessidade de usar o RegisterClass.

Quando você faz uso explícito da classe (como já exemplifiquei), o compilador automaticamente irá registrar a classe em questão. Isto também está no help para RegisterClass:

"Form classes and component classes
that are referenced
in a form declaration (instance variables)
are automatically registered
."

A estas alturas pode mesmo ser complicado/trabalhoso fazer os ajustes para 700 forms. Na análise do projeto, este dipo de coisa também já deveria ter sido pensada e assim ele cresceria sem causar transtornos.

Na prática, qual seria o benefício esperado com esta abordagem?

No meu entendimento (apenas minha opinião), se forem para:

- Customizar a aplicação e ocultar opções entre diferentes clientes: bom, se for possível obter o nome das classes no executável, bastaria adicioná-la à tabela no banco de dados e lá estariam todas as telas disponíveis.

- Economizar memória: a criação dinâmica dos forms e posterior liberação da memória (como feito usualmente ao remover os FormCreate do projeto) não consumiriam muito mais memória do que o modelo pretendido.

- Criação de múltiplas instâncias de um mesmo form: (um form que cria uma nova instância de sua própia classe) Nesta modalidade, cuidados devem ser tomados quando utilizando utiliza-se componentes que referenciam outros forms/datasources (datasets, datasources, ...) - esta referência criada em design-time é traduzida pelo compilador de forma estática.

Seguem algumas referências que podem ser úteis à algum dos leitores do tópico:

- PROGRAMAÇÃO ORIENTADA A OBJETOS & DELPHI – Parte I – Uma Introdução sobre OO

- Introdução ao Paradigma de Orientação a Objetos (artigo muito bom - vale a leitura)

- Dynamic packages in Delphi (talvez a melhor forma de modularizar uma aplicação Delphi)

Abraços

Link to comment
Share on other sites

  • 0

Olá, Micheus!

Muito obrigado por sua explicação, me esclareceu muita coisa. Bem, pelo visto não tenho mesmo outro

caminho, senão o GetClass. Eu estava estudado sobre OO e agora percebo com mais clareza sua explicação.

A minha ideia era customizar o código. Iria criar uma procedure para chamar as telas. Mas com o GetClass

eu consegui o que queria, e acho que vou fechar mesmo nessa ideia.

Muito obrigado, e até a próxima!

Link to comment
Share on other sites

  • 0
A minha ideia era customizar o código. Iria criar uma procedure para chamar as telas. Mas com o GetClass eu consegui o que queria, e acho que vou fechar mesmo nessa ideia.
Voce ainda pode usar um função para este fim e apenas chamá-la passando alguns parâmetros e se você precisar utilizar o método Show ou ShowModal, talvez seja interessante passar um outro parâmetro para a função que cria o form (sugestão):

procedure LoadForm(nome_form: string; owner: TComponent; is_modal :boolean);
var
  vcls_form: TPersistentClass;
  vobj_form: TForm;
begin
  vcls_form := GetClass(nome_form);
  if (Assigned(vcls_form)) and (vcls_form.InheritsFrom(TForm)) then
  begin
    vobj_form := TFormClass(vcls_form).Create(owner);
    if is_modal then
      vobj_form.ShowModal
    else
      vobj_form.Show;
  end else
    ShowMessage('Tentativa de carga de um formulário inválido ['+nome_form+'].');
end;
Voce pode acrescentar o RegisterClass em cada unit do respectivo form utilizando a seção initialization (supostamente torna as coisas simples), como abaixo:
unit uFrmTela1;
type 
  TfrmTela1 = class(TForm)
  ...
  end;

procedure TfrmTela1.<evento>(...);
begin
  ...
end;

initialization
  RegisterClass(TfrmTela1);

end.

Abraços

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Answer this question...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.



  • Forum Statistics

    • Total Topics
      152.2k
    • Total Posts
      652k
×
×
  • Create New...