如何确定 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));
}
}
我有一个应用程序,我在其中显示了各种扩展成本很高的内容。现在,该应用程序始终以无边框 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));
}
}