Windows 8

Gemeinsamer Code für Windows Phone 8- und Windows 8-Anwendungen

Doug Holland

Mit Visual Studio 2012 verfügen Sie über ein ausgezeichnetes Werkzeug zum Erstellen von Anwendungen sowohl für Windows 8 als auch für Windows Phone 8. Es ist daher sicherlich von Interesse, wie viel Code Sie für Windows Store-Apps und Windows Phone 8-Apps gemeinsam verwenden können.

Sie können Windows Store-Apps mit XAML und C#, Visual Basic, C++ und sogar mit HTML5 und JavaScript entwickeln.

Windows Phone 8-Apps werden üblicherweise mithilfe von XAML und C# oder Visual Basic geschrieben, und mit dem Phone 8-SDK können Sie Direct3D-Apps jetzt auch mit XAML und C++ erstellen. Das Windows Phone 8-SDK enthält zwar auch Vorlagen für HTML5-basierte Apps, doch sind diese lediglich XAML-basiert, wobei das WebBrowser-Steuerelement HTML5-basierte Webseiten hostet.

In diesem Artikel werde ich drei Strategien vorstellen, mit denen sich Code in Windows Store-Apps und Windows Phone-Apps gemeinsam verwenden lässt: Portable Klassenbibliotheken (Portable Class Libraries, PCLs), Komponenten für Windows-Runtime (WinRT) und für Windows Phone-Runtime sowie die Visual Studio-Option „Als Link hinzufügen“. Weitere Anleitungen zur Verwendung gemeinsamen Codes in Windows Store-Apps und Windows Phone-Apps finden Sie im Entwicklungscenter (aka.ms/sharecode).

Anzumerken ist, dass es zwar viele Übereinstimmungen zwischen Windows Store-Apps und Windows Phone-Apps gibt (beispielsweise Live-Kacheln), beide aber auf unterschiedlichen Plattformen beruhen, für die Sie die Benutzeroberfläche jeweils speziell entwerfen müssen.

Architektur

Die Architekturprinzipien, mit deren Hilfe Sie den Anteil gemeinsamen Codes erhöhen können, entsprechen im Allgemeinen denen zur Trennung der Belange (Separation of Concerns). Sollten Sie bereits Entwurfsmuster wie beispielsweise Model-View-ViewModel (MVVM) oder Model-View-Controller (MVC) verwenden, welche die Trennung von Belangen unterstützen, werden Sie bemerkt haben, dass die Verwendung gemeinsamen Codes dadurch einfacher wird. Das ist ebenfalls der Fall, wenn Sie Entwurfsmuster mit Abhängigkeitsinjektion (Dependency-Injection) in Ihrer Architektur verwenden. Daher sollten Sie beim Entwerfen neuer Apps die Verwendung dieser Entwurfsmuster unbedingt in Erwägung ziehen, um den Anteil gemeinsamen Codes zu erhöhen. Für vorhandene Apps sollten Sie ein Refactoring der Architektur vornehmen mit dem Ziel, die Trennung von Belangen und damit die Wiederverwendbarkeit von Code zu fördern. Die durch MVVM oder MVC ermöglichte Trennung von Belangen hat zudem den Vorteil, dass Designer und Entwickler gleichzeitig arbeiten können. Die Designer entwerfen die Benutzeroberfläche mit Tools wie Expression Blend, während die Entwickler sie in Visual Studio mit Leben füllen.

Portable Klassenbibliotheken (Portable Class Libraries, PCLs)

Das Projekt „Portable Klassenbibliothek“ in Visual Studio 2012 ermöglicht plattformübergreifende Entwicklungen, bei denen Sie das Zielframework auswählen können, das die entsprechende Assembly unterstützen soll. Die Projektvorlage „Portable Klassenbibliothek“ wurde mit Visual Studio 2010 als Add-In eingeführt und ist nun in Visual Studio Professional 2012 (und höher) integriert.

Wie unterstützt nun eine PCL die gemeinsame Verwendung von Code?

Eine „Portable Klassenbibliothek“ ermöglicht, wie es der Name ausdrückt, die gemeinsame Verwendung portablen Codes. Code ist dann portabel, wenn es sich um verwalteten Code handelt, der in C# oder Visual Basic geschrieben ist. Da aus einer PCL eine einzelne Binärdatei wird, kann portabler Code keine Direktiven zur bedingten Kompilierung enthalten. Stattdessen werden plattformspezifische Funktionen mithilfe von Schnittstellen oder abstrakten Klassen abstrahiert. Wenn portabler Code mit plattformspezifischem Code interagieren muss, werden Entwurfsmuster mit Abhängigkeitsinjektion verwendet, die plattformspezifische Implementierungen der Abstraktionen bereitstellen. Durch die Kompilierung entsteht aus der PCL eine einzelne Assembly, auf die jedes Projekt verweisen kann, das auf den Zielplattformen ausgeführt wird.

Abbildung 1 zeigt einen empfohlenen Architekturansatz für die Verwendung gemeinsamen Codes mithilfe von PCLs. Mit dem MVVM-Entwurfsmuster werden die Ansichtsmodelle und Modelle zusammen mit Abstraktionen plattformspezifischer Funktionen in der PCL zusammengefasst. Die Windows Store-Apps und die Windows Phone-Apps stellen die Startlogik, Ansichten und Implementierungen aller Abstraktionen plattformspezifischer Funktionen bereit. Das MVVM-Entwurfsmuster ist zwar zur Unterstützung gemeinsamen Codes nicht unbedingt erforderlich, aber die damit ausgeführte Trennung von Belangen ergibt eine saubere und erweiterbare Architektur.

Sharing Code Using the MVVM Design PatternAbbildung 1: Verwendung gemeinsamen Codes mithilfe des MVVM-Entwurfsmusters

In Visual Studio 2012 können Sie im Dialogfeld „Portable Klassenbibliothek hinzufügen“ die Zielframeworks auswählen, welche die entsprechende Assembly unterstützen soll.

Auch wenn es auf den ersten Blick sinnvoll erscheint, ist die Aktivierung des Kontrollkästchens für Silverlight 5 nicht erforderlich, um Code gemeinsam in Windows Store-Apps und Windows Phone-Apps zu verwenden. Diese Einstellung würde vielmehr bewirken, dass Ihr portabler Code einige der sehr nützlichen neuen Typen nicht verwenden kann, die in Microsoft .NET Framework 4.5 eingeführt wurden, beispielsweise die CallerMemberNameAttribute-Klasse.

Wenn Sie bereits Anwendungen für Windows Phone entwickelt haben, kennen Sie sicherlich die MessageBox-Klasse, mit der Meldungen an den Benutzer ausgegeben werden. In Windows Store-Apps wird dafür die MessageDialog-Klasse der Windows-Runtime verwendet. Betrachten wir im Folgenden, wie diese plattformspezifische Funktionalität für eine PCL abstrahiert werden kann.

Die IMessagingManager-Schnittstelle in Abbildung 2 ist die Abstraktion der plattformspezifischen Funktionalität zur Ausgabe von Meldungen für den Benutzer. Die IMessagingManager-Schnittstelle stellt eine überladene ShowAsync-Methode bereit, an welche die Informationen (Meldung und Meldungstitel) übergeben werden, die dem Benutzer angezeigt werden sollen.

Abbildung 2: IMessagingManager-Schnittstelle

/// <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);
}

Die Überladung der ShowAsync-Methode ermöglicht die Angabe von Schaltflächen, die zusammen mit der Meldung angezeigt werden. Die MessagingButtons-Enumeration stellt eine plattformunabhängige Abstraktion zur Darstellung der Schaltfläche „OK“, der Schaltflächen „OK“ und „Abbrechen“ und der Schaltflächen „Ja“ und „Nein“ bereit (Abbildung 3).

Abbildung 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
}

Die zugrunde liegenden Ganzzahlwerte der MessagingButtons-Enumeration wurden deshalb der MessageBoxButton-Enumeration von Windows Phone zugeordnet, weil dadurch eine sichere Umwandlung der MessagingButtons-Enumeration in die MessageBoxButton-Enumeration möglich ist.

„ShowAsync“ ist eine asynchrone Methode, die „Task­<MessagingResult>“ zurückgibt und damit die Schaltfläche bezeichnet, die vom Benutzer beim Verlassen der Meldung betätigt wurde. Die MessagingResult-Enumeration (Abbildung 4) ist ebenfalls eine plattformunabhängige Abstraktion.

Abbildung 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 diesem Beispiel sind die IMessagingManager-Schnittstelle sowie die Messaging­Buttons- und MessagingResult-Enumerationen portabel und somit in einer PCL gemeinsam nutzbar.

Für die in der PCL abstrahierte plattformspezifische Funktionalität müssen Sie plattformspezifische Implementierungen der IMessagingManager-Schnittstelle sowohl für Windows Store-Apps als auch für Windows Phone-Apps bereitstellen. Abbildung 5 zeigt die Implementierung für Windows Phone-Apps, Abbildung 6 die für Windows Store-Apps.

Abbildung 5: MessagingManager-Implementierung für Windows Phone

/// <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;
  }
}

Abbildung 6: MessagingManager-Implementierung für Windows Store

/// <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;
  }
}

In der Version der MessagingManager-Klasse für Windows Phone wird die plattformspezifische Klasse „MessageBox“ zur Darstellung der Meldung verwendet. Die zugrunde liegenden Ganzzahlwerte der MessagingButtons-Enumeration wurden deshalb der MessageBoxButton-Enumeration von Windows Phone zugeordnet, weil dadurch eine sichere Umwandlung der MessagingButtons-Enumeration in die MessageBoxButton-Enumeration möglich ist. In gleicher Weise ermöglichen die zugrunde liegenden Ganzzahlwerte der MessagingResult-Enumeration die sichere Umwandlung in die MessageBoxResult-Enumeration.

In der Version der MessagingManager-Klasse für Windows Store in Abbildung 6 wird die Windows-Runtime-Klasse „MessageDialog“ zur Darstellung der Meldung verwendet. Die zugrunde liegenden Ganzzahlwerte der MessagingButtons-Enumeration wurden deshalb der MessageBoxButton-Enumeration von Windows Phone zugeordnet, weil dadurch eine sichere Umwandlung der MessagingButtons-Enumeration in die MessageBoxButton-Enumeration möglich ist.

Abhängigkeitsinjektion (Dependency Injection)

Entsprechend der in Abbildung 1 dargestellten Anwendungsarchitektur stellt „IMessagingManager“ die plattformspezifische Abstraktion für Messaging-Benutzer bereit. Ich verwende im Folgenden Entwurfsmuster mit Abhängigkeitsinjektion, um plattformspezifische Implementierungen dieser Abstraktion in den portablen Code zu „injizieren“. Im Beispiel aus Abbildung 7 injiziert „HelloWorldViewModel“ per Konstruktorinjektion eine plattformspezifische Implementierung der IMessagingManager-Schnittstelle. Anschließend verwendet die HelloWorldView­Model.DisplayMessage-Methode die injizierte Implementierung zur Ausgabe der Meldung an den Benutzer. Weitere Informationen über Abhängigkeitsinjektion finden Sie in dem Buch „Dependency Injection in .NET“ von Mark Seemann (Manning Publications, 2011, bit.ly/dotnetdi).

Abbildung 7: Die portable HelloWorldViewModel-Klasse

/// <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);
  }
}

Komponenten für Windows-Runtime

Mit Komponenten für Windows-Runtime können Sie Code, der nicht portabel ist, in Windows Store-Apps und Windows Phone-Apps gemeinsam verwenden. Da diese Komponenten nicht binärkompatibel sind, müssen Sie zudem vergleichbare Projekte mit Windows-Runtime- und Windows Phone-Runtime-Komponenten erstellen, um Code auf beiden Plattformen nutzen zu können. Obwohl Sie Projekte sowohl für Komponenten für Windows-Runtime als auch für Windows Phone in Ihre Projektmappe aufnehmen müssen, werden diese Projekte mithilfe derselben C++-Quelldateien erstellt.

Da es die Komponenten für Windows-Runtime ermöglichen, systemeigenen C++-Code in Windows Store-Apps und Windows Phone-Apps gemeinsam zu verwenden, sind diese Komponenten bestens geeignet, um mit C++ rechenintensive Operationen mit optimaler Leistungsfähigkeit zu schreiben.

API-Definitionen in Komponenten für Windows-Runtime werden durch Metadaten bereitgestellt, die in WINMD-Dateien enthalten sind. Mithilfe dieser Daten kann die entsprechende Sprache per Sprachprojektion bestimmen, wie die API innerhalb der Sprache verwendet wird. Abbildung8 zeigt die unterstützten Sprachen, mit denen Komponenten für Windows-Runtime erstellt und verarbeitet werden können. Zum Zeitpunkt der Entstehung dieses Artikels steht als gemeinsame Sprache zum Erstellen beider Komponententypen nur C++ zur Verfügung.

Abbildung 8: Erstellen und Verarbeiten von Komponenten für Windows-Runtime

Plattform Erstellen Verarbeiten
Komponenten für Windows-Runtime C++, C#, Visual Basic C++, C#, Visual Basic, JavaScript
Komponenten für Windows Phone-Runtime C++ C++, C#, Visual Basic

Im folgenden Beispiel möchte ich zeigen, wie eine C++-Klasse, die Fibonacci-Zahlen berechnet, sowohl in Windows Store-Apps als auch in Windows Phone-Apps verwendet werden kann. Abbildung 9 und Abbildung 10 zeigen die Implementierung der FibonacciCalculator-Klasse mit C++/Komponentenerweiterungen (Component Extensions, CX).

Abbildung 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);
  };    
}

Abbildung 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);
}

Abbildung 11 zeigt die Projektstruktur der Beispiele zu diesem Artikel im Projektmappen-Explorer von Visual Studio. Wie Sie sehen, enthalten beide Komponenten dieselben C++-Quelldateien.

Visual Studio Solution Explorer
Abbildung 11: Projektmappen-Explorer in Visual Studio

Beim Hinzufügen eines vorhandenen Elements zu einem Visual Studio-Projekt werden Sie den kleinen Pfeil rechts neben der Schaltfläche „Hinzufügen“ bemerkt haben. Wenn Sie diesen Pfeil anklicken, wird ein Menü mit den Optionen „Hinzufügen“ und „Als Link hinzufügen“ geöffnet. Mit der Standardoption „Hinzufügen“ wird die gewünschte Datei in das Projekt kopiert, sodass sie anschließend doppelt auf der Festplatte und in der Quellcodeverwaltung (sofern aktiviert) vorhanden ist. Wenn Sie „Als Link hinzufügen“ wählen, ist weiterhin nur eine einzelne Instanz der Datei auf der Festplatte und in der Quellcodeverwaltung vorhanden, was für die Versionsverwaltung überaus nützlich sein kann. Weil dies beim Hinzufügen einer vorhandenen Datei in Visual C++-Projekte das Standardverhalten ist, enthält die Schaltfläche „Hinzufügen“ im Dialogfeld „Vorhandenes Element hinzufügen“ keine Option. Im Entwicklungscenter finden Sie unter bit.ly/addaslink weitere Anleitungen zur Verwendung gemeinsamen Codes mithilfe von „Als Link hinzufügen“.

Windows-Runtime-APIs sind nicht portabel und können deshalb nicht in einer PCL bereitgestellt werden. Da Windows 8 und Windows Phone 8 eine Teilmenge der Windows-Runtime-API verfügbar machen, kann Code, der für diese Teilmenge geschrieben wurde, von beiden Apps mithilfe von „Als Link hinzufügen“ gemeinsam verwendet werden. Einzelheiten zu dieser Teilmenge der Windows-Runtime-API finden Sie im Entwicklungscenter unter bit.ly/wpruntime.

Zusammenfassung

Seit der Freigabe von Windows 8 und Windows Phone 8 gibt es Möglichkeiten, Code auf beiden Plattformen gemeinsam zu verwenden. In diesem Artikel habe ich erläutert, wie portabler Code mithilfe von binärkompatiblen PCLs gemeinsam genutzt werden kann und wie plattformspezifische Funktionen abstrahiert werden. Ich habe dann gezeigt, wie systemeigener Code, der nicht portabel ist, mithilfe der Komponenten für Windows-Runtime gemeinsam verwendet werden kann. Schließlich habe ich die Visual Studio-Option „Als Link hinzufügen“ vorgestellt.

In Bezug auf die Architektur habe ich dargelegt, dass Entwurfsmuster wie beispielsweise MVVM, welche die Trennung von Belangen unterstützen, für die Verwendung gemeinsamen Codes von Nutzen sind, und dass Entwurfsmuster zur Abhängigkeitsinjektion die Verwendung plattformspezifischer Funktionen mit gemeinsamem Code ermöglichen. Weitere Anleitungen zur Verwendung gemeinsamen Codes in Windows Store-Apps und Windows Phone-Apps finden Sie im Entwicklungscenter unter aka.ms/sharecode. Das PixPresenter-Beispiel ist unter bit.ly/pixpresenter abrufbar.

Doug Holland ist leitender Architekturexperte im Entwicklerteam und im Platform Evangelism-Team von Microsoft. Er hat in den letzten Jahren mit strategischen Partnern zusammengearbeitet, um benutzerorientierte Apps für Windows und Windows Phone zu entwickeln. Er ist ehemaliger Visual C#-MVP und Intel Black Belt-Entwickler sowie Autor des Buchs „Professional Windows 8 Programming: Application Development with C# and XAML“ (Wrox, 2012), das unter bit.ly/prowin8book erhältlich ist.

Unser Dank gilt den folgenden technischen Experten für die Durchsicht dieses Artikels: Andrew Byrne (Microsoft), Doug Rothaus (Microsoft) und Marian Laparu (Microsoft).