When you’re ready to extend the capability of Xamarin.Forms views beyond their out-of-the-box functionality, then it’s time to start customizing them using custom renderers, effects, and native views. Platform-specific controls and layouts have scores of features inaccessible using only the Xamarin.Forms abstraction. There are three ways to access those features and extend the functionality of your application. The custom renderer gives full control over a Xamarin.Forms control by allowing you to gain access to all of its native functionality. It allows you to override the methods of the default renderers provided by Xamarin.Forms or even replace the platform-specific control Xamarin.Forms used with another control. Xamarin.Forms developers’ customization approach of choice is effects, a milder form of customization. Use effects if you need to change properties or use event listeners. Finally, Xamarin.Forms supports the use of the native views directly in XAML. This provides full access to the power of native controls along with the full responsibility of managing their lifecycle and behavior.
Custom Renderer
Xamarin.Forms exposes the mechanism whereby cross-platform views are made into platform-specific views, called renderers . By creating your own custom renderers, you get full access to platform-specific features buried deep within each view! Custom renderers are a bridge between Xamarin.Forms and Xamarin platform-specific libraries, Xamarin.iOS as well as Xamarin.Android.
Note
Custom renderers are the most powerful option for Xamarin.Forms view customization, so we’ll explore it first before moving onto a milder but more popular approach: Effects. Xamarin.Forms controls are drawn on the screen using two primary components: elements and renderers. Throughout this book you’ve been working with the elements: views, pages, or cells defined within Xamarin.Forms. The renderers take a cross-platform element and draw it on the screen using the platform-specific UI library. All Xamarin screens use renderers! For example, if you create a Label view using Xamarin.Forms, this element is rendered in iOS using UILabel and in Android using TextView. However, Xamarin.Forms provides only a partial binding to these platform-specific views. If you want to gain access to all of the properties and methods within platform-specific elements (such as UILabel, TextView, and TextBlock), then you need to create a custom renderer (or an Effect, but we’ll get to that later).
Think of a custom renderer as a way to access and extend your use of the platform-specific elements.
Tip
You can create custom renderers for these elements: Views, Cells, and Pages.
At the end of this topic, I’ll list most of the Xamarin.Forms elements covered in this book, their platform-specific equivalents, and which renderers to use when customizing them.
When to Use a Custom Renderer
When might you want to use a custom renderer?
You may want a view to behave differently and Xamarin.Forms isn’t obliging you. For example, you know for a fact that iOS does text decorations or shadows on a particular view and this isn’t available in Xamarin.Forms, so you create a custom renderer to access a Xamarin.iOS control. Use a custom control when you need direct access to an element’s platform-specific properties and methods, when you need to override platform-specific control methods, or when you need to replace a Xamarin.Forms element with your own custom platform-specific element.
Tip
A Xamarin.Forms customized control uses a custom renderer to access native functionality in a single control. A custom control is typically a group of controls composited into a single reusable component using ContentView (Chapter 5) (but you can also create a custom control using a custom renderer and replace the view with a group of views). Sometimes developers will say “custom control” to refer to a customized control.
Let’s explore how to create a custom renderer for Android and iOS.
Creating and Using a Custom Renderer
A custom renderer is created to implement the visual representation of a custom element. You create a custom element class that inherits from a standard Xamarin.Forms element, such as Button. Then you use that custom element in the UI. You can implement the custom renderer for each platform to use platform-specific members of that element, such as Android’s SetBackgroundColor method or the iOS BackgroundColor property.
Note
Button Backgroundcolor is the example used in this chapter. It’s a simple function that lends itself well to demonstrating how to create custom renderers. In the real world, outside of a teaching context, you would not customize this particular element because there is an easy way to do this in Xamarin.Forms:
View.BackgroundColor = Color.FromRGB(50,205,50);
or
<Button Text="Press Me" HorizontalOptions="FillAndExpand" BackgroundColor="#32cd32" />
Custom renderers should only be written for functionality that cannot be achieved using regular Xamarin.Forms views and their elements.
There are several steps in the creation and implementation of a custom renderer, but I’ll break them into two tasks: preparing the custom element in the Xamarin.Forms project and creating a custom renderer in each platform-specific project.
- 1.Create an element subclass. Create a custom element that is a subclass of the element you want to customize, such as Button, in your Xamarin.Forms project.<Button FontSize="Large" HorizontalOptions="Center" VerticalOptions="Fill" xmlns:="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="CustomRendererExample.CustomButton"/>
- 2.Create the C# partial class implementation of your custom element (code behind).public partial class CustomButton : Button{public CustomButton(){InitializeComponent();}}
- 3.
Use the element. Use the subclassed element, such as this CustomButton, in a layout in your Xamarin.Forms project.
- 1.
Create a custom renderer. Add a custom renderer class to each platform-specific project where you want to make customizations.
- 2.
Add [assembly]. Add the [assembly] attribute outside of the namespace declaration to declare the new renderer.
- 3.
Add using. Add using statements to the renderer class so that the renderer types are resolved.
That’s the upshot for creating a custom renderer.
In the next example, you will create a custom button that has custom renderers for each platform. Start by preparing your custom view in the Xamarin.Forms project before moving onto the renderers.
Creating the Custom Element
A custom renderer first requires a custom Xamarin.Forms element, which can be a View, a Cell, or a Page. In this example, you will use custom renderers to change the background color of a button view to some variant of green. The custom view will be called CustomButton and inherit from the Button view and is defined in XAML file. You need to provide two files for every custom element you are creating: the XAML file and the associated code behind file.
- 1.Create an element subclass. Create a new XAML document called CustomButton.xaml, and replace the root element ContentPage that Visual Studio created with Button both in the XAML and the code behind and assign some default values.<Button xmlns:="http://xamarin.com/schemas/2014/forms"xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"x:Class="CustomRendererExample.CustomButton" FontSize="Large"HorizontalOptions="Center" TextColor="Black"VerticalOptions=="Fill" />
- 2.Create the C# partial class implementation of your custom element (code behind).public partial class CustomButton : Button{public CustomButton(){InitializeComponent();}}
- 3.
Use the element. Use the subclassed element, such as this CustomButton, in a layout in your Xamarin.Forms project.
Use the CustomButton in CustomRendererExamplePage.xaml (in the Forms Project)
Use the CustomButton in CustomRendererExamplePage.xaml (in the Forms Project)
Remember to assign this CustomRendererExamplePage class to the MainPage property of your Application constructor. In the StackLayout, your use of the custom view, CustomButton, is exactly the same as the use of a regular Xamarin.Forms Button view.
Creating the Custom Renderer
Now that you have created a custom element and used it in your UI, you’ll need to create the custom renderer. You’ll need to determine the name of the renderer for your element, and I’ll show you how to do that later in this chapter in the section “Which Renderer and View Do You Customize?” In this example, you’ll use ButtonRenderer.
There are two main ways to customize a control: by property or by replacing the entire control. Customizing a control’s properties involves accessing platform-specific properties unavailable via the Xamarin.Forms view (such as a button’s background color). A Xamarin.Forms control can also be completely replaced by a platform-specific control of the developer’s choice. I’ll cover property customization in depth in this chapter and touch upon control replacement in the notes.
OnElementChanged : This main method fires upon changes to the element and is used for control initialization. Set the initial control value and its properties. This is also where to replace the entire control with your own customized platform-specific control.
OnElementPropertyChanged : This method fires upon changes to element and attached properties and is useful for data binding.
SetNativeControl : Call this method manually to replace the entire element with a custom platform-specific control (such as SetNativeControl(new YourCustomizedControl());).
Control: A reference to the platform-specific element (such as UIButton) displayed by the renderer. Platform-specific properties are available here. This object can also be replaced with an entirely new (and customized) platform-specific control.
Element: A reference to the Xamarin.Forms subclassed element (such as CustomButton). Xamarin.Forms element properties are available here.
Customize controls and their properties by using the Control property within the OnElementChanged method.
Implement data-bound customized controls by assigning Control properties from their corresponding Element properties in the OnElementPropertyChanged method.
Now create a custom renderer on each platform. Begin with the Android platform, then do iOS.
Android Custom Renderer
Renderers realize a view on the native platform. Create your own renderer by inheriting from the standard renderer, such as ButtonRenderer. Then call into the native view’s platform-specific API to customize the view using the renderer’s Control property. In OnElementChanged, you’ll assign your Control's background color property.
- 1.
Create a custom renderer. Add a custom renderer class to the platform-specific project, which is ButtonRenderer in this case.
Tip
Refer to the section “Which Renderer and View Do You Customize?” at the end of this chapter to help you determine the renderer and platform-specific control(s) to use for the element you want to customize.
Customized ButtonRenderer in CustomButtonRenderer.cs (in the Droid Project)
Note
If you don’t add a platform-specific renderer, the default Xamarin.Forms renderer will be used.
- 2.Add the [assembly] attribute outside of the namespace declaration to declare the new renderer.[assembly: ExportRenderer (typeof (CustomButton), typeof (CustomButtonRenderer))]
- 3.Add using statements to the renderer class so that the renderer types are resolved.using Xamarin.Forms.Platform.Android;using Xamarin.Forms;using CustomRendererExample;using CustomRendererExample.Droid;using Android.Content;
Tip
Color is close-captioned in this chapter for all of you black-and-white print readers.
CODE COMPLETE: Android Custom Renderer
CustomButton.xaml (in the Forms Project)
CustomButton Code Behind in CustomButton.xaml.cs (in the Forms Project)
Use the CustomButton in CustomRendererExamplePage.xaml (in the Forms Project)
Use the CustomButton in CustomRendererExamplePage.xaml (in the Forms Project)
CustomButtonRenderer.cs (Droid Project)
Now we’ll do a green button in iOS.
iOS Custom Renderer
Creating an iOS renderer for the Button view is similar to making the Android one. Create a custom renderer that inherits from a standard renderer, such as ButtonRenderer. Then call into the native view’s platform-specific API to customize it using the renderer’s Control property. In OnElementChanged, you’ll assign your Control's background color property.
- 1.
Create a custom renderer. Create CustomButtonRenderer.cs as a class in the iOS project. Inherit from the ButtonRenderer class and modify the Control property to affect your button as needed. The platform-specific view is assigned to the Control property, in this case an iOS UIButton control , and its native properties and methods are available. Listing 8-9 shows an iOS renderer where the background color is set using the UIButton's BackgroundColor property.
Customized ButtonRenderer in CustomButtonRenderer.cs (iOS Project)
- 2.Add [assembly] . Add the [assembly] attribute outside of the namespace declaration to declare the new renderer.[assembly: ExportRenderer (typeof (CustomButton), typeof (CustomButtonRenderer))]
- 3.Add using statements to the renderer class so that the renderer types are resolved.using Xamarin.Forms.Platform.iOS;using Xamarin.Forms;using UIKit;using CustomRenderer;using CustomRenderer.iOS;
CODE COMPLETE: iOS Custom Renderer
CustomButtonRenderer.cs for the iOS Project
That’s how to build a custom renderer on both mobile platforms!
The first trick in building a custom renderer is figuring out what the renderer is called and the native element name. Here’s a quick guide to help with that.
Which Renderer and View Do You Customize?
Elements, Their Renderers, and Platform-Specific Elements
Xamarin.Forms | Renderer | Android | iOS |
---|---|---|---|
Chapter 1 : Views | |||
ContentPage | PageRenderer | ViewGroup | UIViewController |
Label | LabelRenderer | TextView | UILabel |
Button | ButtonRenderer | Button | UIButton |
Entry | EntryRenderer | EditText | UITextField |
Image | ImageRenderer | ImageView | UIImageView |
BoxView | BoxRenderer | ViewGroup | UIView |
ScrollView | ScrollViewRenderer | ScrollView | UIScrollView |
ActivityInidcator | ActivityIndicatorRenderer | ProgressBar | UIActivityIndicator |
SearchBar | SearchBarRenderer | SearchView | UISearchBar |
Map | MapRenderer | MapView | MKMapView |
WebView | WebViewRenderer | WebView | UIWebView |
Chapter 3 : Layouts | |||
StackLayout | ViewRenderer | View | UIView |
RelativeLayout | ViewRenderer | View | UIView |
AbsoluteLayout | ViewRenderer | View | UIView |
Grid | ViewRenderer | View | UIView |
ContentView | ViewRenderer | View | UIView |
Frame | FrameRenderer | ViewGroup | UIView |
Chapter 4 | |||
Editor | EditorRenderer | EditText | UITextView |
Chapter 5 : Controls | |||
Picker | PickerRenderer | TextView, AlertDialog, NumberPicker EditText | UIPickerView, UIPickerViewModel, UIToolBar, UIBarButtonItems, UITextField |
DatePicker | DatePickerRenderer | TextView, AlertDialog | UIDatePicker, UIToolbar, UITextField, UIBarButtonItems |
TimePicker | TimePickerRenderer | TextView, AlertDialog EditText | UIDatePicker, UIToolbar, UITextField, UIBarButtonItems |
Stepper | StepperRenderer | LinearLayout, Button | UIStepper |
Slider | SliderRenderer | SeekBar | UISlider |
Switch | SwitchRenderer | Switch | UISwitch |
TemplatedPage | PageRenderer | ViewGroup | UIViewController |
TemplatedView | ViewRenderer | View | UIView |
ContentPresenter | ViewRenderer | View | UIView |
Chapter 6 : Lists | |||
ListView | ListViewRenderer | ListView | UITableView |
TextCell | TextCellRenderer | LinearLayout, TextView, ImageView | UITableViewCell |
EntryCell | EntryCellRenderer | LinearLayout, TextView, EditText | UITableViewCell, UITextField |
ViewCell | ViewCellRenderer | View | UITableViewCell |
SwitchCell | SwitchCellRenderer | Switch | UITableViewCell, UISwitch |
ImageCell | ImageCellRenderer | LinearLayout, TextView, ImageView | UITableViewCell, UIImage |
Chapter 7 : Navigation | |||
NavigationPage | NavigationRenderer | ViewGroup | UINavigationController, UIToolbar |
MasterDetailPage | MasterDetailRenderer, PhoneMasterDetailRenderer (iPhone) TabletMasterDetailRenderer(iPad) | DrawerLayout | UIViewController(iPhone), UISplitViewController(iPad) |
TableView | TableViewRenderer | ListView | UITableView |
TabbedPage | TabbedRenderer | ViewPager | UIView |
CarouselPage | CarouselPageRenderer | ViewPager | UIScrollView |
That should be sufficient to get you started with custom renderers. Now that you’ve seen the complete implementation of a renderer, it’s time for a subtler approach.
Effects
What do you do when Xamarin.Forms view you want to use is just shy of perfect? If you could just change one or two properties or its behavior, the view would meet your requirements. This is where effects come into play.
Effects allow access to the platform-specific control and container Xamarin.Forms uses to access the Xamarin.Android or Xamarin.iOS layer. Effects can be added to any page, layout, and view in Xamarin.Forms. Effects offer a reusable and lightweight approach to extend the capability of a Xamarin.Forms view compared to a custom renderer. Use effects if you only need to change a small number of the properties or behaviors of the underlying platform-specific control that Xamarin.Forms uses.
Tip
Fast renderers on view have increased performance over containers. Fast renderers are available only for certain controls on certain platforms (e.g., Button, Image, Label on Android, and more every release).
An effect is created by deriving from PlatformEffect in each platform-specific project to access the native control and then registering the effect with a unique identifier and organization-wide name. To use the effect, subclass the RoutingEffect in the shared project, resolving the effect by the organization-wide name and identifier. All Xamarin.Forms views have an Effects member that accepts one or more effects. Use the Effects property in XAML to add an effect.
Creating and Using Effects
An Effect gives access to the native control and container used by the Xamarin.Forms view through the platform-specific PlatformEffect class. The cross-platform RoutingEffect class is used to resolve the platform-specific effect in the Forms project. Both classes need to be subclassed to create and use an effect.
Creating an effect is simpler than creating a custom renderer primarily because you don’t have to determine and implement the native renderer. There is only one class PlatformEffect that gives direct access to a generic Container, Control, and the Xamarin.Forms Element. This simplicity comes with a greater responsibility. The developer needs to ensure that an effect is not accidentally added to a view that the effect does not support.
PlatformEffect
Control—references the platform-specific control Xamarin.Forms uses to implement its view
Container—references the platform-specific control used to implement the layout
Element—references the Xamarin.Forms view itself
OnAttached —called on initialization of your effect on the Xamarin.Forms view. Use this method to implement your effect.
OnDetached —called when the effect is detached from your Xamarin.Forms view for cleanup. This method is used less frequently.
To listen to changes to the bindable properties of the Xamarin.Forms view, override the OnElementPropertyChanged method of PlatformEffect.
Platform-Specific Effect
RoutingEffect
MyEffect RoutingEffect (in the Forms Project)
Note
Attached properties are often used in the context of customizing Xamarin.Forms views. An attached property is a particular kind of bindable property, coded in XAML as an attribute that contains a class.property. Details in the upcoming section, Adding Effects via Attached Properties.
Using the Effect
Applying the Effect to a Xamarin.Forms View (in the Forms Project)
Let’s create an effect that validates the Text entered in an Entry view.
Text Validator Effect
Imagine the following scenario: You want to give visual feedback to a user if the text he or she enters exceeds a maximum length! Wouldn’t it be nice if a Xamarin.Forms Entry would allow this type of validation? Unfortunately, this is not possible out of the box. You can use an effect to implement this behavior.
Let’s create an effect that allows you to validate the length of the text.
Custom RoutingEffect (in the Forms Project)
The empty default constructor passes our namespace EffectExample as the resolution group name and the name of the class TextValidatorEffect as the identifier to the base class RoutingEffect.
Registering an Effect in XAML (in the Forms Project)
Xamarin.Forms uses EditText in Android and UITextField in iOS as the native control to render an Entry. Both platform-specific views provide the ability to set the background color. Begin with the Android platform, then do iOS.
Android Platform Effect
The Android platform-specific control Android.Widget.EditText has the method SetBackgroundColor that expects an Android.Graphics.Color.
Android Platform-Specific TextValidatorEffect (in the Droid Project)
Android Platform-Specific TextValidatorEffect (in the Droid Project)
Now create the platform-specific IOSTextValidatorEffect for iOS.
iOS Platform Effect
The iOS platform-specific control UIKit.UITextField has the property BackgroundColor that can be set using a UIColor.
iOS Platform-Specific TextValidatorEffect (in the iOS Project)
iOS Platform-Specific TextValidatorEffect (in the iOS Project)
Tip
Attached Behaviors are often used in the context of Effects, removing extra code from code behind files. Details here: https://docs.microsoft.com/en-us/xamarin/xamarin-forms/app-fundamentals/behaviors/reusable/effect-behavior
The effect does not listen to changes to the Text property of Entry and will not re-validate it. Let’s change that.
Handling Events
Effects can override the OnElementPropertyChanged of PlatformEffect to listen to property changes of the Xamarin.Forms Element. This is useful when the effect needs to change the behavior of the native view depending on what has changed.
Let’s listen to what the user is writing in the Entry and re-validate the Text.
The implementation of the OnElementPropertyChanged method is identical for both iOS and Android, shown in Listings 8-22 and 8-23.
Tip
You can also register event handlers for the PlatformEffect's Container and Control members. These are platform-specific and require familiarity with the underlying platform.
CODE COMPLETE: TextValidatorEffect
Custom RoutingEffect (in the Forms Project)
Registering and Using an Effect in XAML (in the Forms Project)
Android Platform-Specific TextValidatorEffect (in the Droid Project)
iOS Platform-Specific TextValidatorEffect (in the iOS Project)
You can pass an effect as an attached property instead of adding it in XAML using the view’s Effects property. Let’s see how.
Adding Effects via Attached Properties
Adding an Effect Using the Effects Property (in the Forms Project)
Static Class with Attached Property (in the Forms Project)
MaxLengthPropertyChanged Method (in the Forms Project)
Internally, the previously defined routing effect TextValidatorEffect is used, which references the platform-specific implementation at runtime.
MaxLengthPropertyChanged Method (in the Forms Project)
Tip
Attached properties can also be used to attach commands, behaviors, triggers, and other functionality to XAML elements.
This covers the building blocks to create cross-platform effects for Xamarin.Forms controls. Up until now the platform-specific (sometimes we call these “native”) control’s role was behind the scenes, either encapsulated within the custom renderer or concealed by an effect. The final section of this chapter will present native controls as the headliner on the Xamarin.Forms XAML stage.
Native Views
Native views give you the power of platform-specific controls such as those found in Xamarin.iOS and Xamarin.Android directly within your XAML.
Custom renderers and effects leave the plumbing to Xamarin.Forms, which provides over 40 cross-platform visual elements and views and takes care of the mapping to platform-specific controls using custom renderers. The last topic in this chapter shows you how to use native (platform-specific) controls directly in a XAML page and handling their details manually, such as instantiating them and setting their properties.
Note
Using native views will quickly become advanced as you will find yourself having to take care of all aspects of the controls. This technique requires knowledge of the platform-specific APIs, Xamarin.iOS and Xamarin.Android.
Adding native views to your XAML file is easy. As with any other C# class, you need to declare the namespace in the root element of your XAML file, and you are ready to declare the class in your XAML. For native views this process is called Native View Declaration .
Create a XAML page called NativeViewsPage.xaml.
Registering the Namespaces for Native Views in XAML (in the Forms Project)
The xmlns declaration accepts the directive targetPlatform followed by the name of the platform Android or iOS. This tells XAML parser to ignore any namespace declaration not relevant for the platform the application is currently running on. This is very nice, because it allows you to create one XAML file for all platforms.
Most Android.Widget views require that you pass in the Activity within which they are used as their Android Context in the constructor. Xamarin.Forms provides the Forms.Context member that represents the Activity. Use the XAML directive x:Arguments to pass required parameters.
CODE COMPLETE: Native View Declaration
Native View Declaration (in the Forms Project)
Tip
There are some limitations to consider when working with native views. You cannot use the Style element because the properties of the native view are not bindable properties, and you cannot use the x:Name directive. If you want to reference them in your code behind, create a custom control as a wrapper using ContentView that has a name.
Properties of native views often require instances of other native classes that may require a factory method to construct the object. Let’s use some factory methods to assign properties to a native view next.
Using Factory Methods
Some native classes have in addition to default constructors and non-default constructors factory methods for instantiation. In Chapter 2, you learned to instantiate objects using factory methods and the x:Arguments keyword in the XAML syntax to pass the arguments.
Create a new XAML page called FactoryMethodsPage.xaml.
Both EditText in Android and UITextField on iOS have factory methods that specify a platform-specific font. In iOS the UIFont class is used to specify a font. In Android the TypeFace class is used. Listing 8-30 and 8-31 extend the example provided in Listing 8-29 to specify fonts for the UITextField and EditText views.
Passing Arguments to Native Views (in the Forms Project)
Passing Arguments to Native Views (in the Forms Project)
CODE COMPLETE: Non-Default Constructors and Factory Methods
Passing Arguments to Native Views (in the Forms Project)
This covers the basics of declaring native views. Refer to Chapter 9 to create user interfaces that use data bindings to allow native views and Xamarin.Forms views to interact in harmony.
Summary
Custom renderers, effects, and native views complete the Xamarin.Forms picture, extending the reach of Xamarin.Forms deep into the platform-specific APIs using Xamarin.iOS and Xamarin.Android. The Xamarin.Forms abstraction provides immeasurable value as a cross-platform tool, but the platforms differ, and developers need a way to bridge the gap. These platform-specific techniques, custom renderer, effects, and native views are that bridge.
Now it’s time for the foundation beneath all of our UI technique: the data. Let’s explore data access and data binding in depth.