使用非默认字体时,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示例:
在120 dpi Windows XP下,Windows窗体自动缩放功能会产生这个120 dpi的示例:
如您所见,组框,按钮,列表或树视图的缩放正确,多行文本框在垂直轴上变得太大,并且固定大小的标签在垂直和水平方向上都无法正确缩放。似乎是.NET框架中的错误?
编辑:一些提示:字体更改仅应用于包含表单,控件从表单继承其字体。如果可能的话,我想保持这种方式。
使用默认字体(Microsoft Sans Serif 8.25pt),不会发生此问题。当然,使用AutoScaleMode = Font
(具有足够的AutoScaleDimensions)要么根本不进行缩放,要么完全如上所示进行缩放,具体取决于设置Font的时间(在更改AutoScaleMode之前或之后)。问题不是特定于“Tahoma”字体,它也出现在Microsoft Sans Serif,9.25pt。
是的,我已经阅读了这篇SO帖子 high DPI problems 但它并没有真正帮助我。
有任何建议如何解决这个问题?
EDIT2:关于我的意图的一些额外信息:我有大约50个已经工作的固定大小对话框,其中有数百个正确放置的固定大小控件。它们从较旧的C ++ GUI框架迁移到C#/ Winforms,这就是它们都是固定大小的原因。所有这些都使用9.25pt字体,96 dpi看起来很好。在旧框架下,缩放到120 dpi工作正常 - 所有固定大小的控件在两个维度上缩放相等。上周,我们在切换到120 dpi时在WinForms下检测到这种奇怪的缩放行为。你可以想象我们的大多数对话现在在120 dpi下看起来非常糟糕。我正在寻找一种避免完全重新设计所有这些对话框的解决方案。
EDIT3:为了测试这种行为,恕我直言,建立一个120 dpi的虚拟Windows XP环境是一个好主意,而开发环境只有96 dpi(至少,这就是我所做的)。在96和120 dpi之间切换通常需要在Win XP下重启,否则你看不到真正发生的事情。
// 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());
}
答案 0 :(得分:34)
我终于找到了我的问题的答案。简而言之,当单独设置每个控件的字体而不是设置包含表单的字体时,不会产生效果。这样,自动缩放功能就可以正常工作。有趣的是,即使AutoScaleMode属性设置为AutoScaleMode.Dpi
,设置控件的字体也会更改自动缩放行为,而不仅仅是当它设置为AutoScaleMode.Font
时。
作为一个实用的解决方案,我们创建了一个小命令行程序,它读取designer.cs文件,扫描所有控件是否有明确的字体赋值,如果没有,则将赋值添加到新创建的设计器代码副本中。我们将此程序嵌入到我们的自动测试套件中,因此每当表单获得新控件或添加新表单,并且dev忘记添加显式字体赋值时,测试将失败。在此期间,我们从第一次(4年前)提出这个问题开始就有了这个解决方案,从那以后,我们已经多次避免了扩展问题。
答案 1 :(得分:3)
如果这个令人沮丧的问题导致表单上的间距变得难以置信,那么,将AutoScaleMode更改为“None”并且问题完全消失。
答案 2 :(得分:1)
我能够解决VS 2008上的紧凑框架3.5的类似问题。在我的情况下,我有一个tabcontrol,每个标签页都有一个Panel,并且所有标签都已完全停靠在父母身上。每个面板包含多个标签和文本框控件,因此想法是当用户打开SIP(软输入面板/键盘)时,滚动条将出现在右侧,文本框控件将按比例缩放以避免绘制额外的水平滚动条
我的初始尝试将表单的自动缩放模式设置为dpi,每个tabpages的autoscroll属性设置为true,并且每个面板的autoscroll属性设置为true。每个标签都锚定在顶部,左侧,每个文本框控件都锚定在左侧,顶部,右侧。表单是在设计器中创建的,屏幕宽度为240像素,当在屏幕宽度为480像素的vga设备上运行时,文本框将在右边的空间上绘制,足以容纳2个滚动条(可能是一个用于标签页和一个用于面板)即使滚动条没有出现。激活SIP时,行为是正确的,因为所有文本框都已调整大小,但我仍然在文本框右侧和滚动条之间有40个左右的死区像素。
我只需将面板的autoscroll属性设置为false,然后将SIP设置为true,即启动SIP时的运行时间,即可解决此问题。这样就可以在像素宽度上自动缩放到屏幕的整个宽度,并在激活或取消激活滚动条时动态调整文本框控件的大小。
作为旁注,紧凑框架(3.5)没有Font自动缩放模式(只有none,dpi和inherit),但我尝试重置每个文本框控件的字体,原作者建议,但这没有对控件的自动缩放没有任何影响。
答案 3 :(得分:1)
我也发现这种行为很奇怪,并且在尝试自动扩展应用程序中的控件(及其相关字体)以响应大小或分辨率更改时,也遇到了类似的麻烦。
对于它的价值,这里简要介绍了我一直试图应用手动修复的内容:
首先,在应用程序启动时,我通过访问 Screen :: PrimaryScreen 属性中的 Bounds 成员捕获用户的系统分辨率,并以编程方式根据百分比调整表单大小与开发时间系统的区别。存储此新表单大小并将其用作所有将来调整的基本大小。此外,通过在运行时更改窗体的大小,它会调用我已处理的SizeChanged事件来进行缩放。简而言之,事件处理程序执行以下操作:
在用户选择尺寸后,根据新的宽度和高度百分比调整所有控件的大小,无论是从鼠标还是预定义的大小。
通过新的缩放系数
理论上,如果基于表单大小改变的百分比将控件移动到新位置,则应将它们保持相对于所有其他控件。当然,每次用户更改表单大小时,都会发生上述情况,而不仅仅是在初始运行时。
我不确定这个解决方案是否愚蠢,但是我花费或浪费时间与AutoScale,AutoSize和Anchoring一起战斗无济于事已经是天文数字。我不是想劫持你的部分,Doc,我只是觉得我会和你分享我的思考过程,也许会复活这个话题,希望有人对这个噩梦有一些更好的洞察力。
答案 4 :(得分:1)
请记住,由于提到的许多问题都与字体大小有关,因此与字体系列和大小保持一致非常重要。 这意味着在基本表单或基本用户控件(如果有的话)上设置字体,并让控件继承该设置。 我注意到,当我在其中有一个带有UserControl的表单时,如果我选择了控件并更改了字体大小,则会调整一些项目的大小并且有些项目没有。 我意识到没有调整大小的项目的字体设置是专门设置的(过度使用)。就在那个时候,我意识到当以粗体突出显示属性时它意味着什么。 因此,例如,如果您有一个标签,并且字体道具是粗体,这意味着有人更改了它。 您应该清除所有那些设置的字体道具,以便它们从父级获取字体,在这种情况下是包含的形式。 您只需突出显示字体属性文本并删除或右键单击字体支柱并选择清除即可。 这将从设计器文件中删除字体行,并允许控件从其父级继承字体。这可能会跳回到Microsoft Sans Serif,但如果你构建它将从其父级中获取字体 当然你应该使用布局面板和锚点和停靠属性来遵循正确的设计,但我发现如果你清除所有过度使用的字体道具,你会发现如果从表单中选择用户控件并将autoscalemode更改为没有,你会有更好的运气 同样用于测试,如果您随后更改userontrol的字体大小,其中的所有控件都应调整大小(只要没有字体道具被覆盖) 只要使用适当的布局面板设计表单,一切都应该在其他分辨率上显示正常。 至少到目前为止,这已经奏效了。
答案 5 :(得分: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();
我在添加动态控件时已经这样做了,但是没有更改字体。
答案 6 :(得分:0)
我也有这个问题。特别是LinkLabels显示的字体太大,AutoSize标签在末尾被剪裁。仅在第一个对话框(Main)上将AutoScaleMode更改为Dpi就可以解决所有表单的问题。谢谢你的提示。
答案 7 :(得分:-1)
我的WinForms应用程序有一个字体大小首选项,其中字体可以在主屏幕上设置为三个级别(8pt,10pt或12pt),它应该传播到所有子表单。
我遇到的具体问题是标签不是自动调整大小(通常用于多行标签)和多行文本框。
我找到的解决方案是在InitializeComponent()的Designer.cs文件中添加以下行和适用的控件:
this.Label1.Font = this.Font;
或
this.MultiLineTextbox.Font = this.Font;
这似乎正确设置了AutoScale。