이 내용은 2011년 6월 현재 Silverlight5 Beta를 기준으로 작성 되었으며 Silverlight5 공식버전(RTM) 발표까지 꾸준히 업데이트 예정입니다.
아울러, 현재 Beta버전이지만 강좌를 따라해 보시는데 아무 문제 없으실거에요. 도움 되시길 바랍니다.
==================================================================================================



실버라이트에서 데이터 바인딩은 데이터를 UI단에 직접 연결하여 코드의 도움 없이 데이터를 표시하고 업데이트해주는 기술입니다. 기본적인 개념은 WinForm이나 WebForm에서의 데이터 바인딩과 거의 같지만 사용법에서는 꽤 차이가 나지요.

 

실버라이트에서의 데이터 바인딩은 지난 강좌의 엘리먼트 바인딩과 비슷하게 XAML에서 정의를 하게 됩니다.

 

그런데, 실버라이트의 데이터 바인딩은 구현하는 방법이 여러 가지가 있어서 설명 드리기가 좀 애매한 부분이 있습니다. 특히, 코드와 연계하여 처리하는 부분이 있다 보니 다른 강좌나 책을 보시면 조금씩 이야기를 풀어나가는 방식이 다를 겁니다.

 

저는 어떻게 이야기를 풀어나갈까 고민하다가 실버라이트 데이터 바인딩의 핵심 키워드를 중심으로 차근차근 설명하기로 했습니다.

 

그래서, 이번 강좌에서는 데이터 바인딩의 가장 기초가 되는 DataContext에 대해서 살펴보기로 하죠.

 

 

 

 

DataContext란?

 

실버라이트의 데이터 바인딩은 CLR 객체XAML에 연결하여 자동으로 UI단에 표시하고 업데이트하는 작업입니다. 따라서 데이터 바인딩의 소스가 되는 클래스가 반드시 있어야 합니다. (이런 이유 때문에 XAML만 가지고도 설명할 수 있는 엘리먼트 바인딩을 먼저 설명 드렸습니다.)

 

DataContext는 XAML에 바인딩할 데이터를 담고 있는 객체라고 생각하시면 대충 맞습니다. 

 

다음과 같은 Product 클래스가 있다고 가정합시다.

 

public class Product

{

        public string ModelName {get; set;}

        public string Description { get; set;}

        public double UnitCost { get; set; }        

}


 

 

그리고, 다음과 같은 XAML이 있다고 가정합시다.

<TextBox Text="{Binding Description}"/>


 

위 TextBox의 DataContext에 Product 클래스의 인스턴스를 할당해놓으면 TextBox의 데이터 바인딩 소스가 Product 클래스의 인스턴스가 되고, Text 속성이 Description에 바인딩되어 있으므로 이 TextBox의 Text는 Description 속성이 되는 것이지요.

 

 

설명이 좀 애매하죠? 실제 예를 보면서 살펴보도록 하죠.

 

 

다음과 같이 Product 클래스를 바인딩하기 위한 UI를 구성합니다. 이런 UI를 만들 때 Grid를 사용하는 것이 일반적이겠죠.

 

image_2.png

 

 

 

다음은 위 UI에 대한 XAML 소스입니다.

 

image_6.png

 

 

 

 

XAML 소스를 보니 3개의 TextBox에 Product 클래스의 각 속성을 바인딩했군요.

 

그런데, XAML 소스를 아무리 봐도 DataContext라는 단어가 보이질 않네요?

 

DataContext는 다음과 같이 2가지 방식으로 지정할 수 있습니다.

 

 

 

 

1) 코드로 DataContext 지정하기

 

다음 소스를 보도록 합시다. 꼭 보셔야 할 부분에 밑줄을 쳐놨습니다.

 

using System.Windows;

using System.Windows.Controls;

using System.Windows.Documents;

namespace DataContextSample

{

    public partial class MainPage : UserControl

    {

        private Product newProduct = null;

 

        public MainPage()

        {

            InitializeComponent();

            this.Loaded += new RoutedEventHandler(MainPage_Loaded);

        }

 

        void MainPage_Loaded(object sender, RoutedEventArgs e)

        {

            newProduct = new Product();

            newProduct.ModelName = "My Model Name";

            newProduct.UnitCost = 23.9;

            newProduct.Description = "This is the description of current model.";

 

            GridModelInfo.DataContext = newProduct;

        }

 

    }

}


위 소스를 보시면 MainPage_Loaded 이벤트 핸들러에서 새 Product 인스턴스를 만든 후 GridModelInfo라는 Grid의 DataContext에 할당하고 있죠.

 

 

이 코드를 실행해보면 다음과 같이 Product 객체의 속성들이 TextBox에 바인딩된 것을 확인하실 수 있습니다.

image_8.png

 

 

 

 

 

뭔가 좀 정신 없이 온 것 같지만 어쨌든 잘 돌아가는 것 같군요^^

 

 

그런데, 왜 GridModelInfo.DataContext에 Product 인스턴스를 지정했을까요?

 

 

기본적으로 DataContext를 Panel에 지정하게 되면 자식 요소들에게 전파가 됩니다.

물론, 각 TextBox마다 일일이 지정할 수도 있습니다. 하지만 그러려면 다음과 같이 각 TextBox에 명시적으로 이름을 주어야만 코드 비하인드에서 접근이 가능하겠죠.

 

image_10.png

 

image_12.png

 

 

 

위와 같이 해도 같은 결과를 얻을 수 있습니다만 부모 Grid인 GridModelInfo의 DataContext에 지정하는 편이 더 깔끔하고 디자인 종속성도 줄어들겠죠. 게다가 위와 같이 하려면 데이터바인딩을 사용하는 이유가 없겠죠^^;

 

 

여기서 잠깐

XAML에서 x:Name을 이름을 정의한 요소를 코드 비하인드에서 직접 사용하는 것을 줄일 수록 디자인과 코드가 더 많이 분리됩니다. 디자이너가 XAML에서 기존 요소의 이름을 바꿔버리는 순간 컴파일이 안되겠죠? 그래서 데이터 바인딩을 적극적으로 사용할 수록 디자인과 코드를 더 많이 분리할 수 있습니다. 맨 처음의 XAML 소스를 보시면 TextBox에 Name이 지정되지 않았음을 확인하실 수 있습니다.


 

 

갑자기 실험 정신이 발동하는군요… 다음과 같이 하면 어떻게 될까요?

 

image_14.png

image_16.png

 

 

 

아하~ 자식 요소에 명시적으로 DataContext를 할당하면 그 녀석이 사용되는군요!

 

 

 

 

 

또 다른 실험을 해봤습니다.  다음과 같이 TextBox의 직속 부모가 아니라 부모의 부모 Panel의 DataContext에 Product 객체를 지정해도 같은 결과를 얻을 수 있습니다.

 

image_18.png

 

 

 

 

좀 우스꽝스러운 예제가 되었지만 다음 사실을 확인할 수 있었습니다.

 

1) Panel의 DataContext는 자식 요소로 전파가 된다, 이 때 자식의 자식으로도 계속 전파가 된다.

2) 만약 자식 요소에 DataContext를 바로 지정하면 부모 Panel의 DataContext 대신 지정한 DataContext가 사용이 된다.


 

일반적으로는 처음 예제에서와 같이 직속 Panel의 DataContext를 사용하는 경우가 대부분이겠지만 여러 개의 데이터 소스를 혼합하여 사용하는 것도 충분히 가능하겠네요.

 

 

 

 

 

2) XAML에서 지정하기

 

위 예제에서는 GridModelInfo.DataContext에 Product 객체를 할당하였습니다. 그런데 만약, 디자이너가 GridModelInfo의 이름을 바꿔버리면 어떡하죠?

 

이전 예제에서는 코드 비하인드에서 GridModelInfo를 직접 참조하여 사용하였는데, 이 부분까지 완전히 분리해보도록 하겠습니다. 즉 데이터 바인딩의 전부를 XAML에서 처리해보도록 하죠.

 

다음과 같이 XAML에서 아예 Product의 인스턴스를 Resource로 생성할 수 있습니다.

 

image_20.png

 

 

 

 

이렇게 한 후 Textbox에 바인딩할 때 Source 속성에 생성한 Resource를 지정하면 됩니다.

Text="{Binding ModelName, Mode=TwoWay, Source={StaticResource productDataSource}}"


 

 

 

XAML에 Product 객체의 속성값까지 다 지정해버려서 좀 넌센스가 되어버렸지만 이렇게 하면 컴파일하지 않고도 Visual Studio에서 바로 바인딩 결과를 볼 수 있는 장점이 생기는군요^^ (참고로 Expresson Blend는 SampleData를 이용해서 가짜 데이터를 바인딩해주는 기능이 있습니다.)

 

image_22.png

 

 

 

이 예제에서 대해서는 좀 더 할 말이 있으니 나중에 다시 언급하도록 하겠습니다.

 

 

 

 

 

INotifyPropertyChanged  인터페이스

 

데이터 바인딩의 장점은 객체의 값을 바꾸면 UI에 보이는 값도 바뀐다는 겁니다.

 

다음과 같이 버튼을 클릭하면 Product 객체의 UnitCost 값이 바뀌도록 코딩을 한 후 실행해보면…. 절대로 값이 안 바뀝니다^^;

 

image_24.png

 

 

 

 

왜 그럴까요? Product 클래스가 INotifyPropertyChanged 인터페이스를 구현하고 있지 않기 때문입니다.

실버라이트에서 어떤 클래스의 값이 바뀌었을 때 바인딩된 UI도 같이 바뀌게 하려면 반드시 INotifyPropertyChanged 인터페이스를 구현해야 합니다. 그냥 공식이라고 생각하시면 되요^^

 

 

 

그래서 다음과 같이 Product 클래스를 수정해야 합니다.

 

using System.ComponentModel;

 

namespace DataContextSample

{

    public class Product : INotifyPropertyChanged

    {

        private string _modelName;

        private string _description;

        private double _unitCost;

 

        public string ModelName

        {

            get

            {

                return _modelName;

            }

            set

            {

                _modelName = value;

                RaisePropertyChangedEvent("ModelName");

            }

        }

        public string Description

        {

            get

            {

                return _description;

            }

            set

            {

                _description = value;

                RaisePropertyChangedEvent("Description");

            }

        }

  

        public double UnitCost

        {

            get

            {

                return _unitCost;

            }

            set

            {

                _unitCost = value;

                RaisePropertyChangedEvent("UnitCost");

            }

        }

 

        public event PropertyChangedEventHandler PropertyChanged;

        void RaisePropertyChangedEvent(string propertyName)

        {

            if (PropertyChanged != null)

            {

                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));

            }

  

        }

 

    }

}


 

 

INotifyPropertyChanged 인터페이스를 구현한 후 속성이 변경될 때마다 PropertyChangedEventHandler를 작동시켜줘야만 객체의 변경된 값이 바인딩된 XAML에도 반영이 됩니다. 위 소스에서는 이벤트 발생을 쉽게 하기 위해서 RaisePropertyChangedEvent라는 메서드를 만들어서 사용했습니다.

 

 

이렇게 수정한 후 실행해보면 다음과 같이 값이 바뀌게 되죠.

 

image_28.png

 

 

 

 

자, 이제 마지막 예제가 나갑니다. 위에서 XAML만 가지고 데이터바인딩을 한 예를 보여드렸는데, 이 예를 좀더 유용하게 바꿔보도록 하죠.

 

 

다음과 같이 버튼 하나 빼고 모든 x:Name을 없애버렸습니다. 즉 버튼을 제외하고는 모든 XAML 요소에 이름을 지정하지 않았죠.

(여기서 버튼은 데이터 바인딩과 전혀 상관이 없습니다.^^)

 

image_30.png

 

 

 

 

그 다음 코드에서 다음과 같이 productDataSource 리소스의 참조를 가져온 후 Product로 타입캐스팅 해서 각 속성을 지정합니다.

image_32.png

 

 

 

 

 

그리고 실행해 보면….. 잘 돌아갑니다^^

image_34.png

 

 

 

 

 

코드 비하인드에서는 Resource만 참조했을 뿐 데이터 바인딩을 위해 XAML의 어떠한 요소도 직접 사용하지 않게 되었죠. 물론, Resource의 키가 바뀌면 컴파일 에러가 나겠지만 GridModelInfo를 직접 참조하지 않음으로써 디자인과 코드가 더욱 분리되었습니다. 일반적으로 Resource의 키가 XAML 요소의 이름보다는 더 안전합니다.

 

 

 

왜 더 안전한지 다음의 예를 보도록 하죠. 위 샘플을 좀더 개선해봅시다.

 

각 TextBox에 있던 Source 속성을 다 없애버리고 부모 Grid의 DataContext에 productDataSource 리소스를 할당해버렸네요!

image_36.png  

 

 

 

만약, 디자인이 변경되어서 XAML 요소의 계층 구조가 이리저리 바뀌게 되었을 때 위와 같이 하면 사실상 디자인과 코드가 거의 완벽하게 분리가 됩니다.

 

 

 

그런데, 이 방법에는 한 가지 문제가 있습니다. XAML에 정의되는 Resource는 Static Resource이어서 ReadOnly라는 것이죠. 그래서 위 예의 코드 비하인드에서는 Resource의 참조를 얻어와서 속성만 바꾸고 있습니다. 만약 DataContext를 다른 객체로 완전히 바꾸려면 코드비하인드에서 할 수 밖에 없습니다.

 

 

그래서… 일반적으로 데이터 바인딩에서 DataContext는 코드에서 할당하는 경우가 많습니다.

 

 

 

 

 

 

이로써 DataContext에 관련된 대부분의 이야기를 다 했습니다.

그런데… 왜 이렇게 다양한 상황에 대해서 설명을 드렸냐면… 제가 데이터 바인딩을 공부할 때는 각 샘플/강좌마다 설명하는 방식이 제각각 이었기 때문입니다. 그래서 DataContext의 정확한 동작 방식에 대해서 설명드리기 위해서 장황하게 말씀을 드렸네요.

 

 

 

 

이제 데이터 바인딩에 대한 기초는 배웠으니 다음 강좌에서는 컬렉션을 바탕으로 하는 현실적인 예에 대해서 살펴보도록 하겠습니다.


Silverlight5 시리즈 강좌 리스트
[Silverlight5강좌] 1. 실버라이트, 어디에 쓰는 물건인가요?
[Silverlight5강좌] 2. 실버라이트로 Hello World 만들기
[Silverlight5강좌] 3. 레이아웃 시스템 이해하기 #1 - 레이아웃 시스템 개념 이해하기
[Silverlight5강좌] 4. 레이아웃 시스템 이해하기 #2 - 레이아웃 시스템의 꽃 Grid 살펴보기
[Silverlight5강좌] 5. 이것만은 꼭! - 간단한 Expression Blend 사용법
[Silverlight5강좌] 6. 유저 인터페이스를 만들어봅시다 - 기본 컨트롤 사용하기
[Silverlight5강좌] 7. 나에게 더 많은 컨트롤을 줘! - Silverlight Toolkit
[Silverlight5강좌] 8. 컨트롤 사이의 연결 고리 - Element Binding
[Silverlight5강좌] 9. 데이터 바인딩 #1 - DataContext
[Silverlight5강좌] 10. 좀더 깊은 곳으로 - 의존 속성(Dependency Property) 이해하기
[Silverlight5강좌] 11. 애니메이션 시스템 이해하기 #1 - Render Transform
[Silverlight5강좌] 12. 애니메이션 시스템 이해하기 #2 - Perspective 3D
[Silverlight5강좌] 13. 애니메이션 시스템 이해하기 #3 - Storyboard
[Silverlight5강좌] 14. 한 방에 편리하게 모양 바꾸기 - Style
[Silverlight5강좌] 15. 컨트롤 모양을 내 마음대로 - ControlTemplate
[Silverlight5강좌] 16. 나만의 컨트롤 만들기 - User Control
[Silverlight5강좌] 17. 실버라이트의 최고 장점! 미디어 제어하기 - MediaElement
[Silverlight5강좌] 18. 손쉽게 다중 페이지 UI 만들기 - Navigation Framework
[Silverlight5강좌] 19. 실버라이트로 데스크탑 응용 프로그램 만들기
[Silverlight5강좌] 20. 실버라이트 응용 프로그램 배포하기




Silverlight5 동영상 강좌 리스트

[Silverlight5 동영상 강좌] 2. 실버라이트로 Hello World 만들기
[Silverlight5 동영상 강좌] 3. 레이아웃 시스템 이해하기 #1 - 레이아웃 시스템 개념 이해하기
[Silverlight5 동영상 강좌] 4. 레이아웃 시스템 이해하기 #2 - 레이아웃 시스템의 꽃 Grid 살펴보기
[Silverlight5 동영상 강좌] 5. 이것만은 꼭! - 간단한 Expression Blend 사용법
[Silverlight5 동영상 강좌] 6. 유저 인터페이스를 만들어봅시다 - 기본 컨트롤 사용하기
[Silverlight5 동영상 강좌] 7. 나에게 더 많은 컨트롤을 줘! - Silverlight Toolkit
[Silverlight5 동영상 강좌] 8. 컨트롤 사이의 연결 고리 - Element Binding
[Silverlight5 동영상 강좌] 9. 데이터 바인딩 #1 - DataContext
[Silverlight5 동영상 강좌] 10. 좀더 깊은 곳으로 - 의존 속성(Dependency Property) 이해하기
[Silverlight5 동영상 강좌] 11. 애니메이션 시스템 이해하기 #1 - Render Transform
[Silverlight5 동영상 강좌] 12. 애니메이션 시스템 이해하기 #2 - Perspective 3D
[Silverlight5 동영상 강좌] 13. 애니메이션 시스템 이해하기 #3 - Storyboard
[Silverlight5 동영상 강좌] 14. 한 방에 편리하게 모양 바꾸기 - Style
[Silverlight5 동영상 강좌] 15. 컨트롤 모양을 내 마음대로 - ControlTemplate
[Silverlight5 동영상 강좌] 16. 나만의 컨트롤 만들기 - User Control
[Silverlight5 동영상 강좌] 17. 실버라이트의 최고 장점! 미디어 제어하기 - MediaElement
[Silverlight5 동영상 강좌] 18. 손쉽게 다중 페이지 UI 만들기 - Navigation Framework
[Silverlight5 동영상 강좌] 19. 실버라이트로 데스크탑 응용 프로그램 만들기
[Silverlight5 동영상 강좌] 20. 실버라이트 응용 프로그램 배포하기






profile