Skip to main content
ClaudeWave
Skill429 estrellas del repoactualizado 10d ago

mvvm

This Claude Code skill implements the Model-View-ViewModel pattern for .NET applications using the MVVM Toolkit library. Use it when building UI-separated architectures that require observable properties, relay commands, dependency injection, and unit-testable ViewModels without business logic in code-behind.

Instalar en Claude Code
Copiar
git clone --depth 1 https://github.com/managedcode/dotnet-skills /tmp/mvvm && cp -r /tmp/mvvm/catalog/Libraries/MVVM-Toolkit/skills/mvvm ~/.claude/skills/mvvm
Después abre una sesión nueva de Claude Code; el skill carga automáticamente.

SKILL.md

# MVVM Pattern for .NET

## Trigger On

- implementing UI separation with Model-View-ViewModel
- using MVVM Toolkit (CommunityToolkit.Mvvm) for ViewModels
- designing testable UI architecture
- handling commands, property changes, and messaging
- choosing between MVVM frameworks

## Documentation

- [MVVM Toolkit Overview](https://learn.microsoft.com/en-us/dotnet/communitytoolkit/mvvm/)
- [ObservableObject](https://learn.microsoft.com/en-us/dotnet/communitytoolkit/mvvm/observableobject)
- [RelayCommand](https://learn.microsoft.com/en-us/dotnet/communitytoolkit/mvvm/relaycommand)
- [Messenger](https://learn.microsoft.com/en-us/dotnet/communitytoolkit/mvvm/messenger)
- [Source Generators](https://learn.microsoft.com/en-us/dotnet/communitytoolkit/mvvm/generators/overview)

## References

See detailed examples in the `references/` folder:
- [`patterns.md`](references/patterns.md) — ViewModel, command, navigation, and state patterns
- [`anti-patterns.md`](references/anti-patterns.md) — Common mistakes and how to fix them

## Core Concepts

| Component | Responsibility | Example |
|-----------|---------------|---------|
| **Model** | Business logic and data | `Product`, `Order`, `User` |
| **View** | UI presentation (XAML/Razor) | `ProductPage.xaml` |
| **ViewModel** | UI logic and state | `ProductViewModel` |

## Workflow

1. **Keep Views dumb** — no business logic in code-behind
2. **Use data binding** — connect View to ViewModel properties
3. **Commands for actions** — handle user interactions via ICommand
4. **Inject dependencies** — services go into ViewModel constructors
5. **Test ViewModels** — they should be unit testable without UI

## MVVM Toolkit Setup

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

## ViewModel Patterns

### Basic ViewModel with Source Generators
```csharp
public partial class ProductViewModel(IProductService productService) : ObservableObject
{
    [ObservableProperty]
    private string _name = string.Empty;

    [ObservableProperty]
    private decimal _price;

    [ObservableProperty]
    [NotifyCanExecuteChangedFor(nameof(SaveCommand))]
    private bool _isValid;

    [RelayCommand(CanExecute = nameof(CanSave))]
    private async Task SaveAsync()
    {
        await productService.SaveAsync(new Product { Name = Name, Price = Price });
    }

    private bool CanSave() => IsValid && !string.IsNullOrEmpty(Name);
}
```

### Property Changed Notifications
```csharp
public partial class OrderViewModel : ObservableObject
{
    [ObservableProperty]
    private int _quantity;

    [ObservableProperty]
    private decimal _unitPrice;

    // Computed property - manually notify
    public decimal Total => Quantity * UnitPrice;

    partial void OnQuantityChanged(int value)
    {
        OnPropertyChanged(nameof(Total));
    }

    partial void OnUnitPriceChanged(decimal value)
    {
        OnPropertyChanged(nameof(Total));
    }
}
```

### Collection ViewModel
```csharp
public partial class ProductListViewModel(IProductService productService) : ObservableObject
{
    [ObservableProperty]
    private ObservableCollection<ProductViewModel> _products = [];

    [ObservableProperty]
    private ProductViewModel? _selectedProduct;

    [ObservableProperty]
    private bool _isLoading;

    [RelayCommand]
    private async Task LoadProductsAsync()
    {
        IsLoading = true;
        try
        {
            var items = await productService.GetAllAsync();
            Products = new ObservableCollection<ProductViewModel>(
                items.Select(p => new ProductViewModel(productService)
                {
                    Name = p.Name,
                    Price = p.Price
                }));
        }
        finally
        {
            IsLoading = false;
        }
    }

    [RelayCommand]
    private void DeleteProduct(ProductViewModel product)
    {
        Products.Remove(product);
    }
}
```

## Commands

### Async Commands with Cancellation
```csharp
public partial class SearchViewModel : ObservableObject
{
    [ObservableProperty]
    private string _searchText = string.Empty;

    [RelayCommand(IncludeCancelCommand = true)]
    private async Task SearchAsync(CancellationToken token)
    {
        await Task.Delay(500, token); // Debounce
        // Search logic with cancellation support
    }
}
```

### Command with Parameter
```csharp
public partial class NavigationViewModel : ObservableObject
{
    [RelayCommand]
    private void NavigateTo(string page)
    {
        // Navigate to page
    }

    [RelayCommand]
    private async Task OpenItemAsync(int itemId)
    {
        // Load and open item
    }
}
```

## Messenger Pattern

### Sending Messages
```csharp
// Define message
public record ProductSelectedMessage(Product Product);

// Send from one ViewModel
WeakReferenceMessenger.Default.Send(new ProductSelectedMessage(selectedProduct));
```

### Receiving Messages
```csharp
public partial class ProductDetailViewModel : ObservableRecipient
{
    public ProductDetailViewModel()
    {
        IsActive = true; // Enable message reception
    }

    protected override void OnActivated()
    {
        Messenger.Register<ProductDetailViewModel, ProductSelectedMessage>(
            this, (r, m) => r.LoadProduct(m.Product));
    }

    private void LoadProduct(Product product)
    {
        // Update UI with product details
    }
}
```

## Validation

### Using ObservableValidator
```csharp
public partial class RegistrationViewModel : ObservableValidator
{
    [ObservableProperty]
    [NotifyDataErrorInfo]
    [Required(ErrorMessage = "Email is required")]
    [EmailAddress(ErrorMessage = "Invalid email format")]
    private string _email = string.Empty;

    [ObservableProperty]
    [NotifyDataErrorInfo]
    [Required]
    [MinLength(8, ErrorMessage = "Password must be at least 8 characters")]
    private string _password = string.Empty;

    [RelayCommand(CanExecute = nameof(CanRegister))]
    private async Task RegisterAs
aspnet-coreSkill

Build, debug, modernize, or review ASP.NET Core applications with correct hosting, middleware, security, configuration, logging, and deployment patterns on current .NET. USE FOR: working on ASP.NET Core apps, services, or middleware; changing auth, routing, configuration, hosting, or deployment behavior; deciding between ASP.NET Core sub-stacks. DO NOT USE FOR: unrelated stacks; generic tasks that do not need this specific guidance. INVOKES: inspect the repository context, edit targeted files, and run relevant build, test, lint, or validation commands when changes are made.

aspireSkill

Build, upgrade, and operate .NET Aspire 13.3.x application hosts with current CLI, AppHost, ServiceDefaults, integrations, dashboard, testing, and Azure deployment patterns for distributed apps. USE FOR: Aspire.AppHost.Sdk, Aspire.Hosting.*, DistributedApplication.CreateBuilder, WithReference, WaitFor, AddProject, AddRedis, AddPostgres, aspire run, aspire init, aspire. DO NOT USE FOR: unrelated stacks; generic tasks that do not need this specific guidance. INVOKES: inspect the repository context, edit targeted files, and run relevant build, test, lint, or validation commands when changes are made.

azure-functionsSkill

Build, review, or migrate Azure Functions in .NET with correct execution model, isolated worker setup, bindings, DI, and Durable Functions patterns. USE FOR: working on Azure Functions in .NET; migrating from the in-process model to the isolated worker model; adding Durable Functions, bindings, or host configuration. DO NOT USE FOR: unrelated stacks; generic tasks that do not need this specific guidance. INVOKES: inspect the repository context, edit targeted files, and run relevant build, test, lint, or validation commands when changes are made.

blazorSkill

Build and review Blazor applications across server, WebAssembly, web app, and hybrid scenarios with correct component design, state flow, rendering, and hosting choices. USE FOR: building interactive web UIs with C# instead of JavaScript; choosing between Server, WebAssembly, or Auto render modes; designing component hierarchies and state. DO NOT USE FOR: unrelated stacks; generic tasks that do not need this specific guidance. INVOKES: inspect the repository context, edit targeted files, and run relevant build, test, lint, or validation commands when changes are made.

entity-framework6Skill

Maintain or migrate EF6-based applications with realistic guidance on what to keep, what to modernize, and when EF Core is or is not the right next step. USE FOR: EF6 codebases; runtime versus ORM migration decisions; EDMX, code-first, ObjectContext, and legacy data-access review. DO NOT USE FOR: unrelated stacks; generic tasks that do not need this specific guidance. INVOKES: inspect the repository context, edit targeted files, and run relevant build, test, lint, or validation commands when changes are made.

entity-framework-coreSkill

Design, tune, or review EF Core data access with proper modeling, migrations, query translation, performance, and lifetime management for modern .NET applications. USE FOR: DbContext, migrations, model configuration, EF queries, tracking, loading, performance, transactions, and EF6 migration decisions. DO NOT USE FOR: unrelated stacks; generic tasks that do not need this specific guidance. INVOKES: inspect the repository context, edit targeted files, and run relevant build, test, lint, or validation commands when changes are made.

mauiSkill

Build, review, or migrate .NET MAUI applications across Android, iOS, macOS, and Windows with correct cross-platform UI, platform integration, and native packaging assumptions. USE FOR: working on cross-platform mobile or desktop UI in .NET MAUI; integrating device capabilities, navigation, or platform-specific code; migrating Xamarin.Forms or aligning. DO NOT USE FOR: unrelated stacks; generic tasks that do not need this specific guidance. INVOKES: inspect the repository context, edit targeted files, and run relevant build, test, lint, or validation commands when changes are made.

mlnetSkill

Use ML.NET to train, evaluate, or integrate machine-learning models into .NET applications with realistic data preparation, inference, and deployment expectations. USE FOR: ML.NET integration; local model training or retraining; inference pipelines, model loading, evaluation, and deployment review. DO NOT USE FOR: unrelated stacks; generic tasks that do not need this specific guidance. INVOKES: inspect the repository context, edit targeted files, and run relevant build, test, lint, or validation commands when changes are made.