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.

Advertisement

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.

Showing the Onscreen Keyboard in Silverlight OOB Applications

One of the “interesting” things about Silverlight apps running on Windows is that on touch/tablet systems, textboxes in Silverlight do not display a popup button to use for bringing up the Windows onscreen keyboard when they have focus, whereas native applications do.

image
WPF / Native Window following a “Touch” in a textbox

image
Silverlight OOB Window following a “Touch” in a textbox

The onscreen keyboard is actually present in the Silverlight case – it is usually just “conveniently” tucked against the left side of the screen – and experience has shown me that until you actually show or describe to someone where it is, they will usually not find it on their own.  Tap it once, and it “peeks” out a little bit.  Tap it again, and it is brought to the center of the screen.  Hit the close button, and it returns to its tucked away position on the side of the screen.

I have been asked several times for an option to show the onscreen keyboard from within a Silverlight application in a more consistent and user-friendly fashion.  It turns out that for Silverlight Out of Browser applications running with Elevated Trust, there is an option – COM Automation can be used to invoke the process that displays the onscreen keyboard, or Text Input Panel (TIP).

Locating the Process

As of Windows Vista, the TextInputPanel handles the Tablet Input Panel (TIP).  This is implemented in the file TabTip.exe, which exists in the directory <Program Files>\Common Files\Microsoft Shared\ink – this is true both in 64-bit and 32-bit versions of Windows.  (Note – I have verified this myself only on Windows 7.)  See the following site for more information about the TIP: http://msdn.microsoft.com/en-us/library/ms701746(VS.85).aspx.

Normally, to locate the needed path, the Environment.GetFolderPath method would be used.  However, when called on a 64-bit system from Silverlight with SpecialFolder.CommonProgramFiles as the parameter, it returns the path to the x86 Program Files directory…not the one that is needed in this case.  However, using the scripting shell’s ExpandEnvironmentStrings method can get the correct value, as follows:

   1: dynamic shell = AutomationFactory.CreateObject("WScript.Shell");

   2: String commonPath = shell.ExpandEnvironmentStrings("%CommonProgramW6432%");

   3:  

   4: // This path is the same for both 32 and 64-bit installs of Windows

   5: String filePath = System.IO.Path.Combine(commonPath, @"microsoft shared\ink\TabTip.exe");

Information about the CommonProgramW6432 Environment Variable can be found at this link: http://msdn.microsoft.com/en-us/library/aa384274(VS.85).aspx.

Showing the Onscreen Keyboard

Now that the necessary program has been located, it simply needs to be run to show the keyboard (or to bring it up onscreen, in case the keyboard app is running, but “hidden” along the margin, as pictured above.  To do this, simply call ShellExecute, passing in the path calculated above:

   1: dynamic application = AutomationFactory.CreateObject("Shell.Application");

   2: application.ShellExecute(filePath, "", "", "open", 1);

The full code listing for a ShowKeyboard helper method is shown below:

   1: public static class KeyboardHelper

   2: {

   3:     public static void ShowKeyboard()

   4:     {

   5:         if (AutomationFactory.IsAvailable)

   6:         {

   7:             try

   8:             {

   9:                 // Ensure Windows 7 or Windows Server 2008 R2

  10:                 // OS Version # - http://msdn.microsoft.com/en-us/library/ms724832(VS.85).aspx

  11:                 if (Environment.OSVersion.Platform == PlatformID.Win32NT

  12:                     && Environment.OSVersion.Version >= new Version(6, 1))

  13:                 {

  14:                     // Get the path to the Common Program Files directory (not the x86 version...)

  15:  

  16:                     // Environment.GetFolderPath returns the wrong path (x86 branch on 64-bit systems.)

  17:                     // String commonPath = Environment.GetFolderPath(Environment.SpecialFolder.CommonProgramFiles);

  18:  

  19:                     dynamic shell = AutomationFactory.CreateObject("WScript.Shell");

  20:                     String commonPath = shell.ExpandEnvironmentStrings("%CommonProgramW6432%");

  21:  

  22:                     // This path is the same for both 32 and 64-bit installs of Windows

  23:                     String filePath = System.IO.Path.Combine(commonPath, @"microsoft shared\ink\TabTip.exe");

  24:  

  25:                     // Bring up the TIP - http://msdn.microsoft.com/en-us/library/ms701746(VS.85).aspx

  26:                     dynamic application = AutomationFactory.CreateObject("Shell.Application");

  27:                     application.ShellExecute(filePath, "", "", "open", 1);

  28:                 }

  29:             }

  30:             catch (Exception ex)

  31:             {

  32:                 Debug.WriteLine(ex);

  33:             }

  34:         }

  35:     }

  36: }

What’s Next?

I have shown how to programmatically show the TIP…as far as how to get there, that is left as an exercise for the reader.  In some cases, adding a launcher (such as a “show keyboard” button) somewhere in the UI may be an acceptable option – it is certainly the simplest.  To approximate the behavior seen in native applications, more work is required.  This includes showing a UI control to display the TIP, which is only displayed if the user accesses the textbox control via touch input.  This can be determined by using the Silverlight Multitouch Input APIs, or a helper library such as LightTouch or the Native Extensions for Silverlight

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.

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");

A Matter of State, Part 3

The previous 2 posts discussed using the Page and Application State Dictionaries to restore state following the application being tombstoned, as well as some other potential uses for the Application State Dictionary.  This final post in the series will cover the user of Isolated Storage from Windows Phone applications.

Isolated Storage Basics
Isolated Storage provides a virtual file system that allows an application (and in some cases applications) access to an (ahem) isolated area of persistent storage, usually disk-based.  The precise location is hidden from applications and is not meant to be human-readable/accessible.  The technique of using Isolated Storage to preserve state is one that is available across all flavors of .Net – there is support for Isolated Storage in the .Net Framework, in the Silverlight Runtime (both on the PC and the Mac), and in Silverlight for Windows Phone.  While many of the mechanics of using Isolated Storage APIs are similar between the platforms, there are some distinctions in Windows Phone that make its support for Isolated Storage unique.
At its core, data stored in Isolated Storage is placed in a scoped area called a Store.  Each runtime described above has access to a different collection of scopes for the stores, with .Net having access to the most (User, Domain, Assembly, Roaming, Machine, Application), and Silverlight having access to just two – Site and Application.  The Site scope allows shared storage access to all Silverlight applications on the same web domain, and the Application scope is identified by each unique Silverlight XAP file.
In Silverlight for Windows Phone, the Silverlight applications are not hosted on any web site, so the only Isolated Storage scope available in phone applications is per Application.  One important implication of this is that there is currently no way of sharing or exchanging data between applications via the phone file system.  Having gone over the concept of tombstoning, there’s also an implication that the technique of using Local Connections is not available for inter-application communication (if only one application can be running at a time, there’s nobody to communicate with.)  Taking this all one step further, the only way to communicate between different applications in Windows Phone is by using some form of internet-based storage.
The following code shows the basic procedure for writing data to Isolated Storage:
private void WriteData()
{
using (IsolatedStorageFile store = IsolatedStorageFile.GetUserStoreForApplication())
{
// Get the directories in the current Store
String[] directoryNames = store.GetDirectoryNames(“search pattern”);


// Get the files in the given directory path
String[] fileNames = store.GetFileNames(“search pattern”);
//Maybe create one or more directories
store.CreateDirectory(“SomeNewDirectory”);
// Open a file stream (normal File.Open behavior)
using (IsolatedStorageFileStream fileStream = store.OpenFile(“file path”FileMode.Create))
{
using (StreamWriter writer = new StreamWriter(fileStream))
{
writer.WriteLine(“Some Data”);
}
}
}
}

  • Use the GetUserStoreForApplication call to get a reference to the application’s Isolated Storage file store
  • Using the store reference, it is possible to obtain a list of directories and/or files within the current store, as well as to create files and directories
  • Note that the process for creating files is the same as for regular files (specifying FileMode, etc.)
  • The result of an opened file is an IsolatedStorageFileStream object, which inherits from the FileStream class.
  • Once a reference to the IsolatedStorageFileStream object is available, code can be written against it as it can for pretty much any FileStream.  The example above wraps the stream with a StreamWriter which exposes a WriteLine that can be used to write a line of text into the stream (and hence the underlying file.)
  • Note that using blocks are being used to perform the necessary cleanup on Disposable objects throughout the process.
The following code shows the basic procedure for reading code from Isolated Storage:
private void ReadData()
{
using (IsolatedStorageFile store = IsolatedStorageFile.GetUserStoreForApplication())
{
if (store.FileExists(“file path”))
{
using (IsolatedStorageFileStream fileStream = store.OpenFile(“file path”FileMode.Open))
{
using (StreamReader reader = new StreamReader(fileStream))
{
String lineOfText = String.Empty;
while (null != (lineOfText = reader.ReadLine()))
{
DoSomethingInterestingWithText(lineOfText);
}
}
}
}
}
}
  • Get a reference to the application’s Isolated Storage file store
  • Using the store reference, check to see fi the desired file exists
  • If the file exists, open the file for reading, which returns an IsolatedStorageFileStream object, which inherits from the FileStream class.
  • Once a reference to the IsolatedStorageFileStream object is available, code can be written against it as it can for pretty much any FileStream.  The example above wraps the stream with a StreamWriter which exposes a ReadLine that can be used to read a line of text at a time from the stream (and hence the underlying file.)
Within the System.IO.IsolatedStorage namespace, there is a “helper class” that reduces some of the work required for routine Isolated Storage tasks.  Much like the Application and Page State Dictionaries, the IsolatedStorageSettings class exposes a Dictionary of objects that uses string values for keys.  The IsolatedStorageSettings class is accessible as a singleton through the IsolatedStorageSettings.ApplicationSettings property (note that “regular Silverlight” also exposes IsolatedStorageSettings.SiteSettings for site-scoped storage.)
The following code shows writing and reading from the ApplicationSettings dictionary:
private void WriteSettingsData()
{
IsolatedStorageSettings.ApplicationSettings[“key”] = value;
}
private void ReadSettingsData()
{
if (IsolatedStorageSettings.ApplicationSettings.Contains(“key”))
{
value = IsolatedStorageSettings.ApplicationSettings[“key”];
}
}
Many possibilities are available for storing and retrieving data through Isolated Storage beyond simply reading and writing text using the StreamWriter and/or IsolatedStorageSettings.  These range from using Xml to hold serialized objects through custom database implementations, including my Wintellect colleague Jeremy Likness’s
Sterling Object-Oriented database and the Codeplex implementation/port of Sqlite.  Like most file systems, there are concerns regarding the quantity and size of items in any given folder, and there is a great discussion here on the performance behavior of Isolated Storage in Windows Phone under load.

Capacity
Another important distinction between Isolated Storage for Silverlight and Isolated Storage for Silverlight for Windows Phone is that whereas there are user quotas in “regular” Silverlight (1 MB max for browser-based apps before the user must be asked if they would like to increase the quota, and 25 MB for out-of-browser apps), there are no storage quotas in place for Silverlight for Windows Phone applications.  Storage space is limited by the amount of storage space available on the device itself.
Not only is there no quota for the amount of storage that any one application can consume, there is also no File Explorer or WinDirStat equivalent for the phone to determine how much space is being consumed by any one application.  Although there’s no way to know which application may be consuming all of the available space on a device, if an application is chosen for deletion, when an application is uninstalled from the phone (locate the shortcut in the application list, hold down the application icon, and in the context menu that appears, choose uninstall) its isolated storage contents are deleted and the space they occupied is recovered.
Figure 1- Prompt When Uninstalling an Application
To determine the total amount of space remaining on disk, the IsolatedStorageFile instance offers an AvailableFreeSpace property.  With “traditional Silverlight”, this returns the amount of space remaining in the current quota.  Since Silverlight for Windows Phone does not have quotas, the AvailableFreeSpace property will return the amount of storage space remaining on the device.
In the following screenshots, an application was created that wrote a large amount of data to Isolated Storage.  A second application was written to show the amount of drive space remaining.  In the first screenshot, the storage application has not yet written its data to disk.  In the second screenshot, it has written data to disk, as can be seen by the reduced amount of available free space remaining.  Finally, in the third screenshot, the storage application has been uninstalled and its storage space has been reclaimed.  (Note that the extra space appearing in the third screenshot is most likely due to the additional space received by the removal of the Silverlight XAP file in addition to the content in Isolated Storage.)
Figure 2- Before Saving Large File
Figure 3- After Saving Large File
Figure 4- After “Big File” Application is Uninstalled
Knowing this about uninstallation, the next logical question usually deals with updating.  When an application is updated (a new version is placed in the Marketplace), the data in Isolated Storage is retained.  It is up to the application developer(s) to handle issues related to the handling of versioning with any previous application versions’ contents in Isolated Storage.

Tombstoning
While Isolated Storage is the only one of the three state-preservation mechanisms discussed that can work across multiple launches of the same application (and hence beyond Tombstoning), it is certainly usable for Tombstoning scenarios, and is absolutely necessary in some cases, such as when the amount of data to be stored exceeds the limitations imposed by the other techniques.  Because of the performance implications of Isolated Storage (discussed in the previous article in the series, ~30% to ~55% slower) and the possibility of larger amounts of data, there are important time limits that need to be discussed.
First of all, as stated here in the MSDN documentation, if an application takes more than 10 seconds to launch, the application may be terminated by the operating system.  Furthermore, the Windows Phone Application Certification Requirements state that in order to pass the certification process, an application must render its first screen “within 5 seconds after launch” (5.2.1a), and must be responsive to user input “within 20 seconds after launch” (5.2.1b)  Finally, an application must conclude the Activated and Deactivated events within 10 seconds. (5.2.3)  As a result, it may be beneficial to consider breaking the content in Isolated Storage into smaller files that are accessed as needed (instead of big-load-up-front), background threads be used for loading large content at startup, and similar consideration be employed when saving data.
With these time considerations in mind, the key application lifetime events include:
  • Launching – Raised when a new instance of the application is launched (and will not be raised if the application is being activated following tombstoning.)
  • Closing – Raised when the application is terminated.  In Silverlight applications, this only occurs when the user uses the Back button past the first page in the application’s BackStack (and will not be raised when the application is being tombstoned.)
  • Deactivated – Raised when the user navigates away from the application (and in most cases, is tombstoned.)
  • Activated – Raised when the user returns to an application after it was deactivated.
Because both Deactivated and Closing (under most circumstances) result in the application being terminated and in-memory state being destroyed, it is important to ensure that state is appropriately saved and restored in both circumstances.  State information retrieved when an application is initially launched is not automatically preserved or restored.  For that reason, it is not atypical to see the Launching/Activated and Closing/Deactivated event handlers call into a common pair of methods (such as RestoreState and SaveState, respectively) to deal with common content.

In Closing
In this series, the posts focused on mechanisms for preserving state in Windows Phone Silverlight applications.  The key concepts presented included:
I hope that this coverage has helped to illustrate the benefits and limitations of each.

New England Code Camp 14 Presentation Materials

I have uploaded the content from my two talks at the New England Code Camp – Introduction to Windows Phone 7 Development with Silverlight and Advanced Windows Phone 7 Development with Silverlight. The content can be found here and here (Slideshare links here and here.) The uploaded content includes:

  • Presentation slides, which include the reference and event links I mentioned during the talk
  • An interactive pdf of the outline I worked off for the talk (Acrobat Reader may be required)
  • The sample code

The demo code was prepared with the RTM version of the Windows Phone Developer Tools, as well as some controls from the Silverlight for Windows Phone Toolkit. To use the Bing Maps demos without the watermark indicating the control needs to be licensed, please go to the Bing Maps Developer portal, register, and put your application key into the properties in the BingMapsKey.cs files.

* Note – when using the Notification Service and Bing Maps Demos from the Advanced topic, be sure to set the solution to use Multiple Startup Projects (PhoneDemo and PhoneDemoService.)

Windows Phone 7 Developer Tools RTM, etc.

With all due respect to Steve Martin, “The new phone tools are here! The new phone tools are here!”

Following up on my recent presentations introducing development for Windows Phone 7 with Silverlight, the release of the RTM version of the Windows Phone 7 to the public today was announced today, along with a couple other key items. First and foremost, the new tools can be found here. The “normal” download is a web-based installer, but there is a tiny link at the bottom of the page for downloading an ISO image. The RTM includes the long awaited (by some) Panorama, Pivot, and Map controls, with Panorama and Pivot being backed by specific starter-project templates in both Visual Studio and Blend. The Panorama control enables the presentation of content on a long horizontal canvas through which the phone display serves as a viewport (think of the Panorama Control as the ruler portion of a slide-rule, and the phone as the central strip.) The Pivot control can be thought as the equivalent of the Tab-Control for the phone. Finally, the Map control presents the Bing Maps Silverlight control to the phone. More information on using Bing Map controls (outside of the phone, but there is some translation…) can be found here.

There are some subtle breaking changes in the new toolkit, and they are outlined in the Release Notes, which are also available for download from the download page.

Silverlight for Windows Phone Toolkit

Also announced today was the release of the Silverlight for Windows Phone Toolkit (yes, that name is certainly a mouthful.) The toolkit release includes several additional controls for the phone, including a ContextMenu, Date & Time Pickers, a ToggleSwitch (think on/off light-switch), and a Wrap Panel. The toolkit also includes the Gesture Listener, which allows some common touch-based gestures to be easily hookup to event handlers right in the phone page’s Xaml markup. The Silverlight for Windows Phone Toolkit can be downloaded here, and general Silverlight Toolkit information can be found here. As with the other Silverlight Toolkits, the Source Code is available for download, as is a Sample Application. While the phone’s version of the sample browser is maybe not as slick as the “regular” Silverlight Toolkit’s (including source code visible directly in the application), it is still a great help. The Sources and Sample code can be found here.

More Free Jump-Start Webinars

In July, a series of Jump-Start webinars were produced which have been posted to Channel 9 as 12 hours’ worth of videos. Also announced today is an additional webinar series which will offer a deeper look at Windows Phone 7 development, including using the new Panorama, Pivot, and Map controls, Application Performance, Blend, and a live Q&A. The live webinar will span 7 hours on Tuesday, September 21 2010, and registration is available here. The original content is posted at Channel 9 here, with this content hopefully soon to follow.