[VB.NET] MVVMパターンもどき(2) ~ViewModelからViewを操作する

2013年8月13日

 MVVM モデルで ViewModel から View を操作する場合は Messenger という仕組みを使う…らしいのですが、理解が難しかったので、自分なりにまとめなおしてみます。
 結論的には、以下の 2 の内容か、頑張っても 5 の内容で勝負であって、Messenger の実装はやりすぎという感じがします。まあ私に取り回す実力がないだけかもしれませんが(汗

  1. まず「ViewModel から View を操作したいケース」とは何か

     たとえば、WinForm とかで以下のようなコードを書きたいケースです。

    Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
        MsgBox("Hello")
    End Sub
    

     簡単ですね。

  2. 上記を ViewModel から実行するとどうなるか

     なにも考えなければ、以下で OK のはずです。

    Class MainWindow 
        Private _viewModel As ViewModel = New ViewModel
    
        Private Sub Button_Click(sender As Object, e As RoutedEventArgs)
            ' 操作類は、viewModel 側のメソッドで実行させたいので、転送する
            _viewModel.ShowMsgBox()
        End Sub
    End Class
    
    
    Class ViewModel
        Public Sub ShowMsgBox()
            MsgBox("Hello")
        End Sub
    End Class
    
  3. 疑問1:ViewModel から 直接 MsgBox を実行していいのか?

     MVVM モデルではこれを嫌っているようです。予想される問題は以下の2つ。

    • ViewModel に UI(入力操作) が含まれていることで、単体テストが難しくなること。
    • MsgBox という命令自体が View そのものであること。

     その他には「デザイナーとプログラマーの分業が難しい」とかありそうですが、とりあえず無視とします。

  4. 疑問2:そもそも View(コードビハインド) に直接 MsgBox を書いてはだめなのか?

     嫌われる理由としては、View にビジネスロジック(MVVM でいう ViewModel 部分 / MVC でいう Controller 部分) が混じってしまう、という点です。
     # MsgBox 自体は View ですが「MsgBox を表示する」はビジネスロジックです。

  5. ではどうするか?

     たとえば、上記2.で書いたコードを以下のように書き換えます。

    Class MainWindow 
        Private WithEvents _viewModel As ViewModel = New ViewModel
    
        Private Sub Button_Click(sender As Object, e As RoutedEventArgs)
            ' 操作類は、viewModel 側のメソッドで実行させたいので、転送する
            _viewModel.ShowMsgBox()
        End Sub
    
        Private Sub _viewModel_RequestShowMsgBox() Handles _viewModel.RequestShowMsgBox
            MsgBox("Hello")
        End Sub
    End Class
    
    
    Class ViewModel
        Public Event RequestShowMsgBox()
    
        Public Sub ShowMsgBox()
            ' MsgBoxの表示をお願いする。
            RaiseEvent RequestShowMsgBox()
        End Sub
    End Class
    

     こうすることで、ViewModel から View のコードは排除することができます。
     これで View のことは View側 (コードビハインド側) に集約できます。つまり、MsgBox を デザインがリッチなダイアログに変更したいといった場合も ViewModel は変更せず、View 側の変更だけで対応可能です。

     また、ViewModel はユーザー入力から切り離されているので、直接単体テストすることが可能です。
     今回の例では実装されていませんが、ユーザー入力の入力内容を ViewModel に戻しているケースも、ViewModel からのイベントを受けるテストスタブを作ることで、単体テストが可能です。

  6. MVVM モデルにおける Messenger クラスとは
     要するに、上記のような仕組みを用意するための、一連のクラス群です。
     上記 5 では、イベントをコードビハインドに通知していますが、本家(?)のメッセンジャーはもうちょっと複雑です。
     View 部分の処理 (今回のでは MsgBox 相当) をコマンドオブジェクトとしてラップし、それをコードビハインドではなく、直接 XAML に記載する手法のようです。

  7. Messenger クラスを実装した場合はどうなるか

     こんな感じ。
     しかし、共通部品として隠蔽/再利用できるものがあるとしても、これだけのコードを書く必要があるというのはちょっと…(汗

     コード部分

    Class MainWindow 
        Private _viewModel As ViewModel = New ViewModel
    
        Private Sub MainWindow_Loaded(sender As Object, e As RoutedEventArgs) Handles Me.Loaded
            Me.DataContext = _viewModel
        End Sub
    
        Private Sub Button_Click(sender As Object, e As RoutedEventArgs)
            _viewModel.ShowMsgBox()
        End Sub
    End Class
    
    Class ViewModel
        Public Property RequestShowMsgBox As Messenger = New Messenger
    
        Public Sub ShowMsgBox()
            ' MsgBoxの表示をお願いする。
    
            Me.RequestShowMsgBox.Raise(
                New Message("Hello"),
                Sub(args)
                    MsgBox(args.Response, , "view操作後の処理")
                End Sub
            )
        End Sub
    End Class
    
    Class ConfirmAction
        ' 参照設定: C:Program Files (x86)Microsoft SDKsExpressionBlend.NETFrameworkv4.5LibrariesSystem.Windows.Interactivity.dll
        Inherits System.Windows.Interactivity.TriggerAction(Of DependencyObject)
    
        Protected Overrides Sub Invoke(parameter As Object)
            Dim args = CType(parameter, MessageEventArgs)
    
            MsgBox(args.Message.Body)
    
            args.Message.Response = "View操作後のReturn"
            args.Callback()(args.Message)
        End Sub
    End Class
    
    
    
    ' 
    ' 
    ' これより以下は、たぶん使いまわし(隠蔽)可能
    Class Message
        Public Property Body As Object
        Public Property Response As Object
    
        Public Sub New(body As Object)
            Me.Body = body
        End Sub
    End Class
    
    Class MessageEventArgs
        Inherits EventArgs
    
        Public Property Message As Message
        Public Property Callback As Action(Of Message)
    
        Public Sub New(message As Message, callback As Action(Of Message))
            Me.Message = message
            Me.Callback = callback
        End Sub
    End Class
    
    Class Messenger
        Public Event Raised As EventHandler(Of MessageEventArgs)
    
        Public Sub Raise(message As Message, callback As Action(Of Message))
            RaiseEvent Raised(Me, New MessageEventArgs(message, callback))
        End Sub
    End Class
    
    Class MessageTrigger
        ' 参照設定: C:Program Files (x86)Microsoft SDKsExpressionBlend.NETFrameworkv4.5LibrariesSystem.Windows.Interactivity.dll
        Inherits System.Windows.Interactivity.EventTrigger
    
        Protected Overrides Function GetEventName() As String
            Return "Raised"
        End Function
    End Class
    

     XAML部分

    <Window x:Class="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:local="clr-namespace:WpfApplication1"
        Title="MainWindow" Height="100" Width="300">
        <i:Interaction.Triggers>
            <local:MessageTrigger SourceObject="{Binding Path=RequestShowMsgBox}">
                <local:ConfirmAction />
            </local:MessageTrigger>
        </i:Interaction.Triggers>
    
        <Grid>
            <Button Content="Button" Click="Button_Click"/>
        </Grid>
    </Window>
    






タグ:
カテゴリー: Program, VB.NET, WPF

Follow comments via the RSS Feed | Leave a comment | Trackback URL

1 Comment to "[VB.NET] MVVMパターンもどき(2) ~ViewModelからViewを操作する"

  1. nx802

    VB入門者です。
    MVVMの意味が理解できず調べていてたどり着きました。
    この記事のお陰でやっと理解できました。
    といっても理解できたのは5.までで,それ以上はちょっと難しかったです。XAMLを勉強したらまた出直してきます

コメントを投稿する

日本語が含まれない投稿は無視されますのでご注意ください。(スパム対策)


«   »
 
Powered by Wordpress and MySQL. Theme by Shlomi Noach, openark.org