Sdílet prostřednictvím


Part 4: File access and pickers

[ This article is for Windows 8.x and Windows Phone 8.x developers writing Windows Runtime apps. If you’re developing for Windows 10, see the latest documentation ]

Important  This tutorial is intended for use with Microsoft Visual Studio 2013 and Windows 8.1. Parts of it will not work correctly with Microsoft Visual Studio 2012 and Windows 8.

 

In Part 2 of this tutorial series, Manage app lifecycle and state, you learned about app data and session data, and how to save this data in ApplicationData storage. Your app can access certain file system locations, like app data locations, the app install directory, and items it creates in the Downloads folder, by default.

In contrast, user data, such as pictures, videos, and document files, is independent of your app and is typically stored in other locations in the file system, such as the user’s library folders. To access these locations, your app needs to declare capabilities to access the data programmatically, or use a file picker to let the user open the file manually. Here, you use a file picker to access the user’s Pictures, so you don't need to declare any app capabilities. For more info about capabilities, see App capability declarations.

In this tutorial, you add functionality to the photo page layout you created in Part 3: Navigation, layout, and views. First, you handle the "Get photo" button click event to open a file picker and let the user select an image from their Pictures. Then you bind UI controls to file properties to show the picture info. Finally, we revisit what you learned in Part 2 about how to save app state. Here, you use a MostRecentlyUsedList to keep access to the image selected by the user.

In this tutorial, you learn how to:

Before you start...

Step 1: Use a file picker to get an image file

Through the file picker, your app can access files and folders all over the user's system. When you call the file picker, the user can browse their system and select files (or folders) to access and save. After the user picks files or folders, your app receives those picks as StorageFile and StorageFolder objects. Your app can then operate on picked files and folders by using those objects.

The first thing you need to do is handle the GetPhotoButton_Click event to get a picture to show.

We start here with the code from Part 3: Navigation, layout, and views.

To add a file picker

  1. In Solution Explorer, double-click PhotoPage.xaml to open it.

  2. Select the "Get photo" Button.

  3. In the Properties Window, click the Events button ().

  4. Find the Click event at the top of the event list. In the text box for the event, type "GetPhotoButton_Click" as the name of the method that handles the Click event.

  5. Press Enter. The event handler method is created and opened in the code editor so you can add code that's executed when the event occurs.

  6. Add the async keyword to the method signature of the event handler.

    private async void GetPhotoButton_Click(object sender, RoutedEventArgs e)
    {
    }
    
    Private Async Sub GetPhotoButton_Click(sender As Object, e As RoutedEventArgs)
    
    End Sub
    
  7. Add this code to the event handler method. It opens a file picker to let the user select a picture from their Pictures. When they pick a file, it's set as the source of the image and the data context of the page.

                Windows.Storage.Pickers.FileOpenPicker openPicker = new Windows.Storage.Pickers.FileOpenPicker();
                openPicker.SuggestedStartLocation = Windows.Storage.Pickers.PickerLocationId.PicturesLibrary;
                openPicker.ViewMode = Windows.Storage.Pickers.PickerViewMode.Thumbnail;
    
                // Filter to include a sample subset of file types.
                openPicker.FileTypeFilter.Clear();
                openPicker.FileTypeFilter.Add(".bmp");
                openPicker.FileTypeFilter.Add(".png");
                openPicker.FileTypeFilter.Add(".jpeg");
                openPicker.FileTypeFilter.Add(".jpg");
    
                // Open the file picker.
                Windows.Storage.StorageFile file = await openPicker.PickSingleFileAsync();
    
                // file is null if user cancels the file picker.
                if (file != null)
                {
                    // Open a stream for the selected file.
                    Windows.Storage.Streams.IRandomAccessStream fileStream =
                        await file.OpenAsync(Windows.Storage.FileAccessMode.Read);
    
                    // Set the image source to the selected bitmap.
                    Windows.UI.Xaml.Media.Imaging.BitmapImage bitmapImage =
                        new Windows.UI.Xaml.Media.Imaging.BitmapImage();
    
                    bitmapImage.SetSource(fileStream);
                    displayImage.Source = bitmapImage;
                    this.DataContext = file;
                }
    
            Dim openPicker = New Windows.Storage.Pickers.FileOpenPicker()
            openPicker.SuggestedStartLocation = Windows.Storage.Pickers.PickerLocationId.PicturesLibrary
            openPicker.ViewMode = Windows.Storage.Pickers.PickerViewMode.Thumbnail
    
            ' Filter to include a sample subset of file types.
            openPicker.FileTypeFilter.Clear()
            openPicker.FileTypeFilter.Add(".bmp")
            openPicker.FileTypeFilter.Add(".png")
            openPicker.FileTypeFilter.Add(".jpeg")
            openPicker.FileTypeFilter.Add(".jpg")
    
            ' Open the file picker.
            Dim file = Await openPicker.PickSingleFileAsync()
    
            ' file is null if user cancels the file picker.
            If file IsNot Nothing Then
    
                ' Open a stream for the selected file.
                Dim fileStream = Await file.OpenAsync(Windows.Storage.FileAccessMode.Read)
    
                ' Set the image source to the selected bitmap.
                Dim BitmapImage = New Windows.UI.Xaml.Media.Imaging.BitmapImage()
    
                BitmapImage.SetSource(fileStream)
                displayImage.Source = BitmapImage
                Me.DataContext = file
    
            End If
    
  8. Press F5 to build and run the app. Navigate to the photo page, and click the "Get photo" button to launch the FileOpenPicker. The photo appears, but the photo info text is not updated. You'll fix that in the next step.

    Here's what the app looks like with a picture selected.

Let’s take a closer look at the code to use the FileOpenPicker. To use a file picker you need to create and customize a file picker object, and show the file picker so the user can pick an item.

First, create a FileOpenPicker object.

Windows.Storage.Pickers.FileOpenPicker openPicker = new Windows.Storage.Pickers.FileOpenPicker();
Dim openPicker = New Windows.Storage.Pickers.FileOpenPicker()

Set the properties on the file picker object that are relevant to your users and your app. For guidelines to help you decide how to customize the file picker, see Guidelines for file pickers.

Because the user is picking a picture, you set the SuggestedStartLocation to Pictures and the ViewMode to Thumbnail. You also add file type filters so the picker shows only the file types you specify for image files.

openPicker.SuggestedStartLocation = Windows.Storage.Pickers.PickerLocationId.PicturesLibrary;
openPicker.ViewMode = Windows.Storage.Pickers.PickerViewMode.Thumbnail;
                 
// Filter to include a sample subset of file types.
openPicker.FileTypeFilter.Clear();
openPicker.FileTypeFilter.Add(".bmp");
openPicker.FileTypeFilter.Add(".png");
openPicker.FileTypeFilter.Add(".jpeg");
openPicker.FileTypeFilter.Add(".jpg");
openPicker.SuggestedStartLocation = Windows.Storage.Pickers.PickerLocationId.PicturesLibrary
openPicker.ViewMode = Windows.Storage.Pickers.PickerViewMode.Thumbnail

' Filter to include a sample subset of file types.
openPicker.FileTypeFilter.Clear()
openPicker.FileTypeFilter.Add(".bmp")
openPicker.FileTypeFilter.Add(".png")
openPicker.FileTypeFilter.Add(".jpeg")
openPicker.FileTypeFilter.Add(".jpg")

After you create and customize a file picker, call FileOpenPicker.PickSingleFileAsync to show it and let the user pick one file.

Note  To let a user pick multiple files, call PickMultipleFilesAsync.

 

// Open the file picker.
Windows.Storage.StorageFile file = await openPicker.PickSingleFileAsync();
' Open the file picker.
Dim file = Await openPicker.PickSingleFileAsync()

When the user picks a file, PickSingleFileAsync returns a StorageFile object that represents the picked file. You process the image stream to create a BitmapImage and set the BitmapImage as the Source of the Image control in the UI. You also set the file as the DataContext of the page so that you can bind UI elements to its properties.

// file is null if user cancels the file picker.
if (file != null)
{
    // Open a stream for the selected file
    Windows.Storage.Streams.IRandomAccessStream fileStream =
        await file.OpenAsync(Windows.Storage.FileAccessMode.Read);

    // Set the image source to the selected bitmap.
    Windows.UI.Xaml.Media.Imaging.BitmapImage bitmapImage =
        new Windows.UI.Xaml.Media.Imaging.BitmapImage();

    bitmapImage.SetSource(fileStream);
    displayImage.Source = bitmapImage;
    this.DataContext = file;
}
' file is null if user cancels the file picker.
If file IsNot Nothing Then

    ' Open a stream for the selected file.
    Dim fileStream = Await file.OpenAsync(Windows.Storage.FileAccessMode.Read)

    ' Set the image source to the selected bitmap.
    Dim BitmapImage = New Windows.UI.Xaml.Media.Imaging.BitmapImage()

    BitmapImage.SetSource(fileStream)
    displayImage.Source = BitmapImage
    Me.DataContext = file
End If

Step 2: Bind UI controls to file data

At this point, the image is shown in the UI, but the image file properties are not shown in the text blocks you added. You could set the Text property of each TextBlock in code, just as you set the Image.Source property.

But to display data, you typically use data binding to connect a data source to the UI, either by setting the Binding.Source property or by setting the DataContext on the UI element. When you establish a binding and the data source changes, the UI elements that are bound to the data source reflect the changes automatically.

Note  Bindings are one-way by default, which means updates to the data source are reflected in the UI. You can specify a two-way binding so that changes the user makes in a UI element can be reflected in the data source. For example, if the user edits the value in a TextBox, the binding engine automatically updates the underlying data source to reflect that change.

 

The DataContext property lets you set the default binding for an entire UI element, including all of its child elements. You set the StorageFile selected in the FileOpenPicker as the DataContext of the photo page. Remember that after picking the image, you set the DataContext with this line of code.

this.DataContext = file;
Me.DataContext = file

Here, you show the file name by binding the Text property of the title TextBlock to the StorageFile.DisplayName property of the selected file.

Because you don’t specify a binding Source, the data binding engine looks for the DisplayName property on the DataContext. If the DataContext is null or doesn't have a DisplayName property, the binding fails silently and no text is shown in the TextBlock.

Here, you bind the TextBlock controls to StorageFile properties .

To bind controls to data

  1. In Solution Explorer, double-click PhotoPage.xaml to open it.

  2. Select the TextBlock for the photo name that's under the "Get photo" button.

  3. In the Properties panel, click the Properties button () to show the Properties view.

  4. Under Common in the Properties panel, click the property marker for the Text property. The property menu opens.

    Note  The property marker is the small box symbol to the right of each property value. The Text property marker is black to indicate that it's set to a string value.

     

  5. In the property menu, select Create Data Binding.... The Create Data Binding dialog opens.

  6. In the Create Data Binding dialog, select Data context in the Binding type drop-down list.

  7. Enter "DisplayName" in the Path text box as shown here.

    Note  The message in the Create Data Binding dialog says the data context is not set. That's okay because you set the data context in code when the you run the app and get a picture.

     

  8. Click OK.

    Here's the Extensible Application Markup Language (XAML) for the TextBlock after you add the binding.

    <TextBlock Grid.Row="1" TextWrapping="Wrap" Text="{Binding DisplayName}" 
               Style="{ThemeResource SubheaderTextBlockStyle}"/>
    
  9. Select the TextBlock after the "File name:" TextBlock.

    1. Repeat steps 3-5 to create a data binding for this TextBlock.
    2. Enter "Name" in the Path text box and click OK.
  10. Select the TextBlock after the "Path:" TextBlock.

    1. Repeat steps 3-5 to create a data binding for this TextBlock.
    2. Enter "Path" in the Path text box and click OK.
  11. Select the TextBlock after the "Date created:" TextBlock.

    1. Repeat steps 3-5 to create a data binding for this TextBlock.
    2. Enter "DateCreated.Date" in the Path text box and click OK.

    Here's the XAML for the photo info StackPanel after you add the bindings.

                    <StackPanel Margin="20,0,0,0">
                        <TextBlock TextWrapping="Wrap" Text="File name:" 
                                   Style="{ThemeResource CaptionTextBlockStyle}"/>
                        <TextBlock TextWrapping="Wrap" Text="{Binding Name}" 
                                   Style="{ThemeResource BodyTextBlockStyle}" Margin="10,0,0,30"/>
                        <TextBlock TextWrapping="Wrap" Text="Path:" 
                                   Style="{ThemeResource CaptionTextBlockStyle}"/>
                        <TextBlock TextWrapping="Wrap" Text="{Binding Path}" 
                                   Style="{ThemeResource BodyTextBlockStyle}" Margin="10,0,0,30"/>
                        <TextBlock TextWrapping="Wrap" Text="Date created:" 
                                   Style="{ThemeResource CaptionTextBlockStyle}"/>
                        <TextBlock TextWrapping="Wrap" Text="{Binding DateCreated.Date}" 
                                   Style="{ThemeResource BodyTextBlockStyle}" Margin="10,0,0,30"/>
                    </StackPanel>
    
  12. Press F5 to build and run the app. Navigate to the photo page. Click the "Get photo" button to launch the FileOpenPicker. With bindings in place, file properties are now shown when you pick a file.

    Here's what the app looks like with a picture selected and the text blocks bound to data.

Step 3: Save and load state

In Part 2 of this tutorial series, Manage app lifecycle and state, you learned how to save and restore the app state. Now that you’ve added a new page to the app, you need to save and load the state of the new page, too. For the photo page, you only need to save and restore the currently displayed image file.

But you can’t just save the path to the file and then re-open it using that path. When a user picks a file with a FileOpenPicker, they implicitly give your app permission to that file. If you try to later retrieve the file using just the path, permission is denied.

Instead, to preserve access to the file for later use, the StorageApplicationPermissions class provides 2 lists where you can store the file and its permissions that were granted when the user opened it with the file picker.

You only need access to the last file the user picked so you can use it to restore the page state. For this, the MostRecentlyUsedList fits your needs.

When a user picks a file, you add it to the MostRecentlyUsedList. When you add a file to this list, the MostRecentlyUsedList returns a token that you use to retrieve the file later. You save this token in the pageState dictionary and use it to retrieve the current image file when you restore the page state.

To save state

  1. In Solution Explorer, double-click PhotoPage.xaml.cs/vb to open it.

  2. At the top of the PhotoPage class, add this code. It declares a variable to hold the token returned by the MostRecentlyUsedList.

    private string mruToken = null;
    
    Private mruToken As String = Nothing
    

    Here's the code with its surrounding code.

    public sealed partial class PhotoPage : Page
    {
        private string mruToken = null;
        private NavigationHelper navigationHelper;
    
        ...
    
    Public NotInheritable Class PhotoPage
        Inherits Page
    
        Private mruToken As String = Nothing
    
        ''' <summary>
    ...
    
  3. In the GetPhotoButton_Click event handler, add this code. It adds the picked file to the MostRecentlyUsedList and gets the token.

    // Add picked file to MostRecentlyUsedList.
    mruToken = Windows.Storage.AccessCache.StorageApplicationPermissions.MostRecentlyUsedList.Add(file);
    
    ' Add picked file to MostRecentlyUsedList.
    mruToken = Windows.Storage.AccessCache.StorageApplicationPermissions.MostRecentlyUsedList.Add(file)
    

    Here's the code with its surrounding code.

    // file is null if user cancels the file picker.
    if (file != null)
    {
        // Open a stream for the selected file.
        Windows.Storage.Streams.IRandomAccessStream fileStream =
            await file.OpenAsync(Windows.Storage.FileAccessMode.Read);
    
        // Set the image source to the selected bitmap.
        Windows.UI.Xaml.Media.Imaging.BitmapImage bitmapImage =
            new Windows.UI.Xaml.Media.Imaging.BitmapImage();
    
        bitmapImage.SetSource(fileStream);
        displayImage.Source = bitmapImage;
        this.DataContext = file;
    
        // Add picked file to MostRecentlyUsedList.
        mruToken = Windows.Storage.AccessCache.StorageApplicationPermissions.MostRecentlyUsedList.Add(file);
    }
    
    ' file is null if user cancels the file picker.
    If file IsNot Nothing Then
    
        ' Open a stream for the selected file.
        Dim fileStream = Await file.OpenAsync(Windows.Storage.FileAccessMode.Read)
    
        ' Set the image source to the selected bitmap.
        Dim BitmapImage = New Windows.UI.Xaml.Media.Imaging.BitmapImage()
    
        BitmapImage.SetSource(fileStream)
        displayImage.Source = BitmapImage
        Me.DataContext = file
    
        ' Add picked file to MostRecentlyUsedList.
        mruToken = Windows.Storage.AccessCache.StorageApplicationPermissions.MostRecentlyUsedList.Add(file)
    End If
    
  4. In the navigationHelper_SaveState method, add this code. It checks if the token exists, and saves it in the PageState dictionary if it does.

                if (!string.IsNullOrEmpty(mruToken))
                {
                    e.PageState["mruToken"] = mruToken;
                }
    
            If Not String.IsNullOrEmpty(mruToken) Then
                e.PageState("mruToken") = mruToken
            End If
    

    Here's the complete code for the navigationHelper_SaveState method.

    private void navigationHelper_SaveState(object sender, SaveStateEventArgs e)
    {
        if (!string.IsNullOrEmpty(mruToken))
        {
             e.PageState["mruToken"] = mruToken;
        }
    }
    
    Private Sub NavigationHelper_SaveState(sender As Object, e As Common.SaveStateEventArgs)
        If Not String.IsNullOrEmpty(mruToken) Then
            e.PageState("mruToken") = mruToken
        End If
    End Sub
    

To load state

  1. In PhotoPage.xaml.cs/vb, add the async keyword to the navigationHelper_LoadState method signature.

    private async void navigationHelper_LoadState(object sender, LoadStateEventArgs e)
    {
    }
    
    Private Async Sub NavigationHelper_LoadState(sender As Object, e As Common.LoadStateEventArgs)
    
    End Sub
    
  2. Add this code in the navigationHelper_LoadState method.

    Here, you get the token from the PageState dictionary. You use the token to retrieve the file from the MostRecentlyUsedList and restore the UI state.

                if (e.PageState != null && e.PageState.ContainsKey("mruToken"))
                {
                    object value = null;
                    if (e.PageState.TryGetValue("mruToken", out value))
                    {
                        if (value != null)
                        {
                            mruToken = value.ToString();
    
                            // Open the file via the token that you stored when adding this file into the MRU list.
                            Windows.Storage.StorageFile file =
                                await Windows.Storage.AccessCache.StorageApplicationPermissions.MostRecentlyUsedList.GetFileAsync(mruToken);
    
                            if (file != null)
                            {
                                // Open a stream for the selected file.
                                Windows.Storage.Streams.IRandomAccessStream fileStream =
                                    await file.OpenAsync(Windows.Storage.FileAccessMode.Read);
    
                                // Set the image source to a bitmap.
                                Windows.UI.Xaml.Media.Imaging.BitmapImage bitmapImage =
                                    new Windows.UI.Xaml.Media.Imaging.BitmapImage();
    
                                bitmapImage.SetSource(fileStream);
                                displayImage.Source = bitmapImage;
    
                                // Set the data context for the page.
                                this.DataContext = file;
                            }
                        }
                    }
                }
    
            If e.PageState IsNot Nothing AndAlso e.PageState.ContainsKey("mruToken") Then
                Dim value As Object = Nothing
                If e.PageState.TryGetValue("mruToken", value) Then
    
                    If (value IsNot Nothing) Then
                        mruToken = value.ToString()
    
                        ' Open the file via the token that you stored when adding this file into the MRU list.
                        Dim file =
                            Await Windows.Storage.AccessCache.StorageApplicationPermissions.MostRecentlyUsedList.GetFileAsync(mruToken)
    
                        If (file IsNot Nothing) Then
                            ' Open a stream for the selected file.
                            Dim fileStream = Await file.OpenAsync(Windows.Storage.FileAccessMode.Read)
    
                            ' Set the image source to a bitmap.
                            Dim BitmapImage = New Windows.UI.Xaml.Media.Imaging.BitmapImage()
    
                            BitmapImage.SetSource(fileStream)
                            displayImage.Source = BitmapImage
    
                            ' Set the data context for the page.
                            Me.DataContext = file
                        End If
                    End If
                End If
            End If
    
  3. Press F5 to build and run the app. Navigate to the photo page, and click the "Get photo" button to launch the FileOpenPicker and pick a file. Now, when the app is suspended, terminated, and restored, the image is re-loaded.

    Note  Review Part 2: Manage app lifecycle and state for instructions on how to suspend, terminate, and restore an app.

     

Summary

Congratulations, you're done with the fourth tutorial! You learned how to use file pickers and data binding in a Windows Store app.

See the code

Did you get stuck, or do you want to check your work? If so, see Part 4 complete code.