Delivering Notifications using Windows Azure and Windows Push Notification Service

Over the past little while I have had the pleasure of building the Windows Azure Toolkit for Windows 8. The following is a re-post of my official post on the Windows Azure Blog.

The Windows Azure Toolkit for Windows 8 is designed to make it easier for developers to create a Windows Metro style application that can harness the power of Windows Azure Compute and Storage. It includes a Windows 8 Cloud Application project template for Visual Studio that makes it easier for developers to create a Windows Metro style application that utilizes services in Windows Azure. This template generates a Windows Azure project, an ASP.NET MVC 3 project, and a Windows Metro style JavaScript application project.  Immediately out-of-the-box the client and cloud projects integrate to enable push notifications with the Windows Push Notification Service (WNS). In Addition, the Windows Azure project demonstrates how to use the WNS recipe and how to leverage Windows Azure Blob and Table storage.

The Windows Azure Toolkit for Windows 8 is available for download.

Push Notification Cloud Service Architecture

For those of you who are familiar with working with Windows Phone 7 and the Microsoft Push Notification Service (MPNS), you will be happy to know that the Windows Push Notification service (WNS) is quite similar. Let’s take a look at a birds-eye architectural view of how WNS works.

Windows Push Notification Service and Windows Azure

The process of sending a notification requires few steps:

  1. Request a channel. Utilize the WinRT API to request a Channel Uri from WNS.  The Channel Uri will be the unique identifier you use to send notifications to an application instance.
  2. Register the channel with your Windows Azure cloud services. Once you have your channel you can then store your channel and associate it with any application specific data (e.g user profiles and such) until your services decide that it’s time to send a notification to the given channel
  3. Authenticate against WNS. To send notifications to your channel URI you are first required to Authenticate against WNS using OAuth2 to retrieve a token to be used for each subsequent notification that you push to WNS.
  4. Push notification to channel recipient. Once you have your channel, notification payload and WNS access token you can then perform an HttpWebRequest to post your notification to WNS for delivery to your client.

Fortunately, the Windows Azure Toolkit for Windows 8 accelerates development by providing a set of project templates that enable you to start delivering notifications from your Windows Azure cloud service with a simple file new project experience.  Let’s take a look at the toolkit components.

Toolkit Components

The Windows Azure Toolkit for Windows 8 contains a rich set of assets including a Dependency Checker, Windows Push Notification Service recipe, Dev 11 project templates, VS 2010 project templates and Sample Applications.

Dependency Checker

The dependency checker is designed to help identify and install those missing dependencies required to develop both Windows Metro style apps on and Windows Azure solutions on Windows 8.

Windows Push Notification Service and Windows Azure

Dev 11 Windows Metro style app

The Dev 11 Windows Metro style app provides a simple UI and all the code required to demonstrate how to request a channel from WNS using the WinRT API.  For example, the following listing requests a Channel URI from WNS:

var push = Windows.Networking.PushNotifications;
var promise = push.PushNotificationChannelManager.createPushNotificationChannelForApplicationAsync();

promise.then(function (ch) {
var uri = ch.uri;
var expiry = ch.expirationTime;
updateChannelUri(uri, expiry);
});

Once you have your channel, you then need to register this channel to your Windows Azure cloud service. To do this, the sample app calls into updateChannelUri where we construct a simple JSON payload and POST this up to our WCF REST service running in Windows Azure using the WinJS.xhr API.

function updateChannelUri(channel, channelExpiration) {
if (channel) {
var serverUrl = "https://myservice.com/register";
var payload = { Expiry: channelExpiration.toString(),
URI: channel };

var xhr = new WinJS.xhr({
type: "POST",
url: serverUrl,
headers: { "Content-Type": "application/json; charset=utf-8" },
data: JSON.stringify(payload)
}).then(function (req) { … });
} }

VS 2010 Windows Azure Cloud Project Template

The Windows Azure Cloud project provided by the solution demonstrates several assets for building a Windows Azure service for delivering push notifications.  These assets include:

1.  A WCF REST service for your client applications to register channels and demonstrates how to store them in Windows Azure Table Storage using a TableServiceContext. In the following code listing you can see the simple WCF REST interface exposed by the project.

[ServiceContract]
public interface IWNSUserRegistrationService
{
[WebInvoke(Method = "POST", BodyStyle = WebMessageBodyStyle.Bare)]
void Register(WNSPushUserServiceRequest userChannel);

[WebInvoke(Method = "DELETE", BodyStyle = WebMessageBodyStyle.Bare)]
void Unregister(WNSPushUserServiceRequest userChannel);
}

2.  An ASP .NET MVC 3 portal to build and send Toast, Tile and Badge notifications to clients using the WNS recipe.

Send notifications using the Windows Push Notification Service and Windows Azure

3.  An example of how to utilize Blob Storage for Tile and Toast notification images.

Using Windows Azure Blob Storage for Tiles and Toast notifications

4.  A Windows Push Notification Recipe used by the portal that provides a simple managed API for authenticating against WNS, constructing payloads and posting the notification to WNS.

using Windows.Recipes.Push.Notifications;
using Windows.Recipes.Push.Notifications.Security;

...

//Construct a WNSAccessTokenProvider which will accquire an access token from WNS
IAccessTokenProvider _tokenProvider = new WNSAccessTokenProvider("ms-app%3A%2F%2FS-1-15-2-1633617344-1232597856-4562071667-7893084900-2692585271-282905334-531217761", "XEvTg3USjIpvdWLBFcv44sJHRKcid43QXWfNx3YiJ4g");

//Construct a toast notification for a given CchannelUrl
var toast = new ToastNotification(_tokenProvider)
{
ChannelUrl = "https://db3.notify.windows.com/?token=AQI8iP%2OtQE%3d";
ToastType = ToastType.ToastImageAndText02;
Image = "https://127.0.0.1/devstoreaccount1/tiles/WindowsAzureLogo.png";
Text = new List<string> {"Sending notifications from a Windows Azure WebRole"};
};

//Send the notification to WNS
NotificationSendResult result = toast.Send();

5.  As you can see the Windows Push Notification Recipe simplifies the amount of code required to send your notification down to 3 lines.

The net end result of each of these assets is a notification as demonstrated in the below screenshot of a Toast delivered using the Windows Azure Toolkit for Windows 8.

Sending Toast notifications on Windows 8

As an exercise, it is recommended to spend some time using the website to explore the rich set of templates available to each of the Toast, Tile and Badge notification types.

Sample applications

At present there are also two sample applications included in the toolkit that demonstrate the usage of other Windows Azure features:

  1. PNWorker: This sample demonstrates how you can utlize Windows Azure Storage Queues to offload the work of delivering notifications to a Windows Azure Worker Role.  For more details please see the CodePlex documentation.
  2. ACSMetroClient: An example of how to use ACS in your Windows Metro style applications.  For more details please see this post by Vittorio Bertocci.
  3. Margie’s Travel: As seen in the demo keynote by John Shewchuk, Margie’s Travel is a sample application that shows how a Metro style app can work with Windows Azure. For more details please see this post by Wade Wegner. This sample application will ship shortly after the //build conferene.

Summary

The Windows Azure Toolkit for Windows 8 provides developers a rich set of re-useable assets that demonstrate how to start using Windows Azure quickly from Metro style applications in Windows 8.  To download the toolkit and see a step by step walkthrough please see the Windows Azure Toolkit for Windows 8.

Please feel free to subscribe to my RSS or follow me on twitter at @cloudnick.

Windows Phone 7 Navigation is not allowed when the task is not in the foreground

I hit the following issue bouncing around an app that utilised the SmsComposeTask today.  I have simplified the code to the following for demonstration purposes:

private void OnAdClick(object sender, MouseButtonEventArgs e)
{
   SmsComposeTask sms = new SmsComposeTask();
   sms.To = "111";
   sms.Body = "blah";  �
   sms.Show();
}

Issue:

When clicking the button that creates the SMS compose task in quick succession the following occurs.

InvalidOperationException

Navigation is not allowed when the task is not in the foreground. Error: -2147220989

at Microsoft.Phone.Shell.Interop.ShellPageManagerNativeMethods.CheckHResult(Int32 hr)
at Microsoft.Phone.Shell.Interop.ShellPageManager.NavigateToExternalPage(String pageUri, Byte[] args)
at Microsoft.Phone.Tasks.ChooserHelper.Navigate(Uri appUri, ParameterPropertyBag ppb)
at Microsoft.Phone.Tasks.SmsComposeTask.Show()
at Demo.OnAdClick(Object sender, MouseButtonEventArgs e)
at System.Windows.CoreInvokeHandler.InvokeEventHandler(Int32 typeIndex, Delegate handlerDelegate, Object sender, Object args)
at MS.Internal.JoltHelper.FireEvent(IntPtr unmanagedObj, IntPtr unmanagedObjArgs, Int32 argsTypeIndex, String eventName)
at Microsoft.Xna.Framework.Input.UnsafeNativeMethods.CallWindowProc(IntPtr lpPrevWndFunc, IntPtr hWnd, UInt32 msg, IntPtr wParam, IntPtr lParam)
at Microsoft.Xna.Framework.Input.SafeNativeMethods.CallWindowProc(IntPtr lpPrevWndFunc, IntPtr hWnd, UInt32 msg, IntPtr wParam, IntPtr lParam)
at Microsoft.Xna.Framework.Input.WindowMessageHooker.Hook.WndProc(IntPtr msgWnd, UInt32 msg, IntPtr wParam, IntPtr lParam)

Cause:
The cause is that the first click will launch the SmsComposeTask causing the application to be deactivated i.e sent to the background, with the second click being handled after the deactivated event has occured so its just a matter of cleaning up your click handler.

Kind Regards,
Nick Harris

35ºC, 95ºF bedroom is like a sauna today, difficult to concerntrate, bring on autumn:)

Check if a Capability is enabled in WMAppManifest on Windows Phone 7

While Microsoft provides the handy Capability Detection Tool to determine what Capabilities are required for your application there are scenarios, one being that you are a provider windows phone 7 library, where you may want to check at runtime what capabilities are present in the WMAppManifest.xml. This post will provide a helper class to check each if each capability is present.

Note: This post leverages the great work done by Nick Randolph to gain access to the WMAppManifest.xml for access to the Application Product ID in his blogpost here

The helper class is as follows:

using Microsoft.Xna.Framework;
using System.Linq;
using System.Xml.Linq;
using System.Collections.Generic;

namespace www.NickHarris.NET
{
    public static class CapabilityHelper
    {
        private const string WMAppManifest = "WMAppManifest.xml";
        private const string ID_CAP_NETWORKING = "ID_CAP_NETWORKING";
        private const string ID_CAP_IDENTITY_DEVICE = "ID_CAP_IDENTITY_DEVICE";
        private const string ID_CAP_IDENTITY_USER = "ID_CAP_IDENTITY_USER";
        private const string ID_CAP_LOCATION = "ID_CAP_LOCATION";
        private const string ID_CAP_SENSORS = "ID_CAP_SENSORS";
        private const string ID_CAP_MICROPHONE = "ID_CAP_MICROPHONE";
        private const string ID_CAP_MEDIALIB = "ID_CAP_MEDIALIB";
        private const string ID_CAP_GAMERSERVICES = "ID_CAP_GAMERSERVICES";
        private const string ID_CAP_PHONEDIALER = "ID_CAP_PHONEDIALER";
        private const string ID_CAP_PUSH_NOTIFICATION = "ID_CAP_PUSH_NOTIFICATION";
        private const string ID_CAP_WEBBROWSERCOMPONENT = "ID_CAP_WEBBROWSERCOMPONENT";
        private const string CAPABILITIES = "Capabilities";
        private const string NAME = "Name";

        static CapabilityHelper()
        {
            using (var strm = TitleContainer.OpenStream(WMAppManifest))
            {
                var xml = XElement.Load(strm);
                var capabilities = xml.Descendants(CAPABILITIES).Elements();

                IsNetworkingCapability = CheckCapability(capabilities, ID_CAP_NETWORKING);
                IsDeviceIdentityCapability = CheckCapability(capabilities, ID_CAP_IDENTITY_DEVICE);
                IsUserIdentityCapability = CheckCapability(capabilities, ID_CAP_IDENTITY_USER);
                IsLocationCapability = CheckCapability(capabilities, ID_CAP_LOCATION);
                IsSensorsCapability = CheckCapability(capabilities, ID_CAP_SENSORS);
                IsMicrophoneCapability = CheckCapability(capabilities, ID_CAP_MICROPHONE);
                IsMediaLibCapability = CheckCapability(capabilities, ID_CAP_MEDIALIB);
                IsGamerServicesCapability = CheckCapability(capabilities, ID_CAP_GAMERSERVICES);
                IsPhoneDialerCapability = CheckCapability(capabilities, ID_CAP_PHONEDIALER);
                IsPushNotificationCapability = CheckCapability(capabilities, ID_CAP_PUSH_NOTIFICATION);
                IsWebBrowserComponentCapability = CheckCapability(capabilities, ID_CAP_WEBBROWSERCOMPONENT);
            }
        }

        public static bool IsNetworkingCapability { get; set; }
        public static bool IsDeviceIdentityCapability { get; set; }
        public static bool IsUserIdentityCapability { get; set; }
        public static bool IsLocationCapability { get; set; }
        public static bool IsSensorsCapability { get; set; }
        public static bool IsMicrophoneCapability { get; set; }
        public static bool IsMediaLibCapability { get; set; }
        public static bool IsGamerServicesCapability { get; set; }
        public static bool IsPhoneDialerCapability { get; set; }
        public static bool IsPushNotificationCapability { get; set; }
        public static bool IsWebBrowserComponentCapability { get; set; }

        private static bool CheckCapability(IEnumerable<XElement> capabilities, string capabilityName)
        {
            var capability = capabilities.FirstOrDefault(n => n.Attribute(NAME).Value.Equals(capabilityName));
            return capability != null;
        }     Â
    }
}

Note: You will need to add a reference to the Microsoft.XNA.Framework.dll for the helper to build. If you are concerned about the warning that shows up when adding a reference to this assembly from within a Silverlight application, rest assured, that it is possible to utilize most, but not all, XNA assemblies in a WP7 Silverlight Application as documented here on msdn. Scroll down to the section Using Classes Across Frameworks to see exclusions.

Usage is then as simple as

   if (!CapabilityHelper.IsUserIdentityCapability)
     //capability not present, do something

Enjoy,
Nick

Finding the Device Model on Windows Phone 7

Previously i detailed how to retrieve the Device Manufacturer and users anonymous id from the device here.  This post details how to capturing the Device Model on Windows Phone 7.

Step 1: Add the ID_CAP_IDENTITY_DEVICE capability to your WMAppManifest.xml

<Capabilities>
   <Capability Name="ID_CAP_IDENTITY_DEVICE"/>
   ...
</Capabilities>

Step 2: Use the DeviceName with DeviceExtendedProperties TryGetValue method as follows:

public static string GetDeviceModel()
{
   string model = null;
   object theModel = null;

   if (Microsoft.Phone.Info.DeviceExtendedProperties.TryGetValue("DeviceName", out theModel))
      model = theModel as string;      

   return model
}

The resulting Model of my device is T8697.

If you are interested in how to access your Applications ProductID, yep the one that can change during marketplace certification please see Nick Randolphs Blog here.

Enjoy,
Nick

How to Reverse Geocode a Location to an Address on Windows Phone 7

I have had a few people recently ask me how to reverse geocode a location to an address on Windows Phone 7.  The answer should be straightfoward but at the moment thats not quite true.

So whats the straightforward answer supposed to be?

Well glad you asked :) It should be to use the CivicAddressResolver class and its ResolveAddressAsync method

Ok so whats the Gotcha?

When you try to resolve an address CivicAddressResolver all you get back is a empty CivicAddress.  Yep the resolve method is not implemented.

So what options do you have?

  1. Wait for the CivicAddressResolver to be implemented. Note: At the time of writing I have not heard of when it will be released.
  2. Use the Bing Maps API to perform your reverse geocode in one of the following ways
    • Add a service reference from your WP7 project to the Bing Maps SOAP API <–  Requires Bing Maps Key to be supplied in code so not ideal as we want to protect this.
    • Call the Bing Maps REST API from within your WP7  project <–  Requires Bing Maps Key to be supplied in code so not ideal as we want to protect this.
    • Either of the above two options but access your  Bing Maps Key on a service and request it over SSL for device use – ensures XAP  does not contain key but still returning it to the device over the wire
    • Proxy the call to the Bing Maps REST API in your own WCF Service so your Bing Maps Key never hits the device <– ideal method until CivicAddressResolver is implemented.

If you would prefer grab the code then reading through here it is – You can download the code from this linked page

The remainder of this post will detail how to go about implementing the last option above i.e Proxy the call to your own service so that the Key is not available on the WP7 device.

Creating the WCF Service

  1. In Visual Studio 2010 File –> Add New Project –> Select WCF Service Application
  2. Name the Project “ReverseGeocodeService”
  3. On ReverseGeocodeService Right Click –> Add Reference –> Select System.Configuration (we will use this for retrieving your Bing Maps key from configuration).
  4. Set the project to use the local IIS webserver as the host.  On ReverGeocodeService Right Click –> Properties –> Web –> select Use Local IIS Web server and provide a project URL of http://localhost/ReverseGeocodeService
  5. Create the following Folder structure and add the following files within your service
  6. Create your BingLocationResponse class as follows:
  7. Note this class details the how to serialize and deserialize a response from the Bing Maps Rest API and we will use it deserialize the JSON response from the Bing Maps REST API and then also use it both to return a response to the WP7 client

    using System.Runtime.Serialization;
    
    namespace ReverseGeocodeService.Contracts.Data
    {
        [DataContract]
        public class BingLocationResponse
        {
            [DataMember]
            public string authenticationResultCode { get; set; }
            [DataMember]
            public string brandLogoUri { get; set; }
            [DataMember]
            public string copyright { get; set; }
    
            [DataMember]
            public ResourceSet[] resourceSets { get; set; }
    
            [DataMember]
            public string statusCode { get; set; }
            [DataMember]
            public string statusDescription { get; set; }
            [DataMember]
            public string traceId { get; set; }
    
            [DataContract]
            public class ResourceSet
            {
                [DataMember]
                public int estimatedTotal { get; set; }
    
                [DataMember]
                public Resource[] resources { get; set; }
    
                [DataContract(Namespace = "http://schemas.microsoft.com/search/local/ws/rest/v1", Name = "Location")]
                public class Resource
                {
                    [DataMember]
                    public string __type { get; set; }
    
                    [DataMember]
                    public double[] bbox { get; set; }
    
                    [DataMember]
                    public string name { get; set; }
    
                    [DataMember]
                    public Point point { get; set; }
    
                    [DataContract]
                    public class Point
                    {
                        [DataMember]
                        public string type { get; set; }
    
                        [DataMember]
                        public string[] coordinates { get; set; }
                    }
    
                    [DataMember]
                    public Address address { get; set; }
    
                    [DataContract]
                    public class Address
                    {
                        [DataMember]
                        public string addressLine { get; set; }
                        [DataMember]
                        public string adminDistrict { get; set; }
                        [DataMember]
                        public string adminDistrict2 { get; set; }
                        [DataMember]
                        public string countryRegion { get; set; }
                        [DataMember]
                        public string formattedAddress { get; set; }
                        [DataMember]
                        public string locality { get; set; }
                        [DataMember]
                        public string postalCode { get; set; }
                    }
    
                    [DataMember]
                    public string confidence { get; set; }
    
                    [DataMember]
                    public string entityType { get; set; }
                }
            }
        }
    }
  8. Next Define the Interface for your WCF service IGeocodeService.cs and your Geocode Provider that will call out to Bing Maps
  9. IGeocodeService.cs:

    using System.ServiceModel;
    using ReverseGeocodeService.Contracts.Data;
    
    namespace ReverseGeocodeService.Contracts
    {
        [ServiceContract]
        public interface IGeocodeService
        {
            [OperationContract]
            string GetAddress(double latitude, double longitude);
    
            [OperationContract]
            BingLocationResponse GetFullAddress(double latitude, double longitude);
        }
    }
    

    IGeocodeProvider.cs:

    using System;
    using ReverseGeocodeService.Contracts.Data;
    namespace ReverseGeocodeService.Contracts
    {
        public interface IGeocodeProvider
        {
            BingLocationResponse ReverseGeocode(double latitude, double longitude);
        }
    }
  10. Implement your GeocodeProvider.cs
  11. This class will call out to the Bing Maps REST API return its deserialized its response. If you want to see what other options Bing Maps REST API provides you should use this as your starting point and more specifically for the Locations by Point we are using in this example see this

    using System;
    using System.Diagnostics;
    using System.Net;
    using System.Runtime.Serialization.Json;
    using ReverseGeocodeService.Contracts;
    using ReverseGeocodeService.Contracts.Data;
    
    namespace ReverseGeocodeService.Providers
    {
        public class GeocodeProvider : IGeocodeProvider
        {
            private readonly string _bingMapsKey;
            private string _bingMapsRESTUri = "https://dev.virtualearth.net/REST/v1/Locations/{0}?key={1}";
            public GeocodeProvider(string bingMapsKey)
            {
                _bingMapsKey = bingMapsKey;
            }        
    
            public BingLocationResponse ReverseGeocode(double latitude, double longitude)
            {
                BingLocationResponse result = null;
                string formattedLocation = string.Format("{0},{1}", latitude, longitude); //bing maps requires Lat,Long
                var request = HttpWebRequest.Create(string.Format(_bingMapsRESTUri, formattedLocation, _bingMapsKey)) as HttpWebRequest;
                try
                {
                    using (var response = request.GetResponse() as HttpWebResponse)
                    {
                        result = GetResult(response);
                    }
                }
                catch (Exception ex)
                {
                    Trace.WriteLine(ex.ToString());
                }
    
                return result;
            }
    
            private BingLocationResponse GetResult(HttpWebResponse response)
            {
                BingLocationResponse location = null;
                if (response != null && response.StatusCode == HttpStatusCode.OK)
                {
                    //Deserialize the response and provide the address to the callback action
                    using (var stream = response.GetResponseStream())
                    {
                        DataContractJsonSerializer serialiser = new DataContractJsonSerializer(typeof(BingLocationResponse));
                        location = serialiser.ReadObject(stream) as BingLocationResponse;
                    }
                }
    
                return location;
            }
        }
    }
  12. Finally implement your WCF Service GecodeService.svc.cs as follows
  13. Note that the BingKey is externalised into your config files appSettings, for the example to work you will need to supply your key to the config file.

    using System.Configuration;
    using System.Linq;
    using System.ServiceModel;
    using ReverseGeocodeService.Contracts;
    using ReverseGeocodeService.Contracts.Data;
    using ReverseGeocodeService.Providers;
    
    namespace ReverseGeocodeService
    {
        [ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall)]
        public class GeocodeService : IGeocodeService
        {
            //TODO: set your bingkey in config appsetting named BingKey
            private static IGeocodeProvider _geocodeProvider = new GeocodeProvider(ConfigurationManager.AppSettings["BingKey"]);
    
            public string GetAddress(double latitude, double longitude)
            {
                string formattedAddress = null;
                var geocodeResult = _geocodeProvider.ReverseGeocode(latitude, longitude);
                if (geocodeResult != null
                                && geocodeResult.resourceSets != null
                                && geocodeResult.resourceSets.Any()
                                && geocodeResult.resourceSets.First().resources != null
                                && geocodeResult.resourceSets.First().resources.Any())
                {
                    formattedAddress = geocodeResult.resourceSets.First().resources.First().address.formattedAddress;
                }
    
                return formattedAddress;
            }
    
            public BingLocationResponse GetFullAddress(double latitude, double longitude)
            {
                return _geocodeProvider.ReverseGeocode(latitude, longitude);
            }
        }
    }
  14. Add the appSettings section to your Web.config and supply your Bing Maps API Key
  15. <configuration>
     <appSettings>
        <add key="BingKey" value="Bing Maps Key Here"/>
      </appSettings>
      ...
    </configuration>

    Note: you should encrypt this Key using whatever standard practices you use.

Creating the Windows Phone 7 Client

Note: this is not an example of how to create an MVVM implementation on WP7  i.e we are focusing on how to reverse geocode a location to an address through the use of a proxy service  to protect your Bing Maps Key as such in this post the client code is kept straightforward to demonstrate usage.

  1. Add a Windows Phone 7 project File --> New Project --> Silverlight for Windows Phone --> Windows Phone Application
  2. Name the project ReverseGeocode
  3. On the project Right Click --> Add Service Reference --> Press Discover.
  4. You should see a window as in the following image.  Provide the Namespace ReverseGeocodeClient and Press OK
  5. Note: If this step is generating an Empty ServiceReferences.ClientConfig then delete the Service References and Service References.ClientConfig. Close all instances of Visual Studio. Open Visual Studio and try to add it again - it worked for me :) . If this still doesn't work then you will need to use SlSvcUtil.exe located in your Program Files (Program Files (x86)) for 64bit machines as follows:
    C:\Program Files (x86)\Microsoft SDKs\Windows Phone\v7.0\Tools\SlSvcUtil.exe" http://localhost/ReverseGeocodeService/GeocodeService.svc?wsdl
    Take the two files from the output and include them in your project and continue.

  6. Right click on the generated ServiceReferences.ClientConfig and set its Build Action to Content and Copy to Output Directory to Copy if newer
  7. Add a couple of buttons to perform calls out to the service in your MainPage.xaml and a TextBlock to display the result
  8. <phone:PhoneApplicationPage
        x:Class="ReverseGeocode.MainPage"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
        xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        mc:Ignorable="d" d:DesignWidth="480" d:DesignHeight="768"
        FontFamily="{StaticResource PhoneFontFamilyNormal}"
        FontSize="{StaticResource PhoneFontSizeNormal}"
        Foreground="{StaticResource PhoneForegroundBrush}"
        SupportedOrientations="Portrait" Orientation="Portrait"
        shell:SystemTray.IsVisible="True" Loaded="PhoneApplicationPage_Loaded">
    
        <!--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="MY APPLICATION" Style="{StaticResource PhoneTextNormalStyle}"/>
                <TextBlock x:Name="PageTitle" Text="page name" Margin="9,-7,0,0" Style="{StaticResource PhoneTextTitle1Style}"/>
            </StackPanel>
    
            <!--ContentPanel - place additional content here-->
            <Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
                <Button Click="btnGetAddress_Click" Content="Get Address" Height="72" HorizontalAlignment="Left" Margin="6,6,0,0" Name="button1" VerticalAlignment="Top" Width="270" />
                <TextBlock x:Name="txtAddress" Height="203" HorizontalAlignment="Left" Margin="12,163,0,0" Text="Address goes here" VerticalAlignment="Top" Width="425" />
                <Button Content="Get Full Response" Height="72" HorizontalAlignment="Left" Margin="5,72,0,0" Name="button2" VerticalAlignment="Top" Width="270" Click="btnGetFullResponse_Click" />
            </Grid>
        </Grid>
    
    </phone:PhoneApplicationPage>
  9. Update your codebehind, MainPage.xaml.cs to call out to your new Reverse geocoding service when buttons are clicked
  10. using System;
    using System.Linq;
    using System.Windows;
    using Microsoft.Phone.Controls;
    using ReverseGeocode.ReverseGeocodeClient;
    
    namespace ReverseGeocode
    {
        public partial class MainPage : PhoneApplicationPage
        {
            private GeocodeServiceClient _client;
            private const string NO_RESULT_FOUND = "No Result Found";
            // Constructor
            public MainPage()
            {
                InitializeComponent();
            }
    
            private void PhoneApplicationPage_Loaded(object sender, RoutedEventArgs e)
            {
               _client = new GeocodeServiceClient();
               _client.GetAddressCompleted += new EventHandler(client_GetAddressCompleted);
               _client.GetFullAddressCompleted += new EventHandler(_client_GetFullAddressCompleted);
            }
    
            private void btnGetAddress_Click(object sender, RoutedEventArgs e)
            {
                _client.GetAddressAsync(-33.796526, 151.138267);
            }        
    
            private void btnGetFullResponse_Click(object sender, RoutedEventArgs e)
            {
                _client.GetFullAddressAsync(-33.796526, 151.138267);
            }
    
            void client_GetAddressCompleted(object sender, GetAddressCompletedEventArgs e)
            {
                txtAddress.Text = e.Result ?? NO_RESULT_FOUND;
            }
    
            void _client_GetFullAddressCompleted(object sender, GetFullAddressCompletedEventArgs e)
            {
                string result = NO_RESULT_FOUND;
                if (e.Result != null
                                && e.Result.resourceSets != null
                                && e.Result.resourceSets.Any()
                                && e.Result.resourceSets.First().resources != null
                                && e.Result.resourceSets.First().resources.Any())
                {
                    var x = e.Result.resourceSets.First().resources.First();
                    result = string.Format ("Address:\n{0}\nConfidence:\n{1}\n", x.address.formattedAddress, x.confidence);
                }
    
                txtAddress.Text = result;
            }
        }
    }

    Note: I have not done it in this example however when you are finished with your _client you should always call _client.CloseAsync();

Summary:
And there you have it - in the absence of CivicAddressResolver this provides an implementation to reverse geocode a latitude and longitude into an Address on Windows Phone 7 without the need for your Bing Maps key to either be stored in the XAP or retrieved from a service by keeping your key within the context of your WCF proxy service.

You can download the code from this linked page - note you will have to add your Bing Maps Key to the web.config for it to work and also create the virtual directory on local host as detailed in the WCF service steps.

I hope this has helped you save some time :)

Enjoy,
Nick

Using TouchPanel for Gestures in Windows Phone 7

The Silverlight for Windows Phone Toolkit has some excellent controls, one of which is the GestureListener which makes implementing gesture support a very straightforward task.   However, as happens from time to time you’re hit with a scenario where you’re unable to depend on any third party content and as such I needed to add my own basic handling for gestures.  This is how you do it:

1. Add reference to Microsoft.Xna.Framework.Input.Touch.dll – Note: you will get a warning here that you can ignore for now.

2. In code behind  the gestures you want to listen for

The Gesture types you can enable are as follows Tap, DoubleTap, Hold, HorizontalDrag, VerticalDrag, FreeDrag, Pinch, Flick, DragComplete, PinchComplete

In my case I only want to deal with Flick, Hold and Tap therefore add the following:
TouchPanel.EnabledGestures = GestureType.Flick | GestureType.Hold | GestureType.Tap;

3. Limiting the scope for the gestures.

I want the gestures to be captured only when performed in a given for a StackPanel.  To do this I handle the ManipulationCompleted event on the StackPanel as follows:

<StackPanel ManipulationCompleted="StackPanel_ManipulationCompleted">

...

</StackPanel>

Note: if you wanted the gesture available to the content of the whole page then you would handle the ManipulationCompleted event of the PhoneApplicationPage.

4. Add handling code as follows


private void StackPanel_ManipulationCompleted(object sender, ManipulationCompletedEventArgs e)
{
   //Check if touch Gesture is available
   if (TouchPanel.IsGestureAvailable)
   {
      // Read the gesture so that you can handle the gesture type
      GestureSample gesture = TouchPanel.ReadGesture();
      switch (gesture.GestureType)
     {
         case GestureType.Flick:
         case GestureType.Hold:
            ToggleToolbarVisibility();
         break;
         case GestureType.Tap:
            Navigate();
         break;
         default:
            //do something
         break;
      }
   }
}

Enjoy,

Nick

MarketplaceReviewTask

A question come up on the App Hub forums a few days back and I thought it would be an interesting one to talk through.  The question was along the following lines:

  1. Is there a way to capture reviews of your application?
  2. What is the general user a opinion about ‘Nag’ screens that pop up asking for a product review as seen in some iPhone Applications
  3. What is an acceptable method when asking for Reviews i.e ‘Nag’ screen verse passive link

To answer these questions we need to look at the MarketplaceReviewTask and Windows Phone 7 Application Certification Requirements. - Note you can always find the latest version of the application certification requirements over on App Hub 

  1. Is there a way to capture reviews of your application?
  2. First I some people may wonder why would you want a review, well – allowing users to review your application will help (well depends on the review :) ) you stand out in what will eventually become a very competitive marketplace.  Good reviews will ultimately help to drive downloads of your application.   To provide the user the ability to review you application the Microsoft.Phone.Tasks namespace provides the  MarketplaceReviewTask specifically for this reason.  Now before we jump to the code for implementing this I think it is first important to talk through the remainder of the questions.

  3. What is the general user a opinion about ‘Nag’ screens that pop up asking for a product review as seen in some iPhone Applications?
  4.  I for one am definitely not keen on nag screens of any description, for the simple reason that they can be annoying and can turn someone from enjoying their in app experience and by extension, for many non tech users, their windows phone 7 experience.

  5. What is an acceptable method when asking for Reviews i.e ‘Nag’ screen verse passive link
  6. The general rule of thumb here is that the Windows Phone 7 Application Certification Requirements detail whats ok and whats not ok.  But like any document its difficult to put in every single thing upfront and as a result documents evolve over time.  Currently there is no mention of MarketplaceReviewTask nag screens.  However as a developer I would urge you not to take such a path.  On one hand you may get your reviews through brute force nagging but on the otherhand you may just frustrate your users until they uninstalling the app (I would) or writing a bad review.  In my opinion as devs we need to keep in mind that applications play a big role in the success of WP7 adoption.  If we can have happy rather then frustrated users then we are in a good position to continue moving forward.   Thats why i like the suggestion in the forum of using a passive Submit a Review link in the application and  here is how you would implement it in code.

To implement you use the Microsoft.Phone.Tasks; namesplace and the MarketplaceReviewTask.

In your page add the following XAML:

<HyperlinkButton Content="Submit A Review" Height="30" HorizontalAlignment="Left" Margin="225,665,0,0" Name="btnSubmitReview" VerticalAlignment="Top" Width="200" Click="btnSubmitReview_Click" />

In the code code behind of your page add:

using Microsoft.Phone.Tasks;

and add code to your handler as follows:

private void btnSubmitReview_Click(object sender, RoutedEventArgs e)
{
     MarketplaceReviewTask review = new MarketplaceReviewTask();
     review.Show();
}

The end result is as follows:

MarketplaceReviewTask

MarketplaceReviewTask

Note:
1. The MarketplaceReviewTask will cause your application to be tombstoned so you will need to add handing for that.
2. Running this in the emulator will display a warning/error dialog.

Enjoy,
Nick :)

Windows Phone 7 WebBrowser control Script Notify

Recently I was talking to someone who was just starting to look at implementing Push Notifications in one of their Windows Phone 7 applications – specifically for the Raw Notification functionality.

After a little bit of probing i found that the desired sequence that was intended to be used was along the following lines

  1. Application is used to customise an order
  2. WebBrowser Control used for taking payment
  3. Successful Payment must redirect to a succes page in the WebBrowser control
  4. Server to send Push notification – Raw so the application could acknowledge successful payment and transition the UI away from the WebBrowser control.

Now Push Notifications are indeed cool, I played around with them in the April CTP and created a Push Notification API and another post on how to receive notifications on the WP7 client (update for the current RTM in progress – more on this later). This took some time as it was early days for WP7 and there was not much documentation to create the first cut of the API. The main point to note here is it took time and adds an extra layer of complexity to your solution.

Now like any other tech decision as much as the uber cool, funky, and shiney factor seems to influence people its also important to consider – fit for use.
And as a result the scenario above was updated to use ScriptNotify of the WebBrowser control to remove the overhead of implementing Push Notifications – Raw. The updated sequence now becomes:

  1. Application is used to customise an order
  2. WebBrowser Control used for taking payment
  3. Successful Payment must redirect to a succes page in the WebBrowser control
  4. Success page uses javascript to perform a Notify to the WebBrowser control to transition the UI.

The following has been simplified to demonstrate the key code that required to do this:

  1. On the WebBrowser control set IsScriptEnabled to true and provide a handler for ScriptNotify
  2. <phone:WebBrowser IsScriptEnabled="True"  ScriptNotify="webBrowser_ScriptNotify" HorizontalAlignment="Left" Name="webBrowser"  VerticalAlignment="Top"/>
  3. handle the notify event in the code behind
  4.         private void webBrowser_ScriptNotify(object sender, NotifyEventArgs e)
            {
                int orderId = 0;
                int.TryParse(e.Value, out orderId);
    
                if(orderId > 0)
                    //TODO: transition
            }
  5. In the HTML of the success page that is redirected to in step 3 perform a JS notify as follows:
              window.external.notify("123")

Enjoy,

Nick

Windows Phone 7 HttpWebRequest returns same response from Cache

I hit an problem doing some dev on Windows Phone 7 several weeks back and have since helped a couple people out in the old windows phone 7 forum and LinkedIn Windows Phone 7 user group with the same thing.  This issue, is in fact, not an issue – it is more of a lack of knowledge that the HttpWebRequest and WebClient cache responses for your web request.  Let me tell you that client side caching is a good thing that will save you bandwidth and also speed up the percieved user experience when requesting the same resource.  So probably best to get straight to where problems can be faced. 

If you are performing a HttpWebRequest to a uri like so:

string someUri = "http://someservice/service/GetContent/userid/";
HttpWebRequest request = HttpWebRequest.CreateHttp(someUri);           Â
request.Accept = "application/json"; 

request.BeginGetResponse(OnGetSomeontentCallback, new GetSomeContentState(request, onResultAction));Â

Now lets pretend your service implementation has some smarts around what the user has seen historicaly and returns a different image each time you request the same uri.  You will find on your second call to the service that the same image is displayed in your app as the first call and then you might scratch your head *scratch*, check your service logic – all looks ok, set a breakpoint in your service and bang – the breakpoint is not being hit in the service on the second call and suddently is becomes clear the HttpWebRequest (and WebClient) for that matter will cache the response for that uri. 

So your faced with the following question – Should I change my service interface or change/remove the client side caching for this service operation?.   Well the answer is application specific and will fall into two categories:

1. Your client application knows what resource should be next

  • In this case you should alter your service interface to accept the Id of the item this way the result will be cached only for each specific item.

2. Your client has no idea what should be next as the server figures it out 

  • In this scenario i have identified 3 options – initialy i though you would be able to change the cache to expire or not cache on the HttpWebRequest instance but this is not in fact possible so you have to look at the uri and/or serverside.   Here are 3 options: 

Option 1: Add a random Query String to the end of your URI (think Guid.NewGuid()) this will avoid caching on the client as the Query String will be different each time

Option 2. Specify no cache in the OutgoingResponse header within your WCF service operation:


public ResultABC GetContent(string abc)
{
...
WebOperationContext.Current.OutgoingResponse.Headers.Add("Cache-Control", "no-cache");  //This line
...
return ...;
}

Option 3. markup your service operation with the AspNetCacheProfile attribute:

[AspNetCacheProfile("GetContent")]
public ResultABC GetContent(string abc)
{
...
...
return ...;
}

update your web.config


<system.web>
<caching>
     <outputCache enableOutputCache="true" />
     <outputCacheSettings>Â
        <outputCacheProfiles >Â
            <add name="GetContent" duration="0" noStore="true" location="Client" varyByParam="" enabled="true"/>Â
        </outputCacheProfiles>Â
    </outputCacheSettings>
</caching>
...
</system.web>

What did I choose:
Well all methods work to avoid the particular scenario given.  But in my case, the client application never knows what is next so I was forced to stop the client side cachingso the one I opted for was Option 3 as externalising this configuration means you can change at any later time without needing to rebuild and deploy your service.  

 If my client did know what the user was requesting then I would have gone for passing the ID in the uri to ensure that it is unique per each resource and that each resource would be cached on its unique uri.  Note here it is important for the ID to be passed on the URI it appears the URI forms the key for the cache i did see one example where the user was passing the ID in the Header collection e.g wc.Header["ID"] = id; and this was exhibiting the same problem.  Moving the ID to the URI solves the problem.

Hope this has been helpful,

Nick

 

Windows Phone 7 – How to find the device unique id windows live anonymous Id and manufacturer

This post details how to use DeviceExtendedProperties and UserExtendedProperties classes from the Microsoft.Phone.Info namespace to find a WP7 device manufacturer, device unique Id and users Anonymous Windows Live ID as follows:

To be able to obtain the Device Unique ID and Windows Live Anonymous ID you must first add the capabilities to do this to your WMAppManifest.xml file within your WP7 project as follows:

    <Capabilities>
      ...
      <Capability Name="ID_CAP_IDENTITY_DEVICE"/>
      <Capability Name="ID_CAP_IDENTITY_USER"/>
      ...
    </Capabilities>

One important point to note is that you should only really use the device unique id and anonymous windows live Id in your application if really requires it. The reasoning on msdn is as follows:

DeviceExtendedProperties requires the device identity capability. If your application uses this class, the user will be alerted that the application requires access to the device identity when viewing your application on Windows Phone Marketplace. For this reason, it is recommended that you use this class only if your application requires it.

Note: In the example below the manufacturer is returned from GetManufacturer without needing to list the device identity capability within the WMAppManifest but if you tried to get the device Id onr anonymous Id without the capability it will throw an UnauthorizedAccessException.

The following code example demonstrates how to retrieve the Device Manufacturer, Device Unique ID and Windows Live Anonymous ID

using Microsoft.Phone.Info;
namespace NickHarris.Net
{
    public static class ExtendedPropertyHelper
    {
        private static readonly int ANIDLength = 32;
        private static readonly int ANIDOffset = 2;
        public static string GetManufacturer()
        {
            string result = string.Empty;
            object manufacturer;
            if (DeviceExtendedProperties.TryGetValue("DeviceManufacturer", out manufacturer))
                result = manufacturer.ToString();

            return result;
        }

        //Note: to get a result requires ID_CAP_IDENTITY_DEVICE
        // to be added to the capabilities of the WMAppManifest
        // this will then warn users in marketplace
        public static byte[] GetDeviceUniqueID()
        {
            byte[] result = null;
            object uniqueId;
            if (DeviceExtendedProperties.TryGetValue("DeviceUniqueId", out uniqueId))
                result = (byte[])uniqueId;

            return result;
        }

        // NOTE: to get a result requires ID_CAP_IDENTITY_USER
        //  to be added to the capabilities of the WMAppManifest
        // this will then warn users in marketplace
        public static string GetWindowsLiveAnonymousID()
        {
            string result = string.Empty;
            object anid;
            if (UserExtendedProperties.TryGetValue("ANID", out anid))
            {
                if (anid != null && anid.ToString().Length >= (ANIDLength + ANIDOffset))
                {
                    result = anid.ToString().Substring(ANIDOffset, ANIDLength);
                }
            }

            return result;
        }
    }
}

Other Extended Device information that can be retrieved using this class are:

    DeviceName
    DeviceUniqueId – if you are after an id to identify the user you should not use this, use Microsoft.Phone.Info.UserExtendedProperties with a parameter of “ANID”
    DeviceFirmwareVersion
    DeviceHardwareVersion
    DeviceTotalMemory
    ApplicationCurrentMemoryUsage
    ApplicationPeakMemoryUsage

More details on these extended properties can be found here

Nick

Happy 10000 hits time to introduce the team

Hi there, 

Today the team here at www.NickHarris.net made it to the 10,000 hit milestone 

Hit Graph

Hit Graph

We decided to down tools and celebrate with a team photo: 

Team photo

Team photo

Thats an ROI of 10000/10 = 1000 per team member 

And of course the blog would not be complete without mentioning that the photo was taken using a MS lifecam and a custom Silverlight App using the System.Windows.Media namespace specifically CaptureSource, VideoCaptureDevice and CaptureSource.CaptureImageAsync method 

If you’re sad because this blog post is coming to an end and your just not sure what to do with yourself then I can recommend that you go checkout this excellent .NET Blog www.thatdotnetguy.com

Now time to get back to that unpaid job of mine :)  

Nick

Search Marketplace on Windows Phone 7 with the MarketplaceSearchTask

This describes the straightforward task of how to search Microsoft Marketplace on Windows Phone 7 using the MarketpalceSearchTask. You should note that you can search by keyword and ContentType. Two usage examples would be as follows:

Example 1: Search Marketplace for “Rock” that is of content type Music – allows interesting application scenarios to be developed particularly for record labels like SONY
Example 2: Search Marketplace for “Your Company Name” that is of content type Application – useful if you want users of your application to be able to find other applications by you/your company.

Implementing Example 1:

  1. Add using statement to the Tasks namespace
  2. using Microsoft.Phone.Tasks;
  3. Create an instance of MarketplaceSearchTask set the ContentType and SearchTerms properties then call Show:
  4.     MarketplaceSearchTask mst = new MarketplaceSearchTask();
        mst.ContentType = MarketplaceContentType.Music;
        mst.SearchTerms = "Rock";
        mst.Show();
  5. Result: The first screen is the result, with the second screen being the resul of clicking on Rock Classic 100.
  6. MarketplaceSearchTask search for Rock Music

    MarketplaceSearchTask search for Rock Music

Implementing Example 2:
To search for applications all you need to do is change the mst.ContentType = MarketplaceContentType.Applications; and update the Search property

Note: if you have a specific target in marketplace in mind you can use MarketplaceDetailTask.

Enjoy,
Nick

Asynchronous Image download on Windows Phone 7

When running your solution on local and setting the Source property of the System.Windows.Controls.Image it is not visually apparent that the Image may take some period of time to download simplifying the code the following the original image swap out whereby i was using an arbitrary Uri to an image in Azure Blob Storage that would change at a predefined interval.

imgContent.Source = new BitmapImage(new Uri(arbitraryImageUriThatKeepsChanging));

As soon as the image is available from Azure Blob Storage – or any other hosting provider for that matter if you are not using a CDN and are a long way from your host then or the image is of a large size then it is likely that as soon as the image is set the image content becomes empty until the image is downloaded – i found this to be 10 to 30 seconds over the slow bandwidth of my phone.  To have an empty Image control on the screen was not acceptable so the simple solution is to pull down the image asynchronously using a WebClient then once downloaded update the Image.Source as follows:

Starting the Async download of the image:

WebClient wc = new WebClient();
wc.OpenReadCompleted += new OpenReadCompletedEventHandler(wc_OpenReadCompleted);
wc.OpenReadAsync(new Uri(arbitraryImageUriThatKeepsChanging), wc);

Handling the completed download and updating the image source – note: have intentionally removed the MVVM implementation here to minimise code in post if using MVVM setup the binding on the Image.Source property to the model Source.

void wc_OpenReadCompleted(object sender, OpenReadCompletedEventArgs e)
{
   if (e.Error == null && !e.Cancelled)
  {
      try
      {
         BitmapImage image = new BitmapImage();
         image.SetSource(e.Result);
         imgContent.Source = image;
      }
      catch (Exception ex)
      {
          //Exception handle appropriately for your app
      }
  }
  else
  {
      //Either cancelled or error handle appropriately for your app
  }

Async download of Images from Azure to windows phone.

Note: WebClient executes on the UI thread if you wish to do this on a background thread and then later update the UI you should use HttpWebRequest and then Dispatcher within your response to update the UI thread.

Note: It would be interesting to configure the Azure CDN to see the improved performance once the node is distributed from a CDN node that is geographically close.

Nick

How to place a Phone Call on Windows Phone 7 using PhoneCallTask

This describes the straightforward task of how to place a Phone Call on Windows Phone 7 using PhoneCallTask.

  1. Add using statement to the Tasks namespace
  2. using Microsoft.Phone.Tasks;
    
  3. Create an instance of PhoneCallTask set the DisplayName and PhoneNumber properties then call Show:
  4.             PhoneCallTask pct = new PhoneCallTask();
                pct.DisplayName = "Nick Harris";
                pct.PhoneNumber = "+61000000000";
                pct.Show();
    
  5. Result
  6. PhoneCallTask

    PhoneCallTask

  7. Press Call
  8. PhoneCallTask

    PhoneCallTask

  9. After a bit of fiddling it appears you can do conference calls which is pretty neat.
  10. Press the Down Arrow
    Press the dial button from the ApplicationBar
    Enter your number and press Call
    Then press merge call and you got yourself a conference call.

    Conference Call

    Conference Call

    It would be cool if the PhoneCallTask allowed for a collection of Display Name and Phone Numbers to be supplied for programmatic conference call initiation.

Nick

How to SMS using the SmsComposeTask for Windows Phone 7

Unlike Windows Mobile with Windows Phone 7 we are currently unable to directly send an SMS from within an application unless we show the composer screen using SMSComposerTask.  This does suck a little bit as we now Aps cant send SMS’s direct or intercept them at all for that matter.  So what does WP7 offer – well to send an SMS you can use SMSComposerTask and to intercept an SMS – nothing, you will need to re-think your application architecture

To send an SMS using the composer in Windows Phone 7 you need to do the following:   

  1. Add using statement to Tasks namespace
  2. using Microsoft.Phone.Tasks;
    
  3. Create an instance of SMSComposerTask set the To field and body then call Show:
  4. SmsComposeTask sms = new SmsComposeTask();
    sms.To = "0123456789";
    sms.Body = "Some prefilled text...";
    sms.Show();
    
  5. Result
  6. SmsComposerTask Windows Phone 7

    SmsComposerTask Windows Phone 7

If your application really can’t live without direct access to send and intercept an SMS without any user interaction in the composer:   

  1. Then you can wait and they may add it in future releases although I am unsure if this will ever occur and I would not rely on it
  2. Or you may be able to rework your architecture to replace SMS with Push Notifications, a WCF service and Database or XML as your storage. The obvious downside to this is that you loose the telcos serverside infrastructure and have to replace it yourself. This adds a lot more code and cost to the developer for implementation and continual subscription costs for hosting which then means your cool free App idea may become too costly.

If your application can live without SMS interception but needs direct access to send and SMS without any user interaction in the composer:

  1. Then you may be able to use those options listed above
  2. Or, look into SMS providers that provide a web service based API – needless to say if you can’t figure out how to oncharge this to the end user this will also cost you but it may be significantly less then hosting depending on your volume

 Nick   Â
   

ASP .NET MVC datetime editor template using jQuery datepicker

This post details how to create an ASP .NET MVC editor template for the DateTime? datatype which will show a jQuery datepicker: 

  1.  Add the the jQuery UI js, css and Images to solution:
    • I have put the jQuery images and css under /Content/Images and /Content respectively.  
    • and the js under the /Scripts
  2. Update your site.master to reference the js and css:
    <link href="../../Content/Site.css" rel="stylesheet" type="text/css" />
    <link href="../../Content/jquery.ui.all.css" rel="stylesheet" type="text/css" />

    <script src="../../Scripts/jquery-1.4.1.min.js" type="text/javascript"/>
    <script src="../../Scripts/jquery.ui.core.js" type="text/javascript" />
    <script src="../../Scripts/jquery.ui.widget.js" type="text/javascript" />
    <script src="../../Scripts/jquery.ui.datepicker.js" type="text/javascript" />

    <script src="/Scripts/MicrosoftAjax.js" type="text/javascript" />
    <script src="/Scripts/MicrosoftMvcAjax.js" type="text/javascript" />
    <script src="/Scripts/MicrosoftMvcValidation.js" type="text/javascript" /> 
  1. Create the Editor Template:
    • Create the following folder structure if not already present Views/Shared/EditorTemplates/
    • Add a ViewUserControl called DateTime.ascx and use the following as thee content.
  2. Note: the htmlAttributes parameter is new { @class = “dp”}) this will render a class=’dp’ attribute for later use by jQuery to identify where datepickers are required

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<System.DateTime?>" %>
<%: Html.TextBox("",  String.Format("{0:yyyy-MM-dd}", Model.HasValue ? Model : DateTime.Today), new { @class = "dp"})%>
    Note: that I have set this to accept a nullable datetime – DateTime? and provided the default value of today if the date way empty.

  1.  JQuery datepicker script:
    • Place the following jQuery preferably in a Common.js or in the head of your page or site master wher ethe control will be displayed.
<script  type="text/javascript">
 $(document).ready(function () {
 $(".dp").datepicker({
             changeMonth: true,
             changeYear: true,
             dateFormat: 'yy-mm-dd'
                   });
                });
  </script>

Note: that we are searching for anything using the “.dp” class and supplying arguments to the datepicker to format the date and provide dropdowns for year and month.

At this point any defined view that you have that is rendering the DateTime should now show the editor as the DateTime.ascx editor template

E.g I have a view binding to my Model and one of the properties called StartDate is DateTime?  the view for displaying this model contains
    

 Â
<div class="editor-label">
    <%: Html.LabelFor(model => model.StartDate) %>
</div>
<div class="editor-field">
    <%: Html.EditorFor(model => model.StartDate)%>
    <%: Html.ValidationMessageFor(model => model.StartDate) %>
</div>

Upon rendering the DateTime editor template is applied and hey presto:

Nick

Debugging WCF Data Services – An error occured while processing this request

Issue: I was calling my WCF Data Service today and was getting a NotSupportedException with an InnerException of “An error occured while processing this request” and no further detail which is not very helpful for debugging.

Solution: So the workaround for this is to set UseVerboseErrors in your service in your service initialization method like so

public static void InitializeService(DataServiceConfiguration config)
{
   config.UseVerboseErrors = true;

  .....

}

This however is not good for production scenarios as we don’t want Verbose errors going back to clients.  Given that it is hardcoded to true ideally you would want to be able to configure this in your web.config.  There is no section within the web.config supported out of the box for this.  So you can use an appSetting or create your own custom section.

after building and deploying the service you should now get the full detail in your exception.InnerException.Message E.g

<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<error xmlns="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata">
  <code></code>
  <message xml:lang="en-AU">An error occurred while processing this request.</message>
  <innererror>
    <message>Unable to update the EntitySet 'Posts' because it has a DefiningQuery and no &lt;InsertFunction&gt; element exists in the &lt;ModificationFunctionMapping&gt; element to support the current operation.</message>
    <type>System.Data.UpdateException</type>
    <stacktrace>   at System.Data.SqlClient.SqlGen.DmlSqlGenerator.ExpressionTranslator.Visit(DbScanExpression expression)&#xD;
   at System.Data.SqlClient.SqlGen.DmlSqlGenerator.GenerateInsertSql(DbInsertCommandTree tree, SqlVersion sqlVersion, List`1&amp; parameters)&#xD;
   at System.Data.SqlClient.SqlProviderServices.CreateCommand(DbProviderManifest providerManifest, DbCommandTree commandTree)&#xD;
   at System.Data.Mapping.Update.Internal.UpdateTranslator.CreateCommand(DbModificationCommandTree commandTree)&#xD;
   at System.Data.Mapping.Update.Internal.DynamicUpdateCommand.CreateCommand(UpdateTranslator translator, Dictionary`2 identifierValues)&#xD;
   at System.Data.Mapping.Update.Internal.DynamicUpdateCommand.Execute(UpdateTranslator translator, EntityConnection connection, Dictionary`2 identifierValues, List`1 generatedValues)&#xD;
   at System.Data.Mapping.Update.Internal.UpdateTranslator.Update(IEntityStateManager stateManager, IEntityAdapter adapter)&#xD;
   at System.Data.Objects.ObjectContext.SaveChanges(SaveOptions options)&#xD;
   at System.Data.Services.Providers.ObjectContextServiceProvider.SaveChanges()&#xD;
   at System.Data.Services.DataService`1.HandleNonBatchRequest(RequestDescription description)&#xD;
   at System.Data.Services.DataService`1.HandleRequest()</stacktrace>
  </innererror>
</error>

Nick

Windows Phone 7 OData CTP, nope use REST and JSON

First things first the OData CTP for Windows Phone 7 is exactly that – a CTP - therefore expectations are not high but I did have them set at medium.  Due to the issues I was hitting i ended up having to use REST to consume my WCF data service then write a bunch of code to deserialize the JSON response. So this post will take you through how to do this.

But first, what issues did I encounter – well after being able to successfully to generate the service reference using dataservice util I was able to consume the service successfully and i felt yay big ticks this is awesome.  I then went to implement serverside paging, cool serverside paging out of the box thats pretty neat.  However when I went to then consume the next page from the service the Continuation Token was always null.  This ment that I was unable to navigate to the next page set with the link that should have been returned.  Note:  - if anyone managed to get this working with the CTP please drop me a link and/or some sample code.

Checking out Nicks blog – Windows Phone 7 Data: Json WCF Data Service with IIS 7 Compression I figured out how to construct a REST request using fiddler i tried by application/atom+xml and application/json – both results had the next link :( … With this in mind i really had to narrow down if it was the CTP or not – I created a .NET 4.0 win forms app and generated the service reference  and used the same code to consumed it successfully with the Continuation token being set.  At this point i settled that it is something was from the CTP….

After spending a lot of time trying to get to the bottom of it – using reflector to figure out where the error was coming from because the OData CTP source was not available i finaly decided to cut my losses and just consume the feed using REST.  Funnyly enough, but not so funny at the time, it took less time to write the data contract to deserialize the JSON then I spent fighting the CTP.  To do this I did the following:

Consume your service using REST to see the format of the returned data:

1. Construct request in this case to Posts and add the header accept: application/json then press execute

REST request to WCF Data service

REST request to WCF Data service

2. Take the response and format it using an online formatter to make life easy on yourself

{
   “d”:{
      “results”:[
         {
            "__metadata":{
               "uri":"http://localhost/TestServices/SomeService.svc/Posts(guid'ccb89791-a49b-4224-924d-0e0bc56d8ffb')",
               "type":"Model.Post"
            },
            "Id":"ccb89791-a49b-4224-924d-0e0bc56d8ffb",
            "Title":"some title",
            "Content":"Some long content"
         }
      ],
      “__next”:”http://localhost/TestServices/SomeService.svc/Posts?$skiptoken=guid’ccb89791-a49b-4224-924d-0e0bc56d8ffb’”
   }
}

3. Create your DataContract so that it can consume a response of the format above.  Note the nasty bit about this is the nesting of the above.  I created a generic representation of this so that this can be re-used for your other tables.  So this is what we are building:

Datacontract for JSON deserialization of WCF Data Service

Datacontract for JSON deserialization of WCF Data Service

and the code is as follows: Note the name of the data member maps to the sample JSON for the serialization / deserialization of the JSON content:

using System;
using System.Collections.Generic;
using System.Runtime.Serialization;

namespace JSON.DataContract
{
    [DataContract] //Gets the outer {}
    public class BaseJSONResult<T> where T : BaseMyContent
    {
        [DataMember(Name = "d")] //Gets the d
        public ComposedResult<T> Result { get; set; }
    }

    [DataContract]
    public partial class ComposedResult<T> where T : BaseMyContent
    {
        [DataMember(Name = "results")]
        public List<T> MyContents { get; set; }

        [DataMember(Name = "__next")]
        public string NextLinkUri { get; set; }
    }

    [DataContract(Name = "__metadata")]
    public class MetaData
    {
        [DataMember(Name = "uri")]
        public string Uri { get; set; }

        [DataMember(Name = "type")]
        public string Type { get; set; }
    }

    [DataContract]
    public abstract class BaseMyContent
    {
        [DataMember(Name = "__metadata")]
        public MetaData MetaData { get; set; }
    }

    public partial class Post : BaseMyContent
    {
        [DataMember]
        public Guid Id { get; set; }
        [DataMember]
        public string Title { get; set; }
        [DataMember]
        public string Content { get; set; }
    }

    [DataContract]
    public partial class SomeOtherClass : BaseMyContent
    {
        [DataMember]
        public Guid Id { get; set; }
        [DataMember]
        public string Name { get; set; }
        [DataMember]
        public string Caption { get; set; }
    }
}

Note: if you have more types you would like to extend the support to just derrive them from BaseMyContent like SomeOtherClass above.
Note: this example does not currently take care of Links between types… but you should easily be able to add support for this.

4. Usage: Use a web request and response and Json serializer to process the results

   HttpWebRequest request = HttpWebRequest.Create("http://localhost/TestServices/SomeService.svc/Posts") as HttpWebRequest;
   request.Accept = "application/json"; //atom+xml";
   request.BeginGetResponse(RequestCallback, request);

The callback where deserialization occurs, post will contain the deserialize content

private void OnRequestCallback(IAsyncResult result)
{
     var request = result.AsyncState as HttpWebRequest;
     var response = request.EndGetResponse(result);
     if (response != null)
     {
          var jsonSerializer = new DataContractJsonSerializer(typeof(BaseJSONResult<Post>)); //Note: DataContractJsonSerializer requires a reference to System.Servicemodel.Web

          BaseJSONResult<Post> post = null;
          using (var stream = response.GetResponseStream())
          {
               post = jsonSerializer.ReadObject(stream) as BaseJSONResult<Post>;
          }

          this.Dispatcher.BeginInvoke(() =>
             PostRetrieved(Post));
     }
}

Now finally you can get at a populated Next link with post.NextLinkUri *wipes teary eyes*

5. If you are having troubles and the deserialization is not working the best tip i can give you is to try serializing your datacontract and cross check it with the data returned from your service. To serialize it do the following:

              Â
var test = new BaseJSONResult<Post>()
{
    // Make sure you set all the properties
};
using (MemoryStream ms = new MemoryStream())
{
    jsonSerializer.WriteObject(ms, test);
    byte[] bytes = ms.ToArray();
    string json = Encoding.UTF8.GetString(bytes, 0, bytes.Length);
    Console.WriteLine(json); //Compare this with your fiddler result
}

I really can’t wait until the CTP comes out full form with the bugs fixed, it will save a lot of time, but for now I am keeping a wide berth with the intention of returning once it is out of CTP.

Consuming WCF Data Services on Windows Phone 7

I read a good post recently on Windows Phone 7 Data Json WCF Data Service with IIS 7 Compression by Nick Randolph blog.  I thought I would give it a go minus the IIS compression to see if I could quickly generate a service reference using the VS 2010 Express for Windows Phone April CTP.   I found that currently there were a number of issues with the OData Client Library for Windows Phone 7 Series CTP and adding service references to a WCF Data Service in the CTP of VS – rest assured I am sure they will get sorted by the time things go RTM.  In the mean time these are the steps I used:       

  1. Create your SQL Database, Entity Framework 4.0 model and WCF Data Service project – you can follow up to, but not including,  the IIS compression on Nicks Blog to get your service up and running.
  2. Create a new Windows Phone 7 Application project – File –> New Project –> Silverlight for Windows Phone –> Windows Phone Application
  3. Try to generate a service reference to your  WCF Data Service using right click add service reference.  With the current CTP this will fail against a fails giving  “This service cannot be consumed by the current project. Please check if the project target framework supports this service type.”.  To resolve this you need to use DataSvcutil via the command line to generate your client.
  4. cd C:\Windows\Microsoft.NET\Framework\v4.0.30319
    DataSvcutil.exe /uri:http://localhost/TestDataService/Test.svc/ /DataServiceCollection /Version:2.0 /out:D:\dev\TestSol\TestWCFDataService\Providers\TestClient.cs      

    Hint: drop this into a batch file for later ease of update as your service definition changes.     

       

  5. Include the generated file, TestClient.cs, into your solution and attempt to bulild.  You will find that the System.Data.Services.Client.dll assembly is not included in your project. On attempting to add it you will also find that it does not exist.  This is because that OData Client Library for Windows Phone 7 is still in CTP.  You can get the client from here
  6. Download and install the OData Client Library for Windows Phone 7 Series CTP .  After extracting and adding a reference to System.Data.Services.Client.dll your solution will build but when you try to run it it will start failing with an exception as follows “A first chance exception of type ‘System.IO.FileLoadException’ occurred in mscorlib.dll”.  Unfortunately apart from adding the service reference it was not quite obvious that the cause was the OData client assembly.  Tim Heur has a post explaining the issue and powershell script to resolve the issue in his post Windows Phone 7 Developer Tools April 2010 Refresh.  Follow the steps provided to fix the assembly and add a reference to the new generated assembly.
  7. Finally your solution should build and run.  Now all you need to do is wire up the data
    • Binding
      <Grid Grid.Row="1" x:Name="ContentGrid" Grid.Column="0">          Â
                  <ListBox ItemsSource="{Binding Patients}">              Â
                      <ListBox.ItemTemplate>
                          <DataTemplate>
                              <StackPanel Orientation="Horizontal">  Â
                                      <TextBlock Text="{Binding FirstName}" Margin="5,5,5,5"/>
                                      <TextBlock Text="{Binding LastName}" Margin="5,5,5,5"/>                      Â
                              </StackPanel>
                          </DataTemplate>
                      </ListBox.ItemTemplate>
                  </ListBox>          Â
              </Grid>
    • Code Behind
        public MainPage()
              {
                  InitializeComponent();
      
                  SupportedOrientations = SupportedPageOrientation.Portrait | SupportedPageOrientation.Landscape;
      
                  this.DataContext = ViewModel;
                  ViewModel.GetPatients(20);//future posts will look at using WCF serverside paging
              }
      
              private MainPageViewModel _viewModel;
      
              public MainPageViewModel ViewModel
              {
                  get
                  {
                      if (_viewModel == null)
                          _viewModel = new MainPageViewModel();
                      return _viewModel;
                  }
                  set { _viewModel = value; }
              }

        

    • using System;
      using System.Data.Services.Client;
      using System.Linq;  
      
      namespace TestDataService.ViewModels
      {
          public class MainPageViewModel
          {
              private TestClient _context;  
      
              public PatientViewModel()
              {
                  _context = new TestClient(new Uri("http://localhost/TestDataService/Test.svc/"));//Externalise url to isolated storage
                  Patients = new DataServiceCollection<Patient>();          Â
              }  
      
              public void GetPatients(int count)
              {
                   Patients.LoadAsync(_context.Patients.Take(count));         Â
              }  
      
              public DataServiceCollection<Patient> Patients{ get; set; }
          }
      
      } 

In summary, I cant wait until this goes RTM and becomes just as quick to work with as the serverside component.  I will look at WCF Data Service serverside paging and QueryInterceptors in a coming post.

How to receive Toast Tile and Raw Notifications on Windows Phone 7

Overview:

This post details how to receive Toast, Tile and Raw Notifications on Windows Phone 7 using Visual Studio 2010 CTP April  Refresh.  It assumes that you have already created a new:

Background:

What are push notifications? There three types of notifications you can send to a Windows Phone 7 device:

  1. Tile – A tile is a visual, dynamic representation of application specific state within the quick launch area of the phone’s start experience.  You can control the Image, Text and Count (“Badge”) of a tile.
  2. Toast – Displays as an overlay onto the user’s current screen, a bit like an outlook email notification popup that you can press that launches you into the application.  You can control the title and text of a Toast notification.
  3. Raw – provides the ability for your application service to push data to the application while it is in the foreground without the need for your application to poll the appplication service.

Creating a Notification Channel:

  1. Expand the Properties folder in your windows phone application project and open WMAppManifiest.xml
    •  Ensure that your <App node has the Publisher=”YourPublisherName” property defined.
    • Ensure that your Capabilities node is defned.  oOf specific importance is the ID_CAP_PUSH_NOTIFICATION
         <Capabilities>
            <Capability Name="ID_CAP_NETWORKING" />
            <Capability Name="ID_CAP_LOCATION" />
            <Capability Name="ID_CAP_SENSORS" />
            <Capability Name="ID_CAP_MICROPHONE" />
            <Capability Name="ID_CAP_MEDIALIB" />
            <Capability Name="ID_CAP_GAMERSERVICES" />
            <Capability Name="ID_CAP_PHONEDIALER" />
            <Capability Name="ID_CAP_PUSH_NOTIFICATION" />
            <Capability Name="ID_CAP_WEBBROWSERCOMPONENT" />
          </Capabilities>
  2. In the solution explorer right click on your windows phone application project and select Add a Reference… In the Add Reference window select Microsoft.Phone.Notification.dll and press Ok
  3. HttpNotificationChannel class, contained within the Microsoft.Phone.Notification namespace, is the key to creating a notification channel between the Microsoft Push Notification Service and the WP7 Push Client.   Before jumping into the full code  listing we will look at fundamental features of the the HttpNotificationChannel
    • HttpNotificationChannel _channel  = new HttpNotificationChannel(channelName, serviceName) – The Constructor accepts a channelName which is the identifying name of the notification channel and the serviceName which is the name of the cloud service that the notification channel is associated with
    • Static method HttpNotificationChannel.Find(string channelName) - Allows you to find a previously created notification channel.  This is particularly useful when the notification channel already exists and is not presently closed.
    • Instance property httpNotificationChannel.ChannelURI.  This is the unique URI identifying the current active notification channel
    • Instance method _channel.Open().  Opens the channel between the Push Client and Push Notification Service.  Note as documented in the April CTP the push client debugging has to wait for two minutes after boot of the emulator (or device) before using the APIs otherwise a NotificationChannelOpenException occurs
    • _channel.BindToShellEntryPoint(shellEntryPoint);  – Used to subscribe to Tile notifications the Image of the tile can be either a reference to a local resource image or a remote resource reference.  At the time of writing this post remote tile notifications are currently not working with the Microsoft Push Notification Service 
    •  _channel.BindToShellEntryPoint(); - Used to subscribe to Tile notification the Image of tile can only reference a local image resource . Note the local image must be within your project and have its Build Action set to content otherwise the tile will not display
    •  _channel.BindToShellNotification();  – Used to bind to Toast notifications that are shown when your application is not in the foreground
    •   _channel.ChannelUriUpdated += new EventHandler<NotificationChannelUriEventArgs>(channel_ChannelUriUpdated); - Subscribe to the ChannelUriUpdated event to capture the ChannelUri returned from the Microsoft Push Notification Service after Opening  your channel.  Note each time the application runs the NotificationChannel is not guaranteed to be identical to the previous launch therefore it is important that you utilise this handler to capture and submit your ChannelURI to your cloud service. 
    • _channel.HttpNotificationReceived += new EventHandler<HttpNotificationEventArgs>(channel_HttpNotificationReceived); – Subscribe to the HttpNotificationReceived event to handle Raw Notifications while your application is in the foreground
    • _channel.ShellEntryPointNotificationReceived += new EventHandler<NotificationEventArgs>(channel_ShellEntryPointNotificationReceived); - Subscribe to the ShellEntryPointNotificationReceived event to handle tile notifications sent while your application is in the foreground
    • _channel.ShellNotificationReceived += new EventHandler<NotificationEventArgs>(channel_ShellNotificationReceived); - Subscribe to the ShellNotificationReceived event to handle toast notifications sent while your application is in the foreground
    •  _channel.ExceptionOccurred += new EventHandler<NotificationChannelExceptionEventArgs>(channel_ExceptionOccurred); – Subscribe to this event as your catch all for when something goes wrong within the HttpNotificationChannel
  4. Add a new class to your project called NotificationProvider.cs and paste in the following
    The Code:

       using System;
    using System.Collections.Generic;
    using System.Diagnostics;
    using System.Threading;
    using System.Windows;
    using Microsoft.Phone.Notification;
    using Microsoft.Phone.Shell;
    
    namespace NickHarris.Net.Provider
    {
        public class NotificationProvider : IDisposable
        {
    
            private HttpNotificationChannel _channel;     �
            private string _channelName;
            private string _serviceName;    �
            private Timer _retrySubscribeTimer = null; //retry of 2 mins if just started emulator/rebooted device - April CTP issue
    
            public NotificationProvider(string channelName, string serviceName)
            {
                _channelName = channelName;
                _serviceName = serviceName;         �
            }
    
            public void Connect(object stateInfo)
            {  �
                Connect();
            }
    
            private void Connect(Action actionIfNotFound)
            {
                 try
                {
                    _channel = HttpNotificationChannel.Find(_channelName);
                }
                catch (NotificationChannelNotFoundException e)
                {
                    Debug.WriteLine(e.ToString());
                }
    
                if (_channel != null)
                {
                    if (_channel.ChannelUri != null)
                    {
                        SubscribeToChannelEvents();
                        RegisterChannel(_channel.ChannelUri);
                        SubscribeToNotifications();
                    }
                    else
                    {
                        _channel.UnbindToShellEntryPoint();
                        _channel.UnbindToShellNotification();
                        _channel.Close();
                        RetryChannelConnect();
                    }
                }
                else
                {
                    actionIfNotFound();
                }
            }
    
            public void Connect()
            {
                try
                {
                    Connect(() =>
                    {
                        _channel = new HttpNotificationChannel(_channelName, _serviceName);
                        SubscribeToChannelEvents();
                        _channel.Open();
                        SubscribeToNotifications();
                    });
    
                }
                catch (NotificationChannelOpenException ex)
                {
                    //2mins after restart - documented bug in April CTP
                    RetryChannelConnect();
                }
                catch (NotificationChannelExistsException e)
                {
                    //This exception occurs if the notification channel was previously created and is not closed.             �
                    Connect(() =>
                    {
                        RetryChannelConnect();
                    });
                }
            }
    
            private void SubscribeToNotifications()
            {
                try
                {
                    //Remote tiles - currently not working I think this is a MPNS issue
                    //ShellEntryPoint shellEntryPoint = new ShellEntryPoint();
                    //shellEntryPoint.RemoteImageUri = new Uri("http://www.nickharris.net/wp-content/uploads/2010/06/Background1.png", UriKind.Absolute);            �
                    //_channel.BindToShellEntryPoint(shellEntryPoint); // tile - remote
    
                    _channel.BindToShellEntryPoint(); // tile - local
                }
                catch (NotificationChannelExistsException)
                { } //do nothing - allready been subscribed to for current channel
    
                try
                {
                    _channel.BindToShellNotification(); // - toast
                }          �
                catch (NotificationChannelExistsException)
                { } //do nothing - allready been subscribed to for current channel
            }
    
            private void SubscribeToChannelEvents()
            {
                _channel.ChannelUriUpdated += new EventHandler<NotificationChannelUriEventArgs>(channel_ChannelUriUpdated); // channel URI returned from MPNS
                _channel.HttpNotificationReceived += new EventHandler<HttpNotificationEventArgs>(channel_HttpNotificationReceived); // Raw
                _channel.ShellEntryPointNotificationReceived += new EventHandler<NotificationEventArgs>(channel_ShellEntryPointNotificationReceived); // Tile
                _channel.ShellNotificationReceived += new EventHandler<NotificationEventArgs>(channel_ShellNotificationReceived); // Toast
                _channel.ExceptionOccurred += new EventHandler<NotificationChannelExceptionEventArgs>(channel_ExceptionOccurred);
            }
    
            void channel_ExceptionOccurred(object sender, NotificationChannelExceptionEventArgs e)
            {
                Debug.WriteLine(e.Exception.ToString());
                MessageBox.Show(e.Exception.ToString());
            }
    
            void channel_ShellNotificationReceived(object sender, NotificationEventArgs e)
            {
                if (e.Collection != null)
                {
                    Dictionary<string, string> collection = (Dictionary<string, string>)e.Collection;
                    System.Text.StringBuilder messageBuilder = new System.Text.StringBuilder();
                    foreach (string elementName in collection.Keys)
                    {
                        MessageBox.Show(string.Format("elementName:{0}, value:{1}", elementName, collection[elementName]));
                    }
                }
            }
    
            void channel_ShellEntryPointNotificationReceived(object sender, NotificationEventArgs e)
            {
                if (e.Collection != null)
                {
                    Dictionary<string, string> collection = (Dictionary<string, string>)e.Collection;
                    System.Text.StringBuilder messageBuilder = new System.Text.StringBuilder();
                    foreach (string elementName in collection.Keys)
                    {
                        MessageBox.Show(string.Format("elementName:{0}, value:{1}", elementName, collection[elementName]));
                    }
                }
            }
    
            void channel_HttpNotificationReceived(object sender, HttpNotificationEventArgs e)
            {
                if (e.Notification.Body != null)
                {
                    using (System.IO.StreamReader reader = new System.IO.StreamReader(e.Notification.Body))
                    {
                        Debug.WriteLine(reader.ReadToEnd());
                    }
                }
            }
    
            void channel_ChannelUriUpdated(object sender, NotificationChannelUriEventArgs e)
            {                         �
                RegisterChannel(e.ChannelUri);
            }
    
            private void RegisterChannel(Uri uri)
            {
                using (MyCloudServiceRegistrationProvider provider = new MyCloudServiceRegistrationProvider())
                {
                    provider.RegisterChannel(uri.ToString(), this.ActOnRegisterChannelComplete); �
                }
            }
    
            private void RetryChannelConnect()
            {
                if (_retrySubscribeTimer == null)
                    _retrySubscribeTimer = new Timer(new TimerCallback(this.Connect), null, 2 * 60 * 1000, Timeout.Infinite);
                else
                    _retrySubscribeTimer.Change(2 * 60 * 1000, -1);
            }
    
            public void ActOnRegisterChannelComplete(bool success)
            {
                if (!success)
                    RetryChannelConnect();//Try subscribe/find process to send channel again
            }
    
            public void Dispose()
            {
                //if (_channel != null)
                //{
                //    _channel.Dispose();
                //}
    
                if (_retrySubscribeTimer != null)
                    _retrySubscribeTimer.Dispose();
            }
        }
    
    // Note: I have performed some testing on the code provided and it works for toast,tile(local) and raw notifications.  I will post updates to this code as I find bugs.
    
    }
  5. I Subscribe to my channel notifications on Application_Startup within App.Xaml.cs
            private void Application_Startup(object sender, StartupEventArgs e)
            {
                provider = new NotificationProvider("yourchannelname", "yourcloudservice");
                provider.Connect();
            }
  6. Now all you need to do is learn How to send Tile, Toast and raw notifications to the Microsoft Push Notification Service for delivery to your windows phone 7 emulator/device

How to Send Tile Toast and Raw Notifications to the Microsoft Push Notification Service to Windows Phone 7

Note: Code here applies to CTP – I have not yet updated for the RTM.
This post provides the code required to send notifications through the Microsoft Push Notifications Service to a Windows Phone 7 device. I have put this together for the purpose of re-use across projects. Receiving and processing the notification on Windows Phone 7 will be the subject of a later post but I have some screenshots of the result here.

I will keep this breif as you are likely already aware, but for those of you who are not there are three types of notifications you can send to a Windows Phone 7 device:

  • Tile – A tile is a visual, dynamic representation of application specific state within the quick launch area of the phone’s start experience
  • Toast – Displays as an overlay onto the user’s current screen, a bit like an outlook email notification popup that you can press that launches you into the application.
  • Raw – provides the ability for your application service to push data to the application while it is in the foreground without the need for your application to poll the appplication service.

So as a teaser – what do these look like in action:

  • Tile – ability to change the count, background image(using either a local image and remote image) and text, yes E=mc^2 influnced by the first non programming book i am reading in about 10yrs – “A Brief History of Time” by Steven Hawking, ok so still technical. but not programming.
  • Tile Push Notification Before

    Tile Push Notification Before

    Tile Push Notification After

    Tile Push Notification After

  • Toast – See the Orange pop up at the top with the lame joke – well that is a toast, it looks like the above before and after as below.
  • Toast Push Notification After

    Toast Push Notification After

  • Raw – provides the ability for your application service to push data to the application while it is in the foreground without the need for your application to poll the appplication service. Well really with raw there is nothing to show until you implement a handler in your application to do something useful with the data (HttpNotificationChannel.HttpNotificationReceived). Therefore no image until next post.
  • So what exactly are we building a RawNotification, TileNotification and ToastNotification class with a Send method that returns a parsed NotificationResponse – here it is:

    An API for sending Push Notifications Tile, Toast and Raw
    Once you finish implementing this with the code provided in the post you will be able to send a notification as simply as follows:
  • Tile – TileNotification.cs
  • ToastNotification toast = new ToastNotification("Wanted Toast", "But forgot the bread ", new Uri(txtUri.Text), Guid.NewGuid(), 2, null);
    ThreadPool.QueueUserWorkItem((o) => toast.Send(ProcessResponse));
  • Toast – ToastNotification.cs
  • TileNotification tile = new TileNotification(@"/Images/Background1.png", 2, "E = mc^2", new Uri(txtUri.Text), Guid.NewGuid(), 1, null);
    ThreadPool.QueueUserWorkItem((o) => tile.Send(ProcessResponse));
  • Raw – RawNotification.cs
  •             XmlDocument rawData = new XmlDocument();
                string rawContent = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n\r\n" +
    
                                     "<root>\r\n" +
                                        "<foo>c</foo>\r\n" +
                                        "<bar>3000000</bar>\r\n" +
                                     "</root>";
                rawData.LoadXml(rawContent);           
    
                RawNotification raw = new RawNotification(rawData, new Uri(txtUri.Text), Guid.NewGuid(), 3, null);
                ThreadPool.QueueUserWorkItem((o) => raw.Send(ProcessResponse));

    So now you can see how easy it is to use, how is the code for sending the notifications implemented:

    NotificationBase.cs

    using System;
    using System.IO;
    using System.Net;
    using System.Security.Cryptography.X509Certificates;
    
    namespace NickHarris.Net.Notification
    {
        public enum NotificationType { Token, Toast, Raw };   
    
        public abstract class NotificationBase
        {
            private Uri _URI;
            private Guid? _UUID;
            private int? _priority;
            private X509Certificate _certificate;
            private NotificationType _target;
            private byte[] FormattedNotificationMessage { get; set; }
    
            public NotificationBase(Uri uri, Guid? uuid, int? priority, X509Certificate certificate, NotificationType target)
            {
                _URI = uri;
                _UUID = uuid;
                _priority = priority;
                _certificate = certificate;
                _target = target;
            }
    
            protected abstract Byte[] GetTemplateFormatted();
            protected abstract bool IsPriorityValid(int priority);
    
            protected HttpWebRequest CreateRequest(Uri uri, Guid? uuid, int? priority, X509Certificate certificate, NotificationType target)
            {
                 if (priority.HasValue && !IsPriorityValid(priority.Value))
                    throw new ArgumentOutOfRangeException("Priority {0} is not valid for this notification type", priority.ToString());
    
                // The URI that the Push Notification service returns to the Push Client when creating a notification channel.
                HttpWebRequest request = WebRequest.Create(uri) as HttpWebRequest;
                // HTTP POST is the only allowed method to send the notification.
                request.Method = WebRequestMethods.Http.Post;
                request.ContentType = "text/xml; charset=utf-8";
                FormattedNotificationMessage = GetTemplateFormatted();
                request.ContentLength = FormattedNotificationMessage.Length;
                request.Headers = GetHeaders(uuid, priority, target);
    
                // If the cloud service sending this request is authenticated, it needs to send its certificate.
                // Otherwise, this step is not needed.
                if (certificate != null)
                    request.ClientCertificates.Add(certificate);
                return request;
            }
    
            protected WebHeaderCollection GetHeaders(Guid? uuid, int? priority, NotificationType target)
            {
                WebHeaderCollection headers = new WebHeaderCollection();
    
                // The optional custom header X-MessageID uniquely identifies a notification message. If it is present, the same value is returned.
                // in the notification response. It must be a string that contains a UUID.
                if (uuid.HasValue)
                    headers.Add("X-MessageID", uuid.Value.ToString());
    
                if (target != NotificationType.Raw)
                    headers.Add("X-WindowsPhone-Target", target.ToString().ToLower());
    
                // The optional custom header X-NotificationClass sets the notification class for this notification request. The valid value range is
                // from 1-31. If this value is not present, the server defaults to 2
                if (priority.HasValue)
                    headers.Add("X-NotificationClass", priority.Value.ToString());
    
                return headers;
            }
    
            public void Send(Action<NotificationResponse> callback)
            {
                HttpWebRequest request = CreateRequest(_URI, _UUID, _priority, _certificate, _target);
                request.BeginGetRequestStream((x) =>
                {
                    using (Stream requestStream = request.GetRequestStream())
                    {
                        requestStream.Write(FormattedNotificationMessage, 0, FormattedNotificationMessage.Length);
                        requestStream.Close();
                    }
    
                    //Get Async response from push notification service
                    request.BeginGetResponse((asyncResult) =>
                    {
                        using (WebResponse notifyResponse = request.EndGetResponse(asyncResult))
                        {
                            //callback to notify caller of result
                            callback(new NotificationResponse((HttpWebResponse)notifyResponse));
                        }
                    }, null);
                }, null);
            }
        }
    }

    RawNotification.cs

    using System;
    using System.Security.Cryptography.X509Certificates;
    using System.Text;
    using System.Xml;
    
    namespace NickHarris.Net.Notification
    {
        public class RawNotification:NotificationBase
        {
            public XmlDocument Payload { get; set; }
            public RawNotification(XmlDocument payload, Uri subscriptionUri, Guid? uuid, int? priority, X509Certificate certificate)
            : base(subscriptionUri, uuid, priority, certificate, NotificationType.Raw)
            {
                Payload = payload;
            }
    
            protected override byte[] GetTemplateFormatted()
            {
                return Encoding.UTF8.GetBytes(Payload.OuterXml);
            }
    
            protected override bool IsPriorityValid(int priority)
            {
                return    (priority > 2 && priority < 11)  //3-10 – Real Time. The raw notification is delivered as soon as possible.
                       || (priority > 12 && priority < 21) //13-20 – Priority. The raw notification is delivered at a predefined timeout.
                       || (priority > 22 && priority < 32); //23-31 – Regular. The raw notification is delivered at a predefined timeout which is greater than the Priority batching interval.
            }
        }
    }

    TileNotification.cs

    using System;
    using System.Security.Cryptography.X509Certificates;
    using System.Text;
    
    namespace NickHarris.Net.Notification
    {
        public class TileNotification : NotificationBase
        {
            public string BackgroundImage { get; set; }
            public int Count { get; set; }
            public string Title { get; set; }
    
             public TileNotification(string backgroundImage, int count, string title, Uri subscriptionUri, Guid? uuid, int? priority, X509Certificate certificate)
                : base(subscriptionUri, uuid, priority, certificate, NotificationType.Token)
            {
                BackgroundImage = backgroundImage;
                Count = count;
                Title = title;
            }
    
             protected override Byte[] GetTemplateFormatted()
             {
                 string tileMessage = "X-WindowsPhone-Target: token\r\n\r\n" +  //Note: I have left this in line so you can see the format.  Would be better if you add it to a Resource file
                    "<?xml version=\"1.0\" encoding=\"utf-8\"?>" +
                    "<wp:Notification xmlns:wp=\"WPNotification\">" +
                       "<wp:Token>" +
                          "<wp:Img>{0}</wp:Img>" +
                          "<wp:Count>{1}</wp:Count>" +
                          "<wp:Title>{2}</wp:Title>" +
                       "</wp:Token> " +
                    "</wp:Notification>";
    
                 //return new UTF8Encoding().GetBytes(string.Format(Properties.Resources.ToastNotificationTemplate, Environment.NewLine, Environment.NewLine, BackgroundImage, Count, Title));
                 return new UTF8Encoding().GetBytes(string.Format(tileMessage,BackgroundImage, Count, Title));
             }
    
             protected override bool IsPriorityValid(int priority)
             {
                 return    priority == 1   // 1 – Real Time. The tile notification is delivered as soon as possible.
                        || priority == 11  //11 – Priority. The tile notification is delivered at a predefined timeout.
                        || priority == 21; //21 – Regular. The tile notification is delivered at a predefined timeout which is greater than the Priority batching interval.
             }
        }
    }

    ToastNotification.cs

    using System;
    using System.Security.Cryptography.X509Certificates;
    using System.Text;
    
    namespace NickHarris.Net.Notification
    {
        public class ToastNotification: NotificationBase
        {
            public string TitleOne { get; set; }
            public string TitleTwo { get; set; }
    
            public ToastNotification(string titleOne, string titleTwo, Uri subscriptionUri, Guid? uuid, int? priority, X509Certificate certificate)
                : base(subscriptionUri, uuid, priority, certificate, NotificationType.Toast)
            {
                TitleOne = titleOne;
                TitleTwo = titleTwo;
            }
    
            protected override Byte[] GetTemplateFormatted()
            {
                string toastTemplate = "X-WindowsPhone-Target: TOAST\r\n\r\n" +                 //Note: I have left this in line so you can see the format.  Would be better if you add it to a Resource file
                                        "<?xml version=\"1.0\" encoding=\"utf-8\"?>" +
                                        "<wp:Notification xmlns:wp=\"WPNotification\">" +
                                           "<wp:Toast>" +
                                              "<wp:Text1>{0}</wp:Text1>" +
                                              "<wp:Text2>{1}</wp:Text2>" +
                                           "</wp:Toast>" +
                                        "</wp:Notification>";
    
                return new UTF8Encoding().GetBytes(string.Format(toastTemplate, TitleOne, TitleTwo));
                //return new UTF8Encoding().GetBytes(string.Format(Properties.Resources.ToastNotificationTemplate, Environment.NewLine, Environment.NewLine, TitleOne, TitleTwo));
            }
    
            protected override bool IsPriorityValid(int priority)
            {
                return priority == 2        //2 – Real Time. The toast notification is delivered as soon as possible.
                       || priority == 12    //12 – Priority. The toast notification is delivered at a predefined timeout.
                       || priority == 22;   //22 – Regular. The toast notification is delivered at a predefined timeout which is greater than the Priority batching interval.
    
            }
        }
    }

    NotificationResponse.cs

    using System;
    using System.Net;
    
    namespace NickHarris.Net.Notification
    {
        public enum NotificationStatus { None = 0, Received, QueueFull, Dropped, Unknown }; //N/A mapped to None, Unknown -> unknown will likely occur with updates to the CTP
        public enum DeviceConnectionStatus { None = 0, Connected, TemporarilyDisconnected, Inactive, Unknown };
        public enum SubscriptionStatus { None = 0, Active, Expired, Unknown };
    
        public struct NotificationResponse
        {
            private string _messageID; //keep as string not everyone will be using guids
            private NotificationStatus _notificationStatus;
            private SubscriptionStatus _subscriptionStatus;
            private DeviceConnectionStatus _deviceConnectionStatus;
            private HttpStatusCode _httpStatusCode;
    
            public NotificationResponse(HttpWebResponse response)
            {
                _messageID = response.Headers["X-MessageID"];
                _notificationStatus = GetNotificationStatus(response.Headers["X-NotificationStatus"]);
                _subscriptionStatus = GetSubscriptionStatus(response.Headers["X-SubscriptionStatus"]);
                _deviceConnectionStatus = GetDeviceConnectionStatus(response.Headers["X-DeviceConnectionStatus"]);
                _httpStatusCode = response.StatusCode;
            }
    
            public HttpStatusCode HttpStatusCode
            {
                get { return _httpStatusCode; }
                private set { _httpStatusCode = value; }
            }
    
            public DeviceConnectionStatus DeviceConnectionStatus
            {
                get { return _deviceConnectionStatus; }
                private set { _deviceConnectionStatus = value; }
            }
    
            public SubscriptionStatus SubscriptionStatus
            {
                get { return _subscriptionStatus; }
                private set { _subscriptionStatus = value; }
            }
    
            public NotificationStatus NotificationStatus
            {
                get { return _notificationStatus; }
                private set { _notificationStatus = value; }
            }
    
            public string MessageID
            {
                get { return _messageID; }
                private set { _messageID = value; }
            }
    
            private static NotificationStatus GetNotificationStatus(string notificationStatus)
            {
                NotificationStatus status = NotificationStatus.Unknown;
    
                if (notificationStatus.ToUpper() == "N/A")
                    status = NotificationStatus.None;
                else if (Enum.IsDefined(typeof(NotificationStatus), notificationStatus))
                    status = (NotificationStatus)Enum.Parse(typeof(NotificationStatus), notificationStatus, true);
                else
                {//leave as unknown
                }
    
                return status;
            }
            private static DeviceConnectionStatus GetDeviceConnectionStatus(string deviceConnectionStatus)
            {
                DeviceConnectionStatus status = DeviceConnectionStatus.Unknown;
                deviceConnectionStatus = deviceConnectionStatus.Replace(" ", string.Empty);
    
                if (deviceConnectionStatus.ToUpper() == "N/A")
                    status = DeviceConnectionStatus.None;
                else if (Enum.IsDefined(typeof(DeviceConnectionStatus), deviceConnectionStatus))
                    status = (DeviceConnectionStatus)Enum.Parse(typeof(DeviceConnectionStatus), deviceConnectionStatus, true);
                else
                {//leave as unknown
                }
    
                return status;
            }
            private static SubscriptionStatus GetSubscriptionStatus(string subscriptionStatus)
            {
                SubscriptionStatus status = SubscriptionStatus.Unknown;
    
                if (subscriptionStatus.ToUpper() == "N/A")
                    status = SubscriptionStatus.None;
                else if (Enum.IsDefined(typeof(SubscriptionStatus), subscriptionStatus))
                    status = (SubscriptionStatus)Enum.Parse(typeof(SubscriptionStatus), subscriptionStatus, true);
                else
                {//leave as unknown
                }
    
                return status;
            }       
    
        }
    }

    So now for the disclaimer – Main thing is – it works (apart from remote tile notifications – currently an issue with the CTP as far as I can tell).  This version of the code was created against the requirements of the April CTP so I expect the message format to change although if you put it into your project resource file as the //comments above dictate you should be able to change this as the developer toolkit evolves.

    I will to keep this blog up to date with fixes for any changes as future CTPs are released or bugs I find as I dig deeper so make sure you subscribe to my RSS feed.

    Enjoy the code and the nights you will save not having to write it :)

    Nick

    How to Pin your application in Windows Phone 7 emulator April CTP refresh

    The Windows Phone 7 emulator provides Pin capability within the April CTP refresh. To pin your application perform the following:

    1. Right click on you WP7 app project and select properties.
    2. Set the Title under deployment options to your title name “PinMyApp”
    3. Set the Title under Tile options to “Pin My App” – this is the actual text shown over the tile
    4. Set the background image on the Tile using the dropdown.
    5. Deploy your application
    6. When on the Start screen Press to view your applications
    7. Hold down on your application “PinMyApp” until a context menu appears then select “pin to start”
    8.  

    9. Observe your application is now pinned

    Its important to note that you can use Push notifications to change the text, count and image on your applications tile. This can be achieved by setting up a notification channel using HttpNotificationChannel and binding to the shell entry point BindToShellEntryPoint(); Once you have registered a uri will be returned and you can then send tile notifications using a WebRequest. I am currently working on a blog post for this one, so stay tuned

    Nick

    Upgrading your Windows Phone projects for Windows Phone 7 Developer Tools CTP April refresh

    Upgrading your Windows Phone projects for Windows Phone 7 Developer Tools CTP April refresh 

    Here are a bunch of breaking issues I encountered on installing Upgrading your Windows Phone projects for Windows Phone 7 Developer Tools CTP April refresh 

      For each silverlight for mobile project in your solution you get the following warning (Note: XNA framework apps will not provide a warning but will still require the changes)

    • Issue: WarningYou are using a project created by previous version of Windows Phone Developer Tools CTP. Your application may not run properly. Please edit the WMAppManifest.xml file under Properties node and insert the following <Capability> elements between
      <Capabilities></Capabilities> element as shown below. <Capabilities>
      <Capability Name="ID_CAP_NETWORKING" />
      <Capability Name="ID_CAP_LOCATION" />
      <Capability Name="ID_CAP_SENSORS" />
      <Capability Name="ID_CAP_MICROPHONE" />
      <Capability Name="ID_CAP_MEDIALIB" />
      <Capability Name="ID_CAP_GAMERSERVICES" />
      <Capability Name="ID_CAP_PHONEDIALER" />
      <Capability Name="ID_CAP_PUSH_NOTIFICATION" />
      <Capability Name="ID_CAP_WEBBROWSERCOMPONENT" />
      </Capabilities> 
    • Solution: You can find WMAppManifest.xml in the Properties folderof your project once found add the sections listed above
    • Issue: Push Notifications now require the publisher to be defined
    • Solution: This can be done within the WMAppManifest.xml in the Properties folder.  Update the Publisher on <App> E.g 
    <App xmlns="" ProductID="{1ac9139d-aa7e-6c70-acf9-c6ceb69632a1}" Title="PinMyApp"
    RuntimeType="SilverLight" Version="1.0.0.0" Genre="NormalApp"
    Author="Nick.Harris" Description="Demo"
    Publisher="www.NickHarris.net">
    • Issue: Push Notification payload has been updated
    • Solution: Update notifications as per the specification here  
    • Issue: The property ‘Visible’ does not exist on the type ‘ApplicationBar’ in the XML namespace ‘clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone.Shell’
    • Solution: this has bee renamed to IsVisible
    • Issue: ‘System.Windows.Navigation.NavigationService’ does not contain a definition for ‘TopLevelNavigationService’
    • Solution: change NavigationService.TopLevelNavigationService.Navigate(…) to NavigationService.Navigate(…)  Note: did not find this one in the breaking changes….

    Release notes for other  breaking changes are here

    A first look at SQL 2008 spatial data types to and from SQL Server 2008

    A first look at SQL 2008 spatial data types round trip with sql 2008. I was surprised to find that the Entity Framework, LINQ to SQL and Dataset Designer do not currently support Spatial data types.  Given this was the case and wanting to have a play with the spatial data type the following is how I used the sqlgeometry spatial data type to store and retrieve a list both lines and points consisting of X,Y co-ordinates.

    Lets use a bottom up approach create a table using the GEOMETRY data type:

    CREATE TABLE [dbo].[MyData](
    	[ID] [uniqueidentifier] NOT NULL PRIMARY KEY DEFAULT (newsequentialid()) ,
    	[Data] [GEOMETRY] NULL);
    GO
    

    Create a stored procedure to insert data into the MyData table:

    CREATE PROCEDURE [dbo].[InsertMyData]
                @Lines GEOMETRY
    AS
    	INSERT INTO [dbo].[MyData] (Data)
                 VALUES (@Lines);
    RETURN 0
    GO
    

    And create a stored procedure to retrieve a the data:

    CREATE PROCEDURE [dbo].[GetMyData]
    	@ID uniqueIdentifier
    AS
    	SELECT *
    	FROM [dbo].[MyData]
    	WHERE ID = @ID	
    
    RETURN 0
    GO
    

    Code to insert and retrieve data:

    1. Add a reference to C:\Program Files (x86)\Microsoft SQL Server\100\SDK\Assemblies\Microsoft.SqlServer.Types.dll which contains SqlGeometryBuilder
    2.                      using System;
                           using System.Collections.Generic;
                           using System.Data;
                           using System.Data.SqlClient;
                           using System.Threading;
                           using System.Threading.Tasks;
                           using Microsoft.SqlServer.Types;
      

    This is where it gets back to the !good old days without generation of your DAL. Add a method to call the InsertMyData stored procedure:

            public bool InsertMyData(Guid id, List> lines)
            {
                int result = 0;
                if (lines.Count > 0)
                {
                    using (SqlConnection conn = new SqlConnection(connString))
                    using (SqlCommand cmd = conn.CreateCommand())
                    {
                        cmd.CommandText = "InsertMyData";
                        cmd.CommandType = CommandType.StoredProcedure;
    
                        cmd.Parameters.Add(new SqlParameter("@ID", id));
                        cmd.Parameters.Add(SpatialDataHelper.GetGeometrySqlParameter("@Data", lines, true));
                        conn.Open();
                        result = cmd.ExecuteNonQuery();
                    }
                }
    
                return (result > 0);
            }
    

    Now you may be wondering what is SpatialDataHelper.GetGeometrySqlParameter. Well that too is something that needs to be implemented. Even though the SQL geometry type is a system type the type must be treated as user defined type. Take note of the sqlParam.UdtTypeName = “geometry”;

     public static class SpatialDataHelper
        {
            public static SqlParameter GetGeometrySqlParameter(string paramName, List> lines, bool makeValid)
            {
                SqlParameter sqlParam = new SqlParameter();
                sqlParam.ParameterName = paramName;
                sqlParam.UdtTypeName = "geometry";
    
                SqlGeometryBuilder geomBuilder = new SqlGeometryBuilder();
    
                // SqlGeometryBuilder.SetSrid Must be called at the beginning of each geometry sequence
                geomBuilder.SetSrid(0);
                geomBuilder.BeginGeometry(OpenGisGeometryType.GeometryCollection);
    
                //break out each line
                foreach (List lp in lines)
                {
                    if (lp.Count > 0)
                    {
                        if (lp.Count == 1) //check if its a point or a line and start a geometry for the point or line
                            geomBuilder.BeginGeometry(OpenGisGeometryType.Point);
                        else
                            geomBuilder.BeginGeometry(OpenGisGeometryType.LineString);
    
                        int count = 0;
                        foreach (Point p in lp) //add all points
                        {
                            if (count == 0)
                                geomBuilder.BeginFigure(p.X, p.Y);
                            else
                                geomBuilder.AddLine(p.X, p.Y);
    
                            count++;
                        }
    
                        geomBuilder.EndFigure();
                        geomBuilder.EndGeometry();
                    }
                }
                geomBuilder.EndGeometry();
    
                SqlGeometry constructed = geomBuilder.ConstructedGeometry;
                if (makeValid)
                {
                    //Note required to convert into a geometry instance with a valid Open Geospatial Consortium (OGC) type.
                    // this can cause the points to shift - use with caution...
                    constructed = constructed.MakeValid();
                }
                sqlParam.Value = constructed;
    
                return sqlParam;
            }
        }
    

    Finally to get your data back out its necessary to call the previously defined GetMyData stored procedure and then crunch out the points into their original form.

            public List> GetMyData(Guid id)
            {
                ConcurrentBag> bag = new ConcurrentBag>();
    
                using (SqlConnection conn = new SqlConnection(connString))
                using (SqlCommand cmd = conn.CreateCommand())
                {
                    cmd.CommandText = "GetMyData";
                    cmd.CommandType = CommandType.StoredProcedure;
    
                    cmd.Parameters.Add(new SqlParameter("@ID", id));
                    conn.Open();
    
                    using (SqlDataReader reader = cmd.ExecuteReader())
                    {
                        while (reader.Read())
                        {
                            startLinesID = reader.GetGuid(reader.GetOrdinal("ID"));
                            SqlGeometry gLines = (SqlGeometry)reader["Data"];
                            // using Parallel.For to speed up crunch.  Note: STGeometryN starts with 1 based index therefore start with i of 1
                            Parallel.For(1, gLines.STNumGeometries().Value + 1, delegate(int i)
                            {
                                SqlGeometry geom = gLines.STGeometryN(i);
                                List line = new List();
                                switch (geom.STGeometryType().ToString())
                                {
                                    case "LineString":    //a LineString is a line within the SQLGeometry
                                        for (int j = 1; j <= geom.STNumPoints(); j++) //get all points forming the line
                                        {
                                            line.Add(new Point(geom.STPointN(j).STX.ToSqlInt32().Value, geom.STPointN(j).STY.ToSqlInt32().Value));
                                        }
                                        break;
                                    case "Point": //an individual point
                                        line.Add(new Point(geom.STPointN(1).STX.ToSqlInt32().Value, geom.STPointN(1).STY.ToSqlInt32().Value)); //1 is the first index not 0 based
                                        break;
                                }
                                bag.Add(line);
                            });
                        }
                    }
                }
    
                return bag.ToList();
            }
    

    This seems to do the job although I would be interested to hear of better ways to do this.