Paul
Paul

Reputation: 26660

Avoid flickering in transparent control while runtime themes are enabled

My control is a TCustomControl descendant where all the contents is painted with GDI+ in the overridden Paint method.

Everything is fine when

DoubleBuffered := True;
ParentBackground := False;

and I erase control's background in Paint method with

g := TGPGraphics.Create(Canvas.Handle);
g.Clear(MakeColor(70, 70, 70));

Now I would like to make a transparent background in the areas where I am not painting.

So, I commented the g.Clear out and made

ParentBackground := True;

in constructor.

When runtime themes are off it is enough to set DoubleBuffered of parent control to True in order to avoid flickering, but with runtime themes this does not help any more.

Below is an excerpt from TWinControl code with a marked line that causes flickering:

procedure TWinControl.WMEraseBkgnd(var Message: TWMEraseBkgnd);
begin
  if StyleServices.Enabled and Assigned(Parent) and (csParentBackground in FControlStyle) then
  begin
    { Get the parent to draw its background into the control's background. }
    if Parent.DoubleBuffered then
      PerformEraseBackground(Self, Message.DC) //It flickers here!!!!!
    else
      StyleServices.DrawParentBackground(Handle, Message.DC, nil, False);
  end
  else
  begin
    { Only erase background if we're not doublebuffering or painting to memory. }
    if not FDoubleBuffered or
{$IF DEFINED(CLR)}
       (Message.OriginalMessage.WParam = Message.OriginalMessage.LParam) then
{$ELSE}
       (TMessage(Message).wParam = WPARAM(TMessage(Message).lParam)) then
{$ENDIF}
      FillRect(Message.DC, ClientRect, FBrush.Handle);
  end;
  Message.Result := 1;
end;

Are there any solutions to that?

Upvotes: 2

Views: 684

Answers (1)

Dalija Prasnikar
Dalija Prasnikar

Reputation: 28516

There is an error in TWinControl.WMEraseBkgnd method. It should always skip erasing background for double buffered controls when control is not painting in memory.

You can override WMEraseBkgnd behavior in your own control, or patch TWinControl.WMEraseBkgnd to apply following fix for all controls.

  TMyControl = class(TCustomControl)
  protected
  ...
    procedure WMEraseBkgnd(var Message: TWmEraseBkgnd); message WM_ERASEBKGND;
  ...

procedure TMyControl.WMEraseBkgnd(var Message: TWmEraseBkgnd);
begin
{ Only erase background if we're not doublebuffering or painting to memory. }
  if not FDoubleBuffered or
{$IF DEFINED(CLR)}
    (Message.OriginalMessage.WParam = Message.OriginalMessage.LParam) then
{$ELSE}
    (TMessage(Message).WParam = WParam(TMessage(Message).LParam)) then
{$ENDIF}
    begin
      if StyleServices.Enabled and Assigned(Parent) and (csParentBackground in ControlStyle) then
        begin
          if Parent.DoubleBuffered then
            PerformEraseBackground(Self, Message.DC)
          else
            StyleServices.DrawParentBackground(Handle, Message.DC, nil, False);
        end
      else
        FillRect(Message.DC, ClientRect, Brush.Handle);
    end;
  Message.Result := 1;
end;

Issue is reported as RSP-24415

Upvotes: 2

Related Questions