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>