如何确定 WPF window 调整大小何时开始和结束?

How to determine when a WPF window resize starts and ends?

我有一个应用程序,我在其中显示了各种扩展成本很高的内容。现在,该应用程序始终以无边框 window 全屏显示,但我们希望将其转换为正常的可缩放 window。我想要实现的是仅在 window 的缩放完成后才缩放应用程序的内容,即用户释放他当前拖动的 window 框架以缩放 window.在缩放完成之前,我想显示预览或虚拟内容。但是,我不知道如何检查缩放何时开始或结束。

理想情况下,当我开始缩放我的 window.

当然,我可以自己构建一个这样的机制,使用计时器来检查是否仍在进行缩放,但我更喜欢内置方法或批准的算法。

不幸的是,在 WPF 中似乎不存在与 WinForms 的 ResizeEnd 事件等效的东西。

我觉得满意的解决方案是使用 Reactive Extensions,像这样:

public MainWindow()
{
    InitializeComponent();

    Observable.FromEventPattern<SizeChangedEventArgs>(this, nameof(SizeChanged))
        .Throttle(TimeSpan.FromMilliseconds(300))
        .ObserveOnDispatcher()
        .Subscribe(e => Resize_End(e.EventArgs));
}

private void Resize_End(SizeChangedEventArgs e)
{
    Console.WriteLine($"{e.PreviousSize} / {e.NewSize}");
}

此解决方案在调整大小后每过 300 毫秒就会触发 Resize_End 方法,并且不会进一步调整大小,使用 Throttle.

您有一些类似的反应性扩展使用示例here

在window调整大小或移动开始和结束时检查window消息,然后比较window大小以判断window是否调整大小。在多显示器场景中,您需要包含定义 Per-Monitor DPI 感知的应用程序清单。

using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Interop;

public partial class MainWindow : Window
{
    protected override void OnSourceInitialized(EventArgs e)
    {
        base.OnSourceInitialized(e);

        var source = PresentationSource.FromVisual(this) as HwndSource;
        source?.AddHook(WndProc);
    }

    private const int WM_ENTERSIZEMOVE = 0x0231;
    private const int WM_EXITSIZEMOVE = 0x0232;

    private Rect _windowRect;

    private IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
    {
        switch (msg)
        {
            case WM_ENTERSIZEMOVE:
                _windowRect = GetWindowRect(this);
                break;

            case WM_EXITSIZEMOVE:
                if (_windowRect.Size != GetWindowRect(this).Size)
                {
                    Debug.WriteLine("RESIZED");
                }
                break;
        }
        return IntPtr.Zero;
    }

    private static Rect GetWindowRect(Window window)
    {
        IntPtr handle = new WindowInteropHelper(window).Handle;
        return GetWindowRect(handle, out RECT rect) ? rect : default;
    }

    [DllImport("User32.dll")]
    [return: MarshalAs(UnmanagedType.Bool)]
    private static extern bool GetWindowRect(
        IntPtr hWnd,
        out RECT lpRect);

    [StructLayout(LayoutKind.Sequential)]
    private struct RECT
    {
        public int left;
        public int top;
        public int right;
        public int bottom;

        public static implicit operator Rect(RECT rect) =>
            new Rect(rect.left, rect.top, (rect.right - rect.left), (rect.bottom - rect.top));
    }
}