Load layout exception with Prism add-on

Docking/MDI for WPF Forum

Posted 7 years ago by Valéry Sablonnière - Staubli
Version: 12.2.0570
Avatar

Hi,

I'm using your Prism add-on is in your codeplex site.

The problem is when I close a document window, this document window is still present in the Views of the DockSite region.

I tried to use your advice (in http://www.actiprosoftware.com/community/thread/20350/collectionchanged-event-of-the-iviewscollecti#101275), I wrote this:

         protected override void OnAttach() {
	        IRegion region = this.Region;
		if (region != null)
		  region.ActiveViews.CollectionChanged += this.OnRegionActiveViewsCollectionChanged;

		DockSite dockSite = this.hostControl;
		if (dockSite != null) {
                  dockSite.WindowClosed += this.OnDockSiteWindowClosed;
                  dockSite.WindowActivated += this.OnDockSiteWindowActivated;
  		  dockSite.WindowDeactivated += this.OnDockSiteWindowDeactivated;
                  dockSite.WindowRegistered += this.OnDockSiteWindowRegistered;
		}
	}

        private void OnDockSiteWindowClosed(object sender, DockingWindowEventArgs e) {
            DockingWindow dockingWindow = e.Window;
            if (dockingWindow == null)
                return;

            IRegion region = this.Region;
            if (region == null)
                return;

            if (region.ActiveViews.Contains(dockingWindow))
                region.Deactivate(dockingWindow);
            else if (region.ActiveViews.Contains(dockingWindow.DataContext))
                region.Deactivate(dockingWindow.DataContext);

            if (region.Views.Contains(dockingWindow))
                region.Remove(dockingWindow);
            else if (region.Views.Contains(dockingWindow.DataContext))
                region.Remove(dockingWindow.DataContext);
        }

 This solution is working as expected, except when I load a layout, an InvalidOperationException is thrown.

System.InvalidOperationException occurred
  Message=Collection was modified; enumeration operation may not execute.
  Source=mscorlib
  StackTrace:
       at System.ThrowHelper.ThrowInvalidOperationException(ExceptionResource resource)
       at System.Collections.Generic.List`1.Enumerator.MoveNextRare()
       at System.Collections.Generic.List`1.Enumerator.MoveNext()
       at ActiproSoftware.Windows.Controls.Docking.DockSite.#fu(#ki #4j)
       at ActiproSoftware.Windows.Controls.Docking.Serialization.DockSiteLayoutSerializer.#Lr(DockSite #QOd, XmlDockSiteLayout #1Ef)
       at ActiproSoftware.Windows.Controls.Docking.Serialization.DockSiteLayoutSerializer.ApplyTo(DockSite obj)
       at ActiproSoftware.Windows.Serialization.XmlSerializerBase`2.LoadFromStream(Stream stream, TObj obj)
       at SRS.Services.DockService.<>c__DisplayClass5.<LoadDefaultLayout>b__1() in C:\ccm_user\ccm_wa\win_db2012\SRS\src\Services\DockService.cs:line 304
  InnerException: 

Comments (10)

Posted 7 years ago by Actipro Software Support - Cleveland, OH, USA
Avatar

Hi,

My guess is that it's the foreach loop that is closing the tool windows to clear the layout running at the same time you are changing views.  Perhaps try to dispatch your code like:

this.Dispatcher.BeginInvoke(DispatcherPriority.Send, (DispatcherOperationCallback)delegate {
  ...
}, null);


Actipro Software Support

Posted 7 years ago by Valéry Sablonnière - Staubli
Avatar

Where do you suggest to do this ?

I tried in the OnDockSiteWindowClosed method like this:

this.HostControl.Dispatcher.BeginInvoke(System.Windows.Threading.DispatcherPriority.Send, new Action(() =>
{
    if (region.ActiveViews.Contains(dockingWindow))
        region.Deactivate(dockingWindow);
    else if (region.ActiveViews.Contains(dockingWindow.DataContext))
        region.Deactivate(dockingWindow.DataContext);

    if (region.Views.Contains(dockingWindow))
        region.Remove(dockingWindow);
    else if (region.Views.Contains(dockingWindow.DataContext))
        region.Remove(dockingWindow.DataContext);
}));

Now I have no exception when I load a layout but... there is no more ToolWindow...

Posted 7 years ago by Actipro Software Support - Cleveland, OH, USA
Avatar

Hmmm, this may be something we'll need to debug.  Perhaps doing things differently than the foreach loop.  Probably what's happening with the dispatch is that it's clearing the layout ok now, and it loads everything ok, but your code closes them all again afterward.

Can you make a new simple sample project that shows the issue and e-mail it to our support address?  Reference this post and rename the .zip file extension so it doesn't get spam blocked.  We'll use that as a test to make our code changes.  Thanks!


Actipro Software Support

Answer - Posted 7 years ago by Actipro Software Support - Cleveland, OH, USA
Avatar

Hi, thanks for the sample.  We fixed the bug where the foreach was throwing an exception.  But even with that fix and the BeginInvoke removed so that your original code is back in place, the layout remains cleared after the layout load.

So the problem here is that a layout load first closes all tool windows in the layout.  In this case, your code kicks in that removes the views.  After the tool windows are all closed and the layout is clear, the serialized layout data is restored.  But when it goes looking for the tool windows, it doesn't find them since you've removed the views.  Thus the layout remains empty.

I think what you need to do is set a flag somewhere when you are going to load a layout.  If that flag is set, don't execute your code that updates views.  Then clear the flag when the layout load is complete.  That way you aren't removing the tool windows and they should be able to be located during the layout deserialization process.


Actipro Software Support

Posted 7 years ago by Valéry Sablonnière - Staubli
Avatar

Hi,

is it possible to get a sample ? I don't really understand how to synchronize the views and the tool windows after a layout load.

Are there layout loading and layout loaded events in order to do this in the DockSiteRegionBehavior class ?

Posted 7 years ago by Actipro Software Support - Cleveland, OH, USA
Avatar

Sorry there aren't events for layout loading/ed, since it's something your code kicks off.  So one thing you could easily do is add such events to a custom class that inherits DockSite.  Then raise those events surrounding your layout load code.  You could wrap all that up in a nice method that takes your layout filename.

Then in DockSiteRegionBehavior, attach to those events and set/clear a field-based boolean flag from those event handlers.  In OnDockSiteWindowClosed, examine the flag and skip it if you detect its indicating that the layout is loading.


Actipro Software Support

Posted 7 years ago by Valéry Sablonnière - Staubli
Avatar

OK I did this:

public class PrismDockSite : DockSite
{
    public event EventHandler LayoutLoading;

    public event EventHandler LayoutLoaded;

    public void LoadLayout(string path)
    {
        OnLayoutLoading(EventArgs.Empty);
        new DockSiteLayoutSerializer().LoadFromFile(path, this);
        OnLayoutLoaded(EventArgs.Empty);
    }

    public void SaveLayout(string path)
    {
        new DockSiteLayoutSerializer().SaveToFile(path, this);
    }

    protected virtual void OnLayoutLoading(EventArgs e)
    {
        var eh = LayoutLoading;
        if (eh != null)
        {
            eh(this, EventArgs.Empty);
        }
    }

    protected virtual void OnLayoutLoaded(EventArgs e)
    {
        var eh = LayoutLoaded;
        if (eh != null)
        {
            eh(this, EventArgs.Empty);
        }
    }
}

 And in the DockSiteRegionBehavior:

protected override void OnAttach()
{
    IRegion region = this.Region;
    if (region != null)
        region.ActiveViews.CollectionChanged += this.OnRegionActiveViewsCollectionChanged;

    PrismDockSite dockSite = this.hostControl;
    if (dockSite != null)
    {
        dockSite.LayoutLoading += OnDockSiteLayoutLoading;
        dockSite.LayoutLoaded += OnDockSiteLayoutLoaded;
        dockSite.WindowClosed += this.OnDockSiteWindowClosed;
        dockSite.WindowActivated += this.OnDockSiteWindowActivated;
        dockSite.WindowDeactivated += this.OnDockSiteWindowDeactivated;
        dockSite.WindowRegistered += this.OnDockSiteWindowRegistered;
    }
}
private void OnDockSiteLayoutLoading(object sender, EventArgs e)
{
    layoutLoading = true;
}

private void OnDockSiteLayoutLoaded(object sender, EventArgs e)
{
    layoutLoading = false;
}

private void OnDockSiteWindowClosed(object sender, DockingWindowEventArgs e)
{
    DockingWindow dockingWindow = e.Window;
    if (dockingWindow == null)
        return;

    IRegion region = this.Region;
    if (region == null)
        return;

    if (layoutLoading)
        return;

    if (region.ActiveViews.Contains(dockingWindow))
        region.Deactivate(dockingWindow);
    else if (region.ActiveViews.Contains(dockingWindow.DataContext))
        region.Deactivate(dockingWindow.DataContext);

    if (region.Views.Contains(dockingWindow))
        region.Remove(dockingWindow);
    else if (region.Views.Contains(dockingWindow.DataContext))
        region.Remove(dockingWindow.DataContext);
}

So now if I don't close toolwindows and load a layout, my toolwindows are restored correctly BUT if I close 1 toolwindow and load the layout again, the toolwindow closed will never been shown.

Did I miss something ?

Posted 7 years ago by Actipro Software Support - Cleveland, OH, USA
Avatar

That is because when a single tool window is closed, your code to remove its view is executed.  And since when the layout later loads, it doesn't know about the tool window any more (due to you removing the view and thus the tool window earlier), nothing shows for that tool window.

There are some options for lazy loading tool windows though that are described in the Layout Serialization topic.  You may be able to hook into those and dynamically add your view back in as needed.  But overall, if you are planning on keeping tool windows available to be closed and reopened, it may be better to not do any of this view closing code to begin with.


Actipro Software Support

Posted 7 years ago by Valéry Sablonnière - Staubli
Avatar

Sorry, this is not clear for me. What should I do ? It should be great to have a sample projet, thanks.

Posted 7 years ago by Actipro Software Support - Cleveland, OH, USA
Avatar
Hi, the LayoutAutoCreation and LayoutLazyLoading QuickStarts both show some of the concepts described in the documentation. Sorry but we don't have any Prism examples similar to these.


Actipro Software Support

The latest build of this product (v2019.1 build 0683) was released 1 month ago, which was after the last post in this thread.

Add Comment

Please log in to a validated account to post comments.