Native Ansichten in XAML
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 :
- Fügen Sie eine
xmlns
Namespacedeklaration in der XAML-Datei für den Namespace hinzu, der die native Ansicht enthält. - 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.FormsContentPage
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"
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
, , Android
UWP
, ( Windows
entspricht UWP
), macOS
, , GTK
Tizen
oder WPF
festgelegt 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:
Bidirektionale Bindungen werden automatisch unterstützt, sofern die native Eigenschaft Key-Value Observing (KVO) unter iOS implementiert INotifyPropertyChanged
oder 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:
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:
- 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 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 ContentView
sein 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 contentViewTextParent
enthalten ist. Die folgenden Screenshots veranschaulichen dies auf jeder Plattform:
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:
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.