MFC的OnMouseMove移動位置和OnMouseWheel縮放實現

1. 基本作用

OnMouseMove響應鼠標移動時間

OnMouseWheel響應鼠標中鍵的滾動

 

2. 參數說明

afx_msgvoidOnMouseMove(UINTnFlags, CPointpoint);

nFlags說明:指示虛擬按鍵是否按下 ,此參數可以是任何下列值的組合

MK_CONTROL 當CTRL鍵按下時
MK_LBUTTON 當鼠標左鍵按下時
MK_MBUTTON 當鼠標中鍵按下時(滾動不屬於按下)
MK_RBUTTON 當鼠標右鍵按下時.
MK_SHIFT 當SHIFT按下時。

point說明:鼠標的X,Y座標:該座標爲鼠標相對所在窗口左上角爲基點的位置,是一個相對位置而不是在屏幕像素上的絕對位置。

 

afx_msg BOOL OnMouseWheel( UINT nFlags, shortzDelta, CPointpt );

nFlags同上

zDelta:大於0時爲向上滾動,小於0時爲向下滾動。A value less than zero indicates rotating back (toward the user) while a value greater than zero indicates rotating forward (away from the user). Windows下通常向上滾動縮小/窗口上移,反之放大/下移

pt::鼠標的X,Y座標,是以其父窗口的左上角爲基點的。Specifies the x- and y-coordinate of the cursor. These coordinates are always relative to the upper-left corner of the window.

 

3. 移動的效果實現

要實現移動,例如鼠標左鍵拖動butoon/圖片在窗口上移動,實現的結果附加要求:鼠標放在button/圖片的A點,移動之後,鼠標點依然在A點上

我們通過

a. 檢測鼠標已在button/圖片上(確保不是在哪都可以移動圖標),並且左鍵按下

b. 記錄鼠標當前點和上個點,計算兩個的偏移值,然後使用這個偏移值來移動button/圖片(MoveWindows)

(記錄上個點的方法可以使用靜態變量,移動完畢後,把當前點賦值給靜態變量)(具體實現可以靈活處理)

實現原理是:相對靜止---鼠標和對象相對位置不變,鼠標的偏移量,就是我們對象的偏移量

 

4. 縮放的效果實現(以鼠標點爲中心縮放)

要實現縮放,例如中件滑輪向上滑動縮小,向下滑動放大button/圖片,實現附加要求:鼠標放在button/圖片的A點,縮放之後,鼠標點依然在A點上,縮放是以鼠標點爲中心

a. 同樣檢測鼠標已在button/圖片上(確保不是在哪都可以縮放button/圖片)

b. 獲取當前button/圖片的高和寬(使用getClientRect)

c. 獲取當前pt點x,y相對於button/圖片位置,然後計算該位置相對於寬和高的比值

d. 判斷zDelta正負確定放大縮小(按比例調整圖片高度和寬度),並調整圖片左上點(left,top)的位置,確保c中的比值不變(---確保了以鼠標所在點爲中心放大或縮小)

實現原理是:相對移動---鼠標和所在對象點位置不變,鼠標所在對象點的周圍 長和寬 成比例的縮放


鼠標事件

在日常用到的很多軟件裏,當鼠標停留在某個菜單或者按鈕上一定事件,會彈出關於這個空間的一些提示信息,提示信息可以利用MFC中的CToolTipCtrl 類來實現,輸出提示並不難,但是如何判斷鼠標停留在某個按鈕上呢?可以在對話框中響應WM_SETCOUSE消息,在響應事件中判斷髮生事件的窗口句柄是不是該控件句柄,如果是做出響應的操作即可,但是如果要判斷光標離開控件,則相對複雜一點。因爲光標離開控件後,接收鼠標移動消息的窗口句柄肯定不是該控件句柄了,也可以解決,如下:

BOOLCMy12Dlg::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message)

{

         if(pWnd->GetDlgCtrlID() == IDC_BUTTON3)

         {

                   pWnd->SetWindowTextW(_T("Hover"));

         }

         else if(pWnd->GetDlgCtrlID() != IDC_BUTTON3)

         {

                   GetDlgItem(IDC_BUTTON3)->SetWindowTextW(_T("Leave"));

         }

         returnCDialogEx::OnSetCursor(pWnd, nHitTest, message);

}

(注:這樣做,當鼠標快速的移動的時候,就有可能出現鼠標已經離開了按鈕,但按鈕還是顯示鼠標停留在上面的現象。)

但是如果這樣的控件不止一個的時候,難道要用nelse if去判斷該句柄是不是對應的控件句柄嗎?這樣做就有點不合理了,有沒有更好的解決辦法呢?

經過試驗,以下方法可行且相對來說最好:

Cbutton派生子類MyHoverBtn,在子類中響應WM_MOUSEMOVE事件,在該事件的響應函數中添加如下代碼來響應鼠標的HoverLeave事件:

void MyHoverBtn::OnMouseMove(UINT nFlags, CPointpoint)

{

         TRACKMOUSEEVENT mouse_event;//定義鼠標移動事件結構體

         mouse_event.cbSize = sizeof(mouse_event);//定義結構體大小

         mouse_event.hwndTrack = m_hWnd;//關聯窗口句柄

         mouse_event.dwFlags = TME_HOVER |TME_LEAVE;//設置響應標記

         mouse_event.dwHoverTime = 100/*HOVER_DEFAULT=400*/;//設置Hover的時間

         _TrackMouseEvent(&mouse_event);

 

         CButton::OnMouseMove(nFlags, point);

}

然後在子類MyHoverBtn中響應WM_MOUSEHOVERWM_MOUSELEAVE消息,並在相應的事件響應函數中添加實現代碼;例如

void MyHoverBtn::OnMouseLeave()

{

         this->SetWindowTextW(_T("我離開了~"));

         CButton::OnMouseLeave();

}

 

 

void MyHoverBtn::OnMouseHover(UINT nFlags, CPointpoint)

{

         this->SetWindowTextW(_T("我來了~"));

         CButton::OnMouseHover(nFlags, point);

}

 

網上有人說可以在對話框中重載PreTranslateMessage函數,判斷消息類型和控件句柄,如果消息是WM_MOUSEMOVE,且該事件發生的控件句柄就是你要控制的控件句柄,則做出響應的處理,代碼如下:

BOOLCMy12Dlg::PreTranslateMessage(MSG* pMsg)

{

         CWnd* pWnd = GetDlgItem(IDC_BUTTON1);

         if(pMsg->hwnd == pWnd->GetSafeHwnd() && pMsg->message ==WM_MOUSEMOVE)

         {

                   CPoint point(pMsg->pt);

                   pWnd->ScreenToClient(&point);

                   CRect rect;

                   pWnd->GetClientRect(&rect);

                   if(rect.PtInRect(point))

                            pWnd->SetWindowTextW(_T("Hover"));

                   else

                            pWnd->SetWindowTextW(_T("Leave"));

                   returnTRUE;

         }

         returnCDialogEx::PreTranslateMessage(pMsg);

}

請注意綠色部分,這裏不合理,因爲當光標離開按鈕控件後,鼠標移動的事件目標控件就不是該按鈕了,與文章開頭的方法是一樣的,需要另外判斷···

說了幾種方式,還是派生子類,然後在子類中響應鼠標移動消息,從而做出對應操作最好。但是在設置Hover的時間的時候如果按照默認的HOVER_DEFAULT設置,也就是時間是400毫秒,那麼鼠標移動到控件上面後需要等待這個時間纔會響應OnMouseHover()於是我將時間減小爲100毫秒,比較合理了,如下:

       mouse_event.dwHoverTime = 100/*HOVER_DEFAULT=400*/;//設置Hover的時間

就是不懂如果將時間設置到很小,會不會導致程序不斷的提取鼠標移動消息,導致CPU佔用增加呢?

經測試將時間設置爲1毫秒,相對與設置爲100毫秒,cpu也不會大幅增加,MSDN對該參數的解釋如下:

dwHoverTime

Specifiesthe hover time-out (if TME_HOVER was specified in dwFlags), in milliseconds.Can be HOVER_DEFAULT, which means to use the system default hover time-out.

 

窗口重繪方面:

Invalidate()使窗口區域無效,以等待下一次重繪WM_PAINT消息將在消息隊列爲空時發送。與之類似的還有InvalidateRect(),可指定重繪具體的矩形區域。

UpdateWindow() 強制立即更新窗口,在OnPaint()函數中可以用GetUpdateRect()確定重繪區域或者判斷是否需要重繪。

RedrawWindowUpdateWindow類似,強制重繪窗口,但是它提供了更多的參數以設置重繪的區域等

在重繪時,需調用BeginPaint()來準備好用於繪畫的windows設備環境,填充 PAINTSTRUCT結構,並將DC句柄返回用於重繪。

繪畫結束時,需調用EndPaint()來釋放設備句柄。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章