Migaro. 技術Tips

                       

ミガロ. 製品の技術情報
IBMiの活用に役立つ情報を掲載!


【Delphi】画面の描画停止による処理高速化テクニック

ご利用いただいているDelphi/400のシステムにおいては、
処理速度向上のために様々な施策をされているかと存じます。

処理完了までに時間がかかる要因といえば、
画像の読み込みや大量データの読み書きなどと共に
「連続処理における画面のちらつき」があるかと思います。

今回は既に組み込み済みのお客様も多いかとは思いますが、
処理中のみ描画を停止することでレスポンスを向上する方法を改めて紹介いたします。
 


DBGrid編

TDBGridやTDBCtrlGridなど、DBと連携している明細については
紐づくデータセットで『DisableControls』『EnableControls』メソッドを使用すると
描画を止めることが可能です。
DisableControlsで描画停止、EnableControlsで描画再開となります。

明細の1行目から最終行までカーソルが動きながら処理が行われるような場合に
カレントレコードが動く描画を止めることで、そこに費やされるリソースが不要になるため
処理速度の向上が期待でき、見た目の煩雑さも低減されます。

procedure TForm1.Button1Click(Sender: TObject);
var
  iRecNo: Integer;
begin
  iRecNo  := cdsList.RecNo;  // データセット(今回はTClientDataSet)現在の行番号を記憶
  cdsList.DisableControls;   // 描画停止
  try
    cdsList.First;
    while not cdsList.Eof do
    begin

      //【集計やエラーチェックなど、行単位で行う処理をここに書く】

      cdsList.Next;
    end;

  finally
    cdsList.RecNo := iRecNo; // 記憶した行番号に再セットする
    cdsList.EnableControls;  // 描画再開
  end;
end;

注意点としては、データセットは内部でDisableCountというプロパティを持っており
『DisableControlsで1加算』『EnableControlsで1減算』という処理が行われます。
そのため、DisableControlsが複数回かからないように考慮する必要があります。
(複数回かかった場合は、同数のEnableControlsを行うと描画再開となります。)
 


その他のコンポーネント編

Windows APIのLockWindowUpdate関数を使うことで、
指定したウィンドウでの描画を無効または有効に設定できます。
参考:LockWindowUpdate 関数 (winuser.h) | Microsoft Learn

以下のようにLockWindowUpdateの引数に描画停止したい
コンポーネント(またはフォーム)のハンドルを渡して描画を停止します。
描画再開する場合は、LockWindowUpdateの引数に0を渡します。

先ほどのデータセットの例と異なり、複数回実行した場合は
最後に実行されたLockWindowUpdateが正となります。

  // 画面描画の停止
  LockWindowUpdate(Self.Handle);
  try

    //【画面描画が多く使用される処理をここに書く】

  finally
    // 画面描画再開
    LockWindowUpdate(0);
    // 即時再描画が必要な場合はSelf.Repaintを行う
  end;

(応用)TStringListの活用

こちらは画面描画とは直接は無関係ですが、
画面描画を抑えることで処理速度向上を期待できるパターンです。

TMemo(Linesプロパティ)やTComboBox(Itemsプロパティ)等、
画面表示のためのプロパティにTStringsが使用されている場合、
画面内で多数の行に対して直接項目を編集すると、
その経過を都度描画し続けることになり、処理速度に影響します。

こうしたTMemo.LinesTComboBox.Itemsの多数の行に対して
処理を行う場合は、以下のように内部変数(TStringList)を経由することで
画面に描画される手間がなくなり、処理速度が向上します。

{*******************************************************************************
 例: TMemo 各行単位のトリム
*******************************************************************************}
procedure TfrmMEMO.N12Click(Sender: TObject);
var
  i: Integer;
  sl: TStringList;
begin
  if Application.MessageBox('各行単位でトリムします。',
                            PCHAR(Application.Title), 52) = mrYes then
  begin
    sl := TStringList.Create;
    try
      sl.Text := Memo.Lines.Text; // 内容をまとめてTStringListに移動
      for i := 0 to (sl.Count - 1) do
      begin
        sl[i] := Trim(sl[i]);     // Trim処理をTStringListの中で行う
      end;
      Memo.Lines.Text := sl.Text; // 内容を元のTMemoに戻す
    finally
      FreeAndNil(sl);
    end;
  end;
end;