Showing posts with label Windows Phone apps. Show all posts
Showing posts with label Windows Phone apps. Show all posts

Tuesday, 19 January 2016

Create Windows Phone apps that access SharePoint 2013 : Part 5

How to: Implement business logic and data validation in a Windows Phone app for SharePoint 2013

n a Windows Phone app intended for production use, you likely need to validate data entered by users to, for example, enforce business logic relevant to your particular circumstances, or to ensure appropriate formatting of entered values, or simply to catch mistakes before saving values to a SharePoint list. Projects based on the Windows Phone SharePoint List Application template include default data validation logic, but such projects also provide a mechanism for developers to implement custom data validation.
Important
If you are developing an app for Windows Phone 8, you must use Visual Studio Express 2012 instead of Visual Studio 2010 Express. Except for the development environment, all information in this article applies to creating apps for both Windows Phone 8 and Windows Phone 7.

Default data validation rules

Some data types for fields in SharePoint lists are associated by default with simple formatting or data validation. If you enter an invalid URL for a field based on the Hyperlink or Picture field type in a SharePoint list and attempt to save your changes, you see a message indicating that the address you entered is invalid. If you enter a customer name as a value for a field based on the Date and Time field type, you receive a message directing you to enter a date within a valid range for the field.
Note
Date input validation is with respect to SharePoint date format. If the date format of the phone locale is required, customize the field and add validations accordingly.
Some of these basic validation rules are also enforced by default in a Windows Phone app created from the Windows Phone SharePoint List Application template. If you enter anything other than a date value in a field that is bound to a SharePoint field of Date and Time type in the Edit form of a Windows Phone app based on a SharePoint list, you see a validation error message when the focus shifts from the TextBox control associated with the field. (See Figure 1.)
Figure 1. Validation error cue in a Windows Phone app

Validation error cue in a Windows Phone app
The text box labeled "Start Time" in the Edit form is bound to a Date and Time field in the SharePoint list on which this sample app is based. The validation error cue (in red text) shown in Figure 1 appears if an invalid date is entered in the text box (and the text box subsequently loses focus) because the ValidatesOnNotifyDataErrorsproperty of the Binding object associated with the Text property of the TextBox control is set to True in the XAML declaration that defines the TextBox in the EditForm.xaml file.
<StackPanel Orientation="Vertical" Margin="0,5,0,5">
   <TextBlock TextWrapping="Wrap" HorizontalAlignment="Left" 
                    Style="{StaticResource PhoneTextNormalStyle}">Start Time*
   </TextBlock>
   <TextBox Height="Auto" Style="{StaticResource TextValidationTemplate}"
    FontSize="{StaticResource PhoneFontSizeNormal}" Width="470" 
        HorizontalAlignment="Left" Name="txtEventDate"
    Text="{Binding [EventDate], Mode=TwoWay, ValidatesOnNotifyDataErrors=True,
                       NotifyOnValidationError=True}"
    TextWrapping="Wrap" />
   <TextBlock FontSize="16" TextWrapping="Wrap" HorizontalAlignment="Left"
    Style="{StaticResource PhoneTextSubtleStyle}" Text="{Binding DateTimeFormat}" />
</StackPanel>
(If the ValidatesOnNotifyDataErrors property is set to False, the user has no indication that the entered data is invalid until the Save button is chosen. At that point, the user sees an error message regarding validation errors, because format validation on entered date values is still carried out by the base class from which theEditItemViewModel class is derived.)
But some fields may not provide any notification for invalid data in the Windows Phone app. And well-designed Visual Studio project templates are necessarily generalized to be used as a starting point for many different applications. The Windows Phone SharePoint List Application template can't include validation rules relevant to specific contexts and yet retain its value as a generalized template. Depending on your needs and the circumstances in which your particular Windows Phone app will be used, you likely will want to implement your own custom data-validation rules.

Implement custom data-validation rules

You can validate data entered by users of your Windows Phone app in several ways. A project created by using the Windows Phone SharePoint List Application template includes classes that serve as intermediaries between the forms (that is, the views) of the data in the Windows Phone app (for example, the EditForm.xaml file) and the data itself in the SharePoint list on which the app is based. These classes can be considered implementations of the ViewModel component of the Model-View-ViewModel design pattern (Figure 2). (For more information about how the Windows Phone SharePoint List Application template conforms to the MVVM software design pattern, see Architecture of the Windows Phone SharePoint List Application template.)
Note
The SharePoint list templates do not include default validations (such as percentage complete in a SharePoint task list, post check for a team discussion list, and SP decimal field type validation), but you can implement such validations.
Figure 2. Template files in ViewModel component

Template files in ViewModel component
In applications designed based on the MVVM pattern, data validation is often handled in the data layer (that is, in the Model component). In projects created from the Windows Phone SharePoint List Application template, an extensible mechanism for data validation has been "pushed up" a layer and implemented in the ViewModel component, to make it easier for developers to manage data validation. In projects based on the template, therefore, the most suitable place for custom code that validates user input or otherwise manages data is in these ViewModel classes. In terms of data validation, the EditItemViewModel class and the NewItemViewModelclass (the classes associated with the forms most likely to involve editing and updating list data) both provide an open implementation of a validation method (namedValidate) that overrides the base validation method in the class from which these two classes are derived.
public override void Validate(string fieldName, object value)
   {
      base.Validate(fieldName, value);
   }
This method provides a convenient mechanism to the developer for adding custom validation logic that targets individual fields. The general approach is to check the value of the fieldName argument passed to the Validate method to identify the field you want to associate with your custom validation code. You can, for example, use aswitch statement in your implementation of this method to supply validation logic specific to various fields in the Edit form (EditForm.xaml) of your Windows app.
For the following code example, assume that an installation of SharePoint Server has a Product Orders list created from the Custom List template. The list has been created with the columns and field types shown in Table 1.
Table 1. Product Orders list
Column
Type
Required
Product (i.e., Title)
Single line of text (Text)
Yes
Description
Single line of text (Text)
No
Quantity
Number
Yes
Order Date
Date and Time (DateTime)
No
Fulfillment Date
Date and Time (DateTime)
No
Contact Number
Single line of text (Text)
No
Again, for the purposes of this example, assume that the following simple validation rules are to be enforced, based on the business logic employed at the fictitious company Contoso, Ltd., for a given product ordering system:
  • Fulfillment dates for orders must be later than the date on which the order was placed.
  • If a customer wants to place an order for a product named Fuzzy Dice, the dice must be ordered in pairs. According to the peculiar rules at Contoso, Ltd., there is simply no such thing as a Fuzzy Die.
  • In the Product Orders list, the field type for phone numbers is "Single line of text" (that is, Text), which can be any text (up to 255 characters by default). For this sample, a formatting validation rule will be enforced that requires entered data to be in one of the common phone number formats; for example, "(555) 555-5555".

To implement custom validation rules

  1. Assuming you have created a SharePoint list based on the Custom List template that includes the columns and types specified in Table 1, create a Windows Phone app by using the Windows Phone SharePoint List Application template in Visual Studio by following the steps detailed in How to: Create a Windows Phone SharePoint 2013 list app.
  2. In Solution Explorer, in the ViewModels folder for the project, double-click the EditItemViewModel.cs file (or choose the file and press F7) to open the file for editing.
  3. Add the following using directives to the list of directives at the top of the file.
    using System.Globalization;
    using System.Text.RegularExpressions;
    
  4. Replace the default implementation of the Validate method in the file with the following code.
    public override void Validate(string fieldName, object value)
    {
        string fieldValue = value.ToString();
        if (!string.IsNullOrEmpty(fieldValue)) //Allowing for blank fields.
        {
            bool isProperValue = false;
    
            switch (fieldName)
            {
                case "Quantity":
                    // Enforce ordering Fuzzy Dice in pairs only.
                    int quantityOrdered;
                    isProperValue = Int32.TryParse(fieldValue, out quantityOrdered);
                    if (isProperValue)
                    {
                        if ((quantityOrdered % 2) != 0) // Odd number of product items ordered.
                        {
                            if ((string)this["Title"] == "Fuzzy Dice")
                            {
                                AddError("Item[Quantity]", "Fuzzy Dice must be ordered in pairs. 
                                                                       No such thing as a Fuzzy Die!");
                            }
                            else
                            {
                                // Restriction on ordering in pairs doesn't apply to other products.
                                RemoveAllErrors("Item[Quantity]");
                            }
                        }
                        else
                        {
                            RemoveAllErrors("Item[Quantity]");
                        }
                    }
                    break;
                case "Fulfillment_x0020_Date":
                    // Determine whether fulfillment date is later than order date.
                    DateTime fulfillmentDate;
                    isProperValue = DateTime.TryParse(fieldValue, CultureInfo.CurrentCulture, 
                                  DateTimeStyles.AssumeLocal, out fulfillmentDate);
                    if (isProperValue)
                    {
                        DateTime orderDate;
                        isProperValue = DateTime.TryParse((string)this["Order_x0020_Date"], 
                                   CultureInfo.CurrentCulture, DateTimeStyles.AssumeLocal, out orderDate);
    
                        if (fulfillmentDate.CompareTo(orderDate) > 0)
                        {
                            RemoveAllErrors("Item[Fulfillment_x0020_Date]");
                        }
                        else
                        {
                            AddError("Item[Fulfillment_x0020_Date]", 
                                    "Fulfillment Date must be later than Order Date.");
                        }
                    }
                    break;
                case "Contact_x0020_Number":
                    // Check that contact number is in an appropriate format.
                    Regex rx = new Regex(@"^\(?([0-9]{3})\)?[-. ]?([0-9]{3})[-. ]?([0-9]{4})$");
                    if (rx.IsMatch(fieldValue))
                    {
                        RemoveAllErrors("Item[Contact_x0020_Number]");
                    }
                    else
                    {
                        //Specified Contact Number is not a valid phone number.
                        AddError("Item[Contact_x0020_Number]", "Specified Contact Number is invalid.");
                    }
                    break;
                default:
                    // Not adding custom validation for other fields.
                    break;
            }
        }
    
        //And then proceed with default validation from base class.
        base.Validate(fieldName, value);
    }
    
    Keep in mind that the field names specified in this code sample are based on properties of the sample Product Orders list specified in Table 1. (Notice that in the XML schema for list fields in SharePoint Server, spaces in the names of fields are replaced with the string "_x0020_" for the Name attribute of the Field element that defines a given field. The template uses the Name attribute for a Field element as it is defined in the XML schema on the server, not the DisplayName attribute.) You can identify the field names of those fields for which you want to implement validation logic by looking at the Binding declarations of the Text properties for the TextBox objects defined in EditForm.xaml or by examining the ViewFields string of the CamlQueryBuilder class in the ListProvider.cs file.
  5. Save the file.
The custom validation code in this sample is executed only if the value argument passed to the Validate method is not a null or empty string. As indicated in Table 1, the Fulfillment Date and Contact Number fields are not required to contain data (as the list is defined for the purposes of this sample in SharePoint Server), so we want to allow these fields to be blank. A simple check to determine whether the value argument is null is not sufficient, because the value passed could be a zero-length string (which doesn't equate to a null value), and for this sample we don't want to invalidate zero-length strings for fields that can be blank. The validation logic for the Quantity and Fulfillment Date fields includes additional checks of the values passed in to ensure that they are of the appropriate type. If the initial check here (before the switchstatement) confirmed only that the value passed in were not null (instead of checking against the narrower condition of being a zero-length string), those validations would still not execute if the value were a zero-length string, but the logic to validate data for the Contact Number field would still execute if the value passed were a zero-length string. And in this sample we want to allow for the Contact Number field to be blank (a zero-length string), especially when a user starts editing a list item by opening the Edit form.
If you build the project and deploy it to Windows Phone Emulator to run it, you can test your validation logic by entering data that violates your business rules into the fields of the list in the Edit form of the app. (See Figure 3.)
Figure 3. Custom validation error cues

Custom validation error cues
The code in this sample, if it is included in the EditItemViewModel.cs file only, enforces these validation rules for data entered by users only on the Edit Form. If you want to enforce the validation rules both when users add new items as well as when they edit them, you must include the same validation logic in the Validate method in the NewItemViewModel.cs file (or, preferably, create a separate class file with a function that includes this validation logic and call that same function from the Validatemethods in both the EditItemViewModel.cs file and the NewItemViewModel.cs file).
The validation logic in this sample enforces given business rules by indicating to the user that entered data is not in a format permitted by the rules, but the entered data is not intercepted and changed by this code. To intercept and, for example, format phone numbers in a consistent way before saving the data to the SharePoint list, you can implement custom data conversion for entered phone numbers.

Create Windows Phone apps that access SharePoint 2013 : Part 4

How to: Store and retrieve SharePoint list items on a Windows Phone

On a Windows Phone, only one app runs at a time, and when a user switches to another app on the phone (by pressing the Start button on the phone, for example), the app currently running is deactivated, or, in the terms of Windows Phone development, tombstoned. If the user switches back to the deactivated app (by pressing the Backbutton), the app can be reactivated, but unless you provide logic to handle application state information over the course of the app life cycle, that state information is not preserved by default in the transition from activation to deactivation and back again. (For more information about the application life cycle for Windows Phone apps, seeExecution Model Overview for Windows Phone.)
For Windows Phone apps, the PhoneApplicationService class exposes standard life-cycle events that can be used to manage application state. In projects created from the Windows Phone SharePoint List Application template (as with projects created from all Silverlight for Windows Phone templates), these standard Windows Phone application life-cycle events are declared in the App.xaml file and associated with event handlers in the code-behind file, App.xaml.cs. The declarations in the App.xaml file for your SharePoint list apps should look like the following markup.
<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>
The Application_Activated and Application_Deactivated event handlers declared in the App.xaml file are implemented in the App.xaml.cs code-behind file with default logic that caches application state information for use in the phone app as long as the app is not terminated. The implementation of the handlers for these events uses theState property (which provides access to a Dictionary object) of the PhoneApplicationService class to store data. Data stored in this State property is transient. That is, it is preserved when the app is deactivated or tombstoned, but not when the app is terminated. It is important to keep in mind as you handle application life-cycle events in your projects that if a Windows app is deactivated when a user switches to another app, that deactivated app is subject to termination by the Windows Phone operating system, depending on circumstances. Any data on the phone that isn’t saved to persistent storage is lost, even if that data was saved to transient storage by using theState property of the PhoneApplicationService.
In a Windows Phone app that gets data from a SharePoint list, the data used on the phone from session to session can of course be retrieved from the server running SharePoint Server, if the server is available. But continuous connectivity to a SharePoint Server may not be available for a Windows Phone device, owing to variations in service coverage by location and other factors. To provide users of your app with access to data in the event of lost connectivity with the server running SharePoint Server, or simply to save data to persistent storage between sessions of the app regardless of server availability, you can take advantage of the Closing and Launching events of the PhoneApplicationService class.
The Application_Launching and Application_Closing handlers for these events are declared in App.xaml and defined in the App.xaml.cs file, but they are not implemented. To handle storing and retrieving application state information in the context of app termination, you can provide an implementation for theApplication_Closing event handler to store data in the isolated storage designated for the app so that the data persists between sessions of the app, and you can provide an implementation for the Application_Launching event handler to retrieve data from isolated storage when a new session of the app is started (when the app is launched), even if connectivity to the server running SharePoint Server that is the original source of the data is not available.
Tip
Data should be encrypted before you save it to a local device. For more information about how to encrypt the data, see How to: Encrypt Data in a Windows Phone Application

To implement event handlers for storing and retrieving application state

  1. Create a Windows Phone app by using the Windows Phone SharePoint List Application template in Visual Studio by following the steps in How to: Create a Windows Phone SharePoint 2013 list app.
  2. In Solution Explorer, choose the App.xaml file.
  3. Press F7 to open the code-behind file, App.xaml.cs, for editing.
  4. Locate the (empty) implementation of the Application_Launching event handler and replace the event handler with the following code.
    private void Application_Launching(object sender, LaunchingEventArgs e)
    {
        if (IsolatedStorageSettings.ApplicationSettings.Contains(DataProvider.ListTitle))
        {
            App.MainViewModel = (ListViewModel)IsolatedStorageSettings.ApplicationSettings
                                                  [DataProvider.ListTitle];                
            App.MainViewModel.Initialize();
        }
    }
    
  5. Locate the (empty) implementation of the Application_Closing event handler and replace that event handler with the following code.
    private void Application_Closing(object sender, ClosingEventArgs e)
    {
        if (IsolatedStorageSettings.ApplicationSettings.Contains(DataProvider.ListTitle))
        {
            IsolatedStorageSettings.ApplicationSettings[DataProvider.ListTitle] = App.MainViewModel;
        }
        else
        {
            IsolatedStorageSettings.ApplicationSettings.Add(DataProvider.ListTitle, App.MainViewModel);
        }
        IsolatedStorageSettings.ApplicationSettings.Save();
    }
    
  6. Save the file.
With these implementations in place, run your app to initialize the main ViewModel in the app with data from the server running SharePoint Server. Exit the app on the phone (by pressing the Back button to navigate past the first page of the app) to trigger the Application_Closing event. If you then run your app without connectivity to the server, the ViewModel that was saved to the IsolatedStorageSettingsDictionary object (in the Application_Closing event) is retrieved and initialized. The SharePoint list items that were saved to isolated storage in a previous session of the app are displayed in the List form (List.xaml) of the app.

Implement a mechanism for editing list items offline

If you follow the procedure in the previous section to implement handlers for the Closing and Launching events in your app, SharePoint list data that was retrieved from the server when connectivity was available can be displayed in your app even if connectivity to the server is lost in a subsequent session of the app, because the list items are retrieved from local persistent storage on the phone. Based on the implementation in the previous section, however, the list items made available in this way for display while offline can't be edited and saved back to the server unless connectivity is restored. In the following procedure, you’ll add a mechanism to your app to provide for storing edited versions of list items locally when connectivity is unavailable. When connectivity to the server is available again, you can retrieve these edited list items and save your changes back to the server.
For the procedures in this section, we assume you’re working in the context of a Windows Phone app project created from the Windows Phone SharePoint List Application template and that your app is based on a Product Orders list created from the Custom List template on the server and contains the columns and field types shown in Table 1.
Table 1. Sample Product Orders list
Column
Type
Required
Product (for example, Title)
Single line of text (Text)
Yes
Description
Single line of text (Text)
No
Quantity
Number
Yes
Order Date
Date and Time (DateTime)
No
Fulfillment Date
Date and Time (DateTime)
No
Contact Number
Single line of text (Text)
No

To implement a class to support editing items while offline

  1. Starting with a Visual Studio project that was created based on the Product Orders list represented by Table 1, in Solution Explorer, choose the node that represents the project (for example, SPListAppLocalStorage).
  2. On the Project menu, choose Add Class.
    The Add New Item dialog box appears with the C# Class template selected.
  3. Name the class file DraftItemStore.cs, and then choose Add.
    The class file is added to the project and opened for editing.
  4. Replace the contents of the class file with the following code.
    using System;
    using System.Net;
    using System.Windows;
    using System.Collections.Generic;
    using System.IO.IsolatedStorage;
    
    namespace SPListAppLocalStorage // Based on project name by default.
    {
        public class DraftItemStore
        {
            const string DraftsKey = "Drafts";
    
            public static void AddDraftItem(string id, EditItemViewModel model)
            {
                Dictionary<string, EditItemViewModel> draftCollection = GetDraftItemCollection();
                draftCollection[id] = model;
                SaveDrafts(draftCollection);
            }
    
            public static void RemoveDraftItem(string id)
            {
                Dictionary<string, EditItemViewModel> draftCollection = GetDraftItemCollection();
                draftCollection.Remove(id);
                SaveDrafts(draftCollection);
            }
    
            public static void SaveDrafts(Dictionary<string, EditItemViewModel> draft)
            {
                if (IsolatedStorageSettings.ApplicationSettings.Contains(DraftsKey))
                {
                    IsolatedStorageSettings.ApplicationSettings[DraftsKey] = draft;
                }
                else
                {
                    IsolatedStorageSettings.ApplicationSettings.Add(DraftsKey, draft);
                }
            }
    
            public static List<EditItemViewModel> Drafts
            {
                get
                {
                    Dictionary<string, EditItemViewModel> draftCollection = GetDraftItemCollection();
    
                    List<EditItemViewModel> modelCollection = new List<EditItemViewModel>();
                    foreach (KeyValuePair<string, EditItemViewModel> entry in draftCollection)
                    {
                        modelCollection.Add(entry.Value);
                    }
    
                    return modelCollection;
                }
            }
    
            public static Dictionary<string, EditItemViewModel> GetDraftItemCollection()
            {
                Dictionary<string, EditItemViewModel> draftCollection = null;
                if (IsolatedStorageSettings.ApplicationSettings.Contains(DraftsKey))
                    draftCollection = (Dictionary<string,
                    EditItemViewModel>)IsolatedStorageSettings.ApplicationSettings[DraftsKey];
    
                if (draftCollection == null)
                    draftCollection = new Dictionary<string, EditItemViewModel>();
    
                return draftCollection;
            }
    
            public static EditItemViewModel GetDraftItemById(string id)
            {
                Dictionary<string, EditItemViewModel> draftCollection = GetDraftItemCollection();
                return !draftCollection.ContainsKey(id) ? null : draftCollection[id];
            }
        }
    }
    
    The namespace specified in this code is based on the name of the project (SPListAppLocalStorage in this case). You might want to specify a different namespace, based on the name of your project.
  5. Save the file.
A specific instance of the EditItemViewModel class represents a SharePoint list item that is being edited on the phone. You can consider a list item that was edited as a "draft item" before changes to the item are saved to the server. In the code in this class, the AddDraftItem method adds a specific instance of the EditItemViewModelclass (that is, a draft item) as a value to a Dictionary object, associating the EditItemViewModel in the Dictionary with a key based on the identifier for the given list item. (An identifier is assigned by SharePoint Server to each item in a list. In a project based on the Windows Phone SharePoint List Application template, that identifier is stored in the ID property of the given ViewModel class, such as EditItemViewModel or DisplayItemViewModel, which represents the list item.) The RemoveDraftItemmethod removes an EditItemViewModel from the Dictionary object based on a specified identifier. Both of these methods use the GetDraftItemCollection method to retrieve the Dictionary object containing the EditItemViewModel objects from isolated storage and both methods use the SaveDrafts method to save the modifiedDictionary object (with a draft item either added to it or removed from it) back to isolated storage. The GetDraftItemCollection method first determines whether a "Drafts" Dictionary object has been saved to isolated storage. If so, the method returns that Dictionary object; otherwise, the method initializes and returns a newDictionary object. The Drafts property of the class provides access to the Dictionary of draft items by returning a list (that is, an object based on the List<T> generic) of draft items as EditItemViewModel objects. The GetDraftItemById method returns a given draft item from the Dictionary object based on a specified identifier value.
Now you can add elements to the user interface of the phone app and configure them to use the DraftItemStore class for editing list items offline. In the following procedures, you will:
  • Add and configure a Windows Phone page to display all list items that were saved as draft items to isolated storage on the phone.
  • Add and configure another page, bound to an EditItemViewModel, for editing an individual draft item, analogous to the Edit form (EditForm.xaml) for list items.
  • Add a method, SaveAsDraft, to the EditItemViewModel class that executes the AddDraftItem method of the DraftItemStore class implemented in the previous procedure.
  • Add an ApplicationBar button to the EditForm.xaml file to call the SaveAsDraft method.
  • Add an ApplicationBar button to the List.xaml file to navigate to the page that displays all list items saved as drafts.

To add a page for displaying all draft items saved on the phone

  1. In Solution Explorer, choose the Views folder.
  2. On the Project menu, choose Add New Item.
    The Add New Item dialog box opens.
  3. In the Add New Item dialog box, under the Visual C# node, choose the Silverlight for Windows Phone node.
  4. In the Templates pane, choose the Windows Phone Portrait Page template.
  5. Name the file Drafts.xaml, and then choose Add.
    The file is added to the project under the Views node and opened for editing.
  6. In the XAML pane of the designer, replace the contents of the file with the following XAML.
    <phone:PhoneApplicationPage 
        x:Class="SPListAppLocalStorage.Views.Drafts"
        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"
        FontFamily="{StaticResource PhoneFontFamilyNormal}"
        FontSize="{StaticResource PhoneFontSizeNormal}"
        Foreground="{StaticResource PhoneForegroundBrush}"
        SupportedOrientations="Portrait" Orientation="Portrait"
        mc:Ignorable="d" d:DesignHeight="696" d:DesignWidth="480"
        shell:SystemTray.IsVisible="True">
    
        <!--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 x:Name="ApplicationTitle" Text="Product Orders" 
                                        Style="{StaticResource PhoneTextNormalStyle}"/>
                <TextBlock x:Name="PageTitle" Text="Draft Items" 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">
                <ListBox x:Name="lstBoxDraftItems" ItemsSource="{Binding}"
                                      SelectionChanged="lstBoxDraftItems_SelectionChanged">
                    <ListBox.ItemTemplate>
                        <DataTemplate>
                            <StackPanel>
                                <TextBlock Text="{Binding [Title]}" Style="
                                               {StaticResource PhoneTextTitle2Style}"></TextBlock>
                                <TextBlock Text="{Binding [Description]}" Style="
                                                {StaticResource PhoneTextNormalStyle}"></TextBlock>
                                <TextBlock Text="{Binding [Contact_x0020_Number]}" Style="
                                               {StaticResource PhoneTextNormalStyle}"></TextBlock>
                            </StackPanel>
                        </DataTemplate>
                    </ListBox.ItemTemplate>
                </ListBox>
            </Grid>
        </Grid>
     
        <phone:PhoneApplicationPage.ApplicationBar>
            <shell:ApplicationBar IsVisible="True" IsMenuEnabled="True">
                <shell:ApplicationBarIconButton x:Name="btnCancel" 
                 IconUri="/Images/appbar.cancel.rest.png" Text="Cancel" Click="OnCancelButtonClick" />
            </shell:ApplicationBar>
        </phone:PhoneApplicationPage.ApplicationBar>
    
    </phone:PhoneApplicationPage>
    
    The value of the namespace designation <x:Class> in this code ("SPListAppLocalStorage.Views.Drafts") will vary depending on the name of your project.
  7. With the Drafts.xaml file selected in Solution Explorer, press F7 to open the associated code-behind file, Drafts.xaml.cs, for editing.
  8. Replace the contents of the file with the following code.
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Net;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Documents;
    using System.Windows.Input;
    using System.Windows.Media;
    using System.Windows.Media.Animation;
    using System.Windows.Shapes;
    using Microsoft.Phone.Controls;
    
    namespace SPListAppLocalStorage.Views
    {
        public partial class Drafts : PhoneApplicationPage
        {
            public Drafts()
            {
                InitializeComponent();
                this.Loaded += new RoutedEventHandler(Drafts_Loaded);
            }
    
            private void lstBoxDraftItems_SelectionChanged(object sender, SelectionChangedEventArgs e)
            {
                ListBox lstBox = sender as ListBox;
                if (lstBox.SelectedIndex == -1)
                    return;
    
                EditItemViewModel selectedDraftItem = lstBox.SelectedItem as EditItemViewModel;
                NavigationService.Navigate(new Uri(string.Format("/Views/DraftItemEditForm.xaml?ID={0}",
                                                       selectedDraftItem.ID), UriKind.Relative));
    
                lstBox.SelectedIndex = -1;
            }
    
            void Drafts_Loaded(object sender, RoutedEventArgs e)
            {
                this.DataContext = DraftItemStore.Drafts;
            }
    
            private void OnCancelButtonClick(object sender, EventArgs e)
            {
                // Navigate back to initial List View form.
                NavigationService.Navigate(new Uri("/Views/List.xaml", UriKind.Relative));
            }
        }
    }
    
  9. Save the files.

To add a page for editing individual draft items

  1. In Solution Explorer, choose the Views folder.
  2. On the Project menu, choose Add New Item.
    The Add New Item dialog box opens.
  3. In the Add New Item dialog box, under the Visual C# node, choose the Silverlight for Windows Phone node.
  4. In the Templates pane, choose the Windows Phone Portrait Page template.
  5. Name the file DraftItemEditForm.xaml, and then choose Add.
    The file is added to the project under the Views node and opened for editing.
  6. In the XAML pane of the designer, replace the contents of the file with the following XAML.
    <phone:PhoneApplicationPage
        x:Class="SPListAppLocalStorage.DraftItemEditForm"
        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" d:DesignWidth="480" d:DesignHeight="696"
        FontFamily="{StaticResource PhoneFontFamilyNormal}"
        FontSize="{StaticResource PhoneFontSizeNormal}"
        Foreground="{StaticResource PhoneForegroundBrush}"
        SupportedOrientations="Portrait" Orientation="Portrait"
        shell:SystemTray.IsVisible="True" x:Name="DraftItemEditPage">
    
        <!--LayoutRoot is the root grid where all page content is placed-->
        <Grid x:Name="LayoutRoot" Background="Transparent"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
                 xmlns:controls="clr-namespace:Microsoft.Phone.Controls;assembly=
                    Microsoft.Phone.Controls">
            <StackPanel>
                <ProgressBar Background="Red" x:Name="progressBar" Opacity="1" 
                                        HorizontalAlignment="Center" VerticalAlignment="Top" 
                                        Height="15" Width="470" IsIndeterminate="{Binding IsBusy}" 
                                        Visibility="{Binding ShowIfBusy}" />
                <ScrollViewer HorizontalScrollBarVisibility="Auto" Height="700">
                    <Grid x:Name="ContentPanel" Width="470">
                        <StackPanel Margin="0,5,0,5">
                            <StackPanel Orientation="Vertical" Margin="0,5,0,5">
                                <TextBlock TextWrapping="Wrap" HorizontalAlignment="Left" 
                                    Style="{StaticResource PhoneTextNormalStyle}">Product*</TextBlock>
                                <TextBox Style="{StaticResource TextValidationTemplate}" 
                             FontSize="{StaticResource   PhoneFontSizeNormal}" Width="470" 
                             HorizontalAlignment="Left" Name="txtTitle" Text="{Binding [Title], 
                                       Mode=TwoWay,ValidatesOnNotifyDataErrors=True,NotifyOnValidationError=True}" 
                                                                             TextWrapping="Wrap" />
                            </StackPanel>
                            <StackPanel Orientation="Vertical" Margin="0,5,0,5">
                                <TextBlock TextWrapping="Wrap" HorizontalAlignment="Left" 
                                   Style="{StaticResource PhoneTextNormalStyle}">Description</TextBlock>
                                <TextBox Style="{StaticResource TextValidationTemplate}" 
                                   FontSize="{StaticResource PhoneFontSizeNormal}" Width="470" 
                                    HorizontalAlignment="Left" Name="txtDescription" 
                                                               Text="{Binding [Description],
                                                               Mode=TwoWay, ValidatesOnNotifyDataErrors=True, 
                                                               NotifyOnValidationError=True}" 
                                           TextWrapping="Wrap" />
                            </StackPanel>
                            <StackPanel Orientation="Vertical" Margin="0,5,0,5">
                                <TextBlock TextWrapping="Wrap" HorizontalAlignment="Left" 
                                   Style="{StaticResource PhoneTextNormalStyle}">
                                                               Product Category</TextBlock>
                                <ListBox MaxHeight="400" Width="Auto" x:Name="lstBoxProduct_x0020_Category"
                                                  ItemsSource="{Binding [Product_x0020_Category]}">
                                    <ListBox.ItemTemplate>
                                        <DataTemplate>
                                            <RadioButton FontSize="{StaticResource PhoneFontSizeNormal}" 
                                              HorizontalAlignment="Left" GroupName="Product_x0020_Category" 
                                                                     Content="{Binding Name}" 
                                   IsChecked="{Binding IsChecked, Mode=TwoWay}" />
                                        </DataTemplate>
                                    </ListBox.ItemTemplate>
                                </ListBox>
                            </StackPanel>
                            <StackPanel Orientation="Vertical" Margin="0,5,0,5">
                                <TextBlock TextWrapping="Wrap" HorizontalAlignment="Left" 
                                           Style="{StaticResource PhoneTextNormalStyle}">Quantity*</TextBlock>
                                <TextBox Style="{StaticResource TextValidationTemplate}" 
                                       FontSize="{StaticResource PhoneFontSizeNormal}" Width="470" 
                                          HorizontalAlignment="Left" Name="txtQuantity" Text="{Binding [Quantity], 
                                            Mode=TwoWay, ValidatesOnNotifyDataErrors=True, 
                                               NotifyOnValidationError=True}"
                                                 TextWrapping="Wrap" />
                            </StackPanel>
                            <StackPanel Orientation="Vertical" Margin="0,5,0,5">
                                <TextBlock TextWrapping="Wrap" HorizontalAlignment="Left" 
                                    Style="{StaticResource PhoneTextNormalStyle}">Order Date</TextBlock>
                                <TextBox Height="Auto" Style="{StaticResource TextValidationTemplate}"
                                                   FontSize="{StaticResource PhoneFontSizeNormal}" Width="470"  
                                                             HorizontalAlignment="Left" Name="txtOrder_x0020_Date" 
                             Text="{Binding [Order_x0020_Date], Mode=TwoWay, ValidatesOnNotifyDataErrors=True, 
                                               NotifyOnValidationError=True}" TextWrapping="Wrap" />
                                <TextBlock FontSize="16" TextWrapping="Wrap" HorizontalAlignment="Left" 
                                                     Style="{StaticResource PhoneTextSubtleStyle}" 
                                                                 Text="{Binding DateTimeFormat}" />
                            </StackPanel>
                            <StackPanel Orientation="Vertical" Margin="0,5,0,5">
                                <TextBlock TextWrapping="Wrap" HorizontalAlignment="Left" 
                             Style="{StaticResource PhoneTextNormalStyle}">Fulfillment Date</TextBlock>
                                <TextBox Height="Auto" Style="{StaticResource TextValidationTemplate}"
                                              FontSize="{StaticResource PhoneFontSizeNormal}" Width="470" 
                                              HorizontalAlignment="Left" Name="txtFulfillment_x0020_Date" 
                            Text="{Binding [Fulfillment_x0020_Date], Mode=TwoWay, 
                         ValidatesOnNotifyDataErrors=True, NotifyOnValidationError=True}" 
                             TextWrapping="Wrap" />
                                <TextBlock FontSize="16" TextWrapping="Wrap" HorizontalAlignment="Left"
                                                        Style="{StaticResource PhoneTextSubtleStyle}" Text="{Binding
                                                               DateTimeFormat}" />
                            </StackPanel>
                            <StackPanel Orientation="Horizontal">
                                <TextBlock TextWrapping="Wrap" Width="150" HorizontalAlignment="Left"
                                                      Style="{StaticResource PhoneTextNormalStyle}">Rush 
                                                                       :</TextBlock>
                                <CheckBox Name="txtRush" FontSize="{StaticResource PhoneFontSizeNormal}" 
                                              HorizontalAlignment="Left" IsChecked="{Binding [Rush], Mode=TwoWay, 
                                   ValidatesOnNotifyDataErrors=True, NotifyOnValidationError=True}" />
                            </StackPanel>
                            <StackPanel Orientation="Vertical" Margin="0,5,0,5">
                                <TextBlock TextWrapping="Wrap" HorizontalAlignment="Left" 
                            Style="{StaticResource PhoneTextNormalStyle}">Contact Number</TextBlock>
                                <TextBox Style="{StaticResource TextValidationTemplate}" 
                                       FontSize="{StaticResource PhoneFontSizeNormal}" Width="470"
                                                  HorizontalAlignment="Left" Name="txtContact_x0020_Number"
                                                             Text="{Binding [Contact_x0020_Number], 
                                                             Mode=TwoWay, ValidatesOnNotifyDataErrors=True, 
                                                             NotifyOnValidationError=True}" 
                                                                               TextWrapping="Wrap" />
                            </StackPanel>
                        </StackPanel>
                    </Grid>
                </ScrollViewer>
            </StackPanel>
        </Grid>
    
        <phone:PhoneApplicationPage.ApplicationBar>
            <shell:ApplicationBar IsVisible="True" IsMenuEnabled="True">
                <shell:ApplicationBarIconButton x:Name="btnSubmit" 
                                    IconUri="/Images/appbar.save.rest.png" 
                                    Text="Submit" Click="OnSubmitButtonClick"/>
                <shell:ApplicationBarIconButton x:Name="btnBack" 
                                    IconUri="/Images/appbar.back.rest.png" 
                                     Text="Back to List" Click="OnBackButtonClick"/>
            </shell:ApplicationBar>
        </phone:PhoneApplicationPage.ApplicationBar>
    
    </phone:PhoneApplicationPage>
    
    The XAML for defining this page is similar to that of the EditForm.xaml file. You can copy the EditForm.xaml file to use as a basis for DraftItemEditForm.xaml, making the modifications to the file as indicated in this markup.
  7. With the DraftItemEditForm.xaml file chosen in Solution Explorer, press F7 to open the associated code-behind file, DraftItemEditForm.xaml.cs, for editing.
  8. Replace the contents of the file with the following code.
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Net;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Documents;
    using System.Windows.Input;
    using System.Windows.Media;
    using System.Windows.Media.Animation;
    using System.Windows.Shapes;
    using Microsoft.Phone.Controls;
    using Microsoft.SharePoint.Client;
    using Microsoft.Phone.Tasks;
    using System.Device.Location;
    using Microsoft.Phone.Shell;
    using Microsoft.SharePoint.Phone.Application;
    
    namespace SPListAppLocalStorage
    {
        public partial class DraftItemEditForm : PhoneApplicationPage
        {
            EditItemViewModel viewModel;
    
            /// <summary>
            /// Constructor for Draft Item Edit Form.
            /// </summary>
            public DraftItemEditForm()
            {
                InitializeComponent();
            }
    
            protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)
            {
     // Include initialization of ViewModel here rather than in constructor to be able to use QueryString value.
                if (viewModel == null)
                {
                    viewModel = DraftItemStore.GetDraftItemById(NavigationContext.QueryString["ID"].ToString());
                }
    
                viewModel.Initialize();
                this.DataContext = viewModel;
    
                base.OnNavigatedTo(e);
                viewModel.ItemUpdated += new EventHandler<ItemUpdatedEventArgs>(OnItemUpdated);
            }
    
            protected override void OnNavigatedFrom(System.Windows.Navigation.NavigationEventArgs e)
            {
                base.OnNavigatedFrom(e);
                viewModel.ItemUpdated -= new EventHandler<ItemUpdatedEventArgs>(OnItemUpdated);
            }
    
            private void OnViewModelInitialization(object sender, InitializationCompletedEventArgs e)
            {
                this.Dispatcher.BeginInvoke(() =>
                {
                    // If initialization has failed show error message and return.
                    if (e.Error != null)
                    {
                        MessageBox.Show(e.Error.Message, e.Error.GetType().Name, MessageBoxButton.OK);
                        return;
                    }
    
                    // Set Page's DataContext to current ViewModel instance.
                    this.DataContext = (sender as EditItemViewModel);
                });
            }
    
            private void OnCancelButtonClick(object sender, EventArgs e)
            {
                NavigationService.Navigate(new Uri("/Views/List.xaml", UriKind.Relative));
            }
    
            private void OnSubmitButtonClick(object sender, EventArgs e)
            {
                viewModel.UpdateItem();
            }
    
            private void OnItemUpdated(object sender, ItemUpdatedEventArgs e)
            {
                this.Dispatcher.BeginInvoke(() =>
                {
                    if (e.Error != null)
                    {
                        MessageBox.Show(e.Error.Message, e.Error.GetType().Name, MessageBoxButton.OK);
                        return;
                    }
    
                    // Remove Draft Item from local storage if update to server succeeds.
                    DraftItemStore.RemoveDraftItem(viewModel.ID.ToString());
                    this.NavigationService.Navigate(new Uri("/Views/List.xaml", UriKind.Relative));
                });
            }
    
            private void OnBackButtonClick(object sender, EventArgs e)
            {
                NavigationService.Navigate(new Uri("/Views/List.xaml", UriKind.Relative));
            }
        }
    }
    
    As you can see, the namespace used in this file is based on the name of the project (SPListAppLocalStorage).
  9. Add the appbar.back.rest.png image to your project for the ApplicationBar button (btnBack) declared in the DraftItemEditForm.xaml file. In Solution Explorer, choose the Images folder node in the project.
  10. On the Project menu, choose Add Existing Item.
  11. In the browser that opens, navigate to the folder in which the standard Windows Phone icon images were installed by the Windows Phone SDK 7.1.
    Note
    The images with a light foreground and a dark background are in %PROGRAMFILES%(x86)\Microsoft SDKs\Windows Phone\v7.1\Icons\dark in a standard installation of the SDK.
  12. Choose the image file named appbar.back.rest.png, and choose Add. The image is added to the project under the Images node.
  13. In Solution Explorer, choose the image file you just added, and in the Properties Window for the file, set the Build Action property for the image file to Content, and set the Copy to Output Directory property to Copy if newer.
  14. Save the files.

To add an ApplicationBar button to the Edit Form for saving an item as a draft

  1. In Solution Explorer, choose the EditItemViewModel.cs file under the ViewModels node in the project. Press F7 to open the file for editing.
  2. Within the code block (demarcated by opening and closing braces) that implements the EditItemViewModel class, add the following public method to the file.
    public void SaveAsDraft()
    {
        DraftItemStore.AddDraftItem(this.ID.ToString(), this);
    }
    
  3. In Solution Explorer, under the Views node in the project, double-click the EditForm.xaml file.
    The file is opened for editing in the designer.
  4. In the XAML pane of the designer, add another button to the <shell:ApplicationBar> tag (in addition to the existing Submit and Cancel buttons), as shown in the following XAML.
    <phone:PhoneApplicationPage.ApplicationBar>
        <shell:ApplicationBar IsVisible="True" IsMenuEnabled="True">
            <shell:ApplicationBarIconButton x:Name="btnSubmit" 
                  IconUri="/Images/appbar.save.rest.png" 
                  Text="Submit" Click="OnBtnSubmitClick"/>
            <shell:ApplicationBarIconButton x:Name="btnSaveDraft"            IconUri="/Images/appbar.save.rest.png" Text="Save Draft"            Click="OnSaveDraftButtonClick"/>
            <shell:ApplicationBarIconButton x:Name="btnCancel" 
                          IconUri="/Images/appbar.cancel.rest.png" 
                          Text="Cancel" Click="OnCancelButtonClick"/>
        </shell:ApplicationBar>
    </phone:PhoneApplicationPage.ApplicationBar>
    
  5. With the EditForm.xaml file chosen in Solution Explorer, press F7 to open the associated code-behind file, EditForm.xaml.cs, for editing.
  6. Within the code block (demarcated by opening and closing braces) that implements the EditForm partial class, add the following event handler to the file.
    private void OnSaveDraftButtonClick(object sender, EventArgs e)
    {
        viewModel.SaveAsDraft();
    }
    
  7. Save the files.

To add an ApplicationBar button to the List View Form to display all draft items

  1. In Solution Explorer, under the Views node, double-click the List.xaml file.
    The file is opened for editing in the designer.
  2. In the XAML pane of the designer, add another button to the <shell:ApplicationBar> tag (in addition to the existing New and Refresh buttons), as shown in the following XAML markup.
    <phone:PhoneApplicationPage.ApplicationBar>
        <shell:ApplicationBar IsVisible="True" IsMenuEnabled="True">
            <shell:ApplicationBarIconButton x:Name="btnNew" 
            IconUri="/Images/appbar.new.rest.png" Text="New" 
                        Click="OnNewButtonClick"/>
            <shell:ApplicationBarIconButton x:Name="btnRefresh" 
                       IconUri="/Images/appbar.refresh.rest.png" 
            Text="Refresh" IsEnabled="True" Click="OnRefreshButtonClick"/>
            <shell:ApplicationBarIconButton x:Name="btnDrafts"            IconUri="/Images/appbar.folder.rest.png"            Text="View Drafts" IsEnabled="True"            Click="OnDraftsButtonClick"/>
        </shell:ApplicationBar>
    </phone:PhoneApplicationPage.ApplicationBar>
    
  3. Add an icon image to your project for the Drafts button. In Solution Explorer, choose the Images folder node in the project.
  4. On the Project menu, choose Add Existing Item.
  5. In the browser that opens, navigate to the folder in which the standard Windows Phone icon images were installed by the Windows Phone SDK 7.1.
    Note
    The images with a light foreground and a dark background are in %PROGRAMFILES%(x86)\Microsoft SDKs\Windows Phone\v7.1\Icons\dark in a standard installation of the SDK.
  6. Choose the image file named appbar.folder.rest.png, and then choose Add.
    The image is added is added to the project under the Images node.
  7. In Solution Explorer, choose the image file you just added and in the Properties Window, set the Build Action property for the image file to Content and set theCopy to Output Directory property to Copy if newer.
  8. In Solution Explorer, choose the List.xaml file under the Views node and press F7. The associated code-behind file, List.xaml.cs, is opened for editing.
  9. Add the following event handler to the file, within the code block (demarcated by opening and closing braces) that implements the ListForm partial class.
    private void OnDraftsButtonClick(object sender, EventArgs e)
    {
        NavigationService.Navigate(new Uri("/Views/Drafts.xaml", UriKind.Relative));
    }
    
  10. Save all the files in the solution and press F6 to compile the solution.
If you start the project and deploy it to a Windows Phone Emulator, you see a View Drafts button on the ApplicationBar of the List form (Figure 1), which brings up all list items stored as drafts.
Figure 1. Modified List Form with View Drafts button

Modified List Form with View Drafts button
At first, because no drafts are saved, the page to display drafts will be empty. Choose an item from the List form (to show the Display form (DisplayForm.xaml) for an item), and then choose the Edit button to display the Edit form. If you should lose connectivity with the SharePoint Server, you can then choose the Save Draft button on the Edit Form (Figure 2) to save any changes you've made to the list item to isolated storage.
Figure 2. Modified Edit Form with Save Draft button

Modified Edit Form with Save Draft button
When the server becomes available again, you can choose the View Drafts button on the List form to display the Drafts page (Figure 3).
Figure 3. Drafts page displaying items saved as drafts to isolated storage

Drafts page displaying list items saved as drafts
If you choose an item on the Drafts page, the Draft Item Edit form (DraftItemEditForm.xaml) is displayed (Figure 4) and you can make any additional changes, and then click the Submit button to save the edited item to the server. At that point, the item is removed from isolated storage because it’s no longer treated as a draft item after it’s saved with its changes to the server.
Figure 4. Draft Item Edit Form

The Draft Item Edit Form
Notice the similarity between the Draft Item Edit form (Figure 4) and the standard Edit form (Figure 2) in this app. The editing experience for items as draft items should be about the same as the editing experience for items in the context of the Edit form.