📖

MVVM ToolKit の使い方

2024/05/04に公開

はじめに

MAUIでアプリ作成する際にMVVMで実装を進めたいと思い「MVVM Toolkit」を選択したので、その際の使い方メモ。以下のMicrosoftの公式ページを参考にしてます。
https://learn.microsoft.com/ja-jp/dotnet/communitytoolkit/mvvm/

使用環境

Windows11Pro(64bit)
VisualStudio2022(64bit) Ver 17.9.6
MVVM ToolKit v8.2.2

プロジェクトの作成

VisualStudioにて「新しいプロジェクトの作成」から「.NET MAUIアプリ」を選択

プロジェクト名やら場所は適当に。フレームワークは「.NET8.0」を選択としました。
プロジェクト名のデフォルトが「MauiApp<数値>」なので、数値だけ消してMauiAppとかにすると、作成されたプロジェクト内でこの名前をすでに使用しているので、名前衝突してビルド出来ませんので、「MauiApp」というプロジェクト名は選択しないでください。

新規作成したプロジェクトをそのままビルドした際に下記のエラーメッセージが出る場合があります。

この場合には、ソリューションエクスプローラからソリューションを右クリックして「リビルド」を実施して下さい。

実行すると、アプリが起動します。

MVVM Toolkitのインストール

NuGetからインストールします。メニューから「ツール」「NuGetパッケージマネージャ」「ソリューションのNuGetパッケージの管理」を選択して下さい。

開いた画面内で、「参照」を選択して、検索ボックスに「communitytoolkit」とでも入力するとパッケージが表示されます。

「CommunityToolkit.MVVM」を選択して、インストール下さい。
正しくインストール出来れば、ソリューションエクスプローラ内の依存関係に表示されます。

また、このパッケージを使用する箇所では、以下のusingが必要となります。

using CommunityToolkit.Mvvm;

プロパティの実装

以後の実装は、ViewModelを作成して記述していきますので、まずクラスを追加します。
ソリューションエクスプローラよりプロジェクトを右クリックして、「追加」「クラス」を選択して下さい。

クラス名は、適当に「ViewModel」とかにしました。
まずは、簡単にnameプロパティを作成します。ソースは以下の通り。

using CommunityToolkit.Mvvm.ComponentModel;

namespace MauiAppSample
{
    [INotifyPropertyChanged]
    internal partial class ViewModel
    {
        [ObservableProperty]
        private string? name = "Hoge";
    }
}

[ObservableProperty]にて、nameプロパティのget/set関数が補完され、プロパティ変更時のイベントも以下を自動で作成してくれます。

partial void OnNameChanging(string? value);
partial void OnNameChanged(string? value);
partial void OnNameChanging(string? oldValue, string? newValue);
partial void OnNameChanged(string? oldValue, string? newValue);

これをView側でバインドして表示するには、MainPage.xamlを変更します。
既存のコードはいろいろとコントロールがあったので、分かりやすくLabelコントロールのみとしています。

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
            xmlns:local ="clr-namespace:MauiAppSample"
             x:Class="MauiAppSample.MainPage">
    <ContentPage.BindingContext>
        <local:ViewModel />
    </ContentPage.BindingContext>
    <Grid>
        <Label Text="{Binding Name}"/>
    </Grid>

</ContentPage>

ContentPageにて、ViewModelを指定します。この時にlocalとしてプロジェクトを指定するので、ContentPage内にlocalの定義を追記しています。
ViewModelで定義した「name」プロパティはprivate変数なので、これをViewからバインディングする際には、先頭文字が大文字となった「Name」を使用します。
この名前の変更は、以下のルールに従います。
・m_、_などのプレフィックスの削除
・先頭の1文字を大文字に
以前のコードで使用していたClickイベントが残っててエラーになるので、この記述も削除します。MainPage.xaml.cs内の「OnCounterClicked」の記述は全部削除して下さい。
この状態で実行すると、ViewModelで定義したnameプロパティを表示します。

プロパティ変更通知

[INotifyPropertyChanged]属性を使用する事で、プロパティが変更された際に他のプロパティの更新通知を投げることが出来る。

[ObservableProperty]
[NotifyPropertyChangedFor(nameof(NameChange))] //Nameの変更時に、OnPropertyChanged("NameChange")が読み出される
    private string name = string.Empty;
    public string NameChange => name.ToUpper();

インスタンス変数をバインドする

MainPage.xamlファイル内で<ContentPage.BindingContext>にてバインドするクラスを指定できますが、コード内のインスタンス変数を使用したい場合には、「BindingContext」を使用します。

ViewModel vm = new ViewModel();
BindingContext = vm;

イベントの実装

ボタンが押された際のイベントの実装です。
先ほどのViewModelクラス内のボタンが押された際のイベントを記述します。

[RelayCommand]
private void GreetUser()
{
    name = "HogeHoge";
}

次にView側にて、このイベントを読み出します。

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
            xmlns:local ="clr-namespace:MauiAppSample"
             x:Class="MauiAppSample.MainPage">
    <ContentPage.BindingContext>
        <local:ViewModel />
    </ContentPage.BindingContext>
    <StackLayout>
        <Label Text="{Binding Name}"/>
        <Button Text="Push" Command="{Binding GreetUserCommand}" />
    </StackLayout>
</ContentPage>

ViewModelにて定義した「GreetUser」関数名に「Command」を付加した「GreetUserCommand」をCommandイベントに定義します。
この名前変更は、以下に従う。
・Onで始まる場合にはOnを削除する、プレフィックスの削除
・残った文字列の末尾に「Command」を付ける

使用/不使用の設定

RelayCommand属性はCanExecuteプロパティを公開しています。
RelayCommand属性に使用/不使用を判定する関数を定義する事で、ボタン自体の使用/不使用を操作出来ます。

[RelayCommand(CanExecute = nameof(CanGreetUser))]
private void GreetUser()
{
    Name = "HogeHoge";
}
private bool CanGreetUser()
{
    return true;
}

CanGreetUser関数の戻り値をtrueとしているので、絶えず使用可能となります。

おわり

MVVMにてプロパティのバインディングとボタンのイベントについて調べました。これで最低限の実装は可能かと思います。

Discussion