This is one of a series of posts I have been doing while exploring the new WinRT environment in Windows 8. In this article I am going to talk about the file pickers functionality.
In .NET apps when we need to prompt the user to select a file we have a common dialog that can be displayed. WinRT Metro apps provide something similar called a picker. The way the file picker works is particularly import for WinRT applications. These applications run in a sandbox and by default they have limited access the user’s files. If the app needs to have arbitrary access to user files it must declare this in the Capabilities section of its manifest. This, in theory, would allow the user to know exactly what the app is allowed to do before they download it.
This is where the file picker comes into play. The file picker returns a StorageFile object which can be used to directly access only the file that the user picked. Since the user is in complete control of the selection there is no need to declare this capability in the manifest.
To demonstrate a file picker let’s create a small application that will allow the user to pick an image file and then display that file in an image control.
First create a new C# Windows Metro Style Application. In the XAML view of MainPage.xaml, add the following code inside the Grid element:
<Button x:Name="UIOpenFile" Content="Open File" HorizontalAlignment="Left" Margin="17,18,0,0" VerticalAlignment="Top" Click="UIOpenFile_Click"/> <Image x:Name="UIImage" HorizontalAlignment="Left" Margin="27,74,0,0" VerticalAlignment="Top" />
In the code behind for MainPage add the following namespaces:
using Windows.Storage.Pickers; using Windows.UI.Xaml.Media.Imaging;
Then add the following code:
async private void UIOpenFile_Click(object sender, RoutedEventArgs e) { var picker = new FileOpenPicker(); picker.SuggestedStartLocation = PickerLocationId.PicturesLibrary; picker.ViewMode = PickerViewMode.Thumbnail; picker.FileTypeFilter.Add(".jpg"); picker.FileTypeFilter.Add(".jpeg"); var file = await picker.PickSingleFileAsync(); if (file == null) return; var stream = await file.OpenAsync(Windows.Storage.FileAccessMode.Read); BitmapImage image = new BitmapImage(); image.SetSource(stream); UIImage.Source = image; }
When you run the application and click the button you will get a file picker screen open to your picture library. If you pick an image file it will be displayed in the image control. Let look at how this code works.
First note that the event handler is marked as async. Any potentially long running functions in WinRT will run asynchronously so this keyword is needed for this to work.
The first thing we do in the function is create a FileOpenPicker object and set its properties.
The SuggestedStartLocation property specifies one of a series of standard locations that the picker should start in. In this case we are starting in the PictureLibrary, but the user is free to browse to other locations.
The next property, ViewMode, determines how the picker will display the files. There are currently two options:
PickerViewMode.Thumbnail
PickerViewMode.List
The last property we need to setup is the FileTypeFilter. This is a generic list of strings, each being one of the file type extensions that you want to the picker to look for. In this case we are just adding two, .jpeg, and .jpg. Note that if you don’t add any file types you will get an exception when you display the picker, and you can’t use wildcards. I am assuming that Microsoft will eventually need to add a way to pick any file type.
Once the properties are set on the picker we call it’s PickSingleFileAsync() method to display the picker and allow the user to select a single file. The await keyword will cause the function to pause until the picker is closed, but not block the UI thread. The function will return either a StorageFile object or a null if the user canceled the picker.
As I mentioned at the beginning of the article, we don’t get just a filename back from the picker, but a StorageFile object. In the last four lines of the code we use the object to create a read only stream, create a BitmapImage object from the stream, and then finally set that image as the source of the image control.