在WPF窗口中挂钩进入Windows消息循环,在内部添加白色边框

我试图创建一个WPF窗口,其中WindowStyle="None" (对于自定义按钮,没有标题)无法resize。 将ResizeMode设置为NoResize将删除我想要保留的aero边框。

我可以设置最小/最大尺寸属性并完成它,除了:

  1. resize的游标仍然可见,并且
  2. 显示窗口以响应用户操作并适合其内容。 它显示图像,因此大小会发生变化。

所以,我有一个简单的方案,让我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 ); } } 

基本上;

  1. 覆盖窗口过程。
  2. 调用默认窗口过程。
  3. 如果消息是WM_NCHITTEST ,请检查边框结果。
  4. 如果是边框,则返回常规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; } } } } 

希望能帮助到你… :-)