采用 XAML 的本机视图

Download Sample下载示例

iOS、Android 和通用 Windows 平台的本机视图可直接从 Xamarin.Forms XAML 文件引用。 可以在本机视图上设置属性和事件处理程序,并且可以与 Xamarin.Forms 视图交互。 本文演示如何从 Xamarin.Forms XAML 文件使用本机视图。

若要将本机视图嵌入到 Xamarin.Forms XAML 文件中,请执行以下操作:

  1. 在 XAML 文件中为包含本机视图的命名空间添加 xmlns 命名空间声明。
  2. 在 XAML 文件中创建本机视图的实例。

重要

对于使用本机视图的任何 XAML 页面,必须禁用已编译的 XAML。 这可以通过使用 [XamlCompilation(XamlCompilationOptions.Skip)] 特性修饰 XAML 页面的代码隐藏类来实现。 有关 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>

除了为本机视图命名空间指定 clr-namespaceassembly 外,还必须指定targetPlatform。 这应该设置为 iOSAndroidUWPWindows(相当于 UWP)、macOSGTKTizenWPF。 在运行时,XAML 分析程序将忽略具有与运行应用程序的平台不匹配的 targetPlatform 的任何 XML 命名空间前缀。

每个命名空间声明都可用于引用指定命名空间中的任何类或结构。 例如,ios 命名空间声明可用于引用 iOS UIKit 命名空间中的任何类或结构。 可以通过 XAML 设置本机视图的属性,但属性和对象类型必须匹配。 例如,使用 x:Static 标记扩展和 ios 命名空间将 UILabel.TextColor 属性设置为 UIColor.Red

也可以使用 Class.BindableProperty="value" 语法在本机视图中设置可绑定属性和附加的可绑定属性。 每个本机视图都包装在特定于平台的 NativeViewWrapper 实例中,该实例派生自 Xamarin.Forms.View 类。 在本机视图中设置可绑定属性或附加的可绑定属性会将属性值传输到包装器。 例如,可以通过在本机视图中设置 View.HorizontalOptions="Center" 来指定居中水平布局。

注意

请注意,样式不能与本机视图一起使用,因为样式只能以 BindableProperty 对象支持的属性为目标。

Android 小组件构造函数通常需要 Android Context 对象作为自变量,这可以通过 MainActivity 类中的静态属性来实现。 因此,在 XAML 中创建 Android 小组件时,通常必须使用具有 x:Static 标记扩展的 x:Arguments 特性将 Context 对象传递给小组件的构造函数。 有关详细信息,请参阅将自变量传递给本机视图

注意

请注意,在 .NET Standard 库项目或共享资产项目 (SAP) 中无法命名具有 x:Name 的本机视图。 这样做将生成本机类型的变量,这将导致编译错误。 但是,可以在 ContentView 实例中包装本机视图,并在代码隐藏文件中检索,前提是正在使用 SAP。 有关详细信息,请参阅从代码引用本机视图

本机绑定

数据绑定用于将 UI 与其数据源同步,并简化了 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,其 IsEnabled 属性绑定到 NativeSwitchPageViewModel.IsSwitchOn 属性。 页面的 BindingContext 设置为代码隐藏文件中 NativeSwitchPageViewModel 类的新实例,ViewModel 类实现 INotifyPropertyChanged 接口。

该页还包含每个平台的本机开关。 每个本机开关都使用 TwoWay 绑定来更新 NativeSwitchPageViewModel.IsSwitchOn 属性的值。 因此,当开关关闭时,Entry 被禁用,当开关打开时,Entry 启用。 以下屏幕截图显示了每个平台上的此功能:

Native Switch DisabledNative Switch Enabled

如果本机属性实现 INotifyPropertyChanged,或在 iOS 上支持键值观测 (KVO),或在 UWP 上是 DependencyProperty,则自动支持双向绑定。 但是,许多本机视图不支持属性更改通知。 对于这些视图,可以将 UpdateSourceEventName 属性值指定为绑定表达式的一部分。 此属性应设置为本机视图中事件的名称,该名称指示目标属性发生更改时发出信号。 然后,当本机开关的值发生更改时,系统会通知 Binding 类用户已更改开关值,并且 NativeSwitchPageViewModel.IsSwitchOn 属性值也将更新。

将自变量传递给本机视图

可以使用具有 x:Static 标记扩展的 x:Arguments 特性将构造函数自变量传递给本机视图。 此外,可以通过使用 x:FactoryMethod 特性指定方法的名称,并使用 x:Arguments 特性指定其自变量来调用本机视图工厂方法(public static 方法,返回与定义方法的类或结构相同类型的对象或值)。

下面的代码示例演示了这两种技术:

<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 工厂方法用于在 iOS 上将 UILabel.Font 属性设置为新的 UIFontUIFont 名称和大小是由属于 x:Arguments 特性子级的方法自变量指定的。

Typeface.Create 工厂方法用于在 Android 上将 TextView.Typeface 属性设置为新的 TypefaceTypeface 系列名称和样式是由属于 x:Arguments 特性子级的方法自变量指定的。

FontFamily 构造函数用于在通用 Windows 平台 (UWP) 上将 TextBlock.FontFamily 属性设置为新的 FontFamilyFontFamily 名称是由属于 x:Arguments 特性子级的方法自变量指定的。

注意

自变量必须与构造函数或工厂方法所需的类型匹配。

以下屏幕截图显示了指定工厂方法和构造函数自变量以在不同本机视图上设置字体的结果:

Setting Fonts on Native Views

有关在 XAML 中传递自变量的详细信息,请参阅在 XAML 中传递自变量

从代码引用本机视图

虽然无法使用 x:Name 特性命名本机视图,但可以从共享访问项目中的代码隐藏文件中检索 XAML 文件中声明的本机视图实例,前提是该本机视图是指定 x:Name 特性值的 ContentView 的子级。 然后,在代码隐藏文件中的条件编译指令内,应:

  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>

在上面的示例中,每个平台的本机视图都是 ContentView 控件的子级,x:Name 特性值用于检索代码隐藏中的 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 事件处理程序,该处理程序将缩放和旋转 ContentView 中包含的名为 contentViewTextParent 的本机控件。 以下屏幕截图演示了每个平台上发生的情况:

ContentView Containing a Native Control

子类本机视图

许多 iOS 和 Android 本机视图不适合在 XAML 中实例化,因为它们使用方法而不是属性来设置控件。 此问题的解决方案是在包装器中对本机视图进行子类化,该包装器定义了一个更适合 XAML 的 API,该 API 使用属性来设置控件,并使用平台无关事件。 然后,包装的本机视图可以放置在共享资产项目 (SAP) 中,并用条件编译指令包围,或者放置在特定于平台的项目中,并在 .NET Standard 库项目中从 XAML 引用。

以下代码示例演示了使用子类化本机视图的 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 集合来显示水果集合。 这允许用户选取水果,如以下屏幕截图所示:

Subclassed Native Views

在 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 类公开 ItemsSourceSelectedItem 属性以及 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 类通过 Items 属性为 MyUIPickerView 类提供基础存储。 每当 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 类公开 ItemsSourceSelectedObject 属性以及 ItemSelected 事件。 MySpinner 类显示的项由与视图关联的 Adapter 提供,当首次设置 ItemsSource 属性时,项将填充到 Adapter 中。 每当 MySpinner 类中的选定项发生更改时,OnBindableSpinnerItemSelected 事件处理程序将更新 SelectedObject 属性。