Подтвердить что ты не робот

Как поймать окно изменения размера?

Мне нужно поймать событие endresize в WPF.

4b9b3361

Ответ 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

Что это.