Your Federal Quarterly Tax Payments are due April 15th Get Help Now >>

Windows Phone 7 And The Cloud by vRSYrP

VIEWS: 24 PAGES: 44

									Hands-On Lab
Windows Phone 7 & The Cloud
Lab version:    1.0.0
Last updated:   12/10/2011




                              Page | 1
CONTENTS

OVERVIEW ................................................................................................................................................... 3

EXERCISE 1: CONNECTING WINDOWS PHONE 7 WITH WCF DATA SERVICES ................................ 8
       Task 1 – Exposing an Entity Framework Model Using WCF Data Services ........................................... 8
       Task 2 – Creating WP7 Client Consuming WCF Data Services ............................................................ 14

SUMMARY .................................................................................................................................................. 44




                                                                                                                                                 Page | 2
Overview
This hands-on lab introduces you to the tools and the steps required to build a small Silverlight Windows
Phone 7 application that consumes a WCF Data Services from an application hosted in the Windows
Azure Platform.




WCF Data Services enables you to expose and consume data from different devices and infrastructures
leveraging the Open Data Protocol (OData). OData is a protocol that allows you to expose your data as
resources that are addressable by URIs. This enables you to use the semantics of representational state
transfer (REST) for accessing the exposed data. The standard HTTP verbs of GET, PUT, POST, and DELETE
are supported by it.




                                                                                                Page | 3
In this hands-on lab, you will be creating a simplified version of the MyTODO sample; first, you will
expose its entity model through a WCF Data Service, which will then be consumed by the Windows
Phone 7 Client. MyTODO is a Microsoft Developer & Platform Evangelism sample application built on
Windows Azure to manage and share simple lists (e.g. tasks to complete, favorite movies, books you
enjoyed, etc.).
If you want to explore a complete version of the myTODO sample application, you can download it from
http://code.msdn.microsoft.com/mytodo.




Objectives
In this hands-on lab, you will learn how to:
         Expose an Entity Framework model using WCF Data Services
         Create a Windows Phone 7 Client that consumes WCF Data Services
                                                                                                Page | 4
Prerequisites
The following is required to complete this hands-on lab:

       IIS 7 (with ASP.NET, WCF HTTP Activation)
       Microsoft .NET Framework 4.0

       Microsoft Visual Studio 2010
       Windows Azure Tools for Microsoft Visual Studio 1.4
       Windows Phone 7 SDK



Setup
For convenience, much of the code used in this hands-on lab is available as Visual Studio code snippets.
To check the prerequisites of the lab and install the code snippets:

 Note: Make sure you have checked all the dependencies for this lab before running the setup.



    1. In order to allow the solution to connect with the database using SQL Server credentials, open
       SQL Server Management Studio and connect to the local SQL Server (i.e. .\sqlexpress).
    2. Right click the server node and select Properties.
    3. Select Security and make sure SQL Server and Windows Authentication mode is selected.




                                                                                                 Page | 5
   Figure 1
   SQL Server Properties - Security


4. Click OK.
5. Restart the SQL Server instance to allow the previous configuration change to take effect.




                                                                                           Page | 6
        Figure 2
        Restart SQL Server


     6. Open a Windows Explorer window and browse to the lab’s Source folder.
     7. Double-click the Setup.cmd file in this folder to launch the setup process that will configure
        your environment and install the Visual Studio code snippets for this lab.
     8. If the User Account Control dialog is shown, confirm the action to proceed.



Using the Code Snippets
Throughout the lab document, you will be instructed to insert code blocks. For your convenience, most
of that code is provided as Visual Studio Code Snippets, which you can use from within Visual Studio
2010 to avoid having to add it manually.
If you are not familiar with the Visual Studio Code Snippets, and want to learn how to use them, you can
refer to the Setup.docx document in the Assets folder of the training kit, which contains a section
describing how to use them.


Exercises
This hands-on lab includes the following exercises:
       1. Using the CDN to Deliver Static Content

                                                                                                 Page | 7
Estimated time to complete this lab: 30 minutes.



 Note: When you first start Visual Studio, you must select one of the predefined settings collections.
 Every predefined collection is designed to match a particular development style and determines
 window layouts, editor behavior, IntelliSense code snippets, and dialog box options. The procedures in
 this lab describe the actions necessary to accomplish a given task in Visual Studio when using the
 General Development Settings collection. If you choose a different settings collection for your
 development environment, there may be differences in these procedures that you need to take into
 account.




Exercise 1: Connecting Windows Phone 7
with WCF Data Services
In this exercise, you will start with a simplified version of MyTODO and go through the steps of adding a
WCF Data Service to expose its entity model, and then create a Windows Phone 7 client application that
consumes it.


Task 1 – Exposing an Entity Framework Model Using WCF Data Services
In this task, you will create a WCF Data Service to expose MyTODO’s entity model and host it in the
Cloud.
       1. Open Visual Studio 2010 as an administrator. Go to the File | Open | Project menu and
          select Begin.sln located in the Source\Ex01-Connectivity\Begin folder of the lab.
       2. Press F5 to run the application. Try the different options: creating a task list, adding tasks,
          marking a task as completed.
       3. Close the browser and go back to Visual Studio.
       4. Add a WCF Data Service class named MyTodoDataService to the MyTodo.Web project.




                                                                                                   Page | 8
Figure 3
Adding a WCF Data Service


5. In the newly created MyTodoDataService class, insert the following namespace directives
   following the existing directives at the top of the file.
 (Code Snippet – Windows Phone 7 and The Cloud - Ex01 UsingDataService - C#)
C#
using   System.Diagnostics;
using   System.Linq.Expressions;
using   System.Security.Principal;
using   System.ServiceModel;
using   Microsoft.Samples.MyTodo.Web.Models;
using   System.Web.Security;
using   System.ServiceModel.Activation;



6. In the class definition, replace the placeholder labeled /* TODO: put your data source class
   name here */ with the name MyTodoEntities.
7. Add the following attributes to MyTodoDataService class.
 (Code Snippet – Windows Phone 7 and The Cloud - Ex01 Attributes - C#)
C#
[ServiceBehavior(IncludeExceptionDetailInFaults = true)]
[AspNetCompatibilityRequirements(RequirementsMode =
AspNetCompatibilityRequirementsMode.Required)]

                                                                                        Page | 9
8. Locate the InitializeService method, and replace its code with the following, this will expose
   the TaskLists and Task entities to everyone consuming the service, later on this task, we will
   add the logic to only return the public Tasks.
 (Code Snippet – Windows Phone 7 and The Cloud - Ex01 InitializeService - C#)
C#
config.SetEntitySetAccessRule("TaskLists", EntitySetRights.All);
config.SetEntitySetAccessRule("Tasks", EntitySetRights.All);
config.SetServiceOperationAccessRule("*", ServiceOperationRights.All);
config.UseVerboseErrors = true;




Figure 4
WCF Data Service Definition


9. Add a UserIdentity property, which will be used to identify the user that is consuming the
   service and to validate if he has access to the resources, or not.
 (Code Snippet – Windows Phone 7 and The Cloud - Ex01 UserIdentity - C#)
C#
private IIdentity UserIdentity
{
  get
  {
    string ticketValue = null;
    var cookie =
HttpContext.Current.Request.Cookies[FormsAuthentication.FormsCookieName];
    if (cookie != null)
    {
      // from cookie
      ticketValue = cookie.Value;
    }
    else if (HttpContext.Current.Request.Headers["AuthToken"] != null)
                                                                                        Page | 10
        {
            // from http header
          ticketValue = HttpContext.Current.Request.Headers["AuthToken"];
        }
        if (!string.IsNullOrEmpty(ticketValue))
        {
          try
          {
            var ticket = FormsAuthentication.Decrypt(ticketValue);
            if (ticket != null)
            {
              return new FormsIdentity(ticket);
            }
          }
          catch
          {
          }
        }

        return null;
    }
}



10. Add the code below to the service definition; these methods contain the logic to hide those
    entities which are not public or owned by the user consuming the service.
 (Code Snippet – Windows Phone 7 and The Cloud - Ex01 ListViewEventHandlers - C#)
C#
[WebGet]
public IQueryable<TaskList> GetPublicLists()
{
  return this.CurrentDataSource.TaskLists
    .Where(o => o.IsPublic == 1)
    .OrderBy(o => o.Name);
}

[QueryInterceptor("Tasks")]
public Expression<Func<Task, bool>> QueryTasks()
{
  if (this.UserIdentity == null)
  {
    return c => c.TaskList.IsPublic == 1;
  }
  else
  {


                                                                                       Page | 11
    return c => c.TaskList.UserName.Equals(this.UserIdentity.Name,
StringComparison.OrdinalIgnoreCase);
  }
}

[QueryInterceptor("TaskLists")]
public Expression<Func<TaskList, bool>> QueryTaskLists()
{
  if (this.UserIdentity == null)
  {
    return c => c.IsPublic == 1;
  }
  else
  {
    return c => c.UserName.Equals(this.UserIdentity.Name,
StringComparison.OrdinalIgnoreCase);
  }
}



11. In order to verify the correct implementation of the WCF Data service, press F5 to run the
    application.




                                                                                       Page | 12
Figure 5
Running Application


12. In Internet Explorer, append /MyTodoDataService.svc to the URL, and press ENTER. The
    address should look like http://127.0.0.1:{port}/MyTodoDataService.svc.




                                                                                   Page | 13
       Figure 6
       Running Application



Task 2 – Creating WP7 Client Consuming WCF Data Services
In this task, you will go through the steps of creating a Windows Phone 7 client to consume the WCF
Data Service created in the previous task.
       1. Add New Project using the Silverlight for Windows Phone – Windows Phone Application
          template, named MyTodo.Phone.Online
       2. Add reference to the OData Client Library for WP7 (System.Data.Services.Client) located in
          the folder Assets\libs.
       3. Remove the file MainPage.xaml located into the root of the project since you will not need
          it.
       4. Add New Folder named Model, and create a new class named TasksList inside it.
       5. Replace the class definition with the following.
        (Code Snippet – Windows Phone 7 and The Cloud - Ex01 TaskList - C#)
       C#
       namespace MyTodo.Phone.Online.Models
       {
         using System;
         using System.Collections.ObjectModel;
         using System.Data.Services.Common;

                                                                                              Page | 14
    [DataServiceEntity]
    [DataServiceKey("Id")]
    public class TaskList
    {
      public TaskList()
      {
        this.Tasks = new Collection<Task>();
      }

        public Guid Id { get; set; }

        public string UserName { get; set; }

        public string Name { get; set; }

        public byte IsPublic { get; set; }

        public Collection<Task> Tasks { get; set; }
    }
}



6. Inside the Models folder, add another class, named Task, and replace its implementation
   with the following.
 (Code Snippet – Windows Phone 7 and The Cloud - Ex01 Task - C#)
C#
namespace MyTodo.Phone.Online.Models
{
  using System;
  using System.Data.Services.Common;

    [DataServiceEntity]
    [DataServiceKey("Id")]
    public class Task
    {
      public Guid Id { get; set; }

        public string UserName { get; set; }

        public TaskList TaskList { get; set; }

        public string Name { get; set; }

        public string Description { get; set; }

        public int Status { get; set; }

                                                                                    Page | 15
    public DateTime StartDate { get; set; }

    public DateTime DueDate { get; set; }

    public DateTime TimestampUpdate { get; set; }
    }
}



7. Add a new folder named Services in the Project’s root folder.
8. Inside it, add an interface named ITodoService, and replace its definition with the following
 (Code Snippet – Windows Phone 7 and The Cloud - Ex01 ITodoService - C#)
C#
using System;
using System.Collections.Generic;
using MyTodo.Phone.Online.Models;

namespace MyTodo.Phone.Online.Services
{
  public interface ITodoService
  {
    event EventHandler<ServiceExceptionArgs> ServiceException;

        void   GetLists(Action<IEnumerable<TaskList>> callback);
        void   GetListAndTasks(Guid listId, Action<TaskList> callback);
        void   AddList(TaskList list, Action<TaskList> callback);
        void   AddTask(TaskList list, Task task, Action<Task> callback);
        void   DeleteTask(Task task, Action<Task> callback);
        void   UpdateTask(Task task, Action<Task> callback);
    }

    public class ServiceExceptionArgs : EventArgs
    {
      public Exception Exception { get; set; }
    }
}




9. Add a class named OnlineTodoService in the Services folder, which will implement the
   ITodoService interface and contain the logic required to consume the OData service.
10. Add the following namespace directives below the ones added by the class template.
 (Code Snippet – Windows Phone 7 and The Cloud - Ex01 OnlineTodoServiceNamespaces - C#)
C#
                                                                                        Page | 16
   ...
   using   System.Collections.Generic;
   using   System.Data.Services.Client;
   using   System.Diagnostics;
   using   System.Linq;
   using   MyTodo.Phone.Online.Infrastructure;
   using   MyTodo.Phone.Online.Models;




11. Implement the ITodoService interface, to do this, type : ITodoService behind the
    OnlineTodoService class name, click on the contextual menu, and select Implement Interface
    ITodoService.




   Figure 7
   Implement ITodoService Interface


   12. Add the following code to the beginning of the class definition, this code contains the logic to
       create the Context that will be used to consume the WCF Data Service, adding the
       AuthenticationToken to each requests.
    (Code Snippet – Windows Phone 7 and The Cloud - Ex01 OnlineTodoServiceDeclarations- C#)
   C#
      public class OnlineTodoService : ITodoService
      {
        private readonly Uri serviceUri;

        public OnlineTodoService(Uri serviceUri)
        {
          this.serviceUri = serviceUri;
        }

        private DataServiceContext GetContext()
        {
          var dataService = new DataServiceContext(serviceUri);
          dataService.MergeOption = MergeOption.OverwriteChanges;
          dataService.SendingRequest += (sender, args) =>
          {
            if (string.IsNullOrEmpty(App.AuthenticationToken))
                                                                                             Page | 17
           {
          throw new ArgumentNullException("AuthenticationToken", "The
AuthenticationToken is not set.");
        }

            args.RequestHeaders["AuthToken"] = App.AuthenticationToken;
          };

          return dataService;
      }
...




When using the implement interface option, all methods where added but with no actual
implementation.
13. Replace the auto-generated code of GetLists method with the code required to invoke the
    WCF Data Service.
 (Code Snippet – Windows Phone 7 and The Cloud - Ex01 OnlineTodoServiceGetLists - C#)
C#
      public event EventHandler<ServiceExceptionArgs> ServiceException;

...
    public void GetLists(Action<IEnumerable<TaskList>> callback)
    {
      var requestUri = new Uri(string.Format("{0}TaskLists?$orderby='Name
asc'&$expand=Tasks", serviceUri), UriKind.Absolute);
      var ctx = this.GetContext();

      ctx.BeginExecute<TaskList>(
        requestUri,
        delegate(IAsyncResult asyncResult)
        {
          try
          {
            var result = ctx.EndExecute<TaskList>(asyncResult).ToArray();
            Deployment.Current.Dispatcher.BeginInvoke(() => {
callback(result); });
          }
          catch (Exception ex)
          {
            Debug.WriteLine(ex.ToString());
            Deployment.Current.Dispatcher.BeginInvoke(() =>
            {
              if (this.ServiceException != null)
              {
                                                                                    Page | 18
                 this.ServiceException(this, new ServiceExceptionArgs {
Exception = ex });
               }
             });
           }
        },
        null);
    }
...




 Note: As you can see, the code generates the URI that asks for all the TaskLists to the service,
 ordered by Name (ascendant) and expanding its Tasks. For more information on the URI
 conventions used for OData services you can visit
 http://www.odata.org/developers/protocols/uri-conventions



14. Replace the auto-generated code of AddList method with the code required to invoke the
    WCF Data Service.
 (Code Snippet – Windows Phone 7 and The Cloud - Ex01 OnlineTodoServiceAddList - C#)
C#
    public void AddList(TaskList list, Action<TaskList> callback)
    {
      list.UserName = string.Empty;
      var dataService = this.GetContext();
      dataService.AddObject("TaskLists", list);
      dataService.BeginSaveChanges(
        delegate(IAsyncResult asyncResult)
        {
          try
          {
            var response = dataService.EndSaveChanges(asyncResult).First();
            Deployment.Current.Dispatcher.BeginInvoke(() => {
callback(asyncResult.AsyncState as TaskList); });
          }
          catch (Exception ex)
          {
            Debug.WriteLine(ex.ToString());
            Deployment.Current.Dispatcher.BeginInvoke(() =>
            {
              if (this.ServiceException != null)
              {
                this.ServiceException(this, new ServiceExceptionArgs {
Exception = ex });

                                                                                         Page | 19
                    }
                  });
              }
           },
           list
         );
     }



15. Add the IAuthentication and the Authentication files located in the Assets\Services folder
    into the Services folder of your WP7 project. These files will handle the authentication logic,
    which is not in the scope of this exercise.
16. Add a service reference to the Authentication service. To do this, right-click on the WP7
    project and select Add service reference. Click on Discover, and wait until the
    AuthenticationService.svc appears. Select it and make sure that the Address is
    http://localhost:81/AuthenticationService.svc.
17. Type Services as its namespace and click OK to add the reference.




                                                                                          Page | 20
Figure 8
Add Authentication service reference


18. The next step is to add the ServiceFactory, which will contain the logic for building the
    services required by the application. To do this, add a new folder named Infrastructure to
    the root of the WP7 project.
19. Inside it, add a new class named ServiceFactory
20. Replace the class definition with the following
 (Code Snippet – Windows Phone 7 and The Cloud - Ex01 ServiceFactory - C#)
C#
using   System;
using   System.Diagnostics;
using   System.Windows;
using   MyTodo.Phone.Online.Services;

namespace MyTodo.Phone.Online.Infrastructure
{
  public static class ServiceFactory
  {
    private static IAuthentication authentication;
    private static ITodoService todoService;

    public static Uri GetApplicationUri()
    {
      return new
Uri(Application.Current.Resources["ApplicationUrlString"].ToString(),
UriKind.Absolute);
    }

    public static Uri GetTodoServiceUri()
    {
      return new Uri(GetApplicationUri() + "MyTodoDataService.svc/",
UriKind.Absolute);
    }


    public static IAuthentication GetAuthentication()
    {
      if (authentication == null)
      {
        authentication = new Authentication(GetApplicationUri() +
"AuthenticationService.svc");

          authentication.AuthenticationException += (sender, args) =>
                                                                                       Page | 21
                {
                     if (args.WrongCredentials)
                     {
                       App.UserName = null;
                       App.AuthenticationToken = null;
                     }
                };


                authentication.AuthenticationSuccess += (sender, args) =>
                {
                   App.UserName = args.UserName;
                   App.AuthenticationToken = args.AuthenticationToken;
                };
            }

            return authentication;
        }


    public static ITodoService GetTodoService()
    {
      if (todoService == null)
      {
        todoService = new OnlineTodoService(GetTodoServiceUri());
        todoService.ServiceException += (sender, e) =>
        {
           Debug.WriteLine(e.Exception.ToString());
           MessageBox.Show(e.Exception.ToString(), "Service Error",
MessageBoxButton.OK);
        };
      }

            return todoService;
        }

    }
}




Up to this point, you have added all the logic required to handle the Authentication and
Interaction with the WCF Data Service. Your Project should look as the following:




                                                                                       Page | 22
Figure 9
Windows Phone 7 Project


Now, you will proceed by adding the Views, ViewModels and all resources required by them in
order to display the TaskLists and their tasks.
21. Open the App.xaml file located in the root of the project, and add the following code into
    the Application:Resources tag.
 (Code Snippet – Windows Phone 7 and The Cloud - Ex01 ApplicationResources)
XAML
...
  <!--Application Resources-->
  <Application.Resources>
    <system:String x:Key="ApplicationNameString">MY TODO</system:String>
    <system:String
x:Key="ApplicationUrlString">http://localhost:81/</system:String>
  </Application.Resources>
...



22. Also, add the System namespace into the Application tag.
 (Code Snippet – Windows Phone 7 and The Cloud - Ex01 SystemReference)
XAML
  <Application
  x:Class="Microsoft.Samples.MyTodo.Phone.App"
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  xmlns:system="clr-namespace:System;assembly=mscorlib"

                                                                                       Page | 23
  xmlns:phone="clr-
namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
  xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone">




23. Press F7 to switch to the App.xaml code behind, and add the following fields at the beginning
    of the class. These fields will be used to store the user context of the application.
 (Code Snippet – Windows Phone 7 and The Cloud - Ex01 UserContextInformation)
C#
  public partial class App : Application
  {

      //User Context
      public static string UserName;
      public static string AuthenticationToken;
...




24. Create a new folder named Views, in your WP7 Project. Inside it, add a new Windows Phone
    Portrait Page named ListsView.




                                                                                       Page | 24
Figure 10
Add new Windows Phone Portrait Page


25. If not already opened, open the ListView.xaml page you have just created and add the
    converters namespace into the phone:PhoneApplicationPage tag. You will add all the
    required converters later on this task.
 (Code Snippet – Windows Phone 7 and The Cloud - Ex01 ConvertersNamespace)
XAML
  xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
  xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
  xmlns:converters="clr-namespace:MyTodo.Phone.Online.Converters"
  FontFamily="{StaticResource PhoneFontFamilyNormal}"
  FontSize="{StaticResource PhoneFontSizeNormal}"
  Foreground="{StaticResource PhoneForegroundBrush}"
  SupportedOrientations="PortraitOrLandscape" Orientation="Portrait"
  mc:Ignorable="d" d:DesignHeight="768" d:DesignWidth="480"
  shell:SystemTray.IsVisible="True">
...




26. Replace the code in the phone:PhoneApplicationPage tag with the following.
 (Code Snippet – Windows Phone 7 and The Cloud - Ex01 PhoneApplicationPage)
XAML
shell:SystemTray.IsVisible="True">

  <phone:PhoneApplicationPage.Resources>
    <converters:VisibilityConverter x:Key="VisibilityConverter" />
    <Storyboard x:Name="PageTransitionReset">
      <DoubleAnimationUsingKeyFrames Storyboard.TargetName="LayoutRoot"
Storyboard.TargetProperty="(UIElement.Projection).(PlaneProjection.RotationY)"
>
        <EasingDoubleKeyFrame KeyTime="00:00:00" Value="90"/>
      </DoubleAnimationUsingKeyFrames>
      <DoubleAnimationUsingKeyFrames Storyboard.TargetName="LayoutRoot"
Storyboard.TargetProperty="(UIElement.Projection).(PlaneProjection.CenterOfRot
ationX)">
        <EasingDoubleKeyFrame KeyTime="00:00:00" Value="0"/>
      </DoubleAnimationUsingKeyFrames>
    </Storyboard>
    <Storyboard x:Name="PageTransitionIn">




                                                                                     Page | 25
      <DoubleAnimationUsingKeyFrames Storyboard.TargetName="LayoutRoot"
Storyboard.TargetProperty="(UIElement.Projection).(PlaneProjection.RotationY)"
>
        <EasingDoubleKeyFrame KeyTime="00:00:00" Value="90"/>
        <EasingDoubleKeyFrame KeyTime="00:00:00.3" Value="0">
          <EasingDoubleKeyFrame.EasingFunction>
            <CircleEase EasingMode="EaseIn"/>
          </EasingDoubleKeyFrame.EasingFunction>
        </EasingDoubleKeyFrame>
      </DoubleAnimationUsingKeyFrames>
      <DoubleAnimationUsingKeyFrames Storyboard.TargetName="LayoutRoot"
Storyboard.TargetProperty="(UIElement.Projection).(PlaneProjection.CenterOfRot
ationX)">
        <EasingDoubleKeyFrame KeyTime="00:00:00" Value="0"/>
        <EasingDoubleKeyFrame KeyTime="00:00:00.3" Value="0">
          <EasingDoubleKeyFrame.EasingFunction>
            <CircleEase EasingMode="EaseIn"/>
          </EasingDoubleKeyFrame.EasingFunction>
        </EasingDoubleKeyFrame>
      </DoubleAnimationUsingKeyFrames>
    </Storyboard>
    <Storyboard x:Name="PageTransitionOut">
      <DoubleAnimationUsingKeyFrames Storyboard.TargetName="LayoutRoot"
Storyboard.TargetProperty="(UIElement.Projection).(PlaneProjection.RotationY)"
>
        <EasingDoubleKeyFrame KeyTime="00:00:00" Value="0"/>
        <EasingDoubleKeyFrame KeyTime="00:00:00.3" Value="90">
          <EasingDoubleKeyFrame.EasingFunction>
            <CircleEase EasingMode="EaseIn"/>
          </EasingDoubleKeyFrame.EasingFunction>
        </EasingDoubleKeyFrame>
      </DoubleAnimationUsingKeyFrames>
      <DoubleAnimationUsingKeyFrames Storyboard.TargetName="LayoutRoot"
Storyboard.TargetProperty="(UIElement.Projection).(PlaneProjection.CenterOfRot
ationX)">
        <EasingDoubleKeyFrame KeyTime="00:00:00" Value="0"/>
        <EasingDoubleKeyFrame KeyTime="00:00:00.3" Value="0">
          <EasingDoubleKeyFrame.EasingFunction>
            <CircleEase EasingMode="EaseIn"/>
          </EasingDoubleKeyFrame.EasingFunction>
        </EasingDoubleKeyFrame>
      </DoubleAnimationUsingKeyFrames>
    </Storyboard>
  </phone:PhoneApplicationPage.Resources>
  <Grid x:Name="LayoutRoot" Background="Transparent">
    <Grid.Projection>
      <PlaneProjection />
    </Grid.Projection>
    <Grid.RowDefinitions>
                                                                      Page | 26
      <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="24,24,0,12">
      <TextBlock x:Name="ApplicationTitle" Text="{StaticResource
ApplicationNameString}" Style="{StaticResource PhoneTextNormalStyle}"
Margin="0" />
      <TextBlock x:Name="PageTitle" Text="my lists" Margin="-3,-8,0,0"
Style="{StaticResource PhoneTextTitle1Style}"/>
    </StackPanel>

    <Grid Grid.Row="1">
      <Grid.RowDefinitions>
        <RowDefinition Height="*"/>
        <RowDefinition Height="Auto"/>
      </Grid.RowDefinitions>
      <Border Margin="12,24,0,12">
        <Grid Visibility="{Binding IsBusy, ConverterParameter=False,
Converter={StaticResource VisibilityConverter}}">
          <TextBlock Visibility="{Binding HasItems, ConverterParameter=False,
Converter={StaticResource VisibilityConverter}}" Text="You don't have any
lists." Style="{StaticResource PhoneTextSubtleStyle}"/>
          <ListBox Visibility="{Binding HasItems, Converter={StaticResource
VisibilityConverter}}" x:Name="ItemsListBox" ItemsSource="{Binding Items}">
            <ListBox.ItemTemplate>
              <DataTemplate>
                <StackPanel Orientation="Horizontal" Margin="0,0,0,30">
                  <Image x:Name="ItemImage"
Source="/Microsoft.Samples.MyTodo.Phone;component/Resources/Images/ArrowImg.pn
g" Height="43" Width="43" VerticalAlignment="Top" Margin="10,0,20,0"/>
                  <StackPanel>
                    <TextBlock x:Name="ItemText" Text="{Binding Name}"
Margin="-2,-13,0,0" Style="{StaticResource PhoneTextExtraLargeStyle}"
TextWrapping="Wrap" Width="395" />
                    <StackPanel Orientation="Horizontal" Margin="0,-6,0,3">
                      <Image
Source="/Microsoft.Samples.MyTodo.Phone;component/Resources/Images/PublicImg.p
ng" Height="30" Width="30" Visibility="{Binding IsPublic,
Converter={StaticResource VisibilityConverter}, ConverterParameter=1}" />
                      <TextBlock Text="Public" Style="{StaticResource
PhoneTextSubtleStyle}" Margin="0" Visibility="{Binding IsPublic,
Converter={StaticResource VisibilityConverter}, ConverterParameter=1}" />

                      <Image
Source="/Microsoft.Samples.MyTodo.Phone;component/Resources/Images/PublicImg.p
ng" Height="30" Width="30" Opacity="0.35" Visibility="{Binding IsPublic,
Converter={StaticResource VisibilityConverter},ConverterParameter=0}" />
                                                                      Page | 27
                      <TextBlock Text="Private" Style="{StaticResource
PhoneTextSubtleStyle}" Margin="0" Visibility="{Binding IsPublic,
Converter={StaticResource VisibilityConverter},ConverterParameter=0}" />

                       <TextBlock Text=" | Tasks: " Style="{StaticResource
PhoneTextSubtleStyle}" Margin="0" />
                       <TextBlock Text="{Binding Tasks.Count}"
Style="{StaticResource PhoneTextSubtleStyle}" Margin="0" />
                     </StackPanel>
                  </StackPanel>
                </StackPanel>
              </DataTemplate>
            </ListBox.ItemTemplate>
          </ListBox>
        </Grid>
      </Border>

      <TextBlock Margin="12,24,0,12" Visibility="{Binding IsBusy,
Converter={StaticResource VisibilityConverter}}" Text="Loading..."
Style="{StaticResource PhoneTextSubtleStyle}"/>

      <Border Grid.Row="1">
        <Grid>
          <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*" />
            <ColumnDefinition Width="Auto" />
          </Grid.ColumnDefinitions>
          <TextBox x:Name="NewListTextBox" Text="{Binding NewListName,
Mode=TwoWay}" HorizontalAlignment="Stretch" VerticalAlignment="Center"
KeyUp="NewListTextBox_KeyUp" GotFocus="NewListTextBox_GotFocus"
LostFocus="NewListTextBox_LostFocus" />
          <Button x:Name="NewListButton" Content="+" Grid.Column="1"
Click="NewListButton_Click" VerticalAlignment="Stretch" />
        </Grid>
      </Border>
    </Grid>
  </Grid>

</phone:PhoneApplicationPage>




  Note: The code you have just added contains the UI structure of the page and its bindings to
  the ViewModel which you will add later. The page will simply display a list of tasks.



27. Press F7 to switch to the ListsView’s code behind.

                                                                                       Page | 28
28. Replace the default namespace definition with the ones shown below.
 (Code Snippet – Windows Phone 7 and The Cloud - Ex01 ListViewNamespaces - C#)
C#
  using   System;
  using   System.Windows;
  using   System.Windows.Controls;
  using   System.Windows.Input;
  using   Microsoft.Phone.Controls;
  using   MyTodo.Phone.Online.ViewModels;
  using   MyTodo.Phone.Online.Models;




29. Add the following code into the constructor, below the call to the InitializeComponent
    method, to create the appropriate ViewModel.

  Note: Do not worry if your solution does not build at this point, you will add the missing
  ViewModel later on this task.



 (Code Snippet – Windows Phone 7 and The Cloud - Ex01 ListViewInitializeComponent - C#)
C#
     public ListsView()
     {
       InitializeComponent();

       this.PageTransitionReset.Begin();
       this.Loaded += (sender, args) =>
       {
         this.PageTransitionIn.Begin();
         this.ViewModel.Reload();
       };

      this.PageTransitionOut.Completed += (sender, args) =>
      {
        if (this.selectedItem != null)
        {
          // navigate
          NavigationService.Navigate(new Uri("/Views/TasksView.xaml?listId=" +
this.selectedItem.Id, UriKind.Relative));
        }
        else
        {
          // back
          NavigationService.GoBack();

                                                                                         Page | 29
            }
          };

          this.ViewModel = new ListsViewModel();
          this.ViewModel.IsBusy = true;
      }




30. Add the selectedItem field above the class constructor.
(Code Snippet – Windows Phone 7 and The Cloud - Ex01 TaskListSelectedItem - C#)
C#
  public partial class ListsView : PhoneApplicationPage
  {
    private TaskList selectedItem;

      public ListsView()
      {
...



31. Add the following code below the constructor, this code will handle the user interaction in
    the page.
 (Code Snippet – Windows Phone 7 and The Cloud - Ex01 ListsViewEventHandlers - C#)
C#
          this.ViewModel = new ListsViewModel();
          this.ViewModel.IsBusy = true;
      }

      public ListsViewModel ViewModel
      {
        get { return this.DataContext as ListsViewModel; }
        set { this.DataContext = value; }
      }

    private void ItemsListBox_MouseLeftButtonUp(object sender,
MouseButtonEventArgs e)
    {
      this.selectedItem = ((ListBox)sender).SelectedItem as TaskList;
      if (this.selectedItem != null)
      {
        this.PageTransitionOut.Begin();
      }
    }


                                                                                        Page | 30
     private void NewListButton_Click(object sender, RoutedEventArgs e)
     {
       this.Focus();
       this.ViewModel.SaveNewList();
     }

     private void NewListTextBox_KeyUp(object sender, KeyEventArgs e)
     {
       if (e.Key == Key.Enter)
       {
         this.NewListButton.Focus();
         this.ViewModel.SaveNewList();
       }
     }

     private void NewListTextBox_GotFocus(object sender, RoutedEventArgs e)
     {
       this.ViewModel.OnFocusNewListField();
     }

     private void NewListTextBox_LostFocus(object sender, RoutedEventArgs e)
     {
       this.ViewModel.OnBlurNewListField();
     }

    protected override void
OnBackKeyPress(System.ComponentModel.CancelEventArgs e)
    {
      e.Cancel = true;
      this.selectedItem = null;
      this.PageTransitionOut.Begin();
    }
  }




32. Inside the Windows Phone 7 Project, add a new folder named ViewModels, inside it, create
    a new class named BaseViewModel and replace its implementation with the following code
 (Code Snippet – Windows Phone 7 and The Cloud - Ex01 BaseViewModel - C#)
C#
using System.ComponentModel;
using System.Windows;
namespace MyTodo.Phone.Online.ViewModels
{
  public abstract class BaseViewModel : INotifyPropertyChanged
  {
    public event PropertyChangedEventHandler PropertyChanged;
                                                                                   Page | 31
        protected virtual void NotifyPropertyChanged(string propertyName)
        {
          if (this.PropertyChanged != null)
          {

this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
      }
    }

        public bool IsDesignTime
        {
          get { return DesignerProperties.IsInDesignTool; }
        }
    }
}



    Note: The BaseViewModel is an abstraction that handles the changes in the properties of the
    ViewModels, by implementing the INotifyPropertyChanged, these changes impact on the
    bound elements in the UI.



33. Add a new class ListsViewModel and replace its implementation with the following.
 (Code Snippet – Windows Phone 7 and The Cloud - Ex01 ListViewmodel - C#)
C#
    using   System;
    using   System.Linq;
    using   System.Collections.Generic;
    using   System.Collections.ObjectModel;
    using   MyTodo.Phone.Online.Services;
    using   MyTodo.Phone.Online.Infrastructure;
    using   MyTodo.Phone.Online.Models;
    using   MyTodo.Phone.Online.ViewModels;

    public class ListsViewModel : BaseViewModel
    {

        private const string NewListPlaceHolderName = "New List Name";

        private   ITodoService todoService;
        private   ObservableCollection<TaskList> items;
        private   bool isBusy;
        private   string newListName;

        public ListsViewModel() :
                                                                                       Page | 32
    this(ServiceFactory.GetTodoService())
{
}

public ListsViewModel(ITodoService todoService)
{
  this.Items = new ObservableCollection<TaskList>();
  this.IsBusy = true;
  this.NewListName = NewListPlaceHolderName;
  this.todoService = todoService;
}

public ObservableCollection<TaskList> Items
{
  get
  {
    return this.items;
  }

    set
    {
      if (this.items != value)
      {
        this.items = value;
        this.NotifyPropertyChanged("Items");
        this.NotifyPropertyChanged("HasItems");
      }
    }
}

public bool IsBusy
{
  get
  {
    return this.isBusy;
  }

    set
    {
      if (this.isBusy != value)
      {
        this.isBusy = value;
        this.NotifyPropertyChanged("IsBusy");
        this.NotifyPropertyChanged("HasItems");
      }
    }
}

public bool HasItems
                                                       Page | 33
    {
        get
        {
          return this.Items != null && this.Items.Count() > 0;
        }
    }

    public string NewListName
    {
      get
      {
        return this.newListName;
      }

        set
        {
          if (this.newListName != value)
          {
            this.newListName = value;
            this.NotifyPropertyChanged("NewListName");
          }
        }
    }

    public void Reload()
    {
      this.IsBusy = true;
      this.todoService.GetLists(delegate(IEnumerable<TaskList> lists)
      {
        lock (this.Items)
        {
          this.Items.Clear();
          foreach (var list in lists.OrderBy(o => o.Name))
this.Items.Add(list);
        this.IsBusy = false;
        }
      });
    }

    public void SaveNewList()
    {
      if (!string.IsNullOrEmpty(this.NewListName) && this.NewListName !=
NewListPlaceHolderName)
      {
        var taskList = new TaskList { IsPublic = 0, Name = this.NewListName };
        this.IsBusy = true;
        this.NewListName = NewListPlaceHolderName;

         this.todoService.AddList(
                                                                        Page | 34
               taskList,
               delegate(TaskList savedList)
               {
                 this.Reload();
               });
           }
       }

       public void OnFocusNewListField()
       {
         if (this.NewListName == NewListPlaceHolderName)
         {
           this.NewListName = string.Empty;
         }
       }

       public void OnBlurNewListField()
       {
         if (string.IsNullOrEmpty(this.NewListName))
         {
           this.NewListName = NewListPlaceHolderName;
         }
       }
   }




  Note: As you can see, the ListsViewModel simply contains the Lists, which are populated by
  the Reload method, and a SaveNewList method that contains the logic for creating a new list.
  Both methods leverage the OnlineTodoService to consume the WCF Data Service exposed by
  the MVC Application.



34. In order to authenticate the user, you will add the Login Page and View Model. Since this
    functionality is out of the scope of this exercise, you will simply grab them from the Assets
    Folder.
           a. First, add both LoginView.xaml and the LoginView.xaml.cs files located in
              Assets\Views into the Views folder located in the WP7 Project.
           b. Next, add the LoginViewModel.cs file located in the Assets\ViewModels into the
              ViewModels folder located in the WP7 Project.
35. To add the converters used by the Views, add a new folder named Converters.
36. Add all the converters located in Assets\Converters folder inside the Converters folder you
    have just created.

                                                                                          Page | 35
37. Finally, open the WMAppManifest.xml located in the Properties folder, and set the
    DefaultTask as follows.
(Code Snippet – Windows Phone 7 and The Cloud - Ex01 DefaultTaskLoginView)
XML
...
  <Tasks>
    <DefaultTask      Name ="_default" NavigationPage="Views/LoginView.xaml"/>
  </Tasks>
  <Tokens>
...



38. Verification: Press CTRL +F5 to launch myTodo service hosted in Compute Emulator. Then
    right click on the MyTodo.Phone.Online project file, click on Debug, and select Start new
    instance in order to launch the application in the Windows Phone Emulator. Remember to
    wait until myTodo service is working and the website is opened before running the instance.



  Note: Make sure you select Windows Phone 7 Emulator to run the application in emulator.
  When deploying the Windows Phone 7 application to a Windows Phone 7 device you can
  select Windows Phone 7 Device.




                                                                                      Page | 36
Figure 11
WP7 client consuming WCF Data Services


39. We have a WP7 application consuming WCF Data Services in a simple way. Now we are going
    to complete the myTodo’s functionality.
40. Create a new folder called Resources, and create a folder called Images into it.
41. Add the images located into Assets\Resources\Images to the recently created folder Images.
42. Add both TasksViewModel.cs and TaskDetailViewModel.cs located into Assets\ViewModels
    to ViewModels project folder.
43. Add TasksView.xaml, TasksView.xaml.cs, TaskDetailView.xaml and TaskDetailView.xaml.cs
    located into Assets\Views to Views project folder.
44. Add the MouseLeftButtonUp handler to the Listbox Items into the ListsView.xaml file.
(Code Snippet – Windows Phone 7 and The Cloud - Ex01 MouseLeftButtonUp)
                                                                                       Page | 37
XAML
...
    <ListBox Visibility="{Binding HasItems, Converter={StaticResource
VisibilityConverter}}" x:Name="ItemsListBox" ItemsSource="{Binding Items}"
MouseLeftButtonUp="ItemsListBox_MouseLeftButtonUp">
            <ListBox.ItemTemplate>
...



45. Implement all the remaining methods into the OnlineTodoService.cs class that throw a
    NotImplementedException.




Figure 12
Methods to be implemented


46. To do that, remove the following methods: GetListAndTasks, DeleteTask, UpdateTask,
    AddTask. Then copy the following code in this place.
(Code Snippet – Windows Phone 7 and The Cloud - Ex01 OnlineTodoServiceMethods- C#)
C#
...
      public void GetListAndTasks(Guid listId, Action<TaskList> callback)
      {

                                                                                    Page | 38
      var requestUri = new Uri(string.Format("{0}TaskLists?$filter=Id eq
guid'{1}'&$expand=Tasks", serviceUri, listId), UriKind.Absolute);
      var ctx = this.GetContext();

      ctx.BeginExecute<TaskList>(
        requestUri,
        delegate(IAsyncResult asyncResult)
        {
           try
           {
             var result =
ctx.EndExecute<TaskList>(asyncResult).FirstOrDefault();
             Deployment.Current.Dispatcher.BeginInvoke(() => {
callback(result); });
           }
           catch (Exception ex)
           {
             Debug.WriteLine(ex.ToString());
             Deployment.Current.Dispatcher.BeginInvoke(() =>
             {
               if (this.ServiceException != null)
               {
                 this.ServiceException(this, new ServiceExceptionArgs {
Exception = ex });
               }
             });
           }
        },
        null);
    }

    public void AddTask(TaskList list, Task task, Action<Task> callback)
    {
      var dataService = this.GetContext();
      task.TimestampUpdate = DateTime.UtcNow;
      task.UserName = App.UserName;
      dataService.AttachTo("TaskLists", list);
      dataService.AddRelatedObject(list, "Tasks", task);
      dataService.BeginSaveChanges(
         delegate(IAsyncResult asyncResult)
         {
           try
           {
             var response = dataService.EndSaveChanges(asyncResult).First();
             Deployment.Current.Dispatcher.BeginInvoke(() => {
callback(asyncResult.AsyncState as Task); });
           }
           catch (Exception ex)
           {
                                                                          Page | 39
               Debug.WriteLine(ex.ToString());
               Deployment.Current.Dispatcher.BeginInvoke(() =>
               {
                 if (this.ServiceException != null)
                 {
                   this.ServiceException(this, new ServiceExceptionArgs {
Exception = ex });
                 }
               });
             }
          },
          task
       );
    }

    public void DeleteTask(Task task, Action<Task> callback)
    {
      var dataService = this.GetContext();
      dataService.AttachTo("Tasks", task);
      dataService.DeleteObject(task);
      dataService.BeginSaveChanges(
        delegate(IAsyncResult asyncResult)
        {
           try
           {
             var response = dataService.EndSaveChanges(asyncResult).First();
             var deletedTask = asyncResult.AsyncState as Task;
             Deployment.Current.Dispatcher.BeginInvoke(() => {
callback(deletedTask); });
           }
           catch (Exception ex)
           {
             Debug.WriteLine(ex.ToString());
             Deployment.Current.Dispatcher.BeginInvoke(() =>
             {
               if (this.ServiceException != null)
               {
                 this.ServiceException(this, new ServiceExceptionArgs {
Exception = ex });
               }
             });
           }
        },
        task
      );
    }

    public void UpdateTask(Task task, Action<Task> callback)
    {
                                                                        Page | 40
      var dataService = this.GetContext();
      dataService.AttachTo("Tasks", task);
      dataService.UpdateObject(task);
      dataService.BeginSaveChanges(
        delegate(IAsyncResult asyncResult)
        {
           try
           {
             var response = dataService.EndSaveChanges(asyncResult).First();
             Deployment.Current.Dispatcher.BeginInvoke(() => {
callback(asyncResult.AsyncState as Task); });
           }
           catch (Exception ex)
           {
             Debug.WriteLine(ex.ToString());
             Deployment.Current.Dispatcher.BeginInvoke(() =>
             {
               if (this.ServiceException != null)
               {
                 this.ServiceException(this, new ServiceExceptionArgs {
Exception = ex });
               }
             });
           }
        },
        task
      );
    }
  }
...



47. Finally, you need to update the MyTodoDataService.svc.cs located on the MyTodo.Web
    project to support these operations. To accomplish this, add the following code below the
    QueryTaskLists function.
(Code Snippet – Windows Phone 7 and The Cloud - Ex01 MyTodoDataServiceMethods- C#)
C#
...
[ChangeInterceptor("TaskLists")]
public void OnChangeTaskList(TaskList taskList, UpdateOperations operation)
{
  // authenticate
  if (this.UserIdentity == null)
  {
    throw new DataServiceException(404, "Resource Not Found");
  }

                                                                                      Page | 41
  // authorize
  if (operation == UpdateOperations.Change || operation ==
UpdateOperations.Delete)
  {
    if (operation == UpdateOperations.Change) taskList.UserName =
this.UserIdentity.Name;
    var originalList = this.CurrentDataSource.TaskLists.FirstOrDefault(o => o.Id
== taskList.Id);
    if (originalList == null ||
!originalList.UserName.Equals(this.UserIdentity.Name,
StringComparison.OrdinalIgnoreCase))
    {
      throw new DataServiceException(404, "Resource Not Found");
    }
  }
  else if (operation == UpdateOperations.Add)
  {
    taskList.UserName = this.UserIdentity.Name;
    if (taskList.Id == Guid.Empty)
    {
      taskList.Id = Guid.NewGuid();
    }
  }
}

[ChangeInterceptor("Tasks")]
public void OnChangeTask(Task task, UpdateOperations operation)
{
  // authenticate
  if (this.UserIdentity == null)
  {
    throw new DataServiceException(404, "Resource Not Found");
  }

  // authorize
  if (operation == UpdateOperations.Add)
  {
    var originalList = this.CurrentDataSource.TaskLists.FirstOrDefault(o => o.Id
== task.TaskList.Id);
    if (originalList == null ||
!originalList.UserName.Equals(this.UserIdentity.Name,
StringComparison.OrdinalIgnoreCase))
    {
      throw new DataServiceException(404, "Resource Not Found");
    }
  }
  else if (operation == UpdateOperations.Change || operation ==
UpdateOperations.Delete)
                                                                          Page | 42
    {
    var originalTask = this.CurrentDataSource.Tasks.FirstOrDefault(o => o.Id ==
task.Id);
    var originalList = this.CurrentDataSource.TaskLists.FirstOrDefault(o =>
o.Tasks.FirstOrDefault(t => t.Id == task.Id) != null);
    if (originalTask == null ||
!originalList.UserName.Equals(this.UserIdentity.Name,
StringComparison.OrdinalIgnoreCase))
    {
      throw new DataServiceException(404, "Resource Not Found");
    }
  }

    switch (operation)
    {
      case UpdateOperations.Add:
        if (task.Id == Guid.Empty)
        {
          task.Id = Guid.NewGuid();
        }

          task.StartDate = DateTime.UtcNow;
          task.DueDate = DateTime.MaxValue;
          goto case UpdateOperations.Change;
        case UpdateOperations.Change:
          task.UserName = this.UserIdentity.Name;
          task.TimestampUpdate = DateTime.UtcNow;
          break;
    }
}

protected override void HandleException(HandleExceptionArgs args)
{
  Debug.Fail("DataService.Error", args.Exception.ToString());
  base.HandleException(args);
}


48. Verification: Press CTRL +F5 to launch myTodo service hosted in Compute Emulator. Then
    right click on the MyTodo.Phone.Online project file, click on Debug, and select Start new
    instance in order to launch the application in the Windows Phone Emulator. Remember to
    wait until myTodo service is working and the website is opened before running the instance.
    Now you have the whole WP7 application consuming myTodo’s WCF Data Services layer.




                                                                                      Page | 43
Summary
By completing this hands-on lab you have learned how to:
       Create a WCF Data Service that expose entities and business logic

       Consume a WCF Data Service from Windows Phone 7

       Authenticate a user from Windows Phone 7 against a WCF Service
       Debug a Windows Phone 7 application with the Phone Emulator




                                                                            Page | 44

								
To top