Alex Rivera | Logout

How do I remove event handlers when I'm finished with a View and ViewModel, but not the Model

Asked 2011-09-14T22:39:13.347
22

In my application, I am often creating new Views and ViewModels, but persisting the same Models. For example, I might show a simple view of a list of items in my main window, and have another window with further details of any particular item. The detail window can be opened and closed at any time, or multiple windows can be opened simultaneously for different items on the list.

Therefore, there can be more than one ViewModel for a given model object, and they need to be updated with changes from other places. (I'm using INotifyPropertyChanged on my models.) I want to get rid of ViewModels when I am done with them, i.e., as the detail window is closed.

public DetailViewModel(MyDetailModel detailModel)
{
    // Retain the Detail Model
    this.model = detailModel;

    // Handle changes to the Model not coming from this ViewModel
    this.model.PropertyChanged += model_PropertyChanged;  // Potential leak?
}

It is my understanding that the event handler will cause the Model to retain a reference to the ViewModel, and keep it from getting garbage collected.

1) Is this correct? How can I tell if these references are still present?

2) How should I determine the ViewModel is no longer needed and unsubscribe from the events?

Edit
Report

2 Answers

13

I'm a big fan of using IDisposable for this kind of thing. In fact, you can get excellent results using a CompositeDisposable to handle all of your clean-up needs.

Here's what I do:

public class DetailViewModel : IDisposable
{
    private readonly CompositeDisposable _disposables
        = new CompositeDisposable();

    public void Dispose()
    {
        _disposables.Dispose();
    }

    private readonly MyDetailModel _model;

    public DetailViewModel(MyDetailModel model)
    {
        _model = model;

        _model.PropertyChanged += _model_PropertyChanged;

        Action removeHandler = () =>
            _model.PropertyChanged -= _model_PropertyChanged;

        _disposables.Add(removeHandler);
    }

    private void _model_PropertyChanged(
        object sender, PropertyChangedEventArgs e)
    { /* ... */ }
}

What this lets you do is stick all sorts of clean-up code into a collection that automatically gets run once and only once when IDisposable.Dispose() gets called on your class.

This is particularly nice for event handlers as it allows you to place add handler code next to remove handler code in your source and this makes refactoring much simpler. It's very easy to see if you are actually removing handlers if the code is next to the add handler.

To make this happen you need to add two classes to your code.

The first is CompositeDisposable:

public sealed class CompositeDisposable : IEnumerable<IDisposable>, IDisposable
{
    private readonly List<IDisposable> _disposables;
    private bool _disposed;

    public CompositeDisposable()
    {
        _disposables = new List<IDisposable>();
    }

    public CompositeDisposable(IEnumerable<IDisposable> disposables)
    {
        if (disposables == null)
            { throw new ArgumentNullException("disposables"); }
        _disposables = new Li
answered 2011-09-15T01:14:53.740
2

You may want to consider using a Weak Event Pattern. I believe that Microsoft introduced WeakEventManager and IWeakEventListener to solve this exact garbage collection issue.

answered 2011-09-14T23:58:31.983

Your Answer