【问题标题】:C# How to allow selection of My Computer with folderBrowserDialogC#如何允许使用folderBrowserDialog选择我的电脑
【发布时间】:2013-05-14 09:29:32
【问题描述】:

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

有什么办法可以让My ComputerBrowse Dialog中选择?

【问题讨论】:

  • My Computer 不是文件夹。
  • stackoverflow.com/questions/705409/…,我的电脑不是文件夹。我不明白你要选择它的原因。你不能在那里保存任何东西。
  • @Androidy 在我的应用程序中,我希望允许用户选择我的计算机并且应用程序相应地执行商务逻辑。
  • @SLaks 我没有说 我的电脑 是一个文件夹。是吗?

标签: c# visual-studio-2008 folderbrowserdialog


【解决方案1】:

我的电脑(或更新的 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 属性中定义的其他文件夹的特殊值。 对于“我的电脑”,该值将始终为“::{20D04FE0-3AEA-1069-A2D8-08002B30309D}”。

这个特殊的语法在这里正式定义:Specifying a Namespace Extension's Location

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
    }
}

【讨论】:

    【解决方案2】:

    documentation 说:

    如果用户选择的文件夹 没有物理路径(例如,我的电脑), 对话框上的确定按钮将被禁用。

    你问:

    有什么方法可以启用“我的电脑”的“确定”按钮吗?

    没有。

    【讨论】:

      【解决方案3】:

      有一种可能: 使用函数:

         [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);
      

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

      一些来源来自: http://www.codeproject.com/Articles/159352/FolderBrowserDialogEx-A-C-customization-of-FolderB

      【讨论】:

        猜你喜欢
        • 2014-06-25
        • 1970-01-01
        • 1970-01-01
        • 2012-03-08
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-10-20
        相关资源
        最近更新 更多