原文:
WPF无边框捕获消息改变窗口大小
文章大部分转载自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 在最大化按钮上。