选择文件夹对话框 WPF

发布于 2024-09-28 15:52:49 字数 399 浏览 5 评论 0原文

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

我的要求:

  • 能够查看标准文件夹树

  • 能够选择文件夹

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

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

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

I develop a WPF4 application and in my app I need to let the user select a folder where the application will store something (files, generated reports etc.).

My requirements:

  • Ability to see the standard folder tree

  • Ability to select a folder

  • WPF look & feel, this dialog must look like part of a modern application designed for Windows Vista/7 and not Windows 2000 or even Win9x.

As I understand, until 2010 (.Net 4.0) there won't be a standard folder dialog, but maybe there are some changes in version 4.0?

Or the only thing I can do, is to use an old-school WinForms dialog? If it's the only way to do what I need, how can I make it looking closer to Vista/7 style and not Win9x?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(12

冰之心 2024-10-05 15:52:49

Windows Presentation Foundation 4.5 Cookbook,作者:Pavel Yosifovich,页面155 在“使用通用对话框”部分中说:

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

我从 Windows® API 代码包下载了 API 代码包Microsoft® .NE​​T Framework Windows API 代码包:它在哪里?< /a>,然后将对 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
}

Windows Presentation Foundation 4.5 Cookbook by Pavel Yosifovich on page 155 in the section on "Using the common dialog boxes" says:

"What about folder selection (instead of files)? The WPF
OpenFileDialog does not support that. One solution is to use Windows
Forms' FolderBrowserDialog class. Another good solution is to use the
Windows API Code Pack described shortly."

I downloaded the API Code Pack from Windows® API Code Pack for Microsoft® .NET Framework Windows API Code Pack: Where is it?, then added references to Microsoft.WindowsAPICodePack.dll and Microsoft.WindowsAPICodePack.Shell.dll to my WPF 4.5 project.

Example:

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
}
樱花细雨 2024-10-05 15:52:49

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

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

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

I wrote about it on my blog a long time ago, WPF's support for common file dialogs is really bad (or at least is was in 3.5 I didn't check in version 4), but it's easy to work around it.

You need to add the correct manifest to your application, that will give you a modern style message boxes and folder browser (WinForms, FolderBrowserDialog) but not WPF file open/save dialogs, this is described in those 3 posts (if you don't care about the explanation and only want the solution go directly to the 3rd):

Fortunately, the open/save dialogs are very thin wrappers around the Win32 API that is easy to call with the right flags to get the Vista/7 style (after setting the manifest)

一笑百媚生 2024-10-05 15:52:49

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

using Microsoft.WindowsAPICodePack.Dialogs;

...

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

Add The Windows API Code Pack-Shell to your project

using Microsoft.WindowsAPICodePack.Dialogs;

...

var dialog = new CommonOpenFileDialog();
dialog.IsFolderPicker = true;
CommonFileDialogResult result = dialog.ShowDialog();
叹倦 2024-10-05 15:52:49

如果您不想使用 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;
}

这个黑客的唯一问题是:

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

大多数人不会注意到这些,尽管如果微软愿意的话,我肯定更喜欢使用官方的 WPF 方式,但在他们这样做之前,这就是我的临时解决办法。

If you don't want to use Windows Forms nor edit manifest files, I came up with a very simple hack using WPF's SaveAs dialog for actually selecting a directory.

No using directive needed, you may simply copy-paste the code below !

It should still be very user-friendly and most people will never notice.

The idea comes from the fact that we can change the title of that dialog, hide files, and work around the resulting filename quite easily.

It is a big hack for sure, but maybe it will do the job just fine for your usage...

In this example I have a textbox object to contain the resulting path, but you may remove the related lines and use a return value if you wish...

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

The only issues with this hack are :

  • Acknowledge button still says "Save" instead of something like "Select directory", but in a case like mines I "Save" the directory selection so it still works...
  • Input field still says "File name" instead of "Directory name", but we can say that a directory is a type of file...
  • There is still a "Save as type" dropdown, but its value says "Directory (*.this.directory)", and the user cannot change it for something else, works for me...

Most people won't notice these, although I would definitely prefer using an official WPF way if microsoft would get their heads out of their asses, but until they do, that's my temporary fix.

—━☆沉默づ 2024-10-05 15:52:49

建议使用 System.Windows.Forms 中的 FolderBrowserDialog 类来显示允许用户选择文件夹的对话框。

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

好消息是 FolderBrowserDialog 在 NET Core 3.0 中进行了“现代化”,因此对于那些编写针对该版本或更高版本的 Windows 窗体或 WPF 应用程序的人来说,现在是一个可行的选择。

在 .NET Core 3.0 中,Windows 窗体用户[原文如此] Windows Vista 中引入的基于 COM 的较新控件:
.NET Core 3.0 中的FolderBrowserDialog

参考< code>System.Windows.Forms 在 NET Core WPF 应用中,需要编辑项目文件并添加以下行:

<UseWindowsForms>true</UseWindowsForms>

这可以直接放置在现有 之后; 元素。

那么这只是使用对话框的情况:

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

The FolderBrowserDialog class from System.Windows.Forms is the recommended way to display a dialog that allows a user to select a folder.

Until recently, the appearance and behaviour of this dialog was not in keeping with the other file system dialogs, which is one of the reasons why people were reluctant to use it.

The good news is that FolderBrowserDialog was "modernized" in NET Core 3.0, so is now a viable option for those writing either Windows Forms or WPF apps targeting that version or later.

In .NET Core 3.0, Windows Forms users [sic] a newer COM-based control that was introduced in Windows Vista:
FolderBrowserDialog in NET Core 3.0

To reference System.Windows.Forms in a NET Core WPF app, it is necessary to edit the project file and add the following line:

<UseWindowsForms>true</UseWindowsForms>

This can be placed directly after the existing <UseWPF> element.

Then it's just a case of using the dialog:

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 has a RootFolder property that supposedly "sets the root folder where the browsing starts from" but whatever I set this to it didn't make any difference; SelectedPath seemed to be the better property to use for this purpose, however the trailing backslash is required.

Also, the ShowNewFolderButton property seems to be ignored as well, the button is always shown regardless.

蓝眼睛不忧郁 2024-10-05 15:52:49

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

Microsoft.Win32.OpenFileDialog is the standard dialog that any application on Windows uses. Your user won't be surprised by its appearance when you use WPF in .NET 4.0

The dialog was altered in Vista. WPF in .NET 3.0 and 3.5 still used the legacy dialog but that was fixed in .NET 4.0. I can only guess that you started this thread because you are seeing that old dialog. Which probably means you're actually running a program that is targeting 3.5. Yes, the Winforms wrapper did get the upgrade and shows the Vista version. System.Windows.Forms.OpenFileDialog class, you'll need to add a reference to System.Windows.Forms.

李白 2024-10-05 15:52:49

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

MVVM + WinForms FolderBrowserDialog as behavior

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

Usage

     <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

枯寂 2024-10-05 15:52:49

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

Based on Oyun's answer, it's better to use a dependency property for the FolderName. This allows (for example) binding to sub-properties, which doesn't work in the original. Also, in my adjusted version, the dialog shows selects the initial folder.

Usage in XAML:

<Button Content="...">
   <i:Interaction.Behaviors>
      <Behavior:FolderDialogBehavior FolderName="{Binding FolderPathPropertyName, Mode=TwoWay}"/>
    </i:Interaction.Behaviors>
</Button>

Code:

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);
        }
    }
}
ぽ尐不点ル 2024-10-05 15:52:49

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

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

Ookii 文件夹浏览器对话框

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

The Ookii Dialogs for WPF has a VistaFolderBrowserDialog class that provides a complete implementation of a folder browser dialog for WPF.

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

Ookii Folder Browser dialog

There's also a version that works with Windows Forms.

捶死心动 2024-10-05 15:52:49

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

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

Only such dialog is FileDialog. Its part of WinForms, but its actually only wrapper around WinAPI standard OS file dialog. And I don't think it is ugly, its actually part of OS, so it looks like OS it is run on.

Other way, there is nothing to help you with. You either need to look for 3rd party implementation, either free (and I don't think there are any good) or paid.

笔芯 2024-10-05 15:52:49

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

A comment on the original question from C. Augusto Proiete suggested Ookii dialogs (https://github.com/ookii-dialogs/ookii-dialogs-wpf). That's what I ended up using in my situation. Here's how I used it in my app.

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;
}
老街孤人 2024-10-05 15:52:49

只是说一件事,WindowsAPICodePack 无法在 Windows 7 6.1.7600 上打开 CommonOpenFileDialog

Just to say one thing, WindowsAPICodePack can not open CommonOpenFileDialog on Windows 7 6.1.7600.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文