Xamarin.Forms: Switching TabbedPage Tabs Programmatically
Sometimes, your app uses tabs to separate different user activities. Sometimes, though, those activities aren’t completely independent. When that happens, you need an action in one tab to cause the user to switch to another tab. I’ll show you how to do that in code using Xamarin.Forms. In case you haven’t already started your tabs, we’ll start from the beginning. (Note: while the UI parts can be done quite well in XAML, this post will do it all in code.)
First, Some Tabs
Just in case you haven’t already made your Xamarin.Forms tab system, we’ll start there. This is as simple as using an instance of TabbedPage
. Since we will want a little more control over things going forward, let’s create a subclass inheriting from it.
public class MainTabbedPage : TabbedPage {
public MainTabbedPage() {
Children.Add(new somePage());
Children.Add(new someOtherPage());
}
}
You simply make this the MainPage
in your App, now, and you get tabs.
public class App : Application {
public App() {
MainPage = new MainTabbedPage();
}
}
If you run your app at this point, though, you’ll notice something fairly obvious missing from your tabs.
No titles, no icons. In fact, on iOS, you can’t even tell where one tab ends and another begins.
Tabs UI Details
Xamarin.Forms will convert the Title
and Icon
properties of its child pages into these tab properties. Let’s add those.
public class MainTabbedPage : TabbedPage {
public MainTabbedPage() {
Children.Add(new somePage() { Title = "Tab 1", Icon = "SomeIcon1.png" });
Children.Add(new someOtherPage() { Title = "Tab 2", Icon = "SomeIcon2.png" });
}
}
The Icon
property is of type FileImageSource. You can create one using the handy implicit conversion from a string as we did in the example above or create one manually.
somePage.Icon = ImageSource.FromFile("SomeIcon1.png"); A FileImageSource will use a platform's native high-resolution image system (e.g., @2x/@3x for iOS, hdpi/xhdpi/xxhdpi for Android). For more information about using image resources in Xamarin.Forms, check out the handy [image guide on the Xamarin Developer Portal](http://developer.xamarin.com/guides/cross-platform/xamarin-forms/working-with/images/).
Switching Tabs By Index
Your users can always switch tabs by tapping them. To switch the selected tab programmatically, you simply change the CurrentPage
property of your TabbedPage.
CurrentPage = Children[1];
Without a reference to your child pages, you would be stuck using the index based on the order they were added. We could certainly do that if we want to hard-code what each index translates to. For instance, we could set it up to confuse our user by randomly switching tabs from the constructor. (Don’t do this.)
// NOTE: please don't randomly switch tabs on your user for fun.
Task.Run(async () => {
var nextChildIndex = 0;
while (true) {
nextChildIndex = nextChildIndex == 0 ? 1 : 0;
await Task.Delay(3000);
Device.BeginInvokeOnMainThread(() => {
CurrentPage = Children[nextChildIndex];
});
}
});
Not that this deserves analysis, but this is not terribly complex (which is ideal for a mock example). We start up an asynchronous Task that constantly waits three seconds before switching to the next tab (index 0 or index 1). We also make sure to swap the current page on the UI thread. Even though it looks like an loop that would lock up the app, the async/await means it is never waiting on the UI thread.
Switching Tabs By Reference
To be able to switch tabs by reference, let’s maintain the child pages in fields. Now, with a few switch functions, we can end up on the desired tab without worrying about indexes. At some point later, if you want to change the order of the tabs, you only need to change the order they are added to Children
.
public class MainTabbedPage : TabbedPage {
readonly Page tab1Page;
readonly Page tab2Page;
public MainTabbedPage() {
tab1Page = new Page1() { Title = "Tab 1 title", Icon="circle.png" };
tab2Page =new Page2() { Title = "Tab 2 title", Icon="square.png" };
// To change tab order, just shuffle these Add calls around.
Children.Add(tab1Page);
Children.Add(tab2Page);
}
public void SwitchToTab1() {
CurrentPage = tab1Page;
}
public void SwitchToTab2() {
CurrentPage = tab2Page;
}
}
From within a tabbed page, you can now call to these methods by way of the Parent property.
public class Page1 : ContentPage {
public Page1() {
var button = new Button() {
Text = "Switch to Tab 2",
};
button.Clicked += async (sender, e) => {
var tabbedPage = this.Parent as TabbedPage;
tabbedPage.SwitchToTab2();
};
Content = new StackLayout {
Children = {
button,
}
};
}
}
In this case, we know that Page1 will only exist within a TabbedPage, but you could also exclude that code, or guard the switch call, with a conditional check to make sure you Parent is TabbedPage
.
Switch and Push?
Now that you have a reference to each tab’s Page, you can also do a little work on the way there during a switch. For example, if your destination tab child is a NavigationPage, you could push a new page on its stack. In our prior example, we could accept a destination Page as a parameter on SwitchToTab1
.
readonly NavigationPage tab1Page;
// ...
public async Task SwitchToTab1(Page pageToPush) {
CurrentPage = tab1Page;
if (pageToPush != null) {
await tab1Page.PushAsync(pageToPush);
}
}
It may not have been obvious, but the signature of our switch function has changed from above. Since the PushAsync
method is asynchronous, we should await it so any calling code can await this call as well. (Note: async void
is potentially dangerous, so we give it a Task return type. For more background on this, check out this video by async-master Lucian Wischik’s
Bonus Quirk: Android With Tabs + Navigation
If you have used Xamarin Forms enough, you know that things are rarely one-to-one between platforms. In this case, if you make a NavigationPage a child of a TabbedPage, it will look a little counter-intuitive on Android. The navigation is done first as the ActionBar and the ActionBar tabs sit directly underneath that bar, almost the opposite of the structure you created. This seems to be the expected representation of tabs in Android, so there may not be a workaround; and if there is, it may confuse Android users expecting the typical layout.
Sample Code
To see what would barely be called “finished code”, check out my Xamarin Forms TabbedPage GitHub repo of what I was working on while writing this article.
Comments
-
Prudvee, 2015-04-16 13:47:51 +0000:
Great tutorial. Helped a lot!
-
Tim, 2017-08-15 19:36:08 +0000:
var parentPage = this.Parent as TabbedPage; parentPage.CurrentPage = parentPage.Children[0]; << works IF the tab was added as a ContentPage, like this:</p>
public MainTabbed() { mappage = new Map(); mappage.Icon = “ic_map.png”; mappage.Title = “Map”; Children.Add(mappage);
searchpage = new Search(); searchpage.Icon = "ic_searche.png"; searchpage.Title = "Search"; Children.Add(searchpage);
……
If I use:
public MainTabbed() { mappage = new NavigationPage(new Map()); mappage.Icon = “ic_map.png”; mappage.Title = “Map”; Children.Add(mappage);
searchpage = new NavigationPage(new Search()); searchpage.Icon = “ic_searche.png”; searchpage.Title = “Search”; Children.Add(searchpage); ……
var parentPage = this.Parent as TabbedPage; » parentPage is null
Any ideas?
-
Paul, 2019-01-31 09:24:38 +0000:
Good one.. T’was a great help Keep it up!!
Tags: Xamarin.Forms, Xamarin.Android, Xamarin.iOS