목차

소개

몇 주 전부터 실버라이트를 사용하기 시작했데, 정말 놀랍다. WPF의 강력한 표현력과 C#의 능력을 겸한 실버라이트는 정말 강력한 툴이 아닐까 생각해본다. 이 포스팅에서는 데이터베이스로 부터 데이터를 가져와서 실버라이트 응용 프로그램에 적용시키는 것을 다룰것이다. 실버라이트와 ASP.NET의 공통점에 대해서도 다룰것이기 때문에 ASP.NET 개발을 하고 있는 개발자이고 실버라이트에 대해 알고 싶은 개발자또한 이 포스팅을 보는것을 추천한다.



다룰 내용

이 포스팅을 통해 비지니스 어플리케이션을 제작하고자 한다. 비지니스 어플리케이션은 대부분 데이터(데이터베이스에서 가져온)를 다루기 때문에, 실버라이트에서 데이터가 어떻게 표현되는지를 살펴보기로 하자. Northwind 데이터베이스를 예제로 한다. LINQ 클래스를 생성하고, WCF 서비스를 사용하여 데이터를 받아올것이고, 최종적으로 리스트박스와 데이터그리드(데이터템플릿이 딸린)에 뿌려보기로 하자. 우리가 만들 UI는 고객, 주문, 주문 상세가 있는 마스터-디테일 형태가 될것이다. 그리고 이 포스팅에 소개되는 코드는 실버라이트2 RC1 을 기준으로 작성되었다. (역주:번역하면서 한글 실버라이트2 정식으로 테스트 하였습니다.)



예제 프로그램의 실행

예제 프로젝트를 실행하기위해 조금 해야할것이 있다. 기본적으로 솔루션에는 시작 프로젝트가 없기때문에, 솔루션의 설정 윈도우에서 수작업으로 DataApplication.Web 을 시작 프로젝트로 설정해 준다. (역주:이부분은 뭔가 이해가 안되는데, 제공되는 솔루션에는 이미 DataApplication.Web이 시작 프로젝트로 설정되어 있다.)



ASP.NET 프로그래머가 알아둘것

ASP.NET 프로그래머라면, ASP.NET 과는 다르게 실버라이트는 서버가 아닌 클라이언트에서 C#코드가 실행된다는것을 알아두자. 클라이언트라는 말에 자바스크립트라고 생각할 수도 있는데, 아니다. 실버라이트로 코딩함에 있어서 가장 좋은 방법은 자바스크립트를 사용하지 않고 클라이언트 코딩(C#)으로 모두 다 다루는것이다. (적어도 나는 자바스크립트를 잘 다루지 못하니깐)



왜 WCF를 사용하나? 그냥 DB에 접근하면 안되나?

음, 간단하게 말하자면 C# 코드가 클라이언트에서 돌아가고, 클라이언트에서는 데이터베이스에 직접 연결할 수 없기때문이다. 우리의 실버라이트 프로젝트에는 DataSet이나 DataSource 같은 것이 없다는것을 알아두자. 그리고 System.Data 네임스페이스에는 즐겨쓰던 클래스들이 빠져있다. 하지만, WCF 서비스 같은 좋은 것들이 포함되어 있기도 하다. 이 포스팅은 WCF 서비스를 통해 데이터를 가져오는 방법을 보여준다.



시작해보자

비주얼 스튜디오로 실버라이트 어플리케이션을 만들기 위해, 이곳에서 실버라이트 툴을 다운로드받아서 설치하자. 설치후에 Silverlight 응용 프로그램과 Sliverlight 클래스 라이브러리가 추가된것을 볼 수 있다. 여기서 "Silverlight 응용 프로그램" 을 선택하고 이름을 DataApplication 으로하여 시작하자.

사용자 삽입 이미지

확인하면 실버라이트 응용프로그램을 호스팅할 방법을 선택해야되는데, "ASP.NET 웹 응용프로젝트"를 선택하여 ASP.NET 서버측에서 LINQ 클래스와 WCF서비스를 만들 수 있도록 하자.

사용자 삽입 이미지
여기까지 제대로 했다면 두개의 프로젝트가 추가된 솔루션이 열린다. 그중 DataApplication 프로젝트는 클라이언트에서 돌아갈 실버라이트 프로젝트이고, DataApplication.Web 프로젝트는 서버측의 ASP.NET 프로젝트다.



LINQ 클래스 생성

Northwind 데이터베이스는 가벼우면서도 여러가지 상황에 잘 맞기때문에 쓸모가 많다. (여기서 받을 수 있다.) MDF 파일에는 SQLExpress로 접근할 수 있는 프로젝트도 포함되어있다. LINQ 데이터 클래스를 생성하기위해 ASP.NET 응용 프로그램에서 LINQ to SQL 클래스 추가를 선택하도록 하자. (역주:instNwnd.sql 파일만 실행시켜서 DB생성해도 무관하다.)

사용자 삽입 이미지
이제 서버탐색기에서 Northwind 데이터베이스로 새로운 연결을 하나 만들고(SQLExpress든 SQL Server든 상관없다. 자신이 가진걸로 하자.), Customers, Orders, OrderDetails 테이블을 LINQ 디자이너로 드래그하자. 여기서 중요한 점은 실버라이트 응용프로그램으로 데이터를 전송할 수 있도록 하기 위해서는 LINQ로 생성되는 데이터의 직렬화가 가능하도록 해주어야 한다는것이다. 직렬화가 가능하도록 설정하기 위해서 LINQ 디자이너의 빈공간을 클릭한 후 속성창에서 직렬화모드(Serialization Mode)를 Unidirectional 로 설정하도록 하자.

사용자 삽입 이미지



실버라이트 WCF 서비스 생성

이제 데이터를 받을 수 있는 서비스를 추가하자. 실버라이트2 베타2 이전에는 WCF 서비스를 사용하기 위해서는 몇가지 꼼수가 필요했었다. 그러나 다행히도 실버라이트2 베타2 이상의 버전에서는 "Silverlight 사용 WCF 서비스" 라는 템플릿이 제공된다. 자, 새항목추가 - Silverlight를 선택하고 DataService 라는 이름으로 "Sliverlight 사용 WCF 서비스"를 ASP.NET 프로젝트에 추가하자.

사용자 삽입 이미지
생성한 서비스에 세가지 메서드를 작성할건데, 하나는 모든 고객을 리턴하는것이고, 다른 하나는 한 고객의 주문을 리턴하는것이며, 나머지 하나는 특정 주문의 주문상세내역을 리턴해주는것이다. 이 메서드는 반드시 [OperationContract] 특성이 있다는것을 알아두자. (이 특성은 ASMX 서비스의 [WebMethod]와 유사하다.) 아주 간단한 LINQ를 이용해 데이터를 가져오는 것을 구현하였다. 아래 코드를 DataService.svc.cs 파일에 추가하자. (역주:이후 나오는 코드중 Customer, Order, OrderDetail 테이블은 실제 모두 마지막에 's'가 붙어있다. 원저자의 테이블과 약간 틀린듯하다. 번역본에 소개되는 코드에는 모두 's' 를 붙여 놓았다.)

  1. [OperationContract]   
  2. public List<Customers> GetCustomers()   
  3. {   
  4.     DataClasses1DataContext datacontext = new DataClasses1DataContext();   
  5.     return datacontext.Customers.ToList();   
  6. }   
  7.   
  8. [OperationContract]   
  9. public List<Orders> GetOrders(string customerID)   
  10. {   
  11.     DataClasses1DataContext datacontext = new DataClasses1DataContext();   
  12.     return (from order in datacontext.Orders   
  13.             where order.CustomerID == customerID   
  14.             select order).ToList();   
  15. }   
  16.   
  17. [OperationContract]   
  18. public List<Order_Details> GetOrderDetails(int orderID)   
  19. {   
  20.     DataClasses1DataContext datacontext = new DataClasses1DataContext();   
  21.     return (from orderdetail in datacontext.Order_Details   
  22.             where orderdetail.OrderID == orderID   
  23.             select orderdetail).ToList();   
  24. }  



실버라이트 프로젝트에 서비스 참조 추가

서버측의 ASP.NET 프로젝트에서 할일은 이걸로 끝이다. 데이터베이스에서 데이터를 얻기 위해 LINQ 클래스를 만들었고, 이 LINQ 객체를 전달해주기 위해서 WCF 서비스를 만들었다. 이제, 클라이언트측의 실버라이트에서 데이터를 사용할 준비가 끝난 셈이다. 이제 실제 사용을 위해 실버라이트 프로젝트인 DataApplication 프로젝트에 서비스 참조를 추가하자. 참조 - 서비스 참조 추가를 실행하고 팝업창에서 검색버튼을 누르면 방금 우리가 만들었던 WCF 서비스를 검색해준다.

사용자 삽입 이미지



UI 만들기

실버라이트 페이지, 컨트롤은 레이아웃을 담당하는 XAML 파일과 xaml.cs 파일의 코드 비하인드 파일로 구성된다. 간단하게보면 UI를 구성하기위한 *.aspx 파일과 각종 처리로직과 이벤트 핸들러 등이 있는 *.aspx.cs 파일로 구성되는 ASP.NET 과 유사하다. 자, 우리의 응용 프로그램에도 기본적인 레이아웃을 구성해 보자.



DataGrid 컨트롤을 사용하기위해 어셈블리 추가

데이터를 보여주기위해서 DataGrid 컨트롤을 사용하려고 하는데, 실버라이트에는 기본적으로 DataGrid 컨트롤의 참조가 추가되어 있지 않다. 직접 추가해주자. 이 과정은 ASP.NET 에서 사용자정의 컨트롤을 추가하는 방법과 거의 같다. DLL 참조를 추가하고 aspx 페이지에 태그를 등록했던것을 상기해보도록 하자. 실버라이트에서는 참조 - 참조추가를 클릭하고 목록에서 System.Windows.Controls.Data 를 선택하기만 하면 된다. (이 어셈블리에 DataGrid 컨트롤이 포함되어 있다.)

사용자 삽입 이미지
참조를 추가한 다음, XAML 코드에 추가한 참조의 네임스페이스를 등록해 주어야한다. 아래의 네임스페이스 선언을 Page.xaml 파일에 추가하자.

  1. xmlns:data="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Data"  

사용자 삽입 이미지



UI 레이아웃

이제 우리가 할것은, LayoutRoot 라는 이름의 Grid 를 만들고 3개의 행을 가지도록 한다. 첫번째 행은 어플리케이션 제목을 위해 사용한다.(width=50) 세번째 행은 상태바를 위해 사용한다.(width=20) 그리고 가운데 행은 메인데이터를 위해 사용한다. (width=*, 남는 공간을 전부 사용) LayoutRoot 그리드의 첫번째 행에 TextBlock를 추가하고 마지막 행에 txtStatus 라는 이름의 빈 TextBlock을 추가한다. LayoutRoot 그리드의 가운데 행(컨텐트 홀더로 사용하기로 한)에 ContentRoot 라는 이름의 2행 2열로 구성된 또 다른 그리드를 추가한다. 왼쪽 열은 너비 200이고 오른쪽은 나머지 전체를 차지 하도록 하고, 행들은 각각 60%, 40%의 비율을 가지도록 한다. ContentRoot 그리드의 왼쪽 열에는 ListBox 를 아래 행까지 합쳐서 배치하고, 오른쪽 열에는 위, 아래 행 두곳 각각의 DataGrid 를 배치하는데, 위쪽은 주문 데이터를 위한 그리드이고, 아래쪽은 주문상세를 위한 그리드이다. 말로 줄줄 풀어써서 짜증났다면 이 문장들을 XAML화한 코드가 아래에 있다. Page.xaml 파일에 추가하도록 하자.

  1. <UserControl xmlns:basics="clr-namespace:System.Windows.Controls;   
  2.     assembly=System.Windows.Controls"    
  3.     x:Class="DataApplication.Page"  
  4.     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"    
  5.     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"    
  6.     xmlns:data="clr-namespace:System.Windows.Controls;   
  7.         assembly=System.Windows.Controls.Data"   
  8.     Width="Auto" Height="Auto">   
  9.     <Grid x:Name="LayoutRoot" Background="White">   
  10.         <Grid.RowDefinitions>   
  11.             <RowDefinition Height="55" x:Name="HeaderRow" />   
  12.             <RowDefinition Height="*" x:Name="ContentRow"/>   
  13.             <RowDefinition Height="20" x:Name="FooterRow"/>   
  14.         </Grid.RowDefinitions>   
  15.         <Grid.ColumnDefinitions>   
  16.             <ColumnDefinition Width="*" />   
  17.         </Grid.ColumnDefinitions>   
  18.   
  19.         <!-- Heading -->   
  20.         <TextBlock x:Name="txtHeader" Grid.Row="0"    
  21.                    FontSize="20" Margin="5,5" Foreground="Blue"  
  22.                    Text="My First Data Application in Silverlight">   
  23.         </TextBlock>   
  24.   
  25.         <!-- A textblock in the footer to be used as an Status bar -->   
  26.         <TextBlock x:Name="txtStatus" Grid.Row="2"    
  27.                FontSize="10" Margin="5,0" Foreground="Red">   
  28.         </TextBlock>   
  29.   
  30.         <!-- Content Holder -->   
  31.         <Grid x:Name="ContentGrid" Grid.Row="1" Margin="5">   
  32.             <Grid.RowDefinitions>   
  33.                 <RowDefinition Height=".6*" />   
  34.                 <RowDefinition Height=".4*" />   
  35.             </Grid.RowDefinitions>   
  36.             <Grid.ColumnDefinitions>   
  37.                 <ColumnDefinition Width="200" />   
  38.                 <ColumnDefinition Width="*" />   
  39.             </Grid.ColumnDefinitions>   
  40.   
  41.             <!-- Listbox for displaying customers -->   
  42.             <ListBox x:Name="lstCustomers" Grid.Column="0" Grid.RowSpan="2"  
  43.                      DisplayMemberPath="ContactName"  
  44.                      Loaded="lstCustomers_Loaded"  
  45.                      SelectionChanged="lstCustomers_SelectionChanged">   
  46.             </ListBox>   
  47.   
  48.             <!-- DataGrid for displaying orders of a customer    
  49.                 (with autogenerated columns) -->   
  50.             <data:DataGrid x:Name="dgOrders" Grid.Row="0" Grid.Column="1"    
  51.                            AutoGenerateColumns="True"  
  52.                            SelectionChanged="dgOrders_SelectionChanged">   
  53.             </data:DataGrid>   
  54.   
  55.             <!-- DataGrid for displaying orderdetais for an order -->   
  56.             <data:DataGrid x:Name="dgOrderDetails" Grid.Row="1" Grid.Column="1"    
  57.                            AutoGenerateColumns="True"  
  58.                            AutoGeneratingColumn="dgOrderDetails_AutoGeneratingColumn">   
  59.             </data:DataGrid>   
  60.   
  61.         </Grid>   
  62.   
  63.     </Grid>   
  64. </UserControl>  

WPF 의 레이아웃에 관해서는 코드 프로젝트와 여러 다른 사이트에서 많이 다루고 있기 때문에 굳이 자세히 설명하지는 않겠다.(Sacha Barber의 이런 것 이 있다.) 우리는 ListBox와 DataGrid에 대해 훓어 보는 편이 낮다.



ListBox

lstCustomers 라고 명명한 리스트박스는 데이터베이스의 고객을을 보여주기위해 사용한다. Loaded 이벤트를 등록해서 바인딩을 할것이다. 리스트박스는 object 소스가 바인딩되었을때 각 items 컬렉션의 object.ToString() 을 해당 값으로 보여준다는것을 알아두자. 다른 값을 보여주려면 세가지 방법을사용할수 있다.

  • object.ToString() 메서드를 오버라이드 한다. (이 포스팅에서는 이렇게 구현하지 않는다.)
  • Data Template 를 정의한다. (이 방법이 제일 유연한 접근방법이다. 나중에 DataGrid의 컬럼을 직접 정의하면서 간단하게 살펴보겠지만, 지금은 이 방법을 사용하지 않는다.)
  • ListBox의 DisplayMemberPath 프러퍼티에 보여줄 객체의 프러퍼티를 설정한다. (아주 간단한 방법이다. 지금, 이 방법으로 구현해 보겠다.)

ListBox에 바인딩된 Customer 객체의 ContactName 프러퍼티를 보여줄 것이기 때문에, DisplayMemberPath = "ContactName" 라고 설정한다. 또한, DataGrid에 선택된 고객의 주문을 보여줄수 있도록 하기위해 SelectionChanged 이벤트도 등록해 주도록 하자.



DataGrids

이제는 DataGrid 를 설정하는것이 좀더 수월해졌다. 바인딘된 데이터에 따라서 자동으로 컬럼을 생성하도록 설정하면 되기 때문이다. 그리고, dgOrderDetails 그리드에 AutoGeneratingColumns 이벤트를 등록해주면 컬럼이 자동으로 생성될때 필요치 않는 컬럼을 제거하는 로직을 구현할 수 있는데, 이렇게 하는것이 특정 칼럼을 수월하게 제거하는 일반적인 방법이다. 그리고 자동생성말고 직접 컬럼을 설정하는것은 나중에 다시 알아보기로 하고, 일단은 자동생성으로 간단하게 구현해 보자.



코드 작성

ListBox 처리

ListBox에는 lstCustomers 라고 명명하고, Loaded 이벤트에서 우리가 보여줄 고객에 대한 정보를 보여주자. 실버라이트의 대부분의 서비스 호출은 비동기적으로 일어날 수 있기때문에 데이터가 바인딩되는 시점에 콜백함수를 등록하여 데이터 바인딩 결과에 대한 처리를 해주면 좋다. 여기서는 데이터 로딩과 관련된 메세지를 텍스트로 뿌려줄것이다. (처음에 LayoutGrid의 마지막행에 만들어 두었던 txtStatus 라는 이름의 텍스트 박스를 사용하기로 한다.)

  1. private void lstCustomers_Loaded(object sender, RoutedEventArgs e)   
  2. {   
  3.     DataServiceClient svc = new DataServiceClient();   
  4.     this.txtStatus.Text = "Loading customers...";   
  5.     svc.GetCustomersCompleted += new  
  6.       EventHandler<GetCustomersCompletedEventArgs>(svc_GetCustomersCompleted);   
  7.     svc.GetCustomersAsync();   
  8. }   
  9.   
  10. void svc_GetCustomersCompleted(object sender, GetCustomersCompletedEventArgs e)   
  11. {   
  12.     if (e.Error == null)   
  13.     {   
  14.         this.lstCustomers.ItemsSource = e.Result;   
  15.         this.txtStatus.Text = string.Empty;   
  16.     }   
  17.     else  
  18.     {   
  19.         this.txtStatus.Text =   
  20.             "Error occurred while loading customers from database";   
  21.     }   
  22. }  



고객에 대한 주문 보여주기

이제, ListBox에서 특정 고객을 선택하면 해당 고객에 대한 주문건을 보여주는 코드를 작성해 보자. ListBox의 SelectionChanged 이벤트 핸들러에서 아까 만들어 두었던 WCF 서비스를 호출하여 데이터를 가져오고, 이를 dgOrders 그리드에 바인딩시켜서 보여주자. 이번 구현에서는 간단한 구현을 위해 익명메서드를 사용해보기로 하자.

  1. private void lstCustomers_SelectionChanged(object sender, SelectionChangedEventArgs e)   
  2. {   
  3.     Customers selectedCustomer = this.lstCustomers.SelectedItem as Customers;   
  4.     if (selectedCustomer != null)   
  5.     {   
  6.         DataServiceClient svc = new DataServiceClient();   
  7.         this.txtStatus.Text = "Loading orders...";   
  8.         svc.GetOrdersCompleted +=   
  9.             delegate(object eventSender, GetOrdersCompletedEventArgs eventArgs)   
  10.             {   
  11.                 if (eventArgs.Error == null)   
  12.                 {   
  13.                     this.dgOrders.ItemsSource = eventArgs.Result;   
  14.                     this.txtStatus.Text = string.Empty;   
  15.                 }   
  16.                 else  
  17.                 {   
  18.                     this.txtStatus.Text =   
  19.                         "Error occurred while loading orders from database";   
  20.                 }   
  21.             };   
  22.         svc.GetOrdersAsync(selectedCustomer.CustomerID);   
  23.     }   
  24. }  



주문에 대한 상세주문정보 보여주기

ListBox의 SelectionChanged 이벤트와 유사하게 dgOrders에도 SelectionChanged 이벤트를 등록하고 코드를 작성하자. 이번에는 람다 표현식을 사용해서 구현해 보겠다.

  1. private void dgOrders_SelectionChanged(object sender, EventArgs e)   
  2. {   
  3.     Orders selectedOrder = this.dgOrders.SelectedItem as Orders;   
  4.     if (selectedOrder != null)   
  5.     {   
  6.         DataServiceClient svc = new DataServiceClient();   
  7.         this.txtStatus.Text = "Loading order details...";   
  8.         svc.GetOrderDetailsCompleted +=   
  9.             (eventSender, eventArgs) =>   
  10.             {   
  11.                 if (eventArgs.Error == null)   
  12.                 {   
  13.                     this.dgOrderDetails.ItemsSource = eventArgs.Result;   
  14.                     this.txtStatus.Text = string.Empty;   
  15.                 }   
  16.                 else  
  17.                 {   
  18.                     this.txtStatus.Text =   
  19.                         "Error occurred while loading order details from database";   
  20.                 }   
  21.             };   
  22.         svc.GetOrderDetailsAsync(selectedOrder.OrderID);   
  23.     }   
  24. }  



dgOrderDetails 에서 일부 자동생성된 칼럼 제거하기

아까 작성한 XAML에서 우리는 DataGrid의 AutoGenerateColumns 속성을 true 로 설정함으로써 바인딩된 데이터에 따라서 컬럼을 생성하도록 하였다. 이제 이렇게 생성된 컬럼중에서 dgOrderDetails 그리드의 OrderID 컬럼을 제거해보자. 아까 말했다시피 AutoGeneratingColumns 이벤트 핸들러를 통해서 가능하다.

  1. private void dgOrderDetails_AutoGeneratingColumn(object sender,   
  2.     DataGridAutoGeneratingColumnEventArgs e)   
  3. {   
  4.     if (e.Column.Header.ToString() == "OrderID")   
  5.         e.Column.Visibility = Visibility.Collapsed;   
  6. }  



중간점검.. 프로젝트 실행

어느정도 볼만한 프로그램이 만들어진것 같다. 실행해서 이것저것 마구 건드려보자. 고객, 주문, 주문상세 셋의 선택이 바뀔때 연관되어 바뀌는 관계도 보고, 그리드 자체의 수정기능, 컬럼헤더클릭으로 정렬기능, 헤더의 컬럼 사이즈 변경, 브라우저 크기에 따른 동적 크기 계산 등등.. 원하는건 뭐든지 해보라. 멋지지 않은가? 자, 이제 DataGrid 의 컬럼과 템플릿에 대해 알아보자.

사용자 삽입 이미지



컬럼 정의

실버라이트의 컬럼 설정방법은 ASP.NET의 방법과 유사하다. DataGrid는 세가지 종류의 컬럼을 설정할 수 있다.

  • DataGridTextBoxColumn - 보여주기만 하거나, 수정도 가능한 TextBlock 형태의 데이터. 바인딩된 객체의 어떤 프러퍼티를 보여줄것인지를 DisplayMemberPath 를 통해 설정해줘야 한다.
  • DataGridCheckBoxColumn - 불린, 널러블불린값의 체크박스의 형태. 읽기전용으로 설정하거나, 수정할 수 있도록 설정할 수도 있다.
  • DataGridTemplateColumn - ASP.NET의 TemplateColumn 처럼 강력한 기능을 제공해준다. DataTemplate 을 정의해서 원하는 컨트롤을 해당 칼럼에 설정할 수있다. DataTemplate에 대한 좀 더 자세한 내용은 MSDN 의 이곳에서 확인하도록 하자.

칼럼들에대해 좀더 자세히 알고 싶으면 Scott Morris 의 블로그를 방문해보자. 꽤 좋은 포스팅을 해놓았다.

자, 아제 실제 응용프로그램에 적용해 보도록하자. 간단한 구현을 위해 네개의 칼럼만 정의하겠다. OrderID, EmployeeID 에는 DataGridTextBoxColumn을 사용하고, OrderDate에는 DataGridTemplateColumn 을 사용할건데, CellTemplate 에는 TextBlock을, CellEditingTemplate에는 DatePicker를 사용해보자. 아, 마지막으로 Frieght 칼럼에도 TemplateColumn을 사용하는데 거기에는 데이터 표현을 위해 TextBlock을 넣고 값 수정을 위해 Slider를 넣어서 값의 증감을 조절할 수 있도록 하겠다. 근데, DataTemplate 에는 하나의 컨트롤만 넣을 수 있도록 되어 있으므로, StackPanel을 만들어 이 패널에 TextBlock과 Slider를 넣은뒤에 DataTemplate에 넣기로 하자.

변경된 dgOrders 부분의 코드는 아래와 같다.

  1. <!-- DataGrid for displaying orders of a customer -->   
  2. <data:DataGrid x:Name="dgOrders" Grid.Row="0" Grid.Column="1"    
  3.    AutoGenerateColumns="False"  
  4.    SelectionChanged="dgOrders_SelectionChanged">   
  5.     <data:DataGrid.Columns>   
  6.         <!-- OrderID text column -->   
  7.         <data:DataGridTextColumn Header="Order ID" Binding="{Binding OrderID}" />   
  8.   
  9.         <!-- EmployeeID text column -->   
  10.         <data:DataGridTextColumn Header="Employee ID" Binding="{Binding EmployeeID}" />   
  11.   
  12.         <!-- OrderDate template column -->   
  13.         <data:DataGridTemplateColumn Header="Order Date" Width="150">   
  14.             <data:DataGridTemplateColumn.CellTemplate>   
  15.                 <DataTemplate>   
  16.                     <TextBlock Text="{Binding OrderDate}" />   
  17.                 </DataTemplate>   
  18.             </data:DataGridTemplateColumn.CellTemplate>   
  19.             <data:DataGridTemplateColumn.CellEditingTemplate>   
  20.                 <DataTemplate>   
  21.                     <basics:DatePicker SelectedDate="{Binding OrderDate, Mode=TwoWay}" />   
  22.                 </DataTemplate>   
  23.             </data:DataGridTemplateColumn.CellEditingTemplate>   
  24.         </data:DataGridTemplateColumn>   
  25.   
  26.         <!-- Freight template column -->   
  27.         <data:DataGridTemplateColumn Header="Freight" Width="150">   
  28.             <data:DataGridTemplateColumn.CellTemplate>   
  29.                 <DataTemplate>   
  30.                     <TextBlock Text="{Binding Freight}"></TextBlock>   
  31.                 </DataTemplate>   
  32.             </data:DataGridTemplateColumn.CellTemplate>   
  33.             <data:DataGridTemplateColumn.CellEditingTemplate>   
  34.                 <DataTemplate>   
  35.                     <StackPanel Orientation="Horizontal">   
  36.                         <TextBlock Text="{Binding Freight}" Width="50" />   
  37.                         <Slider Value="{Binding Freight, Mode=TwoWay}" Width="100"  
  38.                                 Minimum="0" Maximum="500" />   
  39.                     </StackPanel>   
  40.                 </DataTemplate>   
  41.             </data:DataGridTemplateColumn.CellEditingTemplate>   
  42.         </data:DataGridTemplateColumn>   
  43.     </data:DataGrid.Columns>   
  44. </data:DataGrid>  

같은 방식으로 DataTemplate 를 ListBox에도 사용할 수 있다. In a similar way, we can use DataTemplates for our ListBox. 우리가 가져올 데이터중에 비트맵이미지를 리턴해주는 PictureProperty가 있다고 한다면, 아래와 같은 방식으로 ListBox에 이미지를 보여줄 수도 있다.

  1. <ListBox x:Name="lstCustomer">   
  2.     <ListBox.ItemTemplate>   
  3.         <DataTemplate>   
  4.             <StackPanel Orientation="Vertical">   
  5.                 <TextBlock Text="{Binding NameProperty}"></TextBlock>   
  6.                 <Image Source="{Binding PictureProperty}"></Image>   
  7.             </StackPanel>   
  8.         </DataTemplate>   
  9.     </ListBox.ItemTemplate>   
  10. </ListBox>  

위 코드는 이렇게도 할수있다라는 샘플에 불과하니깐 우리 응용프로그램에는 적용시키지 말자. (우리가 가져오는 데이터에는 이미지가 없다.)



두번째 실행

다시 프로젝트를 실행해보자. OrderDate 칼럼을 더블클릭해서 DataPicker가 어떻게 나타나는지 보라. Freight 칼럼의 Slider를 조절하여 값을 조절해보고, 여러개의 컨트롤들이 어떻게 결합되어 하나의 칼럼에 나타나는지 유심히 보라. 알아채고 있었는지 모르겠지만, 지금 우리가 이렇게 칼럼을 수정해서 컨트롤을 집어넣은것은 모두 레이아웃코드에서 해결했다. *.cs 파일은 코딩도 하지 않았는데 말이다. 이게 바로 WPF 프레임웍의 장점이 아니겠는가!

사용자 삽입 이미지




DB에 변경데이터 적용하기

음, DB에 데이터를 다시 적용하는것은 이번 포스팅의 범위에서는 좀 벗어난다. 하지만 여러분이 WCF 서비스가 서버와 클라이언트사이에서 통신하는 것을 알고 있으면, 서비스쪽에 데이터를 저장하는 몇가지 함수를 만들고, 그 함수를 호출해서 사용한다면, 실버라이트에서도 쉽게 데이터 작성도 가능하도록 구현할 수 있다고 본다. 그리드에서 바인딩된 값이 변경될때 변경된값은 서버의 값이 아니라, 그리드에 바인딩된 DataContext 라는 것을 기억하자. 실제 DB에 변경을 하고 싶다면, ASP.NET 프로젝트의 WCF 서비스를 이용해서 변경된 데이터를 보내서 DB에 업데이트하도록 해야할 것이다. Ronnie Saurenmann의 동영상이(여기) 두개있는데, 그는 헬퍼 클래스를 이용해서 데이터를 마치 DataSet 처럼 다루고 있다. 변경된 데이터 관리라든지, 원본데이터라든지, 업데이트시에 변경된 데이터만 보내도록 한다든지 하는 방법을 보여주고 있다. 이 동영상을 꼭 한번 보기를 권장한다.



결론

이걸로 끝이다. 나는 이 포스팅을 통해 얼마나 간편하게 데이터관리 응용프로그램을 만들수있는지를 보여주고 싶었다. 배웠던것을 다시 한번 보자면, LINQ로 데이터엑세스 레이어를 만들었고, ASP.NET 프로젝트의 WCF 서비스를 통해 접근할 수 있도록 하였다. 그리고 실버라이트를 통해 클라이언트 측에서 데이터를 가져왔고, 이를 몇몇 컨트롤들을 사용해서 보여주었다.이 포스팅이 여러분들의 실버라이트 응용프로그램 제작의 동기가 될 수 있기를 바란다. 언제나 즐거운 실버라이트 프로그래밍을 하기를..



문서 이력

  • 2008년 08월 12일 - 기사 게시
  • 2008년 10월 19일 - Silverlight 2 RC1에 맞도록 업데이트
  • 2009년 01월 30일 - 한글로 번역되어 포스팅됨




라이센스

이 포스팅에 관련된 모든 파일과 소스코드는 The Code Project Open License (CPOL)를 따릅니다.



저자에 관해

Syed Mehroz AlamSyed Mehroz Alam has done his Bachelors in Computer and Information Systems Engineering in 2007. He loves logical challenges and has written certain logic games and mathematical applications in C/C#/VB in his early age which can be found at: http://www.geocities.com/smehrozalam/myprogs.html
He writes his blog at http://smehrozalam.wordpress.com
Any questions, comments or suggestions are always welcomed at smehrozalam at yahoo dot com.

번역

이 글의 원저자에게 양해를 구하고 번역하였습니다. 전문 번역가도 하니고 학습을 목적으로 번역을 했기때문에 서툰 표현이 많을 수 있습니다. 번역본을 퍼가실때는 출처를 남겨주시기 바랍니다.

+ Recent posts