2.1.3 예제 - 1단계
1️⃣ [ObservableProperty] 사용 예제 - 1단계
✔️ 1. 학습 목표
- CommunityToolkit.Mvvm 패키지를 사용해 프로젝트를 구현하는 방법을 학습합니다.
✔️ 2. 동작 시나리오

- TextBox에 입력하면
- 콘솔에 변경 로그가 찍힙니다.
- ListBox에서 아이템을 클릭하면
- 해당 아이템의 IsSelected가 true로 변경되고 출력됩니다.
2️⃣ Ex03_Step01
: CommunityToolkit.Mvvm 패키지를 활용한 프로젝트를 단계별로 따라하며 구현해봅니다.
📁 1. 가장먼저, 아래 링크를 참고해 WPF 프로젝트를 생성 합니다.
WPF 프로젝트 생성하기
01. Visual Studio 실행더보기 02. 새 프로젝트 생성더보기❶ 최근 실행한 프로젝트의 목록을 보여줍니다.❷ GitHub, Azure, DevOps, Bitbucket 등 원격 저장소에서 프로젝트를 가져옵니다.❸ 내 컴퓨터의 로컬
basiclike.tistory.com
📁 2. 생성한 WPF 프로젝트에, 아래 링크를 참고해 CommunityToolkit.Mvvm 패키지를 설치합니다.
1.2 패키지 설치 방법
1️⃣ "NuGet 패키지 관리자"에서, CommunityToolkit.Mvvm 설치하는 방법더보기 WPF 프로젝트 생성하기01. Visual Studio 실행더보기 02. 새 프로젝트 생성더보기❶ 최근 실행한 프로젝트의 목록을 보여줍니다.
basiclike.tistory.com
📁 3. 프로젝트 구조
Ch02_Sec01_Ex03_Step01/
├── Models/
│ └── ChildViewModel.cs
├── ViewModels/
│ ├── MainViewModel.cs ← 메인 뷰모델
├── Views/
│ ├── MainWindow.xaml ← 메인 뷰
│ └── MainWindow.xaml.cs
├── App.xaml
└── App.xaml.cs
📁 4. Model 클래스 만들기

public partial class ChildViewModel : ObservableObject
{
[ObservableProperty]
private string? title;
[ObservableProperty]
private bool isSelected;
}
📁 5. ViewModel 클래스 만들기

public partial class MainViewModel : ObservableObject
{
public ObservableCollection<ChildViewModel> Items { get; } = new()
{
new ChildViewModel { Title = "항목 1" },
new ChildViewModel { Title = "항목 2" },
new ChildViewModel { Title = "항목 3" }
};
[ObservableProperty]
private string? name;
partial void OnNameChanging(string? value)
{
Console.WriteLine($"이름이 곧 '{value}'(으)로 변경됩니다.");
}
partial void OnNameChanged(string? value)
{
Console.WriteLine($"이름이 '{value}'(으)로 변경되었습니다.\n");
}
[ObservableProperty]
private ChildViewModel? selectedItem;
partial void OnSelectedItemChanging(ChildViewModel? oldValue, ChildViewModel? newValue)
{
if (oldValue is not null)
oldValue.IsSelected = false;
if (newValue is not null)
newValue.IsSelected = true;
}
}
📁 6. XAML 화면 구성

xmlns:vm="clr-namespace:WPF_ToolKit02.ViewModels"
Title="속성 변경 감지 예제" Height="300" Width="400">
<Window.DataContext>
<vm:MainViewModel />
</Window.DataContext>
<StackPanel Margin="10">
<TextBox Text="{Binding Name, UpdateSourceTrigger=PropertyChanged}" />
<ListBox ItemsSource="{Binding Items}"
SelectedItem="{Binding SelectedItem}"
DisplayMemberPath="Title"/>
<TextBlock Text="선택된 항목 상태:"
Margin="0,10,0,0"/>
<TextBlock Text="{Binding SelectedItem.IsSelected}"/>
</StackPanel>
📁7. App 수정

⑤App.Xaml 에서 시작 View 경로를, ⑥ViewModel 폴더로 이동시킨 MainWindow로 변경합니다.
📁 8. 콘솔 실행 설정


*WPF 앱에서는 기본적으로 Console.WriteLine이 눈에 보이지 않기 때문에 디버그 출력 창에서 확인하거나, Debug.WriteLine()을 사용하는 것이 좋습니다
📁 9. 결과
- 텍스트 박스에 입력 → 콘솔에 OnNameChanging과 OnNameChanged의 구현부 로그 출력
실제로 속성 변경 시점에 실행되는지 학생들이 눈으로 확인 - ListBox에 선택된 아이템에 해당하는 문자열이 TextBlock 에 출력
3️⃣ 예제 자세히 살펴보기
: 프로젝트 소스코드의 기능을 주석을 통해 파악하고, 동작 구조를 자세히 이해합니다.
✔️ Model 클래스 만들기
// ChildViewModel 클래스는 각 항목(Item)에 대한 ViewModel로 사용됩니다.
// MVVM 패턴에서 View와 데이터를 연결하는 역할을 하며, UI 요소와의 바인딩을 위해 속성 변경 통지를 제공합니다.
// CommunityToolkit.Mvvm.ComponentModel.ObservableObject를 상속받아 INotifyPropertyChanged 구현을 자동으로 처리합니다.
using CommunityToolkit.Mvvm.ComponentModel;
public partial class ChildViewModel : ObservableObject
{
// [ObservableProperty] 특성을 사용하면 아래의 private 필드(title)에 대해 자동으로 public 속성 Title이 생성됩니다.
// 속성이 변경될 때 자동으로 PropertyChanged 이벤트가 발생하여 UI에 즉시 반영됩니다.
// string?은 null을 허용하는 문자열입니다.
[ObservableProperty]
private string? title;
// isSelected는 UI에서 특정 항목이 선택되었는지를 나타내는 상태입니다.
// 예: ListBox에서 항목을 선택하면 해당 ViewModel의 IsSelected가 true로 설정될 수 있습니다.
// 이 또한 속성 변경 시 자동으로 UI에 반영되며, 주로 시각적 상태 변경(색상 등)에 활용됩니다.
[ObservableProperty]
private bool isSelected;
}
✔️ ViewModel 클래스 만들기
using CommunityToolkit.Mvvm.ComponentModel;
using System;
using System.Collections.ObjectModel;
// MainViewModel은 MVVM 패턴에서 View (UI)와 Model 간의 연결을 담당하는 ViewModel입니다.
// ObservableObject를 상속받아 INotifyPropertyChanged를 자동으로 구현합니다.
public partial class MainViewModel : ObservableObject
{
// Items 컬렉션은 UI에 바인딩되어 ListBox 등의 컨트롤에 데이터를 제공합니다.
// ObservableCollection을 사용하면 컬렉션에 변경이 생길 때 UI가 자동으로 반영됩니다.
public ObservableCollection<ChildViewModel> Items { get; } = new()
{
new ChildViewModel { Title = "항목 1" },
new ChildViewModel { Title = "항목 2" },
new ChildViewModel { Title = "항목 3" }
};
// ObservableProperty 특성을 사용하면 name 필드에 대해 자동으로 Name 속성이 생성되고,
// 속성 변경 시 INotifyPropertyChanged 이벤트가 발생하도록 코드가 자동 생성됩니다.
[ObservableProperty]
private string? name;
// name 속성이 변경되기 직전에 실행되는 partial 메서드입니다.
// 이 메서드는 사용자가 직접 정의할 수 있으며, 주로 로그 출력, 유효성 검사 등에 사용됩니다.
partial void OnNameChanging(string? value)
{
Console.WriteLine($"이름이 곧 '{value}'(으)로 변경됩니다.");
}
// name 속성이 변경된 이후에 실행되는 partial 메서드입니다.
// 변경된 값에 대해 후처리 작업을 수행할 수 있습니다.
partial void OnNameChanged(string? value)
{
Console.WriteLine($"이름이 '{value}'(으)로 변경되었습니다.");
}
// ObservableProperty 특성은 selectedItem 필드에 대해서도 동일하게 적용됩니다.
// selectedItem 속성이 UI의 선택 항목과 바인딩되며, 변경 감지를 자동으로 수행합니다.
[ObservableProperty]
private ChildViewModel? selectedItem;
// selectedItem이 변경되기 직전에 실행되는 메서드입니다.
// 이전 항목의 IsSelected 속성을 false로 설정하고,
// 새로 선택된 항목의 IsSelected를 true로 설정하여 UI 상태를 자동으로 관리합니다.
partial void OnSelectedItemChanging(ChildViewModel? oldValue, ChildViewModel? newValue)
{
if (oldValue is not null)
oldValue.IsSelected = false;
if (newValue is not null)
newValue.IsSelected = true;
}
}
✔️ XAML 화면 구성
<!--
이 XAML 코드는 MVVM 패턴을 따르는 WPF 애플리케이션의 MainWindow 정의입니다.
ViewModel을 DataContext로 바인딩하고, 사용자 입력과 UI 요소가 ViewModel 속성과 실시간으로 연결됩니다.
-->
<Window x:Class="MVVMPropertyChangedSample.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:vm="clr-namespace:MVVMPropertyChangedSample.ViewModels"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="속성 변경 데모" Height="300" Width="400">
<!-- MainViewModel을 이 뷰의 데이터 컨텍스트로 설정합니다. -->
<Window.DataContext>
<vm:MainViewModel/>
</Window.DataContext>
<!-- StackPanel은 UI 요소를 수직으로 배치하는 레이아웃 컨테이너입니다. -->
<StackPanel Margin="10">
<!--
TextBox의 Text 속성을 ViewModel의 Name 속성과 바인딩합니다.
UpdateSourceTrigger=PropertyChanged 설정을 통해 입력할 때마다 ViewModel에 값이 즉시 반영됩니다.
-->
<TextBox Text="{Binding Name, UpdateSourceTrigger=PropertyChanged}"
PlaceholderText="이름을 입력하세요"/>
<!--
ListBox의 항목 목록을 ViewModel의 Items 속성과 바인딩합니다.
SelectedItem 속성은 사용자가 선택한 항목을 ViewModel의 SelectedItem에 실시간으로 반영합니다.
DisplayMemberPath="Title" 설정은 각 항목의 Title 속성을 UI에 표시합니다.
-->
<ListBox ItemsSource="{Binding Items}"
SelectedItem="{Binding SelectedItem}"
DisplayMemberPath="Title"/>
<!-- 선택된 항목의 상태를 확인할 수 있는 텍스트 블록입니다. -->
<TextBlock Text="선택된 항목 상태:"
Margin="0,10,0,0"/>
<!--
현재 선택된 항목의 IsSelected 속성을 바인딩하여 UI에 상태를 표시합니다.
선택되었을 경우 true, 선택되지 않았을 경우 false가 표시됩니다.
-->
<TextBlock Text="{Binding SelectedItem.IsSelected}"/>
</StackPanel>
</Window>
4️⃣ UI 색상 추가하기
: 기존 예제에게 UI에 색상을 변경할 수 있도록 구현합니다.
✔️ 과제 내용
- IsSelected 상태를 UI에 색상으로 표현
✔️ XAML 화면 수정
<ListBox ItemsSource="{Binding Items}"
SelectedItem="{Binding SelectedItem}">
<ListBox.ItemTemplate>
<DataTemplate>
<Border Padding="5" Margin="2">
<TextBlock Text="{Binding Title}" />
<Border.Style>
<Style TargetType="Border">
<Setter Property="Background" Value="White"/>
<Style.Triggers>
<!-- IsSelected 속성이 true일 때 배경색 변경 -->
<DataTrigger Binding="{Binding IsSelected}" Value="True">
<Setter Property="Background" Value="Yellow"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Border.Style>
</Border>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
✔️ 결과
- 항목 클릭 시 ViewModel.SelectedItem이 변경되며, 해당 항목의 IsSelected가 true가 됩니다.
- 트리거에 의해 선택된 항목만 배경색이 변경됩니다.
✔️ 상세 주석
<!--
이 ListBox는 MVVM 패턴에서 ViewModel의 Items 컬렉션을 항목으로 사용하고,
사용자가 선택한 항목을 SelectedItem 속성에 바인딩합니다.
각 항목은 DataTemplate을 통해 렌더링되며, 선택 상태(IsSelected)에 따라 배경색이 바뀌도록 설정되어 있습니다.
-->
<ListBox ItemsSource="{Binding Items}"
SelectedItem="{Binding SelectedItem}">
<!--
ItemTemplate은 각 항목을 어떻게 시각적으로 표현할지를 정의합니다.
여기서는 Border 안에 TextBlock을 넣어 각 항목의 제목을 보여줍니다.
-->
<ListBox.ItemTemplate>
<DataTemplate>
<!--
Border는 각 항목의 배경 및 마진, 패딩 등 레이아웃 및 시각 효과를 담당합니다.
-->
<Border Padding="5" Margin="2">
<!--
TextBlock은 ChildViewModel의 Title 속성을 출력합니다.
-->
<TextBlock Text="{Binding Title}" />
<!--
Style을 통해 Border의 배경색을 동적으로 제어합니다.
기본 배경은 흰색(White)이며, 특정 조건이 만족될 때만 변경됩니다.
-->
<Border.Style>
<Style TargetType="Border">
<!-- 기본 배경색 설정 -->
<Setter Property="Background" Value="White"/>
<!--
트리거 설정: ViewModel의 IsSelected 속성이 true인 경우
배경색을 연한 하늘색(LightSkyBlue)으로 변경합니다.
-->
<Style.Triggers>
<DataTrigger Binding="{Binding IsSelected}" Value="True">
<Setter Property="Background" Value="LightSkyBlue"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Border.Style>
</Border>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>