AgentStack
SKILL verified MIT Self-run

Avalonia Mvvm

skill-linuxdevel-avalonia-skills-avalonia-mvvm · by linuxdevel

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

No reviews yet
0 installs
11 views
0.0% view→install

Install

$ agentstack add skill-linuxdevel-avalonia-skills-avalonia-mvvm

✓ scanned · ✓ verified — works with Claude Code, Cursor, and more.

Security review

✓ Passed

No issues found. Passed automated security review. · v0.1.0 How review works →

  • Prompt-injection patterns
  • Secret / credential exfiltration
  • Dangerous shell & filesystem operations
  • Untrusted network calls
  • Known-malicious package signatures

What it can access

  • Network access No
  • Filesystem access No
  • Shell / process execution No
  • Environment & secrets No
  • Dynamic code execution No

From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.

Are you the author of Avalonia Mvvm? Claim this listing to set pricing, connect Stripe payouts, and keep 70% of every sale.
Sign up to claim

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)

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.

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)

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)

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 (FooViewModelFooView):

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:


    

ViewModel-First Navigation

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

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

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

Design-Time DataContext

ObservableCollection for Lists

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.

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

Reviews

No reviews yet — be the first.

Versions

  • v0.1.0 Imported from the upstream source.