I'm very lost on how to do this.
I understand MEF and can load services and classes from another XAP using the ubiquitous DeploymentCatalogService found in many blog posts. What I don't understand is how to actually load and navigate to a PAGE from another XAP.
What I'd like to do is have my main application be able to call the NavigationService and provide it with the name of a Page that should exist. Like:
NavigationService.Navigate(new Uri("/Test", UriKind.Relative));
It's up to one of the other XAP files to provide this page to the application. However, I have no idea how to make this work. It seems like everyone is building up some complicated infrastructure to handle this situation and it's very annoying and overly complicated.
Is there an easy way to do this?
Some of the examples I've seen are more complicated than they need to be, but this is not a simple task. It took me 2 days to break down the examples I could find before I really understood what is going on.
To do this you need to create minimum two classes.
A class that implements INavigationContentLoader, this will do all of the work.
A class that implements IAsyncResult. This object will passed around the INavigationContentLoader, so use it to keep track of what you are doing.
Your INavgiationContentLoader should do the following.
BeginLoad
- Test if the Uri belongs to the current XAP or a XAP that has already been loaded.
- If not, use DeploymentCatalog to download the XAP. Store the DeploymentCatalog in an AggregateCatalog.
- Create the page for the given Uri using an ExportFactory. Give yourself a property on your IAsyncResult to hold this.
- If the operation has not been cancelled, execute the callback parameter.
CanLoad
You can try to put some logic in here to test of you have access to the specified XAP, or you can just return true and be done with it.
CancelLoad
Set a state within the IAsyncResult to let you know the operation has been cancelled.
EndLoad
- Get the page that was stored in the IAsyncResult, wrap it in a LoadResult and return.
All pages that will be loaded by this INavgiationContentLoader will need to be marked with an ExportAttribute so that ExportFactory can find them.
Edit
My INavigationContentLoader
http://pastebin.com/cT1mJ4Ve
My IAsyncResult
http://pastebin.com/xHWHT4pr
ExportAttribute to use on pages. You need this on all pages, even the ones in the local XAP.
http://pastebin.com/nTJ27mWz
IExportPageMetaData. This is the contract that MEF uses.
http://pastebin.com/8fdwx2Kn
How to use:
Declare your navigation:Frame like this
<navigation:Frame x:Name="ContentFrame"
Source="/Home"`
Grid.Column="1">
<navigation:Frame.ContentLoader>
<navUtil:DynamicContentLoader />
</navigation:Frame.ContentLoader>
</navigation:Frame>
HyperlinkButton to a page from another XAP.
<HyperlinkButton Content="Page from another XAP"
NavigateUri="/NavigateUriFromExportPageAttribute"
navUtil:DynamicContentLoader.Xap="UriToOtherXap" />
HyperlinkButton to a page from this XAP.
<HyperlinkButton Content="Page from this XAP"
NavigateUri="/NavigateUriFromExportPageAttribute" />
You do not need a UriMapper, nor do you need to put the Path of the Page.xaml. MEF will read the ExportPageAttribute from the page and find the Uri that way.
Take a look to Navigating between Pages in Different Xaps (by using MEF) post on http://www.silverlightshow.net. It's exactly what you need. Source for post is also available.