一個程序最重要的部分之一是對鼠標和鍵盤操作的響應.
一. 理解鼠標事件.之前對鼠標事件的認識僅僅局限於處理控件的單擊與雙擊事件.但實際鼠標的操作包含很多.這里將以一個畫圖的小程序講解對鼠標的響應.
首先新建一個MFC程序,選擇對話框類型,將Mouse設為程序標題.建立程序框架后將對話窗口中所有的控件刪除.這樣整個對話框都可以用來作圖.
然后選中對話框窗口在右下角屬性窗口中的message(消息)選項,會列出一大串的事件消息.例如WM_LBUTONDOWN(鼠標左鍵被按下),WM_LBUTTONUP(鼠標左鍵被釋放),WM_MOUSEMOVE(鼠標在應用程序窗口空間中移動).畫圖程序主要通過mousemove事件來實現.選中事件add一個函數.進入函數的實現中加入如下代碼.
void CMouseDlg::OnMouseMove(UINT nFlags, CPoint point) { // TODO: 在此添加消息處理程序代碼和/或調用默認值 //檢查鼠標左鍵是否被按下 if ((nFlags&MK_LBUTTON) == MK_LBUTTON){ //獲取設備上下文 CClientDC dc(this); //畫出像素 dc.SetPixel(point.x, point.y, RGB(100, 100, 250)); } CDialogEx::OnMouseMove(nFlags, point); }
然后便可以運行程序了,只是這個畫圖程序與我們平常的並不一樣.這個后面再說.
我們先來看這段代碼,有兩個參數傳遞給這個函數.第一個參數是一組標記,用來判斷哪個鼠標的按鈕被按下,if中的判斷前一半是按位與,篩選為便是左鍵被按下的標記然后與后一半進行匹配;第二個參數是當前鼠標的位置,即鼠標在對話框窗口中的坐標,它包含兩個成員:x,y;然后可以使用這個信息在窗口上畫一個點.
在畫點之前我們還需要為對話窗口獲取設備上下文.即CClientDC dc(this);這條語句.通過為CClientDC類聲明一個新的實例來完成,參數this是當前窗口的指針.這個類封裝了設備上下文以及大多數可以對其進行的操作,包括所有的屏幕繪制操作.可以這么理解,設備上下文是一塊畫布,你可以在上面為你的程序作畫.
下一條語句就是畫一個點了,並且可以控制點的顏色,顏色控制是RGB的值來控制.
然后來說一說這個程序的問題,在運行程序的時候會發現如果移動過快就不是一條實線了而是一個個的點,這是程序的性質決定的我們的計算機每隔一段時間檢查鼠標的位置然后畫一個點,如果移動很慢還能是一條實現,如果過快就變成虛線了.那我們如何解決呢?很簡單,在鼠標確定的兩點間連上直線.可以按照如下步驟來實現:
首先向我們的對話框類--CMouseDlg類中添加兩個成員變量,m_iPrevX,m_iPrevY,類型為int,屬性為private.用來保存上一個點的數據.方便連線.
然后對上一個函數做點修改.
void CMouseDlg::OnMouseMove(UINT nFlags, CPoint point) { // TODO: 在此添加消息處理程序代碼和/或調用默認值 //檢查鼠標左鍵是否被按下 if ((nFlags&MK_LBUTTON) == MK_LBUTTON){ //獲取設備上下文 CClientDC dc(this); //從從前一點到當前點畫一條線 dc.MoveTo(m_iPrevX, m_iPrevY); dc.LineTo(point.x, point.y); //把當前點作為前一點保存 m_iPrevX = point.x; m_iPrevY = point.y; } CDialogEx::OnMouseMove(nFlags, point); }
dc.MoveTo(m_iPrevX, m_iPrevY);dc.LineTo(point.x, point.y);這兩局用來連線,首先需要移動到第一個位置,然后向第二個位置畫線.這是非常重要的一步,如果沒有這一步,windows將不知道從哪里開始畫.這時再運行程序會好一些不再出現虛線,但又有了一個新的問題,每次按下鼠標左鍵的時候便與剛才最后一個點進行連線.
現在進行最后的完善,將程序完善成:當鼠標左鍵被按下時,用當前的位置來初始化上一個位置的位置變量.
首先在消息模式中為對話框對象的WM_LBUTTONDOWN消息添加一個函數.函數內容如下:
void CMouseDlg::OnLButtonDown(UINT nFlags, CPoint point) { // TODO: 在此添加消息處理程序代碼和/或調用默認值 m_iPrevX = point.x; m_iPrevY = point.y; CDialogEx::OnLButtonDown(nFlags, point); }
再次運行時程序達到了我們想要的功能.
二. 對鍵盤的響應.
獲得鍵盤事件與獲得鼠標事件非常相似.但鍵盤的事件比鼠標事件要少的多.
WM_KEYDOWN 一個鍵被按下
WM_KEYUP 一個鍵被釋放
WM_SYSKEYDOWN F10被按下或者Alt與另一個鍵被同時按下
WM_SYSKEYUP F10被釋放或者Alt與另一個鍵被同時釋放
這些事件消息對於對話框窗口對象是可用的,並且只有窗口中沒有啟用的控件時才會被激發.
我們為上面的畫圖程序添加一點功能,當某個鍵被按下時,改變光標的形狀.A改為默認的光標;B改為I型豎線;C改為沙漏型;X退出程序
首先選擇WM_KEYDOWN消息添加一個函數.代碼如下:
void CMouseDlg::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags) { // TODO: 在此添加消息處理程序代碼和/或調用默認值 char cChar;//當前被按下的字符 HCURSOR hCursor = 0;//顯示光標句柄 HCURSOR hPrevCursor = 0;//以前的光標句柄 cChar = char(nChar);//將按下的鍵轉換為字符 if (cChar == 'A'){ //加載箭頭光標 hCursor = AfxGetApp()->LoadStandardCursor(IDC_ARROW); } if (cChar == 'B'){ //加載箭頭光標 hCursor = AfxGetApp()->LoadStandardCursor(IDC_IBEAM); } if (cChar == 'C'){ //加載箭頭光標 hCursor = AfxGetApp()->LoadStandardCursor(IDC_WAIT); } if (cChar == 'X'){ hCursor = AfxGetApp()->LoadStandardCursor(IDC_ARROW); hPrevCursor = SetCursor(hCursor); if (hPrevCursor) DestroyCursor(hPrevCursor); OnOK();//退出應用 } else{ if (hCursor){ hPrevCursor = SetCursor(hCursor); if (hPrevCursor) DestroyCursor(hPrevCursor); } } CDialogEx::OnKeyDown(nChar, nRepCnt, nFlags); }
這個函數有三個參數.第一個nChar是被按下的鍵,這個是字符的字符代碼,在代碼的的第一行需要被轉化為字符.然后就可直接比較了;第二個參數nRepCnt是這個鍵被按下的時間.通常被按下就釋放,這個值是1.如果一直按下這個鍵的值會上升,這個值告訴你Windows認為這個鍵被按下了多少次;第三個參數nFlags是個組合的標記,它可以確定在鍵被按下的時候是否同時有Alt鍵被按下,或者被按下的是一個擴展鍵.
改光標的過程為:第一步將光標調入內存中,通過LoadStandardCursor(IDC_ARROW)實現.然后這個光標的句柄被傳給SetCursor函數,這個函數將光標轉換為句柄所對應的光標,並返回前一個光標的句柄.可以使用DestroyCursor(hPrevCursor)銷毀前一個光標.
注意:當鼠標移動過程中光標將切換為默認的箭頭.
AfxGetApp函數:上面的LoadStandardCursor是通過AfxGetApp來調用的,這個函數是一個全局函數,它返回當前應用程序類的一個實例.應用程序類是當前應用程序中CWinApp的子孫類.對我們寫的程序來說就是CMouseApp類.當我們需要訪問封裝在CWinApp類中的功能或當前的派生類時,可以使用AfxGetApp函數得到指向它的指針.對繼承派生不熟的回去好好看看c++的書.