Using #Xamarin Forms with #mvvmlight
Update: Sample source code
Update 2: The always awesome Daniel Plaisted made me aware that you can, in fact, add one Nuget package to multiple projects at the same time. Updated the article to reflect this fact.
Update 3: Fixed my faulty assertions about the StaticResource.
A few weeks ago, Xamarin released a new framework on top of their Xamarin solution. This framework called Xamarin Forms allows easy sharing of not only business code (like the “traditional” Xamarin offering) but also UI.
How does it work?
Xamarin created a collection of pages and controls. The package currently includes 5 different page types, 7 layouts and 24 controls. Of course we can expect these numbers to grow any time soon with new elements to build flexible applications.
When you build the application, you select the most appropriate control for your purpose. The existing controls should cover a large range of scenarios.
At run time, the controls are rendered differently for each platform. For example a tabbed page will be rendered differently for on Android, iOS and Windows Phone. On each of these platforms, the most appropriate user experience is used. The screenshots below (taken from the Xamarin Forms documentation) show this in action.
Tabbed page rendered on Windows Phone (pivot)
Tabbed page rendered on Android
Tabbed page rendered on iPhone
And XAML and DataBinding
One thing interesting with Xamarin Forms is the introduction of XAML as UI language for all supported platforms. While you can build the whole UI in code behind (just like on Windows platforms), you can also have a XAML front end coupled with a C# code behind. In addition, and maybe most importantly, Xamarin Forms supports DataBinding! This is great because it lowers the learning curve greatly for people coming from the XAML space into iOS and Android. Even though the XAML looks a bit different from the traditional Windows-based XAML, with different namespaces and different controls, it is a promising first step on getting designer support and a closer development experience from the awesomeness that is the development experience in the Microsoft XAML world.
No designer
At the time of writing, there is no designer support for the Xamarin Forms XAML. If you try to open such a page in Blend, it won’t even render a design surface. However Xamarin Forms is very young and I have no doubt that XAML support will make a designer possible. Looking forward to that, and to enabling design time support through MVVM Light!
Not much intellisense
Almost more annoying than the lack of a designer, Intellisense doesn’t work well in the XAML editor on Visual Studio either. It is when you miss the tools we are used to that we finally realize what an amazing job Microsoft did with their XAML development environment. But hey, this is bleeding edge, and I am sure that these small issues will get sorted out eventually.
An MVVM Light example
Thanks to the built-in DataBinding support in Xamarin Forms, using MVVM Light is even more straightforward than is classic Xamarin. Here is a quick sample with two pages and navigation.
First, let’s create a new Xamarin Forms application supporting Android, iOS and Windows Phone. Note that you have two possible choices: Using a Portable Class Library (PCL) for your business logic, or using shared files.
PCL
In the PCL case, you will have three applications (iOS, Android, Windows Phone) and one additional project (PCL) in which you will put the shared code and the shared XAML. This is the case we are going to take for the demo in this article. This kind of architecture builds 4 different assemblies (iOS, Android, Windows Phone, PCL) which will be packed into three different packages (iOS, Android, Windows Phone).
Shared files
In this architecture, you still have three applications (iOS, Android, Windows Phone) and a shared “virtual folder” in which you can place the shared code and XAML. This is the same architecture than in a Windows Universal application. The shared files will automatically be linked into the three projects by Visual Studio when the applications get built. You will end up with three assemblies (iOS, Android, Windows Phone) packed into three packages.
Creating the new application(s)
We will start this demo by creating a new application. As usual with Xamarin, you can use either Visual Studio or Xamarin Studio. Here we will use Visual Studio, which is probably more familiar to the readers of this blog.
- Start Visual Studio
- Select File, New project.
- Under Mobile Apps, select Blank App (Xamarin.Forms Portable)
- Create the new app.
Like we mentioned, you will see 4 projects in the newly created solution.
Note that only Windows Phone Silverlight 8 is available at the moment, not Windows Phone 8.1.
Then we will add MVVM Light to all the projects.
Update: You can add the same Nuget package to multiple projects in a Solution with the following steps:
- Right click on the Solution file and select Manage Nuget Packages for Solution.
- Select Nuget.org on the left, and type mvvmlight in the search field.
- Select the package names “MVVM Light Libraries Only (PCL)” and click install.
- Select which projects the package must be installed into (which, in our case, is all 4 of them).
Creating a new XAML page
Running the applications now will already show some content, but what we would like to see is XAML in action. Let’s add a shared XAML page with the following steps:
- Right click on the PCL project and select Add, New Item
- Select Forms Xaml Page.
- Note that the name still shows Class.cs. This is a bug. Change this to MyPage.xaml and click on Add.
This creates a new XAML page with code behind (MyPage.xaml.cs). Windows developers are already familiar with this file pair. The code behind is a little like a controller for the XAML view, and this is where event handlers will get implemented. Currently, the XAML code looks like this (formatting added by yours truly):
Figure 1: Original XAML markup of MyPage.
<?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="XamFormsDemo.MyPage"> <Label Text="{Binding MainText}" VerticalOptions="Center" HorizontalOptions="Center" /> </ContentPage>
As you can see, this is not “classic XAML”: the default xmlns points to a different namespace, and there are other differences such as the VerticalOptions and HorizontalOptions.
Before we take care of the UI, let’s add a ViewModel. To do this, add a ViewModel folder to the PCL project, and two new classes called MainViewModel and ViewModelLocator. Note that the usage of ViewModelLocator is not strictly necessary here, we add it for consistency with “classic XAML” applications.
Right click on the PCL project and select Add, New Folder. Name this folder ViewModel.
- In the ViewModel folder, add a new class named MainViewModel.cs
- Then add another class in the same folder named ViewModelLocator.cs.
- Implement the classes as shown in figure 2 and figure 3
Figure 2: ViewModelLocator.cs
public class ViewModelLocator { static ViewModelLocator() { ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default); SimpleIoc.Default.Register<MainViewModel>(); } /// <summary> /// Gets the Main property. /// </summary> [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic", Justification = "This non-static member is needed for data binding purposes.")] public MainViewModel Main { get { return ServiceLocator.Current.GetInstance<MainViewModel>(); } } }
Figure 3: MainViewModel.cs
public class MainViewModel : ViewModelBase { /// <summary> /// The <see cref="ClickCount" /> property's name. /// </summary> public const string ClickCountPropertyName = "ClickCount"; private int _clickCount; /// <summary> /// Sets and gets the ClickCount property. /// Changes to that property's value raise the PropertyChanged event. /// </summary> public int ClickCount { get { return _clickCount; } set { if (Set(() => ClickCount, ref _clickCount, value)) { RaisePropertyChanged(() => ClickCountFormatted); } } } public string ClickCountFormatted { get { return string.Format("The button was clicked {0} time(s)", ClickCount); } } private RelayCommand _incrementCommand; /// <summary> /// Gets the IncrementCommand. /// </summary> public RelayCommand IncrementCommand { get { return _incrementCommand ?? (_incrementCommand = new RelayCommand( () => { ClickCount++; })); } } }
Configuring App.cs and the ViewModelLocator
As I mentioned earlier, the ViewModelLocator is not strictly necessary, and I introduced it in this sample to be consistent with the way that we do MVVM in Windows-XAML. I like to have a ViewModelLocator, it’s a convenient place to do some setup and manage the ViewModels’ lifetime.
In Windows-XAML, we create the ViewModelLocator as a global resource in App.xaml. Here we do not have this possibility, so instead we will create it as a static member of the App class, which is also acting as a global class for the Xamarin.Forms app.
Since we are in App.cs already, let’s take this occasion to use the XAML page instead of the ContentPage which was created in code directly. We will instantiate the new MyPage and return it in the GetMainPage method, which is automatically called by the Xamarin.Forms app when it starts up.
Edit the code of App.cs to look like shown in figure 4. Note that in Visual Studio, you might see a “red squiggly line” indicating an error saying that it cannot convert the expression. You can ignore that error, it’s just a bug.
Figure 4: App.cs, MyPage and ViewModelLocator
public class App { private static ViewModelLocator _locator; public static ViewModelLocator Locator { get { return _locator ?? (_locator = new ViewModelLocator()); } } public static Page GetMainPage() { return new MyPage(); } }
DataContext vs BindingContext
Now that we have a ViewModel, we can assign it as the page’s DataContext. In Xamarin.Forms however, the DataContext is named BindingContext. I am not quite sure why they decided not to name it DataContext, but it’s a small learning curve so let’s just get over it.
Edit: You can set the BindingContext in XAML if you want to, and contrarily to what I said first, there are StaticResources in Xamarin XAML. However in this scenario, setting the BindingContext in XAML does not bring a real advantage over setting it in code behind. The XAML route is beneficial when you have a designer like Blend who is able to instantiate the resources at design time, thus enabling design time data. Here, let’s just keep it simple, but it is true that my first assertion was incorrect! Thanks to the commenters who pointed that to me :)
One difference between Windows-XAML and Xamarin-XAML is that the BindingContext cannot be set in XAML (in fact there are no concepts of StaticResource). So instead we will just use the code behind to do that. Edit MyPage.xaml.cs as shown in figure 5. Here too, you might get some red errors in Visual Studio, these can be ignored and are bugs of the Xamarin.Forms integration.
Figure 5: MyPage.xaml.cs
public partial class MyPage { public MyPage() { InitializeComponent(); BindingContext = App.Locator.Main; } }
Finally, we can edit the XAML markup to connect to the MainViewModel, which is already set as the application’s DataContext (well, BindingContext). This is shown in figure 6.
Figure 6: New MyPage.xaml markup
<?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="XamFormsDemo.MyPage"> <Button Text="{Binding ClickCountFormatted}" Command="{Binding IncrementCommand}" VerticalOptions="Center" /> </ContentPage>
Running the application for test
Now is the time to run the application! First let’s try Windows Phone, by setting the XamFormsDemo.WinPhone project as Startup project (right click on the project in the Solution Explorer and select this option from the context menu).
Then press Ctrl-F5 to run it. Depending on your options, this will start the application in the Windows Phone simulator or on a connected device.
Figure 7: Running the app in the Windows Phone emulator
For Android, you can choose to use the default emulators, but they are really slow. I prefer to use Genymotion which is probably the best Android emulator around at the moment. Of course you can also use a connected physical device. Here too, select the project and set it as Startup, and then press Ctrl-F5.
Figure 8: Running the app in the Android emulator
Finally, we can also run the app in iOS. For this you need a Mac connected through the network. Check the Xamarin documentation for more information on how to do this. If you choose the iPhone simulator, it will run on the Mac. Here too you can connect an iPhone to the Mac in question, and select that device.
Figure 9: Running the app on the iOS simulator
Note: Sometimes I have issues running the app directly from Visual Studio into the Mac-based iOS simulator. I assume there are still a few bugs remaining there. If that is the case for you, you can always copy the app on a memory stick, plug it in the Mac and use Xamarin Studio on the Mac to run and debug the application.
And the rest of the application?
This is of course a simple example but the beauty of Xamarin is that you can use standard .NET for your business layer and viewmodel layer. So you can really write this once and run it on the three platforms we just tested. Using an HttpClient to connect to a web service, for example, is a breeze. You can even connect to WCF services, use databases, do complex calculations, use Nuget packages such as JSON.Net, etc etc etc.
Thanks to MVVM Light on all three platforms, you can use all the standard decoupled mechanisms that we know and love on Windows-XAML. The IOC container (SimpleIoc) just works, as do the Messenger, RelayCommands, ObservableObject and ViewModelBase, etc. So not only can we use the skills we know, but we can even leverage existing code (for example from an existing Windows Phone or Windows 8 (or even WPF) applications and easily adapt it for usage in the Xamarin world.
Conclusion
Xamarin is probably the first client-side code-sharing framework that doesn’t smell like cheating. Because the applications that it creates are genuine native apps, you don’t have to compromise like with all the so-called “Hybrid” frameworks that are most of the time using HTML for the UI, running on a Web view encapsulated into a native shell. Honestly, and while it might make sense for some rare apps, this kind of Frankenstein model makes me shiver. Here on the other hand, we have true native apps, with a true native look and feel and true native performance.
The Xamarin.Forms model is interesting because it allows to quickly create a UI (or rather three UIs) to run the app for multiple purposes: Creating simple user experience, for example for a line of business app with limited external exposure; to quickly test complex business logic and demonstrate the use cases to stakeholders; to prototype new functionalities; etc.
Hopefully this simple example was helpful. In the next few months, I will publish more examples of MVVM Light applications on Xamarin, and create more complex examples. Also (and there will be a more formal announcement), if you visit the Evolve conference in October in Atlanta, I will have a session there talking about MVVM Light, Xamarin and Xamarin.Forms. I cannot wait to be there and share my experiences with you all.
Happy coding!
Laurent