Array
(
)

Converter String para TComponent??????

Cabelo
   - 30 ago 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


Fieldztime
   - 25 jul 2008

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.


Micheus
   - 27 jul 2008


Citação:
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:
#Código

...
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] 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].ClassName);
if PClass <> nil then
begin
LWinControl := TComponentClass(PClass).Create(LForm);
TWinControl(LWinControl).Parent := LForm;
TWinControl(LWinControl).Name := Format(´CompName¬d´, [CompNum]);
TWinControl(LWinControl).Left := CompInfoList[Idx].Left;
TWinControl(LWinControl).Top := CompInfoList[Idx].Top;
TWinControl(LWinControl).Width := CompInfoList[Idx].Width;
TWinControl(LWinControl).Height := CompInfoList[Idx].Height;
TWinControl(LWinControl).SetTextBuf(PChar(CompInfoList[Idx].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 GetClass, é necessário que vc registre as classes desejadas em um statement initialization, como fiz no exemplo.

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

Abraços


Cabelo
   - 28 jul 2008

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.


Marco Salles
   - 28 jul 2008

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

#Código

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


Cabelo
   - 29 jul 2008

Marcos..

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

#Código

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..


Micheus
   - 31 jul 2008


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

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

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

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 SetText 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


Cabelo
   - 01 ago 2008

É 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


Marco Salles
   - 01 ago 2008

citação de marcosalles

Citação:
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:

Citação:
mas tive que implementar usando RTTI pois o usuário ´configura´ a tela em run - time



Cabelo
   - 04 ago 2008

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 ?


Discorpio
   - 05 ago 2008

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:

#Código



// 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.

#Código


// 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:

#Código


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

initialization
RegisterClass(TLabel);

end.



Um abraço.


Cabelo
   - 05 ago 2008

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


Micheus
   - 30 set 2008

Cabelo, a proposta do colega Discorpio 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 .dfm 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