From 0583a9cfdc9e96e03553fce6fbfe807680fcec7b Mon Sep 17 00:00:00 2001 From: David Lozoya Date: Tue, 5 Oct 2021 15:53:40 +0200 Subject: [PATCH 1/2] Add support for multiselect folder browser Co-authored-by: "C. Augusto Proiete" --- .../VistaFolderBrowserDialog.cs | 122 +++++++++++++++++- 1 file changed, 115 insertions(+), 7 deletions(-) diff --git a/src/Ookii.Dialogs.Wpf/VistaFolderBrowserDialog.cs b/src/Ookii.Dialogs.Wpf/VistaFolderBrowserDialog.cs index af49e0f..314f3a8 100644 --- a/src/Ookii.Dialogs.Wpf/VistaFolderBrowserDialog.cs +++ b/src/Ookii.Dialogs.Wpf/VistaFolderBrowserDialog.cs @@ -23,6 +23,7 @@ using System.Windows.Interop; using System.Windows; using System.Runtime.InteropServices; +using System.Linq; namespace Ookii.Dialogs.Wpf { @@ -40,6 +41,8 @@ public sealed class VistaFolderBrowserDialog { private string _description; private string _selectedPath; + private NativeMethods.FOS _options; + private string[] _selectedPaths; /// /// Creates a new instance of the class. @@ -108,8 +111,14 @@ public string SelectedPath { get { - return _selectedPath ?? string.Empty; + var selectedPath = + _selectedPath ?? + _selectedPaths?.FirstOrDefault() ?? + string.Empty; + + return selectedPath; } + set { _selectedPath = value; @@ -135,6 +144,63 @@ public string SelectedPath [Category("Folder Browsing"), DefaultValue(false), Description("A value that indicates whether to use the value of the Description property as the dialog title for Vista style dialogs. This property has no effect on old style dialogs.")] public bool UseDescriptionForTitle { get; set; } + /// + /// Gets or sets a value indicating whether the dialog box allows multiple folder to be selected. + /// + /// + /// if the dialog box allows multiple folder to be selected together or concurrently; otherwise, . + /// The default value is . + /// + [Description("A value indicating whether the dialog box allows multiple folders to be selected."), DefaultValue(false), Category("Behavior")] + public bool Multiselect + { + get + { + return HasOption(NativeMethods.FOS.FOS_ALLOWMULTISELECT); + } + + set + { + SetOption(NativeMethods.FOS.FOS_ALLOWMULTISELECT, value); + } + } + + /// + /// Gets the folder paths of all selected folder in the dialog box. + /// + /// + /// An array of type , containing the folder paths of all selected folder in the dialog box. + /// + [Description("The folder path of all selected folder in the dialog box."), Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public string[] SelectedPaths + { + get + { + var selectedPaths = _selectedPaths; + + if (selectedPaths is null) + { + var selectedPath = _selectedPath; + if (string.IsNullOrWhiteSpace(selectedPath)) + { + return new string[0]; + } + else + { + return new[] { selectedPath }; + } + } + + return (string[])selectedPaths.Clone(); + } + + set + { + _selectedPaths = value; + } + } + + #endregion #region Public Methods @@ -149,6 +215,8 @@ public void Reset() _selectedPath = string.Empty; RootFolder = Environment.SpecialFolder.Desktop; ShowNewFolderButton = true; + _selectedPaths = null; + _options = 0; } /// @@ -184,6 +252,27 @@ public void Reset() #endregion + #region Internal Methods + + internal void SetOption(NativeMethods.FOS option, bool value) + { + if (value) + { + _options |= option; + } + else + { + _options &= ~option; + } + } + + internal bool HasOption(NativeMethods.FOS option) + { + return (_options & option) != 0; + } + + #endregion + #region Private Methods private bool RunDialog(IntPtr owner) @@ -275,9 +364,9 @@ private void SetDialogProperties(Ookii.Dialogs.Wpf.Interop.IFileDialog dialog) } } - dialog.SetOptions(NativeMethods.FOS.FOS_PICKFOLDERS | NativeMethods.FOS.FOS_FORCEFILESYSTEM | NativeMethods.FOS.FOS_FILEMUSTEXIST); + dialog.SetOptions(NativeMethods.FOS.FOS_PICKFOLDERS | NativeMethods.FOS.FOS_FORCEFILESYSTEM | NativeMethods.FOS.FOS_FILEMUSTEXIST | _options); - if( !string.IsNullOrEmpty(_selectedPath) ) + if ( !string.IsNullOrEmpty(_selectedPath) ) { string parent = Path.GetDirectoryName(_selectedPath); if( parent == null || !Directory.Exists(parent) ) @@ -293,11 +382,30 @@ private void SetDialogProperties(Ookii.Dialogs.Wpf.Interop.IFileDialog dialog) } } - private void GetResult(Ookii.Dialogs.Wpf.Interop.IFileDialog dialog) + private void GetResult(IFileDialog dialog) { - Ookii.Dialogs.Wpf.Interop.IShellItem item; - dialog.GetResult(out item); - item.GetDisplayName(NativeMethods.SIGDN.SIGDN_FILESYSPATH, out _selectedPath); + if (Multiselect) + { + ((IFileOpenDialog)dialog).GetResults(out IShellItemArray results); + + results.GetCount(out uint count); + string[] folderPaths = new string[count]; + + for (uint x = 0; x < count; ++x) + { + results.GetItemAt(x, out IShellItem item); + item.GetDisplayName(NativeMethods.SIGDN.SIGDN_FILESYSPATH, out string name); + + folderPaths[x] = name; + } + + SelectedPaths = folderPaths; + } + else + { + dialog.GetResult(out IShellItem item); + item.GetDisplayName(NativeMethods.SIGDN.SIGDN_FILESYSPATH, out _selectedPath); + } } private int BrowseCallbackProc(IntPtr hwnd, NativeMethods.FolderBrowserDialogMessage msg, IntPtr lParam, IntPtr wParam) From de0e029202ea2460526aabc8f931311969c4b9a6 Mon Sep 17 00:00:00 2001 From: David Lozoya Date: Tue, 5 Oct 2021 15:54:16 +0200 Subject: [PATCH 2/2] Add sample for using Folder Browser with multiselect Co-authored-by: "C. Augusto Proiete" --- .../Ookii.Dialogs.Wpf.Sample/MainWindow.xaml | 1 + .../MainWindow.xaml.cs | 46 +++++++++++++++++-- 2 files changed, 42 insertions(+), 5 deletions(-) diff --git a/sample/Ookii.Dialogs.Wpf.Sample/MainWindow.xaml b/sample/Ookii.Dialogs.Wpf.Sample/MainWindow.xaml index 84fdc7a..dd8e390 100644 --- a/sample/Ookii.Dialogs.Wpf.Sample/MainWindow.xaml +++ b/sample/Ookii.Dialogs.Wpf.Sample/MainWindow.xaml @@ -16,6 +16,7 @@ Progress Dialog Credential Dialog Vista-style Folder Browser Dialog + Vista-style Folder Browser Dialog (Select Multiple) Vista-style Open File Dialog Vista-style Save File Dialog diff --git a/sample/Ookii.Dialogs.Wpf.Sample/MainWindow.xaml.cs b/sample/Ookii.Dialogs.Wpf.Sample/MainWindow.xaml.cs index 03a259b..7beeb56 100644 --- a/sample/Ookii.Dialogs.Wpf.Sample/MainWindow.xaml.cs +++ b/sample/Ookii.Dialogs.Wpf.Sample/MainWindow.xaml.cs @@ -60,9 +60,12 @@ private void _showDialogButton_Click(object sender, RoutedEventArgs e) ShowFolderBrowserDialog(); break; case 5: - ShowOpenFileDialog(); + ShowFolderBrowserDialogSelectMultiple(); break; case 6: + ShowOpenFileDialog(); + break; + case 7: ShowSaveFileDialog(); break; } @@ -162,13 +165,46 @@ private void ShowCredentialDialog() private void ShowFolderBrowserDialog() { - VistaFolderBrowserDialog dialog = new VistaFolderBrowserDialog(); + var dialog = new VistaFolderBrowserDialog(); dialog.Description = "Please select a folder."; dialog.UseDescriptionForTitle = true; // This applies to the Vista style dialog only, not the old dialog. - if( !VistaFolderBrowserDialog.IsVistaFolderDialogSupported ) + + if (!VistaFolderBrowserDialog.IsVistaFolderDialogSupported) + { MessageBox.Show(this, "Because you are not using Windows Vista or later, the regular folder browser dialog will be used. Please use Windows Vista to see the new dialog.", "Sample folder browser dialog"); - if( (bool)dialog.ShowDialog(this) ) - MessageBox.Show(this, "The selected folder was: " + dialog.SelectedPath, "Sample folder browser dialog"); + } + + if ((bool)dialog.ShowDialog(this)) + { + MessageBox.Show(this, $"The selected folder was:{Environment.NewLine}{dialog.SelectedPath}", "Sample folder browser dialog"); + } + } + + private void ShowFolderBrowserDialogSelectMultiple() + { + var dialog = new VistaFolderBrowserDialog(); + dialog.Multiselect = true; + dialog.Description = "Please select a folder."; + dialog.UseDescriptionForTitle = true; // This applies to the Vista style dialog only, not the old dialog. + + if (!VistaFolderBrowserDialog.IsVistaFolderDialogSupported) + { + MessageBox.Show(this, "Because you are not using Windows Vista or later, the regular folder browser dialog will be used. Please use Windows Vista to see the new dialog.", "Sample folder browser dialog"); + } + + if ((bool)dialog.ShowDialog(this)) + { + var selectedPaths = dialog.SelectedPaths; + + if (selectedPaths.Length == 1) + { + MessageBox.Show(this, $"The selected folder was:{Environment.NewLine}{selectedPaths[0]}", "Sample folder browser dialog"); + } + else + { + MessageBox.Show(this, $"The selected folders were:{Environment.NewLine}{string.Join(Environment.NewLine, selectedPaths)}", "Sample folder browser dialog"); + } + } } private void ShowOpenFileDialog()