[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M6ADWJGSFHXE7JM9S6SB22DZ":3,"skill-same:01M6ADWJGSFHXE7JM9S6SB22DZ":90},{"skillNo":4,"name":5,"nameZh":6,"description":7,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":16,"categories":17,"keywords":18,"publishedAt":39,"linkOnly":40,"markdownContent":41,"markdownBody":42,"frontMatter":43,"refName":44,"commitSha":45,"skillDirectory":46,"forksCount":47,"repositoryHtmlUrl":48,"repositoryHomepageUrl":49,"ownerHtmlUrl":50,"ownerBlogUrl":51,"ownerDescription":52,"updatedAt":53,"install":54,"resources":67},"01M6ADWJGSFHXE7JM9S6SB22DZ","mvvm-toolkit",null,"CommunityToolkit.Mvvm (the MVVM Toolkit) core: source generators ([ObservableProperty], [RelayCommand], [NotifyPropertyChangedFor], [NotifyCanExecuteChangedFor], [NotifyDataErrorInfo]), base classes (ObservableObject \u002F ObservableValidator \u002F ObservableRecipient), commands (RelayCommand \u002F AsyncRelayCommand), and validation. Companion skills: mvvm-toolkit-messenger for pub\u002Fsub, mvvm-toolkit-di for Microsoft.Extensions.DependencyInjection wiring. Works across WPF, WinUI 3, MAUI, Uno, and Avalonia.","github\u002Fawesome-copilot","github","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F9919?v=4","01M6AC83DS133TPXFSP1DHNCC6","GitHub","01M6AD3PBDYPK7NVFJNJ66Y8RS",41046,"MIT","https:\u002F\u002Fgithub.com\u002Fgithub\u002Fawesome-copilot\u002Fblob\u002Fmain\u002Fskills\u002Fmvvm-toolkit\u002FSKILL.md",[],[19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38],"toolkit","mvvm toolkit","mvvm","commands","di","wiring","source","for","extensions","messenger","companion","maui","avalonia","microsoft","across","base","validation","wpf","winui","core","2026-11-10 10:12:30",false,"---\nname: mvvm-toolkit\ndescription: 'CommunityToolkit.Mvvm (the MVVM Toolkit) core: source generators ([ObservableProperty], [RelayCommand], [NotifyPropertyChangedFor], [NotifyCanExecuteChangedFor], [NotifyDataErrorInfo]), base classes (ObservableObject \u002F ObservableValidator \u002F ObservableRecipient), commands (RelayCommand \u002F AsyncRelayCommand), and validation. Companion skills: mvvm-toolkit-messenger for pub\u002Fsub, mvvm-toolkit-di for Microsoft.Extensions.DependencyInjection wiring. Works across WPF, WinUI 3, MAUI, Uno, and Avalonia.'\n---\n\n# CommunityToolkit.Mvvm (core)\n\nUse this skill when authoring or reviewing ViewModels, properties,\ncommands, or validation in apps that use `CommunityToolkit.Mvvm` 8.x.\n\n> **Companion skills.** Load **`mvvm-toolkit-messenger`** for `IMessenger`\n> pub\u002Fsub patterns. Load **`mvvm-toolkit-di`** for\n> `Microsoft.Extensions.DependencyInjection` integration.\n\n> **Quick recap.** `[ObservableProperty]` on private fields in `partial`\n> classes; `[RelayCommand]` on instance methods; inherit from\n> `ObservableObject` (or `ObservableValidator` for input forms,\n> `ObservableRecipient` when using `IMessenger`).\n\n---\n\n## Package & setup\n\n```xml\n\u003CItemGroup>\n  \u003CPackageReference Include=\"CommunityToolkit.Mvvm\" Version=\"8.*\" \u002F>\n\u003C\u002FItemGroup>\n```\n\nTargets: `netstandard2.0`, `netstandard2.1`, `net6.0`+. Works on .NET, .NET\nFramework, Mono. Source generators ship in the same NuGet — no extra\nanalyzer reference required.\n\nNamespaces:\n\n```csharp\nusing CommunityToolkit.Mvvm.ComponentModel;   \u002F\u002F ObservableObject, [ObservableProperty]\nusing CommunityToolkit.Mvvm.Input;             \u002F\u002F [RelayCommand], RelayCommand, AsyncRelayCommand\n```\n\n> **Universal rule.** Every type that uses `[ObservableProperty]` or\n> `[RelayCommand]` — and every enclosing type, if nested — must be\n> declared `partial`. Without it, the generators emit\n> `MVVMTK0008` \u002F `MVVMTK0042`.\n\n---\n\n## Source generators cheat sheet\n\n| Attribute | Applied to | Generates |\n|-----------|-----------|-----------|\n| `[ObservableProperty]` | private field | Public `INotifyPropertyChanged` property + `OnXxxChanging`\u002F`OnXxxChanged` partial-method hooks |\n| `[NotifyPropertyChangedFor(nameof(Other))]` | observable field | Also raises `PropertyChanged` for the listed property |\n| `[NotifyCanExecuteChangedFor(nameof(MyCommand))]` | observable field | Calls `MyCommand.NotifyCanExecuteChanged()` on change |\n| `[NotifyDataErrorInfo]` | observable field on `ObservableValidator` | Calls `ValidateProperty(value)` from the setter |\n| `[NotifyPropertyChangedRecipients]` | observable field on `ObservableRecipient` | `Broadcast(old, new)` after the change |\n| `[RelayCommand]` | instance method | Lazy `RelayCommand` \u002F `AsyncRelayCommand` exposed as `IRelayCommand` \u002F `IAsyncRelayCommand` |\n| `[RelayCommand(CanExecute = nameof(CanX))]` | instance method | Wires `CanExecute` to a method or property |\n| `[RelayCommand(IncludeCancelCommand = true)]` | async method with `CancellationToken` | Also generates `XxxCancelCommand` |\n| `[RelayCommand(AllowConcurrentExecutions = true)]` | async method | Allows queued\u002Fparallel invocations (default disables while running) |\n| `[RelayCommand(FlowExceptionsToTaskScheduler = true)]` | async method | Surfaces exceptions via `ExecutionTask` instead of awaiting and rethrowing |\n| `[property: SomeAttr]` | observable field or `[RelayCommand]` method | Forwards `SomeAttr` onto the generated property (e.g., `[JsonIgnore]`) |\n\n**Naming.** Field `name` \u002F `_name` \u002F `m_name` → `Name`. Method `LoadAsync` →\n`LoadCommand` (the `Async` suffix is stripped; a leading `On` is also\nstripped).\n\nSee [`references\u002Fsource-generators.md`](references\u002Fsource-generators.md) for\nthe full attribute reference with generated-code samples.\n\n---\n\n## ViewModel patterns\n\n### Simple observable property\n\n```csharp\npublic partial class ContactViewModel : ObservableObject\n{\n    [ObservableProperty]\n    private string? name;\n}\n```\n\n### Hooks: `OnXxxChanging` \u002F `OnXxxChanged`\n\n```csharp\n[ObservableProperty]\nprivate string? name;\n\npartial void OnNameChanged(string? value) =>\n    Logger.LogInformation(\"Name changed to {Name}\", value);\n```\n\nBoth single-arg `(value)` and two-arg `(oldValue, newValue)` overloads\nare available. Implement only the ones you need; unimplemented hooks are\nelided by the compiler (zero runtime cost).\n\n### Dependent properties + dependent commands\n\n```csharp\n[ObservableProperty]\n[NotifyPropertyChangedFor(nameof(FullName))]\n[NotifyCanExecuteChangedFor(nameof(SaveCommand))]\nprivate string? firstName;\n\n[ObservableProperty]\n[NotifyPropertyChangedFor(nameof(FullName))]\n[NotifyCanExecuteChangedFor(nameof(SaveCommand))]\nprivate string? lastName;\n\npublic string FullName => $\"{FirstName} {LastName}\".Trim();\n```\n\n### Wrapping a non-observable model\n\n```csharp\npublic sealed class ObservableUser(User user) : ObservableObject\n{\n    public string Name\n    {\n        get => user.Name;\n        set => SetProperty(user.Name, value, user, (u, n) => u.Name = n);\n    }\n}\n```\n\nPass a static lambda (no captured state) to keep the call allocation-free.\n\n---\n\n## Commands\n\n```csharp\n[RelayCommand]\nprivate void Refresh() => Items.Reset();\n\n[RelayCommand]\nprivate async Task LoadAsync()\n{\n    foreach (var item in await service.GetItemsAsync())\n        Items.Add(item);\n}\n\n[RelayCommand(IncludeCancelCommand = true)]\nprivate async Task DownloadAsync(CancellationToken token)\n{\n    await using var stream = await http.GetStreamAsync(url, token);\n    \u002F\u002F ...\n}\n\n[RelayCommand(CanExecute = nameof(CanSave))]\nprivate Task SaveAsync() => repo.SaveAsync(Name!);\n\nprivate bool CanSave() => !string.IsNullOrWhiteSpace(Name);\n```\n\nReach for manual `RelayCommand` \u002F `AsyncRelayCommand` constructors only\nwhen you must own the command's lifetime explicitly or compose it from\nnon-trivial sources. The attribute style covers ~95% of cases.\n\nSee [`references\u002Frelaycommand-cookbook.md`](references\u002Frelaycommand-cookbook.md)\nfor sync \u002F async \u002F cancellable \u002F concurrency \u002F error-surfacing recipes.\n\n---\n\n## Base class selection\n\n| Base class | Use when |\n|------------|---------|\n| `ObservableObject` | Default. `INotifyPropertyChanged` + `INotifyPropertyChanging` + `SetProperty` overloads + `SetPropertyAndNotifyOnCompletion` for `Task` properties |\n| `ObservableValidator` | The VM needs `INotifyDataErrorInfo` (forms, settings input) |\n| `ObservableRecipient` | The VM sends or receives `IMessenger` messages — see the **`mvvm-toolkit-messenger`** skill |\n\nC# is single-inheritance: `ObservableValidator` and `ObservableRecipient`\nboth extend `ObservableObject`, so combining them requires composition\n(e.g., inject `IMessenger` into an `ObservableValidator`).\n\n---\n\n## Validation\n\n```csharp\nusing System.ComponentModel.DataAnnotations;\n\npublic sealed partial class RegistrationViewModel : ObservableValidator\n{\n    [ObservableProperty]\n    [NotifyDataErrorInfo]\n    [Required, MinLength(2), MaxLength(100)]\n    private string? name;\n\n    [ObservableProperty]\n    [NotifyDataErrorInfo]\n    [Required, EmailAddress]\n    private string? email;\n\n    [RelayCommand]\n    private void Submit()\n    {\n        ValidateAllProperties();\n        if (HasErrors) return;\n        \u002F\u002F submit...\n    }\n}\n```\n\nOther entry points: `TrySetProperty`, `ValidateProperty(value, name)`,\n`ClearAllErrors()`, `GetErrors(propertyName)`. Custom rules support\n`[CustomValidation]` methods and custom `ValidationAttribute` subclasses.\n\nSee [`references\u002Fvalidation.md`](references\u002Fvalidation.md) for the full\nvalidator surface area.\n\n---\n\n## Top pitfalls\n\n1. **Forgetting `partial`.** Class (and every enclosing type) must be\n   `partial`. Compile error `MVVMTK0008` \u002F `MVVMTK0042`.\n2. **PascalCase field name.** `[ObservableProperty] private string Name;`\n   collides with the generated property. Use `name`, `_name`, or `m_name`.\n3. **`async void` on `[RelayCommand]`.** The generator only wraps\n   `Task`-returning methods as `IAsyncRelayCommand`. `async void` becomes\n   a sync `RelayCommand` and exceptions are unobserved. Always return\n   `Task`.\n4. **Forgetting `[NotifyCanExecuteChangedFor]`.** The Save button stays\n   disabled even though `CanSave()` would now return `true`.\n5. **Mutating the same reference held by an `[ObservableProperty]`\n   field.** `EqualityComparer\u003CT>.Default` returns `true`, no notification\n   fires. Replace the instance instead of mutating it.\n\nFor the full diagnostic table (`MVVMTK0xxx`) and more pitfalls, see\n[`references\u002Ftroubleshooting.md`](references\u002Ftroubleshooting.md).\n\n---\n\n## End-to-end mini walkthrough\n\nA two-pane Notes app demonstrating generators + commands +\n`[NotifyCanExecuteChangedFor]`:\n\n```csharp\npublic sealed partial class NoteViewModel(INotesService notes,\n    IMessenger messenger) : ObservableRecipient(messenger)\n{\n    [ObservableProperty]\n    [NotifyCanExecuteChangedFor(nameof(SaveCommand))]\n    [NotifyCanExecuteChangedFor(nameof(DeleteCommand))]\n    private string? filename;\n\n    [ObservableProperty]\n    [NotifyCanExecuteChangedFor(nameof(SaveCommand))]\n    private string? text;\n\n    [RelayCommand(CanExecute = nameof(CanSave))]\n    private Task SaveAsync()\n    {\n        Messenger.Send(new NoteSavedMessage(Filename!));\n        return notes.SaveAsync(Filename!, Text!);\n    }\n\n    [RelayCommand(CanExecute = nameof(CanDelete))]\n    private Task DeleteAsync() => notes.DeleteAsync(Filename!);\n\n    private bool CanSave() =>\n        !string.IsNullOrWhiteSpace(Filename) && !string.IsNullOrEmpty(Text);\n    private bool CanDelete() => !string.IsNullOrWhiteSpace(Filename);\n}\n```\n\nFor the full sample (DI wiring, View code-behind, XAML, unit tests), see\n[`references\u002Fend-to-end-walkthrough.md`](references\u002Fend-to-end-walkthrough.md).\n\n---\n\n## References & companion skills\n\n| Topic | Where |\n|-------|-------|\n| Source generator attribute reference | [`references\u002Fsource-generators.md`](references\u002Fsource-generators.md) |\n| RelayCommand recipes | [`references\u002Frelaycommand-cookbook.md`](references\u002Frelaycommand-cookbook.md) |\n| Validation deep dive | [`references\u002Fvalidation.md`](references\u002Fvalidation.md) |\n| Full Notes-app walkthrough | [`references\u002Fend-to-end-walkthrough.md`](references\u002Fend-to-end-walkthrough.md) |\n| `MVVMTK0xxx` diagnostics & pitfalls | [`references\u002Ftroubleshooting.md`](references\u002Ftroubleshooting.md) |\n| **Messenger pub\u002Fsub** | Companion skill: **`mvvm-toolkit-messenger`** |\n| **`Microsoft.Extensions.DependencyInjection` wiring** | Companion skill: **`mvvm-toolkit-di`** |\n\nExternal sources:\n\n- Toolkit overview: \u003Chttps:\u002F\u002Flearn.microsoft.com\u002Fen-us\u002Fdotnet\u002Fcommunitytoolkit\u002Fmvvm\u002F>\n- WinUI MVVM Toolkit tutorial: \u003Chttps:\u002F\u002Flearn.microsoft.com\u002Fen-us\u002Fwindows\u002Fapps\u002Ftutorials\u002Fwinui-mvvm-toolkit\u002Fintro>\n- Source: \u003Chttps:\u002F\u002Fgithub.com\u002FCommunityToolkit\u002Fdotnet>\n- Samples: \u003Chttps:\u002F\u002Fgithub.com\u002FCommunityToolkit\u002FMVVM-Samples>\n","# CommunityToolkit.Mvvm (core)\n\nUse this skill when authoring or reviewing ViewModels, properties,\ncommands, or validation in apps that use `CommunityToolkit.Mvvm` 8.x.\n\n> **Companion skills.** Load **`mvvm-toolkit-messenger`** for `IMessenger`\n> pub\u002Fsub patterns. Load **`mvvm-toolkit-di`** for\n> `Microsoft.Extensions.DependencyInjection` integration.\n\n> **Quick recap.** `[ObservableProperty]` on private fields in `partial`\n> classes; `[RelayCommand]` on instance methods; inherit from\n> `ObservableObject` (or `ObservableValidator` for input forms,\n> `ObservableRecipient` when using `IMessenger`).\n\n---\n\n## Package & setup\n\n```xml\n\u003CItemGroup>\n  \u003CPackageReference Include=\"CommunityToolkit.Mvvm\" Version=\"8.*\" \u002F>\n\u003C\u002FItemGroup>\n```\n\nTargets: `netstandard2.0`, `netstandard2.1`, `net6.0`+. Works on .NET, .NET\nFramework, Mono. Source generators ship in the same NuGet — no extra\nanalyzer reference required.\n\nNamespaces:\n\n```csharp\nusing CommunityToolkit.Mvvm.ComponentModel;   \u002F\u002F ObservableObject, [ObservableProperty]\nusing CommunityToolkit.Mvvm.Input;             \u002F\u002F [RelayCommand], RelayCommand, AsyncRelayCommand\n```\n\n> **Universal rule.** Every type that uses `[ObservableProperty]` or\n> `[RelayCommand]` — and every enclosing type, if nested — must be\n> declared `partial`. Without it, the generators emit\n> `MVVMTK0008` \u002F `MVVMTK0042`.\n\n---\n\n## Source generators cheat sheet\n\n| Attribute | Applied to | Generates |\n|-----------|-----------|-----------|\n| `[ObservableProperty]` | private field | Public `INotifyPropertyChanged` property + `OnXxxChanging`\u002F`OnXxxChanged` partial-method hooks |\n| `[NotifyPropertyChangedFor(nameof(Other))]` | observable field | Also raises `PropertyChanged` for the listed property |\n| `[NotifyCanExecuteChangedFor(nameof(MyCommand))]` | observable field | Calls `MyCommand.NotifyCanExecuteChanged()` on change |\n| `[NotifyDataErrorInfo]` | observable field on `ObservableValidator` | Calls `ValidateProperty(value)` from the setter |\n| `[NotifyPropertyChangedRecipients]` | observable field on `ObservableRecipient` | `Broadcast(old, new)` after the change |\n| `[RelayCommand]` | instance method | Lazy `RelayCommand` \u002F `AsyncRelayCommand` exposed as `IRelayCommand` \u002F `IAsyncRelayCommand` |\n| `[RelayCommand(CanExecute = nameof(CanX))]` | instance method | Wires `CanExecute` to a method or property |\n| `[RelayCommand(IncludeCancelCommand = true)]` | async method with `CancellationToken` | Also generates `XxxCancelCommand` |\n| `[RelayCommand(AllowConcurrentExecutions = true)]` | async method | Allows queued\u002Fparallel invocations (default disables while running) |\n| `[RelayCommand(FlowExceptionsToTaskScheduler = true)]` | async method | Surfaces exceptions via `ExecutionTask` instead of awaiting and rethrowing |\n| `[property: SomeAttr]` | observable field or `[RelayCommand]` method | Forwards `SomeAttr` onto the generated property (e.g., `[JsonIgnore]`) |\n\n**Naming.** Field `name` \u002F `_name` \u002F `m_name` → `Name`. Method `LoadAsync` →\n`LoadCommand` (the `Async` suffix is stripped; a leading `On` is also\nstripped).\n\nSee [`references\u002Fsource-generators.md`](references\u002Fsource-generators.md) for\nthe full attribute reference with generated-code samples.\n\n---\n\n## ViewModel patterns\n\n### Simple observable property\n\n```csharp\npublic partial class ContactViewModel : ObservableObject\n{\n    [ObservableProperty]\n    private string? name;\n}\n```\n\n### Hooks: `OnXxxChanging` \u002F `OnXxxChanged`\n\n```csharp\n[ObservableProperty]\nprivate string? name;\n\npartial void OnNameChanged(string? value) =>\n    Logger.LogInformation(\"Name changed to {Name}\", value);\n```\n\nBoth single-arg `(value)` and two-arg `(oldValue, newValue)` overloads\nare available. Implement only the ones you need; unimplemented hooks are\nelided by the compiler (zero runtime cost).\n\n### Dependent properties + dependent commands\n\n```csharp\n[ObservableProperty]\n[NotifyPropertyChangedFor(nameof(FullName))]\n[NotifyCanExecuteChangedFor(nameof(SaveCommand))]\nprivate string? firstName;\n\n[ObservableProperty]\n[NotifyPropertyChangedFor(nameof(FullName))]\n[NotifyCanExecuteChangedFor(nameof(SaveCommand))]\nprivate string? lastName;\n\npublic string FullName => $\"{FirstName} {LastName}\".Trim();\n```\n\n### Wrapping a non-observable model\n\n```csharp\npublic sealed class ObservableUser(User user) : ObservableObject\n{\n    public string Name\n    {\n        get => user.Name;\n        set => SetProperty(user.Name, value, user, (u, n) => u.Name = n);\n    }\n}\n```\n\nPass a static lambda (no captured state) to keep the call allocation-free.\n\n---\n\n## Commands\n\n```csharp\n[RelayCommand]\nprivate void Refresh() => Items.Reset();\n\n[RelayCommand]\nprivate async Task LoadAsync()\n{\n    foreach (var item in await service.GetItemsAsync())\n        Items.Add(item);\n}\n\n[RelayCommand(IncludeCancelCommand = true)]\nprivate async Task DownloadAsync(CancellationToken token)\n{\n    await using var stream = await http.GetStreamAsync(url, token);\n    \u002F\u002F ...\n}\n\n[RelayCommand(CanExecute = nameof(CanSave))]\nprivate Task SaveAsync() => repo.SaveAsync(Name!);\n\nprivate bool CanSave() => !string.IsNullOrWhiteSpace(Name);\n```\n\nReach for manual `RelayCommand` \u002F `AsyncRelayCommand` constructors only\nwhen you must own the command's lifetime explicitly or compose it from\nnon-trivial sources. The attribute style covers ~95% of cases.\n\nSee [`references\u002Frelaycommand-cookbook.md`](references\u002Frelaycommand-cookbook.md)\nfor sync \u002F async \u002F cancellable \u002F concurrency \u002F error-surfacing recipes.\n\n---\n\n## Base class selection\n\n| Base class | Use when |\n|------------|---------|\n| `ObservableObject` | Default. `INotifyPropertyChanged` + `INotifyPropertyChanging` + `SetProperty` overloads + `SetPropertyAndNotifyOnCompletion` for `Task` properties |\n| `ObservableValidator` | The VM needs `INotifyDataErrorInfo` (forms, settings input) |\n| `ObservableRecipient` | The VM sends or receives `IMessenger` messages — see the **`mvvm-toolkit-messenger`** skill |\n\nC# is single-inheritance: `ObservableValidator` and `ObservableRecipient`\nboth extend `ObservableObject`, so combining them requires composition\n(e.g., inject `IMessenger` into an `ObservableValidator`).\n\n---\n\n## Validation\n\n```csharp\nusing System.ComponentModel.DataAnnotations;\n\npublic sealed partial class RegistrationViewModel : ObservableValidator\n{\n    [ObservableProperty]\n    [NotifyDataErrorInfo]\n    [Required, MinLength(2), MaxLength(100)]\n    private string? name;\n\n    [ObservableProperty]\n    [NotifyDataErrorInfo]\n    [Required, EmailAddress]\n    private string? email;\n\n    [RelayCommand]\n    private void Submit()\n    {\n        ValidateAllProperties();\n        if (HasErrors) return;\n        \u002F\u002F submit...\n    }\n}\n```\n\nOther entry points: `TrySetProperty`, `ValidateProperty(value, name)`,\n`ClearAllErrors()`, `GetErrors(propertyName)`. Custom rules support\n`[CustomValidation]` methods and custom `ValidationAttribute` subclasses.\n\nSee [`references\u002Fvalidation.md`](references\u002Fvalidation.md) for the full\nvalidator surface area.\n\n---\n\n## Top pitfalls\n\n1. **Forgetting `partial`.** Class (and every enclosing type) must be\n   `partial`. Compile error `MVVMTK0008` \u002F `MVVMTK0042`.\n2. **PascalCase field name.** `[ObservableProperty] private string Name;`\n   collides with the generated property. Use `name`, `_name`, or `m_name`.\n3. **`async void` on `[RelayCommand]`.** The generator only wraps\n   `Task`-returning methods as `IAsyncRelayCommand`. `async void` becomes\n   a sync `RelayCommand` and exceptions are unobserved. Always return\n   `Task`.\n4. **Forgetting `[NotifyCanExecuteChangedFor]`.** The Save button stays\n   disabled even though `CanSave()` would now return `true`.\n5. **Mutating the same reference held by an `[ObservableProperty]`\n   field.** `EqualityComparer\u003CT>.Default` returns `true`, no notification\n   fires. Replace the instance instead of mutating it.\n\nFor the full diagnostic table (`MVVMTK0xxx`) and more pitfalls, see\n[`references\u002Ftroubleshooting.md`](references\u002Ftroubleshooting.md).\n\n---\n\n## End-to-end mini walkthrough\n\nA two-pane Notes app demonstrating generators + commands +\n`[NotifyCanExecuteChangedFor]`:\n\n```csharp\npublic sealed partial class NoteViewModel(INotesService notes,\n    IMessenger messenger) : ObservableRecipient(messenger)\n{\n    [ObservableProperty]\n    [NotifyCanExecuteChangedFor(nameof(SaveCommand))]\n    [NotifyCanExecuteChangedFor(nameof(DeleteCommand))]\n    private string? filename;\n\n    [ObservableProperty]\n    [NotifyCanExecuteChangedFor(nameof(SaveCommand))]\n    private string? text;\n\n    [RelayCommand(CanExecute = nameof(CanSave))]\n    private Task SaveAsync()\n    {\n        Messenger.Send(new NoteSavedMessage(Filename!));\n        return notes.SaveAsync(Filename!, Text!);\n    }\n\n    [RelayCommand(CanExecute = nameof(CanDelete))]\n    private Task DeleteAsync() => notes.DeleteAsync(Filename!);\n\n    private bool CanSave() =>\n        !string.IsNullOrWhiteSpace(Filename) && !string.IsNullOrEmpty(Text);\n    private bool CanDelete() => !string.IsNullOrWhiteSpace(Filename);\n}\n```\n\nFor the full sample (DI wiring, View code-behind, XAML, unit tests), see\n[`references\u002Fend-to-end-walkthrough.md`](references\u002Fend-to-end-walkthrough.md).\n\n---\n\n## References & companion skills\n\n| Topic | Where |\n|-------|-------|\n| Source generator attribute reference | [`references\u002Fsource-generators.md`](references\u002Fsource-generators.md) |\n| RelayCommand recipes | [`references\u002Frelaycommand-cookbook.md`](references\u002Frelaycommand-cookbook.md) |\n| Validation deep dive | [`references\u002Fvalidation.md`](references\u002Fvalidation.md) |\n| Full Notes-app walkthrough | [`references\u002Fend-to-end-walkthrough.md`](references\u002Fend-to-end-walkthrough.md) |\n| `MVVMTK0xxx` diagnostics & pitfalls | [`references\u002Ftroubleshooting.md`](references\u002Ftroubleshooting.md) |\n| **Messenger pub\u002Fsub** | Companion skill: **`mvvm-toolkit-messenger`** |\n| **`Microsoft.Extensions.DependencyInjection` wiring** | Companion skill: **`mvvm-toolkit-di`** |\n\nExternal sources:\n\n- Toolkit overview: \u003Chttps:\u002F\u002Flearn.microsoft.com\u002Fen-us\u002Fdotnet\u002Fcommunitytoolkit\u002Fmvvm\u002F>\n- WinUI MVVM Toolkit tutorial: \u003Chttps:\u002F\u002Flearn.microsoft.com\u002Fen-us\u002Fwindows\u002Fapps\u002Ftutorials\u002Fwinui-mvvm-toolkit\u002Fintro>\n- Source: \u003Chttps:\u002F\u002Fgithub.com\u002FCommunityToolkit\u002Fdotnet>\n- Samples: \u003Chttps:\u002F\u002Fgithub.com\u002FCommunityToolkit\u002FMVVM-Samples>\n",{"name":5,"description":7},"main","5269f7ea2d6c3a9bfda5ed878b6726cf509121fd","skills\u002Fmvvm-toolkit",5340,"https:\u002F\u002Fgithub.com\u002Fgithub\u002Fawesome-copilot","https:\u002F\u002Fawesome-copilot.github.com\u002F","https:\u002F\u002Fgithub.com\u002Fgithub","https:\u002F\u002Fgithub.com\u002Fabout","How people build software.","2026-11-11 13:41:32",{"source":8,"skillName":5,"commands":55,"prompt":65,"zipAvailable":66},[56,59,62],{"tool":57,"command":58},"npx","npx skills add github\u002Fawesome-copilot --skill mvvm-toolkit",{"tool":60,"command":61},"bunx","bunx skills add github\u002Fawesome-copilot --skill mvvm-toolkit",{"tool":63,"command":64},"pnpm","pnpm dlx skills add github\u002Fawesome-copilot --skill mvvm-toolkit","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002Fgithub\u002Fawesome-copilot\u002Fblob\u002Fmain\u002Fskills\u002Fmvvm-toolkit\u002FSKILL.md",true,[68,74,78,82,86],{"path":69,"resourceType":70,"resourceTypeDesc":71,"sizeBytes":72,"sourceHtmlUrl":73},"references\u002Fend-to-end-walkthrough.md","REFERENCE","参考文档",11394,"https:\u002F\u002Fgithub.com\u002Fgithub\u002Fawesome-copilot\u002Fblob\u002Fmain\u002Fskills\u002Fmvvm-toolkit\u002Freferences\u002Fend-to-end-walkthrough.md",{"path":75,"resourceType":70,"resourceTypeDesc":71,"sizeBytes":76,"sourceHtmlUrl":77},"references\u002Frelaycommand-cookbook.md",6887,"https:\u002F\u002Fgithub.com\u002Fgithub\u002Fawesome-copilot\u002Fblob\u002Fmain\u002Fskills\u002Fmvvm-toolkit\u002Freferences\u002Frelaycommand-cookbook.md",{"path":79,"resourceType":70,"resourceTypeDesc":71,"sizeBytes":80,"sourceHtmlUrl":81},"references\u002Fsource-generators.md",9534,"https:\u002F\u002Fgithub.com\u002Fgithub\u002Fawesome-copilot\u002Fblob\u002Fmain\u002Fskills\u002Fmvvm-toolkit\u002Freferences\u002Fsource-generators.md",{"path":83,"resourceType":70,"resourceTypeDesc":71,"sizeBytes":84,"sourceHtmlUrl":85},"references\u002Ftroubleshooting.md",7171,"https:\u002F\u002Fgithub.com\u002Fgithub\u002Fawesome-copilot\u002Fblob\u002Fmain\u002Fskills\u002Fmvvm-toolkit\u002Freferences\u002Ftroubleshooting.md",{"path":87,"resourceType":70,"resourceTypeDesc":71,"sizeBytes":88,"sourceHtmlUrl":89},"references\u002Fvalidation.md",5840,"https:\u002F\u002Fgithub.com\u002Fgithub\u002Fawesome-copilot\u002Fblob\u002Fmain\u002Fskills\u002Fmvvm-toolkit\u002Freferences\u002Fvalidation.md",[91,126,153,183,217,234],{"skillNo":92,"name":93,"nameZh":6,"description":94,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":95,"categories":96,"keywords":105,"publishedAt":39},"01M70KXZNX9ES38N2A8M58ZZMZ","supabase-rls-policies","Write, review, and test Supabase Row Level Security (RLS) policies so that one user cannot read or change another user's rows. Use when creating or changing tables, policies, views, or security definer functions in a Supabase project, when reviewing a migration that touches RLS, or when asked to prove user isolation with pgTAP tests that sign in as two users.","https:\u002F\u002Fgithub.com\u002Fgithub\u002Fawesome-copilot\u002Fblob\u002Fmain\u002Fskills\u002Fsupabase-rls-policies\u002FSKILL.md",[97,102],{"categoryNo":98,"categoryName":99,"taxonomyType":100,"taxonomyTypeDesc":101},"01JBSEEDCAT000000000000008","测试与质量","SCENARIO","使用场景",{"categoryNo":103,"categoryName":104,"taxonomyType":100,"taxonomyTypeDesc":101},"01JBSEEDCAT000000000000023","Skill与智能体开发",[106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125],"supabase","rls","supabase rls","supabase rls policies","project","when","review","change","read","write","security","isolation","tables","as","tests","prove","use","users","functions","with",{"skillNo":127,"name":128,"nameZh":6,"description":129,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":130,"categories":131,"keywords":133,"publishedAt":39},"01M70KWA5YWXDV6V8DZ19E3T2Y","skill-distribution-audit","Audit a skill collection before release or investigate installer omissions, archived\u002Ftemplate leakage, duplicate identities, and manifest drift. Reconcile the intended public source set, distribution manifest, and actual discovery output for a specified consumer version. Includes a deterministic set-comparison helper.","https:\u002F\u002Fgithub.com\u002Fgithub\u002Fawesome-copilot\u002Fblob\u002Fmain\u002Fskills\u002Fskill-distribution-audit\u002FSKILL.md",[132],{"categoryNo":103,"categoryName":104,"taxonomyType":100,"taxonomyTypeDesc":101},[134,135,136,137,138,139,26,140,141,142,143,144,145,146,147,148,149,150,151,152],"distribution","skill distribution audit","audit","SET","deterministic","installer","discovery","comparison","helper","manifest","version","collection","drift","output","investigate","duplicate","template","release","reconcile",{"skillNo":154,"name":155,"nameZh":6,"description":156,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":157,"sourceHtmlUrl":158,"categories":159,"keywords":165,"publishedAt":39},"01M70KCJPFVW7PPQ49CC5R8JGK","logicapps-custom-code-net10-migration","Migrate Azure Logic Apps Standard local-function custom-code projects from .NET 8 to .NET 10. Use for legacy Logic Apps custom-code workspace upgrades, project\u002Fpackage configuration, custom-worker settings, publication, and integrated VS Code debugging. Not for ordinary .NET applications, standalone Azure Functions, Consumption workflows, or cloud deployment.","https:\u002F\u002Fgithub.com\u002Fgithub\u002Fawesome-copilot\u002Fblob\u002Fmain\u002Fskills\u002Flogicapps-custom-code-net10-migration\u002FLICENSE","https:\u002F\u002Fgithub.com\u002Fgithub\u002Fawesome-copilot\u002Fblob\u002Fmain\u002Fskills\u002Flogicapps-custom-code-net10-migration\u002FSKILL.md",[160],{"categoryNo":161,"categoryName":162,"taxonomyType":163,"taxonomyTypeDesc":164},"01JBSEEDCAT000000000000030","开发工程师","PROFESSION","适用职业",[166,167,168,169,170,171,26,110,172,173,174,175,176,177,178,122,179,180,181,182],"custom","code","logicapps custom code net10 migration","migration","projects","standalone","deployment","applications","local","azure","worker","upgrades","package","apps","Legacy","debugging","workflows",{"skillNo":184,"name":185,"nameZh":6,"description":186,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":187,"sourceHtmlUrl":188,"categories":189,"keywords":194,"publishedAt":39},"01M6AEBN37RQMXJ3298PEY7PG1","winmd-api-search","Find and explore Windows desktop APIs. Use when building features that need platform capabilities — camera, file access, notifications, UI controls, AI\u002FML, sensors, networking, etc. Discovers the right API for a task and retrieves full type details (methods, properties, events, enumeration values).","https:\u002F\u002Fgithub.com\u002Fgithub\u002Fawesome-copilot\u002Fblob\u002Fmain\u002Fskills\u002Fwinmd-api-search\u002FLICENSE.txt","https:\u002F\u002Fgithub.com\u002Fgithub\u002Fawesome-copilot\u002Fblob\u002Fmain\u002Fskills\u002Fwinmd-api-search\u002FSKILL.md",[190,193],{"categoryNo":191,"categoryName":192,"taxonomyType":100,"taxonomyTypeDesc":101},"01JBSEEDCAT000000000000010","后端与接口",{"categoryNo":161,"categoryName":162,"taxonomyType":163,"taxonomyTypeDesc":164},[195,196,197,198,199,200,201,202,203,204,205,206,207,208,209,111,210,211,212,213,214,26,215,216],"winmd api search","search","api","windows","desktop","file access","enumeration","ui","ml","file","networking","capabilities","notifications","explore","access","features","full","camera","events","platform","methods","controls",{"skillNo":218,"name":219,"nameZh":6,"description":220,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":221,"licenseFileUrl":222,"sourceHtmlUrl":223,"categories":224,"keywords":230,"publishedAt":39},"01M6AE571R3SC1ZGE202B3NB5S","resemble-detect","Deepfake detection and media safety — detect AI-generated audio, images, video, and text, trace synthesis sources, apply watermarks, verify speaker identity, and analyze media intelligence using Resemble AI","Apache-2.0","https:\u002F\u002Fgithub.com\u002Fgithub\u002Fawesome-copilot\u002Fblob\u002Fmain\u002Fskills\u002Fresemble-detect\u002FLICENSE","https:\u002F\u002Fgithub.com\u002Fgithub\u002Fawesome-copilot\u002Fblob\u002Fmain\u002Fskills\u002Fresemble-detect\u002FSKILL.md",[225,226,229],{"categoryNo":191,"categoryName":192,"taxonomyType":100,"taxonomyTypeDesc":101},{"categoryNo":227,"categoryName":228,"taxonomyType":100,"taxonomyTypeDesc":101},"01JBSEEDCAT000000000000016","图像与多媒体",{"categoryNo":103,"categoryName":104,"taxonomyType":100,"taxonomyTypeDesc":101},[231,137,204,232,233],"resemble detect","https","identity",{"skillNo":235,"name":236,"nameZh":6,"description":237,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":221,"licenseFileUrl":238,"sourceHtmlUrl":239,"categories":240,"keywords":245,"publishedAt":39},"01M6AE30BSK2HCY4TQQ22Z8JRD","quality-playbook","Run a complete quality engineering audit on any codebase. Derives behavioral requirements from the code, generates spec-traced functional tests, runs a three-pass code review with regression tests, executes a multi-model spec audit (Council of Three), and produces a consolidated bug report with TDD-verified patches. Finds the 35% of real defects that structural code review alone cannot catch. Works with any language. Trigger on 'quality playbook', 'spec audit', 'Council of Three', 'fitness-to-purpose', or 'coverage theater'.","https:\u002F\u002Fgithub.com\u002Fgithub\u002Fawesome-copilot\u002Fblob\u002Fmain\u002Fskills\u002Fquality-playbook\u002FLICENSE.txt","https:\u002F\u002Fgithub.com\u002Fgithub\u002Fawesome-copilot\u002Fblob\u002Fmain\u002Fskills\u002Fquality-playbook\u002FSKILL.md",[241,244],{"categoryNo":242,"categoryName":243,"taxonomyType":100,"taxonomyTypeDesc":101},"01JBSEEDCAT000000000000007","代码审查",{"categoryNo":103,"categoryName":104,"taxonomyType":100,"taxonomyTypeDesc":101},[246,247,248,249,167,112,9,136,250,251,232,252,253,254,255,256,257,258,259,260,261,262,120,263,264,265,266],"playbook","quality playbook","quality","code review","tdd","run","report","spec","bug report","council","engineering","functional","spec audit","bug","verified","multi-model","three","coverage","model","regression","structural"]