[WPF/CommunityToolkit.Mvvm] 4. WeakReferenceMessenger 사용

 MVVM 패턴으로 개발을 하다보면 서로 다른 ViewModel 끼리나 ViewModel에서 View로 1:1 혹은 1:N으로 이벤트를 전달해야 하는 경우들이 많습니다. 이 경우 static class를 생성해서 전역에서 접근 가능한 event를 만들어 처리하거나  service class를 만들어서 의존성 주입을 통해 여러 ViewModel에서 접근 가능한 event 변수를 만들어 처리하는 등 여러가지 방법이 있습니다만 CommunityToolkit에서 제공하는 WeakReferenceMessenger 클래스를 사용하면 간단히  Pub/Sub 형태의 이벤트 큐를 구현 할 수 있습니다.

 단순해서 사용하기 편리하지만 실무에서는 event 스트림에 debounce time을 적용한다거나 이벤트 버퍼를 ring 버퍼 형태로 써야한다거나 하는 경우들도 많은데 WeakReferenceMessenger로는 불가능하기 때문에 조금 아쉬운 점이긴 합니다.

 

 간단한 사용법을 알아보기 위해 지난번 글에서 작성해둔 코드를 기준으로 설명하겠습니다.

 

 목표: View에서 버튼을 누르면 WeakReferenceMessager를 사용해서 MainWindowViewModel 에서 MainWindowView로 메세지를 보내서 TextBox 컨트롤에 "Hello World"를 출력하도록 합니다.

 

  View -> ViewModel로의 이벤트는 바인딩을 통해 쉽게 할 수 있지만 ViewModel에서 임의의 이벤트를 발생시켜 View에서 특정 동작을 하게 만들기는 쉽지 않기때문에 WeakReferenceMessenger를 사용해서 이를 구현해봅니다.

 

1. Message 클래스 생성

프로젝트에 Messages 폴더를 하나 생성하고 TestMessage 클래스 파일을 생성합니다.

using CommunityToolkit.Mvvm.Messaging.Messages;

namespace MyTestApp.Messages
{
    public class TestMessage : ValueChangedMessage<string>
    {
        public TestMessage(string text) : base(text)
        {
        }
    }
}

 

2. View에서 Message 수신을 위해 등록하기

MainWindow.xaml.cs

using CommunityToolkit.Mvvm.Messaging;
using MyTestApp.Messages;
using System.Windows;

namespace MyTestApp
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();

            WeakReferenceMessenger.Default.Register<TestMessage>(this, (r, m) =>
            {
                ctlTextBox.Text = m.Value;
            });
        }
    }
}

 

3. View에 버튼과 TextBox 컨트롤 추가하고 ViewModel의 property/command와 바인딩 후 ViewModel에서 버튼 click시 Message 보내도록 코드 작성

 

MainWindowViewModel.cs

using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using CommunityToolkit.Mvvm.Messaging;
using MyTestApp.Messages;

namespace MyTestApp.ViewModels
{
    public partial class MainWindowViewModel : ObservableObject
    {
        [ObservableProperty]
        private string title;

        public MainWindowViewModel()
        {
            Title = "초기값";
        }

        [RelayCommand]
        private void SendMessage()
        {
            WeakReferenceMessenger.Default.Send(new TestMessage("Hello World"));
        }
    }
}

 

MainWindow.xaml

<Window x:Class="MyTestApp.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:MyTestApp"
        mc:Ignorable="d"
        Title="MainWindow"
        Height="450"
        Width="800"
        DataContext="{Binding Source={StaticResource VmLocator}, Path=MainWindowVM}">
    <Grid>
        <StackPanel Orientation="Vertical">
            <TextBox Text="{Binding Title}" />
            <Button Content="클릭하세요"
                    Command="{Binding SendMessageCommand}" />
            <TextBox x:Name="ctlTextBox" />
        </StackPanel>
    </Grid>
</Window>

 

이제 컴파일 후 실행해서 버튼을 클릭하면 TextBox에 "Hello World"가 출력되는걸 볼 수 있습니다.