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

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

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

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

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

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

Ответ 1

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

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

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

Ответ 2

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

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

Полный исходный код (один короткий С# -модуль). Свободно. Лицензия 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;
}

Ответ 3

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

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

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

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

Ответ 4

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

Ответ 5

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

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

Ответ 6

После нескольких часов поиска я нашел этот ответ от leetNightShade до рабочее решение.

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

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

    Нет лицензии, так как вы свободны принимать и делать с кодом, что хотите.

Загрузите код здесь.

Ответ 7

Хорошо, позвольте мне попытаться подключить первую точку;-) Игра немного с Spy ++ или Winspector показывает, что текстовое поле Folder в VS Project Location - это настройка стандартного диалога. Это не то же поле, что и текстовое поле filename в стандартном диалоговом окне файла, например, в "Блокноте".

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

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

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

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

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

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

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

Подробнее в этой статье MSDN.

Ответ 8

Exact Audio Copy работает именно так в Windows XP. Отображается стандартный диалог открытия файла, но в поле имени файла содержится текст "Имя файла будет проигнорировано".

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

Изменить: это намного проще, чем я думал. Здесь код в С++/MFC, вы можете перевести его в среду по вашему выбору.

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

Редактировать 2: Это должен быть перевод на С#, но я не владею С#, поэтому не стреляйте в меня, если он не работает.

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
}

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

Ответ 9

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

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

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

Ответ 10

Вы можете использовать код, подобный этому

  • Фильтр скрывает файлы
  • Имя файла скроет первый текст

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

Код:

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

Ответ 11

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

Ответ 12

Попробуйте использовать Codeproject (кредит для Nitron):

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

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

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

    ::OleUninitialize();

    return retVal;
}

Ответ 13

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

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

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

Ответ 14

Первое решение

Я разработал это как очищенную версию . В диалоговом окне выбора папки NET Win 7 в стиле Билл Сэддон из lyquidity.com (у меня нет аффилированности). (Я узнал о его коде из другого ответа на этой странице). Я написал свой собственный, потому что для его решения требуется дополнительный класс Reflection, который не нужен для этой целенаправленной цели, использует управление потоком на основе исключений, не кэширует результаты своих вызовов отражения. Обратите внимание, что вложенный статический класс 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;
}

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

Второе решение

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

Ответ 15

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

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

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

Ответ 16

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

Как указать путь, используя диалог открытия файла в vb.net?