원하는대로 마음껏 요리하는 Windows Forms 데이터 바인딩

최근 진행 중인 씨티은행의 외환업무시스템 프레임워크 업그레이드 작업은 .NET Framework를 RAD (Rapid Application Development) 도구로 사용하는 것의 난해함에 대해 많은 것을 생각하게 하고 있습니다. .NET Framework 기반의 소프트웨어는 분명히 Unmanaged Code를 기초로 하는 다른 RAD 도구와는 다른 이점이 많이 있으며, 앞으로도 Windows 7과 더불어서 Desktop Scene의 현대화를 이끌어내는 주요한 키 포인트가 될 것으로 보이지만 풀어야 할 숙제 또한 많이 있을 것입니다.


 


다들 인지하고 계시는 부분일 수도 있지만, 또한 도움이 될 수 있을것이라 생각하여 오랫만에 간단한 샘플 하나를 올려봅니다. 이 방법을 통하여, Windows Forms가 가지고 있는 특유의 장점을 살리면서도, 코드를 복잡하게 만들지 않으면서, 손쉽게 다양한 유형의 데이터 바인딩을 다룰 수 있을 것이라 생각합니다.


 


이번 글에서 소개하는 데이터 바인딩 소스는 단순한 난수 생성에 한정되지만, XML 문서 구조를 유추하여 데이터 셋을 완성해야 할 필요가 있다거나 다양한 경우에 대응되는 데이터 변형 시나리오를 구축할 수 있을 것입니다.


 


System.ComponentModel.Component를 기본 클래스로 하는 컴포넌트를 만들고, System.ComponentModel.IListSource 인터페이스를 구현하는 컴포넌트를 만듭니다. Component 클래스를 기본 클래스로 한다는 것은 Visual Studio가 제공하는 Design Time 상호 운용성을 위한 기본 틀을 마련하는 것입니다.


 


IListSource 인터페이스는 ContainsListCollection 프로퍼티와 GetList 메서드로 구현이 됩니다. GetList 메서드를 통하여, 우리가 흔히 필요로 하는 데이터 소스를 직접 반환할 수 있으며, ContainsListCollection 프로퍼티는 좀 더 복잡한 유형의 데이터 소스를 관리할 수 있는 기준을 제공합니다. 보통의 경우, ContainsListCollection 프로퍼티의 값은 항상 false를 반환하도록 하고, GetList 메서드에서는 BindingList<T> 객체나 System.Data.DataView 객체를 반환합니다.


 


다음은 IListSource 인터페이스를 구현해 놓은 예시입니다.


 


        [Browsable(false)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public bool ContainsListCollection
        {
            get { return false; }
        }


 


        public IList GetList()
        {
            DataTable table = new DataTable();


            if (this.DesignMode && !this.liveBinding)
                return table.DefaultView;


            for (int i = 0; i < this.columnCount; i++)
            {
                DataColumn eachColumn = table.Columns.Add();


                eachColumn.ColumnName = String.Format(
                    CultureInfo.CurrentCulture,
                    “Column{0}”, (i + 1));
                eachColumn.Caption = String.Format(
                    CultureInfo.CurrentCulture,
                    “Value #{0}”, (i + 1));
                eachColumn.AllowDBNull = false;
                eachColumn.DataType = typeof(int);
                eachColumn.DefaultValue = 0;
            }


            for (int i = 0; i < this.rowCount; i++)
            {
                object[] dataArray = new object[this.columnCount];


                for (int j = 0; j < this.columnCount; j++)
                {
                    dataArray[j] = this.randomizer.Next(
                        this.minimumValue,
                        this.maximumValue);
                }


                table.Rows.Add(dataArray);
            }


            return table.DefaultView;
        }


 


위에서 굵게 강조표시한 부분이 설명하고자 하는 부분들입니다. ContainsListCollection 속성을 false로 반환하여 일반적인 데이터 소스임을 알리는 부분이 있습니다. 그리고, 이제까지 커뮤니티에서 자주 회자되는 내용들 중 하나입니다만, 디자인 타임을 정확히 구분하는 방법이 여기에 있습니다. protected 접근자로 보호되고 있고, 상속하여 재정의할 수 없는 DesignMode라는 속성을 이용하여 디자인 모드에서 취할 동작을 설정할 수 있습니다.


 


DesignMode 속성을 이용하여 디자인 타임에서 동적으로 데이터를 가져오도록 만들어서 다음 동작을 취할 수 있도록 정하는 것이 가능하며, 동시에 GetList 메서드 자체는 연계하기에 따라서 디자인 타임에 노출시킬 Verb (동사)와 연동시킬 수 있으므로, 데이터 바인딩에 시간이 많이 걸리는 동작을 정의하기에 편리합니다.


 


그리고 마지막으로, IList 인터페이스와 호환되면서도 우리가 논리적으로 편리하게 생각할 수 있는 데이터 모델인 System.DataView를 반환하는 것으로 코드의 대략적인 구조는 마무리됩니다. 이와 같은 형태로 완성된 컴포넌트를 실제로 데이터 바인딩에 연결시켜보도록 하겠습니다.


 



 


위에서 보이는것처럼, 난수를 데이터 테이블의 형태로 생성하였습니다. 이와 같이, 데이터베이스 서버가 아닌 곳의 일정한 자료를 데이터 소스로 사용하는 일이 반드시 제한적이지만은 않다는 것을 알 수 있으며, 응용하기에 따라서는 BindingSource 컴포넌트를 직접 오버라이드하여 데이터베이스가 아닌 대상을 놓고 Create, Read, Update, Delete 연산을 구현하는 것도 생각해볼 수 있을 것입니다.


 


이 샘플에 사용한 소스 코드를 하나 올려봅니다. 새 프로젝트에 추가하여 테스트해볼 수 있을 것입니다.


 


cfile24.uf.1356380B4BDDAAC0A2E999.cs


 


 

댓글 남기기