다음을 통해 공유


데이터 바인딩 개요

이 항목에서는 컨트롤(또는 다른 UI 요소)을 단일 항목에 바인딩하거나 UWP(유니버설 Windows 플랫폼) 앱의 항목 컬렉션에 항목 컨트롤을 바인딩하는 방법을 보여 줍니다. 또한 항목 렌더링을 제어하고, 선택에 따라 세부 정보 보기를 구현하고, 표시할 데이터를 변환하는 방법을 보여 드립니다. 자세한 내용은 데이터 바인딩에 대한 심층 설명을 참조하세요.

필수 조건

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

프로젝트를 만듭니다.

새로운 Blank 애플리케이션(Windows 유니버설) 프로젝트를 생성합니다. "빠른 시작"이라는 이름을 붙이세요.

단일 항목에 바인딩

모든 바인딩은 바인딩 대상과 바인딩 원본으로 구성됩니다. 일반적으로 대상은 컨트롤 또는 다른 UI 요소의 속성이며 원본은 클래스 인스턴스(데이터 모델 또는 뷰 모델)의 속성입니다. 이 예제에서는 컨트롤을 단일 항목에 바인딩하는 방법을 보여줍니다. 대상은 TextBlockText 속성입니다. 원본은 오디오 녹음을 나타내는 녹음이라는 간단한 클래스의 인스턴스입니다. 먼저 클래스를 살펴보겠습니다.

C# 또는 C++/CX를 사용하는 경우 프로젝트에 새 클래스를 추가하고 클래스 이름을 기록로 지정합니다.

C++/WinRT사용하는 경우 아래 나열된 C++/WinRT 코드 예제와 같이 명명된 새 Midl 파일(.idl) 항목을 프로젝트에 추가합니다. 새 파일의 내용을 목록에 나타난 MIDL 3.0 코드로 바꾸고, 프로젝트를 빌드하여 Recording.h.cppRecordingViewModel.h.cpp를 생성한 후, 생성된 파일에 목록과 일치하도록 코드를 추가합니다. 생성된 파일 및 이를 프로젝트에 복사하는 방법에 대한 자세한 내용은 XAML 컨트롤; C++/WinRT 속성에 바인딩하는 방법을 확인하세요.

namespace Quickstart
{
    public class Recording
    {
        public string ArtistName { get; set; }
        public string CompositionName { get; set; }
        public DateTime ReleaseDateTime { get; set; }
        public Recording()
        {
            this.ArtistName = "Wolfgang Amadeus Mozart";
            this.CompositionName = "Andante in C for Piano";
            this.ReleaseDateTime = new DateTime(1761, 1, 1);
        }
        public string OneLineSummary
        {
            get
            {
                return $"{this.CompositionName} by {this.ArtistName}, released: "
                    + this.ReleaseDateTime.ToString("d");
            }
        }
    }
    public class RecordingViewModel
    {
        private Recording defaultRecording = new Recording();
        public Recording DefaultRecording { get { return this.defaultRecording; } }
    }
}
// Recording.idl
namespace Quickstart
{
    runtimeclass Recording
    {
        Recording(String artistName, String compositionName, Windows.Globalization.Calendar releaseDateTime);
        String ArtistName{ get; };
        String CompositionName{ get; };
        Windows.Globalization.Calendar ReleaseDateTime{ get; };
        String OneLineSummary{ get; };
    }
}

// RecordingViewModel.idl
import "Recording.idl";

namespace Quickstart
{
    runtimeclass RecordingViewModel
    {
        RecordingViewModel();
        Quickstart.Recording DefaultRecording{ get; };
    }
}

// Recording.h
// Add these fields:
...
#include <sstream>
...
private:
    std::wstring m_artistName;
    std::wstring m_compositionName;
    Windows::Globalization::Calendar m_releaseDateTime;
...

// Recording.cpp
// Implement like this:
...
Recording::Recording(hstring const& artistName, hstring const& compositionName, Windows::Globalization::Calendar const& releaseDateTime) :
    m_artistName{ artistName.c_str() },
    m_compositionName{ compositionName.c_str() },
    m_releaseDateTime{ releaseDateTime } {}

hstring Recording::ArtistName(){ return hstring{ m_artistName }; }
hstring Recording::CompositionName(){ return hstring{ m_compositionName }; }
Windows::Globalization::Calendar Recording::ReleaseDateTime(){ return m_releaseDateTime; }

hstring Recording::OneLineSummary()
{
    std::wstringstream wstringstream;
    wstringstream << m_compositionName.c_str();
    wstringstream << L" by " << m_artistName.c_str();
    wstringstream << L", released: " << m_releaseDateTime.MonthAsNumericString().c_str();
    wstringstream << L"/" << m_releaseDateTime.DayAsString().c_str();
    wstringstream << L"/" << m_releaseDateTime.YearAsString().c_str();
    return hstring{ wstringstream.str().c_str() };
}
...

// RecordingViewModel.h
// Add this field:
...
#include "Recording.h"
...
private:
    Quickstart::Recording m_defaultRecording{ nullptr };
...

// RecordingViewModel.cpp
// Implement like this:
...
Quickstart::Recording RecordingViewModel::DefaultRecording()
{
    Windows::Globalization::Calendar releaseDateTime;
    releaseDateTime.Year(1761);
    releaseDateTime.Month(1);
    releaseDateTime.Day(1);
    m_defaultRecording = winrt::make<Recording>(L"Wolfgang Amadeus Mozart", L"Andante in C for Piano", releaseDateTime);
    return m_defaultRecording;
}
...
// Recording.h
#include <sstream>
namespace Quickstart
{
    public ref class Recording sealed
    {
    private:
        Platform::String^ artistName;
        Platform::String^ compositionName;
        Windows::Globalization::Calendar^ releaseDateTime;
    public:
        Recording(Platform::String^ artistName, Platform::String^ compositionName,
            Windows::Globalization::Calendar^ releaseDateTime) :
            artistName{ artistName },
            compositionName{ compositionName },
            releaseDateTime{ releaseDateTime } {}
        property Platform::String^ ArtistName
        {
            Platform::String^ get() { return this->artistName; }
        }
        property Platform::String^ CompositionName
        {
            Platform::String^ get() { return this->compositionName; }
        }
        property Windows::Globalization::Calendar^ ReleaseDateTime
        {
            Windows::Globalization::Calendar^ get() { return this->releaseDateTime; }
        }
        property Platform::String^ OneLineSummary
        {
            Platform::String^ get()
            {
                std::wstringstream wstringstream;
                wstringstream << this->CompositionName->Data();
                wstringstream << L" by " << this->ArtistName->Data();
                wstringstream << L", released: " << this->ReleaseDateTime->MonthAsNumericString()->Data();
                wstringstream << L"/" << this->ReleaseDateTime->DayAsString()->Data();
                wstringstream << L"/" << this->ReleaseDateTime->YearAsString()->Data();
                return ref new Platform::String(wstringstream.str().c_str());
            }
        }
    };
    public ref class RecordingViewModel sealed
    {
    private:
        Recording ^ defaultRecording;
    public:
        RecordingViewModel()
        {
            Windows::Globalization::Calendar^ releaseDateTime = ref new Windows::Globalization::Calendar();
            releaseDateTime->Year = 1761;
            releaseDateTime->Month = 1;
            releaseDateTime->Day = 1;
            this->defaultRecording = ref new Recording{ L"Wolfgang Amadeus Mozart", L"Andante in C for Piano", releaseDateTime };
        }
        property Recording^ DefaultRecording
        {
            Recording^ get() { return this->defaultRecording; };
        }
    };
}

// Recording.cpp
#include "pch.h"
#include "Recording.h"

다음으로, 마크업 페이지를 나타내는 클래스에서 바인딩 소스 클래스를 공개합니다. 이렇게 하려면 MainPageRecordingViewModel 형식의 속성을 추가합니다.

C++/WinRT을(를) 사용하는 경우, 먼저 MainPage.idl를 업데이트합니다. 프로젝트를 빌드하여 MainPage.h.cpp을 다시 생성한 후, 생성된 파일의 변경 내용을 프로젝트의 기존 파일에 병합합니다.

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

// MainPage.h
// Add this property and this field:
...
#include "RecordingViewModel.h"
...
    Quickstart::RecordingViewModel ViewModel();

private:
    Quickstart::RecordingViewModel m_viewModel{ nullptr };
...

// MainPage.cpp
// Implement like this:
...
MainPage::MainPage()
{
    InitializeComponent();
    m_viewModel = winrt::make<RecordingViewModel>();
}
Quickstart::RecordingViewModel MainPage::ViewModel()
{
    return m_viewModel;
}
...
// MainPage.h
...
#include "Recording.h"

namespace Quickstart
{
    public ref class MainPage sealed
    {
    private:
        RecordingViewModel ^ viewModel;
    public:
        MainPage();

        property RecordingViewModel^ ViewModel
        {
            RecordingViewModel^ get() { return this->viewModel; };
        }
    };
}

// MainPage.cpp
...
MainPage::MainPage()
{
    InitializeComponent();
    this->viewModel = ref new RecordingViewModel();
}

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

<Page x:Class="Quickstart.MainPage" ... >
    <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
        <TextBlock Text="{x:Bind ViewModel.DefaultRecording.OneLineSummary}"
    HorizontalAlignment="Center"
    VerticalAlignment="Center"/>
    </Grid>
</Page>

C++/WinRT사용하는 경우 프로젝트를 빌드하려면 MainPage::ClickHandler 함수를 제거해야 합니다.

결과는 다음과 같습니다.

텍스트 블록 바인딩

항목 모음에 바인딩하기

일반적인 시나리오는 비즈니스 개체 컬렉션에 바인딩하는 것입니다. C# 및 Visual Basic에서 제네릭 ObservableCollection<T> 클래스는 INotifyPropertyChanged 를 구현하고, 그리고 INotifyCollectionChanged 인터페이스를 구현하여 데이터 바인딩에 적합한 컬렉션입니다. 이러한 인터페이스는 항목이 추가 또는 제거되거나 목록 자체의 속성이 변경될 때 바인딩에 변경 알림을 제공합니다. 바인딩된 컨트롤을 컬렉션의 개체 속성 변경 내용으로 업데이트하려면 비즈니스 개체가 INotifyPropertyChanged를 구현하도록 해야 합니다. 자세한 내용은 데이터 바인딩을 심층적으로 설명하는을 참조하세요.

C++/WinRT을 사용하는 경우, XAML 항목 컨트롤에서 관찰 가능한 컬렉션에 바인딩하는 방법에 대해서 자세히 알아볼 수 있고, C++/WinRT 컬렉션에 바인딩할 수 있습니다. 해당 항목을 먼저 읽는 경우 아래에 표시된 C++/WinRT 코드 목록의 의도가 더 명확해질 것입니다.

다음은 ListViewRecording 개체 컬렉션에 바인딩하는 예제입니다. 먼저 뷰 모델에 컬렉션을 추가해 보겠습니다. 이러한 새 멤버를 RecordingViewModel 클래스에 추가하기만 하면 됩니다.

public class RecordingViewModel
{
    ...
    private ObservableCollection<Recording> recordings = new ObservableCollection<Recording>();
    public ObservableCollection<Recording> Recordings{ get{ return this.recordings; } }
    public RecordingViewModel()
    {
        this.recordings.Add(new Recording(){ ArtistName = "Johann Sebastian Bach",
            CompositionName = "Mass in B minor", ReleaseDateTime = new DateTime(1748, 7, 8) });
        this.recordings.Add(new Recording(){ ArtistName = "Ludwig van Beethoven",
            CompositionName = "Third Symphony", ReleaseDateTime = new DateTime(1805, 2, 11) });
        this.recordings.Add(new Recording(){ ArtistName = "George Frideric Handel",
            CompositionName = "Serse", ReleaseDateTime = new DateTime(1737, 12, 3) });
    }
}
// RecordingViewModel.idl
// Add this property:
...
#include <winrt/Windows.Foundation.Collections.h>
...
Windows.Foundation.Collections.IVector<IInspectable> Recordings{ get; };
...

// RecordingViewModel.h
// Change the constructor declaration, and add this property and this field:
...
    RecordingViewModel();
    Windows::Foundation::Collections::IVector<Windows::Foundation::IInspectable> Recordings();

private:
    Windows::Foundation::Collections::IVector<Windows::Foundation::IInspectable> m_recordings;
...

// RecordingViewModel.cpp
// Update/add implementations like this:
...
RecordingViewModel::RecordingViewModel()
{
    std::vector<Windows::Foundation::IInspectable> recordings;

    Windows::Globalization::Calendar releaseDateTime;
    releaseDateTime.Month(7); releaseDateTime.Day(8); releaseDateTime.Year(1748);
    recordings.push_back(winrt::make<Recording>(L"Johann Sebastian Bach", L"Mass in B minor", releaseDateTime));

    releaseDateTime = Windows::Globalization::Calendar{};
    releaseDateTime.Month(11); releaseDateTime.Day(2); releaseDateTime.Year(1805);
    recordings.push_back(winrt::make<Recording>(L"Ludwig van Beethoven", L"Third Symphony", releaseDateTime));

    releaseDateTime = Windows::Globalization::Calendar{};
    releaseDateTime.Month(3); releaseDateTime.Day(12); releaseDateTime.Year(1737);
    recordings.push_back(winrt::make<Recording>(L"George Frideric Handel", L"Serse", releaseDateTime));

    m_recordings = winrt::single_threaded_observable_vector<Windows::Foundation::IInspectable>(std::move(recordings));
}

Windows::Foundation::Collections::IVector<Windows::Foundation::IInspectable> RecordingViewModel::Recordings() { return m_recordings; }
...
// Recording.h
...
public ref class RecordingViewModel sealed
{
private:
    ...
    Windows::Foundation::Collections::IVector<Recording^>^ recordings;
public:
    RecordingViewModel()
    {
        ...
        releaseDateTime = ref new Windows::Globalization::Calendar();
        releaseDateTime->Year = 1748;
        releaseDateTime->Month = 7;
        releaseDateTime->Day = 8;
        Recording^ recording = ref new Recording{ L"Johann Sebastian Bach", L"Mass in B minor", releaseDateTime };
        this->Recordings->Append(recording);
        releaseDateTime = ref new Windows::Globalization::Calendar();
        releaseDateTime->Year = 1805;
        releaseDateTime->Month = 2;
        releaseDateTime->Day = 11;
        recording = ref new Recording{ L"Ludwig van Beethoven", L"Third Symphony", releaseDateTime };
        this->Recordings->Append(recording);
        releaseDateTime = ref new Windows::Globalization::Calendar();
        releaseDateTime->Year = 1737;
        releaseDateTime->Month = 12;
        releaseDateTime->Day = 3;
        recording = ref new Recording{ L"George Frideric Handel", L"Serse", releaseDateTime };
        this->Recordings->Append(recording);
    }
    ...
    property Windows::Foundation::Collections::IVector<Recording^>^ Recordings
    {
        Windows::Foundation::Collections::IVector<Recording^>^ get()
        {
            if (this->recordings == nullptr)
            {
                this->recordings = ref new Platform::Collections::Vector<Recording^>();
            }
            return this->recordings;
        };
    }
};

그런 다음 ListView을(를) ViewModel.Recordings 속성에 바인딩합니다.

<Page x:Class="Quickstart.MainPage" ... >
    <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
        <ListView ItemsSource="{x:Bind ViewModel.Recordings}"
        HorizontalAlignment="Center" VerticalAlignment="Center"/>
    </Grid>
</Page>

Recording 클래스에 대한 데이터 템플릿을 아직 제공하지 않았으므로 UI 프레임워크에서 수행할 수 있는 최선의 방법은 ListView각 항목에 대해 ToString 호출하는 것입니다. ToString 기본 구현은 형식 이름을 반환하는 것입니다.

리스트 뷰 1 바인딩

이를 해결하기 위해 ToString 재정의하여 OneLineSummary값을 반환하거나 데이터 템플릿을 제공할 수 있습니다. 데이터 템플릿 옵션은 보다 일반적인 솔루션이며 보다 유연한 솔루션입니다. 콘텐츠 컨트롤의 ContentTemplate 속성 또는 항목 컨트롤의 ItemTemplate 속성을 사용하여 데이터 템플릿을 지정합니다. 다음은 결과의 그림과 함께 기록 위한 데이터 템플릿을 디자인하는 두 가지 방법입니다.

<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사용하여 레이아웃 정의를 참조하세요.

세부 정보 보기 추가

ListView 항목에 기록 개체의 모든 세부 정보를 표시하도록 선택할 수 있습니다. 그러나 그것은 많은 공간을 차지합니다. 대신 항목을 식별하기에 충분한 데이터를 표시한 다음 사용자가 선택할 때 선택한 항목의 모든 세부 정보를 세부 정보 보기라고 하는 별도의 UI에 표시할 수 있습니다. 이 정렬을 마스터/세부 정보 보기 또는 목록/세부 정보 보기라고도 합니다.

이 방법에는 두 가지가 있습니다. 세부 정보 보기를 SelectedItem 속성에 ListView에 바인딩할 수 있습니다. 또는 CollectionViewSource를 사용할 수 있으며, 이 경우 ListView와 세부 정보 보기를 CollectionViewSource에 바인딩합니다. 이렇게 하면 현재 선택한 항목이 자동으로 처리됩니다. 두 기술 모두 아래에 나와 있으며 둘 다 동일한 결과를 제공합니다(그림 참조).

비고

지금까지 이 주제에서는 {x:Bind} 마크업 확장을 사용했지만, 아래에 소개할 두 가지 기술 모두 더 유연하지만 성능이 떨어지는 {Binding} 마크업 확장이 필요합니다.

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

중요합니다

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

먼저 SelectedItem 기법입니다.

// No code changes necessary for C#.
// Recording.idl
// Add this attribute:
...
[Windows.UI.Xaml.Data.Bindable]
runtimeclass Recording
...
[Windows::UI::Xaml::Data::Bindable]
public ref class Recording sealed
{
    ...
};

필수적인 다른 변경 사항은 마크업뿐입니다.

<Page x:Class="Quickstart.MainPage" ... >
    <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
        <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>
</Page>

CollectionViewSource 기법의 경우 먼저 CollectionViewSource 페이지 리소스로 추가합니다.

<Page.Resources>
    <CollectionViewSource x:Name="RecordingsCollection" Source="{x:Bind ViewModel.Recordings}"/>
</Page.Resources>

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

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

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

비고

C++를 사용하는 경우 UI는 아래 그림과 똑같지 않습니다. ReleaseDateTime 속성의 렌더링은 다릅니다. 이에 대한 자세한 내용은 다음 섹션을 참조하세요.

리스트 뷰 바인딩 4

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

위의 렌더링에 문제가 있습니다. ReleaseDateTime 속성은 날짜일 뿐 아니라 DateTime입니다 (C++를 사용하는 경우에는 캘린더입니다). 따라서 C#에서는 필요한 것보다 더 정밀하게 표시됩니다. 그리고 C++에서는 형식 이름으로 렌더링됩니다. 한 가지 방법은 Recording 클래스에 this.ReleaseDateTime.ToString("d")상응하는 문자열 속성을 추가하여 반환하는 것입니다. 해당 속성 ReleaseDate 이름을 지정하면 날짜 및 시간이 아닌 날짜를 반환함을 나타냅니다. ReleaseDateAsString이라고 이름을 지정하면 문자열을 반환하는 것을 나타냅니다.

보다 유연한 솔루션은 값 변환기라고 하는 것을 사용하는 것입니다. 다음은 사용자 고유의 값 변환기를 작성하는 방법의 예입니다. C#을 사용하는 경우 아래 코드를 Recording.cs 소스 코드 파일에 추가합니다. C++/WinRT를 사용하는 경우 아래 목록의 C++/WinRT 코드 예제에 표시된 대로 명명된 프로젝트에 새 Midl 파일(.idl) 항목을 추가하고, 프로젝트를 빌드하여 StringFormatter.h를 생성한 후 .cpp을 해당 파일들에 추가한 다음, 코드 목록을 그 파일들에 붙여넣습니다. 또한 #include "StringFormatter.h"부터 MainPage.h까지 추가합니다.

public class StringFormatter : Windows.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();
    }
}
// pch.h
...
#include <winrt/Windows.Globalization.h>

// StringFormatter.idl
namespace Quickstart
{
    runtimeclass StringFormatter : [default] Windows.UI.Xaml.Data.IValueConverter
    {
        StringFormatter();
    }
}

// StringFormatter.h
#pragma once

#include "StringFormatter.g.h"
#include <sstream>

namespace winrt::Quickstart::implementation
{
    struct StringFormatter : StringFormatterT<StringFormatter>
    {
        StringFormatter() = default;

        Windows::Foundation::IInspectable Convert(Windows::Foundation::IInspectable const& value, Windows::UI::Xaml::Interop::TypeName const& targetType, Windows::Foundation::IInspectable const& parameter, hstring const& language);
        Windows::Foundation::IInspectable ConvertBack(Windows::Foundation::IInspectable const& value, Windows::UI::Xaml::Interop::TypeName const& targetType, Windows::Foundation::IInspectable const& parameter, hstring const& language);
    };
}

namespace winrt::Quickstart::factory_implementation
{
    struct StringFormatter : StringFormatterT<StringFormatter, implementation::StringFormatter>
    {
    };
}

// StringFormatter.cpp
#include "pch.h"
#include "StringFormatter.h"
#include "StringFormatter.g.cpp"

namespace winrt::Quickstart::implementation
{
    Windows::Foundation::IInspectable StringFormatter::Convert(Windows::Foundation::IInspectable const& value, Windows::UI::Xaml::Interop::TypeName const& /* targetType */, Windows::Foundation::IInspectable const& /* parameter */, hstring const& /* language */)
    {
        // Retrieve the value as a Calendar.
        Windows::Globalization::Calendar valueAsCalendar{ value.as<Windows::Globalization::Calendar>() };

        std::wstringstream wstringstream;
        wstringstream << L"Released: ";
        wstringstream << valueAsCalendar.MonthAsNumericString().c_str();
        wstringstream << L"/" << valueAsCalendar.DayAsString().c_str();
        wstringstream << L"/" << valueAsCalendar.YearAsString().c_str();
        return winrt::box_value(hstring{ wstringstream.str().c_str() });
    }

    Windows::Foundation::IInspectable StringFormatter::ConvertBack(Windows::Foundation::IInspectable const& /* value */, Windows::UI::Xaml::Interop::TypeName const& /* targetType */, Windows::Foundation::IInspectable const& /* parameter */, hstring const& /* language */)
    {
        throw hresult_not_implemented();
    }
}
...
public ref class StringFormatter sealed : Windows::UI::Xaml::Data::IValueConverter
{
public:
    virtual Platform::Object^ Convert(Platform::Object^ value, TypeName targetType, Platform::Object^ parameter, Platform::String^ language)
    {
        // Retrieve the value as a Calendar.
        Windows::Globalization::Calendar^ valueAsCalendar = dynamic_cast<Windows::Globalization::Calendar^>(value);

        std::wstringstream wstringstream;
        wstringstream << L"Released: ";
        wstringstream << valueAsCalendar->MonthAsNumericString()->Data();
        wstringstream << L"/" << valueAsCalendar->DayAsString()->Data();
        wstringstream << L"/" << valueAsCalendar->YearAsString()->Data();
        return ref new Platform::String(wstringstream.str().c_str());
    }

    // No need to implement converting back on a one-way binding
    virtual Platform::Object^ ConvertBack(Platform::Object^ value, TypeName targetType, Platform::Object^ parameter, Platform::String^ language)
    {
        throw ref new Platform::NotImplementedException();
    }
};
...

비고

위의 C++/WinRT 코드 목록에서 StringFormatter.idl, 우리는 기본 특성를 사용하여 IValueConverter를 기본 인터페이스로 선언합니다. 목록에서 StringFormatter 생성자만 있고 메서드가 없으므로 기본 인터페이스가 생성되지 않습니다. default 특성은 IValueConverter 메서드를 호출하는 데 QueryInterface가 필요하지 않으므로 StringFormatter인스턴스 멤버를 추가하지 않는 경우 최적입니다. 또는 기본 IStringFormatter 인터페이스를 생성하라는 메시지를 표시할 수 있으며, default_interface 특성런타임 클래스 자체에 주석을 추가하면 됩니다. 인스턴스 멤버를 호출하는 데 QueryInterface가 필요하지 않으므로 IValueConverter 메서드보다 자주 호출되는 StringFormatter 인스턴스 멤버를 추가하는 경우 이 옵션이 최적입니다.

이제 StringFormatter 인스턴스를 페이지 리소스로 추가하고 ReleaseDateTime 속성을 표시하는 TextBlock 바인딩에 사용할 수 있습니다.

<Page.Resources>
    <local:StringFormatter x:Key="StringFormatterValueConverter"/>
</Page.Resources>
...
<TextBlock Text="{Binding ReleaseDateTime,
    Converter={StaticResource StringFormatterValueConverter},
    ConverterParameter=Released: \{0:d\}}"/>
...

위에서 볼 수 있듯이 서식 유연성을 위해 태그를 사용하여 변환기 매개 변수를 통해 형식 문자열을 변환기에 전달합니다. 이 항목에 표시된 코드 예제에서는 C# 값 변환기만 해당 매개 변수를 사용합니다. 그러나 C++스타일 형식 문자열을 변환기 매개 변수로 쉽게 전달하고 값 변환기에서 wprintf 또는 swprintf같은 서식 함수와 함께 사용할 수 있습니다.

결과는 다음과 같습니다.

사용자 지정 서식 사용하여 날짜를 표시하는

비고

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

참고하십시오