Microsoft KB Archive/306285

= How to implement a managed component that wraps the Browse For Folder common dialog box by using Visual C# =

Article ID: 306285

Article Last Modified on 12/11/2006

-

APPLIES TO


 * Microsoft Visual C# 2005
 * Microsoft Visual C# .NET 2003 Standard Edition
 * Microsoft Visual C# .NET 2002 Standard Edition

-



This article was previously published under Q306285





For a Microsoft Visual Basic 2005 or Microsoft Visual Basic .NET version of this article, see 811004.

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:

Download the Bffcomp.exe package now.

Release Date: October 1, 2001

For more 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 prevent any unauthorized changes to the file.



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
 Generate an empty project. To do this, create a new blank solution and name it BrowseForFolder. Add a new Visual C# .NET Class Library project to the newly-created solution, and name the project WinFormsExtras. Modify the wizard-generated code. To do this, rename the Class1.cs file from the project to FolderBrowser.cs, and 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.  Add the declarations that are needed for P/Invoke and ComInterop. Make sure that System.Runtime.InteropServices and System.Text are referenced, and provide declarations for the functions and interfaces that you need, as follows: using System.Runtime.InteropServices; using System.Text; internal class Win32API {     // C# representation of the IMalloc interface. [InterfaceType ( ComInterfaceType.InterfaceIsIUnknown ), Guid ( &quot;00000002-0000-0000-C000-000000000046&quot; )] public interface IMalloc {        [PreserveSig] IntPtr Alloc ( [In] int cb ); [PreserveSig] IntPtr Realloc ( [In] IntPtr pv, [In] int cb ); [PreserveSig] void  Free ( [In] IntPtr pv ); [PreserveSig] int   GetSize ( [In] IntPtr pv ); [PreserveSig] int   DidAlloc ( IntPtr pv ); [PreserveSig] void  HeapMinimize ; }

[DllImport(&quot;User32.DLL&quot;)] public static extern IntPtr GetActiveWindow ;

public class Shell32 {        // Styles used in the BROWSEINFO.ulFlags field. [Flags] public enum BffStyles {           RestrictToFilesystem = 0x0001, // BIF_RETURNONLYFSDIRS RestrictToDomain =    0x0002, // BIF_DONTGOBELOWDOMAIN RestrictToSubfolders = 0x0008, // BIF_RETURNFSANCESTORS ShowTextBox =         0x0010, // BIF_EDITBOX ValidateSelection =   0x0020, // BIF_VALIDATE NewDialogStyle =      0x0040, // BIF_NEWDIALOGSTYLE BrowseForComputer =   0x1000, // BIF_BROWSEFORCOMPUTER BrowseForPrinter =    0x2000, // BIF_BROWSEFORPRINTER BrowseForEverything = 0x4000, // BIF_BROWSEINCLUDEFILES }

// Delegate type used in BROWSEINFO.lpfn field. public delegate int BFFCALLBACK ( IntPtr hwnd, uint uMsg, IntPtr lParam, IntPtr lpData );

[StructLayout ( LayoutKind.Sequential, Pack=8 )] public struct BROWSEINFO {           public IntPtr       hwndOwner; public IntPtr      pidlRoot; public IntPtr      pszDisplayName; [MarshalAs ( UnmanagedType.LPTStr )] public string      lpszTitle; public int         ulFlags; [MarshalAs ( UnmanagedType.FunctionPtr )] public BFFCALLBACK lpfn; public IntPtr      lParam; public int         iImage; }

[DllImport ( &quot;Shell32.DLL&quot; )] public static extern int SHGetMalloc ( out IMalloc ppMalloc );

[DllImport ( &quot;Shell32.DLL&quot; )] public static extern int SHGetSpecialFolderLocation (                     IntPtr hwndOwner, int nFolder, out IntPtr ppidl );

[DllImport ( &quot;Shell32.DLL&quot; )] public static extern int SHGetPathFromIDList (                     IntPtr pidl, StringBuilder Path );

[DllImport ( &quot;Shell32.DLL&quot;, CharSet=CharSet.Auto )] public static extern IntPtr SHBrowseForFolder ( ref BROWSEINFO bi ); }  }                      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 establish the component's internal data structures, as follows: using System.Drawing; using System.Windows.Forms; using System.ComponentModel; using System.Security.Permissions; ///   /// Component wrapping access to the Browse For Folder common dialog box. /// Call the ShowDialog method to bring the dialog box up. ///   public sealed class FolderBrowser : Component {     private static readonly int MAX_PATH = 260; // Root node of the tree view. private FolderID startLocation = FolderID.Desktop;

// Browse info options. private int publicOptions = (int) Win32API.Shell32.BffStyles.RestrictToFilesystem | (int) Win32API.Shell32.BffStyles.RestrictToDomain; private int privateOptions = (int) Win32API.Shell32.BffStyles.NewDialogStyle;

// Description text to show. private string descriptionText = &quot;Please select a folder below:&quot;;

// Folder chosen by the user. private string directoryPath = String.Empty;

///      /// Enum of CSIDLs identifying standard shell folders. ///      public enum FolderID {        Desktop                   = 0x0000, Printers                 = 0x0004, MyDocuments              = 0x0005, Favorites                = 0x0006, Recent                   = 0x0008, SendTo                   = 0x0009, StartMenu                = 0x000b, MyComputer               = 0x0011, NetworkNeighborhood      = 0x0012, Templates                = 0x0015, MyPictures               = 0x0027, NetAndDialUpConnections  = 0x0031, } }                      Implement the ShowDialog method on the FolderBrowser class, as follows: ///      /// Helper function that returns the IMalloc interface used by the shell. ///      private static Win32API.IMalloc GetSHMalloc {        Win32API.IMalloc malloc; Win32API.Shell32.SHGetMalloc ( out malloc ); return malloc; }

///      /// Shows the folder browser dialog box. ///      public DialogResult ShowDialog {        return ShowDialog ( null ); }

///      /// Shows the folder browser dialog box with the specified owner window. ///      public DialogResult ShowDialog ( IWin32Window owner ) {        IntPtr pidlRoot = IntPtr.Zero;

// Get/find an owner HWND for this dialog. IntPtr hWndOwner;

if ( owner != null ) {           hWndOwner = owner.Handle; }        else {            hWndOwner = Win32API.GetActiveWindow ; }

// Get the IDL for the specific startLocation. Win32API.Shell32.SHGetSpecialFolderLocation ( hWndOwner, (int) startLocation, out pidlRoot );

if (pidlRoot == IntPtr.Zero) {           return DialogResult.Cancel; }

int mergedOptions = (int)publicOptions | (int)privateOptions;

if ( ( mergedOptions & (int)Win32API.Shell32.BffStyles.NewDialogStyle ) != 0 ) {               if ( System.Threading.ApartmentState.MTA == Application.OleRequired  ) mergedOptions = mergedOptions & (~ (int)Win32API.Shell32.BffStyles.NewDialogStyle); }

IntPtr pidlRet = IntPtr.Zero;

try {           // Construct a BROWSEINFO. Win32API.Shell32.BROWSEINFO bi = new Win32API.Shell32.BROWSEINFO ; IntPtr buffer = Marshal.AllocHGlobal ( MAX_PATH);

bi.pidlRoot = pidlRoot; bi.hwndOwner = hWndOwner; bi.pszDisplayName = buffer; bi.lpszTitle = descriptionText; bi.ulFlags = mergedOptions; // The rest of the fields are initialized to zero by the constructor. // bi.lpfn = null; bi.lParam = IntPtr.Zero;    bi.iImage = 0;

// Show the dialog. pidlRet = Win32API.Shell32.SHBrowseForFolder ( ref bi );

// Free the buffer you've allocated on the global heap. Marshal.FreeHGlobal ( buffer );

if ( pidlRet == IntPtr.Zero ) {              // User clicked Cancel. return DialogResult.Cancel; }

// Then retrieve the path from the IDList. StringBuilder sb = new StringBuilder ( MAX_PATH ); if ( 0 == Win32API.Shell32.SHGetPathFromIDList ( pidlRet, sb ) ) {              return DialogResult.Cancel; }

// Convert to a string. directoryPath = sb.ToString ; }        finally {           Win32API.IMalloc malloc = GetSHMalloc ; malloc.Free ( pidlRoot );

if ( pidlRet != IntPtr.Zero ) {              malloc.Free ( pidlRet ); }        }

return DialogResult.OK; }                     Add convenience properties to allow dialog customization. To do this, add a number of bool properties that allow the user to set and reset flags in the publicOptions field of the dialog box. The following properties are implemented: <ul> OnlyFilesystem</li> ShowNetworkFolders</li> OnlySubfolders</li> ShowTextBox</li> ValidateUserInput</li> SelectComputer</li> SelectPrinter</li> SelectFiles</li> DirectoryPath</li> Description</li></ul>

In addition, implement the StartLocation property as follows: ///      /// Helper function used to set and reset bits in the publicOptions bitfield. ///      private void SetOptionField ( int mask, bool turnOn ) {        if (turnOn) publicOptions |= mask; else publicOptions &= ~mask; }

///      /// Only return file system directories. If the user selects folders /// that are not part of the file system, the OK button is unavailable. ///      [Category ( &quot;Navigation&quot; )] [Description ( &quot;Only return file system directories. If the user selects folders &quot; +                    &quot;that are not part of the file system, the OK button is unavailable.&quot; )] [DefaultValue ( true )] public bool OnlyFilesystem {        get {           return (publicOptions & (int) Win32API.Shell32.BffStyles.RestrictToFilesystem) != 0; }        set {           SetOptionField ( (int) Win32API.Shell32.BffStyles.RestrictToFilesystem, value ); }     }

///      /// Location of the root folder from which to start browsing. Only the specified /// folder and any folders beneath it in the namespace hierarchy appear /// in the dialog box. ///      [Category ( &quot;Navigation&quot; )] [Description ( &quot;Location of the root folder from which to start browsing. Only the specified &quot; +                    &quot;folder and any folders beneath it in the namespace hierarchy appear &quot; +                     &quot;in the dialog box.&quot; )] [DefaultValue ( typeof(FolderID), &quot;0&quot;)] public FolderID StartLocation {        get {           return startLocation; }        set {           new UIPermission ( UIPermissionWindow.AllWindows ).Demand ; startLocation = value; }     }

///      /// Full path to the folder selected by the user. ///      [Category(&quot;Navigation&quot;)] [Description(&quot;Full path to the folder slected by the user.&quot;)] public string DirectoryPath {    get {       return directoryPath; }     }

</li>  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 resize it to be 16 x 16 pixels. Edit the bitmap as desired. In Solution Explorer, 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(typeof(FolderBrowser))] public sealed class FolderBrowser : Component { // ...  }                    </li> Build the project.</li></ol>

Implement the test project
<ol> Generate the empty project. To do this, add a new Visual C# Windows Application project named CsClient to the BrowseForFolder solution. Add a reference to the Microsoft.Samples.WinForms.Extras assembly that you just created.</li> Add a component to the toolbox. To do this, open the Toolbox window, right-click the window, and then select Customize Toolbox. On the .NET Framework Components tab, click Browse and browse to the Microsoft.Samples.WinForms.Extras.dll file that you just created. When the FolderBrowser component appears in the list, select it and dismiss the dialog box.</li> Customize the form. To do this, with Form1 open in design mode, open the Toolbox window and drag a button and a FolderBrowser component to your design area.</li> Customize the FolderBrowser component. To do this, click the folderBrowser1 icon and note all the properties that you can customize in the property grid. Customize these properties as desired.</li> <li> Add code to display the dialog box. To do this, double-click the button that you dragged onto the form and add the following code to the button handler: private void button1_Click(object sender, System.EventArgs e)     { if ( DialogResult.OK == folderBrowser1.ShowDialog ) MessageBox.Show ( folderBrowser1.DirectoryPath ); }                   </li> <li>Build the solution, and then run the CsClient project.</li></ol>

Additional query words: managed component, SHBrowseForFolder

Keywords: kbdownload kbcominterop kbhowtomaster KB306285

-

[mailto:TECHNET@MICROSOFT.COM Send feedback to Microsoft]

© Microsoft Corporation. All rights reserved.