Skip to content

Commit 1fa2a39

Browse files
Merge pull request #62 from lozo2010/master
Add support for multiselect folder browser
2 parents 15b96b9 + de0e029 commit 1fa2a39

File tree

3 files changed

+157
-12
lines changed

3 files changed

+157
-12
lines changed

sample/Ookii.Dialogs.Wpf.Sample/MainWindow.xaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
<ComboBoxItem>Progress Dialog</ComboBoxItem>
1717
<ComboBoxItem>Credential Dialog</ComboBoxItem>
1818
<ComboBoxItem>Vista-style Folder Browser Dialog</ComboBoxItem>
19+
<ComboBoxItem>Vista-style Folder Browser Dialog (Select Multiple)</ComboBoxItem>
1920
<ComboBoxItem>Vista-style Open File Dialog</ComboBoxItem>
2021
<ComboBoxItem>Vista-style Save File Dialog</ComboBoxItem>
2122
</ComboBox.Items>

sample/Ookii.Dialogs.Wpf.Sample/MainWindow.xaml.cs

Lines changed: 41 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -60,9 +60,12 @@ private void _showDialogButton_Click(object sender, RoutedEventArgs e)
6060
ShowFolderBrowserDialog();
6161
break;
6262
case 5:
63-
ShowOpenFileDialog();
63+
ShowFolderBrowserDialogSelectMultiple();
6464
break;
6565
case 6:
66+
ShowOpenFileDialog();
67+
break;
68+
case 7:
6669
ShowSaveFileDialog();
6770
break;
6871
}
@@ -162,13 +165,46 @@ private void ShowCredentialDialog()
162165

163166
private void ShowFolderBrowserDialog()
164167
{
165-
VistaFolderBrowserDialog dialog = new VistaFolderBrowserDialog();
168+
var dialog = new VistaFolderBrowserDialog();
166169
dialog.Description = "Please select a folder.";
167170
dialog.UseDescriptionForTitle = true; // This applies to the Vista style dialog only, not the old dialog.
168-
if( !VistaFolderBrowserDialog.IsVistaFolderDialogSupported )
171+
172+
if (!VistaFolderBrowserDialog.IsVistaFolderDialogSupported)
173+
{
169174
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");
170-
if( (bool)dialog.ShowDialog(this) )
171-
MessageBox.Show(this, "The selected folder was: " + dialog.SelectedPath, "Sample folder browser dialog");
175+
}
176+
177+
if ((bool)dialog.ShowDialog(this))
178+
{
179+
MessageBox.Show(this, $"The selected folder was:{Environment.NewLine}{dialog.SelectedPath}", "Sample folder browser dialog");
180+
}
181+
}
182+
183+
private void ShowFolderBrowserDialogSelectMultiple()
184+
{
185+
var dialog = new VistaFolderBrowserDialog();
186+
dialog.Multiselect = true;
187+
dialog.Description = "Please select a folder.";
188+
dialog.UseDescriptionForTitle = true; // This applies to the Vista style dialog only, not the old dialog.
189+
190+
if (!VistaFolderBrowserDialog.IsVistaFolderDialogSupported)
191+
{
192+
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");
193+
}
194+
195+
if ((bool)dialog.ShowDialog(this))
196+
{
197+
var selectedPaths = dialog.SelectedPaths;
198+
199+
if (selectedPaths.Length == 1)
200+
{
201+
MessageBox.Show(this, $"The selected folder was:{Environment.NewLine}{selectedPaths[0]}", "Sample folder browser dialog");
202+
}
203+
else
204+
{
205+
MessageBox.Show(this, $"The selected folders were:{Environment.NewLine}{string.Join(Environment.NewLine, selectedPaths)}", "Sample folder browser dialog");
206+
}
207+
}
172208
}
173209

174210
private void ShowOpenFileDialog()

src/Ookii.Dialogs.Wpf/VistaFolderBrowserDialog.cs

Lines changed: 115 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
using System.Windows.Interop;
2424
using System.Windows;
2525
using System.Runtime.InteropServices;
26+
using System.Linq;
2627

2728
namespace Ookii.Dialogs.Wpf
2829
{
@@ -40,6 +41,8 @@ public sealed class VistaFolderBrowserDialog
4041
{
4142
private string _description;
4243
private string _selectedPath;
44+
private NativeMethods.FOS _options;
45+
private string[] _selectedPaths;
4346

4447
/// <summary>
4548
/// Creates a new instance of the <see cref="VistaFolderBrowserDialog" /> class.
@@ -108,8 +111,14 @@ public string SelectedPath
108111
{
109112
get
110113
{
111-
return _selectedPath ?? string.Empty;
114+
var selectedPath =
115+
_selectedPath ??
116+
_selectedPaths?.FirstOrDefault() ??
117+
string.Empty;
118+
119+
return selectedPath;
112120
}
121+
113122
set
114123
{
115124
_selectedPath = value;
@@ -135,6 +144,63 @@ public string SelectedPath
135144
[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.")]
136145
public bool UseDescriptionForTitle { get; set; }
137146

147+
/// <summary>
148+
/// Gets or sets a value indicating whether the dialog box allows multiple folder to be selected.
149+
/// </summary>
150+
/// <value>
151+
/// <see langword="true" /> if the dialog box allows multiple folder to be selected together or concurrently; otherwise, <see langword="false" />.
152+
/// The default value is <see langword="false" />.
153+
/// </value>
154+
[Description("A value indicating whether the dialog box allows multiple folders to be selected."), DefaultValue(false), Category("Behavior")]
155+
public bool Multiselect
156+
{
157+
get
158+
{
159+
return HasOption(NativeMethods.FOS.FOS_ALLOWMULTISELECT);
160+
}
161+
162+
set
163+
{
164+
SetOption(NativeMethods.FOS.FOS_ALLOWMULTISELECT, value);
165+
}
166+
}
167+
168+
/// <summary>
169+
/// Gets the folder paths of all selected folder in the dialog box.
170+
/// </summary>
171+
/// <value>
172+
/// An array of type <see cref="string"/>, containing the folder paths of all selected folder in the dialog box.
173+
/// </value>
174+
[Description("The folder path of all selected folder in the dialog box."), Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
175+
public string[] SelectedPaths
176+
{
177+
get
178+
{
179+
var selectedPaths = _selectedPaths;
180+
181+
if (selectedPaths is null)
182+
{
183+
var selectedPath = _selectedPath;
184+
if (string.IsNullOrWhiteSpace(selectedPath))
185+
{
186+
return new string[0];
187+
}
188+
else
189+
{
190+
return new[] { selectedPath };
191+
}
192+
}
193+
194+
return (string[])selectedPaths.Clone();
195+
}
196+
197+
set
198+
{
199+
_selectedPaths = value;
200+
}
201+
}
202+
203+
138204
#endregion
139205

140206
#region Public Methods
@@ -149,6 +215,8 @@ public void Reset()
149215
_selectedPath = string.Empty;
150216
RootFolder = Environment.SpecialFolder.Desktop;
151217
ShowNewFolderButton = true;
218+
_selectedPaths = null;
219+
_options = 0;
152220
}
153221

154222
/// <summary>
@@ -184,6 +252,27 @@ public void Reset()
184252

185253
#endregion
186254

255+
#region Internal Methods
256+
257+
internal void SetOption(NativeMethods.FOS option, bool value)
258+
{
259+
if (value)
260+
{
261+
_options |= option;
262+
}
263+
else
264+
{
265+
_options &= ~option;
266+
}
267+
}
268+
269+
internal bool HasOption(NativeMethods.FOS option)
270+
{
271+
return (_options & option) != 0;
272+
}
273+
274+
#endregion
275+
187276
#region Private Methods
188277

189278
private bool RunDialog(IntPtr owner)
@@ -275,9 +364,9 @@ private void SetDialogProperties(Ookii.Dialogs.Wpf.Interop.IFileDialog dialog)
275364
}
276365
}
277366

278-
dialog.SetOptions(NativeMethods.FOS.FOS_PICKFOLDERS | NativeMethods.FOS.FOS_FORCEFILESYSTEM | NativeMethods.FOS.FOS_FILEMUSTEXIST);
367+
dialog.SetOptions(NativeMethods.FOS.FOS_PICKFOLDERS | NativeMethods.FOS.FOS_FORCEFILESYSTEM | NativeMethods.FOS.FOS_FILEMUSTEXIST | _options);
279368

280-
if( !string.IsNullOrEmpty(_selectedPath) )
369+
if ( !string.IsNullOrEmpty(_selectedPath) )
281370
{
282371
string parent = Path.GetDirectoryName(_selectedPath);
283372
if( parent == null || !Directory.Exists(parent) )
@@ -293,11 +382,30 @@ private void SetDialogProperties(Ookii.Dialogs.Wpf.Interop.IFileDialog dialog)
293382
}
294383
}
295384

296-
private void GetResult(Ookii.Dialogs.Wpf.Interop.IFileDialog dialog)
385+
private void GetResult(IFileDialog dialog)
297386
{
298-
Ookii.Dialogs.Wpf.Interop.IShellItem item;
299-
dialog.GetResult(out item);
300-
item.GetDisplayName(NativeMethods.SIGDN.SIGDN_FILESYSPATH, out _selectedPath);
387+
if (Multiselect)
388+
{
389+
((IFileOpenDialog)dialog).GetResults(out IShellItemArray results);
390+
391+
results.GetCount(out uint count);
392+
string[] folderPaths = new string[count];
393+
394+
for (uint x = 0; x < count; ++x)
395+
{
396+
results.GetItemAt(x, out IShellItem item);
397+
item.GetDisplayName(NativeMethods.SIGDN.SIGDN_FILESYSPATH, out string name);
398+
399+
folderPaths[x] = name;
400+
}
401+
402+
SelectedPaths = folderPaths;
403+
}
404+
else
405+
{
406+
dialog.GetResult(out IShellItem item);
407+
item.GetDisplayName(NativeMethods.SIGDN.SIGDN_FILESYSPATH, out _selectedPath);
408+
}
301409
}
302410

303411
private int BrowseCallbackProc(IntPtr hwnd, NativeMethods.FolderBrowserDialogMessage msg, IntPtr lParam, IntPtr wParam)

0 commit comments

Comments
 (0)