Metro XAML Nugget: App Bar AutoMagic

You may have noticed that in many places of the Windows 8 Metro UI, as well as many Metro applications where list content can be selected, that making a selection automatically/magically (“automagically”) brings up one or more app bars.  This is consistent with the “Guidelines and checklist for app bars” published in the Metro Style Apps Dev Center:

Do place contextual commands on an app bar and show that bar programmatically.

If you have commands that are specific to the mode of the app, such as the Crop command appearing only when a photo is selected, place those commands on an app bar and show it programmatically while in that context.

If you have commands that can be performed whether content is selected or not, keep those commands on the bar if you have enough room.

Do set the app bar’s dismissal mode to sticky when displaying contextual commands.

If you have contextual commands on an app bar, set the mode to sticky while that context exists and turn off the sticky mode when the context is no longer present (such as when a photo is deselected). In sticky mode, the bar doesn’t automatically hide when the user interacts with the app. This is useful for multi-select scenarios or when the context involves interaction such as manipulating crop handles. The bar stays visible while the user performs their actions. The user can still hide the bar by swiping the top or bottom edge of the screen and they can show it again with an edge swipe.

One place where this behavior can be seen occurs when selecting and deselecting tiles in the Metro Start Screen.

image

Of course, this is nice and all, but sitting down to implement this (it isn’t out-of-the-box behavior) for the Metro XAML list controls (ListBox, ListView, GridView), I figured I had several options.  First, I could just handle the SelectionChanged event and in the codebehind I could programmatically bring up and/or collapse the page’s app bar(s).  That would do for one-off code, but its hardly the approach I would want to take for a more robust application.  A second option is to bind the list’s SelectedItem(s) property to property on the page’s ViewModel, and either use a related property or a ValueConverter to bind to the AppBar’s properties.  This felt a little bit much for wanting to simply alter the behavior of one control to react to the behavior another control.  There are other solutions that fell into this category as well (ViewState etc.)  What I ended up coming up with is a quasi-Behavior using Attached Properties that makes this (ahem) behavior reusable and quite easy to wire up.

Note: Another similar approach would be to actually use Behavior<T>.  Although this component of the Blend SDK is not included with the WinRT tools, some folks have published the equivalent WinRtBehaviors project on CodePlex at http://winrtbehaviors.codeplex.com/.

As I mentioned, at the heart of this implementation are an attached property and a set of Flags indicating which app bar(s) should react to the selection change.

   1: [Flags]

   2: public enum AppBarDisplayFlags

   3: {

   4:     None = 0,

   5:     Bottom = 1,

   6:     Top = 2,

   7:     BottomAndTop = 3,

   8: }

   9:  

  10: public static readonly DependencyProperty AppBarDisplayOnListSelectionProperty = 

  11:     DependencyProperty.RegisterAttached(

  12:         "AppBarDisplayOnListSelection", 

  13:         typeof(AppBarDisplayFlags), 

  14:         typeof(Selector), 

  15:         new PropertyMetadata(AppBarDisplayFlags.None, OnAppBarDisplayOnListSelectionChanged));

  16:  

  17: public static void SetAppBarDisplayOnListSelection(Selector element, AppBarDisplayFlags value)

  18: {

  19:     element.SetValue(AppBarDisplayOnListSelectionProperty, value);

  20: }

  21:  

  22: public static AppBarDisplayFlags GetAppBarDisplayOnListSelection(Selector element)

  23: {

  24:     return (AppBarDisplayFlags)element.GetValue(AppBarDisplayOnListSelectionProperty);

  25: }

Nothing really fancy there…just a simple attached property – I opted to indicate the owner as the Selector class instead of the containing class simply for convenience.  The important part is that the attached property is defined with a callback to be used when the value of the attached property is changed – OnAppBarDisplayOnListSelectionChanged.

In the property changed handler, a check is performed to see if the code is running in the designer – if so, everything bails out.  Otherwise, the selector to whom the property is being applied is obtained (if not found, bail out).  The selector and the value of the flags are then passed to a helper method to handle hooking up to the pertinent events.

   1: private static void OnAppBarDisplayOnListSelectionChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs)

   2: {

   3:     // Don't hook up the event listeners when running in the designer

   4:     if (Windows.ApplicationModel.DesignMode.DesignModeEnabled) return;

   5:  

   6:     // Identify the selector to which this property has been applied (if none, then do nothing)

   7:     var selector = dependencyObject as Selector;

   8:     if (selector == null) return;

   9:  

  10:     var selectedFlags = (AppBarDisplayFlags)dependencyPropertyChangedEventArgs.NewValue;

  11:     HookEvents(selector, selectedFlags);

  12: }

In HookEvents, as long as one of the app bars is of interest, listeners are registered for the selector’s Unloaded and SelectionChanged events.  Some preemptive housekeeping is also performed to first unhook the same events in order to prevent leaks.  The Unloaded event merely unhooks these same events to once again prevent leaks.

The big workhorse is the HandleSelectionChanged event handler.  First, the Visual Tree is traversed until an ancestor of the Selector is found that happens to be a Page – which is presumed to be the site of the app bar(s) being affected.  Then the current state of the setting to show the top and/or bottom app bars is determined, and finally, if an item is selected, the appropriate app bars are shown.  If no item is selected, the appropriate app bars are collapsed.

   1: private static void HandleSelectionChanged(Object sender, SelectionChangedEventArgs e)

   2: {

   3:     var selector = sender as Selector;

   4:     if (selector == null) return;

   5:  

   6:     // Traverse the selector's parents to find the firet "page" element

   7:     var containingPage = selector.GetVisualAncestors().OfType<Page>().FirstOrDefault();

   8:     if (containingPage == null) return;

   9:  

  10:     var currentFlags = GetAppBarDisplayOnListSelection(selector);

  11:     var showBottomAppBar = (currentFlags & AppBarDisplayFlags.Bottom) == AppBarDisplayFlags.Bottom;

  12:     var showTopAppBar = (currentFlags & AppBarDisplayFlags.Top) == AppBarDisplayFlags.Top;

  13:  

  14:     if (selector.SelectedItem != null)

  15:     {

  16:         // An item has been selected - show the relevant app bars

  17:         if (showBottomAppBar) ShowAppBar(containingPage.BottomAppBar);

  18:         if (showTopAppBar) ShowAppBar(containingPage.TopAppBar);

  19:     }

  20:     else

  21:     {

  22:         // Nothing has been selected - hide the relevant app bars

  23:         if (showBottomAppBar) HideAppBar(containingPage.BottomAppBar);

  24:         if (showTopAppBar) HideAppBar(containingPage.TopAppBar);

  25:     }

  26: }

Once the project containing this code has been compiled, the attached property is available to be set to Selector-derived UI elements.

   1: <GridView

   2:     x:Name="itemGridView"

   3:     AutomationProperties.AutomationId="ItemGridView"

   4:     AutomationProperties.Name="Grouped Items"

   5:     Margin="116,0,40,46"

   6:     ItemsSource="{Binding Source={StaticResource groupedItemsViewSource}}"

   7:     ItemTemplate="{StaticResource Standard250x250ItemTemplate}"

   8:     SelectionMode="Multiple"

   9:     local:AppBarExtensions.AppBarDisplayOnListSelection="Bottom">

Obviously, this seems like a long way to travel – the value is realized when there are multiple Selector’s scattered throughout an application where this behavior is to be applied, and/or when this code is shared across multiple applications.  The entire code for the class containing the attached property follows:

   1: public static class AppBarExtensions

   2: {

   3:     [Flags]

   4:     public enum AppBarDisplayFlags

   5:     {

   6:         None = 0,

   7:         Bottom = 1,

   8:         Top = 2,

   9:         BottomAndTop = 3,

  10:     }

  11:  

  12:     public static readonly DependencyProperty AppBarDisplayOnListSelectionProperty = 

  13:         DependencyProperty.RegisterAttached(

  14:             "AppBarDisplayOnListSelection", 

  15:             typeof(AppBarDisplayFlags), 

  16:             typeof(Selector), 

  17:             new PropertyMetadata(AppBarDisplayFlags.None, OnAppBarDisplayOnListSelectionChanged));

  18:  

  19:     public static void SetAppBarDisplayOnListSelection(Selector element, AppBarDisplayFlags value)

  20:     {

  21:         element.SetValue(AppBarDisplayOnListSelectionProperty, value);

  22:     }

  23:  

  24:     public static AppBarDisplayFlags GetAppBarDisplayOnListSelection(Selector element)

  25:     {

  26:         return (AppBarDisplayFlags)element.GetValue(AppBarDisplayOnListSelectionProperty);

  27:     }

  28:  

  29:     private static void OnAppBarDisplayOnListSelectionChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs)

  30:     {

  31:         // Don't hook up the event listeners when running in the designer

  32:         if (Windows.ApplicationModel.DesignMode.DesignModeEnabled) return;

  33:  

  34:         // Identify the selector to which this property has been applied (if none, then do nothing)

  35:         var selector = dependencyObject as Selector;

  36:         if (selector == null) return;

  37:  

  38:         var selectedFlags = (AppBarDisplayFlags)dependencyPropertyChangedEventArgs.NewValue;

  39:         HookEvents(selector, selectedFlags);

  40:     }

  41:  

  42:     private static void HookEvents(Selector selector, AppBarDisplayFlags flags)

  43:     {

  44:         if (selector == null) throw new ArgumentNullException("selector");

  45:  

  46:         // Clear any "active" event handlers

  47:         UnhookEvents(selector);

  48:  

  49:         if (flags != AppBarDisplayFlags.None)

  50:         {

  51:             selector.Unloaded += HandleUnloaded;

  52:             selector.SelectionChanged += HandleSelectionChanged;

  53:         }

  54:     }

  55:  

  56:     private static void UnhookEvents(Selector selector)

  57:     {

  58:         if (selector == null) throw new ArgumentNullException("selector");

  59:  

  60:         selector.Unloaded -= HandleUnloaded;

  61:         selector.SelectionChanged -= HandleSelectionChanged;

  62:     }

  63:  

  64:     private static void HandleUnloaded(Object sender, RoutedEventArgs e)

  65:     {

  66:         UnhookEvents((Selector)sender);

  67:     }

  68:  

  69:     private static void HandleSelectionChanged(Object sender, SelectionChangedEventArgs e)

  70:     {

  71:         var selector = sender as Selector;

  72:         if (selector == null) return;

  73:  

  74:         // Traverse the selector's parents to find the firet "page" element

  75:         var containingPage = selector.GetVisualAncestors().OfType<Page>().FirstOrDefault();

  76:         if (containingPage == null) return;

  77:  

  78:         var currentFlags = GetAppBarDisplayOnListSelection(selector);

  79:         var showBottomAppBar = (currentFlags & AppBarDisplayFlags.Bottom) == AppBarDisplayFlags.Bottom;

  80:         var showTopAppBar = (currentFlags & AppBarDisplayFlags.Top) == AppBarDisplayFlags.Top;

  81:  

  82:         if (selector.SelectedItem != null)

  83:         {

  84:             // An item has been selected - show the relevant app bars

  85:             if (showBottomAppBar) ShowAppBar(containingPage.BottomAppBar);

  86:             if (showTopAppBar) ShowAppBar(containingPage.TopAppBar);

  87:         }

  88:         else

  89:         {

  90:             // Nothing has been selected - hide the relevant app bars

  91:             if (showBottomAppBar) HideAppBar(containingPage.BottomAppBar);

  92:             if (showTopAppBar) HideAppBar(containingPage.TopAppBar);

  93:         }

  94:     }

  95:  

  96:     private static void ShowAppBar(AppBar appBar)

  97:     {

  98:         if (appBar == null) return;

  99:  

 100:         appBar.IsSticky = true;

 101:         appBar.IsOpen = true;

 102:     }

 103:  

 104:     private static void HideAppBar(AppBar appBar)

 105:     {

 106:         if (appBar == null) return;

 107:  

 108:         appBar.IsOpen = false;

 109:         appBar.IsSticky = false;

 110:     }

 111:  

 112:     /// <summary>

 113:     /// Gets the ancestors of the element, up to the root.

 114:     /// </summary>

 115:     /// <param name="node">The element to start from.</param>

 116:     /// <returns>An enumerator of the ancestors.</returns>

 117:     public static IEnumerable<FrameworkElement> GetVisualAncestors(this FrameworkElement node)

 118:     {

 119:         var parent = node.GetVisualParent();

 120:         while (parent != null)

 121:         {

 122:             yield return parent;

 123:             parent = parent.GetVisualParent();

 124:         }

 125:     }

 126:  

 127:     /// <summary>

 128:     /// Gets the visual parent of the element.

 129:     /// </summary>

 130:     /// <param name="node">The element to check.</param>

 131:     /// <returns>The visual parent.</returns>

 132:     public static FrameworkElement GetVisualParent(this FrameworkElement node)

 133:     {

 134:         return VisualTreeHelper.GetParent(node) as FrameworkElement;

 135:     }

 136: }

Using the New Caller Information Attributes for Reliable Property Change Notifications

As anyone who has implemented the INotifyPropertyChanged interface knows, the fact that the PropertyChangedEventArgs takes a property name as a string means that you are one fat-fingered mistake away from a bug that can sometimes be difficult to track down.  If the property name supplied in the string doesn’t match the actual property name, the data binding (or other operations) that relies on this interface doesn’t work properly.

   1: public Int32 MyProperty

   2: {

   3:     get { return _myProperty; }

   4:     set

   5:     {

   6:         if (_myProperty != value)

   7:         {

   8:             _myProperty = value;

   9:             OnPropertyChanged("MyProperty");

  10:         }

  11:     }

  12: }

Lambda expressions and Expression Trees in .Net 3 brought a solution to the problem, where the compile-time checking could help ensure that a correct value was provided.  I blogged about this back in 2010 (http://blog.dotnetgator.com/2010/06/21/finding-binding-trouble/), and even cited my (then-future) coworker Jeremy Likness’s treatment of the same topic (http://csharperimage.jeremylikness.com/2010/06/tips-and-tricks-for-inotifypropertychan.html).

   1: public Int32 MyProperty

   2: {

   3:     get { return _myProperty; }

   4:     set

   5:     {

   6:         if (_myProperty != value)

   7:         {

   8:             _myProperty = value;

   9:             OnPropertyChanged(() => MyProperty);

  10:         }

  11:     }

  12: }

Now there’s a new feature in .Net 4.5 that provides yet another opportunity to ensure that INotifyPropertyChanged is implemented correctly – possibly in a simpler fashion than ever before, and with better performance than the Expression Tree/Lambda expression approach.

.Net 4.5 includes 3 new “Caller Information” attributes  – CallerFilePathAttribute, CallerLineNumberAttribute, and CallerMemberNameAttribute.  These three attributes are scoped to individual method parameters, and when used, they apply the indicated value from the CALLING METHOD to the called method’s attributed parameter at run time.  In our case, we’re interested in the CallerMemberName attribute, which we can use to automatically retrieve the property that is trying to raise the property change notification:

   1: private void OnPropertyChanged([CallerMemberName] String caller = null)

   2: {

   3:     var handler = PropertyChanged;

   4:     if (handler != null)

   5:     {

   6:         handler(this, new PropertyChangedEventArgs(caller));

   7:     }

   8: }

Note that the Caller Information attributes require a default value be supplied.

This reduces the overhead of a property in the class that provides this method to the following:

   1: public Int32 MyProperty

   2: {

   3:     get { return _myProperty; }

   4:     set

   5:     {

   6:         if (_myProperty != value)

   7:         {

   8:             _myProperty = value;

   9:             OnPropertyChanged();

  10:         }

  11:     }

  12: }

Of course, one important question is how does this method perform in comparison to either just providing a string, or using the Lambda/Expression Tree approach?  In my tests with iterations of between 1,000-500,000 property changes I saw between ~10-30% performance improvement over the Lambda/Expression Tree approach, and performance between ~20-30% lower than using a directly supplied string.

Note that the latest version of the MSDN documentation illustrating the implementation of the INotifyPropertyChanged interface (as of this writing) show the use of the CallerMemberName attribute – http://msdn.microsoft.com/en-us/library/ms229614(v=vs.110).aspx

NuGet Nugget: File-System Based Package Stores

A recent network “hiccup” posed a bit of a challenge to a demo that was built around showing how the Windows Azure Toolkit for Windows Phone (WATWP) NuGet packages make it easy to add Windows Azure cloud features to a Windows Phone 7 application.  So how do you access NuGet content when a network connection isn’t available?  What if you want to exert some management over the updates that are exposed to the developers in your enterprise, including exposing often-used or internal-use assets?

It turns out that NuGet offers some functionality that addresses these scenarios.  In addition to supporting the ability to set up your own package server (also known as Creating Remote Feeds in the NuGet documentation), there is the ability to consume packages collected in a directory – either a network share or a local file-system folder.  This is illustrated in the NuGet documentation under the subtopic “Creating Local Feeds” within the “Hosting Your Own NuGet Feeds” topic.

Populating a File-System Based Package Store

Any folder that contains NuGet .nupkg files can be set to be as a file-system based package server.  From my own use, folders that contain subfolders with .nupkg files will also work, allowing for organization and hierarchy.  If you already have a Visual Studio solution that has references to NuGet packages, moving these packages into such a local package store can be quite simple (especially for demos!)  Just locate the “packages” folder that is created when the NuGet packages are added

image

The entire package folder is not required, since the nupkg files are really Zip files that contain all of the necessary contents.  Search for files that end with the .nupkg extension. 

SNAGHTML9fbf7a

Simply copy all of these files into the folder you are using for your file-system based package store.  Note that a wider set of packages is available in your local package cache, which is normally maintained in <UserFolder>\AppData\Local\NuGet\Cache, and can be accessed from Visual Studio via Tools-Library Package Manager-Package Manager Settings, and click the “Browse” button in the General section of the Package Manager settings node.

Using File-Based Package Stores

To tell Visual Studio to consume file-based package stores, bring up the settings dialog (via Tools-Library Package Manager-Package Manager Settings or through the other accessors in Visual Studio) and select the Package Sources section of the Package Manager settings node.  Provide a name and type in the path or browse to the package location and click the Add button.  Note that the elements in the Available Package Sources list are shown in a check-list-box – they can be enabled or disabled.  Elements in the list can also be reordered in order to determine the precedence in which the sources are searched for matching packages.

SNAGHTMLab5b5b

When adding NuGet package references to your project in Visual Studio via the Manage NuGet Packages dialog, note that the newly named source now appears within the “Online” package listing section. 

SNAGHTMLb09c32

The new source is also available as a pulldown option in the Package Manager Console window.

SNAGHTMLb430e2

As I’ve been digging into NuGet more lately, I’ve been quite impressed by the functionality it exposes.  There’s much more to it than just a right-click menu item and a dialog box  that adds and updates project assembly references.  Some of my current favorites include managing package references at the solution level and visualizing NuGet package chains.  Be sure to check out the NuGet docs in case you have yet to discover your favorite.

DevBoston (Waltham) Presentation Content

Many thanks to the attendees of my presentation tonight covering Developing Cloud Enabled Windows Phone Applications with Windows Azure.  I apologize once again for the technical network-related gremlins that decided to attack us tonight…hopefully the workaround covered the necessary ground and everyone was able to see the concepts I was trying to show.

As promised, I have posted the content for the presentation here.  This includes the slide deck with the various resource links, as well as the final project.  As I mentioned, I have “sanitized” the configuration of the project to omit my specific Access Control (ACS) settings.  This involved changing the AccessControlResources.xaml file in the phone project and the Web.config file in the web role project.  Look for the text [your-xxx-here] for where to substitute your own ACS information.

Because the network issues prevented us from accessing the NuGet site or repository, I wanted to be sure to list the packages that were used in these projects:

Phone.Storage Adds support in the phone project to access Azure Storage.
WindowsAzure.Storage.Proxy.AccessControl Adds support in the server-side MVC3 based web role to field secured storage requests using ACS to generate the authentication token
Phone.Storage.AccessControl Adds support in the phone project for interacting with ACS-secured storage, including a login page with the ACS-aware login control.
WindowsAzure.Notifications Adds support in the server-side MVC3 based web role to handle registration & storage for Push Notification endpoints
Phone.Notifications.BasePage Adds support in the phone project (including a UI page) for enabling notifications in the application and registering with server-side push notification endpoint.
MPNSRecipe Adds the MPNS Recipe, used in the worker role project.

Boston .Net Code Camp Presentation Content

I would like to thank the organizers and attendees of the recent 16th (wow!) Boston Code Camp where I did a talk on Developing Cloud-Enabled Windows Phone Applications with Windows Azure – and special thanks to everyone who stuck it out once the snow started falling in order to catch my talk.  Obviously, it took longer than expected to get this content posted – Nashua got hit especially hard by the storm, including a 75% city-wide power outage the day following the Code Camp event, along with accompanying telecom and even cellular outages.  I am happy to say that I finally got my power turned back on mid-week, and am slowly but surely digging out of the mountain of backlog that piled up during the multi-day outage.

The content can be found here.  Because I demoed ACS, some of the items that are placed in the project would normally reference my “real” Azure account…they have been removed and replaced with the phrase “YOUR CONTENT HERE.”  You will need to replace these values with your own from the Azure ACS management portal in order to be able to run the provided code.  Also, in order to use the Azure Emulator, be sure to run Visual Studio in Admin mode when working with this project.

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.