3

I have an application that needs to be adaptive to a range of different screen sizes (resolutions). Most of that I've done using table layout panels.

But some of the control (buttons and labels mostly) have too large font and the text doesn't fit in the control. So far I've managed change the font of some controls by using

            if (Screen.PrimaryScreen.Bounds.Width < 1440)
        {
            button_5.Font = new Font("Impact", button_5.Font.Size - 4);
        }

But that is too much text to add for every single control in the application.

Is there a way of changing the fonts of all the controls on the application at once? Or at least all controls on a form?

Matt
  • 25,467
  • 18
  • 120
  • 187
Martynas
  • 168
  • 2
  • 14
  • Every form is a control container where the Controls property lists all the controls on that form. When a control is itself a control container (panel,groupbox) then it has a Controls collection with the controls hosted by that container. It is relatively easy build a recursive function that loops over all controls of your form – Steve Jul 31 '17 at 22:44
  • Could you possibly give me an example of such a function so I can build what I need? – Martynas Jul 31 '17 at 22:49
  • I've managed to set the fonts of all the controls in the table layout panel by using foreach (Control ctrl in tableLayoutPanel1.Controls) { ctrl.Font = new Font("Impact", ctrl.Font.Size - 4); } But no idea how to access all of the control of the whole form yet. – Martynas Jul 31 '17 at 23:05

3 Answers3

7

A simple recursive function will traverse all the controls in your form and change the font size. You need to test it against your controls and look at the effect because in this code there is no exception handling

public void SetAllControlsFont(ControlCollection ctrls)
{
    foreach(Control ctrl in ctrls)
    {
        if(ctrl.Controls != null)
            SetAllControlsFont(ctrl.Controls);

        ctrl.Font = new Font("Impact", ctrl.Font.Size - 4);

    }
}

You can call it from your toplevel form passing the initial form's control collection

SetAllControlsFont(this.Controls);
Steve
  • 213,761
  • 22
  • 232
  • 286
  • That only changes the font for the first table layout panel but doesn't loop through the rest of the controls. – Martynas Aug 01 '17 at 22:33
  • That should not be the case if _this.Controls_ is the Form.Controls collection. The top level of the hierarchy – Steve Aug 02 '17 at 07:07
  • Could it have something to do with the fact that `this.Controls` only contains `{System.Windows.Forms.TableLayoutPanel, BorderStyle: System.Windows.Forms.BorderStyle.None}` which isn't even the actual control on the form and just the type of control. – Martynas Aug 02 '17 at 09:42
  • 1
    I've managed to make it work by changing `public void SetAllControlsFont(ControlCollection ctrls)` to `public void SetAllControlsFont(Control.ControlCollection ctrls)` and instead of this.Controls I gave it tableLayoutPanel1.Controls, which contains all the other table layout panels inside it. Now it loops through al the panels and all the controls inside of those panels. – Martynas Aug 02 '17 at 09:55
  • 1
    The problem was `SetAllControlsFont(ctrl.Controls);` could not convert from Cntrol.ControlCollection to Form.ControlCollection. Apparently those are different control collection types. – Martynas Aug 02 '17 at 09:59
  • it seems that the TableLayoutPanel has its own implementation of the ControlCollection named TableLayourControlCollection. Perhaps you can try with a simple cast. Anyway if your are satisfied passing the TableLayoutControleCollection instead of this.Controls then there is no point to experiment. Glad to be of help – Steve Aug 02 '17 at 10:55
  • Yeah I'm satisfied. Thank you. – Martynas Aug 02 '17 at 16:38
1

Based on Steve's good answer, I would do the following improvements:

/// <summary>
/// Changes fonts of controls contained in font collection recursively. <br/>
/// <b>Usage:</b> <c><br/>
/// SetAllControlsFont(this.Controls, 20); // This makes fonts 20% bigger. <br/>
/// SetAllControlsFont(this.Controls, -4, false); // This makes fonts smaller by 4.</c>
/// </summary>
/// <param name="ctrls">Control collection containing controls</param>
/// <param name="amount">Amount to change: posive value makes it bigger, 
/// negative value smaller</param>
/// <param name="amountInPercent">True - grow / shrink in percent, 
/// False - grow / shrink absolute</param>
public static void SetAllControlsFontSize(
                   System.Windows.Forms.Control.ControlCollection ctrls,
                   int amount = 0, bool amountInPercent = true)
{
    if (amount == 0) return;
    foreach (Control ctrl in ctrls)
    {
        // recursive
        if (ctrl.Controls != null) SetAllControlsFontSize(ctrl.Controls,
                                                          amount, amountInPercent);
        if (ctrl != null)
        {
            var oldSize = ctrl.Font.Size;
            float newSize = 
               (amountInPercent) ? oldSize + oldSize * (amount / 100) : oldSize + amount;
            if (newSize < 4) newSize = 4; // don't allow less than 4
            var fontFamilyName = ctrl.Font.FontFamily.Name;
            ctrl.Font = new Font(fontFamilyName, newSize);
        };
    };
}

This allows to grow or shrink the font size in percent like:

SetAllControlsFont(this.Controls, 20); 

Or you can shrink the font size absolutely by a value of -4 like:

SetAllControlsFont(this.Controls, amount: -4, amountInPercent: false); 

In both examples, all fonts will be affected by the change. You do not need to know the font family names, each control can have different ones.

Combined with this answer you can auto-scale fonts in your application based on Windows settings (which you can find if you right click on the desktop, then select Display settings, Scale and layout and modify the value "Change the size of text, apps, and other items" - in Windows 10 versions newer than build 1809 this is (re-)named as "Make everything bigger"):

var percentage = GetWindowsScaling() - 100;
SetAllControlsFont(this.Controls, percentage); 

You should also limit the size to a certain maximum/minimum, based on your forms layout, e.g.

if (percentage > 80)  percentage = 80;
if (percentage < -20) percentage = -20;

Likewise this is true for absolute values - note that in the code there is already a limit set: Practically, a font cannot be smaller than 4 em - this is set as minimum limit (of course you can adapt that to your needs).

Matt
  • 25,467
  • 18
  • 120
  • 187
0

Just set the font of parent form. It will propagate through controls unless you set font on child control manually

Example code:

public MyForm()
{
    InitializeComponent();
    Font = new Font(new FontFamily("Microsoft Sans Serif"), 8f); //replace 8f with desired font size
}

From Control.Font docs:

Remarks

The Font property is an ambient property. An ambient property is a control property that, if not set, is retrieved from the parent control. For example, a Button will have the same BackColor as its parent Form by default.

Tested in .NetFramework 4.6.2 and .Net6