如何configurationOpenFileDialog来select文件夹?

在VS .NET中,当你为一个项目select一个文件夹时,会显示一个看起来像OpenFileDialog或SaveFileDialog的对话框,但是被设置为只接受文件夹。 自从我看到这个以后,我就想知道它是如何完成的。 我知道FolderBrowserDialog,但我从来没有真正喜欢这个对话框。 它开始太小,不让我利用能够键入path的优势。

现在我几乎可以肯定,.NET没有办法做到这一点,但是我也很好奇你是如何从非托pipe代码中做到这一点的。 在从零开始完全重新实现对话框之后,如何修改对话框来实现这种行为?

我也想重申,我知道FolderBrowserDialog,但有时我不喜欢使用它,除了真正好奇如何configuration对话框以这种方式。 告诉我只使用FolderBrowserDialog可以帮助我保持一致的UI体验,但是不能满足我的好奇心,所以它不会成为答案。

这也不是一个Vista的具体事情; 自VS .NET 2003以来,我一直在看这个对话框,所以在Win2k和WinXP中是可行的。 这不是一个“我想知道正确的方法来做这个”的问题,而更多的是“我从VS 2003开始想要做这件事以来一直对此感到好奇”的问题。 我明白,Vista的文件对话框有一个选项可以做到这一点,但它一直在XP工作,所以我知道他们做了一些工作。 Vista的具体答案不是答案,因为Vista不存在于问题的上下文中。

更新:我接受Scott Wisniewski的答案,因为它带有一个工作示例,但是我认为Serge指向对话定制(从.NET中可以确定是讨厌的,但确实有效),Mark Ransom找出MS可能为此任务推出了一个自定义对话框。

我有一个我写的叫做OpenFileOrFolder对话框的对话框,允许你打开文件夹或文件。

如果将其AcceptFiles值设置为false,则它仅以接受文件夹模式运行。

你可以在这里从GitHub下载源代码

有Windows API代码包。 它有很多与shell相关的东西,包括CommonOpenFileDialog类(在Microsoft.WindowsAPICodePack.Dialogs命名空间中)。 这是一个完美的解决scheme – 通常只显示文件夹的打开对话框。

这是一个如何使用它的例子:

 CommonOpenFileDialog cofd = new CommonOpenFileDialog(); cofd.IsFolderPicker = true; cofd.ShowDialog(); 

不幸的是,微软不再提供这个软件包,但有几个人非正式上传到NuGet的二进制文件。 一个例子可以在这里find。 这个包只是特定于shell的东西。 如果你需要它,同一个用户有几个其他的包提供了更多的function在原始包。

您可以使用FolderBrowserDialogEx – 内置的FolderBrowserDialog的可重用派生。 这个允许你input一个path,甚至是一个UNCpath。 您也可以使用它浏览电脑或打印机。 就像内置的FBD一样工作,但是…更好。

(编辑:我应该指出,这个对话框可以设置为select文件或文件夹。)

完整的源代码(一个简短的C#模块)。 自由。 MS-公共许可证。

代码使用它:

 var dlg1 = new Ionic.Utils.FolderBrowserDialogEx(); dlg1.Description = "Select a folder to extract to:"; dlg1.ShowNewFolderButton = true; dlg1.ShowEditBox = true; //dlg1.NewStyle = false; dlg1.SelectedPath = txtExtractDirectory.Text; dlg1.ShowFullPathInEditBox = true; dlg1.RootFolder = System.Environment.SpecialFolder.MyComputer; // Show the FolderBrowserDialog. DialogResult result = dlg1.ShowDialog(); if (result == DialogResult.OK) { txtExtractDirectory.Text = dlg1.SelectedPath; } 

Ookii.Dialogs软件包包含新的(Vista风格)文件夹浏览器对话框的托pipe包装。 在较老的操作系统上,它也会降级。

最好使用FolderBrowserDialog。

 using (FolderBrowserDialog dlg = new FolderBrowserDialog()) { dlg.Description = "Select a folder"; if (dlg.ShowDialog() == DialogResult.OK) { MessageBox.Show("You selected: " + dlg.SelectedPath); } } 

经过几个小时的search,我发现这个答案 leetNightShade 工作的解决scheme 。

我相信有三件事使得这个解决scheme比所有其他解决scheme更好。

  1. 使用起来很简单。 它只需要在项目中包含两个文件(可以合并为一个文件)。
  2. 在XP或更老的系统上使用时,它会回落到标准的FolderBrowserDialog 。
  3. 作者允许您将代码用于您认为合适的任何目的。

    没有任何许可证,你可以自由采取和使用代码你会。

在这里下载代码。

好的,让我试着连接第一个点;-)用Spy ++或Winspector播放一点点,表明VS项目位置中的文件夹文本框是标准对话框的自定义。 它与标准文件对话框(如记事本)中的文件名文本框不同。

从那里开始,我想,VS隐藏文件名和文件types文本框/combobox,并使用自定义对话框模板在对话框的底部添加它自己的部分。

编辑:这是一个这样的自定义的例子,以及如何做到这一点(在Win32中。

m_ofn是文件对话框下的OPENFILENAME结构。 添加这两行:

  m_ofn.lpTemplateName = MAKEINTRESOURCE(IDD_FILEDIALOG_IMPORTXLIFF); m_ofn.Flags |= OFN_ENABLETEMPLATE; 

其中IDD_FILEDIALOG_IMPORTXLIFF是将在对话框的底部添加的自定义对话框模板。 看下面的红色部分。 替代文字http://apptranslator.com/_so/customizedfiledialog.png

在这种情况下,自定义的部分只是一个标签+一个超链接,但它可以是任何对话框。 它可以包含一个确定button,让我们validation文件夹的唯一select。

但是,我们如何摆脱对话标准部分的一些控制,我不知道。

在这个MSDN文章中的更多细节。

Exact Audio Copy在Windows XP上以这种方式工作。 标准文件打开对话框显示,但文件名字段包含文本“文件名将被忽略”。

只是在这里猜测,但我怀疑每次对对话框进行重大更改时,string被注入combobox编辑控件。 只要该字段不为空,并且对话标志设置为不检查文件的存在,对话框可以正常closures。

编辑:这比我想象的要容易得多。 这里是C ++ / MFC中的代码,你可以把它翻译成你select的环境。

 CFileDialog dlg(true, NULL, "Filename will be ignored", OFN_HIDEREADONLY | OFN_NOVALIDATE | OFN_PATHMUSTEXIST | OFN_READONLY, NULL, this); dlg.DoModal(); 

编辑2:这应该是C#的翻译,但我不是stream利的C#所以不要射我如果不工作。

 OpenFileDialog openFileDialog1 = new OpenFileDialog(); openFileDialog1.FileName = "Filename will be ignored"; openFileDialog1.CheckPathExists = true; openFileDialog1.ShowReadOnly = false; openFileDialog1.ReadOnlyChecked = true; openFileDialog1.CheckFileExists = false; openFileDialog1.ValidateNames = false; if(openFileDialog1.ShowDialog() == DialogResult.OK) { // openFileDialog1.FileName should contain the folder and a dummy filename } 

编辑3:最后在Visual Studio 2005中查看实际的对话框(我之前没有访问它)。 这不是标准的文件打开对话框! 如果您检查Spy ++中的窗口并将它们与打开的标准文件进行比较,您将看到结构和类名称不匹配。 当你仔细观察,你也可以发现对话内容的一些差异。 我的结论是,微软完全取代了Visual Studio中的标准对话框来赋予它这种能力。 我的解决scheme或类似的东西将尽可能接近,除非你愿意从头开始编写自己的代码。

您可以inheritance文件对话框并访问其所有控件。 每个都有一个可以用来获取窗口句柄的标识符。 然后,您可以显示和隐藏它们,从它们获取有关select更改等的消息。这一切都取决于您想要采取多less努力。

我们使用WTL类支持完成了我们的工作,并自定义了文件对话框以包含自定义地点栏和插件COM视图。

MSDN提供了如何使用Win32来完成这个任务的信息, 这篇CodeProject文章包含了一个例子 ,而这个CodeProject文章提供了一个.NET例子 。

你可以使用这样的代码

  • filter是隐藏文件
  • 文件名是隐藏第一个文本

要高级隐藏文本框的文件名你需要看看OpenFileDialogEx

代码:

 { openFileDialog2.FileName = "\r"; openFileDialog1.Filter = "folders|*.neverseenthisfile"; openFileDialog1.CheckFileExists = false; openFileDialog1.CheckPathExists = false; } 

我假设你使用VS2008在Vista上? 在这种情况下,我认为在调用Vista文件对话框IFileDialog时使用了FOS_PICKFOLDERS选项 。 我担心,在.NET代码中,这将涉及大量粗糙的P / Invoke互操作代码。

从Codeproject尝试这个(信贷给Nitron):

我想这是你正在谈论的同一个对话框 – 也许这将有助于如果你添加一个截图?

 bool GetFolder(std::string& folderpath, const char* szCaption=NULL, HWND hOwner=NULL) { bool retVal = false; // The BROWSEINFO struct tells the shell how it should display the dialog. BROWSEINFO bi; memset(&bi, 0, sizeof(bi)); bi.ulFlags = BIF_USENEWUI; bi.hwndOwner = hOwner; bi.lpszTitle = szCaption; // must call this if using BIF_USENEWUI ::OleInitialize(NULL); // Show the dialog and get the itemIDList for the selected folder. LPITEMIDLIST pIDL = ::SHBrowseForFolder(&bi); if(pIDL != NULL) { // Create a buffer to store the path, then get the path. char buffer[_MAX_PATH] = {'\0'}; if(::SHGetPathFromIDList(pIDL, buffer) != 0) { // Set the string value. folderpath = buffer; retVal = true; } // free the item id list CoTaskMemFree(pIDL); } ::OleUninitialize(); return retVal; } 

你可以使用这样的代码

filter是空string。 文件名是AnyName,但不是空白

  openFileDialog.FileName = "AnyFile"; openFileDialog.Filter = string.Empty; openFileDialog.CheckFileExists = false; openFileDialog.CheckPathExists = false; 

第一个scheme

我把它作为lyquidity.com的Bill Seddon(我没有从属关系)的.NET Win 7风格的文件夹select对话框的清理版本。 (我从本页的另一个答案中了解到他的代码)。 我写了我自己的,因为他的解决scheme需要一个额外的Reflection类,这是不需要这个重点的目的,使用基于exception的stream量控制,不caching其reflection调用的结果。 请注意,嵌套静态VistaDialog类是这样的,如果Show方法从不调用,它的静态reflectionvariables不会尝试填充。 如果没有足够高的Windows版本,它将回退到Vista之前的对话框。 应该在Windows 7,8,9,10及更高版本(理论上)工作。

 using System; using System.Reflection; using System.Windows.Forms; namespace ErikE.Shuriken { /// <summary> /// Present the Windows Vista-style open file dialog to select a folder. Fall back for older Windows Versions /// </summary> 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); } /// <param name="hWndOwner">Handle of the control or window to be the parent of the file dialog</param> /// <returns>true if the user clicks OK</returns> 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; } } } } } 

它在Windows窗体中的使用方式如下:

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

你当然可以玩弄它的select和它暴露的属性。 例如,它允许在Vista风格的对话框中多选。

第二个scheme

Simon Mourier给出了一个答案 ,说明如何直接使用互操作对Windows API进行完全相同的工作,尽pipe如果在旧版本的Windows中,他的版本将不得不补充使用旧式对话框。 不幸的是,当我完成我的解决scheme时,我还没有find他的职位。 命名你的毒药!

在Vista上,您可以使用File_PICKFOLDERS选项设置IFileDialog 。 这将导致显示类似OpenFileDialog的窗口,您可以在其中select文件夹:

 var frm = (IFileDialog)(new FileOpenDialogRCW()); uint options; frm.GetOptions(out options); options |= FOS_PICKFOLDERS; frm.SetOptions(options); if (frm.Show(owner.Handle) == S_OK) { IShellItem shellItem; frm.GetResult(out shellItem); IntPtr pszString; shellItem.GetDisplayName(SIGDN_FILESYSPATH, out pszString); this.Folder = Marshal.PtrToStringAuto(pszString); } 

对于较老的Windows,你总是可以select文件夹中的任何文件。

可以在这里find适用于.NET Framework 2.0及更高版本的工作示例。

我知道这个问题是关于OpenFileDialogconfiguration,但看到谷歌带我到这里我可能指出,如果你只是在寻找文件夹,你应该使用一个FolderBrowserDialog而不是由另一个SO问题回答下面

如何使用vb.net中的打开文件对话框指定path?