【问题标题】:Hooking into Windows message loop in WPF window adds white border on the inside在 WPF 窗口中挂钩到 Windows 消息循环会在内部添加白色边框
【发布时间】:2012-02-17 22:36:14
【问题描述】:

我正在尝试使用无法调整大小的WindowStyle="None"(用于自定义按钮且无标题)创建一个 WPF 窗口。将ResizeMode 设置为NoResize 会删除我想保留的航空边框。

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

  1. 调整大小的光标仍然可见,并且
  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

这可以让我保留 aero 窗口边框并隐藏调整大小的光标,但它会在我的窗口内部添加约 5 像素的白色边框。

事实上,即使我在WndPrc 的顶部返回默认的windows 过程结果并且什么都不做,边框仍然在那里。我需要在我的窗口上使用不同的背景颜色,所以这对我不起作用。

有什么想法吗?一如既往地提前感谢。

【问题讨论】:

    标签: c# wpf pinvoke


    【解决方案1】:

    当你添加你的钩子时,你应该只处理你需要的消息,而忽略其他的。我相信您处理某些消息两次,因为您调用了 DefWindowProc,但从未将处理的参数设置为 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);
    }
    

    【讨论】:

    • 天啊...你是对的;我两次调用默认程序。是的。好的,我会在一个小时左右开始工作时尝试一下,谢谢。
    • 这很好,先生。是的,经过多年的 MFC,我不敢相信我犯了这么愚蠢的错误……再次感谢。
    • @Ed - 没问题,重新审视问题总是有帮助的 :-)
    【解决方案2】:

    您可以在 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;
                    }
                }
            }
        }
    

    希望对您有所帮助... :-)

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-10-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多