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

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

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

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

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

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

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

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

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

9 ответов


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

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

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


Windows Presentation Foundation 4.5 Поваренная Книга по Yosifovich Павел на стр. 155 в разделе "использование стандартных диалоговых окон" говорит:

" Как насчет выбора папки (вместо файлов)? В WPF OpenFileDialog не поддерживает это. Одним из решений является использование Windows Класс FolderBrowseDialog форм. Другим хорошим решением является использование Пакет кода API Windows описан вкратце."

Я загрузил пакет кода API из пакет кода API Windows® для Microsoft® .NET Framework пакет кода API Windows: где он?, затем добавлены ссылки на Microsoft.WindowsAPICodePack.dll и Microsoft.WindowsAPICodePack.Ракушка.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
}

добавить пакет кода API Windows-Shell в проекте

using Microsoft.WindowsAPICodePack.Dialogs;

...

var dialog = new CommonOpenFileDialog();
dialog.IsFolderPicker = true;
CommonFileDialogResult result = dialog.ShowDialog();

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

диалоговое окно было изменено в Vista. WPF в .NET 3.0 и 3.5 все еще использовал устаревшее диалоговое окно, но это было исправлено в .NET 4.0. Я могу только догадываться, что вы начали эту тему, потому что вы видите этот старый диалог. Это, вероятно, означает, что вы на самом деле запускаете программу, которая нацелена на 3.5. Да, Обязанности и достижения фантик сделал получить обновление и версия ОС Vista. Система.Окна.Формы.Класс OpenFileDialog, вам нужно добавить ссылку на System.Окна.Формы.


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>

Blogpost: http://kostylizm.blogspot.ru/2014/03/wpf-mvvm-and-winforms-folder-dialog-how.html


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

использование в 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);
        }
    }
}

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

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

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

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

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

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

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

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

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

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


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

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

enter image description here

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


только такой диалог классов filedialog. Его часть WinForms,но его фактически только обертка вокруг стандартного диалогового окна ОС WinAPI. И я не думаю, что это уродливо, на самом деле это часть ОС, поэтому она выглядит как ОС, на которой она работает.

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