Ultimate Guide To Xamarin Forms
Ultimate Guide To Xamarin Forms
Ultimate Guide To Xamarin Forms
Employee Detail
25
Intents / 61
Training / 62
Testing / 62
Publishing / 63
API Endpoints / 64
Conclusion / 89
In this whitepaper, we will walk you through three real-world Xamarin.Forms application
examples, their backend architecture and data access, UI layer and source code. If you are
a .NET developer looking to expand your understanding of Xamarin.Forms application
development by using real sample apps built by fellow developers—you’ve come to the
right place!
From a development standpoint, modern native mobile development can be too limited
and domain-dependent for software engineers who have experience with various stacks,
especially those focused on developing line-of-business applications. Knowing Java and
Objective-C is great if you are serious about developing complex and GUI-heavy apps
for Android and iOS, but if you happen to be a .NET developer working on enterprise
applications, your options for developing mobile apps are rather limited.
Fortunately, cross-platform development for .NET came to the rescue in the face of
While Xamarin is currently the most stable and mature cross-platform framework on the
market designed to cater to the .NET developer community, it comes with its own set of
challenges, especially for people who haven’t had much experience in mobile development
but want or need to reuse their .NET expertise in that field.
Don’t get us wrong, the Xamarin framework does deliver on its promise—it provides
developers with a multifaceted toolset for building iOS and Android applications using
C# as a cross-platform language and .NET as the base framework for both platforms. Yet,
before you dive into your Xamarin project, do take several things into consideration.
If you go down the Xamarin Native road, you can reuse any and every available API for
creating a truly platform-specific, native experience for the end user. This will entail doing
some native iOS or Android tinkering, especially when it comes to updates and application
maintenance.
With Xamarin.Forms on the other hand, you define a single UI layer that is reused across
all platforms. On the plus side, updates and maintenance are significantly simplified since
you’d only need to update a single source file.
Phronesis is considered the virtue of the true inventors and one of the best ways of
learning—by practice and example. We would like to provide you with the practical
knowledge of building powerful Xamarin.Forms applications. As developers, we have skin
in this game and would like to give you our best tips about developing your next great
Xamarin.Forms app.
In the next sections, we will look at the applications showcased in this whitepaper and
make a quick overview of the backend and frontend technologies we used to develop our
Progress® Telerik® Xamarin.Forms sample applications.
The Telerik ERP app is an enterprise resource management application built from the
ground up with Xamarin.Forms, Microsoft’s Azure Services, the MVVM framework and
Progress® Telerik® UI for Xamarin.
The application enables its users to quickly and easily access and manage business-critical
information regarding relations with customers and vendors, business transactions and
the latest updates and follow-ups on products and orders. In todays’ complex and dynamic
global economy, the Telerik ERP app is a must-have for any business that requires
The application provides a seamless user experience that enables users to easily explore
information about employees, customers, products and orders. It allows customers to
interact with a support chat bot that will guide them through the interface and answer
various questions about finding appropriate information.
The Telerik CRM application utilizes three technologies: ASP.NET backend hosted with
Azure App Services, using SQL Server for its database, Azure Bot Framework, part of
Microsoft Cognitive Services suite, using a custom LUIS (Language Understanding) for an
AI-power, machine-learning trained in-app bot support service and—Telerik UI for Xamarin
suite powering the frontend of the application.
The Telerik ToDo sample application is a perfect example of a lightweight popular app
Microsoft Azure
Throughout the development process of our mobile apps, we heavily relied on the rich
variety of Microsoft Azure suite. From data hosting in the cloud to creating an AI-powered
chatbot, our sample apps leverage the capabilities of Microsoft Azure. Here are some of
the technologies we used:
• Microsoft Azure Services for hosting the data of the Telerik ERP application in the
cloud
• Azure App Services for hosting the ASP.NET backend and SQL database of the
Telerik CRM application
• Azure Bot Framework for the AI-power, machine-learning trained in-app bot support
service of the Telerik CRM application
MVVM Framework
Every single Telerik demo application has been built with a MVVM framework
implementation. There are multiple benefits of using the MVVM framework for Xamarin.
Forms development. The framework provides you with all the tools you need to create a
great Xamarin.Forms app, including dependency injection, viewmodel-view association,
navigation, messaging and more. The framework itself has multiple implementations and
• MVVMCross: built for the Xamarin development community. We used MVVMCross for
our ERP sample application
We covered most of what is invisible to the end user, backend and architecture-wise. But
the most crucial element of an application, its interaction point with users, is still to be
discussed. We built our Telerik Xamarin.Forms sample applications using only controls
from our own Telerik UI for Xamarin suite. ListView, TreeView, Popups, conversational UI,
DataGrid—our UI library has every component you will ever need to build truly stunning
Xamarin.Forms applications.
Now, let’s see how all these technologies tie together to create a few real-world Xamarin.
Forms applications.
The data of the application is split into two parts. The first part contains only the business
The application uses Entity Framework’s code first approach to create the database. Inside
the DataObjects folder, you can find the entities that the application needs. Please note
that they all derive from EntityData. This will help in any data serialization/deserialization
process.
As with any real-world application, the separate entities are interconnected. The customer
has a collection of addresses and a collection of orders. Having such relations can result in
complications when loading the required information from the database. To avoid this, we
have introduced custom TableControllers that can be found inside the Controllers folder.
These controllers use a custom ActionFilterAttribute to control what additional information
gets loaded when you execute a query against the database.
public class CustomerController : TableController<Customer>
base.Initialize(controllerContext);
[ExpandProperty(“Addresses”)]
return Query();
// GET tables/Customer/48D68C86-6EA6-4C25-AA33-223FC9A27959
[ExpandProperty(“Addresses”)]
[ExpandProperty(“Orders”)]
return Lookup(id);
You can see that the GetAllCustomers() method has the ExpandProperty(“Addresses”)
attribute. This will populate the addresses collection for each of the customers before
returning result. The GetCustomer(string id) method will load the addresses as well as the
orders of a single customer. If we didn’t introduce these controllers, the EntityFramework
would return empty collections for the addresses and orders and we would be forced to
do additional roundtrips to the database to fetch them. Controllers allow us to do this in a
single roundtrip.
To allow the EntityFramework to create, connect and modify the SQL database, we have
created a custom DbContext that can be found inside the Models folder. This context
holds the separate DbSets, which the application needs, and the connection string to the
database. It also configures the DbModelBuilder. Based on this context and the EntityDate
objects, the EntityFramework autogenerates code that is used to create the database
schema. This code is then wrapped in a migration, which can be found in the Migrations
folder. Executing this migration will prepare all tables in the database.
modelBuilder.Conventions.Add(
new AttributeToColumnAnnotationConvention<TableColumnAttribu
te, string>(
The final step is to populate the database with actual data. This is achieved by a custom
DbMigrationsConfiguration, which allows us to override the Seed() method and control
what data gets populated into the separate database tables.
public Configuration()
AutomaticMigrationsEnabled = false;
SetSqlGenerator(“System.Data.SqlClient”,
ContextKey = “telerikerpService.Models.telerikerpContext”;
if (!context.Customers.Any())
context.Customers.AddOrUpdate(SeedData.Customers);
context.CustomerAddresses.AddOrUpdate(SeedData.CustomerAddresses);
if (!context.Products.Any())
context.Products.AddOrUpdate(SeedData.Products);
if (!context.Vendors.Any())
context.Vendors.AddOrUpdate(SeedData.Vendors);
if (!context.Orders.Any())
{
context.Orders.AddOrUpdate(SeedData.Orders);
context.OrderDetails.AddOrUpdate(SeedData.OrderDetails);
}
}
Note that the actual initial data is stored in a static class, which can be found in the App_
Start folder.
Now that we covered the backend, let’s continue with the application structure. We will go
over the UI and the MVVM framework of choice.
When building our ERP sample application, we made sure to stick to the best
programming practices by the worldwide Xamarin community. Most of you will relate to
building applications with MvvmCross.
On top of the code implementation sits the beautiful and highly-performant UI of the
application, built with Telerik UI for Xamarin controls.
CreatableTypes()
.InNamespace(typeof(Services.IErpService).Namespace)
.EndingWith(“Service”)
.AsInterfaces()
RegisterCustomAppStart<AppStart>();
Services
The AuthenticationService is used by the LoginPageViewMode. It takes the provided
username and password and validates them. If the authentication is successful, the main
page is visualized. Currently, the service does not have a specific implementation while
login is executed. It is up to you to implement the desired functionality here:
return Task.FromResult(false);
this.UserName = username;
return Task.FromResult(true);
The ErpService then establishes the connection with the database. Its main purpose is
to execute the CRUD operations with the data provided by the database. Through its
constructor, an IMvxMessenger is injected.
this.messenger = messenger;
This is a specific service provided by MvvmCross and its main purpose is to send
messages to the ViewModels. In order to receive the messages, the ViewModel
should inject the service as well, which enables you to subscribe to messages sent
by the ERPService. Each ViewModel must call one of the Subscribe methods on the
IMvxMessenger and must store the returned token in order to be able to successfully
receive the messages.
ViewModels
A constructor injection is used for each ViewModel. This injection is used internally within
MvvmCross when the ViewModels are created. The base class of each ViewModel is
the MvcViewModel class. By inheriting from it, the services could be provided to each
ViewModel through injection.
Let’s look at one of the ViewModels that is part of the ERP application—the
DashboardPageViewModel.
this.service = service;
this.navigationService = navigationService;
base.Prepare();
IsBusy = true;
await this.FetchData();
if (await this.service.SyncIfNeededAsync())
await FetchData();
IsBusy = false;
From this ViewModel, navigation is only executed to the AboutPage. For this purpose,
the injected navigation service is used, and navigation happens from one ViewModel to
another:
this.navigationService.Navigate<AboutPageViewModel>();
For the ERP application, however, we used another feature provided by MvvmCross —
the generic implementation of the MvxContextPage where you can specify the exact
ViewModel of the page.
Each View should inherit from the MvxContextPage instead of the Xamarin.Forms content
page. Here is how the DashboardPage inherits from MvxContentPage:
Here, the ViewModel of the page is passed using the Generic version of the
MvxContentPage. There are some built-in attributes, through which you can define how a
view will be displayed. Some of the existing attributes are used to tell the page that it is a
Modal or is inside a NavigationView.
The Telerik ERP application has a unique look for Desktop, Tablet and Phone
that is achieved using custom presentation attributes. By implementing the
IMvxOverridePresentationAttribute, you can set a specific custom attribute for the page.
For the DashboardPage, for example, if the application is visualized on Phone, the page is
wrapped inside a navigation page. When displayed on Desktop or Tablet, it isn’t:
if (Device.Idiom == TargetIdiom.Phone)
else
In addition to the different wrapping, the page itself has a different look for Desktop and
Phone:
<mcf:MvxContentPage.Content>
<OnIdiom x:TypeArguments=”ContentView”>
<OnIdiom.Phone>
<cards:PhoneView/>
</OnIdiom.Phone>
<OnIdiom.Tablet>
<cards:TabletView />
</OnIdiom.Tablet>
<OnIdiom.Desktop>
<cards:TabletView />
</OnIdiom.Desktop>
</OnIdiom>
</mcf:MvxContentPage.Content>
Xamarin Charts
Telerik UI for Xamarin provides many different types of charts and series that are
extremely flexible. Some of them are presented and visualized in the DashboardPage.
With the Chart control, you can show trends and data using line charts, area charts
and spline charts, or visualize data comparisons using bar charts. You can even depict
proportions with pie charts. The PieChart is the main hero in the DashboardPage.
<telerikChart:RadPieChart.ChartBehaviors>
<telerikChart:ChartSelectionBehavior
SeriesSelectionMode=”None” DataPointSelectionMode=”None”/>
</telerikChart:RadPieChart.ChartBehaviors>
<telerikChart:RadPieChart.Series>
</telerikChart:RadPieChart.Series>
</telerikChart:RadPieChart>
The legend displayed on the right side of the Xamarin PieChart is a separate control.
The control makes it easy for you to provide a description regarding the series that are
visualized within the chart. The ChartProvider should only be set to the Chart control whose
series will be described in the Legend and the Legend will visualize the items out of the box.
Using the LegendTitleBinding of the series, you can specify the item that will be used as a
title for the legend.
Style=”{StaticResource LegendStyle}”
LegendProvider=”{x:Reference Name=pieChart}”/>
The Sales Quantities is presented using another type of Xamarin Chart—the CartesianChart.
Here, we have two LineSeries that visualize the expected and actual sales data. The
visualization of several types of series is supported out-of-the-box by the Chart control.
For the CartesianChart, we have specified both the horizontal and vertical axes as well. The
<telerikChart:RadCartesianChart.ChartBehaviors>
<telerikChart:ChartSelectionBehavior
SeriesSelectionMode=”None” DataPointSelectionMode=”None”/>
</telerikChart:RadCartesianChart.ChartBehaviors>
<telerikChart:RadCartesianChart.Series>
<telerikChart:LineSeries ItemsSource=”{Binding
ExpectedSalesQuantitues}” ShowLabels=”false” DisplayName=”Expected”>
<telerikChart:LineSeries.ValueBinding>
<telerikChart:PropertyNameDataPointBinding
PropertyName=”Value”/>
</telerikChart:LineSeries.ValueBinding>
<telerikChart:LineSeries.CategoryBinding>
<telerikChart:PropertyNameDataPointBinding
PropertyName=”Name” />
</telerikChart:LineSeries.CategoryBinding>
</telerikChart:LineSeries>
<telerikChart:LineSeries ItemsSource=”{Binding
ActualSalesQuantitues}” ShowLabels=”false” DisplayName=”Actual”>
<telerikChart:LineSeries.ValueBinding>
<telerikChart:PropertyNameDataPointBinding
PropertyName=”Value”/>
</telerikChart:LineSeries.ValueBinding>
<telerikChart:LineSeries.CategoryBinding>
<telerikChart:PropertyNameDataPointBinding
PropertyName=”Name” />
</telerikChart:LineSeries.CategoryBinding>
</telerikChart:LineSeries>
</telerikChart:RadCartesianChart.Series>
<telerikChart:RadCartesianChart.HorizontalAxis>
<telerikChart:CategoricalAxis LabelTextColor=”Black”
</telerikChart:RadCartesianChart.HorizontalAxis>
<telerikChart:RadCartesianChart.VerticalAxis>
<telerikChart:NumericalAxis LabelTextColor=”White”
Minimum=”0” />
</telerikChart:RadCartesianChart.VerticalAxis>
<telerikChart:RadCartesianChart.Grid>
</telerikChart:RadCartesianChart.Grid>
</telerikChart:RadCartesianChart>
The Business Overview and Order Status data is visualized with RadPieChart. This time
we used DonutSeries instead of PieSeries. DonutSeries visualizes the chart in the shape of
a donut. The inner empty space is set according to the InnerRadiusFactor property. Each
data item is visually represented by a donut slice. There are no other differences compared
to the PieChart visualized in the Sales Channels.
You can find detailed information about the Chart control and all the series and axes it
provides in the official documentation of the Telerik UI for Xamarin controls.
Xamarin DataGrid
The tables that are visualized on the page are implemented using yet another control from
the Telerik UI for Xamarin suite—the Xamarin DataGrid control.
Most of the data on the Internet is stored in tables within a database. RadDataGrid for
Xamarin provides the same abstraction over the data. It has Columns and Rows and
the intersection of a Row and a Column—a Cell. The control supports different type of
columns, selection modes, loading data on demand, a rich customization API and many
more features.
In order to visualize data using the DataGrid control, you have to set its ItemsSource
property. By default, the control will generate out-of-the-box columns using the
provided data type. If you want to manually declare the desired columns, set the
In the DashboardPage, the DataGrid control is used to visualize data for the latest orders,
recently added products and best vendors.
Here is how the DataGrid is declared for the best vendors part:
AutoGenerateColumns=”false”
Style=”{StaticResource DataGridStyle}”>
<telerikDataGrid:RadDataGrid.Columns>
<telerikDataGrid:DataGridTextColumn HeaderText=”Name”
PropertyName=”Name” HeaderStyle=”{StaticResource cellHeaderStyle}” />
<telerikDataGrid:DataGridTextColumn HeaderText=”Sales
Amount” PropertyName=”SalesAmount” CellContentFormat=”{}{0:C}”
HeaderStyle=”{StaticResource cellHeaderStyle}” />
</telerikDataGrid:RadDataGrid.Columns>
</telerikDataGrid:RadDataGrid>
You can find more information about the DataGrid control and its features in our official
documentation.
Xamarin BusyIndicator
Telerik ERP uses the beautiful Telerik UI for Xamarin BusyIndicator control to illustrate
that a long-running process is executing in the background. The BusyIndicator control
supports ten different animations (by the time of writing) right out of the box and can be
customized with visuals of your choice. Here is what we selected for the ERP application:
The Telerik Entry control provides the core functionality expected by any Entry in addition
to many more, including the option to customize the border around the input using the
BorderStyle property.
The Xamarin Popup control is used to visualize dialogs with additional content on top of
other controls. The component provides a flexible API to easily control its behavior and
makes possible the implementation of complex logic for a wide range of scenarios. You
can make it modal (that makes the UI behind it inactive) if you need to or change the
animation that is used when the control is visualized and gets closed.
Many other Telerik UI for Xamarin controls like RadNumericInput, RadBorder, RadButton
and RadPath are used by the pages.
Using Xamarin.Forms in combination with MvvmCross and the Telerik UI for Xamarin
controls, we were able to build a beautiful, fast and feature-rich application that enables
you to manage and grow your business with ease from every corner of the planet.
Download Telerik ERP demo application for iOS, Android and Windows devices.
If you already have a SQLServer and/or SQL Database, select this option. Alternatively, you
can always choose to create a new server and database.
Note: When creating the SQLServer, you will need to create admin credentials. You don’t
have to remember them for the App Service since they are stored in an environment
variable, but it might be a good idea to store them in your preferred password manager or
another secure location.
After you make your selection for a C# backend, click the “Download” button. Azure will
create an ASP.NET project for you, already set up with the right connection string to the
database you’ve selected at Step 1.
This step gives you two options: “create a new app” or “connect an existing app”. If you
choose to connect an existing app, you’ll be given a few code snippets and instructions.
For Telerik CRM, we chose to create a new app. As with the backend, when you click the
Download button, you’ll get a new Xamarin.Forms application that already has the required
code to connect to the App Service.
If you have already checked the source code of the Telerik CRM app, you will notice that
we combined all the projects into a single folder and created a combined solution. This
is not necessary. We did it to keep all the projects for this tutorial in one place for simpler
source control and to make the tutorial easier to follow.
Here is the result: the arrows point to the two different downloads combined in a single
solution.
Moving on to the code, let’s start with the ASP.NET service project. We want to complete
the backend before we add the UI.
Entities
Each of the classes have properties related to the entity. These properties will be used
to create the table fields. For example, an Employee would have OfficeLocation, while an
Order may need properties like ProductId and Quantity.
It is important that you finalize the properties at this stage because, once you initialize the
database, Entity Framework will create the database fields using those properties. Any
changes to the model will require a special operation called Entity Framework Code First
Migration, which can quickly get complicated.
DBContext
We now need to update the database context class used by Entity Framework to interact
with the database. Go ahead and replace the TodoItem DBSet with the four new ones:
modelBuilder.Conventions.Add(new AttributeToColumnAnnotationConve
ntion<TableColumnAttribute, string>(
“ServiceTableColumn”,
Database Initializer
In the AppStart folder, you will find that the autogenerated project nested a class that
seeds the database with the tables and initial records. We moved that class into its own
class and replaced the TodoItem seed data with records for each of the entities:
base.Seed(context);
Table Controllers
Finally, you want a controller for each of the entities to interact with the database. This
is the endpoint that the client app will use to interact with the databases and each
implements the Azure Mobile Server TableController<T> interface.
The content of each WebAPI controller follows the CRUD pattern you would expect. Here
is a screenshot of the EmployeeController’s methods (all the controllers reuse this pattern).
{
base.Initialize(controllerContext);
var context = new ArtGalleryCRMContext();
// GET tables/Employee
public IQueryable<Employee> GetAllEmployees()
return Query();
// GET tables/Employee/48D68C86-6EA6-4C25-AA33-223FC9A27959
public SingleResult<Employee> GetEmployee(string id)
return Lookup(id);
// PATCH tables/Employee/48D68C86-6EA6-4C25-AA33-223FC9A27959
public Task<Employee> PatchEmployee(string id, Delta<Employee> patch)
// POST tables/Employee
// DELETE tables/Employee/48D68C86-6EA6-4C25-AA33-223FC9A27959
public Task DeleteEmployee(string id)
{
throw new HttpException(500, “Demo - Read-only mode”);
// Uncomment before deploying to your Azure account.
// return DeleteAsync(id);
}
}
Publish To Azure
Visual Studio has great built-in Azure tools. If you right click on the ASP.NET project, you
will see a “Publish” option in the context menu:
Selecting it will provide you with a list of options to choose from. Select “Azure App
Service” and then “Select Existing” since we already set up the App Service.
Note: You will need to be signed in the same account that the Azure Subscription uses. If
you have multiple accounts, there is a dropdown at the top right to change accounts.
Once you’ve gone through the publishing steps, Visual Studio will start the publishing
process and upload the application. When done, a browser tab will open and present the
landing page of the running app.
The next time you use the Publish menu option, you’ll see the existing publish profile
details along with several other tools:
With the backend published and running, we can now move on to the frontend
implementation with Telerik UI for Xamarin controls.
The Telerik NuGet packages are available from the Telerik NuGet Server. You will need
to add this server to your Package Sources list. We have easy-to-follow instructions in
the Telerik NuGet Server documentation. Here’s a screenshot of how to add a source in
Visual Studio:
Once you’ve added the server to your Package Sources list, you can right click on the
solution and select “Manage NuGet Packages for Solution”. This will let you install a
package to multiple projects at the same time. When the NuGet Package Manager
appears, do the following:
You are now ready to start using the Telerik UI for Xamarin controls!
We recommend installing the NuGet package. It is faster to get started and, thanks to
the NuGet Package Manager dependency resolution, it will install the other required
dependencies for Telerik UI for Xamarin (e.g. Xamarin SkiaSharp packages and required
Xamarin Android Support Library packages).
If you choose the assembly reference route, you can find the assemblies for each project
in conveniently named folders in the Telerik UI for Xamarin installation folder. The default
path is
Within the Binaries folder, you will find subfolders corresponding to the project types. Each
of these folders contains the assemblies that should be added to the project.
If you only use one or two controls and don’t plan on using the Linker to remove unused
assemblies from the compiled app package, you can pick and choose individual assemblies
from those folders.
To know what the required assemblies are for each control, review the control’s “Required
Telerik Assemblies” article. You will find that every control has this article underneath the
“Getting Started” folder. As an example, here’s the location of the RadListView’s Required
Telerik Assemblies article:
We recommend adding them all from the start so that you can be up and running quickly.
After the project is done, you can go back and remove unused assemblies if necessary.
As mentioned above in Option 1, there are a couple of dependencies. If you choose the
assembly reference route, you’ll need to explicitly add these NuGet packages. Let’s go over
them one by one.
SkiaSharp Dependency
The native Android controls need a few Xamarin Android Support Libraries to operate
properly. Go to the Required Android Support Libraries article to see the full list. You only
need to install these to the Android project.
Open the class library project’s Constants.cs file and notice that the service URL will
already be set in the ApplicationURL property, like this:
public static class Constants
public static string ApplicationURL = “https://YOUR_URL.azurewebsites.
net”;
Data Models
Notice the TodoItem.cs data model that comes with the project. Before deleting it, you can
use it as a template for the four data model classes you need to interact with the backend:
These classes should have the same properties as the entity models in the server
application, but with some extra properties defined in a shared base class:
public class BaseDataObject : ObservableObject
{
public BaseDataObject()
{
Id = Guid.NewGuid().ToString();
}
[JsonProperty(PropertyName = “id”)]
public string Id { get; set; }
[JsonProperty(PropertyName = “createdAt”)]
[Ignore]
[UpdatedAt]
[Ignore]
[Version]
[Ignore]
public string AzureVersion { get; set; }
Service Classes
With the models defined, we now turn our attention to the TodoItemManager class. This is
the workhorse that syncs data between Azure and the app, but also manages the offline
database syncing if you enabled it.
For the Art Gallery CRM app, we need four “manager classes”. Instead of having multiple
methods or complicated overloads, we created an interface that defines those CRUD
operations:
public interface IDataStore<T>
Task<bool> PurgeAsync();
Task<T> GetItemAsync(string id);
Task<string> GetIdAsync(string name);
Task<bool> PullLatestAsync();
Task<bool> SyncAsync();
One option we can use is to place the MobileServiceClient reference in the App class to
make the same instance available to all the services.
With the interface defined and the MobileServiceClient reference available globally, we can
start implementing a DataStore service class for each entity model.
As an example, here is the Customer entity’s DataStore. In the constructor, we just set the
reference for the CustomersTable and the interface-required methods can interact with
that reference.
You can use a Tabbed style or a MasterDetail style page layout. We decided to go with
MasterDetail as it makes more sense to have top-level pages in the MasterPage (the side
pane) and the drill-down pages (details and edit) in the DetailPage.
Here, we use only the Employees table to demonstrate and implement concepts, views
and view models.
From this point on, assume the Master-Detail infrastructure is in place and we’re adding
pages to that setup. You can also reference the demo’s source code on GitHub to follow
along.
We used a naming convention where the list, detail and edit view models use the same
name as the pages. This makes it easy to follow the responsibility of the view models:
• EmployeesPage & EmployeesViewModel
• EmployeeDetailsPage & EmployeeDetailViewModel
• EmployeeEditPage & EmployeeEditViewModel
EmployeesViewModel
When we start with the view model, we can ensure we have the properties we need to
populate the view. This is rather straightforward—a single ObservableCollection to hold
the items and a Task to load the data into the collection:
public EmployeesViewModel()
this.Title = “Employees”;
ItemTapped);
vableCollection<Employee>();
set
this.ApplyFilter(value);
await this.LoadEmployeesAsync();
if (this._allEmployees == null)
return;
? this._allEmployees
Contains(textToSearch.ToLower()));
this.Employees.Clear();
this.Employees.Add(employee);
if (this.IsBusy)
return;
try
if (this.Employees.Count == 0)
this.IsBusy = true;
Get<IDataStore<Employee>>().GetItemsAsync();
this.Employees.Add(employee);
MessagingCenter.Send(new MessagingCenterAlert
Title = “Error”,
Cancel = “OK”
}, “Alert”);
finally
this.IsBusyMessage = “”;
this.IsBusy = false;
ployeeDetailViewModel(employee)));
switch (obj.ToString())
case “sync”:
this.Employees.Clear();
await this.LoadEmployeesAsync();
break;
case “add”:
break;
await DependencyService.Get<IDataStore<Employee>>().
GetItemsAsync(forceRefresh);
This will call the method on the DataStore service class for the Employee entity. All the
heavy lifting is neatly packed away either in the SDK itself or in the service class and keeps
the view model very readable.
EmployeesPage
With the view model ready, we can move on to the UI and our first Telerik UI for Xamarin
controls. We want not only to be able to list the employees, but also quickly filter them. We
can do this with a combination of UI for Xamarin controls:
• RadAutoComplete to filter the Employees
• RadListView to display the filtered results
• RadBusyIindicator to show a busy overlay while data operations are occurring
Style=”{StaticResource BaseListViewStyle}”
Grid.Row=”1”>
Now we need to create an ItemTemplate for the RadListView to display the Employee
properties we need, the name and the photo. The BindingContext of the ItemTemplate
is the individual employee object in the FilteredItems collection. Therefore, we can
bind a Label’s Text property to “Name” and bind an Image control’s Source property to
“PhotoUri”.
Now we can move on to the filtering using RadEntry and its Text property:
WatermarkText=”Search”
Margin=”20,10,10,0”>
The Text is bound to the SearchText property from the EmployeesViewModel, where the
filtering logic is implemented using the ApplyFilter method. This method executes custom
filtering and then updates the Employees collection so that only filtered items are present
and visualized in the RadListView:
set
{
SetProperty(ref _searchText, value);
this.ApplyFilter(value);
RadBusyIndicator
Now let’s look at the RadBusyIndicator. This control offers a nice set of custom animations
out of the box. You can also create your own custom animations if you need to. Since this
app is designed for a professional audience, we used Animation6. See the Animations
documentation article for more details.
If you look at the view model code, you will see that we toggle the BaseViewModel’s IsBusy
property when loading data:
Finally, we want the user to be able to select an item in the RadListView and navigate to
the EmployeeDetailsPage. We use the RadListView ItemTapped event and navigate to an
instance of the EmplyeeDetailPage.
private async void RadListView_OnItemTapped(object sender,
ItemTapEventArgs e)
if (!(e.Item is Employee employee))
return;
await Navigation.PushAsync(new EmployeeDetailPage
(new EmployeeDetailViewModel(employee)));
Take notice that we are passing the selected employee object to the constructor of the
EmployeeDetailsViewModel.
public class EmployeeDetailViewModel : PageViewModelBase
public EmployeeDetailViewModel(Employee item = null)
{
this.SelectedEmployee = item;
...
}
public Employee SelectedEmployee
{
get => this._selectedEmployee;
}
...
We also want to generate data about this employee to populate charts, gauges and other
nice UI features. To support this, we’ve added collections to hold chart data points and
other data, along with a Task to load and calculate the data.
Let’s take a closer look at LoadDataAsync. This is where we do the calculations that result
in the values used for the view’s chart and gauges.
There are two collections, one for compensation and one for sales revenue. We can
calculate the compensation using the SelectedEmployee’s values:
The SalesHistory collection is another story, we need to actually pull items from the Orders
table and find matches for this Employee’s Id:
CompanySalesCount = orders.Count;
EmployeeSalesCount = employeeSales.Count;
foreach (var order in employeeSales)
SalesHistory.Add(new ChartDataPoint
{
Value = order.TotalPrice,
Date = order.OrderDate
});
<Grid x:Name=”RootGrid”
RowDefinitions=”{StaticResource AutoStarRowDefinitions}”>
<StackLayout Padding=”10”>
</StackLayout>
</StackLayout>
<Grid x:Name=”MetricsGrid”
BackgroundColor=”{StaticResource LightBackgroundColor}”
Grid.Row=”1”>
<Grid.RowDefinitions … >
<Grid.ColumnDefinitions … >
<Grid Grid.Row=”0”…>
protected override async void OnAppearing()
base.OnAppearing();
await vm.LoadDataAsync();
SetupVacationGauge();
SetupSalesGauge();
SetupRevenueGauge();
SetupChartLegend();
You will notice that we configure some UI elements here as well. The chart series will
automatically update when the bound ObservableCollections are populated, but the
gauges use a little more fine-grained control.
Charts
Starting with the SalesHistory and Compensation charts, the series ItemsSources can be
bound directly to the respective view model collections:
• LineChart
• PieChart
• Gauges
private void SetupVacationGauge()
VacationGauge.StartAngle = 90;
VacationGauge.SweepAngle = 360;
VacationGauge.AxisRadiusFactor = 1;
VacationLinearAxis.Maximum = vm.SelectedEmployee.VacationBalance;
VacationLinearAxis.StrokeThickness = 0;
VacationLinearAxis.ShowLabels = false;
VacationRangesDefinition.StartThickness = 10;
VacationRangesDefinition.EndThickness = 10;
VacationRange.To = vm.SelectedEmployee.VacationBalance;
VacationRange.Color = (Color)Application.Current.Resources
[“GrayBackgroundColor”];
VacationIndicator.Value = vm.SelectedEmployee.VacationUsed;
VacationIndicator.Fill = (Color)Application.Current.
Resources[“AccentColor”];
VacationIndicator.StartThickness = 10;
VacationIndicator.EndThickness = 10;
VacationIndicator.StartCap = GaugeBarIndicatorCap.ConcaveOval;
VacationIndicator.EndCap = GaugeBarIndicatorCap.Oval;
We also enable the user to delete the item through the Details page. This is done using a
Delete toolbar button’s Clicked event.
Still, deleting an employee will also delete any records in the Orders table that contain
orders with this employee’s ID. We need to first check if they are associated with any open
orders and warn the user that deleting the employee will also delete the orders.
private async void Delete_Clicked(object sender, EventArgs e)
if (matchingOrders.Count > 0)
{
if (!deleteAll)
return;
foreach (var order in matchingOrders)
{
await DependencyService.Get<IDataStore<Order>>().
DeleteItemAsync(order);
}
}
await DependencyService.Get<IDataStore<Employee>>().
DeleteItemAsync(vm.SelectedEmployee);
await Navigation.PopAsync();
Editing an Employee
A common approach to editing is to build the edit form into the details page. We wanted
to facilitate adding an employee with the same edit form.
The Details page toolbar also has an Edit button. We use this button’s Clicked event
handler to navigate to the EmployeeEditPage, passing in the SelectedEmployee to the
page constructor:
private async void Edit_Clicked(object sender, EventArgs e)
if (vm.SelectedEmployee == null)
return;
await Navigation.PushAsync(new EmployeeEditPage(vm.SelectedEmployee));
Note: This time, we pass the employee parameter to the page constructor, not the view
model constructor.
EmployeeEditViewModel
This view model’s responsibility is simple:
• Determine if this is an existing employee or editing an existing employee
• Provide a mechanism to either insert a new or update an existing database record
if (IsNewEmployee)
{
await DependencyService.Get<IDataStore<Employee>>().
AddItemAsync(SelectedEmployee);
}
else
{
await DependencyService.Get<IDataStore<Employee>>().
UpdateItemAsync(SelectedEmployee);
}
Note: Some code is omitted for clarity. Please see the demo source for the full method.
Using the page constructor, we can determine whether this is a new or existing employee.
EmployeeEditPage
The Edit page’s XAML is very simple:
• An Image + Button to select a photo (instead of manually typing in a URL string into
the DataForm)
• A RadDataForm to edit the fields
<Grid x:Name=”RootGrid”
RowDefinitions=”{StaticResource AutoStarRowDefinitions}”>
</StackLayout>
<input:RedDataForm x:Name=”DataForm”
Source=”{Binding SelectedEmployee}”
FormValidationCompleted=”DataForm_OnFormValidationCompleted”
Grid.ROw=”1”
Margin=”10,0,10,0”>
</input:RedDataForm>
</Grid>
As you can see, the DataForm’s Source is bound to the view model’s SelectedEmployee.
The button’s click handler just assigns a random photo to the SelectedEmployee’s
PhotoUri.
The extra interesting part of this page lies in the Employee model’s property attributes
that tell the RadDataForm how to create its Editors and the Edit page how to determine if
we are editing or adding an employee.
Data Annotations
RadDataForm Data Annotations are model property attributes used to set a wide
range of DataForm features. You can find a list of the attributes in the RadDataForm
documentation under the “Data Annotations” subfolder.
[Ignore]
public string PhotoUri
get => photoUri;
}
The page needs to determine whether we are editing an employee or creating a new one.
This is simply achieved by using two constructors.
When the overloaded constructor is used, we are passing an existing employee object to
edit. When the normal constructor is used, we are creating a new employee:
// If we’re adding a new employee
public EmployeeEditPage()
InitializeComponent();
ViewModel.IsNewEmployee = true;
ViewModel.SelectedEmployee = new Employee();
ConfigureEditors();
public EmployeeEditPage(Employee employee)
InitializeComponent();
ViewModel.SelectedEmployee = employee;
ConfigureEditors();
{
DataForm.RegisterEditor(nameof(Employee.Name), EditorType.TextEditor);
DataForm.RegisterEditor(nameof(Employee.OfficeLocation),
EditorType.TextEditor);
DataForm.RegisterEditor(nameof(Employee.HireDate), EditorType.DateEditor);
DataForm.RegisterEditor(nameof(Employee.Salary), EditorType.DecimalEditor);
DataForm.RegisterEditor(nameof(Employee.VacationBalance),
EditorType.IntegerEditor);
DataForm.RegisterEditor(nameof(Employee.VacationUsed),
EditorType.IntegerEditor);
DataForm.RegisterEditor(nameof(Employee.Notes), EditorType.TextEditor);
The final remaining item to cover is how the DataForm saves the data in its editors.
The DataForm has validation considerations, so the values in the editors are not
immediately committed to the bound Source. Instead, you call DataForm.CommitAll and
validation is performed against the values (according to the Data Annotations you have
set).
private void DataForm_OnFormValidationCompleted(object sender,
FormValidationCompletedEventArgs e)
isReadyForSave = e.IsValid;
private async void Save_Clicked(object sender, EventArgs e)
DataForm.CommitAll();
if(!isReadyForSave)
await ViewModel.UpdateDatabaseAsync();
await Navigation.PopAsync();
Note: The code above is abbreviated for clarity. Please see the demo source for the full
implementation.
Wrapping Up
This concludes our deep dive into the ListPage -> DetailsPage ->EditPage approach. The
Art Gallery CRM app uses this pattern for all four of the entity models.
Now that you have a handle on the pattern, take a look at the following special scenarios
and try explaining how the Art Gallery CRM app uses:
• RadDataGrid to easily group, sort and filter open Orders on the OrdersPage
• RadSlideView for a great “out-of-the-box experience” on the WelcomePage
• RadListView with GridLayout to show beautiful product images on the
ProductsPage
• RadCalendar to show collated shipping dates on the CalendarPage
• RadNumericInput and RadMaskedInput on the advanced OrderEditPage
We wanted to ensure that the app had a dynamic and intelligent service communicating
with the user, which is why we chose Azure. You can train a LUIS model to identify certain
contexts and understand what the user is asking for. For example, it can discern whether
the user is inquiring about product details or shipping information.
To get started, follow the Quickstart: create a new app in the LUIS portal tutorial. It’s quick
and easy to spin up a project and get started training the model with your custom intents.
Intents
On the CRM app’s chat page, the user is interacting with a support bot. We mainly need
to determine if the user is asking for information about a product, employee, order or
customer. To achieve this, we used the following Intents:
To train the model to detect an intent, you give it a list of things the user might say. This is
called an Utterance. Let’s look at the utterances for the Product intent.
Training
You can start training the model by clicking the “Train” button in the menu bar at the top
of the portal:
Testing
After LUIS is trained with the current set of Intents and Utterances, you can test how well it
performs. Select the Test button:
In the above test, we tried a search for “where is the artwork?” and the result was a 73%
match for a Product intent. If you want to improve the score, add more utterances to the
intent and retrain it.
Publishing
When you are satisfied with the scores you get for the Intents, you can publish the model.
Use the Publish button:
A dialog will open, prompting you to pick a release slot to push the changes to. You can
stage the new changes or push them to production:
In the Manage tab, the Azure Resources pane will be preselected. In the pane, you will see
the API endpoints and other important information.
Note: We will come back to these values later. You can temporarily store them in a secure
location. Do not share your primary key or example query (it has the primary key in the
query parameters).
This will open a blade and initialize the bot. When it’s ready, you can test the
communication. The default bot template uses an “echo” dialog (covered later).
Test this by sending any message. You should see your message prefixed with a count
number. Next, try sending the word “reset”. You will get a dialog prompt to reset the
message count.
Step 2
Look for a ZIP file named similarly “YourBot-src”. You can now unzip and open
the Microsoft.Bot.Sample.SimpleEchoBot.sln in Visual Studio. Do a Rebuild to restore the
NuGet packages.
[ResponseType(typeof(void))]
{
await Conversation.SendAsync(activity, () => newDialogs.EchoDialog());
}
...
In the case of our echo bot, this is ActivityType.Message, in which case the logic
determines we respond with an instance of EchoDialog.
There are a few methods in the class, but we are focusing on the
MessageReceivedAsync method:
if (message.Text == “reset”)
{
// If the user sent “reset”, then reply with a prompt dialog
PromptDialog.Confirm(
context,
AfterResetAsync,
“Are you sure you want to reset the count?”,
“Didn’t get that!”,
promptStyle: PromptStyle.Auto);
}
else
{
// If the user sent anything else, reply back with
the same message prefixed with the message count number
}
This is the logic that the bot used during the initial test in Web Chat. Here you can see how
all the messages you sent were echoed and why it was prefixed with a message count.
Although you could manually construct and send HTTP requests, Microsoft has provided
a .NET client SDK that makes it very simple and painless to connect to and interact with
the Bot Service via the Microsoft.Bot.Connector.DirectLine NuGet package.
With this, we could technically just use it directly in the view model (or code behind) of the
Xamarin.Forms page. We created a service class that is not tightly bound to that specific
page.
using System;
using System.Linq;
using System.Threading.Tasks;
using ArtGalleryCRM.Forms.Common;
using Microsoft.Bot.Connector.DirectLine;
using Xamarin.Forms;
namespace ArtGalleryCRM.Forms.Services
{
public class ArtGallerySupportBotService
{
private readonly string _user;
private Action<Activity> _onReceiveMessage;
private readonly DirectLineClient _client;
private Conversation _conversation;
private string _watermark;
public ArtGallerySupportBotService(string userDisplayName)
{
this._client = newDirectLineClient(ServiceConstants.
DirectLineSecret);
this._user = userDisplayName;
}
internal void AttachOnReceiveMessage(Action<Activity>
onMessageReceived)
{
this._onReceiveMessage = onMessageReceived;
}
}
public async void SendMessage(string text)
{
{
From = new ChannelAccount(this._user),
Text = text,
Type = ActivityTypes.Message
};
awaitthis._client.Conversations.PostActivityAsync
(this._conversation.ConversationId, userMessage);
await this.ReadBotMessagesAsync();
}
private async Task ReadBotMessagesAsync()
{
var activitySet = awaitthis._client.Conversations.
GetActivitiesAsync(this._conversation.ConversationId, _watermark);
if (activitySet != null)
{
this._watermark = activitySet.Watermark;
Device.BeginInvokeOnMainThread(() =>
{
foreach (Activity activity in activities)
{
this._onReceiveMessage?.Invoke(activity);
}
});
}
}
}
}
Here is a screenshot to guide you. The ID is the “Microsoft App ID” field:
= new ObservableCollection<TextMessage>();
The service instance can be defined as a private field that is created when the view model
is initialized:
public class SupportViewModel : PageViewModelBase
private ArtGallerySupportBotService botService;
{
this.botService = new ArtGallerySupportBotService(“Lance”);
this.botService.AttachOnReceiveMessage(this.OnMessageReceived);
}
...
When a message from the bot comes in, the service will invoke the view model’s
OnMessageReceived method and pass an activity parameter. With this information, you
can add a message to the ConversationItems collection:
private void OnMessageReceived(Activity activity)
{
ConversationItems.Add(new TextMessage
{
Author = this.SupportBot, // the author of the message
});
}
private void ConversationItems_CollectionChanged(object sender,
NotifyCollectionChangedEventArgs e)
if (e.Action == NotifyCollectionChangedAction.Add)
{
// Get the message that was just added to the collection
if (chatMessage.Author == this.Me)
{
Device.StartTimer(TimeSpan.FromMilliseconds(500), () =>
{
Device.BeginInvokeOnMainThread(() =>
{
this.botService.SendMessage(chatMessage.Text);
});
return false;
});
}
}
<conversationalUi:RadChat ItemsSource=”{Binding
ConversationItems}” Author=”{Binding Me}”/>
Typing Indicators
You can show participants who are currently typing using ObservableCollection<Author>
in the view model. Anyone in this collection will be shown by the RadChat control as
current typing.
<conversationalUi:RadChat ItemsSource=”{Binding ConversationItems}”Author=”{B
inding Me}” >
<conversationalUi:RadChat.TypingIndicator>
<conversationalUi:TypingIndicator ItemsSource=”{Binding
TypingAuthors}” />
</conversationalUi:RadChat.TypingIndicator>
</conversationalUi:RadChat>
Visit the Typing Indicators tutorial page for more information. You can also check out this
Knowledge Base article, which explains how to have a real-time chat room experience with
the indicators.
Message Styling
RadChat gives you easy, out-of-the-box functionality and comes with ready styles to
use as-is. You can also customize the items to meet your branding guidelines or create
great-looking effects like grouped consecutive messages. Visit the ItemTemplateSelector
tutorial for more information.
To do this, go back to the bot project and open the EchoDialog class. You could rename
the class to better match its function. For example, the CRM demo dialog is named
“SupportDialog”.
In the MessageReceived Task, you can see what the Xamarin.Forms user typed into the
RadChat control. The next step is to send the input to LUIS for analysis and determine
how to respond to the user.
Microsoft has made it easy to use Azure Services by providing .NET SDK for LUIS. Install
the Microsoft.Azure.CognitiveServices.Language.LUIS.Runtime NuGet package to the
bot project:
Once installed, you can go back to the dialog class and replace the echo logic with a
LUISRuntimeClient that sends the user’s message to LUIS. The detected Intents will be
returned to your bot and you can then choose how to respond.
Here’s an example that takes only the highest scoring Intent and thanks the user:
query: userMessage,
timezoneOffset: null,
verbose: true,
staging: false,
spellCheck: false,
bingSpellCheckSubscriptionKey: null,
log: false,
cancellationToken: CancellationToken.None);
// You will get a full list of intents. For the purposes of this
demo, we’ll just use the highest scoring intent.
if(topScoringIntent == “Product”)
{
}
}
The code above uses a very simple check for a “Product” Intent and replies with a
statement. If there is no match, no reply will be triggered. The takeaway here is that, even
though you control what gets sent back to the user, LUIS helps you make intelligent
decisions and customize the experience to the user’s needs.
Download Telerik ERP demo application for iOS, Android and Windows devices.
• SQLite: SQLite is the most used database engine in the world. It is built into all mobile
phones and most computers and comes bundled inside countless other applications
• Telerik UI For Xamarin: offers high-quality Xamarin Forms UI components and Visual
Studio item templates to enable every developer, regardless of their experience, to
build professional-looking modern mobile applications for iOS, Android and UWP
Application Structure
The different modules of this sample Xamarin.Forms application are separated in folders
with names that are descriptive and quite standard for projects of such size. Some of the
more important folders we have introduced are:
• PageModels: the classes used as BindingContext for each separate Page (or view) are
located here
• Controls: contains some small additional custom controls used within the pages
In order to connect the views and their models, all you need to do is follow the convention
in the naming —each Page should have a corresponding PageModel. For example,
the BindingContext of the MainPage will automatically be set to be an instance of
the MainPageModel class.
The navigation within the application is pretty simple as well. You can use
the CoreMethods property of the FreshBasePageModel class, which is the base class for all
the PageModels within the application, to navigate between models.
For more information about the different features the framework supports, you can refer
to its documentation here—MVVM Fresh.
Data Access
As mentioned earlier, we are using EntityFrameworkCore and its built-in ability to easily
communicate with an SQLite database to power the data access capability of this
Xamarin.Forms sample application. For the purpose, a couple of NuGet packages are
installed:
• Microsoft.EntitiFrameworkCore
• Microsoft.EntityFrameworkCore.SQLite.
The classes needed for EF are all located in the DataAccess folder. Note that there is
a DTO folder where the Data Transfer Objects (the objects used to send data between
our application and the underlying database) are located. They are almost identical to the
objects within the Models folder and take care of the information we would like to keep in
our database.
Additionally, we have added a few sample items in the database so that users can see
{
IDbFileProvider dbFileProvider = DependencyService.
Get<IDbFileProvider>();
if (dbFileProvider == null)
return null;
// Database
string dblocation = dbFileProvider.GetLocalFilePath(“tododb.db”);
DataAccess.TodoItemContext ctx = DataAccess.TodoItemContext.
Create(dblocation);
return ctx;
}
Services
Back to the earlier technology overview, the Application Services consist of a
single ToDoService that takes care of adding, deleting, and updating the “to-do” items
within the application. It can also generate a list of the existing items.
public TodoItem GetTodoItem(int id)
{
return context.TodoItems
.Include(c => c.Category)
.Include(c => c.Alert)
.ThenInclude(c => c.Alert)
.Include(c => c.Recurrence)
.ThenInclude(c => c.Recurrence)
.Include(c => c.Priority)
.SingleOrDefault()?.ToModel(true);
}
public async Task<TodoItem> AddTodoItemAsync(TodoItem newItem)
{
var dto = newItem.ToDTO();
try
{
await this.context.SaveChangesAsync();
}
catch (Exception e)
{
System.Diagnostics.Debug.WriteLine(e);
throw e;
}
TodoItem storedItem = GetTodoItem(entity.Entity.ID);
MessagingCenter.Send<ITodoService, TodoItem>(this, ActionAdd, storedItem);
return storedItem;
}
The Welcome screen greets the user. The page utilizes the RadSlideView control, which
directly sets an ItemTemplate. It is a great fit for any “Splash Screen” scenario or views
where a sliding content is required. The code for bootstrapping the SlideView element is
small and tight:
<telerikPrimitives:RadSlideView x:Name=”slideView”
Grid.Row=”1”
Margin=”0,38,0,34”
ItemsSource=”{Binding Slides}”
ShowButtons=”False”
IndicatorColor=”#F2F2F3”
SelectedIndicatorColor=”#4DA3E0”>
<telerikPrimitives:RadSlideView.ItemTemplate>
<DataTemplate>
<StackLayout Orientation=”Vertical”Mar gin=”0,0,0,50”
Spacing=”0”>
<Image Source=”{Binding Image}”Aspect=”AspectFit”>
<Image.HorizontalOptions>
<OnPlatformx:TypeArguments=”LayoutOptions”
Default=”FillAndExpand”>
<On Platform=”UWP”Value=”CenterAndExpand”/>
</OnPlatform>
</Image.HorizontalOptions>
<Image.WidthRequest>
<OnPlatformx:TypeArguments=”x:Double”
Default=”-1”>
<On Platform=”UWP”Value=”300” />
</OnPlatform>
</Image>
<Label Text=”{Binding Title}”HorizontalTextAlignment=”
Center” TextColor=”Black” FontSize=”Medium”Margin=”0,15,0,0” />
<Label Text=”{Binding Description}”HorizontalText|Align
ment=”Center” TextColor=”#5C5C68” Margin=”0,14,0,0”/>
</StackLayout>
</DataTemplate>
</telerikPrimitives:RadSlideView.ItemTemplate>
</telerikPrimitives:RadSlideView>
From the Welcome page, users are guided to the Main page, where they can see the list of
their do-to items. By default, a list in grid format will show the categories in which the to-do
items are grouped. The view is achieved by solely including a RadListView control. As one
of the more complex and feature-rich controls in the suite, it can be highly customized to
achieve a distinguished look. Here are some of the features we used in this particular view:
• Grid Layout
<telerikDataControls:RadListView.LayoutDefinition>
<telerikListView:ListViewGridLayout HorizontalItemSpacing=”0”
ItemLength=”100”
SpanCount=”2”
VerticalItemSpacing=”0” />
</telerikDataControls:RadListView.LayoutDefinition>
<telerikDataControls:RadListView Grid.Row=”1”
ItemsSource=”{Binding Categories}”
ItemStyle=”{StaticResource UnifiedItemStyle}”
SelectedItemStyle=”{StaticResource UnifiedItemStyle}”
PressedItemStyle=”{StaticResource UnifiedItemStyle}”
SelectedItem=”{Binding SelectedCategory, Mode=TwoWay}”>
• ItemTemplateSelector
<telerikDataControls:RadListView.ItemTemplateSelector>
<templateSelectors:RadListViewItemTemplateSelector>
<templateSelectors:RadListViewItemTemplateSelector.
CategoryTemplate>
<DataTemplate>
<telerikListView:ListViewTemplateCell>
<telerikListView:ListViewTemplateCell.View>
</telerikListView:ListViewTemplateCell.View>
</telerikListView:ListViewTemplateCell>
</templateSelectors:RadListViewItemTemplateSelector.
CategoryTemplate>
<templateSelectors:RadListViewItemTemplateSelector.
NewCategoryTemplate>
<DataTemplate>
<telerikListView:ListViewTemplateCell>
<telerikListView:ListViewTemplateCell.View>
</telerikListView:ListViewTemplateCell.View>
</telerikListView:ListViewTemplateCell>
</DataTemplate>
</templateSelectors:RadListViewItemTemplateSelector.
NewCategoryTemplate>
</templateSelectors:RadListViewItemTemplateSelector>
</telerikDataControls:RadListView.ItemTemplateSelector>
Here is how the different categories will be visualized when using such setup:
Furthermore, you can navigate to the different to-do items and edit their specific
properties from this screen or by simply clicking on the item.
The application contains all the necessary actions to allows users to add and edit different
items and categories. However, we decided to provide an additional option for clients to
customize the list view. Instead of using the default Grid-like view, the app provides an
option to show the items in a list with categories as groups. In order to show this view,
simply click on the hamburger menu on the right side where some additional options will
appear:
<telerikDataControls:RadTreeView x:Name=”treeView” Grid.Row=”1”
ItemsSource=”{Binding Categories}”>
<telerikDataControls:RadTreeView.Descriptors>
<telerikDataControls:TreeViewDescriptor TargetType=”{x:Type
models:Category}” DisplayMemberPath=”Name” ItemsSourcePath=”Items”>
<telerikDataControls:TreeViewDescriptor.ItemTemplate>
<templateSelectors:RadTreeViewItemTemplateSelector>
<templateSelectors:RadTreeViewItemTemplateSelector.
CategoryTemplate>
<DataTemplate>
</DataTemplate>
</templateSelectors:RadTreeViewItemTemplateSelector.
<templateSelectors:RadTreeViewItemTemplateSelector.
NewCategoryTemplate>
<DataTemplate>
</DataTemplate>
</templateSelectors:RadTreeViewItemTemplateSelector.
NewCategoryTemplate>
</templateSelectors:RadTreeViewItemTemplateSelector>
</telerikDataControls:TreeViewDescriptor.ItemTemplate>
</telerikDataControls:TreeViewDescriptor>
<telerikDataControls:TreeViewDescript
or TargetType=”{x:Type models:TodoItem}” DisplayMemberPath=”Name”>
<telerikDataControls:TreeViewDescriptor.ItemTemplate>
<DataTemplate>
<!--ToDo Item template-->
</DataTemplate>
</telerikDataControls:TreeViewDescriptor.ItemTemplate>
</telerikDataControls:TreeViewDescriptor>
</telerikDataControls:RadTreeView.Descriptors>
</telerikDataControls:RadTreeView>
If you explore the demo application, you will notice that we used the RadListView
component on several occasions throughout the application as the control is
versatile and comes in hand in many common mobile scenarios. Other elements
from the Telerik UI for Xamarin suite used in the application are the RadButton,
RadBorder and RadPopup controls.
Download Telerik ERP demo application for iOS, Android and Windows devices.
If you have any questions or feedback regarding the demo applications or the Telerik UI
for Xamarin controls suite, feel free to reach out to our team. We’ll be glad to hear from
you!