Chau Chee Yang
Chau Chee Yang

Reputation: 19650

How to efficiently let a `ParentFont = False` child control to use same font name as parent?

Most VCL controls has Fonts and ParentFont property. It is a good practice to set ParentFont = True and the Fonts will follow it's parent font's Name, Height and Color. This give a uniform visual among controls.

However, we may want to make one or two controls to have different looks from other by setting Font.Style = fsBold or a contrast Font.Color but using same Font.Name as parent's font. Doing so makes ParentFont = false. From this point onward, changing parent's font name or size will have no effects on those control's font property.

I think this might be VCL's design. Perhaps someone has better design practices or experience to share on Fonts and ParentFont issue.

Consider a case where I let user set a default font name for the application. Those ParentFont = False controls will not change accordingly. Manual override in coding is possible but it is tedious work that introduce extra coding.

Upvotes: 2

Views: 934

Answers (3)

Arioch 'The
Arioch 'The

Reputation: 16065

One of possible ways would be to inject post-parentfont handling

just check procedure TControl.CMParentFontChanged in VCL.Controls unit

You should also make some uniform way of injecting font customizations to your controls. Maybe subclassing their WindowsProc or extending standard controls with interfaces like EX-controls in JediVCL

The backbone - the proof of concept that can hardly be replicated a lot as is - maintenance problems - is below.

unit xxx;
interface uses yyyy;

type 
 (*********** hijack start!  ****)

 TFontOverrideEvent = reference to procedure (const Sender: TObject; const Font: TFont);

 TButton = class( VCL.StdCtrls.TButton )
    protected 
       procedure ParentFontChanged(var m: TMessage); message CM_ParentFontChanged;
    public
       var FontOverrideEnabled: Boolean;
       var OnFontOverride: TFontOverrideEvent;
 end;

 (**** hijack end! standard form declaration below ****)

 TForm1 = class(TForm)
 ....
    Button1: TButton;
 ...
    procedure FormCreate(Sender: TObject);
  ...
implementation

procedure TForm1.FormCreate(Sender: TObject);
begin
  Button1.OnFontOverride :=
    procedure (const Sender: TObject; const LFont: TFont) begin
       with LFont do Style := [fsBold] + Style;
    end;
  Button1.FontOverrideEnabled := true;
  Button1.ParentFont := true;     

  PostMessage( Button1.Handle, CM_ParentFontChanged, 0, 0); 
  // trigger control to borrow font with customization
end;

....

 (*** hijack implementation ***)

procedure TButton.ParentFontChanged(var m: TMessage);
var SilenceHim1: IChangeNotifier; SilenceHim2: TNotifyEvent;
begin
  inherited; 
     // let VCL make standard font copying
     // warning! it may also make AutoSize or other automatic re-layout!
     // as we hide the fact of our font tuning from VCL - it would not 
     // have a chance to react to our customizations!

  if FontOverrideEnabled and Assigned( OnFontOverride ) then
  begin
    SilenceHim2 := Font.OnChange;
    SilenceHim1 := Font.FontAdapter; 
    try
      Font.OnChange := nil;
      Font.FontAdapter := nil;
      OnFontOverride( Self, Font );
    finally
      Font.OnChange := SilenceHim2;
      Font.FontAdapter := SilenceHim1;  
    end;
  end;
end;

Then try in runtime to change From1.Font and see how Button1 applies the changes to itself

Upvotes: 1

David Heffernan
David Heffernan

Reputation: 613382

I think that the simplest way to achieve your goals is to set no font properties at design time. Use ParentFont = True everywhere.

Then at runtime make whatever modifications you need to make. That allows you to change the font face centrally, and still have the rest of your program pick it up.

Upvotes: 1

Dalija Prasnikar
Dalija Prasnikar

Reputation: 28522

That is known VCL limitation.

You can either have ParentFont or your custom font settings in which case changing font properties in parent will not be propagated.

The best way around that is to use ParentFont = true everywhere and set custom font properties of specific controls at runtime in form's OnCreate event. Of course, in that case you lose What You See Is What You Get at design time, but you get more control over actual runtime appearance of your forms.

procedure TForm1.OnCreate(Sender: TObject);
begin
  inherited;
  Label1.Font.Style := [fsBold];
  Label1.Font.Color := clRed;
end;

For applying user custom font selection, you would also need to recreate forms, or use ParentFont := true before applying custom styles for specific controls, so they pick up your new font settings.

Upvotes: 4

Related Questions