今回は .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パターンを実装できるようになっているかと思います。