此文章由机器翻译。

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 的图形表示。

模型-视图-视图模型模式概述
图 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

本地编译与 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