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:
- Fügen Sie in der XAML-Datei eine
xmlns
Namespacedeklaration für den Namespace hinzu, der die systemeigene Ansicht enthält. - 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
, Tizen
oder 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:
Bidirektionale Bindungen werden automatisch unterstützt, vorausgesetzt, dass die systemeigene Eigenschaft implementiert INotifyPropertyChanged
oder 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:
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:
- Rufen Sie den
ContentView.Content
Eigenschaftswert ab, und wandeln Sie ihn in einen plattformspezifischenNativeViewWrapper
Typ um. - 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 ContentView
sein 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 contentViewTextParent
Schaltfläche enthalten ist. Die folgenden Screenshots veranschaulichen dies auf jeder Plattform:
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:
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.