バイト長で制限したいときもある(参)。

バイト長制限においてやっかいなのが、日本語入力。日本語入力時、PreviewKeyDownのイベントデータにある「e.Key」の値がすべて「ImeProcess」になるため、それでは防げなかった。
なので、バイト長で制限したいときもある(壱)にも書いたように、TextCompositionManagerクラスを使うことにした。TextChangedイベントを使うタイプは、そのイベント内で下手にTextプロパティをいじると落ちる事があるので今回は採用しない。
namespace TawamureDays {

class LengthAsBytePropertyWorker : AttachedPropertyWorker {

/// <summary>
/// 初期化処理を行います。
/// </summary>
protected override void OnWorkerSet() {

var textBox = this.AttachedElement as TextBox;
TextCompositionManager.AddPreviewTextInputHandler(
textBox,
LengthAsBytePropertyWorker.OnPreviewTextInput);
return;
}

/// <summary>
/// 内部リソースを解放します。
/// </summary>
/// <param name="disposing">false:アンマネージリソースのみ解放します。</param>
protected override void OnDispose(bool disposing) {
if (this.IsDisposed) {
return;
}

//後始末
if (disposing) {
var textBox = this.AttachedElement as TextBox;
TextCompositionManager.RemovePreviewTextInputHandler(textBox,
LengthAsBytePropertyWorker.OnPreviewTextInput);
}
}

/// <summary>
/// TextComposition完了イベント用ハンドラ<br/>
/// 入力確定時のTextChangedイベントの前に発生します。<br/>
/// </summary>
/// <param name="sender">イベント発生元</param>
/// <param name="e">イベントデータ</param>
private static void OnPreviewTextInput(object sender, TextCompositionEventArgs e) {
var textBox = sender as TextBox;

return;
}
}
}

○OnPreviewTextInputメソッド内の実装ポイント:
・入力中の文字列のバイト数と、現在入力済のテキストが制限バイト数を超えないようにする。
・選択中のテキストがあれば、それと入れ替わるので、バイト数の計算に注意する。
・設定後のカーソルの位置も自分で調整する。
・注意点:
 日本語入力のONとOFFによってイベントデータ(e)の内容が異なる。
 日本語入力ON:textBox.Text内に入力中の文字列が既に含まれている。
 日本語入力OFF:textBox.Text内に入力中の文字列は含まれていない。
 入力中の文字列は、イベントデータ(e)のTextプロパティ内にあるので、それを使う。なお、日本語入力の状態(ON/OFF)は、InputMethod.Current.ImeStateでチェックできる。
/// <summary>
/// TextComposition完了イベント用ハンドラ<br/>
/// 入力確定時のTextChangedイベントの前に発生します。<br/>
/// </summary>
/// <param name="sender">イベント発生元</param>
/// <param name="e">イベントデータ</param>
private static void OnPreviewTextInput(object sender, TextCompositionEventArgs e) {
var textBox = sender as TextBox;

if (!TextBoxBehavior.GetLengthAsByte(textBox)) {
//バイト数による制限をしない
return;
}

//追加分のバイト数
var currentByteLength = Utils.GetByteLength(textBox.Text);
//制限したいバイト数。コレ以上は入力させない。
var maxByteLength = textBox.MaxLength;
//入力中のテキスト(文字列)
var appendByteLength = Utils.GetByteLength(e.Text);
//現在入力済のテキスト(文字列)
var tempText = textBox.Text;
//選択中のテキストのバイト数
var selectedByteLength = Utils.GetByteLength(textBox.SelectedText);
//IME(日本語入力)がONかどうか。
var isIMEOn = InputMethod.Current.ImeState == InputMethodState.On;

if (isIMEOn && currentByteLength > maxByteLength) {
//IME=ON...textBox.Text内に既に含まれているので、そのバイト数でチェックする
//現在のカレット位置
var caretIndex = textBox.CaretIndex;

if (caretIndex == e.Text.Length) {
//先頭に追加された。
//追加された文字列分をカットします。
textBox.Text = textBox.Text.Substring(e.Text.Length);
//カーソル位置は先頭に
textBox.Select(0, 0);

} else if (caretIndex == textBox.Text.Length) {
//最後尾に追加されました。
//追加された文字列分をカットします。
textBox.Text = textBox.Text.Substring(0, textBox.Text.Length - e.Text.Length);
//カーソル位置は最後尾に
textBox.Select(textBox.Text.Length, 0);

} else {
//文字列間に追加されました。挿入された位置を検索します。
var index = textBox.Text.Substring(0, caretIndex + 1).LastIndexOf(e.Text);

if (index >= 0) {
//該当する文字列を削ってTextに設定し直します。
textBox.Text =
textBox.Text.Substring(0, index) + textBox.Text.Substring(caretIndex);
//カーソル位置を変更します。
textBox.Select(index, 0);
} else {
//あくまで保険で、多分ここは通らない。
textBox.Text = Utils.ToString(tempText);
}
}

} else if (!isIMEOn && (currentByteLength - selectedByteLength + appendByteLength) > maxByteLength) {
//IME=OFF textBox.Textには入力中の文字列は含まれないので、
//textBox.Textとe.Text内の文字列のバイト数を合算してチェックします。
//選択中のテキストは差し替えられるので、その分のバイト数は引かれます。
//入力された文字は含まれないので、以下のイベント伝播をキャンセルするだけでOK
e.Handled = true;
return;
}

return;
}
直接入力に関してはこれで防ぐことができる。がしかし、まだ課題は残っている。
次は、文字列の貼り付けに対して制限を掛ける。
続く。
スポンサーサイト
当サイトは基本をすっ飛ばしてます。基本文法等は、@ITをどうぞ
カテゴリー: WPF4 | コメント: 0 | トラックバック: 0


この記事へのコメント

コメントの投稿

非公開コメント


サイドバー背後固定表示サンプル

当ブログに書かれたソースコードは流用自由です。

バグ、スペルミス等はありうる事です。

ご利用の際は自己責任でお願いしますm(_ _)m