There are a few steps needed that are slightly different to your normal application development cycle, this post will hopefully help in making it easier. On the simple side, the code I made and used to test it was more experimentation than anything else.
Sure, it helped me make my own custom App for my fitness that was an exact minimal design that I wanted. If people want it I can move it into my preferred open source market anyway.
Step 1: Where To Begin
First off, grab yourself a copy of VS2019 Community if you haven’t got it, or a license for the others yet. When you chose the packages to install you can do your standard options you choose for yourself, just also select Mobile development with .NET. That’s what we will be using for this.
You can create the Xamarin project for yourself, in the location of your desire.
For starters, there are different ways to do this, however we are only focused on the minimal starting steps for ourselves so far. You will need to make an Android emulator, and take note, it’s recommended you add Hyper-V.
With all of that it should be possible for you to run the App you make in the android emulator, while I wanted Android 9.0 and above, you should most likely use a lower Android to support more devices. I built a personal fitness App to test, so yes, this was also to personally help bring back my exercising.
We will move in simple steps that are in a general order from here on out.
Step 2: Initiate Your MVVM
As you can no doubt understand, we won’t go through absolutely every line of code, it’s just hoping you know of your MVVM practices. We’ll just share a touch so it can be more understood. The start of the code is very simple, mind you.
namespace EdgeRsize
{
/// <summary>
/// Navigate between views
/// </summary>
class Navigator
{
public static Navigator Instance { get; private set; }
public Navigator()
{
Instance = this;
}
public void GoTo(string where)
{
View view = null;
switch (where)
{
case "exercises": view = new ExercisesView(); break;
case "addexercise": view = new AddExercisesView(); break;
case "doexercise": view = new DoExerciseView(); break;
case "history": view = new HistoryView(); break;
}
if (null != view)
{
MainPage.FRMContent.Content = view;
}
}
}
}
namespace EdgeRsize
{
class ViewModelLocator
{
public static ViewModelLocator Instance { get; private set; }
public ViewModelLocator()
{
Instance = this;
}
private ExercisesViewModel _exercisesVM = new ExercisesViewModel();
public ExercisesViewModel ExercisesVM
{
get => _exercisesVM;
}
private AddExercisesViewModel _addExercisesVM = new AddExercisesViewModel();
public AddExercisesViewModel AddExercisesVM
{
get => _addExercisesVM;
}
private DoExerciseViewModel _doExerciseVM = new DoExerciseViewModel();
public DoExerciseViewModel DoExerciseVM
{
get => _doExerciseVM;
}
private HistoryViewModel _historyVM = new HistoryViewModel();
public HistoryViewModel HistoryVM
{
get => _historyVM;
}
}
}
Which we make instances of in App.xaml
<Application.Resources>
<local:Navigator x:Key="Nav"/>
<local:ViewModelLocator x:Key="VML"/>
</Application.Resources>
Then, for the ease of views, we get a reference to a Frame we put in MainPage.xaml
<Grid BackgroundColor="#222">
<Frame x:Name="frmContent" BackgroundColor="#222"/>
</Grid>
public partial class MainPage : ContentPage
{
public static Frame FRMContent { get; private set; }
public MainPage()
{
InitializeComponent();
FRMContent = frmContent;
frmContent.Content = new ExercisesView();
}
}
Now, mind you, this wont share absolutely every file we make. This is just for showing off how I got to bind the MVVM into my Android project with the use of Xamarin.
We’d need to make ourselves Models that we use for the data, and with that being said you’ll notice we’re using something else for ourselves.
using SQLite;
namespace EdgeRsize.Models
{
class MExercise
{
[PrimaryKey, AutoIncrement]
public int ID { get; set; }
[Unique]
public string Name { get; set; }
}
}
Using NuGet, we have to add the package sqlite-net-pcl for our project for what I show. I’ve found it works for using SQLite in Android. Just take note, it’s a slightly different way of using SQLite compared to System.Data.SQLite for yourself.
Since we don’t manage the data yet, this is just wrapping in the MVVM ideas, we wont look at more code needed for SQLite yet.
namespace EdgeRsize.ViewModels
{
class AddExercisesViewModel
{
public AddExercisesViewModel()
{
_goBack = new Command(() =>
{
Navigator.Instance.GoTo("exercises");
});
}
private Command _goBack;
public Command GoBack
{
get => _goBack;
}
}
}
Take note, this is slightly different from your standard Windows 10 WPF Application you create. This is how we can easily make a command that we can use to execute through the WPF data binding from our Xamarin View.
<?xml version="1.0" encoding="UTF-8"?>
<ContentView xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:d="http://xamarin.com/schemas/2014/forms/design"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
x:Class="EdgeRsize.Views.AddExercisesView">
<ContentView.Content>
<Grid BindingContext="{Binding AddExercisesVM, Source={StaticResource VML}}">
<Grid.RowDefinitions>
<RowDefinition Height="48"/>
<RowDefinition />
</Grid.RowDefinitions>
<Grid Grid.Row="0">
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Label
Text="Add Exercise"
TextColor="LightGreen"
FontSize="26"
HorizontalTextAlignment="Center"
VerticalTextAlignment="Center"/>
<Button
Grid.Column="1"
Text="Back"
BackgroundColor="Orange"
TextColor="Black"
Command="{Binding GoBack}"/>
</Grid>
</Grid>
</ContentView.Content>
</ContentView>
You should see from that, we can stick to our normal WPF project data binding and creation. We haven’t stepped into the data side yet, we haven’t decided what all we want either, just you’d note from the full solution image we create a simple layout we can already work through completely.
Step 3: Start The Data Connection
We start with a side note, this isn’t to implement every feature. This is to show how we can implement the DataConnection for ourselves.
using EdgeRsize.Models;
using SQLite;
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
namespace EdgeRsize
{
class DataConnection
{
public static DataConnection Instance { get; private set; }
private SQLiteConnection _connection { get; set; }
public DataConnection()
{
Instance = this;
var dbFile = Path.Combine(System.Environment.GetFolderPath(System.Environment.SpecialFolder.Personal), "edgersize.sqlite");
if (!File.Exists(dbFile))
{
var dbcreate = new SQLiteConnection(dbFile);
dbcreate.CreateTable<MExercise>();
dbcreate.CreateTable<MExerciseSets>();
dbcreate.CreateTable<MExerciseTracker>();
dbcreate.Close();
}
_connection = new SQLiteConnection(dbFile);
}
public void Insert(MExercise item)
{
_connection.Insert(item);
}
public void Insert(MExerciseSets item)
{
_connection.Insert(item);
}
internal bool CheckUniqueExercise(string exercise)
{
try
{
var answer = _connection.Get<MExercise>(ob => ob.Name == exercise);
return false;
}
catch { }
return true;
}
public void Insert(MExerciseTracker item)
{
_connection.Insert(item);
}
public List<MExercise> GetExercises()
{
return _connection.Table<MExercise>().Where(s => true).ToList();
}
public List<MExerciseSets> GetExerciseSets(int exerciseID)
{
return _connection.Table<MExerciseSets>().Where(s => s.ExerciseID == exerciseID).ToList();
}
public List<MExerciseTracker> GetExerciseTrackers(int year, int month)
{
return _connection.Table<MExerciseTracker>().Where(s => s.When.Month == month && s.When.Year == year).ToList();
}
}
}
As you can tell, this is very simple code for us to use. We’re using simple data, it’s implementation is really quite easy for us. We also make it a simple line to add to App.xaml
<local:DataConnection x:Key="DataCon" />
We can get the Android function we would like to us and build up the view models as we want.
using Android.Widget;
using EdgeRsize.Models;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Text;
using Xamarin.Forms;
using Xamarin.Forms.PlatformConfiguration;
namespace EdgeRsize.ViewModels
{
class AddExercisesViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public AddExercisesViewModel()
{
_goBack = new Command(() =>
{
Navigator.Instance.GoTo("exercises");
});
_addExercise = new Command(() =>
{
if (Exercise.Length < 5)
{
Toast.MakeText(Android.App.Application.Context, "Exercises need names of 5 or more characters.", ToastLength.Short).Show();
}
else if (ListEX.Count == 0)
{
Toast.MakeText(Android.App.Application.Context, "Exercises need more than 0 sets.", ToastLength.Short).Show();
}
else if (!DataConnection.Instance.CheckUniqueExercise(Exercise))
{
Toast.MakeText(Android.App.Application.Context, "Exercises need unique names.", ToastLength.Short).Show();
}
else
{
var exercise = new MExercise()
{
Name = Exercise
};
DataConnection.Instance.Insert(exercise);
for (int i = 0; i < ListEX.Count; ++i)
{
var set = ListEX[i];
set.ExerciseID = exercise.ID;
DataConnection.Instance.Insert(set);
}
Navigator.Instance.GoTo("exercises");
}
});
_addExName = new Command(() =>
{
if (EXCount == "")
{
EXCount = "0";
}
if (EXName.Length < 5)
{
Toast.MakeText(Android.App.Application.Context, "Set's need names of 5 or more characters.", ToastLength.Short).Show();
}
else
{
ListEX.Add(new MExerciseSets()
{
Name = EXName,
Measured = EXMeasured,
Reps = int.Parse(EXCount)
});
}
});
}
private Command _goBack;
{
get => _goBack;
}
private Command _addExercise;
public Command AddExercise
{
get => _addExercise;
}
public void Refresh()
{
Exercise = "";
EXName = "";
EXMeasured = true;
EXCount = "0";
ListEX = new ObservableCollection<MExerciseSets>();
}
private string _exercise;
public string Exercise
{
get => _exercise;
set
{
_exercise = value;
var args = new PropertyChangedEventArgs(nameof(Exercise));
PropertyChanged?.Invoke(this, args);
}
}
private string _exName;
public string EXName
{
get => _exName;
set
{
_exName = value;
var args = new PropertyChangedEventArgs(nameof(EXName));
PropertyChanged?.Invoke(this, args);
}
}
private bool _exMeasured;
public bool EXMeasured
{
get => _exMeasured;
set
{
_exMeasured = value;
var args = new PropertyChangedEventArgs(nameof(EXMeasured));
PropertyChanged?.Invoke(this, args);
}
}
private string _exCount;
public string EXCount
{
get => _exCount;
set
{
_exCount = value;
var args = new PropertyChangedEventArgs(nameof(EXCount));
PropertyChanged?.Invoke(this, args);
}
}
private Command _addExName;
public Command AddExName
{
get => _addExName;
}
private ObservableCollection<MExerciseSets> _listEX;
public ObservableCollection<MExerciseSets> ListEX
{
get => _listEX;
set
{
_listEX = value;
var args = new PropertyChangedEventArgs(nameof(ListEX));
PropertyChanged?.Invoke(this, args);
}
}
}
}
This is shared for a simple reason, you can see we use our standard WPF style structure for the MVVM project. This way we can easily create our own WPF based Android Apps.
Step 4: You Can Use Your New Android App
So, as you can no doubt see, I have a very simple application I build from scratch, used the WPF based MVVM in, and it’s on my phone. I have several interesting ideas I put in as features, and you can understand I now have a simple ToDo.txt for myself.
It’s easy to show off how to use your software development practices on a Xamarin project for multiple platforms!
The final thing to share, hopefully this can help you create your own MVVM style application for Android devices. It should work the same on other platforms, for example iOS, it’s just to share how simple, and easy, it actually is for you.