Mở hộp thoại thư mục


273

Tôi muốn người dùng chọn một thư mục trong đó một tệp mà sau đó tôi sẽ tạo sẽ được lưu. Tôi biết rằng trong WPF tôi nên sử dụng OpenFileDialogtừ Win32, nhưng không may là hộp thoại yêu cầu (các) tệp được chọn - nó vẫn mở nếu tôi chỉ cần nhấp vào OK mà không chọn. Tôi có thể "hack" chức năng bằng cách cho phép người dùng chọn một tệp và sau đó tách đường dẫn để tìm ra thư mục nào thuộc về nó nhưng điều đó không trực quan nhất. Có ai nhìn thấy điều này được thực hiện trước đây?




Câu trả lời:


406

Bạn có thể sử dụng lớp FolderBrowserDialog tích hợp cho việc này. Đừng bận tâm rằng đó là trong System.Windows.Formskhông gian tên.

using (var dialog = new System.Windows.Forms.FolderBrowserDialog())
{
    System.Windows.Forms.DialogResult result = dialog.ShowDialog();
}

Nếu bạn muốn cửa sổ được kiểm duyệt qua một số cửa sổ WPF, hãy xem câu hỏi Làm thế nào để sử dụng FolderBrowserDialog từ một ứng dụng WPF .


EDIT: Nếu bạn muốn một cái gì đó lạ mắt hơn một chút so với Windows Forms FolderBrowserDialog đơn giản, xấu xí, có một số lựa chọn thay thế cho phép bạn sử dụng hộp thoại Vista thay thế:

  • Thư viện của bên thứ ba, chẳng hạn như hộp thoại Ookii (.NET 3.5)
  • Các Windows API Mã Gói-Shell :

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

    Lưu ý rằng hộp thoại này không khả dụng trên các hệ điều hành cũ hơn Windows Vista, vì vậy hãy chắc chắn kiểm tra CommonFileDialog.IsPlatformSupportedtrước.


78
Hãy lưu ý rằng đây là một hộp thoại khủng khiếp. Bạn không thể sao chép và dán một đường dẫn vào đó và nó không hỗ trợ các thư mục yêu thích. Nhìn chung, tôi cho điểm 0 trên 5 và khuyên mọi người không bao giờ sử dụng nó. Ngoại trừ việc không có sự thay thế hợp lý nào cho đến khi Windows Vista xuất hiện với hộp thoại thư mục tốt hơn nhiều . Có những thư viện miễn phí tốt hiển thị hộp thoại tốt trên Vista + và thư viện xấu trên XP.
Roman Starkov

70
Tuy nhiên, tại sao WPF cung cấp một OpenFileDialog tuyệt vời nhưng không có OpenFolderDialog? Điều đó có hơi lạ không? Tại sao WPF thiếu ở đây? Có kế hoạch nào để thêm một lớp cho hộp thoại này trong WPF không?
Paul-Sebastian Manole

14
Đừng quên rằng FolderBrowserDialog là dùng một lần.
LosManos

9
Lưu ý rằng để sử dụng CommonOpenFileDialogtừ WindowsAPICodePackbạn cần phải Install-Package WindowsAPICodePack-Shell. Các liên kết được cung cấp trong câu trả lời không liệt kê đó.
Nikola Novak

5
"Không thể tìm thấy loại hoặc không gian tên CommonOpenFileDialog". Đó là năm 2017 và tôi không thể chọn một thư mục
Nick.McDilyn

46

Tôi đã tạo một UserControl được sử dụng như thế này:

  <UtilitiesWPF:FolderEntry Text="{Binding Path=LogFolder}" Description="Folder for log files"/>

Nguồn xaml trông như thế này:

<UserControl x:Class="Utilities.WPF.FolderEntry"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <DockPanel>
        <Button Margin="0" Padding="0" DockPanel.Dock="Right" Width="Auto" Click="BrowseFolder">...</Button>
        <TextBox Height="Auto" HorizontalAlignment="Stretch" DockPanel.Dock="Right" 
           Text="{Binding Text, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}}" />
    </DockPanel>
</UserControl>

và mã phía sau

public partial class FolderEntry {
    public static DependencyProperty TextProperty = DependencyProperty.Register("Text", typeof(string), typeof(FolderEntry), new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));
    public static DependencyProperty DescriptionProperty = DependencyProperty.Register("Description", typeof(string), typeof(FolderEntry), new PropertyMetadata(null));

    public string Text { get { return GetValue(TextProperty) as string; } set { SetValue(TextProperty, value); }}

    public string Description { get { return GetValue(DescriptionProperty) as string; } set { SetValue(DescriptionProperty, value); } }

    public FolderEntry() { InitializeComponent(); }

    private void BrowseFolder(object sender, RoutedEventArgs e) {
        using (FolderBrowserDialog dlg = new FolderBrowserDialog()) {
            dlg.Description = Description;
            dlg.SelectedPath = Text;
            dlg.ShowNewFolderButton = true;
            DialogResult result = dlg.ShowDialog();
            if (result == System.Windows.Forms.DialogResult.OK) {
                Text = dlg.SelectedPath;
                BindingExpression be = GetBindingExpression(TextProperty);
                if (be != null)
                    be.UpdateSource();
            }
        }
    }
 }

1
+1, ví dụ hay về cách viết UserControl. Một câu hỏi: Tại sao bạn cần be.UpdateSource? Không nên thay đổi thông báo là tự động trong thuộc tính phụ thuộc?
Heinzi

4
Bạn có thể chỉ định trong ràng buộc khi bắt đầu cập nhật. Theo mặc định, nó nằm trên LostF Focus nhưng bạn cũng có thể yêu cầu nó chạy các bản cập nhật trên PropertyChanged.
Alexandra

3
Các ràng buộc sau đó cũng sẽ được cập nhật cho mỗi tổ hợp phím. Nếu người dùng thực hiện một số loại xác nhận khi cập nhật (ví dụ: Directory.Exist), nó có thể gây ra sự cố.
adrianm


10

Hộp thoại thư mục Ookii có thể được tìm thấy tại Nuget.

PM> Install-Package Ookii.Dialogs

Và, mã ví dụ như dưới đây.

var dialog = new Ookii.Dialogs.Wpf.VistaFolderBrowserDialog();
if (dialog.ShowDialog(this).GetValueOrDefault())
{
    textBoxFolderPath.Text = dialog.SelectedPath;
}

tnx theo cách của bạn là ngắn nhất
ehsan wwe

8

Đối với những người không muốn tạo hộp thoại tùy chỉnh nhưng vẫn thích cách WPF 100% và không muốn sử dụng DDL riêng, phụ thuộc bổ sung hoặc API lỗi thời, tôi đã nghĩ ra cách hack rất đơn giản bằng hộp thoại Lưu dưới dạng.

Không sử dụng chỉ thị cần thiết, bạn có thể chỉ cần sao chép-dán mã bên dưới!

Nó vẫn phải rất thân thiện với người dùng và hầu hết mọi người sẽ không bao giờ chú ý.

Ý tưởng xuất phát từ việc chúng ta có thể thay đổi tiêu đề của hộp thoại đó, ẩn các tệp và làm việc xung quanh tên tệp kết quả khá dễ dàng.

Đó là một hack lớn chắc chắn, nhưng có lẽ nó sẽ làm tốt công việc của bạn ...

Trong ví dụ này tôi có một đối tượng hộp văn bản để chứa đường dẫn kết quả, nhưng bạn có thể xóa các dòng liên quan và sử dụng giá trị trả về nếu bạn muốn ...

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

Các vấn đề duy nhất với hack này là:

  • Nút xác nhận vẫn nói "Lưu" thay vì một cái gì đó như "Chọn thư mục", nhưng trong trường hợp như mỏ tôi "Lưu" lựa chọn thư mục để nó vẫn hoạt động ...
  • Trường nhập vẫn nói "Tên tệp" thay vì "Tên thư mục", nhưng chúng ta có thể nói rằng thư mục là một loại tệp ...
  • Vẫn còn một danh sách thả xuống "Lưu dưới dạng", nhưng giá trị của nó ghi là "Thư mục (* .this.directory)" và người dùng không thể thay đổi nó thành một thứ khác, hoạt động với tôi ...

Hầu hết mọi người sẽ không chú ý đến những điều này, mặc dù tôi chắc chắn sẽ thích sử dụng một cách chính thức của WPF nếu microsoft sẽ thoát khỏi cái mông của họ, nhưng cho đến khi họ làm vậy, đó là cách khắc phục tạm thời của tôi.


1
Điều này thật tuyệt. Ngạc nhiên là không có ai khác đã thử điều này. Gói NuGet dĩ nhiên tốt hơn nhiều nhưng không có NuGet WindowsAPICodePack, đây là một cách tuyệt vời để HACK khả năng chọn thư mục mà không cần thêm bất kỳ gói / tham chiếu mới nào.

7

Đối với Hộp thoại Thư mục để nhận Đường dẫn Thư mục, Đầu tiên Thêm Tham chiếu System.Windows.Forms, rồi Giải quyết, sau đó đặt mã này vào một nút bấm.

    var dialog = new FolderBrowserDialog();
    dialog.ShowDialog();
    folderpathTB.Text = dialog.SelectedPath;

(librarypathTB là tên của TextBox nơi tôi đặt đường dẫn thư mục, HOẶC bạn có thể gán nó cho một biến chuỗi quá)

    string folder = dialog.SelectedPath;

Và nếu bạn muốn lấy Tên tệp / đường dẫn, chỉ cần thực hiện việc này trên Nút Bấm

    FileDialog fileDialog = new OpenFileDialog();
    fileDialog.ShowDialog();
    folderpathTB.Text = fileDialog.FileName;

(librarypathTB là tên của TextBox nơi tôi đặt đường dẫn tệp, HOẶC bạn cũng có thể gán nó cho một biến chuỗi)

Lưu ý: Đối với Hộp thoại Thư mục, System.Windows.Forms.dll phải được thêm vào dự án, nếu không nó sẽ không hoạt động.


Cảm ơn câu trả lời của bạn nhưng cách tiếp cận này đã được giải thích bởi @Heinzi ở trên.
Alexandra

5

Tôi tìm thấy đoạn mã dưới đây trong liên kết bên dưới ... và nó hoạt động Chọn hộp thoại thư mục WPF

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
}

4

Cách tốt nhất để đạt được những gì bạn muốn là tạo điều khiển dựa trên wpf của riêng bạn, hoặc sử dụng một điều khiển được tạo bởi người khác
tại sao? bởi vì sẽ có tác động hiệu suất đáng chú ý khi sử dụng hộp thoại winforms trong ứng dụng wpf (vì một số lý do)
tôi đề xuất dự án này
https://opendialog.codeplex.com/
hoặc Nuget:

PM> Install-Package OpenDialog

nó rất thân thiện với MVVM và nó không bao bọc hộp thoại winforms


3

Tôi muốn đề nghị, để thêm vào gói nugget:

  Install-Package OpenDialog

Sau đó, cách để sử dụng nó là:

    Gat.Controls.OpenDialogView openDialog = new Gat.Controls.OpenDialogView();
    Gat.Controls.OpenDialogViewModel vm = (Gat.Controls.OpenDialogViewModel)openDialog.DataContext;
    vm.IsDirectoryChooser = true;
    vm.Show();

    WPFLabel.Text = vm.SelectedFilePath.ToString();

Đây là tài liệu: http://opendialog.codeplex.com/documentation

Hoạt động cho Tệp, tệp có bộ lọc, thư mục, v.v.


2

Ookii VistaFolderBrowserDialoglà người bạn muốn.

Nếu bạn chỉ muốn Trình duyệt thư mục từ Hộp thoại Ooki và không có gì khác thì hãy tải xuống Nguồn , chọn các tệp bạn cần cho trình duyệt Thư mục (gợi ý: 7 tệp) và nó xây dựng tốt trong .NET 4.5.2. Tôi đã phải thêm một tài liệu tham khảo System.Drawing. So sánh các tài liệu tham khảo trong dự án ban đầu với bạn.

Làm thế nào để bạn tìm ra những tập tin? Mở ứng dụng của bạn và Ookii trong các trường hợp Visual Studio khác nhau. Thêm VistaFolderBrowserDialog.csvào ứng dụng của bạn và tiếp tục thêm tệp cho đến khi lỗi xây dựng biến mất. Bạn tìm thấy các phụ thuộc trong dự án Ookii - Control-Click vào cái bạn muốn theo dõi lại nguồn của nó (ý định chơi chữ).

Dưới đây là các tệp bạn cần nếu bạn quá lười biếng để làm điều đó ...

NativeMethods.cs
SafeHandles.cs
VistaFolderBrowserDialog.cs
\ Interop
   COMGuids.cs
   ErrorHelper.cs
   ShellComInterfaces.cs
   ShellWrapperDefinitions.cs

Chỉnh sửa dòng 197 VistaFolderBrowserDialog.cstrừ khi bạn muốn bao gồm dòng của họResources.Resx

ném mới UnlimitedOperationException (Properties.Resource.FolderBrowserDialogNoRootFolder);

throw new InvalidOperationException("Unable to retrieve the root folder.");

Thêm thông báo bản quyền của họ vào ứng dụng của bạn theo họ license.txt

Mã trong \Ookii.Dialogs.Wpf.Sample\MainWindow.xaml.csdòng 160-169 là một ví dụ bạn có thể sử dụng nhưng bạn sẽ cần xóa this,khỏi MessageBox.Show(this,cho WPF.

Hoạt động trên máy của tôi [TM]


2

Tôi biết đây là một câu hỏi cũ, nhưng một cách đơn giản để làm điều này là sử dụng tùy chọn FileDialog do WPF cung cấp và sử dụng System.IO.Path.GetDirectory (tên tệp).


Nhưng sau đó, người dùng phải chọn một tập tin mặc dù anh ta được yêu cầu chọn một thư mục. Một người dùng thiếu kinh nghiệm có thể gọi HelpDesk vào thời điểm này, hỏi tại sao anh ta phải chọn một tệp khi anh ta phải chọn một thư mục
chriszo111 18/07/18

0

Không có câu trả lời nào trong số này làm việc cho tôi (nói chung là có một tài liệu tham khảo bị thiếu hoặc một cái gì đó dọc theo những dòng đó)

Nhưng điều này khá đơn giản đã làm:

Sử dụng FolderBrowserDialog trong ứng dụng WPF

Thêm một tham chiếu đến System.Windows.Formsvà sử dụng mã này:

  var dialog = new System.Windows.Forms.FolderBrowserDialog();
  System.Windows.Forms.DialogResult result = dialog.ShowDialog();

Không cần phải theo dõi các gói bị thiếu. Hoặc thêm các lớp lớn

Điều này mang lại cho tôi một bộ chọn thư mục hiện đại cũng cho phép bạn tạo một thư mục mới

Tôi vẫn chưa thấy tác động khi triển khai cho các máy khác


0

Bạn có thể sử dụng smth như thế này trong WPF. Tôi đã tạo phương thức ví dụ. Kiểm tra bên dưới.

public string getFolderPath()
{
           // Create OpenFileDialog 
           Microsoft.Win32.OpenFileDialog dlg = new Microsoft.Win32.OpenFileDialog();

           OpenFileDialog openFileDialog = new OpenFileDialog();
           openFileDialog.Multiselect = false;

           openFileDialog.InitialDirectory = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
           if (openFileDialog.ShowDialog() == true)
           {
               System.IO.FileInfo fInfo = new System.IO.FileInfo(openFileDialog.FileName);
               return fInfo.DirectoryName;
           }
           return null;           
       }

1
Điều này yêu cầu người dùng chọn một tệp từ thư mục. Nếu thư mục trống thì bạn không thể chọn thư mục của mình.
Alexandru Dicu

Vâng, tôi hiểu rằng, đây là một cách giải quyết khác, không phải là giải pháp hoàn hảo cho vấn đề này.
koberone
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.