Prism RegionAdapter - Removing then Adding View

4.9k views Asked by At

I have a prism/wpf/mef solution that contains an AvalonDock. I created a RegionAdapterBase<Pane> class that handles creating and removing the Panes from AvalonDock.

Heres the problem I'm running into:

  1. I click a button in my menu and a view is registered with a region and displayed in my DocumentPane
  2. I click the close button in AvalonDock to close the tab and remove the view
  3. I click the same menu button to add it back again
  4. I receive the error:

"Specified element is already the logical child of another element. Disconnect it first."

So... this tells me that something is lingering that I need to remove, but I cannot figure out where it is. Heres some code from my RegionAdapter:

private void OnViewsCollectionChanged(object sender, NotifyCollectionChangedEventArgs e, IRegion region, Pane regionTarget)
{
    if (e.Action == NotifyCollectionChangedAction.Add)
    {
        foreach (object item in e.NewItems)
        {
            UIElement view = item as UIElement;

            if (view is ITabViewInfo)
            {
                if (view != null)
                {
                    DockableContent newContentPane = new DockableContent()
                    {
                        Content = item,
                        Title = ((ITabViewInfo)view).TabViewTitle,
                        Icon = new Image()
                        {
                            Source = new BitmapImage(((ITabViewInfo)view).TabViewIcon)
                        }.Source,
                        IsCloseable = ((ITabViewInfo)view).IsCloseable,
                        HideOnClose = ((ITabViewInfo)view).IsHideOnClose
                    };

                    newContentPane.Closed += (contentPaneSender, args) =>
                    {
                        Debug.WriteLine("Removing view from region", "Prism");
                        region.Remove(item);
                    };

                    regionTarget.Items.Add(newContentPane);
                    newContentPane.Activate();
                }
            }
        }
    } else if (e.Action == NotifyCollectionChangedAction.Remove) {
            regionTarget.Items.Clear();
    }
   }

From my debug lines, the DocumentPane and region views are properly being destroyed... when I click to add the item back to the view, I get the above error message on the line that does:

Content = item,

Heres the code from my module that runs when the menu button is pressed:

    if (_regionManager.Regions["MainRegion"].Views.Any(m => m.GetType() == typeof(Views.ClassicFrontierView)))
    {
        Debug.WriteLine(_regionManager.Regions["MainRegion"].Views.Count());
    }
    else
    {
        Debug.WriteLine("Adding view to region", "Prism");
        _regionManager.RegisterViewWithRegion("MainRegion", typeof(Views.ClassicFrontierView));
    }

Any idea what I'm missing?

3

There are 3 answers

0
Chris Klepeis On BEST ANSWER

Instead of handling the Closed event (which may have lost a reference to the underlying view), I handle the Closing event.

This worked, however, when I tried to re-open the tab, it was displaying the same instance. After reading this In Composite WPF (Prism), what is the difference between IRegion.Add and IRegionManager.RegisterViewWithRegion? I changed this:

_regionManager.RegisterViewWithRegion("MainRegion", typeof(Views.ClassicFrontierView));

to this:

_regionManager.Regions["MainRegion"].Add(new Classic.Views.ClassicFrontierView());

I still have to do some research with Prism / avalondock to make sure there will be no memory leaks, but as of now it appears to be working.

1
user7116 On

You likely need to remove it from regionTarget as well.

You can use Snoop to see what hasn't been removed from the Visual Tree and then attempt to find which container you need to remove your element from. Other possibilities are things like an unfrozen Icon image, etc.

4
Snowbear On

Do you create a new View each time or you trying to show existing View several times? If second is correct I would try this:

else if (e.Action == NotifyCollectionChangedAction.Remove) {
    foreach (DockableContent content in regionTarget.Items)
        content.Content = null;
    regionTarget.Items.Clear();
}