К основному содержанию
правило официальный · GitHub

mvvm-toolkit — правило для GitHub Copilot: что делает и как установить

Задаёт для GitHub Copilot правила работы с CommunityToolkit.Mvvm 8.x в проектах на WPF, WinUI 3, .NET MAUI и Avalonia. Помогает писать генераторы свойств, команды и валидацию без рутины.

Установить в GitHub Copilot · Вручную: положить файлы Все способы и платформы ↓
.github/instructions/mvvm-toolkit.instructions.md
Открыть на GitHub

Что делает mvvm-toolkit

Правило направляет GitHub Copilot при работе с файлами .cs, .xaml и .csproj. Оно требует подключать пакет CommunityToolkit.Mvvm версии 8.x или новее и запрещает устаревший Microsoft.Toolkit.Mvvm. Все классы со свойствами через [ObservableProperty] должны быть объявлены как partial. Атрибут ставится только на приватные поля в нижнем регистре, чтобы генератор сам создал публичное свойство.

Для команд используется атрибут [RelayCommand]. Методы команд обязаны возвращать void или Task, потому что async void скрывает исключения от среды выполнения. Для асинхронных операций правило предписывает передавать CancellationToken и отключать одновременные запуски. Без поддержки source generators в компиляторе C# кодогенерация полей и команд не сработает.

Архитектурные правила исключают антипаттерны в DI и мессенджере. Сервисы и ViewModel внедряются строго через конструктор, вызовы Ioc.Default.GetService<T>() запрещены. Для обмена сообщениями Copilot выбирает WeakReferenceMessenger со статическими лямбдами, предотвращая утечки памяти. Валидация строится на ObservableValidator с вызовом ValidateAllProperties() перед отправкой данных.

Какие задачи решает

  • сгенерировать свойства через [ObservableProperty] без бойлерплейта
  • объявить асинхронные команды с отменой по CancellationToken
  • настроить слабую связь между ViewModel через WeakReferenceMessenger
  • валидировать пользовательский ввод через ObservableValidator

Примеры запросов агенту

  • Создай ViewModel формы редактирования профиля с проверкой полей через ObservableValidator
  • Добавь асинхронную команду сохранения с CancellationToken и поддержкой отмены
  • Настрой отправку сообщения об обновлении записи через WeakReferenceMessenger

Как установить mvvm-toolkit

Вручную: положить файлы

.github/instructions/mvvm-toolkit.instructions.md

Поле applyTo задаёт, к каким файлам применять

По шагам, с командами для каждой платформы: AGENTS.md и CLAUDE.md: что это, кто читает и какой выбрать

Вопросы о mvvm-toolkit

Как установить mvvm-toolkit в GitHub Copilot?

Создайте файл .github/instructions/mvvm-toolkit.instructions.md в корне репозитория и сохраните в него текст правила.

Что делает правило mvvm-toolkit?

Оно задаёт Copilot шаблоны генерации кода для библиотеки CommunityToolkit.Mvvm 8.x и новее. Модель учится объявлять partial-классы, генерировать свойства из приватных полей и связывать команды без ручного бойлерплейта.

Можно ли наследовать ViewModel одновременно от ObservableValidator и ObservableRecipient?

Нет, множественное наследование классов в C# запрещено. Правило требует выбрать один базовый класс, а вторую функциональность подключить через композицию — например, передать IMessenger в конструктор.

Исходный текст

mvvm-toolkit.instructions.md · MIT
---
description: 'CommunityToolkit.Mvvm (MVVM Toolkit) coding conventions for ViewModels, commands, messaging, validation, and DI across WPF, WinUI 3, .NET MAUI, Uno Platform, and Avalonia.'
applyTo: '**/*.cs, **/*.xaml, **/*.csproj'
---

# CommunityToolkit.Mvvm (MVVM Toolkit)

These rules apply whenever a project references `CommunityToolkit.Mvvm`.
For deep reference and end-to-end examples, load the `mvvm-toolkit` skill.

## Package & language

- Reference `CommunityToolkit.Mvvm` 8.x (or newer) in `.csproj`. Do not
  install the legacy `Microsoft.Toolkit.Mvvm` (7.x) for new projects.
- C# `LangVersion` must support source generators (default in modern SDKs).

## ViewModel base class

- Inherit ViewModels from `ObservableObject` by default.
- Use `ObservableValidator` only when the ViewModel needs
  `INotifyDataErrorInfo` (forms, settings, input validation).
- Use `ObservableRecipient` only when the ViewModel sends or receives
  `IMessenger` messages.
- Never hand-implement `INotifyPropertyChanged` when one of the toolkit
  base classes can be used. If the type cannot inherit from a toolkit base
  (e.g., a custom control), apply the class-level `[ObservableObject]` or
  `[INotifyPropertyChanged]` attribute instead.

## Properties

- Declare every type that uses `[ObservableProperty]` as `partial` (and
  every enclosing type, if nested).
- Apply `[ObservableProperty]` to private fields named `name`, `_name`, or
  `m_name` — never PascalCase. Let the generator emit the public property.
- Do not write manual `SetProperty(ref field, value)` boilerplate when the
  field qualifies for `[ObservableProperty]`.
- Use `[NotifyPropertyChangedFor(nameof(Derived))]` to raise change
  notifications for derived/computed properties.
- Use `[NotifyCanExecuteChangedFor(nameof(XxxCommand))]` so commands
  re-evaluate `CanExecute` when their inputs change.
- Implement `OnXxxChanging` / `OnXxxChanged` partial-method hooks for
  side-effects on property changes — do not subscribe to your own
  `PropertyChanged` event.
- Use `[property: SomeAttribute]` to forward an attribute (e.g.,
  `[JsonIgnore]`, `[JsonPropertyName(...)]`) onto the generated property.

## Commands

- Use `[RelayCommand]` on instance methods over manually constructed
  `RelayCommand` / `AsyncRelayCommand` instances.
- `[RelayCommand]` methods must return `void` or `Task` (or `Task<T>`).
  Never use `async void` — exceptions become unobserved.
- For cancellable async work, declare a `CancellationToken` parameter and
  optionally set `IncludeCancelCommand = true` to expose a paired
  `XxxCancelCommand`.
- Use `CanExecute = nameof(...)` plus `[NotifyCanExecuteChangedFor]` on the
  inputs to keep button enable/disable state in sync.
- Default `AllowConcurrentExecutions` to `false` (the default). Only set
  `true` when overlapping invocations are explicitly safe and intended.
- Default error policy is await-and-rethrow. Only set
  `FlowExceptionsToTaskScheduler = true` when the UI binds to
  `ExecutionTask` to render error states.

## Messaging

- Default to `WeakReferenceMessenger.Default`. Only switch to
  `StrongReferenceMessenger.Default` when profiling shows the messenger is
  hot, and document the lifetime guarantees.
- Register handlers with the `(recipient, message)` lambda form using the
  `static` modifier — never capture `this` in the lambda.
- Prefer `IRecipient<TMessage>` interfaces on `ObservableRecipient`
  ViewModels so `RegisterAll(this)` wires everything automatically when
  `IsActive = true`.
- Set `IsActive = true` on activation (e.g., `OnNavigatedTo`) and
  `IsActive = false` on deactivation (e.g., `OnNavigatedFrom`).
- Inheritance is not considered when delivering messages — register each
  concrete message type explicitly.
- Use channel tokens (the `int` / `string` / `Guid` overloads) to scope
  messages to a sub-system or window when more than one consumer would
  otherwise collide.

## Dependency injection

- Use `Microsoft.Extensions.DependencyInjection` for service and ViewModel
  registration. Prefer the .NET Generic Host
  (`Host.CreateDefaultBuilder()`) so configuration, logging, and scope
  validation are wired automatically.
- Register services and ViewModels in the composition root (typically
  `App.xaml.cs`). Resolve the page's root ViewModel from DI in the page
  constructor or via the navigation framework.
- Inject services and child ViewModels through constructors. Do not call
  `Ioc.Default.GetService<T>()` from inside ViewModels, services, or any
  type the DI container can construct.
- Lifetimes:
  - `AddSingleton<T>()` — shell/main-window VMs, settings, file/HTTP
    services, the shared `IMessenger`.
  - `AddTransient<T>()` — per-page or per-document VMs.
  - `AddScoped<T>()` — only with explicit `IServiceScope` usage; rarely
    needed in client apps.
- Register `IMessenger` once
  (`services.AddSingleton<IMessenger>(WeakReferenceMessenger.Default)`)
  and inject it via `ObservableRecipient(messenger)` constructors.

## Validation

- Use `ObservableValidator` plus `[NotifyDataErrorInfo]` and DataAnnotation
  attributes (`[Required]`, `[Range]`, `[EmailAddress]`, `[MinLength]`,
  `[MaxLength]`, `[CustomValidation]`).
- Call `ValidateAllProperties()` before submitting a form; check
  `HasErrors` and bail out if `true`.
- Reset error state with `ClearAllErrors()` after a successful submit or
  when resetting a form.
- For cross-property rules, call `ValidateProperty(value, nameof(Other))`
  from the changed property's `OnXxxChanged` hook.

## XAML

- For WinUI 3 / UWP, prefer `{x:Bind}` (compiled bindings) over
  `{Binding}`. Set `Mode=OneWay` or `Mode=TwoWay` explicitly — `{x:Bind}`
  defaults to `OneTime`.
- Bind `Command="{x:Bind ViewModel.SaveCommand}"` directly to the
  generated command property.
- Bind async-command status (`IsRunning`, `ExecutionTask.Status`,
  `ExecutionTask.Exception`) to surface progress/errors instead of
  blocking the UI thread.

## Things to avoid

- `[ObservableProperty] private string Name;` — PascalCase field collides
  with the generated property; use lowerCamel.
- Manual `RaisePropertyChanged(nameof(X))` calls alongside
  `[ObservableProperty]` — produces duplicate notifications.
- `Ioc.Default.GetService<T>()` from inside a ViewModel constructor —
  hides dependencies, breaks unit tests.
- `StrongReferenceMessenger` without `OnDeactivated` / `UnregisterAll` —
  pins recipients and leaks them.
- Capturing `this` in messenger lambdas — closure allocation and
  lifetime confusion. Always use `(r, m) => r.OnX(m)` with `static`.
- `async void` on `[RelayCommand]` methods — return `Task` instead.
- Mutating the same reference held by an `[ObservableProperty]` field —
  the equality comparer returns `true` and no change notification fires.
  Replace the instance instead.
- Inheriting from both `ObservableValidator` and `ObservableRecipient` —
  not possible; use composition (inject `IMessenger` or implement
  validation manually).

© github · MIT · github/awesome-copilot

Похожие правила IDE

Все правила IDE по теме «Фронтенд и UI» →

Из того же набора861

Весь набор →
no-heredoc Правило запрещает агенту писать файлы через heredoc и перенаправления в терминале, потому что в VS Code такие команды портят содержимое. ★ 40 418звёзд репо nextjs Задаёт стандарты разработки на Next.js 16 с App Router. Предотвращает ошибки с разделением серверных и клиентских компонентов и асинхронными API. ★ 40 418звёзд репо nextjs-tailwind Правило задаёт стандарты для проектов на Next.js, TypeScript и Tailwind CSS. Оно пригодится, когда вы просите Copilot создавать или менять компоненты и маршруты. ★ 40 418звёзд репо nestjs Правило подключает к GitHub Copilot набор стандартов NestJS: модули, DI, DTO и валидация. Пригодится, когда агент пишет серверный код на TypeScript. ★ 40 418звёзд репо ms-sql-dba Правило задаёт поведение GitHub Copilot в режиме чата ms-sql-dba.agent.md: административные задачи Microsoft SQL Server вместо разбора кода. ★ 40 418звёзд репо moodle Правило задаёт Copilot стандарты Moodle: Frankenstyle-неймспейсы, Mustache-шаблоны, обязательные функции безопасности. Пригодится, если пишете плагины и темы. ★ 40 418звёзд репо mongo-dba Настраивает GitHub Copilot для роли администратора MongoDB: помогает управлять кластерами, настраивать индексы, бэкапы и безопасность с учётом MongoDB 7.x+. ★ 40 418звёзд репо microsoft-foundry Правило направляет GitHub Copilot при создании агентов Microsoft Foundry на Python SDK v2 и предотвращает генерацию устаревшего API. ★ 40 418звёзд репо

Спонсоры проекта

Компании, которые помогают держать аналитику открытой