[WPF] ListView와 ItemsSource
최근에 프로젝트를 진행하면서 ListView Control을 하며 어려웠던 부분을 정리하였습니다.
👉 기본 환경
- Language: C#, xaml
- IDE: Visual Basic 2022
ListView의 ItemsSource는 ToDoList에 맞추고 ListView의 Item은 ToDoItem의 필드에 바인딩을 하였습니다.
그 중 하나의 필드는 Button의 Command와 연결하여 간단하게 Message Box를 표시할 예정이었습니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
<Grid>
<ListView ItemsSource="{Binding ToDoList}"
SelectedItem="{Binding SelectedToDoItem}"
>
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Tag}" Width="70"/>
<TextBlock Text="{Binding Content}" Width="60"/>
<TextBlock Text="{Binding RegisteredDate, StringFormat=yyyy-MM-dd}" Width="80"/>
<Button Content="Is it Complete?"
Command="{Binding CheckCompleteCommand}"
/>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
|
이 상태로 실행을 하면,
'CheckCompleteCommand'를 찾을 수 없다는 바인딩 오류가 발생합니다.
(심지어 ListView의 Item 개수만큼 바인딩 오류가 나서 놀람😮 * Item 개수가 됩니다.)
가능성은 크게 3가지입니다.
1. 오타
2. 다른 ViewModel에 선언
3. ItemsSource
이번에는 ItemsSource와 관련한 문제에 대해 작성하고자 합니다.
1
2
3
4
5
6
7
8
9
10
|
private ObservableCollection<ToDoItem> _toDoList = new ObservableCollection<ToDoItem>();
public ObservableCollection<ToDoItem> ToDoList
{
get { return _toDoList; }
set
{
_toDoList = value;
OnPropertyChanged("ToDoList");
}
}
|
현재 ListView는 ItemsSource가 ToDoList로 선언되어있습니다.
때문에 ListView의 각 Item들은 ToDoList를 구성하고 있는 요소들, 여기서는 ToDoItem이 Source가 됩니다.
따라서 ListViewItem에 속한 Button과 바인딩된 CheckCompleteCommand를 ViewModel에서 찾게 되는 게 아니라 Item의 Source인 ToDoItem에서 찾게됩니다.
그러나 제 Command는 그 곳에 없습니다🥸.
ViewModel에 선언해놨습니다🥸.
좀 더 큰 세상에 있는 Command는 ListViewItem과는 다른 Source에서 찾을 수 있도록 코드를 변경해야합니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
<Grid>
<ListView ItemsSource="{Binding ToDoList}"
SelectedItem="{Binding SelectedToDoItem}"
>
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Tag}" Width="70"/>
<TextBlock Text="{Binding Content}" Width="60"/>
<TextBlock Text="{Binding RegisteredDate, StringFormat=yyyy-MM-dd}" Width="80"/>
<Button Content="Is it Complete?"
Command="{Binding DataContext.CheckCompleteCommand, RelativeSource={RelativeSource AncestorType={x:Type ListView}}}"
/>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
|
DataContext.CheckCompleteCommand
- 내 소중한 Command는 DataContext(= ViewModel)에 위치해 있고,
RelativeSource
- 상대 참조로 데이터를 바인딩할 것이고,
AncestorType={x:Type ListView}
- 그 상태 참조의 경로는 현재 Button의 상위 요소 중 ListView의 DataConext임을 나타냅니다.
⭐ ListView 등 ItemsSource를 따로 정해주는 경우, 그 안의 요소를 컨트롤할 때는 어떤 데이터를 바라보고 있는지도 유의해야 합니다.
(🤓 이 글을 작성하면서 ListView와 ListBox가 혼재되어있는 제 코드를 발견했습니다.
월요일에 출근하면 조용히 바꿔놓아야겠습니다..!)