Exibições nativas em XAML
Exibições nativas do iOS, Android e do Plataforma Universal do Windows podem ser referenciadas diretamente de Xamarin.Forms arquivos XAML. Propriedades e manipuladores de eventos podem ser definidos em exibições nativas e podem interagir com Xamarin.Forms exibições. Este artigo demonstra como consumir exibições nativas de Xamarin.Forms arquivos XAML.
Para inserir uma exibição nativa em um Xamarin.Forms arquivo XAML:
- Adicione uma declaração
xmlns
de namespace no arquivo XAML para o namespace que contém a exibição nativa. - Crie uma instância do modo de exibição nativo no arquivo XAML.
Importante
O XAML compilado deve ser desabilitado para qualquer página XAML que use exibições nativas. Isso pode ser feito decorando a classe code-behind para sua página XAML com o [XamlCompilation(XamlCompilationOptions.Skip)]
atributo . Para obter mais informações sobre a compilação XAML, consulte Compilação XAML em Xamarin.Forms.
Para fazer referência a uma exibição nativa de um arquivo code-behind, você deve usar um PROJETO de Ativo Compartilhado (SAP) e encapsular o código específico da plataforma com diretivas de compilação condicional. Para obter mais informações, consulte Consulte exibições nativas do código.
Consumir exibições nativas
O exemplo de código a seguir demonstra o consumo de exibições nativas para cada plataforma para um 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>
Além de especificar o clr-namespace
e assembly
para um namespace de exibição nativo, um targetPlatform
também deve ser especificado. Isso deve ser definido como iOS
, Android
, UWP
, Windows
(que é equivalente a UWP
), macOS
, GTK
, Tizen
ou WPF
. Em runtime, o analisador XAML ignorará quaisquer prefixos de namespace XML que tenham um targetPlatform
que não corresponda à plataforma na qual o aplicativo está em execução.
Cada declaração de namespace pode ser usada para fazer referência a qualquer classe ou estrutura do namespace especificado. Por exemplo, a declaração de ios
namespace pode ser usada para fazer referência a qualquer classe ou estrutura do namespace do iOS UIKit
. As propriedades do modo de exibição nativo podem ser definidas por meio de XAML, mas os tipos de propriedade e objeto devem corresponder. Por exemplo, a UILabel.TextColor
propriedade é definida como UIColor.Red
usando a x:Static
extensão de marcação e o ios
namespace .
Propriedades associáveis e propriedades associáveis anexadas também podem ser definidas em exibições nativas usando a Class.BindableProperty="value"
sintaxe . Cada exibição nativa é encapsulada em uma instância específica NativeViewWrapper
da plataforma, que deriva da Xamarin.Forms.View
classe . Definir uma propriedade associável ou uma propriedade associável anexada em uma exibição nativa transfere o valor da propriedade para o wrapper. Por exemplo, um layout horizontal centralizado pode ser especificado definindo na exibição View.HorizontalOptions="Center"
nativa.
Observação
Observe que os estilos não podem ser usados com exibições nativas, pois os estilos só podem direcionar propriedades que são apoiadas por BindableProperty
objetos.
Os construtores de widget do Android geralmente exigem o objeto Android Context
como um argumento, e isso pode ser disponibilizado por meio de uma propriedade estática na MainActivity
classe . Portanto, ao criar um widget android em XAML, o Context
objeto geralmente deve ser passado para o construtor do widget usando o x:Arguments
atributo com uma x:Static
extensão de marcação. Para obter mais informações, consulte Passar argumentos para exibições nativas.
Observação
Observe que nomear um modo de exibição nativo com x:Name
não é possível em um projeto de biblioteca do .NET Standard ou em um PROJETO de Ativo Compartilhado (SAP). Isso gerará uma variável do tipo nativo, o que causará um erro de compilação. No entanto, as exibições nativas podem ser encapsuladas em ContentView
instâncias e recuperadas no arquivo code-behind, desde que um SAP esteja sendo usado. Para obter mais informações, consulte Consulte o modo de exibição nativo do código.
Associações nativas
A associação de dados é usada para sincronizar uma interface do usuário com sua fonte de dados e simplifica como um Xamarin.Forms aplicativo exibe e interage com seus dados. Desde que o objeto de origem implemente a INotifyPropertyChanged
interface , as alterações no objeto de origem são enviadas automaticamente para o objeto de destino pela estrutura de associação e as alterações no objeto de destino podem, opcionalmente, ser enviadas por push para o objeto de origem .
Propriedades de exibições nativas também podem usar associação de dados. O exemplo de código a seguir demonstra a associação de dados usando propriedades de exibições 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>
A página contém um Entry
cuja IsEnabled
propriedade é associada à NativeSwitchPageViewModel.IsSwitchOn
propriedade . O BindingContext
da página é definido como uma nova instância da NativeSwitchPageViewModel
classe no arquivo code-behind, com a classe ViewModel implementando a INotifyPropertyChanged
interface .
A página também contém um comutador nativo para cada plataforma. Cada comutador nativo usa uma TwoWay
associação para atualizar o valor da NativeSwitchPageViewModel.IsSwitchOn
propriedade. Portanto, quando a opção está desativada, o Entry
é desabilitado e, quando a opção está ativada, o Entry
é habilitado. As capturas de tela a seguir mostram essa funcionalidade em cada plataforma:
As associações bidirecionais têm suporte automático, desde que a propriedade nativa implemente INotifyPropertyChanged
ou dê suporte a Key-Value Observing (KVO) no iOS ou seja um DependencyProperty
na UWP. No entanto, muitas exibições nativas não dão suporte à notificação de alteração de propriedade. Para essas exibições, você pode especificar um UpdateSourceEventName
valor de propriedade como parte da expressão de associação. Essa propriedade deve ser definida como o nome de um evento na exibição nativa que sinaliza quando a propriedade de destino foi alterada. Em seguida, quando o valor do comutador nativo é alterado, a Binding
classe é notificada de que o usuário alterou o valor da opção e o valor da NativeSwitchPageViewModel.IsSwitchOn
propriedade é atualizado.
Passar argumentos para exibições nativas
Os argumentos do construtor podem ser passados para exibições nativas usando o x:Arguments
atributo com uma x:Static
extensão de marcação. Além disso, métodos nativos de fábrica de exibição (public static
métodos que retornam objetos ou valores do mesmo tipo que a classe ou estrutura que define os métodos) podem ser chamados especificando o nome do método usando o x:FactoryMethod
atributo e seus argumentos usando o x:Arguments
atributo .
O exemplo de código a seguir demonstra ambas as 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>
O UIFont.FromName
método de fábrica é usado para definir a UILabel.Font
propriedade como uma nova UIFont
no iOS. O UIFont
nome e o tamanho são especificados pelos argumentos de método que são filhos do x:Arguments
atributo .
O Typeface.Create
método de fábrica é usado para definir a TextView.Typeface
propriedade como uma nova Typeface
no Android. O Typeface
nome e o estilo da família são especificados pelos argumentos de método que são filhos do x:Arguments
atributo.
O FontFamily
construtor é usado para definir a TextBlock.FontFamily
propriedade como uma nova FontFamily
no Plataforma Universal do Windows (UWP). O FontFamily
nome é especificado pelo argumento de método que é um filho do x:Arguments
atributo .
Observação
Os argumentos devem corresponder aos tipos exigidos pelo construtor ou pelo método de fábrica.
As capturas de tela a seguir mostram o resultado da especificação do método de fábrica e dos argumentos do construtor para definir a fonte em diferentes exibições nativas:
Para obter mais informações sobre como passar argumentos em XAML, consulte Passando argumentos em XAML.
Consulte exibições nativas do código
Embora não seja possível nomear uma exibição nativa com o x:Name
atributo , é possível recuperar uma instância de exibição nativa declarada em um arquivo XAML de seu arquivo code-behind em um Projeto de Acesso Compartilhado, desde que o modo de exibição nativo seja um filho de um ContentView
que especifica um x:Name
valor de atributo. Em seguida, dentro de diretivas de compilação condicional no arquivo code-behind, você deve:
- Recupere o valor da propriedade e converta-o
ContentView.Content
em um tipo específicoNativeViewWrapper
da plataforma. - Recupere a
NativeViewWrapper.NativeElement
propriedade e converta-a para o tipo de exibição nativo.
A API nativa pode ser invocada na exibição nativa para executar as operações desejadas. Essa abordagem também oferece o benefício de que várias exibições nativas de XAML para diferentes plataformas podem ser filhos do mesmo ContentView
. O exemplo de código a seguir demonstra essa 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>
No exemplo acima, as exibições nativas de cada plataforma são filhos de controles, com o valor do ContentView
x:Name
atributo sendo usado para recuperar o ContentView
no code-behind:
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;
}
}
A ContentView.Content
propriedade é acessada para recuperar a exibição nativa encapsulada como uma instância específica NativeViewWrapper
da plataforma. Em NativeViewWrapper.NativeElement
seguida, a propriedade é acessada para recuperar a exibição nativa como seu tipo nativo. A API da exibição nativa é invocada para executar as operações desejadas.
Os botões nativos do iOS e do Android compartilham o mesmo OnButtonTap
manipulador de eventos, pois cada botão nativo consome um EventHandler
delegado em resposta a um evento de toque. No entanto, o Plataforma Universal do Windows (UWP) usa um separadoRoutedEventHandler
, que, por sua vez, consome o OnButtonTap
manipulador de eventos neste exemplo. Portanto, quando um botão nativo é clicado, o OnButtonTap
manipulador de eventos é executado, que dimensiona e gira o controle nativo contido no ContentView
chamado contentViewTextParent
. As capturas de tela a seguir demonstram isso ocorrendo em cada plataforma:
Exibições nativas de subclasse
Muitas exibições nativas do iOS e do Android não são adequadas para instanciação em XAML porque usam métodos, em vez de propriedades, para configurar o controle. A solução para esse problema é subclasse exibições nativas em wrappers que definem uma API mais amigável a XAML que usa propriedades para configurar o controle e que usa eventos independentes de plataforma. As exibições nativas encapsuladas podem ser colocadas em um PROJETO de Ativo Compartilhado (SAP) e cercadas por diretivas de compilação condicional ou colocadas em projetos específicos da plataforma e referenciadas de XAML em um projeto de biblioteca do .NET Standard.
O exemplo de código a seguir demonstra uma Xamarin.Forms página que consome exibições nativas subclasse:
<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>
A página contém um Label
que exibe a fruta escolhida pelo usuário de um controle nativo. O Label
associa à SubclassedNativeControlsPageViewModel.SelectedFruit
propriedade . O BindingContext
da página é definido como uma nova instância da SubclassedNativeControlsPageViewModel
classe no arquivo code-behind, com a classe ViewModel implementando a INotifyPropertyChanged
interface .
A página também contém um modo de exibição de seletor nativo para cada plataforma. Cada exibição nativa exibe a coleção de frutas associando sua ItemSource
propriedade à SubclassedNativeControlsPageViewModel.Fruits
coleção. Isso permite que o usuário escolha uma fruta, conforme mostrado nas seguintes capturas de tela:
No iOS e no Android, os seletores nativos usam métodos para configurar os controles. Portanto, esses seletores devem ser subclasses para expor propriedades para torná-los amigáveis com XAML. No Plataforma Universal do Windows (UWP), o ComboBox
já é amigável com XAML e, portanto, não requer subclasse.
iOS
A implementação do iOS subclasse a UIPickerView
exibição e expõe propriedades e um evento que pode ser facilmente consumido do 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 { }
}
}
A MyUIPickerView
classe expõe ItemsSource
e SelectedItem
propriedades e um SelectedItemChanged
evento. Um UIPickerView
requer um modelo de UIPickerViewModel
dados subjacente, que é acessado pelas propriedades e pelo MyUIPickerView
evento. O UIPickerViewModel
modelo de dados é fornecido pela PickerModel
classe :
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());
}
}
}
A PickerModel
classe fornece o armazenamento subjacente para a MyUIPickerView
classe , por meio da Items
propriedade . Sempre que o item selecionado for MyUIPickerView
alterado, o Selected
método será executado, o que atualiza o índice selecionado e aciona o ItemChanged
evento. Isso garante que a SelectedItem
propriedade sempre retornará o último item escolhido pelo usuário. Além disso, a PickerModel
classe substitui os métodos usados para configurar a MyUIPickerView
instância.
Android
A implementação do Android subclasse a Spinner
exibição e expõe propriedades e um evento que pode ser facilmente consumido do 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);
}
}
A MySpinner
classe expõe ItemsSource
e SelectedObject
propriedades e um ItemSelected
evento. Os itens exibidos pela MySpinner
classe são fornecidos pelo Adapter
associado à exibição e os itens são preenchidos no Adapter
quando a ItemsSource
propriedade é definida pela primeira vez. Sempre que o item selecionado na MySpinner
classe for alterado, o OnBindableSpinnerItemSelected
manipulador de eventos atualizará a SelectedObject
propriedade .