[C#][Windows Formsアプリ][SaveFileDialog] InitialDirectory を極める——既定フォルダ指定

スポンサーリンク

はじめに

この記事では、Windows Forms の SaveFileDialog における「InitialDirectory」プロパティの使い方を解説します。ユーザーに最初に開く保存先フォルダを気持ちよく提示できると、毎回の移動が減って操作体験がぐっと楽になります。本稿では基本の指定方法から、存在確認・既定フォルダの選び方・よくある落とし穴まで、初心者でも迷わない手順で説明します。

学べること:
InitialDirectory の正しい指定方法と推奨パターン
Environment.SpecialFolder を使った安全な既定パスの決め方
・存在しないパスの扱い、現在ディレクトリへの副作用対策(RestoreDirectory
・「たまに初期フォルダが効かない」時の考え方と回避策

説明

InitialDirectory は、SaveFileDialog を開いた直後に表示したいフォルダのフルパスを文字列で指定します。たとえば「ダウンロード」や「ドキュメント」など、ユーザーが保存しがちな場所を最初に見せることで操作が短縮できます。

指定の基本は次のとおりです。
・存在する絶対パスを入れる(例:C:\Users\<User>\Documents)。
・ユーザー環境に依存しない書き方として、Environment.GetFolderPath(Environment.SpecialFolder.◯◯) を使う。
・見つからない(存在しない)パスを渡すと OS の既定や直近の場所にフォールバックされることがある。

なお、モダンなスタイルの共通ダイアログでは、OS 側の「直近開いたフォルダ」などのヒントが優先され、InitialDirectory が期待どおり効かないことがあります。確率は高くありませんが、確実に初期フォルダを誘導したい場合は、InitialDirectory に加えて「妥当なファイル名の提案(FileName)」「存在確認」「現在ディレクトリの復元(RestoreDirectory)」を組み合わせるのが無難です。

サンプルコード

テキストを入力し、「保存…」ボタンで SaveFileDialog を開く最小アプリです。コンボボックスで初期フォルダを選び、InitialDirectory に反映します。フォルダが存在しない場合は自動で安全な場所(ドキュメント)にフォールバックします。

using System; using System.IO; 
using System.Text; 
using System.Windows.Forms;


public class MainForm : Form
{
private readonly TextBox _txt = new TextBox();
private readonly ComboBox _cmb = new ComboBox();
private readonly TextBox _custom = new TextBox();
private readonly Button _btnSave = new Button();

public MainForm()
{
    Text = "SaveFileDialog: InitialDirectory デモ";
    Width = 680;
    Height = 420;

    _txt.Multiline = true;
    _txt.ScrollBars = ScrollBars.Both;
    _txt.Dock = DockStyle.Fill;

    _cmb.DropDownStyle = ComboBoxStyle.DropDownList;
    _cmb.Items.AddRange(new object[]
    {
        "ドキュメント",
        "デスクトップ",
        "ユーザープロファイル",
        "カスタム(下のパス)"
    });
    _cmb.SelectedIndex = 0;

    _custom.Width = 320;
    _custom.Text = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);

    _btnSave.Text = "保存...";
    _btnSave.AutoSize = true;
    _btnSave.Click += OnSaveClick;

    var top = new FlowLayoutPanel
    {
        Dock = DockStyle.Top,
        Height = 72,
        Padding = new Padding(8),
        FlowDirection = FlowDirection.LeftToRight,
        WrapContents = false
    };
    top.Controls.Add(new Label { Text = "初期フォルダ:", AutoSize = true, Padding = new Padding(0, 6, 8, 0) });
    top.Controls.Add(_cmb);
    top.Controls.Add(new Label { Text = "カスタム:", AutoSize = true, Padding = new Padding(16, 6, 8, 0) });
    top.Controls.Add(_custom);
    top.Controls.Add(_btnSave);

    Controls.Add(_txt);
    Controls.Add(top);
}

private void OnSaveClick(object? sender, EventArgs e)
{
    var initialDir = ResolveInitialDirectory();

    // 存在しない場合は安全な場所へフォールバック
    if (string.IsNullOrWhiteSpace(initialDir) || !Directory.Exists(initialDir))
    {
        initialDir = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
    }

    using (var sfd = new SaveFileDialog())
    {
        // 初期フォルダ
        sfd.InitialDirectory = initialDir;

        // 後片付け: ダイアログでカレントディレクトリが変わっても復元
        sfd.RestoreDirectory = true;

        // わかりやすいファイル名の提案(初期フォルダ誘導に役立つ)
        sfd.FileName = "memo.txt";

        // 最低限のフィルタ
        sfd.Filter = "テキストファイル (*.txt)|*.txt|すべてのファイル (*.*)|*.*";
        sfd.DefaultExt = "txt";
        sfd.AddExtension = true;

        sfd.Title = "ファイルを保存";

        var dr = sfd.ShowDialog(this);
        if (dr == DialogResult.OK)
        {
            var path = sfd.FileName;

            // 保存(UTF-8 BOMなし)
            File.WriteAllText(path, _txt.Text, new UTF8Encoding(encoderShouldEmitUTF8Identifier: false));

            MessageBox.Show(
                "保存しました:\n" + path,
                "保存完了",
                MessageBoxButtons.OK,
                MessageBoxIcon.Information);
        }
    }
}

private string ResolveInitialDirectory()
{
    switch (_cmb.SelectedIndex)
    {
        case 0:
            return Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
        case 1:
            return Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory);
        case 2:
            return Environment.GetFolderPath(Environment.SpecialFolder.UserProfile);
        case 3:
            return _custom.Text.Trim();
        default:
            return Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
    }
}


}
実行例

実行例

 

 

 

実行例

実行例

— サンプルコードの解説 —

① 初期フォルダの決定: ResolveInitialDirectory()Environment.SpecialFolder を使って安全にユーザーディレクトリを取得しています。ローミング環境や言語環境が違っても適切な実フォルダを返します。

② 存在確認とフォールバック: Directory.Exists(initialDir)false のとき、MyDocuments に退避。見つからないパスを渡すと OS 側の判断に委ねられて初期フォルダが意図せず変わることがあるため、明示的にハンドリングします。

③ 初期フォルダの「効き」を良くする工夫: InitialDirectory に加えて、FileName に妥当な提案名(例:memo.txt)を設定すると、ダイアログがそのフォルダを指し示しやすくなります。

④ 現在ディレクトリの復元: RestoreDirectory = true により、ダイアログがカレントディレクトリを変更してもフォーム側に影響を残しません。ファイル I/O の相対パスを多用するアプリでは特に重要です。

⑤ 文字エンコード: File.WriteAllText に UTF-8(BOM なし)を明示。用途に応じて変更してください。

つまづきポイント

相対パスを渡してしまう: InitialDirectory には絶対パスを。相対パスはアプリのカレントディレクトリに依存し、意図しない場所が開かれる原因になります。

存在しないフォルダ: 存在チェックを省くと OS の既定や直近の場所に切り替わることがあります。事前に Directory.Exists で確認し、無ければフォールバックしましょう。

ユーザーの直近履歴に負ける場合: 一部環境では「直近使ったフォルダ」が優先されることがあります。FileName の提案や、確実な既定フォルダ(ドキュメント等)を使う設計で影響を最小化します。

権限不足: 管理者権限が必要な場所やリダイレクト対象(仮想化)ではアクセスに失敗することがあります。保存前後の例外処理(try-catch)を忘れずに。

多ユーザー/ネットワーク環境: UNC や切断中のネットワークドライブを初期フォルダにすると表示が遅くなる/失敗することがあります。ローカルの既定にフォールバックするなどの配慮を。

まとめ

InitialDirectory は「最初にどのフォルダを見せるか」を決める入口です。Environment.SpecialFolder でユーザーに優しい場所を選び、存在確認で意図どおりの体験を担保。FileName 提案と RestoreDirectory を添えれば、実務でも安定して気持ちよく使える保存ダイアログになります。

 

Please follow and like us:

コメント

タイトルとURLをコピーしました