mcpField
M
mvvm-toolkit

mvvm-toolkit

github.com/github開発40.5k92 回インストール1 日前に更新
リポジトリ

mvvm-toolkit について

mvvm-toolkit は、CommunityToolkit.Mvvm 8.x を使うアプリの ViewModel、プロパティ、コマンド、検証を作成・レビューする開発者向けのスキルです。ソースジェネレーターの利用法、基底クラスの選択、よくある問題を解説します。

主な機能

  1. 01属性による監視可能プロパティの生成
  2. 02同期・非同期コマンドの生成
  3. 03関連プロパティとコマンドへの通知
  4. 04フォーム入力の検証に対応
  5. 05診断情報と一般的な問題の解説

活用シーン

  1. 01ViewModel への監視可能プロパティの追加
  2. 02同期または非同期コマンドの作成
  3. 03入力検証付きフォームの構築
  4. 04CommunityToolkit.Mvvm 実装のレビュー

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/github/awesome-copilot --skill mvvm-toolkit

インストール後、Claude Code で/mvvm-toolkitを入力して読み込まれたか確認します。

同梱ファイル

SKILL.md と一緒にインストールされる参考資料やスクリプト。

references/
end-to-end-walkthrough.md
relaycommand-cookbook.md
source-generators.md
troubleshooting.md
validation.md

詳細

CommunityToolkit.Mvvm(コア)

CommunityToolkit.Mvvm 8.x を使うアプリで、ViewModel、プロパティ、コマンド、または検証を作成またはレビューする際に、このスキルを使用してください。

関連スキル。 IMessenger のパブリッシュ/サブスクライブパターンには mvvm-toolkit-messenger を読み込みます。Microsoft.Extensions.DependencyInjection との統合には mvvm-toolkit-di を読み込みます。

要点のおさらい。 partial クラスのプライベートフィールドに [ObservableProperty] を適用します。インスタンスメソッドに [RelayCommand] を適用します。ObservableObject を継承します(入力フォームでは ObservableValidator、IMessenger を使う場合は ObservableRecipient を継承します)。


パッケージとセットアップ

<ItemGroup>
  <PackageReference Include="CommunityToolkit.Mvvm" Version="8.*" />
</ItemGroup>

対象:netstandard2.0、netstandard2.1、net6.0 以降。.NET、.NET Framework、Mono で動作します。ソースジェネレーターは同じ NuGet パッケージに含まれているため、追加のアナライザー参照は不要です。

名前空間:

using CommunityToolkit.Mvvm.ComponentModel;   // ObservableObject, [ObservableProperty]
using CommunityToolkit.Mvvm.Input;             // [RelayCommand], RelayCommand, AsyncRelayCommand

共通ルール。 [ObservableProperty] または [RelayCommand] を使うすべての型と、ネストされている場合はそれを囲むすべての型を partial として宣言する必要があります。そうしないと、ジェネレーターは MVVMTK0008/MVVMTK0042 を出力します。


ソースジェネレーター早見表

属性適用対象生成されるもの
[ObservableProperty]プライベートフィールドパブリックな INotifyPropertyChanged プロパティと OnXxxChanging/OnXxxChanged 部分メソッドフック
[NotifyPropertyChangedFor(nameof(Other))]オブザーバブルフィールド指定されたプロパティについても PropertyChanged を発生させます
[NotifyCanExecuteChangedFor(nameof(MyCommand))]オブザーバブルフィールド変更時に MyCommand.NotifyCanExecuteChanged() を呼び出します
[NotifyDataErrorInfo]ObservableValidator のオブザーバブルフィールドセッターから ValidateProperty(value) を呼び出します
[NotifyPropertyChangedRecipients]ObservableRecipient のオブザーバブルフィールド変更後に Broadcast(old, new) を呼び出します
[RelayCommand]インスタンスメソッドIRelayCommand/IAsyncRelayCommand として公開される遅延初期化の RelayCommand/AsyncRelayCommand
[RelayCommand(CanExecute = nameof(CanX))]インスタンスメソッドCanExecute をメソッドまたはプロパティに接続します
[RelayCommand(IncludeCancelCommand = true)]CancellationToken を受け取る非同期メソッドXxxCancelCommand も生成します
[RelayCommand(AllowConcurrentExecutions = true)]非同期メソッドキュー処理/並列呼び出しを許可します(デフォルトでは実行中に無効になります)
[RelayCommand(FlowExceptionsToTaskScheduler = true)]非同期メソッド例外を待機して再スローする代わりに、ExecutionTask を通じて表面化させます
[property: SomeAttr]オブザーバブルフィールドまたは [RelayCommand] メソッドSomeAttr を生成されるプロパティに転送します(例:[JsonIgnore])

命名規則。 フィールド name/_name/m_name → Name。メソッド LoadAsync → LoadCommand(Async 接尾辞が削除され、先頭の On も削除されます)。

生成コードのサンプルを含む属性の詳細については、references/source-generators.md を参照してください。


ViewModel のパターン

シンプルなオブザーバブルプロパティ
public partial class ContactViewModel : ObservableObject
{
    [ObservableProperty]
    private string? name;
}
フック:OnXxxChanging/OnXxxChanged
[ObservableProperty]
private string? name;

partial void OnNameChanged(string? value) =>
    Logger.LogInformation("Name changed to {Name}", value);

単一引数 (value) と 2 引数 (oldValue, newValue) の両方のオーバーロードを使用できます。必要なものだけを実装してください。未実装のフックはコンパイラーによって省略されるため、実行時コストはゼロです。

依存プロパティと依存コマンド
[ObservableProperty]
[NotifyPropertyChangedFor(nameof(FullName))]
[NotifyCanExecuteChangedFor(nameof(SaveCommand))]
private string? firstName;

[ObservableProperty]
[NotifyPropertyChangedFor(nameof(FullName))]
[NotifyCanExecuteChangedFor(nameof(SaveCommand))]
private string? lastName;

public string FullName => $"{FirstName} {LastName}".Trim();
オブザーバブルではないモデルのラップ
public sealed class ObservableUser(User user) : ObservableObject
{
    public string Name
    {
        get => user.Name;
        set => SetProperty(user.Name, value, user, (u, n) => u.Name = n);
    }
}

呼び出し時の割り当てをなくすには、静的ラムダ(キャプチャする状態がないもの)を渡してください。


コマンド

[RelayCommand]
private void Refresh() => Items.Reset();

[RelayCommand]
private async Task LoadAsync()
{
    foreach (var item in await service.GetItemsAsync())
        Items.Add(item);
}

[RelayCommand(IncludeCancelCommand = true)]
private async Task DownloadAsync(CancellationToken token)
{
    await using var stream = await http.GetStreamAsync(url, token);
    // ...
}

[RelayCommand(CanExecute = nameof(CanSave))]
private Task SaveAsync() => repo.SaveAsync(Name!);

private bool CanSave() => !string.IsNullOrWhiteSpace(Name);

コマンドのライフタイムを明示的に管理する必要がある場合、または複雑なソースからコマンドを合成する場合に限り、RelayCommand/AsyncRelayCommand のコンストラクターを直接使用してください。属性を使う方法で、約 95% のケースに対応できます。

同期/非同期/キャンセル可能/並行実行/エラーの表面化に関するレシピについては、references/relaycommand-cookbook.md を参照してください。


基底クラスの選択

基底クラス使用する場面
ObservableObjectデフォルトです。INotifyPropertyChanged + INotifyPropertyChanging + SetProperty オーバーロード + Task プロパティ用の SetPropertyAndNotifyOnCompletion を提供します
ObservableValidatorVM で INotifyDataErrorInfo が必要な場合(フォーム、設定入力など)
ObservableRecipientVM が IMessenger メッセージを送受信する場合。mvvm-toolkit-messenger スキルを参照してください

C# は単一継承です。ObservableValidator と ObservableRecipient はいずれも ObservableObject を継承しているため、組み合わせるには コンポジションが必要です(例:ObservableValidator に IMessenger を 注入します)。


検証

using System.ComponentModel.DataAnnotations;

public sealed partial class RegistrationViewModel : ObservableValidator
{
    [ObservableProperty]
    [NotifyDataErrorInfo]
    [Required, MinLength(2), MaxLength(100)]
    private string? name;

    [ObservableProperty]
    [NotifyDataErrorInfo]
    [Required, EmailAddress]
    private string? email;

    [RelayCommand]
    private void Submit()
    {
        ValidateAllProperties();
        if (HasErrors) return;
        // submit...
    }
}

その他のエントリーポイント:TrySetProperty、ValidateProperty(value, name)、 ClearAllErrors()、GetErrors(propertyName)。カスタムルールでは [CustomValidation] メソッドとカスタム ValidationAttribute サブクラスを サポートしています。

バリデーターの全機能については、references/validation.mdを 参照してください。


主な注意点

  1. partial の付け忘れ。 クラス(およびすべての外側の型)を partial にする必要があります。コンパイルエラー MVVMTK0008 / MVVMTK0042 が発生します。
  2. フィールド名が PascalCase。 [ObservableProperty] private string Name; は生成されるプロパティと衝突します。name、_name、または m_name を使用してください。
  3. [RelayCommand] での async void。 ジェネレーターが IAsyncRelayCommand として ラップするのは、Task を返すメソッドのみです。async void は同期的な RelayCommand になり、例外が監視されません。常に Task を返してください。
  4. [NotifyCanExecuteChangedFor] の付け忘れ。 CanSave() が true を返すようになっても、 Save ボタンは無効のままです。
  5. [ObservableProperty] フィールドが保持する同じ参照を変更する。 EqualityComparer<T>.Default は true を返すため、通知が発生しません。インスタンスを 変更するのではなく、置き換えてください。

診断の一覧(MVVMTK0xxx)やその他の注意点については、 references/troubleshooting.mdを参照してください。


エンドツーエンドの簡単なウォークスルー

ジェネレーター + コマンド + [NotifyCanExecuteChangedFor] を示す、2 ペイン構成の Notes アプリです:

public sealed partial class NoteViewModel(INotesService notes,
    IMessenger messenger) : ObservableRecipient(messenger)
{
    [ObservableProperty]
    [NotifyCanExecuteChangedFor(nameof(SaveCommand))]
    [NotifyCanExecuteChangedFor(nameof(DeleteCommand))]
    private string? filename;

    [ObservableProperty]
    [NotifyCanExecuteChangedFor(nameof(SaveCommand))]
    private string? text;

    [RelayCommand(CanExecute = nameof(CanSave))]
    private Task SaveAsync()
    {
        Messenger.Send(new NoteSavedMessage(Filename!));
        return notes.SaveAsync(Filename!, Text!);
    }

    [RelayCommand(CanExecute = nameof(CanDelete))]
    private Task DeleteAsync() => notes.DeleteAsync(Filename!);

    private bool CanSave() =>
        !string.IsNullOrWhiteSpace(Filename) && !string.IsNullOrEmpty(Text);
    private bool CanDelete() => !string.IsNullOrWhiteSpace(Filename);
}

サンプル全体(DI の設定、View のコードビハインド、XAML、単体テスト)については、 references/end-to-end-walkthrough.mdを参照してください。


リファレンスと関連スキル

トピック参照先
ソースジェネレーター属性のリファレンスreferences/source-generators.md
RelayCommand のレシピreferences/relaycommand-cookbook.md
検証の詳細references/validation.md
Notes アプリのウォークスルー全体references/end-to-end-walkthrough.md
MVVMTK0xxx 診断と注意点references/troubleshooting.md
Messenger の pub/sub関連スキル:mvvm-toolkit-messenger
Microsoft.Extensions.DependencyInjection の設定関連スキル:mvvm-toolkit-di

外部ソース:

コメント