×
Namespaces

Variants
Actions
Revision as of 19:16, 9 November 2012 by KooKiz (Talk | contribs)

Maintaining a WP7 and WP8 version of a same Silverlight application

From Nokia Developer Wiki
Jump to: navigation, search

This article explains how to easily maintain a Windows Phone 8 version of an application along the legacy Windows Phone 7 version.

Note.pngNote: This is a community entry in the Windows Phone 8 Wiki Competition 2012Q4.

SignpostIcon XAML 40.png
WP Metro Icon WP8.png
SignpostIcon WP7 70px.png
Article Metadata
CompatibilityArticle
Created: (15 Nov 2012)
Last edited: KooKiz (09 Nov 2012)

Contents

Introduction

Now that Windows Phone 8 is available, a new, dreaded challenge arises for Windows Phone developer: fragmentation. Windows Phone 8 offers a large set of new features to provide great user experience in your apps, but many users remain with Windows Phone 7. Making a WP8-only app would greatly reduce the user base. So, in many cases, you’ll end up needing to maintain two applications, a WP7 version and a WP8 version. How do we make that easier to manage?

There’s no silver bullet here, but a list of tips you can use and combine depending on your needs. Those are:

  • Sharing code files between your projects
  • Adding async/await support to your Windows Phone 7 project
  • Use of conditional symbols to add platform-specific pieces of code
  • Set custom styles
  • Using a custom URI mapper to redirect to specific XAML page depending on the platform


Setting up the projects and sharing code files

First step, create the actual Windows Phone 8 project. Open your Windows Phone 7 solution, and add a new project targeting Windows Phone 8.

AddNewWP8Project.png

Remove the default MainPage.xaml/cs files from the project, then link the pages and classes from your WP7 project to your new project. How do you do that?

  • Right-click on the project
  • Choose ‘Add’ then ‘Existing item’
  • Go to the WP7 project folder, select all the XAML and C# files you want to share
  • Click on the small arrow on the ‘Add’ button
  • Select ‘Add as link’

I recommend not sharing the App.xaml and App.xaml.cs files, as it may actually make some things easier to do, like customizing the styles.

AddWP7Files.png

A word of advice: You’ll most likely want to share some XAML pages and their .cs code-behind file. If you add both files as link, they won’t appear grouped in Visual Studio’s solution explorer:

SolutionExplorerFilesNotGrouped.png

If you want them to be grouped, like they were in the original WP7 solution, you can either just add the .xaml file (Visual Studio will automatically add the .cs file), or use the free Visual Studio extension called VSCommands. It adds an option to the contextual menu that you can use to group your files. Just select the .xaml and .cs file, right-click, and click on ‘Group items’:

ContextualMenuGroupItems.png

Then select the .xaml file as root item:

VSCommandsGroupItems.png

Click on ‘Ok’, and the files are now grouped!

SolutionExplorerFilesGrouped.png

Now the physical files you’ve selected are shared between the two projects. It means that if you make changes to one of those files in a project, it’ll be automatically updated in the other project (since it’s actually the same file).

Up to this point, you should be able to compile the WP8 project (after adding the missing references and updating the manifest if needed) and run it. Now let’s start adding specific features to the WP8 project.

async/await

One of the best new feature of .NET 4.5 has been made available for Windows Phone 8: the async and await keywords. Thankfully, the BCL team has published a NuGet package to make those keywords available for WP7 as well.

Installation steps are detailed in this blog post.

Overall, all you need to do is to search “Microsoft.Bcl.Async” in NuGet, and install the “Async for .NET Framework 4, Silverlight 4 and 5, and Windows Phone 7.5” package. You can also install it directly from the NuGet console by typing:

install-package Microsoft.Bcl.Async –pre

NuGetAsyncAwait.png

Once the package is installed, you can use tasks and the async/await keywords from both the WP7 and WP8 projects!

== Use of conditional symbols for platform-specific code

It’s no surprise that there will come a moment when you’ll be using APIs that can’t be back-ported to WP7. How do we call those new methods without creating compilation errors in the WP7 project? By using preprocessor directives.

Right-click on the WP8 project, go in the properties, then in the Build tab. There, you can see a ‘Conditional compilation symbols’ textbox, with per default “SILVERLIGHT;WINDOWS_PHONE”. Edit it to add “WP8”. The new complete value should then be “SILVERLIGHT;WINDOWS_PHONE;WP8”.

ConditionalSymbols.png

Now, whenever you use a specific WP8 API, you can enclose your code using the directives “#IF WP8” and “#endif”. That way, those portions of code will be ignored when compiling the WP7 project!

For instance, let’s say we want to call the new ‘Application.Terminate’ API only when executing the WP8 app. Then this code should do the trick:

#if WP8
Application.Current.Terminate();
#endif

Custom Styles

You may want to apply different styles to your XAML page depending on whether you’re aiming WP7 or WP8, for instance to hide buttons. If your app.xaml files aren’t shared, then you can simply declare your styles there in that project. However, if they are shared, you can reference an external resource dictionary.

On both projects:

  • Right-click on the project
  • Select “Add”, then “New Item”
  • Select “Text file” and name it “Styles.xaml”

Then, in the solution explorer, select the file, look at the properties, and set the ‘build action’ to ‘Content’.

Open your two “Styles.xaml” files, and add the following XAML code:

<ResourceDictionary xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
 
</ResourceDictionary>

Then, open the App.xaml file, and reference the newly created ResourceDictionary:

<Application.Resources>   
<ResourceDictionary Source="Styles.xaml" />
</Application.Resources>

And you’re done! Now each project has its separate “Styles.xaml” file where you can put your custom styles. How does it work? Let’s say you’ve got a button that you want to style differently depending on whether you’re executing the WP7 or the WP8 app.

First, apply the style to the button:

<Button Style="{StaticResource TestStyle}" x:Name="ButtonContinue" Content="Continue" />

Then create the corresponding style in the “Styles.xaml”. For instance, in the WP7 one:

<ResourceDictionary xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<Style x:Key="TestStyle" TargetType="Button">
<Setter Property="Foreground" Value="Blue" />
</Style>
</ResourceDictionary>

Then in the WP8 one:

<ResourceDictionary xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<Style x:Key="TestStyle" TargetType="Button">
<Setter Property="Foreground" Value="Red" />
</Style>
</ResourceDictionary>

Execute the project, and you’ll see that the button’s text is displayed in blue when executing the WP7 app, and in red when executing WP8 app. It’s a convenient way to display or hide UI elements depending on which application is running.

Specific pages and URI mapper

Sometimes, conditional symbols and custom styles just aren’t enough, it may be easier to create a whole new XAML page for Windows Phone 8.

There’s two main cases where you could want to use specific pages:

1. You’re adding a completely new feature to your app, only for Windows Phone 8. For instance, if you want to add NFC sharing which isn’t supported by Windows Phone 7. Then there’s no point in sharing a common XAML file between WP7 and WP8 since the WP7 one won’t be able to do anything.

In that case, it’s rather straightforward: just add the new page to the WP8 project. Then, use conditional symbols or styles to add the button to navigate to that page.

2. You’re dealing with features that exist in both version of your app, but the WP8 version requires a lot of specific code. For instance, if you’re rewriting a page to use the brand new map control. In that case, you’d end up enclosing most of your page’s code in conditional symbols, and it’ll quickly become a nightmare to maintain. In that case, you’re better out creating a complete new page.

The obvious way to do that is to remove the linked version of the file, and add a project-specific file (as you would do in a ‘normal’ project). However, you’ll end up with files named the same way in your WP7 and WP8 projects, some of them being shared and the others not. To avoid any confusion, it’s better to give them different names.

So let’s say we have a page called ‘Test.xaml’ in our WP7 project. We want to completely rewrite this page in the WP8 project. First, we add a new page called ‘TestWP8.xaml’.

AddNewPageWP8Project.png

Now, at some point in the WP7 project, you want to redirect the user to this page:

NavigationService.Navigate(new Uri("/Test.xaml", UriKind.Relative));

How to redirect to TestWP8.xaml in the WP8 version of the application? If you’ve read the previous chapters, you know you can do that using conditional symbols:

#if WP8
NavigationService.Navigate(new Uri("/TestWP8.xaml", UriKind.Relative));
#else
NavigationService.Navigate(new Uri("/Test.xaml", UriKind.Relative));
#endif

However, you may want to handle those specific redirections in a centralized way, especially if there’s many way in the app to reach the Test.xaml page. We can do that using a custom URI mapper.

What is a URI mapper? It’s a class used by Silverlight to associate a page to an URI. When you call ‘NavigationService.Navigate’, the framework will use the URI mapper to which page is designated by your URI.

Making a custom URI mapper is quite simple. Add a new class to the WP8 project, and call it ‘CustomUriMapper’. The class must inherit from ‘UriMapperBase’. Then we override the ‘MapUri’ method and we check which URI is called. If it’s ‘Test.xaml’, then we redirect to our new ‘TestWP8.xaml’ page. Otherwise, we just return the original URI:

public class CustomUriMapper : System.Windows.Navigation.UriMapperBase
{
public override Uri MapUri(Uri uri)
{
if (uri.OriginalString.StartsWith("/Test.xaml"))
{
return new Uri("/TestWP8.xaml", UriKind.Relative);
}
 
return uri;
}
}

Now, we just need to replace the original URI mapper by our custom one. In the App.xaml.cs file, in the constructor, search the line calling the ‘InitializePhoneApplication’ method. Just after that line, replace the URI mapper of the root frame:

// Phone-specific initialization
InitializePhoneApplication();
 
// Replace the default URI mapper
RootFrame.UriMapper = new CustomUriMapper();

And we’re done! Each time the application will try to navigate to Test.xaml, it’ll be automatically redirected to TestWP8.xaml.

Summary

Maintaining a project for two different platforms will always be a though task, but by using all those little tricks you should be able to make things easier. Don’t forget to thoroughly test both the WP7 and WP8 version of your apps, as there may be slight behavior changes on some common APIs. When you’re confident enough about the quality of your application, the last step is to publish your .xap file. The Windows Phone Dev Center allows you to independently update the WP7 version of your app even after you’ve published a WP8 version, so you always have the opportunity to publish fixes or whole new features for your legacy user-base.

380 page views in the last 30 days.
×