1

Delphi / Lazarus FreePascalコレクションからグリッド(StringGridまたはKGrid)を更新する最も効率的な方法を見つけようとしています。

私のコレクションの1つは以下のとおりです。

{ TEntretien }
TEntretien = class(TCollectionItem)
private
  { private declarations }
  FPrenom: string;
  FSexe: string;
  FSigneDistinctif: string;
  FPays: string;
  FTotale: integer;
  FColumns: integer;
public
  { public declarations }
published
  { published declarations }
  property Prenom: string read FPrenom write FPrenom;
  property Sexe: string read FSexe write FSexe;
  property SigneDistinctif: string read FSigneDistinctif write FSigneDistinctif;
  property Pays: string read FPays write FPays;
property Totale: integer read FTotale write FTotale;
end;

{ TEntretiens }
TEntretiens = class(TCollection)
private
  { private declarations }
  function GetItem(AIndex: integer): TEntretien;
public
  { public declarations }
  constructor Create;
  function Add: TEntretien;
  property Items[AIndex: integer]: TEntretien read GetItem; default;
end;

グリッドの1つを更新するために使用する次のコードスニペットがあります。

// Fill the grid with the results of the query
for intGridRow := 0 to intNumberOfRows - 1 do
begin
  for intGridCol := 0 to intNumberOfColumns - 1 do
  begin
    // Write the rest of the retrieved data into the grid proper   USE RTTI HERE??
    if intGridCol = 0 then
      kgGridName.Cells[intGridCol + kgGridName.FixedCols, intGridRow + kgGridName.FixedRows] := 
         AEntretiens[intGridRow].Prenom
    else if intGridCol = 1 then
       kgGridName.Cells[intGridCol + kgGridName.FixedCols, intGridRow + kgGridName.FixedRows] :=
         AEntretiens[intGridRow].Sexe
    else if intGridCol = 2 then
       kgGridName.Cells[intGridCol + kgGridName.FixedCols, intGridRow + kgGridName.FixedRows] :=
         AEntretiens[intGridRow].SigneDistinctif
    else if intGridCol = 3 then
       kgGridName.Cells[intGridCol + kgGridName.FixedCols, intGridRow + kgGridName.FixedRows] :=
         AEntretiens[intGridRow].Pays
    else if intGridCol = 4 then
       kgGridName.Cells[intGridCol + kgGridName.FixedCols, intGridRow + kgGridName.FixedRows] := IntToStr(AEntretiens[intGridRow].Totale)
    end;
end;

これは、フィールド/プロパティの数が少ないコレクションには問題ありませんが、最大40のフィールドを持つコレクションもあるため、上記で使用する方法は面倒です。

これを行うためのより効率的な方法はありますか?誰かがRTTIを提案しましたが、使用方法がわかりません。

どうもありがとう、

Jダニエル

4

1 に答える 1

3

Delphi 拡張 RTTI 実装を次に示します。これは Delphi-2010 から有効です。

読み取り可能なすべての公開済みプロパティを (宣言された順序で) 検索し、グリッドに値を入力します。

プロパティとして整数と文字列を超える場合は、case ステートメントにさらに追加します。

uses
  System.Classes,System.RTTI,System.TypInfo, System.SysUtils;

procedure Test;
// Fill the grid with the results of the query
Var
  AnItem     : TEntretien;
  fixedCols  : integer;
  fixedRows  : integer;
  ARow,ACol  : integer;
  intGridRow : integer;
  context    : TRttiContext;
  rType      : TRttiType;
  prop       : TRttiProperty;
  value      : TValue;
  s          : String;
begin
  context := TRttiContext.Create;
  rType := context.GetType(TEntretien);
  fixedCols := kgGridName.FixedCols;
  fixedRows := kgGridName.FixedRows;
  for intGridRow := 0 to intNumberOfRows - 1 do
  begin
    AnItem := AEntretiens[intGridRow];
    ARow := intGridRow + fixedRows;
    ACol := fixedCols;
    for prop in rType.GetProperties do
    begin
      if prop.IsReadable then
      begin
        s := '';
        value := prop.GetValue(AnItem);
        case prop.PropertyType.TypeKind of
          tkInteger : s := IntToStr(value.AsInteger);
          tkString : s := value.AsString;
        end;
        kgGridName.Cells[ACol, ARow] := s;
        Inc(ACol);
      end;
    end;
  end;
end;

Ken のコメントで述べたように、拡張 RTTI は Lazarus/FreePascal には実装されていません。

すべてのプラットフォームの一般的な解決策は、プロパティ値を取得できるコレクション アイテムの基本クラスを追加することです。

Type
  TBaseItemClass = class(TCollectionItem)
    private
      function GetPropertyCount : integer; virtual; abstract;  
      function GetPropertyString( index : integer) : string; virtual; abstract;
    public
      property PropertyCount : integer read GetPropertyCount;
      property PropertyString[index : integer] : string read GetPropertyString;
  end;

次に、宣言と実装は次のようになります。

{ TEntretien }
TEntretien = class(TBaseItemClass)
private
  { private declarations }
  FPrenom: string;
  FSexe: string;
  FSigneDistinctif: string;
  FPays: string;
  FTotale: integer;
  FColumns: integer;
  function GetPropertyCount : integer; override;
  function GetPropertyString( index : integer) : string; override;
public
  { public declarations }
published
  { published declarations }
  property Prenom: string read FPrenom write FPrenom;
  property Sexe: string read FSexe write FSexe;
  property SigneDistinctif: string read FSigneDistinctif write FSigneDistinctif;
  property Pays: string read FPays write FPays;
  property Totale: integer read FTotale write FTotale;
end;

function TEntretien.GetPropertyCount : integer;
begin
  Result := 5;
end;

function TEntretien.GetPropertyString(index : integer) : string;
begin
  Result := '';
  case index of
    0 : Result := Prenom;
    1 : Result := Sexe;
    2 : Result := SigneDistinctif;
    3 : Result := Pays;
    4 : Result := IntToStr(Totale);
  end;
end;

procedure Test1;
// Fill the grid with the results of the query
Var
  AnItem    : TEntretien;
  intGridRow,intNumberOfRows : Integer;
  fixedCols : integer;
  fixedRows : integer;
  ARow      : integer;
  i         : integer;
begin
  fixedCols := kgGridName.FixedCols;
  fixedRows := kgGridName.FixedRows;
  for intGridRow := 0 to intNumberOfRows - 1 do
  begin
    AnItem := AEntretiens[intGridRow];
    ARow := intGridRow + FixedRows;
    for i := 0 to AnItem.PropertyCount - 1 do
    begin
      kgGridName.Cells[i + FixedCols, ARow] := AnItem.PropertyString[i];
    end;
  end;
end;

TEntretien クラスに GetPropertyCount と GetPropertyString の実装部分を入力するだけです。

この方法は、すべてのプロパティ値を手動でコーディングする必要があるため、例と同じくらい非効率に見えるかもしれません。しかし、私の例を実行することは、プログラミングの 2 つの基本原則に準拠しています。

  • 繰り返さないでください。塗りつぶすグリッドが多数ある場合、コードを多くの場所で繰り返す必要があります。これで、Collection アイテム内で一度だけ宣言されました。アイテムを再設計する必要がある場合は、2 つの関数を更新するだけです。
  • 範囲を限定してください。プログラムの GUI 部分は、コレクション項目クラスについて可能な限り認識しないようにする必要があります。
于 2012-05-24T22:17:58.190 に答える