Xamarin Community Toolkit - Succinctly (2021)
Xamarin Community Toolkit - Succinctly (2021)
Xamarin Community Toolkit - Succinctly (2021)
Toolkit Succinctly
By
Alessandro Del Sole
If you obtained this book from any other source, please register and download a free copy from
www.syncfusion.com.
The authors and copyright holders provide absolutely no warranty for any information provided.
The authors and copyright holders shall not be liable for any claim, damages, or any other
liability arising from, out of, or in connection with the information in this book.
Please do not use this book if the listed terms are unacceptable.
3
Table of Contents
New layouts..........................................................................................................................17
4
Displaying pop-ups ..........................................................................................................44
Converting objects into Boolean values and comparing for equality .....................................82
5
Detecting null string values ...................................................................................................88
Validating numbers.............................................................................................................103
6
Getting started ....................................................................................................................115
7
The Story behind the Succinctly Series
of Books
Daniel Jebaraj, CEO
Syncfusion, Inc.
Whenever platforms or tools are shipping out of Microsoft, which seems to be about every other
week these days, we have to educate ourselves, quickly.
While more information is becoming available on the Internet and more and more books are
being published, even on topics that are relatively new, one aspect that continues to inhibit us is
the inability to find concise technology overview books.
We are usually faced with two options: read several 500+ page books or scour the web for
relevant blog posts and other articles. Just as everyone else who has a job to do and customers
to serve, we find this quite frustrating.
We firmly believe, given the background knowledge such developers have, that most topics can
be translated into books that are between 50 and 100 pages.
This is exactly what we resolved to accomplish with the Succinctly series. Isn’t everything
wonderful born out of a deep desire to change things for the better?
Free forever
Syncfusion will be working to produce books on several topics. The books will always be free.
Any updates we publish will also be free.
8
Free? What is the catch?
There is no catch here. Syncfusion has a vested interest in this effort.
As a component vendor, our unique claim has always been that we offer deeper and broader
frameworks than anyone else on the market. Developer education greatly helps us market and
sell against competing vendors who promise to “enable AJAX support with one click,” or “turn
the moon to cheese!”
We sincerely hope you enjoy reading this book and that it helps you better understand the topic
of study. Thank you for reading.
9
About the Author
Alessandro Del Sole is a Xamarin Certified Mobile Developer and has been a Microsoft MVP
since 2008. Awarded MVP of the Year in 2009, 2010, 2011, 2012, and 2014, he is
internationally considered a Visual Studio expert and a .NET authority. Alessandro has authored
many printed books and e-books on programming with Visual Studio, including Xamarin.Forms
Succinctly, Visual Basic 2015 Unleashed, and Visual Studio 2019 Succinctly. He has written
tons of technical articles about .NET, Visual Studio, and other Microsoft technologies in Italian
and English for many developer portals, including MSDN Magazine and the Visual Studio
Developer Center from Microsoft. He has also produced a number of instructional videos in both
English and Italian. Alessandro works as a senior software engineer for Fresenius Medical
Care, focusing on building mobile apps with Xamarin in the healthcare market. You can follow
him on Twitter at @progalex.
10
Chapter 1 Introducing the Xamarin
Community Toolkit
When working on several Xamarin.Forms projects, developers often tend to replicate some
elements used in one project into another one. This is very common with value converters,
custom views, and behaviors. In addition, sometimes developers need to build views that are
common in modern mobile app design, and that the Xamarin.Forms code base does not
include. In order to simplify reusing elements across projects, Microsoft offers a new library
called Xamarin Community Toolkit. This chapter introduces the library and explains some
conventions that I will be using across the book.
If you are a beginner, I recommend you read my free book Xamarin.Forms Succinctly first, since
it will give you all the necessary knowledge you need. In addition, and for exactly the same
reasons, it will not be possible to describe the architecture and the implementation of such
elements, except where strictly necessary. If you need to understand more about architecture
and implementation, you can always look at the official Microsoft documentation and investigate
the source code yourself. You will discover that the backing code is not complex at all, and that
it is very well commented.
Note: At this writing, the latest stable version of the Xamarin Community Toolkit
is 1.2.0, which also adds support for F#. Keep in mind that this library continuously
evolves, which means that new elements might be added over time (after the release
of this book). This is another reason to bookmark the documentation for further
updates.
11
Some of the views included in the Xamarin Community Toolkit bridge the gap between modern
mobile app designs and the Xamarin.Forms code base. Technically speaking, it is a .NET
Standard library that you can quickly add to your projects via NuGet, and for which a GitHub
repository is available. This repository is very important, not only because of the availability of
the source code of the library, but also for the availability of sample code and for the product
roadmap.
12
Tip: Although some of the elements provided by the Xamarin Community Toolkit
will work with previous versions of Xamarin.Forms, some of them require
Xamarin.Forms 5.0. My recommendation is that you ensure your projects are using
Xamarin.Forms version 5.0 or later to use all the features discussed in the book.
At this point, you can decide how to get the source code between cloning the repository in
Visual Studio or downloading the full ZIP archive. The repository contains the full source code of
the library, plus the source code for a complete sample application.
If you want to explore the source code for the Xamarin Community Toolkit, you can open the
Xamarin.CommunityToolkit.sln solution file in Visual Studio. For the purposes of this book, the
solution you need to open is called XCT.Sample.sln, which is in the samples folder of the
repository. Figure 3 shows how the solution appears in Solution Explorer.
13
Figure 3: The official sample project in Visual Studio 2019
The project of interest throughout this book is the shared project called
Xamarin.CommunityToolkit.Sample, highlighted in red in Figure 3. This contains the sample
pages, ViewModels, assets, and resources required to build the sample app, which provides
access to examples of all the objects exposed by the toolkit.
Tip: The reason why I told you to download the source code for the full repository
instead of only the source code for the sample app is that the Xamarin Community
Toolkit in the sample project is not downloaded from NuGet; it is referenced via the
Visual Studio projects in the repository. If you only downloaded the sample app
source code, you would have had to install the NuGet package manually and make
some changes that would result in a waste of time.
Choose a platform project as the startup project, select an appropriate device (either physical or
emulated), and then press F5 to start the sample application. I will use the Android platform
project and an Android emulator, but everything will work similarly on a different device.
When the app is running, you will see a welcome page that you can scroll through to see a list
of cards. Each card represents a shortcut to examples about a specific collection of features.
When you click a card, you access a sublist of cards, and each card provides examples about a
specific feature. If you look at Figure 4, starting from left to right, you can see part of the list of
cards, then the list of subcards for a given feature, behaviors in the figure, and the example
page about a specific feature (one specific behavior, in this case). When topics and features are
discussed, keep in mind this way of accessing examples about features, as this will be assumed
later on.
14
Figure 4: The official sample app running
In addition to discovering and running examples, it is a good idea to define some conventions
that will be used across the book, and that will simplify your reading.
xmlns:xct="http://xamarin.com/schemas/2020/toolkit"
15
In this way, a view like the MediaElement will be accessed as follows.
<xct:MediaElement />
Now that you know what the xct identifier refers to, it will not be repeated in the next chapters,
as well as all the other indications, for the sake of simplicity.
Chapter summary
The Xamarin Community Toolkit is an open-source project backed by Microsoft that provides
common, reusable elements for Xamarin.Forms. In this chapter, you got an introduction to the
library, how to get the sample source code that will be used in the book, and how to set up the
development environment. Next, you got some indications about conventions used in the book
that will make your reading better and clearer. It is now time to start working with all the goodies
that the Xamarin Community Toolkit has to offer, and certainly the more natural way to do so is
by looking at new views.
16
Chapter 2 Working with Views
The Xamarin Community Toolkit includes several views that are of common use in mobile apps
but that were not available in the Xamarin.Forms code base, filling the gap with native
development. Views also include new layouts, which extend the possibilities of arranging the
user interface. This chapter describes layouts and views offered by the library, using the official
sample project as the starting point, but with many considerations about practical use.
New layouts
Three new layouts are available in the Xamarin Community Toolkit: DockLayout, StateLayout,
and UniformGrid. Actually, as you’ll discover shortly, the StateLayout is rather a collection of
attached properties, but it is classified as a layout. This section describes both, and the sample
pages are located under the Pages\Views folder of the official sample project.
17
The XAML code for this example is the following.
<xct:DockLayout
LastChildFill="False">
<Button xct:DockLayout.Dock="Top" Text="Top" HeightRequest="50"/>
<Button xct:DockLayout.Dock="Bottom" Text="Bottom"
HeightRequest="50"/>
<Button xct:DockLayout.Dock="Left" Text="Left" WidthRequest="60"/>
<Button xct:DockLayout.Dock="Left" Text="Left" WidthRequest="60"/>
<Button xct:DockLayout.Dock="Right" Text="Right" WidthRequest="80"/>
<Button xct:DockLayout.Dock="Right" Text="Right" WidthRequest="80"/>
</xct:DockLayout>
• You assign the dock direction to each view via the DockLayout.Dock attached property,
and possible values are Top, Left, Bottom, and Right.
• The LastChildFill property allows you to decide whether the last visual element in the
collection should fill the remaining space in the center.
• If multiple views are docked in the same direction, they will be docked to one another
(see Figure 5 about the last two right-aligned buttons).
DockLayout is extremely useful and adds a lot of flexibility to the user interface, so this is really
a great addition.
Each layout that you make state-aware, using the StateLayout attached properties, contains a
collection of StateView objects. These objects can be used as templates for the different states
supported by StateLayout. Whenever the CurrentState property is set to a value that
matches the State property of one of the StateViews, its contents will be displayed instead of
the main content.
This view is demonstrated in the Pages\Views\StateLayoutPage.xaml, and if you run the sample
app, you can press the Cycle All States button to get an example of how each state can be
used to display a specific view for each state. Figure 6 shows an example.
18
Figure 6: Assigning views to a specific state
Possible states are defined in the LayoutState enumeration, and values can be: None,
Loading, Saving, Success, Error, Empty, and Custom. For example, let’s consider the first
piece of the XAML code that defines a StateView for a Grid, to be used when the state is
loading.
19
As you can see, the Grid uses attached properties of the StateLayout. In the example, the
CurrentState property is bound to a property called MainState of type LayoutState, and is
defined in the backing StateLayoutViewModel class. It represents the current state of the view,
and when it’s None, it’s in a default state. By specifying the collection of StateViews, you can
decide what happens when a state changes.
In this case, a StateView object specifies that if the state changes to Loading, the content of
the Grid must be replaced by a StackLayout containing an ActivityIndicator. When the
state changes back to None, the Grid will also return to display its original content. The type of
state can be supplied by assigning the StateKey property of the StateView with one of the
values from the LayoutState enumeration.
Notice how the ActivityIndicator.IsRunning property is bound to the current state and
converted from LayoutState to bool via the StateToBooleanConverter class. The latter
returns true if the value of the bound property (MainState in this case) equals the state
supplied via the ConverterParameter.
The other states in the example work similarly, but with a different state. The key point is that
you can supply a view for a different state directly inside the current view, without the need to
implement custom logic for state changes and without the need to create a more complex UI
hierarchy.
You can also implement custom states by using the Custom value from the LayoutState
enumeration in combination with the CustomStateKey property of the StateView object. An
example is available in the XAML code of the StateLayoutPage.xaml file.
The binding will then work exactly as explained in the previous steps.
<xct:UniformGrid>
<BoxView Color="Red" />
<BoxView Color="Yellow" />
<BoxView Color="Orange" />
<BoxView Color="Purple" />
<BoxView Color="Blue" />
<BoxView Color="Green" />
<BoxView Color="LightGreen" />
20
<BoxView Color="Gray" />
<BoxView Color="Pink" />
</xct:UniformGrid>
The result is visible in Figure 7. Notice that, unlike in the regular Grid, it is not possible to
specify the RowDefinitions and ColumnDefinitions collections, because the UniformGrid
automatically arranges its content wrapping and alignment as necessary.
New views
New design standards have risen in the recent years, and consequently, new views have
entered into common mobile app designs as well. The Xamarin Community Toolkit bridges the
gap between the Xamarin.Forms code base and such new designs, introducing views that are
now common in mobile apps, and that will help you be more productive by avoiding spending
time on creating custom views.
21
The Xamarin Community Toolkit makes it easy to use avatars in your apps by offering the
AvatarView control. For an understanding of how it works, look at Figure 8, which shows the
sample app in action on the Pages\Views\AvatarViewPage.xaml file.
The app shows a list of people, some of whom are real people currently working at Microsoft,
whereas the Xamarin Monkey and Unknown are test data. Pictures are assigned to the
AvatarView via the public URL, and you will shortly see how. The user interface of the page
mainly consists of a CollectionView, which is populated with the Items property of the
AvatarViewViewModel class, whose code is represented in Code Listing 1.
Code Listing 1
namespace Xamarin.CommunityToolkit.Sample.ViewModels.Views
{
public class AvatarViewViewModel : BaseViewModel
{
public object[] Items { get; } =
{
new { Initials = "AM",
Source = string.Empty,
Name = "Andrei Misiukevich" },
new { Initials = "DO",
Source =
"https://picsum.photos/500/500?image=472",
Name = "David Ortinau" },
new { Initials = "ST",
22
Source =
"https://picsum.photos/500/500?image=473",
Name = "Steven Thewissen" },
new { Initials = "GV",
Source = string.Empty,
Name = "Glenn Versweyveld" },
new { Initials = "JSR",
Source = string.Empty,
Name = "Javier Suárez Ruiz" },
new { Initials = "GV",
Source =
"https://picsum.photos/500/500?image=474",
Name = "Gerald Versluis" },
new { Initials = "XM",
Source =
"https://picsum.photos/500/500?image=475",
Name = "Xamarin Monkey" },
new { Initials = string.Empty,
Source = string.Empty,
Name = "Unknown" }
};
}
}
The Items property is of type object[] and contains a collection of anonymous types. This has
been probably implemented in this way for the sake of speed, but in the real world, you will want
to expose a strongly typed ObservableCollection.
Each object instance allows for specifying an image URL (Source property), name initials if an
image is not available (Initials property), and the full name (Name property). In the XAML
code of the page, the AvatarView represents the DataTemplate of the CollectionView, and
is declared as follows.
<xct:AvatarView.Source>
<UriImageSource Uri="{Binding Source}" />
</xct:AvatarView.Source>
</xct:AvatarView>
• The Text property allows for displaying the name initials only when the image is not
available.
23
• The Source property gets the image to be displayed. In this particular example, it is
retrieved under the form of a UriImageSource object since you pass a URI.
• The ColorTheme property allows for assigning one of the predefined color themes
implemented in the Xamarin Community Toolkit, and for which an introduction will be
given later in this chapter.
If no image and no initials are available, the AvatarView will simply display an X character,
which is the initial of Xamarin.
As you can see, you can use this view to show how many new notifications the user got in the
app. We can discuss how it works starting from the first instance at the top of the XAML, and
then adding information incrementally. At a glance, the BadgeView can be thought of as a
transparent container for another view, on which it overlays a badge with the string you want to
display. In terms of XAML, this works as follows.
<xct:BadgeView
BackgroundColor="Red" TextColor="White" Text="1">
<Label
Text="BadgeView"/>
</xct:BadgeView>
24
As you can see, the Label that displays the notification is surrounded by the BadgeView. You
can specify the background color and the text color. The Text property is of type string, so
you are not limited to displaying only numbers. You do not necessarily need to pass something,
so you can also use an empty string if your purpose is only attracting the attention of the user.
Let’s now walk through possible customizations you can do over the control, and that you will be
able to find in the continuing XAML code.
Font customization
You can customize the font of the BadgeView via the FontFamily, FontAttributes, and
FontSize properties, as you would do with any other view supporting text. FontSize also
supports named size values (such as Normal, Medium, and Large).
Border customization
You can assign the BorderColor property with a value of type Color and set a different color
for the border of the BadgeView, so you can have different border and background colors. In
addition, you can set the HasShadow property with true if you want to display a shadow below
the control. The default is false.
Specifying a position
By default, the badge is placed at the top-right corner. However, it is possible to specify a
different position by assigning the BadgePosition property with one of the values from the
BadgePosition enumeration, all self-explanatory, which can be TopLeft, TopRight,
BottomLeft, or BottomRight. Figure 9 has clear examples of repositioning the badge.
The Xamarin Community Toolkit brings back camera capabilities, and it integrates perfectly with
the latest version of Xamarin.Forms—plus, it is fully backed by Microsoft. Camera support is
offered by the CameraView control, which allows for capturing videos and images from your
device. It is demonstrated in the Pages\Views\CameraView.xaml file. The result of this sample
page is visible in Figure 10. Notice that it is based on the simulator, so the camera is presenting
a fake image.
25
Figure 10: Capturing media from your device
You can control several options of the camera and record the captured content, and now you
will see how. In the sample XAML code, the CameraView is defined as follows.
<xct:CameraView
x:Name="cameraView"
CaptureMode="Video"
FlashMode="On"
HorizontalOptions="FillAndExpand"
MediaCaptured="CameraView_MediaCaptured"
OnAvailable="CameraView_OnAvailable"
VerticalOptions="FillAndExpand" />
The CaptureMode property can be assigned with Video, Photo, or Default (which goes for
taking a picture if the property is not specified). The FlashMode option can be assigned with On
or Off. When the camera starts running on the device, the OnAvailable event is raised. In
terms of camera availability, you might want to check in your apps whether the value of the
read-only CameraView.IsAvailable property returns true.
In the sample code-behind for the page, the code sets the camera zoom based on the value of
a Slider defined in the user interface. The CameraView exposes two properties for managing
the zoom, both of type double. Zoom represents the current zoom value for the camera, and
MaxZoom represents the maximum zoom value for the camera.
Another interesting property is CameraOptions, which allows for selecting the current camera,
and whose value can be Default, Back, Front, or External. Default matches the default
camera settings of the device.
26
The IsBusy property is also interesting and returns true if the camera is busy capturing media
and cannot be used. When working with media content, the CameraView allows you to handle
two events: MediaCaptured and MediaCaptureFailed. The first one is raised once the user
stops capturing content, which means taking a picture or recording a video.
The code-behind for the sample page shows how to handle the event as follows.
If the camera is capturing a picture, an image view called previewPicture is made visible, and
its Source property is assigned with the captured image, which is stored in the Image property
of the MediaCapturedEventArgs object instance (and saved on the device if the camera
settings allow for this).
The user interface also defines a Button called doCameraThings, whose text is changed based
on the selection of taking a picture. Before discussing how things work when working with
videos, let’s have a look at the XAML code for both views.
<Button
x:Name="doCameraThings"
Command="{Binding ShutterCommand, Source={x:Reference cameraView}}"
IsEnabled="False" Text="Start Recording" />
<Image
x:Name="previewPicture"
Aspect="AspectFit" BackgroundColor="LightGray"
HeightRequest="250" IsVisible="False" />
The Command property of the Button is bound to the ShutterCommand property of the
CameraView. This command is invoked when the shutter is triggered. Regarding videos, when
the user stops capturing, the media content is just saved on the device.
27
Tip: Remember that a real application must ask for the user’s permission before
accessing the camera. This can be quickly accomplished via the Permissions class
from the Xamarin Essentials library.
You can also execute an action when the Expander is engaged via the Command property and
its CommandParameter value. When the Expander is collapsed, the view displays the value of
its Header property, defined as follows.
<xct:Expander.Header>
<StackLayout Orientation="Horizontal" Spacing="0">
<Label Text="{Binding Name}"
HorizontalOptions="FillAndExpand"
FontSize="32"
FontAttributes="Bold"/>
<Label Text="Enable nested:"
FontSize="13"
VerticalOptions="CenterAndExpand" />
<Switch IsToggled="{Binding IsEnabled}" />
</StackLayout>
</xct:Expander.Header>
The value of the Header is an individual view, which can also be a layout for a more complex
visual hierarchy. When the Expander is expanded, the view displays the value of its Content
property. This is an implicit property, so you can omit declaring it explicitly, and the view will
display the visual element included between the enclosing tags as follows.
28
<xct:Expander>
<!-- Content goes here… -->
</xct:Expander>
In the sample code, the Content is another Expander to demonstrate nested expanders. Notice
that the Content property should only include an individual View object. If you wish to use a
layout for a more complex visual hierarchy, you can use the ControlTemplate property, in
which you define a DataTemplate that contains your structure.
<xct:Expander.ContentTemplate>
<DataTemplate>
<StackLayout Spacing="0" Margin="10"
Padding="1" BackgroundColor="Black">
<BoxView HeightRequest="50" Color="White" />
<BoxView HeightRequest="50" Color="Red" />
<BoxView HeightRequest="50" Color="White" />
</StackLayout>
</DataTemplate>
</xct:Expander.ContentTemplate>
One common implementation of the header is using arrow icons. For instance, an up-arrow icon
indicates a collapsed state, and a down-arrow icon indicates an expanded state.
29
Displaying certified profile pictures with GravatarImageSource
Gravatar is an online service for providing globally unique avatars, and it is widely used to
create certified profile pictures. By creating a profile picture on Gravatar, you can be sure that
your identity is safe. The profile picture is strictly related to your email address, and this is an
important point to highlight, as you will see shortly.
The Xamarin Community Toolkit allows for displaying certified profile pictures from Gravatar via
the GravatarImageSource class, which is demonstrated in the
Pages\Views\GravatarImagePage.xaml file of the sample project. This object retrieves an image
from Gravatar and returns an ImageSource object that can be assigned to an Image view.
A XAML markup extension is also available, and you are going to see both options in action. A
good idea is first having a look at the sample page in action on the device, shown in Figure 12.
As you can see, an email address is specified, and it is how the GravatarImageSource can
download the profile picture from Gravatar. In the example, you see the same picture twice, but
in the XAML this happens with a different syntax. You also see a default picture, which you can
use in several situations, such as download errors.
If you now look at the XAML code of the page, you will see that the first image is displayed via
the following markup.
<Image>
<Image.Source>
<xct:GravatarImageSource Email="{Binding Email}"
Size="{Binding Size}"
CachingEnabled="{Binding EnableCache}" />
30
</Image.Source>
</Image>
As you can see, the GravatarImageSource object is populating the ImageSource property of
the Image view, and the source for the picture is specified via the Email property. In the sample
project, the email is exposed by the Email property of the GravatarImageViewModel class,
and it is of type string.
You can also specify a size for the picture, with the Size property of type int whose value can
be between 0 and 100. You can also cache the image via the CachingEnabled property in
order to optimize memory. Both Size and EnableCache binding properties are exposed by the
ViewModel. As an alternative, you can use the GravatarImage markup extension, which offers
an inline syntax and works as follows.
The key point here is that you can use the GravatarImage markup extension directly in the
binding specification for the Image.Source property. It is also possible to provide a default
picture in case the GravatarImageSource is not able to retrieve the proper one, and this is
possible by assigning the Default property with an object of type DefaultGravatar.
In the example, the source for the image is intentionally empty so that the value of the Default
property is used, as you can also see in Figure 12 with the third image from the top. The
DefaultGravatar object is an enumeration defined as follows.
31
Playing audio and video
The Xamarin Community Toolkit provides a new view called MediaElement, which allows for
playing audio and video in your applications. Actually, during its development, the
MediaElement control was included in the Xamarin.Forms code base, but now it has been
moved to the Xamarin Community Toolkit library.
MediaElement can play media content from a remote URI, from the local library, from media
files embedded inside the app resources, and from local folders. In the sample project, it is
demonstrated in the Pages\Views\MediaElementPage.xaml file, and it is based on a free, public
Microsoft video about Xamarin that can be streamed online.
Figure 13 shows the MediaElement in action. Notice how the player shows buttons that allow
for controlling the media reproduction, such as Play and Pause.
<xct:MediaElement
x:Name="mediaElement"
Source="https://sec.ch9.ms/ch9/5d93/a1eab4bf-3288-4faf-81c4-
294402a85d93/XamarinShow_mid.mp4"
ShowsPlaybackControls="True" MediaOpened="OnMediaOpened"
MediaFailed="OnMediaFailed" MediaEnded="OnMediaEnded"
HorizontalOptions="Fill" SeekCompleted="OnSeekCompleted" />
The Source property contains the URI of the media file. As you can learn through the official
documentation, local files are also represented by URIs that start with the ms-appx:/// or ms-
appdata:/// prefixes. The ShowsPlaybackControls property allows you to avoid the need to
32
create playback controls manually and will make the MediaElement use the playback control of
each native platform.
You certainly have the option to create your custom controls and use data binding to provide a
different look and feel to your player, but this is out of the scope of this chapter. You can make
media start automatically by setting the AutoPlay property with true, and you can control the
media volume using the Volume property, of type double. Its value must be between 0 and 1.
The MediaElement also exposes the Aspect property, which controls the stretching of the
video, and supported values are Fill, AspectFill, and AspectFit. The Duration property, of
type TimeSpan?, returns the duration of the currently opened media, while the Position
property, of type TimeSpan, returns the current progress over the duration.
The MediaElement view also exposes self-explanatory methods such as Play, Stop, and
Pause that you can invoke in your C# code to manually control the media file. Additionally,
among others, this view exposes events like:
• MediaOpened: Raised when the media stream has been validated and is ready for
playing.
• MediaEnded: Raised when the media stream reaches its end.
• MediaFailed: Raised when an error occurs on the media source.
• SeekCompleted: Raised when you move the reproduction to a different position.
The MediaElement is at the same time a very versatile view in its simplest form, and a
completely customizable view for high-quality media playing designs.
33
Figure 14: Creating shields
There are many Shield declarations in the XAML code for the page, but we will consider just
two of them, since they all work in the same way—just with different property values. The first
Shield to consider is also the first one in the code and is declared as follows.
The Subject and Status properties are assigned with the strings you want to display in the two
parts. You can customize colors for both parts, as well. For the status, you can use the
StatusBackgroundColor property to customize the background and the StatusTextColor to
customize the foreground color of the status string. Similarly, you can assign the
SubjectBackgroundColor and SubjectTextColor properties to customize the subject
background color and foreground color, respectively.
The Shield supports interaction; in fact, it exposes the Tapped event. In the sample project, the
event is simply handled to display an alert as follows.
34
The second Shield that is taken into consideration is the following.
In this example, you see color customization for the subject as described previously, plus you
see how to customize the font size. You can use both named font sizes and numbers. Also, you
can use the FontFamily and FontAttributes properties to further customize the font
appearance as you would do with any other control that supports text.
Shields are probably not very common in mobile apps, but if your project needs to display the
status of some services, then they can be a valid choice with no effort.
If you run the sample app and enter the related example, you will be able to play with the
RangeSlider by customizing its appearance. All the possible options are offered in the page, as
shown in Figure 15, where you can see possible customizations available by scrolling the page.
For example, you can customize the thumbs with any View; you can change the size and color
of the sliding track, the size and color of the thumbs, and the corner radius of the thumbs.
35
In the XAML for the sample page, most of the RangeSlider properties are data-bound to other
views in the page, such as pickers that allow for selecting different colors, as you might have
seen if you have played with the example in the app. Code Listing 2 shows how it is declared.
Code Listing 2
<xct:RangeSlider
x:Name="RangeSlider"
MaximumValue="10"
MinimumValue="-10"
StepValue="0.01"
LowerValue="-10"
UpperValue="10"
ValueLabelStringFormat="{StaticResource
CustomValueLabeStringFormat}"
LowerValueLabelStyle="{StaticResource
CustomLowerValueLabelStyle}"
UpperValueLabelStyle="{StaticResource
CustomUpperValueLabelStyle}"
ThumbSize="{Binding Value,
Source={x:Reference ThumbSizeSlider}}"
ThumbColor="{Binding SelectedItem,
Source={x:Reference ThumbColorPicker}}"
LowerThumbColor="{Binding SelectedItem,
Source={x:Reference LowerThumbColorPicker}}"
UpperThumbColor="{Binding SelectedItem,
Source={x:Reference UpperThumbColorPicker}}"
ThumbBorderColor="{Binding SelectedItem,
Source={x:Reference ThumbBorderColorPicker}}"
LowerThumbBorderColor="{Binding SelectedItem,
Source={x:Reference LowerThumbBorderColorPicker}}"
UpperThumbBorderColor="{Binding SelectedItem,
Source={x:Reference UpperThumbBorderColorPicker}}"
TrackSize="{Binding Value,
Source={x:Reference TrackSizeSlider}}"
TrackColor="{Binding SelectedItem,
Source={x:Reference TrackColorPicker}}"
TrackHighlightColor="{Binding SelectedItem,
Source={x:Reference TrackHighlightColorPicker}}"
TrackBorderColor="{Binding SelectedItem,
Source={x:Reference TrackBorderColorPicker}}"
TrackHighlightBorderColor="{Binding SelectedItem,
Source={x:Reference TrackHighlightBorderColorPicker}}"
IsEnabled="{Binding IsToggled,
Source={x:Reference IsEnabledSwitch}}"
ValueLabelSpacing="{Binding Value,
Source={x:Reference ValueLabelSpacingSlider}}">
<xct:RangeSlider.LowerThumbView>
36
<Label Text="L" VerticalTextAlignment="Center"
HorizontalTextAlignment="Center"
IsVisible="{Binding IsToggled,
Source={x:Reference LowerThumbViewSwitch}}" />
</xct:RangeSlider.LowerThumbView>
<xct:RangeSlider.UpperThumbView>
<Label Text="U" VerticalTextAlignment="Center"
HorizontalTextAlignment="Center"
IsVisible="{Binding IsToggled,
Source={x:Reference UpperThumbViewSwitch}}" />
</xct:RangeSlider.UpperThumbView>
</xct:RangeSlider>
37
Property Type Description
be selected with the
RangeSlider.
38
Property Type Description
Properties described in Table 1 are bindable properties, so for example, you could bind the
Value to a command in the ViewModel and handle the value change. However, the
RangeSlider also exposes events. These are summarized in Table 2.
Event Description
LowerDragStarted Occurs when a drag action is started with the lower thumb.
UpperDragStarted Occurs when a drag action is started with the upper thumb.
39
Event Description
In summary, the biggest benefits of using a RangeSlider are the high level of customization,
and that it allows for selecting a range using two thumbs instead of selecting a single value as
with the regular Slider.
With regard to this, the Xamarin Community Toolkit implements two extension methods that
extend the VisualElement class: DisplayToastAsync and DisplaySnackBarAsync, both
demonstrated in the Pages\Views\SnackBarPage.xaml file. The sample page has four buttons,
each opening a snackbar or toast notification. I will start by discussing toast notifications. They
are implemented via the Clicked event handler for the DisplayToastClicked button as
follows.
You can display a toast notification by invoking the DisplayToastAsync method on the current
page instance. The method takes the string to display; in this case, an intentionally long string is
generated by the GenerateLongText method. The toast notification disappears after 3,000
milliseconds by default, but you can provide a different timeout by invoking the second overload
of DisplayToastAsync, whose second argument is the new timeout expressed in milliseconds.
The previous code produces the result shown in Figure 16.
40
Figure 16: Displaying a toast notification
As you can see, the toast notification does not allow for performing an additional action, which a
snackbar does. You can display snackbars by invoking the DisplaySnackBarAsync method,
which optionally takes an argument of type SnackBarOptions. In its simplest form, you pass the
string you want to display and an action, as demonstrated in the DisplaySnackBarClicked
button event handler.
});
The method provides several overloads; the one considered here shows the string passed as
the first argument and allows for executing the Func<Task> action object passed as the second
argument. Figure 17 shows what this looks like.
41
Figure 17: Displaying a snackbar
Snackbars can be customized through an instance of the SnackBarOptions object. If you look
at the C# code for the DisplaySnackBarAdvancedClicked button event handler, it is easier to
understand how to create advanced snackbars. In the first part, the code customizes the way
the text message is displayed.
},
Duration = TimeSpan.FromMilliseconds(5000),
BackgroundColor = Color.Coral,
IsRtl = CultureInfo.CurrentCulture.TextInfo.IsRightToLeft,
Notice how easy it is to set the text options with a right-to-left presentation. The duration is
expressed in milliseconds, after which the snackbar will be automatically dismissed. The last
property is about actions that can be executed from within the snackbar. It is possible to supply
multiple actions, as demonstrated in the following code, which assigns the Actions property
with an object of type List<SnackBarActionOptions>.
42
Actions = new List<SnackBarActionOptions>
{
new SnackBarActionOptions
{
ForegroundColor = Color.Red,
BackgroundColor = Color.Green,
Font = Font.OfSize("Times New Roman", 15),
Padding = new Thickness(10, 20, 30, 40),
Text = "Action1",
Action = () =>
{
Debug.WriteLine("1");
return Task.CompletedTask;
}
},
new SnackBarActionOptions
{
ForegroundColor = Color.Green,
BackgroundColor = Color.Red,
Font = Font.OfSize("Times New Roman", 20),
Padding = new Thickness(40, 30, 20, 10),
Text = "Action2",
Action = () =>
{
Debug.WriteLine("2");
return Task.CompletedTask;
}
}
}
};
You can customize the appearance of each individual action by assigning the
ForegroundColor, BackgroundColor, Font, Padding, and Text properties. The action
executed is an object of type Func<Task>. The way the method is invoked is the following.
Tip: Snackbars and toast notifications can be anchored to a specific view because
both the DisplayToastAsync and DisplaySnackBarAsync methods extend the
VisualElement class, so you can invoke such methods on every view. The sample
app has examples about this scenario, in particular the “Show Snackbar with Anchor”
example.
43
Displaying pop-ups
The Xamarin Community Toolkit exposes the Popup view, which allows for rendering native
pop-ups on each supported platform. Pop-ups can also be customized in several ways. The
sample project provides many examples, located under the Pages\Views\Popups folder.
Note: The sample project provides many examples on pop-ups, but most of them
are built by adding property assignments or views to the pop-up content. For this
reason, I will start discussing pop-ups from the simplest examples possible,
explaining only the most relevant features.
The best thing to do is to start from the simplest pop-up, and then walk through customization
possibilities. The Simple Popup example looks like Figure 18.
The content of a pop-up is generally a layout, so that you can create a complex visual hierarchy
with a title, text, and other views. Code Listing 3 shows the XAML markup for the simple pop-up
shown in Figure 18.
Code Listing 3
44
<xct:Popup.Resources>
<ResourceDictionary>
<Style x:Key="Title" TargetType="Label">
<Setter Property="FontSize" Value="26" />
<Setter Property="FontAttributes" Value="Bold" />
<Setter Property="TextColor" Value="#000" />
<Setter Property="VerticalTextAlignment"
Value="Center" />
<Setter Property="HorizontalTextAlignment"
Value="Center" />
</Style>
<Style x:Key="Divider" TargetType="BoxView">
<Setter Property="HeightRequest" Value="1" />
<Setter Property="Margin" Value="50, 25" />
<Setter Property="Color" Value="#c3c3c3" />
</Style>
<Style x:Key="Content" TargetType="Label">
<Setter Property="HorizontalTextAlignment"
Value="Start" />
<Setter Property="VerticalTextAlignment"
Value="Center" />
</Style>
<Style x:Key="PopupLayout" TargetType="StackLayout">
<Setter Property="Padding"
Value="{OnPlatform Android=20, UWP=20, iOS=5}" />
</Style>
</ResourceDictionary>
</xct:Popup.Resources>
A pop-up is declared inside an individual XAML file and is exposed via the Popup class. You can
control the pop-up size with the Size property, of type Xamarin.Forms.Size. In Code Listing 3,
you can see that such a property is bound to an enumeration called PopupSize, which is not
part of the library, rather it is part of the sample code, so you can walk through it as an exercise.
45
The StackLayout actually represents the content of the pop-up, which means you can show
whatever you need. The styles defined in the Popup.Resources are applied to the child views,
and the result you get is shown in Figure 18. In order to display pop-ups, you need to invoke a
new extension method called ShowPopupAsync, which extends the Navigation class and takes
the pop-up instance as an argument. The usage of this method is demonstrated in the
PopupControlViewModel.cs file, located under ViewModels\Views, and works like this:
await Navigation.ShowPopupAsync(popup);
Optionally, ShowPopupAsync can return a result if the pop-up is enabled to do so. If you look at
the ReturnResultPopup.xaml file, you will see the following assignment in the pop-up
declaration.
x:TypeArguments="x:String"
With this assignment, the method will be able to return an object of type string, but you can
choose a different type to return. Whatever return type you decide on, the syntax for the method
invocation becomes the following.
By default, pop-ups can be dismissed by tapping outside of them (behavior known as light
dismiss), but sometimes you want to avoid this to make sure users tap on a button and perform
a mandatory action. In this case, you can set the IsLightDismissEnabled property as follows.
IsLightDismissEnabled="False"
You can implement actions inside pop-ups. This can be quickly accomplished by adding Button
views to the content of the pop-up. Two examples, called Popup with 1 Button and Popup
with Multiple Buttons, demonstrate this. The following XAML from the second example shows
how to implement multiple buttons.
You can simply handle the Clicked event or bind buttons to commands to execute actions.
Figure 19 shows how the pop-up with buttons from the sample project appears.
46
Figure 19: Implementing actions inside pop-ups
You can also arrange the position of pop-ups by simply assigning the HorizontalOptions and
VerticalOptions properties, both of type LayoutOptions. This allows you to place pop-ups in
a position that is different from the center of the screen (the default). In the
PopupControlViewModel.cs file, you can find several examples. For instance, the following
snippet shows how to display a pop-up at the top-right corner.
47
In summary, native pop-ups can now be displayed very quickly and without the need for building
custom views. In addition, customization options are very easy to implement since most of the
customizations are applied via property assignments, or by adding views to the pop-up content.
Due to the high number of possibilities and customization options, the official sample project
includes several pages to demonstrate the usage of the TabView, located under the
Pages\Views\TabView folder. I will consider the examples that highlight the most relevant
properties and characteristics of the view, starting from the simplest example called Getting
Started that is coded in the GettingStartedPage.xaml file. In the sample app running, this
example looks like the one shown in Figure 21.
As you can see, a tab strip contains two tab definitions, each with its own text and icon. Colors
can be defined in code. The opening tag for the TabView is the following.
<xct:TabView
TabStripPlacement="Bottom"
48
TabStripBackgroundColor="Blue"
TabStripHeight="60"
TabIndicatorColor="Yellow"
TabContentBackgroundColor="Yellow">
The TabStripPlacement property allows you to set the position of the tab strip, and its value
can be Bottom or Top. The TabStripBackgroundColor property, of type Color, allows you to
specify a background color for the tab strip, whereas the TabStripHeight allows you to specify
the tab strip height.
The TabIndicatorColor property, of type Color, is assigned with the color you want to use to
highlight the currently selected tab, whereas the TabContentBackgroundColor property allows
for specifying the background color of the tab content, which is something you can easily
understand by selecting the second tab in the sample page.
The TabView works as a container for TabViewItem objects, each representing a tab. For
example, the first tab in the sample page is defined as follows.
<xct:TabViewItem
Icon="triangle.png" Text="Tab 1" TextColor="White"
TextColorSelected="Yellow" FontSize="12">
<Grid BackgroundColor="Gray">
<Label HorizontalOptions="Center" VerticalOptions="Center"
Text="TabContent1" />
</Grid>
</xct:TabViewItem>
• The tab icon is assigned via the Icon property and is of type ImageSource, so you can
assign icons and images as you would do with any image view.
• The TextColor and TextColorSelected properties, both of type Color, respectively
allow for assigning the color for the text in normal state and in selected state.
• The TabViewItem has a default, implicit Content property that contains the layout or
view that you want to display when the tab is selected, in this case a simple Grid with a
Label inside.
The visibility of the tab strip can be managed via the IsTabStripVisible property. You can
use this property to hide and show the tab strip at runtime based on your conditions. There is
basically no limit to the number of TabViewItem objects you can add, because the TabView has
built-in support for scrolling. If you open the Scrollable Tabs example, you can see nine tabs by
scrolling the tab strip horizontally without specifying any additional property.
An interesting feature of tabs is that they support badges. This is demonstrated in the Tab
Badge example (TabBadgePage.xaml file), as you can see in Figure 22.
49
Figure 22: Display badges on tabs
Creating badges is very easy. Consider the following code snippet, related to the second tab on
the strip.
<xct:TabViewItem
Icon="circle.png"
IconSelected="square"
Text="Tab 2"
TextColor="White"
TextColorSelected="Yellow"
BadgeText="NEW"
BadgeBackgroundColor="DarkOrange"
BadgeBackgroundColorSelected="Red"
BadgeBorderColor="Green"
BadgeBorderColorSelected="LightGreen"
BadgeTextColor="White"
FontSize="10"
FontAttributesSelected="12">
Names of the properties related to badges start with the Badge literal and are self-explanatory.
The BadgeText property can be used to add text to the badge. The BadgeBackgroundColor
and BadgeBackgroundColorSelected properties, both of type Color, respectively allow for
setting the background color for the badge when the tab is not selected and when it is selected.
50
Similarly, you can set colors for the badge border via the BadgeBorderColor and
BadgeBorderColorSelected properties. Finally, you can specify the color for the text in the
badge via the BadgeTextColor property. In this example, some changes are also applied to the
default font of the text in the TabView.
• FontFamily and FontFamilySelected allow for setting the font type to both the normal
and selected states.
• FontAttributes and FontAttributesSelected allow for setting the font attributes to
both the normal and selected states. If you look at the previous code snippet, the value
for FontAttributesSelected is 12. Actually, this is the combination of the 1 and 2
values of the FontAttributes enumeration (Bold and Italic).
• FontSize and FontSizeSelected allow for specifying the font size depending on the
state of the tab.
Before going into customizing tabs, it can be useful to summarize a few more properties:
• The TabWidth property, of type double, allows you to change the width of each
individual tab.
• Because the TabView can contain any view, it can also contain another TabView object
to give you the option to nest tab views and create complex visual hierarchies.
• You can apply animations to the transition between one state and another of the tabs.
This implies that the IsTabTransitionsEnabled property is true. Animations are
represented by classes that implement the ITabViewItemAnimation interface and are
demonstrated in the TabItemAnimationPage.xaml file and in its code-behind C# file.
51
Figure 23: Customizing tabs
As you can see, the tab strip has a completely different look with rounded corners, and the
center tab item has a completely different appearance. Let’s look into this by analyzing small
pieces of code. The TabView definition is using a BoxView with rounded corners as the
background.
<xct:TabView
Style="{StaticResource CustomTabStyle}">
<xct:TabView.TabStripBackgroundView>
<BoxView
Color="#484848"
CornerRadius="36, 36, 0, 0"/>
</xct:TabView.TabStripBackgroundView>
This means that the background for the tab strip can not only be a color, but also a view to be
set via the TabStripBackgroundView property. The style applied to the TabView is very simple.
<Style
x:Key="CustomTabStyle" TargetType="xct:TabView">
<Setter
Property="IsTabTransitionEnabled" Value="True" />
<Setter
Property="TabStripHeight" Value="48" />
52
<Setter
Property="TabContentBackgroundColor" Value="#484848" />
<Setter
Property="TabStripPlacement" Value="Bottom" />
</Style>
All three TabViewItem objects redefine their base layout by assigning the ControlTemplate
property with objects of type ControlTemplate, defined in the same page. While for the first
and third tab, the control templates are quite easy to understand, the control template for the
second tab is more complex (and interesting), so I will focus on this, which is defined as follows.
TabTapped="OnFabTabTapped" />
The first thing to notice is that TabViewItem objects support interaction via the TabTapped
event. The sample project displays a simple alert on tap.
The TabTappedEventArgs object exposes the Position property (0-based), of type int, which
allows you to understand the position of the tapped tab inside the strip. The redefined control
template for the current tab is shown in Code Listing 4.
Code Listing 4
<ControlTemplate
x:Key="FabTabItemTemplate">
<Grid>
<ImageButton
InputTransparent="True"
Source="{TemplateBinding CurrentIcon}"
Padding="10"
HorizontalOptions="Center"
BackgroundColor="#FF0000"
HeightRequest="60"
WidthRequest="60"
Margin="6">
<ImageButton.CornerRadius>
<OnPlatform x:TypeArguments="x:Int32">
<On Platform="iOS" Value="30"/>
<On Platform="Android" Value="60"/>
<On Platform="UWP" Value="36"/>
</OnPlatform>
</ImageButton.CornerRadius>
<ImageButton.IsVisible>
53
<OnPlatform x:TypeArguments="x:Boolean">
<On Platform="Android, iOS, UWP">True</On>
<On Platform="macOS">False</On>
</OnPlatform>
</ImageButton.IsVisible>
</ImageButton>
<BoxView
InputTransparent="True"
HorizontalOptions="Center"
CornerRadius="30"
BackgroundColor="#FF0000"
HeightRequest="60"
WidthRequest="60"
Margin="6">
<BoxView.IsVisible>
<OnPlatform x:TypeArguments="x:Boolean">
<On Platform="Android, iOS, UWP">False</On>
<On Platform="macOS">True</On>
</OnPlatform>
</BoxView.IsVisible>
</BoxView>
</Grid>
</ControlTemplate>
At the core of the template there is an ImageButton view, which is the main view in the default
template. Notice how the Source property assigns the default icon via the TemplateBinding
extension. All the other properties are related to the position, colors, and size. For the corner
radius and for its visibility, the code makes different decisions depending on the platform the
app is running on. A BoxView is used to display the red circle in the template. If you now look
back at Figure 23, it should be clearer how powerful customization options are, especially if you
have knowledge of how control templates work in Xamarin.Forms (and more generally, in
development platforms based on XAML).
Note: You have learned all the most important things about implementing and
customizing tabs and the tab strip. It will now be easier to look at the remaining
sample pages, which leverage the same concepts.
54
Xamarin.Forms has support for accessibility, but the Xamarin Community Toolkit goes a step
further by providing the SemanticOrderView control and the SemanticEffect effect. These are
both discussed here because they belong to the same area. In the sample project, the
SemanticOrderView is represented in the Pages\Views\SemanticOrderViewPage.xaml file.
Tip: You will need to turn on accessibility on your device; otherwise, you will not
be able to hear the text-to-speech engine working in the sample app.
It is declared as follows.
<xct:SemanticOrderView x:Name="acv">
<StackLayout>
<Label x:Name="second" Text="Second" Margin="0,20" />
<Button x:Name="third" Text="Third" Margin="0,20" />
<Label x:Name="fourth" Text="Fourth" Margin="0,20" />
<Button x:Name="fifth" Text="Fifth and last" Margin="0,20" />
<Button x:Name="first" Text="First" Margin="0,20" />
</StackLayout>
</xct:SemanticOrderView>
It basically simplifies the way the user can understand the order of visual elements in a list. The
SemanticEffect object makes some steps forward, helping users to identify the type of content
in the user interface. If you look into the Pages\Effects\SemanticEffectPage.xaml file, you will
see several code snippets of interest. First, a series of Label views declared as follows.
The HeadingLevel property of the SemanticEffect allows for specifying the heading level of a
string inside a text hierarchy. This property is of type HeadingLevel, an enumeration that
supports up to nine heading levels. Another property of the effect is Description and it works
like in the following Label.
When this property is assigned, the device will speak, saying the value of the description. The
last property of the effect is Hint.
<Button
55
Text="Button with hint"
xct:SemanticEffect.Hint="This is a hint that describes the button. For
example, 'sends a message'"/>
The Hint property contains a suggestion or explanation on what a view does. Figure 24 shows
what the sample page looks like, but it obviously cannot represent the spoken phrases.
Microsoft has also published a video about using these new elements, where they are properly
demonstrated with live actions and audio, so I recommend you watch it for a better
understanding.
Chapter summary
Modern mobile designs often include views that are not part of the Xamarin.Forms codebase,
but that are now becoming of common use. The Xamarin Community Toolkit bridges this gap by
providing several views of common use, so that developers do not need to use third-party
controls or develop such views on their own, thus saving time, eliminating external code
dependencies, and improving productivity.
56
However, new views are only a part of modern designs. In fact, these also depend on device
features and screen form factors. The next chapter describes how the Xamarin Community
Toolkit also simplifies the way you can manage the user interface, especially on specific (and
popular) devices.
57
Chapter 3 Improved UI Management with
Effects
In Xamarin.Forms, Effects allows you to customize views with small styling changes, without the
need to implement more complex, custom renderers. The Xamarin Community Toolkit offers
seven effects that address very common needs. This chapter walks through all of them, making
the necessary considerations from the technical point of view.
As usual, the sample app from the official repository will be used for quicker reference. If you
want to use the code in a different page, remember to import the following XML namespace in
the XAML declaration of the page.
xmlns:xct="http://xamarin.com/schemas/2020/toolkit"
On the left side, you can see three Image views with their Source property assigned with a
monochrome icon (the one in the example is available here), and on the right side you can see
three ImageButton views with their Source property assigned with the same icon.
The first icon, the black one, is the original one for both views. For image views, the tint color
has been quickly changed with the following code.
58
<Image Source="https://image.flaticon.com/icons/png/512/48/48639.png"
<Image Source="https://image.flaticon.com/icons/png/512/48/48639.png"
As you can see, you simply need to assign the TintColor property of the effect with an object
of type Color. The way it works for ImageButton views is exactly the same, as you can see in
the following code snippet.
<ImageButton
Source="https://image.flaticon.com/icons/png/512/48/48639.png"
xct:IconTintColorEffect.TintColor="Red"
Grid.Row="2" Grid.Column="1" />
<ImageButton
Source="https://image.flaticon.com/icons/png/512/48/48639.png"
xct:IconTintColorEffect.TintColor="Green"
Grid.Row="3" Grid.Column="1" />
This effect can be very useful when you use icons to present the state of an item in your user
interface, and you want to change the icon color to represent a state change.
The effect exposes two events, Loaded and Unloaded, which can be really useful to take
actions only when a view is loaded or unloaded. If you look at Code Listing 5, you can see how
the effect is applied to both layouts and individual views.
Code Listing 5
x:Class="Xamarin.CommunityToolkit.Sample.Pages.Effects.LifeCycleEffectPage"
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:pages="clr-namespace:Xamarin.CommunityToolkit.Sample.Pages"
xmlns:xct="http://xamarin.com/schemas/2020/toolkit">
<ContentPage.Content>
<StackLayout x:Name="stack">
59
<StackLayout.Effects>
<xct:LifecycleEffect Loaded="LifeCycleEffect_Loaded"
Unloaded="LifeCycleEffect_Unloaded" />
</StackLayout.Effects>
<Label
HorizontalOptions="CenterAndExpand"
Text="When you press the button, the Image will appear and
after 3 seconds will be removed!"
VerticalOptions="CenterAndExpand">
<Label.Effects>
<xct:LifecycleEffect Loaded="LifeCycleEffect_Loaded"
Unloaded="LifeCycleEffect_Unloaded" />
</Label.Effects>
</Label>
<Image
x:Name="img"
IsVisible="false"
Source="https://raw.githubusercontent.com/xamarin/XamarinCommunityToolkit/m
ain/assets/XamarinCommunityToolkit_128x128.png">
<Image.Effects>
<xct:LifecycleEffect Loaded="LifeCycleEffect_Loaded"
Unloaded="LifeCycleEffect_Unloaded" />
</Image.Effects>
</Image>
<Button Clicked="Button_Clicked"
Text="Present Image and Remove it">
<Button.Effects>
<xct:LifecycleEffect Loaded="LifeCycleEffect_Loaded"
Unloaded="LifeCycleEffect_Unloaded" />
</Button.Effects>
</Button>
<Label Text="Log:" />
<Label x:Name="lbl" TextColor="Red" />
</StackLayout>
</ContentPage.Content>
</pages:BasePage>
The sample application only displays a text message when the Loaded or Unloaded events are
raised, but these are the places where you would take your own custom actions. As
demonstrated in the code-behind file, you can use the same event handler for multiple views.
Code Listing 6 demonstrates this.
Code Listing 6
60
lbl.Text += "Button loaded \n";
if (sender is Image)
lbl.Text += "Image loaded \n";
if (sender is Label)
lbl.Text += "Label loaded \n";
if (sender is StackLayout)
lbl.Text += "StackLayout loaded \n";
}
With the is operator, you can quickly check the type of the object that has raised the events
and make the appropriate decisions.
Before the Xamarin Community Toolkit was available, you had to create your own custom view
and implement a custom renderer or effect. Now you can get this done by simply applying the
EntryBorderEffect to the Entry views you want to make consistent.
<Entry.Effects>
<xct:RemoveBorderEffect/>
</Entry.Effects>
Figure 26 shows how the Entry views appear without and with the effect applied, on both iOS
and Android.
61
Figure 26: Applying the RemoveBorderEffect
This is a really cool effect that will save a lot of time with cross-platform design systems.
Note: The SafeAreaEffect does not apply to Android and is simply ignored at
runtime.
The most recent iPhones have larger screens, and consequently, a larger area that app
developers can leverage to build even more beautiful user interfaces. The Apple layout
guidelines divide the screen area on the new iPhones into two parts. The first part is the full
screen area as a whole. The second part is called the safe area, and it is made of a rectangle
that represents the same screen area available on older iPhones.
Figure 27, which is credited to Apple and taken from the Adaptivity and Layout documentation
page, provides a clean representation of how the screen area is organized.
62
Figure 27: The safe area on iPhone devices (source: Apple, Inc.)
When you work with Xamarin.iOS and Xamarin.Forms, your app will automatically fill the entire
screen area. However, there might be exceptions and reasons to avoid filling the entire screen
and use only the safe area, such as:
• You want to really make sure your app looks and runs properly on older iPhones.
• You build for both Android and iOS, and you don’t want to write additional, platform-
specific code for your UI.
• Your app adheres to a design that is thought to be cross-platform, or that is built upon
older iPhone devices and that cannot be changed.
Because by default your app will fill the entire screen when running on iOS, if you want it to run
only inside the safe area, you have to take the responsibility for handling this scenario in code.
Luckily, with Xamarin Community Toolkit, it’s very quick and simple to make your apps use the
safe area only via the SafeAreaEffect.
<StackLayout SafeAreaEffect.SafeArea="true">
</StackLayout>
So, you simply need to assign the SafeArea property of the effect with true.
63
Tip: This example is applied to a StackLayout for consistency with the example
offered by the sample app. However, in the real world, you might need to apply the
effect at the page level for a more consistent layout of the user interface, especially
with different OSes.
As an alternative, you can apply the effect in code-behind with the following code (which
requires a using Xamarin.CommunityToolkit.Effects; directive).
SafeAreaEffect.SetSafeArea(view, value);
view is the view to which the effect is applied, and the value is true (safe area is on) or false
(safe area is off). Actually, the second argument of SetSafeArea is not a bool object; it is a
structure of type SafeArea from the Helpers class. With this structure, you can decide which
sides of the safe area can be enabled with the following constructor overloads.
You can replace the true arguments with false where you do not want the safe area to be
enabled, and then you can pass the safeArea structure instance to the SetSafeArea method
as follows.
SafeAreaEffect.SetSafeArea(view, safeArea);
In the official code example, the safe area is enabled programmatically with a Switch view,
which raises a Toggled event when the state changes, and that is handled as follows.
SafeAreaEffect.SetSafeArea(stack, e.Value);
Figure 28 shows how the safe area looks with the official sample, on an iPhone 12 simulator.
64
Figure 28: The safe area handled on an iPhone 12 simulator
The blue area is the part of the screen that is available to this specific kind of device, whereas
the content is located inside the safe area.
This is another problem that the Xamarin Community Toolkit solves in a clever way, since you
previously had to create a custom renderer or your own effect. Automatic text preselection
happens by simply applying the SelectAllTextEffect to an Entry or Editor, and this is
demonstrated in the Effects\SelectAllTextEffectsPage.xaml file of the sample project.
<Entry Text="https://github.com/xamarin/XamarinCommunityToolkit"
TextColor="{StaticResource DarkLabelTextColor}"
PlaceholderColor="{StaticResource DarkLabelPlaceholderColor}">
<Entry.Effects>
<xct:SelectAllTextEffect/>
</Entry.Effects>
</Entry>
As you can see, it’s very quick and easy. Similarly, the effect can be applied to an Editor view
as follows.
65
PlaceholderColor="{StaticResource DarkLabelPlaceholderColor}">
<Editor.Effects>
<xct:SelectAllTextEffect/>
</Editor.Effects>
</Editor>
If you run the sample code, you will see that the page displays an Entry and an Editor without
the effect, plus an Entry and an Editor with the effect applied. Figure 29 shows how all the
text in the Entry is automatically selected when focused on.
You will get the same result with the Editor at the bottom of the page.
In Xamarin.Forms, only a very few visual elements support shadowing, and with limited
customization options, so in most cases you have to create your own custom renderers. The
ShadowEffect offered by the Xamarin Community Toolkit allows you to apply shadows to
basically any view, and in the sample project it is demonstrated in the
Effects\ShadowEffectPage.xaml file.
For a better understanding, let’s start by looking at the page resulting from the sample code,
shown in Figure 30.
66
Figure 30: Examples of shadows
With this reference in mind, let’s now discuss the usage of the ShadowEffect. In its simplest
form, you apply it to a view by specifying the shadow color as follows.
The biggest benefit of this effect is that it allows you to specify the position of the shadow, with
the OffsetX and OffsetY properties, both of type double, as follows.
More specifically, OffsetX specifies the horizontal distance of the shadow from the view, and
negative values place shadows to the left of the view. OffsetY specifies the vertical distance
from the view, and negative values place the shadow at the top of the element. Another property
of the effect is Radius, which you set like in the following example.
This property, of type double, allows you to set how large the shadow is: the higher the value,
the greater the blur of the shadow. The ShadowEffect object also exposes the Opacity
property, which allows, as the name implies, specifying the opacity of the shadow with a value
from 0 to 1.
67
The TouchEffect is certainly the most complex effect in the library, so it’s a good idea to
discuss its capabilities in separate paragraphs. Before going into the details, you can keep
Figure 31 as a reference for the discussion. It is based on the official sample project, and I will
recall views displayed in the sample page for a better understanding.
At the end of this section, all the pieces will be put together for a comprehensive understanding
of the effect.
Note: The sample project extends the effect with commanding capabilities. This is
not actually part of the TouchEffect; rather, it is part of the Model-View-ViewModel
support offered by the Toolkit, discussed in Chapter 6.
Table 3 describes the most important properties of the TouchEffect that allow for managing
the three states.
68
Fill, AspectFill, and AspectFit from
the Aspect enumeration.
The following XAML code, taken from the sample project, demonstrates how to set up the
normal and pressed states for the first image you see at the top in Figure 31.
<Image xct:TouchEffect.NormalBackgroundImageSource="{xct:ImageResource
Id=Xamarin.CommunityToolkit.Sample.Images.button.png}"
xct:TouchEffect.PressedBackgroundImageSource="{xct:ImageResource
Id=Xamarin.CommunityToolkit.Sample.Images.button_pressed.png}"
69
xct:TouchEffect.IsToggled="False"
xct:TouchEffect.Command="{Binding Command, Source={x:Reference Page}}"/>
You can additionally control the appearance of the view with the IsToggled property. When
true, the view keeps the pressed state. When false, the view returns to the normal state after
it has been pressed.
70
NativeAnimationShadowRadius Set the radius of the shadow for the native
animation.
The XAML syntax for assigning a specific animation type to the properties that support the
Easing class is the following.
xct:TouchEffect.AnimationEasing="{x:Static Easing.CubicInOut}"
The following XAML code demonstrates how the sample project animates the TITLE label, the
one with the dark yellow background you can see in Figure 31.
<StackLayout Orientation="Horizontal"
HorizontalOptions="CenterAndExpand"
Padding="20"
xct:TouchEffect.AnimationDuration="500"
xct:TouchEffect.PulseCount="2"
xct:TouchEffect.NormalBackgroundColor="Gold"
xct:TouchEffect.PressedBackgroundColor="Orange"
xct:TouchEffect.PressedRotation="15"
xct:TouchEffect.Command="{Binding Command,
Source={x:Reference Page}}">
<Label Text="TITLE"
TextColor="White"
FontSize="Large"/>
</StackLayout>
</StackLayout>
There is actually more, because a view can be rotated and translated during an animation.
Table 5 describes the properties that allow you to accomplish this.
71
HoveredRotationX Apply a rotation starting from the specified
point on the x-axis when the view is in
hovered state.
The sample project does not contain an example of these properties, but you can try yourself by
modifying the previous snippet as follows.
<StackLayout Orientation="Horizontal"
HorizontalOptions="CenterAndExpand"
Padding="20"
xct:TouchEffect.AnimationDuration="500"
72
xct:TouchEffect.PulseCount="2"
xct:TouchEffect.NormalBackgroundColor="Gold"
xct:TouchEffect.PressedBackgroundColor="Orange"
xct:TouchEffect.PressedRotation="15"
xct:TouchEffect.PressedTranslationX="50"
xct:TouchEffect.PressedTranslationY="100"
xct:TouchEffect.PressedAnimationDuration="500"
xct:TouchEffect.Command="{Binding Command,
Source={x:Reference Page}}">
<Label Text="TITLE"
TextColor="White"
FontSize="Large"/>
</StackLayout>
If you now run this code, you will see the view translating on the x and y axes when it is
pressed.
Chapter summary
The effects provided by the Xamarin Community Toolkit make it very quick and easy to
implement functionalities of extremely common requirements, and it will save a lot of time during
development. This includes (but it is not limited to) removing borders to Entry views, managing
the safe area on modern iPhones, preselecting text inside Entry and Editor views, or adding
interaction capabilities to any view.
Because saving development time is at the core of this library, in the next chapter you will learn
how to save more with reusable converters.
73
Chapter 4 Saving Time with Reusable
Converters
As for the other chapters, the assumption here is that you already know how value converters
work. If not, I recommend that you read the official documentation before you continue. The
Xamarin Community Toolkit provides a collection of reusable value converters that are
extremely common in a lot of applications, so that you do not need to reinvent the wheel every
time, getting rid of many source code files.
This chapter describes the available value converters in the library, and it will walk through the
examples available with the official sample project. All the converters are located under the
Converters folder of the Xamarin.CommunityToolkit project.
Note: For consistency with a book of the Succinctly series, it is not possible to
show and discuss the class definition of each converter. For this reason, you will
learn how to use each converter and which parameters they need, without going into
the details of the architecture. Converter classes are very easy to understand, so you
can always look at their definition analyzing the .shared.cs files located under the
Converters folder of the Xamarin.CommunityToolkit project. In addition, notice that
the ItemTappedEventArgsConverter and ItemSelectedEventArgsConverter will be
discussed in Chapter 5, because they work in combination with the
EventToCommandBehavior class, discussed with behaviors.
For such situations, the Xamarin Community Toolkit exposes the BoolToObjectConverter
class, which is demonstrated in the Pages\Converters\BoolToObjectConverter.xaml file of the
sample project. For a better understanding, consider Figure 32, where you can see that the
color of a BoxView changes depending on the true or false value of the Switch.
74
Figure 32: Binding and converting a value of type bool to Color
In this example, the bool value coming from the Switch view is converted into a Color.
However, the conversion is possible to any other type for maximum flexibility. Now, let’s see
how this works in code. The BoxView that displays the color is declared as follows.
<BoxView
BindingContext="{x:Reference Name=ColorToggle}"
HeightRequest="200"
WidthRequest="200"
Color="{Binding Path=IsToggled,
Converter={StaticResource BoolToObjectConverter}}" />
Notice how the Color property is bound to the IsToggled property of the Switch, and how the
BoolToObject converter has been specified to convert from one type to another. The converter
declaration in the page resources looks like the following.
<ResourceDictionary>
<converters:BoolToObjectConverter x:Key="BoolToObjectConverter"
FalseObject="#0000FF" TrueObject="#FF0000" />
</ResourceDictionary>
The FalseObject and TrueObject properties specify which values must be returned when the
source property is false or true, respectively. Both properties are of type object, so you can
return anything that derives from object itself (which means any .NET type).
75
The Image view in Xamarin.Forms cannot display a byte array as an image directly, so a
converter is required, and this is something developers do in almost every project. The Xamarin
Community Toolkit simplifies your code base by offering the
ByteArrayToImageSourceConverter, which is demonstrated in the
Pages\Converters\ByteArrayToImageSourceConverterPage.xaml.
In this page, you can see that the converter is declared in the simplest way possible.
<ResourceDictionary>
<xct:ByteArrayToImageSourceConverter
x:Key="ByteArrayToImageSourceConverter" />
</ResourceDictionary>
It can be used when binding a property of type ImageSource, for example the Source property
of the Image view or of the AvatarView, like in the current example.
<xct:AvatarView Size="300"
Source="{Binding Avatar,
Converter={StaticResource ByteArrayToImageSourceConverter}}"
HorizontalOptions="Center" VerticalOptions="Center" />
In the sample project, the ByteArrayToImageSourceViewModel class (which is the data source
of the page) exposes a property called Avatar, of type byte?[]. This property will contain the
profile picture of a person, which is retrieved from the GitHub contributors to the library. The
ViewModel contains all the code that downloads the image and assigns it to the property, but
the relevant part is the following.
if (!response.IsSuccessStatusCode)
return;
var imageBytes =
await response.Content.ReadAsByteArrayAsync().ConfigureAwait(false);
Avatar = imageBytes;
The Avatar property is therefore assigned with a byte array; at the XAML level, when the
assignment happens, the data-binding engine will invoke the converter that will return an
ImageSource object, which is accepted by the Source property of the AvatarView. If you run
the app, you will see how the image will appear after a few seconds required to download it from
GitHub.
Note: The reason I’m not providing a screenshot from the sample app here is that
the image retrieved by the code represents a real person. Though the image is hosted
on GitHub and possibly is public domain, I consider it more appropriate not to show it
here without explicit permission.
76
Converting dates to coordinated universal time
When you have to work with different markets in different time zones, you likely need to
represent dates in a way that is the same across countries. This is accomplished by converting
dates to the coordinated universal time (UTC).
In C# and .NET, this can be done using the DateTimeOffset structure. However, most of the
views that allow working with dates (such as the DatePicker) natively support objects of type
DateTime. For this reason, the Xamarin Community Toolkit provides the
DateTimeOffsetConverter, which makes it easy to bind an object of type DateTimeOffset by
converting an object of type DateTime into its DateTimeOffset counterpart (and vice versa).
<ResourceDictionary>
<xct:DateTimeOffsetConverter x:Key="DateTimeOffsetConverter" />
</ResourceDictionary>
The usage is then very simple, as you can bind a property of type DateTimeOffset passing the
converter, as demonstrated in the following declaration of the DatePicker view.
If you run the sample project, you will see the converter in action in the appropriate page, shown
in Figure 33.
77
Figure 33: Converting a date to UTC
This converter is very useful, because it simplifies the way you address date problems with
different time zones.
For an understanding of the result, look at Figure 34, where you can see an input decimal
number converted into an integer.
<ResourceDictionary>
<xct:DoubleToIntConverter x:Key="DoubleToIntConverter" />
</ResourceDictionary>
78
The relevant part of the code is in the declaration of the Label, which is bound to a property
called Input, of type double, and shows the result of the conversion to int.
<Label
Padding="7,0,0,0"
Text="{Binding Path=Input,
Converter={StaticResource DoubleToIntConverter},
ConverterParameter=1}"
TextColor="{StaticResource NormalLabelTextColor}" />
Notice how a ConverterParameter is passed to the converter. This value is the ratio for the
conversion, which represents the division of the multiplier by the denominator, and that equals 1
by default. This division is used to determine how many decimal numbers the input double
should be rounded by. This can be better understood by looking at the Convert method of the
converter.
};
This ratio is then multiplied by the input double number. This allows you to have control of how
the conversion is done.
79
Converting enumerations into Boolean values
If you need to convert values from an enum into a bool, you can leverage the
EnumToBoolConverter class. This converter is demonstrated in the
Pages\Converters\EnumToBoolConverterPage.xaml file, and it allows you to specify which
values from the enumeration must be treated as true; then you can use the resulting bool as
you like.
The sample page allows for selecting a state for a hypothetical issue on GitHub, and it looks like
the one shown in Figure 35.
The ViewModel also defines a property called SelectedState of type IssueState, used to
bind the current state to the user interface. Converter instances are declared in the page as
follows.
<xct:EnumToBoolConverter x:Key="OpenConverter">
<xct:EnumToBoolConverter.TrueValues>
<vm:IssueState>New</vm:IssueState>
80
<vm:IssueState>Open</vm:IssueState>
<vm:IssueState>Waiting</vm:IssueState>
</xct:EnumToBoolConverter.TrueValues>
</xct:EnumToBoolConverter>
<xct:EnumToBoolConverter x:Key="ClosedConverter">
<xct:EnumToBoolConverter.TrueValues>
<vm:IssueState>WantFix</vm:IssueState>
<vm:IssueState>Rejected</vm:IssueState>
<vm:IssueState>Resolved</vm:IssueState>
</xct:EnumToBoolConverter.TrueValues>
</xct:EnumToBoolConverter>
The first two instances specify the values that should be considered true directly in the
definition, whereas the third one does not specify any value. This is intentional; you will shortly
see how to assign a value at a later stage.
There are multiple instances of the converter because the user interface contains three check
boxes at the bottom, each representing one of the possible states, and each bound to an
instance of the converter.
In the XAML, you will see a number of RadioButton views, each bound to a value of the
IssueState enumeration, but more specifically, you will see how three CheckBox views are
bound to the SelectedState property, so they also need to point to the appropriate converter
to convert this type into bool.
Notice how the last CheckBox is passing a value from the IssueState enumeration to the
bound converter, and this demonstrates how you can pass a value to be evaluated as true at
data-binding time; you are not limited to declaring true values in advance.
81
Converting objects into Boolean values and comparing for
equality
The Xamarin Community Toolkit exposes the EqualConverter, which allows developers to
convert any value binding to a bool, depending on whether or not it is equal to a specific value.
The initial binding contains the object that will be compared, and the ConverterParameter
contains the object to which to compare it.
Figure 36: Converting objects into Boolean values for equality comparison
<ResourceDictionary>
<xct:EqualConverter x:Key="EqualConverter" />
</ResourceDictionary>
The last Label from the top shows how the converter is actually used.
<Label
Padding="7,0,0,0"
BindingContext="{x:Reference Name=ExampleText}"
Text="{Binding Path=Text, Converter={StaticResource EqualConverter},
ConverterParameter=100}" TextColor="{StaticResource
NormalLabelTextColor}" />
The EqualConverter class checks if an object, the user input in this case, equals the value
specified in the ConverterParameter and returns true or false, depending on the result of
the comparison for equality.
As an opposite tool, the NotEqualConverter works exactly like EqualConverter, except for
the fact that it returns true if two objects are not equal and false if they are equal.
82
Displaying images from resources
As you might know, it is possible to store images locally in the app resources. To accomplish
this, you add the desired image files to the shared project. Then, for each image, in the
Properties tool window, you set the Build Action property as EmbeddedResource, whereas
the Copy To Output Directory property is set as Do not copy.
Retrieving and displaying an image from the app resources is very easy to do; if you had an
Image view called Image1, you would write the following line of code.
Image1.Source = ImageSource.FromResource(imageId,
Application.Current.GetType().GetTypeInfo().Assembly);
The imageId object is the image identifier, and has the following form:
AssemblyName.Folder.Filename. If you had an image called myimage.jpg inside a folder called
Resources in a project whose resulting assembly name is MyProject, the image ID would be
MyProject.Resources.myimage.jpg.
This approach works if you can directly access your views in C# code, but it does not work if
you have Image views that are data-bound to properties in a ViewModel, because data-binding
lives in XAML. You need a converter, and the Xamarin Community Toolkit offers one ready to
use, called ImageResourceConverter. What it does is basically implement code similar to the
line you saw previously but building the image ID is still your responsibility.
83
The sample project uses two local images under the Images folder, called button.png and
logo.png. Code Listing 7 shows instead the code for the ViewModel, located at
ViewModels\ImageResourceConverterViewModel.cs, where there is code that builds the image
ID the proper way.
Code Listing 7
using System.Windows.Input;
using Xamarin.Forms;
namespace Xamarin.CommunityToolkit.Sample.ViewModels.Converters
{
public class ImageResourceConverterViewModel : BaseViewModel
{
const string img1 = "button.png";
const string img2 = "logo.png";
const string imagesPath = "Images";
string defaultNamespace;
string? imageName;
ICommand? changeImageCommand;
public ImageResourceConverterViewModel()
{
defaultNamespace = System.Reflection.Assembly.
GetExecutingAssembly().GetName().Name;
ImageName = BuildEmbededImagePath(img1);
}
84
}
As you can see, the constructor retrieves the assembly name with the first line of code. This is
required to build the image ID, which is actually performed by the BuildEmbeddedImagePath
method that takes the image file name as an argument. The resulting image ID is assigned to
the ImageName property, and the image name varies depending on the user tapping a button on
the user interface, an action handled via the ChangeImageCommand. In the XAML, the converter
is declared the usual way.
<pages:BasePage.Resources>
<xct:ImageResourceConverter x:Key="ImageResourceConverter"/>
</pages:BasePage.Resources>
The Image view is bound to the ImageName property of the ViewModel, and invokes the
ImageResourceConverter as follows.
<Image WidthRequest="150"
HeightRequest="150"
Source="{Binding ImageName,
Converter={StaticResource ImageResourceConverter}}"/>
In this way, you can quickly display an image from the app resources, taking advantage of the
data-binding automation—and without the need to write C# code and handle the views’
properties manually.
<ResourceDictionary>
<xct:IndexToArrayItemConverter x:Key="IndexToArrayItemConverter" />
<x:Array x:Key="ValuesArray" Type="{x:Type x:String}">
<x:String>Value1</x:String>
<x:String>Value2</x:String>
<x:String>Value3</x:String>
<x:String>Value4</x:String>
<x:String>Value5</x:String>
</x:Array>
</ResourceDictionary>
85
The declared array contains a series of string values, and the converter is declared the usual
way. The sample app allows entering an index of the array from an Entry, whose Text property
is bound to another property in the ViewModel called Index. Index’s sole purpose is connecting
the Entry with the Label, which displays the correspondent item of the array. It is declared as
follows.
<Label
Padding="7,0,0,0"
Text="{Binding Path=Index, Converter={StaticResource
IndexToArrayItemConverter},
ConverterParameter={StaticResource ValuesArray}}"
TextColor="{StaticResource NormalLabelTextColor}" />
The Index property is an int that represents the index of the desired item in the array. The
Converter property of the binding points to the converter, whereas the ConverterParameter
property allows for specifying the array in which the converter must search the item that
matches the supplied index. Behind the scenes, the IndexToArrayItemConverter returns the
result of the invocation to the Array.GetValue method, which returns the item corresponding to
the supplied index in the form of an object instance.
<ResourceDictionary>
<xct:IntToBoolConverter x:Key="IntToBoolConverter" />
</ResourceDictionary>
The sample code that demonstrates the conversion is the following, where Number is an integer
property defined in the backing ViewModel, and just stores the input value.
<Entry
x:Name="ExampleText"
Placeholder="0 for false other for true"
Text="{Binding Number}"
TextColor="{StaticResource NormalLabelTextColor}" />
<Label
Padding="7,0,0,0"
Text="{Binding Path=Number,
Converter={StaticResource IntToBoolConverter}}"
TextColor="{StaticResource NormalLabelTextColor}" />
The converter implementation is very easy, since it just returns the result of the != operator
compared to 0, but it is still very useful and of quick reuse.
86
Inverting Boolean bindings
When you data-bind properties of type bool, the binding happens when the source property is
true. However, there are situations where you need to make the binding happen when the
source property is false, such as displaying an error state view when the result of an API call is
not successful.
<ResourceDictionary>
<xct:InvertedBoolConverter x:Key="InvertedBoolConverter" />
</ResourceDictionary>
In the sample code, a Label displays a string that represents the opposite state of the Switch
view, as follows.
<Label
BindingContext="{x:Reference Name=ColorToggle}"
Text="{Binding Path=IsToggled,
Converter={StaticResource InvertedBoolConverter}}" />
In the sample app, the result of the code is shown in Figure 38.
87
Figure 38: Inverting the binding of a bool value
88
"Dummy Item 5",
};
The result of the binding is then different depending on the converter. Figure 39 demonstrates
this, where on the left you can see the result of the IsNullOrEmptyConverter class, and on
the right the result of the IsNotNullOrEmptyConverter one.
The declaration of the converters is very simple, as usual. The following is how you declare the
IsNullOrEmptyConverter.
<ResourceDictionary>
<xct1:IsNullOrEmptyConverter x:Key="IsNullOrEmptyConverter" />
</ResourceDictionary>
<ResourceDictionary>
<xct:IsNotNullOrEmptyConverter x:Key="IsNotNullOrEmptyConverter" />
</ResourceDictionary>
In both examples, there is a CollectionView that displays the list of strings with the following
data template.
89
<CollectionView VerticalOptions="StartAndExpand"
HorizontalOptions="Center" SelectionMode="Single"
ItemsSource="{Binding DummyItemSource}"
SelectedItem="{Binding SelectedItem}">
<CollectionView.ItemTemplate>
<DataTemplate>
<Label Text="{Binding .}" Margin="10" TextColor="Black" />
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
Converters are then used in the last Label that displays the result of the converters themselves.
More specifically, for the IsNullOrEmptyConverter, the declaration is the following.
They are both used against the same string: only the result changes.
If you run the sample app and open this sample page, you will see that it’s first a display about
the fact that the data collection is empty. Then, if you click Add, an item will be added to the list
so that the empty state will disappear in favor of the collection. Figure 40 demonstrates this.
90
Figure 40: Displaying and hiding data lists
The IsVisible property of the ListView will be true if this is the value returned by the
ListIsNotNullOrEmptyConverter, which means that the data collection is not empty. Instead,
the IsVisible property of the Label will be true if this is the value returned by the other
converter, ListIsNullOrEmptyConverter.
With these simple converters, you can quickly implement empty states and error states against
ListView controls, without the need to implement custom logic.
91
Converting a list of strings into one string
If you need to bind and convert an IEnumerable<string> object (and obviously derived
collections such as ObservableCollection<string>) into one single string, you can leverage
the ListToStringConverter class, which is demonstrated in the
Pages\Converters\ListToStringConverterPage.xaml file. The ViewModel defines the following
collection of string objects.
};
<ResourceDictionary>
<xct:ListToStringConverter x:Key="ListToStringConverter" Separator="/" />
</ResourceDictionary>
Notice how you can specify a separator for each string. The sample code converts this
collection into one string with the following XAML.
Dummy Item 1/ Dummy Item 2/ Dummy Item 3/ Dummy Item 4/ Dummy Item 5
<ResourceDictionary>
<xct:TextCaseConverter x:Key="TextCaseConverter" Type="Upper" />
</ResourceDictionary>
92
The Type property allows you to specify the target casing. Possible options are Upper, Lower,
FirstUpperRestLower, and None. In the XAML file, a Label displays the result of the
conversion of some input text entered via the Entry view.
<Entry
x:Name="ExampleText"
Placeholder="Enter text here"
Text="{Binding Input}"
TextColor="{StaticResource NormalLabelTextColor}" />
<Label
Padding="7,0,0,0"
BindingContext="{x:Reference Name=ExampleText}"
Text="{Binding Path=Text, Converter={StaticResource TextCaseConverter}}"
TextColor="{StaticResource NormalLabelTextColor}" />
If you run the sample code, you will see the string converted to uppercase.
Tip: Keep in mind that the FirstUpperRestLower option capitalizes only the first
character of the string. If you want to capitalize the first letter of each word in a string,
you need to implement your own converter that returns the result of the invocation to
the System.Globalization.CultureInfo.CurrentCulture.TextInfo.ToTitleCase
method.
For a better understanding of how it works and of how powerful it is, let’s consider the code of
the sample page. The purpose of the sample page is converting to uppercase an input string
and, by default, checking if it is different from the ANDREI string shown in Code Listing 1. This is
possible by combining the TextCaseConverter class with the NotEqualConverter class,
which you saw previously.
<xct:MultiConverter x:Key="myMultiConverter">
<xct:TextCaseConverter />
<xct:NotEqualConverter />
</xct:MultiConverter>
Basically, you just need to declare the converter and assign a key as the identifier, and then you
nest all the converters you want to be used simultaneously. However, this is not enough,
because converters might need parameters. These are supplied in the form of an Array of
MultiConverterParameter objects, as follows.
93
<x:Array
x:Key="multiParams"
Type="{x:Type xct:MultiConverterParameter}">
<xct:MultiConverterParameter
ConverterType="{x:Type xct:TextCaseConverter}"
Value="{x:Static xct:TextCaseType.Upper}" />
<xct:MultiConverterParameter
ConverterType="{x:Type xct:NotEqualConverter}"
Value="ANDREI" />
</x:Array>
For each MultiConverterParameter, you need to assign the ConverterType property with
the converter you want to use via the x:Type reference and the value. This must be of the type
that is accepted by the converter as a parameter; otherwise, an exception will be thrown.
The last step is assigning the MultiConverter to the binding, and this is done exactly as you
would do with an individual converter. In the sample code, this can be seen in the last Label
definition.
<Label
Text="{Binding EnteredName, Converter={StaticResource myMultiConverter},
ConverterParameter={StaticResource multiParams}}"
HorizontalOptions="CenterAndExpand" />
As you can see, you pass the identifier of the MultiConverter to the Converter property of
the binding, and the identifier of the array of parameters to the ConverterParameter property
of the binding. Running the sample app will demonstrate in practice how it works, but the
biggest benefit to remember is that you can use multiple converters simultaneously, including
converters defined outside of the Xamarin Community Toolkit.
94
Figure 41: Converting multiple Booleans into a single one
If you play with the various check boxes, you will see the colored boxes change their color from
red to green when the corresponding check boxes are flagged, and vice versa, depending on
which conditions have been met.
Conditions can be better understood with some code. First, you declare as many
VariableMultiValueConverter instances and as many conditions as you need, in this case
three.
<pages:BasePage.Resources>
<xct:VariableMultiValueConverter
x:Key="AllTrueConverter" ConditionType="All" />
<xct:VariableMultiValueConverter
x:Key="AnyTrueConverter" ConditionType="Any" />
<xct:VariableMultiValueConverter x:Key="TwoTrueConverter"
ConditionType="Exact" Count="2" />
</pages:BasePage.Resources>
Each of the declared converters must be assigned to the DataTrigger of the view to which it is
applied, more specifically to the MultiBinding collection. The following code demonstrates this
for the first BoxView, and the same thing is replicated on the other BoxView objects but
changing the converter to which they refer.
95
VerticalOptions="Center" HorizontalOptions="Center"
HeightRequest="80" WidthRequest="80">
<BoxView.Triggers>
<DataTrigger TargetType="BoxView" Value="true">
<DataTrigger.Binding>
<MultiBinding Converter="{StaticResource AllTrueConverter}">
<Binding Path="CheckBoxValue1" />
<Binding Path="CheckBoxValue2" />
<Binding Path="CheckBoxValue3" />
</MultiBinding>
</DataTrigger.Binding>
<Setter Property="BackgroundColor" Value="LimeGreen" />
</DataTrigger>
</BoxView.Triggers>
</BoxView>
With this approach, multiple bool values can talk to each other, and their combination can be
converted into one single bool.
Chapter summary
Data-binding is one of the most powerful features in development platforms based on XAML. In
many situations, you will need to bind views that support specific data types to objects of
different types, and this is where value converters come in. The Xamarin Community Toolkit
provides a lot of converters of common usage, so that you do not need to reinvent the wheel
every time, which simplifies your code base. Value converters have then to do with data, and so
do the validation behaviors described in the next chapters.
96
Chapter 5 Data Validation with Reusable
Behaviors
In Xamarin.Forms (and, more generally, in XAML-based platforms), behaviors let you add
functionality to user interface views without having to subclass them. The actual functionality is
implemented in a behavior class, but it is simply attached to a control in XAML for easy usage.
As you can imagine, this chapter assumes you already have knowledge of behaviors. If you are
new to this feature, I strongly recommend you first get started with the official documentation.
Here you will find guidance on the behaviors that are most relevant for data validation in the
Xamarin Community Toolkit. Keep the official sample project open in Visual Studio, as this will
serve as the reference for the discussion.
Note: Among others, the Xamarin Community Toolkit also provides behaviors to
animate visual elements. These are not covered in this chapter, whose goal is
explaining how to perform data validation in an efficient way. In addition, the
EventToCommandBehavior is not discussed in this chapter because it is related to
working with the Model-View-ViewModel pattern, so it makes more sense to discuss it
in Chapter 6.
97
IsRunning Returns true when the validation process
is running.
Most of these properties will be recalled very often in the next paragraphs, so it’s important for
you to know what they are about.
For a better understanding, consider Figure 42, where an invalid email address has been
entered.
98
The entered string has been recognized as invalid by the EmailValidationBehavior object,
which automatically performs formal validation. This behavior exposes two properties, IsValid
and IsNotValid, which return true if the email address is either valid or invalid.
Based on these property values, you can style other UI elements; for example, with an invalid
email address, the label you see in Figure 42 is set as visible, and the color for the invalid text is
in red. When the behavior recognizes the supplied email address as valid, the views’ styles
return to their original state. Code Listing 8 shows how this is implemented.
Code Listing 8
x:Class="Xamarin.CommunityToolkit.Sample.Pages.Behaviors.EmailValidationBeh
aviorPage">
<pages:BasePage.Resources>
<Style x:Key="InvalidEntryStyle" TargetType="Entry">
<Setter Property="TextColor" Value="Red" />
</Style>
</pages:BasePage.Resources>
99
</pages:BasePage>
Notice how the InvalidStyle property of the behavior is assigned with a style that is applied to
the Entry only when the email address is recognized as invalid. The DecorationFlags
property allows for managing white spaces in the string. Other supported values, other than
Trim and all self-explanatory, are None, NormalizeWhiteSpace, NullToEmpty, Trim, TrimEnd,
and TrimStart. Finally, notice how the Label becomes visible only when the IsNotValid
property of the behavior becomes true. This is a common approach to display an error message
close to the input box.
Now consider Figure 43. Notice that the sample code offers two examples, and the second one
is based on visual states. I will use the first one.
100
In the example, the UppercaseLetter criterion requires the input string to be all uppercase;
otherwise, it will be considered invalid. In addition, minimum and maximum lengths are also
specified. Because the string in the figure is not all uppercase, it is considered invalid and
displayed in red. The XAML code for this example looks like the following.
};
The values of the CharacterType enumeration are really self-explanatory. Just to give you
some examples, if you select Alphanumeric, the input string is considered valid if it contains
both letters and numbers; if you select Digit, the input string is considered valid if it only
contains numbers. The way the Entry displays the input string (in the example, green when
valid, and red when invalid) is determined by the ValidStyle and InvalidStyle properties,
respectively bound to the following styles.
<pages:BasePage.Resources>
<Style x:Key="InvalidEntryStyle"
TargetType="Entry">
101
<Setter Property="TextColor" Value="Red"/>
</Style>
<Style x:Key="ValidEntryStyle"
TargetType="Entry">
<Setter Property="TextColor" Value="Green"/>
</Style>
</pages:BasePage.Resources>
In the example, all the relevant properties are data-bound to other visual elements. Here’s what
you need to know:
• The MaxLength property must be set on the Entry that needs validation with the
maximum number of allowed characters.
• The ShouldDismissKeyboardAutomatically property of the behavior, of type bool,
allows you to decide whether the keyboard should be automatically dismissed once the
maximum length has been reached.
• The MaxLengthReached event of the behavior is raised when the maximum length has
been reached. With the related event handler, you can decide what action to take when
this happens. In the sample project, the code moves the focus to the next entry.
102
You can quickly try yourself using the sample code. This behavior is very useful when the
validation requirement is only checking for the maximum number of characters in a string.
Validating numbers
The Xamarin Community Toolkit offers the NumericValidationBehavior class, which makes it
possible to validate a number against its length and decimal places. In the sample project, it is
demonstrated in the Behaviors\NumericValidationBehaviorPage.xaml file. Consider Figure 44,
where a number is highlighted in red and considered invalid.
<Entry Placeholder="Number">
<Entry.Behaviors>
<xct:NumericValidationBehavior
InvalidStyle="{StaticResource InvalidEntryStyle}"
MinimumValue="1.0"
MaximumValue="100.0"
MaximumDecimalPlaces="2"/>
</Entry.Behaviors>
</Entry>
The MinimumValue and MaximumValue properties allow you to set the boundaries of the
number. Outside such boundaries, the number is considered invalid. The
MaximumDecimalPlaces property allows you to specify how many decimal numbers can be
accepted after the separator.
103
Validating uniform resource identifiers (URI)
Uniform resource identifiers (also referred to as URIs) are typically used to represent web
addresses. A URI is made up of the protocol (https://) and the address (www.microsoft.com).
Validating URIs is another extremely common requirement in any app, and the Xamarin
Community Toolkit makes this easy via the UriValidationBehavior class. All you have to
pass to the behavior is the type of URI, which can be Absolute, Relative, or
RelativeOrAbsolute (which are the ways the System.Uri class represents URIs).
As for the other behaviors, you can specify a style for the invalid state via the InvalidStyle
property, and one for the valid state via the ValidStyle property. If you run the sample project
and check this behavior, you will see how an invalid URI will be highlighted in red.
For this particular scenario, you can use the RequiredStringValidationBehavior class,
demonstrated in the Behaviors\RequiredStringValidationBehavior.xaml file. The following XAML
code demonstrates how easy it is to use.
104
<Entry.Behaviors>
<xct:RequiredStringValidationBehavior
InvalidStyle="{StaticResource InvalidEntryStyle}"
Flags="ValidateOnValueChanging"
RequiredString="{Binding
Source={x:Reference passwordEntry},Path=Text}" />
</Entry.Behaviors>
</Entry>
Tip: If you want to use this behavior for password validation, it is good practice to
assign the IsPassword property of the Entry with true.
You simply need to bind the RequiredString property to the Text property of an Entry,
Label, Editor, or to any other string you want to match, and set the InvalidStyle with the
style you want to use for invalid strings. Notice that, in this particular case, the Flags property
has been assigned with ValidateOnValueChanging, which causes validation to happen at
every key stroke. If you run the code, you will see the text highlighted in red if it does not match
the text in the first Entry.
105
Note: The original XAML sample code displays validation messages in a different
language, so here you see the English counterparts.
<Entry x:Name="Entry1"
Placeholder="Entry 1"
ReturnType="Next"
xct:SetFocusOnEntryCompletedBehavior.
NextElement="{x:Reference Entry2}"/>
It is very simple to use: you just need to assign the NextElement property of the behavior using
the x:Reference syntax, passing the name of the Entry that will receive the focus. Another
interesting behavior is called UserStoppedTypingBehavior, which is demonstrated in the
Behaviors\UserStoppedTypingBehaviorPage.xaml file of the sample project. This behavior is
useful to assume that the user stopped typing inside an input box, without handling events or
implementing custom logic.
Unlike the other examples, for this behavior the sample project works with a SearchBar, and
the XAML code is the following.
106
</SearchBar>
Chapter summary
Data validation is not always an option in every kind of application, and mobile apps are no
exception. The Xamarin Community Toolkit exposes several reusable behaviors that make it
quick and easy to validate strings without implementing custom logic. In fact, you can quickly
validate email addresses, URIs, characters in a string, numbers, and equality of strings, and you
can even perform multiple validations concurrently.
107
Chapter 6 Supporting Advanced Model-
View-ViewModel Development
One of the biggest benefits of using MVVM is that it is possible to work on the user interface
without touching the code for the logic, making it possible for graphic designers to work on the
XAML markup using specific tools. Developers can also work on the logic and the data
architecture without changing the user interface, so there is also some kind of role separation.
This chapter discusses how the Xamarin Community Toolkit enhances MVVM development,
especially for developers who need to create their own framework and cannot use popular
libraries.
The main purpose of commanding is simplifying the way events are handled from a logic-
separation point of view, and it can certainly be used outside of MVVM, or if you prefer (or need)
to create your own MVVM framework.
So that you don’t have to do this every time for each of your projects, the Xamarin Community
Toolkit helps by providing a reusable implementation of the EventToCommandBehavior, with the
addition of other classes and interfaces that will be discussed in this chapter. For your
reference, Figure 45 shows how the example looks like in the sample app.
108
Figure 45: Handling events as commands
Actually, you will not notice any difference in the functionalities because the counter is simply
incremented at every mouse click. But behind the scenes, this is leveraging a more data-
oriented approach.
Code Listing 9
x:Class="Xamarin.CommunityToolkit.Sample.Pages.Behaviors.EventToCommandBeha
viorPage">
<pages:BasePage.BindingContext>
<vm:EventToCommandBehaviorViewModel />
</pages:BasePage.BindingContext>
109
<Label Text="This sample demonstrates how to use
EventToCommandBehavior. Here we observe Clicked event of the button and
trigger IncrementCommand from ViewModel." />
<Label Text="{Binding ClickCount,
StringFormat='{0} clicks'}" />
<Button Text="Click Me"
TextColor="White"
BackgroundColor="{StaticResource
NormalButtonBackgroundColor}">
<Button.Behaviors>
<xct:EventToCommandBehavior
EventName="Clicked"
Command="{Binding IncrementCommand}" />
</Button.Behaviors>
</Button>
</StackLayout>
</ContentView>
</pages:BasePage>
As you can see, the BindingContext of the page has been assigned with a sample ViewModel
called EventToCommandBehaviorViewModel. This will be discussed shortly with a focus on the
architecture of the behavior. For now, it is important to emphasize that this ViewModel
represents the data context of the page, and that it exposes a command called
IncrementCommand, whose purpose is incrementing a counter.
The key point of the code is in the Button. If you look at Code Listing 9, in the Behaviors
collection, you can see how the EventToCommandBehavior class is used to map the Clicked
event of the button, via the EventName property, to a command in the ViewModel. This means
that when the Clicked event of the button is fired, the behavior redirects the action to the bound
command instead of an event handler.
You might argue that the Button already has a Command property, and that using the behavior is
not necessary, which is certainly true, but obviously this has been used to provide the simplest,
yet most effective example possible.
In real-world scenarios, it is very common to use this behavior with other views where there is
no built-in support for commands. For instance, in a ListView you could use the behavior as
follows.
<ListView>
<ListView.Behaviors>
<xct:EventToCommandBehavior
EventName="SelectionChanged"
Command="{Binding SelectionChangedCommand}" />
</ListView.Behaviors>
</ListView>
110
This would allow you to avoid handling an event in a view’s code-behind to manage data or
logic that is in the ViewModel. At the same time, this approach would make it possible to handle
the action in the ViewModel, which is the place where the logic is, favoring layer separation and
without violating the principles of the MVVM pattern.
For example, when the user taps an item in the list, the ItemTapped event is raised and
information on the tapped item is contained in the ItemTappedEventArgs object. When the
user selects an item in the list, the ItemSelected event is raised and information on the
selected item is contained in the ItemSelectedEventArgs object.
The Xamarin Community Toolkit provides two converters that work in combination with the
EventToCommandBehavior class and allow for retrieving the appropriate event arguments. They
are demonstrated in the Pages\Converters\ItemTappedEventArgsPage.xaml and
Pages\Converters\ItemSelectedEventArgsPage.xaml files, and they work very similarly. Both
assign their result to the EventArgsConverter property of the EventToCommandBehavior
instance, and they work as follows.
<ListView.Behaviors>
<xct:EventToCommandBehavior EventName="ItemTapped"
Command="{Binding ItemTappedCommand}"
EventArgsConverter="{StaticResource
ItemTappedEventArgsConverter}" />
</ListView.Behaviors>
<ListView.Behaviors>
<xct:EventToCommandBehavior EventName="ItemSelected"
Command="{Binding ItemSelectedCommand}"
EventArgsConverter="{StaticResource
ItemSelectedEventArgsConverter}" />
</ListView.Behaviors>
When you make this assignment, the EventToCommandBehavior instance checks if the
EventArgsConverter property contains an object. If any, the assigned converter is called and
the retrieved data can be accessed. An example is in the code-behind file of both sample
pages, and the following code shows how this happens for the
ItemTappedEventArgsConverter inside the ItemTappedEventArgsViewModel class.
111
The ItemTappedCommand can access the bound instance of the Person class because the
behavior has retrieved the appropriate ItemTappedEventArgs instance. The
ItemSelectedEventArgsConverter works the same way; only the returned object is different.
In order to understand more, let’s have a look at the ViewModel bound to the page discussed
previously, whose code is shown in Code Listing 10. The code file is called
EventToCommandBehaviorViewModel.cs, and it is located under ViewModels\Behaviors.
Code Listing 10
using System.Windows.Input;
using Xamarin.CommunityToolkit.ObjectModel;
namespace Xamarin.CommunityToolkit.Sample.ViewModels.Behaviors
{
public class EventToCommandBehaviorViewModel : BaseViewModel
{
int clickCount;
The IncrementCommand property, of type ICommand, is the command that is bound to the button
in the user interface. Different from a common MVVM approach, where the property would be of
type Command and where this would be instantiated with a new instance of the Command object,
with the Xamarin Community Toolkit tools, you can create an instance of the command by
invoking the Create method from the CommandFactory class.
112
In this particular example, the method takes an Action as the argument, whose purpose is
incrementing the ClickCount property by one unit. However, the Create method has 16 more
overloads. These overloads have in common the possibility to take an Action as the first
parameter that represents the Execute call of a command, and a Func<bool> as the second
parameter that represents the CanExecute value of a command.
Among the method overloads, there are some that allow for creating instances of asynchronous
commands, which really makes a difference if compared to the classical commanding pattern
implementation.
Tip: The next example is not part of the official project, so here you learn
something outside of the sample repository.
There are specific overloads of the CommandFactory.Create method that allow for instantiating
asynchronous commands. For a better understanding, consider the following code, whose
purpose is still incrementing the value for the ClickCount property, but asynchronously.
public EventToCommandBehaviorViewModel()
{
IncrementCommandAsync = CommandFactory.Create(async () =>
await IncrementCounterAsync());
}
In addition, following the existing Xamarin.Forms code base, the Xamarin Community Toolkit
also exposes the AsyncCommand class, which implements the IAsyncCommand interface and can
be used like you would do with a normal Command instance. Here is an example:
113
public EventToCommandBehaviorViewModel()
{
IncrementCommandAsync =
new AsyncCommand(() => IncrementAcounterAsync());
}
This approach is useful if you want to stay familiar with the syntax normally used with
synchronous commands. Remember that the AsyncCommand class’s constructor takes a
Func<Task> object as the first argument, representing the action to be executed by the
command, and optionally a second argument of type Func<bool> that determines whether the
command can be executed.
Chapter summary
The Xamarin Community Toolkit enhances your development experience with the MVVM
pattern by allowing you to handle events as commands via the EventToCommandBehavior
class. It also makes it easier to define asynchronous commands via the IAsyncCommand
interface and the CommandFactory.Create method.
The library actually does more: it provides tools to simplify the way applications can be
localized, as discussed in the next chapter.
114
Chapter 7 Localization Made Easy
Localizing applications is a very hot topic, not only in the mobile world. There are many
techniques and approaches to implement localization and string translations, and this absolutely
depends on your architecture. However, especially if you do not need to add or update
translations after your app has been published, it is possible to consider a shared approach.
This is the path drawn by the Xamarin Community Toolkit, which simplifies the way translations
are displayed, leveraging new classes and the XAML data-binding feature. This chapter
describes the classes offered by the library to make your life easier when you need to bring your
app to multiple countries.
Getting started
The Xamarin Community Toolkit makes it easier to localize your mobile applications by
exposing objects that point to translated strings, and that are capable of dynamically updating
the user interface at runtime. Such objects are discussed in detail in the next section. For now,
for a better understanding of the results you can get, focus on the sample app.
When the sample app is running, tap Settings at the upper-right corner of the screen. As you
can see in Figure 46, you will get sample settings in English, and you will have an option to
switch to a different language (Spanish in the example). As soon as you change the language
and tap Save, the user interface will display strings in Spanish. In the sample app, this happens
only in the Settings page, but with the objects provided by the Toolkit, this can happen across
the whole app at the same time.
115
Figure 46: String translation at runtime
The goal of the Xamarin Community Toolkit is twofold: first, it makes it easier to implement
localization; second, it makes it possible to quickly update the user interface with new strings at
runtime. While the second goal might be optional in your app, since you might want to start with
a localized version already instead of asking the user to choose a language, the first goal is
certainly common to every app that needs to be translated.
In the next section, you will learn how this happens. The files of the sample project you might
want to keep open are the SettingPage.xaml, located under Pages, and the
SettingViewModel.cs, located under ViewModels.
If you expand the Resx folder in the sample project, you will see a file called
AppResources.resx. This contains the master strings, which in this case are in English. If you
expand this file, you will see two more files: AppResources.en.resx and AppResources.es.resx.
These, respectively, contain the English and Spanish strings. Figure 47 shows an example
based on the English strings.
116
Figure 47: Resource file for English translations
Note: The reason why there are two files with English strings is that one is the
master; the second one needs to be there because there should always be a file for
each language so that files are independent from the master.
Each language is represented with a read-only struct called Language, which is declared as
follows.
If you want to use a similar translation approach in your apps, you can consider reusing this
structure, which simply stores the language name and the shortened ISO code of the culture
information for that language. This structure is then used in the SettingViewModel class,
where a list is created to populate the Picker view that allows for choosing a language in the
Settings page that you have seen previously in action.
The list of languages and the currently selected language are represented by the following
properties, together with their backing fields.
117
set => SetProperty(ref selectedLanguage, value);
}
The way these properties are populated is discussed in the next section about the
LocalizationResourceManager class. For now, let’s see how they are bound to the user
interface. In the SettingsPage.xaml file, there is the declaration of the Picker view, which looks
like the following.
The SupportedLanguages property populates the list in the Picker, and the
SelectedLanguage property is bound to the SelectedItem property of the view. The Picker
will show the name of the language via the ItemDisplayBinding, represented by the Name
property of each individual instance of the Language class in the SupportedLanguages list.
The next step is understanding how the user interface can handle translated strings and display
them as you change the language. This is a relevant topic and of interest outside of the sample
app, so it is discussed in the next, dedicated section.
LocalizationResourceManager.Current.Init(AppResources.ResourceManager);
118
AppResources is a class that Visual Studio generated starting from the name of the resource
file added to the project (AppResources.resx in this case). It represents a reference to the
resource container that stores the translated strings. The first line of the code in the previous
snippet makes sure the culture information of the app resources and the current culture are
synchronized, which is important when the user interface needs to automatically update.
The second line of code initializes the class with the resources provided, and the final line
assigns a default culture, in this case English. Once the LocalizationResourceManager has
been initialized, it can be used to manage multiple languages across the app. In the sample
project, this is done in the SettingsViewModel.cs file that you started to study previously. If you
look back at this file, you will find the LoadLanguages method, which is implemented as follows.
void LoadLanguages()
{
SupportedLanguages = new List<Language>()
{
{ new Language(AppResources.English, "en") },
{ new Language(AppResources.Spanish, "es") }
};
SelectedLanguage = SupportedLanguages.
FirstOrDefault(pro => pro.CI == LocalizationResourceManager.
Current.CurrentCulture.TwoLetterISOLanguageName);
}
This method assigns to the SelectedLanguage property the first language in the list that
matches the current culture in the LocalizationResourceManager. It is invoked in the
ViewModel’s constructor as follows.
public SettingViewModel()
{
LoadLanguages();
119
public ICommand ChangeLanguageCommand { get; }
The ViewModel also defines a property called AppVersion, of type LocalizedString. This
type deserves a more thorough explanation, which is given in the next section.
Code Listing 11
using System;
using Xamarin.CommunityToolkit.ObjectModel;
using Xamarin.Forms.Internals;
namespace Xamarin.CommunityToolkit.Helpers
{
#if !NETSTANDARD1_0
public class LocalizedString : ObservableObject
{
readonly Func<string> generator;
public LocalizedString(LocalizationResourceManager
localizationManager, Func<string> generator)
{
this.generator = generator;
[Preserve(Conditional = true)]
public string Localized => generator();
[Preserve(Conditional = true)]
public static implicit operator
120
LocalizedString(Func<string> func) =>
new LocalizedString(func);
}
#endif
}
Other than the property change notification, the other relevant part of the class is the Localized
property, which returns the localized version of the resource based on the current culture. This
is a very important property, as you will shortly see in the views. In the ViewModel, the
LocalizedString class is used as follows.
The AppVersion property concatenates a localized string from the app resources, representing
the translation of the version word, and the app version number retrieved via the AppInfo class,
offered by the Xamarin Essentials library. Using a property of type LocalizedString, instead of
a regular string, will cause the bound view to be able to update automatically when the
language changes.
The LocalizedString class usually works in combination with a new markup extension called
Translate, which is actually used to display localized strings in the user interface. You find the
following two examples in the SettingsPage.xaml file.
...
<Label HorizontalTextAlignment="Center"
Text="{Binding AppVersion.Localized}"/>
Behind the scenes, the Translate markup points to the instance of the
LocalizationResourceManager class from which it retrieves the active instance of the .resx
file that contains the translations. In the previous code snippet, you can see how the Label and
the Button have their Text properties bound to the strings defined in the app resources via the
Translate extension. In this way, when the user changes the language at runtime, the Text
property is automatically updated with the new string, without any additional efforts.
121
For strings that are not defined inside a resource file, it is possible to bind the Text property to
the Localized property of the LocalizedString instance, as is happening in the last Label of
the previous code snippet. This will also cause the view to auto-update when the string value
changes based on a different culture.
If you now run the sample app again, following the steps described in the “Getting started”
section of this chapter, it will be completely clear how the translation process works, and why
the user interface can auto-update at runtime without any further efforts on the development
side once the user changes the language.
Chapter summary
Localizing applications is a crucial part of development when you build apps for multiple
markets. There are certainly several techniques available, and the final choice depends on your
architecture. The Xamarin Community Toolkit offers one that works on the front-end side.
With the LocalizationResourceManager, you can quickly retrieve translated strings from .resx
resource files and with the LocalizedString class. With the Translate markup extension, you
can use data binding to assign strings to views that support text and that will auto-refresh when
the user selects a different language at runtime. This approach requires a new app update
every time you want to add a new language, but on the other hand, it is extremely simple and
effective.
122
Chapter 8 Creating the User Interface with
C# Markup
In every development platform based on XAML, the declarative markup is not the only way to
create the user interface. Everything you do in XAML can be also done in C#. Although this
might not be very common, there are situations in which you might need to add views to the
visual tree at runtime depending on some conditions.
Building the user interface with C# can be tricky, especially when you need to set up data-
bindings, and especially because of the way the C# syntax works. In order to simplify creating
the user interface with C#, Microsoft has been working on a new API called C# Markup, which
provides a new syntax you can use to define views, their properties, and their interaction
behaviors more easily and elegantly. This new API is now offered by the Xamarin Community
Toolkit and is the topic of this chapter.
Introducing C# Markup
C# Markup is a new set of fluent APIs that simplify the way developers can create the user
interface in C# with the help of new extension methods, and using a syntax based on lambda
expressions. You will need to install the Xamarin.CommunityToolkit.Markup NuGet package
before using this feature. It is already included in the official sample project, so you do not need
any further steps.
It is worth mentioning that before this feature was released to production, it was part of the
Xamarin.Forms code base in a preview state. So if you had a chance to try this feature in its
early days, you need to remember to upgrade your code by installing the aforementioned NuGet
package and removing the preview flags from the App.xaml.cs file.
In the sample app, you can click the C# Markup card to access a page completely built with this
new feature, shown in Figure 48.
123
Figure 48: An example of a page created with C# Markup
As you can see, the page represents a Twitter-like user interface, with a list of tweets, each with
its own set of visual elements, including images and a search bar. The C# code for the page is
in the Pages\Markup\SearchPage.cs file, whereas the logic for the page is in the
SearchPage.logic.cs file.
You will also notice a file called Styles.cs, where some styles are also defined in C#, as you will
see shortly. Let’s now focus on the definition of the user interface with C# Markup.
Note: This section describes the most relevant and common methods of the
Xamarin C# Markup. For a full list and reference, you can read the official
documentation.
The SearchPage.cs file contains the definition of the page and its child views. The first part of
the page is the header, which includes a back button and the entry where the user can search.
The code is the following.
124
new Entry { Placeholder = "Search" }
.FillExpandHorizontal ()
.Invoke (entry =>
{
entry.Focused += Search_FocusChanged;
entry.Unfocused += Search_FocusChanged;
})
.Bind (nameof(vm.SearchText))
}}.Horizontal ();
The syntax of C# Markup is a combination of the object initializers feature and new extension
methods, together with the shorter syntax of lambda expressions (=>) that simplify property
getters. If you look at the Button declaration, the Style extension method makes it possible to
assign an object of type Style to the current visual element. (Styles used in the example are
also defined in C# inside the Styles.cs file and will be discussed shortly.)
Notice how the Bind extension method quickly allows for binding a property to another object, in
this case a command. If you know how setting up data-binding in C# code normally works, you
can immediately understand the benefit of having this method available. In this particular
example, the bound property BackCommand is defined in the ViewModel and simply allows for
going back to the previous navigation state. You can look at its definition yourself, since it’s not
relevant for the explanation here.
If you now look at the Entry definition, you can see how the FillExpandHorizontal extension
method does with one invocation that you would get assigning the HorizontalOptions
property with a value of FillAndExpand. Then, notice how it is possible to implement
interaction via the Invoke extension method, which allows for assigning event handlers to the
events exposed by the current view instance.
In the end, the Horizontal extension method invoked over the StackLayout provides
horizontal alignment of its child elements, again with a simple method invocation instead of
nested property assignments with a more verbose syntax.
The next relevant piece of the user interface definition that contains interesting extension
methods is the declaration of the CollectionView used to display the list of tweets, and whose
code is shown in Code Listing 12.
Code Listing 12
125
new Grid {
RowDefinitions = Rows.Define (
(TweetRow.Separator, 2 ),
(TweetRow.Title , Auto),
(TweetRow.Body , Auto),
(TweetRow.Actions , 32 )
),
ColumnDefinitions = Columns.Define (
(TweetColumn.AuthorImage, 70 ),
(TweetColumn.Content , Star)),
Children = {
new BoxView { BackgroundColor = Color.Gray }
.Row (TweetRow.Separator) .ColumnSpan
(All<TweetColumn>()) .Top() .Height (0.5),
LikeButton ( )
.Row (TweetRow.Actions) .Column
(TweetColumn.Content) .Left () .Top () .Size (24)
}
})}.Background (Color.FromHex("171F2A"))
.Bind (nameof(vm.SearchResults));
126
In Code Listing 12, you can see many extension methods in action, most of them related to the
definition of the Grid layout that represents the DataTemplate of the CollectionView. For
example, the Rows and Columns collections expose the Define method that simplifies how you
can add rows and columns to the Grid. This method takes an array of objects where each
represents the row or column position and the height. In the previous example, the row or
column position is represented with integer values of the TweetRow and TweetColumn
enumerations.
The next group of methods is related to how visual elements are positioned within their parent
layout. If you consider the visual elements defined inside the Children property of the Grid,
you can see that their position is set via the Row and Column methods. Other methods like Top,
Left, Right, Bottom, TopExpand, LeftExpand, RightExpand, and BottomExpand allow for
specifying the element position with or without expansion to fit the available space.
You can also see how the Margins method allows you to specify a margin with a verbose
syntax (for example, .Margins(left: 10, top: 4)) instead of explicitly assigning an object of
type Thickness. Views that support working with text also expose the Font and FontSize
methods. The first method allows you to assign the font family and font attributes to the view,
whereas the second method allows for assigning the font size with the same fluent approach.
Some more specialized objects for working with text, like Span, expose additional methods to
change the text appearance, such as Bold and Italic.
) .BasedOn (Implicit.Buttons);
127
C# Markup allows for declaring styles by defining properties of type Style<T>, where T is the
target type for the style. In the previous code snippet, the first style is applied to Span objects,
and it simplifies the way properties are assigned with the desired value. The fluent APIs also
allow for using style inheritance via the BasedOn extension method, which you see in action in
the second style, HeaderButton, that targets the Button type. The base style is called
Buttons, and is defined in the Implicit static class located in the same code file, which is
declared as follows.
);
In this example, the base style sets a default background color for the button, and then derived
styles can add further customizations.
For instance, the BindableObjectExtensions.cs file contains extension methods that extend
objects of type BindableObject; VisualElementExtensions.cs contains extension methods that
extend objects of type VisualElement, and so on. This work will not only give you the full list of
fluent APIs available, but it will also give you a different point of view in writing C# code with the
most modern syntax.
Chapter summary
C# Markup is a new set of fluent APIs that simplify the way developers can create the user
interface in C#. New extension methods and a syntax based on lambda expressions provide a
more convenient and elegant way to design views and assign their properties, as well as
implement their interaction behaviors via events. These capabilities are now available via the
Xamarin.CommunityToolkit.Markup NuGet package, and will really boost the way you create the
UI in C#—especially when you need to add views at runtime.
128