INotifyPropertyChanged interface
Peponi │ 1/16/2025 │ 6m
C#
Typeinterface
INotifyPropertyChanged interface
1/16/2025
6m
Peponi
C#
Typeinterface
1. Introduction
INotifyPropertyChanged
인터페이스는 클라이언트에 속성 값이 변경됨을 알리는데 사용한다. PropertyChanged
이벤트만 정의된 간단한 인터페이스로, 프로퍼티 값 변경 시 이벤트를 통해 각 클라이언트에 전파할 수 있도록 한다. 구현이 쉽고 간결하며 수동으로 멤버를 바인딩 하는 경우에 비해 제작 편의성 및 재사용성이 대폭 향상된다.
INotifyPropertyChanged
인터페이스를 적절히 구현하면 다음과 같은 동작 특성을 가질 수 있다.
- 이벤트를 통해 프로퍼티 값 변경을 자동으로 추적할 수 있다.
- UI가 존재하는 경우, 프로퍼티 값 수정 시 자동으로 컨트롤에 반영이 되도록 할 수 있다.
- UI가 존재하는 경우, 컨트롤의 값을 수정할 때 프로퍼티와 동일한 형식의 값인 경우 자동 반영, 잘못된 값을 입력하는 경우 이전 값으로 돌아간다.
2. Example
하단 샘플은 INotifyPropertyChanged
인터페이스에 대한 간단한 사용 방법이다.
public class TestClass : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private bool _testBool;
public bool TestBool
{
get => _testBool;
set
{
if (_testBool != value)
{
_testBool = value;
OnPropertyChanged();
}
}
}
// 바인딩된 컴포넌트 반환. new로 반환하여 이 클래스의 데이터를 표시하는 여러개의 컴포넌트 생성 가능
public UserControl GetComponent() => new TestControl(this);
// 프로퍼티 변경 전파
private void OnPropertyChanged([CallerMemberName] string? propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
public partial class TestControl : UserControl
{
private TestClass _testClass { get; set; }
public TestControl(TestClass testClass)
{
InitializeComponent();
_testClass = testClass;
BindData();
}
private void BindData()
{
// TestBool : boolean 바인딩 테스트를 위한 CheckBox
TestBool.DataBindings.Add(new Binding(nameof(TestBool.Checked), _testClass, nameof(TestClass.TestBool)));
}
}
내용을 요약하자면,
-
Data class
INotifyPropertyChanged
상속 및 구현
-
Control class
- Bindable 클라이언트에 데이터 바인딩
정도로 요약할 수 있다.
만약 추가 예제가 필요한 경우, 마지막 섹션을 참고한다.
3. 참조 자료
A. Additional example
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Threading;
using System.Windows.Forms;
namespace INotifyPropertyChangedExample
{
public enum TestEnum
{
A, B, C
}
public class TestClass : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private bool _testBool = false;
public bool TestBool
{
get => _testBool;
set
{
if (_testBool != value)
{
_testBool = value;
OnPropertyChanged();
}
}
}
private int _testInt = 0;
public int TestInt
{
get => _testInt;
set
{
if (_testInt != value)
{
_testInt = value;
OnPropertyChanged();
}
}
}
private double _testDouble = 0;
public double TestDouble
{
get => _testDouble;
set
{
if (_testDouble != value)
{
_testDouble = value;
OnPropertyChanged();
}
}
}
private string _testString = string.Empty;
public string TestString
{
get => _testString;
set
{
if (_testString != value)
{
_testString = value;
OnPropertyChanged();
}
}
}
private TestEnum _testEnum = TestEnum.A;
public TestEnum TestEnum
{
get => _testEnum;
set
{
if (_testEnum != value)
{
_testEnum = value;
OnPropertyChanged();
}
}
}
// 바인딩된 컴포넌트 반환. new로 반환하여 이 클래스의 데이터를 표시하는 여러개의 컴포넌트 생성 가능
public UserControl GetComponent() => new TestControl(this);
// 프로퍼티 변경 전파
private void OnPropertyChanged([CallerMemberName] string? propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}
using System;
using System.Windows.Forms;
namespace INotifyPropertyChangedExample
{
public partial class TestControl : UserControl
{
private TestClass _testClass { get; set; }
public TestControl()
{
InitializeComponent();
}
public TestControl(TestClass testClass)
{
InitializeComponent();
EnumSelect.Items.AddRange(Enum.GetNames(typeof(TestEnum)));
_testClass = testClass;
BindData();
}
// TestClass 바인딩 : 양방향 바인딩
// 1. 프로퍼티 값을 수정하면 UI에 자동 반영
// 2. 바인딩된 UI의 값을 바꿀 시 자동으로 프로퍼티에 반영
private void BindData()
{
// CheckBox
TestBool.DataBindings.Add(new Binding(nameof(TestBool.Checked), _testClass, nameof(TestClass.TestBool)));
// NumericUpDown
TestInt.DataBindings.Add(new Binding(nameof(TestInt.Value), _testClass, nameof(TestClass.TestInt)));
// NumericUpDown
TestDouble.DataBindings.Add(new Binding(nameof(TestDouble.Value), _testClass, nameof(TestClass.TestDouble)));
// TextBox
TestString.DataBindings.Add(new Binding(nameof(TestString.Text), _testClass, nameof(TestClass.TestString)));
// Enum은 양방향 바인딩을 원할 경우 ComboBox를 고려하는 것이 좋다.
TestEnum.DataBindings.Add(new Binding(nameof(TestEnum.Text), _testClass, nameof(TestClass.TestEnum)));
}
}
}
using System.Threading;
using System.Windows.Forms;
namespace INotifyPropertyChangedExample
{
public partial class MainFrame : Form
{
public MainFrame()
{
InitializeComponent();
SetUI();
}
private void SetUI()
{
TestClass testClass = new TestClass();
TableLayoutPanel tableLayoutPanel = new TableLayoutPanel();
tableLayoutPanel.ColumnCount = 3;
// 같은 데이터를 원본으로 하는 컴포넌트 세개 추가. 어느 한 곳에서 프로퍼티 변경 시 나머지 컴포넌트 또한 값이 자동 변경
tableLayoutPanel.Controls.Add(testClass.GetComponent(), 0, 0);
tableLayoutPanel.Controls.Add(testClass.GetComponent(), 1, 0);
tableLayoutPanel.Controls.Add(testClass.GetComponent(), 2, 0);
tableLayoutPanel.Dock = DockStyle.Fill;
this.Controls.Add(tableLayoutPanel);
}
}
}