拖动无标题窗口
MFC点击Dialog窗体可拖动的实现
MFC创建一个可通过鼠标点击客户区拖动窗口位置的方法
通过添加消息响应函数
afx_msg UINT OnNcHitTest(CPoint point);
添加宏
ON_WM_NCHITTEST()
然后, 定义OnNcHitTest的行为即可, OnNcHitTest的代码如下:
{
CRect rect;
GetClientRect(rect);
ClientToScreen(rect);
if(rect.PtInRect(point))
return HTCAPTION;
return CDialog::OnNcHitTest(point);
}
首先, 发生WM_NCHITTEST消息的时候, 程序先取得客户区的窗口矩形, 这里用到GetClientRect方法得到, 由于GetClientRect取得的是相对于窗口坐标系统, 但OnNcHitTest的参数point却是相对于屏幕的坐标系统, 所以这里用到了ClientToScreen把取到的客户区矩形转成相对于屏幕的坐标, 然后用CRect类的PtInRect方法判断point是否落在这个矩形里面, 如果是, 则返回HTCAPTION(就相当于这次点击是点击标题栏), 所以, 就可以这样来拖动窗口了. 如果, OnNcHitTest直接返回HTCAPTION的话, 那么, 但用户点击非客户区的时候, 有些功能会失效, 比如点击系统的关闭按钮, 试一下就会发现这个按钮无效了; ok, 如果点击的是非客户端, 者直接返回CDialog::OnNcHitTest(point), 这就是按默认处理了.
以上的这个方法比较麻烦, 其实可以更简单一点, 只要先调用父类的OnNcHitTest(point), 取得返回值, 在判断是不是HTCLIENT, 如果是, 则返回HTCAPTION, 否则直接返回父类OnNcHitTest(point)的返回值就ok了, 修改后如下:
{
UINT uRet = CDialog::OnNcHitTest(point);
if(HTCLIENT == uRet)
return HTCAPTION;
return uRet;
}
再简化一下, 就成这样子:
{
UINT uRet = CDialog::OnNcHitTest(point);
return (HTCLIENT == uRet) ? HTCAPTION : uRet;
}
MFC:拖动无标题窗口的三种方法
Greless 2017-02-20 18:52:08 2832 收藏 2
版权
有的时候,我们需要自绘窗口的标题,或者隐藏了窗口标题,那么问题来了,这就是怎么拖动无标题的窗口?
这个问题的本质便是怎么在窗口的客户区上,拖动鼠标,并且移动窗口。
通常状态下,一个窗口只有在标题栏的区域,才会响应鼠标拖动的消息。而在窗口的客户区并不会响应。
这样便有了一个方法,就是我们可以欺骗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
void CMyDlg::OnMouseMove(UINT nFlags, CPoint point)
{
static CPoint PrePoint = CPoint(0, 0);
if(MK_LBUTTON == nFlags)
{
if(point != PrePoint)
{
CPoint ptTemp = point - PrePoint;
CRect rcWindow;
GetWindowRect(&rcWindow);
rcWindow.OffsetRect(ptTemp.x, ptTemp.y);
MoveWindow(&rcWindow);
return ;
}
}
PrePoint = point;
CDialog::OnMouseMove(nFlags, point);
}
通过上面的消息,就能够进行窗口移动了。然而,如果仅仅响应上面的消息发现两个问题:第一,当鼠标按住左键快速移动的时候,鼠标会移出窗口,窗口不动了,拖动效果不好;第二,左键释放消息在上面这种情况下没有响应。解决方法如下:
首先左键按下消息中设置鼠标捕获
[cpp] view plain copy
SetCapture();
在左键释放消息中释放鼠标捕获
[cpp] view plain copy
RealeseCapture();
通过上面的处理,就可以完美拖动,同时,左键抬起的消息也不会受影响。
因此,第一种方法较为简洁,适合不需要响应鼠标其它消息的情况;第二种方法相对麻烦一些,不过实现效果也很好,并且不影响其他消息。
http://blog.csdn.net/zjccsg/article/details/51926084
http://blog.csdn.net/aafengyuan/article/details/7565645
————————————————
版权声明:本文为CSDN博主「Greless」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/greless/article/details/56017766
MFC——无边框窗口的拖动
void CXXXXDialog::OnLButtonDown(UINT nFlags, CPoint point) { PostMessage(WM_NCLBUTTONDOWN, HTCAPTION, MAKELPARAM(point.x, point.y)); }
UINT CEllipseWndDlg::OnNcHitTest(CPoint point) { // 取得鼠标所在的窗口区域 UINT nHitTest = CDialog::OnNcHitTest(point); // 如果鼠标在窗口客户区,则返回标题条代号给Windows // 使Windows按鼠标在标题条上类进行处理,即可单击移动窗口 return (nHitTest==HTCLIENT) ? HTCAPTION : nHitTest; }
void CEllipseWndDlg::OnLButtonDown(UINT nFlags, CPoint point) { // 调用父类处理函数完成基本操作 CDialog::OnLButtonDown(nFlags, point); // 发送WM_NCLBUTTONDOWN消息 // 使Windows认为鼠标在标题条上 PostMessage(WM_NCLBUTTONDOWN, HTCAPTION, MAKELPARAM(point.x, point.y)); //或SendMessage(WM_SYSCOMMAND,0xF012,0); //0xF012 = SC_MOVE | HTCAPTION
}