スポンサーサイト

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。
当サイトは基本をすっ飛ばしてます。基本文法等は、@ITをどうぞ
カテゴリー: スポンサー広告

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

コマンドクラスを実装したので、次は添付プロパティを実装する。
今回の実装方法として、コマンドクラスのインスタンスをVM側ではなく、View側で生成して、データバンディングさせる必要がある。
生成するキッカケを何にするか、と考えた時に、添付プロパティのプロパティ値の変更通知機能を使う事にした。

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

namespace TawamureDays {

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

/// <summary>
/// 情報メッセージ用コマンドを取得します。
/// </summary>
/// <param name="obj">対象オブジェクト(Window)</param>
/// <returns>情報メッセージ用コマンド</returns>
private static ICommand GetInfoMessageCommand(DependencyObject obj) {
return (ICommand)obj.GetValue(InfoMessageCommandProperty);
}

/// <summary>
/// 情報メッセージ用コマンドを設定します。
/// </summary>
/// <param name="obj">対象オブジェクト(Window)</param>
/// <param name="value">情報メッセージ用コマンド</param>
private static void SetInfoMessageCommand(DependencyObject obj, ICommand value) {
obj.SetValue(InfoMessageCommandProperty, value);
}

/// <summary>情報メッセージ用コマンド</summary>
private static readonly DependencyProperty InfoMessageCommandProperty =
DependencyProperty.RegisterAttached("InfoMessageCommand",
typeof(ICommand),
typeof(WindowBehavior),
new UIPropertyMetadata(null));


/// <summary>
/// 情報メッセージ用コマンド名(プロパティ名)を取得します。
/// </summary>
/// <param name="obj">対象オブジェクト(Window)</param>
/// <returns>情報メッセージ用コマンド名</returns>
public static string GetInfoMsgCommandName(DependencyObject obj) {
return (string)obj.GetValue(InfoMsgCommandNameProperty);
}

/// <summary>
/// 情報メッセージ用コマンド名(プロパティ名)を設定します。
/// </summary>
/// <param name="obj">対象オブジェクト(Window)</param>
/// <param name="value">情報メッセージ用コマンド名</param>
public static void SetInfoMsgCommandName(DependencyObject obj, string value) {
obj.SetValue(InfoMsgCommandNameProperty, value);
}

/// <summary>情報メッセージ用コマンド名(プロパティ名)</summary>
public static readonly DependencyProperty InfoMsgCommandNameProperty =
DependencyProperty.RegisterAttached("InfoMsgCommandName",
typeof(string),
typeof(WindowBehavior),
new UIPropertyMetadata(null,
OnInfoMessageCommandNamePropertyChanged));

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

var window = dpObj as Window;

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

return;
}

/// <summary>
/// ウィンドウ終了イベントハンドラ
/// </summary>
/// <param name="sender">イベント発生元</param>
/// <param name="e">イベント引数</param>
private static void Window_Closed(object sender, System.EventArgs e) {
var window = sender as Window;
window.DataContextChanged -=
new DependencyPropertyChangedEventHandler(Window_DataContextChanged);
window.Closed -= new System.EventHandler(Window_Closed);
return;
}

/// <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;

//念のための後始末
var infoMsgCommand = WindowBehavior.
GetInfoMessageCommand(window) as InfoMessageCommand;

if (infoMsgCommand != null) {
infoMsgCommand.Clear();
infoMsgCommand = null;

WindowBehavior.SetInfoMessageCommand(window, null);
BindingOperations.ClearBinding(window,
WindowBehavior.InfoMessageCommandProperty);
}

//コマンド名(プロパティ名)
var msgCommandName = WindowBehavior.GetInfoMsgCommandName(window);

if (!string.IsNullOrWhiteSpace(msgCommandName)) {
//source側のプロパティと、InfoMessageCommandPropertyをバインディング
var binding = new Binding(msgCommandName) {
Source = dataContext,
Mode = BindingMode.OneWayToSource,
UpdateSourceTrigger =
UpdateSourceTrigger.PropertyChanged};
BindingOperations.SetBinding(window,
WindowBehavior.InfoMessageCommandProperty, binding);
//コマンド用インスタンスを生成して、設定します。
//この設定により、インスタンスがソース(VM側)に渡されます。
WindowBehavior.SetInfoMessageCommand(window,
new InfoMessageCommand(window));
}

return;
}
}
}

・使う添付プロパティは2つ(InfoMsgCommandNamePropertyとInfoMessageCommandProperty)
・InfoMsgCommandNamePropertyをXAML側で使う(バインドさせるプロパティ名を設定する)。
・InfoMsgCommandNamePropertyの設定値変更をOnInfoMessageCommandNamePropertyChangedメソッドで受ける。
・上記メソッド内で、DataContextChangedイベントにメソッドを登録する。
・DataContextが変更されたら、ソース(ViewModel)側とInfoMessageCommandPropertyをバンディングさせる。
・このとき、Modeを「OneWayToSource」にする。
・バインディングを行った後で、InfoMessageCommandPropertyにコマンドのインスタンスを設定する。
・Window_Close等のイベントは、後始末用

「OneWayToSource」モードはターゲットからソースの方向へ値を転送する際に使うモード。ただし、このモードでも、バンディング直後はソース側からターゲット側に値が流れこんでくる。なので、値をセットしてからバインディングを行うと、インスタンスはソース側のnullに上書きされてしまう。
次に、Window用VMにその受け皿となるプロパティを実装する。

using System;
using System.Windows.Input;

namespace TawamureDays {

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

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

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

このプロパティ名「InfoMsgCommand」がInfoMsgCommandNamePropertyに設定する値と同じ事が必須となる。getのアクセス修飾子をprivateにしているのは、直接使わせない為。代わりにShowInfoMessageメソッドを提供する。
最後に、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.InfoMsgCommandName="InfoMsgCommand">
<StackPanel VerticalAlignment="Top" HorizontalAlignment="Left">
</StackPanel>
</Window>
これで、ShowInfoMessageメソッドを使う事でメッセージボックスの表示が可能になる。
ただし、この実装。よく考えると、1種類毎に2つの添付プロパティが必要になるし、同じイベントハンドラに3つも4つもメソッドを登録せにゃならんしで、結構めんどくさい。まあ、一度設定してしまえば、後は楽なんだけど。
基底クラスにプロパティを用意するなら、プロパティ名は固定で良いかもしれない。
スポンサーサイト
当サイトは基本をすっ飛ばしてます。基本文法等は、@ITをどうぞ
カテゴリー: WPF4 | コメント: 0 | トラックバック: 0


この記事へのコメント

コメントの投稿

非公開コメント


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

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

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

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

上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。