MVVMについて
MVVMは、ソフトウェアの構造を階層的に整理したデザインパターンの名称です。
中でも、主にクライアントサイドのGUI(グラフィカルユーザーインターフェース)を持つアプリケーション開発で使用されることが多いように思います。
MVVMにおける階層は『Model』、『View』、『ViewModel』の3つに分けられ、ユーザーがアプリケーションに対して行った操作はViewで受けることになるのですが、この操作はCommandとして実行され、イベントとしてModelまで伝えられます。
そして、Modelで処理が完了すると、処理結果がViewまで伝えられ画面上に表示されます。
例えば、電卓アプリであれば「計算結果表示」というCommandが実行されたときに、Viewからイベントが伝わっていってModelのCalc()メソッドが呼び出され、計算が完了すると結果をViewまで伝えて表示するイメージです。

MVVMで開発するメリット
MVVMパターンでのアプリケーション開発には、以下のようなメリットがあります。
- ビューとビジネスロジックを明確に分けることができる
- 階層毎に責務が明確に分かれているため、画面の仕様変更はビューだけで完結できる(ビジネスロジックは変更不要)
- UI設計とビジネスロジック設計のそれぞれを並列に開発することができる
階層ごとの役割
ビューに関するコードを配置する階層です。
ここに書くコードはGUIのレイアウトに関するものだけです。
つまり、『ユーザーにどう見せるか』に関するコードだけを書くことになります。
ViewとModelの間に位置し、両者のデータおよびイベントの中継役となる階層です。
View→Modelに対しては、操作に応じてModelのデータを更新したり、メソッドを呼び出します。
一方、Model→Viewに対しては、Modelでの演算結果やデータをViewで表示できる形式に加工します。
データベースへのアクセスや、様々な演算処理を行うビジネスロジック階層です。
『〇〇されたら××する』という演算機能は、この階層に集約します。
階層間のやりとり
階層を分けたからといってすべてがうまくいくわけではありません。
階層間の繋がりが“密”(密結合)になっていては、階層ごとに分けた意味が半減してしまいますので、なるべく階層間の結合は疎結合にする必要あります。
MVVMでは階層間のやりとりを以下のように行うことを心がけます。
- ViewとViewModel間のデータの伝達はDataBindingを用いて行う
- ViewからViewModelへの操作の伝達はCommandsで行う
- ViewModelからModelへのイベント通知は引数なしのメソッド呼び出しのみ行う
- ModelからViewModelへのデータの変更通知はPropertyChanged イベントで行う

ViewModel~Model間の接続の仕方について解説しているサイトは少なく感じます。
もう少し読み進めて是非自分のものにしてください。
MVVMでの実装例
実装例を以下に示します。
なお、この実装例は「prism」というMVVMフレームワークの利用を前提にしていますので、prismについて詳しく知りたい方は以下記事をご参照ください。
ユーザが操作を開始してから表示が変化するまでの処理の流れを以下で解説します。

ユーザーがボタンを押す。
ViewModelのXXX_Clickedコマンドが発行されてXXX_Clicked()に処理が移る。
ViewModelからModelのMethod()が呼び出される。

このとき、ViewModelからは引数なしのメソッドだけを呼ぶようにModel側を設計します。
ModelのMethod()内の処理を実行し、プロパティ値を更新する。
Modelのプロパティ値の変更通知(PropertyChanged)イベントを発行する。
ModelのPropertyChangedイベントを受けて、ViewModelのPropertyChangedイベントを発行する。
PropertyChangedイベントによって、ViewModelのプロパティ値がViewのTextBoxに反映される。
階層間での処理の伝播がどう行われるのかが理解できたかと思います。
その他にもDIコンテナを利用して、より疎結合な構成になるような工夫も可能です。
アプリを作ってみる
実際にMVVMで、テキストボックスと「=」のボタンだけの単純な『足し算アプリ』を作ってみました。
できあがりはこんな感じです。

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
<Window x:Class="MVVM_Sample.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:local="clr-namespace:MVVM_Sample" mc:Ignorable="d" Title="MainWindow" Height="150" Width="350"> <Window.DataContext> <local:MainWindowViewModel /> </Window.DataContext> <Grid> <!--行の定義--> <Grid.RowDefinitions> <RowDefinition Height="10*" /> <RowDefinition Height="10*" /> <RowDefinition Height="10*" /> </Grid.RowDefinitions> <StackPanel Grid.Row="1" HorizontalAlignment="Center" Orientation="Horizontal"> <TextBox Text="{ Binding LeftParam }" Width="30" VerticalContentAlignment="Center" HorizontalContentAlignment="Center" /> <Label Content="+" FontSize="24" VerticalContentAlignment="Center" /> <TextBox Text="{ Binding RightParam }" Width="30" VerticalContentAlignment="Center" HorizontalContentAlignment="Center" /> <Label Width="10" /> <Button Content="=" Command="{ Binding EqualCommand }" FontSize="24" Width="40" /> <Label Width="10" /> <TextBox Text="{ Binding Result, UpdateSourceTrigger=PropertyChanged}" Width="80" IsEnabled="False" VerticalContentAlignment="Center" HorizontalContentAlignment="Center" /> </StackPanel> </Grid> </Window> |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
#nullable enable namespace MVVM_Sample { public class MainWindowViewModel : BindableBase { private Calculator m_CalcModel; public DelegateCommand EqualCommand { get; } public uint LeftParam { get { return m_CalcModel.LeftParam; } set { m_CalcModel.LeftParam = value; } } public uint RightParam { get { return m_CalcModel.RightParam; } set { m_CalcModel.RightParam = value; } } public uint Result { get { return m_CalcModel.Result; } set { m_CalcModel.Result = value; } } public MainWindowViewModel( ) { m_CalcModel = new Calculator( ); m_CalcModel.PropertyChanged += ( sender, e ) => RaisePropertyChanged( e.PropertyName ); EqualCommand = new DelegateCommand( Equal_Click ); } private void Equal_Click( ) { m_CalcModel.Sum( ); } } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
namespace MVVM_Sample { public class Calculator : BindableBase { public uint LeftParam { get; set; } public uint RightParam { get; set; } private uint m_Result; public uint Result { get { return m_Result; } set { SetProperty( ref m_Result, value ); } } public void Sum( ) { Result = LeftParam + RightParam; } } } |
参考までに、足し算アプリのソースコードは以下のGithubに置いています。