.NET Community ToolkitでMVVMを試してみる(コマンド)

普段のお仕事に役立つ普遍的なプログラミングTIPSや、業界で注目度が高い最新情報をお届けする「編集部ピックアップ」。
今回は .NET Community Toolkitに含まれるライブラリ「CommunityToolkit.Mvvm(MVVM Toolkit)」を使用してコマンドを実装する方法ついてお届けします。

本記事では、.NETアプリケーション開発向けに提供されている「.NET Community Toolkit」に含まれるライブラリの1つ「CommunityToolkit.Mvvm(MVVM Toolkit)」を使用してWPFアプリケーションでコマンドを実装する方法を確認してみます。

.NET Community Toolkitとは?

.NET Community Toolkit は、特定の .NET UI プラットフォームにとらわれないヘルパーとAPIを含んだライブラリになっています。

.NET Community Toolkit のライブラリとして以下が含まれています。

  • CommunityToolkit.Common
    他のCommunityToolkitライブラリと共有されるヘルパーAPIのセット
  • CommunityToolkit.Diagnostics
    引数の検証やエラーチェックをよりきれいに、より効率的に、より少なく行うことができるヘルパーAPI
  • CommunityToolkit.HighPerformance
    高パフォーマンスのシナリオで作業するためのヘルパーのコレクション
  • CommunityToolkit.Mvvm
    高速でモジュール化されたプラットフォームに依存しないMVVM ライブラリ

各ライブラリの詳細は公式サイトで参照できます。ソースコードはGitHubで公開されており更新内容を確認できます。

CommunityToolkit.Mvvmは特定の .NET UIプラットフォームに依存せずWindows Forms、WPF、UWP、Xamarin、.NET MAUI、WinUI 3で利用できます。また、このライブラリにはMVVMソースジェネレーターが実装されており、従来よりもシンプルにMVVMパターンを実装できるようになっています。

実装する内容

前回の記事では、「姓」と「名」のテキストを入力できるTextBoxを2つ配置し、入力された「姓」と「名」のテキストを連結して「氏名」として表示する、といった内容のWPFアプリケーションを作成しました。

前回実装した内容

今回は表示した「氏名」に対してボタンをクリックすると「あいさつ文」を表示するようにWPFアプリケーションを更新します。

実装する内容

ボタンのCommandプロパティにICommandを実装したコマンドをバインドして、ボタンクリック時に上記の処理が実行されるように実装します。

WPFアプリケーションの作成とCommunityToolkit.Mvvmの追加

Visual StudioでWPFアプリケーション「WpfMvvmToolkitApp2」を作成します。Visual Studioのソリューションエクスプローラーからプロジェクトを右クリックして、コンテキストメニューから[NuGet パッケージの管理]を選択してCommunityToolkit.Mvvmを追加します。

WPFアプリケーションの作成とCommunityToolkit.Mvvmの追加1
WPFアプリケーションの作成とCommunityToolkit.Mvvmの追加2

UIの作成

作成したWPFアプリケーションのMainWindowでは、以下のようにコントロールとデータコンテキストを設定します。

<Window x:Class="WpfMvvmToolkitApp2.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WpfMvvmToolkitApp2"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <Window.DataContext>
        <local:NewMainViewModel/>
        <!--<local:OldMainViewModel/>-->
    </Window.DataContext>
    <StackPanel Margin="10">
        <TextBlock Text="姓:"/>
        <TextBox Text="{Binding LastName}"/>
        <TextBlock Text="名:" Margin="0,10,0,0"/>
        <TextBox Text="{Binding FirstName}"/>
        <TextBlock Text="氏名:" Margin="0,10,0,0"/>
        <TextBlock Text="{Binding FullName}" Background="AliceBlue" />
        <Button Content="挨拶" Command="{Binding GreetCommand}" CommandParameter="{Binding FullName}" Margin="0,10,0,0" />
        <TextBlock Text="{Binding GreetingMessage}" Background="AliceBlue" />
    </StackPanel>
</Window>

10、11行目でデータコンテキストを切り替えるようにしておきます。NewMainViewModelはCommunityToolkit.Mvvmを使用するクラス、OldMainViewModelはCommunityToolkit.Mvvmを使用しないクラスとしておきます。

また、20行目でButtonコントロールのCommandプロパティにGreetCommandCommandParameterプロパティにFullName、21行目でTextBlockコントロールのTextプロパティにGreetingMessageをそれぞれバインドしています。

コマンドの追加

NewMainViewModelを以下のように設定します。NewMainViewModelObservableObjectを継承しています。変更を監視するプロパティとしてObservableProperty属性を設定したGreetingMessageを追加します。また、ビュー(XAML)でButtonコントロールのCommandプロパティに設定したGreetCommandRelayCommand属性を設定します。

using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using System;
using System.ComponentModel;
using System.Windows.Input;

namespace WpfMvvmToolkitApp2;

public partial class NewMainViewModel : ObservableObject
{
    [ObservableProperty]
    [NotifyPropertyChangedFor(nameof(FullName))]
    private string firstName = "太郎";

    [ObservableProperty]
    [NotifyPropertyChangedFor(nameof(FullName))]
    private string lastName = "葡萄";

    public string FullName => $"{LastName} {FirstName}";

    [ObservableProperty]
    public string greetingMessage = "";

    [RelayCommand]
    private void Greet(string? user)
    {
        GreetingMessage = $"こんにちは、{user}!";
    }
}

画面の「姓」と「名」の入力フィールド(TextBox)でテキストを更新すると、FirstNameLastNameが変更されたテキストを受け取って、この変更した値をFullNameへと渡します。

そして、「挨拶」ボタンをクリックすると、FullNameをコマンドの引数としてGreetCommandに渡されてGreetingMessageが更新されます。このGreetingMessageは「挨拶」ボタンの下にある表示フィールド(TextBlock)のテキストに渡されます。

さいごに

今回は「CommunityToolkit.Mvvm(MVVM Toolkit)」でMVVMパターンによるコマンドを実装する方法ついて試してみました。

public partial class OldMainViewModel : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler? PropertyChanged;

    private string firstName = "太郎";
    public string FirstName
    {
        get => firstName;
        set
        {
            firstName = value;
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(FullName)));
        }
    }

    private string lastName = "葡萄";
    public string LastName
    {
        get => lastName;
        set
        {
            lastName = value;
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(FullName)));
        }
    }

    public string FullName => $"{LastName} {FirstName}";

    private string greetingMessage = "";
    public string GreetingMessage
    {
        get => greetingMessage;
        set
        {
            greetingMessage = value;
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(GreetingMessage)));
        }
    }

    private DelegateCommand<string>? greetCommand;
    public DelegateCommand<string>? GreetCommand
    {
        get
        {
            return greetCommand ??= new DelegateCommand<string>(Greet);
        }
    }

    private void Greet(string? user)
    {
        GreetingMessage = $"こんにちは、{user}!";
    }
}

public class DelegateCommand<T> : ICommand
{
    private readonly Action<T> _execute;
    private readonly Func<bool> _canExecute;

    public event EventHandler? CanExecuteChanged;

    public DelegateCommand(Action<T> execute) : this(execute, () => true)
    {
    }

    public DelegateCommand(Action<T> execute, Func<bool> canExecute)
    {
        _execute = execute;
        _canExecute = canExecute;
    }

    public void Execute(object? parameter)
    {
        _execute((T)parameter);
    }

    public bool CanExecute(object? parameter)
    {
        return _canExecute();
    }
}

上記のようにコマンドで引数を受け取るMVVMパターンの従来の実装方法であるOldMainViewModelと比較すると、ICommnandを継承して引数を受け取るクラスDelegateCommand<t>を別途追加する必要もなくなるので、NewMainViewModelの方がコード量も少なくシンプルにMVVMパターンを実装できるようになっているかと思います。

\  この記事をシェアする  /