WinUI 앱의 데이터 바인딩을 사용하면 컨트롤을 데이터 원본에 효율적으로 연결할 수 있습니다. 컨트롤을 단일 항목 또는 항목 컬렉션에 바인딩하고, 항목 렌더링을 제어하고, 세부 정보 보기를 구현하고, 표시할 데이터의 서식을 지정하는 방법을 알아봅니다. 자세한 내용은 데이터 바인딩을 자세히 참조하세요.
필수 구성 요소
이 항목에서는 Windows 앱 SDK를 사용하여 기본 WinUI 앱을 만드는 방법을 알고 있다고 가정합니다. 첫 번째 WinUI 앱을 만드는 방법에 대한 지침은 WinUI 앱 만들기를 참조하세요.
프로젝트 만들기
새 WinUI 빈 앱, 패키지된 C# 프로젝트를 만듭니다. 이름을 "빠른 시작"으로 지정합니다.
단일 항목에 바인딩
모든 바인딩은 바인딩 대상과 바인딩 원본으로 구성됩니다. 일반적으로 대상은 컨트롤 또는 다른 UI 요소의 속성이며 원본은 클래스 인스턴스(데이터 모델 또는 뷰 모델)의 속성입니다. 이 예제에서는 컨트롤을 단일 항목에 바인딩하는 방법을 보여줍니다.
Text의 TextBlock 속성이 대상입니다. 원본은 오디오 녹음을 나타내는 Recording이라는 간단한 클래스의 인스턴스입니다. 먼저 클래스를 살펴보겠습니다.
프로젝트에 새 클래스를 추가하고 클래스 이름을 Recording.
namespace Quickstart
{
public class Recording
{
public string ArtistName { get; set; }
public string CompositionName { get; set; }
public DateTime ReleaseDateTime { get; set; }
public Recording()
{
ArtistName = "Wolfgang Amadeus Mozart";
CompositionName = "Andante in C for Piano";
ReleaseDateTime = new DateTime(1761, 1, 1);
}
public string OneLineSummary
{
get
{
return $"{CompositionName} by {ArtistName}, released: "
+ ReleaseDateTime.ToString("d");
}
}
}
public class RecordingViewModel
{
private Recording defaultRecording = new();
public Recording DefaultRecording { get { return defaultRecording; } }
}
}
다음으로, 마크업 창을 표시하는 클래스에서 바인딩 소스 클래스를 공개합니다.
RecordingViewModel 형식 의 속성을 추가합니다.
namespace Quickstart
{
public sealed partial class MainWindow : Window
{
public MainWindow()
{
this.InitializeComponent();
}
public RecordingViewModel ViewModel{ get; } = new RecordingViewModel();
}
}
마지막 단계는 TextBlock을 ViewModel.DefaultRecording.OneLineSummary 속성에 바인딩하는 것입니다.
<Window x:Class="Quickstart.MainWindow" ... >
<Grid>
<TextBlock Text="{x:Bind ViewModel.DefaultRecording.OneLineSummary}"
HorizontalAlignment="Center"
VerticalAlignment="Center"/>
</Grid>
</Window>
결과는 다음과 같습니다.
항목 컬렉션에 바인딩
일반적인 시나리오는 비즈니스 개체 컬렉션에 바인딩하는 것입니다. C#에서 데이터 바인딩에 제네릭 ObservableCollection<T> 클래스를 사용합니다. 항목이 추가되거나 제거될 때 바인딩에 변경 알림을 제공하는 INotifyCollectionChanged 인터페이스를 구현합니다. 그러나 .NET 8 이상의 알려진 WinUI 릴리스 모드 버그로 인해 일부 시나리오에서 List<T> 를 사용해야 할 수 있습니다. 특히 컬렉션이 정적이며 초기화 후 변경되지 않는 경우 그렇습니다. 런타임 ObservableCollection<T>에 컬렉션이 변경되면 UI를 업데이트해야 하는 경우 . 고정된 항목 List<T> 집합만 표시하면 충분합니다. 또한 바인딩된 컨트롤이 컬렉션의 개체 속성 변경 내용으로 업데이트되도록 하려면 해당 개체가 INotifyPropertyChanged를 구현해야 합니다. 자세한 내용은 데이터 바인딩심층 부분을 참조하세요.
메모
사용하면 List<T>컬렉션 변경에 대한 변경 알림을 받지 못할 수 있습니다. 변경 내용 ObservableCollection<T>에 응답해야 하는 경우 . 이 예제에서는 컬렉션 변경 내용에 응답할 필요가 없으므로 List<T> 충분합니다.
다음은 ListView 를 개체 컬렉션 Recording 에 바인딩하는 예제입니다. 먼저 뷰 모델에 컬렉션을 추가합니다. 이러한 새 멤버를 클래스에 추가합니다 RecordingViewModel .
public class RecordingViewModel
{
...
private List<Recording> recordings = new();
public List<Recording> Recordings{ get{ return recordings; } }
public RecordingViewModel()
{
recordings.Add(new Recording(){ ArtistName = "Johann Sebastian Bach",
CompositionName = "Mass in B minor", ReleaseDateTime = new DateTime(1748, 7, 8) });
recordings.Add(new Recording(){ ArtistName = "Ludwig van Beethoven",
CompositionName = "Third Symphony", ReleaseDateTime = new DateTime(1805, 2, 11) });
recordings.Add(new Recording(){ ArtistName = "George Frideric Handel",
CompositionName = "Serse", ReleaseDateTime = new DateTime(1737, 12, 3) });
}
}
그런 다음 ListView 를 속성에 바인딩합니다 ViewModel.Recordings .
<Window x:Class="Quickstart.MainWindow" ... >
<Grid>
<ListView ItemsSource="{x:Bind ViewModel.Recordings}"
HorizontalAlignment="Center"
VerticalAlignment="Center"/>
</Grid>
</Window>
아직 클래스에 대한 Recording 데이터 템플릿을 제공하지 않았으므로 UI 프레임워크에서 수행할 수 있는 최선의 방법은 ListView의 각 항목에 대해 ToString을 호출하는 것입니다. 형식 이름을 반환하는 ToString 기본 구현입니다.
이 문제를 해결하려면 ToString 을 재정의 OneLineSummary하여 값을 반환하거나 데이터 템플릿을 제공할 수 있습니다. 데이터 템플릿 옵션은 보다 일반적이고 유연한 솔루션입니다. 콘텐츠 컨트롤의 ContentTemplate 속성 또는 항목 컨트롤의 ItemTemplate 속성을 사용하여 데이터 템플릿을 지정합니다. 다음은 결과의 그림과 함께 데이터 템플릿을 Recording 디자인할 수 있는 두 가지 방법입니다.
<ListView ItemsSource="{x:Bind ViewModel.Recordings}"
HorizontalAlignment="Center" VerticalAlignment="Center">
<ListView.ItemTemplate>
<DataTemplate x:DataType="local:Recording">
<TextBlock Text="{x:Bind OneLineSummary}"/>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<ListView ItemsSource="{x:Bind ViewModel.Recordings}"
HorizontalAlignment="Center" VerticalAlignment="Center">
<ListView.ItemTemplate>
<DataTemplate x:DataType="local:Recording">
<StackPanel Orientation="Horizontal" Margin="6">
<SymbolIcon Symbol="Audio" Margin="0,0,12,0"/>
<StackPanel>
<TextBlock Text="{x:Bind ArtistName}" FontWeight="Bold"/>
<TextBlock Text="{x:Bind CompositionName}"/>
</StackPanel>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
XAML 구문에 대한 자세한 내용은 XAML사용하여 UI 만들기를 참조하세요. 컨트롤 레이아웃에 대한 자세한 내용은 XAML사용하여 레이아웃 정의를 참조하세요.
세부 정보 보기 추가
Recording 항목에 개체의 모든 세부 정보를 표시하도록 선택할 수 있습니다. 그러나 이러한 접근 방식은 많은 공간을 차지합니다. 대신 항목에 충분한 데이터를 표시하여 식별할 수 있습니다. 사용자가 선택할 때 선택한 항목의 모든 세부 정보를 세부 정보 보기라고 하는 별도의 UI 조각에 표시할 수 있습니다. 이 정렬을 마스터/세부 정보 보기 또는 목록/세부 정보 보기라고도 합니다.
이 정렬은 두 가지 방법으로 구현할 수 있습니다. 세부 정보 보기를 ListViewSelectedItem 속성에 바인딩할 수 있습니다. 또는 CollectionViewSource를 사용할 수 있습니다. 이 경우, ListView과 세부 정보 보기를 CollectionViewSource에 바인딩합니다. 이 방법은 현재 선택한 항목을 처리합니다. 두 기술 모두 다음 섹션에 표시되며 둘 다 동일한 결과를 제공합니다(그림 참조).
메모
지금까지 이 항목에서는 {x:Bind} 태그 확장만 사용했습니다. 그러나 다음 섹션에 표시된 두 기술 모두 더 유연하지만 성능이 낮은 {Binding} 태그 확장이 필요합니다.
첫째, 다음은 SelectedItem 기술입니다. C# 애플리케이션의 경우, 마크업만 변경하면 됩니다.
<Window x:Class="Quickstart.MainWindow" ... >
<Grid>
<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
<ListView x:Name="recordingsListView" ItemsSource="{x:Bind ViewModel.Recordings}">
<ListView.ItemTemplate>
<DataTemplate x:DataType="local:Recording">
<StackPanel Orientation="Horizontal" Margin="6">
<SymbolIcon Symbol="Audio" Margin="0,0,12,0"/>
<StackPanel>
<TextBlock Text="{x:Bind CompositionName}"/>
</StackPanel>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<StackPanel DataContext="{Binding SelectedItem, ElementName=recordingsListView}"
Margin="0,24,0,0">
<TextBlock Text="{Binding ArtistName}"/>
<TextBlock Text="{Binding CompositionName}"/>
<TextBlock Text="{Binding ReleaseDateTime}"/>
</StackPanel>
</StackPanel>
</Grid>
</Window>
CollectionViewSource 기법의 경우 먼저 최상위 CollectionViewSource리소스로 Grid를 추가합니다.
<Grid.Resources>
<CollectionViewSource x:Name="RecordingsCollection" Source="{x:Bind ViewModel.Recordings}"/>
</Grid.Resources>
메모
WinUI의 Window 클래스에는 Resources 속성이 없습니다. 대신 최상위 CollectionViewSource(또는 Grid같은 다른 부모 UI 요소) 요소에 StackPanel 추가할 수 있습니다.
Page에서 작업하는 경우, CollectionViewSource을 Page.Resources에 추가할 수 있습니다.
그런 다음 ListView (더 이상 이름을 지정할 필요가 없음) 및 세부 정보 보기에서 CollectionViewSource를 사용하도록 바인딩을 조정합니다. 세부 정보 보기를 직접 CollectionViewSource바인딩하면 컬렉션 자체에서 경로를 찾을 수 없는 바인딩의 현재 항목에 바인딩하려는 것을 의미합니다. 모호성이 없는 한 CurrentItem 속성을 바인딩 경로로 지정할 필요가 없지만, 만약 모호성이 있다면 지정할 수 있습니다.
...
<ListView ItemsSource="{Binding Source={StaticResource RecordingsCollection}}">
...
<StackPanel DataContext="{Binding Source={StaticResource RecordingsCollection}}" ...>
...
각 사례에서 동일한 결과는 다음과 같습니다.
표시할 데이터 값 서식 지정 또는 변환
위의 렌더링에 문제가 있습니다. 속성은 ReleaseDateTime 날짜가 아니라 DateTime입니다. 따라서 필요한 것보다 더 정밀하게 표시됩니다. 한 가지 해결 방법은 Recording 클래스에 ReleaseDateTime.ToString("d")의 동등한 값을 반환하는 문자열 속성을 추가하는 것입니다. 이 속성 ReleaseDate 의 이름을 지정하면 날짜 및 시간이 아닌 날짜가 반환됩니다. 이름을 더 지정하면 ReleaseDateAsString 문자열이 반환됩니다.
보다 유연한 솔루션은 값 변환기를 사용하는 것입니다. 다음은 사용자 고유의 값 변환기를 작성하는 방법의 예입니다. Recording.cs 소스 코드 파일에 다음 코드를 추가합니다.
public class StringFormatter : Microsoft.UI.Xaml.Data.IValueConverter
{
// This converts the value object to the string to display.
// This will work with most simple types.
public object Convert(object value, Type targetType,
object parameter, string language)
{
// Retrieve the format string and use it to format the value.
string formatString = parameter as string;
if (!string.IsNullOrEmpty(formatString))
{
return string.Format(formatString, value);
}
// If the format string is null or empty, simply
// call ToString() on the value.
return value.ToString();
}
// No need to implement converting back on a one-way binding
public object ConvertBack(object value, Type targetType,
object parameter, string language)
{
throw new NotImplementedException();
}
}
이제 리소스에 StringFormatter의 인스턴스를 추가하고 ReleaseDateTime 속성을 표시하는 바인딩에서 TextBlock을 사용할 수 있습니다.
<Grid.Resources>
...
<local:StringFormatter x:Key="StringFormatterValueConverter"/>
</Grid.Resources>
...
<TextBlock Text="{Binding ReleaseDateTime,
Converter={StaticResource StringFormatterValueConverter},
ConverterParameter=Released: \{0:d\}}"/>
...
볼 수 있듯이 서식 유연성을 위해 태그는 변환기 매개 변수를 통해 형식 문자열을 변환기에 전달합니다. 이 항목에 표시된 코드 예제에서 C# 값 변환기는 해당 매개 변수를 사용합니다.
결과는 다음과 같습니다.
사용자 지정 서식 사용하여 날짜를 표시하는
바인딩과 x:Bind의 차이점
WinUI 앱에서 데이터 바인딩으로 작업할 때 두 가지 기본 바인딩 메커니즘이 Binding발생할 수 있습니다. x:Bind 둘 다 데이터 원본에 UI 요소를 연결하는 용도로 사용되지만 다음과 같은 고유한 차이점이 있습니다.
-
x:Bind: 컴파일 시간 검사, 성능 향상을 제공하며 강력한 형식입니다. 컴파일 시간에 데이터 구조를 알고 있는 시나리오에 적합합니다. -
Binding: 런타임 평가를 제공하며 컴파일 시간에 데이터 구조를 알 수 없는 경우와 같은 동적 시나리오에 더 유연합니다.
x:Bind에서 지원되지 않는 시나리오
강력하지만 x:Bind 특정 시나리오에서는 사용할 수 없습니다.
-
동적 데이터 구조: 컴파일 시간에 데이터 구조를 알 수 없는 경우 사용할
x:Bind수 없습니다. -
요소 간 바인딩:
x:Bind두 UI 요소 간의 직접 바인딩을 지원하지 않습니다. -
DataContext에 바인딩하는 것은DataContext를 부모 요소로부터 자동으로 상속받지 않습니다. -
양방향 바인딩
Mode=TwoWay: 지원되지만,x:Bind는 단방향이나 양방향 바인딩을 사용하는 경우 원본이 변경될 때 UI를 업데이트하려는 모든 속성에 대해INotifyPropertyChanged의 명시적인 구현이 필요합니다. 양방향 바인딩의 주요 차이점은 변경 내용도 UI에서 원본으로 다시 흐른다는 것입니다.
실제 예제 및 각 사용 시기에 대한 자세한 이해는 다음 항목을 참조하세요.
관련 콘텐츠
Windows developer