Xamarin.Forms
Tworzenie niestandardowych elementów sterujących
Szukaj…
Wprowadzenie
Każdy widok Xamarin.Forms
ma towarzyszący mechanizm renderujący dla każdej platformy, który tworzy instancję kontroli rodzimej. Gdy widok jest renderowany na określonej platformie, ViewRenderer
jest instancja klasy ViewRenderer
.
Proces wykonania tego jest następujący:
Utwórz niestandardowy formant Xamarin.Forms.
Skorzystaj z niestandardowego formantu z Xamarin.Forms.
Utwórz niestandardowy moduł renderujący dla formantu na każdej platformie.
Wdrożenie kontroli CheckBox
W tym przykładzie zaimplementujemy niestandardowe pole wyboru dla Androida i iOS.
Tworzenie kontroli niestandardowej
namespace CheckBoxCustomRendererExample
{
public class Checkbox : View
{
public static readonly BindableProperty IsCheckedProperty = BindableProperty.Create<Checkbox, bool>(p => p.IsChecked, true, propertyChanged: (s, o, n) => { (s as Checkbox).OnChecked(new EventArgs()); });
public static readonly BindableProperty ColorProperty = BindableProperty.Create<Checkbox, Color>(p => p.Color, Color.Default);
public bool IsChecked
{
get
{
return (bool)GetValue(IsCheckedProperty);
}
set
{
SetValue(IsCheckedProperty, value);
}
}
public Color Color
{
get
{
return (Color)GetValue(ColorProperty);
}
set
{
SetValue(ColorProperty, value);
}
}
public event EventHandler Checked;
protected virtual void OnChecked(EventArgs e)
{
if (Checked != null)
Checked(this, e);
}
}
}
Zaczniemy od niestandardowego renderowania Androida, tworząc nową klasę ( CheckboxCustomRenderer
) w części naszego rozwiązania dotyczącej Android
.
Kilka ważnych szczegółów, na które należy zwrócić uwagę:
- Musimy oznaczyć najwyższą klasę za pomocą atrybutu ExportRenderer, aby renderer został zarejestrowany w
Xamarin.Forms
. W ten sposóbXamarin.Forms
użyje tego mechanizmu renderującego, gdy będzie próbował utworzyć nasz obiektCheckbox
naAndroid
. - Większość naszej pracy
OnElementChanged
metodąOnElementChanged
, w której tworzymy instancję i konfigurujemy naszą natywną kontrolę.
Wykorzystanie kontroli niestandardowej
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:local="clr-namespace:CheckBoxCustomRendererExample" x:Class="CheckBoxCustomRendererExample.CheckBoxCustomRendererExamplePage">
<StackLayout Padding="20">
<local:Checkbox Color="Aqua" />
</StackLayout>
</ContentPage>
Tworzenie niestandardowego renderowania na każdej platformie
Proces tworzenia niestandardowej klasy mechanizmu renderującego jest następujący:
- Utwórz podklasę
ViewRenderer<T1,T2>
która renderuje formant niestandardowy. Argumentem pierwszego typu powinna być kontrola niestandardowa, dla której moduł renderujący jest, w tym przypadkuCheckBox
. Argumentem drugiego typu powinna być kontrola rodzima, która zaimplementuje kontrolę niestandardową. - Zastąp metodę
OnElementChanged
, która renderuje formant niestandardowy i pisz logikę, aby go dostosować. Ta metoda jest wywoływana, gdy tworzony jest odpowiedni formantXamarin.Forms
. - Dodaj atrybut
ExportRenderer
do niestandardowej klasy renderera, aby określić, że będzie on używany do renderowania formantu niestandardowegoXamarin.Forms
. Ten atrybut służy do rejestrowania niestandardowego mechanizmu renderującego wXamarin.Forms
.
Tworzenie niestandardowego modułu renderującego dla systemu Android
[assembly: ExportRenderer(typeof(Checkbox), typeof(CheckBoxRenderer))]
namespace CheckBoxCustomRendererExample.Droid
{
public class CheckBoxRenderer : ViewRenderer<Checkbox, CheckBox>
{
private CheckBox checkBox;
protected override void OnElementChanged(ElementChangedEventArgs<Checkbox> e)
{
base.OnElementChanged(e);
var model = e.NewElement;
checkBox = new CheckBox(Context);
checkBox.Tag = this;
CheckboxPropertyChanged(model, null);
checkBox.SetOnClickListener(new ClickListener(model));
SetNativeControl(checkBox);
}
private void CheckboxPropertyChanged(Checkbox model, String propertyName)
{
if (propertyName == null || Checkbox.IsCheckedProperty.PropertyName == propertyName)
{
checkBox.Checked = model.IsChecked;
}
if (propertyName == null || Checkbox.ColorProperty.PropertyName == propertyName)
{
int[][] states = {
new int[] { Android.Resource.Attribute.StateEnabled}, // enabled
new int[] {Android.Resource.Attribute.StateEnabled}, // disabled
new int[] {Android.Resource.Attribute.StateChecked}, // unchecked
new int[] { Android.Resource.Attribute.StatePressed} // pressed
};
var checkBoxColor = (int)model.Color.ToAndroid();
int[] colors = {
checkBoxColor,
checkBoxColor,
checkBoxColor,
checkBoxColor
};
var myList = new Android.Content.Res.ColorStateList(states, colors);
checkBox.ButtonTintList = myList;
}
}
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (checkBox != null)
{
base.OnElementPropertyChanged(sender, e);
CheckboxPropertyChanged((Checkbox)sender, e.PropertyName);
}
}
public class ClickListener : Java.Lang.Object, IOnClickListener
{
private Checkbox _myCheckbox;
public ClickListener(Checkbox myCheckbox)
{
this._myCheckbox = myCheckbox;
}
public void OnClick(global::Android.Views.View v)
{
_myCheckbox.IsChecked = !_myCheckbox.IsChecked;
}
}
}
}
Tworzenie niestandardowego modułu renderującego dla systemu iOS
Ponieważ w iOS nie ma wbudowanego pola wyboru, najpierw utworzymy CheckBoxView
a następnie utworzymy renderer dla naszego pola wyboru Xamarin.Forms.
CheckBoxView
jest oparty na dwóch obrazach zaznaczony_checkbox.png i unchecked_checkbox.png, więc właściwość Color
zostanie zignorowana.
Widok CheckBox:
namespace CheckBoxCustomRendererExample.iOS
{
[Register("CheckBoxView")]
public class CheckBoxView : UIButton
{
public CheckBoxView()
{
Initialize();
}
public CheckBoxView(CGRect bounds)
: base(bounds)
{
Initialize();
}
public string CheckedTitle
{
set
{
SetTitle(value, UIControlState.Selected);
}
}
public string UncheckedTitle
{
set
{
SetTitle(value, UIControlState.Normal);
}
}
public bool Checked
{
set { Selected = value; }
get { return Selected; }
}
void Initialize()
{
ApplyStyle();
TouchUpInside += (sender, args) => Selected = !Selected;
// set default color, because type is not UIButtonType.System
SetTitleColor(UIColor.DarkTextColor, UIControlState.Normal);
SetTitleColor(UIColor.DarkTextColor, UIControlState.Selected);
}
void ApplyStyle()
{
SetImage(UIImage.FromBundle("Images/checked_checkbox.png"), UIControlState.Selected);
SetImage(UIImage.FromBundle("Images/unchecked_checkbox.png"), UIControlState.Normal);
}
}
}
Renderowanie niestandardowe CheckBox:
[assembly: ExportRenderer(typeof(Checkbox), typeof(CheckBoxRenderer))]
namespace CheckBoxCustomRendererExample.iOS
{
public class CheckBoxRenderer : ViewRenderer<Checkbox, CheckBoxView>
{
/// <summary>
/// Handles the Element Changed event
/// </summary>
/// <param name="e">The e.</param>
protected override void OnElementChanged(ElementChangedEventArgs<Checkbox> e)
{
base.OnElementChanged(e);
if (Element == null)
return;
BackgroundColor = Element.BackgroundColor.ToUIColor();
if (e.NewElement != null)
{
if (Control == null)
{
var checkBox = new CheckBoxView(Bounds);
checkBox.TouchUpInside += (s, args) => Element.IsChecked = Control.Checked;
SetNativeControl(checkBox);
}
Control.Checked = e.NewElement.IsChecked;
}
Control.Frame = Frame;
Control.Bounds = Bounds;
}
/// <summary>
/// Handles the <see cref="E:ElementPropertyChanged" /> event.
/// </summary>
/// <param name="sender">The sender.</param>
/// <param name="e">The <see cref="PropertyChangedEventArgs"/> instance containing the event data.</param>
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
{
base.OnElementPropertyChanged(sender, e);
if (e.PropertyName.Equals("Checked"))
{
Control.Checked = Element.IsChecked;
}
}
}
}
Wynik: