An Examination of State and Navigation Issues Related to the Windows Phone DatePicker & TimePicker Controls

I recently ran into an interesting issue when using the DatePicker control that is available in the Silverlight for Windows Phone Toolkit.  At first glance it looked like everything was working fine, but on closer inspection, I noticed that it seemed that the date I was selecting was not being returned from the control.  Once I figured out what was happening, I realized the same thing can happen when using the related TimePicker control.

The Setup

I discovered this when working on a Master/Details application.  The Master list provided a view of a list of items, where each item contains a set of properties that includes a Date.  Through the Master page, the user can select to edit an item, which navigates to a dedicated Editing page and opens the details of the item to edit…a fairly common scenario.  The general logic is as follows:

  • The edit command is executed (button pushed, etc.)
  • A request to navigate to the EditingPage is made through the NavigationService.  The Id of the item to edit is placed in the query string of the target Url as follows:
    NavigationService.Navigate(new Uri("/EditingPage.xaml?Id=" + selectedItem.Id, UriKind.Relative));
  • In the EditingPage OnNavigatedTo handler, the id of the item to edit is pulled from the query string and the object that contains the item details is retrieved (in this case, the value is pulled from  a list that is owned by the Application instance.)
  • The EditingPage markup includes a DatePicker control that is databound to the Date property contained in the item details object.

Excerpt form Page Markup:

Code Snippet
  1. <TextBlock Grid.Row="0" Grid.Column="0" Text="Id: " VerticalAlignment="Center"/>
  2. <TextBlock Grid.Row="1" Grid.Column="0" Text="Date: " VerticalAlignment="Center"/>
  3. <TextBlock Grid.Row="2" Grid.Column="0" Text="Some Text: " VerticalAlignment="Center"/>
  4.  
  5. <TextBox Grid.Row="0" Grid.Column="1" Text="{Binding Path=Id, Mode=TwoWay}" />
  6. <toolkit:DatePicker Grid.Row="1" Grid.Column="1" Value="{Binding Path=DateOfInterest, Mode=TwoWay}"/>
  7. <TextBox Grid.Row="2" Grid.Column="1" Text="{Binding Path=SomeRandomText, Mode=TwoWay}" />

The OnNavigatedTo override code follows:

Code Snippet
  1. protected override void OnNavigatedTo(NavigationEventArgs e)
  2. {
  3.     base.OnNavigatedTo(e);
  4.     if (NavigationContext.QueryString.ContainsKey("Id"))
  5.     {
  6.         var idText = NavigationContext.QueryString["Id"];
  7.         if (Int32.TryParse(idText, out _originalItemId))
  8.         {
  9.             var itemToEdit = ((App)(Application.Current)).CurrentViewModel.TestDataItems.FirstOrDefault(x => x.Id == _originalItemId);
  10.             _itemBeingEdited = new TestData();
  11.             itemToEdit.CopyTo(_itemBeingEdited);
  12.         }
  13.     }
  14.     DataContext = _itemBeingEdited;
  15. }

A Few Words About the Controls

The DatePicker and TimePicker controls are included in the Silverlight for Windows Phone Toolkit.  The Toolkit consists of a set of controls and other utilities that are released “out of band” from the regular tools’ release.  The first formal release of this toolkit was in November 2010, followed by a February 2011 update.  In addition to not having to wait for a new update to the full SDK for these tools, they are also published on CodePlex with full source code, which provides great insight into how things work, both for educational and forensic purposes.

The DatePicker and TimePicker controls offer a touch-optimized experience for selecting the corresponding values.  The value is initially presented in the phone UI in text format.  When the control is selected, a series of 3 rotating lists are presented for selecting a specific value, as illustrated below:

image

“Control View” for Date Picker & TimePicker controls

SNAGHTML324d104 SNAGHTML3244a77

The DatePicker Control In “Picker” View

The TimePicker Control in “Picker” View

 

Neither Fish nor Fowl

At the heart of the problem is the fact that although it may not be immediately obvious, the DatePicker and TimePicker controls actually use full-blown PhoneApplicationPage pages to present their selection lists (though the fact that they contain ApplicationBar buttons is probably a big hint.)  However, in some regards, these pages do not behave like “normal” pages – for example, when the application is deactivated and subsequently reactivated, you are not returned to the picker-page, but rather to your page that contains the control.  The picker controls go to some length to simulate this popup behavior:

  • In the OnNavigatedFrom handler, if the destination Uri indicates something external to the application, then the current value is added to the picker’s Page State dictionary with the key specified by the StateKey_Value const:
    Code Snippet
    1. // Save Value if navigating away from application
    2. if ("app://external/" == e.Uri.ToString())
    3. {
    4.     State[StateKey_Value] = Value;
    5. }
    • WHOA!  Did you see that?  Apparently there’s a “magic uri” that can be used to detect that the application is on the verge of being deactivated.  That one gets filed away under the “interesting stuff to know about” heading.
  • In the OnNavigatedTo handler, the page’s State dictionary is examined for this StateKey_Value, and if present, the value is retrieved from this entry, and then the NavigationService is called to back out of the page.
    Code Snippet
    1. // Restore Value if returning to application (to avoid inconsistent state)
    2. if (State.ContainsKey(StateKey_Value))
    3. {
    4.     Value = State[StateKey_Value] as DateTime?;
    5.  
    6.     // Back out from picker page for consistency with behavior of core pickers in this scenario
    7.     if (NavigationService.CanGoBack)
    8.     {
    9.         NavigationService.GoBack();
    10.     }
    11. }

Aha!

So now we have a page that is pretending to be a popup…we’ve all done something like this, and know from experience that inevitably it leads to problems…if you aren’t aware of the masquerade and don’t anticipate the “true” behavior, complications soon follow.  In this case, “closing the popup” results in a traditional page navigation sequence…including the execution of the OnNavigatedTo handler, which dutifully retrieves the Id of the item being edited from the query string, and loads the object to be edited into the page, which is then bound to the controls, including the DatePicker control….uh-oh.  Wait a minute!  How does the list-page communicate its selection back to the original page?  Once again it is time to go back to the toolkit source code.

  • When the user brings up the picker page by selecting the control on the editing page, the control locates the current PhoneApplicationFrame instance that is holding the pages in the current application (the RootFrame object)
  • A handler is attached to the frame’s Navigated event
  • The frame is told to navigate to the picker page
  • In the handler mentioned above, a determination is made to see whether the destination page is the original page that hosts the control or the picker page.
  • If the target page is the original hosting page, the frame event handlers are detached and the control’s Value is set to the value that was selected on the page.
    • This value is ultimately backed by a dependency property, which includes metadata information that (eventually) raises a ValueChanged event from the control.

It is important to note the relationship between the Page Navigation override methods and the Frame Navigation events:

  • Frame Navigating Event
  • Source Page OnNavigatingFrom Handler
  • Frame Navigated Event
  • Source OnNavigatedFrom Handler
  • Target Page OnNavigatedTo Handler

Note that the OnNavigatedTo handler is invoked a few steps AFTER the firing of the Navigated event.  Therefore, any values set in the Navigated event can easily be overwritten in the OnNavigatedTo handler, which is exactly the case that I ran into.  The control’s value was being set to the value selected in the picker page, which was in fact triggering all the necessary bindings.  Then the code in the OnNavigatedTo override was resetting this with the original object value. 

That’s Nice…Now What?

As I see it, there is a general solution that can be applied, with two possible manifestations.  I have to admit that they both feel a little awkward…maybe I’ll stumble into something down the road that leaves me feeling less “dirty.”  With that wonderful preamble, let’s look at the solution.

The general solution is to record that the current navigation is being triggered as a result of a call to the picker control and use that information to prevent and/or repair the undesired value override.  I mentioned there were (at least) two ways to do this.

The first approach involves using the OnNavigatedFrom override to detect if the target page is a DatePickerPage (or TimePickerPage), and if so, record a token in State that a Picker operation is in-progress. In OnNavigatedTo, detect this token, retrieve the value that must have been set when the control was updated, and bypass the use of the query string id to reset the underlying value. (This is missing something for tombstoning, which is detailed below.)

Code Snippet
  1. protected override void OnNavigatedFrom(NavigationEventArgs e)
  2. {
  3.     if (e.Content is DatePickerPage)
  4.     {
  5.         State["WentToDatePicker"] = true;
  6.     }
  7.  
  8.     base.OnNavigatedFrom(e);
  9. }
  10.  
  11. protected override void OnNavigatedTo(NavigationEventArgs e)
  12. {
  13.     base.OnNavigatedTo(e);
  14.  
  15.     if (State.ContainsKey("WentToDatePicker"))
  16.     {
  17.     }
  18.     else if (NavigationContext.QueryString.ContainsKey("Id"))
  19.     {
  20.         var idText = NavigationContext.QueryString["Id"];
  21.         if (Int32.TryParse(idText, out _originalItemId))
  22.         {
  23.             var itemToEdit = ((App)(Application.Current)).CurrentViewModel.TestDataItems.FirstOrDefault(x => x.Id == _originalItemId);
  24.             _itemBeingEdited = new TestData();
  25.             itemToEdit.CopyTo(_itemBeingEdited);
  26.         }
  27.     }
  28.     DataContext = _itemBeingEdited;
  29. }

In the second approach, the control’s ValueChanged event that was described above can be used in a similar value to determine that a picker operation is in progress, which can then be used to bypass the use of the query string id, as mentioned above.

Code Snippet
  1. private Boolean _isNavigationfromPicker;
  2.  
  3. private void HandleDateControlValueChanged(Object sender, DateTimeValueChangedEventArgs e)
  4. {
  5.     _isNavigationfromPicker = true;
  6. }
  7.  
  8. protected override void OnNavigatedTo(NavigationEventArgs e)
  9. {
  10.     base.OnNavigatedTo(e);
  11.  
  12.     if (_isNavigationfromPicker)
  13.     {
  14.         _isNavigationfromPicker = false;
  15.     }
  16.     else if (NavigationContext.QueryString.ContainsKey("Id"))
  17.     {
  18.         var idText = NavigationContext.QueryString["Id"];
  19.         if (Int32.TryParse(idText, out _originalItemId))
  20.         {
  21.             var itemToEdit = ((App)(Application.Current)).CurrentViewModel.TestDataItems.FirstOrDefault(x => x.Id == _originalItemId);
  22.             _itemBeingEdited = new TestData();
  23.             itemToEdit.CopyTo(_itemBeingEdited);
  24.         }
  25.     }
  26.     DataContext = _itemBeingEdited;
  27. }

Bonus Round – Tombstoning Support in the Editing Page

It is generally a good practice to add support for “remembering” the values that have been changed when the application is deactivated and restoring them when the application is reactivated, in order to save the user’s hard work on the page.  In that case, in OnNavigatedFrom the necessary details can be preserved (the code below uses a serializable object and just inserts that into the Page State dictionary), and in OnNavigatedTo, retrieve the value if necessary and use it for editing if present, and possibly override the saved date value if necessary.  If the value is not available for retrieval, the page navigation must be the result of the initial request, so the Id should be retrieved from the query string, and subsequently the item to be edited retrieved form its source.

Code Snippet
  1. protected override void OnNavigatedFrom(NavigationEventArgs e)
  2. {
  3.     if (e.Content is DatePickerPage)
  4.     {
  5.         State["WentToDatePicker"] = true;
  6.     }
  7.  
  8.     State["ItemEditingState"] = _itemBeingEdited;
  9.     base.OnNavigatedFrom(e);
  10. }
  11.  
  12. protected override void OnNavigatedTo(NavigationEventArgs e)
  13. {
  14.     base.OnNavigatedTo(e);
  15.  
  16.     DateTime? tempOverrideDate = null;
  17.     if (State.ContainsKey("WentToDatePicker") && _itemBeingEdited != null)
  18.     {
  19.         tempOverrideDate = _itemBeingEdited.DateOfInterest;
  20.     }
  21.  
  22.     if (State.ContainsKey("ItemEditingState"))
  23.     {
  24.         _itemBeingEdited = State["ItemEditingState"] as TestData;
  25.         if (tempOverrideDate.HasValue)
  26.         {
  27.             if (_itemBeingEdited != null)
  28.             {
  29.                 _itemBeingEdited.DateOfInterest = tempOverrideDate.Value;
  30.             }
  31.         }
  32.     }
  33.     else if (NavigationContext.QueryString.ContainsKey("Id"))
  34.     {
  35.         var idText = NavigationContext.QueryString["Id"];
  36.         if (Int32.TryParse(idText, out _originalItemId))
  37.         {
  38.             var itemToEdit = ((App)(Application.Current)).CurrentViewModel.TestDataItems.FirstOrDefault(x => x.Id == _originalItemId);
  39.             _itemBeingEdited = new TestData();
  40.             itemToEdit.CopyTo(_itemBeingEdited);
  41.         }
  42.     }
  43.     DataContext = _itemBeingEdited;
  44. }

Hopefully, if anyone else is seeing the same issue, this can shed some light on the problem and present some possible solutions.  The DatePicker and TimePicker controls are convenient and visually appealing approaches to presenting the data they represent in a touch-friendly manner.  However, to be used effectively, it is important to understand their nature as separate pages, despite any efforts they may make to masquerade as something else.

New England Code Camp 15 Presentation Materials

I have uploaded the content from my talks at yesterday’s (5/7/2011) New England Code Camp 15.  I had two talks – Introduction to Windows Phone 7 Development with Silverlight and Advanced Windows Phone 7 Development with Silverlight.  The content for my first talk can be found here, and the content for the second talk can be found here.  The content includes:

  • Presentation slides, which include the reference links I mentioned during the talk.
  • The sample code.  In the second presentation I discussed the Bing Maps control.  I have removed my Map Key and replaced it with the text “YOUR_KEY_HERE.”  To remove the licensing warning that appears when running the project, be sure to get an application key from the Bing Maps Developer Portal and replace the temporary text.

After having done various phone talks over the past year, I decided I needed to overhaul the content prior to this presentation.  This was done both in order to relate the contents of the presentation to something that more closely resembled an actual app, as well as in anticipation of including the upcoming “Mango” content.  I think the presentation got closer on those fronts, but it still needs some tuning (especially the first half, which ran a bit long…my apologies to the attendees for having to rush through some of the material.)

I have also discovered an interesting defect in the application that occurred after adding the code to handle saving page-state when the app is tombstoned.  What happens is that the closing of the DatePicker control (from the Silverlight for Windows Phone Toolkit) sets the value of the property before an OnNavigatedTo event is called, which resets the value to what was serialized when the control first appears.  The net result is that the DatePicker control ceases to change the date.  I included a quick fix for this in the code published above, and will be taking a deeper look at this control in the coming days, with a more thorough post to follow.

LightTouch Enhancements–Popups and “Layered” Controls

While I am actively working on adding new functionality to the LightTouch library (such as adding the Pinch Gesture to bring parity with the GestureListener for Windows Phone from the Silverlight for Windows Phone Toolkit) for an upcoming release, a couple of issues came to light that made adding a new release appropriate.  As the title of this post suggests, these issues relate to support for touch in Popups (especially ChildWindow controls) and for controls where different layers of the control’s visual tree make use of different behaviors.

Support for Popups / Child Windows

At the heart of LightTouch is the Manipulation2DBehavior.  Much of the functionality behind this behavior is discussed here.  One of the basic methods’ responsibilities is to determine, based on the coordinates of the touch event, what controls are under that point and which of those controls have the Manipulation2DBehavior attached.  To do this lookup, the VisualTreeHelper.FindElementsInHostCoordinates function is used, with Application.Current.RootVisual supplied for the relative UIElement.  This is where difficulty with popups occurred.  Popup controls exist outside of the Visual Tree of the application’s RootVisual.  This can be seen in the following UI, with its Visual Tree as reported by Silverlight Spy.  Note how the Popup control appears as a sibling to the MainPage element.

image

image

So in order to locate the controls with behaviors in the popup, we have to use the Popup as the relative UIElement for the call to VisualTreeHelper.FindElementsInHostCoordinates.  Now to find the popup in the Visual Tree…this can be accomplished with the VisualTreeHelper.GetOpenPopups call.  Unfortunately, this call does not document how the list is populated relative to the Z-order of the ChildWindow (in case multiple ChildWindows are being displayed, issues of UI-propriety with this approach notwithstanding.)  Empirically, it seems that the topmost child window (if present) is the first in the list, so if any ChildWindows are being shown, we locate the first one in the list, and use it as the argument to FindElementsInHostCoordinates.  From there on, things progress as they had previously.

Supporting “Layers”

Shrek: “NO!  Layers.  Onions have layers.  Ogres have layers.   Onions have layers.  You get it?  We both have layers”
Donkey: “Oh, you both have layers.  Oh.  You know, not everybody likes onions.  What about cake?  Everybody loves cake!”
– Shrek (2001)

So what do I mean about layers?  In this case, I am referring to a situation where multiple related controls under a given point in a visual tree have Manipulation Behaviors attached.  An example would be a ListBox control with the ListBoxTouchScrollBehavior attached, and the underlying data template for the items in the list box having a GestureListener attached with a handler for a DoubleTap event.  The original implementation stopped at the first matching behavior, resulting in the ListBox scrolling, but ignoring the DoubleTap event.  The new implementation now identifies and processes all Manipulations under the given touch point.  This is similar to a Bubbling event, with a couple of key differences.  First, there is no notion of “handled.”  Second, the relationships do not have to exist in common branches of the same visual tree.

Available for Download

The latest code is up on CodePlex and the Alpha-2 release has been built based on these changes, which can be downloaded here.  I hope people find it helpful.

New Hampshire .Net (Seacoast) User Group Presentation Materials

I have uploaded the content from my Introduction to Windows Phone 7 Development with Silverlight talk at the New Hampshire .Net Seacoast User Group meeting last night in Portsmouth. The content can be found here, and includes:

  • Presentation slides, which include the reference links I mentioned during the talk.
  • The sample code, broken into 2 projects. Because the Notification Services portion of the talk adds extra projects to the solution ad is a little more complex to build and launch, that portion has been broken out into its own separate zip file.

For the Notification Services portion of the demo, the web project should be launched first, followed by the phone project (since the phone app calls the web site to register its URL for receiving notifications.) If the solution is run with the web project set as the default start project, the application bits do get deployed to the phone, but the debugger is not hooked up (unless multiple startup projects are used.) If debugging of the phone app is desired, it can be achieved by right clicking on the phone project and selecting Debug / Start New Instance. Also, this time the Notification Services demo includes the Push Notification Server-Side Helper recipe.

As usual, I have “sanitized” the uploaded code content by removing my personal Bing Maps application key. For information on how to obtain your own map key, please check out the Bing Maps Developer Portal.

I have also added a project that  shows the technique for “immediately” updating the application’s tile via a temporary Notification Services endpoint.  This is based on the code first presented here

The posted Slide Deck includes the slides that call out new content in the upcoming “Mango” release of WP7, and I have edited one slide to include one IMPORTANT new addition to the phone SDK that I forgot to previously include – support for Sockets.

Many thanks to Pat Tormey for inviting me to be the inaugural speaker for this new user group, as well as to Mark Mullin and Global Relief Technologies for providing a great site.  Also many thanks are deserved by the attendees, who asked some of the best questions I have gotten since I started doing these talks.

New for Windows Phone (Now) – the Async CTP

As is often the case when big events happen, sometimes some important announcements can get overlooked and/or overshadowed.  Mix 2011 certainly featured big news, including Silverlight 5 Beta, the Windows Phone 7.5 features, IE10, and the latest in the MVC framework.  However, in all of the hoopla, the release of the SP1 refresh of the Microsoft Visual Studio Async CTP SP1 Refresh, as well as some of the important changes, may have gone unnoticed.

There are a few important things to note regarding the Async CTP Refresh:

  • It includes an “As-Is” Go-Live license for use in production environments (albeit with some stern warnings about why you should really consider NOT using it in production code.)
  • It includes support for .Net, Silverlight 4, and Windows Phone 7.0.

Let me restate that last point.  The Async CTP works with Windows Phone 7 today.  No need to wait for the Mango update.

Now Just “await” a Minute…

As a CTP, and with the “As Is” license, there is no guarantee that this will eventually make it into the framework, and even if it does, it may take a very different form.  That being said, it is a great opportunity to see where things are headed and to offer feedback to the development team as to how to best shape this functionality to be as useful as it can be. 

That’s Nice.  How Long do I have to “await” to See Some Code?

So what do the Async tools bring to the table?  Among other things, it allows asynchronous code to be written without the “disruption” that currently occurs when that is being done today.  While a full discussion of asynchrony and the problems it solves and the new ones it presents are beyond the scope of this discussion, some brief coverage is merited.  Writing synchronous code today follows the pattern of “do something, wait for it to finish, then proceed.”  Altering this to be asynchronous (often) changes the pattern either the Asynchronous Programming Model/APM (“Call BeginXXX to start the operation, then EndXXX to wait for it to be completed”) or to the Event-Asynchronous Pattern/EAP (“start doing something, and raise an event that I am listening for when you are done.”)  When the completion of one asynchronous operation needs to trigger another one, code can get complicated and messy.  This situation is particularly apparent to those who use Silverlight and the related Silverlight for Windows Phone, as all I/O operations in Silverlight are inherently asynchronous.  The Async CTP brings a third model – the Task-based Asynchronous Pattern/TAP. 

The TAP model features the use of the async and await keywords to allow asynchronous code to be written in a much more linear fashion.  Under the covers, the compiler will generate all of the necessary “goo”, allowing the development to focus on the problem at a higher level.  The following code may help to visualize what this all means.

In the code that follows, a WebClient is spun up to return the data at a given URL.  The number of bytes in that data is returned as the result of the function.  This is the synchronous version of the code.

Code Snippet
  1. private void button1_Click(object sender, RoutedEventArgs e)
  2. {
  3.     var count = DoSomething();
  4.     MessageBox.Show(count.ToString());
  5. }
  6.  
  7. private Int32 DoSomething()
  8. {
  9.     var webClient = new WebClient();
  10.     var data = webClient.DownloadString(new Uri("http://www.wintellect.com/Consulting/Consultants/John-Garland&quot;));
  11.     var results = data.Length;
  12.     return results;
  13. }

Note that this code blocks the UI when called, and cannot be used in Silverlight or Silverlight for Windows Phone applications.  So what if you don’t want to block the UI while retrieving the information (or better yet, what if you cannot, as is the case with Silverlight?)  Bring in asynchrony, but notice how it changes the nature of the function.  Where the desire had been to call a function to retrieve a result, now the function is called to eventually signal a response, which must be subscribed to by the calling code.

Code Snippet
  1. private void DoSomethingEAP()
  2. {
  3.     var webClient = new WebClient();
  4.     webClient.DownloadDataCompleted += HandleWebClientDownloadDataCompleted;
  5.     webClient.DownloadStringAsync(new Uri("http://www.wintellect.com/Consulting/Consultants/John-Garland&quot;));
  6. }
  7.  
  8. private void HandleWebClientDownloadDataCompleted(object sender, DownloadDataCompletedEventArgs e)
  9. {
  10.     var data = e.Result.Length;
  11.     MessageBox.Show(data.ToString());
  12. }

This is somewhat compressed and contained when inline lambda expressions are brought to bear, but the fundamental problem still exists.  The function has been modified because of its asynchronous implementation. 

Code Snippet
  1. private void DoSomethingEAPLambda()
  2. {
  3.     var webClient = new WebClient();
  4.     webClient.DownloadDataCompleted += (o, e) =>
  5.     {
  6.         var data = e.Result.Length;
  7.         MessageBox.Show(data.ToString());
  8.     };
  9.     webClient.DownloadStringAsync(new Uri("http://www.wintellect.com/Consulting/Consultants/John-Garland&quot;));
  10. }

Now lets see how using the Async tools change things. 

Code Snippet
  1. private async void button1_Click(object sender, RoutedEventArgs e)
  2. {
  3.     var count = await DoSomethingAsync();
  4.     MessageBox.Show(count.ToString());
  5. }
  6.  
  7. private async Task<Int32> DoSomethingAsync()
  8. {
  9.     var webClient = new WebClient();
  10.     var data = await webClient.DownloadStringTaskAsync(new Uri("http://www.wintellect.com/Consulting/Consultants/John-Garland&quot;));
  11.     var results = data.Length;
  12.     return results;
  13. }

Note that the functions now much more closely resemble the initial synchronous version. 

I’m Tired of “awaiting”.  Get Me the Bits.

This was just a light overview.  There are many other combinations and applications for the TAP.  Hopefully, this was enough to pique some curiosity.  If interested, the download for the Microsoft Visual Studio Async CTP SP1 Refresh is available at http://msdn.microsoft.com/en-us/vstudio/async.  This includes documentation, samples, and walkthroughs.  The tools require Visual Studio 2010 SP1.  Check out Eric Lippert’s announcement of the release (and additional links) at http://blogs.msdn.com/b/ericlippert/archive/2011/04/13/refreshing-the-async-ctp.aspx.

Enhanced Touch Manipulations for Silverlight and Windows Phone

Following up on Jeff’s series on Touch Interfaces for Windows Phone, I wanted to show an additional technique that allows support for Manipulations in Silverlight. Furthermore, when this is extended to the Phone, it allows support for moving two or more UI elements independently, which as Jeff noted, is not possible with the Manipulation events exposed in Silverlight for Windows Phone.
The article by Charles Petzold that Jeff refers to discusses the fact that while Manipulation events are present in Silverlight 4, they are not available for use. Attempts to hook these events either in code or in markup will result in a runtime exception. The good news is that there is a downloadable assembly that, with some additional work on the developers’ part, can provide the benefits of Manipulations to Silverlight 4 applications. This assembly is available as part of the Microsoft Surface Manipulations and Inertia Sample for Microsoft Silverlight, which is available for download here. The download includes the System.Windows.Input.Manipulations.dll assembly (hereafter the “Manipulations Assembly”) and a sample application, including code, which offers guidance as to how it can be used.
The Manipulations Assembly is a Silverlight version of the equivalent assembly included in .Net 4.0, and exposes a set of manipulation and inertia-related classes. Besides the inclusion of the letters “2D” in the name of each of the namespace members, these items work together to provide functionality that is similar to what Jeff described. Documentation for these elements is available at http://msdn.microsoft.com/en-us/library/system.windows.input.manipulations(VS.100).aspx.
As I mentioned above, using the Manipulation2D classes will require some additional work. Specifically, your code will need to provide input to the manipulation engine, provided by the ManipulationProcessor2D class, converting events of your choosing into individual manipulation actions and indicating when a particular manipulation has been completed. The manipulation engine will take that input and convert it into the appropriate Started, Delta, and Completed events, including calculating offsets, velocities, etc.
With respect to Christopher Walken, “Enough talkie-talkie, more ping-pong” (Let’s see some code!)
First we’ll start with a Silverlight 4.0 project and add a reference to the Manipulations Assembly (locate the folder the content was downloaded and extracted into from the link above.) As has been discussed, Silverlight UIElements expose several Manipulation events, none of which can be used. However, it would be handy if the Manipulations Assembly could be used to simulate the presence of those events on these types of objects. So we turn to Behaviors, with the intent of providing a behavior that allows us to raise these events out of instances of UIElements in our markup. This means we also need a reference to System.Windows.Interactivity.dll.
clip_image002
Moving from Touches to Manipulations
Once we have our references figured out, we can get to work. As I mentioned above, one of the caveats of working with the Manipulations Assembly is that some extra work is required. Specifically, you need to catch the application-wide Touch events and then feed them to the classes in the Manipulations Assembly. That’s a multi-step process, because Touch events are application-wide and we want manipulations to appear as if they were sourced from individual UIElements. So step 1 is to hook the touch events when the behavior is attached to its UIElement.
First when the behavior is attached:
Code Snippet
  1. protected override void OnAttached()
  2. {
  3. base.OnAttached();
  4. HookTouchEvents();
  5. HookMouseEvents();
  6. _subscribedBehaviors.Add(this);
  7. }
And the actual event subscription:
Code Snippet
  1. private static void HookTouchEvents()
  2. {
  3. if (!_touchEventsHooked)
  4. {
  5. _touchEventsHooked = true;
  6. System.Windows.Input.Touch.FrameReported += OnFrameReported;
  7. }
  8. }
There are two important things to note here. First, when attaching the behavior, the current instance is being added to a static list of attached behaviors (and removed from the list in the OnDetaching override)…more on this in a minute. Also, notice that the Touch events are only being hooked per type, instead of per class. This prevents the touch event from firing once per behavior instance.
The majority of the hard work happens in the OnFrameReported handler and the associated ProcessTouchPoints method, which does three key processing steps.
First, it identifies any new touch-point/behavior associations:
Code Snippet
  1. // Find any new touch-point/behavior associations.
  2. foreach (var touchPoint in touchPoints.Where(x => x.Action == TouchAction.Down))
  3. {
  4. // There shouldn’t be any of these!
  5. if (!_capturedBehaviors.ContainsKey(touchPoint.DeviceId))
  6. {
  7. // “Unowned” touch device – see if a behavior can be found for it to associate with at its current position…
  8. var matchingBehavior = GetBehaviorAssociatedWithTouchPoint(touchPoint.Position, _subscribedBehaviors);
  9. if (matchingBehavior != null)
  10. {
  11. _capturedBehaviors.Add(touchPoint.DeviceId, matchingBehavior);
  12. }
  13. }
  14. }
This is done by examining all of the incoming touch points for those whose action type is “Down.” For each of those touch points, we check to find any UIElements with an associated behavior that are directly under the touchpoint. For this, we use the VisualTreeHelper.FindElementsInHostCoordinates call and iterate over those elements to see if they match the AssociatedObject of any of the behaviors that are in the list that we maintain when attaching/detaching the behaviors.
Code Snippet
  1. private static Manipulate2DBehavior GetBehaviorAssociatedWithTouchPoint(Point touchPointPosition, IEnumerable<Manipulate2DBehavior> behaviorsToCheck)
  2. {
  3. Manipulate2DBehavior result = null;
  4. IEnumerable<UIElement> elements = VisualTreeHelper.FindElementsInHostCoordinates(touchPointPosition, Application.Current.RootVisual);
  5. foreach (var element in elements)
  6. {
  7. result = behaviorsToCheck.FirstOrDefault(x => x.AssociatedObject == element);
  8. if (result != null) break;
  9. }
  10. return result;
  11. }
If a matching behavior is found, it is considered to be “captured” and is added to a static dictionary that gathers the touchpoint’s DeviceId and the behavior. This step is what actually enables us to track multiple concurrent manipulations, as we are not able to react to multiple “captured” manipulations, as will be explained shortly.
The next step in the Touch Point processing is to actually process the touch events for all of the captured behaviors. This includes those who were just gathered in the previous step, as well as any previous touches for which a touch-down has been received without a touch-up to end it.
Code Snippet
  1. // Process any current touch-point/behaviors
  2. foreach (var capturedBehavior in _capturedBehaviors.Values.Distinct())
  3. {
  4. var associatedTouchDeviceIds = _capturedBehaviors.Where(x => x.Value == capturedBehavior).Select(x => x.Key);
  5. var associatedTouchPoints = touchPoints.Where(x => associatedTouchDeviceIds.Contains(x.DeviceId));
  6. capturedBehavior.HandleTouchEvents(associatedTouchPoints);
  7. }
To do this, the current list of touch points associated with any one behavior (in case multiple fingers are touching the same UIElement) are passed to the capturedBehavior’s HandleTouchEvents method.
Code Snippet
  1. private void HandleTouchEvents(IEnumerable<TouchEventData> points)
  2. {
  3. IEnumerable<Manipulator2D> manips = points.Select(p => new Manipulator2D
  4. {
  5. Id = p.DeviceId,
  6. X = p.PositionX,
  7. Y = p.PositionY
  8. });
  9. _manipulationProcessor.ProcessManipulators(DateTime.Now.Ticks, manips);
  10. }
The HandleTouchEvents method introduces an instance of the Manipulation2DProcessor, which is the “engine” that drives the manipulation calculations. Every instance of the behavior class has its own instance of the Manipulation2DProcessor called _manipulationProcessor that is responsible for handling the manipulation inputs (via the Manupulator2D class and ProcessManipulators method), as well as raising the appropriate manipulation events. Once the correct behavior (and hence Manipulation2DProcessor instance) is determined from the location of the input touchpoints, those touchpoints can be provided to the manipulation engine for it to handle the rest.
The final task of the ProcessTouchPoints method is to remove any “captured” behaviors based on the corresponding touchpoint TouchUp actions.
Code Snippet
  1. // Find and remove touch point/behavior associations as needed
  2. foreach (var touchPoint in touchPoints.Where(x => x.Action == TouchAction.Up))
  3. {
  4. if (_capturedBehaviors.ContainsKey(touchPoint.DeviceId))
  5. {
  6. var matchingBehavior = _capturedBehaviors[touchPoint.DeviceId];
  7. _capturedBehaviors.Remove(touchPoint.DeviceId);
  8. if (!_capturedBehaviors.ContainsValue(matchingBehavior))
  9. {
  10. //That was the last device holding on to the specific behavior…
  11. matchingBehavior.CompleteManipulation();
  12. }
  13. }
  14. }
For each touchpoint whose action is “Up”, the corresponding touchpoint/behavior entry is removed from the “captured” dictionary. If the removal causes no more references to a particular behavior to be in the dictionary, CompleteManipulation is called, which simply calls CompleteManipulation on the behavior’s manipulation engine, signaling it that the manipulation it was tracking is now complete.
Observant readers may have noticed that the method for associating behaviors and touches within the OnFrameReported handler means that the previous discussion treats multiple finger presses on a UIElement as a single unit – there is no support for Pinch and Rotate, which is being left as an exercise for another day…
Raising the Events
Once all of this plumbing is done, raising the events is simple enough – it is just a matter of subscribing to the manipulation engine’s events and echoing them to the behavior’s consumers. The manipulation engine events are hooked in the behavior instance’s constructor (the event declaration with the delegate {} assignment avoids all that nasty boilerplate check-for-null-when-firing-an-event code…)
Code Snippet
  1. public event EventHandler<Manipulation2DStartedEventArgs> ManipulationStarted = delegate { };
  2. public event EventHandler<Manipulation2DDeltaEventArgs> ManipulationDelta = delegate { };
  3. public event EventHandler<Manipulation2DCompletedEventArgs> ManipulationCompleted = delegate { };
Code Snippet
  1. /// <summary>
  2. /// Initializes a new instance of the <see cref=”Manipulate2DBehavior “/> class.
  3. /// </summary>
  4. public Manipulate2DBehavior()
  5. {
  6. _manipulationProcessor = new ManipulationProcessor2D(Manipulations2D.Translate);
  7. _manipulationProcessor.Started += (o, e) => ManipulationStarted(AssociatedObject, e);
  8. _manipulationProcessor.Delta += (o, e) => ManipulationDelta(AssociatedObject, e);
  9. _manipulationProcessor.Completed += OnManipulationCompleted;
  10. }
Hooking it All Up
To wire up the behavior, simply attach it to your desired UIElement and provide handlers for the events, as in the following code, adapted from Jeff’s articles…sorry, no animated penguins
Markup:
Code Snippet
  1. <navigation:Page x:Class=”Wintellect.Touch.MultiManipulatePage”
  2. xmlns=”http://schemas.microsoft.com/winfx/2006/xaml/presentation&#8221;
  3. xmlns:x=”http://schemas.microsoft.com/winfx/2006/xaml&#8221;
  4. xmlns:d=”http://schemas.microsoft.com/expression/blend/2008&#8243;
  5. xmlns:mc=”http://schemas.openxmlformats.org/markup-compatibility/2006&#8243;
  6. xmlns:navigation=”clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Navigation”
  7. xmlns:i=”clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity”
  8. xmlns:Touch=”clr-namespace:Wintellect.Touch;assembly=Wintellect.Touch”
  9. mc:Ignorable=”d” d:DesignWidth=”800″ d:DesignHeight=”600″
  10. Title=”MultiManipulatePage Page”>
  11. <Grid x:Name=”LayoutRoot” Background=”White”>
  12. <Rectangle x:Name=”RedRect” Width=”200″ Height=”200″ Fill=”Red”>
  13. <Rectangle.RenderTransform>
  14. <TranslateTransform Y=”-200″/>
  15. </Rectangle.RenderTransform>
  16. <i:Interaction.Behaviors>
  17. <Touch:Manipulate2DBehavior
  18. IsInertial=”True”
  19. ManipulationStarted=”TouchShapeBehavior_ManipulationStarted”
  20. ManipulationDelta=”TouchShapeBehavior_ManipulationDelta”
  21. ManipulationCompleted=”TouchShapeBehavior_ManipulationCompleted”/>
  22. </i:Interaction.Behaviors>
  23. </Rectangle>
  24. <Ellipse x:Name=”BlueCircle” Width=”200″ Height=”200″ Fill=”Blue”>
  25. <Ellipse.RenderTransform>
  26. <TranslateTransform Y=”200″ />
  27. </Ellipse.RenderTransform>
  28. <i:Interaction.Behaviors>
  29. <Touch:Manipulate2DBehavior
  30. IsInertial=”True”
  31. ManipulationStarted=”TouchShapeBehavior_ManipulationStarted”
  32. ManipulationDelta=”TouchShapeBehavior_ManipulationDelta”
  33. ManipulationCompleted=”TouchShapeBehavior_ManipulationCompleted”/>
  34. </i:Interaction.Behaviors>
  35. </Ellipse>
  36. </Grid>
  37. </navigation:Page>
Codebehind:
Code Snippet
  1. public partial class MultiManipulatePage : Page
  2. {
  3. public MultiManipulatePage()
  4. {
  5. InitializeComponent();
  6. }
  7. // Executes when the user navigates to this page.
  8. protected override void OnNavigatedTo(NavigationEventArgs e)
  9. {
  10. }
  11. private void TouchShapeBehavior_ManipulationStarted(Object sender, Manipulation2DStartedEventArgs e)
  12. {
  13. var senderShape = (Shape)sender;
  14. senderShape.Tag = senderShape.Fill;
  15. senderShape.Fill = new SolidColorBrush(Colors.Yellow);
  16. }
  17. private void TouchShapeBehavior_ManipulationDelta(Object sender, Manipulation2DDeltaEventArgs e)
  18. {
  19. var senderShape = (Shape)sender;
  20. var translateTransform = (TranslateTransform)senderShape.RenderTransform;
  21. translateTransform.X += e.Delta.TranslationX;
  22. translateTransform.Y += e.Delta.TranslationY;
  23. }
  24. private void TouchShapeBehavior_ManipulationCompleted(Object sender, Manipulation2DCompletedEventArgs e)
  25. {
  26. var senderShape = (Shape)sender;
  27. //TODO: This should really be done at the end of inertia, otherwise it will take the original color and keep moving…
  28. senderShape.Fill = senderShape.Tag as Brush;
  29. }
  30. }
That’s Interesting – Now What about the Phone?
What we have seen so far is the use of Behaviors and the Manipulations Assembly to provide manipulation event support in Silverlight 4 similar to that available in Windows Phone – in fact, better than that available in Windows Phone, since it can handle the simultaneous manipulation of multiple objects. But I said at the beginning that this could be extended to the phone – soooo…
It turns out this is quite simple to add to the phone. Because Windows Phone can consume regular Silverlight assemblies (as long as they do not stray outside of the phone’s own unique ‘sandbox’), the process is quite straightforward. Starting with a regular Silverlight for Windows Phone project, once again add references to System.Windows.Interactivity and to the Manipulations Assembly System.Windows.Input.Manipulations.dll. Then create the exact same Manipulate2DBehavior class in the phone project (there are techniques for sharing this code, including compiling it into its own assembly or using Add Existing as Link, for which information can be found elsewhere.) Using the same markup and codebehind content will provide similar results on the phone.
Markup:
Code Snippet
  1. <phone:PhoneApplicationPage
  2. x:Class=”MultiManipulate.Views.MultiManipulatePage”
  3. xmlns=”http://schemas.microsoft.com/winfx/2006/xaml/presentation&#8221;
  4. xmlns:x=”http://schemas.microsoft.com/winfx/2006/xaml&#8221;
  5. xmlns:phone=”clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone”
  6. xmlns:shell=”clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone”
  7. xmlns:d=”http://schemas.microsoft.com/expression/blend/2008&#8243;
  8. xmlns:mc=”http://schemas.openxmlformats.org/markup-compatibility/2006&#8243; xmlns:i=”clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity” xmlns:Touch=”clr-namespace:Wintellect.Touch” FontFamily=”{StaticResource PhoneFontFamilyNormal}
  9. FontSize=”{StaticResource PhoneFontSizeNormal}
  10. Foreground=”{StaticResource PhoneForegroundBrush}
  11. SupportedOrientations=”Portrait” Orientation=”Portrait”
  12. mc:Ignorable=”d” d:DesignHeight=”768″ d:DesignWidth=”480″
  13. shell:SystemTray.IsVisible=”True”>
  14. <!–LayoutRoot is the root grid where all page content is placed–>
  15. <Grid x:Name=”LayoutRoot” Background=”Transparent”>
  16. <Grid.RowDefinitions>
  17. <RowDefinition Height=”Auto”/>
  18. <RowDefinition Height=”*”/>
  19. </Grid.RowDefinitions>
  20. <!–TitlePanel contains the name of the application and page title–>
  21. <StackPanel x:Name=”TitlePanel” Grid.Row=”0″ Margin=”12,17,0,28″>
  22. <TextBlock x:Name=”ApplicationTitle” Text=”MY APPLICATION” Style=”{StaticResource PhoneTextNormalStyle}“/>
  23. <TextBlock x:Name=”PageTitle” Text=”page name” Margin=”9,-7,0,0″ Style=”{StaticResource PhoneTextTitle1Style}“/>
  24. </StackPanel>
  25. <!–ContentPanel – place additional content here–>
  26. <Grid x:Name=”ContentPanel” Grid.Row=”1″ Margin=”12,0,12,0″>
  27. <Rectangle x:Name=”RedRect” Width=”100″ Height=”100″ Fill=”Red”>
  28. <Rectangle.RenderTransform>
  29. <TranslateTransform/>
  30. </Rectangle.RenderTransform>
  31. <i:Interaction.Behaviors>
  32. <Touch:Manipulate2DBehavior
  33. IsInertial=”True”
  34. ManipulationStarted=”TouchShapeBehavior_ManipulationStarted”
  35. ManipulationDelta=”TouchShapeBehavior_ManipulationDelta”
  36. ManipulationCompleted=”TouchShapeBehavior_ManipulationCompleted”/>
  37. </i:Interaction.Behaviors>
  38. </Rectangle>
  39. <Ellipse x:Name=”BlueRect” Width=”100″ Height=”100″ Fill=”Blue”>
  40. <Ellipse.RenderTransform>
  41. <TranslateTransform />
  42. </Ellipse.RenderTransform>
  43. <i:Interaction.Behaviors>
  44. <Touch:Manipulate2DBehavior
  45. IsInertial=”True”
  46. ManipulationStarted=”TouchShapeBehavior_ManipulationStarted”
  47. ManipulationDelta=”TouchShapeBehavior_ManipulationDelta”
  48. ManipulationCompleted=”TouchShapeBehavior_ManipulationCompleted”/>
  49. </i:Interaction.Behaviors>
  50. </Ellipse>
  51. </Grid>
  52. </Grid>
  53. </phone:PhoneApplicationPage>
Codebehind:
Code Snippet
  1. public partial class MultiManipulatePage : PhoneApplicationPage
  2. {
  3. public MultiManipulatePage()
  4. {
  5. InitializeComponent();
  6. }
  7. private void TouchShapeBehavior_ManipulationStarted(Object sender, Manipulation2DStartedEventArgs e)
  8. {
  9. var senderShape = (Shape)sender;
  10. senderShape.Tag = senderShape.Fill;
  11. senderShape.Fill = new SolidColorBrush(Colors.Yellow);
  12. }
  13. private void TouchShapeBehavior_ManipulationDelta(Object sender, Manipulation2DDeltaEventArgs e)
  14. {
  15. var senderShape = (Shape)sender;
  16. var translateTransform = (TranslateTransform)senderShape.RenderTransform;
  17. translateTransform.X += e.Delta.TranslationX;
  18. translateTransform.Y += e.Delta.TranslationY;
  19. }
  20. private void TouchShapeBehavior_ManipulationCompleted(Object sender, Manipulation2DCompletedEventArgs e)
  21. {
  22. var senderShape = (Shape)sender;
  23. //TODO: This should really be done at the end of inertia, otherwise it will take the original color and keep moving…
  24. senderShape.Fill = senderShape.Tag as Brush;
  25. }
  26. }
The Wintellect Silverlight Touch Library – aka LightTouch
Ultimately, the code referenced above has made its way into a larger library that provides additional Touch functionality beyond just attachable support for Manipulations, including support for Gestures, as well as enhancements that make it very easy to add touch support to scrollable controls like the ListBox.  For Manipulations, Inertia is also added to the mix.  This core manipulation Behavior is actually at the heart of each of these additional elements.  This project is being published up on CodePlex and can be accessed at http://lighttouch.codeplex.com.
Summary
Obviously, it is a little hard to see all this in action. To that end, if you have a multi-touch-enabled monitor, you can download  the project from CodePlex and try it out. While there was some tedious boilerplate code required to hook touch events up to the individual UIElements’ manipulation behaviors, what results in the end is a reusable behavior that can be easily reused throughout an application.
Author’s Note: Parts of the content prepared for this article were inspired by content from Jeff Prosise’s blog series on “Touch Interfaces for Windows Phone”, as well as Mike Taulty’sTouched” blog series.

Silverlight for Windows Phone Programming Tip – Navigation

When using the Silverlight for Windows Phone APIs to navigate from one page to another, it is necessary to use the Navigate method on the PhoneApplicationPage instance’s NavigationService property.  When making this call, it is important to note the required Uri Syntax, as follows:

Navigating to a page in the same assembly:

NavigationService.Navigate(new Uri("/Folder(s)/TargetPage.xaml", UriKind.relative));

Navigating to a page in another assembly:

NavigationService.Navigate(new Uri("/AssemblyName;component/Folder(s)/TargetPage.xaml", UriKind.Relative));

The current MSDN Documentation (update date of 1/28/2001) for the Navigate method does not include any text that specifies the requirements for the syntax or the Uri, so here goes my attempt to provide the missing content:

  • The provided Uri must start with a forward slash (/), indicating the content is local to the Xap file.  Failure to include the leading slash will result in an ArgumentException (details below) being thrown.
  • The UriKind must be specified as UriKind.Relative (or URIKindRelativeOrAbsolute, but why ask for trouble?)  Omitting the UriKind uses the default value of URIKind.Absolute, which yields the same ArgumentException
  • To navigate to a page in another assembly contained within the same Xap file, the syntax must include the Assembly Name for the assembly that contains the page, then a semicolon and the word “component”, followed by the folder path to the page.
    • The leading slash and the specification of a Relative Uri are still required, with the same consequences if omitted.
    • Omitting the word “component” results in an InvalidOperationException (No Xaml was found at the location…)

The description contained in the Argument exception is actually quite helpful – it reads “Navigation is only supported to relative URIs that are fragments, or begin with ‘/’, or which contain ‘;component.”  However, you have to first fail in your attempts to navigate and be in a position to trap and analyze the exception for it to be useful.

Making small and subtle mistakes in the navigation Uri is quite easy to do…not only is missing relevant documentation missing as noted above, but this API also falls quite short in the “Pit of Success” category at the expense of matching up with the Navigation API in regular Silverlight (I have witnessed good presenters’ phone demos get derailed and lose precious time because of some simple omissions in this regard.)  A better alternative would have been to have offered a way to avoid the specific nuances of the API in favor of phone-specific methods.  (Jeremy’s Ultra-Light Windows Phone 7 MVVM Framework accomplishes something very close/similar.)  Fortunately, if you are doing a lot of navigation in your app and would like to include a set of phone-specific NavigateToPhonePage calls which figure out and correct the necessary Uris for you, there is a quick way to do so – using Extension Methods.  A set of simple & helpful starter Phone Navigation extension methods follows:

public static class PhoneNavigationServiceExtensions
{     
      public static void NavigateToPhonePage(this NavigationService navigationService, String page)     
      {          
           if (navigationService == null) throw new ArgumentNullException("navigationService");          
           if (!page.StartsWith("/")) page = "/" + page;  //Compensate for lack of leading slash          
           navigationService.Navigate(new Uri(page, UriKind.Relative)); //Build the Uri, force it to be Relative.
      }

      public static void NavigateToLocalPhonePage(this NavigationService navigationService, String page)
      {
           NavigateToPhonePage(navigationService, page);
      }     

      public static void NavigateToExternalPhonePage(this NavigationService navigationService, String assemblyName, String page)     
      {          
           if (!page.StartsWith("/")) page = "/" + page;  //Compensate for lack of leading slash in the page          
           NavigateToPhonePage(navigationService, String.Format("{0};component{1}", assemblyName, page));     
      }
}

Given these extensions, the following navigation calls all work without a need to specify the UriKind, and with varying inclusions and omissions of the forward slashes:

// Using NavigateToLocalPhonePage requires nothing more than the path to the page's xaml file
NavigationService.NavigateToLocalPhonePage("/Folder(s)/TargetPage.xaml");
NavigationService.NavigateToLocalPhonePage("Folder(s)/TargetPage.xaml");

// Using NavigateToExternalPhonePage calls out the need for the assembly name and the page's xaml file path
NavigationService.NavigateToExternalPhonePage("/WindowsPhoneClassLibrary1", "/Folder(s)/TargetPage.xaml");
NavigationService.NavigateToExternalPhonePage("WindowsPhoneClassLibrary1", "Folder(s)/TargetPage.xaml");

// Using the NavigateToPage method directly works for either, but the External page must be specified correctly
NavigationService.NavigateToPhonePage("Folder(s)/TargetPage.xaml");
NavigationService.NavigateToPhonePage("WindowsPhoneClassLibrary1;component/Folder(s)/TargetPage.xaml");

Tech Valley .Net User Group Presentation Materials

I have uploaded the content from my Introduction to Windows Phone 7 Development with Silverlight talk at the Tech Valley .Net User Group meeting last night in Albany. The content can be found here, and includes:

  • Presentation slides, which include the reference links I mentioned during the talk.
  • The sample code, broken into 2 projects. Because the Notification Services portion of the talk adds extra projects to the solution ad is a little more complex to build and launch, that portion has been broken out into its own separate zip file.

For the Notification Services portion of the demo, the web project should be launched first, followed by the phone project (since the phone app calls the web site to register its URL for receiving notifications.) If the solution is run with the web project set as the default start project, the application bits do get deployed to the phone, but the debugger is not hooked up (unless multiple startup projects are used.) If debugging of the phone app is desired, it can be achieved by right clicking on the phone project and selecting Debug / Start New Instance. Also, this time the Notification Services demo includes the Push Notification Server-Side Helper recipe.

As usual, I have “sanitized” the uploaded code content by removing my personal Bing Maps application key. For information on how to obtain your own map key, please check out the Bing Maps Developer Portal.

Silverlight for Windows Phone Programming Tip – Be Sure to Scope Your SIP

Scope Your SIP – sounds painful, doesn’t it? Adding on to Jeff‘s series concerning WP7 Silverlight development tips, I figured it worthwhile to add another one. The SIP is the Software Input Panel – AKA the Phone’s onscreen keyboard. One of the handy features of the SIP is the ability to customize it to fit the needs of the textbox it is being displayed for – eg if you need to enter a phone number, why wrestle with a bunch of letter keys?
Figure 1- SIP default appearance (USA)
To do so, it is necessary to provide set the TextBox’s InputScope property to one of the values in the InputScopeNameValue enumeration, as pictured below:
<TextBox>
    <TextBox.InputScope>
        <InputScope>
            <InputScopeName NameValue="TelephoneNumber"/>
        </InputScope>
    </TextBox.InputScope>
</TextBox>
																			
Figure 2- SIP with TelephoneNumber Selected for the InputScope
Note that this is a little convoluted than the original description, which perhaps should have stated “it is necessary to provide the TextBox’s InputScope property with an InputScope object which contains the desired input scope name.” InputScope actually accepts a list of Names, of which only the first currently has any effect on the SIP display.
public sealed class InputScope : DependencyObject
{
    public InputScope();    
    public IList Names { get; }
}
If you want suggested word completions to appear as the user types letters, the only InputScope values that currently support completion are “Text” and “Chat.”
<TextBox>
    <TextBox.InputScope>
        <InputScope>
            <InputScopeName NameValue="Text"/>
        </InputScope>
    </TextBox.InputScope>
</TextBox>
																			
Figure 3- Showing Word Completion Suggestions with “Text” InputScope Selected

The New Phone Tools [are] Here! The New Phone Tools [are] Here!

With all due respect to Steve Martin’s hilarious portrayal of Navin R. Johnson and his quest for “his special purpose” in 1979’s The Jerk. Tonight Microsoft released the Windows Phone Developer Tools January 2011 Update, which can be downloaded here. In addition to the content in the new tools, there’s also some interesting other news concerning the availability of unsubsidized phones for purchase at Zones.com.

The New Tools

The big update item is the addition of Copy & Paste functionality. All TextBox and PasswordBox controls now get Copy and Paste functionality. This apparently applies to applications already in the Marketplace – once the update has been applied to the phone, Copy & Paste is available. The new behavior is bi-directionally compatible…controls within apps already on phones will automatically get Copy & Paste when the phone is upgraded, and apps compiled against the new Developer Tools will not misbehave on phones that have not yet received the update.

Note – the update is only for the developer tools – it is not yet available to be applied to devices – unlocked developer devices or otherwise. It only works in the Emulator.

Looking at the Copy & Paste behavior, when a TextBox has focus and a word is tapped, it gets a special highlight and the Copy Button appears. This works both with the mouse clicking in the emulator, as well as using touch when testing on a touch-enabled PC. There is no option for selecting portions of a word (eg to just select the “op” in the word “Copy”.) That last caveat is probably not that big of a deal, as selecting portions of a word on a touch device the size of a phone probably requires too much finesse. Multiple words can be selected – which is discussed below.

Figure 1- Selecting a Word and Displaying the Copy Button

To select multiple words, once the first word has been selected, it pressing and dragging on either edge allows the amount of text to be copied (in full word increments) to be adjusted

Figure 2- Extending the Selection to Multiple Words

Once text has been copied, a special Paste Button appears over the top of the onscreen keyboard (AKA Software Input Panel or SIP.) This is the same area where type-ahead suggestions appear.

Figure 3- The Paste Button on the Standard (US) SIP

Figure 4- The Paste Button on the SIP with InputScope Set to “TelephoneNumber”

In addition to TextBox and PasswordBox controls, the Copy & Paste functionality extends to controls that internally make use of TextBox controls, such as the Silverlight for Windows Phone Toolkit implementation of the AutoComplete Box. It also works for Text Input controls in web pages displayed in the WebBrowser control or in the phone’s Web Browser. However, in the WebBrowser control, the Copy Button can tend to “wander” a little bit…

Figure 5- Copy & Paste Controls in the Web Browser Control. Note the Odd Copy Button Placement

Paste is fairly straightforward. Select any control where the SIP appears and select the Paste Button. Paste is “single-shot” – there does not appear to be multiple Paste. Once the Paste Button has been pressed, the “clipboard” content is pasted into the target location, and the Paste Button is removed.

Two more quick notes about Copy & Paste:

  1. The “clipboard” persists outside of the application, so copied text can be used in other phone applications (for example, copying a Url from an application and pasting it into the browser’s address bar.) This also includes Tombstoning.
  2. The update also includes updates to the Pivot and Panorama controls. The update is for when TextBox controls have been added to panels within these controls, and aims to suppress inadvertent swipes when trying to copy text. Apps that have TextBoxes in Pivot or Panorama controls will need to be recompiled to get this change (they will still have Copy & Paste, though there may be some usability issues…)
    1. (Yes, the issue Jeff saw with Tombstoning is still there…)

Other Stuff in the New Tools

Other than the Copy & Paste feature, there are a couple of other things included with the update:

  • The Bing Maps Control has been updated for better gesture responsiveness
  • The update includes the Windows Phone Connect Tool, which is installed at <x86_ProgramFilesDir>\Microsoft SDKs\Windows Phone\v7.0\Tools\WPConnect. This tool can be used to connect a device for debugging when Zune is not running, which is necessary when debugging apps that use the media APIs.
  • The update includes the Windows Phone Capability Detection Tool, which is installed at <x86_ProgramFilesDir>\Microsoft SDKs\Windows Phone\v7.0\Tools\CapDetect. This tool is equivalent to the one used when applications are submitted to the Marketplace to inspect the app’s code so as to determine which capabilities are required by the app. These values are then used to replace the list in the app manifest. This tool can be used to preview what capabilities will be being detected before the app is submitted, in order to prevent issues like one (of admittedly several) issues seen by Shawn Wildermuth (see “Problem Part 4”) when an assembly that was inadvertently left in the application caused the app to report capabilities it actually didn’t need.

In addition to the tools, there is also a separate bug fix (VS10-KB2486994-x86.exe) that addresses a bug that existed whereby XAP files over 64MB could not be deployed by developers to physical devices for testing.

Issues with the Update

There is one known (at this time) defect with the update. Installing the update sets the default target for debugging Windows Phone Applications to be the “Windows Phone 7 Device”, even when a device is not installed. It is easy enough to change the value when Visual Studio barks at you, but it doesn’t “remember” the change, so it gets really old really fast.

Figure 6- The Default Debug Target for Phone Apps Changed to Windows Phone 7 Device

According to the Release Notes, the following steps are necessary in order to change this behavior (they work!)

  1. Close all Visual Studio and Windows Phone Emulator instances.
  2. Delete the contents of %LocalAppData%\Microsoft\Phone Tools\CoreCon.
  3. Restart Visual Studio.

Purchasing Unsubsidized Phones

In addition to publishing these new tools, apparently Microsoft has entered into a partnership with zones.com to allow the purchase of WP7 phones without going through one of the wireless carriers and the associated contract. The phones are locked to a specific carrier (it is not possible to take the Samsung Focus to T-Mobile in the US, for example), but they do allow developers to obtain devices for testing. As of the time of this writing, there is mention on the site of a “coupon code” which can be applied for some kind of discount, but I have no other information about any such offers. There are currently 3 phones available for purchase – the AT&T-offered HTC Surround ($500) and Samsung Focus ($525), and the T-Mobile HTC HD7 ($500). The zones.com developer purchase portal can be found at http://www.zones.com/windowsphonedeveloperpurchase.