如何暂时停止控件的绘制?

Jav*_*vid 7 delphi delphi-xe

我们有一个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顺序,使用DisableAlignEnableAlign.并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)


Dav*_*nan 6

我会将所有控件放在面板中,然后移动面板而不是控件.这样你就可以在一次操作中执行换档.

如果您希望在其容器内移动控件,则可以使用TWinControl.ScrollBy.

对于它的价值,使用它SetBounds比修改LeftTop在单独的代码行中更有效.

SetBounds(Left+DX, Top+DY, Width, Height);
Run Code Online (Sandbox Code Playgroud)

  • 如果你给我们一个演示程序,我们可以对它进行优化. (3认同)
  • @David那不是真的; "左"和"顶"被改变了. (2认同)
  • @Javid`TScrollBox`的滚动条调用`TWinControl.ScrollBy`,它使用[`ScrollWindow`](http://msdn.microsoft.com/en-us/library/windows/desktop/bb787591(v = vs.85) ).aspx),并且在子节点不是"TWinControl"的情况下更改`Left`和`Top`属性后面的私有字段. (2认同)

Cos*_*und 6

你认为缓慢来自重新绘画控制的假设可能是正确的,但不是整个故事.处理移动控件的默认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上测试过.

AlignControl Avalanche

无论如何,还有另一个非常重要的因素需要考虑.在运行时移动控件时,需要考虑所有控件的对齐和锚定的所有规则.这可能导致雪崩的AlignControls/ AlignControl/ UpdateAnchorRules呼叫.由于所有这些调用最终都需要对它们进行递归调用,因此调用次数将是指数式的(因此您观察到在a上移动大量对象的TWinControl速度很慢).

最简单的解决方案是,正如大卫建议的那样,将所有内容放在面板上并将面板移动为一个面板.如果那是不可能的,并且所有控件都是实际的TWinControl(即:它们有一个窗口句柄),您可以使用:

BeginDeferWindowPos,DeferWindowPos,EndDeferWindowPos