简体   繁体   中英

C# WinForms disable DPI scaling

I have a WinForm application which hosts many images. When I put the application on a Win7 machines that has a DPI of 120, it completely ruins the look of the form. Is there a way to disable the scaling for my form?

I am aware that this is something that is not advised and that DPI should be seamless and handled by the OS. But when it comes to a skinned application, the images do not scale well. I do not have the luxury of creating images for all the DPI variations, so please don't suggest that as an answer.

You'll have bigger problems when you change the AutoScaleMode property. Increasing the DPI also changes the system font size. Necessarily so, font sizes are expressed in points, 1/72 inch. The fonts need to be bigger to get the same point size when the DPI increases and keep the text just as readable when viewed from the same distance.

Since the controls don't get resized anymore, the text on, say, a button no longer fits. One way to battle this is to change the font size on the controls proportionally. Easy if you let all the controls inherit the form font, just changing the form's Font property automatically updates the controls as well. The clear disadvantage is that the user will have a harder time reading the text. This especially gets bad when the DPI goes to 150 dots per inch and beyond, your UI just turns into an unusable postage stamp.

Yes, background images need to get scaled to fit the larger control or form. A pixel in the image now no longer maps one-to-one to a pixel of the monitor. The default Graphics.InterpolationMode value does a fairly decent job of filtering the image. But it depends on the kind of image how well that turns out. A photo almost always scales very well. Finely detailed line art and text does not. Picking the right kind of image goes a long way to avoiding having to create separate ones.

This problem isn't going to go away until monitors start to have the kind of resolution a printer has. We're still a long way from 600 dpi for desktop monitors. Phones will be first.

Adding one line of code before the auto-generated call to InitializeComponent in the Form1-constructor solved it for me:

public partial class Form1 : Form
{
    public Form1()
    {
        // Make the GUI ignore the DPI setting
        Font = new Font(Font.Name, 8.25f * 96f / CreateGraphics().DpiX, Font.Style, Font.Unit, Font.GdiCharSet, Font.GdiVerticalFont);
        InitializeComponent();
    }
}

您可以将窗体的AutoScaleMode属性设置为None

Create a application manifest file (right-click on project/ add/new item/application file) and uncomment this section:

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

Creds to this site: https://www.telerik.com/blogs/winforms-scaling-at-large-dpi-settings-is-it-even-possible-

The size of fonts is increased, but your UI elements stay the same size. If you have a look at what is wrong, you should be able to think of the certain parts of the UI that need to scale up. This can include:

  • The size of each window.
  • The width of any absolutely sized columns.
  • The height of any absolutely sized rows.
  • The size of any absolutely sized element.

(Basically it is anything that is sized absolutely.)

You can detect the DPI scale factor with something like this:

static (float X, float Y)? sMaybeCachedFactors;

public static (float X, float Y) GetDPIFactors(
   this Control me
) {
   if (sMaybeCachedFactors is { } cachedFactors)
      return cachedFactors;

   using var g = me.CreateGraphics();
   sMaybeCachedFactors = cachedFactors = (g.DpiX / 96f, g.DpiY / 96f);

   return cachedFactors;
}

You can then use these values in the right places to scale your UI up.

For example, if you normally set the size of a window to 800x600:

// instead of 
Size = new Size(800, 600);

// you would do this
var factors = this.GetDPIFactors();
Size = new Size(800 * factors.X, 600 * factors.Y);

Also, instead of a (float, float) tuple as I've used so far, you could make something more specific to represent the scale factor, and then it could be applied more easily to scale things, with operators, etc.

var factors = this.GetDPIFactors();
Size = factors.GetSize(800, 600); // scaled automatically

// or

Size = new Size(800, 600) * factors; // * operator

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM