Open directory dialog
Asked Answered
F

19

326

I want the user to select a directory where a file that I will then generate will be saved. I know that in WPF I should use the OpenFileDialog from Win32, but unfortunately the dialog requires file(s) to be selected - it stays open if I simply click OK without choosing one. I could "hack up" the functionality by letting the user pick a file and then strip the path to figure out which directory it belongs to but that's unintuitive at best. Has anyone seen this done before?

Fresher answered 17/12, 2009 at 14:38 Comment(1)
I think this is a much better solution: #58570127Checkered
D
458

You can use the built-in FolderBrowserDialog class for this. Don't mind that it's in the System.Windows.Forms namespace.

using (var dialog = new System.Windows.Forms.FolderBrowserDialog())
{
    System.Windows.Forms.DialogResult result = dialog.ShowDialog();
}

If you want the window to be modal over some WPF window, see the question How to use a FolderBrowserDialog from a WPF application.


EDIT: If you want something a bit more fancy than the plain, ugly Windows Forms FolderBrowserDialog, there are some alternatives that allow you to use the Vista dialog instead:

  • Third-party libraries, such as Ookii dialogs (.NET 4.5+)

  • The Windows API Code Pack-Shell:

      using Microsoft.WindowsAPICodePack.Dialogs;
    
      ...
    
      var dialog = new CommonOpenFileDialog();
      dialog.IsFolderPicker = true;
      CommonFileDialogResult result = dialog.ShowDialog();
    

    Note that this dialog is not available on operating systems older than Windows Vista, so be sure to check CommonFileDialog.IsPlatformSupported first.

Delrio answered 17/12, 2009 at 14:43 Comment(20)
Do note that this is an awful dialog. You can't copy & paste a path into it, and it doesn't support favourite folders. Overall, I'd give it a 0 out of 5 and recommend nobody ever use it. Except that there was no reasonable alternative until Windows Vista came out with the much better folder dialog. There are good free libraries that show the good dialog on Vista+, and the bad one on XP.Undaunted
@MohamedSakherSawan: You don't need to use Windows Forms, you just have to use a class from the System.Windows.Forms namespace.Delrio
I know, But we are aiming to remove the windows forms assembly reference from our WPF App, because win forms has causes many problems and a hang in our application in the past, this hang has consumed an enormous resources to be solved!Balboa
Still, why does WPF offer a great OpenFileDialog but no OpenFolderDialog? Isn't that a bit strange? Why is WPF lacking here? Are there any plans to add a class for this dialog in WPF?Restless
@Heinzi, not sure why you would say "If you want the window to be modal over some WPF window.." because i didn't do anything special, and it is already modal (just open it using .ShowDialog() method).Barrelchested
Don't forget that FolderBrowserDialog is disposable.Socket
Note that in order to use CommonOpenFileDialog from WindowsAPICodePack you need to Install-Package WindowsAPICodePack-Shell. The link provided in the answer doesn't list that.Rubino
Ookii dialogs do not run out of the box in ".Net 4.0 CP". Addidional dll references are required... Although it still did not work for me - kept throwing compile errors.Indevout
would just like to add that the package is easily downloaded from NuGet: nuget.org/packages/WindowsAPICodePack-Shell, the many urls floating around did not seem to work for me.Perrault
"The type or namespace CommonOpenFileDialog could not be found". It's 2017 and I can't pick a folderCell
Add reference System.Windows.Forms to the project, add "using WinForms = System.Windows.Forms;" on top and then you can use "using (var dialog = new WinForms.FolderBrowserDialog()) { ... }". Checked with .NET 4.7.1Copp
When you search for 'WindowsAPICodePack' nuget, it would list several packages. There's actually one from Microsoft. So if you're a bit hesitant about this nuget, just get that one. oh, here's the nuget page as of the moment: nuget.org/packages/Microsoft.WindowsAPICodePack-Shell/1.1.0 I was hoping that in 2018, we wouldn't need to add this manually but here we are.Chinachinaberry
@mickeymicks: Thanks, I've updated the link in the answer to the official package!Delrio
@Delrio I've added the NuGet package however when I attempt to instantiate the object I'm being told that CommonFileDialog is an Abstract class aka the class was declared using MustInherit. Is there something I'm missing with the use of this reference? Why does your code not require you to create a derived class?Coracorabel
@CodeNovice: Because I use CommonOpenFileDialog. :-)Delrio
@brokenthorn github.com/dotnet/wpf/issues/438 here is the suggestion for a WPF alternative for OpenFolderDialogAnteater
Just a note that (in NET Core 3 at least) FolderBrowserDialog has been updated to use "a newer COM-based control that was introduced in Windows Vista", hence its appearance and behaviour is now less objectionable.Stalinabad
The FolderBrowserDialog should not be used anymore. This info is only available in the native documentaion. This Dialog also (still in Win11) has a bug, that when user renames a folder in the dialog it might return the old name instead of the new one.Geld
"Don't mind?" - I don't mind but the compiler does! The type or namespace name 'Forms' does not exist in the namespace 'System.Windows' (are you missing an assembly reference?) MemoryFolderDwinnell
@PaulMcCarthy: So, are you missing an assembly reference (to System.Windows.Forms.dll)?Delrio
C
49

I created a UserControl which is used like this:

  <UtilitiesWPF:FolderEntry Text="{Binding Path=LogFolder}" Description="Folder for log files"/>

The xaml source looks like this:

<UserControl x:Class="Utilities.WPF.FolderEntry"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <DockPanel>
        <Button Margin="0" Padding="0" DockPanel.Dock="Right" Width="Auto" Click="BrowseFolder">...</Button>
        <TextBox Height="Auto" HorizontalAlignment="Stretch" DockPanel.Dock="Right" 
           Text="{Binding Text, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}}" />
    </DockPanel>
</UserControl>

and the code-behind

public partial class FolderEntry {
    public static DependencyProperty TextProperty = DependencyProperty.Register("Text", typeof(string), typeof(FolderEntry), new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));
    public static DependencyProperty DescriptionProperty = DependencyProperty.Register("Description", typeof(string), typeof(FolderEntry), new PropertyMetadata(null));

    public string Text { get { return GetValue(TextProperty) as string; } set { SetValue(TextProperty, value); }}

    public string Description { get { return GetValue(DescriptionProperty) as string; } set { SetValue(DescriptionProperty, value); } }

    public FolderEntry() { InitializeComponent(); }

    private void BrowseFolder(object sender, RoutedEventArgs e) {
        using (FolderBrowserDialog dlg = new FolderBrowserDialog()) {
            dlg.Description = Description;
            dlg.SelectedPath = Text;
            dlg.ShowNewFolderButton = true;
            DialogResult result = dlg.ShowDialog();
            if (result == System.Windows.Forms.DialogResult.OK) {
                Text = dlg.SelectedPath;
                BindingExpression be = GetBindingExpression(TextProperty);
                if (be != null)
                    be.UpdateSource();
            }
        }
    }
 }
Candiot answered 17/12, 2009 at 15:4 Comment(3)
+1, nice example on how to write a UserControl. One question: Why do you need be.UpdateSource? Shouldn't change notifications be automatic in dependency properties?Delrio
You could specify in the binding when to fire the updates. By default it's on the LostFocus but you can tell it to fire updates on PropertyChanged as well.Fresher
The binding will then also be updated for every keystroke. If the user does some kind of validation on update (e.g. Directory.Exist) it might cause problems.Candiot
S
40

As stated in earlier answers, FolderBrowserDialog is the class to use for this. Some people have (justifiable) concerns with the appearance and behaviour of this dialog. The good news is that it was "modernized" in NET Core 3.0, so is now a viable option for those writing either Windows Forms or WPF apps targeting that version or later (you're out of luck if still using NET Framework though).

In .NET Core 3.0, Windows Forms users [sic] a newer COM-based control that was introduced in Windows Vista: FolderBrowserDialog in NET Core 3.0

To reference System.Windows.Forms in a NET Core WPF app, it is necessary to edit the project file and add the following line:

<UseWindowsForms>true</UseWindowsForms>

This can be placed directly after the existing <UseWPF> element.

Then it's just a case of using the dialog:

using System;
using System.Windows.Forms;

...

using var dialog = new FolderBrowserDialog
{
    Description = "Time to select a folder",
    UseDescriptionForTitle = true,
    SelectedPath = Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory)
        + Path.DirectorySeparatorChar,
    ShowNewFolderButton = true
};

if (dialog.ShowDialog() == DialogResult.OK)
{
    ...
}

FolderBrowserDialog has a RootFolder property that supposedly "sets the root folder where the browsing starts from" but whatever I set this to it didn't make any difference; SelectedPath seemed to be the better property to use for this purpose, however the trailing backslash is required.

Also, the ShowNewFolderButton property seems to be ignored as well, the button is always shown regardless.

Stalinabad answered 8/12, 2020 at 14:9 Comment(2)
I wish I could upvote this more than once! I wasted so much time with other answers that didn't explain how to get the dialog in .NET Core. ThanksAyesha
Only thing missing - you need to reload your project for the UseWindowsForms to take effect!Corrigendum
P
26

Ookii folder dialog can be found at Nuget.

PM> Install-Package Ookii.Dialogs.Wpf

And, example code is as below.

var dialog = new Ookii.Dialogs.Wpf.VistaFolderBrowserDialog();
if (dialog.ShowDialog(this).GetValueOrDefault())
{
    textBoxFolderPath.Text = dialog.SelectedPath;
}

More information on how to use it: https://github.com/augustoproiete/ookii-dialogs-wpf

Percale answered 5/1, 2016 at 5:5 Comment(1)
tnx your way was shortestGustie
B
19

For those who don't want to create a custom dialog but still prefer a 100% WPF way and don't want to use separate DDLs, additional dependencies or outdated APIs, I came up with a very simple hack using the Save As dialog.

No using directive needed, you may simply copy-paste the code below !

It should still be very user-friendly and most people will never notice.

The idea comes from the fact that we can change the title of that dialog, hide files, and work around the resulting filename quite easily.

It is a big hack for sure, but maybe it will do the job just fine for your usage...

In this example I have a textbox object to contain the resulting path, but you may remove the related lines and use a return value if you wish...

// Create a "Save As" dialog for selecting a directory (HACK)
var dialog = new Microsoft.Win32.SaveFileDialog();
dialog.InitialDirectory = textbox.Text; // Use current value for initial dir
dialog.Title = "Select a Directory"; // instead of default "Save As"
dialog.Filter = "Directory|*.this.directory"; // Prevents displaying files
dialog.FileName = "select"; // Filename will then be "select.this.directory"
if (dialog.ShowDialog() == true) {
    string path = dialog.FileName;
    // Remove fake filename from resulting path
    path = path.Replace("\\select.this.directory", "");
    path = path.Replace(".this.directory", "");
    // If user has changed the filename, create the new directory
    if (!System.IO.Directory.Exists(path)) {
        System.IO.Directory.CreateDirectory(path);
    }
    // Our final value is in path
    textbox.Text = path;
}

The only issues with this hack are :

  • Acknowledge button still says "Save" instead of something like "Select directory", but in a case like mines I "Save" the directory selection so it still works...
  • Input field still says "File name" instead of "Directory name", but we can say that a directory is a type of file...
  • There is still a "Save as type" dropdown, but its value says "Directory (*.this.directory)", and the user cannot change it for something else, works for me...

Most people won't notice these, although I would definitely prefer using an official WPF way if microsoft would get their heads out of their asses, but until they do, that's my temporary fix.

Bedridden answered 9/5, 2018 at 20:43 Comment(3)
This was cool. Surprised that no one else appears to have tried this. The NuGet package is much better of course but without the NuGet WindowsAPICodePack this is an excellent way to HACK the ability to select a folder without adding any new packages/references.Coracorabel
Ewww. You had until I saw the way dialog.FileName = "select"; // Filename will then be "select.this.directory" was implemented. That's a little confusing for non-technical end users. But otherwise an interesting, zero-dependency hack.Galop
The button at the bottom still says "Save" , isn't that weird? This will confuse the user easily. Mission impossible !Jakie
A
15

Ookii Dialogs includes a dialog for selecting a folder (instead of a file):

Ookii Dialogs Select Folder Screenshot

https://github.com/ookii-dialogs

Assiniboine answered 10/4, 2014 at 12:12 Comment(0)
C
7

For Directory Dialog to get the Directory Path, First Add reference System.Windows.Forms, and then Resolve, and then put this code in a button click.

    var dialog = new FolderBrowserDialog();
    dialog.ShowDialog();
    folderpathTB.Text = dialog.SelectedPath;

(folderpathTB is name of TextBox where I wana put the folder path, OR u can assign it to a string variable too i.e.)

    string folder = dialog.SelectedPath;

And if you wana get FileName/path, Simply do this on Button Click

    FileDialog fileDialog = new OpenFileDialog();
    fileDialog.ShowDialog();
    folderpathTB.Text = fileDialog.FileName;

(folderpathTB is name of TextBox where I wana put the file path, OR u can assign it to a string variable too)

Note: For Folder Dialog, the System.Windows.Forms.dll must be added to the project, otherwise it wouldn't work.

Catalonia answered 7/11, 2015 at 5:20 Comment(1)
Thanks for your answer but this approach has already been explained by @Delrio above.Fresher
U
6

I found the below code on below link... and it worked Select folder dialog WPF

using Microsoft.WindowsAPICodePack.Dialogs;

var dlg = new CommonOpenFileDialog();
dlg.Title = "My Title";
dlg.IsFolderPicker = true;
dlg.InitialDirectory = currentDirectory;

dlg.AddToMostRecentlyUsedList = false;
dlg.AllowNonFileSystemItems = false;
dlg.DefaultDirectory = currentDirectory;
dlg.EnsureFileExists = true;
dlg.EnsurePathExists = true;
dlg.EnsureReadOnly = false;
dlg.EnsureValidNames = true;
dlg.Multiselect = false;
dlg.ShowPlacesList = true;

if (dlg.ShowDialog() == CommonFileDialogResult.Ok) 
{
  var folder = dlg.FileName;
  // Do something with selected folder string
}
Unexperienced answered 4/3, 2016 at 19:52 Comment(0)
T
5

I'd suggest, to add in the nugget package:

  Install-Package OpenDialog

Then the way to used it is:

    Gat.Controls.OpenDialogView openDialog = new Gat.Controls.OpenDialogView();
    Gat.Controls.OpenDialogViewModel vm = (Gat.Controls.OpenDialogViewModel)openDialog.DataContext;
    vm.IsDirectoryChooser = true;
    vm.Show();

    WPFLabel.Text = vm.SelectedFilePath.ToString();

Here's the documentation: http://opendialog.codeplex.com/documentation

Works for Files, files with filter, folders, etc

Tumid answered 17/6, 2016 at 8:54 Comment(0)
N
4

The best way to achieve what you want is to create your own wpf based control , or use a one that was made by other people
why ? because there will be a noticeable performance impact when using the winforms dialog in a wpf application (for some reason)
i recommend this project
https://opendialog.codeplex.com/
or Nuget :

PM> Install-Package OpenDialog

it's very MVVM friendly and it isn't wraping the winforms dialog

Nonobservance answered 12/7, 2015 at 1:31 Comment(0)
R
4

The Ookii VistaFolderBrowserDialog is the one you want.

If you only want the Folder Browser from Ooki Dialogs and nothing else then download the Source, cherry-pick the files you need for the Folder browser (hint: 7 files) and it builds fine in .NET 4.5.2. I had to add a reference to System.Drawing. Compare the references in the original project to yours.

How do you figure out which files? Open your app and Ookii in different Visual Studio instances. Add VistaFolderBrowserDialog.cs to your app and keep adding files until the build errors go away. You find the dependencies in the Ookii project - Control-Click the one you want to follow back to its source (pun intended).

Here are the files you need if you're too lazy to do that ...

NativeMethods.cs
SafeHandles.cs
VistaFolderBrowserDialog.cs
\ Interop
   COMGuids.cs
   ErrorHelper.cs
   ShellComInterfaces.cs
   ShellWrapperDefinitions.cs

Edit line 197 in VistaFolderBrowserDialog.cs unless you want to include their Resources.Resx

throw new InvalidOperationException(Properties.Resources.FolderBrowserDialogNoRootFolder);

throw new InvalidOperationException("Unable to retrieve the root folder.");

Add their copyright notice to your app as per their license.txt

The code in \Ookii.Dialogs.Wpf.Sample\MainWindow.xaml.cs line 160-169 is an example you can use but you will need to remove this, from MessageBox.Show(this, for WPF.

Works on My Machine [TM]

Retrospection answered 27/4, 2016 at 13:28 Comment(0)
C
3

None of these answers worked for me (generally there was a missing reference or something along those lines)

But this quite simply did:

Using FolderBrowserDialog in WPF application

Add a reference to System.Windows.Forms and use this code:

  var dialog = new System.Windows.Forms.FolderBrowserDialog();
  System.Windows.Forms.DialogResult result = dialog.ShowDialog();

No need to track down missing packages. Or add enormous classes

This gives me a modern folder selector that also allows you to create a new folder

I'm yet to see the impact when deployed to other machines

Cell answered 18/6, 2017 at 7:26 Comment(0)
U
2

I know this is an old question, but a simple way to do this is use the FileDialog option provided by WPF and using System.IO.Path.GetDirectory(filename).

Unhair answered 13/9, 2017 at 21:31 Comment(2)
But then the user must choose a file even though he is told to choose a folder. A inexperienced user might call HelpDesk at this point, asking why he has to choose a file when he has to choose a folderLilalilac
Requires at least one file be in any given folder or else it is not selectablePsycho
B
2

It seems that the Microsoft.Win32 .NET library does not support selecting folders (only files), so you are out of luck in WPF (as of 7/2022). I feel the best option now is Ookii for WPF: https://github.com/ookii-dialogs/ookii-dialogs-wpf. It works great and as expected in WPF minus Microsoft support. You can get it as a NuGet package. Code behind XAML View:

public partial class ExportRegionView : UserControl
{
    public ExportRegionView()
    {
        InitializeComponent();
    }
    private void SavePath(object sender, RoutedEventArgs e)
    {
        var dialog = new Ookii.Dialogs.Wpf.VistaFolderBrowserDialog();
        dialog.Description = "SIPAS Export Folder";
        dialog.UseDescriptionForTitle = true;
        if (dialog.ShowDialog().GetValueOrDefault())
        {
            ExportPath.Text = dialog.SelectedPath;
        }
    }
}

XAML: <Button Grid.Row="1" Grid.Column="3" Style="{DynamicResource Esri_Button}" Click="SavePath" Margin="5,5,5,5">Path</Button>
Basion answered 22/7, 2022 at 1:9 Comment(0)
K
1

You could use smth like this in WPF. I've created example method. Check below.

public string getFolderPath()
{
           // Create OpenFileDialog 
           Microsoft.Win32.OpenFileDialog dlg = new Microsoft.Win32.OpenFileDialog();

           OpenFileDialog openFileDialog = new OpenFileDialog();
           openFileDialog.Multiselect = false;

           openFileDialog.InitialDirectory = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
           if (openFileDialog.ShowDialog() == true)
           {
               System.IO.FileInfo fInfo = new System.IO.FileInfo(openFileDialog.FileName);
               return fInfo.DirectoryName;
           }
           return null;           
       }
Kerianne answered 12/4, 2018 at 21:46 Comment(2)
This requires the user to select a file from the folder. If the folder is empty then you can't select your folder.Gottfried
Yes, I understand that, this is some kind workaround, not the perfect solution for this issue.Kerianne
M
1

Microsoft.Win32.OpenFolderDialog is part of .NET 8.0-preview7 and will be available in .NET 8:

Marilumarilyn answered 20/5, 2023 at 8:31 Comment(0)
M
0

Latest .NET 8 got special OpenFolderDialog - you can use it!

Methylal answered 20/11, 2023 at 0:24 Comment(0)
R
0

This is much simpler answer. WPF is using OpenFolderDialag from Microsoft.Win32

using Microsoft.Win32;

 public void ExportCSV_Click(object sender, RoutedEventArgs e)
 {
      var dialog = new OpenFolderDialog();

      if (dialog.ShowDialog() == true)
      {
          string filePath = dialog.FolderName;
      }
 }
Rhomb answered 17/3 at 10:49 Comment(0)
A
-1
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace Gearplay
{
    /// <summary>
    /// Логика взаимодействия для OpenFolderBrows.xaml
    /// </summary>
    public partial class OpenFolderBrows : Page
    {
        internal string SelectedFolderPath { get; set; }
        public OpenFolderBrows()
        {
            InitializeComponent();
            Selectedpath();
            InputLogicalPathCollection();
             
        }

        internal void Selectedpath()
        {
            Browser.Navigate(@"C:\");
            
            Browser.Navigated += Browser_Navigated;
        }

        private void Browser_Navigated(object sender, NavigationEventArgs e)
        {
            SelectedFolderPath = e.Uri.AbsolutePath.ToString();
            //MessageBox.Show(SelectedFolderPath);
        }

        private void MenuItem_Click(object sender, RoutedEventArgs e)
        {
          
           
        }
        
        string [] testing { get; set; }
        private void InputLogicalPathCollection()
        {            // add Menu items for Cotrol 
            string[] DirectoryCollection_Path = Environment.GetLogicalDrives(); // Get Local Drives
            testing = new string[DirectoryCollection_Path.Length];
            //MessageBox.Show(DirectoryCollection_Path[0].ToString());
            MenuItem[]  menuItems = new MenuItem[DirectoryCollection_Path.Length]; // Create Empty Collection
            for(int i=0;i<menuItems.Length;i++)
            {
                // Create collection depend how much logical drives 
                menuItems[i] = new MenuItem();
                menuItems[i].Header = DirectoryCollection_Path[i];
                menuItems[i].Name = DirectoryCollection_Path[i].Substring(0,DirectoryCollection_Path.Length-1);
                DirectoryCollection.Items.Add(menuItems[i]);
                menuItems[i].Click += OpenFolderBrows_Click;
                testing[i]= DirectoryCollection_Path[i].Substring(0, DirectoryCollection_Path.Length - 1);
            }

            

        }
        
        private void OpenFolderBrows_Click(object sender, RoutedEventArgs e)
        {

            foreach (string str in testing)
            {
                if (e.OriginalSource.ToString().Contains("Header:"+str)) // Navigate to Local drive
                {
                    Browser.Navigate(str + @":\");
                   
                }


            }


        }

        private void Goback_Click(object sender, RoutedEventArgs e)
        {// Go Back
            try
            {
                Browser.GoBack();
            }catch(Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
        }

        private void Goforward_Click(object sender, RoutedEventArgs e)
        { //Go Forward
            try
            {
                Browser.GoForward();
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }

        }

        private void FolderForSave_Click(object sender, RoutedEventArgs e)
        {
            // Separate Click For Go Back same As Close App With send string var to Main Window ( Main class etc.) 
            this.NavigationService.GoBack();
        }
    }
}
Althing answered 20/2, 2021 at 18:6 Comment(1)
You Can Use WebBrowser for This operation for avoid winforms depencyAlthing

© 2022 - 2024 — McMap. All rights reserved.