36
votes

I have some problems with the Form.AutoScaleMode property together with fixed size controls, when using a non-default font. I boiled it down to a simple test application (WinForms 2.0) with only one form, some fixed size controls and the following properties:

class Form1 : Form
{
    // ...
    private void InitializeComponent()
    {
        // ...
        this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F);
        this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi;
        this.Font = new System.Drawing.Font("Tahoma", 9.25F);
        // ...
    }
}

Under 96dpi, Windows XP, the form looks correctly like this 96 dpi example:

96 dpi WinForm

Under 120 dpi, Windows XP, the the Windows Forms autoscaling feature produces this 120 dpi example:

Previous WinForm scaled to 120 dpi

As you can see, groupboxes, buttons, list or tree views are scaled correctly, multiline text boxes get too big in the vertical axis, and a fixed size label does not scale correctly in both vertical and horizontal direction. Seems to be bug in the .NET framework?

EDIT: some hints: the Font change is only applied to the containing form, the controls inherit their font from the form. I would like to keep it this way, if possible.

Using the default font (Microsoft Sans Serif 8.25pt), this problem does not occur. Using AutoScaleMode = Font (with adequate AutoScaleDimensions, of course) either does not scale at all or scales exactly like seen above, depending on when the Font is set (before or after the change of AutoScaleMode). The problem is not specific to the "Tahoma" Font, it occurs also with Microsoft Sans Serif, 9.25pt.

And yes, i already read this SO post high DPI problems but it does not really help me.

Any suggestions how to come around this?

EDIT2: Some additional information about my intention: I have about 50 already working fixed size dialogs with several hundreds of properly placed, fixed size controls. They were migrated from an older C++ GUI framework to C#/Winforms, that's why they are all fixed-size. All of them look fine with 96 dpi using a 9.25pt font. Under the old framework, scaling to 120 dpi worked fine - all fixed size controls scaled equal in both dimensions. Last week, we detected this strange scaling behaviour under WinForms when switching to 120 dpi. You can imagine that most of our dialogs now look very bad under 120 dpi. I am looking for a solution that avoids a complete redesign all those dialogs.

EDIT3: To test this behaviour, IMHO it is a good idea to set up a virtual Windows XP environment with 120 dpi while the development environment resides under 96 dpi (at least, that's what I did). Changing between 96 and 120 dpi normally needs a reboot under Win XP, otherwise you don't see what really happens.

// As requested: the source code of Form1.cs 
namespace DpiChangeTest
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
            Font f = this.textBox1.Font;
        }
    }
}

 // here the source of Form1.Designer.cs:
namespace DpiChangeTest
{
    partial class Form1
    {
        private System.ComponentModel.IContainer components = null;

        protected override void Dispose(bool disposing)
        {
            if (disposing && (components != null))
            {
                components.Dispose();
            }
            base.Dispose(disposing);
        }

        #region Windows Forms Designer generated code

        private void InitializeComponent()
        {
            System.Windows.Forms.ListViewItem listViewItem2 = new System.Windows.Forms.ListViewItem("A list view control");
            System.Windows.Forms.TreeNode treeNode2 = new System.Windows.Forms.TreeNode("A TreeView control");
            this.button1 = new System.Windows.Forms.Button();
            this.groupBox1 = new System.Windows.Forms.GroupBox();
            this.textBox1 = new System.Windows.Forms.TextBox();
            this.label1 = new System.Windows.Forms.Label();
            this.listView1 = new System.Windows.Forms.ListView();
            this.treeView1 = new System.Windows.Forms.TreeView();
            this.SuspendLayout();
            // 
            // button1
            // 
            this.button1.Location = new System.Drawing.Point(12, 107);
            this.button1.Name = "button1";
            this.button1.Size = new System.Drawing.Size(150, 70);
            this.button1.TabIndex = 0;
            this.button1.Text = "Just a button";
            this.button1.UseVisualStyleBackColor = true;
            // 
            // groupBox1
            // 
            this.groupBox1.Location = new System.Drawing.Point(12, 12);
            this.groupBox1.Name = "groupBox1";
            this.groupBox1.Size = new System.Drawing.Size(150, 70);
            this.groupBox1.TabIndex = 1;
            this.groupBox1.TabStop = false;
            this.groupBox1.Text = "Just a groupbox";
            // 
            // textBox1
            // 
            this.textBox1.Location = new System.Drawing.Point(180, 12);
            this.textBox1.Multiline = true;
            this.textBox1.Name = "textBox1";
            this.textBox1.Size = new System.Drawing.Size(150, 70);
            this.textBox1.TabIndex = 2;
            this.textBox1.Text = "A multiline text box";
            // 
            // label1
            // 
            this.label1.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
            this.label1.Location = new System.Drawing.Point(179, 107);
            this.label1.Name = "label1";
            this.label1.Size = new System.Drawing.Size(150, 70);
            this.label1.TabIndex = 3;
            this.label1.Text = "A label with AutoSize=False";
            // 
            // listView1
            // 
            this.listView1.Items.AddRange(new System.Windows.Forms.ListViewItem[] {
            listViewItem2});
            this.listView1.Location = new System.Drawing.Point(12, 201);
            this.listView1.Name = "listView1";
            this.listView1.Size = new System.Drawing.Size(150, 70);
            this.listView1.TabIndex = 4;
            this.listView1.UseCompatibleStateImageBehavior = false;
            // 
            // treeView1
            // 
            this.treeView1.Location = new System.Drawing.Point(179, 201);
            this.treeView1.Name = "treeView1";
            treeNode2.Name = "Knoten0";
            treeNode2.Text = "A TreeView control";
            this.treeView1.Nodes.AddRange(new System.Windows.Forms.TreeNode[] {
            treeNode2});
            this.treeView1.Size = new System.Drawing.Size(150, 70);
            this.treeView1.TabIndex = 5;
            // 
            // Form1
            // 
            this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F);
            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi;
            this.ClientSize = new System.Drawing.Size(343, 289);
            this.Controls.Add(this.treeView1);
            this.Controls.Add(this.listView1);
            this.Controls.Add(this.label1);
            this.Controls.Add(this.textBox1);
            this.Controls.Add(this.button1);
            this.Controls.Add(this.groupBox1);
            this.Font = new System.Drawing.Font("Microsoft Sans Serif", 9.25F);
            this.Name = "Form1";
            this.Text = "Form1";
            this.ResumeLayout(false);
            this.PerformLayout();

        }

        #endregion

        private System.Windows.Forms.Button button1;
        private System.Windows.Forms.GroupBox groupBox1;
        private System.Windows.Forms.TextBox textBox1;
        private System.Windows.Forms.Label label1;
        private System.Windows.Forms.ListView listView1;
        private System.Windows.Forms.TreeView treeView1;
    }
}

 // and Main.cs
    [STAThread]
    static void Main()
    {
        Application.EnableVisualStyles();
        Application.SetCompatibleTextRenderingDefault(false);
        Application.Run(new Form1());
    }
8

8 Answers

38
votes

I finally found an answer for my question. In short, the effect does not arise when one sets the font of each control individually instead of setting the font of the containing form. This way, the auto scaling feature works as as it should. Interestingly, setting the font of the controls changes the auto-scaling behaviour even if the AutoScaleMode property is set to AutoScaleMode.Dpi, not only when it is set to AutoScaleMode.Font.

As a pragmatic solution, we created a small command line program, which reads the designer.cs files, scans if all controls have an explicit font assignment, and if not, adds the assignment into a newly created copy of the designer code. We embedded this program into our automatic test suite, so whenever a form gets new controls, or a new form is added, and the dev forgets to add the explicit font assignment, the tests will fail. In between, we have this solution working from the time I asked this question first (4 years ago), and it saved us from scaling problems for several times since then.

3
votes

Had this frustrating problem causing the spacing on the form to get bewildering out of proportion, however, changed the AutoScaleMode to "None" and the problem completely disappeared.

2
votes

Keep in mind that since many of the issues mentioned are related to font size, it is important to be consistent with the font family and size. This means set the font on the base form or base usercontrol (if you have one) and let the controls inherit that setting. I noticed than when I have a form with a UserControl inside it, if I selected the control and changed the font size, some items resized and some didn't. I realized that the items that didn't resize had their font setting specifically set (over-ridden). It was at that time that I realized what it meant when properties were highlighted in bold. So for example if you have a label and the font prop is bolded that means someone changed it. You should clear all those font props that were set that way so that they get their font from the parent, in this case the containing form. You can simply highlight the font property text and delete or right click the font prop and select clear. That will remove the font line from the designer file and allow the control to inherit the font from its parent. This will probably jump back to Microsoft Sans Serif but if you build it will pick up the font from its parent Of course you should follow proper design using layout panels and anchor and dock properties but I find that in general if you clear all the over-ridden font props you will find that if you select the user control from within your form and change the autoscalemode to None, you will have better luck Also for testing, if you then change the font size for the userontrol all the controls in it should resize (as long as no font props are over-ridden) And as long as the form is designed using the proper layout panels, everything should display fine on other resolutions. At least so far for me this has worked.

1
votes

I was able to resolve a similar issue with compact framework 3.5 on VS 2008. In my case, I have a tabcontrol, and each tabpage has a Panel, and all are docked as full to their parents. Each panel contains several label and textbox controls, so the idea is that when the user opens the SIP (soft input panel/keyboard) that the scrollbar will appear on the right and the textbox controls would scale in width to avoid painting an additional horizontal scrollbar.

My initial attempt had the forms' autoscale mode set to dpi, each tabpages' autoscroll property set to true, and each panels' autoscroll property set to true. Each label was anchored to top,left, and each textbox control was anchored to left,top,right. The forms were created in the designer with a screen width of 240 pixels, and when run on a vga device with a 480 pixel screen width, the textboxes would be painted with space on the right sufficient for 2 scrollbars (presumably one for the tabpage and one for the panel) even though the scrollbars were not appearing. When activating the SIP, the behavior was correct, in that all of the textboxes resized, but I still had 40 or so pixels of dead space between the right side of the textbox and the scrollbar.

I was able to resolve the issue simply by setting the panels' autoscroll property to false, then setting then to true a run time when the SIP was activated. This had the desired result of autoscaling to the full width of the screen in either pixel width, and dynamically resizing the textbox controls as the scrollbar is activated or de-activated.

As a side note, the compact framework (3.5) does not have the Font autoscale mode (only none, dpi, and inherit), but I tried resetting the font of each textbox control as the original author suggested, but this didn't have any effect on the controls' autoscaling.

1
votes

I too found the behavior odd, and have shared similar headaches in trying to automatically scale the controls (and their associated fonts) in my application in response to a size or resolution change.

For what it's worth, here's a brief outline of what I've been trying to apply a manual fix:

First, on application launch, I capture the user's system resolution by accessing the Bounds member in the Screen::PrimaryScreen property and programmatically resize the form based on the percentage difference from the development time system. This new form size is stored and used as the base size for all future adjustments. Also, by changing the size of the form at runtime, it invokes the SizeChanged event that I've handled to do my scaling. In a nutshell, the event handler does the following:

  • Resizes all controls according to the new Width and Height percentages after the user chooses a size, whether from the mouse or a predefined size.

  • Changes the font size associated with every control by the new scaling factor

Theoretically if controls are moved to a new location based on the percentage the size of the form changed it should keep them relative to all the other controls. Of course, every time the form size is changed by the user the above happens, not just at initial runtime.

I'm not sure if this solution is silly or not, but the time I've spent, or wasted, fighting with AutoScale, AutoSize, and Anchoring to no avail has been astronomical. I'm not trying to hijack your section, Doc, I just thought I'd share my thought processes with you and perhaps resurrect this topic in hopes someone has some superior insight on this nightmare.

0
votes

For the accepted solution on making the change for each control: Have you tested changing the Font of the container, but setting the AutoScaleXXX values again?

Something like:

 this.SuspendLayout();
 this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F);
 this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; // Font in this case
 this.Font = new Font(....); // set your new font
 this.ResumeLayout();

I have done this when adding dynamically controls, but not for changing fonts.

0
votes

I had that problem too. Especially LinkLabels were displayed with a too big font and AutoSize labels were clipped somewhere at end. Changing the AutoScaleMode to Dpi on just the first dialog (Main) solved it for all forms. Thanks for the tip.

-1
votes

My WinForms application has a Font Size Preferences, where font can be set at three levels (8pt, 10pt, or 12pt) on the Main screen, and it should propogate down to all sub forms.

The specific issues I encountered are with Labels that are not Autosize (typically used for multi-line labels) and Multiline Textboxes.

The solution I found was to have add the following line with the applicable control in the Designer.cs files in the InitializeComponent():

this.Label1.Font = this.Font;

or

this.MultiLineTextbox.Font = this.Font;

This seems to set the AutoScale correctly.