2011-04-28 21 views
6

Empiezo a usar WPF con PRISM y MVVM. Un problema al que me enfrento es que no puedo encontrar un buen lugar/mejor práctica para cancelar la suscripción a eventos EventAggregator suscritos anteriormente en ViewModel. La siguiente solución, llamada Unsubscribe en el destructor, es demasiado tarde. Solo se ejecuta con la siguiente recolección de basura.Anular la suscripción a eventos de EventAggregator en ViewModels

public class ViewModel : ViewModelBase 
{ 
    public ViewModel() 
    { 
     var eventAggregator = ServiceLocator.Current.GetInstance<IEventAggregator>(); 
     eventAggregator.GetEvent<SeriesSelectionChangedEvent>().Subscribe(OnSeriesSelectionChanged); 
    } 

    ~ViewModel() 
    { 
     var eventAggregator = ServiceLocator.Current.GetInstance<IEventAggregator>(); 
     eventAggregator.GetEvent<SeriesSelectionChangedEvent>().Unsubscribe(OnSeriesSelectionChanged); 
    } 

    void OnSeriesSelectionChanged(SeriesSelectionChangedEventArgs e) 
    { 
    } 
} 

Respuesta

3

Depende de usted! Si su aplicación puede notificar a ViewModel cuando ya no es necesario, debe cancelar la suscripción allí.

Por ejemplo, en nuestro proyecto tenemos IViewDisposeService. Si view (o su modelo) necesita finalización determinística, se registra en IViewDisposeService al mostrarse. Luego, el Core utiliza el mismo servicio para notificar las vistas registradas cuando han sido eliminadas de las regiones.

Otra forma es usar comandos. Su comando de exposición de modelo que debe ser invocado por una vista cuando se está cerrando. ViewModel puede usar el controlador de comandos para anular la suscripción.

Por cierto, si le preocupa que EventAggregator contenga su ViewModel, no es un problema, porque el EventAggregator de Prism usa referencias débiles.

+2

Al registrarse para eventos también puede elegir referencias fuertes. –

+0

Depende de mí, eso es lo que sospechaba. Creo que su segunda sugerencia, activar un comando a través de la vista, se ajusta a mis necesidades. ¡Gran respuesta! –

+0

@ Daniel: Oops, me olvidé de eso. Fue hace mucho tiempo, cuando agregué el último evento en mis proyectos :)) –

3

En algún momento, también tuve el mismo problema. Esto es lo que hicimos (aplicación WPF).

  1. crear una nueva clase de base - DisposableUserControl: control de usuario, IDisposable. Esto contendrá la lógica de disponer de un control de usuario. Código agregado al final.
  2. Reemplace todo el control de usuario en su aplicación con DisposableUserControl. como < aplicación: DisposableUserControl ....> </app.DisposableUserControl>
  3. Agregue un método OnDispose (Virtual) en ViewModelBase que se llama en el método Dispose() de VM. Cada ViewModel de su aplicación debe anular este método OnDispose en que cancelarás tus eventos. Algo así como:
    OnDispose() {base.Dispose(); UnsubscribeEvent (abcEventSubscribername); }

Código

/// <summary> 
    /// Falg used to avoid calling dispose multiple times on same user control 
    /// </summary> 
    private bool isDisposed; 



    /// <summary> 
    /// Dispose 
    /// </summary> 
    public void Dispose() 
    { 
     Dispose(true); 
     GC.SuppressFinalize(this);   
    } 

    /// <summary> 
    /// If disposing equals true, the method has been called directly 
    /// or indirectly by a user's code. Managed and unmanaged resources 
    /// can be disposed. If disposing equals false, the method has been called by the 
    /// runtime from inside the finalizer and you should not reference 
    /// other objects, only unmanaged resources can be disposed. 
    /// </summary> 
    /// <param name="disposing"></param> 
    protected virtual void Dispose(bool disposing) 
    { 
     if (!this.isDisposed) 
     { 
      this.isDisposed = true; 
      if (disposing) 
      { 
       UtilityFunctions.DisposeChildDisposableUserControls(this); 

       if (this.DataContext != null && this.DataContext is IDisposable) 
       { 
        var parent = LogicalTreeHelper.GetParent(this); 

        if (parent == null || ((parent as FrameworkElement).DataContext != this.DataContext)) 
        { 
         (this.DataContext as IDisposable).Dispose(); 
        } 
        BindingOperations.ClearAllBindings(this); 
        this.DataContext = null; 
       } 
      } 
     } 
    } 
2

Usted podría tener la vista notificar al modelo de vista cuando se descarga (o en el caso de una ventana cuando está cerrado). Luego, en el controlador descargado/cerrado en ViewModel, puede darse de baja. Esa es la forma en que lo hago en mi aplicación.

+0

Esto es esencialmente una sugerencia similar a la de Marat. Creo que ese es el camino a seguir. –

+0

Uso Cinch v2 como framework MVVM. Agrega automáticamente eventos para cargar/descargar, etc. –

Cuestiones relacionadas