HelloWorldHeaderGreen

Creating a Left ToolbarItem in Xamarin.Forms

TL;DR: If you’re looking for just the code, you can jump straight to it here.

I’ve been winding down 2015 by focusing on learning new subjects and improving my existing skills. Most of December was occupied with writing my first-ever app and I’ve been using Xamarin.Forms to create my UI. For those of you who are unfamiliar with it, Xamarin.Forms is a cross-platform toolkit for creating native user interfaces for iOS, Andriod, and Windows Phone apps. The native part is important here; simply put, I can code one UI, and Xamarin.Forms will do the heavy lifting for me, mapping my control as the equivalent control on each platform. If you are interested in how Xamarin.Forms works, I highly suggest reading the documentation, including Charles Petzold’s new book ‘Creating Mobile Apps with Xamarin.Forms‘.

One extremely useful feature of Xamarin.Forms is Custom Renderers. If there is something the toolkit doesn’t provide, or if you want to change a default appearance or behavior, you can create a custom renderer and voilà, your needs have been met!

While writing my app one problem I had was creating a lefthand bar button for a modal page on iOS, which is not possible by default in Xamarin.Forms.

I have a screen in my application which allows the user to add Items to a Pantry , and is presented modally as so:

void OnAdd (object sender, EventArgs e)
{
    Navigation.PushModalAsync (new NavigationPage (new ItemPage ()));
}

I want to have the user explicitly save, or cancel, the information they have entered. I don’t want to use a simple push transition that would show a back button; it’s not the right paradigm for what I’m trying to do. iOS allows this modal choice, as evidenced when adding a new contact:IMG_0197

(Android uses the device ‘back’ button in this instance, but right now I’m just concerned about iOS.)

So, now that I’ve created the transition to my modal screen–an instance of ItemPage–I need to add a couple of ToolbarItems in the ItemPage’s XAML:

<ContentPage.ToolbarItem>
	<ToolbarItem Text="Save" Activated="OnSave" Priority="0" Order="Primary" />
	<ToolbarItem Text="Cancel" Activated="OnCancel" Priority="1" Order="Primary" />
</ContentPage.ToolbarItems>

If I run the app in the iOS simulator I notice that all ToolbarItems render on the right hand side of the navigation bar, and herein lies my problem:

ToolBarITems

Xamarin.Forms does not let you change the position of the ToolbarItems, but that isn’t enough to stop me me adjusting it. Fortunately, custom renderers save the day!

Creating the ToolbarItem Custom Renderer

I’m not going to use this post to explore creating custom renderers as Xamarin’s documentation already does a great job of explaining how to create and use them. If you are unfamiliar with them, I recommend reading the docs.

Inspired by this forum post, I want to use the Priority value of each ToolbarItem to determine which side of the Navigation Item it will display on. Each item with Priority = 0 will go on the right side of the bar, and each item with Priority = 1 will go on the left side. Here’s the code:

[assembly:ExportRenderer (typeof(FridgeContentPage), typeof(FridgeContentPageRenderer))]
namespace Fridge.iOS
{

    public class FridgeContentPageRenderer : PageRenderer
    {
        public new FridgeContentPage Element {
            get{ return (FridgeContentPage)base.Element; }
        }

    public override void ViewWillAppear (bool animated)
    {
        base.ViewWillAppear (animated);

        var LeftNavList = new List<UIBarButtonItem> ();
        var rightNavList = new List<UIBarButtonItem> ();

        var navigationItem =this.NavigationController.TopViewController.NavigationItem;

        for (var i = 0; i < Element.ToolbarItems.Count; i++) {

            var reorder = (Element.ToolbarItems.Count - 1);
            var ItemPriority = Element.ToolbarItems [reorder - i].Priority;

            if (ItemPriority == 1) {
                UIBarButtonItem LeftNavItems = navigationItem.RightBarButtonItems [i];
                LeftNavList.Add (LeftNavItems);
            } else if (ItemPriority == 0) {
                UIBarButtonItem RightNavItems = navigationItem.RightBarButtonItems [i];
                rightNavList.Add (RightNavItems);
            }
        }

            navigationItem.SetLeftBarButtonItems (LeftNavList.ToArray (), false);
            navigationItem.SetRightBarButtonItems (rightNavList.ToArray (), false);

    }
  }
}

Great…so what’s going on here?

First, I used the attribute, shown below, to tell Xamarin.Forms that we are using a custom renderer. This attribute takes two parameters: the type name of the Xamarin.Forms page being rendered (In our example FridgeContentPage), and the type name of the custom renderer (FridgeContentPageRenderer in this example).

[assembly:ExportRenderer (typeof(FridgeContentPage), typeof(FridgeContentPageRenderer))]

Next, I created two lists to hold my left and right UIBarButtonItems (the native name for ToolBarItems in iOS):

var LeftNavList = new List<UIBarButtonItem> ();
var rightNavList = new List<UIBarButtonItem> ();

barbuttonitems

I need to have some way to get the items from the right side, and either set them to the left side, or keep them on the right, depending on their Priority. The UINavigationItem in iOS has some properties that will let me get and set these, so I’ll create a variable to capture my navigation item:

var navigationItem = this.NavigationController.TopViewController.NavigationItem;

I then want to go through each of my ToolbarItems and check its priority. If it has Priority = 1, it will be add it to the leftNavList list; if Priority = 0 it’ll be added to the rightNavList. This is achieved through a simple for loop:

for (var i = 0; i < Element.ToolbarItems.Count; i++) {

    ...
    var ItemPriority = Element.ToolbarItems[i].Priority;

    if (ItemPriority == 1) {
        UIBarButtonItem LeftNavItems = navigationItem.RightBarButtonItems [i];
        leftNavList.Add (LeftNavItems);
    } else if (ItemPriority == 0) {
        UIBarButtonItem RightNavItems = navigationItem.RightBarButtonItems [i];
        rightNavList.Add (RightNavItems);
    }
 }

At this point in the code, I have two lists containing left and right items which need to be placed on their respective side of the NavigationItem. As I mentioned earlier, UINavigationItem has properties for this purpose – SetLeftBarButtonItems & SetRightBarButtonItems – which take an array as input. I can use the following lines of code to achieve this:


navigationItem.SetLeftBarButtonItems (leftNavList.ToArray (), false);
 navigationItem.SetRightBarButtonItems (rightNavList.ToArray (), false);

Hooray! We are almost finished… except for one little problem. If I run this in the iOS simulator now, the ToolbarItems won’t be in the expected place. The Save button with Priority = 0 is on the left, the Cancel button with Priority = 1 is on the right:

Screen Shot 2015-12-28 at 9.48.57 PM

Why is this happening?

It’s because ToolbarItems (from Xamarin.Forms) stores the items in a reverse order to RightBarButtonItems (from iOS). Therefore the the item (i) referenced in the ItemPriority variable is not the same as the one that we will be assigning to the LeftNavItems and RightNavItems variables. To get around this, I can reverse the order of the ToolbarItems so that the same ToolbarItem corresponds to the same RightBarButtonItem by adding the follow to the start of my for loop:


 var reorder = (Element.ToolbarItems.Count - 1);
 var ItemPriority = Element.ToolbarItems [reorder - i].Priority;

Finally, if we run the application now:

Screen Shot 2015-12-28 at 10.12.40 PM

Huzzah! It works as expected.

Of course there are improvements that could be made to this custom renderer; null-checking jumps out as a big one. This is where I encourage you, dear reader, to use my code and make it better!

Happy custom rendering!

Amy

9 thoughts on “Creating a Left ToolbarItem in Xamarin.Forms

  1. I tested in X.F. 2.3.0.49 and works perfectly. Very easy to understand and apply.

    A doubt: you say that in Android devices, the users use hardware button to go back. So, in this case, not is recomended implement custom render for Android to provide left button in this platform?

    Thank you for your article, is very helpful!

    Like

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s