Custom Renderer - James Montemagno https://staging.montemagno.com/tag/custom-renderer/ https://staging.montemagno.com/content/images/2017/08/MonogramIcon_Orange_NoBuffer.png Custom Renderer - James Montemagno https://staging.montemagno.com/tag/custom-renderer/ Hugo -- gohugo.io Tue, 13 Oct 2026 23:21:57 GMT 60 Dynamically Changing Xamarin.Forms Tab Icons When Selected https://staging.montemagno.com/dynamically-changing-xamarin-forms-tab-icons-when-select/ 598df8f2c121b60019bf305a Fri, 14 Jul 2017 18:16:47 GMT Spicing up your Xamarin.Forms tab can easily be done in a few ways. You can add tint color in Android when the user deselects a tab, which can also be done in iOS in addition to a full swap of a selected image. It was recently pointed out to me that these blogs highlighted a way of adding back a bit of nativeness, but didn't answer a different question of how to actually completely change the icon itself when a tab is selected. This question caught me off guard as I have never really changed the actual icon, but sure why not I said! My blog post on iOS actually gives you a way to do this if you label things correctly, but that seems a bit maintainable and not cross-platform. So, today lets change those tab icons!

For this blog we are going to start with a blank Xamarin.Forms application for iOS and Android. Then we will add in a new TabbedPage that contains two tabs inside of it that will change icons when selected or unselected. We will be using some MVVM and Data Binding and we will be using my MVVM Helpers Library, so make sure you go install that from NuGet.

Get Some Icons

For this sample we will have two tabs and when we select either of them it will change to a specific icon and back to the original when deselected. So, go ahead and grab some icons from Glyphish for iOS and Android Asset Studio for Android. Make sure that the names are exactly the same and that you add them to the iOS/Android specific projects.

I have imported the following names into Android/iOS:

  • tab_target.png
  • tab_chat.png
  • tab_graph.png

IIconChange Interface

Before we setup any Views or ViewModels we want to create a simple interface that any of our ViewModels can implement that will tell us if the Page that the ViewModel is bound to is selected and what the current icon should be:

public interface IIconChange
{
    bool IsSelected { get; set; }
    string CurrentIcon { get; }
}

ViewModels

This small app that we will make will have two tabs, so let's create two ViewModels and implement the new IIconChange interface. The key is that when ever we change IsSelected we will raise a change notification for CurrentIcon, which we will return a string to the Icon we wanted for the current state.

Tab1ViewModel.cs

public class Tab1ViewModel : BaseViewModel, IIconChange
{
    public Tab1ViewModel()
    {
        Title = "Tab1";
    }

    bool isSelected;
    public bool IsSelected
    {
        get => isSelected;
        set
        {
            if (SetProperty(ref isSelected, value))
                OnPropertyChanged(nameof(CurrentIcon));
        }
    }
    public string CurrentIcon
    {
        get => IsSelected ? "tab_target.png" : "tab_chat.png";
    }
}

Tab2ViewModel.cs

public class Tab2ViewModel : BaseViewModel, IIconChange
{
    public Tab2ViewModel()
    {
        Title = "Tab2";
    }

    bool isSelected;
    public bool IsSelected
    {
        get => isSelected;
        set
        {
            if (SetProperty(ref isSelected, value))
                OnPropertyChanged(nameof(CurrentIcon));
        }
    }
    public string CurrentIcon
    {
        get => IsSelected ? "tab_target.png" : "tab_graph.png";
    }
}

BaseViewModel comes from that MVVM Helpers NuGet that I mentioned earlier and includes a lot of bindable properties such as Title.

Creating The Tabs

Before we create the TabbedPage let's create some pages for our ViewModels that we just created. I created two ContentPage XAML pages and simply data bound the Title and Icon property on the page. I named my pages TabIconsPage and TabIconsPage2.

XAML Example:

<?xml version="1.0" encoding="utf-8"?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
    xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
    xmlns:local="clr-namespace:TabIcons"
    x:Class="TabIcons.TabIconsPage"
    Title="{Binding Title}"
    Icon="{Binding CurrentIcon}">
</ContentPage>

Setting Up MyTabs

Here we go! Finally time to setup and create the Tabs and set our Binding Context. The key here is that we will register for changes to the current page with CurrentPageChanged and then update the IsSelected property and trigger an event that anyone could loop into (more on that later).

public class MyTabs : TabbedPage
{
    //always save a reference to the current page
    Page currentPage;
    public MyTabs()
    {
        //create the pages and set the view models
        //you could also do this in the page code behind
        Children.Add(new TabIconsPage
        {
            BindingContext = new Tab1ViewModel
            {
                IsSelected = true
            }
        });
        Children.Add(new TabIconsPage2
        {
            BindingContext = new Tab2ViewModel()
        });

        currentPage = Children[0];

        //Register for page changes
        this.CurrentPageChanged += Handle_CurrentPageChanged;
    }

    //Update the IsSelected state and trigger an Event that anyone can loop into.
    public event EventHandler UpdateIcons;
    void Handle_CurrentPageChanged(object sender, EventArgs e)
    {
        var currentBinding = currentPage.BindingContext as IIconChange;
        if (currentBinding != null)
            currentBinding.IsSelected = false;

        currentPage = CurrentPage;
        currentBinding = currentPage.BindingContext as IIconChange;
        if (currentBinding != null)
            currentBinding.IsSelected = true;

        UpdateIcons?.Invoke(this, EventArgs.Empty);
    }
}

iOS is DONE!

Yup, that is right, iOS is totally done at this point. Xamarin.Forms for iOS actually will automatically update the icon for us when we trigger this change, which is really awesome. Android... of course it doesn't :(, browsing through the source code for Xamarin.Forms I found that the only way Android updates the tab icon is when a page is added or removed. So time to dip into Android source code.

Custom Renderer for Android

This code can easily, and should be, combined with my previous entry on setting the tint color based on selection of the tab for Android. It is very similar, but this time we will not only save out our current TabLayout, but we will also register for our MyTabs event of UpdateIcons to actually update the icons:

using System;
using System.IO;
using System.Linq;
using Android.Support.Design.Widget;
using TabIcons;
using TabIcons.Droid;
using TabIcons.Interfaces;
using Xamarin.Forms;
using Xamarin.Forms.Platform.Android;
using Xamarin.Forms.Platform.Android.AppCompat;
[assembly: ExportRenderer(typeof(MyTabs), typeof(MyTabsRenderer))]
namespace TabIcons.Droid
{
    public class MyTabsRenderer : TabbedPageRenderer
    {
        TabLayout layout;
        protected override void OnElementChanged(ElementChangedEventArgs<TabbedPage> e)
        {
            base.OnElementChanged(e);
            if(Element != null)
            {
                ((MyTabs)Element).UpdateIcons += Handle_UpdateIcons;
            }
        }

        protected override void OnElementPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
        {
            base.OnElementPropertyChanged(sender, e);

            if (layout == null && e.PropertyName == "Renderer")
            {
                layout = (TabLayout)ViewGroup.GetChildAt(1);
            }
        }

        void Handle_UpdateIcons(object sender, EventArgs e)
        {
            TabLayout tabs = layout;

            if (tabs == null)
                return;

            for (var i = 0; i < Element.Children.Count; i++)
            {
                var child = Element.Children[i].BindingContext as IIconChange;
                var icon = child.CurrentIcon;
                if (string.IsNullOrEmpty(icon))
                    continue;

                TabLayout.Tab tab = tabs.GetTabAt(i);
                SetCurrentTabIcon(tab, icon);
            }
        }

        void SetCurrentTabIcon(TabLayout.Tab tab, string icon)
        {
            tab.SetIcon(IdFromTitle(icon, ResourceManager.DrawableClass));
        }

        int IdFromTitle(string title, Type type)
        {
            string name = Path.GetFileNameWithoutExtension(title);
            int id = GetId(type, name);
            return id;
        }

        int GetId(Type type, string memberName)
        {
            object value = type.GetFields().FirstOrDefault(p => p.Name == memberName)?.GetValue(type)
                ?? type.GetProperties().FirstOrDefault(p => p.Name == memberName)?.GetValue(type);
            if (value is int)
                return (int)value;
            return 0;
        }
    }
}

Overall, this code is pretty simple and actually steals some code from the Xamarin.Forms project. Whenever the UpdateIcons event is triggered we will simply loop through all of the tabs and find the matching drawable for the CurrentIcon property.

Grab the Code

Hopefully you have found this helpful in making your tabs extra fancy. You can grab the source code from my new GitHub repository that will contain this and all future code samples from the blog.

]]>
Xamarin.Forms Android Selected & Unselected Tab Colors https://staging.montemagno.com/xamarin-forms-android-selected-and-unselected-tab-colors/ 598df8f2c121b60019bf2fd5 Tue, 21 Feb 2017 22:51:48 GMT I get a lot of questions about Xamarin.Forms. One of my most popular blogs was about spicing up iOS tabs with selected and unselected tab colors, and since that day developers have been asking about Android. Well, to be honest I was always against adding icons to material tabs in Android, but developers want to do it, so here is how you can, with a Custom Renderer for a Tabbed Page. Here is what it will look like at the end of the day:

Add Icons

First things first, you must set the “Icon” property on each page! As always, I recommend putting all icons into the drawable folders on Android and creating them in Android Asset Studio. 

Custom TabbedPage

Instead of using the default TabbedPage, we will want to create a custom TabbedPage. I called mine “MyTabs”, and there isn’t anything special about it except I use it as my base page in the XAML.

Running the app now should look like:

Android Color Selector

Now, is the fun part. Head over to your Resources folder on Android and add a new folder called color, and then create a new xml file inside of it, which I have called icon_tab.xml. This will be our color selector for each state. There are really only two states, selected and anything else. Here is what mine looks like.

TabbedPage Renderer

Finally, just like we did on iOS we need to add a custom renderer. The goal here is to find the tabs and apply the color selector.

And, that is about it to be honest with you. I hope you enjoy it :)

]]>
Adding a Disclosure Indicator Accessory to Xamarin.Forms ViewCells https://staging.montemagno.com/adding-a-disclosure-indicator-accessory-to/ 598df8f2c121b60019bf3002 Thu, 06 Aug 2015 22:54:21 GMT Xamarin.Forms has a super powerful ListView. You can easily add custom cells, pull to refresh, headers, footers, and even sections with just a few lines of code. A while ago I talked about how to easily group items to display section headers in the ListView. You get a great native styling of section headers with just a few simple lines of code. One thing I have noticed and has bothered me specifically with iOS is the lack of the Disclosure Indicator on cells. A Disclosure Indicator you say? What is that you may be asking? A Disclosure Indicator is a special Accessory on a Cell that has an arrow pointing to the right, normally indicating you can navigate another level deep. It looks like this:

This is just one of the 3 types of Accessories that you can have on a UITableViewCell. You can read all about them on the Xamarin documentation recipe and when you should use each of them. 

So what’s the issue?

The reason I am writing this blog is because out of the box Android and Windows have no concept of Accessories on cells. Users of these platforms simply expect that when you tap on an item you navigate, but on iOS you should really have the Disclosure Indicator or other accessory. Since there is no way to actually abstract this Xamarin.Forms simply leaves out the accessories of ListView cells, which is sad, but makes complete sense.

We can fix it!

Xamarin.Forms has the concept of custom renderers that can be used to customize controls, add custom controls, and even completely replace how a control is rendered! This is crazy and amazing as we can use a custom renderer to replace the default ViewCell implementation with our own that adds in the accessories. Instead of creating our own custom cell we will simply tell Xamarin.Forms to use our ViewCellRenderer for iOS. We can then leverage the fact that the “StyleId” property doesn’t do much besides providing the ability to do awesome things with Xamarin.UITest and Test Cloud. We can use the StyleId property to set what type of indicator we want. In your iOS project simply add a new class called ViewCellRenderer and copy in this code:

Now, all of your ViewCells will automatically be updated with an indicator by default or you can set the StyleId to anything you want in XAML or C#. 

Since I am am creating this renderer explicitly for a “ViewCell” with a “ViewCellRenderer” this means that only my custom ViewCell’s will have this new fancy indicator, but you could apply this same exact concept to TextCell with a TextCellRender or a ImageCell and ImageCellRenderer. It is amazing and really easy to implement without creating custom Xamarin.Forms controls. Enjoy and happy ViewCell Accessorizing!

]]>