2010-06-04 161 views
32

當使用非默認字體時,Form.AutoScaleMode屬性和固定大小的控件一起出現了一些問題。我煮沸它歸結爲一個簡單的測試應用(的WinForms 2.0)只有一種形式中,一些固定尺寸的控制和以下性質:AutoScaleMode更改默認字體的問題

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

在120 DPI,Windows XP中,Windows窗體自動縮放功能產生本120 DPI例如:

Previous WinForm scaled to 120 dpi

正如您所看到的,組合框,按鈕,列表或樹視圖正確縮放,多行文本框在垂直軸上變得太大,而固定大小的標籤在垂直和水平方向上都不能正確縮放。似乎是在.NET框架中的錯誤?

編輯:一些提示:字體更改只適用於包含窗體,控件從窗體繼承它們的字體。如果可能,我想保持這種方式。

使用默認字體(Microsoft Sans Serif 8.25pt),不會發生此問題。使用AutoScaleMode = Font(當然具有足夠的AutoScaleDimensions)根據Font設置的時間(在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時在WinForms下檢測到了這種奇怪的縮放行爲。你可以想象,現在我們的大多數對話框在120dpi以下看起來都很糟糕。我正在尋找一種解決方案,避免完全重新設計所有這些對話框。

EDIT3:爲了測試這種行爲,恕我直言,當開發環境位於96 dpi(至少,這就是我所做的)時,建立一個120 dpi的虛擬Windows XP環境是個好主意。通常需要在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()); 
    } 

回答

33

我終於找到了我的問題的答案。簡而言之,如果單獨設置每個控件的字體而不是設置包含表單的字體,則不會產生這種效果。這樣,自動縮放功能就像它應該那樣工作。有趣的是,即使AutoScaleMode屬性設置爲AutoScaleMode.Dpi,但不僅在設置爲AutoScaleMode.Font時,設置控件的字體纔會更改自動縮放行爲。

作爲一個實用的解決方案,我們創建了一個小型命令行程序,讀取設計器。cs文件,掃描所有控件是否具有明確的字體分配,如果不是,則將該分配添加到新創建的設計器代碼副本中。我們將這個程序嵌入到我們的自動測試套件中,所以無論何時一個表單獲得新的控件,或添加一個新表單,並且dev忘記添加明確的字體分配,測試都將失敗。在這之間,我們從我第一次(4年前)提出這個問題的時候開始就有了這個解決方案,並且從那時起,我們多次從縮放問題中解脫出來。

+4

我遇到了同樣的問題。控制不響應'Scale'(或受保護的「ScaleControl」)方法來縮放其'Font'。我正在玩的解決方案是遞歸迭代所有控件,並且(神奇地)確定字體是否應該是什麼,並修復它。儘管如此,我仍然在努力研究一些陷阱。如果我得到解決方案,會報告回來 – 2011-11-24 19:14:36

+0

我感到你的痛苦。我在同一條船上,似乎這是一個普遍而且非常嚴重的問題,但似乎沒有人解決它或提供答案。 – 2012-04-09 18:02:18

+0

@ShacharWeis:對於我們的情況,我們有一個解決方案。我們創建了一個小程序(小於200 LOC),它讀取designer.cs文件,掃描所有控件是否具有明確的字體分配,如果沒有,則將該分配添加到代碼中。 – 2012-04-16 16:11:57

1

我能夠解決與VS 2008上的緊湊框架3.5類似的問題。在我的情況下,我有一個tabcontrol,並且每個tabpage有一個面板,並且所有都停靠爲他們的​​父母。每個面板都包含多個標籤和文本框控件,因此這個想法是當用戶打開滾動條出現在右側的SIP(軟輸入面板/鍵盤)時,文本框控件的寬度將縮放以避免繪製額外的水平滾動條。

我最初的嘗試將窗體的自動縮放模式設置爲dpi,每個tabpages的autoscroll屬性設置爲true,並將每個面板的autoscroll屬性設置爲true。每個標籤都固定在頂部,左側,每個文本框控件都固定在左側,頂部和右側。這些表單是在屏幕寬度爲240像素的設計器中創建的,並且在具有480像素屏幕寬度的vga設備上運行時,文本框將被塗上足夠2個滾動條的足夠空間(可能是一個用於tabpage和一個用於面板),即使滾動條沒有出現。當激活SIP時,行爲是正確的,因爲所有的文本框都調整了大小,但我仍然在文本框右側和滾動條之間有40個像素的死區。

我能夠通過簡單地將面板的autoscroll屬性設置爲false來解決問題,然後在激活SIP時將其設置爲true。這樣就可以在任何像素寬度上自動縮放屏幕的全寬,並在滾動條被激活或停用時動態調整文本框控件的大小。作爲一個說明,緊湊框架(3.5)沒有字體自動縮放模式(只有none,dpi和繼承),但我嘗試重置每個文本框控件的字體,原始作者建議,但是這個對控件的自動縮放沒有任何影響。

1

我也發現奇怪的行爲,並且在嘗試自動縮放我的應用程序中的控件(及其相關字體)時遇到類似的問題,以響應大小或分辨率更改。

對於它的價值,這裏是什麼,我一直在試圖申請一個手動修復一個簡要介紹:

首先,在應用程序啓動,我通過訪問成員捕獲用戶的系統分辨率Screen :: PrimaryScreen屬性,並根據與開發時系統的百分比差異以編程方式調整窗體大小。這個新的表格大小被存儲並用作所有未來調整的基本大小。另外,通過在運行時更改表單的大小,它會調用我處理的SizeChanged事件來執行縮放。概括地說,事件處理程序將執行以下操作:

  • 大小調整所有控件根據新的寬度和高度的百分數的用戶選擇的尺寸後,是否從鼠標或預定義的大小。

  • 更改由新的比例係數與每個控件關聯的字體大小

從理論上講,如果控制移動到基於百分比形式的尺寸改變一個新的位置,應該讓他們相對於所有其他控件。當然,每次用戶更改表單大小時,都會發生上述情況,而不僅僅是在初始運行時。

我不確定這個解決方案是否愚蠢,但是我花了時間,或者浪費了,與AutoScale,AutoSize和Anchoring作鬥爭的時間都是天文數字。我沒有試圖劫持你的部分,Doc,我只是想我會與你分享我的思維過程,也許重新提出這個話題,希望有人對這個噩夢有一些更深入的瞭解。

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

我這樣做時動態添加控件,但不能改變字體。

3

讓這個令人沮喪的問題導致窗體上的間距變得不相稱,但是,將AutoScaleMode更改爲「無」,問題完全消失。

+0

如果用戶在Windows控制面板中更改了其首選文本大小,則會導致文本被裁剪或用戶的首選文本大小被忽略。 – 2017-01-23 12:04:32

1

請記住,由於提到的許多問題都與字體大小有關,因此與字體系列和大小保持一致非常重要。 這意味着將字體設置爲基本窗體或基本用戶控件(如果有的話),並讓控件繼承該設置。 我注意到,當我有一個UserControl裏面的窗體,如果我選擇控件並更改字體大小,一些項目調整大小,有些則沒有。 我意識到,沒有調整大小的項目的字體設置專門設置(over-ridden)。那時我才意識到這些財產以粗體顯示時意味着什麼。 因此,例如,如果您有一個標籤,字體道具爲粗體,表示有人對其進行了更改。 你應該清除所有這些設置的字體道具,以便他們從父級獲取字體,在這種情況下是包含表單。 您可以簡單地突出顯示字體屬性文本並刪除或右鍵單擊字體道具並選擇清除。 這將刪除設計器文件中的字體行,並允許控件從其父項繼承該字體。這可能會跳回到微軟Sans Serif,但如果你構建它會從其父母拿起字體 當然,你應該遵循適當的設計使用佈局面板和錨和碼頭屬性,但我發現,一般來說,如果你清除所有結束你會發現如果你從你的表單中選擇用戶控件並將自動縮放控制代碼改爲無,那麼你將會有更好的運氣 同樣對於測試,如果你改變了userontrol的字體大小,那麼它的所有控件應該調整大小(只要沒有字體道具被覆蓋) 只要表單使用合適的佈局面板設計,所有內容在其他分辨率下都應該顯示正常。 至少到目前爲止,我已經工作。

+0

對不起,你似乎錯過了我的答案。 Winforms中有一個** bug **,它完全阻止使用您描述的繼承機制。如果要確保多行文本框和標籤正確縮放,每個控件都需要自己的字體分配。 – 2016-03-16 14:28:19

+0

哎呀,很抱歉... 我經歷過的唯一時間是更改字體時,它不會生效,直到我重新構建 除此之外,我不懷疑你是正確的,它發生在其他時間作爲好。 我的文章的主要觀點是提到尋找重疊的字體設置,因爲如果有的話,父字體設置不會生效。 – billymac 2016-03-18 18:14:38

0

我也有這個問題。特別是LinkLabels顯示的字體太大,AutoSize標籤在某處結束。在第一個對話框(Main)上將AutoScaleMode更改爲Dpi可以解決所有窗體的問題。謝謝你的提示。

-1

我的WinForms應用程序具有字體大小首選項,其中字體可以在主屏幕上設置爲三個級別(8pt,10pt或12pt),並且應該傳播到所有子表單。

我遇到的具體問題是不是自動調整大小的標籤(通常用於多行標籤)和多行文本框。

我找到的解決方案是與在InitializeComponent()在Designer.cs文件適用的控制添加以下行:

this.Label1.Font = this.Font; 

this.MultiLineTextbox.Font = this.Font; 

這似乎設置AutoScale正確。