Connect(); 2016

第 31 卷,第 12 期

此文章由机器翻译。

移动 - 在 Xamarin.Forms 应用中嵌入本机视图

通过 Charles Petzold

Xamarin.Forms 首次引入小于三年前,当应用程序程序员立即识别它为跨平台移动开发的强大且用途广泛解决方案。您可以在 Visual Studio 中创建 Xamarin.Forms 解决方案并编写整个移动应用程序在 C# 中,无论有 XAML 中,您可以为 iOS、 Android 和通用 Windows 平台 (UWP) 编译。在 macOS 上您可以使用 Xamarin Studio 到面向 iOS 和 Android。

Xamarin.Forms 包含一些 20 控件,例如按钮、 滑块和项,通常称为视图因为它们从 Xamarin.Forms.View 类派生。这些呈现在各种平台使用本机的视图或小组件或控件或元素,即所谓的各种平台上。例如,Xamarin.Forms Entryview 将映射到 iOS UITextField,Android EditText 和 UWP 文本框。

可能之处在于平台呈现器,可以封装本机视图,并公开的属性和对应于相应的 Xamarin.Forms 视图的 API 的事件集合统一的可扩展基础结构。可以定义自己的自定义视图,并支持使用您自己的呈现器,但它不是普通作业。由于这个原因,Xamarin.Forms 已得到增强最近引入避免编写呈现器的麻烦的各种扩展性快捷方式。

这些快捷方式最具吸引力的其中一个称为本机视图功能,您可以实例化本机 iOS、 Android 和 UWP 视图一起正常 Xamarin.Forms 视图。这是什么这篇文章的各个方面。本机视图的情景开始的代码中,而 XAML 开始介入时变得更加有趣。

特定于平台的扩展方法

Xamarin.Forms 支持本机视图具有几个特定于平台的类。每个 Xamarin.Forms 平台包含 LayoutExtensions 类具有名为 ToView 可以在以下的本机类型的后代中调用扩展方法︰

  • iOS: UIKit.UIView
  • Android: Android.Views.View
  • UWP: Windows.UI.Xaml.FrameworkElement

每个版本的 ToView 方法返回 NativeViewWrapper,从 Xamarin.Forms.View 派生而来的特定于平台的实例。NativeViewWrapper 继承的 Xamarin.Forms.View,所有公共和受保护成员,并虽平台而异,都被视为用户 Xamarin.Forms 中普通视图实例。第二个扩展方法进行执行 ToView 操作时向如 StackLayout 布局中添加的视图对象的添加。

每个平台的 NativeViewWrapper 版具有相应的呈现器︰ 一个名为 NativeViewWrapperRenderer 是比大多数呈现器简单得多,因为它不需要支持任何属性、 方法或基础本机控件的事件的类。(Xamarin.Forms 是开放源代码,以便你可以检查这些和相关类在 github.com/xamarin/Xamarin.Forms。)

我们来看一下这是如何工作的。

通常情况下 Xamarin.Forms 解决方案包含很小的存根 (stub) 应用程序项目类型提供的每个平台和常见可移植类库 (PCL),其中包含大部分 Xamarin.Forms 应用程序。但是,如果在代码中使用本机的视图,您不能使用 PCL。相反,您将需要将 Xamarin.Forms 代码放在共享项目中,这在 Xamarin 中通常称为共享资产项目或 SAP。在 Visual Studio 的新建项目对话框而不是常用的空白应用 (Xamarin.Forms Portable) 中选择空白应用 (Xamarin.Forms 共享)。(在 Xamarin Studio 中选择可移植类库或共享库之间与单选按钮。) 此共享的项目中的代码实际上是每个应用程序,这意味着您可以使用 C# 条件编译指令 (#if、 #elif 和 #endif) 来分隔平台特定代码的扩展。

可下载代码中的这篇文章是 HelloNativeViews 程序与 SAP 包含中显示的页类 图 1。(请注意,一些代码示例,以便容纳在可用空间中更改了普通代码缩进操作)。 此类创建的每个平台的标签︰ 适用于 iOS、 android TextView 和 UWP TextBlock UILabel。然后,它调用 ToView 将每个对象转换为一个 Xamarin.Forms.View 对象,但它实际上是一个 NativeViewWrapper 对象。该页面可以将 Xamarin.Forms 属性,如垂直选项和水平选项应用到视图,并最后将其设置为页上的 Content 属性。图 2 显示在所有三个平台上运行每个都有一种字体不同于该平台的程序。

图 1 HelloNativeViews 类

using System;
using Xamarin.Forms;
#if __IOS__
using Xamarin.Forms.Platform.iOS;
using UIKit;
#elif __ANDROID__
using Xamarin.Forms.Platform.Android;
using Android.Graphics;
using Android.Widget;
#elif WINDOWS_UWP
using Xamarin.Forms.Platform.UWP;
using Windows.UI.Text;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Media;
#endif
namespace HelloNativeViews
{
  public class HelloNativeViewsPage : ContentPage
  {
    public HelloNativeViewsPage()
    {
      View view = null;
#if __IOS__
      UILabel label = new UILabel
      {
        Text = "Hello iOS Native!",
        Font = UIFont.FromName("Papyrus", 32f),
      };
      view = label.ToView();
#elif __ANDROID__
      TextView textView = new TextView(Forms.Context)
      {
        Text = "Hello Android Native!",
        Typeface = Typeface.Create("cursive", TypefaceStyle.Normal),
        TextSize = 32f
      };
      view = textView.ToView();
#elif WINDOWS_UWP
      TextBlock textBlock = new TextBlock
      {
        Text = "Hello Windows Native!",
        FontFamily = new FontFamily("Georgia"),
        FontStyle = FontStyle.Italic,
        FontSize = 32
      };
      view = textBlock.ToView();
#endif
      view.HorizontalOptions = LayoutOptions.Center;
      view.VerticalOptions = LayoutOptions.Center;
      Content = view;
    }
  }
}

在 iOS、 Android 和 Windows 10 移动 HelloNativeViews 程序
图 2 HelloNativeViews 程序在 iOS、 Android 和 Windows 10 移动

当然,可以将标签的 FontFamily 属性设置为引用三个字体系列名称的 Device.OnPlatform 方法调用完全在标准 Xamarin.Forms 取得相同的效果。但我认为您可以看到如何扩展此方法更复杂的方式通过利用每个平台支持的特定 Api。

有时您可能需要将自定义调整大小方法应用于这些视图,因此它们将作为正确 Xamarin.Forms 布局对象的子对象。签出的 Xamarin 开发人员站点上的文章 bit.ly/2dhBxDk 的更多详细信息。

虽然这是一种有趣的技术,当然它当然会更好些,若要实例化这些本机直接在 XAML 视图。

纯 XAML 视图

截至 Xamarin.Forms 2.3.3 (这是在预发布状态,当我写这篇文章) 您可以真正嵌入本机视图 Xamarin.Forms 的 XAML 文件中。这些视图上,可以设置属性和事件处理程序。您可以在相同的 XAML 文件中,包含从多个平台的并行视图,并且它们可以与其他所有 Xamarin.Forms 视图进行交互。

此功能的关键之一是对 XAML 文件的 XML 命名空间 (xmlns) 声明的扩展。Xamarin.Forms 中的自定义 XML 命名空间通常使用 clr 命名空间来表示公共语言运行时 (CLR) 命名空间和程序集的程序集。新项是 targetPlatform,该值指示此特定的 XML 命名空间应用到哪一种平台。将此项设置为 Xamarin.Forms TargetPlatform 枚举的成员︰ iOS、 Android 或 Windows UWP。

例如,您可以定义以下 XML 命名空间︰

xmlns:ios ="clr-namespace:UIKit; assembly=Xamarin.iOS; targetPlatform = iOS"

可以使用 XAML 文件中此前缀以引用任何类或结构中的 iOS UIKit 命名空间,例如︰

<ios:UILabel Text="Hello iOS Native!"
             TextColor="{x:Static ios:UIColor.Red}"
             View.VerticalOptions="Center"
             View.HorizontalOptions="Center" \>

文本和 TextColor 是 UILabel,属性和 TextColor 设置为 UIColor 静态只读属性。但是,请注意,VerticalOptions 和 HorizontalOptions 属性的前面有视图中,它们确实是 Xamarin.Forms 视图类的属性。此语法-类、 圆点和属性名称 — 通常用于附加可绑定属性,并在此处指示更高版本将这些属性应用到的结果是从 UILabel 转换 NativeViewWrapper 对象的视图对象。仅对由可绑定属性的属性,可以使用此语法。

若要引用 Android 小组件将需要如下所示 (其中我已经介绍了在这里,三个行上但它在 XAML 文件中必须是在不含空格的同一行) 的 XML 命名空间︰

xmlns:androidWidget="clr-namespace:Android.Widget;
                       assembly=Mono.Android;
                         targetPlatform=Android"

当然,可以使用任何名称用于此命名空间,但我没有使用 android 只是因为 Android 是有点儿棘手比 iOS: 小组件的构造函数通常都需要 Android 上下文对象作为参数。此上下文对象是可用的程序集中 Xamarin.Forms.Platform.Android Xamarin.Forms 命名空间中的窗体类的公共静态属性。若要获取此上下文对象,你将需要此 XML 命名空间 (它还必须在 XAML 文件中的一行)︰

xmlns:formsAndroid="clr-namespace:Xamarin.Forms;
                      assembly=Xamarin.Forms.Platform.Android;
                        targetPlatform=Android"

然后可以通过将参数传递给 X:arguments 属性使用 X:static 标记扩展的构造函数实例化在 XAML 中的 Android 小组件︰

<androidWidget:TextView x:Arguments="{x:Static formsAndroid:Forms.Context}"
                        Text="Hello Android Native!" />

UWP 的程序集名称是很长,具体而言︰ Windows,Version = 255.255.255.255,区域性 = neutral,PublicKeyToken = null,ContentType = WindowsRuntime。适用于 Android 和 UWP,你可能需要使用各种类、 结构和枚举倾向于采用在 UI 标记中所涉及的各种 CLR 命名空间的多个 XML 命名空间规范。

请记住,当 XAML 分析器遇到这些本机视图之一,它不具有访问权限类型转换器通常用于将 XAML 文本字符串转换为对象,因此标记中往往具有更多,无法在与属性和对象的类型匹配。通常,您需要在 XAML 中,使用构造函数或工厂方法时,这意味着,如果 x︰ 参数标记和 X:factorymethod 的元素不熟悉,那么现在是学习的好时机中显式创建对象。

这非常重要: 使用 XAML 本机视图时,不能启用 XAML 编译。编译时 XAML 分析器不具有对这些本机类型的引用。分析必须推迟到运行时,并在该点的 XAML 分析器只会忽略 XML 命名空间前缀都与在其运行该程序的平台不匹配 targetPlatform 的任何内容。(我被告知 Xamarin.Forms 开发人员正在研究消除此限制。)

您不能使用本机视图样式。样式可以针对由 BindableProperty 对象支持的属性,本机视图没有此类属性。

由于运行时 XAML 分析器,这些 XAML 本机视图实例化,可将其包含在 PCL 项目或一个 SAP 中的 XAML 文件中。但是,如果您需要从代码隐藏文件引用本机视图,您必须使用一个 SAP 并分隔与 C# 条件编译指令的特定于平台的代码。

下面是另一个限制︰ 使用 PCL 或一个 SAP 中,不能在 XAML 本机视图上使用 X:name。问题在于,在编译时 XAML 解析程序生成代码文件包含这两个命名的对象作为字段,但如果这些字段基于特定于平台的类型,不能为所有平台编译生成的代码。

XamlNativeViewDemo 程序包含 XAML 文件中所示 图 3 具有三个特定于平台的红色文本字符串和三个特定于平台的按钮。按下按钮时调用事件处理程序将旋转圈中的文本的代码隐藏文件中。

图 3 XamlNativeViewDemo 程序的 XAML 文件

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="https://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:local="clr-namespace:XamlNativeViewDemo"
             xmlns:ios="clr-namespace:UIKit; ... "
             xmlns:androidWidget="clr-namespace:Android.Widget; ... "
             xmlns:androidGraphics="clr-namespace:Android.Graphics; ... "
             xmlns:formsAndroid="clr-namespace:Xamarin.Forms; ... "
             xmlns:winui="clr-namespace:Windows.UI; ... "
             xmlns:winText="clr-namespace:Windows.UI.Text; ... "
             xmlns:winControls="clr-namespace:Windows.UI.Xaml.Controls; ... "
             xmlns:winMedia="clr-namespace:Windows.UI.Xaml.Media; ... "
             x:Class="XamlNativeViewDemo.XamlNativeViewDemoPage">
  <Grid x:Name="grid">
    <ContentView x:Name="textToRotateParent"
                 Grid.Row="0"
                 VerticalOptions="Center"
                 HorizontalOptions="Center">
      <ios:UILabel Text="Text to Rotate"
                   TextColor="{x:Static ios:UIColor.Red}">
        <ios:UILabel.Font>
          <ios:UIFont x:FactoryMethod="FromName">
            <x:Arguments>
              <x:String>Papyrus</x:String>
              <x:Single>32</x:Single>
            </x:Arguments>
          </ios:UIFont>
        </ios:UILabel.Font>
      </ios:UILabel>
      <androidWidget:TextView x:Arguments=
                            "{x:Static formsAndroid:Forms.Context}"
                              Text="Text to Rotate"
                              TextSize="32">
        <androidWidget:TextView.Typeface>
          <androidGraphics:Typeface x:FactoryMethod="Create">
            <x:Arguments>
              <x:String>cursive</x:String>
              <androidGraphics:
                TypefaceStyle>Normal</androidGraphics:TypefaceStyle>
            </x:Arguments>
          </androidGraphics:Typeface>
        </androidWidget:TextView.Typeface>
      </androidWidget:TextView>
      <winControls:TextBlock Text="Text to Rotate"
                             FontSize="32"
                             FontStyle="{x:Static winText:FontStyle.Italic}">
        <winControls:TextBlock.FontFamily>
          <winMedia:FontFamily>
            <x:Arguments>
              <x:String>Georgia</x:String>
            </x:Arguments>
          </winMedia:FontFamily>
        </winControls:TextBlock.FontFamily>
        <winControls:TextBlock.Foreground>
          <winMedia:SolidColorBrush Color="{x:Static winui:Colors.Red}" />
        </winControls:TextBlock.Foreground>
      </winControls:TextBlock>
    </ContentView>
    <ContentView x:Name="rotateButtonParent"
                 Grid.Row="1"
                 VerticalOptions="Center"
                 HorizontalOptions="Center">
      <ios:UIButton TouchUpInside="OnButtonTap" />
      <androidWidget:Button x:Arguments="{x:Static formsAndroid:Forms.Context}"
                            Text="Rotate the Text"
                            Click="OnButtonTap" />
      <winControls:Button Content="Rotate the Text" />
    </ContentView>
  </Grid>
</ContentPage>

我乐观地开始 XamlNativeViewDemo 为 PCL 项目,但它很快就变得很明显,XAML 还需要一些帮助。甚至无法设置上的 iOS UIButton 从 XAML 中的文本。您需要调用一个方法,并且需要代码。同样,无法在 Android TextView 且有属性上设置的文本颜色和 UWP 按钮的单击处理程序属于类型 RoutedEventHandler,涉及一个 RoutedEventArgs 对象,它也不是派生自 EventArgs 和,因此,需要一个特定于平台的处理程序。

这些问题隐含的代码隐藏文件所需的补偿的 XAML,我需要放弃 PCL 改为使用一个 SAP 进一步权限隐含的限制。与 SAP,甚至不能使用 X:name 本机视图,因此我将本机视图放在与 X:name 属性 ContentView,才能访问其代码隐藏文件中的所示 图 4。ContentView 也会设置一些布局属性 (如 VerticalOptions 和 HorizontalOptions) 以避免大量重复对本机视图非常方便。

图 4 XamlNativeViewDemo 程序的代码隐藏文件

using System;
using Xamarin.Forms;
namespace XamlNativeViewDemo
{
  public partial class XamlNativeViewDemoPage : ContentPage
  {
    View viewTextToRotate;
    public XamlNativeViewDemoPage()
    {
      InitializeComponent();
      viewTextToRotate = textToRotateParent.Content;
      View rotateButton = rotateButtonParent.Content;
#if __ANDROID__
      // Set Android text color
      var wrapper =
        (Xamarin.Forms.Platform.Android.NativeViewWrapper)
          viewTextToRotate;
      var textView = (Android.Widget.TextView)wrapper.NativeView;
      textView.SetTextColor(Android.Graphics.Color.Red);
#endif
#if __IOS__
      // Set iOS button text and color
      var wrapper = (Xamarin.Forms.Platform.iOS.NativeViewWrapper)rotateButton;
      var button = (UIKit.UIButton)wrapper.NativeView;
      button.SetTitle("Rotate the Text", UIKit.UIControlState.Normal);
      button.SetTitleColor(UIKit.UIColor.Black, UIKit.UIControlState.Normal);
#endif
#if WINDOWS_UWP
      // Set UWP button Click handler
      var wrapper = (Xamarin.Forms.Platform.UWP.NativeViewWrapper)rotateButton;
      var button = (Windows.UI.Xaml.Controls.Button)wrapper.NativeElement;
      button.Click += (sender, args) => OnButtonTap(sender, EventArgs.Empty);
#endif
    }
    void OnButtonTap(object sender, EventArgs args)
    {
      viewTextToRotate.RelRotateTo(360);
    }
  }
}

我已经使用完全限定为清楚起见,并避免大量的 using 指令的平台的代码隐藏文件中的所有特定于平台的类型。从 NativeViewWrapper 获取基础本机视图的关键是名为 NativeView (对于 iOS 和 Android) 或 NativeElement (适用于 UWP) 的属性。

IOS 和 Android 的按钮可以共享相同的事件处理代码隐藏文件中,因为它定义与事件处理程序委托。但 UWP 按钮必须使用类型 RoutedEventHandler,只需调用使用适用于 iOS 和 Android 的处理程序实现的一个单独的事件处理程序。

在代码隐藏文件中获取访问本机视图的另一种方法涉及枚举子对象版式和搜索的各种类型或 id。所有三个平台定义 Tag 属性 — 在 iOS 和 Android 和 UWP 上的对象的整数,可用于此目的。

我发现 XamlNativeViewDemo 程序不令人满意,因为我不喜欢使用 SAP 为 Xamarin.Forms 应用。我不知道您是否因为我更倾向于 SAP 的 PCL 有关,但如果您是,您将很高兴地看到这篇文章中的最后两个程序是纯 PCL 充满激情。

数据绑定和 MVVM

若要避免代码隐藏文件中的代码的最佳方法之一是构造围绕模型-视图-视图模型体系结构 (MVVM) 应用程序。在独立于平台的 ViewModel 内出现在页面上视图之间的所有交互。ViewModel 连接到使用 Xamarin.Forms 数据绑定的视图 (UI)。数据绑定源是 ViewModel 的属性,而数据绑定目标是视图的属性。

但是,稍等片刻。前面我提到过无法对本机视图都使用一种样式因为设置了样式的属性必须由可绑定属性。数据绑定具有同样的限制︰ 数据绑定的目标属性 — 和 MVVM 利用这些目标始终是页上的视图,必须还属性由支持 BindableProperty 对象。那么,如何在本机视图上设置绑定?

此处是一个好消息︰ 若要对 XAML 本机视图支持数据绑定,每个平台包含自动生成动态即席 BindableProperty 对象的 SetBinding 扩展方法。这些 BindableProperty 对象允许将更改从 ViewModel 的本机属性值。

现在您可能要考虑的另一个问题︰ 在许多情况下这些数据绑定需继续执行这两种方式 — 不仅从源到目标,但从目标到源。在用户界面视图中的更改必须反映在 ViewModel 属性。Xamarin.Forms BindableProperty 基础结构支持 INotifyPropertyChanged 接口,通过属性更改的通知,但本机视图不支持此接口,那么,如何绑定对象可以知道本机视图的属性值更改时?

Binding 类现在具有新属性︰ UpdateSourceEventName。在 XAML 中绑定标记扩展可以设置此属性,在本机视图,用于通知的目标属性已更改时事件的名称。

中显示的 PlatformRgbSliders 程序 图 5 是一个简单示例。(一些重复的标记已替换为省略号。) XAML 文件包含以下三组特定于平台的滑块,我增强,从而相对应的颜色在程序中的功能。(您无法针对 Android 拖动条执行此操作。) 设置为页上的 BindingContext RgbColorViewModel 定义红色、 绿色和蓝色属性,使用它来构造的颜色属性。

图 5 PlatformRgbSliders 程序的 XAML 文件

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="https://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:local="clr-namespace:PlatformRgbSliders"
             xmlns:ios="clr-namespace:UIKit; ... "
             xmlns:androidWidget="clr-namespace:Android.Widget; ... "
             xmlns:formsAndroid="clr-namespace:Xamarin.Forms; ... "
             xmlns:winControls="clr-namespace:Windows.UI.Xaml.Controls; ... "
             xmlns:winMedia="clr-namespace:Windows.UI.Xaml.Media; ... "
             xmlns:winui="clr-namespace:Windows.UI; ... "
             x:Class="PlatformRgbSliders.PlatformRgbSlidersPage">
  <ContentPage.Padding>
    <OnPlatform x:TypeArguments="Thickness"
                iOS="0, 20, 0, 0" />
  </ContentPage.Padding>
  <ContentPage.Resources>
    <ResourceDictionary>
      <local:DoubleToSingleConverter x:Key="doubleToSingle" />
      <local:DoubleToIntConverter x:Key="doubleToInt"
                                        Multiplier="256" />
    </ResourceDictionary>
  </ContentPage.Resources>
  <ContentPage.BindingContext>
    <local:RgbColorViewModel Color="Gray" />
  </ContentPage.BindingContext>
  <Grid>
    <Grid.RowDefinitions>
      <RowDefinition Height="3*" />
      <RowDefinition Height="*" />
      <RowDefinition Height="*" />
      <RowDefinition Height="*" />
    </Grid.RowDefinitions>
    <BoxView Grid.Row="0"
             Color="{Binding Color}" />
    <ios:UISlider Grid.Row="1"
                  ThumbTintColor="{x:Static ios:UIColor.Red}"
                  MinimumTrackTintColor="{x:Static ios:UIColor.Black}"
                  MaximumTrackTintColor="{x:Static ios:UIColor.Red}"
                  Value="{Binding Red,
                          Mode=TwoWay,
                          UpdateSourceEventName=ValueChanged,
                          Converter={StaticResource doubleToSingle}}"/>
    ...
    <androidWidget:SeekBar x:Arguments=
                           "{x:Static formsAndroid:Forms.Context}"
                           Grid.Row="2"
                           Max="256"
                           Progress="{Binding Green,
                             Mode=TwoWay,
                             UpdateSourceEventName=ProgressChanged,
                             Converter={StaticResource doubleToInt}}" />
    ...
    <winControls:Slider Grid.Row="3"
                        Maximum="1"
                        StepFrequency="0.01"
                        IsThumbToolTipEnabled="True"
                        Value="{Binding Blue,
                                Mode=TwoWay,
                                UpdateSourceEventName=ValueChanged}">
      <winControls:Slider.Foreground>
        <winMedia:SolidColorBrush Color=
          "{x:Static winui:Colors.Blue}" />
      </winControls:Slider.Foreground>
    </winControls:Slider>
  </Grid>
</ContentPage>

在 iOS UISlider 上的绑定需要将 ViewModel 中的双精度值转换为浮点值的值转换器,Android 拖动条需要值转换器将双精度值转换为整数值。您可以看到,所有数据绑定将都使用 UpdateSourceEventName 属性,因此可接收通知的绑定类,当用户已更改的滑块值。结果显示在图 6 中。

在三个平台上运行的 PlatformRgbSliders 程序
图 6 在三个平台上运行的 PlatformRgbSliders 程序

下面是一个有趣的实验︰ 删除 Windows 滑块绑定 UpdateSourceEventName 项。此程序仍然起作用。这是因为 Xamarin.Forms 是能够使用内置于 UWP 依赖项属性的通知机制。此外,工作要做是为了允许 UpdateSourceEventName 如果视图实现键 / 值观察要消除对 iOS 的视图。

PlatformRgbSliders 在代码隐藏文件中,有没有具体问题的代码,因此没有毫无问题地使用 PCL。但是,不可否认,PlatformRgbSliders 很简单。您将能够在较大的程序中使用 Pcl?

首先,它看起来不很有前景︰ 许多 iOS 和 Android 的本机视图只是有利于在 XAML 中,实例化并没有真正没有的理由为什么应将它们。可总结问题︰ 此外,并非总是足够 iOS 和 Android 的重要设置和访问所需要的选项的视图中的属性。相反,这里有太多的方法。

太多的方法

若要使 iOS 和 Android 更适用于 XAML,您需要将一些方法替换为属性。很明显 UWP 是好得多这方面因为它专为 XAML 中,但正如您看到 UWP 事件处理程序通常基于特定于平台的委托。

此问题最直接的解决方案是子类化的特定于平台的视图定义包含属性和独立于平台的事件的更多的 XAML 友好 API 的包装中。你还可能会启动以查看在 XAML 中嵌入纯视图的真正功能时,如果创建 (或使用) 自定义的 iOS、 Android 和 UWP 视图来 Xamarin.Forms 应用程序中使用。

但是,其中将这些类呢?

如果您使用的一个 SAP,可以将其放在 SAP 中,将其括起与 C# 条件编译指令。但是,如果你想要使用 PCL — 并且你通常想要使用 PCL,则无法做到这一点。PCL 只可以引用另一个 PCL,并通过定义 PCL 不能包含任何 iOS、 Android 或 UWP 代码。它是一点困惑。

至少在开始。

请记住您正在不编译 XAML。在编译时分析 XAML,但这通常是生成一个包含与 X:name 特性对应的字段的代码文件。您已看到未编译的 XAML 可包含引用到 iOS、 Android 和 UWP 类。这些引用在运行时解析而不是编译时,并会在世界上的所有差别。在运行时,XAML 分析器有权访问所有组成该应用程序,并且包括单个平台启动项目的程序集。

这意味着,可以将特定于平台的类放在平台应用程序项目中,也可以将代码放在这些应用程序项目引用的特定于平台的库。可以从 XAML 引用这些类。这看起来可能奇怪,首先为 XAML 文件中的引用中的类应用程序程序集,PCL 在非自然,但一切正常。

PlatformSpinners 解决方案演示了这种技术。其思路是使用 iOS UIPickerView,Android 微调框和用于选择的内容从列表中,UWP 组合框,但应 UIPickerView 和微调框有需要作为属性公开一些方法。此外,UIPickerView 需要的数据模型,必须在代码中实现。

出于此原因,PlatformSpinners.iOS 应用程序项目包含 PickerDataModel 和派生自 UIPickerView,因此称为,因为它将重要的属性添加到 UIPickerView PropertiedUIPickerView。PlatformSpinners.Droid 项目包含派生自微调框 PropertiedSpinner。

PlatformSpinners PCL 包含简单视图模型公开集合的 Xamarin.Forms 的颜色名称,并将这些颜色名称转换为实际的颜色。图 7 显示完整的 XAML 文件除外的长 XML 命名空间和 图 8 显示三个平台和 Android 的微调框和 UWP ComboBox 上运行它打开以显示选项。

图 7 PlatformSpinners XAML 文件

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="https://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:local="clr-namespace:PlatformSpinners"
             xmlns:iosLocal="clr-namespace:PlatformSpinners.iOS; ... "
             xmlns:androidLocal="clr-namespace:PlatformSpinners.Droid; ... "
             xmlns:formsAndroid="clr-namespace:Xamarin.Forms; ... "
             xmlns:winControls="clr-namespace:Windows.UI.Xaml.Controls; ... "
             x:Class="PlatformSpinners.PlatformSpinnersPage">
  <ContentPage.Padding>
    <OnPlatform x:TypeArguments="Thickness"
                iOS="0, 20, 0, 0" />
  </ContentPage.Padding>
  <ContentPage.BindingContext>
    <local:ColorNameViewModel SelectedColorName="Black" />
  </ContentPage.BindingContext>
  <Grid>
    <Grid.RowDefinitions>
      <RowDefinition Height="*" />
      <RowDefinition Height="*" />
    </Grid.RowDefinitions>
    <BoxView Grid.Row="0"
             Color="{Binding SelectedColor}" />
    <iosLocal:PropertiedUIPickerView Grid.Row="1"
                                     Items="{Binding ColorNames}"
                                     SelectedItem=
                                       "{Binding SelectedColorName,
                                       Mode=TwoWay,
                                       UpdateSourceEventName=ValueChanged}"/>
    <androidLocal:PropertiedSpinner x:Arguments=
                                      "{x:Static formsAndroid:Forms.Context}"
                                    Grid.Row="1"
                                    View.VerticalOptions="Center"
                                    Items="{Binding ColorNames}"
                                    SelectedObject=
                                      "{Binding SelectedColorName,
                                      Mode=TwoWay,
                                      UpdateSourceEventName=ItemSelected}" />
    <winControls:ComboBox Grid.Row="1"
                          View.VerticalOptions="Center"
                          ItemsSource="{Binding ColorNames}"
                          SelectedItem=
                            "{Binding SelectedColorName,
                            Mode=TwoWay,
                            UpdateSourceEventName=SelectionChanged}"/>
  </Grid>
</ContentPage>

PlatformSpinners 三个平台上运行
图 8 PlatformSpinners 三个平台上运行

提供附加属性的此技术是您可能希望使用第三方 iOS 和 Android 自定义视图时执行的操作。子类的视图,使其便于为 XAML 和 MVVM 数据绑定,并通常将是胜利在望。

是真的更容易?

您已了解如何 Xamarin.Forms 现在允许你引用本机视图 — 或类派生自本机视图 — 直接在 XAML 而不是隐藏特定于平台的代码即可在呈现器和定义独立于平台的界面中。

因此,您可能会问︰ 这是比创建呈现器真的更容易吗?

是的,确实如此。


Charles Petzold 具有在过去的 30 年为 MSDN 杂志 》 和其前身,Microsoft Systems Journal 编写大量文章。 现在,他任职于 microsoft 的 Xamarin 文档组,也是"创建移动应用程序使用 Xamarin.Forms,"可用于从 Xamarin 网站下载免费书的作者。

衷心感谢以下 Microsoft 技术专家对本文的审阅: David Britch、 Stephane Delcroix 和 Rui Marinho
David Britch 在 Microsoft 的 Xamarin 文档组工作。他曾为各种软件开发出版物包括书籍、 指南文档、 参考实现、 白皮书、 视频和讲师引导式培训课程,编写了。

Stephane Delcroix 适用于 Xamarin.Forms 团队 microsoft XAML 和的详细信息。

Rui Marinho 是供职于在 Microsoft,Xamarin.Forms 团队的葡萄牙语软件工程师和是一位热心的编码员和开源参与者。