Xamarin.Forms is a cross-platform UI toolkit that allows developers to efficiently create native user interface layouts that can be shared across iOS, Android, and Universal Windows Platform apps. This series introduces the basics of Xamarin.Forms development and covers building multi-platform and multi-screen applications.
- Installation of Visual Studio 2017 for Windows with Xamarin [instructions]
- Installation of GitHub Desktop Client [instructions] (You can use whatever client you are familiar with.)
- Update Visual Studio 2017 for Windows [instructions]
- Installation of Visual Studio for Mac [instructions]
- Installation of GitHub Desktop Client [instructions]
- Update Visual Studio for Mac [instructions]
Documentation: [here]
Create new Solution with Xamarin Forms Template
- Open Visual Studio
- Open Menu
File > New > Projects
- Select Template
Installed > Visual C# > Cross-Platform > Mobile App (Xamarin.Forms)
- Enter the name for project your project (i.e.
NorthDallas.Contacts
) - Select the settings as shown in the picture and click
Ok
- Project will be generated with all the needed files
Setup LiveReload for rapid iterations through development.
- Open Menu
Tools > Extensions and Updates
- Select
Online
from the left - Type
Live Reload
in the search bar - Click
Download
- You will be prompted to restart Visual Studio. Do so.
- Click
Modify
when prompted by Live Reload installer. - Reopen Visual Studio after the installation is complete
- Re-Open your newly created project
- Right Click
Dependencies
in your Xamarin.Forms ProjectNorthDallas.Contacts
- Click
Manage NuGet Packages
- Click the
Browse
Tab - Select
LiveReload
for thePackage source
- Install the
Xamarin.LiveReload
package - Add
LiveReload.Init();
to the class App in the Xamarin.Forms ProjectNorthDallas.Contacts
public partial class App : Application
{
public App ()
{
// Initialize Live Reload.
#if DEBUG
LiveReload.Init();
#endif
InitializeComponent();
MainPage = new MainPage();
}
}
- Open
NorthDallas.Contacts\MainPage.xaml
page - Click the
Connect
link in the yellow bar above your XAML page - Open your firewall for the Live Reload connection
- Select
NorthDallas.Contacts.Android
from your start project selector - Select your emulator and press run
- If you are using Live Reload, you may make changes to the
NorthDallas.Contacts\MainPage.xaml
page and save the file to see the changes.
- With your
MainPage.xaml
page open, change the label to say "Welcome to Dallas!" from "Welcome to Xamarin.Forms!" - If you are running LiveReload, it should just update in the emulator. Otherwise you have to run the app again.
We'll be using Model-View-ViewModel for Xamarin.Forms. There are a lot of advantages to them. The first thing we'll need to do is create a ViewModel.
- Stop the application from running.
- In the
NorthDallas.Contacts
Solution, right click the project and selectManage NuGet Packages for Solution
from the context menu. - Select
nuget.org
as yourPackage Source
- Click on the
Browse
tab - Type the following into the search
Refractored.MvvmHelpers
- In the right pane, select all the projects and click
Install
- In the
NorthDallas.Contacts
project, right click the project and chooseAdd > New Folder
. - Name as
ViewModels
- Now right click that folder and select
Add > Class
- Enter the name
ContactListViewModel.cs
and clickAdd
- Make
ContactListViewModel
implementINotifyPropertyChanged
usingMvvmHelpers
public class ContactListViewModel
: BaseViewModel
{
public ContactListViewModel()
{
Title = "Welcome to the Contact List";
}
}
Views is another piece of the MVVM architecture. This is what the user interacts with, the UI.
- Create another folder inside of
NorthDallas.Contacts
namedViews
. - Right click the new folder
Views
and chooseAdd > New Item
from the context menu. - Select the template
Xamarin.Forms > Content Page
and enter the nameContactListView.xaml
and clickAdd
. - Point the application to start with
ContactListView.xaml
- Open
NorthDallas.Contacts\App.xaml.cs
file from under theApp.xaml
file. - Assign a new instance of
ContactListView
to theMainPage
property by replacing the existing assignment.
MainPage = new ContactListView();
- You can delete
NorthDallas.Contacts\MainPage.xaml
file now. - Run the application and see that it now starts with the new view.
Most of Mobile development is displaying lists of data. Let's add a ListView
to ContactListView
page.
- Create a new property to store contacts in
ContactListViewModel.cs
.
public ObservableRangeCollection<string> Contacts { get; set; } =
new ObservableRangeCollection<string>();
- Add fake data to the
ContactListViewModel
to render on the view by placing the following code in your constructor.
Contacts.AddRange(new string[]
{
"Sean",
"Homero",
"Gabrielle",
"Anna",
"Annette",
"Bella",
"Brett",
"Stephen"
});
- Replace the
Label
in theNorthDallas.Contacts\Views\ContactListView.xaml
with aListView
.
<ListView ItemsSource="{Binding Contacts}"
RowHeight="70">
<ListView.ItemTemplate>
<DataTemplate>
<TextCell Text="{Binding .}" />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
- Open
NorthDallas.Contacts\View\ContactListView.xaml.cs
from under theContactListView.xaml
file. - Add the
ContactListViewModel
as theBindingContext
in the constructorContactListView
class.
public ContactListView ()
{
BindingContext = new ContactListViewModel();
InitializeComponent ();
}
- Run the Application and see the list of people.
For this workshop, we will be using RandomUser API [documentation]. We will be using JSON 2 CSharp to generate POCO (Plain Old CSharp Object).
- Take a results example from the documentation page and paste it into the JSON 2 CSharp page.
- Click the
Generate
button. - Click the
Copy
button at the bottom of the page, select the code, and copy the text to get the generated code. - Create a new folder
Models
in theNorthDallas.Contacts
project. - Create new a class in the
Models
folder by right clicking the folder and choosingAdd > Class...
. - Name the class
RandomUserResults.cs
. - Replace the class in the new file with the copied code from JSON 2 CSharp.
- In the
NorthDallas.Contacts
Solution, right click the project and selectManage NuGet Packages for Solution
from the context menu. - Select
nuget.org
as yourPackage Source
- Click on the
Browse
tab - Type the following into the search
Newtonsoft.Json
- In the right pane, select all the projects and click
Install
- Create a new folder
Services
in theNorthDallas.Contacts
project. - Create new a class in the
Services
folder by right clicking the folder and choosingAdd > Class...
. - Name the new class
UserService.cs
. - Replace the new class with the following.
public class UserService
{
public async Task<List<Result>> GetUsersAsync(int count)
{
var httpClient = new HttpClient();
var response = await httpClient.GetAsync($"https://randomuser.me/api/?results={count}&seed=northdallas");
if (response.IsSuccessStatusCode)
{
var json = await response.Content.ReadAsStringAsync();
var results = JsonConvert.DeserializeObject<RootObject>(json);
return results.results;
}
throw new Exception("API call failed!");
}
}
Now that you have created a client to pull user data from RandomUser.me, you need to use the data.
- Open
NorthDallas.Contacts\ViewModels\ContactListViewModel.cs
and add a new parameter to theContactListViewModel
constructor.
public ContactListViewModel(UserService userService)
- Create a new class variable inside of the
ContactListViewModel
.
private UserService _userService;
- Assign the constructor parameter to the class variable inside the constructor and remove the
AddRange
from the constructor.
public ContactListViewModel(UserService userService)
{
_userService = userService;
}
- Update the
Contacts
property to the type ofObservableRangeCollection<Result>
.
public ObservableRangeCollection<Result> Contacts { get; set; } =
new ObservableRangeCollection<Result>();
- Create a new method inside of
NorthDallas.Contacts\ViewModels\ContactListViewModel.cs
and name itExecuteRefreshCommandAsync
.
private async Task ExecuteRefreshCommandAsync()
{
try
{
IsBusy = true;
Contacts.Clear();
Contacts.AddRange(await _userService.GetUsersAsync(100));
}
catch(Exception ex)
{
Debug.WriteLine(ex);
}
finally
{
IsBusy = false;
}
}
- Add a new command. We will use the
RefreshCommand
to pull down random users from the API.
private ICommand _refreshCommand;
public ICommand RefreshCommand =>
_refreshCommand ?? (_refreshCommand = new Command(async () => await ExecuteRefreshCommandAsync()));
- Open the
NorthDallas.Contacts\Views\ContactListView.xaml.cs
file from under theContactListView.xaml
file. - Create a new class variable to store the view model.
private ContactListViewModel _viewModel;
- Assign the new
ContactListViewModel
to the_viewModel
inside of the constructor ofContactListView
.
BindingContext = _viewModel = new ContactListViewModel(new UserService());
- Now we want to pull the data on when the page is first being put on the screen.
protected override void OnAppearing()
{
base.OnAppearing();
_viewModel.RefreshCommand.Execute(null);
}
- Now run the application. You will find the cells blank.
- Fix this by updating your
TextCell
to bind to name.
<TextCell Text="{Binding name.first}" Detail="{Binding name.last}" />
- Run the application again.
While a list of names is great, you will want to display more than one or two pieces of data or even an email. You need a custom cell for this.
- You need to replace the
TextCell
with aViewCell
- Your content for the template will be inside the
ViewCell
- Replace your
TextCell
with the following.
<ViewCell>
<StackLayout>
<Label FontAttributes="Bold">
<Label.FormattedText>
<FormattedString>
<Span Text="{Binding name.last}" />
<Span Text=", " />
<Span Text="{Binding name.first}" />
</FormattedString>
</Label.FormattedText>
</Label>
<Label>
<Label.FormattedText>
<FormattedString>
<Span Text="{Binding location.city}" />
<Span Text=", " />
<Span Text="{Binding location.state}" />
</FormattedString>
</Label.FormattedText>
</Label>
</StackLayout>
</ViewCell>
- To add an image, you will need to include another NuGet package.
- Click on the
Browse
tab - Type the following into the search
Xamarin.FFImageLoading.Forms
- In the right pane, select all the projects and click
Install
- In the
NorthDallas.Contacts
Solution, right click the project and selectManage NuGet Packages for Solution
from the context menu. - In the right pane, select the iOS, Android and UWP projects and click
Install
- Click on the
Updates
tab - Check the box for
Xamarin.Forms
- Click the
Update
button in the left pane - Accept the EULA
- Open the
NorthDallas.Contacts.Android\MainActivity.cs
file and add the following to the beginning of theOnCreate
method.
Xamarin.Forms.Forms.SetFlags("FastRenderers_Experimental");
FFImageLoading.Forms.Platform.CachedImageRenderer.Init(true);
- Open the
NorthDallas.Contacts.iOS\AppDelegate.cs
file and add the following the beginning of theFinishedLaunching
method.
FFImageLoading.Forms.Platform.CachedImageRenderer.Init();
- Go back to the
ContactListView.xaml
file and add the following toContentPage
node.
xmlns:ffimage="clr-namespace:FFImageLoading.Forms;assembly=FFImageLoading.Forms"
- Now you can add a caching image to your custom view cell.
- Update your
ViewCell
with the following.
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="50" />
<RowDefinition Height="50" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="50" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<ffimage:CachedImage Grid.Row="0"
Grid.Column="0"
WidthRequest="300"
HeightRequest="300"
Source="{Binding picture.large}"/>
<StackLayout Grid.Row="0"
Grid.Column="1">
<Label FontAttributes="Bold">
<Label.FormattedText>
<FormattedString>
<Span Text="{Binding name.last}" />
<Span Text=", " />
<Span Text="{Binding name.first}" />
</FormattedString>
</Label.FormattedText>
</Label>
<Label>
<Label.FormattedText>
<FormattedString>
<Span Text="{Binding location.city}" />
<Span Text=", " />
<Span Text="{Binding location.state}" />
</FormattedString>
</Label.FormattedText>
</Label>
</StackLayout>
</Grid>
As you scroll through the ListView, you will notice that some of the images look like a previous image until it has a chance to finish loading. To fix this, you wil need to add a LoadingPlaceHolder
tag.
- Add the
LoadingPlaceholder
tag to theCachedImage
node.
LoadingPlaceholder="http://placekitten.com/300/300"
- Run the application.
To keep up with trends, mobile applications have be updated regularly. One of the latest trends is circular images. It is easy to add a circular transformation with Fast & Furious Image Loading.
- In the
NorthDallas.Contacts
Solution, right click the project and selectManage NuGet Packages for Solution
from the context menu. - Click on the
Browse
tab - Type the following into the search
Xamarin.FFImageLoading.Transformations
- In the right pane, select all the projects and click
Install
- In the
NorthDallas.Contacts
Solution, right click the project and selectManage NuGet Packages for Solution
from the context menu. - Go back to the
ContactListView.xaml
file and add the following reference toContentPage
node.
xmlns:tranformations="clr-namespace:FFImageLoading.Transformations;assembly=FFImageLoading.Transformations"
- Add a
CircleTransformation
to theCachedImage
.
<ffimage:CachedImage Grid.Row="0"
Grid.Column="0"
WidthRequest="300"
HeightRequest="300"
Source="{Binding picture.thumbnail}"
LoadingPlaceholder="http://placekitten.com/300/300">
<ffimage:CachedImage.Transformations>
<tranformations:CircleTransformation />
</ffimage:CachedImage.Transformations>
</ffimage:CachedImage>
This is the first step towards creating a detail page. Once we have a detail page, we will enable the user to navigate to the detail by selecting an item in the ListView
.
- Now right click that folder and select
Add > Class
- Enter the name
ContactDetailViewModel.cs
and clickAdd
- Make
ContactDetailViewModel
implementINotifyPropertyChanged
usingMvvmHelpers
public class ContactDetailViewModel
: BaseViewModel
{
public ContactDetailViewModel(Result contact)
{
Contact = contact;
Title = $"{contact.name.first} {contact.name.last}";
}
public Result Contact { get; set; }
}
We need to create a view to display the details of the contact.
- Right click the folder
Views
and chooseAdd > New Item
from the context menu. - Select the template
Xamarin.Forms > Content Page
and enter the nameContactDetailView.xaml
and clickAdd
. - Replace the XAML for
ContactDetailView.xaml
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:ffimage="clr-namespace:FFImageLoading.Forms;assembly=FFImageLoading.Forms"
xmlns:tranformations="clr-namespace:FFImageLoading.Transformations;assembly=FFImageLoading.Transformations"
x:Class="NorthDallas.Contacts.Views.ContactDetailView"
Title="{Binding Title}">
<ContentPage.Content>
<ScrollView>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="300" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<ffimage:CachedImage Grid.Row="0"
VerticalOptions="FillAndExpand"
HorizontalOptions="FillAndExpand"
Source="{Binding Contact.picture.large}"
LoadingPlaceholder="http://placekitten.com/300/300"
Margin="50, 0, 50, 0">
<ffimage:CachedImage.Transformations>
<tranformations:CircleTransformation />
</ffimage:CachedImage.Transformations>
</ffimage:CachedImage>
<StackLayout Grid.Row="1" Margin="10, 0, 10, 0">
<Label FontSize="Micro">Name:</Label>
<Label FontAttributes="Bold"
FontSize="Large">
<Label.FormattedText>
<FormattedString>
<Span Text="{Binding Contact.name.last}" />
<Span Text=", " />
<Span Text="{Binding Contact.name.first}" />
</FormattedString>
</Label.FormattedText>
</Label>
<Label FontSize="Micro">Location:</Label>
<Label FontAttributes="Bold"
FontSize="Large">
<Label.FormattedText>
<FormattedString>
<Span Text="{Binding Contact.location.city}" />
<Span Text=", " />
<Span Text="{Binding Contact.location.state}" />
</FormattedString>
</Label.FormattedText>
</Label>
<Label FontSize="Micro">Email:</Label>
<Label FontAttributes="Bold"
FontSize="Large"
Text="{Binding Contact.email}"/>
<Label FontSize="Micro">Phone:</Label>
<Label FontAttributes="Bold"
FontSize="Large"
Text="{Binding Contact.phone}"/>
<Label FontSize="Micro">Cell:</Label>
<Label FontAttributes="Bold"
FontSize="Large"
Text="{Binding Contact.cell}"/>
</StackLayout>
</Grid>
</ScrollView>
</ContentPage.Content>
</ContentPage>
- Add a constructor parameter for
ContactDetailView
inNorthDallas.Contacts\Views\ContactDetailView.xaml.cs
public ContactDetailView (ContactDetailViewModel viewModel)
{
BindingContext = viewModel;
InitializeComponent ();
}
Now it's time to wire everything up.
- Open the
ContactListView.xaml
file. - Add
ItemSelected="OnContactSelected"
to theListView
node inside of the file. - Open the
ContactListView.xaml.cs
file under theContactListView.xaml
page. - Add a new method to the class to handle the
ItemSelected
event.
private void OnContactSelected(object sender, SelectedItemChangedEventArgs e)
{
if (e.SelectedItem is Result)
{
Navigation.PushAsync(new ContactDetailView(new ContactDetailViewModel((Result)e.SelectedItem)));
var listView = (ListView)sender;
listView.SelectedItem = null;
}
}
- Open the
App.xaml.cs
file under theApp.xaml
inside of your project. - Wrap the
MainPage
with a new instance ofNavigationPage
.
MainPage = new NavigationPage(new ContactListView());