Freigeben über


Übersicht über Datenbindung

In diesem Thema wird gezeigt, wie Sie ein Steuerelement (oder ein anderes UI-Element) an ein einzelnes Element binden oder ein Elementsteuerelement an eine Sammlung von Elementen in einer UWP-App (Universelle Windows-Plattform) binden. Darüber hinaus zeigen wir, wie sie das Rendern von Elementen steuern, eine Detailansicht basierend auf einer Auswahl implementieren und Daten für die Anzeige konvertieren. Ausführlichere Informationen finden Sie unter Datenbindung im Detail.

Voraussetzungen

In diesem Thema wird davon ausgegangen, dass Sie wissen, wie Sie eine einfache UWP-App erstellen. Anweisungen zum Erstellen Ihrer ersten UWP-App finden Sie unter Erste Schritte mit Windows-Apps.

Erstelle das Projekt

Erstellen Sie ein neues Leere Anwendung (Windows Universal) Projekt. Nennen Sie ihn "Schnellstart".

Binden an ein einzelnes Element

Jede Bindung besteht aus einem Bindungsziel und einer Bindungsquelle. In der Regel ist das Ziel eine Eigenschaft eines Steuerelements oder eines anderen UI-Elements, und die Quelle ist eine Eigenschaft einer Klasseninstanz (ein Datenmodell oder ein Ansichtsmodell). In diesem Beispiel wird gezeigt, wie ein Steuerelement an ein einzelnes Element gebunden wird. Das Ziel ist die Eigenschaft Text eines TextBlocks . Die Quelle ist eine Instanz einer einfachen Klasse mit dem Namen Recording, die eine Audioaufzeichnung darstellt. Sehen wir uns zuerst die Klasse an.

Wenn Sie C# oder C++/CX verwenden, fügen Sie Ihrem Projekt eine neue Klasse hinzu, und nennen Sie die Klasse Recording.

Wenn Sie C++/WinRTverwenden, fügen Sie dem Projekt neue Midl-Datei (.idl)--Elemente hinzu, wie im unten stehenden C++/WinRT-Codebeispiel gezeigt. Ersetzen Sie den Inhalt dieser neuen Dateien durch den MIDL 3.0- Code, der in der Auflistung angezeigt wird, erstellen Sie das Projekt, um Recording.h und .cpp und RecordingViewModel.h und .cppzu generieren, und fügen Sie dann Code zu den generierten Dateien hinzu, um der Auflistung zu entsprechen. Weitere Informationen zu diesen generierten Dateien und zum Kopieren in Ihr Projekt finden Sie unter XAML-Steuerelemente; binden an eine C++/WinRT-Eigenschaft.

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"

Stellen Sie als nächsten Schritt die Bindungsquellenklasse aus der Klasse, die Ihre Markupseite repräsentiert, zur Verfügung. Dazu fügen wir eine Eigenschaft vom Typ RecordingViewModel zu MainPage-hinzu.

Wenn Sie C++/WinRTverwenden, aktualisieren Sie zuerst MainPage.idl. Erstellen Sie das Projekt, um MainPage.h und .cppneu zu generieren, und führen Sie die Änderungen dieser generierten Dateien in den Dateien in Ihrem Projekt zusammen.

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

Der letzte Teil besteht darin, einen TextBlock an die ViewModel.DefaultRecording.OneLineSummary-Eigenschaft zu binden.

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

Wenn Sie C++/WinRTverwenden, müssen Sie die MainPage::ClickHandler-Funktion entfernen, damit das Projekt erstellt werden kann.

Hier sehen Sie das Ergebnis.

Binden eines Textblocks

Anbindung an eine Sammlung von Elementen

Ein häufiges Szenario ist die Bindung an eine Sammlung von Geschäftsobjekten. In C# und Visual Basic ist die generische ObservableCollection<T> Klasse eine gute Sammlungsauswahl für die Datenbindung, da sie die INotifyPropertyChanged und INotifyCollectionChanged Schnittstellen implementiert. Diese Schnittstellen stellen Änderungsbenachrichtigungen für Bindungen bereit, wenn Elemente hinzugefügt oder entfernt werden oder eine Eigenschaft der Liste selbst geändert wird. Wenn Ihre gebundenen Steuerelemente mit Änderungen an Eigenschaften von Objekten in der Auflistung aktualisiert werden sollen, sollte das Geschäftsobjekt auch INotifyPropertyChangedimplementieren. Weitere Informationen finden Sie unter Datenbindung im Detail.

Wenn Sie C++/WinRT-verwenden, erfahren Sie mehr über die Bindung an eine feststellbare Auflistung in XAML-Elementsteuerelementen; binden an eine C++/WinRT-Auflistung. Wenn Sie dieses Thema zuerst lesen, ist die Absicht der unten gezeigten C++/WinRT-Codeauflistung klarer.

Im nächsten Beispiel wird eine ListView- mit einer Sammlung von Recording-Objekten verknüpft. Beginnen wir mit dem Hinzufügen der Sammlung zu unserem Ansichtsmodell. Fügen Sie einfach diese neuen Mitglieder der Klasse RecordingViewModel hinzu.

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

Binden Sie dann die ListView an die Eigenschaft ViewModel.Recordings.

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

Wir haben noch keine Datenvorlage für die Klasse Recording bereitgestellt, daher kann das Beste, was das UI-Framework tun kann, darin bestehen, ToString- für jedes Element in der ListView-aufzurufen. Die Standardimplementierung von ToString- besteht darin, den Typnamen zurückzugeben.

Anbinden einer Listenansicht 1

Um dies zu beheben, können wir entweder ToString außer Kraft setzen, um den Wert von OneLineSummaryzurückzugeben, oder wir können eine Datenvorlage bereitstellen. Die Datenvorlagenoption ist eine gängigere Lösung und eine flexiblere Lösung. Sie geben eine Datenvorlage mithilfe der ContentTemplate--Eigenschaft eines Inhaltssteuerelements oder der ItemTemplate--Eigenschaft eines Elementsteuerelements an. Im Folgenden zeigen wir Ihnen zwei Möglichkeiten, eine Datenvorlage für die Aufnahme zu entwerfen, zusammen mit einer Abbildung des Ergebnisses.

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

Verknüpfen einer Listenansicht 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>

Bindung einer Listenansicht 3

Weitere Informationen zur XAML-Syntax finden Sie unter Erstellen einer Benutzeroberfläche mit XAML-. Weitere Informationen zur Layoutsteuerung finden Sie unter Definieren von Layouts mit XAML.

Hinzufügen einer Detailansicht

Sie können auswählen, alle Details der Recording-Objekte in den ListView---Elementen anzuzeigen. Aber das beansprucht viel Platz. Stattdessen können Sie nur genügend Daten im Element anzeigen, um es zu identifizieren, und dann können Sie, wenn der Benutzer eine Auswahl trifft, alle Details des ausgewählten Elements in einem separaten Ui-Element anzeigen, das als Detailansicht bezeichnet wird. Diese Anordnung wird auch als Haupt-/Detailansicht oder als Listen-/Detailansicht bezeichnet.

Dazu gibt es zwei Möglichkeiten. Sie können die Detailansicht an die SelectedItem-Eigenschaft der ListView-binden. Sie können auch eine CollectionViewSource-verwenden; in diesem Fall binden Sie sowohl die ListView- als auch die Detailansicht an die CollectionViewSource-. Dadurch wird das aktuell ausgewählte Element automatisch für Sie verwaltet. Beide Techniken werden unten dargestellt, und beide weisen die gleichen Ergebnisse auf (in der Abbildung dargestellt).

Hinweis

Bisher haben wir in diesem Thema nur die {x:Bind}-Markuperweiterungverwendet, aber beide Techniken, die unten gezeigt werden, erfordern die flexiblere (aber weniger leistungsfähige) {Binding}-Markuperweiterung.

Wenn Sie C++/WinRT oder die Visual C++-Komponentenerweiterungen (C++/CX) verwenden, müssen Sie, um die {Binding}--Markuperweiterung zu verwenden, das BindableAttribute-Attribut zu jeder Laufzeitklasse hinzufügen, an die Sie eine Bindung vornehmen möchten. Um {x:Bind}-zu verwenden, benötigen Sie dieses Attribut nicht.

Von Bedeutung

Wenn Sie C++/WinRTverwenden, ist das Bindable-Attribut verfügbar, wenn Sie die Windows SDK-Version 10.0.17763.0 (Windows 10, Version 1809) oder eine höhere Version installiert haben. Ohne dieses Attribut müssen Sie die ICustomPropertyProvider-- und ICustomProperty--Schnittstellen implementieren, um die {Binding}--Markup-Erweiterung verwenden zu können.

Zunächst haben wir die Technik für das ausgewählte Element ().

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

Die einzige andere Änderung ist für das Markup erforderlich.

<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ügen Sie für die CollectionViewSource--Methode zunächst eine CollectionViewSource- als Seitenressource hinzu.

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

Passen Sie dann die Bindungen für die ListView- (die nicht mehr benannt werden muss) und die Detailansicht an, um die CollectionViewSource-zu verwenden. Beachten Sie, dass Sie durch direktes Binden der Detailansicht an das CollectionViewSource-implizieren, dass Sie eine Bindung an das aktuelle Element in Bindungen herstellen möchten, in denen der Pfad in der Auflistung selbst nicht gefunden werden kann. Es ist nicht erforderlich, die CurrentItem-eigenschaft als Pfad für die Bindung anzugeben, obwohl Sie dies tun können, wenn eine Mehrdeutigkeit vorhanden ist).

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

Und hier ist das gleiche Ergebnis in allen Fällen.

Hinweis

Wenn Sie C++ verwenden, sieht Ihre UI nicht genau so aus, wie in der Abbildung unten dargestellt: Die Darstellung der ReleaseDateTime--Eigenschaft ist unterschiedlich. Weitere Informationen hierzu finden Sie im folgenden Abschnitt.

Einbinden einer Listenansicht 4

Formatieren oder Konvertieren von Datenwerten für die Anzeige

Es gibt ein Problem mit dem obigen Rendering. Die ReleaseDateTime--Eigenschaft ist nicht nur ein Datum, sondern ein DateTime (wenn Sie C++ verwenden, dann ist sie ein Calendar). In C# wird es also präziser angezeigt, als wir brauchen. Und in C++ wird es als Typname gerendert. Eine Lösung besteht darin, der klasse Recording eine Zeichenfolgeneigenschaft hinzuzufügen, die das Äquivalent von this.ReleaseDateTime.ToString("d")zurückgibt. Die Benennung dieser Eigenschaft ReleaseDate würde darauf hinweisen, dass sie ein Datum und nicht Datum und Zeit zurückgibt. Durch die Benennung ReleaseDateAsString- würde weiter angegeben, dass sie eine Zeichenfolge zurückgibt.

Eine flexiblere Lösung besteht darin, einen Wertkonverter zu verwenden. Hier ist ein Beispiel, wie man einen eigenen Wertkonverter erstellt. Wenn Sie C# verwenden, fügen Sie der Recording.cs Quellcodedatei den folgenden Code hinzu. Wenn Sie C++/WinRT verwenden, fügen Sie dem Projekt ein neues Midl File (.idl) Element hinzu, benannt wie im C++/WinRT-Codebeispiel unten gezeigt. Erstellen Sie dann das Projekt, um StringFormatter.h und .cppzu generieren, fügen Sie diese Dateien zu Ihrem Projekt hinzu und kopieren Sie anschließend die Codeauflistungen in diese Dateien. Fügen Sie auch #include "StringFormatter.h" zu MainPage.hhinzu.

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

Hinweis

Für den obigen C++/WinRT-Codeeintrag verwenden wir in StringFormatter.idldas Standardattribute, um IValueConverter- als Standardschnittstelle zu deklarieren. In der Liste hat StringFormatter nur einen Konstruktor und keine Methoden, sodass keine Standardschnittstelle dafür generiert wird. Das default-Attribut ist optimal, wenn Sie keine Instanzmitglieder zu -StringFormatter-hinzufügen, da kein QueryInterface erforderlich ist, um die -IValueConverter--Methoden aufzurufen. Alternativ können Sie eine standardmäßige IStringFormatter- Schnittstelle zum Generieren auffordern, und Sie können dies tun, indem Sie die Laufzeitklasse selbst mit dem default_interface Attributkommentieren. Diese Option ist optimal, wenn Sie Instanzmitglieder zu StringFormatter hinzufügen, die häufiger aufgerufen werden als die Methoden von IValueConverter, da dann keine QueryInterface erforderlich ist, um die Instanzmitglieder aufzurufen.

Jetzt können wir eine Instanz von StringFormatter- als Seitenressource hinzufügen und sie bei der Bindung des TextBlock- verwenden, der die ReleaseDateTime--Eigenschaft anzeigt.

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

Wie Sie oben sehen können, nutzen wir das Markup für mehr Flexibilität bei der Formatierung, indem wir eine Formatzeichenfolge über den Konverterparameter an den Konverter übergeben. In den codebeispielen in diesem Thema verwendet nur der C#-Wertkonverter diesen Parameter. Sie können jedoch problemlos eine Formatzeichenfolge im Stil von C++ als Konverterparameter übergeben und diese in Ihrem Wertkonverter mit einer Formatierungsfunktion wie wprintf oder swprintfverwenden.

Hier sehen Sie das Ergebnis.

Anzeigen eines Datums mit benutzerdefinierter Formatierung

Hinweis

Ab Windows 10, Version 1607, bietet das XAML-Framework einen integrierten boolean-to-Visibility-Konverter. Der Konverter mappt true auf den Enumerationswert Visibility.Visible und false auf Visibility.Collapsed, sodass Sie eine Visibility-Eigenschaft an einen booleschen Wert binden können, ohne einen Konverter erstellen zu müssen. Um den integrierten Konverter zu verwenden, muss die Mindestziel-SDK-Version Ihrer App 14393 oder höher sein. Sie können sie nicht verwenden, wenn Ihre App auf frühere Versionen von Windows 10 ausgerichtet ist. Weitere Informationen zu Zielversionen finden Sie unter version-adaptiven Code.

Siehe auch