1

I've got this down to a very simple repro, and cannot figure out what is going wrong with this form. When run at 96 DPI / 100% scale it appears fine:

enter image description here

But when run at 144 DPI / 150% scale (or even 96 DPI / 150% scale), everything scales except the form height:

enter image description here

Originally I thought this was a DPI issue, but after verifying that it repros at 96 DPI, I'm not sure what's going on.

There is nothing special going on with the dialog or control, other than specifically setting the font of the dialog, and setting the AutoScaleMode to DPI. The form lives inside an assembly that is loaded automatically by the app.

I'm using .NET 4.7.2 and Windows 10.

Here's the form code:

using System;
using System.Net;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace FormTestLib
{
    partial class ValidatingSplash : Form
    {
        public ValidatingSplash()
        {
            InitializeComponent();
        }

        protected override void OnLoad(EventArgs e)
        {
            base.OnLoad(e);
            this.CenterToParent();
        }
    }
}

Here's the designer file:

namespace FormTestLib
{
    public partial class ValidatingSplash
    {
        /// <summary>
        /// Required designer variable.
        /// </summary>
        private System.ComponentModel.IContainer components = null;

        /// <summary>
        /// Clean up any resources being used.
        /// </summary>
        /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
        protected override void Dispose(bool disposing)
        {
            if (disposing && (components != null))
            {
                components.Dispose();
            }
            base.Dispose(disposing);
        }

        #region Windows Form Designer generated code

        /// <summary>
        /// Required method for Designer support - do not modify
        /// the contents of this method with the code editor.
        /// </summary>
        private void InitializeComponent()
        {
            System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(ValidatingSplash));
            this.lblValidating = new System.Windows.Forms.Label();
            this.SuspendLayout();
            // 
            // lblValidating
            // 
            this.lblValidating.Anchor = System.Windows.Forms.AnchorStyles.None;
            this.lblValidating.AutoSize = true;
            this.lblValidating.Location = new System.Drawing.Point(58, 45);
            this.lblValidating.Name = "lblValidating";
            this.lblValidating.Size = new System.Drawing.Size(166, 13);
            this.lblValidating.TabIndex = 7;
            this.lblValidating.Text = "Validating cached credentials...";
            // 
            // ValidatingSplash
            // 
            this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F);
            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi;
            this.ClientSize = new System.Drawing.Size(274, 104);
            this.ControlBox = false;
            this.Controls.Add(this.lblValidating);
            this.Font = new System.Drawing.Font("Segoe UI", 8.25F);
            this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog;
            this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon")));
            this.MaximizeBox = false;
            this.MinimizeBox = false;
            this.Name = "ValidatingSplash";
            this.Text = "Validating Credentials";
            this.ResumeLayout(false);
            this.PerformLayout();

        }

        #endregion
        private System.Windows.Forms.Label lblValidating;
    }
}

In the app.config I'm setting DpiAwareness according to the docs:

<System.Windows.Forms.ApplicationConfigurationSection>
  <add key="DpiAwareness" value="PerMonitorV2"/>
</System.Windows.Forms.ApplicationConfigurationSection>

And in the manifest I'm setting the compatibility:

<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
  <application>
    <!-- Windows 10 compatibility -->
    <supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" />
  </application>
</compatibility>

All this according to the instructions for high DPI support here.

The application code simply shows the dialog:

namespace TestApp
{
    static class Program
    {
        /// <summary>
        /// The main entry point for the application.
        /// </summary>
        [STAThread]
        static void Main()
        {
            // set the visual styles
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(true);

            ValidatingSplash Splash = new ValidatingSplash();
            Splash.ShowDialog();
        }
    }
}

Can anyone see what I might be doing wrong, or what I'm missing?

Thanks in advance!

Bill Brooks
  • 751
  • 1
  • 10
  • 30
  • Remove `this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F);`. You can find the dpiAwareness settings in your `app.manifest`. Even if *it's not recommended anymore*, try it. Un-comment what you prefer. I doubt you have found this: `Application.SetCompatibleTextRenderingDefault(true);` in the Docs. – Jimi Mar 03 '19 at 23:13
  • Does it do this if you change the DPI _before_ starting the program, or only if the scaling changes while the program is running? – Joel Coehoorn Mar 04 '19 at 00:40
  • Also, this question is a useful resource: https://stackoverflow.com/questions/22735174/how-to-write-winforms-code-that-auto-scales-to-system-font-and-dpi-settings – Joel Coehoorn Mar 04 '19 at 00:43
  • Thanks @JoelCoehoorn, I've seen that question but it doesn't address this case. This occurs if I close the app, change the scaling, log out, log back in, and run the program. It also occurs without the logout/login, but I've discovered that enough cycles of the scale setting without logout/login can result in unpredictable results. I haven't tried _while the app is running_. – Bill Brooks Mar 04 '19 at 04:28
  • @Jimi, I tried your suggestions. Removing the AutoScaleDimensions just results in a differently-sized dialog, but still too small and cutting off text. Adding/moving dpiAwareness to app.manifest had no effect. And good catch - the true parameter to SetCompatibleTextRenderingDefault() was just a variable I tried a while back and accidentally left in. Unfortunately, changing it back to false doesn't seem to have any effect either. – Bill Brooks Mar 04 '19 at 08:38
  • What are the real numbers? Could it be you hit the limit (i.e. the screen height??) – TaW Mar 04 '19 at 09:23
  • If you're using the app,config file, try setting this: ``. When enabling DPI awarenes in the app.config, all opt-in features are also enabled. That's why I suggested to use **only** the manifest to set a specifc `dpiAwareness` setting or simply `dpiAware = true`. I think I had missed that: *The form lives inside an assembly that is loaded automatically by the app*. This also has *implications*. If something is dpiAware, you better have everything that works that way. – Jimi Mar 04 '19 at 14:35
  • Setting that to false had no effect. I've tried adding a config file for the assembly, without any difference. [This link](https://msdn.microsoft.com/en-us/library/windows/desktop/mt846517(v=vs.85).aspx) indicates that unless specified, all threads will use the process default for dpi awareness. I haven't been able to find anything that specifies the implications for assemblies. It does seem to be an issue with dpiAwareness (even when the DPI hasn't changed), as I can remove dpiAwareness from app.config and the problem goes away. – Bill Brooks Mar 04 '19 at 15:35
  • 1
    I have found the offending line in the designer code: `this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog;` If that line is commented out, everything works like it should. This border style is not supported when (or buggy if) a specific dialog font is set and both DPI awareness and display scaling are enabled. Can anyone confirm if this is indeed a bug or by design? I haven't been able to find anything in the docs. – Bill Brooks Mar 04 '19 at 16:56
  • 1
    Bonus: This problem only occurs if the primary display is scaled. In that case, the problem occurs on all monitors (regardless of the scale on each monitor). If the primary display is not scaled, the problem does not occur on any monitor (even the scaled monitors). Also, this happens whether or not the font is explicitly set. That is, it also occurs with the default system font. – Bill Brooks Mar 04 '19 at 17:16

1 Answers1

2

To follow Microsoft guidelines and provide High DPI support for your application you should change several things. First of all in your Designer file of form change AutoScaleDimensions to AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);. And AutoScaleMode to this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;.

In the application just use Application.SetCompatibleTextRenderingDefault(false);

I also added a small correction for for setting ClientSize of the form. AdjustClientWidthToDPIScale(). Depending on DPI scale the client width of form is changed according to the DPI factor.

All code is listed below.

App.config file:

<?xml version="1.0" encoding="utf-8"?>
<configuration>
    <startup> 
        <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.7.1"/>
    </startup>
      <System.Windows.Forms.ApplicationConfigurationSection>
    <add key="DpiAwareness" value="PerMonitorV2" />
  </System.Windows.Forms.ApplicationConfigurationSection>
</configuration>

Form code:

using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace FormTestLib
{
    public partial class ValidatingSplash : Form
    {
        public ValidatingSplash()
        {
            InitializeComponent();

            AdjustClientWidthToDPIScale();
        }

        protected override void OnLoad(EventArgs e)
        {
            base.OnLoad(e);
            this.CenterToParent();
        }

        private void AdjustClientWidthToDPIScale()
        {
            double dpiKoef = Graphics.FromHdc(GetDC(IntPtr.Zero)).DpiX / 96f;

            int compansatedWidth = (int)(ClientSize.Width * dpiKoef);


            this.ClientSize = new Size(compansatedWidth, this.ClientSize.Height);
        }

        [DllImport("User32.dll")]
        private static extern IntPtr GetDC(IntPtr hWnd);
    }
}

Form designer:

    namespace FormTestLib
{
    partial class ValidatingSplash
    {
        /// <summary>
        /// Required designer variable.
        /// </summary>
        private System.ComponentModel.IContainer components = null;

        /// <summary>
        /// Clean up any resources being used.
        /// </summary>
        /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
        protected override void Dispose(bool disposing)
        {
            if (disposing && (components != null))
            {
                components.Dispose();
            }
            base.Dispose(disposing);
        }

        #region Windows Form Designer generated code

        /// <summary>
        /// Required method for Designer support - do not modify
        /// the contents of this method with the code editor.
        /// </summary>
        private void InitializeComponent()
        {
            System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(ValidatingSplash));
            this.lblValidating = new System.Windows.Forms.Label();
            this.SuspendLayout();

            // 
            // lblValidating
            // 
            this.lblValidating.Anchor = System.Windows.Forms.AnchorStyles.None;
            this.lblValidating.AutoSize = true;
            this.lblValidating.Location = new System.Drawing.Point(58, 45);
            this.lblValidating.Name = "lblValidating";
            this.lblValidating.Size = new System.Drawing.Size(166, 13);
            this.lblValidating.TabIndex = 7;
            this.lblValidating.Text = "Validating cached credentials...";
            // 
            // ValidatingSplash
            // 
            this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
            this.ClientSize = new System.Drawing.Size(274, 104);
            this.ControlBox = false;
            this.Controls.Add(this.lblValidating);
            this.Font = new System.Drawing.Font("Segoe UI", 8.25F);
            this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog;
            //this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon")));
            this.MaximizeBox = false;
            this.MinimizeBox = false;
            this.Name = "ValidatingSplash";
            this.Text = "Validating Credentials";
            this.ResumeLayout(false);
            this.PerformLayout();
        }

        #endregion
        private System.Windows.Forms.Label lblValidating;
    }
}

Apllication code:

[STAThread]
    static void Main()
    {
        Application.EnableVisualStyles();
        Application.SetCompatibleTextRenderingDefault(false);
        ValidatingSplash Splash = new ValidatingSplash();
        Splash.ShowDialog();
    }
Andrew Patynko
  • 121
  • 1
  • 4