我需要在WPF中捕获endresize事件。
在 WPF 中没有只在调整大小过程结束时触发的事件。 SizeChanged 是与窗口调整大小相关的唯一事件,并且在调整大小过程中会多次触发。
一个完全的 hack(技巧)是在 SizeChanged 事件触发时不断设置一个计时器。然后,直到调整大小结束时计时器才有机会运行,并在此时执行您的一次性处理。
public MyUserControl()
{
_resizeTimer.Tick += _resizeTimer_Tick;
}
DispatcherTimer _resizeTimer = new DispatcherTimer { Interval = new TimeSpan(0, 0, 0, 0, 1500), IsEnabled = false };
private void UserControl_SizeChanged(object sender, SizeChangedEventArgs e)
{
_resizeTimer.IsEnabled = true;
_resizeTimer.Stop();
_resizeTimer.Start();
}
void _resizeTimer_Tick(object sender, EventArgs e)
{
_resizeTimer.IsEnabled = false;
//Do end of resize processing
}
Reactive Extensions for .NET提供了一些非常酷的功能来处理标准事件模式,包括能够节流事件。我在处理大小改变事件时遇到了类似的问题,虽然解决方案仍然有些“hacky”,但我认为Reactive Extensions提供了一种更优雅的实现方式。这是我的实现:
IObservable<SizeChangedEventArgs> ObservableSizeChanges = Observable
.FromEventPattern<SizeChangedEventArgs>(this, "SizeChanged")
.Select(x => x.EventArgs)
.Throttle(TimeSpan.FromMilliseconds(200));
IDisposable SizeChangedSubscription = ObservableSizeChanges
.ObserveOn(SynchronizationContext.Current)
.Subscribe(x => {
Size_Changed(x);
});
这将有效地限制SizeChanged
事件,使得直到200毫秒(或者您想要等待的时间)没有其他SizeChanged
事件被触发,您的Size_Changed
方法(在其中可以执行自定义代码)才会被执行。
private void Size_Changed(SizeChangedEventArgs e) {
// custom code for dealing with end of size changed here
}
可以使用@Bohoo提供的非常干净的解决方案,无需计时器,我只是将他的vb.net代码改成了c#代码
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.Loaded += MainWindow_Loaded;
}
private void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
// this two line have to be exactly onload
HwndSource source = HwndSource.FromHwnd(new WindowInteropHelper(this).Handle);
source.AddHook(new HwndSourceHook(WndProc));
}
const int WM_SIZING = 0x214;
const int WM_EXITSIZEMOVE = 0x232;
private static bool WindowWasResized = false;
private static IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{
if (msg == WM_SIZING)
{
if (WindowWasResized == false)
{
// 'indicate the the user is resizing and not moving the window
WindowWasResized = true;
}
}
if (msg == WM_EXITSIZEMOVE)
{
// 'check that this is the end of resize and not move operation
if (WindowWasResized == true)
{
// your stuff to do
Console.WriteLine("End");
// 'set it back to false for the next resize/move
WindowWasResized = false;
}
}
return IntPtr.Zero;
}
}
WM_EXITSIZEMOVE
消息。WPF 窗口无法接收此消息,因此我们需要连接一个 WndProc
函数来接收它。我们可以使用 HwndSource
与 WindowInteropHelper
来获取我们的窗口句柄。然后我们将在窗口的Loaded
事件(vb.net代码)中添加钩子到我们的 WndProc
函数中。请注意,您需要保留 HTML 标签。Dim WinSource As HwndSource
Private Sub WindowLoaded_(sender As Object, e As RoutedEventArgs)
WinSource = HwndSource.FromHwnd(New WindowInteropHelper(Me).Handle)
WinSource.AddHook(New HwndSourceHook(AddressOf WndProc))
End Sub
WndProc
中,我们将监听 WM_EXITSIZEMOVE
消息:Const WM_EXITSIZEMOVE As Integer = &H232
Private Function WndProc(hwnd As IntPtr, msg As Integer, wParam As IntPtr, lParam As IntPtr, ByRef handled As Boolean) As IntPtr
If msg = WM_EXITSIZEMOVE Then
DoWhatYouNeed()
End If
Return IntPtr.Zero
End Function
请注意,该函数应返回IntPtr.Zero。此外,在此函数中除了处理您感兴趣的特定消息之外,不要做任何其他事情。
现在,WM_EXITSIZEMOVE
也会在移动操作结束时发送,而我们只对调整大小感兴趣。有几种方法可以确定这是调整大小操作的结束。我通过监听WM_SIZING
消息(在调整大小期间多次发送)并结合标志来完成。整个解决方案如下:
(注意:不要被此处的代码高亮所困扰,因为对于vb.net来说是错误的)
Dim WinSource As HwndSource
Const WM_SIZING As Integer = &H214
Const WM_EXITSIZEMOVE As Integer = &H232
Dim WindowWasResized As Boolean = False
Private Sub WindowLoaded_(sender As Object, e As RoutedEventArgs)
WinSource = HwndSource.FromHwnd(New WindowInteropHelper(Me).Handle)
WinSource.AddHook(New HwndSourceHook(AddressOf WndProc))
End Sub
Private Function WndProc(hwnd As IntPtr, msg As Integer, wParam As IntPtr, lParam As IntPtr, ByRef handled As Boolean) As IntPtr
If msg = WM_SIZING Then
If WindowWasResized = False Then
'indicate the the user is resizing and not moving the window
WindowWasResized = True
End If
End If
If msg = WM_EXITSIZEMOVE Then
'check that this is the end of resize and not move operation
If WindowWasResized = True Then
DoWhatYouNeed()
'set it back to false for the next resize/move
WindowWasResized = False
End If
End If
Return IntPtr.Zero
End Function
就是这样。
WM_ENTERSIZEMOVE
而不是 WM_SIZING
。 - yumetodo对于使用 Rx (System.Reactive) 的 UWP
//Stop window updates
rootFrame = new Frame
{
HorizontalAlignment = HorizontalAlignment.Left,
VerticalAlignment = VerticalAlignment.Top,
Width = Window.Current.Bounds.Width,
Height = Window.Current.Bounds.Height
};
//updates after throttling
var sizeChangedObservable = Observable.FromEventPattern<WindowSizeChangedEventHandler, WindowSizeChangedEventArgs>(
handler => Window.Current.SizeChanged += handler,
handler => Window.Current.SizeChanged -= handler);
sizeChangedObservable.Throttle(TimeSpan.FromSeconds(0.35)).ObserveOnDispatcher(CoreDispatcherPriority.Normal).Subscribe(x =>
{
rootFrame.Width = x.EventArgs.Size.Width;
rootFrame.Height = x.EventArgs.Size.Height;
});