[C#](純Winform)讓UI適應不同解析度

  • 10804
  • 0

摘要:[C#](純Winform)讓UI適應不同解析度

這是一項非常大的困擾,所以我把我找來的知識全部做個整理。

特別是針對Winform的部分,若是WPF建議使用ViewBox並且設定適合的模式。
 

大致的概念是這樣:
 

Step 1: 將你的UI先固定大小,但實際操作還是依個人以及美工的複雜度(圖片越複雜就要小心所謂的作用範圍問題,以免圖片重疊等等),像有的時候Label需要用AutoSize=true,有的則是false。

Step2:用程式碼取得現在的解析度(顯示器)

Step3:依照公式:你的UI大小除以現在的解析度,來做UI的調整。
 

調整時要記得不只是大小改變,位置也要改變!

若是在容器裡就用for抓每個元件,若容器裡還有容器,則用遞迴的方式做處理。

範例以容器為Panel為例:


            string sHeight = SystemInformation.PrimaryMonitorSize.Height.ToString();
            string sWidth = SystemInformation.PrimaryMonitorSize.Width.ToString();
            double sw = Convert.ToDouble(sWidth);
            double sh = Convert.ToDouble(sHeight);
            double sizeX = sw / 1024;
            double sizeY = sh / 768;             
            foreach (Control p in panel1.Controls)
            {
                p.Top = Convert.ToInt32(Convert.ToDouble(p.Top) * sizeX);
                p.Left = Convert.ToInt32(Convert.ToDouble(p.Left) * sizeY);
                if (p is Label || p is TextBox || p is ComboBox)
                {
                    p.Font = new Font(p.Font.FontFamily, p.Font.SizeInPoints * Convert.ToSingle(sizeX) * Convert.ToSingle(sizeY));
                    if (!(p is Label))
                        p.Width = Convert.ToInt32(Convert.ToDouble(p.Width) * sizeX);
                }
                else
                {
                    p.Width = Convert.ToInt32(Convert.ToDouble(p.Width) * sizeX);
                    p.Height = Convert.ToInt32(Convert.ToDouble(p.Height) * sizeY);
                }
            }

 

panel1即是容器,而p則是裡面的元件,假設原本設計的解析度是1024x768。

但如果容器裡還有容器,比方另一個panel,

原本的寫法是這樣:


            string sHeight = SystemInformation.PrimaryMonitorSize.Height.ToString();
            string sWidth = SystemInformation.PrimaryMonitorSize.Width.ToString();
            double sw = Convert.ToDouble(sWidth);
            double sh = Convert.ToDouble(sHeight);
            double sizeX = sw / 1024;
            double sizeY = sh / 768;            
            foreach (Control p in panel1.Controls)
            {
                p.Top = Convert.ToInt32(Convert.ToDouble(p.Top) * sizeX);
                p.Left = Convert.ToInt32(Convert.ToDouble(p.Left) * sizeY);
                if (p is Label || p is TextBox || p is ComboBox)
                {
                   
                    p.Font = new Font(p.Font.FontFamily, p.Font.SizeInPoints * Convert.ToSingle(sizeX) * Convert.ToSingle(sizeY));
                    if (!(p is Label))
                        p.Width = Convert.ToInt32(Convert.ToDouble(p.Width) * sizeX);
                }
                else
                {
                    if (p is Panel)
                    {
                        foreach (Control c in p.Controls)
                        {
                            c.Top = Convert.ToInt32(Convert.ToDouble(c.Top) * sizeX);
                            c.Left = Convert.ToInt32(Convert.ToDouble(c.Left) * sizeY);
                            if (c is Label || c is TextBox || c is ComboBox)
                            {

                                c.Font = new Font(c.Font.FontFamily, c.Font.SizeInPoints * Convert.ToSingle(sizeX) * Convert.ToSingle(sizeY));
                                if (!(c is Label))
                                    c.Width = Convert.ToInt32(Convert.ToDouble(c.Width) * sizeX);
                            }
                            else
                            {
                                c.Width = Convert.ToInt32(Convert.ToDouble(c.Width) * sizeX);
                                c.Height = Convert.ToInt32(Convert.ToDouble(c.Height) * sizeY);
                            }
                        }
                    }
                    p.Width = Convert.ToInt32(Convert.ToDouble(p.Width) * sizeX);
                    p.Height = Convert.ToInt32(Convert.ToDouble(p.Height) * sizeY);
                }
            }

 

所以概念就是若碰到容器則要以容器本身再做一次同樣的事,所以就是:
 


double sw = Convert.ToDouble(SystemInformation.PrimaryMonitorSize.Width);
double sh = Convert.ToDouble(SystemInformation.PrimaryMonitorSize.Height);
double sizeX = sw / 1024;
double sizeY = sh / 768;         
private void chResolution(Panel p,sizeX,sizeY)
        {
            if (p.Name == "panel1" || p.Name == "panel2" || p.Name == "panel3")
            {
                p.Top = Convert.ToInt32(Convert.ToDouble(p.Top) * sizeX);
                p.Left = Convert.ToInt32(Convert.ToDouble(p.Left) * sizeY);
                p.Width = Convert.ToInt32(Convert.ToDouble(p.Width) * sizeX);
                p.Height = Convert.ToInt32(Convert.ToDouble(p.Height) * sizeY);
            }
            foreach (Control c in p.Controls)
            {
                c.Top = Convert.ToInt32(Convert.ToDouble(c.Top) * sizeX);
                c.Left = Convert.ToInt32(Convert.ToDouble(c.Left) * sizeY);
                if (c is Label || c is TextBox || c is ComboBox)
                {
                    c.Font = new Font(c.Font.FontFamily, c.Font.SizeInPoints * Convert.ToSingle(sizeX) * Convert.ToSingle(sizeY));
                    if (!(c is Label))
                        c.Width = Convert.ToInt32(Convert.ToDouble(c.Width) * sizeX);
                }
                else
                {
                    if (c is Panel) chResolution(c as Panel);
                    c.Width = Convert.ToInt32(Convert.ToDouble(c.Width) * sizeX);
                    c.Height = Convert.ToInt32(Convert.ToDouble(c.Height) * sizeY);
                }
            }
        }

中間遇到control是Panel時呼叫自己。
中間的is Label那些是為了調整Label等元件的字型,
因為這些元件,如果你改變大小(Width, Height),只是改變作用範圍,字的大小不變。

目前這樣的作法差不多能解決我的困擾了,
若是再遇到比較困難的處理會繼續研究,也一併更新文章。

 

Edit:

實際操作後,修改了遞迴的版本程式碼。
panel1~panel3是最大的panel(就是一開始在winform裡的),若是從winform的角度抓取panel,則不用if (p.Name == "panel1" || p.Name == "panel2" || p.Name == "panel3")的部分。因為先在winform的角度將所有的control修改大小了(即是使用基本公式)

若是取從form取得Control則是用:

http://stackoverflow.com/questions/653284/get-available-controls-from-a-form-using-c-sharp