如何向这个自定义文件对话框添加多选功能。

huangapple go评论64阅读模式
英文:

How add Multiselect to this custom File Dialog

问题

Here is the translation of the code you provided:

我想要在这个自定义文件对话框中添加多选功能。我添加了"FOS_ALLOWMULTISELECT"并且可以选择多个文件夹。但是当我尝试以与Multiselect=false相同的方式获取文件夹名称时shellItemArray为空,而不是System._ComObject。我不知道该怎么做,如果有人能帮助我,我将不胜感激。

需要引入以下命名空间:

using System.Runtime.InteropServices;
using System.Runtime.CompilerServices;

要使用这段代码,请添加:

using (OpenFolderDialog dialog = new OpenFolderDialog())
{
    dialog.Multiselect = true;
    if (dialog.ShowDialog(this) == DialogResult.OK)
    {
        
    }
}

代码:

internal class OpenFolderDialog : IDisposable
{
    /// <summary>
    /// 获取/设置对话框将在其中打开的文件夹。
    /// </summary>
    public string InitialFolder { get; set; }

    /// <summary>
    /// 获取/设置如果没有最近的目录可用,则对话框将在其中打开的目录。
    /// </summary>
    public string DefaultFolder { get; set; }

    /// <summary>
    /// 获取选定的文件夹。
    /// </summary>
    public string Folder { get; private set; }

    public bool Multiselect { get; set; }
    public string Title { get; set; }
    public string OkButtonLabel { get; set; }
    public string FileNameLabel { get; set; }

    internal DialogResult ShowDialog(IWin32Window owner)
    {
        if (Environment.OSVersion.Version.Major >= 6)
        {
            return ShowVistaDialog(owner);
        }
        else
        {
            return ShowLegacyDialog(owner);
        }
    }

    private DialogResult ShowVistaDialog(IWin32Window owner)
    {
        var frm = (NativeMethods.IFileDialog)(new NativeMethods.FileOpenDialogRCW());
        uint options;
        frm.GetOptions(out options);
        options |= NativeMethods.FOS_PICKFOLDERS | NativeMethods.FOS_FORCEFILESYSTEM | NativeMethods.FOS_NOVALIDATE | NativeMethods.FOS_NOTESTFILECREATE | NativeMethods.FOS_DONTADDTORECENT;
        if (Multiselect)
        {
            options |= NativeMethods.FOS_ALLOWMULTISELECT;
        }
        frm.SetOptions(options);

        if (Title != null)
        {
            frm.SetTitle(Title);
        }

        if (OkButtonLabel != null)
        {
            frm.SetOkButtonLabel(OkButtonLabel);
        }

        if (FileNameLabel != null)
        {
            frm.SetFileName(FileNameLabel);
        }

        if (!string.IsNullOrEmpty(InitialFolder))
        {
            NativeMethods.IShellItem directoryShellItem;
            var riid = new Guid("43826D1E-E718-42EE-BC55-A1E261C37BFE"); //IShellItem
            if (NativeMethods.SHCreateItemFromParsingName(this.InitialFolder, IntPtr.Zero, ref riid, out directoryShellItem) == NativeMethods.S_OK)
            {
                frm.SetFolder(directoryShellItem);
            }
        }
        if (!string.IsNullOrEmpty(DefaultFolder))
        {
            NativeMethods.IShellItem directoryShellItem;
            var riid = new Guid("43826D1E-E718-42EE-BC55-A1E261C37BFE"); //IShellItem
            if (NativeMethods.SHCreateItemFromParsingName(this.DefaultFolder, IntPtr.Zero, ref riid, out directoryShellItem) == NativeMethods.S_OK)
            {
                frm.SetDefaultFolder(directoryShellItem);
            }
        }

        if (frm.Show(owner.Handle) == NativeMethods.S_OK)
        {
            if (!Multiselect)
            {
                NativeMethods.IShellItem shellItem;
                if (frm.GetResult(out shellItem) == NativeMethods.S_OK)
                {
                    IntPtr pszString;
                    if (shellItem.GetDisplayName(NativeMethods.SIGDN_FILESYSPATH, out pszString) == NativeMethods.S_OK)
                    {
                        if (pszString != IntPtr.Zero)
                        {
                            try
                            {
                                this.Folder = Marshal.PtrToStringAuto(pszString);
                                return DialogResult.OK;
                            }
                            finally
                            {
                                Marshal.FreeCoTaskMem(pszString);
                            }
                        }
                    }
                }
            }
            else
            {
                NativeMethods.IShellItemArray shellItemArray;
                if (frm.GetResults(out shellItemArray) == NativeMethods.S_OK)
                {
                    uint items;
                    if (shellItemArray.GetCount(out items) == NativeMethods.S_OK)
                    {
                        
                    }
                }
            }
        }
        return DialogResult.Cancel;
    }

    private DialogResult ShowLegacyDialog(IWin32Window owner)
    {
        using (var frm = new SaveFileDialog())
        {
            frm.CheckFileExists = false;
            frm.CheckPathExists = true;
            frm.CreatePrompt = false;
            frm.Filter = "|" + Guid.Empty.ToString();
            frm.FileName = "any";
            if (this.InitialFolder != null) { frm.InitialDirectory = this.InitialFolder; }
            frm.OverwritePrompt = false;
            frm.Title = "Select Folder";
            frm.ValidateNames = false;
            if (frm.ShowDialog(owner) == DialogResult.OK)
            {
                this.Folder = Path.GetDirectoryName(frm.FileName);
                return DialogResult.OK;
            }
            else
            {
                return DialogResult.Cancel;
            }
        }
    }

    public void Dispose() { } //just to have possibility of Using statement.
}

internal static class NativeMethods
{
    #region Constants

    public const uint FOS_OVERWRITEPROMPT = 0x00000002;
    public const uint FOS_STRICTFILETYPES = 0x00000004;
    public const uint FOS_NOCHANGEDIR = 0x00000008;
    public const uint FOS_PICKFOLDERS = 0x00000020;
    public const uint FOS_FORCEFILESYSTEM = 0x00000040;
    public const uint FOS_ALLNONSTORAGEITEMS = 0x00000080;
    public const uint FOS_NOVALIDATE = 0x00000100;
    public const uint FOS_ALLOWMULTISELECT = 0x00000200;
    public const uint FOS_PATHMUSTEXIST = 0x00000800;
    public const uint FOS_FILEMUSTEXIST = 0x00001000;
    public const uint FOS_CREATEPROMPT = 0x00002000;
    public const uint FOS_SHAREAWARE = 0x00004000;
    public const uint FOS_NOREADONLYRETURN = 0x00008000;
    public const uint FOS_NOTESTFILECREATE = 0x00010000;
    public const uint FOS_HIDEMRUPLACES = 0x00020000;
    public const uint FOS_HIDEPINNEDPLACES = 0x00040000;
    public const uint FOS_NODEREFERENCELINKS = 0x00100000;
    public const uint FOS_OKBUTTONNEEDSINTER

<details>
<summary>英文:</summary>

I want to added Multiselect to this custom File Dialog. I add the &quot;FOS_ALLOWMULTISELECT&quot; and i can select more than one folder. But when i want to get the folders name the same way, when Multiselect=false, the shellItemArray comes null and not with the System._ComObject. I don&#39;t know how to do it, is someone can help me i would be grateful.

Need:

            using System.Runtime.InteropServices;
            using System.Runtime.CompilerServices;

To use the code add:

            using (OpenFolderDialog dialog = new OpenFolderDialog())
            {
                dialog.Multiselect = true;
                if (dialog.ShowDialog(this) == DialogResult.OK)
                {
                    
                }
            }


Code:

        internal class OpenFolderDialog : IDisposable
        {
            /// &lt;summary&gt;
            /// Gets/sets folder in which dialog will be open.
            /// &lt;/summary&gt;
            public string InitialFolder { get; set; }

            /// &lt;summary&gt;
            /// Gets/sets directory in which dialog will be open if there is no recent directory available.
            /// &lt;/summary&gt;
            public string DefaultFolder { get; set; }

            /// &lt;summary&gt;
            /// Gets selected folder.
            /// &lt;/summary&gt;
            public string Folder { get; private set; }

            public bool Multiselect { get; set; }
            public string Title { get; set; }
            public string OkButtonLabel { get; set; }
            public string FileNameLabel { get; set; }

            internal DialogResult ShowDialog(IWin32Window owner)
            {
                if (Environment.OSVersion.Version.Major &gt;= 6)
                {
                    return ShowVistaDialog(owner);
                }
                else
                {
                    return ShowLegacyDialog(owner);
                }
            }

            private DialogResult ShowVistaDialog(IWin32Window owner)
            {
                var frm = (NativeMethods.IFileDialog)(new NativeMethods.FileOpenDialogRCW());
                uint options;
                frm.GetOptions(out options);
                options |= NativeMethods.FOS_PICKFOLDERS | NativeMethods.FOS_FORCEFILESYSTEM | NativeMethods.FOS_NOVALIDATE | NativeMethods.FOS_NOTESTFILECREATE | NativeMethods.FOS_DONTADDTORECENT;
                if (Multiselect)
                {
                    options |= NativeMethods.FOS_ALLOWMULTISELECT;
                }
                frm.SetOptions(options);

                if (Title != null)
                {
                    frm.SetTitle(Title);
                }

                if (OkButtonLabel != null)
                {
                    frm.SetOkButtonLabel(OkButtonLabel);
                }

                if (FileNameLabel != null)
                {
                    frm.SetFileName(FileNameLabel);
                }

                if (!string.IsNullOrEmpty(InitialFolder))
                {
                    NativeMethods.IShellItem directoryShellItem;
                    var riid = new Guid(&quot;43826D1E-E718-42EE-BC55-A1E261C37BFE&quot;); //IShellItem
                    if (NativeMethods.SHCreateItemFromParsingName(this.InitialFolder, IntPtr.Zero, ref riid, out directoryShellItem) == NativeMethods.S_OK)
                    {
                        frm.SetFolder(directoryShellItem);
                    }
                }
                if (!string.IsNullOrEmpty(DefaultFolder))
                {
                    NativeMethods.IShellItem directoryShellItem;
                    var riid = new Guid(&quot;43826D1E-E718-42EE-BC55-A1E261C37BFE&quot;); //IShellItem
                    if (NativeMethods.SHCreateItemFromParsingName(this.DefaultFolder, IntPtr.Zero, ref riid, out directoryShellItem) == NativeMethods.S_OK)
                    {
                        frm.SetDefaultFolder(directoryShellItem);
                    }
                }

                if (frm.Show(owner.Handle) == NativeMethods.S_OK)
                {
                    if (!Multiselect)
                    {
                        NativeMethods.IShellItem shellItem;
                        if (frm.GetResult(out shellItem) == NativeMethods.S_OK)
                        {
                            IntPtr pszString;
                            if (shellItem.GetDisplayName(NativeMethods.SIGDN_FILESYSPATH, out pszString) == NativeMethods.S_OK)
                            {
                                if (pszString != IntPtr.Zero)
                                {
                                    try
                                    {
                                        this.Folder = Marshal.PtrToStringAuto(pszString);
                                        return DialogResult.OK;
                                    }
                                    finally
                                    {
                                        Marshal.FreeCoTaskMem(pszString);
                                    }
                                }
                            }
                        }
                    }
                    else
                    {
                        NativeMethods.IShellItemArray shellItemArray;
                        if (frm.GetResults(out shellItemArray) == NativeMethods.S_OK)
                        {
                            uint items;
                            if (shellItemArray.GetCount(out items) == NativeMethods.S_OK)
                            {
                                
                            }
                        }
                    }
                }
                return DialogResult.Cancel;
            }

            private DialogResult ShowLegacyDialog(IWin32Window owner)
            {
                using (var frm = new SaveFileDialog())
                {
                    frm.CheckFileExists = false;
                    frm.CheckPathExists = true;
                    frm.CreatePrompt = false;
                    frm.Filter = &quot;|&quot; + Guid.Empty.ToString();
                    frm.FileName = &quot;any&quot;;
                    if (this.InitialFolder != null) { frm.InitialDirectory = this.InitialFolder; }
                    frm.OverwritePrompt = false;
                    frm.Title = &quot;Select Folder&quot;;
                    frm.ValidateNames = false;
                    if (frm.ShowDialog(owner) == DialogResult.OK)
                    {
                        this.Folder = Path.GetDirectoryName(frm.FileName);
                        return DialogResult.OK;
                    }
                    else
                    {
                        return DialogResult.Cancel;
                    }
                }
            }

            public void Dispose() { } //just to have possibility of Using statement.
        }

        internal static class NativeMethods
        {
            #region Constants

            public const uint FOS_OVERWRITEPROMPT = 0x00000002;
            public const uint FOS_STRICTFILETYPES = 0x00000004;
            public const uint FOS_NOCHANGEDIR = 0x00000008;
            public const uint FOS_PICKFOLDERS = 0x00000020;
            public const uint FOS_FORCEFILESYSTEM = 0x00000040;
            public const uint FOS_ALLNONSTORAGEITEMS = 0x00000080;
            public const uint FOS_NOVALIDATE = 0x00000100;
            public const uint FOS_ALLOWMULTISELECT = 0x00000200;
            public const uint FOS_PATHMUSTEXIST = 0x00000800;
            public const uint FOS_FILEMUSTEXIST = 0x00001000;
            public const uint FOS_CREATEPROMPT = 0x00002000;
            public const uint FOS_SHAREAWARE = 0x00004000;
            public const uint FOS_NOREADONLYRETURN = 0x00008000;
            public const uint FOS_NOTESTFILECREATE = 0x00010000;
            public const uint FOS_HIDEMRUPLACES = 0x00020000;
            public const uint FOS_HIDEPINNEDPLACES = 0x00040000;
            public const uint FOS_NODEREFERENCELINKS = 0x00100000;
            public const uint FOS_OKBUTTONNEEDSINTERACTION = 0x00200000;
            public const uint FOS_DONTADDTORECENT = 0x02000000;
            public const uint FOS_FORCESHOWHIDDEN = 0x10000000;
            public const uint FOS_DEFAULTNOMINIMODE = 0x20000000;
            public const uint FOS_FORCEPREVIEWPANEON = 0x40000000;
            public const uint FOS_SUPPORTSTREAMABLEITEMS = unchecked(0x80000000);

            public const uint S_OK = 0x0000;

            public const uint SIGDN_DESKTOPABSOLUTEEDITING = 0x8004c000;
            public const uint SIGDN_DESKTOPABSOLUTEPARSING = 0x80028000;
            public const uint SIGDN_FILESYSPATH = 0x80058000;
            public const uint SIGDN_NORMALDISPLAY = 0;
            public const uint SIGDN_PARENTRELATIVE = 0x80080001;
            public const uint SIGDN_PARENTRELATIVEEDITING = 0x80031001;
            public const uint SIGDN_PARENTRELATIVEFORADDRESSBAR = 0x8007c001;
            public const uint SIGDN_PARENTRELATIVEPARSING = 0x80018001;
            public const uint SIGDN_URL = 0x80068000;

            #endregion

            #region COM

            [ComImport, ClassInterface(ClassInterfaceType.None), TypeLibType(TypeLibTypeFlags.FCanCreate), Guid(&quot;DC1C5A9C-E88A-4DDE-A5A1-60F82A20AEF7&quot;)]
            internal class FileOpenDialogRCW { }

            [ComImport(), Guid(&quot;42F85136-DB7E-439C-85F1-E4075D135FC8&quot;), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
            internal interface IFileDialog
            {
                [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
                [PreserveSig()]
                uint Show([In, Optional] IntPtr hwndOwner); //IModalWindow 

                [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
                uint SetFileTypes([In] uint cFileTypes, [In, MarshalAs(UnmanagedType.LPArray)] IntPtr rgFilterSpec);

                [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
                uint SetFileTypeIndex([In] uint iFileType);

                [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
                uint GetFileTypeIndex(out uint piFileType);

                [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
                uint Advise([In, MarshalAs(UnmanagedType.Interface)] IntPtr pfde, out uint pdwCookie);

                [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
                uint Unadvise([In] uint dwCookie);

                [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
                uint SetOptions([In] uint fos);

                [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
                uint GetOptions(out uint fos);

                [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
                void SetDefaultFolder([In, MarshalAs(UnmanagedType.Interface)] IShellItem psi);

                [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
                uint SetFolder([In, MarshalAs(UnmanagedType.Interface)] IShellItem psi);

                [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
                uint GetFolder([MarshalAs(UnmanagedType.Interface)] out IShellItem ppsi);

                [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
                uint GetCurrentSelection([MarshalAs(UnmanagedType.Interface)] out IShellItem ppsi);

                [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
                uint SetFileName([In, MarshalAs(UnmanagedType.LPWStr)] string pszName);

                [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
                uint GetFileName([MarshalAs(UnmanagedType.LPWStr)] out string pszName);

                [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
                uint SetTitle([In, MarshalAs(UnmanagedType.LPWStr)] string pszTitle);

                [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
                uint SetOkButtonLabel([In, MarshalAs(UnmanagedType.LPWStr)] string pszText);

                [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
                uint SetFileNameLabel([In, MarshalAs(UnmanagedType.LPWStr)] string pszLabel);

                [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
                uint GetResult([MarshalAs(UnmanagedType.Interface)] out IShellItem ppsi);

                [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
                uint AddPlace([In, MarshalAs(UnmanagedType.Interface)] IShellItem psi, uint fdap);

                [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
                uint SetDefaultExtension([In, MarshalAs(UnmanagedType.LPWStr)] string pszDefaultExtension);

                [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
                uint Close([MarshalAs(UnmanagedType.Error)] uint hr);

                [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
                uint SetClientGuid([In] ref Guid guid);

                [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
                uint ClearClientData();

                [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
                uint SetFilter([MarshalAs(UnmanagedType.Interface)] IntPtr pFilter);

                [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
                uint GetResults([MarshalAs(UnmanagedType.Interface)] out IShellItemArray ppenum);

                [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
                uint GetSelectedItems([MarshalAs(UnmanagedType.Interface)] out IShellItemArray ppsai);
            }

            [ComImport, Guid(&quot;43826D1E-E718-42EE-BC55-A1E261C37BFE&quot;), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
            internal interface IShellItem
            {
                [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
                uint BindToHandler([In] IntPtr pbc, [In] ref Guid rbhid, [In] ref Guid riid, [Out, MarshalAs(UnmanagedType.Interface)] out IntPtr ppvOut);

                [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
                uint GetParent([MarshalAs(UnmanagedType.Interface)] out IShellItem ppsi);

                [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
                uint GetDisplayName([In] uint sigdnName, out IntPtr ppszName);

                [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
                uint GetAttributes([In] uint sfgaoMask, out uint psfgaoAttribs);

                [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
                uint Compare([In, MarshalAs(UnmanagedType.Interface)] IShellItem psi, [In] uint hint, out int piOrder);
            }
         
            [ComImport, Guid(&quot;b63ea76d-1f85-456f-a19c-48159efa858b&quot;), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
            internal interface IShellItemArray
            {
                // Not supported: IBindCtx
                [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
                uint BindToHandler([In, MarshalAs(UnmanagedType.Interface)] IntPtr pbc, [In] ref Guid rbhid, [In] ref Guid riid, out IntPtr ppvOut);

                [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
                uint GetPropertyStore([In] int Flags, [In] ref Guid riid, out IntPtr ppv);

                [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
                uint GetPropertyDescriptionList([In] ref Guid riid, out IntPtr ppv);

                [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
                uint GetAttributes([In] uint sfgaoMask, out uint psfgaoAttribs);

                [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
                uint GetCount(out uint pdwNumItems);

                [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
                uint GetItemAt([In] uint dwIndex, [MarshalAs(UnmanagedType.Interface)] out IShellItem ppsi);

                // Not supported: IEnumShellItems (will use GetCount and GetItemAt instead)
                [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
                uint EnumItems([MarshalAs(UnmanagedType.Interface)] out IntPtr ppenumShellItems);
            }

            #endregion

            [DllImport(&quot;shell32.dll&quot;, CharSet = CharSet.Unicode, SetLastError = true)]
            internal static extern int SHCreateItemFromParsingName([MarshalAs(UnmanagedType.LPWStr)] string pszPath, IntPtr pbc, ref Guid riid, [MarshalAs(UnmanagedType.Interface)] out IShellItem ppv);
        }

</details>


# 答案1
**得分**: 2

```csharp
/// <summary>
/// 为IFileDialog更改Guid号以启用多选。感谢Simon Mourier(https://stackoverflow.com/a/66187224/8458887)提供了将列表传递到字符串的想法。
/// </summary>
internal class OpenFolderDialog : IDisposable
{
    ...

    /// <summary>
    /// 启用选择多个文件夹的选项。
    /// </summary>
    public bool Multiselect { get; set; }

    /// <summary>
    /// 为对话框标题设置文本。
    /// </summary>
    public string Title { get; set; }

    /// <summary>
    /// 为对话框确定按钮设置文本。
    /// </summary>
    public string OkButtonLabel { get; set; }

    /// <summary>
    /// 为对话框文件名标签设置文本。
    /// </summary>
    public string FileNameLabel { get; set; }

    ...

    internal DialogResult ShowDialog(IWin32Window owner)
    {
        ...
    }

    ...
}

internal static class NativeMethods
{
    ...
}
英文:

Changing Guid number for IFileDialog enable the multiselect. Thanks to Simon Mourier (https://stackoverflow.com/a/66187224/8458887) for the idea to past the list to the string.

    /// &lt;summary&gt;
/// Vista-style open file dialog to select one folder or multiple folders
/// &lt;/summary&gt;
internal class OpenFolderDialog : IDisposable
{
#region Variables
/// &lt;summary&gt;
/// Gets/sets folder in which dialog will be open.
/// &lt;/summary&gt;
public string InitialFolder { get; set; }
/// &lt;summary&gt;
/// Gets/sets directory in which dialog will be open if there is no recent directory available.
/// &lt;/summary&gt;
public string DefaultFolder { get; set; }
/// &lt;summary&gt;
/// Gets selected folders paths.
/// &lt;/summary&gt;
public List&lt;string&gt; FoldersPaths = new List&lt;string&gt;();
/// &lt;summary&gt;
/// Gets selected folders names.
/// &lt;/summary&gt;
public List&lt;string&gt; FoldersNames = new List&lt;string&gt;();
/// &lt;summary&gt;
/// Gets selected folder path.
/// &lt;/summary&gt;
public string FolderPath =&gt; FoldersPaths.FirstOrDefault();
/// &lt;summary&gt;
/// Gets selected folders name.
/// &lt;/summary&gt;
public string FolderName =&gt; FoldersNames.FirstOrDefault();
/// &lt;summary&gt;
/// Enable the option to select multiple folders.
/// &lt;/summary&gt;
public bool Multiselect { get; set; }
/// &lt;summary&gt;
/// Sets text for dialog title.
/// &lt;/summary&gt;
public string Title { get; set; }
/// &lt;summary&gt;
/// Sets text for dialog ok button.
/// &lt;/summary&gt;
public string OkButtonLabel { get; set; }
/// &lt;summary&gt;
/// Sets text for dialog file name label.
/// &lt;/summary&gt;
public string FileNameLabel { get; set; }
#endregion
internal DialogResult ShowDialog(IWin32Window owner)
{
if (Environment.OSVersion.Version.Major &gt;= 6)   //ShowVistaDialog
{
var dialogFrm = (NativeMethods.IFileDialog)new NativeMethods.FileOpenDialogRCW();
uint options;
dialogFrm.GetOptions(out options);
options |= NativeMethods.FOS_PICKFOLDERS | NativeMethods.FOS_FORCEFILESYSTEM | NativeMethods.FOS_NOVALIDATE | NativeMethods.FOS_NOTESTFILECREATE | NativeMethods.FOS_DONTADDTORECENT;
if (Multiselect)
{
options |= NativeMethods.FOS_ALLOWMULTISELECT;
}
dialogFrm.SetOptions(options);
if (Title != null)
{
dialogFrm.SetTitle(Title);
}
if (OkButtonLabel != null)
{
dialogFrm.SetOkButtonLabel(OkButtonLabel);
}
if (FileNameLabel != null)
{
dialogFrm.SetFileName(FileNameLabel);
}
if (!string.IsNullOrEmpty(InitialFolder))
{
NativeMethods.IShellItem directoryShellItem;
var riid = new Guid(&quot;43826D1E-E718-42EE-BC55-A1E261C37BFE&quot;); //IShellItem
if (NativeMethods.SHCreateItemFromParsingName(this.InitialFolder, IntPtr.Zero, ref riid, out directoryShellItem) == NativeMethods.S_OK)
{
dialogFrm.SetFolder(directoryShellItem);
}
}
if (!string.IsNullOrEmpty(DefaultFolder))
{
NativeMethods.IShellItem directoryShellItem;
var riid = new Guid(&quot;43826D1E-E718-42EE-BC55-A1E261C37BFE&quot;); //IShellItem
if (NativeMethods.SHCreateItemFromParsingName(this.DefaultFolder, IntPtr.Zero, ref riid, out directoryShellItem) == NativeMethods.S_OK)
{
dialogFrm.SetDefaultFolder(directoryShellItem);
}
}
if (dialogFrm.Show(owner.Handle) == NativeMethods.S_OK)
{
NativeMethods.IShellItemArray shellItemArray;
if (dialogFrm.GetResults(out shellItemArray) == NativeMethods.S_OK)
{
uint count;
if (shellItemArray.GetCount(out count) == NativeMethods.S_OK)
{
for (uint i = 0; i &lt; count; i++)
{
shellItemArray.GetItemAt(i, out var item);
string path;
item.GetDisplayName(NativeMethods.SIGDN_FILESYSPATH, out path);
string name;
item.GetDisplayName(NativeMethods.SIGDN_PARENTRELATIVE, out name);
if (!string.IsNullOrEmpty(path) || !string.IsNullOrEmpty(name))   //path != null || name != null
{
//FoldersPaths.Add(Marshal.PtrToStringAuto(path));
//FoldersNames.Add(Marshal.PtrToStringAuto(name));
FoldersPaths.Add(path);
FoldersNames.Add(name);
if (FoldersNames.Count == count)
{
return DialogResult.OK;
}
}
}
}
}
}
return DialogResult.Cancel;
}
else   //ShowLegacyDialog
{
using (var dialogFrm = new FolderBrowserDialog())
{
dialogFrm.Description = &quot;OS is not compatible with Windows Vista IFileDialog to support multiple folder selection&quot;;
if (dialogFrm.ShowDialog(owner) == DialogResult.OK)
{
FoldersPaths.Add(dialogFrm.SelectedPath);
FoldersNames.Add(Path.GetFileName(dialogFrm.SelectedPath));
return DialogResult.OK;
}
else
{
return DialogResult.Cancel;
}
}
// A hack to use SaveFileDialog to select folder
//using (var dialogFrm = new SaveFileDialog())
//{
//    dialogFrm.CheckFileExists = false;
//    dialogFrm.CheckPathExists = true;
//    dialogFrm.CreatePrompt = false;
//    dialogFrm.Filter = &quot;|&quot; + Guid.Empty.ToString();
//    dialogFrm.FileName = &quot;any&quot;;
//    if (this.InitialFolder != null) { dialogFrm.InitialDirectory = this.InitialFolder; }
//    dialogFrm.OverwritePrompt = false;
//    dialogFrm.Title = &quot;Select Folder (OS is not compatible with Windows Vista IFileDialog to support multiple folder selection)&quot;;
//    dialogFrm.ValidateNames = false;
//    if (dialogFrm.ShowDialog(owner) == DialogResult.OK)
//    {
//        FoldersPaths.Add(Path.GetDirectoryName(dialogFrm.FileName));
//        FoldersNames.Add(Path.GetFileName(FoldersPaths[0]));
//        return DialogResult.OK;
//    }
//    else
//    {
//        return DialogResult.Cancel;
//    }
//}
}
}
public void Dispose() { } //just to have possibility of Using statement.
}
internal static class NativeMethods
{
#region Constants
//public const uint FOS_OVERWRITEPROMPT = 0x00000002;
//public const uint FOS_STRICTFILETYPES = 0x00000004;
//public const uint FOS_NOCHANGEDIR = 0x00000008;
public const uint FOS_PICKFOLDERS = 0x00000020;
public const uint FOS_FORCEFILESYSTEM = 0x00000040;
//public const uint FOS_ALLNONSTORAGEITEMS = 0x00000080;
public const uint FOS_NOVALIDATE = 0x00000100;
public const uint FOS_ALLOWMULTISELECT = 0x00000200;
//public const uint FOS_PATHMUSTEXIST = 0x00000800;
//public const uint FOS_FILEMUSTEXIST = 0x00001000;
//public const uint FOS_CREATEPROMPT = 0x00002000;
//public const uint FOS_SHAREAWARE = 0x00004000;
//public const uint FOS_NOREADONLYRETURN = 0x00008000;
public const uint FOS_NOTESTFILECREATE = 0x00010000;
//public const uint FOS_HIDEMRUPLACES = 0x00020000;
//public const uint FOS_HIDEPINNEDPLACES = 0x00040000;
//public const uint FOS_NODEREFERENCELINKS = 0x00100000;
//public const uint FOS_OKBUTTONNEEDSINTERACTION = 0x00200000;
public const uint FOS_DONTADDTORECENT = 0x02000000;
//public const uint FOS_FORCESHOWHIDDEN = 0x10000000;
//public const uint FOS_DEFAULTNOMINIMODE = 0x20000000;
//public const uint FOS_FORCEPREVIEWPANEON = 0x40000000;
//public const uint FOS_SUPPORTSTREAMABLEITEMS = unchecked(0x80000000);
public const uint S_OK = 0x00000000;
//public const uint SIGDN_NORMALDISPLAY = 0x00000000;   //Name
//public const uint SIGDN_PARENTRELATIVEPARSING = 0x80018001;   //Name
//public const uint SIGDN_DESKTOPABSOLUTEPARSING = 0x80028000;   //Full Name
//public const uint SIGDN_PARENTRELATIVEEDITING = 0x80031001;   //Name
//public const uint SIGDN_DESKTOPABSOLUTEEDITING = 0x8004c000;   //Full Name
public const uint SIGDN_FILESYSPATH = 0x80058000;   //Full Name
//public const uint SIGDN_URL = 0x80068000;   //Name like a URL
//public const uint SIGDN_PARENTRELATIVEFORADDRESSBAR = 0x8007c001;   //Name
public const uint SIGDN_PARENTRELATIVE = 0x80080001;   //Name
#endregion
#region COM
[ComImport, ClassInterface(ClassInterfaceType.None), TypeLibType(TypeLibTypeFlags.FCanCreate), Guid(&quot;DC1C5A9C-E88A-4DDE-A5A1-60F82A20AEF7&quot;)]
internal class FileOpenDialogRCW { }
[ComImport, Guid(&quot;d57c7288-d4ad-4768-be02-9d969532d960&quot;), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
internal interface IFileDialog
{
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
[PreserveSig()]
uint Show([In, Optional] IntPtr hwndOwner); //IModalWindow 
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
uint SetFileTypes([In] uint cFileTypes, [In, MarshalAs(UnmanagedType.LPArray)] IntPtr rgFilterSpec);
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
uint SetFileTypeIndex([In] uint iFileType);
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
uint GetFileTypeIndex(out uint piFileType);
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
uint Advise([In, MarshalAs(UnmanagedType.Interface)] IntPtr pfde, out uint pdwCookie);
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
uint Unadvise([In] uint dwCookie);
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
uint SetOptions([In] uint fos);
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
uint GetOptions(out uint fos);
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
void SetDefaultFolder([In, MarshalAs(UnmanagedType.Interface)] IShellItem psi);
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
uint SetFolder([In, MarshalAs(UnmanagedType.Interface)] IShellItem psi);
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
uint GetFolder([MarshalAs(UnmanagedType.Interface)] out IShellItem ppsi);
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
uint GetCurrentSelection([MarshalAs(UnmanagedType.Interface)] out IShellItem ppsi);
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
uint SetFileName([In, MarshalAs(UnmanagedType.LPWStr)] string pszName);
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
uint GetFileName([MarshalAs(UnmanagedType.LPWStr)] out string pszName);
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
uint SetTitle([In, MarshalAs(UnmanagedType.LPWStr)] string pszTitle);
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
uint SetOkButtonLabel([In, MarshalAs(UnmanagedType.LPWStr)] string pszText);
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
uint SetFileNameLabel([In, MarshalAs(UnmanagedType.LPWStr)] string pszLabel);
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
uint GetResult([MarshalAs(UnmanagedType.Interface)] out IShellItem ppsi);
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
uint AddPlace([In, MarshalAs(UnmanagedType.Interface)] IShellItem psi, uint fdap);
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
uint SetDefaultExtension([In, MarshalAs(UnmanagedType.LPWStr)] string pszDefaultExtension);
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
uint Close([MarshalAs(UnmanagedType.Error)] uint hr);
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
uint SetClientGuid([In] ref Guid guid);
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
uint ClearClientData();
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
uint SetFilter([MarshalAs(UnmanagedType.Interface)] IntPtr pFilter);
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
uint GetResults([MarshalAs(UnmanagedType.Interface)] out IShellItemArray ppenum);
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
uint GetSelectedItems([MarshalAs(UnmanagedType.Interface)] out IShellItemArray ppsai);
}
[ComImport, Guid(&quot;43826D1E-E718-42EE-BC55-A1E261C37BFE&quot;), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
internal interface IShellItem
{
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
uint BindToHandler([In] IntPtr pbc, [In] ref Guid rbhid, [In] ref Guid riid, [Out, MarshalAs(UnmanagedType.Interface)] out IntPtr ppvOut);
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
uint GetParent([MarshalAs(UnmanagedType.Interface)] out IShellItem ppsi);
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
uint GetDisplayName([In] uint sigdnName, [MarshalAs(UnmanagedType.LPWStr)] out string ppszName);   //, out IntPtr ppszName   //, [MarshalAs(UnmanagedType.LPWStr)] out string ppszName
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
uint GetAttributes([In] uint sfgaoMask, out uint psfgaoAttribs);
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
uint Compare([In, MarshalAs(UnmanagedType.Interface)] IShellItem psi, [In] uint hint, out int piOrder);
}
[ComImport, Guid(&quot;b63ea76d-1f85-456f-a19c-48159efa858b&quot;), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
internal interface IShellItemArray
{
// Not supported: IBindCtx
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
uint BindToHandler([In, MarshalAs(UnmanagedType.Interface)] IntPtr pbc, [In] ref Guid rbhid, [In] ref Guid riid, out IntPtr ppvOut);
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
uint GetPropertyStore([In] int Flags, [In] ref Guid riid, out IntPtr ppv);
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
uint GetPropertyDescriptionList([In] ref Guid riid, out IntPtr ppv);   //[In] ref PROPERTYKEY keyType, [In] ref Guid riid
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
uint GetAttributes([In] uint sfgaoMask, out uint psfgaoAttribs);   //[In] SIATTRIBFLAGS dwAttribFlags, [In] uint sfgaoMask
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
uint GetCount(out uint pdwNumItems);
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
uint GetItemAt([In] uint dwIndex, [MarshalAs(UnmanagedType.Interface)] out IShellItem ppsi);
// Not supported: IEnumShellItems (will use GetCount and GetItemAt instead)
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
uint EnumItems([MarshalAs(UnmanagedType.Interface)] out IntPtr ppenumShellItems);
}
#endregion
[DllImport(&quot;shell32.dll&quot;, CharSet = CharSet.Unicode, SetLastError = true)]
internal static extern int SHCreateItemFromParsingName([MarshalAs(UnmanagedType.LPWStr)] string pszPath, IntPtr pbc, ref Guid riid, [MarshalAs(UnmanagedType.Interface)] out IShellItem ppv);
}

答案2

得分: 0

以下是您提供的代码的中文翻译部分:

// 我无法将这部分放在另一个答案中,因为超出字符限制。

// 第二种方法。只需要一个using:
using System.Runtime.InteropServices;

// 我不知道哪个更好。我使用try和catch处理对话框结果。我不知道是否最优,但可以正常工作。

/// <summary>
/// 类似Vista风格的打开文件对话框,用于选择一个文件夹或多个文件夹
/// </summary>
internal class OpenFolderDialog : IDisposable
{
    #region 变量

    /// <summary>
    /// 获取/设置对话框将打开的文件夹。
    /// </summary>
    public string InitialFolder { get; set; }

    /// <summary>
    /// 如果没有可用的最近目录,则获取/设置对话框将打开的目录。
    /// </summary>
    public string DefaultFolder { get; set; }

    /// <summary>
    /// 获取选择的文件夹路径。
    /// </summary>
    public List<string> FoldersPaths = new List<string>();

    /// <summary>
    /// 获取选择的文件夹名称。
    /// </summary>
    public List<string> FoldersNames = new List<string>();

    /// <summary>
    /// 获取选择的文件夹路径。
    /// </summary>
    public string FolderPath => FoldersPaths.FirstOrDefault();

    /// <summary>
    /// 获取选择的文件夹名称。
    /// </summary>
    public string FolderName => FoldersNames.FirstOrDefault();

    /// <summary>
    /// 启用选择多个文件夹的选项。
    /// </summary>
    public bool Multiselect { get; set; }

    /// <summary>
    /// 设置对话框标题的文本。
    /// </summary>
    public string Title { get; set; }

    /// <summary>
    /// 设置对话框确定按钮的文本。
    /// </summary>
    public string OkButtonLabel { get; set; }

    /// <summary>
    /// 设置对话框文件名标签的文本。
    /// </summary>
    public string FileNameLabel { get; set; }

    #endregion

    internal DialogResult ShowDialog(IWin32Window owner)
    {
        if (Environment.OSVersion.Version.Major >= 6)   // 显示Vista对话框
        {
            var dialogFrm = (NativeMethods.IFileDialog)new NativeMethods.FileOpenDialogRCW();
            uint options;
            dialogFrm.GetOptions(out options);
            options |= NativeMethods.FOS_PICKFOLDERS | NativeMethods.FOS_FORCEFILESYSTEM | NativeMethods.FOS_NOVALIDATE | NativeMethods.FOS_NOTESTFILECREATE | NativeMethods.FOS_DONTADDTORECENT;

            if (Multiselect)
            {
                options |= NativeMethods.FOS_ALLOWMULTISELECT;
            }
            dialogFrm.SetOptions(options);

            if (Title != null)
            {
                dialogFrm.SetTitle(Title);
            }

            if (OkButtonLabel != null)
            {
                dialogFrm.SetOkButtonLabel(OkButtonLabel);
            }

            if (FileNameLabel != null)
            {
                dialogFrm.SetFileName(FileNameLabel);
            }

            if (!string.IsNullOrEmpty(InitialFolder))
            {
                NativeMethods.IShellItem directoryShellItem;
                var riid = new Guid("43826D1E-E718-42EE-BC55-A1E261C37BFE"); // IShellItem
                if (NativeMethods.SHCreateItemFromParsingName(this.InitialFolder, IntPtr.Zero, ref riid, out directoryShellItem) == NativeMethods.S_OK)
                {
                    dialogFrm.SetFolder(directoryShellItem);
                }
            }
            if (!string.IsNullOrEmpty(DefaultFolder))
            {
                NativeMethods.IShellItem directoryShellItem;
                var riid = new Guid("43826D1E-E718-42EE-BC55-A1E261C37BFE"); // IShellItem
                if (NativeMethods.SHCreateItemFromParsingName(this.DefaultFolder, IntPtr.Zero, ref riid, out directoryShellItem) == NativeMethods.S_OK)
                {
                    dialogFrm.SetDefaultFolder(directoryShellItem);
                }
            }

            try
            {
                dialogFrm.Show(owner.Handle);
                NativeMethods.IShellItemArray shellItemArray;
                dialogFrm.GetResults(out shellItemArray);
                uint count;
                shellItemArray.GetCount(out count);
                for (uint i = 0; i < count; i++)
                {
                    shellItemArray.GetItemAt(i, out var item);
                    string path;
                    item.GetDisplayName(NativeMethods.SIGDN_FILESYSPATH, out path);
                    string name;
                    item.GetDisplayName(NativeMethods.SIGDN_PARENTRELATIVE, out name);
                    if (!string.IsNullOrEmpty(path) || !string.IsNullOrEmpty(name))
                    {
                        FoldersPaths.Add(path);
                        FoldersNames.Add(name);

                        if (FoldersNames.Count == count)
                        {
                            return DialogResult.OK;
                        }
                    }
                }
            }
            catch
            {
                return DialogResult.Cancel;
            }
            return DialogResult.Cancel;
        }
        else   // 显示传统对话框
        {
            using (var dialogFrm = new FolderBrowserDialog())
            {
                dialogFrm.Description = "操作系统不支持Windows Vista IFileDialog以支持多文件夹选择";
                if (dialogFrm.ShowDialog(owner) == DialogResult.OK)
                {
                    FoldersPaths.Add(dialogFrm.SelectedPath);
                    FoldersNames.Add(Path.GetFileName(dialogFrm.SelectedPath));
                    return DialogResult.OK;
                }
                else
                {
                    return DialogResult.Cancel;
                }
            }
        }
    }

    public void Dispose() { } // 仅用于可能的Using语句。
}

internal static class NativeMethods
{
    #region 常量

    //public const uint FOS_OVERWRITEPROMPT = 0x00000002;
    //public const uint FOS_STRICTFILETYPES = 0x00000004;
    //public const uint FOS_NOCHANGEDIR = 0x00000008;
    public const uint FOS_PICKFOLDERS = 0x00000020;
    public const uint FOS_FORCEFILESYSTEM = 0x00000040;
    //public const uint FOS_ALLNONSTORAGEITEMS = 0x00000080;
    public const uint FOS_NOVALIDATE = 0x00000100;
    public const uint FOS_ALLOWMULTISELECT = 0x00000200;
    //public const uint FOS_PATHMUSTEXIST = 0x00000800;
    //public const uint FOS_FILEMUSTEXIST = 0x00001000;
    //public const uint FOS_CREATEPROMPT = 0x00002000;
    //public const uint FOS_SHAREAWARE = 0x00004000;
    //public const uint FOS_NOREADONLYRETURN = 0x00008000;
    public const uint

<details>
<summary>英文:</summary>

I can&#39;t put this in the other answer because of the characters limit.

Second method. Only need one using:

    using System.Runtime.InteropServices;

I don&#39;t know if is better. I use try and catch for the dialog result. I don&#39;t know if is optimal but work correctly.

        /// &lt;summary&gt;
        /// Vista-style open file dialog to select one folder or multiple folders
        /// &lt;/summary&gt;
        internal class OpenFolderDialog : IDisposable
        {
            #region Variables

            /// &lt;summary&gt;
            /// Gets/sets folder in which dialog will be open.
            /// &lt;/summary&gt;
            public string InitialFolder { get; set; }

            /// &lt;summary&gt;
            /// Gets/sets directory in which dialog will be open if there is no recent directory available.
            /// &lt;/summary&gt;
            public string DefaultFolder { get; set; }

            /// &lt;summary&gt;
            /// Gets selected folders paths.
            /// &lt;/summary&gt;
            public List&lt;string&gt; FoldersPaths = new List&lt;string&gt;();

            /// &lt;summary&gt;
            /// Gets selected folders names.
            /// &lt;/summary&gt;
            public List&lt;string&gt; FoldersNames = new List&lt;string&gt;();

            /// &lt;summary&gt;
            /// Gets selected folder path.
            /// &lt;/summary&gt;
            public string FolderPath =&gt; FoldersPaths.FirstOrDefault();

            /// &lt;summary&gt;
            /// Gets selected folders name.
            /// &lt;/summary&gt;
            public string FolderName =&gt; FoldersNames.FirstOrDefault();

            /// &lt;summary&gt;
            /// Enable the option to select multiple folders.
            /// &lt;/summary&gt;
            public bool Multiselect { get; set; }

            /// &lt;summary&gt;
            /// Sets text for dialog title.
            /// &lt;/summary&gt;
            public string Title { get; set; }

            /// &lt;summary&gt;
            /// Sets text for dialog ok button.
            /// &lt;/summary&gt;
            public string OkButtonLabel { get; set; }

            /// &lt;summary&gt;
            /// Sets text for dialog file name label.
            /// &lt;/summary&gt;
            public string FileNameLabel { get; set; }

            #endregion

            internal DialogResult ShowDialog(IWin32Window owner)
            {
                if (Environment.OSVersion.Version.Major &gt;= 6)   //ShowVistaDialog
                {
                    var dialogFrm = (NativeMethods.IFileDialog)new NativeMethods.FileOpenDialogRCW();
                    uint options;
                    dialogFrm.GetOptions(out options);
                    options |= NativeMethods.FOS_PICKFOLDERS | NativeMethods.FOS_FORCEFILESYSTEM | NativeMethods.FOS_NOVALIDATE | NativeMethods.FOS_NOTESTFILECREATE | NativeMethods.FOS_DONTADDTORECENT;

                    if (Multiselect)
                    {
                        options |= NativeMethods.FOS_ALLOWMULTISELECT;
                    }
                    dialogFrm.SetOptions(options);

                    if (Title != null)
                    {
                        dialogFrm.SetTitle(Title);
                    }

                    if (OkButtonLabel != null)
                    {
                        dialogFrm.SetOkButtonLabel(OkButtonLabel);
                    }

                    if (FileNameLabel != null)
                    {
                        dialogFrm.SetFileName(FileNameLabel);
                    }

                    if (!string.IsNullOrEmpty(InitialFolder))
                    {
                        NativeMethods.IShellItem directoryShellItem;
                        var riid = new Guid(&quot;43826D1E-E718-42EE-BC55-A1E261C37BFE&quot;); //IShellItem
                        if (NativeMethods.SHCreateItemFromParsingName(this.InitialFolder, IntPtr.Zero, ref riid, out directoryShellItem) == NativeMethods.S_OK)
                        {
                            dialogFrm.SetFolder(directoryShellItem);
                        }
                    }
                    if (!string.IsNullOrEmpty(DefaultFolder))
                    {
                        NativeMethods.IShellItem directoryShellItem;
                        var riid = new Guid(&quot;43826D1E-E718-42EE-BC55-A1E261C37BFE&quot;); //IShellItem
                        if (NativeMethods.SHCreateItemFromParsingName(this.DefaultFolder, IntPtr.Zero, ref riid, out directoryShellItem) == NativeMethods.S_OK)
                        {
                            dialogFrm.SetDefaultFolder(directoryShellItem);
                        }
                    }

                    try
                    {
                        dialogFrm.Show(owner.Handle);
                        NativeMethods.IShellItemArray shellItemArray;
                        dialogFrm.GetResults(out shellItemArray);
                        uint count;
                        shellItemArray.GetCount(out count);
                        for (uint i = 0; i &lt; count; i++)
                        {
                            shellItemArray.GetItemAt(i, out var item);
                            string path;
                            item.GetDisplayName(NativeMethods.SIGDN_FILESYSPATH, out path);
                            string name;
                            item.GetDisplayName(NativeMethods.SIGDN_PARENTRELATIVE, out name);
                            if (!string.IsNullOrEmpty(path) || !string.IsNullOrEmpty(name))
                            {
                                FoldersPaths.Add(path);
                                FoldersNames.Add(name);

                                if (FoldersNames.Count == count)
                                {
                                    return DialogResult.OK;
                                }
                            }
                        }
                    }
                    catch
                    {
                        return DialogResult.Cancel;
                    }
                    return DialogResult.Cancel;
                }
                else   //ShowLegacyDialog
                {
                    using (var dialogFrm = new FolderBrowserDialog())
                    {
                        dialogFrm.Description = &quot;OS is not compatible with Windows Vista IFileDialog to support multiple folder selection&quot;;
                        if (dialogFrm.ShowDialog(owner) == DialogResult.OK)
                        {
                            FoldersPaths.Add(dialogFrm.SelectedPath);
                            FoldersNames.Add(Path.GetFileName(dialogFrm.SelectedPath));
                            return DialogResult.OK;
                        }
                        else
                        {
                            return DialogResult.Cancel;
                        }
                    }

                    // A hack to use SaveFileDialog to select folder
                    //using (var dialogFrm = new SaveFileDialog())
                    //{
                    //    dialogFrm.CheckFileExists = false;
                    //    dialogFrm.CheckPathExists = true;
                    //    dialogFrm.CreatePrompt = false;
                    //    dialogFrm.Filter = &quot;|&quot; + Guid.Empty.ToString();
                    //    dialogFrm.FileName = &quot;any&quot;;
                    //    if (this.InitialFolder != null) { dialogFrm.InitialDirectory = this.InitialFolder; }
                    //    dialogFrm.OverwritePrompt = false;
                    //    dialogFrm.Title = &quot;Select Folder (OS is not compatible with Windows Vista IFileDialog to support multiple folder selection)&quot;;
                    //    dialogFrm.ValidateNames = false;
                    //    if (dialogFrm.ShowDialog(owner) == DialogResult.OK)
                    //    {
                    //        FoldersPaths.Add(Path.GetDirectoryName(dialogFrm.FileName));
                    //        FoldersNames.Add(Path.GetFileName(FoldersPaths[0]));
                    //        return DialogResult.OK;
                    //    }
                    //    else
                    //    {
                    //        return DialogResult.Cancel;
                    //    }
                    //}
                }
            }

            public void Dispose() { } //just to have possibility of Using statement.
        }

        internal static class NativeMethods
        {
            #region Constants

            //public const uint FOS_OVERWRITEPROMPT = 0x00000002;
            //public const uint FOS_STRICTFILETYPES = 0x00000004;
            //public const uint FOS_NOCHANGEDIR = 0x00000008;
            public const uint FOS_PICKFOLDERS = 0x00000020;
            public const uint FOS_FORCEFILESYSTEM = 0x00000040;
            //public const uint FOS_ALLNONSTORAGEITEMS = 0x00000080;
            public const uint FOS_NOVALIDATE = 0x00000100;
            public const uint FOS_ALLOWMULTISELECT = 0x00000200;
            //public const uint FOS_PATHMUSTEXIST = 0x00000800;
            //public const uint FOS_FILEMUSTEXIST = 0x00001000;
            //public const uint FOS_CREATEPROMPT = 0x00002000;
            //public const uint FOS_SHAREAWARE = 0x00004000;
            //public const uint FOS_NOREADONLYRETURN = 0x00008000;
            public const uint FOS_NOTESTFILECREATE = 0x00010000;
            //public const uint FOS_HIDEMRUPLACES = 0x00020000;
            //public const uint FOS_HIDEPINNEDPLACES = 0x00040000;
            //public const uint FOS_NODEREFERENCELINKS = 0x00100000;
            //public const uint FOS_OKBUTTONNEEDSINTERACTION = 0x00200000;
            public const uint FOS_DONTADDTORECENT = 0x02000000;
            //public const uint FOS_FORCESHOWHIDDEN = 0x10000000;
            //public const uint FOS_DEFAULTNOMINIMODE = 0x20000000;
            //public const uint FOS_FORCEPREVIEWPANEON = 0x40000000;
            //public const uint FOS_SUPPORTSTREAMABLEITEMS = unchecked(0x80000000);

            public const uint S_OK = 0x00000000;

            //public const uint SIGDN_NORMALDISPLAY = 0x00000000;   //Name
            //public const uint SIGDN_PARENTRELATIVEPARSING = 0x80018001;   //Name
            //public const uint SIGDN_DESKTOPABSOLUTEPARSING = 0x80028000;   //Full Name
            //public const uint SIGDN_PARENTRELATIVEEDITING = 0x80031001;   //Name
            //public const uint SIGDN_DESKTOPABSOLUTEEDITING = 0x8004c000;   //Full Name
            public const uint SIGDN_FILESYSPATH = 0x80058000;   //Full Name
            //public const uint SIGDN_URL = 0x80068000;   //Name like a URL
            //public const uint SIGDN_PARENTRELATIVEFORADDRESSBAR = 0x8007c001;   //Name
            public const uint SIGDN_PARENTRELATIVE = 0x80080001;   //Name

            #endregion

            #region COM

            [ComImport, ClassInterface(ClassInterfaceType.None), TypeLibType(TypeLibTypeFlags.FCanCreate), Guid(&quot;DC1C5A9C-E88A-4DDE-A5A1-60F82A20AEF7&quot;)]
            internal class FileOpenDialogRCW { }

            [ComImport, Guid(&quot;d57c7288-d4ad-4768-be02-9d969532d960&quot;), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
            internal interface IFileDialog
            {
                void Show([In, Optional] IntPtr hwndOwner); //IModalWindow

                void SetFileTypes([In] uint cFileTypes, [In, MarshalAs(UnmanagedType.LPArray)] IntPtr rgFilterSpec);

                void SetFileTypeIndex([In] uint iFileType);

                void GetFileTypeIndex(out uint piFileType);

                void Advise([In, MarshalAs(UnmanagedType.Interface)] IntPtr pfde, out uint pdwCookie);

                void Unadvise([In] uint dwCookie);

                void SetOptions([In] uint fos);

                void GetOptions(out uint fos);

                void SetDefaultFolder([In, MarshalAs(UnmanagedType.Interface)] IShellItem psi);

                void SetFolder([In, MarshalAs(UnmanagedType.Interface)] IShellItem psi);

                void GetFolder([MarshalAs(UnmanagedType.Interface)] out IShellItem ppsi);

                void GetCurrentSelection([MarshalAs(UnmanagedType.Interface)] out IShellItem ppsi);

                void SetFileName([In, MarshalAs(UnmanagedType.LPWStr)] string pszName);

                void GetFileName([MarshalAs(UnmanagedType.LPWStr)] out string pszName);

                void SetTitle([In, MarshalAs(UnmanagedType.LPWStr)] string pszTitle);

                void SetOkButtonLabel([In, MarshalAs(UnmanagedType.LPWStr)] string pszText);

                void SetFileNameLabel([In, MarshalAs(UnmanagedType.LPWStr)] string pszLabel);

                void GetResult([MarshalAs(UnmanagedType.Interface)] out IShellItem ppsi);

                void AddPlace([In, MarshalAs(UnmanagedType.Interface)] IShellItem psi, uint fdap);

                void SetDefaultExtension([In, MarshalAs(UnmanagedType.LPWStr)] string pszDefaultExtension);

                void Close([MarshalAs(UnmanagedType.Error)] uint hr);

                void SetClientGuid([In] ref Guid guid);

                void ClearClientData();

                void SetFilter([MarshalAs(UnmanagedType.Interface)] IntPtr pFilter);

                void GetResults([MarshalAs(UnmanagedType.Interface)] out IShellItemArray ppenum);   //PreserveSigAttribute

                void GetSelectedItems([MarshalAs(UnmanagedType.Interface)] out IShellItemArray ppsai);
            }

            [ComImport, Guid(&quot;43826D1E-E718-42EE-BC55-A1E261C37BFE&quot;), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
            internal interface IShellItem
            {
                void BindToHandler([In] IntPtr pbc, [In] ref Guid rbhid, [In] ref Guid riid, [Out, MarshalAs(UnmanagedType.Interface)] out IntPtr ppvOut);

                void GetParent([MarshalAs(UnmanagedType.Interface)] out IShellItem ppsi);

                void GetDisplayName([In] uint sigdnName, [MarshalAs(UnmanagedType.LPWStr)] out string ppszName);   //, out IntPtr ppszName   //, [MarshalAs(UnmanagedType.LPWStr)] out string ppszName

                void GetAttributes([In] uint sfgaoMask, out uint psfgaoAttribs);

                void Compare([In, MarshalAs(UnmanagedType.Interface)] IShellItem psi, [In] uint hint, out int piOrder);
            }

            [ComImport, Guid(&quot;b63ea76d-1f85-456f-a19c-48159efa858b&quot;), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
            internal interface IShellItemArray
            {
                void BindToHandler([In, MarshalAs(UnmanagedType.Interface)] IntPtr pbc, [In] ref Guid rbhid, [In] ref Guid riid, out IntPtr ppvOut);

                void GetPropertyStore([In] int Flags, [In] ref Guid riid, out IntPtr ppv);

                void GetPropertyDescriptionList([In] ref Guid riid, out IntPtr ppv);   //[In] ref PROPERTYKEY keyType, [In] ref Guid riid

                void GetAttributes([In] uint sfgaoMask, out uint psfgaoAttribs);   //[In] SIATTRIBFLAGS dwAttribFlags, [In] uint sfgaoMask

                void GetCount(out uint pdwNumItems);

                void GetItemAt([In] uint dwIndex, [MarshalAs(UnmanagedType.Interface)] out IShellItem ppsi);

                void EnumItems([MarshalAs(UnmanagedType.Interface)] out IntPtr ppenumShellItems);
            }

            #endregion

            [DllImport(&quot;shell32.dll&quot;, CharSet = CharSet.Unicode, SetLastError = true)]
            internal static extern int SHCreateItemFromParsingName([MarshalAs(UnmanagedType.LPWStr)] string pszPath, IntPtr pbc, ref Guid riid, [MarshalAs(UnmanagedType.Interface)] out IShellItem ppv);
        }


</details>



huangapple
  • 本文由 发表于 2023年5月11日 08:44:55
  • 转载请务必保留本文链接:https://go.coder-hub.com/76223434.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定