要約:
TFormと2つのパネルがあるとします。パネルはalTopとalClientに配置されます。alClientパネルにはTPaintBoxが含まれており、そのOnPaintには描画コードが含まれます。
コンポーネントのDoubleBufferedのデフォルト値はfalseです。
描画プロセス中、フォーム、パネルはすべて背景をペイントするため、ちらつきは明らかです。
フォームはパネルで覆われているため、WM_ERASEBKGNDメッセージを傍受することはおそらく問題ありません。そうでない場合は、フォームが背景をペイントするため、フォームのサイズが変更されると、パネルでちらつきが見られ、パネルの右端でちらつきが見られます。
次に、alTopパネルは一部のボタンのコンテナとして使用することを目的としているため、DoubleBufferedをtrueに設定して、Delphiにちらつきがないことを確認するのはおそらく問題ありません。おそらくパフォーマンスの負担はそれほど大きくないでしょう。
第3に、alClientパネルは別の図面コンポーネントのコンテナとしてのみ意図されているため、このパネルは最終的な図面の作成に関与していない可能性があります。この点で、標準のTPanelの代わりにTPanelの子孫を使用するのはおそらく良いことです。このTPanelの子孫では、保護されたプロシージャPaintをオーバーライドし、プロシージャ内では何もしません。特に、基本クラスTCustomPanel.PaintのFillRect呼び出しを回避するために、継承された呼び出しは行いません。さらに、WM_ERASEBKGNDメッセージを傍受し、内部では何もしません。これは、TPanel.ParentBackgroundがFalseの場合、Delphiが背景の再描画を担当し、Trueの場合、ThemeServiceが担当するためです。
最後に、TPaintBoxでちらつきなしでペイントするには、次のようにします。
(1)VCL組み込みの描画ルーチンを使用する場合は、おそらく次の方が適切です...
(2)OpenGLを使用し、OpenGLのダブルバッファを有効にします。
(3)..。
=== Q:TPaintBoxの右端のちらつきをなくす方法は?===
1つのTFormに対して、2つのパネルがあるとします。一番上のものはフォームに対してalTopに配置され、ボタンのコンテナと見なされます。もう1つは、フォームに対してalClientに配置され、描画コンポーネント(VCLのTPaintBoxやGraphics32のTPaintBox32など)のコンテナーと見なされます。後者のパネルでは、そのWM_ERASEBKGNDメッセージが傍受されます。
ここで、次のサンプルコードでTPaintBoxインスタンスを使用します。OnPaintハンドラーでは、ちらつきがないと予想される描画を描画するための2つの選択肢があります。選択肢1は、長方形を埋めた後に描画します。親パネルは背景を消去してはならないため、描画はちらつきがないようにする必要があります。選択肢2はTBitmapに描画し、そのCanvasはペイントボックスにコピーされます。
ただし、両方の選択肢がちらつき、2番目の選択肢が特にちらつきます。私の主な関心事は選択1に関するものです。フォームのサイズを変更すると、ちらつきの主要部分が右端で発生していることがわかります。なぜこれが起こるのですか?誰かが理由と可能な解決策についてコメントするのを手伝ってもらえますか?(ここでTPaintBoxの代わりにTPaintBox32を使用すると、右端がまったくちらつきません。)
私の2番目の懸念は、選択肢1を使用すると、ちらつきのごく一部がペイントボックスでランダムに発生することです。あまり明白ではありませんが、フォームのサイズをすばやく変更すると、それでも観察できます。さらに、選択肢2を使用すると、この種のちらつきはさらに深刻になります。その理由はわかりませんでした。考えられる理由と解決策について誰かがコメントするのを手伝ってもらえますか?
どんな提案でも大歓迎です!!
unit uMainForm;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
ExtCtrls, Dialogs;
type
TMainForm = class(TForm)
procedure FormCreate(Sender: TObject);
private
{ Private declarations }
FPnlCtrl, FPnlScene: TPanel;
FPbScene: TPaintBox;
OldPnlWndProc: TWndMethod;
procedure PnlWndProc(var Message: TMessage);
procedure OnScenePaint(Sender: TObject);
public
{ Public declarations }
end;
var
MainForm: TMainForm;
implementation
{$R *.dfm}
procedure TMainForm.FormCreate(Sender: TObject);
begin
Self.Color := clYellow;
Self.DoubleBuffered := False;
FPnlCtrl := TPanel.Create(Self);
FPnlCtrl.Parent := Self;
FPnlCtrl.Align := alTop;
FPnlCtrl.Color := clPurple;
FPnlCtrl.ParentColor := False;
FPnlCtrl.ParentBackground := False;
FPnlCtrl.FullRepaint := False;
FPnlCtrl.DoubleBuffered := False;
FPnlScene := TPanel.Create(Self);
FPnlScene.Parent := Self;
FPnlScene.Align := alClient;
FPnlScene.Color := clBlue;
FPnlScene.ParentColor := False;
FPnlScene.ParentBackground := False;
FPnlScene.FullRepaint := False;
FPnlScene.DoubleBuffered := False;
FPbScene := TPaintBox.Create(Self);
FPbScene.Parent := FPnlScene;
FPbScene.Align := alClient;
FPbScene.Color := clRed;
FPbScene.ParentColor := False;
//
OldPnlWndProc := Self.FPnlScene.WindowProc;
Self.FPnlScene.WindowProc := Self.PnlWndProc;
FPbScene.OnPaint := Self.OnScenePaint;
end;
procedure TMainForm.PnlWndProc(var Message: TMessage);
begin
if (Message.Msg = WM_ERASEBKGND) then
Message.Result := 1
else
OldPnlWndProc(Message);
end;
procedure TMainForm.OnScenePaint(Sender: TObject);
var
tmpSceneBMP: TBitmap;
begin
// Choice 1
FPbScene.Canvas.FillRect(FPbScene.ClientRect);
FPbScene.Canvas.Ellipse(50, 50, 150, 150);
// Choice 2
// tmpSceneBMP := TBitmap.Create;
// tmpSceneBMP.Width := FPbScene.ClientWidth;
// tmpSceneBMP.Height := FPbScene.ClientHeight;
// tmpSceneBMP.Canvas.Brush.Color := FPbScene.Color;
// tmpSceneBMP.Canvas.FillRect(FPbScene.ClientRect);
// tmpSceneBMP.Canvas.Ellipse(50, 50, 150, 150);
// FPbScene.Canvas.CopyRect(FPbScene.ClientRect, tmpSceneBMP.Canvas,
// FPbScene.ClientRect);
end;
end.
=== Q:パネルの背景の再描画を正しくインターセプトするにはどうすればよいですか?===
(別の質問でこれを尋ねる必要がある場合は、そのように言ってください。これを削除します。)
新しいVCLアプリケーションを作成し、サンプルコードを貼り付け、FormCreateをアタッチし、デバッグを実行します。フォームの上にマウスを置くと、パネルが背景をはっきりと塗り直していることがわかります。ただし、サンプルコードに示されているように、WM_ERASEBKGNDメッセージをインターセプトすることで、この動作をインターセプトする必要があります。
この3行をコメントアウトすると、
FPnlScene.Color := clBlue;
FPnlScene.ParentColor := False;
FPnlScene.ParentBackground := False;
次に、WM_ERASEBKGNDメッセージをキャプチャできます。この違いについてはわかりません。
この動作の理由と、WM_ERASEBKGNDメッセージを正しくインターセプトする方法(ParentBackground:= Falseの場合)についてコメントするのに役立つ人がいますか?
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
ExtCtrls, Dialogs;
type
TForm1 = class(TForm)
procedure FormCreate(Sender: TObject);
private
{ Private declarations }
FPnlScene: TPanel;
FPbScene: TPaintBox;
FOldPnlWndProc: TWndMethod;
procedure PnlWndProc(var Message: TMessage);
procedure OnSceneMouseMove(Sender: TObject; Shift: TShiftState;
X, Y: Integer);
procedure OnScenePaint(Sender: TObject);
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.FormCreate(Sender: TObject);
begin
Self.Color := clYellow;
Self.DoubleBuffered := False;
FPnlScene := TPanel.Create(Self);
FPnlScene.Parent := Self;
FPnlScene.Align := alClient;
FPnlScene.Color := clBlue;
FPnlScene.ParentColor := False;
FPnlScene.ParentBackground := False;
FPnlScene.FullRepaint := False;
FPnlScene.DoubleBuffered := False;
FPbScene := TPaintBox.Create(Self);
FPbScene.Parent := FPnlScene;
FPbScene.Align := alClient;
FPbScene.Color := clRed;
FPbScene.ParentColor := False;
//
FOldPnlWndProc := Self.FPnlScene.WindowProc;
Self.FPnlScene.WindowProc := Self.PnlWndProc;
Self.FPbScene.OnMouseMove := Self.OnSceneMouseMove;
Self.FPbScene.OnPaint := Self.OnScenePaint;
end;
procedure TForm1.PnlWndProc(var Message: TMessage);
begin
if Message.Msg = WM_ERASEBKGND then
begin
OutputDebugStringW('WM_ERASEBKGND');
Message.Result := 1;
end
else
FOldPnlWndProc(Message);
end;
procedure TForm1.OnSceneMouseMove(Sender: TObject; Shift: TShiftState; X,
Y: Integer);
begin
FPbScene.Repaint;
end;
procedure TForm1.OnScenePaint(Sender: TObject);
begin
FPbScene.Canvas.FillRect(FPbScene.ClientRect);
FPbScene.Canvas.Ellipse(50, 50, 150, 150);
end;
end.