対Window用ViewModelをとりあえず作るか。(続き)

対Window用ViewModelをとりあえず作るか。
の続き。
各イベント用コマンドを稼働させるための添付プロパティを実装する。
Loadedイベント用は前に作ったので省略。

○Unloadedイベント用コマンド
Unloadedイベントは、ウィンドウの終了後に発生する。ただし、TabページやPage系とBindingすると、タブページの切り替え等でUnloadedイベントは発生する。

using System;
using System.Windows;
using System.Windows.Input;

namespace TawamureDays {

/// <summary>
/// FrameworkElement用ビヘイビアクラス
/// </summary>
public static class FrameworkElementBehavior {

/// <summary>
/// Unloaded用コマンドを取得します。
/// </summary>
/// <param name="obj">対象オブジェクト(FrameworkElement)</param>
/// <returns>Unloaded用コマンド</returns>
public static ICommand GetUnloadedCommand(DependencyObject obj) {
return (ICommand)obj.GetValue(UnloadedCommandProperty);
}

/// <summary>
/// Unloaded用コマンドを設定します。
/// </summary>
/// <param name="obj">対象オブジェクト(FrameworkElement)</param>
/// <param name="value">Unloaded用コマンド</param>
public static void SetUnloadedCommand(DependencyObject obj, ICommand value) {
obj.SetValue(UnloadedCommandProperty, value);
}

/// <summary>Unloaded用コマンド</summary>
public static readonly DependencyProperty UnloadedCommandProperty =
DependencyProperty.RegisterAttached("UnloadedCommand",
typeof(ICommand),
typeof(FrameworkElementBehavior),
new UIPropertyMetadata(null,
OnUnloadedCommandPropertyChanged));

/// <summary>
/// Unloadedプロパティ値変更イベントハンドラ
/// </summary>
/// <param name="dpObj">変更発生元</param>
/// <param name="e">イベント引数</param>
private static void OnUnloadedCommandPropertyChanged(
DependencyObject dpObj, DependencyPropertyChangedEventArgs e) {
var frwElement = dpObj as FrameworkElement;

if (frwElement == null) {
//FrameworkElementを継承していない
//クラスインスタンスは無視します。
return;
}

if (e.NewValue != null) {
if (!frwElement.IsLoaded) {
frwElement.Loaded += new RoutedEventHandler(FrwElement_Loaded2);
} else {
FrameworkElementBehavior.FrwElement_Loaded2(
frwElement,
new RoutedEventArgs {
Source = frwElement, Handled = false});
}
}
}

/// <summary>
/// Loadedイベントハンドラ用メソッド
/// </summary>
/// <param name="sender">イベント発生元</param>
/// <param name="e">イベント引数</param>
private static void FrwElement_Loaded2(object sender, RoutedEventArgs e) {
var frwElement = sender as FrameworkElement;
//登録したメソッドは削除しないと、メモリリークの元になる。
frwElement.Loaded -= new RoutedEventHandler(FrwElement_Loaded2);

frwElement.Unloaded += new RoutedEventHandler(FrwElement_Unloaded);

var window = Window.GetWindow(frwElement);
Action<object, EventArgs> closedHandler = null;

closedHandler = (sender2, e2) => {
frwElement.Unloaded -=
new RoutedEventHandler(FrwElement_Unloaded);

if (closedHandler != null) {
window.Closed -= new EventHandler(closedHandler);
closedHandler = null;
}
};

window.Closed += new EventHandler(closedHandler);
return;
}

/// <summary>
/// Unloadedイベントハンドラ
/// </summary>
/// <param name="sender">イベント発生元</param>
/// <param name="e">イベント引数</param>
private static void FrwElement_Unloaded(object sender, RoutedEventArgs e) {
var frwElement = sender as FrameworkElement;
//登録したメソッドは削除しないと、メモリリークの元になる。
frwElement.Unloaded -= new RoutedEventHandler(FrwElement_Loaded);

var command = FrameworkElementBehavior.GetUnloadedCommand(frwElement);

if (command != null && command.CanExecute(e)) {
command.Execute(e);
}
return;
}
}
}

Unloadedイベントへのハンドラ(メソッド)登録をプロパティ値変更時ではなく、Loadedイベント時に行う。
これは、Loaded時にClosedイベントをフックし、画面終了時にはUnloadedのコマンドを呼び出さないようにするための処置。

次、Closingイベント用コマンドを実装する。ClosingイベントはWindowクラスが持つので、WindowBehaviorに実装する。

using System.Windows;
using System.Windows.Input;
using System.ComponentModel;

namespace TawamureDays {

/// <summary>
/// ウィンドウ(Window)用ビヘイビアクラス
/// </summary>
public static class WindowBehavior {

#region ClosingCommandプロパティ

/// <summary>
/// Closingイベント用コマンドを取得します。
/// </summary>
/// <param name="obj">対象オブジェクト(Window)</param>
/// <returns>Closingイベント用コマンド</returns>
public static ICommand GetClosingCommand(DependencyObject obj) {
return (ICommand)obj.GetValue(ClosingCommandProperty);
}

/// <summary>
/// Closingイベント用コマンドを設定します。
/// </summary>
/// <param name="obj">対象オブジェクト(Window)</param>
/// <param name="value">Closingイベント用コマンド</param>
public static void SetClosingCommand(DependencyObject obj, ICommand value) {
obj.SetValue(ClosingCommandProperty, value);
}

/// <summary>Closingイベント用コマンド</summary>
public static readonly DependencyProperty ClosingCommandProperty =
DependencyProperty.RegisterAttached("ClosingCommand",
typeof(ICommand),
typeof(WindowBehavior),
new UIPropertyMetadata(null,
OnClosingCommandPropertyChanged));

/// <summary>
/// ClosingCommandプロパティ値変更イベントハンドラ
/// </summary>
/// <param name="dpObj">変更発生元</param>
/// <param name="e">イベント引数</param>
private static void OnClosingCommandPropertyChanged(
DependencyObject dpObj, DependencyPropertyChangedEventArgs e) {

var window = dpObj as Window;

if (window == null) {
//Window以外を無視
//クラスインスタンスは無視します。
return;
}

if (e.NewValue != null) {
window.Closing += new CancelEventHandler(Window_Closing);
}
}

/// <summary>
/// ウィンドウ終了時イベントハンドラ(終了確定ではない)
/// </summary>
/// <param name="sender">イベント発生元</param>
/// <param name="e">イベント引数</param>
private static void Window_Closing(object sender, CancelEventArgs e) {
var window = sender as Window;

var command = WindowBehavior.GetClosingCommand(window);

if (command != null && command.CanExecute(e)) {
command.Execute(e);

if (!e.Cancel) {
window.Closing -= new CancelEventHandler(Window_Closing);
WindowBehavior.SetClosingCommand(window, null);
}
}
}

#endregion
}
}

コマンドパラメータとして、CancelEventArgsクラスのインスタンスを渡す。
Execute側でキャストして、e.Cancel=trueにすれば、画面終了をキャンセルできる。
Window用のViewModelを実装する。

using System;
using System.Windows;
using System.Windows.Data;

namespace TawamureDays {

/// <summary>
/// ウィンドウ用ViewModelクラス
/// </summary>
public sealed class MainWindowVM : BaseWindowVM {

#region BaseWindowVMメンバ

/// <summary>
/// Loadedイベント発生時のメソッド
/// </summary>
/// <param name="parameter">パラメータ</param>
protected override void OnLoaded(object parameter) {
base.OnLoaded(parameter);
}

/// <summary>
/// Unloadedイベント発生時のメソッド
/// </summary>
/// <param name="parameter">パラメータ</param>
protected override void OnUnloaded(object parameter) {
base.OnUnloaded(parameter);
}

/// <summary>
/// Closingイベント発生時のメソッド
/// </summary>
/// <param name="parameter">パラメータ</param>
protected override void OnClosing(object parameter) {
base.OnClosing(parameter);
}

#endregion
}
}

実装した各コマンド用添付プロパティをXAML側に設定する。

<Window x:Class="TawamureDays.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:nfc="http://nichia.jp/sys/foundation/component/gui"
xmlns:local="clr-namespace:TawamureDays"
Title="MainWindow" Height="213" Width="385"
local:WindowBehavior.ClosingCommand="{Binding ClosingCommand}"
local:FrameworkElementBehavior.UnloadedCommand="{Binding UnloadedCommand}"
local:FrameworkElementBehavior.OnLoadedCommand="{Binding LoadedCommand}"
>
<StackPanel VerticalAlignment="Top" HorizontalAlignment="Left">
</StackPanel>
</Window>

コードビハインド

using System;
using System.Windows;
using System.Windows.Data;
namespace TawamureDays {

/// <summary>
/// MainWindow.xaml の相互作用ロジック
/// </summary>
public partial class MainWindow : Window {

public MainWindow() {
InitializeComponent();
this.DataContext = new MainWindowVM();
return;
}
}
}

おおう。長い。まあ、これで起動時と終了時に必ず呼び出されるメソッドができるので、
OnLoadedメソッド→起動時の処理
OnClosingメソッド→終了時の処理。終了確認メッセージ表示も可能。
ってなるかな。
ああ、メッセージボックスってVM側であまり直接は呼び出さないな。
次はメッセージボックス系か。
スポンサーサイト
当サイトは基本をすっ飛ばしてます。基本文法等は、@ITをどうぞ
カテゴリー: WPF4 | コメント: 0 | トラックバック: 0


この記事へのコメント

コメントの投稿

非公開コメント


サイドバー背後固定表示サンプル

当ブログに書かれたソースコードは流用自由です。

バグ、スペルミス等はありうる事です。

ご利用の際は自己責任でお願いしますm(_ _)m