Поделиться через


Собственные представления в XAML

Собственные представления из iOS, Android и универсальная платформа Windows можно напрямую ссылаться на Xamarin.Forms файлы XAML. Свойства и обработчики событий можно задать в собственных представлениях, и они могут взаимодействовать с представлениями Xamarin.Forms . В этой статье показано, как использовать собственные представления из Xamarin.Forms XAML-файлов.

Чтобы внедрить собственное представление в Xamarin.Forms XAML-файл:

  1. xmlns Добавьте объявление пространства имен в XAML-файле для пространства имен, содержащего собственное представление.
  2. Создайте экземпляр собственного представления в XAML-файле.

Внимание

Скомпилированный КОД XAML должен быть отключен для всех страниц XAML, использующих собственные представления. Это можно сделать, декорируя класс code-behind для страницы XAML атрибутом [XamlCompilation(XamlCompilationOptions.Skip)] . Дополнительные сведения о компиляции XAML см. в Xamarin.Formsразделе "Компиляция XAML".

Чтобы ссылаться на собственное представление из файла программной части, необходимо использовать проект общего ресурса (SAP) и упаковать код для конкретной платформы с помощью директив условной компиляции. Дополнительные сведения см. в статье "Ссылки на собственные представления из кода".

Использование собственных представлений

В следующем примере кода демонстрируется использование собственных представлений для каждой 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>

Кроме того, targetPlatform необходимо указать clr-namespace assembly и для собственного пространства имен представления. Это значение должно иметь значение iOS, Android, UWPWindows (что эквивалентно UWPmacOS), , , GTK, Tizenили WPF. Во время выполнения средство синтаксического анализа XAML будет игнорировать все префиксы пространства имен XML, имеющие не соответствующую targetPlatform платформе, на которой работает приложение.

Каждое объявление пространства имен можно использовать для ссылки на любой класс или структуру из указанного пространства имен. Например, ios объявление пространства имен можно использовать для ссылки на любой класс или структуру из пространства имен iOS UIKit . Свойства собственного представления можно задать с помощью XAML, но свойства и типы объектов должны соответствовать. Например, UILabel.TextColor для свойства задано UIColor.Red использование x:Static расширения разметки и ios пространства имен.

Привязываемые свойства и присоединенные привязываемые свойства также можно задать в собственных представлениях с помощью синтаксиса Class.BindableProperty="value" . Каждое собственное представление упаковывается в экземпляр для конкретной NativeViewWrapper платформы, который является производным от Xamarin.Forms.View класса. Задание привязываемого свойства или присоединенного привязываемого свойства в собственном представлении передает значение свойства оболочке. Например, по центру горизонтального макета можно указать, задав View.HorizontalOptions="Center" в собственном представлении.

Примечание.

Обратите внимание, что стили нельзя использовать с собственными представлениями, так как стили могут использовать только целевые свойства, поддерживаемые объектами BindableProperty .

Конструкторы мини-приложений Android обычно требуют объекта Android Context в качестве аргумента, и это можно сделать доступным через статическое свойство в MainActivity классе. Поэтому при создании мини-приложения Android в XAML Context объект обычно должен передаваться конструктору мини-приложения с помощью x:Arguments атрибута с расширением x:Static разметки. Дополнительные сведения см. в разделе "Передача аргументов в собственные представления".

Примечание.

Обратите внимание, что именование собственного представления x:Name невозможно либо в проекте библиотеки .NET Standard, либо в проекте общего ресурса (SAP). Это приведет к созданию переменной собственного типа, что приведет к ошибке компиляции. Однако собственные представления можно упаковать в ContentView экземпляры и получить в файле программной части, если используется SAP. Дополнительные сведения см. в разделе "Машинное представление" из кода.

Собственные привязки

Привязка данных используется для синхронизации пользовательского интерфейса с источником данных и упрощает Xamarin.Forms отображение и взаимодействие приложения с данными. При условии, что исходный объект реализует INotifyPropertyChanged интерфейс, изменения в исходном объекте автоматически отправляются в целевой объект платформой привязки, а изменения в целевом объекте при необходимости могут быть отправлены в исходный объект.

Свойства собственных представлений также могут использовать привязку данных. В следующем примере кода демонстрируется привязка данных с помощью свойств собственных представлений:

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

Страница содержит свойство, свойство которого привязывается Entry к свойствуNativeSwitchPageViewModel.IsSwitchOn.IsEnabled На BindingContext странице устанавливается новый экземпляр NativeSwitchPageViewModel класса в файле кода программной части с классом ViewModel, реализующим INotifyPropertyChanged интерфейс.

Страница также содержит собственный коммутатор для каждой платформы. Каждый собственный коммутатор использует привязку TwoWay для обновления значения NativeSwitchPageViewModel.IsSwitchOn свойства. Поэтому при отключении Entry выключения отключена и включена функция включения переключателя Entry . На следующих снимках экрана показана эта функция на каждой платформе:

Отключен собственный коммутаторВключение собственного коммутатора

Двусторонняя привязка поддерживается автоматически, если собственное свойство реализует INotifyPropertyChangedили поддерживает наблюдение за ключом (KVO) в iOS или используется DependencyProperty в UWP. Однако многие собственные представления не поддерживают уведомление об изменении свойств. Для этих представлений можно указать UpdateSourceEventName значение свойства в рамках выражения привязки. Это свойство должно иметь имя события в собственном представлении, которое сигнализирует об изменении целевого свойства. Затем, когда изменяется значение собственного коммутатора, класс уведомляется о том, Binding что пользователь изменил значение коммутатора, а NativeSwitchPageViewModel.IsSwitchOn значение свойства обновляется.

Передача аргументов в собственные представления

Аргументы конструктора можно передать в собственные представления с помощью атрибута x:Arguments с расширением x:Static разметки. Кроме того, методы фабрики собственных представлений (public static методы, возвращающие объекты или значения того же типа, что и класс или структура, определяющие методы), могут вызываться путем указания имени метода с помощью атрибута и его аргументов с помощью x:FactoryMethod атрибута x:Arguments .

В следующем примере кода показаны оба метода:

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

Метод UIFont.FromName фабрики используется для задания UILabel.Font свойства новым UIFont в iOS. UIFont Имя и размер указываются аргументами метода, которые являются дочерними элементами атрибутаx:Arguments.

Метод Typeface.Create фабрики используется для задания TextView.Typeface свойства новым Typeface в Android. Имя Typeface семейства и стиль задаются аргументами метода, которые являются дочерними элементами атрибута x:Arguments .

Конструктор FontFamily используется для задания TextBlock.FontFamily свойства новым FontFamily для универсальная платформа Windows (UWP). Имя FontFamily указывается аргументом метода, который является дочерним элементом атрибута x:Arguments .

Примечание.

Аргументы должны соответствовать типам, необходимым конструктору или методу фабрики.

На следующих снимках экрана показан результат указания метода фабрики и аргументов конструктора для задания шрифта в различных собственных представлениях:

Настройка шрифтов в собственных представлениях

Дополнительные сведения о передаче аргументов в XAML см. в разделе "Передача аргументов" в XAML.

Ссылки на собственные представления из кода

Хотя имя собственного представления с x:Name атрибутом невозможно, можно получить экземпляр собственного представления, объявленный в XAML-файле из файла кода программной части в проекте общего доступа, если собственное представление является дочерним ContentView x:Name элементом атрибута. Затем в директивах условной компиляции в файле программной части необходимо выполнить следующее:

  1. ContentView.Content Извлеките значение свойства и приведите его к типу конкретной NativeViewWrapper платформы.
  2. NativeViewWrapper.NativeElement Получите свойство и приведите его к типу собственного представления.

Затем собственный API можно вызвать в собственном представлении для выполнения требуемых операций. Этот подход также обеспечивает преимущество того, что несколько собственных представлений XAML для разных платформ могут быть дочерними элементами одного и того же ContentView. В следующем примере кода демонстрируется этот метод:

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

В приведенном выше примере собственные представления для каждой платформы являются дочерними элементами управления, с x:Name значением атрибутаContentView, используемым для получения ContentView в коде программной части:

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 к свойству осуществляется для получения встроенного представления в виде экземпляра конкретной NativeViewWrapper платформы. Затем NativeViewWrapper.NativeElement свойство получает доступ к собственному представлению в качестве собственного типа. Затем API собственного представления вызывается для выполнения требуемых операций.

Собственные кнопки iOS и Android используют один и тот же OnButtonTap обработчик событий, так как каждая собственная кнопка использует EventHandler делегат в ответ на событие сенсорного ввода. Однако в универсальная платформа Windows (UWP) используется отдельный RoutedEventHandlerобработчик событий, который, в свою очередь, использует OnButtonTap обработчик событий в этом примере. Поэтому при нажатии собственной кнопки обработчик событий выполняется, OnButtonTap который масштабируется и поворачивает собственный элемент управления, содержащийся в именованном элементе contentViewTextParentContentView управления. На следующих снимках экрана показано, что это происходит на каждой платформе:

ContentView, содержащий собственный элемент управления

Собственные представления подклассов

Многие собственные представления iOS и Android не подходят для создания экземпляров в XAML, так как они используют методы, а не свойства для настройки элемента управления. Решением этой проблемы является подкласс собственных представлений в оболочках, определяющих более понятный API XAML, который использует свойства для настройки элемента управления и использует события, независимые от платформы. Затем упакованные собственные представления можно поместить в проект общего ресурса (SAP) и окружены директивами условной компиляции или поместить в проекты для конкретной платформы и ссылаться на XAML в проекте библиотеки .NET Standard.

В следующем примере кода показана Xamarin.Forms страница, используюющая подклассированные собственные представления:

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

Страница содержит Label фрукты, выбранные пользователем из собственного элемента управления. Привязка Label к свойству SubclassedNativeControlsPageViewModel.SelectedFruit . На BindingContext странице устанавливается новый экземпляр SubclassedNativeControlsPageViewModel класса в файле кода программной части с классом ViewModel, реализующим INotifyPropertyChanged интерфейс.

Страница также содержит собственное представление средства выбора для каждой платформы. Каждое собственное представление отображает коллекцию фруктов путем привязки его ItemSource свойства к SubclassedNativeControlsPageViewModel.Fruits коллекции. Это позволяет пользователю выбрать фрукты, как показано на следующих снимках экрана:

Подклассированные собственные представления

В iOS и Android собственные средства выбора используют методы для настройки элементов управления. Поэтому эти средства выбора должны быть подклассами для предоставления свойств, чтобы сделать их понятными для XAML. В универсальная платформа Windows (UWP) ComboBox уже понятно xaml и поэтому не требует подкласса.

iOS

Реализация iOS классирует UIPickerView представление и предоставляет свойства и событие, которое можно легко использовать из 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 { }
    }
}

Класс MyUIPickerView предоставляет ItemsSource и SelectedItem свойства, а также SelectedItemChanged событие. Требуется UIPickerView базовая UIPickerViewModel модель данных, доступ к которой осуществляется свойствами и событием MyUIPickerView . Модель UIPickerViewModel данных предоставляется классом 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());
        }
    }
}

Класс PickerModel предоставляет базовое хранилище для MyUIPickerView класса через Items свойство. При каждом изменении выбранного элемента MyUIPickerView Selected метод выполняется, который обновляет выбранный индекс и запускает ItemChanged событие. Это гарантирует, что SelectedItem свойство всегда возвращает последний элемент, выбранный пользователем. Кроме того, PickerModel класс переопределяет методы, используемые для настройки экземпляра MyUIPickerView .

Android

Реализация Android классифицирует Spinner представление и предоставляет свойства и событие, которое можно легко использовать из 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);
    }
}

Класс MySpinner предоставляет ItemsSource и SelectedObject свойства, а также ItemSelected событие. Элементы, отображаемые MySpinner классом Adapter , предоставляются связанным с представлением, а элементы заполняются Adapter ItemsSource при первом наборе свойства. Всякий раз, когда выбранный элемент в MySpinner классе изменяется, OnBindableSpinnerItemSelected обработчик событий обновляет SelectedObject свойство.