【備忘録】複数行テキストボックスで改行とIME確定を見分ける

要件は以下の通り。

1. 複数行入力可能なTextBoxで、
2. 日本語入力と英語入力が混ざった状態で、
3. IME入力の確定には反応せず、
4. 改行を表すEnterキーが押されたことを検知する

(Visual Studio 2019・.NET Framework4.7.2)

## 要件1
作るだけ。

```MainWindow.xaml

<Window x:Class="sample.MainWindow"
        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:sample"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <Grid>
        <TextBox x:Name="Txtbox" Margin="20,20,20,20" Padding="5,5,5,5" Text=""
                 AcceptsReturn="True" VerticalScrollBarVisibility="Visible" HorizontalScrollBarVisibility="Auto"/>
    </Grid>
</Window>

```

できた。

|![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/305299/6f2400bd-986f-81b7-1eec-89cc51540507.png)
|:-:|

## 要件2～4
確認のため、とりあえず普通に、KeyDownを検知してみる。

```MainWindow.xaml

    <Grid>
        <TextBox x:Name="Txtbox" Margin="20,20,20,20" Padding="5,5,5,5" Text=""
                 AcceptsReturn="True" VerticalScrollBarVisibility="Visible" HorizontalScrollBarVisibility="Auto"
                 KeyDown="Txtbox_KeyDown"/>
    </Grid>

```

```MainWindow.xaml.cs

private void Txtbox_KeyDown(object sender, KeyEventArgs e)
{
    Console.WriteLine("{0}が押されました！", e.Key.ToString());
}

```

これでよし。

|![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/305299/380dbd92-94a8-bc9f-ffc8-3f0b9b742cd7.png)
|:-:|

なんでやねん。

英語入力しか検知できないようです。

### 作戦変更
Enterキーが物理的に押されているかどうか確認する。CtrlキーやShiftキーが押されていた場合の動作は別途用意すれば良い。

```MainWindow.xaml.cs

private void Txtbox_KeyDown(object sender, KeyEventArgs e)
{
    if (Keyboard.IsKeyDown(Key.Return))
    {
        Console.WriteLine("Enterキーが押されました！");
    }
}

```

これでよし。

|![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/305299/694f4d6a-975c-fe2d-07be-cba2353292ae.png)
|:-:|

なんでやねん。

先ほどの結果と合わせて考えると、IME確定も改行もKeyDownイベントとして認識されていないっぽい。

### さらに作戦変更
検知するイベントを変えます。

```MainWindow.xaml

    <Grid>
        <TextBox x:Name="Txtbox" Margin="20,20,20,20" Padding="5,5,5,5" Text=""
                 AcceptsReturn="True" VerticalScrollBarVisibility="Visible" HorizontalScrollBarVisibility="Auto"
                 PreviewKeyDown="Txtbox_PreviewKeyDown"/>
    </Grid>

```

```MainWindow.xaml.cs

private void Txtbox_PreviewKeyDown(object sender, KeyEventArgs e)
{
    Console.WriteLine("{0}が押されました！", e.Key.ToString());
}

```

|![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/305299/8f03a5c9-ab83-4d8d-11f9-8c4dad45fa8c.png)
|:-:|

いけそう。

```MainWindow.xaml.cs

private void Txtbox_PreviewKeyDown(object sender, KeyEventArgs e)
{
    if (e.Key.ToString() == "Return")
    {
        Console.WriteLine("改行されました！");
        // ここ、正しくは、「改行されそう！」ですが、そんなことは気にしない。
    }
}

```

|![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/305299/f4cecac8-224f-d8da-4468-6f41c4d4f20a.png)
|:-:|

完璧です。

## 結局
PreviewKeyDownイベントで、``e.Key.ToString() == "Return"``を判定してやればよい。

ちなみになぜこれを備忘録として残す事になったかというと、ネットで調べるとTextChangedイベントで、テキストボックスの行数が変わったら改行するというコードが先に出てきて、謎実装しかけるはめになったから。
ちなみに、TextChangedイベントを使うと、最後のEnterが改行として使われてからイベントが発火することになるため、それを許さない要件の場合そもそも使えない。(つまり、「あいうえお」の「い」と「う」の間にカーソルがある状態で、イベントを発火させようとしてEnterすると、先に改行が入るため、「あい\r\nうえお」を処理する羽目になる。)
その点、PreviewKeyDownイベントを使えば、``e.Handled = true;``としてやることで、キーイベントの処理を明示的に終了できるので、その心配も無い。

さらにちなみに、``KeyEventArgs.ImeProcessedKey``というのもあるみたいだが、これは英語圏で使われる文字を打ったときは全部``None``を返してくるので、今回のように改行を検知したいなら、``Keyboard.IsKeyDown(Key.Return) && e.ImeProcessedKey.ToString() == "None"``とすれば使えないこともない。
そうではなく、IME確定を検知したいなら、``e.ImeProcessedKey.ToString() == "Return"``となるので、強い味方となりそうだ。