【WPF(XAML)C#1】MVVMパターンの基礎とGridの使い方

今回はWordPress関連以来の技術アウトプット記事になります。私が今携わっているのはWPF(XAML)+C#なので今はWPF関連について情報を共有できればと思っております。

とはいえ、MVVMパターンって何?みたいな概念的な説明は他サイトに結構文献がありますのでそちらを参照してもらうとして、イナコーシステムとしてはすぐに現場で使えそうな実践的知識をメインにお送りしたいと思います。

私が去年初めてWPFに触れた時はまだ技術情報はそんなに多くなかったのですが、ここ最近は参考になるサイトも多くなってきた気がします。当サイトも基本的な事はほどほどにして、実際業務で活用した技術を今後ご紹介していければと思います。

Grid

一番最初にWPFのファイルを新規作成すると<Grid></Grid>だけ入ったXAMLファイルが生成されていると思います。この<Grid>は結構重要で主に何のために使うかと言うと画面の区画を分けるために使われます。以下の画像を見てください。

長さを直接指定した場合

上の画像のように定義することで明確に区画毎に役割を区切ることができます。画像の通り説明してしまってますが、長さ指定も可能です。全体の長さが900pxと定義してますがGridの指定はトータルで600pxなので残るウィンドウの300pxは描画されてない状態になっています。

長さを直接指定した時に気をつける事は解像度です。ディスプレイは解像度以外にサイズ(100%~150%)も変えられますのでこのサイズにより、大きくも小さくも見えてしまいますので業務でアプリを作成する時は留意すべき点です。

割合指定した場合

上記の注意事項のように直接長さを指定すると全体の描画が足りなかったり、解像度に左右されてしまいますのでそれを防ぐために割合で指定する事も多いです。以下の画像をご覧ください。

長さの割合の意味は右のウィンドウの解説通りなので参考にどうぞ!この指定であれば解像度を変えても対応可能なので活用できる技術です。

Binding

一枚目の画像に記載があった「Binding testNo」と書かれたエリアに1111が表示されています。これが表題にあったMVVMの基礎でViewModelを変更した値がView(XAMLファイルの事)の指定した部分に伝達されて画面上に表示されています。XAML+C#でプロダクトを開発する際はこのBinding+ViewModelからの変更通知を使いこなすことが第一関門なのでよく覚えておいてください。

もちろんこのBindingしたTextBoxのパラメータをView側で変更したものを逆にViewModelに反映させる事もできます、こちらは後のセクションで解説します。

C#(Model/ViewModel)側

先ほどの説明ではXAMLはMVVMの中のV=Viewに相当するものという説明をしました。ではM(Model)とVM(ViewModel)はどこで定義するのでしょうか? こちらはC#側で二つとも定義します。以下のコードをご覧ください。

namespace TestApp
{
    /// <summary>
    /// MainWindow.xaml の相互作用ロジック
    /// </summary>
    public partial class MainWindow : Window
    {
        MainWindowViewModel MyVM = new MainWindowViewModel();

       // Model
       public MainWindow()
        {
            InitializeComponent();

            this.DataContext = MyVM;

            MyVM.testNo = 1111;
        }
    }

    /// <summary>
    /// MainWindowのViewModel
    /// </summary>
    public class MainWindowViewModel : INotifyPropertyChanged
    {
        private int? _testNo;
        public int? testNo
        {
            get { return _testNo; }
            set
            {
                if (value != _testNo)
                {
                    _testNo = value;
                    NotifyPropertyChanged();
                }
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;
        private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

ソースコードの通りですが、メインメソッド部分(public partial class MainWindow : Window)と定義部分public class MainWindowViewModel : INotifyPropertyChangedは分けて定義します。

メインメソッドがModel。後半のClass定義がViewModelと考えて頂いて問題ありません。

Model側

こちらで行っていることは初期化したViewModelをDataContextに入れてViewModelのプロパティに値を設定してViewに反映されるといった事をしています。DataContextにViewModelを入れないとView側はViewModelが存在している事を認識できないので必ず設定してください。

ViewModel側

privateとpublicの使用はJavaBeansのようなアクセッサーだと考えてもらえばOKです。(C#はアクセッサーの記述が簡単なので便利ですね☆)

ではINotifyPropertyChangedは何でしょうか?こちらはプロパティがModelによって変更された時にView側に変更された事を通知する構文です。これによって代入された値はその都度Viewに通知されるのでView側の値も変わります。これは特にViewからも値を変更できるようにする時に大事になります。とはいえ、View側にBindingさせたいプロパティに関してはおまじないようにいつも使っていただければまず間違いありません。

        public event PropertyChangedEventHandler PropertyChanged;
        private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }

も一緒につけてようやくINotifyPropertyChangedを使用できると考えてください。

INotifyPropertyChangedは記述が長いので継承したものを作成したBindableBaseを実装するともっとViewModelの記述を簡略化できます。これは解説サイトがあるので参考にしてみてください。

注意事項

というようにXAML+C#で運用する場合、ViewModelの定義は必須なのですが新規作成した時はこれしかありません。

namespace TestApp.main
{
    /// <summary>
    /// Page1.xaml の相互作用ロジック
    /// </summary>
    public partial class Page1 : Page
    {
        public Page1()
        {
            InitializeComponent();
        }
    }
}
自分

あれ?ViewModelはどこ?

ってなってしまいますよね。ちょっと不親切です。実はそうならないようにPrismというMVVM用のフレームワークがあるらしく、使うと自動でMVVMパターンを作成してくれるようですよ。

ちなみにうちの業務では未使用なのでこれ以上の解説はしないですが便利そうなのは間違いないので初めてWPFでプロダクトを作る方は導入の一考はあると思います。

以上でセクション1の解説を終わります。セクション2はView上から値を変更した時にViewModelに通知する方法等を解説しますね~。