WPF 標準コントロールデモアプリ › Button
Button Inputs
Button は、クリックされると Click イベントか Command にバインドしたコマンドで処理を実行するボタンです。ClickMode、IsPressed、コマンド関連のプロパティは基底クラスの ButtonBase が持っています。
Esc と Enter:IsCancel と IsDefault
IsCancel は、Esc でボタンを押せるようにします。デモアプリはボタンを UserControl の中に置いており、そこでも動きます。UserControl の中の TextBox にフォーカスがある状態で、Esc でボタンが押されました。ウィンドウが閉じるかどうかは、ウィンドウの開き方で変わります。ShowDialog で開いたウィンドウは Esc で閉じ、ShowDialog は false を返しました。Show で開いたウィンドウでは、ボタンは押されましたがウィンドウは開いたままだったので、自分で閉じます。
IsCancel のボタンは 1 つのウィンドウに 1 つにします。2 つあると、Esc ではどちらも押されませんでした。フォーカスが 1 つ目のボタンへ移り、もう一度押すと 2 つ目へ移りました。
IsDefault は、Enter でボタンを押せるようにします。TextBox にフォーカスがあると Enter で押され、そのときの IsDefaulted は True でした。AcceptsReturn="True" の TextBox にフォーカスがあると、Enter は改行になり、ボタンは押されませんでした。ほかのボタンにフォーカスがあると、Enter ではフォーカスのあるボタンが押され、IsDefaulted は False でした。
| 条件 | 計測値 |
|---|---|
| UserControl の中、TextBox にフォーカス: Esc で押されたボタン / ウィンドウが開いたままか | IsCancel / True |
| Enter で押されたボタン(キーを押す前の IsDefaulted) | IsDefault(True) |
| AcceptsReturn の TextBox にフォーカス: Enter で押されたボタン / 文字列の改行の数 | (なし) / 1 |
| 別の Button にフォーカス: Enter で押されたボタン(IsDefault のボタンの IsDefaulted) | Other(False) |
| IsCancel のボタンが 2 つ: Esc で押されたボタン / キーボードフォーカス | (なし) / Cancel 1 |
| もう一度 Esc: 押されたボタン / キーボードフォーカス | (なし) / Cancel 2 |
| ShowDialog で開いたウィンドウの IsCancel: Esc でウィンドウが閉じたか / ShowDialog の戻り値 | True / False |
フォーカスの位置とウィンドウの開き方ごとの IsCancel と IsDefault。.NET 10 / Windows 11 で計測。
どの時点でクリックになるか:ClickMode と IsPressed
ClickMode は ButtonBase のプロパティです。実際のマウスで試すと、既定値の Release はマウスボタンを離した時点でクリックになりました。離す前にポインターをボタンの外へ出すと、IsPressed が False になり、クリックになりませんでした。Press はマウスボタンを押した時点でクリックになりました。Hover は、マウスボタンを押さなくても、ポインターがボタンに入った時点でクリックになりました。
キーボードでは規則が違います。Space は Release ならキーを離した時点、Press ならキーを押した時点でクリックになり、Enter はどちらもキーを押した時点でクリックになりました。Hover のボタンはキーボードでは押せず、Space でも Enter でも何も起きませんでした。キーボードで操作するボタンには ClickMode="Hover" を使いません。
IsPressed は読み取り専用です。ボタンの上でマウスボタンを押している間と、Space を押している間は True で、離すと False でした。Hover のボタンは、ポインターが上にある間押された状態でしたが、キーボードでは押された状態にならず、Space を押している間も IsPressed は False のままでした。
| 条件 | 計測値 |
|---|---|
| ClickMode の既定値 / IsPressed が読み取り専用か | Release / True |
| Hover(マウス): ポインターが入る / 出る | Hover、IsPressed True / (なし)、IsPressed False |
| Press(マウス): ボタンを押す / 離す | Press、IsPressed True / (なし)、IsPressed False |
| Release(マウス): ボタンを押す / 離す | (なし)、IsPressed True / Release、IsPressed False |
| Release(マウス): ボタンの上で押し、外へ動かして離す | 外へ動かした後の IsPressed False / (なし) |
| Release(キーボード): Space を押す / Space を離す / Enter を押す | (なし)、IsPressed True / Release、IsPressed False / Release |
| Press(キーボード): Space を押す / Space を離す / Enter を押す | Press、IsPressed True / (なし)、IsPressed False / Press |
| Hover(キーボード): Space を押す / Space を離す / Enter を押す | (なし)、IsPressed False / (なし)、IsPressed False / (なし) |
実際のマウスとキーボードでの ClickMode と IsPressed。.NET 10 / Windows 11 で計測。
コマンド:何がボタンを有効にし、いつ問い合わせ直されるか
バインドしたコマンドの CanExecute が false を返す間、ボタンの IsEnabled は False でした。ボタンに IsEnabled="True" を設定しても同じです。有効・無効は CanExecute で決めます。
デモアプリの RelayCommand は、CanExecuteChanged を CommandManager.RequerySuggested に委ねています。この場合、CanExecute の結果が変わっても、CommandManager.InvalidateRequerySuggested を呼ぶか、フォーカスの移動などで WPF が問い合わせ直すまで、ボタンは変わりませんでした。この問い合わせはボタンごとに行われます。同じコマンドをバインドしたボタンが 20 個あると、フォーカスの移動 1 回で CanExecute が 20 回呼ばれました。CanExecute は軽くします。
CommandParameter は、CanExecute と Execute に渡す値です。デモアプリは、Command の後に TextBox の Text へバインドしています。この XAML では、読み込み時の 2 回の CanExecute にはどちらも ShowMessageText が渡り、null は渡りませんでした。パラメーターが変わると、すぐに問い合わせ直されます。コードからテキストを空にすると、InvalidateRequerySuggested を呼ばなくても CanExecute が 1 回呼ばれ、ボタンは無効になりました。Execute には、クリックした時点のテキストが渡りました。一覧では、項目のテンプレートに置いたボタンでその項目を渡せます。CommandParameter="{Binding}" にすると、2 行目のボタンを実際にクリックしたとき、Execute は Row 2 を受け取りました。
| 条件 | 計測値 |
|---|---|
| CanExecute が false: IsEnabled / IsEnabled="True" を指定したボタン | False / False |
| CanExecute が true に変わる: IsEnabled / InvalidateRequerySuggested の後 | False / True |
| 同じコマンドのボタンが 20 個: フォーカスが 1 回移るごとの CanExecute の呼び出し回数 | 20 |
| デモの XAML: 読み込み中に CanExecute へ渡された引数 | ShowMessageText, ShowMessageText |
| コードから TextBox.Text を空にする: CanExecute の呼び出し回数 / IsEnabled | 1 / False |
| TextBox.Text を Hello にしてクリック: Execute の引数 | Hello |
| 項目のテンプレート、CommandParameter="{Binding}": 2 行目を実際にクリック: Execute が受け取った値 | Row 2 |
Command と CommandParameter。.NET 10 / Windows 11 で計測。
内容、アクセスキー、テンプレート
Button は ButtonBase を継承し、ButtonBase は ContentControl を継承しています。そのため Content には文字列、画像、パネルを置けます。既定のテンプレートは内容を ContentPresenter で表示し、その RecognizesAccessKey は True です。Content="_Save" では S がアクセスキーになり、UI オートメーションの名前は Save でした。画像だけのボタンでは UI オートメーションの名前が空だったので、AutomationProperties.Name を付けます。
既定のテンプレートには表示状態のグループ(VisualStateGroup)がありません。見た目は、IsDefaulted・IsMouseOver・IsPressed・IsChecked(いずれも true)と IsEnabled = false のトリガーで切り替えています。
| 条件 | 計測値 |
|---|---|
| 基底クラス | ButtonBase -> ContentControl |
| 既定のテンプレート: VisualStateGroups の数 / トリガー | 0 / IsDefaulted=True, IsMouseOver=True, IsPressed=True, IsChecked=True, IsEnabled=False |
| ContentPresenter.RecognizesAccessKey | True |
| UI オートメーションの名前: Content が "Save" | Save |
| Content が "_Save" | Save(アクセスキー S) |
| Content が Image | (空) |
| AutomationProperties.Name="Save" を付けた Image | Save |
基底クラス、既定のテンプレート、UI オートメーションの名前。.NET 10 / Windows 11 で計測。
デモアプリで試す

デモアプリの Button のページには、IsCancel と IsDefault、ClickMode、IsPressed、Command、CommandParameter の欄があります。各欄の下の「Show Code」リンクで、その欄の XAML を表示できます。次の XAML は、CommandParameter の欄(ButtonUsageControl.xaml)から、スタイルと周囲の GroupBox を省き、名前空間の宣言を加えたものです。
<StackPanel xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<TextBox x:Name="CommandParameterText" Text="ShowMessageText" />
<Button x:Name="ClickWithParameterCommandButton"
Command="{Binding ClickWithParameterCommand}"
CommandParameter="{Binding Text, ElementName=CommandParameterText}"
Content="ShowMessageBox" />
</StackPanel>
コマンドはビューモデル(ButtonUsageViewModel.cs)で定義しています。
public ICommand ClickWithParameterCommand { get; } =
new RelayCommand(param => MessageBox.Show(param?.ToString()), null);
関連するコントロールと記事
- RepeatButton — 押している間クリックを繰り返す ButtonBase です。
- ToggleButton — クリックすると押された状態のまま残る ButtonBase です。
- WPF で RelayCommand の CanExecute がボタンの有効・無効に反映されない問題の解決方法 — ボタンが
CanExecuteに追従しない理由と、問い合わせ直す方法を扱います。 - WPF の Label でアンダーバーが消える理由と回避方法 — ボタンの内容にも当てはまるアクセスキーの挙動を扱います。
ソースコードと計測の方法
このページの挙動の記述は、すべて .NET 10 / Windows 11 で実際に動かして確かめたものです。計測には、このサイトのスクリーンショット生成ツールの ButtonDemoScene を使いました。IsCancel と IsDefault は WPF の入力管理(InputManager)の中で処理されるため、キーはキーボードから打ったときと同じく InputManager を通して送りました。ClickMode は、計測用のウィンドウの上で実際のマウスを動かし、クリックして確かめました。TextBox の文字を CommandParameter で渡すクリックは UI オートメーションの Invoke で、項目のテンプレートの行のボタンのクリックは実際のマウスで行いました。