WPFでTextBoxの文字数がオーバーした時に文字色変更したい

タグの編集
投稿者 あさ  (社会人) 投稿日時 2023/8/31 14:20:45
WPFで文字数の入力制限を行っています。
例えば10バイト以上入力した際に超えた分の文字色のみ色を変えたいです。
ツイッターの文字数オーバーした時のイメージです。
動的に一部の色変更することできますでしょうか
投稿者 魔界の仮面弁士  (社会人) 投稿日時 2023/8/31 16:12:08
> WPFで文字数の入力制限を行っています。
> 例えば10バイト以上入力した際に
「文字数」なのか「バイト数」なのかハッキリしないですね…?

たとえば "👩‍👩‍👧‍👧" という結合絵文字は、この 1 文字だけで
 System.Char だと 11 個
 System.Text.Rune だと 7 個
から構成されており、これをバイト数で数えると
 UTF-16 だと 22 バイト
 UTF-8 だと 25 バイト
という換算になります。


> 超えた分の文字色のみ色を変えたいです。
> ツイッターの文字数オーバーした時のイメージです。
あれは文字色ではなく背景色だったかと思います。(おそらくは、カラー絵文字への配慮のため)

文字色にせよ文字背景色にせよ、文字単位で色を変えるとなると、
TextBox では対応できないと思います。
RichTextBox を用いるか、もしくは TextBlock を使うことになるでしょう。

単純に文字数で良いのなら、こんな感じで切り出して、
これらの Inline を RichTextBox なり TextBlock なりに出力するとか。
Dim text As String = 入力された文字列
Dim s1 As String = Strings.Left(text, 10)  '先頭10文字 
Dim s2 As String = Strings.Mid(text, 11)  'それ以降の超過分 
Dim normalText As Inline = New Run(s1) 
Dim overText As Inline = New Run(s2) With {.Background = New SolidColorBrush(Colors.Yellow)}
投稿者 あさ  (社会人) 投稿日時 2023/8/31 17:09:31
ありがとうございます。
入力はできるようにしないといけないので
RichTextBoxに変更したら
Text="{Binding Word}"
TextWrapping="Wrap"
が使えないのですね。。

RichTextBoxはなかなかやっかいですねえ
投稿者 魔界の仮面弁士  (社会人) 投稿日時 2023/8/31 19:20:18
入力用に「透明な TextBox」を用意しておき

<TextBox
  TextWrapping="Wrap"
  Background="Transparent"
  Foreground="Transparent"
  Text="{Binding Word, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />


それを表示用の TextBlock の座標に重ねて配置してみるのはどうでしょう。

<TextBlock TextWrapping="Wrap"
  ><Run Text="{Binding WordNormal, Mode=OneWay}"
  /><Run Text="{Binding WordOver, Mode=OneWay}" Background="Yellow"
/></TextBlock>
投稿者 あさ  (社会人) 投稿日時 2023/9/1 10:37:45
重ねてみて期待通りの動きができていたのですが
透明なため、入力候補が出ている時は文字が透明なままになってしまいます。
確定すれば表示されますが。。
投稿者 (削除されました)  () 投稿日時 2023/9/1 15:02:48
(削除されました)
投稿者 魔界の仮面弁士  (社会人) 投稿日時 2023/9/1 16:45:20
であれば単純に、両者の前景色を入れ替えてみては如何でしょう?

TextBox が入力担当&前景表示担当で、
TextBlock が背景表示担当という組み合わせです。

<TextBlock
  TextWrapping="Wrap"
  Foreground="Transparent"
  ><Run  Text="{Binding WordNormal, Mode=OneWay}"
  /><Run Text="{Binding WordOver, Mode=OneWay}" Background="Yellow"
/></TextBlock>

<TextBox
  TextWrapping="Wrap"
  Background="Transparent"
  Foreground="Blue"
  Text="{Binding Word, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />



文字列が長い場合はスクロールに注意が必要かもしれません。

あと、Word プロパティの文字列を WordNormal / WordOver プロパティへ切り出す場合、
分割基準が文字数であれバイト数であれ、分割位置が泣き別れにならないように注意。
たとえば vbCrLf が vbCr と vbLf で別れてしまうパターンとか、
その他、サロゲートペア、結合文字列、合字、異体字セレクタなど。