覆盖WP8导航 - 在PhoneApplicationPage中崩溃

时间:2013-02-09 20:11:06

标签: windows-phone-8 windows-phone

我正在尝试做一些可能是坏主意的事情,但我认为这仍然是可能的。我试图覆盖WP8如何处理后退按钮并自己实现它。我认为如果我:

计划

  1. 只在整个应用程序中创建一个“框架”和“页面”
  2. 除非他们即将退出应用程序,否则请始终自行处理PhoneApplicationPage.BackKeyPress
  3. Repro

    Here's a sample project that has the crash

    代码

    ..然后它应该工作。但是,我的尝试被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;
    });
    

    伤心

    这很有效,直到执行此代码之后,框架排队的DispatcherItem会崩溃应用程序:

    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
    

3 个答案:

答案 0 :(得分:5)

所以,我已经解决了这个问题 - 我的代码存在问题,因为我没有弄清楚WP8是如何工作的:)这就是我现在所理解的,这可能也是错的,但我还是会写的

您的WP8应用程序如何初始化:

  1. 操作系统通过补充App.xaml.cs
  2. 来创建您的App类
  3. 这意味着,您的构造函数会被运行,并且作为其中一部分,您创建了PhoneApplicationFrame
  4. 创建PhoneApplicationFrame似乎设置了一个全局静态变量(在App.xaml中创建PhoneApplicationService时也会发生相同的事情,它会设置PhoneApplicationService.Current) 。
  5. NavigationService然后尝试通过资源字符串(即'/MainPage.xaml')重新创建XAML视图。它会重新创建以前被逻辑删除的那个,或者如果没有,它默认为WMAppManifest中的那个(这是我不理解的部分)。
  6. PhoneApplicationFrame.Navigated被NavigationService调用 - 这是你可以实际开始做的事情,包括最重要的是,设置Application.RootVisual ,这将发送Loading ...屏幕< / LI>
  7. PhoneApplicationService.LaunchedPhoneApplicationService.Activated一旦基本上全部设置完毕,最终会触发,具体取决于您的应用被唤醒的方式。

答案 1 :(得分:4)

发现了这个问题。好吧,冰山一角。

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;
}

经过一些调试后,我得出结论:eApplication.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

答案 2 :(得分:1)

由于您正在使用Windows Phone的内置自动导航到WMAppManifest.xml中定义的页面,我尝试删除自动导航,它基本上有效(没有例外)。

我刚刚更换了

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

<DefaultTask Name="_default" />

不确定这是否能解决您的问题,但至少不会再崩溃。