WinForms De-Blurred

The Problem

We have projects working with WinForms GUIS. Different Developers work with different Computers, and these have different DPI settings. Every time the Project is opened on a system with different DPIs the WinForms get scaled, which is painfully bad.

Solution Summary

  • Set in your Forms Designer: Font = MS Sans; 11px
  • In the Forms Ctor, after InitializeComponent, set: Font = SystemFonts.DefaultFont
  • Enable DPI-Awareness, either through a manifest or by API function SetProcessDPIAwareness

Solution Details

mmconf_blurless
Make your WinForms GUI less blurry by being DPI aware

A modern Windows approach to High-DPI Displays

More and more Computers get equipped with High-DPI Displays, a trend I like very much. Pixels cannot be small enough. With modern Windows, however, GUI of older Applications get infamously blurry. This is due to Microsoft’s approach for backward-compatibility: applications will be rendered at their native DPI and scaled up to the system’s DPI. This way, the application does not need to know anything about DPI, but user controls will keep a decent size on any setting. While most people hate the blurriness of old GUIs on modern Windows, this approach does make a lot of sense:

  • Backward-Compatibility is instantly given, as nothing changes for the old Applications.
  • The users input experience is retained, as the GUI will keep its apparent size. (Just thing, if you’re old enough, of the original GUI of Winamp, where the buttons in the default skin were sometimes just a few Pixels in size.)
  • (last but not least) for new Applications, Developers hopefully get upset with the blurry look, that they actually invest some time, to do it right!

So, don’t claim it’s all Microsoft’s fault that the GUI of your applications look blurry. Truth is, you were just too lazy to do it right.

Why WinForms?

If you browse the internet in search for how to handle high-DPI settings with WinForms, you are bound to stumble upon a smart-ass telling you to switch to WPF. That person does have a point: WPF is designed to be a GUI for all resolutions. But, that person is also an idiot. Don’t bother discussing.

If you decided to use WinForms, then use WinForms. It is not deprecated, it is not legacy, it is not broken. There are good reasons to use WinForms. One, for example, would be the nice compatibility with Mono (Linux and MacOS). Another one would be compatibility with native GUI controls. Whatever your reason is, don’t let other people easily throw you off track.

If you’re not fixed on WinForms, but want to write a new Application for Windows, then have a look at WPF.

Why does Visual Studio Scale?

Normally Windows works at 96 DPI. That is, you need 96 pixels to fill up one inch of screen space. On a higher setting, let’s say 144 DPI, you need 144 pixels. So, either your GUI elements will look smaller, or your GUI elements must be larger to look the same. Modern graphical content is thus not described in pixels, but often in points (pt). Points are defined as 1/72 inch, that is in screen space, and not in pixels.

WinForms is not a modern GUI. All Elements are designed with pixels. However, higher DPI settings were present in Windows for a long time (accessibility feature). WinForms answers to this by having a mechanism to scale the whole GUI ‘manually’. If a scaling factor different from one is determined, all GUI elements, positions, sizes, etc., are multiplied by that factor, including the overall size of the window. By default, this scaling factor is determined comparing the Font settings of the form. Fonts are usually specified with a size in pt. Windows computes the font size in pixels based on the active DPI value. If WinForms now detects a mismatch between the pixel-based font sizes between design-time specifications and run-time evaluation, the form and all content is scaled. And this is exactly what happens in the Visual Studio Windows Forms designer.

Visual Studio does basically nothing at all. However, the font size evaluation is based on the system’s DPI setting. So on high-DPI systems, the font’s pixel size will be different from the stored design-time pixel size, and thus the whole form will be scaled accordingly. That is a good idea at run-time. I mean, that is the whole point of this mechanism. However, we are still at design time. The problem raises, because the Forms designer in Visual Studio actually runs the WinForms engine. As now all GUI elements change their sizes, the designer is informed (likely by the normal events) and adjusts all generated code to the new sizes and locations. This is, of course, ugly, painful and stupid, if you are going to continue the development on another machine with another, maybe, lower DPI setting.

Disable Scaling at Design-Time, Enable Scaling at Run-Time

What I am writing here is not a premium solution. It is the workaround I found for myself to work best.

The basic idea is to (manually) disable scaling at design time, and to (manually) enable scaling at run time.

I write scaling, but what we actually change is the Font!

Keep the Form’s AutoScaleMode = Font. That setting is correct and is not the problem at all. The problem is the automatically used font. It is the system’s default font, which specifies its size in pt. Again, a good idea at run time. What we change is the Font setting of the Form, to specify the size in pixels.

In the Designer, set the Font to: Microsoft Sans Serif; 11px

Windows default Font is Microsoft San Serif 8 pt. according to MSDN. Actually, it seems more like 8.25 pt. So this is 8.25/72 inch, which finally results in 8.25 * 96/72 = 11 pixel on a normal DPI system. That is why you set the font to this painfully small looking value. It is the right one! Now edit your GUI on all systems you have. Your Forms will not be scaled by Visual Studio any more. So, design time is fixed.

Now for the run time. That one is easy, too. All we need to do is to ‘reset’ the Form’s font to have its size specified in pt. again. The easiest way to do that is to reassign the system’s default font. Just set it in the constructor, right after InitializeComponent:

Font = SystemFonts.DefaultFont;

This, of course, only works if you are on a system where the system’s default font is as expected, and only if you do not change fonts for the controls inside your form. If you did change some control’s font, you specify the font with pixel size for design time and you update these at run time initialization to use pt.-based sizes again.

Scalable GUI Design

And that is that. If your application is already DPI aware, your forms will now scale nicely. That is, if you designed them correctly.

  • You should not mix docking-based and anchor-based design in the same form. That is bound to produce weird scaling issues.
  • You must use either, otherwise the controls will just randomly shift somewhere.
  • Be aware that the control might no longer fit into your Form, due to the maximum window size. Use flowing layout containers and auto scrollbars.

Enable DPI Awareness

All that, of course, only makes any sense if you enable DPI awareness for your application. There are basically two options:

Manifest-Segment I use:

<application xmlns="urn:schemas-microsoft-com:asm.v3">
  <windowsSettings>
    <dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true</dpiAware>
  </windowsSettings>
</application>

Code I use:

[DllImport("SHCore.dll")]
private static extern bool SetProcessDpiAwareness(PROCESS_DPI_AWARENESS awareness);

private enum PROCESS_DPI_AWARENESS {
    Process_DPI_Unaware = 0,
    Process_System_DPI_Aware = 1,
    Process_Per_Monitor_DPI_Aware = 2
}

[DllImport("user32.dll", SetLastError = true)]
static extern bool SetProcessDPIAware();

internal static void TryEnableDPIAware() {
    try {
        SetProcessDpiAwareness(PROCESS_DPI_AWARENESS.Process_Per_Monitor_DPI_Aware);
    } catch {
        try { // fallback, use (simpler) internal function
            SetProcessDPIAware();
        } catch { }
    }
}

Maybe, you know, that modern Windows can be even more complicated by per-monitor DPI settings. The idea is, that attached external screens have different sizes and resolutions, and should thus be handled with different DPIs. The good news is: this approach here works instantly with per-monitor DPI. When the form is dragged onto another monitor, the system automatically adjusts the font setting, as the font size is at run time specified in points. This automatically triggers a rescaling of the form. Wheee!

When I read this comic on xkcd i started thinking. I enjoy reading xkcd. Few comics are bad, most are nice, and some are brilliant. And those are the ones I will now start reblogging here, as distributed archive. ;-)

“If you can read this, congratulations—the archive you’re using still knows about the mouseover text”!
“If you can read this, congratulations—the archive you’re using still knows about the mouseover text”!
Digital Data [xkcd_1683]

All I did was hitting the “Update” button in my WordPress installation.

Server closed session midway during update. Installer is unable to repair or fix or continue or whatever. And I spent my precious afternoon reinstalling my website and all the content from the backup. Thank you.

And don’t come to me claiming open source is not at fault here.

There were no post on the last two Sundays. And that was on purpose. The whole idea to write one post every Sunday was kind of good, but did not work. Far too many posts were fillers without any content. That is not good. Those posts only bloated my blog. Which helped no one. And bloating things up is something I do not like at all. Thus, it is time for a change. It is time to clean up!

I deleted all old filler posts. No one needs them.

Exoworlds is history. I had the idea for this game since school. And now it is time to let go. I will never find enough time to create the game in the form I imagined. The little time I have for something like this is better invested in my other projects, I have to say.

To be precise: MegaMol and SpringerJagd are my two remaining projects I will be working on. In the vicinity of those, more small utilities will emerge.

In future I try to post regularly. But I will not promise anything.

I am very cautious about photos of myself on the internet. And I am even more cautious about private information. However, this week there was a nice moment I want to share: If you can, you can see me through the web cam of the MS Europa (no, I was not traveling with the ship).
MS-Europa-Webcam

I am a Microsoft fan boy. This, I gladly admit. But one thing is for sure, the guys from Redmond are only humans after all. And I don’t have time nor patience for beta tests.

This week I upgraded my three PCs to Windows 10. All of my computers were running Windows 8.1 installed from the same image, and I used all three roughly for the same stuff. So, I was rather impressed how different the results and installation experiences were, especially, since I used the same downloaded image on all three computers:

My computer at work decided to forget most icons of most installed programs after the upgrade. This was because I moved the useless installation cache from my tiny SSD onto a normal hard disk and relinked it back. This works find in every aspect, but the windows installation decided to throw away the link before doing anything. Smart move.

On my Surface Pro 3 Windows 10 welcomed me after the upgrade with some information on functions I could use if I would have had touch input. … Wut? Well, after two additional reboots the surface remembered its core feature touch screen and pen. Just, the quick-note button on the pen itself, however, cannot be persuaded to use OneNote. Instead it stubbornly insist on using the useless OneNote imposter app. Annoying.

And, finally, one my private Desktop, almost everything worked fine. Only exception was that the setup de-installed my antivirus. It was fine with the same antivirus software on the other two computers, so I guess it was about time to stumble now.

So much for that. Until now Windows 10 leaves and Ok impression. Only some things sucks, only some things seems improved, only some things got worse. It’ll be fine. However, I haven’t seen any real killer feature.

P.S.: Microsoft cannot count! cf. Visual Studio 14

Most new data sets for my scientific visualization find their way to my desk in form of arbitrarily structures text files. This is not really a problem. The first sensible step is converting them into a fast binary format for the visual analysis. With this, however, I face the problem of understanding the structure of 11 Gigabytes text files (no exaggeration here!). But, such files do have structure. So, only the few first and few last lines really matter. The bits in-between will be roughly the same way. What I need are the Linux-known commands “head” and “tail”. However, I am a Windows guy. So? The Powershell comes to the rescue:

gc log.txt | select -first 10 # head
gc log.txt | select -last 10 # tail

I found these on: http://stackoverflow.com/a/9682594 (where else)

At least the “head” version was fast and sufficient for me. I am happy.

It is time again. In the coming week, there will be the last lectures for some time. Although I like giving lectures, it is fun, and I believe I am good at it. Nevertheless, I am looking forward to get rid of this part of my work again. At least for a little while. So, I can focus again on all the other unfinished issues on my desk, and there are a lot. For example, the release of the next version of MegaMol should have happened two weeks ago. I have to work on my upcoming paper projects. And, of course, the development of MegaMol needs to go further for future projects.

Yesterday I tried to write yet another “nothing new” post, following my normal posting schedule. However, due to some reasons I completely don’t understand, I could not log into my admin dashboard. I tried everything on the login troubleshooting guide, until I reached “If everything else fails…” Luckily, it then was late, I was tired and I did not want to try anything else.

And today, everything works fine again. … WTF …

Why is software so unreliable, and why does software these days fails so silently and so elusively?