I’m trying to do something that’s arguably a bad idea, but I think it’s still possible. I’m trying to override how WP8 handles the Back Button and implement it myself. I theorize that if I:
The Plan
- Only ever create one “Frame” and “Page” in the entire application
- Always handle
PhoneApplicationPage.BackKeyPressmyself unless they were about to back out of the application.
The Repro
Here’s a sample project that has the crash
The code
..then it should work. However, my attempts are being thwarted by Windows Phone. Here’s the code:
// This basically happens on PhoneApplicationService.OnLaunched
_viewModelChanged.StartWith(ViewModel).Where(x => x != null).Subscribe(vm => {
var page = default(IViewFor);
var frame = RootVisual as PhoneApplicationFrame;
// Find the initial PhoneApplicationPage for the app
page = RxApp.GetService<IViewFor>("InitialPage");
// Depending on how we're being signalled (i.e. if this is cold start
// vs. resume), we need to create the PhoneApplicationFrame ourselves
if (frame == null) {
frame = new PhoneApplicationFrame() {
Content = page,
};
}
page.ViewModel = vm;
var pg = page as PhoneApplicationPage;
if (pg != null) {
pg.BackKeyPress += (o, e) => {
if (ViewModel.Router.NavigationStack.Count <= 1 ||
ViewModel.Router.NavigateBack.CanExecute(null)) {
return;
}
e.Cancel = true;
ViewModel.Router.NavigateBack.Execute(null);
};
}
// Finally, set Application.RootVisual
RootVisual = frame;
});
Sadness
This works great, until right after this code executes, where a DispatcherItem queued by the framework crashes the app:
System.NullReferenceException occurred
Message: A first chance exception of type 'System.NullReferenceException' occurred in Microsoft.Phone.ni.dll
Microsoft.Phone.ni.dll!Microsoft.Phone.Controls.PhoneApplicationPage.InternalOnNavigatedFrom(System.Windows.Navigation.NavigationEventArgs e) Unknown
Microsoft.Phone.ni.dll!Microsoft.Phone.Controls.PhoneApplicationPage.Microsoft.Phone.Controls.IPhoneApplicationPage.InternalOnNavigatedFromX(System.Windows.Navigation.NavigationEventArgs e) Unknown
Microsoft.Phone.ni.dll!System.Windows.Navigation.NavigationService.RaiseNavigated(object content, System.Uri uri, System.Windows.Navigation.NavigationMode mode, bool isNavigationInitiator, Microsoft.Phone.Controls.IPhoneApplicationPage existingContentPage, Microsoft.Phone.Controls.IPhoneApplicationPage newContentPage) Unknown
Microsoft.Phone.ni.dll!System.Windows.Navigation.NavigationService.CompleteNavigation(System.Windows.DependencyObject content, System.Windows.Navigation.NavigationMode mode) Unknown
Microsoft.Phone.ni.dll!System.Windows.Navigation.NavigationService.ContentLoader_BeginLoad_Callback(System.IAsyncResult result) Unknown
Microsoft.Phone.ni.dll!System.Windows.Navigation.PageResourceContentLoader.BeginLoad_OnUIThread(System.AsyncCallback userCallback, System.Windows.Navigation.PageResourceContentLoader.PageResourceContentLoaderAsyncResult result) Unknown
Microsoft.Phone.ni.dll!System.Windows.Navigation.PageResourceContentLoader.BeginLoad.AnonymousMethod__0(object args) Unknown
[Native to Managed Transition]
mscorlib.ni.dll!System.Delegate.DynamicInvokeImpl(object[] args) Unknown
System.Windows.ni.dll!System.Windows.Threading.DispatcherOperation.Invoke() Unknown
System.Windows.ni.dll!System.Windows.Threading.Dispatcher.Dispatch(System.Windows.Threading.DispatcherPriority priority) Unknown
System.Windows.ni.dll!System.Windows.Threading.Dispatcher.OnInvoke(object context) Unknown
System.Windows.ni.dll!System.Windows.Hosting.CallbackCookie.Invoke(object[] args) Unknown
System.Windows.RuntimeHost.ni.dll!System.Windows.RuntimeHost.ManagedHost.InvokeDelegate(System.IntPtr pHandle, int nParamCount, System.Windows.Hosting.NativeMethods.ScriptParam* pParams, System.Windows.Hosting.NativeMethods.ScriptParam* pResult) Unknown
So, I’ve solved this – my code was problematic because I didn’t grok how WP8 works 🙂 Here’s what I understand now, which may also be wrong but I’ll write it anyways
How your WP8 app is initialized:
PhoneApplicationFramePhoneApplicationFrameseems to also set a global static variable (same thing happens with creatingPhoneApplicationServicein the App.xaml, it setsPhoneApplicationService.Current).NavigationServicethen attempts to recreate a XAML View via a resource string (i.e.'/MainPage.xaml'). Either it will recreate the one that was previously tombstoned, or if not, it defaults to the one in your WMAppManifest (this is the part I didn’t understand).PhoneApplicationFrame.Navigatedgets called by NavigationService – this is where you can actually start doing stuff, including most importantly, setting Application.RootVisual, which will send the Loading… screen awayPhoneApplicationService.LaunchedorPhoneApplicationService.Activatedfinally fires, once basically everything is set up, depending how your app was woken up.