概要
WPF の Label コントロールに _ (アンダーバー)を含む文字列を設定すると、その文字が画面に表示されず消えてしまうことがある。
これは WPF の仕様によるものであり、原因と代表的な回避方法を整理する。
前提・対象環境
- フレームワーク/言語: WPF / C# / XAML
- 対象コントロール:
Label - アーキテクチャ: MVVM / コードビハインドのいずれでも発生する
- 前提知識: WPF 基本操作、XAML の基礎
問題
Label の Content にアンダーバーを含む文字列(例: my_variable)を設定したとき、画面には myvariable のようにアンダーバーが欠落した状態で表示される。
また、_F のように書くと F に下線が付いた状態で表示されることがある。
バインドしている文字列データにアンダーバーが含まれている場合でも同様に消えてしまうため、動的なデータ表示でも問題が発生する。
原因・背景
Label は内部で AccessText というコントロールを使ってテキストを描画している。
AccessText はアンダーバーをアクセスキー(Alt キーと組み合わせてフォーカス移動を行うショートカット機能)の目印として解釈する。
具体的な挙動は以下のとおりである。
| 入力文字列 | 画面上の表示 | 解釈 |
|---|---|---|
_File |
File | F がアクセスキーとして登録される |
my_var |
myvar | v がアクセスキーとして登録される |
name_ |
name | アンダーバーが末尾で消える |
このため、データにアンダーバーが含まれているだけで、意図しない表示崩れが起きる。
解決方法
回避方法は主に 3 つある。用途に合わせて使い分けることを推奨する。
- アンダーバーをエスケープしたいだけなら、
__と 2 つ重ねて書く(方法 1)。 - アクセスキー機能が不要で単純にテキストを表示したい場合は、
TextBlockに変更する(方法 2)。 Labelのまま動的なバインドデータを正しく表示したい場合は、ContentTemplateでTextBlockを使う(方法 3)。
実装例
方法 1:アンダーバーを 2 つ重ねてエスケープする
__ と 2 つ続けて書くことで、画面に 1 つのアンダーバーが表示される。
XAML 側で静的に文字列を設定しているケースに向いている。
<Label Content="my__variable" />
- メリット: XAML を 1 箇所修正するだけで対応できる。
- デメリット: 動的バインドのデータにアンダーバーが含まれている場合、ViewModel 側で置換処理が必要になる。
方法 2:TextBlock コントロールに変更する
アクセスキー機能や Target プロパティによるフォーカス制御が不要であれば、Label を TextBlock に変更するのがベストプラクティスである。
TextBlock は AccessText を使わないため、アンダーバーをそのまま表示できる。
<TextBlock Text="my_variable" />
バインドの場合も同様にそのまま動作する。
<TextBlock Text="{Binding VariableName}" />
- メリット: アンダーバーを気にする必要がなくなる。
Labelより軽量で、表示専用であればTextBlockが適切である。 - デメリット:
LabelのTargetプロパティによるフォーカス制御は使えなくなる。
方法 3:ContentTemplate で TextBlock を使う
Label を維持しつつ、動的バインドのデータにアンダーバーが含まれる場合でも正しく表示するには、ContentTemplate に TextBlock を指定する。
これにより、Label の Content を AccessText ではなく TextBlock で描画させることができる。
<Label Content="{Binding VariableName}">
<Label.ContentTemplate>
<DataTemplate>
<TextBlock Text="{Binding}" />
</DataTemplate>
</Label.ContentTemplate>
</Label>
アプリ全体で統一したい場合は、このテンプレートをスタイルとして定義する方法もある。
<Style x:Key="PlainLabel" TargetType="Label">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<TextBlock Text="{Binding}" />
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
- メリット: 動的バインドのデータにアンダーバーが含まれても、そのまま表示できる。スタイルとして共通化すれば複数箇所への適用も容易である。
- デメリット: XAML のコード量が増える。
注意点
Targetプロパティを使ってフォーカス移動を行いたい場合、アクセスキーの仕組みは有効に保つ必要があるため、方法 2 や方法 3 は適していない。その場合は方法 1(エスケープ)を使う。- 方法 3 で
ContentTemplateを使うと、Contentが文字列以外のオブジェクトであるケースにも適用されるため、バインドしているデータ型がstringであることを確認してから適用する。 - ViewModel 側で文字列を処理する場合、
_を__に置換する方法もあるが、View 側のルールを ViewModel に持ち込む構成になるため避けることを推奨する。
代替案・比較
| 方法 | メリット | デメリット | 適するケース |
|---|---|---|---|
方法 1: __ でエスケープ |
XAML 1 箇所の修正で簡単 | 動的データには ViewModel 側の処理が必要 | 静的な文字列に個別で対処したい場合 |
方法 2: TextBlock に変更 |
最もシンプルで軽量 | Label の Target 機能は使えない |
表示専用でアクセスキーが不要な場合 |
方法 3: ContentTemplate を変更 |
動的バインドにも対応できる | コード量が増える | Label を維持しつつ動的表示が必要 |
まとめ
WPF の Label でアンダーバーが消えるのは、内部の AccessText がアンダーバーをアクセスキーの目印として解釈するためである。
- 表示専用のテキストであれば、
TextBlockへの変更(方法 2)が最もシンプルで適切な対応である。 - どうしても
Labelを使う必要があり、静的な文字列であれば__でエスケープ(方法 1)が手軽である。 - 動的バインドのデータにアンダーバーが含まれる場合は、
ContentTemplateでTextBlockを使う(方法 3)。
用途に応じて上記 3 つを使い分けることで、アンダーバーの表示崩れを確実に回避できる。