英文:
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 "FOS_ALLOWMULTISELECT" 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'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
{
/// <summary>
/// Gets/sets folder in which dialog will be open.
/// </summary>
public string InitialFolder { get; set; }
/// <summary>
/// Gets/sets directory in which dialog will be open if there is no recent directory available.
/// </summary>
public string DefaultFolder { get; set; }
/// <summary>
/// Gets selected folder.
/// </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_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("DC1C5A9C-E88A-4DDE-A5A1-60F82A20AEF7")]
internal class FileOpenDialogRCW { }
[ComImport(), Guid("42F85136-DB7E-439C-85F1-E4075D135FC8"), 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("43826D1E-E718-42EE-BC55-A1E261C37BFE"), 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("b63ea76d-1f85-456f-a19c-48159efa858b"), 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("shell32.dll", 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.
/// <summary>
/// Vista-style open file dialog to select one folder or multiple folders
/// </summary>
internal class OpenFolderDialog : IDisposable
{
#region Variables
/// <summary>
/// Gets/sets folder in which dialog will be open.
/// </summary>
public string InitialFolder { get; set; }
/// <summary>
/// Gets/sets directory in which dialog will be open if there is no recent directory available.
/// </summary>
public string DefaultFolder { get; set; }
/// <summary>
/// Gets selected folders paths.
/// </summary>
public List<string> FoldersPaths = new List<string>();
/// <summary>
/// Gets selected folders names.
/// </summary>
public List<string> FoldersNames = new List<string>();
/// <summary>
/// Gets selected folder path.
/// </summary>
public string FolderPath => FoldersPaths.FirstOrDefault();
/// <summary>
/// Gets selected folders name.
/// </summary>
public string FolderName => FoldersNames.FirstOrDefault();
/// <summary>
/// Enable the option to select multiple folders.
/// </summary>
public bool Multiselect { get; set; }
/// <summary>
/// Sets text for dialog title.
/// </summary>
public string Title { get; set; }
/// <summary>
/// Sets text for dialog ok button.
/// </summary>
public string OkButtonLabel { get; set; }
/// <summary>
/// Sets text for dialog file name label.
/// </summary>
public string FileNameLabel { get; set; }
#endregion
internal DialogResult ShowDialog(IWin32Window owner)
{
if (Environment.OSVersion.Version.Major >= 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("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);
}
}
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 < 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 = "OS is not compatible with Windows Vista IFileDialog to support multiple folder selection";
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 = "|" + Guid.Empty.ToString();
// dialogFrm.FileName = "any";
// if (this.InitialFolder != null) { dialogFrm.InitialDirectory = this.InitialFolder; }
// dialogFrm.OverwritePrompt = false;
// dialogFrm.Title = "Select Folder (OS is not compatible with Windows Vista IFileDialog to support multiple folder selection)";
// 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("DC1C5A9C-E88A-4DDE-A5A1-60F82A20AEF7")]
internal class FileOpenDialogRCW { }
[ComImport, Guid("d57c7288-d4ad-4768-be02-9d969532d960"), 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("43826D1E-E718-42EE-BC55-A1E261C37BFE"), 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("b63ea76d-1f85-456f-a19c-48159efa858b"), 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("shell32.dll", 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't put this in the other answer because of the characters limit.
Second method. Only need one using:
using System.Runtime.InteropServices;
I don't know if is better. I use try and catch for the dialog result. I don't know if is optimal but work correctly.
/// <summary>
/// Vista-style open file dialog to select one folder or multiple folders
/// </summary>
internal class OpenFolderDialog : IDisposable
{
#region Variables
/// <summary>
/// Gets/sets folder in which dialog will be open.
/// </summary>
public string InitialFolder { get; set; }
/// <summary>
/// Gets/sets directory in which dialog will be open if there is no recent directory available.
/// </summary>
public string DefaultFolder { get; set; }
/// <summary>
/// Gets selected folders paths.
/// </summary>
public List<string> FoldersPaths = new List<string>();
/// <summary>
/// Gets selected folders names.
/// </summary>
public List<string> FoldersNames = new List<string>();
/// <summary>
/// Gets selected folder path.
/// </summary>
public string FolderPath => FoldersPaths.FirstOrDefault();
/// <summary>
/// Gets selected folders name.
/// </summary>
public string FolderName => FoldersNames.FirstOrDefault();
/// <summary>
/// Enable the option to select multiple folders.
/// </summary>
public bool Multiselect { get; set; }
/// <summary>
/// Sets text for dialog title.
/// </summary>
public string Title { get; set; }
/// <summary>
/// Sets text for dialog ok button.
/// </summary>
public string OkButtonLabel { get; set; }
/// <summary>
/// Sets text for dialog file name label.
/// </summary>
public string FileNameLabel { get; set; }
#endregion
internal DialogResult ShowDialog(IWin32Window owner)
{
if (Environment.OSVersion.Version.Major >= 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("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 //ShowLegacyDialog
{
using (var dialogFrm = new FolderBrowserDialog())
{
dialogFrm.Description = "OS is not compatible with Windows Vista IFileDialog to support multiple folder selection";
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 = "|" + Guid.Empty.ToString();
// dialogFrm.FileName = "any";
// if (this.InitialFolder != null) { dialogFrm.InitialDirectory = this.InitialFolder; }
// dialogFrm.OverwritePrompt = false;
// dialogFrm.Title = "Select Folder (OS is not compatible with Windows Vista IFileDialog to support multiple folder selection)";
// 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("DC1C5A9C-E88A-4DDE-A5A1-60F82A20AEF7")]
internal class FileOpenDialogRCW { }
[ComImport, Guid("d57c7288-d4ad-4768-be02-9d969532d960"), 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("43826D1E-E718-42EE-BC55-A1E261C37BFE"), 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("b63ea76d-1f85-456f-a19c-48159efa858b"), 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("shell32.dll", 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>
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论