Vistas nativas en XAML
Se puede hacer referencia directamente a las vistas nativas de iOS, Android y el Plataforma universal de Windows desde Xamarin.Forms archivos XAML. Las propiedades y los controladores de eventos se pueden establecer en vistas nativas y pueden interactuar con Xamarin.Forms las vistas. En este artículo se muestra cómo consumir vistas nativas de Xamarin.Forms archivos XAML.
Para insertar una vista nativa en un Xamarin.Forms archivo XAML:
- Agregue una
xmlns
declaración de espacio de nombres en el archivo XAML para el espacio de nombres que contiene la vista nativa. - Cree una instancia de la vista nativa en el archivo XAML.
Importante
Xaml compilado debe estar deshabilitado para las páginas XAML que usen vistas nativas. Esto se puede lograr mediante la decoración de la clase de código subyacente para la página XAML con el [XamlCompilation(XamlCompilationOptions.Skip)]
atributo . Para obtener más información sobre la compilación XAML, consulta Compilación XAML en Xamarin.Forms.
Para hacer referencia a una vista nativa desde un archivo de código subyacente, debe usar un proyecto de recursos compartidos (SAP) y encapsular el código específico de la plataforma con directivas de compilación condicional. Para obtener más información, consulte Referencia a vistas nativas desde el código.
Consumo de vistas nativas
En el ejemplo de código siguiente se muestra cómo consumir vistas nativas para cada plataforma en :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>
Además de especificar y clr-namespace
assembly
para un espacio de nombres de vista nativo, targetPlatform
también se debe especificar . Debe establecerse iOS
en , Android
, UWP
, Windows
(que es equivalente a UWP
), macOS
, GTK
, Tizen
o WPF
. En tiempo de ejecución, el analizador XAML omitirá los prefijos de espacio de nombres XML que tengan un targetPlatform
que no coincida con la plataforma en la que se ejecuta la aplicación.
Cada declaración de espacio de nombres se puede usar para hacer referencia a cualquier clase o estructura del espacio de nombres especificado. Por ejemplo, la ios
declaración de espacio de nombres se puede usar para hacer referencia a cualquier clase o estructura del espacio de nombres de iOS UIKit
. Las propiedades de la vista nativa se pueden establecer a través de XAML, pero los tipos de propiedad y objeto deben coincidir. Por ejemplo, la UILabel.TextColor
propiedad se establece en UIColor.Red
mediante la x:Static
extensión de marcado y el ios
espacio de nombres .
Las propiedades enlazables y las propiedades enlazables adjuntas también se pueden establecer en vistas nativas mediante la Class.BindableProperty="value"
sintaxis . Cada vista nativa se ajusta en una instancia específica NativeViewWrapper
de la plataforma, que se deriva de la Xamarin.Forms.View
clase . Establecer una propiedad enlazable o una propiedad enlazable adjunta en una vista nativa transfiere el valor de propiedad al contenedor. Por ejemplo, se puede especificar un diseño horizontal centrado estableciendo View.HorizontalOptions="Center"
en la vista nativa.
Nota
Tenga en cuenta que los estilos no se pueden usar con vistas nativas, ya que los estilos solo pueden tener como destino propiedades respaldadas por BindableProperty
objetos .
Por lo general, los constructores de widgets de Android requieren el objeto Android Context
como argumento y esto se puede poner a disposición a través de una propiedad estática en la MainActivity
clase . Por lo tanto, al crear un widget de Android en XAML, el Context
objeto normalmente se debe pasar al constructor del widget mediante el x:Arguments
atributo con una x:Static
extensión de marcado. Para obtener más información, vea Pasar argumentos a vistas nativas.
Nota
Tenga en cuenta que no es posible asignar un nombre a una vista nativa con x:Name
en un proyecto de biblioteca de .NET Standard o en un proyecto de recursos compartidos (SAP). Al hacerlo, se generará una variable del tipo nativo, lo que provocará un error de compilación. Sin embargo, las vistas nativas se pueden encapsular en ContentView
instancias y recuperarse en el archivo de código subyacente, siempre que se use una instancia de SAP. Para obtener más información, consulte Referencia a la vista nativa desde el código.
Enlaces nativos
El enlace de datos se usa para sincronizar una interfaz de usuario con su origen de datos y simplifica la forma en que una Xamarin.Forms aplicación muestra e interactúa con sus datos. Siempre que el objeto de origen implemente la INotifyPropertyChanged
interfaz, los cambios en el objeto de origen se insertan automáticamente en el objeto de destino mediante el marco de enlace y los cambios en el objeto de destino se pueden insertar opcionalmente en el objeto de origen.
Las propiedades de las vistas nativas también pueden usar el enlace de datos. En el ejemplo de código siguiente se muestra el enlace de datos mediante propiedades de vistas nativas:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:ios="clr-namespace:UIKit;assembly=Xamarin.iOS;targetPlatform=iOS"
xmlns:androidWidget="clr-namespace:Android.Widget;assembly=Mono.Android;targetPlatform=Android"
xmlns:androidLocal="clr-namespace:SimpleColorPicker.Droid;assembly=SimpleColorPicker.Droid;targetPlatform=Android"
xmlns:win="clr-namespace:Windows.UI.Xaml.Controls;assembly=Windows, Version=255.255.255.255,
Culture=neutral, PublicKeyToken=null, ContentType=WindowsRuntime;targetPlatform=Windows"
xmlns:local="clr-namespace:NativeSwitch"
x:Class="NativeSwitch.NativeSwitchPage">
<StackLayout Margin="20">
<Label Text="Native Views Demo" FontAttributes="Bold" HorizontalOptions="Center" />
<Entry Placeholder="This Entry is bound to the native switch" IsEnabled="{Binding IsSwitchOn}" />
<ios:UISwitch On="{Binding Path=IsSwitchOn, Mode=TwoWay, UpdateSourceEventName=ValueChanged}"
OnTintColor="{x:Static ios:UIColor.Red}"
ThumbTintColor="{x:Static ios:UIColor.Blue}" />
<androidWidget:Switch x:Arguments="{x:Static androidLocal:MainActivity.Instance}"
Checked="{Binding Path=IsSwitchOn, Mode=TwoWay, UpdateSourceEventName=CheckedChange}"
Text="Enable Entry?" />
<win:ToggleSwitch Header="Enable Entry?"
OffContent="No"
OnContent="Yes"
IsOn="{Binding IsSwitchOn, Mode=TwoWay, UpdateSourceEventName=Toggled}" />
</StackLayout>
</ContentPage>
La página contiene una Entry
cuya IsEnabled
propiedad se enlaza a la NativeSwitchPageViewModel.IsSwitchOn
propiedad . La BindingContext
propiedad de la página se establece en una nueva instancia de la NativeSwitchPageViewModel
clase en el archivo de código subyacente, con la clase ViewModel que implementa la INotifyPropertyChanged
interfaz .
La página también contiene un modificador nativo para cada plataforma. Cada conmutador nativo usa un TwoWay
enlace para actualizar el valor de la NativeSwitchPageViewModel.IsSwitchOn
propiedad. Por lo tanto, cuando el conmutador está desactivado, Entry
está deshabilitado y, cuando el conmutador está activado, Entry
está habilitado. En las capturas de pantalla siguientes se muestra esta funcionalidad en cada plataforma:
Los enlaces bidireccionales se admiten automáticamente siempre que la propiedad nativa implemente INotifyPropertyChanged
o admita Key-Value Observación (KVO) en iOS o sea en DependencyProperty
UWP. Sin embargo, muchas vistas nativas no admiten la notificación de cambio de propiedad. Para estas vistas, puede especificar un UpdateSourceEventName
valor de propiedad como parte de la expresión de enlace. Esta propiedad debe establecerse en el nombre de un evento en la vista nativa que indica cuándo ha cambiado la propiedad de destino. A continuación, cuando cambia el valor del modificador nativo, se notifica a la Binding
clase que el usuario ha cambiado el valor del modificador y se actualiza el valor de la NativeSwitchPageViewModel.IsSwitchOn
propiedad.
Pasar argumentos a vistas nativas
Los argumentos del constructor se pueden pasar a vistas nativas mediante el x:Arguments
atributo con una x:Static
extensión de marcado. Además, se puede llamar a los métodos de generador de vistas nativas (public static
métodos que devuelven objetos o valores del mismo tipo que la clase o estructura que define los métodos) especificando el nombre del método mediante el x:FactoryMethod
atributo y sus argumentos mediante el x:Arguments
atributo .
En el ejemplo de código siguiente se muestran ambas técnicas:
<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>
El UIFont.FromName
método factory se usa para establecer la UILabel.Font
propiedad en un nuevo UIFont
en iOS. El UIFont
nombre y el tamaño se especifican mediante los argumentos del método que son elementos secundarios del x:Arguments
atributo .
El Typeface.Create
método factory se usa para establecer la TextView.Typeface
propiedad en un nuevo Typeface
en Android. El Typeface
nombre de familia y el estilo se especifican mediante los argumentos del método que son elementos secundarios del x:Arguments
atributo.
El FontFamily
constructor se usa para establecer la TextBlock.FontFamily
propiedad en un nuevo FontFamily
en el Plataforma universal de Windows (UWP). El FontFamily
nombre se especifica mediante el argumento de método que es un elemento secundario del x:Arguments
atributo .
Nota
Los argumentos deben coincidir con los tipos requeridos por el constructor o el método factory.
En las capturas de pantalla siguientes se muestra el resultado de especificar argumentos de constructor y método de fábrica para establecer la fuente en diferentes vistas nativas:
Para obtener más información sobre cómo pasar argumentos en XAML, consulta Pasar argumentos en XAML.
Consulte las vistas nativas desde el código.
Aunque no es posible asignar un nombre a una vista nativa con el x:Name
atributo , es posible recuperar una instancia de vista nativa declarada en un archivo XAML de su archivo de código subyacente en un proyecto de acceso compartido, siempre que la vista nativa sea un elemento secundario de un ContentView
que especifique un x:Name
valor de atributo. A continuación, dentro de las directivas de compilación condicional en el archivo de código subyacente debe:
- Recupere el valor de la
ContentView.Content
propiedad y consértelo en un tipo específicoNativeViewWrapper
de la plataforma. - Recupere la
NativeViewWrapper.NativeElement
propiedad y consértala al tipo de vista nativa.
A continuación, se puede invocar la API nativa en la vista nativa para realizar las operaciones deseadas. Este enfoque también ofrece la ventaja de que varias vistas nativas xaml para distintas plataformas pueden ser elementos secundarios del mismo ContentView
. En el ejemplo de código siguiente se muestra esta técnica:
<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>
En el ejemplo anterior, las vistas nativas de cada plataforma son elementos secundarios de controles, con el valor de ContentView
x:Name
atributo que se usa para recuperar en ContentView
el código subyacente:
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;
}
}
Se ContentView.Content
tiene acceso a la propiedad para recuperar la vista nativa ajustada como una instancia específica NativeViewWrapper
de la plataforma. A NativeViewWrapper.NativeElement
continuación, se obtiene acceso a la propiedad para recuperar la vista nativa como su tipo nativo. A continuación, se invoca la API de la vista nativa para realizar las operaciones deseadas.
Los botones nativos de iOS y Android comparten el mismo OnButtonTap
controlador de eventos, ya que cada botón nativo consume un EventHandler
delegado en respuesta a un evento táctil. Sin embargo, el Plataforma universal de Windows (UWP) usa un elemento independiente RoutedEventHandler
, que a su vez consume el OnButtonTap
controlador de eventos en este ejemplo. Por lo tanto, cuando se hace clic en un botón nativo, se ejecuta el OnButtonTap
controlador de eventos, que escala y gira el control nativo contenido en el ContentView
denominado contentViewTextParent
. Las capturas de pantalla siguientes muestran que esto se produce en cada plataforma:
Vistas nativas de subclase
Muchas vistas nativas de iOS y Android no son adecuadas para crear instancias en XAML porque usan métodos, en lugar de propiedades, para configurar el control. La solución a este problema consiste en subclases de vistas nativas en contenedores que definen una API más fácil de usar XAML que usa propiedades para configurar el control y que usa eventos independientes de la plataforma. Las vistas nativas ajustadas se pueden colocar en un proyecto de recursos compartidos (SAP) y estar rodeadas de directivas de compilación condicional, o colocarse en proyectos específicos de la plataforma y hacer referencia a ellos desde XAML en un proyecto de biblioteca de .NET Standard.
En el ejemplo de código siguiente se muestra una Xamarin.Forms página que consume vistas nativas con subclases:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:ios="clr-namespace:UIKit;assembly=Xamarin.iOS;targetPlatform=iOS"
xmlns:iosLocal="clr-namespace:SubclassedNativeControls.iOS;assembly=SubclassedNativeControls.iOS;targetPlatform=iOS"
xmlns:android="clr-namespace:Android.Widget;assembly=Mono.Android;targetPlatform=Android"
xmlns:androidLocal="clr-namespace:SimpleColorPicker.Droid;assembly=SimpleColorPicker.Droid;targetPlatform=Android"
xmlns:androidLocal="clr-namespace:SubclassedNativeControls.Droid;assembly=SubclassedNativeControls.Droid;targetPlatform=Android"
xmlns:winControls="clr-namespace:Windows.UI.Xaml.Controls;assembly=Windows, Version=255.255.255.255,
Culture=neutral, PublicKeyToken=null, ContentType=WindowsRuntime;targetPlatform=Windows"
xmlns:local="clr-namespace:SubclassedNativeControls"
x:Class="SubclassedNativeControls.SubclassedNativeControlsPage">
<StackLayout Margin="20">
<Label Text="Subclassed Native Views Demo" FontAttributes="Bold" HorizontalOptions="Center" />
<StackLayout Orientation="Horizontal">
<Label Text="You have chosen:" />
<Label Text="{Binding SelectedFruit}" />
</StackLayout>
<iosLocal:MyUIPickerView ItemsSource="{Binding Fruits}"
SelectedItem="{Binding SelectedFruit, Mode=TwoWay, UpdateSourceEventName=SelectedItemChanged}" />
<androidLocal:MySpinner x:Arguments="{x:Static androidLocal:MainActivity.Instance}"
ItemsSource="{Binding Fruits}"
SelectedObject="{Binding SelectedFruit, Mode=TwoWay, UpdateSourceEventName=ItemSelected}" />
<winControls:ComboBox ItemsSource="{Binding Fruits}"
SelectedItem="{Binding SelectedFruit, Mode=TwoWay, UpdateSourceEventName=SelectionChanged}" />
</StackLayout>
</ContentPage>
La página contiene un Label
objeto que muestra la fruta elegida por el usuario desde un control nativo. Enlaza Label
a la SubclassedNativeControlsPageViewModel.SelectedFruit
propiedad . La BindingContext
propiedad de la página se establece en una nueva instancia de la SubclassedNativeControlsPageViewModel
clase en el archivo de código subyacente, con la clase ViewModel que implementa la INotifyPropertyChanged
interfaz .
La página también contiene una vista de selector nativa para cada plataforma. Cada vista nativa muestra la colección de frutas enlazando su ItemSource
propiedad a la SubclassedNativeControlsPageViewModel.Fruits
colección. Esto permite al usuario elegir una fruta, como se muestra en las capturas de pantalla siguientes:
En iOS y Android, los selectores nativos usan métodos para configurar los controles. Por lo tanto, estos selectores deben estar subclases para exponer propiedades para que sean compatibles con XAML. En el Plataforma universal de Windows (UWP), ya ComboBox
es compatible con XAML, por lo que no requiere subclases.
iOS
La implementación de iOS subclase la UIPickerView
vista y expone propiedades y un evento que se puede consumir fácilmente desde XAML:
public class MyUIPickerView : UIPickerView
{
public event EventHandler<EventArgs> SelectedItemChanged;
public MyUIPickerView()
{
var model = new PickerModel();
model.ItemChanged += (sender, e) =>
{
if (SelectedItemChanged != null)
{
SelectedItemChanged.Invoke(this, e);
}
};
Model = model;
}
public IList<string> ItemsSource
{
get
{
var pickerModel = Model as PickerModel;
return (pickerModel != null) ? pickerModel.Items : null;
}
set
{
var model = Model as PickerModel;
if (model != null)
{
model.Items = value;
}
}
}
public string SelectedItem
{
get { return (Model as PickerModel).SelectedItem; }
set { }
}
}
La MyUIPickerView
clase expone ItemsSource
las propiedades y SelectedItem
, y un SelectedItemChanged
evento . Un UIPickerView
requiere un modelo de datos subyacente UIPickerViewModel
, al que acceden las propiedades y el MyUIPickerView
evento . La UIPickerViewModel
clase proporciona el PickerModel
modelo de datos:
class PickerModel : UIPickerViewModel
{
int selectedIndex = 0;
public event EventHandler<EventArgs> ItemChanged;
public IList<string> Items { get; set; }
public string SelectedItem
{
get
{
return Items != null && selectedIndex >= 0 && selectedIndex < Items.Count ? Items[selectedIndex] : null;
}
}
public override nint GetRowsInComponent(UIPickerView pickerView, nint component)
{
return Items != null ? Items.Count : 0;
}
public override string GetTitle(UIPickerView pickerView, nint row, nint component)
{
return Items != null && Items.Count > row ? Items[(int)row] : null;
}
public override nint GetComponentCount(UIPickerView pickerView)
{
return 1;
}
public override void Selected(UIPickerView pickerView, nint row, nint component)
{
selectedIndex = (int)row;
if (ItemChanged != null)
{
ItemChanged.Invoke(this, new EventArgs());
}
}
}
La PickerModel
clase proporciona el almacenamiento subyacente para la MyUIPickerView
clase , a través de la Items
propiedad . Cada vez que cambia el elemento seleccionado, MyUIPickerView
se ejecuta el Selected
método , que actualiza el índice seleccionado y desencadena el ItemChanged
evento. Esto garantiza que la SelectedItem
propiedad siempre devolverá el último elemento seleccionado por el usuario. Además, la PickerModel
clase invalida los métodos que se usan para configurar la MyUIPickerView
instancia.
Android
La implementación de Android subclase la Spinner
vista y expone propiedades y un evento que se puede consumir fácilmente desde XAML:
class MySpinner : Spinner
{
ArrayAdapter adapter;
IList<string> items;
public IList<string> ItemsSource
{
get { return items; }
set
{
if (items != value)
{
items = value;
adapter.Clear();
foreach (string str in items)
{
adapter.Add(str);
}
}
}
}
public string SelectedObject
{
get { return (string)GetItemAtPosition(SelectedItemPosition); }
set
{
if (items != null)
{
int index = items.IndexOf(value);
if (index != -1)
{
SetSelection(index);
}
}
}
}
public MySpinner(Context context) : base(context)
{
ItemSelected += OnBindableSpinnerItemSelected;
adapter = new ArrayAdapter(context, Android.Resource.Layout.SimpleSpinnerItem);
adapter.SetDropDownViewResource(Android.Resource.Layout.SimpleSpinnerDropDownItem);
Adapter = adapter;
}
void OnBindableSpinnerItemSelected(object sender, ItemSelectedEventArgs args)
{
SelectedObject = (string)GetItemAtPosition(args.Position);
}
}
La MySpinner
clase expone ItemsSource
las propiedades y SelectedObject
, y un ItemSelected
evento . Los elementos mostrados por la MySpinner
clase se proporcionan mediante el Adapter
asociado a la vista y los elementos se rellenan en cuando Adapter
se establece la ItemsSource
propiedad por primera vez. Cada vez que cambia el elemento seleccionado de la MySpinner
clase, el OnBindableSpinnerItemSelected
controlador de eventos actualiza la SelectedObject
propiedad .