INotifyCollectionChanged
인터페이스는 컬렉션의 변경 사항(추가, 삭제, 갱신 등)을 알리는 기능을 제공하는 인터페이스로, WPF에서 ObservableCollection
에서 사용하는 인터페이스입니다. 이를 이용해 키-값 형태의 Dictionary
같은 컬렉션을 구현하려면, Dictionary
에 INotifyCollectionChanged
를 추가적으로 구현해 컬렉션의 변경을 알릴 수 있어야 합니다. 이를 통해 WPF 바인딩 시스템에서 변경을 감지하도록 할 수 있습니다.
원리
INotifyCollectionChanged
인터페이스는 컬렉션의 변경 이벤트를 알리기 위해CollectionChanged
이벤트를 제공합니다.- 이 이벤트는 컬렉션에 항목이 추가되거나 삭제되었을 때 UI가 이를 감지해 업데이트할 수 있도록 합니다.
구현 방법
INotifyCollectionChanged
를 구현하는 클래스 생성.- 내부적으로
Dictionary<TKey, TValue>
를 사용해 키-값 구조를 유지. - 항목이 추가, 삭제, 갱신될 때
CollectionChanged
이벤트를 트리거.
다음은 INotifyCollectionChanged
와 INotifyPropertyChanged
를 함께 구현하여 ObservableDictionary
를 만드는 예시입니다.
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.ComponentModel;
public class ObservableDictionary<TKey, TValue> : IDictionary<TKey, TValue>, INotifyCollectionChanged, INotifyPropertyChanged
{
private readonly Dictionary<TKey, TValue> _dictionary;
public event NotifyCollectionChangedEventHandler CollectionChanged;
public event PropertyChangedEventHandler PropertyChanged;
public ObservableDictionary()
{
_dictionary = new Dictionary<TKey, TValue>();
}
public void Add(TKey key, TValue value)
{
_dictionary.Add(key, value);
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, new KeyValuePair<TKey, TValue>(key, value)));
OnPropertyChanged("Count");
OnPropertyChanged("Keys");
OnPropertyChanged("Values");
}
public bool Remove(TKey key)
{
if (_dictionary.TryGetValue(key, out TValue value) && _dictionary.Remove(key))
{
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, new KeyValuePair<TKey, TValue>(key, value)));
OnPropertyChanged("Count");
OnPropertyChanged("Keys");
OnPropertyChanged("Values");
return true;
}
return false;
}
public TValue this[TKey key]
{
get => _dictionary[key];
set
{
if (_dictionary.ContainsKey(key))
{
var oldValue = _dictionary[key];
_dictionary[key] = value;
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace, new KeyValuePair<TKey, TValue>(key, value), new KeyValuePair<TKey, TValue>(key, oldValue)));
OnPropertyChanged("Values");
}
else
{
Add(key, value);
}
}
}
public void Clear()
{
_dictionary.Clear();
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
OnPropertyChanged("Count");
OnPropertyChanged("Keys");
OnPropertyChanged("Values");
}
public bool ContainsKey(TKey key) => _dictionary.ContainsKey(key);
public bool TryGetValue(TKey key, out TValue value) => _dictionary.TryGetValue(key, out value);
public ICollection<TKey> Keys => _dictionary.Keys;
public ICollection<TValue> Values => _dictionary.Values;
public int Count => _dictionary.Count;
public bool IsReadOnly => false;
public void Add(KeyValuePair<TKey, TValue> item) => Add(item.Key, item.Value);
public bool Contains(KeyValuePair<TKey, TValue> item) => _dictionary.ContainsKey(item.Key) && _dictionary[item.Key].Equals(item.Value);
public void CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex) => ((ICollection<KeyValuePair<TKey, TValue>>)_dictionary).CopyTo(array, arrayIndex);
public bool Remove(KeyValuePair<TKey, TValue> item) => Remove(item.Key);
public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator() => _dictionary.GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => _dictionary.GetEnumerator();
protected virtual void OnCollectionChanged(NotifyCollectionChangedEventArgs e) => CollectionChanged?.Invoke(this, e);
protected virtual void OnPropertyChanged(string propertyName) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
설명
Dictionary<TKey, TValue>
: 키-값 컬렉션을 보유하며,ObservableDictionary
는 이를 감싸고INotifyCollectionChanged
와INotifyPropertyChanged
를 구현해 변경 사항을 알립니다.Add
,Remove
,Clear
메서드: 컬렉션을 변경할 때CollectionChanged
이벤트를 발생시켜 WPF 바인딩 시스템에 변경 사실을 알립니다.this[TKey key]
프로퍼티: 인덱서를 통해 값을 설정할 때, 기존 키가 있으면 값을 업데이트하고 없으면 새로운 항목으로 추가합니다.
활용
이렇게 구현된 ObservableDictionary
는 WPF
프로젝트에서 DataContext
나 Binding
으로 연결해 UI 요소가 컬렉션의 변경을 실시간으로 반영하도록 사용할 수 있습니다. 예를 들어, DataGrid
나 ListBox
의 ItemsSource
로 바인딩하면, 컬렉션 변경 시 자동으로 UI에 업데이트가 반영됩니다.
활용 예시 코드
1. WPF XAML 파일 (MainWindow.xaml
)
ListBox
를 사용하여 ObservableDictionary
의 데이터를 키-값 형식으로 표시하는 예제입니다.
<Window x:Class="WpfApp241112.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="ObservableDictionary Example" Height="350" Width="525">
<Grid>
<ListBox ItemsSource="{Binding MyDictionary}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Key}" Margin="5" />
<TextBlock Text=": " />
<TextBlock Text="{Binding Value}" Margin="5" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<Button Content="Add Item" Width="100" Height="30" HorizontalAlignment="Left" VerticalAlignment="Bottom" Click="AddItemButton_Click" />
</Grid>
</Window>
2. 코드 비하인드 파일 (MainWindow.xaml.cs
)
WPF 윈도우의 코드 비하인드에서 ObservableDictionary
를 생성하고 바인딩합니다.
using System.Windows;
namespace WpfApp241112
{
public partial class MainWindow : Window
{
public ObservableDictionary<string, string> MyDictionary { get; set; }
public MainWindow ()
{
InitializeComponent();
MyDictionary = new ObservableDictionary<string, string>
{
{ "Item1", "Value1" },
{ "Item2", "Value2" },
{ "Item3", "Value3" }
};
// DataContext를 설정하여 바인딩할 수 있도록 함
DataContext = this;
}
private void AddItemButton_Click (object sender, RoutedEventArgs e)
{
int newItemIndex = MyDictionary.Count + 1;
MyDictionary.Add($"Item{newItemIndex}", $"Value{newItemIndex}");
}
}
}
실행 결과
설명
ObservableDictionary<string, string>
: 키와 값을 문자열로 사용하는ObservableDictionary
입니다.DataContext
: WPF의DataContext
로 설정하여 XAML에서MyDictionary
를 바인딩할 수 있도록 합니다.AddItemButton_Click
이벤트: 버튼을 클릭하면ObservableDictionary
에 새로운 항목이 추가되고, 이로 인해CollectionChanged
이벤트가 발생하여 UI가 업데이트됩니다.
실행 결과
- 처음 실행하면
ListBox
에Item1: Value1
,Item2: Value2
,Item3: Value3
이 표시됩니다. Add Item
버튼을 클릭할 때마다 새로운 항목이Item4: Value4
,Item5: Value5
등의 형태로 추가되고ListBox
가 자동으로 갱신됩니다.
이 코드는 ObservableDictionary
가 CollectionChanged
이벤트를 발생시켜 WPF UI가 변경 사항을 실시간으로 반영하는 예시입니다.