Thursday, March 11, 2021

Custom Sharpnado Tabs

xxx
Thursday, March 11, 2021

Sharpnado Tabs are a very popular nuget package for Xamarin. I recently needed to create custom tabs and think the documentation is a bit off.

The Sharpnado docs for tabs talks about customiztion under a section labeled “Custom SPAM tabs!”

It says to extend TabItem and then gives this example code:

<tabs:TabItem x:Class="SillyCompany.Mobile.Practices.Presentation.CustomViews.SpamTab"
              xmlns="http://xamarin.com/schemas/2014/forms"
              xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
              xmlns:tabs="clr-namespace:Sharpnado.Presentation.Forms.CustomViews.Tabs;assembly=Sharpnado.Presentation.Forms"
              x:Name="RootLayout">
    <ContentView.Content>
        <Grid ColumnSpacing="0" RowSpacing="0">
            <Image x:Name="Spam"
                   VerticalOptions="End"
                   Aspect="Fill"
                   Source="{Binding Source={x:Reference RootLayout}, Path=SpamImage}" />
            <Image x:Name="Foot"
                   Aspect="Fill"
                   Source="monty_python_foot" />
        </Grid>
    </ContentView.Content>
</tabs:TabItem>

...

<tabs:TabHostView x:Name="TabHost"
                  Grid.Row="2"
                  BackgroundColor="White"
                  SelectedIndex="{Binding Source={x:Reference Switcher}, Path=SelectedIndex, Mode=TwoWay}">
    <tabs:TabHostView.Tabs>
        <tb:SpamTab SpamImage="spam_classic_home" />
        <tb:SpamTab SpamImage="spam_classic_list" />
        <tb:SpamTab SpamImage="spam_classic_grid" />
    </tabs:TabHostView.Tabs>

I’m pretty sure this is wrong and that you should not use the Sharpnado.Presentation.Forms.CustomViews.Tabs namespace.

Instead, inherit from Sharpnado.Tabs.UnderlinedTabItem. There are other tab types, but UnderlinedTabItem gives us a Label bindable property which comes in super handy. My use case was needing the tabs to appear as circles with initials inside them. I’m using Syncfusion’s Avatar View for this, but the concept is the same for any custom component that you want to create. My code ends up looking like this:

CustomTab.xaml

<?xml version="1.0" encoding="UTF-8"?>
<tabs:UnderlinedTabItem
    xmlns="http://xamarin.com/schemas/2014/forms"
    xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
    xmlns:tabs="http://sharpnado.com"
    xmlns:sfavatar="clr-namespace:Syncfusion.XForms.AvatarView;assembly=Syncfusion.Core.XForms"
    x:Class="MyApp.Components.CustomTab"
    x:Name="thisComponent"
    Padding="3, 0, 3, 0">

    <ContentView.Content>

        <sfavatar:SfAvatarView
            ContentType="Custom"
            AvatarShape="Circle" AvatarSize="Small"
            BackgroundColor="{AppThemeBinding Light={StaticResource LightThemeBackgroundColor}, Dark={StaticResource DarkThemeBackgroundColor}}"
            BorderColor="{AppThemeBinding Light={StaticResource LightThemeTextColor}, Dark={StaticResource DarkThemeTextColor}}"
            VerticalOptions="Center" HorizontalOptions="Center">

            <Label
                Text="{Binding Label, Source={x:Reference thisComponent}}"
                TextColor="{AppThemeBinding Light={StaticResource LightThemeTextColor}, Dark={StaticResource DarkThemeTextColor}}"
                VerticalTextAlignment="Center"
                HorizontalTextAlignment="Center" />

        </sfavatar:SfAvatarView>

    </ContentView.Content>

</tabs:UnderlinedTabItem>

If you are curious about the AppThemeBinding for colors, read about that here.

CustomTab.xaml.cs

using Sharpnado.Tabs;

namespace MyApp.Components
{

    public partial class CustomTab : UnderlinedTabItem
    {

        public WidgetTab()
        {
            InitializeComponent();
        }

        protected override void OnBadgeChanged(BadgeView oldBadge)
        {
        }

    }
}

Note that it’s required to implement OnBadgeChanged, even if you don’t plan on doing anything with it.

Use the custom tabs like this:

MyPageWithTabs.xaml.cs

xmlns:components="clr-namespace:MyApp.Components"

...

<tabs:TabHostView
    x:Name="TabHost"
    SelectedIndex="{Binding Source={x:Reference TabSwitcher}, Path=SelectedIndex, Mode=TwoWay}"
    TabType="Scrollable">

    <tabs:TabHostView.Tabs>

        <components:CustomTab Label="AA" />
        <components:CustomTab Label="BB" />
        <components:CustomTab Label="CC" />

    </tabs:TabHostView.Tabs>

</tabs:TabHostView>

The end result ends up looking like simple circles instead of the normal tabs: