关于C#:在WPF窗口中进入Windows消息循环会在内部添加白色边框

Hooking into Windows message loop in WPF window adds white border on the inside

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

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

  • 调整大小光标仍然可见,并且
  • 响应于用户动作而显示该窗口并适合其内容。它显示图像,因此尺寸会改变。
  • 因此,我有一个简单的方案可以让我获得99%的解决方案:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    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
  • 此功能可以使我保持航空窗口边框并隐藏调整大小的光标,但是它会在我的窗口内部添加一个?5像素的白色边框。

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

    有什么想法吗?像往常一样先感谢您。


    添加挂钩时,您应该只处理所需的消息,而忽略其他消息。我相信自从调用DefWindowProc以来,您两次处理了某些消息,但从未将处理的参数设置为true。

    因此,在您的情况下,您将使用:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    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覆盖中,如下所示:

    1
    2
    3
    4
    5
    6
    7
    protected override void OnSourceInitialized(EventArgs e) {
        base.OnSourceInitialized(e);

        IntPtr mainWindowPtr = new WindowInteropHelper(this).Handle;
        HwndSource mainWindowSrc = HwndSource.FromHwnd(mainWindowPtr);
        mainWindowSrc.AddHook(WndProc);
    }


    您可以在WPF应用程序中的任何地方尝试

    1
                    ComponentDispatcher.ThreadFilterMessage += new ThreadMessageEventHandler(ComponentDispatcherThreadFilterMessage);

    和:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
        // ******************************************************************
        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;
                    }
                }
            }
        }

    希望有帮助... :-)