Xamarin.Forms lets developers create native user interfaces on iOS, Android and Windows Phone from a single, shared C# codebase. Since the UI is rendered using the native controls of the target platform, it gives you great flexibility in customizing the controls separately on each platform. Each control is rendered differently on each platform using a Renderer class, which in turn creates a native control, arranges it on the screen and adds the behavior specified in the shared code.
Switch Control
Switch is UI control that allows a user to toggle between two states. While on iOS and Windows Phone platforms this control is not labeled, on Android it contains text fields within, defaulted to OFF and ON. Because Xamarin tends to have unified API across all platforms, Switch control in Xamarin Forms doesn’t have TextOn and TextOff properties, as those properties wouldn’t make sense for iOS or Windows Phone. Consequence of this is often undesired user experience on Android where user have to choose between ON and OFF states for fields like: IsPortable, IsCommecial, IsPrimary etc. This is where writing custom renderer comes handy.
There are two main parts to implementing a custom control with a renderer:
- Create your own custom Xamarin.Forms control with bindable properties in PCL (shared) project so that Xamarin.Forms API can refer them.
- Create a renderer in Android platform that will be used to display the custom control and subscribe to property changed notifications.
TextSwitch Control
We will create TextSwitch class which extends Xamarin Forms Switch class with TextOn and TextOff bindable properties. Default values for these properties are loaded from resource file (we will default them to “Yes” and “No”), but they could be bound to any other value in XAML or code behind.
{
public static readonly BindableProperty TextOnProperty = BindableProperty.Create<textswitch,>(p => p.TextOn, AppResources.CustomSwitch_DefaultTextOn);
public static readonly BindableProperty TextOffProperty = BindableProperty.Create<textswitch,>(p => p.TextOff, AppResources.CustomSwitch_DefaultTextOff);
public string TextOn
{
get { return (string)this.GetValue(TextOnProperty); }
set { this.SetValue(TextOnProperty, value); }
}
public string TextOff
{
get { return (string)this.GetValue(TextOffProperty); }
set { this.SetValue(TextOffProperty, value); }
}
}
TextSwitch Rendered
When Xamarin Forms control is in place we can write Android platform specific code. We will implement a TextSwitchRenderer class that inherits from a ViewRenderer using Android Switch control (unlike inheriting from SwitchRenderer). This is important difference, as Android Switch control have TextOn and TextOff properties which we need to update.
{
/// <summary>
/// Called when [element changed].
/// </summary>
protected override void OnElementChanged(ElementChangedEventArgs<textswitch> e)
{
base.OnElementChanged(e);
if (e.OldElement != null)
{
this.Element.Toggled -= ElementToggled;
return;
}
if (this.Element == null)
{
return;
}
var switchControl = new Switch(Forms.Context)
{
TextOn = this.Element.TextOn,
TextOff = this.Element.TextOff
};
switchControl.CheckedChange += ControlValueChanged;
this.Element.Toggled += ElementToggled;
this.SetNativeControl(switchControl);
}
/// <summary>
/// Releases unmanaged and - optionally - managed resources.
/// </summary>
/// <paramname="disposing"/><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.
protected override void Dispose(bool disposing)
{
if (disposing)
{
this.Control.CheckedChange -= this.ControlValueChanged;
this.Element.Toggled -= ElementToggled;
}
base.Dispose(disposing);
}
/// <summary>
/// Handles the Toggled event of the Element control.
/// </summary>
/// <paramname="sender"/>The source of the event.
/// <paramname="e"/>The <seecref="ToggledEventArgs"> instance containing the event data.
private void ElementToggled(object sender, ToggledEventArgs e)
{
this.Control.Checked = this.Element.IsToggled;
}
/// <summary>
/// Handles the ValueChanged event of the Control control.
/// </summary>
/// <paramname="sender"/>The source of the event.
/// <paramname="e"/>The <seecref="EventArgs"> instance containing the event data.
private void ControlValueChanged(object sender, EventArgs e)
{
this.Element.IsToggled = this.Control.Checked;
}
}
Xamarin Forms will handle all of the size calculations and normal properties of a standard Android View. All we had to do is to create custom control and tell the renderer to display it, which we did by overriding OnElementChanged method, where we have access to all the properties we need.
this.Element property represents our TextSwitch control. We created Android Switch control providing TextOn and TextOff properties from this.Element.
{
TextOn = this.Element.TextOn,
TextOff = this.Element.TextOff
};
We also needed to handle Toogle property change events. That is why we wrote ElementToggled and ControlValueChanged event handlers and bound them to controls.
Using TextSwitch
Example of using TextSwitch control in XAML:
We didn’t set TextOn and TextOff properties for the first TextSwitch, so it used default “Yes” and “No” value, while we provided them from resource file for the second TextSwitch. Result is following:
Extending controls using custom renderers is very flexible. Using the same approach you could easily customize colors for disabled and enabled states of Switch control.