-- 作者:卷积内核
-- 发布时间:4/9/2008 8:22:00 AM
-- 实现窗体自动隐藏(c代码)
最近写个小程序,想让窗体自动隐藏,到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: { //保存窗体位置 }
|