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.
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.
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.
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.
Once the project containing this code has been compiled, the attached property is available to be set to Selector-derived UI elements.
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: