(1)Windows坐标系統
Windows坐标系分為邏輯坐标系和裝置坐标系兩種,GDI支援這兩種坐标系。一般而言,
GDI的文本和圖形輸出函數使用邏輯坐标,而在客戶區移動或按下滑鼠的滑鼠位置是采用裝置坐标。
<1>邏輯坐标系是面向DC的坐标系,這種坐标不考慮具體的裝置類型,在繪圖時,Windows會根據目前設定的映射模式将邏輯坐标轉換為裝置坐标。
<2>裝置坐标系是面向實體裝置的坐标系,這種坐标以像素或裝置所能表示的最小長度機關為機關,X軸方向向右,Y軸方向向下。裝置坐标系的原點位置(0, 0)不限定在裝置顯示區域的左上角。
裝置坐标系分為螢幕坐标系、視窗坐标系和客戶區坐标系三種互相獨立的坐标系。
1.螢幕坐标系以螢幕左上角為原點,一些與整個螢幕有關的函數均采用螢幕坐标,如GetCursorPos()、SetCursorPos()、CreateWindow()、MoveWindow()。彈出式菜單使用的也是螢幕坐标。
2.視窗坐标系以視窗左上角為坐标原點,它包括視窗标題欄、菜單欄和工具欄等範圍。
3.客戶區坐标系以視窗客戶區左上角為原點,主要用于客戶區的繪圖輸出和視窗消息的處理。滑鼠消息的坐标參數使用客戶區坐标,CDC類繪圖成員函數使用與客戶區坐标對應的邏輯坐标。
(2)坐标之間的互相轉換
程式設計時,有時需要根據目前的具體情況進行三種裝置坐标之間或與邏輯坐标的互相轉換。
1.MFC提供了兩個函數CDC::DPtoLP()和CDC:: LPtoDP()用于裝置坐标與邏輯坐标之間的互相轉換。
2.MFC提供了兩個函數CWnd::ScreenToClient()和CWnd::ClientToScreen()用于螢幕坐标與客戶區坐标的互相轉換。
GetWindowRect() 得到的是在螢幕坐标系下的RECT(即以螢幕左上角為原點)
GetClientRect() 得到的是在客戶區坐标系下的RECT(即以所在視窗左上角為原點,去掉了标題欄計算,僅僅是個大小,傳回值的左上角永遠為0,0)
CRect rect;
GetWindowRect(&rect);
ScreentoClient(&rect);
不等同于
GetClient(&rect);
舉個例如:有個單文檔程式
View調用GetWindwoRect(&rect), 得到的坐标是:左上角(33,99),右下角(1040,524),這是View相對于螢幕的坐标,當調用ScreenToClient(&rect),rect變成為:左上角(-2,-2),右上角(1007,423),-2,-2是client和window之間的間隔差--border。
rect = CRect(0,0,1,1),當調用ClientToScreen(&rect),rect變成為:左上角(35,101),右上角(36,102),這是也有2個機關的border的作用。
Frame調用GetWindowRect(&rect),得到的坐标是:左上角(25,25),右下角(1050,551),這是Frame相對于螢幕的坐标,與View的坐标有差别是因為Frame還有菜單欄,工具欄。
(3).映射模式
映射模式确定了在繪制圖形時所依據的坐标系,它定義了邏輯機關的實際大小、坐标增長方向,所有映射模式的坐标原點均在裝置輸出區域(如客戶區或列印區)的左上角。此外,對于某些映射模式,使用者還可以自定義視窗的長度和寬度,設定視圖區的實體範圍。
Windows定義了8種映射模式,見下表。
映射模式使得程式員可不必考慮輸出裝置的具體裝置坐标系,而在一個統一的邏輯坐标系中進行圖形的繪制。
映射方法(Mapping Mode)
邏輯機關 坐标軸方向
MM_TEXT(預設方式)
1 pixel X軸正方向朝右,Y軸正方向朝下
MM_LOMETRIC
0.1 mm X軸正方向朝右,Y軸正方向朝上
MM_HIMETRIC
0.01 mm X軸正方向朝右,Y軸正方向朝上
MM_LOENGLISH
0.01 inch X軸正方向朝右,Y軸正方向朝上
MM_HIENGLISH
0.001 inch X軸正方向朝右,Y軸正方向朝上
MM_TWIPS
1/1440 inch X軸正方向朝右,Y軸正方向朝上
MM_ISOTROPIC
自定義(X=Y) 自定義
MM_ANISOTROPIC
自定義(X!=Y) 自定義
當繪制的圖形需要随着視窗的大小改變而自動改變的時候,一般選擇MM_ISOTROPIC和MM_ANISOTROPIC映射方式。它們的唯一差別就是前者的X軸和Y軸的邏輯機關的大小是相同的,單詞“isotropic”就是各個方向相等的意思,此映射方式适合繪制圓或正方形。而實際應用中,常常給X軸和Y軸取不同的比例,這時候選擇MM_ANISOTROPIC映射方式。單詞“anisotropic”就是各個方向相異的意思。
在預設的模式(MM_TEXT)下,邏輯坐标的方向和機關與裝置坐标的方向和機關相同,也是以像素為機關來表示的,X軸向右為正,Y軸向下為正,坐标原點位于視窗的左上角。邏輯坐标和裝置坐标即使在預設模式下其數值也未必一緻,除了在以下兩種情況下:
1. 視窗為非滾動視窗
2. 視窗為滾動視窗,但垂直滾動條位于滾動邊框的最上端,水準滾動條位于最左端,但如果移動了滾動條這兩種坐标就不一緻了。
(4).自定義映射模式
“視窗”和“視口”的概念:
視窗(Window):對應邏輯坐标系上程式員設定的區域
視口(Viewport):對應實際輸出裝置上程式員設定的區域
1.視窗原點是指邏輯視窗坐标系的原點在視口(裝置)坐标系中的位置,視口原點是指裝置實際輸出區域的原點。
2.除了映射模式,視窗和視口也是決定一個點的邏輯坐标如何轉換為裝置坐标的一個因素。一個點的邏輯坐标按照如下式子轉換為裝置坐标:
裝置(視口)坐标 = 邏輯坐标 – 視窗原點坐标 + 視口原點坐标
//定義坐标映射方式
WINGDIAPI int WINAPI SetMapMode(HDC, int);
此API函數在MFC中封裝為CDC::virtual int SetMapMode(int nMapMode);
//定義邏輯視窗區域,機關為邏輯機關(Logical)
WINGDIAPI BOOL WINAPI SetWindowExtEx (HDC, int, int, LPSIZE);
此API函數在MFC中封裝為CDC::virtual CSize SetWindowExt(int cx, int cy);
//設定邏輯視窗的原點坐标,預設原點為(0,0)。
WINGDIAPI BOOL WINAPI SetWindowOrgEx(HDC, int, int, LPPOINT);
此API函數在MFC中封裝為CDC::CPoint SetWindowOrg(int x, int y);
注意:SetWindowOrg(Ex) 隻有在映射模式為MM_ANISOTROPIC或MM_ISOTROPIC時才有意義。
//定義視口的坐标軸方向及區域、定義域和值域,機關為像素(Pixel)
WINGDIAPI BOOL WINAPI SetViewportExtEx(HDC, int, int, LPSIZE);
此API函數在MFC中封裝為CDC::virtual CSize SetViewportExt(int cx, int cy);
注意:SetViewportExt(Ex) 隻有在映射模式為MM_ANISOTROPIC或MM_ISOTROPIC時才有意義。
//設定視口的原點坐标,預設原點為(0,0)。
WINGDIAPI BOOL WINAPI SetViewportOrgEx(HDC, int, int, LPPOINT);
此API函數在MFC中封裝為CDC:: virtual CPoint SetViewportOrg(int x, int y);
void MoveWindow( int x, int y, int nWidth, int nHeight, BOOL bRepaint = TRUE );
void MoveWindow( LPCRECT lpRect, BOOL bRepaint = TRUE );
參數:
x指定了CWnd的左邊的新位置。
y指定了CWnd的頂部的新位置。
nWidth指定了CWnd的新寬度。
nHeight指定了CWnd的新高度。
bRepaint指定了是否要重畫CWnd。如果為TRUE,則CWnd象通常那樣在OnPaint消息處理函數中接收到一條WM_PAINT消息。如果這個參數為FALSE,則不會發生任何類型的重畫操作。這應用于客戶區、非客戶區(包括标題條和滾動條)和由于CWnd移動而露出的父視窗的任何部分。當這個參數為FALSE的時候,應用程式必須明确地使CWnd和父視窗中必須重畫的部分無效或重畫。lpRectCRect對象或RECT結構,指定了新的大小和位置。說明這個函數改變視窗的位置和大小。對于頂層的CWnd對象,x和y參數是相對于螢幕的左上角的。對于子對象,它們是相對于父視窗客戶區的左上角的。
MoveWindow函數發送一條WM_GETMINMAXINFO消息。處理這個消息時,CWnd得到一個改變最大和最小的視窗預設值的機會。如果傳遞給MoveWindow成員函數的參數超過了這些值,則在WM_GETMINMAXINFO處理函數中可以用最小或最大值來代替這些值。
BOOL CWnd::SetWindowPos(const CWnd* pWndInsertAfter, int x, int y, int cx, int cy,UINT nFlags );
傳回值如果函數成功,則傳回非零值;否則傳回0。
參數pWndInsertAfter辨別了在Z軸次序上位于這個CWnd對象之前的CWnd對象。這個參數可以是指向CWnd對象的指針,也可以是指向下列值的指針:l wndBottom 将視窗放在Z軸次序的底部。如果這個CWnd是一個頂層視窗,則視窗将失去它的頂層狀态;系統将這個視窗放在其它所有視窗的底部。l wndTop 将視窗放在Z軸次序的頂部。l wndTopMost 将視窗放在所有非頂層視窗的上面。這個視窗将保持它的頂層位置,即使它失去了活動狀态。wndNoTopMost 将視窗重新定位到所有非頂層視窗的頂部(這意味着在所有的頂層視窗之下)。這個标志對那些已經是非頂層視窗的視窗沒有作用。有關這個函數以及這些參數的使用規則參見說明部分。x指定了視窗左邊的新位置。y指定了視窗頂部的新位置。cx指定了視窗的新寬度。cy指定了視窗的新高度。nFlags指定了大小和位置選項。這個參數可以是下列值的組合:l SWP_DRAWFRAME 圍繞視窗畫出邊框(在建立視窗的時候定義)。l SWP_FRAMECHANGED 向視窗發送一條WM_NCCALCSIZE消息,即使視窗的大小不會改變。如果沒有指定這個标志,則僅當視窗的大小發生變化時才發送 WM_NCCALCSIZE消息。l SWP_HIDEWINDOW 隐藏視窗。SWP_NOACTIVATE 不激活視窗。如果沒有設定這個标志,則視窗将被激活并移動到頂層或非頂層視窗組(依賴于pWndInsertAfter參數的設定)的頂部。l SWP_NOCOPYBITS 廢棄這個客戶區的内容。如果沒有指定這個參數,則客戶區的有效内容将被儲存,并在視窗的大小或位置改變以後被拷貝回客戶區。l SWP_NOMOVE 保持目前的位置(忽略x和y參數)。l SWP_NOOWNERZORDER 不改變擁有者視窗在Z軸次序上的位置。l SWP_NOREDRAW 不重畫變化。如果設定了這個标志,則不發生任何種類的變化。這适用于客戶區、非客戶區(包括标題和滾動條)以及被移動視窗覆寫的父視窗的任何部分。當這個标志被設定的時候,應用程式必須明确地無效或重畫要重畫的視窗和父視窗的任何部分。l SWP_NOREPOSITION 與SWP_NOOWNERZORDER相同。l SWP_NOSENDCHANGING 防止視窗接收WM_WINDOWPOSCHANGING消息。l SWP_NOSIZE 保持目前的大小(忽略cx和cy參數)。l SWP_NOZORDER 保持目前的次序(忽略pWndInsertAfter)。l SWP_SHOWWINDOW 顯示視窗。
調用這個成員函數以改變子視窗、彈出視窗和頂層視窗的大小、位置和Z軸次序。視窗在螢幕上按照它們的Z軸次序排序。在Z軸次序上處于頂端的視窗将程式在所有其它視窗的頂部。子視窗的所有坐标都是客戶坐标(相對于父視窗客戶區的左上角)。視窗可以被移動到Z軸次序的頂部,既可以通過将 pWndInsertAfter參數設為&wndTopMost,并確定沒有設定SWP_NOZORDER标志,也可以通過設定視窗的Z軸次序使它位于所有現存的頂層視窗上方。當一個非頂層視窗被設為頂層視窗時,它擁有的視窗也被設為頂層的。它的擁有者不發生變化。如果頂層視窗被重新定位到Z軸次序的底部(&wndBottom)或任何非頂層視窗之後,則它将不再是頂層視窗。當頂層視窗被變為非頂層視窗時,它所有的擁有者和它擁有的所有視窗都被變為非頂層視窗。如果既沒有指定SWP_NOACTIVE标志也沒有指定SWP_NOZORDER标志(這意味着應用程式要求視窗被同時激活并放入指定的Z軸次序),則pWndInsertAfter參數中指定的值将隻在下列環境下适用:l 在pWndInsertAfter參數中既沒有指定&wndTopMost也沒有指定&wndNoTopMost。
這個視窗不是活動視窗。應用程式不能激活一個非活動視窗但同時又不把它帶到Z軸次序的頂部。應用程式可以沒有任何限制地改變活動視窗的Z軸次序。非頂層視窗可能擁有一個頂層視窗,但是反之則不成立。任何被頂層視窗擁有的視窗(例如對話框)都将自己變為頂層視窗,以確定所有被擁有的視窗位于它們的擁有者上方。在Windows 3.1或更新的版本中,可以将視窗移動到Z軸次序的頂部,并通過設定它們的WS_EX_TOPMOST風格而将之鎖定在那裡。這種頂層視窗即使在失去活動狀态以後也會保持頂層位置。例如,選擇WinHelp的Always On Top指令會使幫助視窗變為頂層,并且在你傳回應用程式之後它還保持可見。要建立一個頂層視窗,應在調用SetWindowPos的時候将 pWndInsertAfter參數設為&wndTopMost,或者在建立視窗的時候設定WS_EX_TOPMOST風格。如果Z軸次序中包含了任何具有WS_EX_TOPMOST風格的視窗,則用&wndTopMost移動的視窗将被放到所有非頂層視窗的頂部,但是位于任何頂層視窗的下面。當應用程式激活一個不具有WS_EX_TOPMOST風格的非活動視窗時,該視窗将被移動到所有非頂層視窗的上方,但是位于所有頂層視窗的下方。如果在調用SetWindowPos的時候pWndInsertAfter參數被設為&wndBottom,并且CWnd是一個頂層視窗,則該視窗失去頂層狀态(WS_EX_BOTTOM風格被清除),并且系統将視窗放在Z軸次序的底部。