Click here to Skip to main content
Click here to Skip to main content
Add your own
alternative version

Clipz - A Friendly Introduction to the Windows 7 Taskbar Features

, 17 Dec 2009
An overview of the Windows 7 taskbar features, and how to use then in your own applications.
Clipz.zip
Clipz
bin
Debug
Clipz.csproj.user
CopyIcon.ico
DeleteIcon.ico
Model
Native Methods
Properties
Settings.settings
Resources
Audio.png
Clipboard.png
NextIcon.ico
PreviousIcon.ico
Utility
Core
AppRestartRecovery
Dialogs
Common
TaskDialogs
Interop
AppRestartRecovery
Dialogs
NetworkList
PowerManagement
TaskDialogs
NetworkList
PowerManagement
Properties
PropertySystem
SafeHandles
Libraries
StructureMap.dll
Shell
Common
CommonFileDialogs
Controls
Design
ShellObjects.cd
ShellThumbnailClassDiagram.cd
DesktopWindowManager
ExplorerBrowser
ExplorerBrowserDiagram.cd
Interop
Common
Dialogs
ExplorerBrowser
KnownFolders
PropertySystem
StockIcons
Taskbar
KnownFolders
Properties
PropertySystem
StockIcons
Taskbar
//Copyright (c) Microsoft Corporation.  All rights reserved.

using System;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Interop;
using System.Windows.Markup;
using Microsoft.WindowsAPICodePack.Dialogs.Controls;
using Microsoft.WindowsAPICodePack.Shell;
using MS.WindowsAPICodePack.Internal;

namespace Microsoft.WindowsAPICodePack.Dialogs
{
    /// <summary>
    /// Defines the abstract base class for the common file dialogs.
    /// </summary>
    [ContentProperty("Controls")]
    public abstract class CommonFileDialog : IDialogControlHost, IDisposable
    {
        /// <summary>
        /// The collection of names selected by the user.
        /// </summary>
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2104:DoNotDeclareReadOnlyMutableReferenceTypes")]
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1051:DoNotDeclareVisibleInstanceFields", Justification = "This is an internal field used by the CommonOpenFileDialog and possibly other dialogs deriving from this base class.")]
        protected readonly Collection<string> fileNames;
        internal readonly Collection<IShellItem> items;
        internal DialogShowState showState = DialogShowState.PreShow;

        private IFileDialog nativeDialog;
        private IFileDialogCustomize customize;
        private NativeDialogEventSink nativeEventSink;
        private bool? canceled;
        private bool resetSelections;
        private IntPtr parentWindow = IntPtr.Zero;

        private bool filterSet = false; // filters can be set only once

        /// <summary>
        /// Contains a common error message string shared by classes that 
        /// inherit from this class.
        /// </summary>
        protected const string IllegalPropertyChangeString = " cannot be changed while dialog is showing";

        #region Constructors

        /// <summary>
        /// Creates a new instance of this class.
        /// </summary>
        protected CommonFileDialog()
        {
            if (!CoreHelpers.RunningOnVista)
                throw new PlatformNotSupportedException(
                    "Common File Dialog requires Windows Vista or later.");

            fileNames = new Collection<string>();
            filters = new CommonFileDialogFilterCollection();
            items = new Collection<IShellItem>();
            controls = new CommonFileDialogControlCollection<CommonFileDialogControl>(this);
        }

        /// <summary>
        /// Creates a new instance of this class with the specified title.
        /// </summary>
        /// <param name="title">The title to display in the dialog.</param>
        protected CommonFileDialog(string title)
            : this()
        {
            this.title = title;
        }

        #endregion

        // Template method to allow derived dialog to create actual
        // specific COM coclass (e.g. FileOpenDialog or FileSaveDialog).
        internal abstract void InitializeNativeFileDialog();
        internal abstract IFileDialog GetNativeFileDialog();
        internal abstract void PopulateWithFileNames(Collection<string> names);
        internal abstract void PopulateWithIShellItems(Collection<IShellItem> shellItems);
        internal abstract void CleanUpNativeFileDialog();
        internal abstract ShellNativeMethods.FOS GetDerivedOptionFlags(ShellNativeMethods.FOS flags);

        #region Public API

        // Events.
        /// <summary>
        /// Raised just before the dialog is about to return with a result. Occurs when the user clicks on the Open 
        /// or Save button on a file dialog box. 
        /// </summary>
        public event CancelEventHandler FileOk;
        /// <summary>
        /// Raised just before the user navigates to a new folder.
        /// </summary>
        public event EventHandler<CommonFileDialogFolderChangeEventArgs> FolderChanging;
        /// <summary>
        /// Raised when the user navigates to a new folder.
        /// </summary>
        public event EventHandler FolderChanged;
        /// <summary>
        /// Raised when the user changes the selection in the dialog's view.
        /// </summary>
        public event EventHandler SelectionChanged;
        /// <summary>
        /// Raised when the dialog is opened to notify the application of the initial chosen filetype.
        /// </summary>
        public event EventHandler FileTypeChanged;
        /// <summary>
        /// Raised when the dialog is opening.
        /// </summary>
        public event EventHandler DialogOpening;

        private CommonFileDialogControlCollection<CommonFileDialogControl> controls;
        /// <summary>
        /// Gets the collection of controls for the dialog.
        /// </summary>
        public CommonFileDialogControlCollection<CommonFileDialogControl> Controls
        {
            get { return controls; }
        }

        private CommonFileDialogFilterCollection filters;
        /// <summary>
        /// Gets the filters used by the dialog.
        /// </summary>
        public CommonFileDialogFilterCollection Filters
        {
            get { return filters; }
        }

        private string title;
        /// <summary>
        /// Gets or sets the dialog title.
        /// </summary>
        /// <value>A <see cref="System.String"/> object.</value>
        public string Title
        {
            get { return title; }
            set
            {
                title = value;
                if (NativeDialogShowing)
                    nativeDialog.SetTitle(value);
            }
        }

        // This is the first of many properties that are backed by the FOS_*
        // bitflag options set with IFileDialog.SetOptions(). 
        // SetOptions() fails
        // if called while dialog is showing (e.g. from a callback).
        private bool ensureFileExists;
        /// <summary>
        /// Gets or sets a value that determines whether the file must exist beforehand.
        /// </summary>
        /// <value>A <see cref="System.Boolean"/> value. <b>true</b> if the file must exist.</value>
        /// <exception cref="System.InvalidOperationException">This property cannot be set when the dialog is visible.</exception>
        public bool EnsureFileExists
        {
            get { return ensureFileExists; }
            set
            {
                ThrowIfDialogShowing("EnsureFileExists" + IllegalPropertyChangeString);
                ensureFileExists = value;
            }
        }

        private bool ensurePathExists;
        /// <summary>
        /// Gets or sets a value that specifies whether the returned file must be in an existing folder.
        /// </summary>
        /// <value>A <see cref="System.Boolean"/> value. <b>true</b> if the file must exist.</value>
        /// <exception cref="System.InvalidOperationException">This property cannot be set when the dialog is visible.</exception>
        public bool EnsurePathExists
        {
            get { return ensurePathExists; }
            set
            {
                ThrowIfDialogShowing("EnsurePathExists" + IllegalPropertyChangeString);
                ensurePathExists = value;
            }
        }

        private bool ensureValidNames;
        /// <summary>Gets or sets a value that determines whether to validate file names.
        /// </summary>
        ///<value>A <see cref="System.Boolean"/> value. <b>true </b>to check for situations that would prevent an application from opening the selected file, such as sharing violations or access denied errors.</value>
        /// <exception cref="System.InvalidOperationException">This property cannot be set when the dialog is visible.</exception>
        /// 
        public bool EnsureValidNames
        {
            get { return ensureValidNames; }
            set
            {
                ThrowIfDialogShowing("EnsureValidNames" + IllegalPropertyChangeString);
                ensureValidNames = value;
            }
        }

        private bool ensureReadOnly;
        /// <summary>
        /// Gets or sets a value that determines whether read-only items are returned.
        /// Default value for CommonOpenFileDialog is true (allow read-only files) and 
        /// CommonSaveFileDialog is false (don't allow read-only files).
        /// </summary>
        /// <value>A <see cref="System.Boolean"/> value. <b>true</b> includes read-only items.</value>
        /// <exception cref="System.InvalidOperationException">This property cannot be set when the dialog is visible.</exception>
        public bool EnsureReadOnly
        {
            get { return ensureReadOnly; }
            set
            {
                ThrowIfDialogShowing("EnsureReadOnly" + IllegalPropertyChangeString);
                ensureReadOnly = value;
            }
        }

        private bool restoreDirectory;
        /// <summary>
        /// Gets or sets a value that determines the restore directory.
        /// </summary>
        /// <remarks></remarks>
        /// <exception cref="System.InvalidOperationException">This property cannot be set when the dialog is visible.</exception>
        public bool RestoreDirectory
        {
            get { return restoreDirectory; }
            set
            {
                ThrowIfDialogShowing("RestoreDirectory" + IllegalPropertyChangeString);
                restoreDirectory = value;
            }
        }

        private bool showPlacesList = true;
        /// <summary>
        /// Gets or sets a value that controls whether 
        /// to show or hide the list of pinned places that
        /// the user can choose.
        /// </summary>
        /// <value>A <see cref="System.Boolean"/> value. <b>true</b> if the list is visible; otherwise <b>false</b>.</value>
        /// <exception cref="System.InvalidOperationException">This property cannot be set when the dialog is visible.</exception>
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly", MessageId = "ShowPlaces", Justification = "The property is for showing or hiding the _Places_ section in Vista")]
        public bool ShowPlacesList
        {

            get { return showPlacesList; }
            set
            {
                ThrowIfDialogShowing("ShowPlacesList" + IllegalPropertyChangeString);
                showPlacesList = value;
            }
        }

        private bool addToMruList = true;
        /// <summary>
        /// Gets or sets a value that controls whether to show or hide the list of places where the user has recently opened or saved items.
        /// </summary>
        /// <value>A <see cref="System.Boolean"/> value.</value>
        /// <exception cref="System.InvalidOperationException">This property cannot be set when the dialog is visible.</exception>
        public bool AddToMostRecentlyUsedList
        {
            get { return addToMruList; }
            set
            {
                ThrowIfDialogShowing("AddToMostRecentlyUsedList" + IllegalPropertyChangeString);
                addToMruList = value;
            }
        }

        private bool showHiddenItems;
        ///<summary>
        /// Gets or sets a value that controls whether to show hidden items.
        /// </summary>
        /// <value>A <see cref="System.Boolean"/> value.<b>true</b> to show the items; otherwise <b>false</b>.</value>
        /// <exception cref="System.InvalidOperationException">This property cannot be set when the dialog is visible.</exception>
        public bool ShowHiddenItems
        {
            get { return showHiddenItems; }
            set
            {
                ThrowIfDialogShowing("ShowHiddenItems" + IllegalPropertyChangeString);
                showHiddenItems = value;
            }
        }
        private bool allowPropertyEditing;
        /// <summary>
        /// Gets or sets a value that controls whether 
        /// properties can be edited.
        /// </summary>
        /// <value>A <see cref="System.Boolean"/> value. </value>
        public bool AllowPropertyEditing
        {
            get { return allowPropertyEditing; }
            set { allowPropertyEditing = value; }
        }

        private bool navigateToShortcut = true;
        ///<summary>
        /// Gets or sets a value that controls whether shortcuts should be treated as their target items, allowing an application to open a .lnk file.
        /// </summary>
        /// <value>A <see cref="System.Boolean"/> value. <b>true</b> indicates that shortcuts should be treated as their targets. </value>
        /// <exception cref="System.InvalidOperationException">This property cannot be set when the dialog is visible.</exception>
        public bool NavigateToShortcut
        {
            get { return navigateToShortcut; }
            set
            {
                ThrowIfDialogShowing("NavigateToShortcut" + IllegalPropertyChangeString);
                navigateToShortcut = value;
            }
        }

        /// <summary>
        /// Gets or sets the default file extension to be added to file names. If the value is null
        /// or String.Empty, the extension is not added to the file names.
        /// </summary>
        public string DefaultExtension
        {
            get;
            set;
        }

        /// <summary>
        /// Gets the index for the currently selected file type.
        /// </summary>
        public int SelectedFileTypeIndex
        {
            get
            {
                uint fileType;

                if (nativeDialog != null)
                {
                    nativeDialog.GetFileTypeIndex(out fileType);
                    return (int)fileType;
                }

                return -1;
            }
        }

        /// <summary>
        /// Tries to set the File(s) Type Combo to match the value in 
        /// 'DefaultExtension'.  Only doing this if 'this' is a Save dialog 
        /// as it makes no sense to do this if only Opening a file.
        /// </summary>
        /// 
        /// <param name="dialog">The native/IFileDialog instance.</param>
        /// 
        private void SyncFileTypeComboToDefaultExtension(IFileDialog dialog)
        {
            // make sure it's a Save dialog and that there is a default 
            // extension to sync to.
            if (!(this is CommonSaveFileDialog) || DefaultExtension == null ||
                filters.Count <= 0)
            {
                return;
            }

            // The native version of SetFileTypeIndex() requires an
            // unsigned integer as its parameter. This (having it be defined
            // as a uint right up front) avoids a cast, and the potential 
            // problems of casting a signed value to an unsigned one.
            uint filtersCounter = 0;

            CommonFileDialogFilter filter = null;

            for (filtersCounter = 0; filtersCounter < filters.Count;
                filtersCounter++)
            {
                filter = (CommonFileDialogFilter)filters[(int)filtersCounter];

                if (filter.Extensions.Contains(DefaultExtension))
                {
                    // set the docType combo to match this 
                    // extension. property is a 1-based index.
                    dialog.SetFileTypeIndex(filtersCounter + 1);

                    // we're done, exit for
                    break;
                }
            }

        }

        /// <summary>
        /// Gets the selected filename.
        /// </summary>
        /// <value>A <see cref="System.String"/> object.</value>
        /// <exception cref="System.InvalidOperationException">This property cannot be used when multiple files are selected.</exception>
        public string FileName
        {
            get
            {
                CheckFileNamesAvailable();

                if (fileNames.Count > 1)
                    throw new InvalidOperationException("Multiple files selected - the FileNames property should be used instead.");

                string  returnFilename = fileNames[0];

                // "If extension is a null reference (Nothing in Visual 
                // Basic), the returned string contains the specified 
                // path with its extension removed."  Since we do not want 
                // to remove any existing extension, make sure the 
                // DefaultExtension property is NOT null.

                // if we should, and there is one to set...
                if (!string.IsNullOrEmpty(DefaultExtension))
                {
                    returnFilename = System.IO.Path.ChangeExtension(returnFilename, DefaultExtension);
                }

                return returnFilename;
            }
        }

        /// <summary>
        /// Gets the selected item as a ShellObject.
        /// </summary>
        /// <value>A <see cref="Microsoft.WindowsAPICodePack.Shell.ShellObject"></see> object.</value>
        /// <exception cref="System.InvalidOperationException">This property cannot be used when multiple files
        /// are selected.</exception>
        public ShellObject FileAsShellObject
        {
            get
            {
                CheckFileItemsAvailable();

                if (items.Count > 1)
                    throw new InvalidOperationException("Multiple files selected - the Items property should be used instead.");

                if (items.Count == 1)
                    return ShellObjectFactory.Create(items[0]);
                else
                    return null;
            }
        }

        /// <summary>
        /// Adds a location, such as a folder, library, search connector, or known folder, to the list of
        /// places available for a user to open or save items. This method actually adds an item
        /// to the <b>Favorite Links</b> or <b>Places</b> section of the Open/Save dialog.
        /// </summary>
        /// <param name="place">The item to add to the places list.</param>
        /// <param name="location">One of the enumeration values that indicates placement of the item in the list.</param>
        public void AddPlace(ShellContainer place, FileDialogAddPlaceLocation location)
        {
            // Get our native dialog
            if (nativeDialog == null)
            {
                InitializeNativeFileDialog();
                nativeDialog = GetNativeFileDialog();
            }

            // Add the shellitem to the places list
            if (nativeDialog != null)
                nativeDialog.AddPlace(((ShellObject)place).NativeShellItem, (ShellNativeMethods.FDAP)location);
        }

        /// <summary>
        /// Adds a location (folder, library, search connector, known folder) to the list of
        /// places available for the user to open or save items. This method actually adds an item
        /// to the <b>Favorite Links</b> or <b>Places</b> section of the Open/Save dialog. Overload method
        /// takes in a string for the path.
        /// </summary>
        /// <param name="path">The item to add to the places list.</param>
        /// <param name="location">One of the enumeration values that indicates placement of the item in the list.</param>
        public void AddPlace(string path, FileDialogAddPlaceLocation location)
        {
            if (string.IsNullOrEmpty(path))
                throw new ArgumentNullException("path");

            // Get our native dialog
            if (nativeDialog == null)
            {
                InitializeNativeFileDialog();
                nativeDialog = GetNativeFileDialog();
            }

            // Create a native shellitem from our path
            IShellItem2 nativeShellItem;
            Guid guid = new Guid(ShellIIDGuid.IShellItem2);
            int retCode = ShellNativeMethods.SHCreateItemFromParsingName(path, IntPtr.Zero, ref guid, out nativeShellItem);

            if (!CoreErrorHelper.Succeeded(retCode))
                throw new ExternalException("Shell item could not be created.", Marshal.GetExceptionForHR(retCode));

            // Add the shellitem to the places list
            if (nativeDialog != null)
                nativeDialog.AddPlace(nativeShellItem, (ShellNativeMethods.FDAP)location);
        }

        // Null = use default directory.
        private string initialDirectory;
        /// <summary>
        /// Gets or sets the initial directory displayed when the dialog is shown. 
        /// A null or empty string indicates that the dialog is using the default directory.
        /// </summary>
        /// <value>A <see cref="System.String"/> object.</value>
        public string InitialDirectory
        {
            get { return initialDirectory; }
            set { initialDirectory = value; }
        }

        private ShellContainer initialDirectoryShellContainer;
        /// <summary>
        /// Gets or sets a location that is always selected when the dialog is opened, 
        /// regardless of previous user action. A null value implies that the dialog is using 
        /// the default location.
        /// </summary>
        public ShellContainer InitialDirectoryShellContainer
        {
            get
            {
                return initialDirectoryShellContainer;
            }
            set
            {
                initialDirectoryShellContainer = value;
            }
        }

        private string defaultDirectory;
        /// <summary>
        /// Sets the folder and path used as a default if there is not a recently used folder value available.
        /// </summary>
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1044:PropertiesShouldNotBeWriteOnly", Justification = "This is following the native API")]
        public string DefaultDirectory
        {
            set { defaultDirectory = value; }
        }

        private ShellContainer defaultDirectoryShellContainer;
        /// <summary>
        /// Sets the location (<see cref="Microsoft.WindowsAPICodePack.Shell.ShellContainer">ShellContainer</see> 
        /// used as a default if there is not a recently used folder value available.
        /// </summary>
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1044:PropertiesShouldNotBeWriteOnly", Justification = "This is following the native API")]
        public ShellContainer DefaultDirectoryShellContainer
        {
            set { defaultDirectoryShellContainer = value; }
        }

        // Null = use default identifier.
        private Guid cookieIdentifier;
        /// <summary>
        /// Gets or sets a value that enables a calling application 
        /// to associate a GUID with a dialog's persisted state.
        /// </summary>
        public Guid CookieIdentifier
        {
            get { return cookieIdentifier; }
            set { cookieIdentifier = value; }
        }

        /// <summary>
        /// Displays the dialog.
        /// </summary>
        /// <param name="ownerWindowHandle">Window handle of any top-level window that will own the modal dialog box.</param>
        /// <returns>A <see cref="CommonFileDialogResult"/> object.</returns>
        public CommonFileDialogResult ShowDialog(IntPtr ownerWindowHandle)
        {
            if (ownerWindowHandle == IntPtr.Zero)
                throw new ArgumentException("ownerWindowHandle");

            // Set the parent / owner window
            parentWindow = ownerWindowHandle;

            // Show the modal dialog
            return ShowDialog();
        }

        /// <summary>
        /// Displays the dialog.
        /// </summary>
        /// <param name="window">Top-level WPF window that will own the modal dialog box.</param>
        /// <returns>A <see cref="CommonFileDialogResult"/> object.</returns>
        public CommonFileDialogResult ShowDialog(Window window)
        {
            if (window == null)
                throw new ArgumentNullException("window");

            // Set the parent / owner window
            parentWindow = (new WindowInteropHelper(window)).Handle;

            // Show the modal dialog
            return ShowDialog();
        }
        
        /// <summary>
        /// Displays the dialog.
        /// </summary>
        /// <returns>A <see cref="CommonFileDialogResult"/> object.</returns>
        public CommonFileDialogResult ShowDialog()
        {
            CommonFileDialogResult result;

            // Fetch derived native dialog (i.e. Save or Open).
            InitializeNativeFileDialog();
            nativeDialog = GetNativeFileDialog();

            // Apply outer properties to native dialog instance.
            ApplyNativeSettings(nativeDialog);
            InitializeEventSink(nativeDialog);

            // Clear user data if Reset has been called 
            // since the last show.
            if (resetSelections)
            {
                resetSelections = false;
            }

            // Show dialog.
            showState = DialogShowState.Showing;
            int hresult = nativeDialog.Show(parentWindow);
            showState = DialogShowState.Closed;

            // Create return information.
            if (CoreErrorHelper.Matches(hresult, (int)HRESULT.ERROR_CANCELLED))
            {
                canceled = true;
                result = CommonFileDialogResult.Cancel;
                fileNames.Clear();
            }
            else
            {
                canceled = false;
                result = CommonFileDialogResult.OK;

                // Populate filenames if user didn't cancel.
                PopulateWithFileNames(fileNames);

                // Populate the actual IShellItems
                PopulateWithIShellItems(items);
            }

            return result;
        }
        /// <summary>
        /// Removes the current selection.
        /// </summary>
        public void ResetUserSelections()
        {
            resetSelections = true;
        }

        /// <summary>
        /// Default file name.
        /// </summary>
        public string DefaultFileName
        {
            get;
            set;
        }

        #endregion

        #region Configuration

        private void InitializeEventSink(IFileDialog nativeDlg)
        {
            // Check if we even need to have a sink.
            if (FileOk != null
                || FolderChanging != null
                || FolderChanged != null
                || SelectionChanged != null
                || FileTypeChanged != null
                || DialogOpening != null
                || (controls != null && controls.Count > 0))
            {
                uint cookie;
                nativeEventSink = new NativeDialogEventSink(this);
                nativeDlg.Advise(nativeEventSink, out cookie);
                nativeEventSink.Cookie = cookie;
            }
        }

        private void ApplyNativeSettings(IFileDialog dialog)
        {
            Debug.Assert(dialog != null, "No dialog instance to configure");

            if (parentWindow == IntPtr.Zero)
            {
                if (System.Windows.Application.Current != null && System.Windows.Application.Current.MainWindow != null)
                    parentWindow = (new WindowInteropHelper(System.Windows.Application.Current.MainWindow)).Handle;
                else if (System.Windows.Forms.Application.OpenForms.Count > 0)
                    parentWindow = System.Windows.Forms.Application.OpenForms[0].Handle;
            }

            Guid guid = new Guid(ShellIIDGuid.IShellItem2);

            // Apply option bitflags.
            dialog.SetOptions(CalculateNativeDialogOptionFlags());

            // Other property sets.
            if (title != null)
                dialog.SetTitle(title);
            if (initialDirectoryShellContainer != null)
            {
                dialog.SetFolder(((ShellObject)initialDirectoryShellContainer).NativeShellItem);
            }
            if (defaultDirectoryShellContainer != null)
            {
                dialog.SetDefaultFolder(((ShellObject)defaultDirectoryShellContainer).NativeShellItem);
            }
            if (!String.IsNullOrEmpty(initialDirectory))
            {
                // Create a native shellitem from our path
                IShellItem2 initialDirectoryShellItem;
                ShellNativeMethods.SHCreateItemFromParsingName(initialDirectory, IntPtr.Zero, ref guid, out initialDirectoryShellItem);

                // If we get a real shell item back, 
                // then use that as the initial folder - otherwise,
                // we'll allow the dialog to revert to the default folder. 
                // (OR should we fail loudly?)
                if (initialDirectoryShellItem != null)
                    dialog.SetFolder(initialDirectoryShellItem);
            }
            if (!string.IsNullOrEmpty(defaultDirectory))
            {
                // Create a native shellitem from our path
                IShellItem2 defaultDirectoryShellItem;
                ShellNativeMethods.SHCreateItemFromParsingName(defaultDirectory, IntPtr.Zero, ref guid, out defaultDirectoryShellItem);

                // If we get a real shell item back, 
                // then use that as the initial folder - otherwise,
                // we'll allow the dialog to revert to the default folder. 
                // (OR should we fail loudly?)
                if (defaultDirectoryShellItem != null)
                    dialog.SetDefaultFolder(defaultDirectoryShellItem);
            }

            // Apply file type filters, if available.
            if (filters.Count > 0 && !filterSet)
            {
                dialog.SetFileTypes(
                    (uint)filters.Count,
                    filters.GetAllFilterSpecs());

                filterSet = true;

                SyncFileTypeComboToDefaultExtension(dialog);
            }

            if (cookieIdentifier != Guid.Empty)
                dialog.SetClientGuid(ref cookieIdentifier);

            // Set the default extension
            if (!string.IsNullOrEmpty(DefaultExtension))
                dialog.SetDefaultExtension(DefaultExtension);

            // Set the default filename
            dialog.SetFileName(DefaultFileName);
        }

        private ShellNativeMethods.FOS CalculateNativeDialogOptionFlags()
        {
            // We start with only a few flags set by default, 
            // then go from there based on the current state
            // of the managed dialog's property values.
            ShellNativeMethods.FOS flags =
                ShellNativeMethods.FOS.FOS_NOTESTFILECREATE;

            // Call to derived (concrete) dialog to 
            // set dialog-specific flags.
            flags = GetDerivedOptionFlags(flags);

            // Apply other optional flags.
            if (ensureFileExists)
                flags |= ShellNativeMethods.FOS.FOS_FILEMUSTEXIST;
            if (ensurePathExists)
                flags |= ShellNativeMethods.FOS.FOS_PATHMUSTEXIST;
            if (!ensureValidNames)
                flags |= ShellNativeMethods.FOS.FOS_NOVALIDATE;
            if (!EnsureReadOnly)
                flags |= ShellNativeMethods.FOS.FOS_NOREADONLYRETURN;
            if (restoreDirectory)
                flags |= ShellNativeMethods.FOS.FOS_NOCHANGEDIR;
            if (!showPlacesList)
                flags |= ShellNativeMethods.FOS.FOS_HIDEPINNEDPLACES;
            if (!addToMruList)
                flags |= ShellNativeMethods.FOS.FOS_DONTADDTORECENT;
            if (showHiddenItems)
                flags |= ShellNativeMethods.FOS.FOS_FORCESHOWHIDDEN;
            if (!navigateToShortcut)
                flags |= ShellNativeMethods.FOS.FOS_NODEREFERENCELINKS;
            return flags;
        }

        #endregion

        #region IDialogControlHost Members

        private static void GenerateNotImplementedException()
        {
            throw new NotImplementedException(
                "The method or operation is not implemented.");
        }

        bool IDialogControlHost.IsCollectionChangeAllowed()
        {
            return true;
        }

        void IDialogControlHost.ApplyCollectionChanged()
        {
            // Query IFileDialogCustomize interface before adding controls
            GetCustomizedFileDialog();
            // Populate all the custom controls and add them to the dialog
            foreach (CommonFileDialogControl control in controls)
            {
                if (!control.IsAdded)
                {
                    control.HostingDialog = this;
                    control.Attach(customize);
                    control.IsAdded = true;
                }
            }

        }

        bool IDialogControlHost.IsControlPropertyChangeAllowed(string propertyName, DialogControl control)
        {
            CommonFileDialog.GenerateNotImplementedException();
            return false;
        }

        void IDialogControlHost.ApplyControlPropertyChange(string propertyName, DialogControl control)
        {
            if (propertyName == "Text")
            {
                if (control is CommonFileDialogTextBox)
                    customize.SetEditBoxText(control.Id, ((CommonFileDialogControl)control).Text);
                else
                    customize.SetControlLabel(control.Id, ((CommonFileDialogControl)control).Text);
            }
            else if (propertyName == "Visible")
            {
                CommonFileDialogControl dialogControl = control as CommonFileDialogControl;
                ShellNativeMethods.CDCONTROLSTATE state;

                customize.GetControlState(control.Id, out state);

                if (dialogControl.Visible == true)
                    state |= ShellNativeMethods.CDCONTROLSTATE.CDCS_VISIBLE;
                else if (dialogControl.Visible == false)
                {
                    state &= ~ShellNativeMethods.CDCONTROLSTATE.CDCS_VISIBLE;
                }

                customize.SetControlState(control.Id, state);
            }
            else if (propertyName == "Enabled")
            {
                CommonFileDialogControl dialogControl = control as CommonFileDialogControl;
                ShellNativeMethods.CDCONTROLSTATE state;

                customize.GetControlState(control.Id, out state);

                if (dialogControl.Enabled == true)
                    state |= ShellNativeMethods.CDCONTROLSTATE.CDCS_ENABLED;
                else if (dialogControl.Enabled == false)
                {
                    state &= ~ShellNativeMethods.CDCONTROLSTATE.CDCS_ENABLED;
                }

                customize.SetControlState(control.Id, state);
            }
            else if (propertyName == "SelectedIndex")
            {
                if (control is CommonFileDialogRadioButtonList)
                {
                    CommonFileDialogRadioButtonList list = control as CommonFileDialogRadioButtonList;
                    customize.SetSelectedControlItem(control.Id, list.SelectedIndex);
                }
                else if (control is CommonFileDialogComboBox)
                {
                    CommonFileDialogComboBox box = control as CommonFileDialogComboBox;
                    customize.SetSelectedControlItem(control.Id, box.SelectedIndex);
                }
            }
            else if (propertyName == "IsChecked")
            {
                if (control is CommonFileDialogCheckBox)
                {
                    CommonFileDialogCheckBox checkBox = control as CommonFileDialogCheckBox;
                    customize.SetCheckButtonState(control.Id, checkBox.IsChecked);
                }
            }
        }

        #endregion

        #region Helpers

        /// <summary>
        /// Ensures that the user has selected one or more files.
        /// </summary>
        /// <permission cref="System.InvalidOperationException">
        /// The dialog has not been dismissed yet or the dialog was cancelled.
        /// </permission>
        protected void CheckFileNamesAvailable()
        {
            if (showState != DialogShowState.Closed)
                throw new InvalidOperationException(
                    "Filename not available - dialog has not closed yet.");

            if (canceled.GetValueOrDefault())
                throw new InvalidOperationException(
                    "Filename not available - dialog was canceled.");

            Debug.Assert(fileNames.Count != 0,
              "FileNames empty - shouldn't happen unless dialog canceled or not yet shown.");
        }

        /// <summary>
        /// Ensures that the user has selected one or more files.
        /// </summary>
        /// <permission cref="System.InvalidOperationException">
        /// The dialog has not been dismissed yet or the dialog was cancelled.
        /// </permission>
        protected void CheckFileItemsAvailable()
        {
            if (showState != DialogShowState.Closed)
                throw new InvalidOperationException(
                    "Filename not available - dialog has not closed yet.");

            if (canceled.GetValueOrDefault())
                throw new InvalidOperationException(
                    "Filename not available - dialog was canceled.");

            Debug.Assert(items.Count != 0,
              "Items list empty - shouldn't happen unless dialog canceled or not yet shown.");
        }

        static IntPtr GetHandleFromWindow(Window window)
        {
            if (window == null)
                return IntPtr.Zero;

            return (new WindowInteropHelper(window)).Handle;
        }

        private bool NativeDialogShowing
        {
            get
            {
                return (nativeDialog != null)
                    && (showState == DialogShowState.Showing ||
                    showState == DialogShowState.Closing);
            }
        }

        internal static string GetFileNameFromShellItem(IShellItem item)
        {
            string filename = null;
            IntPtr pszString = IntPtr.Zero;
            HRESULT hr = item.GetDisplayName(ShellNativeMethods.SIGDN.SIGDN_DESKTOPABSOLUTEPARSING, out pszString);
            if (hr == HRESULT.S_OK && pszString != IntPtr.Zero)
            {
                filename = Marshal.PtrToStringAuto(pszString);
                Marshal.FreeCoTaskMem(pszString);
            }
            return filename;
        }

        internal static IShellItem GetShellItemAt(IShellItemArray array, int i)
        {
            IShellItem result;
            uint index = (uint)i;
            array.GetItemAt(index, out result);
            return result;
        }

        /// <summary>
        /// Throws an exception when the dialog is showing preventing
        /// a requested change to a property or the visible set of controls.
        /// </summary>
        /// <param name="message">The message to include in the exception.</param>
        /// <permission cref="System.InvalidOperationException"> The dialog is in an
        /// invalid state to perform the requested operation.</permission>
        protected void ThrowIfDialogShowing(string message)
        {
            if (NativeDialogShowing)
                throw new InvalidOperationException(message);
        }
        /// <summary>
        /// Get the IFileDialogCustomize interface, preparing to add controls.
        /// </summary>
        private void GetCustomizedFileDialog()
        {
            if (customize == null)
            {
                if (nativeDialog == null)
                {
                    InitializeNativeFileDialog();
                    nativeDialog = GetNativeFileDialog();
                }
                customize = (IFileDialogCustomize)nativeDialog;
            }
        }
        #endregion

        #region CheckChanged handling members
        /// <summary>
        /// Raises the <see cref="CommonFileDialog.FileOk"/> event just before the dialog is about to return with a result.
        /// </summary>
        /// <param name="e">The event data.</param>
        protected virtual void OnFileOk(CancelEventArgs e)
        {
            CancelEventHandler handler = FileOk;
            if (handler != null)
            {
                handler(this, e);
            }
        }
        /// <summary>
        /// Raises the <see cref="FolderChanging"/> to stop navigation to a particular location.
        /// </summary>
        /// <param name="e">Cancelable event arguments.</param>
        protected virtual void OnFolderChanging(CommonFileDialogFolderChangeEventArgs e)
        {
            EventHandler<CommonFileDialogFolderChangeEventArgs> handler = FolderChanging;
            if (handler != null)
            {
                handler(this, e);
            }
        }
        /// <summary>
        /// Raises the <see cref="CommonFileDialog.FolderChanged"/> event when the user navigates to a new folder.
        /// </summary>
        /// <param name="e">The event data.</param>
        protected virtual void OnFolderChanged(EventArgs e)
        {
            EventHandler handler = FolderChanged;
            if (handler != null)
            {
                handler(this, e);
            }
        }
        /// <summary>
        /// Raises the <see cref="CommonFileDialog.SelectionChanged"/> event when the user changes the selection in the dialog's view.
        /// </summary>
        /// <param name="e">The event data.</param>
        protected virtual void OnSelectionChanged(EventArgs e)
        {
            EventHandler handler = SelectionChanged;
            if (handler != null)
            {
                handler(this, e);
            }
        }
        /// <summary>
        /// Raises the <see cref="CommonFileDialog.FileTypeChanged"/> event when the dialog is opened to notify the 
        /// application of the initial chosen filetype.
        /// </summary>
        /// <param name="e">The event data.</param>
        protected virtual void OnFileTypeChanged(EventArgs e)
        {
            EventHandler handler = FileTypeChanged;
            if (handler != null)
            {
                handler(this, e);
            }
        }
        /// <summary>
        /// Raises the <see cref="CommonFileDialog.DialogOpening"/> event when the dialog is opened.
        /// </summary>
        /// <param name="e">The event data.</param>
        protected virtual void OnOpening(EventArgs e)
        {
            EventHandler handler = DialogOpening;
            if (handler != null)
            {
                handler(this, e);
            }
        }

        #endregion

        #region NativeDialogEventSink Nested Class

        private class NativeDialogEventSink : IFileDialogEvents, IFileDialogControlEvents
        {
            private CommonFileDialog parent;
            private bool firstFolderChanged = true;

            public NativeDialogEventSink(CommonFileDialog commonDialog)
            {
                this.parent = commonDialog;
            }

            private uint cookie;
            [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "This can be called from an application using our library")]
            public uint Cookie
            {
                get { return cookie; }
                set { cookie = value; }
            }

            public HRESULT OnFileOk(IFileDialog pfd)
            {
                CancelEventArgs args = new CancelEventArgs();
                parent.OnFileOk(args);

                if (!args.Cancel)
                {
                    // Make sure all custom properties are sync'ed
                    if (parent.Controls != null)
                    {
                        foreach (CommonFileDialogControl control in parent.Controls)
                        {
                            if (control is CommonFileDialogTextBox)
                            {
                                (control as CommonFileDialogTextBox).SyncValue();
                                (control as CommonFileDialogTextBox).Closed = true;
                            }
                            // Also check subcontrols
                            else if (control is CommonFileDialogGroupBox)
                            {
                                CommonFileDialogGroupBox groupbox = control as CommonFileDialogGroupBox;
                                foreach (CommonFileDialogControl subcontrol in groupbox.Items)
                                {
                                    if (subcontrol is CommonFileDialogTextBox)
                                    {
                                        (subcontrol as CommonFileDialogTextBox).SyncValue();
                                        (subcontrol as CommonFileDialogTextBox).Closed = true;
                                    }
                                }
                            }
                        }
                    }
                }

                return (args.Cancel ? HRESULT.S_FALSE : HRESULT.S_OK);
            }

            public HRESULT OnFolderChanging(IFileDialog pfd, IShellItem psiFolder)
            {
                CommonFileDialogFolderChangeEventArgs args =
                    new CommonFileDialogFolderChangeEventArgs(CommonFileDialog.GetFileNameFromShellItem(psiFolder));
                if (!firstFolderChanged)
                    parent.OnFolderChanging(args);
                return (args.Cancel ? HRESULT.S_FALSE : HRESULT.S_OK);
            }

            public void OnFolderChange(IFileDialog pfd)
            {
                if (firstFolderChanged)
                {
                    firstFolderChanged = false;
                    parent.OnOpening(EventArgs.Empty);
                }
                else
                    parent.OnFolderChanged(EventArgs.Empty);
            }

            public void OnSelectionChange(IFileDialog pfd)
            {
                parent.OnSelectionChanged(EventArgs.Empty);
            }

            public void OnShareViolation(
                IFileDialog pfd,
                IShellItem psi,
                out ShellNativeMethods.FDE_SHAREVIOLATION_RESPONSE pResponse)
            {
                // Do nothing: we will ignore share violations, 
                // and don't register
                // for them, so this method should never be called.
                pResponse = ShellNativeMethods.FDE_SHAREVIOLATION_RESPONSE.FDESVR_ACCEPT;
            }

            public void OnTypeChange(IFileDialog pfd)
            {
                parent.OnFileTypeChanged(EventArgs.Empty);
            }

            public void OnOverwrite(IFileDialog pfd, IShellItem psi, out ShellNativeMethods.FDE_OVERWRITE_RESPONSE pResponse)
            {
                // Don't accept or reject the dialog, keep default settings
                pResponse = ShellNativeMethods.FDE_OVERWRITE_RESPONSE.FDEOR_DEFAULT;
            }

            public void OnItemSelected(IFileDialogCustomize pfdc, int dwIDCtl, int dwIDItem)
            {
                // Find control
                DialogControl control = this.parent.controls.GetControlbyId(dwIDCtl);

                // Process ComboBox and/or RadioButtonList
                if (control is ICommonFileDialogIndexedControls)
                {
                    // Update selected item and raise SelectedIndexChanged event
                    ICommonFileDialogIndexedControls controlInterface = control as ICommonFileDialogIndexedControls;
                    controlInterface.SelectedIndex = dwIDItem;
                    controlInterface.RaiseSelectedIndexChangedEvent();
                }
                // Process Menu
                else if (control is CommonFileDialogMenu)
                {
                    CommonFileDialogMenu menu = control as CommonFileDialogMenu;

                    // Find the menu item that was clicked and invoke it's click event
                    foreach (CommonFileDialogMenuItem item in menu.Items)
                    {
                        if (item.Id == dwIDItem)
                        {
                            item.RaiseClickEvent();
                            break;
                        }
                    }
                }
            }

            public void OnButtonClicked(IFileDialogCustomize pfdc, int dwIDCtl)
            {
                // Find control
                DialogControl control = this.parent.controls.GetControlbyId(dwIDCtl);

                // Call corresponding event
                if (control is CommonFileDialogButton)
                {
                    ((CommonFileDialogButton)control).RaiseClickEvent();
                }
            }

            public void OnCheckButtonToggled(IFileDialogCustomize pfdc, int dwIDCtl, bool bChecked)
            {
                // Find control
                DialogControl control = this.parent.controls.GetControlbyId(dwIDCtl);

                // Update control and call corresponding event
                if (control is CommonFileDialogCheckBox)
                {
                    CommonFileDialogCheckBox box = control as CommonFileDialogCheckBox;
                    box.IsChecked = bChecked;
                    box.RaiseCheckedChangedEvent();
                }
            }

            public void OnControlActivating(IFileDialogCustomize pfdc, int dwIDCtl)
            {
            }
        }

        #endregion

        #region IDisposable Members

        /// <summary>
        /// Releases the unmanaged resources used by the CommonFileDialog class and optionally 
        /// releases the managed resources.
        /// </summary>
        /// <param name="disposing"><b>true</b> to release both managed and unmanaged resources; 
        /// <b>false</b> to release only unmanaged resources.</param>
        public void Dispose(bool disposing)
        {
            if (disposing)
            {
                CleanUpNativeFileDialog();
            }
        }

        /// <summary>
        /// Releases the resources used by the current instance of the CommonFileDialog class.
        /// </summary>
        public void Dispose()
        {
            Dispose(true);
        }

        #endregion

        /// <summary>
        /// Indicates whether this feature is supported on the current platform.
        /// </summary>
        public static bool IsPlatformSupported
        {
            get
            {
                // We need Windows Vista onwards ...
                return CoreHelpers.RunningOnVista;
            }
        }
    }


}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

TylerBrinks
Web Developer PageLabs
United States United States
I'm the founder of PageLabs, a web-based performance and SEO optimization site.

Give your site a boost in performance, even take a free speed test!
 
http://www.pagelabs.com
Follow on   Twitter

| Advertise | Privacy | Mobile
Web03 | 2.8.140721.1 | Last Updated 17 Dec 2009
Article Copyright 2009 by TylerBrinks
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid