LoginSignup
2
3

ファイル/ディレクトリ選択をユーザーコントロール化

Last updated at Posted at 2024-02-24

はじめに

自作アプリケーションを開発する上で、ファイルを選択する or ディレクトリを選択する場面が多くある。

例)
image.png

たかがコントロール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

FileSelector.cs(ユーザーコントロール)
// デザイナー側は省略
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        
    }
}

ビルド後、以下の様になっていればOK
image.png

プロパティ

コード内で「CustomProperty」とした理由は独自プロパティである事を明確化する為
image.png

心残り

FolderBrowserDialogの「フォルダーの選択(ダイアログタイトル)」を変えたかったが諦めた。
参考元に記載した「C# Winforms フォルダ選択ダイアログ」の方法で解決しそうだが…
SHFolderForBrowser(API)を使うでのは手軽とは程遠く、且つ労力に見合わないので断念

参考元

FolderBrowserDialog.cs

2
3
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
3