diff --git a/Nuclex.Windows.Forms (net-4.0).csproj b/Nuclex.Windows.Forms (net-4.0).csproj
index 37b940b..37d31d3 100644
--- a/Nuclex.Windows.Forms (net-4.0).csproj
+++ b/Nuclex.Windows.Forms (net-4.0).csproj
@@ -57,6 +57,8 @@
+
+
diff --git a/Source/CommonDialogs/CommonDialogManager.cs b/Source/CommonDialogs/CommonDialogManager.cs
new file mode 100644
index 0000000..3d50d78
--- /dev/null
+++ b/Source/CommonDialogs/CommonDialogManager.cs
@@ -0,0 +1,187 @@
+#region CPL License
+/*
+Nuclex Framework
+Copyright (C) 2002-2019 Nuclex Development Labs
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the IBM Common Public License as
+published by the IBM Corporation; either version 1.0 of the
+License, or (at your option) any later version.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+IBM Common Public License for more details.
+
+You should have received a copy of the IBM Common Public
+License along with this library
+*/
+#endregion
+
+using System;
+using System.Text;
+using System.Windows.Forms;
+
+namespace Nuclex.Windows.Forms.CommonDialogs {
+
+ /// Displays common dialogs for selecting files and directories
+ public class CommonDialogManager : ICommonDialogService {
+
+ /// Initializes a new task dialog message service
+ public CommonDialogManager() : this(NullActiveWindowTracker.Default) { }
+
+ /// Initializes a new task dialog message service
+ ///
+ /// Active window tracker used to obtain the parent window for message boxes
+ ///
+ public CommonDialogManager(IActiveWindowTracker tracker) {
+ this.tracker = tracker;
+ }
+
+ /// Asks the user for a location to save a file under
+ /// Caption of the dialog
+ ///
+ /// File masks in the form "Description|*.dat" or "Description2|*.da2;*.da3"
+ ///
+ /// The full path of the file the user wishes to save as
+ public string AskForSaveLocation(string caption, params string[] masks) {
+ var saveDialog = new SaveFileDialog() {
+ Title = caption,
+ Filter = combineMasks(masks)
+ };
+
+ DialogResult result;
+ {
+ Form activeWindow = this.tracker.ActiveWindow;
+ if(activeWindow == null) {
+ result = saveDialog.ShowDialog();
+ } else {
+ result = saveDialog.ShowDialog(activeWindow);
+ }
+ }
+
+ if(result == DialogResult.OK) {
+ return saveDialog.FileName;
+ } else {
+ return null;
+ }
+ }
+
+ /// Asks the user to select a file to open
+ /// Caption of the dialog
+ ///
+ /// File masks in the form "Description|*.dat" or "Description2|*.da2;*.da3"
+ ///
+ /// The full path of the file the user selected
+ public string AskForFileToOpen(string caption, params string[] masks) {
+ var openDialog = new OpenFileDialog() {
+ Title = caption,
+ Filter = combineMasks(masks),
+ CheckFileExists = true,
+ CheckPathExists = true,
+ Multiselect = false
+ };
+
+ DialogResult result;
+ {
+ Form activeWindow = this.tracker.ActiveWindow;
+ if(activeWindow == null) {
+ result = openDialog.ShowDialog();
+ } else {
+ result = openDialog.ShowDialog(activeWindow);
+ }
+ }
+
+ if(result == DialogResult.OK) {
+ return openDialog.FileName;
+ } else {
+ return null;
+ }
+ }
+
+ /// Asks the user to select one or more files to open
+ /// Caption of the dialog
+ ///
+ /// File masks in the form "Description|*.dat" or "Description2|*.da2;*.da3"
+ ///
+ /// The full path of all files the user selected
+ public string[] AskForFilesToOpen(string caption, params string[] masks) {
+ var openDialog = new OpenFileDialog() {
+ Title = caption,
+ Filter = combineMasks(masks),
+ CheckFileExists = true,
+ CheckPathExists = true,
+ Multiselect = true
+ };
+
+ DialogResult result;
+ {
+ Form activeWindow = this.tracker.ActiveWindow;
+ if(activeWindow == null) {
+ result = openDialog.ShowDialog();
+ } else {
+ result = openDialog.ShowDialog(activeWindow);
+ }
+ }
+
+ if(result == DialogResult.OK) {
+ return openDialog.FileNames;
+ } else {
+ return null;
+ }
+ }
+
+ /// Asks the user to select a directory
+ /// The directory the user has selected
+ public string AskForDirectory(string caption) {
+ var folderDialog = new System.Windows.Forms.FolderBrowserDialog() {
+ Description = caption
+ };
+
+ DialogResult result;
+ {
+ Form activeWindow = this.tracker.ActiveWindow;
+ if(activeWindow == null) {
+ result = folderDialog.ShowDialog();
+ } else {
+ result = folderDialog.ShowDialog(activeWindow);
+ }
+ }
+
+ if(result == DialogResult.OK) {
+ return folderDialog.SelectedPath;
+ } else {
+ return null;
+ }
+ }
+
+ /// Combines an array of file masks into a single mask
+ /// Masks that will be combined
+ /// That combined masks
+ private static string combineMasks(string[] masks) {
+ if((masks == null) || (masks.Length == 0)) {
+ return null;
+ }
+
+ int requiredCapacity = 0;
+ for(int index = 0; index < masks.Length; ++index) {
+ requiredCapacity += masks[index].Length;
+ requiredCapacity += 1;
+ }
+
+ var maskBuilder = new StringBuilder(requiredCapacity);
+ maskBuilder.Append(masks[0]);
+ for(int index = 1; index < masks.Length; ++index) {
+ maskBuilder.Append('|');
+ maskBuilder.Append(masks[index]);
+ }
+
+ return maskBuilder.ToString();
+ }
+
+ /// Provides the currently active top-level window
+ private IActiveWindowTracker tracker;
+
+ }
+
+} // namespace Nuclex.Windows.Forms.CommonDialogs
diff --git a/Source/CommonDialogs/ICommonDialogService.cs b/Source/CommonDialogs/ICommonDialogService.cs
new file mode 100644
index 0000000..6f94b26
--- /dev/null
+++ b/Source/CommonDialogs/ICommonDialogService.cs
@@ -0,0 +1,58 @@
+#region CPL License
+/*
+Nuclex Framework
+Copyright (C) 2002-2019 Nuclex Development Labs
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the IBM Common Public License as
+published by the IBM Corporation; either version 1.0 of the
+License, or (at your option) any later version.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+IBM Common Public License for more details.
+
+You should have received a copy of the IBM Common Public
+License along with this library
+*/
+#endregion
+
+using System;
+
+namespace Nuclex.Windows.Forms.CommonDialogs {
+
+ /// Displays common dialogs for selecting files and directories
+ public interface ICommonDialogService {
+
+ /// Asks the user for a location to save a file under
+ /// Caption of the dialog
+ ///
+ /// File masks in the form "Description|*.dat" or "Description2|*.da2;*.da3"
+ ///
+ /// The full path of the file the user wishes to save as
+ string AskForSaveLocation(string caption, params string[] masks);
+
+ /// Asks the user to select a file to open
+ /// Caption of the dialog
+ ///
+ /// File masks in the form "Description|*.dat" or "Description2|*.da2;*.da3"
+ ///
+ /// The full path of the file the user selected
+ string AskForFileToOpen(string caption, params string[] masks);
+
+ /// Asks the user to select one or more files to open
+ /// Caption of the dialog
+ ///
+ /// File masks in the form "Description|*.dat" or "Description2|*.da2;*.da3"
+ ///
+ /// The full path of all files the user selected
+ string[] AskForFilesToOpen(string caption, params string[] masks);
+
+ /// Asks the user to select a directory
+ /// The directory the user has selected
+ string AskForDirectory(string caption);
+
+ }
+
+} // namespace Nuclex.Windows.Forms.CommonDialogs