8 min read

 In this article by Paul F. Johnson, author of the book Cross-platform UI Development with Xamarin.Forms, we’ll look at how to add a custom renderer for Windows Phone in particular.

(For more resources related to this topic, see here.)

This article doesn’t depend on anything because there is no requirement to have a Xamarin subscription; the Xamarin Forms library is available for free via NuGet. All you require is Visual Studio 2013 (or higher) running on Windows 8 (or higher—this is needed for the Windows Phone 8 emulator).

Let’s make a start

Before we can create a custom renderer, we have to create something to render. In this case, we need to create a Xamarin Forms application. For this, create a new project in Visual Studio, as shown in the following screenshot:

Selecting the OK button creates the project. Once the project is created, you will see the following screenshot on the right-hand side:

In the preceding screenshot, there are four projects created:

  • Portable (also known as the PCL—portable class library)
  • Droid (Android 4.0.3 or higher)
  • iOS (iOS 7 or higher)
  • Windows Phone (8 or higher). By default, it is 8.0, but it can be set to 8.1

If we expand the WinPhone profile and examine References, we will see the following screenshot:

Here, you can see that Xamarin.Forms is already installed. You can also see the link to the PCL at the bottom.

Creating a button

Buttons are available natively in Xamarin Forms. You can perform some very basic operations on a button (such as assign text, a Click event, and so on). When built, the platform will render their own version of Button.

This is how the code looks:

var button = new Button
{
  Text = "Hello"
};
button.Click += delegate {…};

For our purposes, we don’t want a dull standard button, but we want a button that looks similar to the following image:

We may also want to do something really different by having a button with both text and an image, where the image and text positions can look similar to the following image on either side:

Creating the custom button

The first part to creating the button is to create an empty class that inherits Button, as shown in the following code:

using Xamarin.Forms;
namespace CustomRenderer
{
  public class NewButton : Button
  {
    public NewButton()
    {
    }
  }
}

As NewButton inherits Button, it will have all the properties and events that a standard Button has. Therefore, we can use the following code:

var btnLogin = new NewButton()
{
  Text = "Login",
};
btnLogin.Clicked += delegate
{
  if (!string.IsNullOrEmpty(txtUsername.Text) && !string.IsNullOrEmpty(txtPassword.Text))
    LoginUser(txtUsername.Text, txtPassword.Text);
};

However, the difference here is that as we will use something that inherits a class, we can use the default renderer or define our own renderer.

The custom renderer

To start with, we need to tell the platform that we will use a custom renderer as follows:

[assembly: ExportRenderer(typeof(NewButton), typeof(NewButtonRenderer))]
namespace WinPhone
{
  class NewButtonRenderer : ButtonRenderer

We start by saying that we will use a renderer on the NewButton object from the PCL with the NewButtonRenderer class. The class itself has to inherit ButtonRenderer that contains the code we need to create the renderer.

The next part is to override OnElementChanged. This method is triggered when an element from within the object being worked on changes.

Considerations for Windows Phone

A prime consideration on Windows Phone is that the ViewRenderer base is actually a Canvas that has the control (in this case, a button) on it as a child. This is an advantage for us. If we clear the child from the canvas, the canvas can be manipulated, and the button can be added back.

It is important to remember that we are dealing with two distinct entities, and each has its own properties. For example, the white rectangle that surrounds a Windows Phone button is part of the control, whereas the color and styling are part of the canvas, as shown in the following code:

protected override void OnElementChanged(ElementChangedEventArgs<Xamarin.Forms.Button> e)
{
  base.OnElementChanged(e);
  if (Control != null)
  {
    // clear the children of the canvas. We are not deleting the button.
    Children.Clear();
    // create the new background
    var border = new Border
    {
      CornerRadius = new System.Windows.CornerRadius(10),
      Background = new SolidColorBrush(System.Windows.Media.Color.FromArgb(255, 130, 186, 132)),
      BorderBrush = new SolidColorBrush(System.Windows.Media.Color.FromArgb(255,45,176,51)),
      BorderThickness = new System.Windows.Thickness(0.8),
      Child = Control  // this adds the control back to the border
    };
    Control.Foreground = new SolidColorBrush(Colors.White);  // make the text white
    Control.BorderThickness = new System.Windows.Thickness(0); // remove the button border that is always there
    Children.Add(border); // add the border to the canvas. Remember, this also contains the Control
  }
}

When compiled, the UI will give you a button, as shown in the following image:

I’m sure you’ll agree; it’s much nicer than the standard Windows Phone button.

The sound of music

An image button is also fairly simple to create. Again, create a new Xamarin.Forms project in Visual Studio. Once created, as we did before, create a new empty class that inherits Button.

Why is it empty?

Unfortunately, it’s not that simple to pass additional properties with a custom renderer, so to ensure an easier life, the class just inherits the base class, and anything else that is needed to go to the renderer is accessed through the pointer to app.

Setting up the PCL code

In the PCL, we will have the following code:

App.text = "This is a cow";
App.filename = "cow.png";
App.onTheLeft = true;
var btnComposite = new NewCompositeButton(){ };

Text, filename, and onTheLeft are defined in the App class and are accessed from the PCL using CompositeUI.App.filename (CompositeUI is the namespace I’ve used).

The PCL is now set up, so the renderer is needed.

The Windows Phone renderer

As before, we need to tell the platform that we will use our own renderer and override the default OnElementChanged event, as shown in the following code:

[assembly: ExportRenderer(typeof(NewCompositeButton), typeof(NewCompositeButtonRenderer))]
namespace WinPhone
{
  class NewCompositeButtonRenderer :ButtonRenderer
  {
    protected override void OnElementChanged(ElementChangedEventArgs<Xamarin.Forms.Button> e)
    {
      base.OnElementChanged(e);

As with the first example, we will deal with a base class that is a Canvas with a single child. This child needs to be removed from the canvas before it can be manipulated as follows:

Children.Clear();

Our next problem is that we have an image and text.

Accessing the image

It is recommended that images are kept either in the Assets directory in the project or in the dedicated Images directory. For my example, my image is in assets.

To create the image, we need to create a bitmap image, set the source, and finally assign it to an image (for good measure, a small amount of padding is also added) as follows:

var bitmap = new BitmapImage();
bitmap.SetSource(App.GetResourceStream(new Uri(@"Assets/"+CompositeUI.App.filename, UriKind.Relative)).Stream);
var image = new System.Windows.Controls.Image
{
  Source = bitmap,
  Margin = new System.Windows.Thickness(8,0,8,0)
};

Adding the image to the button

We now have a problem. If we add the image directly to the canvas, we can’t specify whether it is on the left-hand side or on the right-hand side of the text. Moreover, how do you add the image to the canvas? Yes, you can use the child property, but this still leads to the issue of position.

Thankfully, Windows Phone provides a StackPanel class. If you think of a stack panel as a set of ladders, you will quickly understand how it works. A ladder can be vertical or horizontal. If it’s vertical, each object is directly before or after each other. If it is horizontal, each object is either at the left-hand side or the right-hand side of each other. With the Orientation property of a StackPanel class, we can create a horizontal or vertical ladder for whatever we need. In the case of the button, we want the Panel to be horizontal, as shown in the following code:

var panel = new StackPanel
{
  Orientation = Orientation.Horizontal,
};

Then, we can set the text for the button and any other attributes:

Control.Foreground = new SolidColorBrush(Colors.White);
Control.BorderThickness = new System.Windows.Thickness(0);
Control.Content = CompositeUI.App.text;

Note that there isn’t a Text property for the button on Windows Phone. Its equivalent is Content.

Our next step is to decide which side the image goes on and add it to the panel, as shown in the following code:

if (CompositeUI.App.onTheLeft)
{
  panel.Children.Add(image);
  panel.Children.Add(Control);
}
else
{
  panel.Children.Add(Control);
  panel.Children.Add(image);
}

We can now create the border and add the panel as the child:

var border = new Border
{
  CornerRadius = new System.Windows.CornerRadius(10),
  Background = new SolidColorBrush(System.Windows.Media.Color.FromArgb(255, 130, 186, 132)),
  BorderBrush = new SolidColorBrush(System.Windows.Media.Color.FromArgb(255, 45, 176, 51)),
  BorderThickness = new System.Windows.Thickness(0.8),
  Child = panel
};

Lastly, add the border to the canvas:

Children.Add(border);

We now have a button with an image and text on it, as shown in the following image:

This rendering technique can also be applied to Lists and anywhere else required. It’s not difficult; it’s just not as obvious as it really should be.

Summary

Creating styled buttons is certainly for the platform to work on, but the basics are there in the PCL. The code is not difficult to understand, and once you’ve used it a few times, you’ll find that the styling buttons to create attractive user interfaces is not such as big effort. Xamarin Forms will always help you create your UI, but at the end of the day, it’s only you who can make it stand out.

Resources for Article:


Further resources on this subject:


LEAVE A REPLY

Please enter your comment!
Please enter your name here