语言

教程:在 WinUI 中使用选取器打开文件和文件夹

使用Windows 应用 SDK文件和文件夹选取器,让用户在 WinUI 应用中浏览和选择文件或文件夹。 选取器 API 提供熟悉的Windows体验,可帮助用户导航其设备和云存储位置。 本文介绍如何实现文件打开选取器和文件夹选取器、自定义其行为以及处理应用中所选结果。

Windows 应用 SDK FileOpenPicker 和 FileSavePicker 类创建一个选取器对话框,允许用户指定要打开或保存的文件的名称和位置。 FolderPicker 类允许你选择文件夹。

若要了解如何使用选取器保存文件,请参阅使用Windows 应用 SDK选取器保存文件。

Prerequisites

  • Windows 应用 SDK 1.8 或更高版本。 本文中使用的 Microsoft.Windows.Storage.Pickers API 是在 Windows 应用 SDK 1.8 中引入的。 如果项目以较早版本为目标,请参阅将 WinRT 选取器与 HWND 互操作结合使用,以便了解旧式方法。

重要 API

本文使用以下 API:

文件选择界面

文件选取器显示面向用户的信息,并在打开或保存文件时提供一致的体验。

这些信息包括:

  • 当前位置
  • 用户选取的项目
  • 用户可以浏览到的位置的树。 这些位置包括文件系统位置(如音乐或下载文件夹)以及实现文件选取器协定的应用(如相机、照片和Microsoft OneDrive)。

你可能有一个应用,允许用户打开或保存文件。 当用户执行该操作时,你的应用会调用文件选取器,以显示文件选取器用户界面。

打开的文件选取器的屏幕截图,其中选择了一个筛选器以显示 .txt、.pdf、.doc和 .docx 文件。

选取器如何在你的应用中工作

使用选取器,你的应用可以访问、浏览和保存用户系统上的文件和文件夹。 你的应用接收这些选取作为轻型 PickFileResult 和 PickFolderResult 对象,这些对象提供所选文件或文件夹的路径。

选取器使用单个统一界面,让用户从文件系统或其他应用中选取文件和文件夹。 从其他应用选取的文件类似于文件系统中的文件。 一般情况下,你的应用可以像其他对象一样对它们进行作。 其他应用通过参与文件选取器协定来提供文件。

例如,可以在应用中调用文件选取器,以便用户可以打开文件。 此操作会使您的应用成为呼叫应用。 文件选取器与系统和其他应用交互,以便用户导航和选取文件。 当用户选择文件时,文件选取器会返回该文件的应用路径。

选择要打开示例的文件

以下代码演示如何使用 FileOpenPicker 类让用户选取单个文件,例如照片。 代码设置选取器的属性以自定义其外观和行为,然后使用 PickSingleFileAsync 方法向用户显示选取器。 如果用户选取文件,应用将读取文件的内容并将其存储在变量中。

using Microsoft.Windows.Storage.Pickers;

var openPicker = new FileOpenPicker(this.AppWindow.Id)
{
    // (Optional) Specify the initial location for the picker. 
    //     If the specified location doesn't exist on the user's machine, it falls back to the DocumentsLibrary.
    //     If not set, it defaults to PickerLocationId.Unspecified, and the system will use its default location.
    SuggestedStartLocation = PickerLocationId.DocumentsLibrary,
    
    // (Optional) specify the text displayed on the commit button. 
    //     If not specified, the system uses a default label of "Open" (suitably translated).
    CommitButtonText = "Choose selected files",

    // (Optional) specify file extension filters. If not specified, defaults to all files (*.*).
    FileTypeFilter = { ".txt", ".pdf", ".doc", ".docx" },

    // (Optional) specify the view mode of the picker dialog. If not specified, defaults to List.
    ViewMode = PickerViewMode.List,
};

var result = await openPicker.PickSingleFileAsync();
if (result is not null)
{
    var content = System.IO.File.ReadAllText(result.Path);
}
else
{
    // Add your error handling here.
}
#include <winrt/Microsoft.Windows.Storage.Pickers.h>
#include <fstream>
#include <string>

using namespace winrt::Microsoft::Windows::Storage::Pickers;

FileOpenPicker openPicker(this->AppWindow().Id());

// (Optional) Specify the initial location for the picker. 
//     If the specified location doesn't exist on the user's machine, it falls back to the DocumentsLibrary.
//     If not set, it defaults to PickerLocationId.Unspecified, and the system will use its default location.
openPicker.SuggestedStartLocation(PickerLocationId::DocumentsLibrary);

// (Optional) specify the text displayed on the commit button. 
//     If not specified, the system uses a default label of "Open" (suitably translated).
openPicker.CommitButtonText(L"Choose selected files");

// (Optional) specify file extension filters. If not specified, defaults to all files (*.*).
openPicker.FileTypeFilter().ReplaceAll({ L".txt", L".pdf", L".doc", L".docx" });

// (Optional) specify the view mode of the picker dialog. If not specified, defaults to List.
openPicker.ViewMode(PickerViewMode::List);

auto result{ co_await openPicker.PickSingleFileAsync() };
if (result)
{
    std::ifstream fileReader(result.Path().c_str());
    std::string text((std::istreambuf_iterator<char>(fileReader)), std::istreambuf_iterator<char>());
    winrt::hstring hText = winrt::to_hstring(text);
}
else
{
    // Add your error handling here.
}

选取多个文件以打开示例

还可以让用户选取多个文件。 以下代码演示如何使用 FileOpenPicker 类让用户选取多个文件,例如照片。 此过程相同,但 PickMultipleFilesAsync 方法返回 PickFileResult 对象的集合。 需要原始文件路径时,每个结果都会公开一个 Path 属性。

using Microsoft.Windows.Storage.Pickers;

var openPicker = new FileOpenPicker(this.AppWindow.Id);

var results = await openPicker.PickMultipleFilesAsync();
if (results.Count > 0)
{
    var pickedFilePaths = results.Select(f => f.Path);
    foreach (var path in pickedFilePaths)
    {
        var content = System.IO.File.ReadAllText(path);
    }
}
else
{
    // Add your error handling here.
}
#include <winrt/Microsoft.Windows.Storage.Pickers.h>
#include <fstream>
#include <string>

using namespace winrt::Microsoft::Windows::Storage::Pickers;

FileOpenPicker openPicker(this->AppWindow().Id());
auto results{ co_await openPicker.PickMultipleFilesAsync() };
if (results.Size() > 0)
{
    for (auto const& result : results)
    {
        std::ifstream fileReader(result.Path().c_str());
        std::string text((std::istreambuf_iterator<char>(fileReader)), std::istreambuf_iterator<char>());
        winrt::hstring hText = winrt::to_hstring(text);
    }
}
else
{
    // Add your error handling here.
}

选取文件夹示例

若要使用 FolderPicker 类选取文件夹,请使用以下代码。 此代码创建文件夹选取器,使用 PickSingleFolderAsync 方法向用户显示文件夹,并在 PickFolderResult 对象中检索所选文件夹的路径。 如果用户选取文件夹,应用将检索文件夹的路径,并将其存储在变量中供以后使用。

using Microsoft.Windows.Storage.Pickers;

var folderPicker = new FolderPicker(this.AppWindow.Id)
{
    // (Optional) Specify the initial location for the picker. 
    //     If the specified location doesn't exist on the user's machine, it falls back to the DocumentsLibrary.
    //     If not set, it defaults to PickerLocationId.Unspecified, and the system will use its default location.
    SuggestedStartLocation = PickerLocationId.DocumentsLibrary,

    // (Optional) specify the text displayed on the commit button. 
    //     If not specified, the system uses a default label of "Open" (suitably translated).
    CommitButtonText = "Select Folder",

    // (Optional) specify the view mode of the picker dialog. If not specified, default to List.
    ViewMode = PickerViewMode.List,
};

var result = await folderPicker.PickSingleFolderAsync();

if (result is not null)
{
    var path = result.Path;
}
else
{
    // Add your error handling here.
}
#include <winrt/Microsoft.Windows.Storage.Pickers.h>

using namespace winrt::Microsoft::Windows::Storage::Pickers;

FolderPicker folderPicker(this->AppWindow().Id());

// (Optional) Specify the initial location for the picker. 
//     If the specified location doesn't exist on the user's machine, it falls back to the DocumentsLibrary.
//     If not set, it defaults to PickerLocationId.Unspecified, and the system will use its default location.
folderPicker.SuggestedStartLocation(PickerLocationId::DocumentsLibrary);

// (Optional) specify the text displayed on the commit button. 
//     If not specified, the system uses a default label of "Open" (suitably translated).
folderPicker.CommitButtonText(L"Select Folder");

// (Optional) specify the view mode of the picker dialog. If not specified, default to List.
folderPicker.ViewMode(PickerViewMode::List);

auto result{ co_await folderPicker.PickSingleFolderAsync() };
if (result)
{
    auto path{ result.Path() };
}
else
{
    // Add your error handling here.
}

小窍门

每当应用通过选取器访问文件或文件夹时,请将其添加到应用的 FutureAccessList 或 MostRecentlyUsedList 以使用 Windows 运行时 (WinRT) API 跟踪它。 有关详细信息,请参阅 “跟踪最近使用的文件和文件夹”。

文件夹选取器 UI 如下所示:

查看 C 驱动器的文件夹选取器屏幕截图。

将 WinRT 选取器与 HWND 互操作结合使用

如果你的项目面向 Windows 应用 SDK 1.7 或更早版本,或者你使用旧版 Windows.Storage.Pickers API(例如,在从 UWP 迁移时),则必须先使用窗口句柄(HWND)对选取器进行初始化,然后才能显示它。 如果没有此步骤,选取器会在桌面(WinUI 3)应用中以无提示方式引发异常或失败。

Important

WinUI 3 桌面应用没有 CoreWindow。 在进行任何 Pick*Async 调用之前,必须通过调用 WinRT.Interop.InitializeWithWindow.Initialize(C#)或 IInitializeWithWindow::Initialize(C++/WinRT)将选取器与应用窗口关联。

C# 示例(旧版 WinRT 选取器)

// This example assumes you have a reference to your app's main window.
// In a Window class, use 'this'. In a Page, use a stored reference like App.MainWindow.

var picker = new Windows.Storage.Pickers.FileOpenPicker();
picker.FileTypeFilter.Add(".png");
picker.FileTypeFilter.Add(".jpg");

// Get the window handle (HWND) for the current WinUI 3 window.
var hwnd = WinRT.Interop.WindowNative.GetWindowHandle(App.MainWindow);

// Associate the picker with the window.
WinRT.Interop.InitializeWithWindow.Initialize(picker, hwnd);

var file = await picker.PickSingleFileAsync();
if (file is null)
{
    // The user cancelled the picker.
}

C++/WinRT 示例(旧版 WinRT 选取器)

// pch.h
#include <winrt/Windows.Storage.Pickers.h>
#include <microsoft.ui.xaml.window.h>
#include <shobjidl_core.h>

// MainWindow.xaml.cpp — inside a MainWindow member function
Windows::Storage::Pickers::FileOpenPicker picker;
picker.FileTypeFilter().Append(L".png");
picker.FileTypeFilter().Append(L".jpg");

// Get the window handle (HWND) of the current WinUI 3 window.
auto windowNative{ this->m_inner.as<::IWindowNative>() };
HWND hwnd{ 0 };
windowNative->get_WindowHandle(&hwnd);

// Associate the picker with the window.
auto initializeWithWindow{ picker.as<::IInitializeWithWindow>() };
initializeWithWindow->Initialize(hwnd);

auto file{ co_await picker.PickSingleFileAsync() };
if (!file)
{
    // The user cancelled the picker.
}

小窍门

对于新应用程序,推荐做法是使用 Windows 应用 SDK 选取器(Microsoft.Windows.Storage.Pickers);这些选取器在构造函数中接受一个 WindowId,并且不需要 InitializeWithWindow 模式。 请参阅本文前面的示例。

有关检索窗口句柄的详细信息,请参阅“检索窗口句柄”(HWND)。