[Version 2.0] Prism Region Adapter

Jun 6, 2012 at 8:32 PM

Does anyone have sample code on how to create a Region Adapter for AvalonDock's LayoutDocumentPane and LayoutAnchorablePane?

Jun 20, 2012 at 8:49 PM

I tried to adapt my region adapter code i had in version 1 but can't get it to work sinc e LayoutDocumentPanel doesnt inherits from DependencyObject.

I don't see how to make it work... :(

Any plan on supporting PRISM adapters?

 

Thanks

Jun 25, 2012 at 11:45 PM
Edited Jun 25, 2012 at 11:46 PM

I am using prism with AvalonDock.  But I don't use prism adapters (it would be nice if they were supported, but they are not).

Here is how I do it for any who are interested:

In the example MVVM App the backing objects are the ViewModels.  Clearly if you are using prism, this will not work for you.  So instead you make objects that you can use to manage your tabs (which in many ways is what a ViewModel is).

I start with a base class.  This is what all tabs will inherit from.  It uses IYourTabInfo (I renamed this from my app specific name).  IYourAppInfo should have a URI to your icon and a RegionName property that knows the string text of the region it will show.

public abstract class TabBase
{
    public TabBase(IYourTabInfo yourTabInfo)
    {
        YourTabInfo = yourTabInfo;
        RegionContentControl = new ContentControl();
        IconSource = new Uri(yourTabInfo.SmallImagePath, UriKind.RelativeOrAbsolute);
    }
    public ContentControl RegionContentControl { getset; }
    public IOMStudioTab YourTabInfo{ getset; }
 
    public string Title { getset; }
    public bool CanClose { getset; }
    public bool CanFloat { getset; }
    public bool CanHide { getset; }
    public Uri IconSource { getset; }
}

I then make a content type (I also make a tools type, but I will not show it for brevity)

public abstract class ContentType : TabBase
{
    public ContentType(IYourTabInfo yourTabInfo) : base (yourTabInfo){}
}

Then I make the actual content I want to show (and will host my region).  This will create an instance of the region and hold it in memory.

public class YourTabNameContent : ContentType
{
    public YourTabNameContent(IYourTabInfo yourTabInfo) : base(yourTabInfo)
    {
        Title = "Your Tab Title";
 
        CanClose = false;
 
        CanFloat = false;
 
        RegionManager.SetRegionName(RegionContentControl, yourTabInfo.RegionName);
    }
}

Last this needs to be plugged in to your TemplateSelector.  This assumes that you have already made your templates in PanesTemplateSelector.cs (as per the MVVM example with the AvalonDock source code):

<avalonDock:DockingManager.LayoutItemTemplateSelector>
    <Dock:PanesTemplateSelector>
        <Dock:PanesTemplateSelector.YourTabNameTempateFromPanesTemplateSelector>
            <DataTemplate>
                <Grid>
                    <ContentControl x:Name="ContentDisplay" Content="{Binding RegionContentControl}"></ContentControl>
                </Grid>
            </DataTemplate>
        </Dock:PanesTemplateSelector.YourTabNameTemplateFromPanesTemplateSelector>
    </Dock:PanesTemplateSelector>
</avalonDock:DockingManager.LayoutItemTemplateSelector>

I hope this helps someone out.

Jul 12, 2012 at 10:54 AM

Vaccano, this is great. I had something like

<local:PanesTemplateSelector.DocumentTemplate>
                        <DataTemplate>
                            <ContentControl 
                                x:Name="DocumentRegion" 
                                prism:RegionManager.RegionName="{Binding RegionName}"/>
                        </DataTemplate>
</local:PanesTemplateSelector.DocumentTemplate>

and got my content from dynamically loaded modules hosted in the document window indeed,
but it disapperead as soon as a document window was dragged to floating state or another tab selected.
Something to do with the DelayedRegionCreationBehavior not finding the region I suppose, but reading up
on all that would have taken me a lot of time. Giving the content control within the template not a region
but another content control with a region did the trick.

Thank you for the hint.

Frank

Jul 17, 2012 at 7:11 AM

 

Hi,
I was just wondering, before fighting further with your method, if updating regions in floating windows works at all...I have implemented a very simple dockng manager with several regions which I can update without anything fancy except when the panes are floating then the regions are no longer in the manager. I also tried the adapter route that i found on stackoverflow but then when floating the windows the content became all black.I tried implementing your version but have as yet had no success finding the regions! Using the RegionManager.SetRegion( ) method did not add any regions to my actual region manager, So before i embark on the next stage of fighting I was just wondering if the requirement of updating floating regions works at all!
Appreciate any and all help!

Jul 17, 2012 at 8:25 AM

When you have added the region with 

 RegionManager.SetRegionName(RegionContentControl, yourTabInfo.RegionName);
How do you find the region again to update it? Its not in the region collection...??
Jul 17, 2012 at 2:44 PM

The problem seems to be that AvalonDock uses a DataTemplate construction for its windows, and Prism's RegionManager doesn't work with regions defined inside a data template. Looks like a data template is not part of the visual tree and therefore has its own RegionManager, and the global RegionManager knows nothing of the regions defined within data templates.

There's a good explanation here:

http://blogs.southworks.net/dcherubini/2011/11/10/regions-inside-datatemplates-in-prism-v4-using-a-region-behavior/

They have a nice looking general solution there which I haven't yet tried, but their code gave me the missing hint:

If you use Vaccano's strategy of loading the ContentControl from the view model, where a new ContentControl is being created and provided with a region name

<Dock:PanesTemplateSelector.YourTabNameTempateFromPanesTemplateSelector>
    <DataTemplate>
        <Grid>
            <ContentControl 
                x:Name="ContentDisplay" 
                Content="{Binding RegionContentControl}">
            </ContentControl>
        </Grid>
    </DataTemplate>
</Dock:PanesTemplateSelector.YourTabNameTemplateFromPanesTemplateSelector>

then you can force the use of the global RegionManager as well:

<Dock:PanesTemplateSelector.YourTabNameTempateFromPanesTemplateSelector>
    <DataTemplate>
        <Grid>
            <ContentControl 
                x:Name="ContentDisplay" 
                Content="{Binding RegionContentControl}"
                prism:RegionManager.RegionManager="{Binding RegionManager}">
            </ContentControl>
        </Grid>
    </DataTemplate>
</Dock:PanesTemplateSelector.YourTabNameTemplateFromPanesTemplateSelector>
The view model must then have a suitable property for binding (in AvalonDock.MVVMTestApp this could be in the class PaneViewModel):

public IRegionManager RegionManager
{
    get
    {
        IRegionManager regionManager =
            ServiceLocator.Current.GetInstance<IRegionManager>();
        return regionManager;
    }
}

This did the trick for me.

Jul 17, 2012 at 3:02 PM

Hmm, Thanks Frank, I'll have a look tomorrow in my next round of fighting... :-/ 

At least I am slowly understanding it...

 

And you think the updating of floating panels should generally work(?)

Jul 17, 2012 at 5:46 PM

And you think the updating of floating panels should generally work(?)

As of today, IRegionManager.RequestNavigate works for me, no matter whether the AvalonDock based target window is docked, or tabbed as document, or floating. The reason it did not work was simply that the RegionManager attached to the shell window had 0 regions, and forcing this RegionManager on the DataTemplate took care of that.

Jul 19, 2012 at 12:46 PM

Hi Frank, (OR ANYONE ELSE!)

The regions are now updating perfectly! Many thanks. From floating to floating tab docked etc. 

I have however another and new question. :-/ 

I am trying to get the layout working....

I load 9 regions at the beginning. They are all in seperate document tabs. That i naturally don't want.

I have tried to use the LayoutInitializer to set the View model types to Anchorable panes via pane name but unfortunately it doesn't go into the BeforeInsertAnchorable method.... I have got this to work following the MVVM example. But when i copy and paste that code into my prism solution nothing doing. 

Any idea if this is a prism together with avalon bug or am i just missing something...?

 

 

 

 

Jul 19, 2012 at 1:06 PM

OK solved my own problem..the LayoutInitializer  works only for the anchorable panes. Not the documentpanes... 

 

Shame shame...