C#에 있는 WinForm을 이용하여 ListBox 상에 데이터를 추가할 때 여러가지 방법이 있지만
나름대로 상당히 좋은 방법이 있어 여기에 기록합니다.
ListBox 상에 데이터를 넣는 방법은 보통 ListBox 객체 내에 있는 items를 이용하여
추가/삭제를 시도하게 됩니다. 하지만, 만일 ListBox 내에 표시되는 내용과 실제 사용하게
될 데이터가 틀린 경우에는 조금 다른 문제가 발생합니다.
여기서는 두가지 방법을 제시하려 합니다.
첫번째 방법은 가장 일반적인 방법으로 ListBox.items 라는 콜렉션 내에 추가하는 방법입니다.
가장 직관적이고 빠르게 처리할 수 있는 방법이죠.
두번째 방법은 DataSource 속성을 이용해 별도 데이터를 담은 콜렉션을 만들어 이 ListBox와 연결해주는 방법입니다.
1. ListBox.items 를 이용하는 방법
ListBox.items 콜렉션을 이용하는 방법은 보통 다양한 초보 서적에서 보기 쉬운 방법입니다. ListBox.items 콜렉션을 보시면 Object 객체의 콜렉션으로 나타내게 되는데, 여기에는 다양한 형이 들어갈 수 있습니다. 사실 Object를 상속 받은 모든 형태의 객체가 들어가므로 int 든 string 이든 거의 대부분의 값이 들어가게 됩니다.
그런데, 사실 찬찬히 이 items에 담긴 내용이 리스트 컨트롤에 출력될 때는 item[n].ToString() 을 해서 얻는 문자열로 나타낸다는 것입니다. 이를 이용하면 실제 출력될 문자열과 다양한 데이터를 담은 강력 리스트 박스를 얻을 수 있습니다.
다음 코드 내용이 바로 이 기능을 이용한 방법입니다.
using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Forms;
using System.Drawing;
자 이젠 DataSource를 이용한 방법입니다.
DataSource를 이용하려면 몇가지 준비사항이 있는데, 앞의 방법에서는 ListBox.items에
직접 데이터를 넣어 처리했다면, 이번에는 데이터를 별도로 저장할 자료구조 인스턴스
를 만드는 것입니다. 원래는 DB와 같은 데이터 원본을 갖추는 형태를 잘 활용할 수 있도록
제공된 기능이지만, 이런 부분에서 더 큰 장점이 될 수 있으리라 생각됩니다.
먼저 List<T> 라는 제네릭으로 객체를 만듦니다. 이 안에 리스트 박스에 넣을 데이터
원본을 기록하고, 이 원본을 리스트 박스와 바인드(연결) 하게 되는 것이죠.
그럼 그 방법을 소스로 표현하면 다음과 같습니다.
using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Forms;
using System.Drawing;_
class DataType1 : Object
{
private string _name;
private System.Drawing.Point _point;
public string Name
{
// 이전 소스의 ToString()의 내용이 여기를 통해 처리하게 됩니다.
get
{
StringBuilder sb = new StringBuilder();
sb.Append(_name);
sb.Append(" [ ");
sb.Append(_point.X.ToString());
sb.Append(",");
sb.Append(_point.Y.ToString());
sb.Append(" ]");
return sb.ToString();
}
set { _name = Name; }
}
public Point Pos
{
get { return _point; }
set { _point = Pos; }
}
public DataType1(string name, System.Drawing.Point pos)
{
_name = name;
_point = pos;
}
// 이제 이 부분은 더이상 쓰이지 않습니다.
//public override string ToString()
//{
// StringBuilder sb = new StringBuilder();
// sb.Append(_name);
// sb.Append(" [ ");
// sb.Append(_point.X.ToString());
// sb.Append(",");
// sb.Append(_point.Y.ToString());
// sb.Append(" ]");
// return sb.ToString();
//}
}
class MainForm : Form
{
ListBox listTest;
List<DataType1> listDataSource;
public MainForm()
{
listTest = new ListBox();
listDataSource = new List<DataType1>(); // 데이터 소스 개체 생성
this.listTest.FormattingEnabled = true;
this.listTest.Location = new System.Drawing.Point(5, 5);
this.listTest.Name = "TestListBox";
this.listTest.Size = new System.Drawing.Size(300, 400);
this.listTest.TabIndex = 1;
this.Load += new System.EventHandler(this.OnLoad);
}
private void OnLoad(object sender, EventArgs e)
{
this.Controls.Add(listTest);
listDataSource.Add(new DataType1("항목1",new Point(1,1)));
listDataSource.Add(new DataType1("항목2",new Point(5,6)));
listDataSource.Add(new DataType1("항목3",new Point(2,1)));
this.listTest.DataSource = listDataSource; // ListBox와의 바인딩(연결)
this.listTest.DisplayMember = "Name"; // DataType1의 Name 프로퍼티의 Get 내용이 출력
this.listTest.ValueMember = "Pos"; // DataType1의 Pos 프로퍼티의 Get 내용이 내장 값
}
}
}
자 그럼 이게 무엇을 뜻하는 것일까요?
바로 Data와 View의 분리를 완전하게 선언 할 수 있게 됩니다.
지금은 예제를 위해 Console 프로그램에서 Windows.Form을 띄워 처리했지만,
Visual Studio의 Template로 제공하는 Windows application을 만들게 되면,
보통 Form용 클래스가 만들어집니다.
그러나 대부분이 이 Form 안에 Data든 View든 Control 이든 모든 로직을
이 클래스 하나에만 넣게 됩니다. 그렇게 되면 프로그램도 복잡해지고
소스관리도 어려워 지며 나중에 유지 보수 할 때도 애먹게 됩니다.
그러나 이 처럼 Data와 View를 분리해서 짜다보면 이런 문제점을 위의 문제를
수월하게 제어 하실 수 있게 됩니다.
3. ListBox 데이터 바인딩 팁.
이 글을 쓰게된 가장 중요한 사항인데, 사실 DataSource를 이용해 데이터 객체와 바인딩 하고 난뒤 이후의 처리가 굉장히 황당한 경우가 있습니다. 특히 원본 데이터 부분을 업데이트 했는데도 불구하고, ListBox내의 내용이 전혀 변화가 없는 경우입니다.
처음에 저는 다음과 같은 함수를 만들어 위의 문제를 해결해 보려고 했었습니다.
[CODE]
private void UpdateList(ListBox listbox, List<DataType1> list)
{
try
{
BindingManagerBase bm;
bm = listbox.BindingContext[list];
bm.CancelCurrentEdit();
bm.ResumeBinding();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
listbox.Refresh();
}
[/CODE]
보통 컨트롤 내에 데이터 바인딩 기법을 사용하게 되면 데이터 바인딩에 관련된 기능들을 이용할 수 있는데, 여기서 데이터 바인딩 매니저를 이용하여 업데이트 하게 끔 만든 것입니다.
그러나 최악의 문제가 원본 데이터가 완전히 비어 있는 경우 입니다.
이 경우 원본 데이터가 비어 있으므로 ListBox의 컨트롤 데이터가 전혀 변화가 없게 됩니다.
그래서 결국 위의 내용을 다음과 같이 변경 했습니다
코드를 보시면 알겠지만, 바로 바인딩 작업을 완전히 리셋한 뒤 다시 바인딩을 시도했습니다.
이 경우 바인딩 정보가 완전히 리셋되기 때문에, 원본 데이터 내용이 비어 있어도,
정상적으로 처리가 됩니다.
물론 위의 방법은 제가 발견한 팁일 분, 프로그래밍의 정답은 아닙니다.
개인적으로도 위의 방법은 비용이 조금 비쌀듯 해보입니다. 바인딩 정보를 완전히
날리고, 다시 재 바인딩 하기 때문에, 무언가 비용이 많이 들어갈 것 같습니다.
하지만, 현재 제가 발견한 방법 중에 확실하게 처리되는 것 같아 제시한 것입니다.