5.2 UI を作成する
5.4.1 アプリケーションの終了方法はメニューだけではない
5.4 「終了」メニューでアプリケーションを終了させる
アプリケーションをいきなり終了されてしまうとこれまでの作業内容が保存されなかったり、ログを保存できなかったり いろいろ不便が生じます。アプリケーションを終了するときは内部でなんらかの処理をおこない、その上で終了するように したほうが良いでしょう。ここでは、ユーザーからアプリケーションを終了したいという要求を受け取ったときにどのよう に処理するかについて説明します。
75 }
コード 5.17:「終了」メニューに ExitCommand プロパティを紐付ける MainView.xaml
1 2 3 4 5 6 7 8 9 16 17 18 19 20 21 22 23 24 25 26 27 28
<Window x:Class="YKWpfIntroduction.Practices.Views.MainView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:b="clr-namespace:YKWpfIntroduction.Practices.Views.Behaviors"
Title="MainView" Height="300" Width="300">
<DockPanel>
<Menu DockPanel.Dock="Top">
<MenuItem Header="ファイル (_F)">
<MenuItem Header="開く(_O)".../>
<Separator />
<MenuItem Header="終了 (_X)" Command="{Binding ExitCommand}" />
</MenuItem>
<MenuItem Header="ヘルプ (_H)" />
</Menu>
<StatusBar DockPanel.Dock="Bottom">
</StatusBar>
<Grid Background="MediumSeaGreen">
</Grid>
</DockPanel>
</Window>
C# コード上からアプリケーションを終了させるときは Application クラスの Shutdown() メソッドを使います。コー ド 5.16 のように、App.Current プロパティで現在の Application クラスを取得し、これに対する Shutdown() メソッ ドを呼び出します。
もし終了前に何か処理をしたい場合はコード 5.16 の 69 行目の OnExit() メソッドに処理を追加すればいいし、そも そも終了できないようにする場合は、ExitCommand プロパティの実行可能判別処理を追加してfalse を返すようにすると、
「終了」メニューが無効化して選択できなくなります。
5.4.3 「x」ボタンでアプリケーションを終了される場合
「x」ボタンでアプリケーションを終了されてしまう場合、前節のように直接コマンドで処理することができません。そ こで、添付ビヘイビアで Window.Closing イベントを利用し、このイベントハンドラで ViewModel 側のコールバック処 理をおこなうようにします。添付ビヘイビアについては「5.3.3 添付ビヘイビアの作成」で説明しているため、詳細は省略 します。
添付ビヘイビアとして WindowClosingBehavior クラスを次のように定義します。
コード 5.18:Window.Closing イベントを利用した WindowClosingBehavior 添付ビヘイビア WindowClosingBehavior.cs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48
namespace YKWpfIntroduction.Practices.Views.Behaviors {
using System;
using System.ComponentModel;
using System.Windows;
/// <summary>
/// Window を閉じるときのビヘイビアを表します。
/// </summary>
internal class WindowClosingBehavior {
#region Callback 添付プロパティ
/// <summary>
/// Func<bool> 型の Callback 添付プロパティを定義します。
/// </summary>
public static readonly DependencyProperty CallbackProperty =
DependencyProperty.RegisterAttached("Callback", typeof(Func<bool>), typeof(WindowClosingBehavior), new PropertyMetadata(null, OnIsEnabledPropertyChagned));
/// <summary>
/// Callback 添付プロパティを取得します。
/// </summary>
/// <param name="target">対象とする DependencyObject を指定します。</param>
/// <returns>取得した値を返します。</returns>
public static Func<bool> GetCallback(DependencyObject target) {
return (Func<bool>)target.GetValue(CallbackProperty);
}
/// <summary>
/// Callback 添付プロパティを設定します。
/// </summary>
/// <param name="target">対象とする DependencyObject を指定します。</param>
/// <param name="value">設定する値を指定します。</param>
public static void SetCallback(DependencyObject target, Func<bool> value) {
target.SetValue(CallbackProperty, value);
}
#endregion Callback 添付プロパティ
/// <summary>
/// Callback 添付プロパティ変更イベントハンドラ /// </summary>
/// <param name="sender">イベント発行元</param>
/// <param name="e">イベント引数</param>
private static void OnIsEnabledPropertyChagned(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
var w = sender as Window;
if (w != null) {
49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76
var callback = GetCallback(w);
if ((callback != null) && (e.OldValue == null)) {
w.Closing += OnClosing;
}
else if (callback == null) {
w.Closing -= OnClosing;
} } }
/// <summary>
/// Closing イベントハンドラ /// </summary>
/// <param name="sender">イベント発行元</param>
/// <param name="e">イベント引数</param>
private static void OnClosing(object sender, CancelEventArgs e) {
var callback = GetCallback(sender as DependencyObject);
if (callback != null) {
// コールバック処理の結果が false のときキャンセルする e.Cancel = !callback();
} } } }
ViewModel 側から Func<bool> 型のプロパティとしてコールバックメソッドをもらうことを想定し、このメソッドが
null でないとき、Window.Closing イベントを捕捉し、そのコールバック処理の結果に応じてアプリケーションを終了し
たりしなかったりするようにしています。
例えば ViewModel 側を次のように変更します。
コード 5.19:アプリケーション終了に条件を付ける
WindowClosingBehavior.cs 1
2 3 4 5 6 7 8 9 10 48 49 50 51 52 53 54 55 56 57 58 59 60
namespace YKWpfIntroduction.Practices.ViewModels {
using System;
/// <summary>
/// MainView ウィンドウに対するデータコンテキストを表します。
/// </summary>
internal class MainViewModel : NotificationObject {
ファイルを開く
#region アプリケーションを終了する
public Func<bool> ClosingCallback {
get { return OnExit; } }
private DelegateCommand _exitCommand;
/// <summary>
/// アプリケーション終了コマンドを取得します。
/// </summary>
public DelegateCommand ExitCommand {
61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89
get {
return this._exitCommand ?? (this._exitCommand = new DelegateCommand(
_ =>
{
OnExit();
}));
} }
/// <summary>
/// アプリケーションを終了します。
/// </summary>
private bool OnExit() {
if (this._counter < 3) {
this._counter++;
return false;
}
App.Current.Shutdown();
return true;
}
private int _counter;
#endregion アプリケーションを終了する
} }
86 行目で定義した変数 _counter で終了コマンドが投げられた回数を数え、これが 4 回以上になったときに初めて 82 行目の Shutdown() メソッドが呼ばれるようにしています。3 回以下のとき、OnExit() メソッドが false を返している た め 、ClosingCallback プ ロ パ テ ィ か ら 呼 ば れ た 場 合 、 そ の 戻 り 値 が false と な り ま す 。 こ れ は 、 WindowClosingBehavior クラスからコールバック処理として呼び出されることを想定しており、コード 5.18 の 69 行 目にあるように、戻り値が false の場合は、反転して e.Calncel プロパティが true となります。Window.Closing イ ベントハンドラでは、このように e.Cancel プロパティを true にすることでウィンドウが閉じる処理を中断させること ができます。
WindowClosingBehavior 添付ビヘイビアを実際に使ってみましょう。MainView ウィンドウの XAML は次のようになり ます。
コード 5.20:WindowClosingBehavior 添付ビヘイビアを Window に適用する MainView.xaml
1 2 3 4 5 6 7 8 9 10 17 18 19 20 21 22 23
<Window x:Class="YKWpfIntroduction.Practices.Views.MainView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:b="clr-namespace:YKWpfIntroduction.Practices.Views.Behaviors"
Title="MainView" Height="300" Width="300"
b:WindowClosingBehavior.Callback="{Binding ClosingCallback}">
<DockPanel>
<Menu DockPanel.Dock="Top">
<MenuItem Header="ファイル (_F)">
<MenuItem Header="開く(_O)".../>
<Separator />
<MenuItem Header="終了 (_X)" Command="{Binding ExitCommand}" />
</MenuItem>
<MenuItem Header="ヘルプ (_H)" />
</Menu>
<StatusBar DockPanel.Dock="Bottom">
24 25 26 27 28 29
</StatusBar>
<Grid Background="MediumSeaGreen">
</Grid>
</DockPanel>
</Window>
アプリケーションを実行すると、「終了」メニューやウィンドウの「x」ボタンからアプリケーションを終了しようとし ても、3 回まで終了せず、4 回目で終了するようになっています。