Xamarin has gained a lot of traction in the mobile development community as a viable cross-platform solution for developing performant mobile apps. When we first started with Xamarin a few years ago, the only option was to create a platform-specific UI (Views and ViewControllers/Presenters) for each platform you wanted to bring your app to. Then Xamarin introduced Forms, a solution for simple mobile apps to be created cross-platform with a shared UI that still used native controls on each platform. As many mobile apps contain as much or more presentation logic as business logic, this makes much more sense as a cross-platform solution.
It is now possible to create a pretty full featured and performant mobile app using Forms. I'll walk you through a simple start to an app which uses multiple views and lists and can run on both platforms. For this example, I'm developing using Xamarin Studio on my Mac.
- Open Xamarin Studio and select New Solution... Select Forms App and Next
- Name it RecipeBook, Android and iOS platforms, Portable Class Library, and XAML for interfaces.
- Select a location and Create
After your project is created you want to make sure to turn on XAML compilation for the entire assembly. To do this open App.xaml.cs (by expanding the tree next to App.xaml in the explorer) and add the following code right above the namespace definition.
XAML compilation is an important step to improve performance of Forms apps.
Create data model
For this sample app just create a simple data model in the App class. For a production app a local persistent data store plus a possible cloud storage solution and API would be used.
This dictionary will be used as a starting point for categories and recipes in the app.
Create Categories Page
Replace the default created RecipeBookPage.xaml with the following code.
Added the Title="Categories" and the x:Name="RecipeBookPage". Title will used to display a Title in the Navigation/Action bars, and Name is a good habit to be in adding to your View elements so they can be referenced in the code if needed.
A simple layout which organizes subviews in either a horizontal or vertical stack.
"FillAndExpand" set at this level means the StackLayout itself will fill and expand all the available space vertically and horizontally on the ContentPage.
Spacing="15" puts spacing between subviews.
This view will display the list of categories to the user. It uses a ListView on Android and a TableView on iOS.
x:Name="CategoriesView" - Set name to be able to reference in code
This button will be used to add a category to the list. It has a name so it can be referenced in the code, and Text to be displayed on the button.
Tie list to data model
In your RecipeBookPage.xaml.cs add the following code.
This is a collection that can be data bound to the ListView so that it is automatically updated whenever the contents change. Create it as a property in the class, and set the Keys of the RecipesDictionary to it. Then set the CategoriesView (the name of the ListView in the XAML file) ItemsSource to it.
Run your app!
You should now be able to run your app on both an Android and an iOS device and see the list of categories and the button displayed. Xamarin Studio will let you use simulators/emulators, as well as physical devices. For Android devices you'll need to enable developer options/USB debugging as usual, and trust the computer. For iOS devices you also need to trust the computer.
You'll notice right away that the user interface is not how you'd normally want it - the list extends all the way into the status bar on iOS. It's time to add Navigation!
Wrapping in Navigation
This is something that is somehow as simple/simpler in Xamarin.Forms than it is for either platform natively! All you need to do to add Hierarchical Navigation into your Forms app is make the following change in App.xaml.cs:
Instead of simply setting MainPage to a new RecipeBookPage as was done by default, create a new NavigationPage and pass in the new RecipeBookPage. This will result in the following display instead:
Notice also the "Categories" Title we specified for the page is displayed in the navigation bar.
Now that you're able to display the data in a list and it looks halfway decent, allow the user to interact with it.
Add Category Button
Let's just make the Add Category button add another meal. Who says there are only 3 meals in a day? Add the following into your RecipeBookPage.xaml:
When you type Clicked=" you might even be prompted by Xamarin Studio to auto-complete with Handle_Clicked and insert the event handler into your cs code. Go with it and you can fill in the details:
This code adds the category to the data model, then updates the Categories to reflect the new state of the data model. If a more complex data model was used that could replace this directly, and an async could be used to get the updated state of the data model to update Categories, without blocking the UI thread. For our purposes this will suffice. Try it out and you'll see pressing the Add Category button will add 4th meal, 5th meal, etc for each press.
Navigating to a new page from the first
First add some event handlers to the ListView to handle the user's interactions with the list:
ItemTapped="Handle_ItemTapped" - Set event handler name for tapping on list items
ItemSelected="Handle_ItemSelected" - Set item selected event handler function
In your cs file, then add the following:
The Handle_ItemSelected is simply to set the selection back to no selection after a user selects a list item. This is so that when the user navigates back to the Categories page it won't still have the previous selection selected.
Handle_ItemTapped is navigating to a new page which displays the recipes for the selected category. Navigation.PushAsync uses the Navigation we set up in App.xaml.cs and pushes a new page onto the stack. This won't build because you need to create it!
Create the RecipesPage
Right click the RecipeBook project (not the Solution!) in the explorer and select Add > New File... Then select Forms ContentPage Xaml and set RecipesPage to the name.
You'll now have two new files to update. Replace the code in both:
I won't go into as much detail on this page, as most of it is the same as the previous one. Significant differences are the Entry field for allowing users to choose a recipe name to add to the current category. Pay attention to the nested StackLayout and the HorizontalOptions for each of the views in it to achieve the desired layout. Finally, note that the RecipeName.Text is set back to an empty string after the user's entry is added. Also significant is that the category is passed into the page constructor - this is one way to pass data between pages.
Running the app now you'll see the user is able to add categories and add recipes to each category, viewing the list for each category. If you look closely you may notice a few things that aren't quite right for each platform that can be fixed though.
The Forms app works on both iOS and Android, but to ensure a better user experience optimizations are necessary.
Android cell text
Depending on your Android device and version, the default text color may not be set properly and you could end up with variable results, sometimes even with empty cells showing in the list. To correct this set the text color of your cells by changing your ListView xaml to the following:
This allows you to specify how the list cells are displayed and populated in greater detail. In this case we'll use a simple TextCell and we'll use 'Binding .' for Text to indicate that the item source itself, rather than any property of it should be used. The important part here is TextColor="Black" which makes sure text is black across platforms, versions and devices. Note this change is actually applying to both platforms, but is only needed because of the differences between Android devices and versions.
iOS Keyboard Overlap
You'll notice that when you select the textfield on the bottom of the RecipesPage on iOS the keyboard displays and covers the textfield, rather than moving it up to see what you're typing. This is an issue in Xamarin.Forms right now, but there is a community created Nuget package (MIT license) which fixes it.
To add this package expand your iOS project (RecipeBook.iOS) right click Packages > Add Package... Then search and select the KeyboardOverlap package as seen below.
Then open your AppDelegate.cs file in your iOS project and add the following in:
Take it further
So now you've scratched the surface of what you can do using Xamarin.Forms, and hopefully found it performant on both platforms. The rest is up to you - we'd love to hear what you've done with it!
Stay tuned in the future for more mobile development topics! For a later post I will show you how to bring this app to Windows as well, using Visual Studio on a Windows PC.