Click here to Skip to main content

Mark Salsbery - Professional Profile

Summary

46,265
Authority
9,966
Debator
157
Editor
818
Enquirer
139
Organiser
3,846
Participant
0
Author
No Biography provided
Member since Wednesday, September 27, 2006 (6 years, 8 months)
  • 31 Dec 2008: CodeProject MVP 2009
  • 31 Dec 2007: CodeProject MVP 2008

Contributions

Articles 0
Tech Blogs 0
Messages 10,564 (Master)
Q&A Questions 0
Q&A Answers 110
Tips/Tricks 0
Comments 217

Links

Groups

Below is the list of groups in which the member is participating

CodeProject Beta Testers

United States United States
No Biography provided
Group type: Collaborative Group
This member has Group Status: Member

members

Reputation

For more information on Reputation please see the FAQ.

Privileges

Members need to achieve at least one of the given member levels in the given reputation categories in order to perform a given action. For example, to store personal files in your account area you will need to achieve Platinum level in either the Author or Authority category. The "If Owner" column means that owners of an item automatically have the privilege, and the given member types also gain the privilege regardless of their reputation level.

ActionAuthorAuthorityDebatorEditorEnquirerOrganiserParticipantIf OwnerMember Types
Have no restrictions on voting frequencysilversilversilversilverAdmin
Store personal files in your account areaplatinumplatinumSitebuilder, Subeditor, Supporter, Editor, Staff
Have live hyperlinks in your biographybronzebronzebronzebronzebronzebronzesilverSubeditor, Protector, Editor, Staff, Admin
Edit a Question in Q&AsilversilversilversilverYesSubeditor, Protector, Editor, Admin
Edit an Answer in Q&AsilversilversilversilverYesSubeditor, Protector, Editor, Admin
Delete a Question in Q&AYesSubeditor, Protector, Editor, Admin
Delete an Answer in Q&AYesSubeditor, Protector, Editor, Admin
Report an Articlesilversilversilversilver
Approve/Disapprove a pending ArticlegoldgoldgoldgoldSubeditor, Mentor, Protector, Editor, Staff, Admin
Edit other members' articlesSubeditor, Protector, Editor, Admin
Create an article without requiring moderationplatinumSubeditor, Mentor, Protector, Editor, Staff, Admin
Report a forum messagesilversilverbronzeProtector, Editor, Admin
Create a new tagsilversilversilversilverAdmin
Modify a tagsilversilversilversilverAdmin

Actions with a green tick can be performed by this member.


 
You must Sign In to use this message board.
Search this forum  
GeneralA Folder Browser Common Dialog for WPF Pin
Wednesday, November 12, 2008 10:29am by Mark Salsbery
Do you need to present a folder browser dialog to the user from your WPF app?
 
Sure, you can use the FolderBrowserDialog Class[^], but what self-respecting WPF
programmer wants to pull in the System.Windows.Forms namespace
and its associated DLL(s)?
 
Not me. Smile | :)
 
Here's a simple wrapper around the SHBrowseForFolder Function[^] that I posted
on the WPF board.
 
I'm not sure it's article-worthy, so for now I'll just keep it here in case
someone asks for it.
 
On to the code, in its entirety, with an example of its usage following...
// .NET 3.5, Visual Studio 2008 SP1, Windows XP or better...

using System;
using System.Text;
using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Interop;
 

namespace CommonDialogWrappers
{
    /// <summary>
    /// Represents a common dialog box (Win32::SHBrowseForFolder()) that allows a user to select a folder.
    /// </summary>
    public class BrowseForFolderDialog
    {
        #region Public Properties
 
        /// <summary>
        /// Gets the current and or final selected folder path.
        /// </summary>
        public string SelectedFolder { get; protected set; }
        /// <summary>
        /// Gets or sets the string that is displayed above the tree view control in the dialog box (must set BEFORE calling ShowDialog()). 
        /// </summary>
        public string Title
        {
            get { return BrowseInfo.lpszTitle; }
            set { BrowseInfo.lpszTitle = value; }
        }
        /// <summary>
        /// Gets or sets the initially selected folder path.
        /// </summary>
        public string InitialFolder { get; set; }
        /// <summary>
        /// Gets or sets the initially selected and expanded folder path.  Overrides SelectedFolder.
        /// </summary>
        public string InitialExpandedFolder { get; set; }
        /// <summary>
        /// Gets or sets the text for the dialog's OK button.
        /// </summary>
        public string OKButtonText { get; set; }
        BROWSEINFOW browseInfo;
        /// <summary>
        /// Provides direct access to the Win32::SHBrowseForFolder() BROWSEINFO structure used to create the dialog in ShowDialog().
        /// </summary>
        public BROWSEINFOW BrowseInfo
        {
            get { return browseInfo; }
            protected set { browseInfo = value; }
        }
        /// <summary>
        /// Provides direct access to the ulFlags field of the Win32::SHBrowseForFolder() structure used to create the dialog in ShowDialog().
        /// </summary>
        public BrowseInfoFlags BrowserDialogFlags
        {
            get { return BrowseInfo.ulFlags; }
            set { BrowseInfo.ulFlags = value; }
        }
 
        #endregion
 

        #region Public Constructors
 
        /// <summary>
        /// Constructs a BrowseForFolderDialog with default BrowseInfoFlags set to BIF_NEWDIALOGSTYLE.
        /// </summary>
        public BrowseForFolderDialog()
        {
            BrowseInfo = new BROWSEINFOW();
            BrowseInfo.hwndOwner = IntPtr.Zero;
            BrowseInfo.pidlRoot = IntPtr.Zero;
            BrowseInfo.pszDisplayName = new String(' ', 260);
            BrowseInfo.lpszTitle = "Select a folder:";
            BrowseInfo.ulFlags = BrowseInfoFlags.BIF_NEWDIALOGSTYLE;
            BrowseInfo.lpfn = new BrowseCallbackProc(BrowseEventHandler);
            BrowseInfo.lParam = IntPtr.Zero;
            BrowseInfo.iImage = -1;
        }
 
        #endregion
 

        #region Public ShowDialog() Overloads
 
        /// <summary>
        /// Shows the dialog (Win32::SHBrowseForFolder()).
        /// </summary>
        public Nullable<bool> ShowDialog()
        {
            return PInvokeSHBrowseForFolder(null);
        }
 
        /// <summary>
        /// Shows the dialog (Win32::SHBrowseForFolder()) with its hwndOwner set to the handle of 'owner'.
        /// </summary>
        public Nullable<bool> ShowDialog(Window owner)
        {
            return PInvokeSHBrowseForFolder(owner);
        }
 
        #endregion
 

        #region PInvoke Stuff
 
        private Nullable<bool> PInvokeSHBrowseForFolder(Window owner)
        {
            WindowInteropHelper windowhelper;
            if (null != owner)
            {
                windowhelper = new WindowInteropHelper(owner);
                BrowseInfo.hwndOwner = windowhelper.Handle;
            }
 
            IntPtr pidl = SHBrowseForFolderW(browseInfo);
 
            if (IntPtr.Zero != pidl)
            {
                StringBuilder pathsb = new StringBuilder(260);
                if (false != SHGetPathFromIDList(pidl, pathsb))
                {
                    SelectedFolder = pathsb.ToString();
                    Marshal.FreeCoTaskMem(pidl);
                    return true;
                }
            }
 
            return false;
        }
 
        private int BrowseEventHandler(IntPtr hwnd, MessageFromBrowser uMsg, IntPtr lParam, IntPtr lpData)
        {
            switch (uMsg)
            {
                case MessageFromBrowser.BFFM_INITIALIZED:
                {
                    // The dialog box has finished initializing.
                    // lParam   Not used, value is NULL.

                    if (!string.IsNullOrEmpty(InitialExpandedFolder))
                        SendMessageW(hwnd, MessageToBrowser.BFFM_SETEXPANDED, new IntPtr(1), InitialExpandedFolder);
                    else if (!string.IsNullOrEmpty(InitialFolder))
                        SendMessageW(hwnd, MessageToBrowser.BFFM_SETSELECTIONW, new IntPtr(1), InitialFolder);
 
                    if (!string.IsNullOrEmpty(OKButtonText))
                        SendMessageW(hwnd, MessageToBrowser.BFFM_SETOKTEXT, new IntPtr(1), OKButtonText);
 
                    break;
                }
                case MessageFromBrowser.BFFM_SELCHANGED:
                {
                    // The selection has changed in the dialog box.
                    // lParam   A pointer to an item identifier list (PIDL) identifying the newly selected item.

                    StringBuilder pathsb = new StringBuilder(260);
                    if (false != SHGetPathFromIDList(lParam, pathsb))
                    {
                        SelectedFolder = pathsb.ToString();
                    }
 
                    break;
                }
                case MessageFromBrowser.BFFM_VALIDATEFAILEDA:   // ANSI
                {
                    // The user typed an invalid name into the dialog's edit box. A nonexistent folder is considered an invalid name.
                    // lParam   A pointer to a string containing the invalid name. An application can use this data in an error dialog informing the user that the name was not valid.
                    // Return zero to dismiss the dialog or nonzero to keep the dialog displayed
                    break;
                }
                case MessageFromBrowser.BFFM_VALIDATEFAILEDW:   // Unicode
                {
                    // The user typed an invalid name into the dialog's edit box. A nonexistent folder is considered an invalid name.
                    // lParam   A pointer to a string containing the invalid name. An application can use this data in an error dialog informing the user that the name was not valid.
                    // Return zero to dismiss the dialog or nonzero to keep the dialog displayed
                    break;
                }
                case MessageFromBrowser.BFFM_IUNKNOWN:
                {
                    // An IUnknown interface is available to the dialog box.
                    // lParam   A pointer to an IUnknown interface.
                    break;
                }
            }
 
            return 0;
        }
 
        public delegate int BrowseCallbackProc(IntPtr hwnd, MessageFromBrowser uMsg, IntPtr lParam, IntPtr lpData);
 
        [Flags]
        public enum BrowseInfoFlags : uint
        {
            /// <summary>
            /// No specified BIF_xxx flags.
            /// </summary>
            BIF_None = 0x0000,
            /// <summary>
            /// Only return file system directories. If the user selects folders that are not part of the file system, the OK button is grayed.
            /// </summary>
            BIF_RETURNONLYFSDIRS = 0x0001,  // For finding a folder to start document searching
            /// <summary>
            /// Do not include network folders below the domain level in the dialog box's tree view control.
            /// </summary>
            BIF_DONTGOBELOWDOMAIN = 0x0002, // For starting the Find Computer
            /// <summary>
            /// Include a status area in the dialog box. 
            /// </summary>
            BIF_STATUSTEXT = 0x0004,        // Top of the dialog has 2 lines of text for BROWSEINFO.lpszTitle and one line if
                                            // this flag is set.  Passing the message BFFM_SETSTATUSTEXTA to the hwnd can set the
                                            // rest of the text.  This is not used with BIF_USENEWUI and BROWSEINFO.lpszTitle gets
                                            // all three lines of text.
            /// <summary>
            /// Only return file system ancestors. An ancestor is a subfolder that is beneath the root folder in the namespace hierarchy.
            /// </summary>
            BIF_RETURNFSANCESTORS = 0x0008,
            /// <summary>
            /// Include an edit control in the browse dialog box that allows the user to type the name of an item.
            /// </summary>
            BIF_EDITBOX = 0x0010,           // Add an editbox to the dialog
            /// <summary>
            /// If the user types an invalid name into the edit box, the browse dialog box will call the application's BrowseCallbackProc with the BFFM_VALIDATEFAILED message. 
            /// </summary>
            BIF_VALIDATE = 0x0020,          // insist on valid result (or CANCEL)
            /// <summary>
            /// Use the new user interface. Setting this flag provides the user with a larger dialog box that can be resized.
            /// </summary>
            BIF_NEWDIALOGSTYLE = 0x0040,    // Use the new dialog layout with the ability to resize
                                            // Caller needs to call OleInitialize() before using this API
            /// <summary>
            /// Use the new user interface, including an edit box. This flag is equivalent to BIF_EDITBOX | BIF_NEWDIALOGSTYLE. 
            /// </summary>
            BIF_USENEWUI = BIF_NEWDIALOGSTYLE | BIF_EDITBOX,
            /// <summary>
            /// The browse dialog box can display URLs. The BIF_USENEWUI and BIF_BROWSEINCLUDEFILES flags must also be set. 
            /// </summary>
            BIF_BROWSEINCLUDEURLS = 0x0080, // Allow URLs to be displayed or entered. (Requires BIF_USENEWUI)
            /// <summary>
            /// When combined with BIF_NEWDIALOGSTYLE, adds a usage hint to the dialog box in place of the edit box.
            /// </summary>
            BIF_UAHINT = 0x0100,            // Add a UA hint to the dialog, in place of the edit box. May not be combined with BIF_EDITBOX
            /// <summary>
            /// Do not include the New Folder button in the browse dialog box.
            /// </summary>
            BIF_NONEWFOLDERBUTTON = 0x0200, // Do not add the "New Folder" button to the dialog.  Only applicable with BIF_NEWDIALOGSTYLE.
            /// <summary>
            /// When the selected item is a shortcut, return the PIDL of the shortcut itself rather than its target.
            /// </summary>
            BIF_NOTRANSLATETARGETS = 0x0400,// don't traverse target as shortcut
            /// <summary>
            /// Only return computers. If the user selects anything other than a computer, the OK button is grayed.
            /// </summary>
            BIF_BROWSEFORCOMPUTER = 0x1000, // Browsing for Computers.
            /// <summary>
            /// Only allow the selection of printers. If the user selects anything other than a printer, the OK button is grayed. 
            /// </summary>
            BIF_BROWSEFORPRINTER = 0x2000,  // Browsing for Printers
            /// <summary>
            /// The browse dialog box will display files as well as folders.
            /// </summary>
            BIF_BROWSEINCLUDEFILES = 0x4000,// Browsing for Everything
            /// <summary>
            /// The browse dialog box can display shareable resources on remote systems. 
            /// </summary>
            BIF_SHAREABLE = 0x8000          // sharable resources displayed (remote shares, requires BIF_USENEWUI)
        }
 
        [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
        public class BROWSEINFOW
        {
            /// <summary>
            /// A handle to the owner window for the dialog box.
            /// </summary>
            public IntPtr hwndOwner;
            /// <summary>
            /// A pointer to an item identifier list (PIDL) specifying the location of the root folder from which to start browsing. 
            /// </summary>
            public IntPtr pidlRoot;         // PCIDLIST_ABSOLUTE
            /// <summary>
            /// The address of a buffer to receive the display name of the folder selected by the user. The size of this buffer is assumed to be MAX_PATH characters.
            /// </summary>
            public string pszDisplayName;   // Output parameter! (length must be >= MAX_PATH)
            /// <summary>
            /// The address of a null-terminated string that is displayed above the tree view control in the dialog box. 
            /// </summary>
            public string lpszTitle;
            /// <summary>
            /// Flags specifying the options for the dialog box. 
            /// </summary>
            public BrowseInfoFlags ulFlags;
            /// <summary>
            /// A BrowseCallbackProc delegate that the dialog box calls when an event occurs.
            /// </summary>
            public BrowseCallbackProc lpfn;
            /// <summary>
            /// An application-defined value that the dialog box passes to the BrowseCallbackProc delegate, if one is specified.
            /// </summary>
            public IntPtr lParam;
            /// <summary>
            /// A variable to receive the image associated with the selected folder. The image is specified as an index to the system image list.
            /// </summary>
            public int iImage;              // Output parameter!
        }
 
        // message from browser
        public enum MessageFromBrowser : uint
        {
            /// <summary>
            /// The dialog box has finished initializing.
            /// </summary>
            BFFM_INITIALIZED = 1,
            /// <summary>
            /// The selection has changed in the dialog box.
            /// </summary>
            BFFM_SELCHANGED = 2,
            /// <summary>
            /// (ANSI) The user typed an invalid name into the dialog's edit box. A nonexistent folder is considered an invalid name.
            /// </summary>
            BFFM_VALIDATEFAILEDA = 3,
            /// <summary>
            /// (Unicode) The user typed an invalid name into the dialog's edit box. A nonexistent folder is considered an invalid name.
            /// </summary>
            BFFM_VALIDATEFAILEDW = 4,
            /// <summary>
            /// An IUnknown interface is available to the dialog box.
            /// </summary>
            BFFM_IUNKNOWN = 5
        }
 
        // messages to browser
        public enum MessageToBrowser : uint
        {
 
            /// <summary>
            /// Win32 API macro - start of user defined window message range.
            /// </summary>
            WM_USER = 0x0400,
            /// <summary>
            /// (ANSI) Sets the status text. Set lpData to point to a null-terminated string with the desired text. 
            /// </summary>
            BFFM_SETSTATUSTEXTA = WM_USER + 100,
            /// <summary>
            /// Enables or disables the dialog box's OK button.  lParam - To enable, set to a nonzero value. To disable, set to zero.
            /// </summary>
            BFFM_ENABLEOK = WM_USER + 101,
            /// <summary>
            /// (ANSI) Specifies the path of a folder to select. 
            /// </summary>
            BFFM_SETSELECTIONA = WM_USER + 102,
            /// <summary>
            /// (Unicode) Specifies the path of a folder to select. 
            /// </summary>
            BFFM_SETSELECTIONW = WM_USER + 103,
            /// <summary>
            /// (Unicode) Sets the status text. Set lpData to point to a null-terminated string with the desired text. 
            /// </summary>
            BFFM_SETSTATUSTEXTW = WM_USER + 104,
            /// <summary>
            /// Sets the text that is displayed on the dialog box's OK button.
            /// </summary>
            BFFM_SETOKTEXT = WM_USER + 105,       // Unicode only
            /// <summary>
            /// Specifies the path of a folder to expand in the Browse dialog box. 
            /// </summary>
            BFFM_SETEXPANDED = WM_USER + 106      // Unicode only
        }
 
        [DllImport("shell32.dll")]
        private static extern IntPtr SHBrowseForFolderW([MarshalAs(UnmanagedType.LPStruct), In, Out] BROWSEINFOW bi);
        [DllImport("shell32.dll")]
        private static extern bool SHGetPathFromIDList(IntPtr pidl, StringBuilder path);
        [DllImport("user32.dll")]
        public static extern IntPtr SendMessageW(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);
        [DllImport("user32.dll")]
        public static extern IntPtr SendMessageW(IntPtr hWnd, MessageToBrowser msg, IntPtr wParam, [MarshalAs(UnmanagedType.LPWStr)] string str);
 
        #endregion
    }
}
Example:
    CommonDialogWrappers.BrowseForFolderDialog dlg = new CommonDialogWrappers.BrowseForFolderDialog();
    dlg.Title = "Select a folder and click OK!";
    dlg.InitialExpandedFolder = @"c:\";
    dlg.OKButtonText = "OK!";
    if (true == dlg.ShowDialog(this))
    {
        // Do something with the selected folder...
        MessageBox.Show(dlg.SelectedFolder, "Selected Folder");
    }

 
Mark Salsbery
Microsoft MVP - Visual C++

Java | [Coffee]

GeneralRe: A Folder Browser Common Dialog for WPF Pinmembersonamsingh_1914-Feb-09 1:10 
GeneralRe: A Folder Browser Common Dialog for WPF PinmvpMark Salsbery14-Feb-09 6:51 
GeneralRe: A Folder Browser Common Dialog for WPF Pinmemberjmcc2k5-Apr-09 18:30 
GeneralRe: A Folder Browser Common Dialog for WPF PinmemberWes Aday27-Feb-09 4:44 
GeneralRe: A Folder Browser Common Dialog for WPF Pinmemberrhyous8-Feb-10 4:58 
GeneralRe: A Folder Browser Common Dialog for WPF PinmemberMark Salsbery9-Feb-10 11:40 
GeneralRe: A Folder Browser Common Dialog for WPF Pinmemberrhyous10-Feb-10 16:56 
GeneralRe: A Folder Browser Common Dialog for WPF Pinmemberbscaer16-Feb-10 4:26 
GeneralRe: A Folder Browser Common Dialog for WPF PinmemberMark Salsbery25-Feb-10 6:49 
GeneralRe: A Folder Browser Common Dialog for WPF Pinmemberjmarden1-Jul-10 7:00 
GeneralRe: A Folder Browser Common Dialog for WPF PinmemberErik Rude4-May-12 1:21 
 
GeneralTesting...Testing... Pin
Tuesday, July 17, 2007 9:29am by Mark Salsbery
Check...Check One...Check...Is this thing on?
Generalgood grief Pinmvpled mike18-Jul-07 7:17 
GeneralRe: good grief PinmemberMark Salsbery18-Jul-07 7:21 
GeneralRe: good grief PinmvpRajesh R Subramanian18-Oct-08 2:41 
GeneralRe: good grief PinmvpMark Salsbery18-Oct-08 8:29 
GeneralRe: good grief PinmemberJim Crafton27-Jul-07 9:04 
GeneralRe: good grief PinmemberMark Salsbery27-Jul-07 15:40 
GeneralRe: good grief Pinmvpled mike30-Jul-07 5:12 
GeneralRe: good grief PinmemberJim Crafton30-Jul-07 5:20 
GeneralRe: good grief PinmvpRajesh R Subramanian2-Mar-08 21:08 
GeneralPretty bad day on CP today Pinmvpled mike2-Aug-07 7:35 
GeneralRe: Pretty bad day on CP today PinmemberMark Salsbery2-Aug-07 7:43 
GeneralRe: Pretty bad day on CP today Pinmvpled mike2-Aug-07 9:06 
JokeRe: Testing...Testing... PinmemberThatsAlok7-Aug-07 23:12 
GeneralAnother fishing derby day Pinmvpled mike11-Sep-07 7:06 
GeneralRe: Another fishing derby day PinmemberMark Salsbery11-Sep-07 13:40 
GeneralRe: Another fishing derby day Pinmvpled mike12-Sep-07 5:06 
GeneralRe: Another fishing derby day PinmemberMark Salsbery11-Oct-07 12:35 
GeneralRe: Another fishing derby day Pinmvpled mike12-Oct-07 4:58 
GeneralRe: Another fishing derby day PinmemberMark Salsbery12-Oct-07 5:05 
GeneralRe: Another fishing derby day PinmvpRajesh R Subramanian25-Sep-08 21:21 
GeneralRe: Another fishing derby day PinmvpMark Salsbery26-Sep-08 4:37 
GeneralCode Project Forums on a Friday night Pinmvpled mike12-Oct-07 21:13 
GeneralRe: Code Project Forums on a Friday night PinmemberMark Salsbery13-Oct-07 6:08 
GeneralRe: Code Project Forums on a Friday night PinmvpRajesh R Subramanian25-Sep-08 19:43 
Generalheheheehehe Pinmvpled mike1-Nov-07 7:53 
GeneralRe: heheheehehe PinmemberMark Salsbery1-Nov-07 8:01 
GeneralYou're ignoring me Pinmvpled mike14-Nov-07 11:09 
GeneralRe: You're ignoring me PinmemberMark Salsbery14-Nov-07 11:13 
GeneralRe: You're ignoring me Pinmvpled mike14-Nov-07 11:29 
GeneralRe: You're ignoring me PinmemberMark Salsbery14-Nov-07 11:44 
GeneralWow... OMG Pinmvpled mike5-Dec-07 5:50 
GeneralI totally screwed up that Is process running thread Pinmvpled mike26-Mar-08 11:52 
GeneralCheck this post out Pinmvpled mike9-Oct-08 5:52 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   


Advertise | Privacy | Mobile
Web01 | 2.6.130617.1 | Last Updated 18 Jun 2013
Copyright © CodeProject, 1999-2013
All Rights Reserved. Terms of Use
Layout: fixed | fluid