从PropertyGrid显示详细的文件夹浏览器

请注意,这不是一个重复的问题。

如何在PropertyGrid中显示详细的FolderBrowser,如下图所示(来自具有省略号的字段/属性…) 详细文件夹浏览器

运用

[EditorAttribute(typeof(System.Windows.Forms.Design.FileNameEditor), typeof(System.Drawing.Design.UITypeEditor))] [EditorAttribute(typeof(System.Windows.Forms.Design.FolderNameEditor), typeof(System.Drawing.Design.UITypeEditor))] 

我们得到了minimalistic文件夹浏览器

简约文件夹浏览器

这是一个自定义UITypeEditor,允许您使用Vista文件夹浏览器:

在此处输入图像描述

你可以像任何其他编辑器一样使用它:

 [EditorAttribute(typeof(FolderNameEditor2), typeof(System.Drawing.Design.UITypeEditor))] 

它依赖于我为此场合编写的自定义FolderBrowser2类。 当然,这仅适用于Windows Vista及更高版本。 在以前的Windows版本中,除了简单的浏览器之外没有其他文件夹浏览器。

 public class FolderNameEditor2 : UITypeEditor { public override UITypeEditorEditStyle GetEditStyle(ITypeDescriptorContext context) { return UITypeEditorEditStyle.Modal; } public override object EditValue(ITypeDescriptorContext context, IServiceProvider provider, object value) { FolderBrowser2 browser = new FolderBrowser2(); if (value != null) { browser.DirectoryPath = string.Format("{0}", value); } if (browser.ShowDialog(null) == DialogResult.OK) return browser.DirectoryPath; return value; } } public class FolderBrowser2 { public string DirectoryPath { get; set; } public DialogResult ShowDialog(IWin32Window owner) { IntPtr hwndOwner = owner != null ? owner.Handle : GetActiveWindow(); IFileOpenDialog dialog = (IFileOpenDialog)new FileOpenDialog(); try { IShellItem item; if (!string.IsNullOrEmpty(DirectoryPath)) { IntPtr idl; uint atts = 0; if (SHILCreateFromPath(DirectoryPath, out idl, ref atts) == 0) { if (SHCreateShellItem(IntPtr.Zero, IntPtr.Zero, idl, out item) == 0) { dialog.SetFolder(item); } Marshal.FreeCoTaskMem(idl); } } dialog.SetOptions(FOS.FOS_PICKFOLDERS | FOS.FOS_FORCEFILESYSTEM); uint hr = dialog.Show(hwndOwner); if (hr == ERROR_CANCELLED) return DialogResult.Cancel; if (hr != 0) return DialogResult.Abort; dialog.GetResult(out item); string path; item.GetDisplayName(SIGDN.SIGDN_FILESYSPATH, out path); DirectoryPath = path; return DialogResult.OK; } finally { Marshal.ReleaseComObject(dialog); } } [DllImport("shell32.dll")] private static extern int SHILCreateFromPath([MarshalAs(UnmanagedType.LPWStr)] string pszPath, out IntPtr ppIdl, ref uint rgflnOut); [DllImport("shell32.dll")] private static extern int SHCreateShellItem(IntPtr pidlParent, IntPtr psfParent, IntPtr pidl, out IShellItem ppsi); [DllImport("user32.dll")] private static extern IntPtr GetActiveWindow(); private const uint ERROR_CANCELLED = 0x800704C7; [ComImport] [Guid("DC1C5A9C-E88A-4dde-A5A1-60F82A20AEF7")] private class FileOpenDialog { } [ComImport] [Guid("42f85136-db7e-439c-85f1-e4075d135fc8")] [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] private interface IFileOpenDialog { [PreserveSig] uint Show([In] IntPtr parent); // IModalWindow void SetFileTypes(); // not fully defined void SetFileTypeIndex([In] uint iFileType); void GetFileTypeIndex(out uint piFileType); void Advise(); // not fully defined void Unadvise(); void SetOptions([In] FOS fos); void GetOptions(out FOS pfos); void SetDefaultFolder(IShellItem psi); void SetFolder(IShellItem psi); void GetFolder(out IShellItem ppsi); void GetCurrentSelection(out IShellItem ppsi); void SetFileName([In, MarshalAs(UnmanagedType.LPWStr)] string pszName); void GetFileName([MarshalAs(UnmanagedType.LPWStr)] out string pszName); void SetTitle([In, MarshalAs(UnmanagedType.LPWStr)] string pszTitle); void SetOkButtonLabel([In, MarshalAs(UnmanagedType.LPWStr)] string pszText); void SetFileNameLabel([In, MarshalAs(UnmanagedType.LPWStr)] string pszLabel); void GetResult(out IShellItem ppsi); void AddPlace(IShellItem psi, int alignment); void SetDefaultExtension([In, MarshalAs(UnmanagedType.LPWStr)] string pszDefaultExtension); void Close(int hr); void SetClientGuid(); // not fully defined void ClearClientData(); void SetFilter([MarshalAs(UnmanagedType.Interface)] IntPtr pFilter); void GetResults([MarshalAs(UnmanagedType.Interface)] out IntPtr ppenum); // not fully defined void GetSelectedItems([MarshalAs(UnmanagedType.Interface)] out IntPtr ppsai); // not fully defined } [ComImport] [Guid("43826D1E-E718-42EE-BC55-A1E261C37BFE")] [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] private interface IShellItem { void BindToHandler(); // not fully defined void GetParent(); // not fully defined void GetDisplayName([In] SIGDN sigdnName, [MarshalAs(UnmanagedType.LPWStr)] out string ppszName); void GetAttributes(); // not fully defined void Compare(); // not fully defined } private enum SIGDN : uint { SIGDN_DESKTOPABSOLUTEEDITING = 0x8004c000, SIGDN_DESKTOPABSOLUTEPARSING = 0x80028000, SIGDN_FILESYSPATH = 0x80058000, SIGDN_NORMALDISPLAY = 0, SIGDN_PARENTRELATIVE = 0x80080001, SIGDN_PARENTRELATIVEEDITING = 0x80031001, SIGDN_PARENTRELATIVEFORADDRESSBAR = 0x8007c001, SIGDN_PARENTRELATIVEPARSING = 0x80018001, SIGDN_URL = 0x80068000 } [Flags] private enum FOS { FOS_ALLNONSTORAGEITEMS = 0x80, FOS_ALLOWMULTISELECT = 0x200, FOS_CREATEPROMPT = 0x2000, FOS_DEFAULTNOMINIMODE = 0x20000000, FOS_DONTADDTORECENT = 0x2000000, FOS_FILEMUSTEXIST = 0x1000, FOS_FORCEFILESYSTEM = 0x40, FOS_FORCESHOWHIDDEN = 0x10000000, FOS_HIDEMRUPLACES = 0x20000, FOS_HIDEPINNEDPLACES = 0x40000, FOS_NOCHANGEDIR = 8, FOS_NODEREFERENCELINKS = 0x100000, FOS_NOREADONLYRETURN = 0x8000, FOS_NOTESTFILECREATE = 0x10000, FOS_NOVALIDATE = 0x100, FOS_OVERWRITEPROMPT = 2, FOS_PATHMUSTEXIST = 0x800, FOS_PICKFOLDERS = 0x20, FOS_SHAREAWARE = 0x4000, FOS_STRICTFILETYPES = 4 } } 

这是一个解决方案,与Simon Mourier的答案完成相同的工作,而不直接使用互操作(.Net为您处理)。 如果没有足够高的Windows版本,它还具有回退到Vista之前对话框的附加function。 应该在Windows 7,8,10和更高版本(理论上)工作。

有一个很大的警告:微软可以随意改变他们的内部课程。 由于这使用Reflection来破坏范围规则,并且不支持此操作(Microsoft只对公开的内容感兴趣),如果Microsoft确实进行了更改,此代码可能会中断。

 using System; using System.Reflection; using System.Windows.Forms; namespace ErikE.Shuriken { ///  /// Present the Windows Vista-style open file dialog to select a folder. Fall back for older Windows Versions ///  public class FolderSelectDialog { private string _initialDirectory; private string _title; private string _fileName = ""; public string InitialDirectory { get { return string.IsNullOrEmpty(_initialDirectory) ? Environment.CurrentDirectory : _initialDirectory; } set { _initialDirectory = value; } } public string Title { get { return _title ?? "Select a folder"; } set { _title = value; } } public string FileName { get { return _fileName; } } public bool Show() { return Show(IntPtr.Zero); } /// Handle of the control or window to be the parent of the file dialog /// true if the user clicks OK public bool Show(IntPtr hWndOwner) { var result = Environment.OSVersion.Version.Major >= 6 ? VistaDialog.Show(hWndOwner, InitialDirectory, Title) : ShowXpDialog(hWndOwner, InitialDirectory, Title); _fileName = result.FileName; return result.Result; } private struct ShowDialogResult { public bool Result { get; set; } public string FileName { get; set; } } private static ShowDialogResult ShowXpDialog(IntPtr ownerHandle, string initialDirectory, string title) { var folderBrowserDialog = new FolderBrowserDialog { Description = title, SelectedPath = initialDirectory, ShowNewFolderButton = false }; var dialogResult = new ShowDialogResult(); if (folderBrowserDialog.ShowDialog(new WindowWrapper(ownerHandle)) == DialogResult.OK) { dialogResult.Result = true; dialogResult.FileName = folderBrowserDialog.SelectedPath; } return dialogResult; } private static class VistaDialog { private const string c_foldersFilter = "Folders|\n"; private const BindingFlags c_flags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic; private readonly static Assembly s_windowsFormsAssembly = typeof(FileDialog).Assembly; private readonly static Type s_iFileDialogType = s_windowsFormsAssembly.GetType("System.Windows.Forms.FileDialogNative+IFileDialog"); private readonly static MethodInfo s_createVistaDialogMethodInfo = typeof(OpenFileDialog).GetMethod("CreateVistaDialog", c_flags); private readonly static MethodInfo s_onBeforeVistaDialogMethodInfo = typeof(OpenFileDialog).GetMethod("OnBeforeVistaDialog", c_flags); private readonly static MethodInfo s_getOptionsMethodInfo = typeof(FileDialog).GetMethod("GetOptions", c_flags); private readonly static MethodInfo s_setOptionsMethodInfo = s_iFileDialogType.GetMethod("SetOptions", c_flags); private readonly static uint s_fosPickFoldersBitFlag = (uint) s_windowsFormsAssembly .GetType("System.Windows.Forms.FileDialogNative+FOS") .GetField("FOS_PICKFOLDERS") .GetValue(null); private readonly static ConstructorInfo s_vistaDialogEventsConstructorInfo = s_windowsFormsAssembly .GetType("System.Windows.Forms.FileDialog+VistaDialogEvents") .GetConstructor(c_flags, null, new[] { typeof(FileDialog) }, null); private readonly static MethodInfo s_adviseMethodInfo = s_iFileDialogType.GetMethod("Advise"); private readonly static MethodInfo s_unAdviseMethodInfo = s_iFileDialogType.GetMethod("Unadvise"); private readonly static MethodInfo s_showMethodInfo = s_iFileDialogType.GetMethod("Show"); public static ShowDialogResult Show(IntPtr ownerHandle, string initialDirectory, string title) { var openFileDialog = new OpenFileDialog { AddExtension = false, CheckFileExists = false, DereferenceLinks = true, Filter = c_foldersFilter, InitialDirectory = initialDirectory, Multiselect = false, Title = title }; var iFileDialog = s_createVistaDialogMethodInfo.Invoke(openFileDialog, new object[] { }); s_onBeforeVistaDialogMethodInfo.Invoke(openFileDialog, new[] { iFileDialog }); s_setOptionsMethodInfo.Invoke(iFileDialog, new object[] { (uint) s_getOptionsMethodInfo.Invoke(openFileDialog, new object[] { }) | s_fosPickFoldersBitFlag }); var adviseParametersWithOutputConnectionToken = new[] { s_vistaDialogEventsConstructorInfo.Invoke(new object[] { openFileDialog }), 0U }; s_adviseMethodInfo.Invoke(iFileDialog, adviseParametersWithOutputConnectionToken); try { int retVal = (int) s_showMethodInfo.Invoke(iFileDialog, new object[] { ownerHandle }); return new ShowDialogResult { Result = retVal == 0, FileName = openFileDialog.FileName }; } finally { s_unAdviseMethodInfo.Invoke(iFileDialog, new[] { adviseParametersWithOutputConnectionToken[1] }); } } } // Wrap an IWin32Window around an IntPtr private class WindowWrapper : IWin32Window { private readonly IntPtr _handle; public WindowWrapper(IntPtr handle) { _handle = handle; } public IntPtr Handle { get { return _handle; } } } } } 

我将此开发为lyquidity.com的Bill Seddon的.NET Win 7式文件夹选择对话框的清理版本(我没有隶属关系)。 我编写了自己的解决方案,因为他的解决方案需要一个额外的Reflection类,这个聚类目的不需要,使用基于exception的流控制,不会缓存其reflection调用的结果。 请注意,嵌套的静态VistaDialog类是这样的,如果从不调用Show方法,它的静态reflection变量不会尝试填充。

在Windows窗体中使用它是这样的:

 var dialog = new FolderSelectDialog { InitialDirectory = musicFolderTextBox.Text Title = "Select a folder to import music from" }; if (dialog.Show(Handle)) { musicFolderTextBox.Text = dialog.FileName; } 

您当然可以使用它的选项以及它所暴露的属性。 例如,它允许在Vista风格的对话框中进行多选。