Connect(); Special Issue 2018

Volume 33 Number 13

[Cross-Platform Development]

Introducing the Xamarin.Forms Shell

By David Ortinau; Special Issue 2018

Xamarin.Forms is a favored toolkit for cross-platform developers who love XAML and C#, because it maximizes code sharing while also providing full access to all the native platform APIs and UI controls. This capability comprises technologies and concepts that can be both exhilarating and confusing when you’re getting started. The truth is that some developers find it frustrating at the outset. You chose Xamarin to be productive, and the last thing you want to encounter is unwanted hassle. This year at Connect(); we’re thrilled to introduce Xamarin.Forms Shell, a new default starting point for mobile application development that reduces complexity and increases productivity.

As the name suggests, Shell is fundamentally a container that takes care of the basic UI features that every application needs, so you can focus on the core work of your application. Existing iOS and Android applications can also easily adopt Shell and benefit immediately from the improvements to navigation, UI performance and extensibility. Shell offers the following benefits:

  • A single place to describe the visual structure of applications
  • A common navigation UI and omnipresent navigation service with deep linking
  • An integrated search handler to improve the overall in-app search experience
  • An extensible-by-default philosophy to add more versatility and flexibility

The App

At the beginning of every project, someone sketches out the structure of the application for you to build (and hopefully not just in their head). Sometimes it’s provided in a design comp, and sometimes it’s just penciled in on paper. Shell makes it super easy to take that content and translate it into a running application container that’s ready for anyone to populate with content and functionality.

In this article, I’ll use the example of a mobile shopping application called Tailwind Traders. This is a new reference application the team has made to demonstrate how you can use Xamarin.Forms Shell, Azure, Cognitive Services, and several other features and services. Take a look at the design comps provided by our awesome design team, shown in Figure 1.

Design Comps for Tailwind Traders Sample App
Figure 1 Design Comps for Tailwind Traders Sample App

As you can see from the displayed screens, the app provides all the obvious functionality you need, including login and registration flow, a browsing experience with product categories and search, and a checkout flow. This app also leverages the device camera and the power of the Azure Custom Vision API to identify products in real time.

The Quick Start

Let’s quickly scaffold out this application using Shell. Open Visual Studio 2019 and start a new cross-platform application with Xamarin.Forms. For the purpose of this article and to understand the power of Shell, let’s start with a Blank project and build up the structure of the Tailwind Traders app.

Once the project files are generated, open the App.xaml.cs and notice that MainPage is set to a new Shell instance. (You can download Shell templates from aka.ms/xf-shell-templates.) Structurally, this is the only difference from a typical Xamarin.Forms application that you may have seen in the past. Here’s the code:

namespace TailwindTraders.Mobile
{
  public partial class App
  {
    public App()
    {
      InitializeComponent();
      MainPage = new AppShell();
    }
  }
}

Open the AppShell.xaml in the root of your .NET Standard library project, as shown in Figure 2.

Figure 2 A Single Page Shell.xaml

<?xml version="1.0" encoding="UTF-8"?>
<Shell xmlns="http://xamarin.com/schemas/2014/forms"
       xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
       xmlns:local="clr-namespace:TailwindTraders"
       RouteHost="tailwindtraders.com"
       RouteScheme="app"
       FlyoutBehavior="Disabled"
       Title="TailwindTraders"
       x:Class=" TailwindTraders.AppShell">
  <ShellItem>
    <ShellSection>
      <ShellContent>
        <local:MainPage/>
      </ShellContent>
    </ShellSection>
  <ShellItem>
</Shell>

Let’s break down the pieces of this file. A Shell consists of three hierarchical elements: ShellItems, ShellSections and ShellContent. Every ShellContent is a child of a ShellSection, which is a child of a ShellItem—all parts of the Shell. None of these by themselves represent the UI, but rather the organization of your application’s architecture. Shell takes these items and produces the appropriate navigation UI for the platform it’s running on:

  • ShellItem: The top-level structure of the application represented by an item in the flyout. Can contain multiple ShellSections.
  • ShellSection: A grouping of the application content, which is navigable by bottom tabs. Can contain one or more ShellContents, with multiple ShellContents navigable by top tabs.
  • ShellContent: The ContentPages of your application.

I can use these three elements to describe the visual structure of the Tailwind Traders mobile application. Ignoring the login and registration flow, I’ll add several ShellItems to host content, represented with a flyout menu on the left.

Why not use names like Flyout­Item, BottomTab, TopTab for Shell concepts? Our team at Microsoft has had many discussions about this and feels that Xamarin.Forms caters to known and future platforms that sometimes don’t share the exact concepts of tabs or menus. By keeping the nomenclature abstract, we let you decide through styling and templates if these ele­ments should be represented consistently across divergent platforms, or if they should adhere to each platform’s design aesthetic. Of course, your feedback is always welcome in these matters!

Figure 3 provides an example. Here you see a menu in the flyout (the lower two-thirds of the UI), which is populated automatically by ShellItems. This allows you to navigate to the different areas of your application. In addition to those items, you can explicitly add menu items that aren’t associated with a ShellItem. The flyout header at the top (the two buttons) presents special content that consists of anything you wish to present in that space. To declare a custom FlyoutHeader within the Shell.xaml, use this code:

<Shell.FlyoutHeader>
  <local:FlyoutHeader />
</Shell.FlyoutHeader>

Elements of the FlyoutMenu
Figure 3 Elements of the FlyoutMenu

The header element allows you to control how it behaves when users scroll the display. There are three options:

  • Fixed: The header remains fixed while the content below scrolls.
  • Scroll: Scrolls with the menu items.
  • CollapseOnScroll: Collapses in a parallax fashion as you scroll.

To adjust this behavior, set the FlyoutHeaderBehavior property on your Shell to the desired value just previously detailed. For now, we’ll keep it fixed with the following code:

<Shell
  x:Class="TailwindTraders.Mobile.Features.Shell.Shell"
  FlyoutHeaderBehavior="Fixed"  
  ...            
  >             
  ...            
</Shell>

Next, let’s set up the content. Looking at the design, I can see that there’s a home screen, a series of product categories, a profile and, finally, a logout screen. Let’s start with the home screen, with the following XAML code:

<ShellItem Title="Home">
  <ShellSection>
    <ShellContent>
      <local:HomePage />
    </ShellContent>
  </ShellSection>
</ShellItem>

Breaking down this XAML from the inside out, I’ve added the HomePage to the app, which will be the first ContentPage to launch, because it’s the first content declared in the shell file. This is the same ContentPage type you use in your existing Xamarin.Forms applications, now hosted within a Shell context.

For this design I only need to set a title, but ShellItem also provides the FlyoutIcon property, which lets you provide an image to display on the left of the item. Icons may be any Xamarin.Forms ImageSource.

Go ahead and run the app. On the home­page click the hamburger icon to open the flyout menu. Tapping this menu item navigates you to the home screen (which is currently the only screen). Let’s get to work adding more to that.

Next, I’ll implement the product categories, such as “Holiday decorations,” “Appliances” and the like. I could add ShellItems for each of them, but because the product category pages are all the same page with different content, I can be clever about it. I’ll use a simple Menu­Item to navigate to the same page and pass data via the CommandParameter to avoid unnecessary duplication of pages. Here’s the code to add a MenuItem in the Shell.xaml:

<Shell.MenuItems>
  <MenuItem
    Command="{Binding ProductTypeCommand}"
    CommandParameter="1"
    Text="Holiday decorations" />
</Shell.MenuItems>

A great feature of Shell is that it supports data binding. In this case I have a “Command” on a view model that can execute the navigation. Just like ShellItems, MenuItems take text and an icon. Also, just like ShellItems, I can provide styling or even a custom template to further customize the design by setting the MenuItemTemplate property on the Shell.

I can add more menu items for each category to complete the task. Figure 4 shows the code for all the menu items, while Figure 5 shows the visual result in the flyout menu of the app.

Figure 4 All the Menu Items

<?xml version="1.0" encoding="UTF-8" ?>
<Shell
  x:Class="TailwindTraders.AppShell"
  FlyoutHeaderBehavior="Fixed"
  xmlns="http://xamarin.com/schemas/2014/forms"
  xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
  xmlns:local="clr-namespace:TailwindTraders.Views"
  Title="Tailwind Traders"
  x:Name="theShell"
  Route="tailwindtraders"
  RouteHost="microsoft.com"
  RouteScheme="app">
  <Shell.MenuItems>
    <MenuItem
      Command="{Binding ProductTypeCommand}"
      CommandParameter="1"
      Text="Holiday decorations" />
    <MenuItem
      Command="{Binding ProductTypeCommand}"
      CommandParameter="2"
      Text="Appliances" />
    <MenuItem
      Command="{Binding ProductTypeCommand}"
      CommandParameter="3"
      Text="Bathrooms" />
    <MenuItem
      Command="{Binding ProductTypeCommand}"
      CommandParameter="4"
      Text="Doors &amp; Windows" />
    <MenuItem
      Command="{Binding ProductTypeCommand}"
      CommandParameter="5"
      Text="Flooring" />
    <MenuItem
      Command="{Binding ProductTypeCommand}"
      CommandParameter="6"
      Text="Kitchen" />
    <MenuItem
      Command="{Binding ProductTypeCommand}"
      CommandParameter="7"
      Text="Storage" />
  </Shell.MenuItems>
  <ShellItem Title="Home">
    <ShellSection>
      <ShellContent>
        <local:HomePage />
      </ShellContent>
    </ShellSection>
  </ShellItem>
</Shell>

Flyout with All Menu Items
Figure 5 Flyout with All Menu Items

Adding More Pages

Let’s now add the Profile page from the design to our app. Add a new ContentPage to the project and then move back to the Shell.xaml file. You could copy the XAML (shown in Figure 4) used for the HomePage and just replace it with the Profile page, but you risk bogging down the application because HomePage is created immediately during the application startup. To avoid having all the pages of the application loading at once, I use a data template, like so:

<ShellContent
  Title="Profile"
  ContentTemplate="{DataTemplate local:ProfilePage}" />

Rather than providing the ContentPage directly to the content property of the ShellContent, I supply a data template. When the user navigates to a screen, the Shell instantiates the requested page dynamically.

One thing to note with this treatment compared to the Home­Page is that I’ve omitted the ShellItem and ShellSection wrappers and placed the title directly in the ShellContent. This is much less verbose and the Shell knows how to handle it for me by supplying the required logical wrappers. It’s also important to note that these wrappers don’t introduce UI views to the tree. Shell is written with rendering speed and memory consumption in mind. The result of this is that the performance impact on the Android OS is kept low by hosting the same content and UI you currently have within this new Shell context. Of course, you’re still ultimately responsible for architecting the internals of your applications, but Shell offers a great starting point.

Styling the Flyout

You can style aspects of Shell and the FlyoutMenu just as you would any other XAML element using CSS or XAML styling. What if you wish to go further with how the flyout menu item appears? Looking back at the design in Figure 3, the menu items are bolder than the other shell items.

The display of menu items and shell items are extensible by providing a DataTemplate to Shell. A MenuItem is rendered in the flyout menu using the Shell’s MenuItemTemplate, and a ShellItem is rendered using an ItemTemplate. To take full control of how these look, set each property to a DataTemplate containing a custom ContentView. Shell will provide the Title and Icon bindable properties to the template BindingContext, as shown in Figure 6. You can see the visual result in Figure 7.

Figure 6 Customizing the Item Template for ShellItems in Shell.xaml

<Shell.ItemTemplate>
  <DataTemplate>
    <ContentView HeightRequest="32">
      <ContentView.Padding>
        <Thickness
          Left="32"
          Top="16" />
      </ContentView.Padding>
      <Label Text="{Binding Title}" />
    </ContentView>
  </DataTemplate>
</Shell.ItemTemplate>
<Shell.MenuItemTemplate>
  <DataTemplate>
    <ContentView HeightRequest="32">
      <ContentView.Padding>
        <Thickness
          Left="32"
          Top="16" />
      </ContentView.Padding>
      <Label Text="{Binding Text}" FontAttributes="Bold" />
    </ContentView>
  </DataTemplate>
</Shell.MenuItemTemplate>

Images of Flyout Item Template Results
Figure 7 Images of Flyout Item Template Results

In addition to customizing the item renderers, let’s add a nice header to the flyout that includes a box with a label and two buttons for quick access to camera features. As with the other templates, add one for the FlyoutHeaderTemplate in the Shell.xaml file. The content can be any ContentView, so here use a StackLayout to vertically position the child controls, as shown in the code in Figure 8. Add some styling to get it close to the design comp, and run the app to see the result as shown in Figure 9. You can set FlyoutHeaderBehavior in the Shell element to determine if the header is fixed, or if it can scroll or collapse when the user scrolls the screen.

Figure 8 FlyoutHeaderTemplate

<Shell.FlyoutHeaderTemplate>
  <DataTemplate>
    <StackLayout HorizontalOptions="Fill" VerticalOptions="Fill"
      BackgroundColor="White" Padding="16">
      <StackLayout.Resources>
        <Style TargetType="Button">
          <Setter Property="BackgroundColor" Value="White" />
          <Setter Property="BorderColor" Value="#2F4B66" />
          <Setter Property="BorderWidth">2</Setter>
          <Setter Property="CornerRadius">28</Setter>
          <Setter Property="HeightRequest">56</Setter>
          <Setter Property="Padding">
            <Thickness
              Left="24"
              Right="24" />
           </Setter>
         </Style>
       </StackLayout.Resources>
       <Label FontSize="Medium" Text="Smart Shopping">
         <Label.Margin>
           <Thickness Left="8" />
         </Label.Margin>
       </Label>
       <Button Image="photo" Text="By taking a photo">
         <Button.Margin>
           <Thickness Top="16" />
         </Button.Margin>
       </Button>
       <Button Image="ia" Text="By using AR">
         <Button.Margin>
           <Thickness Top="8" />
         </Button.Margin>
       </Button>
     </StackLayout>
   </DataTemplate>
 </Shell.FlyoutHeaderTemplate>

Image of Flyout with Header
Figure 9 Image of Flyout with Header

Now it’s time to implement the command that navigates to the menu item pages. To do so, I’ll use the new URI-based routing that Shell introduces. URIs let users jump instantly to any part of the application, and even provide the ability to go backward without having to create all the pages between the two points. Let’s look at how this is accomplished.

First, I need to declare the routes, beginning with the scheme and host for my app, like so:

<Shell
  Route="tailwindtraders"
  RouteHost="www.microsoft.com"
  RouteScheme="app"

Putting these pieces together into a URL I end up with the following URI: app://www.microsoft.com/tailwindtraders.

Each Shell element I’ve defined in the Shell file also takes a route property, which I can later use to navigate programmatically. For pages that aren’t represented by a Shell element I can explicitly register a route. This is what I’ll do for those menu items added to the flyout. Each of them will navigate to a ProductCategoryPage, a page that displays a list of products for a specific category. Here’s the route registration code:

Routing.RegisterRoute("productcategory", typeof(ProductCategoryPage));

Now I can declare the necessary routes in the constructor of the Shell.cs, or anywhere that runs before the routes are called. Menu items expose a command to implement the necessary navigation, as you can see in this code:

public ICommand ProductTypeCommand { get; } =
  new Command<string>(NavigateToProductType);
private static void NavigateToProductType(string typeId)
  {
    (App.Current.MainPage as Xamarin.Forms.Shell).GoToAsync(
      $"app:///tailwindtraders/productcategory?id={typeId}", true);
  }

Another great benefit of Shell is that it has static navigation methods that are accessible from anywhere in the application. Gone are the days of worrying if the navigation service was available, passing it around from view to view models and adding navigation pages to wrap everything. Now, you can grab a reference to the application Shell, which is the MainPage of your application, accessible as a property of App.Current. You can see this in the previous code snippet. To execute the navigation, call the GoToAsync method, passing in a valid URL as a ShellNavigationState. A ShellNavigationState may be constructed from a string or a URI. Look at the code again, and you can see that GoToAsync also allows you to supply only a string, and Shell will do the work to instantiate a ShellNavigationState.

Data can be passed between views and view models with querystring parameters. Shell will set those values directly on the ContentPage or ViewModel when you decorate the appropriate properties with query property attributes, as shown in Figure 10.

Figure 10 Example of Query Attribute

[Preserve]
[QueryProperty("TypeID", "id")]
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class ProductCategoryPage : ContentPage
{
  private string _typeId;
  public ProductCategoryPage()
  {
    InitializeComponent();
    BindingContext = new ProductCategoryViewModel();
  }
  public string TypeID
  {
    get => _typeId;
    set => MyLabel.Text = value;
  }
}

The QueryProperty takes the public property name (“TypeID” in this example) from your receiving class and the querystring parameter name (“id” in this example) used in the URL.

Intercepting the Back Action

Intercepting Back action is a common requirement in mobile app development, and it can be a challenge with Xamarin.Forms. Shell remedies this problem, by letting you hook into the navigation routing before and after it’s completed to implement a myriad of custom needs. Here’s an example of navigation handling, first with XAML code assigning an event handler:

<Shell           ...
  Navigating="Shell_Navigating"

And then the C# code for the event handler:

private void Shell_Navigating(object sender, ShellNavigatingEventArgs e)
{
  if (// Some Boolean evaluation)
  {
    e.Cancel(); // Do not allow this navigation AND/OR do something else
  }
}

On an instance of Shell, add an event handler to the Navigating event. In your codebehind, the ShellNavigatingEventArgs provides the base details of the navigation, as shown in Figure 11.

Figure 11 ShellNavigatingEventArgs

Element Type Description
Current ShellNavigationState The URI of the current page.
Source ShellNavigatinState The URI representing where the navigation originated.
Target ShellNavigationState The URI representing the navigation destined.
CanCancel Boolean Property indicating if it’s possible to cancel the navigation.
Cancel Boolean Method to cancel the requested navigation.
Canceled Boolean Property indicating if the current navigation was canceled.

Tabs, Tabs, Everywhere Tabs

The flyout menu is a popular UI pattern for navigation. As you think about the hierarchy of content within your application, the top or outermost level of navigation is the flyout menu. From there, the next level of detail is the bottom tab. When you don’t have a flyout present, the bottom tabs are generally considered to be the top level of navigation in an application. Then within the bottom tabs, the next level of navigation would be the top tabs. Beyond that you’re into single pages that push one to another. This is an opinionated approach that Shell takes to providing navigation UI.

Let’s start with bottom tabs. Each ShellSection within a single Shell­Item can be represented as a bottom tab when there’s more than one. Here’s an example of XAML code producing bottom tabs for an app:

<ShellItem Title="Bottom Tab Sample" Style="{StaticResource BaseStyle}">
  <ShellSection Title="AR" Icon="ia.png">
    <ShellContent ContentTemplate="{DataTemplate local:ARPage}"/>
  </ShellSection>
  <ShellSection Title="Photo" Icon="photo.png">
    <ShellContent ContentTemplate="{DataTemplate local:PhotoPage}"/>
  </ShellSection>
</ShellItem>

This code presents two ShellSections in a single ShellItem. These ShellSections are represented in the UI as tabs at the bottom of the screen. What about when you don’t need the flyout? When there’s only one ShellItem, it can be hidden altogether by setting the FlyoutBehavior to Disabled. Tabs can be styled using the existing style options or by supplying a custom renderer. Unlike the flyout menu items that can be customized data templates, the tabs are much more platform-specific. To style the color of the tabs, use the style properties of the Shell class for TabBar items, like so:

<Style x:Key="BaseStyle" TargetType="Element">
  <Setter Property=
    "Shell.ShellTabBarBackgroundColor"
    Value="#3498DB" />
  <Setter Property=
    "Shell.ShellTabBarTitleColor"
    Value="White" />
  <Setter Property=
    "Shell.ShellTabBarUnselectedColor"
    Value="#B4FFFFFF" />
  </Style>

Assigning the style class to the ShellItem applies those colors to all tabs in that section.

Now let’s move on to top tabs. To have content navigable from top tabs, add multiple ShellContent items within a single ShellSection. Styling is applied just like the previous example for the bottom tabs. Here’s the code:

<ShellItem Title="Store Home" Shell.TitleView="Store Home"
  Style="{StaticResource BaseStyle}">
    <ShellSection Title="Browse Product">
      <ShellContent Title="Featured"
        ContentTemplate=
        "{DataTemplate local:FeaturedPage}" />
      <ShellContent Title="On Sale"
        ContentTemplate=
        "{DataTemplate local:SalePage}" />
    </ShellSection>
  </ShellItem>

The Roadmap

There’s plenty more to discover in Xamarin.Forms Shell. I could continue on describing how to customize the navigation bar, the back button, and all about the very powerful search handler that makes it easier than ever to add search to a page. Those features and more are available now, and will be documented as we approach stable release.

The Shell journey is just starting. We hear loud and clear from Xamarin.Forms developers that you often need to make your iOS and Android applications look mostly or exactly the same. To address this, we plan to release Material Shell, which is an implementation of Shell that applies Google’s Material Design styling as the starting point for all supported controls. The controls are still native so there are no performance or feature compromises.

Navigation transitions and segues are also on the way. With transitions you can control how one page animates to another (left-to-right, right-to-left, crossfade, curl and more). Segues are a declarative way to say, “When this button action happens, execute this route.” It reduces the need to write GoToAsync navigation code and expresses with more clarity in XAML how things are connected. Blending transitions and Material Shell together we can provide some additional animations, such as the Hero animation where an element such as an image icon transitions from one page seamlessly to another page.

Start Exploring Today

Xamarin.Forms Shell is available today in the preview of Xamarin.Forms 4.0, which includes amazing new features like CollectionView, CarouselView and the all-new Material Visual that makes it easier than ever to start your Xamarin.Forms applications from a consistent, common UI styling point instead of the platform-specific blank point. Use your Visual Studio NuGet package manager to update to version 4.0-pre1 by toggling the pre-release option.

To make things even easier, we’ve created an updated package of project templates that unify on Shell and provide version 4.0-pre by default. Download and install the templates from aka.ms/xf-shell-templates. After you’ve done that, new Shell-powered templates will be available when you create a new Xamarin.Forms project.

As the Visual Studio 2019 pre-release continues to evolve, so will Xamarin.Forms 4.0 and Shell. We need your feedback. Let us know your thoughts and experiences by visiting aka.ms/xf-4-feedback.


David Ortinau is a senior program manager for Mobile Developer Tools at Microsoft, focused on Xamarin.Forms. A .NET developer since 2002, and versed in a range of programming languages, Ortinau has developed Web, environmental and mobile experiences for a wide variety of industries. After several successes with tech startups and running his own software company, Ortinau joined Microsoft to follow his passion: crafting tools that help developers create better app experiences. When not at a computer or with his family, he’s galloping through the woods.

Thanks to the following Microsoft technical experts for reviewing this article: David Britch, Jason Smith


Discuss this article in the MSDN Magazine forum