📄

C#定石 - ファイル選択 - Drag & Drop とファイル選択ダイアログ

2025/02/03に公開

はじめに

C# ソフト開発時に、決まり事として実施していた内容を記載します。

記載内容

本記事では、下記2つのセクションにわけて、情報を記載することとします。

  • ファイル選択ダイアログの選択肢
  • ファイル選択イベントハンドラ

テスト環境

ここに記載した情報/ソースコードは、Visual Studio Community 2022 を利用した下記プロジェクトで生成したモジュールを Windows 11 24H2 で動作確認しています。

  • Windows Forms - .NET Framework 4.8
  • Windows Forms - .NET 8
  • WPF - .NET Framework 4.8
  • WPF - .NET 8

ファイル選択ダイアログの選択肢

ファイル選択ダイアログの選択肢として下記が存在します。

  • System.Windows.Forms.OpenFileDialog
    • Windows Forms - .NET Framework / .NET で利用可能
    • WPF から、わざわざ理由するメリットはない
  • Microsoft.Win32.OpenFileDialog
    • WPF - .NET Framework / .NET で利用可能
    • Windows Forms から、わざわざ理由するメリットはない
  • Microsoft.WindowsAPICodePack.Dialogs.CommonOpenFileDialog
    • NuGet Gallery | Microsoft-WindowsAPICodePack-Shell 導入が必要
    • Microsoft-WindowsAPICodePack-Shell は、Windows API CodePack の一部で、Windows シェル機能を、.NET Framework / .NET で利用するためのライブラリ
      • Dialogs - ダイアログ表示機能
    • プロパティで、ファイル選択とフォルダ選択の切り替え可能
    • .NET Framework 4.5.2 以降、.NET 6 以降で利用可能

それぞれのサンプルコードと、ダイアログ表示形態を以降に記載します。

System.Windows.Forms.OpenFileDialog

https://learn.microsoft.com/ja-jp/dotnet/api/system.windows.forms.openfiledialog

// 初期選択値
string path = @"C:\Program Files (x86)\Microsoft Visual Studio\Installer\NOTICE.txt";

// ファイル選択ダイアログ
using (var dlg = new OpenFileDialog())
{
  dlg.Filter = "テキストファイル (*.txt)|*.txt|すべてのファイル (*.*)|*.*";
  dlg.Multiselect = false;
  dlg.Title = "ファイルを選択してください";

  // 初期選択値があれば設定
  if (!string.IsNullOrEmpty(path))
  {
    var info = new FileInfo(path);
    if (Directory.Exists(info.DirectoryName))
    {
      dlg.InitialDirectory = info.DirectoryName;
      dlg.FileName = info.Name;
    }
  }
  // ファイル選択ダイアログ表示
  if (dlg.ShowDialog() == DialogResult.OK)
  {
    // 選択値で更新
    path = dlg.FileName;
  }
}

Microsoft.Win32.OpenFileDialog

https://learn.microsoft.com/ja-jp/dotnet/api/microsoft.win32.openfiledialog

// 初期選択値
string path = @"C:\Program Files (x86)\Microsoft Visual Studio\Installer\NOTICE.txt";

// ファイル選択ダイアログ
var dlg = new Microsoft.Win32.OpenFileDialog
{
  Filter = "テキストファイル (*.txt)|*.txt|すべてのファイル (*.*)|*.*",
  Multiselect = false,
  Title = "ファイルを選択してください"
};
// 初期選択値があれば設定
if (!string.IsNullOrEmpty(path))
{
  var info = new FileInfo(path);
  if (Directory.Exists(info.DirectoryName))
  {
    dlg.InitialDirectory = info.DirectoryName;
    dlg.FileName = info.Name;
  }
}
// ファイル選択ダイアログ表示
if (dlg.ShowDialog() == true)
{
    // 選択値で更新
    path = dlg.FileName;
}

Microsoft.WindowsAPICodePack.Dialogs.CommonOpenFileDialog

NuGet で、NuGet Gallery | Microsoft-WindowsAPICodePack-Shell を導入します。

PM> NuGet\Install-Package Microsoft-WindowsAPICodePack-Shell

CommonOpenFileDialog プロパティについては、下記、公開ソースで確認できます。

https://github.com/contre/Windows-API-Code-Pack-1.1/tree/main/source/WindowsAPICodePack/Shell/CommonFileDialogs

using Microsoft.WindowsAPICodePack.Dialogs;
// 初期選択値
string path = @"C:\Program Files (x86)\Microsoft Visual Studio\Installer\NOTICE.txt";

// ファイル選択ダイアログ
using (var dlg = new CommonOpenFileDialog())
{
  dlg.IsFolderPicker = false;  // true:フォルダ選択  false:ファイル選択

  dlg.Filters.Add(new CommonFileDialogFilter("テキストファイル", "*.txt"));
  dlg.Filters.Add(new CommonFileDialogFilter("すべてのファイル", "*.*"));
  dlg.Multiselect = false;
  dlg.Title = "ファイルを選択してください";

  // 初期選択値があれば設定
  if (!string.IsNullOrEmpty(path))
  {
    var info = new FileInfo(path);
    if (Directory.Exists(info.DirectoryName))
    {
      dlg.InitialDirectory = info.DirectoryName;
      dlg.DefaultFileName = info.Name;
    }
  }
  // ファイル選択ダイアログ表示
  if (dlg.ShowDialog() == CommonFileDialogResult.Ok)
  {
    // 選択値で更新
    path = dlg.FileName;
  }
}

ファイル選択イベントハンドラ

Drag & Drop 可能なテキストボックス、ファイル選択ダイアログ表示ボタンのペアで、ファイル選択する UI を良く利用していました。
このようなペアが複数存在するケースでは、イベントハンドラを共有させると効率的です。

テキストボックスとファイル選択ダイアログ表示ボタンとして、下記をデザイナーで配置した場合のサンプルコードを、いくつかのパターンで以降に記載します。

TextBox txtFile1 - Drag & Drop 可能なテキストボックス
Button  btnFile1 - txtFile1 のファイル選択ダイアログ表示ボタン
TextBox txtFile2 - Drag & Drop 可能なテキストボックス
Button  btnFile2 - txtFile2 のファイル選択ダイアログ表示ボタン

Windows Forms - .NET 8 + System.Windows.Forms.OpenFileDialog

txtFile1.AllowDrop = true;
txtFile1.DragEnter += OneFile_DragEnter;
txtFile1.DragDrop += OneFile_DragDrop;
btnFile1.Click += (sender, e) => OneFile_Selection(sender, e, txtFile1);

txtFile2.AllowDrop = true;
txtFile2.DragEnter += OneFile_DragEnter;
txtFile2.DragDrop += OneFile_DragDrop;
btnFile2.Click += (sender, e) => OneFile_Selection(sender, e, txtFile2);
// Drag & Drop - 対象:ファイル単一
private void OneFile_DragEnter(object? sender, DragEventArgs e)
{
  // 1つのファイルがドラッグされている場合のみ対象
  if (e != null)
  {
    if (e.Data?.GetDataPresent(DataFormats.FileDrop) == true)
    {
      var paths = e.Data?.GetData(DataFormats.FileDrop) as string[];
      if (paths?.Length == 1)
      {
        if (File.Exists(paths[0]))
        {
          e.Effect = DragDropEffects.Copy;
        }
      }
    }
  }
}
private void OneFile_DragDrop(object? sender, DragEventArgs e)
{
  var paths = e?.Data?.GetData(DataFormats.FileDrop) as string[];
  if (paths?.Length == 1)
  {
    var target = sender as TextBox;
    if (target != null)
    {
      target.Text = paths[0];
    }
  }
}
// ファイル選択ダイアログ表示 - テキストボックス反映
private void OneFile_Selection(object? sender, EventArgs e, TextBox target)
{
  // 初期選択値
  string path = target.Text.Trim();

  // ファイル選択ダイアログ
  using (var dlg = new OpenFileDialog())
  {
    dlg.Filter = "テキストファイル (*.txt)|*.txt|すべてのファイル (*.*)|*.*";
    dlg.Multiselect = false;
    dlg.Title = "ファイルを選択してください";

    // 初期選択値があれば設定
    if (!string.IsNullOrEmpty(path))
    {
      var info = new FileInfo(path);
      if (Directory.Exists(info.DirectoryName))
      {
        dlg.InitialDirectory = info.DirectoryName;
        dlg.FileName = info.Name;
      }
    }
    // ファイル選択ダイアログ表示
    if (dlg.ShowDialog() == DialogResult.OK)
    {
      target.Text = dlg.FileName;
    }
  }
}

WPF - .NET 8 + Microsoft.Win32.OpenFileDialog

txtFile1.AllowDrop = true;
txtFile1.PreviewDragOver += OneFile_PreviewDragOver;
txtFile1.Drop += OneFile_Drop;
btnFile1.Click += (sender, e) => OneFile_Selection(sender, e, txtFile1);

txtFile2.AllowDrop = true;
txtFile2.PreviewDragOver += OneFile_PreviewDragOver;
txtFile2.Drop += OneFile_Drop;
btnFile2.Click += (sender, e) => OneFile_Selection(sender, e, txtFile2);
// Drag & Drop - 対象:ファイル単一
private void OneFile_PreviewDragOver(object sender, DragEventArgs e)
{
  // 1つのファイルがドラッグされている場合のみ対象
  if (e != null)
  {
    e.Effects = DragDropEffects.None;
    
    if (e.Data?.GetDataPresent(DataFormats.FileDrop) == true)
    {
      var paths = e.Data?.GetData(DataFormats.FileDrop) as string[];
      if (paths?.Length == 1)
      {
        if (File.Exists(paths[0]))
        {
          e.Effects = DragDropEffects.Copy;
        }
      }
    }
    e.Handled = true;
  }
}
private void OneFile_Drop(object sender, DragEventArgs e)
{
  var paths = e?.Data?.GetData(DataFormats.FileDrop) as string[];
  if (paths?.Length == 1)
  {
    var target = sender as TextBox;
    if (target != null)
    {
      target.Text = paths[0];
    }
  }
}
// ファイル選択ダイアログ表示 - テキストボックス反映
private void OneFile_Selection(object? sender, EventArgs e, TextBox target)
{
  // 初期選択値
  string path = target.Text.Trim();

  // ファイル選択ダイアログ
  var dlg = new Microsoft.Win32.OpenFileDialog
  {
    Filter = "テキストファイル (*.txt)|*.txt|すべてのファイル (*.*)|*.*",
    Multiselect = false,
    Title = "ファイルを選択してください"
  };
  // 初期選択値があれば設定
 if (!string.IsNullOrEmpty(path))
 {
    var info = new FileInfo(path);
    if (Directory.Exists(info.DirectoryName))
    {
      dlg.InitialDirectory = info.DirectoryName;
      dlg.FileName = info.Name;
    }
  }
  // ファイル選択ダイアログ表示
  if (dlg.ShowDialog() == true)
  {
    // 選択値で更新
    target.Text = dlg.FileName;
  }
}

WPF - .NET Framework 4.8 + CommonOpenFileDialog

using Microsoft.WindowsAPICodePack.Dialogs;
txtFile1.AllowDrop = true;
txtFile1.PreviewDragOver += OneFile_PreviewDragOver;
txtFile1.Drop += OneFile_Drop;
btnFile1.Click += (sender, e) => OneFile_Selection(sender, e, txtFile1);

txtFile2.AllowDrop = true;
txtFile2.PreviewDragOver += OneFile_PreviewDragOver;
txtFile2.Drop += OneFile_Drop;
btnFile2.Click += (sender, e) => OneFile_Selection(sender, e, txtFile2);
// Drag & Drop - 対象:ファイル単一
private void OneFile_PreviewDragOver(object sender, DragEventArgs e)
{
  // 1つのファイルがドラッグされている場合のみ対象
  if (e != null)
  {
    e.Effects = DragDropEffects.None;
    
    if (e.Data?.GetDataPresent(DataFormats.FileDrop) == true)
    {
      var paths = e.Data?.GetData(DataFormats.FileDrop) as string[];
      if (paths?.Length == 1)
      {
        if (File.Exists(paths[0]))
        {
          e.Effects = DragDropEffects.Copy;
        }
      }
    }
    e.Handled = true;
  }
}
private void OneFile_Drop(object sender, DragEventArgs e)
{
  var paths = e?.Data?.GetData(DataFormats.FileDrop) as string[];
  if (paths?.Length == 1)
  {
    var target = sender as TextBox;
    if (target != null)
    {
      target.Text = paths[0];
    }
  }
}
// ファイル選択ダイアログ表示 - テキストボックス反映
private void OneFile_Selection(object sender, EventArgs e, TextBox target)
{
  // 初期選択値
  string path = target.Text.Trim();

  // ファイル選択ダイアログ
  using (var dlg = new CommonOpenFileDialog())
  {
    dlg.IsFolderPicker = false;  // true:フォルダ選択  false:ファイル選択

    dlg.Filters.Add(new CommonFileDialogFilter("テキストファイル", "*.txt"));
    dlg.Filters.Add(new CommonFileDialogFilter("すべてのファイル", "*.*"));
    dlg.Multiselect = false;
    dlg.Title = "ファイルを選択してください";

    // 初期選択値があれば設定
    if (!string.IsNullOrEmpty(path))
    {
      var info = new FileInfo(path);
      if (Directory.Exists(info.DirectoryName))
      {
        dlg.InitialDirectory = info.DirectoryName;
        dlg.DefaultFileName = info.Name;
      }
    }
    // ファイル選択ダイアログ表示
    if (dlg.ShowDialog() == CommonFileDialogResult.Ok)
    {
      // 選択値で更新
      path = dlg.FileName;
    }
  }
}

本記事は、2025/02/03 Qiita 投稿記事の転載です。

C#定石 - ファイル選択 - Drag & Drop とファイル選択ダイアログ

Discussion