Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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?

like image 455
Nathan A Avatar asked Dec 14 '22 12:12

Nathan A


2 Answers

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!

like image 178
Nathan A Avatar answered Dec 24 '22 13:12

Nathan A


Modification of Nathan A's answer for Prism 6 (IView was removed) and uses a handy built in helper method to call Dispose an View and ViewModel.

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 || e.Action == NotifyCollectionChangedAction.Replace))
            return;

        foreach (var removedView in e.OldItems)
            MvvmHelpers.ViewAndViewModelAction<IDisposable>(removedView, d => d.Dispose());
    }
}
like image 31
Ronnie Overby Avatar answered Dec 24 '22 12:12

Ronnie Overby