MDI / Multiple Tabs Flat Navigation App Bar in Windows Store Apps

Summary

The Windows 8 User Experience Guidelines mentions two different navigation designs: the hierarchical and the flat system. As said in the guidelines, most Windows Store apps use the hierarchical system. But I needed a flat navigation. And in addition, I needed a way to implement a Multiple Document Interface (MDI) like approach. Or, in other words, an IE 10 like multiple tabs flat system interface.

IE 10 Tab Navigation

This sample implements such an app bar. The user can add new tabs (pages), switch between open pages, and of course remove existing. The app bar holds a navigation list of all currently open pages.

The user can enable / disable some app bar animation when a new page is added. The side effects of this, and a reason why one might prefer the animation, are explained in detail below.

Another configuration setting is available to set the way the navigation list reacts on user interaction. One option is to open an existing tab by just tapping on it (single tap). To select an item from the navigation list, the user has to right tap it. The app gives visual feedback when right tapping is registered. (Right tapping means to press and hold until a time threshold is crossed.)

The other option is to select the navigation item by a single tap and open the page by double tapping. Because tap events a fired by finger tapping, mouse click or pen input, the app does not have to distinguish between different input devices.

Below you will find descriptions and explanations of some of the implementation details.

You Cannot Capture a Screenshot

My initial idea was to capture a screenshot of the current page and display the list of screenshots in the app bar. This is what the IE 10 app is doing.

But I had to learn that this is not possible in XAML Windows Store apps (see How to capture screen in Metro app?? in Dev Center).

Even using DirectX will not solve the problem. I tried the .NET / RT DirectX implementation SharpDX. It lacks of one key feature: create a copy of the output to be able to acquire the next frame. But from what I understand, this is not a SharpDX issue.

For those who ask why IE can: it is using its own rendering engine.

This is why there is no thumbnail thing in the navigation list, but a simple rectangle containing text. Of course, one might put some time in symbol design. But that is not the focus of this sample.

Global App Bar Definition

The navigation app bar might be defined for every page class that is used by the app. In this sample, the app bar is declared globally. The reason is that no matter which page is opened, the app bar always looks the same. Having different kinds of content pages, there might be the need to define different kinds of app bar.

The global AppBar is defined in the MainPage XAML. The main page itself has no content (beside some informational text that is hidden when a content page is added). It contains a Frame that hosts the current content page. Instead of navigating to the newly created page, the frame’s content is set to this page. So the MainPage is always present, and this is why its app bar will be shown.

I found the initial sample for this in Create Global AppBar in Windows Store Apps Using XAML/C#.

App Bar Animation

Animating new tabs means that the app bar stays open for a while when the add button is pressed. An indeterminate progress bar gets started. The new page opens, the new tab is added to the app bar’s list (animated), and then the app bar closes.

Having no animation, the app bar closes immediately when the add button is pressed, while the new page opens.

A drawback of having no animation, from my point of view, is when you open the app bar for the first time after a new tab was added. Without animation, all tabs are newly rendered with transition. On my machine, this took a while. I was not able to find out how to disable the transition. So the user waits a while until all tabs are visible. Opening the app bar again, without adding new tabs, the items are visible immediately.

Having the animation, the app looks like some background activities are required to setup the new page. The time the app bar stays open is not deterministic. So you might have to change to delay when using it in your own app. But when the app bar is re-opened, all items are displayed immediately.

All the content page adding code, together with the app bar animation, can be found in MainPage.OnAddButtonClicked.

Animation Update (V1.1.0.0)

Checking the sample on a Windows 8.1 Preview system, I noticed that the navigation tabs were shown with transition every time the app bar was opened – no matter if animation was enabled or not.

So I started again looking for the right place to disable transition of GridViewItems in the app bar. And I found it 🙂 In C# you have to set

GridView.ItemContainerTransitions = null;

or define in XAML

 <GridView>
  <GridView.ItemContainerTransitions/>
</GridView>

To be able to animate new items in the list, the sample stores the ItemContainerTransitions in MainPage.NavigationItemTransitionCollection when all UI controls were initialized. The collection is restored only in MainPage.OnAddButtonClicked when animation is requested and a new content page gets added.

Update 2013-10-25
In case you cannot see any animation under Windows 8.1, please check the Performance Options setting “Animate controls and elements inside windows” (System / Advanced system settings / Performance / Settings). If this option is not set, transition is disabled for Windows Store apps too.

User Interaction Considerations

My first approach for user interaction with the tab navigation was like this: single click opens the selected page. Right click selects the item for removal. Why just clicking? Because my dev machine has no touch display, and I almost do not run the apps in the simulator.

After having this coded, I switched to the simulator, testing the navigation in basic tap mode. Have you ever tried to right tap? The Quickstart: Static gestures says “A single contact that does not move until a time threshold is crossed. […] Right tap is closely associated with the press and hold gesture. The right tap event is fired when the press and hold is released.”. This is true so far. I received right tap events.

But there is no (visual) feedback by default for GridView when the time threshold is crossed. So when will the user knows she has right tapped? And the app won’t know either. This is because the RightTapped event is fired when the press and hold is released, not when the time threshold is crossed. Accordingly, the app cannot give visual feedback itself.

So I re-designed the navigation. Single click / single tap selects an item. Double click / double tap opens it.

Then I found this thread: Right tapped visual feedback rectangle. OK, the visual feedback is not built in, but can be added with a few event handler. So I implemented both versions. Still can’t tell which one I like more.

Failed to start tracking the pointer because it is already being tracked

Handling the CoreWindow.PointerPressed, CoreWindow.PointerReleased, and CoreWindow.PointerMoved for right-tapped visual feedback, I sometimes had an unhandled exception saying “Failed to start tracking the pointer because it is already being tracked”.

Well, the thread Failed to start tracking the pointer because it is already being tracked gave the important information, and I found it also in the remarks of the docs of UiElement.PointerCaptureLost event (unfortunately not in the remarks of CoreWindow.PointerPressed): “PointerCaptureLost might fire instead of PointerReleased. Don’t rely on PointerPressed and PointerReleased events always occurring in pairs. To function properly, your app must listen for and handle all events that represent likely conclusions to the Press action, and that includes PointerCaptureLost. A pointer can lose capture either because of user interactions or because you programmatically captured another pointer or released the current pointer capture deliberately.

Adding a PointerCaptureLost handler helped on this.

private void OnCoreWindowPointerCaptureLost
  (
  object sender,
  PointerEventArgs args
  )
{
  // Just set the flag that we've lost the capture.
  PointerCaptureIsLost = true;
}

The flag needs to be tested by the PointerPressed handler.

private void OnCoreWindowPointerPressed
  (
  object sender,
  PointerEventArgs args
  )
{
  // In case we've lost the pointer capture, just reset the flag and do nothing.
  // Otherwise, an exception will be thrown that says 
  // "Failed to start tracking the pointer, because it is already being tracked".
  if (PointerCaptureIsLost)
  {
    PointerCaptureIsLost = false;
    return;
  }

  // We still have the capture, so handle the event.
  GestureRecognizer.ProcessDownEvent(args.CurrentPoint);
}

Which Source of Pointer Events?

I was playing around to figure out from which source the pointer events should be handled. At least I think it is best to take Window.Current.CoreWindow. All pointer event handling is implemented in MainPage.

One approach was to only handle these events from the GridView that contains the navigation list. This lead to some unwanted behavior. When I right-tapped the app bar, waited until the visual feedback was shown, then moved the pointer without releasing it, the highlighted rectangle stayed open. Even when I switched to another app, the start screen, or close the app: the rectangle was not closed any more. At least this is how the app behaved in the simulator.

Tapping Empty GridView in AppBar Causes Unhandled Exception

Some unexpected things happen when I tapped into the empty GridViewGridView, and then into the main window (so that the app bar closes). When I repeat these steps for a second time (starting with opening the app bar), after tapping into the main window, the app terminates. As soon as the GridView contains at least one item, the error does not occur.

At the time of writing, I have not received a response / explanation for this. So maybe you want to make sure there is at minimum one item left in the list, means one content page needs to be open.

Get the Sources Directly

Click here to download the source code.

Load It From The Windows Store

A slightly extented version of the sample can be found in the Windows Store.

Screenshots

Sample Settings
Behavior Settings

Flat System Navigation App Bar
Tab Navigation App Bar with selected items

History

2013-10-25: Source code update V1.2.0.0
Crashes on Windows 8.1 – fixed.

2013-08-14: Source code update V1.1.0.0
Corrections on transition / animation of items in navigation bar.

2013-08-13: Source code update V1.0.0.1
Instance Factory Logo changed.

Links

Click here to download the source code.

Windows 8 User Experience Guidelines

Dev Center UX Guide

Forum thread How to capture screen in Metro app??

.NET / RT implementation DirecX implementation SharpDX

Create Global AppBar in Windows Store Apps Using XAML/C#

The Quickstart: Static gestures

Forum thread Right tapped visual feedback rectangle

Forum thread Failed to start tracking the pointer because it is already being tracked

Implement Settings Popup Pages for Windows Store Apps