Unleashed: Embedding Xamarin.Forms in Xamarin Native

David Ortinau

If you’re anything like me, when you’re ready to start coding a new application you face the question, “Xamarin Native or Xamarin.Forms?” The only correct answer should be “yes”. In fact, I want to show you how this shouldn’t be a choice at all; you can just start coding with total confidence that you’re on the right path.

Introducing Xamarin.Forms Embedding

At Microsoft Build 2017 we announced Xamarin.Forms Embedding, the ability to take any ContentPage and add it to your native applications.

At its core, Xamarin.Forms is a shared UI toolkit that gives you the ability to write a single UI to target Android, iOS, and UWP. It also provides services to empower rapid application development such as navigation, dependency management, and messaging. With that, you may write your entire application in Xamarin.Forms and never need to touch the underlying native OS layers. But there is an underlying layer!

Xamarin.Forms is built upon the foundation of Xamarin.iOS and Xamarin.Android, our top of the line solution for producing highly performant native applications with 100% access to platform APIs.

 

Let’s see how this looks on iOS.

Adding A Xamarin.Forms Page to Xamarin.iOS

The weather has been a big story in my hometown of St. Louis this past month, with severe rain and flooding, so naturally, I’m obsessed with what’s going on outdoors. Looking at the Xamarin Weather sample app, you’ll see we have Android, iOS, and UWP native applications.

While it’s fine for searching ad hoc, I wanted to store search history and compare locations. I’ll show you how I did this on iOS and shared this across all three platforms using Xamarin.Forms.

I added a new Shared Project (or PCL) to host the Xamarin.Forms page. As a side note, you could put a ContentPage directly into your native project and write your entire Xamarin native application using XAML for your UI.

The Xamarin.Forms project and each platform project needs a reference to the early pre-release Xamarin.Forms 3.0 package. In the new shared project I have my HistoryPage.xaml and (for demo simplicity) I’ve set my BindingContext to the page. An MVVM pattern would work perfectly fine here as well.

As you review this XAML, you’ll note there’s nothing special going on here; nothing different from if this were a Xamarin.Forms application from top to bottom. This XAML now works everywhere in Xamarin. That’s powerful!

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="Weather.Forms.HistoryPage">
    <Grid BackgroundColor="#002050">
        <Image Source="buildheader.jpg" VerticalOptions="Start" />
        <StackLayout VerticalOptions="Fill">
            <StackLayout.Margin>
                <OnIdiom x:TypeArguments="Thickness">
                    <OnIdiom.Phone>
                        <OnPlatform x:TypeArguments="Thickness">
                            <On Platform="iOS" Value="10,80,10,10"></On>
                            <On Platform="WinRT, UWP, Android" Value="10"></On>
                        </OnPlatform>
                    </OnIdiom.Phone>
                    <OnIdiom.Tablet>
                        <OnPlatform x:TypeArguments="Thickness">
                            <On Platform="iOS" Value="10,80,10,10"></On>
                            <On Platform="WinRT, UWP, Android" Value="10"></On>
                        </OnPlatform>
                    </OnIdiom.Tablet>
                    <OnIdiom.Desktop>
                        <OnPlatform x:TypeArguments="Thickness">
                            <On Platform="WinRT, UWP" Value="20"></On>
                        </OnPlatform>
                    </OnIdiom.Desktop>
                </OnIdiom>
            </StackLayout.Margin>
            <BoxView HeightRequest="120"/>
            <Label Text="Your Places">
                <Label.TextColor>
                        <OnPlatform x:TypeArguments="Color">
                            <On Platform="Android" Value="GhostWhite"></On>
                            <On Platform="UWP, iOS" Value="White"></On>
                        </OnPlatform>
                </Label.TextColor>
                <Label.FontSize>
                    <OnPlatform x:TypeArguments="x:Double">
                        <On Platform="iOS" Value="24"></On>
                        <On Platform="WinRT, UWP, Android" Value="18"></On>
                    </OnPlatform>
                </Label.FontSize>
            </Label>
            <ListView x:Name="HistoryItems" VerticalOptions="Fill" BackgroundColor="Transparent">
                <ListView.ItemTemplate>
                    <DataTemplate>
                        <ViewCell>
                            <Grid>
                                <Grid.ColumnDefinitions>
                                    <ColumnDefinition Width="50"></ColumnDefinition>
                                    <ColumnDefinition></ColumnDefinition>
                                    <ColumnDefinition Width="100"></ColumnDefinition>
                                </Grid.ColumnDefinitions>
                                <Label Text="{Binding WeatherIcon}" FontSize="18" Grid.Column="0" VerticalTextAlignment="Center" TextColor="White">
                                    <Label.FontFamily>
                                        <OnPlatform x:TypeArguments="x:String">
                                            <On Platform="UWP" Value="/Assets/WeatherIcons.ttf#Weather Icons"></On>
                                            <On Platform="iOS" Value="Weather Icons"></On>
                                            <On Platform="Android" Value="WeatherIcons.ttf#Weather Icons"></On>
                                        </OnPlatform>
                                    </Label.FontFamily>
                                </Label>
                                <Label Text="{Binding LocationName}" Grid.Column="1" VerticalOptions="Center" TextColor="White">
                                    <Label.FontSize>
                                        <OnPlatform x:TypeArguments="x:Double">
                                            <On Platform="iOS" Value="18"></On>
                                            <On Platform="WinRT, UWP, Android" Value="14"></On>
                                        </OnPlatform>
                                    </Label.FontSize>
                                </Label>
                                <Label Text="{Binding PostalCode}" VerticalOptions="Center" TextColor="White" Grid.Column="2" VerticalTextAlignment="Center">
                                    <Label.FontSize>
                                        <OnPlatform x:TypeArguments="x:Double">
                                            <On Platform="iOS" Value="18"></On>
                                            <On Platform="WinRT, UWP, Android" Value="14"></On>
                                        </OnPlatform>
                                    </Label.FontSize>
                                </Label>
                            
                            </Grid>
                        </ViewCell>
                    </DataTemplate>
                </ListView.ItemTemplate>
            </ListView>
            <StackLayout Orientation="Horizontal" HorizontalOptions="Center">
                <Label Text="{Binding PlatformName}" TextColor="White" VerticalTextAlignment="Center">
                    <Label.FontSize>
                        <OnPlatform x:TypeArguments="x:Double">
                            <On Platform="iOS" Value="24"></On>
                            <On Platform="WinRT, UWP, Android" Value="18"></On>
                        </OnPlatform>
                    </Label.FontSize>
                </Label>
                <Label TextColor="Red" VerticalTextAlignment="Center">
                    <Label.Text>
                        <OnPlatform x:TypeArguments="x:String">
                            <On Platform="UWP" Value=""></On>
                            <On Platform="iOS" Value=""></On>
                            <On Platform="Android" Value=""></On>
                        </OnPlatform>
                    </Label.Text>
                    <Label.FontSize>
                        <OnPlatform x:TypeArguments="x:Double">
                            <On Platform="iOS" Value="24"></On>
                            <On Platform="WinRT, UWP, Android" Value="18"></On>
                        </OnPlatform>
                    </Label.FontSize>
                    <Label.FontFamily>
                        <OnPlatform x:TypeArguments="x:String">
                            <On Platform="UWP" Value="Segoe MDL2 Assets"></On>
                            <On Platform="iOS" Value="FontAwesome"></On>
                            <On Platform="Android" Value="FontAwesome.otf#FontAwesome"></On>
                        </OnPlatform>
                    </Label.FontFamily>
                </Label>
                <Label Text=" Xamarin.Forms" VerticalTextAlignment="Center" TextColor="White">
                    <Label.FontSize>
                        <OnPlatform x:TypeArguments="x:Double">
                            <On Platform="iOS" Value="24"></On>
                            <On Platform="WinRT, UWP, Android" Value="18"></On>
                        </OnPlatform>
                    </Label.FontSize>
                </Label>
            </StackLayout>
        
        
        </StackLayout>
    </Grid>
</ContentPage>

You get full use of images and custom fonts here, as you would expect. Below in the code you’ll see you can use XAML Compilation to pre-compile the XAML, MessagingCenter for messaging, and all your data bindings work.

[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class HistoryPage : ContentPage
{
	public const string HistoryItemSelected = "HistoryItemSelected";

	public HistoryPage()
	{
		InitializeComponent();

		HistoryItems.ItemsSource = HistoryRecorder.LocationHistory;
		HistoryItems.ItemTapped += HistoryItemsOnItemTapped;

		BindingContext = this;
	}

	public string PlatformName => $"{Device.RuntimePlatform} ";

	private void HistoryItemsOnItemTapped(object sender, ItemTappedEventArgs itemTappedEventArgs)
	{
		var historyItem = itemTappedEventArgs.Item as HistoryItem;

		if (historyItem == null)
		{
			return;
		}

		MessagingCenter.Send(this, HistoryItemSelected, historyItem.PostalCode);
	}
}

To use HistoryPage in the Xamarin.iOS application, I now only need to do two things:

  1. Initialize Forms with Forms.Init().
  2. Add the page to my view.
namespace WeatherApp.iOS
{
    [Register ("AppDelegate")]
    public class AppDelegate : UIApplicationDelegate
    {
        UINavigationController _navigation;
        UIViewController _historyViewController;
        
        ...
        
        public override bool FinishedLaunching (UIApplication application, NSDictionary launchOptions)
        {
            // #1 INITIALIZE
            Forms.Init();
            ...
        }

        public void ShowHistory()
        {
            if (_historyViewController == null)
            {
                // #2 ADD
                _historyViewController = new HistoryPage().CreateViewController();
            }

            _navigation.PushViewController(_historyViewController, true);
        }
    }
}

Note the returned value of the method CreateViewController() is UIViewController. How you interact with that native control from this point follows the same rules as if you had made it a UIViewController from the start—because you did! On Android CreateFragment(*Context*) returns the native Fragment, and on UWP CreateFrameworkElement() returns the native FrameworkElement.

What’s Coming Next

With your help, we’ll continue to validate our implementation and make sure it’s all working as needed. Of the non-UI parts of Xamarin.Forms, the NavigationService is not supported outside of a Xamarin.Forms application, whereas DependencyService and MessagingCenter are fully operational.

There is work yet to be done here. Right now the ContentPage is fully supported, but we also think we can do this at the control (and custom control) level. We need to look closely at Application.Resources and shared styling. We’re also looking at our templates and IDE support for making it easy and elegant to use Xamarin.Forms everywhere.

Start Embedding Today!

The Xamarin.Forms 3.0 preview of Embedding is published to a custom NuGet feed. To get it:

  • Add a new source to your NuGet Manager: https://www.myget.org/F/xamarinforms-dev/api/v3/index.json
  • Check Pre-Release
  • Select and install the package with the feature name “Embedding” – 3.0.0.100-embeddingpreview

The source for this Weather demo application code is available on GitHub: https://github.com/davidortinau/build2017-new-in-xamarin-forms

As you can see, you can start with Xamarin.iOS, Xamarin.Android, and UWP and bring in Xamarin.Forms anywhere it benefits you. Or you can start with Xamarin.Forms and eventually migrate to Xamarin.iOS, Xamarin.Android, UWP, and everywhere else Xamarin.Forms take you in the future. Now you can do more and use your code in even more fantastic ways!

This is a Preview capability, so we need your feedback! Join us in the forums and please share your experience with us.

0 comments

Discussion is closed.

Feedback usabilla icon