FolderBrowser with textbox in VB.NET
Asked Answered
L

2

12

I have a textBox which gets filled with a path, acquired from a database (but that's irrelevant). So I want to have a FolderBrowserDialog button where I open -> navigate through the folders and select a path (i.e. by selecting a certain folder) and then OK/Save-ing this path and it to be updated in the textBox.

So in the whole case - I don't know how to use the OpenFolderBrowserDialog, because when I drag it - it only goes in the bottom of the page, but I want to place it as a button on a certain place in my WinForm. And also - how is the selected path saved into a (i.e. string) variable so I can set it as the value of the textbox? A certain method?

Lisalisabet answered 16/4, 2013 at 12:16 Comment(6)
@Chad - this answer has an equivalent code in C# I believe (FolderBrowser2): https://mcmap.net/q/82485/-show-detailed-folder-browser-from-a-propertygridLecompte
Simon Mourier's definition of the Shell interfaces and functions target C# style and notation better than an on-line converter can do. If the converter you used has translated as-is to C# the ref keyword, change the all to out, except when there's also an [In] parameter (usually a Guid). The <MethodImpl> decoration is not needed, as almost all the unmanaged Marshaling, except for strings, since it probably better to marshal them in the method call than to use Marshal.PtrToStringAuto(string) after.Arst
@Chad The Interface definition you find in Simons Mourier's answer is partial, while here it's complete (and also correct, though VB.Net style), so you may want to integrate what's missing in that code, following the same pattern -- Note that specifying FOS_ALLOWMULTISELECT instead of FOS_PICKFOLDERS (in relation to the code you see here), you have a FileDialog that allows selections of both files and directories. It's somewhat more difficult to implement, but may be worth it.Arst
@jimi - It's partial on purpose, it doesn't mean it does no work. What's "missing" that would be needed?Lecompte
@Simon Mourier What's "missing" that would be needed? In this limited use case, nothing. If Chad is simply looking for a C# translation, then it's all that's needed. There's no real purpose, though, in keeping the implementation partial, no advantage whatsoever. Since I know that the Shell Interfaces (what you can do with them) can become quite addictive (IShellItem/IShellItem2 are major contributors to the addiction), Chad may want to complete the implementation(s), should curiosity take over. Anyway, my comments are simple information, as is the note about FOS_ALLOWMULTISELECT.Arst
@Simon Mourier P.S. I Know that the implementation of GetAttributes() (for example) requires the definition of another enumerator, which has a number of values, but it's also a quite interesting one, since it's used elsewhere and quite often. It's not directly used in this context (the creation of the Dialog itself), but it could be useful later, if/when extended details about the files become a requirement or an improvement.Arst
S
35

Try like below it will help you..

Place a Text box, Button and FolderBrowserDialog in your Form like below...

enter image description here

Then Double click the button and create Button click Event on your code like below..

Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
    If (FolderBrowserDialog1.ShowDialog() = DialogResult.OK) Then
        TextBox1.Text = FolderBrowserDialog1.SelectedPath
    End If
End Sub

Then run it.. Now you can open the Folder Browser dialog by clicking the Browse Button..

After selecting the folder Path the path will assigned to Text Box

You can also refer this Article for full reference : FolderBrowserDialog

Sabinasabine answered 16/4, 2013 at 12:28 Comment(1)
Do pay attention to the return value of ShowDialog().Thinia
K
5

Here is a better version. Put this code in a module

Imports System
Imports System.IO
Imports System.Runtime.CompilerServices
Imports System.Runtime.InteropServices
Imports System.Windows.Forms

Public Class OpenFolderDialog
    Implements IDisposable

    ''' <summary>
    ''' Gets/sets folder in which dialog will be open.
    ''' </summary>
    Public Property InitialFolder() As String
        Get
            Return m_InitialFolder
        End Get
        Set(ByVal value As String)
            m_InitialFolder = value
        End Set
    End Property
    Private m_InitialFolder As String

    ''' <summary>
    ''' Gets/sets directory in which dialog will be open if there is no recent directory available.
    ''' </summary>
    Public Property DefaultFolder() As String
        Get
            Return m_DefaultFolder
        End Get
        Set(ByVal value As String)
            m_DefaultFolder = value
        End Set
    End Property
    Private m_DefaultFolder As String

    ''' <summary>
    ''' Gets selected folder.
    ''' </summary>
    Public Property Folder() As String
        Get
            Return m_Folder
        End Get
        Private Set(ByVal value As String)
            m_Folder = value
        End Set
    End Property
    Private m_Folder As String


    Public Function ShowDialog(ByVal owner As IWin32Window) As DialogResult
        If Environment.OSVersion.Version.Major >= 6 Then
            Return ShowVistaDialog(owner)
        Else
            Return ShowLegacyDialog(owner)
        End If
    End Function

    Private Function ShowVistaDialog(ByVal owner As IWin32Window) As DialogResult
        Dim frm = DirectCast(New NativeMethods.FileOpenDialogRCW(), NativeMethods.IFileDialog)
        Dim options As UInteger
        frm.GetOptions(options)
        options = options Or NativeMethods.FOS_PICKFOLDERS Or NativeMethods.FOS_FORCEFILESYSTEM Or NativeMethods.FOS_NOVALIDATE Or NativeMethods.FOS_NOTESTFILECREATE Or NativeMethods.FOS_DONTADDTORECENT
        frm.SetOptions(options)
        If Me.InitialFolder IsNot Nothing Then
            Dim directoryShellItem As NativeMethods.IShellItem
            Dim riid = New Guid("43826D1E-E718-42EE-BC55-A1E261C37BFE")
            'IShellItem
            If NativeMethods.SHCreateItemFromParsingName(Me.InitialFolder, IntPtr.Zero, riid, directoryShellItem) = NativeMethods.S_OK Then
                frm.SetFolder(directoryShellItem)
            End If
        End If
        If Me.DefaultFolder IsNot Nothing Then
            Dim directoryShellItem As NativeMethods.IShellItem
            Dim riid = New Guid("43826D1E-E718-42EE-BC55-A1E261C37BFE")
            'IShellItem
            If NativeMethods.SHCreateItemFromParsingName(Me.DefaultFolder, IntPtr.Zero, riid, directoryShellItem) = NativeMethods.S_OK Then
                frm.SetDefaultFolder(directoryShellItem)
            End If
        End If

        If frm.Show(owner.Handle) = NativeMethods.S_OK Then
            Dim shellItem As NativeMethods.IShellItem
            If frm.GetResult(shellItem) = NativeMethods.S_OK Then
                Dim pszString As IntPtr
                If shellItem.GetDisplayName(NativeMethods.SIGDN_FILESYSPATH, pszString) = NativeMethods.S_OK Then
                    If pszString <> IntPtr.Zero Then
                        Try
                            Me.Folder = Marshal.PtrToStringAuto(pszString)
                            Return DialogResult.OK
                        Finally
                            Marshal.FreeCoTaskMem(pszString)
                        End Try
                    End If
                End If
            End If
        End If
        Return DialogResult.Cancel
    End Function

    Private Function ShowLegacyDialog(ByVal owner As IWin32Window) As DialogResult
        Using frm = New SaveFileDialog()
            frm.CheckFileExists = False
            frm.CheckPathExists = True
            frm.CreatePrompt = False
            frm.Filter = "|" & Guid.Empty.ToString()
            frm.FileName = "any"
            If Me.InitialFolder IsNot Nothing Then
                frm.InitialDirectory = Me.InitialFolder
            End If
            frm.OverwritePrompt = False
            frm.Title = "Select Folder"
            frm.ValidateNames = False
            If frm.ShowDialog(owner) = DialogResult.OK Then
                Me.Folder = Path.GetDirectoryName(frm.FileName)
                Return DialogResult.OK
            Else
                Return DialogResult.Cancel
            End If
        End Using
    End Function


    Public Sub Dispose() Implements IDisposable.Dispose
    End Sub
    'just to have possibility of Using statement.
End Class

Friend Module NativeMethods


#Region "Constants"

    Public Const FOS_PICKFOLDERS As UInteger = &H20
    Public Const FOS_FORCEFILESYSTEM As UInteger = &H40
    Public Const FOS_NOVALIDATE As UInteger = &H100
    Public Const FOS_NOTESTFILECREATE As UInteger = &H10000
    Public Const FOS_DONTADDTORECENT As UInteger = &H2000000

    Public Const S_OK As UInteger = &H0

    Public Const SIGDN_FILESYSPATH As UInteger = &H80058000UI

#End Region


#Region "COM"

    <ComImport(), ClassInterface(ClassInterfaceType.None), TypeLibType(TypeLibTypeFlags.FCanCreate), Guid("DC1C5A9C-E88A-4DDE-A5A1-60F82A20AEF7")> _
    Friend Class FileOpenDialogRCW
    End Class


    <ComImport(), Guid("42F85136-DB7E-439C-85F1-E4075D135FC8"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)> _
    Friend Interface IFileDialog
        <MethodImpl(MethodImplOptions.InternalCall, MethodCodeType:=MethodCodeType.Runtime)> _
        <PreserveSig()> _
        Function Show(<[In](), [Optional]()> ByVal hwndOwner As IntPtr) As UInteger
        'IModalWindow 

        <MethodImpl(MethodImplOptions.InternalCall, MethodCodeType:=MethodCodeType.Runtime)> _
        Function SetFileTypes(<[In]()> ByVal cFileTypes As UInteger, <[In](), MarshalAs(UnmanagedType.LPArray)> ByVal rgFilterSpec As IntPtr) As UInteger

        <MethodImpl(MethodImplOptions.InternalCall, MethodCodeType:=MethodCodeType.Runtime)> _
        Function SetFileTypeIndex(<[In]()> ByVal iFileType As UInteger) As UInteger

        <MethodImpl(MethodImplOptions.InternalCall, MethodCodeType:=MethodCodeType.Runtime)> _
        Function GetFileTypeIndex(ByRef piFileType As UInteger) As UInteger

        <MethodImpl(MethodImplOptions.InternalCall, MethodCodeType:=MethodCodeType.Runtime)> _
        Function Advise(<[In](), MarshalAs(UnmanagedType.[Interface])> ByVal pfde As IntPtr, ByRef pdwCookie As UInteger) As UInteger

        <MethodImpl(MethodImplOptions.InternalCall, MethodCodeType:=MethodCodeType.Runtime)> _
        Function Unadvise(<[In]()> ByVal dwCookie As UInteger) As UInteger

        <MethodImpl(MethodImplOptions.InternalCall, MethodCodeType:=MethodCodeType.Runtime)> _
        Function SetOptions(<[In]()> ByVal fos As UInteger) As UInteger

        <MethodImpl(MethodImplOptions.InternalCall, MethodCodeType:=MethodCodeType.Runtime)> _
        Function GetOptions(ByRef fos As UInteger) As UInteger

        <MethodImpl(MethodImplOptions.InternalCall, MethodCodeType:=MethodCodeType.Runtime)> _
        Sub SetDefaultFolder(<[In](), MarshalAs(UnmanagedType.[Interface])> ByVal psi As IShellItem)

        <MethodImpl(MethodImplOptions.InternalCall, MethodCodeType:=MethodCodeType.Runtime)> _
        Function SetFolder(<[In](), MarshalAs(UnmanagedType.[Interface])> ByVal psi As IShellItem) As UInteger

        <MethodImpl(MethodImplOptions.InternalCall, MethodCodeType:=MethodCodeType.Runtime)> _
        Function GetFolder(<MarshalAs(UnmanagedType.[Interface])> ByRef ppsi As IShellItem) As UInteger

        <MethodImpl(MethodImplOptions.InternalCall, MethodCodeType:=MethodCodeType.Runtime)> _
        Function GetCurrentSelection(<MarshalAs(UnmanagedType.[Interface])> ByRef ppsi As IShellItem) As UInteger

        <MethodImpl(MethodImplOptions.InternalCall, MethodCodeType:=MethodCodeType.Runtime)> _
        Function SetFileName(<[In](), MarshalAs(UnmanagedType.LPWStr)> ByVal pszName As String) As UInteger

        <MethodImpl(MethodImplOptions.InternalCall, MethodCodeType:=MethodCodeType.Runtime)> _
        Function GetFileName(<MarshalAs(UnmanagedType.LPWStr)> ByRef pszName As String) As UInteger

        <MethodImpl(MethodImplOptions.InternalCall, MethodCodeType:=MethodCodeType.Runtime)> _
        Function SetTitle(<[In](), MarshalAs(UnmanagedType.LPWStr)> ByVal pszTitle As String) As UInteger

        <MethodImpl(MethodImplOptions.InternalCall, MethodCodeType:=MethodCodeType.Runtime)> _
        Function SetOkButtonLabel(<[In](), MarshalAs(UnmanagedType.LPWStr)> ByVal pszText As String) As UInteger

        <MethodImpl(MethodImplOptions.InternalCall, MethodCodeType:=MethodCodeType.Runtime)> _
        Function SetFileNameLabel(<[In](), MarshalAs(UnmanagedType.LPWStr)> ByVal pszLabel As String) As UInteger

        <MethodImpl(MethodImplOptions.InternalCall, MethodCodeType:=MethodCodeType.Runtime)> _
        Function GetResult(<MarshalAs(UnmanagedType.[Interface])> ByRef ppsi As IShellItem) As UInteger

        <MethodImpl(MethodImplOptions.InternalCall, MethodCodeType:=MethodCodeType.Runtime)> _
        Function AddPlace(<[In](), MarshalAs(UnmanagedType.[Interface])> ByVal psi As IShellItem, ByVal fdap As UInteger) As UInteger

        <MethodImpl(MethodImplOptions.InternalCall, MethodCodeType:=MethodCodeType.Runtime)> _
        Function SetDefaultExtension(<[In](), MarshalAs(UnmanagedType.LPWStr)> ByVal pszDefaultExtension As String) As UInteger

        <MethodImpl(MethodImplOptions.InternalCall, MethodCodeType:=MethodCodeType.Runtime)> _
        Function Close(<MarshalAs(UnmanagedType.[Error])> ByVal hr As UInteger) As UInteger

        <MethodImpl(MethodImplOptions.InternalCall, MethodCodeType:=MethodCodeType.Runtime)> _
        Function SetClientGuid(<[In]()> ByRef guid As Guid) As UInteger

        <MethodImpl(MethodImplOptions.InternalCall, MethodCodeType:=MethodCodeType.Runtime)> _
        Function ClearClientData() As UInteger

        <MethodImpl(MethodImplOptions.InternalCall, MethodCodeType:=MethodCodeType.Runtime)> _
        Function SetFilter(<MarshalAs(UnmanagedType.[Interface])> ByVal pFilter As IntPtr) As UInteger
    End Interface


    <ComImport(), Guid("43826D1E-E718-42EE-BC55-A1E261C37BFE"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)> _
    Friend Interface IShellItem
        <MethodImpl(MethodImplOptions.InternalCall, MethodCodeType:=MethodCodeType.Runtime)> _
        Function BindToHandler(<[In]()> ByVal pbc As IntPtr, <[In]()> ByRef rbhid As Guid, <[In]()> ByRef riid As Guid, <Out(), MarshalAs(UnmanagedType.[Interface])> ByRef ppvOut As IntPtr) As UInteger

        <MethodImpl(MethodImplOptions.InternalCall, MethodCodeType:=MethodCodeType.Runtime)> _
        Function GetParent(<MarshalAs(UnmanagedType.[Interface])> ByRef ppsi As IShellItem) As UInteger

        <MethodImpl(MethodImplOptions.InternalCall, MethodCodeType:=MethodCodeType.Runtime)> _
        Function GetDisplayName(<[In]()> ByVal sigdnName As UInteger, ByRef ppszName As IntPtr) As UInteger

        <MethodImpl(MethodImplOptions.InternalCall, MethodCodeType:=MethodCodeType.Runtime)> _
        Function GetAttributes(<[In]()> ByVal sfgaoMask As UInteger, ByRef psfgaoAttribs As UInteger) As UInteger

        <MethodImpl(MethodImplOptions.InternalCall, MethodCodeType:=MethodCodeType.Runtime)> _
        Function Compare(<[In](), MarshalAs(UnmanagedType.[Interface])> ByVal psi As IShellItem, <[In]()> ByVal hint As UInteger, ByRef piOrder As Integer) As UInteger
    End Interface

#End Region

    <DllImport("shell32.dll", CharSet:=CharSet.Unicode, PreserveSig:=False)> _
     Public Function SHCreateItemFromParsingName( _
         <MarshalAs(UnmanagedType.LPWStr)> ByVal pszPath As String, _
         ByVal pbc As IntPtr, _
         <MarshalAs(UnmanagedType.LPStruct)> ByVal riid As Guid, _
         <MarshalAs(UnmanagedType.Interface, IidParameterIndex:=2)> ByRef ppv As IShellItem) As Integer
    End Function
End Module

Use it like this

Using frm = New OpenFolderDialog()
                If frm.ShowDialog(Me) = DialogResult.OK Then
                    MessageBox.Show(Me, frm.Folder)
                End If
            End Using
Kaltman answered 24/4, 2018 at 13:27 Comment(5)
Can you explain why this version is better? Or superior?Counterforce
@modu It's better because it's a better user experience. Unlike with the accepted answer, using the provided class in this answer will have the path bar present inside the dialog window. This allows you to paste a directory into the path bar and hit the enter key, and continue navigating exactly as you can do with the OpenFileDialog class. There are a few things this class could use though, such as a RootFolder property like FolderBrowserDialog contains.Pity
@Kaltman I can't seem to get this fully converted into C#. The telerik converter got me most of the way, but then I had to change SHCreateItemFromParsingName() from containing a body to simply ending with a ; and I had to remove the static keyword from const local members. Lastly, I had to initialize directoryShellItem and shellItem with values, respectively (these both needed to be null and pszString needed to be new IntPtr()). But when MyNamespace.NativeMethods() is called, I receive the following error:Pity
A call to PInvoke function 'MyProject!MyNamespace.NativeMethods::SHCreateItemFromParsingName' has unbalanced the stack. This is likely because the managed PInvoke signature does not match the unmanaged target signature. Check that the calling convention and parameters of the PInvoke signature match the target unmanaged signature.' I've added CallingConvention = CallingConvention.Cdecl to the [DllImport()] attribute as per this answer, but I still receive the error message.Pity
Oh and I forgot to mention I had to add extern to the SHCreateItemFromParsingName() definition's modifiers, as DllImport was yelling without it. Any ideas on getting this fully converted?Pity

© 2022 - 2024 — McMap. All rights reserved.