Ordnerdialog WPF auswählen

70

Ich entwickle eine WPF4-Anwendung und muss in meiner App den Benutzer einen Ordner auswählen lassen, in dem die Anwendung etwas speichern wird (Dateien, generierte Berichte usw.).

Meine Anforderungen:

  • Möglichkeit, den Standardordnerbaum anzuzeigen

  • Möglichkeit, einen Ordner auszuwählen

  • WPF Look & Feel, dieser Dialog muss Teil einer modernen Anwendung sein, die für Windows Vista / 7 entwickelt wurde und nicht für Windows 2000 oder sogar Win9x.

Soweit ich weiß, wird es bis 2010 (.Net 4.0) keinen Standardordner-Dialog geben, aber vielleicht gibt es einige Änderungen in Version 4.0?

Oder müssen Sie nur den WinForms-Dialog der alten Schule verwenden? Wenn es der einzige Weg ist, das zu tun, was ich brauche, wie kann ich es dem Vista / 7-Stil näher bringen und nicht Win9x?

In einigen Foren habe ich die Implementierung solcher Dialoge gesehen, aber mit alten hässlichen Symbolen à la Windows 95. Es sieht wirklich nicht gut aus.

Mike B.
quelle
2
Schauen Sie sich Sven Groots fantastische Ookii.Dialogs für WinForms und WPF an, die Ihnen moderne Ordner- und Dateidialoge im "Vista" -Stil bieten.
David Cuccia
Ich benutze wxPython Python-Modul github.com/wxWidgets/Phoenix
JinSnow
Hier ist ein Link zu einem aktualisierten Ookii-Dialogfeld für WPF, das auf .NET 4.5 abzielt und auf NuGet
C. Augusto Proiete

Antworten:

21

Ich habe vor langer Zeit in meinem Blog darüber geschrieben. Die Unterstützung von WPF für allgemeine Dateidialoge ist wirklich schlecht (oder zumindest in Version 3.5, die ich in Version 4 nicht eingecheckt habe) - aber es ist einfach, das zu umgehen.

Sie müssen Ihrer Anwendung das richtige Manifest hinzufügen. Dadurch erhalten Sie ein modernes Meldungsfeld und einen Ordnerbrowser (WinForms FolderBrowserDialog), jedoch keine Dialogfelder zum Öffnen / Speichern von WPF-Dateien. Dies wird in diesen drei Beiträgen beschrieben (wenn Sie sich nicht darum kümmern) über die Erklärung und möchte nur die Lösung direkt zum 3. gehen):

Glücklicherweise sind die Dialogfelder zum Öffnen / Speichern sehr dünne Wrapper um die Win32-API, die einfach mit den richtigen Flags aufgerufen werden können, um den Vista / 7-Stil zu erhalten (nach dem Festlegen des Manifests).

Nir
quelle
109

Das Windows Presentation Foundation 4.5-Kochbuch von Pavel Yosifovich auf Seite 155 im Abschnitt "Verwenden der allgemeinen Dialogfelder" lautet:

"Was ist mit der Ordnerauswahl (anstelle von Dateien)? Der WPF OpenFileDialog unterstützt dies nicht. Eine Lösung ist die Verwendung der FolderBrowseDialog-Klasse von Windows Forms. Eine weitere gute Lösung ist die Verwendung des in Kürze beschriebenen Windows API Code Pack."

Ich habe das API- Codepaket aus dem Windows® API-Codepaket für Microsoft® .NET Framework heruntergeladen. Windows-API- Codepaket : Wo ist es?und fügte dann meinem WPF 4.5-Projekt Verweise auf Microsoft.WindowsAPICodePack.dll und Microsoft.WindowsAPICodePack.Shell.dll hinzu.

Beispiel:

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
}
T Kräfte
quelle
130
Ich kann nicht glauben, dass Microsoft sich nicht die Mühe gemacht hat, standardmäßig einen FolderBrowserDialog in WPF aufzunehmen ...
Snooze
24
Die Windows API Code Packs sind hier und hier über Nuget verfügbar . Das hat bei mir gut funktioniert.
Wallace Kelly
3
Es funktioniert gut mit mir. Außerdem schreibe ich einfach die folgenden Befehle, damit der Beitrag alle Informationen enthält. Installation über die Package Manager-Konsole in VS "Install-Package Windows7APICodePack-Core", "Install-Package Windows7APICodePack-Shell"
Peter T.
Ich glaube, der Link zu dieser Datei ist ausgefallen.
alex
Ich habe es versucht, CommonOpenFileDialogwurde aber nicht erkannt. (Ja, ich habe das usingeingegeben).
Nick.McDermaid
15

Fügen Sie Ihrem Projekt die Windows API Code Pack-Shell hinzu

using Microsoft.WindowsAPICodePack.Dialogs;

...

var dialog = new CommonOpenFileDialog();
dialog.IsFolderPicker = true;
CommonFileDialogResult result = dialog.ShowDialog();
Zeynab Farzaneh
quelle
11

Wenn Sie weder Windows Forms verwenden noch Manifestdateien bearbeiten möchten, habe ich mir einen sehr einfachen Hack über das SaveAs-Dialogfeld von WPF ausgedacht, um tatsächlich ein Verzeichnis auszuwählen.

Keine Verwendung der Direktive erforderlich, Sie können einfach den folgenden Code kopieren und einfügen!

Es sollte immer noch sehr benutzerfreundlich sein und die meisten Leute werden es nie bemerken.

Die Idee kommt von der Tatsache, dass wir den Titel dieses Dialogfelds ändern, Dateien ausblenden und den resultierenden Dateinamen ganz einfach umgehen können.

Es ist sicher ein großer Hack, aber vielleicht macht es den Job gut für Ihren Gebrauch ...

In diesem Beispiel habe ich ein Textfeldobjekt, das den resultierenden Pfad enthält, aber Sie können die zugehörigen Zeilen entfernen und einen Rückgabewert verwenden, wenn Sie möchten ...

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

Die einzigen Probleme mit diesem Hack sind:

  • Die Schaltfläche "Bestätigen" sagt immer noch "Speichern" anstelle von "Verzeichnis auswählen", aber in einem Fall wie Minen "speichere" ich die Verzeichnisauswahl, damit es immer noch funktioniert ...
  • Das Eingabefeld sagt immer noch "Dateiname" anstelle von "Verzeichnisname", aber wir können sagen, dass ein Verzeichnis ein Dateityp ist ...
  • Es gibt immer noch eine Dropdown-Liste "Dateityp", deren Wert jedoch "Verzeichnis (* .this.directory)" lautet, und der Benutzer kann sie nicht für etwas anderes ändern, funktioniert für mich ...

Die meisten Leute werden dies nicht bemerken, obwohl ich es definitiv vorziehen würde, einen offiziellen WPF-Weg zu verwenden, wenn Microsoft ihre Köpfe aus ihren Ärschen bekommen würde, aber bis sie es tun, ist das meine vorübergehende Lösung.

Olivier St-L
quelle
1
unterschätzte Antwort
Ahmed Mohammed
5

MVVM + WinForms FolderBrowserDialog als Verhalten

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

Verwendung

     <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

Oyun
quelle
4

Microsoft.Win32.OpenFileDialog ist der Standarddialog, den jede Anwendung unter Windows verwendet. Ihr Benutzer wird von seinem Erscheinungsbild nicht überrascht sein, wenn Sie WPF in .NET 4.0 verwenden

Der Dialog wurde in Vista geändert. WPF in .NET 3.0 und 3.5 verwendete weiterhin das alte Dialogfeld, das jedoch in .NET 4.0 behoben wurde. Ich kann nur vermuten, dass Sie diesen Thread gestartet haben, weil Sie diesen alten Dialog sehen. Was wahrscheinlich bedeutet, dass Sie tatsächlich ein Programm ausführen, das auf 3.5 abzielt. Ja, der Winforms-Wrapper hat das Upgrade erhalten und zeigt die Vista-Version an. In der System.Windows.Forms.OpenFileDialog-Klasse müssen Sie einen Verweis auf System.Windows.Forms hinzufügen.

Hans Passant
quelle
18
Ich denke, der Punkt ist, dass der OpenFileDialog nicht zum Auswählen eines Ordners verwendet werden kann.
Neutrino
3

Basierend auf Oyuns Antwort ist es besser, eine Abhängigkeitseigenschaft für den Ordnernamen zu verwenden. Dies ermöglicht (zum Beispiel) das Binden an Untereigenschaften, was im Original nicht funktioniert. In meiner angepassten Version wird im Dialogfeld der ursprüngliche Ordner ausgewählt.

Verwendung 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);
        }
    }
}
Edward
quelle
Kombiniere dies mit der Antwort von @TPowers und das funktioniert großartig. Auch für den "i" xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
-Namensraum
3

Die Ookii-Dialoge für WPF verfügen über eine VistaFolderBrowserDialogKlasse, die eine vollständige Implementierung eines Ordner-Browser-Dialogs für WPF bietet.

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

Dialogfeld "Ookii-Ordnerbrowser"

Es gibt auch eine Version, die mit Windows Forms funktioniert .

C. Augusto Proiete
quelle
Dies ist die einzige Antwort, die keine vollständige Kluge ist. Der Grund, warum Sie WPF schreiben, besteht darin, sich von Windows Forms zu entfernen. Wenn Sie ein Windows Forms-Dialogfeld verwenden, werden alle Arten von Dingen eingezogen, die Sie nicht möchten.
AQuirky
2

Die FolderBrowserDialogKlasse vonSystem.Windows.Forms ist die empfohlene Methode zum Anzeigen eines Dialogfelds, in dem ein Benutzer einen Ordner auswählen kann.

Bis vor kurzem stimmte das Erscheinungsbild und Verhalten dieses Dialogfelds nicht mit den anderen Dateisystemdialogen überein. Dies ist einer der Gründe, warum Benutzer es nur ungern verwendeten.

Die gute Nachricht ist, dass FolderBrowserDialog es in NET Core 3.0 "modernisiert" wurde und daher eine praktikable Option für diejenigen ist, die entweder Windows Forms oder WPF-Apps schreiben, die auf diese Version oder höher abzielen.

In .NET - Core 3.0, Windows Forms-Benutzer [sic] eine neuere COM-basierte Steuerung , die in Windows Vista eingeführt wurde: FolderBrowserDialog in NET Core 3.0

Um in einer NET Core WPF-App zu referenzierenSystem.Windows.Forms , muss die Projektdatei bearbeitet und die folgende Zeile hinzugefügt werden:

<UseWindowsForms>true</UseWindowsForms>

Dies kann direkt nach dem vorhandenen platziert werden <UseWPF> Element platziert werden.

Dann müssen Sie nur noch den Dialog verwenden:

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)
{
    ...
}

FolderBrowserDialoghat eine RootFolderEigenschaft, die angeblich "den Stammordner festlegt, von dem aus das Browsen beginnt", aber was auch immer ich darauf gesetzt habe, hat keinen Unterschied gemacht; SelectedPathschien die bessere Eigenschaft für diesen Zweck zu sein, jedoch ist der nachfolgende Backslash erforderlich.

Außerdem ShowNewFolderButtonscheint die Eigenschaft ebenfalls ignoriert zu werden. Die Schaltfläche wird unabhängig davon immer angezeigt.

Steven Rands
quelle
1

Nur ein solcher Dialog ist FileDialog . Es ist Teil von WinForms, aber es ist eigentlich nur ein Wrapper um den WinAPI-Standard-OS-Dateidialog. Und ich denke nicht, dass es hässlich ist, es ist tatsächlich Teil des Betriebssystems, also sieht es so aus, als würde es ausgeführt.

Auf andere Weise gibt es nichts, bei dem Sie helfen können. Sie müssen entweder nach einer Implementierung durch einen Drittanbieter suchen, entweder kostenlos (und ich glaube nicht, dass es etwas Gutes gibt) oder bezahlt.

Euphorisch
quelle
Vielen Dank! Dies ist bei weitem die beste Antwort. Sehr einfach zu codieren für MVVM oder Standardfenster.
Mark Bonafe
4
Dies erlaubt Ihnen jedoch nicht, einen Ordner auszuwählen ... der springende Punkt der Frage
springende
0

Nur um eins zu sagen, WindowsAPICodePackkann CommonOpenFileDialogunter Windows 7 6.1.7600 nicht geöffnet werden .

pivnothebearrrrrrrrr
quelle