Skip to main content
skill GitHub Copilot official · GitHub

mvvm-toolkit — GitHub Copilot skill: what it does, how to install

Guides GitHub Copilot to scaffold ViewModels, source generator attributes, commands, and validation for CommunityToolkit.Mvvm.

Install into GitHub Copilot · Skills CLI (Vercel) All methods and platforms ↓
npx skills add github/awesome-copilot --skill mvvm-toolkit
Open on GitHub

What mvvm-toolkit does

This skill guides the assistant when writing C# ViewModels backed by CommunityToolkit.Mvvm. It instructs Copilot to place [ObservableProperty] on private fields and [RelayCommand] on methods. It also configures dependent notifications like [NotifyPropertyChangedFor] and [NotifyCanExecuteChangedFor]. Classes must be partial. Missing the partial modifier triggers generator diagnostic errors.

The skill relies on base classes like ObservableObject and ObservableValidator. It also covers cancellation tokens on asynchronous commands. If your project avoids source generators or uses a different MVVM framework, you do not need this skill. For XAML developers on WPF, MAUI, or Avalonia, it cuts boilerplate code immediately.

What it helps with

  • generate observable properties from private fields with source generators
  • create asynchronous relay commands with cancellation tokens
  • wire property change notifications to dependent properties and commands
  • configure model validation rules using ObservableValidator

Example prompts for the agent

  • Create a partial ContactViewModel with an observable Name property
  • Add a LoadCommand that runs asynchronously with a CancellationToken
  • Set up an ObservableValidator with required fields for user registration

How to install mvvm-toolkit

1Skills CLI (Vercel)

npx skills add github/awesome-copilot --skill mvvm-toolkit

2GitHub CLI

gh skill install github/awesome-copilot mvvm-toolkit

3Manually: put the files in place

.github/skills/mvvm-toolkit/SKILL.md

From the same set554

The whole set →
git-commit official github This skill reviews Git changes, stages related files, and creates Conventional Commits. Use it when you want an agent to make one commit or split work into separate commits. GitHub Copilot 47,165installs excalidraw-diagram-generator official github Turn a plain-language description into an Excalidraw diagram. Useful for mapping processes, system structure, and relationships between entities. GitHub Copilot 31,639installs multi-stage-dockerfile official github This skill guides GitHub Copilot in writing multi-stage Dockerfiles for different languages and frameworks. Use it when the final runtime image should contain fewer build dependencies. GitHub Copilot 29,688installs documentation-writer official github Skill that turns the agent into a technical writer following the Diátaxis framework: tutorials, how-to guides, reference, and explanation. Proposes structure before writing. GitHub Copilot 27,757installs prd official github Turns a feature idea into a structured PRD with user stories, technical requirements, and risks. Use it when planning a software feature or an AI-powered one. GitHub Copilot 24,035installs refactor official github This skill reshapes existing code without changing its external behavior. Use it for gradual cleanup of large functions, duplication, weak types, and code smells. GitHub Copilot 22,321installs java-springboot official github Gives GitHub Copilot Spring Boot development practices, from project layout to tests and security. Use it when writing or reviewing Java application code. GitHub Copilot 21,272installs create-readme official github This skill reviews a project and creates a structured README.md. Use it when the repository needs clear documentation based on its actual files and code. GitHub Copilot 19,081installs

Similar

landing Alireza Rezvani This skill creates a single-page HTML landing page with 3D CSS animation, GSAP scroll effects, and mouse parallax. It collects product and brand details before generating the page. Claude Code 404installs frontend-app-builder official OpenAI This skill designs and builds frontend apps, websites, and games, then checks the result in a browser. Use it for a new interface or an explicit redesign request. CX Codex GP ChatGPT 252installs frontend-design official anthropics This skill guides intentional visual design for new interfaces and redesigns. Use it when a generic generated UI is not enough. Claude Code Claude.ai 1,025,790installs design-taste-frontend Leonxlnx This skill directs the design of public pages and work screens, then checks the result in a browser through screenshots. Use it when an interface feels generic or unfinished. Claude Code CX Codex Cursor +1 673,100installs prototype Matt Pocock Creates throwaway prototypes for checking state logic or exploring UI before committing the decision to the main codebase. Claude Code 986,201installs hyperframes heygen-com Instructs coding agents on creating, validating, and rendering HTML-based videos using the HyperFrames framework and CLI. Claude Code CX Codex Cursor +1 1,200,000installs hyperframes-cli heygen-com Controls HyperFrames video workflows from the CLI: audits layouts and contrast, edits timeline clips, captures snapshots, and renders locally or in cloud runtimes. Claude Code CX Codex Cursor +1 1,200,000installs webapp-testing official anthropics This skill tests local web apps with Playwright, helping agents debug UI behavior, capture screenshots, and inspect browser logs. Claude Code Claude.ai 186,758installs

Questions about mvvm-toolkit

How do I install mvvm-toolkit in GitHub Copilot?

Run gh skill install github/awesome-copilot mvvm-toolkit or add .github/skills/mvvm-toolkit/SKILL.md to your repository.

What does the mvvm-toolkit skill do?

It provides patterns and prompts for CommunityToolkit.Mvvm, ensuring Copilot uses source generators and partial classes correctly.

Why must classes using ObservableProperty be marked partial?

The toolkit source generators produce the property implementations in separate code files. Without the partial keyword, the compiler fails with diagnostic errors.

Source text

SKILL.md · MIT
---
name: mvvm-toolkit
description: 'CommunityToolkit.Mvvm (the MVVM Toolkit) core: source generators ([ObservableProperty], [RelayCommand], [NotifyPropertyChangedFor], [NotifyCanExecuteChangedFor], [NotifyDataErrorInfo]), base classes (ObservableObject / ObservableValidator / ObservableRecipient), commands (RelayCommand / AsyncRelayCommand), and validation. Companion skills: mvvm-toolkit-messenger for pub/sub, mvvm-toolkit-di for Microsoft.Extensions.DependencyInjection wiring. Works across WPF, WinUI 3, MAUI, Uno, and Avalonia.'
---

# CommunityToolkit.Mvvm (core)

Use this skill when authoring or reviewing ViewModels, properties,
commands, or validation in apps that use `CommunityToolkit.Mvvm` 8.x.

> **Companion skills.** Load **`mvvm-toolkit-messenger`** for `IMessenger`
> pub/sub patterns. Load **`mvvm-toolkit-di`** for
> `Microsoft.Extensions.DependencyInjection` integration.

> **Quick recap.** `[ObservableProperty]` on private fields in `partial`
> classes; `[RelayCommand]` on instance methods; inherit from
> `ObservableObject` (or `ObservableValidator` for input forms,
> `ObservableRecipient` when using `IMessenger`).

---

## Package & setup

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

Targets: `netstandard2.0`, `netstandard2.1`, `net6.0`+. Works on .NET, .NET
Framework, Mono. Source generators ship in the same NuGet — no extra
analyzer reference required.

Namespaces:

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

> **Universal rule.** Every type that uses `[ObservableProperty]` or
> `[RelayCommand]` — and every enclosing type, if nested — must be
> declared `partial`. Without it, the generators emit
> `MVVMTK0008` / `MVVMTK0042`.

---

## Source generators cheat sheet

| Attribute | Applied to | Generates |
|-----------|-----------|-----------|
| `[ObservableProperty]` | private field | Public `INotifyPropertyChanged` property + `OnXxxChanging`/`OnXxxChanged` partial-method hooks |
| `[NotifyPropertyChangedFor(nameof(Other))]` | observable field | Also raises `PropertyChanged` for the listed property |
| `[NotifyCanExecuteChangedFor(nameof(MyCommand))]` | observable field | Calls `MyCommand.NotifyCanExecuteChanged()` on change |
| `[NotifyDataErrorInfo]` | observable field on `ObservableValidator` | Calls `ValidateProperty(value)` from the setter |
| `[NotifyPropertyChangedRecipients]` | observable field on `ObservableRecipient` | `Broadcast(old, new)` after the change |
| `[RelayCommand]` | instance method | Lazy `RelayCommand` / `AsyncRelayCommand` exposed as `IRelayCommand` / `IAsyncRelayCommand` |
| `[RelayCommand(CanExecute = nameof(CanX))]` | instance method | Wires `CanExecute` to a method or property |
| `[RelayCommand(IncludeCancelCommand = true)]` | async method with `CancellationToken` | Also generates `XxxCancelCommand` |
| `[RelayCommand(AllowConcurrentExecutions = true)]` | async method | Allows queued/parallel invocations (default disables while running) |
| `[RelayCommand(FlowExceptionsToTaskScheduler = true)]` | async method | Surfaces exceptions via `ExecutionTask` instead of awaiting and rethrowing |
| `[property: SomeAttr]` | observable field or `[RelayCommand]` method | Forwards `SomeAttr` onto the generated property (e.g., `[JsonIgnore]`) |

**Naming.** Field `name` / `_name` / `m_name` → `Name`. Method `LoadAsync` →
`LoadCommand` (the `Async` suffix is stripped; a leading `On` is also
stripped).

See [`references/source-generators.md`](references/source-generators.md) for
the full attribute reference with generated-code samples.

---

## ViewModel patterns

### Simple observable property

```csharp
public partial class ContactViewModel : ObservableObject
{
    [ObservableProperty]
    private string? name;
}
```

### Hooks: `OnXxxChanging` / `OnXxxChanged`

```csharp
[ObservableProperty]
private string? name;

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

Both single-arg `(value)` and two-arg `(oldValue, newValue)` overloads
are available. Implement only the ones you need; unimplemented hooks are
elided by the compiler (zero runtime cost).

### Dependent properties + dependent commands

```csharp
[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();
```

### Wrapping a non-observable model

```csharp
public sealed class ObservableUser(User user) : ObservableObject
{
    public string Name
    {
        get => user.Name;
        set => SetProperty(user.Name, value, user, (u, n) => u.Name = n);
    }
}
```

Pass a static lambda (no captured state) to keep the call allocation-free.

---

## Commands

```csharp
[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);
```

Reach for manual `RelayCommand` / `AsyncRelayCommand` constructors only
when you must own the command's lifetime explicitly or compose it from
non-trivial sources. The attribute style covers ~95% of cases.

See [`references/relaycommand-cookbook.md`](references/relaycommand-cookbook.md)
for sync / async / cancellable / concurrency / error-surfacing recipes.

---

## Base class selection

| Base class | Use when |
|------------|---------|
| `ObservableObject` | Default. `INotifyPropertyChanged` + `INotifyPropertyChanging` + `SetProperty` overloads + `SetPropertyAndNotifyOnCompletion` for `Task` properties |
| `ObservableValidator` | The VM needs `INotifyDataErrorInfo` (forms, settings input) |
| `ObservableRecipient` | The VM sends or receives `IMessenger` messages — see the **`mvvm-toolkit-messenger`** skill |

C# is single-inheritance: `ObservableValidator` and `ObservableRecipient`
both extend `ObservableObject`, so combining them requires composition
(e.g., inject `IMessenger` into an `ObservableValidator`).

---

## Validation

```csharp
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...
    }
}
```

Other entry points: `TrySetProperty`, `ValidateProperty(value, name)`,
`ClearAllErrors()`, `GetErrors(propertyName)`. Custom rules support
`[CustomValidation]` methods and custom `ValidationAttribute` subclasses.

See [`references/validation.md`](references/validation.md) for the full
validator surface area.

---

## Top pitfalls

1. **Forgetting `partial`.** Class (and every enclosing type) must be
   `partial`. Compile error `MVVMTK0008` / `MVVMTK0042`.
2. **PascalCase field name.** `[ObservableProperty] private string Name;`
   collides with the generated property. Use `name`, `_name`, or `m_name`.
3. **`async void` on `[RelayCommand]`.** The generator only wraps
   `Task`-returning methods as `IAsyncRelayCommand`. `async void` becomes
   a sync `RelayCommand` and exceptions are unobserved. Always return
   `Task`.
4. **Forgetting `[NotifyCanExecuteChangedFor]`.** The Save button stays
   disabled even though `CanSave()` would now return `true`.
5. **Mutating the same reference held by an `[ObservableProperty]`
   field.** `EqualityComparer<T>.Default` returns `true`, no notification
   fires. Replace the instance instead of mutating it.

For the full diagnostic table (`MVVMTK0xxx`) and more pitfalls, see
[`references/troubleshooting.md`](references/troubleshooting.md).

---

## End-to-end mini walkthrough

A two-pane Notes app demonstrating generators + commands +
`[NotifyCanExecuteChangedFor]`:

```csharp
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);
}
```

For the full sample (DI wiring, View code-behind, XAML, unit tests), see
[`references/end-to-end-walkthrough.md`](references/end-to-end-walkthrough.md).

---

## References & companion skills

| Topic | Where |
|-------|-------|
| Source generator attribute reference | [`references/source-generators.md`](references/source-generators.md) |
| RelayCommand recipes | [`references/relaycommand-cookbook.md`](references/relaycommand-cookbook.md) |
| Validation deep dive | [`references/validation.md`](references/validation.md) |
| Full Notes-app walkthrough | [`references/end-to-end-walkthrough.md`](references/end-to-end-walkthrough.md) |
| `MVVMTK0xxx` diagnostics & pitfalls | [`references/troubleshooting.md`](references/troubleshooting.md) |
| **Messenger pub/sub** | Companion skill: **`mvvm-toolkit-messenger`** |
| **`Microsoft.Extensions.DependencyInjection` wiring** | Companion skill: **`mvvm-toolkit-di`** |

External sources:

- Toolkit overview: <https://learn.microsoft.com/en-us/dotnet/communitytoolkit/mvvm/>
- WinUI MVVM Toolkit tutorial: <https://learn.microsoft.com/en-us/windows/apps/tutorials/winui-mvvm-toolkit/intro>
- Source: <https://github.com/CommunityToolkit/dotnet>
- Samples: <https://github.com/CommunityToolkit/MVVM-Samples>

© github · MIT · github/awesome-copilot

See also

Project sponsors

Companies that keep this analytics open
Gold sponsor

Impressions, clicks and code copies — open statistics. We keep the numbers public for the same reason NeBlask itself is public. Become a sponsor