Search code examples
delphidelphi-xe

How to temporarily stop a control from being painted?


We have a win control object which moves its clients to some other coordiantes. The problem is, when there are too many children - for example 500 controls - the code is really slow. It must be because of each control being repainted each time I set Left and Top property. So, I want to tell the WinControl object stop being repainted, and after moving all objects to their new positions, it may be painted again (Something like BeginUpdate for memo and list objects). How can I do this? Here's the code of moving the objects; it's quite simple:

for I := 0 to Length(Objects) - 1 do begin
  with Objects[I].Client do begin
    Left := Left + DX;
    Top := Top + DY;
  end;
end;

Solution

  • As Cosmin Prund explains, the cause for the long duration is not an effect of repainting but of VCL's realignment requisites at control movement. (If it really should take as long as it does, then you might even need to request immediate repaints).

    To temporarily prevent realignment and all checks and work for anchors, align settings and Z-order, use DisableAlign and EnableAlign. And halve the count of calls to SetBounds by called it directly:

    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;