June 2013

Volume 28 Number 6

Windows 8 - Sharing Code between Windows Phone 8 and Windows 8 Applications

By Doug Holland

With Visual Studio 2012, you have an excellent set of tools to build apps for Windows 8 and Windows Phone 8. Therefore, it’s appropriate to explore how much code you’re able to share between the Windows Store and Windows Phone versions of your apps.

You can write Windows Store apps using several different programming languages—XAML with C#, Visual Basic, C++ and even HTML5 with JavaScript.

Windows Phone 8 apps are usually written using XAML with either C# or Visual Basic, though the Windows Phone 8 SDK now lets you write Direct3D apps using XAML and C++. While the Windows Phone 8 SDK also provides a template for HTML5-based apps, these are merely XAML-based, with the WebBrowser control hosting HTML5-based Web pages.

In this article I’ll explore three strategies for sharing code between Windows Store and Windows Phone apps: Portable Class Libraries (PCLs), Windows Runtime (WinRT) components (and Windows Phone Runtime components) and the Visual Studio Add as Link option. You can find further guidance on sharing code between Windows Store and Windows Phone apps at the Dev Center (aka.ms/sharecode).

It’s worth noting that while there are many similarities between Windows Store and Windows Phone apps—live tiles, for example—­these are distinct platforms for which you should specifically design the UX.

Architecture

The architectural principles that enable you to increase the percentage of code that can be shared are, in general, those that promote a separation of concerns. If you’re already using patterns that promote a separation of concerns, such as the Model-View-ViewModel (MVVM) or Model-View-Controller (MVC), you’ll find it easier to enable code sharing—and that’s also the case if you use dependency-injection patterns in your architecture. You should definitely consider using these patterns when architecting new apps to increase the level of code sharing that can be achieved. With existing apps, you may want to consider refactoring the architecture to promote a separation of concerns and therefore sharing of code. With the separation of concerns provided by MVVM or MVC there are additional benefits, such as the ability to have designers and developers work simultaneously. Designers design the UX with tools such as Expression Blend, while developers write code to bring the UX to life in Visual Studio.

Portable Class Libraries

The PCL project in Visual Studio 2012 enables cross-platform development, which lets you choose the target frameworks the resulting assembly will support. Introduced in Visual Studio 2010 as an optional add-in, the PCL project template is now included within Visual Studio Professional 2012 and above.

So what code can you share within a PCL?

PCLs are so named because they enable the sharing of portable code, and for code to be portable it must be managed code written in either C# or Visual Basic. Because a PCL produces a single binary, portable code does not employ conditional compilation directives; instead, platform-specific capabilities are abstracted using either interfaces or abstract base classes. When portable code needs to interact with platform-specific code, dependency-injection patterns are used to provide platform-specific implementations of the abstractions. When compiled, the PCL results in a single assembly that can be referenced from any project based on the target frameworks.

Figure 1 shows one recommended architectural approach to enable shared code using PCLs. With the MVVM pattern, the view models and models are contained within the PCL, along with abstractions of any platform-specific capabilities. The Windows Store and Windows Phone apps provide startup logic, views, and implementations of any abstractions of platform-specific capabilities. While the MVVM design pattern is not specifically required to enable portable code, the separation of concerns the pattern promotes results in a clean and extensible architecture.

Sharing Code Using the MVVM Design Pattern
Figure 1 Sharing Code Using the MVVM Design Pattern

The Add Portable Class Library dialog in Visual Studio 2012 is where you can select the target frameworks the resulting assembly will support.

You might initially think you should check the box for Silverlight 5, but it isn’t necessary for sharing code between Windows Store and Windows Phone apps. In fact, selecting Silverlight 5 means your portable code won’t be able to take advantage of some very useful new types, such as the CallerMemberNameAttribute class introduced in the Microsoft .NET Framework 4.5.

If you’ve been developing for Windows Phone, you’re most likely familiar with the MessageBox class that enables messages to be presented to the user. Windows Store apps use the Windows Runtime MessageDialog class for this purpose. Let’s take a look at how to abstract this platform-specific functionality in a PCL.

The IMessagingManager interface in Figure 2 abstracts the platform-specific functionality to display messages to the user. The IMessagingManager interface provides an overloaded ShowAsync method that takes the message and title of the message to be displayed to the user.

Figure 2 IMessagingManager Interface

/// <summary>
/// Provides an abstraction for platform-specific user messaging capabilities.
/// </summary>
public interface IMessagingManager
{
  /// <summary>
  /// Displays the specified message using platform-specific
  /// user-messaging capabilities.
  /// </summary>
  /// <param name="message">The message to be displayed to the user.</param>
  /// <param name="title">The title of the message.</param>
  /// <returns>A <see cref="T:MessagingResult"/>
  value representing the user's response.</returns>
  Task<MessagingResult> ShowAsync(string message, string title);
  /// <summary>
  /// Displays the specified message using platform-specific
  /// user-messaging capabilities.
  /// </summary>
  /// <param name="message">The message to be displayed to the user.</param>
  /// <param name="title">The title of the message.</param>
  /// <param name="buttons">The buttons to be displayed.</param>
  /// <returns>A <see cref="T:MessagingResult"/>
  value representing the user's response.</returns>
  Task<MessagingResult> ShowAsync(string message, string title,
    MessagingButtons buttons);
}

The ShowAsync method is overloaded to let you optionally specify buttons to display along with the message. The MessagingButtons enumeration provides a platform-independent abstraction for displaying an OK button, OK and Cancel buttons, or Yes and No buttons (see Figure 3).

Figure 3 MessagingButtons Enumeration

/// <summary>
/// Specifies the buttons to include when a message is displayed.
/// </summary>
public enum MessagingButtons
{
  /// <summary>
  /// Displays only the OK button.
  /// </summary>
  OK = 0,
  /// <summary>
  /// Displays both the OK and Cancel buttons.
  /// </summary>
  OKCancel = 1,
  /// <summary>
  /// Displays both the Yes and No buttons.
  /// </summary>
  YesNo = 2
}

The underlying integer values of the MessagingButtons enumeration were intentionally mapped to the Windows Phone MessageBoxButton enumeration in order to safely cast the MessagingButtons enumeration to the MessageBoxButton enumeration.

ShowAsync is an asynchronous method that returns a Task­<MessagingResult> indicating the button the user clicked when dismissing the message. The MessagingResult enumeration (see Figure 4) is also a platform-independent abstraction.

Figure 4 MessagingResult Enumeration

/// <summary>
/// Represents the result of a message being displayed to the user.
/// </summary>
public enum MessagingResult
{
  /// <summary>
  /// This value is not currently used.
  /// </summary>
  None = 0,
  /// <summary>
  /// The user clicked the OK button.
    /// </summary>
    OK = 1,
    /// <summary>
    /// The user clicked the Cancel button.
    /// </summary>
    Cancel = 2,
    /// <summary>
    /// The user clicked the Yes button.
    /// </summary>
    Yes = 6,
   /// <summary>
  /// The user clicked the No button.
  /// </summary>
  No = 7
}

In this example, the IMessagingManager interface and Messaging­Buttons and MessagingResult enumerations are portable, and therefore sharable within a PCL.

With the platform-specific functionality abstracted in the PCL, you need to provide platform-specific implementations of the IMessagingManager interface for both Windows Store and Windows Phone apps. Figure 5 shows the implementation for Windows Phone apps, and Figure 6 shows the implementation for Windows Store apps.

Figure 5 MessagingManager—Windows Phone Implementation

/// <summary>
/// Windows Phone implementation of the <see cref="T:IMessagingManager"/> interface.
/// </summary>
internal class MessagingManager : IMessagingManager
{
  /// <summary>
  /// Initializes a new instance of the <see cref="T:MessagingManager"/> class.
  /// </summary>
  public MessagingManager()
  {
  }
  /// <summary>
  /// Displays the specified message using platform-specific
  /// user-messaging capabilities.
  /// </summary>
  /// <param name="message">The message to be displayed to the user.</param>
  /// <param name="title">The title of the message.</param>
  /// <returns>A <see cref="T:MessagingResult"/>
      value representing the users response.</returns>
  public async Task<MessagingResult> ShowAsync(string message, string title)
  {
    MessagingResult result = await this.ShowAsync(message, title,
      MessagingButtons.OK);
    return result;
  }
  /// <summary>
  /// Displays the specified message using platform-specific
  /// user-messaging capabilities.
  /// </summary>
  /// <param name="message">The message to be displayed to the user.</param>
  /// <param name="title">The title of the message.</param>
  /// <param name="buttons">The buttons to be displayed.</param>
  /// <exception cref="T:ArgumentException"/>
  /// The specified value for message or title is <c>null</c> or empty.
  /// </exception>
  /// <returns>A <see cref="T:MessagingResult"/>
  /// value representing the users response.</returns>
  public async Task<MessagingResult> ShowAsync(
    string message, string title, MessagingButtons buttons)
  {
    if (string.IsNullOrEmpty(message))
    {
      throw new ArgumentException(
        "The specified message cannot be null or empty.", "message");
    }
    if (string.IsNullOrEmpty(title))
    {
      throw new ArgumentException(
        "The specified title cannot be null or empty.", "title");
    }
    MessageBoxResult result = MessageBoxResult.None;
    // Determine whether the calling thread is the thread
    // associated with the Dispatcher.
    if (App.RootFrame.Dispatcher.CheckAccess())
    {
      result = MessageBox.Show(message, title, 
        (MessageBoxButton)buttons);
    }
    else
    {
      // Execute asynchronously on the thread the Dispatcher is associated with.
      App.RootFrame.Dispatcher.BeginInvoke(() =>
      {
        result = MessageBox.Show(message, title, 
          (MessageBoxButton)buttons);
      });
    }
    return (MessagingResult) result;
  }
}

Figure 6 MessagingManager—Windows Store Implementation

/// <summary>
/// Windows Store implementation of the <see cref="T:IMessagingManager"/> interface.
/// </summary>
internal class MessagingManager : IMessagingManager
{
  /// <summary>
  /// Initializes a new instance of the <see cref="T:MessagingManager"/> class.
  /// </summary>
  public MessagingManager()
  {
  }
  /// <summary>
  /// Displays the specified message using platform-specific
  /// user-messaging capabilities.
  /// </summary>
  /// <param name="message">The message to be displayed to the user.</param>
  /// <param name="title">The title of the message.</param>
  /// <returns>A <see cref="T:MessagingResult"/>
      value representing the users response.</returns>
  public async Task<MessagingResult> ShowAsync(string message, string title)
  {
    MessagingResult result = await this.ShowAsync(message, title,
      MessagingButtons.OK);
    return result;
  }
  /// <summary>
  /// Displays the specified message using platform-specific
  /// user-messaging capabilities.
  /// </summary>
  /// <param name="message">The message to be displayed to the user.</param>
  /// <param name="title">The title of the message.</param>
  /// <param name="buttons">The buttons to be displayed.</param>
  /// <exception cref="T:ArgumentException"/>
  /// The specified value for message or title is <c>null</c> or empty.
  /// </exception>
  /// <exception cref="T:NotSupportedException"/>
  /// The specified <see cref="T:MessagingButtons"/> value is not supported.
  /// </exception>
  /// <returns>A <see cref="T:MessagingResult"/>
  /// value representing the users response.</returns>
  public async Task<MessagingResult> ShowAsync(
    string message, string title, MessagingButtons buttons)
  {
    if (string.IsNullOrEmpty(message))
    {
      throw new ArgumentException(
        "The specified message cannot be null or empty.", "message");
    }
    if (string.IsNullOrEmpty(title))
    {
      throw new ArgumentException(
        "The specified title cannot be null or empty.", "title");
    }
    MessageDialog dialog = new MessageDialog(message, title);
    MessagingResult result = MessagingResult.None;
    switch (buttons)
    {
      case MessagingButtons.OK:
        dialog.Commands.Add(new UICommand("OK",
          new UICommandInvokedHandler((o) => result = MessagingResult.OK)));
        break;
      case MessagingButtons.OKCancel:
        dialog.Commands.Add(new UICommand("OK",
          new UICommandInvokedHandler((o) => result = MessagingResult.OK)));
        dialog.Commands.Add(new UICommand("Cancel",
          new UICommandInvokedHandler((o) => result = MessagingResult.Cancel)));
        break;
      case MessagingButtons.YesNo:
        dialog.Commands.Add(new UICommand("Yes",
          new UICommandInvokedHandler((o) => result = MessagingResult.Yes)));
        dialog.Commands.Add(new UICommand("No",
          new UICommandInvokedHandler((o) => result = MessagingResult.No)));
        break;
      default:
        throw new NotSupportedException(
          string.Format("MessagingButtons.{0} is not supported.",
          buttons.ToString()));
            }
    dialog.DefaultCommandIndex = 1;
    // Determine whether the calling thread is the
    // thread associated with the Dispatcher.
    if (Window.Current.Dispatcher.HasThreadAccess)
    {
      await dialog.ShowAsync();
    }
    else
    {
      // Execute asynchronously on the thread the Dispatcher is associated with.
      await Window.Current.Dispatcher.RunAsync(
        CoreDispatcherPriority.Normal, async () =>
      {
        await dialog.ShowAsync();
      });
    }
    return result;
  }
}

The Windows Phone version of the MessagingManager class uses the platform-specific MessageBox class to display the message. The underlying integer values of the MessagingButtons enumeration were intentionally mapped to the Windows Phone MessageBoxButton enumeration, which allows you to safely cast from the MessagingButtons enumeration to the MessageBoxButton enumeration. In the same way, the underlying integer values of the MessagingResult enumeration allow you to safely cast to the MessageBoxResult enumeration.

The Windows Store version of the MessagingManager class in Figure 6 uses the Windows Runtime MessageDialog class to display the message. The underlying integer values of the MessagingButtons enumeration were intentionally mapped to the Windows Phone MessageBoxButton enumeration, which allows you to safely cast from the MessagingButtons enumeration to the MessageBoxButton enumeration.

Dependency Injection

With the application architecture defined as illustrated in Figure 1, IMessagingManager provides the platform-specific abstraction for messaging users. I’ll now use dependency-injection patterns to inject platform-specific implementations of this abstraction into the portable code. In the example in Figure 7, the HelloWorldViewModel uses constructor injection to inject a platform-specific implementation of the IMessagingManager interface. The HelloWorldView­Model.DisplayMessage method then uses the injected implementation to message the user. To learn more about dependency injection, I recommend reading “Dependency Injection in .NET” by Mark Seemann (Manning Publications, 2011, bit.ly/dotnetdi).

Figure 7 Portable HelloWorldViewModel Class

/// <summary>
/// Provides a portable view model for the Hello World app.
/// </summary>
public class HelloWorldViewModel : BindableBase
{
  /// <summary>
  /// The message to be displayed by the messaging manager.
  /// </summary>
  private string message;
  /// <summary>
  /// The title of the message to be displayed by the messaging manager.
  /// </summary>
  private string title;
  /// <summary>
  /// Platform specific instance of the <see cref="T:IMessagingManager"/> interface.
  /// </summary>
  private IMessagingManager MessagingManager;
  /// <summary>
  /// Initializes a new instance of the <see cref="T:HelloWorldViewModel"/> class.
  /// </summary>
  public HelloWorldViewModel(IMessagingManager messagingManager,
    string message, string title)
  {
    this.messagingManager = MessagingManager;
    this.message = message;
    this.title = title;
    this.DisplayMessageCommand = new Command(this.DisplayMessage);
  }
  /// <summary>
  /// Gets the display message command.
  /// </summary>
  /// <value>The display message command.</value>
  public ICommand DisplayMessageCommand
  {
    get;
    private set;
  }
  /// <summary>
  /// Displays the message using the platform-specific messaging manager.
  /// </summary>
  private async void DisplayMessage()
  {
    await this.messagingManager.ShowAsync(
      this.message, this.title, MessagingButtons.OK);
  }
}

Windows Runtime Components

Windows Runtime components let you share non-portable code between Windows Store and Windows Phone apps. The components are not binary-compatible, however, so you’ll also need to create comparable Windows Runtime and Windows Phone Runtime component projects to leverage code across both platforms. While you’ll have to include projects within your solution for both Windows Runtime components and Windows Phone Runtime components, these projects are built using the same C++ source files.

With the ability to share native C++ code between Windows Store and Windows Phone apps, Windows Runtime components are an excellent choice for writing computationally intensive operations using C++ to attain optimal performance.

API definitions within Windows Runtime components are exposed in the metadata contained in .winmd files. Using this metadata, language projections let the consuming language determine how the API is consumed within that language. Figure8 shows the supported languages for creating and consuming Windows Runtime components. At the time of this writing, only C++ is supported for creating both types of components.

Figure 8 Creating and Consuming Windows Runtime Components

Platform Create Consume
Windows Runtime Components C++, C#, Visual Basic C++, C#, Visual Basic, JavaScript
Windows Phone Runtime Components C++ C++, C#, Visual Basic

In the following example, I’ll demonstrate how a C++ class designed to calculate Fibonacci numbers can be shared across Windows Store and Windows Phone apps. Figure 9 and Figure 10 show the implementation of the FibonacciCalculator class in C++/Component Extensions (CX).

Figure 9 Fibonacci.h

#pragma once
namespace MsdnMagazine_Fibonacci
{
  public ref class FibonacciCalculator sealed
  {
  public:
    FibonacciCalculator();
    uint64 GetFibonacci(uint32 number);
  private:
    uint64 GetFibonacci(uint32 number, uint64 p0, uint64 p1);
  };    
}

Figure 10 Fibonacci.cpp

#include "pch.h"
#include "Fibonacci.h"
using namespace Platform;
using namespace MsdnMagazine_Fibonacci;
FibonacciCalculator::FibonacciCalculator()
{
}
uint64 FibonacciCalculator::GetFibonacci(uint32 number)
{
  return number == 0 ? 0L : GetFibonacci(number, 0, 1);
}
uint64 FibonacciCalculator::GetFibonacci(uint32 number, 
  uint64 p0, uint64 p1)
{
  return number == 1 ? p1 : GetFibonacci(number - 1, 
    p1, p0 + p1);
}

In Figure 11 you can see the solution structure in Visual Studio Solution Explorer for the samples that accompany this article, showing the same C++ source files contained in both components.

Visual Studio Solution Explorer
Figure 11 Visual Studio Solution Explorer

When adding an existing item to a project in Visual Studio, you might have noticed the little arrow to the right of the Add button. If you click that arrow, you’ll be presented with the option to either Add or Add as Link. If you choose the default option to Add a file, the file will be copied to the project and the two copies of the file will exist separately on disk and within source control (if used). If you choose to Add as Link there will be only a single instance of the file on disk and within source control, which can be extremely useful for versioning. When adding existing files within Visual C++ projects this is the default behavior, and therefore the Add Existing Item dialog doesn’t provide an option on the Add button. The Dev Center provides further guidance on sharing code with Add as Link at bit.ly/addaslink.

Windows Runtime APIs are not portable, and therefore can’t be shared within a PCL. Windows 8 and Windows Phone 8 expose a subset of the Windows Runtime API, so code can be written against this subset and subsequently shared between both apps using Add as Link. The Dev Center provides details on the shared subset of the Windows Runtime API at bit.ly/wpruntime.

Wrapping Up

With the releases of Windows 8 and Windows Phone 8, you could begin to explore ways in which you could share code between the two platforms. In this article, I explored how portable code can be shared using binary-compatible PCLs and how platform-specific capabilities can be abstracted. I then demonstrated how non-portable native code can be shared using Windows Runtime components. Finally, I discussed the Visual Studio Add as Link option.

In terms of architecture, I noted that patterns that promote a separation of concerns, such as MVVM, can be useful for enabling code sharing, and that dependency-injection patterns enable shared code to leverage platform-specific capabilities. The Windows Phone Dev Center provides further guidance on sharing code between Windows Store and Windows Phone apps at aka.ms/sharecode, and also provides the PixPresenter sample app at bit.ly/pixpresenter.


Doug Holland works as a senior architect evangelist in the Microsoft Developer and Platform Evangelism team. He has spent the last few years working with strategic partners to bring consumer-focused apps to Windows and Windows Phone. He’s a former Visual C# MVP and Intel Black Belt Developer and an author of “Professional Windows 8 Programming: Application Development with C# and XAML” (Wrox, 2012), available at bit.ly/prowin8book.

Thanks to the following technical experts for reviewing this article: Andrew Byrne (Microsoft), Doug Rothaus (Microsoft) and Marian Laparu (Microsoft)