はじめに
自作アプリケーションを開発する上で、ファイルを選択する or ディレクトリを選択する場面が多くある。
たかがコントロール3つを配置し
・入力対象の概要を書く:Label
・ダイアログで選択したファイル/ディレクトリを出力:TextBox
・OpenFileDialog/FolderBrowserDialogを呼ぶ:Button
これらを毎回毎回書くのがとても面倒臭くなった
※コピペツールを使うという手もあるが、それでは意味がない
無いのならば、作ってしまえ!
環境
実施環境
・Windows 10 Professional
・Visual Studio 2022 Community Ver.17.9.0
・C#
・.NET 8(5以降ならば動く筈)
ユーザーコントロールに設置するのは以下の通り
・lblTitle:Label
・txtInput:TextBox
・btnSelect:Button
// デザイナー側は省略
using System.ComponentModel;
using System.Reflection;
using System.IO;
namespace InputParts
{
/// <summary>ボタン押下時の挙動</summary>
public enum SelectorType
{
File = 0, // OpenFileDialog
Directory = 1, // FolderBrowserDialog
}
public partial class FileSelector : UserControl
{
// 固定値及びメンバ変数宣言
private const string CATEGORY_NAME = "CustomProperty"; // プロパティウィンドウのグループ分け、後述
private SelectorType _selectorType = SelectorType.File; //
private string _dialogTitle = "ファイルを選択"; // ダイアログタイトル
private string _returnPath = string.Empty; // 選択アイテムのフルパス
private string _initialDir = string.Empty; // 初期ディレクトリ
private string _filter = "すべてのファイル(*.*)|*.*"; // SelectorType.Directoryの時は無視
public FileSelector()
{
InitializeComponent();
}
#region デザイナー公開プロパティ
/// <summary>ラベル</summary>
[Browsable(true)]
[Category(CATEGORY_NAME)]
[Description("ラベルに表示する文字列")]
public string Title{ get => lblTitle.Text; set => lblTitle.Text = value; }
/// <summary>テキストボックス</summary>
[Browsable(true)]
[Category(CATEGORY_NAME)]
[Description("テキストボックスの内容を読取専用にするか")]
public bool ReadOnly{ get => txtInput.ReadOnly; set => txtInput.ReadOnly = value; }
[Browsable(false)]
public string SelectedPath{ get => txtInput.Text; set => txtInput.Text = value; }
/// <summary>
/// ボタン
/// </summary>
[Browsable(true)]
[Category(CATEGORY_NAME)]
[Description("選択タイプ")]
public SelectorType SelectType{ get => _selectorType; set => _selectorType = value; }
[Browsable(true)]
[Category(CATEGORY_NAME)]
[Description("ダイアログタイトルを指定")]
public string DialogTitle{ get => _dialogTitle; set => _dialogTitle = value; }
#endregion
#region デザイナー非公開プロパティ
/// <summary>
/// 表示フィルタ
/// Example:"テキストファイル(*.txt)|*.txt|すべてのファイル(*.*)|*.*";
/// </summary>
[Browsable(false)]
public string Filter{ get => _filter; set => _filter = value; }
#endregion
#region イベント
private void btnSelect_Click(object sender, EventArgs e)
{
var FullPath = string.Empty;
switch(this.SelectType)
{
case SelectorType.File:
FullPath = DialogSelectFile(SelectedPath);
break;
case SelectorType.Directory:
FullPath = DialogSelectDirectory(SelectedPath);
break;
}
if(FullPath == string.Empty) return; // キャンセル時は無視
this.SelectedPath = FullPath;
}
#endregion
#region ダイアログ表示
/// <summary>ファイル選択ダイアログ表示</summary>
private string DialogSelectFile(string CurrentValue)
{
DialogPathInitialize(ref _returnPath, ref _initialDir, CurrentValue);
using(var OFD = new OpenFileDialog())
{
OFD.Title = this.DialogTitle;
OFD.Filter = this.Filter;
OFD.FilterIndex = 0;
OFD.InitialDirectory = _initialDir;
OFD.Multiselect = false;
OFD.CheckFileExists = true;
OFD.CheckPathExists = true;
OFD.FileName = CurrentValue;
if(OFD.ShowDialog() == DialogResult.OK){ _returnPath = OFD.FileName; }
}
return _returnPath;
}
/// <summary>フォルダ選択ダイアログ表示</summary>
private string DialogSelectDirectory(string CurrentValue)
{
DialogPathInitialize(ref _returnPath, ref _initialDir, CurrentValue);
using(var FBD = new FolderBrowserDialog())
{
//FBD.Description = this.DialogTitle;
FBD.InitialDirectory = _initialDir;
if(FBD.ShowDialog() == DialogResult.OK)
{
//_returnPath = FBD.SelectedPath;
_returnPath = $"{FBD.SelectedPath}\\";
}
}
return _returnPath;
}
/// <summary>初期化の記述が煩雑になるので一括化</summary>
private void DialogPathInitialize(ref string SelPath, ref string InitDir, string CurVal)
{
SelPath = string.Empty; InitDir = CurVal;
if(string.IsNullOrEmpty(InitDir)){ InitDir = GetExecLocalDir(); }
}
#endregion
#region 汎用
/// <summary>実行ファイルの親ディレクトリパスを返す</summary>
#pragma warning disable CS8602
private string GetExecLocalDir() => Directory.GetParent(GetExecFullPath()).FullName;
#pragma warning restore CS8602
/// <summary>実行ファイルパスを返す</summary>
private string GetExecFullPath() => Assembly.GetExecutingAssembly().Location;
#endregion
}
}
プロパティ
コード内で「CustomProperty」とした理由は独自プロパティである事を明確化する為
心残り
FolderBrowserDialogの「フォルダーの選択(ダイアログタイトル)」を変えたかったが諦めた。
参考元に記載した「C# Winforms フォルダ選択ダイアログ」の方法で解決しそうだが…
SHFolderForBrowser(API)を使うのでは手軽とは程遠く、且つ労力に見合わないので断念
参考元
※2024/11/07 今更誤字を見つけたので修正