40

I'd like a SaveFileDialog with the following behavior:

  • The first time you open it, it goes to "My Documents".

  • Afterwards, it goes to the last selected folder. What's the best way to accomplish this?

If I don't set the InitialDirectory, it goes to the exe's directory - which is not what I want. It rememebers the last selected directory though - even between executions.

If I set the InitialDirectory, it does not remember the last selected directory. Of course, I could save the last selected directory in the registry :( but I am looking for a better solution.

      SaveFileDialog dialog = new SaveFileDialog();
      //??? dialog.InitialDirectory = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
      dialog.ShowDialog();

Any advice?

tom greene
  • 5,361
  • 9
  • 39
  • 52

15 Answers15

36

You need to set the RestoreDirectory to true as well as the InitialDirectory property.

Andrew Hare
  • 344,730
  • 71
  • 640
  • 635
  • 3
    Also see this thread, where programmer says that setting "RestoreDirectory" to true doesn't help: http://discuss.joelonsoftware.com/default.asp?dotnet.12.424113.2 – SLA80 Dec 30 '09 at 14:43
  • 9
    really? Doesn't work for me. It just always navigates to the InitialDirectory? – Patrick Klug Sep 20 '10 at 00:31
  • 2
    I had the same issue, but was finally able to get past it (see my answer to this question for an example). For some reason, passing the full path into Path.GetFullPath(), and then setting InitialDirectory to it works. – Jeffrey Harmon Aug 14 '12 at 18:17
22

I have no idea why this works, but I was finally able to get it working for me.

I found that if I gave the full path, it would not work, but if I put that full path inside of Path.GetFullPath(), then it would work. Looking at the before and after values show them being the same, but it would consistently not work without it, and work with it.

//does not work
OpenFileDialog dlgOpen = new OpenFileDialog();
string initPath = Path.GetTempPath() + @"\FQUL";
dlgOpen.InitialDirectory = initPath;
dlgOpen.RestoreDirectory = true;

//works
OpenFileDialog dlgOpen = new OpenFileDialog();
string initPath = Path.GetTempPath() + @"\FQUL";
dlgOpen.InitialDirectory = Path.GetFullPath(initPath);
dlgOpen.RestoreDirectory = true;
Jeffrey Harmon
  • 2,268
  • 23
  • 18
5

Make sure to check that the directory path exists before setting the Initial directory property. Create the directory if it does not exist. ie

if (!Directory.Exists(FooDirectory))
{
     Directory.CreateDirectory(FooDirectory);
}
user1956901
  • 81
  • 2
  • 3
  • 3
    You can just call Directory.CreateDirectory(FooDirectory) and it will not throw an error if the directory already exists. – Hagelt18 Aug 01 '14 at 13:36
3

I too have tried different "solutions" found in different places, but none of them seem to work as soon as there is an MRU list entry in the registry :/ But here is my own simple workaround…

Instead of setting the dialog's InitialDirectory property, set the FileName property to your path, but combined with the selected Filter, e.g.:

dialog.FileName = Path.Combine(myPath, "*.*");
mousio
  • 10,079
  • 4
  • 34
  • 43
  • 1
    This didn't help for me - it just opened a dialog with the full path and filename in the file name field, but the directory shown was the one remembered (Windows 7, 64-bit, .net framework 4.5.1) – danio Jan 28 '16 at 17:12
2

The suggested workarounds didn't work for me, so after finding How does WPF OpenFileDialog track directory of last opened file? I implemented:

public static void SetInitialDirectory(this FileDialog dlg, string fileExtension, string initialDirectory)
        {
            // RestoreDirectory doesn't seem to be implemented - https://stackoverflow.com/questions/11144770/how-does-wpf-openfiledialog-track-directory-of-last-opened-file
            // so manually only set InitialDirectory if nothing is stored
            try
            {
                var mru = @"Software\Microsoft\Windows\CurrentVersion\Explorer\ComDlg32\OpenSavePidlMRU\" + fileExtension;
                var rk = Registry.CurrentUser.OpenSubKey(mru);
                if (rk == null)
                {
                    dlg.InitialDirectory = initialDirectory;
                }
            }
            catch (Exception)
            {
                // SecurityException, ObjectDisposedException => allow default behaviour
            }
        }

This will use the provided initialDirectory if the dialog has not been used before for this file extension. Once the dialog has been used, it reverts to the default behaviour of remembering the previous directory.

Community
  • 1
  • 1
danio
  • 8,548
  • 6
  • 47
  • 55
  • +1 Even though it didn't fix my problem directly, it does work if you have a directory you deleted and no longer want to be the initial directory, but it's stuck in the MRU, IF you actually have it go through the `dlg.InitialDirectory = initialDirectory;` part to set it to something else, null or not. Mine skipped it during debug using `jpg` as the `fileExtension` since I had a JPG file where I inadvertently created a directory with that file's name & saved the file inside, then deleted the folder to cause my problem. Somehow just backing up the debugger to run that line and set a path worked. – vapcguy May 22 '17 at 20:06
  • And actually, what I found was I wasn't using an environmental path variable correctly, so it wasn't saving the correct path as the `initialDirectory`. Setting that variable manually through the debugger helped me to figure out what it was trying to be set to, and to set it manually to something to ensure it even could be set. Once I figured out what the problem was, I went a different way, since I could always `null` it and set it anew, but this helped so I could have a function to pass what I thought was the correct path to it, just to find out where I actually messed up. :) – vapcguy May 23 '17 at 13:45
2

I found that setting InitialDirectory to null first works around user history.

    OpenFileDialog dlgOpen = new OpenFileDialog();
    dlgOpen.InitialDirectory = null;
    dlgOpen.InitialDirectory = @"c:\user\MyPath";
user104933
  • 21
  • 1
  • Didn't work setting it to `null` to avoid user history issue for me, on `OpenSaveDialog()`, where I had deleted the previous save location. Still have error that the location doesn't exist and the old place is still the place it tries to go. – vapcguy May 22 '17 at 19:35
  • I figured out that my issue was in my path. Above code will work fine. I was incorrectly using an environmental path variable (`%USERPROFILE%`), causing my path to be incorrect, so the dialog kept just using the last path it had - which was the one I had deleted. So as long as your path is correct, this will set it. :) – vapcguy May 23 '17 at 13:48
1

None of the provided solutions worked for me sadly.

In addition to the OP specifications, I wanted the program to remember the last save location between runs. For this, in the Visual Studios Solution Explorer under ProjectName -> Properties -> Settings.settings, I setup the following property:

Settings.settings property: Name=PreviousPath, Type=string, Scope=User, leave Value empty

Because I am keeping the SaveFileDialog around for the duration of the program's running, I instantiate at the start. Then in the Command for bringing up the dialog:

if (string.IsNullOrEmpty(Settings.Default.PreviousPath))
{
    this.saveDialog.InitialDirectory = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
}
else
{
    this.saveDialog.InitialDirectory = Settings.Default.PreviousPath;
}

this.saveDialog.FileName = "Default_File_Name";

bool result = this.saveDialog.ShowDialog() ?? false;

if (result)
{
    Settings.Default.PreviousPath = Path.GetDirectoryName(this.saveDialog.FileName);
    Settings.Default.Save();

    // Code writing to the new file...
}

This gives the behavior:

  • First time the program is run and dialog is opened, it navigates to "My Documents".
  • Consecutive runs and dialog opened:
    • If saved on a previous open, it navigates to previous save location.
    • If not saved on a previous open, navigates to "My Documents".
OhBeWise
  • 5,350
  • 3
  • 32
  • 60
0

If you use forward slash anywhere in your path, InitialDirectory does not work. Make sure they are converted to back slashes

Ali Akbar
  • 764
  • 1
  • 9
  • 13
0

I did some testing with .NET 2.0 and it seems if I set FileName to anything other than a literal string it doesn't work. When I use a method or accesstor to set the property the initial directory is ignored.

Icono123
  • 3,830
  • 3
  • 21
  • 20
0
savefiledialog.InitialDirectory = Application.StartupPath;
savefiledialog.RestoreDirectory = true;

tested a second ago.

PauLEffect
  • 423
  • 5
  • 17
0

This is what I ended up with, that goes along with where the OP wanted to point their dialog:

Microsoft.Win32.SaveFileDialog dlg = new Microsoft.Win32.SaveFileDialog();
dlg.InitialDirectory = null;

// May or may not want "saves", but this shows how to append subdirectories
string path = (Path.Combine(Environment.ExpandEnvironmentVariables("%USERPROFILE%"), "My Documents") + "\\saves\\");  

if (!Directory.Exists(path))
    Directory.CreateDirectory(path);

dlg.InitialDirectory = path;
dlg.RestoreDirectory = true;

if (dlg.ShowDialog().Value == true)
{
    // This is so you can have JUST the directory they selected
    path = dlg.FileName.Remove(dlg.FileName.LastIndexOf('\\'), dlg.FileName.Length - dlg.FileName.LastIndexOf('\\'));

    string filePath = Path.Combine(path, fileName);

    // This is "just-in-case" - say they created a new directory in the dialog,
    // but the dialog doesn't seem to think it exists because it didn't see it on-launch?
    if (!Directory.Exists(path))
        Directory.CreateDirectory(path);

    // Remove a file if it has the same name
    if (File.Exist(filePath))
        File.Delete(filePath);

    // Write the file, assuming you have and pass in the bytes
    using (FileStream fs = new FileStream(filePath, FileMode.CreateNew, FileAccess.Write)
    {
        fs.Write(bytes, 0, (int)bytes.Length);
        fs.Close();
    }
}
vapcguy
  • 7,097
  • 1
  • 56
  • 52
0

Just wanted to weigh in on this discussion (a couple of years too late) as I had exact problem too. Even though one is led to believe that this behavior - i.e using a default path the first time and then the previous selected path after that - can be achieved by using openFileDialog's properties and functions, one simply can't (per now)!

One could change the working directory to the desired default path (e.g. Environment.CurrentDirectory = Environment.GetFolderPath(Environment.SpecialFolder.Personal);) but some framework, like Unity, doesn't like this very much and terminates.

Because of this I ended up with this code:

private bool firstDialogOpened = true;

public void BrowseFiles()
{
    OpenFileDialog openFileDialog = new OpenFileDialog();
    openFileDialog.Filter = "Model files (*.obj)|*.obj|All files (*.*)|*.*";
    openFileDialog.FilterIndex = 1;

    if (firstDialogOpened)
    {
        openFileDialog.InitialDirectory = Environment.GetFolderPath(Environment.SpecialFolder.Personal);
        firstDialogOpened = false;
    }

    if (openFileDialog.ShowDialog() == DialogResult.OK)
        filepath.text = openFileDialog.FileName;
}

This gives the desired behavior for me, although I would have loved a more elegant solution.

0

'In terms of saving an output file to a desired directory in vb.net, 'the following was the way I found that worked like a charm:

Dim filcsv As String = fileNamey.Replace(".txt", "_Denied2.csv")
Dim filcsv2 As String = fileNamey.Replace(".txt", "_Approved2.csv")

Dim outputDirectory As String = "C:\Users\jlcmil\Documents\EnableMN\output\"
Dim totalPath As String = System.IO.Path.Combine(outputDirectory, filcsv)
Dim totalPath2 As String = System.IO.Path.Combine(outputDirectory, filcsv2)

Dim outDenied As StreamWriter = New StreamWriter(totalPath)
Dim outApproved As StreamWriter = New StreamWriter(totalPath2)
0

use the saveFileDialog.InitialDirectory property as follows:

Stream stream;
SaveFileDialog saveFileDialog = new SaveFileDialog();

saveFileDialog.Filter = "dat files (*.dat)|*.dat|All files (*.*)|*.*";
saveFileDialog.FilterIndex = 1;
saveFileDialog.RestoreDirectory = true;
saveFileDialog.InitialDirectory = @"D:\Data\...\foldername\";
saveFileDialog.ShowDialog();

if (!saveFileDialog.FileName.Equals(string.Empty))
    Console.WriteLine(saveFileDialog.FileName);
Pixel_95
  • 954
  • 2
  • 9
  • 21
0

In case anyone is looking to do this the proper way... just follow this MSDN article and everything will work properly: https://learn.microsoft.com/en-us/dotnet/api/system.windows.forms.filedialog.initialdirectory?view=net-5.0

And yes: this can be done dynamically... smart way to keep track is to use a textbox control as a "last path" reference... mind you most interfaces will already be showing a path textbox on the form, in which case, just use that one... note also here the fallback to MyDocuments if the folder does not exist...

Example:

Your form here is expected to have your typical Label, TextBox and "Browse" Button trio for 2 different file path lookups, but as you can see I use the same OpenFileDialog instance to setup both dialogs when their respective browse buttons are pushed:

    private void btn_KnownServersFilePath_Browse_Click(object sender, EventArgs e)
    {

        string tmppath = Path.GetDirectoryName(this.txt_Servers_TemplatePath.Text) + "\\";
        this.openFileDialog1.InitialDirectory = Directory.Exists(tmppath) ? tmppath : Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
        this.openFileDialog1.Filter = "excel files (*.xlsx)|*.xlsx|All files (*.*)|*.*";
        this.openFileDialog1.FilterIndex = 2;
        this.openFileDialog1.RestoreDirectory = true;
        this.openFileDialog1.FileName = Path.GetFileName(this.txt_Servers_TemplatePath.Text);
        

        if (this.openFileDialog1.ShowDialog() == DialogResult.OK)
            this.txt_Servers_TemplatePath.Text = this.openFileDialog1.FileName;
    }

    private void btn_ServerDumpFilePath_Browse_Click(object sender, EventArgs e)
    {
        string tmppath = Path.GetDirectoryName(this.txt_AllOU_TemplatePath.Text) + "\\";
        this.openFileDialog1.InitialDirectory = Directory.Exists(tmppath) ? tmppath : Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
        this.openFileDialog1.Filter = "AD dump files (*.csv)|*.csv|All files (*.*)|*.*";
        this.openFileDialog1.FilterIndex = 2;
        this.openFileDialog1.RestoreDirectory = true;
        this.openFileDialog1.FileName = Path.GetFileName(this.txt_AllOU_TemplatePath.Text);

        if (this.openFileDialog1.ShowDialog() == DialogResult.OK)
            this.txt_AllOU_TemplatePath.Text = this.openFileDialog1.FileName;
    }
MaxOvrdrv
  • 1,780
  • 17
  • 32