Как поймать конечное окно изменения размера? - PullRequest
21 голосов
/ 17 декабря 2010

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

Ответы [ 3 ]

27 голосов
/ 18 декабря 2010

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
}
13 голосов
/ 07 мая 2013

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
}
5 голосов
/ 26 апреля 2016

Вы можете точно определить, когда изменение размера окна WPF закончилось, и вам не нужен таймер.Собственное окно получает сообщение WM_EXITSIZEMOVE, когда пользователь отпускает левую кнопку мыши в конце окна, изменяя размер операции или .Окно WPF не получает это сообщение, поэтому нам нужно подключить функцию WndProc, которая его получит.Мы можем использовать HwndSource с WindowInteropHelper, чтобы получить дескриптор нашего окна.Затем мы добавим хук к нашей функции WndProc.Мы сделаем все это в событии окна Loaded (код 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.Кроме того, не выполняйте в этой функции ничего, кроме обработки определенных сообщений, которые вас интересуют.

Теперь 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

Вот и все.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...