I found a bug while testing the NavigationFramework. I really like the FragmentNavigation system that allows to put browser-history entry without reloading a page. That seems very usefull to me for data-list paging and Master / Details scenarii.
The bug I found is due to the way UriMapping is done, and how the NavigationService expose sources. In fact, it exposes only the before-mapping Uri.
Here is how I declared my Frame and its associated mappings:
If I navigate to /TestPage1.xaml?id=1 and then to /TestPage1.xaml?id=1#detail=2, I correctly receive the FragmentNavigation event with the "Fragment" string correctly set to "detail=2".
If I navigate to test/1 and then to test/1/details/2, I receive the FragmentNavigation event, but the Fragment string is empty. As the NavigationService does not expose the after-mapping uri, I even cannot parse the uri manually to find the fragment part.
However, after looking with Reflector on the NavigationService, I see a private field named "_currentSourceAfterMapping". When looking at the NavigationService state in my FragmentNavigation EventHanlder, I saw it was correctly set to "/TestPage1.xaml?id=1#detail=2".
But it seems that the FragmentNavigation event arg is not generated from the "_currentSourceAfterMapping" field but only the "_currentSource". As there is no public view of "_currentSourceAfterMapping" nor to the UriMapper property of the NavigationService,
we come to a dead end. The only workaround that I see in the current bits is to keep a reference to the UriMapper somewhere and call it's MapUri method to get the equivalent to the "_currentSourceAfterMapping" field when needed, and then make Substring to
get the fragment part of the URI.
Hope to see this fixed (that seems very important to me for a SEO-enabled RIA perspective)
RIASilverlight 3NavigationBug
Simon Ferquel
MVP Client App Dev
http://www.simonferquel.net/blog
I'm able to reproduce this problem - you're correct, it's that we're looking at the Uri before mapping, not after (as we obviously should be).
Thanks for reporting this! It will be addressed before RTW, as Kathy mentions - unfortunately, for now I can think of no simple workaround. You could get a reference to the Frame (though this is difficult from a Page, and probably best accomplished today
by walking up the visual tree until you find it), and doing something like this:
(frame.Resources["uriMapper"] as UriMapper).MapUri(this.NavigationService.Source).OriginalString.Split(new char[] { '#' }, StringSplitOptions.RemoveEmptyEntries)[1];
That, of course, doesn't do any error handling, but hopefully the idea is clear.
It is not the application that is wrong. Rather, the interface is wrong and broken.
First, MappedUri assumes your absolute path is a .NET assembly, but there is no [TypeConverterAttribute(typeof(MappedUriConverter))] formalizing this contract. Instead, it is not discoverable and buried inside the core logic of the class. MappedUriConverter
should be the feature that provides "convention over configuration", not core logic.
MappedUri and Uri both claim to be of type Uri, but they are not. They are UriPattern or UriTemplate objects (even though such a class doesn't exist in .NET). This is an Impostor Type bug.
Bear in mind this is a rough sketch, the interface should look like:
public class UriMapping { #region Properties // UriPatternConverter converts relative UriPatterns into // Application Pack Format formatted UriPatterns. // The reason is that all UriPattern objects must be in System.Uri // canonical form. [TypeConverterAttribute(typeof(UriPatternConverter))] public UriPattern From {get; set;} [TypeConverterAttribute(typeof(UriPatternConverter))] public UriPattern To {get; set;} #endregion }
In order to understand the role of UriPatternConverter in this design, you need to understand WPF's Application Pack protocol. In particular, Absolute vs. Relative Pack URIs:
The following is an Absolute Pack URIs:
pack://application:,,,/ResourceFile.xaml
The relative pack URI that refers to this resource file would be the following.
/ResourceFile.xaml
UriPatternConverter maintains the pattern matching constructs in the string to be passed to UriPattern, but it absolutely resolves the relative Uri.
The only hard thing is what to do with the To string. It currently cannot be null, or contain a ? or contain a #. It should probably also not begin with a /. UriPatternConverter would use the "doesn't begin with /" rule to know it is a fragment identifier.
Although, what I like best is probably requiring # at the start of the To string. This is how the Web works with HTML anchors - why change it?
Just look at how bad the interface for UriMapping is. Here is another example: UriMapping.MapUri method is broken.
Read the documentation. The docs say: "Converts the specified uniform resource identifier (URI) to a new URI, if the specified URI matches the defined template for converting."
What defined template? Just reading this should jump out at you that this is a code smell.
I'm not saying my proposal above is that great, either. It is mainly to kickstart some thinking, and is sort of a continuation of a discussion between
me and Tim Heuer on the Silverlight video blog.
This is a problem with .NET since day one: the Uri class was not implemented in an OO fashion, and people build on top of it or use it in a non-OO fashion. For instance, Schemes are hardcoded into the Uri class. It's why Uri is actually one of the biggest
if not the biggest classes in Silverlight -- it is monolithic and poorly designed. It actually is one of the .NET classes with the most obsolete methods, too.
In my experience, programmers just don't know what a Uri is or what object-oriented navigation is at a theoretical level. This is all the more reason for Microsoft to deliver the solution, so Joe Blow doesn't try coding his own navigation subsystem. The Uri
class and its associated UriBuilder should've been deprecated and rewritten for .NET 3.0, with a new Uri for the four foundation class libraries, but it wasn't and we're stuck with it.
simon.ferquel
Member
344 Points
94 Posts
[Bug] FragmentNavigation + UriMapping
Mar 26, 2009 02:38 PM | LINK
Hi,
I found a bug while testing the NavigationFramework. I really like the FragmentNavigation system that allows to put browser-history entry without reloading a page. That seems very usefull to me for data-list paging and Master / Details scenarii.
The bug I found is due to the way UriMapping is done, and how the NavigationService expose sources. In fact, it exposes only the before-mapping Uri.
Here is how I declared my Frame and its associated mappings:
If I navigate to /TestPage1.xaml?id=1 and then to /TestPage1.xaml?id=1#detail=2, I correctly receive the FragmentNavigation event with the "Fragment" string correctly set to "detail=2".
If I navigate to test/1 and then to test/1/details/2, I receive the FragmentNavigation event, but the Fragment string is empty. As the NavigationService does not expose the after-mapping uri, I even cannot parse the uri manually to find the fragment part.
However, after looking with Reflector on the NavigationService, I see a private field named "_currentSourceAfterMapping". When looking at the NavigationService state in my FragmentNavigation EventHanlder, I saw it was correctly set to "/TestPage1.xaml?id=1#detail=2". But it seems that the FragmentNavigation event arg is not generated from the "_currentSourceAfterMapping" field but only the "_currentSource". As there is no public view of "_currentSourceAfterMapping" nor to the UriMapper property of the NavigationService, we come to a dead end. The only workaround that I see in the current bits is to keep a reference to the UriMapper somewhere and call it's MapUri method to get the equivalent to the "_currentSourceAfterMapping" field when needed, and then make Substring to get the fragment part of the URI.
Hope to see this fixed (that seems very important to me for a SEO-enabled RIA perspective)
RIA Silverlight 3 Navigation Bug
MVP Client App Dev
http://www.simonferquel.net/blog
Kathykam
Member
21 Points
14 Posts
Microsoft
Re: [Bug] FragmentNavigation + UriMapping
Mar 26, 2009 06:56 PM | LINK
Hi Simon,
Thank you for letting us know. We'll take a look at this and make sure it is addressed before RTW.
Thanks,
Program Manager | Microsoft
http://blogs.msdn.com/kathykam
***This posting is provided "AS IS" with no warranties, and confers no rights ***
AustinLamb
Member
602 Points
104 Posts
Microsoft
Re: [Bug] FragmentNavigation + UriMapping
Mar 27, 2009 01:39 AM | LINK
Hi Simon,
I'm able to reproduce this problem - you're correct, it's that we're looking at the Uri before mapping, not after (as we obviously should be).
Thanks for reporting this! It will be addressed before RTW, as Kathy mentions - unfortunately, for now I can think of no simple workaround. You could get a reference to the Frame (though this is difficult from a Page, and probably best accomplished today by walking up the visual tree until you find it), and doing something like this:
(frame.Resources["uriMapper"] as UriMapper).MapUri(this.NavigationService.Source).OriginalString.Split(new char[] { '#' }, StringSplitOptions.RemoveEmptyEntries)[1];
That, of course, doesn't do any error handling, but hopefully the idea is clear.
jzabroski
Member
70 Points
53 Posts
Re: [Bug] FragmentNavigation + UriMapping
Apr 01, 2009 05:18 PM | LINK
Actually, this is funny.
It is not the application that is wrong. Rather, the interface is wrong and broken.
First, MappedUri assumes your absolute path is a .NET assembly, but there is no [TypeConverterAttribute(typeof(MappedUriConverter))] formalizing this contract. Instead, it is not discoverable and buried inside the core logic of the class. MappedUriConverter should be the feature that provides "convention over configuration", not core logic.
MappedUri and Uri both claim to be of type Uri, but they are not. They are UriPattern or UriTemplate objects (even though such a class doesn't exist in .NET). This is an Impostor Type bug.
Bear in mind this is a rough sketch, the interface should look like:
In order to understand the role of UriPatternConverter in this design, you need to understand WPF's Application Pack protocol. In particular, Absolute vs. Relative Pack URIs:The following is an Absolute Pack URIs:
pack://application:,,,/ResourceFile.xaml
The relative pack URI that refers to this resource file would be the following.
/ResourceFile.xaml
UriPatternConverter maintains the pattern matching constructs in the string to be passed to UriPattern, but it absolutely resolves the relative Uri.
Here is an example:
The From string is passed to UriPatternConverter and becomes an UriPattern object with an absolute path:
UriPattern { Pattern = "pack://application:,,,/Views/{page}.xaml" }
The only hard thing is what to do with the To string. It currently cannot be null, or contain a ? or contain a #. It should probably also not begin with a /. UriPatternConverter would use the "doesn't begin with /" rule to know it is a fragment identifier. Although, what I like best is probably requiring # at the start of the To string. This is how the Web works with HTML anchors - why change it?
Just look at how bad the interface for UriMapping is. Here is another example: UriMapping.MapUri method is broken. Read the documentation. The docs say: "Converts the specified uniform resource identifier (URI) to a new URI, if the specified URI matches the defined template for converting." What defined template? Just reading this should jump out at you that this is a code smell.
I'm not saying my proposal above is that great, either. It is mainly to kickstart some thinking, and is sort of a continuation of a discussion between me and Tim Heuer on the Silverlight video blog.
This is a problem with .NET since day one: the Uri class was not implemented in an OO fashion, and people build on top of it or use it in a non-OO fashion. For instance, Schemes are hardcoded into the Uri class. It's why Uri is actually one of the biggest if not the biggest classes in Silverlight -- it is monolithic and poorly designed. It actually is one of the .NET classes with the most obsolete methods, too.
In my experience, programmers just don't know what a Uri is or what object-oriented navigation is at a theoretical level. This is all the more reason for Microsoft to deliver the solution, so Joe Blow doesn't try coding his own navigation subsystem. The Uri class and its associated UriBuilder should've been deprecated and rewritten for .NET 3.0, with a new Uri for the four foundation class libraries, but it wasn't and we're stuck with it.