我们有一个win控制对象,它将客户端移动到其他一些coordiantes.问题是,当孩子太多时 - 例如500个控件 - 代码真的很慢.这一定是因为每次设置Left和Top属性时每个控件都被重新绘制.因此,我想告诉WinControl对象停止重新绘制,并在将所有对象移动到新位置后,可能会再次绘制(BeginUpdate对于备忘录和列表对象).我怎样才能做到这一点?这是移动对象的代码; 这很简单:
for I := 0 to Length(Objects) - 1 do begin
with Objects[I].Client do begin
Left := Left + DX;
Top := Top + DY;
end;
end;
Run Code Online (Sandbox Code Playgroud)
NGL*_*GLN 12
正如Cosmin Prund所解释的那样,持续时间长的原因不是重新绘制的效果,而是VCL在控制运动中的重新调整要求.(如果它确实应该花费它,那么你甚至可能需要立即重绘).
要暂时阻止重新调整以及所有检查和锚点工作,请对齐设置和Z顺序,使用DisableAlign和EnableAlign.并SetBounds通过直接调用它将调用次数减半:
procedure TForm1.FormCreate(Sender: TObject);
var
I: Integer;
Control: TControl;
begin
for I := 0 to 499 do
begin
Control := TButton.Create(Self);
Control.SetBounds((I mod 10) * 40, (I div 10) * 20, 40, 20);
Control.Parent := Panel1;
end;
end;
procedure TForm1.Button1Click(Sender: TObject);
var
I: Integer;
C: TControl;
begin
// Disable Panel1 paint
SendMessage(Panel1.Handle, WM_SETREDRAW, Integer(False), 0);
Panel1.DisableAlign;
try
for I := 0 to Panel1.ControlCount - 1 do
begin
C := Panel1.Controls[I];
C.SetBounds(C.Left + 10, C.Top + 5, C.Width, C.Height);
end;
finally
Panel1.EnableAlign;
// Enable Panel1 paint
SendMessage(Panel1.Handle, WM_SETREDRAW, Integer(True), 0);
// Update client area
RedrawWindow(Panel1.Handle, nil, 0, RDW_INVALIDATE or RDW_UPDATENOW or RDW_ALLCHILDREN);
end;
end;
Run Code Online (Sandbox Code Playgroud)
我会将所有控件放在面板中,然后移动面板而不是控件.这样你就可以在一次操作中执行换档.
如果您希望在其容器内移动控件,则可以使用TWinControl.ScrollBy.
对于它的价值,使用它SetBounds比修改Left和Top在单独的代码行中更有效.
SetBounds(Left+DX, Top+DY, Width, Height);
Run Code Online (Sandbox Code Playgroud)
你认为缓慢来自重新绘画控制的假设可能是正确的,但不是整个故事.处理移动控件的默认Delphi代码会延迟绘制,直到WM_PAINT收到下一条消息,并且在完成移动所有控件后,在消息队列被泵入时会发生这种情况.不幸的是,这涉及到很多东西,默认行为可以在许多地方改变,包括Delphi和Windows本身.我使用以下代码来测试在运行时移动控件时会发生什么:
var i: Integer;
begin
for i:=1 to 100 do
begin
Panel1.Left := Panel1.Left + 1;
Sleep(10); // Simulate slow code.
end;
end;
Run Code Online (Sandbox Code Playgroud)
行为取决于控制!A TControl(例如TLabel:)将根据德尔福的规则行事,但TWinControl取决于太多因素.一个简单的东西TPanel直到循环之后才重新绘制,TButton 在我的机器上只有背景被重新绘制,而a TCheckBox完全重新绘制.在David的机器TButton上也完全重新粉刷,证明这取决于许多因素.在TButton最可能的因素是Windows版本:我在Windows 8上测试过,David在Windows 7上测试过.
无论如何,还有另一个非常重要的因素需要考虑.在运行时移动控件时,需要考虑所有控件的对齐和锚定的所有规则.这可能导致雪崩的AlignControls/ AlignControl/ UpdateAnchorRules呼叫.由于所有这些调用最终都需要对它们进行递归调用,因此调用次数将是指数式的(因此您观察到在a上移动大量对象的TWinControl速度很慢).
最简单的解决方案是,正如大卫建议的那样,将所有内容放在面板上并将面板移动为一个面板.如果那是不可能的,并且所有控件都是实际的TWinControl(即:它们有一个窗口句柄),您可以使用:
BeginDeferWindowPos,DeferWindowPos,EndDeferWindowPos