Search code examples
delphidelphi-7

Child controls flicker during resize of TCustomControl descendant


For long time I used descendant of TCustomPanel class, called MyContainer, in my application. Typical container for other visual controls I can say. And it was fine. One day I realized that I don't use panel functionality at all so I can derive MyContainer directly from TCustomControl.

After doing so I experience horrible flickering for child controls (e.g. aligned TMemo) when MyContainer is being resized using mouse. It is just horrible - it looks like entire TMemo is disappering for a moment so I can see the background. MyContainer paints itself fine - it is a problem only with child controls.

It was not happening when MyContainer was derived from TCustomPanel. What I am missing and where? Child control is double buffered and MyContainer too. I use Delphi 7 Personal Edition, so I don't have VCL source and so I am unable to compare TCustomPanel with TCustomControl implementation. Handling WM_EXITSIZEMOVE and WM_ENTERSIZEMOVE messages (to enable/disable child align) does not help.

I believe my problem is connected with erasing control background. As part of my "migration" to TCustomControl I added the following code to Paint method:

Canvas.Font.Assign(Font);
Canvas.Brush.Style := bsSolid;
Canvas.Brush.Color := Color;
PatBlt(Canvas.Handle, Canvas.ClipRect.Left, Canvas.ClipRect.Top, Canvas.ClipRect.Right, Canvas.ClipRect.Bottom, PATCOPY);

Without this code, child control does not flicker anymore but painting of parent control is ruined.


Solution

  • The difference between TCustomPanel and TCustomControl that affects this behaviour is that TCustomPanel adds the csAcceptControls style to the ControlStyle in the constructor. This in turn then affects the behaviour in the TWinControl base class, which adds the WS_CLIPCHILDREN style to the window for the controls with that style set.

    So, you can achieve the same result in one of two ways:

    1. Override the constructor and add the csAcceptsControls to the ControlStyle of your container control

    OR

    1. Override CreateParams and directly add the WS_CLIPCHILDREN flag to the window Style of your container control

    The Code

    Option 1:

    constructor TMyContainer.Create(Owner: TComponent);
    begin
      inherited;
      ControlStyle := ControlStyle + [csAcceptsControls];
    end;
    

    Note that this will mean that your container control can now accept controls dropped on it at design-time. Even without this ControlStyle you can add controls to a container at run-time by setting the Parent property.

    Option 2:

    procedure TMyContainer.CreateParams(var aParams: TCreateParams);
    begin
      inherited;
      aParams.Style := aParams.Style or WS_CLIPCHILDREN;
    end;
    

    This achieves the specific change in painting behaviour you are after but without affecting the ability of the control to accept controls at design-time.