Выберите диалог папки WPF

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

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

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

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

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

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

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

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

Ответ 1

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

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

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

Ответ 2

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

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

Я загрузил API Code Pack из Код кода Windows® для Microsoft®.NET Framework= "https://stackoverflow.com/q/24081665/109702" > Код кода 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
}

Ответ 3

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.

Ответ 4

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

Ответ 5

Добавить оболочку Windows API Pack-Shell в ваш проект

using Microsoft.WindowsAPICodePack.Dialogs;

...

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

Ответ 6

Только такое диалоговое окно FileDialog. Его часть WinForms, но на самом деле это только оболочка вокруг стандартного диалогового окна ОС Windows. И я не думаю, что это уродливо, его фактически часть ОС, поэтому он выглядит как ОС, на котором он запущен.

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

Ответ 7

На основе ответа Oyun лучше использовать свойство зависимостей для 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);
        }
    }
}