Navigate to a different page from ContinueFileOpen

2019-06-10 10:28发布

问题:

I'm using the FilePicker for Windows Universal apps and I'm trying to launch the File Picker from MainPage.xaml and then navigate to a different page (LoadPhoto.xaml) to render the selected image.

I initially implemented my app so that I'd navigate to LoadPhoto.xaml and then, as part of loading the page, I'd call the File Picker. However that caused issues when resuming the app, so I moved the File Picker call out of the constructor.

In the newest implementation, I call the file picker from MainPage.xaml and then, if a photo has been selected, navigate to LoadPhoto.xaml. However, there seems to be a race condition somewhere: Sometimes, the app stays on the MainPage after selecting a picture (it looks like it's actually navigating to the LoadPhoto page but something makes the Frame go back to the MainPage). Other times, the app successfully navigates to the LoadPhoto page and renders the image, but if I navigate back with the Back button and then press the Pick photo button again, the FilePicker is briefly shown and then the app crashes. This behavior doesn't repro with VS attached. Everything works fine while executing in Debug mode.

I think the root cause is that the ContinueFileOpenPicker code is executed from a worker thread, so I shouldn't call this.Frame.Navigate(typeof(LoadPhoto), file); from that thread. That call should be made from the main thread, but I'm not sure how to do that.

Unfortunately, this doesn't fix the issue: await CoreWindow.GetForCurrentThread().Dispatcher.RunAsync(CoreDispatcherPriority.Norm‌​al, () => { this.Frame.Navigate(typeof(LoadPhoto), file); });

How can I navigate to a different page from the ContinueFileOpenPicker method? The full code with the repro is here.

回答1:

When you call to Frame.Navigate to go to the LoadPhoto page you are passing a complex object as a parameter: the file the user has picked. When you go back to MainPage and start a new picker session your app gets suspended and SuspensionManager serializes the frame's state (see SaveFrameNavigationState method in that class). Unfortunately, the GetNavigationState method in Frame does not support serializing a complex object, but only simple ones like strings, integers or guids. This is documented in the Frame.Navigate method on MSDN.

The reason you don't see the app crashing when you are debugging in VS is because (by default) the app doesn't get suspended in this scenario, so the code that throws the exception is never called. However, with no debugger attached, your app is suspended when you navigate away from it. To force the suspension, use the Lifecycle Events drop-down in the Debug Location toolbar once you have launched the picker session.

If you really need to save/restore the state of the frame, then you should avoid passing StorageFiles when navigating. You can use FutureAccessList, pass the path to the file when navigating and load it in LoadPhoto.

If you don't need (or want to use) what SuspensionManager has to offer, then you could get rid of it and keep passing a StorageFile object. However, keep in mind that if you do this a reference to that object will be kept in the navigation stack.