sqlite with a bit of mvvm light in windows phone 8

While SQLce is still a viable solution in Windows Phone 8 to have some form of local database we also have an official SQLite implementation available. So why use SQLite when we can just keep using SQLce? Because Windows 8 only support SQLite and if you ever want to port over your app it would be nice not to have two versions of local databases to maintain. In this post I’ll explain how to implement a SQLite database into an MVVM Light Windows Phone 8 project (there is an unofficial Windows Phone 7 SQLite version as well but I have no idea how stable / buggy that is). I’ll be using Tim Heuer’s SQLite .net wrapper so we can use LINQ to SQLite instead of writing SQL queries manually (hooray for intellisense Smile). Let’s kick things off by creating an empty Windows Phone 8 app.

SQLite

Before we can use SQLite, we’ll need to install the SDK. Click here (official SQLite download page) to download the VSIX file and install it into Visual Studio.

NuGet fun

Before we can write any code we’ll need some NuGet packages. Use these commands in the Package Manager Console.

Install-Package MvvmLight

Install-Package sqlite-net

Install-Package WPtoolkit

Install-Package Cimbalino.Phone.Toolkit

Changing target platform

SQLite is a C++ library, meaning that it should be compiled against the architecture that the app will be running on. On Windows 8 that means creating separate packages for ARM and x86. On Windows Phone 8 that means switching from Any CPU to ARM when running on a device or when creating your XAP. When you’re running your app on the emulator the target platform needs to be set to x86.

Moving files around

When you  install the MVVM Light package it will add some folder structure and some files. I like to adjust this a bit more by adding a View folder and moving the MainPage into that view. That means that the startup page has to change as well. Open up the WMAppManifest.xml and change it like on the screenshot.

At this stage I couldn’t build the project because of a whole bunch of compile errors in the sqlite-net files. If you get the same problem (by the time you read this, it might be fixed), download the sqlite-net source from GitHub and from your project, add a reference to your local sqlite-net repo/lib/wp7/Community.CsharpSqlite.WinPhone.dll and that should fix it right up. Also, add a folder “Model” to the project so that our MVVM folder structure is complete.

The demo app

The app that we’ll be creating today is an app to keep track of tasks, which seems to be the new “Hello, World!”. We’ll start with the model, and work our way up to the view from there. Our class is called “Task” that means we’ll have to be careful that we use Model.Task instead of System.Threading.Task but we’ll manage.

Code Snippet
  1. [Table("Tasks")]
  2. public class Task : INotifyPropertyChanged
  3. {
  4.     private int _id;
  5.     private string _title;
  6.     private DateTime _date;
  7.  
  8.     [PrimaryKey, AutoIncrement]
  9.     public int Id
  10.     {
  11.         get { return _id; }
  12.         set
  13.         {
  14.             if (value == _id) return;
  15.             _id = value;
  16.             OnPropertyChanged("Id");
  17.         }
  18.     }
  19.  
  20.     public string Title
  21.     {
  22.         get { return _title; }
  23.         set
  24.         {
  25.             if (value == _title) return;
  26.             _title = value;
  27.             OnPropertyChanged("Title");
  28.         }
  29.     }
  30.  
  31.     public DateTime Date
  32.     {
  33.         get { return _date; }
  34.         set
  35.         {
  36.             if (value.Equals(_date)) return;
  37.             _date = value;
  38.             OnPropertyChanged("Date");
  39.         }
  40.     }
  41.  
  42.     public event PropertyChangedEventHandler PropertyChanged;
  43.     protected virtual void OnPropertyChanged(string propertyName = null)
  44.     {
  45.         PropertyChangedEventHandler handler = PropertyChanged;
  46.         if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
  47.     }
  48. }

The “Task” class implements INotifyPropertyChanged so that controls that are bound to its properties get updated like good citizens. Now for the annotations, those come from sqlite-net and mark this class as a table in the database. The same goes for the annotations on the Id property, that property is marked as being the primarykey and being an autoincremented value. If you have a property that you don’t want in the database, add the [Ignore] attribute and there won’t be any column generated for it. Now that we have a model we can start working on the service, the class that will do all the SQLite communication. (Yes we could do all this in the viewmodel but it’s called seperation of concerns Smile). And to do this the right way we’ll start by creating an interface for the service.

Code Snippet
  1. public interface IDataService
  2. {
  3.     Task SaveTask(Model.Task newTask);
  4.     Task<IList<Model.Task>> LoadTasks();
  5.     Task UpdateTask(Model.Task selectedTask);
  6.     Task DeleteTask(Model.Task selectedTask);
  7. }

Those are all the basic CRUD (Create, Read, Update, Delete) that we can (should be able to) perform on any datacontainer. Here’s the implementation

Code Snippet
  1. public class DataService : IDataService
  2. {
  3.     public async Task SaveTask(Model.Task newTask)
  4.     {
  5.         await App.Connection.InsertAsync(newTask);
  6.     }
  7.  
  8.     public async Task<IList<Model.Task>> LoadTasks()
  9.     {
  10.         return await App.Connection.Table<Model.Task>().ToListAsync();
  11.     }
  12.  
  13.     public async Task UpdateTask(Model.Task selectedTask)
  14.     {
  15.         await App.Connection.UpdateAsync(selectedTask);
  16.     }
  17.  
  18.     public async Task DeleteTask(Model.Task selectedTask)
  19.     {
  20.         await App.Connection.DeleteAsync(selectedTask);
  21.     }
  22.  
  23.     public async Task<IList<SubTask>> LoadSubTasks()
  24.     {
  25.         return await App.Connection.Table<SubTask>().ToListAsync();
  26.     }
  27. }

Hmm looks like I forgot to mention something, go to App.xaml.cs and add this property

Code Snippet
  1. public static SQLiteAsyncConnection Connection { get; set; }

Keep App.xaml.cs open, we’ll need it in a minute. In the DataService class we’re calling all the CRUD methods provided to us by sqlite-net. We can get a list of all records in a table by calling .Table<T>().ToListAsync() or do any of the other CRUD operations by just calling the function and passing in the modified POCO. Really easy and quite powerful.

Let’s jump back to App.xaml.cs, there should be an empty function called Application_Launching. In this function we’ll need to check if the database exists, open a connection to it if it exists or create it first and then open the connection.

Code Snippet
  1. private async void Application_Launching(object sender, LaunchingEventArgs e)
  2. {
  3.     try
  4.     {
  5.         await ApplicationData.Current.LocalFolder.GetFileAsync("taskDB.db");
  6.         Connection = new SQLiteAsyncConnection("taskDB.db");
  7.     }
  8.     catch (FileNotFoundException)
  9.     {
  10.         CreateDB();
  11.     }
  12. }


<p>Unfortunately, there is no DataBaseExists() function like in SQLce so I choose to do it the quick and dirty way. I try to get the database, which is basically a file in the ApplicationData, if the file doesn’t exist it will throw a FileNotFoundException and that’s where I call the CreateDB() method.</p> <div id="scid:9ce6104f-a9aa-4a17-a79f-3a39532ebf7c:cde6377c-a9ea-4f85-afb1-5131558d72b1" class="wlWriterEditableSmartContent" style="float: none; padding-bottom: 0px; padding-top: 0px; padding-left: 0px; margin: 0px; display: inline; padding-right: 0px"> <div style="border: #000080 1px solid; color: #000; font-family: 'Courier New', Courier, Monospace; font-size: 10pt"> <div style="background: #000080; color: #fff; font-family: Verdana, Tahoma, Arial, sans-serif; font-weight: bold; padding: 2px 5px">Code Snippet</div> <div style="background: #ddd; max-height: 300px; overflow: auto"> <ol start="1" style="background: #ffffff; margin: 0 0 0 2em; padding: 0 0 0 5px;"> <li>private async void CreateDB()</li> <li style="background: #f3f3f3">{</li> <li>    Connection = new SQLiteAsyncConnection("taskDB.db");</li> <li style="background: #f3f3f3"> </li> <li>    await Connection.CreateTableAsync<Task>();</li> <li style="background: #f3f3f3">}</li> </ol> </div> </div> </div> <p>Line 3 creates the database while line 5 creates the Task table in the database. When all that’s in place, we’re ready to move to the viewmodels.</p> <h2>ViewModel</h2> <p>Not much to say here, we all know what a viewmodel is, so here is the MainViewModel.</p> <div id="scid:9ce6104f-a9aa-4a17-a79f-3a39532ebf7c:4ccf49f3-ec5e-4f91-90f7-ff1bd5ba1684" class="wlWriterEditableSmartContent" style="float: none; padding-bottom: 0px; padding-top: 0px; padding-left: 0px; margin: 0px; display: inline; padding-right: 0px"> <div style="border: #000080 1px solid; color: #000; font-family: 'Courier New', Courier, Monospace; font-size: 10pt"> <div style="background: #000080; color: #fff; font-family: Verdana, Tahoma, Arial, sans-serif; font-weight: bold; padding: 2px 5px">Code Snippet</div> <div style="background: #ddd; max-height: 500px; overflow: auto"> <ol start="1" style="background: #ffffff; margin: 0 0 0 3em; padding: 0 0 0 5px;"> <li>public class MainViewModel : ViewModelBase</li> <li style="background: #f3f3f3">{</li> <li>    private readonly IDataService _dataService;</li> <li style="background: #f3f3f3">    private readonly INavigationService _navigationService;</li> <li> </li> <li style="background: #f3f3f3">    /// <summary></li> <li>    /// The <see cref="Tasks" /> property's name.</li> <li style="background: #f3f3f3">    /// </summary></li> <li>    public const string TasksPropertyName = "Tasks";</li> <li style="background: #f3f3f3"> </li> <li>    private IList<Task> _tasks;</li> <li style="background: #f3f3f3"> </li> <li>    /// <summary></li> <li style="background: #f3f3f3">    /// Sets and gets the Tasks property.</li> <li>    /// Changes to that property's value raise the PropertyChanged event. </li> <li style="background: #f3f3f3">    /// </summary></li> <li>    public IList<Task> Tasks</li> <li style="background: #f3f3f3">    {</li> <li>        get</li> <li style="background: #f3f3f3">        {</li> <li>            return _tasks;</li> <li style="background: #f3f3f3">        }</li> <li> </li> <li style="background: #f3f3f3">        set</li> <li>        {</li> <li style="background: #f3f3f3">            if (Equals(_tasks, value))</li> <li>            {</li> <li style="background: #f3f3f3">                return;</li> <li>            }</li> <li style="background: #f3f3f3"> </li> <li>            RaisePropertyChanging(TasksPropertyName);</li> <li style="background: #f3f3f3">            _tasks = value;</li> <li>            RaisePropertyChanged(TasksPropertyName);</li> <li style="background: #f3f3f3">        }</li> <li>    }</li> <li style="background: #f3f3f3">       </li> <li>    /// <summary></li> <li style="background: #f3f3f3">    /// The <see cref="NewTask" /> property's name.</li> <li>    /// </summary></li> <li style="background: #f3f3f3">    public const string NewTaskPropertyName = "NewTask";</li> <li> </li> <li style="background: #f3f3f3">    private Task _newTask;</li> <li> </li> <li style="background: #f3f3f3">    /// <summary></li> <li>    /// Sets and gets the NewTask property.</li> <li style="background: #f3f3f3">    /// Changes to that property's value raise the PropertyChanged event. </li> <li>    /// </summary></li> <li style="background: #f3f3f3">    public Task NewTask</li> <li>    {</li> <li style="background: #f3f3f3">        get</li> <li>        {</li> <li style="background: #f3f3f3">            return _newTask;</li> <li>        }</li> <li style="background: #f3f3f3"> </li> <li>        set</li> <li style="background: #f3f3f3">        {</li> <li>            if (_newTask == value)</li> <li style="background: #f3f3f3">            {</li> <li>                return;</li> <li style="background: #f3f3f3">            }</li> <li> </li> <li style="background: #f3f3f3">            RaisePropertyChanging(NewTaskPropertyName);</li> <li>            _newTask = value;</li> <li style="background: #f3f3f3">            RaisePropertyChanged(NewTaskPropertyName);</li> <li>        }</li> <li style="background: #f3f3f3">    }</li> <li> </li> <li style="background: #f3f3f3">    public RelayCommand SaveNewTaskCommand</li> <li>    {</li> <li style="background: #f3f3f3">        get { return new RelayCommand(SaveNewTask); }</li> <li>    }</li> <li style="background: #f3f3f3"> </li> <li>    public RelayCommand<SelectionChangedEventArgs> SelectionChangedCommand</li> <li style="background: #f3f3f3">    {</li> <li>        get { return new RelayCommand<SelectionChangedEventArgs>(SelectionChanged);}</li> <li style="background: #f3f3f3">    }</li> <li> </li> <li style="background: #f3f3f3">    /// <summary></li> <li>    /// Initializes a new instance of the MainViewModel class.</li> <li style="background: #f3f3f3">    /// </summary></li> <li>    public MainViewModel(IDataService dataService, INavigationService navigationService)</li> <li style="background: #f3f3f3">    {</li> <li>        _dataService = dataService;</li> <li style="background: #f3f3f3">        _navigationService = navigationService;</li> <li>        NewTask = new Task { Date = DateTime.Today };</li> <li style="background: #f3f3f3"> </li> <li>        LoadTasks();</li> <li style="background: #f3f3f3">    }</li> <li> </li> <li style="background: #f3f3f3">    private async void LoadTasks()</li> <li>    {</li> <li style="background: #f3f3f3">        Tasks = await _dataService.LoadTasks();</li> <li>    }</li> <li style="background: #f3f3f3"> </li> <li>    private async void SaveNewTask()</li> <li style="background: #f3f3f3">    {</li> <li>        await _dataService.SaveTask(NewTask);</li> <li style="background: #f3f3f3">        Tasks.Add(NewTask);</li> <li>        NewTask = new Task { Date = DateTime.Today };</li> <li style="background: #f3f3f3">    }</li> <li> </li> <li style="background: #f3f3f3">    private void SelectionChanged(SelectionChangedEventArgs args)</li> <li>    {</li> <li style="background: #f3f3f3">        if (args.AddedItems.Count > 0)</li> <li>        {</li> <li style="background: #f3f3f3">            Messenger.Default.Send(new TaskSelectedMessage(args.AddedItems[0] as Task));</li> <li>            _navigationService.NavigateTo(new Uri(@"/View/EditPage.xaml", UriKind.Relative));</li> <li style="background: #f3f3f3">        }</li> <li>    }</li> <li style="background: #f3f3f3">}</li> </ol> </div> </div> </div> <p>The IDataService field is what we’ve defined just a minute ago, it gets instantiated through the constructor. INavigationService comes from the Cimbalino toolkit, it allows us to do page to page navigation from within the viewmodels. There’s a property of IList<Task> that one will hold all the available tasks, they are loaded at startup, also newly added tasks will be put in that list. There’s a property of type Task, his properties will be bound to the input fields on the new task form, when the user clicks save the property will be pushed to the dataservice to save it in the database. Talking about the save button, there are two RelayCommands (MVVM Light’s implementation of ICommand). One is for saving a new property and the second one is for navigating to the detail page when a task is selected. In the constructor both fields are set and the Task property is initialized, setting the date to today. Since our datepicker will be bound to this property it will automatically be set to today’s date. Loading all the tasks needs to be done asynchronous, since the constructor can’t be marked as async we’ll put the service call in a synchronous method and call that one from the constructor, that way we can use the async / await keywords. Saving a task is as easy as calling the SaveTask function on IDataService and adding the new task to the list, and reinitializing it afterwards to clear all the fields. You might want to think about adding some check here in case something goes wrong while saving to the DB (have it return a boolean for example), I’ll just be living on the edge here and assume this never fails Smile. For navigating to the detail page we’ll add a command to the SelectionChanged event of our LongListSelector. We use the MVVM Light messenger, some sort of implementation of the Observer pattern, to send over the selected item to anyone registered to listen to a message of type TaskSelectedMessage. The TaskSelectedMessage class is pretty basic.</p> <div id="scid:9ce6104f-a9aa-4a17-a79f-3a39532ebf7c:04be6c8a-9bbc-460b-b3da-97b30e42bf3c" class="wlWriterEditableSmartContent" style="float: none; padding-bottom: 0px; padding-top: 0px; padding-left: 0px; margin: 0px; display: inline; padding-right: 0px"> <div style="border: #000080 1px solid; color: #000; font-family: 'Courier New', Courier, Monospace; font-size: 10pt"> <div style="background: #000080; color: #fff; font-family: Verdana, Tahoma, Arial, sans-serif; font-weight: bold; padding: 2px 5px">Code Snippet</div> <div style="background: #ddd; max-height: 300px; overflow: auto"> <ol start="1" style="background: #ffffff; margin: 0 0 0 2.5em; padding: 0 0 0 5px;"> <li>public class TaskSelectedMessage : MessageBase</li> <li style="background: #f3f3f3">{</li> <li>    public Model.Task Task { get; set; }</li> <li style="background: #f3f3f3"> </li> <li>    public TaskSelectedMessage(Model.Task task)</li> <li style="background: #f3f3f3">    {</li> <li>        Task = task;</li> <li style="background: #f3f3f3">    }</li> <li>}</li> </ol> </div> </div> </div> <p>The class inherits from MessageBase, which is a class in the MVVM Light library, it has one property that is set in the constructor (that’s just to make life a bit easier).</p> <p>In the MainViewModel, when the SelectionChanged event fires we send a message of this type containing the selected item, once the message is on its way we use the INavigationService to navigate to the detail page.</p> <p>Here’s the  viewmodel for the editpage.</p> <div id="scid:9ce6104f-a9aa-4a17-a79f-3a39532ebf7c:a2a4a19b-bab2-4268-9f5d-a12b2a8e27f3" class="wlWriterEditableSmartContent" style="float: none; padding-bottom: 0px; padding-top: 0px; padding-left: 0px; margin: 0px; display: inline; padding-right: 0px"> <div style="border: #000080 1px solid; color: #000; font-family: 'Courier New', Courier, Monospace; font-size: 10pt"> <div style="background: #000080; color: #fff; font-family: Verdana, Tahoma, Arial, sans-serif; font-weight: bold; padding: 2px 5px">Code Snippet</div> <div style="background: #ddd; max-height: 500px; overflow: auto"> <ol start="1" style="background: #ffffff; margin: 0 0 0 2.5em; padding: 0 0 0 5px;"> <li>public class EditViewModel : ViewModelBase</li> <li style="background: #f3f3f3">{</li> <li>    private readonly IDataService _dataService;</li> <li style="background: #f3f3f3">    private readonly INavigationService _navigationService;</li> <li> </li> <li style="background: #f3f3f3">    /// <summary></li> <li>    /// The <see cref="SelectedTask" /> property's name.</li> <li style="background: #f3f3f3">    /// </summary></li> <li>    public const string SelectedTaskPropertyName = "SelectedTask";</li> <li style="background: #f3f3f3"> </li> <li>    private Task _selectedTask;</li> <li style="background: #f3f3f3"> </li> <li>    /// <summary></li> <li style="background: #f3f3f3">    /// Sets and gets the SelectedTask property.</li> <li>    /// Changes to that property's value raise the PropertyChanged event. </li> <li style="background: #f3f3f3">    /// </summary></li> <li>    public Task SelectedTask</li> <li style="background: #f3f3f3">    {</li> <li>        get</li> <li style="background: #f3f3f3">        {</li> <li>            return _selectedTask;</li> <li style="background: #f3f3f3">        }</li> <li> </li> <li style="background: #f3f3f3">        set</li> <li>        {</li> <li style="background: #f3f3f3">            if (_selectedTask == value)</li> <li>            {</li> <li style="background: #f3f3f3">                return;</li> <li>            }</li> <li style="background: #f3f3f3"> </li> <li>            RaisePropertyChanging(SelectedTaskPropertyName);</li> <li style="background: #f3f3f3">            _selectedTask = value;</li> <li>            RaisePropertyChanged(SelectedTaskPropertyName);</li> <li style="background: #f3f3f3">        }</li> <li>    }</li> <li style="background: #f3f3f3"> </li> <li>    public RelayCommand UpdateTaskCommand</li> <li style="background: #f3f3f3">    {</li> <li>        get { return new RelayCommand(UpdateTask); }</li> <li style="background: #f3f3f3">    }</li> <li> </li> <li style="background: #f3f3f3">    public RelayCommand DeleteTaskCommand</li> <li>    {</li> <li style="background: #f3f3f3">        get { return new RelayCommand(DeleteTask); }</li> <li>    }</li> <li style="background: #f3f3f3"> </li> <li>    public EditViewModel(IDataService dataService, INavigationService navigationService)</li> <li style="background: #f3f3f3">    {</li> <li>        _dataService = dataService;</li> <li style="background: #f3f3f3">        _navigationService = navigationService;</li> <li> </li> <li style="background: #f3f3f3">        Messenger.Default.Register<TaskSelectedMessage>(this, msg => SelectedTask = msg.Task);</li> <li>    }</li> <li style="background: #f3f3f3"> </li> <li>    private void UpdateTask()</li> <li style="background: #f3f3f3">    {</li> <li>        _dataService.UpdateTask(SelectedTask);</li> <li style="background: #f3f3f3">    }</li> <li> </li> <li style="background: #f3f3f3">    private void DeleteTask()</li> <li>    {</li> <li style="background: #f3f3f3">        _dataService.DeleteTask(SelectedTask);</li> <li>    }</li> <li style="background: #f3f3f3">}</li> </ol> </div> </div> </div> <p>The same fields can be found here (I could put them in a base class, would be cleaner but who cares about clean code anyway? – well you should all care!). One property in this viewmodel, to hold the selected task and bind its properties to the view. A few commands for update and delete, they just call their respective functions on the DataService passing in the selected Task. The interesting part here is in the constructor. The fields get set and we register the viewmodel to listen if the messenger has a message of type TaskSelectedMessage, if it does set the task in the message to the property. However, the viewmodel by default gets instantiated when we navigate to the view meaning that the message has left the building before the receiver has registered as a receiver so it won’t arrive. Let’s fix that shall we? When you’ve added the MVVM Light libraries through NuGet (or you used the MVVM Light project templates) there should be a ViewModelLocator class in your ViewModel folder. This class registers your viewmodels in the TinyIoc container. Registering those viewmodels has an overload that, when set to True, creates an instance of each viewmodel at application launch, meaning that the viewmodels register themselves on the messenger before any message can be send. Here are my viewmodel registrations (from the ViewModelLocator constructor).</p> <div id="scid:9ce6104f-a9aa-4a17-a79f-3a39532ebf7c:19f5e722-1058-4188-971b-9cfbdc8d2bb6" class="wlWriterEditableSmartContent" style="float: none; padding-bottom: 0px; padding-top: 0px; padding-left: 0px; margin: 0px; display: inline; padding-right: 0px"> <div style="border: #000080 1px solid; color: #000; font-family: 'Courier New', Courier, Monospace; font-size: 10pt"> <div style="background: #000080; color: #fff; font-family: Verdana, Tahoma, Arial, sans-serif; font-weight: bold; padding: 2px 5px">Code Snippet</div> <div style="background: #ddd; max-height: 300px; overflow: auto"> <ol start="1" style="background: #ffffff; margin: 0 0 0 2em; padding: 0 0 0 5px;"> <li>SimpleIoc.Default.Register<MainViewModel>();</li> <li style="background: #f3f3f3">SimpleIoc.Default.Register<EditViewModel>(true);</li> </ol> </div> </div> </div> <p>MainViewModel won’t get instantiated at registration but EditViewModel will. So that’s a problem solved. Next piece of the puzzle are those constructor parameters in the viewmodels. They get resolved by dependency injection, we register the correct types here in the ViewModelLocator and when the viewmodel constructor is called, the correct instances will get injected automagically.</p> <div id="scid:9ce6104f-a9aa-4a17-a79f-3a39532ebf7c:f9165faf-eba9-4fe7-b3d9-02e643518ea7" class="wlWriterEditableSmartContent" style="float: none; padding-bottom: 0px; padding-top: 0px; padding-left: 0px; margin: 0px; display: inline; padding-right: 0px"> <div style="border: #000080 1px solid; color: #000; font-family: 'Courier New', Courier, Monospace; font-size: 10pt"> <div style="background: #000080; color: #fff; font-family: Verdana, Tahoma, Arial, sans-serif; font-weight: bold; padding: 2px 5px">Code Snippet</div> <div style="background: #ddd; max-height: 300px; overflow: auto"> <ol start="1" style="background: #ffffff; margin: 0 0 0 2.5em; padding: 0 0 0 5px;"> <li>if (ViewModelBase.IsInDesignModeStatic)</li> <li style="background: #f3f3f3">{</li> <li>    // Create design time view services and models</li> <li style="background: #f3f3f3">    SimpleIoc.Default.Register<IDataService, DesignService>();</li> <li>}</li> <li style="background: #f3f3f3">else</li> <li>{</li> <li style="background: #f3f3f3">    // Create run time view services and models</li> <li>    SimpleIoc.Default.Register<IDataService, DataService>();</li> <li style="background: #f3f3f3">    SimpleIoc.Default.Register<INavigationService, NavigationService>();</li> <li>}</li> </ol> </div> </div> </div> <p>Take this for example, when we are in design mode (Blend for example) we can load an IDataService implementation that returns dummy data so that we can style our views very easily (code gets executed when running at designtime so even when you’re not using dummy data it’s a good idea to register these types in an if-block to prevent design time errors).</p> <p>What everything in place, let’s have a look at the xaml and hook everything up. We’ll start with the MainPage.xaml and since XAML has a tendency of growing quite large, I’ll chop it in pieces. First thing a page needs in an MVVM scenario is a DataContext, meaning our ViewModel. This can be set from code behind (DataContext = new MainViewModel()) but that would just null out every use of the ViewModelLocator. We’ll set the datacontext from XAML.</p> <div id="scid:9ce6104f-a9aa-4a17-a79f-3a39532ebf7c:cace1f44-3fdd-4322-8ce4-8854a2a1212f" class="wlWriterEditableSmartContent" style="float: none; padding-bottom: 0px; padding-top: 0px; padding-left: 0px; margin: 0px; display: inline; padding-right: 0px"> <div style="border: #000080 1px solid; color: #000; font-family: 'Courier New', Courier, Monospace; font-size: 10pt"> <div style="background: #000080; color: #fff; font-family: Verdana, Tahoma, Arial, sans-serif; font-weight: bold; padding: 2px 5px">Code Snippet</div> <div style="background: #ddd; max-height: 500px; overflow: auto"> <ol start="1" style="background: #ffffff; margin: 0 0 0 2.5em; padding: 0 0 0 5px;"> <li><phone:PhoneApplicationPage x:Class="SqLitePoc.View.MainPage"</li> <li style="background: #f3f3f3">                            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"</li> <li>                            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"</li> <li style="background: #f3f3f3">                            xmlns:command="clr-namespace:GalaSoft.MvvmLight.Command;assembly=GalaSoft.MvvmLight.Extras.WP8"</li> <li>                            xmlns:behaviors="clr-namespace:Cimbalino.Phone.Toolkit.Behaviors;assembly=Cimbalino.Phone.Toolkit"</li> <li style="background: #f3f3f3">                            xmlns:d="http://schemas.microsoft.com/expression/blend/2008"</li> <li>                            xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"</li> <li style="background: #f3f3f3">                            xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"</li> <li>                            xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"</li> <li style="background: #f3f3f3">                            xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"</li> <li>                            xmlns:toolkit="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone.Controls.Toolkit"</li> <li style="background: #f3f3f3">                            DataContext="{Binding Main,</li> <li>                                                  Source={StaticResource Locator}}"</li> <li style="background: #f3f3f3">                            FontFamily="{StaticResource PhoneFontFamilyNormal}"</li> <li>                            FontSize="{StaticResource PhoneFontSizeNormal}"</li> <li style="background: #f3f3f3">                            Foreground="{StaticResource PhoneForegroundBrush}"</li> <li>                            Orientation="Portrait"</li> <li style="background: #f3f3f3">                            SupportedOrientations="Portrait"</li> <li>                            shell:SystemTray.IsVisible="True"</li> <li style="background: #f3f3f3">                            mc:Ignorable="d"></li> </ol> </div> </div> </div> <p>The key here is DataContext="{Binding Main, Source={StaticResource Locator}}" this says to the view that its datacontext is bound to a property called Main and that property lives in a resource called Locator (that resource is defined in App.xaml). Now for the page itself, the page consists of a pivot control with two pivot pages, one for entering new tasks and one for viewing a list of all the tasks that have been created so far.</p> <div id="scid:9ce6104f-a9aa-4a17-a79f-3a39532ebf7c:b5590042-a4f7-4bc7-9088-4006b0947750" class="wlWriterEditableSmartContent" style="float: none; padding-bottom: 0px; padding-top: 0px; padding-left: 0px; margin: 0px; display: inline; padding-right: 0px"> <div style="border: #000080 1px solid; color: #000; font-family: 'Courier New', Courier, Monospace; font-size: 10pt"> <div style="background: #000080; color: #fff; font-family: Verdana, Tahoma, Arial, sans-serif; font-weight: bold; padding: 2px 5px">Code Snippet</div> <div style="background: #ddd; max-height: 500px; overflow: auto"> <ol start="1" style="background: #ffffff; margin: 0 0 0 2.5em; padding: 0 0 0 5px;"> <li><!–  LayoutRoot is the root grid where all page content is placed  –></li> <li style="background: #f3f3f3"><Grid x:Name="LayoutRoot" Background="Transparent"></li> <li>    <!–  Bindable Appbar buttons  –></li> <li style="background: #f3f3f3">    <i:Interaction.Behaviors></li> <li>        <behaviors:ApplicationBarBehavior></li> <li style="background: #f3f3f3">            <behaviors:ApplicationBarIconButton Command="{Binding SaveNewTaskCommand, Mode=OneTime}" IconUri="/Assets/AppBar/save.png" Text="Save Task" /></li> <li>        </behaviors:ApplicationBarBehavior></li> <li style="background: #f3f3f3">    </i:Interaction.Behaviors></li> <li> </li> <li style="background: #f3f3f3">    <Grid.RowDefinitions></li> <li>        <RowDefinition Height="Auto" /></li> <li style="background: #f3f3f3">        <RowDefinition Height="*" /></li> <li>    </Grid.RowDefinitions></li> <li style="background: #f3f3f3">    <phone:Pivot Title="SQLite POC" Grid.Row="1"></li> <li>        <phone:PivotItem x:Name="NewTask" CacheMode="{x:Null}" Header="new task"></li> <li style="background: #f3f3f3">            <StackPanel></li> <li>                <TextBlock Text="Title" TextWrapping="Wrap" /></li> <li style="background: #f3f3f3">                <TextBox x:Name="TextBoxTitle"</li> <li>                            Height="72"</li> <li style="background: #f3f3f3">                            Text="{Binding NewTask.Title, Mode=TwoWay}"</li> <li>                            TextWrapping="Wrap" /></li> <li style="background: #f3f3f3">                <TextBlock Text="Complete by" TextWrapping="Wrap" /></li> <li>                <toolkit:DatePicker Value="{Binding NewTask.Date, Mode=TwoWay}" /></li> <li style="background: #f3f3f3">            </StackPanel></li> <li>        </phone:PivotItem></li> <li style="background: #f3f3f3">        <phone:PivotItem x:Name="AllTasks" CacheMode="{x:Null}" Header="all tasks"></li> <li>            <phone:LongListSelector ItemTemplate="{StaticResource TaskListItemTemplate}" ItemsSource="{Binding Tasks}"></li> <li style="background: #f3f3f3">                <i:Interaction.Triggers></li> <li>                    <i:EventTrigger EventName="SelectionChanged"></li> <li style="background: #f3f3f3">                        <command:EventToCommand Command="{Binding SelectionChangedCommand}" PassEventArgsToCommand="True" /></li> <li>                    </i:EventTrigger></li> <li style="background: #f3f3f3">                </i:Interaction.Triggers></li> <li>            </phone:LongListSelector></li> <li style="background: #f3f3f3">        </phone:PivotItem></li> <li>    </phone:Pivot></li> <li style="background: #f3f3f3"></Grid></li> </ol> </div> </div> </div>
<p>First thing in this snippet is a behavior for a bindable appbar button. The default appbar is not bindable, meaning that you can’t bind the buttons Command property to an ICommand on your viewmodel. This wasn’t the case in WP7 and it still isn’t in WP8, bit of a pain. Luckily, Cimbalino toolkit gives us an ApplicationBarBehavior, allowing us to bind our ICommands to the appbar, the only trade off we need to make is that the appbar buttons won’t be visible at design time but that’s a small trade-off in my opinion. We’ll add one button in the appbar, bind it to the SaveNewTaskCommand RelayCommand in MainViewModel and appoint it the save icon. Then there’s the pivot control, first pivotitem contains a stackpanel with a textbox for entering a task title and a datepicker (courtesy of the Windows Phone Toolkit) both are bound to properties of the NewTask property on the MainViewModel. Don’t forget to set the bind mode to TwoWay so that we can update the properties from our view. The second pivot item contains a list of all the tasks. Now, in WP8 they advice us to use the LongListSelector instead of the listbox that’s all great but at least make it behave more like a listbox and not some crippled dependency property missing piece of **. The problem lies in the SelectedItem property, myself and many other XAML devs usually create a SelectedTask property and bind it to the SelectedItem property of the ListBox, the setter of that SelectedTask property would then be used to navigate to the detailspage. That was a clean, fast solution but the LongListSelector’s SelectedItem property is not a dependency property, meaning it cannot be bound so that’s not a viable solution anymore. Second option would be to bind an ICommand to the SelectionChanged event, again a no-go. There are some implementations of the LongListSelector floating around on the internet that has a bindable SelectedItem property so that would be a solution, another one is to add an EventToCommand behavior and binding the SelectionChanged event to the MainViewModel in the behavior (that’s right Windows 8 devs, we Windows Phone devs still get behaviors out of the box). I’m going with the EventToCommand solution, only thing I haven’t solved here is that when we navigate to the detail page, navigate back to the mainpage and click the same task again it won’t do anything anymore since that item still is the selected item so the selection doesn’t change and the event doesn’t fire. A solution here would be to use the messenger to send a message to the code behind of the view to set the SelectedItem property of the LongListSelector to null.</p> <p>tl;dr version: LongListSelector kind off sucks but it can be solved.</p> <p>The LongListSelector is bound to the Tasks collection, the ItemTemplate is defined in the resources part of the page</p> <div id="scid:9ce6104f-a9aa-4a17-a79f-3a39532ebf7c:342d086a-4c04-4ad3-93aa-2564a21e645d" class="wlWriterEditableSmartContent" style="float: none; padding-bottom: 0px; padding-top: 0px; padding-left: 0px; margin: 0px; display: inline; padding-right: 0px"> <div style="border: #000080 1px solid; color: #000; font-family: 'Courier New', Courier, Monospace; font-size: 10pt"> <div style="background: #000080; color: #fff; font-family: Verdana, Tahoma, Arial, sans-serif; font-weight: bold; padding: 2px 5px">Code Snippet</div> <div style="background: #ddd; max-height: 500px; overflow: auto"> <ol start="1" style="background: #ffffff; margin: 0 0 0 2.5em; padding: 0 0 0 5px;"> <li><phone:PhoneApplicationPage.Resources></li> <li style="background: #f3f3f3">    <DataTemplate x:Key="TaskListItemTemplate"></li> <li>        <StackPanel></li> <li style="background: #f3f3f3">            <TextBlock x:Name="Title"</li> <li>                       Style="{StaticResource JumpListAlphabetStyle}"</li> <li style="background: #f3f3f3">                       Text="{Binding Title}"</li> <li>                       TextWrapping="Wrap" /></li> <li style="background: #f3f3f3">            <TextBlock x:Name="Date"</li> <li>                       Margin="12,0,0,0"</li> <li style="background: #f3f3f3">                       Text="{Binding Date}"</li> <li>                       TextWrapping="Wrap"></li> <li style="background: #f3f3f3">                <Run /></li> <li>                <LineBreak /></li> <li style="background: #f3f3f3">                <Run /></li> <li>            </TextBlock></li> <li style="background: #f3f3f3">        </StackPanel></li> <li>    </DataTemplate></li> <li style="background: #f3f3f3"></phone:PhoneApplicationPage.Resources></li> </ol> </div> </div> </div> <p>That gives the list a nice look (as far as I can tell that is, I really really suck at designing apps…)</p> <p>Last part on this page is the appbar itself, the button are defined using the Cimbalino toolkit but we need to actually put an appbar on the page, this sits between the resources and the LayoutRoot grid.</p> <div id="scid:9ce6104f-a9aa-4a17-a79f-3a39532ebf7c:50773ee4-abbd-4b38-980b-12f27e13faa9" class="wlWriterEditableSmartContent" style="float: none; padding-bottom: 0px; padding-top: 0px; padding-left: 0px; margin: 0px; display: inline; padding-right: 0px"> <div style="border: #000080 1px solid; color: #000; font-family: 'Courier New', Courier, Monospace; font-size: 10pt"> <div style="background: #000080; color: #fff; font-family: Verdana, Tahoma, Arial, sans-serif; font-weight: bold; padding: 2px 5px">Code Snippet</div> <div style="background: #ddd; max-height: 300px; overflow: auto"> <ol start="1" style="background: #ffffff; margin: 0 0 0 2em; padding: 0 0 0 5px;"> <li><!–  Buttons are defined using the behaviors in the Cimbalino toolkit to allow a bindable appbar  –></li> <li style="background: #f3f3f3"><phone:PhoneApplicationPage.ApplicationBar></li> <li>    <shell:ApplicationBar IsMenuEnabled="True" IsVisible="True" /></li> <li style="background: #f3f3f3"></phone:PhoneApplicationPage.ApplicationBar></li> </ol> </div> </div> </div> <p>And that’s it for the MainPage, on to the final part of this post, the EditPage.xaml</p> <p>First, the datacontext</p> <div id="scid:9ce6104f-a9aa-4a17-a79f-3a39532ebf7c:cb96f2ed-abf3-4393-b6e8-00d3f6d0f2c3" class="wlWriterEditableSmartContent" style="float: none; padding-bottom: 0px; padding-top: 0px; padding-left: 0px; margin: 0px; display: inline; padding-right: 0px"> <div style="border: #000080 1px solid; color: #000; font-family: 'Courier New', Courier, Monospace; font-size: 10pt"> <div style="background: #000080; color: #fff; font-family: Verdana, Tahoma, Arial, sans-serif; font-weight: bold; padding: 2px 5px">Code Snippet</div> <div style="background: #ddd; max-height: 300px; overflow: auto"> <ol start="1" style="background: #ffffff; margin: 0 0 0 2em; padding: 0 0 0 5px;"> <li>DataContext="{Binding Edit, Source={StaticResource Locator}}"</li> </ol> </div> </div> </div> <p>Then the appbar buttons, again using Cimbalino (these need to sit in the LayoutRoot grid)</p> <div id="scid:9ce6104f-a9aa-4a17-a79f-3a39532ebf7c:87917077-121e-4d7c-8a54-56a23cf69978" class="wlWriterEditableSmartContent" style="float: none; padding-bottom: 0px; padding-top: 0px; padding-left: 0px; margin: 0px; display: inline; padding-right: 0px"> <div style="border: #000080 1px solid; color: #000; font-family: 'Courier New', Courier, Monospace; font-size: 10pt"> <div style="background: #000080; color: #fff; font-family: Verdana, Tahoma, Arial, sans-serif; font-weight: bold; padding: 2px 5px">Code Snippet</div> <div style="background: #ddd; max-height: 300px; overflow: auto"> <ol start="1" style="background: #ffffff; margin: 0 0 0 2em; padding: 0 0 0 5px;"> <li><!–  Bindable Appbar buttons  –></li> <li style="background: #f3f3f3"><i:Interaction.Behaviors></li> <li>    <behaviors:ApplicationBarBehavior></li> <li style="background: #f3f3f3">        <behaviors:ApplicationBarIconButton Command="{Binding UpdateTaskCommand, Mode=OneTime}" IconUri="/Assets/AppBar/save.png" Text="Save Task" /></li> <li>        <behaviors:ApplicationBarIconButton Command="{Binding DeleteTaskCommand, Mode=OneTime}" IconUri="/Toolkit.Content/ApplicationBar.Delete.png" Text="Save Task" /></li> <li style="background: #f3f3f3">    </behaviors:ApplicationBarBehavior></li> <li></i:Interaction.Behaviors></li> </ol> </div> </div> </div> <p>And then there’s the controls</p> <div id="scid:9ce6104f-a9aa-4a17-a79f-3a39532ebf7c:14fe7265-945e-4981-a31b-42be6d4950f4" class="wlWriterEditableSmartContent" style="float: none; padding-bottom: 0px; padding-top: 0px; padding-left: 0px; margin: 0px; display: inline; padding-right: 0px"> <div style="border: #000080 1px solid; color: #000; font-family: 'Courier New', Courier, Monospace; font-size: 10pt"> <div style="background: #000080; color: #fff; font-family: Verdana, Tahoma, Arial, sans-serif; font-weight: bold; padding: 2px 5px">Code Snippet</div> <div style="background: #ddd; max-height: 500px; overflow: auto"> <ol start="1" style="background: #ffffff; margin: 0 0 0 2.5em; padding: 0 0 0 5px;"> <li><!–  TitlePanel contains the name of the application and page title  –></li> <li style="background: #f3f3f3"><StackPanel Grid.Row="0" Margin="12,17,0,28"></li> <li>    <TextBlock Style="{StaticResource PhoneTextNormalStyle}" Text="SQLite POC" /></li> <li style="background: #f3f3f3">    <TextBlock Margin="9,-7,0,0" Style="{StaticResource PhoneTextTitle1Style}" Text="Edit Task" /></li> <li></StackPanel></li> <li style="background: #f3f3f3"> </li> <li><!–  ContentPanel - place additional content here  –></li> <li style="background: #f3f3f3"><Grid x:Name="ContentPanel"</li> <li>      Grid.Row="1"</li> <li style="background: #f3f3f3">      Margin="12,0,12,0"></li> <li>    <StackPanel Margin="0,0,0,76"></li> <li style="background: #f3f3f3">        <TextBlock Text="Title" TextWrapping="Wrap" /></li> <li>        <TextBox x:Name="TextBoxTitle" Height="72" Text="{Binding SelectedTask.Title, Mode=TwoWay}" TextWrapping="Wrap" /></li> <li style="background: #f3f3f3">        <TextBlock Text="Complete by" TextWrapping="Wrap" /></li> <li>        <toolkit:DatePicker Value="{Binding SelectedTask.Date, Mode=TwoWay}" /></li> <li style="background: #f3f3f3">    </StackPanel></li> <li></Grid></li> </ol> </div> </div> </div> <p>That’s all pretty much the same as in the MainPage. And with that our small SQLite POC is finished.</p> <h2>Conclusion</h2> <p>In this post I’ve discussed SQLite in a Windows Phone 8 app. What you should take away from this is that as of Windows Phone 8 SQLite is a first class citizen, even more so when using the excellent sqlite-net library. Don’t forget to switch the platform when running on emulator or on a device, this is necessary because SQLite is a C++ library. I’ve also talked a bit about MVVM Light and the way I use it, I don’t claim that this is the best / only way to use the excellent MVVM implementation by Laurent Bugnion but it is one I feel comfortable with and that gives me great results. If you have any questions / remarks, feel free to drop a comment!</p> <p> </p> <p>UPDATE: </p> <p>for the LongListSelector, you can also use an extension of the control instead of defining a trigger, see http://stackoverflow.com/questions/14586521/bind-viewmodel-to-item-from-longlistselector-in-datatemplate/14600157#14600157 for more detail.
thanks for the tip Glenn (link to Glenn’s Twitter)!</p>

This is an imported post. It was imported from my old blog using an automated tool and may contain formatting errors and/or broken images.

Leave a Comment