Month: November 2009

Silverlight 4 Beta Released

As I was expecting, Scott Guthrie presented the Beta of Silverlight 4 at PDC today. The number of new features it offers is fairly impressive, some of which appear to enable some really exciting possiblities, although playing with the beta bits, there are a few important restrictions. Let’s look at some of the new features first.

  • Printing support
    • Silverlight hasn’t had any real printing support. Silverlight 4 offers a Printing API, including print preview. This is pretty important for business apps, and will definitely be important for the things I’m currently working on.
  • Rich Text Editing
    • Something that could be done by rolling your own editing, this will enable a lot of interesting applications. So many apps require rich text input, and this will be a great help
  • Elevated Out of Browser
    • You can set your app to ask for ‘elevated’ privileges, and then you get access to a lot more goodies – full keyboard in full screen, no cross-browser restrictions, more (but not unrestricted) access to the user’s filesystem
  • WebBrowser control
    • lets you embed another web page in your silverlight app. This looks amazing, but there are some restrictions which might make it less useful. It only works in Out of Browser (for security), and loading HTML from any website requires elevated rights. But you can do things like use the browser output as a brush onto any elements. Scott demonstrated this by putting YouTube into an interactive jigsaw – a demo that I found particularly amusing, given that one of my first silverlight apps was a jigsaw. Here’s the Silverlight 2 version.
  • Webcam and microphone support – not something I desperately need, but it’s always been a top request
  • Clipboard support – something that almost works in SL3 but not in all browsers
  • Drag and drop support
    • this is something I’ve wanted for previous demos. Being able to drag & drop pictures on the app is a lot better than having to fire up a Load dialog.

There’s an awful lot more in there, too. Here’s the info on Silverlight.net.

Advertisements

Ordnance Survey maps in Bing Maps Silverlight control

The latest incarnation of Bing Maps (the web version) has the option of showing certain levels using Ordnance Survey mapping. This was of interest to me, because I’ve spent the last year working on prototypes of mapping applications using the Ordnance Survey 25k layer (the classic Rambling maps). Here’s an example.

They’ve also just released the Silverlight Map Component (which has been in CTP since March) as a V1.0 release. I’ve played quite a bit with the CTP, so I was interested to see how much it had changed. Turns out, not so much. My own OS map layer needed only a handful of changes, almost all around their (sensible) decision to remove the MapViewSpecification object (which made animating the map difficult, so it was good to see it go). But I also wanted to see if the Silverlight component could use the OS maps.

It doesn’t support them out of the box – the only modes offered are the standard RoadMode, and the two Aerial modes (with or without labels) so it’s necessary to roll your own.

The easiest way to put different tiles on the map is to create a custom TileLayer. All you really need to know, then, is how to construct the URL for the tiles you want.

Where do you find the tiles?

A little snooping is required. I fired up a browser, and a copy of Fiddler to watch the requests it was sending. For the OS map tiles, here’s what a typical URL looks like:

http://ecn.t3.tiles.virtualearth.net/tiles/r031313112303.png?g=41&productSet=mmOS

This is a fairly typical tile Url. The long number in the name of the image is something called a QuadKey – a way to encode x, y and zoom values in a single value. Here’s a good explanation. Luckily, the map control supplies a QuadKey object to do all the work.

To create a custom tile layer, here’s the code I used:


public class OsTileSource : TileSource
 {
 public override Uri GetUri(int x, int y, int zoomLevel)
 {
 QuadKey key = new QuadKey(x, y, zoomLevel);
 // http://ecn.t3.tiles.virtualearth.net/tiles/r031313112303.png?g=41&productSet=mmOS

 if (zoomLevel < 12)
 {
 return new Uri("http://ecn.t2.tiles.virtualearth.net/tiles/r" + key.Key + ".png?g=373&mkt=en-gb&shading=hill", UriKind.Absolute);
 }
 else
 {
 return new Uri("http://ecn.t2.tiles.virtualearth.net/tiles/r" + key.Key + ".png?g=41&productSet=mmOS", UriKind.Absolute);
 }
 }
 }

You’ll notice that I had to do something different with lower zoom levels. The OS maps only exist above level 12, so below that, I use the normal road tiles.

To use this in the map, here’s what you do:


// Mercator mode means no underlying tiles
 map.Mode = new MercatorMode();
 MapTileLayer layer = new MapTileLayer();
 layer.TileSources.Add(new OsTileSource());
 map.Children.Add(layer);

And it all works fine.

Of course, this is probably breaking the Bing Maps terms, but only slightly, since the same maps are available in the Ajax component. I’m sure they’ll turn up officially in the Silverlight component at some point.

Here’s a live example.

One final thought. It’s interesting that the Bing tiles are not the exact map tiles that the OS use. You can tell that by looking at the slightly lower zoom levels – the OS grid lines are not perpendicular. This is because the maps are originally projected onto the OS Grid Projection (which is ideal for a country the size of the UK), while Bing maps uses a Mercator projection (which is easier to manage for a world map). So for the Bing maps, the OS tiles have to be ‘crunched’ to project them onto the Mercator projection, hence the non-perpendicular grid lines.

The OS OpenSpace service provides an uncrunched set of map tiles, although it looks like they miss out the really nice 25k layer, so it would probably be possible to use those tiles, but you’d have to do more work, because those tiles are in the OS Grid Projection, which isn’t compatible with the Mercator projection.

How much more work? That’s a subject for another post. I’ve done exactly that as part of my BBC prototypes, so I have a Bing Maps component working in OSGB coordinates, using uncrunched 25K imagery (see this previous post talking about the problems of hosting map tiles) but the project isn’t yet available for public consumption. However, I’m hoping to give a presentation on the subject at the first Bing Maps UK user group meeting in January, so do come along if you’re interested.

P.S. I did this code in Visual Studio 2010 Beta 2, and the Map component works, live, in the VS designer. As I typed values into the Xaml for the Center and ZoomLevel, the map animated smoothly to that location. Very cool indeed.

Binding to a Silverlight Chart Series gives a null reference exception

This just wasted a good part of my day. I wanted to use a Silverlight Toolkit chart, and I wanted to use declarative databinding for the ColumnSeries (although LineSeries would fail similarly).

I have a DependencyObject as my datasource (although the same would probably happen with a POCO or an INotifyPropertyChanged object) with various dependency properties I want to bind. Here’s the kind of declaration that gives the error:


<chart:Chart x:Name="chart"
 Height="500">
 <chart:Chart.Series>
 <chart:ColumnSeries ItemsSource="{Binding Results, Mode=OneWay}"
 IndependentValuePath="GenreTitle" />
 </chart:Chart.Series>
 </chart:Chart>

When the chart is created, I get a System.NullReferenceException when I set the value of Results.

At first, I thought my Results enumeration was somehow wrong, but the same data exactly worked perfectly in a ListBox and a DataGrid, so the data was probably OK.

It was simpler than that, though – I’d missed out the DependentValuePath. You need to specify both otherwise you (rightly) get a null reference exception.

Simple, but it took me a bit of puzzling to find it.

Why can’t I use Linq on a CaptureCollection?

Doing some Regular Expression work, and I wanted to get a simple enumeration of multiple captures in a match group. I tried this:

var caps = from capture in match.Groups[2].Captures select capture.Value;


but Intellisense told me “Could not find an implementation of the query pattern for source type ‘System.Text.RegularExpressions.CaptureCollection’.  ‘Select’ not found.  Consider explicitly specifying the type of the range variable ‘capture’.”

This is because Captures is a specialised collection, not a generic collection, and it doesn’t therefore support IEnumerable<T>. The solution (which I found here) is:

var caps = from capture in pathmatch.Groups[2].Captures.Cast<Capture>() select capture.Value;

adding the Cast to a Capture allows the rest of the Linq query to work.

I love Linq, but just sometimes, it can be a bit impenetrable.