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:
- Initialize Forms with
Forms.Init()
. - 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