Udostępnij za pośrednictwem


Widoki natywne w języku XAML

Widoki natywne z systemów iOS, Android i platforma uniwersalna systemu Windows można odwoływać się bezpośrednio z Xamarin.Forms plików XAML. Właściwości i programy obsługi zdarzeń można ustawić w widokach natywnych i mogą wchodzić w interakcje z widokami Xamarin.Forms . W tym artykule pokazano, jak korzystać z widoków natywnych z Xamarin.Forms plików XAML.

Aby osadzić widok natywny w Xamarin.Forms pliku XAML:

  1. Dodaj deklarację xmlns przestrzeni nazw w pliku XAML dla przestrzeni nazw zawierającej widok macierzysty.
  2. Utwórz wystąpienie widoku natywnego w pliku XAML.

Ważne

Skompilowany kod XAML musi być wyłączony dla wszystkich stron XAML korzystających z widoków natywnych. Można to osiągnąć, dekorując klasę za kodem dla strony XAML za pomocą atrybutu [XamlCompilation(XamlCompilationOptions.Skip)] . Aby uzyskać więcej informacji na temat kompilacji XAML, zobacz Kompilacja XAML w programie Xamarin.Forms.

Aby odwołać się do widoku natywnego z pliku za pomocą kodu, należy użyć projektu udostępnionego zasobu (SAP) i opakować kod specyficzny dla platformy za pomocą dyrektyw kompilacji warunkowej. Aby uzyskać więcej informacji, zobacz Zobacz widoki natywne z kodu.

Korzystanie z widoków natywnych

W poniższym przykładzie kodu pokazano korzystanie z widoków natywnych dla każdej platformy do elementu 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>

Oprócz określania clr-namespace i assembly dla przestrzeni nazw targetPlatform widoku natywnego należy również określić wartość . Należy ustawić iOSwartość , , Android, UWPWindows (która jest równoważna UWP), macOS, , GTK, Tizen, lub WPF. W czasie wykonywania analizator XAML zignoruje wszystkie prefiksy przestrzeni nazw XML, które targetPlatform nie są zgodne z platformą, na której działa aplikacja.

Każda deklaracja przestrzeni nazw może służyć do odwołowania się do dowolnej klasy lub struktury z określonej przestrzeni nazw. Na przykład ios deklaracja przestrzeni nazw może służyć do odwołowania się do dowolnej klasy lub struktury z przestrzeni nazw systemu iOS UIKit . Właściwości widoku natywnego można ustawić za pomocą języka XAML, ale właściwości i typy obiektów muszą być zgodne. Na przykład UILabel.TextColor właściwość jest ustawiona na UIColor.Red użycie x:Static rozszerzenia znaczników i ios przestrzeni nazw.

Właściwości możliwe do powiązania i dołączone właściwości możliwe do powiązania można również ustawić w widokach natywnych przy użyciu Class.BindableProperty="value" składni . Każdy widok macierzysty jest owinięty w wystąpieniu specyficznym dla NativeViewWrapper platformy, które pochodzi z Xamarin.Forms.View klasy . Ustawienie właściwości możliwej do powiązania lub dołączonej właściwości możliwej do powiązania w widoku natywnym powoduje przeniesienie wartości właściwości do otoki. Można na przykład określić wyśrodkowany układ poziomy, ustawiając View.HorizontalOptions="Center" widok macierzysty.

Uwaga

Należy pamiętać, że style nie mogą być używane z widokami natywnymi, ponieważ style mogą dotyczyć tylko właściwości docelowych, które są wspierane przez BindableProperty obiekty.

Konstruktory widżetów systemu Android zwykle wymagają obiektu systemu Android Context jako argumentu i można je udostępnić za pośrednictwem właściwości statycznej w MainActivity klasie. W związku z tym podczas tworzenia widżetu systemu Android w języku XAML Context obiekt musi być zazwyczaj przekazywany do konstruktora widżetu przy użyciu atrybutu x:Arguments z x:Static rozszerzeniem znaczników. Aby uzyskać więcej informacji, zobacz Przekazywanie argumentów do widoków natywnych.

Uwaga

Należy pamiętać, że nazewnictwo widoku natywnego z x:Name programem nie jest możliwe w projekcie biblioteki platformy .NET Standard lub w projekcie udostępnionego zasobu (SAP). Spowoduje to wygenerowanie zmiennej typu natywnego, co spowoduje błąd kompilacji. Widoki natywne można jednak opakować w ContentView wystąpienia i pobrać w pliku za pomocą kodu, pod warunkiem że jest używany system SAP. Aby uzyskać więcej informacji, zobacz Temat Widok natywny z kodu.

Powiązania natywne

Powiązanie danych służy do synchronizowania interfejsu użytkownika ze źródłem danych i upraszcza Xamarin.Forms sposób wyświetlania i interakcji aplikacji z danymi. Pod warunkiem, że obiekt źródłowy implementuje INotifyPropertyChanged interfejs, zmiany w obiekcie źródłowym są automatycznie wypychane do obiektu docelowego przez strukturę powiązania, a zmiany w obiekcie docelowym można opcjonalnie wypchnąć do obiektu źródłowego.

Właściwości widoków natywnych mogą również używać powiązania danych. Poniższy przykład kodu przedstawia powiązanie danych przy użyciu właściwości widoków natywnych:

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

Strona zawiera Entry właściwość, której IsEnabled właściwość wiąże się z właściwością NativeSwitchPageViewModel.IsSwitchOn . Strona BindingContext jest ustawiona na nowe wystąpienie NativeSwitchPageViewModel klasy w pliku za kodem, a klasa ViewModel implementuje INotifyPropertyChanged interfejs.

Strona zawiera również przełącznik macierzysty dla każdej platformy. Każdy przełącznik macierzysty używa TwoWay powiązania, aby zaktualizować wartość NativeSwitchPageViewModel.IsSwitchOn właściwości. W związku z tym, gdy przełącznik jest wyłączony, Entry jest wyłączony, a gdy przełącznik jest włączony, Entry jest włączony. Na poniższych zrzutach ekranu przedstawiono tę funkcję na każdej platformie:

Przełącznik macierzysty wyłączonyPrzełącznik macierzysty włączony

Powiązania dwukierunkowe są obsługiwane automatycznie, pod warunkiem że właściwość natywna implementuje INotifyPropertyChangedelement lub obsługuje funkcję Obserwowanie wartości klucza (KVO) w systemie iOS lub jest elementem DependencyProperty na platformie UWP. Jednak wiele widoków natywnych nie obsługuje powiadomienia o zmianie właściwości. W przypadku tych widoków można określić UpdateSourceEventName wartość właściwości w ramach wyrażenia powiązania. Ta właściwość powinna być ustawiona na nazwę zdarzenia w widoku natywnym, który sygnalizuje zmianę właściwości docelowej. Następnie, gdy wartość przełącznika natywnego zmieni się, klasa zostanie powiadomiona, Binding że użytkownik zmienił wartość przełącznika, a NativeSwitchPageViewModel.IsSwitchOn wartość właściwości zostanie zaktualizowana.

Przekazywanie argumentów do widoków natywnych

Argumenty konstruktora można przekazać do widoków natywnych przy użyciu atrybutu x:Arguments z x:Static rozszerzeniem znaczników. Ponadto metody fabryki widoku natywnego (public static metody zwracające obiekty lub wartości tego samego typu co klasa lub struktura definiujące metody) mogą być wywoływane przez określenie nazwy metody przy użyciu atrybutu x:FactoryMethod i jego argumentów przy użyciu atrybutu x:Arguments .

W poniższym przykładzie kodu przedstawiono obie techniki:

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

Metoda UIFont.FromName factory służy do ustawiania UILabel.Font właściwości na nową UIFont w systemie iOS. Nazwa UIFont i rozmiar są określane przez argumenty metody, które są elementami podrzędnymi atrybutu x:Arguments .

Metoda Typeface.Create factory służy do ustawiania TextView.Typeface właściwości na nową Typeface w systemie Android. Typeface Nazwa i styl rodziny są określane przez argumenty metody, które są elementami podrzędnymi atrybutux:Arguments.

Konstruktor FontFamily służy do ustawiania TextBlock.FontFamily właściwości na nową FontFamily w platforma uniwersalna systemu Windows (UWP). Nazwa FontFamily jest określana przez argument metody, który jest elementem podrzędnym atrybutu x:Arguments .

Uwaga

Argumenty muszą być zgodne z typami wymaganymi przez konstruktora lub metodę fabryki.

Na poniższych zrzutach ekranu przedstawiono wynik określania metody fabryki i argumentów konstruktora w celu ustawienia czcionki w różnych widokach natywnych:

Ustawianie czcionek w widokach natywnych

Aby uzyskać więcej informacji na temat przekazywania argumentów w języku XAML, zobacz Przekazywanie argumentów w języku XAML.

Zapoznaj się z widokami natywnymi z kodu

Chociaż nie można nazwać widoku natywnego atrybutem x:Name , możliwe jest pobranie wystąpienia widoku natywnego zadeklarowanego w pliku XAML z pliku za pomocą kodu w projekcie dostępu współdzielonego, pod warunkiem, że widok natywny jest elementem podrzędnym elementu ContentView określającego x:Name wartość atrybutu. Następnie wewnątrz dyrektyw kompilacji warunkowej w pliku za pomocą kodu należy:

  1. ContentView.Content Pobierz wartość właściwości i rzutuj ją na typ specyficzny dla NativeViewWrapper platformy.
  2. NativeViewWrapper.NativeElement Pobierz właściwość i rzutuj ją na typ widoku natywnego.

Następnie można wywołać natywny interfejs API w widoku natywnym, aby wykonać żądane operacje. Takie podejście oferuje również korzyść, że wiele natywnych widoków XAML dla różnych platform może być elementami podrzędnymi tego samego ContentViewelementu . W poniższym przykładzie kodu przedstawiono tę 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>

W powyższym przykładzie widoki natywne dla każdej platformy są elementami podrzędnymi ContentView kontrolek, z wartością x:Name atrybutu używaną do pobierania ContentView elementu w kodzie:

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

Dostęp ContentView.Content do właściwości umożliwia pobranie opakowanego widoku natywnego jako wystąpienia specyficznego dla NativeViewWrapper platformy. Następnie NativeViewWrapper.NativeElement uzyskuje się dostęp do właściwości w celu pobrania widoku natywnego jako typu natywnego. Interfejs API widoku natywnego jest następnie wywoływany w celu wykonania żądanych operacji.

Przyciski natywne dla systemów iOS i Android współdzielą tę samą OnButtonTap procedurę obsługi zdarzeń, ponieważ każdy przycisk natywny używa delegata EventHandler w odpowiedzi na zdarzenie dotykowe. Jednak platforma uniwersalna systemu Windows (UWP) używa oddzielnego RoutedEventHandlerelementu , który z kolei korzysta OnButtonTap z programu obsługi zdarzeń w tym przykładzie. W związku z tym po kliknięciu OnButtonTap przycisku natywnego program obsługi zdarzeń jest wykonywany, który skaluje i obraca natywną kontrolkę zawartą ContentView w nazwie contentViewTextParent. Poniższe zrzuty ekranu pokazują, że występuje to na każdej platformie:

ContentView zawierający kontrolkę natywną

Widoki natywne podklasy

Wiele natywnych widoków systemów iOS i Android nie nadaje się do tworzenia wystąpień w języku XAML, ponieważ używają metod, a nie właściwości, aby skonfigurować kontrolkę. Rozwiązaniem tego problemu jest podklasa natywnych widoków w otokach, które definiują bardziej przyjazny interfejs API XAML, który używa właściwości do konfigurowania kontrolki i który używa zdarzeń niezależnych od platformy. Opakowane widoki natywne można następnie umieścić w projekcie udostępnionego zasobu (SAP) i otoczony dyrektywami kompilacji warunkowej lub umieścić w projektach specyficznych dla platformy i przywoływane z języka XAML w projekcie biblioteki platformy .NET Standard.

Poniższy przykład kodu przedstawia Xamarin.Forms stronę, która korzysta z podklasowanych widoków natywnych:

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

Strona zawiera obiekt Label , który wyświetla owoce wybrane przez użytkownika z kontrolki natywnej. Powiązanie Label z właściwością SubclassedNativeControlsPageViewModel.SelectedFruit . Strona BindingContext jest ustawiona na nowe wystąpienie SubclassedNativeControlsPageViewModel klasy w pliku za kodem, a klasa ViewModel implementuje INotifyPropertyChanged interfejs.

Strona zawiera również natywny widok selektora dla każdej platformy. Każdy widok macierzysty wyświetla kolekcję owoców przez powiązanie jej ItemSource właściwości z kolekcją SubclassedNativeControlsPageViewModel.Fruits . Dzięki temu użytkownik może wybrać owoce, jak pokazano na poniższych zrzutach ekranu:

Podklasowane widoki natywne

W systemach iOS i Android selektory natywne używają metod do konfigurowania kontrolek. W związku z tym te selektory muszą być podklasowane, aby uwidocznić właściwości, aby były przyjazne dla języka XAML. Na platformie platforma uniwersalna systemu Windows (UWP) ComboBox obiekt jest już przyjazny dla języka XAML i dlatego nie wymaga podklasy.

iOS

Podklasy implementacji systemu iOS uwidacznia UIPickerView właściwości i zdarzenie, które można łatwo użyć z kodu 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 { }
    }
}

Klasa MyUIPickerView uwidacznia ItemsSource i SelectedItem właściwości oraz SelectedItemChanged zdarzenie. Element UIPickerView wymaga podstawowego UIPickerViewModel modelu danych, który jest uzyskiwany przez MyUIPickerView właściwości i zdarzenie. UIPickerViewModel Model danych jest dostarczany przez klasęPickerModel:

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

Klasa PickerModel udostępnia podstawowy magazyn dla MyUIPickerView klasy za pośrednictwem Items właściwości . Za każdym razem, gdy wybrany element w MyUIPickerView zmianach zostanie wykonany, Selected metoda aktualizuje wybrany indeks i uruchamia ItemChanged zdarzenie. SelectedItem Dzięki temu właściwość będzie zawsze zwracać ostatni element wybrany przez użytkownika. Ponadto PickerModel klasy zastępują metody używane do konfigurowania MyUIPickerView wystąpienia.

Android

Podklasy Spinner implementacji systemu Android wyświetlają właściwości i zdarzenie, które można łatwo użyć z kodu 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);
    }
}

Klasa MySpinner uwidacznia ItemsSource i SelectedObject właściwości oraz ItemSelected zdarzenie. Elementy wyświetlane przez klasę MySpinner są dostarczane przez Adapter skojarzony z widokiem, a elementy są wypełniane w Adapter momencie ItemsSource pierwszego ustawienia właściwości. Za każdym razem, gdy wybrany element w MySpinner klasie ulegnie zmianie, OnBindableSpinnerItemSelected program obsługi zdarzeń aktualizuje SelectedObject właściwość.