2

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?

Chris Klepeis
  • 9,783
  • 16
  • 83
  • 149

3 Answers3

2

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();
}
Snowbear
  • 16,924
  • 3
  • 43
  • 67
  • I create a new view each time. The "else if" only executes when the view is removed first (and then it removes the associated Pane)... the first part of the if (with the Closed event handler) runs when I click "X" in the Pane, and it should remove the view from the region... which is where the issue is occuring. – Chris Klepeis Feb 14 '11 at 15:39
  • I'm getting a little closer, instead of handling the Closed event, I changed it to Closing... but now, when I click the menu item to open it back up - it has the same instance... I need to create a new instance of the view – Chris Klepeis Feb 14 '11 at 15:42
  • @Chris, It's hard to tell from this code why you're getting the same View. – Snowbear Feb 14 '11 at 15:45
  • +1 because this will probably fix the issue. The problem is that the `UIElement item` has been previously added to another `DockableContent` object, so you cannot assign it to a 2nd one. You may be clearing the `Region` of `DockablePanel` objects, however that is not clearing `DockablePanel.Content` so the `UIElement` is still assigned to the `DockablePanel` – Rachel Nov 08 '11 at 15:58
2

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.

Community
  • 1
  • 1
Chris Klepeis
  • 9,783
  • 16
  • 83
  • 149
1

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.

user7116
  • 63,008
  • 17
  • 141
  • 172
  • After region.Remove(item) is called, and I call Debug.WriteLine(regionTarget.Items.Count); it outputs 0, so I'm pretty sure this is taken care of. AvalonDock is taking care of that I believe. – Chris Klepeis Feb 14 '11 at 15:15