How to create a beautiful UI in Xamarin Forms

In this article we will focus mainly on the topic of beautiful UI/UX and how we can achieve it in Xamarin Forms.

A few words about the technology

Xamarin Forms is a cross-platform technology which allows to create native mobile applications using our favorite C# language. We can write many words about that, but there’s a ton of articles in the wild about Xamarin already.

In 2016 Microsoft acquired Xamarin and a new era began. After acquisition many things happened in tooling and the whole developer ecosystem.

Onstage

Our software house has recently developed an application called Onstage.

ONSTAGE – more than the sound

Find your classical music concert > Buy the ticket > See the unseen.

ONSTAGE mobile app helps you find classical music concerts and fall in love with the music performed live.

Search for classical music concerts, no matter where you are. Discover the stories of artists and composers. Have a closer look at the scores and read comprehensive info about the performed programme. Rate the concert, give feedback to the organizer, share your impressions on social media.

While at the venue, get immersed with real-time video close-ups on the conductor’s face and musicians, directly in your smartphone – as if you were together with them on the stage.

Onstage

The application has a nice and clean design which helps the end user discover info about events in the nearby area. But how do we create such a UI? If you want know more please check at https://www.onstageapplication.com/.

Our goals

You probably heard the legends about Xamarin and custom renderers. For example, “to achieve anything in Xamarin Forms, you need to use those scary, infamous Custom Renderers” and so on.
The goal with this series is to refute this myth.

Implementing the UI

First, our UX team delivered wireframes created with Adobe XD. Our work was to recreate those prototype views using only the Xamarin-provided controls, possibly without any hacking and custom renderers. Below we’ll delve into what we were able to manage to deliver.

Let’s decompose the “Discover” view.

Discover

The main part of this page is a ListView control which is refreshed from the dedicated ViewModel

ViewModel

<ListView x_Name="listView"
                  ItemsSource="{Binding Events, Mode=OneWay}"
                  ItemSelected="ListView_OnItemSelected"
                  IsVisible="{Binding IsVisible}"
                  IsPullToRefreshEnabled="True"
                  IsRefreshing="{Binding IsRefreshing}"
                  RefreshCommand="{Binding RefreshCommand}"
                  Style="{StaticResource baseListStyle}"
                  CachingStrategy="RecycleElement">
                  [...]</ListView>

In this article I don’t go deep into settings of this list like Caching Strategy it is a topic for another article.

Core of this UI lives in a single ItemTemplate.
It contains a Grid with:

  • Two columns – one set for Auto scaling and the second set to fill the remaining space
  • A single row set for Auto scaling

Auto scaling

<Grid Margin="0, 0, 0, 40">
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="Auto" />
        <ColumnDefinition Width="*" />
    </Grid.ColumnDefinitions>
    <Grid.RowDefinitions>
        <RowDefinition Height="Auto" />
    </Grid.RowDefinitions>
[...]</Grid>

Date section

This section is simply a StackLayout that lives in the first row of our Grid, with all the elements stacked vertically. It also contains some String Format to display only required parts of date.

Date section

<StackLayout Grid.Row="0" Grid.Column="0" Padding="15, 0" >

    <!-- Day -->
    <Label Text="{Binding EventDate, StringFormat='\{0:dd\}'}" Style="{StaticResource dayLabelStyle}" />

    <!-- Month -->
    <Label Text="{Binding EventDate, StringFormat='\{0:MMM\}'}" Style="{StaticResource monthLabelStyle}" />

    <!-- Year -->
    <Label Text="{Binding EventDate, StringFormat='\{0:yyyy\}'}" Style="{StaticResource yearLabelStyle}" />

</StackLayout>

And finally, event description

This part is much more complicated. The main part and container which hold old controls is StackLayout with Grid inside.

upcomming

<StackLayout Grid.Row="0" Grid.Column="1" Padding="10, 0, 0, 0">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="135" />
        </Grid.RowDefinitions>
[...]</StackLayout>

Single item should contain several items

  • Image
  • Add to favourites
  • Live stream indicator
  • Event title & location

Image & add to favourites

To create an image and add to favourites we use CachedImage control from FFImageLoading NuGet together with ImageButton with DataTrigger.

CachedImage

    <!-- Event image -->
    <ffimageloading:CachedImage Source="{Binding Image}" Aspect="AspectFill" />

    <!-- Add to favorite button -->
    <ImageButton Source="StarIcon" Command="{Binding AddEventToFavoritesCommand}" Style="{StaticResource addToFavoritesButtonStyle}">
        <ImageButton.Triggers>
            <DataTrigger Binding="{Binding IsStored}" TargetType="ImageButton" Value="true">
                <Setter Property="Source" Value="StarFilledIcon" />
                <Setter Property="HeightRequest" Value="24" />
                <Setter Property="WidthRequest" Value="24" />
                <Setter Property="Aspect" Value="Fill" />
                <Setter Property="Margin" Value="5" />
                <Setter Property="Scale" Value="1" />
            </DataTrigger>
        </ImageButton.Triggers>
    </ImageButton>

Live stream indicator

This element is created using GridView, few Image controls and Label.

    <!-- live stream banner -->
    <Grid IsVisible="{Binding IsPremium}" Style="{StaticResource livestreamGridStyle}">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*" />
            <ColumnDefinition Width="Auto" />
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition Height="Auto" />
        </Grid.RowDefinitions>

    <Image Source="Elem" Grid.Column="0" Grid.Row="1" Aspect="AspectFill" Margin="0, 0, 0, -12" />

    <Image Source="banner_bg" Grid.Column="0" Aspect="Fill" />

    <Image Source="banner_bg" Grid.Column="1" Aspect="Fill" />

    <Label Text="{Binding [OnstageStreamLong], Source={Static i18n:Translator.Instance}, Converter={StaticResource caseConverter}, ConverterParameter=Uppercase}" Grid.Column="1" Style="{StaticResource livestreamLabelStyle}" />
          </Grid>
    </Grid>

At the end of layout we have two Labels with Title and Location.

    <!-- Title -->
    <Label Text="{Binding Title}" Style="{StaticResource eventTitleLabelStyle}" />

    <!-- Location -->
    <Label Text="{Binding Location}"
    Style="{StaticResource eventLocationLabelStyle}" />

Summary

As you can see currently Xamarin.Forms and XAML is quite powerful technology which allow us to create quite complicated UI’s. All of elements are pure 100% XAML implemented without any custom renderers or other tricks achieved in code behind.

In the next part we go to the next screen where we need to use our single custom renderer.