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 包括通常稱為檢視它們衍生自 Xamarin.Forms.View 類別,因為某些 20 控制項,例如按鈕、 滑桿和項目。這些會呈現不同的平台使用原生的檢視,或 widget,或控制項或項目,所謂在各種平台上。例如,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 內的一般檢視執行個體。第二個擴充方法是加入執行時檢視物件加入至配置,例如 StackLayout ToView 作業。

每個平台版本 NativeViewWrapper 具有對應的轉譯器︰ NativeViewWrapperRenderer 非常比大部分的轉譯器,因為它不需要任何屬性、 方法或事件的基礎原生控制項支援名為的類別。(Xamarin.Forms 是開放原始碼,因此您可以檢查這些錯誤和相關類別 github.com/xamarin/Xamarin.Forms。)

讓我們查看其運作方式。

通常 Xamarin.Forms 方案包含小虛設常式每個平台和一般可攜式類別庫 (PCL),其中包含大量的 Xamarin.Forms 應用程式的應用程式專案。不過,當程式碼中使用原生的檢視,您無法使用 PCL。相反地,您必須將 Xamarin.Forms 程式碼放在共用專案中,這在 Xamarin 中通常稱為共用資產專案或 SAP。在 Visual Studio 的 [新增專案] 對話方塊中選取 [空白應用程式 (Xamarin.Forms 共用) 而不是一般的空白應用程式 (Xamarin.Forms 可攜式)。(在 Xamarin Studio 中您選取可攜式類別庫或共用程式庫之間使用選項按鈕)。 此共用的專案中的程式碼實際上就是每個應用程式,這表示您可以使用 C# 條件式編譯指示詞 (#if、 #elif 和 #endif) 來分隔平台特定程式碼的延伸模組。

可下載的程式碼的這篇文章是 HelloNativeViews 程式,與 SAP,其中包含所顯示的頁面類別 [圖 1。(請注意正常的程式碼縮排作法已被竄改,無法納入可用空間的一些程式碼範例中)。 這個類別會建立每個平台的標籤︰ UILabel ios、 android TextView 和 UWP 的 TextBlock。然後它會呼叫 ToView 到每個物件轉換成 Xamarin.Forms.View 物件,但這是一個 NativeViewWrapper 物件。網頁可以再套用到檢視的 Xamarin.Forms 屬性,例如垂直與水平的選項,並最後將它設為頁面的內容屬性。[圖 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 命名空間通常會使用以表示 Common Language Runtime (CLR) 命名空間和組件的組件的 clr 命名空間。新的項目是 targetPlatform,表示這個特定的 XML 命名空間適用的平台。您將此項目設定為 Xamarin.Forms TargetPlatform 列舉的成員︰ iOS、 Android 或 UWP 的 Windows。

例如,您可以定義下列的 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 widget,您必須如下所示 (我已經示範三行,但在 XAML 檔案中必須是在同一行,不含空格) 的 XML 命名空間︰

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

您可以使用任何名稱,此命名空間,當然,但我只是 android 使用,因此請避免 Android 就比較困難於 iOS: Widget 建構函式通常會需要 Android 內容物件做為引數。這個內容物件是 Xamarin.Forms.Platform.Android 組件中的 Xamarin.Forms 命名空間中的表單類別的公用靜態屬性。若要取得此內容物件,您將需要此 XML 命名空間 (這也必須是在 XAML 檔案中的同一行)︰

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

然後,您可以藉由使用 X:static 標記延伸的 x︰ 引數屬性建構函式傳遞引數初始化在 XAML 中的 Android widget:

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

UWP 的組件名稱是很長,尤其是︰ Windows,Version = 255.255.255.255,Culture = neutral,PublicKeyToken = null,ContentType = WindowsRuntime。Android 和 UWP,您可能需要多個 XML 命名空間規格的各種不同類別、 結構和列舉型別,通常涉及 UI 的標記所使用的 CLR 命名空間。

請記住,當 XAML 剖析器遇到這些原生模式時,它不會擁有存取權類型通常用來轉換為 XAML 文字字串物件,讓標記通常是更廣泛,符合屬性和物件的型別轉換子。通常,您必須明確建立物件,在 XAML 中使用建構函式或 factory 方法,這表示如果您不熟悉 X:arguments 標記和 X:factorymethod 項目,現在是學習的好時機。

這是很重要︰ 使用 XAML 的原生檢視時,您無法啟用 XAML 編譯。編譯時期 XAML 剖析器不會有這些原生型別的參考。剖析必須延遲到執行階段,並在該點 XAML 剖析器會完全忽略任何 XML 命名空間前置詞已不符合程式執行所在的平台 targetPlatform。(我聽說這麼 Xamarin.Forms 開發人員同時處理移除這項限制。)

您無法使用原生檢視樣式。原生的檢視表沒有這類屬性和樣式可以目標都由 BindableProperty 物件的屬性。

因為這些原生 XAML 的檢視表會產生執行階段 XAML 剖析器,您可以將它們包含 PCL 專案或 SAP 中的 XAML 檔案中。不過,如果您需要從程式碼後置檔案參考原生的檢視,您必須使用 SAP,並分隔平台特定程式碼與 C# 條件式編譯指示詞。

以下是另一項限制︰ PCL 或 SAP 中,您無法使用 X:name XAML 原生的檢視。問題是編譯時期 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 需要一些協助。您甚至不能從 XAML 設定 iOS 標示的文字。您需要呼叫方法,而且需要程式碼。同樣地,您不能設定屬性後,Android TextView 的文字色彩,UWP 按鈕的 Click 處理常式的類型 RoutedEventHandler,這牽涉到 RoutedEventArgs 物件,也不是衍生自 EventArgs 及,因此,需要的平台特定處理常式。

這些問題隱含的程式碼後置檔案所需補償的 XAML,進一步隱含的我必須放棄 PCL 並改為使用 SAP 的限制。即使使用 SAP,就不能使用 X:name 原生的檢視,讓我放入 ContentView 使用 X:name 屬性來存取它們在程式碼後置檔案中所示的原生檢視 [圖 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 的個別事件處理常式。

另一種方法來取得存取原生檢視程式碼後置檔案中包括列舉配置物件的子系及搜尋不同的型別或識別碼。三個平台定義 Tag 屬性 — 在 iOS 和 Android 等 UWP 物件上的整數,可讓您針對此目的。

我發現 XamlNativeViewDemo 程式無法令人滿意因為我不喜歡使用 SAP 我 Xamarin.Forms 應用程式。我不知道您是否我有關偏好 PCL 到 SAP,但是如果您是,您會很高興知道最後兩個程式在這篇文章是純粹的 PCL 的熱愛。

資料繫結與 MVVM

若要避免在程式碼後置檔案中的程式碼的最佳方式是 Model View ViewModel 架構 (MVVM) 將應用程式結構。平台獨立 ViewModel 發生在頁面上檢視之間的所有互動。ViewModel 會連接到使用 Xamarin.Forms 資料繫結的檢視 (UI)。資料繫結來源是 ViewModel 的屬性,而資料繫結目標是檢視的屬性。

但是等一下。我之前有提到,您無法使用樣式的原生檢視因為樣式的屬性必須由可繫結屬性。資料繫結有相同的限制︰ 資料繫結的目標屬性 — 和 MVVM 這些目標一定會與檢視頁面上的,必須也屬性由備份 BindableProperty 物件。那麼要如何設定繫結上原生的檢視?

以下是好消息︰ 若要支援資料繫結 XAML 的原生檢視上,每個平台包含 SetBinding 自動產生即時的臨機操作 BindableProperty 物件的擴充方法。這些 BindableProperty 物件允許從 ViewModel 變更的原生屬性值。

現在您可能會考慮採用另一個問題︰ 在許多情況下這些資料繫結需要這兩種方式 — 不只是從來源到目標,但從目標到來源。UI 的檢視中的變更必須反映在 ViewModel 屬性。Xamarin.Forms BindableProperty 基礎結構可支援透過 INotifyPropertyChanged 介面、 屬性變更的通知,但原生的檢視不支援這個介面,那麼要如何繫結物件知道當原生檢視的屬性變更值?

繫結類別現在有新的屬性︰ UpdateSourceEventName。在 XAML 中繫結標記延伸您可以將這個屬性設定原生通知的目標屬性已變更的檢視中的事件名稱。

中所示的 PlatformRgbSliders 程式 [圖 5 是一個簡單範例。(某些重複標記已取代含有省略符號)。 XAML 檔案中包含三個平台專屬滑桿,我已增強讓程式在對應的色彩組。(它不可能對 Android 分別執行這個動作。) 設為頁面的 Istreamupgradeproviderelement 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 相依性屬性的通知機制。此外,工作執行允許終結 iOS 檢視上,如果檢視實作索引鍵-值觀察 UpdateSourceEventName。

PlatformRgbSliders 會有任何特定問題的程式碼在程式碼後置檔案中,因此使用 PCL 沒有問題。但是,無可否認地,PlatformRgbSliders 很簡單。將您能夠在大型程式中使用 PCLs 嗎?

一開始看起來不可行︰ 許多 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 下拉式方塊的三個平台上執行開啟以顯示此選項。

圖 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 寫過許多文章的 MSDN Magazine 和它的前身 Microsoft Systems Journal,過去 30 年。 他現在 Microsoft Xamarin 文件小組的運作方式,並且是 「 建立行動應用程式使用 Xamarin.Forms,「 可從 Xamarin 網站下載免費的書籍的作者。

感謝下列 Microsoft 技術專家來檢閱這份文件︰ David Britch、 Stephane Delcroix 和 Rui Marinho
David Britch 適用於 Microsoft Xamarin 文件小組。他曾經撰寫軟體開發發行集,包括書籍、 指引文件、 參考實作、 白皮書、 影片和講師訓練課程的範圍。

Stephane Delcroix 適用於在 XAML 和其他 Microsoft 的 Xamarin.Forms 小組。

Rui Marinho 是使用 Xamarin.Forms 在 Microsoft 的小組,葡萄牙文的軟體工程師和是一個 avid 編碼器和開放原始碼參與者。