Parsing a GeoRSS Atom feed using XML to LINQ in Silverlight

…or how to put a map of your blogposts on your blog.

I recently started a little pet project with a “photo a day” blog. I thought it could be fun to geocode each blogpost with to the place where each photograph was taken, and then place each photo on a map, similar to the flickr map I created earlier.

The most common way of geocoding an atom feed, is by adding a <georss:point>[latitude] [longitude]</georss:point> field for each entry. However, the blogengine I’m using currently doesn’t support that, so I will also try and find the location using a magic string in the blogpost, in this case “Location: [latitude] [longitude]”. You will notice that the posts I made so far, all have this at the end of the post. This might not be the most elegant solution to geocoding your blogposts, but it should work for any type of blog.

So the first step is to create a WebRequest that will download the feed (this could be simplified by using the WebClient, but I like the full control of the WebRequest):

System.Net.WebRequest request = System.Net.WebRequest.Create(
     new Uri("http://socaldaily.sharpgis.net/syndication.axd?format=atom", UriKind.Absolute));
request.BeginGetResponse(new AsyncCallback(createRssRequest), new object[] { request, this });

Begin get response handler:

private static void createRssRequest(IAsyncResult asyncRes)
{
      object[] state = (object[])asyncRes.AsyncState;
      System.Net.HttpWebRequest httpRequest = (System.Net.HttpWebRequest)state[0];
      Page page = (Page)state[1];
 
      if (!httpRequest.HaveResponse) { return; }
 
      System.Net.HttpWebResponse httpResponse = (System.Net.HttpWebResponse)httpRequest.EndGetResponse(asyncRes);
      if (httpResponse.StatusCode != System.Net.HttpStatusCode.OK) { return; }
      Stream stream = httpResponse.GetResponseStream();
      page.Dispatcher.BeginInvoke(() => { page.ParseAtomUsingLinq(stream); });
}

When the request comes back, it will call our ParseAtomUsingLinq method with the response stream. The LINQ expression will select basic parameters like title, date, link, contents and if available, the georss location point.

private void ParseAtomUsingLinq(System.IO.Stream stream)
{
    System.Xml.Linq.XDocument feedXML = System.Xml.Linq.XDocument.Load(stream);
    System.Xml.Linq.XNamespace xmlns = "http://www.w3.org/2005/Atom"; //Atom namespace
    System.Xml.Linq.XNamespace georssns = "http://www.georss.org/georss"; //GeoRSS Namespace
 
    //Use LINQ to select all entries
    var posts = from item in feedXML.Descendants(xmlns + "entry")
                select new 
                {
                    Title = item.Element(xmlns + "title").Value,
                    Published = DateTime.Parse(item.Element(xmlns + "updated").Value),
                    Url = item.Element(xmlns + "link").Attribute("href").Value,
                    Description = item.Element(xmlns + "summary").Value,
                    Location = fromGeoRssPoint(item.Element(georssns + "point"))  //Simple GeoRSS <georss:point>X Y</georss.point>
                };
     foreach (var post in postsOrdered)
     {
          ESRI.ArcGIS.Geometry.MapPoint point = null;
          if (post.Location != null)
          {
               point = post.Location; //Use GeoRSS location
          }
          else
          {
               point = ExtractLocation(post.Description); //Search for location in blog content
          }
          if (point == null) continue; //We didn't find a point
          string imageSrc = ExtractImageSource(post.Description, post.Url); //try and find an image to use for symbol
          //TODO: Add points to map...
     }
}

You will notice in the above code we use three utility methods for extracting data. First we have a method that converts the simple GeoRSS format “<georss:point>X Y</georss.point>” to a point. In this case I’m using the MapPoint class from the ESRI ArcGIS Silverlight API, since I wan’t to use that to draw my entries on the map.

private ESRI.ArcGIS.Geometry.MapPoint fromGeoRssPoint(System.Xml.Linq.XElement elm)
{
    if (elm == null) return null;
    string val = elm.Value;
    string[] vals = val.Split(new char[] { ' ' });
    if (vals.Length != 2) return null;
    double x = double.NaN;
    double y = double.NaN;
    if (double.TryParse(vals[1], out x) && double.TryParse(vals[0], out y))
    {
        return new ESRI.ArcGIS.Geometry.MapPoint(x, y);
    }
    return null;
}

If this doesn’t return any results (usually if the feed is not georss enabled), we will during the loop look for the location tag in the entry content, using the following helper method:

private ESRI.ArcGIS.Geometry.MapPoint ExtractLocation(string description)
{    
    int idx = description.LastIndexOf("Location: ");
    int idx2 = description.LastIndexOf("</p>");
    double x = double.NaN;
    double y = double.NaN;
    if (idx < idx2 && idx > -1)
    {
        string sub = description.Substring(idx, idx2 - idx);
        string[] vals = sub.Split(new char[] { ' ' });
        foreach (string val in vals)
        {
            if (val[0] == 'N')
            {
                double.TryParse(val.Substring(1), out y);
            }
            else if (val[0] == 'S')
            {
                if (double.TryParse(val.Substring(1), out y)) y *= -1;
            }
            else if (val[0] == 'E')
            {
                double.TryParse(val.Substring(1), out x);
            }
            else if (val[0] == 'W')
            {
                if (double.TryParse(val.Substring(1), out x)) x *= -1;
            }
        }
        if (!double.IsNaN(x) && !double.IsNaN(y))
        {
            return new ESRI.ArcGIS.Geometry.MapPoint(x, y);
        }
    }
    return null;
}

Lastly, we will search for the first <img src=”…”/> entry in the body and use that for displaying the entries on the map:

private string ExtractImageSource(string description, string feedlink)
{
    int idxSrc = description.IndexOf("<img ");
    if (idxSrc >= 0)
    {
        int idxSrc2 = description.Substring(idxSrc).IndexOf("src=\"");
        int idxSrc3 = description.Substring(idxSrc + idxSrc2 + 5).IndexOf("\"");
        if (idxSrc2 >= 0 && idxSrc3 >= 0)
        {
            string src = description.Substring(idxSrc + idxSrc2 + 5, idxSrc3);
            Uri uri = new Uri(src, UriKind.RelativeOrAbsolute);
            if (uri.IsAbsoluteUri) return uri.AbsoluteUri;
            Uri page = new Uri(feedlink, UriKind.Absolute);
            return new UriBuilder(page.Scheme, page.Host, page.Port, src).Uri.AbsoluteUri;
        }
    }
    return null;
}

In our feed entries loop, we can now simply construct a graphic, set a few attributes that we will use for binding the look for the symbol (ie title and image) and add it to our graphics layer. The symbol I use here is the same as used for the flickr application mentioned above.

ESRI.ArcGIS.Graphic g = new ESRI.ArcGIS.Graphic()
{
    Geometry = point,
    Symbol = myFeedSymbol
};
g.Attributes.Add("Title", title);
g.Attributes.Add("ImageURI", src);
g.Attributes.Add("WebURI", link);
myGraphicsLayer.Graphics.Add(g);

You can view the blog map in action here: http://socaldaily.sharpgis.net/page/Photo-map.aspx

Note that there are several ways to geocode blogposts, and this approach only deals with the simplest version.

ESRI ArcGIS Silverlight/WPF API released

FINALLY we released the beta of our new ArcGIS client API for Silverlight and WPF the end of this week.

You can download the beta here, where you also can find links to documentation and samples. Note that the download requires an ESRI global account, which you can create for free.

Art has the details on his blog.

This his has been (well still is since it's beta :-) a great fun project to work on. We tried to design it to be similar to our JavaScript and Flex APIs, but at the same time make it more ".NET" when it made sense, or taken advantage of capabilities Silverlight had. We really look forward to the feedback from you, as well as talking to anyone who's at the ESRI Developer summit next week. I'll be there Monday through Thursday, mostly hanging out at the showcase area, so if you're there, stop by say hi and get to see the API first hand. Art and Rex will be doing an intro session Wednesday at 1pm, and I'll join them Thursday 10:15am for the advanced session.

Below is a simple flickr application that I created for the api, (source is available for download at the code gallery). Just zoom to any area of interest, hit the flickr button, and images in that area pops up. The sample demonstrates the power of templating the symbols which allows you to associate animations to states of the symbols, as well as using binding. This is really no different than the states model used in other Silverlight controls. The flickr symbols here has two states: MouseOver, which zooms the image and displays a small description at the bottom. The result box to the left uses the selection state when you hover on the features to first scale the image slightly and highlight the border, and if you stay hovering on it, will expand to full size. Click the result to center on that feature.

Silverlight 3.0 Pixel shaders

rippleeffect Silverlight 3.0 beta1 was released today. One of the new cool features is the pixel shader support, allowing you to make some really cool effects. I managed to fairly quickly convert the WPF Pixel Shader library to a Silverlight library (except a couple of effects), and successfully apply them to any UIElement.

To save you the trouble, you can grab the source for the library from here: ShaderEffectLibrary.zip (44.65 kb)

To use it, simply import the assembly namespace:

xmlns:fx=”clr-namespace:ShaderEffectLibrary;ShaderEffectLibrary"

and then apply any effect to your elements by setting the Effect property:

<Grid>
    <Grid.Effect>
        <fx:RippleEffect />
    </Grid.Effect>
</Grid>

The supported effects are: BandedSwirl, Bloom, BrightExtract, ColorKeyAlpha, ColorTone, ContrastAdjust, DirectionalBlur, Embossed, Gloom, GrowablePoissonDiskEffect, InvertColor, LightStreak, Magnify, Monochrome, Pinch, Pixelate, Ripple, Sharpen, SmoothMagnify, Swirl, Tone, Toon, and ZoomBlu.

Note that you will need to have the Silverlight 3.0 bits installed to use this.

UPDATE: The WPF Pixel Shader library has now added support for Silverlight as well: http://wpffx.codeplex.com/

Also note that if you want to take advantage of the new GPU hardware acceleration to further enhance performance, this is an opt-in feature! To get it, you have to go to the hosting html page and set the following object parameter (update: This doesn't apply to pixel shaders which are software only):

      <param name=”enableGPUAcceleration” value=”true” />

Also note that pixel shaders are software only, and if you add a shader to your hardware accelerated element, it will switch back to software.

Loading pages dynamically in Silverlight

I often find myself creating a large number of small test pages in my application, and every time I want to test one of them, I will have to go into App.Xaml.cs and change which page to load by setting the RootVisual.

Therefore I created a small class that allows you to on-the-fly change the currently loaded user control. It uses the #anchor tag in the URL so you can link directly to that page and use the back/forward buttons between your test pages. Furthermore, it will add a small unobtrusive 10x10px dropdown in the upper left corner. Clicking it will show you a list of all user controls available in your assembly, so you quickly can navigate to a specific user control.

To use it, first copy the code below, and add it to your project.

 

using System;
using System.Collections.Generic;
using System.Reflection;
using System.Windows;
using System.Windows.Browser;
using System.Windows.Controls;
 
namespace SharpGIS
{
    public class ControlNavigator
    {
        private string currentHash;
        private System.Windows.Application application;
        private Grid rootGrid = new Grid();
        private string defaultControlName;
 
        /// <summary>
        /// Initializes a new instance of the <see cref="ControlNavigator"/> class.
        /// </summary>
        /// <param name="application">The application to navigate.</param>
        /// <param name="defaultControl">The default control to load when no/invalid anchor is specified.</param>
        public ControlNavigator(System.Windows.Application application, UIElement defaultControl)
        {
            this.application = application;
            defaultControlName = defaultControl.GetType().FullName;
            CreateControl();
            UIElement element = null;
            if (HtmlPage.IsEnabled)
            {
                System.Windows.Threading.DispatcherTimer timer = new System.Windows.Threading.DispatcherTimer();
                timer.Interval = TimeSpan.FromMilliseconds(500);
                timer.Tick += timer_Tick;
                currentHash = GetAnchor();
                element = LoadElement(currentHash);
                timer.Start();
            }
            ShowElement(element ?? defaultControl);
        }
 
        private void CreateControl()
        {
            Grid grid = new Grid();
            grid.Children.Add(rootGrid);
            ComboBox box = new ComboBox()
            {
                Width = 10,
                Height = 10,
                Opacity = 0.5,
                ItemsSource = GetUserControls(),
                HorizontalAlignment = HorizontalAlignment.Left,
                VerticalAlignment = VerticalAlignment.Top,
                Cursor = System.Windows.Input.Cursors.Hand
            };
            box.SelectionChanged += new SelectionChangedEventHandler(box_SelectionChanged);
            grid.Children.Add(box);
            this.application.RootVisual = grid;
        }
 
        private void box_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            string value = (sender as ComboBox).SelectedItem as string;
            if (HtmlPage.IsEnabled)
                HtmlPage.Window.Navigate(new Uri("#" + value, UriKind.Relative));
            else
                if (rootGrid.Children.Count == 0 || rootGrid.Children[0].GetType().FullName != value)
                    ShowElement(LoadElement(value));
        }
 
        private void timer_Tick(object sender, EventArgs e)
        {
            string hash = GetAnchor();
            if (hash != currentHash && 
                (rootGrid.Children.Count==0 || rootGrid.Children[0].GetType().FullName!=hash))
            {
                UIElement element = LoadElement(hash);
                if (element != null)
                {
                    ShowElement(element);
                }
            }
            currentHash = hash;
        }
 
        private void ShowElement(UIElement element)
        {
            if (element == null) return;
            rootGrid.Children.Clear();
            rootGrid.Children.Add(element);
        }
 
        private UIElement LoadElement(string name)
        {
            if (string.IsNullOrEmpty(name)) name = defaultControlName;
            try
            {
                return Assembly.GetExecutingAssembly().CreateInstance(name) as UIElement;
            }
            catch (System.Exception ex)
            {
                System.Diagnostics.Debug.WriteLine(
                    string.Format("Couldn't load control '{0}':\n{1}\n{2}",
                    name, ex.Message, ex.StackTrace));
                MessageBox.Show(string.Format("Failed to load control {0}. See output for stack trace", name));
            }
            return null;
        }
 
        private static string GetAnchor()
        {
            if (HtmlPage.IsEnabled)
                return HtmlPage.Window.CurrentBookmark;
            else
                return null;
        }
 
        /// <summary>
        /// Creates a list of UserControls in this assembly that has an empty constructor
        /// </summary>
        /// <returns></returns>
        private List<string> GetUserControls()
        {
            List<string> types = new List<string>();
            Assembly assembly = System.Reflection.Assembly.GetExecutingAssembly();
            foreach (Type type in assembly.GetTypes())
            {
                if (type.IsSubclassOf(typeof(UserControl)) && type.GetConstructor(new Type[] { }) != null)
                    types.Add(type.FullName);
            }
            return types;
        }
    }
}

Go to Application_Startup() in App.Xaml.cs and change the code to:

new SharpGIS.ControlNavigator(this, new MyPage());
where MyPage() is the page control that you want to load by default.

Download code sample here

View online demo

Why EPSG:4326 is usually the wrong “projection”

…or why spherical coordinate systems are not flat!

One of the most common ways the round world is displayed on a map is using the simplest projection we have:

x = longitude
y = latitude

The name of this projection is “Plate Carree”, and is widely used because it is so simple. However we often seem to forget that we are talking about a projection. Therefore the spatial reference for this projection is very often (mis)referenced as a spherical coordinate system like the following for EPSG:4326:

GEOGCS["GCS_WGS_1984",DATUM["D_WGS_1984",SPHEROID["WGS_1984",6378137,298.257223563]],
PRIMEM["Greenwich",0],UNIT["Degree",0.017453292519943295]]

GEOGCS denotes “Geographical Coordinate System”, meaning a spherical coordinate system using angles for latitude and longitudes, not X’s and Y’s. However, the moment we project this map onto a flat screen or piece of paper, we projected the coordinate system (using the simple formula above), so how can the spatial reference of our map be the above one?

I found that in ArcMap you can manually define a projected coordinate system that renders the same map, but correctly identifies the coordinate system as being projected. Below is the Well-known-text representation of this projection string (note how PROJCS denotes projected coordinate system, and how EPSG:4326 is defined inside it):

PROJCS["World_Plate_Carree_Degrees",GEOGCS["GCS_WGS_1984",
DATUM["D_WGS_1984",SPHEROID["WGS_1984",6378137,298.257223563]],
PRIMEM["Greenwich",0],UNIT["Degree",0.017453292519943295]
],
PROJECTION["Plate_Carree"],PARAMETER["False_Easting",0],
PARAMETER["False_Northing",0],PARAMETER["Central_Meridian",0],
UNIT["Degree",111319.49079327357264771338267056]]

As far as I know we don’t have a spatial reference ID for the above (EPSG:54001 comes close but uses a different linear unit), so we often incorrectly start using EPSG:4326 to describe a projection. This has been done for so long, that this is now common (mal)practice. Even OGC’s WMS specification allows you request flat maps in a spherical coordinate system, even though this doesn’t really make any sense.

So why is this a problem? Well until recently it hasn’t really been a problem, mostly because it was rare that the spherical coordinate were correctly handled, and most applications assumed that the world was flat. However, Microsoft SQL Server 2008 can correctly handle spherical coordinates, and suddenly this becomes a major problem.

Below is a query on all the counties that follow the red line defined by two points. The blue polygons is the result returned. The map claims that its spatial reference is EPSG:4326. Because of the curvature Earth, a straight line between the two endpoints looks like a curve on the projected map, so the results returned seems like they don’t match with the line, but they are in fact the counties on the line between the two endpoints if you think in a spherical coordinate system.

image

The real problem here is not that the line is not really a curve (I drew the line like that because I want the features along that latitude). The problem is that the line didn’t know that it was projected, because the map it was displayed on was incorrectly set to EPSG:4326. However had the application known that this was not a spherical coordinate system, but a Plate Carree projection, it would have known that when converting from the flat coordinate system to the spherical coordinate system, it should ensure that my line follows the latitude. But because the input line already claims to be in spherical units, the application can not know that it needs to do anything to it (aside the fact that the application of course knows that this came from a flat screen map, but the business logic is of course separate from the UI).

This brings me to another even worse issue: I’d like to make the claim that almost all of the data you have that claims to be in EPSG:4326 has never been EPSG:4326 ! (point data excluded).

Example: Most of the northern US border roughly follows the 49’th latitude. Let’s for sake of argument define the whole northern border as two points, from coast to coast, 49 degrees north. If I then think that my data is in EPSG:4326 (or any other geographic coordinate system), the border will NOT be following the 49th latitude, but go along a great circle that cuts into Canada.

So let us thank Microsoft for creating a database that handles spherical coordinates correctly, and for giving us major headaches when trying to handle these things correctly in a clean clear way :-). Of course there wouldn’t be any headaches if we never mixed spherical and flat coordinate systems in the first place.

Silverlight and WPF code reuse pitfalls.

The idea is really great: Write your code once, and reuse it across all your platforms. This is one of the great things about .NET. I can reuse business logic in ASP.NET, Mobile, Desktop, Embedded devices, Phones, Web services and so on. Usually the only difference is the UI that behaves a little different on the various platforms.

With WPF and Silverlight, even the UI code can be reused between Desktop and Web, since Silverlight is a subset of WPF. That means that any Silverlight code should be able to run in a WPF application, right? Well at least that’s what Microsoft keeps telling us, and you will fine numerous blog posts discussing it. However, in reality it’s not that straight forward. I’ve been trying to reuse some Silverlight code in a WPF application, and ran into a lot of headaches. What I found is that Silverlight is NOT a subset of WPF and .NET. It is a completely new framework with the subset as a design goal, but Microsoft made several mistakes, some even so bad that fixing it in either WPF or Silverlight would cause a breaking change. In other cases the XAML syntax is incompatible different. This means that you won’t get around using several compiler conditionals and duplicate XAML files.

Below is a list of some of the things I’ve run into, and I’ll try and add to it if I found more. I you know of more, or find errors or explanation to differences please add comments below.

For code that is only for Silverlight I have declared a SILVERLIGHT compiler conditional. so anything inside a #if SILVERLIGHT block is Silverlight only code, and vice verse if it’s in #if !SILVERLIGHT or #else it’s for WPF.

As a side note, the following blog posts discusses some good practices for reusing the same code files in your WPF and Silverlight projects: Sharing source code between .NET and Silverlight. However it hardly deals with code differences like the ones below.

Loading a bitmap

Loading bitmaps are probably one of the biggest differences in the two frameworks (that I’ve seen). In WPF you must call BeginInit and EndInit, and checking for download errors are very different, which requires you to use two different handlers for handling an image load error.

BitmapImage bmi = new BitmapImage();
#if 
!SILVERLIGHTbmi.BeginInit();
#endif
Image img = new Image();
bmi.UriSource = new Uri(url, UriKind.Absolute);
#if
SILVERLIGHTimg.ImageFailed += new EventHandler<ExceptionRoutedEventArgs>(img_ImageFailed); //Silverlight specific error handler
#else
bmi.DownloadFailed += new EventHandler<System.Windows.Media.ExceptionEventArgs>(bmi_DownloadFailed); //WPF specific error handler
bmi.EndInit();
#endif
img.Source = bmi;

Modifying Animations

WPF is very restrictive when it comes to working with animations. When you initialize the animation, you must use an overload that takes the element you are modifying as well as specifying that its modifiable. Silverlight doesn’t have any of these overloads, and is there fore not required.

#if SILVERLIGHT
myStoryboard.Begin();
myStoryboard.Stop();
#else
myStoryboard.Begin(element, true); //true allows for changing animation later
myStoryboard.Stop(element); //element parameter required when Begin was called with element
#endif

Delay Signing

Silverlight does not support delay signing of your assemblies. This might not be a big issue for you though.

Binding to Dictionary

This is more of a subset limitation, but it’s an annoying one, that is fairly tricky to get around.

In WPF you can bind a Dictionary<string,object> object as simple as :

<TextBlock Text="{Binding Path=MyDictionary.[KeyName]}" />

However in Silverlight you have to create your own value converter:

public class DictionaryItemConverter : IValueConverter
{
   public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
   var dict = value as Dictionary<string, string>;
   if (dict != null)
   {
     return dict[parameter as string];
   }
     throw new NotImplementedException();
   }
   public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
   {
     throw new NotImplementedException();
   }
}

Using the converter you can then bind your dictionary:

<Grid x:Name="LayoutRoot" Background="White">
    <Grid.Resources>
   <local:DictionaryItemConverter x:Name="myDictConvert" />
    </Grid.Resources>
    <TextBlock Text="{Binding Converter={StaticResource myDictConvert}, ConverterParameter=KeyName}" />
</Grid>

(note that you will have to define the local: namespace to point to the namespace/assembly where DictionaryItemConverters is placed).

Value converters are pretty powerful though, so this lesson might come in handy later. This approach does work for WPF too.

Accessing resources declared in XAML from code

Accessing resources you have in your XAML is a common pitfall. If I were to declare a resource in a grid like this:

<Grid x:Name="LayoutRoot">
    <Grid.Resources>
   <local:MyClass x:Key="myResource" />
    </Grid.Resources>
</Grid>

In WPF you would access it like this:

object o = LayoutRoot.Resources["myResource"];

However in Silverlight that would return null! Instead in Silverlight you have to use x:Name instead of x:Key in your XAML:

<local:MyClass x:Name="myResource" />

Unfortunately in Silverlight you can’t declare both a Key and a Name in your XAML (you can in WPF), so you will have to maintain two different XAML files. In WPF you are required to specify a x:Key attribute, so you can’t just make do with the name attribute either.

Control Templates

When you create a control template, you will have to assign the type that the control template belongs to. Here’s the syntax in Silverlight:

<ControlTemplate TargetType="local:MyControl">

And in WPF:

<ControlTemplate TargetType="{x:Type local:MyControl}">

Debug.WriteLine

This one is a little interesting, because it's a method in Silverlight that proves that it’s not necessarily a subset of WPF, since here’s a method that actually have different overloads in Silverlight and WPF. I often use System.Diagnostics.Debug.WriteLine to write out values or warnings that I or the developer should be aware of, but not necessarily is an error.

Here are the overloads in Silverlight:

public static void WriteLine(object value);
public static void WriteLine(string message);
public static void WriteLine(string format, params object[] args); //NOT IN WPF! 

and WPF:

public static void WriteLine(object value);
public static void WriteLine(string message);
public static void WriteLine(object value, string category);
public static void WriteLine(string message, string category); 

Notice that the 3rd method in Silverlight which is equivalent of using string.Format, doesn’t exist in WPF. Therefore always use WriteLine(string.Format(format,args)) instead.

OnApplyTemplate fired in different order

The OnApplyTemplate call on your controls are fired at different times in WPF and Silverlight. This can cause a lot of problems if you rely on certain events to have happened before the OnApplyTemplate event has triggered, or vice versa. This is a case where you have to be really careful with code-reuse in Silverlight and WPF.

In the example below I created a simple sample and ran it in Silverlight and WPF, with breakpoints in each loaded handler, constructor and OnApplyTemplate in my custom control.  The XAML is below (simplified and removed namespace declarations for readability):

<UserControl Loaded="UserControl_Loaded”>     <my:Control Loaded="MyControl_Loaded /> </UserControl>

Order the methods were hit:

  Silverlight WPF
1. UserControl Constructor UserControl Constructor
2. MyControl Constructor MyControl Constructor
3. MyControl Loaded MyControl.OnApplyTemplate
4. UserControl Loaded UserControl Loaded
5. MyControl.OnApplyTemplate MyControl Loaded

See this post for a workaround.

Case sensitivity

Silverlight in general seems less restrictive when it comes to your XAML. For instance case sensitivity. I was recently trying to use a class modifier on my UserControl using the following:

<UserControl x:ClassModifier=”Internal”>

However this doesn’t work in WPF. It turns out that the “internal” keyword must be lowercase in WPF.

--------------------------

Jeff Wilcox also has a list of issues that he hit in his gravatar project described in this blog post: http://www.jeff.wilcox.name/2009/03/gravatar-control/

Doubleclicking in Silverlight

Mike Snow recently posted an article on how to do double clicks in Silverlight. However, his approach isn't very reusable, doesn't allow for multiple listeners and doesn't check to see if the mouse moved between the two clicks. Below is my solution, which uses extension methods and attached properties to track clicks and event handlers.

To use it, first include the namespace in your code:

using MyApplication.Extensions;

This will add AddDoubleClick and RemoveDoubleClick methods to any UIElement. You can then add an event handler to your element like so:

MyDblClickElement.AddDoubleClick(MyDblClickElement_MouseDoubleClick);

The double click handler signature is the same as for mouse down. Example:

private void MyDblClickElement_MouseDoubleClick(object sender, MouseButtonEventArgs e)
{
Point position = e.GetPosition(this);
MessageBox.Show(string.Format("dblclick at {0},{1}", position.X, position.Y));
} 

Here's the code:

using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Input;
using System.Windows.Threading;
namespace MyApplication.Extensions
{
public static class Mouse
{
private const int doubleClickInterval = 200;
private static readonly DependencyProperty DoubleClickTimerProperty = DependencyProperty.RegisterAttached("DoubleClickTimer", typeof(DispatcherTimer), typeof(UIElement), null);
private static readonly DependencyProperty DoubleClickHandlersProperty = DependencyProperty.RegisterAttached("DoubleClickHandlers", typeof(List<MouseButtonEventHandler>), typeof(UIElement), null);
private static readonly DependencyProperty DoubleClickPositionProperty = DependencyProperty.RegisterAttached("DoubleClickPosition", typeof(Point), typeof(UIElement), null);
/// <summary>
/// Adds a double click event handler.
/// </summary>
/// <param name="element">The Element to listen for double clicks on.</param>
/// <param name="handler">The handler.</param>
public static void AddDoubleClick(this UIElement element, MouseButtonEventHandler handler)
{
element.MouseLeftButtonDown += element_MouseLeftButtonDown;
List<MouseButtonEventHandler> handlers;
handlers = element.GetValue(DoubleClickHandlersProperty) as List<MouseButtonEventHandler>;
if (handlers == null)
{
handlers = new List<MouseButtonEventHandler>();
element.SetValue(DoubleClickHandlersProperty, handlers);
}
handlers.Add(handler);
}
/// <summary>
/// Removes a double click event handler.
/// </summary>
/// <param name="element">The element.</param>
/// <param name="handler">The handler.</param>
public static void RemoveDoubleClick(this UIElement element, MouseButtonEventHandler handler)
{
element.MouseLeftButtonDown -= element_MouseLeftButtonDown;
List<MouseButtonEventHandler> handlers = element.GetValue(DoubleClickHandlersProperty) as List<MouseButtonEventHandler>;
if (handlers != null)
{
handlers.Remove(handler);
if(handlers.Count == 0)
element.ClearValue(DoubleClickHandlersProperty);
}
}
private static void element_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
UIElement element = sender as UIElement;
Point position = e.GetPosition(element);
DispatcherTimer timer = element.GetValue(DoubleClickTimerProperty) as DispatcherTimer;
if (timer != null) //DblClick
{
timer.Stop();
Point oldPosition = (Point)element.GetValue(DoubleClickPositionProperty);
element.ClearValue(DoubleClickTimerProperty);
element.ClearValue(DoubleClickPositionProperty);
if (Math.Abs(oldPosition.X - position.X) < 1 && Math.Abs(oldPosition.Y - position.Y) < 1) //mouse didn't move => Valid double click
{
List<MouseButtonEventHandler> handlers = element.GetValue(DoubleClickHandlersProperty) as List<MouseButtonEventHandler>;
if (handlers != null)
{
foreach (MouseButtonEventHandler handler in handlers)
{
handler(sender, e);
}
}
return;
}
}
//First click or mouse moved. Start a new timer
timer = new DispatcherTimer() { Interval = TimeSpan.FromMilliseconds(doubleClickInterval) };
timer.Tick += new EventHandler((s, args) =>
{  //DblClick timed out
(s as DispatcherTimer).Stop();
element.ClearValue(DoubleClickTimerProperty); //clear timer
element.ClearValue(DoubleClickPositionProperty); //clear first click position
});
element.SetValue(DoubleClickTimerProperty, timer);
element.SetValue(DoubleClickPositionProperty, position);
timer.Start();
}
}
}

Impressions from Microsoft PDC

I attended the Microsoft PDC in Los Angeles Convention Center this week, and here's a recap of various statements, quotes and sessions that I found interesting.

 

WCF Preconference notes

It looks like a class, it feels like a class, it smells like a class, it works like a class – but it’s a service!

This was a full day seminar on Windows Communication Foundation. It was very cool to see how ordinary looking classes could be turned into WCF services that can be exposed through a multitude of protocols using just two class/method attributes. One of the interesting statements were how .NET had replaced COM 7 years after its introduction, and now 8 years later, WCF is replacing .NET (well at least the part that replaced COM). Or to put it in the presenters word: ".NET as you know it is dead!"

WCF is host agnostic. No difference between hosting on IIS, in-proc, self-hosting, Windows Activation Service etc.

Possible transport protocols: HTTP, HTTPS, TCP, P2P, IPC, MSMQ.
Possible message formats: Plain text, Binary, MTOM.
Security options: None, Transport security, Message security, Authentication and authorizing callers.

WCF is the first platform that provides Interoperability, Productivity/Quality AND Extensibility all at once.

Favorite sessions

I must admit that many of the sessions were a little dissappointing. They were often marked as advanced or expert level, but rarely lived up to that. There were also way too many Twitter demos. But having said that, here are the ones I attended that didn't dissappoint. You can watch them all online by clicking the links next to them or see the whole list of sessions here.

  • TL16: "The Future of C#" by Anders Hejlsberg. My Danish hero and the brain behind C# talks about where C# is headed with upcoming v4 and 5 of C#. View
  • TL49: "Microsoft .NET Framework: Overview and Applications for Babies" by Scott Hanselman. Using a a wide varity of .NET core technologies, Scott takes his BabySmash application and ports it to WPF, Silverlight, Mobile and Surface, in his usual entertaining way. Great demonstration of the power of .NET. View
  • PC06: "Deep Dive: Building an Optimized, Graphics-Intensive Application in Microsoft Silverlight" by Seema Ramchandani. Seema goes through all the gory internal details of how Silverlight works and uses this knowledge to present a great set of tips and tricks to making the performance of your Silverlight applications scream. A must-see if you are doing serious work with Silverlight. View
  • TL26: "Parallel Programming for Managed Developers with the Next Version of Microsoft Visual Studio" by Daniel Moth. Processors are not really getting much faster anymore, but instead we get more and more cores to work with, but this also requires us to start changing the way we make software. The Parallel Framework that comes in .NET 4 makes this task really easy. All I can say is that this framework rocks! View
  • BB24: "SQL Server 2008: Deep Dive into Spatial Data" by Isaac Kunen. Isaac's deep dive on the spatial support in SQL Server (thanks for the plug Isaac :-). View
  • PC29: "Microsoft Silverlight 2: Control Model" by Karen Corby. Good information on how to build reusable, skinnable controls for Silverlight. View
  • PC32: "ASP.NET AJAX Futures" by Bertrand Le Roy. Upcoming features for ASP.NET AJAX. View
  • TL46: "Microsoft Visual C# IDE: Tips and Tricks" by Dustin Campbell. LOTS of great shortcuts and features in Visual Studio that you never knew was there and you wonder how you could ever live without. MUST SEE if you want to be more efficient when coding C# in Visual Studio. View
There were a lot of sessions I didn't make it to, but hopefully I'll get some time to view them online over the next coming weeks. If I see anything more that I like, I'll update this list. If you have watched any good sessions as well, please feel free to mention it in the comments.

 

My favorite comment was Juval Lowy's reaction when he the first time heard that Microsoft was working on the successor to VB6:

Giving VB developers access to a multithreaded environment (VB.NET) is like giving razorblades to babies.

It’s not that C++ developers are better off with C#, but they are more used to seeing blood.

 

Microsoft is already planning a new PDC next year November 17-20, 2009 (unfortunately same place).

Yet another map data API released

Blom just announced the release of their BlomURBEX (Urban Explorer) SDK. It includes access to high resolution ortho and SEAMLESS birds eye imagery of over 1000 european cities. These are the same images as Microsoft uses on the Virtual Earth platform, but the unique is that they have managed to seamlessly stich the birds eye imagery together seamlessly, making the navigation much more intuitive.

They also released a free iPhone/iPod Touch app "Demyko" that can be used to navigate ortho images and North/South/East/West facing oblique imagery with the touch of a finger. I can´t say I like the fancy way they show the tiles (they rotate in), but the dataset is impressive.

Read the full press release here.

 

First country to be fully mapped in 3D

My former employer just announced that they have mapped all of Denmark's buildings and terrain in 3D. Denmark is small, but it’s still an amazing feat, considering its roughly 2.5 million buildings!

The modeling is mostly based on LIDAR data. I’ve seen several attempts to do this, but they have always resulted in either ugly or oversimplified buildings with flat roofs, or even worse made up of so many redundant points that no PC were able to show more than a few buildings at a time. This is the first fully automated attempt I’ve seen that comes out with a convincing result, and the scale of this is breathtaking. Considering they also have full terrain and surface model coverage, full high resolution ortho imagery coverage and lots of Pictometry (Birds Eye) imagery they can drape onto the facades, this makes for an impressive 3D model.

Hopefully there will soon be some more detailed images to share after the unvailing next week at the Danish geodata convention, but below are a few images I was able to find from a danish news site and an ad.

You can also find more details at Asger's blog.