Диалог открытия каталога

274

Я хочу, чтобы пользователь выбрал каталог, в который будет сохранен файл, который я сгенерирую. Я знаю, что в WPF я должен использовать OpenFileDialogWin32, но, к сожалению, диалог требует выбора файла (ов) - он остается открытым, если я просто нажму OK, не выбрав один. Я мог бы «взломать» функциональность, позволив пользователю выбрать файл, а затем лишить путь, чтобы выяснить, к какому каталогу он принадлежит, но в лучшем случае он не интуитивно понятен. Кто-нибудь видел это сделано раньше?

Александра
источник
Возможное дублирование диалога выбора папки WPF
Developer
Взгляните сюда stackoverflow.com/questions/4007882/select-folder-dialog-wpf
Разработчик

Ответы:

406

Для этого вы можете использовать встроенный класс FolderBrowserDialog . Не против того, что это в System.Windows.Formsпространстве имен.

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

Если вы хотите, чтобы окно было модальным по сравнению с некоторым окном WPF, см. Вопрос « Как использовать FolderBrowserDialog из приложения WPF» .


РЕДАКТИРОВАТЬ: Если вы хотите что-то более причудливое, чем простая, уродливая Windows Forms FolderBrowserDialog, есть несколько альтернатив, которые позволяют использовать вместо этого диалог Vista:

  • Сторонние библиотеки, такие как диалоги Ookii (.NET 3.5)
  • Windows API Code Pack-Shell :

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

    Обратите внимание, что это диалоговое окно недоступно в операционных системах, более старых, чем Windows Vista, поэтому обязательно проверьте CommonFileDialog.IsPlatformSupportedсначала.

Heinzi
источник
78
Обратите внимание, что это ужасный диалог. Вы не можете скопировать и вставить в него путь, и он не поддерживает избранные папки. В целом, я бы дал 0 из 5 и рекомендовал бы, чтобы никто никогда не использовал его. За исключением того, что не было никакой разумной альтернативы, пока Windows Vista не выпустила намного лучший диалог папки . Есть хорошие бесплатные библиотеки, которые показывают хороший диалог на Vista +, и плохой на XP.
Роман Старков
70
Тем не менее, почему WPF предлагает отличный OpenFileDialog, но не OpenFolderDialog? Разве это не странно? Почему здесь не хватает WPF? Есть ли планы добавить класс для этого диалога в WPF?
Пол-Себастьян Маноле
14
Не забывайте, что FolderBrowserDialog является одноразовым.
LosManos
9
Обратите внимание, что для того, чтобы использовать CommonOpenFileDialogот WindowsAPICodePackвас нужно Install-Package WindowsAPICodePack-Shell. Ссылка в ответе не содержит этого.
Никола Новак
5
Msgstr "Не удалось найти тип или пространство имен CommonOpenFileDialog". Сейчас 2017, и я не могу выбрать папку
Nick.McDermaid
46

Я создал UserControl, который используется следующим образом:

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

Исходный код xaml выглядит так:

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

и код позади

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();
            }
        }
    }
 }
adrianm
источник
1
+1, хороший пример того, как написать UserControl. Один вопрос: зачем тебе be.UpdateSource? Разве уведомления об изменениях не должны быть автоматическими в свойствах зависимостей?
Хайнци
4
Вы можете указать в привязке, когда запускать обновления. По умолчанию он включен в LostFocus, но вы также можете указать ему запускать обновления и в PropertyChanged.
Александра
3
Привязка будет также обновляться для каждого нажатия клавиши. Если пользователь выполняет какую-либо проверку при обновлении (например, Directory.Exist), это может вызвать проблемы.
Адриан
10

Диалог папки Ookii можно найти на Nuget.

PM> Install-Package Ookii.Dialogs

И пример кода, как показано ниже.

var dialog = new Ookii.Dialogs.Wpf.VistaFolderBrowserDialog();
if (dialog.ShowDialog(this).GetValueOrDefault())
{
    textBoxFolderPath.Text = dialog.SelectedPath;
}
Youngjae
источник
tnx твой путь был самым коротким
ehsan wwe
8

Для тех, кто не хочет создавать настраиваемое диалоговое окно, но все же предпочитает 100% WPF-способ и не хочет использовать отдельные DDL, дополнительные зависимости или устаревшие API-интерфейсы, я предложил очень простой взлом с помощью диалогового окна «Сохранить как».

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

Это все еще должно быть очень удобным, и большинство людей никогда не заметят.

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

Это большой взлом, но, возможно, он отлично подойдет для вашего использования ...

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

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

Единственные проблемы с этим хаком:

  • Кнопка подтверждения по-прежнему говорит «Сохранить» вместо чего-то вроде «Выбрать каталог», но в случае, как мины, я «Сохранить» выбор каталога, чтобы он все еще работал ...
  • Поле ввода по-прежнему говорит «Имя файла» вместо «Имя каталога», но мы можем сказать, что каталог - это тип файла ...
  • По-прежнему есть раскрывающийся список «Сохранить как тип», но его значение говорит «Каталог (* .this.directory)», и пользователь не может изменить его на что-то другое, у меня работает ...

Большинство людей не заметят этого, хотя я бы определенно предпочел использовать официальный способ WPF, если бы Майкрософт вытащил свои головы из задниц, но пока они не заметят, это мое временное исправление.

Оливье St-L
источник
1
Это было круто. Удивлен, что никто другой, кажется, не пробовал это. Пакет NuGet намного лучше, конечно, но без NuGet WindowsAPICodePack это отличный способ взломать возможность выбора папки без добавления каких-либо новых пакетов / ссылок.
Код новичка
7

Чтобы в диалоге каталогов получить путь к каталогу, сначала добавьте ссылку System.Windows.Forms, а затем Resolve, а затем вставьте этот код в нажатие кнопки.

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

(folderpathTB - это имя TextBox, в которое я хочу поместить путь к папке, ИЛИ вы также можете присвоить его строковой переменной, т.е.)

    string folder = dialog.SelectedPath;

И если вы хотите получить FileName / path, просто сделайте это при нажатии кнопки

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

(folderpathTB - это имя TextBox, куда я хочу поместить путь к файлу, ИЛИ вы также можете присвоить его строковой переменной)

Примечание. Для диалога папок в проект необходимо добавить файл System.Windows.Forms.dll, иначе он не будет работать.

Зия Ур Рахман
источник
Спасибо за ваш ответ, но @Heinzi уже объяснил этот подход.
Александра
5

Я нашел код ниже по ссылке ниже ... и это сработало. Выберите папку в диалоговом окне 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
}
Саураб Раот
источник
4

Лучший способ добиться того, чего вы хотите - это создать свой собственный элемент управления на основе wpf или использовать тот, который был создан другими людьми,
почему? поскольку при использовании диалогового окна winforms в приложении wpf (по какой-то причине) будет заметно снижаться производительность,
я рекомендую этот проект
https://opendialog.codeplex.com/
или Nuget:

PM> Install-Package OpenDialog

он очень дружелюбен к MVVM и не переносит диалог winforms

bigworld12
источник
3

Я бы предложил добавить в пакет самородков:

  Install-Package OpenDialog

Тогда способ использовать это:

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

Вот документация: http://opendialog.codeplex.com/documentation

Работает для файлов, файлов с фильтром, папок и т. Д.

Хосе Ортега
источник
2

Ookii VistaFolderBrowserDialog- это то, что вы хотите.

Если вам нужен только браузер папок из Ooki Dialogs и ничего больше, тогда скачайте Source , выберите нужные вам файлы для браузера Folder (подсказка: 7 файлов), и он прекрасно встраивается в .NET 4.5.2. Я должен был добавить ссылку на System.Drawing. Сравните ссылки в оригинальном проекте с вашими.

Как вы выясните, какие файлы? Откройте свое приложение и Ookii в разных экземплярах Visual Studio. Добавьте VistaFolderBrowserDialog.csв свое приложение и продолжайте добавлять файлы, пока ошибки сборки не исчезнут. Вы найдете зависимости в проекте Ookii - удерживая нажатой клавишу «Control», щелкните на том, за которым вы хотите вернуться к его источнику (каламбур).

Вот файлы, которые вам нужны, если вам лень это делать ...

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

Отредактируйте строку 197, VistaFolderBrowserDialog.csесли вы не хотите включать ихResources.Resx

бросить новое InvalidOperationException (Properties.Resources.FolderBrowserDialogNoRootFolder);

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

Добавьте их уведомление об авторских правах в ваше приложение согласно их license.txt

Код в \Ookii.Dialogs.Wpf.Sample\MainWindow.xaml.csстроке 160-169 - это пример, который вы можете использовать, но вам нужно удалить его this,из MessageBox.Show(this,WPF.

Работает на моей машине [ТМ]

CAD CAD
источник
2

Я знаю, что это старый вопрос, но простой способ сделать это - использовать опцию FileDialog, предоставленную WPF, и использовать System.IO.Path.GetDirectory (имя файла).

Грегори Итон
источник
Но тогда пользователь должен выбрать файл, даже если ему говорят, чтобы выбрать папку. Неопытный пользователь может вызвать HelpDesk в этот момент, спросив, почему он должен выбрать файл, когда ему нужно выбрать папку
chriszo111
0

Ни один из этих ответов не работал для меня (как правило, отсутствовала ссылка или что-то в этом роде)

Но это довольно просто:

Использование FolderBrowserDialog в приложении WPF

Добавьте ссылку System.Windows.Formsи используйте этот код:

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

Нет необходимости отслеживать пропущенные пакеты. Или добавить огромные классы

Это дает мне современный селектор папок, который также позволяет создавать новые папки

Мне еще предстоит увидеть влияние при развертывании на других машинах

Nick.McDermaid
источник
0

Вы можете использовать что-то подобное в WPF. Я создал пример метода. Проверьте ниже.

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;           
       }
koberone
источник
1
Это требует от пользователя выбрать файл из папки. Если папка пуста, вы не можете выбрать ее.
Александру Дику
Да, я понимаю, что это своего рода обходной путь, а не идеальное решение для этой проблемы.
Кобероне