If you’re looking for how to navigate inside a Xamarin.Forms Tab Page programatically in a MVVM friendly and Test-able manner, without having any XAML-Code-Behind garbage. Welcome to my post!
Keep in mind, since this is an advanced topic, there’s not going to be any step by step intro’s to Prism or MVVM or whatnot ;)!
Sneak Peak!
Here’s a little sneak peak of the outcome of it.
PERKS:
- Switch between Child-Tabs when you are,
- Coming into the TabbedPage
- Already in the TabbedPage
- Coming back to the TabbedPage
- Fully MVVM compatible
- Fully Test-able, yaay!
- Binding, Commands and Interfaces FTW!
- Almost no XAML-code-behind garbage
- Coming back to the TabbedPage, the Child-Tab switching occurs only when the TabbedPage is actually visible
How cool is that eh! 😉
Woot! Let’s get started then…
My MVVM addiction…
You know me, I’m all about that MVVM & Test-able Software Architecture life forever yol! xD
Being hustling with one of the best dot net application craftsman, has made a massive impact on my perspective of software engineering, rather than just writing some code, putting some shit together and make it work. I’m extremely obsessed with architecture of any given application I develop now, long last extendability, and fully test driven approach. High quality, clean code with all of dot net standards and complete separation of Views and ViewModels.
Backstory…
So recently, I was working on this Xamarin.Forms application which was using Prism as the MVVM framework with a fully test driven architecture. There we had all of our Views and ViewModels separately implemented with a clean architecture, whereas we didn’t have a single line of extra XAML-code-behind garbage. 😛
So the requirement was to implement a TabbedPage and which should be able to switch between its Child-Tabs programatically at runtime, to be more specific, we should be able switch the selected Child-Tab when the user is:
- Coming into the TabbedPage
- Already in the TabbedPage
- Coming back to the TabbedPage after navigating forward.
And the most interesting part was this we had to handle this in a fully MVVM and Testable manner. 😮
Now this would have been much easier, if you had taken out the whole MVVM and Test-first aspect out of the equation, with some dirty XAML-code-behind garbage you can easily handle this. But in this situation, I was backed against the wall, How on earth could anyone achieve this?
but as usual, I figured it out!
The Recipe time…
So here’s how I implemented it, in conclusion we’re going to use an Interface that will allow us to bridge the View-ViewModel separation and a Bindable property inside the TabbedPage, that will react to the changes of the ViewModel’s “SelectedTab” property.
That interface is going to be implemented into the ViewModel. Then through the ViewModel we’re going to register that whole instance in our IoC container, when the user navigates into the TabbedPage. The bindable-property of the TabbedPage will be bound to the property in ViewModel. 😀
Which will allow us to access the interface instance from the IoC container from anywhere in our code and change the Selected Child-Tab.
Oh also I forgot to mention that with Prism, by default we can set the selected Tab-Child when we ‘first navigate into the Tab Page’, so handling that scenario is going to be a no-brainer, thus I will not focus on it in this post.
We shall work on navigating inside the TabbedPage when we’re already inside it, and when we’re coming back to the TabbedPage from another page after navigating forward. Which is going to be a piece of cake with our interface implementation approach.
Just to add something extra, we will maintain a flag property inside the TabbedPage, to make sure to allow the Selected Child-Tab switching happens only when the TabbedPage is visible to the user. Just to make it look nicer!
Since all of this is a simple combination of Bindable-Properties, Interfaces and Commands, this whole implementation is fully test-able. Yeah fine, I’ll show you how to write some tests for it as well! 😛
Sounds pretty straight forward eh! 😀 time for coding! 😉
Implementation time…
So just a heads up, my project configuration is as follows.
I’m using a Xamarin.Forms project (dot net Standard 2.0) created with Prism Templates for Visual Studio. And the IoC container is the default Unity container comes with Prism.Forms setup. As of the Tests I’m using a xUnit Dot net project, which I will get into details later.
So before you begin make sure you have the above setting in place.
1. interface to save the day…
First, the simple Interface which is going to save the day like we discussed above…
public interface IMyTabbedPageSelectedTab
{
int SelectedTab { get; set; }
void SetSelectedTab(int tabIndex);
}
There we have a SelectedTab property, and a separate setter method for it, just in case for backup scenario. If you don’t need both then stick to one of them. 😀
2. the TabbedPage…
Alright then let’s get started with our TabbedPage implementation, let’s call it MyTabbedPage.
<TabbedPage
x:Class="AdvPrismTabNavigation.Views.MyTabbedPage"
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:AdvPrismTabNavigation.Views;assembly=AdvPrismTabNavigation"
xmlns:prism="clr-namespace:Prism.Mvvm;assembly=Prism.Forms"
Title="{Binding Title}"
prism:ViewModelLocator.AutowireViewModel="True"
SelectedTabIndex="{Binding SelectedTab}">
<TabbedPage.Children>
<local:TabChild1Page />
<local:TabChild2Page />
<local:TabChild3Page />
</TabbedPage.Children>
</TabbedPage>
There’s the XAML code for our MyTabbedPage, notice how we’re binding the SelectedTabIndex property to the ViewModel’s property which I’ll show later in this post. Meanwhile let’s have add some child pages to our MyTabbedPage as well.
Alright next take a look at the MyTabbedPage‘s code behind stuff, which is very minimal.
public partial class MyTabbedPage : TabbedPage
{
private bool _isTabPageVisible;
...
// SelectedTabIndex
// bindable property goes here...
...
public MyTabbedPage()
{
InitializeComponent();
}
protected override void OnAppearing()
{
base.OnAppearing();
// the tabbed page is now visible...
_isTabPageVisible = true;
// go ahead and update the Selected Child-Tab page..
this.CurrentPage = this.Children[SelectedTabIndex];
}
protected override void OnDisappearing()
{
base.OnDisappearing();
// the Tabbed Page is not visible anymore...
_isTabPageVisible = false;
}
protected override void OnCurrentPageChanged()
{
base.OnCurrentPageChanged();
// when the user manually changes the Tab,
// we need to update it back to the ViewModel...
SelectedTabIndex
= this.Children.IndexOf(this.CurrentPage);
}
}
As I mentioned in the recipe, there’s the flag _isTabPageVisible, we’re going to use to keep track of the Visibility of the TabbedPage. There when we’re coming back to the TabbedPage from a backward navigation, we’re executing the selected Child-Tab according to the SelectedTabIndex bindable property.
Important Note: You can even make the above little chunks of code go away from the XAML-Code-behind, by using triggers and attached properties, which I’m not going to get into here, to maintain the simplicity of the implementation. Come on, use your own creativity people! 😉
Next we’re creating the Bindable Property inside the TabbedPage which will handle the View-ViewModel communication. Let’s call it SelectedTabIndex property.
public static readonly BindableProperty SelectedTabIndexProperty =
BindableProperty.Create(
nameof(SelectedTabIndex),
typeof(int),
typeof(MyTabbedPage), 0,
BindingMode.TwoWay, null,
propertyChanged: OnSelectedTabIndexChanged);
static void OnSelectedTabIndexChanged
(BindableObject bindable, object oldValue, object newValue)
{
if (((MyTabbedPage)bindable)._isTabPageVisible)
{
// update the Selected Child-Tab page
// only if Tabbed Page is visible..
((MyTabbedPage)bindable).CurrentPage
= ((MyTabbedPage)bindable).Children[(int)newValue];
}
}
public int SelectedTabIndex
{
get { return (int)GetValue(SelectedTabIndexProperty); }
set { SetValue(SelectedTabIndexProperty, value); }
}
Looks neat eh, so its a simple Bindable property as you can see, but however we’re handling it’s OnSelectedTabIndexChanged event ourselves because when the value changes from ViewModel’s end we need to update it on our UI’s end, as you can see we’re having a little flag property inside our MyTabbedPage
3. the ViewModel…
Now is the time for MyTabbedPageViewModel stuff to come along. Nothing fancy just a standard ViewModel, but we need a reference to the IoC container (whichever it is you’re using) because we need to register our interface instance in it. This ViewModel as we discussed before is going to implement our IMyTabbedPageSelectedTab interface and its method and property.
public class MyTabbedPageViewModel
: ViewModelBase, IMyTabbedPageSelectedTab
{
private readonly IUnityContainer _unityContainer;
private int _selectedTab;
/// <summary>
/// Binds to the View's property
/// View-ViewModel communcation
/// </summary>
public int SelectedTab
{
get { return _selectedTab; }
set
{
SetProperty(ref _selectedTab, value);
Title = $"My Tabbed Page - Tab [{SelectedTab + 1}]";
}
}
public MyTabbedPageViewModel
(INavigationService navigationService,
IUnityContainer unityContainer)
: base(navigationService)
{
Title = $"My Tabbed Page - Tab [{SelectedTab + 1}]";
this._unityContainer = unityContainer;
// register this instance so we can access
// IMyTabbedPageSelectedTab anywhere in the code
_unityContainer.RegisterInstance<IMyTabbedPageSelectedTab>
(this, new ContainerControlledLifetimeManager());
}
public void SetSelectedTab(int tabIndex)
{
SelectedTab = tabIndex;
}
}
The SelectedTab property in the ViewModel is the one that’s binding to the SelectedTabIndex property in the MyTabbedPage, now you can see the bridge between the View-ViewModel.
Here we’re registering this ViewModel instance by the type of IMyTabbedPageSelectedTab so that we can access it from anywhere using the same type and also we’re passing in ContainerControlledLifetimeManager() parameter because we need to make sure that instance is properly managed by the container and garbage collected later when not in use.
4. finally Consume it…
So here is the little Magic code snippet you need to execute, wherever you wish to have access to programatically switching the Selected Child-Tab of our MyTabbedPage.
_unityContainer.Resolve<IMyTabbedPageSelectedTab>().SetSelectedTab(tabIndex);
You simply access the registered service interface and call on the SetSelectedTab() or simply you could also call the IMyTabbedPageSelectedTab.SelectedTab property directly as well. 😉
Just a little note, well this may not be the best of all approaches but this is what I believe is a better solution given my experience and expertise. But if you have a better alternative, please feel free to share!
Let’s fire it up!
So here’s this bad boy in action…
That’s the Child-Tab being switched programatically while inside the TabbedPage!
And here’s how nicely the Child-Tabs are switching programatically while outside the TabbedPage! As you can see when coming back to the TabbedPage, it nicely moves to the Selected Child-Tab…
Eyyy look at that! 😀
How about UnitTest-ing…
Uh fine, let me show you. 😛
In my case I used xUnit.net for my Test project, along side Prism.Forms and Xamarin.Forms.Mocks for mocking Xamarin.Forms run time.
Switching between Child-Tabs inside the TabbedPage:
// Let's Tab-Navigate to TabChild2Page
_appInstance.Container.Resolve<TabChild1PageViewModel>()
.GoToNextTabCommand.Execute("1");
// Am I in the MyTabbedPage-> TabChild2Page?
Assert.IsType<TabChild2PageViewModel>
(myTabbedPage.CurrentPage.BindingContext);
// Let's Tab-Navigate to TabChild3Page
_appInstance.Container.Resolve<TabChild2PageViewModel>()
.GoToNextTabCommand.Execute("2");
// Am I in the MyTabbedPage-> TabChild2Page?
Assert.IsType<TabChild3PageViewModel>
(myTabbedPage.CurrentPage.BindingContext);
I’m calling the Commands through my child page’s ViewModel and switching the Selected Child-Tab and then asserting to make sure the myTabbedPage instance has updated accordingly.
Switching between Child-Tabs outside the TabbedPage:
// Am I inside the DetailPage?
Assert.IsType<DetailPageViewModel>
(navigationStack.Last().BindingContext);
// Let's go back to Tabbed Page -> TabChild3Page
_appInstance.Container.Resolve<DetailPageViewModel>()
.GoBackToTabChild3PageCommand.Execute();
// Am I inside the MyTabbedPage?
Assert.IsType<MyTabbedPageViewModel>
(navigationStack.Last().BindingContext);
// Am I in the MyTabbedPage-> TabChild3Page?
Assert.IsType<TabChild3PageViewModel>
(myTabbedPage.CurrentPage.BindingContext);
Here you can clearly see I’m calling the GoBackToTabChild3PageCommand in an page(DetailPage) which Ihave navigated to after the TabbedPage, and what happens in that command is I’m changing the Selected Child-Tab in the MyTabbedPage and immediately going back to it by exiting the DetailPage. Then I’m coming back to the MyTabbedPage, and the Child-Tab 3 is selected in the TabbedPage.
Here’s where you could take a look at the full test implementation : https://github.com/AdvPrismTabNavigation.xUnitTest
Voila! 😀 UnitTest-ing Done!
Github it if yo lazy!
So all of this is hosted on my git repo: https://github.com/AdvPrismTabNavigation
Feel free to grab your copy if you’re too lazy to DIY! 😛
That’s it for today.
Cheers all! 😀 Share the love!