MFC中一個完整的應用一般由四個類組成:CWinApp應用類,CFrameWnd窗口框架類,CDocument文檔類,CView視類。
在程序運行時CWinApp將創建一個CFrameWnd框架窗口實例,而框架窗口將創建文檔模板,然後有文檔模板創建文檔實例和視實例,並將兩者關聯。一般來講我們只需對文檔和視進行操作,框架的各種行爲已經被MFC安排好了而不需人爲干預
接下來看看如何在程序中得到各種對象的指針:
- 全局函數AfxGetApp可以得到CWinApp應用類指針
- AfxGetApp()->m_pMainWnd爲框架窗口指針
- 在框架窗口中:CFrameWnd::GetActiveDocument得到當前活動文檔指針
- 在框架窗口中:CFrameWnd::GetActiveView得到當前活動視指針
- 在視中:CView::GetDocument得到對應的文檔指針
- 在文檔中:CDocument::GetFirstViewPosition,CDocument::GetNextView用來遍歷所有和文檔關聯的視。
- 在文檔中:CDocument::GetDocTemplate得到文檔模板指針
- 在多文檔界面中:CMDIFrameWnd::MDIGetActive得到當前活動的MDI子窗口
一般來講用戶輸入消息(如菜單選擇,鼠標,鍵盤等)會先發往視,如果視未處理則會發往框架窗口。所以定義消息映射時定義在視中就可以了,如果一個應用同時擁有多個視而當前活動視沒有對消息進行處理則消息會發往框架窗口。
在視圖中接收鼠標輸入:
鼠標消息是我們常需要處理的消息,消息分爲:鼠標移動,按鈕按下/鬆開,雙擊。利用ClassWizard可以輕鬆的添加這幾種消息映射,下面分別講解每種消息的處理。
WM_MOUSEMOVE對應的函數爲OnMouseMove( UINT nFlags, CPoint point ),nFlags表明了當前一些按鍵的消息,你可以通過“位與”操作進行檢測。
- MK_CONTROL Ctrl鍵是否被按下 Set if the CTRL key is down.
- MK_LBUTTON 鼠標左鍵是否被按下 Set if the left mouse button is down
- MK_MBUTTON 鼠標中間鍵是否被按下 Set if the middle mouse button is down.
- MK_RBUTTON 鼠標右鍵是否被按下 Set if the right mouse button is down.
- MK_SHIFT Shift鍵是否被按下 Set if the SHIFT key is down.
point表示當前鼠標的設備座標,座標原點對應視左上角。
WM_LBUTTONDOWN/WM_RBUTTONDOWN(鼠標左/右鍵按下)對應的函數爲OnLButtonDown/OnRButtonDown( UINT nFlags, CPoint point )參數意義和OnMouseMove相同。
WM_LBUTTONUP/WM_RBUTTONUP(鼠標左/右鍵鬆開)對應的函數爲OnLButtonUp/OnRButtonUp( UINT nFlags, CPoint point )參數意義和OnMouseMove相同。
WM_LBUTTONDBLCLK/WM_RBUTTONDBLCLK(鼠標左/右鍵雙擊)對應的函數爲OnLButtonDblClk/OnRButtonDblClk( UINT nFlags, CPoint point )參數意義和OnMouseMove相同。
下面我用一段僞代碼來講解一下這些消息的用法:
代碼的作用是用鼠標拉出一個矩形 global BOOL fDowned;//是否在拉動 global CPoint ptDown;//按下位置 global CPoint ptUp;//鬆開位置 OnLButtonDown(UINT nFlags, CPoint point) { fDowned=TRUE; ptUp=ptDown=point; DrawRect(); ... } OnMouseMove(UINT nFlags, CPoint point) { if(fDowned) { DrawRect();//恢復上次所畫的矩形 ptUp=point; DrawRect();//畫新矩形 } } OnLButtonUp(UINT nFlags, CPoint point) { if(fDowned) { DrawRect();//恢復上次所畫的矩形 ptUp=point; DrawRect();//畫新矩形 fDowned=FALSE; } } DrawRect() {//以反色屏幕的方法畫出ptDown,ptUp標記的矩形 CClientDC dc(this); MakeRect(ptDown,ptUp); SetROP(NOT); Rect(); }
座標間轉換:在以上的函數中point參數對應的都是窗口的設備座標,我們應該將設備座標和邏輯座標相區別,在圖32_g1由於窗口使用了滾動條,所以傳入的設備座標是對應於當前窗口左上角的座標,沒有考慮是否滾動,而邏輯座標必須考慮滾動後對應的座標,所以我以黃線虛擬的表達一個邏輯座標的區域。可以看得出同一點在滾動後的座標值是不同的,這一規則同樣適用於改變了映射方式的窗口,假設你將映射方式設置爲每點爲0.01毫米,那麼設備座標所對應的邏輯座標也需要重新計算。進行這種轉換需要寫一段代碼,所幸的是系統提供了進行轉換的功能DC的DPtoLP,LPtoDP,下面給出代碼完成由設備座標到邏輯座標的轉換。
CPoint CYourView::FromDP(CPoint point) { CClientDC dc(this); CPoint ptRet=point; dc.PrepareDC();//必須先準備DC,這在使用滾動時讓DC重新計算座標 //如果你作圖設置了不同的映射方式,則在下面需要設置 dc.SetMapMode(...) // dc.DPtoLP(&ptRet);//DP->LP進行轉換 return ptRet; }
在圖32_g1中以藍線標記的是屏幕區域,紅線標記的客戶區域。利用ScreenToClient,ClientToScreen可以將座標在這兩個區域間轉換。
在視圖中接收鍵盤輸入:
鍵盤消息有三個:鍵盤被按下/鬆開,輸入字符。其中輸入字符相當於直接得到用戶輸入的字符這在不需要處理按鍵細節時使用,而鍵盤被按下/鬆開在按鍵狀態改變時發送。
WM_CHAR對應的函數爲OnChar( UINT nChar, UINT nRepCnt, UINT nFlags ),其中nChar爲被按下的字符,nRepCnt表明在長時間爲鬆開時相當於的按鍵次數,nFlags中的不同位代表不同的含義,在這裏一般不使用。
WM_KEYDOWN/WM_KEYUP所對應的函數爲OnKeyDown/OnKeyUp( UINT nChar, UINT nRepCnt, UINT nFlags )nChar代表按鍵的虛擬碼值,如VK_ALT爲ALT鍵,VK_CONTROL爲Ctrl鍵。