Диалог выбора папки WPF

Я разрабатываю приложение WPF4, и в моем приложении мне нужно позволить пользователю выбрать папку, в которой приложение будет что-то хранить (файлы, сгенерированные отчеты и т. Д.).

Мои требования:

  • Возможность увидеть стандартное дерево папок

  • Возможность выбрать папку

  • Внешний вид WPF, это диалоговое окно должно выглядеть как часть современного приложения, разработанного для Windows Vista / 7, а не Windows 2000 или даже Win9x.

Насколько я понимаю, до 2010 года (.Net 4.0) стандартного диалога папок не будет, но, может быть, в версии 4.0 есть какие-то изменения?

Или все, что остается, - это использовать диалог WinForms старой школы? Если это единственный способ сделать то, что мне нужно, как мне сделать его ближе к стилю Vista / 7, а не Win9x?

На некоторых форумах я видел реализацию таких диалогов, но со старыми уродливыми иконками а-ля Windows 95. Это действительно выглядит не очень красиво.


person Mike B.    schedule 24.10.2010    source источник
comment
Ознакомьтесь с фантастическим Ookii.Dialogs Свена Гроота как для WinForms, так и для WPF, которые дают вам современную Vista стиль папок и файловых диалогов.   -  person lightw8    schedule 02.03.2012
comment
Я использую модуль python wxPython github.com/wxWidgets/Phoenix   -  person JinSnow    schedule 19.01.2017
comment
Вот ссылка на обновленные диалоги Ookii для WPF, ориентированные на .NET 4.5 и доступно в NuGet   -  person C. Augusto Proiete    schedule 04.10.2018
comment
См. Это решение с использованием Microsoft.Win32.OpenDialog   -  person Lupa    schedule 20.03.2020


Ответы (12)


Я давно писал об этом в своем блоге, поддержка WPF для общих диалоговых окон файлов действительно плохая (или, по крайней мере, была в 3.5, я не проверял в версии 4), но это легко обойти.

Вам нужно добавить правильный манифест в свое приложение - это даст вам окна сообщений в современном стиле и браузер папок (WinForms FolderBrowserDialog), но не диалоговые окна открытия / сохранения файла WPF, это описано в этих 3 сообщениях (если вам все равно об объяснении и хочу, чтобы решение перешло непосредственно к 3-му):

К счастью, диалоги открытия / сохранения - это очень тонкие оболочки вокруг Win32 API, которые легко вызвать с правильными флагами, чтобы получить стиль Vista / 7 (после установки манифеста).

person Nir    schedule 24.10.2010

Поваренная книга Windows Presentation Foundation 4.5 Павла Йосифовича на стр. 155 в разделе «Использование общих диалоговых окон» говорится:

«А как насчет выбора папки (вместо файлов)? WPF OpenFileDialog не поддерживает это. Одно из решений - использовать класс Windows Forms FolderBrowseDialog. Еще одно хорошее решение - использовать пакет кода Windows API, описанный вкратце».

Я загрузил пакет кода API с сайта Windows® API Code Pack для Microsoft® .NET Framework Пакет кода Windows API: где он?, затем добавлены ссылки на Microsoft .WindowsAPICodePack.dll и Microsoft.WindowsAPICodePack.Shell.dll в мой проект WPF 4.5.

Пример:

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
}
person T Powers    schedule 18.07.2013
comment
Я не могу поверить, что Microsoft не удосужилась включить FolderBrowserDialog по умолчанию в WPF ... - person Snooze; 22.07.2013
comment
Пакеты кода Windows API доступны через Nuget здесь и здесь. У меня это сработало. - person Wallace Kelly; 09.10.2013
comment
У меня отлично работает. Также я просто пишу команды ниже, чтобы в посте была вся информация. Установить через консоль диспетчера пакетов в VS Install-Package Windows7APICodePack-Core, Install-Package Windows7APICodePack-Shell - person Peter T.; 20.10.2015
comment
Я считаю, что ссылка на этот файл не работает. - person alex; 25.04.2016
comment
Я пробовал это, но CommonOpenFileDialog не был распознан. (Да, я вставил using). - person Nick.McDermaid; 17.06.2017
comment
@ Nick.McDermaid Вы должны установить пакет через nuget: Install-Package WindowsAPICodePack-Shell -Version 1.1.1 - person bravohex; 20.06.2017
comment
Спасибо, что вернулись с советом. Мне не удалось найти пункт меню для консоли диспетчера пакетов, чтобы ввести это. Поэтому вместо этого я перешел в область управления пакетами nuget, нашел этот пакет (того же автора) и установил его. Я почти уверен, что он был установлен, потому что using работал нормально. Ответ внизу этой страницы сработал для меня впервые без каких-либо возня с пакетами nuget: stackoverflow.com/questions/4547320/ - person Nick.McDermaid; 20.06.2017
comment
CommonOpenFileDialog IDisposable. Вы должны заключить свой код в директиву using. - person Alexander Høst; 16.11.2019

Добавьте Windows API Code Pack-Shell в свой проект.

using Microsoft.WindowsAPICodePack.Dialogs;

...

var dialog = new CommonOpenFileDialog();
dialog.IsFolderPicker = true;
CommonFileDialogResult result = dialog.ShowDialog();
person zeynab farzaneh    schedule 23.07.2016

Если вы не хотите использовать Windows Forms или редактировать файлы манифеста, я придумал очень простой прием, используя диалоговое окно WPF SaveAs для фактического выбора каталога.

Директива using не требуется, вы можете просто скопировать и вставить приведенный ниже код!

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

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

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

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

// 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, если бы Microsoft вытащила голову из своей задницы, но пока они этого не сделают, это мое временное исправление.

person Olivier St-L    schedule 09.05.2018
comment
недооцененный ответ - person Ahmed Mohammed; 01.08.2018

MVVM + WinForms FolderBrowserDialog как поведение

public class FolderDialogBehavior : Behavior<Button>
{
    public string SetterName { get; set; }

    protected override void OnAttached()
    {
        base.OnAttached();
        AssociatedObject.Click += OnClick;
    }

    protected override void OnDetaching()
    {
        AssociatedObject.Click -= OnClick;
    }

    private void OnClick(object sender, RoutedEventArgs e)
    {
        var dialog = new FolderBrowserDialog();
        var result = dialog.ShowDialog();
        if (result == DialogResult.OK && AssociatedObject.DataContext != null)
        {
            var propertyInfo = AssociatedObject.DataContext.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public)
            .Where(p => p.CanRead && p.CanWrite)
            .Where(p => p.Name.Equals(SetterName))
            .First();

            propertyInfo.SetValue(AssociatedObject.DataContext, dialog.SelectedPath, null);
        }
    }
}

использование

     <Button Grid.Column="3" Content="...">
            <Interactivity:Interaction.Behaviors>
                <Behavior:FolderDialogBehavior SetterName="SomeFolderPathPropertyName"/>
            </Interactivity:Interaction.Behaviors>
     </Button>

Сообщение в блоге: http://kostylizm.blogspot.ru/2014/03/wpf-mvvm-and-winforms-folder-dialog-how.html

person Oyun    schedule 02.04.2014

Microsoft.Win32.OpenFileDialog - это стандартный диалог, который использует любое приложение в Windows. Ваш пользователь не удивится его внешнему виду, если вы используете WPF в .NET 4.0.

Диалог был изменен в Vista. WPF в .NET 3.0 и 3.5 по-прежнему использовал устаревшее диалоговое окно, но это было исправлено в .NET 4.0. Я могу только догадываться, что вы начали эту беседу, потому что видите старый диалог. Это, вероятно, означает, что вы на самом деле запускаете программу, ориентированную на 3.5. Да, оболочка Winforms действительно получила обновление и показывает версию Vista. System.Windows.Forms.OpenFileDialog, вам нужно добавить ссылку на System.Windows.Forms.

person Hans Passant    schedule 24.10.2010
comment
Я думаю, дело в том, что OpenFileDialog нельзя использовать для выбора папки. - person Neutrino; 30.04.2015

Основываясь на ответе Оюна, лучше использовать свойство зависимости для FolderName. Это позволяет (например) привязать к вложенным свойствам, что не работает в оригинале. Кроме того, в моей скорректированной версии в диалоговом окне выбирается исходная папка.

Использование в XAML:

<Button Content="...">
   <i:Interaction.Behaviors>
      <Behavior:FolderDialogBehavior FolderName="{Binding FolderPathPropertyName, Mode=TwoWay}"/>
    </i:Interaction.Behaviors>
</Button>

Код:

using System.Windows;
using System.Windows.Forms;
using System.Windows.Interactivity;
using Button = System.Windows.Controls.Button;

public class FolderDialogBehavior : Behavior<Button>
{
    #region Attached Behavior wiring
    protected override void OnAttached()
    {
        base.OnAttached();
        AssociatedObject.Click += OnClick;
    }

    protected override void OnDetaching()
    {
        AssociatedObject.Click -= OnClick;
        base.OnDetaching();
    }
    #endregion

    #region FolderName Dependency Property
    public static readonly DependencyProperty FolderName =
            DependencyProperty.RegisterAttached("FolderName",
            typeof(string), typeof(FolderDialogBehavior));

    public static string GetFolderName(DependencyObject obj)
    {
        return (string)obj.GetValue(FolderName);
    }

    public static void SetFolderName(DependencyObject obj, string value)
    {
        obj.SetValue(FolderName, value);
    }
    #endregion

    private void OnClick(object sender, RoutedEventArgs e)
    {
        var dialog = new FolderBrowserDialog();
        var currentPath = GetValue(FolderName) as string;
        dialog.SelectedPath = currentPath;
        var result = dialog.ShowDialog();
        if (result == DialogResult.OK)
        {
            SetValue(FolderName, dialog.SelectedPath);
        }
    }
}
person Edward    schedule 09.03.2015
comment
Объедините это с ответом @TPowers, и это отлично работает. Кроме того, для пространства имен i .. xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity" - person bdeem; 20.04.2016

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

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

Диалоговое окно браузера папок Ookii

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

person C. Augusto Proiete    schedule 04.10.2018
comment
Это единственный ответ, который не является полным клуджем. Причина, по которой вы пишете WPF, - уйти от Windows Forms. Использование одного диалогового окна Windows Forms перетаскивает всевозможные вещи, которые вам не нужны. - person AQuirky; 21.12.2020

Класс FolderBrowserDialog из System.Windows.Forms - это рекомендуемый способ отображения диалогового окна, позволяющего пользователю выбрать папку.

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

Хорошей новостью является то, что FolderBrowserDialog был модернизирован. в NET Core 3.0, поэтому теперь это жизнеспособный вариант для тех, кто пишет приложения Windows Forms или WPF, ориентированные на эту версию или более позднюю.

В .NET Core 3.0 пользователи Windows Forms [sic] используют новый элемент управления на основе COM, представленный в Windows Vista:  FolderBrowserDialog в NET Core 3.0

Чтобы ссылаться на System.Windows.Forms в приложении NET Core WPF, необходимо отредактировать файл проекта и добавить следующую строку:

<UseWindowsForms>true</UseWindowsForms>

Его можно разместить сразу после существующего элемента <UseWPF>.

Тогда это просто случай использования диалога:

using System;
using System.Windows.Forms;

...

using var dialog = new FolderBrowserDialog
{
    Description = "Time to select a folder",
    UseDescriptionForTitle = true,
    SelectedPath = Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory)
        + Path.DirectorySeparatorChar,
    ShowNewFolderButton = true
};

if (dialog.ShowDialog() == DialogResult.OK)
{
    ...
}

FolderBrowserDialog имеет свойство RootFolder, которое якобы устанавливает корневую папку, с которой начинается просмотр, но все, что я установил для него, не имело никакого значения; SelectedPath казалось лучшим свойством для этой цели, однако обратная косая черта в конце является обязательной.

Кроме того, свойство ShowNewFolderButton, похоже, также игнорируется, кнопка всегда отображается независимо.

person Steven Rands    schedule 08.12.2020

Только такой диалог - это FileDialog. Это часть WinForms, но на самом деле это единственная оболочка вокруг стандартного диалогового окна файлов ОС WinAPI. И я не думаю, что это некрасиво, на самом деле это часть ОС, поэтому похоже, что ОС, на которой она запущена.

В противном случае вам нечем помочь. Вам либо нужно искать стороннюю реализацию, либо бесплатную (и я не думаю, что есть что-то хорошее) или платное.

person Euphoric    schedule 24.10.2010
comment
Спасибо! Это, безусловно, лучший ответ. Очень просто кодировать как для MVVM, так и для стандартных окон. - person Mark Bonafe; 21.09.2015
comment
А вот папку ковырять не дает ... весь смысл вопроса - person Nick.McDermaid; 17.06.2017

Сразу скажу, что WindowsAPICodePack не может открыть CommonOpenFileDialog в Windows 7 6.1.7600.

person pivnothebearrrrrrrrr    schedule 28.10.2020

Комментарий к исходному вопросу от C.Augusto Proiete предложил диалоги Ookii (https://github.com/ookii-dialogs/ookii-dialogs-wpf). Это то, что я в итоге использовал в своей ситуации. Вот как я использовал это в своем приложении.

var dialog = new VistaFolderBrowserDialog()
{
    Description = "Select Folder",
    RootFolder = Environment.SpecialFolder.Desktop,
    ShowNewFolderButton = true,
    UseDescriptionForTitle = true
};

var result = dialog.ShowDialog();

if (result.HasValue && result.Value)
{
    _mySelectedFolder = dialog.SelectedPath;
}
person Matt Becker    schedule 01.04.2021