Thursday, August 28, 2014

File Picking for Windows Phone App


If you  try to use file picker in Windows Phone App in the same way you did in Windows Store App, you would likely meet with some difficulties.

 One of the example is in Windows Phone 8.1 Hello World app,  you can get the resource from this link

When you are done with your code for picking up a photo to display the photo as following:


var openPicker = new FileOpenPicker()

            {
                SuggestedStartLocation = PickerLocationId.PicturesLibrary,
                ViewMode = PickerViewMode.Thumbnail
            };
            openPicker.FileTypeFilter.Clear();
            openPicker.FileTypeFilter.Add(".bmp");
            openPicker.FileTypeFilter.Add(".png");
            openPicker.FileTypeFilter.Add(".jpeg");
            openPicker.FileTypeFilter.Add(".jpg");
            var photo = await openPicker.PickSingleFileAsync();
            if (photo != null)
            {
                var fileStream = await photo.OpenAsync(Windows.Storage.FileAccessMode.Read);
                var bitmapImage = new BitmapImage();
                bitmapImage.SetSource(fileStream);
                displayImage.Source = bitmapImage;
            }

The code builds, but when the code executed to await openPicker.PickSingleFileAsync() you will get an exception with following message:

{System.Exception: The request is not supported. (Exception from HRESULT: 0x80070032)
   at Windows.Storage.Pickers.FileOpenPicker.PickSingleFileAsync()

you would have no idea on what is going on here, Maybe Microsoft could do better job in vetting  its Hello Word App for Windows Phone 8.1.

This link  from MSDN provided some good explanation how to do file picking.

In this article, it said  in Windows Store App, you use PickSingleFileAsync, PickSaveFileAsync and PickSiingleFolderAsync. However for Windows Phone app, you should use PickSingleFileAndContinue, PickSaveFileAndContinue and PickFolderAndContinue

 Bingo, it ends up that we should use PickSingleFileAndContinue instead of PickSingleFileAsync.

Now let’s see how PickSingleFileAndContinue works. One thing I want to get you be prepared, it is much complicated PickSingleFileAsync.

First of all,  I would like to refresh you on Windows Phone (Windows Store)Application life cycle as following:
 

When PickSingleFileAndContinue is called, you app is suspended into Suspended mode and then terminated to NotRunning state, when the user is done with his or her file picking operation,  the app will be reactivated to running state.

When the app is activated, the app’s OnActivated event is fired. You need to add the event handler in your Application class.  Most likely it is part of the code behind file for App.XAML

        protected async override void OnActivated(IActivatedEventArgs args)
        {
            base.OnActivated(args);
            var rootFrame = CreateRootFrame();
            await RestoreStatusAsync(args.PreviousExecutionState);
            if (rootFrame.Content == null)
            {
                rootFrame.Navigate(typeof(MainPage));
            }
            if (rootFrame.Content is PhotoPage)
            {
               var photoPage = rootFrame.Content as PhotoPage;
                if (args is FileOpenPickerContinuationEventArgs)
                {
                   photoPage.ContinueFileOpenPicker(args as FileOpenPickerContinuationEventArgs);
                }
            }
        }

Here CreateRootFrame and RestoreStatusAsync are 2 private methods. I will show the code for them at the bottom of this post.

What it does is get the previous page when the app is suspended. And then check to see if the argument is FileOpenPickerContinuationEventArgs. If so call a method in the page’s code behind class.

Now let’s see how it is handled in the page level and how to access the file that the user picked.

        public async void ContinueFileOpenPicker(FileOpenPickerContinuationEventArgs args)
        {
            if (args.Files != null)
            {
                var file = args.Files.FirstOrDefault();
                 var fileStream = await file.OpenAsync(Windows.Storage.FileAccessMode.Read);
                // Set the image source to the selected bitmap.
                 var bitmapImage = new BitmapImage();
                 bitmapImage.SetSource(fileStream);
                 displayImage.Source = bitmapImage;
            }
        }

 
The file the user picked is in the Argrment.Files collection. In this case, as I used PickSingleFileAndContinue,so there should be only one file, if you use PickMultipleFilesAndContinue, you will have multiple files in this collection.

All you need to do is to open the file and steam and create a bitmapImage with the stream file then use it as the source for the Image element in your XAML.   

The following are the rest of the code needed for the app to work: 

        private Frame CreateRootFrame()
        {
            var rootFrame = Window.Current.Content as Frame;
            if (rootFrame == null)
            {
                // Create a Frame to act as the navigation context and navigate to the first page
                rootFrame = new Frame {Language = Windows.Globalization.ApplicationLanguages.Languages[0]};
                // Set the default language
                rootFrame.NavigationFailed += OnNavigationFailed;
                // Place the frame in the current Window
                Window.Current.Content = rootFrame;
            }
            return rootFrame;
        }

        private async Task RestoreStatusAsync(ApplicationExecutionState previousExecutionState)
        {
            // Do not repeat app initialization when the Window already has content,
            // just ensure that the window is active
            if (previousExecutionState == ApplicationExecutionState.Terminated)
            {
                // Restore the saved session state only when appropriate
                try
                {
                    await SuspensionManager.RestoreAsync();
                }
                catch (SuspensionManagerException)
                {
                    //Something went wrong restoring state.
                    //Assume there is no state and continue
                }
            }
        }




in conclusion. you will find you need to break your logic into 2 blocks, one is launch the file picker the other is the logic to get the result. in between, you need to put some code in OnActiviated event handler. Comparing to PickSingleFileAsync, you just need to put your logic in various places.
 


 
 
 
 
 

        private void OnNavigationFailed(object sender, NavigationFailedEventArgs e)

        {

            throw new Exception("Failed to load Page " + e.SourcePageType.FullName);

        }

2 comments:

  1. Thanks, that's really helpful. I will try to split the logic for my windows phone application.

    ReplyDelete
  2. Wow. your post is helpful indeed. Such things about windows app development should be shared frequently.

    ReplyDelete