Xamarin has been popping up on my radar for quite a while now. Of course, I have memories of the pre-history around Novell and Mono and then onto Moonlight and those things and I know a few people over at Xamarin but I’d say that in maybe the past 12 months I’m finding that folks that I wouldn’t expect to know about it (i.e. those that don’t live in a .NET or development world) are increasingly mentioning Xamarin to me.
I should say – this is all credit to Xamarin for the work they do both in the products they build and in their promotion and the community they are building. It’s impressive to see the momentum.
If you haven’t encountered them (where’ve you been?) then Xamarin are a Microsoft/Visual Studio partner and they have “Xamarin for Visual Studio” which provides a means via which you can write C# code for apps built for iOS and Android. Naturally, you can already write C# code to build apps for Windows/Phone.
Some of the folks that I know at Xamarin will be laughing at me when I say that I’ve been “getting around” to properly trying out Xamarin for longer than I’d care to admit.
So, today I thought I’d try it out. I thought I’d write a bit of code that I can use across Windows Store, then Windows Phone and then Android. My intention is to do the first 2 pieces without Xamarin and then download and install Xamarin and see what it’s like to get what I’ve already written on Android.
I should point out that I know very little about Android development although I have built the bare bones of an app or two but it’s been very “hello world” type of stuff.
In order to get something going, I’m going to re-use some of the bits that I put together for my “Windows 8/Windows Phone: Building for Both” blog post primarily because I already have those bits and so it means I spend less time getting started. It’s the same (or very similar) code that I used to experiment with the Prism framework back in this post as well but I’m going to move it around a little in this post.
The “app” that came from that post is very simple in just presenting 3 screens.
- Screen 1 – offers a search box and goes off to search flickR for photos that match that search term. tapping on a photo navigates to screen 2.
- Screen 2 – display a list of search results. If the user taps on a photo that causes navigation across to screen 3.
- Screen 3 – display one image with more detail. If the user chooses to, they can save that image to their picture library.
and that’s it. I’m going to try and broadly follow a “Model View ViewModel” approach and I suspect that in doing so I might hit some troubles at the point where I get to Android but that’s part of the fun/challenge. When I used this example to talk about Prism code I ended up with 3 screens which looked something like;
and I’ll probably steal that “UI” and although it would probably be sensible to combine screen 1 and screen 2 I’ll leave them separate here as I have those pieces already.
Getting Started
I kicked off by creating a solution called FlickrCode and dropped two app projects into it (using the blank app template) – one called WindowsApp and another PhoneApp;
I then wanted to have at least one portable class library shared between these 2 projects which I could use to share both implementation and abstractions that are going to be common across both platforms. I chose the “Windows 8+” and “Windows Phone 8” platform options and included .NET 4.5 because that effectively comes for free;
that leaves me to reference the CrossPlatform project from both the other projects;
Building out the Cross Platform Code
Because I’m moving various pieces that I already have into a new place rather than writing from scratch I already know pretty much what I’m going to end up with for this particular app. The structure that ends up inside of my portable class library is as below;
That’s quite a lot of portable code (and note the dependence on System.Net.Http (not Windows.Web.Http)) and it’s broken down into a few areas;
Services
I have 3 services that my code is ultimately going to depend upon.
- A service which knows how to do page->page navigation. Because Windows Phone and Windows 8 apps handle navigation slightly differently, this would be a platform specific service so there’s no implementation in this project.
- A service which knows how to save a photograph into the photos library of the device. Again, this would be a platform specific service so there’s no implementation in this project.
- A service which knows how to get some information from the flickR service online. This service is entirely portable and so there is an implementation in this project.
Those services look like this (in their shape – I’m going to omit the implementation here);
Those interfaces probably speak for themselves even without detailing the parameters/return types. The one that I suspect I’m going to have more trouble with when I get around to Android is the INavigationService interface because it’s built on an assumption of page->page navigation and that such navigation involves some kind of Frame that actually knows how to switch pages. It’s worth saying that in order to be portable, the RegisterFrame() method here takes an object parameter because it’s not possible to make use of a Frame in a portable class library (because it’s not portable ).
It’s questionable as to whether this whole aspect of “navigation” is one that’s actually cross-platform at all here. It just so happens that Windows/Phone work that way. Other platforms may not.
I also built out a little portable base class that I called Locator. I toyed with this for a while but decided that I would take a dependency here on Autofac because that’s available (from Nuget) as a portable IoC container and so I wrote this locator;
using Autofac; using CrossPlatform.ViewModel; using System; namespace CrossPlatform.Service { public class Locator { public Locator() { } public void Initialise(Type navigationServiceImplementation, Type photoServiceImplementation) { ContainerBuilder builder = new ContainerBuilder(); // view models builder.RegisterType<SearchPageViewModel>().AsSelf(); builder.RegisterType<SearchResultsPageViewModel>().AsSelf(); builder.RegisterType<PhotoDetailsPageViewModel>().AsSelf(); builder.RegisterType<SearchResultViewModel>().AsSelf(); // portable service builder.RegisterType<FlickrService>().As<IFlickrService>(); // platform specific services builder.RegisterType(navigationServiceImplementation).As<INavigationService>().InstancePerLifetimeScope(); builder.RegisterType(photoServiceImplementation).As<IPhotoSavingService>(); this._container = builder.Build(); } public T Resolve<T>() { return (this._container.Resolve<T>()); } public object this[string viewModelName] { get { return (this.Resolve(viewModelName)); } } object Resolve(string viewModelName) { var type = Type.GetType(viewModelName); return (this._container.Resolve(type)); } IContainer _container; } }
with the idea here being that this already registers all the view models and the IFlickrService so that’s nice and I ultimately want to drop an instance of this class as a resource in my app.xaml such that Views can use databinding to reach into it and extract their view models with the appropriate dependencies already resolved via Autofac.
The downside here is that something in my code has to call Initialise() on this object but, equally, I have a service (INavigationService) which needs to be passed a reference to a Frame when the application starts up so there’s no easy way to avoid interacting with this Locator at some point in the early life of the app.
Model
All these methods on the services deal in simple types (including Task) with the exception of IFlickrService.SearchAsync which returns a collection of the model class for my app – the details of the photos that it has found from flickR;
which is a fairly simple thing.
Platform
Another simple thing is the SimpleCommand class living in the Platform subfolder which simply implements ICommand;
along with the ViewModelBase class which is sitting in the same folder and provides an implementation of INotifyPropertChanged;
but the implementation makes use of attributes like [CallerMemberName] as per below so I wonder whether that’ll in any way stop this .NET code being portable to Android?
namespace CrossPlatform.Platform { using System; using System.ComponentModel; using System.Runtime.CompilerServices; public abstract class ViewModelBase : INotifyPropertyChanged { public event PropertyChangedEventHandler PropertyChanged; protected bool SetProperty<T>(ref T storage, T value, [CallerMemberName] String propertyName = null) { if (object.Equals(storage, value)) return false; storage = value; this.OnPropertyChanged(propertyName); return true; } protected void OnPropertyChanged([CallerMemberName] string propertyName = null) { var eventHandler = this.PropertyChanged; if (eventHandler != null) { eventHandler(this, new PropertyChangedEventArgs(propertyName)); } } } }
ViewModels
There’s ultimately 3 pages in this app. The first page simply displays a TextBox (for search text) and a Button (to launch the search) and so the ViewModel that underpins it is not overly complex;
So, nothing too surprising there – a ViewModel that surfaces a SearchTerm and a SearchCommand to be data-bound into the UI. The class also relies on the INavigationService so that when the search term is entered, it can drive navigation to the search results page passing the search term as a navigation parameter.
That search results page has a ViewModel which (on construction) picks up the parameter passed from the previous page, it stores this into its own SearchTerm property for display on the UI and then uses the IFlickrService implementation that it depends upon in order to search flickR for photos. For each photo that’s returned (modelled by the FlickrSearchIterm class) it adds a new ViewModel of a different type (SearchResultViewModel) to a collection that it maintains called SearchResults.
The ViewModel also has a BackCommand which invokes the INavigationService to go backwards in the navigation stack. As I mentioned, the SearchResults property is a collection of SearchResultViewModel which looks as below;
Essentially, this ViewModel is simply wrapping the model classes returned from the IFlickrService and adding one important property – the InvokeCommand which is what would be invoked if a user taps on this particular item in the UI. The implementation of that command uses the INavigationService to navigate to the final page in the app – the photo details page passing the Id of the image in question so that its details can be displayed.
That leads on to the final ViewModel which supports that photo details page;
Once again, at construction time this ViewModel uses the INavigationService to retrieve the Id parameter passed to it. It places that value into the Id property and it then uses the IFlickrService to go and query the Title and the ImageUrl for a larger image than was displayed on the previous page. It drops those values into the Title and ImageUrl properties respectively. There is again a BackCommand which uses the INavigationService and there’s also a SaveCommand which uses the IPhotoSavingService to request that the implementation saves the photo into the photos library.
Windows 8.1 UI
With all that in place it’s time to build a basic Windows 8.1 UI to host these ViewModels and display something on the screen. I used the blank template and, first off, I tweaked my app.xaml to include my Locator class;
<Application x:Class="WindowsApp.App" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:WindowsApp" xmlns:svc="using:CrossPlatform.Service"> <Application.Resources> <svc:Locator x:Key="viewLocator" /> </Application.Resources> </Application>
and I modified the code for the App class such that it Initialises the Locator when the app starts up and also passes the Frame which the start up code creates through to the INavigationService. The standard start-up code from a blank template got modified to include (I missed out the rest of the code);
sealed partial class App : Application { public App() { this.InitializeComponent(); this.Suspending += OnSuspending; } Locator Locator { get { Locator locator = (Locator)this.Resources["viewLocator"]; return (locator); } } T Resolve<T>() { return (this.Locator.Resolve<T>()); } protected override void OnLaunched(LaunchActivatedEventArgs e) { Frame rootFrame = Window.Current.Content as Frame; // Do not repeat app initialization when the Window already has content, // just ensure that the window is active if (rootFrame == null) { // Create a Frame to act as the navigation context and navigate to the first page rootFrame = new Frame(); this.Locator.Initialise(typeof(SimpleNavigationService), typeof(PhotoSavingService)); this.Resolve<INavigationService>().RegisterFrame(rootFrame); sealed partial class App : Application { public App() { this.InitializeComponent(); this.Suspending += OnSuspending; } Locator Locator { get { Locator locator = (Locator)this.Resources["viewLocator"]; return (locator); } } T Resolve<T>() { return (this.Locator.Resolve<T>()); } protected override void OnLaunched(LaunchActivatedEventArgs e) { Frame rootFrame = Window.Current.Content as Frame; // Do not repeat app initialization when the Window already has content, // just ensure that the window is active if (rootFrame == null) { // Create a Frame to act as the navigation context and navigate to the first page rootFrame = new Frame(); this.Locator.Initialise(typeof(SimpleNavigationService), typeof(PhotoSavingService)); this.Resolve<INavigationService>().RegisterFrame(rootFrame);
Naturally, I had to implement both the SimpleNavigationService and PhotoSavingService – those got written;
and then the remainder of the work here is to remember to set access to the Pictures Library in the app manifest and then implement my 3 simple pages;
which data-bind suitable properties on the ViewModels. This is the XAML for the initial SearchPage – not that it finds its ViewModel by that rather clunky use of the Locator;
<Page x:Class="WindowsApp.Views.SearchPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:WindowsApp.Views" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d" DataContext="{Binding Source={StaticResource viewLocator},Path='[CrossPlatform.ViewModel.SearchPageViewModel,CrossPlatform]'}"> <Grid> <Grid.Background> <ImageBrush ImageSource="ms-appx:///Assets/backdrop.png" Stretch="UniformToFill" /> </Grid.Background> <Grid.RowDefinitions> <RowDefinition Height="100" /> <RowDefinition Height="40" /> <RowDefinition /> </Grid.RowDefinitions> <Grid.ColumnDefinitions> <ColumnDefinition Width="120" /> <ColumnDefinition /> </Grid.ColumnDefinitions> <TextBlock HorizontalAlignment="Left" TextWrapping="Wrap" Text="search" VerticalAlignment="Bottom" FontFamily="Global User Interface" Style="{StaticResource HeaderTextBlockStyle}" Grid.Column="1" /> <StackPanel Grid.Column="1" Grid.Row="2" VerticalAlignment="Top" Margin="24"> <TextBox Text="{Binding SearchTerm, Mode=TwoWay}" FontSize="36" Height="64" Margin="0,0,0,24" Background="Black" Foreground="White" /> <Button HorizontalAlignment="Left" VerticalAlignment="Top" Command="{Binding SearchCommand}" Template="{x:Null}"> <Image Source="ms-appx:///Assets/search.png" Width="192" /> </Button> </StackPanel> </Grid> </Page>
and that ultimately (via the ViewModel) causes navigation to the SearchResultsPage.xaml page;
<Page x:Class="WindowsApp.Views.SearchResultsPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:WindowsApp.Views" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d" DataContext="{Binding Source={StaticResource viewLocator},Path='[CrossPlatform.ViewModel.SearchResultsPageViewModel]'}"> <Page.Resources> <DataTemplate x:Key="GridViewItemTemplate"> <Button Template="{x:Null}" Command="{Binding InvokeCommand}"> <Grid Width="250" Height="250"> <Border Background="{StaticResource ListViewItemPlaceholderBackgroundThemeBrush}"> <Image Source="{Binding ImageUrl}" Stretch="UniformToFill" AutomationProperties.Name="{Binding Title}" /> </Border> <StackPanel VerticalAlignment="Bottom" Background="{StaticResource ListViewItemOverlayBackgroundThemeBrush}"> <TextBlock Text="{Binding Title}" Foreground="{StaticResource ListViewItemOverlayForegroundThemeBrush}" Style="{ThemeResource TitleTextBlockStyle}" Height="60" Margin="15,0,15,0" /> </StackPanel> </Grid> </Button> </DataTemplate> </Page.Resources> <Grid> <Grid.Background> <ImageBrush ImageSource="ms-appx:///Assets/backdrop.png" Stretch="UniformToFill" /> </Grid.Background> <Grid.RowDefinitions> <RowDefinition Height="140" /> <RowDefinition Height="*" /> </Grid.RowDefinitions> <!-- Back button and page title --> <Grid> <Grid.ColumnDefinitions> <ColumnDefinition Width="Auto" /> <ColumnDefinition Width="*" /> </Grid.ColumnDefinitions> <Grid> <Grid.ColumnDefinitions> <ColumnDefinition Width="120" /> <ColumnDefinition Width="*" /> </Grid.ColumnDefinitions> <Button x:Name="backButton" Margin="39,59,39,0" Command="{Binding BackCommand}" Style="{StaticResource NavigationBackButtonNormalStyle}" VerticalAlignment="Top" AutomationProperties.Name="Back" AutomationProperties.AutomationId="BackButton" AutomationProperties.ItemType="Navigation Button" /> <TextBlock x:Name="pageTitle" Text="{Binding SearchTerm}" Style="{StaticResource HeaderTextBlockStyle}" Grid.Column="1" IsHitTestVisible="false" TextWrapping="NoWrap" VerticalAlignment="Bottom" Margin="0,0,30,40" /> </Grid> </Grid> <GridView Grid.Row="1" Margin="120,0,0,0" ItemsSource="{Binding SearchResults}" ItemTemplate="{StaticResource GridViewItemTemplate}" /> </Grid> </Page>
which then navigates to the PhotoDetailsPage.xaml page;
<Page x:Class="WindowsApp.Views.PhotoDetailsPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:WindowsApp.Views" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d" DataContext="{Binding Source={StaticResource viewLocator},Path='[CrossPlatform.ViewModel.PhotoDetailsPageViewModel]'}"> <Page.BottomAppBar> <CommandBar> <CommandBar.PrimaryCommands> <AppBarButton Icon="Save" AutomationProperties.Name="Save" Command="{Binding SaveCommand}" Label="Save" /> </CommandBar.PrimaryCommands> </CommandBar> </Page.BottomAppBar> <Grid> <Grid.Background> <ImageBrush ImageSource="ms-appx:///Assets/backdrop.png" Stretch="UniformToFill" /> </Grid.Background> <Grid.RowDefinitions> <RowDefinition Height="140" /> <RowDefinition Height="*" /> </Grid.RowDefinitions> <Grid> <Grid.ColumnDefinitions> <ColumnDefinition Width="120" /> <ColumnDefinition Width="*" /> </Grid.ColumnDefinitions> <Button x:Name="backButton" Margin="39,59,39,0" Command="{Binding BackCommand}" Style="{StaticResource NavigationBackButtonNormalStyle}" VerticalAlignment="Top" AutomationProperties.Name="Back" AutomationProperties.AutomationId="BackButton" AutomationProperties.ItemType="Navigation Button" /> <TextBlock x:Name="pageTitle" Text="{Binding Title}" Style="{StaticResource HeaderTextBlockStyle}" Grid.Column="1" IsHitTestVisible="false" TextWrapping="NoWrap" VerticalAlignment="Bottom" Margin="0,0,30,40" /> </Grid> <Image Grid.Row="1" Margin="120,0,120,120" Stretch="Fill" Source="{Binding ImageUrl}" /> </Grid> </Page>
and that’s then the entirety of the Windows 8 App (such as it is) – there’s no code-behind the XAML files and the majority of the code is in the portable library;
Windows Phone UI
The process in creating the Windows Phone version of the app is pretty much the same thing as the Windows 8 version of the app. That is;
- Set up my service locator in app.xaml
- Initialise it in app.xaml.cs
- Create 3 pages with very similar data-bound controls as the windows 8 pages.
- Implement the INavigationService and IPhotoSavingService for Windows Phone.
These steps take some time and (4) needs more thought than the other three steps because I actually need to write some code at that point whereas the other steps are already well-understood.
One thing I’d say is that because on Windows Phone you can’t really bind commands to app bar buttons, implementing the photo details page of the app becomes a bit tedious so I made use of the AppBarUtils library just to make my one app bar button bindable to an ICommand.
Also, because the code I wrote for the phone app uses HttpClient in order to implement the IPhotoSavingService and because Windows Phone 8 doesn’t have HttpClient (either the .NET one or the WinRT one) I brought in a reference to that package from Nuget.
At the end of that process, I have a project that’s very similar in structure to the Windows 8.1 project;
and just like the Windows 8.1 project, there’s only a small amount of UI/code in this project – most of the code is coming from the portable class library.
Summing Up
At the end of all that, I’ve got 2 incomplete “apps” drawing most of their implementation from a portable class library.
The code for all that is shared here.
The next steps are for me to attempt to replicate the part of the work that was involved in implementing the Windows8.1/Windows Phone 8 clients for Android.
That involves first installing the Xamarin bits into Visual Studio and then hoping that I can figure out how to build 3 “pages” ( I’m not sure if “paging” is even going to be a valid paradigm ) and the implementation of my INavigationService ( if it’s needed ) and my IPhotoSavingService. That’s assuming that the portable class library I’ve built can be used and referenced from an Android Xamarin project ( who knows? ).
The next steps could take “a while” but I’ll report back on progress in subsequent posts and I’ll try and be more granular about the intermediate steps as I’m breaking new ground ( for me ).