Native Ansichten in XAML

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

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

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

Wichtig

Kompilierter XAML-Code muss für alle XAML-Seiten deaktiviert sein, die systemeigene Ansichten verwenden. Dies kann erreicht werden, indem sie die CodeBehind-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 systemeigene 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 systemeigene Ansichten aus Code.

Nutzen nativer Ansichten

Im folgenden Codebeispiel wird veranschaulicht, wie systemeigene Ansichten für jede Plattform verwendet werden:Xamarin.FormsContentPage

<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 der Angabe des clr-namespace und assembly für einen nativen Ansichtsnamespace muss auch ein targetPlatform Wert angegeben werden. Dies sollte auf iOS, , Android, UWP, Windows (das entspricht UWP), macOS, , , GTK, Tizenoder WPF. Zur Laufzeit ignoriert der XAML-Parser alle XML-Namespacepräfixe, targetPlatform die nicht mit der Plattform übereinstimmen, 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 eine beliebige Klasse oder Struktur aus dem iOS-Namespace UIKit zu verweisen. Eigenschaften der nativen Ansicht können über XAML festgelegt werden, die Eigenschaften und Objekttypen müssen jedoch übereinstimmen. Beispielsweise wird die UILabel.TextColor Eigenschaft auf UIColor.Red die Verwendung der x:Static Markuperweiterung und des ios Namespaces festgelegt.

Bindende Eigenschaften und angefügte bindungsfähige Eigenschaften können auch für systemeigene Ansichten mithilfe der Class.BindableProperty="value" Syntax festgelegt werden. Jede systemeigene Ansicht wird in eine plattformspezifische NativeViewWrapper Instanz umschlossen, die von der Xamarin.Forms.View Klasse abgeleitet wird. Durch Festlegen einer bindbaren Eigenschaft oder einer angefügten bindbaren Eigenschaft für eine systemeigene 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 im Allgemeinen das Android-Objekt Context als Argument, und dies kann über eine statische Eigenschaft in der MainActivity Klasse verfügbar gemacht werden. Daher muss das Context Objekt beim Erstellen eines Android-Widgets in XAML im Allgemeinen mithilfe des x:Arguments Attributs mit einer x:Static Markuperweiterung an den Konstruktor des Widgets übergeben werden. Weitere Informationen finden Sie unter Übergeben von Argumenten an systemeigene Ansichten.

Hinweis

Beachten Sie, dass das Benennen einer nativen Ansicht in x:Name einem .NET Standard-Bibliotheksprojekt oder einem Shared Asset Project (SAP) nicht möglich ist. Dadurch wird eine Variable des systemeigenen Typs generiert, was zu einem Kompilierungsfehler führt. Systemeigene 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 die systemeigene Ansicht aus Code.

Systemeigene 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 automatisch vom Bindungsframework an das Zielobjekt übertragen, und Änderungen am Zielobjekt können optional an das Quellobjekt übertragen werden.

Eigenschaften systemeigener Ansichten können auch datenbindung verwenden. Im folgenden Codebeispiel wird die Datenbindung mithilfe von Eigenschaften systemeigener 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 Eigenschaft, deren IsEnabled Eigenschaft an die NativeSwitchPageViewModel.IsSwitchOn Eigenschaft gebunden wird. Die BindingContext Seite wird auf eine neue Instanz 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 systemeigene Switch verwendet eine TwoWay Bindung, um den Wert der NativeSwitchPageViewModel.IsSwitchOn Eigenschaft zu aktualisieren. Wenn der Schalter ausgeschaltet ist, ist die Entry Option deaktiviert und wenn der Schalter eingeschaltet ist, ist die Entry Option aktiviert. Die folgenden Screenshots zeigen diese Funktionalität auf jeder Plattform:

Systemeigener Switch deaktiviertSystemeigener Switch aktiviert

Bidirektionale Bindungen werden automatisch unterstützt, vorausgesetzt, dass die systemeigene Eigenschaft implementiert INotifyPropertyChangedoder Key-Value Observing (KVO) unter iOS unterstützt oder eine DependencyProperty UWP ist. Viele native Ansichten unterstützen jedoch keine Eigenschaftsänderungsbenachrichtigung. 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 signalisiert, wenn sich die Zieleigenschaft geändert hat. Wenn sich der Wert des systemeigenen Switches ändert, wird die Binding Klasse benachrichtigt, dass der Benutzer den Switchwert geändert hat und der NativeSwitchPageViewModel.IsSwitchOn Eigenschaftswert aktualisiert wird.

Übergeben von Argumenten an systemeigene Ansichten

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

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 Factorymethode wird verwendet, um die UILabel.Font Eigenschaft auf ein neues UIFont unter iOS festzulegen. Der UIFont Name und die Größe werden durch die Methodenargumente angegeben, die untergeordnete Elemente des x:Arguments Attributs sind.

Die Typeface.Create Factorymethode wird verwendet, um die TextView.Typeface Eigenschaft auf ein neues Typeface unter Android festzulegen. Der Familienname und die Typeface Formatvorlage 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 in der 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 vom Konstruktor oder der Factorymethode benötigt werden.

Die folgenden Screenshots zeigen das Ergebnis der Angabe von Factorymethoden- und Konstruktorargumenten zum Festlegen der Schriftart in verschiedenen nativen Ansichten:

Festlegen von Schriftarten in nativen Ansichten

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

Verweisen auf systemeigene Ansichten aus Code

Obwohl es nicht möglich ist, eine systemeigene Ansicht mit dem x:Name Attribut zu benennen, ist es möglich, eine systemeigene Ansichtsinstanz abzurufen, die in einer XAML-Datei in ihrer CodeBehind-Datei in einem Shared Access-Projekt deklariert ist, vorausgesetzt, die native Ansicht ist ein untergeordnetes Element eines ContentView x:Name Attributwerts. Führen Sie dann in den CodeBehind-Direktiven in der CodeBehind-Datei folgende Schritte aus:

  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 systemeigenen Ansichtstyp um.

Die systemeigene 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 systemeigene XAML-Ansichten für verschiedene Plattformen untergeordnete Elemente desselben ContentViewsein können. Das folgende Codebeispiel veranschaulicht diese Technik:

<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 den 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 die ContentView.Content Eigenschaft wird zugegriffen, um die umschlossene native Ansicht als plattformspezifische NativeViewWrapper Instanz abzurufen. Auf NativeViewWrapper.NativeElement die Eigenschaft wird dann zugegriffen, um die systemeigene Ansicht als nativen Typ abzurufen. Die API der nativen Ansicht wird dann 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 als Reaktion auf ein Touchereignis einen EventHandler Delegaten nutzt. Die Universelle Windows-Plattform (UWP) verwendet jedoch eine separate RoutedEventHandler, die wiederum den OnButtonTap Ereignishandler in diesem Beispiel verwendet. Wenn auf eine systemeigene Schaltfläche geklickt wird, wird der OnButtonTap Ereignishandler ausgeführt, wodurch das systemeigene Steuerelement skaliert und gedreht wird, das in der ContentView benannten contentViewTextParentSchaltfläche enthalten ist. Die folgenden Screenshots veranschaulichen dies auf jeder Plattform:

ContentView mit einem systemeigenen Steuerelement

Native Ansichten der Unterklassen

Viele native iOS- und Android-Ansichten eignen sich nicht zum Instanziieren in XAML, da sie Methoden anstelle von Eigenschaften verwenden, um das Steuerelement einzurichten. Die Lösung für dieses Problem besteht darin, systemeigene Ansichten in Wrappern zu unterklassen, die eine xamlfreundlichere 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 in bedingten Kompilierungsdirektiven eingeschlossen oder in plattformspezifischen Projekten platziert und aus XAML in einem .NET Standardbibliotheksprojekt referenziert werden.

Im folgenden Codebeispiel wird eine Xamarin.Forms Seite veranschaulicht, die unterklassige native Ansichten 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 ein Label Element, das die vom Benutzer ausgewählten Früchte aus einem systemeigenen Steuerelement anzeigt. Die Label Bindung an die SubclassedNativeControlsPageViewModel.SelectedFruit Eigenschaft. Die BindingContext Seite wird auf eine neue Instanz 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 ihre ItemSource Eigenschaft an die SubclassedNativeControlsPageViewModel.Fruits Sammlung gebunden wird. Auf diese Weise kann der Benutzer eine Frucht auswählen, wie in den folgenden Screenshots gezeigt:

Unterklassige native Ansichten

Unter iOS und Android verwenden die systemeigenen Auswahlmethoden zum Einrichten der Steuerelemente. Daher müssen diese Auswahlen unterklassen sein, um Eigenschaften verfügbar zu machen, damit sie XAML-freundlich sind. Auf dem Universelle Windows-Plattform (UWP) ist dies ComboBox bereits XAML-freundlich und erfordert daher keine Unterklassen.

iOS

Die iOS-Implementierung unterklassiert die UIPickerView Ansicht und macht Eigenschaften und ein Ereignis verfügbar, das einfach aus XAML verwendet werden kann:

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 und eigenschaften und SelectedItem ein SelectedItemChanged Ereignis verfügbarItemsSource. A UIPickerView erfordert ein zugrunde liegendes UIPickerViewModel Datenmodell, auf das über die MyUIPickerView Eigenschaften und das 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 Items Eigenschaft bereit. Wenn das ausgewählte Element in den MyUIPickerView Änderungen ausgeführt wird, 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 Instanz verwendet werden.

Android

Die Android-Implementierung unterklassiert die Spinner Ansicht und macht Eigenschaften und ein Ereignis verfügbar, das einfach aus XAML verwendet werden kann:

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 und eigenschaften und SelectedObject ein ItemSelected Ereignis verfügbarItemsSource. Die von der MySpinner Klasse angezeigten Elemente werden von der Adapter zugeordneten Ansicht bereitgestellt, und Elemente werden beim ersten Festlegen der ItemsSource Eigenschaft aufgefülltAdapter. Wenn sich das ausgewählte Element in der MySpinner Klasse ändert, aktualisiert der OnBindableSpinnerItemSelected Ereignishandler die SelectedObject Eigenschaft.