【问题标题】:Select folder dialog WPF选择文件夹对话框 WPF
【发布时间】:2011-04-29 19:16:46
【问题描述】:

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

我的要求:

  • 能够查看标准文件夹树

  • 能够选择文件夹

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

据我了解,在 2010 年 (.Net 4.0) 之前不会有标准文件夹对话框,但 4.0 版可能会有一些变化?

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

在一些论坛上,我看到了此类对话框的实现,但带有旧的丑陋图标,就像 Windows 95 一样。它看起来真的不好看。

【问题讨论】:

标签: c# .net wpf folderbrowserdialog


【解决方案1】:

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

【讨论】:

    【解决方案2】:

    System.Windows.Forms 中的 FolderBrowserDialog 类是显示允许用户选择文件夹的对话框的推荐方式。

    直到最近,此对话框的外观和行为与其他文件系统对话框不一致,这也是人们不愿使用它的原因之一。

    好消息是FolderBrowserDialogwas "modernized" in NET Core 3.0,因此现在对于那些编写针对该版本或更高版本的 Windows 窗体或 WPF 应用程序的人来说是一个可行的选择。

    在 .NET Core 3.0 中,Windows 窗体用户 [sic] 在 Windows Vista 中引入了基于 COM 的较新控件:

    reference System.Windows.Forms in a NET Core WPF app,需要编辑工程文件并添加以下行:

    <UseWindowsForms>true</UseWindowsForms>
    

    这可以直接放在现有的&lt;UseWPF&gt; 元素之后。

    那么就是使用对话框的一个例子:

    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 属性似乎也被忽略了,无论如何按钮总是显示。

    【讨论】:

      【解决方案3】:

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

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

      还有一个适用于Windows Forms 的版本。

      【讨论】:

      • 这是唯一不完整的答案。您编写 WPF 的原因是为了摆脱 Windows 窗体。使用一个 Windows 窗体对话框会拖入各种您不想要的东西。
      【解决方案4】:

      顺便说一句,WindowsAPICodePack 在 Windows 7 6.1.7600 上无法打开CommonOpenFileDialog

      【讨论】:

        【解决方案5】:

        如果您不想使用 Windows 窗体也不想编辑清单文件,我想出了一个非常简单的技巧,使用 WPF 的 SaveAs 对话框来实际选择目录。

        不需要 using 指令,您可以简单地复制粘贴下面的代码!

        它应该仍然非常用户友好,大多数人都不会注意到。

        这个想法来自这样一个事实,即我们可以很容易地更改该对话框的标题、隐藏文件以及处理生成的文件名。

        这肯定是一个大技巧,但也许它可以很好地满足您的使用需求...

        在这个例子中,我有一个文本框对象来包含生成的路径,但如果你愿意,你可以删除相关的行并使用返回值......

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

        这个 hack 的唯一问题是:

        • 确认按钮仍然显示“保存”而不是“选择目录”之类的内容,但在像我这样的情况下,我“保存”目录选择,所以它仍然有效...
        • 输入字段仍然显示“文件名”而不是“目录名”,但我们可以说目录是一种文件...
        • 仍有“另存为类型”下拉菜单,但其值为“目录 (*.this.directory)”,用户无法将其更改为其他内容,对我有用...

        大多数人不会注意到这些,虽然我肯定更喜欢使用官方的 WPF 方式,如果微软会让他们的头脑清醒,但在他们这样做之前,这是我的临时解决方案。

        【讨论】:

        • 被低估的答案
        【解决方案6】:

        只有这样的对话框是FileDialog。它是 WinForms 的一部分,但实际上它只是 WinAPI 标准 OS 文件对话框的包装器。而且我不认为它难看,它实际上是操作系统的一部分,所以看起来就像它运行的操作系统。

        否则,没有什么可以帮助您的。您要么需要寻找第 3 方实施,要么免费(我认为没有任何好处),要么付费。

        【讨论】:

        • 谢谢!到目前为止,这是最好的答案。为 MVVM 或标准窗口编写代码非常简单。
        • 但这不允许你选择一个文件夹......问题的重点
        【解决方案7】:

        Windows Presentation Foundation 4.5 Cookbook 由 Pavel Yosifovich 在第 155 页的“使用常用对话框”部分中说:

        “文件夹选择(而不是文件)怎么样?WPF OpenFileDialog 不支持。一种解决方案是使用 Windows Forms 的 FolderBrowseDialog 类。另一个好的解决方案是使用 简要介绍了 Windows API 代码包。”

        我从 Windows® API Code Pack for Microsoft® .NET Framework Windows API Code Pack: Where is it? 下载了 API 代码包,然后在我的 WPF 4.5 项目中添加了对 Microsoft.WindowsAPICodePack.dll 和 Microsoft.WindowsAPICodePack.Shell.dll 的引用。

        例子:

        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
        }
        

        【讨论】:

        • 我不敢相信微软没有费心在 WPF 中默认包含一个 FolderBrowserDialog...
        • Windows API 代码包可通过 Nuget herehere 获得。这对我来说效果很好。
        • 它适用于我。另外我只写下面的命令,所以帖子有所有信息。在 VS "Install-Package Windows7APICodePack-Core" , "Install-Package Windows7APICodePack-Shell" 中通过 Package Manager Console 安装
        • 我认为此文件的链接已关闭。
        • 我试过了,但 CommonOpenFileDialog 未被识别。 (是的,我输入了using)。
        【解决方案8】:

        The Windows API Code Pack-Shell 添加到您的项目中

        using Microsoft.WindowsAPICodePack.Dialogs;
        
        ...
        
        var dialog = new CommonOpenFileDialog();
        dialog.IsFolderPicker = true;
        CommonFileDialogResult result = dialog.ShowDialog();
        

        【讨论】:

          【解决方案9】:

          根据 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);
                  }
              }
          }
          

          【讨论】:

          • 将此与@TPowers 答案结合起来,效果很好。此外,对于“i”命名空间.. xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
          【解决方案10】:

          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

          【讨论】:

            【解决方案11】:

            很久以前我在我的博客上写过,WPF 对通用文件对话框的支持非常糟糕(或者至少是在 3.5 中,我没有在版本 4 中签入)——但它很容易解决。

            您需要将正确的清单添加到您的应用程序 - 这将为您提供现代风格的消息框和文件夹浏览器 (WinForms FolderBrowserDialog) 但不是 WPF 文件打开/保存对话框,这在这 3 个帖子中进行了描述(如果您不'不关心解释,只希望解决方案直接进入第3):

            幸运的是,打开/保存对话框是围绕 Win32 API 的非常薄的包装器,很容易使用正确的标志调用以获得 Vista/7 样式(在设置清单之后)

            【讨论】:

              【解决方案12】:

              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 的引用。

              【讨论】:

              • 我觉得重点是OpenFileDialog不能用来选择文件夹。
              猜你喜欢
              • 1970-01-01
              • 2015-11-29
              • 1970-01-01
              • 1970-01-01
              • 2017-10-21
              • 1970-01-01
              • 2011-05-22
              • 1970-01-01
              • 2020-11-24
              相关资源
              最近更新 更多