Search code examples
c#.netwpfmvvmprism-5

Prism Regions Not Disposing Views or ViewModels


I assumed that Prism Regions would automatically detect and call Dispose any views or view models that implemented the IDisposable interface. Turns out I was wrong.

I then considered implementing IActiveAware so I could dispose my views/viewmodels on my own, but that seems rather hackish. I'd rather have it done automatically.

How can I configure Prism Regions to automatically dispose my views and view models that implement IDisposable?


Solution

  • After searching far and wide on the internet, and not finding any real solution, I developed my own custom RegionBehavior, which turned out to work very nicely.

    The behavior listens to the region's view collection for any changes, and when any are removed, it checks for and calls Dispose on the view and/or view model, only if they implement IDisposable.

    class DisposeClosedViewsBehavior : RegionBehavior
    {
        protected override void OnAttach()
        {
            Region.Views.CollectionChanged += Views_CollectionChanged;
        }
    
        private void Views_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
        {
            if (e.Action != NotifyCollectionChangedAction.Remove) return;
    
            foreach (var removedView in e.OldItems)
            {
                IDisposable disposableView = removedView as IDisposable;
                IDisposable disposableViewModel;
    
                var iviewView = removedView as IView;
                if (iviewView != null)
                {
                    disposableViewModel = iviewView.DataContext as IDisposable;
                }
                else
                {
                    var frameworkElementView = removedView as FrameworkElement;
                    disposableViewModel = frameworkElementView?.DataContext as IDisposable;
                }
    
                disposableView?.Dispose();
                disposableViewModel?.Dispose();
            }
        }
    }
    

    The final step is to plug this behavior into prism by overriding the bootstrapper ConfigureDefaultRegionBehaviors method:

    protected override IRegionBehaviorFactory ConfigureDefaultRegionBehaviors()
    {
        var factory = base.ConfigureDefaultRegionBehaviors();
    
        factory.AddIfMissing(nameof(DisposeClosedViewsBehavior), typeof(DisposeClosedViewsBehavior));
    
        return factory;
    }
    

    Works like a charm!