3 Comments

Pivotal CRM has an application for the iPad and for the iPhone. Both applications talk to a middle tier called Pivotal Device Server. This middle tier is the one responsible of accessing the information on the Pivotal Business Server. The Device Server exposes REST services which are consumed by these applications.

At the moment, the Device Server is only used by these two iOS applications. However, having a REST endpoint to our Pivotal server is something that could be leveraged in other applications. For example, I could have a HTML5 web site that reads/writes to our Pivotal backend using the REST interface. I could also write any mobile app I like (e.g. for Windows Phone or Android) to access the information. In other words, I could use the REST endpoint for anything I like.

The problem is, there is no documentation (or at least I haven’t found any) on how to use the Device Server. How can I use the REST endpoint if I don’t know the services exposed, the lifecycle, and the parameters required? Well, after some digging I’ve found some information that will allows us to use the Device Server as a middle tier for any application we want. As an example, I provide a simple console application that gets a list of contacts and then gets the data for one particular contact (you can extrapolate this to more useful and fun applications such as a MVC HTML5 app or a mobile cross platform app for any of the phones out there).

Note: I don’t know how the licensing works on this. You should check with your Pivotal provider to see how you can license this service to be used as a middle tier for any non-Pivotal application. This is just a technical article on how to do things, and as with everything, this doesn’t mean you can do them.

After the little disclaimer, let’s get to work.

Discovering the Service Metadata

Services created with Microsoft WCF can display a help page to show a little of documentation about the services exposed. This can be disabled, but fortunately enough, the developers didn’t disable this. You can get the metadata for the web service by appending /help to your service endpoint.

All the Device Server service metadata should be exposed by a call to http://<server>/PivotalDeviceServer/App/HelpAll (and I knew this after I did the actions below), but this throws an error.

So, how can I found what services are being used if there is no documentation? So our first step is to find out what services are exposed. Since all the communication between the devices and the device server is through HTTP, the easiest thing to do is to put something in the middle to allow us to see what information is being exchanged. I used Fiddler to do this. I configured Fiddler as a reverse proxy and then I configured the URL on the Pivotal CRM iOS application (either iPad or iPhone) to point to Fiddler instead of the actual server.

Pivotal CRM for iPad/iPhone login screenIn the image we can see the Pivotal CRM iPhone/iPad login screen. We can specify our own URL for the “Server URL” field". In this case we are pointing to the server where the Device Server is, but we are using the default Fiddler port 8888. Fiddler is installed on the same machine where the Device Server is installed, so it will then forward the request to the Device Server, but in the middle will show us what the request looks like. This will allow us to see what URLs are being called by the application, thus exposing the services endpoints.


After playing around with the app I found that the Device Server exposes four services:

  • App
  • User
  • Data
  • Metadata

If we navigate to the service metadata page (by appending the /help to the end of the url for the service) for each one of these services we will get a list of all the methods exposed by the service. The image shows the metadata for the Data service:

dataServiceMetadata

After doing this for each service, I got the following list:

/Data/Attachment/ByDataFormId/{dataformid}/{recordid}/{fieldid}
/Data/Attachment/ByDataFormName/{dataformname}/{recordid}/{fieldid}
/Data/ChoiceLists/ById/{tableid}?choicetype={choicetype}
/Data/ChoiceLists/ByName/{tablename}?choicetype={choicetype}
/Data/Command/CommandType/{commandtype}/CommandId/{commandid}/?returnSchema={returnSchema}
/Data/DataFormId/ByTableId/{tableid}/ByRecordId/{recordid}
/Data/Image/ById/{imageId}
/Data/Image/ByName/{imageName}
/Data/Images
/Data/Graph/ByGraphId/{graphId}/BySearchId/{searchId}
/Data/Graph/ByGraphName/{graphName}/BySearchId/{searchId}
/Data/ListDataForField/ByDataFormId/{dataformid}/{recordid}/{listformfieldid}
/Data/RecordAdd/ByDataFormId/{dataformid}?returnSchema={returnSchema}
/Data/RecordAdd/ByDataFormName/{dataformname}?returnSchema={returnSchema}
/Data/Record/ByDataFormId/{dataformid}/{recordid}?returnSchema={returnSchema}
/Data/Record/ByDataFormName/{dataformname}/{recordid}?returnSchema={returnSchema}
/Data/RecordCancel/ByDataFormId/{dataformid}/{recordid}
/Data/RecordDelete/ByDataFormId/{dataformid}/{recordid}
/Data/RecordDelete/ByDataFormName/{dataformname}/{recordid}
/Data/RecordLink/ByDataFormId/{parentdataformid}/{parentrecordid}/{linktableid}/{linkrecordid}?returnSchema={returnSchema}
/Data/RecordNew/ByDataFormId/{dataformid}?returnSchema={returnSchema}
/Data/RecordNew/ByDataFormName/{dataformname}?returnSchema={returnSchema}
/Data/RecordSecondary/ByDataFormId/{dataformid}/{recordid}/{secondarygroupid}?returnSchema={returnSchema}
/Data/RecordUnlink/ByDataFormId/{parentdataformid}/{parentrecordid}/{unlinktableid}/{unlinkrecordid}
/Data/RecordUpdate/ByDataFormId/{dataformid}/{recordid}?returnSchema={returnSchema}
/Data/RecordUpdate/ByDataFormName/{dataformname}/{recordid}?returnSchema={returnSchema}
/Data/Search/BySearchId/{searchId}?criteria={criteria}&returnSearchResultsListDef={returnSearchResultsListDef}&searchResultsListId={searchResultsListId}
/Data/Search/BySearchName/{searchName}?criteria={criteria}&returnSearchResultsListDef={returnSearchResultsListDef}&searchResultsListId={searchResultsListId}
/Data/Search/ByTableId/{tableId}?criteria={criteria}
/Data/Search/ByTableName/{tableName}?criteria={criteria}
/Data/SearchCount/BySearchId/{searchId}?criteria={criteria}
/Data/SearchCount/BySearchName/{searchName}?criteria={criteria}
/Data/SearchCount/ByTableId/{tableId}?criteria={criteria}
/Data/SearchCount/ByTableName/{tableName}?criteria={criteria}

/App
/App/HelpAll

/Metadata/AppLayout/BusinessObject
/Metadata/AppLayout/Dashboard
/Metadata/AppLayout/DataForm/ByTableName/{table}
/Metadata/AppLayout/All
/Metadata/AppLayout/Images
/Metadata/AppLayout/SearchList/ByTableName/{table}
/Metadata/AppLayout/Taskpads

/User/Login
/User/Logout
/User/Permissions

Ok, now I have some documentation. Next thing is how to use these methods.

Authentication

The first thing we need to do is login with the Device Server. If we use Fiddler, and login using the iPad/iPhone application, we will notice the following calls:

Device Server requests

The first call is the most important. If we inspect the request and the response, we will see something like this:

------------------------ Request ------------------------
POST http://windows2008/PivotalDeviceServer/User/Login HTTP/1.1
Host: windows2008
X-Titanium-Id: a406e9f4-38bf-40ff-b6ed-ed1c17ee11eb
X-Requested-With: XMLHttpRequest
Accept-Encoding: gzip
Content-Type: application/json
Content-Length: 737
Connection: close
Cookie: ASP.NET_SessionId=phfvgrdugwvhyiu1gah54kzy
User-Agent: Appcelerator Titanium/3.1.3.GA (iPad/7.1.1; iPhone OS; en_ES;)

{
    "AppInstallationId": "32C74B8E-C640-43D3-8556-64D29065C872",
    "Domain": "",
    "Id": "PCS",
    "Password": "%27%3C%C3%8D%03yOy%C3%96%C2%96",
    "AppVersion": "6.0.5.0.4.2",
    "SecurityGroups": {
        "SecurityGroups": [{
            "Id": "0x8000000000000002",
            "Name": "ContactManagementAdministrator"
        },
        {
            "Id": "0x8000000000000003",
            "Name": "ContactManagementSuperUser"
        },
        {
            "Id": "0x8000000000000004",
            "Name": "ContactManagementUser"
        }]
    },
    "AuthenticatedToken": null,
    "ForceLogon": false
}


------------------------ Response ------------------------
HTTP/1.1 200 OK
Cache-Control: private
Content-Type: application/json; charset=utf-8
Server: Microsoft-IIS/7.5
X-AspNet-Version: 4.0.30319
X-Powered-By: ASP.NET
Date: Thu, 08 May 2014 11:13:04 GMT
Connection: close
Content-Length: 1659

{
    "AppSchemaVersion": "1.0.3",
    "StatusCode": "Success",
    "CRMSystemName": "CRM",
    "DaysToKeepInHistory": 20,
    "DeviceServerVersion": "6.0.4.16",
    "DisableAutoLoginAfterIdleTimeout": "false",
    "DisableAutoLoginOnAppStart": "false",
    "DisableCachedMeta": "true",
    "DisableOffline": "false",
    "DoNotRememberUsernameOnAppStart": "false",
    "DoNotRunQuickSearchImmediately": "false",
    "ImagesChecksum": "817575750",
    "MetaDataSchemaVersion": "06.13",
    "MetaDataVersion": "826",
    "Permissions": {
        "SecurityGroups": [{
            "Id": "0x8000000000000002",
            "Name": "Contact Management Administrator"
        },
        {
            "Id": "0x8000000000000003",
            "Name": "Contact Management Super User"
        },
        {
            "Id": "0x8000000000000004",
            "Name": "Contact Management User"
        }],
        "TablePermissions": [{
            "CanDelete": true,
            "CanInsert": true,
            "CanQuickSearch": true,
            "CanRead": true,
            "CanUpdate": true,
            "Table": "Contact",
            "TableId": "0x800000000000003D"
        }]
    },
    "SecurityGroupsModified": "false",
    "StatusText": null,
    "SystemGlobalId": "17f65970-9322-47e7-b112-e424af3a430e",
    "SystemServerVersion": "6.0.13.16",
    "SystemType": "Pivotal",
    "UserToken": "4c8925a3753392da383f229f4ef4706ecb0ad9d3f8fcab016301e7f1fb2e7b2e8b8c171c4001dd6bcbba0057bd63b131ad744a286224d742b42788537063ea715e68407fc3062a165436bd6b95a36143818b9ed562b8833351b95a2f9aa68b6a5d64a74d4b9634c3ee00c1e374a0520ed35a999fcdc3df594d88817d495e5102"
}

The important information is highlighted. On the request we see that we need to specify a username and a password to authenticate with the Device Server. On the response we see that the Device Server returns an authentication token. This token will be used for all the subsequent requests to the Device Server.

The tricky part here is how to encrypt the password. The encryption used is some kind of the ARCFOUR algorithm. Here is an utility to encrypt the password:

using System;
using System.Text;
using System.Web;

namespace Pivotal
{
    public static class Util
    {
        public static string EncryptString(string valueToEncrypt)
        {
            if (string.IsNullOrWhiteSpace(valueToEncrypt))
            {
                return valueToEncrypt;
            }
            return Uri.EscapeDataString(RC4Encrypt("rzPeOQapG67sJfUqATJOfzAl74JXzyFdo0xTNYwVh", valueToEncrypt));
        }
        
        private static string RC4Encrypt(string key, string pt)
        {
            int num;
            int[] numArray = new int[256];
            for (int i = 0; i < 256; i++)
            {
                numArray[i] = i;
            }
            int num1 = 0;
            for (int j = 0; j < 256; j++)
            {
                int num2 = key[j % key.Length];
                num1 = (num1 + numArray[j] + num2) % 256;
                num = numArray[j];
                numArray[j] = numArray[num1];
                numArray[num1] = num;
            }
            int num3 = 0;
            num1 = 0;
            StringBuilder stringBuilder = new StringBuilder();
            for (int k = 0; k < pt.Length; k++)
            {
                num3 = (num3 + 1) % 256;
                num1 = (num1 + numArray[num3]) % 256;
                num = numArray[num3];
                numArray[num3] = numArray[num1];
                numArray[num1] = num;
                int num4 = pt[k];
                stringBuilder.Append(Convert.ToChar(num4 ^ numArray[(numArray[num3] + numArray[num1]) % 256]));
            }
            return stringBuilder.ToString();
        }
    }
}

With this we can authenticate with the server. Let’s create a simple console application to test the authentication. Since we are working with REST and JSON we will use the RestSharp and Json.Net libraries from NuGet.

static void Main(string[] args)
{
    string user = args[0];
    string password = args[1];
    
    password = Util.EncryptString(password));

    var client = new RestClient("http://windows2008/PivotalDeviceServer");
    
    // get the authentication token
    var request = new RestRequest("User/Login", Method.POST) { RequestFormat = DataFormat.Json };
    request.AddHeader("Accept", "application/json");
    request.AddBody(new
    {
        Id = user,
        Password = password
    });

    var response = client.Execute(request);
    JObject resultResponse = JObject.Parse(Util.DecodeJson(response.Content));
    string authenticationToken = (string)resultResponse["UserToken"];
}

We are reading the username and password from the command line (as mentioned above, you can extrapolate this to do more fun applications, this is just a simple example to explain things). In line 6, we encrypt the password using the utility presented above, and then, on line 16 and 17, we build a JSON body using RestSharp. Notice that we only care about the username and password, any other parameters we can skip. We then use Json.Net to parse the response and get the authentication token, in line 21, identified by the parameter UserToken. Notice also that we use an utility to decode the JSON response returned by the Device Server.

Not sure why (maybe is an iOS thing) but the JSON response has a lot of escape characters which are not standard JSON format. In order to get a valid JSON from the response, we need to trim all this escape chars. For this we use another utility method:

public static string DecodeJson(string json)
{
    json = json.Replace("\\\\\\\"","'").Replace("\\\"", "\"").Replace("\\\\/","/");
    return json.Substring(1,json.Length - 2);
}

We will use this utility each time we get a response back from the Device Server to format the returned JSON. If we don’t do this we will not be able to parse the JSON using Json.Net

Now that we have an authentication token, the next step is start using the API to work with the Device Server and the Pivotal backend. For our example we will get a list of contacts and then get the information from one of them. To get a list of records for a specific table we can use the method Data/Search/ByTableName/{tableName}. You can use Fiddler to see how to call this method. To save space, I won’t put the request and response output from Fiddler here, but instead I will show how to call this method from our sample program:

// get the list of contacts
request = new RestRequest("Data/Search/ByTableName/{tableName}", Method.POST) { RequestFormat = DataFormat.Json };
request.AddHeader("Accept", "application/json");
request.AddUrlSegment("tableName", "Contact");
request.AddBody(new
{
    AuthenticatedToken = authenticationToken
});
response = client.Execute(request);
resultResponse = JObject.Parse(JsonUtils.DecodeJson(response.Content));
JArray results = (JArray)resultResponse["SearchResult"];
List<Contact> records = results.Select(r => 
    {
        JArray fields = (JArray)r;
        int idFieldPosition = fields.Count - 1;
        return new Contact() 
        { 
            RnDescriptor = (string)fields[idFieldPosition]["Descriptor"],
            Id = (string)fields[idFieldPosition]["Value"]
        };
    }).ToList();

Notice, in line 4, how we specify the table name to be Contact. And in line 7, we use the authentication token we got from the first call and append this to the body of our request. I’m using a POCO object called Contact to store the information. This class is as follows:

public class PivotalRecord
{
    public string Id { get; set; }
    public string RnDescriptor { get; set; }
    public DateTime RnEditDate { get; set; }
    public DateTime RnCreateDate { get; set; }
    public string RnEditUser { get; set; }
    public string RnCreateUser { get; set; }

    public override string ToString()
    {
        return String.Format("{0} [{1}]",RnDescriptor, Id);
    }
}

public class Contact : PivotalRecord
{
    public string FirstName { get; set; }
    public string LastName { get; set; }        
}

We now have a list of all the contact Ids. Next, let’s see how we can get the data form a specific record. For this we will use the method Data/Record/ByDataFormName/{formName}/{recordId}

Contact record = records[0];
request = new RestRequest("Data/Record/ByDataFormName/{formName}/{recordId}?returnSchema=false", Method.POST) { RequestFormat = DataFormat.Json };
request.AddHeader("Accept", "application/json");
request.AddUrlSegment("formName", "Contact - Device");
request.AddUrlSegment("recordId", record.Id);
request.AddBody(new
{
    AuthenticatedToken = authenticationToken
});
response = client.Execute(request);                
resultResponse = JObject.Parse(JsonUtils.DecodeJson(response.Content));

record.RnDescriptor = (string)resultResponse["RecordDescriptor"];
record.RnEditDate = DateTime.Parse((string)resultResponse["RecordEditDate"]);
record.RnCreateDate = DateTime.Parse((string)resultResponse["RecordCreateDate"]);
record.RnCreateUser = (string)resultResponse["RecordCreateUserId"];
record.RnEditUser = (string)resultResponse["RecordEditUserId"];

JArray fieldList = (JArray)resultResponse["FormDataTableList"][0]["FormDataRowList"][0]["FormDataFieldList"];
record.FirstName = (string)fieldList[0]["Value"];
record.LastName = (string)fieldList[1]["Value"];

Notice again in line 8 how we specify the authentication token to be part of the body. The rest is just parsing the returned JSON to extract the information we need on the contact. Unfortunately, using the deserializer from Json.Net is not straighforward to do due to the way the JSON is constructed by the Device Server (it is a generic format used to represent any object).

And this is it. You can now use the Device Server as a REST endpoint to your Pivotal backend from any of your applications.

1 Comments

There are many options to manage data in Windows RT and Windows Phone. You can store data locally on the device using XML o JSON files, you can store data on the Cloud using Azure Mobile Services, you can use local databases, etc. Each one of these solutions have their advantages and disadvantages. In this article I will focus on the use of local databases, specifically using the SQLite database. You can find more information about SQLite on their web site www.sqlite.com. The good thing about SQLite is that it works on almost any platform you can think of, including of course Windows RT and Windows Phone. If you want to effectively use PCL and have most of the logic of the app shared between Windows RT and Windows Phone, then your best option (at least, at the time I write this article) is to use SQLite.

sqliteSQLite has SDK packages for both Windows RT (8.0 and 8.1) and Windows Phone (8) that can be installed as a .vsix package into Visual Studio. These are just wrappers to the sqlite library (written in C++) and their syntax is not what we would expect if our project is using C#/XAML. Besides, both SDKs are not suitable for a PCL project and we can’t have all the data access logic in our PCL project. Nick Randolph’s blog  has a very good 4-article series on how to use SQLite in a PCL library. He basically sets four goals: the project has to be blendable, has to use SQLite, all the data access logic needs to be in a PCL project, and the data access needs to be done using objects (LINQ style). I highly recommend reading the articles since it gives a step-by-step guide on how to install SQLite SDKs, and a bunch of nuget packages to achieve the goal. You will see that it is not easy to have it all configured, and having to do that every time you start a new project might be cumbersome. But, hey! there was no other alternative at the time he wrote the article, so kudos to him.

Fortunately, there is now a nuget package that will make things easier for you: SQLite.Net PCL. This is actually a set of packages you need to install (depending on the platform) in order to use SQLite. The thing is that setting it up is not trivial, you need to take the information from a bunch of blog posts, github docs, and even some source files, and there are some gotchas you need to be aware of. In this article I will summarize my experience in setting things up, hopefully to save time to those who would like to use the package. This is a step-by-step guide, so grab a cup of coffee and be ready to spend some time reading since it is a long article.

As with Nick’s articles, we will have the same 4 goals in mind (blendable, SQLite, PCL and LINQ). Of course, if we want to effectively achieve these goals we need to architecture our projects to use MVVM. I’m a big fan of MVVM Light, and this is what I will use in this guide.

Project structureLet’s start by creating a project structure such as the one we have in the figure. We have three projects, and the suffix for the project name describes it, but lets explain it in detail:

  • SampleApp.PCL: this is where all the logic for the app, including the data access layer, will be developed.
  • SampleApp.WinRT: this is the WinRT (in this case Windows 8.1) application. Notice that we have included a reference to the SampleApp.PCL project
  • SampleApp.WP: this is the Windows Phone (in this case, Windows Phone 8) application. Notice that we have included a reference to the SampleApp.PCL project

Notice the project structure is already organized to work with MVVM (notice the folder structure) and services and view models are already wired up with the IoC container. You can create your own project structure, or you can follow the structure I described in this set of articles:

The last article presents a sample application that uses device isolated storage for the data access layer. I will use the code in that article to change the data layer to SQLite. You can download the sample project from here.

Project configuration: the platform

Before continuing you need to change the platform of the project configuration. By default, the platform is AnyCPU. If you leave as this you will get errors when trying to install the nuget packages (and later, to run the project). This is because the SQLite’s SDKs are not built for AnyCPU, they must target a specific platform. The platform you choose should be based on the following:

  • For Windows Phone:
    • ARM: if you run on the device and to package the app for the Windows Store
    • x86: to debug and run on the emulator
  • For WinRT
    • x86: you can use this to debug and run on the simulator. You also need this to package the app for the Windows Store
    • x64: to package the app for the Windows Store

In the case of WinRT, when submitting the app in the Windows Store, you need to upload both x86 and x64 versions of your app.

For now we will use x86 on both the WinRT and WP projects. On VS, right click the solution and choose properties. On the configuration properties node change the platform from AnyCPU to x86 for both the WinRT and WP project:

platformConfiguration

SQLite.Net nuget package on the PCL project

SQLite.Net is actually a set of nuget packages. There is a common package for the PCL project, and then there is a package for each of the platforms you want to target. Let’s us start with our PCL project. On the PCL project, using the nuget package manager, install both SQLite.Net PCL (SQLite.Net-PCL) and SQLite.Net.Async PCL (SQLite.Net.Async-PCL) packages.

SQLite.Net nuget package on the WinRT project

For the WinRT project we need to install the same packages, plus the device specific package. The device specific package is the SQLite.Net PCL - WinRT Platform (SQLite.Net.Platform.WinRT). This package depends on a package called SQLite for Windows Runtime (Windows 8.1) – Redistribution (SQLite.WinRT.redist), which installs the SQLite SDK for the WinRT platform. The problem with this package (at the time of this writing) is that it targets a version of SQLite that is not the current one (when I write this article the latest SQLite SDK is version 3.8.4.3, and the version used by the nuget package is 3.8.4.1), so if you have already installed SQLite SDKs you will get an error when installing the nuget package. To workaround this, first uninstall the version of the SQLite SDK for the WinRT platform, and then add the nuget package.

If you have a version of the SQLite SDK that is equal or lower than the one targeted by the SQLite.WinRT.redist then you’re fine. If your installed version is greater then follow these steps (again, only do this if your version of the SQLite SDK is greater than the one targeted by SQLite.WinRT.redist):

  • Open Visual Studio, go to “Tools” menu, and then select “Extensions and Updates…”.
  • On the dialog box, on the tree that is displayed on the left, select “Installed –> SDK”.
  • Locate the extension called “SQLite for Windows Runtime (Windows 8.1)” and click Uninstall.
  • Restart Visual Studio

Now you’re ready to install the nuget packages. On the WinRT project, install the SQLite.Net.Platform.WinRT package. This will attempt to install first SQLite.WinRT.redist and you will notice that a .vsix package for the SQLite SDK is installed. Unfortunately the package will fail to install the first time, I guess because VS and the nuget package manager are not aware of the newly installed SDK. The workaround is to restart VS and then attempt to install the nuget package again. This time it should install without any problems.

Now, if you’re like me, who likes to use the latest version whenever possible, you would like to use the latest version of the SQLite SDK. These are the steps you need to use the latest version:

  • Uninstall the version of the SQLite SDK that was installed with the SQLite.WinRT.redist package using the steps outlined above. Close VS.
  • Download the latest .vsix package from the SQLite download page. At the time of this writing is version 8.3.4.3. You will probably download a .zip file, that’s ok, just change the file extension to .vsix. Install this package.
  • Now reopen your solution in VS. On the WinRT project expand the References node. You will see that the reference to the SQLite SDK has a warning: SQLite reference warning
  • Remove this reference.
  • Add a reference to the new SQLite SDK. You will find this reference on the node for Windows –>Extensions SQLite WinRT reference
  • Compile to make sure everything works

winrtReferencesOf course, if you do this, you need to repeat all the above every time you start a new project that uses SQLite. I whish that the SQLite.Net package would not depend on the SQLite.WinRT.redist, because this whole process is a pain in the a..., but anyway… let’s move on.

The SQLite.Net.Platform.WinRT will also install the base SQLite.Net-PCL package. For consistency, let’s add the SQLite.Net.Async-PCL as well. The figure shows how the WinRT project references should be at this point.


SQLite.Net nuget package on the WP project

If you haven’t done so before, install the SQLite SDK for the Windows Phone platform first. Download the latest .vsix package from the SQLite download page. At the time of this writing is version 8.3.4.3. You will probably download a .zip file, that’s ok, just change the file extension to .vsix and then install this package. Make sure you restart VS.

wpReferencesNow, on your WP project use the nuget package manager to install the the SQL.Net PCL – WindowsPhone8 Platform (SQLite.Net.Platform.WindowsPhone8) package.  The SQLite.Net.Platform.WindowsPhone8 will also install the base SQLite.Net-PCL package. For consistency, let’s add the SQLite.Net.Async-PCL as well.

Next, add a reference to the SQLite SDK. On the References node, choose “Add Reference..” and on the “Windows Phone –> Extensions” node select the SQLite SDK SQLite WP reference

The figure shows how the WP project references should be at this point.

Compile to make sure everything works fine.


Using SQLite

Now for the fun part, let’s change or data access to use SQLite. If you’ve followed along with my project we will make changes to our existing model and configure a new data access to be injected in our IoC container. If you’re not following along with my project, that’s ok, just create the files with the full code below.

The model

Let’s add an id property to our model. This id will be our primary key for the object. On the PCL project, on the “Models” folder, open the file Person.cs and make the following changes:

using GalaSoft.MvvmLight;
using SQLite.Net.Attributes;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace SampleApp.Models
{
    public class Person : ObservableObject
    {
        private int id;
        [PrimaryKey, AutoIncrement]
        public int Id 
        {
            get { return id; }
            set { Set(ref id, value); }
        }

        private string name;
        public string Name
        {
            get { return name; }
            set { Set(ref name, value); }
        }
    }
}

Notice the use of the PrimaryKey and AutoIncrement attributes (which are self explanatory).

The storage service

SQLite is just a file in the device storage. We will save this file on the application isolation storage. We need to tell the SQLite API the location of this file on the device. The location of a file on the device is dependent of the device, so this needs to be handled by a service. In our project we already have a service that handles storage specific actions. On the PCL project, on the “Services\Interfaces” folder, open the IStorageService.cs file and add the highlighted code:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace SampleApp.Services
{
    public interface IStorageService
    {
        Task SaveTextAsync(string file, string text);

        Task<string> LoadTextAsync(string file);

        Task<string> GetPathForFileAsync(string file);
    }
}

The GetPathForFileAsync method will provide SQLite with the full path to the database file. To implement this new method go to the WinRT project and on the “Services” folder open the file StorageService.cs and add the method implementation as follows:

public async Task<string> GetPathForFileAsync(string file)
{
    StorageFile storageFile = await ApplicationData.Current.LocalFolder.CreateFileAsync(file, CreationCollisionOption.OpenIfExists);

    return storageFile.Path;
}

Notice the use of StorageFile API to create the file, and the CreationCollisionOption.OpenIfExists option used. For SQLite to work the file needs to exist. If the file doesn’t exists you will get an error when trying to use the SQLite API. This is one of the gotchas to be aware of. Notice also that the file is created on the application local folder.

The data service

We will create our data service for SQLite now. On the PCL project, on the “Services” folder, add a new class and name it SQLiteDataService.cs:

using SampleApp.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using SQLite.Net;
using SQLite.Net.Interop;
using SQLite.Net.Async;
using System.IO;

namespace SampleApp.Services
{
    public class SQLiteDataService : IDataService
    {

        private IStorageService storageService;
        private ISQLitePlatform sqlitePlatform;

        private SQLiteAsyncConnection connection;

        public SQLiteDataService(ISQLitePlatform sqlitePlatform, IStorageService storageService)
        {
            this.sqlitePlatform = sqlitePlatform;
            this.storageService = storageService;

            InitializeAsync();
        }

        private async Task InitializeAsync()
        {
            if (connection == null)
            {
                string databaseFile = await storageService.GetPathForFileAsync("database.sqlite");
                connection = new SQLiteAsyncConnection(() => new SQLiteConnectionWithLock(sqlitePlatform, new SQLiteConnectionString(databaseFile, false)));
                await connection.CreateTableAsync<Person>();
            }
        }

        public async Task<IEnumerable<Person>> GetPersonsAsync()
        {
            return await connection.Table<Person>().ToListAsync();
        }

        public async Task AddPersonAsync(Person person)
        {
            await connection.InsertAsync(person);
        }
    }
}

There are a couple of things to notice here. Remember that we are using the PCL version of the SQLite.Net package. This PCL version actually relies on the platform specific implementation of SQLite. All PCL methods are based on the ISQLitePlatform interface. Each platform implements this interface and handles the platform's specific details for SQLite. In our case we are injecting the specific platform implementation using our IoC container. Notice how the constructor expects a ISQLitePlatform object which is assigned to a local property (lines 18 and 24).

The other thing to notice is the initialization of the database. First we get a reference to the file using our storage service (line 34) and then a SQLiteAsyncConnection is instantiated by passing the ISQLitePlatform implementation and the full path to the database file (line 35). I’m using the async implementation of the API, but you can use the synchronous one as well. The key thing is the platform specific object passed on the constructor.

The other parts of the file don’t deserve a details explanation since they just use the SQLite API (for more info refer to the SQLite docs or to the SQLite.Net docs).

We need to tell the IoC container to use this new data service. On the PCL project, on the “Services” folder, open the LocatorService.cs file and make the changes highlighted below:

using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.Ioc;
using Microsoft.Practices.ServiceLocation;
using SampleApp.ViewModels;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace SampleApp.Services
{
    public class LocatorService
    {
        static LocatorService()
        {
            ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default);

            // Services
            if (ViewModelBase.IsInDesignModeStatic)
            {
                SimpleIoc.Default.Register<IDataService, Design.DataService>();
            }
            else
            {
                SimpleIoc.Default.Register<IDataService, Services.SQLiteDataService>();
            }

            // View Models
            SimpleIoc.Default.Register<IMainViewModel, MainViewModel>();
        }

        public IMainViewModel MainViewModel
        {
            get
            {
                return ServiceLocator.Current.GetInstance<IMainViewModel>();
            }
        }

        public static void Cleanup()
        {
        }
    }
}

Also, we need to register with the IoC container the platform specific implementation of the ISQLitePlatform interface on both the WinRT and WP projects. On the WinRT project, on the “Services” folder, open the LocatorService.cs file and make the changes highlighted below:

using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.Ioc;
using Microsoft.Practices.ServiceLocation;
using SQLite.Net.Interop;
#if NETFX_CORE
using SQLite.Net.Platform.WinRT;
#else
using SQLite.Net.Platform.WindowsPhone8;
#endif
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace SampleApp.Services
{
    public class DeviceLocatorService
    {
        static DeviceLocatorService()
        {
            ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default);

            if (ViewModelBase.IsInDesignModeStatic)
            {
            }
            else
            {
            }

            #if NETFX_CORE
            if (!SimpleIoc.Default.IsRegistered<ISQLitePlatform>()) SimpleIoc.Default.Register<ISQLitePlatform, SQLitePlatformWinRT>();
            #else
            if (!SimpleIoc.Default.IsRegistered<ISQLitePlatform>()) SimpleIoc.Default.Register<ISQLitePlatform, SQLitePlatformWP8>();
            #endif

            if (!SimpleIoc.Default.IsRegistered<IStorageService>()) SimpleIoc.Default.Register<IStorageService, StorageService>();
        }

        public static void Cleanup()
        {
        }
    }
}

Notice the use of compiler directives, which is another trick to share files between WP and WinRT projects. In our case the LocatorService.cs file is a shared file (the WP project as a link to it from the WinRT project). We register the appropriate ISQLitePlatform interface: on WinRT we use SQLitePlatformWinRT and on WP we use SQLitePlatformWP8. With this, the IoC container will inject the appropriate SQLite implementation in the PCL data service.

Compile and make sure everything works fine. You should now be able to add objects to your SQLite database on both WinRT and WP, and all the data access logic developed on the PCL project. You can see the contents of your SQLite database using a tool such as sqlitebrowser.

You can download the sample project for this article from here:

1 Comments

In the article How to prepare a cross platform visual studio project for MVVM, I explained how to create a Visual Studio solution for cross platform application development using PCL and MVVM. In this article I continue with the sample project and implement a very simple example to put things together and see how the project structure and organization is used.

Let’s do a quick example. Let’s do a list of persons and display it on each devices using our recently configured MVVM project.

The model

On the PCL project, on the “Models” folder, create a file called Person.cs and replace it with this code:

using GalaSoft.MvvmLight;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace SampleApp.Models
{
    public class Person : ObservableObject
    {
        private string name;
        public string Name
        {
            get { return name; }
            set { Set(ref name, value); }
        }
    }
}

We are inheriting from ObservableObject (from MVVM Light) thus implementing INotifyPropertyChanged. We will now create a simple service to store data in the device. For this we need two services: a data service that will be common to all platforms, and a storage service that will be device specific.

The data service

Let’s start with the storage service. The interface for this service is declared in the PCL, but the implementation will be specific to both WinRT and WP. On the PCL project, on the “Services\Interfaces” folder, create a class called IStorageService.cs:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace SampleApp.Services
{
    public interface IStorageService
    {
        Task SaveTextAsync(string file, string text);

        Task<string> LoadTextAsync(string file);
    }
}

Now, let’s implement this interface on each of the devices. On the WinRT project, on the “Services” folder create a class named StorageService.cs:

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Windows.Storage;

namespace SampleApp.Services
{
    public class StorageService : IStorageService
    {

        public async Task SaveTextAsync(string fileName, string text)
        {
            StorageFile file = await ApplicationData.Current.LocalFolder.CreateFileAsync(fileName, CreationCollisionOption.ReplaceExisting);

            byte[] data = Encoding.UTF8.GetBytes(text);

            using (var s = await file.OpenStreamForWriteAsync())
            {
                await s.WriteAsync(data, 0, data.Length);
            }
        }

        public async Task<string> LoadTextAsync(string fileName)
        {
            try
            {
                StorageFile file = await ApplicationData.Current.LocalFolder.GetFileAsync(fileName);

                using (var s = await file.OpenStreamForReadAsync())
                {
                    using (var sr = new StreamReader(s))
                    {
                        return await sr.ReadToEndAsync();
                    }
                }
            }
            catch (Exception)
            {
                Debug.WriteLine("Error loading file '{0}'", fileName);
                return null;
            }
        }
    }
}

This is pretty simple, we just create a file on the application folder with the content. Let’s do the same for the WP project, but instead of creating and repeating the code, we will use the same file for the WinRT project, since the code is basically the same and it works without any changes on WP. This will be more and more the case as Microsoft is slowly converging both platforms, and now with the announce in Build2014 of universal apps this convergence is much closer. On the WP project, on the “Services” folder, add a link to the StorageService.cs file created before.

Now, we need to register both services with the IoC container. We will do this in LocatorService.cs file on the WinRT project (if you remember from the previous article, we just added this same file as a link on the WP project, se we need to change it once). Add the highlighted lines to the file:

using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.Ioc;
using Microsoft.Practices.ServiceLocation;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace SampleApp.Services
{
    public class DeviceLocatorService
    {
        static DeviceLocatorService()
        {
            ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default);

            if (ViewModelBase.IsInDesignModeStatic)
            {
            }
            else
            {
            }

            if (!SimpleIoc.Default.IsRegistered<IStorageService>()) SimpleIoc.Default.Register<IStorageService, StorageService>();
        }

        public static void Cleanup()
        {
        }
    }
}

Compile your solution to make sure everything works. Now, let’s create the data service. This data service will use the storage service to store the data in an XML format. In the PCL project, on the “Services\Interfaces” folder, create a file called IDataService.cs and replace the code with this:

using SampleApp.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace SampleApp.Services
{
    public interface IDataService
    {
        Task<IEnumerable<Person>> GetPersonsAsync();

        Task AddPersonAsync(Person person);
    }
}

As I mentioned before, let’s keep it simple :-). Now the implementation, in the PCL project, on the “Services” folder, create a file called DataService.cs with this code:

using SampleApp.Models;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Xml.Serialization;

namespace SampleApp.Services
{
    public class DataService : IDataService
    {
        private const string dataFile = "data.xml";

        private IStorageService storageService;
        private List<Person> persons;

        public DataService(IStorageService storageService)
        {
            this.storageService = storageService;
        }

        public async Task<IEnumerable<Person>> GetPersonsAsync()
        {
            if (persons == null) await LoadAsync();
            return persons;
        }

        public async Task AddPersonAsync(Person person)
        {
            if (persons == null) await LoadAsync();
            persons.Add(person);
            await SaveAsync();
        }

        private async Task SaveAsync()
        {
            StringWriter sw = new StringWriter();
            XmlSerializer serializer = new XmlSerializer(typeof(List<Person>));
            serializer.Serialize(sw, persons);

            await storageService.SaveTextAsync(dataFile, sw.ToString());
        }

        private async Task LoadAsync()
        {
            string data = await storageService.LoadTextAsync(dataFile);

            if (!string.IsNullOrEmpty(data))
            {
                StringReader sr = new StringReader(data);
                XmlSerializer serializer = new XmlSerializer(typeof(List<Person>));

                persons = (List<Person>)serializer.Deserialize(sr);
                
            }
            else
            {
                persons = new List<Person>();
            }
        }
    }
}

The service just serializes and deserializes data to and from an XML file. Notice the constructor of the service, it expects as a parameter an instance to a storage service, this instance is resolved by the IoC container and injected at run time. The service that will be injected will be the one registered in the locator service specific to the platform where the app is running. Everything is wired up appropriately.

Design time data

Now, this works fine when running on the device, but what about Blend? What about design time data? Designers will thank us if we provide design time data as part of the project as well. Let’s do this. On the PCL project, create a folder called “Design” and add a class named DataService.cs:

using SampleApp.Models;
using SampleApp.Services;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace SampleApp.Design
{
    public class DataService : IDataService
    {
        public Task<IEnumerable<Person>> GetPersonsAsync()
        {
            List<Person> persons = new List<Person>() {
                new Person() { Name = "Giovanni" },
                new Person() { Name = "Antonio"}
            };

            TaskCompletionSource<IEnumerable<Person>> tcs = new TaskCompletionSource<IEnumerable<Person>>();
            tcs.SetResult(persons);
            return tcs.Task;
        }

        public Task AddPersonAsync(Person person)
        {
            throw new NotImplementedException();
        }
    }
}

The above class will provide design time data for our views. Notice that we only provide the implementation of the GetPersosnAsync method, since this is the only method that we will need at design time. Now, we need to register this service to be used instead of the previous one when we are working at design time. We do this in the LocatorService.cs file in the PCL project. Open the file and add the lines highlighted below:

using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.Ioc;
using Microsoft.Practices.ServiceLocation;
using SampleApp.ViewModels;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace SampleApp.Services
{
    public class LocatorService
    {
        static LocatorService()
        {
            ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default);

            // Services
            if (ViewModelBase.IsInDesignModeStatic)
            {
                SimpleIoc.Default.Register<IDataService, Design.DataService>();
            }
            else
            {
                SimpleIoc.Default.Register<IDataService, Services.DataService>();
            }

            // View Models
            SimpleIoc.Default.Register<IMainViewModel, MainViewModel>();
        }

        public IMainViewModel MainViewModel
        {
            get
            {
                return ServiceLocator.Current.GetInstance<IMainViewModel>();
            }
        }

        public static void Cleanup()
        {
        }
    }
}

With this the IoC container will inject the design time data service when in design time, and the real data service (the one that works with the device storage) at run time on the device.

We have completed the model and the data access, next thing is the view model.

The view model

Let’s add some methods to our view model. In the PCL project, on the “ViewModels\Interfaces” folder, edit the IMainViewModel.cs file as follows:

using GalaSoft.MvvmLight.Command;
using SampleApp.Models;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;


namespace SampleApp.ViewModels
{
    public interface IMainViewModel
    {
        ObservableCollection<Person> Persons { get; }

        RelayCommand RefreshCommand { get; }
        RelayCommand AddCommand { get; }
    }
}
We’ve added an observable collection to bind to the views (we will do this later) and also commands to

execute business logic. Let’s implement the interface, open the MainViewModel.cs file and replace it with the following code:

using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.Command;
using SampleApp.Models;
using SampleApp.Services;
using System;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;


namespace SampleApp.ViewModels
{
    public class MainViewModel : ViewModelBase,IMainViewModel
    {
        private IDataService dataService;

        public MainViewModel(IDataService dataService)
        {         
            this.dataService = dataService;

            RefreshAsync();

        }

        private ObservableCollection<Person> persons = new ObservableCollection<Person>();
        public ObservableCollection<Person> Persons
        {
            get
            {
                return persons;
            }
        }

        #region Commands

        #region Refresh
        private RelayCommand refreshCommand;
        public RelayCommand RefreshCommand
        {
            get
            {
                return refreshCommand
                    ?? (refreshCommand = new RelayCommand(
                                          async () =>
                                          {
                                              await RefreshAsync();
                                          }));
            }
        }

        private async Task RefreshAsync()
        {
            persons.Clear();
            foreach (Person person in await dataService.GetPersonsAsync())
                persons.Add(person);
        }
        #endregion

        #region Add
        private RelayCommand addCommand;
        public RelayCommand AddCommand
        {
            get
            {
                return addCommand
                    ?? (addCommand = new RelayCommand(
                                          async () =>
                                          {
                                              Person person = new Person() { Name = "Giovanni" };
                                              await dataService.AddPersonAsync(person);
                                              persons.Add(person);
                                          }));
            }
        }
        #endregion

        #endregion
    }
}

Notice how the constructor expects a reference to a data service, which, as explained before, will be injected by the IoC container. The other thing to notice is the use of the design time data, the constructor does a refresh of the data so we can have data when working with the views.

We have completed our view model. Now we will work the views.

The views

I will provide a very simple view, no beauty on it, we will leave this for the designer. Instead, let’s focus on the code. Let’s first work the WinRT view. We will add a grid view and an app bar to add and refresh the data. On the WinRT project, open the MainPage.xaml and replace it with the following:

<Page
    x:Class="SampleApp.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:SampleApp"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d"
    DataContext="{Binding MainViewModel, Source={StaticResource Locator}}">

    <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">

        <GridView ItemsSource="{Binding Persons}">
            <GridView.ItemTemplate>
                <DataTemplate>
                    <Grid>
                        <TextBlock Text="{Binding Name}" />
                    </Grid>
                </DataTemplate>
            </GridView.ItemTemplate>
        </GridView>
    </Grid>

    <Page.BottomAppBar>
        <AppBar>
            <Grid>
                <StackPanel Orientation="Horizontal">
                    <AppBarButton Command="{Binding AddCommand}" Icon="AddFriend" Label="Add Person" />
                </StackPanel>

                <StackPanel Orientation="Horizontal" HorizontalAlignment="Right">
                    <AppBarButton Command="{Binding RefreshCommand}" Icon="Refresh" Label="Refresh" />
                </StackPanel>
            </Grid>
        </AppBar>
    </Page.BottomAppBar>
</Page>

We have used data binding to bind the list of persons and the commands as well. No code-behind! If you do this in the VS XAML editor, you will see the design time data:

WinRT view

On WP, we have one problem: the app bar doesn’t provide binding to commands. For this we will rely on a cool package called AppBarUtils. On the WP project, add this package using the nuget package manager. Replace the code in MainPage.xaml with the following:

<phone:PhoneApplicationPage
    x:Class="SampleApp.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
    xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"    
    xmlns:abu="clr-namespace:AppBarUtils;assembly=AppBarUtils"
    mc:Ignorable="d"
    FontFamily="{StaticResource PhoneFontFamilyNormal}"
    FontSize="{StaticResource PhoneFontSizeNormal}"
    Foreground="{StaticResource PhoneForegroundBrush}"
    SupportedOrientations="Portrait" Orientation="Portrait"
    shell:SystemTray.IsVisible="True"
    DataContext="{Binding MainViewModel,Source={StaticResource Locator}}">
    
    <!--LayoutRoot is the root grid where all page content is placed-->
    <Grid x:Name="LayoutRoot" Background="Transparent">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>

        <!--TitlePanel contains the name of the application and page title-->
        <StackPanel x:Name="TitlePanel" Grid.Row="0" Margin="12,17,0,28">
            <TextBlock Text="MY APPLICATION" Style="{StaticResource PhoneTextNormalStyle}" Margin="12,0"/>
            <TextBlock Text="page name" Margin="9,-7,0,0" Style="{StaticResource PhoneTextTitle1Style}"/>
        </StackPanel>

        <!--ContentPanel - place additional content here-->
        <Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
            <phone:LongListSelector Margin="0,0,-22,0" ItemsSource="{Binding Persons}">
                <phone:LongListSelector.ItemTemplate>
                    <DataTemplate>
                        <Grid>
                            <TextBlock Text="{Binding Name}" />
                        </Grid>
                    </DataTemplate>
                </phone:LongListSelector.ItemTemplate>
            </phone:LongListSelector>
        </Grid>

        <!--<Image Source="/Assets/AlignmentGrid.png" VerticalAlignment="Top" Height="800" Width="480" Margin="0,-32,0,0" Grid.Row="0" Grid.RowSpan="2" IsHitTestVisible="False" />-->
    </Grid>

    <phone:PhoneApplicationPage.ApplicationBar>
        <shell:ApplicationBar Mode="Default" Opacity="1.0" IsMenuEnabled="True" IsVisible="True">
            <shell:ApplicationBarIconButton IconUri="/Assets/AppBar/add.png" Text="add" />
            <shell:ApplicationBarIconButton IconUri="/Assets/AppBar/refresh.png" Text="refresh" />
        </shell:ApplicationBar>
    </phone:PhoneApplicationPage.ApplicationBar>

    <i:Interaction.Behaviors>
        <abu:AppBarItemCommand Id="add" Command="{Binding AddCommand}" Text="Add Person" />
        <abu:AppBarItemCommand Id="refresh" Command="{Binding RefreshCommand}" Text="Refresh" />
    </i:Interaction.Behaviors>
</phone:PhoneApplicationPage>

We have used data binding to bind the list of persons and the commands as well. Again, no code-behind! If you do this in the VS XAML editor, you will see the design time data:

WP

We have completed the views. Now, test it (pray) and make sure that everything works.

You can download the sample code from here:

2 Comments

In a previous blog post I explained how to configure a visual studio project structure for cross platform application development, using a PCL project for most of our app logic. In this article I will continue on the example and explain how to prepare the projects to work with MVVM. There is a lot of documentation about MVVM on the web so I won’t repeat it here, instead I will focus on project organization: where do I put models? how do I organize folders? what changes do I need to make in VS?, etc. I hope with this article to save you time reading a bunch of articles that only show part of the big picture.

visualStudioSolutionI’m assuming you have a project structure such as the one in the figure, where you have a PCL project for the business logic (our models and view models in MVVM jargon), and a project for each of the platforms you want to target: Windows RT and Windows Phone in our example. The PCL project has been included as a reference on each of the platform specific projects.

We want to do things right so we will use an IoC container to inject objects in our code. You can use any IoC container you want, I’m a big fan of MVVM Light, so this is what I will use for the example. MVVM Light has its own IoC container and also has some utility classes that are very useful for MVVM. I recommend you read the documentation and see some sample of MVVM Light on its web page.

MVVM has three main entities: models, view models and views. Where do we put these? Models and view models should be independent of the platform, so they should go in the PCL project. Views, on the other hand, are dependent of the platform (e.g. to present a list of items in WinRT we use a  GridView, while in Windows Phone we use a LongListSelector), so they should go on each of the platform projects. There is another entity in a good MVVM project: services.

Services fill the gap between the platform independent code in the PCL and the platform dependent code on each of the platform projects. This is better illustrated with an example: suppose our business logic (which is coded in the view model) needs to display a message to the user; messages are displayed differently on each platform, so a dialog box in WP looks different and has a different API than the one in WinRT. The view model doesn’t know (and this is ok) how to display a message for a specific platform. This is the whole purpose of MVVM, each layer is only concerned with what it needs to do. Displaying a message is the job of the platform. So, how can we display a message to the user if the logic is in the view model. This is where services come in handy. We will have a service for each platform and we will tell the view model to use that service. When the view model wants to display a message to the user, it will call the appropriate service to do it. Since view models are independent of the platform, services are injected at run time using an IoC container. This works the same for a bunch of other features such as accessing devices specific hardware (GPS, accelerometer, etc.), accessing data on the device, working with the device screen, etc. There’s a lot of documentation on this on the web, this was just a brief description since we will need to know about this fourth entity (the service) when creating our project structure.

The views

Since this is all about organization, let’s start by organizing our views. When we create the WinRT/WP project VS will automatically create a MainPage.xaml (a view) into our project root. If we follow this, we will end up with all our views in the root folder. I like my root folder very clean, so the first thing is to create a “Views” folder on each of the WinRT and WP folder and move MainPage.xaml into it. If you just do this, you will find that your project might not even start. There are some things you need to do as well.

In the WinRT project this is straightforward, create the “Views” folder and move the MainPage.xaml into it. That’s it! This works since the initial page to show is called from App.xaml.cs on the OnLaunched event, and since we haven’t changed the name of the class nor its namespace things still work. In WP project is not as easy. Create the “Views” folder and move the MainPage.xaml into it, open the app manifest (located in Properties\WMAppManifest.xml) and edit the “Navigation Page” field to specify the full path to the page, including the “Views” folder (see image below). You will have a project structure as follows:

Views folder created on projects


The services

servicesNow, in the PCL project create folders for the “Models”, “ViewModels” and “Services”, as shown in the figure. For the “Models” and “ViewModels” folder, create a subfolder called "Interfaces”. This folder is where we will create all the interface declarations. Create a “Services” folder on both the WinRT and WP project as well. You should now have a structure such as the one on the figure.

Next, use the nuget package manager to install MVVM Light PCL. Install only the libraries. You need to install this package on all three projects. Next we will configure the MVVM Light IoC container and wire things up. We need a locator service, which will be responsible for configuring the IoC container and to provide the right view models to the view. In the case of platform services we will need to configure these on both WinRT and WP projects.

On the PCL project, on the “Services” folder, create a file called LocatorService.cs. Replace the code with the one below:

using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.Ioc;
using Microsoft.Practices.ServiceLocation;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace SampleApp.Services
{
    public class LocatorService
    {
        static LocatorService()
        {
            ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default);


            // Services
            if (ViewModelBase.IsInDesignModeStatic)
            {
            }
            else
            {
            }

            // View Models
        }


        public static void Cleanup()
        {
        }
    }
}

We will later add code to this file to register all the services and view models. For the moment let’s leave it as it is and compile the solution. We will register here services that are cross platform. For services that are platform specific (most of them) we will need a similar file on both the WinRT and WP projects. On the WinRT project, on the “Services” folder, create a file called LocatorService.cs and replace the code with the following:

using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.Ioc;
using Microsoft.Practices.ServiceLocation;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace SampleApp.Services
{
    public class DeviceLocatorService
    {
        static DeviceLocatorService()
        {
            ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default);

            if (ViewModelBase.IsInDesignModeStatic)
            {
            }
            else
            {

            }

        }

        public static void Cleanup()
        {
        }
    }
}

Notice that the name of the class is DeviceLocatorService. This is different than the one specified on the PCL project, since this locator service is for device specific services and since they both share the same namespace (remember that in our case we want the same namespace for all projects) we need to specify a different name. We need to do this on the WP project as well, but instead of creating a new class we will add a link to the file used for the WinRT project, as the code is the same for both platforms. This is one of the tricks used to share code between platform projects, just make sure to select the “Add as Link” from the drop down button in the “Add existing item” dialog box.

Now we need to register these services on each of the platforms. On the WinRT project, open the file App.xaml, add the lines highlighted below:

<Application
    x:Class="SampleApp.App"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:ignore="http://www.ignore.com"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"    
    mc:Ignorable="d ignore"    
    xmlns:services="using:SampleApp.Services"    
    xmlns:local="using:SampleApp">

    <Application.Resources>
        <ResourceDictionary>
            <services:DeviceLocatorService x:Key="Locator.W8" d:IsDataSource="True" />
            <services:LocatorService x:Key="Locator" d:IsDataSource="True" />
        </ResourceDictionary>
    </Application.Resources>
</Application>

The important thing to understand is that by declaring both classes (DeviceLocatorService and LocatorService) as resources the application will automatically execute the static code on both classes, thus configuring the IoC container with the classes we need. Notice that the device locator service is specified before the generic locator service. This is because the generic locator service will need references to device services and thus is dependent of these.

On the WP project, open the App.xaml file and add the lines highlighted below:

<Application
    x:Class="SampleApp.App"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
    xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    mc:Ignorable="d"    
    xmlns:services="clr-namespace:SampleApp.Services;assembly=SampleApp.PCL"
    xmlns:deviceServices="clr-namespace:SampleApp.Services">

    <!--Application Resources-->
    <Application.Resources>
        <local:LocalizedStrings xmlns:local="clr-namespace:SampleApp" x:Key="LocalizedStrings"/>
        <deviceServices:DeviceLocatorService x:Key="Locator.WP8" d:IsDataSource="true" />
        <services:LocatorService x:Key="Locator" d:IsDataSource="true" />
   </Application.Resources>

    <Application.ApplicationLifetimeObjects>
        <!--Required object that handles lifetime events for the application-->
        <shell:PhoneApplicationService
            Launching="Application_Launching" Closing="Application_Closing"
            Activated="Application_Activated" Deactivated="Application_Deactivated"/>
    </Application.ApplicationLifetimeObjects>

</Application>

At this point, compile your projects and make sure everything runs.

The view models

We will now create our view model for the MainPage.xaml view. On the PCL project, on the ViewModels\Interfaces folder, create a file called IMainViewModel.cs, and replace the code with the one below:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace SampleApp.ViewModels
{
    public interface IMainViewModel
    {        
    }
}

VS by default will add the folder structure as part of the namespace. This means that the interface will have the namespace SampleApp.ViewModels.Interfaces. This is ok, but I don’t want that many namespaces so I remove the .Interfaces part and make it just one namespace for the view models SampleApp.ViewModels. Next, let’s create the implementation of this interface. In the PCL project, on the “ViewModels” folder, create a file called MainViewModel.cs and replace the code as follows:

using System;
using System.Linq;
using System.Text;
using System.Threading.Tasks;


namespace SampleApp.ViewModels
{
    public class MainViewModel : IMainViewModel
    {
    }
}

Now, let’s register this view model with the IoC container and wire it up on the view. On the PCL project, open the LocatorService.cs file and add the lines highlighted below:

using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.Ioc;
using Microsoft.Practices.ServiceLocation;
using SampleApp.ViewModels;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace SampleApp.Services
{
    public class LocatorService
    {
        static LocatorService()
        {
            ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default);

            // Services
            if (ViewModelBase.IsInDesignModeStatic)
            {
            }
            else
            {
            }

            // View Models
            SimpleIoc.Default.Register<IMainViewModel, MainViewModel>();
        }

        public IMainViewModel MainViewModel
        {
            get
            {
                return ServiceLocator.Current.GetInstance<IMainViewModel>();
            }
        }

        public static void Cleanup()
        {
        }
    }
}

On the WinRT project, open the Views\MainPage.xaml file and add the lines highlighted below:

<Page
    x:Class="SampleApp.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:SampleApp"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d"
    DataContext="{Binding MainViewModel, Source={StaticResource Locator}}">

    <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">

    </Grid>
</Page>

We are setting up the data context of the page to the view model, which is exposed by the service locator (identified by the key Locator) through the property MainViewModel. We will do the same for our WP project. Open the Views\MainPage.xaml and add the lines highlighted below:

<phone:PhoneApplicationPage
    x:Class="SampleApp.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
    xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d"
    FontFamily="{StaticResource PhoneFontFamilyNormal}"
    FontSize="{StaticResource PhoneFontSizeNormal}"
    Foreground="{StaticResource PhoneForegroundBrush}"
    SupportedOrientations="Portrait" Orientation="Portrait"
    shell:SystemTray.IsVisible="True"
    DataContext="{Binding MainViewModel,Source={StaticResource Locator}}">
    
    <!-- code omitted for simplicity -->

</phone:PhoneApplicationPage>

The project structure is already set up, things are wired up, and we can now start working on the specifics of our application.

I the next article I will explain how to use this project structure for a sample application.

0 Comments

One of our goals as developers when building for multiple platform is to minimize the code duplication required to support each platform. For projects that target the Windows RT and Windows Phone platforms (and even Android and iOS using Xamarin) we can make use of a type of project called Portable Class Library, or PCL for short. There is a lot of documentation about how to use PCL in cross platform projects on the web so I will not talk about how to use PCL, but instead talk about one of the challenges that these kind of projects have: creating the solution and project structure in Visual Studio.

How to name projects? what namespaces should they use? how to name the assemblies? and how to make Visual Studio do what we want to do? In this article I will give a step-by-step guide on how to create a Visual Studio solution and projects for a cross platform application.

Visual Studio SolutionLet’s start by describing what we want: we want three projects, a PCL project, a WinRT project and WP project. As an example, we would like to create a project structure such as the one we have in the figure. We will use a suffix on the project name to specify the type of project. Let’s explain it in detail:

  • SampleApp.PCL: this is where all the logic for the app will be developed.
  • SampleApp.WinRT: this is the WinRT (in this case Windows 8.1) application, containing the views and platform specific services for WinRT. Notice that we have included a reference to the SampleApp.PCL project
  • SampleApp.WP: this is the Windows Phone (in this case, Windows Phone 8) application, containing the views and platform specific services for Windows Phone. Notice that we have included a reference to the SampleApp.PCL project

I have chosen to create blank projects for both WinRT and WP apps since I like to have control over all the code, but you can choose any template you want. You need to be careful when creating the projects since Visual Studio will use the name of the project as the default namespace and you will end up with three different namespaces (one for each project) and then changing this can be problematic.

It is important to specify the same default namespace for all three projects in order to minimize the using clauses on the code and xmlns declarations in the XAML. This is just a personal choice, you can of course have three different namespaces, one for each type of project if you want. In this case I want the default namespace for all the projects to be the same (SampleApp in the example).

I will describe how to achieve the project structure described above and I will use “SampleApp” as the name of my project in the examples, but of course, you can use your own project name.

The Portal Class Library project

Start by creating the PCL project first. VS will automatically create the solution when you’re creating the first project.

Open Visual Studio and create a new PCL project and call it “SampleApp”:

New PCL project

Notice that we don’t name the project “SampleApp.PCL”, we just use “SampleApp”. If we name it “SampleApp.PCL” VS will configure the project with a default namespace of “SampleApp.PCL” and we don’t want this. Although we can then change it in the project properties, this is easy for a PCL project, but it is somewhat difficult for a WinRT and WP project since we would have to go and change all the generated XAML files, and .cs files and change the namespace. So, for consistency, let’s do the same process on all our projects.

Choose the appropriate target frameworks, in our case choose Windows Store and Windows Phone. VS will create the PCL project. Remove the auto-generated Class1.cs file. Next, rename the project to “SampleApp.PCL”, and in the project properties change the assembly name to “SampleApp.PCL”. You will have something like this:

PCL project

If you’re using TFS or Git (or any source code repository for that matter) don’t add the project to it just yet, let’s first create the structure and then we can add it to source control. It will be easier this way.

Now, here comes the tricky part. Since we will use the same project name (SampleApp) for all the projects, we need to change the folder name on disk, otherwise VS will put all the projects in the same folder, and will throw an error since files already exist.

PCL folderClose  VS, locate the folder on disk where the solution is stored, and rename the folder for the project from “SampleApp” to “SampleApp.PCL” (see the figure). Now you need to edit the solution file (SampleApp.sln) to tell VS that the project is now in a different folder. Open the solution file with a text editor, locate the line where the project file path is specified and change the directory to the appropriate one. In my case, this is the line I have to change:

Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SampleApp.PCL", "SampleApp.PCL\SampleApp.PCL.csproj", "{8DCACD8E-1DC0-4119-ADD8-41CDFE5E4D85}"

Now, we need to repeat the process for the rest of the projects.

The Windows Store project

Open the VS solution again, and add a Windows Store project to it:

New WinRT project

Notice we use SampleApp as the name of the project. Once created, rename it to “SampleApp.WinRT”, open the properties for the project and change the assembly name to “SampleApp.WinRT”. Notice how the default namespace remains as we want to “SampleApp”. Of course, all the XAML and .cs files use the appropriate namespace.

WinRT project

WinRT folderNow, close VS, and change the folder name as we did with the PCL project. Rename the “SampleApp” folder to “SampleApp.WinRT” (see figure). Open the solution file with a text editor, locate the line where the project file path for this new project is specified and change the directory to the appropriate one. In my case, this is the line I have to change:


Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SampleApp.PCL", "SampleApp.PCL\SampleApp.PCL.csproj", "{8DCACD8E-1DC0-4119-ADD8-41CDFE5E4D85}"

Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SampleApp.WinRT", "SampleApp.WinRT\SampleApp.WinRT.csproj", "{DD03BAB5-6174-46F7-A926-0E1FAEB3BD62}"

The Windows Phone project

One last time for the Windows Phone project. Open the VS solution again, and add a Windows Phone project to it:

New WP project

Notice again we use “SampleApp” as the name of the project. Once created, rename it to “SampleApp.WP”, open the properties for the project and change the assembly name to “SampleAppWP”. Notice how the default namespace remains as we want to “SampleApp”. Of course, as with the WinRT case, all the XAML and .cs files use the appropriate namespace.

WP project

Notice that in the case of the WP project, the assembly name has no dot between the name of the project and the suffix. This is because when I specify the dot the WP emulator starts behaving strange. Maybe is something about my particular installation, but I rather not put the dot for the WP assembly.

WP folderClose VS, and change the folder name as we did with the PCL and WinRT projects. Rename the “SampleApp” folder to “SampleApp.WP” (see figure). Open the solution file, locate the line where the project file path for this new project is specified and change the directory to the appropriate one. In my case, this is the line I have to change:


Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SampleApp.PCL", "SampleApp.PCL\SampleApp.PCL.csproj", "{8DCACD8E-1DC0-4119-ADD8-41CDFE5E4D85}"

Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SampleApp.WinRT", "SampleApp.WinRT\SampleApp.WinRT.csproj", "{DD03BAB5-6174-46F7-A926-0E1FAEB3BD62}"

Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SampleApp.WP", "SampleApp.WP\SampleApp.WP.csproj", "{CAA9B94D-607E-42D7-829C-0D0B366180CB}"

Wrapping up

We have our project structure almost done. There is one last step we need to do: add the PCL project reference to both the Windows RT and Windows Phone projects, but I guess you know how to do this already.

Finally, we end up with a solution/project structure as the one in the first image of this article and we are now ready to start developing. At this point you can add your solution to source control if you like.

In a next article I will describe how to prepare and configure our projects for MVVM.