Xamarin.Forms: Customizing our Entry control (Custom renderer)

Xamarin.Forms: Customizing our Entry control (Custom renderer)

If you ever have worked with Xamarin.Forms, you definitively noticed that our entry control (most used control) looks pretty different between platforms (iOS and Android).

But, our designs most of the time should look the same to accomplish a look & feel like this:

So I took on the task of extending our Entry control to enable customizations like BorderColor, Padding and CornerRadius .

If you prefer you can watch this video about it (sad to admit that it's in Spanish, by the way but an English version is coming so subscribe and keep tune in).

TLDR;

If you prefer to review our customization code and include it in your project, you can check the full source code available in Github https://github.com/jesulink2514/XamBooksApp/tree/feature/feat-entry

Let's explore these customizations

To begin with, we used my XamBooksApp project available in Github in case you want to check the code.

I've created our StandardEntry class, this class extends Xamarin.Forms' control and includes some additional properties.  

As you can see, in this case, there is not much code in our class because almost everything of the personalization code resides in each platform project. As you can imagine in our Custom Renderers.

Let's start with Android

Here we have our renderer, which is properly exported and registered to be linked with our new control, StandarEntry.

Obviously, we don't want to lose base functionality already implemented by Xamarin.Forms' team, we just want to extend it so our renderer inherits from EntryRenderer.

To make it works in Android, we need to define a constructor that receives an Android Context parameter.

Now it's time to override CreateNativeControl method, not for replacing our FormsEditText with other native control, we're going to keep it but I want to make sure that our Background is updated when our native control is created taking into account our new properties.

For this, we're going to use this method, UpdateBackground, here is where all magic happens.

In our case, given that we've extended EntryRenderer class, its property: Element returns an instance of Entry but it holds a StandardEntry (you can review about basic renderer properties  here). This property gives us access to Xamarin.Forms control instance, in other words, to our abstraction in our cross-platform project, my StandardEntry. Sad to say we can't override this property so we've defined a new property which does the casting for us, our objective here is made easier to access our new properties.

You might notice that we've overridden OnElementPropertyChanged method to allow us to react to properties changes inside our control. You must not forget to call base class implementation at the end.

So take a look at this Android code snippet which is used to apply required customizations.

Using a GradientDrawable as background for our control gives us all that we need like background, border, etc. It's worthy to mentioned these extension methods that Xamarin.Forms' team put to our disposition to write code that interacts with platform specifics.

  • ToAndroid, this extension method takes a Color instance from Xamarin.Forms world and transforms it into Color class that Android needs.
  • ToPixels, this extension method takes an independent pixel density measurement (a number) and converts it into the pixels equivalent.

Both methods extends Android Context class.

Finally, we take our  Padding property in form of Xamarin.Forms Thicknes instance and extract from it each of its components. The same as before, we must convert these values to pixels.

To complete this renderer, we must override a method called  UpdateBackgroundColor , which is already in use by base class when we update BackgroundColor property.

Time to look at iOS

The same as before, our iOS renderer is properly registered to be used with our StandardEntry control.

We can spot our first difference, our renderer doesn't extend EntryRenderer, instead of it extend EntryRendererBase. If we review Xamarin.Forms source code, given that it's an open source project and there is no better way to learn about renderers than check it out, we would notice that Xamarin's team have created a generic class to be easier to replace UITextField native control with a class that extends it.

But, wait a minute, Why do we need a UITextField derived class? That's what I was wondering till I tried to make padding works in iOS.

To make padding works I need to override TextRect, PlaceholderRect and EditingRect method, these methods are used to determine the sizing of showed text, placeholder and the editing rect.  To accomplish this goal, we're using a native iOS feature called UIEdgeInsets (closets feature to our idea of padding).

Similar to Android implementation, we've created a ElementV2 property, in order to get access to new properties defined in our derived control.

In order to use our new UITextFieldPadding class and update its background we've overriden  CreateNativeControl method, this time this makes all sense due to replacement of native control.

Probably you noticed similarity with Android, here in iOS background is defined used Layer property.

An speaking of extension methods, we just needed ToCGColor method, which takes a Xamarin.Forms color and convert it to CGColor needed by iOS.

Y voilá ...

https://github.com/jesulink2514/XamBooksApp/tree/feature/feat-entry