16 min read

Since the beginning of Xamarin’s life as a company, their motto has always been to present the native APIs on iOS and Android idiomatically to C#. This was a great strategy in the beginning, because applications built with Xamarin.iOS or Xamarin.Android were pretty much indistinguishable from native Objective-C or Java applications. Code sharing was generally limited to non-UI code, which left a potential gap to fill in the Xamarin ecosystem: a cross-platform UI abstraction. Xamarin.Forms is the solution to this problem, a cross-platform UI framework that renders native controls on each platform. Xamarin.Forms is a great framework for those that know C# (and XAML), but also may not want to get into the full details of using the native iOS and Android APIs. In this chapter, we will do the following:

  • Create Hello World in Xamarin.Forms
  • Discuss the Xamarin.Forms architecture
  • Use XAML with Xamarin.Forms
  • Cover data binding and MVVM with Xamarin.Forms

Creating Hello World in Xamarin.Forms

To understand how a Xamarin.Forms application is put together, let’s begin by creating a simple Hello World application. OpenXamarin Studio and perform the following steps:

  1. Create a new Multiplatform | App | Forms App project from the new solution dialog.
  2. Name your solution something appropriate, such as HelloForms.
  3. Make sure Use Portable Class Library is selected.
  4. Click Next, then click Create.

Notice the three new projects that were successfully created:

  • HelloForms
  • HelloForms.Android
  • HelloForms.iOS

In Xamarin.Forms applications, the bulk of your code will be shared, and each platform-specific project is just a small amount of code that starts up the Xamarin.Forms framework. Let’s examine theminimum parts of a Xamarin.Forms application:

  • App.xaml and App.xaml.cs in the HelloForms PCL library — this class is the main starting point of the Xamarin.Forms application. A simple property, MainPage, is set to the first page in the application. In the default project template, HelloFormsPage is created with a single label that will be rendered as a UILabel on iOS and a TextView on Android.
  • MainActivity.cs in the HelloForms.Android Android project — the main launcher activity of the Android application. The important parts for Xamarin.Forms here is the call to Forms.Init(this, bundle), which initializes the Android-specific portion of the Xamarin.Forms framework. Next is a call to LoadApplication(new App()), which starts our Xamarin.Forms application.
  • AppDelegate.cs in the HelloForms.iOS iOS project — very similar to Android, except iOS applications start up using a UIApplicationDelegate class. Forms.Init() will initialized the iOS-specific parts of Xamarin.Forms, and just as Android’s LoadApplication(new App()), will start the Xamarin.Forms application.

Go ahead and run the iOS project; you should see something similar to the following screenshot:

If you run theAndroid project, you will get a UI verysimilar to the iOS one shown in the following screenshot, but using native Android controls:

Even though it’s not covered in this book, Xamarin.Forms also supports Windows Phone, WinRT, and UWP applications. However, a PC running Windows and Visual Studio is required to develop for Windows platforms. If you can get a Xamarin.Forms application working on iOS and Android, then getting a Windows Phone version working should be a piece of cake.

Understanding the architecture behind Xamarin.Forms

Getting started with Xamarin.Forms is very easy, but it is always good to look behind the scenes to understand how everything is put together. In the earlier chapters of this book, we created a cross-platform application using native iOS and Android APIs directly. Certain applications are much more suited for this development approach, so understanding the difference between a Xamarin.Forms application and a classic Xamarin application is important when choosing what framework is best suited for your app. Xamarin.Forms is an abstraction over the native iOS and Android APIs that you can call directly from C#. So, Xamarin.Forms is using the same APIs you would in a classic Xamarin application, while providing a framework that allows you to define your UIs in a cross-platform way. An abstraction layer such as this is in many ways a very good thing, because it gives you the benefit of sharing the code driving your UI as well as any backend C# code that could also have been shared in a standard Xamarin app. The main disadvantage, however, is a slight hit in performance that might make it more difficult to create a perfect, buttery-smooth experience. Xamarin.Forms gives the option of writing renderers and effects that allow you to override your UI in a platform-specific way. This gives you the ability to drop down to native controls where needed. Have a look at the differences between a Xamarin.Forms application and a traditional Xamarin app in the following diagram:

In both applications, the business logic and backend code of the application can be shared, but Xamarin.Forms gives an enormous benefit by allowing your UI code to be shared as well. Additionally, Xamarin.Forms applications have two projecttemplates to choose from, so let’s cover each option:

  • Xamarin.Forms Shared: Creates a shared project with all of your Xamarin.Forms code, an iOS project, and an Android project
  • Xamarin.Forms Portable: Creates a Portable Class Library (PCL) containing all shared Xamarin.Forms code, an iOS project, and an Android project

Both options will work well for any application, in general. Shared projects are basically a collection of code files that get added automatically by another project referencing it. Using a shared project allows you to use preprocessor statements to implement platform-specific code. PCL projects, on the other hand, create a portable .NET assembly that can be used on iOS, Android, and various other platforms. PCLs can’t use preprocessor statements, so you generally set up platform-specific code with interface or abstract/base classes. In most cases, I think a PCL is a better option, since it inherently encourages better programming practices. See Chapter 3, Code Sharing between iOS and Android, for details on the advantages and disadvantages of these two code-sharing techniques.

Using XAML in Xamarin.Forms

In addition to defining Xamarin.Forms controls from C# code, Xamarin has provided the tooling for developing your UI in Extensible Application Markup Language (XAML). XAML is a declarative language that is basically a set of XML elements that map to a certain control in the Xamarin.Forms framework. Using XAML is comparable to using HTML to define the UI on a webpage, with the exception that XAML in Xamarin.Forms is creating C# objects that represent a native UI. To understand how XAML works in Xamarin.Forms, let’s create a new page with different types of Xamarin.Forms controls on it. Return to your HelloForms project from earlier, and open the HelloFormsPage.xaml file. Add the following XAML code between the <ContentPage> tags:

<StackLayout Orientation="Vertical" Padding="10,20,10,10"> 
    <Label Text="My Label" XAlign="Center" /> 
    <Button Text="My Button" /> 
    <Entry Text="My Entry" /> 
    <Image Source="https://www.xamarin.com/content/images/ 
      pages/branding/assets/xamagon.png" /> 
    <Switch IsToggled="true" /> 
    <Stepper Value="10" /> 
</StackLayout> 

Go ahead and run the application on iOS; your application will look something like the following screenshot:

On Android, the application looks identical to iOS, except it is using native Android controls instead of the iOS counterparts:

In our XAML, we created a StackLayout control, which is a container for other controls. It can lay out controls either vertically or horizontally one by one, as defined by the Orientation value. We also applied a padding of 10 around the sides and bottom, and 20 from the top to adjust for the iOS status bar. You may be familiar with this syntax for defining rectangles if you are familiar with WPF or Silverlight. Xamarin.Forms uses the same syntax of left, top, right, and bottom values, delimited by commas. We also usedseveral of the built-in Xamarin.Forms controls to see how they work:

  1. Label: We used this earlier in the chapter. Used only for displaying text, this maps to a UILabel on iOS and a TextView on Android.
  2. Button: A general purpose button that can be tapped by a user. This control maps to a UIButton on iOS and a Button on Android.
  3. Entry: This control is a single-line text entry. It maps to a UITextField on iOS and an EditText on Android.
  4. Image: This is a simple control for displaying an image on the screen, which maps to a UIImage on iOS and an ImageView on Android. We used the Source property of this control, which loads an image from a web address. Using URLs on this property is nice, but it is best for performance to include the image in your project where possible.
  5. Switch: This is an on/off switch or toggle button. It maps to a UISwitch on iOS and a Switch on Android.
  6. Stepper: This is a general-purpose input for entering numbers using two plus and minus buttons. On iOS, this maps to a UIStepper, while on Android, Xamarin.Forms implements this functionality with two buttons.

These are just some of the controls provided by Xamarin.Forms. There are also more complicated controls, such as the ListView and TableView, which you would expect for delivering mobile UIs. Even though we used XAML in this example, you could also implement this Xamarin.Forms page from C#. Here is an example of what that would look like:

public class UIDemoPageFromCode : ContentPage 
{
public UIDemoPageFromCode()
{
var layout = new StackLayout
{
Orientation = StackOrientation.Vertical,
Padding = new Thickness(10, 20, 10, 10),
};

layout.Children.Add(new Label
{
Text = "My Label",
XAlign = TextAlignment.Center,
});

layout.Children.Add(new Button
{
Text = "My Button",
});

layout.Children.Add(new Image
{
Source = "https://www.xamarin.com/content/images/pages/
branding/assets/xamagon.png",
});

layout.Children.Add(new Switch
{
IsToggled = true,
});

layout.Children.Add(new Stepper
{
Value = 10,
});

Content = layout;
}
}

So, you can see where using XAML can be a bit more readable, and is generally a bit better at declaring UIs than C#. However, using C# to define your UIs is still a viable, straightforward approach.

Using data-binding and MVVM

At this point, you should begrasping the basics of Xamarin.Forms, but are wondering how theMVVM design pattern fits into the picture. The MVVM design pattern was originally conceived for use along with XAML and the powerful data binding features XAML provides, so it is only natural that it is a perfect design pattern to be used with Xamarin.Forms. Let’s cover the basics of how data-binding and MVVM is set up with Xamarin.Forms:

  1. Your Model and ViewModel layers will remain mostly unchanged from the MVVM pattern we covered earlier in the book.
  2. Your ViewModels should implement the INotifyPropertyChanged interface, which facilitates data binding. To simplify things in Xamarin.Forms, you can use the BindableObject base class and call OnPropertyChanged when values change on your ViewModels.
  3. Any Page or control in Xamarin.Forms has a BindingContext, which is the object that it is data-bound to. In general, you can set a corresponding ViewModel to each view’s BindingContext property.
  4. In XAML, you can set up a data-binding by using syntax of the form Text="{Binding Name}". This example would bind the Text property of the control to a Name property of the object residing in the BindingContext.
  5. In conjunction with data binding, events can be translated to commands using the ICommand interface. So, for example, the click event of a Button can be data-bound to a command exposed by a ViewModel. There is a built-in Command class in Xamarin.Forms to support this.
Data binding can also be set up with C# code in Xamarin.Forms using the Binding class. However, it is generally much easier to set up bindings with XAML, since the syntax has been simplified with XAML markup extensions.

Now that we have covered the basics, let’s go through step-by-step and partially convert our XamSnap sample application from earlier in the book to use Xamarin.Forms. For the most part, we can reuse most of the Model and ViewModel layers, although we will have to make a few minor changes to support data-binding with XAML. Let’s begin by creating a new Xamarin.Forms application backed by a PCL, named XamSnap:

  1. First, create three folders in the XamSnap project named Views, ViewModels, and Models.
  2. Add the appropriate ViewModels and Models classes from the XamSnap application from earlier chapters; these are found in the XamSnap project.
  3. Build the project, just to make sure everything is saved. You will get a few compiler errors, which we will resolve shortly.

The first class we will need to edit is the BaseViewModel class; open it and make the following changes:

public class BaseViewModel : BindableObject 
{
protected readonly IWebService service =
DependencyService.Get<IWebService>();
protected readonly ISettings settings =
DependencyService.Get<ISettings>();

bool isBusy = false;

public bool IsBusy
{
get { return isBusy; }
set
{
isBusy = value;
OnPropertyChanged();
}
}
}

First of all, we removed the calls to the ServiceContainer class, because Xamarin.Forms provides its own IoC container called the DependencyService. It functions very similarly to the container we built in the previous chapters, except it only has one method, Get<T>, and registrations are set up via an assembly attribute that we will set up shortly. Additionally, we removed the IsBusyChanged event in favor of the INotifyPropertyChanged interface that supports data binding. Inheriting from BindableObject gave us the helper method, OnPropertyChanged, which we use to inform bindings in Xamarin.Forms that the value has changed. Notice we didn’t pass a string containing the property name to OnPropertyChanged. This method is using a lesser-known feature of .NET 4.0 called CallerMemberName, which will automatically fill in the calling property’s name at runtime. Next, let’s set up the services we need with the DependencyService. Open App.xaml.cs in the root of the PCL project and add the following two lines above the namespace declaration:

[assembly: Dependency(typeof(XamSnap.FakeWebService))] 
[assembly: Dependency(typeof(XamSnap.FakeSettings))]

The DependencyService will automatically pick up these attributes and inspect the types we declared. Any interfaces these types implement will be returned for any future callers of DependencyService.Get<T>. I normally put all Dependency declarations in the App.cs file, just so they are easy to manage and in one place. Next, let’s modify LoginViewModel by adding a new property:

public Command LoginCommand { get; set; } 

We’ll use this shortly for data-binding the command of a Button. One last change in the view model layer is to set up INotifyPropertyChanged for MessageViewModel:

Conversation[] conversations; 
 
public Conversation[] Conversations 
{ 
  get { return conversations; } 
  set 
  { 
    conversations = value; 
    OnPropertyChanged(); 
  } 
} 

Likewise, you could repeat this pattern for the remaining public properties throughout the view model layer, but this is all we will need for this example. Next, let’s create a new Forms ContentPage Xaml file named LoginPage in the Views folder. In the code-behind file, LoginPage.xaml.cs, we’ll just need to make a few changes:

public partial class LoginPage : ContentPage 
{
readonly LoginViewModel loginViewModel = new LoginViewModel();


public LoginPage()
{
Title = "XamSnap";
BindingContext = loginViewModel;

loginViewModel.LoginCommand = new Command(async () =>
{
try
{
await loginViewModel.Login();
await Navigation.PushAsync(new ConversationsPage());
}
catch (Exception exc)
{
await DisplayAlert("Oops!", exc.Message, "Ok");
}
});

InitializeComponent();
}
}

We did a few important things here, including setting the BindingContext to our LoginViewModel. We set up the LoginCommand, which basically invokes the Login method and displays a message if something goes wrong. It also navigates to a new page if successful. We also set the Title, which will show up in the top navigation bar of the application. Next, open LoginPage.xaml and we’ll add the following XAML code inside ContentPage:

<StackLayout Orientation="Vertical" Padding="10,10,10,10"> 
<Entry
Placeholder="Username" Text="{Binding UserName}" />
<Entry
Placeholder="Password" Text="{Binding Password}"
IsPassword="true" />
<Button
Text="Login" Command="{Binding LoginCommand}" />
<ActivityIndicator
IsVisible="{Binding IsBusy}"
IsRunning="true" />
</StackLayout>

This will set up the basics of two text fields, a button, and a spinner, complete with all the bindings to make everything work. Since we set up BindingContext from the LoginPage code-behind file, all the properties are bound to LoginViewModel. Next, create ConversationsPage as a XAML page just like before, and edit the ConversationsPage.xaml.cs code-behind file:

public partial class ConversationsPage : ContentPage 
{
readonly MessageViewModel messageViewModel =
new MessageViewModel();


public ConversationsPage()
{
Title = "Conversations";
BindingContext = messageViewModel;

InitializeComponent();
}

protected async override void OnAppearing()
{
try
{
await messageViewModel.GetConversations();
}
catch (Exception exc)
{
await DisplayAlert("Oops!", exc.Message, "Ok");
}
}
}

In this case, we repeated a lot of the same steps. The exception is that we used the OnAppearing method as a way to load the conversations to display on the screen. Now let’s add the following XAML code to ConversationsPage.xaml:

<ListView ItemsSource="{Binding Conversations}"> 
<ListView.ItemTemplate>
<DataTemplate>
<TextCell Text="{Binding UserName}" />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>

In this example, we used ListView to data-bind a list of items and display on the screen. We defined a DataTemplate class, which represents a set of cells for each item in the list that the ItemsSource is data-bound to. In our case, a TextCell displaying the Username is created for each item in the Conversations list. Last but not least, we must return to the App.xaml.cs file and modify the startup page:

MainPage = new NavigationPage(new LoginPage());  

We used a NavigationPage here so that Xamarin.Forms can push and pop between different pages. This uses a UINavigationController on iOS, so you can see how the native APIs are being used on each platform. At this point, if youcompile and run the application, you will get afunctional iOS and Android application that can log in and view a list of conversations:

Summary

Xamarin.Forms

In this chapter, we covered the basics of Xamarin.Forms and how it can be very useful for building your own cross-platform applications. Xamarin.Forms shines for certain types of apps, but can be limiting if you need to write more complicated UIs or take advantage of native drawing APIs. We discovered how to use XAML for declaring our Xamarin.Forms UIs and understood how Xamarin.Forms controls are rendered on each platform. We also dived into the concepts of data-binding and how to use the MVVM design pattern with Xamarin.Forms. Last but not least, we began porting the XamSnap application from earlier in the book to Xamarin.Forms, and were able to reuse a lot of our existing code. In the next chapter, we will cover the process of submitting applications to the iOS App Store and Google Play. Getting your app into the store can be a time-consuming process, but guidance from the next chapter will give you a head start.

LEAVE A REPLY

Please enter your comment!
Please enter your name here