在Windows應用程式開發中,視窗主要分為兩類:普通視窗與常駐頂層的無標題列高階視窗。普通視窗由Windows系統內建功能定義,擁有標題列、邊框、最大化/最小化按鈕以及系統預設的快捷鍵與滑鼠支援。使用者可透過拖曳標題列移動視窗,或拖曳邊框調整大小。
高階視窗則完全不同,它不具備普通視窗的任何屬性,整個視窗的控制邏輯必須由程式設計師自行實現。典型應用包括輸入法編輯器(IME)、UCWIN4.0平台、各類浮動工具箱、Office桌面工具列以及第三方漢字輸入平台等。這類視窗的一大特徵是:無需改變大小,但必須具備客戶區拖曳功能。
普通視窗的拖曳由系統自動處理,開發者無需操心。但高階視窗若要實現客戶區拖曳,開發者必須親自處理WM_LBUTTONDOWN、WM_MOUSEMOVE、WM_LBUTTONUP等訊息,自行控制拖曳命令區偵測、拖曳啟動、視窗移動、虛線框繪製與移動、拖曳結束等完整流程。筆者曾嘗試模擬系統標題列點擊或使用API發送內部拖曳命令,效果皆不理想,最終採用訊息迴路直接處理的方案。
當視窗包含專門的拖曳圖示按鈕時,需在資源檔中定義其矩形區域RECT DragRT,以相對視窗客戶區左上角的座標描述。在WM_LBUTTONDOWN處理中,透過MAKEPOINT(lParam)取得滑鼠位置,再用PtInRect(&DragRT, pt)判斷是否點擊在拖曳按鈕內,以啟動拖曳功能。
若採用非特定區域拖曳(如空白處),需先定義所有特定命令按鈕的矩形陣列DragRT[I]。在WM_LBUTTONDOWN中遍歷陣列,若滑鼠未落在任何按鈕區域,即視為點擊非特定區域,啟動拖曳。
為讓使用者知曉可拖曳區域,需實作游標動態變化。步驟如下:
DEBUG.EXE修正熱點偏移(位移10h與12h處的雙字節),使游標中心對齊滑鼠位置。imecurm CURSOR mousem.curHCURSOR hCurm = LoadCursor(hInstance, "imecurm");wc.hCursor = hCurm;WM_MOUSEMOVE中判斷滑鼠是否進入拖曳區域,呼叫SetCursor(hCurm)與SetCapture(hWnd)改變游標並捕獲輸入焦點;離開時恢復預設游標並ReleaseCapture()。拖曳啟動後,關鍵在於螢幕上顯示並更新代表視窗大小的虛線框。此部分需處理:
SetROP2(R2_NOTXORPEN)以便重疊擦除)SetWindowPos)完整流程包含:記錄拖曳起始螢幕座標、計算位移量、以DrawFocusRect或自訂GDI繪製虛線框、於WM_MOUSEMOVE中先擦除舊框再繪製新框、WM_LBUTTONUP確認新位置並銷毀虛線框。
高階視窗客戶區拖曳雖非系統內建,但透過訊息迴路精細控制滑鼠命令偵測、游標動態提示、虛線框繪製與移動邏輯,即可實現媲美經典軟體的流暢體驗。關鍵在於:明確區分特定與非特定命令區、善用PtInRect與游標資源管理、採用XOR繪製模式高效處理虛線框更新。