MFC中无标题栏窗口的移动

原文链接: http://blog.sina.com.cn/s/blog_6288219501015dwa.html

移动标准窗口是通过用鼠标单击窗口标题条来实现的,但对于没有标题条的窗口,就需要用鼠标单击窗口标题条以外区域来移动窗口。有两种方法可以达到这一目标。

方法一:当窗口确定鼠标位置时,Windows向窗口发送WM_NCHITTEST消息,可以处理该消息,使得只要鼠标在窗口内,Windows便认为鼠标在标题条上。这需要重载CWnd类处理WM_NCHITTEST消息的OnNcHitTest函数,在函数中调用父类的该函数,如果返回HTCLIENT,说明鼠标在窗口客户区内,使重载函数返回HTCAPTION,使Windows误认为鼠标处于标题条上。

下例是使用该方法的实际代码:

UINT CEllipseWndDlg::OnNcHitTest(CPoint point)

{

// 取得鼠标所在的窗口区域

UINT nHitTest = CDialog::OnNcHitTest(point);

// 如果鼠标在窗口客户区,则返回标题条代号给Windows

// 使Windows按鼠标在标题条上类进行处理,即可单击移动窗口

return (nHitTest==HTCLIENT) ? HTCAPTION : nHitTest;

}

方法二:当用户在窗口客户区按下鼠标左键时,使Windows认为鼠标是在标题条上,即在处理WM_LBUTTONDOWN消息的处理函数OnLButtonDown中发送一个wParam参数为HTCAPTION,lParam为当前坐标的WM_NCLBUTTONDOWN消息。

下面是使用该方法的实际代码:

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

}

首先,看看在正常情况下系统是怎样来移动程序窗口的。当使用者在程序窗口标题栏区域(非工作区)内,按下鼠标左键时将会发生下列事情:

◆ 系统向该窗口过程函数发送WM_NCLBUTTONDOWN消息。

◆ WM_NCLBUTTONDOWN消息最终将传送到窗口过程函数中的DefWindowProc()函数中去。

◆ DefWindowProc()函数将根据鼠标左键按下并移动,以及HTCAPTION标识所表示鼠标按下时的位置诸多信息,来执行该消息的缺省动作即窗口随同鼠标光标一起移动的操作。

下面作为练习来测试一下,首先在窗口回调函数(即窗口过程函数)中设置下列语句:

caseWM_NCLBUTTONDOWN

return 0;

然后,同样是在窗口标题栏内按住鼠标左键并移动鼠标,但此时窗口却并不随同鼠标一起移动了,这是怎么回事?这是因为上述语句中设有“return 0”语句的缘故。该语句使得WM_NCLBUTTONDOWN消息未能传递到DefWindowProc()函数,就在窗口过程函数中提前返回了,当然移动窗口的操作就无从进行了。这也从反面印证了一个事实,那就是,最后完成移动窗口的操作将是由DefWindowProc()函数来完成的。

通过上面的分析可以勾划出这样一个操作过程:即用户在窗口标题栏内按下鼠标左键→ 系统发送WM_NCLBUTTONDOWN消息 → DefWindowProc()函数接收消息 → 用户移动鼠标 → DefWindowProc()函数执行窗口随同鼠标一起移动的操作。

由此得出一个结论,那就是要想实现移动窗口的操作,必须具备两个条件:一是要按下鼠标左键并移动(DefWindowProc()函数将检测这个条件);二是在按下鼠标左键时能发送WM_NCLBUTTONDOWN消息并返回HTCAPTION标识。

下面就根据以上的分析,在没有窗口标题栏的情况下,采取骗取DefWindowProc()函数的方式,来实现对无标题栏窗口实体的移动操作。

1、主动发送WM_NCLBUTTONDOWN消息

在窗口没有标题栏的情况下,在窗口实体上按下鼠标左键时,系统是不会发送WM_NCLBUTTONDOWN消息的,这是因为鼠标光标是在窗口的工作区内被按下的,此时系统发送的是WM_LBUTTONDOWN消息。

但通过上述的分析,可以知道DefWindowProc()函数并不关心WM_NCLBUTTONDOWN消息是由谁发出的,而只是关心是否有该消息发出。这样只要我们在按下鼠标左键的事件中,主动将WM_NCLBUTTONDOWN消息发出,岂不就可同时满足这两个条件吗!下面的代码就是根据这个思路来设计的。

caseWM_LBUTTONDOWN:

SendMessage(hWnd,WM_NCLBUTTONDOWN,HTCAPTION,0);

break;

消息发送时,通过HTCAPTION参数给DefWindowProc()函数带去一个信息,告诉它鼠标左键是在窗口非工作区内的标题栏处按下的。当然这是一个假情报,但DefWindowProc()函数会信以为真并根据这个信息来执行相应的操作。

2、主动发送WM_SYSCOMMAND消息

caseWM_LBUTTONDOWN:

SendMessage(hWnd,WM_SYSCOMMAND,SC_DRAGMOVE,0);

break;

能使用WM_SYSCOMMAND消息来移动窗口,得益于一个新近扩展的SC_DRAGMOVE风格标志,该标志从字面上就能看出是"拖曳移动"的意思。该标志在低版本的编译器(VC 6.0中就没有)中是找不到的,所以在引用该标志时应预先声明:

#define        SC_DRAGMOVE  0xF012

也可以直接使用其常量值:

SendMessage(hWnd,WM_SYSCOMMAND,0xF012,0);

上述无标题栏窗口移动窗体的机理与窗口有标题栏时是相似的,相同处最后都是由DefWindowProc()函数来完成实际的操作;不同处是发送消息的方式不同,一个是由系统隐含发送;另一个则是由程序公开发送。

采取上述两种方法来移动窗口,同有标题栏移动窗口的视觉效果是一样的,那就是在移动时,先移动一个指示框(一个虚线框),等确定好窗体移动后的新位置后,当松开鼠标左键时窗口的实体才被正真移动到虚线框所指向的位置处。那么,能否直接移动窗口实体而不出现虚线框呢?答案是肯定的。

实际上,操作系统准备了两种移动窗口的方式,一种是有虚线框,另一种则是没有虚线框,只是Windows系统默认的是有虚线框。不过,如果我们在上述示例代码中再添加下列语句:

SystemParametersInfo(SPI_SETDRAGFULLWINDOWS,true,NULL,0);

即:

caseWM_LBUTTONDOWN:

SystemParametersInfo(SPI_SETDRAGFULLWINDOWS,true,NULL,0);

SendMessage(hWnd,WM_NCLBUTTONDOWN,HTCAPTION,0);

break;

这样在移动窗口时,虚线指示框将不会出现。注意,上述语句的位置顺序不能错,否则,在移动时虚线框还会出现。

不过,系统默认按有虚线指示框的方式来移动窗口是通过权衡利弊的。这是因为有虚线框方式移动窗口,在开始时并未正真地移动窗口,而是用一个线框来指定窗口将要到达的位置,然后一次性的将窗口移动到指定的位置(在指定的位置重新绘制)处。也就是说,在移动窗口的过程中,窗口实体只需要重绘一次即可。如果不用虚线框,而是直接移动窗口实体的话,在窗口的移动过程中,将会形成N次绘制窗口,增加系统处理图形的负担,而使窗口的绘制质量严重下降,造成不好的视觉效果。

为此,在编程实践中可以这样来安排:对异形窗口为了体现异形视觉效果,可以使用无虚线框的方式来移动窗口;而对于一般矩形窗口就可按有虚线框的方式来移动窗口,以求确保窗口的重绘质量。

诚然,上面设置的代码可以使异形窗口无虚线框方式移动,但由于SystemParametersInfo()函数是系统级的,对它的调用将会影响电脑桌面上所有程序窗口都会按无虚线框方式移动,如果这样的话。必将会使桌面整体的视觉效果大打折扣。如果不想影响其它窗口的移动效果,而只是要求在移动本异形窗口时不出现虚线框的话,则可在窗口过程函数中再增添下列代码即可:

case WM_MOUSEMOVE:

SystemParametersInfo(SPI_SETDRAGFULLWINDOWS,false ,NULL,0);

break;

3、自编代码

前两种方法都有缺陷,会改变消息流,使OnLButtonDown,OnLButtonUp等不响应

自编代码同样可以实现无标题栏窗口的移动操作,而且更加灵活多样。例如,操作可以由鼠标左键或鼠标右键来完成。下面是通过鼠标右键来完成窗口移动的实际代码,在程序窗口的过程函数中添加下列代码语句:

static POINT pt, pe;

static RECT rt, re;

case WM_RBUTTONDOWN:

SetCapture(hWnd);                         // 设置鼠标捕获(防止光标跑出窗口失去鼠标热点)

GetCursorPos(&pt);                         // 获取鼠标光标指针当前位置

GetWindowRect(hWnd,&rt);   // 获取窗口位置与大小

re.right=rt.right-rt.left;               // 保存窗口宽度

re.bottom=rt.bottom-rt.top; // 保存窗口高度

break;

case WM_RBUTTONUP:

ReleaseCapture();                           // 释放鼠标捕获,恢复正常状态

break;

case WM_MOUSEMOVE:

GetCursorPos(&pe);                              // 获取光标指针的新位置

if(wParam==MK_RBUTTON)               // 当鼠标右键按下

{

re.left=rt.left+(pe.x - pt.x);    // 窗口新的水平位置

re.top =rt.top+(pe.y - pt.y); // 窗口新的垂直位置

MoveWindow(hWnd,re.left,re.top,re.right,re.bottom,true);// 移动窗口

}

break;

或:

OnLButtonDown() 中:

SetCapture();

CRect rW;

GetWindowRect(rW);

CPoint ptW = point;

ClientToScreen(&ptW);

m_ptCursorOffset.x = ptW.x - rW.left;

OnMouseMove()中:

if ((nFlags & MK_LBUTTON) && this == GetCapture())

{

CPoint ptW = point;

ClientToScreen(&ptW);

ptW.x -= m_ptCursorOffset.x;

ptW.y -= m_ptCursorOffset.y;

::SetWindowPos(m_hWnd, 0,ptW.x,ptW.y,0,0,SWP_NOSIZE);

}

m_ptCursorOffset.y = ptW.y - rW.top;

OnLButtonUp()中:

ReleaseCapture();

时间: 2024-11-08 11:19:33

MFC中无标题栏窗口的移动的相关文章

关于无标题栏窗口拖动的问题

要使无标题栏窗口实现拖动效果,除了处理OnNCHitTest()函数外:就是在OnLButtonDown()函数中发送虚假消息了. 但是,这儿得注意一下,在OnLButtonDown()函数中如果使用SetCapture()函数后再::SendMessage(m_hWnd, WM_NCLBUTTONDOWN, HTCAPTION, 0);是不会成功的!必须去掉或在之后使用. void XWindow::OnLButtonDown(UINT nFlags, CPoint point) { XCon

MFC中的主窗口修改标题

MFC中的主窗口修改标题 如何去掉“无标题”1.在主程序中的InitInstance(): m_pMainWnd->SetWindowText("你要显示的东西如果不想显示置空就行") 2.在框架类的PreCreateWindow函数里修改风格 BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs) { cs.style &= ~FWS_ADDTOTITLE; //去除标题条中的文档名 return CFrameWnd

MFC中通过子窗口关闭父窗口以实现退出程序

这应该是个老问题,我来稍微总结下:(我刚刚接触Windows编程希望大家多多指点) 1.上来来个最鲁莽的. 直接通过exit(0),ExitProcess(0); //不好的用法 猜测资源得不到回收 原因如下: http://blog.csdn.net/jiang1013nan/article/details/4831020 2.向父窗口发送WM_CLOSE消息,然后自己关闭自己的窗口. 获取将父窗口的句柄直接传给子窗口,然后子窗口可以直接关闭父窗口. 示例代码如下: 父窗口类的定义: // C

PyQt4中无边框窗口的移动(拖动)

import sys from PyQt4.QtGui import * from PyQt4.Qt import * from PyQt4.QtCore import * class AboutUsDialog(QDialog): def __init__(self): super(AboutUsDialog,self).__init__() self.setWindowFlags(Qt.FramelessWindowHint|Qt.Dialog) def mousePressEvent(se

在MFC中修改opencv的namedWindow窗口位置

当在MFC中应用opencv的窗口来显示图片.希望namedWindow创建的窗口能根据需要改变风格: //by dongchunxiao cv::namedWindow("windows1",0); //创建一个窗口 HWND hWnd = (HWND)cvGetWindowHandle("windows1");//获取子窗口的HWND HWND hParentWnd = ::GetParent(hWnd);//获取父窗口HWND.父窗口是我们要用的 ::SetW

MFC无标题栏对话框移动的处理方法

很多时候,为了界面的美观,我们会隐藏标题栏,而在客户区通过自绘模拟标题栏.但是这样处理,会导致窗体无法移动.下面提供几种移动方法: 1.采用欺骗的方式.即当鼠标点击客户区的时候,我们欺骗windows,让windows认为我们点击在了标题栏,这样,拖动过程就通过windows进行处理了.因此可以大大减少我们的代码量.具体方法如下: 响应鼠标左键按下消息WM_LBUTTONDOWN,相应函数中,直接发送欺骗消息: PostMessage(WM_NCLBUTTONDOWN, HTCAPTION, M

【MFC】MFC中窗口重绘

MFC中窗口重绘 摘自:http://blog.csdn.net/shuilan0066/article/details/5859057 在刷新窗口时经常要调用重绘函数 MFC提供了三个函数用于窗口重绘 InvalidateRect(&Rect) Invalidate() UpdateWindow() 当需要更新或者重绘窗口时,一般系统会发出两个消息WM_PAINT(通知客户区有变化)和 WM_NCPAINT(通知非客户区有变化)WM_NVPAINT系统会自己搞定WM_PAINT消息对应的函数是

MFC中窗口重绘

搬家于CSDN 2015-05-14 MFC提供了三个函数用于窗口重绘 InvalidateRect(&Rect) Invalidate() UpdateWindow() 当需要更新或者重绘窗口时,一般系统会发出两个消息WM_PAINT(通知客户区有变化)和 WM_NCPAINT    (通知非客户区有变化) WM_NVPAINT    系统会自己搞定 WM_PAINT         消息系统默认对应的函数是OnPaint(),但‍一般在重绘时都在OnDraw,因为在ONPAIN中调用了OND

Android中实现全屏、无标题栏的两种办法

在进行UI设计时,我们经常需要将屏幕设置成无标题栏或者全屏.要实现起来也非常简单,主要有两种方法:配置xml文件和编写代码设置. 1.在xml文件中进行配置 在项目的清单文件AndroidManifest.xml中,找到需要全屏或设置成无标题栏的Activity,在该Activity进行如下配置即可. 实现全屏效果: android:theme="@android:style/Theme.NoTitleBar.Fullscreen" 实现无标题栏(但有系统自带的任务栏): androi