Agent Skills
› NeverSight/learn-skills.dev
› avalonia-mvvm
avalonia-mvvm
GitHub指导在Avalonia应用中基于CommunityToolkit.Mvvm实现MVVM模式,涵盖ViewModel创建、可观察属性、命令及依赖注入。
Trigger Scenarios
创建Avalonia视图模型
实现UI变更通知的可观察属性
添加处理用户交互的命令
配置带有SukiSideMenu或ContentControl的导航
在视图模型间传递服务
实现视图模型属性验证
Install
npx skills add NeverSight/learn-skills.dev --skill avalonia-mvvm -g -y
SKILL.md
Frontmatter
{
"name": "avalonia-mvvm",
"license": "MIT",
"metadata": {
"audience": "avalonia-developers",
"framework": "avalonia"
},
"description": "Implement MVVM patterns using CommunityToolkit.Mvvm in Avalonia applications with ViewModels, Commands, and Dependency Injection",
"compatibility": "opencode"
}
MVVM Patterns in Avalonia
Complete guide to MVVM patterns using CommunityToolkit.Mvvm in Avalonia applications.
What I do
- Guide implementation of ViewModels with ObservableObject base class
- Show how to create observable properties with [ObservableProperty] attribute
- Demonstrate command patterns with [RelayCommand] including async and CanExecute
- Explain View-ViewModel mapping using DataTemplates (critical for navigation)
- Show dependency injection patterns for passing services to ViewModels
When to use me
Use this skill when you need to:
- Create new ViewModels for Avalonia views
- Implement observable properties that notify the UI of changes
- Add commands to handle user interactions (button clicks, etc.)
- Set up navigation patterns with SukiSideMenu or ContentControl
- Pass services (like ISukiToastManager) between ViewModels
- Implement validation on ViewModel properties
ViewModel Base Classes
Basic ViewModel
using CommunityToolkit.Mvvm.ComponentModel;
namespace YourApp.ViewModels;
public partial class ViewModelBase : ObservableObject
{
}
Page ViewModel Base
using CommunityToolkit.Mvvm.ComponentModel;
namespace YourApp.ViewModels;
public abstract partial class PageViewModelBase : ViewModelBase
{
public abstract string DisplayName { get; }
public abstract string Icon { get; }
}
Observable Properties
Basic Observable Property
[ObservableProperty]
private string _name = "";
With Validation
using System.ComponentModel.DataAnnotations;
[ObservableProperty]
[Required(ErrorMessage = "Name is required")]
[MinLength(3, ErrorMessage = "Name must be at least 3 characters")]
private string _name = "";
With Property Changed Notification
[ObservableProperty]
private string _firstName = "";
[ObservableProperty]
private string _lastName = "";
public string FullName => $"{FirstName} {LastName}";
partial void OnFirstNameChanged(string value)
{
OnPropertyChanged(nameof(FullName));
}
partial void OnLastNameChanged(string value)
{
OnPropertyChanged(nameof(FullName));
}
Commands
Basic Command
[RelayCommand]
private void Save()
{
// Save logic
}
Command with Parameter
[RelayCommand]
private void ButtonClick(string buttonName)
{
LastButtonClicked = buttonName;
}
View:
<Button Content="Save"
Command="{Binding ButtonClickCommand}"
CommandParameter="Save Button" />
Async Command
[RelayCommand]
private async Task LoadDataAsync()
{
IsLoading = true;
await Task.Delay(1000);
IsLoading = false;
}
Command with CanExecute
[ObservableProperty]
private bool _isDataLoaded;
[RelayCommand(CanExecute = nameof(CanSave))]
private void Save()
{
// Save logic
}
private bool CanSave() => IsDataLoaded;
partial void OnIsDataLoadedChanged(bool value)
{
SaveCommand.NotifyCanExecuteChanged();
}
Critical Pattern: DataTemplates
When using navigation patterns where ViewModels are displayed in a ContentControl (like SukiSideMenu), you MUST define DataTemplates to map ViewModels to their corresponding Views.
In SukiWindow:
<suki:SukiWindow.DataTemplates>
<DataTemplate DataType="vm:HomePageViewModel">
<views:HomePage />
</DataTemplate>
<DataTemplate DataType="vm:SettingsPageViewModel">
<views:SettingsPage />
</DataTemplate>
</suki:SukiWindow.DataTemplates>
Dependency Injection
Passing Services to ViewModels
Parent ViewModel:
public partial class MainWindowViewModel : ViewModelBase
{
private readonly ISukiToastManager _toastManager;
public MainWindowViewModel()
{
_toastManager = new SukiToastManager();
Pages = new ObservableCollection<PageViewModelBase>
{
new NotificationsPageViewModeler)
};
}
}
Child ViewModel:
public partial class NotificationsPageViewModel : PageViewModelBase
{
private readonly ISukiToastManager _toastManager;
public NotificationsPageViewModel(ISukiToastManager toastManager)
{
_toastManager = toastManager;
}
}
Common Mistakes to Avoid
- Forgetting DataTemplates - Navigation won't work without ViewModel-to-View mapping
- Not using partial class - CommunityToolkit.Mvvm requires partial keyword
- Missing OnPropertyChanged - Computed properties won't update without manual notification
- Not calling NotifyCanEuteChanged - Commands with CanExecute won't re-evaluate automatically
- Using wrong field naming - Observable property fields must start with underscore and be private
Best Practices
- Always use partial keyword on ViewModels
- Use [ObservableProperty] instead of manual INotifyPropertyChanged
- Use [RelayCommand] instead of manual ICommand implementation
- Define DataTemplates in the root window for navigation scenarios
- Pass services through constructor injection
- Use computed properties for derived values
- Implement validation attributes on properties need validation
Version History
- e0220ca Current 2026-07-05 22:17


