App model¶
Startup¶
XamlHost (default.xex) starts the CLR, reads AppManifest.xaml, loads the entry assembly and creates the Application class named there. As in any Silverlight app, Startup sets RootVisual:
private void Application_Startup(object sender, StartupEventArgs e)
{
RootVisual = new MainPage();
}
The screen is 1280x720. Design pages at that size and keep important content inside the title-safe area, the inner 90% (64 pixels from the left and right, 36 from the top and bottom), because TVs crop the edges.
XboxApplicationFrame¶
Apps with more than one page use XboxApplicationFrame (Microsoft.Xbox.Controls) as the root. It's a navigation Frame that also:
- Starts GamerServices: it calls
GamerServicesDispatcher.Initialize, then pumpsGamerServicesDispatcher.UpdateandFrameworkDispatcher.Updateevery frame. Signed-in gamer information, the Guide, storage containers andMediaTransport's screen-saver control depend on that. - Initializes the UI sounds.
- Plays the page transitions set with
TransitionService. - Hosts the one
VuiBarused for speech, in its template.
private void Application_Startup(object sender, StartupEventArgs e)
{
var frame = new XboxApplicationFrame();
frame.Source = new Uri("/Pages/HomePage.xaml", UriKind.Relative);
RootVisual = frame;
}
Without the frame, an app that uses gamer services or storage has to start GamerServices itself. The PlatformCalls sample does:
if (!GamerServicesDispatcher.IsInitialized)
{
GamerServicesDispatcher.Initialize(null);
}
CompositionTarget.Rendering += delegate
{
FrameworkDispatcher.Update();
GamerServicesDispatcher.Update();
};
Pages and navigation¶
Pages are XboxApplicationPage XAML files (a Page with Xbox transitions):
<xbox:XboxApplicationPage x:Class="MyApp.Pages.HomePage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:xbox="clr-namespace:Microsoft.Xbox.Controls;assembly=Microsoft.Xbox">
...
</xbox:XboxApplicationPage>
Navigate by URI. Parameters go on the query string and come back in NavigationContext.QueryString:
NavigationService.Navigate(new Uri("/Pages/DetailPage.xaml?id=7", UriKind.Relative));
protected override void OnNavigatedTo(NavigationEventArgs e)
{
base.OnNavigatedTo(e);
int id = int.Parse(NavigationContext.QueryString["id"]);
Dispatcher.BeginInvoke(delegate { FirstButton.Focus(); });
}
- Every navigation creates a new page instance. Keep state that must survive in the
Appclass or a static field, and focus something inOnNavigatedTo. - Navigation is asynchronous. To go back several pages, call
GoBack()again from the frame'sNavigatedevent rather than in a loop, throughDispatcher.BeginInvokeso the back stack has settled. Detach the handlers when the walk ends, and also when aNavigatingevent comes with aNavigationModeother thanBack. A handler left attached sends every later navigation back to the first page. On the console that happened a few pages deep. SeeApp.GoHomeinsamples\ui\PageNavigation.
Transitions¶
XboxApplicationPage slides pages in and out (ZIndexTransition) by default. Elements marked with TransitionService.TransitionTarget="True" animate on their own, staggered by TransitionService.TransitionOrder, with each step 160 ms later:
<TextBlock Text="Title" transitions:TransitionService.TransitionTarget="True"
transitions:TransitionService.TransitionOrder="0" />
<Button Content="First" transitions:TransitionService.TransitionTarget="True"
transitions:TransitionService.TransitionOrder="1" />
with xmlns:transitions="clr-namespace:Microsoft.Xbox.Transitions;assembly=Microsoft.Xbox".
A page change takes longer than its animations. XboxApplicationFrame shows the new page only when a 300 ms timer fires after it arrives. The exception is a page with ItemsControls, which it shows once they have all loaded. The default transitions then fade the old page out over 250 ms and the new one in over 350 ms, and the page ignores input until they end. In xenia that came to about 875 ms from pressing A to the new page taking input.
To make page changes quicker:
- Start at once: put an empty
ItemsControlin the page, for examplenew ItemsControl { Width = 0, Height = 0 }added to its root panel. The transition then starts on the page's first frame. - Shorter transitions: derive from
TransitionElement, returnnew Transition(element, storyboard)fromGetTransitionwith a short storyboard of your own, and set it withTransitionService.SetNavigationInTransitionandSetNavigationOutTransition. Animate aTranslateTransformrather than a large area's opacity where you can: opacity redraws the whole area on every frame.
Doing both, with a 150 ms transition, gets it to about 270 ms.
Back and exit¶
B is the back button by convention, and the app handles it. A frame-level handler sees B after the focused control has had its chance:
frame.GamePadButtonDown += delegate(object s, GamePadButtonEventArgs args)
{
if (args.Button == GamePadButton.B && frame.CanGoBack)
{
frame.GoBack();
args.Handled = true;
}
};
Application.Current.Shutdown(), an Xbox addition, ends the app and returns to the dashboard. Shipped apps ask first (with Guide.BeginShowMessageBox) when B is pressed on the first page.
Unhandled exceptions¶
An exception nobody catches reaches Application.UnhandledException. Unless the handler sets e.Handled = true, XamlHost quits to the dashboard. While developing, handle it and show the message on screen; the samples' App classes all do.
Threading¶
The UI thread runs the app. Network callbacks from WebClient arrive on the UI thread. Storage, cache container and gamer services callbacks (Begin.../End..., ...Async completion events) arrive on other threads, so use Dispatcher.BeginInvoke before touching the UI (see Storage and Gamer services).