Delphi XE2 で DBGrid を使用して、いくつかのクエリ結果を表示します。結果の一部の(整数)フィールドは、「hh:mm:ss」のような形式で表示したい秒単位の期間を表しますクエリが異なる可能性があるため、フィールドの順序は事前にわかりませんが、フィールドは知っていますフォーマットしたい名前。それを行う最も効率的な方法は何ですか?
2 に答える
@AndreaBoc による回答は正しいですが (ただし、質問の 1 つにしか回答しません)、やや反復的です。同じ計算をコピーして貼り付けることは、私にはアンチパターンに見えます。
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;
さらに、本当に効率的になりたい場合は (しかし、なぜ ? を使用するのか)、柔軟すぎる Format 関数を 3 つの一時文字列変数と、 http://docwiki.embarcadero.com/Libraries/XE4/enTDataset
で概説されているような古いスタイルのStr
手順に置き換えることができます。 /System.SysUtils.IntToStr :-)
これは、http://docwiki.embarcadero.com/Libraries/XE2/en/Data.DB.TField.OnGetTextに設定する必要があります。
これは、同じ操作を 2 回以上コピーして貼り付けるよりも優れており、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
、クエリの所有者のために新しいプロパティを作成することです。 Int32
Win32 では、 propertyを (ab) 使用TDataSet.Tag
して、型キャストを介してポインタを格納することができます。これは壊れやすく、安全でなく、移植性のないコードですが、これらの変数のいずれかにInteger
,pointer
および値を格納することは、 ( TStringList.Objects のように)TObject
やや「古い Delphi スタイル」であり、注意して Win32 のみに関心がある場合は機能します。
- 適切ではあるがやや退屈な方法は
- 日時型の計算フィールドを追加する
'hh:nn:ss'
その新しいフィールドのフォーマットとして設定: http://docwiki.embarcadero.com/Libraries/XE2/en/Data.DB.TDateTimeField.DisplayFormat- 新しいフィールドを DBGrid やその他の db 対応コントロールに適したものにします
- 元のフィールドを非表示にするか、その列をグリッドから削除します - 代わりに新しいフィールドがあります。
次に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