概要
本記事では、WPF においてエラーメッセージやログなどの表示専用テキストを、編集不可のまま選択・コピー可能に表示する方法を扱う。
TextBlock は表示用途に適している一方、標準では部分選択やコピーを前提としたコントロールではない。
この要件に対しては、TextBox を読み取り専用で使用し、見た目を調整する構成が有効である。
—
前提・対象環境
- フレームワーク/言語: WPF / C# / XAML
- 対象コントロール・機能:
TextBlock,TextBox - アーキテクチャ: MVVM / コードビハインドのいずれでも適用可能
- 想定用途: エラーメッセージ、ログ、詳細情報の表示
- 検証環境: .NET 10 / Windows 11
本記事の表は、上記の環境で実際に計測したものである。
計測の条件は、それぞれの表の説明に書いた。
この環境で確認しているのは次の点である。
TextBlockには、テキストを選択する API が無い。IsReadOnlyのTextBoxでは、テキストを選択できる。- 見た目を
TextBlockに寄せる設定を加えても、選択できる。 IsTabStopを切っても、フォーカス可能なままである。
問題
エラーメッセージや詳細情報を画面に表示する場面では、内容をユーザーに編集させたくない一方で、表示中のテキストをコピーしたい要件がある。
全文コピー用のボタンを別途配置する方法もあるが、実際には一部だけを選択してコピーしたいケースも多い。
このため、表示専用でありながら、テキストの選択とコピーに対応した 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 を利用する。
そのうえで、以下の設定を適用する。
IsReadOnly="True"
編集を禁止しつつ、選択とコピーを可能にするBackground="Transparent"
背景を透明にするBorderThickness="0"
枠線を消すPadding="0"
内側の余白を消し、TextBlockと同じ位置に文字を置くTextWrapping="Wrap"
長文を折り返して表示する
この構成により、見た目は TextBlock に近いまま、TextBox の選択・コピー機能を利用できる。
実装例
以下の XAML は、エラーメッセージを編集不可かつ選択可能な状態で表示する最小構成である。
TextBlock の置き換え先として利用しやすいように、背景・枠線・内側の余白を除去している。読み取り専用時のキャレットは、IsReadOnlyCaretVisible の既定値が False のため、指定しなくても表示されない。
<TextBox
Text="{Binding ErrorMessage}"
IsReadOnly="True"
Background="Transparent"
BorderThickness="0"
Padding="0"
TextWrapping="Wrap" />
この設定により、表示内容は編集できないが、ユーザーは必要な範囲を選択してコピーできる。
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 を表示して測った結果。
注意点
TextBoxはTextBlockよりも入力コントロールとしての性質が強いため、既定スタイルによっては余白やフォーカス時の見た目が異なる場合がある。- 読み取り専用の
TextBoxは、既定ではキャレットを表示しない。IsReadOnlyCaretVisibleの既定値がFalseのため、IsReadOnly="True"だけでよい。キーボードで範囲選択させたいなど、キャレットを見せたい場合だけTrueにする。 - デザイン上の統一が必要な場合は、
PaddingやFocusable、スタイル定義もあわせて調整する構成が適する。
代替案・比較
| 方法 | メリット | デメリット | 適するケース |
|---|---|---|---|
TextBlock をそのまま使う |
軽量で表示用途に適する | 標準では選択・コピーを前提にしにくい | 単純なラベル表示 |
TextBox を読み取り専用で使う |
選択・コピーに対応でき、TextBlock の代替として扱いやすい |
見た目調整が必要になる | エラー表示、ログ表示、共有用テキスト表示 |
| コピーボタンを別途配置する | ワンクリックで全文コピーできる | 一部分だけのコピーには向かない | 定型メッセージや識別子の全文コピー |
まとめ
WPF で、編集は不可としつつテキストの選択・コピーを可能にしたい場合は、TextBlock の代わりに TextBox を読み取り専用で利用する方法が有効である。
IsReadOnly="True" により編集を防げる。IsReadOnlyCaretVisible の既定値は False のため、入力できるように見せるキャレットも表示されない。
さらに、背景や枠線を調整することで、TextBlock と同じような表示用途で扱いながら、選択・コピーに対応できる。
表示専用でありながらコピー性が求められる場面では、この構成を基本パターンとして採用するのが適切である。
—
- TextBox(WPF 標準コントロールデモアプリ):
IsReadOnlyが入力を無視しても、選択とコピーはできることを実測したページ