動くコード図鑑技術記事現場の渡り方キャリア論すべての記事About
技術記事

C#でOpenFileDialogが継承できない?それならフィールドに持っちゃえばいいんじゃね?

バイブス父さん
現役の業務SE
2020年10月29日8 min read
C#でOpenFileDialogが継承できない?それならフィールドに持っちゃえばいいんじゃね?

みなさんこんにちは!ヒロポンです!

毎日張り切ってプログラミングの勉強してますか!!!!!

私は張り切ってやっています!!!!

さて今回はC#使いなら一度は使ったことがあるクラスOpenFileDialogについて書いていきたいと思います!

OpenFileDialogは継承できない!

💡 OpenFileDialog をフィールドで持つ場合のパターンは別記事 OpenFileDialog をフォームのフィールドにする方法 で書いてます。

このOpenFileDialogって初期設定が結構めんどくさい!!!

だから継承してCustomOpenFileDoalog的なものを作りたいがOpenFileDialogは継承が許されてません!

こんな感じでコンパイルエラーが出るので、コードの中身を見てみるとsealedクラスとして定義されています。

となるとどうあがいても継承はできません。

そこで、思ったのがOpenFileDialogをフィールドに持ったCustomOpenFileDialogを作ればよいのでは?ということ

実際にCustomOpenFileDialogを作ってみる。

とりあえずこんな感じで作ってみた。

実際にCustomOpenFileDialogを作ってみる。 (csharp)#8467efcda984
using System;
using System.IO;
using System.Windows.Forms;
 
namespace CustomOpenFileDialog
{
    public class CustomOpenFileDialog
    {
        private OpenFileDialog _ofd;
    public string FileName => _ofd.FileName;
 
    public CustomOpenFileDialog()
    {
        _ofd = new OpenFileDialog();
        _ofd.InitialDirectory = Environment.GetFolderPath(Environment.SpecialFolder.Desktop);
        _ofd.Title = "ファイル選択";
        _ofd.Filter = "htmlファイル(*.html)| *.html";
    }
 
    public DialogResult ShowDialog()
    {
        return _ofd.ShowDialog();
    }
}
▸ この snippet は実行結果未収録
▸ 実行結果は未収録です

}

画面はこんな感じで用意して

テキストボックス右側のボタンを押したらダイアログが立ち上がる仕組み

実際にCustomOpenFileDialogを作ってみる。 (csharp)#cfb43496e568
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
 
namespace CustomOpenFileDialog
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }
    private void button1_Click(object sender, EventArgs e)
    {
        var cofd = new CustomOpenFileDialog();
        if (cofd.ShowDialog() == DialogResult.OK)
        {
            textBox1.Text = cofd.FileName;
        }
    }
}
▸ この snippet は実行結果未収録
▸ 実行結果は未収録です

}

これを実行すると下記のような感じになる。

初期画面ではちゃんとデスクトップが表示されていて、ダイアログのタイトルにもちゃんと値が入っている。

それにちゃんとフィルターもかかっている。

ファイルを選択すると下記のような感じになる。

ちゃんとファイルが選択されている。

これを応用すると下記のような感じにもできる。

まずはFilterTypeのEnumを作る

これを応用すると下記のような感じにもできる。 (csharp)#4150199d1ce1
    public enum FilterType
    {
        HTML = 1,
        CSV
    }
▸ この snippet は実行結果未収録
▸ 実行結果は未収録です

でCustomOpenFileDialogのコンストラクタの引数にセットする。

これを応用すると下記のような感じにもできる。 (csharp)#b19dc111a5eb
    public class CustomOpenFileDialog
    {
        private OpenFileDialog _ofd;
 
    public string FileName => _ofd.FileName;
 
    public CustomOpenFileDialog(FilterType type)
    {
        _ofd = new OpenFileDialog();
        _ofd.InitialDirectory = Environment.GetFolderPath(Environment.SpecialFolder.Desktop);
        _ofd.Title = "ファイル選択";
        switch (type)
        {
            case FilterType.CSV:
                _ofd.Filter = "csvファイル(*.csv)| *.csv";
                break;
            case FilterType.HTML:
                _ofd.Filter = "htmlファイル(*.html)| *.html";
                break;
        }
 
    }
 
    public DialogResult ShowDialog()
    {
        return _ofd.ShowDialog();
    }
}
▸ この snippet は実行結果未収録
▸ 実行結果は未収録です

使う側はこんな感じ

これを応用すると下記のような感じにもできる。 (csharp)#3cbb7f609ff9
        private void button1_Click(object sender, EventArgs e)
        {
            var cofd = new CustomOpenFileDialog(FilterType.CSV);
            if (cofd.ShowDialog() == DialogResult.OK)
            {
                textBox1.Text = cofd.FileName;
            }
        }
▸ この snippet は実行結果未収録
▸ 実行結果は未収録です

これで実行すると下記のような感じでFilterがちゃんと変わってる

継承ができなくても別の方法があるはず!

今回OpenFileDialogの継承ができないために別の方法で継承的なことをしたけども、結構こんなパターンが多いんですよね!!

でもいろいろ調べてみたら方法はありますし、なんなら方法を考えるのが楽しい!ってのがあります。

そもそもその方法を考えるのがエンジニアの仕事であって醍醐味であると思います!

とにかくこういうパターンは経験でしかないので、公私問わず勉強をして経験を積むしかないのでしょうね!!

今回のコードはGithubに上げています!

実際の動きを見たい場合はかきリポジトリにアクセスしてみてください!

https://github.com/adaman3568/blog-source-demo

💡 補足: OpenFileDialog でハマる3点

俺もこの OpenFileDialog、 業務でハマってきたところを3つ並べておきます。

① 継承不可なので Wrapper 必須

sealed class 指定で継承不可。 拡張したい場合は「Wrapper クラス + 集約 (has-a 関係)」で対応。 業務系では FileDialogHelper 等のユーティリティクラスを作る。

② Filter プロパティの構文ミス

Filter = "CSV files|*.csv|All files|*.*" のパイプ区切りを間違えると例外。 「表示名 | 拡張子 | 表示名 | 拡張子」の繰り返し。 セミコロン使うと複数拡張子 ( "画像|*.png;*.jpg" )。

③ ShowDialog() の戻り値判定

if (dialog.ShowDialog() == DialogResult.OK) が定石。 戻り値見ずに dialog.FileName 使うと、 ユーザーがキャンセルした時に空文字で NRE。

❓ よくある質問

Q1. SaveFileDialog との違いは?

A. OpenFileDialog = 既存ファイル選択、 SaveFileDialog = 新規ファイル名指定 + 上書き確認。 API は似てるが用途が違う。

Q2. 複数選択するには?

A. Multiselect = true 設定 → FileNames プロパティで配列取得。 デフォルトは単一選択。

Q3. 初期ディレクトリ指定は?

A. InitialDirectory プロパティ。 業務系では「最後に選択したフォルダ」を Settings に保存 → 次回オープン時に復元。

Q4. ファイル拡張子フィルタの動的設定は?

A. FilterIndex プロパティで起動時のデフォルトフィルタを指定 (1-based)。 ユーザーが選択変更すると自動更新。

Q5. ダイアログ閉じ忘れの心配は?

A. using (var dlg = new OpenFileDialog()) で自動 Dispose。 OpenFileDialog は IDisposable なので using 推奨。

📚 関連記事

執筆者

バイブス父さん — 業務 SE 7 年 (正社員 2 / フリーランス 5)。 現職は SEO 直轄部の AI アドバイザー兼 PL、 副業で中小 SIer の CTO。 SES 複数社・フリーランスエージェント複数経由の経験ベースで「業務 SE 視点」 の技術 + キャリア記事を書いています。

🐦 X: @hiro_progra0524 (日々の現場メモ更新中)
📝 About Me で経歴詳細を見る

この記事のコードと手順は ぜんぶ動作検証済み。 安心して現場で試してくれ。
バイブス父さん

現役の業務SE。C# / SQL Server 保守の現場から、コードも人もキャリアも全部書く。 実体験ベース。

運営者について