Files
fluid-explorer/README.md
T

12 KiB
Raw Blame History

Fluid 文件资源管理器(WinUI 3 重构版)

用 Windows App SDK / WinUI 3 从零实现的 Windows 11 文件资源管理器替代品。 视觉与布局对齐原版(标签栏在标题栏内、Mica、命令栏、面包屑地址栏、导航窗格、详情/图标视图、状态栏), 但针对原版被吐槽最多的几个痛点做了结构性改造。

只用 Windows 11 原生那一套:WinUI 3 自带控件(NavigationView 风格侧边栏用 TreeView、TabView、BreadcrumbBar、AutoSuggestBox、InfoBar、ContentDialog…)、 Segoe Fluent Icons 官方图标字体、外壳原版文件图标(SHGetFileInfo / IShellItemImageFactory,即资源管理器显示的同一批 imageres.dll 图标)、 以及系统自带的 Mica 材质与 WinUI 原生动画。没有引入任何第三方 UI/图标/动画库。


1. 直接运行

dotnet build E:\deepseek\FluidExplorer\FluidExplorer.csproj -c Debug
E:\deepseek\FluidExplorer\bin\Debug\net8.0-windows10.0.26100.0\win-x64\FluidExplorer.exe
  • 目标框架 net8.0-windows10.0.26100.0,x64,非打包(WindowsPackageType=None)+ Windows App SDK 自包含, 因此不需要预装 Windows App SDK 运行时,双击 exe 即可。
  • 本机 NuGet 离线(nuget.config 里 <clear/>):所有依赖已在全局包缓存中,可直接还原。 依赖仅 Microsoft.WindowsAppSDK 2.2.0 与 CommunityToolkit.Mvvm 8.4.2(后者只用于减少 INotifyPropertyChanged 样板代码)。
  • 设置文件:%LOCALAPPDATA%\FluidExplorer\settings.json(写入失败时退回程序目录)。
  • 启动日志:程序目录下 startup.log(用于定位启动期异常)。

2. 针对原版痛点的改造

原版痛点 本实现的做法
搜索慢/搜不到(原版走 Windows Search 索引,往往等半天甚至永远"正在搜索") Everything 式 NTFS 索引:FSCTL_ENUM_USN_DATA 直读 MFT 全集 + 原始 MFT 解析取真实大小/时间 + FSCTL_READ_USN_JOURNAL 增量监听;查询是内存并行扫描,20 万条目下 0.3–9 ms(见 §5)。没有索引时自动退化为可取消的实时扫描,并在界面上明确标注"实时扫描",绝不假装在搜
搜索框必须回车、没有即时反馈 键入即搜(110 ms 防抖)+ 搜索框下拉即时建议 + 结果区实时显示"N 项结果 · X 毫秒 · 数据来源"
搬文件很麻烦(开两个窗口来回拖) ① 双窗格(Ctrl+Shift+D,中间可拖动分隔条)② 跨窗格剪贴板 ③ 拖到文件夹行/侧边栏即可移动(按住 Ctrl 复制)④ "移动到/复制到"以外的批量操作统一进队列
复制/移动卡死、无法暂停/取消 独立文件操作引擎:后台队列、可暂停/继续/取消、字节级进度 + 速度 + 剩余时间、失败重试(100/300/900 ms)、单文件失败不中断整批、长路径(\\?\)支持、同卷移动走 File.Move(1000 个文件 15 ms)
覆盖冲突弹系统模态框、还打断操作 应用内冲突对话框(保留两者/替换/跳过 + "对后续所有冲突应用"),作业在等待用户选择时保持运行态、UI 完全不卡
误操作无法挽回 Ctrl+Z 一步撤销:移动/重命名搬回原位;删除默认进回收站并记录 $I→$R 映射,撤销即从回收站还原(不依赖系统弹窗)
打开大文件夹/网络盘就"无响应" 枚举走 FileSystemEnumerable(底层 NtQueryDirectoryFile 批量缓冲,一次拿回名称+属性+大小+时间),分批回调(首批立刻上屏)、全程可取消、错误只在 InfoBar 提示不阻塞;属性列的真类型名走注册表并缓存
状态栏信息少 底部状态栏常驻:项目数 / 选中项数与合计体积 / 索引进度与条数 / 一键建索引 / 视图切换 / 操作队列入口
深浅色与强调色不跟随 全部颜色取自 WinUI 内置主题资源(不写死任何色值),ElementTheme.Default + MicaBackdrop 跟随系统;标题栏按钮颜色随主题切换;设置里可手动覆盖为浅/深色
动画要么没有要么过度 只用 WinUI 原生动画(列表项增删、悬停/选中反馈、TabView、对话框、Mica 过渡);设置里可一键关闭(内部用 Timeline.AllowDependentAnimations 总开关),不额外叠加自造动效

其他对齐原版的细节:真实路径面包屑(此电脑 › 本地磁盘 › …)、侧边栏(主页/图库/快速访问/此电脑/网络/回收站,可展开、跟随当前路径高亮)、 按文件夹记住视图方式与排序(写入 settings.json)、"名称"列用系统 StrCmpLogicalW 自然排序(文件2 排在 文件10 前)、文件夹恒排在文件前、 显示/隐藏隐藏项与扩展名、系统原版右键菜单(外壳 IContextMenu,第三方扩展条目也在)。

键盘:Ctrl+T/W/Tab 标签页、Ctrl+Shift+D 双窗格、Alt+←/→/↑ 前进后退上级、Backspace 上级、F5 刷新、F2 重命名、 Delete/Shift+Delete 删除/彻底删除、Ctrl+X/C/V、Ctrl+Shift+C 复制路径、Ctrl+Shift+N 新建文件夹、Ctrl+Z 撤销、Ctrl+F 聚焦搜索、Ctrl+L 编辑地址。


3. 代码结构

FluidExplorer/
├─ App.xaml(.cs)                应用入口、启动阶段日志、全局异常兜底
├─ MainWindow.xaml(.cs)         窗口外壳:Mica、扩展标题栏、标题栏按钮配色、快捷键、冲突对话框
├─ Views/
│  ├─ ShellView.xaml(.cs)       标签栏(位于标题栏区域内)+ 操作队列面板
│  ├─ ExplorerTabView.xaml(.cs) 单/双窗格布局与分隔条
│  ├─ ExplorerPaneView.xaml(.cs) 一个浏览窗格:命令栏 / 地址栏 / 导航窗格 / 内容区 / 状态栏
│  └─ SettingsDialog.xaml(.cs)  设置
├─ ViewModels/
│  ├─ MainViewModel.cs          标签页、侧边栏、索引状态、作业快照、设置
│  ├─ ExplorerTabViewModel.cs   标签页 = 1~2 个窗格
│  ├─ ExplorerPaneViewModel.cs  导航状态机:枚举、排序、搜索、选择、文件操作、回收站、图库
│  ├─ JobRowViewModel.cs        UI 线程的作业快照(作业在后台线程更新,不能直接绑 UI)
│  └─ SidebarNode.cs            侧边栏节点 / 面包屑段 / 搜索结果行
├─ Models/                      FileEntry / ExplorerItem / FolderListing / 排序与视图枚举
├─ Navigation/                  位置模型(本机/图库/此电脑/回收站/驱动器/路径)+ 前进后退历史
├─ Services/
│  ├─ AppServices.cs            组合根
│  ├─ AppSettings.cs            设置持久化(含按文件夹视图状态)
│  ├─ FileSystem/               快速枚举(NtQueryDirectoryFile 批量缓冲)+ 路径工具
│  ├─ Search/                   查询语法解析、搜索门面、NTFS 索引(Usn/:MFT 解析、名字池、索引存储、通配符)
│  ├─ Icons/                    外壳原版图标与缩略图(SHGetFileInfo / IShellItemImageFactory,LRU + 同键合并)
│  ├─ Operations/               文件操作队列引擎(复制/移动/删除/重命名/撤销/回收站定位)
│  ├─ ItemVisuals/              列表行的图标/缩略图按需加载(并发上限 + 去重)
│  └─ Shell/                    外壳能力:已知文件夹、类型名、打开/属性/剪贴板、回收站视图、原版右键菜单
├─ Themes/                      Styles.xaml(尺寸/样式)、Glyphs.xaml(Segoe Fluent Icons 码点)
└─ Helpers/                     DispatcherQueue 异步封装、值转换器

设计要点:

  1. 接口先冻结再并行开发:IFileIndex / IIconService / IFileOperationService / IFileSystemService 先定义, 索引、图标、操作三个模块独立实现(互不依赖),最后由 AppServices 装配。
  2. UI 线程零阻塞:所有文件系统交互都是异步 + 可取消;集合更新经 DispatcherQueue 分批提交。
  3. 后台对象不直接绑 UI:作业在后台线程更新,界面绑的是 250 ms 刷新的 UI 线程快照(JobRowViewModel)。
  4. 失败降级而不是崩溃:假索引不可用时搜索自动退化;外壳取图失败返回 null 并回退到扩展名图标;启动异常写入 startup.log。

4. 搜索语法(对齐 Everything 习惯)

keyword            名称包含(大小写不敏感,多个词 = AND)
"两个 词"          带空格的短语
!keyword           排除
*.json / pre*      通配符
ext:log;txt        扩展名(可多值)
size:>100mb        大小(kb/mb/gb,支持 > < =)
dm:today / dm:7d   修改时间(today/yesterday/thisweek/thismonth/thisyear/Nd/Nh/Nw 或具体日期)
dc:today           创建时间(需要索引提供创建时间,当前版本未启用)
folder: / file:    只看文件夹 / 只看文件
path:Windows       在完整路径中匹配

5. 验证情况(都基于本机实测,不采信"应该能用")

实测通过

  • 工程编译:0 error;XAML 全部编译为 xbf(含主题字典)。
  • 端到端启动:进程存活、窗口标题「文件资源管理器」、UI Automation 树确认——标签栏在标题栏区域内(系统按钮区已正确让位)、 命令栏 12 个按钮、地址栏(后退/前进/上级/刷新 + 2 段面包屑 + 搜索框)、导航窗格 16 个节点、 详情视图 4 个列头(名称/修改日期/类型/大小)、真实列出 107 行文件、状态栏与操作队列入口在位。
  • 搜索:通过 UI Automation 向搜索框写入关键字,界面返回结果行并显示「N 项结果 · X 毫秒 · 数据来源」。
  • 文件操作引擎(独立探针,62/62 断言 PASS):300 MB 复制的字节级进度;暂停 1 秒增长 0 字节;取消保留已完成部分; 同卷移动 1000 文件 15 ms(零字节流量);KeepBoth/Replace/Skip/Ask+ApplyToAll 全部正确; 308→312 字符长路径复制/移动成功;跨卷移动可撤销;永久删除不入撤销栈。
  • NTFS 索引(独立探针,走生产代码路径):20 万条目合成数据集 77 字节/条; 查询 *.json 3.3 ms、ext:log 1.4 ms、size:>100mb 0.66 ms、dm:today 0.3 ms、path:Windows 9.3 ms; 2000 条路径还原 1.66 ms;增量(创建/改名/删除)与墓碑正确;大小未知的条目不会被 size: 误判为 0 字节。 过程中修掉一个会静默丢 64% 结果的扩容 bug(名字池被换新导致老记录名字失效)。
  • 图标:.txt/.exe/目录/驱动器 均取到系统原生图标(32×32 原生尺寸、真 alpha);shell:RecycleBinFolder 等外壳对象正常; 图片缩略图 256×160 正常;1000 次扩展名取图 86 ms;300 次取图后 GDI/USER 句柄零增长。

已知限制(不隐瞒)

  • 当前会话没有管理员权限:CreateFile(@"\\.\C:") 被拒(FSCTL_* 返回 ERROR_INVALID_FUNCTION), 因此 NTFS 索引在本机本次验证中走的是 RequiresElevation 分支;界面状态栏会提示并可一键「以管理员身份重启」。 索引本身的正确性用同一份生产代码 + 合成数据集验证(见上)。要拿到真实全盘索引,请以管理员身份运行一次。
  • 非管理员降级模式下文件大小全部为"未知"(USN 记录本身不含 size,真实大小依赖原始 MFT 解析),此时 size: 过滤会偏宽松。
  • 拖放到其他应用程序、拖出标签新建窗口、网络邻居枚举为简化实现;图库依赖索引(无索引时给提示而非空列表)。
  • 缩略图/图标语义、字体字形只做了"码点在字体中存在"的校验(60/60 存在,不会出现方框), 但字形语义未经人眼确认(本会话的模型不能读图),个别按钮图标若观感不佳可直接改 Themes/Glyphs.xaml 里的码点。

6. 复现验证

# 编译
dotnet build E:\deepseek\FluidExplorer\FluidExplorer.csproj -c Debug

# 启动 + UI 自动化结构检查 + 截图(截图落在 E:\deepseek\artifacts\shell_window.png)
powershell -NoProfile -ExecutionPolicy Bypass -File E:\deepseek\tools\smoke_test.ps1

# 字形存在性校验(Segoe Fluent Icons)
powershell -NoProfile -ExecutionPolicy Bypass -File E:\deepseek\tools\check_glyphs.ps1