2011-04-28 20 views
6

Zaczynam używać WPF z PRISM i MVVM. Jednym z problemów, przed którymi stoję, jest to, że nie mogę znaleźć dobrego miejsca/najlepszej praktyki do anulowania subskrypcji EventAggregator wcześniej subskrybowanych w ViewModel. Następujące rozwiązanie - wywołanie funkcji "Anuluj subskrypcję" w destruktorze - jest o wiele za późno. Działa tylko z następnym zbiorem śmieci.Anulowanie subskrypcji zdarzeń EventAggregator w 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) 
    { 
    } 
} 

Odpowiedz

3

To zależy od Ciebie! Jeśli twoja aplikacja może powiadomić ViewModel, kiedy nie jest już potrzebna, więc powinieneś się tam wypisać.

Na przykład w naszym projekcie mamy IViewDisposeService. Jeśli widok (lub jego model) wymaga deterministycznej finalizacji, rejestruje się w widoku IViewDisposeService podczas wyświetlania. Następnie Core używa tej samej usługi do powiadamiania zarejestrowanych widoków, gdy zostały one usunięte z regionów.

Innym sposobem jest użycie poleceń. Twój model ujawnia polecenie, które musi zostać wywołane przez widok podczas zamykania. ViewModel może użyć programu obsługi komend, aby anulować subskrypcję.

Nawiasem mówiąc, jeśli obawiasz się, że EventAggregator przetrzyma Twój ViewModel, nie stanowi to problemu, ponieważ EventAggregator Prisma używa słabych referencji.

+2

Podczas rejestrowania się w wydarzeniach możesz także wybrać silne referencje. –

+0

To zależy od mnie - podejrzewam. Myślę, że twoja druga sugestia - wywołanie polecenia w widoku - pasuje do moich potrzeb. Świetna odpowiedź! –

+0

@Daniel: Ups, zapomniałem o tym. To było dawno temu, kiedy dodałem ostatnie wydarzenie w moich projektach :)) –

3

Cóż, kiedyś wróciłem, również stanąłem w obliczu tego samego problemu. Oto co zrobiliśmy (aplikacja WPF).

  1. Utwórz nową klasę bazową - DisposableUserControl: UserControl, IDisposable. Będzie to zawierało logikę unieszkodliwienia kontroli użytkownika. Kod został dodany na końcu.
  2. Zastąp wszystkie elementy sterowania użytkownika w aplikacji za pomocą polecenia DisposableUserControl. jak < aplikacja: DisposableUserControl ....> </app.DisposableUserControl>
  3. Dodaj metodę OnDispose (wirtualny) w ViewModelBase który jest nazywany w Dispose() metody VM.Each ViewModel swojej aplikacji należy zastąpić tę metodę w OnDispose które zrezygnujesz z subskrypcji wydarzeń. Coś w rodzaju -
    OnDispose() {base.Dispose(); UnsubscribeEvent (abcEventSubscribername); }

Kod

/// <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

Można mieć View powiadomi ViewModel gdy jest rozładowany (lub w przypadku, gdy okno jest zamknięte). Następnie w programie obsługi Unloaded/Closed w ViewModel można zrezygnować z subskrypcji. Tak to robię w mojej aplikacji.

+0

Jest to w zasadzie podobna sugestia niż w przypadku Marata. Myślę, że to jest najlepsza droga. –

+0

Używam Cinch v2 jako framework MVVM. Automatycznie dodaje zdarzenia do załadowania/rozładowania itp. –

Powiązane problemy