MFC提供了一個框架性的打印和打印預覽功能代碼,它的基本思想是將實際顯示和打印文檔的代碼合二為一,即都由此文檔關聯的CView中的OnDraw(CDC *pDC)來處理,由MFC框架根據用戶的操作來決定傳進來的pDC是指向屏幕還是打印機,當pDC指向屏幕設備,就在屏幕上顯示文檔,
當指向打印機設備時就打印文檔。這些都很好理解,但是,當我們要打印預覽時,傳進來的pDC就有些特別了。因為打印預覽是在屏幕上進行的,所以通常我們會認為這時傳進來的pDC一定也是屬於屏幕DC一類的。但是其實不然,這個pDC是根據當前默認的打印機的屬性來構造的。舉個例子,我的計算機上安裝了一個打印機,它的默認打印紙是A4大小(210mm X 297mm),而它的默認分辨率是1200dpi。當打印預覽時Cview::OnDraw函數中的pDC指向的就是那個用來預覽輸出的DC,現在我們調用pDC->GetDeviceCaps(HORZSIZE),pDC->GetDeviceCaps(VERTSIZE),這兩個函數用來取得DC顯示區域的大小,以毫米為單位,得到的返回值為210和297,這恰好就是A4紙的大小。接著再調用pDC->GetDeviceCaps(HORZRES),pDC->GetDeviceCaps(VERTRES),這兩個調用返回的也是DC顯示區域的大小,不過單位是象素(或者說是點dot),得到的值是9917和14031。讓我們看看這兩個值與上面的210和297有什麼關系。1英寸等於25.4毫米,那麼210毫米的長度按照1200dpi的分辨率能容納多少點?當然是210/25.4×1200,算算看等於多少?沒錯,就是9917左右(實際是9921.259……),同樣可以將297換算成14031。
現在來看看另外一個問題。當我們用MemDC.CreateCompatibleDC(pDC)來創建一個與pDC兼容的內存DC時,調用GetDeviceCaps(HORZSIZE)等函數得到的值是否與pDC的一樣呢?答案是不一樣。對內存DC調用上述4個函數得到的值分別是320和240(Windows一般固定返回這兩個數值),1024和768(這正是屏幕的分辨率)。假如pDC的映射模式是MM_LOMETRIC,那麼MemDC的映射模式會和它一樣嗎?還是不一樣。MemDC在剛創建的時候映射模式就是默認的MM_TEXT,而不會和pDC一樣。那麼當執行如下操作:pDC->SetMapMode(MM_LOMETRIC);MemDC.SetMapMode(MM_LOMETRIC);後,對同一個CRect進行DPtoLP,或者LPtoDP得到的結果一樣嗎?仍然不一樣,為什麼映射模式相同了,坐標轉換的結果還是不一樣?
我們來弄清楚WINDOWS的映射模式到底怎麼一回事。對於DC來說有兩個區域,一個叫“視口”(Viewport),一個叫“窗口”(Window,與普通的顯示出的窗口不一樣,只是一個概念)。你可以把“窗口”想象成一個邏輯上的繪圖區域,而所謂的邏輯坐標系就是基於“窗口”的。假設以DC的(0,0)點為左上頂點畫一個20×20的矩形,你就可以把它理解成在“窗口”中畫的,可是度量單位是什麼呢?那就要根據DC的當前映射模式來定了,比如現在的模式是MM_LOMETRIC,那麼單位就是0.1mm,也就是我們在這個DC上畫了一個2mm×2mm大小的矩形。“視口”是一個與實際顯示設備緊密相連的概念,它對應著實際的輸出區域,設備坐標系就是基於它的,而度量單位則永遠是象素。DC根據視口和窗口的屬性產生一種規則保證把一個窗口恰好映射為一個視口。待我們真正要顯示或打印圖形的時候,DC實際上用這種規則把Window坐標(邏輯坐標)映射為Viewport坐標(設備坐標)進行輸出,至於DC會把2mm的線段轉換為多少象素長的線段,我們無需關心,WINDOWS自會料理一切。CDC分別提供GetViewportOrg(),GetWindowOrg()來取得Viewport和Window的原點坐標(相對於一個假想的中立的坐標系),還有GetViewportExt(),GetWindowExt()來取得Viewport和Window大小(分別基於他們各自的度量單位)。那麼DPtoLP和LPtoDP到底做了些什麼呢?其實很簡單,拿LPtoDP來說就是執行了這樣一個運算:
xViewport=(xWindow-oxWindow)×cxViewport/cxWindow+oxViewport
yViewport=(yWindow-oyWindow)×cyViewport/cyWindow+oyViewport
而DPtoLP則是上述運算的逆運算。還拿剛才的pDC,MemDC來說,映射模式都是MM_LOMETRIC,打印設置不變。對pDC調用GetViewportOrg等函數得到Viewport原點為(0,0),大小為9917×-14031象素(沒錯,是負的,因為MM_LOMETRIC下y軸方向朝上),Window原點為(0,0),大小為2099×2970(0.1mm);同樣得到MemDC的Viewport原點為(0,0),大小為1024×-768象素,Window原點為(0,0),大小為3200×2400(0.1mm)。這就可以解釋pDC和MemDC的坐標轉換為什麼不同了。