1️⃣ 
ObservableRecipient 예제4. 시나리오

더보기

✔️ 1. 동작 시나리오

  • MainWindow 리스트에서 항목을 선택하고
  • 상세 보기 버튼을 누르면 DetailWindow 에서 선택한 학생의 상세정보와 함께 실행됩니다.
  • 이름과 나이를 수정하고
  • 정보 반환 버튼을 누르면
  • 수정된 내용이 MainWindow 리스트에 반영됩니다.

 

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

using CommunityToolkit.Mvvm.Messaging.Messages; // Toolkit에서 제공하는 메시지 기반 클래스 사용
using WpfMvvmToolkit07_ObservableRecipient04.Models; // Student 모델 참조

namespace WpfMvvmToolkit07_ObservableRecipient04.Messages
{
    // 이 클래스는 선택된 학생 정보를 다른 ViewModel이나 화면에 전달할 때 사용하는 메시지입니다.
    // 메시지 안에는 Student 객체와 대상 View 이름(TargetView)이 함께 들어 있습니다.
    public class StudentSelectedMessage
    {
        // 선택된 학생 정보를 담는 속성입니다. 읽기 전용입니다.
        public Student SelectedStudent { get; }

        // 메시지를 수신해야 할 View 이름입니다.
        // 예: "DetailView", "LogView" 등의 식별자로 사용됩니다.
        public string TargetView { get; }

        // 생성자: 메시지를 생성할 때 학생 정보와 대상 View 이름을 설정합니다.
        public StudentSelectedMessage(Student student, string targetView)
        {
            SelectedStudent = student;
            TargetView = targetView;
        }
    }

    // 이 메시지는 학생(Student) 객체를 요청(Request)할 때 사용됩니다.
    // 수신자는 이 메시지를 받고, 적절한 Student 객체를 응답으로 반환합니다.
    public class StudentRequestMessage : RequestMessage<Student> { }

    // 이 메시지는 학생(Student) 객체를 반환(Return)할 때 사용됩니다.
    // 일반적으로 RequestMessage 응답용으로 활용되며, 비동기 응답 처리에도 사용됩니다.
    public class StudentReturnMessage : RequestMessage<Student> { }
}

 

4️⃣  Model 구현

더보기

📁 Model

using CommunityToolkit.Mvvm.ComponentModel; // ObservableObject와 ObservableProperty 사용을 위한 네임스페이스

// Student 클래스는 학생 한 명의 이름과 나이를 저장하는 데이터 모델입니다.
// MVVM 구조에서 ViewModel과 View가 다룰 수 있는 바인딩 가능한 데이터 구조로 사용됩니다.
// ObservableObject를 상속하면, 속성 값이 바뀔 때 자동으로 View에 알림을 보낼 수 있습니다.

public partial class Student : ObservableObject
{
    // 학생의 이름을 저장하는 속성입니다.
    // [ObservableProperty]를 사용하면 자동으로 Name 프로퍼티와 PropertyChanged 알림이 생성됩니다.
    // 이 속성이 바뀌면 UI에서도 자동으로 값이 업데이트됩니다.
    [ObservableProperty]
    private string name = string.Empty; // 기본값: 빈 문자열

    // 학생의 나이를 저장하는 속성입니다.
    // 역시 [ObservableProperty]를 통해 자동으로 Age 프로퍼티가 생성되고, 변경 시 알림이 발생합니다.
    [ObservableProperty]
    private int age;

    // 이 메서드는 객체를 문자열로 표현할 때 사용됩니다.
    // 예: ToString()을 호출하면 "홍길동 (20세)" 와 같은 형식으로 출력됩니다.
    public override string ToString() => $"{Name} ({Age}세)";
}


5️⃣ ViewModel 
구현

더보기

📁 MainViewModel

using CommunityToolkit.Mvvm.ComponentModel; // ObservableObject와 ObservableProperty 사용
using CommunityToolkit.Mvvm.Input;       // RelayCommand 사용
using CommunityToolkit.Mvvm.Messaging;   // Messenger 메시지 기능 사용
using System;                            // Action 대리자 사용
using System.Collections.ObjectModel;   // ObservableCollection 사용
using System.Linq;                       // FirstOrDefault 등 LINQ 사용
using WpfMvvmToolkit07_ObservableRecipient02.Messages; // 메시지 클래스들 참조

public partial class MainViewModel : ObservableObject, IRecipient<Student>
{
    // 학생 리스트를 저장하는 ObservableCollection입니다.
    // UI와 바인딩되어 있어 항목이 추가되거나 변경되면 자동으로 반영됩니다.
    public ObservableCollection<Student> Students { get; } = new()
    {
        new Student { Name = "홍길동", Age = 20 },
        new Student { Name = "김영희", Age = 22 },
        new Student { Name = "이철수", Age = 21 },
    };

    // 사용자가 선택한 학생을 저장하는 프로퍼티입니다.
    // ObservableProperty 어트리뷰트로 자동으로 프로퍼티와 변경 알림이 생성됩니다.
    [ObservableProperty]
    private Student? selectedStudent;

    // View에게 특정 화면을 열어달라고 요청하는 이벤트입니다.
    // 문자열 인자로 어떤 화면("DetailView" 등)을 열지 구분합니다.
    public event Action<string>? RequestShowDetail;

    // 생성자: 이 ViewModel이 Student 타입 메시지를 받을 수 있도록 등록합니다.
    public MainViewModel()
    {
        WeakReferenceMessenger.Default.Register<Student>(this);
    }

    // 메시지를 수신하면 실행되는 메서드입니다.
    // Student 객체를 메시지로 받으면 SelectedStudent에 저장합니다.
    public void Receive(Student message)
    {
        SelectedStudent = message;
    }

    // 상세 보기 화면을 요청하는 커맨드입니다.
    // 선택된 학생이 있을 때만 RequestShowDetail 이벤트를 발생시킵니다.
    [RelayCommand]
    private void ShowInDetailView()
    {
        if (SelectedStudent is not null)
            RequestShowDetail?.Invoke("DetailView");
    }

    // 상세 보기 화면에서 수정된 학생 정보를 요청하여 가져오는 커맨드입니다.
    // StudentReturnMessage 메시지를 보내고, 응답이 있다면 해당 학생 정보를 업데이트합니다.
    [RelayCommand]
    private void ReturnStudentFromDetail()
    {
        // 메시지를 전송하고 응답을 받습니다.
        var response = WeakReferenceMessenger.Default.Send<StudentReturnMessage>();

        // 응답이 있다면
        if (response.HasReceivedResponse)
        {
            var updated = response.Response; // 반환된 학생 정보

            // 기존 학생 목록에서 동일한 이름의 학생을 찾습니다.
            var existing = Students.FirstOrDefault(s => s.Name == updated.Name);

            if (existing != null)
            {
                // 기존 학생 정보 업데이트
                existing.Name = updated.Name;
                existing.Age = updated.Age;

                // 선택된 학생 변경 알림을 유도하여 UI 갱신
                SelectedStudent = null;
                SelectedStudent = existing;
            }
            else
            {
                // 목록에 없으면 새로 추가합니다.
                Students.Add(updated);
                SelectedStudent = updated;
            }
        }
    }
}

 

📁 DetailViewModel

 



6️⃣ 
View 구현

더보기

📁 MainWindow

    xmlns:vm="clr-namespace:WpfMvvmToolkit07_ObservableRecipient04.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">
        <ListBox.ItemTemplate>
            <DataTemplate>
                <StackPanel Orientation="Horizontal">
                    <TextBlock Text="{Binding Name}"/>
                    <TextBlock Text="(" />
                    <TextBlock Text="{Binding Age}" />
                    <TextBlock Text="세)" />
                </StackPanel>
            </DataTemplate>
        </ListBox.ItemTemplate>
    </ListBox>

    <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

public partial class MainView : Window
{
    public MainView()
    {
        InitializeComponent();

        if (DataContext is MainViewModel vm)
        {
            vm.RequestShowDetail += ShowTargetWindow;
        }
    }

    private void ShowTargetWindow(string target)
    {
        if (DataContext is MainViewModel vm && vm.SelectedStudent != null)
        {
            Window? dialog = target switch
            {
                "DetailView" => new DetailView(),
                "LogView" => new LogView(),
                _ => null
            };

            if (dialog != null)
            {
                dialog.Loaded += (_, _) =>
                {
                    WeakReferenceMessenger.Default.Send(new StudentSelectedMessage(vm.SelectedStudent, target));
                };
                dialog.Owner = this;
                dialog.ShowDialog();
            }
        }
    }
}

 

📁 DetailWindow

<Window x:Class="WpfMvvmToolkit07_ObservableRecipient04.Views.DetailView"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        
        xmlns:vm="clr-namespace:WpfMvvmToolkit07_ObservableRecipient04.ViewModels"
        Title="상세 정보" Height="250" Width="300" WindowStartupLocation="CenterOwner">
    
    <Window.DataContext>
        <vm:DetailViewModel />
    </Window.DataContext>

    <Grid Margin="10">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>

        <TextBlock Text="학생 정보" FontSize="16" FontWeight="Bold" Grid.Row="0"/>

        <StackPanel Grid.Row="1" Margin="0,10,0,0">
            <StackPanel Orientation="Horizontal">
                <TextBlock Text="이름: " Width="50"/>
                <TextBox Text="{Binding StudentInfo.Name, UpdateSourceTrigger=PropertyChanged}" Width="200"/>
            </StackPanel>
            <StackPanel Orientation="Horizontal" Margin="0,5,0,0">
                <TextBlock Text="나이: " Width="50"/>
                <TextBox Text="{Binding StudentInfo.Age, UpdateSourceTrigger=PropertyChanged}" Width="200"/>
            </StackPanel>
        </StackPanel>

        <Button Content="정보 반환" Grid.Row="2" Height="30" Margin="0,20,0,0"
                HorizontalAlignment="Center" Width="100"
                Click="OnReturnButtonClick"/>
    </Grid>
    
</Window>