WP7–tips for young players

Application state

Your two new best friends are PhoneApplicationService.Current.State and IsolatedStorageSettings.ApplicationSettings. Get to know them and they will serve you well. Don’t overdo it with PhoneApplicationService.Current.State because this is automatically serialised and there is a 2Mb limit. Exceeding this limit will produce a rather confusing error that appears to be about access permissions and trustees.

Context menus and ListBoxes

Let me guess, you’d like to use a context menu with a data-bound ListBox , but you can’t see how to get the selected item, because the SelectionChanged event doesn’t fire when you tap and hold to invoke the menu, even though the menu is positioned for the ListBox item you had in mind. People have told you to put a ManipulationStarted event handler on each ListBoxItem, but you can’t see how since they’re all generated by the binding. Here, let me help you. Set up an extension method like this:

    1 using System.Collections.Generic;

    2 using System.Windows;

    3 using System.Windows.Media;

    4 

    5 namespace YourNamespace

    6 {

    7   public static class ExtensionMethods

    8   {

    9     public static IEnumerable<DependencyObject> GetVisuals(this DependencyObject root)

   10     {

   11       int count = VisualTreeHelper.GetChildrenCount(root);

   12       for (int i = 0; i < count; i++)

   13       {

   14         var child = VisualTreeHelper.GetChild(root, i);

   15         yield return child;

   16         foreach (var visual in child.GetVisuals())

   17         {

   18           yield return visual;

   19         }

   20       }

   21     }

   22   }

   23 }

   24 

Now change your ListBox XAML to specify a DataTemplate containing a TextBox with a ManipulationStarted event handler attached.

      <ListBox Grid.Row="1" Name="listBoxResult" Grid.ColumnSpan="2"                 SelectionChanged="listBoxResult_SelectionChanged">
        <ListBox.ItemTemplate>
          <DataTemplate>
            <StackPanel Margin="0,10">
              <TextBlock Text="{Binding DisplayName}" TextWrapping="Wrap" ManipulationStarted="TextBlock_ManipulationStarted" FontSize="40" />
            </StackPanel>
          </DataTemplate>
        </ListBox.ItemTemplate>
        <toolkit:ContextMenuService.ContextMenu>
          <toolkit:ContextMenu>
            <toolkit:MenuItem x:Name="menuItemRemove" Header="remove" Click="menuItemRemove_Click"/>
            <toolkit:MenuItem x:Name="menuItemRename" Header="rename" Click="menuItemRename_Click"/>
          </toolkit:ContextMenu>
        </toolkit:ContextMenuService.ContextMenu>
      </ListBox>

This is the event handler. It’s a lot shorter than it might otherwise be, on account of using LINQ with the extension method. Basically it walks the visual tree to discover a ListBoxItem whose children include the TextBox that fired the event.

   89     Target _imminentSelection;

   90 

   91     private void TextBlock_ManipulationStarted(object sender, System.Windows.Input.ManipulationStartedEventArgs e)

   92     {

   93       _imminentSelection = LayoutRoot.GetVisuals().OfType<ListBoxItem>().Where(i => i.GetVisuals().Contains(sender as UIElement)).First().Content as Target;

   94     }

   95 

   96 

And now _imminentSelection is a typed reference to the object that would have been in SelectedItem had the selection change occurred normally. Since this is written for young players I suppose I’d better point out that you need to add

    3 using System.Linq;

to the top of the file.

You could use a similar trick to attach event handlers directly to the ListBoxItems but I wouldn’t advise it because you’ll prevent timely garbage collection of them and run out of memory.

Capturing the Enter key on an AutoCompleteBox

The rumours are true, you can’t use the KeyDown event to detect the Enter key on an AutoCompleteBox because it simply does not fire. However, the KeyUp event does fire. So just use that instead.

WCF and the dreaded uncatchable FaultException`1

You can handle it, you know. Look at the first couple of lines of this correctly implemented completion event handler:

   26     void _routeService_CalculateRouteCompleted(object sender, CalculateRouteCompletedEventArgs e)

   27     {

   28       if (e.Error != null)

   29       {

   30         MessageBox.Show(e.Error.Message);

   31         App.TargetHistory.Remove(App.CurrentTargetName);

   32         App.CurrentTargetName = null;

   33         NavigationService.GoBack();

   34       }

   35       else

   36       {

   37         RouteResult route = e.Result.Result;

   38 

Just check whether the call worked. If e.Error is null, everything’s fine. If e.Error has a value, the call failed and this is your chance to put things right. Attempting to access e.Result will produce that horrible uncatchable FaultException`1 business.

Lines 31 and 32 are application-specific clean-up code. You generally have to do this, but yours will be different.

All this is predicated on the service being correctly implemented. Most commercial ones are. If you’re implementing the WCF service and you don’t know how to correctly expose a fault condition for consumption by Silverlight then I suggest you consult your good buddy Google before you see an angry mob of Silverlight developers brandishing pitchforks and torches.

InputScope

imageObserve the word suggestion bar shown at right.

The mark-up that produces this is called an input scope because it defines the scope of the expected input, so for example if you specify an input scope of “Maps” then place names will be suggested.

If you don’t specify an input scope, you won’t get word suggestions.

Here’s the XAML that produced the example at right.

   55       <TextBox Name="textBoxFind" FontSize="36" KeyUp="textBoxFind_KeyUp" Margin="-10,0">

   56         <TextBox.InputScope>

   57           <InputScope>

   58             <InputScopeName NameValue="Maps" />

   59           </InputScope>

   60         </TextBox.InputScope>

   61       </TextBox>