很多基於對話框的應用程序都是不帶框架的,也就是說對話框沒有標題欄。眾所周知,窗口的移動都是通過鼠標點住標題欄拖動窗口實現的,那麼現在沒有了標題欄,如何移動對話框呢?本文擬針對這個問題提出解決的辦法。
解決這個問題有兩種方案。一種很業余,另外一種比較專業。前者使用一種常規思路處理鼠標拖拽事件。當窗口獲得WM_LBUTTONDOWN(OnLButtonDown)時,通過設置標志並調用SetCapture控制鼠標使應用程序進入移動模式。進入移動模式之後,只要有WM_MOUSEMOVE消息過來,就可以據此移動框架窗口。最後,當用戶釋放鼠標按鈕,則WM_LBUTTONUP消息處理例程清除標志並調用ReleaseCapture函數將鼠標控制返還給Windows。之所以說這種方法業余,主要是因為比較繁瑣,首先要決定窗口移到哪?然後要想好如何重繪窗口等等,而且根據屏幕顯示屬性對話框“效果”頁中“視覺效果”項的“拖動實顯示窗口內容”復選框是不是選中,拖動效果是不同的。那麼你怎麼知道的設置呢?(方法是調用SystemParametersInfo(SPI_GETDRAGFULLWINDOWS)。Windows要程序員來事務巨細地處理這些繁瑣的事情真是太遭了。由於Windows本身知道通過鼠標點住標題欄可以移動窗口,那麼能不能將鼠標在窗口客戶區任何地方的點擊拖動行為都模仿成好像是在標題欄中一樣呢?
圖五 顯示器的屬性對話框-“效果”標簽
答案是肯定的。有心的讀者可能已經猜測到下一步該怎麼做了答。實際上,用鼠標點住對話框背景進行拖動操作並不難,但是你必須了解在標題欄裡拖動窗口的原理。Windows首先確定鼠標點中了那個窗口,然後向那個窗口發送一個WM_NCHITTEST消息找出此窗口的哪個“非客戶區”(如邊界、最大化/最小化按鈕、菜單、標題等等)擁有鼠標光標。接著默認的窗口過程響應消息並返回一個特定的代碼。如果鼠標指針落在標題欄中,那麼這個神奇的特定代碼就是HTCAPTIONA。如果WM_NCHITTEST返回HTCAPTION,那麼Windows便進入拖拽模式,以便對窗口進行移動操作。所以要想在客戶區裡用鼠標拖動對話框,那麼只要在客戶區裡模仿標題欄裡的鼠標拖動行為即可。這個我們下面要介紹的比較專業的方法,其主要思路是處理WM_NCHITTEST消息:
UINT CMyDialog::OnNcHitTest(CPoint pt)
{
CRect rc;
GetClientRect(&rc);
ClientToScreen(&rc);
return rc.PtInRect(pt) ? HTCAPTION : CDialog::OnNcHitTest(pt);
}
上面這個代碼很容易理解,當鼠標落在客戶區內,函數返回HTCAPTION。對於一個簡單的對話框來說,僅僅用這個代碼就完全可以實現在對話框背景內的拖動操作。因為Windows使用z-order坐標來確定鼠標下是哪個窗口,所以對話框中其它的所有對象照常工作。如果用戶單擊某個控制,只要這個控制不是靜態位圖圖像或者文本,那麼Windows都將鼠標事件發送到該控制上,而不是對話框。由於靜態位圖圖像或者文本對於對話框是透明的,所以鼠標在上面的拖動同樣實現移動,而對於對話框中的編輯框、按鈕、組合框等其它非靜態控制則按通常的行為方式運行。
如圖二是本文例子程序運行畫面:
如圖二
用鼠標在背景上可以拖動對話框。如果應用不是一個純粹的對話框程序,而是CFormView或其它非對話框視圖,處理方法幾乎是一樣的,只需在視圖代碼中做一點小小的改動即可,因為Windows在發送WM_NCHITTEST消息時,是將它發送到鼠標光標下的框架/視圖最頂層非透明窗口,由於視圖首先獲得WM_NCHITTEST消息。所以只要在視圖的WM_NCHITTEST消息處理例程中返回HTTRANSPARENT,讓視圖對鼠標點擊透明即可。注意是在在視圖中,而不是框架中加上下面代碼:
UINT CMyView::OnNcHitTest(CPoint pt)
{
return HTTRANSPARENT;
}
這樣做以後,Windows將忽略視圖並繼續搜索能接收WM_NCHITTEST的窗口。如果順利的話,將找到父窗口,這時用與對話框相同的WM_NCHITTEST處理代碼即可,即在客戶區中的點擊返回HTCAPTION。你甚至可以通過鼠標坐標的象素計算,在規定的局部范圍內實現視圖透明。
UINT CMyView::OnNcHitTest(CPoint pt)
{
return PointLiesWithinDraggableRegion(pt) ?
HTTRANSPARENT : CView::OnNcHitTest(pt);
}
這樣拖拽/移動特性只能在PointLiesWithinDraggableRegion的非零窗口區域內有效。為此請參見另外一篇文章的基於框架/視圖的例子:“MFC框架程序中全屏顯示特性的實現”,其中就實現了用鼠標在客戶區拖動整個框架窗口的效果。
本文配套源碼