0

我使用 DBGrid 和 Delphi XE2 来显示一些查询结果。结果中,一些(整数)字段表示以秒为单位的持续时间,我想以某种格式显示,例如“hh:mm:ss”我事先不知道字段顺序,因为查询可能不同,但我知道字段我要格式化的名称。最有效的方法是什么?

4

2 回答 2

3

虽然@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;

类似地,您可以从BeforeClose事件中移除(设置为)此事件处理程序。nil只是为了罕见地检查相同的 TField 对象稍后被重新用于不同的目的。除了一些非常奇特的情况外,它不应该发生,而只是为了“清理自己”。


效率较低(从 CPU 的角度来看),但在精神方法上也有更多的 VCL。

对于其AfterOpen事件中的查询:查找上面的给定字段名称,如果找到则:

  1. 保存结果以备后用(因为内部的线性字符串搜索FieldByName本身并不是最有效的操作,所以不应该对每个显示的值都重复它)
    • 正确但有点乏味的方法是TTimeField为查询所有者创建新属性。
    • 在 Win32 下,您可以(ab)使用Int32属性TDataSet.Tag,通过类型转换将指针存储在那里。这是脆弱、不安全和不可移植的代码,但是在任何这些变量中存储Integer,pointerTObject值有点“旧的 Delphi 风格” (就像在 TStringList.Objects 中一样),如果你小心并且只对 Win32 感兴趣,它就可以工作。
  2. 添加日期时间类型的计算字段
  3. 设置'hh:nn:ss'为该新字段的格式:http: //docwiki.embarcadero.com/Libraries/XE2/en/Data.DB.TDateTimeField.DisplayFormat
  4. 使新字段对 DBGrid 和其他 db-aware 控件友好
  5. 使原始字段不可见或从网格中删除其列 - 您将拥有新字段。

然后设置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

于 2013-06-26T21:23:12.730 回答
1

您可以根据请求使用该字段的 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

于 2013-06-26T18:59:04.893 回答