打开文件对话框的默认路径

27
using (var openFileDialog1 = new OpenFileDialog())
        {
            openFileDialog1.Reset();
            if (!string.IsNullOrEmpty(ExcelFilePath))
            {
                string fileName = Path.GetFileName(ExcelFilePath);
                string fileExt = Path.GetExtension(ExcelFilePath);
                //Avoid "you can't open this location using this program file" dialog 
                //if there is a file name in the path strip it )
                if (!string.IsNullOrEmpty(fileName))
                    initialDirectory = Path.GetDirectoryName(ExcelFilePath);  
      //if not let it be   
                else
                    initialDirectory = ExcelFilePath;

            openFileDialog1.InitialDirectory = initialDirectory;
            }
            else
                openFileDialog1.InitialDirectory = "c:\\";
            openFileDialog1.Filter = "Excel files (*.xls or *.xlsx)|*.xls;*.xlsx";
            //openFileDialog1.Filter = "xls files (*.xls)|*.xls|xlsx files(*.xlsx)|.xlsx";
            openFileDialog1.FilterIndex = 2;
            openFileDialog1.RestoreDirectory = false;
            openFileDialog1.CheckFileExists = true;
            openFileDialog1.CheckPathExists = true;
            if (openFileDialog1.ShowDialog() == DialogResult.OK)
            {
                var browseSelectionMade = BrowseSelectionMade;
                if (browseSelectionMade!=null)
                    browseSelectionMade(this, new DataEventArgs<string>(openFileDialog1.FileName));
            }
        }

不管我是否将RestoreDirectory设置为true,如果我的初始目录设置为不存在的路径,我总是会浏览到上次使用过的目录。 OpenFileDialog保存上次使用的目录在哪里?是否有办法覆盖这种行为?(例如,如果初始目录不存在,我始终希望将其设置为C:\)?
8个回答

46

看起来你只需要做以下操作:

string path; // this is the path that you are checking.
if(Directory.Exists(path)) {
    openFileDialog1.InitialDirectory = path;
} else {
    openFileDialog1.InitialDirectory = @"C:\";
} 

除非我漏掉了什么。


1
openFileDialog1.InitialDirectory = Directory.Exists(path) ? path : @"C:"; - Gilles jr Bisson
请注意,如果您使用正斜杠 '/',InitialiDirectory 将无法正常工作。如果您的应用程序使用正斜杠来确定目录位置,则可能需要将它们替换为反斜杠:path.Replace("/", "\\") - Victor Chelaru

18
上次使用的目录保存在注册表中。确切的位置取决于Windows版本,对于Win7,它位于HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\ComDlg32。通过regedit进行快速查看应该能够说服您不要去更改它。
简单的解决方法是提供一个有效的路径。如果您计算的路径无效,Directory.Exists将返回false,则提供一个有效的路径,例如Environment.GetFolderPath()返回的Documents文件夹。然而,上次使用的路径也没有问题,用户很容易识别它,并且它很可能靠近所需的路径。

感谢你们两个的回答,我会将你的回答标记为采纳的答案,虽然第二个答案也很好。对我来说,最重要的是终于有了一个确切的回答——路径存储在哪里,这是一个加分项。再次感谢! - Eu Lupu

5

我认为没有内置的功能可以实现这一点。只需在打开对话框之前进行检查:

if (!Directory.Exists(initialDirectory))
{
    openFileDialog1.InitialDirectory = @"C:\";
}

3

检查Excel文件路径是否存在,你需要检查其是否为null或为空,但如果在代码块之前检查目录是否存在,并且如果不存在则将其重置为空字符串,则应该没有问题。

(是的,你需要提前应用你的文件名逻辑等)但是一旦你解析出所有内容,确定目录是否存在就很容易。

if (!Directory.Exists(excelPath))
{
    ExcelFilePath = String.Empty;
}

2

1

给未来的我:

记得要做以下事情:

            try        
            {
                result = dialog.ShowDialog(Window);
            }
            catch
            {
                dialog.InitialDirectory = Environment.GetFolderPath(Environment.SpecialFolder.Desktop);
                result = dialog.ShowDialog(Window);
            }

当用户从不存在的位置(例如USB驱动器、映射的网络驱动器)打开文件时,这将有所帮助——如果InitialDirectory无效,则ShowDialog会抛出异常。

1
编辑:在咨询了一个更有经验的朋友之后,也许更好的解决方案在回顾时显而易见。只需将您自己的注册表键存储在HKEY_CURRENT_USER\SOFTWARE\YourCompanyOrAppName\Whatevs或类似位置(不确定最佳实践或您具有读/写访问权限的文件夹,自行研究),就可以完全避免这个问题。只需让用户导航到他们想要的位置一次,然后将路径存储在注册表中(作为普通字符串,而不是PIDL),并在下一次检索该路径。有关注册表和RegistryKey类的详细信息,请参阅MSDN文章以及RegistryKey / Methods / SetValue文章中的示例。尽管如此,我仍将保留此帖子,作为好奇心或如果有人有非常特定的问题并需要此解决方案。祝好运!

如果未来有任何可怜的人在这里徘徊,似乎我找到了如何找到最后使用的目录。就像之前所述,它存储在注册表中,更具体地说是在HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\ComDlg32\OpenSavePidlMRU\。这里有一组文件扩展名的文件夹,包括未知文件扩展名的“*”。我将为txt文件执行此操作,并根据需要更改路径。要访问此路径,我们创建一个RegistryKey并调用OpenSubKey(顺便说一句,下面是完整代码)。

string RegistryPath = @"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\ComDlg32\\OpenSavePidlMRU\\txt";
RegistryKey rk = Registry.CurrentUser.OpenSubKey(RegistryPath);

这里是一组条目,它们都包含最近打开或保存的项目的PIDL(我们会提到它)。请注意:文件夹名称OpenSavePidlMRU有时被称为OpenSaveMRU,并且似乎适用于早于win 10版本的系统。还有一个名为“MRUListEx”的条目,MRU代表“最近使用”。在此条目中,有一个索引指示最近使用的项目。因此,如果我有10个条目,命名为0至9,而9是最后一个使用的,则MRUListEx中的第一个字节将是0x09。
byte[] mrulistex = (byte[])rk.GetValue("MRUListEx");
byte Last = mrulistex[0];

在我的系统上,Last将等于0x09。然后我们再次调用GetValue,但是针对该条目。

byte[] LastPathByteArray = (byte[])rk.GetValue(Last.ToString());

这里出现了问题,它不会返回一个字节数组,其中每个字节都是我们文件路径中的一个字符,而是返回所谓的PIDL。虽然字节数组似乎包含了路径,在char和wide char中都是如此,但它也包含了一堆难以转换的无用信息。 我不会假装理解它,但https://stackoverflow.com/a/4318663提供了一种将其转换为字符串的方法。(请参见下面的代码)
string LastPath = GetPathFromPIDL(LastPathByteArray);

我们完成了。请注意,这并不一定代表一个好的解决方案,但是在我挖掘半小时后,我没有找到太多官方文档。显然,这段代码不检查注册表路径是否正确,注册表键是否存在,也没有进行太多错误检查,但至少可以工作。

using Microsoft.Win32; //for the registry class
using System.Runtime.InteropServices; //for converting the PIDL

//GetPathFromPIDL from matt.schechtman at https://stackoverflow.com/a/4318663
[DllImport("shell32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool SHGetPathFromIDListW(IntPtr pidl, MarshalAs(UnmanagedType.LPTStr)] StringBuilder pszPath);
private string GetPathFromPIDL(byte[] byteCode)
{
    //MAX_PATH = 260
    StringBuilder builder = new StringBuilder(260);

    IntPtr ptr = IntPtr.Zero;
    GCHandle h0 = GCHandle.Alloc(byteCode, GCHandleType.Pinned);
    try
    {
        ptr = h0.AddrOfPinnedObject();
    }
    finally
    {
        h0.Free();
    }

    SHGetPathFromIDListW(ptr, builder);

    return builder.ToString();
}

public void OnClick_Button_OpenFile(object sender, RoutedEventArgs e)
{
    string RegistryPath = @"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\ComDlg32\\OpenSavePidlMRU\\txt";
    RegistryKey rk = Registry.CurrentUser.OpenSubKey(RegistryPath);
    byte[] mrulistex = (byte[])rk.GetValue("MRUListEx");
    byte Last = mrulistex[0];
    byte[] LastPathByteArray = (byte[])rk.GetValue(Last.ToString());
    string LastPath = GetPathFromPIDL(LastPathByteArray);

    // Configure open file dialog box
    Microsoft.Win32.OpenFileDialog dlg = new Microsoft.Win32.OpenFileDialog();`
    dlg.InitialDirectory = LastPath;
    result = dlg.ShowDialog();
    if (result == true)
    {
        string filename = dlg.FileName;
    }
    //etc etc, rest of your code
}

祝你好运。


0
如果您正在使用存储在某个字符串中的文件名,最好使用路径来截取文件名(在我的W10上,如果我只提供文件名,打开对话框不会打开在初始目录中)。
    if (!System.IO.Directory.Exists(filename))
    {
        openDlg.InitialDirectory =
            System.IO.Path.GetDirectoryName(filename);
    }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接