WP8 Navigation Override - Crash in PhoneApplicationPage

I am trying to do something that may seem like a bad idea, but I think it is still possible. I am trying to override how WP8 handles the back button and implements it myself. I assume that if I:

Plan

  • Just create one “frame” and “page” throughout the application
  • Always handle it PhoneApplicationPage.BackKeyPressyourself if they are not going to return from the application.

Repro

Here is an example of a project that crashed

The code

.. then it should work. However, my attempts interfere with Windows Phone. Here is 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 fine until right after the execution of this code, where the DispatcherItem queued using the framework disables the application:

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
+5
3

, - , , WP8:) , ,

WP8:

  • App rehydrating App.xaml.cs
  • , , , PhoneApplicationFrame
  • PhoneApplicationFrame ( PhoneApplicationService App.xaml, PhoneApplicationService.Current).
  • NavigationService XAML (.. '/MainPage.xaml'). , , , , , WMAppManifest ( , ).
  • PhoneApplicationFrame.Navigated NavigationService - , , , Application.RootVisual, "...".
  • PhoneApplicationService.Launched PhoneApplicationService.Activated - , , , .
+5

. , .

InternalOnNavigatedFrom:

internal override void InternalOnNavigatedFrom(NavigationEventArgs e)
{
    PhoneApplicationPage content = e.Content as PhoneApplicationPage;
    string str = ((content == null) || (content.Title == null)) ? string.Empty : content.Title;
    PerfUtil.BeginLogMarker(MarkerEvents.TH_ONNAVIGATEDFROM_PAGE, string.Format("{0},{1},{2}", (base.Title == null) ? "" : base.Title, e.NavigationMode, str));
    this.OnNavigatedFrom(e);
    PerfUtil.EndLogMarker(MarkerEvents.TH_ONNAVIGATEDFROM_PAGE, string.Format("{0},{1},{2}", (base.Title == null) ? "" : base.Title, e.NavigationMode, str));
    DeviceStatus.KeyboardDeployedChanged -= new EventHandler(this.OnKeyboardDeployedChanged);
    Task rootTask = ApplicationHost.Current.RootTask;
    rootTask.OnVisibleRegionChange = (ITask.VisibleRegionChanged) Delegate.Remove(rootTask.OnVisibleRegionChange, new ITask.VisibleRegionChanged(this.OnVisibleRegionChange));
    Task task2 = ApplicationHost.Current.RootTask;
    task2.OnSipVisibilityChange = (ITask.SipVisibilityChange) Delegate.Remove(task2.OnSipVisibilityChange, new ITask.SipVisibilityChange(this.OnSipVisibilityChange));
    this._lastSipHeight = 0.0;
    this._dictionary = null;
}

, e, Application.Current.RootTask . , KeyboardDeployedChanged:

public static  event EventHandler KeyboardDeployedChanged
{
    [SecuritySafeCritical] add
    {
        if (KeyboardDeployedSubscription == null)
        {
            KeyboardDeployedSubscription = new SubscriptionHandler(DeviceTypes.KeyBoard);
        }
        KeyboardDeployedSubscription.Changed += value;
    }
    [SecuritySafeCritical] remove
    {
        KeyboardDeployedSubscription.Changed -= value;
    }
}

. remove add, KeyboardDeployedSubscription null, . , App:

    public App()
    {
        // Global handler for uncaught exceptions.
        UnhandledException += Application_UnhandledException;

        DeviceStatus.KeyboardDeployedChanged += (sender, e) => { };

, , . , , , , . InternalOnNavigatedTo.

, , OnNavigatedFrom , OnNavigatedTo .

+4

Windows Phone , WMAppManifest.xml, ( ).

<DefaultTask Name="_default" NavigationPage="MainPage.xaml" />

<DefaultTask Name="_default" />

, , .

+1

All Articles