shuhelohelo’s blog

Xamarin.Forms多めです.

Xamarin.FormsでMvvmHelpersを使ってみる

channel9.msdn.com

montemagno.com

github.com

MVVMフレームワーク色々

MVVMパターンでのアプリケーション開発をサポートするライブラリやフレームワークは色々あります.

  • Prism
  • ReactiveProperty
  • MvvmCross
  • ReactiveUI
  • MVVMLite
  • MVVMHelpers

他にもあると思います.

それぞれ特徴があるので,合うものを探す必要があります.

今回はMvvmHelpersについて.Xamarin.Formsについてです.

MVVMフレームワークを使う理由

これらのフレームワークを使わなくてもMVVMパターンでアプリケーションを作成することはできます.

しかし,MVVMフレームワークを使うと,実装の手間を大きく減らすことができます.

難しいことはないのですが,各ViewModelで,その中の各プロパティで,こまごまとデータバインディングのための記述をする必要があるため,手間がかかります.

そういった手間をフレームワーク側に任せて,作りたいものに集中できるようにすることが,フレームワーク導入のメリットかと思います.

MvvmHelpersをインストールする

NugetパッケージマネージャからRefractored.MvvmHelpersをインストールする.

BaseViewModelクラスを作成する

ViewModelは共通するプロパティやメソッドを実装することが多々あるので,そのようなプロパティ,メソッドをBaseViewModelとして定義しておき,各ViewModelクラスはそれを継承するようにすると,実装の手間を省くことができる.

通化できるプロパティには以下のようなものがある.

  • Title
  • IsBusy
  • IsNotBusy
  • Iconのパス

BaseViewModelは例えば以下のようになる.

    public class BaseViewModel : ObservableObject
    {
        private string _title;
        public string Title
        {
            get => _title;
            set => SetProperty(ref _title, value);
        }

        private string _subTitle;
        public string SubTitle
        {
            get => _subTitle;
            set => SetProperty(ref _subTitle, value);
        }

        private string _icon;
        public string Icon
        {
            get => _icon;
            set => SetProperty(ref _icon, value);
        }

        private bool _isBusy;
        public bool IsBusy
        {
            get => _isBusy;
            set
            {
                if (SetProperty(ref _isBusy, value))
                    IsNotBusy = !_isBusy;
            }
        }

        private bool _isNotBusy;
        public bool IsNotBusy
        {
            get => _isNotBusy;
            set => SetProperty(ref _isNotBusy, value);
        }

        private bool _canLoadMore;
        public bool CanLoadMore
        {
            get => _canLoadMore;
            set => SetProperty(ref _canLoadMore, value);
        }
    }

この内容であれば,MvvmHelpers.BaseViewModelが用意されていて,それを継承するだけで良かったりする.

MvvmHelpersの機能を利用するためにはObservableObjectを継承すること.

ViewModelを作成する

このBaseViewModelを継承したクラスを作成します.

BaseViewModelに定義したいくつかのプロパティを使うだけのシンプルなViewModelとしてMainPageViewModelを作成しました.

using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using System.Text;

namespace XFMvvmHelperPractice.ViewModels
{
    public class MainPageViewModel : BaseViewModel
    {
        public MainPageViewModel()
        {
            //テスト
            this.Title = "Hello MvvmHelper";
            this.SubTitle = "How to use MvvmHelper";
            this.IsBusy = true;
        }
    }
}

このMainPageViewModelを使うViewは以下のようにしました.

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage
    x:Class="XFMvvmHelperPractice.Views.MainPage"
    xmlns="http://xamarin.com/schemas/2014/forms"
    xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
    xmlns:d="http://xamarin.com/schemas/2014/forms/design"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:viewmodels="clr-namespace:XFMvvmHelperPractice.ViewModels"
    mc:Ignorable="d">

    <ContentPage.BindingContext>
        <viewmodels:MainPageViewModel />
    </ContentPage.BindingContext>

    <StackLayout VerticalOptions="Center">
        <Label
            FontSize="Large"
            HorizontalOptions="Center"
            Text="{Binding Title}" />
        <Label
            FontSize="Medium"
            HorizontalOptions="Center"
            Text="{Binding SubTitle}" />
        <ActivityIndicator
            IsRunning="{Binding IsBusy}"
            IsVisible="{Binding IsBusy}"
            Color="Orange" />
    </StackLayout>

</ContentPage>

タイトル, サブタイトル, クルクル回るやつの3つのコントロールを持ち,それぞれがViewModelのTitle, SubTitle, IsBusyプロパティにバインディングされています.

このMainPageのBinding先のViewModelとしてMainPageViewModelを指定しています.

    <ContentPage.BindingContext>
        <viewmodels:MainPageViewModel />
    </ContentPage.BindingContext>

これを実行した結果がこちらです.

f:id:shuhelohelo:20200604171603p:plain

ObservableCollectionをもっと使いやすく

アプリケーションの中でリストを表示させることがよくあります.

例えば,ToDoリストだったり,連絡先一覧だったりです.

このときリストに表示させるデータの型として使われるのがObservableCollection<T>というクラスです.

Listではなくこれが使われる理由としては,コレクションに対するアイテムの追加や削除を通知する機能を持っているからです.

表示するだけであればListでも良いと思います.

このObservableCollectionですが,アイテムが1つ追加される毎に変更通知を出し,そのたびにViewの更新処理が実行されることになります.

端末の処理性能次第ですが,多少は問題にならないとしても多くなればなるほど負荷が高くなりますし,なんだかスマートではありません.

ListにはAddRangeメソッドがあるのでまとめて追加などができるのですが,ObservableCollectionにはありません.

MvvmHelpersではこの問題を解決してくれます.

それがObservableRangeCollection<T>です.

ObservableCollectionと同じように使えますが,大きな特徴はAddRangeメソッドを持っていることです.

以下のようにIEnumerable<T>なコレクションを引数として受け取り,それを自身のコレクションに一括で追加します.

f:id:shuhelohelo:20200604181713p:plain

追加するコレクションにいくつアイテムがあろうとも,このAddRangeによって発生する変更通知は1回だけです.

ObservableRangeCollectionのCollectionChangedイベントにイベントハンドラをセットして確認します.

            People = new ObservableRangeCollection<Person>();
            People.CollectionChanged += (s, e) =>
            {
                //Peopleに変更(追加や削除. 各アイテムのデータの変更ではない)があった場合に出力
                //確認用
                Debug.WriteLine("People collection changed");
            };

アプリケーションを実行して「GET PEOPLE」ボタンを押すとPeopleにデータが入れられますが,その際にVisual Studioの「出力(output)」ウィンドウに出力されるのは以下の1行だけです.1回しかイベントが発行されていないことがわかります.

f:id:shuhelohelo:20200604182314p:plain

とても便利です.

他にもRemoveRangeやReplaceRangeがあります.

ソースコード

github.com