371 lines
17 KiB
C#
371 lines
17 KiB
C#
using System;
|
|
using System.Text;
|
|
using System.Runtime.InteropServices;
|
|
using System.Windows;
|
|
using System.Windows.Interop;
|
|
|
|
namespace DTS.Common.Dialogs
|
|
{
|
|
/// <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 => 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; }
|
|
|
|
/// <summary>
|
|
/// Provides direct access to the Win32::SHBrowseForFolder() BROWSEINFO structure used to create the dialog in ShowDialog().
|
|
/// </summary>
|
|
public BROWSEINFOW BrowseInfo { get; protected set; }
|
|
|
|
/// <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 => 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;
|
|
}
|
|
|
|
var pidl = SHBrowseForFolderW(BrowseInfo);
|
|
|
|
if (IntPtr.Zero != pidl)
|
|
{
|
|
var pathsb = new StringBuilder(260);
|
|
if (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.
|
|
|
|
var pathsb = new StringBuilder(260);
|
|
if (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
|
|
}
|
|
} |