How to implement a managed component that wraps the Browse For Folder common dialog box by using Visual Basic .NET or Visual Basic 2005 (811004)



The information in this article applies to:

  • Microsoft Visual Basic .NET (2002)
  • Microsoft Visual Basic .NET (2003)
  • Microsoft Visual Basic 2005


For a Microsoft Visual C# .NET version of this article, see 306285.

This article refers to the following Microsoft .NET Framework Class Library namespaces:
  • System.Runtime.InteropServices
  • System.Text
  • System.Security.Permissions
The following file is available for download from the Microsoft Download Center:
Release Date: 12-12-2002

For additional information about how to download Microsoft Support files, click the following article number to view the article in the Microsoft Knowledge Base:

119591 How to Obtain Microsoft Support Files from Online Services

Microsoft scanned this file for viruses. Microsoft used the most current virus-detection software that was available on the date that the file was posted. The file is stored on security-enhanced servers that help to prevent any unauthorized changes to the file.
Microsoft scanned this file for viruses. Microsoft used the most current virus-detection software that was available on the date that the file was posted. The file is stored on secure servers that prevent any unauthorized changes to the file.

IN THIS TASK

SUMMARY

This step-by-step article describes how to write a design time component that wraps the Browse For Folder common dialog box.

Implement the Design-Time Component

  1. Generate an empty project. To do this, create a new blank solution and name it BrowseForFolder. Add a new Visual Basic .NET or Visual Basic 2005 Class Library project to the newly-created solution, and name the project WinFormsExtras.
  2. Modify the wizard-generated code. To do this, rename the Class1.vb file from the project FolderBrowser.vb, and then delete the Class1 class that the wizard adds to this file. Rename the namespace from WinFormsExtra.cs (WinFormsExtras) to Microsoft.Samples.WinForms.Extras. On the project's Properties pages, rename the default namespace from WinFormsExtras to Microsoft.Samples.WinForms.Extras. Change the assembly name from WinFormsExtras to Microsoft.Samples.WinForms.Extras.
  3. Add the declarations for P/Invoke and ComInterop. Make sure that System.Runtime.InteropServices and System.Text are referenced, and then provide declarations for the functions and interfaces, as follows:
    Imports System.Runtime.InteropServices
    Imports System.Text
    
        Class Win32API
            'Visual Basic representation of the IMalloc interface.
            <InterfaceType(ComInterfaceType.InterfaceIsIUnknown), Guid("00000002-0000-0000-C000-000000000046")> _
            Interface IMalloc
                <PreserveSig()> Function Alloc(ByVal cb As Integer) As IntPtr
                <PreserveSig()> Function Realloc(ByVal pv As IntPtr, ByVal cb As Integer) As IntPtr
                <PreserveSig()> Sub Free(ByVal pv As IntPtr)
                <PreserveSig()> Function GetSize(ByVal pv As IntPtr) As Integer
                <PreserveSig()> Function DidAlloc(ByVal pv As IntPtr) As Integer
                <PreserveSig()> Sub HeapMinimize()
            End Interface
    
            Public Declare Function GetActiveWindow Lib "user32" Alias "GetActiveWindow" () As IntPtr
    
            Public Class Shell32
                'Styles used in the BROWSEINFO.ulFlags field.
                <Flags()> Public Enum BffStyles As Integer
                    RestrictToFilesystem = &H1  ' BIF_RETURNONLYFSDIRS
                    RestrictToDomain = &H2      ' BIF_DONTGOBELOWDOMAIN
                    RestrictToSubfolders = &H8  ' BIF_RETURNFSANCESTORS
                    ShowTextBox = &H10          ' BIF_EDITBOX
                    ValidateSelection = &H20    ' BIF_VALIDATE
                    NewDialogStyle = &H40       ' BIF_NEWDIALOGSTYLE
                    BrowseForComputer = &H1000  ' BIF_BROWSEFORCOMPUTER
                    BrowseForPrinter = &H2000   ' BIF_BROWSEFORPRINTER
                    BrowseForEverything = &H4000 ' BIF_BROWSEINCLUDEFILES
                End Enum
    
                'Delegate type used in BROWSEINFO.lpfn field.
                Public Delegate Function BFFCALLBACK(ByVal hwnd As IntPtr, ByVal uMsg As UInt32, ByVal lParam As IntPtr, ByVal lpData As IntPtr) As Integer
    
                <StructLayout(LayoutKind.Sequential, Pack:=8)> _
                 Public Structure BROWSEINFO
                    Public hwndOwner As IntPtr
                    Public pidlRoot As IntPtr
                    Public pszDisplayName As IntPtr
                    <MarshalAs(UnmanagedType.LPStr)> _
                    Public lpszTitle As String
                    Public ulFlags As Integer
                    <MarshalAs(UnmanagedType.FunctionPtr)> _
                    Public lpfn As BFFCALLBACK
                    Public lParam As IntPtr
                    Public iImage As Integer
                End Structure
    
                Public Declare Function SHGetMalloc Lib "shell32" Alias "SHGetMalloc" (ByRef ppMalloc As IMalloc) As Integer
    
                Public Declare Function SHGetSpecialFolderLocation Lib "shell32" Alias "SHGetSpecialFolderLocation" (ByVal hwndOwner As IntPtr, ByVal nFolder As Integer, ByRef ppidl As IntPtr) As Integer
    
                Public Declare Function SHGetPathFromIDList Lib "shell32" Alias "SHGetPathFromIDList" (ByVal pidl As IntPtr, ByVal Path As StringBuilder) As Integer
    
                Public Declare Function SHBrowseForFolder Lib "shell32" Alias "SHBrowseForFolder" (ByRef bi As BROWSEINFO) As IntPtr
    
            End Class 'End of Class Shell32.
        End Class ' End of Class win32api.
    
  4. Create the FolderBrowser component class. To do this, add references to the System.Drawing and System.Windows.Forms .NET assemblies. Make sure to identify the namespaces that are used in the implementation of the component, and then establish the internal data structures of the component, as follows:
    Imports System.Drawing
    Imports System.Windows.Forms
    Imports System.ComponentModel
    Imports System.Security.Permissions
    
        '<summary>
        'Component wrapping access to the Browse For Folder common dialog box.
        'Call the ShowDialog() method to show the dialog box.
        '</summary>
        Public NotInheritable Class FolderBrowser
            Inherits Component
            Private Shared ReadOnly MAX_PATH As Integer = 260
    
            ' Root node of the tree view.
            Private m_startLocation As FolderID = FolderID.Desktop
    
            ' Browse info options.
            Private publicOptions As Integer = Win32API.Shell32.BffStyles.RestrictToFilesystem Or Win32API.Shell32.BffStyles.RestrictToDomain
            Private privateOptions As Integer = Win32API.Shell32.BffStyles.NewDialogStyle
    
            ' Description text to show.
            Private descriptionText As String = "Please select a folder below:"
    
            'Folder chosen by the user.
            Private m_directoryPath As String = String.Empty
    
            ' <summary>
            ' Enum of CSIDLs identifying standard shell folders.
            ' </summary>
            Public Enum FolderID
                Desktop = &H0
                Printers = &H4
                MyDocuments = &H5
                Favorites = &H6
                Recent = &H8
                SendTo = &H9
                StartMenu = &HB
                MyComputer = &H11
                NetworkNeighborhood = &H12
                Templates = &H15
                MyPictures = &H27
                NetAndDialUpConnections = &H31
            End Enum
        End Class
  5. Implement the ShowDialog method on the FolderBrowser class, as follows:
            ' <summary>
            ' Helper function that returns the IMalloc interface used by the shell.
            '</summary>
            Private Shared Function GetSHMalloc() As Win32API.IMalloc
                Dim malloc As Win32API.IMalloc
                Win32API.Shell32.SHGetMalloc(malloc)
                Return malloc
            End Function
    
            '<summary>
            'Shows the folder browser dialog box.
            '</summary>
            Public Function ShowDialog() As DialogResult
                Return ShowDialog(Nothing)
            End Function
    
            ' <summary>
            ' Shows the folder browser dialog box with the specified owner window.
            ' </summary>
            Public Function ShowDialog(ByVal owner As IWin32Window) As DialogResult
                Dim pidlRoot As IntPtr = IntPtr.Zero
                'Get/find an owner HWND for this dialog
                Dim hWndOwner As IntPtr
                If (Not (owner Is Nothing)) Then
                    hWndOwner = owner.Handle
                Else
                    hWndOwner = Win32API.GetActiveWindow()
                End If
                'Get the IDL for the specific startLocation.
                Win32API.Shell32.SHGetSpecialFolderLocation(hWndOwner, m_startLocation, pidlRoot)
    
                If (pidlRoot.Equals(IntPtr.Zero) = True) Then
                    Return DialogResult.Cancel
                End If
    
                Dim mergedOptions As Integer = publicOptions Or privateOptions
    
                If ((mergedOptions And Win32API.Shell32.BffStyles.NewDialogStyle) <> 0) Then
                    Application.OleRequired()
                End If
                Dim pidlRet As IntPtr = IntPtr.Zero
                Try
                    ' Construct a BROWSEINFO.
                    Dim bi As Win32API.Shell32.BROWSEINFO = New Win32API.Shell32.BROWSEINFO()
                    Dim buffer As IntPtr = Marshal.AllocHGlobal(MAX_PATH)
                    bi.pidlRoot = pidlRoot
                    bi.hwndOwner = hWndOwner
                    bi.pszDisplayName = buffer
                    bi.lpszTitle = descriptionText
                    bi.ulFlags = mergedOptions
                    'Rest of the fields are initialized to zero by constructor.
                    'bi.lpfn = null;  bi.lParam = IntPtr.Zero;    bi.iImage = 0;
                    'Show the dialog.
                    pidlRet = Win32API.Shell32.SHBrowseForFolder(bi)
                    ' Free the buffer that you have allocated on the global heap.
                    Marshal.FreeHGlobal(buffer)
    
                    If (pidlRet.Equals(IntPtr.Zero) = True) Then
                        'User pressed Cancel
                        Return DialogResult.Cancel
                    End If
    
                    'Then retrieve the path from the IDList.
                    Dim sb As StringBuilder = New StringBuilder(MAX_PATH)
                    If (0 = Win32API.Shell32.SHGetPathFromIDList(pidlRet, sb)) Then
                        Return DialogResult.Cancel
                    End If
                    ' Convert to a string.
                    m_directoryPath = sb.ToString()
    
                Finally
                    Dim malloc As Win32API.IMalloc = GetSHMalloc()
                    malloc.Free(pidlRoot)
                    If (pidlRet.Equals(IntPtr.Zero) = False) Then
                        malloc.Free(pidlRet)
                    End If
                End Try
                Return DialogResult.OK
            End Function
    
  6. Add convenience properties to allow dialog box customization. To do this, add a number of Boolean properties that allow the user to set and reset flags in the publicOptions field of the dialog box. The following properties are implemented:
    • OnlyFilesystem
    • ShowNetworkFolders
    • OnlySubfolders
    • ShowTextBox
    • ValidateUserInput
    • SelectComputer
    • SelectPrinter
    • SelectFiles
    • DirectoryPath
    • Description
    In addition, implement the StartLocation property, as follows:
     
            ' <summary>
            ' Helper function used to set / reset bits in the publicOptions bitfield.
            ' </summary>
            Private Sub SetOptionField(ByVal mask As Integer, ByVal turnOn As Boolean)
                If (turnOn) Then
                    publicOptions = publicOptions Or mask
                Else
                    publicOptions = publicOptions And (Not mask)
                End If
            End Sub
    
            ' <summary>
            'Only return file system directories. If the user selects folders
            'that are not part of the file system, the OK button is dimmed.
            '</summary>
    
           <Category("Navigation"), _
            Description("Only return file system directories. If the user selects folders " + "that are not part of the file system, the OK button is grayed."), _
            DefaultValue(True)> _
            Public Property OnlyFilesystem() As Boolean
                Get
                    Return ((publicOptions And Win32API.Shell32.BffStyles.RestrictToFilesystem) <> 0)
                End Get
                Set(ByVal Value As Boolean)
                    SetOptionField(Win32API.Shell32.BffStyles.RestrictToFilesystem, Value)
                End Set
            End Property
    
            ' <summary>
            ' Location of the root folder to start browsing from. Only the specified
            ' folder and any subfolders under it in the namespace hierarchy will appear
            ' in the dialog box.
            ' </summary>
    
            <Category("Navigation"), _
            Description("Location of the root folder to start browsing from. Only the specified " + _
              "folder and any subfolders under it in the namespace hierarchy will appear " + _
              "in the dialog box."), _
            DefaultValue(GetType(FolderID), "0")> _
            Public Property StartLocation() As FolderID
                Get
                    Return m_startLocation
                End Get
                Set(ByVal Value As FolderID)
                    Dim Obj As New UIPermission(UIPermissionWindow.AllWindows)
                    Obj.Demand()
                    m_startLocation = Value
                End Set
            End Property
    
            ' <summary>
            ' Gets the directory path of the folder the user picked.
            ' </summary>
            <Description("Contains the directory path of the folder the user picked.")> _
            Public ReadOnly Property DirectoryPath() As String
                Get
                    Dim Obj As New FileIOPermission(FileIOPermissionAccess.PathDiscovery, m_directoryPath)
                    Obj.Demand()
                    Return m_directoryPath
                End Get
            End Property
    
    
  7. Provide a toolbox icon for the component. To do this, right-click the project in Solution Explorer, click Add, click New Item, click Resources, and then click Bitmap File. Name the new bitmap file FolderBrowser.bmp, and then resize it to be 16 x 16 pixels. Edit the bitmap. In Solution Explorer, click to select the FolderBrowser.bmp file, open the property grid, and then set Build Action to Embedded Resource. Next, associate this bitmap with your component by adding an attribute to the FolderBrowser class, as follows:
    <ToolboxBitmap(GetType(FolderBrowser))> _
        Public NotInheritable Class FolderBrowser
            Inherits Component
                ' ....
    End Class
    
  8. Build the project.
back to the top

Implement the Test Project

  1. Generate an empty project. To do this, add a new Visual Basic .NET or Visual Basic 2005 Windows Application project named VbClient to the BrowseForFolder solution. Add a reference to the Microsoft.Samples.WinForms.Extras assembly that you just created.
  2. Add a component to the toolbox. To do this, open the Toolbox window, right-click the window, and then click to select Customize Toolbox. On the .NET Framework Components tab, click Browse, and then visit the Microsoft.Samples.WinForms.Extras.dll file that you just created. Click to select the FolderBrowser component when it appears in the list, and then close the dialog box.
  3. Customize the form. To do this, open Form1 in design mode, open the Toolbox window, and then drag a button and a FolderBrowser component to your design area.
  4. Customize the FolderBrowser component. To do this, click the folderBrowser1 icon. Notice all the properties that you can customize in the Properties window. Customize these properties.
  5. Add code to display the dialog box. To do this, double-click the button that you dragged onto the form, and then add the following code to the button handler:
        Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
            If DialogResult.OK = folderBrowser1.ShowDialog() Then
                MessageBox.Show(folderBrowser1.DirectoryPath)
            End If
         End Sub
  6. Build the solution, and then run the VbClient project.
back to the top

Modification Type:MinorLast Reviewed:10/3/2006
Keywords:kbvs2005swept kbvs2005applies kbdownload kbAPI kbfile kbWindowsForms kbBrowse kbHOWTOmaster kbhowto KB811004 kbAudDeveloper