WPFで編集不可のままテキストを選択・コピー可能に表示する方法

TextBlock は部分選択やコピーを前提としていない。WPF で表示専用のテキストを編集不可のまま選択・コピー可能にするため、TextBox を IsReadOnly で使い、キャレットと枠線を抑えて TextBlock と同じ見た目に整える方法を解説する。

概要

本記事では、WPF においてエラーメッセージやログなどの表示専用テキストを、編集不可のまま選択・コピー可能に表示する方法を扱う。
TextBlock は表示用途に適している一方、標準では部分選択やコピーを前提としたコントロールではない。
この要件に対しては、TextBox を読み取り専用で使用し、見た目を調整する構成が有効である。
—

前提・対象環境

本記事の表は、上記の環境で実際に計測したものである。
計測の条件は、それぞれの表の説明に書いた。
この環境で確認しているのは次の点である。


問題

エラーメッセージや詳細情報を画面に表示する場面では、内容をユーザーに編集させたくない一方で、表示中のテキストをコピーしたい要件がある。
全文コピー用のボタンを別途配置する方法もあるが、実際には一部だけを選択してコピーしたいケースも多い。
このため、表示専用でありながら、テキストの選択とコピーに対応した UI が必要となる。
—

原因・背景

TextBlock は軽量な表示専用コントロールであり、静的なテキスト表示には適している。
ただし、標準の TextBlock は入力コントロールではないため、TextBox のような操作感でテキストを選択・コピーする用途には向いていない。
一方、TextBox は本来入力用のコントロールであるが、IsReadOnly="True" を設定することで編集を禁止しつつ、テキストの選択とコピーを可能にできる。
さらに、背景や枠線、キャレット表示を調整することで、見た目を TextBlock に近づけられる。
このため、表示用途では TextBlock の代替として同じように扱うことができる。
—

表示専用の候補ごとに、テキストを選択できるかとフォーカスの扱いを測った結果が次の表である。

コントロール SelectAll() で選択されるもの Focusable IsTabStop
TextBlock 選択の API が無い False True
TextBox IsReadOnly=True selectable text True True
+ 枠なし、背景を透明に selectable text True True
+ IsTabStop=False selectable text True False

.NET 10 / Windows 11 で、同じ文字列を各候補に与えて測った結果。「SelectAll() で選択されるもの」は SelectAll() の後に SelectedText から読み出した内容である。

TextBlock は Focusable が False である。 選択の API を持たないだけでなく、フォーカスも受け取らない。
TextBox に変えると選択でき、枠と背景を消しても選択できることは変わらない。見た目と機能は独立している。

最終行が示すとおり、IsTabStop を False にしても Focusable は True のままである。
Tab キーの巡回からは外れるが、Focusable は True のままで、選択の API も使える。表示専用の見た目に寄せつつ選択を残したい場合は、この組み合わせになる。


解決方法

表示専用のテキストを選択可能にしたい場合は、TextBlock の代わりに TextBox を利用する。
そのうえで、以下の設定を適用する。

この構成により、見た目は TextBlock に近いまま、TextBox の選択・コピー機能を利用できる。


実装例

以下の XAML は、エラーメッセージを編集不可かつ選択可能な状態で表示する最小構成である。
TextBlock の置き換え先として利用しやすいように、背景・枠線・内側の余白を除去している。読み取り専用時のキャレットは、IsReadOnlyCaretVisible の既定値が False のため、指定しなくても表示されない。

<TextBox
    Text="{Binding ErrorMessage}"
    IsReadOnly="True"
    Background="Transparent"
    BorderThickness="0"
    Padding="0"
    TextWrapping="Wrap" />

この設定により、表示内容は編集できないが、ユーザーは必要な範囲を選択してコピーできる。

同じ例外メッセージを TextBlock と読み取り専用 TextBox で表示した画面。TextBox 側だけ例外の型名の部分が選択範囲としてハイライトされている。
同じ文字列を TextBlock(上)と本節の設定を適用した読み取り専用 TextBox(下)で表示したもの。下では例外の型名の部分だけを範囲選択している。背景と枠線を除いているため、選択していない状態の見た目は TextBlock とほぼ変わらない。

また、見た目も TextBlock に近いため、既存の表示用テキストを TextBox に置き換える形で適用しやすい。
長文の表示を前提とする場合は、縦のスクロールバーを加えると実用性が高まる。
以下はその例である。

<TextBox
    Text="{Binding ErrorMessage}"
    IsReadOnly="True"
    Background="Transparent"
    BorderThickness="0"
    Padding="0"
    TextWrapping="Wrap"
    VerticalScrollBarVisibility="Auto" />

VerticalScrollBarVisibility="Auto" により、表示領域を超えた内容はスクロールバーで確認できる。改行を含む文字列の表示に AcceptsReturn は要らない。AcceptsReturn は Enter キーで改行を入れるかどうかを決めるだけで、読み取り専用の TextBox に 3 行の文字列を表示すると、False でも True でも 3 行で表示された。

条件 計測値
IsReadOnlyCaretVisible の既定値 False
IsReadOnly, AcceptsReturn=False LineCount 3、高さ 201
IsReadOnly, AcceptsReturn=True LineCount 3、高さ 201

.NET 10 / Windows 11 で、IsReadOnlyCaretVisible の既定値を読み、読み取り専用の TextBox に line1〜line3 を表示して測った結果。


注意点


代替案・比較

方法 メリット デメリット 適するケース
TextBlock をそのまま使う 軽量で表示用途に適する 標準では選択・コピーを前提にしにくい 単純なラベル表示
TextBox を読み取り専用で使う 選択・コピーに対応でき、TextBlock の代替として扱いやすい 見た目調整が必要になる エラー表示、ログ表示、共有用テキスト表示
コピーボタンを別途配置する ワンクリックで全文コピーできる 一部分だけのコピーには向かない 定型メッセージや識別子の全文コピー

まとめ

WPF で、編集は不可としつつテキストの選択・コピーを可能にしたい場合は、TextBlock の代わりに TextBox を読み取り専用で利用する方法が有効である。
IsReadOnly="True" により編集を防げる。IsReadOnlyCaretVisible の既定値は False のため、入力できるように見せるキャレットも表示されない。
さらに、背景や枠線を調整することで、TextBlock と同じような表示用途で扱いながら、選択・コピーに対応できる。
表示専用でありながらコピー性が求められる場面では、この構成を基本パターンとして採用するのが適切である。
—