How do I use OpenFileDialog to select a folder?

I was going to use the following project:

However, there's a problem: it uses the GetOpenFileName function and OPENFILENAME structure. OPENFILENAME has the member named templateID, which is the identifier for dialog template. And the project contains the res1.rc file and the templated dialog init, too. But I couldn't figure out how to attach this file to my C# project.

Is there a better way to use an OpenFileDialog to select folders?

10

13 Answers

Basically you need the FolderBrowserDialog class:

Prompts the user to select a folder. This class cannot be inherited.

Example:

using(var fbd = new FolderBrowserDialog()) { DialogResult result = fbd.ShowDialog(); if (result == DialogResult.OK && !string.IsNullOrWhiteSpace(fbd.SelectedPath)) { string[] files = Directory.GetFiles(fbd.SelectedPath); System.Windows.Forms.MessageBox.Show("Files found: " + files.Length.ToString(), "Message"); } } 

If you work in WPF you have to add the reference to System.Windows.Forms.

you also have to add using System.IO for Directory class

9

As a note for future users who would like to avoid using FolderBrowserDialog, Microsoft once released an API called the WindowsAPICodePack that had a helpful dialog called CommonOpenFileDialog, that could be set into a IsFolderPicker mode. The API is available from Microsoft as a NuGet package.

This is all I needed to install and use the CommonOpenFileDialog. (NuGet handled the dependencies)

Install-Package Microsoft.WindowsAPICodePack-Shell 

For the include line:

using Microsoft.WindowsAPICodePack.Dialogs; 

Usage:

CommonOpenFileDialog dialog = new CommonOpenFileDialog(); dialog.InitialDirectory = "C:\\Users"; dialog.IsFolderPicker = true; if (dialog.ShowDialog() == CommonFileDialogResult.Ok) { MessageBox.Show("You selected: " + dialog.FileName); } 
14

Here is a pure C# version, nuget-free, that should work with all versions of .NET (including .NET Core, .NET 5, WPF, Winforms, etc.) and uses Windows Vista (and higher) IFileDialog interface with the FOS_PICKFOLDERS options so it has the nice folder picker Windows standard UI.

Update 2023/3/17: the class now support multiple selection.

I have also added WPF's Window type support but this is optional, the WPF-marked lines can just be removed.

usage:

var dlg = new FolderPicker(); dlg.InputPath = @"c:\windows\system32"; if (dlg.ShowDialog() == true) { MessageBox.Show(dlg.ResultPath); } 

code:

using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Runtime.InteropServices; using System.Runtime.InteropServices.ComTypes; using System.Windows; using System.Windows.Interop; public class FolderPicker { private readonly List<string> _resultPaths = new List<string>(); private readonly List<string> _resultNames = new List<string>(); public IReadOnlyList<string> ResultPaths => _resultPaths; public IReadOnlyList<string> ResultNames => _resultNames; public string ResultPath => ResultPaths.FirstOrDefault(); public string ResultName => ResultNames.FirstOrDefault(); public virtual string InputPath { get; set; } public virtual bool ForceFileSystem { get; set; } public virtual bool Multiselect { get; set; } public virtual string Title { get; set; } public virtual string OkButtonLabel { get; set; } public virtual string FileNameLabel { get; set; } protected virtual int SetOptions(int options) { if (ForceFileSystem) { options |= (int)FOS.FOS_FORCEFILESYSTEM; } if (Multiselect) { options |= (int)FOS.FOS_ALLOWMULTISELECT; } return options; } // for WPF support public bool? ShowDialog(Window owner = null, bool throwOnError = false) { owner = owner ?? Application.Current?.MainWindow; return ShowDialog(owner != null ? new WindowInteropHelper(owner).Handle : IntPtr.Zero, throwOnError); } // for all .NET public virtual bool? ShowDialog(IntPtr owner, bool throwOnError = false) { var dialog = (IFileOpenDialog)new FileOpenDialog(); if (!string.IsNullOrEmpty(InputPath)) { if (CheckHr(SHCreateItemFromParsingName(InputPath, null, typeof(IShellItem).GUID, out var item), throwOnError) != 0) return null; dialog.SetFolder(item); } var options = FOS.FOS_PICKFOLDERS; options = (FOS)SetOptions((int)options); dialog.SetOptions(options); if (Title != null) { dialog.SetTitle(Title); } if (OkButtonLabel != null) { dialog.SetOkButtonLabel(OkButtonLabel); } if (FileNameLabel != null) { dialog.SetFileName(FileNameLabel); } if (owner == IntPtr.Zero) { owner = Process.GetCurrentProcess().MainWindowHandle; if (owner == IntPtr.Zero) { owner = GetDesktopWindow(); } } var hr = dialog.Show(owner); if (hr == ERROR_CANCELLED) return null; if (CheckHr(hr, throwOnError) != 0) return null; if (CheckHr(dialog.GetResults(out var items), throwOnError) != 0) return null; items.GetCount(out var count); for (var i = 0; i < count; i++) { items.GetItemAt(i, out var item); CheckHr(item.GetDisplayName(SIGDN.SIGDN_DESKTOPABSOLUTEPARSING, out var path), throwOnError); CheckHr(item.GetDisplayName(SIGDN.SIGDN_DESKTOPABSOLUTEEDITING, out var name), throwOnError); if (path != null || name != null) { _resultPaths.Add(path); _resultNames.Add(name); } } return true; } private static int CheckHr(int hr, bool throwOnError) { if (hr != 0 && throwOnError) Marshal.ThrowExceptionForHR(hr); return hr; } [DllImport("shell32")] private static extern int SHCreateItemFromParsingName([MarshalAs(UnmanagedType.LPWStr)] string pszPath, IBindCtx pbc, [MarshalAs(UnmanagedType.LPStruct)] Guid riid, out IShellItem ppv); [DllImport("user32")] private static extern IntPtr GetDesktopWindow(); #pragma warning disable IDE1006 // Naming Styles private const int ERROR_CANCELLED = unchecked((int)0x800704C7); #pragma warning restore IDE1006 // Naming Styles [ComImport, Guid("DC1C5A9C-E88A-4dde-A5A1-60F82A20AEF7")] // CLSID_FileOpenDialog private class FileOpenDialog { } [ComImport, Guid("d57c7288-d4ad-4768-be02-9d969532d960"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] private interface IFileOpenDialog { [PreserveSig] int Show(IntPtr parent); // IModalWindow [PreserveSig] int SetFileTypes(); // not fully defined [PreserveSig] int SetFileTypeIndex(int iFileType); [PreserveSig] int GetFileTypeIndex(out int piFileType); [PreserveSig] int Advise(); // not fully defined [PreserveSig] int Unadvise(); [PreserveSig] int SetOptions(FOS fos); [PreserveSig] int GetOptions(out FOS pfos); [PreserveSig] int SetDefaultFolder(IShellItem psi); [PreserveSig] int SetFolder(IShellItem psi); [PreserveSig] int GetFolder(out IShellItem ppsi); [PreserveSig] int GetCurrentSelection(out IShellItem ppsi); [PreserveSig] int SetFileName([MarshalAs(UnmanagedType.LPWStr)] string pszName); [PreserveSig] int GetFileName([MarshalAs(UnmanagedType.LPWStr)] out string pszName); [PreserveSig] int SetTitle([MarshalAs(UnmanagedType.LPWStr)] string pszTitle); [PreserveSig] int SetOkButtonLabel([MarshalAs(UnmanagedType.LPWStr)] string pszText); [PreserveSig] int SetFileNameLabel([MarshalAs(UnmanagedType.LPWStr)] string pszLabel); [PreserveSig] int GetResult(out IShellItem ppsi); [PreserveSig] int AddPlace(IShellItem psi, int alignment); [PreserveSig] int SetDefaultExtension([MarshalAs(UnmanagedType.LPWStr)] string pszDefaultExtension); [PreserveSig] int Close(int hr); [PreserveSig] int SetClientGuid(); // not fully defined [PreserveSig] int ClearClientData(); [PreserveSig] int SetFilter([MarshalAs(UnmanagedType.IUnknown)] object pFilter); [PreserveSig] int GetResults(out IShellItemArray ppenum); [PreserveSig] int GetSelectedItems([MarshalAs(UnmanagedType.IUnknown)] out object ppsai); } [ComImport, Guid("43826D1E-E718-42EE-BC55-A1E261C37BFE"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] private interface IShellItem { [PreserveSig] int BindToHandler(); // not fully defined [PreserveSig] int GetParent(); // not fully defined [PreserveSig] int GetDisplayName(SIGDN sigdnName, [MarshalAs(UnmanagedType.LPWStr)] out string ppszName); [PreserveSig] int GetAttributes(); // not fully defined [PreserveSig] int Compare(); // not fully defined } [ComImport, Guid("b63ea76d-1f85-456f-a19c-48159efa858b"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] private interface IShellItemArray { [PreserveSig] int BindToHandler(); // not fully defined [PreserveSig] int GetPropertyStore(); // not fully defined [PreserveSig] int GetPropertyDescriptionList(); // not fully defined [PreserveSig] int GetAttributes(); // not fully defined [PreserveSig] int GetCount(out int pdwNumItems); [PreserveSig] int GetItemAt(int dwIndex, out IShellItem ppsi); [PreserveSig] int EnumItems(); // not fully defined } #pragma warning disable CA1712 // Do not prefix enum values with type name 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_OVERWRITEPROMPT = 0x2, FOS_STRICTFILETYPES = 0x4, FOS_NOCHANGEDIR = 0x8, FOS_PICKFOLDERS = 0x20, FOS_FORCEFILESYSTEM = 0x40, FOS_ALLNONSTORAGEITEMS = 0x80, FOS_NOVALIDATE = 0x100, FOS_ALLOWMULTISELECT = 0x200, FOS_PATHMUSTEXIST = 0x800, FOS_FILEMUSTEXIST = 0x1000, FOS_CREATEPROMPT = 0x2000, FOS_SHAREAWARE = 0x4000, FOS_NOREADONLYRETURN = 0x8000, FOS_NOTESTFILECREATE = 0x10000, FOS_HIDEMRUPLACES = 0x20000, FOS_HIDEPINNEDPLACES = 0x40000, FOS_NODEREFERENCELINKS = 0x100000, FOS_OKBUTTONNEEDSINTERACTION = 0x200000, FOS_DONTADDTORECENT = 0x2000000, FOS_FORCESHOWHIDDEN = 0x10000000, FOS_DEFAULTNOMINIMODE = 0x20000000, FOS_FORCEPREVIEWPANEON = 0x40000000, FOS_SUPPORTSTREAMABLEITEMS = unchecked((int)0x80000000) } #pragma warning restore CA1712 // Do not prefix enum values with type name } 

result:

enter image description here

15

There is a hackish solution using OpenFileDialog where ValidateNames and CheckFileExists are both set to false and FileName is given a mock value to indicate that a directory is selected.

I say hack because it is confusing to users about how to select a folder. They need to be in the desired folder and then just press Open while file name says "Folder Selection."

C# Folder selection dialog

This is based on Select file or folder from the same dialog by Denis Stankovski.

OpenFileDialog folderBrowser = new OpenFileDialog(); // Set validate names and check file exists to false otherwise windows will // not let you select "Folder Selection." folderBrowser.ValidateNames = false; folderBrowser.CheckFileExists = false; folderBrowser.CheckPathExists = true; // Always default to Folder Selection. folderBrowser.FileName = "Folder Selection."; if (folderBrowser.ShowDialog() == DialogResult.OK) { string folderPath = Path.GetDirectoryName(folderBrowser.FileName); // ... } 
6

Strange that so much answers/votes, but no one add the following code as an answer:

using (var opnDlg = new OpenFileDialog()) //ANY dialog { //opnDlg.Filter = "Png Files (*.png)|*.png"; //opnDlg.Filter = "Excel Files (*.xls, *.xlsx)|*.xls;*.xlsx|CSV Files (*.csv)|*.csv" if (opnDlg.ShowDialog() == DialogResult.OK) { //opnDlg.SelectedPath -- your result } } 
7

Sounds to me like you're just after the FolderBrowserDialog.

2

Here is another solution, that has all the source available in a single, simple ZIP file.

It presents the OpenFileDialog with additional windows flags that makes it work like the Windows 7+ Folder Selection dialog.

Per the website, it is public domain: "There’s no license as such as you are free to take and do with the code what you will."

  • Article: .NET Win 7-style folder select dialog ()
  • Source code:

Archive.org links:

  • Article:
  • Source code:
3

Take a look at the Ookii Dialogs libraries which has an implementation of a folder browser dialog for Windows Forms and WPF respectively.

enter image description here

Ookii.Dialogs.WinForms


Ookii.Dialogs.Wpf

6

Im new to C# and came across this thread just now.

This might be of interest to those that are new to the language as well.

Change design of FolderBrowserDialog

The answer given by Simon Mourier would be the best answer considering the OP question. It does not involve NuGET package so there will not be any dependency issue in the future for selecting folder method.

If you encountered error related to "...is not available in C# 7.3", just add <LangVersion>8.0</LangVersion> to your .csproj (testing with Visual Studio does not produce any error when build and run)

If you can't change the project language then just replace owner ??= Application.Current.MainWindow with

owner = owner ?? Application.Current.MainWindow

This is how I use the folder browser dialog in Small Visual Basic. This code solves the non-selected initial folder issue, and also selects the folder from the clipboard or the registry (if any), and if the folder is deleted it goes up throw parents until selecting an existing folder. This makes using the dialog very comfortable. Note that I am sending 4 tabs because I show the Create new folder button, but if you hide it, use two tabs only.

 Public Shared Function OpenFolderDialog(initialFolder As String) As String Dim folder = GeIinitialFolder(initialFolder) If folder = "" Then folder = GeIinitialFolder(System.Windows.Clipboard.GetText()) If folder = "" Then folder = GeIinitialFolder(GetSetting("sVB", "OpenFolder", "LastFolder", "")) Dim th As New Threading.Thread( Sub() Threading.Thread.Sleep(300) System.Windows.Forms.SendKeys.SendWait("{TAB}{TAB}{TAB}{TAB}{RIGHT}") End Sub) th.Start() Try Dim dlg As New System.Windows.Forms.FolderBrowserDialog With { .Description = "Select a folder:", .SelectedPath = folder } If dlg.ShowDialog() = System.Windows.Forms.DialogResult.OK Then SaveSetting("sVB", "OpenFolder", "LastFolder", dlg.SelectedPath) OpenFolderDialog = dlg.SelectedPath End If Catch ex As Exception MsgBox(ex.Message) End Try End Function Private Shared Function GeIinitialFolder(folder As String) As String Try If folder <> "" Then If IO.File.Exists(folder) Then folder = Path.GetDirectoryName(folder) Else Do If Directory.GetDirectoryRoot(folder) = folder OrElse Directory.Exists(folder) Then Exit Do End If folder = Path.GetDirectoryName(folder) Loop End If End If Catch folder = "" End Try Return folder End Function 

Don't reject FolderBrowserDialog just yet. If you are on .NET Core (e.g. .NET 5 and higher; with <UseWindowsForms>true</UseWindowsForms>), there is the AutoUpgradeEnabled property (true by default), which controls whether the dialog is allowed to upgrade. When enabled, it will use the newer common dialog without needing any additional effort.

On .NET Framework, I know of no standardized solution.

this should be the most obvious and straight forward way

using (var dialog = new System.Windows.Forms.FolderBrowserDialog()) { System.Windows.Forms.DialogResult result = dialog.ShowDialog(); if(result == System.Windows.Forms.DialogResult.OK) { selectedFolder = dialog.SelectedPath; } } 
1

Your Answer

Sign up or log in

Sign up using Google Sign up using Facebook Sign up using Email and Password

Post as a guest

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

You Might Also Like