wpf-mvvm-generator

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

WPF MVVM Generator 스킬

WPF MVVM Generator 技能

CommunityToolkit.Mvvm source generator를 사용하여 WPF MVVM 컴포넌트를 생성합니다.
중요: 모든 결과는 반드시 한국어로 작성합니다. 코드 식별자, 기술 용어, 패턴 이름 등은 원문 그대로 유지하되, 설명 부분은 한국어를 사용합니다.
使用CommunityToolkit.Mvvm源生成器生成WPF MVVM组件。
重要:所有结果必须使用中文撰写。 代码标识符、技术术语、模式名称等保留原文,说明部分使用中文。

인자

参数

  • $ARGUMENTS[0]
    : 엔티티 이름 (필수) - 예:
    User
    ,
    Product
    ,
    Order
  • $ARGUMENTS[1]
    : 생성 유형 (선택):
    viewmodel
    ,
    view
    ,
    model
    ,
    all
    (기본값:
    all
    )
    • all
      : Model + ViewModel + View + Code-Behind + Messages + Service Interface 모두 생성
  • $ARGUMENTS[0]
    : 实体名称(必填)- 示例:
    User
    ,
    Product
    ,
    Order
  • $ARGUMENTS[1]
    : 生成类型(可选):
    viewmodel
    ,
    view
    ,
    model
    ,
    all
    (默认值:
    all
    • all
      : 生成Model + ViewModel + View + 代码后置文件 + 消息类型 + 服务接口

실행 단계

执行步骤

1단계: 인자 검증

步骤1:参数验证

$ARGUMENTS[0]
이 비어있는 경우:
  • 사용자에게 엔티티 이름 요청
  • 프로젝트의 기존 Model 기반으로 제안
엔티티 이름 검증:
  • PascalCase 여부 확인
  • C# 예약어 사용 확인
  • 특수문자, 공백 포함 시 사용자에게 재입력 요청
$ARGUMENTS[0]
为空时:
  • 向用户请求实体名称
  • 根据项目中已有的Model提供建议
实体名称验证:
  • 检查是否符合PascalCase命名规范
  • 检查是否使用了C#保留字
  • 若包含特殊字符或空格,请求用户重新输入

2단계: 프로젝트 구조 탐색

步骤2:项目结构探索

기존 패턴을 식별:
  • Glob("**/*ViewModel.cs")
    로 기존 ViewModel 패턴 확인
  • 첫 번째 발견된 파일에서 네임스페이스 추출
  • /ViewModels
    ,
    /Views
    ,
    /Models
    디렉토리 존재 여부 확인
  • 기존 베이스 클래스 또는 인터페이스 탐색
识别现有模式:
  • 通过
    Glob("**/*ViewModel.cs")
    检查现有的ViewModel模式
  • 从第一个找到的文件中提取命名空间
  • 检查
    /ViewModels
    ,
    /Views
    ,
    /Models
    目录是否存在
  • 探索现有的基类或接口

3단계: 코드 생성

步骤3:代码生成

$ARGUMENTS[1]
기준으로 생성:
根据
$ARGUMENTS[1]
的值进行生成:

생성 컴포넌트

生成的组件

Model (
model
)

Model (
model
)

csharp
namespace {Namespace}.Models;

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

ViewModel (
viewmodel
)

csharp
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;
}
csharp
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
)

View (
view
)

xml
<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>
xml
<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 (최소한)

代码后置文件(最简版)

csharp
namespace {Namespace}.Views;

public partial class {EntityName}View : UserControl
{
    public {EntityName}View()
    {
        InitializeComponent();
    }
}
csharp
namespace {Namespace}.Views;

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

Message Types

Message Types

csharp
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});
csharp
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

Service Interface

csharp
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);
}
csharp
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);
}

출력 형식

输出格式

모든 내용은 한국어로 작성합니다. 코드 식별자와 기술 용어는 원문을 유지합니다.
markdown
undefined
所有内容均以中文撰写。代码标识符和技术术语保留原文。
markdown
undefined

MVVM 생성 결과

MVVM生成结果

엔티티: {EntityName}

实体: {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생성됨
文件路径状态
Model/Models/{EntityName}.cs已生成
ViewModel/ViewModels/{EntityName}ViewModel.cs已生成
View/Views/{EntityName}View.xaml已生成
代码后置文件/Views/{EntityName}View.xaml.cs已生成
消息类型/Messages/{EntityName}Messages.cs已生成
服务接口/Services/I{EntityName}Service.cs已生成

다음 단계

后续步骤

  1. I{EntityName}Service
    구현
  2. DI 컨테이너에 등록
  3. View 내비게이션 추가
  4. 필요 시 디자인 타임 데이터 생성
undefined
  1. 实现
    I{EntityName}Service
  2. 在DI容器中注册
  3. 添加View导航
  4. 如有需要,生成设计时数据
undefined

에러 처리

错误处理

상황처리
엔티티 이름 미입력사용자에게 요청, 기존 Model 기반 제안
유효하지 않은 이름 (특수문자, 예약어)사용자에게 재입력 요청
ViewModels/Views/Models 디렉토리 없음디렉토리 자동 생성 후 진행
동일 이름 파일 존재사용자에게 덮어쓰기 확인
场景处理方式
未输入实体名称向用户请求,根据现有Model提供建议
无效名称(含特殊字符、保留字)请求用户重新输入
不存在ViewModels/Views/Models目录自动创建目录后继续
存在同名文件向用户确认是否覆盖

가이드라인

指导原则

  • CommunityToolkit.Mvvm source generator를 사용합니다
  • 기존 프로젝트 명명 규칙을 따릅니다
  • 최소한의 Code-Behind을 생성합니다 (UI 로직만)
  • 적절한 XML 문서화를 포함합니다
  • 비동기 작업에 CancellationToken을 지원합니다
  • ViewModel 간 통신에 WeakReferenceMessenger를 사용합니다
  • 使用CommunityToolkit.Mvvm源生成器
  • 遵循现有项目的命名规范
  • 生成最简版的Code-Behind(仅包含UI逻辑)
  • 包含适当的XML文档注释
  • 为异步操作支持CancellationToken
  • 使用WeakReferenceMessenger实现ViewModel间的通信