こんにちは、働くC#プログラマーのさんさめです。
ユーザーの操作に反応して、
別のウィンドウを表示する。
そして、そのウィンドウが閉じるまでは、
元のウィンドウは操作を受け付けない。
という仕様、いわゆるモーダルウィンドウが必要になることは、
アプリケーションを開発しているとよくあります。
代表的なのはテキストエディタなどで保存せずに
アプリ終了しようとしたときの確認ダイアログです。
大抵の場合はMessageBoxを使えば事足ります。
ですが、様々な理由により、
独自のダイアログを使いたいこともあるでしょう。
この時、MessageBoxには当然ある
- 「OK」のボタンは「Enter」キーでも効く
- 「キャンセル」のボタンは「Esc」キーでも効く
という機能があると気が利いています。
えー…そのためだけにKeyBindingとか、
(他のコントロールより優先させるために)
PreviewKeyDownとか使うの面倒くさすぎる…
たしかに、KeyBindingを使えば、
どんなキーにもアサインできるなど汎用性が高いですが、
この方法はかなり手間です。
実は、
EnterキーとEscキーに関しては
Buttonに超簡単に紐づけられるプロパティがあります。
本記事ではこれらのプロパティの使い方について解説します。
IsDefaultとIsCancelの普通の使い方
さっそくサンプルコードです。
以下のWindowはShowDialogによって
表示されるモーダルウィンドウであると想定してください。
<Window x:Class="IsDefaultIsCancel.MyDialog"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MyDialog" Height="150" Width="300">
<DockPanel>
<WrapPanel DockPanel.Dock="Bottom"
HorizontalAlignment="Center"
Margin="4">
<Button Content="OK"
Width="80"
Click="OnOkButtonClick"
IsDefault="True"/>
<Button Content="キャンセル"
Width="80"
IsCancel="True"
Margin="8 0 0 0"/>
</WrapPanel>
<TextBox VerticalAlignment="Center"
HorizontalAlignment="Left"
Margin="4"
IsReadOnly="True"
Text="ボタンが押されました
もう一度このダイアログを開きますか?"/>
</DockPanel>
</Window>
2つのボタンにおいて、それぞれ
IsDefaultとIsCancelをtrueに設定しています。
これだけで、Enterキー、およびEscキーへの紐づけは完了です。
ウィンドウがアクティブなら、
Enterキーを押した瞬間にOnOkButtonClickメソッドが呼び出されます。
もちろん、MVVMではCommandを使うのが一般的ですが、
これも設定されていればちゃんと呼びだされます。
一方、IsCancelを設定しているボタンを見てください。
<Button Content="キャンセル"
Width="80"
IsCancel="True"
Margin="8 0 0 0"/>
ClickイベントもCommandプロパティも設定されていません。
これじゃあEscキー押しても何にも起きないよ…
紐づけできてるかどうか分からないじゃん!
ところがどっこい、
なんと「ShowDialogで呼び出された場合では、
IsCancel=trueにするだけで
Escキーを押したときに勝手にWindowを閉じてくれる」
という隠し仕様があるのです。
これはダイアログ的に使っているとき限定のまさに隠し仕様です。
しかもきっちりDialogResultプロパティもfalseにしてくれます。
超便利。知名度の低さが難点
ウィンドウを閉じる、という実装すら省けるのは
WPFらしからぬ気の利きようですね。
ただし、Commandプロパティを設定している場合は、
この隠し機能は無効になります。
逆にClickにメソッドを設定している場合は
隠し機能は有効(メソッドが呼ばれた後ダイアログが閉じる)なので、
覚えておくと、いざというときにハマらずに済むでしょう。
ややこしいから、
「MVVMだったらEscキーに
手軽に紐づけられる」と覚えておけば充分
小ネタ1.IsDefaultを複数のボタンにつけるとどうなる?
間違って複数のButtonにIsDefaultを設定したらどうなるのか試してみました。
<Button Content="OK"
Width="80"
Click="OnOkButtonClick"
IsDefault="True"/>
<Button Content="キャンセル"
Width="80"
IsDefault="True"
Click="Button_Click"
Margin="8 0 0 0"/>
結果は、
「最初のEnterキー入力で手前のIsDefaultのボタンにフォーカスが合い、
2回目のEnterキー入力でボタンが押された扱いになる」
でした。
操作感が変わってしまうので、
2つ以上のボタンにIsDefaultはつけない方が良いですね。
ちなみに、IsCancelを複数のボタンにつけた場合は、
Escキーを押すたびにフォーカスが変わります。
IsCancelの方では、
「Escキーをいくら押してもキャンセル操作にはならない」
ということになってしまいます。
IsCancelを複数につけるのはIsDefault以上にやめた方が良いでしょう。
小ネタ2.IsDefaultを設定したボタンが見えてないときはどうなる?
今度は、ボタンのVisibilityをCollapsedにして、
「ボタンは見えないけど、実はEnterキーにアサインされている」
という挙動が実現できるのか試してみました。
<Button Content="OK"
Width="80"
Click="OnOkButtonClick"
IsDefault="True"
Visibility="Collapsed"/>
結論から言うと、これはダメでした。
IsDefault=trueはあくまでも表示されているボタンじゃないと効果がないようです。
まとめ
まとめです。
- IsDefaultやIsCancelを使うと
手軽にEnterキーやEscキーにキーアサインできる - 複数のボタンに設定するのはやめた方がよい
- 非表示のボタンに設定しても効果はない
- 素直にKeyBindingを使うしかない
最後までお読みいただき、ありがとうございました。
コメント