Мне нужно поймать событие endresize в WPF.
Как поймать окно изменения размера?
Ответ 1
WPF не предоставляет событие, которое только срабатывает в конце процесса изменения размера. SizeChanged - единственное событие, связанное с изменением размера окна - и оно будет срабатывать несколько раз во время процесса изменения размера.
Общий хак будет заключаться в том, чтобы постоянно устанавливать таймер, когда происходит событие 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
}
Ответ 2
Reactive Extensions для .NET предоставляет некоторые действительно классные возможности для работы со стандартными шаблонами событий, включая возможность дросселирования событий. У меня была аналогичная проблема при работе с измененными размерами событиями, и, хотя решение по-прежнему несколько хакерское, я думаю, что 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
таким образом, что ваш метод Size_Changed (где вы можете выполнить собственный код) не будет выполняться до тех пор, пока не будет выполнено 200 миллисекунд (или сколько бы вы хотели ждать) без другого события SizeChanged
увольнение.
private void Size_Changed(SizeChangedEventArgs e) {
// custom code for dealing with end of size changed here
}
Ответ 3
Вы можете точно определить, когда закончилось изменение размера окна WPF, и вам не нужен таймер. Собственное окно получает сообщение WM_EXITSIZEMOVE
, когда пользователь отпускает левую кнопку мыши в конце изменения размера окна или перемещения. Окно WPF не получает это сообщение, поэтому нам нужно подключить функцию WndProc
, которая ее получит. Мы можем использовать HwndSource
с WindowInteropHelper
, чтобы получить наш дескриптор окна. Затем мы добавим привязку к нашей функции WndProc
. Мы сделаем все это в окне Loaded
event (код vb.net):
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. Кроме того, не делайте этого func ничего, кроме обработки конкретных сообщений, которые вас интересуют.
Теперь 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
Что это.