WinUI3アプリでクリップボードに文字列や画像をコピーしたり、クリップボードから貼り付けたりする方法を説明します。C#からクリップボードを使い方法はいくつかありますが、ここではWinRT APIを用いる方法を説明します。
開発環境
- .NET 6.0
- WinUI 3.0 (Windows App SDK 1.0)
- WinRT Build 22000
C#におけるクリップボードへのアクセス
従来からのWindowsFormアプリやWPFアプリではクリップボードはSystem.Windows.Forms.ClipboardクラスやSystem.Windows.Clipboardクラスを用いて実装していましたが、WinUI3アプリではUWPアプリと同様にWinRT APIを用いてクリップボード機能を実装します。WinRT APIではWindows.ApplicationModel.DataTransfer名前空間にクリップボードを含めたデータ移行に関するクラスがまとめられており、ここではその中のClipboardクラスを用います。
文字列のコピー・貼り付けを行う
文字列のコピー・貼り付けの基本
まずは文字列のコピー・貼り付けを行う簡単なアプリを作成してみます。
MainWindow
MainWindow.xaml
<Window ...(省略)...>
<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
<TextBox x:Name="txtBox"/>
<StackPanel Orientation="Horizontal">
<TextBlock Text="クリップボード操作:" VerticalAlignment="Center"/>
<Button Click="CopyButton_Click">コピー</Button>
<Button Click="PasteButton_Click">貼り付け</Button>
</StackPanel>
</StackPanel>
</Window>
MainWindow.xaml.cs
public sealed partial class MainWindow : Window
{
public MainWindow()
{
this.InitializeComponent();
}
// クリップボードへコピーする
private void CopyButton_Click(object sender, RoutedEventArgs e)
{
string txt = txtBox.Text;
DataPackage data = new DataPackage();
data.SetText(txt);
Clipboard.SetContent(data);
}
// クリップボードから貼り付ける
private async void PasteButton_Click(object sender, RoutedEventArgs e)
{
DataPackageView dataPackageView = Clipboard.GetContent();
if (dataPackageView.Contains(StandardDataFormats.Text))
{
string txt = await dataPackageView.GetTextAsync();
txtBox.Text = txt;
}
}
}
これで以下のようにTextBoxに入力した文字列をクリップボードにコピーし、またクリップボードにコピーされた文字列をTextBoxに表示するアプリが作成できました。
クリップボードへのコピーは12行目でDataPackageを作成して、13行目のSetTextメソッドでそこにコピーしたい文字列を登録し、14行目でClipboardのSetContentメソッドでクリップボードに登録することで実行しています。
また、クリップボードからの文字列の取得はClipboardのGetContentメソッドで一番最後にクリップボードに保存されたデータをDataPackageViewオブジェクトとして取得し(20行目)、Containsメソッドで文字列であることを確かめた上で(21行目)、GetTextAsyncメソッドで文字列として取得しています(23行目)。
書式のある文字列をクリップボードから取得する
先ほどはクリップボードのデータをDataPackageViewオブジェクトとして取得して、GetTextAsyncメソッドで文字列として取得していましたが、書式ありの文字列を取得する場合はGetRtfAsyncメソッドを用いることができます。また、ブラウザ上でコピーした文字列をHTMLタグ付きで取得する場合にはGetHtmlFormatAsyncメソッドが用意されています。
画像のコピー・貼り付けを行う
画像のコピー・貼り付けを行うサンプルアプリを作成します。
MainWindow
MainWindow.xaml
<Window ...(省略)...>
<StackPanel Orientation="Vertical" HorizontalAlignment="Center" VerticalAlignment="Center">
<Image Height="300">
<Image.Source>
<BitmapImage x:Name="bitmap"/>
</Image.Source>
</Image>
<TextBlock x:Name="txtBlock"/>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center">
<Button Click="LoadButton_Click">画像取得</Button>
<Button Click="CopyButton_Click">コピー</Button>
<Button Click="PasteButton_Click">貼り付け</Button>
</StackPanel>
</StackPanel>
</Window>
MainWindow.xaml.cs
public sealed partial class MainWindow : Window
{
StorageFile ImageFile { get; set; }
public MainWindow()
{
this.InitializeComponent();
}
// 画像を取得する
private async void LoadButton_Click(object sender, RoutedEventArgs e)
{
var picker = new FileOpenPicker();
picker.FileTypeFilter.Add(".png");
picker.FileTypeFilter.Add(".jpg");
picker.FileTypeFilter.Add(".jpeg");
picker.SuggestedStartLocation = PickerLocationId.PicturesLibrary;
InitializeWithWindow.Initialize(picker, WindowNative.GetWindowHandle(this));
ImageFile = await picker.PickSingleFileAsync();
if (ImageFile != null)
{
RandomAccessStreamReference streamReference = RandomAccessStreamReference.CreateFromFile(ImageFile);
IRandomAccessStreamWithContentType stream = await streamReference.OpenReadAsync();
bitmap.SetSource(stream);
}
}
// クリップボードへコピーする
private void CopyButton_Click(object sender, RoutedEventArgs e)
{
if (ImageFile != null)
{
RandomAccessStreamReference streamReference = RandomAccessStreamReference.CreateFromFile(ImageFile);
DataPackage data = new DataPackage();
data.SetBitmap(streamReference);
Clipboard.SetContent(data);
}
}
// クリップボードから貼り付ける
private async void PasteButton_Click(object sender, RoutedEventArgs e)
{
DataPackageView dataPackageView = Clipboard.GetContent();
if (dataPackageView.Contains(StandardDataFormats.Bitmap))
{
RandomAccessStreamReference streamReference = await dataPackageView.GetBitmapAsync();
IRandomAccessStreamWithContentType stream = await streamReference.OpenReadAsync();
bitmap.SetSource(stream);
}
}
}
これで以下のように、「画像取得」ボタンで画像を表示させ、「コピー」ボタンでそれをクリップボードへコピーできるアプリが作成できました。なお、「貼り付け」ボタンでクリップボードにコピーされた画像を表示させることもできます。
「画像取得」ボタンの処理は11-27行目で、ファイル選択ダイアログを用いて画像ファイルをStorageFileオブジェクトとして取得する処理になります。なお、ファイル選択ダイアログの使い方については以下の記事をご覧ください。
画像をクリップボードへコピーする処理は30-39行目です。StorageFileオブジェクトからRandomAccessStreamReferenceクラスのCreateFromFileメソッドを用いて、RandomAccessStreamReferenceオブジェクトへ変換し、それをDataPackageオブジェクトのSetBitmapメソッドでDataPackageに登録します。最後にClipboardクラスのSetContentメソッドでクリップボードにコピーして終了です。
クリップボードの画像をアプリにコピーする処理は42-51行目です。ClipboardクラスのGetContentメソッドでDataPackageViewオブジェクトを取得し、そのContainsメソッドで画像データであることを確かめてからDataPackageViewクラスのGetBitmapAsyncメソッドでRandomAccessStreamReferenceオブジェクトを取得しています。最後にOpenReadAsyncメソッドでストリームに変換しBitmapImageに登録し画像を表示させています。
クリップボードの監視を行う
ClipboardクラスのContentChangedイベントでクリップボードの内容が変更されたら通知することができます。ContentChangedイベントにメソッドを登録することでクリップボードの内容を監視するプログラムを作ってみましょう。
MainWindow
MainWindow.xaml
<Window ...(省略)...>
<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
<TextBlock x:Name="txtBlock"/>
</StackPanel>
</Window>
MainWindow.xaml.cs
public sealed partial class MainWindow : Window
{
public MainWindow()
{
this.InitializeComponent();
Clipboard.ContentChanged += OnContentChanged;
}
// クリップボードの内容が変化したら行う処理
private async void OnContentChanged(object sender, object e)
{
DataPackageView dataPackageView = Clipboard.GetContent();
if (dataPackageView.Contains(StandardDataFormats.Text))
{
string txt = await dataPackageView.GetTextAsync();
txtBlock.Text = txt;
}
}
}
これでクリップボードを監視して、コピーされた文字列を表示するプログラムができました。
クリップボードの内容が変化するとClipboardクラスのContentChangedイベントが呼び出されるので、OnContentChangedメソッドを登録しておきます(6行目)。OnContentChangedメソッドには文字列を貼り付ける際と同じ処理を記述しておき(10-18行目)、TextBlockに表示させています。
コメント