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

时间:2011-06-16 00:06:07

标签: c# wpf pinvoke

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

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

  1. 调整大小的游标仍然可见,
  2. 显示窗口以响应用户操作并适合其内容。它显示图像,因此尺寸会发生变化。
  3. 所以,我有一个简单的方案,让我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
    5. 这可以让我保持aero窗口边框并隐藏调整大小的光标,但它会在我的窗口内部添加一个~5像素的白色边框。

      实际上,即使我在WndPrc顶部返回默认的Windows程序结果,也没有做任何其他操作,边框仍然。我的窗口需要不同的背景颜色,所以这对我不起作用。

      有什么想法吗?提前谢谢。

2 个答案:

答案 0 :(得分:10)

添加挂钩时,您应该只处理所需的消息,而忽略其他消息。我相信您正在处理某些消息两次,因为您调用了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);
}

答案 1 :(得分:3)

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

希望它有所帮助...... :-)