Freigeben über


Übersicht über Windows-Datenbindung

Mit der Datenbindung in WinUI-Apps können Sie Steuerelemente effizient mit Datenquellen verbinden. Erfahren Sie, wie Sie ein Steuerelement an ein einzelnes Element oder eine Sammlung von Elementen binden, das Rendern von Steuerelementelementen steuern, Detailansichten implementieren und Daten für die Anzeige formatieren. Weitere Informationen finden Sie unter "Datenbindung" im Detail.

Voraussetzungen

In diesem Thema wird davon ausgegangen, dass Sie wissen, wie Sie eine einfache WinUI-App mit dem Windows App SDK erstellen. Anweisungen zum Erstellen Ihrer ersten WinUI-App finden Sie unter Erstellen einer WinUI-App.

Projekt erstellen

Erstellen Sie ein neues WinUI Blank App,Paket C#-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 (TextBlock). Die Quelle ist eine Instanz einer einfachen Klasse namens Recording, die eine Audioaufzeichnung darstellt. Befassen wir uns zuerst mit der Klasse.

Fügen Sie Ihrem Projekt eine neue Klasse hinzu, und nennen Sie die Klasse 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();
        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 eine Eigenschaft vom Typ RecordingViewModel zum MainWindow.xaml.cs hinzu.

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

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>

Hier sehen Sie das Ergebnis.

Screenshot einer WinUI-App mit einem TextBlock, der an ein einzelnes Element gebunden ist.

Binden an eine Sammlung von Elementen

Ein häufiges Szenario ist das Binden an eine Sammlung von Geschäftsobjekten. Verwenden Sie in C# die generische ObservableCollection<T-Klasse> für die Datenbindung. Es implementiert die INotifyCollectionChanged-Schnittstelle , die Änderungsbenachrichtigungen für Bindungen bereitstellt, wenn Elemente hinzugefügt oder entfernt werden. Aufgrund eines bekannten WinUI-Versionsmodusfehlers mit .NET 8 und höher müssen Sie in einigen Szenarien möglicherweise eine Liste<T> verwenden, insbesondere, wenn Ihre Sammlung statisch ist und sich nach der Initialisierung nicht ändert. Wenn die Benutzeroberfläche aktualisiert werden muss, wenn sich die Sammlung zur Laufzeit ändert, verwenden Sie ObservableCollection<T>. Wenn Sie nur einen festen Satz von Elementen anzeigen müssen, List<T> reicht dies aus. Wenn Ihre gebundenen Steuerelemente mit Änderungen an Eigenschaften von Objekten in der Auflistung aktualisiert werden sollen, sollten diese Objekte INotifyPropertyChanged implementieren. Weitere Informationen finden Sie unter Datenbindung im Detail.

Anmerkung

Durch die Verwendung List<T>erhalten Sie möglicherweise keine Änderungsbenachrichtigungen für Sammlungsänderungen. Wenn Sie auf Änderungen reagieren müssen, sollten Sie die Verwendung in Betracht ziehen ObservableCollection<T>. In diesem Beispiel brauchen Sie nicht auf Änderungen zu reagieren, sodass List<T> ausreichend ist.

Im folgenden Beispiel wird eine ListView an eine Auflistung von Recording Objekten gebunden. Fügen Sie zunächst die Auflistung zu Ihrem Ansichtsmodell hinzu. Fügen Sie diese neuen Member zur RecordingViewModel Klasse hinzu.

public class RecordingViewModel
{
    ...
    private List<Recording> recordings = new();
    public List<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>

Sie haben noch keine Datenvorlage für die Recording Klasse bereitgestellt, daher kann das Benutzeroberflächen-Framework lediglich ToString für jedes Element in der ListView aufrufen. Die Standardimplementierung des ToString Typs gibt den Typnamen zurück.

Binden einer Listenansicht 1

Um dieses Problem zu beheben, können Sie entweder ToString außer Kraft setzen, um den Wert zurückzugeben OneLineSummary, oder Sie können eine Datenvorlage bereitstellen. Die Datenvorlagenoption ist eine häufigere und flexiblere Lösung. Sie legen eine Datenvorlage mithilfe der ContentTemplate-Eigenschaft eines Inhaltssteuerelements oder mit der ItemTemplate-Eigenschaft eines Elementsteuerelements fest. Hier sind zwei Möglichkeiten, wie Sie eine Datenvorlage für Recording entwerfen können, 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 Steuerungslayout finden Sie unter Definieren von Layouts mit XAML.

Hinzufügen einer Detailansicht

Sie können auswählen, dass alle Details von Recording Objekten in ListView- Elementen angezeigt werden sollen. Aber dieser Ansatz nimmt viel Platz ein. Stattdessen können Sie nur genügend Daten im Element anzeigen, um es zu identifizieren. Wenn der Benutzer eine Auswahl trifft, können Sie alle Details des ausgewählten Elements in einer separaten Benutzeroberfläche anzeigen, die als Detailansicht bezeichnet wird. Diese Anordnung wird auch als Haupt-/Detailansicht oder als Listen-/Detailansicht bezeichnet.

Sie können diese Anordnung auf zwei Arten implementieren. 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 das ListView als auch die Detailansicht an das CollectionViewSource. Dieser Ansatz kümmert sich um das aktuell ausgewählte Element für Sie. Beide Techniken werden in den folgenden Abschnitten gezeigt, und beide weisen die gleichen Ergebnisse auf (in der Abbildung dargestellt).

Anmerkung

Bisher haben Sie in diesem Thema nur die {x:Bind}-Markuperweiterung verwendet. Beide Techniken in den folgenden Abschnitten erfordern 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>

Bei der CollectionViewSource-Technik fügen Sie zuerst eine CollectionViewSource als Ressource des obersten Grid hinzu.

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

Anmerkung

Die Window-Klasse in WinUI verfügt nicht über eine Resources-Eigenschaft. Sie können die CollectionViewSource stattdessen dem obersten Grid (oder einem anderen übergeordneten UI-Element wie StackPanel) hinzufügen. Wenn Sie in einer Page arbeiten, können Sie die CollectionViewSource den Page.Resources hinzufügen.

Passen Sie dann die Bindungen in der ListView (die nicht mehr benannt werden muss) und die Detailansicht an, um die CollectionViewSource zu verwenden. Indem Sie die Detailansicht direkt an die CollectionViewSourceDetailansicht binden, bedeutet dies, dass Sie eine Bindung an das aktuelle Element in Bindungen herstellen möchten, bei denen der Pfad in der Sammlung selbst nicht gefunden werden kann. 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

Das obige Rendering hat ein Problem. Die ReleaseDateTime Eigenschaft ist nicht nur ein Datum, es ist ein DateTime. Es wird also präziser angezeigt, als Sie benötigen. Eine Lösung besteht darin, der Recording Klasse eine Zeichenfolgeneigenschaft hinzuzufügen, die das Äquivalent von ReleaseDateTime.ToString("d")zurückgibt. Die Benennung dieser Eigenschaft ReleaseDate gibt an, dass sie ein Datum und kein Datum und keine Uhrzeit zurückgibt. Wenn sie ReleaseDateAsString weiter benannt wird, wird angegeben, dass sie eine Zeichenfolge zurückgibt.

Eine flexiblere Lösung besteht darin, einen Wertkonverter zu verwenden. Hier ein Beispiel, wie Sie einen eigenen Wertkonverter verfassen. Fügen Sie der Recording.cs Quellcodedatei 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();
    }
}

Jetzt können Sie eine Instanz von StringFormatter als Ressource hinzufügen und diese in der Bindung des TextBlock verwenden, das die ReleaseDateTime-Eigenschaft anzeigt.

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

Wie Sie sehen können, übergibt das Markup eine Formatzeichenfolge mithilfe des Konverterparameters an den Konverter. Im in diesem Thema gezeigten Codebeispiel verwendet der C#-Wertkonverter diesen Parameter.

Hier sehen Sie das Ergebnis.

Anzeige eines Datums mit benutzerdefinierter Formatierung

Unterschiede zwischen Bindung und x:Bind

Beim Arbeiten mit der Datenbindung in WinUI-Apps treten möglicherweise zwei primäre Bindungsmechanismen auf: Binding und x:Bind. Beide dienen zwar dem Verbinden von UI-Elementen mit Datenquellen, weisen jedoch unterschiedliche Unterschiede auf:

  • x:Bind: Bietet Kompilierungszeitüberprüfung, bessere Leistung und ist stark typiert. Es ist ideal für Szenarien, in denen Sie die Datenstruktur zur Kompilierungszeit kennen.
  • Binding: Bietet Laufzeitauswertung und ist flexibler für dynamische Szenarien, z. B. wenn die Datenstruktur zur Kompilierungszeit nicht bekannt ist.

Szenarien, die von x:Bind nicht unterstützt werden

Zwar ist x:Bind leistungsstark, aber Sie können es in bestimmten Szenarien nicht verwenden:

  • Dynamische Datenstrukturen: Wenn die Datenstruktur zur Kompilierungszeit nicht bekannt ist, können Sie dies nicht verwenden x:Bind.
  • Element-zu-Element-Bindung: x:Bind Unterstützt die Bindung nicht direkt zwischen zwei UI-Elementen.
  • Bindung an ein DataContext: x:Bind erbt nicht automatisch das DataContext eines übergeordneten Elements.
  • Bidirektionale Bindungen mit Mode=TwoWay: Obwohl unterstützt wird, erfordert x:Bind eine explizite Implementierung von INotifyPropertyChanged für jede Eigenschaft, die die Benutzeroberfläche aktualisieren soll, wenn sich die Quelle ändert, unabhängig davon, ob eine unidirektionale oder bidirektionale Bindung verwendet wird. Der Hauptunterschied bei bidirektionale Bindungen besteht darin, dass Änderungen auch von der Benutzeroberfläche zurück zur Quelle fließen.

Praktische Beispiele und ein tieferes Verständnis der jeweiligen Verwendung finden Sie in den folgenden Themen: