2010-12-17 8 views

Respuesta

24

WPF no proporciona un evento que solo se dispare al final del proceso de cambio de tamaño. SizeChanged es el único evento asociado con el cambio de tamaño de la ventana, y se disparará varias veces durante el proceso de cambio de tamaño.

Un truco total sería establecer constantemente un contador de tiempo cuando se activa el evento SizeChanged. Entonces, el temporizador no tendrá la posibilidad de marcar hasta que el cambio de tamaño termine y en ese punto realice su procesamiento de una sola vez.

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 
} 
+6

Form.ResizeBegin/End in Winforms. La notificación todavía está allí, pero se ignora en WPF. Dos pasos adelante, un paso atrás. –

+1

@Martin, explica por qué pusiste _resizeTimer.IsEnabled = verdadero; antes de parar y comenzar? Eso no tiene sentido para mí. –

+0

Me gusta este mecanismo porque permite hacer algunos procesamientos cuando el usuario pausa un cambio de tamaño. Tenía una situación en la que era necesario volver a diseñar el lienzo cuando el usuario cambiaba el tamaño. Con este enfoque de temporizador cuando el usuario dejó de mover el mouse (pero no lo liberó) se puede ejecutar el re-diseño y se puede ver el efecto del nuevo tamaño. A mi equipo de pruebas le gustó más que al anterior; el nuevo diseño solo se produjo cuando se lanzó el mouse, es decir, el enfoque WM_EXITSIZEMOVE. Establecí el intervalo del temporizador en 200 ms en lugar del valor 1500 utilizado en este código de ejemplo. – pjm

11

Extensiones reactivas para .NET ofrece algunas capacidades realmente geniales para tratar con patrones de eventos estándar que incluyen la posibilidad de regular los eventos. Tuve un problema similar al lidiar con los cambios de tamaño de los eventos y, aunque la solución todavía es algo "hacky", creo que Reactive Extensions ofrece una forma mucho más elegante de implementarlo. Aquí está mi aplicación:

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

Esto le estrangular efectivamente el SizeChanged caso de tal manera que su método de Size_Changed (donde se puede ejecutar código personalizado) no se ejecutará hasta 200 milisegundos (o el tiempo que le gustaría que esperar) han pasado sin otro evento SizeChanged que se está disparando.

private void Size_Changed(SizeChangedEventArgs e) { 
    // custom code for dealing with end of size changed here 
} 
+0

¡Realmente elegante! – MuiBienCarlota

3

Puede detectar exactamente cuándo cambiar el tamaño de una ventana de WPF terminó, y no es necesario un temporizador. Una ventana nativa recibe el mensaje WM_EXITSIZEMOVE cuando el usuario suelta el botón izquierdo del mouse al final de una ventana cambiar el tamaño o mover operación. Una ventana de WPF no recibe este mensaje, por lo que debemos conectar una función WndProc que la recibirá. Podemos usar HwndSource con WindowInteropHelper para obtener nuestro identificador de ventana. Luego agregaremos el gancho a nuestra función WndProc. Haremos todo lo que en el caso Loaded ventana (código de 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 

Ahora, en nuestro WndProc, vamos a escuchar el mensaje 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 

Esto y una técnica similar se explica here y here.

Observe que la función debe devolver IntPtr.Zero. Además, no haga nada en esta función excepto manejar los mensajes específicos que le interesen.

Ahora, WM_EXITSIZEMOVE también se envía al final de una operación de traslado, y solo estamos interesados ​​en cambiar el tamaño. Hay varias formas de determinar que este fue el final de la operación de cambio de tamaño. Lo hice escuchando el mensaje WM_SIZING (que se envió muchas veces durante el cambio de tamaño), combinado con un indicador. La solución entera se ve así:

(Nota: No se confunda con el código de destacar en este sentido, la causa de su mal 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 

Eso es todo.

+1

¡Muchas gracias por esta pista! La solución con un temporizador es muy mala ... –

+0

Deberíamos manejar 'WM_ENTERSIZEMOVE' en lugar de' WM_SIZING', creo. – yumetodo

Cuestiones relacionadas