Freigeben über


Übersicht über Windows-Datenbindung

In diesem Thema erfahren Sie, wie Sie in einer Windows App SDK-App ein Steuerelement (oder ein anderes Benutzeroberflächenelement) an ein einzelnes Element oder ein Elementsteuerelement an eine Sammlung von Elementen binden. Darüber hinaus wird erläutert, wie Sie die Anzeige von Elementen steuern, eine Detailansicht auf Grundlage einer Auswahl implementieren und Daten für die Anzeige umwandeln. Ausführliche Informationen finden Sie unter Datenbindung im Detail.

Voraussetzungen

In diesem Thema wird vorausgesetzt, dass Sie mit dem Erstellen einer einfachen Windows App SDK-App vertraut sind. Eine Anleitung für die Erstellung Ihrer ersten Windows App SDK-App finden Sie unter Erstellen Ihres ersten WinUI 3-Projekts (Windows App SDK).

Erstellen des Projekts

Erstellen Sie ein neues C#-Projekt vom Typ Leere App, Gepackt (WinUI 3 in Desktop). Nennen Sie sie „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 anderen Benutzeroberflächenelements, und die Quelle ist eine Eigenschaft einer Klasseninstanz (ein Datenmodell oder ein Ansichtsmodell). In diesem Beispiel wird veranschaulicht, wie Sie ein Steuerelement an ein einzelnes Element binden. Das Ziel ist die Eigenschaft Text eines Textblocks (TextBlock). Die Quelle ist eine Instanz einer einfachen Klasse namens Recording, die eine Audioaufnahme darstellt. Befassen wir uns zuerst mit der Klasse.

Fügen Sie Ihrem Projekt eine neue Klasse hinzu, und nennen Sie sie Recording.

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

Machen Sie als Nächstes die Bindungsquellklasse aus der Klasse verfügbar, die Ihr Markupfenster darstellt. Fügen Sie hierzu eine Eigenschaft vom Typ RecordingViewModel zu MainWindow.xaml.cs hinzu.

namespace Quickstart
{
    public sealed partial class MainWindow : Window
    {
        public MainWindow()
        {
            this.InitializeComponent();
            ViewModel = new RecordingViewModel();
        }
        public RecordingViewModel ViewModel{ get; set; }
    }
}

Der letzte Codeteil ist das Binden eines TextBlock an die ViewModel.DefaultRecording.OneLineSummary-Eigenschaft.

<Window x:Class="Quickstart.MainWindow" ... >
    <Grid>
        <TextBlock Text="{x:Bind ViewModel.DefaultRecording.OneLineSummary}"
    HorizontalAlignment="Center"
    VerticalAlignment="Center"/>
    </Grid>
</Window>

Dies ist das Ergebnis.

Binden eines Textblocks

Binden an eine Sammlung von Elementen

Ein häufiges Szenario ist das Binden an eine Sammlung von Geschäftsobjekten. In C# stellt die generische Klasse ObservableCollection<T> eine gute Wahl für die Datenbindung bei Sammlungen dar, da sie die Schnittstellen INotifyPropertyChanged und INotifyCollectionChanged implementiert. Diese Schnittstellen bieten eine Änderungsbenachrichtigung für Bindungen, wenn Elemente hinzugefügt oder entfernt werden oder eine Eigenschaft der Liste selbst geändert wird. Wenn Ihre gebundenen Steuerelemente bei Änderungen an Eigenschaften von Objekten in der Sammlung aktualisiert werden sollen, muss das Geschäftsobjekt auch INotifyPropertyChanged implementieren. Weitere Informationen finden Sie unter Datenbindung im Detail.

In diesem nächsten Beispiel wird eine ListView an eine Sammlung von Recording-Objekten gebunden. Beginnen wir, indem wir die Sammlung zum Ansichtsmodell hinzufügen. Fügen Sie einfach diese neuen Member zur RecordingViewModel-Klasse hinzu.

public class RecordingViewModel
{
    ...
    private ObservableCollection<Recording> recordings = new ObservableCollection<Recording>();
    public ObservableCollection<Recording> Recordings{ get{ return recordings; } }
    public RecordingViewModel()
    {
        recordings.Add(new Recording(){ ArtistName = "Johann Sebastian Bach",
            CompositionName = "Mass in B minor", ReleaseDateTime = new DateTime(1748, 7, 8) });
        recordings.Add(new Recording(){ ArtistName = "Ludwig van Beethoven",
            CompositionName = "Third Symphony", ReleaseDateTime = new DateTime(1805, 2, 11) });
        recordings.Add(new Recording(){ ArtistName = "George Frideric Handel",
            CompositionName = "Serse", ReleaseDateTime = new DateTime(1737, 12, 3) });
    }
}

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

<Window x:Class="Quickstart.MainWindow" ... >
    <Grid>
        <ListView ItemsSource="{x:Bind ViewModel.Recordings}"
        HorizontalAlignment="Center" VerticalAlignment="Center"/>
    </Grid>
</Window>

Wir haben noch keine Datenvorlage für die Recording-Klasse bereitgestellt. Daher kann das Benutzeroberflächenframework nur ToString für jedes Element in der ListView aufrufen. Die Standardimplementierung von ToString ist die Rückgabe des Typnamens.

Binden einer Listenansicht 1

Um dies zu beheben, können wir entweder ToString so überschreiben, dass der Wert von OneLineSummary zurückgegeben wird, oder wir können eine Datenvorlage bereitstellen. Die Datenvorlagenoption ist eine gängigere Lösung, die außerdem flexibler ist. Sie legen eine Datenvorlage mithilfe der ContentTemplate-Eigenschaft eines Inhaltssteuerelements oder mit der ItemTemplate-Eigenschaft eines Elementsteuerelements fest. Nachfolgend sind zwei Möglichkeiten zum Entwerfen einer Datenvorlage für Recording dargestellt, 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>

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

Binden einer Listenansicht 3

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

Hinzufügen einer Detailansicht

Sie können auch alle Details der Recording-Objekte in ListView-Elementen anzeigen. Dies nimmt jedoch sehr viel Platz in Anspruch. Stattdessen können Sie gerade so viele Daten im Element anzeigen, um es zu identifizieren, und wenn der Benutzer dann eine Auswahl vornimmt, können Sie alle Details des ausgewählten Elements in einem separaten Teil der Benutzeroberfläche anzeigen, der als „Detailansicht“ bezeichnet wird. Diese Anordnung wird auch als „Haupt-/Detailansicht“ oder „Listen-/Detailansicht“ bezeichnet.

Sie haben zwei Möglichkeiten, dieses Verhalten zu implementieren. Sie können die Detailansicht an die SelectedItem-Eigenschaft der ListView binden. Alternativ kannst du eine CollectionViewSource verwenden. In diesem Fall bindest du dann sowohl die ListView als auch die Detailansicht an die CollectionViewSource (hierdurch wird das derzeit ausgewählte Element für dich behandelt). Die beiden Methoden sind unten aufgeführt, und haben dieselben Ergebnisse (in der Abbildung dargestellt).

Hinweis

Bisher haben wir in diesem Thema nur die {x:Bind}-Markuperweiterung verwendet. Die beiden weiter unten aufgeführten Methoden benötigen jedoch die flexiblere (aber weniger leistungsfähige) {Binding}-Markuperweiterung.

Sehen wir uns zuerst die SelectedItem-Methode an. Bei einer C#-Anwendung muss lediglich das Markup geändert werden.

<Window x:Class="Quickstart.MainWindow" ... >
    <Grid>
        <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>
</Window>

Fügen Sie für die Methode CollectionViewSource zunächst ein Element vom Typ CollectionViewSource als Fensterressource hinzu.

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

Passen Sie dann die Bindungen in der ListView (die nicht mehr benannt werden muss) und in der Detailansicht so an, dass die CollectionViewSource verwendet wird. Beachten Sie, dass Sie durch die direkte Bindung der Detailansicht an die CollectionViewSource implizieren, dass Sie in Bindungen, in denen der Pfad in der Sammlung selbst nicht gefunden werden kann, an das aktuelle Element binden möchten. Die CurrentItem-Eigenschaft muss nicht als Pfad für die Bindung angegeben werden, obwohl dies im Zweifelsfall möglich ist.

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

Nachfolgend ist das identische Ergebnis für die beiden Methoden dargestellt.

Binden einer Listenansicht 4

Formatieren oder Konvertieren von Datenwerten für die Anzeige

Es gibt ein Problem mit dem oben erwähnten Rendering. Die Eigenschaft ReleaseDateTime ist nicht nur ein Datum, sondern ein Datum und eine Uhrzeit (DateTime). Sie wird also mit höherer Genauigkeit angezeigt, als wir benötigen. Eine Lösung besteht darin, eine Zeichenfolgeneigenschaft zur Recording-Klasse hinzuzufügen, die das Äquivalent von ReleaseDateTime.ToString("d") zurückgibt. Das Benennen dieser Eigenschaft als ReleaseDate, würde anzeigen, dass sie ein Datum und nicht ein Datum mit Uhrzeit zurückgibt. Die Benennung als ReleaseDateAsString gibt dann auch noch an, dass sie eine Zeichenfolge zurückgibt.

Eine flexiblere Lösung ist die Verwendung eines so genannten „Wertkonverters“. Nachfolgend ist ein Beispiel zum Erstellen Ihres eigenen Wertkonverter aufgeführt. Fügen Sie Ihrer Quellcodedatei Recording.cs den folgenden Code hinzu:

public class StringFormatter : Microsoft.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();
    }
}

Nun können wir eine Instanz von StringFormatter als Seitenressource hinzufügen und sie in der Bindung von TextBlock verwenden, in dem die ReleaseDateTime-Eigenschaft angezeigt wird.

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

Wie du oben sehen kannst, verwenden wir für die Flexibilität der Formatierung das Markup, um eine Formatzeichenfolge mithilfe des Konverterparameters in den Konverter zu übergeben. Im hier gezeigten Codebeispiel wird dieser Parameter vom C#-Wertkonverter verwendet.

Dies ist das Ergebnis.

Anzeigen eines Datums mit benutzerdefinierter Formatierung

Siehe auch