{"id":92,"date":"2018-06-25T10:00:56","date_gmt":"2018-06-25T08:00:56","guid":{"rendered":"https:\/\/billigeplaetze.com\/?p=92"},"modified":"2024-08-14T13:46:37","modified_gmt":"2024-08-14T11:46:37","slug":"xamarin-tabbed-page-navigation","status":"publish","type":"post","link":"https:\/\/craftcoders.app\/xamarin-tabbed-page-navigation\/","title":{"rendered":"Xamarin: Tabbed Page Navigation"},"content":{"rendered":"

If you have a background in native mobile development and for some reason had to switch to Xamarin, you are probably familiar with that “oh yeah, it doesn’t work this way here” feeling. Having to find new solutions for problems you haven’t even considered to be problems before is what I would call business as usual in Xamarin. Call me a weirdo, but that is also what made me enjoy learning the technology so much.<\/p>\n

The project I had to dive into cross-platform development with, was a big adventure on its own – a very ambitious idea, a rather vague picture of the expected result (and therefore constantly changing requirements), a critical lack of time and the total of 3 developers, all rather new to the technology. Time pressure, general cluelessness about Xamarin and no expert looking over our shoulder turned into hours of online research, in a desperate hope that someone has already encountered the exact same issue. I know that, as any other developer, you have been there yourself, and I don’t have to tell you about that inexplicable relief of finding what you were looking for. Just as much as I don’t have to elaborate on the frustration of trying to fix the problem that you’ve created, trying to fix the problem that you’ve created, trying to fix the problem…<\/p>\n

\"\"<\/p>\n

One of the issues, which has nearly caused such infinite loop of research, was connected to building up a pretty common navigation concept. Here is what we were trying to achieve:<\/p>\n

\"\"<\/p>\n

After logging in, a user should see their dashboard. From there they can navigate by switching between individual tabs. Those are always present on the bottom of the screen, “framing” any active content. Each tab has it’s own navigation tree, meaning one can go back and forth between pages inside any tab. If a user decides to switch between tabs, the app remembers current position in all the trees. This way one can always pick up from where they left the tab, no matter what has happened in the meantime.<\/p>\n

This navigation concept has been around for a while, and you could probably name a couple of apps using it off the top of your head. However, just like I have mentioned earlier, things in Xamarin are not necessarily what, where and how you are used to. It’s not rocket science, but if you are new to the platform, this little tab page navigation tutorial could hopefully save you some headache. Let’s dive right into it.<\/p>\n

Xamarin Navigation Basics<\/h3>\n

1. Defining MainPage<\/strong>
\nThe root page of your application is defined in App.xaml.cs<\/code>, which is a part of the shared code. Here you can tell your app, which page to display on start.<\/p>\n

public App()\r\n{\r\n    InitializeComponent();\r\n    MainPage = new MyMainPage();\r\n}   \r\n<\/code><\/pre>\n

2. Hierarchical navigation<\/strong>
\nHierarchical navigation is the most basic way to move between pages in Xamarin. You can think of it as a stack of cards in solitaire. The first card in the stack is your root page. You can put new pages (or cards) on top of it in any order you like, as long as you comply with a couple of predefined rules. In a solitaire, these rules would tell you, say, to only put lower ranked cards on top of higher ranked ones. In a Xamarin app, the rules define legitimate navigation paths. They are nested in the code and influence the way a user can interact with your app.<\/p>\n

So how does it look from a less abstract, more code-oriented perspective? Your navigation tree is represented by a NavigationPage<\/code>. You can pass the root page of the tree as a constructor argument, like this:<\/p>\n

var myNavigationPage = new NavigationPage(new MyPage());\r\n<\/code><\/pre>\n

Now that you have your base card on the table, you can add some more on top of it, by performing a push operation. In this example, you push a new page on the navigation stack after a button is clicked:<\/p>\n

private async void Handle_MyButtonClicked(object sender, EventArgs e)\r\n{\r\n    await Navigation.PushAsync(new MyOtherPage());\r\n}\r\n<\/code><\/pre>\n

If you want the top page of the stack to disappear and show the page below, do so by calling the pop method of Navigation<\/code> property:<\/p>\n

private async void Handle_DismissButtonClicked(object sender, EventArgs e)\r\n{\r\n    await Navigation.PopAsync();\r\n}\r\n<\/code><\/pre>\n

Now that you can manipulate your stack, it’s time to check out, how tabbed pages work in Xamarin.<\/p>\n

Tabbed page navigation<\/h3>\n

Step 1. Creating a Tabbed Page<\/strong><\/p>\n

public partial class MyTabbedPage : TabbedPage\r\n{\r\n    public MyTabbedPage()\r\n    {\r\n        \/\/ Your constructor code here\r\n    }\r\n}\r\n<\/code><\/pre>\n

All we do here is inherit from a TabbedPage<\/code> class, which is provided by Xamarin. It is important to remember, that tabs will not render the exact same way on different platforms. On iOS, they will show up at the bottom of the screen, beneath the actual page content. Android, on the contrary, displays tabs on the top of the page. There are many more differences in tab behavior across the two platforms, the most important ones covered here<\/a>.<\/p>\n

Note:<\/strong> For our scenario, we had to go for a unified look (icon tabs on the bottom) for both, iOS and Android. If this is what you are going for as well, check out BottomTabbedPage<\/code> control by Naxam<\/a>.<\/p>\n

Step 2. Making TabbedPage to your root<\/strong>
\nAll you are doing here is telling your App to display the tabbed page you’ve created in the previous step on start. Use MainPage property to do so.<\/p>\n

public App()\r\n{\r\n    InitializeComponent();\r\n    MainPage = new MyTabbedPage();\r\n}   \r\n<\/code><\/pre>\n

Step 3. Filling the tabs<\/strong>
\nHow many tabs is your app going to have and what are they going to be? When you’ve decided on the navigation concept and created all the necessary pages, all you need to do is pass them to the tabbed page.<\/p>\n

Let’s go back to our solitaire metaphor for this one. Imagine, that your game has a row of three cards in the beginning. Maybe, two of those are the basis for stacks a player needs to build, and the third one is just a single card, pointing at a trump. How do you create an analogy of this constellation within a tabbed page?<\/p>\n

The game field you lay out your cards on is your tabbed page. It should display three cards in one row so you will need the total of three tabs. One of them is very simple since it only has one card in it. Nothing goes on top of that card, and the player does not have the power to alter it. For our app, it means that no navigation takes place inside the tab. This behavior is going to be represented by a ContentPage<\/code>.<\/p>\n

The other two cards have a more complex role. They will become stack bases. Cards are going to be put on top of them, and, maybe, taken back off. For our pages, this means that they are going to become roots of their own navigation trees, which users will interact with. By pressing buttons or swiping, they will travel from one page to another, pushing them on or popping them off the stack. To enable this, we will need a NavigationPage<\/code> class to wrap around our root page. It will allow us to use the push and pop methods and let our users travel up and down the stack.<\/p>\n

An important notice here:<\/strong> you should not wrap TabbedPage<\/code> itself into a NavigationPage<\/code>. iOS does not support this, and you are going to run into troubles trying to do so. Your concept should base on populating a TabbedPage<\/code> with ContentPages<\/code> and NavigationPages<\/code> only.<\/p>\n

Finally, you can add content and navigation pages to the TabbedPage<\/code> using its Children<\/code> property:<\/p>\n

public Tabbs()\r\n{\r\n    \/\/ Your constructor code here\r\n    Children.Add(new NavigationPage(new MyPage());\r\n    Children.Add(new NavigationPage(new MyOtherPage());\r\n    Children.Add(new MyContentPage());\r\n}\r\n<\/code><\/pre>\n

Step 4. Manipulating the stacks<\/strong>
\nNavigationPages<\/code> give you a couple of methods to work with the stack. We are only going to look closely at the two most basic ones today: PushAsync()<\/code> and PopAsync()<\/code>. These methods can be called on the Navigation<\/code> property of a Page<\/code> and allow you to put a new card on top of your stack or remove the top card.<\/p>\n

Any class, which derives from Page<\/code> provides you with a Navigation<\/code> property. Sometimes, however, it might be useful to manipulate the navigation tree from outside the pages themselves. In order to expose the tree to other classes in the app, you might consider declaring a public variable in your App<\/code> class:<\/p>\n

public static INavigation Nav { get; set; }\r\npublic App()\r\n{\r\n    InitializeComponent();\r\n    \/\/...\r\n    var myNavigationPage = new NavigationPage(new MyPage());\r\n    Nav = myNavigationPage.Navigation;\r\n}\r\n<\/code><\/pre>\n

After you took care of this, you can access the navigation tree from anywhere inside of your app:<\/p>\n

private async void Handle_SomeNavigationButtonClicked(object sender, EventArgs e)\r\n{\r\n    await App.Nav.PushAsync(new MyOtherPage());\r\n}\r\n<\/code><\/pre>\n

While this step can be useful regardless of the navigation concept of your application, it was absolutely crucial for our multiple-navigation-stacks solution, because we needed to know which branch of our navigation tree we are on at all times.<\/p>\n

Step 5. Knowing where you are<\/strong>
\nHow do you let the App know which tab is currently active so that the pages get pushed to the right stack? One way to achieve this would be to let TabbedPage<\/code> take care of changes by overriding its OnCurrentPageChanged()<\/code> method:<\/p>\n

public partial class MyTabbedPage : TabbedPage\r\n{\r\n    public MyTabbedPage()\r\n    {\r\n        \/\/ Your constructor here \r\n    }\r\n\r\n    protected override void OnCurrentPageChanged()\r\n    {\r\n        base.OnCurrentPageChanged();\r\n        App.Nav = CurrentPage.Navigation;\r\n    }\r\n}\r\n<\/code><\/pre>\n

Now, whenever you are calling the Navigation<\/code> property using App<\/code> class, the active stack is going to be chosen.<\/p>\n

Extras<\/h3>\n

The basic idea of navigation is simple. You have an overall context, your App<\/code> class, which has the information about your current location and provides a mechanism to interact with it. You can use this mechanism from anywhere inside the application to go forward and backward in the navigation stack. Classes requesting such manipulations do not have any knowledge about the selected tab or current position in its navigation stack. All they need to do is ask the App to perform a specific operation on the right stack.<\/p>\n

Depending on your project, you might need some extra functionalities in connection to your TabbedPage<\/code>. Here are two optional steps we had to use in our app:<\/p>\n

Step 6.(Optional) Reset all stacks<\/strong>
\nUsing the concept of separate navigation trees inside a TabbedPage<\/code>, you might also want to be able to reset all of them at once. For instance, if a user logs out, you would not want the app to keep all the navigation stacks for the next logged in user to see. What you would need here is to call PopToRootAsync()<\/code> method on the Navigation<\/code> property of each one of your TabbedPage<\/code> children.<\/p>\n

Step 7. (Optional) Set selected tab<\/strong>
\nSometimes it can also be useful to define selected tab programmatically. In our case, a user should always land on a specific tab after login. To achieve this you can set the CurrentPage<\/code> property of your TabbedPage<\/code>.<\/p>\n

I guess that would be it for our short dive into the unwonted peculiarities of cross-platform development. I hope this post was useful for some of you Xamarin warriors out there. Just like any of us here at Billige Pl\u00e4tze, I would be very happy to hear from you in the comment section below. Any feedback is appreciated.<\/p>\n

Let’s learn from each other.
\nDannynator.<\/p>\n","protected":false},"excerpt":{"rendered":"

If you have a background in native mobile development and for some reason had to switch to Xamarin, you are probably familiar with that “oh yeah, it doesn’t work this way here” feeling. Having to find new solutions for problems you haven’t even considered to be problems before is what I would call business as usual in Xamarin. Call me … Read More<\/a><\/p>\n","protected":false},"author":6,"featured_media":2368,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"inline_featured_image":false,"footnotes":""},"categories":[118,110,120,109,122],"tags":[],"acf":[],"yoast_head":"\nXamarin: Tabbed Page Navigation - CraftCoders.app<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/craftcoders.app\/xamarin-tabbed-page-navigation\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Xamarin: Tabbed Page Navigation - CraftCoders.app\" \/>\n<meta property=\"og:description\" content=\"If you have a background in native mobile development and for some reason had to switch to Xamarin, you are probably familiar with that “oh yeah, it doesn’t work this way here” feeling. Having to find new solutions for problems you haven’t even considered to be problems before is what I would call business as usual in Xamarin. Call me ... Read More\" \/>\n<meta property=\"og:url\" content=\"https:\/\/craftcoders.app\/xamarin-tabbed-page-navigation\/\" \/>\n<meta property=\"og:site_name\" content=\"CraftCoders.app\" \/>\n<meta property=\"article:published_time\" content=\"2018-06-25T08:00:56+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2024-08-14T11:46:37+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/craftcoders.app\/wp-content\/uploads\/2018\/06\/34623039_957422327766963_7040670799488876544_n-850x714-1.jpg\" \/>\n\t<meta property=\"og:image:width\" content=\"850\" \/>\n\t<meta property=\"og:image:height\" content=\"714\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/jpeg\" \/>\n<meta name=\"author\" content=\"Danny Nateme\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Danny Nateme\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"9 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"WebPage\",\"@id\":\"https:\/\/craftcoders.app\/xamarin-tabbed-page-navigation\/\",\"url\":\"https:\/\/craftcoders.app\/xamarin-tabbed-page-navigation\/\",\"name\":\"Xamarin: Tabbed Page Navigation - CraftCoders.app\",\"isPartOf\":{\"@id\":\"https:\/\/craftcoders.app\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/craftcoders.app\/xamarin-tabbed-page-navigation\/#primaryimage\"},\"image\":{\"@id\":\"https:\/\/craftcoders.app\/xamarin-tabbed-page-navigation\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/craftcoders.app\/wp-content\/uploads\/2018\/06\/34623039_957422327766963_7040670799488876544_n-850x714-1.jpg\",\"datePublished\":\"2018-06-25T08:00:56+00:00\",\"dateModified\":\"2024-08-14T11:46:37+00:00\",\"author\":{\"@id\":\"https:\/\/craftcoders.app\/#\/schema\/person\/c5aebaa622b96d8666f6f704f53f3666\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/craftcoders.app\/xamarin-tabbed-page-navigation\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/craftcoders.app\/xamarin-tabbed-page-navigation\/#primaryimage\",\"url\":\"https:\/\/craftcoders.app\/wp-content\/uploads\/2018\/06\/34623039_957422327766963_7040670799488876544_n-850x714-1.jpg\",\"contentUrl\":\"https:\/\/craftcoders.app\/wp-content\/uploads\/2018\/06\/34623039_957422327766963_7040670799488876544_n-850x714-1.jpg\",\"width\":850,\"height\":714},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/craftcoders.app\/#website\",\"url\":\"https:\/\/craftcoders.app\/\",\"name\":\"CraftCoders.app\",\"description\":\"Jira and Confluence apps\",\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/craftcoders.app\/?s={search_term_string}\"},\"query-input\":\"required name=search_term_string\"}],\"inLanguage\":\"en-US\"},{\"@type\":\"Person\",\"@id\":\"https:\/\/craftcoders.app\/#\/schema\/person\/c5aebaa622b96d8666f6f704f53f3666\",\"name\":\"Danny Nateme\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/craftcoders.app\/#\/schema\/person\/image\/\",\"url\":\"https:\/\/secure.gravatar.com\/avatar\/9f1f491b56380e35e17fca92c513a9fd?s=96&d=mm&r=g\",\"contentUrl\":\"https:\/\/secure.gravatar.com\/avatar\/9f1f491b56380e35e17fca92c513a9fd?s=96&d=mm&r=g\",\"caption\":\"Danny Nateme\"},\"url\":\"https:\/\/craftcoders.app\/author\/danny\/\"}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Xamarin: Tabbed Page Navigation - CraftCoders.app","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/craftcoders.app\/xamarin-tabbed-page-navigation\/","og_locale":"en_US","og_type":"article","og_title":"Xamarin: Tabbed Page Navigation - CraftCoders.app","og_description":"If you have a background in native mobile development and for some reason had to switch to Xamarin, you are probably familiar with that “oh yeah, it doesn’t work this way here” feeling. Having to find new solutions for problems you haven’t even considered to be problems before is what I would call business as usual in Xamarin. Call me ... Read More","og_url":"https:\/\/craftcoders.app\/xamarin-tabbed-page-navigation\/","og_site_name":"CraftCoders.app","article_published_time":"2018-06-25T08:00:56+00:00","article_modified_time":"2024-08-14T11:46:37+00:00","og_image":[{"width":850,"height":714,"url":"https:\/\/craftcoders.app\/wp-content\/uploads\/2018\/06\/34623039_957422327766963_7040670799488876544_n-850x714-1.jpg","type":"image\/jpeg"}],"author":"Danny Nateme","twitter_card":"summary_large_image","twitter_misc":{"Written by":"Danny Nateme","Est. reading time":"9 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"WebPage","@id":"https:\/\/craftcoders.app\/xamarin-tabbed-page-navigation\/","url":"https:\/\/craftcoders.app\/xamarin-tabbed-page-navigation\/","name":"Xamarin: Tabbed Page Navigation - CraftCoders.app","isPartOf":{"@id":"https:\/\/craftcoders.app\/#website"},"primaryImageOfPage":{"@id":"https:\/\/craftcoders.app\/xamarin-tabbed-page-navigation\/#primaryimage"},"image":{"@id":"https:\/\/craftcoders.app\/xamarin-tabbed-page-navigation\/#primaryimage"},"thumbnailUrl":"https:\/\/craftcoders.app\/wp-content\/uploads\/2018\/06\/34623039_957422327766963_7040670799488876544_n-850x714-1.jpg","datePublished":"2018-06-25T08:00:56+00:00","dateModified":"2024-08-14T11:46:37+00:00","author":{"@id":"https:\/\/craftcoders.app\/#\/schema\/person\/c5aebaa622b96d8666f6f704f53f3666"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/craftcoders.app\/xamarin-tabbed-page-navigation\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/craftcoders.app\/xamarin-tabbed-page-navigation\/#primaryimage","url":"https:\/\/craftcoders.app\/wp-content\/uploads\/2018\/06\/34623039_957422327766963_7040670799488876544_n-850x714-1.jpg","contentUrl":"https:\/\/craftcoders.app\/wp-content\/uploads\/2018\/06\/34623039_957422327766963_7040670799488876544_n-850x714-1.jpg","width":850,"height":714},{"@type":"WebSite","@id":"https:\/\/craftcoders.app\/#website","url":"https:\/\/craftcoders.app\/","name":"CraftCoders.app","description":"Jira and Confluence apps","potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/craftcoders.app\/?s={search_term_string}"},"query-input":"required name=search_term_string"}],"inLanguage":"en-US"},{"@type":"Person","@id":"https:\/\/craftcoders.app\/#\/schema\/person\/c5aebaa622b96d8666f6f704f53f3666","name":"Danny Nateme","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/craftcoders.app\/#\/schema\/person\/image\/","url":"https:\/\/secure.gravatar.com\/avatar\/9f1f491b56380e35e17fca92c513a9fd?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/9f1f491b56380e35e17fca92c513a9fd?s=96&d=mm&r=g","caption":"Danny Nateme"},"url":"https:\/\/craftcoders.app\/author\/danny\/"}]}},"_links":{"self":[{"href":"https:\/\/craftcoders.app\/wp-json\/wp\/v2\/posts\/92"}],"collection":[{"href":"https:\/\/craftcoders.app\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/craftcoders.app\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/craftcoders.app\/wp-json\/wp\/v2\/users\/6"}],"replies":[{"embeddable":true,"href":"https:\/\/craftcoders.app\/wp-json\/wp\/v2\/comments?post=92"}],"version-history":[{"count":1,"href":"https:\/\/craftcoders.app\/wp-json\/wp\/v2\/posts\/92\/revisions"}],"predecessor-version":[{"id":2369,"href":"https:\/\/craftcoders.app\/wp-json\/wp\/v2\/posts\/92\/revisions\/2369"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/craftcoders.app\/wp-json\/wp\/v2\/media\/2368"}],"wp:attachment":[{"href":"https:\/\/craftcoders.app\/wp-json\/wp\/v2\/media?parent=92"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/craftcoders.app\/wp-json\/wp\/v2\/categories?post=92"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/craftcoders.app\/wp-json\/wp\/v2\/tags?post=92"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}