本文へスキップ
ysk-hack-log
戻る

WinUIでMVVMをはじめる|CommunityToolkit.Mvvmで「画面とロジックの分離」を最小コードで

「WinUIでMVVMをはじめる|CommunityToolkit.Mvvmで「画面とロジックの分離」を最小コードで」の記事アイキャッチ

WinUIで少し大きめのアプリを作り始めると、必ず「画面のコードがどんどん膨らんで、収拾がつかない」壁にぶつかります。私も最初はボタンのクリックイベントに処理を直書きし続け、あとで手に負えなくなりました。その処方箋が MVVM です。

この記事では、開発ロードマップのステップ4にあたる MVVMの考え方と、最小コードで始める型を、初心者向けに解説します。難しい理屈より「なぜ必要で、どう書くか」に絞って進めます。

Table of contents

Open Table of contents

そもそもMVVMとは

MVVMは、アプリを3つの役割に分ける設計パターンです。

ポイントはただ一つ、「画面(View)には見た目だけを置き、処理はViewModelに寄せる」こと。まずはこの一点だけ押さえれば十分です。

なぜMVVMが必要か(Before / After)

MVVMを使わないと、処理が画面のコード(コードビハインド)に溜まっていきます。たとえばカウンターを作るとき、こう書きがちです。

// Before:画面のコードに直書き
private int _count;
private void Button_Click(object sender, RoutedEventArgs e)
{
    _count++;
    CountText.Text = _count.ToString(); // UI部品を直接いじる
}

一見動きますが、①ロジックがUIと密結合してテストできない ②再利用できない ③画面が育つほどコードが肥大化するという問題を抱えます。

MVVMにすると、処理はViewModelへ移り、画面はViewModelを見るだけになります。

// After:処理はViewModelへ。画面はデータを表示するだけ
public partial class CounterViewModel : ObservableObject
{
    [ObservableProperty]
    private int count;

    [RelayCommand]
    private void Increment() => Count++;
}

UI部品を直接触るコードが消え、ロジックが独立しました。これがMVVMの効果です。

手書きは大変:だからCommunityToolkit.Mvvm

MVVMの肝は、「ViewModelの値が変わったら画面に自動で反映される」仕組み(INotifyPropertyChanged)です。ところが、これを手書きすると定型コードが大量になります。プロパティ1つごとに変更通知を書くのは、正直かなり面倒です。

そこで使うのが CommunityToolkit.Mvvm。この定型コードを属性を付けるだけで自動生成してくれる、MVVMの必須級ライブラリです。NuGetで追加できます(入門記事のNuGet紹介も参照)。

3つの主役だけ覚える

CommunityToolkit.Mvvmは機能豊富ですが、最初は次の3つだけで十分です。先ほどのCounterViewModelが、この3つでできています。

クラスに partial を付けるのを忘れないでください(自動生成コードと合体させるため)。たったこれだけで、変更通知もコマンドも書かずに済みます。

ViewとViewModelのつなぎ方(x:Bind)

あとは画面(XAML)から、ViewModelの値とコマンドをバインドでつなぐだけです。WinUIでは高速な x:Bind が推奨です。

<TextBlock Text="{x:Bind ViewModel.Count, Mode=OneWay}" />
<Button Content="+1" Command="{x:Bind ViewModel.IncrementCommand}" />

これで「ボタンを押す→ViewModelのIncrementが走る→Countが変わる→画面が自動更新」という流れが、画面側にロジックを一切書かずに実現します。

WPFのMVVMとの違い

WPF経験者がWinUIでMVVMを書くと、いくつか戸惑う差分があります。ここは実際につまずいたポイントです。

一番の違いは「バインドが値をどこから探すか」です。

WPFの {Binding} は、実行時に DataContext を起点に値を探します。だからMVVMでは、画面にViewModelをDataContextとして流し込むのが定石でした。

// WPF:DataContext に ViewModel をセット
DataContext = new CounterViewModel();
<TextBlock Text="{Binding Count}" />

一方WinUIの {x:Bind} は DataContext を見ず、**そのページの分離コード(this)**を起点にします。{x:Bind Count} は「ページクラスに Count があるか」を探すのです。そのためViewModelにつなぐには、ViewModelをページの公開プロパティとして出すのが基本形になります。

// WinUI:ページの公開プロパティとして ViewModel を出す
public sealed partial class MainPage : Page
{
    public CounterViewModel ViewModel { get; } = new();
    public MainPage() => InitializeComponent();
}
<TextBlock Text="{x:Bind ViewModel.Count, Mode=OneWay}" />

要点を対比するとこうなります。

WPF {Binding}WinUI {x:Bind}
値の探し先DataContextページの分離コード(this)
ViewModelの渡し方DataContext = vmpublic ViewModel { get; } を公開
解決タイミング実行時(リフレクション)コンパイル時(型安全・高速)
既定モードOneWayOneTime

(WinUIでも従来型の {Binding}+DataContext は使えますが、推奨は型安全な x:Bind です。)考え方(ViewとModelの分離)は同じでも、つなぎ方の道具がモダンになっている、と捉えると移行がスムーズです。

まとめ

MVVMは難しく見えて、要点は「画面には見た目、処理はViewModel」の一言に尽きます。

  1. なぜやるか … UIとロジックを分離し、肥大化とテスト不能を防ぐ
  2. 何でやるか … CommunityToolkit.Mvvm で定型コードを自動生成
  3. どう書くか … ObservableObject + [ObservableProperty] + [RelayCommand] の3点セット
  4. どうつなぐか … 画面から x:Bind でバインド

まずは今回のカウンターのような小さな例で、この型を一度自分の手で書いてみてください。動かせれば、あとはプロパティやコマンドを増やしていくだけです。次回は、WPFからWinUIへ移行する際の具体的な勘所を整理します。


この記事をシェア:

前の記事
WinUI 3開発の始め方ロードマップ|初心者が挫折せず動くアプリに辿り着く学習手順
次の記事
WPFからWinUI 3への移行の勘所|つまずく差分を一枚のマップで整理