19

重複質問ではありませんのでご注意ください。

以下の画像のように、PropertyGrid から詳細な FolderBrowser を表示する方法 (省略記号を持つフィールド/プロパティから ...) 詳細フォルダ ブラウザ

使用する

[EditorAttribute(typeof(System.Windows.Forms.Design.FileNameEditor), typeof(System.Drawing.Design.UITypeEditor))]

[EditorAttribute(typeof(System.Windows.Forms.Design.FolderNameEditor), typeof(System.Drawing.Design.UITypeEditor))]

ミニマルなフォルダーブラウザーを取得します

最小限のフォルダー ブラウザー

4

3 に答える 3

45

以下は、Vista フォルダー ブラウザーを使用できるようにするカスタム UITypeEditor です。

ここに画像の説明を入力

他のエディターと同じように使用できます。

[EditorAttribute(typeof(FolderNameEditor2), typeof(System.Drawing.Design.UITypeEditor))]

これは、この機会に作成したカスタム FolderBrowser2 クラスに依存しています。もちろん、これは Windows Vista 以降でのみ機能します。以前の Windows バージョンでは、単純なフォルダー ブラウザー以外にフォルダー ブラウザーはありませんでした。

public class FolderNameEditor2 : UITypeEditor
{
    public override UITypeEditorEditStyle GetEditStyle(ITypeDescriptorContext context)
    {
        return UITypeEditorEditStyle.Modal;
    }

    public override object EditValue(ITypeDescriptorContext context, IServiceProvider provider, object value)
    {
        FolderBrowser2 browser = new FolderBrowser2();
        if (value != null)
        {
            browser.DirectoryPath = string.Format("{0}", value);
        }

        if (browser.ShowDialog(null) == DialogResult.OK)
            return browser.DirectoryPath;

        return value;
    }
}

public class FolderBrowser2
{
    public string DirectoryPath { get; set; }

    public DialogResult ShowDialog(IWin32Window owner)
    {
        IntPtr hwndOwner = owner != null ? owner.Handle : GetActiveWindow();

        IFileOpenDialog dialog = (IFileOpenDialog)new FileOpenDialog();
        try
        {
            IShellItem item;
            if (!string.IsNullOrEmpty(DirectoryPath))
            {
                IntPtr idl;
                uint atts = 0;
                if (SHILCreateFromPath(DirectoryPath, out idl, ref atts) == 0)
                {
                    if (SHCreateShellItem(IntPtr.Zero, IntPtr.Zero, idl, out item) == 0)
                    {
                        dialog.SetFolder(item);
                    }
                    Marshal.FreeCoTaskMem(idl);
                }
            }
            dialog.SetOptions(FOS.FOS_PICKFOLDERS | FOS.FOS_FORCEFILESYSTEM);
            uint hr = dialog.Show(hwndOwner);
            if (hr == ERROR_CANCELLED)
                return DialogResult.Cancel;

            if (hr != 0)
                return DialogResult.Abort;

            dialog.GetResult(out item);
            string path;
            item.GetDisplayName(SIGDN.SIGDN_FILESYSPATH, out path);
            DirectoryPath = path;
            return DialogResult.OK;
        }
        finally
        {
            Marshal.ReleaseComObject(dialog);
        }
    }

    [DllImport("shell32.dll")]
    private static extern int SHILCreateFromPath([MarshalAs(UnmanagedType.LPWStr)] 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);

    [DllImport("user32.dll")]
    private static extern IntPtr GetActiveWindow();

    private const uint ERROR_CANCELLED = 0x800704C7;

    [ComImport]
    [Guid("DC1C5A9C-E88A-4dde-A5A1-60F82A20AEF7")]
    private class FileOpenDialog
    {
    }

    [ComImport]
    [Guid("42f85136-db7e-439c-85f1-e4075d135fc8")]
    [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
    private interface IFileOpenDialog
    {
        [PreserveSig]
        uint Show([In] IntPtr parent); // IModalWindow
        void SetFileTypes();  // not fully defined
        void SetFileTypeIndex([In] uint iFileType);
        void GetFileTypeIndex(out uint piFileType);
        void Advise(); // not fully defined
        void Unadvise();
        void SetOptions([In] 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([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(out IShellItem ppsi);
        void AddPlace(IShellItem psi, int alignment);
        void SetDefaultExtension([In, 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
    }

    [ComImport]
    [Guid("43826D1E-E718-42EE-BC55-A1E261C37BFE")]
    [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
    private interface IShellItem
    {
        void BindToHandler(); // not fully defined
        void GetParent(); // not fully defined
        void GetDisplayName([In] SIGDN sigdnName, [MarshalAs(UnmanagedType.LPWStr)] out string ppszName);
        void GetAttributes();  // not fully defined
        void Compare();  // not fully defined
    }

    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
    }

    [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
    }
}
于 2013-03-13T13:40:27.697 に答える
4

相互運用機能を直接使用せずに、Simon Mourierの回答と同じ仕事をするソリューションを次に示します(.Netがそれを処理します)。十分に高い Windows バージョンでない場合、Vista 以前のダイアログにフォールバックする追加機能があります。Windows 7、8、10 以降で動作するはずです (理論上)。

大きな注意点が 1 つあります。Microsoft は内部クラスを自由に変更できます。これはリフレクションを使用してスコープ ルールを無効にし、このアクションはサポートされていないため (Microsoft は公開されているものにのみ関心があります)、Microsoft が変更を加えた場合、このコードは壊れる可能性があります。

using System;
using System.Reflection;
using System.Windows.Forms;

namespace ErikE.Shuriken {
    /// <summary>
    /// Present the Windows Vista-style open file dialog to select a folder. Fall back for older Windows Versions
    /// </summary>
    public class FolderSelectDialog {
        private string _initialDirectory;
        private string _title;
        private string _fileName = "";

        public string InitialDirectory {
            get { return string.IsNullOrEmpty(_initialDirectory) ? Environment.CurrentDirectory : _initialDirectory; }
            set { _initialDirectory = value; }
        }
        public string Title {
            get { return _title ?? "Select a folder"; }
            set { _title = value; }
        }
        public string FileName { get { return _fileName; } }

        public bool Show() { return Show(IntPtr.Zero); }

        /// <param name="hWndOwner">Handle of the control or window to be the parent of the file dialog</param>
        /// <returns>true if the user clicks OK</returns>
        public bool Show(IntPtr hWndOwner) {
            var result = Environment.OSVersion.Version.Major >= 6
                ? VistaDialog.Show(hWndOwner, InitialDirectory, Title)
                : ShowXpDialog(hWndOwner, InitialDirectory, Title);
            _fileName = result.FileName;
            return result.Result;
        }

        private struct ShowDialogResult {
            public bool Result { get; set; }
            public string FileName { get; set; }
        }

        private static ShowDialogResult ShowXpDialog(IntPtr ownerHandle, string initialDirectory, string title) {
            var folderBrowserDialog = new FolderBrowserDialog {
                Description = title,
                SelectedPath = initialDirectory,
                ShowNewFolderButton = false
            };
            var dialogResult = new ShowDialogResult();
            if (folderBrowserDialog.ShowDialog(new WindowWrapper(ownerHandle)) == DialogResult.OK) {
                dialogResult.Result = true;
                dialogResult.FileName = folderBrowserDialog.SelectedPath;
            }
            return dialogResult;
        }

        private static class VistaDialog {
            private const string c_foldersFilter = "Folders|\n";

            private const BindingFlags c_flags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;
            private readonly static Assembly s_windowsFormsAssembly = typeof(FileDialog).Assembly;
            private readonly static Type s_iFileDialogType = s_windowsFormsAssembly.GetType("System.Windows.Forms.FileDialogNative+IFileDialog");
            private readonly static MethodInfo s_createVistaDialogMethodInfo = typeof(OpenFileDialog).GetMethod("CreateVistaDialog", c_flags);
            private readonly static MethodInfo s_onBeforeVistaDialogMethodInfo = typeof(OpenFileDialog).GetMethod("OnBeforeVistaDialog", c_flags);
            private readonly static MethodInfo s_getOptionsMethodInfo = typeof(FileDialog).GetMethod("GetOptions", c_flags);
            private readonly static MethodInfo s_setOptionsMethodInfo = s_iFileDialogType.GetMethod("SetOptions", c_flags);
            private readonly static uint s_fosPickFoldersBitFlag = (uint) s_windowsFormsAssembly
                .GetType("System.Windows.Forms.FileDialogNative+FOS")
                .GetField("FOS_PICKFOLDERS")
                .GetValue(null);
            private readonly static ConstructorInfo s_vistaDialogEventsConstructorInfo = s_windowsFormsAssembly
                .GetType("System.Windows.Forms.FileDialog+VistaDialogEvents")
                .GetConstructor(c_flags, null, new[] { typeof(FileDialog) }, null);
            private readonly static MethodInfo s_adviseMethodInfo = s_iFileDialogType.GetMethod("Advise");
            private readonly static MethodInfo s_unAdviseMethodInfo = s_iFileDialogType.GetMethod("Unadvise");
            private readonly static MethodInfo s_showMethodInfo = s_iFileDialogType.GetMethod("Show");

            public static ShowDialogResult Show(IntPtr ownerHandle, string initialDirectory, string title) {
                var openFileDialog = new OpenFileDialog {
                    AddExtension = false,
                    CheckFileExists = false,
                    DereferenceLinks = true,
                    Filter = c_foldersFilter,
                    InitialDirectory = initialDirectory,
                    Multiselect = false,
                    Title = title
                };

                var iFileDialog = s_createVistaDialogMethodInfo.Invoke(openFileDialog, new object[] { });
                s_onBeforeVistaDialogMethodInfo.Invoke(openFileDialog, new[] { iFileDialog });
                s_setOptionsMethodInfo.Invoke(iFileDialog, new object[] { (uint) s_getOptionsMethodInfo.Invoke(openFileDialog, new object[] { }) | s_fosPickFoldersBitFlag });
                var adviseParametersWithOutputConnectionToken = new[] { s_vistaDialogEventsConstructorInfo.Invoke(new object[] { openFileDialog }), 0U };
                s_adviseMethodInfo.Invoke(iFileDialog, adviseParametersWithOutputConnectionToken);

                try {
                    int retVal = (int) s_showMethodInfo.Invoke(iFileDialog, new object[] { ownerHandle });
                    return new ShowDialogResult {
                        Result = retVal == 0,
                        FileName = openFileDialog.FileName
                    };
                }
                finally {
                    s_unAdviseMethodInfo.Invoke(iFileDialog, new[] { adviseParametersWithOutputConnectionToken[1] });
                }
            }
        }

        // Wrap an IWin32Window around an IntPtr
        private class WindowWrapper : IWin32Window {
            private readonly IntPtr _handle;
            public WindowWrapper(IntPtr handle) { _handle = handle; }
            public IntPtr Handle { get { return _handle; } }
        }
    }
}

これは、 lyquidity.comの Bill Seddon による.NET Win 7 スタイルのフォルダー選択ダイアログのクリーンアップ バージョンとして開発しました(私は何の関係もありません)。彼のソリューションには、この焦点を絞った目的には必要のない追加の Reflection クラスが必要であり、例外ベースのフロー制御を使用し、リフレクション呼び出しの結果をキャッシュしないため、私は自分で作成しました。ネストされた静的クラスは、メソッドが呼び出されない場合、その静的リフレクション変数が設定されないようにするためのものであることに注意してください。VistaDialogShow

Windows フォームでは次のように使用されます。

var dialog = new FolderSelectDialog {
    InitialDirectory = musicFolderTextBox.Text
    Title = "Select a folder to import music from"
};
if (dialog.Show(Handle)) {
    musicFolderTextBox.Text = dialog.FileName;
}

もちろん、そのオプションとそれが公開するプロパティをいじることができます。たとえば、Vista スタイルのダイアログで複数選択が可能です。

于 2015-11-20T21:09:40.493 に答える
0

ErikEによるコードは .NETCore3.1では動作しません(申し訳ありませんが、コメントを投稿するには評判が不十分です...)。この行は変更する必要があります:

    private readonly static MethodInfo s_onBeforeVistaDialogMethodInfo = typeof(OpenFileDialog).GetMethod("OnBeforeVistaDialog", c_flags);

次のようにする必要があります。

    private readonly static MethodInfo s_onBeforeVistaDialogMethodInfo = typeof(FileDialog).GetMethod("OnBeforeVistaDialog", c_flags);

メソッド「OnBeforeVistaDialog」は、.NETFramework 4.6 と .NETCore 3.1 の両方でタイプ「FileDialog」で定義されていますが、.NETCore ではリフレクションの動作が異なるようです。

.NET 6では、さらに内部的な変更がありました。インターフェイス "IFileDialog" がアセンブリ "System.Windows.Forms.Primitives" に移動され、内部クラス "Interop"、ネストされたクラス "Shell32" にネストされました。Enum "FOS" についても同様で、値の名前も変更されています。

リフレクション部分の完全なコードは次のとおりです。

      private const BindingFlags c_flags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;
      private readonly static Assembly s_windowsFormsAssembly = typeof(FileDialog).Assembly;
      private readonly static Assembly s_windowsFormsPrimitivesAssembly = Assembly.Load("System.Windows.Forms.Primitives");
      private readonly static Type s_iFileDialogType = s_windowsFormsPrimitivesAssembly.GetType("Interop+Shell32+IFileDialog");
      private readonly static MethodInfo s_createVistaDialogMethodInfo = typeof(OpenFileDialog).GetMethod("CreateVistaDialog", c_flags);
      private readonly static MethodInfo s_onBeforeVistaDialogMethodInfo = typeof(FileDialog).GetMethod("OnBeforeVistaDialog", c_flags);

      private readonly static MethodInfo s_getOptionsMethodInfo = typeof(FileDialog).GetMethod("GetOptions", c_flags);
      private readonly static MethodInfo s_setOptionsMethodInfo = s_iFileDialogType.GetMethod("SetOptions", c_flags);
      private readonly static uint s_fosPickFoldersBitFlag = (uint)s_windowsFormsPrimitivesAssembly
          .GetType("Interop+Shell32+FOS")
          .GetField("PICKFOLDERS")
          .GetValue(null);
      private readonly static ConstructorInfo s_vistaDialogEventsConstructorInfo = s_windowsFormsAssembly
          .GetType("System.Windows.Forms.FileDialog+VistaDialogEvents")
          .GetConstructor(c_flags, null, new[] { typeof(FileDialog) }, null);
      private readonly static MethodInfo s_adviseMethodInfo = s_iFileDialogType.GetMethod("Advise");
      private readonly static MethodInfo s_unAdviseMethodInfo = s_iFileDialogType.GetMethod("Unadvise");
      private readonly static MethodInfo s_showMethodInfo = s_iFileDialogType.GetMethod("Show");
于 2020-05-22T15:37:11.733 に答える