With the Silverlight 3 release, we get a new Navigation API that allows you to deep-link into your Silverlight controls, as well as enable the back/forward buttons in the browser. This blog post will show you how you can use the navigation API to link to specific extents in the map, and allow the user to go back and forward in the extent history user the browser history.
The Navigation API allows you to use parts of the URI in the browser to load specific user controls in your project. To do this, let's first link to a specific user control in our a new default Silverlight + website application. First add a reference to the navigation assembly "System.Windows.Controls.Navigation".
In our main page control, we create a Frame element, which maps to another "page", in this case Map.xaml. Map.xaml will be the page that holds our map. The MainPage.xaml should look something like this:
<UserControl x:Class="MapNavigation.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:navigationCtrl="clr-namespace:System.Windows.Controls;
assembly=System.Windows.Controls.Navigation"
xmlns:navigation="clr-namespace:System.Windows.Navigation;
assembly=System.Windows.Controls.Navigation" >
<Grid>
<navigationCtrl:Frame>
<navigationCtrl:Frame.UriMapper>
<navigation:UriMapper>
<navigation:UriMapping Uri="" MappedUri="/Map.xaml" />
</navigation:UriMapper>
</navigationCtrl:Frame.UriMapper>
</navigationCtrl:Frame>
</Grid>
</UserControl>
The Frame uses the UriMapping entry to map the empty (root) URI to the user control "/Map.xaml". Since the Frame’s Source property is not set, the Map.xaml associated with the empty URI is loaded by a default at runtime.
Next, right-click your Silverlight project in Visual Studio, and select to add a new User Control. Name it Map.xaml. This will be the sub-page that will be hosted by MainPage. This must be a of type System.Windows.Controls.Page, so the first step is to change the superclass. Go to Map.xaml's header and change the UserControl tag to navigation:Page and add the navigation prefix. While we are at it we also add the ESRI namespace and a simple map control:
<navigation:Page x:Class="MapNavigation.Map"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:esri="clr-namespace:ESRI.ArcGIS.Client;assembly=ESRI.ArcGIS.Client"
xmlns:navigation="clr-namespace:System.Windows.Controls;
assembly=System.Windows.Controls.Navigation">
<Grid x:Name="LayoutRoot">
<esri:Map x:Name="myMap">
<esri:ArcGISTiledMapServiceLayer ID="StreetMap"
Url="http://server.arcgisonline.com/ArcGIS/rest/services/
ESRI_StreetMap_World_2D/MapServer" />
</esri:Map>
</Grid>
</navigation:Page>
In the code-behind of Map.xaml.cs, change the class declaration to inherit from "Page" instead of "UserControl" : public partial class Map : Page
If you run the application now, the application should load up Map.xaml. Notice the browser URI does not contain any parameters and the back button in the browser is disabled.

So far so good. So now to allow the users to go back and forth when they zoom in/out we first need to tell the navigation service that the state has changed and provide it with a the link to the current extent that changed. We do this by listening to the ExtentChanged event of the map, and in the event handler call NavigationService.Navigate:
<esri:Map x:Name="myMap" ExtentChanged="myMap_ExtentChanged" >
private void myMap_ExtentChanged(object sender, ESRI.ArcGIS.Client.ExtentEventArgs e)
{
NavigationService.Navigate(new Uri(String.Format(CultureInfo.InvariantCulture,
"#extent={0},{1},{2},{3}", e.NewExtent.XMin, e.NewExtent.YMin,
e.NewExtent.XMax, e.NewExtent.YMax), UriKind.Relative));
}
The format we use can be anything, but we prefix it with "#" to tell the service that this is a "fragment" within the Map URI. If we didn't do this, the service would try and load a new instance of Map.xaml causing the map to reset. If we run the application now, everytime you change the extent, the browser URI will update:

Notice the "$" symbol which signifies a fragment that contains extent parameters. The fragment is handled by the Map page which is mapped to the default or root URI.
The back and forward buttons now work, but nothing happens when we click them. We still need to handle when the fragment changes and set the map extent according. We do this by overriding Page.OnFragmentNavigation. This method is called every time the browser fragment after "#$" has changed. In this method we will parse out the extent and set the extent of the map. Notice that we check if the extent really has changed. This is because the code in myMap_ExtentChanged also causes this method to get called, but in those cases there is no need to update the extent, since it already happened. Doing that could cause an infinite loop. Similar we ignore the code in the extent changed handler if we update the extent in OnFragmentNavigation using a simple boolean flag:
protected override void OnFragmentNavigation(FragmentNavigationEventArgs e)
{
if(e.Fragment.StartsWith("extent="))
{
try
{
string[] vals = e.Fragment.Substring(7).Split(new char[] { ',' });
double xmin = Double.Parse(vals[0]);
double ymin = Double.Parse(vals[1]);
double xmax = Double.Parse(vals[2]);
double ymax = Double.Parse(vals[3]);
Envelope mapExtent = myMap.Extent;
Envelope newExtent = new Envelope(xmin, ymin, xmax, ymax);
if (mapExtent==null || !AreEqual(mapExtent, newExtent, 0.000000001))
{
flag = true;
myMap.Extent = newExtent;
}
}
catch { }
}
base.OnFragmentNavigation(e);
}
bool flag = true;
private void myMap_ExtentChanged(object sender, ESRI.ArcGIS.Client.ExtentEventArgs e)
{
if (flag) flag = false;
else
NavigationService.Navigate(new Uri(String.Format(CultureInfo.InvariantCulture,
"#extent={0},{1},{2},{3}", e.NewExtent.XMin, e.NewExtent.YMin,
e.NewExtent.XMax, e.NewExtent.YMax), UriKind.Relative));
}
Now when you run the application, you can use the back and forward buttons to visit previous extents, you can copy/paste the URL into a different browser or email it, and the map will start up at the same location.
Download a sample from our development server - view the sample live to see it in action.
You can learn more about the navigation framework on the following links:
Silverlight Navigation framework screencast
Tim Heuer's blog on the Navigation framework
Tim Heuer's blog on Navigation behavior
Morten Nielsen
Senior Software Engineer
ArcGIS Server.NET, Silverlight/WPF, MapIt