|
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;
}
|