【问题标题】:Overriding WP8 Navigation - crash in PhoneApplicationPage覆盖 WP8 导航 - PhoneApplicationPage 崩溃
【发布时间】:2013-02-09 20:11:06
【问题描述】:

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

计划

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

再现

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

【问题讨论】:

  • 这可能与这是一个“硬件”按钮这一事实有关,并且事件正在从手机操作系统转换到应用程序,因此手机操作系统会在你得到它之前做任何它想做的事情通知...
  • @AwkwardCoder 这可能是真的 - 我在初始化时搞砸了一些东西,但我无法弄清楚它是什么

标签: windows-phone-8 windows-phone


【解决方案1】:

所以,我已经解决了这个问题 - 我的代码有问题,因为我没有了解 WP8 的工作原理:) 这是我现在理解的内容,这也可能是错误的,但我还是会写出来

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

  1. 操作系统通过重新处理 App.xaml.cs 来创建您的 App 类
  2. 这意味着,你的构造函数开始运行,作为其中的一部分,你创建了一个PhoneApplicationFrame
  3. 创建PhoneApplicationFrame 似乎设置了一个全局静态变量(在App.xaml 中创建PhoneApplicationService 也会发生同样的事情,它设置PhoneApplicationService.Current)。
  4. NavigationService 然后尝试通过资源字符串(即'/MainPage.xaml')重新创建 XAML 视图。要么它会重新创建以前被墓碑化的那个,要么如果没有,它默认为你的 WMAppManifest 中的那个(这是我不明白的部分)。
  5. PhoneApplicationFrame.Navigated 被 NavigationService 调用 - 这是您可以真正开始做事的地方,包括最重要的设置 Application.RootVisual,它将发送 Loading... 屏幕
  6. PhoneApplicationService.LaunchedPhoneApplicationService.Activated 最终会触发,一旦基本上一切都设置好了,这取决于您的应用是如何被唤醒的。

【讨论】:

    【解决方案2】:

    发现问题。好吧,只是冰山一角。

    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 都不是 null。挠了挠头,看了看KeyboardDeployedChanged事件处理器的代码:

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

    这段代码写得不好。如果在add 之前调用处理程序的remove 部分,则KeyboardDeployedSubscription 将为空并且将引发异常。为了验证我的理论,我在App 的构造函数中订阅了该事件:

        public App()
        {
            // Global handler for uncaught exceptions.
            UnhandledException += Application_UnhandledException;
    
            DeviceStatus.KeyboardDeployedChanged += (sender, e) => { };
    

    果然,异常消失了。现在,为了了解为什么您的代码会触发此问题,我回溯到框架的哪个部分应该订阅该事件。唯一的候选者是InternalOnNavigatedTo 方法。

    因此,您的问题是 OnNavigatedFrom 被调用,即使 OnNavigatedTo 从未被调用。

    【讨论】:

      【解决方案3】:

      由于您对 Windows Phone 的内置自动导航到 WMAppManifest.xml 中定义的页面感到困惑,我尝试删除自动导航,它基本上可以工作(没有例外)。

      我刚换了

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

      <DefaultTask Name="_default" />
      

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-06-09
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多