一:關於坐標
MFC中繪圖時經常涉及到坐標計算,GetWindowRect和GetClientRect這兩個函數,是獲取邏輯坐標系中窗口或控件(其實也是窗口)大小和坐標的常用函數了,有什么不一樣的?
先說說什么叫邏輯坐標?講到邏輯坐標,它相對的一個概念是設備坐標,是為了屏蔽掉不同設備屬性差別而設置的抽象坐標系,說白了,就是獨立於設備坐標的統一接口,程序員不需要去在具體的設備上進行繪圖操作,而只需要在虛擬的環境下進行繪圖,就是CDC。
然后由設備驅動去負責虛擬坐標到實際設備坐標之間的轉換。通常邏輯坐標與設備坐標之間有不同的映射轉換關系,缺省模式下的映射方式是MM_TEXT,這種方式下的邏輯坐標的方向和單位與設備坐標的相同,也是以像素為單位來表示,X軸向右為正,Y軸向下為正,坐標原點位於窗口的左上角
然后再說上面的兩個函數之間的差別:
GetWindowRect得到的是相對於當前界面的整個窗口左上角的坐標,比如一個對話框程序,那就是相對於對話框左上角的坐標,從左往右是X方向,從上往下是Y方向。
注意:這個函數的相對坐標原點分兩種情況:
1 窗口還沒有初始化完成時:原點是整個窗口的左上角
2 初始化完成后,原點是設備屏幕左上角
GetClientRect得到的是相對於窗口客戶區左上角的坐標。
二:驗證
下面通過一段代碼來理解:
新建一個MFC對話框程序,在Dialog資源中,把一個靜態框加入進來。OnInitDialog中添加如下代碼:
BOOL CmfcdialogtestDlg::OnInitDialog() { /*.............其它代碼.............*/ /*****************for testing***************/ CRect wndRect1; CRect wndRect2; /****首先看對話框的******/ this->GetWindowRect(&wndRect1); //PrintRect("Dialog:GetWindowRect", wndRectScreen1); this->ScreenToClient(&wndRect1); //PrintRect("Dialog:ScreenToClient", wndRectClient1); this->GetClientRect(&wndRect2); //PrintRect("Dialog:GetClientRect", wndRectClient2); this->ClientToScreen(&wndRect2); //PrintRect("Dialog:ClientToScreen", wndRectScreen2); /****再看控件的**********/ CRect ctrlRect1; CRect ctrlRect2; CStatic *pCtrl = (CStatic*)GetDlgItem(IDC_STC_TEST); pCtrl->GetClientRect(&ctrlRect1); //PrintRect("static:GetClientRect", ctrlRectClient1); pCtrl->ClientToScreen(&ctrlRect1); //PrintRect("static:ClientToScreen", ctrlRectScreen1); pCtrl->GetWindowRect(&ctrlRect2); //PrintRect("static:GetWindowRect", ctrlRectScreen2); pCtrl->ScreenToClient(&ctrlRect2); //PrintRect("static:ScreenToClient", ctrlRectClient2); /*****************ending********************/ /*.............其它代碼.............*/ }
三:分析
我們分別觀察對整個對話框和靜態控件,分別調用這兩個函數的效果。首先看看對話框的:
(1)對話框部分的程序運行結果:
對話框: GetWindowRect: + &wndRect1 0x0017f410 {top=0 bottom=378 left=0 right=566} CRect * ScreenToClient:+ &wndRect1 0x0017f410 {top=-25 bottom=353 left=-3 right=563} CRect * GetClientRect: + &wndRect2 0x0017f3f8 {top=0 bottom=350 left=0 right=560} CRect * ClientToScreen:+ &wndRect2 0x0017f3f8 {top=25 bottom=375 left=3 right=563} CRect *
我們給出一份圖片分析:
第一個GetWindowRect得到的是整個窗口相對於窗口左上角的坐標,實際上就是這個對話框的大小。
然后ScreenToClient,注意了,我們發現top和left都變成了負值?為什么呢,因為這個時候的轉換是基於Client的原點進行的,即客戶區的左上角。因為原來的窗口左上角位於Client原點的左上方,所以是負值。
第二個GetClientRect得到的是客戶區的大小,因為不包含窗口周邊的藍色區域,所以比第一個的窗口大小要小。
然后ClientToScreen,這個就很好解釋了,計算相對於窗口左上角的坐標。你可以自己動手算一下。
(2)控件的程序運行結果:
控件: GetClientRect: + &ctrlRect1 0x0017f3e0 {top=0 bottom=170 left=0 right=285} CRect * ClientToScreen: + &ctrlRect1 0x0017f3e0 {top=127 bottom=297 left=73 right=358} CRect * GetWindowRect: + &ctrlRect2 0x0017f3c8 {top=127 bottom=297 left=73 right=358} CRect * ScreenToClient: + &ctrlRect2 0x0017f3c8 {top=0 bottom=170 left=0 right=285} CRect *
還是圖片分析:
同理,第一個GetClientRect得到是控件的大小。
然后ClientToScreen,可以看到這個時候,控件相對於對話框窗口左上角的位移是(127,73)。
第二個GetWindowRect,得到的值與上面的值相同,這個很好理解了,他們的含義是一樣的,就是控件在窗口中的坐標。
然后ScreenToClient,得到的值與第一個GetClientRect的值一樣。不多說了
為什么對話框和控件的轉換之間略有差別(兩次轉換結果不太一樣),主要是對話框包含了非客戶區,使得相對坐標不一致。
四:小結
算是把這個小問題給弄清楚了,適合和我一樣的新手學習。網上太多資料,很多都是錯的,還是自己動手,豐衣足食~!有錯誤,請指正!
可以看看這篇博文:http://www.cnblogs.com/flying-roc/articles/1970298.html