如何配置OpenFileDialog来选择文件夹?

在VS . net中,当你为项目选择文件夹时,会显示一个类似于OpenFileDialog或SaveFileDialog的对话框,但设置为只接受文件夹。自从我看到这个,我就想知道它是怎么做到的。我知道FolderBrowserDialog,但我从来都不喜欢那个对话框。它开始太小,不让我利用能够键入路径的优势。

到目前为止,我几乎可以肯定在。net中没有办法做到这一点,但我很好奇在非托管代码中如何做到这一点。如果不从头开始完全重新实现对话框,如何修改对话框以使其具有这种行为呢?

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

这也不是vista特有的东西;自从VS . net 2003以来,我一直在看到这个对话框,所以它在Win2k和WinXP中是可行的。这不是一个“我想知道正确的方法来做这个”的问题,而更多的是一个“我对这个很好奇,因为我第一次想在VS 2003中做这个”的问题。我知道Vista的文件对话框有一个选项可以这样做,但它一直在XP中工作,所以我知道他们做了某物来让它工作。特定于Vista的答案不是答案,因为Vista不存在于问题上下文中。

更新:我接受Scott Wisniewski的答案,因为它附带了一个工作示例,但我认为Serge值得赞扬,因为他指出了对话框定制(这在。net中确实很讨厌,但它does work), Mark Ransom指出MS可能为这个任务滚动了一个自定义对话框。

240982 次浏览

最好使用FolderBrowserDialog。

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

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

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

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

编辑:这里有一个这样的自定义示例以及如何进行定制(在Win32中)。没有。net):

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

  m_ofn.lpTemplateName = MAKEINTRESOURCE(IDD_FILEDIALOG_IMPORTXLIFF);
m_ofn.Flags |= OFN_ENABLETEMPLATE;
其中IDD_FILEDIALOG_IMPORTXLIFF是一个自定义对话框模板,将被添加到对话框的底部。请看下面红色部分。 alt text < br > (来源:apptranslator.com) < /订阅>

在这种情况下,自定义部分只是一个标签+一个超链接,但它可以是任何对话框。它可以包含一个OK按钮,让我们只验证文件夹选择。

但是我们如何在对话框的标准部分中去掉一些控件,我不知道。

更多细节在这篇MSDN文章中。

试试下面这个来自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;
}

您可以子类化文件对话框并获得对其所有控件的访问权。每个都有一个标识符,可用于获取其窗口句柄。然后你可以显示和隐藏它们,从它们那里获得关于选择更改的消息,等等。这完全取决于你想付出多少努力。

我们使用了WTL类支持,并自定义了文件对话框,以包括自定义位置栏和插件COM视图。

MSDN提供了关于如何使用Win32、这篇CodeProject文章包含了一个示例这篇CodeProject文章提供了一个。net示例来做到这一点的信息。

精确的音频副本在Windows XP上是这样工作的。标准文件打开对话框显示,但文件名字段包含文本“filename将被忽略”。

这里只是猜测,但我怀疑每当对对话框进行重大更改时,字符串都会注入到组合框编辑控件中。只要该字段不为空,并且对话框标志设置为不检查文件的存在,对话框就可以正常关闭。

编辑:这比我想象的要简单得多。下面是c++ /MFC中的代码,您可以将其转换到您选择的环境中。

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

编辑2:这应该是翻译到c#,但我不流利的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
}

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

Ookii。对话框包包含新的(vista风格)文件夹浏览器对话框的托管包装器。它在旧的操作系统上也会优雅地退化。

您可以使用如下代码

  • 过滤器为hide files
  • 文件名是hide first text

到文本框的高级隐藏文件名你需要看 OpenFileDialogEx < br > < / p >

代码:< br >

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

我写了一个名为OpenFileOrFolder的对话框,它允许你打开文件夹或文件。

如果将其AcceptFiles值设置为false,则它只在接受文件夹模式下操作。

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

你可以使用FolderBrowserDialogEx - 内置FolderBrowserDialog的可重用衍生物。这个选项允许您输入路径,甚至是UNC路径。你也可以用它浏览电脑或打印机。工作原理就像内置的FBD,但是…更好。< / p >

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

完整的源代码(一个简短的c#模块)。免费的。微软公开许可。

使用它的代码:

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

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

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

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

不幸的是,微软不再发布这个软件包,但一些人已经非正式地将二进制文件上传到NuGet。一个例子可以在在这里中找到。这个包只是特定于shell的东西。如果您需要它,同一用户还有几个其他的包,这些包在原始包中提供了更多的功能。

在Vista上,你可以使用带有FOS_PICKFOLDERS选项集的IFileDialog。这将导致显示类似于openfiledialog的窗口,您可以在其中选择文件夹:

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系统,你可以选择文件夹中的任何文件。

在. net Framework 2.0及更高版本上工作的示例可以在在这里中找到。

您可以使用这样的代码

过滤器为空字符串。 文件名为AnyName,不为空

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

经过几个小时的搜索,我找到了这个答案leetNightShade可行的解决方案

我认为有三件事使这个解决方案比其他解决方案要好得多。

  1. 使用简单。 它只需要你在你的项目中包含两个文件(无论如何都可以合并为一个文件)
  2. 当在XP或更老的系统上使用时,它会回到标准FolderBrowserDialog
  3. 作者授权你将代码用于任何你认为合适的目的。

    没有什么许可可以让你随心所欲地使用代码。

    李< /引用> < / >

下载代码在这里

第一个解决方案

我将其开发为Bill Seddon的lyquidity.com.NET win7风格的文件夹选择对话框的清理版本(我没有隶属关系)。(我从另一个答案在本页了解到他的代码)。我自己写的,因为他的解决方案需要一个额外的反射类,而这个类对于这个关注的目的不需要,使用基于异常的流控制,不缓存其反射调用的结果。请注意,嵌套的静态VistaDialog类是这样的,如果Show方法从未被调用,它的静态反射变量不会被填充。如果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;
}

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

第二个解决方案

西蒙·穆里耶给出了答案,它展示了如何直接对Windows API使用互操作来完成完全相同的工作,尽管如果在旧版本的Windows中,他的版本必须补充使用旧样式的对话框。不幸的是,当我制定解决方案时,我还没有找到他的帖子。说出你的毒药!

我知道这个问题是关于OpenFileDialog的配置,但看到谷歌把我带到这里,我不妨指出,如果你只是在寻找文件夹,你应该使用FolderBrowserDialog,而不是下面另一个SO问题的答案

如何在vb.net中使用打开文件对话框指定路径

Ookii对话框的WPF库有一个类,它为WPF提供了文件夹浏览器对话框的实现。

https://github.com/augustoproiete/ookii-dialogs-wpf

Ookii文件夹浏览器对话框

还有一个版本适用于Windows窗体