Delen via


Overzicht van gegevensbinding

In dit onderwerp wordt beschreven hoe u een controle (of een ander UI-element) koppelt aan één item of een items-besturingselement koppelt aan een verzameling items in een UWP-app (Universal Windows Platform). Daarnaast laten we zien hoe u het weergeven van items kunt beheren, hoe u een detailweergave implementeert op basis van een selectie en hoe u gegevens converteert om weer te geven. Voor meer gedetailleerde informatie, zie Gegevensbinding in detail.

Vereiste voorwaarden

In dit onderwerp wordt ervan uitgegaan dat u weet hoe u een eenvoudige UWP-app maakt. Zie Aan de slag met Windows-appsvoor instructies over het maken van uw eerste UWP-app.

Het project maken

Maak een nieuw Blank Application (Windows Universal) project. Geef deze de naam 'Quickstart'.

Binden aan één item

Elke binding bestaat uit een bindingsdoel en een bindingsbron. Het doel is doorgaans een eigenschap van een besturingselement of een ander UI-element en de bron is een eigenschap van een klasse-exemplaar (een gegevensmodel of een weergavemodel). In dit voorbeeld ziet u hoe u een besturingselement koppelt aan één item. Het doel is de eigenschap Text van een TextBlock-. De bron is een exemplaar van een eenvoudige klasse met de naam Recording die een audio-opname vertegenwoordigt. Laten we eerst eens kijken naar de klas.

Als u C# of C++/CX gebruikt, voegt u een nieuwe klasse toe aan uw project en noemt u de klasse Opname-.

Als u C++/WinRT-gebruikt, voeg dan nieuwe Midl-bestand (.idl)-items toe aan het project, zoals te zien in het onderstaande C++/WinRT-codevoorbeeld. Vervang de inhoud van deze nieuwe bestanden door de MIDL 3.0 code die wordt weergegeven in de vermelding, bouw het project om Recording.h en .cpp en RecordingViewModel.h en .cppte genereren, en voeg vervolgens code toe aan de gegenereerde bestanden die overeenkomen met de vermelding. Zie XAML-besturingselementen voor meer informatie over deze gegenereerde bestanden en hoe u ze naar uw project kopieert; binden aan een C++/WinRT-eigenschap.

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"

Maak vervolgens de bindingsbronklasse beschikbaar vanuit de klasse die uw pagina met markeringen vertegenwoordigt. Dat doen we door een eigenschap van het type RecordingViewModel toe te voegen aan MainPage.

Als u C++/WinRT-gebruikt, werkt u eerst MainPage.idlbij. Bouw het project om MainPage.h en .cppopnieuw te genereren en voeg de wijzigingen in de gegenereerde bestanden samen in de wijzigingen in uw project.

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

Het laatste stuk is het binden van een TextBlock- aan de eigenschap 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>

Als u C++/WinRT-gebruikt, moet u de functie MainPage::ClickHandler verwijderen om het project te kunnen bouwen.

Dit is het resultaat.

Een tekstblok binden

Binden aan een verzameling items

Een veelvoorkomend scenario is het binden aan een verzameling zakelijke objecten. In C# en Visual Basic is de algemene ObservableCollection<T> klasse een goede verzameling voor gegevensbinding, omdat hiermee de INotifyPropertyChanged en INotifyCollectionChanged interfaces worden geïmplementeerd. Deze interfaces bieden wijzigingsmeldingen voor bindingen wanneer items worden toegevoegd of verwijderd of een eigenschap van de lijst zelf wordt gewijzigd. Als u wilt dat uw gekoppelde besturingselementen worden bijgewerkt met wijzigingen in de eigenschappen van objecten binnen de verzameling, moet het bedrijfsobject ook INotifyPropertyChangedimplementeren. Voor meer informatie, zie Gegevensbinding in detail.

Als u C++/WinRT-gebruikt, kunt u meer te weten komen over het binden aan een observeerbare verzameling in XAML-itemsbesturingselementen, en binden aan een C++/WinRT-verzameling. Als u dat onderwerp eerst leest, is de intentie van de onderstaande C++/WinRT-codevermelding duidelijker.

In dit volgende voorbeeld wordt een ListView- gekoppeld aan een verzameling Recording objecten. Laten we beginnen met het toevoegen van de verzameling aan ons weergavemodel. Voeg deze nieuwe leden toe aan de RecordingViewModel klasse.

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

Vervolgens moet u een ListView koppelen aan de eigenschap ViewModel.Recordings.

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

We hebben nog geen gegevenssjabloon opgegeven voor de klasse Recording, dus het beste dat het UI-framework kan doen, is het aanroepen van ToString- voor elk item in de ListView-. De standaard implementatie van ToString- is het retourneren van de typenaam.

Een lijstweergave binden 1

Om dit te verhelpen, kunnen we ToString- overschrijven om de waarde van OneLineSummary-te retourneren, of we kunnen een gegevenssjabloon opgeven. De optie voor gegevenssjablonen is een meer gebruikelijke oplossing en een flexibelere optie. U geeft een gegevenssjabloon op met behulp van de eigenschap ContentTemplate van een inhoudsbesturingselement of de eigenschap ItemTemplate van een itembesturingselement. Hier volgen twee manieren waarop we een gegevenssjabloon kunnen ontwerpen voor Opname samen met een afbeelding van het resultaat.

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

Een lijstweergave verbinden 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>

Een lijstweergave binden 3

Zie Een gebruikersinterface maken met XAML-voor meer informatie over de syntaxis van XAML. Zie Indelingen definiëren met XAML-voor meer informatie over de besturingselementindeling.

Een detailweergave toevoegen

U kunt ervoor kiezen om alle details van Opname--objecten weer te geven in ListView- items. Maar dat neemt veel ruimte in beslag. In plaats daarvan kunt u voldoende gegevens weergeven in het item om het te identificeren. Wanneer de gebruiker een selectie maakt, kunt u alle details van het geselecteerde item weergeven in een afzonderlijk gedeelte van de gebruikersinterface, ook wel de detailweergave genoemd. Deze weergave wordt ook wel een master-/detailweergave of een lijst-/detailweergave genoemd.

Er zijn twee manieren om dit te doen. U kunt de detailweergave binden aan de eigenschap SelectedItem van de ListView-. U kunt ook een CollectionViewSourcegebruiken. In dat geval verbindt u zowel de ListView- als de detailweergave met de CollectionViewSource- (dit zorgt voor het momenteel geselecteerde item voor u). Beide technieken worden hieronder weergegeven en ze geven beide dezelfde resultaten (weergegeven in de afbeelding).

Opmerking

Tot nu toe hebben we in dit onderwerp alleen de extensie {x:Bind}-markeringengebruikt, maar voor beide technieken die hieronder worden weergegeven, is de flexibelere (maar minder presterende) extensie voor {Binding}-markeringenvereist.

Als u C++/WinRT of Visual C++-onderdeelextensies (C++/CX) gebruikt, moet u, om de {Binding} markupextensie te gebruiken, het kenmerk BindableAttribute- toevoegen aan elke runtimeklasse waarmee u wilt binden. Als u {x:Bind}wilt gebruiken, hebt u dat kenmerk niet nodig.

Belangrijk

Als u C++/WinRT-gebruikt, is het kenmerk BindableAttribute beschikbaar als u de Windows SDK-versie 10.0.17763.0 (Windows 10, versie 1809) of hoger hebt geïnstalleerd. Zonder dat kenmerk moet u de ICustomPropertyProvider en ICustomProperty interfaces implementeren om de {Binding} markeringsextensie te kunnen gebruiken.

Hier is eerst de SelectedItem techniek.

// 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
{
    ...
};

De enige andere wijziging die nodig is, is voor de markering.

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

Gebruik de techniek CollectionViewSource door eerst een CollectionViewSource- als pagina-bron toe te voegen.

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

En pas vervolgens de bindingen aan op de ListView- (die niet meer de naam hoeft te hebben) en in de detailweergave om de CollectionViewSource-te gebruiken. Houd er rekening mee dat u door de detailweergave rechtstreeks aan de CollectionViewSourcete koppelen, u impliceert dat u verbinding wilt maken met het huidige item in bindingen waar het pad niet kan worden gevonden in de verzameling zelf. U hoeft de eigenschap CurrentItem niet op te geven als het pad voor de binding, maar u kunt dit doen als er sprake is van dubbelzinnigheid).

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

En dit is het identieke resultaat in elk geval.

Opmerking

Als u C++ gebruikt, ziet uw gebruikersinterface er niet precies uit zoals in de onderstaande afbeelding: de weergave van de eigenschap ReleaseDateTime verschilt. Zie de volgende sectie voor meer informatie hierover.

Een lijstweergave binden 4

Gegevenswaarden opmaken of converteren voor weergave

Er is een probleem met de bovenstaande rendering. De eigenschap ReleaseDateTime is niet alleen een datum, het is een DateTime- (als u C++gebruikt, is dit een Agenda). In C# wordt het dus met meer precisie weergegeven dan we nodig hebben. En in C++ wordt het weergegeven als een typenaam. Een oplossing is het toevoegen van een tekenreekseigenschap aan de klasse Recording die het equivalent van this.ReleaseDateTime.ToString("d")retourneert. Als u die eigenschap ReleaseDate noemt, geeft dit aan dat er een datum en geen datum en tijd wordt geretourneerd. Door het de naam ReleaseDateAsString te geven, geeft aan dat het een tekenreeks retourneert.

Een flexibelere oplossing is het gebruik van een waardeconversieprogramma. Hier volgt een voorbeeld van het ontwerpen van uw eigen waardeconversieprogramma. Als u C# gebruikt, voegt u de onderstaande code toe aan uw Recording.cs broncodebestand. Als u C++/WinRT gebruikt, voegt u een nieuw Midl-bestand (.idl) item toe aan het project, zoals wordt weergegeven in het onderstaande C++/WinRT-codevoorbeeld, bouwt u het project om StringFormatter.h en .cppte genereren, voegt u deze bestanden toe aan uw project en plakt u de codevermeldingen erin. Voeg ook #include "StringFormatter.h" toe aan 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();
    }
};
...

Opmerking

Voor de bovenstaande C++/WinRT-codevermelding gebruiken we in StringFormatter.idlhet standaardkenmerk om IValueConverter als standaardinterface te declareren. In de vermelding heeft StringFormatter alleen een constructor en geen methoden, zodat er geen standaardinterface voor wordt gegenereerd. Het kenmerk default is optimaal als u geen exemplaarleden toevoegt aan StringFormatter, omdat er geen QueryInterface nodig is om de IValueConverter- methoden aan te roepen. U kunt ook een standaardinterface voor IStringFormatter laten genereren. Dit doet u door aantekeningen te maken bij de runtimeklasse zelf met het default_interface kenmerk. Deze optie is optimaal als u exemplaarleden toevoegt aan StringFormatter- die vaker worden aangeroepen dan de methoden van IValueConverter zijn, omdat er geen QueryInterface nodig is om de exemplaarleden aan te roepen.

Nu kunnen we een exemplaar van StringFormatter toevoegen als een paginaresource en deze gebruiken in de binding van de TextBlock- waarin de eigenschap ReleaseDateTime wordt weergegeven.

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

Zoals u hierboven kunt zien, gebruiken we voor de opmaakflexibiliteit de markup om een notatietekenreeks door te geven aan de converter via de parameter van de converter. In de codevoorbeelden die in dit onderwerp worden weergegeven, maakt alleen het C#-waardeconversieprogramma gebruik van die parameter. U kunt echter eenvoudig een C++-stijl formaatreeks doorgeven als de omzettingsparameter en deze gebruiken in uw waardeomzetter met een formaatfunctie zoals wprintf of swprintf.

Dit is het resultaat.

een datum met aangepaste opmaak weergeven

Opmerking

Vanaf Windows 10 versie 1607 biedt het XAML-framework een ingebouwd Boolean-to-Visibility-conversieprogramma. Het conversieprogramma wijst true toe aan de Visibility.Visible opsommingswaarde en false aan Visibility.Collapsed, zodat u een eigenschap Zichtbaarheid kunt binden aan een Boolean zonder een converter te maken. Als u het ingebouwde conversieprogramma wilt gebruiken, moet de minimale doel-SDK-versie van uw app 14393 of hoger zijn. U kunt deze niet gebruiken wanneer uw app is gericht op eerdere versies van Windows 10. Voor meer informatie over versie-doelstellingen, zie Versie-adaptieve code.

Zie ook