WPFでファイルマネージャを作成しています。
今回はファイルのコピー・切り取り・貼り付け機能を追加します。
GitHubリポジトリ(最新)
https://github.com/kareteruhito/MyFileManager
ソースコード
追加変更部分
ファイル名:MainWindow.xaml
変更部分のみ抜粋
<!-- コンテキストメニュー ここから -->
<ListView.ContextMenu>
<ContextMenu x:Name="FileContextMenu"
Opened="FileContextMenu_Opening">
<MenuItem Header="コピー"
Click="Copy_Click"/>
<MenuItem Header="切り取り"
Click="Cut_Click"/>
<Separator/>
<MenuItem Header="貼り付け"
Click="Paste_Click"/>
<Separator/>
<MenuItem Header="開く"
Click="Menu_Open_Click" />
<MenuItem Header="エクスプローラーで開く"
Click="Menu_OpenInExplorer_Click" />
<Separator/>
<MenuItem Header="削除"
Click="Menu_Delete_Click" />
</ContextMenu>
</ListView.ContextMenu>
<!-- コンテキストメニュー ここまで -->
ファイル名:MainWindow.FileListView.cs
追加部分
/*
* コピー・カット&ペースト処理(アプリ内クリップボード)
*/
// クリップボードの内容
enum FileOperation
{
None,
Copy,
Cut
}
// ファイルクリップボード
class FileClipboard
{
public FileOperation Operation { get; set; } = FileOperation.None;
public List<string> Paths { get; } = new();
}
// 選択中のファイルを取得
List<string> GetSelectedPaths()
{
return FileListView.SelectedItems
.Cast<FileItem>()
.Select(x => x.FullPath)
.ToList();
}
private FileClipboard _clipboard = new FileClipboard();
// 「コピー」メニュー項目クリック
void Copy_Click(object sender, RoutedEventArgs e)
{
SetClipboard(FileOperation.Copy);
}
// 「切り取り」メニュー項目クリック
void Cut_Click(object sender, RoutedEventArgs e)
{
SetClipboard(FileOperation.Cut);
}
// クリップボードに選択中のファイルをセット
void SetClipboard(FileOperation op)
{
var paths = GetSelectedPaths();
if (paths.Count == 0) return;
_clipboard.Paths.Clear();
_clipboard.Paths.AddRange(paths);
_clipboard.Operation = op;
}
// 「貼り付け」メニュー項目クリック
void Paste_Click(object sender, RoutedEventArgs e)
{
if (_clipboard.Operation == FileOperation.None) return;
string destDir = _currentDirectory; // カレントディレクトリ
foreach (var src in _clipboard.Paths)
{
string name = Path.GetFileName(src);
string dest = Path.Combine(destDir, name);
if (File.Exists(dest) || Directory.Exists(dest))
{
// 同名が存在する場合はスキップ
continue;
}
if (_clipboard.Operation == FileOperation.Copy)
{
if (Directory.Exists(src))
{
// ディレクトリ
// 未実装
continue;
}
else if (File.Exists(src))
{
// ファイル
File.Copy(src, dest, overwrite: false);
}
}
else if (_clipboard.Operation == FileOperation.Cut)
{
if (Directory.Exists(src))
{
// ディレクトリ
Directory.Move(src, dest);
}
else if (File.Exists(src))
{
// ファイル
File.Move(src, dest);
}
}
}
_clipboard.Paths.Clear();
_clipboard.Operation = FileOperation.None;
// 既存の一覧更新処理
SetFileListViewDirectory(_currentDirectory);
}
// コンテキストメニュー表示前処理
void FileContextMenu_Opening(object sender, RoutedEventArgs e)
{
var menu = (ContextMenu)sender;
var copy = (MenuItem)menu.Items[0];
var cut = (MenuItem)menu.Items[1];
var paste = (MenuItem)menu.Items[3];
// メニュー項目の有効/無効設定
// ディレクトリはコピー不可とする
var paths = FileListView.SelectedItems
.Cast<FileItem>()
.Select(x => x.FullPath)
.ToList();
bool hasFile = false;
bool hasDirectory = false;
foreach (var path in paths)
{
if (File.Exists(path))
hasFile = true;
else if (Directory.Exists(path))
hasDirectory = true;
}
copy.IsEnabled =
paths.Count > 0 &&
hasFile &&
!hasDirectory;
cut.IsEnabled =
paths.Count > 0;
paste.IsEnabled =
_clipboard.Operation != FileOperation.None &&
paths.Count == 0;
}
// 右クリックで項目を選択状態にする
void FileListView_MouseRightButtonUp(object sender, MouseButtonEventArgs e)
{
DependencyObject? current = e.OriginalSource as DependencyObject;
while (current != null && current is not ListViewItem)
{
current = VisualTreeHelper.GetParent(current);
}
if (current is ListViewItem item)
{
item.IsSelected = true;
}
}
説明
xamlではFileListView上のコンテキストメニューにコピー・切り取り・貼り付けの項目追加をしています。
C#側で、メニューから呼び出される機能を実装します。
便宜上クリップボードという名称を使っていますが、本物クリップボードではないので、
他のアプリケーションに貼り付けは出来ません。
また、実際試してみて、
File.Copy()やFile.Move()で同名ファイルの場合の処理を書いていませんでした。
例外が発生していたので、
とりあえず、同名が存在する場合はスキップする回避をしています。
このような想定外な操作が、まだまだ沢山存在していると思われます。
操作はエクスプローラ準拠としたいところですが、開発者の力量的に難しいので、
操作してみて問題が発生したら都度対応になります。


コメント