05. RelayCommand
RelayCommand는 MVVM 구조에서 메서드(Logic)를 실행하는 방법입니다.
Relay(중계자) = 바통을 넘기다, 전달하다 → "동작(명령)을 전달(delegate)하는 역할"
(View)Command → RelayCommand → (ViewModel)Method
1️⃣ RelayCommand 개요
📚 Microsoft Docs: CommunityToolkit.Mvvm.Input.RelayCommand

✅ 작동 방식
CommunityToolkit.Mvvm.Input.RelayCommand의 [RelayCommand] 어트리뷰트는
ICommand 인터페이스를 자동으로 구현해주는 소스 생성기(SG) 기반 기능입니다.
[RelayCommand] 어트피뷰트를 메서드에 붙이면
해당 메서드를 실행하는 <메서드이름>Command 속성이 자동으로 생성됩니다.
UI에서 ViewModel의 메서드를 실행하도록 연결하는 복잡한 ICommand 구현을 자동으로 대체할 수 있습니다.
✅ 예제 소스코드
2️⃣ 01ICommand - 자동완성 없이, C# MVVM 구현 구조 예제
✔️ 1. 학습 목표
- Step 1.
- CommunityToolkit.Mvvm 패키지를 통한 자동완성 기능을 학습하기에 앞서,
전통적인 방식의 C# MVVM 패턴 구현 방법을 되짚어 봅니다. - ICommand 인터페이스를 상속 구현한 RelayCommand 구현하고,
ViewModel 에서 메서드에 적용시키는 방법을 되짚어봅니다.
- CommunityToolkit.Mvvm 패키지를 통한 자동완성 기능을 학습하기에 앞서,
- Step 2.
- 이후에 CommunityToolkit.Mvvm 패키지를 사용해 자동 구현할 때,
어떤 부분이 자동화되어 편리한지 확인합니다.
- 이후에 CommunityToolkit.Mvvm 패키지를 사용해 자동 구현할 때,
- Step 3.
- 그리고 프로젝트의 어느 부분에 적용하면 효과적일지 예상해봅니다.
✔️ 2. 동작 시나리오

- 사용자가 이름을 입력할 수 있는 TextBox에 이름을 입력한다.
- View의 "인사하기" 버튼을 눌렀을 때, ViewModel 메서드가 동작하고
- Hello, [이름]! 메시지를 하단의 TextBlock에 출력합니다.
- *이름이 비어 있으면 인사하지 않고 "이름을 입력하세요!" 라고 안내한다.
✔️ 3. 자동완성 없이, 전통적인 방식으로 C# MVVM 구조를 만들기 위해 ICommand 구현
📁 3.1 가장먼저, 아래 링크를 참고해 WPF 프로젝트를 생성 합니다.
WPF 프로젝트 생성하기
01. Visual Studio 실행더보기 02. 새 프로젝트 생성더보기❶ 최근 실행한 프로젝트의 목록을 보여줍니다.❷ GitHub, Azure, DevOps, Bitbucket 등 원격 저장소에서 프로젝트를 가져옵니다.❸ 내 컴퓨터의 로컬
basiclike.tistory.com
📁 3.2 생성한 WPF 프로젝트에, 아래 링크를 참고해 CommunityToolkit.Mvvm 패키지를 설치합니다.
02. 패키지 설치 방법
1️⃣ "NuGet 패키지 관리자"에서, CommunityToolkit.Mvvm 설치하는 방법더보기 WPF 프로젝트 생성하기01. Visual Studio 실행더보기 02. 새 프로젝트 생성더보기❶ 최근 실행한 프로젝트의 목록을 보여줍니다.
basiclike.tistory.com
📁 3.3 프로젝트 구조
WpfMvvmToolkit05_01ICommand/
├── Commands/
│ └── RelayCommand.cs
├── ViewModels/
│ └── MainViewModel.cs
└── Views/
└── MainWindow.xaml ← 메인 뷰
└── MainWindow.xaml.cs
📁 3.4 ICommand 동작 구조

①ViewModel 네임스페이스를 ②View에 등록합니다. ③DataContext를 지정합니다.

④View에서 버튼을 클릭하면 ⑤바인딩 된 ⑥RelayCommand가 실행됩니다.
① RelayCommand는, MainViewModel 생성시 ②SayHello() 메서드가 등록되도록 구현되어 있습니다.
③ RelayCommand가 실행되면, 등록된 S ayHello() 메서드가 동작합니다.
📁 3.5 ICommand 구현

// ICommand를 간단하게 구현한 RelayCommand 클래스 (매개변수 없는 Action 지원)
public class RelayCommand : ICommand
{
private readonly Action _execute;
public RelayCommand(Action execute)
{
_execute = execute;
}
// 항상 true를 반환하여 버튼을 항상 활성 상태로 유지
public bool CanExecute(object? parameter) => true;
// 버튼 클릭 시 실행될 메서드 호출
public void Execute(object? parameter) => _execute.Invoke();
// CanExecuteChanged 이벤트 (사용되지 않음)
public event EventHandler? CanExecuteChanged;
}
📁 3.6 ViewModel 구현하기

// CommandParameter 없이, 단순히 ViewModel 속성으로 동작하는 예제
public class MainViewModel : INotifyPropertyChanged
{
private string? name;
private string? greetingMessage;
// 사용자 입력을 저장하는 속성
public string? Name
{
get => name;
set
{
if (name != value)
{
name = value;
OnPropertyChanged(); // UI에 알림
}
}
}
// 인사 메시지를 저장하는 속성
public string? GreetingMessage
{
get => greetingMessage;
set
{
if (greetingMessage != value)
{
greetingMessage = value;
OnPropertyChanged(); // UI에 알림
}
}
}
// ICommand 타입의 커맨드 속성. 버튼 클릭 시 실행됨
public ICommand SayHelloCommand { get; }
public MainViewModel()
{
// CommandParameter 없이 ViewModel 속성을 참조하는 방식
SayHelloCommand = new RelayCommand(SayHello);
}
// 커맨드로 실행될 메서드
private void SayHello()
{
if (!string.IsNullOrWhiteSpace(Name))
GreetingMessage = $"안녕하세요, {Name}님!";
else
GreetingMessage = "이름을 입력하세요!";
}
public event PropertyChangedEventHandler? PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] string propertyName = null!)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
📁 3.7 View 구성하기

xmlns:vm="clr-namespace:WpfMvvmToolkit05_01ICommand.ViewModels"
Title="RelayCommand Demo" Height="200" Width="300">
<Window.DataContext>
<vm:MainViewModel />
</Window.DataContext>
<StackPanel Margin="20">
<TextBox Text="{Binding Name, UpdateSourceTrigger=PropertyChanged}"
Margin="0,0,0,10"/>
<Button Content="인사하기"
Command="{Binding SayHelloCommand}"
Margin="0,0,0,10"/>
<TextBlock Text="{Binding GreetingMessage}"
FontWeight="Bold" />
</StackPanel>
3️⃣ 02RelayCommand - CommunityToolkit.Mvvm 패키지 사용 구조 예제
✔️ 1. 학습 목표
- "01ICommand - 기본 C# MVVM 구현 구조 예제" 와 비교하여
CommunityToolkit.Mvvm 패키지를 사용할 때, 어떤 부분을 효과적으로 사용할 수 있는지 확인합니다.
// MainViewModel 클래스는 MVVM 패턴에서 View와 Model 사이의 중간 역할을 담당합니다.
// ObservableObject를 상속받음으로써 INotifyPropertyChanged를 자동으로 구현합니다.
public partial class MainViewModel : ObservableObject
{
// [RelayCommand] 특성을 메서드에 붙이면 내부적으로 SayHelloCommand라는 ICommand 속성이 자동으로 생성됩니다.
// 이 명령은 XAML에서 Button의 Command 속성과 연결되어 버튼 클릭 시 이 메서드가 실행되도록 합니다.
// 예: <Button Command="{Binding SayHelloCommand}" />
[RelayCommand]
private void SayHello()
{
// Name 속성이 비어 있거나 공백일 경우 안내 메시지를 출력하고
// 값이 있을 경우 인사 메시지를 GreetingMessage 속성에 저장합니다.
if (string.IsNullOrWhiteSpace(Name))
{
GreetingMessage = "이름을 입력하세요!";
}
else
{
GreetingMessage = $"안녕하세요, {Name}님!";
}
// GreetingMessage 속성 값이 변경되면 PropertyChanged 이벤트가 발생하며,
// 이 속성을 바인딩한 TextBlock의 UI 내용도 자동으로 업데이트됩니다.
}
}
✔️ 2. CommunityToolkit.Mvvm 패키지의 [RelayCommand] 사용해 보기
📁 2.1 가장먼저, 아래 링크를 참고해 WPF 프로젝트를 생성 합니다.
WPF 프로젝트 생성하기
01. Visual Studio 실행더보기 02. 새 프로젝트 생성더보기❶ 최근 실행한 프로젝트의 목록을 보여줍니다.❷ GitHub, Azure, DevOps, Bitbucket 등 원격 저장소에서 프로젝트를 가져옵니다.❸ 내 컴퓨터의 로컬
basiclike.tistory.com
📁 2.2 생성한 WPF 프로젝트에, 아래 링크를 참고해 CommunityToolkit.Mvvm 패키지를 설치합니다.
02. 패키지 설치 방법
1️⃣ "NuGet 패키지 관리자"에서, CommunityToolkit.Mvvm 설치하는 방법더보기 WPF 프로젝트 생성하기01. Visual Studio 실행더보기 02. 새 프로젝트 생성더보기❶ 최근 실행한 프로젝트의 목록을 보여줍니다.
basiclike.tistory.com
📁 2.3 ViewModel 구현하기

① 기존 ViewModel 파일에
② 메시지 박스에 입력받은 name 프로퍼티 값을 greetingMessage 프로퍼티에 추가하는 로직을 구현하고
③ 메서드 상단에 [RelayCommand] 어트리뷰트를 추가하면,
④ 관련 패키지의 네임스페이스가 자동 추가됩니다.

⑤ ViewModel 클래스에 ObservableObject를 상속받고,
⑥ 클래스는 public partial로 변경되었는지 확인하고
⑦ 빌드합니다.
public partial class MainViewModel : ObservableObject
{
[ObservableProperty]
private string? name;
[ObservableProperty]
private string? greetingMessage;
[RelayCommand]
private void SayHello()
{
if (!string.IsNullOrWhiteSpace(Name))
GreetingMessage = $"안녕하세요, {Name}님!";
else
GreetingMessage = "이름을 입력하세요!";
}
}
✔️ 2.3 XAML 화면 구성 (동일)
xmlns:vm="clr-namespace:WPF_ToolKit04_03.ViewModel"
Title="RelayCommand Demo" Height="200" Width="300">
<Window.DataContext>
<vm:MainViewModel />
</Window.DataContext>
<StackPanel Margin="20">
<TextBox Text="{Binding Name, UpdateSourceTrigger=PropertyChanged}" Margin="0,0,0,10"/>
<Button Content="인사하기" Command="{Binding SayHelloCommand}" Margin="0,0,0,10"/>
<TextBlock Text="{Binding GreetingMessage}" FontWeight="Bold" />
</StackPanel>
✔️ 3. 동작 구조
- 실행 흐름
- 버튼 클릭 → XAML Command → RelayCommand → ViewModel Method 실행
- ViewModel
- [RelayCommand] 어트리뷰트를 붙이면 SayHelloCommand가 자동으로 생성됨
- View
- Command="{Binding SayHelloCommand}"를 통해 버튼과 연결됨
- 입력 필드와 Name 속성은 양방향 바인딩
4️⃣ Source Generator가 자동 생성한 구현부 살펴보기

① CommunityToolkit.Mvvm 패키지의 Source Generator는
② MVVM에 필요한 소스코드를 자동 완성해 줍니다.
② 솔루션 탐색기에서, MainViewModel.cs에 자동완성된 ___Command 함수를 더블 클릭합니다.

③ 테스트용 ViewModel 클래스 파일의 partial 클래스에서
④ 자동완성된 MVVM 소스코드들을 확인할 수 있습니다.
CommunityToolkit.Mvvm 패키지의 Source Generator가 자동구현하지 않았다면, 개발자가 수동으로 구현해야 하는 부분입니다
5️⃣ [ RelayCommand ] 구현 - 상세 주석
🔍 ViewModel 클래스 만들기 (MainViewModel.cs)
// MainViewModel 클래스는 MVVM 패턴에서 View와 Model 사이의 중간 역할을 담당합니다.
// ObservableObject를 상속받음으로써 INotifyPropertyChanged를 자동으로 구현합니다.
public partial class MainViewModel : ObservableObject
{
// 사용자로부터 입력받은 이름을 저장하는 속성입니다.
// [ObservableProperty] 특성을 사용하면 name 필드에 대한 Name 속성이 자동 생성되고,
// 속성이 변경될 때 UI에 자동으로 통지됩니다 (PropertyChanged 이벤트 발생).
[ObservableProperty]
private string? name;
// 인사 메시지를 화면에 표시하기 위한 속성입니다.
// GreetingMessage 속성은 TextBlock에 바인ㅁ
private string? greetingMessage;
// [RelayCommand] 특성을 메서드에 붙이면 내부적으로 SayHelloCommand라는 ICommand 속성이 자동으로 생성됩니다.
// 이 명령은 XAML에서 Button의 Command 속성과 연결되어 버튼 클릭 시 이 메서드가 실행되도록 합니다.
// 예: <Button Command="{Binding SayHelloCommand}" />
[RelayCommand]
private void SayHello()
{
// Name 속성이 비어 있거나 공백일 경우 안내 메시지를 출력하고
// 값이 있을 경우 인사 메시지를 GreetingMessage 속성에 저장합니다.
if (string.IsNullOrWhiteSpace(Name))
{
GreetingMessage = "이름을 입력하세요!";
}
else
{
GreetingMessage = $"안녕하세요, {Name}님!";
}
// GreetingMessage 속성 값이 변경되면 PropertyChanged 이벤트가 발생하며,
// 이 속성을 바인딩한 TextBlock의 UI 내용도 자동으로 업데이트됩니다.
}
}
🔍 XAML 화면 구성
<!--
이 XAML 파일은 MVVM 패턴을 따르는 WPF 애플리케이션의 MainWindow입니다.
ViewModel(MainViewModel)과의 바인딩을 통해 사용자 입력을 처리하고 결과를 화면에 표시합니다.
-->
<Window x:Class="StudentRelayCommandApp.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vm="clr-namespace:StudentRelayCommandApp.ViewModels"
Title="RelayCommand 실습" Height="200" Width="300">
<!-- ViewModel(MainViewModel)을 이 View의 DataContext로 설정합니다.
이렇게 하면 ViewModel의 속성과 명령을 이 XAML에서 직접 바인딩할 수 있습니다. -->
<Window.DataContext>
<vm:MainViewModel />
</Window.DataContext>
<!-- StackPanel은 요소를 수직으로 정렬하는 레이아웃 컨트롤입니다. -->
<StackPanel Margin="20">
<!--
TextBox는 사용자로부터 이름을 입력받는 UI 요소입니다.
Text 속성을 ViewModel의 Name 속성과 바인딩합니다.
UpdateSourceTrigger=PropertyChanged 설정을 통해 텍스트가 변경될 때마다 ViewModel에 즉시 반영됩니다.
-->
<TextBox Text="{Binding Name, UpdateSourceTrigger=PropertyChanged}"
PlaceholderText="이름 입력" Margin="0,0,0,10"/>
<!--
Button은 ViewModel의 SayHelloCommand 명령에 바인딩되어 있습니다.
[RelayCommand] 특성이 적용된 SayHello() 메서드에 의해 SayHelloCommand 속성이 자동으로 생성됩니다.
버튼을 클릭하면 SayHello() 메서드가 실행되고, 그 결과가 GreetingMessage에 저장됩니다.
-->
<Button Content="인사하기" Command="{Binding SayHelloCommand}" Margin="0,0,0,10"/>
<!--
TextBlock은 ViewModel의 GreetingMessage 속성을 바인딩하여 인사 메시지를 화면에 출력합니다.
Name 속성 입력 → 버튼 클릭 → 인사 메시지 출력의 흐름을 확인할 수 있습니다.
-->
<TextBlock Text="{Binding GreetingMessage}" FontWeight="Bold" />
</StackPanel>
</Window>