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

Ed *_* S. 16 c# wpf pinvoke

我正在尝试创建一个WPF窗口WindowStyle="None"(用于自定义按钮,没有标题),无法调整大小.设置ResizeModeNoResize删除我想要保留的空气边界.

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

  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 );
    }
}
Run Code Online (Sandbox Code Playgroud)

基本上;

  1. 覆盖窗口过程.
  2. 调用默认窗口过程.
  3. 如果是消息WM_NCHITTEST,请检查边框结果.
  4. 如果是边框,请返回常规HTBORDER.

这可以让我保持aero窗口边框并隐藏调整大小的光标,但它在我的窗口内部添加了一个~5像素的白色边框.

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

有任何想法吗?一如既往地感谢您.

Cod*_*ked 10

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

另外,我可能会在OnSourceInitialized覆盖中添加钩子,如下所示:

protected override void OnSourceInitialized(EventArgs e) {
    base.OnSourceInitialized(e);

    IntPtr mainWindowPtr = new WindowInteropHelper(this).Handle;
    HwndSource mainWindowSrc = HwndSource.FromHwnd(mainWindowPtr);
    mainWindowSrc.AddHook(WndProc);
}
Run Code Online (Sandbox Code Playgroud)