☰
UWP 文件夹枚举与分组查询实战:解读 Windows-universal-samples 的 FolderEnumeration 示例
2026/9/26 2:55:44 网站建设 项目流程
  • 示例工程

【免费下载链接】Windows-universal-samples

API samples for the Universal Windows Platform.

项目地址:https://gitcode.com/gh_mirrors/wi/Windows-universal-samples
点击查看免费下载

导读

本指南以 Windows-universal-samples 仓库中归档的 FolderEnumeration 示例(archived/FolderEnumeration/README.md)为核心,系统讲解 UWP 应用如何基于Windows.Storage与Windows.Storage.Search命名空间完成四类典型任务:枚举位置(文件夹、设备、网络位置)的顶层文件与子文件夹、按分组查询整个位置内的全部文件、在查询结果中预取文件属性与缩略图、以及展示文件的存储提供程序与离线可用性。读完本文,你将掌握StorageFolder、StorageFolderQueryResult、QueryOptions、CommonFolderQuery等核心 API 的实际用法,并能直接对照仓库中 C#、C++/CX、C++/WinRT 与 JavaScript 四种语言的完整实现进行二次开发。

示例概览:一份文档,四种语言,四个场景

FolderEnumeration 示例的目标非常聚焦:枚举某个位置(如文件夹、设备或网络位置)内部的顶层文件与文件夹,并通过查询按文件组(file groups)枚举该位置内的所有文件。它被归档在 archived/FolderEnumeration 目录下(JavaScript 实现),同时在 Samples/FolderEnumeration 目录下保留着当前维护的 C#(cs/)、C++/CX(cpp/)与 C++/WinRT(cppwinrt/)版本。

从源码结构看,四个语言版本在场景划分上完全对齐,每个场景对应一个独立的页面与代码文件:

场景核心任务C# 实现C++/CX 实现C++/WinRT 实现JavaScript(归档)
Scenario 1枚举顶层文件与子文件夹cs/Scenario1.xaml.cscpp/Scenario1.xaml.cppcppwinrt/Scenario1.cppjs/js/scenario1.js
Scenario 2按月份/评分/标签分组枚举全部文件cs/Scenario2.xaml.cs—cppwinrt/Scenario2.cppjs/js/scenario2.js
Scenario 3查询结果中预取文件属性与缩略图cs/Scenario3.xaml.cs——js/js/scenario3.js
Scenario 4展示文件提供程序与可用性cs/Scenario4.xaml.cs——js/js/scenario4.js

归档文档本身即明确了示例所依赖的两大核心命名空间:Windows.Storage(提供StorageFolder、StorageFile等对象模型)与Windows.Storage.Search(提供StorageFolderQueryResult、QueryOptions、CommonFolderQuery、CommonFileQuery等查询能力),这与仓库中各语言源码顶部的using/#include声明完全吻合。

前置准备:系统要求与库访问能力

归档文档给出了明确的运行与构建前提,并在各语言版本的Package.appxmanifest中得到了印证:

  • 运行环境:Windows 10 build 10500 或更高版本;Windows Server 2016 Technical Preview build 10500 或更高版本;Windows Phone 同样要求 Windows 10 build 10500 及以上。(当前维护版 Samples/FolderEnumeration/README.md 中标注的系统要求为 Windows 10 build 10586。)
  • 开发工具:Visual Studio 2017(归档版本要求);当前维护版本要求 Visual Studio 与 Windows 10 即可构建执行。
  • 库访问能力:示例访问"图片库"(Pictures library),因此必须在应用清单中声明picturesLibrary能力。这一点在 cs/Package.appxmanifest 与 js/Package.appxmanifest 的<Capabilities>节点中均有明确声明:
<Capabilities> <uap:Capability Name="picturesLibrary" /> </Capabilities>

缺少该声明时,KnownFolders.GetFolderForUserAsync获取图片库将失败,这是运行示例前必须检查的第一项配置。

场景一:枚举位置的顶层文件与子文件夹

归档文档描述的第一个任务,是使用StorageFolder.GetFilesAsync与StorageFolder.GetFoldersAsync两个方法,枚举某个位置(示例中为图片库)仅顶层(immediate children)的文件与文件夹。

C# 实现

cs/Scenario1.xaml.cs 展示了完整的调用链:

StorageFolder picturesFolder = await KnownFolders.GetFolderForUserAsync(null /* current user */, KnownFolderId.PicturesLibrary); IReadOnlyList<StorageFile> fileList = await picturesFolder.GetFilesAsync(); IReadOnlyList<StorageFolder> folderList = await picturesFolder.GetFoldersAsync(); var count = fileList.Count + folderList.Count; StringBuilder outputText = new StringBuilder(picturesFolder.Name + " (" + count + ")\n\n"); foreach (StorageFolder folder in folderList) { outputText.AppendLine(" " + folder.DisplayName + "\\"); } foreach (StorageFile file in fileList) { outputText.AppendLine(" " + file.Name); }

要点拆解:

  • KnownFolders.GetFolderForUserAsync(null, KnownFolderId.PicturesLibrary):null表示当前用户,返回代表图片库的StorageFolder。该 API 同样出现在 C++/WinRT 与 JavaScript 版本中,是三种语言共用的入口。
  • GetFilesAsync()/GetFoldersAsync():分别只返回直接位于该文件夹下的文件与子文件夹,不递归深入子目录。
  • 子文件夹使用DisplayName显示并附加\后缀,文件使用Name,用于在 UI 上区分目录项与文件项。

C++/WinRT 实现

cppwinrt/Scenario1.cpp 使用协程与co_await编写了语义等价、异步非阻塞的实现:

StorageFolder picturesFolder = co_await KnownFolders::GetFolderForUserAsync(nullptr /* current user */, KnownFolderId::PicturesLibrary); IVectorView<StorageFile> fileList = co_await picturesFolder.GetFilesAsync(); IVectorView<StorageFolder> folderList = co_await picturesFolder.GetFoldersAsync(); uint32_t count = fileList.Size() + folderList.Size(); std::wstringstream output; output << std::wstring_view{ picturesFolder.Name() } << L" (" << count << L")\n\n"; for (StorageFolder const& folder : folderList) { output << L" " << std::wstring_view{ folder.DisplayName() } << L"\\" << std::endl; } for (StorageFile const& file : fileList) { output << L" " << std::wstring{ file.Name() } << std::endl; } OutputTextBlock().Text(output.str());

C++/CX 版本(cpp/Scenario1.xaml.cpp)则使用concurrency::create_task链式组合异步操作,三种 C++ 风格可以对照学习:C++/CX 的任务延续(task continuation)、C++/WinRT 的协程、C# 的async/await本质上是同一异步模型的三种表达。

JavaScript 实现

归档的 js/js/scenario1.js 使用 WinJS Promise 风格,并调用picturesLibrary.getItemsAsync()一次性获取全部顶层项,再通过item.isOfType(Windows.Storage.StorageItemTypes.folder)区分文件夹与文件:

Windows.Storage.KnownFolders.getFolderForUserAsync(null, Windows.Storage.KnownFolderId.picturesLibrary).then(function (picturesLibrary) { group = outputResultGroup(picturesLibrary.name); return picturesLibrary.getItemsAsync(); }).done(function (items) { outputItems(group, items); });

值得注意:C#/C++ 版本刻意将文件与文件夹分两次异步调用(GetFilesAsync与GetFoldersAsync),而 JS 版本用一次getItemsAsync()合并获取,随后按类型分类显示——两种写法均可,前者在只需文件或只需文件夹时更省开销。

场景二:按分组查询位置内的全部文件

归档文档描述的核心概念是file groups(文件组):使用StorageFolder.CreateFolderQueryWithOptions将位置(图片库)中包括子文件夹在内的全部文件,按你指定的条件(如评分 rating)排序成组;每组是一个由StorageFolderQueryResult.GetFoldersAsync返回的虚拟文件夹,同样以StorageFolder对象表示——组内所有文件共享你指定的共同特征。

三种分组条件

cs/Scenario2.xaml.cs 提供了三个按钮,分别演示CommonFolderQuery枚举的三个取值:

// 按拍摄月份分组 await GroupByHelperAsync(new QueryOptions(CommonFolderQuery.GroupByMonth)); // 按评分分组 await GroupByHelperAsync(new QueryOptions(CommonFolderQuery.GroupByRating)); // 按标签分组 await GroupByHelperAsync(new QueryOptions(CommonFolderQuery.GroupByTag));

查询与结果消费

GroupByHelperAsync(cs/Scenario2.xaml.cs)演示了完整的查询流水线:

StorageFolder picturesFolder = await KnownFolders.GetFolderForUserAsync(null /* current user */, KnownFolderId.PicturesLibrary); StorageFolderQueryResult queryResult = picturesFolder.CreateFolderQueryWithOptions(queryOptions); IReadOnlyList<StorageFolder> folderList = await queryResult.GetFoldersAsync(); foreach (StorageFolder folder in folderList) { IReadOnlyList<StorageFile> fileList = await folder.GetFilesAsync(); // 以 "组名 (文件数)" 作为标题,逐文件列出 }

三步流程可概括为:

  1. 构造QueryOptions:传入CommonFolderQuery枚举值,决定分组维度;
  2. 创建查询:picturesFolder.CreateFolderQueryWithOptions(queryOptions)返回StorageFolderQueryResult;
  3. 取结果:queryResult.GetFoldersAsync()返回一组虚拟文件夹,每个虚拟文件夹再GetFilesAsync()展开其成员文件。

C++/WinRT 版本(cppwinrt/Scenario2.cpp)在接口使用上完全一致,仅将QueryOptions(CommonFolderQuery::GroupByMonth)等枚举改为 C++/WinRT 命名空间形式。JavaScript 归档版本(js/js/scenario2.js)使用picturesLibrary.createFolderQuery(Windows.Storage.Search.CommonFolderQuery.groupByMonth)创建查询,并用WinJS.Promise.join聚合多个folder.getFilesAsync()的 Promise,确保所有分组按顺序显示:

var promises = folders.map(function (folder) { return folder.getFilesAsync(); }); WinJS.Promise.join(promises).done(function (folderContents) { for (var i in folderContents) { var group = outputResultGroup(folders.getAt(i).name); outputItems(group, folderContents[i]); } });

设计含义

从实现可以推断CommonFolderQuery分组查询的价值:它让系统(而非应用)负责跨子目录收集与归类文件,应用只需消费分组结果。典型的应用场景包括相册按月份归档、媒体库按流派分类等。归档文档特别指出,CommonFolderQuery与CommonFileQuery枚举是示例中"额外的重要 API",后者用于文件级查询的排序维度(如OrderByName),在场景三中会用到。

场景三:查询结果中预取文件属性与缩略图

第三个任务引入性能优化手段:在创建查询时,通过QueryOptions.SetPropertyPrefetch指定要预取的属性,系统会在索引阶段提前抓取,之后访问这些属性时无需逐文件异步等待;SetThumbnailPrefetch对缩略图同理。归档文档同时提醒:预取可能延长查询执行时间,但会让大量文件信息访问更高效——这是典型的"以查询耗时换遍历效率"的取舍。

属性预取配置

cs/Scenario3.xaml.cs 的配置流程:

// 文件类型过滤器:只查询图片 List<string> fileTypeFilter = new List<string>(); fileTypeFilter.Add(".jpg"); fileTypeFilter.Add(".png"); fileTypeFilter.Add(".bmp"); fileTypeFilter.Add(".gif"); // 构造查询选项:按名称排序 + 类型过滤 var queryOptions = new QueryOptions(CommonFileQuery.OrderByName, fileTypeFilter); // 预取:顶层属性使用 PropertyPrefetchOptions,附加属性使用字符串列表 List<string> propertyNames = new List<string>(); propertyNames.Add("System.Copyright"); // 版权 propertyNames.Add("System.Image.ColorSpace"); // 色彩空间 queryOptions.SetPropertyPrefetch(PropertyPrefetchOptions.ImageProperties, propertyNames); // 缩略图预取(按需启用,例如构建相册网格视图时) /* const uint requestedSize = 190; const ThumbnailMode thumbnailMode = ThumbnailMode.PicturesView; const ThumbnailOptions thumbnailOptions = ThumbnailOptions.UseCurrentScale; queryOptions.SetThumbnailPrefetch(thumbnailMode, requestedSize, thumbnailOptions); */

参数含义:

  • PropertyPrefetchOptions.ImageProperties:PropertyPrefetchOptions枚举值,代表一组"顶层属性"(尺寸、日期等图片常用属性),可与其他选项按位组合;
  • 附加属性列表:以 Windows 系统属性名(如System.Copyright、System.Image.ColorSpace)补充预取;
  • SetThumbnailPrefetch(mode, requestedSize, options):ThumbnailMode决定缩略图用途(示例用PicturesView),requestedSize为请求边长(示例为 190 像素),ThumbnailOptions控制生成行为(示例用UseCurrentScale)。

JavaScript 归档版本(js/js/scenario3.js)以search.QueryOptions(search.CommonFileQuery.orderByName, fileTypeFilter)构造选项,queryOptions.setPropertyPrefetch(imageProperties, additionalProperties)完成同样的预取声明,并把缩略图预取同样保留为注释块,说明它是按需开启的可选项。

消费预取结果

预取的效果体现在结果消费阶段(cs/Scenario3.xaml.cs):

var query = picturesFolder.CreateFileQueryWithOptions(queryOptions); IReadOnlyList<StorageFile> fileList = await query.GetFilesAsync(); foreach (StorageFile file in fileList) { // 预取成功时,GetImagePropertiesAsync 会同步返回 var properties = await file.Properties.GetImagePropertiesAsync(); // 展示 Dimensions: 宽x高 // 附加属性同理,预取完成后可立即返回 IDictionary<string, object> extraProperties = await file.Properties.RetrievePropertiesAsync(propertyNames); // 读取 System.Copyright 与 System.Image.ColorSpace,空值显示 "none" }

关键点:GetImagePropertiesAsync()与RetrievePropertiesAsync()在接口上仍是异步方法,但当属性已被预取时会近乎立即返回,避免了遍历数百个文件时逐次发起磁盘/索引 I/O。这正是归档文档所述"预取让大量文件信息访问更高效"的机制所在。

场景四:展示文件提供程序与离线可用性

第四个任务把查询对象从固定的图片库扩展到用户任意选取的文件夹,并利用StorageFile.Provider与StorageFile.IsAvailable两个属性展示每个文件的存储提供程序(provider)与离线可用状态。

cs/Scenario4.xaml.cs 的实现:

// 1. 让用户选择文件夹 FolderPicker picker = new FolderPicker(); picker.FileTypeFilter.Add("*"); picker.ViewMode = PickerViewMode.List; picker.SuggestedStartLocation = PickerLocationId.DocumentsLibrary; StorageFolder folder = await picker.PickSingleFolderAsync(); if (folder != null) { // 2. 创建默认文件查询并获取全部文件 var query = folder.CreateFileQuery(); var fileList = await query.GetFilesAsync(); // 3. 逐文件展示 provider 与可用性 foreach (StorageFile file in fileList) { var line = file.Name; line += ": On " + file.Provider.DisplayName; // 本机、OneDrive、网络或应用内容 line += " ("; line += file.IsAvailable ? "available" : "not available"; line += ")"; // 输出到 UI } }

行为语义:

  • FolderPicker的FileTypeFilter加入"*"表示接受任意文件类型;SuggestedStartLocation = DocumentsLibrary只影响打开对话框的起始位置,用户仍可导航到任意目录——这绕开了picturesLibrary等库能力的限制;
  • folder.CreateFileQuery():无参数重载创建覆盖该文件夹(含子文件夹)全部文件的默认查询;
  • file.Provider.DisplayName:文件所在存储提供程序的显示名。从源码注释看,典型值包括本机(This PC)、OneDrive、网络位置(Network)以及应用内容(Application Content);
  • file.IsAvailable:指示文件当前是否可用。对于 OneDrive 这类云占位文件,联机时或标记为"始终离线可用"时通常为true,否则为false,是判断云文件是否可立即读取的直接手段。

JavaScript 归档版本(js/js/scenario4.js)使用Windows.Storage.Pickers.FolderPicker与folder.createFileQuery()完成同样的流程,逐项拼接": On " + file.provider.displayName + " (available/not available)"输出。

关键 API 速查

归档文档明确列出的"额外重要 API"及本仓库中的对应用法汇总如下:

API用途仓库证据
CommonFolderQuery文件夹分组维度(GroupByMonth、GroupByRating、GroupByTag等)cs/Scenario2.xaml.cs
CommonFileQuery文件查询排序维度(如OrderByName)cs/Scenario3.xaml.cs
PropertyPrefetchOptions顶层属性预取选项(如ImageProperties)cs/Scenario3.xaml.cs
StorageFolderQueryResult分组查询的结果句柄,GetFoldersAsync()返回虚拟文件夹cs/Scenario2.xaml.cs
QueryOptions封装排序、过滤、属性/缩略图预取的查询参数cs/Scenario3.xaml.cs

构建与运行

归档文档给出了完整的构建与运行步骤,适用于 Samples/FolderEnumeration 下的当前版本:

  1. 若通过 ZIP 下载整个示例集合,务必解压完整归档,不要只解压包含目标示例的文件夹——示例依赖仓库根目录的SharedContent共享资源;
  2. 启动 Visual Studio,选择File>Open>Project/Solution;
  3. 在解压目录下进入Samples子目录 → 本示例子目录 → 所选语言子目录(C++、C# 或 C++/WinRT),双击其中的解决方案文件(.sln),如 cs/FolderEnumeration.sln;
  4. 按Ctrl+Shift+B或选择Build>Build Solution构建。

运行分两种方式:

  • 仅部署:选择Build>Deploy Solution;
  • 部署并运行:按F5或选择Debug>Start Debugging进行调试运行;按Ctrl+F5或选择Debug>Start Without Debugging则不调试直接运行。

关联资源

归档文档将本示例与仓库内其他文件/文件夹相关主题归为一组,可在本仓库中继续深入:

  • 当前维护版本说明:Samples/FolderEnumeration/README.md(标注了该示例面向 C#、C++/CX、C++/WinRT 三种语言,并链接回归档的 JavaScript 版本);
  • 程序化文件搜索:Samples/FileSearch;
  • 文件访问:Samples/FileAccess;
  • 文件与文件夹缩略图:Samples/FileThumbnails;
  • 核心命名空间Windows.Storage与Windows.Storage.Search的用法,均可直接对照上述示例源码与 archived/FolderEnumeration/js 目录下的全部 JavaScript 页面实现学习。

需要注意的是:归档版本(JavaScript)与当前维护版本(C#/C++/C++/WinRT)在场景划分与 API 调用上一一对应,四种语言实现互为翻译参照,是学习 UWP 异步编程与文件系统查询 API 的极佳对照样本。

  • 示例工程

【免费下载链接】Windows-universal-samples

API samples for the Universal Windows Platform.

项目地址:https://gitcode.com/gh_mirrors/wi/Windows-universal-samples
点击查看免费下载
上一篇:FerretDB 遥测机制全解析:数据采集范围、telemetry.json 结构与开启/禁用实操指南
下一篇:Hearthstone-Script深度解析:炉石传说智能脚本的5大核心价值与实战指南

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询