AvalonDock 2.0 - PRISM Integration

Aug 4, 2012 at 10:55 AM
Edited Aug 4, 2012 at 11:13 AM

Hi all,
as I see that there is a large interest in PRISM integration I took some time to build a sample project that shows a possible way to use AD as region for PRISM views. First of all, I want to let you know that absolutely I'm not a PRISM expert so many of the ideas that you can find in the sample project I build could simply not applicable in you scenario.

Anyway this thread can be used to share implementation samples that can be useful for many of us challenging with this integration.

I've build two region adapter one for the LayoutAnchorble and one for the DockingManager. the second use internally a behavior.

This is the region adapter for the LayoutAnchorable:

 

    class AnchorableRegionAdapter : RegionAdapterBase<LayoutAnchorable>
    {
        public AnchorableRegionAdapter(IRegionBehaviorFactory regionBehaviorFactory)
            : base(regionBehaviorFactory)
        { 
        
        }



        protected override void Adapt(IRegion region, LayoutAnchorable regionTarget)
        {
            if (regionTarget == null) 
                throw new ArgumentNullException("regionTarget");

            if (regionTarget.Content != null)
            {
                throw new InvalidOperationException();
            }

            region.ActiveViews.CollectionChanged += delegate
            {
                regionTarget.Content = region.ActiveViews.FirstOrDefault();
            };

            region.Views.CollectionChanged +=
                (sender, e) =>
                {
                    if (e.Action == NotifyCollectionChangedAction.Add && region.ActiveViews.Count() == 0)
                    {
                        region.Activate(e.NewItems[0]);
                    }
                };
        }

        protected override IRegion CreateRegion()
        {
            return new SingleActiveRegion();
        }

    }

 

and this is the region adapter (and behavior) for the docking manager:

 

    class DockingManagerRegionAdapter : RegionAdapterBase<DockingManager>
    {
        public DockingManagerRegionAdapter(IRegionBehaviorFactory regionBehaviorFactory)
            : base(regionBehaviorFactory)
        {

        }

        protected override void Adapt(IRegion region, DockingManager regionTarget)
        {
            
        }

        protected override IRegion CreateRegion()
        {
            return new Region();
        }

        protected override void AttachBehaviors(IRegion region, DockingManager regionTarget)
        {
            if (region == null) 
                throw new System.ArgumentNullException("region");

            // Add the behavior that syncs the items source items with the rest of the items
            region.Behaviors.Add(DockingManagerDocumentsSourceSyncBehavior.BehaviorKey, new DockingManagerDocumentsSourceSyncBehavior()
            {
                HostControl = regionTarget
            });

            base.AttachBehaviors(region, regionTarget);
        }
    }

    class DockingManagerDocumentsSourceSyncBehavior : RegionBehavior, IHostAwareRegionBehavior
    {
        public static readonly string BehaviorKey = "DockingManagerDocumentsSourceSyncBehavior";
        private bool _updatingActiveViewsInManagerActiveContentChanged;
        private DockingManager _dockingManager;

        public DependencyObject HostControl
        {
            get
            {
                return this._dockingManager;
            }

            set
            {
                this._dockingManager = value as DockingManager;
            }
        }

        ObservableCollection<object> _documents = new ObservableCollection<object>();
        ReadOnlyObservableCollection<object> _readonlyDocumentsList = null;
        public ReadOnlyObservableCollection<object> Documents
        {
            get
            {
                if (_readonlyDocumentsList == null)
                    _readonlyDocumentsList = new ReadOnlyObservableCollection<object>(_documents);

                return _readonlyDocumentsList;
            }

        }

        /// <summary>
        /// Starts to monitor the <see cref="IRegion"/> to keep it in synch with the items of the <see cref="HostControl"/>.
        /// </summary>
        protected override void OnAttach()
        {
            bool itemsSourceIsSet = this._dockingManager.DocumentsSource != null;


            if (itemsSourceIsSet)
            {
                throw new InvalidOperationException();
            }

            this.SynchronizeItems();

            this._dockingManager.ActiveContentChanged += this.ManagerActiveContentChanged;
            this.Region.ActiveViews.CollectionChanged += this.ActiveViews_CollectionChanged;
            this.Region.Views.CollectionChanged += this.Views_CollectionChanged;
        }

        private void Views_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
        {
            if (e.Action == NotifyCollectionChangedAction.Add)
            {
                int startIndex = e.NewStartingIndex;
                foreach (object newItem in e.NewItems)
                {
                    _documents.Insert(startIndex++, newItem);
                }
            }
            else if (e.Action == NotifyCollectionChangedAction.Remove)
            {
                foreach (object oldItem in e.OldItems)
                {
                    _documents.Remove(oldItem);
                }
            }
        }

        private void SynchronizeItems()
        {
            BindingOperations.SetBinding(
                _dockingManager,
                DockingManager.DocumentsSourceProperty,
                new Binding("Documents") { Source = this });

            foreach (object view in this.Region.Views)
            {
                _documents.Add(view);
            }
        }


        private void ActiveViews_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
        {
            if (this._updatingActiveViewsInManagerActiveContentChanged)
            {
                return;
            }

            if (e.Action == NotifyCollectionChangedAction.Add)
            {
                if (this._dockingManager.ActiveContent != null
                    && this._dockingManager.ActiveContent != e.NewItems[0]
                    && this.Region.ActiveViews.Contains(this._dockingManager.ActiveContent))
                {
                    this.Region.Deactivate(this._dockingManager.ActiveContent);
                }

                this._dockingManager.ActiveContent = e.NewItems[0];
            }
            else if (e.Action == NotifyCollectionChangedAction.Remove &&
                     e.OldItems.Contains(this._dockingManager.ActiveContent))
            {
                this._dockingManager.ActiveContent = null;
            }
        }

        private void ManagerActiveContentChanged(object sender, EventArgs e)
        {
            try
            {
                this._updatingActiveViewsInManagerActiveContentChanged = true;

                if (_dockingManager == sender)
                {
                    object activeContent = _dockingManager.ActiveContent;
                    foreach (var item in this.Region.ActiveViews.Where(it => it != activeContent))
                    {
                        this.Region.Deactivate(item);
                    }


                    if (this.Region.Views.Contains(activeContent) && !this.Region.ActiveViews.Contains(activeContent))
                    {
                        this.Region.Activate(activeContent);
                    }
                }
            }
            finally
            {
                this._updatingActiveViewsInManagerActiveContentChanged = false;
            }
        }

    }

 

Download the project sample here.

Thanks,

Ado

Aug 4, 2012 at 12:53 PM

I've only heard of PRISM because of people asking about whether AvalonDock works with it, but it does sound worthwhile. Thanks for the example code!

Aug 7, 2012 at 1:34 PM

I'm new to prism and avalon dock. Thanks for the helping example...

How can I set/get the avalon properties like "Title" etc. from within the module?

Thanks for your help

 

AnTri

Aug 9, 2012 at 8:32 PM
Edited Aug 9, 2012 at 8:35 PM

Thanks Ado,

your adapters works great!

 

AnTri, if your viewmodel has a Title property you can defined a Style using the style selector like it is demonstrated in the MVVM example: 

<ad:DockingManager.LayoutItemContainerStyleSelector>
    <local:PanesStyleSelector>
       <local:PanesStyleSelector.ProjectInstanceStyle>
          <Style TargetType="{x:Type ad:LayoutItem}">
             <Setter Property="Title" Value="{Binding Model.Title}"/>
                            ...
           </Style>
      </local:PanesStyleSelector.ProjectInstanceStyle> 
  </local:PanesStyleSelector> 
</ad:DockingManager.LayoutItemContainerStyleSelector>


 

 

 

Aug 24, 2012 at 7:18 PM

How can you use these adapters in a PanesTemplateSelector?

Currently I load my prism regions like this:

<Dock:PanesTemplateSelector.RequisitionHelpTemplate>
     <DataTemplate>
          <Grid>
               <ContentControl Content="{Binding RegionContentControl}"/>
         </Grid>
     </DataTemplate>
</Dock:PanesTemplateSelector.RequisitionHelpTemplate>

Is there a way to adapt the example (that binds the regions directly to LayoutAnchorables) to use the MVVM style that most of avalon dock now uses?

Aug 28, 2012 at 6:42 PM

I'm converting a Prism/Unity application from a TabControl to AvalonDock 2.0.  The old code uses Triggers to support INavigationAware and IConfirmNavigationRequest for all modules.  For the supporting these interfaces with a TabControl we implemented twoTrigger Actions.  One for when the users closes a tab item and one for when the selected tab item changes. 

We are new to AvalonDock and are getting road blocked with every thing we try.  Any ideas would be greatly appreciated.

 

 

Sep 3, 2012 at 10:04 AM

@Vaccano: You could simple create a DockingManagerAnchorablesSourceSyncBehavior cloning the DockingManagerDocumentsSourceSyncBehavior attaching the AnchorableSource instead of DocumentsSource and then simply register the behavior in the DockingManagerRegionAdapter. It should to the trick, Ado

Sep 3, 2012 at 10:07 AM

@JayGilbreath: Sorry, I'm not aware of such interfaces but I think you can use the DocumentClose/Closing events and ActiveContent property to get close and document switch events. Ado

Sep 3, 2012 at 1:32 PM
Edited Sep 3, 2012 at 2:01 PM
SlickRick wrote:

AnTri, if your viewmodel has a Title property you can defined a Style using the style selector like it is demonstrated in the MVVM example: 

<ad:DockingManager.LayoutItemContainerStyleSelector>
    <local:PanesStyleSelector>
       <local:PanesStyleSelector.ProjectInstanceStyle>
          <Style TargetType="{x:Type ad:LayoutItem}">
             <Setter Property="Title" Value="{Binding Model.Title}"/>
                            ...
           </Style>
      </local:PanesStyleSelector.ProjectInstanceStyle> 
  </local:PanesStyleSelector> 
</ad:DockingManager.LayoutItemContainerStyleSelector>


Can anyone get this working? I can't... SlickRick, can you provide the source code for the PanesStyleSelector?

I can set the Title property manually, but when binding it to my viewmodel (using Model.Title like above), nothing happens like it cant reach the view model.

Sep 4, 2012 at 2:21 PM

Hey,

I'm facing the same issue as the user thj above. I'm trying to bind the title property in my viewmodel to be show on the tab but nothing is shown when I run my application. I'm using avalondock with Prism. 

 

Sep 5, 2012 at 10:40 AM
Farre82 wrote:

Hey,

I'm facing the same issue as the user thj above. I'm trying to bind the title property in my viewmodel to be show on the tab but nothing is shown when I run my application. I'm using avalondock with Prism. 

 


Nice to know that I'm not alone. Did you figure it out Farre82?

Sep 5, 2012 at 3:04 PM
Edited Sep 5, 2012 at 3:08 PM

thj wrote:


Can anyone get this working? I can't... SlickRick, can you provide the source code for the PanesStyleSelector?

I can set the Title property manually, but when binding it to my viewmodel (using Model.Title like above), nothing happens like it cant reach the view model.


Sorry for the delay, i havent notice that someone actually asked me a question :)

I understand that the tabs gets populated correctly when you add them to the region but the Tab's Title doesnt?

At first glance i would say that the binding must be wrong.

The immediate window should show you exactly what is the binding error but im pretty sure that the error is that it is trying to bind using the View as the 'Model' item and not the ViewModel. Therefore you should change the binding to:

 <Setter Property="Title" Value="{Binding Model.DataContext.Title}"/>

So the Model would be your View, and it's DataContext would return the ViewModel. After that just use the property path to you Title property.

I hope this solves your problem.

Remember, when faced with binding issue, the immediate Window is your best friend :)

Sep 5, 2012 at 10:16 PM
thj wrote:
Farre82 wrote:

Hey,

I'm facing the same issue as the user thj above. I'm trying to bind the title property in my viewmodel to be show on the tab but nothing is shown when I run my application. I'm using avalondock with Prism. 

 


Nice to know that I'm not alone. Did you figure it out Farre82?


Hi,

The StyleSelector pattern works fine with a DockingManagerRegionAdapter as the content of the LayoutDocument is known when the SelectStyle function is called.

This does not work that way with an AnchorableRegionAdapter. I guess the StyleSelector is applied once when the LayoutAnchorable is created with an empty content and is not dynamically binded to this content. We can see the item parameter of the SelectStyle function is set to null when called.

Sep 6, 2012 at 9:55 AM

Hey again, 

SlickRick your solution worked thanks !, but liked YannRomefort said the StyleSelector only works with LayoutDocument. The item parameter is always null with LayoutAnchorable. 

public override System.Windows.Style SelectStyle(object item, System.Windows.DependencyObject container)

local:PanesStyleSelector>
                            <local:PanesStyleSelector.ToolStyle>
                                <Style TargetType="{x:Type avalonDock:LayoutAnchorableItem}">
                                    <Setter Property="Title" Value="{Binding Model.DataContext.HeaderInfo}"/>

Sep 6, 2012 at 2:46 PM

Oh I see...

 in my solution im only using, currently, the DockingManager adapter... i havent tried the LayoutAnchorable adpater yet.

I will try it during the day and give a feedback when possible.

 

Sep 6, 2012 at 7:45 PM
Edited Sep 6, 2012 at 7:45 PM

Well my initial test confirm your statements, but i beleive it is a normal behaviour... Let me explain...

If you use the region directly on a LayoutAnchorable then at loading avalon dock creates it's layout and the LayoutAnchorable get created with an empty content (since the region was not yet created or populated. Therefore the style selector gets trigger with an empty content.

Sadly, i havent found an "easy" way to do this. I could be missing something...

This is what i tried:

  1. Adding the Title binding directly on the LayoutAnchorable - At first i thought that this would be THE solution, but found out that binding is not possible since it's not a Dependency property.
  2. Adding an Attached Behavior to the LayoutAnchorable - To work around the fact that the Title is not a dependency property, i've created a attached property with a binding. Sadly the binding on this Attached Property is failing because it can't find the "governing FramewokrElement" on the LayoutAnchorable
  3. Adding a 'Default' Style to the LayoutAnchorableItem - So i've tried the following code, but found out that when the Model was actualy updated with the Content, the binding was not notified by it and therefore nothing was shown.
    <ad:DockingManager.LayoutItemContainerStyle>
        <Style TargetType="{x:Type ad:LayoutAnchorableItem}">
            <Setter Property="Title"
                    Value="{Binding Model.DataContext.Test}" />
        </Style>
    </ad:DockingManager.LayoutItemContainerStyle>
    
    
  4. Trying to add the binding manually in the region.Views.CollectionChanged of the AnchorableRegionAdapter - This could be a good solution if it was possible to retreive the LayoutAchorableItem of a Target LayoutAchorable. Sadly I couldn't find a way to retreive it :(

 

SO the only solution that i beleive should work (not tested) would be to create a Region Behavior for the AnchorableSource (like we have for the DocumentsSource) and use this to add the regions.

 

Maybe Ado or someone else could see an easier solution for this?

 


Sep 7, 2012 at 6:42 AM
SlickRick wrote:

Sorry for the delay, i havent notice that someone actually asked me a question :)

I understand that the tabs gets populated correctly when you add them to the region but the Tab's Title doesnt?

At first glance i would say that the binding must be wrong.

The immediate window should show you exactly what is the binding error but im pretty sure that the error is that it is trying to bind using the View as the 'Model' item and not the ViewModel. Therefore you should change the binding to:

 <Setter Property="Title" Value="{Binding Model.DataContext.Title}"/>

So the Model would be your View, and it's DataContext would return the ViewModel. After that just use the property path to you Title property.

I hope this solves your problem.

Remember, when faced with binding issue, the immediate Window is your best friend :)

Thank you very much! I did try all kinds of variations of the binding over the last few days, but apparently not that one. Thanks again for letting us know.

Sep 7, 2012 at 8:18 AM

I agree with SlickRick that the solution might me to make a class similar to the DockingManagerDocumentsSourceSyncBehavior class. I will try later today and see how it works. 

Also, have anyone figured out how to handle view injection to a region with LayoutAnchorable, I have this code which injects a view to a region in runtime.

if (!regionManager.Regions[RegionNames.ProjectRegion].Views.Any())
                regionManager.RequestNavigate(RegionNames.ProjectRegion,
                                              new Uri("ProjectView" + parameters, UriKind.Relative), NavigationCompleted);

But I'm getting an error saying "The region manager does not contain the ProjectRegion region". 
I am doing similar view injection to a region with Docking Manager and there I have no problem finding the region. 
I have a workaround that works where I in the shells code behind have this:
 public Shell(ShellViewModel viewModel,IRegionManager regionManager)
        {
            InitializeComponent();            
            this.DataContext = viewModel;

            ProjectLayoutAnchorable.SetValue(RegionManager.RegionManagerProperty, regionManager);

And in Shell.xaml:
 <avalonDock:LayoutAnchorable x:Name="ProjectLayoutAnchorable" local:AvalonDockRegion.Name="ProjectRegion" />

So if anyone have solved viewinjection to a region with LayoutAnchorable then please share your solution!

Sep 10, 2012 at 1:57 PM
Edited Sep 10, 2012 at 2:07 PM

Hey SlickRick ,

Have you figured out how the behaviorclass would look like for LayoutAnchorable ??? Would really like some help with that.

BR

Sep 10, 2012 at 11:20 PM

Well if you want to use AnchorableSource just create the same thing as the DockingManagerDocumentsSourceSyncBehavior but use the LayoutAnchorable instead.

You might have to create a extended property for the region (like it has been done for the LayoutAnchroable) to be able to attach the AnchorableSource to the region name (since we have to specify the region on the DockinManger that already have one region specified for the DocumentSource)

Otherwise im not really sure because of the binding issue of the Title bar.

 

What approach you're trying to use?

I will try to take a look at it again tomorrow if the time allows.

Sep 11, 2012 at 7:25 AM
Edited Sep 11, 2012 at 7:26 AM
SlickRick wrote:

Well if you want to use AnchorableSource just create the same thing as the DockingManagerDocumentsSourceSyncBehavior but use the LayoutAnchorable instead.

You might have to create a extended property for the region (like it has been done for the LayoutAnchroable) to be able to attach the AnchorableSource to the region name (since we have to specify the region on the DockinManger that already have one region specified for the DocumentSource)

Otherwise im not really sure because of the binding issue of the Title bar.

 

What approach you're trying to use?

I will try to take a look at it again tomorrow if the time allows.

I'm with you on the first part, but the second part you have to explain better. Thanks

Sep 11, 2012 at 7:34 PM
Edited Sep 11, 2012 at 7:35 PM

Ok i just try one approach which works fine on my side, i'll try to describe it as much as possible.

So i have applications that display Documents and some Tools using prism.

Imagine that you have the following layout: LeftTools | Documents | RightTools defined by the following XAML:

<ad:LayoutRoot>
<ad:LayoutPanel Orientation="Horizontal">
<ad:LayoutAnchorablePaneGroup DockWidth="450">
<ad:LayoutAnchorablePane Name="LeftToolsPane"/>
</ad:LayoutAnchorablePaneGroup>

<ad:LayoutPanel Orientation="Vertical">

<ad:LayoutDocumentPane/>

</ad:LayoutPanel>

<ad:LayoutAnchorablePaneGroup DockWidth="450">
<ad:LayoutAnchorablePane Name="RightToolsPane"/>
</ad:LayoutAnchorablePaneGroup>

</ad:LayoutPanel>
</ad:LayoutRoot> 

So for Document part, I assume you already know how to define your region; We have the DockingManagerDocumentsSourceSyncBehavior Class that define the RegionBehavior when a new view gets added to the region, and this behavior specify how to handle the active view.

 

Now for connecting the Anchorable the approach is the same. We will define a RegionBehavior for it:

Imports System

Imports System.Collections.Generic

Imports System.Linq

Imports System.Text

Imports Microsoft.Practices.Prism.Regions

Imports Microsoft.Practices.Prism.Regions.Behaviors

Imports AvalonDock

Imports System.Windows

Imports System.Collections.Specialized

Imports System.Collections.ObjectModel

Imports System.Windows.Data

 

Namespace MyTest

   Class DockingManagerAnchorablesSourceSyncBehavior

       Inherits RegionBehavior

       Implements IHostAwareRegionBehavior

 

       Public Shared ReadOnly BehaviorKey As String = "DockingManagerAnchorablesSourceSyncBehavior"

       Private _updatingActiveViewsInManagerActiveContentChanged As Boolean

       Private _dockingManager As DockingManager

 

       Public Property HostControl() As DependencyObject Implements Microsoft.Practices.Prism.Regions.Behaviors.IHostAwareRegionBehavior.HostControl

            Get

               Return Me._dockingManager

           End Get

 

           Set(value As DependencyObject)

               Me._dockingManager = TryCast(value, DockingManager)

           End Set

       End Property

 

       Private _anchorables As New ObservableCollection(Of Object)()

       Private _readonlyAnchorablesList As ReadOnlyObservableCollection(Of Object) = Nothing

       Public ReadOnly Property Anchorables() As ReadOnlyObservableCollection(Of Object)

           Get

               If _readonlyAnchorablesList Is Nothing Then

                   _readonlyAnchorablesList = New ReadOnlyObservableCollection(Of Object)(_anchorables)

               End If

 

               Return _readonlyAnchorablesList

           End Get

       End Property

 

 

       ''' <summary>

       ''' Starts to monitor the <see cref="IRegion"/> to keep it in synch with the items of the <see cref="HostControl"/>.

       ''' </summary>

       Protected Overrides Sub OnAttach()

           Dim itemsSourceIsSet As Boolean = Me._dockingManager.AnchorablesSource IsNot Nothing

 

           If itemsSourceIsSet Then

               Throw New InvalidOperationException()

           End If

 

           Me.SynchronizeItems()

 

           AddHandler Me._dockingManager.ActiveContentChanged, AddressOf Me.ManagerActiveContentChanged

           AddHandler Me.Region.ActiveViews.CollectionChanged, AddressOf Me.ActiveViews_CollectionChanged

           AddHandler Me.Region.Views.CollectionChanged, AddressOf Me.Views_CollectionChanged

       End Sub

 

       Private Sub Views_CollectionChanged(sender As Object, e As NotifyCollectionChangedEventArgs)

           If e.Action = NotifyCollectionChangedAction.Add Then

               Dim startIndex As Integer = e.NewStartingIndex

               For Each newItem As Object In e.NewItems

 

                   _anchorables.Insert(startIndex, newItem)

 

                   startIndex += 1

               Next

           ElseIf e.Action = NotifyCollectionChangedAction.Remove Then

               For Each oldItem As Object In e.OldItems

                   _anchorables.Remove(oldItem)

               Next

           End If

       End Sub

 

       Private Sub SynchronizeItems()

           BindingOperations.SetBinding(_dockingManager, DockingManager.AnchorablesSourceProperty, New Binding("Anchorables") With { _

                                         .Source = Me})

 

           For Each view As Object In Me.Region.Views

               _anchorables.Add(view)

           Next

       End Sub

 

 

       Private Sub ActiveViews_CollectionChanged(sender As Object, e As NotifyCollectionChangedEventArgs)

           If Me._updatingActiveViewsInManagerActiveContentChanged Then

               Return

           End If

 

           If e.Action = NotifyCollectionChangedAction.Add Then

               If Me._dockingManager.ActiveContent IsNot Nothing _

                   AndAlso Not Me._dockingManager.ActiveContent.Equals(e.NewItems(0)) _

                   AndAlso Me.Region.ActiveViews.Contains(Me._dockingManager.ActiveContent) Then

 

                   Me.Region.Deactivate(Me._dockingManager.ActiveContent)

               End If

 

               Me._dockingManager.ActiveContent = e.NewItems(0)

           ElseIf e.Action = NotifyCollectionChangedAction.Remove AndAlso e.OldItems.Contains(Me._dockingManager.ActiveContent) Then

               Me._dockingManager.ActiveContent = Nothing

           End If

       End Sub

 

       Private Sub ManagerActiveContentChanged(sender As Object, e As EventArgs)

           Try

               Me._updatingActiveViewsInManagerActiveContentChanged = True

 

               If _dockingManager Is sender Then

                   Dim activeContent As Object = _dockingManager.ActiveContent

 

                   ' If TypeOf activeContent Is ProjectInstance.Views.ProjectInstanceView Then

                   For Each item In Me.Region.ActiveViews.Where(Function(it) Not it.Equals(activeContent))

                        If Me.Region.Views.Contains(item) Then

                           Me.Region.Deactivate(item)

                       End If

                   Next

 

 

                   If Me.Region.Views.Contains(activeContent) AndAlso Not Me.Region.ActiveViews.Contains(activeContent) Then

                       Me.Region.Activate(activeContent)

                   End If

                   'd If

               End If

           Finally

               Me._updatingActiveViewsInManagerActiveContentChanged = False

           End Try

       End Sub

 

   End Class

End Namespace

 

We also need to update the DockingManagerRegionAdapter to attach the right behavior to the region. This is necessary because of the fact that we will define two regions on the DockingManager.

Updated Code:

Class DockingManagerRegionAdapter

       Inherits RegionAdapterBase(Of DockingManager)

       Public Sub New(regionBehaviorFactory As IRegionBehaviorFactory)

           MyBase.New(regionBehaviorFactory)

       End Sub

 

       Protected Overrides Sub Adapt(region As IRegion, regionTarget As DockingManager)

 

       End Sub

 

       Protected Overrides Function CreateRegion() As IRegion

           Return New Region()

       End Function

 

       Protected Overrides Sub AttachBehaviors(region As IRegion, regionTarget As DockingManager)

           If region Is Nothing Then

               Throw New System.ArgumentNullException("region")

           End If

 

           If region.Name = “ToolsRegion” Then

               ' Add the behavior that syncs the items source items with the rest of the items

               region.Behaviors.Add(DockingManagerDocumentsSourceSyncBehavior.BehaviorKey, New DockingManagerAnchorablesSourceSyncBehavior() With { _

                                     .HostControl = regionTarget})

           ElseIf region.Name = “DocumentsRegion”

               ' Add the behavior that syncs the items source items with the rest of the items

               region.Behaviors.Add(DockingManagerDocumentsSourceSyncBehavior.BehaviorKey, New DockingManagerDocumentsSourceSyncBehavior() With { _

                                     .HostControl = regionTarget})

           End If

 

           MyBase.AttachBehaviors(region, regionTarget)

       End Sub

   End Class

 

So how to define the Two regions on the DockingManager in the XAML? We will need to create an Attached Property that looks like this:

Imports AvalonDock

Imports Microsoft.Practices.ServiceLocation

Imports Microsoft.Practices.Prism.Regions

Imports Microsoft.Practices.Prism.Regions.Behaviors

 

Namespace MyTest

   Public Class AnchorablesSourceRegion

       Inherits DependencyObject

 

#Region "Name"

 

       ''' <summary>

       ''' Name Attached Dependency Property

       ''' </summary>

       Public Shared ReadOnly NameProperty As DependencyProperty = _

           DependencyProperty.RegisterAttached("Name", GetType(String), GetType(AnchorablesSourceRegion), _

                                               New FrameworkPropertyMetadata(DirectCast(Nothing, String), _

                                                                             New PropertyChangedCallback(AddressOf OnNameChanged)))

 

       ''' <summary>

       ''' Gets the Name property. This dependency property

       ''' indicates the region name of the layout item.

       ''' </summary>

       Public Shared Function GetName(d As DependencyObject) As String

           Return DirectCast(d.GetValue(NameProperty), String)

       End Function

 

       ''' <summary>

       ''' Sets the Name property. This dependency property

       ''' indicates the region name of the layout item.

       ''' </summary>

       Public Shared Sub SetName(d As DependencyObject, value As String)

           d.SetValue(NameProperty, value)

       End Sub

 

       ''' <summary>

       ''' Handles changes to the Name property.

       ''' </summary>

       Private Shared Sub OnNameChanged(s As DependencyObject, e As DependencyPropertyChangedEventArgs)

           CreateRegion(DirectCast(s, DockingManager), DirectCast(e.NewValue, String))

       End Sub

 

#End Region

 

       Private Shared Sub CreateRegion(element As DockingManager, regionName As String)

           If element Is Nothing Then

               Throw New ArgumentNullException("element")

           End If

 

           'If I'm in design mode the main window is not set

           If Application.Current Is Nothing OrElse Application.Current.MainWindow Is Nothing Then

               Return

           End If

 

           Try

               If ServiceLocator.Current Is Nothing Then

                   Return

              End If

 

               ' Build the region

               Dim mappings = ServiceLocator.Current.GetInstance(Of RegionAdapterMappings)()

               If mappings Is Nothing Then

                   Return

               End If

               Dim regionAdapter As IRegionAdapter = mappings.GetMapping(element.[GetType]())

               If regionAdapter Is Nothing Then

                   Return

               End If

 

               regionAdapter.Initialize(element, regionName)

           Catch ex As Exception

               Throw New RegionCreationException(String.Format("Unable to create region {0}", regionName), ex)

           End Try

 

       End Sub

   End Class

End Namespace

 

And now you’ll be able to define your regions in XAML like so:

<ad:DockingManager x:Name="dockManager"

                   prism:RegionManager.RegionName="DocumentsRegion"

                   local:AnchorablesSourceRegion.Name="ToolsRegion">

 

Next Step: How to tell AvalonDock in which Tool pane (left or right) to put the Views that are added to the AnchorablesSource?

To do this, we must define a LayoutUpdateStrategy that will do a logical choice on which pane to add the view:


 

Imports AvalonDock.Layout

 

Namespace MyTest

   Public Class AvalonLayoutInitializer

       Implements AvalonDock.Layout.ILayoutUpdateStrategy

 

       Public Function BeforeInsertAnchorable(layout As AvalonDock.Layout.LayoutRoot, anchorableToShow As AvalonDock.Layout.LayoutAnchorable, destinationContainer As AvalonDock.Layout.ILayoutContainer) As Boolean Implements AvalonDock.Layout.ILayoutUpdateStrategy.BeforeInsertAnchorable

 

           'AD wants to add the anchorable into destinationContainer

           'just for test provide a new anchorablepane

           'if the pane is floating let the manager go ahead

           Dim destPane As LayoutAnchorablePane = TryCast(destinationContainer, LayoutAnchorablePane)

           If destinationContainer IsNot Nothing AndAlso destinationContainer.FindParent(Of LayoutFloatingWindow)() IsNot Nothing Then

               Return False

           End If

 

           Dim destPaneName = String.Empty

           If TypeOf anchorableToShow.Content Is SomeTypeForLeft Then

               destPaneName = "LeftToolsPane"

           ElseIf TypeOf anchorableToShow.Content Is SomeTypeForRight Then

               destPaneName = "RightToolsPane"

           Else

               Return False

           End If

 

           Dim toolsPane = layout.Descendents().OfType(Of LayoutAnchorablePane)().FirstOrDefault(Function(d) d.Name = destPaneName)

           If toolsPane IsNot Nothing Then

               toolsPane.Children.Add(anchorableToShow)

               Return True

           End If

 

           Return False

 

       End Function

 

       Public Sub AfterInsertAnchorable(layout As AvalonDock.Layout.LayoutRoot, anchorableShown As AvalonDock.Layout.LayoutAnchorable) Implements AvalonDock.Layout.ILayoutUpdateStrategy.AfterInsertAnchorable

 

       End Sub

 

       Public Function BeforeInsertDocument(layout As AvalonDock.Layout.LayoutRoot, anchorableToShow As AvalonDock.Layout.LayoutDocument, destinationContainer As AvalonDock.Layout.ILayoutContainer) As Boolean Implements AvalonDock.Layout.ILayoutUpdateStrategy.BeforeInsertDocument

           Return False

       End Function

 

       Public Sub AfterInsertDocument(layout As AvalonDock.Layout.LayoutRoot, anchorableShown As AvalonDock.Layout.LayoutDocument) Implements AvalonDock.Layout.ILayoutUpdateStrategy.AfterInsertDocument

 

       End Sub

   End Class

End Namespace

 

 

So now all you’ll have to do is to add the LayoutUpdateStrategy to the DockingManager:

<ad:DockingManager.LayoutUpdateStrategy>

   <local:AvalonLayoutInitializer/>

</ad:DockingManager.LayoutUpdateStrategy>

 

So the Final XAML should look like this:

<ad:DockingManager x:Name="dockManager"

                   prism:RegionManager.RegionName="DocumentsRegion"

                   local:AnchorablesSourceRegion.Name="ToolsRegion">

 

<ad:DockingManager.LayoutItemContainerStyleSelector>

   <local:PanesStyleSelector>

       <local:PanesStyleSelector.DocumentInstanceStyle>

           <Style TargetType="{x:Type ad:LayoutItem}">

               <Setter Property="Title" Value="{Binding Model.DataContext.TabModel.Title}"/>

               <Setter Property="CloseCommand" Value="{Binding Model.DataContext.CloseProjectCommand}"/>

           </Style>

       </local:PanesStyleSelector.DocumentInstanceStyle >

 

       <local:PanesStyleSelector.ToolPanelStyle>

           <Style TargetType="{x:Type ad:LayoutAnchorableItem}">

               <Setter Property="Title" Value="{Binding Model.DataContext.Title}"/>

               <Setter Property="IconSource" Value="{Binding Model.DataContext.IconSource}"/>

               <Setter Property="Visibility" Value="{Binding Model.DataContext.IsVisible, Mode=TwoWay, Converter={StaticResource BoolToVisibilityConverter}, ConverterParameter={x:Static Visibility.Hidden}}"/>

               <Setter Property="ContentId" Value="{Binding Model.DataContext.ContentId}"/>

               <Setter Property="IsSelected" Value="{Binding Model.DataContext.IsSelected, Mode=TwoWay}"/>

               <Setter Property="IsActive" Value="{Binding Model.DataContext.IsActive, Mode=TwoWay}"/>

           </Style>

       </local:PanesStyleSelector.ToolPanelStyle>

   </local:PanesStyleSelector>

</ad:DockingManager.LayoutItemContainerStyleSelector>

 

<ad:DockingManager.LayoutUpdateStrategy>

   <local:AvalonLayoutInitializer/>

</ad:DockingManager.LayoutUpdateStrategy>

 

<ad:LayoutRoot>
<ad:LayoutPanel Orientation="Horizontal">
<ad:LayoutAnchorablePaneGroup DockWidth="450">
<ad:LayoutAnchorablePane Name="LeftToolsPane"/>
</ad:LayoutAnchorablePaneGroup>

<ad:LayoutPanel Orientation="Vertical">

<ad:LayoutDocumentPane/>

</ad:LayoutPanel>

<ad:LayoutAnchorablePaneGroup DockWidth="450">
<ad:LayoutAnchorablePane Name="RightToolsPane"/>
</ad:LayoutAnchorablePaneGroup>

</ad:LayoutPanel>
</ad:LayoutRoot>

</ad:DockingManager>

 

 

It is highly possible that a simpler way is possible, but this what I did with the little time a have J

Also the posted code is an adaptation of what I did so it is possible that I introduced a typo or bugs when trying to simplify it.

 

Sep 12, 2012 at 9:26 PM

Thank you!, I will try this and get back to you.

Sep 17, 2012 at 1:22 PM

Does anyone else have a problem when you close a tab and try to navigate to the view again? To my understanding, this should open the tab again, but nothing happens in my case. Any ideas?

Sep 17, 2012 at 7:25 PM

I may be wrong, but doesn't navigation is used to activate (make visible) a view already added to a region?

By closing the tab, a piece of code will execute (the Views_CollectionChanged method in the DockingManagerSyncBehavior) to remove the underlying view from the region collection of views.

Therefore when you try to navigate (activate) the view, it is no longer found loaded in the region and therefore nothings happens.

Im not a prism expert, but i believe that you're using the wrong approach for this.  You should probably just add the view to the region again in order to trigger the DocumentManagerSync behavior to add the view to the region.

 

Sep 20, 2012 at 10:30 AM
SlickRick wrote:

I may be wrong, but doesn't navigation is used to activate (make visible) a view already added to a region?

By closing the tab, a piece of code will execute (the Views_CollectionChanged method in the DockingManagerSyncBehavior) to remove the underlying view from the region collection of views.

Therefore when you try to navigate (activate) the view, it is no longer found loaded in the region and therefore nothings happens.

Im not a prism expert, but i believe that you're using the wrong approach for this.  You should probably just add the view to the region again in order to trigger the DocumentManagerSync behavior to add the view to the region.


You might be correct. I will try to describe my problem more detailed, because I might have located the problem. Note that I am far from an Prism experct - I'm trying to learn it here, so I might be doing something fundamentally wrong :-)

My views are registered as described in the Prism manual:

_container.RegisterType<object, ListCustomersView>("ListCustomersView");
_container.RegisterType<object, CustomerDetailsView>("CustomerDetailsView");

ListCustomersView is displayed in a tab in a LayoutDocumentPane, using your DockingManagerRegionAdapter. When a customer is double clicked, a command is executed in my view model, requesting navigation:

_regionManager.RequestNavigate(RegionNames.Workspace, "CustomerDetailsView");

This activates and displays the CustomerDetailsView in a new tab. Obviously it also creates the underlying CustomerDetailsViewModel. So far so good. This ViewModel implements the INavigationAware interface because if the user activates the ListCustomersView and selects the same customer, the tab which is already loaded gets activated again. Otherwise a new tab would be created and you would end up with multiple tabs displaying the same customer details. So INavigationAware.IsNavigationTarget returns true for the CustomerDetailsViewModel if the requested customer is the same.

The problem is, when I close the view, the IsNavigationTarget in the "closed" CustomerDetailsViewModel gets called and thus IsNavigationTarget returns true. From what I can read from the Navigation Pipeline, Prism then tries to locate the view in the container in order to activate it. But because it was closed, it no longer exists and thus nothing happens (no view shows up). It's like the CustomerDetailsViewModel doesn't get disposed when the view is closed. When the View is closed, no view model is supposed to exist (to my understanding that is), instead a new one should be instantiated when the view is navigated to again, which would result in the IsNavigationTarget returning false, forcing the container to instantiate a new view and activate it.

Am I correct in my assumptions, and do you have any idea how to fix it? :-)

Sep 21, 2012 at 4:20 PM

Well like you said, you probably need to dispose of your viewmodel when the tab gets closed. There is probably some handler of a some kind of reference that is keeping it alive and therefore since it's always alive the IsNavigationTarget property still return true.

Try implementing IDisposable on you ViewModel, can call dispose in the DockingManagerSyncBehavior when the view gets removed from the collection.

Also when disposing, set a flag 'IsDisposed' to true and in the IsNavigationTarget property return False if this flag is true.

Also when disposing the view/viewmodel, make sure to remove any handler on events, unregister any prism composite (global) commands and to be safe unsubscribe to any prism event (even if they are weak referenced, this is to be safe).

I have not played much with navigation but this is my assumption.

 

Sep 21, 2012 at 10:31 PM

Hey,

I can't figure out which events gets called when closing a LayoutAnchorablePane , I thought it would be closedevent in LayoutAnchorable but is not that either is it ActiveContentChanged . I'm not connecting my LayoutAnchorables to AnchorableSource so the AnchorableSource changing events is of no use. Please help as I need to capture the closingevent

Sep 22, 2012 at 11:36 PM

I tried creating a RegionAdapter around LayoutAnchorable: turns out a Region must be a FrameworkElement.  Otherwise the region never gets added to RegionManager.  It appears LayoutAnchorable isn't derived from FrameworkElement, so I'm not sure how it would work as a region, either.

 

Sep 24, 2012 at 7:05 PM
Edited Sep 24, 2012 at 7:05 PM
Farre82 wrote:

Hey,

I can't figure out which events gets called when closing a LayoutAnchorablePane , I thought it would be closedevent in LayoutAnchorable but is not that either is it ActiveContentChanged . I'm not connecting my LayoutAnchorables to AnchorableSource so the AnchorableSource changing events is of no use. Please help as I need to capture the closingevent


@Farre82: If you're using the AnchorableSourceSyncBehavior, you can be aware of when a LayoutAnchorablePane gets removed at:

  Private Sub Views_CollectionChanged(sender As Object, e As NotifyCollectionChangedEventArgs)
            If e.Action = NotifyCollectionChangedAction.Add Then
                Dim startIndex As Integer = e.NewStartingIndex
                For Each newItem As Object In e.NewItems

                    _anchorables.Insert(startIndex, newItem)

                    startIndex += 1
                Next
            ElseIf e.Action = NotifyCollectionChangedAction.Remove Then
                For Each oldItem As Object In e.OldItems
                    _anchorables.Remove(oldItem)      <---------LayoutAnchorablePane get's removed
                Next
            End If
        End Sub

But if you're not using prism or if you really want to hook on some event in LayoutAnchorablePane, then im not sure. In this case you might want to try to do a search first and post on another discussion is you didn't find any related post.

Sep 24, 2012 at 7:12 PM
mrfichtn wrote:

I tried creating a RegionAdapter around LayoutAnchorable: turns out a Region must be a FrameworkElement.  Otherwise the region never gets added to RegionManager.  It appears LayoutAnchorable isn't derived from FrameworkElement, so I'm not sure how it would work as a region, either.

 


Have you tried out the example that Ado posted? (here)

You need to create a Attached Property for the LayoutAnchorable in order to be able to specify the region's name and also you need a RegionAdapter for it.

 

Sep 24, 2012 at 9:16 PM

So, if I understand you correctly, one has to replace the RegionManager.RegionName property with a LayoutAnchorable.RegionName Property?  Following the PRISM code in its RegionName dependency property, the property callback calls the following method:

 

private static void CreateRegion(DependencyObject element)
        {
            IServiceLocator locator = ServiceLocator.Current;
            DelayedRegionCreationBehavior regionCreationBehavior = locator.GetInstance<DelayedRegionCreationBehavior>();
            regionCreationBehavior.TargetElement = element;
            regionCreationBehavior.Attach();
        }

So, we'd have to rewrite that as well as the DelayedRegionCreationBehavior object that tries to create the Region on the FrameworkElement.Loaded event:

class DelayedRegionCreationBehavior
{
        public void Attach()
        {
            this.RegionManagerAccessor.UpdatingRegions += this.OnUpdatingRegions;
            this.WireUpTargetElement();
        }

        private void WireUpTargetElement()
        {
            FrameworkElement element = this.TargetElement as FrameworkElement;
            if (element != null)
            {
                element.Loaded += this.ElementLoaded;
            }
        }

        private void ElementLoaded(object sender, RoutedEventArgs e)
        {
            this.UnWireTargetElement();
            this.TryCreateRegion();
        }
}

 

Sep 26, 2012 at 5:09 AM

@slickrick I implemented your sample code for the AnchorablesrSourceSyncBehaviour , it works great . thank you.

However I have a a couple problems , if I set CanClose property to False for a LayoutAnchorableItem it does not seem to make a difference , that is the x is still in the tool title window and I can still click on it and close the Tool.

Assuming I have to live with that, I am unable to find a way to intercept this, is there a specific event I can cancel ?

I do not want Avalon dock itself to close this tool window, it is supposed to be handled by CollectionChanged in AnchorablesrSourceSyncBehaviour  when the back end removes the view from the region.  

 

Thanks

Sep 26, 2012 at 12:54 PM

Thank you !, I figured that out too.

Sep 26, 2012 at 12:58 PM
SlickRick wrote:
Farre82 wrote:

Hey,

I can't figure out which events gets called when closing a LayoutAnchorablePane , I thought it would be closedevent in LayoutAnchorable but is not that either is it ActiveContentChanged . I'm not connecting my LayoutAnchorables to AnchorableSource so the AnchorableSource changing events is of no use. Please help as I need to capture the closingevent


@Farre82: If you're using the AnchorableSourceSyncBehavior, you can be aware of when a LayoutAnchorablePane gets removed at:

  Private Sub Views_CollectionChanged(sender As Object, e As NotifyCollectionChangedEventArgs)
            If e.Action = NotifyCollectionChangedAction.Add Then
                Dim startIndex As Integer = e.NewStartingIndex
                For Each newItem As Object In e.NewItems

                    _anchorables.Insert(startIndex, newItem)

                    startIndex += 1
                Next
            ElseIf e.Action = NotifyCollectionChangedAction.Remove Then
                For Each oldItem As Object In e.OldItems
                    _anchorables.Remove(oldItem)      <---------LayoutAnchorablePane get's removed
                Next
            End If
        End Sub

But if you're not using prism or if you really want to hook on some event in LayoutAnchorablePane, then im not sure. In this case you might want to try to do a search first and post on another discussion is you didn't find any related post.

 

Thank you !, I figured that out too.

Sep 26, 2012 at 1:03 PM
adospace wrote:

Hi all,
as I see that there is a large interest in PRISM integration I took some time to build a sample project that shows a possible way to use AD as region for PRISM views. First of all, I want to let you know that absolutely I'm not a PRISM expert so many of the ideas that you can find in the sample project I build could simply not applicable in you scenario.

Anyway this thread can be used to share implementation samples that can be useful for many of us challenging with this integration.

I've build two region adapter one for the LayoutAnchorble and one for the DockingManager. the second use internally a behavior.

Download the project sample here.

Thanks,

Ado

When you save your Layout settings, Prism regionName is not included, is anyone else aware if this ??? @adospace, @SlickRick

Sep 26, 2012 at 4:12 PM
doublesman wrote:

@slickrick I implemented your sample code for the AnchorablesrSourceSyncBehaviour , it works great . thank you.

However I have a a couple problems , if I set CanClose property to False for a LayoutAnchorableItem it does not seem to make a difference , that is the x is still in the tool title window and I can still click on it and close the Tool.

Assuming I have to live with that, I am unable to find a way to intercept this, is there a specific event I can cancel ?

I do not want Avalon dock itself to close this tool window, it is supposed to be handled by CollectionChanged in AnchorablesrSourceSyncBehaviour  when the back end removes the view from the region.  

 

Thanks

I figured this out, I was setting the wrong item to get rid of the X in LayoutAnchorableItem, to remove it you need to set CanHide = false. 

Sep 26, 2012 at 8:52 PM

@Farre82:

It doesn't matter. I believe AD export only the information required for it to be able to load the saved layout (windows positions/size/dock/etc...).

Once loaded it doesn't overwrite the layout that was defining in the XAML, it only update it with the saved layout.

You may have notice that you don't see any binding expression or any content information about a given panel.

So when the layout get's loaded the regions should still be attached to your panel. At least it is for me :)

 

 

Sep 28, 2012 at 8:58 AM
SlickRick wrote:

Well like you said, you probably need to dispose of your viewmodel when the tab gets closed. There is probably some handler of a some kind of reference that is keeping it alive and therefore since it's always alive the IsNavigationTarget property still return true.

Try implementing IDisposable on you ViewModel, can call dispose in the DockingManagerSyncBehavior when the view gets removed from the collection.

Also when disposing, set a flag 'IsDisposed' to true and in the IsNavigationTarget property return False if this flag is true.

Also when disposing the view/viewmodel, make sure to remove any handler on events, unregister any prism composite (global) commands and to be safe unsubscribe to any prism event (even if they are weak referenced, this is to be safe).

I have not played much with navigation but this is my assumption.

I'm still struggling with this.

When I close a tab, I believe that it should be deactivated and removed from the region (default PRISM behavior is to keep a reference to it). Using DockingManagerDocumentsSourceSyncBehavior also keeps having a reference to a closed tab, because it never gets removed from the Region.Views collection, and neither does it from the _documents collection. It's default PRISM behavior and it's probaly okay.

In order to make PRISM remove it's reference to the view/viewModel, one should implement IRegionMemberLifetime and make KeepAlive return false. When I do this DockingManagerDocumentsSourceSyncBehavior never displays the view. I can't seem to figure out why this is. I've been debugging for hours without luck. Do you have any idea what is causing this behavior?

Sep 28, 2012 at 5:25 PM

@thj

Well this is a good example of why im not a the Prism reference, i was not aware of the IRegionMemberLifetime :). I guess that i've stop reading the guide when my application was working  :)

The approach i took, which might not be the right pattern, was to create a controller for my project instance which wait for new project request.

You will see in my sample code that i manage myself when i view get's removed from the region. But i will surely take a look at the IRegionMemberLifetime implementation and see if i can follow more the 'good' proposed practices.

My implementation look like this:

  ''' <summary>
    ''' This is the project instance controller that is runned when the module is loaded.
    ''' It will listen for open new project instance events.
    ''' </summary>
    Public Class ProjectInstanceController
        Implements IProjectInstanceController

        Private Shared count As Integer = 1

        Private eventAggregator As IEventAggregator
        Private ReadOnly container As IUnityContainer
        Private rm As IRegionManager

        ''' <summary>
        ''' The controller constructor.
        ''' </summary>
        ''' <param name="container"></param>
        ''' <param name="aggr">A container for event. It is used to subscribe/publish events accross the application in a decouple way.</param>
        ''' <param name="rm">A container for regions. It is used register views to given regions.</param>
        ''' <remarks></remarks>
        Public Sub New(container As IUnityContainer, aggr As IEventAggregator, rm As IRegionManager)
            Me.container = container
            Me.eventAggregator = aggr
            Me.rm = rm

            'region = rm.Regions(Infrastructure.RegionNames.ProjectInstanceRegion)
        End Sub

        ''' <summary>
        ''' This method is called upon the creation of the controller.
        ''' It will hook a listening on the <see cref="OpenNewProjectRequest"/> in order to be notified when a open new project request comes in.
        ''' </summary>
        ''' <remarks></remarks>
        Public Sub run() Implements IProjectInstanceController.run
            Me.eventAggregator.GetEvent(Of OpenNewProjectRequest)().Subscribe(AddressOf OpenNewProject)
        End Sub


        ''' <summary>
        ''' This method is called when a open new project request comes in.
        ''' It will create a new Project instance and display it to the user.
        ''' </summary>
        ''' <param name="projectDetails">A <see cref="Infrastructure.ProjectInstanceDetails"/> that contains all the required information and events about this project's instance.</param>
        Public Sub OpenNewProject(ByVal projectDetails As Infrastructure.ProjectInstanceDetails)
            Dim [module] = New Views.ProjectInstanceView(projectDetails)
            '  Dim [module] = container.Resolve(Of Views.ProjectInstanceView)()
            ''  [module].HeaderInfo = "View " + count
            'DirectCast([module].DataContext, ViewModels.ProjectInstanceViewModel).ProjectDetails = projectDetails

            count += 1

            If rm IsNot Nothing AndAlso rm.Regions.ContainsRegionWithName(Infrastructure.RegionNames.ProjectInstanceRegion) Then
                rm.Regions(Infrastructure.RegionNames.ProjectInstanceRegion).Add([module])
                Workspace.Instance.AddProjectToOpenedProjectList(projectDetails)

                AddHandler DirectCast([module].DataContext, ViewModels.ProjectInstanceViewModel).CloseTabRequest, AddressOf RemoveView
            Else
                Throw New KeyNotFoundException(ResourceHelper.GetProjectInstanceString("ProjectInstanceController_OpenNewProject_ExceptionRegionNotFound"))
            End If

            AddHandler [module].Unloaded, Sub() count -= 1
        End Sub

        Private Sub RemoveView(ByVal sender As Object)
            If TypeOf sender Is ViewModels.ProjectInstanceViewModel Then
                RemoveHandler DirectCast(sender, ViewModels.ProjectInstanceViewModel).CloseTabRequest, AddressOf RemoveView
            End If

            If rm.Regions(Infrastructure.RegionNames.ProjectInstanceRegion).Views.Contains(sender) Then
                rm.Regions(Infrastructure.RegionNames.ProjectInstanceRegion).Remove(sender)
            End If

            For Each vw In rm.Regions(Infrastructure.RegionNames.ProjectInstanceRegion).Views
                If TypeOf vw Is Views.ProjectInstanceView AndAlso DirectCast(vw, Views.ProjectInstanceView).DataContext.Equals(sender) Then
                    rm.Regions(Infrastructure.RegionNames.ProjectInstanceRegion).Deactivate(vw)
                    rm.Regions(Infrastructure.RegionNames.ProjectInstanceRegion).Remove(vw)
                End If
            Next
        End Sub
    End Class
Oct 9, 2012 at 3:48 PM

There is a PRISM implementation that uses MEF, AvalonDock,  Elysium ... in http://central.codeplex.com/

Oct 10, 2012 at 1:28 PM

SlickRick: It would be awesome if you could add IRegionMemberLifetime support. It would make it more complete I think. But you are right, the manual removal approach does work.

alfredop13: Yours have the same problem and doesn't take IRegionMemberLifetime into account.

Dec 18, 2012 at 7:51 AM

Has anyone looked more into adding support for the INavigationAware interface?

Feb 21, 2013 at 6:14 PM
Hi,

I have been working on a GitHub project to create a participatory IDE framework that uses PRISM, AvalonDock, MahApps Metro etc. Please feel free to look at Wide IDE - comment, participate and contribute to the project if you have time.

Image

Thanks
Chandra
Jul 30, 2013 at 9:32 PM
Edited Jul 30, 2013 at 9:33 PM
@SlickRick, @adospace

Thanx guys, you have really helped me getting to grips with with PRISM & Avalondock!

One question for SlickRick.

In your AvalonLayoutInitializer,
the following line always returns null:

dynamic toolsPane = layout.Descendents().OfType<LayoutAnchorablePane>().FirstOrDefault(d => d.Name == destPaneName);

thus, thus my tool panel always gets docked to the default side (right)

Is there an error in my implementation, or why is it not finding the correct pane? All my names are right, if you iterate through the layout.Descendents(), you actually do not get a single LayoutAnchorablePane. (They are defined in my XAML as per your example)

eg
        <ADLayout:LayoutRoot>
            <ADLayout:LayoutPanel Orientation="Horizontal">
                <ADLayout:LayoutAnchorablePaneGroup DockWidth="250">
                    <ADLayout:LayoutAnchorablePane Name="LeftDockingPane"/>
                </ADLayout:LayoutAnchorablePaneGroup>
                <ADLayout:LayoutDocumentPane/>
                <ADLayout:LayoutAnchorablePaneGroup DockWidth="250">
                    <ADLayout:LayoutAnchorablePane Name="RightDockingPane"/>
                </ADLayout:LayoutAnchorablePaneGroup>
            </ADLayout:LayoutPanel>
        </ADLayout:LayoutRoot>
Thanx
Hein
Jul 31, 2013 at 11:35 AM
OK, what I've found is that AvalonDock does a Garbage Collection on empty Panes/PaneGroups etc, removing them from the DockingManager. I've done a dirty hack - adding a Persistent Property to ILayoutPane to prevent garbage collection.

Does anyone know of a proper way to do this?

Thanx
Hein
Aug 2, 2013 at 5:08 PM
Hi,

To be honest I have not used the LayoutInitializer since the time posted, so I am no longer of big help (especially with all my active projects).

Are you loading the layout at startup? Maybe the name get lost upon recreation?

Sorry for my lack of help :(
Aug 12, 2013 at 8:45 PM
Hello.

I've convert your code to c# and i have problem with views.

When the region is creating in DockingManagerRegionAdapter, the RegionManager is always null and in the IRegionManager is only 2 regions which i create via RegionManager.RegisterViewWithRegion and i need to add Views to ToolsRegion and DocumentsRegion in realtime.
So i add this to DockingManagerRegionAdapter.AttachBehaviors
regionManager.Regions.Add(region.Name, region);

base.AttachBehaviors(region, regionTarget);
Now it is work, but is there any way to avoid DataContext property n style:
<Setter Property="Title" Value="{Binding Model.DataContext.Title}"/>
Like before, when i've not use PRISM:
<Setter Property="Title" Value="{Binding Model.Title}" />