[WPF] TextBox 毎にフォーカス時の IME の変換モードを設定する

WPF で TextBox 毎にフォーカス時の IME の変換モードを設定するコードです。
かなり稀だとは思いますが、要件として実際存在するそうです。

まず、普通に XAMLで設定する XAML コードです。
普通に、ドキュメントに載っているマニュアル通りの方法です。

<StackPanel>
    <!-- IME を無効にする -->
    <TextBox InputMethod.IsInputMethodEnabled="False" />
    <!-- ひらがな -->
    <TextBox InputMethod.PreferredImeState="On" InputMethod.PreferredImeConversionMode="FullShape,Native"/>
    <!-- 全角カタカナ -->
    <TextBox InputMethod.PreferredImeState="On" InputMethod.PreferredImeConversionMode="Katakana,FullShape"/>
    <!-- 全角英数 -->
    <TextBox InputMethod.PreferredImeState="On" InputMethod.PreferredImeConversionMode="Alphanumeric,FullShape"/>
    <!-- 半角カタカナ -->
    <TextBox InputMethod.PreferredImeState="On" InputMethod.PreferredImeConversionMode="Katakana,Native"/>
    <!-- 半角英数 -->
    <TextBox InputMethod.PreferredImeState="On" InputMethod.PreferredImeConversionMode="Alphanumeric"/>
</StackPanel>

これが正しい方法だと思うのですが、実際に動作させると動作に問題があります。
全角英数の次に全角カタカナの項目に移動した場合に IME のモードが、全角カタカナに変更されません。
発生条件としては、上記の通り、全角英数の次に全角カタカナの項目に移動する場合で、この組み合わせでのみ発生します。

この問題を回避するコードを書いてみました。軽く動かしたところは上手く動いてくれています。
TextBox を拡張した独自コントロールクラスを作るアプローチで挑んでみました。
要点は、GotFocus イベントと LostFocus の処理です。
全角英数項目の LostFocus で IME を別のものに一度変更し、全角カタカナ項目の GotFocus で非同期処理で IME を全角カタカナに変更します。
LostFocus、GotFocus、非同期処理、の3つのどれか一つがかけても上手く行きませんでした。

// IME の設定の種類
internal enum ImeMode { DoNotCare, Disabled, Hiragana, FullKana, FullAlphaNum, HalfKana, HalfAlphaNum }

// IME を指定可能な TextBox
internal class ImeModeTextBox : TextBox
{
    // IME の設定の種類
    public ImeMode ImeMode { get; set; } = ImeMode.DoNotCare;

    // コンストラクタ
    public ImeModeTextBox()
    {
        // 初期化
        this.Initialized += (sender, e) =>
        {
            switch (this.ImeMode)
            {
                case ImeMode.Disabled:        // Disabled
                    InputMethod.SetIsInputMethodEnabled(this, false);
                    break;
                case ImeMode.Hiragana:        // 全角ひらがな
                    InputMethod.SetPreferredImeState(this, InputMethodState.On);
                    InputMethod.SetPreferredImeConversionMode(this, ImeConversionModeValues.FullShape | ImeConversionModeValues.Native);
                    break;
                case ImeMode.FullKana:        // 全角カナ
                    InputMethod.SetPreferredImeState(this, InputMethodState.On);
                    InputMethod.SetPreferredImeConversionMode(this, ImeConversionModeValues.Katakana | ImeConversionModeValues.FullShape);
                    break;
                case ImeMode.FullAlphaNum:    // 全角英数
                    InputMethod.SetPreferredImeState(this, InputMethodState.On);
                    InputMethod.SetPreferredImeConversionMode(this, ImeConversionModeValues.Alphanumeric | ImeConversionModeValues.FullShape);
                    break;
                case ImeMode.HalfKana:        // 半角カナ
                    InputMethod.SetPreferredImeState(this, InputMethodState.On);
                    InputMethod.SetPreferredImeConversionMode(this, ImeConversionModeValues.Katakana | ImeConversionModeValues.Native);
                    break;
                case ImeMode.HalfAlphaNum:    // 半角英数
                    InputMethod.SetPreferredImeState(this, InputMethodState.On);
                    InputMethod.SetPreferredImeConversionMode(this, ImeConversionModeValues.Alphanumeric);
                    break;
            }

            // 全角英数項目 -> 全角カナ項目 のフォーカス移動時の問題を回避
            // 全角カナ項目側の処理
            if (this.ImeMode == ImeMode.FullKana)
            {
                this.GotFocus += async (sender_, e_) =>
                {
                    await Dispatcher.InvokeAsync(() =>
                    {
                        InputMethod.Current.ImeConversionMode = ImeConversionModeValues.Katakana | ImeConversionModeValues.FullShape;
                    });
                };
            };

            // 全角英数項目 -> 全角カナ項目 のフォーカス移動時の問題を回避
            // 全角英数項目側の処理
            if (this.ImeMode == ImeMode.FullAlphaNum)
            {
                this.LostFocus += (sender_, e_) =>
                {
                    InputMethod.Current.ImeConversionMode = ImeConversionModeValues.Alphanumeric;
                };
            };
        };
    }
}

この拡張 TextBox コントロールを使った XAML です。

<Window x:Class="WpfApplication.Window"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WpfApplication"
        mc:Ignorable="d"
        Title="Window" Height="300" Width="300">
    <StackPanel>
        <!-- IME を無効にする -->
        <local:ImeModeTextBox ImeMode="Disabled"/>
        <!-- ひらがな -->
        <local:ImeModeTextBox ImeMode="Hiragana"/>
        <!-- 全角カタカナ -->
        <local:ImeModeTextBox ImeMode="FullKana"/>
        <!-- 全角英数 -->
        <local:ImeModeTextBox ImeMode="FullAlphaNum"/>
        <!-- 半角カタカナ -->
        <local:ImeModeTextBox ImeMode="HalfKana"/>
        <!-- 半角英数 -->
        <local:ImeModeTextBox ImeMode="HalfAlphaNum"/>
    </StackPanel>
</Window>

これで上手く動いてくれました。

コメントを残す