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   
   

Free Windows Phone 7 Deep Dive Workshop(Melbourne, Sydney, Adelaide and Brisbane)

Hi All,

Nick Randolph from Built to Roam is running a Free Windows Phone 7 deep dive over two days, the content looks great, and did i mention it was Free :)

For more details and to register jump on over to his blog Windows Phone 7 Deep Dive Workshop(Melbourne, Sydney, Adelaide and Brisbane)

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.

Windows Phone 7 Application Certification Requirements

I am being dragged away from the computer for the weekend :( but will try to get the OData post I have been working on done when I return. Until then some good reading material for all those who are intending on releasing a Windows Phone 7 application to marketplace – Windows Phone 7 Application Certification Requirements

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

    New Windows Phone 7 Developer Tools CTP April refresh 2010

    Download and install Windows Phone 7 Developer Tools April 2010 Refresh.

    Note as part of this install I had to remove the following components in order:

    • Microsoft Visual Studio 2010  RC       
    • Microsoft Windows Phone Developer Resources         
    • Microsoft Windows Phone Developer Tools
    • Microsoft Silverlight 4.0 SDK
    • Microsoft Visual Studio 2010 Express Prerequisites x64.
    • NET Framework 4 Multi-Targeting Pack
    • Microsoft .NET Framework 4 Extended
    • Microsoft .NET Framework 4 Client Profile
    • VC 10.0 Runtime
    • Microsoft Windows Phone Emulator x64
    • Windows Phone 7 Add-in for Visual Studio 2010 – ENU
    • Microsoft XNA Game Studio 4.0

    Ever had one of those days when your mouse batteries go flat in the middle of a blog post.  Then you swap out one of the batteries from the mouse and switch it with the keyboard to suck the last remaining juice from it.  Ten minutes later both are flat and you got no spares.  Lets just say its one of those days :)

    Anyhow the refresh has new features (-take note of the additions to the emulator ) and breaking changes(download release notes at this link) - take note of the new publisher setting and notification templates.

    Implementing MVVM for the first time on Windows Phone 7 using Silverlight for Mobile

    This post is my first attempt at implementing the MVVM pattern on Windows Phone 7 using Silverlight for Mobile.
    Prior to reading this post you should read Silverlight for Mobile on Windows Phone 7 InkPresenter Fun as a reference point for context and contrast.

    1. The MainPageViewModel is added for MainPage.   Key points to note are:
      • MainPageViewModel inherits from DependencyObject
      • Strokes is a DependencyProperty
      • Functionality from event handlers of MainPage moved into MainPageViewModel
      • No current use of Dispatcher as currently no separate thread attempting to update UI thread.
       public class MainPageViewModel : DependencyObject
          {
              private StrokeCollection _strokes;
              private Stack<Stroke> _removedStrokes = new Stack<Stroke>();
              private Stroke _currentStroke;
               
              public MainPageViewModel()
              {
                  Strokes = new StrokeCollection();
              }
           
              public StrokeCollection Strokes
              {
                  get { return (StrokeCollection)GetValue(StrokesProperty); }
                  set { SetValue(StrokesProperty, value); }
              }
      
              // Using a DependencyProperty as the backing store for Strokes.  This enables animation, styling, binding, etc...
              public static readonly DependencyProperty StrokesProperty =
                  DependencyProperty.Register("Strokes", typeof(StrokeCollection), typeof(MainPageViewModel), new PropertyMetadata(null));
      
              public void BeginCapture(Point point)
              {
                
                  _currentStroke = new Stroke();
                  _currentStroke.StylusPoints.Add(GetStylusPoint(point));
                  _currentStroke.DrawingAttributes.Color = Colors.Blue;
                  this.Dispatcher.BeginInvoke(
                  Strokes.Add(_currentStroke);          
              }
      
              public void CaptureStrokePoint(Point point)
              {          
                  if (_currentStroke != null)
                      _currentStroke.StylusPoints.Add(GetStylusPoint(point));
              }
      
              public void EndCapture()
              {
                  _currentStroke = null;
              }
      
              private StylusPoint GetStylusPoint(Point position)
              {
                  return new StylusPoint(position.X, position.Y);
              }
      
              public void Undo()
              {
                  if (Strokes != null && Strokes.Count > 0)
                  {
                      _removedStrokes.Push(Strokes.Last());
                      Strokes.RemoveAt(Strokes.Count - 1);
                  }
              }
      
              public void Redo()
              {
                  if (_removedStrokes != null && _removedStrokes.Count > 0)
                  {
                      Strokes.Add(_removedStrokes.Pop());              
                  }
              }
          }
      
    2. The MainPage code behind now becomes as follows.  Important points to note are:
      • The Datacontext is now set to an instance of MainPageViewModel i.e _vm; 
      • The _vm is called within each event handler to manipulate the view.
      public partial class MainPage : PhoneApplicationPage
          {
              private MainPageViewModel _vm;
      
              public MainPage()
              {          
                  InitializeComponent();
      
                  SupportedOrientations = SupportedPageOrientation.Portrait | SupportedPageOrientation.Landscape;
                
                  _vm = new MainPageViewModel();
                  this.DataContext = _vm;
               
                  inkTest.MouseMove += new MouseEventHandler(inkTest_MouseMove);
                  inkTest.MouseLeftButtonDown += new MouseButtonEventHandler(inkTest_MouseLeftButtonDown);
                  inkTest.MouseLeftButtonUp += new MouseButtonEventHandler(inkTest_MouseLeftButtonUp);
              }
      
              private void inkTest_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
              {
                  _vm.EndCapture();
                  inkTest.ReleaseMouseCapture();
              }
      
              private void inkTest_MouseMove(object sender, MouseEventArgs e)
              {
                  _vm.CaptureStrokePoint(e.GetPosition(inkTest));
              }
      
              private void inkTest_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
              {
                  _vm.BeginCapture(e.GetPosition(inkTest));
              }
      
              private void btnUndo_Click(object sender, EventArgs e)
              {
                  _vm.Undo();
              }
      
              private void btnRedo_Click(object sender, EventArgs e)
              {
                  _vm.Redo();
              }  
          }
      
    3. The MainPage XAML now becomes as follows: Its important to note that:
      • InkPresenter Now binds Strokes
               <Grid x:Name="ContentGrid" Grid.Row="1">
                  <InkPresenter Name="inkTest" Strokes="{Binding Strokes}" Background="White" Height="652" VerticalAlignment="Top" />
              </Grid>
      
          <phoneNavigation:PhoneApplicationPage.ApplicationBar>
              <shell:ApplicationBar Visible="True" IsMenuEnabled="True">
                  <shell:ApplicationBar.Buttons>
                      <shell:ApplicationBarIconButton x:Name="btnUndo" IconUri="/Images/dark/appbar.minus.rest.png" Click="btnUndo_Click"></shell:ApplicationBarIconButton>
                      <shell:ApplicationBarIconButton x:Name="btnRedo" IconUri="/Images/dark/appbar.add.rest.png" Click="btnRedo_Click"></shell:ApplicationBarIconButton>
                  </shell:ApplicationBar.Buttons>
              </shell:ApplicationBar>      
          </phoneNavigation:PhoneApplicationPage.ApplicationBar>  
      

    This implementation is now easier to write unit tests against then its predecessor.  One thing I am not happy with is the lack of use of Command and ICommand to route all commands to the MainPageViewModel using bindings.  I noticed that although ICommand exists in silverlight for mobile the Button, ApplicationBarIconButton and InkPresenter dont support the command property.  Initial thoughts would be to create a derived class e.g for ApplicationBarIconButton to provide a Command property but it seems like it is a lot of effort for little gain.  Anyone out there know if the Command prop and bindings will be supported on the user controls in Silverlight for Mobile?

    Adding an Application Bar to your Windows Phone 7 application

    This example uses my previous InkPresenter example to add an Application Bar to replace the undo and redo buttons with two ApplicationBarIconButton.  I also add MenuItems to the application bar for illustrative purposes.  As yet they serve no functional purpose.
    1. Add a reference to “Microsoft.Phone.Shell”
    2. In your page Add a namespace declaration with prefix of shell:
    3.  xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone.Shell"
    4. Add images to your project either and ensure that you set the Copy to Output Directory as Always and Build Action as Content
    5. Add your application bar
    6.     <phoneNavigation:PhoneApplicationPage.ApplicationBar>
              <shell:ApplicationBar Visible="True" IsMenuEnabled="True">
                  <shell:ApplicationBar.Buttons>
                      <shell:ApplicationBarIconButton x:Name="btnUndo" IconUri="/Images/light/appbar.minus.rest.png" Click="btnUndo_Click"></shell:ApplicationBarIconButton>
                      <shell:ApplicationBarIconButton x:Name="btnRedo" IconUri="/Images/light/appbar.add.rest.png" Click="btnRedo_Click" ></shell:ApplicationBarIconButton>
                  </shell:ApplicationBar.Buttons>
                  <shell:ApplicationBar.MenuItems>
                      <shell:ApplicationBarMenuItem x:Name="mnuItemX" Text="Menu Item X"></shell:ApplicationBarMenuItem>
                      <shell:ApplicationBarMenuItem x:Name="mnuItemY" Text="Menu Item Y"></shell:ApplicationBarMenuItem>
                  </shell:ApplicationBar.MenuItems>
              </shell:ApplicationBar>
          </phoneNavigation:PhoneApplicationPage.ApplicationBar>
      </phoneNavigation:PhoneApplicationPage>
    7. Wire up the event handlers
    8.         private void btnUndo_Click(object sender, EventArgs e)
              {
                  _vm.Undo();
              }
      
              private void btnRedo_Click(object sender, EventArgs e)
              {
                  _vm.Redo();
              }
    9. Results
    Before using ApplicationBar

    Before using ApplicationBarAfter WP7 ApplicationBar Collapsed

    After WP7 ApplicationBar Collapsed

    After WP7 ApplicationBar Collapsed

    After WP7 ApplicationBar Expanded

    After WP7 ApplicationBar Expanded

     Some useful resources for the Application Bar are as follows:

  • Application Bar Icons for Windows Phone 7 Series
  • Application Bar Best Practices for Windows Phone
  • Windows Phone 7 NotificationChannelOpenException with MS Push Notification HttpNotificationChannel.Open

    Issue: Had a NotificationChannelOpenException today when testing out MS Push Notifications  for Windows Phone 7 using code as follows.

                HttpNotificationChannel _channel = new HttpNotificationChannel("NickTest");      �
    ...
                try
                {             �
                    _channel.ChannelUriUpdated += new EventHandler&lt;NotificationChannelUriEventArgs&gt;(channel_ChannelUriUpdated);
                    _channel.HttpNotificationReceived += new EventHandler&lt;HttpNotificationEventArgs&gt;(channel_HttpNotificationReceived);
                    _channel.ShellEntryPointNotificationReceived += new EventHandler&lt;NotificationEventArgs&gt;(channel_ShellEntryPointNotificationReceived);
                    _channel.ShellNotificationReceived += new EventHandler&lt;NotificationEventArgs&gt;(channel_ShellNotificationReceived);
                    _channel.ExceptionOccurred += new EventHandler&lt;NotificationChannelExceptionEventArgs&gt;(channel_ExceptionOccurred);
    
                    _channel.Open(); //Error occurs here
                    //_channel.BindToShellEntryPoint(); // tile�
                    _channel.BindToShellNotification(); // - toast
                }
                catch (NotificationChannelExistsException)
                {
                    _channel = HttpNotificationChannel.Find(_channelName);
                }

    Exception stack dump was as follows when calling Open on the _channel:

    Microsoft.Phone.Notification.NotificationChannelOpenException was unhandled
      Message=NotificationChannelOpenException
      StackTrace:
           at Microsoft.Phone.Notification.SafeNativeMethods.ThrowExceptionFromHResult(Int32 hr, Exception defaultException)
           at Microsoft.Phone.Notification.HttpNotificationChannel.Open()
           at MyStrokes.Provider.NotificationProvider.Subscribe()
           at MyStrokes.App.Application_Startup(Object sender, StartupEventArgs 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)

    Solution:  If you try to Open a channel almost immediately after you hit Debug when emulator fires up this issue will occur.  The solution is simple – Give the emulator two minutes before making the call. 

    It appears this is a documented issue in the Windows Phone 7 Developer Tools CTP Release Notes:

  • The push client debugging has to wait for two minutes after boot of the emulator (or device) before using the APIs (i.e. Start screen show up).
  • For push notifications, if the emulator host computer is behind a proxy, SOCKS proxy has to be configured on the host computer.You can go to Control Panel > Network and Internet > Internet Options > Connections tab, and then choose the Setting option to set this configuration. //Also thought this was useful
  •  

    Adding a Windows Phone 7 WCF service reference in VS2010 RC

    Issue: You want to add a WCF service reference to a Windows Phone 7 project in Visual Studio 2010 RC Ultimate.  You right click the project to find “Add Service Reference…” option is not available.

    Solution: Assume this will be fixed in the RTM. Until then open up your Windows Phone 7 project in the Visual Studio 2010 Express edition that was installed with the Windows Phone Developer Tools.  The express allows you to “Add Service Reference…” to the WP7 project without issue.  Once done, reload your project in VS 2010 RC Ulitimate.

    Silverlight for Mobile on Windows Phone 7 InkPresenter fun

    After installing the Windows Phone Developer Tools this is a simple test for a bit of fun using Silverlight for Mobile for the first time to capturing user strokes using the InkPresenter.

    1. In Visual Studio 2010 Press File –> New Project –> Silverlight for Windows Phone –> Windows Phone Application
    2. Creating a Silverlight for Mobile Application

      Creating a Silverlight for Mobile Application

    3. Add an InkPresenter and two buttons Undo and Redo to the to the content Grid
    4.  

            <!--ContentGrid is empty. Place new content here-->
              <Grid x:Name="ContentGrid" Grid.Row="1">
                  <InkPresenter  Name="inkTest" Background="White" Margin="0,0,0,62" />
                  <Button Name="btnUndo" Content="Undo" Grid.Row="1" Height="72" HorizontalAlignment="Left" Margin="44,584,0,0" VerticalAlignment="Top" Width="160" Click="btnUndo_Click" Background="#FF933A3A" />
                  <Button Name="btnRedo" Content="Redo" Height="72" HorizontalAlignment="Left" Margin="272,584,0,0" VerticalAlignment="Top" Width="160" Grid.Row="1" Click="btnRedo_Click" Background="#FF933A3A" />
              </Grid>
    5. On the InkPresenter, named inkTest in this example, Add Event handlers for MouseMove, MouseLeftButtonDown, MouseLeftButtonUp to capture the movement from the user
    6.             inkTest.MouseMove += new MouseEventHandler(inkTest_MouseMove);
                  inkTest.MouseLeftButtonDown += new MouseButtonEventHandler(inkTest_MouseLeftButtonDown);
                  inkTest.MouseLeftButtonUp += new MouseButtonEventHandler(inkTest_MouseLeftButtonUp);
    7. To capture the mouse movements and turn them into Strokes on the InkPresenter the corresponding EventHandlers and member variables are as follows
    8. private Stroke _currentStroke;
      
      private void inkTest_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
      {
          _currentStroke = null;
      }
      
      private void inkTest_MouseMove(object sender, MouseEventArgs e)
      {
          if (_currentStroke != null)
              _currentStroke.StylusPoints.Add(GetStylusPoint(e.GetPosition(inkTest)));
      }
      
      private void inkTest_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
      {
          inkTest.CaptureMouse();
          _currentStroke = new Stroke();
          _currentStroke.StylusPoints.Add(GetStylusPoint(e.GetPosition(inkTest)));
          _currentStroke.DrawingAttributes.Color = Colors.Blue;
          inkTest.Strokes.Add(_currentStroke);
      }
      
      private StylusPoint GetStylusPoint(Point position)
      {
          return new StylusPoint(position.X, position.Y);
      }
    9. And since its difficult to draw with a mouse I want to be able to undo and redo some of my Strokes I use a simple Stack so implement the following EventHandlers for the Undo and Redo buttons
    10.         private Stack _removedStrokes = new Stack();
      
              private void btnUndo_Click(object sender, RoutedEventArgs e)
              {
                  if (inkTest.Strokes != null && inkTest.Strokes.Count > 0)
                  {
                      _removedStrokes.Push(inkTest.Strokes.Last());
                      inkTest.Strokes.RemoveAt(inkTest.Strokes.Count - 1);
                  }
              }
      
              private void btnRedo_Click(object sender, RoutedEventArgs e)
              {
                  if (_removedStrokes != null && _removedStrokes.Count > 0)
                  {
                      inkTest.Strokes.Add(_removedStrokes.Pop());
                  }
              }
    11. And the final result is :)
    12. InkPresenter on Windows Phone 7

      InkPresenter on Windows Phone 7

    This demonstrated a simple application that was faster to code then to blog about and that even with an undo button I am still hopeless at drawing :) . Try implementing the same functionality in the .NET Compact Framework :)

    Next posts will look at

    • Reworking this application to use MVVM
    • Using the Microsoft Notification Service

    What a day – dev therapy with tonights downloads + installs

    When you have a bad day there is nothing better then some dev therapy = tonights downloads + installs;

    1. .NET Framework 4 full RC
    2. Visual Studio 2010 Ultimate RC
    3. Visual Studio 2010 remote debugger RC
    4. Windows Phone Developer Tools CTP includes
      • Windows Phone Emulator CTP
      • Silverlight for Windows Phone CTP
      • XNA 4.0 Game Studio CTP
    5. Microsoft Expression Blend 4 Beta
    6. Visual Studio 2010 and .NET Framework 4 Training Kit