Disclaimer: I’m learning about windows phone and keeping a log of that experience here. I fully expect that some of the things I do here will be wrong. Copy code at your own risk.
In an effort to keep the posts in this series shorter than a dissertation, I wont be covering every class and method in detail, just some of the stuff that I think is interesting. You can download the code here and feel free to contact me with any comments/questions.
This is the forth and final post in this series that I’ve been writing on how to create a simple windows phone application. You can read the other posts here:
Building a simple Windows Phone App – Part 1
Building a simple Windows Phone App – Part2
Building a simple Windows Phone App – Part 3
If you recall, in v1 we stored the data in XML and in v2 we stored the data in Sql Server. Here in v3 of the app we’re going to modify the TaskMaster application so that it stores data in the cloud and accesses it via a service call.
Some of the steps involved here are so simple I went back and forth as to whether I should even include them. I decided that I would for the sake of completeness….so without further delay, here are the steps I followed for this version of the app.
- Create a simple database table in sql server to store tasks
- Add a ASP.NET web project to our solution
- Create an entity data model for our newly created table
- Create and configure a WCF Data Service to expose the table as an OData endpoint
- Build the web site
- From the Phone project, Add a service reference to the service we created in step 3
- Configure the phone application to use the data service for retrieving, updating and inserting task data.
So, lets go thru each step
Step 1: Create simple db table
I have sql server 2008 installed on my dev box so I just fired it up, created a new database called TaskMaster and created a new table called tasks. Here is the table definition:
easy peasy.
Step 2: Add an ASP.NET web project to the solution
This one speaks for itself
Step 3: Create an entity data model for our newly created table
This model is created in the web project and I added my tasks table to it.
and the added table shown in the designer:
Step 4. Create and configure a WCF Data Service to expose the table as an OData endpoint
Again, this WCF Data Service is added to the web project
With the services added, we just need to configure it to expose the data that we want. Here is the code that lives in TaskService.svc.cs
namespace TaskMaster.Web { public class TaskService : DataService<TaskMasterEntities> { // This method is called only once to initialize service-wide policies. public static void InitializeService(DataServiceConfiguration config) { config.SetEntitySetAccessRule("*", EntitySetRights.All); config.DataServiceBehavior.MaxProtocolVersion = DataServiceProtocolVersion.V2; } // [QueryInterceptor("Tasks1")] // public Expression<Func<Task, bool>> OnQueryTasks() // { // Thread.Sleep(5000); // return j => true; // } } }
In the InitializeService Method I set the SetEntitySetAccessRule to * and EntitySetRights.All. This allows complete access to all tables in this model and allows all reads and writes. Probably not the setting that you want to have in your production environment but for our purposes in this demo it’ll do. Also notice that I have another method called OnQueryTasks. This guy is marked with the QueryInterceptor attribute. This will be useful when testing later on to simulate network delay. You can read all about QueryInterceptors on here on the MSDN site
Step 5. Build the Website
OK, so with that in place, I just do a quick build on the web site project, right click on the service and choose View in Browser. If you have it all configured correctly, you should see something like the following:
If you get something like that, your service is up and running correctly. You can see in the URL that this service is accessible on localhost via port 62830. I set this port in the project properties for the web project so that it wont change going forward and I wont have to continually updated the base service address in my phone application.
Step 6. From the Phone project, Add a service reference to the service we created in step 3
and finally….
Step 7: Configure the phone application to use the data service for retrieving, updating and inserting task data.
OK, so if you’ve been following along with this series, the original intent was to keep the data access implementation hidden behind an interface so I could just swap them out. Well, the more I got into this version of the project the more difficult that approach became so I decided to side step it in this version. Rather than creating a CloudTaskService that implemented ITaskService, I just added the code to the view model. Lets take a look at the ViewTaskViewModel first.
[DataContract] public class ViewTaskViewModel : ViewModelBase { //here is the base URI that Ill access for each call private readonly Uri _baseUri = new Uri("http://localhost:62830/TaskService.svc"); //I maintain one DataServiceCollection so that I can track changes private DataServiceCollection<Task> _tasks; //the rest I expose as ObservableCollections since they are just for display private ObservableCollection<Task> _completedTasks; private ObservableCollection<Task> _homeTasks; private ObservableCollection<Task> _personalTasks; private ObservableCollection<Task> _workTasks; private TaskMasterEntities context; private bool _isDataLoaded; [DataMember] public DataServiceCollection<Task> Tasks { get { return _tasks; } set { if (_tasks != value) { _tasks = value; NotifyPropertyChanged("Tasks"); } } } [DataMember] public ObservableCollection<Task> PersonalTasks { get { return _personalTasks; } set { if (_personalTasks != value) { _personalTasks = value; NotifyPropertyChanged("PersonalTasks"); } } } [DataMember] public ObservableCollection<Task> WorkTasks { get { return _workTasks; } set { if (_workTasks != value) { _workTasks = value; NotifyPropertyChanged("WorkTasks"); } } } [DataMember] public ObservableCollection<Task> HomeTasks { get { return _homeTasks; } set { if (_homeTasks != value) { _homeTasks = value; NotifyPropertyChanged("HomeTasks"); } } } [DataMember] public ObservableCollection<Task> CompletedTasks { get { return _completedTasks; } set { if (_completedTasks != value) { _completedTasks = value; NotifyPropertyChanged("CompletedTasks"); } } } public bool IsDataLoaded { get { return _isDataLoaded; } set { _isDataLoaded = value; NotifyPropertyChanged("IsDataLoaded"); } } public void GetTasks() { context = new TaskMasterEntities(_baseUri); DataServiceQuery<Task> taskQuery = context.Tasks1; _tasks = new DataServiceCollection<Task>(context); _tasks.LoadCompleted += TasksLoadCompleted; _tasks.LoadAsync(taskQuery); } private void TasksLoadCompleted(object sender, LoadCompletedEventArgs e) { if (e.Error != null) { throw new Exception("error loading tasks", e.Error); } HomeTasks = _tasks.Where(t => t.category == "home" && t.iscomplete == false).ToObservableCollection(); WorkTasks = _tasks.Where(t => t.category == "work" && t.iscomplete == false).ToObservableCollection(); PersonalTasks = _tasks.Where(t => t.category == "personal" && t.iscomplete == false).ToObservableCollection(); CompletedTasks = _tasks.Where(t => t.category == "complete").ToObservableCollection(); IsDataLoaded = true; } public void MarkTaskComplete(string taskId) { Task t = Tasks.FirstOrDefault(task => task.id == taskId); t.iscomplete = true; SaveChanges(); //Remove the task from its category OC RemoveTaskFromCategoryCollection(t); //remove it from the "all tasks" OC //Tasks.Remove(t); //add it to the completed collection CompletedTasks.Add(t); } public void SaveChanges() { context.BeginSaveChanges(result => Deployment.Current.Dispatcher.BeginInvoke(() => { DataServiceResponse response = context.EndSaveChanges(result); foreach (OperationResponse opres in response) { if (opres.Error != null) throw opres.Error; } }), null); } public void DeleteTask(string id) { //remove it from the "all tasks" list Task tsk = Tasks.FirstOrDefault(r => r.id == id); Tasks.Remove(tsk); //remove it from the completed tasks CompletedTasks.Remove(tsk); //send the http request to delete it SaveChanges(); } private void RemoveTaskFromCategoryCollection(Task task) { switch (task.category) { case "home": HomeTasks.Remove(task); break; case "personal": PersonalTasks.Remove(task); break; case "work": WorkTasks.Remove(task); break; case "completed": CompletedTasks.Remove(task); break; } } }
The main reason that I did this was that I wanted to use the DataServiceCollection<T> and its associated LoadAsync() method. Doing so provides me the ability to track all of the changes that are made and easily submit them back to the service. You can see this in action in the MarkTaskComplete and DeleteTask Methods above. From each of those methods I just call SaveChanges() which posts my changes that are tracked in the DataServiceCollection back to the server. It almost feels too easy.
A couple other things to take note of.
1. I added a PerformanceProgressBar to the ViewTasksView in the home pivot.
<toolkit:PerformanceProgressBar
IsIndeterminate="{Binding IsDataLoaded, Converter={StaticResource ReverseBoolConverter}}"
Visibility="{Binding IsDataLoaded, Converter={StaticResource boolToVisConverter}}" />
this allows us to provide a visual indicator to the user that “something” is happening while the network call is made to get the data. You can see in the xaml above that it references a couple of converters. These converters are there to make sure that the PerformanceProgressBar is only visible when appropriate.
As always, you can review all of this goodness in the code download.
And with that dear reader, I think Ill call an end to this series. Actually, I’m just getting sort of tired of looking at this ugly thing…![]()
Let me know you thoughts about this series and any requests you may have for future content.
Thanks for visiting.

{ 1 comment… read it below or add one }
Hi Ritz.
It was an great series. i was Searching for this for past two weeks. keep Going Ritz.
Am her to mention one thing, The code which you have given for the phone part, in that ” CloudTaskService.cs” is missing. Can u Please update that. it will be really help full for all those who looking at this.