Как настроить OpenFileDialog для выбора папок?

в VS .NET при выборе папки для проекта отображается диалоговое окно, которое выглядит как OpenFileDialog или SaveFileDialog, но настроено на прием только папок. С тех пор, как я это увидел, я хочу знать, как это делается. Я знаю о FolderBrowserDialog, но мне никогда не нравился этот диалог. Он начинается слишком маленьким и не позволяет мне воспользоваться возможностью ввода пути.

Я почти уверен, что сейчас нет способа сделать это из .NET, но Мне так же интересно, как вы это делаете из неуправляемого кода. За исключением полного переопределения диалога с нуля, как вы изменяете диалог, чтобы иметь такое поведение?

Я также хотел бы повторить, что я знаю о FolderBrowserDialog, но иногда мне не нравится его использовать, в дополнение к тому, что мне действительно интересно, как настроить диалог таким образом. Говоря мне просто использовать FolderBrowserDialog помогает мне поддерживать согласованный пользовательский интерфейс, но не удовлетворяет мой любопытство, так что это не будет считаться ответом.

Это не специфичная для Vista вещь; я видел этот диалог с VS .NET 2003, поэтому он выполним в Win2k и WinXP. Это меньше" я хочу знать правильный способ сделать это " вопрос и больше "мне было любопытно об этом, так как я впервые хотел сделать это в VS 2003" вопрос. Я понимаю, что диалог файла Vista имеет возможность сделать это, но он работает в XP, поэтому я знаю, что они сделали что-то получить это сработает. Конкретные ответы Vista не являются ответами, потому что Vista не существует в контексте вопроса.

Update: я принимаю ответ Скотта Вишневского, потому что он поставляется с рабочим образцом, но я думаю, что Серж заслуживает уважения за указание на настройку диалога (что, по общему признанию, неприятно из .NET, но это тут work) и Mark Ransom для выяснения того, что MS, вероятно, свернул пользовательский диалог для этой задачи.

17 ответов


У меня есть диалог, который я написал, называется диалогом OpenFileOrFolder, который позволяет вам открыть папку или файл.

Если значение AcceptFiles равно false,то он работает только в режиме accept folder.

вы можете скачать источник, из GitHub здесь


существует пакет кода API Windows. У этого есть много связанных с оболочкой вещей, включая CommonOpenFileDialog класса (на Microsoft.WindowsAPICodePack.Dialogs пространство имен). Это идеальное решение-обычный открытый диалог с отображением только папок.

вот пример того, как его использовать:

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

к сожалению, Microsoft больше не отправляет этот пакет, но несколько человек неофициально загрузили двоичные файлы в NuGet. Один пример можно найти здесь. Этот пакет только материал, специфичный для оболочки. Если вам это нужно, у того же пользователя есть несколько других пакетов, которые предлагают больше функциональности в исходном пакете.


можно использовать FolderBrowserDialogEx - повторно используемая производная от встроенного FolderBrowserDialog. Это позволяет ввести путь, даже UNC-путь. С его помощью можно также искать компьютеры или принтеры. Работает так же, как встроенный FBD, но ... лучше.

(EDIT: я должен был указать, что в этом диалоговом окне можно выбрать файлы или папки. )

полный исходный код (один короткий модуль 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;
}

на Ookii.Диалоги пакет содержит управляемую оболочку вокруг нового (Vista-style) диалогового окна браузера папок. Он также изящно деградирует в старых операционных системах.


для этого лучше использовать FolderBrowserDialog.

using (FolderBrowserDialog dlg = new FolderBrowserDialog())
{
    dlg.Description = "Select a folder";
    if (dlg.ShowDialog() == DialogResult.OK)
    {
        MessageBox.Show("You selected: " + dlg.SelectedPath);
    }
}

после нескольких часов поиска я нашел ответ by leetNightShade to рабочий раствор.

есть три вещи, которые, я считаю, делают это решение намного лучше, чем все остальные.

  1. он прост в использовании. Это требует только включения двух файлов (которые могут быть объединены в один в любом случае) в ваш проект.
  2. он возвращается к стандартному FolderBrowserDialog когда используется на XP или более старых системах.
  3. автор дает разрешение использовать код для любых целей, которые вы считаете подходящими.

    там нет лицензии, как таковой, как вы можете взять и сделать с кодом, что вы будете.

скачать код здесь.


OK, позвольте мне попробовать подключить первую точку ;-) Воспроизведение немного с Spy++ или Winspector показывает, что текстовое поле папки в расположении VS Project является настройкой стандартного диалога. Это не то же самое поле, что текстовое поле filename в стандартном диалоговом окне файла, например в блокноте.

оттуда, я полагаю, VS скрывает имя файла и тип файла textboxes / comboboxes и использует пользовательский шаблон диалога, чтобы добавить свою собственную часть в нижней части диалог.

EDIT: вот пример такой настройки и как это сделать (в Win32. не .NET):

m_ofn-это структура OPENFILENAME, лежащая в основе диалогового окна файла. Добавить эти 2 строчки:

  m_ofn.lpTemplateName = MAKEINTRESOURCE(IDD_FILEDIALOG_IMPORTXLIFF);
  m_ofn.Flags |= OFN_ENABLETEMPLATE;

где IDD_FILEDIALOG_IMPORTXLIFF-пользовательский шаблон диалога, который будет добавлен в нижней части диалога. См. часть красным цветом ниже. alt текст http://apptranslator.com/_so/customizedfiledialog.png

в этом случае настроенная часть - это только метка + гиперссылка, но это может быть любой диалог. Он может содержать кнопку OK, которая позволит нам проверять только выбор папки.

но как мы избавимся от некоторых элементов управления в стандартной части диалога, я не знаю.

более подробно эта статья MSDN.


Точная Аудио Копия работает таким образом в Windows XP. Стандартный диалог открытия файла отображается, но поле filename содержит текст "Filename будет проигнорирован".

просто угадать здесь, но я подозреваю, что строка вводится в поле со списком edit control каждый раз, когда в диалоговом окне вносятся значительные изменения. Если поле не пустое, а флаги диалогового окна установлены так, чтобы не проверять наличие файла, диалог можно закрыть обычно.

Edit: это намного проще, чем я думал. Вот код на C++ / MFC, вы можете перевести его в среду по вашему выбору.

CFileDialog dlg(true, NULL, "Filename will be ignored", OFN_HIDEREADONLY | OFN_NOVALIDATE | OFN_PATHMUSTEXIST | OFN_READONLY, NULL, this);
dlg.DoModal();

Edit 2: это должен быть перевод на C#, но я не свободно говорю на C#, поэтому не стреляйте в меня, если это не сработает.

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
}

Edit 3: наконец, посмотрел на фактический диалог, о котором идет речь, в Visual Studio 2005 (у меня не было доступа к нему ранее). это не стандартный диалог открытия файла! если вы проверите окна в Spy++ и сравните их со стандартным открытым файлом, вы увидите, что структура и имена классов не совпадают. При ближайшем рассмотрении можно также обнаружить некоторые различия между содержимым диалоговых окон. Мой вывод заключается в том, что Microsoft полностью заменила стандартный диалог в Visual Studio, чтобы дать ему эту возможность. Мое решение или что-то подобное будет настолько близко, насколько вы можете получить, если вы не хотите кодировать свой собственный с нуля.


вы можете подкласс диалогового окна файла и получить доступ ко всем его элементам управления. Каждый из них имеет идентификатор, который можно использовать для получения дескриптора окна. Затем вы можете показывать и скрывать их, получать от них сообщения об изменениях выбора и т. д. так далее. Все зависит от того, сколько усилий вы хотите взять.

мы сделали наш, используя поддержку класса WTL и настроили диалоговое окно файла, чтобы включить панель пользовательских мест и подключаемые представления COM.

MSDN предоставляет информацию о том, как сделать это использование Win32, эта статья CodeProject включает в себя пример и в этой статье CodeProject приведен пример .NET.


вы можете использовать такой код

  • фильтр скрывает файлы
  • имя файла-Скрыть текст

для расширенного скрытия текстового поля для имени файла вам нужно посмотреть OpenFileDialogEx

код:

{
    openFileDialog2.FileName = "\r";
    openFileDialog1.Filter = "folders|*.neverseenthisfile";
    openFileDialog1.CheckFileExists = false;
    openFileDialog1.CheckPathExists = false;
}

Я предполагаю, что вы на Vista, используя VS2008? В этом случае я думаю, что опция FOS_PICKFOLDERS используется при вызове диалогового окна файла Vista методе ifiledialog. Я боюсь, что в .NET-коде это потребует много gnarly P/Invoke interop-кода для работы.


Первый Вариант

Я разработал это как очищенную версию .NET Win 7-стиль папки выберите диалоговое окно Билл Седдон из lyquidity.com (у меня нет принадлежности). (Я узнал о его коде от еще один ответ на этой странице). Я написал свой собственный, потому что его решение требует дополнительного класса отражения, который не нужен для этой цели, использует управление потоком на основе исключений, не кэширует результаты его отражение зовет. Обратите внимание, что вложенный static VistaDialog класс таков, что его статические переменные отражения не пытаются заполнить, если Show метод никогда не вызывается. Он возвращается к диалогу pre-Vista, если не в достаточно высокой версии Windows. Должен работать в Windows 7, 8, 9, 10 и выше (теоретически).

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

Он используется так в форме Windows:

var dialog = new FolderSelectDialog {
    InitialDirectory = musicFolderTextBox.Text,
    Title = "Select a folder to import music from"
};
if (dialog.Show(Handle)) {
    musicFolderTextBox.Text = dialog.FileName;
}

вы можете, конечно, играть с его параметрами и какие свойства он предоставляет. Например, это позволяет multiselect в диалоговом окне в стиле Vista.

Второй Вариант

Саймон Мурье дал ответ это показывает, как выполнить ту же самую работу, используя взаимодействие с API Windows напрямую, хотя его версия должна быть дополнена, чтобы использовать диалог более старого стиля, если в более старой версии Windows. К сожалению, я еще не нашел его сообщение, когда работал над своим решением. Назови свой яд!


попробуйте этот из Codeproject (кредит Нитрон):

Я думаю, что это тот же диалог, о котором вы говорите - может быть, это поможет, если вы добавите скриншот?

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] = {''};
        if(::SHGetPathFromIDList(pIDL, buffer) != 0)
        {
            // Set the string value.
            folderpath = buffer;
            retVal = true;
        }       

        // free the item id list
        CoTaskMemFree(pIDL);
    }

    ::OleUninitialize();

    return retVal;
}

на Vista вы можете использовать методе ifiledialog с установленным параметром FOS_PICKFOLDERS. Это вызовет отображение OpenFileDialog-подобного окна, где вы можете выбрать папки:

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

для старых окон вы всегда можете прибегнуть к трюку с выбором любого файла в папке.

рабочий пример, который работает на .NET Framework 2.0 и более поздних версиях, можно найти здесь.


вы можете использовать такой код

фильтр является пустой строкой. Имя файла-AnyName, но не пустой

        openFileDialog.FileName = "AnyFile";
        openFileDialog.Filter = string.Empty;
        openFileDialog.CheckFileExists = false;
        openFileDialog.CheckPathExists = false;

Я знаю, что вопрос был о конфигурации OpenFileDialog но, видя, что Google привел меня сюда, я могу также указать, что если вы ищете только папки, вы должны использовать FolderBrowserDialog вместо этого, как ответил другой вопрос SO ниже

как указать путь с помощью диалогового окна открыть файл в vb.net?


на диалоги Ookii для WPF библиотека имеет класс, который предоставляет реализацию диалогового окна браузера папок для WPF.

https://github.com/caioproiete/ookii-dialogs-wpf

enter image description here

существует также версия, которая работает с Windows Forms.