Fórum Rotina para preenchimento dos controles com as propriedades das classes #1835

12/01/2009

0

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;
Alexandre Neto

Alexandre Neto

Responder

Posts

13/01/2009

Wesley Yamazack

Alexandre, estaremos verificando esta rotina, assim que obtivermos o resultado entramos em contato.

Att,
Wesley Yamazack
Responder

Gostei + 0

14/01/2009

Wesley Yamazack

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
Responder

Gostei + 0

14/01/2009

Alexandre Neto

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

Gostei + 0

14/01/2009

Alexandre Neto

Complementando a resposta anterior, uso Delphi 7.   []s Alexandre Amaral.
Responder

Gostei + 0

14/01/2009

Wesley Yamazack

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
Responder

Gostei + 0

14/01/2009

Alexandre Neto

Valeu Wesley, show de bola.   Obrigado e até a próxima!   []s Alexandre Amaral.
Responder

Gostei + 0

14/01/2009

Wesley Yamazack

Alexandre, boa sorte em seus projetos. Se precisar estamos ai.

Abraço

Wesley Yamazack
Responder

Gostei + 0

Utilizamos cookies para fornecer uma melhor experiência para nossos usuários, consulte nossa política de privacidade.

Aceitar