7 Results for '2009/04'
- 2009/04/27 I am a Developer and... (11)
- 2009/04/24 왕(King)과 황제(Emperor)의 다른 점. (4)
- 2009/04/19 DevDcc 1회 행사에 와주신 분들께 감사드려요. (5)
- 2009/04/07 데이터 바인딩#2 데이터 바인딩의 원리와 사용법 (1)
- 2009/04/05 스타크래프트 프로리그를 즐기는 방법 (5)
- 2009/04/05 Behavior를 실버라이트 2에서 사용하기 (5)
- 2009/04/04 블렌드의 희한한 이름 제한
행사 끝내고 원고 하나 쓰고 16시간 자다가 지금은 회사일 하는 중 ㅋ
지난 글에서 실버라이트로 데이터 바인딩을 살짝 맛보았다. 이번에는실버라이트의 데이터 바인딩이 구체적으로 어떻게 이루어졌는지, 그리고 어떤 기능들을 제공하는지 알아보겠다.
데이터 바인딩의 동작 원리
데이터 바인딩은 기본적으로 UI 엘리먼트와 데이터를 연결하는 것을말한다. 이를 구현하는 것이 바로 Binding 클래스이고, ystem.Windows.Data 네임스페이스에서 찾을 수 있다. 지난글에서 우리가 사용했던 Text=”{Binding Path=Name}” 과 같은 바인딩 표현식에서도 Binding 지시자를 통해 UI 엘리먼트와 데이터가 연결되는 것을하였다. 이들 사이의 관계를 도식화 해보자면 다음과 같다.
[그림 1. 데이터 바인딩의 구조]
l 바인딩 엔진, 즉 Binding 클래스는 바인딩 원본과 바인딩 대상의 사이에서 서로의 값을 설정하거나 변화를 감시하는 역할을 수행한다.
l 바인딩 원본은 어떤 종류의 CLR 개체도 사용할 수 있으며 바인딩 원본의 바인딩 할 속성도 제한이 없다.
l 바인딩 대상은 반드시 FrameworkElement에서 파생된 클래스이어야 하고 바인딩 대상 속성은 반드시 DependencyProperty이어야 한다.
l 화살표는 바인딩의 방향을 나타내며 구성에 따라 단방향 혹은 양방향으로 연결할 수 있다.
l 바인딩에 의해 값이 변경될 때 ValueConverter가 지정되어 있을 경우 값이 적절하게 변환되어 설정된다.
바인딩은 다른 실버라이트 개체들과 마찬가지로 XAML과 코드로
선언할 수 있다.
[XAML] [C#]
<TextBlock Text="{Binding Id}" …/>
TextBlock tb = new TextBlock();
Binding binding = new Binding("Id");
tb.SetBinding(TextBlock.TextProperty, binding);
이렇게 Binding 개체에 의해 연결된 바인딩은 명시적으로 데이터 바인딩 할 원본 개체의 인스턴스를 설정하지 않으면 기본적으로 DataContext 속성에 설정되어 있는 데이터 개체로부터 바인딩 할 원본 속성을 찾는다. DataContext는 실버라이트의 비주얼 트리를 타고 하위 개체로 전파되므로 공통적인 데이터 바인딩을 하는 UI 집합에서 편리하게 사용할 수 있다.
[그림 2. DataContext를 이용한 바인딩 원본 개체 설정]
바인딩의 기능과 표현식
1.
바인딩 할 원본 개체의 속성 설정
앞서 소개한 것처럼 바인딩 할 원본 개체의 속성은 Binding 클래스의 Path 속성을 사용하여 지정한다. XAML에서 바인딩 표현식을 사용할 때에는 Path지시자를 생략하고 바인딩 할 원본 개체의 속성만 지정할 수 있다.
또한 Path 속성을 아예 생략할 수도 있는데, 이 경우 바인딩 대상 속성에는 바인딩 원본 개체 그 자체가 바인딩 된다.
[리스트 2. 바인딩
경로를 설정하는 XAML 표현식]
<TextBlock Text="{Binding Path=Name}" />
<!--
또는 -->
<TextBlock Text="{Binding Name }" />
<!--
또는 -->
<TextBlock Text="{Binding }" />
2.
명시적인 데이터 원본 설정
데이터 원본은 Binding클래스의 Source 속성으로 설정할 수 있다. Source 속성을 생략할
경우 자동으로 DataContext에서 바인딩할 속성을 가져온다. 일반적인
경우 Source 속성을 생략하고 DataContext로부터
바인딩 받는 경우가 대부분이지만 때로 명시적인 DataContext가 아닌 명시적인 원본 설정이 필요할
때도 있다.
[XAML]
[리스트 3. 명시적으로
바인딩 원본을 설정하는 XAML 표현식]
<TextBlock Text="{Binding Path=Id, Source={StaticResource MyProfile} }" />
XAML에서 Source를 명시적으로 사용하기 위해서는 먼저 해당 엘리먼트를 포함하는 사용자 컨트롤이나 App.xaml에 반드시 바인딩할 원본을 리소스로 등록한 후 StaticResource 표현식을 사용하여 설정해야 한다. 위의 예제는 리소스로 등록된 MyResource 개체를 바인딩 원본으로 사용하여 MyResource.Id 속성을 Text 속성에 바인딩한다는 의미이다.
3.
데이터의 흐름 설정
Binding 클래스의 Mode 속성으로 원본을 대상에 어떤 방향으로 바인딩 할지 설정할 수 있다. 데이터 흐름에는 다음과 같은 세 종류가 있으며 Mode 속성이 생략 될 경우 자동으로 OneWay 방식이 선택된다.
l OneTime
최초로 바인딩 원본이 설정될 때 단 한번만 바인딩 대상을 변경하는 모드.
l OneWay
바인딩된 원본이 변경사항을 알려줄 때마다 자동으로 바인딩 대상도 변경하는 모드.
l TwoWay
바인딩 대상이 변경되었을 때 역으로 바인딩 원본도 변경하는 양방향 모드.
[리스트 4. 바인딩
흐름을 설정하는 XAML 표현식]
<TextBlock Text="{Binding Id, Mode=OneTime}" />
<!--
또는 -->
<TextBlock Text="{Binding Id, Mode=OneWay}" />
<!--
또는 -->
<TextBlock Text="{Binding Id, Mode=TwoWay}" />
단, OneWay와 TwoWay 모드가 정상적으로 동작하기 위해서는 데이터 원본 오브젝트가 반드시 다음의 INotifyPropertyChanged 인터페이스를 구현해야 한다.
4.
데이터 원본의 변경 알림
위에서 본 데이터 바인딩의 OneWay혹은 TwoWay 모드는 데이터 원본이 변경되면 자동으로 바인딩 대상도 변경되어야 한다. 이를 위해 데이터 원본 개체는 자신의 데이터가 변경될 때 바인딩 개체에 그 사실을 알려줘야 한다.
이 과정은 모든 CLR 개체에서 자동으로 되는 것이 아니라 INotifyPropertyChanged라는 인터페이스를 상속받은 개체에서 코드를 통해 명시적으로 구현을 해야 한다.
[리스트 5.
INotifyPropertyChanged 인터페이스를 구현하는 원본 개체의 예제]
public class Profile : INotifyPropertyChanged
{
private
string _id;
public string Id
{
get
{ return _id; }
set
{
_id = value;
FirePropertyChanged("Id");
}
}
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler
PropertyChanged;
protected
void FirePropertyChanged(string propertyName)
{
if
(PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
#endregion
}
이렇게 데이터 원본은 PropertyChanged 이벤트를 발생시키고 바인딩 엔진은 그 이벤트를 받아 바인딩 대상 속성을 변경한다.
5.
데이터 컨버팅
때로는 바인딩 원본 속성의 타입과 바인딩 대상 속성의 타입이 일치하지 않을 수도 있다. 예를 들어 TextBlock의 Text 속성에 DateTime 타입의 값을 바인딩한다면 시스템의 로캘에 따라 “18/03/2009 PM 08:09:00”과 같이 원치 않는 형식으로 바인딩되는 것을 볼 수 있다. 이 때 IValueConverter 인터페이스를 상속하는 클래스를 하나 만들고 IValueConverter 인터페이스의 Convert 메서드와 ConvertBack 메서드를 구현하면 된다. 이렇게 구현한 ValueConverter는 먼저 XAML에 리소스로 등록을 한 후 Binding 클래스의 Converter속성에 리소스를 설정하면 된다. 또한 ConverterParameter 속성을 사용하면 값을 변환할 때 옵션을 부여할 수 있다.
[XAML]
<TextBox Text="{Binding Birthday,
Mode=TwoWay,
Converter={StaticResource
DateConverter},
ConverterParameter=yyyy년 MM월 dd일}"
/>
[C#]
public class DateValueConverter : IValueConverter
{
public object Convert(object
value, Type targetType, object parameter, CultureInfo
culture)
{
// 생략…
return
…;
}
public object ConvertBack(object
value, Type targetType, object parameter, CultureInfo
culture)
{
// 생략
return
…;
}
#endregion
}
[리스트 6.
IValueConverter 인터페이스를 구현하는 원본 개체의 예제]
Convert 메서드는 데이터 원본에서 바인딩 대상 방향으로 설정할 때 사용되고 ConvertBack 메서드는 반대로 바인딩 대상의 변화를 데이터 원본에 설정할 때 사용된다. 참고로 앞의 코드 조각에서 볼 수 있듯이 바인딩 표현식은 보기 좋게 정리하기 위해 표현식의 각 지시자 사이에 엔터를 포함한 공백을 넣을 수 있다.
6.
데이터 유효성 검사
TwoWay 바인딩이 설정된 TextBox에서 사용자가 문자열을 입력하면 입력된 문자열이 다시 데이터 원본 속성에 설정이 된다. 이 때 기본적으로 데이터 원본 속성의 설정자에서 사용자 입력의 유효성을 검사할 수도 있고 ValueConverter를 사용하여 유효한 형태로 변환할 수도 있다. 그러나 이렇게 할 경우 사용자가 잘못된 입력을 했다는 것을 상위 UI에 알려주려면 별도의 이벤트나 메서드를 사용해야 한다. 특히 복잡한 UI를 가진 입력 폼 등의 애플리케이션은 표준적인 입력 오류를 알려주는 수단이 필요하다.
Binding 클래스는 데이터 유효성 검사를 위해 ValidatesOnExceptions 속성과 NotifyOnValidationError 속성을 지원한다. 이 두 속성을 모두 True로 설정하면 데이터 원본의 속성이 바인딩 엔진에 의해 설정 중에 발생된 예외를 잡아서 BindingValidationError 이벤트로 전달한다. BindingValidationError 이벤트는 모든 FrameworkElement가 가지고 있는 이벤트로 이벤트가 발생한 엘리먼트에서부터 시작하여 이벤트가 처리될때까지 상위 엘리먼트로 계속 라우팅되는 버블 이벤트이다.
[리스트 7. BindingValidationError를
이용한 데이터 유효성 검사 예제]
<TextBox
Text="{Binding Path=Age,
NotifyOnValidationError=true,
ValidatesOnExceptions=true}" />
[C#]
public class Profile : INotifyPropertyChanged
{
private int _age;
public int Age
{
get
{ return _age; }
set
{
if
(value < 0 || value
> 150)
throw
new Exception("나이는 0~150까지만 가능합니다.");
_age = value;
FirePropertyChanged("Age");
}
}
}
// 바인딩 에러 처리
void Page_BindingValidationError(object
sender, ValidationErrorEventArgs e)
{
// 에러가 발생할 때 처리
if
(e.Action == ValidationErrorEventAction.Added)
ErrorCaption.Text =
e.Error.Exception.Message;
// 에러가 해결되었을 때 처리
else if (e.Action == ValidationErrorEventAction.Removed)
ErrorCaption.Text = "";
e.Handled = true;
}
지금까지 실버라이트 데이터 바인딩의 원리와 중요한 기능들을 살펴보았다. 물론 이것들은 데이터 바인딩의 가장 기초적인 부분이다. 보다 효과적인 사용을 위해서는 세심한 데이터 모델 설계와 UI의 구성, 그리고 무엇보다도 디자이너와의 협업이 중요하다. 다음 글에서는 데이터 바인딩을 보다 효과적으로 사용하는 방법과 디자이너와 협업하기 위한 방법에 대해 알아보도록 하겠다.
-----------------------------
이 전 글에서 실버라이트 3에서 추가된 슈퍼 쿨한 기능 중 하나인 Behavior를 실버라이트 2에서도 구현할 수 있다고 살짝 언급했는데요, 어떻게 구현했는지 소개할께요. 소개에 앞서서 Behavior와 관련된 라이브러리 코드들은 모두 블렌드 3에 포함된 Microsoft.Expression.Interactivity 어셈블리를 리버스엔지니어링으로 카피한거에요. 사용시 참고하시길 바래요.
먼저 동작 완구 부터…http://shiverlight.net/sample/SilverlightInteractivity/
뭐어… 특별할 게 없어죠? 이 정도 기능이야 뚝딱뚝딱 코딩하면 10분도 안걸릴거에요. 그렇지만 여기 있는 모든 동작은 코딩이 단 한줄도 들어가지 않은 즉, 순수하게 XAML에서 Behavior와 Trigger만 사용한 거죠.
[샘플 코드]
그럼 구현 과정을 알아보죠.
1. Microsoft.Expression.Interactivity 어셈블리 복사
먼저 실버라이트 3 개발 머신에 블렌드 3를 설치하면 C:\Program Files (x86)\Microsoft Expression\Blend 3 Preview\Libraries\Silverlight 폴더에 Microsoft.Expression.Interactivity.dll 어셈블리가 있는데요, 이것이 바로 Behavior와 Trigger를 지원하기 위한 기반 클래스들을 담고 있죠. 이 파일을 실버라이트 2 개발 머신에 복사해두세요.
2. 리버스 엔지니어링
Microsoft.Expression.Interactivity 어셈블리를 Reflector등으로 까서 코드를 봅니다. 그리고 실버라이트 2 라이브러리 프로젝트를 하나 만들어서 거기에 옮기면 끝.
단, 여기에서 주의할 점이 있는데요, 블렌드의 희한한 이름 제한때문에 라이브러리 프로젝트의 이름은 반드시 Microsoft.Expression.Interactivity가 아닌 다른 이름을 써야 해요. 물론 코드에서 정의하는 네임스페이스의 이름은 아무거나 관계 없어요. 전 HugeFlow.Interactivity라는 이름을 붙여봤어요.
3. Behavior와 Action 만들기복사하기
Microsoft.Expression.Interactivity에는 Behavior와 Action을 위한 기반 클래스만 준비만 되어 있을 뿐, 실제로 동작하는 건 없어요. 제대로 동작하는지 확인하려면 만들어 줄 필요가 있는데요, Expression 갤러리에 보면 Microsoft의 Kirupa가 공개한 Sample Silverlight 3 Behaviors 라이브러리가 있어요. 당연히 이것은 실버라이트 3를 위해 만든 라이브러리이지만 Microsoft.Expression.Interactivity를 기반으로 하므로 우리가 리버스 엔지니어링 한 라이브러리와 호환이 돼요. 여튼 이 라이브러리를 다운 받아서 실버라이트 2 라이브러리 프로젝트로 만들어주면 준비는 끝.
4. 블렌드 3에서 테스트 프로젝트 만들기
이제 동작 완구에서 보이는 것처럼 코드를 짜기만 하면 되죠. 그렇지만 단순히 XAML로 이 모든 걸 작성하기엔 좀 귀찮은 생각이 들죠? 문법도 헤깔리고요. 당연히 XAML 디자인은 블렌드에서 하는 게 기본이에요. 그러나 블렌드2는 블렌드3처럼 Behavior나 Trigger와 관련된 기능을 지원하지 않아요. 그렇다면? 그냥 블렌드 3를 써서 디자인 하는거죠.
5. 블렌드 3에서 만들어진 XAML을 실버라이트 2 프로젝트로 복사하기
블렌드 3로 디자이너가 Behavior와 Action을 사용하여 이것저것 디자인한 후 만들어진 XAML을 그대로 실버라이트 2 프로젝트로 복사하세요. 그리고 XAML의 xmlns에서 Microsoft.Expression.Interaction 부분만 우리가 리버스 엔지니어링 한 라이브러리로 바꿔주면 끝.
하나하나 과정을 보일까 하다가 별로 중요한 작업은 아니라 결과물만 첨부했어요.
이렇게 만든 Interactivity 라이브러리의 장점으로는
- 블렌드 3에서 Behavior와 Action 기능을 사용한 디자인을 그대로 복사하여 사용할 수 있다.
- 블렌드 2에서도 디자인이 깨지지 않고 보인다.
- 실버라이트 2 개발 환경에서도 아무 문제 없이 동작한다.
- 실버라이트 2의 지저분한 코딩을 XAML로 수용할 수 있게 한다.
- 가볍다. Interactivity 라이브러리의 용량은 겨우 36.5kb로 실제로 위의 샘플 애플리케이션의 XAP 파일은 33.3kb, 최적화하면 26.6kb밖에 안된다.
반면 물론 주의 할 점도 있어요.
- 리버스 엔지니어링한 것으로 일단은 불법이다.
(그렇지만 별로 문제 될 것 같지는 않습니다만… –_-) - 당연하지만 Behavior나 Action외의 ElementBinding이나 Effect와 같은 기능은 지원하지 않는다.
그 외엔 딱히 단점을 찾기 어렵네요^^
암튼, 실버라이트 3의 강화된 기능 중 일부는 이처럼 실버라이트 2에서 당장 사용할 수 있어요. 현업 개발자들은 실버라이트 3가 정식으로 릴리즈 될 때까지는 이런 방법을 적극적으로 활용하는 것도 좋을 거에요.
요즘 실버라이트 3 나와서 바쁘게 공부하시는 분들이 많을 거에요. 저도 그러고 싶은데 아직 하고 있는 일이 많아서 거의 손을 못대고 있네요. 즉, 저처럼 현재 실버라이트를 업무로 하는 분은 실버라이트 3가 릴리즈될 때까지는 좋든 싫든 실버라이트 2와 블렌드 2를 써야 해요. 한 가지 좋은 소식이 있어요. 실버라이트 3가 엄청나게 많은 기능들을 추가했지만 그 중에 일부는 비트맵 이펙트처럼 실버라이트 2에서는 구현이 불가능한 것도 있지만 비헤이비어Behavior처럼 실버라이트 2에서도 비교적 간단하게 구현할 수 있는 것도 있다는거죠!
네, 저는 최근 앞으로 5개월 이상 사용할 실버라이트 2를 위해 실버라이트 3 코드에서 Behavior와 Action 그리고 Trigger를 옮기고 있어요. 이 기능들은 단순히 AttachedProperty를 비롯한 실버라이트 2에서 지원하는 몇몇 클래스로만 이루어져 있거든요.
그런데 이상하게 새로 만든 Trigger클래스가 실행도 잘되고 비주얼 스튜디오의 디자이너에서도 잘 보이는데 유독 블렌드(2)에서만 에러 화면이 나오는거에요. 아무리 Behavior와 Trigger가 좋다고 해도 블렌드와 충돌하면 의미가 없겠죠. 그래서 원인을 격리시키기 위해 새 솔루션을 만들고 클래스를 따로 분리해서 테스트하다가 재밌는 사실을 발견했어요.
바로 프로젝트의 이름 즉, 어셈블리의 이름이 Microsoft.Expression로 시작할 경우 블렌드가 그 어셈블리에 있는 클래스를 못 찾아온다는 사실이에요.
그런데 더 재밌는 건 어셈블리의 이름이 Microsoft.Expression까지만 썼을 경우는 동작하고요, 당연히 다른 이름을 쓸 때도 잘 동작한다는 거에요. 오직 Microsoft.Expression.XXXXXX라는 이름을 가진 어셈블리만 이 문제가 발생해요. 그리고 네임스페이스의 이름은 이 현상에 어떤 영향도 주지 않고요.
…후우… 이것 때문에 몇 시간을 썼는지…
그래서 결론이 뭐냐면요, 혹시 마이크로소프트에서 나온 라이브러리를 베껴쓸 때에는(…) 적어도 어셈블리 이름은 바꿔서 쓰라는거죠. 도둑질도 배워야 한다나요. ㅎㅎ
Silverlight DataBinding #2.zip