.net FolderBrowser对话框:保持树和文本字段同步

.net FolderBrowser对话框:保持树和文本字段同步,.net,wpf,winapi,.net,Wpf,Winapi,由于WPF在10多年后仍然缺少一个文件夹浏览器对话框,我不想只为这个对话框设置对System.Windows.Forms的引用,我在谷歌上搜索了一下,找到了一个(我一直认为)适合我需要的好的源代码 代码如下: 土特产: Class NativeMethods <DllImport("shell32.dll")> Public Shared Function SHCreateShellItem(pidlParent As IntPtr, psfParent As In

由于WPF在10多年后仍然缺少一个文件夹浏览器对话框,我不想只为这个对话框设置对System.Windows.Forms的引用,我在谷歌上搜索了一下,找到了一个(我一直认为)适合我需要的好的源代码

代码如下:

土特产:

Class NativeMethods

    <DllImport("shell32.dll")>
    Public Shared Function SHCreateShellItem(pidlParent As IntPtr, psfParent As IntPtr, pidl As IntPtr, ByRef ppsi As IShellItem) As Integer
    End Function

    <DllImport("shell32.dll")>
    Public Shared Function SHParseDisplayName(<MarshalAs(UnmanagedType.LPWStr)> ByVal pszPath As String, ByVal bindingContext As IntPtr, <Out()> ByRef pidl As IntPtr, ByVal sfgaoIn As UInteger, <Out()> ByRef sfgaoOut As UInteger) As Integer
    End Function

End Class

<ComImport, Guid("43826D1E-E718-42EE-BC55-A1E261C37BFE"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)>
Friend Interface IShellItem
    Sub BindToHandler(<[In], MarshalAs(UnmanagedType.[Interface])> pbc As IntPtr, <[In]> ByRef bhid As Guid, <[In]> ByRef riid As Guid, ByRef ppv As IntPtr)
    Sub GetParent(<MarshalAs(UnmanagedType.[Interface])> ByRef ppsi As IShellItem)
    Sub GetDisplayName(<[In]> sigdnName As SIGDN, <MarshalAs(UnmanagedType.LPWStr)> ByRef ppszName As String)
    Sub GetAttributes(<[In]> sfgaoMask As UInteger, ByRef psfgaoAttribs As UInteger)
    Sub Compare(<[In], MarshalAs(UnmanagedType.[Interface])> psi As IShellItem, <[In]> hint As UInteger, ByRef piOrder As Integer)
End Interface

<ComImport, Guid("B63EA76D-1F85-456F-A19C-48159EFA858B"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)>
Friend Interface IShellItemArray
    Sub BindToHandler(<[In], MarshalAs(UnmanagedType.[Interface])> pbc As IntPtr, <[In]> ByRef rbhid As Guid, <[In]> ByRef riid As Guid, ByRef ppvOut As IntPtr)
    Sub GetPropertyStore(<[In]> Flags As Integer, <[In]> ByRef riid As Guid, ByRef ppv As IntPtr)
    Sub GetPropertyDescriptionList(<[In], MarshalAs(UnmanagedType.Struct)> ByRef keyType As IntPtr, <[In]> ByRef riid As Guid, ByRef ppv As IntPtr)
    Sub GetAttributes(<[In], MarshalAs(UnmanagedType.I4)> dwAttribFlags As IntPtr, <[In]> sfgaoMask As UInteger, ByRef psfgaoAttribs As UInteger)
    Sub GetCount(ByRef pdwNumItems As UInteger)
    Sub GetItemAt(<[In]> dwIndex As UInteger, <MarshalAs(UnmanagedType.[Interface])> ByRef ppsi As IShellItem)
    Sub EnumItems(<MarshalAs(UnmanagedType.[Interface])> ByRef ppenumShellItems As IntPtr)
End Interface

<ComImport, Guid("d57c7288-d4ad-4768-be02-9d969532d960"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown), CoClass(GetType(FileOpenDialog))>
Friend Interface IFileOpenDialog
    <PreserveSig>
    Function Show(<[In]> parent As IntPtr) As Integer
    Sub SetFileTypes(<[In]> cFileTypes As UInteger, <[In], MarshalAs(UnmanagedType.Struct)> ByRef rgFilterSpec As IntPtr)
    Sub SetFileTypeIndex(<[In]> iFileType As UInteger)
    Sub GetFileTypeIndex(ByRef piFileType As UInteger)
    Sub Advise(<[In], MarshalAs(UnmanagedType.[Interface])> pfde As IntPtr, ByRef pdwCookie As UInteger)
    Sub Unadvise(<[In]> dwCookie As UInteger)
    Sub SetOptions(<[In]> fos As FOS)
    Sub GetOptions(ByRef pfos As FOS)
    Sub SetDefaultFolder(<[In], MarshalAs(UnmanagedType.[Interface])> psi As IShellItem)
    Sub SetFolder(<[In], MarshalAs(UnmanagedType.[Interface])> psi As IShellItem)
    Sub GetFolder(<MarshalAs(UnmanagedType.[Interface])> ByRef ppsi As IShellItem)
    Sub GetCurrentSelection(<MarshalAs(UnmanagedType.[Interface])> ByRef ppsi As IShellItem)
    Sub SetFileName(<[In], MarshalAs(UnmanagedType.LPWStr)> pszName As String)
    Sub GetFileName(<MarshalAs(UnmanagedType.LPWStr)> ByRef pszName As String)
    Sub SetTitle(<[In], MarshalAs(UnmanagedType.LPWStr)> pszTitle As String)
    Sub SetOkButtonLabel(<[In], MarshalAs(UnmanagedType.LPWStr)> pszText As String)
    Sub SetFileNameLabel(<[In], MarshalAs(UnmanagedType.LPWStr)> pszLabel As String)
    Sub GetResult(<MarshalAs(UnmanagedType.[Interface])> ByRef ppsi As IShellItem)
    Sub AddPlace(<[In], MarshalAs(UnmanagedType.[Interface])> psi As IShellItem, fdcp As Object)
    Sub SetDefaultExtension(<[In], MarshalAs(UnmanagedType.LPWStr)> pszDefaultExtension As String)
    Sub Close(<MarshalAs(UnmanagedType.[Error])> hr As Integer)
    Sub SetClientGuid(<[In]> ByRef guid As Guid)
    Sub ClearClientData()
    Sub SetFilter(<MarshalAs(UnmanagedType.[Interface])> pFilter As IntPtr)
    Sub GetResults(<MarshalAs(UnmanagedType.[Interface])> ByRef ppenum As IShellItemArray)
    Sub GetSelectedItems(<MarshalAs(UnmanagedType.[Interface])> ByRef ppsai As IShellItemArray)
End Interface

<ComImport, Guid("DC1C5A9C-E88A-4dde-A5A1-60F82A20AEF7")>
Class FileOpenDialog
End Class

Enum SIGDN As UInteger
    DESKTOPABSOLUTEEDITING = &H8004C000UI
    DESKTOPABSOLUTEPARSING = &H80028000UI
    FILESYSPATH = &H80058000UI
    NORMALDISPLAY = 0
    PARENTRELATIVE = &H80080001UI
    PARENTRELATIVEEDITING = &H80031001UI
    PARENTRELATIVEFORADDRESSBAR = &H8007C001UI
    PARENTRELATIVEPARSING = &H80018001UI
    URL = &H80068000UI
End Enum

<Flags>
Enum FOS
    ALLNONSTORAGEITEMS = &H80
    ALLOWMULTISELECT = &H200
    CREATEPROMPT = &H2000
    DEFAULTNOMINIMODE = &H20000000
    DONTADDTORECENT = &H2000000
    FILEMUSTEXIST = &H1000
    FORCEFILESYSTEM = &H40
    FORCESHOWHIDDEN = &H10000000
    HIDEMRUPLACES = &H20000
    HIDEPINNEDPLACES = &H40000
    NOCHANGEDIR = 8
    NODEREFERENCELINKS = &H100000
    NOREADONLYRETURN = &H8000
    NOTESTFILECREATE = &H10000
    NOVALIDATE = &H100
    OVERWRITEPROMPT = 2
    PATHMUSTEXIST = &H800
    PICKFOLDERS = &H20
    SHAREAWARE = &H4000
    STRICTFILETYPES = 4
End Enum
…显示文件夹浏览器对话框,对话框顶部的文本字段设置为我的初始文件夹
C:\Windows\System32
,但对话框左侧的树视图仅设置为drive
C:
。因此,当我想用鼠标选择初始文件夹的子文件夹时,我需要先单击几下导航到初始文件夹,然后才能选择我真正喜欢的文件夹

有没有办法保持对话框的文本字段和树视图同步


我知道有像ookii.dialogs这样的第三方工具,但我想控制大部分代码。

如果添加对System.Windows.Forms的引用,那又有什么关系呢?如果安装了.NET Framework,它始终是可用的,因此您不妨使用它。因为它可能会向内存中添加许多额外的库,如果没有必要,我希望避免这种情况
Public NotInheritable Class FolderBrowserDialog
    Private _selectedPath As String
    Private _selectedElementName As String
    Private _selectedPaths As String()
    Private _selectedElementNames As String()
    Private _allowNonStoragePlaces As Boolean
    Private _multiSelect As Boolean

    Public Property SelectedPath As String
        Get
            Return _selectedPath
        End Get
        Set(ByVal value As String)
            _selectedPath = value
        End Set
    End Property

    Public Property SelectedElementName As String
        Get
            Return _selectedElementName
        End Get
        Private Set(value As String)
            _selectedElementName = value
        End Set
    End Property

    Public Property SelectedPaths As String()
        Get
            Return _selectedPaths
        End Get
        Private Set(value As String())
            _selectedPaths = value
        End Set
    End Property

    Public Property SelectedElementNames As String()
        Get
            Return _selectedElementNames
        End Get
        Private Set(value As String())
            _selectedElementNames = value
        End Set
    End Property

    Public Property AllowNonStoragePlaces As Boolean
        Get
            Return _allowNonStoragePlaces
        End Get
        Set(ByVal value As Boolean)
            _allowNonStoragePlaces = value
        End Set
    End Property

    Public Property MultiSelect As Boolean
        Get
            Return _multiSelect
        End Get
        Set(ByVal value As Boolean)
            _multiSelect = value
        End Set
    End Property

    Public Function ShowDialog(ByVal owner As Window) As Boolean
        Return Me.ShowDialog(If(owner Is Nothing, IntPtr.Zero, New Interop.WindowInteropHelper(owner).Handle))
    End Function

    Public Function ShowDialog(ByVal owner As IntPtr) As Boolean
        Dim dialog As IFileOpenDialog

        If (Environment.OSVersion.Version.Major < 6) Then
            Throw New InvalidOperationException("The dialog needs at least Windows Vista to work.")
        End If

        dialog = Me.CreateNativeDialog

        Try
            Call Me.SetInitialFolder(dialog)
            Call Me.SetOptions(dialog)

            If (dialog.Show(owner) <> 0) Then
                Return False
            End If

            Call Me.SetDialogResults(dialog)

            Return True
        Finally
            Marshal.ReleaseComObject(dialog)
        End Try
    End Function

    Private Function CreateNativeDialog() As IFileOpenDialog
        Return DirectCast(New FileOpenDialog, IFileOpenDialog)
    End Function

    Private Sub SetInitialFolder(ByVal dialog As IFileOpenDialog)
        Dim item As IShellItem = Nothing
        Dim idl As IntPtr
        Dim atts As UInteger

        If Not String.IsNullOrEmpty(Me.SelectedPath) Then
            If (NativeMethods.SHParseDisplayName(Me.SelectedPath, IntPtr.Zero, idl, 0, atts) = 0) AndAlso (NativeMethods.SHCreateShellItem(IntPtr.Zero, IntPtr.Zero, idl, item) = 0) Then
                dialog.SetDefaultFolder(item)
            End If
        End If
    End Sub

    Private Sub SetOptions(ByVal dialog As IFileOpenDialog)
        dialog.SetOptions(Me.GetDialogOptions)
    End Sub

    Private Function GetDialogOptions() As FOS
        Dim options As FOS

        options = FOS.PICKFOLDERS
        If Me.MultiSelect Then
            options = options Or FOS.ALLOWMULTISELECT
        End If

        If Not Me.AllowNonStoragePlaces Then
            options = options Or FOS.FORCEFILESYSTEM
        End If

        Return options
    End Function

    Private Sub SetDialogResults(ByVal dialog As IFileOpenDialog)
        Dim item As IShellItem = Nothing
        Dim path As String = Nothing
        Dim value As String = Nothing

        If Not Me.MultiSelect Then
            dialog.GetResult(item)

            Call Me.GetPathAndElementName(item, path, value)

            Me.SelectedPath = path
            Me.SelectedPaths = New String() {path}
            Me.SelectedElementName = value
            Me.SelectedElementNames = New String() {value}
        Else
            Dim items As IShellItemArray = Nothing
            Dim count As UInteger

            dialog.GetResults(items)
            items.GetCount(count)

            Me.SelectedPaths = New String(count - 1) {}
            Me.SelectedElementNames = New String(count - 1) {}

            For i As UInteger = 0 To count - 1
                items.GetItemAt(i, item)

                Call Me.GetPathAndElementName(item, path, value)
                Me.SelectedPaths(i) = path
                Me.SelectedElementNames(i) = value
            Next i

            Me.SelectedPath = Nothing
            Me.SelectedElementName = Nothing
        End If
    End Sub

    Private Sub GetPathAndElementName(ByVal item As IShellItem, ByRef path As String, ByRef elementName As String)
        item.GetDisplayName(SIGDN.PARENTRELATIVEFORADDRESSBAR, elementName)

        Try
            item.GetDisplayName(SIGDN.FILESYSPATH, path)
        Catch ex As ArgumentException When (ex.HResult = -2147024809)
            path = Nothing
        End Try
    End Sub

End Class
    Dim dialog As FolderBrowserDialog

    dialog = New FolderBrowserDialog
    dialog.SelectedPath = "C:\Windows\System32"

    If dialog.ShowDialog(Me) Then
        MsgBox(String.Join(", ", dialog.SelectedPaths))
    End If