0

I am developing a WinForms application in C#. It uses a panel to draw an image of the Mandelbrot fractal. In the manual for the program and whenever I post about it somewhere, I recommend people to set their scaling setting to 100%, as otherwise the images won't look nice. This is because on other settings, the image is scaled up after drawing it, and it becomes blurry. All other controls are blurry too.

For example: my panel is 500x500. The scaling in my Windows is set to 125%. When I run the program, the panel is internally still 500x500, but it appears as 625x625, blurry.
Instead, when the program is run, I want the panel to internally resize to 625x625, and appear as 625x625 too.

I have found the following solution: I found out about SetProcessDPIAware() (from here). Setting that makes the window not scale (it appears as if it was at the 100% scale setting), but the text does (and without becoming blurry). I can then, at the start of the program, calculate the appropriate multiplier (dpi = DpiX / 96) and give that to a huge method that includes commands like

xentrylabel.Location = new Point((int)(xentrylabel.Location.X * dpi),(int)(xentrylabel.Location.Y * dpi));
xentry.Location = new Point((int)(xentry.Location.X * dpi), (int)(xentry.Location.Y * dpi));
xentry.Size = new Size((int)(xentry.Width * dpi), (int)(xentry.Height * dpi));

One for every control property that might need to be updated. While writing this question, I got this idea and got started with it. However, I realised that this will need very many lines of code, so I wonder if there isn't a built-in way to do this. It seems like an option that many would like to go for, rather than their applications becoming blurry or hard to read on screens with high dpi.

Is this way of manually correcting positions and sizes the way to go, or is there a built-in way to scale the form for other DPI settings by actually scaling everything in the form, instead of scaling the end result?

Edit: From some comments it seems as if SetProcessDPIAware() alone should scale up everything. But in my experience, it doesn't. Here are some screenshots: Application on 100% scale setting: https://i.stack.imgur.com/pws9B.png

Application on 125% scale setting without SetProcessDPIAware(): https://i.stack.imgur.com/cE4Wx.png

Application on 125% scale setting with SetProcessDPIAware(): https://i.stack.imgur.com/oVA8W.png

607
  • 35
  • 7
  • [How to configure an app to run correctly on a machine with a high DPI setting (e.g. 150%)?](https://stackoverflow.com/a/13228495/7444103) – Jimi Dec 31 '19 at 16:48
  • You're making some other kind of mistake. When you declare you app to be dpiAware then the form design does *not* get rescaled. Maybe you created the panel in your code instead of with the designer and hard-coded its size to 500x500, creating it in the Load event instead of the constructor. Just make sure you do everything in the constructor, so rescaling is consistent for all controls regardless of the DPI setting. – Hans Passant Dec 31 '19 at 17:00
  • @HansPassant Indeed, when I declare the app to be dpiAware it does not get rescaled. Should it? I don't use the designer. – 607 Dec 31 '19 at 17:02
  • Not on your machine. It might well get rescaled on another machine with a different DPI setting. – Hans Passant Dec 31 '19 at 17:03
  • @Jimi Interestingly, that is exactly what I used. From that being the accepted answer, you would think that this should do the job. But in my experience, it doesn't: it only upscales the text, nothing else. – 607 Dec 31 '19 at 17:05
  • I added screenshots to my post, for clarity. – 607 Dec 31 '19 at 17:16
  • The Font size is another matter.When you set the Application DpiAwareness, you have to consider multiple factors. The Form must AutoScale to Dpi, not Font. All controls need to have their Font explicitly set, don't let them inherit from the Parent (at some point). Use TableLayoutPanels / FlowLayoutPanels to layout your controls etc. Note also that DpiAwareness has different levels: [High DPI support in Windows Forms](https://learn.microsoft.com/en-us/dotnet/framework/winforms/high-dpi-support-in-windows-forms#configuring-your-windows-forms-app-for-high-dpi-support). – Jimi Dec 31 '19 at 17:32
  • Decide if you want to use the `app.manifest`, the `app.config`, or use the Windows API to *manually* redefine this behavior: [High DPI Desktop Application Development on Windows](https://learn.microsoft.com/en-us/windows/win32/hidpi/high-dpi-desktop-application-development-on-windows), [How can I update my WinForms app to behave better at high DPI, or at normal DPI on very large screens?](https://devblogs.microsoft.com/oldnewthing/?p=93695) (and so on). Mixing the available tools is not that straightforward. It needs some testing. – Jimi Dec 31 '19 at 17:36
  • @Jimi Setting either 'AutoScaleMode = AutoScaleMode.Font;' or 'AutoScaleMode = AutoScaleMode.Dpi;' in the form's constructor method does not visibly change anything. In both cases the form still looks like the posted screenshot.
    I have now explicitly set the font for each control to ("Microsoft Sans Serif", 8). This also does not visibly change anything.
    I don't know if I want to use 'app.manifest' or 'app.config' as I had never heard of either, interestingly. I would rather not manually recreate the behaviour if it is indeed built-in, as using a built-in method seems cleaner.
    – 607 Jan 01 '20 at 14:02
  • You *never heard* about the `app.manifest`? It's in the first like I posted. You add the `app.manifest` as you add any other component: Project → Add → New Item... You'll see the DpiAware setting already there, commented out. Then follow the linked answer. -- In your first image, the Form is virtualized (DpiAware = NotAware). In your second Image, the Form is is not vistualized, but the Font is scaled, causing some of the control to be resized. This is usually clear *symptom* that the Form scales on Fonts and the Font is inherited. Set these properties in the Form's designer. – Jimi Jan 01 '20 at 14:10
  • Use the manifest to set the DpiAwareness status of your app, use `Segoe UI` as Font, follow the other Documents I linked. – Jimi Jan 01 '20 at 14:11
  • I have set the app.manifest and App.config lines mentioned in the default app.manifest, and commented out the API call SetProcessDPIAware();. The result is unchanged, so using the manifest and config works. However, the result should be changed.
    I do not use the designer. What properties are you talking about? Surely any properties that could be set in the designer can be set by code as well.
    Why would I have to use Segoe UI?
    – 607 Jan 01 '20 at 14:20
  • Well, use the Designer for testing, so you can understand what you're doing wrong. Segoe UI because it's a System Font (always available, widely used) and it's not the default one, so you have a clear *visual* when the Font has not been applied. Btw, this is a quite common/classic scenario. If you follow the instructions (to the letter, without any *interpretation*) you'll get it right. Btw2, use the `app.manifest` **OR** `app.config` not both. The former overrides the latter (this is also described in the Documents I linked). – Jimi Jan 01 '20 at 14:34
  • Without _interpretation_ one wouldn't get anywhere. Imagine trying to create a bot that would solve problems through reading StackOverflow threads. The Designer doesn't show anything useful, because it doesn't actually interpret any code. It only shows a default form, which is of course very much unlike the end product. To test, you should click the 'Start' button. – 607 Jan 01 '20 at 14:53
  • Quoting from app.manifest: ` ` I'm pretty sure I'm targeting .NET Framework 4.6. Edit: Yes, 4.6.1. – 607 Jan 01 '20 at 14:54
  • I think I will go with my proposed solution, as it seems like there is no working built-in solution. However, I am hesitant, because it seems like that for some people this setting that for me only changes font size actually _did_ change everything. I hypothesise that these are people that used the Designer to add all their controls. But the Designer shouldn't do any magic. Anything that can be done with the Designer has to be possible without it as well, I'm pretty sure. – 607 Jan 01 '20 at 14:59
  • I figured it out. My hypothesis was correct. I wish I had got the idea to test it earlier. I opened a new project, and used the Designer to add some controls. Then I added another control using regular code. When using the DPI scaling, the Designer-added controls scale, and so does the form, but the code-added control does not. So _yes_, SetProcessDPI aware should scale everything up. But there's an issue when the Designer isn't used, apparently. I will look into what the Designer actually does so I can find out how to fix this. – 607 Jan 01 '20 at 15:17

1 Answers1

-1

We solved the issue of rendering controls for high DPI screens with scaling greater than 100% in Chem4Word by creating a WPF user control which is hosted in an ElementHost control, which is set to fill the form.

WinForm +- ElementHost +- WPFUserControl

Looking at your uploaded images, that's exactly the problem which is solved by using a WPF user control.

  • Can I ask why this was down voted? The answer may not be to the OP's liking, but it certainly fixes the issue they are having with text overloading the panels. – Mike Williams Dec 31 '19 at 17:59
  • I may look into this answer (I have not voted on it), but I would like to point out ahead of time that I do not have an issue with text overloading panels. The text resizing is in fact desired: I would like the rest of the form to resize as well. – 607 Jan 01 '20 at 14:04
  • I appreciate that it's not trivial to swap to a WPF user control, but we have found the pain is worth it to enable support for high DPI screens with scaling not at 100%. WPF and windows forms can safely be used in the same project. – Mike Williams Jan 01 '20 at 14:50