3
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

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

Posted at

はじめに

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

記載内容

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

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

C#定石 - フォルダ選択 - Drag & Drop とフォルダ選択ダイアログ 同一フォーマットで、ファイル選択もまとめてみました。

本記事の主題は「ファイル選択イベントハンドラ」で、サンプルコードを記載する上で、「ファイル選択ダイアログの選択肢」も併載しています。

テスト環境

ここに記載した情報/ソースコードは、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 以降で利用可能

フォルダ選択とは異なり、上記3つともに、ほぼ同一 UI です。
フォルダ選択で CommonOpenFileDialog を利用したならば、ファイル選択も CommonOpenFileDialog で良いと思います。
それ以外は、Windows Forms、WPF の標準ダイアログ利用で良いと思います。

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

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;
  }
}

FileSelect-01.png

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;
}

FileSelect-02.png

Microsoft.WindowsAPICodePack.Dialogs.CommonOpenFileDialog

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

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

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

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;
  }
}

FileSelect-03.png

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

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

FileSelect.png

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

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

Drag & Drop 操作に関して、Windows Forms と WPF では、イベント / ソースコード記載内容に相違があるので、注意してくだい。

ファイルパスが格納されているテキストボックスと、ファイル選択ダイアログ表示ボタンの連動は、カスタムコントロール化が正規の手順ですが、、、
ファイル選択ダイアログ表示ボタン - Click イベントハンドラ「OneFile_Selection」引数に、対象となるテキストボックスを追加して、連動させる手法を選択しています。

前述「ファイル選択ダイアログ選択肢」で記載されているファイル選択ダイアログと、フレームワークの組み合わせとなりますが、全てのパターンを網羅した記載はしません。

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;
    }
  }
}
3
0
0

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
3
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?