9

1 秒に 2 回更新/更新されるカスタム プログレス バーがフォーム上にあり、ちらつきます。

TMyProgressBar = class(TCustomControl)

といくつかのイベントTCustomControlが必要だったので、 からコントロールを継承しました。コントロール (最大 64 項目) は動的に作成され、ScrollBox に配置されます。進行状況が更新されたら、最初に を呼び出します。HandleTWinControlInvalidateRect

すべての描画作業 (四角形のセットなど - hereDrawTextから着想を得たもの) は、メモリ DC で実行され、コントロールの DC で実行されます。とにかくちらつきます。コンポーネントが消えて再び現れるようです。私見は、背景の消去が原因です。BitBlt

このちらつきのない描画アドバイスWM_ERASEBKGNDでは、次の方法で処理するように書かれています。

type
  TMyProgressBar = class(TCustomControl)
    procedure WMEraseBkGnd(var Message:TMessage); message WM_ERASEBKGND;

procedure TMyProgressBar.WMEraseBkGnd(var Message: TMessage);
begin
  Message.Result := 1;
end;

しかし、別のコンポーネントでは、TMS ( TAdvProgressBar)によって、同じメッセージに対してResultが設定されます。0

現在、Windows のドキュメントには次のように記載されています。

背景を消去する場合、アプリケーションはゼロ以外を返す必要があります。それ以外の場合は、ゼロを返す必要があります。

両方のバリアント (結果 = 0、1) をテストしましたが、驚いたことに、どちらもちらつきを回避しました。

では、Delphi コードに何を入れればよいでしょうか。正しい方法は何ですか?

4

3 に答える 3

8

それは問題ではありません。重要なのは、 を呼び出さない限りinherited、デフォルトのウィンドウ プロシージャは背景を消去しないということです。コントロールの表面全体をペイントしているため、デフォルトの処理は必要ありません。

'0' または '1' ('0' ではない) を返す場合の変更点は、BeginPaintが呼び出されると、それに応じてシステムが のfEraseメンバーを設定することPAINTSTRUCTです。「0」を返すと「True」に設定され、ペイント プロセスで背景を消去する必要があることを示します。「1」の場合は「False」に設定され、消去が不要であることを示します。BeginPaintで呼び出されTWinControl.PaintHandlerます。fEraseVCLはデバイスコンテキストの戻り値のみを使用するため、何が返されても違いはありませBeginPaintん。

それでも、消去が処理されたことを概念的にほのめかして、「1」を返します。

于 2013-11-05T20:02:53.757 に答える
4

背景が (完全に) 消去されていない場合は 0 を返す必要があり、背景が消去されたと見なされる場合は別の値を返し、0 を返す必要があります。これは、あなたが守らなければならない慣習です。

さらに重要なことは、inheritedこのメッセージ ハンドラー内で呼び出さないことです1)。これにより、継承されたメッセージ ハンドラーが呼び出され、最終的には、作成時にウィンドウに与えられたブラシを使用してデバイス コンテキストをペイントする既定の Windows プロシージャが呼び出されます (存在する場合)。

実際には、特にこのカスタム コントロールの例では、消去タスクを実行しているのは自分だけなので、どの値を返すかは問題ではありません。ただし、基本コントロール クラスまたはコントロールの展開を設計することを検討してください。バックグラウンドが完全に検証されていないことをコントロールの子孫またはユーザーに示したい場合があります。というMessage.Result = 0ことです。Message.Result = ebLeftSideまた、コントロールの現在の状態では左側のみ (それが意味するものは何でも) が「消去」されていることを示す返信を返すこともできます。

この文脈での「消去」は「描く」ことも意味しますが、それは問題外だと思います。


1) Inherited仮想メソッドと比較して、メッセージハンドラーの動作は少し異なります。同じことを意味しますが (継承チェーンの最初のハンドラーが呼び出されます)、その宣言にはオーバーライド ディレクティブがなく、メソッド名を追加することはできません。

于 2013-11-05T21:06:02.117 に答える
2

の戻り値は、後続のハンドラーで呼び出したときにのメンバーがWM_ERASEBKNDどのように初期化されるかを決定します。ペイント ハンドラーがそのメンバーを無視する場合、何が返されるかは問題ではありません。fErasePAINTSTRUCTBeginPaintWM_PAINTWM_ERASEBKND

ちらつきは、2 回ではなく 1 回ペイントすることで回避されます。で領域を色で塗りつぶし、WM_ERASEBKGNDしばらくして でその領域をブリットすると、WM_PAINTちらつきが発生します。でペイントせずWM_ERASEBKGNDにブリットするだけWM_PAINTなら、ちらつきはありません。唯一の秘訣は、ブリットが無効化された領域全体を初期化されたピクセルでカバーするようにすることです。

于 2013-11-06T00:09:19.507 に答える