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

问题描述 投票:243回答:17

在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令人讨厌但它确实有效)和Mark Ransom弄清楚MS是值得赞扬的。可能为此任务滚动了一个自定义对话框。

.net windows winapi openfiledialog
17个回答
57
投票

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

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

You can download the source from GitHub here


10
投票

你可以使用这样的代码

  • 过滤器是隐藏文件
  • 文件名是隐藏第一个文本

要高级隐藏文本框的文件名,您需要查看this MSDN article

代码:

MSDN

5
投票

我假设你使用VS2008在Vista上?在这种情况下,我认为在调用Vista文件对话框this CodeProject article includes an example时正在使用this CodeProject article provides a .NET example。我担心在.NET代码中,这将涉及到大量粗糙的P / Invoke互操作代码才能正常工作。


2
投票

第一解决方案

我把它作为OpenFileDialogEx的Bill Seddon的清理版{ openFileDialog2.FileName = "\r"; openFileDialog1.Filter = "folders|*.neverseenthisfile"; openFileDialog1.CheckFileExists = false; openFileDialog1.CheckPathExists = false; } 开发了(我没有隶属关系)。 (我从FOS_PICKFOLDERS option那里学到了他的代码)。我编写了自己的解决方案,因为他的解决方案需要一个额外的Reflection类,这个聚类目的不需要,使用基于异常的流控制,不会缓存其反射调用的结果。请注意,嵌套的静态IFileDialog类是这样的,如果永远不调用.NET Win 7-style folder select dialog方法,它的静态反射变量不会尝试填充。如果没有足够高的Windows版本,它会回到Vista之前的对话框。应该适用于Windows 7,8,9,10及更高版本(理论上)。

lyquidity.com

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

another answer on this page

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

二解决方案

VistaDialog显示如何使用interop直接对Windows API执行完全相同的工作,尽管如果在旧版本的Windows中,必须补充使用旧版本对话框的版本。不幸的是,当我编写解决方案时,我还没有找到他的帖子。说出你的毒药!


1
投票

试试这个来自Show(归功于Nitron):

我认为这是你正在谈论的同一个对话 - 如果你添加截图,它可能会有所帮助吗?

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

1
投票

在Vista上,您可以使用var dialog = new FolderSelectDialog { InitialDirectory = musicFolderTextBox.Text, Title = "Select a folder to import music from" }; if (dialog.Show(Handle)) { musicFolderTextBox.Text = dialog.FileName; } 和FOS_PICKFOLDERS选项集。这将导致显示类似OpenFileDialog的窗口,您可以在其中选择文件夹:

Simon Mourier gave an answer

对于较旧的Windows,您可以随时选择技巧来选择文件夹中的任何文件。

可以在Codeproject找到适用于.NET Framework 2.0及更高版本的工作示例。


1
投票

你可以使用这样的代码

过滤器是空字符串。文件名是AnyName但不是空白

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

1
投票

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

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

here

还有一个适用于 openFileDialog.FileName = "AnyFile"; openFileDialog.Filter = string.Empty; openFileDialog.CheckFileExists = false; openFileDialog.CheckPathExists = false; 的版本。


0
投票

我知道问题是关于Ookii Dialogs for WPF的配置,但看到谷歌把我带到这里我也可以指出,如果你只是寻找文件夹你应该使用https://github.com/caioproiete/ookii-dialogs-wpf而不是下面的另一个问题的回答

enter image description here


48
投票

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

以下是如何使用它的示例:

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

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


47
投票

您可以使用FolderBrowserDialogEx - 内置FolderBrowserDialog的可重用衍生物。这个允许您输入路径,甚至是UNC路径。您还可以使用它浏览计算机或打印机。就像内置的FBD一样,但是......更好。

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

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

使用它的代码:

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

35
投票

Ookii.Dialogs包中包含一个围绕新(Vista样式)文件夹浏览器对话框的托管包装器。它在旧操作系统上也会优雅地降级。

  • qazxsw poi针对.NET 4.5和qazxsw poi
  • qazxsw poi针对.NET 4.5和qazxsw poi

27
投票

最好使用FolderBrowserDialog。

Ookii Dialogs for WPF

23
投票

经过几个小时的搜索,我发现available on NuGetOokii Dialogs for Windows Formsavailable on NuGet

我相信有三件事情可以使这个解决方案比其他所有解决方案都好。

  1. 它使用简单。它只需要在项目中包含两个文件(可以组合为一个)。
  2. 当在XP或旧系统上使用时,它会回退到标准的using (FolderBrowserDialog dlg = new FolderBrowserDialog()) { dlg.Description = "Select a folder"; if (dlg.ShowDialog() == DialogResult.OK) { MessageBox.Show("You selected: " + dlg.SelectedPath); } }
  3. 作者允许您将代码用于您认为合适的任何目的。 没有许可证,因为您可以随意使用代码。

下载代码this answer


17
投票

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

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

编辑:这比我想象的容易得多。这是C ++ / MFC中的代码,您可以将其转换为您选择的环境。

a working solution

编辑2:这应该是C#的翻译,但是我不熟练使用C#所以如果它不起作用,请不要开枪。

FolderBrowserDialog

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


17
投票

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

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

编辑:这是一个这样的自定义和如何做的例子(在Win32。而不是.NET):

m_ofn是OPENFILENAME结构,它是文件对话框的基础。添加以下两行:

here

其中IDD_FILEDIALOG_IMPORTXLIFF是一个自定义对话框模板,将添加到对话框的底部。请参阅下面的红色部分。 Exact Audio Copy (来源:CFileDialog dlg(true, NULL, "Filename will be ignored", OFN_HIDEREADONLY | OFN_NOVALIDATE | OFN_PATHMUSTEXIST | OFN_READONLY, NULL, this); dlg.DoModal();

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

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

更多细节在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 }


10
投票

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

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

m_ofn.lpTemplateName = MAKEINTRESOURCE(IDD_FILEDIALOG_IMPORTXLIFF); m_ofn.Flags |= OFN_ENABLETEMPLATE; 提供了有关如何使用Win32,alt textapptranslator.com执行此操作的信息。

© www.soinside.com 2019 - 2024. All rights reserved.