Issues with the Silverlight Navigation architecture

by StefanOlson 3. June 2009 15:56

In my last post, I described some of the ways I was able to use the Silverlight URI mapping architecture to provide more customized routing.

However, there are a few problems with the navigation/URI mapping architecture that I haven't been able to find ways to work around.

Setting a title

One of the major problems I've encountered is getting the title (in the browser's title bar) to be set. There is a field in the Page class called title. So you would think would be relatively simple. It appears that there's some difficulty in setting the page title, unless it's set at a particular time during the navigation. Here's how I try and set it:

protected override void OnNavigatedTo(NavigationEventArgs e)
{
    CachedCategory category = ClientStaticCache.GetCategoryById(
int.Parse(NavigationContext.QueryString["categoryid"])); Title = category.Name + " - " + "my site"; }

 

But the title never appears -  the URI Appears as the title. I started a thread on the Silverlight forum, but no one has been able to give me an answer as to when you can set the title so that it appears correctly in the title bar and the navigation history of the browser.

When you're working in an asynchronous world you often won't get the title at the time the navigation starts, there needs to be a way to set the title when the data has come down the line.

Asynchronous operation

If I want to go and just find out if a particular URI exists (e.g: a category), before navigating to it, I either have to have a cache of categories on my local Silverlight client, which is what I'm doing at the moment, or you need to go to the server and verify that that is a valid category. The problem with the URI mapping architecture is that it is synchronous, so it is not possible to go and ask the server and then come back to the URI mapper and say yes, this is where I want to go.

Null return from the URI mapper causes exception

As described in my previous post, if you return null from the URI mapper because you've handled the URI by displaying a dialog or something like that, an exception is caused. However, the exception doesn't happen if you change the URI in the address bar, only if you call it via Frame.Navigate.

Bug on connect

Reusing a page

Another scenario occurs when we have we have content on a page which is very slow to load. In my case the Virtual Tour Viewer has a page that contains floor plans. These are very slow to load, and I don't want to have to reload the floor plans as the page changes, because some pages display the floor plans and others don't.

Ideally, the Frame system would be able to come to you and say here's a page I'm asking for, would you like to give me the page. In this case, I could reuse the pages containing the floor plan and increase the speed of the application simply by handling an event on the frame.  e.g:

Page Frame_GetPageForUri(object sender, GetPageForUriEventArgs e)
{
    if (e.Uri.ToString() == "categorycontrol.xaml")
    {
        return CategoryControl;
    }
    return null;
}

private Page _CategoryControl;

private Page CategoryControl
{
    get
    {
        if (_CategoryControl == null)
        {
            _CategoryControl = new CategoryControl();
        }
        return _CategoryControl;
    }
}

The way I am looking to do this currently is to store the floor plan control outside of the page so that it doesn't get destroyed if the garbage collector tries to clean up when the page isn't displayed, but that is not very satisfactory.

Role-based pages

Another useful piece of the navigation architecture would be the ability to use an attribute to declare the required roles to display a particular page. If the user does not have that role, then a login dialog would be displayed, in the same way as is done with the ASP.net authentication system. Under Silverlight, you could do this in the same way as is done on domain service you have a requires authentication attribute and a requires role attribute:

[RequiresAuthentication]
public partial class CategoryPage : Page

WPF compatibility

The virtual tour viewer needs to work on both WPF and Silverlight. I've encountered a number of problems trying to share the code between WPF and Silverlight with regard to the navigation framework.  WPF 4 currently doesn't have the API improvements that have been made to the Silverlight navigation framework such as the URI mapper and a number of virtual functions in the page class.  This means I have had to create a whole set of classes just to create a level of compatibility, part of which is completely impossible because of the lack of the URI mapper!

It's not clear at this stage of this will be fixed by Beta 2 of the .net framework 4.

Overall the Silverlight navigation API is pretty good,  Hopefully some minor tweaks will make it a huge amount better.

Tags:

Silverlight | WPF

Comments

6/4/2009 9:07:14 AM #

Hi Stefan,

I work on the Navigation feature for Silverlight, so I'll try to respond to a few of your comments.  Feel free to send me mail at austin.lamb@microsoft.com to continue this conversation if you like.

Titles - for RTW, we've fixed the problem - I responded to your thread in the forums with full details.

For URI mapper - we no longer throw a NullReferenceException when you return null from a URI mapper (instead, when null is returned we'd just use the original URI to avoid the exception), but this still won't solve what you're really trying to do, as I understand it.  We see the mapper as simply a way to put "cleaner" URI's in the browser's address bar, not a way to cancel navigation and instead show a dialog box, as your example is showing.

To cancel navigation like that, you can sign up for the Navigating event and set e.Cancel = true when you want to cancel a navigation and do something else (like show a dialog box).

Instead of doing this, why not simply have a button that, when clicked, shows the dialog box?  What value do you get by trying to put it in a URI mapper and navigating the Frame to a specific Uri?

Maybe if you can explain a bit more behind the scenario for using the mapper to cancel navigation I could suggest some alternatives.

Page reuse - we've heard this request from a lot of people and are definitely thinking about what the correct solution is.  For the beta, one option is to use "fragment navigation" to navigate within a page and still get back/forward integration, without new instances being created.

Role-based pages - unfortunately the authentication mechanism for Silverlight is currently in .NET RIA Services, which the SDK can't take a dependency on at this time.  We're actively working with the RIA Services team to get a better story here in the future.  In the meantime, you could create a derived "AuthenticatedPage" class that checks authentication in OnNavigatedTo and redirects if it's not authenticated.  There's a bug in the Beta where navigating in OnNavigatedTo will fail so you need to BeginInvoke it, but that is fixed for RTW.

WPF compatibility - we've been working with the WPF team to iron out these inconsistencies, but given we're on different ship cycles, it takes time to get everybody in the same place.  We're definitely trying to make sharing code easier, though!

Austin Lamb

6/4/2009 9:49:07 AM #

Austin,

Thanks for your response, it is greatly appreciated.

Returning null for URI mapper. The reason I want to do this is that I want to have consistency between all the links on the HTML version of the website (for SEO purposes) and the Silverlight version. So for example www.palacevirtualtours.com/contactus.aspx. In the html-based version it will just be another page. If the user is running Silverlight and navigates to that address either by Google or via another HTML page, I wish to display a dialog in the Silverlight version.  In the beta that actually worked, if you go directly to the address, rather than using Frame.Navigate, doesn't give an exception if you return null.  However, for consistency. I felt that the easiest way to handle this I was to use the navigation system within the site as well.  That way in the future, if I decide not to display a dialog, I just have to remove the route.

The trouble with using the navigating event for this purpose is that it starts to spread around the code that I'm using to handle URIs, really I want to have all the code relating to URIs in a single place. The URI mapper.

Also, this is the way I was going to solve the login problem.  I was going to have a custom route that checked the authentication and displayed the login dialog if the user was not currently authenticated to the right level, but didn't display the page. Then when the dialog is closed I was going to navigate back to the same URI, this time being authenticated. It will display the page. The reason that I want to do this, rather than using OnNavigatedTo, is that I don't want the user to see any content in the page until we're authenticated. OnNavigatedTo is at the point the page is being displayed, it is too late.

Using fragment navigation to reuse the page is probably not a bad workaround in the meantime. I'll take a look at that.

Thanks,
Stefan

StefanOlson

6/4/2009 10:04:28 AM #

Maybe I'm misunderstanding the part about the URI being the same for the HTML and Silverlight versions - in Silverlight we'll always be putting stuff after the "#" in the address bar, so they'll most likely not be the same as any existing HTML page.  So you could have www.foo.com/contactus.aspx be an HTML page, but the SL equivalent would presumably something closer to www.foo.com/contactus.aspx#ContactUs (assuming you use URI mapping to map "ContactUs" to something).

I follow that you want to have all the URI handling logic in one place, but our intention was that "handling" a Uri and "mapping" a Uri are different things.  "Handling" the Uri is best done with events and the virtual methods on Page, as that's where interesting logic might go.  "Mapping" is really just about providing cleaner user-visible URI's (when browser-integrated).

Austin Lamb

6/23/2009 9:28:44 PM #

Pingback from silverlight-travel.com

Silverlight Travel » Displaying a login dialog based on page attributes in Silverlight 3

silverlight-travel.com

6/29/2009 12:27:00 AM #

An issue I've just come across, which may be a bug, is if the JournalOwnership property, of the Frame being mapped, is set to 'OwnsJournal'.  This appears to prevent the MapUri() method of the CustomUriMapper being called.

Mark Sutton

6/29/2009 12:05:07 PM #


@Mark,

I can't repeat the problem that you describe. The urimapper is used by the NavigationService, and it always uses the URI mapper.

In the sample application that I provided in this post www.olsonsoft.com/.../...es-in-Silverlight-3.aspx. I added JournalOwnership="OwnsJournal" to the MainFrame's Frame and everything still works correctly.

...Stefan

StefanOlson

2/18/2010 7:15:37 PM #

Displaying a login dialog based on page attributes in Silverlight 3

Displaying a login dialog based on page attributes in Silverlight 3

Stefan Olson's Blog

Add comment




biuquote
  • Comment
  • Preview
Loading



About the author

Stefan Olson is the Managing Director of Olson Software.  He has been developing software using Microsoft Technologies for nearly 20 years.

He is currently working on building the next generation Virtual Tour software in WPF and Silverlight for www.palacevirtualtours.com.

Tag cloud