Диалоговое окно выбора папки в 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 58

9 ответов:

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

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

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

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

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

Я загрузил пакет кода API из пакет кода Windows® API для Microsoft® .NET FrameworkWindows API Code Pack: где это?, затем добавлены ссылки на 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, вам нужно будет добавить ссылку на систему.Окна.Формы.

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

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

использование в 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. И я не думаю, что это уродливо, на самом деле это часть ОС, поэтому похоже, что она работает.

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