Converter String para TComponent??????

30/08/2004

Aê Galera..

Tudo bem?

É o seguinte, preciso converter uma classe armazenada no banco de dados, no formato string para TComponent..

Assim :

banco de dados

TRLLabel 0000000001
TRLMemo 0000000002
.
.
.

assim por diante, quero dar um select nos campos string e criar em RUN TIME os componentes, referêntes as classes armazenadas no banco..

tem como??

agradeço a todos antecipadamente..

Cabelo


Cabelo

Respostas

25/07/2008

Fieldztime

Ola Amigão, tudo bem?, no delphi os componentes são da classe ´Tcomponent´, não podemos acessa-los por strings que é o seu caso, mas podemos chamar uma funcao chamanda FINDCOMPONENT que procura pelo nome que voce passar e retorna TCOMPONENT ou NIL <-caso nao achar por exemplo: digamos que vc tenha 3 Labels: Label1, label2 e label3

vamos mudar o caption do label2 pelo nome do componente, seria assim:


tlabel( findcomponent(´label2´) ).caption:=´Novo Caption do Label2!´;

para procurar em outros forms é a mesma coisa, se vc estiver no form1 para procurar no form2 é assim

tlabel( form2.findcomponent(´label2´) ).caption:=´Novo Caption do Label2!´;

dara uma excecao de erro se o componente nao existir, voce pode fazer assim para checar:

temp_comp: tcomponent;

temp_comp:= tlabel( form2.findcomponent(´label2´) );

if temp_comp<>nil then begin

tlabel( form2.findcomponent(´label2´) ).caption:=´Novo Caption do Label2!´;

end;

Simples, porém limitado.

Existe a Forma mais Complexa de fazer a mesma coisa:


i: integer;
begin


for i:=0 to form1.componentcount-1 do begin

if form1.components(i) is tlabel then begin

if tlabel( form1.components(i) ).caption=´label2´ then

tlabel( form1.components(i) ).caption:=´Novo Caption do Label2´;

end;

end;

end;

espero ter ajudado.


Responder Citar

27/07/2008

Micheus

quero dar um select nos campos string e criar em RUN TIME os componentes, referêntes as classes armazenadas no banco.. tem como??

Veja se com este exemplo, vc consegue captar a idéia e implementar algo:
...
implementation
uses
  TypInfo;
{$R *.DFM}

procedure TForm1.Button1Click(Sender: TObject);
type
  TCompInfo = record
    ClassName :string;
    Top,
    Left,
    Width,
    Height :Integer;
    Text :string;
  end;

const
  CompInfoList :array[0..3&93; of TCompInfo =
                ((ClassName :´TLabel´; Top :8; Left :8; Width :30; Height :20; Text :´Nome:´),
                 (ClassName :´TEdit´;  Top :8; Left :58; Width :30; Height :20; Text :´´),
                 (ClassName :´TLabel´; Top :33; Left :8; Width :20; Height :20; Text :´Idade:´),
                 (ClassName :´TEdit´;  Top :33; Left :58; Width :10; Height :20; Text :´´));
var
  PClass: TPersistentClass;
  LForm :TCustomForm;
  LWinControl :TComponent;
  Idx,
  CompNum :Integer;
begin
  PClass := GetClass(´TForm´);
  if PClass <> nil then
  begin
   // criamos um Form em branco
    LForm := TComponentClass(PClass).Create(Application) as TCustomForm;
    LForm.Caption := ´Form dinâmico´;
    LForm.Top := 30;
    LForm.Left := 100;
    LForm.Width := 290;
    LForm.Height := 150;
    CompNum := 1;
   // colocaremos os componentes da lista neste form
    for Idx := 0 to 3 do
    begin
      PClass := GetClass(CompInfoList[Idx&93;.ClassName);
      if PClass <> nil then
      begin
        LWinControl := TComponentClass(PClass).Create(LForm);
        TWinControl(LWinControl).Parent := LForm;
        TWinControl(LWinControl).Name := Format(´CompName¬d´, [CompNum&93;);
        TWinControl(LWinControl).Left :=  CompInfoList&91;Idx&93;.Left;
        TWinControl(LWinControl).Top :=  CompInfoList&91;Idx&93;.Top;
        TWinControl(LWinControl).Width :=  CompInfoList&91;Idx&93;.Width;
        TWinControl(LWinControl).Height :=  CompInfoList&91;Idx&93;.Height;
        TWinControl(LWinControl).SetTextBuf(PChar(CompInfoList&91;Idx&93;.Text));
        Inc(CompNum);
      end;
    end;
    LForm.ShowModal;
    LForm.Release;
  end;
end;

initialization
  RegisterClass(TForm);
  RegisterClass(TLabel);
  RegisterClass(TEdit);

end.

Para que funcione o uso de [i:92e92915d0]GetClass[/i:92e92915d0], é necessário que vc registre as classes desejadas em um statement [i:92e92915d0]initialization[/i:92e92915d0], como fiz no exemplo.

Veja o que consegue, e qualquer dúvida pergunte. Talvez consiga lhe ajudar mais.

Abraços


Responder Citar

28/07/2008

Cabelo

Acho que não conseguí explicar direito minha dúvida.

É o seguinte, vou precisar de criar em tempo de execução os componentes que o usuário selecionar, na verdade o usuário é que irá montar o relatório com os campos que ele desejar usar, mas para isso preciso de saber quais componentes irei disponibilizar.

Por isso preciso de ler o nome da string que o usuário selecionar e criar o componente.


Responder Citar

28/07/2008

Marco Salles

´mais ou menos para voce ter uma idéia :

var
  newObj:TControl;

procedure TForm1.Button1Click(Sender: TObject);
var
umaClasse:TClass;
begin
umaClasse:=GetClass(edit1.Text);
if umaclasse <> nil then
NewControl(umaClasse);
end;

procedure TForm1.NewControl(ClassRef: TClass);
begin
  newObj:=TControlClass(ClassRef).Create(self);
  newObj.Parent:=self;
//colocar Nome
//Atribuir tamanho...
//Atribuir eventos
//posicionr etc...
end;

initialization
registerClass(Tedit);


apesar de não ter lido eu acho que a dica do micheus te atende tb


Responder Citar

29/07/2008

Cabelo

Marcos..

Eu usei exatamente como você passou e funciona perfeitamente desde o ano passado...

var 
  v_componente : TControl; 

procedure TForm1.Button1Click(Sender: TObject); 
var 
v_classe :TClass; 
begin 
v_classe := GetClass(cdsComponente.FieldByName(´C_COMPONENTE´).AsString); 
if v_classe <> nil then 
P_Cria_Componente(v_classe); 
end; 

procedure P_Cria_Componente(l_class : TClass); 
begin 
  v_componente := TControlClass(l_class).Create(Self); 
  v_componente.Parent := Self; 
  P_Configura_Control(v_componente)
end; 

initialization 
registerClass(Tedit);



mas tive que implementar usando RTTI pois o usuário ´configura´ a tela em run - time... na procedure P_Configura_Control(l_componente : TComponent)

A dica do nosso amigo micheus atende, mas deixa fixo as configurações do TCcontrol. Mas uma coisa me ajudou, tive a idéia de criar um vetor dinâmico com oc componentes, ao invés de ficar criando variáveis, deixo tudo num memso lugar e só uso um ponteiro.

Mesmo assim, este tópico foi ´renascido´ esses dias... rssss..

Coloquei este tópico em 2004, hoje já resolví o problema usando como descreví acima..

mesmo assim muito obrigado..


Responder Citar

31/07/2008

Micheus

A dica do nosso amigo micheus atende, mas deixa fixo as configurações do TCcontrol.

[b:aac9d78e97]Cabelo[/b:aac9d78e97], a questão do type-cast para TWinControl está relacionada ao fato de que todos os componentes [b:aac9d78e97]visuais[/b:aac9d78e97] deverão ser descendentes deste.

Como o modo de construir dinamicamente componentes a partir do nome da classe passa pelo uso desta ´formula´:
[color=blue:aac9d78e97][i:aac9d78e97]<variável TComponent> := TComponentClass(<nome da classe>).Create(<owner>);[/i:aac9d78e97][/color:aac9d78e97]

temos que levar em conta que os componentes visuais tem as propriedades de posicionamento e muitas outras que não estão disponíveis na classe TComponent.

Observe ainda, que para generalizar qualquer atribuição que deveria ser feita à Caption (no caso de um TLabel) ou a Text (no caso de um TEdit), fiz uso do método [i:aac9d78e97]SetText[/i:aac9d78e97] que se aplicará a qualquer dos casos, sem que tenhamos que saber quem é quem.

O uso de um vetor constante, foi apenas uma forma de simplificar uma situação em que a lista seria obtida dos registros de de uma tabela.

Abraços


Responder Citar

01/08/2008

Cabelo

É isso mesmo micheus...

Implementei minha solução com a idéia de um vetor do tipo record e ficou perfeito.. está funcionando mais rápido e a codificação ficou muito mais simples..

Obrigado pela sua idéia..

Abs


Responder Citar

01/08/2008

Marco Salles

citação de marcosalles
apesar de não ter lido eu acho que a dica do micheus te atende tb


na verdade passei o olho e vi que tb atendia

Agora quanto a ser mais rápido é claro que sera pq na implementação que o Cabel uzou continha codigo de RTTI ... Ora RTTI so em ultimo caso.

citação de cabelo:
mas tive que implementar [b:8534ca7380]usando RTTI [/b:8534ca7380]pois o usuário ´configura´ a tela em run - time



Responder Citar

04/08/2008

Cabelo

marcos.. desculpe a ignorancia, mas pq somente em último caso o uso de RTTI, para fazer o q faço, utilizar um Object Inspector em RUN Time, que será configurado pelo usuário, com qual tecnologia eu deveria criar o object inspector, só lembrando que os componentes são criados em run-time ?


Responder Citar

05/08/2008

Discorpio

Bom dia a todos.

Se não me falhe a memória, a partir do Delphi 6, o mesmo disponibilizou duas funções que converte a configuração do componente em string e vice-versa, são elas:


  // Converte componente para string
  ObjectBinaryToText(BinStream, StrStream);

  // Converte string para componente.
 ObjectTextToBinary(StrStream, BinStream);



Entretanto estas funções devem ser usadas em conjunto com um TMemoryStream, assim sugiro que voce crie as seguintes Funções.


// Para transformar componente em string.
function TForm1.ComponentToString(Component: TComponent): string;
var
  BinStream:TMemoryStream;
  StrStream: TStringStream;
  s: string;
begin
  BinStream := TMemoryStream.Create;
  try
    StrStream := TStringStream.Create(s);
    try
      BinStream.WriteComponent(Component);
      BinStream.Seek(0, soFromBeginning);
      ObjectBinaryToText(BinStream, StrStream);
      StrStream.Seek(0, soFromBeginning);
      Result:= StrStream.DataString;
    finally
      StrStream.Free;
    end;
  finally
    BinStream.Free
  end;
end;


// Para transformar string em componente.
function TForm1.StringToComponent(Value: string): TComponent;
var
  StrStream:TStringStream;
  BinStream: TMemoryStream;
begin
  StrStream := TStringStream.Create(Value);
  try
    BinStream := TMemoryStream.Create;
    try
      ObjectTextToBinary(StrStream, BinStream);
      BinStream.Seek(0, soFromBeginning);
      Result := BinStream.ReadComponent(nil);
    finally
      BinStream.Free;
    end;
  finally
    StrStream.Free;
  end;
end;



So não esqueça de registrar a classe do componente que voce está convertendo para String ao final da Unit do seu formulário, assim:


procedure TForm1.btnQualquerClick(Sender: TObject);
begin
  .....
  ....
end;

initialization
RegisterClass(TLabel);

end.



Um abraço.


Responder Citar

05/08/2008

Cabelo

Então..

Sua Dica funciona, mas isso se o usuário não tiver que selecionar o componente que ele quer usar, eu disponibilizo os componentes para ele montar a tela como preferir e portanto não posso registrar na unit os componentes, já que não sei quais componentes serão usados.. sem contar que o usuário também vai ´configurar´ o componente selecionado através de um object inspector, como no desenvolvimento em project - time

Correto ??

Ou será que sou eu que não tô entendendo o que vc quis dizer ?

Abs


Responder Citar

30/09/2008

Micheus

[b:796b9f37c0]Cabelo[/b:796b9f37c0], a proposta do colega [b:796b9f37c0]Discorpio[/b:796b9f37c0] seria aplicável em uma situação em que o usuário já desenhou o seu form e vai salvá-lo para depois recuperá-lo. Estas funções permitem fazer exatamente o que o Delphi faz quando grava as definições do form nos arquivos [i:796b9f37c0].dfm[/i:796b9f37c0] e quando restaura ele, a partir do arquivo, para edição. Assim, para gravar, o objeto já deve existir na memória e para carregar ele já deve existir gravado em algum lugar (stream - disco, blob,...).

Talvez vc possa associar as duas coisas para ter um editor mais completo, gravando em um blob o form desenhado e depois recuperando ele de lá, para uso ou edição.

Abraços


Responder Citar