Otwórz okno dialogowe katalogu

274

Chcę, aby użytkownik wybrał katalog, w którym zapisany zostanie plik, który wygeneruję. Wiem, że w WPF powinienem używać OpenFileDialogz Win32, ale niestety okno dialogowe wymaga wybrania pliku (plików) - pozostaje otwarty, jeśli po prostu kliknę OK bez wybierania jednego. Mógłbym „zhakować” funkcjonalność, pozwalając użytkownikowi wybrać plik, a następnie usunąć ścieżkę, aby dowiedzieć się, do którego katalogu należy, ale w najlepszym razie jest to nieintuicyjne. Czy ktoś widział to wcześniej?

Alexandra
źródło

Odpowiedzi:

406

W tym celu można użyć wbudowanej klasy FolderBrowserDialog . Nie przejmuj się, że jest w System.Windows.Formsprzestrzeni nazw.

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

Jeśli chcesz, aby okno było modalne w niektórych oknach WPF, zobacz pytanie Jak korzystać z FolderBrowserDialog z aplikacji WPF .


EDYCJA: Jeśli chcesz czegoś bardziej fantazyjnego niż zwykły, brzydki folder Windows Forms FolderBrowserDialog, istnieje kilka alternatyw, które pozwalają na korzystanie z okna dialogowego Vista:

  • Biblioteki innych firm, takie jak okna dialogowe Ookii (.NET 3.5)
  • Windows API Code Pack-Shell :

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

    Pamiętaj, że to okno dialogowe nie jest dostępne w systemach operacyjnych starszych niż Windows Vista, więc CommonFileDialog.IsPlatformSupportednajpierw sprawdź .

Heinzi
źródło
78
Pamiętaj, że jest to okropne okno dialogowe. Nie możesz skopiować i wkleić ścieżki do niej, a ona nie obsługuje ulubionych folderów. Ogólnie dałbym mu 0 na 5 i polecam, żeby nikt go nie używał. Tyle, że nie było żadnej rozsądnej alternatywy, dopóki system Windows Vista nie wyszedł ze znacznie lepszym oknem dialogowym folderów . Istnieją dobre bezpłatne biblioteki, które pokazują dobre okno dialogowe w systemie Vista +, a złe w systemie XP.
Roman Starkov,
70
Mimo to, dlaczego WPF oferuje świetny OpenFileDialog, ale nie ma OpenFolderDialog? Czy to nie jest trochę dziwne? Dlaczego brakuje tutaj WPF? Czy są jakieś plany dodania klasy dla tego okna dialogowego w WPF?
Paul-Sebastian Manole
14
Nie zapominaj, że FolderBrowserDialog jest jednorazowy.
LosManos,
9
Pamiętaj, że aby korzystać CommonOpenFileDialogz WindowsAPICodePackciebie, musisz Install-Package WindowsAPICodePack-Shell. Link podany w odpowiedzi nie zawiera tego.
Nikola Novak,
5
„Nie można znaleźć typu lub przestrzeni nazw CommonOpenFileDialog”. Jest rok 2017 i nie mogę wybrać folderu
Nick.McDermaid
46

Utworzyłem UserControl, który jest używany w następujący sposób:

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

Źródło xaml wygląda następująco:

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

i kod

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();
            }
        }
    }
 }
adrianm
źródło
1
+1, fajny przykład jak napisać UserControl. Jedno pytanie: dlaczego potrzebujesz be.UpdateSource? Czy zmiany powiadomień nie powinny być automatyczne we właściwościach zależności?
Heinzi,
4
W powiązaniu można określić, kiedy uruchomić aktualizacje. Domyślnie jest on na LostFocus, ale możesz mu również polecić uruchamianie aktualizacji na PropertyChanged.
Alexandra,
3
Powiązanie zostanie również zaktualizowane przy każdym naciśnięciu klawisza. Jeśli użytkownik dokonuje weryfikacji podczas aktualizacji (np. Directory.Exist), może to powodować problemy.
adrianm,
10

Okno dialogowe folderu Ookii można znaleźć w Nuget.

PM> Install-Package Ookii.Dialogs

I przykładowy kod jest jak poniżej.

var dialog = new Ookii.Dialogs.Wpf.VistaFolderBrowserDialog();
if (dialog.ShowDialog(this).GetValueOrDefault())
{
    textBoxFolderPath.Text = dialog.SelectedPath;
}
Youngjae
źródło
Na twoją drogę był najkrótszy
ehsan wwe
8

Dla tych, którzy nie chcą tworzyć niestandardowego okna dialogowego, ale nadal wolą 100% sposób WPF i nie chcą używać oddzielnych plików DDL, dodatkowych zależności lub przestarzałych interfejsów API, wpadłem na bardzo prosty hack przy użyciu okna dialogowego Zapisz jako.

Nie wymaga użycia dyrektywy, możesz po prostu skopiować i wkleić poniższy kod!

Powinien nadal być bardzo przyjazny dla użytkownika i większość ludzi nigdy tego nie zauważy.

Pomysł wynika z tego, że możemy łatwo zmienić tytuł tego okna dialogowego, ukryć pliki i obejść wynikową nazwę pliku.

Z pewnością jest to duży hack, ale może zrobi to dobrze dla twojego użytku ...

W tym przykładzie mam obiekt pola tekstowego, który zawiera ścieżkę wynikową, ale możesz usunąć powiązane linie i użyć wartości zwracanej, jeśli chcesz ...

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

Jedyne problemy z tym hackiem to:

  • Przycisk Potwierdź nadal mówi „Zapisz” zamiast czegoś takiego jak „Wybierz katalog”, ale w przypadku takich jak kopalnie „Zapisz” wybór katalogu, więc nadal działa ...
  • W polu wejściowym nadal widnieje „Nazwa pliku” zamiast „Nazwa katalogu”, ale możemy powiedzieć, że katalog jest rodzajem pliku ...
  • Nadal jest rozwijane menu „Zapisz jako typ”, ale jego wartość mówi „Katalog (* .this.directory)”, a użytkownik nie może go zmienić na coś innego, działa dla mnie ...

Większość ludzi tego nie zauważy, chociaż zdecydowanie wolałbym użyć oficjalnego sposobu WPF, jeśli Microsoft straci głowę z tyłków, ale dopóki tego nie zrobią, to moja tymczasowa poprawka.

Olivier St-L
źródło
1
To było fajne. Zaskoczony, że nikt inny nie próbował tego. Pakiet NuGet jest oczywiście znacznie lepszy, ale bez NuAPet WindowsAPICodePack jest to doskonały sposób na HAKOWANIE możliwości wyboru folderu bez dodawania nowych pakietów / referencji.
Kod Nowicjusz
7

Aby okno dialogowe katalogu otrzymało ścieżkę katalogu, najpierw dodaj odniesienie System.Windows.Forms, a następnie Resolve, a następnie umieść ten kod jednym kliknięciem przycisku.

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

(folderpathTB to nazwa TextBox, w której chcę wstawić ścieżkę folderu, LUB możesz też przypisać ją do zmiennej łańcuchowej, tj.)

    string folder = dialog.SelectedPath;

A jeśli chcesz uzyskać nazwę pliku / ścieżkę, po prostu zrób to za pomocą przycisku kliknięcia

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

(folderpathTB to nazwa TextBox, w której chcę wstawić ścieżkę pliku, LUB możesz też przypisać ją do zmiennej łańcuchowej)

Uwaga: W przypadku okna dialogowego folderu do projektu należy dodać plik System.Windows.Forms.dll, w przeciwnym razie nie zadziałałby.

Zia Ur Rahman
źródło
Dziękuję za odpowiedź, ale to podejście zostało już wyjaśnione przez @Heinzi powyżej.
Alexandra,
5

Znalazłem poniższy kod na poniższym linku ... i działało Wybierz okno dialogowe folderu 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
}
Saurabh Raoot
źródło
4

Najlepszym sposobem na osiągnięcie tego, co chcesz, jest utworzenie własnej kontroli opartej na WPF lub użycie takiej, która została stworzona przez innych ludzi,
dlaczego? ponieważ będzie zauważalny wpływ na wydajność podczas korzystania z okna dialogowego winforms w aplikacji wpf (z jakiegoś powodu)
polecam ten projekt
https://opendialog.codeplex.com/
lub Nuget:

PM> Install-Package OpenDialog

jest bardzo przyjazny dla MVVM i nie zawija okna dialogowego winforms

bigworld12
źródło
3

Sugeruję, aby dodać w pakiecie samorodek:

  Install-Package OpenDialog

Zatem sposobem na to jest:

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

Oto dokumentacja: http://opendialog.codeplex.com/documentation

Działa z plikami, plikami z filtrem, folderami itp

Jose Ortega
źródło
2

Ookii VistaFolderBrowserDialogjest tym, czego chcesz.

Jeśli chcesz tylko przeglądarkę folderów z Ooki Dialogs i nic więcej, pobierz Źródło , wybierz pliki potrzebne do przeglądarki folderów (wskazówka: 7 plików) i będzie dobrze działać w .NET 4.5.2. Musiałem dodać odniesienie do System.Drawing. Porównaj odniesienia w oryginalnym projekcie do swojego.

Jak ustalić, które pliki? Otwórz aplikację i Ookii w różnych instancjach Visual Studio. Dodaj VistaFolderBrowserDialog.csdo aplikacji i kontynuuj dodawanie plików, aż błędy kompilacji znikną. Zależności można znaleźć w projekcie Ookii - kliknij z wciśniętym klawiszem Control w tę, z której chcesz wrócić do źródła (zamierzona gra słów).

Oto pliki, których potrzebujesz, jeśli jesteś zbyt leniwy, aby to zrobić ...

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

Edytuj wiersz 197, VistaFolderBrowserDialog.cschyba że chcesz dołączyć ichResources.Resx

wyrzuć nowy InvalidOperationException (Properties.Resources.FolderBrowserDialogNoRootFolder);

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

Dodaj informację o prawach autorskich do swojej aplikacji zgodnie z ich license.txt

Kod w \Ookii.Dialogs.Wpf.Sample\MainWindow.xaml.cslinii 160-169 jest przykładem, którego możesz użyć, ale będziesz musiał go usunąć this,z MessageBox.Show(this,WPF.

Działa na mojej maszynie [TM]

CAD CAD
źródło
2

Wiem, że to stare pytanie, ale prostym sposobem na to jest skorzystanie z opcji FileDialog dostarczonej przez WPF i użycie System.IO.Path.GetDirectory (nazwa pliku).

Gregory Eaton
źródło
Ale wtedy użytkownik musi wybrać plik, nawet jeśli powie mu się wybrać folder. Niedoświadczony użytkownik może w tym momencie zadzwonić do HelpDesk, pytając, dlaczego musi wybrać plik, gdy musi wybrać folder
chriszo111
0

Żadna z tych odpowiedzi nie działała dla mnie (ogólnie brakowało referencji lub coś podobnego)

Ale to po prostu:

Korzystanie z FolderBrowserDialog w aplikacji WPF

Dodaj odwołanie do System.Windows.Formsi użyj tego kodu:

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

Nie musisz szukać brakujących pakietów. Lub dodaj ogromne klasy

Daje mi to nowoczesny selektor folderów, który umożliwia także utworzenie nowego folderu

Jeszcze nie widzę wpływu po wdrożeniu na innych komputerach

Nick.McDermaid
źródło
0

Możesz użyć czegoś takiego w WPF. Stworzyłem przykładową metodę. Sprawdź poniżej.

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;           
       }
koberone
źródło
1
Wymaga to od użytkownika wybrania pliku z folderu. Jeśli folder jest pusty, nie możesz wybrać swojego folderu.
Alexandru Dicu
Tak, rozumiem, że to jest jakieś obejście, a nie idealne rozwiązanie tego problemu.
koberone