Tab Retap in Xamarin.Forms, Now With Icons!

A while back, I wrote about how to handle reselecting/retapping of tabs in a Xamarin.Forms TabbedPage (covering iOS and Android in part 1 here and UWP and NavigationPage in part 2 here).  The basic problem is that app designs frequently want to react to a user selecting the already-active tab on a TabbedPage — reselecting, or retapping, the tab.  Sometimes the app might want to pop the navigation stack, or perhaps it would scroll to the top of a ListView.  Whatever the desire, Xamarin.Forms does not make it easy to catch these reselect events.

A lot has happened since the initial solution presented in the above-mentioned posts, for instance Xamarin.Forms has moved from version 2.3 to version 3.6, and tab icons are supported across the three platforms in the sample (iOS, Android, UWP).

If you’re only interested in iOS and Android, there’s nothing new here.  If you need to know how to do this for UWP, read on!

Adding Icons to Tabs

Icons are now supported on tabs on all three of the platforms implemented by this app, just set the Icon property on the immediate children of the TabbedPage. The Image resource must be in the platform-specific projects; shared assembly resources cannot be used for tab Icons.

Unlike iOS and Android, UWP does not support icons on tabs by default. In order to enable tab icons on UWP, the XAML declaration for the TabbedPage needs to be updated:

<TabbedPage xmlns="http://xamarin.com/schemas/2014/forms"
            xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
            xmlns:windows="clr-namespace:Xamarin.Forms.PlatformConfiguration.WindowsSpecific;assembly=Xamarin.Forms.Core"
            x:Class="TabReselectDemo.MainPage"
            windows:TabbedPage.HeaderIconsEnabled="true">

Without setting TabbedPage.HeaderIconsEnabled=”true”, the Icon on the children of the TabbedPage on UWP will be ignored. Setting it to true makes the Icon property behave the same as on iOS and Android.

Extending UWP TabbedPageRenderer

Recalling how the original version was implemented, the UWP renderer doesn’t expose the tabs or tab selection events. In order to support tab reselection, we had to reverse engineer the implementation a bit, but luckily that seems to have been stable over time.

If we add icons to the tabs without any modification to the UWP renderer we built, then tapping (or clicking) on the icon won’t register as a retap/reselect. More reverse engineering is required. A stackoverflow answer shows one way to recognize the tab icon, which notices that the icon is an Image with the same StackPanel parent as the TextBlock we already recognize. This structure is the same, even if the Title on the tab is null.

The approach I take here recognizes that the icon Image element has a distinctive Name, so the implementation is parallel between the two elements. This makes the core of the MainPageTabRenderer:

     protected override void OnElementChanged(VisualElementChangedEventArgs e)
     {
         base.OnElementChanged(e);

         Control.Tapped += Control_Tapped;
         _prevPage = Control.SelectedItem as Xamarin.Forms.Page;
     }
     private void Control_Tapped(object sender,
Windows.UI.Xaml.Input.TappedRoutedEventArgs e)
{
if (!(Element is TabReselectDemo.MainPage))
return;
switch (e.OriginalSource)
{
case Image image:
if (image.Name == "TabbedPageHeaderImage")
HandleReselect(image);
break;
case TextBlock textBlock:
if (textBlock.Name == "TabbedPageHeaderTextBlock")
HandleReselect(textBlock);
break;
}
}
private void HandleReselect(FrameworkElement frameworkElement)
{
var newPage = frameworkElement.DataContext as Xamarin.Forms.Page;
if (newPage == _prevPage)
{
var mainPage = Element as TabReselectDemo.MainPage;
mainPage.NotifyTabReselected();
}
_prevPage = newPage;
}

Hopefully our luck will continue to hold and the names of the tab elements stay stable in future releases.

The Full Code

Here’s a fairly barebones app to demonstrate the key elements:

https://github.com/david-js/XFTabReselect/tree/master

This version has a a little extra to make it a little more interesting, like NavigationPage as the TabbedPage children and showing “pop to root” behavior on retap:

https://github.com/david-js/XFTabReselect/tree/NavigationPage

Author

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.