WinUI 3 で、TextBox と WebView2 を同じ画面に置いたときの話です。
TextBox に日本語を打ったあと、WebView2 をマウスでクリックして続きを打つと、日本語入力はオンのままなのに英字が入ります。
Tab キーで移ったときは起きません。
見えない部品に一度だけフォーカスを経由させると、日本語が通るようになります。
正式な修正ではなく、なぜ直るのかも分かっていません。
動くことを確かめた回避策です。
直し方
まず、WebView2 と同じ Grid に、見えない部品 ImeRelay を置きます。
1 ピクセルの ContentControl で、Opacity を 0 にして、クリックも Tab キーも受け付けないようにしています。
あわせて WebView2 に GettingFocus を付けます。
<WebView2 x:Name="WebView" GettingFocus="OnWebViewGettingFocus" />
<ContentControl x:Name="ImeRelay" Width="1" Height="1" HorizontalAlignment="Left" VerticalAlignment="Top" IsHitTestVisible="False" IsTabStop="False" Opacity="0" UseSystemFocusVisuals="False" />
次に、GettingFocus イベントのメソッドを書きます。
入力欄から来たときだけ、移動先を ImeRelay に差し替えて、そのあとであらためて WebView2 にフォーカスを移します。
private void OnWebViewGettingFocus(UIElement sender, GettingFocusEventArgs args)
{
// 入力欄から来たときだけ経由する。WebView2 の準備ができる前は何もしない
if (WebView.CoreWebView2 is null
|| args.OldFocusedElement is not (TextBox or PasswordBox or RichEditBox))
{
return;
}
// 移動先を見えない部品に差し替える。差し替えられなかったときは、あとから Focus を呼ぶ
if (!args.TrySetNewFocusedElement(ImeRelay))
{
WebView.DispatcherQueue.TryEnqueue(() => ImeRelay.Focus(FocusState.Programmatic));
}
// いまの処理が終わってから、あらためて WebView2 にフォーカスを移す
WebView.DispatcherQueue.TryEnqueue(DispatcherQueuePriority.Low, () => WebView.Focus(FocusState.Programmatic));
}
DispatcherQueuePriority を使うので、using Microsoft.UI.Dispatching; が要ります。
症状
TextBox で日本語を打って確定したあと、WebView2 をマウスでクリックして打つと英字が入ります。
エラーは出ません。
MmmTool ではタスクバーの日本語入力はオンのままでした。
日本語入力は、Win32 の ImmSetOpenStatus でプログラムからオンにしても、手でオンにしても、同じ症状が出ました。
日本語入力のオンのしかたは関係がありません。
| 操作 | 回避策が無いとき | 回避策を付けたとき |
|---|---|---|
| TextBox で日本語を打ってから、WebView2 をマウスでクリックする | 英字になる | 日本語が通る |
| TextBox で日本語を打ってから、Tab キーで WebView2 に移る | 日本語が通る | 日本語が通る |
症状を確かめるときは、Tab キーではなくマウスでクリックして移ってください。
向かない場面
Tab キーだけで WebView2 に移る画面は症状が出ないので、この回避策は要りません。
WinUI や WebView2 を更新したら、回避策を外しても、TextBox から WebView2 をクリックして日本語が通るか確かめてください。
通れば、この回避策は要りません。
前提
- Windows 11 の WinUI 3 のアプリです
- WinUI 3 で WebView2 を使った例 (ターミナルの埋め込み)は、こちらの記事にあります
- パッケージ化しない形の Windows App SDK 2.5.1 と .NET 10 で動かして確かめました
- 試していないもの:
- パッケージ化する形
- WPF と WinForms の WebView2
- PasswordBox と RichEditBox から移るとき (コードの対象には入れてあります)
- 入力欄以外の部品からクリックで WebView2 に移るとき
つまずきやすいところ
見えない部品にフォーカスを移せなかったとき
MmmTool で、見えない部品へ向け直すたびに WebView2 へのフォーカスを呼び直す作りにしたところ、止まらなくなって CPU を使い続けました。
見えない部品にフォーカスを移せないと、移動元が入力欄のままなので、同じ差し替えを繰り返す可能性があります。
試して効かなかったもの
MmmTool の WebView2 では IME の状態の引き継ぎ・blur と focus の付け直し・WebView2 への Focus の再呼び出しも試しましたが、効果はありませんでした。
この仕組みが動いているところ
MmmTool は Windows 11 のタスクトレイに常駐するツールで、CLI補助の画面にこの回避策を使っています。
下の入力欄から上のターミナルへクリックで移るときだけ、見えない部品を一度経由させています。
AI と一緒に作りました。どう動くかは自分で決めて、気に入らないところは指示して直しています。
最後に
治らなかったらごめんね (;´・ω・)