Home
Products
Forums
Help
Publish Article
Go Freelance

Use Interop To Build FolderBrowser Dialog Control

Recently we were working on a project that required a dialog box to browse folders. We looked around in .Net framework to find out if there is a windows forms control that could provide this control. After a bit of research we found out that answer to our question is "yes and no".

Undocumented .Net Framework

There is a class FolderBrowser in framework that does exactly provide the folder browsing control. It is the same control that is displayed when you use SHBrowseForFolder API from Win32 SDK. And it seems that Microsoft did not want developers to use this dialog box directly in their applications. This class is defined inside FolderNameEditor class and is defined as prottected. This means that only FolderNameEditor class has access to it. But one good thing that Microsoft did was that FolderNameEditor class is defined as public and is not sealed. This gives us the oppurtunity to derive our own class from FolderNameEditor class and then access FolderBrowser class to use the dialog box to select folders.

First we will show you how you can explot this already existing FolderBrowser class to use folder browsing capability. The process is pretty simple.

  • Define a new class and derive it from FolderNameEditor class.
    public class FolderBrowser : FolderNameEditor
    							
  • Define a FolderBrowserEditor.FolderBrowser class variable.
    private FolderNameEditor.FolderBrowser m_obBrowser = null;								
    								
  • Define a method in this new class that will call ShowDialog method on FolderBrowserEditor.FolderBrowser variable instance.
    m_obBrowser.ShowDialog();								
    								

Before we discuss this any further, we would like to warn you that Microsoft has put a warning in documentation for these classes that these classes should not be called directly from applications. It is intended for .Net framework infrastructure. Following is a complete implementation of class that gives the capability to invoke FolderBrowser dialog box.

public class FolderBrowser : FolderNameEditor
{
	private FolderNameEditor.FolderBrowser m_obBrowser = null;
	private string m_strDescription;
	public FolderBrowser()
	{
		m_strDescription = "Select folder";
		m_obBrowser = new FolderNameEditor.FolderBrowser();
	}

	public string DirectoryPath
	{
		get{return this.m_obBrowser.DirectoryPath;}
	}

	public DialogResult ShowDialog()
	{
		m_obBrowser.Description = m_strDescription;
		return m_obBrowser.ShowDialog();
	}
}						
						

Use Interop To Build FolderBrowser Dialog Box

We will honour Microsoft's warning of not using FolderBrowser and FolderBrowserEditor classes directly from our application. If you want to build this type of control, your first thought will be to somehow use SHFolderBrowser Shell API. And if you look at the MSIL code for FolderBrowser class, you will not be surprised to find out that this what Micorosoft is doing too. They have used Inerop to call SHBrowseForFolder call from shell32.dll and the supporting helper APIs.

We will try to show you the use of Interop for implementing folder browsing capability. Following is the complete implementation of such a class. We named it same as FolderBrowser. For details download the attached project files.

using System;
using System.Diagnostics;
using System.Collections;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Windows.Forms;
using System.Runtime.InteropServices;

namespace PardesiServices.WinControls
{
	[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Auto)]
	[ComVisible(true)]
	public class BROWSEINFO 
	{
		public IntPtr hwndOwner;
		public IntPtr pidlRoot;
		public IntPtr pszDisplayName;
		public string lpszTitle;
		public int ulFlags;
		public IntPtr lpfn;
		public IntPtr lParam;
		public int iImage;
	} 

	public class Win32SDK
	{
		[DllImport("shell32.dll", PreserveSig=true, CharSet=CharSet.Auto)]
		public static extern IntPtr SHBrowseForFolder(BROWSEINFO bi);

		[DllImport("shell32.dll", PreserveSig=true, CharSet=CharSet.Auto)]
		public static extern bool SHGetPathFromIDList(IntPtr pidl, IntPtr pszPath);

		[DllImport("shell32.dll", PreserveSig=true, CharSet=CharSet.Auto)]
		public static extern int SHGetSpecialFolderLocation(IntPtr hwnd, int csidl, ref IntPtr ppidl);
	}

	[Flags, Serializable]
	public enum BrowseFlags
	{
		BIF_DEFAULT				=	0x0000,
		BIF_BROWSEFORCOMPUTER	=	0x1000,
		BIF_BROWSEFORPRINTER	=	0x2000,
		BIF_BROWSEINCLUDEFILES	=	0x4000,
		BIF_BROWSEINCLUDEURLS	=	0x0080,
		BIF_DONTGOBELOWDOMAIN	=	0x0002,
		BIF_EDITBOX				=	0x0010,
		BIF_NEWDIALOGSTYLE		=	0x0040,
		BIF_NONEWFOLDERBUTTON	=	0x0200,
		BIF_RETURNFSANCESTORS	=	0x0008,
		BIF_RETURNONLYFSDIRS	=	0x0001,
		BIF_SHAREABLE			=	0x8000,
		BIF_STATUSTEXT			=	0x0004,
		BIF_UAHINT				=	0x0100,
		BIF_VALIDATE			=	0x0020,
		BIF_NOTRANSLATETARGETS	=	0x0400,
	}

	public class FolderBrowser : Component
	{
		private string m_strDirectoryPath;
		private string m_strTitle;
		private string m_strDisplayName;
		private BrowseFlags m_Flags;
		public FolderBrowser()
		{
			m_Flags = BrowseFlags.BIF_DEFAULT;
			m_strTitle = "";
		}

		public string DirectoryPath
		{
			get{return this.m_strDirectoryPath;}
		}

		public string DisplayName
		{
			get{return this.m_strDisplayName;}
		}

		public string Title
		{
			set{this.m_strTitle = value;}
		}

		public BrowseFlags Flags
		{
			set{this.m_Flags = value;}
		}

		public DialogResult ShowDialog()
		{
			BROWSEINFO bi = new BROWSEINFO();
			bi.pszDisplayName = IntPtr.Zero;
			bi.lpfn = IntPtr.Zero;
			bi.lParam = IntPtr.Zero;
			bi.lpszTitle = "Select Folder";
			IntPtr idListPtr = IntPtr.Zero;
			IntPtr pszPath = IntPtr.Zero;
			try
			{
				if (this.m_strTitle.Length != 0)
				{
					bi.lpszTitle = this.m_strTitle;
				}
				bi.ulFlags = (int)this.m_Flags;
				bi.pszDisplayName = Marshal.AllocHGlobal(256);
				// Call SHBrowseForFolder
				idListPtr = Win32SDK.SHBrowseForFolder(bi);
				// Check if the user cancelled out of the dialog or not.
				if (idListPtr == IntPtr.Zero)
				{
					return DialogResult.Cancel;
				}

				// Allocate ncessary memory buffer to receive the folder path.
				pszPath = Marshal.AllocHGlobal(256);
				// Call SHGetPathFromIDList to get folder path.
				bool bRet = Win32SDK.SHGetPathFromIDList(idListPtr, pszPath);
				// Convert the returned native poiner to string.
				m_strDirectoryPath = Marshal.PtrToStringAuto(pszPath);
				this.m_strDisplayName = Marshal.PtrToStringAuto(bi.pszDisplayName);
			}
			catch (Exception ex)
			{
				Trace.WriteLine(ex.Message);
				return DialogResult.Abort;
			}
			finally
			{
				// Free the memory allocated by shell.
				if (idListPtr != IntPtr.Zero)
				{
					Marshal.FreeHGlobal(idListPtr);
				}
				if (pszPath != IntPtr.Zero)
				{
					Marshal.FreeHGlobal(pszPath);
				}
				if (bi != null)
				{
					Marshal.FreeHGlobal(bi.pszDisplayName);
				}
			}
			return DialogResult.OK;
		}

		private IntPtr GetStartLocationPath()
		{
			return IntPtr.Zero;
		}
	}
}						
						

How to use it?

Following is sample code that we used for test implementation of our FolderBrowser class.

FolderBrowser myBrowser = new FolderBrowser();
myBrowser.Title = "My Title";
myBrowser.Flags = BrowseFlags.BIF_NEWDIALOGSTYLE |
				  BrowseFlags.BIF_STATUSTEXT |
				  BrowseFlags.BIF_EDITBOX;;
DialogResult res = myBrowser.ShowDialog();
if (res == DialogResult.OK)
{
	string myPath = myBrowser.DirectoryPath;
}
						
Go Freelance
Home     About us     Contact us    Copyright    Privacy Policy    Return Policy    Advertisers
Copyright © Netomatix