Changing default font in Windows messes up my Win Forms buttons - c#

We have a collection of C# (.NET 2.0) applications that have custom dialog windows. When the user changes the default font size and resolution etc in Windows the edges of the form lose controls (they get pushed off the visible portion of the form) typically dropping off buttons from the bottom. This is problematic for us and will cause issues across other applications using the standard look-and-feel framework we are creating around these applications.
How would you make your entire application independent from windows font changes? How would you integrate this into multiple solutions?
Or better still how can one adapt the entire application to be able to adjust itself with the Windows appearance changes?

WinForms have the AutoScaleMode property, which can be set to either Font, DPI, Inherit, or None. The default is "Font" which, in my experience, doesn't always scale things right. You might try one of the other options (maybe "None" if you don't want your form to scale at all?)
As for making the entire application able to adjust itself, look into the "Inherit" option (you still need to set something other than "Inherit" on the parent or startup form of your application, though).

You could handle the SystemEvents.UserPreferenceChanged event in the Microsoft.Win32 namespace. The UserPreferenceChangedEventArgs parameter will have a Category of UserPreferenceCategory.Window when the system font changes. I think you'll then have to trigger a manual window layout and redraw in response to this event.

Related

Form1 visual size doesn't match Form1 debug visual size

I'm having a problem that when i debug my application it doesn't match the size i've set in the designer. i've tried to set form minimum size to the desirable values.
The form is built up with a splitpanel, with a panel docked as fill on each side.
The labels have default anchors. Textboxes have anchors left,top,right.
Buttons have anchors left, top.
if i drag the bottom down during runtime i get the size/design i want, but why doesn't it start like that and how can i fix it?
Looks like you're working on quite a high resolution screen. Windows Forms isn't very good with scaling the content and has all kinds of quirks that you need to be aware of. I would move to WPF if possible, but if you really need to continue using Windows Forms, here's what you should do.
Use AutoScaleMode.Dpi on your main form. It'll scale and relocate the controls to match your design when the DPI of the monitor is higher than the default 96 (100%). You could also try AutoScaleMode.Font but it might not work well if you use fonts other than the default (Tahoma 8,25 pt or something like that).
Use TableLayoutPanel or FlowLayoutPanel to make positioning controls easier. FlowLayoutPanel dynamically lays down your controls horizontally or vertically. If you're familiar with WPF or Windows Phone development, it's basically a StackPanel control.
Make sure your screen DPI is 96 (100%) and keep it the same throughout the development. You'll still have to make sure to test the application on other DPI's so that users with different settings will be able to use your application.
Here's more information about DPI scaling:
How to configure an app to run correctly on a machine with a high DPI setting (e.g. 150%)?
How to control the font DPI in .NET WinForms app
Creating a DPI-Aware Application
Edit: Visual Studio's Windows Forms designer shows the form using a different theme than your Windows version so that's one reason why your form looks different in runtime. In runtime the form uses the theme of the operating system currently running the application.

How to write WinForms code that auto-scales to system font and dpi settings?

Intro: There's a lot of comments out there that say "WinForms doesn't auto-scale to DPI/font settings well; switch to WPF." However, I think that is based on .NET 1.1; it appears they actually did a pretty good job of implementing auto-scaling in .NET 2.0. At least based on our research and testing so far. However, if some of you out there know better, we'd love to hear from you. (Please don't bother arguing we should switch to WPF... that's not an option right now.)
Questions:
What in WinForms does NOT auto-scale properly and therefore should be avoided?
What design guidelines should programmers follow when writing WinForms code such that it will auto-scale well?
Design Guidelines we have identified so far:
See community wiki answer below.
Are any of those incorrect or inadequate? Any other guidelines we should adopt? Are there any other patterns that need to be avoided? Any other guidance on this would be very appreciated.
Controls which do not support scaling properly:
Label with AutoSize = False and Font inherited. Explicitly set Font on the control so it appears in bold in the Properties window.
ListView column widths don't scale. Override the form's ScaleControl to do it instead. See this answer
SplitContainer's Panel1MinSize, Panel2MinSize and SplitterDistance properties
TextBox with MultiLine = True and Font inherited. Explicitly set Font on the control so it appears in bold in the Properties window.
ToolStripButton's image. In the form's constructor:
Set ToolStrip.AutoSize = False
Set ToolStrip.ImageScalingSize according to CreateGraphics.DpiX and .DpiY
Set ToolStrip.AutoSize = True if needed.
Sometimes AutoSize can be left at True but sometimes it fails to resize without those steps. Works without that changes with .NET Framework 4.5.2 and EnableWindowsFormsHighDpiAutoResizing.
TreeView's images. Set ImageList.ImageSize according to CreateGraphics.DpiX and .DpiY. For StateImageList, works without that changes with .NET Framework 4.5.1 and EnableWindowsFormsHighDpiAutoResizing.
Form's size. Scale fixed size Form's manually after creation.
Design Guidelines:
All ContainerControls must be set to the same AutoScaleMode = Font.
(Font will handle both DPI changes and changes to the system font
size setting; DPI will only handle DPI changes, not changes to the
system font size setting.)
All ContainerControls must also be set with the same AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);, assuming 96dpi (see the next bullet) and default Font of MS Sans Serif (see the bullet two down). That is auto-added by the designer
based on the DPI you open the designer in... but was missing from
many of our oldest designer files. Perhaps Visual Studio .NET (the
version before VS 2005) was not adding that in properly.
Do all your designer work in 96dpi (we might be able to switch to
120dpi; but the wisdom on the internet says to stick to 96dpi;
experimentation is in order there; by design, it shouldn't matter as it just changes the AutoScaleDimensions line that the designer inserts).
To set Visual Studio to run at a virtual 96dpi on a high-resolution display,
find its .exe file, right-click to edit properties, and under Compatibility
select "Override high DPI scaling behavior. Scaling performed by: System".
Be sure you never set the Font at the container level... only on the
leaf controls OR in the constructor of your most base Form if you want an application-wide default Font other than MS Sans Serif. (Setting the Font on a Container seems to turn off
the auto-scaling of that container because it alphabetically comes after the setting of AutoScaleMode and AutoScaleDimensions settings.) NOTE that if you do change the Font in your most base Form's constructor, that will cause your AutoScaleDimensions to compute differently than 6x13; in particular, if you change to Segoe UI (the Win 10 default font), then it will be 7x15... you will need to touch every Form in the Designer so that it can recompute all the dimensions in that .designer file, including the AutoScaleDimensions = new System.Drawing.SizeF(7F, 15F);.
Do NOT use Anchor Right or Bottom anchored to a UserControl... its
positioning will not auto-scale; instead, drop a Panel or other
container into your UserControl and Anchor your other Controls to
that Panel; have the Panel use Dock Right, Bottom, or Fill in your
UserControl.
Only the controls in the Controls lists when ResumeLayout at the end
of InitializeComponent is called will be auto-scaled... if you
dynamically add controls, then you need to SuspendLayout();
AutoScaleDimensions = new SizeF(6F, 13F); AutoScaleMode = AutoScaleMode.Font;
ResumeLayout(); on that control before you add it in. And your
positioning will also need to be adjusted if you are not using Dock
modes or a Layout Manager like FlowLayoutPanel or TableLayoutPanel.
Base classes derived from ContainerControl should leave AutoScaleMode set to Inherit (the default value set in class ContainerControl; but NOT the default set by the designer). If you set it to anything else, and then your derived class tries to set it to Font (as it should), then the act of setting that to Font will clear out the designer's setting of AutoScaleDimensions, resulting in actually toggling off auto-scaling! (This guideline combined with the prior one means you can never instantiate base classes in a designer... all classes need to either be designed as base classes or as leaf classes!)
Avoid using Form.MaxSize statically / in the Designer. MinSize and MaxSize on Form do not scale as much as everything else. So, if you do all your work in 96dpi, then when at higher DPI your MinSize won't cause problems, but may not be as restrictive as you expected, but your MaxSize may limit your Size's scaling, which can cause problems. If you want MinSize == Size == MaxSize, don't do that in the Designer... do that in your constructor or OnLoad override... set both MinSize and MaxSize to your properly-scaled Size.
All of the Controls on a particular Panel or Container should either use Anchoring or Docking. If you mix them, the auto-scaling done by that Panel will often misbehave in subtle bizarre ways.
When it does its auto-scaling, it will be trying to scale the overall Form... however, if in that process it runs into the upper limit of the screen size, that is a hard limit that can then screw up (clip) the scaling. Therefore, you should make sure all Forms in the Designer at 100%/96dpi are sized no larger than 1024x720 (which corresponds to 150% on a 1080p screen or 300% which is the Windows recommended value on a 4K screen). But you need to subtract out for the giant Win10 title/caption bar... so more like 1000x680 max Size... which in the designer will be like 994x642 ClientSize. (So, you can do a FindAll References on ClientSize to find violators.)
My experience has been fairly different to the current top voted answer. By stepping through the .NET framework code and perusing the reference source code, I concluded that everything is in place for auto-scaling to work, and there was only a subtle issue somewhere messing it up. This turned out to be true.
If you create a properly reflowable / auto-sized layout, then almost everything works exactly like it should, automatically, with the default settings used by Visual Studio (namely, AutoSizeMode = Font on the parent form, and Inherit on everything else).
The only gotcha is if you have set the Font property on the form in the designer. The generated code will sort the assignments alphabetically, which means that AutoScaleDimensions will be assigned before Font. Unfortunately, this completely breaks WinForms auto scaling logic.
The fix is simple though. Either don't set the Font property in the designer at all (set it in your form constructor), or manually reorder these assignments (but then you have to keep doing this every time you edit the form in the designer). Voila, nearly perfect and fully automatic scaling with minimal hassle. Even the form sizes are scaled correctly.
I will list known problems here as I encounter them:
Nested TableLayoutPanel calculates control margins incorrectly. No known work-around short of avoiding margins and paddings altogether - or avoiding nested table layout panels.
Target your Application for .Net Framework 4.7 and run it under Windows 10 v1703 (Creators Update Build 15063). With .Net 4.7 under Windows 10 (v1703), MS made a lot of DPI improvements.
Starting with the .NET Framework 4.7, Windows Forms includes
enhancements for common high DPI and dynamic DPI scenarios. These
include:
Improvements in the scaling and layout of a number of Windows Forms controls, such as the MonthCalendar control and the
CheckedListBox control.
Single-pass scaling. In the .NET Framework 4.6 and earlier versions, scaling was performed through multiple passes, which caused
some controls to be scaled more than was necessary.
Support for dynamic DPI scenarios in which the user changes the DPI or scale factor after a Windows Forms application has been
launched.
To support it, add an application manifest to your application and signal that your app supports Windows 10:
<compatibility xmlns="urn:schemas-microsoft.comn:compatibility.v1">
<application>
<!-- Windows 10 compatibility -->
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" />
</application>
</compatibility>
Next, add an app.config and declare the app Per Monitor Aware. This is NOW done in app.config and NOT in the manifest like before!
<System.Windows.Forms.ApplicationConfigurationSection>
<add key="DpiAwareness" value="PerMonitorV2" />
</System.Windows.Forms.ApplicationConfigurationSection>
This PerMonitorV2 is new since Windows 10 Creators Update:
DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2
Also known as Per Monitor v2. An advancement over the original
per-monitor DPI awareness mode, which enables applications to access
new DPI-related scaling behaviors on a per top-level window basis.
Child window DPI change notifications - In Per Monitor v2 contexts, the entire window tree is notified of any DPI changes that
occur.
Scaling of non-client area - All windows will automatically have their non-client area drawn in a DPI sensitive fashion. Calls to
EnableNonClientDpiScaling are unnecessary.
Scaling of Win32 menus - All NTUSER menus created in Per Monitor v2 contexts will be scaling in a per-monitor fashion.
Dialog Scaling - Win32 dialogs created in Per Monitor v2 contexts will automatically respond to DPI changes.
Improved scaling of comctl32 controls - Various comctl32 controls have improved DPI scaling behavior in Per Monitor v2
contexts.
Improved theming behavior - UxTheme handles opened in the context of a Per Monitor v2 window will operate in terms of the DPI
associated with that window.
Now you can subscribe to 3 new events to get notified about DPI changes:
Control.DpiChangedAfterParent, which is fired Occurs when the DPI setting for a control is changed programmatically after a DPI
change event for it's parent control or form has occurred.
Control.DpiChangedBeforeParent, which is fired when the DPI setting for a control is changed programmatically before a DPI change
event for its parent control or form has occurred.
Form.DpiChanged, which is fired when the DPI setting changes on the display device where the form is currently displayed.
You also have 3 helper methods about DPI handling/scaling:
Control.LogicalToDeviceUnits, which converts a value from logical to device pixels.
Control.ScaleBitmapLogicalToDevice, which scales a bitmap image to the logical DPI for a device.
Control.DeviceDpi, which returns the DPI for the current device.
If you still see issues, you can opt-out of the DPI improvements via app.config entries.
If you don't have access to source code, you can go to application properties in Windows Explorer, go to compatibility and select System (Enhanced)
which activates GDI scaling to also improve DPI handling:
For applications that are GDI-based Windows can now DPI scale these on
a per-monitor basis. This means that these applications will,
magically, become per-monitor DPI aware.
Do all those steps and you should get a better DPI experience for WinForms applications. But remember, you need to target your app for .net 4.7 and need at least Windows 10 Build 15063 (Creators Update). In next Windows 10 Update 1709, we might get more improvements.
A guide I wrote at work:
WPF works in 'device independent units' which means all controls scale
perfectly to high dpi screens. In WinForms, it takes more care.
WinForms works in pixels. Text will be scaled according to the system dpi but it will often be cropped by an unscaled control. To avoid such problems, you must eschew explicit sizing and positioning. Follow these rules:
Wherever you find it (labels, buttons, panels) set the AutoSize property to True.
For layout, use FlowLayoutPanel (a la WPF StackPanel) and TableLayoutPanel (a la WPF Grid) for layout, rather than vanilla
Panel.
If you are developing on a high dpi machine, the Visual Studio designer can be a frustration. When you set AutoSize=True, it will resize the control to your screen. If the control has AutoSizeMode=GrowOnly, it will remain this size for people on normal dpi, ie. be bigger than expected. To fix this, open the designer on a computer with normal dpi and do right-click, reset.
I found it to be very hard to get WinForms to play nice with high DPI. So, I wrote a VB.NET method to override the form behavior:
Public Shared Sub ScaleForm(WindowsForm As System.Windows.Forms.Form)
Using g As System.Drawing.Graphics = WindowsForm.CreateGraphics
Dim sngScaleFactor As Single = 1
Dim sngFontFactor As Single = 1
If g.DpiX > 96 Then
sngScaleFactor = g.DpiX / 96
'sngFontFactor = 96 / g.DpiY
End If
If WindowsForm.AutoScaleDimensions = WindowsForm.CurrentAutoScaleDimensions Then
'ucWindowsFormHost.ScaleControl(WindowsForm, sngFontFactor)
WindowsForm.Scale(sngScaleFactor)
End If
End Using
End Sub
I recently came across this problem, especially in combination with Visual Studio rescaling when the editor is opened on high-dpi system. I found it best to keep AutoScaleMode = Font, but to set the Forms Font to the default font, but specifying the size in pixel, not point, i.e.: Font = MS Sans; 11px. In code, I then reset the font to the default: Font = SystemFonts.DefaultFont and all is fine.
Just my two cents. I thought I share, because “keeping AutoScaleMode=Font”, and “Set font size in pixel for the Designer” was something I did not find on the internet.
I have some more details on my Blog: http://www.sgrottel.de/?p=1581&lang=en
In addition to the anchors not working very well: I would go a step farther and say that exact positioning (aka, using the Location property) does not work very well with the font scaling. I've had to address this issue in two different projects. In both of them, we had to convert the positioning of all the WinForms controls to using the TableLayoutPanel and FlowLayoutPanel. Using the Dock (usually set to Fill) property inside the TableLayoutPanel works very well and scales fine with the system font DPI.
I had to go through and fix scaling on a whole bunch of WinForms programs, at least 20 of them, written by different people in different styles. Lots of user controls, splitters, anchors, docking, panels, custom controls, dynamic layout code, etc. It took a lot of experimenting, but I think I've come up with a good way of handling it.
This answer is what got me started in the right direction: Trying to make WinForms look good in 4K but forms too large after using AutoScaleMode.Dpi?
The problem is the LayoutManager tends to mangle the layout if you have anything slightly complicated. It's really a problem with calling SuspendLayout() and then doing stuff and then ResumeLayout(). (This also plays havoc with anchors when you mix user controls with TabControl. But that's a separate issue.)
The key is to move the AutoScaleDimension and AutoScaleMode properties on the form outside of the SuspendLayout()/ResumeLayout(), so everything will be properly laid out before it scales. Since the form designer orders statements however it wants to, just remove those two lines from the .Designer.cs file, and move them to right after the InitializeComponent() method in the constructor.
The other important part is to set all your user controls AutoScaleMode to Inherit, not font. That way everything gets scaled all at once instead of doing a scale in the user control, and then rescaling stuff when it's added to the form.
Before changing AutoScaleMode on the form, I visit all the controls recursively, and anything which isn't docked and has an anchor other than Top|Left, I temporarily set the anchor to Top|Left, and then restore it back to its original value after setting AutoScaleMode.
Doing those three things gets me about 90% of the way, and almost everything works automagically. Together, these 3 things ensure that everything is scaled one time, all together, and to the same proportions. Any deviation from this pattern seems to lead to chaos in the layout.
It's also a good idea to PInvoke user32.dll SetProcessDPIAware() at the beginning of the application. This seems to allow programmatic scaling to work even at 150%. I haven't had any luck making it behave properly when setting SetProcessDpiAwareness() or SetProcessDpiAwarenessContext(), they both seem to lead to layout chaos no matter what I do.

disable windows font size increase in my C# application

I built a windows application in C# that is very size specific. I hard coded the size of the form so that it could not be resized. The problem is when someone changes there windows settings to font size for example (125% or 150%) my application breaks and buttons get moved over, and things fall off of screen. The entire application is a mess.
Is there a way in C# to disable the custom font effects of windows and use the pre-set font I told it to use in my form. I want all windows custom features to be turned to default on my application.
Is this possible at all, like import a DLL and call a function or change my program.cs some how.
Don't do that. Write your application so it adapts to the user's selected font size.

c# how can i make sure that my application will look the same on other systems?

how do i make sure that the application iam developing will look the same on other windows-systems?
iam developing now on a windows7, with .net 3.5 framework .. (VS 2008)
as an example, i have a toolbar, that i changed its rendermode to system, it looks ok on my windows7, but when i run the application on windows xp, it is different, even the onmouseover backcolor is different. ..
is there a way to make the application looks like on every windows system (talking abt xp, vista and windows 7 only), lets say like exporting the settings of all the controls with the application !? or any trick to make sure it will be always the same ?
thankss in advance
Unless you render the window yourself you're going to always have slight differences between OS'es, they all have different ways of rendering your primitive controls like text boxes, panels, etc.
Also under windows the background color of a window/control is actually (by default) tied into the theme set in windows.
So allowing people to use their own themes is a plus. You really shouldn't force a style on people unless you're theming your own application.
You would have to you get a grab on all of the colours, fonts, transperancy, etc. and use them to override every controls' Paint event/method so that they may use your values.
The only way I see fit would be by writing yourself custom controls while handling these controls appearance throught the Paint() method so that the control may use what you give it. The pain!...
As SLaks commented, you shouldn't care about such details and let the defaut user preferences take over on the native OS, because some surprises may be encountered over time as if Windows doesn't find the font, for instance, it will replace it with its default, which could lead to horrible results. That is just one example. Need others?

Closing Windows Forms on a Touchscreen

Our clients have fat fingers, and so do we. We take touchscreen netbooks apart to insert them into our custom hardware, and I write a software interface that shows up on the touchscreen. The problem is that it has about a 3/4" bezel over the screen, which means hitting that little red "X" becomes a challenge, especially considering reduced capacitive ability on the edges and corners.
Is there a way to make this standard close button larger? Of course in the application I can always make really nice 80x80 buttons that are perfectly usable, but there seems to be no way to override the default frame of the form. We have tried enabling Large Fonts and all the built-in accessibility features, but nothing seems to make it large enough to hit successfully.
Simply adding a toolbar button is also not much of an option. We prefer to utilize the standard look and feel of a normal Windows application.
Alternatively, should we be looking at making some sort of "kiosk mode" where we simply go fullscreen and do nothing involving the taskbar or title bar? How difficult is this to accomplish, if so?
Well, since you're setting up the hardware, I presume you're able to configure preinstalled software, including Windows. Can't you just go into Display Settings and make the title bar larger, so that the close button grows accordingly?
See MS Article about distributing windows themes: http://support.microsoft.com/kb/310514
Getting a large close button is fairly easy to do. It is hidden well since Vista, in Win7 it is Control Panel + Personalization + Window Color, Advanced appearance settings, Item = Caption buttons, change the Size. You probably won't like this much though, you'll get a rather large caption bar, lots of waste screen real estate.
Tackling this from the other end: your request is unusual. Most anybody that sets up a touch screen app wants to know how to prevent the user from closing the window. Windows Forms makes it too easy to design a bunch of forms and switch between them. That isn't much of a user interface on a regular desktop, especially not here. You can design your forms as user controls as well and switch them in and out of the main window as the user navigates through the UI. Not unlike, say, Microsoft Outlook. You can even turn your existing form into a control. Set its TopLevel property to False, FormBorderStyle to None, Visible to true.

Categories