VB のたまご

作成日: 2017/03/20, 更新日: 2017/03/20



メッセンジャー(画面アクション)

  •  (※もし、直接このページにたどり着いた方は、以前投稿した Livetアプリケーション作成時の注意 を読んで、 名前空間やプロジェクト追加に気を付ける必要があることを知ってください)

  • イメージ
  •  ここからは、メッセンジャー機能を利用した、画面を扱う処理を学習していきます。 ViewModel → View へ、画面操作を依頼する部分ですね。 Messenger は「使者、配達人」という意味合いがあり、画面を運ぶという意味なのかなと思っています。

  • スポンサーリンク


  •  それでは最初にサンプルを見てみましょう。サンプルはボタンを押すと「画面を終了する」処理になります。 以下では、「Livet WPF4.5 MVVM アプリケーション」で作成していて、ViewModel と View を範囲にしています。

  •  まずは、ViewModel からです。 ViewModels フォルダ内に、「Livet WPF4 ビュー・モデル」を選択して、WindowActionWindow1ViewModel.vb と言う名前で作成して、以下のように実装します。

  • Namespace ViewModels
        Public Class WindowActionWindow1ViewModel
            Inherits ViewModel
    
            ' コードスニペットを利用して楽に実装する
            ' lvcomn と入力してタブキーを押すとコードスニペットが挿入される
            ' XxxCommand とコマンド名は、任意の文字列と最後に Command と付けるが、
            ' Xxx の部分が選択されているので名前を付けてエンターキーを押す
            ' するとコマンド名とコマンドに紐づける、実行処理名が決まる
            ' lvcom の場合は、実行有無の判定処理も含まれる
            ' ViewModelCommand コマンドは、引数無しのコマンド
    
    #Region "Click1Command"
            Private _Click1Command As ViewModelCommand
    
            Public ReadOnly Property Click1Command() As ViewModelCommand
                Get
                    If _Click1Command Is Nothing Then
                        _Click1Command = New ViewModelCommand(AddressOf Click1)
                    End If
                    Return _Click1Command
                End Get
            End Property
    
            Private Sub Click1()
    
                ' View に依頼。WindowCloseAction というキー名で、画面を閉じる、アクションを依頼
                Messenger.Raise(New WindowActionMessage(WindowAction.Close, "WindowCloseAction"))
    
                ' 今回のサンプルは「画面を閉じる」ので、依頼の後などにデータ保存したりする
                Console.WriteLine("(もしあれば)データ保存処理など")
    
            End Sub
    #End Region
    
            ' 今度は、ListenerCommand コマンドという、引数を1つ持つコマンドの例
            ' かつ、実行の有無判定処理付き
            ' llcom と入力してタブキーを押す。コマンド名を入力してタブキーを押す。
            ' 引数の型を入力してエンターキーを押す。
    
    #Region "Click2Command"
            Private _Click2Command As ListenerCommand(Of WindowActionMessage)
    
            Public ReadOnly Property Click2Command() As ListenerCommand(Of WindowActionMessage)
                Get
                    If _Click2Command Is Nothing Then
                        _Click2Command = New ListenerCommand(Of WindowActionMessage)(AddressOf Click2, AddressOf CanClick2)
                    End If
                    Return _Click2Command
                End Get
            End Property
    
            ' いつでも実行可能にする
            Private Function CanClick2() As Boolean
    
                Return True
    
            End Function
    
            Private Sub Click2(ByVal parameter As WindowActionMessage)
    
                ' ここでブレークポイントを付けると、ちゃんとコールバックで呼び出されていることが分かります。
                ' 今回のサンプルは「画面を閉じる」ので、依頼の後などにデータ保存したりする
                Console.WriteLine("(もしあれば)データ保存処理など")
    
            End Sub
    #End Region
    
    
    
    
    
            ' コマンド、プロパティの定義にはそれぞれ 
            ' 
            '  lvcom   : ViewModelCommand
            '  lvcomn  : ViewModelCommand(CanExecute無)
            '  llcom   : ListenerCommand(パラメータ有のコマンド)
            '  llcomn  : ListenerCommand(パラメータ有のコマンド・CanExecute無)
            '  lprop   : 変更通知プロパティ(.NET4.5ではlpropn)
            '  
            ' を使用してください。
    
    
            Public Sub Initialize()
            End Sub
    
        End Class
    End Namespace
    

  •  続いて、View です。 Views フォルダ内に、「Livet WPF4 ウィンドウ」を選択して、WindowActionWindow1.xaml と言う名前で作成して、以下のように実装します。

  • <Window x:Class="Views.WindowActionWindow1"
            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:ei="http://schemas.microsoft.com/expression/2010/interactions"
            xmlns:l="http://schemas.livet-mvvm.net/2011/wpf"
            xmlns:v="clr-namespace:w10_Messengers.Views"
            xmlns:vm="clr-namespace:w10_Messengers.ViewModels"
            Title="WindowActionWindow1" Height="350" Width="525">
    
        <Window.DataContext>
            <vm:WindowActionWindow1ViewModel/>
        </Window.DataContext>
    
        <i:Interaction.Triggers>
    
            <!--WindowのContentRenderedイベントのタイミングでViewModelのInitializeメソッドが呼ばれます-->
            <i:EventTrigger EventName="ContentRendered">
                <l:LivetCallMethodAction MethodTarget="{Binding}" MethodName="Initialize"/>
            </i:EventTrigger>
    
            <!--Windowが閉じたタイミングでViewModelのDisposeメソッドが呼ばれます-->
            <i:EventTrigger EventName="Closed">
                <l:DataContextDisposeAction/>
            </i:EventTrigger>
            
            <!-- 画面を閉じる -->
            <l:InteractionMessageTrigger MessageKey="WindowCloseAction" Messenger="{Binding Messenger}">
                <l:WindowInteractionMessageAction />
            </l:InteractionMessageTrigger>
            
        </i:Interaction.Triggers>
    
        <StackPanel Margin="10">
    
            <!-- 
            ボタンのクリックイベント
            → ViewModel から「画面を閉じる」指示を発信 
            → View で受け取り実行 
            -->
            <Button Content="終了(ViewModel 発信)" Margin="10" Command="{Binding Click1Command}" />
    
            <!-- 
            Xaml 上で完結する版
            イベントトリガーが「クリック」イベントを検知
            →画面に関するアクションを実行(WindowInteractionMessageAction)
            →直接実行するメッセージ(DirectInteractionMessage)として、WindowActionMessage を指定(画面を閉じる)
            ※ここでの Message は、「命令とか命令を伝える」という意味合いで使っている
            -->
            <Button Content="終了(View 発信)1" Margin="10">
                <i:Interaction.Triggers>
                    <i:EventTrigger EventName="Click">
                        <l:WindowInteractionMessageAction>
                            <l:DirectInteractionMessage>
                                <l:WindowActionMessage Action="Close" />
                            </l:DirectInteractionMessage>
                        </l:WindowInteractionMessageAction>
                    </i:EventTrigger>
                </i:Interaction.Triggers>
            </Button>
    
            <!-- 
            上記と同じ。
            違いは、コールバックコマンドを設定して、画面上の処理が終わった後で、ViewModel 側の処理をさせている 
            -->
            <Button Content="終了(View 発信)2" Margin="10">
                <i:Interaction.Triggers>
                    <i:EventTrigger EventName="Click">
                        <l:WindowInteractionMessageAction>
                            <l:DirectInteractionMessage CallbackCommand="{Binding Click2Command}">
                                <l:WindowActionMessage Action="Close" />
                            </l:DirectInteractionMessage>
                        </l:WindowInteractionMessageAction>
                    </i:EventTrigger>
                </i:Interaction.Triggers>
            </Button>
    
        </StackPanel>
    
    </Window>
    

    スポンサーリンク


  •  1つ目のボタンの遷移順は以下の通りです。

  • イメージ
  •  ビューモデルの継承元である ViewModel クラスのメンバーに Messenger というプロパティがいます。 この子が View と ViewModel の間で仲介役を担当してくれるので対話が成立しています。

  •  ボタンをクリックすることで、コマンドが実行されます。コマンド内で、Messenger に View へ指示依頼を出します。 指示は「「WindowCloseAction」キーを設定しているトリガーに対して、「画面を閉じる」アクションをしてほしい」です。

  •  これにより、View 側にあるトリガーが、ウィンドウに関するアクション(具体的には、ViewModel から渡された「画面を閉じる」)を実行します。 画面が閉じられてプログラムが終了しますが、一応メソッドの最後までは待ってくれます。

  •  2つ目のボタンの遷移順は以下の通りです。

  • イメージ
  •  やっていることは、1つ目のボタンと同じ事ですが、ViewModel と対話しないので、Messenger は出てきません。 インタラクショントリガーであるイベントトリガーが、クリックイベントを検知して、対応するアクション(画面を閉じる)を実行します。

  •  3つ目のボタンの遷移順は以下の通りです。

  • イメージ
  •  3つ目のボタンは、2つ目のボタンと同じですが、「画面を閉じる」アクションを実行した後、コールバックコマンドを実行します。 1つ目のボタンの時と同じで、画面が閉じられてプログラムが終了しますが、一応メソッドの最後までは待ってくれます。

  •  これを体験してみて、従来のやり方はもっと楽だったのに面倒くさくなったな~、と思われたかもしれませんね。

  • Public Sub Test()
        Me.Close()
    End Sub
    

  •  コードビハインドのやり方があるように、MVVM のやり方があります。 特に画面に関わる処理をしたい時に、Me.Xxx 経由で何かをしたくなる考え方はいったん置いておいて、 MVVM の考え方である「責務の分離」に従ったやり方はどうやるのかなと、考える癖をつけた方がいいかもしれません。