Galileo Figaro

常に初陣!

Chapter 1: MVVM

力試し

簡単なプログラムを作成します。 まずは自力でチャレンジしてみましょう。

次のようなクラスがあるとします。

internal class User
{
    public string Name { get; } = "Freddie";
    public int Age { get; } = 42;
}

このクラスのインスタンスを一つ作り、起動すると画面に名前と年齢を表示する、次のようなプログラムを作成してください。

MVVM の概要

MVVM はソフトウェアアーキテクチャの一つで、ソフトを Model、View、ViewModel の 3 要素で表すものです。 Microsoft による MVVM の説明では、利点がいくつか挙げられていますが、簡単に言うと次のようなことを言っているのだと思います。

  • UI とビジネスロジック疎結合になるので、 お互いに、一方の変更がもう一方に影響しにくい。
  • 一般的に UI が絡んでくると単体テストがやりにくいが、 View を分離できるため ViewModel と Model の単体テストがやりやすい。
  • デザイナーは View の開発、プログラマは ViewModel と Model の 開発をすることができ、分業しやすい。 (ただ、私は xaml を書けるデザイナーに出会ったことはないです。)

ソフトを Model、View、ViewModel の 3 つに別れるということですが、 それぞれの役割は大まかに次のような役割だと思います。

  • View

    • UI の見た目担当
    • コントロールの配置やサイズや色を決定する
  • ViewModel

    • Model が持っている値を、View に伝える
    • View で入力された値を、Model に伝える
    • ユーザーが入力中の、まだ確定していない値の保持
    • ユーザーが入力した値の、ビジネスロジックが絡まない検証
    • Model の状態の変化を検知し、View に通知する
  • Model

こう書くと ViewModel のやることが多いように見えますが、 実際には Model の分量が多くなると思います。

解答例

冒頭の力試しの解答例を示します。 ぜひ、自分の考えたコードと比較してください。

https://github.com/Geroshabu/WpfBootCamp/tree/main/Chapter1/Code/Sample

以降、要点を説明していきます。

ビューモデルの作成

今回の例では、User クラスがモデルです。 このモデルが持つ名前と年齢の情報を、ビューに表示するために、 ビューモデルが橋渡し役になっています。

ビューモデルの例を下記に示します。 ビューモデルの名前は、「ビューの名前+ViewModel」という名前にすることが多いです。

internal class MainWindowViewModel
{
    private readonly User model = new();

    public string Name
    {
        get
        {
            return model.Name;
        }
    }

    // ...(略)...
}

ご覧の通り、ビューモデルは User インスタンスを持っています。 これがモデルです。 モデルが持つ値を、プロパティ Name を通じて、ビューに返しています。

ビューとビューモデルの紐づけ

ビューモデルを作っただけでは、画面には表示されません。 ビューとビューモデルを対応付けることが必要です。 まずは、ビューが持つ DataContext というプロパティに、 ビューモデルを設定する必要があります。 DataContext へのビューモデルの セットにはいくつか方法がありますが、 今回は、MainWindow のコードビハインド (.caml.csファイル) で、 自身の DataContext プロパティにビューモデルを設定してみます。

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();

        DataContext = new MainWindowViewModel();
    }
}

さらに .xaml ファイルでは、ビューモデルの Name プロパティを、 Label.Content プロパティにバインディング (紐づけ) しています。 これで、ビューモデルから返された値を、ラベルに表示することができます。

<Label Content="{Binding Name}"/>

.xaml ファイルにこのように書くと、 実行時に Name というプロパティがあるかを探し、 見つかれば Name プロパティの値を、Label.Content の値とします。 どこから探し出すのかというと、DataContext プロパティに設定した ビューモデルから探し出します。 ですので、DataContext プロパティに何も設定していないと、 紐づけがうまくいきません。