In the MVVM pattern, the ViewModel is aware of the Model and the View interacts with the Model through the ViewModel. Instantiating the ViewModel in the App class allows all the various pages to interact with the single object. With that in place, although it’s not a prerequisite, I’ll move on to store contacts in a SQLite database and Views will interact with the DB through the VM.
-
Contact Model
Define the object in a class. This becomes the model.
123456789namespace App3{public class Contact{public int Id { get; set; }public string Name { get; set; }public int Phone { get; set; }}} -
Get and Set Contacts
Keeping the list in the Main Page, we can add records to it and send it to a ListView to display them.
12345678910111213141516171819202122232425262728public partial class MainPage : ContentPage{private List<Contact> contacts { get; set; }public MainPage(){contacts = new List<Contact>();InitializeComponent();}public void OnAdd(object sender, EventArgs e){Contact contact = new Contact {Name = ContactName.Text,Phone = int.Parse(ContactPhone.Text)};contacts.Add(contact);}public void OnCancel(object sender, EventArgs e){}public void OnContacts(object sender, EventArgs e){Navigation.PushAsync(new ListViewContacts(contacts));}}12345678910111213141516171819202122public partial class ListViewContacts : ContentPage{public List<Contact> Items { get; set; }public ListViewContacts(List<Contact> contacts){InitializeComponent();Items = contacts;MyListView.ItemsSource = Items;}async void Handle_ItemTapped(object sender, ItemTappedEventArgs e){if (e.Item == null)return;Contact c = (Contact)e.Item;var result = await DisplayAlert("Hello", c.Name, "Save", "Delete");//Deselect Item((ListView)sender).SelectedItem = null;}}123456789101112131415161718192021<?xml version="1.0" encoding="utf-8" ?><ContentPage xmlns="http://xamarin.com/schemas/2014/forms"xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"x:Class="App3.ListViewContacts"><ListView x:Name="MyListView"ItemsSource="{Binding Items}"ItemTapped="Handle_ItemTapped"CachingStrategy="RecycleElement"><ListView.ItemTemplate><DataTemplate><ViewCell><StackLayout Orientation="Horizontal"><Label Text="{Binding Name}" FontSize="20"/><Label Text="{Binding Phone}" FontSize="20"/></StackLayout></ViewCell></DataTemplate></ListView.ItemTemplate></ListView></ContentPage> -
Interact with Model through MVVM
Now, rather than creating a list in the Main Page to store contacts, move it to the ViewModel which will instantiate the list and handle interactions with it. The ListViewContacts will no longer take a List<Contact> argument and instead get the list from App.vm.GetContacts();
12345678910111213141516171819202122232425public class ContactsViewModel{private List<Contact> contacts { get; set; }public ContactsViewModel(){contacts = new List<Contact>();}public void AddContact(Contact contact){contacts.Add(contact);}public List<Contact> GetContacts(){return contacts;}public void RemoveContact(int id){Contact contact = contacts.Find(c => c.Id == id);contacts.Remove(contact);}}123456789101112public partial class App : Application{public static ContactsViewModel vm { get; set; }public App(){InitializeComponent();vm = new ContactsViewModel();MainPage = new NavigationPage(new MainPage());}// etc.}1234567891011121314151617181920212223242526272829303132public partial class MainPage : ContentPage{public MainPage(){InitializeComponent();}public void OnAdd(object sender, EventArgs e){Contact contact = new Contact {Name = ContactName.Text,Phone = int.Parse(ContactPhone.Text)};App.vm.AddContact(contact);Clear();}public void OnCancel(object sender, EventArgs e){// not implemented}public void OnContacts(object sender, EventArgs e){Navigation.PushAsync(new ListViewContacts());}private void Clear(){ContactName.Text = ContactPhone.Text = String.Empty;}}12345678910111213141516171819202122public partial class ListViewContacts : ContentPage{public List<Contact> Items { get; set; }public ListViewContacts(){InitializeComponent();Items = App.vm.GetContacts();MyListView.ItemsSource = Items;}async void Handle_ItemTapped(object sender, ItemTappedEventArgs e){if (e.Item == null)return;Contact c = (Contact)e.Item;var result = await DisplayAlert("Hello", c.Name, "Save", "Delete");//Deselect Item((ListView)sender).SelectedItem = null;}} -
Import SQLite
The MVVM will manage how views interact with the information in memory, but we still need a database to store data when the application is closed. Luckily, SQLite will help us with that. We need to add it to our project from NuGet, then create a db interface that defines a connection and a platform specific class that implements it. Lastly, we’ll add a couple of data attributes to the Contact model.
1234public interface ISQLite{SQLiteConnection Connection();}
123456789101112131415161718using App3.Droid;using SQLite;[assembly: Xamarin.Forms.Dependency(typeof(SQLite_Android))]namespace App3.Droid{public class SQLite_Android : ISQLite{public SQLiteConnection Connection(){var dbName = "MyContacts.db3";var path = System.Environment.GetFolderPath(System.Environment.SpecialFolder.Personal);path = System.IO.Path.Combine(path, dbName);return new SQLiteConnection(path);}}}
12345678public class Contact{[PrimaryKeyAttribute, AutoIncrement]public int Id { get; set; }[NotNull]public string Name { get; set; }public int Phone { get; set; }} -
Create, Read and Delete
Now the relationship between the ViewModel and the database needs to be established. Since the contact list will reside in the database, we’ll use the ViewModel to pass new contacts to it and retrieve existing contacts from it. The DB will be instantiated in App.xaml.cs to make it accessible everywhere and the VM will be called by the pages as they need it, MainPage and ListViewContacts.
123456789101112131415161718192021222324252627public class ContactsViewModel{private List<Contact> contacts { get; set; }public ContactsViewModel(){contacts = App.Database.GetContacts();//contacts = new List<Contact>();}public void AddContact(Contact contact){App.Database.AddContact(contact);//contacts.Add(contact);}public List<Contact> GetContacts(){return contacts;}public void RemoveContact(int id){App.Database.RemoveContact(id);//Contact contact = contacts.Find(c => c.Id == id);//contacts.Remove(contact);}}
12345678910111213141516171819202122232425public partial class App : Application{private static MyDatabase database;public static MyDatabase Database{get { if (database == null){database = new MyDatabase();}return database;}}public App(){InitializeComponent();//vm = new ContactsViewModel();MainPage = new NavigationPage(new MainPage());}protected override void OnStart(){...}protected override void OnSleep(){...}protected override void OnResume(){...}}
1234567891011121314151617181920212223242526272829public class MyDatabase{private SQLiteConnection database;static object locker = new object();public MyDatabase(){database = DependencyService.Get<ISQLite>().Connection();database.CreateTable<Contact>();}public List<Contact> GetContacts(){lock (locker){return (from c in database.Table<Contact>()select c).ToList();}}public void AddContact(Contact contact){database.Insert(contact);}public void RemoveContact(int id){database.Delete<Contact>(id);}}
12345678910111213public partial class ListViewContacts : ContentPage{public List<Contact> Items { get; set; }private ContactsViewModel vm { get; set; }public ListViewContacts(){InitializeComponent();vm = new ContactsViewModel();Items = vm.GetContacts();MyListView.ItemsSource = Items;}}