MVVMで、メッセージボックスを表示する(参)

(弐)の最後でも書いたけど、各メッセージコマンド用のプロパティ名を固定で持てるなら、まとめて実装するという手抜きができる。最初に設定するプロパティを文字列からboolに変える。
↓はInfoMsgCommandNameの代わりに設定するbool型の添付プロパティ。

/// <summary>
/// メッセージコマンドを使うかどうかを取得します。
/// </summary>
/// <param name="obj">対象オブジェクト</param>
/// <returns>true:使う</returns>
public static bool GetUseMessageCommand(DependencyObject obj) {
return (bool)obj.GetValue(UseMessageCommandProperty);
}

/// <summary>
/// メッセージコマンドを使うかどうかを設定します。
/// </summary>
/// <param name="obj">対象オブジェクト</param>
/// <param name="value">true:使う</param>
public static void SetUseMessageCommand(DependencyObject obj, bool value) {
obj.SetValue(UseMessageCommandProperty, value);
}

/// <summary>メッセージコマンドを使うかどうか</summary>
public static readonly DependencyProperty UseMessageCommandProperty =
DependencyProperty.RegisterAttached("UseMessageCommand",
typeof(bool),
typeof(WindowBehavior),
new UIPropertyMetadata(false,
OnUseMessageCommandPropertyChanged));

デフォルト値をfalseにして、XAML上でtrueに設定する事で、OnUseMessageCommandPropertyChangedメソッドが呼び出される。

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

var window = dpObj as Window;

if (window != null && (bool)e.NewValue) {
window.DataContextChanged +=
new DependencyPropertyChangedEventHandler(Window_DataContextChanged);
window.Closed += new System.EventHandler(Window_Closed);
}

return;
}

DataContextChangedイベント用メソッド内の実装をガラっと変える。
タイトルとか設定できないなぁ…。

/// <summary>
/// DataContext変更イベントハンドラ
/// </summary>
/// <param name="sender">イベント発生元</param>
/// <param name="e">イベント引数</param>
private static void Window_DataContextChanged(object sender,
DependencyPropertyChangedEventArgs e) {
var window = sender as Window;
var dataContext = e.NewValue;

//念のための後始末 START!
var commandList = new []{
WindowBehavior.GetInfoMessageCommand(window),
WindowBehavior.GetWarningMessageCommand(window),
WindowBehavior.GetErrorMessageCommand(window),
WindowBehavior.GetConfirmMessageCommand(window)
};

foreach (var command in commandList.OfType<MessageCommand>()) {
if (command != null) {
command.Clear();
}
}

commandList = null;

var dpProps = new []{
WindowBehavior.InfoMessageCommandProperty,
WindowBehavior.WarningMessageCommandProperty,
WindowBehavior.ErrorMessageCommandProperty,
WindowBehavior.ConfirmMessageCommandProperty
};

foreach (var dp in dpProps) {
BindingOperations.ClearBinding(window, dp);
}

dpProps = null;
//念のための後始末 END!

//DataContext(ViewModel)がnull以外の時のみ実行する。
if (dataContext != null) {
//VM側プロパティ名、バインドする依存関係プロパティ
// 設定用コマンドオブジェクト、設定用メソッドを
//アイテムに持つTupleを作成する。
//別に無理にする必要はない。無名クラスでもやれなくはない。
//各コマンドとバインドするプロパティ名はここで固定に定義されている。
//VM側でプロパティを持たなければ、バインディングエラーになる
//エラーはとは言いながら、 例外ははっせいしない。
Tuple<string, DependencyProperty, ICommand,
Action<ICommand>>[] bindingList =
new [] {
Tuple.Create("InfoMsgCommand",
WindowBehavior.InfoMessageCommandProperty,
new InfoMessageCommand(window) as ICommand,
new Action<ICommand>(cmd =>
WindowBehavior.SetInfoMessageCommand(window, cmd))),
Tuple.Create("WarnMsgCommand",
WindowBehavior.WarningMessageCommandProperty,
new WarningMessageCommand(window) as ICommand,
new Action<ICommand>(cmd =>
WindowBehavior.SetWarningMessageCommand(window, cmd))),
Tuple.Create("ErrorMsgCommand",
WindowBehavior.ErrorMessageCommandProperty,
new ErrorMessageCommand(window) as ICommand,
new Action<ICommand>(cmd =>
WindowBehavior.SetErrorMessageCommand(window, cmd))),
Tuple.Create("ConfMsgCommand",
WindowBehavior.ConfirmMessageCommandProperty,
new ConfirmMessageCommand(window) as ICommand,
new Action<ICommand>(cmd =>
WindowBehavior.SetConfirmMessageCommand(window, cmd))),
};

foreach (var bindingTrio in bindingList) {
//source側のプロパティと、依存関係プロパティをバインディング
var binding = new Binding(bindingTrio.Item1) {
Source = dataContext,
Mode = BindingMode.OneWayToSource,
UpdateSourceTrigger =
UpdateSourceTrigger.PropertyChanged};
BindingOperations.SetBinding(window,
bindingTrio.Item2, binding);
//コマンド用インスタンスを生成して、設定します。
//この設定により、インスタンスがソース(VM側)に渡されます。
bindingTrio.Item4(bindingTrio.Item3);
}

bindingList = null;
}

return;
}

ViewModel側に、各プロパティとメソッドを用意する。

using System;
using System.Windows.Input;

namespace TawamureDays {

/// <summary>
/// ウィンドウ用ViewModelの基底クラス
/// </summary>
public class BaseWindowVM : BaseViewModel {

/// <summary>
/// 情報メッセージ用コマンド
/// </summary>
public ICommand InfoMsgCommand {
get; set;
}

/// <summary>
/// 警告メッセージ用コマンド
/// </summary>
public ICommand WarnMsgCommand {
get; set;
}

/// <summary>
/// エラーメッセージ用コマンド
/// </summary>
public ICommand ErrorMsgCommand {
get; set;
}

/// <summary>
/// 確認メッセージ用コマンド
/// </summary>
public ICommand ConfMsgCommand {
get; set;
}

/// <summary>
/// 情報メッセージを表示します。
/// </summary>
/// <param name="message">情報メッセージ</param>
protected void ShowInfoMessage(string message) {
this.InfoMsgCommand.Execute(message);
}

/// <summary>
/// 警告メッセージを表示します。
/// </summary>
/// <param name="message">警告メッセージ</param>
protected void ShowWarnMessage(string message) {
this.WarnMsgCommand.Execute(message);
}

/// <summary>
/// エラーメッセージを表示します。
/// </summary>
/// <param name="message">エラーメッセージ</param>
protected void ShowErrorMessage(string message) {
this.ErrorMsgCommand.Execute(message);
}

/// <summary>
/// 確認メッセージを表示します。
/// </summary>
/// <param name="message">確認メッセージ</param>
protected bool ConfirmMessage(string message) {
var result = false;
this.ConfMsgCommand.Execute(
Tuple.Create(message, new Action<bool>(res => result = res)));
return result;
}
}
}

「OneWayToSource」なので、getは要らないだろと思ったら、binding直後はgetter側で値を参照しようとするので、エラーになっていた。自分で書いたのにOrz。
で、XAMLでの設定は1つで済む。

<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}"
local:WindowBehavior.UseMessageCommand="True"
>
<StackPanel VerticalAlignment="Top" HorizontalAlignment="Left">
</StackPanel>
</Window>

使うときはこんな感じ。

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

this.ShowInfoMessage("情報メッセージです");
this.ShowWarnMessage("警告メッセージです");
this.ShowErrorMessage("エラーメッセージです");
var result = this.ConfirmMessage("確認メッセージです");
return;
}

各々メソッドの役割ははっきりしてるから、使い間違いはないかな。
(2012/09/09 追記)
プロパティ値変更メソッドの実装を改修する必要がでた。
マスターコンテンツとViewModelクラスの連携
スポンサーサイト
当サイトは基本をすっ飛ばしてます。基本文法等は、@ITをどうぞ
カテゴリー: WPF4 | コメント: 0 | トラックバック: 0


この記事へのコメント

コメントの投稿

非公開コメント


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

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

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

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