Cheryl Simmons
Microsoft Corporation

2006년 8월

적용 대상:
Microsoft Visual Studio 2005
Microsoft .NET Framework 2.0
Microsoft Windows Forms

요약 : 바인딩된 데이터의 서식을 쉽게 지정할 수 있도록 지원하는 BindingControlBindingsCollection 클래스의 추가 기능에 대해 자세히 알아봅니다.

Microsoft 다운로드 센터 (영문)에서 C# 및 Visual Basic으로 작성된 코드 샘플을 다운로드하십시오(23KB).

목차

소개 소개
서식 지정 및 고급 바인딩 대화 상자 서식 지정 및 고급 바인딩 대화 상자
코드를 사용한 자동 서식 지정 코드를 사용한 자동 서식 지정
DataSourceUpdateMode 사용 DataSourceUpdateMode 사용
Null 및 DBNull 값 자동 처리 Null 및 DBNull 값 자동 처리
향상된 오류 및 예외 처리 향상된 오류 및 예외 처리
결론 결론

소개

Windows Forms 데이터 바인딩에 추가된 내용 중 가장 중요한 것은 아마도 BindingSource 구성 요소일 것입니다. BindingSource 구성 요소는 통화 관리, 변경 알림 및 바인딩된 목록에 있는 멤버에 대한 간편한 액세스 기능을 제공하여 개발자의 작업을 간소화해 줍니다. 한편, 데이터 바인딩에 추가된 몇 가지 기능은 잘 알려지지 않았지만 살펴볼 가치가 있으며 실제로 BindingSource 구성 요소에서 제공하는 기능을 보완하는 중요한 기능이 추가되었습니다.

Binding 개체는 .NET Framework 2.0에서 바인딩 작업에 대한 제어를 향상시키는 몇 가지 새로운 멤버를 갖고 있습니다. 예를 들어 데이터 원본이 업데이트될 경우 바인딩 컨트롤에서 데이터의 서식을 지정하는 방법과 데이터 원본에서 nullDBNull 값을 처리하는 방법을 제어할 수 있습니다. 이러한 새로운 멤버는 ControlBindingsCollection의 해당 Add 메서드에서도 지원됩니다. 이러한 추가 기능은 Visual Studio의 서식 지정 및 고급 바인딩 대화 상자를 사용하거나 코드를 통해 활용할 수 있습니다. 또한 BindingComplete 이벤트가 추가되어 Binding 개체는 바인딩 과정에서 발생할 수 있는 예외 및 오류 처리를 보다 효과적으로 지원할 수 있게 되었습니다. 이 문서에서는 BindingSource 구성 요소의 사용 여부에 관계없이 사용자의 응용 프로그램에서 활용할 수 있는 BindingControlBindingsCollection 개체의 이러한 변경 사항에 대해 설명합니다.

이 문서에서는 먼저 서식 지정 및 고급 바인딩 대화 상자에 대해 설명합니다. 그런 다음 코드를 사용하여 이 대화 상자에서 수행하는 것과 동일한 작업을 수행하는 방법에 대해 설명합니다. 처음 세 가지 예에서는 DataTable이 사용되고 네 번째 예에서는 BindingSource 개체가 사용됩니다. 모든 예에서 응용 프로그램의 데이터 원본으로 BindingSource를 사용할지 여부를 선택할 수 있지만 특히 동일한 데이터 원본에 대해 여러 컨트롤을 바인딩하는 경우에는 BindingSource를 사용하는 것이 좋습니다.

화면 맨 위로

서식 지정 및 고급 바인딩 대화 상자

Visual Studio에서 서식 지정 및 고급 바인딩 대화 상자를 사용하면 컨트롤 속성을 데이터 원본에 바인딩하고 결과의 서식을 지정할 수 있습니다. 이 대화 상자는 Binding 개체의 변경된 기능을 활용하도록 설계되었습니다. Visual Studio에서 서식 지정 및 고급 바인딩 대화 상자에 액세스하려면 데이터에 바인딩하려는 컨트롤을 마우스 오른쪽 단추로 클릭하고 속성을 선택합니다. 그림 1에 표시된 속성 창에서 (DataBindings) 항목을 확장하고 (고급) 항목을 선택합니다.

wfbindp101.gif

그림 1. 속성 창의 DataBindings 설정

(고급) 옆에 있는 줄임표 단추를 클릭하면 그림 2와 같은 서식 지정 및 고급 바인딩 대화 상자가 열립니다.

wfbindp102.gif

그림 2. 서식 지정 및 고급 바인딩 대화 상자

서식 지정 및 고급 바인딩 대화 상자에서는 응용 프로그램의 데이터 원본을 선택합니다. 데이터 원본은 BindingSource 개체, 데이터베이스 테이블, 웹 서비스 유형 또는 개체일 수 있습니다. BindingSource가 아닌 데이터 원본을 선택한 경우에는 해당 데이터 원본에 대한 BindingSource가 자동으로 생성됩니다. 그런 다음에는 컨트롤 값이 변경될 경우 데이터 원본을 업데이트하는 방법과 바인딩에 대한 서식 유형 및 Null 값을 설정할 수 있습니다.

참고 이 문서에서 설명하는 대부분의 작업은 서식 지정 및 고급 바인딩 대화 상자에서 수행할 수 있습니다.

화면 맨 위로

코드를 사용한 자동 서식 지정

이전 버전의 .NET Framework에서는 Binding 개체의 FormatParse 이벤트를 사용하여 형식 변환 및 서식 지정을 수동으로 수행해야 했습니다. 이제는 FormattingEnabled 속성을 직접 설정하거나 ControlBindingsCollectionAdd 메서드에 true를 전달하여 Binding 개체에 대한 서식 지정을 설정하면 됩니다. 또한 Add 메서드를 사용하거나 Binding 개체의 FormatString 속성을 설정하여 형식 문자열을 지정해야 합니다.

다음 코드에는 두 개의 서식 지정 예가 포함되어 있습니다. 첫 번째 서식 지정 예에서는 데이터 원본의 DateTime 값과 TextBox 컨트롤의 Text 값 간에 바인딩을 만드는 방법을 보여 줍니다. Binding 개체에서 FormattingEnabled 속성을 설정하고 FormatString 값은 DateTime에 자세한 날짜/시간 문자열 서식을 사용하도록 지정한 다음 컬렉션에 이 바인딩을 추가합니다.

두 번째 서식 지정 예에서는 DataTable의 데이터를 TextBox 컨트롤의 Text 속성에 바인딩하는 Binding 개체를 만들어 데이터의 서식을 지정하는 방법을 보여 줍니다. 고용 날짜 서식은 자세한 날짜로 지정하고 시작 급여 서식은 통화 단위로 지정합니다. 이 예에서는 ControlBindingsCollectionAdd 메서드를 사용하여 Binding 개체를 만듭니다. 또한 바인딩의 FormatString을 직접 설정합니다. 이 문서의 뒷부분에서는 ControlBindingsCollectionAdd 메서드를 사용하여 FormatString을 설정하는 방법을 설명합니다.

Dim table1 As New DataTable()
table1.Columns.Add("Name", GetType(String))
table1.Columns.Add("Hire Date", GetType(DateTime))
table1.Columns.Add("Starting salary", GetType(Integer))
table1.Rows.Add(New Object() {"Claus Hansen", New DateTime(2003, 5, 5), _   63000})
Dim dateBinding As New Binding("Text", table1, "Hire Date")
' Binding 개체에서 FormattingEnabled 및 FormatString을 직접 설정합니다.
' "D" 서식 지정자는 자세한 날짜 패턴을 나타냅니다.
dateBinding.FormattingEnabled = True
dateBinding.FormatString = "D"
' dateTextBox 바인딩 컬렉션에 바인딩을 추가합니다.
dateTextBox.DataBindings.Add(dateBinding)
' ControlBindingsCollection.Add 메서드의 
' 일부로 FormattingEnabled를 설정합니다.
Dim salaryBinding As Binding = salaryTextBox.DataBindings.Add("text", _   table1, "Starting Salary", True)
' "c" 서식 지정자는 통화 서식을 나타냅니다.
salaryBinding.FormatString = "c"
Dim nameBinding As Binding = nameTextBox.DataBindings.Add("Text", table1, _   "Name", True)

결과는 그림 3에 나와 있습니다.

wfbindp103.gif

그림 3. 서식 지정된 통화 및 날짜

화면 맨 위로

DataSourceUpdateMode 사용

DataSourceUpdateMode 열거형을 사용하면 컨트롤 속성의 변경 사항을 데이터 원본에 전파하는 시점을 지정할 수 있습니다. 기본값 DataSourceUpdateMode.OnValidation을 사용하면 사용자가 데이터가 바인딩된 값을 변경하고 Tab 키를 눌러 컨트롤에서 나올 때 데이터 원본이 업데이트됩니다. DataSourceUpdateNever를 사용하면 바인딩된 컨트롤의 변경된 값이 데이터 원본에 전파되지 않으며 OnPropertyChange를 사용하면 컨트롤 값이 변경될 때마다 변경된 값이 전파됩니다. OnPropertyChange 또는 OnValidation을 사용하는 경우 컨트롤 값이 유효성 검사를 통과하지 못하면 올바른 값을 입력하기 전에는 사용자가 Tab 키를 눌러 다른 컨트롤로 이동하거나 폼을 닫을 수 없습니다.

다음 예에서는 ControlBindingsCollectionAdd 메서드를 사용하여 DataSourceUpdateMode를 설정하는 방법을 보여 줍니다. DataTable의 고용 날짜는 고용 날짜 텍스트 상자의 내용이 변경될 때마다 업데이트됩니다. DataTable의 시작 급여는 시작 급여 텍스트 상자가 포커스를 잃고 값이 유효성 검사를 통과하면 업데이트됩니다. 시작 급여 텍스트 상자에 숫자가 아닌 잘못된 값(예: 하나 이상의 문자)을 입력하면 사용자는 Tab 키를 눌러도 텍스트 상자를 벗어날 수 없습니다.

Dim table1 As New DataTable()
table1.Columns.Add("Name", GetType(String))
table1.Columns.Add("Hire Date", GetType(DateTime))
table1.Columns.Add("Starting salary", GetType(Integer))
table1.Rows.Add(New Object() {"Claus Hansen", New DateTime(2003, 5, 5), _   63000})
' table1의 고용 날짜는 고용 날짜 텍스트 상자의 내용이 변경될 때
' 업데이트됩니다.
Dim dateBinding2 As Binding = dateTextBox.DataBindings.Add("Text", table1, _   "Hire Date", True, DataSourceUpdateMode.OnPropertyChanged)
dateBinding2.FormatString = "D"
' table1의 시작 급여는 시작 급여 텍스트 상자가
' 포커스를 잃고 값이 유효성 검사를 통과하면 업데이트됩니다.
Dim salaryBinding As Binding = salaryTextBox.DataBindings.Add("text", _   table1, "Starting salary", True, DataSourceUpdateMode.OnValidation)
salaryBinding.FormatString = "c"
Dim nameBinding As Binding = nameTextBox.DataBindings.Add("Text", table1, _   "Name", True)

화면 맨 위로

Null 및 DBNull 값 자동 처리

이전 버전의 Windows Forms 데이터 바인딩에서는 FormatParse 이벤트를 처리할 때 데이터 원본의 DBNull 또는 null 값을 컨트롤에서 어떻게 표시할지를 고려해야 했습니다. 이를 위해서는 데이터 원본의 특정 컨트롤 값을 구문 분석하여 null 또는 DBNull인지 확인할 수 있어야 했습니다. 이제는 Binding 개체의 NullValue 속성을 사용하여 이러한 작업을 훨씬 쉽게 수행할 수 있습니다.

다음 예에서는 데이터 원본의 DBNull 값을 NullValue 속성을 사용하여 바인딩된 컨트롤에 표시하는 방법을 보여 줍니다. 고용 날짜는 DataTable에서 DBNull 값이지만 텍스트 상자에는 "New Hire"로 표시됩니다.

Dim table1 As New DataTable()
table1.Columns.Add("Name", GetType(String))
table1.Columns.Add("Hire Date", GetType(DateTime))
table1.Columns.Add("Starting salary", GetType(Integer))
' 고용 날짜를 DBNull로 초기화합니다.
table1.Rows.Add(New Object() {"Claus Hansen", DBNull.Value, 63000})
' 고용 날짜가 Null 또는 DBNull인 경우 "New Hire"로 변경합니다.
Dim dateBinding2 As Binding = dateTextBox.DataBindings.Add("Text", table1, _   "Hire Date", True, DataSourceUpdateMode.OnValidation, "New Hire", "D")
Dim salaryBinding As Binding = salaryTextBox.DataBindings.Add("text", _   table1, "Starting salary", True)
salaryBinding.FormatString = "c"
Dim nameBinding As Binding = nameTextBox.DataBindings.Add("Text", table1, _   "Name", True)

결과는 그림 4에 나와 있습니다.

wfbindp104.gif

그림 4. "New Hire" 문자열로 서식이 지정된 DBNull

화면 맨 위로

향상된 오류 및 예외 처리

이제는 데이터 바인딩에 대한 서식 지정을 설정할 때마다 Binding 개체의 BindingComplete 이벤트를 사용하여 오류 및 예외 처리를 보다 효율적으로 제어할 수 있습니다.

다음 예에서는 바인딩된 컨트롤 값이 잘못된 값으로 변경된 경우 사용자에게 피드백을 제공하도록 BindingComplete 이벤트를 처리하는 방법을 보여 줍니다. 이 예에 사용된 Employee라는 비즈니스 개체는 Salary 속성에 20000 미만의 값을 사용하지 못하도록 합니다. 바인딩을 위해 Employee 개체는 BindingSource에 저장합니다.

여기에서는 먼저 BindingSource를 만들고 두 개의 Employee 개체를 추가한 다음 바인딩을 설정했습니다. 또한 BindingComplete 이벤트를 해당 이벤트 처리 메서드에 연결했습니다.

' Binding 개체를 선언합니다.
Dim WithEvents salaryBinding As Binding
Dim employeeBindingSource As New BindingSource()
employeeBindingSource.Add(New Employee("Hansen", "Claus", 63000, _   New DateTime(2002, 5, 5)))
employeeBindingSource.Add(New Employee("Han", "Mu", 54000, _   New DateTime(2001, 3, 4)))
nameTextBox.DataBindings.Add("Text", employeeBindingSource, _   "LastName", True)
dateTextBox.DataBindings.Add("Text", employeeBindingSource, _   "StartDate", True, DataSourceUpdateMode.OnValidation)
salaryBinding = salaryTextBox.DataBindings.Add("text", _   employeeBindingSource, "Salary", True, DataSourceUpdateMode.OnValidation, _   "Unknown", "c")

그런 다음 BindingComplete 이벤트를 처리합니다. 이 이벤트 처리기는 오류 메시지를 ErrorProvider 컨트롤에 전달합니다.

Sub salaryBinding_BindingComplete(ByVal sender As Object, _   ByVal e As BindingCompleteEventArgs) Handles salaryBinding.BindingComplete     ' BindingComplete 상태가 성공 이외인 경우     ' 오류 메시지에 대해 ErrorProvider를 설정합니다.     If e.BindingCompleteState <> BindingCompleteState.Success Then         errorProvider1.SetError( _           CType(e.Binding.BindableComponent, Control), e.ErrorText)     Else         errorProvider1.SetError( _           CType(e.Binding.BindableComponent, Control), "")     End If
End Sub

다음 코드는 Employee 비즈니스 개체를 보여 줍니다.

Public Class Employee     Private lastNameValue As String     Private firstNameValue As String     Private salaryValue As Integer     Private startDateValue As DateTime     Public Sub New(ByVal lastName As String, ByVal firstName As String, _       ByVal salary As Integer, ByVal startDate As DateTime)         lastNameValue = lastName         firstNameValue = firstName         salaryValue = salary         startDateValue = startDate     End Sub     Public Property LastName() As String         Get             Return lastNameValue         End Get         Set(ByVal value As String)             lastNameValue = value         End Set     End Property     Public Property FirstName() As String         Get             Return firstNameValue         End Get         Set             firstNameValue = value         End Set     End Property     Public Property Salary() As Integer         Get             Return salaryValue         End Get         Set             If value < 20000 Then                 Throw New Exception("Salary cannot be less than $20,000")             Else                 salaryValue = value             End If         End Set     End Property     Public Property StartDate() As DateTime         Get             Return startDateValue         End Get         Set             startDateValue = value         End Set     End Property     Public Overrides Function ToString() As String         Return LastName & ", " & FirstName & vbLf & "Salary:" _           & salaryValue & vbLf & "Start date:" & startDateValue     End Function
End Class

오류 처리가 이루어지는지 확인하려면 예제 코드를 실행하고 급여 값을 $20,000 미만의 값으로 변경해 보십시오. 그림 5는 BindingComplete 이벤트가 처리되어 사용자에게 피드백이 제공된 경우를 보여 줍니다.

wfbindp105.gif

그림 5. BindingComplete 이벤트를 사용하여 바인딩된 컨트롤의 데이터를 확인하는 예

뒷 이야기: .NET Framework 2.0에서 향상된 Windows Forms 데이터 바인딩 기술, 2부

Cheryl Simmons

Microsoft Corporation

2006년 8월

적용 대상:

Microsoft Visual Studio 2005

Microsoft .NET Framework 2.0

Microsoft Windows Forms

요약 : 제네릭 BindingList와 이 제네릭 컬렉션 형식을 확장하여 정렬 및 검색 기능을 추가하는 방법에 대해 자세히 알아봅니다.

Microsoft 다운로드 센터 (영문)에서 C# 및 Visual Basic으로 작성된 코드 샘플을 다운로드하십시오(29KB).

이 기사의 1부도 읽어 보십시오.

목차

소개 소개
제네릭 BindingList 정보 제네릭 BindingList 정보
코드 샘플 개요 코드 샘플 개요
제네릭 BindingList 검색 제네릭 BindingList 검색
제네릭 BindingList 정렬 제네릭 BindingList 정렬
다음 단계 다음 단계
소개 소개

소개

Windows Forms 데이터 바인딩의 새로운 기능 중 BindingSource 구성 요소는 가장 많은 주목을 받았습니다. 이 구성 요소는 통화 관리, 변경 알림 및 바인딩된 목록에 있는 멤버에 대한 간편한 액세스 기능을 제공하여 개발자의 작업을 간소화해 줍니다. 데이터 원본에 IBindingList 인터페이스와 검색 및 정렬 기능이 구현되어 있으면 BindingSource를 사용하여 데이터 원본을 검색하고 정렬할 수 있습니다. DataView 클래스는 IBindingList를 구현하는 형식 중 하나입니다. 그러나 IBindingList를 구현하는 클래스를 사용하지 않고 직접 IBindingList 인터페이스의 구현을 제공하여 사용자 지정 비즈니스 개체 목록을 만들고 바인딩해야 하는 경우도 있습니다. 이러한 경우를 위해 Microsoft .NET Framework에서는 TBindingList라고 하는 제네릭 BindingList 형식을 도입했습니다. 이 기사에서는 데이터 바인딩 응용 프로그램에서 제네릭 BindingList를 사용하여 검색 및 정렬이 가능한 사용자 지정 비즈니스 개체 목록을 제공하는 방법에 대해 설명합니다.

페이지 맨 위로

제네릭 BindingList 정보

제네릭 BindingListIBindingList 인터페이스의 제네릭 구현입니다. IBindingList 인터페이스는 Windows Forms 데이터 바인딩의 핵심 인터페이스인 IList 인터페이스의 확장입니다. IList 인터페이스에서 상속되는 기능 외에도 IBindingList를 구현하는 클래스는 정렬, 검색 및 변경 알림 기능을 선택적으로 지원할 수 있습니다. IBindingList 인터페이스는 상당히 복잡한 인터페이스이며 제네릭 BindingList 형식으로 제네릭 구현을 제공함으로써 Windows Forms 데이터 바인딩 엔진에 사용할 바인딩 가능한 사용자 지정 비즈니스 개체 목록을 훨씬 쉽게 작성할 수 있도록 해줍니다. 데이터 바인딩 시나리오에서 제네릭 BindingList를 사용하여 비즈니스 개체를 저장하는 경우 이 목록을 사용하면 항목을 쉽게 삽입 및 제거할 수 있으며 목록이 변경될 경우 변경 알림이 자동으로 제공됩니다. 또한 몇 가지 속성 및 메서드를 재정의하여 제네릭 BindingList에서 검색 및 정렬을 구현할 수 있습니다.

페이지 맨 위로

코드 샘플 개요

여기에서는 간단한 코드 샘플을 사용하여 데이터 원본에 검색 및 정렬 기능을 추가하는 방법을 설명하겠습니다. 몇 개의 Employee 개체가 포함된 목록이 있다고 가정해 보겠습니다. 목록에서 이름, 급여 및 입사 날짜별로 특정 직원을 검색하는 기능을 사용자의 응용 프로그램에 추가하려고 합니다. 응용 프로그램 사용자는 목록 검색 이외에도 이름, 급여, 고용 날짜별로 직원 목록을 정렬하는 기능을 원합니다. 이 예에서 볼 수 있듯이 DataGridView 컨트롤에 바인딩된 제네릭 BindingList에서 정렬 기능을 구현하면 추가 코드 없이도 DataGridView에서 정렬 기능을 제공할 수 있습니다.

Employee 비즈니스 개체 소개

이 코드 샘플에서는 간단한 Employee 비즈니스 개체가 사용됩니다. 다음은 Employee 형식의 구현입니다.

public class Employee
{     private string lastNameValue;     private string firstNameValue;     private int salaryValue;     private DateTime startDateValue;     public Employee()     {         lastNameValue = "Last Name";         firstNameValue = "First Name";         salaryValue = 0;         startDateValue = DateTime.Today;     }     public Employee(string lastName, string firstName,         int salary, DateTime startDate)     {         LastName = lastName;         FirstName = firstName;         Salary = salary;         StartDate = startDate;     }     public string LastName     {         get { return lastNameValue; }         set { lastNameValue = value; }     }     public string FirstName     {         get { return firstNameValue; }         set { firstNameValue = value; }     }     public int Salary     {         get { return salaryValue; }         set { salaryValue = value; }     }     public DateTime StartDate     {         get { return startDateValue; }         set { startDateValue = value; }     }     public override string ToString()     {         return LastName + ", " + FirstName + "\n" +         "Salary:" + salaryValue + "\n" +         "Start date:" + startDateValue;     }
}
제네릭 BindingList 확장

코드 샘플에서는 Employee 개체 목록에 대한 검색 및 정렬 기능을 구현하기 위해 BindingList를 기반으로 SortableSearchableList라는 이름의 형식을 만들었습니다. 검색 및 정렬을 구현하기 위해 재정의해야 하는 메서드 및 속성은 protected이기 때문에 제네릭 BindingList를 확장해야 합니다. 또한 클래스 선언에서 <T> 구문을 사용할 수 있도록 SortableSearchableList 형식을 제네릭 목록으로 만들도록 선택했습니다. 확장된 목록을 제네릭으로 만들면 여러 상황에서 이 목록을 다시 사용할 수 있습니다.

public class SortableSearchableList<T> : BindingList<T>

페이지 맨 위로

제네릭 BindingList 검색

제네릭 BindingList에서 검색을 구현하려면 여러 단계가 필요합니다. 우선 SupportsSearchingCore 속성을 재정의하여 검색이 지원됨을 나타내야 합니다. 그런 다음 검색을 수행하는 FindCore 메서드를 재정의해야 하며 마지막으로 검색 기능을 노출해야 합니다.

검색 지원 표시

SupportsSearchingCore 속성은 재정의해야 하는 읽기 전용 protected 속성입니다. SupportsSearchingCore 속성은 목록에서 검색이 지원되는지 여부를 나타냅니다. 기본적으로 이 속성은 false로 설정되어 있습니다. 목록에 검색이 구현되었음을 나타내려면 이 속성을 true로 설정합니다.

protected override bool SupportsSearchingCore{    get    {        return true;    }}
검색 기능 구현

그런 다음 FindCore 메서드를 재정의하고 목록을 검색하는 구현을 제공해야 합니다. FindCore 메서드는 목록을 검색하고 검색된 항목의 인덱스를 반환합니다. FindCore 메서드는 검색할 데이터 원본 형식의 속성 또는 열을 나타내는 PropertyDescriptor 개체와 검색할 값을 나타내는 키 개체를 받습니다. 다음 코드는 대/소문자를 구분하는 간단한 검색 기능을 구현한 것입니다.

protected override int FindCore(PropertyDescriptor prop, object key){    // 지정된 속성에 대한 속성 정보를 가져옵니다.     PropertyInfo propInfo = typeof(T).GetProperty(prop.Name);    T item;    if (key != null)    {        // 키 값이 속성 값과 일치하는지 확인하기 위해        // 루프로 항목을 조회합니다.         for (int i = 0; i < Count; ++i)        {            item = (T)Items[i];            if (propInfo.GetValue(item, null).Equals(key))                return i;        }    }    return -1;}
검색 기능 노출

마지막으로 공용 메서드를 사용하여 검색 기능을 노출하거나 기본 IBindingListFind 메서드를 사용하여 검색 기능을 호출할 수 있습니다. 검색 기능 호출을 단순화하기 위해 다음 코드에 나와 있는 공개적으로 노출된 Find 메서드는 검색할 문자열과 키를 받습니다. 그런 다음 코드는 속성 문자열을 PropertyDescriptor 개체로 변환하려고 시도합니다. 변환이 성공적이면 PropertyDescriptor와 키가 FindCore 메서드에 전달됩니다.

public int Find(string property, object key){    // 속성에서 지정한 이름의 속성을 확인합니다.     PropertyDescriptorCollection properties =         TypeDescriptor.GetProperties(typeof(T));    PropertyDescriptor prop = properties.Find(property, true);    // 일치하는 항목이 없으면 -1을 반환하고 그렇지 않으면    // FindCore 메서드로 검색을 전달합니다.     if (prop == null)        return -1;    else       return FindCore(prop, key);}

그림 1은 실행 중인 검색 기능을 보여 줍니다. 성을 입력하고 Search 단추를 클릭하면 검색된 항목이 있는 경우 표에서 해당 성이 나와 있는 첫 번째 행이 식별됩니다.

wfbindp201.gif

그림 1. 제네릭 BindingList 사용하여 구현된 검색 기능

페이지 맨 위로

제네릭 BindingList 정렬

응용 프로그램 사용자는 목록 검색 이외에도 이름, 급여, 고용 날짜별로 Employees 목록을 정렬하는 기능을 원합니다. SupportsSortingCore 속성과 ApplySortCoreRemoveSortCore 메서드를 재정의하여 제네릭 BindingList에서 정렬을 구현할 수 있습니다. 또한 선택적으로 SortDirectionCoreSortPropertyCore 속성을 재정의하여 정렬 방향 및 속성을 각각 반환할 수 있습니다. 마지막으로 목록에 추가된 새 항목이 정렬되도록 하려면 EndNew 메서드를 재정의해야 합니다.

정렬 지원 표시

SupportsSortingCore 속성은 재정의해야 하는 읽기 전용 protected 속성입니다. SupportsSortingCore 속성은 목록에서 정렬이 지원되는지 여부를 나타냅니다. 목록에 정렬이 구현되었음을 나타내려면 이 속성을 true로 설정합니다.

protected override bool SupportsSortingCore{    get { return true; }}
정렬 적용

그런 다음 ApplySortCore 메서드를 재정의하고 실제 정렬 세부 사항을 제공해야 합니다. ApplySortCore 메서드는 정렬할 목록 항목 속성을 식별하는 PropertyDescriptor 개체와 목록을 오름차순 또는 내림차순으로 정렬할지를 나타내는 ListSortDescription 열거를 사용합니다.

다음 코드에서는 선택한 속성에서 CompareTo 메서드를 제공하는 IComparable 인터페이스를 구현하는 경우에만 목록이 정렬됩니다. 문자열, 정수 및 DateTime 형식과 같은 대부분의 간단한 형식에서는 이 인터페이스가 구현됩니다. Employee 개체의 모든 속성이 간단한 형식이므로 이것은 합리적인 결정입니다. 또한 구현에서는 사용자가 간단한 형식이 아닌 속성으로 데이터 원본을 정렬하려고 시도하는 경우를 위해 간단한 오류 처리 기능을 제공합니다.

다음 코드에서는 RemoveSortCore 메서드에서 사용하기 위해 정렬되지 않은 목록의 복사본을 저장합니다. 그런 다음 지정된 속성의 값을 ArrayList에 복사합니다. ArrayList Sort 메서드에 대한 호출이 수행되며 이 메서드는 다시 해당 배열 멤버의 CompareTo 메서드를 호출합니다. 마지막으로 정렬된 배열의 값을 사용하여 정렬이 내림차순인지 또는 오름차순인지에 따라 제네릭 BindingList 값을 재구성합니다. 그런 다음 목록이 초기화되었음을 나타내는 ListChanged 이벤트가 발생하고 바인딩된 컨트롤의 값이 새로 고쳐집니다. 다음 코드는 정렬 기능을 구현한 것입니다.

ListSortDirection sortDirectionValue;
PropertyDescriptor sortPropertyValue;
protected override void ApplySortCore(PropertyDescriptor prop,     ListSortDirection direction)
{     sortedList = new ArrayList();     // 정렬 중인 속성 형식에서 IComparable 인터페이스가 구현되는지     // 확인합니다.     Type interfaceType = prop.PropertyType.GetInterface("IComparable");     if (interfaceType != null)     {         // 구현되는 경우에는 SortPropertyValue 및 SortDirectionValue를 설정합니다.         sortPropertyValue = prop;         sortDirectionValue = direction;         unsortedItems = new ArrayList(this.Count);         // 루프로 각 항목에 sortedItems ArrayList에 추가합니다.         foreach (Object item in this.Items) {             sortedList.Add(prop.GetValue(item));             unsortedItems.Add(item);         }         // ArrayList에서 Sort를 호출합니다.         sortedList.Sort();         T temp;         // 정렬 방향을 확인하고 정렬된 항목을 다시 목록에         // 복사합니다.         if (direction == ListSortDirection.Descending)             sortedList.Reverse();         for (int i = 0; i < this.Count; i++)         {             int position = Find(prop.Name, sortedList[i]);             if (position != i) {                 temp = this[i];                 this[i] = this[position];                 this[position] = temp;             }         }         // 바인딩된 컨트롤이 해당 값을 새로 고치도록 ListChanged 이벤트를         // 발생시킵니다.         OnListChanged(new ListChangedEventArgs(ListChangedType.Reset, -1));     }     else         // 속성 형식에서 IComparable이 구현되지 않으면 이를 사용자에게         // 알려 줍니다.         throw new NotSupportedException("Cannot sort by " + prop.Name +             ". This" + prop.PropertyType.ToString() +             " does not implement IComparable");
}
정렬 제거

다음 단계는 RemoveSortCore 메서드를 재정의하는 것입니다. RemoveSortCore 메서드는 목록에 적용된 마지막 정렬을 제거하고 목록이 초기화되었음을 나타내는 ListChanged 이벤트를 발생시킵니다. RemoveSort 메서드는 정렬 제거 기능을 노출합니다.

protected override void RemoveSortCore()
{     int position;     object temp;     // 목록이 정렬되었는지 확인합니다.     if (unsortedItems != null)     {         // 정렬되지 않은 항목에 대해 루프를 사용하고         // 정렬되지 않은 목록마다 목록을 다시 정렬합니다.         for (int i = 0; i < unsortedItems.Count; )         {             position = this.Find("LastName",                 unsortedItems[i].GetType().                 GetProperty("LastName").GetValue(unsortedItems[i], null));             if (position > 0 && position != i)             {                 temp = this[i];                 this[i] = this[position];                 this[position] = (T)temp;                 i++;             }             else if (position == i)                 i++;             else                 // 정렬되지 않은 목록에 항목이 더 이상 없으면                 // 이를 삭제합니다.                 unsortedItems.RemoveAt(i);         }         OnListChanged(new ListChangedEventArgs(ListChangedType.Reset, -1));     }
}
public void RemoveSort()
{     RemoveSortCore();
}
정렬 방향 및 속성 노출

마지막으로 prorected SortDirectionCoreSortPropertyCore 속성을 재정의합니다. SortDirectionCore 속성은 정렬 방향이 오름차순인지 또는 내림차순인지를 나타냅니다. SortPropertyCore 속성은 목록 정렬에 사용되는 속성 설명자를 나타냅니다.

ListSortDirection sortDirectionValue;PropertyDescriptor sortPropertyValue;protected override PropertyDescriptor SortPropertyCore{    get { return sortPropertyValue; }}protected override ListSortDirection SortDirectionCore{    get { return sortDirectionValue; }}
정렬 기능 노출

샘플에서는 Employees(SortableSearchableList)의 사용자 지정 제네릭 BindingListDataGridView 컨트롤에 바인딩하였습니다. DataGridView 컨트롤은 정렬이 구현되었는지 여부를 검색하는데 여기에서는 정렬이 구현되어 있기 때문에 DataGridView의 열을 클릭하면 DataGridView의 내용이 열 내용에 따라 오름차순으로 정렬됩니다. 열을 다시 클릭하면 항목이 내림차순으로 정렬됩니다. DataGridViewIBindingList 인터페이스를 통해 목록을 호출합니다.

검색 코드와 비슷하게 선택적으로 ApplySortCore를 호출하는 공용 메서드를 노출하거나 DataGridView 컨트롤과 비슷하게 기본 IBindingList를 호출할 수 있습니다.

((IBindingList)employees).ApplySort(somePropertyDescriptor,    ListSortDirection.Ascending);
목록에 추가된 항목의 정렬 보장

Employee 개체는 기본 생성자를 노출하므로 BindingListAllowNew 속성은 true를 반환하여 사용자가 목록에 직원을 추가할 수 있도록 합니다. 사용자가 새 항목을 추가할 수 있으므로 정렬이 적용된 경우에는 목록에 추가된 항목이 정렬된 순서에 맞게 추가되도록 해야 합니다. 이를 위해 EndNew 메서드를 재정의했습니다. 이 메서드 재정의에서는 SortPropertyValuenull이 아님을 확인하여 목록이 정렬되었음을 확인하고, 새 항목이 목록 끝에 추가되는지 확인(이 경우 목록을 다시 정렬해야 함)한 다음, 목록의 ApplySortCore를 호출합니다.

public override void EndNew(int itemIndex)
{     // 항목이 목록 끝에 추가되었는지 확인하고     // 목록 끝에 추가되었으면 목록을 다시 정렬합니다.     if (sortPropertyValue != null && itemIndex == this.Count - 1)         ApplySortCore(this.sortPropertyValue, this.sortDirectionValue);     base.EndNew(itemIndex);
}

그림 2는 실행 중인 정렬 기능을 보여 줍니다. DataGridView의 열을 클릭하면 내용이 정렬됩니다. Remove sort 단추를 클릭하면 마지막으로 적용된 정렬이 제거됩니다.

wfbindp202.gif

그림 2. 제네릭 BindingList 사용하여 구현된 정렬 기능

다음 단계

앞에서 설명한 대로 이 예는 제네릭 BindingList에서 검색 및 정렬 기능을 구현하는 방법을 보여 주는 간단한 샘플입니다. 이 샘플에서 사용하는 정렬 알고리즘은 IComparable 인터페이스를 구현하는 형식을 사용합니다. IComparable 인터페이스를 구현하지 않는 속성으로 정렬을 시도하면 예외가 발생합니다. 이 논리는 속성이 주로 간단한 형식인 사용자 지정 비즈니스 개체를 정렬하는 경우에 사용할 수 있습니다. 이 샘플의 기능을 향상시키려면 사용자 지정 비즈니스 개체에서 복잡한 형식을 갖는 모든 속성에 대해 IComparable 인터페이스를 구현하는 방법을 고려할 수 있습니다. 예를 들어 Employee 개체에 복잡한 Address 형식의 Address 속성이 포함된 경우 Address 형식을 정렬하기 위한 비즈니스 규칙을 결정하고(거리 이름, 번지 등) 적절하게 Address 형식에서 IComparable 인터페이스 및 CompareTo 메서드를 구현해야 합니다.

또한 목록 끝에 새 항목이 추가될 경우에는 목록이 자동으로 다시 정렬됩니다. 응용 프로그램의 비즈니스 논리에 따라 항목이 추가되는 위치에 관계없이 목록을 다시 정렬하도록 할 수도 있습니다.

마지막으로 데이터 원본에 필터링 및 다중 열 정렬 기능을 추가할 수도 있습니다. 이 경우에는 데이터 원본에 대한 IBindingListView 인터페이스를 구현할 것을 고려해 봅니다.

결론

BindingSource 구성 요소는 Windows Forms 데이터 바인딩에 추가된 매우 유용한 기능입니다. 그러나 사용자 지정 비즈니스 개체의 목록을 사용할 때는 검색 및 정렬 기능을 추가해야 할 경우가 있습니다. 새로운 제네릭 BindingList는 검색 및 정렬이 지원되는 사용자 지정 비즈니스 개체의 바인딩 목록을 보다 간단하게 만들 수 있게 해 줍니다. 이러한 기능 및 Windows Forms의 다른 기능에 대한 자세한 내용은 다음을 참조하십시오.

+ Recent posts