XAML のネイティブ ビュー
iOS、Android、ユニバーサル Windows プラットフォームのネイティブ ビューは、Xamarin.Forms XAML ファイルから直接参照できます。 プロパティとイベント ハンドラーはネイティブ ビューで設定でき、Xamarin.Forms ビューと連携できます。 この記事では、Xamarin.Forms XAML ファイルからネイティブ ビューを使用する方法を見ていきます。
ネイティブ ビューを Xamarin.Forms XAML ファイルに埋め込むには:
- ネイティブ ビューを含む名前空間の XAML ファイルに、
xmlns
名前空間の宣言を追加します。 - XAML ファイルでネイティブ ビューのインスタンスを作成します。
重要
ネイティブ ビューを使う XAML ページで、コンパイル済み XAML を無効にする必要があります。 これを行うには、XAML ページの分離コード クラスを [XamlCompilation(XamlCompilationOptions.Skip)]
属性で修飾します。 XAML のコンパイルについて詳しくは、「Xamarin.Forms での XAML のコンパイル」をご覧ください。
分離コード ファイルからネイティブ ビューを参照するには、共有アセット プロジェクト (SAP) を使い、プラットフォーム固有のコードを条件付きコンパイル ディレクティブでラップする必要があります。 詳しくは、「コードからネイティブ ビューを参照する」をご覧ください。
ネイティブ ビューを使用する
次のコード例では、Xamarin.Forms の ContentPage
に各プラットフォームのネイティブ ビューを使用する方法を見ていきます。
<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-namespace
と assembly
を指定するだけでなく、targetPlatform
も指定する必要があります。 これは、iOS
、Android
、UWP
、Windows
(UWP
と同等)、macOS
、GTK
、Tizen
、または WPF
に設定する必要があります。 実行時に、XAML パーサーは、アプリケーションが実行されているプラットフォームと一致しない targetPlatform
を持つ XML 名前空間プレフィックスを無視します。
各名前空間宣言を使って、指定した名前空間の任意のクラスまたは構造体を参照できます。 たとえば、ios
名前空間宣言を使って、iOS の UIKit
名前空間の任意のクラスまたは構造体を参照できます。 ネイティブ ビューのプロパティは XAML を使って設定できますが、プロパティとオブジェクトの型が一致している必要があります。 たとえば、UILabel.TextColor
プロパティは、x:Static
マークアップ拡張と ios
名前空間を使って UIColor.Red
に設定されます。
バインド可能プロパティとアタッチされたバインド可能プロパティは、Class.BindableProperty="value"
構文を使ってネイティブ ビューで設定することもできます。 各ネイティブ ビューは、Xamarin.Forms.View
クラスから派生したプラットフォーム固有の NativeViewWrapper
インスタンスにラップされます。 ネイティブ ビューでバインド可能プロパティまたはアタッチされたバインド可能プロパティを設定すると、プロパティ値がラッパーに転送されます。 たとえば、ネイティブ ビューで View.HorizontalOptions="Center"
を設定して、中央揃えの水平レイアウトを指定できます。
Note
スタイルで対象にできるのは BindableProperty
オブジェクトによってサポートされるプロパティだけなので、ネイティブ ビューではスタイルを使用できないことに注意してください。
Android ウィジェット コンストラクターでは、通常、引数として Android Context
オブジェクトが必要であり、これは MainActivity
クラスの静的プロパティを介して使用できます。 そのため、XAML で Android ウィジェットを作成するときは、通常、x:Arguments
属性と x:Static
マークアップ拡張を使って、Context
オブジェクトをウィジェットのコンストラクターに渡す必要があります。 詳しくは、「ネイティブ ビューに引数を渡す」をご覧ください。
Note
.NET Standard ライブラリ プロジェクトまたは共有アセット プロジェクト (SAP) では、x:Name
を使ってネイティブ ビューの名前を指定できないことに注意してください。 それを行うと、ネイティブ型の変数が生成され、コンパイル エラーが発生します。 ただし、SAP が使われている場合は、ネイティブ ビューを ContentView
インスタンスにラップして、分離コード ファイルで取得できます。 詳しくは、「コードからネイティブ ビューを参照する」をご覧ください。
ネイティブ バインド
データ バインディングは、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
は有効です。 次のスクリーンショットは、各プラットフォームでのこの機能を示したものです。
ネイティブ プロパティが、INotifyPropertyChanged
を実装している場合、または iOS でキー値監視 (KVO) をサポートしている場合、または UWP で DependencyProperty
である場合は、両方向のバインドが自動的にサポートされます。 ただし、多くのネイティブ ビューでは、プロパティ変更通知はサポートされていません。 これらのビューでは、バインド式の一部として UpdateSourceEventName
プロパティの値を指定できます。 このプロパティには、ターゲット プロパティが変化したときに通知するネイティブ ビューのイベントの名前を設定する必要があります。 その後は、ネイティブ スイッチの値が変化すると、ユーザーがスイッチの値を変更したことが Binding
クラスに通知されて、NativeSwitchPageViewModel.IsSwitchOn
プロパティの値が更新されます。
ネイティブ ビューに引数を渡す
x:Arguments
属性と x:Static
マークアップ拡張を使って、コンストラクター引数をネイティブ ビューに渡すことができます。 さらに、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
プロパティを新しい UIFont
に設定するために使われます。 UIFont
の名前とサイズは、x:Arguments
属性の子であるメソッド引数で指定します。
Typeface.Create
ファクトリ メソッドは、Android で、TextView.Typeface
プロパティを新しい Typeface
に設定するために使われます。 Typeface
ファミリの名前とスタイルは、x:Arguments
属性の子であるメソッド引数で指定します。
FontFamily
コンストラクターは、ユニバーサル Windows プラットフォーム (UWP) で、TextBlock.FontFamily
プロパティを新しい FontFamily
に設定するために使われます。 FontFamily
の名前は、x:Arguments
属性の子であるメソッド引数で指定します。
Note
引数は、コンストラクターまたはファクトリ メソッドで必要な型と一致する必要があります。
次のスクリーンショットは、ファクトリ メソッドとコンストラクターの引数を指定して、異なるネイティブ ビューでフォントを設定した結果を示したものです。
XAML で引数を渡す方法について詳しくは、「XAML での引数の受け渡し」をご覧ください。
コードからネイティブ ビューを参照する
x:Name
属性でネイティブ ビューの名前を指定することはできませんが、ネイティブ ビューが x:Name
属性値を指定する ContentView
の子である場合は、XAML ファイルで宣言されているネイティブ ビュー インスタンスを、共有アクセス プロジェクトの分離コード ファイルから取得できます。 その後、分離コード ファイルの条件付きコンパイル ディレクティブ内で、次のようにする必要があります。
ContentView.Content
プロパティの値を取得して、プラットフォーム固有のNativeViewWrapper
型にキャストします。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
コントロールの子であり、コードビハインドで ContentView
を取得するには x:Name
属性の値が使われています。
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 を呼び出して、必要な操作を実行します。
各ネイティブ ボタンはタッチ イベントへの応答で EventHandler
デリゲートを使うため、iOS と Android のネイティブ ボタンは同じ OnButtonTap
イベント ハンドラーを共有します。 一方、ユニバーサル Windows プラットフォーム (UWP) は別の RoutedEventHandler
を使い、それによって、この例では、OnButtonTap
イベント ハンドラーが使われます。 したがって、ネイティブ ボタンがクリックされると、OnButtonTap
イベント ハンドラーが実行して、contentViewTextParent
という名前の ContentView
に含まれるネイティブ コントロールの拡大縮小と回転を行います。 次のスクリーンショットは、各プラットフォームでこれが行われているのを示したものです。
ネイティブ ビューをサブクラス化する
iOS と Android の多くのネイティブ ビューは、コントロールを設定するためにプロパティではなくメソッドを使っているため、XAML でのインスタンス化に適していません。 この問題の解決策は、プロパティを使ってコントロールを設定し、プラットフォームに依存しないイベントを使用する、いっそう XAML で使いやすい 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
インターフェイスを実装しています。
このページには、各プラットフォームのネイティブ ピッカー ビューも含まれています。 各ネイティブ ビューでは、SubclassedNativeControlsPageViewModel.Fruits
コレクションにバインドされた ItemSource
プロパティを使って、果物のコレクションを表示します。 これにより、次のスクリーンショットで示すように、ユーザーは果物を選ぶことができます。
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
クラスは、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
クラスは、ItemsSource
と SelectedObject
プロパティ、および ItemSelected
イベントを公開します。 MySpinner
クラスによって表示される項目は、ビューに関連付けられた Adapter
によって提供され、ItemsSource
プロパティが最初に設定されたときに、項目が Adapter
に設定されます。 MySpinner
クラスで選択項目が変わるたびに、OnBindableSpinnerItemSelected
イベント ハンドラーによって SelectedObject
プロパティが更新されます。