第一種方法:
基礎知識:滑鼠在視窗內移動,點選或者釋放時都會產生WM_NCHITTEST訊息,響應函式OnNcHitTest會返回一個列舉值,系統會根據這個列舉值進行相應的處理。當返回值為HTCAPTION時,系統會認為此時滑鼠位於標題欄上,因而當滑鼠按下並移動時就會執行拖動操作。
- 在Duilib中在設定caption高度就能能讓使用者拖動視窗,其實就是當滑鼠按下時在OnNcHitTest訊息響應裡面返回HTCAPTION,讓系統預設為此時滑鼠位於標題欄。
LRESULT OnNcHitTest(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
POINT pt;
RECT rcClient;
RECT rcCaption;
rcCaption = m_pm.GetCaptionRect();
GetClientRect(m_pm.GetPaintWindow(), &rcClient);
pt.x = GET_X_LPARAM(lParam);
pt.y = GET_Y_LPARAM(lParam);
::ScreenToClient(m_pm.GetPaintWindow(), &pt);
//xml中設定bottom為-1時,整個視窗區域都可以拖動
if (-1 == rcCaption.bottom)
{
rcCaption.bottom = rcClient.bottom;
}
if ((pt.x >= rcClient.left)
&& (pt.x < rcClient.right)
&& (pt.y >= rcCaption.top)
&& (pt.y < rcCaption.bottom))
{
return HTCAPTION;
}
return __super::OnNcHitTest(uMsg, wParam, lParam, bHandled);
}
複製程式碼
- 最後,在視窗xml中指定caption="0,0,0,-1",不管視窗大小如何變,整個視窗就可以拖動了。其實這種方法也相當於把caption的bottom設定成視窗的高度。
- 但是,這樣做有個明顯的缺點,就是這個視窗的其他事件訊息都無法處理了。如果視窗中有一個編輯框就無法編輯了。
第二種方法
基礎知識:我們可以模擬在win32中視窗移動的函式處理過程。簡單的說,我們只需要在自己的視窗中對WM_LBUTTONDOWN、WM_MOUSEMOVE、WM_LBUTTONUP這三個訊息進行處理即可。在WM_LBUTTONDOWN中記錄滑鼠左鍵被按下時的資訊,WM_MOUSEMOVE中記錄滑鼠移動距離,WM_LBUTTONUP記錄滑鼠左鍵彈起。
LRESULT OnLButtonDown(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
//記錄滑鼠按下
is_lbutton_down_ = true;
//滑鼠按下時的座標
start_point_.x = GET_X_LPARAM(lParam);
start_point_.y = GET_Y_LPARAM(lParam);
bHandled = TRUE;
return 0;
}
LRESULT OnLButtonUp(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
//左鍵彈起,改變滑鼠狀態
is_lbutton_down_ = false;
bHandled = TRUE;
return 0;
}
LRESULT OnMouseMove(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
if (is_lbutton_down_ == true)
{
POINT point;
//獲取當前滑鼠的位置
::GetCursorPos(&point);
::ScreenToClient(m_pm.GetPaintWindow(), &point);
//獲取新的位置
int Dx = point.x - start_point_.x;
int Dy = point.y - start_point_.y;
start_rect_.left += Dx;
start_rect_.right += Dx;
start_rect_.top += Dy;
start_rect_.bottom += Dy;
//將視窗移到新的位置
SetWindowPos(m_hWnd, HWND_TOP, start_rect_.left, start_rect_.top, 0, 0, SWP_NOSIZE);
}
bHandled = TRUE;
return 0;
}
複製程式碼
-
但是,這種方法也存在一個明顯的bug,當你拖動視窗一直到工作列,然後鬆開滑鼠左鍵,這時視窗就會自動跟著滑鼠移動。
-
解決這個bug的方法就需要響應WM_MOUSELEAVE訊息,在該訊息中記錄滑鼠已經移出視窗。
-
預設情況下,視窗是不響應WM_MOUSELEAVE和WM_MOUSEHOVER訊息的,所以要使用_TrackMouseEvent函式來啟用這兩個訊息。呼叫這個函式後,當滑鼠在指定視窗上停留超過一定時間或離開視窗後,該函式會Post這兩個訊息到指定視窗。
MSDN:The _TrackMouseEvent function posts messages when the mouse pointer leaves a window or hovers over a window for a specified amount of time. This function calls TrackMouseEvent if it exists, otherwise it emulates it.
-
具體方法如下:
- 在視窗類中定義一個變數來標識是否追蹤當前滑鼠狀態,之所以要這樣定義是要避免滑鼠已經在窗體之上時,一移動滑鼠就不斷重複產生WM_MOUSEHOVER訊息。
BOOL is_mouse_track_=TRUE ; 複製程式碼
- 在OnMouseMove中呼叫_TrackMouseEvent函式
if (is_mouse_track_) { TRACKMOUSEEVENT csTME; csTME.cbSize = sizeof (csTME); csTME.dwFlags = TME_LEAVE|TME_HOVER; csTME.hwndTrack = m_hWnd ; csTME.dwHoverTime = 10; // 滑鼠在按鈕上停留超過10ms ,才認為狀態 HOVER ::_TrackMouseEvent (&csTME); is_mouse_track_=FALSE ; } 複製程式碼
- 在 OnMouseLeave 中再次允許追蹤滑鼠狀態
is_mouse_track_=TRUE ; 複製程式碼