[C#/WPF] ListView와 ListBox에 컬렉션바인딩

ListView나 ListBox는 하나의 객체만 바인딩하는게 아니라, 객체의 묶음을 바인딩해야하기 때문에, 데이터 클래스도 저번하고는 좀 다르게 설계해야한다.
그러기 위해서는 다음과 같은 클래스를 상속한다.

[code gutter=”false” language=”csharp”]
class System.Collections.ObjectModel.ObservableCollection<T>
[/code]

사실 저 제네릭클래스는 상속하기만 하면 상관이 없다. 여기서 T는 그냥 데이터만 저장하는 용도로만 사용해도 된다. INotifyPropertyChanged같은 것을 상속하지 않아도 상관이 없다.

테스트로 만들 WPF 어플리케이션은 다음과 같이 생겼다.

ListViewListBox
입력상자

여기에 사용할 내용은 언제나 그랬듯이 NameCard를 만들 것이다. 이 클래스는 다음과 같이 생겼다.

[code language=”csharp” title=”Model/TestModel.cs”]
using System.Collections.ObjectModel;

namespace BindingTest.Model
{
public class NameCardList : ObservableCollection<NameCard> { }

public class NameCard
{
private string name;
private int age;
private string tag;

public string Name
{
get { return name; }
}

public int Age
{
get { return age; }
}

public string Tag
{
get { return tag; }
}

public override string ToString()
{
return Name;
}

public NameCard(string name, int age, string tag)
{
this.name = name;
this.age = age;
this.tag = tag;
}
}
}
[/code]

ListView나 ListBox에서 직접 바인딩하는 대상은 위에서 말했듯이 NameCardList가 될 것이고, ListViw의 각각의 Column에는 NameCard의 Name, Age, Tag이 위치할 것이다. 굳이 ToString을 오버라이딩 하는 이유는 ListBox가 저걸 사용하기 때문이다.(ListBox는 Column이 나뉘어져 있지 않고, 오로지 객체를 나타내는 string으로 바꿔서 사용한다. 이때 사용하는 메서드가 ToString이다.)

[code language=”xml” title=”MainWindow.xaml#Window”]
<Window x:Class="BindingTest.MainWindow"

xmls:model="clr-namepsace:BindingTest.Model"

>
[/code]

[code language=”xml” title=”MainWindow.xaml#Window.Resources”]
<Window.Resources>
<model:NameCardList x:Key="MyListKey" />
</Window.Resources>
[/code]

이 위까지는 저번과 비슷하다.

[code language=”xml” title=”MainWindow.xaml#ListView” highlight=”1,4,5,6″]
<ListView Name="ListViewNameCard" ItemsSource="{Binding Source={StaticResource MyListKey}}">
<ListView.View>
<GridView AllowsColumnReorder="True">
<GridViewColumn DisplayMemberBinding="{Binding Path=Name}" Header="Name" Width="80" />
<GridViewColumn DisplayMemberBinding="{Binding Path=Age}" Header="Age" Width="80" />
<GridViewColumn DisplayMemberBinding="{Binding Path=Tag}" Header="Tag" Width="80" />
</GridView>
</ListView.View>
</ListView>
[/code]

  1. Grid.DataContext와 Binding 태그를 이용하지 않고 바인딩을 하는 것이다.(원래 방법을 이용해도 상관없음) ListView나 ListBox는 ItemsSource에 바인딩해야한다.
  2. 첫번째 Column의 Header 이름은 Name이고, ItemsSource의 개개의 아이템에서 Name이라는 프로퍼티와 바인딩한다.
  3. 마찬가지로 두번째 Column은 Age이고 ItemsSource 아이템의 Age와 바인딩한다.
  4. 세번째는 Tag이고 아이템의 Tag와 바인딩한다.

[code language=”xml” title=”MainWindow.xaml#ListBox”]
<ListBox ItemsSource="{Binding Source={StaticResource MyListKey}}" />
[/code]

리스트박스는 Column이 없으므로 그다지 복잡할 게 없다.

[code language=”xml” title=”MainWindow.xaml#InputBox”]
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="auto" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="auto" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="auto" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="auto" />
</Grid.ColumnDefinitions>

<Label Content="Name : " Margin="5, 5" />
<TextBox Grid.Column="1" Margin="5,5" Name="NameTextBox" />
<Label Grid.Column="2" Margin="5,5" Content="Age : " />
<TextBox Grid.Column="3" Margin="5,5" Name="AgeTextBox" />
<Label Grid.Column="4" Margin="5,5" Content="Tag : " />
<TextBox Grid.Column="5" Margin="5,5" Name="TagTextBox" />
<Button Name="AddButton" Margin="5,5" Grid.Column="6" Click="AddButton_Click">추가</Button>
</Grid>
[/code]

얘도 뭐 딱히 어려울 건 없다. 다음은 버튼의 이벤트핸들러 AddButton_Click를 정의한다.

[code language=”csharp”]
using System;
using System.Windows;

namespace BindingTest
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}

private void AddButton_Click(object sender, RoutedEventArgs e)
{
Model.NameCardList myList = Resources["MyListKey"] as Model.MyList;
string inputName, inputTag;
int inputAge;

inputName = NameTextBox.Text;
inputTag = TagTextBox.Text;
try
{
inputAge = Convert.ToInt32(AgeTextBox.Text);
}
catch (Exception ex)
{
inputAge = 30;
}

myList.Add(new Model.NameCard(inputName, inputAge, inputTag));

NameTextBox.Text = AgeTextBox.Text = TagTextBox.Text = "";
NameTextBox.Focus();
}
}
}
[/code]

이렇게 해놓으면 입력 후 추가 버튼을 누르면 버튼은 네임카드 리스트에 저장을 하지만, ListView와 ListBox에 자동으로 추가되는 모습을 볼 수 있다.

“[C#/WPF] ListView와 ListBox에 컬렉션바인딩”에 대한 7개의 응답

  1. 좋은 정보 감사합니다.
    혹시 반대로 listview의 값을 라벨에 바인딩하여 담을 수 있나요???

    1. 안녕하세요… 버려진 블로그에 오셨군요 ㅠㅠㅠ 리스트뷰의 어떤값이냐에 따라 다릅니다 바인딩 가능한 프로퍼티라면 마찬가지로 Label의 Text(확인해보니 Text가 아니라 Content네요;;)에 바인딩할 수 있습니다.

      가령 위 예제의 리스트뷰의 SelectedItem의 각각의 name, age, tag를 바인딩할 수도 있습니다.

  2. 우선 알려주셔서 감사합니다. 뭔가 그 이후에 내용이 바뀌었는지,” System.Windows.Controls.ItemCollection’ 형식의 컬렉션에 대한 값 추가에서 예외가 throw되었습니다” 오류가 뜹니다. 올려주신 글을 토대로 더 공부해봐야겠네요. 감사합니다.

    1. 코드를 그대로 따라했는데 아이템을 추가할 때에 에러가 발생한다는 말씀이신가요?

  3. WPF 초보인데 좋은 정보 감사합니다
    근데 혹시 프로그램샘플이 따로 있을까요
    어떻게 따라하는지 잘 모르겟어서 글남깁니다ㅠㅠ

    1. https://github.com/Aosamesan/ListBindingTest
      이거 보시면 될텐데 지금 보니 코드 되게 이상하네요;;; 다른 예제를 참고하시는 편이 좋을 것 같습니다.

  4. 앗.. 빠른답변 감사합니다 ㅎㅎ 컨트롤러 예제가 많이 없었는데 감사합니다 ㅎㅎ

수박남자에 답글 남기기 응답 취소

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다