c#如何允许选择我的电脑与文件夹浏览器对话框

本文关键字:文件夹 浏览器 对话框 电脑 我的 何允许 选择 选择我 | 更新日期: 2023-09-27 18:14:34

在我的应用程序中,我希望folderBrowserDialog允许选择My Computer。但是,在对话框中选择My Computer后,OK按钮被禁用。

是否有办法允许在Browse Dialog中选择My Computer ?

c#如何允许选择我的电脑与文件夹浏览器对话框

我的电脑(或这台电脑在最近的Windows版本)是一个特殊的文件夹(不是文件系统文件夹),标准的FolderBrowserDialog类不支持它。

下面是一个FolderBrowser类的替换样例,它允许用户选择任何文件夹。这是一个在Windows表单应用程序中使用它的例子:
public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
    }
    private void button1_Click(object sender, EventArgs e)
    {
        // other special GUIDs are defined in Windows SDK's ShlGuid.h
        Guid CLSID_MyComputer = new Guid("20D04FE0-3AEA-1069-A2D8-08002B30309D");
        FolderBrowser dlg = new FolderBrowser();
        dlg.Title = "Choose any folder you want";
        // optionally uncomment the following line to start from a folder
        //dlg.SelectedPath = @"c:'temp";
        // optionally uncomment the following line to start from My Computer/This PC
        //dlg.SelectedDesktopAbsoluteParsing = "::" + CLSID_MyComputer.ToString("B");
        if (dlg.ShowDialog(null) == DialogResult.OK)
        {
            MessageBox.Show(dlg.SelectedDesktopAbsoluteParsing + Environment.NewLine +
                dlg.SelectedNormalDisplay + Environment.NewLine +
                dlg.SelectedPath + Environment.NewLine +
                dlg.SelectedUrl);
            if (dlg.SelectedDesktopAbsoluteParsing == "::" + CLSID_MyComputer.ToString("B").ToUpperInvariant())
            {
                MessageBox.Show("My Computer was selected!");
            }
        }
    }
}

在这个答案的底部,你会发现FolderBrowser替代类。有趣的部分是这一行:

dialog.SetOptions(FOS.FOS_PICKFOLDERS | FOS.FOS_ALLNONSTORAGEITEMS);

指示代码只选择文件夹,并允许非文件系统项。

对于普通文件夹,返回的SelectedPath属性将包含文件夹路径。对于没有存储空间的特殊文件夹,它将为空。但是shell为我们提供了一个规范的名称,其中包含普通文件夹的文件夹路径和其他文件夹的特殊值,这些值将在SelectedDesktopAbsoluteParsing属性中定义。在My Computer的情况下,该值将始终为" ::{20D04FE0-3AEA-1069-A2D8-08002B30309D} "。

这个特殊的语法在这里有一些正式的定义:指定一个命名空间扩展的位置。

public class FolderBrowser
{
    public string SelectedPath { get; set; }
    public string SelectedDesktopAbsoluteParsing { get; set; }
    public string Title { get; set; }
    public string SelectedNormalDisplay { get; private set; }
    public string SelectedUrl { get; private set; }
    public DialogResult ShowDialog(IWin32Window owner)
    {
        var dialog = (IFileOpenDialog)new FileOpenDialog();
        if (!string.IsNullOrEmpty(SelectedPath))
        {
            SelectInitialPath(dialog, SelectedPath);
        }
        else if (!string.IsNullOrEmpty(SelectedDesktopAbsoluteParsing))
        {
            SelectInitialPath(dialog, SelectedDesktopAbsoluteParsing);
        }
        if (!string.IsNullOrWhiteSpace(Title))
        {
            dialog.SetTitle(Title);
        }
        dialog.SetOptions(FOS.FOS_PICKFOLDERS | FOS.FOS_ALLNONSTORAGEITEMS);
        uint hr = dialog.Show(owner != null ? owner.Handle : IntPtr.Zero);
        if (hr == ERROR_CANCELLED)
            return DialogResult.Cancel;
        if (hr != 0)
            return DialogResult.Abort;
        dialog.GetResult(out IShellItem result);
        SelectedPath = GetDisplayName(result, SIGDN.SIGDN_FILESYSPATH);
        SelectedNormalDisplay = GetDisplayName(result, SIGDN.SIGDN_NORMALDISPLAY);
        SelectedDesktopAbsoluteParsing = GetDisplayName(result, SIGDN.SIGDN_DESKTOPABSOLUTEPARSING);
        SelectedUrl = GetDisplayName(result, SIGDN.SIGDN_URL);
        return DialogResult.OK;
    }
    private static string GetDisplayName(IShellItem item, SIGDN sigdnName)
    {
        item.GetDisplayName(sigdnName, out var ptr);
        var name = Marshal.PtrToStringUni(ptr);
        Marshal.FreeCoTaskMem(ptr);
        return name;
    }
    private void SelectInitialPath(IFileOpenDialog dialog, string path)
    {
        uint atts = 0;
        IntPtr idl = IntPtr.Zero;
        if (SHILCreateFromPath(path, out idl, ref atts) == 0)
        {
            if (SHCreateShellItem(IntPtr.Zero, IntPtr.Zero, idl, out IShellItem initial) == 0)
            {
                dialog.SetFolder(initial);
            }
            Marshal.FreeCoTaskMem(idl);
        }
    }
    [DllImport("shell32.dll", CharSet = CharSet.Unicode)]
    private static extern int SHILCreateFromPath(string pszPath, out IntPtr ppIdl, ref uint rgflnOut);
    [DllImport("shell32.dll")]
    private static extern int SHCreateShellItem(IntPtr pidlParent, IntPtr psfParent, IntPtr pidl, out IShellItem ppsi);
    private const uint ERROR_CANCELLED = 0x800704C7;
    [ComImport]
    [Guid("DC1C5A9C-E88A-4dde-A5A1-60F82A20AEF7")]
    private class FileOpenDialog
    {
    }
    [Guid("42f85136-db7e-439c-85f1-e4075d135fc8")]
    [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
    private interface IFileOpenDialog
    {
        [PreserveSig]
        uint Show(IntPtr parent); // IModalWindow
        void SetFileTypes();  // not fully defined
        void SetFileTypeIndex(uint iFileType);
        void GetFileTypeIndex(out uint piFileType);
        void Advise(); // not fully defined
        void Unadvise();
        void SetOptions(FOS fos);
        void GetOptions(out FOS pfos);
        void SetDefaultFolder(IShellItem psi);
        void SetFolder(IShellItem psi);
        void GetFolder(out IShellItem ppsi);
        void GetCurrentSelection(out IShellItem ppsi);
        void SetFileName([MarshalAs(UnmanagedType.LPWStr)] string pszName);
        void GetFileName(out IntPtr pszName);
        void SetTitle([MarshalAs(UnmanagedType.LPWStr)] string pszTitle);
        void SetOkButtonLabel([MarshalAs(UnmanagedType.LPWStr)] string pszText);
        void SetFileNameLabel([MarshalAs(UnmanagedType.LPWStr)] string pszLabel);
        void GetResult(out IShellItem ppsi);
        void AddPlace(IShellItem psi, int alignment);
        void SetDefaultExtension([MarshalAs(UnmanagedType.LPWStr)] string pszDefaultExtension);
        void Close(int hr);
        void SetClientGuid();  // not fully defined
        void ClearClientData();
        void SetFilter([MarshalAs(UnmanagedType.Interface)] IntPtr pFilter);
        void GetResults([MarshalAs(UnmanagedType.Interface)] out IntPtr ppenum); // not fully defined
        void GetSelectedItems([MarshalAs(UnmanagedType.Interface)] out IntPtr ppsai); // not fully defined
    }
    [Guid("43826D1E-E718-42EE-BC55-A1E261C37BFE")]
    [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
    private interface IShellItem
    {
        void BindToHandler(); // not fully defined
        void GetParent(); // not fully defined
        [PreserveSig]
        int GetDisplayName(SIGDN sigdnName, out IntPtr ppszName);
        void GetAttributes();  // not fully defined
        void Compare();  // not fully defined
    }
    // https://msdn.microsoft.com/en-us/library/windows/desktop/bb762544.aspx
    private enum SIGDN : uint
    {
        SIGDN_DESKTOPABSOLUTEEDITING = 0x8004c000,
        SIGDN_DESKTOPABSOLUTEPARSING = 0x80028000,
        SIGDN_FILESYSPATH = 0x80058000,
        SIGDN_NORMALDISPLAY = 0,
        SIGDN_PARENTRELATIVE = 0x80080001,
        SIGDN_PARENTRELATIVEEDITING = 0x80031001,
        SIGDN_PARENTRELATIVEFORADDRESSBAR = 0x8007c001,
        SIGDN_PARENTRELATIVEPARSING = 0x80018001,
        SIGDN_URL = 0x80068000
    }
    // https://msdn.microsoft.com/en-us/library/windows/desktop/dn457282.aspx
    [Flags]
    private enum FOS
    {
        FOS_ALLNONSTORAGEITEMS = 0x80,
        FOS_ALLOWMULTISELECT = 0x200,
        FOS_CREATEPROMPT = 0x2000,
        FOS_DEFAULTNOMINIMODE = 0x20000000,
        FOS_DONTADDTORECENT = 0x2000000,
        FOS_FILEMUSTEXIST = 0x1000,
        FOS_FORCEFILESYSTEM = 0x40,
        FOS_FORCESHOWHIDDEN = 0x10000000,
        FOS_HIDEMRUPLACES = 0x20000,
        FOS_HIDEPINNEDPLACES = 0x40000,
        FOS_NOCHANGEDIR = 8,
        FOS_NODEREFERENCELINKS = 0x100000,
        FOS_NOREADONLYRETURN = 0x8000,
        FOS_NOTESTFILECREATE = 0x10000,
        FOS_NOVALIDATE = 0x100,
        FOS_OVERWRITEPROMPT = 2,
        FOS_PATHMUSTEXIST = 0x800,
        FOS_PICKFOLDERS = 0x20,
        FOS_SHAREAWARE = 0x4000,
        FOS_STRICTFILETYPES = 4
    }
}

文档说:

如果用户选择一个文件夹没有物理路径(例如,"我的计算机"),对话框上的OK按钮将被禁用。

你问:

有什么方法可以启用我的电脑的OK按钮吗?

有可能:使用函数:

   [StructLayout(LayoutKind.Sequential)]
    public struct BROWSEINFO
    {
        public IntPtr hwndOwner;
        public IntPtr pidlRoot;
        //public IntPtr pszDisplayName;
        public string pszDisplayName;
        public string lpszTitle;
        public uint ulFlags;
        public BrowseCallbackProc lpfn;
        public IntPtr lParam;
        public int iImage;
     }
[DllImport("shell32.dll")]
public static extern IntPtr SHBrowseForFolder(ref BROWSEINFO lpbi);
 [DllImport("shell32.dll")]
    [return: MarshalAs(UnmanagedType.Bool)]
    public static extern bool SHGetPathFromIDListW(IntPtr pidl, [MarshalAs(UnmanagedType.LPTStr)] StringBuilder pszPath);
   [DllImport("shell32.dll")]
    [return: MarshalAs(UnmanagedType.Bool)]
    public static extern bool SHGetPathFromIDListW(IntPtr pidl, [MarshalAs(UnmanagedType.LPTStr)] StringBuilder pszPath);

回呼可以设置为:

 private int _callback(IntPtr hDlg, int msg, IntPtr lParam, IntPtr lpData)
    {
        switch (msg)
        {
            case 2://BFFM_SELCHANGED:
                StringBuilder sb1 = new StringBuilder();
                Win32.SHGetPathFromIDListW((IntPtr)lParam, sb);
                Win32.EnableWindow(Win32.GetDlgItem(hDlg, CtlIds.IDOK), true);
                break;
        }
        return 0;
    }

调用:

 BROWSEINFO bi = new Win32.BROWSEINFO();
    bi.lpfn = new Win32.BrowseCallbackProc(callback);

    string selected = SHBrowseForFolder(ref bi);

OK将被点击。唯一的问题,我还没有发现,是如何确保我的计算机被选中。

部分资料来源:http://www.codeproject.com/Articles/159352/FolderBrowserDialogEx-A-C-customization-of-FolderB