Livet で Messenger を使ってみる(その参)

本日はさらに踏み込んで、Livet が提供するメッセージングアクションのうち

  • InformationDialogInteractionMessageAction
  • ConfirmationDialogInteractionMessageAction
  • OpenFileDialogInteractionMessageAction
  • SaveFileDialogInteractionMessageAction

の四つを試してみたいと思います。これらのアクションを使えば、コードを一切書かずに確認ダイアログやファイル選択ダイアログを使うことも可能になります。
え? ViewModel のコマンドでダイアログ使えばいいじゃんって? 確かにそうなんですが、View にこれらのダイアログを表示する責任を押し付ける*1ことで、ViewModel の単体テストが可能になるというメリットがあります。

ソースのコメントを見ると、各アクションは以下の機能が提供されてます。

InformationDialogInteractionMessageAction 情報ダイアログを表示するアクションです。
ConfirmationDialogInteractionMessageAction 確認ダイアログを表示するアクションです。
OpenFileDialogInteractionMessageAction 「ファイルを開く」ダイアログを表示するアクションです。
SaveFileDialogInteractionMessageAction 「ファイルを保存する」ダイアログを表示するアクションです。



では早速ためしてみましょう。

ViewModel を編集する


Visual Studio 2010 を起動し、新しいプロジェクトを作成します。今回も Visual Basic を選択し*2、Livet WPF4 MVVM アプリケーションを選択。プロジェクト名を「MessagingSample」にして「OK」ボタンをクリックします。


プロジェクトが作成されたらいったんビルドしときましょう。

次に ViewModel にコマンドを作成します。「MainWindowViewModel.vb」を開き、ViewModel にコマンドを四つ追加します。InformCommand は CanExecute 無しの ViewModelCommand を作成するためスニペットlvcomn と打ち込んでタブキー。それ以外はすべて View からのレスポンスを検知する リスナーコマンド を作るので llcomnスニペットでコマンドを作ります。


以下の表を参考に展開したスニペットを編集してください。Confirm 〜 Save の三つのコマンドはリスナーコマンドなので、View からのメッセージを受信するため型パラメータを指定します。

スニペット プレフィックス コマンドの型パラメータ
lvcomn Inform なし
llcomn Confirm ConfirmationMessage
llcomn Open OpeningFileSelectionMessage
llcomn Save SavingFileSelectionMessage


するとこうなります。

Public Class MainWindowViewModel
	Inherits ViewModel

#Region "InformCommand"
	Private _InformCommand As ViewModelCommand

	Public ReadOnly Property InformCommand() As ViewModelCommand
		Get
			If _InformCommand Is Nothing Then
				_InformCommand = New ViewModelCommand(AddressOf Inform)
			End If
			Return _InformCommand
		End Get
	End Property

	Private Sub Inform()

	End Sub
#End Region

#Region "ConfirmCommand"
	Private _ConfirmCommand As ListenerCommand(Of ConfirmationMessage)

	Public ReadOnly Property ConfirmCommand() As ListenerCommand(Of ConfirmationMessage)
		Get
			If _ConfirmCommand Is Nothing Then
				_ConfirmCommand = New ListenerCommand(Of ConfirmationMessage)(AddressOf Confirm)
			End If
			Return _ConfirmCommand
		End Get
	End Property

	Private Sub Confirm(ByVal parameter As ConfirmationMessage)

	End Sub
#End Region

#Region "OpenCommand"
	Private _OpenCommand As ListenerCommand(Of OpeningFileSelectionMessage)

	Public ReadOnly Property OpenCommand() As ListenerCommand(Of OpeningFileSelectionMessage)
		Get
			If _OpenCommand Is Nothing Then
				_OpenCommand = New ListenerCommand(Of OpeningFileSelectionMessage)(AddressOf Open)
			End If
			Return _OpenCommand
		End Get
	End Property

	Private Sub Open(ByVal parameter As OpeningFileSelectionMessage)

	End Sub
#End Region

#Region "SaveCommand"
	Private _SaveCommand As ListenerCommand(Of SavingFileSelectionMessage)

	Public ReadOnly Property SaveCommand() As ListenerCommand(Of SavingFileSelectionMessage)
		Get
			If _SaveCommand Is Nothing Then
				_SaveCommand = New ListenerCommand(Of SavingFileSelectionMessage)(AddressOf Save)
			End If
			Return _SaveCommand
		End Get
	End Property

	Private Sub Save(ByVal parameter As SavingFileSelectionMessage)

	End Sub
#End Region

End Class

情報コマンド

InformCommand はとりあえずメッセージを表示するだけにしておきます。

#Region "InformCommand"
	Private _InformCommand As ViewModelCommand

	Public ReadOnly Property InformCommand() As ViewModelCommand
		Get
			If _InformCommand Is Nothing Then
				_InformCommand = New ViewModelCommand(AddressOf Inform)
			End If
			Return _InformCommand
		End Get
	End Property

	Private Sub Inform()
		MessageBox.Show("InformCommand コマンドが実行されました。")
	End Sub
#End Region

確認コマンド

ConfirmCommand は少し複雑です。View で表示した確認ダイアログで Yes が押されたのを検知するため、ConfirmationMessage.Responce プロパティで戻り値を判定します。HasValue と Value が True なら Yes が選択されているので、処理を実行します。

#Region "ConfirmCommand"
	Private _ConfirmCommand As ListenerCommand(Of ConfirmationMessage)

	Public ReadOnly Property ConfirmCommand() As ListenerCommand(Of ConfirmationMessage)
		Get
			If _ConfirmCommand Is Nothing Then
				_ConfirmCommand = New ListenerCommand(Of ConfirmationMessage)(AddressOf Confirm)
			End If
			Return _ConfirmCommand
		End Get
	End Property

	Private Sub Confirm(ByVal parameter As ConfirmationMessage)
		If (parameter.Response.HasValue AndAlso parameter.Response.Value) Then
			MessageBox.Show("「はい」が選択されました。")
		End If
	End Sub
#End Region

開くと保存コマンド

OpenCommand と SaveCommand は 各Message の Response プロパティに選択されたファイル名が入ってきます。このコマンドでは、ファイルが選択されない場合、戻り値は Null (VB なら Nothing) なのでガード節を設けて直ちに抜け、ファイルが選択されたら MessageBox でファイル名を表示するよう実装します。

#Region "OpenCommand"
	Private _OpenCommand As ListenerCommand(Of OpeningFileSelectionMessage)

	Public ReadOnly Property OpenCommand() As ListenerCommand(Of OpeningFileSelectionMessage)
		Get
			If _OpenCommand Is Nothing Then
				_OpenCommand = New ListenerCommand(Of OpeningFileSelectionMessage)(AddressOf Open)
			End If
			Return _OpenCommand
		End Get
	End Property

	Private Sub Open(ByVal parameter As OpeningFileSelectionMessage)
		If String.IsNullOrEmpty(parameter.Response) Then Return
		MessageBox.Show("選択されたファイルは " + parameter.Response + " です。")
	End Sub
#End Region

#Region "SaveCommand"
	Private _SaveCommand As ListenerCommand(Of SavingFileSelectionMessage)

	Public ReadOnly Property SaveCommand() As ListenerCommand(Of SavingFileSelectionMessage)
		Get
			If _SaveCommand Is Nothing Then
				_SaveCommand = New ListenerCommand(Of SavingFileSelectionMessage)(AddressOf Save)
			End If
			Return _SaveCommand
		End Get
	End Property

	Private Sub Save(ByVal parameter As SavingFileSelectionMessage)
		If String.IsNullOrEmpty(parameter.Response) Then Return
		MessageBox.Show("保存しようとしたファイル名は " + parameter.Response + " です。")
	End Sub
#End Region


ここまでできたら、次は Blend で作業します。プロジェクトをビルドして保存し、Blend で開いてください。


View をデザインする

View を Blend で開き、画面サイズを「高さ 300・幅 200」に変更します。次に「オブジェクトとタイムライン」で「Grid」を選択、「レイアウトの種類の変更」から「StackPanel」を選択してください。
「アセット」から「Button」を4つ「StackPanel」にドラッグして追加し、以下の順に上から Content を変更します。

  • 表示
  • 確認
  • 開く
  • 保存

するとこうなります。

<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
	xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
	xmlns:l="http://schemas.livet-mvvm.net/2011/wpf"
	xmlns:local="clr-namespace:MessagingSample"
	x:Class="MainWindow"
	Title="MainWindow" Height="300" Width="200">
	<Window.DataContext>
		<local:MainWindowViewModel />
	</Window.DataContext>
	<StackPanel>
		<Button Content="表示"/>
		<Button Content="確認"/>
		<Button Content="開く"/>
		<Button Content="保存"/>
	</StackPanel>
</Window>

情報表示メッセージ

まず情報表示メッセージから設定しましょう。「アセット」→「ビヘイビアー」→「Livet」→「メッセージング」と展開し、「InformationDialogInteractionMessageAction」を選択。


「オブジェクトとタイムライン」の一番上の Button にドラッグ&ドロップします。


「InformationDialogInteractionMessageAction」のプロパティを編集します。「プロパティ」→「その他」→「DirectInteractionMessage」の「新規作成」をクリックして、「CollbackCommand」の「詳細オプション」→「データバインド」に ViewModel で定義した 「InformCommand」コマンドをバインドします。さらに「Message」の「新規作成」をクリック。図のように

Caption 表示
Image Asterisk
Text 情報を表示します。

こんな感じでプロパティを設定します。


ここまで設定したら、F5キーで実行してみましょう。「表示」ボタンをクリックすると、最初に Blend で設定したメッセージが表示されます。


OK ボタンをクリックすると、CollbackCommand でバインドしたコマンドが実行されます。このサンプルではあえてコマンドをバインドさせてますが、ダイアログだけ表示したい場合はコマンドをバインドさせる必要ありませんね。


ここまでの XAML です。

<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
	xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
	xmlns:l="http://schemas.livet-mvvm.net/2011/wpf"
	xmlns:local="clr-namespace:MessagingSample"
	xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
	x:Class="MainWindow"
	Title="MainWindow" Height="300" Width="200">
	<Window.DataContext>
		<local:MainWindowViewModel />
	</Window.DataContext>
	<StackPanel>
		<Button Content="表示">
			<i:Interaction.Triggers>
				<i:EventTrigger EventName="Click">
					<l:InformationDialogInteractionMessageAction>
						<l:DirectInteractionMessage CallbackCommand="{Binding InformCommand, Mode=OneWay}">
							<l:InformationMessage Caption="表示" Image="Asterisk" Text="情報を表示します。"/>
						</l:DirectInteractionMessage>
					</l:InformationDialogInteractionMessageAction>
				</i:EventTrigger>
			</i:Interaction.Triggers>
		</Button>
		<Button Content="確認"/>
		<Button Content="開く"/>
		<Button Content="保存"/>
	</StackPanel>
</Window>

確認メッセージ

次は確認メッセージを設定します。「アセット」→「ビヘイビアー」→「Livet」→「メッセージング」と展開し「ConfirmationDialogInteractionMessageAction」を選択したら、「オブジェクトとタイムライン」で上から二番目の「確認」ボタンにドラッグしてください。


次は「ConfirmationDialogInteractionMessageAction」の設定です。先ほどの確認メッセージと同じようにプロパティを編集します。「プロパティ」→「その他」→「DirectInteractionMessage」の「新規作成」をクリックして、「CollbackCommand」の「詳細オプション」→「データバインド」に ViewModel で定義した 「ConfirmCommand」コマンドをバインドします。さらに「Message」を展開して、以下の図のように各プロパティを設定します。


設定できたら F5キーで実行して動作を確認してみましょう。「確認」ボタンをクリックすると、Message のプロパティで設定したとおり確認ダイアログが表示されます。


「いいえ」をクリックすると何も起きませんが、「はい」を選択すると CollbacckCommand でバインドさせた ConfirmCommand 内の条件が満たされ、ダイアログが表示されます。Visual Studio で Confirm メソッドに渡された ConfirmationMessage.Responce の戻り値をウォッチすれば、より動作が詳しく判ると思います。


ここまでの XAML です。

<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
	xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
	xmlns:l="http://schemas.livet-mvvm.net/2011/wpf"
	xmlns:local="clr-namespace:MessagingSample"
	xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
	x:Class="MainWindow"
	Title="MainWindow" Height="300" Width="200">
	<Window.DataContext>
		<local:MainWindowViewModel />
	</Window.DataContext>
	<StackPanel>
		<Button Content="表示">
			<i:Interaction.Triggers>
				<i:EventTrigger EventName="Click">
					<l:InformationDialogInteractionMessageAction>
						<l:DirectInteractionMessage CallbackCommand="{Binding InformCommand, Mode=OneWay}">
							<l:InformationMessage Caption="表示" Image="Asterisk" Text="情報を表示します。"/>
						</l:DirectInteractionMessage>
					</l:InformationDialogInteractionMessageAction>
				</i:EventTrigger>
			</i:Interaction.Triggers>
		</Button>
		<Button Content="確認">
			<i:Interaction.Triggers>
				<i:EventTrigger EventName="Click">
					<l:ConfirmationDialogInteractionMessageAction>
						<l:DirectInteractionMessage CallbackCommand="{Binding ConfirmCommand, Mode=OneWay}">
							<l:ConfirmationMessage Button="YesNo" Caption="確認" Image="Question" Text="実行しますか?"/>
						</l:DirectInteractionMessage>
					</l:ConfirmationDialogInteractionMessageAction>
				</i:EventTrigger>
			</i:Interaction.Triggers>
		</Button>
		<Button Content="開く"/>
		<Button Content="保存"/>
	</StackPanel>
</Window>

開くと保存メッセージ

最後は開くと保存メッセージです。「アセット」から「オブジェクトとタイムライン」の「開く」ボタンに「OpenFileDialogInteractionMessageAction」、「保存」ボタンに「SaveFileDialogInteractionMessageAction」をドロップします。


各メッセージアクションは、以下の画像を参考に設定してください。まず「OpenFileDialogInteractionMessageAction」。「CollbackCommand」は「OpenCommand」とバインドしています。


次は「SaveFileDialogInteractionMessageAction」です。「CollbackCommand」は「SaveCommand」とバインドします。


ここまでできたら F5 で実行します。「開く」ボタンをクリックすると、OpenDialog が起動します。フィルターが指定された拡張子になっていることを確認してください。


キャンセルするとそのまま閉じますが、ファイルを選択して「開く」ボタンをクリックすると、ViewModel で実装したとおりメッセージが表示されます。


完成した XAML です。

<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
	xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
	xmlns:l="http://schemas.livet-mvvm.net/2011/wpf"
	xmlns:local="clr-namespace:MessagingSample"
	xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
	x:Class="MainWindow"
	Title="MainWindow" Height="300" Width="200">
	<Window.DataContext>
		<local:MainWindowViewModel />
	</Window.DataContext>
	<StackPanel>
		<Button Content="表示">
			<i:Interaction.Triggers>
				<i:EventTrigger EventName="Click">
					<l:InformationDialogInteractionMessageAction>
						<l:DirectInteractionMessage CallbackCommand="{Binding InformCommand, Mode=OneWay}">
							<l:InformationMessage Caption="表示" Image="Asterisk" Text="情報を表示します。"/>
						</l:DirectInteractionMessage>
					</l:InformationDialogInteractionMessageAction>
				</i:EventTrigger>
			</i:Interaction.Triggers>
		</Button>
		<Button Content="確認">
			<i:Interaction.Triggers>
				<i:EventTrigger EventName="Click">
					<l:ConfirmationDialogInteractionMessageAction>
						<l:DirectInteractionMessage CallbackCommand="{Binding ConfirmCommand, Mode=OneWay}">
							<l:ConfirmationMessage Button="YesNo" Caption="確認" Image="Question" Text="実行しますか?"/>
						</l:DirectInteractionMessage>
					</l:ConfirmationDialogInteractionMessageAction>
				</i:EventTrigger>
			</i:Interaction.Triggers>
		</Button>
	<Button Content="開く">
		<i:Interaction.Triggers>
			<i:EventTrigger EventName="Click">
				<l:OpenFileDialogInteractionMessageAction>
					<l:DirectInteractionMessage CallbackCommand="{Binding OpenCommand, Mode=OneWay}">
						<l:OpeningFileSelectionMessage Filter="テキストファイル(*.txt)|*.txt|すべてのファイル(*.*)|*.*" Title="テキストファイルを開く"/>
					</l:DirectInteractionMessage>
				</l:OpenFileDialogInteractionMessageAction>
			</i:EventTrigger>
		</i:Interaction.Triggers>
	</Button>
	<Button Content="保存">
		<i:Interaction.Triggers>
			<i:EventTrigger EventName="Click">
				<l:SaveFileDialogInteractionMessageAction>
					<l:DirectInteractionMessage CallbackCommand="{Binding SaveCommand, Mode=OneWay}">
						<l:SavingFileSelectionMessage Filter="テキストファイル(*.txt)|*.txt" Title="テキストを保存"/>
					</l:DirectInteractionMessage>
				</l:SaveFileDialogInteractionMessageAction>
			</i:EventTrigger>
		</i:Interaction.Triggers>
	</Button>
	</StackPanel>
</Window>


本日は Livet が提供する4つのメッセージアクションを試してみました。次は機会を見て画面遷移を行うアクション「TransitionInteractionMessageAction」を試してみたいと思います。


 

*1:この表現が妥当かはわかりませんが(汗・・・

*2:相変わらず VB ユーザーに優しい仕様です。C#使いなら、この程度のコード読めるよね