using Microsoft.Win32;
using System.ComponentModel;
using System.Runtime.InteropServices;
using System.Text;
namespace System.Windows.Forms
{
/*///
///
///
public enum FolderBrowserDialogOptions
{
///
Folders,
///
FoldersAndFiles,
///
Computers,
///
Printers
}*/
///
/// Class to let the user browse for a folder.
///
[System.Drawing.ToolboxBitmap(typeof(Microsoft.Win32.TaskScheduler.TaskEditDialog), "Dialog"), Description("Dialog that browses network computers.")]
public class FolderBrowserDialog2 : CommonDialog
{
///
/// Initializes a new instance of the class.
///
public FolderBrowserDialog2()
{
Reset();
}
///
/// Gets a that browses network computers.
///
/// A that browses network computers.
public static FolderBrowserDialog2 ComputerBrowser
{
get
{
FolderBrowserDialog2 dlg = new FolderBrowserDialog2();
dlg.BrowserFlag = (int)BrowseInfoFlag.BIF_BROWSEFORCOMPUTER;
dlg.RootFolder = (Environment.SpecialFolder)0x12;
return dlg;
}
}
///
/// Gets a that browses printers.
///
/// A that browses printers.
public static FolderBrowserDialog2 PrinterBrowser
{
get
{
FolderBrowserDialog2 dlg = new FolderBrowserDialog2();
dlg.BrowserFlag = (int)BrowseInfoFlag.BIF_BROWSEFORPRINTER;
dlg.RootFolder = (Environment.SpecialFolder)4;
return dlg;
}
}
#region Enumerations
// BFFM
///
/// Enumeration with dialog messages used by the Browse For Folder dialog box.
///
private enum BrowseForFolderMessages : uint
{
// statusMessage from browser
BFFM_INITIALIZED = 1,
BFFM_SELCHANGED = 2,
BFFM_VALIDATEFAILEDA = 3, // lParam:szPath ret:1(cont),0(EndDialog)
BFFM_VALIDATEFAILEDW = 4, // lParam:wzPath ret:1(cont),0(EndDialog)
BFFM_IUNKNOWN = 5, // provides IUnknown to client. lParam: IUnknown*
// messages to browser
// 0x400 = WM_USER
BFFM_SETSTATUSTEXTA = (0x0400 + 100),
BFFM_ENABLEOK = (0x0400 + 101),
BFFM_SETSELECTIONA = (0x0400 + 102),
BFFM_SETSELECTIONW = (0x0400 + 103),
BFFM_SETSTATUSTEXTW = (0x0400 + 104),
BFFM_SETOKTEXT = (0x0400 + 105), // Unicode only
BFFM_SETEXPANDED = (0x0400 + 106) // Unicode only
}
// BIF
///
/// Flags enumeration to specify the dialog style.
///
[Flags]
private enum BrowseInfoFlag : uint
{
BIF_RETURNONLYFSDIRS = 0x0001, // For finding a folder to start document searching
BIF_DONTGOBELOWDOMAIN = 0x0002, // For starting the Find Computer
BIF_STATUSTEXT = 0x0004, // Top of the dialog has 2 lines of okText for BROWSEINFO.lpszTitle and
// one line if this flag is set. Passing the statusMessage
// BFFM_SETSTATUSTEXTA to the hwnd can set the rest of the okText.
// This is not used with BIF_USENEWUI and BROWSEINFO.lpszTitle gets
// all three lines of okText.
BIF_RETURNFSANCESTORS = 0x0008,
BIF_EDITBOX = 0x0010, // Add an editbox to the dialog
BIF_VALIDATE = 0x0020, // insist on valid result (or CANCEL)
BIF_NEWDIALOGSTYLE = 0x0040, // Use the new dialog layout with the ability to resize
// Caller needs to call OleInitialize() before using this API
BIF_USENEWUI = (BIF_NEWDIALOGSTYLE | BIF_EDITBOX),
BIF_BROWSEINCLUDEURLS = 0x0080, // Allow URLs to be displayed or entered. (Requires BIF_USENEWUI)
BIF_UAHINT = 0x0100, // Add a UA hint to the dialog, in place of the edit box. May not be
// combined with BIF_EDITBOX
BIF_NONEWFOLDERBUTTON = 0x0200, // Do not add the "New Folder" button to the dialog. Only applicable
// with BIF_NEWDIALOGSTYLE.
BIF_NOTRANSLATETARGETS = 0x0400, // don't traverse target as shortcut
BIF_BROWSEFORCOMPUTER = 0x1000, // Browsing for Computers.
BIF_BROWSEFORPRINTER = 0x2000, // Browsing for Printers
BIF_BROWSEINCLUDEFILES = 0x4000, // Browsing for Everything
BIF_SHAREABLE = 0x8000 // sharable resources displayed (remote shares, requires BIF_USENEWUI)
}
#endregion Enumerations
#region Delegates
///
/// Signature for the BrowseCallBackProc callback.
///
/// Window handle of the browse dialog box.
/// Dialog box event that generated the statusMessage.
/// Value whose meaning depends on the event specified in uMsg.
/// Application-defined value that was specified in the lParam member of the BROWSEINFO structure used in the call to SHBrowseForFolder.
/// Returns 0 except in the case of BFFM_VALIDATEFAILED. For that flag, returns 0 to dismiss the dialog or nonzero to keep the dialog displayed.
private delegate int BrowseCallBackProc(IntPtr hwnd, uint uMsg, IntPtr lParam, IntPtr lpData);
#endregion Delegates
#region Events
///
/// Occurs when dialog box has been initialized and primary values have been set.
///
public event EventHandler Initialized;
///
/// Event that is raised when the user selects an invalid folder.
///
public event EventHandler InvalidFolderSelected;
///
/// Occurs when property has changed.
///
public event PropertyChangedEventHandler SelectedPathChanged;
#endregion Events
#region Properties
///
/// Gets or sets an additional settings flag.
///
[DefaultValue(0), Category("Folder Browsing"), Localizable(false)]
public int BrowserFlag
{
get; set;
}
///
/// Gets or sets the caption of the dialog box.
///
[DefaultValue(""), Category("Folder Browsing"), Localizable(true)]
public string Caption
{
get; set;
}
///
/// Gets or sets the description shown to the user.
///
[DefaultValue(""), Category("Folder Browsing"), Localizable(true)]
public string Description
{
get;
set;
}
///
/// Gets or sets the text on the OK button.
///
[DefaultValue(""), Category("Folder Browsing"), Localizable(true)]
public string OkText
{
get; set;
}
///
/// Gets or sets the root folder.
///
[DefaultValue(0), Localizable(false), Category("Folder Browsing")]
public Environment.SpecialFolder RootFolder
{
get; set;
}
///
/// Gets or sets the path selected by the user. The initially selected path if set before
/// the dialog box is shown.
///
[DefaultValue(""), Category("Folder Browsing"), Localizable(true)]
public string SelectedPath
{
get; set;
}
///
/// Gets or sets whether or not to show files as well.
///
[DefaultValue(false), Localizable(false), Category("Folder Browsing")]
public bool ShowFiles
{
get; set;
}
///
/// Gets or sets whether or not to show an edit box for the folder path.
///
[DefaultValue(false), Localizable(false), Category("Folder Browsing")]
public bool ShowFolderPathEditBox
{
get; set;
}
///
/// Gets or sets whether or not to show the new folder button.
///
[DefaultValue(false), Localizable(false), Category("Folder Browsing")]
public bool ShowNewFolderButton
{
get; set;
}
#endregion Properties
#region Methods
///
/// When overridden in a derived class, resets the properties of a common dialog box to their default values.
///
public override void Reset()
{
this.BrowserFlag = 0;
this.Caption = this.Description = this.OkText = this.SelectedPath = string.Empty;
this.RootFolder = (Environment.SpecialFolder)0;
this.ShowFiles = this.ShowFolderPathEditBox = this.ShowNewFolderButton = false;
}
///
/// Shows the dialog box to let the user browse for and select a folder.
///
/// The HWND of the parent window.
/// The selected folder or null if no folder was selected by the user.
protected override bool RunDialog(IntPtr parentWindowHandle)
{
// Make sure OLE is initialized. This is a prerequisite for calling SHBrowseForFolder.
NativeMethods.OleInitialize(IntPtr.Zero);
IntPtr rpidl = IntPtr.Zero;
NativeMethods.SHGetSpecialFolderLocation(parentWindowHandle, (int)this.RootFolder, out rpidl);
if (rpidl == IntPtr.Zero)
{
NativeMethods.SHGetSpecialFolderLocation(parentWindowHandle, 0, out rpidl);
if (rpidl == IntPtr.Zero)
throw new InvalidOperationException("No root folder specified for FolderBrowserDialog2.");
}
if (Control.CheckForIllegalCrossThreadCalls && (Application.OleRequired() != System.Threading.ApartmentState.STA))
throw new System.Threading.ThreadStateException("Debugging Exception Only. Thread must be STA.");
BrowseInfoFlag browseInfoFlag = BrowseInfoFlag.BIF_NEWDIALOGSTYLE | BrowseInfoFlag.BIF_SHAREABLE;
if (!ShowNewFolderButton)
browseInfoFlag |= BrowseInfoFlag.BIF_NONEWFOLDERBUTTON;
if (ShowFolderPathEditBox)
browseInfoFlag |= BrowseInfoFlag.BIF_EDITBOX | BrowseInfoFlag.BIF_VALIDATE;
if (ShowFiles)
browseInfoFlag |= BrowseInfoFlag.BIF_BROWSEINCLUDEFILES;
browseInfoFlag |= (BrowseInfoFlag)BrowserFlag;
NativeMethods.BROWSEINFO bi;
bi.hwndOwner = parentWindowHandle;
bi.pidlRoot = rpidl;
bi.pszDisplayName = new string('\0', 260);
bi.lpszTitle = Description;
bi.ulFlags = (uint)browseInfoFlag;
bi.lpfn = new NativeMethods.BrowseCallBackProc(OnBrowseEvent);
bi.lParam = IntPtr.Zero;
bi.iImage = 0;
StringBuilder sb = new StringBuilder(260);
IntPtr pidl = IntPtr.Zero;
try
{
pidl = NativeMethods.SHBrowseForFolder(ref bi);
if (((browseInfoFlag & BrowseInfoFlag.BIF_BROWSEFORPRINTER) == BrowseInfoFlag.BIF_BROWSEFORPRINTER) ||
((browseInfoFlag & BrowseInfoFlag.BIF_BROWSEFORCOMPUTER) == BrowseInfoFlag.BIF_BROWSEFORCOMPUTER))
{
this.SelectedPath = bi.pszDisplayName;
}
else
{
if (pidl == IntPtr.Zero || 0 == NativeMethods.SHGetPathFromIDList(pidl, sb))
return false;
this.SelectedPath = sb.ToString();
}
return true;
}
finally
{
if (rpidl != IntPtr.Zero)
Marshal.FreeCoTaskMem(rpidl);
if (pidl != IntPtr.Zero)
Marshal.FreeCoTaskMem(pidl);
}
}
///
/// Callback for Windows.
///
/// Window handle of the browse dialog box.
/// Dialog box event that generated the statusMessage.
/// Value whose meaning depends on the event specified in uMsg.
/// Application-defined value that was specified in the lParam member of the BROWSEINFO structure used in the call to SHBrowseForFolder.
/// Returns 0 except in the case of BFFM_VALIDATEFAILED. For that flag, returns 0 to dismiss the dialog or nonzero to keep the dialog displayed.
//[CLSCompliant(false)]
private int OnBrowseEvent(IntPtr hwnd, uint uMsg, IntPtr lParam, IntPtr lpData)
{
BrowseForFolderMessages messsage = (BrowseForFolderMessages)uMsg;
switch (messsage)
{
case BrowseForFolderMessages.BFFM_INITIALIZED:
// Dialog is being initialized, so set the initial parameters
if (!String.IsNullOrEmpty(Caption))
NativeMethods.SetWindowText(hwnd, Caption);
if (!String.IsNullOrEmpty(SelectedPath))
{
NativeMethods.SendMessage(hwnd, (uint)BrowseForFolderMessages.BFFM_SETEXPANDED, (IntPtr)1, SelectedPath);
NativeMethods.SendMessage(hwnd, (uint)BrowseForFolderMessages.BFFM_SETSELECTIONW, (IntPtr)1, SelectedPath);
}
if (!String.IsNullOrEmpty(OkText))
NativeMethods.SendMessage(hwnd, (uint)BrowseForFolderMessages.BFFM_SETOKTEXT, (IntPtr)0, OkText);
if (this.Initialized != null)
{
EventHandler h = this.Initialized;
h(this, new FolderBrowserDialogInitializedEventArgs(hwnd));
}
return 0;
case BrowseForFolderMessages.BFFM_SELCHANGED:
try
{
StringBuilder sb = new StringBuilder(260);
if (lParam == IntPtr.Zero || 0 == NativeMethods.SHGetPathFromIDList(lParam, sb))
return 0;
this.SelectedPath = sb.ToString();
}
catch { return 0; }
if (this.SelectedPathChanged != null)
{
PropertyChangedEventHandler h = this.SelectedPathChanged;
h(this, new PropertyChangedEventArgs("SelectedPath"));
}
return 0;
case BrowseForFolderMessages.BFFM_VALIDATEFAILEDA:
case BrowseForFolderMessages.BFFM_VALIDATEFAILEDW:
if (this.InvalidFolderSelected != null)
{
string folderName;
if (messsage == BrowseForFolderMessages.BFFM_VALIDATEFAILEDA)
folderName = Marshal.PtrToStringAnsi(lParam);
else
folderName = Marshal.PtrToStringUni(lParam);
EventHandler h = this.InvalidFolderSelected;
InvalidFolderEventArgs e = new InvalidFolderEventArgs(folderName, true);
h(this, e);
return (e.DismissDialog ? 0 : 1);
}
return 0;
default:
return 0;
}
}
///
/// Enables or disables the OK button in the dialog box.
///
/// The hwnd of the dialog box.
/// Whether or not the OK button should be enabled.
private static void EnableOk(IntPtr hwnd, bool isEnabled)
{
NativeMethods.SendMessage(hwnd, (uint)BrowseForFolderMessages.BFFM_ENABLEOK, (IntPtr)0, (IntPtr)(isEnabled ? 1 : 0));
}
///
/// Sets the status text of the dialog box.
///
/// The hwnd of the dialog box.
/// The status text to set.
private static void SetStatusText(IntPtr hwnd, string statusText)
{
NativeMethods.SendMessage(hwnd, (uint)BrowseForFolderMessages.BFFM_SETSTATUSTEXTW, (IntPtr)1, statusText);
}
#endregion Methods
}
///
/// Event arguments for when an invalid fodler is selected.
///
public class InvalidFolderEventArgs : EventArgs
{
///
/// Constructs an instance.
///
/// The name of the invalid folder.
/// Whether or not to dismiss the dialog.
public InvalidFolderEventArgs(string folderName, bool dismissDialog)
{
FolderName = folderName;
DismissDialog = dismissDialog;
}
///
/// Gets or sets whether or not to dismiss the dialog box.
///
public bool DismissDialog { get; set; }
///
/// Gets the name of the invalid folder.
///
public string FolderName { get; private set; }
}
///
/// Event arguments for when the has been initialized.
///
public class FolderBrowserDialogInitializedEventArgs : EventArgs
{
///
/// The HWND of the dialog box.
///
public readonly IntPtr hwnd;
///
/// Initializes a new instance of the class.
///
/// The HWND of the dialog box.
public FolderBrowserDialogInitializedEventArgs(IntPtr hwnd)
{
this.hwnd = hwnd;
}
}
}