Visually Located

XAML and GIS

A simpler FilePicker for Windows Phone and Windows apps

If you’ve built a Windows Phone app that uses the FileOpenPicker you know that it can be a pain while the Windows variation is pretty simple to use. In Windows you use the PickSingleFileAsync method and continue code as you think you would.

StorageFile storageFile = await openPicker.PickSingleFileAsync();
// Do something with the file

However in Windows Phone this becomes a little more complex. It’s especially complex when you look at the sample on MSDN. First you call the PickSingleFileAndContinue method, then in App.xaml.cs wait for the app to be activated, then somehow get back to where you were before. It’s a mess. I wanted to make this easier in a recent app I was working on and I wanted it to work the same for Windows and Windows Phone.

To get started you’ll need to know that the CoreApplicationView can give us access to when the app is activated. With this, we can bypass all the weirdness of using the App class.

CoreApplication.GetCurrentView().Activated += OnViewActivated
...
...
private async void OnViewActivated(CoreApplicationView sender, IActivatedEventArgs args)
{
    sender.Activated -= OnViewActivated;
    // do some stuff
}

Let’s create a new Picker that will get any image files. We’ll create a new ImagePicker class. This class will be a shared class between Windows and Windows Phone apps. Let’s stub that out now.

public class ImagePicker
{
    public Task<IRandomAccessStream> PickAsync()
    {
        // TODO: this
    }
}

Pretty simple class, right? Let’s fill in the pieces. We’ll still use the FileOpenPicker within the PickAsync method. This is an ImagePicker so we want to state that it needs to look for images

public Task<IRandomAccessStream> PickAsync()
{
    FileOpenPicker openPicker = new FileOpenPicker();
    openPicker.ViewMode = PickerViewMode.Thumbnail;
    openPicker.SuggestedStartLocation = PickerLocationId.PicturesLibrary;
    openPicker.FileTypeFilter.Add(".jpg");
    openPicker.FileTypeFilter.Add(".jpeg");
    openPicker.FileTypeFilter.Add(".png");
 
    // TODO: the rest
}

At this point we need to break up into Windows Phone code and Windows code

private TaskCompletionSource<IRandomAccessStream> _imageCompletionSource;
 
#if WINDOWS_PHONE_APP
public Task<IRandomAccessStream> PickAsync()
#else
public async Task<IRandomAccessStream> PickAsync()
#endif
{
    FileOpenPicker openPicker = new FileOpenPicker();
    openPicker.ViewMode = PickerViewMode.Thumbnail;
    openPicker.SuggestedStartLocation = PickerLocationId.PicturesLibrary;
    openPicker.FileTypeFilter.Add(".jpg");
    openPicker.FileTypeFilter.Add(".jpeg");
    openPicker.FileTypeFilter.Add(".png");
 
#if WINDOWS_PHONE_APP
    _imageCompletionSource = new TaskCompletionSource<IRandomAccessStream>();
    CoreApplication.GetCurrentView().Activated += OnViewActivated;
    openPicker.PickSingleFileAndContinue();
    return _imageCompletionSource.Task;
#else
    StorageFile storageFile = await openPicker.PickSingleFileAsync();
    return await storageFile.OpenAsync(FileAccessMode.Read);
#endif
}

Notice for Windows Phone we will use a TaskCompletionSource to return the value. The result for the TaskCompletionSource will be set in the OnViewActivated method.

private async void OnViewActivated(CoreApplicationView sender, IActivatedEventArgs args)
{
    sender.Activated -= OnViewActivated;
    FileOpenPickerContinuationEventArgs pickerArgs = args as FileOpenPickerContinuationEventArgs;
 
    IRandomAccessStream stream = null;
    if (pickerArgs != null)
    {
        if (pickerArgs.Files.Count > 0)
        {
            StorageFile storageFile = pickerArgs.Files[0];
            stream = await storageFile.OpenAsync(FileAccessMode.Read);
        }
    }
    _imageCompletionSource.SetResult(stream);
}

Now with this simple class you can pick an image file like such

var picker = new ImagePicker();
var accessStream = await picker.PickAsync();

And that works the same for both Windows and Windows Phone. I did test this on a low memory device and it did work there as well!

You can find a “generic” version of this class here.

blog comments powered by Disqus