选择文件夹对话框WPF

时间:2010-10-24 10:36:31

标签: c# .net wpf folderbrowserdialog

我开发了一个WPF4应用程序,在我的应用程序中,我需要让用户选择一个应用程序存储内容的文件夹(文件,生成的报告等)。

我的要求:

  • 能够查看标准文件夹树

  • 能够选择文件夹

  • WPF外观&我觉得,这个对话框必须看起来像是为Windows Vista / 7而不是Windows 2000甚至Win9x设计的现代应用程序的一部分。

据我了解,直到2010年(.Net 4.0)才会出现标准文件夹对话框,但版本4.0可能会有一些变化吗?

或者剩下要做的就是使用老式的WinForms对话框?如果这是我需要的唯一方法,我怎样才能让它更接近Vista / 7风格而不是Win9x?

在某些论坛上,我看到了这种对话框的实现,但是在Windows 95中看到了丑陋的旧图标。它真的看起来不太好。

12 个答案:

答案 0 :(得分:98)

Pavel Yosifovich在第155页“使用常用对话框”部分中的

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;
}

这个黑客的唯一问题是:

  • 确认按钮仍然显示&#34;保存&#34;取而代之的是&#34;选择目录&#34;,但是在类似地雷的情况下我会&#34;保存&#34;目录选择,所以它仍然有效......
  • 输入字段仍然显示&#34;文件名&#34;而不是&#34;目录名称&#34;,但我们可以说目录是一种文件......
  • 还有一个&#34;保存类型&#34;下拉菜单,但其价值表示&#34;目录(* .this.directory)&#34;,用户无法更改其他内容,对我有用...

大多数人都没有注意到这些,虽然我绝对更喜欢使用正式的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的控件: FolderBrowserDialog in NET Core 3.0

对于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)

Ookii Dialogs for WPF有一个VistaFolderBrowserDialog类,它为WPF提供了文件夹浏览器对话框的完整实现。

  

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

enter image description here

还有一个Windows Forms可用的版本。

答案 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;
}