我使用 DBGrid 和 Delphi XE2 来显示一些查询结果。结果中,一些(整数)字段表示以秒为单位的持续时间,我想以某种格式显示,例如“hh:mm:ss”我事先不知道字段顺序,因为查询可能不同,但我知道字段我要格式化的名称。最有效的方法是什么?
2 回答
虽然@AndreaBoc 的回答是正确的(但只回答了您的一个问题),但它有些重复:复制粘贴相同的计算在我看来是一种反模式。
使用http://docwiki.embarcadero.com/Libraries/XE4/en/System.Math.DivMod可以消除重复性
procedure TForm1.PrettyPrintSeconds(Sender: TField;
var Text: string; DisplayText: Boolean);
var
hh,mm,ss: Word;
begin
DivMod( Sender.AsInteger, 3600, hh, mm );
DivMod( mm, 60, mm, ss );
Text := Format('%.2d:%.2d:%.2d',[hh,mm,ss]);
end;
此外,如果您真的想要高效(但为什么要使用TDataset
?),您可以用 3 个临时字符串变量和旧式Str
过程替换过于灵活的 Format 函数,如http://docwiki.embarcadero.com/Libraries/XE4/en中所述/System.SysUtils.IntToStr :-)
这应该为http://docwiki.embarcadero.com/Libraries/XE2/en/Data.DB.TField.OnGetText设置
这比复制粘贴相同的操作两次或更多次要好,这会增加 CPU 时间和做出愚蠢错字的机会。
然后是问题的下一部分:不知道字段顺序...但我知道要格式化的字段名称。这意味着您应该在查询打开后调整此字段:
procedure TDataModule1.Query1AfterOpen(DataSet: TDataSet);
var F: TField;
begin
F := DataSet.FindField('MY-Name-For-Time');
if F = nil
then (* .... no such field - do something about it .... *)
else F.OnGetText := TForm1.PrettyPrintSeconds;
end;
- http://docwiki.embarcadero.com/Libraries/XE2/en/Data.DB.TDataSet.AfterOpen
- http://docwiki.embarcadero.com/Libraries/XE2/en/Data.DB.TDataSet.FindField
- http://docwiki.embarcadero.com/Libraries/XE2/en/Data.DB.TDataSet.FieldByName
类似地,您可以从BeforeClose
事件中移除(设置为)此事件处理程序。nil
只是为了罕见地检查相同的 TField 对象稍后被重新用于不同的目的。除了一些非常奇特的情况外,它不应该发生,而只是为了“清理自己”。
效率较低(从 CPU 的角度来看),但在精神方法上也有更多的 VCL。
对于其AfterOpen
事件中的查询:查找上面的给定字段名称,如果找到则:
- 保存结果以备后用(因为内部的线性字符串搜索
FieldByName
本身并不是最有效的操作,所以不应该对每个显示的值都重复它)- 正确但有点乏味的方法是
TTimeField
为查询所有者创建新属性。 - 在 Win32 下,您可以(ab)使用
Int32
属性TDataSet.Tag
,通过类型转换将指针存储在那里。这是脆弱、不安全和不可移植的代码,但是在任何这些变量中存储Integer
,pointer
和TObject
值有点“旧的 Delphi 风格” (就像在 TStringList.Objects 中一样),如果你小心并且只对 Win32 感兴趣,它就可以工作。
- 正确但有点乏味的方法是
- 添加日期时间类型的计算字段
- 设置
'hh:nn:ss'
为该新字段的格式:http: //docwiki.embarcadero.com/Libraries/XE2/en/Data.DB.TDateTimeField.DisplayFormat - 使新字段对 DBGrid 和其他 db-aware 控件友好
- 使原始字段不可见或从网格中删除其列 - 您将拥有新字段。
然后设置OnCalcFields
查询,如:
// 1. Delphi TDateTime is double and 1 day equals to 1.0
// 2. Multiplication is more efficient than division - do it by pen and paper and see
procedure TDataModule1.Query1AfterOpen(DataSet: TDataSet);
const coeff = 1.0 / ( 24 * 60 * 60 );
begin
MyCalcField.AsTime := MySourceField.AsInteger * coeff;
end;
然后,您将能够在 Delphi 例程期望值的每个地方使用新的虚拟字段TDateTime
。
您可以根据请求使用该字段的 OnGetText 事件:
procedure TForm1.Table1secondsGetText(Sender: TField;
var Text: string; DisplayText: Boolean);
var
seconds,hh,mm,ss:Integer;
begin
seconds := Sender.AsInteger;
hh := seconds div 3600;
mm := (seconds - (hh * 3600)) div 60;
ss := (seconds - (hh * 3600) - (mm * 60));
Text := Format('%.2d:%.2d:%.2d',[hh,mm,ss]);
end;
例如 185 秒将以这种方式显示:00:03:05