WPF.NET没有原生的文件夹选择对话框,只能“借用”为其它框架设计的文件夹选择对话框。在前UWP时代,WPF通常需要借用Windows窗体的FolderBrowserDialog,但这个对话框的缺点是只能选择单个文件夹,不支持多选。后来UWP提供了原生的FolderPicker,支持包括文件夹多选等更多丰富功能。对于新开发的应用,我们应当尽量使用FolderPicker,弃用FolderBrowserDialog。
但是,在WPF中使用FolderPicker并不像原本使用FolderBrowserDialog那样,只要在项目属性中启用Windows窗体就可以直接使用。首先,你需要在项目属性中设置受支持的OS版本:
建议对新开发的应用至少选择19041以上版本,除非特别需要支持旧版系统。完成设置后,你应当能在代码中使用Windows.Storage命名空间了。
Imports Windows.Storage
Class MainWindow
'''
ReadOnly 目录浏览对话框 As New Pickers.FolderPicker
'''
End Class
但在WPF中,仅仅这样做是不够的。如果你直接调用其PickSingleFolderAsync方法,将会遇到如下错误:
System.Runtime.InteropServices.COMException:“Invalid window handle. (0x80070578)
Consider WindowNative, InitializeWithWindow
See https://aka.ms/cswinrt/interop#windows-sdk”
这是因为,WPF要求对FolderPicker实行更复杂的初始化:
Private Sub MainWindow_Loaded(sender As Object, e As RoutedEventArgs) Handles Me.Loaded
'只能在Loaded中初始化,因为构造阶段窗口还没有句柄
WinRT.Interop.InitializeWithWindow.Initialize(目录浏览对话框, New System.Windows.Interop.WindowInteropHelper(Me).Handle)
'Win10中必须添加FileTypeFilter,Win11中为可选添加。
目录浏览对话框.FileTypeFilter.Add("*")
End Sub
这个初始化方法来自官方文档:
从 .NET 应用调用互操作 APIhttps://learn.microsoft.com/zh-cn/windows/apps/desktop/modernize/winrt-com-interop-csharp此方法因为要获取WPF应用的窗口句柄,所以必须在窗口建立后才能调用,而不能在窗口类的构造方法中调用。此外,示例代码中设置了FileTypeFilter,测试发现此设置在Win10中为必需,Win11则不需要,但设置了也不会出错。
完成初始化后,可以持久保留FolderPicker对象以便重复多次使用,不需要重新设置。典型的使用方法如下:
Dim 目录 As StorageFolder = Await 目录浏览对话框.PickSingleFolderAsync
If 目录 IsNot Nothing Then
'''
End If
此代码要求其所在的方法声明为Async。如果用户没有选择任何文件夹就关闭了窗口,将会返回Nothing;否则将会返回一个StorageFolder对象,你可以进行任何后续操作。