.NET MAUI의 CollectionView는 데이터 컬렉션을 유연하게 표시하고 관리하기 위한 강력한 컨트롤입니다. 이 문서는 CollectionView의 기본적인 사용법부터 다양한 시각적 사용자 정의, 데이터 관리 기능까지 심도 있게 다룹니다.
1. 기본적인 사용법 및 데이터 바인딩
CollectionView를 사용하기 위해서는 표시할 데이터 모델과 해당 데이터를 CollectionView에 제공할 뷰 모델이 필요합니다. 여기서는 StaffMember라는 간단한 직원 모델을 사용하겠습니다.
1.1. 데이터 모델 정의 (Models/StaffMember.cs)
public class StaffMember
{
public int EmployeeId { get; set; }
public string FullName { get; set; }
public string DepartmentName { get; set; }
public string ProfileImage { get; set; } // 예: "kim.png"
public string ShortDescription { get; set; }
}
1.2. 뷰 모델 구현 (ViewModels/EmployeeListViewModel.cs)
ObservableCollection<StaffMember>을 사용하여 데이터 변경 시 UI가 자동으로 업데이트되도록 합니다. 프로필 이미지는 Resources/Images 폴더에 미리 추가해야 합니다.
using CommunityToolkit.Mvvm.ComponentModel;
using System.Collections.ObjectModel;
namespace MyMauiApp.ViewModels;
public partial class EmployeeListViewModel : ObservableObject
{
[ObservableProperty]
private ObservableCollection<StaffMember> teamMembers;
public EmployeeListViewModel()
{
TeamMembers = new ObservableCollection<StaffMember>
{
new StaffMember { EmployeeId = 1, FullName = "김철수", DepartmentName = "개발부", ProfileImage = "kim.png", ShortDescription = "소프트웨어 개발 담당" },
new StaffMember { EmployeeId = 2, FullName = "이영희", DepartmentName = "개발부", ProfileImage = "lee.png", ShortDescription = "UX/UI 디자인 담당" },
new StaffMember { EmployeeId = 3, FullName = "박민준", DepartmentName = "영업부", ProfileImage = "park.png", ShortDescription = "신규 고객 유치" },
new StaffMember { EmployeeId = 4, FullName = "최지혜", DepartmentName = "마케팅부", ProfileImage = "choi.png", ShortDescription = "온라인 캠페인 기획" },
new StaffMember { EmployeeId = 5, FullName = "정수민", DepartmentName = "영업부", ProfileImage = "jung.png", ShortDescription = "기존 고객 관리" }
};
}
}
1.3. XAML에서 CollectionView 사용 (Views/EmployeeListPage.xaml)
CollectionView의 ItemsSource 속성을 뷰 모델의 TeamMembers에 바인딩하고, ItemTemplate을 사용하여 각 항목의 시각적 요소를 정의합니다.
<ContentPage
xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:vm="clr-namespace:MyMauiApp.ViewModels"
x:Class="MyMauiApp.Views.EmployeeListPage"
Title="직원 목록">
<ContentPage.BindingContext>
<vm:EmployeeListViewModel />
</ContentPage.BindingContext>
<!-- ItemsSource 속성으로 데이터 원본 지정 -->
<CollectionView ItemsSource="{Binding TeamMembers}">
<!-- ItemTemplate 속성으로 각 데이터 항목의 UI 정의 -->
<CollectionView.ItemTemplate>
<DataTemplate x:DataType="{x:Type vm:StaffMember}">
<Grid ColumnDefinitions="70,*" RowDefinitions="Auto,Auto,Auto" Padding="10">
<Image
Grid.RowSpan="3"
Source="{Binding ProfileImage}"
Aspect="AspectFill"
HeightRequest="60"
WidthRequest="60"
Margin="0,0,10,0" />
<Label
Grid.Column="1"
Text="{Binding FullName}"
FontAttributes="Bold"
FontSize="16" />
<Label
Grid.Row="1"
Grid.Column="1"
Text="{Binding DepartmentName}"
FontSize="14"
TextColor="Gray" />
<Label
Grid.Row="2"
Grid.Column="1"
Text="{Binding ShortDescription}"
FontSize="12"
FontAttributes="Italic"
TextColor="DarkSlateGray" />
</Grid>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
</ContentPage>
2. 시각적 요소 사용자 정의
2.1. 조건부 스타일 적용 (DataTemplateSelector)
데이터 항목의 특정 조건에 따라 다른 UI 템플릿을 적용할 수 있습니다. 예를 들어, 부서에 따라 다른 배경색이나 텍스트 색상을 사용할 수 있습니다.
2.1.1. 템플릿 셀렉터 클래스 정의 (Controls/DepartmentTemplateSelector.cs)
using MyMauiApp.ViewModels; // StaffMember 클래스 접근을 위해 추가
using Microsoft.Maui.Controls;
using System.Collections.Generic;
namespace MyMauiApp.Controls;
public class DepartmentTemplateSelector : DataTemplateSelector
{
public DataTemplate DevelopmentTemplate { get; set; }
public DataTemplate SalesMarketingTemplate { get; set; }
protected override DataTemplate OnSelectTemplate(object item, BindableObject container)
{
if (item is StaffMember staff)
{
// '개발부' 소속이면 DevelopmentTemplate, 그 외는 SalesMarketingTemplate 사용
return staff.DepartmentName.Contains("개발부") ? DevelopmentTemplate : SalesMarketingTemplate;
}
return base.OnSelectTemplate(item, container);
}
}
2.1.2. XAML에서 DataTemplateSelector 사용
ContentPage.Resources에 여러 DataTemplate을 정의하고, DepartmentTemplateSelector를 인스턴스화하여 이 템플릿들을 바인딩합니다. 이후 CollectionView의 ItemTemplate 속성에 DepartmentTemplateSelector를 지정합니다.
<ContentPage
xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:vm="clr-namespace:MyMauiApp.ViewModels"
xmlns:c="clr-namespace:MyMauiApp.Controls"
x:Class="MyMauiApp.Views.EmployeeListPage"
Title="조건부 스타일 예제">
<ContentPage.BindingContext>
<vm:EmployeeListViewModel />
</ContentPage.BindingContext>
<ContentPage.Resources>
<!-- 개발부 직원용 DataTemplate -->
<DataTemplate x:Key="DevelopmentTemplate" x:DataType="{x:Type vm:StaffMember}">
<Grid ColumnDefinitions="70,*" RowDefinitions="Auto,Auto,Auto" Padding="10" BackgroundColor="#E0F2F7">
<Image Grid.RowSpan="3" Source="{Binding ProfileImage}" Aspect="AspectFill" HeightRequest="60" WidthRequest="60" Margin="0,0,10,0" />
<Label Grid.Column="1" Text="{Binding FullName}" FontAttributes="Bold" FontSize="16" />
<Label Grid.Row="1" Grid.Column="1" Text="{Binding DepartmentName}" FontSize="14" TextColor="DarkCyan" />
<Label Grid.Row="2" Grid.Column="1" Text="{Binding ShortDescription}" FontSize="12" FontAttributes="Italic" TextColor="DarkSlateGray" />
</Grid>
</DataTemplate>
<!-- 영업/마케팅부 직원용 DataTemplate -->
<DataTemplate x:Key="SalesMarketingTemplate" x:DataType="{x:Type vm:StaffMember}">
<Grid ColumnDefinitions="70,*" RowDefinitions="Auto,Auto,Auto" Padding="10" BackgroundColor="#FFF8E1">
<Image Grid.RowSpan="3" Source="{Binding ProfileImage}" Aspect="AspectFill" HeightRequest="60" WidthRequest="60" Margin="0,0,10,0" />
<Label Grid.Column="1" Text="{Binding FullName}" FontAttributes="Bold" FontSize="16" />
<Label Grid.Row="1" Grid.Column="1" Text="{Binding DepartmentName}" FontSize="14" TextColor="DarkOrange" />
<Label Grid.Row="2" Grid.Column="1" Text="{Binding ShortDescription}" FontSize="12" FontAttributes="Italic" TextColor="DarkSlateGray" />
</Grid>
</DataTemplate>
<!-- DataTemplateSelector 인스턴스화 및 템플릿 할당 -->
<c:DepartmentTemplateSelector
x:Key="DepartmentSelector"
DevelopmentTemplate="{StaticResource DevelopmentTemplate}"
SalesMarketingTemplate="{StaticResource SalesMarketingTemplate}" />
</ContentPage.Resources>
<!-- ItemTemplate에 DataTemplateSelector 적용 -->
<CollectionView ItemTemplate="{StaticResource DepartmentSelector}" ItemsSource="{Binding TeamMembers}" />
</ContentPage>
2.2. 항목 배열 방식 (ItemsLayout)
CollectionView는 ItemsLayout 속성을 통해 다양한 레이아웃 옵션을 제공합니다. 수직/수평 리스트, 수직/수평 그리드 등을 설정할 수 있습니다.
2.2.1. 수직 리스트 (기본값)
<!-- 간단한 설정 -->
<CollectionView ItemsSource="{Binding TeamMembers}" ItemsLayout="VerticalList">
<!-- ... ItemTemplate ... -->
</CollectionView>
<!-- 속성 요소 방식 -->
<CollectionView ItemsSource="{Binding TeamMembers}">
<CollectionView.ItemsLayout>
<LinearItemsLayout Orientation="Vertical" ItemSpacing="5" />
</CollectionView.ItemsLayout>
<!-- ... ItemTemplate ... -->
</CollectionView>
2.2.2. 수평 리스트
<!-- 간단한 설정 -->
<CollectionView ItemsSource="{Binding TeamMembers}" ItemsLayout="HorizontalList">
<!-- ... ItemTemplate ... -->
</CollectionView>
<!-- 속성 요소 방식 -->
<CollectionView ItemsSource="{Binding TeamMembers}">
<CollectionView.ItemsLayout>
<LinearItemsLayout Orientation="Horizontal" ItemSpacing="5" />
</CollectionView.ItemsLayout>
<!-- ... ItemTemplate ... -->
</CollectionView>
2.2.3. 수직 그리드
<!-- 2열 수직 그리드 -->
<CollectionView ItemsSource="{Binding TeamMembers}" ItemsLayout="VerticalGrid, 2">
<!-- ... ItemTemplate ... -->
</CollectionView>
<!-- 속성 요소 방식 -->
<CollectionView ItemsSource="{Binding TeamMembers}">
<CollectionView.ItemsLayout>
<GridItemsLayout Orientation="Vertical" Span="2" VerticalItemSpacing="10" HorizontalItemSpacing="10" />
</CollectionView.ItemsLayout>
<!-- ... ItemTemplate ... -->
</CollectionView>
2.2.4. 수평 그리드
<!-- 2행 수평 그리드 -->
<CollectionView ItemsSource="{Binding TeamMembers}" ItemsLayout="HorizontalGrid, 2">
<!-- ... ItemTemplate ... -->
</CollectionView>
<!-- 속성 요소 방식 -->
<CollectionView ItemsSource="{Binding TeamMembers}">
<CollectionView.ItemsLayout>
<GridItemsLayout Orientation="Horizontal" Span="2" VerticalItemSpacing="10" HorizontalItemSpacing="10" />
</CollectionView.ItemsLayout>
<!-- ... ItemTemplate ... -->
</CollectionView>
2.3. 헤더 및 푸터 설정 (Header/Footer)
CollectionView의 시작과 끝에 고정된 콘텐츠를 표시할 수 있습니다.
2.3.1. 문자열 헤더/푸터
<CollectionView ItemsSource="{Binding TeamMembers}"
Header="우리 회사 팀원 목록"
Footer="Copyright © 2024">
<!-- ... ItemTemplate ... -->
</CollectionView>
2.3.2. 뷰를 이용한 헤더/푸터
<CollectionView ItemsSource="{Binding TeamMembers}">
<CollectionView.Header>
<StackLayout BackgroundColor="#E0E0E0" Padding="8">
<Label Text="직원 현황" FontAttributes="Bold" FontSize="15" HorizontalOptions="Center" />
</StackLayout>
</CollectionView.Header>
<CollectionView.Footer>
<StackLayout BackgroundColor="#F0F0F0" Padding="8">
<Label Text="MAUI CollectionView 예제" FontAttributes="Italic" FontSize="11" TextColor="Gray" HorizontalOptions="Center" />
</StackLayout>
</CollectionView.Footer>
<!-- ... ItemTemplate ... -->
</CollectionView>
2.3.3. DataTemplate을 이용한 헤더/푸터
더 복잡한 UI를 구성하거나 데이터 바인딩이 필요한 경우 DataTemplate을 사용할 수 있습니다.
<CollectionView
Header="{Binding .}" <!-- 현재 BindingContext를 헤더 템플릿으로 전달 -->
Footer="{Binding .}"
ItemsSource="{Binding TeamMembers}">
<CollectionView.HeaderTemplate>
<DataTemplate>
<StackLayout BackgroundColor="#D0D0D0" Padding="10">
<Label Text="전체 팀원 목록" FontAttributes="Bold" FontSize="16" TextColor="DarkBlue" HorizontalOptions="Center" />
</StackLayout>
</DataTemplate>
</CollectionView.HeaderTemplate>
<CollectionView.FooterTemplate>
<DataTemplate>
<StackLayout BackgroundColor="#E8E8E8" Padding="10">
<Label Text="데이터 제공: 회사 인트라넷" FontAttributes="Italic" FontSize="12" TextColor="Gray" HorizontalOptions="Center" />
</StackLayout>
</DataTemplate>
</CollectionView.FooterTemplate>
<!-- ... ItemTemplate ... -->
</CollectionView>
2.4. 데이터 항목 간 간격 설정
ItemsLayout 속성 요소를 사용하여 항목 간의 간격을 조절할 수 있습니다.
2.4.1. 수직/수평 리스트의 항목 간격
<CollectionView ItemsSource="{Binding TeamMembers}">
<CollectionView.ItemsLayout>
<LinearItemsLayout ItemSpacing="10" Orientation="Vertical" />
</CollectionView.ItemsLayout>
<!-- ... ItemTemplate ... -->
</CollectionView>
2.4.2. 수직/수평 그리드의 항목 간격
<CollectionView ItemsSource="{Binding TeamMembers}">
<CollectionView.ItemsLayout>
<GridItemsLayout Orientation="Vertical"
Span="2"
VerticalItemSpacing="15"
HorizontalItemSpacing="25" />
</CollectionView.ItemsLayout>
<!-- ... ItemTemplate ... -->
</CollectionView>>
2.5. 데이터 항목 크기 조정 전략 (ItemSizingStrategy)
CollectionView가 항목의 크기를 계산하는 방식을 지정합니다.
<!-- 기본값: 모든 항목의 크기를 개별적으로 측정 -->
<CollectionView ItemsSource="{Binding TeamMembers}"
ItemSizingStrategy="MeasureAllItems">
<!-- ... -->
</CollectionView>
<!-- 첫 번째 항목 크기를 기준으로 나머지 항목 크기를 결정 -->
<CollectionView ItemsSource="{Binding TeamMembers}"
ItemSizingStrategy="MeasureFirstItem">
<!-- ... -->
</CollectionView>
2.6. 데이터 항목 정렬 (FlowDirection)
ContentPage 또는 상위 레이아웃의 FlowDirection 속성을 사용하여 항목의 정렬 방향을 변경할 수 있습니다.
<ContentPage
xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
FlowDirection="RightToLeft" <!-- 오른쪽에서 왼쪽으로 정렬 -->
Title="방향 정렬 예제">
<StackLayout Padding="20">
<CollectionView ItemsSource="{Binding TeamMembers}">
<!-- ... ItemTemplate ... -->
</CollectionView>
</StackLayout>
</ContentPage>
2.7. 데이터 항목 동적 크기 조정
사용자 상호작용에 따라 항목의 크기를 동적으로 변경하는 것은 코드 비하인드에서 처리할 수 있습니다.
<!-- EmployeeListPage.xaml -->
<ContentPage
xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:vm="clr-namespace:MyMauiApp.ViewModels"
x:Class="MyMauiApp.Views.EmployeeListPage"
Title="동적 크기 조정 예제">
<ContentPage.BindingContext>
<vm:EmployeeListViewModel />
</ContentPage.BindingContext>
<Grid RowDefinitions="*,50">
<CollectionView
Grid.Row="0"
ItemsSource="{Binding TeamMembers}"
SelectionMode="None">
<CollectionView.ItemTemplate>
<DataTemplate x:DataType="{x:Type vm:StaffMember}">
<Grid ColumnDefinitions="*,Auto" Padding="10">
<StackLayout Grid.Column="0" VerticalOptions="Center">
<Label Text="{Binding FullName}" FontAttributes="Bold" FontSize="16" />
<Label Text="{Binding DepartmentName}" FontSize="14" TextColor="Gray" />
</StackLayout>
<ImageButton
Grid.Column="1"
Clicked="ProfileImage_Tapped" <!-- 클릭 이벤트 핸들러 -->
HeightRequest="70"
WidthRequest="70"
Aspect="AspectFill"
Source="{Binding ProfileImage}"
VerticalOptions="Center" />
</Grid>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
</Grid>
</ContentPage>
// EmployeeListPage.xaml.cs (코드 비하인드)
namespace MyMauiApp.Views;
public partial class EmployeeListPage : ContentPage
{
public EmployeeListPage()
{
InitializeComponent();
}
private void ProfileImage_Tapped(object sender, EventArgs e)
{
if (sender is ImageButton imgButton)
{
// 클릭 시 이미지 버튼의 크기를 토글합니다.
imgButton.HeightRequest = imgButton.WidthRequest = imgButton.HeightRequest.Equals(70) ? 110 : 70;
}
}
}
2.8. 스와이프 컨텍스트 메뉴 (SwipeView)
SwipeView 컨트롤을 사용하여 CollectionView 항목에 스와이프 동작 기반의 컨텍스트 메뉴를 추가할 수 있습니다.
<!-- EmployeeListPage.xaml -->
<ContentPage
xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:vm="clr-namespace:MyMauiApp.ViewModels"
x:Class="MyMauiApp.Views.EmployeeListPage"
x:Name="employeeListPage" <!-- 뷰 모델에 접근하기 위한 x:Name -->
Title="스와이프 메뉴 예제">
<ContentPage.BindingContext>
<vm:EmployeeListViewModel />
</ContentPage.BindingContext>
<CollectionView
x:Name="staffCollectionView"
ItemsSource="{Binding TeamMembers}"
SelectionMode="None">
<CollectionView.ItemsLayout>
<LinearItemsLayout Orientation="Vertical" ItemSpacing="1" />
</CollectionView.ItemsLayout>
<CollectionView.ItemTemplate>
<DataTemplate x:DataType="{x:Type vm:StaffMember}">
<!-- SwipeView로 DataTemplate 내용 감싸기 -->
<SwipeView>
<!-- 왼쪽 스와이프 메뉴 정의 -->
<SwipeView.LeftItems>
<SwipeItems Mode="Reveal">
<SwipeItem
BackgroundColor="LightGreen"
Command="{Binding Source={x:Reference employeeListPage}, Path=BindingContext.ToggleFavoriteCommand}"
CommandParameter="{Binding .}" <!-- 현재 항목을 명령 파라미터로 전달 -->
Text="즐겨찾기"
IconImageSource="star.png" />
<SwipeItem
BackgroundColor="LightCoral"
Command="{Binding Source={x:Reference employeeListPage}, Path=BindingContext.RemoveStaffMemberCommand}"
CommandParameter="{Binding .}"
Text="삭제"
IconImageSource="delete.png" />
</SwipeItems>
</SwipeView.LeftItems>
<!-- 실제 항목 내용 -->
<Grid ColumnDefinitions="70,*" RowDefinitions="Auto,Auto,Auto" Padding="10" BackgroundColor="White">
<Image Grid.RowSpan="3" Source="{Binding ProfileImage}" Aspect="AspectFill" HeightRequest="60" WidthRequest="60" Margin="0,0,10,0" />
<Label Grid.Column="1" Text="{Binding FullName}" FontAttributes="Bold" FontSize="16" />
<Label Grid.Row="1" Grid.Column="1" Text="{Binding DepartmentName}" FontSize="14" TextColor="Gray" />
<Label Grid.Row="2" Grid.Column="1" Text="{Binding ShortDescription}" FontSize="12" FontAttributes="Italic" TextColor="DarkSlateGray" />
</Grid>
</SwipeView>
</DataTemplate>>
</CollectionView.ItemTemplate>
</CollectionView>
</ContentPage>
// EmployeeListViewModel.cs (뷰 모델)
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using System.Collections.ObjectModel;
using Microsoft.Maui.Controls; // DisplayAlert를 위해 추가
namespace MyMauiApp.ViewModels;
public partial class EmployeeListViewModel : ObservableObject
{
[ObservableProperty]
private ObservableCollection<StaffMember> teamMembers;
public EmployeeListViewModel()
{
TeamMembers = new ObservableCollection<StaffMember>
{
new StaffMember { EmployeeId = 1, FullName = "김철수", DepartmentName = "개발부", ProfileImage = "kim.png", ShortDescription = "소프트웨어 개발 담당" },
new StaffMember { EmployeeId = 2, FullName = "이영희", DepartmentName = "개발부", ProfileImage = "lee.png", ShortDescription = "UX/UI 디자인 담당" },
new StaffMember { EmployeeId = 3, FullName = "박민준", DepartmentName = "영업부", ProfileImage = "park.png", ShortDescription = "신규 고객 유치" },
new StaffMember { EmployeeId = 4, FullName = "최지혜", DepartmentName = "마케팅부", ProfileImage = "choi.png", ShortDescription = "온라인 캠페인 기획" },
new StaffMember { EmployeeId = 5, FullName = "정수민", DepartmentName = "영업부", ProfileImage = "jung.png", ShortDescription = "기존 고객 관리" }
};
}
[RelayCommand]
private void RemoveStaffMember(StaffMember member)
{
if (member != null)
{
TeamMembers.Remove(member);
Application.Current.MainPage?.DisplayAlert("삭제 완료", $"{member.FullName} 님이 목록에서 제거되었습니다.", "확인");
}
}
[RelayCommand]
private void ToggleFavorite(StaffMember member)
{
if (member != null)
{
// 즐겨찾기 상태를 토글하는 로직 구현
Application.Current.MainPage?.DisplayAlert("알림", $"{member.FullName} 님을 즐겨찾기 상태 변경했습니다.", "확인");
}
}
}
3. 데이터 원본 관리
3.1. 데이터 항목 선택 제어
CollectionView는 항목 선택 방식을 제어하는 다양한 옵션을 제공합니다.
3.1.1. 단일 선택 및 초기 선택
<ContentPage
xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:vm="clr-namespace:MyMauiApp.ViewModels"
x:Class="MyMauiApp.Views.EmployeeListPage"
Title="단일 선택 예제">
<ContentPage.BindingContext>
<vm:EmployeeListViewModel />
</ContentPage.BindingContext>
<VerticalStackLayout>
<Label Text="{Binding SelectedStaffMember.FullName, StringFormat='선택된 직원: {0}'}" FontSize="18" Margin="10" />
<CollectionView
ItemsSource="{Binding TeamMembers}"
SelectedItem="{Binding SelectedStaffMember}" <!-- 단일 선택 항목 바인딩 -->
SelectionChanged="OnStaffSelectionChanged" <!-- 선택 변경 이벤트 -->
SelectionMode="Single"> <!-- 단일 선택 모드 -->
<!-- ... ItemTemplate ... -->
</CollectionView>
</VerticalStackLayout>
</ContentPage>
// EmployeeListViewModel.cs (뷰 모델)
public partial class EmployeeListViewModel : ObservableObject
{
// ... 기존 코드 ...
[ObservableProperty]
private StaffMember selectedStaffMember; // 단일 선택된 항목
public EmployeeListViewModel()
{
// ... TeamMembers 초기화 ...
SelectedStaffMember = TeamMembers.Skip(1).FirstOrDefault(); // 두 번째 항목을 초기 선택
}
}
// EmployeeListPage.xaml.cs (코드 비하인드 - 선택 이벤트 예시)
namespace MyMauiApp.Views;
public partial class EmployeeListPage : ContentPage
{
public EmployeeListPage()
{
InitializeComponent();
}
private void OnStaffSelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (e.CurrentSelection.FirstOrDefault() is StaffMember selected)
{
System.Diagnostics.Debug.WriteLine($"새로 선택된 직원: {selected.FullName}");
}
}
}
3.1.2. 다중 선택 및 초기 선택
<ContentPage
xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:vm="clr-namespace:MyMauiApp.ViewModels"
x:Class="MyMauiApp.Views.EmployeeListPage"
Title="다중 선택 예제">
<ContentPage.BindingContext>
<vm:EmployeeListViewModel />
</ContentPage.BindingContext>
<VerticalStackLayout>
<Label Text="{Binding SelectedStaffMembers.Count, StringFormat='선택된 직원 수: {0}'}" FontSize="18" Margin="10" />
<CollectionView
ItemsSource="{Binding TeamMembers}"
SelectedItems="{Binding SelectedStaffMembers}" <!-- 다중 선택 항목 바인딩 -->
SelectionMode="Multiple"> <!-- 다중 선택 모드 -->
<!-- ... ItemTemplate ... -->
</CollectionView>
</VerticalStackLayout>
</ContentPage>
// EmployeeListViewModel.cs (뷰 모델)
public partial class EmployeeListViewModel : ObservableObject
{
// ... 기존 코드 ...
[ObservableProperty]
private ObservableCollection<object> selectedStaffMembers; // SelectedItems는 ObservableCollection<object>로 바인딩
public EmployeeListViewModel()
{
// ... TeamMembers 초기화 ...
SelectedStaffMembers = new ObservableCollection<object> { TeamMembers[0], TeamMembers[2] }; // 첫 번째와 세 번째 항목을 초기 선택
}
}
3.1.3. 선택된 항목의 배경색 변경
VisualStateManager를 사용하여 항목이 선택되었을 때의 시각적 상태를 정의할 수 있습니다. ItemTemplate의 루트 요소에 스타일을 적용합니다.
<ContentPage
xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:vm="clr-namespace:MyMauiApp.ViewModels"
x:Class="MyMauiApp.Views.EmployeeListPage"
Title="선택 색상 예제">
<ContentPage.BindingContext>
<vm:EmployeeListViewModel />
</ContentPage.BindingContext>
<ContentPage.Resources>
<Style TargetType="Grid"> <!-- ItemTemplate의 루트 요소에 적용 -->
<Setter Property="VisualStateManager.VisualStateGroups">
<VisualStateGroupList>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal" />
<VisualState x:Name="Selected">
<VisualState.Setters>
<Setter Property="BackgroundColor" Value="LightBlue" /> <!-- 선택 시 배경색 -->
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateGroupList>
</Setter>
</Style>
</ContentPage.Resources>
<StackLayout Margin="10">
<CollectionView ItemsSource="{Binding TeamMembers}" SelectionMode="Multiple">
<CollectionView.ItemTemplate>
<DataTemplate x:DataType="{x:Type vm:StaffMember}">
<Grid Padding="10"> <!-- Style이 적용될 루트 Grid -->
<Label Text="{Binding FullName}" FontSize="16" />
<!-- ... 다른 항목 내용 ... -->
</Grid>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
</StackLayout>
</ContentPage>
3.2. 빈 뷰 (EmptyView)
CollectionView의 ItemsSource가 비어 있거나 null일 때 표시될 콘텐츠를 정의할 수 있습니다.
3.2.1. 문자열 EmptyView
<!-- EmptyTeamMembers가 비어있으면 "표시할 직원 데이터가 없습니다." 문자열이 표시됩니다. -->
<CollectionView ItemsSource="{Binding EmptyTeamMembers}" EmptyView="표시할 직원 데이터가 없습니다.">
<!-- ... ItemTemplate ... -->
</CollectionView>
// EmployeeListViewModel.cs (뷰 모델)
public partial class EmployeeListViewModel : ObservableObject
{
// ... 기존 코드 ...
[ObservableProperty]
private ObservableCollection<StaffMember> emptyTeamMembers = new ObservableCollection<StaffMember>(); // 빈 컬렉션
}
3.2.2. 뷰를 이용한 EmptyView
더 복잡한 UI를 표시하려면 ContentView를 사용하여 EmptyView를 정의할 수 있습니다.
<CollectionView ItemsSource="{Binding EmptyTeamMembers}">
<CollectionView.EmptyView>
<!-- ContentView는 생략 가능 -->
<ContentView>
<StackLayout HorizontalOptions="Center" VerticalOptions="Center" Padding="20">
<Label
Text="데이터를 찾을 수 없습니다."
FontAttributes="Bold"
FontSize="18"
HorizontalTextAlignment="Center" />
<Image Source="no_data_icon.png" HeightRequest="100" Margin="0,15,0,0" />
<Label
Text="나중에 다시 시도하거나 데이터를 추가해주세요."
FontSize="14"
TextColor="Gray"
HorizontalTextAlignment="Center" />
</StackLayout>
</ContentView>
</CollectionView.EmptyView>
<!-- ... ItemTemplate ... -->
</CollectionView>
3.2.3. 런타임에 동적으로 EmptyView 변경
ContentPage.Resources에 여러 EmptyView 템플릿을 정의하고, 코드 비하인드에서 필요에 따라 변경할 수 있습니다.
<!-- EmployeeListPage.xaml -->
<ContentPage
xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:vm="clr-namespace:MyMauiApp.ViewModels"
x:Class="MyMauiApp.Views.EmployeeListPage"
Title="동적 EmptyView 예제">
<ContentPage.BindingContext>
<vm:EmployeeListViewModel />
</ContentPage.BindingContext>
<ContentPage.Resources>
<ContentView x:Key="NoDataViewA">
<StackLayout HorizontalOptions="CenterAndExpand" VerticalOptions="CenterAndExpand">
<Label FontSize="18" Text="데이터 없음 (옵션 A)" />
</StackLayout>
</ContentView>
<ContentView x:Key="NoDataViewB">
<StackLayout HorizontalOptions="CenterAndExpand" VerticalOptions="CenterAndExpand">
<Label FontSize="18" Text="데이터 없음 (옵션 B)" />
</StackLayout>
</ContentView>
</ContentPage.Resources>
<VerticalStackLayout>
<Switch Toggled="OnEmptyViewToggle" IsToggled="False" Margin="10" />
<Label Text="토글하여 EmptyView 변경" HorizontalOptions="Center" Margin="0,0,0,10" />
<CollectionView x:Name="dynamicEmptyCollectionView" ItemsSource="{Binding EmptyTeamMembers}">
<CollectionView.ItemTemplate>
<!-- ... ItemTemplate ... -->
</CollectionView.ItemTemplate>
</CollectionView>
</VerticalStackLayout>
</ContentPage>
// EmployeeListPage.xaml.cs (코드 비하인드)
namespace MyMauiApp.Views;
public partial class EmployeeListPage : ContentPage
{
public EmployeeListPage()
{
InitializeComponent();
}
private void OnEmptyViewToggle(object sender, ToggledEventArgs e)
{
// 스위치 상태에 따라 다른 EmptyView를 할당
dynamicEmptyCollectionView.EmptyView = e.Value ? Resources["NoDataViewA"] : Resources["NoDataViewB"];
}
}
3.3. 풀 투 리프레시 (Pull-to-refresh)
CollectionView는 RefreshView 컨트롤과 함께 사용하여 사용자가 목록을 아래로 당겨 데이터를 새로 고치도록 할 수 있습니다. RefreshView의 Command와 IsRefreshing 속성을 뷰 모델에 바인딩합니다.
3.4. 점진적 로딩 (Incremental Loading)
방대한 데이터 세트를 처리할 때, 모든 데이터를 한 번에 로드하는 대신 사용자가 목록을 스크롤할 때 추가 데이터를 로드하도록 구성할 수 있습니다. CollectionView는 스크롤 임계값과 관련된 이벤트를 제공하여 이를 구현할 수 있습니다.
4. 목록 스크롤 제어
4.1. 스크롤 이벤트
Scrolled 이벤트를 사용하여 CollectionView의 스크롤 위치 변화를 감지할 수 있습니다. 이벤트 인자 ItemsViewScrolledEventArgs는 스크롤 양, 오프셋, 보이는 항목의 인덱스 등 다양한 정보를 제공합니다.
<CollectionView Scrolled="OnCollectionViewScrolled" ItemsSource="{Binding TeamMembers}">
<!-- ... -->
</CollectionView>
// 코드 비하인드
private void OnCollectionViewScrolled(object sender, ItemsViewScrolledEventArgs e)
{
System.Diagnostics.Debug.WriteLine($"수평 이동: {e.HorizontalDelta}, 수직 이동: {e.VerticalDelta}");
System.Diagnostics.Debug.WriteLine($"수평 오프셋: {e.HorizontalOffset}, 수직 오프셋: {e.VerticalOffset}");
System.Diagnostics.Debug.WriteLine($"첫 번째 가시 항목 인덱스: {e.FirstVisibleItemIndex}, 마지막: {e.LastVisibleItemIndex}");
// 필요한 경우 추가 로직 구현 (예: 특정 위치에 도달했을 때 데이터 로드)
}
4.2. 코드를 통한 스크롤 제어
ScrollTo 메서드를 사용하여 특정 인덱스 또는 특정 데이터 항목으로 CollectionView를 프로그래밍 방식으로 스크롤할 수 있습니다.
4.2.1. 인덱스로 스크롤
// 인덱스 3의 항목으로 스크롤하고, 항목을 뷰포트 시작 부분에 배치하며, 애니메이션 사용
staffCollectionView.ScrollTo(3, position: ScrollToPosition.Start, animate: true);
// 그룹화된 데이터의 경우, 특정 그룹의 특정 인덱스로 스크롤
// staffCollectionView.ScrollTo(그룹인덱스, 항목인덱스, ...);
4.2.2. 특정 데이터 항목으로 스크롤
// 뷰 모델에서 특정 직원을 찾아 해당 직원으로 스크롤
var viewModel = BindingContext as EmployeeListViewModel;
var targetMember = viewModel?.TeamMembers.FirstOrDefault(m => m.FullName == "정수민");
if (targetMember != null)
{
// '정수민' 팀원으로 스크롤하고, 항목을 뷰포트 중앙에 배치하며, 애니메이션은 사용하지 않음
staffCollectionView.ScrollTo(targetMember, position: ScrollToPosition.Center, animate: false);
}
// 그룹화된 데이터의 경우, 특정 그룹 내의 특정 항목으로 스크롤
// staffCollectionView.ScrollTo(특정항목, 특정그룹, ...);
5. 데이터 그룹화 표시
CollectionView는 데이터를 그룹으로 묶어 표시하는 기능을 지원합니다. 이는 일반적으로 두 가지 단계로 이루어집니다: 그룹화된 데이터 원본 설정 및 XAML에서 그룹 헤더 템플릿 정의.
5.1. 그룹화된 데이터 원본 설정
먼저, 그룹을 나타내는 클래스를 정의하고 뷰 모델에서 데이터를 그룹화합니다. 예를 들어, StaffMember를 DepartmentName별로 그룹화할 수 있습니다.
// Models/StaffGroup.cs
using System.Collections.ObjectModel;
using System.Collections.Generic;
public class StaffGroup : ObservableCollection<StaffMember>
{
public string GroupTitle { get; private set; } // 그룹 헤더에 표시될 제목
public StaffGroup(string title, IEnumerable<StaffMember> staffMembers) : base(staffMembers)
{
GroupTitle = title;
}
}
// ViewModels/EmployeeListViewModel.cs (수정된 뷰 모델)
public partial class EmployeeListViewModel : ObservableObject
{
[ObservableProperty]
private ObservableCollection<StaffGroup> groupedTeamMembers; // 그룹화된 데이터 컬렉션
public EmployeeListViewModel()
{
var allMembers = new List<StaffMember>
{
new StaffMember { EmployeeId = 1, FullName = "김철수", DepartmentName = "개발부", ProfileImage = "kim.png", ShortDescription = "소프트웨어 개발 담당" },
new StaffMember { EmployeeId = 2, FullName = "이영희", DepartmentName = "개발부", ProfileImage = "lee.png", ShortDescription = "UX/UI 디자인 담당" },
new StaffMember { EmployeeId = 3, FullName = "박민준", DepartmentName = "영업부", ProfileImage = "park.png", ShortDescription = "신규 고객 유치" },
new StaffMember { EmployeeId = 4, FullName = "최지혜", DepartmentName = "마케팅부", ProfileImage = "choi.png", ShortDescription = "온라인 캠페인 기획" },
new StaffMember { EmployeeId = 5, FullName = "정수민", DepartmentName = "영업부", ProfileImage = "jung.png", ShortDescription = "기존 고객 관리" }
};
// 부서 이름으로 그룹화
GroupedTeamMembers = new ObservableCollection<StaffGroup>(
allMembers.GroupBy(m => m.DepartmentName)
.OrderBy(g => g.Key) // 그룹을 부서 이름으로 정렬
.Select(g => new StaffGroup(g.Key, g))
);
}
}
5.2. XAML에서 그룹화된 데이터 표시
CollectionView의 IsGrouped 속성을 True로 설정하고 GroupHeaderTemplate을 정의하여 각 그룹의 헤더를 표시합니다. ItemTemplate은 그룹 내 각 항목을 표시하는 데 사용됩니다.
<ContentPage
xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:vm="clr-namespace:MyMauiApp.ViewModels"
x:Class="MyMauiApp.Views.EmployeeListPage"
Title="그룹화된 직원 목록">
<ContentPage.BindingContext>
<vm:EmployeeListViewModel />
</ContentPage.BindingContext>
<CollectionView
IsGrouped="True" <!-- 그룹화 사용 설정 -->
ItemsSource="{Binding GroupedTeamMembers}">
<!-- 그룹 헤더 템플릿 정의 -->
<CollectionView.GroupHeaderTemplate>
<DataTemplate x:DataType="{x:Type vm:StaffGroup}">
<Grid BackgroundColor="#DDF" Padding="10">
<Label Text="{Binding GroupTitle}" FontAttributes="Bold" FontSize="18" TextColor="DarkBlue" VerticalOptions="Center" />
</Grid>
</DataTemplate>
</CollectionView.GroupHeaderTemplate>
<!-- 개별 항목 템플릿 정의 -->
<CollectionView.ItemTemplate>
<DataTemplate x:DataType="{x:Type vm:StaffMember}">
<Grid Padding="10" BackgroundColor="White" Margin="0,1,0,0">
<Label Text="{Binding FullName}" FontSize="16" />
<!-- ... 다른 항목 세부 정보 (예: 프로필 이미지, 설명) ... -->
</Grid>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
</ContentPage>
이 외에도 GroupFooterTemplate을 사용하여 각 그룹의 푸터를 정의하거나, EmptyView와 유사하게 그룹이 비어있을 때 표시할 뷰를 설정하는 등 다양한 고급 기능을 활용할 수 있습니다.