In-Depth
Porting a Silverlight App to a Metro-Style App
Many developers are worried about the compatibility of Silverlight with Metro-style applications. This project shows that those fears are overblown.
- By Michael Crump
- 03/01/2012
Windows 8, which was introduced at the Microsoft BUILD conference last year, is an exciting new OS that introduced a new UI as well as a new runtime: Windows Runtime, called WinRT. It's the backbone of the new Metro experience in Windows 8. Like many others, I've been playing with Windows 8 and kept hearing that Silverlight skills can be reused in Metro-style applications. I decided to find out for myself by taking a Silverlight 2 project and seeing how easy it was to port to a Windows 8 Metro application using C#/XAML.
Enter Silverlight 2
Several years ago, Scott Guthrie, corporate vice president of Microsoft Server & Tools Business, posted a seven-part series he called "First Look at Silverlight 2". The series walked readers through building a Silverlight 2 application. Some of the important concepts taught in the series included:
- Layout Management
- Networking
- ListBox and DataBinding
- User Controls
- Control Templates
At the end of the series, the completed project looked like Figure 1.
[Click on image for larger view.] |
Figure 1. The Silverlight 2 Digg Client in its completed form. |
If the user clicked on an item, Figure 2 was the result.
[Click on image for larger view.] |
Figure 2. The Silverlight 2 Digg Client after a user clicked on an item. |
Finally, clicking on the title would launch the default browser with the current story.
I was interested in seeing just how easy it would be to do a direct port of this code over to a Metro application. Note that I'm not trying to make this application fit the Metro guidelines in Windows 8. I simply want to run this as a Metro application in Windows 8 using C#/XAML.
The Initial Assessment
The first step was to download a completed version of Guthrie's Digg client sample, available here, and uncompress it to a temp folder. I then navigated to the \DiggSample_CSharp\DiggSample folder and inspected the files shown in Figure 3.
[Click on image for larger view.] |
Figure 3. The DiggSample Silverlight 2 project uncompressed to a file folder. |
After looking through the file structure, I decided I'd need only the following files:
- App.xaml. Application Initialization, which contains all of the Styles the application is using. (Note: App.xaml.cs wasn't needed, as it didn't contain any custom code.)
- DiggStory.cs. Contains a class called DiggStory with each element that will be used later in the LINQ statement.
- Page.xaml/Page.xaml.cs. The UI shown in Figure 1 with code that will definitely have to change because it uses WebClient.
- StoryDetailsView.xaml/StoryDetailsView.xaml.cs. The UI shown after a user selects an item. The codebehind looks simple and won't have to change.
Download and Installation
Using the Windows 8 Developer Preview, I launched Visual Studio 11 and selected Windows Metro style/Application and named it DiggSample (just like the original project), as shown in Figure 4.
[Click on image for larger view.] |
Figure 4. A new Windows Metro-style project in Visual Studio 11. |
I decided to start with the DiggStory.cs file first because it was a simple class that wouldn't need any modification. Listing 1 shows the class.
Because my project is also named DiggSample, all I had to do was copy and paste the class into my project. Right off the bat I thought I'd have to fix my namespaces. Here are the default Metro application XML namespaces:
<Application xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="DiggSample.App" />
And the DiggStory Silverlight application XML namespaces:
<Application xmlns="http://schemas.microsoft.com/client/2007"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="DiggSample.App">
The only difference is the first XML namespace. Because I'm creating a Metro application, I was able to leave the Metro application XML namespace untouched. The only thing I needed to do was copy <Application.Resources> out of the Silverlight application and into my Metro application.
I hit "build" and received the error shown in Figure 5.
[Click on image for larger view.] |
Figure 5. The Error List window detailing an unknown type in RadialGradientBrush. |
After searching the Web, I found that RadialGradientBrush isn't included in the current build (the reasoning pertains to GPU acceleration, as explained in the MSDN Forums). Nor is it supported in the Microsoft .NET Framework 4.5.
Instead of the RadialGradientBrush, I decided to use the LinearGradientBrush for this sample.
I replaced this code:
<RadialGradientBrush GradientOrigin=".3, .3">
<GradientStop Color="#FFF" Offset=".15"/>
<GradientStop Color="#777" Offset="1"/>
</RadialGradientBrush>
With this:
<LinearGradientBrush>
<GradientStop Color="#FFF" Offset=".15"/>
<GradientStop Color="#777" Offset="1"/>
</LinearGradientBrush>
This resulted in a successful build.
Next, I added a new User Control called StoryDetailsView. I then opened the existing StoryDetailsView.xaml from the DiggStory solution and noticed the XML namespace was identical to the default Metro application. So I copied and pasted the entire StoryDetailsView.xaml inside my Metro application and hit "build" again. I was immediately greeted with the error shown in Figure 6.
[Click on image for larger view.] |
Figure 6. The Error List window detailing several unknown members. |
There's an error stating that the NavigateUri doesn't exist on HyperlinkButton. It existed in Silverlight and Windows Phone, so where is it in WinRT?
This is where I discovered the differences in the XML namespaces being used. Hovering on top of the HyperlinkButton brings up the text shown in Figure 7.
[Click on image for larger view.] |
Figure 7. The Windows.UI.Xaml.Controls.HyperlinkButton in Windows Runtime. |
This demonstrates that a Metro-based Hyperlink class inherits the ButtonBase class without any special properties or events, such as NavigateUri. I can quickly fix this by removing NavigateUri and adding a Click Event Handler that will navigate to the Web site in the default browser. Here's how to fix it:
<HyperlinkButton x:Name="hlbStoryTitle" Content="{Binding Title}"
Click="HyperlinkButton_Click" Style="{StaticResource TitleLink}"
Tag="{Binding HrefLink}" />
Notice the Tag on the HyperlinkButton to pass the current URL. If I add the event handler and build the project again, it will compile successfully.
Next, I needed to add in our event handler for the HyperlinkButton and copy/paste the existing Close Button event handler:
void HyperlinkButton_Click(object sender, RoutedEventArgs e)
{
Windows.System.Launcher.LaunchDefaultProgram(
new Uri(hlbStoryTitle.Tag.ToString(), UriKind.RelativeOrAbsolute));
}
void CloseBtn_Click(object sender, RoutedEventArgs e)
{
Visibility = Visibility.Collapsed;
}
By using Windows.System.Launcher.LaunchDefaultProgram, I was able to pass it a URI so it automatically launches the default browser. This method can only be called from a click event or some other user interaction.
In Silverlight 2, the MainPage was just called Page.xaml. This changed in Silverlight 3 with the name MainPage.xaml (which is also what Metro applications use).
With that out of the way, let's look at the XML namespaces again.
The Page.xaml inside the Silverlight application looks like this:
<UserControl x:Class="DiggSample.Page"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:Digg="clr-namespace:DiggSample">
I was able to copy and paste the entire Page.xaml inside of my MainPage.xaml file and fix the following namespaces for the Metro application:
<UserControl x:Class="DiggSample.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:Digg="using:DiggSample">
Notice that DiggSample.Page turned into DiggSample.MainPage (remember what I said earlier about Silverlight 2?), and instead of using "clr-namespace" I used the "using" statement in WinRT applications.
If I run the application, it won't compile because I don't have event handlers set up for the buttons. That's OK for the meantime.
Here are the existing methods in MainPage.xaml.cs:
- searchBtn_Click. I know I'll need to see if the Digg API has changed and that I can't use WebClient in Metro applications.
- DiggService_DownloadStoriesCompleted. Will more than likely be removed because it's part of the WebClient.
- DisplayStories. Can be reused as long as the Digg API didn't change.
- StoriesList_SelectionChanged. Can be reused completely.
Initial Assessment of the Digg API
One thing I had to research was the Digg API. I assumed (correctly) that it had changed since 2008. But what had changed?
In Scott Guthrie's example, it calls the following URL:
http://services.digg.com/stories/topic/{0}?count=20&appkey=http://www.scottgu.com
Here, {0} is the name of the search term.
I tried that URL and found out it returns nothing. After reading the Digg API -- which is deprecated again -- I found that it's changed to the following:
http://services.digg.com/search/stories?query={0}&appkey=http://www.scottgu.com
Again, {0} is the name of the search term.
So I replaced the URL with the new one, leaving the appkey as is. (Request your own appkey if you're planning on using the digg API in your own applications.)
The next review item was the XML returned by the service, to see how well it matched the DisplayStories method. Listing 2 is sample XML returned by the Digg API using the service mentioned earlier.
I took each item from the DiggStory class, made sure it still existed and that the data type was correct. The only item that concerned me was the ID, as Guthrie's sample code cast ID as an integer. From looking at some random sample data it appears the ID is no longer an integer. I did several Google searches and others had hit this issue and recommended using a string, which I did.
The Search Button Event Handler
The existing search button event handler looked like Listing 3 in Silverlight.
I changed it to what's shown in Listing 4.
The only differences are marking the method as async, and instead of using WebClient I used HttpClient, and for the response I used HttpResponseMessage. I then passed the responseString into the existing DisplayStories that Guthrie had built. The DiggService_DownloadStoriesCompleted method was no longer needed, so it was removed.
DisplayStories Method
Because I knew I'd have problems with ID, I simply changed it from an integer to a string, as shown in Listing 5.
This caused a ripple effect and broke DiggStory.cs, so I changed ID to a string here as well. The result is shown in Listing 6.
I was all set, so I built the solution and everything compiled successfully.
Then the moment of truth: Run it!
After running the application, it appeared just like it did with the Silverlight application. Figure 8 shows the UI, waiting for input.
[Click on image for larger view.] |
Figure 8. The Windows Simulator running the Metro version of the Digg Client Sample, waiting for user input. |
I typed "Microsoft" into the search box and noticed I had the clear text option as well.
I then hit "Search"; the results are shown in Figure 9.
[Click on image for larger view.] |
Figure 9. Data being returned to the Digg Sample Metro application. |
I then selected an item and got the window shown in Figure 10.
[Click on image for larger view.] |
Figure 10. A selected item in the Digg Sample Metro application. |
Clicking on an item title will launch the story in Internet Explorer.
I then took Guthrie's existing Silverlight 2 application and updated the Digg API; it immediately worked in the Windows 8 Desktop Mode.
The Best of Both Worlds
Every Microsoft-focused developer should be learning about Metro applications. It's also important that Silverlight developers begin to understand how to work with this new technology using their existing skillset.
What did I learn from this exercise?
- XAML is XAML. If you can write XAML in Silverlight, you can easily port that code to Metro applications.
- It's safe to write a Silverlight application today and port it over four years from now when Metro becomes more mainstream.
- The crucial factors are the differences in XML namespaces and the asynchronous nature of Metro applications.
Silverlight developers have the best of both worlds. They can create an application in native Silverlight and easily port it to Metro, or run it on the Windows 8 Desktop. HTML5 developers don't get this luxury.