다음을 통해 공유


Windows 데이터 바인딩 개요

WinUI 앱의 데이터 바인딩을 사용하면 컨트롤을 데이터 원본에 효율적으로 연결할 수 있습니다. 컨트롤을 단일 항목 또는 항목 컬렉션에 바인딩하고, 항목 렌더링을 제어하고, 세부 정보 보기를 구현하고, 표시할 데이터의 서식을 지정하는 방법을 알아봅니다. 자세한 내용은 데이터 바인딩을 자세히 참조하세요.

필수 구성 요소

이 항목에서는 Windows 앱 SDK를 사용하여 기본 WinUI 앱을 만드는 방법을 알고 있다고 가정합니다. 첫 번째 WinUI 앱을 만드는 방법에 대한 지침은 WinUI 앱 만들기를 참조하세요.

프로젝트 만들기

WinUI 빈 앱, 패키지된 C# 프로젝트를 만듭니다. 이름을 "빠른 시작"으로 지정합니다.

단일 항목에 바인딩

모든 바인딩은 바인딩 대상과 바인딩 원본으로 구성됩니다. 일반적으로 대상은 컨트롤 또는 다른 UI 요소의 속성이며 원본은 클래스 인스턴스(데이터 모델 또는 뷰 모델)의 속성입니다. 이 예제에서는 컨트롤을 단일 항목에 바인딩하는 방법을 보여줍니다. TextTextBlock 속성이 대상입니다. 원본은 오디오 녹음을 나타내는 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();
    }
}

마지막 단계는 TextBlockViewModel.DefaultRecording.OneLineSummary 속성에 바인딩하는 것입니다.

<Window x:Class="Quickstart.MainWindow" ... >
    <Grid>
        <TextBlock Text="{x:Bind ViewModel.DefaultRecording.OneLineSummary}"
                   HorizontalAlignment="Center"
                   VerticalAlignment="Center"/>
    </Grid>
</Window>

결과는 다음과 같습니다.

단일 항목에 바인딩된 TextBlock을 보여 주는 WinUI 앱의 스크린샷

항목 컬렉션에 바인딩

일반적인 시나리오는 비즈니스 개체 컬렉션에 바인딩하는 것입니다. 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 기본 구현입니다.

목록 보기 1 바인딩

이 문제를 해결하려면 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>

리스트 뷰 바인딩 2

<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>

목록 보기를 바인딩 3

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에서 작업하는 경우, CollectionViewSourcePage.Resources에 추가할 수 있습니다.

그런 다음 ListView (더 이상 이름을 지정할 필요가 없음) 및 세부 정보 보기에서 CollectionViewSource를 사용하도록 바인딩을 조정합니다. 세부 정보 보기를 직접 CollectionViewSource바인딩하면 컬렉션 자체에서 경로를 찾을 수 없는 바인딩의 현재 항목에 바인딩하려는 것을 의미합니다. 모호성이 없는 한 CurrentItem 속성을 바인딩 경로로 지정할 필요가 없지만, 만약 모호성이 있다면 지정할 수 있습니다.

...
<ListView ItemsSource="{Binding Source={StaticResource RecordingsCollection}}">
...
<StackPanel DataContext="{Binding Source={StaticResource RecordingsCollection}}" ...>
...

각 사례에서 동일한 결과는 다음과 같습니다.

리스트 뷰 바인딩 4

표시할 데이터 값 서식 지정 또는 변환

위의 렌더링에 문제가 있습니다. 속성은 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에서 원본으로 다시 흐른다는 것입니다.

실제 예제 및 각 사용 시기에 대한 자세한 이해는 다음 항목을 참조하세요.