Dela via


Översikt över databindning

Det här avsnittet visar hur du binder en kontroll (eller ett annat gränssnittselement) till ett enskilt objekt eller binder en objektkontroll till en samling objekt i en UWP-app (Universal Windows Platform). Dessutom visar vi hur du styr återgivningen av objekt, implementerar en informationsvy baserat på en markering och konverterar data för visning. För mer information, se Databindning på djupet.

Förutsättningar

Det här avsnittet förutsätter att du vet hur du skapar en grundläggande UWP-app. Anvisningar om hur du skapar din första UWP-app finns i Komma igång med Windows-appar.

Skapa projektet

Skapa ett nytt projekt för Blank-applikation (Windows Universal). Ge den namnet "Snabbstart".

Bindning till ett enskilt objekt

Varje bindning består av ett bindningsmål och en bindningskälla. Vanligtvis är målet en egenskap för en kontroll eller ett annat gränssnittselement, och källan är en egenskap för en klassinstans (en datamodell eller en vymodell). Det här exemplet visar hur du binder en kontroll till ett enda objekt. Målet är egenskapen Text för en TextBlock. Källan är en instans av en enkel klass med namnet Inspelning som representerar en ljudinspelning. Låt oss titta på klassen först.

Om du använder C# eller C++/CX lägger du till en ny klass i projektet och namnger klassen Inspelning.

Om du använder C++/WinRT, lägg till nya Midl-fil (.idl)-objekt till projektet, med namn enligt C++/WinRT-kodexemplet nedan. Ersätt innehållet i de nya filerna med MIDL 3.0 kod som visas i listan, skapa projektet för att generera Recording.h och .cpp och RecordingViewModel.h och .cppoch sedan lägga till kod i de genererade filerna för att matcha listan. Mer information om de genererade filerna och hur du kopierar dem till projektet finns i XAML-kontroller. binda till en C++/WinRT-egenskap.

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"

Exponera sedan bindningskällans klass från klassen som representerar din sida med kod. Det gör vi genom att lägga till en egenskap av typen RecordingViewModel till MainPage-.

Om du använder C++/WinRT-uppdaterar du först MainPage.idl. Skapa projektet för att återskapa MainPage.h och .cppoch sammanfoga ändringarna i de genererade filerna till dem i projektet.

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();
}

Den sista delen är att binda en TextBlock till egenskapen 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>

Om du använder C++/WinRT-måste du ta bort funktionen MainPage::ClickHandler för att projektet ska kunna byggas.

Här är resultatet.

Binda ett textblock

Bindning till en samling objekt

Ett vanligt scenario är att binda till en samling affärsobjekt. I C# och Visual Basic är den generiska klassen ObservableCollection<T> ett bra samlingsalternativ för databindning eftersom den implementerar INotifyPropertyChanged och INotifyCollectionChanged-gränssnitt. Dessa gränssnitt ger ändringsmeddelande till bindningar när objekt läggs till eller tas bort eller en egenskap för själva listan ändras. Om du vill att dina bundna kontroller ska uppdateras med ändringar i egenskaperna för objekt i samlingen bör affärsobjektet även implementera INotifyPropertyChanged. Mer information finns i Databindning på djupet.

Om du använder C++/WinRTkan du lära dig mer om hur du binder till en observerbar samling i kontroller för XAML-objekt. Du kan binda till en C++/WinRT-samling. Om du först läser det ämnet blir avsikten med C++/WinRT-kodlistan som visas nedan tydligare.

I nästa exempel binds ListView till en samling av Recording objekt. Vi börjar med att lägga till samlingen i vår vymodell. Lägg bara till dessa nya medlemmar i klassen 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;
        };
    }
};

Och bind sedan en ListView- till egenskapen ViewModel.Recordings.

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

Vi har ännu inte angett någon datamall för klassen Inspelning, så det bästa UI-ramverket kan göra är att anropa ToString- för varje objekt i ListView-. Standardimplementeringen av ToString är att returnera typnamnet.

Att binda en listvy 1

För att åtgärda detta kan vi antingen åsidosätta ToString- för att returnera värdet för OneLineSummary, eller så kan vi ange en datamall. Alternativet datamall är en mer vanlig lösning och en mer flexibel lösning. Du anger en datamall med hjälp av egenskapen ContentTemplate för en innehållskontroll eller egenskapen ItemTemplate för en objektkontroll. Här är två sätt att utforma en datamall för Inspelning tillsammans med en bild av resultatet.

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

Att binda listvy 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>

Koppla en listvy 3

Mer information om XAML-syntax finns i Skapa ett användargränssnitt med XAML-. Mer information om kontrolllayout finns i Definiera layouter med XAML-.

Lägga till en informationsvy

Du kan välja att visa all information om Inspelning objekt i ListView objekt. Men det tar upp mycket utrymme. I stället kan du visa tillräckligt med data i objektet för att identifiera det och när användaren gör ett val kan du visa all information om det markerade objektet i ett separat användargränssnitt som kallas för informationsvyn. Det här arrangemanget kallas även för en huvud-/detaljvy eller en lista-/detaljvy.

Det finns två sätt att göra detta på. Du kan binda informationsvyn till egenskapen SelectedItem för ListView. Eller så kan du använda en CollectionViewSource-, i vilket fall du binder både ListView- och informationsvyn till CollectionViewSource- (detta tar hand om det markerade objektet åt dig). Båda teknikerna visas nedan och båda ger samma resultat (visas i bilden).

Anmärkning

Hittills i det här avsnittet har vi bara använt {x:Bind} markeringstillägget, men båda metoderna som vi visar nedan kräver det mer flexibla (men mindre högpresterande) {Binding}-markeringstillägget.

Om du använder C++/WinRT eller Visual C++-komponenttillägg (C++/CX) och vill använda markeringstillägget {Binding}, måste du lägga till attributet BindableAttribute till varje körningsklass som du vill binda till. Om du vill använda {x:Bind}behöver du inte det attributet.

Viktigt!

Om du använder C++/WinRT-är attributet BindableAttribute tillgängligt om du har installerat Windows SDK version 10.0.17763.0 (Windows 10, version 1809) eller senare. Utan det attributet måste du implementera gränssnitten ICustomPropertyProvider och ICustomProperty för att kunna använda {Binding} markeringstillägg.

Först, här är tekniken 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
{
    ...
};

Den enda andra förändringen som krävs är till markeringen.

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

För tekniken CollectionViewSource lägger du först till en CollectionViewSource- som en sidresurs.

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

Justera sedan bindningarna på ListView- (som inte längre behöver namnges) och i informationsvyn för att använda CollectionViewSource-. Observera att genom att binda informationsvyn direkt till CollectionViewSourceantyder du att du vill binda till det aktuella objektet i bindningar där sökvägen inte kan hittas i själva samlingen. Du behöver inte ange egenskapen CurrentItem som sökväg för bindningen, även om du kan göra det om det finns tvetydigheter).

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

Och här är det identiska resultatet i varje fall.

Anmärkning

Om du använder C++ser användargränssnittet inte exakt ut som bilden nedan: återgivningen av egenskapen ReleaseDateTime är annorlunda. Mer information om detta finns i följande avsnitt.

Binda en listvy till 4

Formatera eller konvertera datavärden för visning

Det finns ett problem med återgivningen ovan. Egenskapen ReleaseDateTime är inte bara ett datum, det är en DateTime- (om du använder C++, är det en Kalender). Så i C# visas den med mer precision än vi behöver. Och i C++ återges det som ett typnamn. En lösning är att lägga till en strängegenskap i klassen Inspelning som returnerar motsvarigheten till this.ReleaseDateTime.ToString("d"). Att namnge egenskapen ReleaseDate- skulle indikera att den returnerar ett datum och inte ett datum och en tid. Att namnge den ReleaseDateAsString skulle ytterligare indikera att den returnerar en sträng.

En mer flexibel lösning är att använda något som kallas värdekonverterare. Här är ett exempel på hur du skapar en egen värdekonverterare. Om du använder C# lägger du till koden nedan i din Recording.cs källkodsfil. Om du använder C++/WinRT lägger du till ett nytt Midl-fil (.idl) objekt i projektet, med namnet som visas i C++/WinRT-kodexemplet nedan, skapar projektet för att generera StringFormatter.h och .cpp, lägger till filerna i projektet och klistrar sedan in kodlistorna i dem. Lägg också till #include "StringFormatter.h" i 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();
    }
};
...

Anmärkning

För C++/WinRT-kodlistan ovan använder vi StringFormatter.idl i för att deklarera IValueConverter som standardgränssnitt. I listan har StringFormatter bara en konstruktor och inga metoder, så inget standardgränssnitt genereras för den. Attributet default är optimalt om du inte lägger till instansmedlemmar i StringFormattereftersom ingen QueryInterface krävs för att anropa metoderna IValueConverter. Du kan också uppmana ett standardgränssnitt för IStringFormatter att genereras, och det gör du genom att kommentera själva körningsklassen med attributet default_interface. Det här alternativet är optimalt om du lägger till instansmedlemmar i StringFormatter- som anropas oftare än metoderna för IValueConverter är, eftersom ingen QueryInterface krävs för att anropa instansmedlemmarna.

Nu kan vi lägga till en instans av StringFormatter som en sidresurs och använda den i bindningen av TextBlock- som visar egenskapen ReleaseDateTime.

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

Som du ser ovan använder vi markup för formateringsflexibilitet för att skicka en formatsträng till konverteraren via konverterarens parameter. I kodexemplen som visas i det här avsnittet använder bara C#-värdekonverteraren den parametern. Men du kan enkelt skicka en C++-formatsträng som konverterarparameter och använda den i värdekonverteraren med en formateringsfunktion som wprintf eller swprintf.

Här är resultatet.

visar ett datum med anpassad formatering

Anmärkning

Från och med Windows 10 version 1607 tillhandahåller XAML-ramverket en inbyggd boolesk-till-synlighet-konverterare. Konverteraren mappar true till Visibility.Visible uppräkningsvärdet och false till Visibility.Kollapsad så att du kan binda en Synlighetsegenskap till ett booleskt värde utan att skapa en omvandlare. Om du vill använda den inbyggda konverteraren måste appens lägsta SDK-målversion vara 14393 eller senare. Du kan inte använda den när din app riktar in sig på tidigare versioner av Windows 10. Mer information om målversioner finns tillgänglig i versionsanpassad kod.

Se även