導入
**MVVM(Model-View-ViewModel)**は、WPFの標準的な設計パターンです。「見た目(View)」と「画面のロジック・状態(ViewModel)」と「業務データ(Model)」を分離し、テスト可能で保守しやすいアプリにします。第11章のSOLID・第13章のパターンがここで統合されます。
図解
flowchart LR
V["View(XAML)<br/>見た目だけ<br/>コードビハインドは最小限"]
VM["ViewModel<br/>画面の状態+操作<br/>(INotifyPropertyChanged / ICommand)"]
M["Model<br/>業務データ・ルール<br/>(第22章のドメイン)"]
V <-->|バインディング| VM
VM --> M
style VM fill:#fff3e0
役割分担(漏れなく)
- View(
.xaml):見た目だけを担当。{Binding}でViewModelのプロパティ・コマンドに結びつく。コードビハインド(.xaml.cs)は極力空にする - ViewModel:画面の状態(表示する値)と操作(コマンド)を持つ。
INotifyPropertyChanged(データバインディングのレッスン)で変更通知、ICommand(イベントとコマンドのレッスン)で操作を公開。Viewを一切参照しない(=ViewModel単体でテストできる) - Model:業務データとルール(第22章のエンティティ・値オブジェクト)。UIを知らない
- DataContext:ViewにどのViewModelを結びつけるかの設定。これでバインディングが機能する
XAMLサンプル(読み物)
<!-- View: DataContextにViewModelを設定し、バインドするだけ -->
<Window ...>
<Window.DataContext>
<local:CounterViewModel />
</Window.DataContext>
<StackPanel Margin="20">
<TextBlock Text="{Binding Count}" FontSize="24" />
<Button Content="+1" Command="{Binding IncrementCommand}" />
</StackPanel>
</Window>
// ViewModel: 状態(Count)と操作(IncrementCommand)を持つ。Viewを知らない
public class CounterViewModel : INotifyPropertyChanged
{
private int _count;
public int Count
{
get => _count;
set { _count = value; PropertyChanged?.Invoke(this, new(nameof(Count))); }
}
public ICommand IncrementCommand { get; }
public CounterViewModel()
{
IncrementCommand = new RelayCommand(() => Count++);
}
public event PropertyChangedEventHandler? PropertyChanged;
}
詳細手順
- スターター
141-wpf-mvvm/starter.zipを開く(View / ViewModel / Model がフォルダで分離済み)
- F5で実行し、ボタンでカウントが増える(View⇄ViewModelがバインディングで連動)ことを確認する
CounterViewModelにはViewへの参照が一切ないこと=単体テスト可能であることを確認する(第14章のTDDと接続)
まとめ
- MVVMはView(見た目)・ViewModel(状態と操作)・Model(業務データ)を分離する
- ViewModelはViewを参照しない=単体テストできる
- バインディング+INotifyPropertyChanged+ICommandがMVVMの三種の神器
次回: 複数画面を切り替える「画面遷移」です。
テンプレート構成(教材制作用メモ)
141-wpf-mvvm/
└── starter.zip # Views/ViewModels/Models分離 / RelayCommand / README