WPF で韓国語(ハングル)入力対応の業務アプリを作ると、TextBox の ReadOnly 切り替えをきっかけに IME だけおかしくなることがあります。特に InkCanvas 上に TextBox を配置してダブルクリック編集をさせる構成では、「編集できるのにハングルだけ入力できない」という現象が起こりがちです。この記事では、その原因と実用的な対処法を、サンプルコード付きで詳しく解説します。
WPF TextBoxで「ReadOnly解除後だけ韓国語IMEが入力できない」現象
まずは問題の具体的な状況を整理します。典型的には次のような構成の WPF アプリで発生します。
- 画面上に InkCanvas を配置して、手書き+テキスト混在の入力アプリを作っている
- InkCanvas 上に TextBox を載せ、最初は
IsReadOnly = trueで表示専用にする - ユーザーが TextBox をダブルクリックしたタイミングで
IsReadOnly = falseにして編集可能にする - この TextBox に対して韓国語 IME で入力しようとすると、ハングルが入力できずローマ字だけが入る
- しかし、最初から
IsReadOnlyを設定しない場合はハングル入力は正常に動作する
つまり、「TextBox 自体は編集可能」だが「IME が韓国語モードとして認識されていない」状態になっています。CJK(日本語・中国語・韓国語)IME のうち、特に韓国語 IME で問題が目立つケースが多く報告されています。
| 条件 | 挙動 |
|---|---|
| TextBox を常に IsReadOnly = false | 韓国語 IME でハングル入力可 |
| 起動時 IsReadOnly = true → ダブルクリックで false | TextBox は編集できるがハングルは入力不可(ローマ字のみ) |
| 同じ TextBox で日本語 IME | 環境によっては正常/同様の不具合が混在 |
このように、IsReadOnly を true → false に切り替えたときだけ IME が意図せず無効状態のまま残ることが、今回の不具合の本質です。
原因の概要:IsReadOnly切り替え時に「IMEが内部で無効のまま残る」
WPF の TextBox は、内部的に InputMethod クラスを通じて IME(テキストサービス)を制御しています。特に IsReadOnly を切り替えた際に、次のような処理が内部で行われます。
IsReadOnly = trueにすると、編集不可能なコントロールとして扱うため IME を一時的に無効化 する- 通常は
IsReadOnly = falseに戻した時点で IME の状態も復元されるはず - しかし環境によっては、IME の状態が「無効」のまま戻らない ことがある
特に韓国語 IME / 中国語 IME のように、入力方式が英数字から大きく異なる IME は、一時的に無効化された後の復帰タイミングがシビア で、WPF の復元処理とうまく噛み合わない場合があります。その結果、次のような状態になります。
- Windows の IME インジケーター上は「韓国語、IMEオン」になっている
- しかし、TextBox に対しては 英数キーボードとして扱われている
- ユーザーからは「韓国語 IME が効いていない」ように見える
この問題は WPF 内部の挙動と IME 実装の相性に依存しており、アプリ側から完全に根本修正することは困難です。そのため、実務的には「IME を再初期化して TextBox に認識させ直す」というアプローチが最も現実的な解決策になります。
解決策1:IMEを強制的に再初期化する(最有力・実用解)
最もシンプルで再現性が高いのが、IsReadOnly を解除した直後に IME を一度オフにし、すぐオンに戻すという方法です。WPF では InputMethod.SetIsInputMethodEnabled を使って IME の有効/無効を制御できます。
基本のサンプルコード
using System.Windows.Input;
private void TextBox_MouseDoubleClick(object sender, MouseButtonEventArgs e)
{
var textBox = (TextBox)sender;
// 編集可能に切り替え
textBox.IsReadOnly = false;
textBox.Focus();
textBox.SelectAll();
// IME をいったん無効 → 有効にしてリフレッシュ
InputMethod.SetIsInputMethodEnabled(textBox, false);
InputMethod.SetIsInputMethodEnabled(textBox, true);
}
ポイントは次の通りです。
IsReadOnly = falseにした直後に 必ず TextBox にフォーカスを当てる- その直後に
InputMethod.SetIsInputMethodEnabledで IME を一度オフにする - 同じ TextBox に対してオンに戻すことで、TextBox 単位で IME 状態がリセット される
| 処理ステップ | 目的 |
|---|---|
| IsReadOnly = false | 編集可能状態に切り替える |
| Focus() / SelectAll() | ユーザーがすぐに編集できるようにし、対象 TextBox を明示 |
| InputMethod.IsInputMethodEnabled = false → true | TextBox 単位で IME を再初期化し、韓国語 IME を再認識させる |
MVVM や複数 TextBox で使い回せるようにする
上記のコードを毎回書くのは煩雑なので、共通メソッドや添付ビヘイビアに切り出すと保守性が高まります。
共通ヘルパーメソッドとして実装する例
public static class ImeHelper
{
public static void EnableEditingWithIme(TextBox textBox)
{
textBox.IsReadOnly = false;
textBox.Focus();
textBox.SelectAll();
// IME リフレッシュ
InputMethod.SetIsInputMethodEnabled(textBox, false);
InputMethod.SetIsInputMethodEnabled(textBox, true);
}
}
ダブルクリックイベント側では次のように呼び出せます。
private void TextBox_MouseDoubleClick(object sender, MouseButtonEventArgs e)
{
ImeHelper.EnableEditingWithIme((TextBox)sender);
}
添付ビヘイビアで XAML から指定する例
コードビハインドを極力減らしたい場合は、添付プロパティとしてまとめることもできます。
public static class ReadOnlyToEditableBehavior
{
public static readonly DependencyProperty EnableOnDoubleClickProperty =
DependencyProperty.RegisterAttached(
"EnableOnDoubleClick",
typeof(bool),
typeof(ReadOnlyToEditableBehavior),
new PropertyMetadata(false, OnEnableOnDoubleClickChanged));
public static void SetEnableOnDoubleClick(DependencyObject obj, bool value)
=> obj.SetValue(EnableOnDoubleClickProperty, value);
public static bool GetEnableOnDoubleClick(DependencyObject obj)
=> (bool)obj.GetValue(EnableOnDoubleClickProperty);
private static void OnEnableOnDoubleClickChanged(
DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is TextBox textBox)
{
if ((bool)e.NewValue)
{
textBox.MouseDoubleClick += TextBox_MouseDoubleClick;
}
else
{
textBox.MouseDoubleClick -= TextBox_MouseDoubleClick;
}
}
}
private static void TextBox_MouseDoubleClick(object sender, MouseButtonEventArgs e)
{
var textBox = (TextBox)sender;
textBox.IsReadOnly = false;
textBox.Focus();
textBox.SelectAll();
InputMethod.SetIsInputMethodEnabled(textBox, false);
InputMethod.SetIsInputMethodEnabled(textBox, true);
}
}
XAML 側では次のように書くだけで、どの TextBox にも同じ挙動を付与できます。
<TextBox
Text="韓国語テキスト"
IsReadOnly="True"
local:ReadOnlyToEditableBehavior.EnableOnDoubleClick="True" />
このアプローチを採用すると、アプリ全体の「ReadOnly → 編集」の挙動を一括で統一でき、韓国語 IME に限らず他言語 IME の問題にも流用しやすくなります。
フォーカス獲得時にも IME をリフレッシュして安定性を高める
環境によっては、ReadOnly 切り替えだけでなく「フォーカスが移り変わった瞬間」に IME の状態が崩れることもあります。その場合、GotKeyboardFocus イベントで IME 再初期化を行うと改善するケースがあります。
private void TextBox_GotKeyboardFocus(object sender, KeyboardFocusChangedEventArgs e)
{
var textBox = (TextBox)sender;
if (!textBox.IsReadOnly)
{
InputMethod.SetIsInputMethodEnabled(textBox, false);
InputMethod.SetIsInputMethodEnabled(textBox, true);
}
}
ダブルクリックで編集状態に入った後、ユーザーが別の TextBox に移動してもハングル入力が継続して利用しやすくなるため、韓国語を多用する画面では標準で組み込んでおく価値があります。
| パターン | IME 再初期化タイミング | メリット |
|---|---|---|
| ダブルクリック時のみ | IsReadOnly を解除した直後 | 実装が簡単で副作用が少ない |
| フォーカス獲得時も | GotKeyboardFocus / GotFocus | 別 TextBox へ移動した後も IME 状態が安定しやすい |
解決策2:PreferredImeStateを事前に設定する(環境依存のある別案)
WPF の TextBox には InputMethod.PreferredImeState というプロパティがあり、「このコントロールがフォーカスを得たときの IME のデフォルト状態」を指定できます。これを利用して、最初から IME ON の TextBox として扱うのが第二の方法です。
基本的な設定例
TextBox を生成するタイミングで、次のように設定します。
textBox = new TextBox();
textBox.IsReadOnly = true;
// 既定で IME を ON にしておく
InputMethod.SetPreferredImeState(textBox, InputMethodState.On);
ダブルクリックで編集可能にするときは、ユーザーの IME 状態を優先させるように戻します。
private void TextBox_MouseDoubleClick(object sender, MouseButtonEventArgs e)
{
var textBox = (TextBox)sender;
textBox.IsReadOnly = false;
// これ以降はユーザーが直前に使っていた IME モードに従う
InputMethod.SetPreferredImeState(textBox, InputMethodState.DoNotCare);
textBox.Focus();
textBox.SelectAll();
}
この方法のメリットと注意点
| 観点 | 内容 |
|---|---|
| メリット | IME の再初期化フラグを直接触らないため、実装が比較的シンプル 「この TextBox は原則 IME ON で使う」という UI ポリシーを明示できる |
| デメリット | 環境によっては 韓国語 IME で問題が解消されない 場合がある ユーザーが英数字入力をしたい場合でも、自動で IME が ON になる挙動を嫌がられることがある |
特に今回の「ReadOnly 切り替え後に韓国語が入力できない」という症状そのものに対しては、必ずしも十分な対処にならないケースが多いと考えた方が安全です。「IME 再初期化」がどうしても行えない事情がある場合の第二候補、という位置付けにするとよいでしょう。
解決策3:表示用と編集用の2つのTextBoxを重ねて切り替える(回避策)
根本的に「ReadOnly 切り替え時の副作用」を避ける方法として、表示用 TextBox と編集用 TextBox を別コントロールとして用意し、重ねて切り替えるという設計があります。古典的な「ラベル+編集用テキスト」のパターンを WPF の上で再現するイメージです。
基本アイデア
- 表示用 TextBox
IsReadOnly = trueまたはIsHitTestVisible = falseにして、IME はそもそも関与させない- 見た目を整えるために Border や背景色を調整
- 編集用 TextBox
- 通常の編集可能な TextBox として配置し、IME は常に ON/OFF 可能
- 必要なときだけ
VisibilityをVisibleにする
XAML のイメージは以下のようになります。
<Grid>
<TextBox x:Name="DisplayTextBox"
Text="{Binding Text, Mode=TwoWay}"
IsReadOnly="True"
Background="Transparent" />
<TextBox x:Name="EditTextBox"
Text="{Binding Text, Mode=TwoWay}"
Visibility="Collapsed" />
</Grid>
ダブルクリック時の切り替えコード例です。
private void DisplayTextBox_MouseDoubleClick(object sender, MouseButtonEventArgs e)
{
DisplayTextBox.Visibility = Visibility.Collapsed;
EditTextBox.Visibility = Visibility.Visible;
EditTextBox.Focus();
EditTextBox.SelectAll();
}
private void EditTextBox_LostKeyboardFocus(object sender, KeyboardFocusChangedEventArgs e)
{
EditTextBox.Visibility = Visibility.Collapsed;
DisplayTextBox.Visibility = Visibility.Visible;
}
この方法の位置づけ
| 項目 | 内容 |
|---|---|
| メリット | ReadOnly 切り替えに伴う IME の副作用を完全に回避できる 編集用 TextBox 側で IME 設定を自由にカスタマイズしやすい |
| デメリット | XAML・コードともに実装量が増える フォーカスの遷移やバインディングの整合性に気をつける必要がある |
業務アプリで「一部の TextBox にだけ韓国語入力の不具合が出る」など、トラブルが集中する箇所だけに限定してこのパターンを採用するのも現実的です。
3つの対処法の比較
ここまで紹介した 3 つのアプローチを簡単に比較してみます。
| 対処法 | 実装コスト | 安定性 | おすすめ度 | 想定ユースケース |
|---|---|---|---|---|
| 1. IME 再初期化 | 低 | 高(実績豊富) | ◎ | 既存 TextBox の挙動を最小限の変更で直したい |
| 2. PreferredImeState 設定 | 低〜中 | 中(環境差がありうる) | △ | IME の初期状態を統一したい、英数入力をあまり想定しない画面 |
| 3. 表示用/編集用 TextBox 分離 | 中〜高 | 高(IME 仕様の影響を受けにくい) | ○ | 重要画面・トラブルが多い箇所だけ別実装で完全に安定させる |
多くのケースでは、まず1 の「IME 再初期化」を導入し、必要に応じて 3 の「表示/編集分離」を併用する、という二段構えにすると安定して運用しやすくなります。
InkCanvas上にTextBoxを配置する場合の追加の注意点
今回の問題は、単体の TextBox よりも InkCanvas 上に配置した TextBox で発生しやすい傾向があります。InkCanvas は手書き入力のためにマウスやペンのイベントを横取りするため、フォーカスやヒットテストの挙動が通常のパネルと異なるためです。
典型的な落とし穴
- ダブルクリックを InkCanvas 側でハンドルしてしまい、TextBox の MouseDoubleClick が発火していない
- TextBox の位置をドラッグで移動した後、フォーカスが InkCanvas に残ってしまい IME が TextBox に紐づかない
- 手書き入力とテキスト入力を同時に扱うため、フォーカスが頻繁に行き来し IME 状態が不安定になりやすい
安定させるためのポイント
- TextBox 上でのダブルクリックは必ず TextBox 側のハンドラで処理する
- 編集開始時には 必ず TextBox.Focus() を呼び出して、フォーカスを明示的に移す
- InkCanvas 側のマウスイベントで
e.Handled = trueを多用しすぎると TextBox のイベントが届かないことがあるので注意する
特に「フォーカスがどこにあるか」は IME の動作に直結するため、フォーカス制御と IME 再初期化をセットで設計することが重要です。
実装手順のサンプル:最小構成で再現&解決
ここでは、問題の再現と解決を行うための「最小サンプル」のイメージを紹介します。実際のプロジェクトに組み込む前に、テスト用として単一 Window のプロジェクトで挙動を確認すると原因切り分けがしやすくなります。
1. シンプルな再現用ウィンドウを作成
- InkCanvas を 1 つ配置
- その上に TextBox を 1 つ配置(位置は固定でも可)
- TextBox は起動時 IsReadOnly = true
- MouseDoubleClick イベントで IsReadOnly を false に切り替え
XAML のイメージです。
<Window x:Class="WpfImeSample.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="IME Sample" Height="400" Width="600">
<InkCanvas x:Name="inkCanvas">
<TextBox x:Name="textBox"
Width="200"
Height="30"
IsReadOnly="True"
MouseDoubleClick="TextBox_MouseDoubleClick"
GotKeyboardFocus="TextBox_GotKeyboardFocus"
Text="ダブルクリックで編集" />
</InkCanvas>
</Window>
2. コードビハインドでIME再初期化を実装
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void TextBox_MouseDoubleClick(object sender, MouseButtonEventArgs e)
{
textBox.IsReadOnly = false;
textBox.Focus();
textBox.SelectAll();
// 韓国語 IME 対策:IME を再初期化
InputMethod.SetIsInputMethodEnabled(textBox, false);
InputMethod.SetIsInputMethodEnabled(textBox, true);
}
private void TextBox_GotKeyboardFocus(object sender, KeyboardFocusChangedEventArgs e)
{
if (!textBox.IsReadOnly)
{
// 念のためフォーカス獲得時にも IME をリフレッシュ
InputMethod.SetIsInputMethodEnabled(textBox, false);
InputMethod.SetIsInputMethodEnabled(textBox, true);
}
}
}
この状態で韓国語 IME を ON にして TextBox を操作し、「ダブルクリック → 編集開始 → ハングル入力」が正常に行えるかを確認します。もしここで正常に動くようであれば、アプリ固有の複雑なロジックではなく、ReadOnly 切り替え+IME の組み合わせが原因だったと判断できます。
韓国語・中国語・日本語など複数IMEを扱うときの設計指針
今回のような問題は韓国語 IME で見つかることが多いですが、同様の現象が中国語や日本語の IME でも問題になる可能性があります。WPF アプリ全体で IME を扱う設計をする際には、次のような指針を意識すると安定しやすくなります。
- 「ReadOnly の切り替え」で IME が再初期化される可能性がある前提で設計する
- 編集開始時(ダブルクリック・Enter キーなど)には、フォーカス移動と IME 再初期化を一連の処理として実装する
- ユーザーにとって「この TextBox は常に韓国語で入力する」などの用途が明確な場合、PreferredImeState を ON に設定しておく
- 逆に「英数字の入力が主で、必要な時だけ韓国語」という TextBox では、ユーザーの IME 操作を尊重し、強制的に ON にしない
- 重要な画面ほど、簡易な再現プロジェクトを作って IME の挙動を検証しておく
特に韓国語 IME を多用するシステムでは、テスト環境でも実際に韓国語 IME をインストールし、ネイティブユーザーに近い使用方法で検証することが重要です。開発者マシンが英語/日本語だけだと、IME 固有の不具合を見落としがちになります。
トラブルシューティングチェックリスト
最後に、韓国語 IME が絡む TextBox の不具合を調査するときに確認したいポイントをチェックリスト形式でまとめます。
| チェック項目 | 確認内容 |
|---|---|
| 1. IsReadOnly の切り替え有無 | 起動直後の状態と、編集開始時に IsReadOnly を true/false 切り替えしていないか |
| 2. フォーカス制御 | 編集開始時に TextBox.Focus() が確実に呼ばれているか、InkCanvas などにフォーカスが残っていないか |
| 3. IME 再初期化 | InputMethod.SetIsInputMethodEnabled(…, false/true) で IME を明示的にリフレッシュしているか |
| 4. PreferredImeState の設定 | 不要に IME 状態を固定しておらず、かつ必要な TextBox には適切に設定しているか |
| 5. 別 TextBox との連携 | 複数の TextBox 間でフォーカス移動時に IME 状態が引き継がれているか |
| 6. InkCanvas 等のコンテナ | 親コンテナがマウスイベントを奪っていないか、TextBox 側のイベントが正しく発火しているか |
| 7. 環境差 | 同じコードで日本語 IME / 韓国語 IME / 中国語 IME で挙動が変わらないか、OS バージョン差はないか |
これらを順に確認し、特に 3(IME 再初期化) の有無をチェックすることで、多くの韓国語 IME 関連の不具合は短時間で切り分けられます。
まとめ:最小の変更で韓国語IME問題を解決する
WPF TextBox で IsReadOnly を true → false に切り替えた後だけ、韓国語(ハングル)が入力できないという問題は、WPF が内部で IME を無効化したまま戻さないことがあるのが原因です。アプリ側から完全な根本修正を行うのは難しいものの、実務的には次のように対処できます。
- 第一選択肢:IsReadOnly 解除後に
InputMethod.SetIsInputMethodEnabledで IME を一度オフ → オンにする - 必要に応じて
GotKeyboardFocusでも同様の IME リフレッシュを行い、複数 TextBox 間の安定性を高める - 根本的に ReadOnly 切り替えを避けたい箇所では、表示用と編集用の TextBox を分ける
- PreferredImeState は「IME 初期状態の指定」として補助的に利用する
特に、最初に紹介した 「IME 強制再初期化」 のアプローチは、既存コードへの変更を最小限に抑えつつ効果が高く、多くの韓国語 IME 問題で実用的な解決策として採用されています。韓国語ユーザーがストレスなく入力できる WPF アプリを提供するためにも、ReadOnly 切り替えやフォーカス制御と IME の関係を意識して設計しておくことが重要です。

コメント