当鼠标位置到窗口边缘某区域内,SHOWWINDOW(SW_HIDE)

解决方案 »

  1.   

    最简单的是使用FindWindow,跟据窗口类类名或窗口标题名联系。不过也可使用其它通信手段,如命名事件,共享内存等。
      

  2.   

    enum {
    ALIGN_NONE,
    ALIGN_TOP,
    ALIGN_LEFT,
    ALIGN_RIGHT
    };void C***Dlg::hideWindow(BOOL hide)
    {
    CRect rc;
    GetWindowRect(rc);
    int moves = 20;
    int xStep, yStep;
    int xEnd, yEnd;
    int cxScreen; switch (alignType) {
    case ALIGN_TOP:
    xStep = 0;
    xEnd = rc.left;
    if (hide) {
    yStep = -rc.bottom / moves;
    yEnd = -rc.Height() + 2;
    } else {
    yStep = -rc.top / moves;
    yEnd = 0;
    }
    break;
    case ALIGN_LEFT:
    yStep = 0;
    yEnd = rc.top;
    if (hide) {
    xStep = -rc.right / moves;
    xEnd = -rc.Width() + 2;
    } else {
    xStep = -rc.left / moves;
    xEnd = 0;
    }
    break;
    case ALIGN_RIGHT:
    yStep = 0;
    yEnd = rc.top;
    cxScreen = GetSystemMetrics(SM_CXSCREEN);
    if (hide) {
    xStep = (cxScreen - rc.left) / moves;
    xEnd = cxScreen - 2;
    } else {
    xStep = (cxScreen - rc.right) / moves;
    xEnd = cxScreen - rc.Width();
    }
    break;
    }/* for (int i = 0; i < moves; i++) {
    rc.left += xStep;
    rc.top += yStep;
    SetWindowPos(NULL, rc.left, rc.top, 0, 0, SWP_NOSIZE | SWP_NOSENDCHANGING);
    RedrawWindow(NULL, NULL, RDW_INVALIDATE | RDW_UPDATENOW | RDW_ERASE | RDW_ALLCHILDREN);
    Sleep(20);
    }*/
    SetWindowPos(NULL, xEnd, yEnd, 0, 0, SWP_NOSIZE); isHide = hide;
    if (!isHide)
    SetTimer(IDT_HOVER, 50, NULL);
    }UINT C***Dlg::OnNcHitTest(CPoint point) 
    {
    UINT res = CDialog::OnNcHitTest(point);
    if (res == HTCLIENT)
    return HTCAPTION;// TRACE("OnNcHitTest point=%d  %d. res=%d ,alignType=%d\n",point.x,point.y ,res,alignType);
    switch (alignType) {
    case ALIGN_TOP:
    if (res == HTLEFT || res == HTRIGHT || res == HTTOP ||
    res == HTTOPLEFT || res == HTTOPRIGHT)
    // res = HTERROR;
    // else if (res == HTBOTTOMLEFT || res == HTBOTTOMRIGHT)
    res = HTBOTTOM;
    // if (isHide && res == HTBOTTOM)
    hideWindow(FALSE);
    break;
    case ALIGN_LEFT:
    if (res == HTTOP || res == HTBOTTOM || res == HTLEFT ||
    res == HTTOPLEFT || res == HTBOTTOMLEFT)
    // res = HTERROR;
    // else if (res == HTTOPRIGHT || res == HTBOTTOMRIGHT)
    res = HTRIGHT;
    if (isHide && res == HTRIGHT)
    hideWindow(FALSE);
    break;
    case ALIGN_RIGHT:
    if (res == HTTOP || res == HTBOTTOM || res == HTRIGHT ||
    res == HTTOPRIGHT || res == HTBOTTOMRIGHT)
    // res = HTERROR;
    // else if (res == HTTOPLEFT || res == HTBOTTOMLEFT)
    res = HTLEFT;
    if (isHide && res == HTLEFT)
    hideWindow(FALSE);
    break;
    }
    return res;
    }void C***Dlg::OnTimer(UINT nIDEvent) 
    {
    if (nIDEvent == IDT_HOVER) {
    CPoint pt;
    GetCursorPos(&pt);
    CRect rc;
    GetWindowRect(rc);
    rc.InflateRect(10, 10);
    if (!rc.PtInRect(pt)) {
    KillTimer(nIDEvent);
    hideWindow();
    }

    ****
    }void C***Dlg::OnMoving(UINT fwSide, LPRECT pRect) 
    {
    CRect rcOld, rcWorkArea;
    CPoint pt;
    GetWindowRect(rcOld);
    SystemParametersInfo(SPI_GETWORKAREA, 0, rcWorkArea, 0);
    GetCursorPos(&pt);
    int cxScreen = GetSystemMetrics(SM_CXSCREEN); if (alignType == ALIGN_NONE) {
    if (pt.y - rcWorkArea.top < 20) {
    alignType = ALIGN_TOP;
    pRect->top = 0;

    else if (pt.x - rcWorkArea.left < 20) {
    alignType = ALIGN_LEFT;
    pRect->left = 0;
    winNormalSize = rcOld.Size();

    else if (rcWorkArea.right - pt.x < 20) {
    alignType = ALIGN_RIGHT;
    pRect->right = cxScreen;
    winNormalSize = rcOld.Size();
    }
    if (alignType != ALIGN_NONE)
    SetTimer(IDT_HOVER, 50, NULL);
    } if (alignType == ALIGN_TOP) {
    if (pRect->top > 0)
    alignType = ALIGN_NONE;
    else {
    pRect->top = 0;
    pRect->bottom = rcOld.Height();
    }

    else if (alignType == ALIGN_LEFT) {
    if (pRect->left > 0) {
    pRect->right = pRect->left + winNormalSize.cx;
    pRect->bottom = pRect->top + winNormalSize.cy;
    alignType = ALIGN_NONE;
    } else {
    pRect->left = 0;
    pRect->right = rcOld.Width();
    pRect->top = 0;
    pRect->bottom = rcWorkArea.bottom;
    }

    else if (alignType == ALIGN_RIGHT) {
    if (pRect->right < cxScreen) {
    pRect->left = pRect->right - winNormalSize.cx;
    pRect->bottom = pRect->top + winNormalSize.cy;
    alignType = ALIGN_NONE;
    } else {
    pRect->right = cxScreen;
    pRect->left = pRect->right - rcOld.Width();
    pRect->top = 0;
    pRect->bottom = rcWorkArea.bottom;
    }
    } if (alignType == ALIGN_NONE)
    KillTimer(IDT_HOVER); CDialog::OnMoving(fwSide, pRect);

    }最开始,alignType = ALIGN_NONE
      

  3.   

    本代码的流程如下:
    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:
    {
    //保存窗体位置
    }