[wpf] ボタンのIsDefault・IsCancelを設定する


## はじめに

仕事でOKボタンを押したらシャットダウンする自作windowを作る機会があったのですが、
「**windowが表示されたときにEnterキー押したら、OKボタンのクリックイベントが実行されるようにしてね**」と言われました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/438389/d9e87c9a-b90b-914a-d6fa-da175ca44f4e.png)

言われるまではクリックでok等のボタンイベントが正常か確認していましたが、キーイベントでの挙動は見ていなかったのでこの機会に少しまとめてみました。

**IsDefault**と**IsCancel**という便利なプロパティがwpfのボタンには存在するようなので、それを使ってみます。

## 作ったサンプル

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/438389/6416999c-a6ef-eff3-2fdc-822e609150aa.png)

テキストボックスを1つと、ボタンを5つ並べました。

default(okCancel)と書かれたボタンにIsDefault=Trueを割り当て、escape(ync)と書かれたボタンにIsCancel=Trueを割り当てています。

各ボタンにはクリックイベントを割り当てていて、show NodDefWin以外はメッセージボックスを表示させています。

### xaml
```xml
<Grid Background="#b33e5c">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*"></ColumnDefinition>
            <ColumnDefinition Width="*"></ColumnDefinition>
            <ColumnDefinition Width="*"></ColumnDefinition>
        </Grid.ColumnDefinitions>

        <Grid Grid.ColumnSpan="3">
            <TextBlock x:Name="text" Text="result" />
        </Grid>
        
        <!--Defaultのボタン-->
        <Grid Grid.Column="0">
            <Button Content="default(okCancel)" Width="100" Height="30" Click="Button_Default" IsDefault="True" />
        </Grid>

        <Grid Grid.Column="1">
            <StackPanel>
                <!--Escape時に選択されるボタン-->
                <Button Content="escape(ync)" Width="100" Height="30" IsCancel="True" Click="Button_Escape" />
                <Button Content="onlyOk" Width="100" Height="30" Click="Button_OnlyOK" />
                <Button Content="YsNo" Width="100" Height="30" Click="Button_YesNo" />
            </StackPanel>
        </Grid>

        <Grid Grid.Column="2">
            <Button Content="show NoDefWin" Width="100" Height="30" Click="Button_Custom" />
        </Grid>
    </Grid>
```

### xaml.cs

```cs
private void Button_Default(object sender, RoutedEventArgs e)
        {
            var r = MessageBox.Show("Enter default ok", "caption", MessageBoxButton.OKCancel);
            this.text.Text = r.ToString();
        }

        private void Button_Escape(object sender, RoutedEventArgs e)
        {
            var r = MessageBox.Show("Enter", "caption", MessageBoxButton.YesNoCancel);
            this.text.Text = r.ToString();
        }

        private void Button_OnlyOK(object sender, RoutedEventArgs e)
        {
            var r = MessageBox.Show("Enter", "caption", MessageBoxButton.OK);
            this.text.Text = r.ToString();
        }

        private void Button_YesNo(object sender, RoutedEventArgs e)
        {
            var r = MessageBox.Show("Enter", "caption", MessageBoxButton.YesNo);
            this.text.Text = r.ToString();
        }

        /// 自作のwindowクラスを表示する
        private void Button_Custom(object sender, RoutedEventArgs e)
        {
            var s = new SubWindow();
            s.ShowDialog();
        }

```

## 動作

### Enterキー
default(okCancel) と書かれたボタンの **IsDefault=True** にしているので、アプリ起動時にEnterを押すとこのボタンのClickイベントが実行されて、**MessageBoxButton.OKCancel**のメッセージボックスが表示されます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/438389/f53a470e-e32b-55fa-cd59-0fa44ebba28b.png)

**MessageBoxButton.OKCancel**のメッセージボックスは **OKボタンがIsDefault=True**で **キャンセルボタンがIsCancel=True**になっています。
ですので、Enterを押すとテキストブロックにOKが表示され、Escapeを押すとCancelが表示されます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/438389/edf18ec8-751f-690d-dd8b-8f3c477b7a3d.png)

テキストブロックの文字がCancelになっていますね。

### escapeキー
escapeキーを押すと **IsCancel=true**にしているescape(ync)を表示しているボタンのclickイベントが実行されます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/438389/97853416-413e-9e3a-85a6-37670a90c0e6.png)

**MessageBoxButton.YesNoCancel**  で表示しているメッセージボックスも **デフォルトでYes**、**EscapeでCancel**が選択されるようです。

### OKのみ / YesNo

**MessageBoxButton.OK**のメッセージボックスは **デフォルト・Escape共にtrue**のようで、Enter・escapeどちらでもokが選択されます。

**MessageBoxButton.YesNo**のメッセージボックスは デフォルトでyesが選択されるようですが、**Escapeでは反応がありません。**

**IsCancel**というプロパティ名なので、キャンセルボタンがない場合は基本的にはtrueにしないほうがいいようです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/438389/7f5c6a89-6029-399f-61aa-17c689b4e402.png)

escapeを押してもcloseしてくれないメッセージボックス。

### show NoDefWin(自作window)

show NodefWinボタンで表示する自作windowはボタンを2つ並べているwindowです。

```xml
<!--一部のみ-->
<Grid.ColumnDefinitions>
            <ColumnDefinition Width="*"></ColumnDefinition>
            <ColumnDefinition Width="*"></ColumnDefinition>
        </Grid.ColumnDefinitions>

        <TextBlock Text="okでシャットダウンします" />

        <Grid Grid.Column="0">
            <Button Content="ok" Width="100" Height="30" Click="Button_Close"/>

        </Grid>

        <Grid Grid.Column="1">
            <Button Content="cancel" Width="100" Height="30" Click="Button_Close"/>
        </Grid>

```
Button_Closeイベントはthis.Close()のみです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/438389/d9e87c9a-b90b-914a-d6fa-da175ca44f4e.png)

IsDefaultもIsCancelもtrueにしていないので、**Enter・Escapeキーを押しても反応がありません。**まぁ、当然ですね。

ということで自作windowでOK・キャンセルボタンを設置するときは**IsDefault**と**IsCancel**プロパティを使ってあげようというお話でした。

## まとめ

windowsの仕様に倣うのであれば、okタイプのボタンならDefault=Trueにして、キャンセルのボタンならIsCancel=Trueにしたほうがいいと改めて感じました。

・・・評価仕様でこういう確認見たことないな。

## 参考サイト
https://docs.microsoft.com/ja-jp/dotnet/api/system.windows.controls.button.isdefault?view=netcore-3.1

https://threeshark3.com/isdefault/