다음을 통해 공유


데이터 바인딩 심층 분석

중요 API

비고

이 항목에서는 데이터 바인딩 기능에 대해 자세히 설명합니다. 짧고 실용적인 소개는 데이터 바인딩 개요를 참조하세요.

이 항목에서는 Windows.UI.Xaml.Data 네임스페이스에 있는 API에 대한 데이터 바인딩에 대해 설명합니다.

데이터 바인딩은 앱의 UI에서 데이터를 표시하고 필요에 따라 해당 데이터와 동기화 상태를 유지하는 방법입니다. 데이터 바인딩을 사용하면 데이터의 문제를 UI와 분리할 수 있으며, 이로 인해 앱의 가독성, 테스트 가능성 및 유지 관리 효율성이 향상되고 더 간단한 개념적 모델이 생성됩니다.

데이터 바인딩을 사용하여 UI가 처음 표시되면 데이터 원본의 값을 표시하지만 해당 값의 변경 내용에 응답하지 않을 수 있습니다. 일회성 이라는 바인딩 모드이며 런타임 동안 변경되지 않는 값에 적합합니다. 또는 값을 "관찰"하고 변경 시 UI를 업데이트하도록 선택할 수 있습니다. 이 모드는 단방향이라고 하며 읽기 전용 데이터에 적합합니다. 궁극적으로 사용자가 UI의 값에 적용한 변경 내용이 자동으로 데이터 원본으로 다시 푸시되도록 관찰하고 업데이트하도록 선택할 수 있습니다. 이 모드는 양방향이라고 하며 읽기/쓰기 데이터에 적합합니다. 다음은 몇 가지 예입니다.

  • 일회성 모드를 사용하여 이미지를 현재 사용자의 사진에 바인딩할 수 있습니다.
  • 단방향 모드를 사용하여 ListView 를 신문 섹션별로 그룹화된 실시간 뉴스 기사 모음에 바인딩할 수 있습니다.
  • 양방향 모드를 사용하여 텍스트 상자를 양식의 고객 이름에 바인딩할 수 있습니다.

모드와 관계없이 두 종류의 바인딩이 있으며 둘 다 일반적으로 UI 태그에서 선언됩니다. {x:Bind} 태그 확장 또는 {Binding} 태그 확장을 사용하도록 선택할 수 있습니다. 동일한 UI 요소에서도 동일한 앱에서 두 항목의 혼합을 사용할 수도 있습니다. {x:Bind}는 Windows 10의 새로운 기능이며 성능이 향상되었습니다. 이 항목에 설명된 모든 세부 정보는 명시적으로 달리 말하지 않는 한 두 종류의 바인딩에 모두 적용됩니다.

{x:Bind}를 보여 주는 샘플 앱

{Binding}을(를) 보여 주는 샘플 앱

모든 바인딩에는 이러한 부분이 포함됩니다.

  • 바인딩 소스입니다. 바인딩에 대한 데이터의 원본이며 UI에 표시할 값이 있는 멤버가 있는 모든 클래스의 인스턴스일 수 있습니다.
  • 바인딩 대상입니다. 데이터를 표시하는 UI에서 FrameworkElementDependencyProperty입니다.
  • 바인딩 개체입니다. 원본에서 대상으로, 필요에 따라 대상에서 원본으로 데이터 값을 전송하는 요소입니다. 바인딩 개체는 {x:Bind} 또는 {Binding} 태그 확장에서 XAML 로드 시간에 만들어집니다.

다음 섹션에서는 바인딩 원본, 바인딩 대상 및 바인딩 개체를 자세히 살펴보겠습니다. 또한 HostViewModel이라는 클래스에 속하는 NextButtonText라는 문자열 속성에 단추의 콘텐츠를 바인딩하는 예제와 섹션을 연결합니다.

바인딩 원본

다음은 바인딩 소스로 사용할 수 있는 클래스의 매우 기본적인 구현입니다.

C++/WinRT를 사용하는 경우 아래 목록의 C++/WinRT 코드 예제와 같이 이름이 지정된 새 Midl 파일(.idl) 항목을 프로젝트에 추가합니다. 이러한 새 파일의 내용을 목록에 표시된 MIDL 3.0 코드로 바꾸고, 생성 HostViewModel.h 할 프로젝트를 빌드한 .cpp다음, 생성된 파일에 목록을 일치하도록 코드를 추가합니다. 생성된 파일 및 프로젝트에 복사하는 방법에 대한 자세한 내용은 XAML 컨트롤을 참조하고 C++/WinRT 속성에 바인딩합니다.

public class HostViewModel
{
    public HostViewModel()
    {
        this.NextButtonText = "Next";
    }

    public string NextButtonText { get; set; }
}
// HostViewModel.idl
namespace DataBindingInDepth
{
    runtimeclass HostViewModel
    {
        HostViewModel();
        String NextButtonText;
    }
}

// HostViewModel.h
// Implement the constructor like this, and add this field:
...
HostViewModel() : m_nextButtonText{ L"Next" } {}
...
private:
    std::wstring m_nextButtonText;
...

// HostViewModel.cpp
// Implement like this:
...
hstring HostViewModel::NextButtonText()
{
    return hstring{ m_nextButtonText };
}

void HostViewModel::NextButtonText(hstring const& value)
{
    m_nextButtonText = value;
}
...

HostViewModel 및 해당 속성 NextButtonText의 구현은 일회성 바인딩에만 적합합니다. 그러나 단방향 및 양방향 바인딩은 매우 일반적이며 이러한 종류의 바인딩에서는 바인딩 원본의 데이터 값 변경에 따라 UI가 자동으로 업데이트됩니다. 이러한 종류의 바인딩이 올바르게 작동하려면 바인딩 개체에 대한 바인딩 소스를 "관찰 가능"하게 만들어야 합니다. 따라서 이 예제에서 NextButtonText 속성에 단방향 또는 양방향 바인딩을 수행하려는 경우 런타임에 해당 속성 값에 대해 발생하는 모든 변경 내용을 바인딩 개체에서 관찰할 수 있어야 합니다.

이 작업을 수행하는 한 가지 방법은 DependencyObject에서 바인딩 소스를 나타내는 클래스를 파생시키고 DependencyProperty를 통해 데이터 값을 노출하는 것입니다. 이것이 FrameworkElement 를 관찰하는 방법입니다. FrameworkElements는 올바른 바인딩 원본입니다.

클래스를 관찰할 수 있도록 하는 보다 간단한 방법과 기본 클래스가 이미 있는 클래스에 필요한 방법은 System.ComponentModel.INotifyPropertyChanged를 구현하는 것입니다. 실제로 는 PropertyChanged라는 단일 이벤트를 구현하는 작업만 포함됩니다. HostViewModel을 사용하는 예제는 다음과 같습니다.

...
using System.ComponentModel;
using System.Runtime.CompilerServices;
...
public class HostViewModel : INotifyPropertyChanged
{
    private string nextButtonText;

    public event PropertyChangedEventHandler PropertyChanged = delegate { };

    public HostViewModel()
    {
        this.NextButtonText = "Next";
    }

    public string NextButtonText
    {
        get { return this.nextButtonText; }
        set
        {
            this.nextButtonText = value;
            this.OnPropertyChanged();
        }
    }

    public void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        // Raise the PropertyChanged event, passing the name of the property whose value has changed.
        this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}
// HostViewModel.idl
namespace DataBindingInDepth
{
    runtimeclass HostViewModel : Windows.UI.Xaml.Data.INotifyPropertyChanged
    {
        HostViewModel();
        String NextButtonText;
    }
}

// HostViewModel.h
// Add this field:
...
    winrt::event_token PropertyChanged(Windows::UI::Xaml::Data::PropertyChangedEventHandler const& handler);
    void PropertyChanged(winrt::event_token const& token) noexcept;

private:
    winrt::event<Windows::UI::Xaml::Data::PropertyChangedEventHandler> m_propertyChanged;
...

// HostViewModel.cpp
// Implement like this:
...
void HostViewModel::NextButtonText(hstring const& value)
{
    if (m_nextButtonText != value)
    {
        m_nextButtonText = value;
        m_propertyChanged(*this, Windows::UI::Xaml::Data::PropertyChangedEventArgs{ L"NextButtonText" });
    }
}

winrt::event_token HostViewModel::PropertyChanged(Windows::UI::Xaml::Data::PropertyChangedEventHandler const& handler)
{
    return m_propertyChanged.add(handler);
}

void HostViewModel::PropertyChanged(winrt::event_token const& token) noexcept
{
    m_propertyChanged.remove(token);
}
...

이제 NextButtonText 속성을 관찰할 수 있습니다. 해당 속성에 대한 단방향 또는 양방향 바인딩을 작성할 때(나중에 방법을 보여 드리겠습니다) 결과 바인딩 개체는 PropertyChanged 이벤트를 구독합니다. 해당 이벤트가 발생하면 바인딩 개체의 처리기는 변경된 속성의 이름을 포함하는 인수를 받습니다. 이것이 바인딩 개체가 다시 읽고 이동해야 하는 속성의 값을 파악하는 방법입니다.

위에 표시된 패턴을 여러 번 구현할 필요가 없으므로 C#을 사용하는 경우 QuizGame 샘플("Common" 폴더)에서 찾을 수 있는 BindableBase 기본 클래스에서 파생할 수 있습니다. 다음은 모양에 대한 예입니다.

public class HostViewModel : BindableBase
{
    private string nextButtonText;

    public HostViewModel()
    {
        this.NextButtonText = "Next";
    }

    public string NextButtonText
    {
        get { return this.nextButtonText; }
        set { this.SetProperty(ref this.nextButtonText, value); }
    }
}
// Your BindableBase base class should itself derive from Windows::UI::Xaml::DependencyObject. Then, in HostViewModel.idl, derive from BindableBase instead of implementing INotifyPropertyChanged.

비고

C++/WinRT의 경우 기본 클래스에서 파생되는 애플리케이션에서 선언하는 모든 런타임 클래스를 작성 가능한 클래스라고 합니다. 그리고 구성 가능한 클래스에 대한 제약 조건이 있습니다. 애플리케이션이 Visual Studio 및 Microsoft Store에서 제출의 유효성을 검사하는 데 사용하는 Windows 앱 인증 키트 테스트를 통과하려면(따라서 애플리케이션이 Microsoft Store에 성공적으로 수집되려면) 구성 가능한 클래스는 궁극적으로 Windows 기본 클래스에서 파생되어야 합니다. 즉, 상속 계층 구조의 루트에 있는 클래스는 Windows.* 네임스페이스에서 시작되는 형식이어야 합니다. 기본 클래스에서 런타임 클래스를 파생해야 하는 경우(예: 모든 뷰 모델에서 파생할 BindableBase 클래스를 구현하려면) Windows.UI.Xaml.DependencyObject에서 파생할 수 있습니다.

String.Empty 또는 null 인수를 사용하여 PropertyChanged 이벤트를 발생시키는 것은 개체의 모든 비 인덱서 속성을 다시 읽어야 했음을 나타냅니다. 특정 인덱서(인덱서가 인덱스 값인 경우)에 대해 "Item[indexer]" 인수를 사용하거나 모든 인덱서에 대해 "Item[]" 값을 사용하여 개체의 인덱서 속성이 변경되었음을 나타내는 이벤트를 발생할 수 있습니다.

바인딩 원본은 속성에 데이터가 포함된 단일 개체 또는 개체 컬렉션으로 처리할 수 있습니다. C# 및 Visual Basic 코드에서는 List(Of T) 를 구현하는 개체에 일회성 바인딩을 사용하여 런타임에 변경되지 않는 컬렉션을 표시할 수 있습니다. 관찰 가능한 컬렉션(항목이 컬렉션에 추가되고 컬렉션에서 제거되는 경우 관찰)의 경우 단방향으로 ObservableCollection(Of T) 에 바인딩합니다. C++/CX 코드에서는 관찰 가능한 컬렉션과 관찰할 수 없는 컬렉션 모두에 대해 Vector<T> 에 바인딩할 수 있으며 C++/WinRT에는 고유한 형식이 있습니다. 사용자 고유의 컬렉션 클래스에 바인딩하려면 다음 표의 지침을 사용합니다.

Scenario C# 및 VB(CLR) C++/WinRT C++/CX
개체에 바인딩합니다. 모든 개체일 수 있습니다. 모든 개체일 수 있습니다. 개체에는 BindableAttribute 가 있거나 ICustomPropertyProvider를 구현해야 합니다.
바인딩된 개체에서 속성 변경 알림을 가져옵니다. 개체 는 INotifyPropertyChanged를 구현해야 합니다. 개체 는 INotifyPropertyChanged를 구현해야 합니다. 개체 는 INotifyPropertyChanged를 구현해야 합니다.
컬렉션에 바인딩합니다. List(Of T) IInspectable 또는 IBindableObservableVectorIVector입니다. XAML 항목 컨트롤을 참조하세요. C++/WinRT를 사용하여 C++/WinRT 컬렉션컬렉션에 바인딩합니다. 벡터<T>
바인딩된 컬렉션에서 컬렉션 변경 알림을 가져옵니다. ObservableCollection(Of T) IInspectable의 IObservableVector입니다. 예를 들어 winrt::single_threaded_observable_vector<T>입니다. IObservableVector<T>. 벡터<T> 는 이 인터페이스를 구현합니다.
바인딩을 지원하는 컬렉션을 구현합니다. 목록(Of T)을 확장하거나 IList, IList(Of Object), IEnumerable 또는 IEnumerable(Of Object)을 구현합니다. 제네릭 IList(Of T)IEnumerable(Of T)에 대한 바인딩은 지원되지 않습니다. IInspectableIVector를 구현합니다. XAML 항목 컨트롤을 참조하세요. C++/WinRT를 사용하여 C++/WinRT 컬렉션컬렉션에 바인딩합니다. IBindableVector, IBindableIterable, IVector<Object^>, IIterable<Object^>, IVector<IInspectable*>또는 IIterable<IInspectable*>을 구현합니다. 제네릭 IVector<T>IIterable<T>에 대한 바인딩은 지원되지 않습니다.
컬렉션 변경 알림을 지원하는 컬렉션을 구현합니다. ObservableCollection(Of T)을 확장하거나(제네릭이 아닌) IListINotifyCollectionChanged를 구현합니다. IInspectable 또는 IBindableObservableVector의 IObservableVector를 구현합니다. IBindableVectorIBindableObservableVector를 구현합니다.
증분 로드를 지원하는 컬렉션을 구현합니다. ObservableCollection(Of T)을 확장하거나(제네릭이 아닌) IListINotifyCollectionChanged를 구현합니다. 또한 ISupportIncrementalLoading을 구현합니다. IInspectable 또는 IBindableObservableVector의 IObservableVector를 구현합니다. 또한 ISupportIncrementalLoading 구현 IBindableVector, IBindableObservableVectorISupportIncrementalLoading을 구현합니다.

증분 로드를 사용하여 목록 컨트롤을 임의로 큰 데이터 원본에 바인딩하고 고성능을 달성할 수 있습니다. 예를 들어 모든 결과를 한 번에 로드하지 않고도 목록 컨트롤을 Bing 이미지 쿼리 결과에 바인딩할 수 있습니다. 대신 일부 결과만 즉시 로드하고 필요에 따라 추가 결과를 로드합니다. 증분 로드를 지원하려면 컬렉션 변경 알림을 지원하는 데이터 원본에서 ISupportIncrementalLoading 을 구현해야 합니다. 데이터 바인딩 엔진이 더 많은 데이터를 요청하는 경우 데이터 원본은 적절한 요청을 수행하고 결과를 통합한 다음 UI를 업데이트하기 위해 적절한 알림을 보내야 합니다.

바인딩 대상

아래 두 예제에서 Button.Content 속성은 바인딩 대상이며 해당 값은 바인딩 개체를 선언하는 태그 확장으로 설정됩니다. 첫 번째 {x:Bind} 가 표시되고 {Binding}이 표시됩니다. 태그에서 바인딩을 선언하는 것이 일반적인 경우입니다(편리하고 읽기 가능하며 도구 가능). 그러나 태그를 방지하고 필요한 경우 대신 바인딩 클래스의 인스턴스를 명령적으로(프로그래밍 방식으로) 만들 수 있습니다.

<Button Content="{x:Bind ...}" ... />
<Button Content="{Binding ...}" ... />

C++/WinRT 또는 Visual C++ 구성 요소 확장(C++/CX)을 사용하는 경우 {Binding} 태그 확장을 사용하려는 런타임 클래스에 BindableAttribute 특성을 추가해야 합니다.

중요합니다

C++/WinRT를 사용하는 경우 Windows SDK 버전 10.0.17763.0(Windows 10, 버전 1809) 이상을 설치한 경우 BindableAttribute 특성을 사용할 수 있습니다. 이 특성이 없으면 {Binding} 태그 확장을 사용하려면 ICustomPropertyProviderICustomProperty 인터페이스를 구현해야 합니다.

{x:Bind}를 사용하여 선언된 바인딩 개체

{x:Bind} 태그를 작성하기 전에 수행해야 하는 한 단계가 있습니다. 태그 페이지를 나타내는 클래스에서 바인딩 소스 클래스를 노출해야 합니다. 이 경우 MainPage 페이지 클래스에 속성(이 경우 HostViewModel 형식)을 추가하여 이 작업을 수행합니다.

namespace DataBindingInDepth
{
    public sealed partial class MainPage : Page
    {
        public MainPage()
        {
            this.InitializeComponent();
            this.ViewModel = new HostViewModel();
        }
    
        public HostViewModel ViewModel { get; set; }
    }
}
// MainPage.idl
import "HostViewModel.idl";

namespace DataBindingInDepth
{
    runtimeclass MainPage : Windows.UI.Xaml.Controls.Page
    {
        MainPage();
        HostViewModel ViewModel{ get; };
    }
}

// MainPage.h
// Include a header, and add this field:
...
#include "HostViewModel.h"
...
    DataBindingInDepth::HostViewModel ViewModel();

private:
    DataBindingInDepth::HostViewModel m_viewModel{ nullptr };
...

// MainPage.cpp
// Implement like this:
...
MainPage::MainPage()
{
    InitializeComponent();

}

DataBindingInDepth::HostViewModel MainPage::ViewModel()
{
    return m_viewModel;
}
...

이렇게 하면 이제 바인딩 개체를 선언하는 태그를 자세히 살펴볼 수 있습니다. 아래 예제에서는 앞에서 "바인딩 대상" 섹션에서 사용한 것과 동일한 Button.Content 바인딩 대상을 사용하며 HostViewModel.NextButtonText 속성에 바인딩되는 것을 보여 줍니다.

<!-- MainPage.xaml -->
<Page x:Class="DataBindingInDepth.Mainpage" ... >
    <Button Content="{x:Bind Path=ViewModel.NextButtonText, Mode=OneWay}" ... />
</Page>

Path에 대해 지정하는 값을 확인 합니다. 이 값은 페이지 자체의 컨텍스트에서 해석되며, 이 경우 경로는 방금 MainPage 페이지에 추가한 ViewModel 속성을 참조하여 시작됩니다. 이 속성은 HostViewModel 인스턴스를 반환하므로 해당 개체에 점을 적용하여 HostViewModel.NextButtonText 속성에 액세스할 수 있습니다. 또한 {x:Bind} 기본값을 일회성으로 재정의하기 위해 모드를 지정합니다.

Path 속성은 중첩 속성, 연결된 속성, 정수 및 문자열 인덱서에 바인딩하기 위한 다양한 구문 옵션을 지원합니다. 자세한 내용은 속성 경로 구문을 참조하세요. 문자열 인덱서에 바인딩하면 ICustomPropertyProvider를 구현하지 않고도 동적 속성에 바인딩할 수 있습니다. 다른 설정은 {x:Bind} 태그 확장을 참조하세요.

HostViewModel.NextButtonText 속성이 실제로 관찰 가능함을 보여 주려면 단추에 Click 이벤트 처리기를 추가하고 HostViewModel.NextButtonText의 값을 업데이트합니다. 단추를 빌드, 실행 및 클릭하여 단추의 콘텐츠 업데이트 값을 확인합니다.

// MainPage.xaml.cs
private void Button_Click(object sender, RoutedEventArgs e)
{
    this.ViewModel.NextButtonText = "Updated Next button text";
}
// MainPage.cpp
void MainPage::ClickHandler(IInspectable const&, RoutedEventArgs const&)
{
    ViewModel().NextButtonText(L"Updated Next button text");
}

비고

TextBox.Text에 대한 변경 내용은 TextBox가 포커스를 잃을 때 양방향 바인딩된 원본으로 전송되며, 모든 사용자 키 입력 후에는 전송되지 않습니다.

DataTemplate 및 x:DataType

DataTemplate 내에서(항목 템플릿, 콘텐츠 템플릿 또는 헤더 템플릿으로 사용되든) Path 값은 페이지의 컨텍스트에서 해석되지 않고 템플릿을 지정하는 데이터 개체의 컨텍스트에서 해석됩니다. 데이터 템플릿에서 {x:Bind}을(를) 사용하여 컴파일 시간에 바인딩의 유효성을 검사하고 효율적인 코드를 생성할 수 있도록 DataTemplatex:DataType을 사용하여 데이터 개체의 형식을 선언해야 합니다. 아래에 제공된 예제는 SampleDataGroup 개체 컬렉션에 바인딩된 항목 컨트롤의 ItemTemplate으로 사용할 수 있습니다.

<DataTemplate x:Key="SimpleItemTemplate" x:DataType="data:SampleDataGroup">
    <StackPanel Orientation="Vertical" Height="50">
      <TextBlock Text="{x:Bind Title}"/>
      <TextBlock Text="{x:Bind Description}"/>
    </StackPanel>
  </DataTemplate>

Path의 약한 형식의 개체

Title이라는 문자열 속성을 구현하는 SampleDataGroup이라는 형식이 있는 경우를 예로 들어 보세요. 또한 MainPage.SampleDataGroupAsObject 속성이 있는데, 이 속성은 형식 개체이지만 실제로 SampleDataGroup의 인스턴스를 반환합니다. 형식 개체에서 Title 속성을 찾을 수 없으므로 바인딩 <TextBlock Text="{x:Bind SampleDataGroupAsObject.Title}"/> 으로 인해 컴파일 오류가 발생합니다. 이에 대한 해결 방법은 다음과 <TextBlock Text="{x:Bind ((data:SampleDataGroup)SampleDataGroupAsObject).Title}"/>같이 경로 구문에 캐스트를 추가하는 것입니다. 다음은 Element가 개체로 선언되었지만 실제로 TextBlock <TextBlock Text="{x:Bind Element.Text}"/>인 또 다른 예입니다. 그리고 캐스트는 문제를 해결 : <TextBlock Text="{x:Bind ((TextBlock)Element).Text}"/>.

데이터가 비동기적으로 로드되는 경우

{x:Bind}를 지원하는 코드는 컴파일 타임에 페이지의 부분 클래스에서 생성됩니다. 이러한 파일은 (C#의 경우) obj같은 이름으로 폴더에서 <view name>.g.cs 찾을 수 있습니다. 생성된 코드에는 페이지의 Loading 이벤트에 대한 처리기가 포함되며, 해당 처리기는 페이지의 바인딩을 나타내는 생성된 클래스에서 Initialize 메서드를 호출합니다. 다시 초기화Update 를 호출하여 바인딩 원본과 대상 간에 데이터 이동을 시작합니다. 로드 는 페이지 또는 사용자 컨트롤의 첫 번째 측정값 전달 바로 전에 발생합니다. 따라서 데이터가 비동기적으로 로드되는 경우 Initialize 가 호출될 때까지 준비되지 않을 수 있습니다. 따라서 데이터를 로드한 후에는 호출 this.Bindings.Update();을 통해 일회성 바인딩을 강제로 초기화할 수 있습니다. 비동기적으로 로드된 데이터에 대해 일회성 바인딩만 필요한 경우 단방향 바인딩을 사용하고 변경 내용을 수신 대기하는 것보다 이러한 방식으로 초기화하는 것이 훨씬 저렴합니다. 데이터가 세분화된 변경을 거치지 않고 특정 작업의 일부로 업데이트될 가능성이 있는 경우 바인딩을 한 번 만들고 업데이트 호출을 통해 언제든지 수동 업데이트를 적용할 수 있습니다.

비고

{x:Bind} 는 JSON 개체의 사전 구조 탐색이나 오리 입력과 같은 런타임에 바인딩된 시나리오에 적합하지 않습니다. "오리 입력"은 속성 이름에 어휘 일치를 기반으로 입력의 약한 형태입니다 (에서와 같이, "그것은 걷고, 수영하고, 오리처럼 quacks 경우, 그것은 오리입니다"). 오리 입력을 사용하면 Age 속성에 대한 바인딩이 Person 또는 Wine 개체에 동일하게 만족됩니다(이러한 유형에 각각 Age 속성이 있다고 가정). 이러한 시나리오에서는 {Binding} 태그 확장을 사용합니다.

{Binding}을(를) 사용하여 선언된 바인딩 개체

C++/WinRT 또는 Visual C++ 구성 요소 확장(C++/CX)을 사용하는 경우 {Binding} 태그 확장을 사용하려면 바인딩하려는 런타임 클래스에 BindableAttribute 특성을 추가해야 합니다. {x:Bind}를 사용하려면 해당 특성이 필요하지 않습니다.

// HostViewModel.idl
// Add this attribute:
[Windows.UI.Xaml.Data.Bindable]
runtimeclass HostViewModel : Windows.UI.Xaml.Data.INotifyPropertyChanged
...

중요합니다

C++/WinRT를 사용하는 경우 Windows SDK 버전 10.0.17763.0(Windows 10, 버전 1809) 이상을 설치한 경우 BindableAttribute 특성을 사용할 수 있습니다. 이 특성이 없으면 {Binding} 태그 확장을 사용하려면 ICustomPropertyProviderICustomProperty 인터페이스를 구현해야 합니다.

{Binding} 은 기본적으로 태그 페이지의 DataContext 에 바인딩한다고 가정합니다. 따라서 페이지의 DataContext 를 바인딩 소스 클래스의 인스턴스로 설정합니다(이 경우 HostViewModel 형식). 아래 예제에서는 바인딩 개체를 선언하는 태그를 보여 줍니다. 앞에서 "바인딩 대상" 섹션에서 사용한 것과 동일한 Button.Content 바인딩 대상을 사용하며 HostViewModel.NextButtonText 속성에 바인딩합니다.

<Page xmlns:viewmodel="using:DataBindingInDepth" ... >
    <Page.DataContext>
        <viewmodel:HostViewModel x:Name="viewModelInDataContext"/>
    </Page.DataContext>
    ...
    <Button Content="{Binding Path=NextButtonText}" ... />
</Page>
// MainPage.xaml.cs
private void Button_Click(object sender, RoutedEventArgs e)
{
    this.viewModelInDataContext.NextButtonText = "Updated Next button text";
}
// MainPage.cpp
void MainPage::ClickHandler(IInspectable const&, RoutedEventArgs const&)
{
    viewModelInDataContext().NextButtonText(L"Updated Next button text");
}

Path에 대해 지정하는 값을 확인 합니다. 이 값은 이 예제에서 HostViewModel 인스턴스로 설정된 페이지의 DataContext 컨텍스트에서 해석됩니다. 경로는 HostViewModel.NextButtonText 속성을 참조합니다. 여기서는 단방향 {Binding} 기본값이 작동하므로 모드를 생략할 수 있습니다.

UI 요소에 대한 DataContext 의 기본값은 해당 부모의 상속된 값입니다. 물론 DataContext 를 명시적으로 설정하여 기본값을 재정의할 수 있으며, 이는 기본적으로 자식에 의해 상속됩니다. 동일한 원본을 사용하는 여러 바인딩을 사용하려는 경우 요소에 DataContext 를 명시적으로 설정하는 것이 유용합니다.

바인딩 개체에는 원본 속성이 있으며, 기본값은 바인딩이 선언된 UI 요소의 DataContext 입니다. 바인딩에서 Source, RelativeSource 또는 ElementName 을 명시적으로 설정하여 이 기본값을 재정의할 수 있습니다(자세한 내용은 {Binding} 참조).

DataTemplate 내에서 DataContext는 템플릿을 만드는 데이터 개체로 자동으로 설정됩니다. 아래에 제공된 예제는 Title 및 Description이라는 문자열 속성이 있는 모든 형식의 컬렉션에 바인딩된 항목 컨트롤의 ItemTemplate으로 사용할 수 있습니다.

<DataTemplate x:Key="SimpleItemTemplate">
    <StackPanel Orientation="Vertical" Height="50">
      <TextBlock Text="{Binding Title}"/>
      <TextBlock Text="{Binding Description"/>
    </StackPanel>
  </DataTemplate>

비고

기본적으로 TextBox.Text 에 대한 변경 내용은 TextBox 에 포커스가 끊어지면 양방향 바인딩된 원본으로 전송됩니다. 모든 사용자 키 입력 후에 변경 내용을 보내도록 하려면 태그의 바인딩에서 UpdateSourceTriggerPropertyChanged 로 설정합니다. UpdateSourceTrigger를 Explicit로 설정하여 변경 내용이 원본으로 전송되는 시기를 완전히 제어할 수도 있습니다. 그런 다음 텍스트 상자(일반적으로 TextBox.TextChanged)에서 이벤트를 처리하고, 대상에서 GetBindingExpression을 호출하여 BindingExpression 개체를 가져오고, 마지막으로 BindingExpression.UpdateSource 를 호출하여 데이터 원본을 프로그래밍 방식으로 업데이트합니다.

Path 속성은 중첩 속성, 연결된 속성, 정수 및 문자열 인덱서에 바인딩하기 위한 다양한 구문 옵션을 지원합니다. 자세한 내용은 속성 경로 구문을 참조하세요. 문자열 인덱서에 바인딩하면 ICustomPropertyProvider를 구현하지 않고도 동적 속성에 바인딩할 수 있습니다. ElementName 속성은 요소-요소 바인딩에 유용합니다. RelativeSource 속성에는 여러 가지 용도가 있으며, 그 중 하나는 ControlTemplate 내의 템플릿 바인딩에 대한 보다 강력한 대안입니다. 다른 설정은 {Binding} 태그 확장Binding 클래스를 참조하세요.

원본과 대상이 동일한 형식이 아니면 어떻게 해야 할까요?

부울 속성의 값을 기반으로 UI 요소의 표시 여부를 제어하거나 숫자 값의 범위 또는 추세 함수인 색으로 UI 요소를 렌더링하려는 경우 또는 문자열을 예상하는 UI 요소 속성에 날짜 및/또는 시간 값을 표시하려는 경우 그런 다음 값을 한 형식에서 다른 형식으로 변환해야 합니다. 올바른 솔루션이 바인딩 소스 클래스에서 올바른 형식의 다른 속성을 노출하고 변환 논리를 캡슐화하고 테스트 가능한 상태로 유지하는 경우가 있습니다. 그러나 원본 및 대상 속성의 많은 수 또는 큰 조합이 있는 경우 유연하거나 확장성이 없습니다. 이 경우 다음과 같은 몇 가지 옵션이 있습니다.

  • {x:Bind}를 사용하는 경우 함수에 직접 바인딩하여 변환을 수행할 수 있습니다.
  • 또는 변환을 수행하도록 설계된 개체인 값 변환기를 지정할 수 있습니다.

값 변환기

다음은 DateTime 값을 월을 포함하는 문자열 값으로 변환하는 일회용 또는 단방향 바인딩에 적합한 값 변환기입니다. 이 클래스는 IValueConverter를 구현합니다.

public class DateToStringConverter : IValueConverter
{
    // Define the Convert method to convert a DateTime value to 
    // a month string.
    public object Convert(object value, Type targetType, 
        object parameter, string language)
    {
        // value is the data from the source object.
        DateTime thisdate = (DateTime)value;
        int monthnum = thisdate.Month;
        string month;
        switch (monthnum)
        {
            case 1:
                month = "January";
                break;
            case 2:
                month = "February";
                break;
            default:
                month = "Month not found";
                break;
        }
        // Return the value to pass to the target.
        return month;
    }

    // ConvertBack is not implemented for a OneWay binding.
    public object ConvertBack(object value, Type targetType, 
        object parameter, string language)
    {
        throw new NotImplementedException();
    }
}
// See the "Formatting or converting data values for display" section in the "Data binding overview" topic.

바인딩 개체 태그에서 해당 값 변환기를 사용하는 방법은 다음과 같습니다.

<UserControl.Resources>
  <local:DateToStringConverter x:Key="Converter1"/>
</UserControl.Resources>
...
<TextBlock Grid.Column="0" 
  Text="{x:Bind ViewModel.Month, Converter={StaticResource Converter1}}"/>
<TextBlock Grid.Column="0" 
  Text="{Binding Month, Converter={StaticResource Converter1}}"/>

바인딩에 대해 Converter 매개 변수가 정의된 경우 바인딩 엔진은 Convert 및 ConvertBack 메서드를 호출합니다. 원본에서 데이터가 전달되면 바인딩 엔진은 Convert를 호출하고 반환된 데이터를 대상에 전달합니다. 양방향 바인딩의 경우 대상에서 데이터가 전달되면 바인딩 엔진은 ConvertBack을 호출하고 반환된 데이터를 원본에 전달합니다.

변환기에서는 변환에 사용할 언어를 지정할 수 있는 ConverterLanguage와 변환 논리에 대한 매개 변수를 전달할 수 있는 ConverterParameter 등의 선택적 매개 변수도 있습니다. 변환기 매개 변수를 사용하는 예제는 IValueConverter를 참조하세요.

비고

변환에 오류가 있는 경우 예외를 throw하지 마세요. 대신 DependencyProperty.UnsetValue를 반환하여 데이터 전송을 중지합니다.

바인딩 소스를 확인할 수 없을 때마다 사용할 기본값을 표시하려면 태그의 바인딩 개체에 FallbackValue 속성을 설정합니다. 변환 및 서식 지정 오류를 처리하는 데 유용합니다. 또한 다른 유형의 바인딩된 컬렉션에 있는 모든 개체에 존재하지 않을 수 있는 소스 속성에 바인딩하는 것도 유용합니다.

텍스트 컨트롤을 문자열이 아닌 값에 바인딩하는 경우 데이터 바인딩 엔진은 값을 문자열로 변환합니다. 값이 참조 형식인 경우 데이터 바인딩 엔진은 ICustomPropertyProvider.GetStringRepresentation 또는 IStringable.ToString 을 호출하여 문자열 값을 검색하고, 그렇지 않으면 Object.ToString을 호출합니다. 그러나 바인딩 엔진은 기본 클래스 구현을 숨기는 ToString 구현을 무시합니다. 서브클래스 구현은 기본 클래스 ToString 메서드를 대신 재정의해야 합니다. 마찬가지로, 네이티브 언어에서 모든 관리되는 개체 는 ICustomPropertyProviderIStringable을 구현하는 것처럼 보입니다. 그러나 GetStringRepresentationIStringable.ToString 에 대한 모든 호출은 Object.ToString 또는 해당 메서드의 재정의로 라우팅되며 기본 클래스 구현을 숨기는 새 ToString 구현으로 라우팅되지 않습니다.

비고

Windows 10 버전 1607부터 XAML 프레임워크는 기본 제공 부울-표시 변환기를 제공합니다. 변환기는 Visible 열거형 값에 true를 매핑하고 falseCollapsed에 매핑하므로 변환기를 만들지 않고 Visibility 속성을 부울에 바인딩할 수 있습니다. 기본 제공 변환기를 사용하려면 앱의 최소 대상 SDK 버전은 14393 이상이어야 합니다. 앱이 이전 버전의 Windows 10을 대상으로 하는 경우에는 사용할 수 없습니다. 대상 버전에 대한 자세한 내용은 버전 적응 코드를 참조하세요.

{x:Bind}의 함수 바인딩

{x:Bind}를 사용하면 바인딩 경로의 마지막 단계가 함수가 될 수 있습니다. 변환을 수행하고 둘 이상의 속성에 의존하는 바인딩을 수행하는 데 사용할 수 있습니다. x:Bind의 함수 참조

요소-요소 바인딩

한 XAML 요소의 속성을 다른 XAML 요소의 속성에 바인딩할 수 있습니다. 태그에서 표시되는 방식의 예는 다음과 같습니다.

<TextBox x:Name="myTextBox" />
<TextBlock Text="{x:Bind myTextBox.Text, Mode=OneWay}" />

중요합니다

C++/WinRT를 사용하는 요소-요소 바인딩에 필요한 워크플로는 요소-요소 바인딩을 참조하세요.

{x:Bind}가 있는 리소스 사전

{x:Bind} 태그 확장은 코드 생성에 따라 달라지므로 생성된 코드를 초기화하기 위해 InitializeComponent를 호출하는 생성자가 포함된 코드 숨김 파일이 필요합니다. 파일 이름을 참조하는 대신 해당 형식을 인스턴스화하여( InitializeComponent 가 호출되도록) 리소스 사전을 다시 사용합니다. 다음은 기존 리소스 사전이 있고 {x:Bind}를 사용하려는 경우 수행할 작업의 예입니다.

TemplatesResourceDictionary.xaml

<ResourceDictionary
    x:Class="ExampleNamespace.TemplatesResourceDictionary"
    .....
    xmlns:examplenamespace="using:ExampleNamespace">
    
    <DataTemplate x:Key="EmployeeTemplate" x:DataType="examplenamespace:IEmployee">
        <Grid>
            <TextBlock Text="{x:Bind Name}"/>
        </Grid>
    </DataTemplate>
</ResourceDictionary>

TemplatesResourceDictionary.xaml.cs

using Windows.UI.Xaml.Data;
 
namespace ExampleNamespace
{
    public partial class TemplatesResourceDictionary
    {
        public TemplatesResourceDictionary()
        {
            InitializeComponent();
        }
    }
}

MainPage.xaml

<Page x:Class="ExampleNamespace.MainPage"
    ....
    xmlns:examplenamespace="using:ExampleNamespace">

    <Page.Resources>
        <ResourceDictionary>
            .... 
            <ResourceDictionary.MergedDictionaries>
                <examplenamespace:TemplatesResourceDictionary/>
            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>
    </Page.Resources>
</Page>

재사용 가능한 스타일에서 {x:Bind} 및 {Binding} 혼합

이전 예제에서는 DataTemplates에서 {x:Bind}를 사용하여 보여 주지만 {x:Bind} 및 {Binding} 태그 확장을 결합하는 재사용 가능한 스타일을 만들 수도 있습니다. 이는 {x:Bind}를 사용하여 컴파일 시간 알려진 값에 일부 속성을 바인딩하고 다른 속성을 {Binding}을 사용하여 런타임 DataContext 값에 바인딩하려는 경우에 유용합니다.

다음은 두 바인딩 방법을 모두 사용하는 재사용 가능한 단추 스타일을 만드는 방법을 보여 주는 예제입니다.

TemplatesResourceDictionary.xaml

<ResourceDictionary
    x:Class="ExampleNamespace.TemplatesResourceDictionary"
    .....
    xmlns:examplenamespace="using:ExampleNamespace">
    
    <!-- DataTemplate using x:Bind -->
    <DataTemplate x:Key="EmployeeTemplate" x:DataType="examplenamespace:IEmployee">
        <Grid>
            <TextBlock Text="{x:Bind Name}"/>
        </Grid>
    </DataTemplate>
    
    <!-- Style that mixes x:Bind and Binding -->
    <Style x:Key="CustomButtonStyle" TargetType="Button">
        <Setter Property="Background" Value="{Binding ButtonBackgroundBrush}"/>
        <Setter Property="Foreground" Value="{Binding ButtonForegroundBrush}"/>
        <Setter Property="FontSize" Value="16"/>
        <Setter Property="Margin" Value="4"/>
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="Button">
                    <Border x:Name="RootBorder"
                            Background="{TemplateBinding Background}"
                            BorderBrush="{TemplateBinding BorderBrush}"
                            BorderThickness="{TemplateBinding BorderThickness}"
                            CornerRadius="4">
                        <StackPanel Orientation="Horizontal" 
                                    HorizontalAlignment="Center"
                                    VerticalAlignment="Center">
                            <!-- x:Bind to a static property or page-level property -->
                            <Ellipse Width="8" Height="8" 
                                     Fill="{x:Bind DefaultIndicatorBrush}" 
                                     Margin="0,0,8,0"/>
                            <!-- Binding to DataContext -->
                            <ContentPresenter x:Name="ContentPresenter"
                                              Content="{TemplateBinding Content}"
                                              Foreground="{TemplateBinding Foreground}"
                                              FontSize="{TemplateBinding FontSize}"/>
                        </StackPanel>
                        <VisualStateManager.VisualStateGroups>
                            <VisualStateGroup x:Name="CommonStates">
                                <VisualState x:Name="Normal"/>
                                <VisualState x:Name="PointerOver">
                                    <VisualState.Setters>
                                        <!-- Binding to DataContext for hover color -->
                                        <Setter Target="RootBorder.Background" 
                                                Value="{Binding ButtonHoverBrush}"/>
                                    </VisualState.Setters>
                                </VisualState>
                                <VisualState x:Name="Pressed">
                                    <VisualState.Setters>
                                        <!-- x:Bind to a compile-time known resource -->
                                        <Setter Target="RootBorder.Background" 
                                                Value="{x:Bind DefaultPressedBrush}"/>
                                    </VisualState.Setters>
                                </VisualState>
                            </VisualStateGroup>
                        </VisualStateManager.VisualStateGroups>
                    </Border>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</ResourceDictionary>

TemplatesResourceDictionary.xaml.cs

using Windows.UI;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Media;
 
namespace ExampleNamespace
{
    public partial class TemplatesResourceDictionary
    {
        public TemplatesResourceDictionary()
        {
            InitializeComponent();
        }
        
        // Properties for x:Bind - these are compile-time bound
        public SolidColorBrush DefaultIndicatorBrush { get; } = 
            new SolidColorBrush(Colors.Green);
            
        public SolidColorBrush DefaultPressedBrush { get; } = 
            new SolidColorBrush(Colors.DarkGray);
    }
}

런타임 값을 제공하는 ViewModel이 있는 MainPage.xaml의 사용법:

<Page x:Class="ExampleNamespace.MainPage"
    ....
    xmlns:examplenamespace="using:ExampleNamespace">

    <Page.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <examplenamespace:TemplatesResourceDictionary/>
            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>
    </Page.Resources>
    
    <Page.DataContext>
        <examplenamespace:ButtonThemeViewModel/>
    </Page.DataContext>

    <StackPanel Margin="20">
        <!-- This button uses the mixed binding style -->
        <Button Content="Save" Style="{StaticResource CustomButtonStyle}"/>
        <Button Content="Cancel" Style="{StaticResource CustomButtonStyle}"/>
    </StackPanel>
</Page>

ButtonThemeViewModel.cs(런타임 바인딩 값을 제공하는 DataContext):

using System.ComponentModel;
using Windows.UI;
using Windows.UI.Xaml.Media;

namespace ExampleNamespace
{
    public class ButtonThemeViewModel : INotifyPropertyChanged
    {
        private SolidColorBrush _buttonBackgroundBrush = new SolidColorBrush(Colors.LightBlue);
        private SolidColorBrush _buttonForegroundBrush = new SolidColorBrush(Colors.DarkBlue);
        private SolidColorBrush _buttonHoverBrush = new SolidColorBrush(Colors.LightCyan);

        public SolidColorBrush ButtonBackgroundBrush
        {
            get => _buttonBackgroundBrush;
            set
            {
                _buttonBackgroundBrush = value;
                PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(ButtonBackgroundBrush)));
            }
        }

        public SolidColorBrush ButtonForegroundBrush
        {
            get => _buttonForegroundBrush;
            set
            {
                _buttonForegroundBrush = value;
                PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(ButtonForegroundBrush)));
            }
        }

        public SolidColorBrush ButtonHoverBrush
        {
            get => _buttonHoverBrush;
            set
            {
                _buttonHoverBrush = value;
                PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(ButtonHoverBrush)));
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;
    }
}

이 예제에서:

  • {Binding} 은 DataContext(ButtonBackgroundBrush, ButtonForegroundBrush, ButtonHoverBrush)에 의존하는 속성에 사용됩니다.
  • {x:Bind} 는 컴파일 시간이 알려지고 ResourceDictionary 자체에 속하는 속성에 사용됩니다(DefaultIndicatorBrush, DefaultPressedBrush).
  • 스타일을 다시 사용할 수 있으며 모든 단추에 적용할 수 있습니다.
  • 런타임 테마는 DataContext를 통해 가능하지만 정적 요소에 대한 {x:Bind}의 성능을 활용할 수 있습니다.

이벤트 바인딩 및 ICommand

{x:Bind} 는 이벤트 바인딩이라는 기능을 지원합니다. 이 기능을 사용하면 바인딩을 사용하여 이벤트에 대한 처리기를 지정할 수 있습니다. 이 옵션은 코드 숨김 파일의 메서드를 사용하여 이벤트를 처리하는 추가 옵션입니다. MainPage 클래스에 RootFrame 속성이 있다고 가정해 보겠습니다.

public sealed partial class MainPage : Page
{
    ...
    public Frame RootFrame { get { return Window.Current.Content as Frame; } }
}

그런 다음 단추의 Click 이벤트를 다음과 같이 RootFrame 속성에서 반환된 Frame 개체의 메서드에 바인딩할 수 있습니다. 또한 단추의 IsEnabled 속성을 동일한 Frame의 다른 멤버에 바인딩합니다.

<AppBarButton Icon="Forward" IsCompact="True"
IsEnabled="{x:Bind RootFrame.CanGoForward, Mode=OneWay}"
Click="{x:Bind RootFrame.GoForward}"/>

오버로드된 메서드는 이 기술을 사용하여 이벤트를 처리하는 데 사용할 수 없습니다. 또한 이벤트를 처리하는 메서드에 매개 변수가 있는 경우 이벤트의 모든 매개 변수 형식에서 각각 할당할 수 있어야 합니다. 이 경우 Frame.GoForward 는 오버로드되지 않으며 매개 변수가 없습니다(그러나 두 개의 개체 매개 변수를 사용한 경우에도 여전히 유효합니다). 그러나 Frame.GoBack 은 오버로드되므로 이 기술을 사용하여 해당 메서드를 사용할 수 없습니다.

이벤트 바인딩 기술은 명령을 구현하고 사용하는 것과 유사합니다(명령은 ICommand 인터페이스를 구현하는 개체를 반환하는 속성임). {x:Bind}{Binding} 모두 명령으로 작동합니다. 명령 패턴을 여러 번 구현할 필요가 없도록 QuizGame 샘플("Common" 폴더)에서 찾을 수 있는 DelegateCommand 도우미 클래스를 사용할 수 있습니다.

폴더 또는 파일 컬렉션에 바인딩

Windows.Storage 네임스페이스의 API를 사용하여 폴더 및 파일 데이터를 검색할 수 있습니다. 그러나 다양한 GetFilesAsync, GetFoldersAsyncGetItemsAsync 메서드는 목록 컨트롤에 바인딩하는 데 적합한 값을 반환하지 않습니다. 대신 FileInformationFactory 클래스의 GetVirtualizedFilesVector, GetVirtualizedFoldersVectorGetVirtualizedItemsVector 메서드의 반환 값에 바인딩해야 합니다. StorageDataSource 및 GetVirtualizedFilesVector 샘플의 다음 코드 예제는 일반적인 사용 패턴을 보여 줍니다. 앱 패키지 매니페스트에서 picturesLibrary 기능을 선언하고 사진 라이브러리 폴더에 그림이 있는지 확인해야 합니다.

protected override void OnNavigatedTo(NavigationEventArgs e)
{
    var library = Windows.Storage.KnownFolders.PicturesLibrary;
    var queryOptions = new Windows.Storage.Search.QueryOptions();
    queryOptions.FolderDepth = Windows.Storage.Search.FolderDepth.Deep;
    queryOptions.IndexerOption = Windows.Storage.Search.IndexerOption.UseIndexerWhenAvailable;

    var fileQuery = library.CreateFileQueryWithOptions(queryOptions);

    var fif = new Windows.Storage.BulkAccess.FileInformationFactory(
        fileQuery,
        Windows.Storage.FileProperties.ThumbnailMode.PicturesView,
        190,
        Windows.Storage.FileProperties.ThumbnailOptions.UseCurrentScale,
        false
        );

    var dataSource = fif.GetVirtualizedFilesVector();
    this.PicturesListView.ItemsSource = dataSource;
}

일반적으로 이 방법을 사용하여 파일 및 폴더 정보의 읽기 전용 보기를 만듭니다. 파일 및 폴더 속성에 대한 양방향 바인딩을 만들 수 있습니다. 예를 들어 사용자가 음악 보기에서 노래를 평가하도록 할 수 있습니다. 그러나 적절한 SavePropertiesAsync 메서드(예: MusicProperties.SavePropertiesAsync)를 호출할 때까지 변경 내용은 유지되지 않습니다. 선택 재설정을 트리거하므로 항목이 포커스를 잃을 때 변경 내용을 커밋해야 합니다.

이 기술을 사용하는 양방향 바인딩은 음악과 같은 인덱싱된 위치에서만 작동합니다. FolderInformation.GetIndexedStateAsync 메서드를 호출하여 위치가 인덱싱되는지 여부를 확인할 수 있습니다.

또한 가상화된 벡터는 값을 채우기 전에 일부 항목에 대해 null 을 반환할 수 있습니다. 예를 들어 가상화된 벡터에 바인딩된 목록 컨트롤의 SelectedItem 값을 사용하거나 SelectedIndex를 대신 사용하기 전에 null을 확인해야 합니다.

키로 그룹화된 데이터에 바인딩

항목의 플랫 컬렉션(예: BookSku 클래스가 나타내는 책)을 가져와서 공통 속성(예: BookSku.AuthorName 속성)을 키로 사용하여 항목을 그룹화하면 결과를 그룹화된 데이터라고 합니다. 데이터를 그룹화하면 더 이상 플랫 컬렉션이 아닙니다. 그룹화된 데이터는 각 그룹 개체가 있는 그룹 개체의 컬렉션입니다.

  • 키 및
  • 속성이 해당 키와 일치하는 항목의 컬렉션입니다.

책 예제를 다시 실행하기 위해 책을 저자 이름으로 그룹화하면 각 그룹이 있는 저자 이름 그룹의 컬렉션이 생성됩니다.

  • 작성자 이름인 키 및
  • AuthorName 속성이 그룹의 키와 일치하는 BookSku의 컬렉션입니다.

일반적으로 컬렉션을 표시하려면 항목 컨트롤의 ItemsSource (예: ListView 또는 GridView)를 컬렉션을 반환하는 속성에 직접 바인딩합니다. 항목의 플랫 컬렉션인 경우 특별한 작업을 수행할 필요가 없습니다. 그러나 그룹화된 데이터에 바인딩할 때처럼 그룹 개체의 컬렉션인 경우 항목 컨트롤과 바인딩 원본 사이에 있는 CollectionViewSource 라는 중간 개체의 서비스가 필요합니다. 그룹화된 데이터를 반환하는 속성에 CollectionViewSource 를 바인딩하고 CollectionViewSource에 항목 컨트롤을 바인딩합니다. CollectionViewSource의 추가 값 추가는 현재 항목을 추적하므로 둘 이상의 항목 컨트롤을 모두 동일한 CollectionViewSource에 바인딩하여 동기화된 상태로 유지할 수 있다는 것입니다. CollectionViewSource.View 속성에서 반환된 개체의 ICollectionView.CurrentItem 속성을 통해 프로그래밍 방식으로 현재 항목에 액세스할 수도 있습니다.

CollectionViewSource의 그룹화 기능을 활성화하려면 IsSourceGroupedtrue로 설정합니다. ItemsPath 속성도 설정해야 하는지 여부는 그룹 개체를 작성하는 방법에 따라 달라집니다. 그룹 개체를 작성하는 방법에는 "is-a-group" 패턴과 "has-a-group" 패턴의 두 가지가 있습니다. "is-a-group" 패턴에서 그룹 개체는 컬렉션 형식(예: List<T>)에서 파생되므로 그룹 개체는 실제로 항목 그룹입니다. 이 패턴을 사용하면 ItemsPath를 설정할 필요가 없습니다. "has-a-group" 패턴에서 그룹 개체에는 컬렉션 형식의 하나 이상의 속성(예: List<T>)이 있으므로 그룹은 속성 형식의 항목 그룹(또는 여러 속성 형식의 여러 항목 그룹)을 "가집니다". 이 패턴을 사용하면 ItemsPath 를 항목 그룹을 포함하는 속성의 이름으로 설정해야 합니다.

아래 예제에서는 "has-a-group" 패턴을 보여 줍니다. 페이지 클래스에는 뷰 모델의 인스턴스를 반환하는 ViewModel이라는 속성이 있습니다. CollectionViewSource는 뷰 모델의 Authors 속성(Authors는 그룹 개체의 컬렉션)에 바인딩되며 그룹화된 항목을 포함하는 Author.BookSkus 속성임을 지정합니다. 마지막으로 GridViewCollectionViewSource에 바인딩되며 그룹의 항목을 렌더링할 수 있도록 그룹 스타일이 정의되어 있습니다.

<Page.Resources>
    <CollectionViewSource
    x:Name="AuthorHasACollectionOfBookSku"
    Source="{x:Bind ViewModel.Authors}"
    IsSourceGrouped="true"
    ItemsPath="BookSkus"/>
</Page.Resources>
...
<GridView
ItemsSource="{x:Bind AuthorHasACollectionOfBookSku}" ...>
    <GridView.GroupStyle>
        <GroupStyle
            HeaderTemplate="{StaticResource AuthorGroupHeaderTemplateWide}" ... />
    </GridView.GroupStyle>
</GridView>

두 가지 방법 중 하나로 "is-a-group" 패턴을 구현할 수 있습니다. 한 가지 방법은 고유한 그룹 클래스를 작성하는 것입니다. 목록<T>에서 클래스를 파생합니다(여기서 T는 항목의 형식). 예: public class Author : List<BookSku>. 두 번째 방법은 LINQ 식을 사용하여 BookSku 항목의 속성 값과 같은 그룹 개체(및 그룹 클래스)를 동적으로 만드는 것입니다. 이 방법은 항목의 플랫 목록만 유지하고 즉석에서 그룹화하여 클라우드 서비스의 데이터에 액세스하는 앱의 일반적입니다. Author Genre와 같은 특별한 그룹 클래스 없이도 저자 또는 장르별로 책을 유연하게 그룹화할 수 있습니다.

아래 예제에서는 LINQ를 사용하는 "is-a-group" 패턴을 보여 줍니다. 이번에는 그룹 머리글에 장르 이름으로 표시된 장르별로 책을 그룹화합니다. 이는 그룹 키 값을 참조하는 "Key" 속성 경로로 표시됩니다.

using System.Linq;
...
private IOrderedEnumerable<IGrouping<string, BookSku>> genres;

public IOrderedEnumerable<IGrouping<string, BookSku>> Genres
{
    get
    {
        if (this.genres == null)
        {
            this.genres = from book in this.bookSkus
                          group book by book.genre into grp
                          orderby grp.Key
                          select grp;
        }
        return this.genres;
    }
}

데이터 템플릿과 함께 {x:Bind} 를 사용할 때 는 x:DataType 값을 설정하여 바인딩되는 형식을 나타내야 합니다. 형식이 제네릭인 경우 태그에서 이를 표현할 수 없으므로 그룹 스타일 헤더 템플릿 대신 {Binding} 을(를) 사용해야 합니다.

    <Grid.Resources>
        <CollectionViewSource x:Name="GenreIsACollectionOfBookSku"
        Source="{x:Bind Genres}"
        IsSourceGrouped="true"/>
    </Grid.Resources>
    <GridView ItemsSource="{x:Bind GenreIsACollectionOfBookSku}">
        <GridView.ItemTemplate x:DataType="local:BookTemplate">
            <DataTemplate>
                <TextBlock Text="{x:Bind Title}"/>
            </DataTemplate>
        </GridView.ItemTemplate>
        <GridView.GroupStyle>
            <GroupStyle>
                <GroupStyle.HeaderTemplate>
                    <DataTemplate>
                        <TextBlock Text="{Binding Key}"/>
                    </DataTemplate>
                </GroupStyle.HeaderTemplate>
            </GroupStyle>
        </GridView.GroupStyle>
    </GridView>

SemanticZoom 컨트롤은 사용자가 그룹화된 데이터를 보고 탐색할 수 있는 좋은 방법입니다. Bookstore2 샘플 앱은 SemanticZoom을 사용하는 방법을 보여 줍니다. 해당 앱에서 저자(축소 보기)로 그룹화된 책 목록을 보거나 축소하여 저자의 점프 목록(축소 보기)을 볼 수 있습니다. 점프 목록은 책 목록을 스크롤하는 것보다 훨씬 빠른 탐색을 제공합니다. 축소 및 축소 보기는 실제로 ListView 또는 GridView 컨트롤이 동일한 CollectionViewSource에 바인딩됩니다.

SemanticZoom의 그림

범주 내의 하위 범주와 같은 계층적 데이터에 바인딩하는 경우 일련의 항목 컨트롤을 사용하여 UI의 계층 수준을 표시하도록 선택할 수 있습니다. 한 항목 컨트롤의 선택 영역에 따라 후속 항목 컨트롤의 내용이 결정됩니다. 각 목록을 자체 CollectionViewSource 에 바인딩하고 CollectionViewSource 인스턴스를 체인에 함께 바인딩하여 목록을 동기화된 상태로 유지할 수 있습니다. 이를 마스터/세부 정보(또는 목록/세부 정보) 보기라고 합니다. 자세한 내용은 계층적 데이터에 바인딩하고 마스터/세부 정보 보기를 만드는 방법을 참조하세요.

데이터 바인딩 문제 진단 및 디버깅

바인딩 태그에는 속성의 이름이 포함되며 C#의 경우 필드와 메서드도 포함됩니다. 따라서 속성의 이름을 바꿀 때 속성을 참조하는 바인딩도 변경해야 합니다. 이를 잊어버리면 데이터 바인딩 버그의 일반적인 예가 발생하며 앱이 컴파일되지 않거나 올바르게 실행되지 않습니다.

{x:Bind}{Binding}에서 만든 바인딩 개체는 대체로 기능적으로 동일합니다. 그러나 {x:Bind}에는 바인딩 소스에 대한 형식 정보가 있으며 컴파일 시간에 소스 코드를 생성합니다. {x:Bind}를 사용하면 코드의 나머지 부분과 동일한 종류의 문제 검색을 얻을 수 있습니다. 여기에는 바인딩 식의 컴파일 시간 유효성 검사와 페이지의 partial 클래스로 생성된 소스 코드에서 중단점을 설정하여 디버깅하는 것이 포함됩니다. 이러한 클래스는 폴더의 파일( obj 예: C#) <view name>.g.cs과 같은 이름으로 찾을 수 있습니다. 바인딩에 문제가 있는 경우 Microsoft Visual Studio 디버거에서 처리되지 않은 예외 중단을 켭니다. 디버거는 해당 시점에서 실행을 중단한 다음 무엇이 잘못되었는지 디버그할 수 있습니다. {x:Bind}에서 생성된 코드는 바인딩 소스 노드 그래프의 각 부분에 대해 동일한 패턴을 따르며, 호출 스택 창의 정보를 사용하여 문제가 발생한 호출 시퀀스를 확인할 수 있습니다.

{Binding} 에 바인딩 원본에 대한 형식 정보가 없습니다. 하지만 디버거가 연결된 상태에서 앱을 실행하면 Visual Studio의 출력 창에 바인딩 오류가 표시됩니다.

코드에서 바인딩 만들기

메모 이 섹션은 코드에서 {x:Bind} 바인딩을 만들 수 없으므로 {Binding}에만 적용됩니다. 그러나 종속성 속성에 대한 변경 알림을 등록할 수 있는 DependencyObject.RegisterPropertyChangedCallback을 사용하면 {x:Bind}의 동일한 이점 중 일부를 얻을 수 있습니다.

XAML 대신 절차 코드를 사용하여 데이터에 UI 요소를 연결할 수도 있습니다. 이렇게 하려면 새 Binding 개체를 만들고 적절한 속성을 설정한 다음 FrameworkElement.SetBinding 또는 BindingOperations.SetBinding을 호출합니다. 프로그래밍 방식으로 바인딩을 만드는 것은 런타임에 바인딩 속성 값을 선택하거나 여러 컨트롤 간에 단일 바인딩을 공유하려는 경우에 유용합니다. 그러나 SetBinding을 호출한 후에는 바인딩 속성 값을 변경할 수 없습니다.

다음 예제에서는 코드에서 바인딩을 구현하는 방법을 보여줍니다.

<TextBox x:Name="MyTextBox" Text="Text"/>
// Create an instance of the MyColors class 
// that implements INotifyPropertyChanged.
MyColors textcolor = new MyColors();

// Brush1 is set to be a SolidColorBrush with the value Red.
textcolor.Brush1 = new SolidColorBrush(Colors.Red);

// Set the DataContext of the TextBox MyTextBox.
MyTextBox.DataContext = textcolor;

// Create the binding and associate it with the text box.
Binding binding = new Binding() { Path = new PropertyPath("Brush1") };
MyTextBox.SetBinding(TextBox.ForegroundProperty, binding);
' Create an instance of the MyColors class 
' that implements INotifyPropertyChanged. 
Dim textcolor As New MyColors()

' Brush1 is set to be a SolidColorBrush with the value Red. 
textcolor.Brush1 = New SolidColorBrush(Colors.Red)

' Set the DataContext of the TextBox MyTextBox. 
MyTextBox.DataContext = textcolor

' Create the binding and associate it with the text box.
Dim binding As New Binding() With {.Path = New PropertyPath("Brush1")}
MyTextBox.SetBinding(TextBox.ForegroundProperty, binding)

{x:Bind} 및 {Binding} 기능 비교

특징 {x:Bind} vs. {Binding} 비고
경로가 기본 속성입니다. {x:Bind a.b.c}
-
{Binding a.b.c}
Path 속성 {x:Bind Path=a.b.c}
-
{Binding Path=a.b.c}
x:Bind에서 경로는 기본적으로 DataContext가 아니라 페이지에 루팅됩니다.
인덱서 {x:Bind Groups[2].Title}
-
{Binding Groups[2].Title}
컬렉션에서 지정된 항목에 바인딩합니다. 정수 기반 인덱스만 지원됩니다.
연결된 속성 {x:Bind Button22.(Grid.Row)}
-
{Binding Button22.(Grid.Row)}
연결된 속성은 괄호를 사용하여 지정됩니다. 속성이 XAML 네임스페이스에 선언되지 않은 경우 문서의 머리글에 있는 코드 네임스페이스에 매핑되어야 하는 xml 네임스페이스로 접두사를 지정합니다.
주조 {x:Bind groups[0].(data:SampleDataGroup.Title)}
-
{Binding}에 필요하지 않습니다.
캐스트는 괄호를 사용하여 지정됩니다. 속성이 XAML 네임스페이스에 선언되지 않은 경우 문서의 머리글에 있는 코드 네임스페이스에 매핑되어야 하는 xml 네임스페이스로 접두사를 지정합니다.
변환기 {x:Bind IsShown, Converter={StaticResource BoolToVisibility}}
-
{Binding IsShown, Converter={StaticResource BoolToVisibility}}
변환기는 Page/ResourceDictionary의 루트 또는 App.xaml에서 선언해야 합니다.
ConverterParameter, ConverterLanguage {x:Bind IsShown, Converter={StaticResource BoolToVisibility}, ConverterParameter=One, ConverterLanguage=fr-fr}
-
{Binding IsShown, Converter={StaticResource BoolToVisibility}, ConverterParameter=One, ConverterLanguage=fr-fr}
변환기는 Page/ResourceDictionary의 루트 또는 App.xaml에서 선언해야 합니다.
TargetNullValue {x:Bind Name, TargetNullValue=0}
-
{Binding Name, TargetNullValue=0}
바인딩 식의 리프가 null일 때 사용됩니다. 문자열 값에 작은따옴표를 사용합니다.
FallbackValue {x:Bind Name, FallbackValue='empty'}
-
{Binding Name, FallbackValue='empty'}
바인딩 경로의 일부(리프 제외)가 null일 때 사용됩니다.
ElementName {x:Bind slider1.Value}
-
{Binding Value, ElementName=slider1}
{x:Bind}를 사용하면 필드에 바인딩됩니다. 경로는 기본적으로 페이지에 루팅되므로 명명된 모든 요소는 해당 필드를 통해 액세스할 수 있습니다.
RelativeSource: Self <Rectangle x:Name="rect1" Width="200" Height="{x:Bind rect1.Width}" ... />
-
<Rectangle Width="200" Height="{Binding Width, RelativeSource={RelativeSource Self}}" ... />
{x:Bind}를 사용하여 요소의 이름을 지정하고 Path에서 해당 이름을 사용합니다.
RelativeSource: TemplatedParent {x:Bind}에 필요하지 않음
-
{Binding <path>, RelativeSource={RelativeSource TemplatedParent}}
ControlTemplate에서 {x:Bind} TargetType을 사용하면 템플릿 부모에 대한 바인딩을 나타냅니다. {Binding}의 경우 대부분의 경우 컨트롤 템플릿에서 일반 템플릿 바인딩을 사용할 수 있습니다. 그러나 변환기 또는 양방향 바인딩을 사용해야 하는 경우 TemplatedParent를 사용합니다.<
출처 {x:Bind}에 필요하지 않음
-
<ListView ItemsSource="{Binding Orders, Source={StaticResource MyData}}"/>
{x:Bind}의 경우 명명된 요소를 직접 사용하고 속성 또는 정적 경로를 사용할 수 있습니다.
Mode {x:Bind Name, Mode=OneWay}
-
{Binding Name, Mode=TwoWay}
모드는 OneTime, OneWay 또는 TwoWay일 수 있습니다. {x:Bind}는 기본적으로 OneTime으로 설정됩니다. {Binding}은(는) 기본적으로 OneWay로 설정됩니다.
UpdateSourceTrigger {x:Bind Name, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}
-
{Binding UpdateSourceTrigger=PropertyChanged}
UpdateSourceTrigger는 Default, LostFocus 또는 PropertyChanged일 수 있습니다. {x:Bind}는 UpdateSourceTrigger=Explicit를 지원하지 않습니다. {x:Bind}는 LostFocus 동작을 사용하는 TextBox.Text를 제외한 모든 경우에 PropertyChanged 동작을 사용합니다.