// 이 클래스는 Student 객체 하나를 다른 ViewModel 또는 컴포넌트로 전달하기 위한 메시지입니다.
// MVVM 구조에서 ViewModel 간 직접 참조 없이 데이터를 전달하고자 할 때 Messenger와 함께 사용합니다.
// 예를 들어, 학생 목록에서 하나의 학생(Student)을 선택했을 때,
// 그 정보를 상세보기 화면에 전달할 수 있도록 하는 역할을 합니다.
public class StudentSelectedMessage
{
// 선택된 학생 정보를 담는 속성입니다.
// 생성자에서 초기화되며, 외부에서는 읽기만 가능합니다 (get-only).
public Student SelectedStudent { get; }
// 생성자: 외부에서 Student 객체를 전달받아 속성에 저장합니다.
// 이 생성자는 메시지를 만들 때 사용됩니다.
public StudentSelectedMessage(Student student)
{
// 매개변수로 받은 student 객체를 SelectedStudent 속성에 저장합니다.
SelectedStudent = student;
}
}
// 이 클래스는 한 명의 학생(Student)에 대한 정보를 표현하는 데이터 모델입니다.
// MVVM 구조에서 ViewModel과 View가 다룰 데이터의 기본 단위로 사용됩니다.
public class Student
{
// 학생의 이름을 저장하는 속성입니다.
// string.Empty로 초기화하여 기본값을 빈 문자열로 설정합니다.
// 예: "홍길동"
public string Name { get; set; } = string.Empty;
// 학생의 나이를 저장하는 속성입니다.
// 예: 18
public int Age { get; set; }
// 객체를 문자열로 표현할 때 사용되는 메서드입니다.
// 예: student.ToString()을 호출하면 "홍길동 (18세)"처럼 출력됩니다.
public override string ToString() => $"{Name} ({Age}세)";
}
using CommunityToolkit.Mvvm.ComponentModel; // MVVM용 ViewModel 기반 클래스 제공 (ObservableObject)
using CommunityToolkit.Mvvm.Input; // RelayCommand 및 커맨드 관련 기능 제공
using System.Collections.ObjectModel; // ObservableCollection 컬렉션 사용
using WpfMvvmToolkit07_ObservableRecipient02.Models; // Student 클래스 모델 참조
namespace WpfMvvmToolkit07_ObservableRecipient02.ViewModels
{
// MainViewModel은 학생 목록을 보여주고, 선택한 학생 정보를 상세보기(View)로 전달하는 역할을 합니다.
// ObservableObject를 상속받아 프로퍼티 변경 알림 기능을 자동으로 구현합니다.
public partial class MainViewModel : ObservableObject
{
// 학생 목록을 담고 있는 컬렉션입니다.
// ObservableCollection은 UI와 자동으로 연결되어 리스트가 변경되면 UI도 자동으로 갱신됩니다.
public ObservableCollection<Student> Students { get; } =
[
new Student { Name = "홍길동", Age = 20 },
new Student { Name = "김영희", Age = 22 },
new Student { Name = "이철수", Age = 21 },
];
// 사용자가 선택한 학생을 저장하는 속성입니다.
// [ObservableProperty]를 사용하면 자동으로 SelectedStudent 프로퍼티와 PropertyChanged 이벤트가 생성됩니다.
[ObservableProperty]
private Student? selectedStudent;
// 상세 보기 창을 열어야 한다는 요청을 View에 알리는 이벤트입니다.
// MVVM 구조에서 ViewModel은 직접 View를 열 수 없기 때문에, 이벤트를 통해 View에게 알려줍니다.
public event Action? RequestShowDetail;
// 버튼에 바인딩되는 커맨드 메서드입니다.
// [RelayCommand]를 사용하면 ShowDetailCommand라는 ICommand 속성이 자동 생성됩니다.
// CanExecute 조건으로 CanShowDetail 메서드를 지정하면, 실행 가능 여부가 자동으로 판단됩니다.
[RelayCommand(CanExecute = nameof(CanShowDetail))]
private void ShowDetail()
{
// 선택된 학생이 null이 아닌 경우에만 상세 보기 요청 이벤트를 발생시킵니다.
if (SelectedStudent is not null)
{
RequestShowDetail?.Invoke(); // View가 이 이벤트를 수신하면 상세 보기 창을 띄움
}
}
// SelectedStudent 속성의 값이 변경될 때 자동 호출되는 partial 메서드입니다.
// RelayCommand는 이 메서드를 자동으로 감지하여 값 변경 시점에 실행되도록 만듭니다.
// 이곳에서 커맨드의 실행 가능 여부를 다시 평가하도록 알립니다.
partial void OnSelectedStudentChanged(Student? oldValue, Student? newValue)
{
ShowDetailCommand.NotifyCanExecuteChanged(); // 버튼 비활성화/활성화 상태 갱신
}
// ShowDetail 커맨드가 실행 가능한지 판단하는 메서드입니다.
// 학생이 선택되어 있을 때만 true를 반환합니다.
private bool CanShowDetail() => SelectedStudent != null;
}
}
📁 2.4 DetailWindow
using CommunityToolkit.Mvvm.ComponentModel; // ObservableRecipient, ObservableProperty 제공
using CommunityToolkit.Mvvm.Messaging; // 메시지 수신 기능 제공
using WpfMvvmToolkit07_ObservableRecipient02.Messages; // StudentSelectedMessage 메시지 참조
using WpfMvvmToolkit07_ObservableRecipient02.Models; // Student 모델 참조
namespace WpfMvvmToolkit07_ObservableRecipient02.ViewModels
{
// DetailViewModel은 학생 정보 상세 화면에 해당하는 ViewModel입니다.
// 메시지를 통해 선택된 학생(Student)을 전달받아 화면에 표시합니다.
public partial class DetailViewModel : ObservableRecipient, IRecipient<StudentSelectedMessage>
{
// 수신한 학생 정보를 저장하는 프로퍼티입니다.
// [ObservableProperty]는 자동으로 StudentInfo 프로퍼티와 PropertyChanged 알림을 생성합니다.
// View는 이 속성과 바인딩하여 학생 정보를 자동으로 표시할 수 있습니다.
[ObservableProperty]
private Student? studentInfo;
// 생성자에서 IsActive를 true로 설정하면 메시지를 수신할 준비가 되었음을 의미합니다.
// ObservableRecipient는 IsActive가 true일 때만 Messenger로부터 메시지를 받을 수 있습니다.
public DetailViewModel()
{
IsActive = true; // 메시지 수신 시작
}
// 메시지 수신 시 호출되는 메서드입니다.
// 다른 ViewModel에서 StudentSelectedMessage를 전송하면 이 메서드가 자동으로 실행됩니다.
// 메시지 안에 들어있는 Student 객체를 StudentInfo에 저장하면 UI가 자동 갱신됩니다.
public void Receive(StudentSelectedMessage message)
{
StudentInfo = message.SelectedStudent; // 전달받은 학생 정보로 화면 갱신
}
}
}
// MainView는 메인 화면에 해당하는 WPF Window입니다.
// ViewModel(MainViewModel)과 연결되어 있으며, 상세보기 요청 이벤트에 반응합니다.
public partial class MainView : Window
{
// 생성자: MainView가 만들어질 때 실행되는 초기화 메서드입니다.
public MainView()
{
InitializeComponent(); // XAML에 정의된 UI 요소들을 초기화합니다.
// DataContext는 View에 연결된 ViewModel을 의미합니다.
// 이 View에 연결된 ViewModel이 MainViewModel이라면,
// 그 ViewModel에서 발생시키는 이벤트(RequestShowDetail)를 구독합니다.
if (DataContext is MainViewModel vm)
{
// ViewModel이 RequestShowDetail 이벤트를 발생시키면,
// 이 클래스(MainView)의 OnRequestShowDetail 메서드를 실행하도록 연결합니다.
vm.RequestShowDetail += OnRequestShowDetail;
}
}
// ViewModel에서 상세보기 요청 이벤트(RequestShowDetail)가 발생했을 때 실행되는 메서드입니다.
private void OnRequestShowDetail()
{
// View의 DataContext가 MainViewModel이고,
// ViewModel의 SelectedStudent가 null이 아닐 경우에만 실행합니다.
if (DataContext is MainViewModel vm && vm.SelectedStudent != null)
{
// 새로운 상세 보기 창을 생성합니다.
var dlg = new DetailView
{
// 메인 창(MainView)을 부모 창(Owner)으로 지정합니다.
Owner = this
};
// 상세 보기 창이 로드되었을 때 메시지를 전송합니다.
dlg.Loaded += (_, _) =>
{
// 선택된 학생 정보를 포함한 메시지를 생성하여 보냅니다.
// DetailViewModel이 이 메시지를 수신하여 화면을 갱신할 수 있습니다.
WeakReferenceMessenger.Default.Send(new StudentSelectedMessage(vm.SelectedStudent));
};
// 상세 보기 창을 모달 형태로 띄웁니다.
// 사용자가 이 창을 닫을 때까지 메인 창과 상호작용할 수 없습니다.
dlg.ShowDialog();
}
}
}