Caixa de diálogo Abrir diretório

274

Quero que o usuário selecione um diretório em que um arquivo que gerarei será salvo. Eu sei que no WPF eu deveria usar o OpenFileDialogdo Win32, mas infelizmente a caixa de diálogo requer que o arquivo seja selecionado - ele permanece aberto se eu simplesmente clicar em OK sem escolher um. Eu poderia "hackear" a funcionalidade, permitindo que o usuário escolhesse um arquivo e depois desviasse o caminho para descobrir a qual diretório ele pertence, mas isso é pouco intuitivo. Alguém já viu isso antes?

Alexandra
fonte
Possível duplicado de Selecionar pasta de diálogo WPF
desenvolvedor

Respostas:

406

Você pode usar a classe FolderBrowserDialog interna para isso. Não importa que esteja no System.Windows.Formsespaço para nome.

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

Se você deseja que a janela seja modal sobre alguma janela WPF, consulte a pergunta Como usar um FolderBrowserDialog em um aplicativo WPF .


EDIT: Se você deseja algo um pouco mais sofisticado do que o simples e feio FolderBrowserDialog do Windows Forms, existem algumas alternativas que permitem usar a caixa de diálogo do Vista:

  • Bibliotecas de terceiros, como caixas de diálogo Ookii (.NET 3.5)
  • O pacote de códigos da API do Windows :

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

    Observe que esse diálogo não está disponível em sistemas operacionais mais antigos que o Windows Vista, portanto, verifique CommonFileDialog.IsPlatformSupportedprimeiro.

Heinzi
fonte
78
Observe que este é um diálogo terrível. Você não pode copiar e colar um caminho nele, e ele não suporta pastas favoritas. No geral, eu daria um 0 de 5 e recomendaria que ninguém nunca o usasse. Exceto que não havia alternativa razoável até o Windows Vista ter uma caixa de diálogo muito melhor . Existem boas bibliotecas gratuitas que mostram a boa caixa de diálogo no Vista + e a ruim no XP.
Roman Starkov
70
Ainda assim, por que o WPF oferece um ótimo OpenFileDialog, mas não o OpenFolderDialog? Isso não é um pouco estranho? Por que o WPF está faltando aqui? Existem planos para adicionar uma classe para esta caixa de diálogo no WPF?
Paul-Sebastian Manole
14
Não esqueça que o FolderBrowserDialog é descartável.
21714 LosManos
9
Observe que, para usar a CommonOpenFileDialogpartir de WindowsAPICodePackvocê precisa Install-Package WindowsAPICodePack-Shell. O link fornecido na resposta não lista isso.
Nikola Novak
5
"O tipo ou espaço de nome CommonOpenFileDialog não pôde ser encontrado". É 2017 e eu não posso escolher uma pasta
Nick.McDermaid
46

Eu criei um UserControl que é usado assim:

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

A fonte xaml fica assim:

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

e o code-behind

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
fonte
1
+1, bom exemplo de como escrever um UserControl. Uma pergunta: por que você precisa be.UpdateSource? As notificações de alteração não devem ser automáticas nas propriedades da dependência?
Heinzi 17/12/2009
4
Você pode especificar na ligação quando acionar as atualizações. Por padrão, está no LostFocus, mas você também pode acionar atualizações no PropertyChanged.
Alexandra
3
A ligação também será atualizada para cada pressionamento de tecla. Se o usuário fizer algum tipo de validação na atualização (por exemplo, Directory.Exist), isso poderá causar problemas.
adrianm
10

O diálogo da pasta Ookii pode ser encontrado em Nuget.

PM> Install-Package Ookii.Dialogs

E, o código de exemplo é como abaixo.

var dialog = new Ookii.Dialogs.Wpf.VistaFolderBrowserDialog();
if (dialog.ShowDialog(this).GetValueOrDefault())
{
    textBoxFolderPath.Text = dialog.SelectedPath;
}
Youngjae
fonte
tnx seu caminho foi mais curto
wwe ehsan
8

Para aqueles que não desejam criar uma caixa de diálogo personalizada, mas ainda preferem uma maneira 100% WPF e não desejam usar DDLs separadas, dependências adicionais ou APIs desatualizadas, criei um hack muito simples usando a caixa de diálogo Salvar como.

Nenhuma diretiva de uso é necessária, você pode simplesmente copiar e colar o código abaixo!

Ainda deve ser muito fácil de usar e a maioria das pessoas nunca notará.

A idéia vem do fato de podermos alterar o título dessa caixa de diálogo, ocultar arquivos e contornar o nome do arquivo resultante com bastante facilidade.

É um grande truque, com certeza, mas talvez faça o trabalho muito bem para o seu uso ...

Neste exemplo, tenho um objeto de caixa de texto para conter o caminho resultante, mas você pode remover as linhas relacionadas e usar um valor de retorno, se desejar ...

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

Os únicos problemas com esse hack são:

  • O botão Reconhecer ainda diz "Salvar" em vez de algo como "Selecionar diretório", mas em um caso como minas eu "Salvar" a seleção de diretório para que ainda funcione ...
  • O campo de entrada ainda diz "Nome do arquivo" em vez de "Nome do diretório", mas podemos dizer que um diretório é um tipo de arquivo ...
  • Ainda existe um menu suspenso "Salvar como tipo", mas seu valor diz "Diretório (* .este.diretório)", e o usuário não pode alterá-lo para outra coisa, funciona para mim ...

A maioria das pessoas não notará isso, embora eu definitivamente prefira usar uma maneira oficial do WPF se a Microsoft tirar a cabeça da cabeça, mas até isso acontecer, essa é a minha solução temporária.

Olivier St-L
fonte
1
Isso foi legal. Surpreso que ninguém mais parece ter tentado isso. É claro que o pacote NuGet é muito melhor, mas sem o NuGet WindowsAPICodePack, essa é uma excelente maneira de HACK a capacidade de selecionar uma pasta sem adicionar novos pacotes / referências.
Code Novice
7

Para a caixa de diálogo Diretório obter o caminho do diretório, primeiro adicione a referência System.Windows.Forms e, em seguida, resolva e, em seguida, coloque esse código em um botão.

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

(folderpathTB é o nome do TextBox onde eu coloco o caminho da pasta, OU você também pode atribuí-lo a uma variável de string)

    string folder = dialog.SelectedPath;

E se você deseja obter o FileName / path, basta fazer isso no botão Clique

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

(folderpathTB é o nome do TextBox onde eu coloco o caminho do arquivo, OU você também pode atribuí-lo a uma variável de string)

Nota: Para o Diálogo de Pastas, o System.Windows.Forms.dll deve ser adicionado ao projeto, caso contrário, não funcionaria.

Zia Ur Rahman
fonte
Obrigado pela sua resposta, mas essa abordagem já foi explicada por @Heinzi acima.
Alexandra
5

Encontrei o código abaixo no link abaixo ... e funcionou Selecionar caixa de 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
fonte
4

A melhor maneira de conseguir o que você deseja é criar seu próprio controle baseado em wpf ou usar um que foi criado por outras pessoas
por que? porque haverá um impacto perceptível no desempenho ao usar a caixa de diálogo winforms em um aplicativo wpf (por algum motivo),
recomendo este projeto
https://opendialog.codeplex.com/
ou Nuget:

PM> Install-Package OpenDialog

é muito amigável ao MVVM e não está envolvendo a caixa de diálogo winforms

bigworld12
fonte
3

Eu sugiro, para adicionar no pacote nugget:

  Install-Package OpenDialog

Então a maneira de usá-lo é:

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

Aqui está a documentação: http://opendialog.codeplex.com/documentation

Funciona para arquivos, arquivos com filtro, pastas, etc.

Jose Ortega
fonte
2

The Ookii VistaFolderBrowserDialog é o que você deseja.

Se você deseja apenas o Navegador de Pastas da Ooki Dialogs e nada mais, faça o download do Código - fonte , escolha os arquivos necessários para o Navegador de Pastas (dica: 7 arquivos) e ele será compilado no .NET 4.5.2. Eu tive que adicionar uma referência aSystem.Drawing . Compare as referências no projeto original com as suas.

Como você descobre quais arquivos? Abra seu aplicativo e o Ookii em diferentes instâncias do Visual Studio. AdicionarVistaFolderBrowserDialog.cs ao seu aplicativo e continue adicionando arquivos até os erros de compilação desaparecerem. Você encontra as dependências no projeto Ookii - clique com a tecla Control pressionada na que deseja seguir de volta à sua fonte (trocadilho intencional).

Aqui estão os arquivos que você precisa se tiver preguiça de fazer isso ...

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

Edite a linha 197, a VistaFolderBrowserDialog.csmenos que você queira incluirResources.Resx

lança nova InvalidOperationException (Properties.Resources.FolderBrowserDialogNoRootFolder);

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

Adicione o aviso de direitos autorais ao aplicativo, conforme license.txt

O código na \Ookii.Dialogs.Wpf.Sample\MainWindow.xaml.cslinha 160-169 é um exemplo que você pode usar, mas será necessário remover this,do MessageBox.Show(this,WPF.

Trabalhos em minha máquina [TM]

Cara de CAD
fonte
2

Sei que essa é uma pergunta antiga, mas uma maneira simples de fazer isso é usar a opção FileDialog fornecida pelo WPF e usar System.IO.Path.GetDirectory (nome do arquivo).

Gregory Eaton
fonte
Mas então o usuário deve escolher um arquivo, mesmo que seja instruído a escolher uma pasta. Um usuário inexperiente pode chamar de HelpDesk, neste ponto, perguntando por que ele tem de escolher um arquivo quando ele tem que escolher uma pasta
chriszo111
0

Nenhuma dessas respostas funcionou para mim (geralmente faltava uma referência ou algo nesse sentido)

Mas isso simplesmente fez:

Usando FolderBrowserDialog no aplicativo WPF

Adicione uma referência System.Windows.Formse use este código:

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

Não há necessidade de rastrear pacotes ausentes. Ou adicione classes enormes

Isso me dá um seletor de pastas moderno que também permite criar uma nova pasta

Ainda estou para ver o impacto quando implantado em outras máquinas

Nick.McDermaid
fonte
0

Você poderia usar smth como este no WPF. Eu criei o método de exemplo. Confira abaixo.

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
fonte
1
Isso requer que o usuário selecione um arquivo da pasta. Se a pasta estiver vazia, não será possível selecionar sua pasta.
Alexandru Dicu 30/10
Sim, entendo que essa é uma solução alternativa, não a solução perfeita para esse problema.
Koberone 01/12/19