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