Home > Software engineering >  How to determine when a WPF window resize starts and ends?
How to determine when a WPF window resize starts and ends?

Time:02-05

I have an application in which I show various content that is costly to scale. Right now the application is always shown fullscreen in a borderless window, but we want to transform it into a normal scalable window. What I would like to achieve is to scale the content of the application only once the scaling of the window has been completed, i.e. the user releases the window frame he is currently dragging to scale the window. Until the completion of the scaling, I would like to show a preview or dummy content. However, I do not know how to check, when a scaling starts or ends.

Ideally, I would like to have a behavior like the one from GridSplitter. It has a property called ShowsPreview. If this property is true, you can move the splitter around, but the content does not get updated until you release the splitter. For WPF windows, I have not found either preview events or properties to achieve something similar. All I found was SizeChanged which gets called every single pixel change, when I start scaling my window.

Of course I could build a mechanism like this myself using timers to check, if there is still an ongoing scaling, but I'd prefer a built-in method or approved algorithm.

CodePudding user response:

Unfortunately there does not seem to exist in WPF an equivalent of what would be the ResizeEnd event of WinForms.

A solution that I found satisfactory is to use Reactive Extensions, like this:

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}");
}

This solution fires the Resize_End method every time 300ms elapses after a resize, and no further resize takes place, using Throttle.

You have some similar samples of using of Reactive Extensions here.

CodePudding user response:

Check window messages when window resize or move starts and ends, and then compare the window size to tell whether the window is resized or not. In multiple monitor scenario, you need to include the application manifest which defines Per-Monitor DPI awareness.

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));
    }
}
  •  Tags:  
  • Related