# Avalonia Mvvm

> Use when implementing the MVVM pattern in Avalonia, including ViewModels, INotifyPropertyChanged, ICommand, ReactiveUI, CommunityToolkit.Mvvm, or ViewModel-first navigation.

- **Type:** Skill
- **Install:** `agentstack add skill-linuxdevel-avalonia-skills-avalonia-mvvm`
- **Verified:** Yes — security-reviewed for prompt injection and unsafe behavior
- **Seller:** [linuxdevel](https://agentstack.voostack.com/s/linuxdevel)
- **Installs:** 0
- **Category:** [Agent Skills](https://agentstack.voostack.com/c/agent-skills)
- **Latest version:** 0.1.0
- **License:** MIT
- **Upstream author:** [linuxdevel](https://github.com/linuxdevel)
- **Source:** https://github.com/linuxdevel/Avalonia-skills/tree/main/skills/avalonia/avalonia-mvvm

## Install

```sh
agentstack add skill-linuxdevel-avalonia-skills-avalonia-mvvm
```

Requires the [AgentStack CLI](https://agentstack.voostack.com/docs/cli). Works with Claude Code, Cursor, and any MCP-compatible agent.

## About

# Avalonia MVVM

## Overview

MVVM: Model (data/business), View (.axaml), ViewModel (presentation logic, bindable state). ViewModel knows nothing about View. Views bind to ViewModel properties and commands.

## INotifyPropertyChanged (Manual)

```csharp
public class PersonViewModel : INotifyPropertyChanged
{
    private string _name = "";
    public string Name
    {
        get => _name;
        set { _name = value; PropertyChanged?.Invoke(this, new(nameof(Name))); }
    }
    public event PropertyChangedEventHandler? PropertyChanged;
}
```

## CommunityToolkit.Mvvm (Recommended)

Source-generator based, no reflection, clean code.

```csharp
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;

public partial class MainViewModel : ObservableObject
{
    [ObservableProperty]
    private string _name = "";

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

    [RelayCommand(CanExecute = nameof(CanSave))]
    private async Task SaveAsync()
    {
        // save logic
    }

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

NuGet: `CommunityToolkit.Mvvm`

Generated: `Name` property with change notification, `NameChanged` partial method hook, `SaveCommand` as `AsyncRelayCommand`.

## ReactiveUI (Alternative)

```csharp
using ReactiveUI;

public class MainViewModel : ReactiveObject
{
    private string _name = "";
    public string Name
    {
        get => _name;
        set => this.RaiseAndSetIfChanged(ref _name, value);
    }

    public ReactiveCommand SaveCommand { get; }

    public MainViewModel()
    {
        var canSave = this.WhenAnyValue(x => x.Name, n => !string.IsNullOrEmpty(n));
        SaveCommand = ReactiveCommand.CreateFromTask(SaveAsync, canSave);
    }

    private async Task SaveAsync() { /* ... */ }
}
```

NuGet: `Avalonia.ReactiveUI`  
App setup: `.UseReactiveUI()` in Program.cs

## ICommand (Manual)

```csharp
public class RelayCommand : ICommand
{
    private readonly Action _execute;
    private readonly Func? _canExecute;

    public RelayCommand(Action execute, Func? canExecute = null)
    { _execute = execute; _canExecute = canExecute; }

    public event EventHandler? CanExecuteChanged;
    public bool CanExecute(object? p) => _canExecute?.Invoke() ?? true;
    public void Execute(object? p) => _execute();
    public void RaiseCanExecuteChanged() => CanExecuteChanged?.Invoke(this, EventArgs.Empty);
}
```

## ViewLocator Pattern

Auto-maps ViewModels → Views by naming convention (`FooViewModel` → `FooView`):

```csharp
public class ViewLocator : IDataTemplate
{
    public Control? Build(object? data)
    {
        if (data is null) return null;
        var name = data.GetType().FullName!.Replace("ViewModel", "View");
        var type = Type.GetType(name);
        return type != null
            ? (Control)Activator.CreateInstance(type)!
            : new TextBlock { Text = $"Not Found: {name}" };
    }
    public bool Match(object? data) => data is ViewModelBase;
}
```

Register in App.axaml:

```xml

    

```

## ViewModel-First Navigation

Pattern: Router/NavigationViewModel holds current page ViewModel, ContentControl displays it:

```xml

```

```csharp
public partial class MainViewModel : ObservableObject
{
    [ObservableProperty]
    private ViewModelBase _currentPage = new HomeViewModel();

    public void NavigateTo(ViewModelBase page) => CurrentPage = page;
}
```

## Design-Time DataContext

```xml

```

## ObservableCollection for Lists

```csharp
public ObservableCollection People { get; } = new();
```

Use `ObservableCollection` (not `List`) so UI updates on add/remove.

## Community Libraries

| Library | NuGet / Repo | Purpose |
|---|---|---|
| CommunityToolkit.Mvvm | `CommunityToolkit.Mvvm` | Source-generator MVVM (ObservableObject, RelayCommand) — recommended |
| ReactiveUI | `Avalonia.ReactiveUI` | Reactive MVVM with Rx.NET observables |
| Prism | `Prism.Avalonia` | Full MVVM framework with regions, modules, DI, dialogs |
| Epoxy | `Epoxy.Avalonia` | Lightweight multi-platform MVVM |
| FuncUI | `Avalonia.FuncUI` | F# MVU pattern with view DSL |
| ReactiveElmish.Avalonia | GitHub: JordanMarr/ReactiveElmish.Avalonia | F# MVU with XAML views |
| Stylet.Avalonia | `Stylet.Avalonia` | Lightweight ViewModel-first MVVM (Caliburn.Micro-inspired) |
| HanumanInstitute.MvvmDialogs | `HanumanInstitute.MvvmDialogs.Avalonia` | Open dialogs from ViewModel in pure MVVM |
| ReactiveValidation | `ReactiveValidation` | Fluent-style property validation with UI messages |
| Markup Declarative | `Avalonia.Markup.Declarative` | Declarative C# UI and MVU helpers |
| Lemon.ModuleNavigation | GitHub: NeverMorewd/Lemon.ModuleNavigation | .NET Generic Host + module navigation |
| AsyncNavigation | GitHub: NeverMorewd/AsyncNavigation | Async navigation via Microsoft.Extensions.DI |

## Common Mistakes

- Using `List` instead of `ObservableCollection` — list changes not reflected in UI
- Not calling `PropertyChanged` with exact property name — use `nameof()`
- Doing UI work in ViewModel (creating controls, accessing `DataContext`)
- Async command without `AsyncRelayCommand` — exceptions swallowed, UI blocks
- Forgetting `partial` keyword on CommunityToolkit ViewModel class — source gen fails silently

## Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

- **Author:** [linuxdevel](https://github.com/linuxdevel)
- **Source:** [linuxdevel/Avalonia-skills](https://github.com/linuxdevel/Avalonia-skills)
- **License:** MIT

Install and usage instructions live in the source repository linked above.

## Pricing

- **Free** — Free

## Security capabilities

Automated source analysis of v0.1.0 — what this tool can access:

- **Network access:** no
- **Filesystem access:** no
- **Shell / process execution:** no
- **Environment & secrets:** no
- **Dynamic code execution:** no

*"Yes" means the capability is present in the source — more access means more to trust, not that it is unsafe.*


## Versions

- **0.1.0** — security scan: passed — Imported from the upstream source.

## Links

- Listing page: https://agentstack.voostack.com/l/skill-linuxdevel-avalonia-skills-avalonia-mvvm
- Seller: https://agentstack.voostack.com/s/linuxdevel
- Browse the marketplace: https://agentstack.voostack.com/browse

---
Listed on AgentStack — the marketplace for AI agent skills and MCP servers. Every listing is security-reviewed. Creators keep 70%.
