WPF中DPI的問題


先搞清楚一下幾個概念:

  1. DPIdots  per  inch ,每英寸的點數。我們常說的鼠標DPI,是指鼠標移動一英寸的距離滑過的點數;打印DPI,每英寸的長度打印的點數;掃描DPI,每英寸掃描了多少個點。(更多請參考百度百科http://baike.baidu.com/view/49853.htm)
  2. 像素:pixel,picute和element的縮寫。像素可以簡單的理解為DPI里面的點。例如,顯示器的分辨率為1024像素*768像素,就是說顯示器的橫向可顯示1024個點(像素),縱向科研可以顯示768個點(像素)。有的顯示器每個顯示點排列的比較緊密,1英寸的長度內可以排列的點就多一些,有的排列比較疏松,點就少一些,所以像素和英寸之間是沒有直接的關系。(更多請參考百度百科http://baike.baidu.com/view/575.htm
  3. 分辨率:如上例,但是我們常說調整一下顯示器的分辨率,是啥意思呢?顯示器有一個自然的分辨率,就是顯示器的最大能耐,比如說,顯示器的自然分辨率為1600*1200,那么長度小於1600、寬度小於1200的分辨率都可以顯示的,1900*1300這樣子的分辨率就不行。
  4. Set Custom Text Size(DPI)WIN7這個DPI我個人覺得,和上述的DPI不是一回事,不知道為啥微軟把這個東東也叫DPI。首先,顯示設備的物理DPI是不太可能改變的;其次,朋友們發現,這個DPI的默認值是96,也就是1英寸打印96個像素,當我們把DPI調整為125后,1英寸打印125個點。我們的大多數應用程序界面是按照96DPI設計的,到了125DPI后,1英寸的長度不再是1英寸,應該小一點,實際上,沒有變小,反而變大了,所以,我覺得和上述的dpi不是一回事,最起碼不是改變物理DPI

 

WPF單位

WPF窗口和所有之內的元素都是使用“獨立設備單位”進行測量的。一個獨立設備單位被定義為一英寸的96分之一。要理解它在實際應用中的含義,需要考慮一個例子。

假設你在WPF中創建了一個96×96單位大小的按鈕,如果你使用的是標准的Windows DPI設置(96dpi),那么每一個獨立設備單位對應一個實際的物理象素。這是因為WPF采用如下的計算方式:

[物理單位大小]=[獨立設備單位大小] × [系統DPI點數]

                       =1/96 英寸 * 96 點每英寸

                       = 1 象素

本質上,WPF假定使用96個象素來組成一英寸是通過Windows的系統DPI的設置而獲得的。無論如何,實際值取決於你的顯示設備。

舉例來講,一個20英寸的液晶顯示器的最大分辨率是1600×1200象素。通過下面的簡單計算,就可以計算出這個顯示器的象素密度。

[系統每英寸點數]=Sqrt(1600×1600 +1200×1200) 象素 /19 英寸       (Sqrt為開根號)

                          =100點每英寸

這種情況下的象素密度是100dpi,這實際上比Windows假定的值要高一些。結果,這個顯示器按96×96象素顯示的按鈕比一英寸要小一些。

另一方面,15英寸的液晶顯示器的分辨率為1024×768,它的象素密度降到了大約85dpi每英寸,所以96×96的按鈕實際上比一英寸要大一些。

這兩種情形中,如果減少屏幕尺寸(選擇800×600分辨率),按鈕會相應成比例的變大,這是因為系統的DPI設置仍然是96dpi的緣故。換句話說,Windows仍然假定使用96象素大小作為一英寸。即使在分辨率較低,象素數已經遠遠少了很多的情形下。

系統DPI

目前為止,WPF的按鈕工作原理和應用程序中的其他用戶界面元素的工作原理是嚴格一致的。不同的是在你改變系統DPI設置時的結果。在早期的Windows中,這個特性偶爾被稱之為“大字體”。這是因為系統DPI影響了系統字體的大小,但是其他細節卻沒有甚么改變。

這恰恰是WPF不同的地方。WPF考慮系統特有的DPI設置,如果將系統DPI改為120dpi,WPF會假定它需要120個象素去填充一英寸的空間。WPF使用如下的計算去得出如何將邏輯單位轉換為物理設備的象素。

[Physical Unit Size]= [獨立設備單位尺寸]×[系統DPI]

                =1/96 英寸 ×120 dpi

                =1.25 象素

換句話說,當你設置系統DPI為120dpi的時候,WPF渲染引擎假定一獨立設備單位等於1.25象素大小。如果顯示一個96×96的按鈕,物理尺寸實際上是120象素×120象素,這是期待中的結果――一個在標准顯示器中顯示為一英寸的按鈕,在更高象素密度的顯示器中仍然顯示為一英寸。

這種自動縮放的功能如果只被應用到按鈕中,則實際上是沒有多大用處的。但是WPF使用獨立設備單位來顯示所有的東西,包括形狀,控件,文本和放進窗體中的任何元素。結果,系統的DPI可以被更改為任何需要的值,WPF會自動無縫的調整應用程序的尺寸。如何更改DPI取決於所用的系統,具體步驟就不贅述了。

位圖與矢量圖

當使用普通的控件時,可以利用WPF的分辨率的獨立性。WPF會自動關注所有的控件以保證它們都有正確的尺寸。可是,如果你想將圖片合並到你的應用程序,那就不能夠漫不經心了。舉例來說,在傳統的應用程序中,開發人員使用很小的位圖到工具條指令。在WPF應用程序中,這種方式卻不理想,因為會根據系統的DPI的設置而放大和縮小,位圖有可能顯示的很模糊。取而代之,在設計WPF應用程序界面時,即使最小的圖標也通常設計為矢量圖。矢量圖被定義為一個圖形集,正因為如此,它們可以被縮放到任意尺寸大小。

很難去高估分辨率獨立性的重要性。乍看起來它是一個簡單的(straightforward)的一流的(elegant)解決方案,解決了一個歷史悠久的問題。然而,為了設計的用戶界面能夠富有彈性。開發人員需要一種新的思維方法。

--=====================================華麗分隔符===============================================

WPF程序中的單位是與設備無關的單位,每個單位是1/96英寸,如果電腦的DPI設置為96(每個英寸96個像素),那么此時每個WPF單位對應一個像素,不過如果電腦的DPI設備為120(每個英寸120個像素),那此時每個WPF單位對應應該是120/96=1.25個像素

  一般在程序中我們常常需要得到當前屏幕的寬和高,常見做法有:

 

  1.System.Windows.Forms.Screen.PrimaryScreen.Bounds.Width

 

  System.Windows.Forms.Screen.PrimaryScreen.Bounds.Height

 

  這兩個方法可以返回當前屏幕選擇的分辨率,該分辨率是以像素為單位,在DPI為96的情況下我們可以利用它們來做一些控件的定位,因為此時WPF單位對應一個像素,而當DPI非96的情況下,用該分辨率來做定位就會發現誤差了,因此此時每個WPF單位並不是對應於一個像素

 

  2.SystemParameters.PrimaryScreenWidth

 

  SystemParameters.PrimaryScreenHeight

 

  這兩個方法可以返回當前屏幕的寬和高,它是與設備無關的單位(1/96英寸),因此用它來做控件的定位,在DPI改變的情況下,也不會發生定位上的誤差

 

  3.SystemParameters.WorkArea.Size.Width

 

  SystemParameters.WorkArea.Size.Height

 

  這兩個方法可以返回當前屏幕工作區的寬和高(除去任務欄),它也是與設備無關的單位,通常我們可以結合2和3來得到任務欄的高度

 

--=====================================華麗分隔符===============================================

WPF單位真的與分辨率無關嗎?

WPF從發布之日起,一直將“分辨率無關(resolution independence)”作為其亮點,聲稱使用WPF制作的用戶界面在輕巧的Ultra-Mobile PC的屏幕上和在50英寸的電視機上都能很好地顯示。微軟之所以稱WPF具備“分辨率無關”這一特性,主要是因為WPF的坐標單位設計成為以1/96英寸為一個邏輯像素單位,而不是與設備相關的像素單位。

但是微軟本身對WPF“分辨率無關”這一特性沒有作更多的具體解釋,導致用戶會產生很多誤解。

誤解之一

改變顯示器的分辨率設置,同一個WPF的用戶界面和繪制的圖形尺寸不會變化。

這個可以用一個非常簡單的實驗證明該結論是錯誤的。新建一個WPF應用程序窗口,其中高度為400DIUs(DIU:Device independent unit,設備無關單位),寬度為600DIUs,讓這個窗口分別在分辨率設置為1280 * 1024和800*600的環境下運行,如下圖所示,兩個窗口的尺寸是明顯不一樣的。

 

誤解之二

 改變顯示的DPI設置,同一個WPF的用戶界面和繪制的圖形尺寸不會變化。

顯示的DPI設置,在XP系統下是通過右鍵——屬性——設置選項卡——高級,可以調用出來,如下圖所示:

 

圖 2 顯示屬性DPI設置

這個也可以用同樣的方法進行證明該結論是錯誤的。仍然是高度為400DIUs[1],寬度為600DIUs的窗口分別運行在96DPI和192DPI兩種設置環境下。從下圖也可以明顯看出窗口的尺寸是不一樣的。

   

誤解之三

在不同屏幕上,如果DPI設置相同,則同一個WPF的用戶界面和繪制的圖形尺寸不會變化。

在這個地方有必要對屏幕的DPI設置進行一下解釋說明。DPI設置是指屏幕上每英寸多少個像素,比如當前設置為96DPI,即屏幕上96個像素為1英寸。一般的Windows XP系統有正常尺寸(96DPI)、大尺寸(120DPI)和自定義尺寸三種選項。既然WPF的坐標單位是以1/96英寸為一個邏輯像素單位,那么我們有理由相信,如果兩個顯示器的DPI設置是相同的,那么同一個WPF的用戶界面和繪制的圖形尺寸不會變化。很遺憾,這樣的結論依舊是一個錯誤。

CalvinP.Schrotenboer 也用一個實驗證明這是一個錯誤。實驗環境如表 1,比如桌面LCD顯示器實際屏幕寬度和高度(像素單位)為1600 x 1200,這個和普通的分辨率設置需要區分,這是顯示設備的最大分辨率或者說是物理分辨率,即物理上該顯示器屏幕上是1600 x 1200個像元,英文中又稱這種分辨率為“native resolution(原生分辨率)”。由於兩個屏幕物理尺寸也不一樣,所以實際的物理DPI可以通過表中的計算公式得到。實際的物理DPI和操作系統的DPI設置是沒有什么聯系的。

表 1實驗環境

 

實驗環境

系統一

系統二

顯示器類型

桌面LCD顯示器

筆記本LCD顯示器

屏幕寬度和高度

(像素單位)

1600 x 1200

1400 x 1050

屏幕寬度和高度

(英寸單位)

17.0 x 12.75

12.0 x 9.0

實際的物理DPI

縱向:1600 / 17.0 = 94DPI

橫向:1200 / 12.75 = 94DPI

縱向:1400 / 12 = 117DPI

橫向:1050 / 9 = 117DPI

操作系統的DPI設置

96DPI

96DPI

在兩個不同系統當中運行同一個WPF應用程序,該程序了繪制了一條長為384DIUs的直線,換算成英寸即為384/96= 4英寸。結果在兩個系統當中的實際尺寸如下圖所示:


 

問題出在哪兒了?

其實從表 1當中就能看出一些端倪,原因正是在於實際的物理DPI和操作系統設置的DPI不一致造成的。WPF無法知道當前使用設備實際的物理DPI為多少,相反通過操作系統的API函數獲得操作系統的DPI值,然后簡單地認為這就是實際的物理DPI值。比如在桌面LCD顯示器上,實際一個物理像元的尺寸為1/94英寸,由於操作系統設置為96DPI,因此WPF還固執地以為一個實際的像元為1/96英寸,因此線段長度為1/94 * 384 = 4.08英寸。筆記本顯示器實際一個物理像元的尺寸為1/117英寸,因此線段長度為1/117 * 384 = 3.28英寸。這個值和我們測量的結果正好相符。

那么我們有理由推測,如果將操作系統的DPI設置成實際的物理DPI,則能做到真正的“分辨率獨立”,即在兩個不同顯示器上顯示的線段長度都為4英寸,如圖 5所示:

 

WPF的“分辨率無關”到現在為止已經是山高月小,水落石出。那么我們還要接着討論另一個問題,在顯示器上存在這樣的問題,那么是否在打印機上也存在這樣的問題呢?仍然可以用一個實驗證明。同樣繪制一個4英寸的直線,分別在DPI設置為96DPI和120DPI下進行打印,得到的打印結果尺寸相同。如下圖所示:

結論

通過上面幾個實驗分析,我們可以得到如下兩個結論:

(1) WPF在打印得時候可以做到“分辨率無關”,即同一個WPF用戶界面和繪制的圖形尺寸在任何一台打印機上輸出都是一致的;

(2) 當顯示器實際象元的物理尺寸和系統設置的DPI保持一致的時候,WPF可以在顯示器上做到“分辨率無關”,即同一個WPF用戶界面和繪制的圖形尺寸在任何一台顯示器(實際象元的物理尺寸和系統設置的DPI保持一致)上輸出都是一致的。反之則無法保證。

更多的討論

“分辨率無關”這樣一個概念,由於微軟本身討論得不多,的確容易造成誤解。最為詳細地討論了WPF當中“分辨率無關”的是CalvinP.Schrotenboer 的一篇博文“Is WPF Really Resolution Independent?”。當然Charles Peztold也在自己的博客當中討論過這個問題。另外在微軟的論壇上StephenW,Charles Peztold等人也就WPF的“分辨率無關”和“設備無關”作了比較深入的討論。

用戶固然可以不理睬這些,但是對於一個程序員來說,尤其是一個正在做繪圖程序的程序員,尤其尤其是一個還需要打印輸出的繪圖程序員,是需要清楚這其中細節的。而且了解細節本身也是一件很愉快的事情。

 

參考文檔

http://www.cnblogs.com/xiaokang088/archive/2011/03/02/1969237.html
http://www.cnblogs.com/helloj2ee/archive/2009/04/21/1440709.html
http://www.cnblogs.com/dayrain/archive/2008/11/02/1324870.html


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM