Porting a Windows Phone Silverlight project to a Windows Runtime 8 project
[This article is for Windows 8.x and Windows Phone 8.x developers writing Windows Runtime apps. If you’re developing for Windows 10, see the latest documentation]
Note For info about porting to a Universal Windows Platform (UWP) app for Windows 10, see Porting the project.
You begin the porting process by creating a new Windows Runtime project (a type of Store project) in Visual Studio and copying your files into it. If you choose to create a Universal App project then you'll be able to support PCs, tablets, and phones from one code base.
Create the project and copy files to it
- Launch Microsoft Visual Studio 2013 and create a new Universal App project: either the Blank or Hub variety. Even if you're not yet targeting both Windows and Windows Phone, you can still use the Universal App project template. Alternatively, you can use one of the Windows Apps or Windows Phone Apps project templates. Virtually all of what follows will apply in any case. For more info, see Jumpstart your Windows Store app using templates (C#, C++, Visual Basic).
- Your new Universal App project contains three sub-projects: one that builds an app package for Windows (an appx file), one that builds an app package for Windows Phone (also an appx file), and one that contains source code and other files that are visible to, and shared by, both the Windows and the Windows Phone sub-projects.
- In your Windows Phone Silverlight app project, identify all the source code files and visual asset files that you want to reuse. Using File Explorer, copy data models, view models, visual assets, Resource Dictionaries, folder structure, and anything else that you wish to share, to the Shared project folder of your Universal App project. Copy or create sub-folders on disk as necessary.
- Copy views (for example, MainPage.xaml and MainPage.xaml.cs) into the Shared project node, too. You can be optimistic, and assume that your views are shareable—or can conveniently be made so—unless or until you find otherwise. As you gain experience with Universal Windows apps, you'll improve in your ability to gauge the best node in which to put a view. If, using the techniques described in this guide, you find that a view can't be further refactored for re-use then you can move different versions of it into the Windows and the Windows Phone project nodes. Again, create new sub-folders as necessary, and remove the existing views from the Windows and the Windows Phone project nodes. But before you over-write or remove a view that Visual Studio generated, keep a copy because it may be useful to refer to it later. The first phase of porting a Windows Phone Silverlight app focuses on the Windows Phone Store app. Later, you'll turn your attention to generalizing the code, including the views, to support the Windows Store app also.
- In Solution Explorer, make sure Show All Files is toggled on. Select the files that you copied, right-click them, and click Include In Project. This will automatically include their containing folders. You can then toggle Show All Files off if you like. An alternative workflow, if you prefer, is to use the Add Existing Item command, having created any necessary sub-folders in the Visual Studio Solution Explorer. Double-check that your visual assets have Build Action set to Content and Copy to Output Directory set to Do not copy.
- The differences in namespace and class names will generate lots of build errors at this stage. For example, if you open the views that Visual Studio generated, you'll see that they are of type Page, and not PhoneApplicationPage. There are lots of XAML markup and imperative code differences that the following topics in this porting guide cover in detail. But you'll make fast progress just following these general steps: change "clr-namespace" to "using" in your namespace prefix declarations in XAML markup; use the Windows Phone Silverlight to Windows Runtime namespace and class mappings topic and Visual Studio's Find and Replace command to make bulk changes to your source code (for example, replace "System.Windows" with "Windows.UI.Xaml"); and in the imperative code editor in Visual Studio use the Resolve and Organize Usings commands on the context menu for more targeted changes.
Maximizing markup and code reuse
You will find that refactoring a little, and/or adding conditional logic, will allow you to maximize the markup and code in the Shared project node. Here are more details on the techniques for sharing code between Windows and Windows Phone.
- In general, files that are common to both Windows and Windows Phone can be kept in the Shared project. This includes XAML markup files, imperative source code files, and asset files. Files that are specific to one platform or the other should be kept in that project. But since you as the developer control the contents of source code files, by refactoring you can control whether a file is common, or is platform-specific.
- When it comes to an imperative source code file, "common" and "platform-specific" needn't mean the entire file. If you can factor your code so that most, if not all, of a file will work on both platforms then you don't need to have two copies of it. You can wrap any platform-specific logic in the file inside conditional compilation directives where possible, or run-time conditions where necessary. That only works for imperative code (C#, C++, and Visual Basic). See the next section below, and C# Preprocessor Directives. There's no conditional compilation for XAML markup, but there are other techniques you can use instead.
- For XAML markup, there is no way of having parts of the file ignored at compile-time or at run-time. So "common" and "platform-specific" is all-or-nothing for the content in the file itself. But a markup file can reference resources and types defined elsewhere, so we can use the technique of indirection to maximize reuse. If a page consists of mostly common content with platform-specific titles/headers/navigation/etc surrounding it, then you can factor the common content into a UserControl in the Shared project and put an instance of that UserControl inside platform-specific pages in each platform-specific project. The page in each platform-specific project will create its own instance of the same, shared UserControl.
- If a page consists of mostly common content with platform-specific content embedded inside it, then you can factor the platform-specific content into a UserControl in each of the platform-specific projects. Give both of them the same namespace and type name; they are essentially two copies of the same type in different projects, differing only in content. Put an instance of a UserControl with that namespace and type name in a single, common page in the Shared project. Each platform-specific project will create its own instance of the same, shared page, and each page will then instantiate the version of the UserControl defined in the platform-specific project that created it. This is similar in spirit to the master pages concept from ASP.NET.
- If a page consists of common content, but you want to apply platform-specific styles or templates or other resources to the elements in the page, then within the page you can use a common resource key and then define that key in a ResourceDictionary file in each of the platform-specific projects. This is similar in spirit to the previous technique, except that here the indirection is via a common resource key rather than a common type name. Your options for defining the resource give you a lot of flexibility. If you want to apply a system style to a TextBlock, and the Windows Phone Store app system style you want to use has a different key than the Windows Store app style you want to use, the solution is to assign a common alias to the two system keys. Invent your own common key and use that in the page, then create an empty style with that key in each of the platform-specific resource dictionaries, and use BasedOn to base the empty styles on their platform-specific system key. The styles needn't be empty, of course, if you want to override or add any properties. If you want to edit a copy of a control template or style on one platform but not the other, then create an edited copy of the style in one ResourceDictionary, and an empty style in the other, both with the same common key. The empty style will essentially be invisible because it doesn't override any properties, and so the default style and template will be applied to the control on that platform. These techniques are illustrated in the case studies.
- Even if you don't use a Universal App project, with its Shared sub-project, you can still share source code files between projects, and use any of the techniques described above. Here's how: in Visual Studio, right-click the project in Solution Explorer, select Add Existing Item, select the files to share, and then click Add As Link. Store your source code files in a common folder on the file system where the projects that link to them can see them. And don't forget to add them to source control.
- For reuse at the binary level, rather than the source code level, see Creating Windows Runtime Components in C# and Visual Basic. There are also Portable Class Libraries, which support the subset of .NET APIs that are available in the .NET Framework for Microsoft Silverlight, the .NET APIs for Windows Store apps, and the full .NET Framework. Portable Class Library assemblies are binary compatible with all three platforms. Use Microsoft Visual Studio Professional 2013 to create a project that targets a Portable Class Library. See Cross-Platform Development with the Portable Class Library.
Conditional compilation with C# preprocessor directives
If you look in your Universal App project at the project properties pages, you'll see that the Windows project defines NETFX_CORE;WINDOWS_APP as conditional compilation symbols, and the Windows Phone project defines NETFX_CORE;WINDOWS_PHONE_APP.
In general, use this logic to perform conditional compilation in source code targeting the Windows Runtime:
#if WINDOWS_APP // Code that you want to compile into the Windows Store app. #else // Code that you want to compile into the Windows Phone Store app. #endif // WINDOWS_APP
If you have code that you've been sharing between a Windows Phone Silverlight app and a Windows Store app, then you may already have source code with logic like this:
#if NETFX_CORE // Code that you want to compile into the Windows Store app. #else // Code that you want to compile into the Windows Phone Silverlight app. #endif // NETFX_CORE
If so, and if you now want to support all three platforms, then you can nest the Windows Runtime logic into what you already have, like this:
#if NETFX_CORE #if WINDOWS_APP // Code that you want to compile into the Windows Store app. #else // Code that you want to compile into the Windows Phone Store app. #endif // WINDOWS_APP #else // Code that you want to compile into the Windows Phone Silverlight app. #endif // NETFX_CORE
The next topic is Troubleshooting.