0

I am using Prism for WPF, and I am having an issue disposing view models in a TabControl when the tab is closed. This is leading to a memory leak. I have IDisposable in my view and view models (VM) to perform necessary cleanup. The issue is that where I feel the need to call Dispose on the VM that the DataContext is null on the view.

The VM are auto wired in XAML: prism:ViewModelLocator.AutoWireViewModel="True"

My custom RegionBehavior is below. My expectation is that Prism automatically sets the DataContext on the view. The view is populated with data from the VM so I'm not sure how the DataContext is null in Views_CollectionChanged. Note that when calling Dispose on the view that the DataContext is null in that method too.

Is this normal behavior? How should I call Dispose() on the VM that is being removed from the region in order to cleanup data & unsubscribe from events?

using Prism.Regions;
using System;
using System.Collections.Specialized;
using System.Windows;

namespace WpfProject.ModuleShared
{
    /// <summary>
    /// Code is pulled from PluralSight course download Mastering TabControl.
    /// 
    /// When a view is injected into a region the view goes into the Region Adapter first. The region adapter adapts the view to the region. 
    /// Such as a TabItem is created to create a new tab item and add the view to the tab item for display in the tab control region.
    /// 
    /// Region Behaviors can apply to all regions and not be limited to a single region adapter.
    /// </summary>
    public class RegionManagerAwareBehavior : RegionBehavior
    {
        public const String BehaviorKey = "RegionManagerAwareBehavior";

        protected override void OnAttach()
        {
            Region.Views.CollectionChanged += Views_CollectionChanged;
        }

        /// <summary>
        /// Associate the scoped RegionManager to the injected View.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e">Contains new Views to be added to the region.</param>
        void Views_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
        {
            if (e.Action == NotifyCollectionChangedAction.Add)
            {
                . . . 
            }
            else if (e.Action == NotifyCollectionChangedAction.Remove)
            {
                foreach (var item in e.OldItems)
                {
                    InvokeOnRegionManagerAwareElement(item, x => x.RegionManager = null);

                    var view = item as FrameworkElement;

                    // view.DataContext is always null
                    var vm = view.DataContext;

                    // dispose the view model
                    var disposableVM = vm as IDisposable;
                    if (disposableVM != null)
                        disposableVM.Dispose();

                    // dispose the view
                    var disposableView = view as IDisposable;
                    if (disposableView != null)
                        disposableView.Dispose();
                }
            }
        }
        . . .
    }
}

2
  • As a note, you must not use the as operator without checking the result for null. var view = item as FrameworkElement; is null when item is not a FrameworkElement, and the next code line would throw a NullReferenceException. Commented Mar 18, 2024 at 14:50
  • Thank you for the response. Before I even try and cast item as a FrameworkElement I peek into e.OldItems and each item in the collection's DataContext property is null. I believe there is something I do not understand about Prism and cleanup after removing a view from a region. Commented Mar 18, 2024 at 16:42

1 Answer 1

0

My understanding now is that before RegionManagerAwareBehavior .Views_CollectionChanged is called the view is already removed from the visual tree so WPF has set the DataContext to null.

I've seen this before I call Region.Remove(view) the DataContext is not null. After calling Region.Remove(view) the DataContext is null.

I've found a place where I can wire in calling Dispose before the view is removed from the region.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.