How to repaint a MDIChild form when the MDIChild itself contains containers such as TPanel with aligned := alClient and ParentBackground := False

StackOverflow https://stackoverflow.com/questions/5383061

  •  28-10-2019
  •  | 
  •  

Question

Summarization:

Please see Andreas' knowledgeable comments!

==========================================

As shown in the following code, TForm7 is the MDIForm form, TForm8 is the MDIChild form. TForm8 contains an alClient aligned panel, which further contains a TPaintBox. If the TForm8's panel's ParentBackground is set to False, I cannot trigger TForm8's paintbox's paint event from TForm7. I am wondering why would this happen, and how can I trigger TForm8's paintbox's paint event without exlicitly refering to it. Any suggestion is appreciated!

Note: If I call Self.Repaint withint TForm8, for example inside its Click event, TForm8's paintbox's paint event can be triggered. It cannot be triggered only when I call form8.repaint outside TForm8. I am wondering why would this happen?

Possibly relevant SO pages:
How to repaint a parent form while a modal form is active?

Unit that contains the MDIForm form.

    unit Unit7;

    interface

    uses
      Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
      Dialogs;

    type
      TForm7 = class(TForm)
        procedure FormShow(Sender: TObject);
        procedure FormClick(Sender: TObject);

      end;

    var
      Form7: TForm7;

    implementation

    {$R *.dfm}

    uses
      Unit8;

    procedure TForm7.FormShow(Sender: TObject);
    begin
      TForm8.Create(Self);
    end;

    procedure TForm7.FormClick(Sender: TObject);
    begin
      TForm8(ActiveMDIChild).Repaint;
    end;

    end.

Dfm of the above Unit.

    object Form7: TForm7
      Left = 0
      Top = 0
      Caption = 'Form7'
      ClientHeight = 379
      ClientWidth = 750
      Color = clBtnFace
      Font.Charset = DEFAULT_CHARSET
      Font.Color = clWindowText
      Font.Height = -11
      Font.Name = 'Tahoma'
      Font.Style = []
      FormStyle = fsMDIForm
      OldCreateOrder = False
      OnClick = FormClick
      OnShow = FormShow
      PixelsPerInch = 96
      TextHeight = 13
    end

Unit that contains the MDIChild form.

    unit Unit8;

    interface

    uses
      Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
      Dialogs, ExtCtrls;

    type
      TForm8 = class(TForm)
        pb1: TPaintBox;
        pnl1: TPanel;
        procedure pb1Paint(Sender: TObject);
        procedure pb1Click(Sender: TObject);
      private
        fCounter: Integer;

      end;

    implementation

    {$R *.dfm}

    procedure TForm8.pb1Click(Sender: TObject);
    begin
      Self.Repaint;
    end;

    procedure TForm8.pb1Paint(Sender: TObject);
    begin
      Self.pb1.Canvas.TextOut(30, 30, IntToStr(Self.fCounter));
      Self.fCounter := Self.fCounter + 1;
    end;

    end.    

Dfm of the above Unit.

    object Form8: TForm8
      Left = 0
      Top = 0
      Caption = 'Form8'
      ClientHeight = 226
      ClientWidth = 233
      Color = clBtnFace
      Font.Charset = DEFAULT_CHARSET
      Font.Color = clWindowText
      Font.Height = -11
      Font.Name = 'Tahoma'
      Font.Style = []
      FormStyle = fsMDIChild
      OldCreateOrder = False
      Visible = True
      PixelsPerInch = 96
      TextHeight = 13
      object pnl1: TPanel
        Left = 0
        Top = 0
        Width = 233
        Height = 226
        Align = alClient
        ShowCaption = False
        TabOrder = 0
        object pb1: TPaintBox
          Left = 1
          Top = 1
          Width = 231
          Height = 224
          Align = alClient
          OnClick = pb1Click
          OnPaint = pb1Paint
          ExplicitLeft = 56
          ExplicitTop = -64
          ExplicitWidth = 105
          ExplicitHeight = 105
        end
      end
    end
Was it helpful?

Solution

I think this is the case:

Believe it or not, the "normal" behaviour is that, if you repaint a form (or some other container), only that container gets repainted, not the children contained in it. However, with the advent of visual themes, controls got semi-transparent parts, and all of a sudden you need to repaint the child controls when the parent is redrawn, simply because the children need to reblend into the new background.

My hypothesis is (relatively) readily validated by scrutinizing the VCL source code, e.g.

procedure TWinControl.CMInvalidate(var Message: TMessage);
begin
  { Removed irrelevant code to avoid copyvio issues. }  
      InvalidateRect(WindowHandle, nil, not (csOpaque in ControlStyle));
      { Invalidate child windows which use the parentbackground when themed }
      if ThemeServices.ThemesEnabled then
        for I := 0 to ControlCount - 1 do
          if csParentBackground in Controls[I].ControlStyle then
            Controls[I].Invalidate;
  { Removed irrelevant code to avoid copyvio issues. }
end;

Therefore, when ParentBackground is set to false, and the panel bahaves like a classic panel, it isn't repainted when its parent is. On the other hand, if ParentBackground is true, it does get repainted along with its parent.

Hence there is no problem, really; you simply expect a behaviour that isn't to be expected.

So you need to repaint the paint box manually, by following David's advice.

OTHER TIPS

You just need to call pb1.Invalidate when you want the paint box to re-draw itself.

Or am I misunderstanding your question?

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top