Visualizzazioni native in XAML

Download Sample Scaricare l'esempio

È possibile fare riferimento direttamente alle visualizzazioni native da iOS, Android e dal piattaforma UWP (Universal Windows Platform) dai Xamarin.Forms file XAML. Le proprietà e i gestori eventi possono essere impostati nelle visualizzazioni native e possono interagire con Xamarin.Forms le visualizzazioni. Questo articolo illustra come usare le visualizzazioni native dai Xamarin.Forms file XAML.

Per incorporare una visualizzazione nativa in un Xamarin.Forms file XAML:

  1. Aggiungere una xmlns dichiarazione dello spazio dei nomi nel file XAML per lo spazio dei nomi che contiene la visualizzazione nativa.
  2. Creare un'istanza della visualizzazione nativa nel file XAML.

Importante

Xaml compilato deve essere disabilitato per tutte le pagine XAML che usano visualizzazioni native. A tale scopo, è possibile decorare la classe code-behind per la pagina XAML con l'attributo [XamlCompilation(XamlCompilationOptions.Skip)] . Per altre informazioni sulla compilazione XAML, vedere Compilazione XAML in Xamarin.Forms.

Per fare riferimento a una visualizzazione nativa da un file code-behind, è necessario usare un progetto di asset condiviso (SAP) ed eseguire il wrapping del codice specifico della piattaforma con direttive di compilazione condizionale. Per altre informazioni, vedere Fare riferimento alle visualizzazioni native dal codice.

Utilizzare le visualizzazioni native

Nell'esempio di codice seguente viene illustrato l'utilizzo di viste native per ogni piattaforma in un Xamarin.FormsContentPageoggetto :

<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>

Oltre a specificare e clr-namespaceassembly per uno spazio dei nomi di visualizzazione nativa, è necessario specificare anche un oggetto targetPlatform . Deve essere impostato su , , , (equivalente a UWP), macOS, GTKTizen, o WPF. WindowsUWPAndroidiOS In fase di esecuzione, il parser XAML ignorerà tutti i prefissi dello spazio dei nomi XML con un targetPlatform oggetto che non corrisponde alla piattaforma in cui è in esecuzione l'applicazione.

Ogni dichiarazione dello spazio dei nomi può essere usata per fare riferimento a qualsiasi classe o struttura dallo spazio dei nomi specificato. Ad esempio, la ios dichiarazione dello spazio dei nomi può essere usata per fare riferimento a qualsiasi classe o struttura dallo spazio dei nomi iOS UIKit . Le proprietà della visualizzazione nativa possono essere impostate tramite XAML, ma i tipi di proprietà e di oggetto devono corrispondere. Ad esempio, la UILabel.TextColor proprietà è impostata su UIColor.Red usando l'estensione x:Static di markup e lo ios spazio dei nomi .

Le proprietà associabili e le proprietà associabili associate possono essere impostate anche nelle viste native usando la Class.BindableProperty="value" sintassi . Ogni visualizzazione nativa viene sottoposta a wrapping in un'istanza specifica NativeViewWrapper della Xamarin.Forms.View piattaforma, che deriva dalla classe . L'impostazione di una proprietà associabile o associabile associata in una visualizzazione nativa trasferisce il valore della proprietà al wrapper. Ad esempio, è possibile specificare un layout orizzontale centrato impostando View.HorizontalOptions="Center" nella visualizzazione nativa.

Nota

Si noti che gli stili non possono essere usati con le visualizzazioni native, perché gli stili possono essere destinati solo alle proprietà supportate dagli BindableProperty oggetti .

I costruttori di widget Android richiedono in genere l'oggetto Android Context come argomento e questo può essere reso disponibile tramite una proprietà statica nella MainActivity classe . Pertanto, quando si crea un widget Android in XAML, l'oggetto Context deve in genere essere passato al costruttore del widget usando l'attributo con un'estensione x:Argumentsx:Static di markup. Per altre informazioni, vedere Passare argomenti a viste native.

Nota

Si noti che la denominazione di una visualizzazione nativa con x:Name non è possibile in un progetto di libreria .NET Standard o in un progetto di asset condiviso (SAP). In questo modo verrà generata una variabile del tipo nativo, che causerà un errore di compilazione. Tuttavia, le viste native possono essere sottoposte a wrapping in ContentView istanze e recuperate nel file code-behind, purché venga usato un sap. Per altre informazioni, vedere Fare riferimento alla visualizzazione nativa dal codice.

Associazioni native

Il data binding viene usato per sincronizzare un'interfaccia utente con l'origine dati e semplifica la visualizzazione e l'interazione di un'applicazione Xamarin.Forms con i relativi dati. A condizione che l'oggetto di origine implementi l'interfacciaINotifyPropertyChanged, le modifiche apportate all'oggetto di origine vengono automaticamente inserite nell'oggettodi destinazione dal framework di associazione e le modifiche apportate all'oggetto di destinazione possono essere eventualmente spostate nell'oggetto di origine.

Le proprietà delle viste native possono anche usare il data binding. Nell'esempio di codice seguente viene illustrato il data binding usando le proprietà delle viste native:

<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>

La pagina contiene un oggetto Entry la NativeSwitchPageViewModel.IsSwitchOn cui IsEnabled proprietà è associata alla proprietà . La BindingContext proprietà della pagina è impostata su una nuova istanza della NativeSwitchPageViewModel classe nel file code-behind, con la classe ViewModel che implementa l'interfaccia INotifyPropertyChanged .

La pagina contiene anche un commutatore nativo per ogni piattaforma. Ogni opzione nativa usa un'associazione TwoWay per aggiornare il valore della NativeSwitchPageViewModel.IsSwitchOn proprietà. Pertanto, quando l'interruttore è spento, l'oggetto Entry è disabilitato e, quando l'opzione Entry è attivata, è abilitata. Gli screenshot seguenti mostrano questa funzionalità in ogni piattaforma:

Native Switch DisabledNative Switch Enabled

Le associazioni bidirezionali sono supportate automaticamente a condizione che la proprietà nativa implementi INotifyPropertyChangedo supporti L'osservazione chiave-valore (KVO) in iOS o sia una piattaforma DependencyProperty UWP. Tuttavia, molte viste native non supportano la notifica delle modifiche alle proprietà. Per queste viste, è possibile specificare un UpdateSourceEventName valore della proprietà come parte dell'espressione di associazione. Questa proprietà deve essere impostata sul nome di un evento nella visualizzazione nativa che segnala quando la proprietà di destinazione è stata modificata. Quindi, quando il valore dell'opzione nativa cambia, la Binding classe riceve una notifica che indica che l'utente ha modificato il valore dell'opzione e il valore della NativeSwitchPageViewModel.IsSwitchOn proprietà viene aggiornato.

Passare argomenti a viste native

Gli argomenti del costruttore possono essere passati alle viste native usando l'attributo con un'estensione x:Argumentsx:Static di markup. Inoltre, i metodi della factory di visualizzazione nativa (public static metodi che restituiscono oggetti o valori dello stesso tipo della classe o della struttura che definisce i metodi) possono essere chiamati specificando il nome del metodo usando l'attributo x:FactoryMethod e i relativi argomenti usando l'attributo x:Arguments .

L'esempio di codice seguente illustra entrambe le tecniche:

<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>

Il UIFont.FromName metodo factory viene usato per impostare la UILabel.Font proprietà su un nuovo UIFont in iOS. Il UIFont nome e le dimensioni vengono specificati dagli argomenti del metodo figlio dell'attributo x:Arguments .

Il Typeface.Create metodo factory viene usato per impostare la TextView.Typeface proprietà su un nuovo Typeface in Android. Il Typeface nome e lo stile della famiglia vengono specificati dagli argomenti del metodo figlio dell'attributo x:Arguments .

Il FontFamily costruttore viene usato per impostare la TextBlock.FontFamily proprietà su un nuovo FontFamily oggetto nella piattaforma UWP (Universal Windows Platform) (UWP). Il FontFamily nome viene specificato dall'argomento del metodo figlio dell'attributo x:Arguments .

Nota

Gli argomenti devono corrispondere ai tipi richiesti dal costruttore o dal metodo factory.

Gli screenshot seguenti mostrano il risultato della specifica di argomenti del metodo factory e del costruttore per impostare il tipo di carattere in visualizzazioni native diverse:

Setting Fonts on Native Views

Per altre informazioni sul passaggio di argomenti in XAML, vedere Passaggio di argomenti in XAML.

Fare riferimento alle visualizzazioni native dal codice

Sebbene non sia possibile denominare una visualizzazione nativa con l'attributo , è possibile recuperare un'istanza x:Name di visualizzazione nativa dichiarata in un file XAML dal file code-behind in un progetto di accesso condiviso, purché la visualizzazione nativa sia un elemento figlio di un ContentView oggetto che specifica un x:Name valore di attributo. Quindi, all'interno delle direttive di compilazione condizionale nel file code-behind è necessario:

  1. Recuperare il valore della proprietà ed eseguirne il ContentView.Content cast in un tipo specifico NativeViewWrapper della piattaforma.
  2. Recuperare la NativeViewWrapper.NativeElement proprietà ed eseguirne il cast nel tipo di visualizzazione nativa.

L'API nativa può quindi essere richiamata nella visualizzazione nativa per eseguire le operazioni desiderate. Questo approccio offre anche il vantaggio che più visualizzazioni native XAML per piattaforme diverse possono essere elementi figlio dello stesso ContentView. L'esempio di codice seguente illustra questa tecnica:

<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>

Nell'esempio precedente, le visualizzazioni native per ogni piattaforma sono elementi figlio di controlli, con il x:Name valore dell'attributo ContentViewContentView usato per recuperare in code-behind:

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;
    }
}

È ContentView.Content possibile accedere alla proprietà per recuperare la visualizzazione nativa di cui è stato eseguito il wrapping come istanza specifica NativeViewWrapper della piattaforma. Viene NativeViewWrapper.NativeElement quindi eseguito l'accesso alla proprietà per recuperare la visualizzazione nativa come tipo nativo. L'API della visualizzazione nativa viene quindi richiamata per eseguire le operazioni desiderate.

I pulsanti nativi iOS e Android condividono lo stesso OnButtonTap gestore eventi, perché ogni pulsante nativo usa un EventHandler delegato in risposta a un evento di tocco. Tuttavia, il piattaforma UWP (Universal Windows Platform) (UWP) usa un oggetto separatoRoutedEventHandler, che a sua volta usa il OnButtonTap gestore eventi in questo esempio. Pertanto, quando si fa clic su un pulsante nativo, viene eseguito il OnButtonTap gestore eventi, che ridimensiona e ruota il controllo nativo contenuto all'interno dell'oggetto ContentView denominato contentViewTextParent. Gli screenshot seguenti illustrano questa situazione in ogni piattaforma:

ContentView Containing a Native Control

Viste native della sottoclasse

Molte visualizzazioni native iOS e Android non sono adatte per creare un'istanza in XAML perché usano metodi, anziché proprietà, per configurare il controllo. La soluzione a questo problema consiste nella sottoclasse delle visualizzazioni native nei wrapper che definiscono un'API più semplice da XAML che usa le proprietà per configurare il controllo e che usa eventi indipendenti dalla piattaforma. Le visualizzazioni native di cui è stato eseguito il wrapping possono quindi essere inserite in un progetto di asset condiviso (SAP) e racchiuse con direttive di compilazione condizionale oppure inserite in progetti specifici della piattaforma e a cui viene fatto riferimento da XAML in un progetto di libreria .NET Standard.

Nell'esempio di codice seguente viene illustrata una Xamarin.Forms pagina che utilizza viste native sottoclassate:

<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>

La pagina contiene un oggetto Label che visualizza la frutta scelta dall'utente da un controllo nativo. L'oggetto Label viene associato alla SubclassedNativeControlsPageViewModel.SelectedFruit proprietà . La BindingContext proprietà della pagina è impostata su una nuova istanza della SubclassedNativeControlsPageViewModel classe nel file code-behind, con la classe ViewModel che implementa l'interfaccia INotifyPropertyChanged .

La pagina contiene anche una visualizzazione selezione nativa per ogni piattaforma. Ogni visualizzazione nativa visualizza la raccolta di frutti associandone la ItemSourceSubclassedNativeControlsPageViewModel.Fruits proprietà alla raccolta. In questo modo l'utente può scegliere un frutto, come illustrato negli screenshot seguenti:

Subclassed Native Views

In iOS e Android gli strumenti di selezione nativi usano metodi per configurare i controlli. Pertanto, questi picker devono essere sottoclassati per esporre le proprietà per renderle compatibili con XAML. Nella piattaforma UWP (Universal Windows Platform) (UWP), è ComboBox già compatibile con XAML e quindi non richiede la sottoclasse.

iOS

L'implementazione di iOS sottoclasse la UIPickerView visualizzazione ed espone le proprietà e un evento che può essere facilmente utilizzato da XAML:

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 { }
    }
}

La MyUIPickerView classe espone ItemsSource le proprietà e SelectedItem e un SelectedItemChanged evento. Un UIPickerView oggetto richiede un modello di dati sottostante UIPickerViewModel , accessibile dalle proprietà e dall'evento MyUIPickerView . Il UIPickerViewModel modello di dati viene fornito dalla PickerModel classe :

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());
        }
    }
}

La PickerModel classe fornisce l'archiviazione sottostante per la MyUIPickerView classe tramite la Items proprietà . Ogni volta che l'elemento selezionato viene MyUIPickerView modificato, viene eseguito il Selected metodo che aggiorna l'indice selezionato e genera l'evento ItemChanged . In questo modo, la SelectedItem proprietà restituirà sempre l'ultimo elemento selezionato dall'utente. Inoltre, la classe esegue l'override PickerModel dei metodi utilizzati per configurare l'istanza MyUIPickerView .

Android

L'implementazione di Android sottoclasse la Spinner visualizzazione ed espone le proprietà e un evento che può essere facilmente utilizzato da XAML:

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);
    }
}

La MySpinner classe espone ItemsSource le proprietà e SelectedObject e un ItemSelected evento. Gli elementi visualizzati dalla MySpinner classe vengono forniti dall'oggetto Adapter associato alla visualizzazione e gli elementi vengono popolati in Adapter quando la ItemsSource proprietà viene impostata per la prima volta. Ogni volta che l'elemento selezionato nella MySpinner classe cambia, il OnBindableSpinnerItemSelected gestore eventi aggiorna la SelectedObject proprietà.