
OpenFileDialog() はかなり便利ですが、なぜかデフォルトのフォルダ選択画面は使いづらい。

これを回避する方法を2つ紹介します。
Windows API Codepack を使う
Windows7 以降であれば Windows API Codepack を使うことで CommonOpenFileDialog が使えます。
CommonOpenFileDialog dialog = new CommonOpenFileDialog();
dialog.IsFolderPicker = true;
dialog.Title = "保存先のフォルダを選択してください";
// はじめに表示されるフォルダ
string inipath = "d:\\";
string dirname = null;
dialog.InitialDirectory = inipath;
dialog.DefaultDirectory = inipath;
dialog.DefaultFileName = inipath;
if (dialog.ShowDialog() == CommonFileDialogResult.Ok)
{
dirname = dialog.FileName;
}
dialog.Dispose();
ただ、ファイルダイアログのためだけにこれを入れるのは…と感じるかもしれません。
ゴリゴリカスタマイズ
もはや標準機能を使っているとは言えませんが、以下のクラスを使うことで Windows API Codepack を入れずにフォルダダイアログを表示させることができます。namespace は好みに変更してください。
using System;
using System.Runtime.InteropServices;
namespace App
{
/// <summary>
///
/// </summary>
public class FolderSelectDialog
{
/// <summary>
///
/// </summary>
public string Path { get; set; }
/// <summary>
///
/// </summary>
public string Title { get; set; }
/// <summary>
///
/// </summary>
public System.Windows.Forms.DialogResult ShowDialog()
{
return ShowDialog(IntPtr.Zero);
}
/// <summary>
///
/// </summary>
public System.Windows.Forms.DialogResult ShowDialog(System.Windows.Forms.IWin32Window owner)
{
return ShowDialog(owner.Handle);
}
/// <summary>
///
/// </summary>
public System.Windows.Forms.DialogResult ShowDialog(IntPtr owner)
{
var dlg = new FileOpenDialogInternal() as IFileOpenDialog;
try
{
dlg.SetOptions(FOS.FOS_PICKFOLDERS | FOS.FOS_FORCEFILESYSTEM);
IShellItem item;
if (!string.IsNullOrEmpty(this.Path))
{
IntPtr idl;
uint atts = 0;
if (NativeMethods.SHILCreateFromPath(this.Path, out idl, ref atts) == 0)
{
if (NativeMethods.SHCreateShellItem(IntPtr.Zero, IntPtr.Zero, idl, out item) == 0)
{
dlg.SetFolder(item);
}
}
}
if (!string.IsNullOrEmpty(this.Title))
dlg.SetTitle(this.Title);
var hr = dlg.Show(owner);
if (hr.Equals(NativeMethods.ERROR_CANCELLED))
return System.Windows.Forms.DialogResult.Cancel;
if (!hr.Equals(0))
return System.Windows.Forms.DialogResult.Abort;
dlg.GetResult(out item);
string outputPath;
item.GetDisplayName(SIGDN.SIGDN_FILESYSPATH, out outputPath);
this.Path = outputPath;
return System.Windows.Forms.DialogResult.OK;
}
finally
{
Marshal.FinalReleaseComObject(dlg);
}
}
[ComImport]
[Guid("DC1C5A9C-E88A-4dde-A5A1-60F82A20AEF7")]
private class FileOpenDialogInternal
{
}
// not fully defined と記載された宣言は、支障ない範囲で端折ってあります。
[ComImport]
[Guid("42f85136-db7e-439c-85f1-e4075d135fc8")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
private interface IFileOpenDialog
{
[PreserveSig]
UInt32 Show([In] IntPtr hwndParent);
void SetFileTypes(); // not fully defined
void SetFileTypeIndex(); // not fully defined
void GetFileTypeIndex(); // not fully defined
void Advise(); // not fully defined
void Unadvise();
void SetOptions([In] FOS fos);
void GetOptions(); // not fully defined
void SetDefaultFolder(); // not fully defined
void SetFolder(IShellItem psi);
void GetFolder(); // not fully defined
void GetCurrentSelection(); // not fully defined
void SetFileName(); // not fully defined
void GetFileName(); // not fully defined
void SetTitle([In, MarshalAs(UnmanagedType.LPWStr)] string pszTitle);
void SetOkButtonLabel(); // not fully defined
void SetFileNameLabel(); // not fully defined
void GetResult(out IShellItem ppsi);
void AddPlace(); // not fully defined
void SetDefaultExtension(); // not fully defined
void Close(); // not fully defined
void SetClientGuid(); // not fully defined
void ClearClientData();
void SetFilter(); // not fully defined
void GetResults(); // not fully defined
void GetSelectedItems(); // 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 // not fully defined
{
SIGDN_FILESYSPATH = 0x80058000,
}
[Flags]
private enum FOS // not fully defined
{
FOS_FORCEFILESYSTEM = 0x40,
FOS_PICKFOLDERS = 0x20,
}
private class NativeMethods
{
[DllImport("shell32.dll")]
public static extern int SHILCreateFromPath([MarshalAs(UnmanagedType.LPWStr)] string pszPath, out IntPtr ppIdl, ref uint rgflnOut);
[DllImport("shell32.dll")]
public static extern int SHCreateShellItem(IntPtr pidlParent, IntPtr psfParent, IntPtr pidl, out IShellItem ppsi);
public const uint ERROR_CANCELLED = 0x800704C7;
}
}
}
使用方法
// ウィンドウフォームから呼び出す
string dirname = null;
var dialog = new FolderSelectDialog
{
Path = "D:\\",
Title = "フォルダを選択してください"
};
if (dialog.ShowDialog(Handle) == DialogResult.OK)
{
dirname = dialog.Path;
}





