it-roy-ru.com

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

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

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

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

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

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

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

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

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

58
Mike B.

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

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

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

19
Nir

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

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

Я скачал API Code Pack с Windows® API Code Pack для Microsoft® .NET Framework Windows API Code Pack: где это? , затем добавил ссылки на 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
}
93
T Powers

Добавьте Windows API Code Pack-Shell к вашему проекту

using Microsoft.WindowsAPICodePack.Dialogs;

...

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

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

5
Oyun

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. 

5
Hans Passant

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

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

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

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

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

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

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

3
Olivier St-L

Основываясь на ответе Оюна, лучше использовать свойство зависимости для 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);
        }
    }
}
2
Edward

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

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

 enter image description here

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

1
Caio Proiete

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

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

0
Euphoric