当 MDIChild 本身包含 TPanel 等容器时,如何重新绘制 MDIChild 窗体,其对齐:= alClient 和 ParentBackground := False

发布于 2024-10-26 01:00:31 字数 3539 浏览 1 评论 0原文

总结:

请参阅 Andreas 的知识渊博的评论!

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

如下代码所示,TForm7是MDIForm 窗体,TForm8 是MDIChild 窗体。 TForm8 包含一个 alClient 对齐面板,其中还包含一个 TPaintBox。如果TForm8的面板的ParentBackground设置为False,我无法从TForm7触发TForm8的油漆盒的绘画事件。我想知道为什么会发生这种情况,以及如何在不明确引用的情况下触发 TForm8 的 Paintbox 的绘制事件。任何建议表示赞赏!

注意:如果我在 TForm8 内调用 Self.Repaint,例如在其 Click 事件内,则可以触发 TForm8 的 Paintbox 的 Paint 事件。仅当我在 TForm8 外部调用 form8.repaint 时才能触发它。我想知道为什么会发生这种情况?

可能相关的 SO 页面:
如何在模态表单是否处于活动状态?

包含 MDIForm 表单的单元。

    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。

    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

包含 MDIChild 窗体的单元。

    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。

    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

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

浊酒尽余欢 2024-11-02 01:00:31

我认为情况是这样的:

不管你信不信,“正常”行为是,如果您重新绘制表单(或其他一些容器),则只有该容器被重新绘制,而不是其中包含的子项被重新绘制。然而,随着视觉主题的出现,控件获得了半透明部分,突然间您需要在重绘父控件时重新绘制子控件,仅仅是因为子控件需要重新混合到新的背景中。

通过仔细检查 VCL 源代码,我的假设(相对)很容易得到验证,例如

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;

因此,当 ParentBackground 设置为 false 时,并且面板像经典面板一样,它不是当其父级重新绘制时,不会重新绘制。另一方面,如果 ParentBackgroundtrue,它确实会与其父级一起重新绘制。

因此,确实没有问题;你只是期待一种不符合预期的行为。

因此,您需要按照大卫的建议手动重新绘制油漆盒。

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.

蛮可爱 2024-11-02 01:00:31

当您希望绘画框重新绘制自身时,您只需要调用 pb1.Invalidate 即可。

或者我误解了你的问题?

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

Or am I misunderstanding your question?

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文