본문으로 바로가기

WPF가 내세우는 자랑거리 중에 하나가 바로 의존 속성(Dependency Property)이다.
이 의존 속성이 기존의 속성과 다른 점이라면, '의존' 이다.
말 장난 같지만, 이 '의존' 이 가지는 의미가, 굉장히 편리한 코딩을 할 수 있게 해 주는 요소이기 때문에 강조하지 않을 수 없다.
'의존' 이란 간단히 말해, 어떤 객체의 모습 혹은 행동이 의존 속성에 의해 영향을 받는다는 의미다.
이게 단순히 의미로 끝나는 것이 아니라, 실제로 의존 속성의 값을 변경하면 자동적으로 객체의 모습 혹은 행동이 변하게 된다는 말이다.

예를 들어, Button의 Background Brush를 바꾸면 Button이 자동으로 다시 그려진다.
전통적인 Win32 프로그래밍에서는 Invalidate()와 WM_PAINT를 항상 생각해야 하지만, WPF에서는 그럴 필요가 없다는 말이다.

'의존 속성'의 이런 작동 방식은, 애니메이션등에서 유용하게 사용되는 메커니즘이다.
간단히 생각해 보면 '의존 속성'의 구현은 간단해 보인다. Button의 Brush를 바꾸면 Button이 다시 그려지게 하는 정도야 쉽게 구현할 수 있을 듯 하다.

그러나, Button의 Brush의 Color만 바꾸어도 Button이 다시 그려지는 상황은 어떤가? 이미 Button에 포함되어 있는 객체인 Brush가 있고, 이 Brush의 Color을 바꾸는 상황 말이다.

Brush의 Color 뿐만 아니라, 어떤 요소의 Size같은 것들이 바뀌면, Layout이 바뀌고 재정렬이 이루어지는 상황 같은 것들도 말이다.

또한 특정 속성이 변경될 때 마다, 특별한 처리를 해야 하는 상황도 있다. 결국 '의존 속성'의 구현이 간단하지 않다는 것이며, WPF에는 이런 것들을 구현해 놓았다는 이야기다.

'의존 속성'의 구성요소를 살펴보면...

1. System.Windows.DependencyObject

2. System.Windows.DependencyProperty

3. System.Windows.PropertyMetadata

 

1. DependencyObject는 '의존 속성'을 가지는 객체다.

WPF의 대부분의 UI 요소들은 이 DependencyObject를 상속받아 구현되어 있다. 이 DependencyObject는 GetValue 와 SetValue 메소드가 있는데, 모든 '의존 속성'은 이 GetValue 와 SetValue를 통해 구현되어 있다.

2. DependencyProperty는 바로 '의존 속성' 자체를 정의한 것이다.

예를 들어 Button의 Background Brush에 대한 DependencyProperty가 있고, 또한 Brush의 Color에 대한 DependencyProperty도 있다. (Brush도 DependencyObject 이다.)

3. PropertyMetadata는 DependencyProperty의 부가 정보 정도로 생각하면 된다.

따라서 이 PropertyMetadata는 DependencyProperty에 포함되어 있다고 할 수 있다. 이름이 비슷해서 헥갈리기 쉬운데, 일단 DependencyProperty는 DependecyObject의 클래스 단위(인스턴스 단위가 아니라)로 정의된다. 모든 Button에 Background Brush가 있는 것이지, 어떤 Button에는 Brush가 있고, 어떤 Button에는 Brush가 없는 것이 아니기 때문이다.

따라서 DependencyProperty는 DependencyObject의 static 멤버로 정의된다. DependencyObject의 SetValue는 DependencyProperty와 Value 값, 2개의 인자를 넣어 호출하게 되는데, SetValue에서는 DependencyProperty에 기반하여 Value 값을 평가한다. 그리고 DependencyProperty에는 디폴트 값에 대한 정보도 포함하고 있다.

또한 SetValue는 DependencyProperty의 PropertyMetadata도 살펴보고, 화면을 다시 그려야 하는지, Layout 정렬을 다시 해야 하는지 판단하며, 값이 바뀌면 호출해야 하는 Callback이 있으면 호출하기도 한다.
(SetValue는 DependecyObject.OnPropertyChanged()가 호출되게 만든다.)
생각해 보면 DependencyProperty는 어떤 속성에 대한 정보를 정의할 뿐이지, 어떤 객체에 대한 속성을 정의한다고 할 수는 없다.

예를 들어, Button의 Background Brush에 대한 DependencyProperty라고 했지만, 사실 DependencyProperty 입장에서 보면, 그냥 Brush 클래스 타입이며, 디폴트 값은 뭐다 등등의 정보만을 유지하면 된다. 굳이 Button의 Background Brush라고 정의할 필요가 없다는 말이다.
그러나 DependencyProperty를 생성할 때는(Register 메소드), Button 클래스에 대한 정보(owner type)도 추가하도록 되어 있다.

뭐 굳이 필요 없다고 하더라도 나중에 쓸 일이 있을지 모르기 때문에 Owner를 가지고 있는 것에 대해 그러려니 하고 넘어가려고 했더니, AddOwner라는 메소드도 함께 있다. DependencyProperty를 생성할 때 Owner를 넣어 주었는데도, AddOwner가 존재하는 것이다. 이 Owner라는 개념과 AddOwner 메소드의 존재에 대해 나름대로 생각해 보니,
앞서 말했듯이 DependencyProperty의 기능을 위해서는 Owner가 필요없을 수도 있다. 그러나 만약 Owner를 통해서 Owner가 가진 DepenedencyProperty를 가져와야 하는 상황이라면, Owner의 정보가 필요할 것이다.
그리고 결정적으로 DependencyProperty를 새로 생성하지 않고 기존에 만들어진 DependencyProperty를 사용할 수 있다면 메모리도 절약될 것이다

결국 DependencyProperty에는 Owner라는 개념이 필요하고,
Owner를 여러개 가질 수 있도록 만들어 둔 것이 아닐까 생각한다.

그리고 한가지더 언급할 것은, DependecyProperty의 PropertyMetadata는 Owner 별로 유지된다는 사실이다.
AddOwner를 호출할때 PropertyMetadata도 함께 추가할 수 있는데, 이때에도 기존에 추가된 Owner의 PropertyMetadata는 변하지 않는다.
하지만 새롭게 추가하는 Owner의 PropertyMetadata는 기존 Owner의 PropertyMetadata와 병합(Merge)된다.

결국 Owner로 추가한다는 말은 기존의 PropertyMetadata의 속성들을 물려받으면서 Metadata 몇가지를 더 추가한다는 의미로 생각할 수 있다.