MFC:拖动无标题窗口的三种方法

有的时候,我们需要自绘窗口的标题,或者隐藏了窗口标题,那么问题来了,这就是怎么拖动无标题的窗口

这个问题的本质便是怎么在窗口的客户区上,拖动鼠标,并且移动窗口

通常状态下,一个窗口只有在标题栏的区域,才会响应鼠标拖动的消息。而在窗口的客户区并不会响应。

这样便有了一个方法,就是我们可以欺骗windows,让它误认为鼠标点击窗口客户区时是点击在了标题栏上。

有这么个消息,就是WM_NCHITTEST。

MSDN对它的解释是:

The WM_NCHITTEST message is sent to a window when the cursor moves, or when a mouse button is pressed or released. If the mouse is not captured, the message is sent to the window beneath the cursor. Otherwise, the message is sent to the window that has captured the mouse.

这个消息是当鼠标移动或者有鼠标键按下时候发出的。

如果WM_NCHITTEST的消息响应函数的返回值是HTCLIENT,表示鼠标点击的是客户区,则Windows会向窗口发送WM_LBUTTONDOWN消息;如果WM_NCHITTEST的消息响应函数的返回值不是HTCLIENT(可能是HTCAPTION、HTCLOSE、HTMAXBUTTON等),即鼠标点击的是非客户区,Windows就会向窗口发送WM_NCLBUTTONDOWN消息。

接下来便是实现的具体方法,这个方法分为两步:

1.在MESSAGE_MAP中添加ON_WM_NCHITTEST()

2.手动添加响应函数UINT OnNcHitTest(CPoint point)

其中应这么处理:

UINT CYourClass::OnNcHitTest(CPoint point)
{
 UINT nh=CDialog::OnNcHitTest(point);
 return ((nh==HTCLIENT)?HTCAPTION:nh);
}

或则这样

UINT CYourClass::OnNcHitTest(CPoint point)
{

CRect          rc;
 GetClientRect(&rc);
 ClientToScreen(&rc);
 return rc.PtInRect(point) ? HTCAPTION : CDialog::OnNcHitTest(point);

}

或则这样win32

 if (uMsg == WM_NCHITTEST)
 {
  POINT ptPos= {0};
  GetCursorPos(&ptPos);
  ScreenToClient(m_hWnd, &ptPos);
  RECT rcClient  ;
  GetClientRect(m_hWnd,&rcClient);
  UINT nHitTest = PtInRect(&rcClient,ptPos);
  if (nHitTest == HTCLIENT) //如果是客户区
   nHitTest = HTCAPTION; //则把它当成标题栏
  return nHitTest;
 }


第二个办法很简单了,就是处理鼠标左击消息

首先添加鼠标左键单击消息,用自动添加便可。

void CYourClass::OnLButtonDown(UINT nFlags, CPoint point)
{
 // TODO: Add your message handler code here and/or call default
 SendMessage(WM_SYSCOMMAND,0xF012,0);
 CDialog::OnLButtonDown(nFlags, point);
}

需要注意的是SendMessage(WM_SYSCOMMAND,0xF012,0);

或则PostMessage(WM_NCLBUTTONDOWN, HTCAPTION, MAKELPARAM(point.x, point.y));  

这跟第一个方法的效果相差不大,发送的消息说明的是发送SC_MOVE + HTCAPTION,所以这里简写成为0xF012。

第二个方法要比第一种更加简单,容易使用。

 采用这种操作后,会导致我们的对话框无法响应WM_LBUTTONUP消息,同时在我编写程序过程中发现WM_NCLBUTTONUP也一般不能响应。当然,可以通过注册WM_MOUSEHOVER以及WM_MOUSELEAVE消息,响应这两个消息去处理相应的代码,或者采用下钩子的方法,在钩子中正确发送消息,不过不推荐这种方法,实现也比较麻烦。

 所以下面提供第二种方法。

    2、在WM_MOUSEMOVE消息中,自己处理窗口移动。具体就是先判断鼠标左键是否处于按下,如果是的话,就获取鼠标移动到的位置,然后将窗口进行移动就可以了。具体实现如下:

[cpp]  view plain  copy
  1. void CMyDlg::OnMouseMove(UINT nFlags, CPoint point)    
  2. {    
  3.     static CPoint PrePoint = CPoint(0, 0);    
  4.     if(MK_LBUTTON == nFlags)    
  5.     {    
  6.          if(point != PrePoint)    
  7.          {    
  8.               CPoint ptTemp = point - PrePoint;    
  9.               CRect rcWindow;    
  10.               GetWindowRect(&rcWindow);    
  11.               rcWindow.OffsetRect(ptTemp.x, ptTemp.y);    
  12.               MoveWindow(&rcWindow);    
  13.               return ;    
  14.          }    
  15.      }    
  16.     PrePoint = point;    
  17.     CDialog::OnMouseMove(nFlags, point);   
  18. }  

    通过上面的消息,就能够进行窗口移动了。然而,如果仅仅响应上面的消息发现两个问题:第一,当鼠标按住左键快速移动的时候,鼠标会移出窗口,窗口不动了,拖动效果不好;第二,左键释放消息在上面这种情况下没有响应。解决方法如下:

    首先左键按下消息中设置鼠标捕获

[cpp]  view plain  copy
  1. SetCapture();  

    在左键释放消息中释放鼠标捕获

[cpp]  view plain  copy
  1. RealeseCapture();  

    通过上面的处理,就可以完美拖动,同时,左键抬起的消息也不会受影响。


    因此,第一种方法较为简洁,适合不需要响应鼠标其它消息的情况;第二种方法相对麻烦一些,不过实现效果也很好,并且不影响其他消息。


http://blog.csdn.net/zjccsg/article/details/51926084

http://blog.csdn.net/aafengyuan/article/details/7565645

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值