最近寫個小程序,想讓窗體自動隱藏,到csdn搜索,發現不少網友問這個問題,可是具體實現的例子不多,我經過琢磨,實現可記錄停靠位置,可左上右三方停靠并隱藏。現將實現的例子拿出來供大家參考。 實現窗體自動隱藏方法有多種,可以使用定時器,不斷監視鼠標,當鼠標移動到窗體邊緣時,顯示窗體,當鼠標離開后隱藏窗體。也可以在鼠標收到WM_NCMOUSEMOVE或 WM_MOUSEMOVE(無邊框窗體)時激活窗體,然后在窗體消息WM_ACTIVE中設置顯示或隱藏,這種方法在窗體未失去焦點時不會隱藏。我在原本的設計中便使用這種方法,只是在設計時發現非主窗體不太合適,激活窗體時會出現兩個鍵盤焦點,而且我所需要的焦點是虛假的,可能我的設計不當,那位朋友若能完美實現,不妨交流一下。 本代碼的流程如下: 1. 初始化窗體時設置窗體位置,并設置依靠狀態窗體狀態。 2. 當接收到WM_MOUSEMOVE消息時,檢查窗體是否顯示,若否,顯示,并打開定時器。 3. 在WM_MOVING中檢測窗體位置,并自動靠攏邊界。 4. 在定時器中檢測鼠標,當鼠標離開窗體后,關閉定時器,隱藏窗體。 當然,在隱藏窗體時首先判斷位置,若停靠在邊緣,則隱藏,否則,不隱藏。 現在我們一步步看代碼。 int alignType; //全局變量,用于記錄窗體停靠狀態 enum { ALIGN_NONE, //不停靠 ALIGN_TOP, //停靠上邊 ALIGN_LEFT, //停靠左邊 ALIGN_RIGHT //停靠右邊 }; #define NEAR_SIZE 20 //定義自動停靠有效距離 #define NEAR_SIDE 2 //窗體隱藏后在屏幕上保留的像素,以使鼠標可以觸及 /* 下面代碼處理窗體消息WM_MOVING,pRect是由參數lParam傳來的指針 */ void OnMoving(HWND hWnd, LPRECT pRect) { //未靠邊界由pRect測試 if (alignType == ALIGN_NONE) { if (pRect->top < NEAR_SIZE) //在上邊有效距離內,自動靠攏。 { alignType = ALIGN_TOP; pRect->bottom -= pRect->top; pRect->top = 0; } if (pRect->left < NEAR_SIZE) //在左邊有效距離內 { alignType = ALIGN_LEFT; pRect->right -= pRect->left; pRect->left = 0; } else if (pRect->right + NEAR_SIZE > ScreenX) //在右邊有效距離內,ScreenX為屏幕寬度,可由GetSystemMetrics(SM_CYSCREEN)得到。 { alignType = ALIGN_RIGHT; pRect->left += (ScreenX - pRect->right); pRect->right = ScreenX; }
} else { //靠邊界由鼠標測試 POINT pt; GetCursorPos(&pt); if (alignType == ALIGN_TOP) { if (pt.y > NEAR_SIZE) //由于我們移動窗體時,鼠標在標題欄內,當鼠標位置超過有效距離后,我們可以考慮用戶要向下拖動鼠標。我們便解除上部停靠。 { alignType = ALIGN_NONE; pRect->bottom += NEAR_SIZE; pRect->top = NEAR_SIZE; } else { pRect->bottom -= pRect->top; pRect->top = 0; if (pRect->left < NEAR_SIZE) //在上部停靠時,我們也考慮左右邊角。 { pRect->right -= pRect->left; pRect->left = 0; } else if (pRect->right + NEAR_SIZE > ScreenX) { pRect->left += (ScreenX - pRect->right); pRect->right = ScreenX; } }
} if (alignType == ALIGN_LEFT) { if (pt.x - pRect->right > 0) //鼠標可以在整個標題條來回移動,所以我們不能簡單用左邊界和鼠標的距離來解除停靠,這里我們在鼠標離開右邊界時解除停靠。 { alignType = ALIGN_NONE; pRect->right += NEAR_SIZE; pRect->left = NEAR_SIZE; } else { pRect->right -= pRect->left; pRect->left = 0; if (pRect->top < NEAR_SIZE) //考慮左上角。 { pRect->bottom -= pRect->top; pRect->top = 0; } } } else if (alignType == ALIGN_RIGHT) { if (pt.x < pRect->left) //當鼠標離開左邊界時,解除停靠。 { alignType = ALIGN_NONE; pRect->left -= NEAR_SIZE; pRect->right -= NEAR_SIZE; } else { pRect->left += (ScreenX - pRect->right); pRect->right = ScreenX; if (pRect->top < NEAR_SIZE) //考慮右上角。 { pRect->bottom -= pRect->top; pRect->top = 0; } } } } } /* 在窗體初始化是設定窗體狀態,如果可以停靠,便停靠在邊緣 我本想尋求其他方法來解決初始化,而不是為它專一尋求一個函數,可是,窗體初始化時不發送WM_MOVING消息,我不得不重復類似任務. */ void NearSide(HWND hWnd) { int change = 0; RECT rect; GetWindowRect(hWnd, &rect); alignType = ALIGN_NONE; if (rect.left < NEAR_SIZE) { alignType = ALIGN_LEFT; if ((rect.left != 0) && rect.right != NEAR_SIDE) { rect.right -= rect.left; rect.left = 0; change = 1; } } else if (rect.right > ScreenX - NEAR_SIZE) { alignType = ALIGN_RIGHT; if (rect.right != ScreenX && rect.left != ScreenX - NEAR_SIDE) { rect.left += (ScreenX - rect.right); rect.right = ScreenX; change = 1; } } //調整上 else if (rect.top < NEAR_SIZE) { alignType = ALIGN_TOP; if (rect.top != 0 && rect.bottom != NEAR_SIDE) { rect.bottom -= rect.top; rect.top = 0; change = 1; } } if (change) { MoveWindow(hWnd, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, TRUE); }
} /* 窗體的顯示隱藏由該函數完成,參數hide決定顯示還是隱藏. */ void HideSide(HWND hWnd, BOOL hide) { RECT rc; int moves = 20; //動畫滾動窗體的步數,如果你覺得不夠平滑,可以增大該值. int xStep, yStep; int xEnd, yEnd; int width; int height; register int i; GetWindowRect(hWnd, &rc); width = rc.right - rc.left; height = rc.bottom - rc.top;
//下邊判斷窗體該如何移動,由停靠方式決定 switch (alignType) { case ALIGN_TOP: { //向上移藏 xStep = 0; xEnd = rc.left; if (hide) { yStep = -rc.bottom / moves; yEnd = -height + NEAR_SIDE; } else { yStep = -rc.top / moves; yEnd = 0; } break; } case ALIGN_LEFT: { //向左移藏 yStep = 0; yEnd = rc.top; if (hide) { xStep = -rc.right / moves; xEnd = -width + NEAR_SIDE; } else { xStep = -rc.left / moves; xEnd = 0; } break; } case ALIGN_RIGHT: { //向右移藏 yStep = 0; yEnd = rc.top; if (hide) { xStep = (ScreenX - rc.left) / moves; xEnd = ScreenX - NEAR_SIDE; } else { xStep = (ScreenX - rc.right) / moves; xEnd = ScreenX - width; } break; } default: return; } //動畫滾動窗體. for (i = 0; i < moves; i++) { rc.left += xStep; rc.top += yStep; SetWindowPos(hWnd, NULL, rc.left, rc.top, 0, 0, SWP_NOSIZE | SWP_NOSENDCHANGING); RedrawWindow(hWnd, NULL, NULL, RDW_INVALIDATE | RDW_UPDATENOW); Sleep(5); } SetWindowPos(hWnd, NULL, xEnd, yEnd, 0, 0, SWP_NOSIZE); if (!hide) //如果窗體已被顯示,設置定時器.監視鼠標. { SetTimer(hWnd, WM_TIMER, 500, NULL); } } //下面就是通過窗體回調函數將這些函數組織起來. //這里僅列出使用的消息 case WM_TIMER: //定時器消息 { POINT pt; RECT rc; GetCursorPos(&pt); GetWindowRect(hWnd, &rc); if (!PtInRect(&rc, pt)) //若鼠標不在窗體內,隱藏窗體. { KillTimer(hWnd, WM_TIMER); HideSide(hWnd, TRUE); } break; } case WM_CREATE: case WM_INITDIALOG: //初始化消息 { SetWindowPos(...) //程序保存窗體上次靠位置,在這里恢復. NearSide(hWnd); break; } //這兩個消息是在窗體移動開始時和結束時產生的,我們在窗體開始移動時關閉定時器,移動結束后再打開,這樣避免窗體移動時隱藏,金山快譯的浮動條就有這種情況出現. case WM_ENTERSIZEMOVE: { KillTimer(hWnd, WM_TIMER); break; } case WM_EXITSIZEMOVE: { SetTimer(hWnd, WM_TIMER, 500, NULL); break; } case WM_MOUSEMOVE: //受到窗體移動消息時,判斷窗體是否顯示, { RECT rc; GetWindowRect(hWnd, &rc); if (rc.left < 0 || rc.top < 0 || rc.right > ScreenX) //未顯示 HideSide(hWnd, FALSE); break; } case WM_MOVING: //處理窗體移動時消息,實現自動停靠 { OnMoving(hWnd, (LPRECT) lParam); break; } case WM_MOVE: { //保存窗體位置 } 這些代碼是從我的程序中摘錄出來的, 我已盡量檢查它們的完整性, 但人總有犯錯的時候, 如果你發現這些代碼有問題, 或有更好的建議, 請聯系我, 我的E-Mail:ggg82@sina.com
|