我开发了一个WPF4应用程序,在我的应用程序中,我需要让用户选择一个应用程序存储内容的文件夹(文件,生成的报告等)。
我的要求:
能够查看标准文件夹树
能够选择文件夹
WPF外观&我觉得,这个对话框必须看起来像是为Windows Vista / 7而不是Windows 2000甚至Win9x设计的现代应用程序的一部分。
据我了解,直到2010年(.Net 4.0)才会出现标准文件夹对话框,但版本4.0可能会有一些变化吗?
或者剩下要做的就是使用老式的WinForms对话框?如果这是我需要的唯一方法,我怎样才能让它更接近Vista / 7风格而不是Win9x?
在某些论坛上,我看到了这种对话框的实现,但是在Windows 95中看到了丑陋的旧图标。它真的看起来不太好。
答案 0 :(得分:98)
Windows Presentation Foundation 4.5 Cookbook说:
“文件夹选择(而不是文件)怎么样?WPF OpenFileDialog不支持这一点。一种解决方案是使用Windows 表单'FolderBrowseDialog类。另一个好的解决方案是使用 Windows API代码包很快就会描述。“
我从 Windows® API Code Pack for Microsoft® .NET Framework Windows API Code Pack: Where is it?下载了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
}
答案 1 :(得分:20)
我很久以前在我的博客上写过这篇文章,WPF对普通文件对话框的支持非常糟糕(或者至少在3.5版本中没有检查过版本4) - 但是很容易解决它。
您需要将正确的清单添加到您的应用程序中 - 这将为您提供现代风格的消息框和文件夹浏览器(WinForms FolderBrowserDialog),而不是WPF文件打开/保存对话框,这些在这3篇文章中有描述(如果您不喜欢不关心解释,只希望解决方案直接进入第3节):
幸运的是,打开/保存对话框是围绕Win32 API的非常薄的包装器,使用正确的标志可以轻松调用以获得Vista / 7样式(在设置清单之后)
答案 2 :(得分:8)
将The Windows API Code Pack-Shell添加到您的项目
using Microsoft.WindowsAPICodePack.Dialogs;
...
var dialog = new CommonOpenFileDialog();
dialog.IsFolderPicker = true;
CommonFileDialogResult result = dialog.ShowDialog();
答案 3 :(得分:5)
Microsoft.Win32.OpenFileDialog是Windows上任何应用程序使用的标准对话框。在.NET 4.0中使用WPF时,您的用户不会对其外观感到惊讶
Vista中的对话框已被更改。 .NET 3.0和3.5中的WPF仍然使用旧版对话框,但在.NET 4.0中已修复。我只能猜到你开始这个帖子是因为你看到了那个旧的对话框。这可能意味着您实际上正在运行一个针对3.5的程序。是的,Winforms包装器已经获得升级并显示Vista版本。在System.Windows.Forms.OpenFileDialog类中,您需要添加对System.Windows.Forms的引用。
答案 4 :(得分:5)
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 :(得分:3)
如果您不想使用Windows窗体或编辑清单文件,我想出了一个非常简单的黑客,它使用WPF的SaveAs对话框来实际选择目录。
不需要使用指令,您只需复制粘贴下面的代码即可!
它应该仍然非常人性化,大多数人都不会注意到。
这个想法来自于我们可以更改对话框的标题,隐藏文件以及轻松解决生成的文件名这一事实。
这肯定是一个很大的黑客,但也许它可以很好地满足您的使用......
在这个示例中,我有一个文本框对象来包含生成的路径,但如果您愿意,可以删除相关的行并使用返回值...
// 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方式,如果微软会把他们的头从他们的屁股中解脱出来,但直到他们这样做,这才是我的临时解决方案。
答案 6 :(得分:2)
根据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);
}
}
}
答案 7 :(得分:2)
推荐使用FolderBrowserDialog
中的System.Windows.Forms
类来显示允许用户选择文件夹的对话框。
直到最近,此对话框的外观和行为都与其他文件系统对话框不一致,这是人们不愿意使用它的原因之一。
好消息是FolderBrowserDialog
was "modernized" in NET Core 3.0,对于那些编写针对该版本或更高版本的Windows Forms或WPF应用程序的人来说,现在是一个可行的选择。
在.NET Core 3.0中,Windows Forms用户 [sic] 是Windows Vista中引入的更新的基于COM的控件:
对于reference System.Windows.Forms
in a NET Core WPF app,有必要编辑项目文件并添加以下行:
<UseWindowsForms>true</UseWindowsForms>
可以将其直接放置在现有<UseWPF>
元素之后。
这只是使用对话框的一种情况:
using System;
using System.Windows.Forms;
...
using var dialog = new FolderBrowserDialog
{
Description = "Time to select a folder",
UseDescriptionForTitle = true,
SelectedPath = Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory)
+ Path.DirectorySeparatorChar,
ShowNewFolderButton = true
};
if (dialog.ShowDialog() == DialogResult.OK)
{
...
}
FolderBrowserDialog
具有一个RootFolder
属性,该属性应该“设置浏览从其开始的根文件夹” ,但是我对此进行的设置都没有任何区别; SelectedPath
似乎是用于此目的的更好属性,但是必须在结尾加上反斜杠。
此外,ShowNewFolderButton
属性似乎也被忽略,无论如何始终显示按钮。
答案 8 :(得分:1)
答案 9 :(得分:0)
只有FileDialog这样的对话框。它是WinForms的一部分,但它实际上只是围绕WinAPI标准OS文件对话框的包装。而且我认为它不是丑陋的,它实际上是操作系统的一部分,因此它看起来像是运行的操作系统。
其他方式,没有什么可以帮助你。您要么需要寻找第三方实施,要么免费(我认为没有任何好处)或付费。
答案 10 :(得分:0)
仅需说一句,WindowsAPICodePack
在Windows 7 6.1.7600上无法打开CommonOpenFileDialog
。
答案 11 :(得分:0)
C. Augusto Proiete 对原始问题的评论建议使用 Ookii 对话 (https://github.com/ookii-dialogs/ookii-dialogs-wpf)。这就是我最终在我的情况下使用的。这是我在我的应用中使用它的方式。
var dialog = new VistaFolderBrowserDialog()
{
Description = "Select Folder",
RootFolder = Environment.SpecialFolder.Desktop,
ShowNewFolderButton = true,
UseDescriptionForTitle = true
};
var result = dialog.ShowDialog();
if (result.HasValue && result.Value)
{
_mySelectedFolder = dialog.SelectedPath;
}