LIDNUG Webinar Presentation Materials

Many thanks to the participants, organizers, and sponsors of today’s LIDNUG webinar – “Putting the Cloud in Your Pocket Pt1 – Using Windows Azure to Build Cloud-Enabled WP7 Apps.”  I especially appreciate the patience of those who attended as we struggled to do the best we could to resolve the LiveMeeting technical issues that dogged us during the presentation.  For what it is worth, prior to the presentation, the LIDNUG folks made sure we did a technical walkthrough to do everything possible to mitigate the possibility of running into these kinds of glitches…alas, despite our best efforts, the “demo gods” decided to frown upon us today.

As I mentioned during the talk, I have gone ahead and posted the code (along with the slide that were available for download during the talk) here.  As is often the case with talks about this topic, the demo contains keys and other “private” information that is specific to my own Azure account.  With that in mind, I have sanitized/removed the private content from the posted demo code, and included a document “ACS Update Instructions” alongside the code zip file that describes the steps necessary to get yourself up and running with your own Azure subscription.

As we mentioned during the talk, I will be working with the LIDNUG folks to make sure we are able to post a complete recording of the presentation.  Stay tuned for updates.  In the meantime, please be sure to check out additional upcoming Wintellect events as well as upcoming LIDNUG events, and please be sure to visit our webinar’s sponsor – Syncfusion.

Boston Azure Bootcamp Presentation Materials

I had a tremendous time this weekend presenting alongside Bill Wilder, Michael Collier, John Zablocki, and Jim O’Neil at the Boston Azure Bootcamp event in Cambridge, MA.  The topic once again covered the concepts of using Windows Azure to enhance mobile Windows Phone application and general mobile development considerations, and went beyond my demos to include a hands on lab that most everyone seems to have enjoyed.

As promised, the slide and code content I referred to in my talk can be found here.  I mentioned to a few who asked – there are some values in the lab that are specific to the ACS namespaces I have set up.  I am including in the code file a word doc that indicates how to set up the ACS values for the demo code in question.

Again, many thanks!  I’m looking forward to hearing about how folks are using Azure to add cloud “goodness” to their mobile applications.

CodeStock 2012 Presentation Content

I would like to thank the attendees of my “Putting the Cloud in Your Pocket – A Guide to Using Windows Azure to Build Cloud-Enabled Windows Phone Apps” talk at the recent Codestock event – especially considering the early hour following the previous night’s fun.  The slide and code content I referred to in my talk can be found here.   Also, many thanks go out to the event organizers – I had a great time traveling down to Tennessee for this event, and hope to maybe do so again in the future.

As can be expected, I removed my custom/personal ACS information from the sample code.  This includes the acsnamespace and realm resources in the AccessControlResources.xaml file within the Phone project, and the SwtSigningKey, realm, and namespace values from the MVC project’s web.config file.  These values can be obtained from a new or existing ACS namespace as follows:

ACS Configuration Values

These values are available in the following locations (Note – this is based on the current Silverlight-based management portal.  Precise locations may shift slightly when this content moves to the newer HTML5-based portal.)

The namespace value is the namespace you indicated when creating the ACS instance.

image

The Realm is specific to the relying party application that has been configured, and can be found on the Relying Party Application page:

image

The symmetric key can be obtained from the Access Control Service management portal, selecting Certificates and Keys, selecting (and/or creating) a Symmetric Key specific to the namespace:

image

image

image

Async CTP

Also, please remember that the code made use of the Async CTP assembly.  This was not strictly required, but was instead put in place to help improve the code flow instead of using Lambdas or complete methods for the various callback functions used when interacting with Azure Storage.  Information about the Async CTP is available here.

Boston Phone Camp Presentation Materials

I had a great time talking about the Windows Azure Toolkit for Windows Phone at the Windows Phone Camp event in Cambridge, MA earlier today.  I have posted the slide deck here for those who are interested in getting at the web links it contains.

If you missed the event and/or are interested in other Windows Phone Camp events, more information is available here.

Vermont Code Camp Presentation Materials

Many thanks to the organizers and attendees of this year’s Vermont Code Camp.   This event continues to set the bar for excellence, and a ton of credit is due to Julie Lerman, Rob Hale, and the rest of the volunteers who all clearly work quite hard to pull it all together.

I have uploaded the code and content from my “What’s New in Windows Phone 7.1 Silverlight Development”, and they can be downloaded from here.

Creating Custom Ringtones in Windows Phone Mango

Now that I have upgraded my phone to the Windows Phone 7.1 Developers’ Preview, one of the features I’ve been anxious to try out is Custom Ringtones – the default tones and the ones provided by AT&T are “interesting”, but they’re just not “me.”  Windows Phone 7.1 (AKA Mango) supports custom ringtones – but how do you go about making that happen?

IMPORTANT – The information below is based on Windows Phone 7.1 (AKA Mango) Beta 2, with a phone and the related Zune software upgraded to use the Developer Preview.

The available ringtones on the phone are segmented into groups which can be seen by going to Settings / Ringtones + Sounds and/or Contact / Edit / Ringtone (the latter location is used to override the default ringtone and set a per-contact ringtone.)  The default collection of ringtones that are included by the phone OS, those provided by the carrier, and any custom ringtones installed by the user.  Custom and OEM-provided ringtones can be deleted (for pruning the available ringtone list) whereas the ringtones included with the Phone OS cannot.

Custom ringtones are audio files that are “registered” with the phone OS in a certain way – more on this in a moment.  However, in order to be registered, an audio file must meet the following criteria:

  • It must be either a WMA or an MP3 file
  • It must be 39 seconds or shorter in length
  • It must be smaller than 1MB in size
  • It cannot be copy protected / have DRM restrictions

Creating Custom Ringtone Files

So what do you need to create a custom ringtone file that fits this criteria?  While there are several tools that can be used, I’ll highlight 2 here – Expression Encoder and Audacity.  Expression Encoder is part of Microsoft’s Expression Suite.  As of this writing, there are both free and pro versions available, and while I happen to have Pro (available as part of my MSDN Subscription), the audio features should be part of both SKUs.  Expression Encoder succeeds the Windows Media Encoder, and although it is primarily targeted at video projects, it is usable for audio projects.

To get started with Expression Encoder, launch the application and select a “Transcoding Project” in the New Project dialog.  To load the audio file, select either Import from the File menu, or locate the file in Windows Explorer and drag it onto the application.  The file will be loaded into the editing timeline, and its metadata will be displayed.  Using the editing controls within Encoder, the section of interest for the ringtone can be isolated.  Since there is no waveform monitor within Encoder, this is exercise must largely be performed by ear and with an eye on the time displayed in the timeline.  (Note – a full discussion of editing within the Encoder product is beyond the scope of this discussion.)  Once the desired segment of audio has been isolated, the specific WMA format can be selected from the Encode toolstrip, and the Audio property panel can be used to set the target bitrate mode, bitrate, channel information, sample rate, and bit depth (these settings push and pull against each other to determine audio quality vs file size.  Remember, this is a ringtone, and there are limits to the file size.  Audio quality is probably negotiable here.)  The Metadata toolstrip can be used to set the clip;s metadata attributes (in particular, Title and Genre are important here), and the Output toolstrip can be used to determine the file output.  When ready, selecting Encode or Encode Selected Item from the File menu starts creating the new ringtone file.

SNAGHTML80d969d

Audacity is a free, open source, cross-platform sound editor.  In addition to downloading the Audacity application itself, for these purposes 2 add-ons should be considered – the LAME MP3 encoder (if MP3 files are the desired output), and the FFmpeg import/export library (if WMA files will be imported or exported.)  Links to these add-ons can be found at the Audactiy download site here.  To start editing a file, select Import from the File menu, or drag the file into the application.  Audacity does display a waveform synchronized to the timeline, making the audio editing experience more visual than in Expression Encoder.  Once the target section of the file has been isolated, Export or Export Selection can be selected from the File menu.  From here, the type of file being exported can be chosen (WMA or MP3 for ringtones), and depending on the selection, encoding options can be chosen.  Note that there are far fewer encoding options for WMA here than in Expression Encoder.  Once a target file location and options are selected, the metadata editor will be displayed, where metadata for the output file can be set. 

SNAGHTML8192f82

Regardless of what is set in the encoding applications above, the details tab in the file properties dialog can be used to set the metadata.  The Title property controls what is displayed when the file is shown in the Custom Ringtones group in the phone.  The Genre value is actually quite important – in order to use the Zune software to load the ringtone, it must be set to “Ringtone.”

SNAGHTML81f29f7

Adding Ringtones Using Zune Software

To manually transfer the ringtone, make sure the ringtone file that was just created is in one of the configured music folders within the Zune software, as pictured below. 

SNAGHTML822444a

Set the file (or fill folder) to be synced with the phone device.  The next time the phone is plugged in, the file will be placed on the phone.  Because its genre is set to “ringtone”, the files will not appear in the phone’s audio collection, and will appear in the list of custom ringtones.

image

Programmatically Adding Ringtones

If the ringtone files are not originating on the phone user’s desktop and/or using the Zune software is not practical, there is a mechanism for adding ringtones programmatically.  This would be useful, for example, to provide an application that allows ringtones to be selected from an internet site or related resource.  The SaveRingtoneTask takes center stage for programmatically setting ringtones.  For SaveRingtoneTask, the DisplayName property determines the name the ringtone will be displayed with in the Custom Ringtones section, and the Source property provides a Uri to the location of the ringtone file to be used.  This Uri typically points to a location in Isolated Storage, using the URI syntax for accessing Isolated Storage, as shown below (notice the similarity with the Uri used for the Connection String specified when accessing SQL CE databases in the WP 7.1 API.  There is some discussion that the Source Uri will also support the appdata prefix syntax that is available for read-only files that are included as part of the app’s payload in the Xap file, but I have yet to test this specific functionality.) 

   1: saveRingtoneChooser.DisplayName = "My Ringtone";

   2: saveRingtoneChooser.Source = new Uri(@"isostore:/myringtone.wma");

   3: saveRingtoneChooser.IsShareable = true;

   4: saveRingtoneChooser.Show();

Note that the file in Isolated Storage is “copied” to the internal ringtone location on the phone…if the application that ran the Chooser is subsequently uninstalled (which clears Isolated Storage for that application), the ringtone persists until it is explicitly deleted from the Custom Ringtone collection.  Also, in the case of the web-based ringtone provider example discussed above, it may be prudent to use the Background Transfer Service to queue the selected ringtone download, then process the file once it has been fully downloaded.

A Note About the Audio Files Used in this Example

The audio files used in this discussion are ripped from CD’s that are physically in my own personal collection (my taste in music notwithstanding.)  Please be sure to perform your own copyright research prior to launching any ringtone software / services.

CT Code Camp 4 Presentation Materials

I have uploaded the content from my talk at the Connecticut .Net Developers’ Group Code Camp 4 from today (6/18/2011).  The code and content  from my “What’s New in Windows Phone 7.1 Silverlight Development” are available here.

Many thanks to SB Chatterjee and his team for putting together the event.  Also thank you to the attendees for some great questions and discussion during and after the session (and yes, I did make it back home in time to attend my daughter’s recital.)

NH .Net Code Camp 3 Presentation Materials

I have uploaded the content from my 2 talks at the NH .Net Code Camp 3 event from yesterday (6/4/2011). 

  • The code and material for my first talk – Introduction to Windows Phone 7 Development with Silverlight – are available here.
  • The code and material for my second talk –  What’s New in Windows Phone 7.1 Silverlight Development – are available here.

As is usually the case, the code has been sanitized, which involves the removal of my personal Bing Maps developer key.  For information on how to obtain your own map key, please check out the Bing Maps Developer Portal.

Many thanks to Patty, Udai, and Pat for hosting another fun event. 

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.

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.