泥庭

2010年10月25日

IMEで変換状態中でもTextBox.TextChangedが発生する

Filed under: WPF — タグ: , , — yone64 @ 4:24 AM

WPFのTextBoxはIMEの変換中でも、TextBox.TextChangedイベントが発生します。
これはこれで便利なのですが、TextBoxへ入力可能な文字種別を制限したい場合などには苦労します。

TextChangedイベント内では、文字列が確定されているかどうかを判断する適切な方法が見つかりませんでした。
とりあえず、ネットで検索していると、TextCompositionManagerを使うとうまくいくかも的な書き込みを発見したのでいろいろと調査してみました。

対象のイベントは4つ。TextBoxの場合、TextInputイベントは発生することはないらしいので(※)、すべてPreview系で統一です。
(※)http://msdn.microsoft.com/ja-jp/library/system.windows.uielement.textinput(v=VS.90).aspx
・TextBox.TextChanged
・TextCompositionManager.PreviewTextInputStart
・TextCompositionManager.PreviewTextInputUpdate
・TextCompositionManager.PreviewTextInput

IME OFFで「ABC」と入力した場合

image
一文字入力ごとに、
・TextCompositionManager.PreviewTextInputStart
・TextCompositionManager.PreviewTextInput
・TextBox.TextChanged
が順番に発生しています。

IME ONで「あい」と入力後、「愛」に変換し、確定した場合

image
こちらは、MS-IMEとATOKで若干挙動が違いました。TSFに対応しているかどうかの違いかもしれません。
共通していることは、IMEを通じて入力し始めたときに、
・TextCompositionManager.PreviewTextInputStart
が発生し、後は、一文字入力または変換のタイミングで、
・TextCompositionManager.PreviewTextInputUpdate
・TextBox.TextChanged
が順番に発生します。そして確定時に、
・TextCompositionManager.PreviewTextInput
・TextBox.TextChanged
が順番に発生します。

ここから、
・TextCompositionManager.PreviewTextInputStart

・TextCompositionManager.PreviewTextInput
の間の
・TextBox.TextChanged
は、IMEによる変換中の処理として、無視できるのではないか?と予想できます。

IME ONで「あ」と入力後変換、引き続き「い」と入力後変換した場合

image
さらに検証ケースです。
一度変換したあと、引き続き文字列を入力することで、変換を確定させるパターンです。
注目すべきは、「あ」を変換した後に「い」を入力した場合に、「あ」の変換が確定するにもかかわらず、
・TextCompositionManager.PreviewTextInput
が発生しない点です。なんかいやな予感がしますが、最終的に「い」の変換が確定した時点で
・TextCompositionManager.PreviewTextInput
が発生するのでかろうじてOKでしょうか。

IME ONで「あ」と入力後、バックスペースで削除した場合

image
こちらは、IMEごとの動作が致命的に違います。
ATOKでは、
・TextCompositionManager.PreviewTextInput
の後に、
・TextBox.TextChanged
が発生するのに、MS-IMEでは発生しません。しかも前ケースで、IME変換中の確定は
・TextCompositionManager.PreviewTextInput
が発生しないことがわかっているので、
「あ」入力→変換→「い」入力→Backspace
と入力すると、
・TextCompositionManager.PreviewTextInputStart

・TextCompositionManager.PreviewTextInput
の間で、一度も
・TextBox.TextChanged
が発生しないにもかかわらず、Textには確定済みの文字列が追加されることになります。

そのため、最後の
・TextCompositionManager.PreviewTextInputUpdate
を識別して、
・TextBox.TextChanged
を抑制しないようにする必要があります。

さらに調べていると、上記ケースのみ
・TextComposition.CompositionText
が、空文字列であることがわかりました。どうやらこれで判別できそうです。その他、 クリップボードからの貼り付け、手書きボードからの入力も問題ありませんでした。
結果として、以下のようなコードになりました、現在のところうまく動いているようです。

/// <summary>
/// IMEで変換中かのフラグ
/// </summary>
private bool _imeFlag = false;

public ImeTestWindow()
{
	InitializeComponent();

	TextCompositionManager.AddPreviewTextInputHandler(textBox1, OnPreviewTextInput);
	TextCompositionManager.AddPreviewTextInputStartHandler(textBox1, OnPreviewTextInputStart);
	TextCompositionManager.AddPreviewTextInputUpdateHandler(textBox1, OnPreviewTextInputUpdate)
}

private void textBox1_TextChanged(object sender, TextChangedEventArgs e)
{
	if (_imeFlag) return;
	//IMEで確定した場合のみ、ここに入る
	Console.WriteLine(textBox1.Text);
}

private void OnPreviewTextInput(object sender, TextCompositionEventArgs e)
{
	_imeFlag = false;
}
private void OnPreviewTextInputStart(object sender, TextCompositionEventArgs e)
{
	_imeFlag = true;
}
private void OnPreviewTextInputUpdate(object sender, TextCompositionEventArgs e)
{
	if (e.TextComposition.CompositionText.Length == 0)
		_imeFlag = false;
}
広告

1件のコメント »


RSS feed for comments on this post. TrackBack URI

コメントを残す

以下に詳細を記入するか、アイコンをクリックしてログインしてください。

WordPress.com ロゴ

WordPress.com アカウントを使ってコメントしています。 ログアウト / 変更 )

Twitter 画像

Twitter アカウントを使ってコメントしています。 ログアウト / 変更 )

Facebook の写真

Facebook アカウントを使ってコメントしています。 ログアウト / 変更 )

Google+ フォト

Google+ アカウントを使ってコメントしています。 ログアウト / 変更 )

%s と連携中

WordPress.com Blog.

%d人のブロガーが「いいね」をつけました。