1. 基本作用
OnMouseMove響應鼠標移動時間
OnMouseWheel響應鼠標中鍵的滾動
2. 參數說明
nFlags說明:指示虛擬按鍵是否按下 ,此參數可以是任何下列值的組合
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);
}
(注:這樣做,當鼠標快速的移動的時候,就有可能出現鼠標已經離開了按鈕,但按鈕還是顯示鼠標停留在上面的現象。)
但是如果這樣的控件不止一個的時候,難道要用n個else if去判斷該句柄是不是對應的控件句柄嗎?這樣做就有點不合理了,有沒有更好的解決辦法呢?
經過試驗,以下方法可行且相對來說最好:
從Cbutton派生子類MyHoverBtn,在子類中響應WM_MOUSEMOVE事件,在該事件的響應函數中添加如下代碼來響應鼠標的Hover和Leave事件:
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_MOUSEHOVER和WM_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()確定重繪區域或者判斷是否需要重繪。
RedrawWindow與UpdateWindow類似,強制重繪窗口,但是它提供了更多的參數以設置重繪的區域等
在重繪時,需調用BeginPaint()來準備好用於繪畫的windows設備環境,填充 PAINTSTRUCT結構,並將DC句柄返回用於重繪。
繪畫結束時,需調用EndPaint()來釋放設備句柄。