Как устранить мерцание на правом краю TPaintBox (например, при изменении размера)
уплотнения:
Скажите, что у меня есть TForm и две панели. Панели выровняны alTop и alClient. Панель alClient содержит TPaintBox, OnPaint которого включает коды рисования.
значение по умолчанию DoubleBuffered для компонентов равно false.
во время процесса рисования мерцание очевидно, потому что форма, панели все рисуют свой фон.
потому что форма покрыта панелями, вероятно, хорошо перехватить его WM_ERASEBKGND сообщение. Если нет, можно увидеть мерцание на панелях и мерцание на правом краю панелей при изменении размера формы, потому что форма рисует ее фон.
во-вторых, поскольку панель alTop предназначена для контейнера для некоторых кнопок, вероятно, хорошо установить ее DoubleBuffered в true, чтобы позволить Delphi убедиться, что на ней нет мерцания. Это, вероятно, не приведет к большой нагрузке на производительность.
В-третьих, потому что alClient панель предназначена только для контейнера для другого компонента чертежа, эта панель, скорее всего,не участвует в составлении финального розыгрыша. В этом отношении, вероятно, хорошо использовать потомок TPanel вместо стандартной TPanel. В этом потоке TPanel переопределите защищенную процедуру Paint и ничего не делайте внутри процедуры, особенно унаследованный вызов, чтобы избежать вызова FillRect в базовом классе TCustomPanel.Краска. Кроме того, перехватить wm_erasebkgnd, за сообщение и ничего не делать внутри. Это потому, что когда TPanel.ParentBackground является ложным, Delphi отвечает за перекраску фона, а когда это правда, themeservice несет ответственность.
наконец, рисовать без мерцания в TPaintBox:
(1) используя встроенные процедуры рисования VCL, это, вероятно, лучше...
(2) Использование OpenGL с включенным двойным буфером OpenGL.
(3) ...
===Q: Как устранить мерцание справа край TPaintBox?===
предположим,что для одной TForm у меня есть две панели. Верхний выравнивается по высоте относительно формы и рассматривается как контейнер для кнопок. Другой выравнивается относительно формы и рассматривается как контейнер для рисования компонентов (например, TPaintBox из VCL или TPaintBox32 из Graphics32). Для последней панели перехватывается сообщение WM_ERASEBKGND.
Теперь, я использую экземпляр TPaintBox в следующем пример кода. В своем обработчике OnPaint у меня есть два варианта нарисовать рисунок, который, как я ожидаю, будет без мерцания. Выбор 1-рисование после заполнения прямой кишки. Поскольку родительская панель не должна стирать фон, рисунок не должен мерцать. Выбор 2-это рисование на TBitmap, холст которого затем копируется обратно в paintbox.
однако оба варианта мерцают, и 2-й выбор особенно мерцает. Моя главная забота касается выбора 1. Если вы измените размер форма, вы могли видеть, что основная часть мерцания происходит на правом краю. Почему это происходит? Может ли кто-нибудь помочь прокомментировать причину и возможное решение? (Обратите внимание, если я использую TPaintBox32 вместо TPaintBox здесь, правый край не будет мерцать вообще.)
моя вторичная озабоченность заключается в том, что при использовании выбора 1 незначительная часть мерцания происходит на paintbox случайным образом. Это не очень очевидно, но все же заметно, если вы быстро измените размер формы. Кроме того, при использовании выбор 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.
Примечание, если я прокомментирую эти три строки,
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.
2 ответов
обычная техника-играть с формой.Дублбуфер, который, как я вижу, вы уже делаете в коде, так что если бы это было так просто, я бы подумал,что вы бы уже решили его.
Я думаю, что можно также, возможно, избежать любой операции в OnPaint, кроме растяжки-рисовать прямо на ваш paintbox.Холст, с вашего экранного растрового изображения. Все остальное в OnPaint является потенциально вызывающей мерцание ошибкой. Это означает, что нет модификации TBitmap из OnPaint. Позволять я говорю это в третий раз; не меняйте состояние в событиях paint. События Paint должны содержать операцию "bitmap-blit", прямоугольник GDI и вызовы линий и т. д., Но ничего больше.
Я не решаюсь рекомендовать кому-либо, чтобы они экспериментировали с WM_SETREDRAW, но это один метод, который люди используют. Вы можете поймать события или сообщения окна перемещения/изменения размера и включить/выключить WM_SETREDRAW, но это настолько чревато осложнениями и проблемами, что я не рекомендую его. Также вы можете позвонить различных с Win32 функции для блокировки окна, и все это очень опасно и не рекомендуется.
для чего это стоит, следующее не мерцает для меня:
unit uMainForm;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, ExtCtrls, Dialogs;
type
TMainForm = class(TForm)
procedure FormCreate(Sender: TObject);
private
FPnlCtrl, FPnlScene: TPanel;
FPbScene: TPaintBox;
procedure OnScenePaint(Sender: TObject);
end;
implementation
{$R *.dfm}
procedure TMainForm.FormCreate(Sender: TObject);
begin
Self.Color := clYellow;
FPnlCtrl := TPanel.Create(Self);
FPnlCtrl.Parent := Self;
FPnlCtrl.Align := alTop;
FPnlCtrl.Color := clPurple;
FPnlScene := TPanel.Create(Self);
FPnlScene.Parent := Self;
FPnlScene.Align := alClient;
FPnlScene.Color := clBlue;
FPbScene := TPaintBox.Create(Self);
FPbScene.Parent := FPnlScene;
FPbScene.Align := alClient;
FPbScene.Color := clRed;
FPbScene.OnPaint := Self.OnScenePaint;
end;
procedure TMainForm.OnScenePaint(Sender: TObject);
begin
FPbScene.Canvas.FillRect(FPbScene.ClientRect);
FPbScene.Canvas.Ellipse(50, 50, 150, 150);
end;
end.