文字列のコピーをサポートするには?[Win 8]:WinRT/Metro TIPS
Windowsストア・アプリに表示しているテキスト・データをユーザーにコピーさせたい場合、クリップボードにコピーすればよい。その実装方法を説明する。
powered by Insider.NET
Windowsストア・アプリに表示しているデータをユーザーにコピーさせたいことがある。テキストボックスであれば、クリップボードへのコピー操作がサポートされている。マウス操作では、文字列を選択して[Ctrl]+[C]キーか、あるいは右クリックで出てくるコンテキスト・メニューの[コピー]を使えばよい。タッチ操作でも、文字列を1回タップすればグリッパーが出てくるので、それを使って範囲を指定し、もう1度タップして出てきたコンテキスト・メニューからコピーを選べばよい(下に掲載した参考画像を参照)。
それではテキストボックス以外のコントロールでは、どうしたらよいだろうか? 本稿では、Windowsストア・アプリで文字列のコピーを実装する方法を説明する。本稿のサンプルは「Windows Store app samples:MetroTips #16」からダウンロードできる。
参考画像:テキストボックスの文字列をタッチ操作でコピーする様子
(上)タッチ操作のとき、テキストボックスの文字列にタップするとグリッパー(丸いハンドル)が出る。グリッパーをドラッグすることで、文字列の選択範囲を変更できる。
(下)文字列が選択されている部分かグリッパーをタップすると、コンテキスト・メニューが出る。[コピー]を選べば、選択した文字列がクリップボードにコピーされる。
●事前準備
Windows 8(以降、Win 8)向けのWindowsストア・アプリを開発するには、Win 8とVisual Studio 2012(以降、VS 2012)が必要である。これらを準備するには、第1回のTIPSを参考にしてほしい。本稿では64bit版Win 8 ProとVS 2012 Express for Windows 8を使用している。
なお、今回のソース・コードはVS 2012の「グリッド アプリケーション (XAML)」テンプレートをベースにしている。本稿中に登場するソース・コードのファイル名などは、そのテンプレートで自動生成されたものである。
●TextBlockなどをコピー可能にするには?
TextBlockコントロールやRichTextBlockコントロールなどの文字列を表示するためのコントロールは、クリップボードへのコピー操作をサポートしている。IsTextSelectionEnabledプロパティをTrueに設定するだけで、コピーが可能になるのだ。
例として、ItemDetailPage.xamlファイル中のRichTextBlockコントロールで試してみよう。
<common:RichTextColumns x:Name="richTextColumns" Margin="117,0,117,47">
<RichTextBlock x:Name="richTextBlock" Width="560"
Style="{StaticResource ItemRichTextStyle}"
IsTextSelectionEnabled="True"><!-- False から True に変更 -->
<Paragraph>
<Run FontSize="26.667" ……以下略……
ItemDetailPage.xamlファイル中のRichTextBlockコントロールをコピー可能にする(70行目付近)
実行してみると、テキストボックスと同じようにコピー操作ができるようになっている(次の画像)。なお、入力が不可能なコントロールなので、タッチ・キーボードは表示されないし、コンテキスト・メニューにも[貼り付け]などはない。
●GridViewでも同じようにすると……?
では、GridViewコントロールやListViewコントロールなどでも同様にすればよいだろうか? そのItemTemplateの中にあるTextBlockコントロールなどのIsTextSelectionEnabledプロパティをTrueにすれば、コピー可能になるはずだ。
確かにそれでコピーできるようになる。例として、筆者がストアに公開しているアプリを挙げておこう(次の画像)。
GridViewコントロールの中にあるTextBlockコントロールでも、IsTextSelectionEnabledプロパティをTrueにすれば、TextBoxコントロールと同様なコピー操作が可能になる(筆者のアプリの一部分)
しかしこの方法は、重大な問題を引き起こすことがある。GridViewコントロールのアイテムをタップ/クリックしてほかの画面へ遷移するアプリでは、文字列部分をタップ/クリックするとコピー操作になってしまって遷移できなくなるのだ。上のアプリのように画面遷移がないのであれば構わないが、そうでないならばこの方法は使えない。
MSDNのガイドラインでは、タップの長押しやマウスの右クリックでコンテキスト・メニューを出すのがよいとされている。以下、その方法を順番に説明していく。
●コードから文字列をクリップボードにコピーするには?
Windows.ApplicationModel.DataTransfer名前空間のDataPackageクラスとClipboardクラスを使って、次のようなコードを記述すればよい。
// 文字列をクリップボードにコピーする
private void CopyToClipBoard(string content)
{
var package = new Windows.ApplicationModel.DataTransfer.DataPackage();
package.SetText(content);
Windows.ApplicationModel.DataTransfer.Clipboard.SetContent(package);
}
' 文字列をクリップボードにコピーする
Private Sub CopyToClipBoard(content As String)
Dim package = New Windows.ApplicationModel.DataTransfer.DataPackage()
package.SetText(content)
Windows.ApplicationModel.DataTransfer.Clipboard.SetContent(package)
End Sub
文字列をクリップボードにコピーするコード(上:C#、下:VB)
●コードからコンテキスト・メニューを出すには?
Windows.UI.Popups名前空間のPopupMenuクラスを使う。個々のメニューには、UICommandクラス(同じくWindows.UI.Popups名前空間)を使う。UICommandには、メニューとして表示する文字列と、選択されたときに実行するコードを一緒にセットする。なお、PopupMenuクラスのShowForSelectionAsyncメソッドを使ってコンテキスト・メニューを出す際には、対象とするコントロールの領域を引数として与えねばならない。
// コンテキスト・メニューを出し、クリックされたらコピーを実行する
private async void ShowCopyPopup(string content, FrameworkElement target)
{
// コンテキスト・メニューを作成する
var menu = new Windows.UI.Popups.PopupMenu();
menu.Commands.Add(
new Windows.UI.Popups.UICommand(
"コピー", (cmd) => {
// [コピー]が選択されたときに実行されるコード
CopyToClipBoard(content);
})
);
// コンテキスト・メニューを表示する
await menu.ShowForSelectionAsync(GetElementRect(target));
}
// UIコントロールの領域を算出する
private static Rect GetElementRect(FrameworkElement element)
{
GeneralTransform buttonTransform = element.TransformToVisual(null);
Point point = buttonTransform.TransformPoint(new Point());
return new Rect(point, new Size(element.ActualWidth, element.ActualHeight));
}
Private Async Sub ShowCopyPopup(content As String, target As FrameworkElement)
' コンテキスト・メニューを作成する
Dim menu = New Windows.UI.Popups.PopupMenu()
menu.Commands.Add(
New Windows.UI.Popups.UICommand(
"コピー", Sub(cmd)
' [コピー]が選択されたときに実行されるコード
CopyToClipBoard(content)
End Sub
)
)
' コンテキスト・メニューを表示する
Await menu.ShowForSelectionAsync(GetElementRect(target))
End Sub
' UIコントロールの領域を算出する
Private Shared Function GetElementRect(element As FrameworkElement) As Rect
Dim buttonTransform As GeneralTransform = element.TransformToVisual(Nothing)
Dim point As Point = buttonTransform.TransformPoint(New Point())
Return New Rect(Point, New Size(element.ActualWidth, element.ActualHeight))
End Function
コンテキスト・メニューを出し、クリックされたらコピーを実行するコード(上:C#、下:VB)
●長押し/右クリックでコピーできるようにするには?
例としてGroupDetailPage画面でやってみよう。まずXAMLコードでイベント・ハンドラを設定する。そのためには、StandardStyles.xamlファイル中の「Standard500x130ItemTemplate」という名称のDataTemplateを、GroupDetailPage.xamlファイルの<Page.Resources>要素内にコピーし、新しい名前を付ける(ここでは「MyItemTemplate」とした)。次に、GroupDetailPage.xamlファイル内で「Standard500x130ItemTemplate」を使っている箇所を探し、「MyItemTemplate」に変更する。
そうしたら、次のコードのようにMyItemTemplateの中のGridコントロールにイベント・ハンドラを2つ設定する。Holdingイベント(=長押し)とRightTappedイベント(=右クリック)である。
<DataTemplate x:Key="MyItemTemplate">
<Grid Height="110" Width="480" Margin="10"
Holding="itemGridView_Holding" RightTapped="itemGridView_RightTapped">
<Grid.ColumnDefinitions ……以下略……
ItemTemplateとして使うDataTemplateに含まれるGridコントロールに、イベント・ハンドラを2つ設定した
このイベントはItemTemplate内のコントロールに付けたので、GridViewコントロールの全体ではなく、個々のアイテムごとに発生することに注意してほしい。また、このGridコントロールにバインドされるデータも、SampleDataSourceオブジェクトの全体ではなく、その中の個々のSampleDataItemオブジェクトである。
イベント・ハンドラの実装は次のようになる。注意点が2つある。
まず、Holdingイベントは、「開始」(=Started)と「終了」(=Completed)(または「キャンセル」(=Canceled))の2回発生することだ。今回は、開始時だけコンテキスト・メニューを出せばよい。もう1点は、Holdingイベントが終わった後に続いてRightTappedイベントも発生することだ。長押しすると、HoldingイベントもRightTappedイベントも発生するので、重複して処理を実行してしまわないような対策が必要だ。
// GridView のアイテムが長押しされたときのイベント・ハンドラ
private void itemGridView_Holding(object sender, HoldingRoutedEventArgs e)
{
if (e.HoldingState != Windows.UI.Input.HoldingState.Started)
return; // Completed や Canceled のときは何もしない
// コピーのコンテキスト・メニューを出す
ShowCopyPopup(sender as Grid, e.OriginalSource as FrameworkElement);
e.Handled = true;
}
// GridView のアイテムが右クリックされたときのイベント・ハンドラ
private void itemGridView_RightTapped(object sender, RightTappedRoutedEventArgs e)
{
// Holding の後にも RightTapped が発生するので、二重実行しない対策が必要
if (e.PointerDeviceType != Windows.Devices.Input.PointerDeviceType.Mouse)
return; // Touch のときは、itemGridView_Holding で実行済み
// コピーのコンテキスト・メニューを出す
ShowCopyPopup(sender as Grid, e.OriginalSource as FrameworkElement);
e.Handled = true;
}
// コンテキスト・メニューを出し、クリックされたらコピーを実行する
private void ShowCopyPopup(Grid gridInItem, FrameworkElement target)
{
// ItemTemplate の Grid にバインドされている DataContext は SampleDataItem
SampleDataItem selected = gridInItem.DataContext as SampleDataItem;
// クリップボードへコピーする内容
var content = string.Format(
@"{0}
{1}
{2}
{3}",selected.Group, selected.Title, selected.Subtitle, selected.Description);
// コンテキスト・メニューを作成して表示する
ShowCopyPopup(content, target);
}
' GridView のアイテムが長押しされたときのイベント・ハンドラ
Private Sub itemGridView_Holding(sender As Object, e As HoldingRoutedEventArgs)
If (e.HoldingState <> Windows.UI.Input.HoldingState.Started) Then
Return ' Completed や Canceled のときは何もしない
End If
' コピーのコンテキスト・メニューを出す
ShowCopyPopup(DirectCast(sender, Grid), _
DirectCast(e.OriginalSource, FrameworkElement))
e.Handled = True
End Sub
' GridView のアイテムが右クリックされたときのイベント・ハンドラ
Private Sub itemGridView_RightTapped(sender As Object, e As RightTappedRoutedEventArgs)
' Holding の後にも RightTapped が発生するので、二重実行しない対策が必要
If (e.PointerDeviceType <> Windows.Devices.Input.PointerDeviceType.Mouse) Then
Return ' Touch のときは、itemGridView_Holding で実行済み
End If
' コピーのコンテキスト・メニューを出す
ShowCopyPopup(DirectCast(sender, Grid), DirectCast(e.OriginalSource, FrameworkElement))
e.Handled = True
End Sub
' コンテキスト・メニューを出し、クリックされたらコピーを実行する
Private Sub ShowCopyPopup(gridInItem As Grid, target As FrameworkElement)
' ItemTemplate の Grid にバインドされている DataContext は SampleDataItem
Dim selected As Data.SampleDataItem _
= DirectCast(gridInItem.DataContext, Data.SampleDataItem)
' クリップボードへコピーする内容
Dim content = selected.Group.ToString() + vbCrLf _
+ selected.Title + vbCrLf _
+ selected.Subtitle + vbCrLf _
+ selected.Description
' コンテキスト・メニューを作成して表示する
ShowCopyPopup(Content, target)
End Sub
長押し/右クリックでコンテキスト・メニューを出し、クリックされたらコピーを実行するコード(上:C#、下:VB)
これで完成である。
●実行結果
以上を実装して実行してみると、次の画像のようになる。
タップの長押し/マウスの右クリックで[コピー]というコンテキスト・メニューが現れ、それを選択するとアイテムのタイトルや説明がクリップボードにコピーされる。その内容は、テキスト・エディタなどに切り替えてペーストして確認できる(冒頭の参考画像に見えるテキストは、そのようにしてペーストしたものだ)。
●まとめ
クリップボードへの文字列コピーをサポートするには、TextBlockコントロールなど文字列を表示するだけのコントロールでは、IsTextSelectionEnabledプロパティをTrueに設定すればよい。GridViewコントロールなど、タッチ/クリックがコマンド・トリガーになっている場合は、長押し/右クリックでコピーできるように実装する。また、本稿では説明しなかったが、GridViewコントロールなどでアイテムが選択状態にあるときは、キーボード操作[Ctrl]+[C]にも対応させる。
クリップボードへのコピーのガイドラインや、タップ操作/マウス操作については、次のドキュメントを参考にしてほしい。
Copyright© Digital Advantage Corp. All Rights Reserved.