开发精彩实例:窗体自动隐藏

 最近写个小程序,想让窗体自动隐藏,到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:
 {
 //保存窗体位置
 }

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值