24

Is there a quick and effective way of applying a global Font to be used in a project?

By this I mean I would like to set a specific Font name that all controls in my project will use such as TButton, TEdit, TLabel etc.

Typically setting the Font for the Form rather than a specific control will change all the controls on that Form to the Font specified.

There is a slight issue with this however, if you have manually changed a Font on a specific control, then setting the Font by the Form will no longer update those controls that have previously been changed manually.

Idea 1

I was thinking of using a For loop and iterating through each component on my Forms and setting the Font this way, such as:

procedure TForm1.FormCreate(Sender: TObject);
var
  i: Integer;
begin
  with TForm(Self) do
  begin
    for i := 0 to ComponentCount - 1 do
    begin
      if Components[i] is TButton then
      begin
        TButton(Components[i]).Font.Name  := 'MS Sans Serif';
        TButton(Components[i]).Font.Size  := 8;
        TButton(Components[i]).Font.Style := [fsBold];
      end;

      if Components[i] is TLabel then
      begin
        TLabel(Components[i]).Font.Name  := 'MS Sans Serif';
        TLabel(Components[i]).Font.Size  := 8;
        TLabel(Components[i]).Font.Style := [fsBold];
      end;
    end;
  end;
end;

But doing this seems very messy, it will also be a considerable amount of code for a simple task.

Idea 2

I know I could manually change the fonts at design time one by one for each control, but with several forms to go through this could take time and even then I might of missed a control.

Idea 3

Similar to Idea 2, another way could be to view the Form as Text (DFM) and Find and Replace the font that way.


Basically I am going for consistency within my Application, and having one Font used throughout is what I was looking to achieve.

Am I missing something completely obvious here, is what I am trying to do overkill for such a task?

Fabrizio
  • 7,603
  • 6
  • 44
  • 104
  • 7
    If you change control's font manually you also set its `ParentFont` property to `False`; set it `True` to return default (Parent) font – kludg May 14 '12 at 18:10
  • 1
    Sneaky, I did not realise this property had been getting changed! –  May 14 '12 at 18:15
  • 11
    Before creating any forms, set `Application.DefaultFont.Name := 'MS Sans Serif';` With the ParentFont set to true in your forms as Serg points out. To set the font in the message dialogs, set the `Screen.MessageFont.Name`property. – LU RD May 14 '12 at 18:16
  • 1
    If you ever need to do this at runtime, you can iterate form components as you did above and check to see which components have the Font property published using the IsPublishedProp(Components[i], 'Font') method from the TypInfo unit. Then, you can hack TControl to set the component's Font property, i.e., – Max Williams May 14 '12 at 18:36
  • @LURD I was not aware of `Application.DefaultFont.Name` –  May 14 '12 at 18:43
  • 5
    If you strip all Font and ParentFont properties from your .dfm files then ParentFont defaults to True, and it's all good. – David Heffernan May 14 '12 at 18:43
  • 1
    @DavidHeffernan that is another good tip :) –  May 14 '12 at 18:45

3 Answers3

27

As discussed in the comments, the key to this is the ParentFont property. This property is defined at various points in the VCL hierarchy. If you set ParentFont to be True for all components in the chain, then you can change the fonts for the entire application simply by modifying

Application.DefaultFont

By default most components set ParentFont to True and so you have nothing to do. The odd one out though is TForm. A brand new default form has ParentFont set to False. This is somewhat disappointing but I suspect reflects the fact that the original designers of the VCL did not anticipate this and that ParentFont was grafted on relatively late in the development of the VCL.

No matter, in an ideal world, all forms in your application should be derived from a common base class that you control. If that is so then you can simply make the change there, set ParentFont to be True, make sure no explicit font settings are applied to any components on you forms, and you are golden. Control the entire application's fonts through a single property. If you don't have a common base class for your forms, here's an ideal time to add it. If you don't want to do that then you need to set ParentFont for each form.

Other related properties are Screen.MessageFont and Screen.MenuFont. These provide global control over the fonts used in message boxes and menus. However, recent versions of Delphi have handed back to Windows control over the painting of message boxes and menus and so these properties have no effect.

David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
  • There is nothing really to add to what you and others have commented on already. Wouldn't it be great though if there was an option in the Project Options to set a Font style and apply it to the project, in the same way that `Application.DefaultFont` works - if like me been unaware of this built-in function, having the visual setting to change the font globally would make it much easier. Anyway I accept your answer because the information is well written, and +1 for highlighting message and menus. –  May 14 '12 at 21:46
  • 12
    Remember that VCL forms are not "layout managed" like Swing (java) or WPF (C#/.net) and that changing fonts at runtime leads to a perfect hell of font-clipping, and makes your UI an unreadable mess. Good luck. There is no practical way to change an applications fonts without reviewing the results by hand, form by form, for readability. – Warren P May 14 '12 at 23:47
  • 3
    @Warren that's a good point and I intended to mention it in my answer, but forgot. In my app, I show fonts in either Tahoma 8 for XP or Segoe UI 9 for Vista+. This needs calls to ChangeScale to change form layout. But yes you have to check the forms in both variants. So long as you don't cram your labels hard up against each other, there's never a problem. ParentFont works wonders in all of this. – David Heffernan May 15 '12 at 06:37
  • 4
    I am going to try this myself and will probably blog about the results. – Warren P May 15 '12 at 23:28
  • 3
    +1 for "all forms in your application should be derived from a common base class that you control". – kobik May 17 '12 at 16:27
  • Is there a way to set `Application.DefaultFont` at design time? – Fabrizio Oct 16 '19 at 07:25
  • Above Answer is incorrect, ParentFont for Forms will use the default font which does not scale with user display settings. If the user sets display scaling to anything other than 100%, your form fonts will not scale with the UI controls. Parentfont must be disabled and Scaled set to true for forms to obey windows settings. – Andy k Feb 27 '20 at 10:44
  • @Andy Scaling wasn't part of the question, which is why it isn't mentioned. But yes, if you want your app to scale with prevailing dpi, then you need to set `Scaled` to be `True`. I'm not sure what you are getting at with `ParentFont`. I use the `ParentFont` mechanism, with `Scaled` set `True`, and have good dpi scaling behaviour. – David Heffernan Feb 27 '20 at 11:01
  • @David Try setting display to 150%. If parentfont is checked the displayed fonts are exactly the same as 100%. If parentfont is false displayed font is scaled 150% and aligns with UI scaling. Just been testing this, which is how I got here. The only way to get the runtime windows default font is to read it from the system and assign it in the form constructor, don't use Parentfont. – Andy k Feb 27 '20 at 11:48
  • @Andyk `ParentFont` working fine for me at all DPI settings – David Heffernan Feb 27 '20 at 11:56
  • @DavidHeffernan Form.ParentFont = True doesn't work with per-monitor DPI scaling. It will come up correctly on monitors that match the system DPI, but if you move to a different monitor the font doesn't change scale. Tested in Delphi 10.3.3 w/ DPI Awareness set to "Per Monitor v2". Assigning Font = Application.Font in InitializeNewForm does work – Zoë Peterson Apr 02 '20 at 21:06
2

The real key, as was mentioned, is to ensure that all your forms descend from your own application base form.

Then, you can view each form and button etc, and review the properties, where any modified font property should be displayed in bold, and is easily identified. Most properties have a "Revert to inherited" menu choice. This should essentially undo any previous selection, without having to go to the text version for editting. (Although it probably does exactly that, deleting any text entry resulting from previous font-setting).

I would definitely want to fix each form once rather than leaving it defined incorrectly and adding more code to fix it at runtime. That change will leave you with a worse problem if you later decide to do something different.

Patrick Moloney
  • 642
  • 5
  • 14
1

If you want to do this runtime, like you describe it in Idea 1, you should consider making it a recursive function, like this:

procedure SetFontProperties(Control: TControl; Name: TFontName; Size: Integer; Styles: TFontStyles);
// Set font properties
var
  Index: Integer;
  Font: TFont;
  AnObject: TObject;
  ChildControl: TControl;
begin
  // Set font properties
  AnObject := GetObjectProp(Control, 'Font', nil);
  if AnObject is TFont then
  begin
    // Set properties
    Font := TFont(AnObject);
    Font.Name  := Name;
    Font.Size  := Size;
    Font.Style := Styles;
  end;

  // Set child font properties
  if Control is TWinControl then
  begin
    // Set
    for Index := 0 to TWinControl(Control).ControlCount - 1 do
    begin
      // Child control
      ChildControl := TWinControl(Control).Controls[Index];

      // Set font properties
      SetFontProperties(ChildControl, Name, Size, Styles);
    end;
  end;
end;

You can then switch fonts for all controls within a form by using it like this:

SetFontProperties(Self, 'Courier', 14, []);

The function will then set the font properties of the form, and the font properties of all child controls on the form, including controls nested within TPanels or other container controls.

However I do agree with you that it's sort of a half messy way of doing it.

bjaastad_e
  • 691
  • 6
  • 10
  • It is not ideal coding such a thing, but your answer is useful non the less :) –  May 14 '12 at 21:41
  • @MarjanVenema It was only intended as an illustration. If you have non-VCL components deploying other font property names, you will have to expand the function with those property names, of course. However the main point in this discussion is the point mentioned by Warren P in the comment above: Usually it's not viable to alter font properties runtime like this, because it alters alignments, introduces font-clipping, etc. – bjaastad_e May 15 '12 at 07:49