本文章是由機器翻譯。

Windows Phone

使用 Xamarin 及 MvvmCross 建置 MVVM 應用程式

Thomas LeBrun

模型-視圖-模型 (MVVM) 模式勢必成為任何 XAML (Windows Presentation Foundation[WPF]、 Windows 8、 Windows Phone 和 Silverlight) 應用程式選擇的參考模式。 介紹了在 WPF 的開始,它分離關切、 可測試性和更多。 最好的部分是你可以使用它的任何其他的技術,甚至包括那些不使用 XAML。 事實上,你可以用ASP.NET,JavaScript 和更多的使用模式。

Xamarin 使您可以開發安卓或 iOS 應用程式中的 C# 代碼。 這些應用程式來與他們自己的發展模式,但由於一個稱為 MvvmCross 的框架,你可以把這些平臺以及 MVVM 模式。 在這篇文章,我會給你所有你需要瞭解 MvvmCross,以及如何在 Android 和 iOS 應用程式中使用它。

快看看 MVVM

有很多條,涉及 MVVM 最近,所以我不會花很多時間複習 MVVM 模式。 總結一下,使用 MVVM 是由三部分組成:(對應于要顯示和操作在螢幕上的資料) 的模型、 視圖 (這是演示文稿元件和使用者介面) 和 ViewModel (其中將採取模式和顯示它上使用資料繫結的視圖,並將對使用者交互作出回應)。 圖 1 顯示 MVVM 的圖形表示。

Overview of the Model-View-ViewModel Pattern
圖 1 模型-視圖-視圖模型模式概述

隨著 Microsoft 技術的發展,時,容易看到 MVVM 所提供的再使用性。 但非微軟的技術怎麼樣? 安卓系統呢? IOS 呢?

當然,你仍然可以實現您自己的模式或方法,但那些可能不會提供一些 MVVM,如資料繫結和可測試性的最強大功能。 以下 MVVM 的最大好處之一就是 viewmodel 都是很容易檢驗。 這也讓你推入 ViewModel 跨平臺的代碼。 此代碼將否則載于一個平臺 — —­特定類,像一個控制器。

Xamarin 和 MvvmCross"解決"這個問題並提供了一種統一的方式在其他平臺上使用 MVVM。 在其他平臺上使用 MVVM 之前,我要解釋 Xamarin 一下。

Xamarin 為 Android/iOS 應用程式的

Xamarin 是一套工具,本機的所有 api 提供高性能已編譯的代碼具有完全存取權限。 它允許您創建本機應用程式具有特定于設備的經驗。 你可以在目標 C 或JAVA中的任何東西,你可以在 C# 中的 Xamarin。

雖然您可以使用 Xamarin 工作室開發的應用程式,您還可以使用Visual Studio和你已經在使用 C# 發展今天的所有其他工具。 這包括Team Foundation伺服器 (為源控制項) 和外掛程式如 Resharper GhostDoc 等等。

從開發人員的角度來看,Xamarin 提供了三個主要的 products—Xamarin.Mac,Xamarin.iOS (MonoTouch.dll) 和 Xamarin.Android (Mono.Android.dll)。 所有的這些都是在單聲道,開放原始碼版本的 Microsoft.NET 框架開發。 單聲道其實最初是由MiguelDe Icaza 的聯合創始人和當前 Xamarin 首席技術官。

在 iOS,專用的編譯器編譯直接到手臂的本機代碼在 C# 編寫的應用程式。 安卓系統,過程是類似于.NET 編譯和執行。 原始程式碼編譯為中間語言 (IL)。 當在設備上執行代碼時,(只是在時間執行) 第二次彙編編譯為本機代碼的 IL 代碼。 這有意義,因為在JAVA,具有類似于.NET 框架的體系結構的內部體系結構開發 Android 應用程式。 你可以看到 iOS 和 Android 在編譯過程的視覺化表示形式圖 2。

Native Compilation with Xamarin
圖 2 本地編譯與 Xamarin

大部分的時間,你不必擔心記憶體管理、 資源配置等,因為一切都由 Xamarin 提供的運行時。 然而,有的案件時你必須要意識到發生了什麼,如與目的 C.交互操作 這可以創建保留週期或在您的託管的類實際上換一些昂貴的資源,如 UIImage 在 iOS 上。 這的更多資訊,請參見 bit.ly/1iRCIa2

有的 iOS 應用程式的特殊注意事項。 而在 Mac 上的 Xamarin 工作室提供一切所需的 iOS 開發­發展,Visual Studio使用者在 PC 上的將仍需要 Mac 安裝的 Xamarin 工具。 這允許您通過網路編譯的應用程式並測試它們的 ios 模擬器或 iOS 設備。

Xamarin 可以讓你構建的 iOS 和 Android 應用程式使用的 C# 或 F # 中,但使用傳統的模型-視圖-控制器模式。 如果你想增加的可測試性、 可維護性和可攜性,需要一種方法來為這些平臺帶來 MVVM 模式。 輸入 MvvmCross。

MvvmCross Xamarin 的應用程式

MvvmCross 是一個開源的跨平臺使用 MVVM 框架開發的斯圖亞特 · 洛奇。 它是可用於 Windows Phone,Windows 8,iOS,安卓系統和 WPF 應用程式。 MvvmCross 給平臺那裡以前不可用的像 iOS 和 Android 帶來 MVVM 模式。

在視圖中,它還支援資料繫結。 這是一個強大的功能,提供了極大的關注點分離。 視圖將使用 Viewmodel 提供在應用程式中的正確行為。 MvvmCross 甚至位於 Viewmodel 專用專案以便您可以輕鬆地引用和在他人中重用它們。

在談到 MvvmCross 時,這是最重要的一點。 通過定位 Viewmodel 中可擕式類圖書館 (PCL),你可以添加它們作為對任何其他專案的引用。 當然,這不是 MvvmCross 只有趣點。 也是一個外掛程式架構,依賴注入 (DI) 和更多。

在 Android/iOS 上使用 MvvmCross

使用 MvvmCross 是很容易的因為它是只有幾個 NuGet 包您將添加到您的專案。 一旦你完成了這,還有你必須採取在啟動應用程式之前的幾個小步驟。 步驟有所不同有點之間 iOS 和安卓系統,但他們非常相似。 核心專案包含您 Viewmodel 和 App 類。 此初始化服務並定義 ViewModel 將開始啟動:

public class App : MvxApplication
{
  public override void Initialize()
  {
    this.CreatableTypes()
      .EndingWith("Service")
      .AsInterfaces()
      .RegisterAsLazySingleton();
    this.RegisterAppStart<HomeViewModel>();
  }
}

在你的 iOS 或 Android 應用程式,您必須創建一個 Setup.cs 檔。 這將會引用核心專案,讓運行時知道如何具現化應用程式:

public class Setup : MvxAndroidSetup
{
  public Setup(Context applicationContext) : base(applicationContext)
  {
  }
  protected override IMvxApplication CreateApp()
  {
    return new Core.App();
  }
}

所以安裝檔創建使用該應用程式檔的應用程式。 後者指示運行時載入特定 ViewModel 在啟動時使用 RegisterAppStart 方法。

每個視圖是特定于每個應用程式。 這是改變的唯一部分。 在安卓系統上的類繼承從 MvxActivity (在 Android 上的活動標準繼承從活動)。 Ios,意見從 MvxViewController (標準 ViewController 在 iOS 上的繼承 UIViewController) 繼承:

[Activity(ScreenOrientation = ScreenOrientation.Portrait)]
public class HomeView : MvxActivity
{
  protected override void OnViewModelSet()
  {
    SetContentView(Resource.Layout.HomeView);
  }
}

MvvmCross 需要知道的 ViewModel 與檢視器關聯。 你可以在預設情況下由於的命名約定。 您還可以輕鬆更改它通過重寫視圖的視圖模型屬性或使用 MvxViewForAttribute:

[Activity(ScreenOrientation = ScreenOrientation.Portrait)]
[MvxViewFor(typeof(HomeViewModel))]
public class HomeView : MvxActivity
{ ... }

在 iOS 和 Android,意見的設計不同的方法。 在 iOS,該視圖定義中的 C# 代碼。 安卓,你還可以使用 C# 代碼。 甚至更好,不過,您可以使用 AXML 格式 (XML 格式用於描述使用者介面在 android 系統上)。 因為這些差異,資料繫結是以不同的方式定義的每個平臺上。

在 iOS,你創建的 BindingDescriptionSet 來表示視圖和視圖模型之間的聯繫。 在那一組,您指定您想要申請綁定前綁定到哪個屬性的控制項:

var label = new UILabel(new RectangleF(10, 10, 300, 40));
Add(label);
var textField = new UITextField(new RectangleF(10, 50, 300, 40));
Add(textField);
var set = this.CreateBindingSet<HomeView, 
  Core.ViewModels.HomeViewModel>();
set.Bind(label).To(vm => vm.Hello);
set.Bind(textField).To(vm => vm.Hello);
set.Apply();

在安卓系統使用 AXML,你可以使用新的 XML 屬性 MvxBind 執行資料繫結:

<TextView xmlns:local="http://schemas.android.com/apk/res-auto"
          android:text="Text"
          android:layout_width="match_parent"
          android:layout_height="wrap_content"
          android:id="@+id/tripitem_title"
          local:MvxBind="Text Name"
          android:gravity="center_vertical"
          android:textSize="17dp" />

MvxBind 屬性以指定要綁定的控制項的屬性和 ViewModel 使用作為源的屬性的參數。 如果你是一個 XAML 開發人員,請注意 MvvmCross 綁定模式,預設情況下是雙向。 在 XAML 中,預設綁定模式是單向。 MvvmCross 框架理解幾個自訂 XML 屬性可用在 Mvx­BindingAttributes.xml,如你可以看到在圖 3

圖 3 MvxBindingAttributes.xml File 的內容

<?xml version="1.0" encoding="utf-8"?>
<resources>
  <declare-stylable name="MvxBinding">
    <attr name="MvxBind" format="string"/>
    <attr name="MvxLang” format="string"/>
  </declare-styleable>
  <declare-stylable name="MvxControl">
    <attr name="MvxTemplate" format="string"/>
  </declare-styleable>
  <declare-styleable name="MvxListView">
    <attr name="MvxItemTemplate" format= "string"/>
    <attr name="MvxDropDownItemTemplate" format="string"/>
  </declare-stylable>
  <item type="id" name="MvxBindingTagUnique">
  <declare-styleable name="MvxImageView">
    <attr name="MvxSource" format="string"/>
  </declare-stylable>
</resources>

這個檔的內容很簡單,但非常重要的。 該檔指示你可以在 AXML 檔中使用的屬性。 所以,你可以看到在綁定操作中,您可以使用 MvxBind 或 MvxLang 屬性。 您還可以使用一些新的控制項 (MvxImageView,MvxListView)。 他們每個人都有專門的自訂屬性,正如你可以看到在圖 4

圖 4 新控制項具有自訂屬性

<LinearLayout
  android:orientation="horizontal"
  android:layout_width="fill_parent"
  android:layout_height="fill_parent"
  android:layout_weight="2">
  <Mvx.MvxListView
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    local:MvxBind="ItemsSource Trips;ItemClick SelectTripCommand"
    local:MvxItemTemplate="@layout/tripitemtemplate" />
</LinearLayout>

這應該是熟悉的 XAML 開發人員。 若和專案按一下屬性綁定到資料來源 (在本例中 ViewModel) 的一些性質。 MvxItemTemplate 在 ListView 中定義的每個專案的介面。

你可能期望的語法的 iOS 是完全不同的但是,在現實中,它是其實頗為相似。 在 AXML 檔中,使用的語法綁定"流利",被稱為是只是您可以映射到的 C# 版本,以及一個文本格式綁定。 在前面的示例中用於挑選清單中的項的命令是 ICommand 物件:

public ICommand<Trip> SelectTripCommand { get; set; }

此介面的實現是通過 MvvmCross 使用 MvxCommand 類提供的:

private void InitializeCommands()
{
  this.SelectTripCommand = new MvxCommand<Trip>(
    trip => this.ShowViewModel<TripDetailsViewModel>(trip),
    trip => this.Trips != null && this.Trips.Any() && trip != null);
}

一個常見的問題,當使用 MVVM 模式轉換類型。 發生這種情況是當你定義屬性使用一種類型,並不是直接消耗由 UI。 例如,您可能有一個位元組陣列作為圖像屬性,但您想要使用的源屬性的影像控制。 在 XAML 中,您可以解決這一問題的 IValueConverter 介面,映射視圖和視圖模型之間的值。

過程是相當類似與 MvvmCross,IMvxValueConverter 介面和其轉換和 ConvertBack 的兩個方法。 此介面使您可以執行類似的轉換為 XAML,但銘記的交叉技術目標。 請牢記這介面具有相同的缺點作為 XAML。 它採用物件作為參數並返回它作為值。 所以鑄造是必需的。 為了優化這,MvvmCross 提供了泛型的 MvxValueConverter 類,以用於輸入和返回類型的參數:

public class ByteArrayToImageConverter : 
  MvxValueConverter<byte[], Bitmap>
{
  protected override Bitmap Convert(byte[] value, Type targetType,
    object parameter, CultureInfo culture)
  {
    if (value == null)
        return null;
    var options = new BitmapFactory.Options { InPurgeable = true };
    return BitmapFactory.DecodeByteArray(value, 
      0, value.Length, options);
  }
}

引用轉換器是很容易的。 在 iOS,使用 WithConversion 方法在流利的語法:

var set = this.CreateBindingSet<HomeView, 
  Core.ViewModels.HomeViewModel>();
set.Bind(label).To(vm => vm.Trips).WithConversion("ByteArrayToImage");
set.Apply();

在 Android 中,引用的轉換器直接在 AXML 檔中:

<ImageView
  local:MvxBind="Bitmap Image,Converter=ByteArrayToImage"
  android:layout_width="fill_parent"
  android:layout_height="wrap_content" />

轉換器位於使用反射他們的名字。 預設情況下,框架將搜索包含在名稱中的"轉換器"任何類型。 您可以通過重寫中的安裝程式類的 FillValueConverters 方法將還手動註冊轉換器。

MvvmCross 提供了簡單、 品質輕的 DIcontainer。 你可以在使用多個模式,包括單身人士註冊、 動態註冊和更多的容器註冊的類和介面:

Mvx.RegisterType<ISQLiteConnectionFactory, SQLiteConnectionFactory>();
Mvx.RegisterSingletong<ISQLiteConnectionFactory, SQLiteConnectionFactory>();

解析類型的容器中會發生兩種方式。 首先,你可以使用 Mvx.Resolve 方法來顯式解析類型。 它還支援建構函式注入,讓 MvvmCross 執行反射和自動解決在物件創建過程中的參數:

private readonly ISQLiteConnectionFactory _sqlFactory;
public DataAccessLayerService(ISQLiteConnectionFactory sqlFactory)
{
  this._sqlFactory = sqlFactory;
}

您可以使用建構函式注入的服務,以及 Viewmodel。 這是重要的是理解因為任何服務,為您的應用程式放在核心專案中開發,在預設情況下,跨平臺。

您還可以利用服務,似乎是跨平臺的但是對於哪一種實現是特定于平臺的。 例如,拍一張照片用相機,獲得使用者座標,使用一個資料庫,等等。 建構函式注入,Viewmodel 可以得到執行的特定于平臺的介面。

要進一步定義這種機制的注射和特定于平臺的代碼,MvvmCross 提供了一個外掛程式系統。 該系統允許您創建和運行時注入新的功能。 每個外掛程式是一種服務,公開的介面,它為每個平臺提供一個具體實現。 外掛程式系統註冊介面和實現。 應用程式使用 DI 的外掛程式。 迪也讓外掛程式開發人員提供一個簡化的"假"實現測試和開發階段。

從開發人員的角度來看,編寫一個外掛程式是一樣簡單寫一個介面。 您創建一個類來實現的介面和一個外掛程式的載入程式。 外掛程式載入程式是一個類,實現 IMvxPluginLoader 介面。 它註冊的外掛程式介面與實現 (使用 Mvx.RegisterType) 調用其 EnsureLoaded 方法時。 

有很多外掛程式已經可用。 他們會提供訪問檔、 電子郵件、 JSON 轉換等功能等等。 你可以找到其中的大多數使用 NuGet。 請注意某些外掛程式不包括所有平臺的實現。 規劃利用外掛程式時注意這個細節。 即使外掛程式缺少您的平臺的支援,你可能會發現更容易地遵循的模式和執行而不是創建一個新的缺少平臺上您自己的外掛程式。 如果發生這種情況,考慮您回外掛程式擁有者的實現作出貢獻,所以別人也可以使用它,以及。

MvvmCross 是一個寶貴的框架。 在開發移動應用程式時考慮這 — — 即使在 Android 和 iOS。 MVVM 模式,再加上資料繫結和外掛程式程式,為創建高度可維護性和可移植的代碼提供了一個強大的系統。


Thomas LeBrun 是在無限廣場,法語 Microsoft 合作夥伴致力於 Windows 8、 Windows Phone、Windows Presentation Foundation(WPF),Silverlight,表面和更多的技術顧問。有關 WPF 和 MVVM 模式,他寫了兩本書。他也是普通的音響喇叭在社區活動。你可以跟隨他的博客就是 blog.thomaslebrun.net 和在 Twitter 上 twitter.com/thomas_lebrun

感謝以下的微軟技術專家對本文的審閱:傑瑞德 Bienz