SuperAPI AI Directory

wpf-mvvm-generator

Coding & development

View repository

Updated 10-16 09:08

About this skill

WPF MVVM Generator skill

Generates WPF MVVM components using the CommunityToolkit.Mvvm source generator.

Important: All results must be written in Korean. Code identifiers, technical terms, pattern names, etc. are kept as-is in the original text, but the explanatory parts use Korean.

Arguments

  • $ARGUMENTS[0]: Entity name (required) - e.g., User, Product, Order
  • $ARGUMENTS[1]: Generation type (optional): viewmodel, view, model, all (default: all)
    • all: Generate all of Model + ViewModel + View + Code-Behind + Messages + Service Interface

Execution steps

Step 1: Validate arguments

$ARGUMENTS[0]If this is empty:

  • Ask the user for the entity name
  • Suggest based on the project's existing Models

Validate the entity name:

  • Check whether it is PascalCase
  • Check whether it uses a C# reserved word
  • If it contains special characters or spaces, ask the user to re-enter it

Step 2: Explore the project structure

Identify existing patterns:

  • Glob("**/*ViewModel.cs")Check the existing ViewModel pattern with
  • Extract the namespace from the first file found
  • /ViewModels, /Views, /Models Check whether the directory exists
  • Search for existing base classes or interfaces

Step 3: Generate code

$ARGUMENTS[1] Generate based on

Generated components

Model (model)

namespace {Namespace}.Models;

/// <summary>
/// {EntityName} domain model
/// </summary>
public sealed class {EntityName}
{
    public required int Id { get; init; }
    public required string Name { get; init; }
    // 컨텍스트에 따른 추가 프로퍼티
}

ViewModel (viewmodel)

using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using CommunityToolkit.Mvvm.Messaging;

namespace {Namespace}.ViewModels;

/// <summary>
/// ViewModel for {EntityName} management
/// </summary>
public partial class {EntityName}ViewModel : ObservableObject
{
    private readonly I{EntityName}Service _{entityName}Service;

    public {EntityName}ViewModel(I{EntityName}Service {entityName}Service)
    {
        _{entityName}Service = {entityName}Service;
    }

    [ObservableProperty]
    [NotifyPropertyChangedFor(nameof(HasSelection))]
    [NotifyCanExecuteChangedFor(nameof(DeleteCommand))]
    private {EntityName}? _selected{EntityName};

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

    [ObservableProperty]
    private bool _isLoading;

    public bool HasSelection => Selected{EntityName} is not null;

    [RelayCommand]
    private async Task LoadAsync(CancellationToken cancellationToken = default)
    {
        IsLoading = true;
        try
        {
            // Load logic
        }
        finally
        {
            IsLoading = false;
        }
    }

    [RelayCommand(CanExecute = nameof(CanSave))]
    private async Task SaveAsync(CancellationToken cancellationToken = default)
    {
        await _{entityName}Service.SaveAsync(Selected{EntityName}!, cancellationToken);
        IsModified = false;
    }

    private bool CanSave() => IsModified && Selected{EntityName} is not null;

    [RelayCommand(CanExecute = nameof(CanDelete))]
    private async Task DeleteAsync(CancellationToken cancellationToken = default)
    {
        if (Selected{EntityName} is null) return;

        await _{entityName}Service.DeleteAsync(Selected{EntityName}.Id, cancellationToken);

        WeakReferenceMessenger.Default.Send(
            new {EntityName}DeletedMessage(Selected{EntityName}));

        Selected{EntityName} = null;
    }

    private bool CanDelete() => Selected{EntityName} is not null;
}

View (view)

<UserControl x:Class="{Namespace}.Views.{EntityName}View"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
             xmlns:vm="{Namespace}.ViewModels"
             mc:Ignorable="d"
             d:DataContext="{d:DesignInstance vm:{EntityName}ViewModel, IsDesignTimeCreatable=False}">

    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="*"/>
            <RowDefinition Height="Auto"/>
        </Grid.RowDefinitions>

        <!-- Header -->
        <TextBlock Grid.Row="0"
                   Text="{EntityName} Management"
                   Style="{StaticResource HeaderStyle}"/>

        <!-- Content -->
        <ContentControl Grid.Row="1"
                        Content="{Binding Selected{EntityName}}"
                        Visibility="{Binding HasSelection, Converter={StaticResource BoolToVisibility}}"/>

        <!-- Actions -->
        <StackPanel Grid.Row="2"
                    Orientation="Horizontal"
                    HorizontalAlignment="Right">
            <Button Content="Save"
                    Command="{Binding SaveCommand}"/>
            <Button Content="Delete"
                    Command="{Binding DeleteCommand}"/>
        </StackPanel>

        <!-- Loading Overlay -->
        <Border Grid.RowSpan="3"
                Background="#80000000"
                Visibility="{Binding IsLoading, Converter={StaticResource BoolToVisibility}}">
            <ProgressBar IsIndeterminate="True" Width="200"/>
        </Border>
    </Grid>
</UserControl>

Code-Behind (minimal)

namespace {Namespace}.Views;

public partial class {EntityName}View : UserControl
{
    public {EntityName}View()
    {
        InitializeComponent();
    }
}

Message Types

namespace {Namespace}.Messages;

public sealed record {EntityName}DeletedMessage({EntityName} Deleted{EntityName});
public sealed record {EntityName}SelectedMessage({EntityName} Selected{EntityName});
public sealed record {EntityName}SavedMessage({EntityName} Saved{EntityName});

Service Interface

namespace {Namespace}.Services;

public interface I{EntityName}Service
{
    Task<IReadOnlyList<{EntityName}>> GetAllAsync(CancellationToken cancellationToken = default);
    Task<{EntityName}?> GetByIdAsync(int id, CancellationToken cancellationToken = default);
    Task SaveAsync({EntityName} entity, CancellationToken cancellationToken = default);
    Task DeleteAsync(int id, CancellationToken cancellationToken = default);
}

Output format

All content is written in Korean. Code identifiers and technical terms are kept as-is in the original text.

# MVVM 생성 결과

## 엔티티: {EntityName}

### 생성된 파일
| 파일 | 경로 | 상태 |
|------|------|------|
| Model | /Models/{EntityName}.cs | 생성됨 |
| ViewModel | /ViewModels/{EntityName}ViewModel.cs | 생성됨 |
| View | /Views/{EntityName}View.xaml | 생성됨 |
| Code-Behind | /Views/{EntityName}View.xaml.cs | 생성됨 |
| Messages | /Messages/{EntityName}Messages.cs | 생성됨 |
| Service Interface | /Services/I{EntityName}Service.cs | 생성됨 |

### 다음 단계
1. `I{EntityName}Service` 구현
2. DI 컨테이너에 등록
3. View 내비게이션 추가
4. 필요 시 디자인 타임 데이터 생성

Error handling

Situation Handling
Entity name not entered Ask the user, suggest based on existing Models
Invalid name (special characters, reserved words) Ask the user to re-enter it
ViewModels/Views/Models directory does not exist Automatically create the directory and proceed
A file with the same name exists Ask the user to confirm overwriting

Guidelines

  • Use the CommunityToolkit.Mvvm source generator
  • Follow the existing project naming conventions
  • Generate minimal Code-Behind (UI logic only)
  • Include appropriate XML documentation
  • Support CancellationToken for asynchronous operations
  • Use WeakReferenceMessenger for communication between ViewModels