Выберите диалог папки 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);
}
}
}