今回は .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を追加します。


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プロパティにGreetCommand、CommandParameterプロパティにFullName、21行目でTextBlockコントロールのTextプロパティにGreetingMessageをそれぞれバインドしています。
コマンドの追加
NewMainViewModelを以下のように設定します。NewMainViewModelはObservableObjectを継承しています。変更を監視するプロパティとしてObservableProperty属性を設定したGreetingMessageを追加します。また、ビュー(XAML)でButtonコントロールのCommandプロパティに設定したGreetCommandにRelayCommand属性を設定します。
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)でテキストを更新すると、FirstNameとLastNameが変更されたテキストを受け取って、この変更した値を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パターンを実装できるようになっているかと思います。
