Delphi 10.3.3 FireDAC:如何在DBGrid中继承上一行值填充缺失字段
解决Delphi FireDAC DBGrid继承上一行字段值的问题
嗨,Franck,针对你遇到的问题,核心思路是维护一个全局缓存来记录各列的最新值,每次加载行数据时先更新缓存,再用缓存填充当前行的空字段。下面是具体的实现步骤和代码示例:
1. 定义缓存变量
首先在窗体类中添加一个私有数组,用来保存C0、C1、C2的最新值,作为后续行的继承来源:
type TForm1 = class(TForm) FDConnection1: TFDConnection; FDQuery1: TFDQuery; DBGrid1: TDBGrid; DataSource1: TDataSource; procedure FDQuery1BeforeOpen(DataSet: TDataSet); procedure FDQuery1CalcFields(DataSet: TDataSet); private FCachedValues: array[0..2] of string; // 对应C0、C1、C2的缓存值 procedure ParseData(const DataStr: string); // 解析data字段的辅助函数 public { Public declarations } end;
2. 在BeforeOpen事件初始化缓存并创建计算字段
在FDQuery打开前,初始化缓存为空,并动态创建C0、C1、C2三个计算字段:
procedure TForm1.FDQuery1BeforeOpen(DataSet: TDataSet); var i: Integer; begin // 初始化缓存为空白 FillChar(FCachedValues, SizeOf(FCachedValues), 0); // 动态添加C0-C2计算字段 for i := 0 to 2 do begin with TStringField.Create(DataSet) do begin FieldName := 'C' + IntToStr(i); Size := 20; Calculated := True; DataSet.FieldDefs.Add(FieldName, ftString, 20); DataSet.Fields.Add(Self); end; end; end;
3. 实现data字段解析函数
写一个辅助函数,解析当前行的data字段内容,更新缓存中对应索引的值:
procedure TForm1.ParseData(const DataStr: string); var Parts, KeyValue: TStringList; i, Index: Integer; begin if Trim(DataStr) = '' then Exit; // 空data字段直接跳过 Parts := TStringList.Create; KeyValue := TStringList.Create; try Parts.Delimiter := ';'; Parts.StrictDelimiter := True; Parts.DelimitedText := DataStr; KeyValue.Delimiter := '='; for i := 0 to Parts.Count - 1 do begin KeyValue.DelimitedText := Parts[i]; if (KeyValue.Count = 2) and TryStrToInt(KeyValue[0], Index) then begin // 仅更新有效索引(0-2)的缓存值 if (Index >= 0) and (Index <= 2) then FCachedValues[Index] := KeyValue[1]; end; end; finally KeyValue.Free; Parts.Free; end; end;
4. 在CalcFields事件填充字段值
利用OnCalcFields事件(每行数据加载后触发),先解析当前行的data更新缓存,再将缓存值赋值给对应的计算字段:
procedure TForm1.FDQuery1CalcFields(DataSet: TDataSet); var i: Integer; DataStr: string; begin // 获取当前行的data字段内容 DataStr := DataSet.FieldByName('data').AsString; // 解析data,更新全局缓存 ParseData(DataStr); // 将缓存值赋值给C0-C2字段 for i := 0 to 2 do begin DataSet.FieldByName('C' + IntToStr(i)).AsString := FCachedValues[i]; end; end;
关键说明
- 为什么不用
OnCreateFields?这个事件仅在字段创建时触发一次,无法针对每行数据做处理,也就拿不到上一行的内容。而OnCalcFields是每行数据加载后都会触发,刚好适合维护行与行之间的状态缓存。 - 缓存是全局的,会随着行的遍历持续更新,确保后续行能继承最新的字段值。
- 如果你的列数更多,可以把数组改成动态列表(比如
TStringList),方便扩展。
内容的提问来源于stack exchange,提问作者franckcl
相关产品推荐
相关产品推荐

