Диалог открытия каталога
Я хочу, чтобы пользователь выбрал каталог, в который будет сохранен файл, который я сгенерирую. Я знаю, что в WPF я должен использовать OpenFileDialog
из Win32, но, к сожалению, диалог требует выбора файла (ов) - он остается открытым, если я просто нажму OK, не выбрав один. Я мог бы "взломать" функциональность, позволив пользователю выбрать файл, а затем лишить путь, чтобы выяснить, к какому каталогу он принадлежит, но в лучшем случае это не интуитивно понятно. Кто-нибудь видел это сделано раньше?
17 ответов
Для этого вы можете использовать встроенный класс FolderBrowserDialog. Не против того, что это в System.Windows.Forms
Пространство имен.
using (var dialog = new System.Windows.Forms.FolderBrowserDialog())
{
System.Windows.Forms.DialogResult result = dialog.ShowDialog();
}
Если вы хотите, чтобы окно было модальным поверх какого-либо окна WPF, см. Вопрос " Как использовать FolderBrowserDialog из приложения WPF".
РЕДАКТИРОВАТЬ: Если вы хотите что-то более причудливое, чем простая, безобразная Windows Forms FolderBrowserDialog, есть несколько альтернатив, которые позволяют вам вместо этого использовать диалог Vista:
- Сторонние библиотеки, такие как диалоги Ookii (.NET 3.5)
using Microsoft.WindowsAPICodePack.Dialogs; ... var dialog = new CommonOpenFileDialog(); dialog.IsFolderPicker = true; CommonFileDialogResult result = dialog.ShowDialog();
Обратите внимание, что это диалоговое окно недоступно в операционных системах старше Windows Vista, поэтому обязательно проверьте
CommonFileDialog.IsPlatformSupported
первый.
Я создал UserControl, который используется следующим образом:
<UtilitiesWPF:FolderEntry Text="{Binding Path=LogFolder}" Description="Folder for log files"/>
Исходный код xaml выглядит так:
<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>
и код позади
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();
}
}
}
}
As stated in earlier answers, is the class to use for this. Some people have (justifiable) concerns with the appearance and behaviour of this dialog. The good news is that it was "modernized" in NET Core 3.0, so is now a viable option for those writing either Windows Forms or WPF apps targeting that version or later (you're out of luck if still using NET Framework though).
In .NET Core 3.0, Windows Forms users [sic] a newer COM-based control that was introduced in Windows Vista:
To reference <tcode id="854928"></tcode> in a NET Core WPF app, it is necessary to edit the project file and add the following line:
<UseWindowsForms>true</UseWindowsForms>
This can be placed directly after the existing
<UseWPF>
element.
Then it's just a case of using the dialog:
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)
{
...
}
FolderBrowserDialog
has a
RootFolder
property that supposedly "sets the root folder where the browsing starts from" but whatever I set this to it didn't make any difference;
SelectedPath
seemed to be the better property to use for this purpose, however the trailing backslash is required.
Also, the
ShowNewFolderButton
property seems to be ignored as well, the button is always shown regardless.
Диалог папки Ookii можно найти на Nuget.
PM> Install-Package Ookii.Dialogs
И пример кода, как показано ниже.
var dialog = new Ookii.Dialogs.Wpf.VistaFolderBrowserDialog();
if (dialog.ShowDialog(this).GetValueOrDefault())
{
textBoxFolderPath.Text = dialog.SelectedPath;
}
Для тех, кто не хочет создавать пользовательское диалоговое окно, но все еще предпочитает 100% WPF-способ и не хочет использовать отдельные DDL, дополнительные зависимости или устаревшие API-интерфейсы, я предложил очень простой взлом с помощью диалогового окна "Сохранить как".
Нет необходимости в использовании директивы, вы можете просто скопировать код ниже!
Это все еще должно быть очень удобным, и большинство людей никогда не заметят.
Идея исходит из того факта, что мы можем легко изменить заголовок этого диалога, скрыть файлы и обойти полученное имя файла.
Это большой взлом, но, возможно, он отлично подойдет для вашего использования...
В этом примере у меня есть объект текстового поля, в котором содержится результирующий путь, но вы можете удалить связанные строки и использовать возвращаемое значение, если хотите...
// 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;
}
Единственные проблемы с этим хаком:
- Кнопка подтверждения по-прежнему говорит "Сохранить" вместо чего-то вроде "Выбрать каталог", но в случае, подобном минам, я "Сохранить" выбор каталога, чтобы он все еще работал...
- Поле ввода все еще говорит "Имя файла" вместо "Имя каталога", но мы можем сказать, что каталог - это тип файла...
- По-прежнему есть раскрывающийся список "Сохранить как тип", но его значение говорит "Каталог (*.this.directory)", и пользователь не может изменить его на что-то другое, у меня работает...
Большинство людей этого не заметят, хотя я бы определенно предпочел использовать официальный способ WPF, если бы Майкрософт вытащил свои головы из задниц, но пока они не заметят, это мое временное исправление.
Я использую диалоги Ookii некоторое время, и это хорошо работает для WPF.
Вот прямая страница:
Чтобы в диалоге каталогов получить путь к каталогу, сначала добавьте ссылку System.Windows.Forms, а затем Resolve, а затем вставьте этот код в нажатие кнопки.
var dialog = new FolderBrowserDialog();
dialog.ShowDialog();
folderpathTB.Text = dialog.SelectedPath;
(folderpathTB - это имя TextBox, в которое я хочу поместить путь к папке, ИЛИ вы также можете присвоить его строковой переменной, т.е.)
string folder = dialog.SelectedPath;
И если вы хотите получить FileName/path, просто сделайте это при нажатии кнопки
FileDialog fileDialog = new OpenFileDialog();
fileDialog.ShowDialog();
folderpathTB.Text = fileDialog.FileName;
(folderpathTB - это имя TextBox, куда я хочу поместить путь к файлу, ИЛИ вы также можете присвоить его строковой переменной)
Примечание. Для диалога папок в проект необходимо добавить файл System.Windows.Forms.dll, иначе он не будет работать.
Я нашел код ниже по ссылке ниже... и это сработало. Выберите папку в диалоге 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
}
Я бы предложил добавить в пакет самородков:
Install-Package OpenDialog
Тогда способ использовать это:
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();
Вот документация: http://opendialog.codeplex.com/documentation
Работает для файлов, файлов с фильтром, папок и т. Д.
Лучший способ достичь желаемого - создать собственный элемент управления на основе wpf или использовать тот, который был создан другими людьми.
Зачем? поскольку при использовании диалогового окна winforms в приложении wpf (по какой-то причине) будет заметно снижаться производительность
я рекомендую этот проект
https://opendialog.codeplex.com/
или Nuget:
PM> Install-Package OpenDialog
это очень дружественный к MVVM и не переносит диалог winforms
The Ookii VistaFolderBrowserDialog
это тот, который вы хотите.
Если вам нужен только браузер папок из Ooki Dialogs и ничего больше, тогда скачайте Source, выберите нужные вам файлы для браузера Folder (подсказка: 7 файлов), и он прекрасно встраивается в.NET 4.5.2. Я должен был добавить ссылку на System.Drawing
, Сравните ссылки в оригинальном проекте с вашими.
Как вы выясните, какие файлы? Откройте свое приложение и Ookii в разных экземплярах Visual Studio. добавлять VistaFolderBrowserDialog.cs
в ваше приложение и продолжайте добавлять файлы, пока ошибки сборки не исчезнут. Вы найдете зависимости в проекте Ookii - Control-Click тот, который вы хотите следовать обратно к его источнику (каламбур).
Вот файлы, которые вам нужны, если вам лень это делать...
NativeMethods.cs
SafeHandles.cs
VistaFolderBrowserDialog.cs
\ Interop
COMGuids.cs
ErrorHelper.cs
ShellComInterfaces.cs
ShellWrapperDefinitions.cs
Редактировать строку 197 в VistaFolderBrowserDialog.cs
если вы не хотите включить их Resources.Resx
бросить новое InvalidOperationException(Properties.Resources.FolderBrowserDialogNoRootFolder);
throw new InvalidOperationException("Unable to retrieve the root folder.");
Добавьте их уведомление об авторских правах в ваше приложение согласно их license.txt
Код в \Ookii.Dialogs.Wpf.Sample\MainWindow.xaml.cs
строка 160-169 является примером, который вы можете использовать, но вам нужно будет удалить this,
от MessageBox.Show(this,
для WPF.
Работает на моей машине [ТМ]
Ни один из этих ответов не работал для меня (как правило, отсутствовала ссылка или что-то в этом роде)
Но это довольно просто:
Использование FolderBrowserDialog в приложении WPF
Добавить ссылку на System.Windows.Forms
и используйте этот код:
var dialog = new System.Windows.Forms.FolderBrowserDialog();
System.Windows.Forms.DialogResult result = dialog.ShowDialog();
Нет необходимости отслеживать пропущенные пакеты. Или добавить огромные классы
Это дает мне современный селектор папок, который также позволяет создавать новые папки
Мне еще предстоит увидеть влияние при развертывании на других машинах
Я знаю, что это старый вопрос, но простой способ сделать это - использовать опцию FileDialog, предоставленную WPF, и использовать System.IO.Path.GetDirectory(имя файла).
Кажется, что библиотека Microsoft.Win32 .NET не поддерживает выбор папок (только файлы), поэтому вам не повезло с WPF (по состоянию на 7/2022). Я считаю, что сейчас лучший вариант — это Ookii для WPF: https://github.com/ookii-dialogs/ookii-dialogs-wpf . Он отлично работает и, как и ожидалось, в WPF без поддержки Microsoft. Вы можете получить его в виде пакета NuGet. Код представления XAML:
public partial class ExportRegionView : UserControl
{
public ExportRegionView()
{
InitializeComponent();
}
private void SavePath(object sender, RoutedEventArgs e)
{
var dialog = new Ookii.Dialogs.Wpf.VistaFolderBrowserDialog();
dialog.Description = "SIPAS Export Folder";
dialog.UseDescriptionForTitle = true;
if (dialog.ShowDialog().GetValueOrDefault())
{
ExportPath.Text = dialog.SelectedPath;
}
}
}
XAML: <Button Grid.Row="1" Grid.Column="3" Style="{DynamicResource Esri_Button}" Click="SavePath" Margin="5,5,5,5">Path</Button>
Microsoft.Win32.OpenFolderDialog
является частью .NET 8.0-preview7 и будет доступен в .NET 8:
Вы можете использовать что-то подобное в WPF. Я создал пример метода. Проверьте ниже.
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;
}
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
Browser.Navigate(@"C:\"); // Navigate Browser
Browser.Navigated += Browser_Navigated; // Create Event handl
}
string path { get; set; }
private void Button_Click(object sender, RoutedEventArgs e)
{
MessageBox.Show(path); // Check if work's , after you can use path as selected path ))
}
private void Browser_Navigated(object sender, NavigationEventArgs e)
{
path = e.Uri.LocalPath; // event when selected
}
}