简体   繁体   中英

WinForm changes size at runtime… high DPI related?

When I run my WinForm app in Windows in a VM on a retina MacBook Pro, the size of the form shrinks at runtime, while the buttons simultaneously move outward. This can cause buttons at the bottom edge to slip below the window's edge, out of sight. Since they're bottom-anchored, they're rendered completely inaccessible. When run from a Windows-native desktop, the app usually behaves fine.

This only happens with the Font or DPI AutoScaleMode settings on the form. With Inherit or None , the form and its contents are huge, but directly proportional to how I designed them.

I've reproduced this with a fresh-from-template WinForm app, doing nothing other than resizing the form, and dropping in a button. How can I get the app to scale without the dimensions changing relative to each other?

This is the InitializeComponent() method in the designer.cs :

  private void InitializeComponent()
  {
        this.sendButton = new System.Windows.Forms.Button();
        this.SuspendLayout();
        // 
        // sendButton
        // 
        this.sendButton.Location = new System.Drawing.Point(60, 856);
        this.sendButton.Margin = new System.Windows.Forms.Padding(4);
        this.sendButton.MinimumSize = new System.Drawing.Size(200, 60);
        this.sendButton.Name = "sendButton";
        this.sendButton.Size = new System.Drawing.Size(200, 62);
        this.sendButton.TabIndex = 1004;
        this.sendButton.Text = "Send";
        this.sendButton.UseVisualStyleBackColor = true;
        // 
        // Form1
        // 
        this.AutoScaleDimensions = new System.Drawing.SizeF(12F, 25F);
        this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
        this.ClientSize = new System.Drawing.Size(282, 981);
        this.Controls.Add(this.sendButton);
        this.Name = "Form1";
        this.Text = "Form1";
        this.ResumeLayout(false);

  }

Here's a screenshot of the form in the designer:

在设计时表格

And at runtime:

在运行时表单

This issue is not caused by a rescaling problem, it is far more mundane. It is caused by the Form.SetBoundsCore() method , I linked to the problem statement.

You can see it using the SystemInformation class, applying constraints on the bounds so the window cannot be too small and cannot be larger than the monitor. This method runs twice in your program. The very first time is when your form's InitializeComponent() method runs. Note that at this point the size of the form is still far too large, it does not fit the monitor until after it is rescaled.

Perhaps you smell the bug, it applies the size constraint too early, before the new size is calculated by the ScaleControl() method. So your design Size.Height gets clipped by the size of the monitor. Then the window is scaled to adjust for the DPI difference, the resulting height is too small.

Normally you can override a virtual method like SetBoundsCore(), it is however not very practical to bypass the base.SetBoundsCore() call. Too much stuff happens in this method and bypassing it can cause other bugs. The practical workaround is almost too silly to mention. Note from the linked code that it does not apply the size constraint unless the form's WindowState is set to normal. So you can bypass it by setting the WindowState to Minimized in the designer. All you then have to do is set it to normal in the Load event, it fires after the window is rescaled:

    protected override void OnLoad(EventArgs e) {
        this.WindowState = FormWindowState.Normal;
        base.OnLoad(e);
    }

Heh. Scaling up is always a lot less troublesome than scaling down.

I would be remiss to not post the more typical dpiAware code. In this case similar to:

    protected override void OnLoad(EventArgs e) {
        this.ClientSize = new Size(button1.Width + 2 * button1.Left, button1.Bottom + 10);
        base.OnLoad(e);
    }

So simply force the client area to show the controls. You'd pick a control at the bottom and one on the far right.

I think the issue is AutoScaleMode. Make these changes to your code in Form1:

this.AutoScaleDimensions = new System.Drawing.Size(96, 96);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi;

This may resolve your problem. SizeF is used for Font , and for 100% font size, its value is (13F, 8F) .

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