Rotina para preenchimento dos controles com as propriedades das classes
Olá Consultores,
Gostaria da ajuda de vocês para desenvolver uma rotina (função/procedure/método) para preencher os controles de tela (Edit/ListBox/Memo/DataEdit) com as respectivas propriedades das classes.
Atualmente estou desenvolvendo a rotina abaixo, mas não sei se essa abordagem é indicada, portanto gostaria de contar com o suporte de vocês para solucionar esta situação.
[]s
Alexandre Amaral.
{---------------------------------------------------------------------}
{ preencher controles com os valores das propriedades }
{ da classe passada por parametro }
{---------------------------------------------------------------------}
class procedure TClasseViewBase.PreencherControles(Obj: TObject);
var
Count, Size, I: Integer;
List: PPropList;
PropInfo: PPropInfo;
Controle: TComponent;
begin
{ Contar quantas propriedade o objeto possui }
Count := GetPropList(Obj.ClassInfo, tkAny, nil); { Apontar para o Endereço de memória }
Size := Count * SizeOf(Pointer);
GetMem(List, Size); try
{ Contar quantas propriedades o objeto }
{ possui passando os dados para a Lista }
Count := GetPropList(Obj.ClassInfo, tkAny, List); { Varrer as propridades do objeto }
for I := 0 to Count - 1 do
begin
PropInfo := List^[I]; { procurar pelo controle correspondente a propriedade } { que neste caso terá o mesmo nome dela } with Screen.ActiveForm do
Controle := FindComponent( PropInfo^.Name ); if (Controle is TDateEdit) then
TDateEdit(Controle).Date := GetPropValue(Obj, PropInfo^.Name)
else if (Controle is TCalcEdit) then
TCalcEdit(Controle).Value := GetPropValue(Obj, PropInfo^.Name)
else if (Controle is TListBox) then
begin
{ aqui está a minha dúvida, quando a propriedade é um TCollection } { onde são armazenadas várias strings. Como preencher o listbox } { com essas strings }
end
else if (Controle is TTimeEdit) then
TTimeEdit(Controle).Hora := VarToDateTime(GetPropValue(Obj, PropInfo^.Name))
else if (Controle is TCustomEdit) then
TCustomEdit(Controle).Text := VarToStr(GetPropValue(Obj, PropInfo^.Name))
else if (Controle is TCheckBox) then
TCheckBox(Controle).Checked := GetPropValue(Obj, PropInfo^.Name); end; finally
{ Libero a memória }
FreeMem(List);
end; end;
var
Count, Size, I: Integer;
List: PPropList;
PropInfo: PPropInfo;
Controle: TComponent;
begin
{ Contar quantas propriedade o objeto possui }
Count := GetPropList(Obj.ClassInfo, tkAny, nil); { Apontar para o Endereço de memória }
Size := Count * SizeOf(Pointer);
GetMem(List, Size); try
{ Contar quantas propriedades o objeto }
{ possui passando os dados para a Lista }
Count := GetPropList(Obj.ClassInfo, tkAny, List); { Varrer as propridades do objeto }
for I := 0 to Count - 1 do
begin
PropInfo := List^[I]; { procurar pelo controle correspondente a propriedade } { que neste caso terá o mesmo nome dela } with Screen.ActiveForm do
Controle := FindComponent( PropInfo^.Name ); if (Controle is TDateEdit) then
TDateEdit(Controle).Date := GetPropValue(Obj, PropInfo^.Name)
else if (Controle is TCalcEdit) then
TCalcEdit(Controle).Value := GetPropValue(Obj, PropInfo^.Name)
else if (Controle is TListBox) then
begin
{ aqui está a minha dúvida, quando a propriedade é um TCollection } { onde são armazenadas várias strings. Como preencher o listbox } { com essas strings }
end
else if (Controle is TTimeEdit) then
TTimeEdit(Controle).Hora := VarToDateTime(GetPropValue(Obj, PropInfo^.Name))
else if (Controle is TCustomEdit) then
TCustomEdit(Controle).Text := VarToStr(GetPropValue(Obj, PropInfo^.Name))
else if (Controle is TCheckBox) then
TCheckBox(Controle).Checked := GetPropValue(Obj, PropInfo^.Name); end; finally
{ Libero a memória }
FreeMem(List);
end; end;
Alexandre Neto
Curtidas 0
Respostas
Wesley Yamazack
12/01/2009
Alexandre, estaremos verificando esta rotina, assim que obtivermos o resultado entramos em contato.
Att,
Wesley Yamazack
Att,
Wesley Yamazack
GOSTEI 0
Wesley Yamazack
12/01/2009
Olá Alexandre,
Muito interessante seu método, a abordagem que está utilizando é interessante pois aumenta o reuso através do RTTi, eu inseri alguns comentários com sugestões na execução de alguns métodos para otimizar o código.
Quanto ao Collection tenho a seguinte observação:
- Caso você sempre vá armazenar nesta collection uma série de Strings, recomendo o uso do TStringList ao invés do TCollection, fiz a implementação do método para preencher o ListBox a partir do StringList.
- Caso você vá armazenar na collection, objetos de diferentes classes, então preciso saber qual a versão do Delphi em que estás desenvolvendo, pois se for no Delphi 2007 poderemos usar uma abordagem baseada em uma Collection com Generics, porém se for em versões anteriores teremos que criar uma interface ou uma classe abstrata do qual descenderão todas as suas CollectionItems, pois precisaremos que as CollectionItems compartilhem de um comportamento comum para serem lidos e executados em seu método.
{---------------------------------------------------------------------}
{ preencher controles com os valores das propriedades }
{ da classe passada por parametro }
{---------------------------------------------------------------------}
class procedure TClasseViewBase.PreencherControles(Obj: TObject);
var
Count, Size, I, X: Integer;
List: PPropList;
PropInfo: PPropInfo;
Controle: TComponent;
listaValores:TStringList;
begin
{ Contar quantas propriedade o objeto possui }
Count := GetPropList(Obj.ClassInfo, tkAny, nil);
{ Apontar para o Endereço de memória }
Size := Count * SizeOf(Pointer);
GetMem(List, Size);
try
{ Contar quantas propriedades o objeto }
{ possui passando os dados para a Lista }
(* SUGESTÃO - Recomendo o uso do método GetPropList(Obj, List) pois já faz
a alocação de memória necessária para List, com isso, as três linhas acima poderiam
ser removidas *)
Count := GetPropList(Obj.ClassInfo, tkAny, List);
{ Varrer as propridades do objeto }
for I := 0 to Count - 1 do
begin
PropInfo := List^[I];
{ procurar pelo controle correspondente a propriedade }
{ que neste caso terá o mesmo nome dela }
with Screen.ActiveForm do
Controle := FindComponent( PropInfo^.Name );
if (Controle is TDateEdit) then
TDateEdit(Controle).Date := GetPropValue(Obj, PropInfo^.Name)
else if (Controle is TCalcEdit) then
TCalcEdit(Controle).Value := GetPropValue(Obj, PropInfo^.Name)
else if (Controle is TListBox) then
begin
{ aqui está a minha dúvida, quando a propriedade é um TCollection }
{ onde são armazenadas várias strings. Como preencher o listbox }
{ com essas strings }
{ GetObjectProp(Obj, PropInfo^.Name) -> Este método retorna a referência do objeto apontado pela property }
if GetObjectProp(Obj, PropInfo^.Name) is TStringList then
begin
{ Limpamos o listBox para não acumular os dados }
TListBox(Controle).Items.Clear;
{ Retorna para ListaValores a stringList que está na property }
listaValores := TStringList(GetObjectProp(Obj, PropInfo^.Name));
{ percorre a lista de valores da property e insere no ListBox }
for x:=0 to Pred(listaValores.Count) do
begin
TListBox(Controle).Items.Add(listaValores[x]);
end;
end
else if GetObjectProp(Obj, PropInfo^.Name) is TCollection then
begin
{ Caso precise armazenar em collections de diversos objetos, poderemos usar Collection com generics no caso do delphi 2007 ou teremos que criar uma classe abstrata herdando de TCollectionItem em versões anteriores }
end;
end
else if (Controle is TTimeEdit) then
TTimeEdit(Controle).Hora := VarToDateTime(GetPropValue(Obj, PropInfo^.Name))
else if (Controle is TCustomEdit) then
TCustomEdit(Controle).Text := VarToStr(GetPropValue(Obj, PropInfo^.Name))
else if (Controle is TCheckBox) then
TCheckBox(Controle).Checked := GetPropValue(Obj, PropInfo^.Name);
end;
finally
{ Libero a memória }
FreeMem(List);
end;
end;
Att,
Wesley Yamazack
Muito interessante seu método, a abordagem que está utilizando é interessante pois aumenta o reuso através do RTTi, eu inseri alguns comentários com sugestões na execução de alguns métodos para otimizar o código.
Quanto ao Collection tenho a seguinte observação:
- Caso você sempre vá armazenar nesta collection uma série de Strings, recomendo o uso do TStringList ao invés do TCollection, fiz a implementação do método para preencher o ListBox a partir do StringList.
- Caso você vá armazenar na collection, objetos de diferentes classes, então preciso saber qual a versão do Delphi em que estás desenvolvendo, pois se for no Delphi 2007 poderemos usar uma abordagem baseada em uma Collection com Generics, porém se for em versões anteriores teremos que criar uma interface ou uma classe abstrata do qual descenderão todas as suas CollectionItems, pois precisaremos que as CollectionItems compartilhem de um comportamento comum para serem lidos e executados em seu método.
{---------------------------------------------------------------------}
{ preencher controles com os valores das propriedades }
{ da classe passada por parametro }
{---------------------------------------------------------------------}
class procedure TClasseViewBase.PreencherControles(Obj: TObject);
var
Count, Size, I, X: Integer;
List: PPropList;
PropInfo: PPropInfo;
Controle: TComponent;
listaValores:TStringList;
begin
{ Contar quantas propriedade o objeto possui }
Count := GetPropList(Obj.ClassInfo, tkAny, nil);
{ Apontar para o Endereço de memória }
Size := Count * SizeOf(Pointer);
GetMem(List, Size);
try
{ Contar quantas propriedades o objeto }
{ possui passando os dados para a Lista }
(* SUGESTÃO - Recomendo o uso do método GetPropList(Obj, List) pois já faz
a alocação de memória necessária para List, com isso, as três linhas acima poderiam
ser removidas *)
Count := GetPropList(Obj.ClassInfo, tkAny, List);
{ Varrer as propridades do objeto }
for I := 0 to Count - 1 do
begin
PropInfo := List^[I];
{ procurar pelo controle correspondente a propriedade }
{ que neste caso terá o mesmo nome dela }
with Screen.ActiveForm do
Controle := FindComponent( PropInfo^.Name );
if (Controle is TDateEdit) then
TDateEdit(Controle).Date := GetPropValue(Obj, PropInfo^.Name)
else if (Controle is TCalcEdit) then
TCalcEdit(Controle).Value := GetPropValue(Obj, PropInfo^.Name)
else if (Controle is TListBox) then
begin
{ aqui está a minha dúvida, quando a propriedade é um TCollection }
{ onde são armazenadas várias strings. Como preencher o listbox }
{ com essas strings }
{ GetObjectProp(Obj, PropInfo^.Name) -> Este método retorna a referência do objeto apontado pela property }
if GetObjectProp(Obj, PropInfo^.Name) is TStringList then
begin
{ Limpamos o listBox para não acumular os dados }
TListBox(Controle).Items.Clear;
{ Retorna para ListaValores a stringList que está na property }
listaValores := TStringList(GetObjectProp(Obj, PropInfo^.Name));
{ percorre a lista de valores da property e insere no ListBox }
for x:=0 to Pred(listaValores.Count) do
begin
TListBox(Controle).Items.Add(listaValores[x]);
end;
end
else if GetObjectProp(Obj, PropInfo^.Name) is TCollection then
begin
{ Caso precise armazenar em collections de diversos objetos, poderemos usar Collection com generics no caso do delphi 2007 ou teremos que criar uma classe abstrata herdando de TCollectionItem em versões anteriores }
end;
end
else if (Controle is TTimeEdit) then
TTimeEdit(Controle).Hora := VarToDateTime(GetPropValue(Obj, PropInfo^.Name))
else if (Controle is TCustomEdit) then
TCustomEdit(Controle).Text := VarToStr(GetPropValue(Obj, PropInfo^.Name))
else if (Controle is TCheckBox) then
TCheckBox(Controle).Checked := GetPropValue(Obj, PropInfo^.Name);
end;
finally
{ Libero a memória }
FreeMem(List);
end;
end;
Att,
Wesley Yamazack
GOSTEI 0
Alexandre Neto
12/01/2009
Olá Wesley,
Gostei das suas observações, interessantes.
Com relação ao TCollection é o seguinte, eu modelei meu banco de dados assim:
Ex.:
Tabela Cliente (ID, NOME, etc)
Tabela Endereços (ID, RUA, BAIRRO, etc)
Tabela Fornecedor (ID, NOME, etc)
Tabela Usuarios (ID, NOME, etc)
Tabela Telefones (ID, FONE)
Tabela Emails (ID, EMAIL)
na situacao em questão, a tabela Telefones assim como Emails, guarda os dados de Clientes, Funcionários, Fornecedores tudo junto, identificando-os pelo ID único. E como cada indivíduo pode ter mais de um telefone eu faço uso do collection para armazenar ID + FONE.
Então quando quero fazer uma consulta, eu passo apenas o ID e ela preenche as classes com o conteúdo das respectivas tabelas e a Procedure que estamos desenvolvendo preenche os controles de tela.
TFone = class(TCollectionItem)
private
FFONE : string;
function GetFONE: string;
procedure SetFONE(const Value: string);
public
constructor Create(Collection: TCollection); reintroduce; virtual;
destructor Destroy; override;
published
property ELEMENTO: string read GetFONE write SetFONE;
end; TFones = class(TCollection)
private
function GetFones(index: integer): TFone;
procedure SetFones(index: integer; const Value: TFone);
public
function Add: TFone;
property Items[index: integer]: TFone read GetFones write SetFones;
end; TContatoFones = class(TPersistent)
private
FID : integer;
FFones : TFones;
function GetID: integer;
procedure SetID(const Value: integer);
public
constructor Create;
destructor Destroy; override;
published
property ID : integer read GetID write SetID;
property FONES: TFones read FFones write FFones;
end;
[]s Alexandre Amaral.
private
FFONE : string;
function GetFONE: string;
procedure SetFONE(const Value: string);
public
constructor Create(Collection: TCollection); reintroduce; virtual;
destructor Destroy; override;
published
property ELEMENTO: string read GetFONE write SetFONE;
end; TFones = class(TCollection)
private
function GetFones(index: integer): TFone;
procedure SetFones(index: integer; const Value: TFone);
public
function Add: TFone;
property Items[index: integer]: TFone read GetFones write SetFones;
end; TContatoFones = class(TPersistent)
private
FID : integer;
FFones : TFones;
function GetID: integer;
procedure SetID(const Value: integer);
public
constructor Create;
destructor Destroy; override;
published
property ID : integer read GetID write SetID;
property FONES: TFones read FFones write FFones;
end;
[]s Alexandre Amaral.
GOSTEI 0
Alexandre Neto
12/01/2009
Complementando a resposta anterior, uso Delphi 7.
[]s
Alexandre Amaral.
GOSTEI 0
Wesley Yamazack
12/01/2009
Olá, segue continuação da ajuda, espero tenha sido o mais claro possível.
Como estamos trabalhando no Delphi 7, não poderemos usar Generics pois ainda não há nesta versão. Teremos que criar uma classe base para as classes associativas, como por exemplo TContatoFones, que faz a ligação entre Cliente e seus respectivos telefones.
Esta classe base, no qual chamei de "TCollectionObjects", terá dois métodos, um para retornar a descrição
que é o valor a ser exibido na tela e outro para retornar a collection manipulada pela classe. Isso é para verificarmos a quantidade de elementos na Collection e caso precise acessar os mesmos posteriormente
Estes métodos serão abstratos na classe base e serão implementados nas classes filhas, como por exemplo "TContatoFones" a implementação os mesmos está no aquivo, assim como alguns comentários para facilitar o entendimento.
{ Teremos que ter uma classe comum para ser chamada no método preencherControles }
{ Com isso as suas classes associativas herdariam desta classe base }
TCollectionObjects = class(TPersistent)
{ Este Método retornará a descrição desejada para o Objeto }
{ Com isso, ganha-se flexibilidade, pois hoje temos apenas a informação de telefone
porém pode-se ter também outros atributos como DDD por exemplo }
function getDescription(index:integer):String;virtual;abstract;
{ Este método retornará a collection manipulada pela classe concreta }
function getCollection:TCollection;virtual;abstract;
end;
{ A classe associativa passa a herdar de sua classe associativa Base
e a sobrescrever os métodos da mesma }
TContatoFones = class(TCollectionObjects)
{Abaixo estão os métodos sobrescritos na classe TContatoFones}
function getDescription(index:integer):String;override;
function getCollection:TCollection;override;
function TContatoFones.getDescription(index: integer): String;
begin
result := FFones.Items[index].ELEMENTO;
end;
function TContatoFones.getCollection: TCollection;
begin
result := FONES;
end;
{ No método preencherControles, declaramos outra variável do tipo de nossa classe base }
CollectionObj:TCollectionObjects;
{A implementação da rotina de preenchimento do controle com collection ficaria assim}
{Aqui verificamos se o atributo herda da classe associativa base TCollectionObjects}
if GetObjectProp(Obj, PropInfo^.Name).InheritsFrom(TCollectionObjects) then
begin
{ Limpamos o listBox para não acumular os dados }
TListBox(Controle).Items.Clear;
{ Retorna para ListaValores a stringList que está na property }
CollectionObj := TCollectionObjects(GetObjectProp(Obj, PropInfo^.Name));
{ percorre a collection}
for x:=0 to Pred(CollectionObj.getCollection.Count) do
begin
{Insere na listBox os valores dos objetos da collection}
TListBox(Controle).Items.Add(CollectionObj.getDescription(x));
end;
end;
======================================================================
Att,
Wesley Yamazack
Como estamos trabalhando no Delphi 7, não poderemos usar Generics pois ainda não há nesta versão. Teremos que criar uma classe base para as classes associativas, como por exemplo TContatoFones, que faz a ligação entre Cliente e seus respectivos telefones.
Esta classe base, no qual chamei de "TCollectionObjects", terá dois métodos, um para retornar a descrição
que é o valor a ser exibido na tela e outro para retornar a collection manipulada pela classe. Isso é para verificarmos a quantidade de elementos na Collection e caso precise acessar os mesmos posteriormente
Estes métodos serão abstratos na classe base e serão implementados nas classes filhas, como por exemplo "TContatoFones" a implementação os mesmos está no aquivo, assim como alguns comentários para facilitar o entendimento.
{ Teremos que ter uma classe comum para ser chamada no método preencherControles }
{ Com isso as suas classes associativas herdariam desta classe base }
TCollectionObjects = class(TPersistent)
{ Este Método retornará a descrição desejada para o Objeto }
{ Com isso, ganha-se flexibilidade, pois hoje temos apenas a informação de telefone
porém pode-se ter também outros atributos como DDD por exemplo }
function getDescription(index:integer):String;virtual;abstract;
{ Este método retornará a collection manipulada pela classe concreta }
function getCollection:TCollection;virtual;abstract;
end;
{ A classe associativa passa a herdar de sua classe associativa Base
e a sobrescrever os métodos da mesma }
TContatoFones = class(TCollectionObjects)
{Abaixo estão os métodos sobrescritos na classe TContatoFones}
function getDescription(index:integer):String;override;
function getCollection:TCollection;override;
function TContatoFones.getDescription(index: integer): String;
begin
result := FFones.Items[index].ELEMENTO;
end;
function TContatoFones.getCollection: TCollection;
begin
result := FONES;
end;
{ No método preencherControles, declaramos outra variável do tipo de nossa classe base }
CollectionObj:TCollectionObjects;
{A implementação da rotina de preenchimento do controle com collection ficaria assim}
{Aqui verificamos se o atributo herda da classe associativa base TCollectionObjects}
if GetObjectProp(Obj, PropInfo^.Name).InheritsFrom(TCollectionObjects) then
begin
{ Limpamos o listBox para não acumular os dados }
TListBox(Controle).Items.Clear;
{ Retorna para ListaValores a stringList que está na property }
CollectionObj := TCollectionObjects(GetObjectProp(Obj, PropInfo^.Name));
{ percorre a collection}
for x:=0 to Pred(CollectionObj.getCollection.Count) do
begin
{Insere na listBox os valores dos objetos da collection}
TListBox(Controle).Items.Add(CollectionObj.getDescription(x));
end;
end;
======================================================================
Att,
Wesley Yamazack
GOSTEI 0
Alexandre Neto
12/01/2009
Valeu Wesley, show de bola.
Obrigado e até a próxima!
[]s
Alexandre Amaral.
GOSTEI 0
Wesley Yamazack
12/01/2009
Alexandre, boa sorte em seus projetos. Se precisar estamos ai.
Abraço
Wesley Yamazack
Abraço
Wesley Yamazack
GOSTEI 0