2011. június 15., szerda

How to prevent csOpaque child controls from flickering


Problem/Question/Abstract:

I have a TPaintBox that I use to draw a representation of the data the user is entering. It updates whenever the form is repainted or data is changed. This works fine. Using D5, I've noticed that when the user is moving the mouse around causing hints to pop up and down, it causes a tremendous amount of flicker. In part, this is caused by the fact that I clear the canvas before redrawing. Should I draw to an invisible paintbox and then copy to a TImage?

Answer:

{ Overrides the WM_ERASEBKGND message in TWinControl and TForm to prevent flicker of csOpaque child controls.
Unpublished; (c) 1999, David Best, davebest@usa.net
You are free to use this and derived works provided you acknowlege it's source in your code.}

procedure WMEraseBkgndEx(WinControl: TWinControl; var Message: TWmEraseBkgnd);
var
  i, Clip, SaveIndex: Integer;
begin
  { Only erase background if we're not doublebuffering or painting to memory }
  with WinControl do
    if not DoubleBuffered or (TMessage(Message).wParam = TMessage(Message).lParam) then
    begin
      SaveIndex := SaveDC(Message.DC);
      Clip := SimpleRegion;
      if ControlCount > 0 then
      begin
        for i := 0 to ControlCount - 1 do
          if not (Controls[i] is TWinControl) then
            {child windows already excluded}
            with Controls[i] do
            begin
              if (Visible or (csDesigning in ComponentState) and not
                                                                (csNoDesignVisible in ControlStyle))
                 and (csOpaque in ControlStyle) then
              begin
                Clip := ExcludeClipRect(Message.DC, Left, Top, Left +
                                                                 Width, Top + Height);
                if Clip = NullRegion then
                  break;
              end;
            end;
      end;
      if Clip <> NullRegion then
        FillRect(Message.DC, ClientRect, Brush.Handle);
      RestoreDC(Message.DC, SaveIndex);
    end;
  Message.Result := 1;
end;

procedure TNoFlickerForm.WMEraseBkGnd(var msg: TWMEraseBkGnd);
begin
  WMEraseBkgndEx(Self, msg);
end;

1 megjegyzés: