Abrir diálogo de directorio

274

Quiero que el usuario seleccione un directorio donde se guardará un archivo que luego generaré. Sé que en WPF debería usar OpenFileDialogWin32, pero desafortunadamente el cuadro de diálogo requiere que se seleccionen los archivos: permanece abierto si simplemente hago clic en Aceptar sin elegir uno. Podría "hackear" la funcionalidad permitiendo que el usuario elija un archivo y luego elimine la ruta para descubrir a qué directorio pertenece, pero eso no es intuitivo en el mejor de los casos. ¿Alguien ha visto esto hecho antes?

Alexandra
fuente

Respuestas:

406

Puede usar la clase FolderBrowserDialog incorporada para esto. No importa que esté en el System.Windows.Formsespacio de nombres.

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

Si desea que la ventana sea modal sobre alguna ventana de WPF, consulte la pregunta Cómo usar un FolderBrowserDialog desde una aplicación WPF .


EDITAR: Si desea algo un poco más elegante que el simple y feo Windows Forms FolderBrowserDialog, hay algunas alternativas que le permiten usar el diálogo de Vista en su lugar:

  • Bibliotecas de terceros, como los cuadros de diálogo de Ookii (.NET 3.5)
  • El API Code Pack-Shell de Windows :

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

    Tenga en cuenta que este cuadro de diálogo no está disponible en sistemas operativos anteriores a Windows Vista, así que asegúrese de verificarlo CommonFileDialog.IsPlatformSupportedprimero.

Heinzi
fuente
78
Tenga en cuenta que este es un diálogo horrible. No puede copiar y pegar una ruta en él, y no admite carpetas favoritas. En general, le daría un 0 sobre 5 y recomendaría que nadie lo use. Excepto que no había una alternativa razonable hasta que Windows Vista salió con el diálogo de carpeta mucho mejor . Hay buenas bibliotecas gratuitas que muestran el diálogo bueno en Vista +, y el malo en XP.
Roman Starkov
70
Aún así, ¿por qué WPF ofrece un excelente OpenFileDialog pero no OpenFolderDialog? ¿No es eso un poco extraño? ¿Por qué falta WPF aquí? ¿Hay planes para agregar una clase para este diálogo en WPF?
Paul-Sebastian Manole
14
No olvide que FolderBrowserDialog es desechable.
LosManos
9
Tenga en cuenta que con el fin de uso CommonOpenFileDialogde WindowsAPICodePacklo que necesita Install-Package WindowsAPICodePack-Shell. El enlace proporcionado en la respuesta no enumera eso.
Nikola Novak
55
"No se pudo encontrar el tipo o espacio de nombres CommonOpenFileDialog". Es 2017 y no puedo elegir una carpeta
Nick.McDermaid
46

Creé un UserControl que se usa así:

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

La fuente de xaml se ve así:

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

y el código subyacente

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
fuente
1
+1, buen ejemplo sobre cómo escribir un UserControl. Una pregunta: ¿Por qué lo necesitas be.UpdateSource? ¿Las notificaciones de cambio no deberían ser automáticas en las propiedades de dependencia?
Heinzi
44
Puede especificar en el enlace cuándo activar las actualizaciones. De forma predeterminada, está en LostFocus, pero también puede indicarle que active actualizaciones en PropertyChanged.
Alexandra
3
El enlace también se actualizará para cada pulsación de tecla. Si el usuario realiza algún tipo de validación en la actualización (por ejemplo, Directory.Exist), puede causar problemas.
adrianm
10

El diálogo de la carpeta Ookii se puede encontrar en Nuget.

PM> Install-Package Ookii.Dialogs

Y, el código de ejemplo es el siguiente.

var dialog = new Ookii.Dialogs.Wpf.VistaFolderBrowserDialog();
if (dialog.ShowDialog(this).GetValueOrDefault())
{
    textBoxFolderPath.Text = dialog.SelectedPath;
}
Youngjae
fuente
tnx tu camino fue más corto
ehsan wwe
8

Para aquellos que no quieren crear un cuadro de diálogo personalizado, pero aún prefieren una forma 100% WPF y no quieren usar DDL separados, dependencias adicionales o API obsoletas, se me ocurrió un truco muy simple usando el cuadro de diálogo Guardar como.

No es necesario usar la directiva, ¡simplemente puede copiar y pegar el código a continuación!

Todavía debe ser muy fácil de usar y la mayoría de la gente nunca se dará cuenta.

La idea proviene del hecho de que podemos cambiar el título de ese diálogo, ocultar archivos y solucionar el nombre de archivo resultante con bastante facilidad.

Es un gran truco seguro, pero tal vez haga el trabajo bien para su uso ...

En este ejemplo, tengo un objeto de cuadro de texto para contener la ruta resultante, pero puede eliminar las líneas relacionadas y usar un valor de retorno si lo desea ...

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

Los únicos problemas con este truco son:

  • El botón Reconocer aún dice "Guardar" en lugar de algo así como "Seleccionar directorio", pero en un caso como minas, "Guardar" la selección de directorio para que siga funcionando ...
  • El campo de entrada todavía dice "Nombre de archivo" en lugar de "Nombre de directorio", pero podemos decir que un directorio es un tipo de archivo ...
  • Todavía hay un menú desplegable "Guardar como tipo", pero su valor dice "Directorio (* .this.directory)", y el usuario no puede cambiarlo por otra cosa, funciona para mí ...

La mayoría de las personas no se darán cuenta de esto, aunque definitivamente preferiría usar una forma oficial de WPF si microsoft sacara sus cabezas de sus traseros, pero hasta que lo hagan, esa es mi solución temporal.

Olivier St-L
fuente
1
Esto fue genial. Sorprendido de que nadie más parece haber intentado esto. El paquete NuGet es mucho mejor, por supuesto, pero sin el NuGet WindowsAPICodePack, esta es una excelente manera de HACKEAR la capacidad de seleccionar una carpeta sin agregar ningún paquete / referencia nuevo.
Code Novice
7

Para que Directory Dialog obtenga la ruta del directorio, primero agregue la referencia System.Windows.Forms, y luego Resolver, y luego coloque este código haciendo clic en un botón.

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

(folderpathTB es el nombre de TextBox donde quiero colocar la ruta de la carpeta, O también puede asignarla a una variable de cadena, es decir)

    string folder = dialog.SelectedPath;

Y si desea obtener FileName / path, simplemente haga esto al hacer clic en el botón

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

(folderpathTB es el nombre de TextBox donde quiero colocar la ruta del archivo, O también puede asignarlo a una variable de cadena)

Nota: Para el cuadro de diálogo Carpeta, System.Windows.Forms.dll debe agregarse al proyecto, de lo contrario no funcionaría.

Zia Ur Rahman
fuente
Gracias por su respuesta, pero este enfoque ya ha sido explicado por @Heinzi arriba.
Alexandra
5

Encontré el código a continuación en el siguiente enlace ... y funcionó Seleccionar carpeta diálogo 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
fuente
4

La mejor manera de lograr lo que desea es crear su propio control basado en wpf, o utilizar uno que haya sido creado por otras personas
¿ por qué? porque habrá un impacto notable en el rendimiento al usar el diálogo winforms en una aplicación wpf (por alguna razón)
recomiendo este proyecto
https://opendialog.codeplex.com/
o Nuget:

PM> Install-Package OpenDialog

es muy amigable con MVVM y no está envolviendo el diálogo de winforms

bigworld12
fuente
3

Sugeriría, para agregar en el paquete de pepitas:

  Install-Package OpenDialog

Entonces la forma de usarlo es:

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

Aquí está la documentación: http://opendialog.codeplex.com/documentation

Funciona para archivos, archivos con filtro, carpetas, etc.

Jose Ortega
fuente
2

El Ookii VistaFolderBrowserDialoges el que quieres.

Si solo desea el explorador de carpetas de Ooki Dialogs y nada más, descargue la fuente , seleccione los archivos que necesita para el explorador de carpetas (sugerencia: 7 archivos) y se compila bien en .NET 4.5.2. Tuve que agregar una referencia a System.Drawing. Compare las referencias en el proyecto original con las suyas.

¿Cómo averiguar qué archivos? Abra su aplicación y Ookii en diferentes instancias de Visual Studio. Agregue VistaFolderBrowserDialog.csa su aplicación y siga agregando archivos hasta que desaparezcan los errores de compilación. Encontrará las dependencias en el proyecto Ookii: presione Control y haga clic en el que desea seguir hasta su origen (juego de palabras).

Aquí están los archivos que necesita si es demasiado vago para hacer eso ...

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

Edite la línea 197 a VistaFolderBrowserDialog.csmenos que desee incluir suResources.Resx

lanzar una nueva InvalidOperationException (Properties.Resources.FolderBrowserDialogNoRootFolder);

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

Agregue su aviso de copyright a su aplicación según su license.txt

El código en \Ookii.Dialogs.Wpf.Sample\MainWindow.xaml.cslínea de 160 a 169 es un ejemplo que puede utilizar, pero que tendrá que eliminar this,a partir MessageBox.Show(this,de WPF.

Funciona en mi máquina [TM]

CAD bloke
fuente
2

Sé que esta es una pregunta antigua, pero una forma simple de hacerlo es usar la opción FileDialog proporcionada por WPF y usar System.IO.Path.GetDirectory (nombre de archivo).

Gregory Eaton
fuente
Pero el usuario debe elegir un archivo a pesar de que se le indique que elija una carpeta. Un usuario inexperto puede llamar a servicio de asistencia en este punto, preguntando por qué él tiene que elegir un archivo cuando tiene que elegir una carpeta
chriszo111
0

Ninguna de estas respuestas funcionó para mí (generalmente faltaba una referencia o algo por el estilo)

Pero esto simplemente hizo:

Usando FolderBrowserDialog en la aplicación WPF

Agregue una referencia System.Windows.Formsy use este código:

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

No es necesario localizar paquetes perdidos. O agrega clases enormes

Esto me da un selector de carpeta moderno que también te permite crear una nueva carpeta

Todavía tengo que ver el impacto cuando se implementa en otras máquinas

Nick.McDermaid
fuente
0

Podrías usar algo así en WPF. He creado un método de ejemplo. Verifique a continuación.

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
fuente
1
Esto requiere que el usuario seleccione un archivo de la carpeta. Si la carpeta está vacía, no puede seleccionar su carpeta.
Alexandru Dicu
Sí, entiendo que esta es una solución alternativa, no la solución perfecta para este problema.
koberone