本文章是由機器翻譯。

Windows 8

Windows Phone 8 和 Windows 8 應用程式之間共用代碼

Doug Holland

下載代碼示例

Visual Studio2012年,你有一套很好的工具來為 Windows 8 和 Windows Phone 8 構建的應用程式。因此,它是適當,探索多少代碼你就能夠共用 Windows 存儲區和 Windows Phone 之間您的應用程式的版本。

您可以編寫 Windows 應用商店應用程式使用幾種不同的程式設計語言 — — C#Visual Basicc + +、 甚至 HTML5 與 JavaScript 與 XAML。

Windows Phone 8 應用程式通常使用編寫 XAML 與 C# 或Visual Basic,雖然 Windows Phone 8 SDK 現在您可以編寫使用 XAML 和 c + + 的 Direct3D 應用程式。雖然 Windows Phone 8 SDK 還提供了一個範本,用於基於 HTML5 的應用程式,這些都只是基於 XAML 與承載基於 HTML5 的 Web 頁的 web 瀏覽器控制項。

在這篇文章中,我將探討共用 Windows 存儲區和 Windows Phone 應用程式之間的代碼的三個策略:Windows 運行時 (WinRT) 元件 (和 Windows Phone 運行時元件) 的可擕式類庫 (PCLs) 和Visual Studio添加為連結選項。你可以找到進一步的開發中心在 Windows 應用商店和 Windows Phone 應用程式間共用代碼的指導 (aka.ms/sharecode)。

它值得注意,雖然 Windows 存儲區和 Windows Phone 應用程式之間有許多相似之處 — — 例如生活瓷磚,— —­這些都是為其專門設計應該體驗的獨特平臺

架構

一般情況下,使您能夠提高可共用的百分比的建築原則是代碼的那些促進關注點分離。如果您已經在使用模式,以促進分離的問題,例如模型-視圖-模型 (MVVM) 或模型-視圖-控制器 (MVC),你就會覺得它容易使代碼共用 — — 和,也是如此,如果您在您的體系結構中使用依賴注入模式。你絕對應該考慮在構建新的應用程式時使用這些模式來增加共用程式碼的級別可以實現。與現有的應用程式,你可能想要考慮重構的體系結構,以促進關注點分離和因此共用程式碼。與分離關注由使用 MVVM 或 MVC 提供有額外的好處,例如,能夠有設計師和開發人員同時工作。設計師設計工具,例如 Expression Blend,UX 雖然開發人員編寫代碼,使 UXVisual Studio中生活。

可移植類庫

Visual Studio2012年的 PCL 專案使跨平臺開發,允許您選擇生成的程式集將支援的目標框架。Visual Studio2010 作為可選外接程式中引入,PCL 專案範本是現在包括內Visual Studio專業 2012年及以上。

所以什麼代碼可以分享在 PCL 內?

PCLs 之所以這樣命名,是因為他們啟用共用可移植代碼,並且是可移植的代碼必須是託管代碼寫在 C# 或Visual Basic。因為 PCL 產生一個單一的二進位檔案,可移植代碼不雇用條件編譯指令 ; 相反,平臺特定的功能被提取使用介面或抽象基類,這些類。當可移植代碼需要與特定于平臺的代碼進行交互時,依賴注入模式用於提供特定于平臺的抽象物件的實現。在編譯時,PCL 結果在一個單一的程式集,可以從任何基於目標框架的專案引用。

圖 1 展示了那個建議建築的辦法,使共用程式碼中使用 PCLs。與使用 MVVM 模式,查看模型和模型都包含在 PCL,以及特定于平臺的任何功能的抽象。Windows 存儲區和 Windows Phone 應用程式提供啟動邏輯、 視圖和任何抽象的平臺特定的功能的實現。但使用 MVVM 設計模式不是特別需要,使可移植代碼分離模式促進清潔的、 可擴展的體系結構中的結果的關注。


圖 1 共用代碼使用 MVVM 設計模式

Visual Studio2012年中的添加便攜類庫對話方塊是您可以在其中選擇目標框架生成的程式集將會支援。

最初,你可能認為您應該檢查框中為 Silverlight 5,但它不是必需的 Windows 存儲區和 Windows Phone 應用程式間共用代碼。事實上,選擇 Silverlight 5 意味著您可移植的代碼不能利用的一些非常有用的新類型,例如在 Microsoft.NET 框架中 4.5 介紹的 CallerMemberNameAttribute 類。

如果您已經被開發的 Windows Phone,你最有可能熟悉的 MessageBox 類的允許消息顯示給使用者。Windows 應用商店的應用程式使用 Windows 運行庫 MessageDialog 類實現此目的。讓我們看看如何抽象在 PCL 此平臺特定的功能。

中的 IMessagingManager 介面圖 2 文摘平臺特定的功能,以向使用者顯示的消息。IMessagingManager 介面提供了重載的 ShowAsync 方法,需要的消息和標題要向使用者顯示的消息。

圖 2 IMessagingManager 介面

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

ShowAsync 方法被重載,讓您可以選擇指定與消息一起顯示的按鈕。 MessagingButtons 枚舉提供了獨立于平臺抽象為顯示確定按鈕、 確定和取消按鈕或是和沒有按鈕 (請參閱圖 3)。

圖 3 MessagingButtons 枚舉

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

MessagingButtons 枚舉的基礎整數值是故意為了安全地強制轉換為 MessageBoxButton 枚舉 MessagingButtons 枚舉映射到 Windows Phone MessageBoxButton 枚舉。

ShowAsync 是一種非同步方法,返回一項任務­<MessagingResult> 指示按鈕,使用者按一下時解雇該消息。 MessagingResult 枚舉 (見圖 4) 也是一個獨立于平臺的抽象。

圖 4 MessagingResult 枚舉

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

在此示例中,IMessagingManager 介面和消息­按鈕和 MessagingResult 的枚舉值是可移植的並因此可共用 PCL 內。

PCL 中抽象出來的平臺特定功能,您需要為 Windows 存儲區和 Windows Phone 應用程式提供特定于平臺實現 IMessagingManager 介面。 圖 5 演示了 Windows Phone 的應用程式,實現和圖 6 顯示了用於 Windows 應用商店的應用程式執行。

圖 5 MessagingManager — — 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;
  }
}

圖 6 MessagingManager — — Windows 存儲實現

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

Windows Phone 版本的 MessagingManager 類使用特定于平臺的 MessageBox 類來顯示消息。 MessagingButtons 枚舉的基礎整數值被有意地映射到 Windows Phone MessageBoxButton 枚舉,它使您可以安全地強制轉換從 MessagingButtons 枚舉為 MessageBoxButton 枚舉。 同樣的在 MessagingResult 枚舉的基礎整數值允許您將安全地強制轉換為 MessageBoxResult 枚舉。

Windows 存儲區版本中的 MessagingManager 類圖 6 使用 Windows 運行庫 MessageDialog 類來顯示消息。 MessagingButtons 枚舉的基礎整數值被有意地映射到 Windows Phone MessageBoxButton 枚舉,它使您可以安全地強制轉換從 MessagingButtons 枚舉為 MessageBoxButton 枚舉。

相依性的插入 (Dependency Injection)

與定義中所示的應用程式體系結構圖 1,IMessagingManager 提供了特定于平臺抽象為消息的使用者。 我現在會使用依賴注入模式注入這種抽象成可移植代碼平臺特定的實現。 在示例中圖 7,HelloWorldViewModel 使用建構函式注入來注入的 IMessagingManager 介面的特定于平臺實現。 HelloWorldView­Model.DisplayMessage 方法然後使用注射的執行郵件使用者。 若要瞭解更多關於依賴注入,我推薦閱讀"依賴注射在.NET"由MarkSeemann (曼甯出版物,2011 年, bit.ly/dotnetdi)。

圖 7 可擕式 HelloWorldViewModel 類

/// <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 執行階段元件

Windows 運行時元件讓你非可擕式之間共用代碼 Windows 存儲區和 Windows Phone 的應用程式。 元件不是二進位相容的但是,所以您還需要創建類似 Windows 運行庫和 Windows Phone 運行時元件專案,在這兩個平臺上利用代碼。 雖然你要包括在您的 Windows 運行時元件和 Windows Phone 運行時元件的解決方案內的專案,這些專案是使用相同的 c + + 原始程式碼檔生成的。

與共享 Windows 存儲區和 Windows Phone 應用程式之間的本機 c + + 代碼的能力,Windows 運行時元件是寫計算密集型操作操作使用 c + + 實現最佳性能的最佳選擇。

在 Windows 運行時元件內的 API 定義在.winmd 檔中包含的中繼資料中公開。 使用此中繼資料,語言預測讓消費語言確定 API 內那種語言的消耗方式。 圖8顯示為創建和使用 Windows 運行時元件所支援的語言。 在撰寫本文時,只有 c + + 支援創建兩種類型的元件。

圖 8 創建和使用 Windows 運行時元件

平台 建立 消費
Windows 執行階段元件 C + +、 C# 中,Visual Basic C + +、 C# 中,Visual Basic的 JavaScript
Windows Phone 運行時元件 C++ C + +、 C# 中,Visual Basic

在以下示例中,我將演示如何設計 c + + 類來計算斐波那契數位可以在 Windows 應用商店和 Windows Phone 應用程式之間共用。 圖 9圖 10 顯示 FibonacciCalculator 類的實現在 C + + 元件擴展 (CX)。

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

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

圖 11 你可以看到Visual Studio的解決方案資源管理器中的解決方案結構,伴隨著這篇文章,顯示相同的 c + + 的樣本源這兩個元件中包含的檔。


圖 11Visual Studio的解決方案資源管理器

Visual Studio添加為連結能力

將現有專案添加到Visual Studio的一個專案,你可能已經注意到的添加按鈕右邊的小箭頭。如果您按一下這個箭頭,你會提交與選項添加或添加為連結。如果您選擇預設選項,添加一個檔,該檔將被覆制到該專案和該檔的兩個副本將單獨存在磁片上和內源控制 (如果使用)。如果您選擇將作為連結添加將只在磁片上和內源控制項,它可以是非常有用的版本控制的檔的單個實例。添加現有檔在 Visual c + + 專案範圍內的時這是預設行為,並因此添加現有項對話方塊中不提供選項上的添加按鈕。開發中心提供進一步的指導與添加共用程式碼作為連結在 bit.ly/addaslink

Windows 運行時 Api 不是可擕式的並因此不能在 PCL 內共用。Windows 8 和 Windows Phone 8 公開的 Windows 運行時 API 的一個子集,以便可以針對此子集編寫和隨後作為連結使用添加這兩個應用程式之間共用代碼。開發中心提供的共用在 Windows 運行時 API 的子集的詳細資訊 bit.ly/wpruntime

總結

Windows 8 和 Windows Phone 8 的版本,你可以開始探討的如何在其中您可以共用兩個平臺之間的代碼。在本文中,我探討了如何可以使用二進位相容 PCLs 共用可移植代碼,以及如何可以提取特定于平臺的能力。我然後可以使用 Windows 運行時元件共用演示如何非可擕式本機代碼。最後,我討論了Visual Studio添加作為連結選項。

在體系結構中,我注意到推廣的問題,例如使用 MVVM,分離的模式可用於啟用代碼共用,和依賴關係注入模式啟用共用程式碼,以利用特定于平臺的功能。Windows Phone 開發中心提供進一步指導在 Windows 應用商店和 Windows Phone 應用程式間共用代碼 aka.ms/sharecode,並且還提供 PixPresenter 應用程式範例在 bit.ly/pixpresenter

Doug Holland 工程作為高級建築師福音傳教士在微軟開發者和平臺福音事工團隊。他花了幾年來,與戰略夥伴合作,為 Windows 和 Windows Phone 帶來消費為重點的應用程式。他是前 Visual C# MVP 和英特爾黑帶開發人員和作者的"專業 Windows 8 程式設計:用 C# 和 XAML 的應用程式開發"(Wrox 2012),可在 bit.ly/prowin8book

衷心感謝以下技術專家對本文的審閱:Andrew Byrne(Microsoft),DougRothaus (Microsoft) 和MarianLaparu (Microsoft)