Comment saisir la fenêtre de fin de redimensionnement?

j'ai besoin d'attraper la fin de l'événement dans WPF.

19
demandé sur Dave Clemmer 2010-12-17 23:52:49

3 réponses

WPF ne fournit pas un événement qui déclenche uniquement à la fin du processus de redimensionnement. SizeChanged est le seul événement associé au redimensionnement de fenêtre - et il se déclenchera plusieurs fois pendant le processus de redimensionnement.

un hack total serait de mettre constamment un ticking de minuterie quand l'événement SizeChanged se déclenche. Puis timer n'aura pas la chance de cocher jusqu'à ce que le redimensionnement se termine et à ce point faire votre traitement unique.

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
}
24
répondu Martin 2010-12-17 21:09:47

les Extensions réactives pour .NET offrent des capacités vraiment cool pour traiter les modèles d'événements standard, y compris la possibilité d'étouffer les événements. J'ai eu un problème similaire en traitant les événements de taille changée et bien que la solution soit encore un peu "hacky" je pense que les Extensions réactives fournit une façon beaucoup plus élégante de la mettre en œuvre. Voici mon oeuvre:

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);
    });

cela va effectivement étrangler le SizeChanged événement tel que votre méthode Size_Changed (où vous pouvez exécuter du code personnalisé) ne sera pas exécuté avant que 200 millisecondes (ou aussi longtemps que vous voulez attendre) ne soient passées sans un autre SizeChanged événement étant tiré.

private void Size_Changed(SizeChangedEventArgs e) {
    // custom code for dealing with end of size changed here
}
12
répondu Jesse Carter 2013-05-07 16:22:29

vous pouvez détecter exactement quand une fenêtre WPF redimensionnée s'est terminée, et vous n'avez pas besoin de minuterie. Une fenêtre native reçoit le WM_EXITSIZEMOVE message lorsque l'utilisateur relâchez le bouton gauche de la souris à la fin d'une fenêtre redimensionner ou opération de déplacement. Une fenêtre WPF ne reçoit pas ce message, nous avons donc besoin de brancher un WndProc fonction qui le recevra. Nous pouvons utiliser HwndSourceWindowInteropHelper pour obtenir notre poignée de la fenêtre. Alors nous allons ajouter le crochet à notre WndProc fonction. Nous ferons tout ce qui dans la fenêtre Loaded événement (vb.net code):

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

Maintenant, dans notre WndProc, nous allons écouter l' WM_EXITSIZEMOVE message:

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

C'et une technique similaire est expliqué ici et ici.

Notez que la fonction doit retourner IntPtr.Zéro. De plus, ne faites rien dans cette func, sauf Gérer les messages spécifiques qui vous intéressent.

Maintenant, WM_EXITSIZEMOVE est également envoyé à la fin d'une opération de déplacement, et nous nous intéressons uniquement à redimensionner. Il y a plusieurs façons de déterminer que c'était la fin de l'opération de redimensionnement. Je l'ai fait par l'écoute de l' WM_SIZING message (envoyé plusieurs fois lors de redimensionnement), combiné avec un drapeau. Toute la solution ressemble à ceci:

(Note: Ne pas confondre avec la mise en évidence du code ici, car son mauvais pour 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

C'est ça.

4
répondu Bohoo 2017-05-23 12:01:34