■はじめに
今回はエクスプローラーからファイルのドラッグ&ドロップを処理します。
最初にドロップされたファイルのフルパス一覧を表示するサンプルを作った後、
ドラッグ&ドロップでテキストファイルの内容を表示するビューアを作成します。
■開発環境
- Windows 10
- Visual Studio Community 2019 (Version 16.4.5)
- .NET Framework 4.5.2 / .NET Core 3.1
■ドラッグ&ドロップのサンプル
◇プロジェクト作成
WPFアプリ(.NET Framework)
またはWPF App(.NET Core)
でプロジェクトを作成します。
ここでは.NET Framework
で作成していますが、.NET Core
でも作成方法は同じです。
◇画面
TextBlock
のAllowDrop
プロパティをTrue
にし、PreviewDragOver
イベントとDrop
イベントを作成します。
<Window x:Class="DragDropSample.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:DragDropSample"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid>
<TextBlock
Text=""
Margin="5"
AllowDrop="True"
PreviewDragOver="TextBlock_PreviewDragOver"
Drop="TextBlock_Drop"/>
</Grid>
</Window>
◇ロジック
using System.Text;
using System.Windows;
using System.Windows.Controls;
namespace DragDropSample
{
/// <summary>
/// MainWindow.xaml の相互作用ロジック
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
/// <summary>
/// ドラッグ中オブジェクトがテキストの上に来た時
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void TextBlock_PreviewDragOver(object sender, DragEventArgs e)
{
// マウスカーソルをコピー中に変更
e.Effects = DragDropEffects.Copy;
// ドラッグ中のオブジェクトがファイルの場合、受け付ける
e.Handled = e.Data.GetDataPresent(DataFormats.FileDrop);
}
/// <summary>
/// テキストにドロップ
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void TextBlock_Drop(object sender, DragEventArgs e)
{
var txt = sender as TextBlock;
// ドロップしたファイル一覧を取得
var files = e.Data.GetData(DataFormats.FileDrop) as string[];
if (files == null)
{
txt.Text = "";
return;
}
var sb = new StringBuilder();
foreach (string file in files)
{
sb.Append(file).Append("\r\n");
}
// 表示
txt.Text = sb.ToString();
}
}
}
◇動かしてみる
次はテキストファイルビューアを作ってみます。
ウィンドウにファイルをドラッグ&ドロップすると内容を表示します。
ドラッグ&ドロップ部分の実装だけ知りたかった人はここから先は読む必要ありません。
■テキストビューア
◇.NET Framework版
◎プロジェクト作成
WPFアプリ(.NET Framework)
(C#)でプロジェクトを作成します。
名前はTextViewFW
にします。
◎NuGet
NuGet
で高機能テキストコントロールのAvalonEdit
をインストールします。
◎画面
Window
にxmlns:ae="http://icsharpcode.net/sharpdevelop/avalonedit"
を追加し、Grid
の中にae:TextEditor
を入れます。
読み取り専用や行番号表示等のプロパティを設定します。
<Window x:Class="TextViewFW.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:TextViewFW"
xmlns:ae="http://icsharpcode.net/sharpdevelop/avalonedit"
mc:Ignorable="d"
Title="テキストビューア" Height="450" Width="800" ResizeMode="CanResizeWithGrip">
<Grid>
<ae:TextEditor
x:Name="textView"
IsReadOnly="True"
ShowLineNumbers="True"
Drop="textView_Drop"
PreviewDragOver="textView_PreviewDragOver">
<ae:TextEditor.Options>
<!-- 改行マーク、空白表示 -->
<ae:TextEditorOptions
ShowEndOfLine="True"
ShowSpaces="True"/>
</ae:TextEditor.Options>
<ae:TextEditor.ContextMenu>
<!-- コンテキストメニュー -->
<ContextMenu>
<MenuItem Header="Shift_JIS" Tag="sjis" Click="menuItem_Click"/>
<MenuItem Header="UTF-8" Tag="utf8" Click="menuItem_Click"/>
</ContextMenu>
</ae:TextEditor.ContextMenu>
</ae:TextEditor>
</Grid>
</Window>
◎ロジック
using System;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using AEHL = ICSharpCode.AvalonEdit.Highlighting;
namespace TextViewFW
{
/// <summary>
/// MainWindow.xaml の相互作用ロジック
/// </summary>
public partial class MainWindow : Window
{
/// <summary>
/// 読み込んだファイルパス
/// </summary>
private string loadFilePath;
public MainWindow()
{
InitializeComponent();
}
/// <summary>
/// ドラッグ中オブジェクトが上に来た時
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void textView_PreviewDragOver(object sender, DragEventArgs e)
{
// マウスカーソルをコピー中に変更
e.Effects = DragDropEffects.Copy;
// ドラッグ中のオブジェクトがファイルの場合、受け付ける
e.Handled = e.Data.GetDataPresent(DataFormats.FileDrop);
}
/// <summary>
/// ドロップ時
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void textView_Drop(object sender, DragEventArgs e)
{
loadFilePath = "";
// ドロップしたファイル一覧を取得
var files = e.Data.GetData(DataFormats.FileDrop) as string[];
if (files == null)
{
textView.Clear();
return;
}
// ドロップしたファイルが複数あっても1つだけ取得
string filePath = files.First();
try
{
// シンタックスハイライト設定
textView.SyntaxHighlighting = GetSyntaxHilight(filePath);
// ファイル読み込み
textView.Load(filePath);
// ファイルパスを保存しておく
loadFilePath = filePath;
}
catch (NotSupportedException)
{
MessageBox.Show("このファイルは読み込めません。");
return;
}
}
/// <summary>
/// シンタックスハイライト取得
/// </summary>
/// <param name="filePath">ファイルパス</param>
/// <returns>シンタックスハイライト情報</returns>
private AEHL.IHighlightingDefinition GetSyntaxHilight(string filePath)
{
// 拡張子取得
string ext = System.IO.Path.GetExtension(filePath).ToLower();
switch (ext)
{
case ".cs":
return AEHL.HighlightingManager.Instance.GetDefinition("C#");
case ".md":
return AEHL.HighlightingManager.Instance.GetDefinition("MarkDown");
case ".cpp":
return AEHL.HighlightingManager.Instance.GetDefinition("C++");
case ".js":
return AEHL.HighlightingManager.Instance.GetDefinition("JavaScript");
case ".json":
return AEHL.HighlightingManager.Instance.GetDefinition("Json");
case ".htm":
case ".html":
return AEHL.HighlightingManager.Instance.GetDefinition("HTML");
case ".css":
return AEHL.HighlightingManager.Instance.GetDefinition("CSS");
case ".xml":
case ".xaml":
case ".config":
case ".csproj":
return AEHL.HighlightingManager.Instance.GetDefinition("XML");
case ".ps1":
case ".psm1":
return AEHL.HighlightingManager.Instance.GetDefinition("PowerShell");
case ".java":
return AEHL.HighlightingManager.Instance.GetDefinition("Java");
case ".sql":
return AEHL.HighlightingManager.Instance.GetDefinition("TSQL");
case ".vb":
return AEHL.HighlightingManager.Instance.GetDefinition("VB");
case ".py":
return AEHL.HighlightingManager.Instance.GetDefinition("Python");
case ".php":
return AEHL.HighlightingManager.Instance.GetDefinition("PHP");
case ".txt":
case ".log":
case ".bat":
case ".ini":
case ".csv":
return null;
default:
throw new NotSupportedException();
}
}
/// <summary>
/// コンテキストメニューの項目クリック時
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
/// <remarks>エンコーディングの変更</remarks>
private void menuItem_Click(object sender, RoutedEventArgs e)
{
bool changeEncoding = false;
string tag = (sender as MenuItem).Tag.ToString();
if (tag == "sjis")
{
if (textView.Encoding != Encoding.GetEncoding("Shift_JIS"))
{
// エンコーディング変更
textView.Encoding = Encoding.GetEncoding("Shift_JIS");
changeEncoding = true;
}
}
else
{
if (textView.Encoding != Encoding.UTF8)
{
// エンコーディング変更
textView.Encoding = Encoding.UTF8;
changeEncoding = true;
}
}
// エンコーディングが変更された?
if (changeEncoding &&
string.IsNullOrEmpty(loadFilePath) == false)
{
// 再読み込み
textView.Load(loadFilePath);
}
}
}
}
◎動かしてみる
◇.NET Core版
◎プロジェクト作成
WPF App(.NET Core)
(C#)でプロジェクトを作成します。
名前はTextViewCore
にします。
◎NuGet
NuGet
でAvalonEdit
をインストールするところまでは.NET Framework版と同じです。
.NET Core版はShift_JISのファイルを使えるように追加でSystem.Text.Encoding.CodePages
をインストールします。
◎画面
ファイルを読み込んで文字化けした時のために、コンテキストメニューでエンコーディングを変更できるようにします。
<Window x:Class="TextViewCore.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:TextViewCore"
xmlns:ae="http://icsharpcode.net/sharpdevelop/avalonedit"
mc:Ignorable="d"
Title="テキストビューア" Height="450" Width="800" ResizeMode="CanResizeWithGrip">
<Grid>
<ae:TextEditor
x:Name="textView"
IsReadOnly="True"
ShowLineNumbers="True"
Drop="textView_Drop"
PreviewDragOver="textView_PreviewDragOver">
<ae:TextEditor.Options>
<!-- 改行マーク、空白表示 -->
<ae:TextEditorOptions
ShowEndOfLine="True"
ShowSpaces="True"/>
</ae:TextEditor.Options>
<ae:TextEditor.ContextMenu>
<!-- コンテキストメニュー -->
<ContextMenu>
<MenuItem Header="Shift_JIS" Tag="sjis" Click="menuItem_Click"/>
<MenuItem Header="UTF-8" Tag="utf8" Click="menuItem_Click"/>
</ContextMenu>
</ae:TextEditor.ContextMenu>
</ae:TextEditor>
</Grid>
</Window>
◎ロジック
using System;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using AEHL = ICSharpCode.AvalonEdit.Highlighting;
namespace TextViewCore
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
/// <summary>
/// 読み込んだファイルパス
/// </summary>
private string loadFilePath;
public MainWindow()
{
InitializeComponent();
}
/// <summary>
/// ドラッグ中オブジェクトが上に来た時
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void textView_PreviewDragOver(object sender, DragEventArgs e)
{
// マウスカーソルをコピー中に変更
e.Effects = DragDropEffects.Copy;
// ドラッグ中のオブジェクトがファイルの場合、受け付ける
e.Handled = e.Data.GetDataPresent(DataFormats.FileDrop);
}
/// <summary>
/// ドロップ時
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void textView_Drop(object sender, DragEventArgs e)
{
loadFilePath = "";
// ドロップしたファイル一覧を取得
var files = e.Data.GetData(DataFormats.FileDrop) as string[];
if (files == null)
{
textView.Clear();
return;
}
// ドロップしたファイルが複数あっても1つだけ取得
string filePath = files.First();
try
{
// シンタックスハイライト設定
textView.SyntaxHighlighting = GetSyntaxHilight(filePath);
// SJISも使えるようにする
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
// ファイル読み込み
textView.Load(filePath);
// ファイルパスを保存しておく
loadFilePath = filePath;
}
catch (NotSupportedException)
{
MessageBox.Show("このファイルは読み込めません。");
return;
}
}
/// <summary>
/// シンタックスハイライト取得
/// </summary>
/// <param name="filePath">ファイルパス</param>
/// <returns>シンタックスハイライト情報</returns>
private AEHL.IHighlightingDefinition GetSyntaxHilight(string filePath)
{
// 拡張子取得
string ext = System.IO.Path.GetExtension(filePath).ToLower();
switch (ext)
{
case ".cs":
return AEHL.HighlightingManager.Instance.GetDefinition("C#");
case ".md":
return AEHL.HighlightingManager.Instance.GetDefinition("MarkDown");
case ".cpp":
return AEHL.HighlightingManager.Instance.GetDefinition("C++");
case ".js":
return AEHL.HighlightingManager.Instance.GetDefinition("JavaScript");
case ".json":
return AEHL.HighlightingManager.Instance.GetDefinition("Json");
case ".htm":
case ".html":
return AEHL.HighlightingManager.Instance.GetDefinition("HTML");
case ".css":
return AEHL.HighlightingManager.Instance.GetDefinition("CSS");
case ".xml":
case ".xaml":
case ".config":
case ".csproj":
return AEHL.HighlightingManager.Instance.GetDefinition("XML");
case ".ps1":
case ".psm1":
return AEHL.HighlightingManager.Instance.GetDefinition("PowerShell");
case ".java":
return AEHL.HighlightingManager.Instance.GetDefinition("Java");
case ".sql":
return AEHL.HighlightingManager.Instance.GetDefinition("TSQL");
case ".vb":
return AEHL.HighlightingManager.Instance.GetDefinition("VB");
case ".py":
return AEHL.HighlightingManager.Instance.GetDefinition("Python");
case ".php":
return AEHL.HighlightingManager.Instance.GetDefinition("PHP");
case ".txt":
case ".log":
case ".bat":
case ".ini":
case ".csv":
return null;
default:
throw new NotSupportedException();
}
}
/// <summary>
/// コンテキストメニューの項目クリック時
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
/// <remarks>エンコーディングの変更</remarks>
private void menuItem_Click(object sender, RoutedEventArgs e)
{
bool changeEncoding = false;
string tag = (sender as MenuItem).Tag.ToString();
if (tag == "sjis")
{
// SJIS使えるようにする
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
if (textView.Encoding != Encoding.GetEncoding("Shift_JIS"))
{
// エンコーディング変更
textView.Encoding = Encoding.GetEncoding("Shift_JIS");
changeEncoding = true;
}
}
else
{
if (textView.Encoding != Encoding.UTF8)
{
// エンコーディング変更
textView.Encoding = Encoding.UTF8;
changeEncoding = true;
}
}
// エンコーディングが変更された?
if (changeEncoding &&
string.IsNullOrEmpty(loadFilePath) == false)
{
// 再読み込み
textView.Load(loadFilePath);
}
}
}
}
◎動かしてみる
おしまい