AutoScaleMode 与更改默认字体的问题

38

当使用非默认字体时,我在Form.AutoScaleMode属性与固定大小控件方面遇到了一些问题。我将其简化为一个仅包含一个窗体、一些固定大小控件和以下属性的简单测试应用程序(WinForms 2.0):

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);
        // ...
    }
}

在96dpi、Windows XP下,表单看起来像这个96 dpi示例一样正确:

96 dpi WinForm

在120dpi、Windows XP下,WinForms自动缩放功能会产生这个120 dpi示例:

Previous WinForm scaled to 120 dpi

可以看到,groupboxes、buttons、list或tree views缩放得很好,而多行文本框在垂直轴上变得太大,固定大小的标签在垂直和水平方向上都无法正确缩放。这似乎是.NET框架中的一个错误?

编辑:一些提示:字体更改仅适用于包含的表单,控件继承其字体自表单。如果可能的话,我希望保持这种方式。

使用默认字体(Microsoft Sans Serif 8.25pt),这个问题就不会出现。使用AutoScaleMode = Font(当然需要适当的AutoScaleDimensions)要么根本不缩放,要么像上面看到的那样精确地缩放,这取决于字体何时设置(在更改AutoScaleMode之前还是之后)。问题并不特定于“Tahoma”字体,它也发生在Microsoft Sans Serif,9.25pt。

是的,我已经阅读了这个SO帖子high DPI problems,但它并没有真正帮助我。

有什么建议可以避免这种情况吗?

编辑2:关于我的意图的一些附加信息:我有大约50个已经工作良好的固定大小对话框,其中包含数百个正确放置的固定大小控件。它们从一个较旧的C++ GUI框架迁移到了C#/Winforms,这就是为什么它们都是固定大小的。所有这些看起来很好,使用9.25pt字体的96 dpi。在旧框架下,缩放到120 dpi没问题——所有固定大小的控件在两个维度上都缩放得相等。上周,在切换到120 dpi时,我们检测到了这种奇怪的缩放行为。你可以想象,在120 dpi下,我们的大多数对话框现在看起来非常糟糕。我正在寻找一种解决方案,避免完全重新设计所有这些对话框。

编辑3:为了测试这种行为,我认为设置一个虚拟的Windows XP环境,120 dpi,而开发环境则在96 dpi下(至少我是这么做的)。在Win XP下,切换96和120 dpi通常需要重新启动,否则你看不到真正发生的事情。

// 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个回答

41

我终于找到了我的问题的答案。简而言之,当一个人单独设置每个控件的字体而不是设置包含表单的字体时,效果不会出现。这样,自动缩放功能就像它应该的那样工作。有趣的是,即使将AutoScaleMode属性设置为AutoScaleMode.Dpi,也会更改控件的字体设置并改变自动缩放行为,而不仅仅是当它被设置为AutoScaleMode.Font时。

作为一个实用的解决方案,我们创建了一个小型命令行程序,它读取designer.cs文件,扫描所有控件是否有显式的字体分配,如果没有,则将分配添加到新创建的设计师代码副本中。我们将这个程序嵌入到我们的自动化测试套件中,因此每当一个表单获得新的控件或加入一个新表单时,如果开发人员忘记添加显式字体分配,测试将失败。从我第一次提出这个问题开始,我们已经使用这个解决方案运行了4年,它已经多次为我们解决了缩放问题。


4
我遇到了相同的问题。控件不能按照“Scale”(或受保护的“ScaleControl”)方法的响应来调整它们的字体大小。我正在考虑的解决方案是递归地迭代所有控件,并(神奇地)确定字体是否是正确的,并进行修复。不过,还有一些要注意的地方,我仍在努力解决中。如果我找到解决方案,我会回报的。 - Ian Boyd
2
是的,我很确定几乎没有人会在非标准分辨率下测试他们的应用程序。或者在多屏幕设置中测试。或者将任务栏停靠在左侧进行测试。或者使用奇怪的系统字体进行测试。或者使用非美国系统区域设置进行测试。或者使用希腊字母进行测试。或者......你懂的 :) 很遗憾成为少数例外的用户。 - Roman Starkov
4
谢谢@Doc,实际上我在这段时间里找到了一个稍微更好的答案。如果您在InitializeComponent调用之前在表单的构造函数中设置字体,它似乎会为自动缩放使用正确的字体。至少我是这么认为的。 - Craig Brett
@文档:对我来说似乎可以工作,除非我漏掉了什么(它本身不能使您的整个应用程序响应,但可以节省在每个控件上设置字体)。我正在使用更现代的系统Win 7,.NET Framework 3.5。 - Craig Brett
1
@CraigBrett:字体修复确实有所帮助 - 在我看来应该单独回答。有了这个,我几乎可以保持其他所有内容不变,使用AutoScaleMode: Font。之后需要修复的一件事是某些控件(Listbox、多行TextBox)文本,它们被锚定在顶部和底部,但向上移动了。我认为原因是缩放器根据字体大小更改了Height属性,由于锚定,控件会移动。所以我最终在InitializeComponent()调用后添加了位置和高度的修正。(这全部是在.NET 4.7.2下完成的) - H B
显示剩余5条评论

3

我曾遇到一个令人沮丧的问题,导致表单中的间距变得错综复杂,但是,将AutoScaleMode更改为“None”后,问题完全消失了。


如果用户在Windows控制面板中更改了其首选文本大小,则此操作将导致文本被剪切或用户的首选文本大小被忽略。 - Ian Goldby

2
请记住,由于许多问题与字体大小有关,因此在字体系列和大小方面保持一致非常重要。 这意味着在基本表单或基本用户控件上设置字体,并让控件继承该设置。 我注意到当我在一个带有用户控件的表单中选择控件并更改字体大小时,有些项目会重新调整大小,而有些则不会。 我意识到那些没有调整大小的项目具有特定设置(被覆盖)的字体设置。那时我才意识到什么是加粗突出显示的属性。 例如,如果您有一个标签,字体属性是加粗的,那就意味着有人更改了它。 您应该清除所有以这种方式设置的字体属性,以便它们从父级(在此情况下为包含表单)获得其字体。 您可以简单地突出显示字体属性文本并删除或右键单击字体属性并选择清除。 这将从设计文件中删除字体行,并允许控件从其父级继承字体。这可能会跳回到Microsoft Sans Serif,但如果您构建它,它将从其父级中获取字体。 当然,您应该使用布局面板和锚定和停靠属性进行正确的设计,但总的来说,如果您清除了所有覆盖的字体属性,您会发现如果您从表单中选择用户控件并将自动缩放模式更改为无,则会更好地运行。 此外,对于测试,如果您随后更改用户控件的字体大小,则其中所有控件都应调整大小(只要没有覆盖字体属性) 只要表单使用适当的布局面板设计,一切都应该在其他分辨率上正常显示。 至少到目前为止,对我来说这是有效的。

抱歉,但您似乎没有理解我的回答重点。在Winforms中存在一个错误,它确切地阻止了您所描述的继承机制的使用。如果想要确保多行文本框和标签正确缩放,每个控件都需要分配自己的字体。 - Doc Brown
抱歉,发生这种情况的唯一时候是更改字体并且直到重新构建后才生效。除此之外,我不怀疑您正确地指出它在其他时间也会发生。我发帖的主要目的是提醒大家注意被覆盖的字体设置,因为如果有任何覆盖的设置,父级字体设置将不会生效。 - billymac

1

我也发现这种行为很奇怪,并且在尝试自动缩放应用程序中的控件(及其相关字体)以响应大小或分辨率更改时,遇到了类似的问题。

就我所知,以下是我一直在尝试应用手动修复的简要概述:

首先,在应用程序启动时,通过访问 Screen::PrimaryScreen 属性中的 Bounds 成员来捕获用户系统分辨率,并根据与开发时间系统的百分比差异以编程方式调整窗体大小。这个新的窗体大小被存储并用作所有未来调整的基本大小。此外,通过在运行时更改窗体大小,它会调用我已处理的 SizeChanged 事件来进行缩放。简而言之,事件处理程序执行以下操作:

  • 在用户选择大小后(无论是通过鼠标还是预定义大小),根据新的宽度和高度百分比调整所有控件的大小。

  • 通过新的缩放因子更改与每个控件关联的字体大小

理论上,如果控件根据表单大小的百分比移动到新位置,它们应该相对于所有其他控件保持相对位置。当然,每次用户更改表单大小时都会发生上述情况,而不仅仅是在初始运行时。

我不确定这个解决方案是否愚蠢,但我花费或浪费的时间与AutoScale、AutoSize和Anchoring无济于事的斗争是巨大的。我并不想劫持你的部分,医生,我只是想与你分享我的思考过程,并希望能够重新唤起这个话题,以期有人对这个噩梦有更好的见解。


1

我曾经在VS 2008上使用紧凑框架3.5解决了类似的问题。在我的情况下,我有一个TabControl,每个TabPage都有一个Panel,并且所有控件都被停靠到其父控件上。每个面板包含多个标签和文本框控件,因此当用户打开SIP(软输入面板/键盘)时,滚动条将出现在右侧,文本框控件的宽度将缩小以避免绘制额外的水平滚动条。

我的初始尝试是将窗体的自动缩放模式设置为dpi,每个选项卡页面的自动滚动属性设置为true,并将每个面板的自动滚动属性设置为true。每个标签都锚定在顶部和左侧,每个文本框控件都锚定在左侧、顶部和右侧。这些窗体是在设计器中创建的,屏幕宽度为240像素,当在480像素屏幕宽度的vga设备上运行时,即使滚动条没有出现,文本框也会被绘制出足够容纳2个滚动条的空间(可能是一个用于选项卡页面,一个用于面板)。激活SIP时,行为是正确的,即所有文本框都被调整大小,但我仍然有大约40个像素的空白空间位于文本框的右侧和滚动条之间。

我成功解决了这个问题,只需将面板的autoscroll属性设置为false,然后在运行时将其设置为true即可激活SIP。这样可以实现自动缩放到屏幕的完整宽度,无论是像素宽度还是动态调整文本框控件滚动条的激活或取消激活。

顺便提一下,紧凑框架(3.5)没有字体自动缩放模式(仅有none、dpi和inherit),但我尝试按照原始作者建议重置每个文本框控件的字体,但对控件的自动缩放没有任何影响。


0

针对每个控件进行更改的已接受解决方案: 您是否尝试更改容器的字体,但再次设置AutoScaleXXX值呢?

像这样:

 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();

我在动态添加控件时做过这个,但没有更改字体。


0

我也遇到了这个问题。特别是LinkLabels以太大的字体显示,AutoSize标签在末尾被裁剪。将AutoScaleMode更改为Dpi仅适用于第一个对话框(Main),解决了所有表单的问题。感谢您的提示。


-1
我的WinForms应用程序有一个字体大小首选项,在主屏幕上可以设置三个级别的字体大小(8pt、10pt或12pt),并且应该向所有子窗体传播。
我遇到的具体问题是标签不是自动调整大小(通常用于多行标签)和多行文本框。
我找到的解决方案是在Designer.cs文件的InitializeComponent()中添加以下行,并将其应用于相应的控件:
this.Label1.Font = this.Font;

或者

this.MultiLineTextbox.Font = this.Font;

这似乎正确设置了自动缩放。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接