文章大部分转载自http://blog.csdn.net/fwj380891124,如有问题,请联系删除
?最近一直在学习 WPF,看着别人做的WPF程序那么漂亮,眼红啊~ 很多漂亮的程序都是无边框的。于是无边框窗口操作就是最重要的了。无边框窗口的操作一直以来相关的资料就很少。WPF 下的就更少了,有的大多是无边框窗体的移动。在得到群里高人的指点,再查了一些资料之后,终于把问题解决了。
? ? ? 废话不多说,直接来看看如何实现吧!其实现原理很简单:拦截并处理 Windows 消息:WM_NCHITTEST。
? ? ? WPF 处理 Windows 消息的模式和 WinForm 不一样了。Window 类里没有 WndProc 函数了,想要截取 Windows 消息必须借助 HwndSource 添加 Hook。
-
protected?override?void?OnSourceInitialized(EventArgs?e)??
-
{??
-
????base.OnSourceInitialized(e);??
-
????HwndSource?hwndSource?=?PresentationSource.FromVisual(this)?as?HwndSource;??
-
????if?(hwndSource?!=?null)??
-
????{??
-
????????hwndSource.AddHook(new?HwndSourceHook(this.WndProc));??
-
????}??
-
}??
-
??
-
protected?virtual?IntPtr?WndProc(IntPtr?hwnd,?int?msg,?IntPtr?wParam,?IntPtr?lParam,?ref?bool?handled)??
-
{??
-
????return?IntPtr.Zero;??
-
}??
? ? OK,WndProc 注册完成之后就可以通过 WndProc 函数完成对Windows消息的处理了。可以发现,这里的 WndProc 和标准的 Win32 消息循环很像,只是多了一个 ref bool handled 参数,对于该参数MSDN是这样说明的:指示该消息是否已处理的值。如果该消息已处理,请将值设置为?true;否则请将其设置为?false。?在下面我们将会使用到这个参数数。
-
private?const?int?WM_NCHITTEST?=?0x0084;??
-
private?readonly?int?agWidth?=?12;???
-
private?readonly?int?bThickness?=?4;???
-
private?Point?mousePoint?=?new?Point();???
-
???
-
protected?virtual?IntPtr?WndProc(IntPtr?hwnd,?int?msg,?IntPtr?wParam,?IntPtr?lParam,?ref?bool?handled)??
-
{??
-
????switch?(msg)??
-
????{??
-
????????case?WM_NCHITTEST:??
-
????????this.mousePoint.X?=?(lParam.ToInt32()?&0xFFFF);??
-
????????this.mousePoint.Y?=?(lParam.ToInt32()?>>?16);??
-
??
-
????????测试鼠标位置#region?测试鼠标位置??
-
??
-
???????????
-
?????????if?(this.mousePoint.Y?-?this.Top?<=?this.agWidth???
-
????????????&&?this.mousePoint.X?-?this.Left?<=?this.agWidth)???
-
????????{??
-
????????????handled?=?true;??
-
????????????return?new?IntPtr((int)HitTest.HTTOPLEFT);??
-
????????}??
-
??????????
-
?????????else?if?(this.ActualHeight?+?this.Top?-?this.mousePoint.Y?<=?this.agWidth???
-
????????????&&?this.mousePoint.X?-?this.Left?<=?this.agWidth)??
-
????????{??
-
????????????handled?=?true;??
-
????????????return?new?IntPtr((int)HitTest.HTBOTTOMLEFT);??
-
????????}??
-
??????????
-
?????????else?if?(this.mousePoint.Y?-?this.Top?<=?this.agWidth???
-
????????????&&?this.ActualWidth?+?this.Left?-?this.mousePoint.X?<=?this.agWidth)??
-
????????{??
-
????????????handled?=?true;??
-
????????????return?new?IntPtr((int)HitTest.HTTOPRIGHT);??
-
????????}??
-
??????????
-
?????????else?if?(this.ActualWidth?+?this.Left?-?this.mousePoint.X?<=?this.agWidth???
-
????????????&&?this.ActualHeight?+?this.Top?-?this.mousePoint.Y?<=?this.agWidth)??
-
????????{??
-
????????????handled?=?true;??
-
????????????return?new?IntPtr((int)HitTest.HTBOTTOMRIGHT);??
-
????????}??
-
??????????
-
?????????else?if?(this.mousePoint.X?-?this.Left?<=?this.bThickness)??
-
????????{??
-
????????????handled?=?true;??
-
????????????return?new?IntPtr((int)HitTest.HTLEFT);??
-
????????}??
-
??????????
-
?????????else?if?(this.ActualWidth?+?this.Left?-?this.mousePoint.X?<=?this.bThickness)??
-
????????{??
-
????????????handled?=?true;??
-
????????????return?new?IntPtr((int)HitTest.HTRIGHT);??
-
????????}??
-
??????????
-
?????????else?if?(this.mousePoint.Y?-?this.Top?<=?this.bThickness)??
-
????????{??
-
????????????handled?=?true;??
-
????????????return?new?IntPtr((int)HitTest.HTTOP);??
-
????????}??
-
??????????
-
?????????else?if?(this.ActualHeight?+?this.Top?-?this.mousePoint.Y?<=?this.bThickness)??
-
????????{??
-
????????????handled?=?true;??
-
????????????return?new?IntPtr((int)HitTest.HTBOTTOM);??
-
????????}??
-
????????else???
-
????????{??
-
????????????handled?=?true;??
-
????????????return?new?IntPtr((int)HitTest.HTCAPTION);??
-
????????}??
-
????????#endregion??
-
????}??
-
????return?IntPtr.Zero;??
-
}??
? ? ?从上面的代码可以看出,工作原理很简单:截取 WM_NCHITTEST 消息,获得鼠标坐标,再在你希望的地方返回不同的消息以模拟鼠标的状态即可。需要注意的是,返回消息之前必须将handled 设为 true。告诉系统你已经处理过该消息,不然无效果。
??? 关于 HitTest 是自定义的枚举类,里面包含了鼠标的各种消息。
-
1public?enum?HitTest:int??
-
{??
-
????HTERROR?=?-2,??
-
????HTTRANSPARENT?=?-1,??
-
????HTNOWHERE?=?0,??
-
????HTCLIENT?=?1,??
-
????HTCAPTION?=?2,??
-
????HTSYSMENU?=?3,??
-
????HTGROWBOX?=?4,??
-
????HTSIZE?=?HTGROWBOX,??
-
????HTMENU?=?5,??
-
????HTHSCROLL?=?6,??
-
????HTVSCROLL?=?7,??
-
????HTMINBUTTON?=?8,??
-
????HTMAXBUTTON?=?9,??
-
????HTLEFT?=?10,??
-
????HTRIGHT?=?11,??
-
????HTTOP?=?12,??
-
????HTTOPLEFT?=?13,??
-
????HTTOPRIGHT?=?14,??
-
????HTBOTTOM?=?15,??
-
????HTBOTTOMLEFT?=?16,??
-
????HTBOTTOMRIGHT?=?17,??
-
????HTBORDER?=?18,??
-
????HTREDUCE?=?HTMINBUTTON,??
-
????HTZOOM?=?HTMAXBUTTON,??
-
????HTSIZEFIRST?=?HTLEFT,??
-
????HTSIZELAST?=?HTBOTTOMRIGHT,??
-
????HTOBJECT?=?19,??
-
????HTCLOSE?=?20,??
-
????HTHELP?=?21,??
-
} ?
-
?HTBORDER 在不具有可变大小边框的窗口的边框上。
· HTBOTTOM 在窗口的水平边框的底部。
· HTBOTTOMLEFT 在窗口边框的左下角。
· HTBOTTOMRIGHT 在窗口边框的右下角。
· HTCAPTION 在标题条中。
· HTCLIENT 在客户区中。
· HTERROR 在屏幕背景或窗口之间的分隔线上(与HTNOWHERE相同,除了Windows的DefWndProc函数产生一个系统响声以指明错误)。
· HTGROWBOX 在尺寸框中。
· HTHSCROLL 在水平滚动条上。
· HTLEFT 在窗口的左边框上。
· HTMAXBUTTON 在最大化按钮上。
· HTMENU 在菜单区域。
· HTMINBUTTON 在最小化按钮上。
· HTNOWHERE 在屏幕背景或窗口之间的分隔线上。
· HTREDUCE 在最小化按钮上。
· HTRIGHT 在窗口的右边框上。
· HTSIZE 在尺寸框中。(与HTGROWBOX相同)
· HTSYSMENU 在控制菜单或子窗口的关闭按钮上。
· HTTOP 在窗口水平边框的上方。
· HTTOPLEFT 在窗口边框的左上角。
· HTTOPRIGHT 在窗口边框的右上角。
· HTTRANSPARENT 在一个被其它窗口覆盖的窗口中。
· HTVSCROLL 在垂直滚动条中。
· HTZOOM 在最大化按钮上。