窗口移动无边框各种方式
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
第一步:
//先定义一个坐标点对象 mypoint
private Point mypoint;
第二步:在窗体的点击事件中写
private void Form1_MouseDown(object sender, MouseEventArgs e) {
//鼠标在窗体内按下时,自动记录鼠标的 x y 值,并将它们改为负数
mypoint = new Point(-e.X,-e.Y);
}
第三步:在窗体的鼠标移动事件中写
private void Form1_MouseMove(object sender, MouseEventArgs e)
{
/*
鼠标在窗体内移动时,首先判断e.Button按下的是哪个鼠标按钮再判断
* 是否等于左键按下,MouseButtons.Left这句的意思是鼠标左
键按下状态
*/
if (e.Button == MouseButtons.Left)
{
/*新建一个坐标点对象,它的坐标等于
(Control.MousePosition;)
* 桌面上坐标的位置
*/
Point myposition = Control.MousePosition;
/*
myposition.offset中的Offset是坐标平移的意思,现在将在窗体
* 内点击左键时产生的负数加进来,也就等于现在鼠标在桌面上的坐标
* 减去鼠标在窗体内的坐标位置,就等于现在窗体的位置
*/
myposition.Offset(mypoint.X, mypoint.Y);
/*
this.DesktopLocation这句的意思是获取或设置窗体在桌
面上的位置
* 它的位置来自myposition
*/
this.DesktopLocation = myposition;
}
其实这是个让人说过无数次的内容,但是最近在写一个测试小程序的时候发现了一个问题,今天没什么事做,就做个小的总结。
通过拖动窗体的客户区来移动一个窗体并不是很新鲜的内容,很多的程序都用到了这一点,尤其是一些可以换肤的程序。
这篇文章并不打算详细论述如何在C#下实现这一功能,因为它的代码实在是简单得不能再简单。这里简单说一下实现的原理:
首先说一个概念——窗体的客户区,窗体的客户区指的是一个窗体除了标题栏和边框以外的部分。
当我们的鼠标在窗体中移动的时候,会触发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 posted to the window that has captured the mouse. (当光标移动或一个鼠标键被按下或释放时,WM_NCHITTEST消息会被发送到一个窗口中,如果光标没有被捕获,这个消息被送到光标下的窗口。否则这个消息被送到捕获了光标的窗口。)
这个消息被默认的(请注意是“默认的”)窗口过程(窗口过程这个概念后面再说)处理后,会根据触发这个消息时鼠标的位置返回一个值,例如当鼠标在窗口的标题栏上时,返回HTCAPTION;当鼠标在一个窗口的客户区中时,返回HTCLIENT;如果鼠标指向某个窗口的字窗口的“关闭”按钮或系统菜单(就是点击窗口图标后出现的那个菜单),就返回HTSYSMENU。
所以我们要做的就是骗!我们要欺骗Windows,当我们的鼠标在窗体的客户区中移动时,默认的窗口过程处理后会返回 HTCLIENT,Windows系统根据这个值进行相应的操作,把适当的消息插入到应用程序的消息队列(这个概念同样在后面讨论)中。这时如果我们做一些改变,人为地修改窗口过程的返回值,把HTCLIENT修改为HTCAPTION并返回给系统,系统就会认为鼠标这时在窗体的标题栏中,而拖动标题栏可以移动一个窗体,所以当我们在一个被这样修改后的应用程序的客户区按下鼠标并拖动时,Windows会认我们在拖动一个窗体的标题栏,于是它把一个移动窗体的消息插入到程序的消息队列中,再经过窗口过程的处理,就实现了我们需要的功能——拖动窗体的客户区移动窗体。
于是就有了下面的代码:
protected override WndProc(ref message m)
{
switch (m.Msg)
{
case WM_NCHITTEST: //如果鼠标移动或单击
base.WndProc(ref m);//调用基类的窗口过程——WndProc方法处理这个消息
if (m.Result == (IntPtr)HTCLIENT)//如果返回的是HTCLIENT
{
m.Result = (IntPtr)HTCAPTION;//把它改为HTCAPTION
return;//直接返回退出方法
}
break;
}
base.WndProc(ref m);//如果不是鼠标移动或单击消息就调用基类的窗口过程进行处理
}
这也是MSDN上的一个例子,重写窗体的WndProc方法,判断消息然后返回。
我本来也是想这样就完事了,可是后来测试的时候却发现有个大问题:双击窗体的时候,窗体会最大化。
那天脑子有点迷糊,也没多想就到网上问了一下,一个高手提醒我说可能是那个重写的窗口过程的问题,于是一下子明白了。我们向Windows传递了假信息,Windows我们在窗体的客户区双击鼠标在Windows看来是双击了窗体的标题栏,理所当然的Windows会告诉窗口过程窗体需要最大化,窗口过程又理所当然的照做了,于是就出现了这样的现象。