07.3 ObservableRecipient - 예제3

1️⃣ ObservableRecipient 예제3. 시나리오
더보기

✔️ 1. 동작 시나리오

- MainWindow 리스트에서 항목을 선택하고
- 상세 보기 버튼을 누르면 DetailWindow 에서 선택한 학생의 상세정보와 함께 실행됩니다.
- 로그 보기 버튼을 누르면 LogWindow 에서 로그 기록을 보여줍니다.
2️⃣ 프로젝트 구조
더보기
📁 프로젝트 구조
ProjectName/ ├── Messages/ │ └── StudentSelectedMessage.cs ├── Models/ │ └── Student.cs ├── ViewModels/ │ ├── MainViewModel.cs │ └── DetailViewModel.cs │ └── LogViewModel.cs ├── Views/ │ ├── MainView.xaml │ └── DetailView.xaml │ └── LogView.xaml ├── App.xaml └── App.xaml.cs
3️⃣ Message 구현
더보기

📁 Message

// 이 클래스는 학생(Student) 객체와 대상 화면(TargetView)에 대한 정보를 함께 담아서 // ViewModel 간에 메시지로 전달하기 위한 용도로 사용됩니다. // Messenger를 통해 데이터를 전달할 때, 수신자가 자신에게 온 메시지인지 식별할 수 있도록 돕습니다. public class StudentSelectedMessage { // 전달하고자 하는 학생 정보입니다. // 외부에서는 읽을 수만 있고, 변경은 할 수 없습니다. public Student SelectedStudent { get; } // 이 메시지를 수신해야 할 대상 화면의 이름입니다. // 예: "DetailView", "EditView" 등. 수신자가 메시지 필터링을 할 수 있게 도와줍니다. public string TargetView { get; } // 생성자: 메시지를 생성할 때 학생 정보와 대상 화면 이름을 함께 전달받습니다. // 이 생성자를 통해 속성들이 초기화됩니다. public StudentSelectedMessage(Student student, string targetView) { SelectedStudent = student; // 전달할 학생 객체 저장 TargetView = targetView; // 수신자 식별용 문자열 저장 } }
4️⃣ Model 구현
더보기

📁 Model

// 이 클래스는 한 명의 학생(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}세)"; }
5️⃣ ViewModel 구현
더보기



📁 MainViewModel

using CommunityToolkit.Mvvm.ComponentModel; // ObservableObject, ObservableProperty 사용을 위한 네임스페이스 using CommunityToolkit.Mvvm.Input; // RelayCommand 사용을 위한 네임스페이스 using System.Collections.ObjectModel; // ObservableCollection 사용 using System; // Action 델리게이트 사용 using WpfMvvmToolkit07_ObservableRecipient02.Models; // Student 모델 참조 public partial class MainViewModel : ObservableObject { // 학생 목록을 담는 컬렉션입니다. // ObservableCollection은 UI와 바인딩되어 있을 경우 자동으로 변경 사항이 반영됩니다. public ObservableCollection<Student> Students { get; } = new() { new Student { Name = "홍길동", Age = 20 }, new Student { Name = "김영희", Age = 22 }, new Student { Name = "이철수", Age = 21 }, }; // 사용자가 선택한 학생 정보를 저장하는 속성입니다. // [ObservableProperty]를 붙이면 SelectedStudent 프로퍼티와 PropertyChanged 이벤트가 자동 생성됩니다. [ObservableProperty] private Student? selectedStudent; // 특정 화면(View)의 이름을 인자로 받아서, 해당 화면을 열어달라는 요청을 보낼 이벤트입니다. // View에서는 이 이벤트를 구독하고, 전달받은 문자열을 기준으로 어떤 창을 열지 결정합니다. public event Action<string>? RequestShowDetail; // DetailView 화면을 열어달라고 요청하는 명령입니다. // [RelayCommand]를 붙이면 ShowInDetailViewCommand라는 ICommand가 자동으로 생성됩니다. [RelayCommand] private void ShowInDetailView() { // 학생이 선택된 경우에만 이벤트를 발생시킵니다. if (SelectedStudent is not null) RequestShowDetail?.Invoke("DetailView"); // "DetailView" 라는 문자열로 수신자 구분 } // LogView 화면을 열어달라고 요청하는 명령입니다. // 이 명령은 선택된 학생 정보를 로그 화면에 표시하고자 할 때 사용됩니다. [RelayCommand] private void ShowInLogView() { if (SelectedStudent is not null) RequestShowDetail?.Invoke("LogView"); // "LogView" 라는 문자열로 수신자 구분 } }
📁 DetailViewModel

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입니다. // 메시지를 통해 선택된 학생 정보를 받아서 화면에 표시합니다. public partial class DetailViewModel : ObservableRecipient, IRecipient<StudentSelectedMessage> { // 생성자: ViewModel이 메시지를 수신할 수 있도록 IsActive를 true로 설정합니다. // ObservableRecipient는 IsActive가 true일 때만 메시지를 받을 수 있습니다. public DetailViewModel() { IsActive = true; // 메시지 수신 시작 } // 메시지 수신 메서드: StudentSelectedMessage를 받으면 자동으로 실행됩니다. // 메시지 안의 TargetView가 "DetailView"일 때만 해당 데이터를 수신하도록 필터링합니다. public void Receive(StudentSelectedMessage message) { if (message.TargetView == "DetailView") { // 메시지로 전달받은 학생 정보를 StudentInfo에 저장 // StudentInfo 속성은 View에 바인딩되어 있으므로 자동으로 화면에 반영됩니다. StudentInfo = message.SelectedStudent; } } // 수신한 학생 정보를 저장하는 프로퍼티입니다. // ObservableProperty 특성을 사용하면, 자동으로 StudentInfo 속성과 변경 알림이 생성됩니다. // View에서는 이 속성을 바인딩하여 학생 정보를 표시할 수 있습니다. [ObservableProperty] private Student? studentInfo; } }
📁 LogViewModel

using CommunityToolkit.Mvvm.ComponentModel; // ObservableRecipient, ObservableProperty 사용 using CommunityToolkit.Mvvm.Messaging; // 메시지 수신 인터페이스 사용 using WpfMvvmToolkit07_ObservableRecipient02.Messages; // StudentSelectedMessage 메시지 참조 namespace WpfMvvmToolkit07_ObservableRecipient02.ViewModels { // LogViewModel은 선택된 학생 정보를 받아 로그 형태의 문자열로 저장하는 역할을 합니다. // 이 ViewModel은 메시지를 수신할 수 있도록 ObservableRecipient를 상속받고, // 특정 메시지를 받기 위해 IRecipient<StudentSelectedMessage> 인터페이스를 구현합니다. public partial class LogViewModel : ObservableRecipient, IRecipient<StudentSelectedMessage> { // 생성자: 메시지 수신을 활성화합니다. // ObservableRecipient는 IsActive가 true일 때만 메시지를 수신할 수 있습니다. public LogViewModel() { IsActive = true; // 메시지 수신 시작 } // 메시지를 수신했을 때 자동으로 호출되는 메서드입니다. // 메시지의 TargetView가 "LogView"인 경우에만 메시지를 처리합니다. public void Receive(StudentSelectedMessage message) { if (message.TargetView == "LogView") { // 전달받은 학생 정보를 로그 문자열로 가공하여 Log 속성에 저장합니다. // 이 속성은 View와 바인딩되어 있으므로 자동으로 UI에 표시됩니다. Log = $"[{message.SelectedStudent.Name}] 선택됨 - 나이 {message.SelectedStudent.Age}"; } } // 로그 메시지를 저장하는 속성입니다. // [ObservableProperty]를 사용하면 Log 속성과 변경 알림 기능이 자동으로 생성됩니다. // View에서는 이 속성을 바인딩하여 로그 메시지를 표시할 수 있습니다. [ObservableProperty] private string? log; } }
6️⃣ View 구현
더보기




📁 MainWindow

xmlns:vm="clr-namespace:WpfMvvmToolkit07_ObservableRecipient03.ViewModels" Title="학생 목록" Height="300" Width="400"> <Window.DataContext> <vm:MainViewModel /> </Window.DataContext> <StackPanel Margin="10"> <ListBox ItemsSource="{Binding Students}" SelectedItem="{Binding SelectedStudent, Mode=TwoWay}" Height="150"/> <StackPanel Orientation="Horizontal" HorizontalAlignment="Center"> <Button Content="상세 보기" Command="{Binding ShowInDetailViewCommand}" Margin="5" Width="100"/> <Button Content="로그 보기" Command="{Binding ShowInLogViewCommand}" Margin="5" Width="100"/> </StackPanel> </StackPanel>
📁 MainWindow - Code Behind

// 이 메서드는 전달받은 문자열 target에 따라 다른 창(윈도우)을 띄우고, // 선택된 학생(Student) 정보를 메시지로 함께 전달하는 역할을 합니다. private void ShowTargetWindow(string target) { // 1. 현재 View의 DataContext가 MainViewModel인지 확인합니다. // 2. ViewModel에 SelectedStudent(선택된 학생)가 설정되어 있는지 확인합니다. if (DataContext is MainViewModel vm && vm.SelectedStudent != null) { // target 문자열 값에 따라 새로 열 창을 결정합니다. // switch 표현식을 사용하여 target이 "DetailView"면 DetailWindow를, // "LogView"면 LogWindow를 생성합니다. // 그 외의 값은 null을 반환합니다. Window? dialog = target switch { "DetailView" => new DetailWindow(), // 상세 보기 창 "LogView" => new LogWindow(), // 로그 보기 창 _ => null // 그 외의 경우는 창 없음 }; // 창이 유효하게 생성되었는지 확인한 후 실행합니다. if (dialog != null) { // 창이 화면에 완전히 표시되었을 때 실행할 이벤트를 등록합니다. // Loaded 이벤트는 창이 모두 그려졌을 때 발생합니다. dialog.Loaded += (sender, args) => { // 선택된 학생 정보와 창의 대상 이름을 담은 메시지를 생성합니다. var message = new StudentSelectedMessage(vm.SelectedStudent, target); // 메시지를 전송하여 해당 ViewModel이 이 메시지를 수신할 수 있도록 합니다. WeakReferenceMessenger.Default.Send(message); }; // 새로 열 창의 부모 창(owner)을 현재 창(this)으로 설정합니다. // 이렇게 하면 자식 창이 부모 창 위에 항상 떠 있게 됩니다. dialog.Owner = this; // 창을 모달 모드로 띄웁니다. // 사용자가 이 창을 닫기 전까지는 다른 창과 상호작용할 수 없습니다. dialog.ShowDialog(); } } }
📁 DetailWindow

xmlns:vm="clr-namespace:WpfMvvmToolkit07_ObservableRecipient03.ViewModels" Title="상세 정보" Height="200" Width="300" WindowStartupLocation="CenterOwner"> <Window.DataContext> <vm:DetailViewModel /> </Window.DataContext> <StackPanel Margin="20"> <TextBlock Text="학생 정보" FontWeight="Bold" FontSize="16" Margin="0,0,0,10"/> <TextBlock Text="{Binding StudentInfo.Name}" FontSize="14" Margin="0,5"/> <TextBlock Text="{Binding StudentInfo.Age}" FontSize="14" Margin="0,5"/> </StackPanel>
📁 LogWindow

xmlns:vm="clr-namespace:WpfMvvmToolkit07_ObservableRecipient03.ViewModels" Title="로그 뷰" Height="200" Width="300" WindowStartupLocation="CenterOwner"> <Window.DataContext> <vm:LogViewModel /> </Window.DataContext> <Grid> <TextBlock Text="{Binding Log}" FontSize="14" HorizontalAlignment="Center" VerticalAlignment="Center"/> </Grid>
댓글을 사용할 수 없습니다.