共用方式為


數據系結概觀

本主題說明如何將控件(或其他UI元素)系結至單一專案,或將專案控件系結至通用Windows平臺 (UWP) 應用程式中的專案集合。 此外,我們會示範如何控制項目的渲染、根據選取項目實作詳細檢視,以及轉換數據以供顯示。 如需詳細資訊,請參閱 深入數據系結

先決條件

本主題假設您知道如何建立基本的 UWP 應用程式。 如需建立第一個 UWP app 的指示,請參閱 開始使用 Windows 應用程式

建立專案

建立新的 空白應用程式(Windows Universal) 專案。 將它命名為 「Quickstart」。

系結至單一項目

每個系結都包含系結目標和系結來源。 一般而言,目標是控件或其他UI元素的屬性,而來源是類別實例的屬性(資料模型或檢視模型)。 這個範例示範如何將控件系結至單一專案。 目標是 TextBlockText 屬性。 來源是名為 Recording 之簡單類別的實例,代表音訊錄製。 讓我們先看看班級。

如果您使用 C# 或 C++/CX,請將新的類別新增至您的專案,並將類別命名為 Recording

如果您使用 C++/WinRT,請將新的 Midl 檔案 (.idl) 項目新增到專案中,命名如以下C++/WinRT 代碼範例清單所示。 將這些新檔案的內容取代為清單中顯示的 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"

接下來,從代表標記頁面的類別中公開繫結來源類別。 我們藉由將類型為 RecordingViewModel 的屬性新增至 MainPage來達成這項作業。

如果您使用 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();
}

最後一個部分是將 TextBlock 系結至 ViewModel.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> 類別是數據系結的絕佳集合選擇,因為它會實作 INotifyPropertyChangedINotifyCollectionChanged 介面。 當新增或移除專案或清單本身的屬性變更時,這些介面會提供變更通知給系結。 如果您想要讓繫結控制項隨集合中物件屬性變更而更新,商業物件也應該實作 INotifyPropertyChanged。 如需詳細資訊,請參閱 數據系結深入

如果您使用 C++/WinRT,那麼您可以深入學習如何將可觀察集合繫結到 XAML 項目控制項,或繫結到 C++/WinRT 集合。 如果您先閱讀該主題,會更清楚以下所展示的 C++/WinRT 程式碼列表的意圖。

下一個範例會將 ListView 系結至 Recording 物件的集合。 讓我們從將集合新增至檢視模型開始。 只要將這些新成員新增至 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中的每個項目呼叫 ToStringToString 的預設實作是傳回類型名稱。

綁定列表檢視 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 中顯示所選專案的所有詳細數據。 這種安排也稱為主要/詳細數據檢視,或清單/詳細數據檢視。

有兩種方式可以解決此問題。 您可以將詳細數據檢視系結至 ListViewSelectedItem 屬性。 或者,您可以使用 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 屬性。 如果沒有該屬性,您就必須實作 ICustomPropertyProviderICustomProperty 介面,以便能夠使用 {Binding} 標記擴展。

首先,以下是 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++,則它是 Calendar)。 因此,在 C# 中,其顯示的精確度比我們需要的要高。 在 C++,它會轉譯為類型名稱。 其中一個解決方案是將字串屬性新增至 Recording 類別,以傳回 對等的 this.ReleaseDateTime.ToString("d")。 命名該屬性 ReleaseDate 會指出它會傳回日期,而不是日期和時間。 將它命名為 ReleaseDateAsString 進而指出這個方法/函式會傳回字串。

更有彈性的解決方案是使用所謂的值轉換器。 以下是如何撰寫您自己的值轉換器的範例。 如果您使用 C#,請將下列程式代碼新增至 Recording.cs 原始碼檔案。 如果您使用 C++/WinRT,請將新的 Midl File (.idl) 項目新增至專案,如下列 C++/WinRT 程式代碼範例所示,建置專案以產生 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如果您不會將實例成員新增至 StringFormatter,則屬性是最佳的,因為不需要 QueryInterface 才能呼叫 IValueConverter 方法。 或者,您可以藉由將運行時間類別本身標註為 default_interface 屬性,來提示生成預設的 IStringFormatter 介面。 如果您將實例成員新增至 StringFormatter,並且這些成員比 IValueConverter 的方法更常被呼叫,那麼這個選項是最佳選擇,因為呼叫實例成員不需要 QueryInterface。

現在,我們可以將 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++樣式的格式字串當做轉換器參數傳遞,並在您的值轉換器中使用該字串搭配 wprintfswprintf 等格式函式。

以下是結果。

顯示具有自訂格式設定的日期

備註

從 Windows 10 版本 1607 開始,XAML 架構會提供內建布爾值到可見度轉換器。 轉換器會將 true 對應至 Visibility.Visible 列舉值,並將 false 對應至 Visibility.Collapsed,這樣您就可以在不建立轉換器的情況下將 Visibility 屬性綁定到布林值。 若要使用內建轉換器,應用程式的最低目標 SDK 版本必須是 14393 或更新版本。 當您的應用程式以舊版 Windows 10 為目標時,就無法使用它。 如需目標版本的詳細資訊,請參閱 版本調適型程序代碼

另請參閱