Native Ansichten in XAML

Beispiel herunterladen Das Beispiel herunterladen

Native Ansichten von iOS, Android und der Universelle Windows-Plattform können direkt aus Xamarin.Forms XAML-Dateien referenziert werden. Eigenschaften und Ereignishandler können für native Ansichten festgelegt werden, und sie können mit Xamarin.Forms Ansichten interagieren. In diesem Artikel wird veranschaulicht, wie Sie native Ansichten aus Xamarin.Forms XAML-Dateien nutzen.

So betten Sie eine native Ansicht in eine XAML-Datei ein Xamarin.Forms :

  1. Fügen Sie eine xmlns Namespacedeklaration in der XAML-Datei für den Namespace hinzu, der die native Ansicht enthält.
  2. Erstellen Sie eine instance der nativen Ansicht in der XAML-Datei.

Wichtig

Kompilierte XAML muss für alle XAML-Seiten deaktiviert werden, die native Ansichten verwenden. Dies kann erreicht werden, indem Sie die Code-Behind-Klasse für Ihre XAML-Seite mit dem [XamlCompilation(XamlCompilationOptions.Skip)] -Attribut versehen. Weitere Informationen zur XAML-Kompilierung finden Sie unter XAML-Kompilierung in Xamarin.Forms.

Um aus einer CodeBehind-Datei auf eine native Ansicht zu verweisen, müssen Sie ein Shared Asset Project (SAP) verwenden und den plattformspezifischen Code mit Bedingten Kompilierungsdirektiven umschließen. Weitere Informationen finden Sie unter Verweisen auf native Ansichten aus Code.

Nutzen von nativen Ansichten

Im folgenden Codebeispiel wird die Verwendung nativer Ansichten für jede Plattform in ein Xamarin.FormsContentPageveranschaulicht:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
        xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
        xmlns:ios="clr-namespace:UIKit;assembly=Xamarin.iOS;targetPlatform=iOS"
        xmlns:androidWidget="clr-namespace:Android.Widget;assembly=Mono.Android;targetPlatform=Android"
        xmlns:androidLocal="clr-namespace:SimpleColorPicker.Droid;assembly=SimpleColorPicker.Droid;targetPlatform=Android"
        xmlns:win="clr-namespace:Windows.UI.Xaml.Controls;assembly=Windows, Version=255.255.255.255,
            Culture=neutral, PublicKeyToken=null, ContentType=WindowsRuntime;targetPlatform=Windows"
        x:Class="NativeViews.NativeViewDemo">
    <StackLayout Margin="20">
        <ios:UILabel Text="Hello World" TextColor="{x:Static ios:UIColor.Red}" View.HorizontalOptions="Start" />
        <androidWidget:TextView Text="Hello World" x:Arguments="{x:Static androidLocal:MainActivity.Instance}" />
        <win:TextBlock Text="Hello World" />
    </StackLayout>
</ContentPage>

Neben dem Angeben von clr-namespace und assembly für einen nativen Ansichtsnamespace muss auch ein targetPlatform angegeben werden. Dies sollte auf iOS, , AndroidUWP, ( Windows entspricht UWP), macOS, , GTKTizenoder WPFfestgelegt werden. Zur Laufzeit ignoriert der XAML-Parser alle XML-Namespacepräfixe, die über einen targetPlatform verfügen, der nicht mit der Plattform übereinstimmt, auf der die Anwendung ausgeführt wird.

Jede Namespacedeklaration kann verwendet werden, um auf eine beliebige Klasse oder Struktur aus dem angegebenen Namespace zu verweisen. Beispielsweise kann die ios Namespacedeklaration verwendet werden, um auf jede Klasse oder Struktur aus dem iOS-Namespace UIKit zu verweisen. Eigenschaften der nativen Ansicht können über XAML festgelegt werden, aber die Eigenschaften- und Objekttypen müssen übereinstimmen. Die -Eigenschaft wird beispielsweise UILabel.TextColor mit UIColor.Red der x:Static Markuperweiterung und dem ios Namespace auf festgelegt.

Bindungsfähige Eigenschaften und angefügte bindungsfähige Eigenschaften können auch für native Ansichten mithilfe der Class.BindableProperty="value" Syntax festgelegt werden. Jede native Ansicht wird in eine plattformspezifische NativeViewWrapper instance umschlossen, die von der Xamarin.Forms.View -Klasse abgeleitet ist. Durch Festlegen einer bindbaren Eigenschaft oder einer angefügten bindungsfähigen Eigenschaft in einer nativen Ansicht wird der Eigenschaftswert an den Wrapper übertragen. Beispielsweise kann ein zentriertes horizontales Layout durch Festlegen View.HorizontalOptions="Center" der nativen Ansicht angegeben werden.

Hinweis

Beachten Sie, dass Formatvorlagen nicht mit nativen Ansichten verwendet werden können, da Formatvorlagen nur auf Eigenschaften abzielen können, die von BindableProperty Objekten unterstützt werden.

Android-Widgetkonstruktoren erfordern in der Regel das Android-Objekt Context als Argument, und dies kann über eine statische Eigenschaft in der MainActivity -Klasse verfügbar gemacht werden. Daher muss das Objekt beim Erstellen eines Android-Widgets in XAML in der Context Regel an den Konstruktor des Widgets übergeben werden, indem das x:Arguments Attribut mit einer x:Static Markuperweiterung verwendet wird. Weitere Informationen finden Sie unter Übergeben von Argumenten an native Ansichten.

Hinweis

Beachten Sie, dass das Benennen einer nativen Ansicht mit x:Name weder in einem .NET Standard-Bibliotheksprojekt noch in einem Shared Asset Project (SAP) möglich ist. Dadurch wird eine Variable des systemeigenen Typs generiert, die einen Kompilierungsfehler verursacht. Native Ansichten können jedoch in ContentView Instanzen umschlossen und in der CodeBehind-Datei abgerufen werden, sofern ein SAP verwendet wird. Weitere Informationen finden Sie unter Verweisen auf native Ansicht aus Code.

Native Bindungen

Die Datenbindung wird verwendet, um eine Benutzeroberfläche mit ihrer Datenquelle zu synchronisieren, und vereinfacht die Anzeige und Interaktion einer Xamarin.Forms Anwendung mit ihren Daten. Sofern das Quellobjekt die INotifyPropertyChanged Schnittstelle implementiert, werden Änderungen im Quellobjekt vom Bindungsframework automatisch an das Zielobjekt gepusht, und Änderungen im Zielobjekt können optional an das Quellobjekt gepusht werden.

Eigenschaften nativer Ansichten können auch Die Datenbindung verwenden. Im folgenden Codebeispiel wird die Datenbindung mithilfe von Eigenschaften nativer Ansichten veranschaulicht:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
        xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
        xmlns:ios="clr-namespace:UIKit;assembly=Xamarin.iOS;targetPlatform=iOS"
        xmlns:androidWidget="clr-namespace:Android.Widget;assembly=Mono.Android;targetPlatform=Android"
        xmlns:androidLocal="clr-namespace:SimpleColorPicker.Droid;assembly=SimpleColorPicker.Droid;targetPlatform=Android"
        xmlns:win="clr-namespace:Windows.UI.Xaml.Controls;assembly=Windows, Version=255.255.255.255,
            Culture=neutral, PublicKeyToken=null, ContentType=WindowsRuntime;targetPlatform=Windows"
        xmlns:local="clr-namespace:NativeSwitch"
        x:Class="NativeSwitch.NativeSwitchPage">
    <StackLayout Margin="20">
        <Label Text="Native Views Demo" FontAttributes="Bold" HorizontalOptions="Center" />
        <Entry Placeholder="This Entry is bound to the native switch" IsEnabled="{Binding IsSwitchOn}" />
        <ios:UISwitch On="{Binding Path=IsSwitchOn, Mode=TwoWay, UpdateSourceEventName=ValueChanged}"
            OnTintColor="{x:Static ios:UIColor.Red}"
            ThumbTintColor="{x:Static ios:UIColor.Blue}" />
        <androidWidget:Switch x:Arguments="{x:Static androidLocal:MainActivity.Instance}"
            Checked="{Binding Path=IsSwitchOn, Mode=TwoWay, UpdateSourceEventName=CheckedChange}"
            Text="Enable Entry?" />
        <win:ToggleSwitch Header="Enable Entry?"
            OffContent="No"
            OnContent="Yes"
            IsOn="{Binding IsSwitchOn, Mode=TwoWay, UpdateSourceEventName=Toggled}" />
    </StackLayout>
</ContentPage>

Die Seite enthält eine Entry , deren IsEnabled Eigenschaft an die NativeSwitchPageViewModel.IsSwitchOn -Eigenschaft gebunden ist. Die BindingContext der Seite ist auf einen neuen instance der NativeSwitchPageViewModel Klasse in der CodeBehind-Datei festgelegt, wobei die ViewModel-Klasse die INotifyPropertyChanged Schnittstelle implementiert.

Die Seite enthält auch einen nativen Switch für jede Plattform. Jeder native Switch verwendet eine TwoWay Bindung, um den Wert der NativeSwitchPageViewModel.IsSwitchOn Eigenschaft zu aktualisieren. Daher ist der Schalter deaktiviert, Entry und wenn der Schalter eingeschaltet ist, ist der Entry aktiviert. Die folgenden Screenshots zeigen diese Funktionalität auf jeder Plattform:

Native SwitchDeaktivierter nativer Switch aktiviert

Bidirektionale Bindungen werden automatisch unterstützt, sofern die native Eigenschaft Key-Value Observing (KVO) unter iOS implementiert INotifyPropertyChangedoder unterstützt.DependencyProperty Viele native Ansichten unterstützen jedoch keine Benachrichtigung über Eigenschaftenänderungen. Für diese Ansichten können Sie einen UpdateSourceEventName Eigenschaftswert als Teil des Bindungsausdrucks angeben. Diese Eigenschaft sollte auf den Namen eines Ereignisses in der nativen Ansicht festgelegt werden, das angibt, wann sich die Zieleigenschaft geändert hat. Wenn sich der Wert des nativen Switches ändert, wird die Binding Klasse benachrichtigt, dass der Benutzer den Switchwert geändert hat, und der NativeSwitchPageViewModel.IsSwitchOn Eigenschaftswert wird aktualisiert.

Übergeben von Argumenten an native Ansichten

Konstruktorargumente können mithilfe des x:Arguments Attributs mit einer x:Static Markuperweiterung an systemeigene Ansichten übergeben werden. Darüber hinaus können Native View Factory-Methoden (public static Methoden, die Objekte oder Werte desselben Typs zurückgeben wie die Klasse oder Struktur, die die Methoden definiert) aufgerufen werden, indem der Name der Methode mithilfe des x:FactoryMethod -Attributs und deren Argumente mithilfe des x:Arguments -Attributs angegeben wird.

Im folgenden Codebeispiel werden beide Techniken veranschaulicht:

<ContentPage ...
        xmlns:ios="clr-namespace:UIKit;assembly=Xamarin.iOS;targetPlatform=iOS"
        xmlns:androidWidget="clr-namespace:Android.Widget;assembly=Mono.Android;targetPlatform=Android"
        xmlns:androidGraphics="clr-namespace:Android.Graphics;assembly=Mono.Android;targetPlatform=Android"
        xmlns:androidLocal="clr-namespace:SimpleColorPicker.Droid;assembly=SimpleColorPicker.Droid;targetPlatform=Android"
        xmlns:winControls="clr-namespace:Windows.UI.Xaml.Controls;assembly=Windows, Version=255.255.255.255, Culture=neutral, PublicKeyToken=null, ContentType=WindowsRuntime;targetPlatform=Windows"
        xmlns:winMedia="clr-namespace:Windows.UI.Xaml.Media;assembly=Windows, Version=255.255.255.255, Culture=neutral, PublicKeyToken=null, ContentType=WindowsRuntime;targetPlatform=Windows"
        xmlns:winText="clr-namespace:Windows.UI.Text;assembly=Windows, Version=255.255.255.255, Culture=neutral, PublicKeyToken=null, ContentType=WindowsRuntime;targetPlatform=Windows"
        xmlns:winui="clr-namespace:Windows.UI;assembly=Windows, Version=255.255.255.255, Culture=neutral, PublicKeyToken=null, ContentType=WindowsRuntime;targetPlatform=Windows">
        ...
        <ios:UILabel Text="Simple Native Color Picker" View.HorizontalOptions="Center">
            <ios:UILabel.Font>
                <ios:UIFont x:FactoryMethod="FromName">
                    <x:Arguments>
                        <x:String>Papyrus</x:String>
                        <x:Single>24</x:Single>
                    </x:Arguments>
                </ios:UIFont>
            </ios:UILabel.Font>
        </ios:UILabel>
        <androidWidget:TextView x:Arguments="{x:Static androidLocal:MainActivity.Instance}"
                    Text="Simple Native Color Picker"
                    TextSize="24"
                    View.HorizontalOptions="Center">
            <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="Simple Native Color Picker"
                    FontSize="20"
                    FontStyle="{x:Static winText:FontStyle.Italic}"
                    View.HorizontalOptions="Center">
            <winControls:TextBlock.FontFamily>
                <winMedia:FontFamily>
                    <x:Arguments>
                        <x:String>Georgia</x:String>
                    </x:Arguments>
                </winMedia:FontFamily>
            </winControls:TextBlock.FontFamily>
        </winControls:TextBlock>
        ...
</ContentPage>

Die UIFont.FromName factory-Methode wird verwendet, um die UILabel.Font -Eigenschaft unter iOS auf eine neue UIFont festzulegen. Name UIFont und Größe werden durch die Methodenargumente angegeben, die untergeordnete Elemente des x:Arguments Attributs sind.

Die Typeface.Create Factory-Methode wird verwendet, um die TextView.Typeface Eigenschaft auf eine neue Typeface unter Android festzulegen. Der Typeface Familienname und der Stil werden durch die Methodenargumente angegeben, die untergeordnete Elemente des x:Arguments Attributs sind.

Der FontFamily Konstruktor wird verwendet, um die TextBlock.FontFamily Eigenschaft auf eine neue FontFamily auf dem Universelle Windows-Plattform (UWP) festzulegen. Der FontFamily Name wird durch das Methodenargument angegeben, das ein untergeordnetes Element des x:Arguments Attributs ist.

Hinweis

Argumente müssen mit den Typen übereinstimmen, die für den Konstruktor oder die Factorymethode erforderlich sind.

Die folgenden Screenshots zeigen das Ergebnis der Angabe von Factorymethoden- und Konstruktorargumenten, um die Schriftart für verschiedene native Ansichten festzulegen:

Festlegen von Schriftarten für native Ansichten

Weitere Informationen zum Übergeben von Argumenten in XAML finden Sie unter Übergeben von Argumenten in XAML.

Verweisen Sie im Code auf native Ansichten.

Obwohl es nicht möglich ist, eine native Ansicht mit dem x:Name Attribut zu benennen, ist es möglich, eine native Ansicht abzurufen, instance die in einer XAML-Datei aus der CodeBehind-Datei in einem Shared Access-Projekt deklariert wurde, vorausgesetzt, die native Ansicht ist ein untergeordnetes Element einesContentView, das einen x:Name Attributwert angibt. Anschließend sollten Sie innerhalb von Anweisungen zur bedingten Kompilierung in der CodeBehind-Datei Folgendes ausführen:

  1. Rufen Sie den ContentView.Content Eigenschaftswert ab, und wandeln Sie ihn in einen plattformspezifischen NativeViewWrapper Typ um.
  2. Rufen Sie die NativeViewWrapper.NativeElement Eigenschaft ab, und wandeln Sie sie in den nativen Ansichtstyp um.

Die native API kann dann in der nativen Ansicht aufgerufen werden, um die gewünschten Vorgänge auszuführen. Dieser Ansatz bietet auch den Vorteil, dass mehrere native XAML-Ansichten für verschiedene Plattformen untergeordnete Elemente derselben ContentViewsein können. Im folgenden Codebeispiel wird dieses Verfahren veranschaulicht:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
        xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
        xmlns:ios="clr-namespace:UIKit;assembly=Xamarin.iOS;targetPlatform=iOS"
        xmlns:androidWidget="clr-namespace:Android.Widget;assembly=Mono.Android;targetPlatform=Android"
        xmlns:androidLocal="clr-namespace:SimpleColorPicker.Droid;assembly=SimpleColorPicker.Droid;targetPlatform=Android"
        xmlns:winControls="clr-namespace:Windows.UI.Xaml.Controls;assembly=Windows, Version=255.255.255.255,
            Culture=neutral, PublicKeyToken=null, ContentType=WindowsRuntime;targetPlatform=Windows"
        xmlns:local="clr-namespace:NativeViewInsideContentView"
        x:Class="NativeViewInsideContentView.NativeViewInsideContentViewPage">
    <StackLayout Margin="20">
        <ContentView x:Name="contentViewTextParent" HorizontalOptions="Center" VerticalOptions="CenterAndExpand">
            <ios:UILabel Text="Text in a UILabel" TextColor="{x:Static ios:UIColor.Red}" />
            <androidWidget:TextView x:Arguments="{x:Static androidLocal:MainActivity.Instance}"
                Text="Text in a TextView" />
              <winControls:TextBlock Text="Text in a TextBlock" />
        </ContentView>
        <ContentView x:Name="contentViewButtonParent" HorizontalOptions="Center" VerticalOptions="EndAndExpand">
            <ios:UIButton TouchUpInside="OnButtonTap" View.HorizontalOptions="Center" View.VerticalOptions="Center" />
            <androidWidget:Button x:Arguments="{x:Static androidLocal:MainActivity.Instance}"
                Text="Scale and Rotate Text"
                Click="OnButtonTap" />
            <winControls:Button Content="Scale and Rotate Text" />
        </ContentView>
    </StackLayout>
</ContentPage>

Im obigen Beispiel sind die nativen Ansichten für jede Plattform untergeordnete ContentView Steuerelemente, wobei der x:Name Attributwert verwendet wird, um im ContentView CodeBehind abzurufen:

public partial class NativeViewInsideContentViewPage : ContentPage
{
    public NativeViewInsideContentViewPage()
    {
        InitializeComponent();

#if __IOS__
        var wrapper = (Xamarin.Forms.Platform.iOS.NativeViewWrapper)contentViewButtonParent.Content;
        var button = (UIKit.UIButton)wrapper.NativeView;
        button.SetTitle("Scale and Rotate Text", UIKit.UIControlState.Normal);
        button.SetTitleColor(UIKit.UIColor.Black, UIKit.UIControlState.Normal);
#endif
#if __ANDROID__
        var wrapper = (Xamarin.Forms.Platform.Android.NativeViewWrapper)contentViewTextParent.Content;
        var textView = (Android.Widget.TextView)wrapper.NativeView;
        textView.SetTextColor(Android.Graphics.Color.Red);
#endif
#if WINDOWS_UWP
        var textWrapper = (Xamarin.Forms.Platform.UWP.NativeViewWrapper)contentViewTextParent.Content;
        var textBlock = (Windows.UI.Xaml.Controls.TextBlock)textWrapper.NativeElement;
        textBlock.Foreground = new Windows.UI.Xaml.Media.SolidColorBrush(Windows.UI.Colors.Red);
        var buttonWrapper = (Xamarin.Forms.Platform.UWP.NativeViewWrapper)contentViewButtonParent.Content;
        var button = (Windows.UI.Xaml.Controls.Button)buttonWrapper.NativeElement;
        button.Click += (sender, args) => OnButtonTap(sender, EventArgs.Empty);
#endif
    }

    async void OnButtonTap(object sender, EventArgs e)
    {
        contentViewButtonParent.Content.IsEnabled = false;
        contentViewTextParent.Content.ScaleTo(2, 2000);
        await contentViewTextParent.Content.RotateTo(360, 2000);
        contentViewTextParent.Content.ScaleTo(1, 2000);
        await contentViewTextParent.Content.RelRotateTo(360, 2000);
        contentViewButtonParent.Content.IsEnabled = true;
    }
}

Auf ContentView.Content die Eigenschaft wird zugegriffen, um die umschlossene native Ansicht als plattformspezifische NativeViewWrapper instance abzurufen. Anschließend NativeViewWrapper.NativeElement wird auf die -Eigenschaft zugegriffen, um die native Ansicht als nativen Typ abzurufen. Anschließend wird die API der nativen Ansicht aufgerufen, um die gewünschten Vorgänge auszuführen.

Die nativen iOS- und Android-Schaltflächen verwenden denselben OnButtonTap Ereignishandler, da jede native Schaltfläche einen EventHandler Delegaten als Reaktion auf ein Touchereignis nutzt. Die Universelle Windows-Plattform (UWP) verwendet jedoch eine separate RoutedEventHandler, die wiederum den OnButtonTap Ereignishandler in diesem Beispiel nutzt. Wenn auf eine native Schaltfläche geklickt wird, wird daher der OnButtonTap Ereignishandler ausgeführt, der das systemeigene Steuerelement skaliert und rotiert, das in dem ContentView mit dem Namen contentViewTextParententhalten ist. Die folgenden Screenshots veranschaulichen dies auf jeder Plattform:

ContentView mit einem nativen Steuerelement

Native Ansichten der Unterklasse

Viele native iOS- und Android-Ansichten eignen sich nicht für die Instanziierung in XAML, da sie zum Einrichten des Steuerelements Methoden anstelle von Eigenschaften verwenden. Die Lösung für dieses Problem besteht darin, native Ansichten in Wrappern unterzuklassieren, die eine XAML-freundlichere API definieren, die Eigenschaften zum Einrichten des Steuerelements verwendet und plattformunabhängige Ereignisse verwendet. Die umschlossenen nativen Ansichten können dann in einem Shared Asset Project (SAP) platziert und mit bedingten Kompilierungsdirektiven umgeben oder in plattformspezifischen Projekten platziert und von XAML in einem .NET Standard-Bibliotheksprojekt referenziert werden.

Im folgenden Codebeispiel wird eine Xamarin.Forms Seite veranschaulicht, die native Unterklassenansichten verwendet:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
        xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
        xmlns:ios="clr-namespace:UIKit;assembly=Xamarin.iOS;targetPlatform=iOS"
        xmlns:iosLocal="clr-namespace:SubclassedNativeControls.iOS;assembly=SubclassedNativeControls.iOS;targetPlatform=iOS"
        xmlns:android="clr-namespace:Android.Widget;assembly=Mono.Android;targetPlatform=Android"
        xmlns:androidLocal="clr-namespace:SimpleColorPicker.Droid;assembly=SimpleColorPicker.Droid;targetPlatform=Android"
        xmlns:androidLocal="clr-namespace:SubclassedNativeControls.Droid;assembly=SubclassedNativeControls.Droid;targetPlatform=Android"
        xmlns:winControls="clr-namespace:Windows.UI.Xaml.Controls;assembly=Windows, Version=255.255.255.255,
            Culture=neutral, PublicKeyToken=null, ContentType=WindowsRuntime;targetPlatform=Windows"
        xmlns:local="clr-namespace:SubclassedNativeControls"
        x:Class="SubclassedNativeControls.SubclassedNativeControlsPage">
    <StackLayout Margin="20">
        <Label Text="Subclassed Native Views Demo" FontAttributes="Bold" HorizontalOptions="Center" />
        <StackLayout Orientation="Horizontal">
          <Label Text="You have chosen:" />
          <Label Text="{Binding SelectedFruit}" />      
        </StackLayout>
        <iosLocal:MyUIPickerView ItemsSource="{Binding Fruits}"
            SelectedItem="{Binding SelectedFruit, Mode=TwoWay, UpdateSourceEventName=SelectedItemChanged}" />
        <androidLocal:MySpinner x:Arguments="{x:Static androidLocal:MainActivity.Instance}"
            ItemsSource="{Binding Fruits}"
            SelectedObject="{Binding SelectedFruit, Mode=TwoWay, UpdateSourceEventName=ItemSelected}" />
        <winControls:ComboBox ItemsSource="{Binding Fruits}"
            SelectedItem="{Binding SelectedFruit, Mode=TwoWay, UpdateSourceEventName=SelectionChanged}" />
    </StackLayout>
</ContentPage>

Die Seite enthält eine Label , die die vom Benutzer aus einem nativen Steuerelement ausgewählte Frucht anzeigt. Der Label bindet an die SubclassedNativeControlsPageViewModel.SelectedFruit -Eigenschaft. Die BindingContext der Seite ist auf einen neuen instance der SubclassedNativeControlsPageViewModel Klasse in der CodeBehind-Datei festgelegt, wobei die ViewModel-Klasse die INotifyPropertyChanged Schnittstelle implementiert.

Die Seite enthält auch eine native Auswahlansicht für jede Plattform. Jede native Ansicht zeigt die Sammlung von Früchten an, indem sie ihre ItemSource Eigenschaft an die SubclassedNativeControlsPageViewModel.Fruits Auflistung bindet. Dadurch kann der Benutzer eine Frucht auswählen, wie in den folgenden Screenshots gezeigt:

Unterklassige native Ansichten

Unter iOS und Android verwenden die nativen Auswahlmethoden, um die Steuerelemente einzurichten. Daher müssen diese Auswahlen unterklassigt werden, um Eigenschaften verfügbar zu machen, damit sie XAML-freundlich sind. Auf der Universelle Windows-Plattform (UWP) ist bereits ComboBox XAML-freundlich, daher ist keine Unterklasse erforderlich.

iOS

Die iOS-Implementierung unterklassiert die UIPickerView Ansicht und macht Eigenschaften und ein Ereignis verfügbar, die problemlos von XAML genutzt werden können:

public class MyUIPickerView : UIPickerView
{
    public event EventHandler<EventArgs> SelectedItemChanged;

    public MyUIPickerView()
    {
        var model = new PickerModel();
        model.ItemChanged += (sender, e) =>
        {
            if (SelectedItemChanged != null)
            {
                SelectedItemChanged.Invoke(this, e);
            }
        };
        Model = model;
    }

    public IList<string> ItemsSource
    {
        get
        {
            var pickerModel = Model as PickerModel;
            return (pickerModel != null) ? pickerModel.Items : null;
        }
        set
        {
            var model = Model as PickerModel;
            if (model != null)
            {
                model.Items = value;
            }
        }
    }

    public string SelectedItem
    {
        get { return (Model as PickerModel).SelectedItem; }
        set { }
    }
}

Die MyUIPickerView -Klasse macht eigenschaften und SelectedItem ein SelectedItemChanged -Ereignis verfügbarItemsSource. Ein UIPickerView erfordert ein zugrunde liegendes UIPickerViewModel Datenmodell, auf das über die Eigenschaften und das MyUIPickerView Ereignis zugegriffen wird. Das UIPickerViewModel Datenmodell wird von der PickerModel -Klasse bereitgestellt:

class PickerModel : UIPickerViewModel
{
    int selectedIndex = 0;
    public event EventHandler<EventArgs> ItemChanged;
    public IList<string> Items { get; set; }

    public string SelectedItem
    {
        get
        {
            return Items != null && selectedIndex >= 0 && selectedIndex < Items.Count ? Items[selectedIndex] : null;
        }
    }

    public override nint GetRowsInComponent(UIPickerView pickerView, nint component)
    {
        return Items != null ? Items.Count : 0;
    }

    public override string GetTitle(UIPickerView pickerView, nint row, nint component)
    {
        return Items != null && Items.Count > row ? Items[(int)row] : null;
    }

    public override nint GetComponentCount(UIPickerView pickerView)
    {
        return 1;
    }

    public override void Selected(UIPickerView pickerView, nint row, nint component)
    {
        selectedIndex = (int)row;
        if (ItemChanged != null)
        {
            ItemChanged.Invoke(this, new EventArgs());
        }
    }
}

Die PickerModel -Klasse stellt den zugrunde liegenden Speicher für die MyUIPickerView -Klasse über die -Eigenschaft bereit Items . Wenn sich das ausgewählte Element in den MyUIPickerView Änderungen ändert, wird die Selected -Methode ausgeführt, die den ausgewählten Index aktualisiert und das ItemChanged Ereignis auslöst. Dadurch wird sichergestellt, dass die SelectedItem -Eigenschaft immer das letzte vom Benutzer ausgewählte Element zurückgibt. Darüber hinaus überschreibt die PickerModel -Klasse Methoden, die zum Einrichten der MyUIPickerView instance verwendet werden.

Android

Die Android-Implementierung unterklassiert die Spinner Ansicht und macht Eigenschaften und ein Ereignis verfügbar, die problemlos von XAML genutzt werden können:

class MySpinner : Spinner
{
    ArrayAdapter adapter;
    IList<string> items;

    public IList<string> ItemsSource
    {
        get { return items; }
        set
        {
            if (items != value)
            {
                items = value;
                adapter.Clear();

                foreach (string str in items)
                {
                    adapter.Add(str);
                }
            }
        }
    }

    public string SelectedObject
    {
        get { return (string)GetItemAtPosition(SelectedItemPosition); }
        set
        {
            if (items != null)
            {
                int index = items.IndexOf(value);
                if (index != -1)
                {
                    SetSelection(index);
                }
            }
        }
    }

    public MySpinner(Context context) : base(context)
    {
        ItemSelected += OnBindableSpinnerItemSelected;

        adapter = new ArrayAdapter(context, Android.Resource.Layout.SimpleSpinnerItem);
        adapter.SetDropDownViewResource(Android.Resource.Layout.SimpleSpinnerDropDownItem);
        Adapter = adapter;
    }

    void OnBindableSpinnerItemSelected(object sender, ItemSelectedEventArgs args)
    {
        SelectedObject = (string)GetItemAtPosition(args.Position);
    }
}

Die MySpinner -Klasse macht eigenschaften und SelectedObject ein ItemSelected -Ereignis verfügbarItemsSource. Die von der MySpinner -Klasse angezeigten Elemente werden von der bereitgestellt, die Adapter der Ansicht zugeordnet ist, und Elemente werden in die Adapter aufgefüllt, wenn die ItemsSource Eigenschaft zum ersten Mal festgelegt wird. Wenn sich das ausgewählte Element in der MySpinner -Klasse ändert, aktualisiert der OnBindableSpinnerItemSelected Ereignishandler die SelectedObject -Eigenschaft.