使用 VCL-styles 时,滚动条会导致丑陋的 Window 更新 - 解决方法?
Scrollbar causes ugly Window update when using VCL-styles - workaround?
Delphi 版本 10.3.3(社区)。
下面是一个显示不良行为的表单单元。它只有一个按钮和一个滚动框。如果在表单的项目中启用了任何 VCL-style,并且 window 很大,比如最大化,则滚动条滚动会导致 window 的更新延迟,看起来很难看。当使用 mouse-wheel 时,一切都很好。此外,当从滚动框的 StyleElements 中删除 seBorder 时,该行为就消失了。
我看到有人抱怨闪烁的帖子,我想这个错误已经被报告过了。
有人知道如何解决这个问题吗?
当我查看源代码时,只见树木不见森林:)。
编辑:我在 Listbox-View 中将滚动框设置为原来的 3 倍宽,那样效果更明显。
这里有两张截图,第一张来自test-app,第二张来自我的真实应用程序,其中绘画稍微复杂一些。
unit UStoryTest;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants,
System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.ExtCtrls, Vcl.StdCtrls;
type
TfrmSTest = class(TForm)
Scroller: TScrollBox;
Button1: TButton;
procedure FormCreate(Sender: TObject);
procedure PaintBoxPaint(Sender: TObject);
procedure Button1Click(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure FormShow(Sender: TObject);
procedure ScrollerMouseWheel(Sender: TObject; Shift: TShiftState;
WheelDelta: Integer; MousePos: TPoint; var Handled: Boolean);
private
{ Private declarations }
Picturelist, Colorlist: TList;
ScrollerSize: integer;
Procedure DisplayStoryBoard;
procedure DisplayListbox;
public
{ Public declarations }
end;
var
frmSTest: TfrmSTest;
implementation
{$R *.dfm}
procedure TfrmSTest.Button1Click(Sender: TObject);
begin
if Scroller.Align = alRight then
DisplayStoryBoard
else
DisplayListbox;
end;
procedure TfrmSTest.DisplayStoryBoard;
var
i, x, dx: integer;
aP: TPaintbox;
begin
for i := 0 to Picturelist.Count - 1 do
TControl(Picturelist[i]).Parent := nil;
Scroller.Align := alNone;
Scroller.Height := MulDiv(ScrollerSize,Monitor.PixelsPerInch,96);
Scroller.VertScrollBar.Visible := false;
Scroller.HorzScrollBar.Visible := true;
Scroller.AutoScroll := true;
Scroller.HorzScrollBar.Tracking := true;
Scroller.Align := alBottom;
dx := 10;
x := dx - Scroller.HorzScrollBar.Position;
Scroller.DisableAlign;
for i := 0 to Picturelist.Count - 1 do
begin
aP := TPaintbox(Picturelist[i]);
aP.Parent := Scroller;
aP.SetBounds(x, dx, aP.Width, aP.Height);
x := x + aP.Width + dx;
end;
Scroller.EnableAlign;
Scroller.Invalidate;
end;
procedure TfrmSTest.DisplayListbox;
var
i, x, y, dx: integer;
aP: TPaintbox;
begin
for i := 0 to Picturelist.Count - 1 do
TControl(Picturelist[i]).Parent := nil;
Scroller.Align := alNone;
Scroller.Width := MulDiv(3*ScrollerSize,Monitor.PixelsPerInch,96);
Scroller.HorzScrollBar.Visible := false;
Scroller.VertScrollBar.Visible := true;
Scroller.AutoScroll := true;
Scroller.VertScrollBar.Tracking := true;
Scroller.Align := alRight;
dx := 10;
y := dx - Scroller.VertScrollBar.Position;
Scroller.DisableAlign;
x := dx;
for i := 0 to Picturelist.Count - 1 do
begin
aP := TPaintbox(Picturelist[i]);
aP.Parent := Scroller;
aP.SetBounds(x, y, aP.Width, aP.Height);
x := x + aP.Width + dx;
if x + aP.Width > Scroller.Width then
begin
x := dx;
y := y + aP.Height + dx
end;
end;
Scroller.EnableAlign;
Scroller.Invalidate;
end;
procedure TfrmSTest.FormCreate(Sender: TObject);
var
i: integer;
aP: TPaintbox;
begin
Picturelist := TList.Create;
Colorlist := TList.Create;
ScrollerSize:=200;
for i := 0 to 120 do
begin
aP := TPaintbox.Create(self);
aP.Height := ScrollerSize - 40;
aP.Width := aP.Height;
aP.OnPaint := PaintBoxPaint;
aP.Tag := i;
Picturelist.Add(aP);
Colorlist.Add(Pointer(RGB(random(255), random(255), random(255))));
end;
end;
procedure TfrmSTest.FormDestroy(Sender: TObject);
begin
Picturelist.Free;
Colorlist.Free;
end;
procedure TfrmSTest.FormShow(Sender: TObject);
begin
DisplayStoryBoard;
end;
procedure TfrmSTest.PaintBoxPaint(Sender: TObject);
var
aP: TPaintbox;
begin
if Sender is TPaintbox then
begin
aP := TPaintbox(Sender);
aP.Canvas.Brush.Color := TColor(Colorlist[aP.Tag]);
aP.Canvas.Pen.Color := clLime;
aP.Canvas.Rectangle(aP.ClientRect);
aP.Canvas.Font.Color := clWhite;
aP.Canvas.Font.Style := [fsBold];
aP.Canvas.TextOut(3, 3, IntToStr(aP.Tag));
end;
end;
procedure TfrmSTest.ScrollerMouseWheel(Sender: TObject; Shift: TShiftState;
WheelDelta: Integer; MousePos: TPoint; var Handled: Boolean);
var SB: TControlScrollbar;
begin
if (Scroller.Align=alBottom) then
SB:=Scroller.HorzScrollBar
else
SB:=Scroller.VertScrollBar;
SB.Position:=SB.Position-WheelDelta;
Handled:=true;
end;
initialization
ReportMemoryLeaksOnShutDown := true;
end.
为了让事情更舒服一点,这里的表格是:
object frmSTest: TfrmSTest
Left = 0
Top = 0
Caption = 'frmSTest'
ClientHeight = 291
ClientWidth = 505
Color = clBtnFace
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -11
Font.Name = 'Tahoma'
Font.Style = []
OldCreateOrder = False
OnCreate = FormCreate
OnDestroy = FormDestroy
OnShow = FormShow
PixelsPerInch = 96
TextHeight = 13
object Scroller: TScrollBox
Left = 0
Top = 98
Width = 505
Height = 193
Align = alBottom
DoubleBuffered = False
ParentDoubleBuffered = False
TabOrder = 0
OnMouseWheel = ScrollerMouseWheel
ExplicitLeft = 2
end
object Button1: TButton
Left = 8
Top = 8
Width = 75
Height = 25
Caption = 'Button1'
TabOrder = 1
OnClick = Button1Click
end
end
好像没有其他人有这个问题。我找到了一个修复程序,虽然不完美,但好多了,所以为了完整起见,这里是我的答案。对所有 space 表示抱歉,感谢所有花时间思考这个问题的人。
通过调用 inherited 和 update 创建一个处理 WM_VScroll 和 WM_HScroll 的 TScrollbox 后代。
不作废,不重绘。
可以在表单的
OnCreate,父级设置为窗体。切换滚动选项,也许你会看到不同。
interface
type
TScrolloption = (soVCL, soNew);
TStyleScroller = class(TScrollbox)
private
protected
procedure WMVScroll(var Msg: TWMVScroll); message WM_VSCROLL;
procedure WMHScroll(var Msg: TWMHScroll); message WM_HSCROLL;
public
Scrolloption: TScrolloption;
Constructor Create(AOwner: TComponent); override;
end;
implementation
{ TStyleScroller }
constructor TStyleScroller.Create(AOwner: TComponent);
begin
inherited;
Scrolloption := soNew;
end;
procedure TStyleScroller.WMHScroll(var Msg: TWMHScroll);
begin
inherited;
if Scrolloption = soNew then
update;
end;
procedure TStyleScroller.WMVScroll(var Msg: TWMVScroll);
begin
inherited;
if Scrolloption = soNew then
update;
end;
end.
Delphi 版本 10.3.3(社区)。
下面是一个显示不良行为的表单单元。它只有一个按钮和一个滚动框。如果在表单的项目中启用了任何 VCL-style,并且 window 很大,比如最大化,则滚动条滚动会导致 window 的更新延迟,看起来很难看。当使用 mouse-wheel 时,一切都很好。此外,当从滚动框的 StyleElements 中删除 seBorder 时,该行为就消失了。 我看到有人抱怨闪烁的帖子,我想这个错误已经被报告过了。
有人知道如何解决这个问题吗? 当我查看源代码时,只见树木不见森林:)。
编辑:我在 Listbox-View 中将滚动框设置为原来的 3 倍宽,那样效果更明显。 这里有两张截图,第一张来自test-app,第二张来自我的真实应用程序,其中绘画稍微复杂一些。
unit UStoryTest;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants,
System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.ExtCtrls, Vcl.StdCtrls;
type
TfrmSTest = class(TForm)
Scroller: TScrollBox;
Button1: TButton;
procedure FormCreate(Sender: TObject);
procedure PaintBoxPaint(Sender: TObject);
procedure Button1Click(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure FormShow(Sender: TObject);
procedure ScrollerMouseWheel(Sender: TObject; Shift: TShiftState;
WheelDelta: Integer; MousePos: TPoint; var Handled: Boolean);
private
{ Private declarations }
Picturelist, Colorlist: TList;
ScrollerSize: integer;
Procedure DisplayStoryBoard;
procedure DisplayListbox;
public
{ Public declarations }
end;
var
frmSTest: TfrmSTest;
implementation
{$R *.dfm}
procedure TfrmSTest.Button1Click(Sender: TObject);
begin
if Scroller.Align = alRight then
DisplayStoryBoard
else
DisplayListbox;
end;
procedure TfrmSTest.DisplayStoryBoard;
var
i, x, dx: integer;
aP: TPaintbox;
begin
for i := 0 to Picturelist.Count - 1 do
TControl(Picturelist[i]).Parent := nil;
Scroller.Align := alNone;
Scroller.Height := MulDiv(ScrollerSize,Monitor.PixelsPerInch,96);
Scroller.VertScrollBar.Visible := false;
Scroller.HorzScrollBar.Visible := true;
Scroller.AutoScroll := true;
Scroller.HorzScrollBar.Tracking := true;
Scroller.Align := alBottom;
dx := 10;
x := dx - Scroller.HorzScrollBar.Position;
Scroller.DisableAlign;
for i := 0 to Picturelist.Count - 1 do
begin
aP := TPaintbox(Picturelist[i]);
aP.Parent := Scroller;
aP.SetBounds(x, dx, aP.Width, aP.Height);
x := x + aP.Width + dx;
end;
Scroller.EnableAlign;
Scroller.Invalidate;
end;
procedure TfrmSTest.DisplayListbox;
var
i, x, y, dx: integer;
aP: TPaintbox;
begin
for i := 0 to Picturelist.Count - 1 do
TControl(Picturelist[i]).Parent := nil;
Scroller.Align := alNone;
Scroller.Width := MulDiv(3*ScrollerSize,Monitor.PixelsPerInch,96);
Scroller.HorzScrollBar.Visible := false;
Scroller.VertScrollBar.Visible := true;
Scroller.AutoScroll := true;
Scroller.VertScrollBar.Tracking := true;
Scroller.Align := alRight;
dx := 10;
y := dx - Scroller.VertScrollBar.Position;
Scroller.DisableAlign;
x := dx;
for i := 0 to Picturelist.Count - 1 do
begin
aP := TPaintbox(Picturelist[i]);
aP.Parent := Scroller;
aP.SetBounds(x, y, aP.Width, aP.Height);
x := x + aP.Width + dx;
if x + aP.Width > Scroller.Width then
begin
x := dx;
y := y + aP.Height + dx
end;
end;
Scroller.EnableAlign;
Scroller.Invalidate;
end;
procedure TfrmSTest.FormCreate(Sender: TObject);
var
i: integer;
aP: TPaintbox;
begin
Picturelist := TList.Create;
Colorlist := TList.Create;
ScrollerSize:=200;
for i := 0 to 120 do
begin
aP := TPaintbox.Create(self);
aP.Height := ScrollerSize - 40;
aP.Width := aP.Height;
aP.OnPaint := PaintBoxPaint;
aP.Tag := i;
Picturelist.Add(aP);
Colorlist.Add(Pointer(RGB(random(255), random(255), random(255))));
end;
end;
procedure TfrmSTest.FormDestroy(Sender: TObject);
begin
Picturelist.Free;
Colorlist.Free;
end;
procedure TfrmSTest.FormShow(Sender: TObject);
begin
DisplayStoryBoard;
end;
procedure TfrmSTest.PaintBoxPaint(Sender: TObject);
var
aP: TPaintbox;
begin
if Sender is TPaintbox then
begin
aP := TPaintbox(Sender);
aP.Canvas.Brush.Color := TColor(Colorlist[aP.Tag]);
aP.Canvas.Pen.Color := clLime;
aP.Canvas.Rectangle(aP.ClientRect);
aP.Canvas.Font.Color := clWhite;
aP.Canvas.Font.Style := [fsBold];
aP.Canvas.TextOut(3, 3, IntToStr(aP.Tag));
end;
end;
procedure TfrmSTest.ScrollerMouseWheel(Sender: TObject; Shift: TShiftState;
WheelDelta: Integer; MousePos: TPoint; var Handled: Boolean);
var SB: TControlScrollbar;
begin
if (Scroller.Align=alBottom) then
SB:=Scroller.HorzScrollBar
else
SB:=Scroller.VertScrollBar;
SB.Position:=SB.Position-WheelDelta;
Handled:=true;
end;
initialization
ReportMemoryLeaksOnShutDown := true;
end.
为了让事情更舒服一点,这里的表格是:
object frmSTest: TfrmSTest
Left = 0
Top = 0
Caption = 'frmSTest'
ClientHeight = 291
ClientWidth = 505
Color = clBtnFace
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -11
Font.Name = 'Tahoma'
Font.Style = []
OldCreateOrder = False
OnCreate = FormCreate
OnDestroy = FormDestroy
OnShow = FormShow
PixelsPerInch = 96
TextHeight = 13
object Scroller: TScrollBox
Left = 0
Top = 98
Width = 505
Height = 193
Align = alBottom
DoubleBuffered = False
ParentDoubleBuffered = False
TabOrder = 0
OnMouseWheel = ScrollerMouseWheel
ExplicitLeft = 2
end
object Button1: TButton
Left = 8
Top = 8
Width = 75
Height = 25
Caption = 'Button1'
TabOrder = 1
OnClick = Button1Click
end
end
好像没有其他人有这个问题。我找到了一个修复程序,虽然不完美,但好多了,所以为了完整起见,这里是我的答案。对所有 space 表示抱歉,感谢所有花时间思考这个问题的人。
通过调用 inherited 和 update 创建一个处理 WM_VScroll 和 WM_HScroll 的 TScrollbox 后代。 不作废,不重绘。
可以在表单的 OnCreate,父级设置为窗体。切换滚动选项,也许你会看到不同。
interface
type
TScrolloption = (soVCL, soNew);
TStyleScroller = class(TScrollbox)
private
protected
procedure WMVScroll(var Msg: TWMVScroll); message WM_VSCROLL;
procedure WMHScroll(var Msg: TWMHScroll); message WM_HSCROLL;
public
Scrolloption: TScrolloption;
Constructor Create(AOwner: TComponent); override;
end;
implementation
{ TStyleScroller }
constructor TStyleScroller.Create(AOwner: TComponent);
begin
inherited;
Scrolloption := soNew;
end;
procedure TStyleScroller.WMHScroll(var Msg: TWMHScroll);
begin
inherited;
if Scrolloption = soNew then
update;
end;
procedure TStyleScroller.WMVScroll(var Msg: TWMVScroll);
begin
inherited;
if Scrolloption = soNew then
update;
end;
end.