Integrating Prism v4 Region Navigation with Silverlight Frame Navigation


Updated 10/14/2010

This blog post was updated on 10/14/2010 and requires Prism 4 or later.

The code download was updated and the Prismv4FrameRegionNavigation project, FrameRegionNavigationService was updated, adding support for the NavigationFailed and Navigating events.

Updated 2/9/2011

I have published a related blog post that shows how to implement cancelling of the navigation request when using the Silverlight Frame Navigation API that can be read here.

Introduction

This article covers integrating Prism v4 Region Navigation with Silverlight 4 Frame Navigation. Integration is not directly supported by the Prism v4 Library. The included download provides the required classes to integrate the two navigation API’s.

The below image pictures the demo application; notice the user friendly, deep link unmapped Uri in the address bar. The right ListBox lists an Item view was opened and subsequently navigated away from.

This included application demonstrates how to integrate Prism Region Navigation with Silverlight Frame Navigation. Additionally, we’ll examine implementing Non-Linear Navigation in this configuration.

Prerequisites

A general knowledge of Prism regions, modules, MEF, Silverlight Frame Navigation and the Silverlight UriMapper is required to understand this article and demo application.

Before proceeding, please read the Prism v4 Region Navigation Pipeline article. Information presented in that article will not be repeated here.

Integrating Navigation API’s

The demo application includes the Prismv4FrameRegionNavigation assembly. This assembly provides the required classes to integrate the two navigation API’s such as a content loader, region adapter, journal, navigation service and region behavior. For the remainder of the article, I’ll refer to the Prismv4FrameRegionNavigation assembly as, "the assembly."

<navigation:Frame 
    x:Name="ContentFrame" 
    Style="{StaticResource ContentFrameStyle}" 
    Source="/HomeView" 
    Navigated="ContentFrame_Navigated" 
    NavigationFailed="ContentFrame_NavigationFailed"
    prism:RegionManager.RegionName="MainContentRegion"
    >

    <navigation:Frame.ContentLoader>
        <prism_Regions:FrameContentLoader RegionName="MainContentRegion"/>
    </navigation:Frame.ContentLoader>

    <navigation:Frame.UriMapper>
        <uriMapper:UriMapper>
                        
         <!--Default applicaiton mapper-->
         <uriMapper:UriMapping Uri="" MappedUri="/ThePhoneCompany.Views.HomeView"/>
                       
         <!--Used to add a new record-->
         <uriMapper:UriMapping Uri="/{moduleName}/{pageName}/add" MappedUri="ThePhoneCompany.{moduleName}.Views.{pageName}?key=0"/>
                        
         <!--Used to edit a record-->
         <uriMapper:UriMapping Uri="/{moduleName}/{pageName}/{key}" MappedUri="ThePhoneCompany.{moduleName}.Views.{pageName}?key={key}"/>

         <!--Used to view a page-->
         <uriMapper:UriMapping Uri="/{moduleName}/{pageName}" MappedUri="ThePhoneCompany.{moduleName}.Views.{pageName}"/>
                        
         <!--Used to navigate to a page in the Shell-->
         <uriMapper:UriMapping Uri="/{pageName}" MappedUri="/ThePhoneCompany.Views.{pageName}"/>

        </uriMapper:UriMapper>
    </navigation:Frame.UriMapper>
</navigation:Frame>

The RegionManager.RegionName attached property is attached to the Frame control. In your past Prism projects you probably added this attached property to a ContentControl, ItemsControl or TabControl. The assembly provides the region adapter and behavior which enables the Prism Region to be attached to the Frame control.

The included FrameContentLoader is used in the above XAML snippet. This content loader is a replacement for the default Silverlight ContentLoader. Notice the required RegionName property on the FrameContentLoader that matches the RegionName in the Frame RegionManager.RegionName attached property.

The assembly classes route all Region Navigation API requests to the Frame. This was done so that all navigation requests, regardless of origin, are processed uniformly by the Frame. This also ensures that the Frame’s UriMapper can handle mapping user friendly Uri’s to application MappedUri’s.

A glance at the above UriMappings shows that this scheme supports multiple modules that can use the same UriMappings.

By default, the Region Navigation API’s use an object’s short type name to look it up in the container for creation and when iterating a region’s contents to determine if the object can handle the navigation request.

I have elected to use full type names to identify objects in the container for navigation purposes. I also did this in the Prism v4 Region Navigation Pipeline demo application.

The ThePhoneCompany.Infrastructure.FullTypeNameRegionNavigationContentLoader class is used to change the default strategy from short type names to full type names, for determining which objects in the region are candidates to be checked if they are the navigation target. This the same class used in the Prism v4 Region Navigation Pipeline demo application.

User friendly Uri’s are not only used in the address bar, but also by the application RequestNavigate method calls.  The below code snippet shows the CategoryViewModel CloseExecute method, calling RequestNavigate from code.  The InventoryHomeView string constant keeps the magic strings out of the code.

public const String InventoryHomeView = "/Inventory/InventoryView";

void CloseExecute() {
    _keepAlive = false;
    _regionManager.RequestNavigate(RegionNames.MainContentRegion, Constants.InventoryHomeView);
}

Using friendly Uri’s also allows HyperlinkButtons to initiate navigation without additional code other than setting the NavigateUri property in XAML. In the below XAML snippet from the  InventoryView.xaml ListBox DataTemplate.  You can see how simple it is to data bind the ItemID property to set the NavigateUri property.

<DataTemplate>
    <Grid Loaded="lbDataItemsGridItemTemplate_Loaded">

      ...
      
      <HyperlinkButton 
        Grid.Column="3" Margin="7,0,0,0" Content="edit" 
        NavigateUri="{Binding Path=ItemID, StringFormat=/Inventory/ItemView/\{0\}}" />
    </Grid>
</DataTemplate>

In-fact all but two navigation requests are initiated in XAML and not code. The two RequestNavigate method calls are in the ItemViewModel and CategoryViewModel CloseExecute methods.

Errors that are thrown during navigation requests will be bubbled up to the Frame and the Frame will raise the NavigationFailed event.

In addition to address bar deep linking and the Frame handling navigation errors, navigating against a Frame from XAML is a very good benefit of this implementation.

Developers can modify the Prismv4FrameRegionNavigation assembly’s classes to meet the specific needs of their applications.

Lessons Learned

Assembly Loading on Application Start Up

In Silverlight, Prism Modules can be created using one of the Visual Studio Silverlight Applications project templates or a Silverlight Class Library project template.

The reason you use one of the Silverlight Application project templates is for packing. When the Silverlight Application is compiled, it will be packed in its own XAP. Packaging the assembly in its own XAP provides the developer a number of options for when the XAP is actually downloaded and loaded into the Silverlight application domain. If your Silverlight application consists of several XAPs, your initial load time can be decreased, since you will only have to get the XAP that contains the shell download and display the shell.

The reason you use the Silverlight Class Library project template is to provide code separation and promote reuse across solutions. Another reason to use this project template type is to package the assembly with the main Silverlight application; in other words, this assembly will be part of the Silverlight application’s XAP.

So, what does this have to do with start up?

The Navigation API’s require that the container can create the target object. Meaning, if you are using MEF or an IOC container, the Navigation API will call into the container requesting the target object by name. If the container can’t create the object, the container will throw and the navigation request will stop.

If your solution is packaged in several XAPs, and the target of the navigation is in a XAP that has not been downloaded or is in the process of downloading, the container will throw an exception when the target object is requested.

Now let’s look at two real-world scenarios that could cause a problem.

Please note, these issues are not caused by using the Navigation API, but are a side-effect of the asynchronous downloading of assemblies and the timing of accessing those assemblies before they are loaded and ready for use.

  • Deep linking – the user opens their browser; enters a deep link Url and presses enter. If your application is broken down into multiple XAPs and the target of the deep link is not in the Silverlight application XAP it is possible that the user will get an error because the target XAP has not yet been downloaded.
  • Main application attempts to navigate to a target that has not yet been loaded – this can happen even if your application is not navigating using a deep link. The main application has loaded and contains a button that navigates to class in module that is still loading. If the user clicks that button, an error will occur because the target is still loading.

To solve these problems you can: 

  • Package your assemblies in the main Silverlight application XAP.
  • Intercept the navigation request, verify the assembly is loaded then proceed with the navigation request. If the assembly is not loaded or is in the process of loading, you can wait until the assembly loads, and then proceed with the request.
    • MEF Catalogs have a Changed event that you can hook to determine the status of assembly loading.

Silverlight Applications and MEF

There is a known issue when referencing the Prism MefExtensions assembly from multiple Silverlight assemblies. Your main Silverlight application should reference the assembly and set Copy Local to True.

All other assemblies that reference the Prism MefExtensions assembly must set Copy Local to False as pictured below.

If you forget to change the Copy Local property value to False, an exception will be thrown during the bootstrapping process. However, the message will indicate that you need to set Copy Local to False.

Non-Linear Navigation Implementation

In addition to Region Navigation integration, this application implements Non-Linear Navigation features similar to the Prism v4 Region Navigation Pipeline demo application.

This application demonstrates using the metadata by surfacing it in four locations as pictured below.

The Application metadata property is used to aggregate the count of the views in the region from the Inventory module. See the top inventory-(3) button text.  Each time the Frame is navigated the Navigated event is raised and this event handler is invoked.

void ContentFrame_Navigated(Object sender, System.Windows.Navigation.NavigationEventArgs e) {

. . .
    //Posting to the dispatcher because the remove item from region does not get executed before this gets called.
    //Delaying the execution by posting this, enables the Inventory Hyperlink button to have the correct count displayed.
    this.Dispatcher.BeginInvoke((Action)delegate {
        this.hlbInventory.Content = MakeLabelWithCountForApplicationSuite(Constants.Inventory, Constants.Inventory.ToLower());
    });
}

The below method is called from the above code and returns a formatted string that will be used as the Hyperlink’s content.

String MakeLabelWithCountForApplicationSuite(String applicaitonName, String labelText) {
    Int32 count = 0;

    foreach (var item in this.RegionManager.Regions[RegionNames.MainContentRegion].Views) {
        var fwe = item as FrameworkElement;
        if (fwe != null) {
            var nonLinearNavigationObject = fwe.DataContext as INonLinearNavigationObject;
            if (nonLinearNavigationObject != null && nonLinearNavigationObject.Application == applicaitonName) {
                count += 1;
            }
        }
    }

    if (count == 0)
        return labelText;
    else
        return String.Format("{0}-({1})", labelText, count);
}

The InventoryViewModel creates the text for two inventory function buttons, Item-(1) and Category-(2), see in the above screen shot. This is accomplished by matching the types in the region with the type that each of the button provides selection for.

String MakeLabelWithCountForApplicationView(Type viewType, String labelText) {
    Int32 count = 0;

    foreach(var item in _regionManager.Regions[RegionNames.MainContentRegion].Views) {
        if(item.GetType() == viewType) {
            count += 1;
        }
    }

    if(count == 0)
        return labelText;
    else
        return String.Format("{0}-({1})", labelText, count);
}

The open items for the selected inventory function are listed in the right side ListBox. This information comes from the region views metadata.  The below code snippet  checks which inventory function is active, then iterates over the views in the region looking for matching types; when found adds the metadata to the list for display in the right side ListBox.

void UpdateActiveDataItems() {
    try {
        if(ItemIsChecked)
            ActiveDataItems.Source = this.GetActiveDataItems(typeof(ItemView));
        else if(CategoryIsChecked)
            ActiveDataItems.Source = this.GetActiveDataItems(typeof(CategoryView));
    } catch(Exception) { }
}

IList<NonLinearNavigationMetadata> GetActiveDataItems(Type viewType) {
    var list = new List<NonLinearNavigationMetadata>();
    foreach(var item in _regionManager.Regions[RegionNames.MainContentRegion].Views) {
        if(item.GetType() == viewType) {
            var fwe = item as FrameworkElement;
            if(fwe != null) {
                var nonLinearNavigationObject = fwe.DataContext as INonLinearNavigationObject;
                if(nonLinearNavigationObject != null) {
                    list.Add(new NonLinearNavigationMetadata(nonLinearNavigationObject));
                }
            }
        }
    }
    return list;
}

As you can see, metadata is at the heart of implementing Non-Linear Navigation. Surfacing the metadata in creative ways makes it very easy for end users to use your software applications.

Video

The demo application video can be viewed here.

Note: You can view the high resolution version of the .wmv video file by:

  • Clicking on the video link
  • Log into Vimeo

After logging in, you’ll be given access to the high resolution video download. There is no cost associated with this.

Download and Video

The link to download the demo application is located at the bottom of this article.

Requirements: you must download Prism 4 or later and run the RegisterPrismBinaries.bat batch file. The Prism v4 Readme covers this file in detail. If you do not want to run this batch file, you’ll need to remove and re-add the references to the Prism assemblies.

Comments

Microsoft values your opinion about our products, guidance, documentation and samples.

Thank you for your feedback and have a great day,

Karl Shifflett

Patterns & Practices Prism Team

SilverlightPrismv4IntegratedNavigationUpdated-10-14-2010.zip

Comments (81)

  1. Russell says:

    Great work! Can you tell me how I can make this work with nested frame regions? For example, I have 2 frames: OuterFrame and InnerFrame. How can I make a deep link load OuterView2 and InnerView5 in their respective frames?

    In my app, my Shell has a MasterRegion where I first load my LoginView and RegistrationView. After the user logs in, I replace the LoginView with a LayoutView. My LayoutView contains several regions that switch between views.

  2. RussellLee04 says:

    Great work! Can you tell me how I can make this work with nested frame regions? For example, I have 2 frames: OuterFrame and InnerFrame. How can I make a deep link load OuterView2 and InnerView5 in their respective frames?

    In my app, my Shell has a MasterRegion where I first load my LoginView and RegistrationView. After the user logs in, I replace the LoginView with a LayoutView. My LayoutView contains several regions that switch between views.

  3. karl140.6 says:

    Hi Russell,

    Sorry for the delayed response, I've been out of town.

    You have several options.

    1.  If the top level Uri contains the information necessary for the inner frame to navigate, your parent form can parse the top level Uri, then initiate a second navigation on the InnerFrame.

    2.  If the top level Uri does not contain the information necessary for the inner frame to navigate, your parent form can simply initiate a second navigation on the InnerFrame to a default Uri or possibly the last location, provided you store that information.

    Does this help?

    Cheers,

    Karl

  4. RussellLee04 says:

    It turns out that option 1 is how I ended up solving my situation.

    Thanks for the reply, though. At least I know there isn't a more elegant way of implementing my scenario.

  5. derekchan2k says:

    Hi Karl, I've been trying out this sample and would like to use it for our upcoming Silverlight project.

    However, we got stuck when trying to load xap files dynamically. Can this project be modified to do that? We're having troubles in getting the module to download and run on this project. Any help or guidance would be extremely useful. Thanks again Karl!

  6. karl140.6 says:

    Derek,

    Yes, you can modify the project to download xap files dynamically.  Currently, the implementation expects the target of the navigation to be available in the container when you navigate.

    You can implement a check to see if MEF or your IOC container has the target, if not, download the correct XAP and initiate the navigation.  

    Basically, you'll add your check in the navigation pipeline.  Remember, users can initiate navigate using deeplinking or a UI elment on a form.  

    If you can figure this out, please post a message on the Prism Discussion page on CodePlex and we'll help you out.

    Cheers,

    Karl

  7. Chooksii says:

    Hi Karl,

    This is great stuff, thanks for the post. I’ve run in to a small problem with the browser integration when trying to veto a navigation request. Normally the pipeline is paused until the continuationCallback(bool) is called. However, when using the Frame region this doesn’t appear to be the case. In my example I’ve added an InteractionRequest in to the Page stub (FrameNavigationWrapperPage.xaml) and set the Page.DataContext = view.datacontext. My base view model has the following implementation of IConfirmNavigationRequest Which works correctly when the region is a ContentControl (i.e. not using the Frame adapter etc)

           void IConfirmNavigationRequest.ConfirmNavigationRequest(NavigationContext navigationContext, Action<bool> continuationCallback)

           {

               //ask user if they want to save?

               if (IsDirty)

               {

                   _cancelNavigationRequestConfirmation = new InteractionRequest<Confirmation>();

                   _cancelNavigationRequestConfirmation.Raise(

                           new Confirmation { Content = LocalResources.Nav_UnsavedChangesPrompt, Title = AppStrings.Dialog_ConfirmationLabel},

                           c => { continuationCallback(c.Confirmed); });

               }

               else

               {

                   //Default

                   continuationCallback(true);

               }

           }

    I’m Looking at the  “protected override void OnNavigatingFrom(NavigatingCancelEventArgs e)” for a potential fix at the moment.

    Just wondering if you had any thoughts/potential solutions?

  8. karl140.6 says:

    Chooksii,

    I'll look at this on Monday, 25 Nov and reply back.  I'll run a test and verify it works or is not working.

    For now, have you tried returning continuationCallback(false); without the interaction request?  This would allow you to nail down where the problem is happening.

    Cheers,

    Karl

  9. karl140.6 says:

    Chooksii,

    How have you added an InteractionRequest to the FrameNavigationWrapperPage.xaml?  This UserControl's content is completely replaced during navigation by the FrameContentLoader.cs file.  Meaning that the InteractionRequest XAML wll be removed from the visual tree and can't be executed.

    Please advise.

    Karl

  10. Chooksii says:

    Hi Karl,

    If my understanding is correct each View is wrapped in the a new Page (wrapper) each navigate request on the frame. I’ve added the interaction request in to the Page Wrapper as below. The interaction request is bound to my view model (I set the Page.DataContext = view.DataContext). The dialog box actually works and appears correctly, however, the navigation continues regardless (even if continuationCallback(?) isn’t called). If I test with a blocking dialog box (MessageBox) it works correctly and I can cancel or accept the request. I meant to look in to it yesterday but got held up, but thanks for taking an interest.

       <UserControl.Resources>

           <DataTemplate x:Key="ConfirmWindowTemplate">

               <Grid MinWidth="250" MinHeight="100">

                   <TextBlock TextWrapping="Wrap" Grid.Row="0" Text="Are you sure you want to navigate away?"/>

               </Grid>

           </DataTemplate>

       </UserControl.Resources>

       <i:Interaction.Triggers>

           <prismInt:InteractionRequestTrigger SourceObject="{Binding CancelNavigationRequestConfirmation}">

               <prism:PopupChildWindowAction ContentTemplate="{StaticResource ConfirmWindowTemplate}"/>

           </prismInt:InteractionRequestTrigger>

       </i:Interaction.Triggers>

    I tested this quickly this morning with your PhoneCompany sample by adding the IConfirmNavigation to ItemViewModel.cs and the following implementation. This produces the same behaviour (the dialog shows – but the navigation continues.) I realise the ChildWindow doesn’t stop execution but in non-frame based navigation the service pauses (as per design) until called back.

           public void ConfirmNavigationRequest(NavigationContext navigationContext, Action<bool> continuationCallback)

           {

               var cw = new System.Windows.Controls.ChildWindow();

               cw.Closed += (s, e) =>

               {

                   continuationCallback(cw.DialogResult.Value);

               };

               cw.Show();

           }

    Further, I pasted the code above in to the Navigation Quickstart (Drop 10) and although useless it operates correctly (i.e. the nav stops)

    Thanks again.

  11. karl140.6 says:

    chooksii,

    We understand your scenario and have a workaround for you.  I'll update the blog post and code Monday, 1 Nov.

    To unblock you for now, instead of calling the interaction request, call a MessageBox.  This is required because the ConfirmNavigationRequest needs to be synchronous.  The MessageBox blocks the thread until the user clicks OK or Cancel.

    The new sample will show you how to modify your interaction request to display a message box instead.

    Best,

    Karl

  12. Chooksii says:

    Thanks Karl,

    Yep, have been using a MessageBox in the meantime.

    Just wondering if you've had any luck with a workaround?

    Matt

  13. karl140.6 says:

    Matt,

    Yes, workaround in place.  Will blog it Monday-Tuesday next week when Prism 4 RTM's.  Will update the content of the blog post with answers to other questions as well.

    Best,

    Karl

  14. Jean Paul says:

    Hi Karl,

    What about the OnDemand module download feature of Prism… Does this integration deals with that?

    Thanks!

    JP

  15. karl140.6 says:

    JP,

    We don't provide code example for this as there are too many scenarios to cover.  Instead we provide the hooks for customers to enable this in their applications.

    Karl

  16. Jean Paul says:

    Thanks Karl,

    I'm lost in translation… What do you mean by "we provide the hooks"… is there any clue in the source code we can follow, any external guidance?

    JP

  17. karl140.6 says:

    JP,

    Look at our Modularity QuickStart code and written guidance.  This has an example of loading oni demand.

    WRT Navigation, this is a much more difficult topic because it all depends on how you implement your navigation.  If you allow a user to navigate to a view that is in an assembly that has not been loaded, the default navigation implementation will fail to locate the view.  In this case you must cancel the navigation, load the assembly and notify the user the asseembly has been loaded and then attempt the navigation operation again.

    I think the best way to solve this is, have a button that when clicked will load the assembly; while loading, display and animation within the button, then when the assembly is loaded, display the navigation options for the assembly.

    Cheers,

    Karl

  18. Yuri Zholobov says:

    Hi Karl,

    We are using Prismv4FrameRegionNavigation on Drop 10 and are having the same bug with inability to veto navigation. Are there any plans on fixing it?

    /Yuri (zholobov@gmail.com)

  19. karl140.6 says:

    Yuri,

    Please use the latest build of Prism 4 http://www.microsoft.com/…/details.aspx

    I'm unavailable until 1 Dec 2010 and will investiage the veto problem.

    Have a great day,

    Karl

  20. Andrii says:

    When navigating to ThePhoneCompanyTestPage.html#

    Home hyperlink button doesn't highlight.

    How to fix it?

  21. karl140.6 says:

    Andrii

    What is the: ThePhoneCompanyTestPage.html#

    I don't have that page in the solution.

    Karl

  22. Ben says:

    Any update on the veto?

    I want to call a service method to see if the user has permission to access the page they are navigating to so a message box isn't appropriate. I've had a very quick go at using ManualResetEvent but only managed to block my main thread so far,

    I'll have to look at it further but if you've got a workround blogged then that would be great.

    Thanks

    Ben

  23. karl140.6 says:

    Ben,

    Yes, I have it working.  I'm looking into Silverlight modal dialog implementations beside MessageBox and will post the code soon.

    Karl

  24. BenCr says:

    Hi Karl

    Is there any chance you could share that code sample with me before you blog it? Not being able to cancel navigation is causing us a bit of a security headache.

    My email address is ben dot crinion at civica dot co dot uk

    Thanks

    Ben

  25. karl140.6 says:

    Ben,

    Its going up today.

    Karl

  26. Sul says:

    Hi Karl,

    Thank you for the code and video. I have a question.

    In the VS 2010 solution I noticed that you batch the View, View Code behind and ViewModel class under one file. I wonder how can you achieve that in Visual Studio 2010 because the usual approach is to have your Views folder and your VeiwModels folder. I know that this will have nothing to do with the functionality because you have to Import the ViewModel to the View regardless of the file location but it looks really nice and handy.

    Regards,

    Sul

  27. BenCr says:

    Hi Karl

    That's great news. Please can you provide a link?

    Thanks

    Ben

  28. karl140.6 says:

    Ben,

    I will post this later today or Friday.  Sorry for the delay.  I need to finish up the blog post.

    Karl

  29. jane says:

    I have a prism based app and am not sure how to it use ur sample to implement deep linking.

    I have a shellview which has 3 regions- the intial view shows a list  and once user selects one of items in the list, it shows shrinks the listbox to 1/4 of the entire space and shows one region @ top and other to right of the list.

    User can click another item on list and data changes get reflected in the other regions.

    Can you please guide me how to implement deep linking?

  30. Lukas Cenovsky says:

    Hi, I'd like to point out a mistake I made. I forgot to override ConfigureRegionAdapterMappings() in the Bootstrapper. The result was quite a cryptic exception during ContentFrame loading:

    The content loaded was of type System.Windows.Controls.Frame, which is not a subclass of System.Windows.Controls.UserControl.

    As soon as I properly specify RegionAdapterMappings, the error is gone and navigation works perfectly.

  31. karl140.6 says:

    Jane,

    The attached example does deeplinking.  If you have nested regions, when the parent region gets navigated to, then you'll need to navigate any sub regions.  In the parents OnNavigatedTo method, parse the Url for sub region parameters and then take action to navigate the sub region.

    Have a great day,

    Karl

  32. karl140.6 says:

    Lukas Cenovsky,

    Glad you sorted out the issue.

    Best,

    Karl

  33. Faress says:

    Hmm, i can't figure out my mistake, I'm using your library for the navigation in my prism application, but instead of the page I have a system.Object displayed.

    Anyone had this issue ?

  34. karl140.6 says:

    Faress,

    Not sure why you are having this.  Can you post your project somewhere and post a link?

    Karl

  35. Chooksii says:

    Hi Karl,

    Did you get a chance to post the workaround for the Veto issue?

    Cheers,

    Matt

  36. karl140.6 says:

    Matt,

    So sorry I have not posted this.  I'll post the solution today.

    Karl

  37. faress says:

    I'm having a hard time making this work with prism, anyone achieved this with prism ?

  38. karl140.6 says:

    faress

    What problem are you having?  This uses Prism.

    Karl

  39. aspnet-scotland says:

    Hi Karl,

    I'm really interested in the prism silverlight concept. I'm just wondering if prism, mainly the navigation api, is possible within a silverlight application that is running out of browser? I'm hoping to develop a silverlight business application that runs out of browser using the prism navigation frames to cut down my xaml. Prism navigation should also allow me to re-use my silverlight user controls and allow me to do the main bulk of my development within the visual studio environment rather than expression blend. Currently I have numerous states within blend for my silverlight app pages which is becoming a messy affair, prism navigation reads like a great fix for this as long as it can be used out of browser?

    Thanks.

  40. I'm tired of imcomplete tuts says:

    For the sake of all that is good, people.  If you create a tutorial that uses a custom attached property, PLEASE include the XMLNS in your code snippets.

  41. karl140.6 says:

    aspnet-scotland,

    Yes, Prism Navigation works in out of browser applications.

    Karl

  42. Carl says:

    Hi Karl,

    In your post you mention: "The included FrameContentLoader is used in the above XAML snippet. This content loader is a replacement for the default Silverlight ContentLoader.". Could you ellaborate on this? Why have you done this and what you have done?

    Many thanks,

  43. karl140.6 says:

    Carl,

    The custom content loader is a bridge between the Prism Navigation API's and the Silverlight Frame Navigation API.  

    This bridge forwards all Prism Navigation calls to the Silverlight Frame Navigation API while at the same time enabling the Prism Navigation API to work the same as other applicaiton that are not using the Silveright Navigation API.

    Best,

    Karl

  44. Carl says:

    Karl,

    Thank you for the reponse. This example is using MEF, have you tried this with Unity? I am trying to fit this in to an existing project which uses Unity. Any pointers would be greatly appreciated.

    Thanks again,

  45. Greg says:

    Nice Work Karl !

    I had a few problems getting the navigation setup, so I though I would post this in case it could help others…

    I kept getting this error in the BeginLoad event of the FrameContentLoader:

    Cannot create navigation target 'MIS-Maps.Views.HomeView'

    This was the URImapping I had in place:

    <navigation:Frame x:Name="ContentFrame"

                                 Style="{StaticResource ContentFrameStyle}"

                                 Source="/HomeView"

                                 prism:RegionManager.RegionName="MainContentRegion"

                                 Navigated="ContentFrameNavigated"

                                 NavigationFailed="ContentFrameNavigationFailed">

    …. Other Code …..

    <!–Used to navigate to a page in the Shell–>

                           <uriMapper:UriMapping Uri="/{pageName}"

                                                 MappedUri="/MIS_Maps.Views.{pageName}" />

    Gotcha # 1 is to make sure that there is NO preceding '/' in front of your MappedURI attribute…

    <!–Used to navigate to a page in the Shell–>

                           <uriMapper:UriMapping Uri="/{pageName}"

                                                 MappedUri="MIS_Maps.Views.{pageName}" />

    Gotcha # 2 is

    If you are using MEF & you are exporting the type via interface then the URI needs to be the the namespace and name of the interface correct ?

    namespace MIS_Maps.Views

    [Export(typeof(IHomeView))]

    public partial class HomeView : ViewModelBase , IHomeView

    So that this is the correct hyperlink for the 'Source' property above:

    HomeView needs to be the name of the Interface that I export the HomeView type as which happens to be IHomeView

    <navigation:Frame x:Name="ContentFrame"

                                 Style="{StaticResource ContentFrameStyle}"

                                 Source="/IHomeView"

                                 prism:RegionManager.RegionName="MainContentRegion"

                                 Navigated="ContentFrameNavigated"

                                 NavigationFailed="ContentFrameNavigationFailed">

    Thanks

    Greg

  46. Faress, if you are using Unity in your Prism app check out compositewpf.codeplex.com/…/7383 for a solution to the System.Object problem.

  47. karl140.6 says:

    Greg,

    You had problems because the you must navigate to the same name that you loaded into MEF.

    This is why I use the full type name when registering types with MEF or Unity and always navigate using the full type name.

    Then the issues you bring up won't be a problem..

    Karl

  48. zoomba says:

    Hi Karl,

    What is the approach if I need to load more than one view in the region when I click on the menu item?

    Dragan

  49. karl140.6 says:

    Dragan,

    Not sure I fully understand your question.  

    You can navigate to a region that has sub regions or a region that has multiple UserControls.  Either of these should handle your requirements.

    Karl

  50. Chad says:

    I have a basic implementation using this framework and it is working well.  However, it appears that the sync of navigation events is only one way.  If I start navigation from RegionNavigationService.RequestNavigation() then navigation happens correctly and the RegionNavigationService.Navigated event is called.  When I start navigation from a hyperlink or updating the url in the browser the navigation happens correctly but the RegionNavigationSevice.Navigated event is not called.  Is this a known limitation and/or is there a good way to hook this up correctly.

    Thanks,

    Chad

  51. karl140.6 says:

    Chad,

    I'll look into this.  Thank you for reporting this.

    Karl

  52. Great work! I would like to remove some of my views and make them eligible for garbage collection when I navigate away from them. I have tried to implement the IRegionMemberLifetime interface and return “false” for the the KeepAlive property, but it does not seem to help. Any ideas on how I can achieve this?

    Thanks,

    Andreas

  53. karl140.6 says:

    Andreas,

    Please post your question and code here:  compositewpf.codeplex.com/discussions

    I've never had a problem with IRegionMemberLifetime.  The Prism support team and help you to get your issued resolved quickly.

    Karl

  54. Pallone says:

    Hi Karl,

    I hope this post finds you all well.

    I am using Silverlight 4 + PRISM v4 + Unity

    I am trying to use your Prismv4FrameRegionNavigation in my Silverlight 4 project but I am having some issues.

    I have only one module in my app – CodeGeneratorModule – and I created it as a Silverlight Application project. I am not sure if this is causing the problem since it is not packaged in the main Silverlight application XAP.

    When I run the application I get no errors but nothing is displayed in the shell. Well I get System.Object even though I am using the right way to register the types in the containder as per  you article.

    You said  " Intercept the navigation request, verify the assembly is loaded then proceed with the navigation request. If the assembly is not loaded or is in the process of loading, you can wait until the assembly loads, and then proceed with the request. "

    Please could you provide some sample code of how to do that since I am not sure how and where to write this code. Would I intercept the navigation in the OnNavigatedTo(NavigationContext navigationContext) of the view model?

    Cheers

    Please see some code below:

    ViewModel

    public class CodeGenViewViewModel : ViewModelBase, INavigationAware, IRegionMemberLifetime, IPageTitle

       {

           readonly IRegionManager _regionManager;

           public CodeGenViewViewModel(IRegionManager regionManager)

           {

              _regionManager = regionManager;

           }

           public bool IsNavigationTarget(NavigationContext navigationContext)

           {

               return true;

           }

           public void OnNavigatedFrom(NavigationContext navigationContext)

           {

           }

           public void OnNavigatedTo(NavigationContext navigationContext)

           {

               var test = 1;

           }

           String IPageTitle.PageTitle

           {

               get { return "Code Generator"; }

           }

           void CloseExecute()

           {

               _keepAlive = true;

               //_regionManager.RequestNavigate(RegionNames.MainContentRegion, "regionNAme");

           }

           Boolean _keepAlive = true;

           Boolean IRegionMemberLifetime.KeepAlive

           {

               get { return _keepAlive; }

           }

       }

    This is how I register my ViewModel and View

    public ModuleInit(IUnityContainer container)

    {

               container

                   .RegisterType<CodeGenViewViewModel>()

                   .RegisterType<Object, CodeGenView>(typeof(CodeGenView).FullName);

    }

    In my BootStrapper I have tried both approaches below to create/configure the Catalog. However I would prefer using the CreateFromXaml since I do not want to have a reference to the module in my main Silverlight project.

    protected override IModuleCatalog CreateModuleCatalog()

    {

               return Microsoft.Practices.Prism.Modularity.ModuleCatalog.CreateFromXaml(new Uri("/CodeGenerator;component/ModuleCatalog.xaml", UriKind.Relative));

    or

    protected override void ConfigureModuleCatalog()

           {

               var moduleInitModuleType = typeof(ModuleInit);

               this.ModuleCatalog.AddModule(

                   new ModuleInfo

                   {

                       ModuleName = moduleInitModuleType.Name,

                       ModuleType = moduleInitModuleType.AssemblyQualifiedName,

                       InitializationMode = InitializationMode.WhenAvailable,

                   });

           }

    }

    This is my Shell.xaml

    <Border Style="{StaticResource ContentBorderStyle}" Grid.Row="1">

                   <navigation:Frame

                   x:Name="ContentFrame"

                   Style="{StaticResource ContentFrameStyle}"

                   Source="/CodeGeneratorModule/CodeGenView"

                   Navigated="ContentFrame_Navigated"

                   NavigationFailed="ContentFrame_NavigationFailed"

                   prism:RegionManager.RegionName="MainContentRegion"

                   >

                       <navigation:Frame.ContentLoader>

                           <prism_Regions:FrameContentLoader RegionName="MainContentRegion"/>

                       </navigation:Frame.ContentLoader>

                       <navigation:Frame.UriMapper>

                           <uriMapper:UriMapper>

                               <!–Used to view a page–>

                               <uriMapper:UriMapping Uri="/{moduleName}/{pageName}" MappedUri="ThePhoneCompany.{moduleName}.Views.{pageName}"/>

                           </uriMapper:UriMapper>

                       </navigation:Frame.UriMapper>

                   </navigation:Frame>

    </Border>

  55. karl140.6 says:

    Claudio,

    Please post this question at:  compositewpf.codeplex.com/discussions

    You could have multiple issues.  The Prism support team can help you get up an running quickly.

    One thing to check what is the full name of the CodeGenView?

    Karl

  56. Pallone says:

    Hi Karl,

    Thanks for your reply. After writing the post above I also wrote a post to codeplex.

    What did you mean by " check what is the full name of the CodeGenView" ?

    This is the full name including the namespace   –    CodeGeneratorModule.Views.CodeGenView

    Do I need to change the anything in the Source or uriMapper?

     Source="/CodeGeneratorModule/CodeGenView"

    <uriMapper:UriMapping Uri="/{moduleName}/{pageName}" MappedUri="ThePhoneCompany.{moduleName}.Views.{pageName}"/>

    Also, does your library works with modules that are Silverlight Application project instead of Silverlight Class libraries?

    Cheers

    Claudio

  57. karl140.6 says:

    Claudio,

    Yes.  ThePhoneCompany is not your assembly name, correct?

    Karl

  58. Pallone says:

    Yep, you are right.

    I have fixed that but I still have the same problem.

    In you app you used NavigateUri="/Inventory/InventoryView"

    However you do not have any modules with Inventory name. Your module name is ThePhoneCompany.Inventory. Could you please explain that to me?

    This is what I have in my APP:

    CodeGenerator

    CodeGenerator.Web

    CodeGeneratorModule

    Prismv4FrameRegionNavigation

    I do not have a dot after my module name like you do. CodeGeneratorModule.

    Do you think that my NavigateUri and UriMapping are set wrongly?

    I really would like to get it to work. Also could you please clarify if the library will work with Silverlight application modules instead of Silverlight class libraries

    You said  " Intercept the navigation request, verify the assembly is loaded then proceed with the navigation request. If the assembly is not loaded or is in the process of loading, you can wait until the assembly loads, and then proceed with the request. "

    Please could you provide some sample code of how to do that since I am not sure how and where to write this code. Would I intercept the navigation in the OnNavigatedTo(NavigationContext navigationContext) of the view model?

    Cheers

    C

  59. karl140.6 says:

    Claudio,

    Please update your question at:  compositewpf.codeplex.com/discussions and work with the team there.  I have to work on another project.

    If you can't get your answer, please write me back.

    Karl

  60. Pallone says:

    Thanks Karl,

    I will update my question in codeplex

  61. Pallone says:

    Hi Karl,

    The guys in codeplex were very helpful. Thanks for this hint.

    I am reading and re-reading your articles to try and learn more about how the library works. I am also debugging.

    There is one item that I am still confused about. NavigateKey.

    Could you please elaborate a bit on how this works and where it gets set?

    What do I have to do for my pages to have a NavigateKey?

    Cheers

    Claudio

  62. karl140.6 says:

    Claudio,

    NavigateKey is from my Ocean framework.  Where did you read about this.  It's not part of Prism.

    Best,

    Karl

  63. Pallone says:

    Karl,

    well, I am reading all your articles and I came across this in the Navigatus sample.

    I am finding a bit difficult to figure out how you manage to add the labels with the numbers to show how many items you have edited to the tab, and Item and category labels.

    I saw a method like this there

    protected List<NavigateKey> GetActiveDataItems(Type view)

           {

               var list = new List<NavigateKey>();

               if (this.NavigateKey != null)

               {

                   foreach (var item in this.NavigateKey.ActivePages.Pages.Values)

                   {

                       var nk = item.GetValue(NonLinearNavigationContentLoader.NavigateKeyProperty) as NavigateKey;

                       if (nk != null && nk.ViewType == view)

                       {

                           list.Add(nk);

                       }

                   }

               }

               return list;

           }

    Is this something I should not worry about?

    Cheers

    Claudio

  64. karl140.6 says:

    Claudio,

    This does not use Prism.  I recommend using Prism.  I wrote this before I joined patterns & practices.  I also wrote the WPF version of this when I wrote the BBQ Shack.  

    I recommand Prism because it has many people behind it, as opposed to me, writing a framework and posting on my blog.

    Best,

    Karl

  65. Pallone says:

    ok, thanks for the advice. I will use PRISM 🙂

    So, prism will take of all that for me right?

    I will concentrate on your PRISM demo then….

  66. karl140.6 says:

    Claudio,

    Prism has a complete, well documented navigation solution for WPF and Silverlight.

    Best,

    Karl

  67. Pallone says:

    Karl,

    I am a bit confused about how you manage to create the labels and numbers for the tab and item and category labels. Also how you create the edited items list in the Prism demo.

    Do you have any other article or video I can watch?

    I have watched your video on vimeo and Silverlight TV.

    Cheers

    C

  68. karl140.6 says:

    Claudio,

    For the Inventory tab item header, look at ContentFrame_Navigated in ShellView.xaml.cs.  Each time the frame is navigated, I update the label.

    The items being edited list is populated in UpdateActiveDataItems  in InventoryViewModel.cs.  This method searches the region for views matching a certain Type, then extracts required information from the views DataContext.  This results in a list of items that the can be displayed in the item currently being edited.

    Karl

  69. Pallone says:

    Thanks Karl,

    I will have a look at those methods.

    I have noticed that you are using a DataTemplateSelector. Where does the DataItems.View come from?

    <ListBox Grid.Row="1" Grid.Column="1" x:Name="lbDataItems" ItemsSource="{Binding Path=DataItems.View}" Margin="6">

    Cheers

    Claudio

  70. Pallone says:

    thanks Karl. I will keep debugging until I get it.

    Cheers

    Claudio

  71. karl140.6 says:

    Claudio,

    DataItems is a CollectionViewSource, View is a view of the collection view source.  You can read about it here: msdn.microsoft.com/…/system.windows.data.collectionviewsource.view.aspx

    Karl

  72. Pallone says:

    thanks Karl.

  73. Hallo Karl,

    many thanks for this nice framework. Unfortunately I'm stuck at trying to handle

    deep links that use caracter casings that differ from my uri mappings. These lead

    to navigation errors because the right type couldn't be loaded due to the differing

    caracter casings. But I think it's quite common that people type urls in lower case.

    Do you have any suggestions how I could solve this?

    Thanks in advance and regards

    Holger

  74. karl140.6 says:

    Holger,

    You can override the logic that does the comparison to not perform a case sensitive test.  I'll don't recall the method or syntax, but can consult with the lead developer on Monday and get back to you.  This override is not difficult to perform.

    Best,

    Karl

  75. Thanks a lot, I'll also try to find it myself in the mean time. Meanwhile I'm experiencing another weird behaviour:

    I changed all content xamls from Page to UserControl. When I know run the project I get this navigation error:

    {System.InvalidOperationException: The content loaded was of type System.Windows.Controls.Frame, which is not a subclass of System.Windows.Controls.UserControl. at System.Windows.Navigation.NavigationService.ContentLoader_BeginLoad_Callback(IAsyncResult result)}

    If I hit F5 the content shows up as expected and when navigating further the error doesn't show up any more. Why does it try to load a Frame?

    I already tried to step through the navigation code and what the FrameNavigationWrapperPage is doing but until now I don't

    reallyhave a clue. Especially why this only happens on the first load and first since I switched to UserControls instead of Pages?

    Thanks in advance

    Holger

  76. karl140.6 says:

    Holger,

    Silverlight Frame Navigation uses pages.  Why switch to UserControls?

    Karl

  77. vrubel says:

    Views always just added to the collection Region.Views

    But never removed.

    This is a memory leak.

  78. karl140.6 says:

    vruble,

    The details views are removed from the Region.Views collection when you close them.

    Karl