我正在尝试找到从 Delphi/Lazarus FreePascal 集合更新网格(StringGrid 或 KGrid)的最有效方法。
我的收藏之一如下:
{ 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;
我有以下代码片段,用于更新我的一个网格:
// 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,但我不知道如何使用它。
非常感谢,
丹尼尔
最佳答案
这是一个 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 在评论中提到的,Lazarus/FreePascal 中未实现增强型 RTTI。
适用于所有平台的通用解决方案是添加一个集合项基类,并可以获取属性值。
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 实现部分即可。
这种方法可能看起来和您的示例一样效率低下,因为必须手动编码所有属性值。但按照我的示例进行操作符合编程中的两个基本原则:
- 不要重复自己的话。如果要填充许多网格,则必须在许多地方重复该代码。现在它在 Collection 项目中一劳永逸地声明。如果您重新设计该项目,只需更新两个功能即可。
- 限制范围。程序的 GUI 部分应该尽可能少地了解集合项类。
关于delphi - 有效地将 delphi/freepascal 集合复制到网格,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10744405/