1️⃣ ObservableProperty 개요

더보기

📚 Microsoft Docs: Microsoft CommunityToolkit MVVM - ObservableObject

 

 

✅ ObservableProperty 개요

  • ObservableObject는 MVVM Toolkit에서 제공하는 MVVM의 핵심 클래스입니다.
  • ObservableObject는 INotifyPropertyChanged, INotifyPropertyChanging 인터페이스가 구현되어 있습니다.
  • 즉, MVVM 구조에서 Observable하지 않은 기존 객체를 MVVM에 통합하는 핵심 기술입니다.

 

 

✅ ObservableProperty 사용법

 


 예제  소스 코드

 

WPF_ToolKit06.zip
0.14MB

 


 

2️⃣ 비교 ( 전통적 구현 vs ObservableObject  vs [ObservableProperty] )

더보기

✔️  1. 동작 시나리오

  1. 공통 시나리오: 이름(Name)을 입력하고 
  2. 인사 메시지(Greeting)를 출력
WpfApp1.zip
0.00MB

✔️ 2. 구현


 2.1 (공통) XAML 구현

        xmlns:vm="clr-namespace:WpfApp1.ViewModel"
        Title="INotifyPropertyChanged 비교" Height="200" Width="300">
        
    <Window.DataContext>
        <vm:ManualViewModel />
        <!-- 변경 가능: ManualViewModel, ObjectViewModel -->
    </Window.DataContext>

    <StackPanel Margin="20">
        <TextBox Text="{Binding Name, UpdateSourceTrigger=PropertyChanged}" Margin="0 0 0 10" />
        <TextBlock Text="{Binding Greeting}" FontSize="16" />
    </StackPanel>
</Window>

 

2.2 INotifyPropertyChanged

class ManualViewModel : INotifyPropertyChanged
{
    private string? name;
    public string Name
    {
        get => name ?? string.Empty;
        set
        {
            if (name != value)
            {
                name = value;
                OnPropertyChanged(nameof(Name));
                OnPropertyChanged(nameof(Greeting));
            }
        }
    }

    public string Greeting => $"안녕하세요, {Name}님!";

    public event PropertyChangedEventHandler? PropertyChanged;

    protected void OnPropertyChanged(string propertyName)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

🟢 장점

  • Source Generator 없이 작동
  • 완전한 제어 가능

🔴 단점

  • 매 속성마다 반복되는 boilerplate 코드

 

2.2 [ObservableProperty] 

public partial class AttributeViewModel : ObservableObject
{
    [ObservableProperty]
    [NotifyPropertyChangedFor(nameof(Greeting))]
    private string? name;

    public string Greeting => $"안녕하세요, {Name}님!";
}

🟢 장점

  • 가장 간결함 (속성 한 줄이면 끝)
  • OnNameChanged 같은 partial method 지원
  • 관련된 속성 알림도 연동 가능 (NotifyPropertyChangedFor)
  • 커맨드 상태 자동 갱신도 가능 (NotifyCanExecuteChangedFor)

🔴 단점

  • C# Source Generator 기능이 필요한 환경에서만 작동 (Visual Studio 2019+ / .NET 5+)

 

2.3 ObservableObject 

public class ObjectViewModel : ObservableObject
{
    private string? name;
    public string Name
    {
        get => name ?? string.Empty;
        set
        {
            if (SetProperty(ref name, value))
            {
                OnPropertyChanged(nameof(Greeting));
            }
        }
    }

    public string Greeting => $"안녕하세요, {Name}님!";
}
SetProperty 매개변수 설명
ref name 실제 저장되는 필드
value 새로 설정할 값
내부 처리 값이 다를 경우 필드를 변경하고 PropertyChanged 이벤트 발생

 

🟢 장점

  • SetProperty를 통해 OnPropertyChanged를 깔끔하게 처리
  • Source Generator 없이도 깔끔한 구조
  • 커스텀 로직을 섞기 용이함

🔴 단점

  • 여전히 property 선언을 직접 작성해야 함
  • [ObservableProperty]보다 코드량 많음

 


✔️
 3. MVVM 구현 방식 비교

 

방식 핵심 키워드 코드 작성량 유지보수성 가독성 Source Generator
전통적 방식 INotifyPropertyChanged 수동 구현 ❌ 많음 ❌ 낮음 ❌ 불편 ❌ 필요 없음
Toolkit 자동 [ObservableProperty]
+ [INotifyPropertyChanged]
✅ 매우 적음 ✅ 높음 ✅ 매우 좋음 ✅ 필요
Toolkit 수동 ObservableObject 상속
 SetProperty 수동 작성
🔸 보통 🔸 보통 🔸 명시적 ✅ 필요


3️⃣ 예제1

더보기

✔️ 구현 목표

 

  1. ObservableObject + SetProperty 방식과
    [ObservableProperty] 방식의 차이점을 직접 구현하여 비교할 수 있도록 구현


📁 프로젝트 구조

ObservableComparisonDemo/
├── ViewModel/
│   ├── SetPropertyViewModel.cs
│   └── ObservablePropertyViewModel.cs
├── MainWindow.xaml
└── MainWindow.xaml.cs

 

 

 

✔️ ViewModel 클래스 만들기 (ObservablePropertyViewModel.cs)

    public partial class ObservablePropertyViewModel : ObservableObject
    {
        // 자동으로 public Name 속성 생성 + PropertyChanged 알림
        [ObservableProperty]
        private string? name;
    }

 

 

 


✔️ ViewModel 클래스 만들기 (SetPropertyViewModel.cs)

public class SetPropertyViewModel : ObservableObject
{
    private string name = string.Empty;

    public string Name
    {
        get => name;

        set => SetProperty(ref name, value); // 수동 구현
    }
}

 

 

 

 

✔️ XAML 화면 구성

    Title="ObservableProperty 비교 실습" Height="250" Width="400">

<StackPanel Margin="20">

    <TextBlock Text="① SetProperty 방식 (수동)" FontWeight="Bold"/>
    <StackPanel Orientation="Vertical">
        <TextBox Text="{Binding Name, UpdateSourceTrigger=PropertyChanged}" Margin="0,0,0,5"/>
        <TextBlock Text="{Binding Name}" />
    </StackPanel>

    <Separator Margin="10"/>

    <TextBlock Text="② ObservableProperty 방식 (자동)" FontWeight="Bold"/>
    <StackPanel Orientation="Vertical">
        <TextBox Text="{Binding Name, UpdateSourceTrigger=PropertyChanged}" Margin="0,0,0,5"/>
        <TextBlock Text="{Binding Name}" />
    </StackPanel>
</StackPanel>

 

 

 


✔️ XAML 코드 비하인드 구성

// 아래 둘 중 하나를 주석 해제하여 실습
// DataContext = new SetPropertyViewModel();
DataContext = new ObservablePropertyViewModel();

 

 

 

 


4️⃣ 예제2

더보기

✔️ 구현 목표

 

  1. 탭 전환 비교
    UI ObservableObject + SetProperty 방식 VS [ObservableProperty] 방식 비교
  2. OnChanged 후처리 비교
    SetProperty 방식의 조건부 후처리 vs [ObservableProperty]의 partial void On___Changed() 활용


📁 프로젝트 구조

WPF_ToolKit06_Ex01/
├── ViewModel/
│   ├── SetPropertyViewModel.cs
│   └── ObservablePropertyViewModel.cs
├── MainWindow.xaml
└── MainWindow.xaml.cs

 

 

 

✔️ ViewModel 클래스 만들기 (ObservablePropertyViewModel.cs)

using CommunityToolkit.Mvvm.ComponentModel;

namespace WPF_ToolKit06_Ex01.ViewModels
{
    // SetPropertyViewModel은 MVVM Toolkit의 ObservableObject를 상속받아
    // INotifyPropertyChanged를 간편하게 구현하는 ViewModel입니다.
    // 이 클래스에서는 수동으로 속성을 정의하고, SetProperty() 메서드를 통해 속성 변경 시 알림을 발생시킵니다.
    public class SetPropertyViewModel : ObservableObject
    {
        // 이름을 저장하는 private 필드
        private string name = string.Empty;

        // 사용자에게 보여줄 메시지를 저장하는 private 필드
        private string message = string.Empty;

        // Name 속성은 사용자의 입력값을 바인딩합니다.
        // set 접근자 내부에서 SetProperty()를 사용하면 값이 변경될 때 자동으로 UI에 알림이 전파됩니다.
        // 또한 값이 바뀐 이후에 메시지를 업데이트하는 후처리 로직을 직접 구현할 수 있습니다.
        public string Name
        {
            get => name;
            set
            {
                // 값이 실제로 바뀌었을 때만 PropertyChanged 이벤트 발생
                if (SetProperty(ref name, value))
                {
                    // 후처리: 메시지 속성에 변경 내용을 저장
                    Message = $"[SetProperty] 이름이 '{name}'으로 변경되었습니다.";
                }
            }
        }

        // Message 속성은 후처리 결과를 화면에 출력하기 위해 바인딩됩니다.
        // SetProperty를 통해 변경되었음을 UI에 알립니다.
        public string Message
        {
            get => message;
            set => SetProperty(ref message, value);
        }
    }
}

 

 

 


✔️ ViewModel 클래스 만들기 (SetPropertyViewModel.cs)

using CommunityToolkit.Mvvm.ComponentModel;

namespace WPF_ToolKit06_Ex01.ViewModels
{
    // ObservablePropertyViewModel 클래스는 MVVM Toolkit의 기능을 활용하여
    // 속성을 간결하게 정의하고, 변경 알림 및 후처리까지 자동화하는 예제입니다.
    // ObservableObject를 상속하여 INotifyPropertyChanged를 내부적으로 처리하며,
    // [ObservableProperty] 특성을 사용해 필드 기반으로 속성을 자동 생성합니다.
    public partial class ObservablePropertyViewModel : ObservableObject
    {
        // [ObservableProperty]를 선언하면 다음과 같은 기능이 자동 생성됩니다:
        // - public string Name { get; set; } 속성
        // - 값이 변경되면 PropertyChanged 이벤트 자동 발생
        // - partial void OnNameChanged(string value) 메서드 호출

        [ObservableProperty]
        private string name = string.Empty;

        [ObservableProperty]
        private string message = string.Empty;

        // OnNameChanged 메서드는 Name 속성의 값이 변경되었을 때 자동 호출됩니다.
        // 여기서는 후처리로 Message 속성을 업데이트하여 UI에 알림 메시지를 전달합니다.
        // MVVM Toolkit은 이러한 후처리 메서드를 partial 메서드로 지원합니다.
        partial void OnNameChanged(string value)
        {
            Message = $"[ObservableProperty] 이름이 '{value}'으로 변경되었습니다.";
        }
    }
}

 

 

 

 

✔️ XAML 화면 구성

        xmlns:vm="clr-namespace:WPF_ToolKit06_Ex01.ViewModels"
        Title="비교 실습" Height="300" Width="400">

    <!-- ViewModel 인스턴스를 정적으로 생성하여 리소스로 등록 -->
    <Window.Resources>
        <!-- SetProperty 방식 ViewModel 등록 -->
        <vm:SetPropertyViewModel x:Key="SetVM"/>
        <!-- ObservableProperty 방식 ViewModel 등록 -->
        <vm:ObservablePropertyViewModel x:Key="ObsVM"/>
    </Window.Resources>

    <Grid>
        <!-- TabControl을 사용하여 두 가지 ViewModel의 차이를 비교 실습할 수 있도록 구성 -->
        <TabControl Margin="10">

            <!-- 탭 1: 수동 방식 (SetProperty) -->
            <TabItem Header="SetProperty 방식">
                <!-- 해당 탭의 DataContext는 SetVM 리소스(ViewModel) -->
                <StackPanel DataContext="{StaticResource SetVM}" Margin="10">
                    <TextBlock Text="이름 입력:"/>
                    <!-- TextBox: Name 속성과 양방향 바인딩. 텍스트가 변경될 때마다 ViewModel에 전달됨 -->
                    <TextBox Text="{Binding Name, UpdateSourceTrigger=PropertyChanged}" />

                    <TextBlock Text="메시지:" Margin="0,10,0,0"/>
                    <!-- 메시지 출력: ViewModel에서 후처리로 생성된 문자열 출력 -->
                    <TextBlock Text="{Binding Message}" FontWeight="Bold"/>
                </StackPanel>
            </TabItem>

            <!-- 탭 2: 자동 방식 (ObservableProperty) -->
            <TabItem Header="ObservableProperty 방식">
                <!-- 해당 탭의 DataContext는 ObsVM 리소스(ViewModel) -->
                <StackPanel DataContext="{StaticResource ObsVM}" Margin="10">
                    <TextBlock Text="이름 입력:"/>
                    <!-- ObservableProperty에 의해 자동 생성된 Name 속성과 바인딩 -->
                    <TextBox Text="{Binding Name, UpdateSourceTrigger=PropertyChanged}" />

                    <TextBlock Text="메시지:" Margin="0,10,0,0"/>
                    <!-- OnNameChanged()를 통해 자동 생성된 메시지 출력 -->
                    <TextBlock Text="{Binding Message}" FontWeight="Bold"/>
                </StackPanel>
            </TabItem>
        </TabControl>
    </Grid>
</Window>

 

 

 

 


5️⃣ 예제3 - Wrapping a Non-Observable Model

더보기

✔️ 구현 목표


항목 설명
문제 상황 Model은 DB 엔터티나 외부 API 결과 등 외부 구조체인 경우 많음
제약 대부분 INotifyPropertyChanged를 구현하지 않음 (바인딩 불가)
해결 방법 ViewModel이 Model을 "감싸고(wrap)" 속성을 노출하며 변경 감지를 구현
핵심 도구 ObservableObject + ViewModel 속성에서 SetProperty() 사용


📁 프로젝트 구조

WPF_ToolKit06_Ex01/
├── ViewModel/
│   ├── SetPropertyViewModel.cs
│   └── ObservablePropertyViewModel.cs
├── MainWindow.xaml
└── MainWindow.xaml.cs

 

 

 

✔️ ViewModel 클래스 만들기 (ObservablePropertyViewModel.cs)

using CommunityToolkit.Mvvm.ComponentModel;

namespace WPF_ToolKit06_Ex01.ViewModels
{
    // SetPropertyViewModel은 MVVM Toolkit의 ObservableObject를 상속받아
    // INotifyPropertyChanged를 간편하게 구현하는 ViewModel입니다.
    // 이 클래스에서는 수동으로 속성을 정의하고, SetProperty() 메서드를 통해 속성 변경 시 알림을 발생시킵니다.
    public class SetPropertyViewModel : ObservableObject
    {
        // 이름을 저장하는 private 필드
        private string name = string.Empty;

        // 사용자에게 보여줄 메시지를 저장하는 private 필드
        private string message = string.Empty;

        // Name 속성은 사용자의 입력값을 바인딩합니다.
        // set 접근자 내부에서 SetProperty()를 사용하면 값이 변경될 때 자동으로 UI에 알림이 전파됩니다.
        // 또한 값이 바뀐 이후에 메시지를 업데이트하는 후처리 로직을 직접 구현할 수 있습니다.
        public string Name
        {
            get => name;
            set
            {
                // 값이 실제로 바뀌었을 때만 PropertyChanged 이벤트 발생
                if (SetProperty(ref name, value))
                {
                    // 후처리: 메시지 속성에 변경 내용을 저장
                    Message = $"[SetProperty] 이름이 '{name}'으로 변경되었습니다.";
                }
            }
        }

        // Message 속성은 후처리 결과를 화면에 출력하기 위해 바인딩됩니다.
        // SetProperty를 통해 변경되었음을 UI에 알립니다.
        public string Message
        {
            get => message;
            set => SetProperty(ref message, value);
        }
    }
}

 

 

 


✔️ ViewModel 클래스 만들기 (SetPropertyViewModel.cs)

using CommunityToolkit.Mvvm.ComponentModel;

namespace WPF_ToolKit06_Ex01.ViewModels
{
    // ObservablePropertyViewModel 클래스는 MVVM Toolkit의 기능을 활용하여
    // 속성을 간결하게 정의하고, 변경 알림 및 후처리까지 자동화하는 예제입니다.
    // ObservableObject를 상속하여 INotifyPropertyChanged를 내부적으로 처리하며,
    // [ObservableProperty] 특성을 사용해 필드 기반으로 속성을 자동 생성합니다.
    public partial class ObservablePropertyViewModel : ObservableObject
    {
        // [ObservableProperty]를 선언하면 다음과 같은 기능이 자동 생성됩니다:
        // - public string Name { get; set; } 속성
        // - 값이 변경되면 PropertyChanged 이벤트 자동 발생
        // - partial void OnNameChanged(string value) 메서드 호출

        [ObservableProperty]
        private string name = string.Empty;

        [ObservableProperty]
        private string message = string.Empty;

        // OnNameChanged 메서드는 Name 속성의 값이 변경되었을 때 자동 호출됩니다.
        // 여기서는 후처리로 Message 속성을 업데이트하여 UI에 알림 메시지를 전달합니다.
        // MVVM Toolkit은 이러한 후처리 메서드를 partial 메서드로 지원합니다.
        partial void OnNameChanged(string value)
        {
            Message = $"[ObservableProperty] 이름이 '{value}'으로 변경되었습니다.";
        }
    }
}

 

 

 

 

✔️ XAML 화면 구성

        xmlns:vm="clr-namespace:WPF_ToolKit06_Ex01.ViewModels"
        Title="비교 실습" Height="300" Width="400">

    <!-- ViewModel 인스턴스를 정적으로 생성하여 리소스로 등록 -->
    <Window.Resources>
        <!-- SetProperty 방식 ViewModel 등록 -->
        <vm:SetPropertyViewModel x:Key="SetVM"/>
        <!-- ObservableProperty 방식 ViewModel 등록 -->
        <vm:ObservablePropertyViewModel x:Key="ObsVM"/>
    </Window.Resources>

    <Grid>
        <!-- TabControl을 사용하여 두 가지 ViewModel의 차이를 비교 실습할 수 있도록 구성 -->
        <TabControl Margin="10">

            <!-- 탭 1: 수동 방식 (SetProperty) -->
            <TabItem Header="SetProperty 방식">
                <!-- 해당 탭의 DataContext는 SetVM 리소스(ViewModel) -->
                <StackPanel DataContext="{StaticResource SetVM}" Margin="10">
                    <TextBlock Text="이름 입력:"/>
                    <!-- TextBox: Name 속성과 양방향 바인딩. 텍스트가 변경될 때마다 ViewModel에 전달됨 -->
                    <TextBox Text="{Binding Name, UpdateSourceTrigger=PropertyChanged}" />

                    <TextBlock Text="메시지:" Margin="0,10,0,0"/>
                    <!-- 메시지 출력: ViewModel에서 후처리로 생성된 문자열 출력 -->
                    <TextBlock Text="{Binding Message}" FontWeight="Bold"/>
                </StackPanel>
            </TabItem>

            <!-- 탭 2: 자동 방식 (ObservableProperty) -->
            <TabItem Header="ObservableProperty 방식">
                <!-- 해당 탭의 DataContext는 ObsVM 리소스(ViewModel) -->
                <StackPanel DataContext="{StaticResource ObsVM}" Margin="10">
                    <TextBlock Text="이름 입력:"/>
                    <!-- ObservableProperty에 의해 자동 생성된 Name 속성과 바인딩 -->
                    <TextBox Text="{Binding Name, UpdateSourceTrigger=PropertyChanged}" />

                    <TextBlock Text="메시지:" Margin="0,10,0,0"/>
                    <!-- OnNameChanged()를 통해 자동 생성된 메시지 출력 -->
                    <TextBlock Text="{Binding Message}" FontWeight="Bold"/>
                </StackPanel>
            </TabItem>
        </TabControl>
    </Grid>
</Window>