【问题标题】:Delphi Component: How to use parent font?Delphi 组件:如何使用父字体?
【发布时间】:2011-06-27 04:43:06
【问题描述】:

我有一个使用 ParentFont 的自定义组件。

在我的组件构建过程中,我可以看到最初组件的字体设置为默认的MS Sans Serif

constructor TCustomWidget.Create(AOwner: TComponent);
begin
   inherited Create(AOwner);

   ...
end;

检查节目Self.Font.Name: 'MS Sans Serif'

一段时间后,我的组件的字体会更新以反映父级的字体:

TReader.ReadComponent(nil)
   SetCompName
      TControl.SetParentComponent
         TControl.SetParent
            TWinControl.InsertControl
               AControl.Perform(CM_PARENTFONTCHANGED, 0, 0);

然后一切都很好,我的组件的字体已更改为父字体(例如`MS Shell Dlg 2')。

问题是我的子控件与其父控件的字体(即我的组件)不同步。

在我的组件构造过程中,我创建了子控件:

constructor TCustomWidget.Create(AOwner: TComponent);
begin
   inherited Create(AOwner);

   ...
   CreateComponents;
end;

procedure TCustomWidget.CreateComponents;
begin
   ...
   FpnlBottom := TPanel.Create(Self);
   FpnlBottom.Caption := '';
   FpnlBottom.Parent := Self;
   FpnlBottom.Align := alBottom;
   FpnlBottom.Height := 46;
   FpnlBottom.ParentFont := True;
   ...
end;

最初我的FpnlBottom 也有默认字体MS Sans Serif

后来,当我的组件的字体更新为 父的字体(例如MS Shell Dlg 2)时,子控件的字体没有更新,而是保持MS Sans Serif

  • 为什么我的子控件的 ParentFont 属性没有得到遵守?
  • 如何让子控件的ParentFont 属性起作用?

示例代码

工具两小时将其精简为可管理、可重现的代码:

unit WinControl1;

interface

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

type
    TWidget = class(TWinControl)
    private
        FTitleLabel: Tlabel;
        FpnlBottom: TPanel;

        procedure CreateComponents;
    protected
        procedure FontChange(Sender: TObject);
    public
        constructor Create(AOwner: TComponent); override;
    published
        {Inherited from TWinControl}
        property Align;
        property Font;
        property ParentFont;
    end;

    procedure Register;

implementation

procedure Register;
begin
    RegisterComponents('Samples',[TWidget]);
end;

{ TCustomWidget }

constructor TWidget.Create(AOwner: TComponent);
begin
    inherited Create(AOwner);

    ControlStyle := ControlStyle + [csAcceptsControls, csNoDesignVisible];

    Self.Width := 384;
    Self.Height := 240;

    Self.Font.OnChange := FontChange;

    CreateComponents;
end;

procedure TWidget.CreateComponents;
begin
    FpnlBottom := TPanel.Create(Self);
    FpnlBottom.Parent := Self;
    FpnlBottom.Align := alBottom;
    FpnlBottom.Color := clWindow;
    FpnlBottom.Caption := 'FpnlBottom';
    FpnlBottom.Height := 45;

    FTitleLabel := TLabel.Create(Self);
    FTitleLabel.Parent := FpnlBottom;
    FTitleLabel.Left := 11;
    FTitleLabel.Top := 11;
    FTitleLabel.Caption := 'Hello, world!';
    FTitleLabel.AutoSize := True;
    FTitleLabel.Font.Color := $00993300;
    FTitleLabel.Font.Size := Self.Font.Size+3;
    FTitleLabel.ParentFont := False;
end;

procedure TWidget.FontChange(Sender: TObject);
begin
    //title label is always 3 points larger than the rest of the content
    FTitleLabel.Font.Name := Self.Font.Name;
    FTitleLabel.Font.Size := Self.Font.Size+3;

    OutputDebugString(PChar('New font '+Self.Font.Name));
end;

end.

【问题讨论】:

  • 我的所有控件都使用其父控件的字体。这显然是它的工作方式。为什么你的控件行为不端?我不知道。也许故障的最小再现会有所帮助。

标签: delphi fonts components delphi-5


【解决方案1】:

看到您的示例代码后,您使用的FontChange 事件处理程序全错了。你根本不应该使用它。您正在绕过触发CM_FONTCHANGEDCM_PARENTFONTCHANGED 通知的本机TControl.FontChanged() 事件处理程序,因此您实际上打破了ParentFont 逻辑。完全摆脱您的 TWidget.FontChanged() 事件处理程序。如果您需要对组件的Font 属性更改做出反应,则需要拦截CM_FONTCHANGED 消息,例如:

unit WinControl1;

interface

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

type
  TWidget = class(TWinControl)
  private
    FTitleLabel: TLabel;
    FpnlBottom: TPanel;
    procedure CreateComponents;
    procedure CMFontChanged(var Message: TMessage); message CM_FONTCHANGED;
  public
    constructor Create(AOwner: TComponent); override;
  published
    {Inherited from TWinControl}
    property Align;
    property Font;
    property ParentFont;
  end;

procedure Register;

implementation

procedure Register;
begin
  RegisterComponents('Samples',[TWidget]);
end;

{ TCustomWidget }

constructor TWidget.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);
  ControlStyle := ControlStyle + [csAcceptsControls, csNoDesignVisible];
  Self.Width := 384;
  Self.Height := 240;
  CreateComponents;
end;

procedure TWidget.CreateComponents;
begin
  FpnlBottom := TPanel.Create(Self);
  FpnlBottom.Parent := Self;
  FpnlBottom.Align := alBottom;
  FpnlBottom.Color := clWindow;
  FpnlBottom.Caption := 'FpnlBottom';
  FpnlBottom.Height := 45;

  FTitleLabel := TLabel.Create(Self);
  FTitleLabel.Parent := FpnlBottom;
  FTitleLabel.Left := 11;
  FTitleLabel.Top := 11;
  FTitleLabel.Caption := 'Hello, world!';
  FTitleLabel.AutoSize := True;
  FTitleLabel.Font.Color := $00993300;
  FTitleLabel.Font.Size := Self.Font.Size+3;
  FTitleLabel.ParentFont := False;
end;

procedure TWidget.CMFontChanged(var Message: TMessage);
begin
  inherited; // let TControl and TWinControl react first
  //title label is always 3 points larger than the rest of the content
  FTitleLabel.Font.Name := Self.Font.Name;
  FTitleLabel.Font.Size := Self.Font.Size + 3;
  OutputDebugString(PChar('New font ' + Self.Font.Name));
end;

end. 

【讨论】:

  • +1 在这种情况下,您甚至不需要处理 CMFontChanged,因为您将其全部设置在 CreateComponents 中。
  • 是的,CM_FONTCHANGED 需要处理,因为 Ian 的“标题标签总是比其余内容大 3 点”要求。如果 TWidget 的 Font.Size 在初始设置后发生更改,FTitleLabel.Font.Size 的值将不同步而不会处理 CM_FONTCHANGED
  • 哇。我永远不会猜到 VCL 基础设施会默默地接管OnChange 事件,而不是使用内部机制。但看起来你是对的。 +1 并被接受。
  • TFontTPenTBrush 等类是独立的实用程序类。 TControl 必须分配自己的内部 OnChange 事件处理程序以响应对其 Font 属性的更改。你正在用你自己的替换那个内部事件处理程序。
【解决方案2】:

每次更新组件的Font 属性时,组件会自动向其每个子控件发送CM_PARENTFONTCHANGED 消息,此时每个控件检查其ParentFont 属性是否为True。您是否检查过以确保您的子控件的 ParentFont 属性仍设置为 True?也许在他们自己的 DFM 流式传输期间,子控件正在设置他们的 Font 属性,这会将 ParentFont 重置为 False。

【讨论】:

  • 还有+1,这很有用。有时很难跟踪(未记录的)信息流向上和向下控制层次结构。
猜你喜欢
  • 2016-08-07
  • 2011-04-06
  • 1970-01-01
  • 2011-01-05
  • 1970-01-01
  • 2020-12-28
  • 2015-05-08
  • 1970-01-01
  • 2011-02-25
相关资源
最近更新 更多