在WPF窗口中挂钩进入Windows消息循环,在内部添加白色边框
我试图创建一个WPF窗口,其中WindowStyle="None"
(对于自定义按钮,没有标题)无法resize。 将ResizeMode
设置为NoResize
将删除我想要保留的aero边框。
我可以设置最小/最大尺寸属性并完成它,除了:
- resize的游标仍然可见,并且
- 显示窗口以响应用户操作并适合其内容。 它显示图像,因此大小会发生变化。
所以,我有一个简单的方案,让我99%的方式:
public class BorderedWindowNoResize : Window { [DllImport( "DwmApi.dll" )] public static extern int DwmExtendFrameIntoClientArea( IntPtr hwnd, ref MARGINS pMarInset ); [DllImport( "user32.dll", CharSet = CharSet.Auto )] public static extern IntPtr DefWindowProc( IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam ); public BorderedWindowNoResize() { Loaded += BorderedWindowNoResize_Loaded; } private void BorderedWindowNoResize_Loaded( object sender, RoutedEventArgs e ) { IntPtr mainWindowPtr = new WindowInteropHelper( this ).Handle; HwndSource mainWindowSrc = HwndSource.FromHwnd( mainWindowPtr ); mainWindowSrc.AddHook( WndProc ); } private IntPtr WndProc( IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled ) { var htLocation = DefWindowProc( hwnd, msg, wParam, lParam ).ToInt32(); if( msg == (uint)WM.NCHITTEST ) { handled = true; switch( htLocation ) { case (int)HitTestResult.HTBOTTOM: case (int)HitTestResult.HTBOTTOMLEFT: case (int)HitTestResult.HTBOTTOMRIGHT: case (int)HitTestResult.HTLEFT: case (int)HitTestResult.HTRIGHT: case (int)HitTestResult.HTTOP: case (int)HitTestResult.HTTOPLEFT: case (int)HitTestResult.HTTOPRIGHT: htLocation = (int)HitTestResult.HTBORDER; break; } } return new IntPtr( htLocation ); } }
基本上;
- 覆盖窗口过程。
- 调用默认窗口过程。
- 如果消息是
WM_NCHITTEST
,请检查边框结果。 - 如果是边框,则返回常规
HTBORDER
。
这可以让我保持空气窗口边框并隐藏resize的光标,但它在我的窗口内部添加了一个~5像素的白色边框。
事实上,即使我在WndPrc
顶部返回默认的Windows程序结果,也没有做任何其他事情,边框仍然存在。 我的窗口需要不同的背景颜色,所以这对我不起作用。
有任何想法吗? 一如既往地感谢您。
添加挂钩时,您应该只处理所需的消息,而忽略其他消息。 我相信你正在处理某些消息两次,因为你调用了DefWindowProc,但从未将handle参数设置为true。
所以在你的情况下,你会使用:
private IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled) { if (msg == (uint)WM.NCHITTEST) { handled = true; var htLocation = DefWindowProc(hwnd, msg, wParam, lParam).ToInt32(); switch (htLocation) { case (int)HitTestResult.HTBOTTOM: case (int)HitTestResult.HTBOTTOMLEFT: case (int)HitTestResult.HTBOTTOMRIGHT: case (int)HitTestResult.HTLEFT: case (int)HitTestResult.HTRIGHT: case (int)HitTestResult.HTTOP: case (int)HitTestResult.HTTOPLEFT: case (int)HitTestResult.HTTOPRIGHT: htLocation = (int)HitTestResult.HTBORDER; break; } return new IntPtr(htLocation); } return IntPtr.Zero; }
另外,我可能会在OnSourceInitialized覆盖中添加钩子,如下所示:
protected override void OnSourceInitialized(EventArgs e) { base.OnSourceInitialized(e); IntPtr mainWindowPtr = new WindowInteropHelper(this).Handle; HwndSource mainWindowSrc = HwndSource.FromHwnd(mainWindowPtr); mainWindowSrc.AddHook(WndProc); }
您可以在WPF应用程序的任何位置尝试
ComponentDispatcher.ThreadFilterMessage += new ThreadMessageEventHandler(ComponentDispatcherThreadFilterMessage);
和:
// ****************************************************************** private static void ComponentDispatcherThreadFilterMessage(ref MSG msg, ref bool handled) { if (!handled) { if (msg.message == WmHotKey) { HotKey hotKey; if (_dictHotKeyToCalBackProc.TryGetValue((int)msg.wParam, out hotKey)) { if (hotKey.Action != null) { hotKey.Action.Invoke(hotKey); } handled = true; } } } }
希望能帮助到你… :-)