Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow set font application-wide #3001

Closed
RussKie opened this issue Mar 21, 2020 · 26 comments · Fixed by #4911
Closed

Allow set font application-wide #3001

RussKie opened this issue Mar 21, 2020 · 26 comments · Fixed by #4911
Assignees
Labels
api-approved (4) API was approved in API review, it can be implemented api-suggestion (1) Early API idea and discussion, it is NOT ready for implementation

Comments

@RussKie
Copy link
Member

RussKie commented Mar 21, 2020

Is your feature request related to a problem? Please describe.

The default font has been updated in .NET Core 3.0 (#656) and documented. However for some users there is still an element of surprise when they migrate their apps to .NET Core.
We've received several questions regarding different sizes of forms (e.g. #1122, #1827, etc.).

Whilst the new default font is here to stay, some users may wish to retain the original font (e.g. due to a design of their app). However for an application with more than a handful of forms, setting the original font may be tedious and cumbersome exercise.

Describe the solution you'd like

Add the ability to set an application-wide font, similar to SetHighDpiMode() or SetCompatibleTextRenderingDefault() methods.

  • This method must be "run only once" kind, i.e. a user may not be allowed to invoke it once an app has started.
  • Any form that doesn't explicitly specify its own font, must inherit the application default font.

API Proposal

namespace System.Windows.Forms
{
    public partial class Application
    {
        public void SetDefaultFont(Font font);
    }
}

API Usage

class Program
{
    [STAThread]
    static void Main()
    {
        Application.SetHighDpiMode(HighDpiMode.SystemAware);
        Application.EnableVisualStyles();
        Application.SetCompatibleTextRenderingDefault(false);

        Application.SetDefaultFont(new Font(new FontFamily("Microsoft Sans Serif"), 8f));

        Application.Run(new Form1());
    }
}

Will this feature affect UI controls?

No

@RussKie RussKie added the api-suggestion (1) Early API idea and discussion, it is NOT ready for implementation label Mar 21, 2020
@merriemcgaw merriemcgaw added this to the 5.0 milestone Mar 27, 2020
@merriemcgaw merriemcgaw added the api-ready-for-review (2) API is ready for formal API review; applied by the issue owner label Mar 27, 2020
@merriemcgaw
Copy link
Member

We should totally do this for .NET 5, I think this is a great idea.

@terrajobst
Copy link
Member

terrajobst commented Mar 27, 2020

  • This method must be "run only once" kind, i.e. a user may not be allowed to invoke it once an app has started.

Are you going to throw an exception when the SetDefaultFont is called after Run? If so, I'd throw InvalidOperationException.

@RussKie
Copy link
Member Author

RussKie commented Mar 27, 2020

  • This method must be "run only once" kind, i.e. a user may not be allowed to invoke it once an app has started.

Are you going to throw an exception when the SetDefaultFont is called after Run? If so, I'd throw InvalidOperationException.

I propose we follow the same pattern set by SetCompatibleTextRenderingDefault() method and throw if SetDefaultFont() is called after the application has started:

public static void SetCompatibleTextRenderingDefault(bool defaultValue)
{
if (NativeWindow.AnyHandleCreated)
{
throw new InvalidOperationException(SR.Win32WindowAlreadyCreated);
}
Control.UseCompatibleTextRenderingDefault = defaultValue;
}

The rationale behind it, if a form has been created, it may have been laid out and rendered using one font, the font may have been cached etc., and setting a new font could be detrimental from both performance and UX.

@bartonjs
Copy link
Member

Is there already a way to get the default font? (Get/set symmetry)

@weltkante
Copy link
Contributor

probably Control.DefaultFont ?

@terrajobst
Copy link
Member

terrajobst commented Mar 27, 2020

Might be worthwhile to add a corresponding GetDefaultFont() to Application so that folks can just tweak parts, like:

[STAThread]
static void Main()
{
    Application.SetHighDpiMode(HighDpiMode.SystemAware);
    Application.EnableVisualStyles();
    Application.SetCompatibleTextRenderingDefault(false);

    // Set default to 10pt
    var font = Application.GetDefaultFont();
    font = new Font(font.FontFamily, 10f);
    Application.SetDefaultFont(font);

    Application.Run(new Form1());
}

It would be odd if users had to call Control.DefaultFont to do this.

@weltkante

This comment has been minimized.

@terrajobst

This comment has been minimized.

@RussKie
Copy link
Member Author

RussKie commented Mar 30, 2020

The rationale behind this FR is to allow users to set the original font, that may have been used to design the app (i.e. Font-scaling pixel-perfect design scenario), to help with a migration from .NET Framework to .NET Core/.NET.
To this end, users are expected to set both FontFamily and FontSize explicitly, and not leverage the current default font.

I don't feel that leveraging the default font to change its properties fits the above scenario. And, to be honest, I'm struggling to think of a use-case that would require this.
Do you have a use-case that would necessitate this?

@RussKie

This comment has been minimized.

@terrajobst
Copy link
Member

No objections, you're the domain expert. We just generally strive to avoid asymmetries like set-only APIs.

@RussKie RussKie modified the milestones: 5.0 Previews 1-4, 5.0 Apr 20, 2020
@RussKie
Copy link
Member Author

RussKie commented Apr 20, 2020

We just generally strive to avoid asymmetries like set-only APIs.

So do we, but in this case we want to have set-once kind of API, akin to SetHighDpiMode() or EnableVisualStyles().
Thank you

@terrajobst terrajobst added api-approved (4) API was approved in API review, it can be implemented and removed api-ready-for-review (2) API is ready for formal API review; applied by the issue owner labels Apr 21, 2020
@terrajobst
Copy link
Member

Reviewed and approved offline:

namespace System.Windows.Forms
{
    public partial class Application
    {
        public void SetDefaultFont(Font font);
    }
}

@RussKie RussKie added Priority:1 Work that is critical for the release, but we could probably ship without 🚧 work in progress Work that is current in progress labels Aug 5, 2020
@RussKie RussKie removed their assignment Aug 7, 2020
@RussKie
Copy link
Member Author

RussKie commented May 14, 2021

We can now implement this proposal. #4909 will facilitate a communication of the the font information to the designer process.

@kirsan31
Copy link
Contributor

kirsan31 commented May 14, 2021

@RussKie #4009 is grate! But, I want to point out some one thing that related to this issue and #4009 too. It's system font scaling:
image
Currently explicitly set font of the control (most time in designer), will stop it's scaling with this technique. What will be if we set app wide font?
My thoughts on this:
Current behavior is not good at all. The System.Windows.Forms.AutoScaleMode.Font is default to the form and disabling this scale if you set the font is not obvious.
I want an ability to set the font and still be the part of system font scaling (at least if we use default font but different size / style). #3263 related. Good question what to do if we use not default (system) font at all? But in 90% of time we need a proportional increase of all controls, regardless of the font. For other 10% we have AutoScaleMode.Dpi or AutoScaleMode.None.

P.s. I want to remind some old annoying DataGridView Designer issue, that become more annoying due to working font scaling in .net. And I think it definitely needs to be addressed 😋

@ghost ghost added the 🚧 work in progress Work that is current in progress label May 14, 2021
@RussKie

This comment has been minimized.

@mdtauk
Copy link

mdtauk commented May 16, 2021

With Windows 10 21H2 changing the System font to Segoe UI Variable with its Small, Text, and Display OpticalSize variants - will WinForms be ready to handle that, and will there be suitable fallbacks for Windows 8, and older Windows 10 versions?

https://blogs.windows.com/windows-insider/2021/05/06/announcing-windows-10-insider-preview-build-21376/

@kirsan31
Copy link
Contributor

kirsan31 commented May 16, 2021

@RussKie

Changing the text size I didn't observe the any changes to the font metrics in net472
...
but net6.0 appears to be updating the default font metrics

Yes, becouse .net framework doesn't respect system font, and .net core does (#656). But font scaling in .net will work only if you not set font explicitly (then it will be taken from Control.DefaultFont). And I see that size of the form is the same on all of your screenshots, it means that font scaling not working... That's all is my previous post about.

@RussKie
Copy link
Member Author

RussKie commented May 16, 2021

And I see that size of the form is the same on all of your screenshots, it means that font scaling not working...

They are in fact different in net6.0 scenarios :)

@RussKie
Copy link
Member Author

RussKie commented May 16, 2021

With Windows 10 21H2 changing the System font to Segoe UI Variable with its Small, Text, and Display OpticalSize variants - will WinForms be ready to handle that, and will there be suitable fallbacks for Windows 8, and older Windows 10 versions?

Thanks for the link. We get the default font from .NET runtime, and take it for granted:

public static Font DefaultFont
{
get
{
if (s_defaultFont is null)
{
s_defaultFont = SystemFonts.MessageBoxFont;
Debug.Assert(s_defaultFont is not null, "defaultFont wasn't set!");
}
return s_defaultFont;
}
}

If you're running that build already you could probably run a test yourself and see:
https://github.com/dotnet/runtime/blob/64303750a9198a49f596bcc3aa13de804e421579/src/libraries/System.Drawing.Common/src/System/Drawing/SystemFonts.Windows.cs#L85-L99

I think it may just work out of the box... @safern any thoughts?

@RussKie

This comment has been minimized.

@kirsan31

This comment has been minimized.

RussKie added a commit to RussKie/winforms that referenced this issue May 18, 2021
The default font has been updated in .NET Core 3.0 (dotnet#656) and documented.
However for some users who built their apps in pixel-perfect manner this
change has proved to be a significant hurdle in migrating their apps to
.NET.

Allow setting application-wide default font in a similar manner we set
high dpi or visual styles:

    Application.SetDefaultFont(new Font(new FontFamily("Calibri"), 11f));

* The application-wide default font can only be set before the first
window is created by an application.
* The font will be scaled by the system text scale factor, whenever it is
getting changed.

Resolves dotnet#3001
@ghost ghost removed the 🚧 work in progress Work that is current in progress label May 18, 2021
RussKie added a commit that referenced this issue May 18, 2021
The default font has been updated in .NET Core 3.0 (#656) and documented.
However for some users who built their apps in pixel-perfect manner this
change has proved to be a significant hurdle in migrating their apps to
.NET.

Allow setting application-wide default font in a similar manner we set
high dpi or visual styles:

    Application.SetDefaultFont(new Font(new FontFamily("Calibri"), 11f));

* The application-wide default font can only be set before the first
window is created by an application.
* The font will be scaled by the system text scale factor, whenever it is
getting changed.

Resolves #3001
RussKie added a commit that referenced this issue May 18, 2021
The default font has been updated in .NET Core 3.0 (#656) and documented.
However for some users who built their apps in pixel-perfect manner this
change has proved to be a significant hurdle in migrating their apps to
.NET.

Allow setting application-wide default font in a similar manner we set
high dpi or visual styles:

    Application.SetDefaultFont(new Font(new FontFamily("Calibri"), 11f));

* The application-wide default font can only be set before the first
window is created by an application.
* The font will be scaled by the system text scale factor, whenever it is
getting changed.

Resolves #3001
@Zheng-Li01
Copy link
Member

Verified the issue with latest 6.0.0-preview.5.21275.7, the main functions have been fully merged/implemented, found a new issue too and filed it as #4978

@imba-tjd
Copy link

imba-tjd commented Jun 19, 2021

Question: Can WPF set default font? rather than set FontFamily in every xaml. I had googled it but got no answer.

Edit:

I managed to set it by typeof(SystemFonts).GetField("_messageFontFamily", BindingFlags.Static | BindingFlags.NonPublic).SetValue(null, new System.Windows.Media.FontFamily("xxx"));

It's the first time I monkey patch in C#, interesting. Previously I only did this kind of things in Python.

@weltkante
Copy link
Contributor

@imba-tjd you can try setting the default value of the FontFamily and related properties in the metadata before creating any window or control. In general this is the wrong repo to ask in though, you'll find more people knowing about WPF in the WPF repo.

@ghost ghost locked as resolved and limited conversation to collaborators Feb 1, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
api-approved (4) API was approved in API review, it can be implemented api-suggestion (1) Early API idea and discussion, it is NOT ready for implementation
Projects
None yet
Development

Successfully merging a pull request may close this issue.

10 participants