Freigeben über


Windows-Datenbindung im Detail

In diesem Artikel werden die Windows App SDK-Datenbindungsfeatures für die APIs aus dem Namespace Microsoft.UI.Xaml.Data beschrieben.

Hinweis

In diesem Thema werden die Datenbindungsfeatures ausführlich beschrieben. Eine kurze und praktische Einführung finden Sie unter Übersicht „Datenbindung“.

Wichtige APIs

Einführung

Die Datenbindung ist eine Methode, mit der die Benutzeroberfläche Ihrer App Daten anzeigen und diese Daten optional synchronisieren kann. Mit der Datenbindung können Sie Datenaspekte von Benutzeroberflächenaspekten trennen, was zu einem einfacheren konzeptionellen Modell und besserer Lesbarkeit, Testbarkeit und Wartung Ihrer App führt.

Sie können die Datenbindung einfach verwenden, um nur Werte aus einer Datenquelle anzuzeigen, wenn die Benutzeroberfläche das erste Mal angezeigt wird, jedoch nicht auf Änderungen an diesen Werten reagieren. Dieser Bindungsmodus wird als einmalig bezeichnet und funktioniert gut bei Werten, die sich während der Laufzeit nicht ändern. Alternativ können Sie die Werte auch „beobachten“ und die Benutzeroberfläche bei einer Änderung aktualisieren. Dieser Modus wird als unidirektional bezeichnet und eignet sich gut für schreibgeschützte Daten. Letztendlich können Sie die Daten sowohl feststellen als auch aktualisieren, damit die vom Benutzer in der Benutzeroberfläche vorgenommenen Änderungen automatisch in die Datenquelle übernommen werden. Dieser Modus wird als bidirektionale bezeichnet und eignet sich gut für Daten mit Lese-/Schreibberechtigung. Beispiele:

  • Sie können den einmaligen Modus verwenden, um ein Image an das Foto des aktuellen Benutzers zu binden.
  • Sie können den unidirektionalen Modus verwenden, um ein ListView-Objekt an eine Sammlung von Nachrichtenartikeln in Echtzeit nach Zeitungsteil gruppiert zu binden.
  • Sie könnten den bidirektionalen Modus verwenden, um ein TextBox-Objekt an den Namen eines Kunden in einem Formular zu binden.

Unabhängig vom Modus gibt es zwei Arten von Bindungen. Diese werden in der Regel beide im Benutzeroberflächenmarkup deklariert. Sie können entweder die {x:Bind}-Markuperweiterung oder die {Binding}-Markuperweiterung verwenden. Sie können sogar eine Mischung aus den beiden in derselben App verwenden – sogar im gleichen Benutzeroberflächenelement. {x:Bind} war in der UWP für Windows 10 neu und zeichnet sich durch eine bessere Leistung aus. Alle in diesem Thema beschriebenen Details gelten für beide Arten von Bindungen, es sei denn, es wird ausdrücklich auf eine Abweichung hingewiesen.

UWP-Beispiel-Apps zur Veranschaulichung von {x:Bind}

UWP-Beispiel-Apps zur Veranschaulichung von {Binding}

Was zu jeder Bindung gehört

  • Eine Bindungsquelle: Dies ist die Quelle der Daten für die Bindung und kann eine Instanz einer Klasse mit Membern sein, deren Werte Sie in der Benutzeroberfläche anzeigen möchten.
  • Ein Bindungsziel: Dies ist eine DependencyProperty des FrameworkElement in Ihrer Benutzeroberfläche, die die Daten anzeigt.
  • Ein Bindungsobjekt: Dies ist der Teil, der Datenwerte von der Quelle an das Ziel sowie optional vom Ziel zurück an die Quelle überträgt. Das Bindungsobjekt wird zur XAML-Ladezeit von der {x:Bind}- oder {Binding}-Markuperweiterung erstellt.

In den folgenden Abschnitten betrachten wird die Bindungsquelle, das Bindungsziel und das Bindungsobjekt genauer. Außerdem verknüpfen wir die Abschnitte durch ein Beispiel, bei dem der Inhalt einer Schaltfläche an eine Zeichenfolgeneigenschaft mit dem Namen NextButtonText gebunden wird, die zur Klasse HostViewModel gehört.

Bindungsquelle

Nachfolgend wird eine sehr einfache Implementierung einer Klasse gezeigt, die wir als Bindungsquelle verwenden könnten.

public class HostViewModel
{
    public HostViewModel()
    {
        NextButtonText = "Next";
    }

    public string NextButtonText { get; set; }
}

Diese Implementierung der HostViewModel-Klasse und der zugehörigen NextButtonText-Eigenschaft ist nur für eine einmalige Bindung geeignet. Unidirektionale und bidirektionale Bindungen sind jedoch sehr häufig, und bei diesen Arten von Bindungen wird die Benutzeroberfläche als Reaktion auf Änderungen bei den Datenwerten der Bindungsquelle automatisch aktualisiert. Damit diese Bindungen ordnungsgemäß funktionieren, muss die Bindungsquelle für das Bindungsobjekt beobachtbar sein. Wenn wir in unserem Beispiel also eine unidirektionale oder bidirektionale Bindung an die NextButtonText-Eigenschaft anwenden möchten, müssen alle Änderungen, die zur Laufzeit am Wert dieser Eigenschaft vorgenommen werden, für das Bindungsobjekt feststellbar sein.

Eine Möglichkeit für die Umsetzung besteht darin, die Klasse, die die Bindungsquelle darstellt, von DependencyObject abzuleiten und einen Datenwert über eine DependencyProperty verfügbar zu machen. Auf diese Weise wird ein FrameworkElement feststellbar. Ein Framework-Element (FrameworkElement) ist eine gute standardmäßige Bindungsquelle.

Eine einfachere Möglichkeit, eine Klasse feststellbar zu machen – und eine Notwendigkeit für Klassen, die bereits über eine Basisklasse verfügen – besteht in der Implementierung von System.ComponentModel.INotifyPropertyChanged. Hierzu muss lediglich ein einzelnes Ereignis mit dem Namen PropertyChanged implementiert werden. Ein Beispiel für die Verwendung von HostViewModel finden Sie weiter unten.

...
using System.ComponentModel;
using System.Runtime.CompilerServices;
...
public class HostViewModel : INotifyPropertyChanged
{
    private string nextButtonText;

    public event PropertyChangedEventHandler PropertyChanged = delegate { };

    public HostViewModel()
    {
        NextButtonText = "Next";
    }

    public string NextButtonText
    {
        get { return nextButtonText; }
        set
        {
            nextButtonText = value;
            OnPropertyChanged();
        }
    }

    public void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        // Raise the PropertyChanged event, passing the name of the property whose value has changed.
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

Die NextButtonText-Eigenschaft ist jetzt feststellbar. Wenn Sie eine uni- oder bidirektionale Bindung an diese Eigenschaft erstellen (was weiter unten genauer gezeigt wird), abonniert das sich ergebende Bindungsobjekt das PropertyChanged-Ereignis. Wenn das Ereignis ausgelöst wird, erhält der Handler des Bindungsobjekts ein Argument mit dem Namen der Eigenschaft, die geändert wurde. Daher weiß das Bindungsobjekt, welchen Eigenschaftswert es erneut lesen muss.

Damit Sie das oben gezeigte Muster nicht mehrmals implementieren müssen, können Sie dann, wenn Sie C# verwenden, einfach eine Ableitung von der BindableBase-Basisklasse vornehmen, die im QuizGame-Beispiel (im Ordner „Common“) enthalten ist. Dies ist ein Beispiel hierfür.

public class HostViewModel : BindableBase
{
    private string nextButtonText;

    public HostViewModel()
    {
        NextButtonText = "Next";
    }

    public string NextButtonText
    {
        get { return nextButtonText; }
        set { SetProperty(ref nextButtonText, value); }
    }
}

Das Auslösen des PropertyChanged-Ereignisses mit dem Argument String.Empty oder null gibt an, dass alle Eigenschaften, die keine Indexereigenschaften sind, für das Objekt erneut gelesen werden sollen. Sie können das Ereignis auslösen, um anzugeben, dass die Indexereigenschaften für das Objekt geändert wurden, indem Sie ein Argument von „Item[indexer]“ für bestimmte Indexer (mit indexer als Indexwert) oder einen Wert von „Item[]“ für alle Indexer verwenden.

Eine Bindungsquelle kann entweder als einzelnes Objekt behandelt werden, dessen Eigenschaften Daten enthalten, oder als Sammlung von Objekten. In C#-Code können Sie eine einmalige Bindung an ein Objekt vornehmen, das List<T> zum Anzeigen einer Sammlung implementiert, die nicht zur Laufzeit geändert wird. Verwenden Sie für eine beobachtbare Sammlung (es wird beobachtet, wann der Sammlung Elemente hinzugefügt oder daraus entfernt werden) stattdessen eine unidirektionale Bindung an ObservableCollection<T>. Wenn Sie eine Bindung an Ihre eigenen Sammlungsklassen vornehmen möchten, verwenden Sie die Anleitung in der folgenden Tabelle.

Szenario C# (CLR) C++/WinRT
An ein Objekt binden Es kann sich um jedes Objekt handeln. Es kann sich um jedes Objekt handeln.
Abrufen von Benachrichtigungen bei Eigenschaftenänderungen von einem gebundenen Objekts. Das Objekt muss INotifyPropertyChanged implementieren. Das Objekt muss INotifyPropertyChanged implementieren.
An eine Sammlung binden List<T> IVector von IInspectable oder IBindableObservableVector. Siehe XAML-Elementsteuerelemente: Binden an eine C++/WinRT-Sammlung und Sammlungen mit C++/WinRT.
Abrufen von Benachrichtigungen bei Sammlungsänderungen von einer gebundenen Sammlung. ObservableCollection<T> IObservableVector von IInspectable. Beispielsweise winrt::single_threaded_observable_vector<T>.
Implementieren einer Sammlung, die Bindungen unterstützt Erweitern Sie List<T>, oder implementieren Sie IList, IList<Object>, IEnumerable oder IEnumerable<Object>. Bindungen an generische Elemente vom Typ IList<T> und IEnumerable<T> werden nicht unterstützt. Implementieren Sie IVector von IInspectable. Siehe XAML-Elementsteuerelemente: Binden an eine C++/WinRT-Sammlung und Sammlungen mit C++/WinRT.
Implementieren einer Sammlung, die Benachrichtigungen bei Änderungen an der Sammlung unterstützt. Erweitern Sie ObservableCollection<T>, oder implementieren Sie IList und INotifyCollectionChanged (jeweils nicht generisch). Implementieren Sie IObservableVector von IInspectable oder IBindableObservableVector.
Implementieren einer Sammlung, die inkrementelles Laden unterstützt Erweitern Sie ObservableCollection<T>, oder implementieren Sie IList und INotifyCollectionChanged (jeweils nicht generisch). Implementieren Sie zusätzlich ISupportIncrementalLoading. Implementieren Sie IObservableVector von IInspectable oder IBindableObservableVector. Implementieren Sie zusätzlich ISupportIncrementalLoading.

Sie können mithilfe von inkrementellem Laden Listensteuerelemente an beliebig große Datenquellen binden und dennoch eine hohe Leistung erreichen. Zum Beispiel können Sie Listensteuerelemente an Ergebnisse von Bildabfragen in Bing binden, ohne alle Ergebnisse auf einmal laden zu müssen. Stattdessen laden Sie nur einige der Ergebnisse sofort und weitere Ergebnisse nach Bedarf. Sie müssen ISupportIncrementalLoading für eine Datenquelle umsetzen, die Sammlungsänderungsbenachrichtigungen unterstützt, um das inkrementelle Laden zu unterstützen. Wenn das Datenbindungsmodul weitere Daten anfordert, muss Ihre Datenquelle die entsprechenden Anforderungen senden, die Ergebnisse integrieren und anschließend die entsprechende Benachrichtigung senden, um die UI zu aktualisieren.

Bindungsziel

In den folgenden beiden Beispielen ist die Button.Content-Eigenschaft das Bindungsziel. Ihr Wert ist auf eine Markuperweiterung festgelegt, die das Bindungsobjekt deklariert. Es wird zuerst {x:Bind} angezeigt, dann {Binding}. Das Deklarieren von Bindungen im Markup wird häufig verwendet (es ist praktisch, lesbar und toolfreundlich). Sie können Markups jedoch auch vermeiden und eine Instanz der Binding-Klasse stattdessen auch bei Bedarf imperativ (programmgesteuert) erstellen.

<Button Content="{x:Bind ...}" ... />
<Button Content="{Binding ...}" ... />

Mit {x:Bind} deklariertes Bindungsobjekt

Vor dem Erstellen des {x:Bind}-Markups müssen wir jedoch noch einen Schritt durchführen. Wir müssen die Bindungsquellenklasse aus der Klasse verfügbar machen, die die Markupseite darstellt. Hierzu fügen wir der Fensterklasse MainWindow eine Eigenschaft hinzu (in diesem Fall vom Typ HostViewModel).

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

Wenn dies erledigt ist, können wir uns das Markup, das das Bindungsobjekt deklariert, genauer ansehen. Das unten aufgeführte Beispiel verwendet das gleiche Button.Content-Bindungsziel, das wir weiter oben im Abschnitt „Bindungsziel“ verwendet haben, und zeigt seine Bindung an die HostViewModel.NextButtonText-Eigenschaft.

<!-- MainWindow.xaml -->
<Window x:Class="DataBindingInDepth.MainWindow" ... >
    <Button Content="{x:Bind Path=ViewModel.NextButtonText, Mode=OneWay}" ... />
</Window>

Beachten Sie den Wert, den wir für Path angeben. Dieser Wert wird im Kontext des Fensters selbst interpretiert, und in diesem Fall beginnt der Pfad durch einen Verweis auf die Eigenschaft ViewModel, die wir gerade der Seite MainWindow hinzugefügt haben. Da diese Eigenschaft eine HostViewModel-Instanz zurückgibt, können wir die Punktnotation verwenden, um mit dem Objekt zu arbeiten und auf die Eigenschaft HostViewModel.NextButtonText zuzugreifen. Außerdem geben wir Mode an, um die Standardeinstellung für einmaliges Binden {x:Bind} zu überschreiben.

Die Path-Eigenschaft unterstützt eine Vielzahl von Syntaxoptionen zum Binden geschachtelter Eigenschaften, angefügter Eigenschaften sowie von Ganzzahl- und Zeichenfolgenindexern. Weitere Informationen finden Sie unter Property-path-Syntax. Die Bindung an Zeichenfolgenindexer hat dieselbe Wirkung wie die Bindung an dynamische Eigenschaften, ohne ICustomPropertyProvider implementieren zu müssen. Informationen zu weiteren Einstellungen finden Sie unter {x:Bind}-Markuperweiterung.

Um zu veranschaulichen, dass die HostViewModel.NextButtonText-Eigenschaft tatsächlich beobachtbar ist, fügen Sie der Schaltfläche einen Click-Ereignishandler hinzu, und aktualisieren Sie den Wert von HostViewModel.NextButtonText. Erstellen Sie es, führen Sie es aus, und klicken Sie auf die Schaltfläche, um zu sehen, wie der Wert der Schaltfläche Content aktualisiert wird.

// MainWindow.xaml.cs
private void Button_Click(object sender, RoutedEventArgs e)
{
    ViewModel.NextButtonText = "Updated Next button text";
}

Hinweis

Änderungen an TextBox.Text werden an eine bidirektionale Bindungsquelle gesendet, wenn das TextBox-Element den Fokus verliert, und nicht nach jedem Tastaturanschlag des Benutzers.

DataTemplate und x:DataType

Innerhalb einer Datenvorlage (DataTemplate; unabhängig davon, ob als Elementvorlage, Inhaltsvorlage oder Kopfzeilenvorlage verwendet) wird der Wert von Path nicht im Kontext des Fensters, sondern im Kontext des Datenobjekts interpretiert, das als Vorlage verwendet werden soll. Wenn {x:Bind} in einer Datenvorlage so verwendet wird, dass die Bindungen zum Zeitpunkt der Kompilierung überprüft werden können (und effizienter Code für sie generiert werden kann), muss DataTemplate mithilfe von x:DataType den Typ des Datenobjekts deklarieren. Das unten aufgeführte Beispiel kann als ItemTemplate eines Elementsteuerelements verwendet werden, das an eine Sammlung von SampleDataGroup-Objekten gebunden ist.

<DataTemplate x:Key="SimpleItemTemplate" x:DataType="data:SampleDataGroup">
    <StackPanel Orientation="Vertical" Height="50">
      <TextBlock Text="{x:Bind Title}"/>
      <TextBlock Text="{x:Bind Description}"/>
    </StackPanel>
  </DataTemplate>

Schwach typisierte Objekte im Pfad

Angenommen, Sie verfügen über einen Typ mit dem Namen SampleDataGroup, der eine Zeichenfolgeneigenschaft mit dem Namen Title implementiert. Zudem verfügen Sie über die Eigenschaft MainWindow.SampleDataGroupAsObject vom Typ object. Diese gibt jedoch eine Instanz von SampleDataGroup zurück. Die Bindung <TextBlock Text="{x:Bind SampleDataGroupAsObject.Title}"/> führt zu einem Kompilierungsfehler, da die Eigenschaft Title nicht im Typobjekt (object) gefunden wird. Die Lösung hierfür besteht darin, Ihrer Pfadsyntax (Path) eine Umwandlung hinzuzufügen: <TextBlock Text="{x:Bind ((data:SampleDataGroup)SampleDataGroupAsObject).Title}"/>. Hier sehen Sie ein weiteres Beispiel, in dem Element als Objekt (object) deklariert wird, tatsächlich aber ein Textblock (TextBlock) ist: <TextBlock Text="{x:Bind Element.Text}"/>. Und eine Umwandlung behebt das Problem: <TextBlock Text="{x:Bind ((TextBlock)Element).Text}"/>.

Wenn die Daten asynchron geladen werden

Code zur Unterstützung von {x:Bind} wird während der Kompilierung in den partiellen Klassen für Ihre Fenster generiert. Diese Dateien befinden sich in Ihrem obj-Ordner und weisen Namen wie <view name>.g.cs auf (für C#). Der generierte Code enthält einen Handler für das Ereignis vom Typ Loading des Fensters, und dieser Handler ruft die Methode Initialize für eine generierte Klasse auf, die die Bindungen Ihres Fensters darstellt. Initialize ruft im Gegenzug Update auf, um Daten zwischen der Bindungsquelle und dem Ziel zu verschieben. Loading wird kurz vor dem ersten Messdurchlauf des Fensters oder Benutzersteuerelements ausgelöst. Wenn Ihre Daten asynchron geladen werden, sind sie zum Zeitpunkt des Aufrufs von Initialize möglicherweise nicht bereit. Nachdem Sie die Daten geladen haben, können Sie daher durch den Aufruf von this.Bindings.Update();einmalige Bindungen erzwingen. Wenn Sie einmalige Bindungen nur für asynchron geladene Daten benötigen, ist es sehr viel kostengünstiger, sie auf diese Weise zu initialisieren, anstatt unidirektionale Bindungen zu verwenden und Änderungen zu überwachen. Wenn Ihre Daten keinen differenzierteren Änderungen unterliegen und wahrscheinlich im Rahmen einer bestimmten Aktion aktualisiert werden, können Sie einmalige Bindungen erstellen und durch Aufrufen von Update jederzeit ein manuelles Update erzwingen.

Hinweis

{x:Bind} eignet sich weder für spät gebundene Szenarien, z. B. das Navigieren in der Wörterbuchstruktur eines JSON-Objekts, noch für „Duck-Typing“ (Ententypisierung). Beim „Duck-Typing“ handelt es sich um eine schwache Form der Typisierung, basierend auf lexikalischen Übereinstimmungen von Eigenschaftennamen (gemäß dem Motto: „Wenn es watschelt, schwimmt und quakt wie eine Ente, dann ist es eine Ente.“). Im Fall von Duck-Typing würde eine Bindung an die Age-Eigenschaft auch von einem Objekt vom Typ Person oder Wine erfüllt (vorausgesetzt, dass jeder dieser Typen eine Age-Eigenschaft besaß). Verwenden Sie für diese Szenarien die {Binding} -Markuperweiterung.

Mit {Binding} deklariertes Bindungsobjekt

{Binding} geht standardmäßig davon aus, dass Sie eine Bindung an den Datenkontext (DataContext) Ihres Markupfensters vornehmen. Daher legen wir den Datenkontext (DataContext) des Fensters als eine Instanz der Bindungsquellenklasse (in diesem Fall vom Typ HostViewModel) fest. Das folgende Beispiel zeigt das Markup, mit dem das Bindungsobjekt deklariert wird. Wir verwenden das gleiche Button.Content-Bindungsziel, das wir bereits weiter oben im Abschnitt „Bindungsziel“ verwendet haben, und führen eine Bindung an die HostViewModel.NextButtonText-Eigenschaft vor.

<Window xmlns:viewmodel="using:DataBindingInDepth" ... >
    <Window.DataContext>
        <viewmodel:HostViewModel x:Name="viewModelInDataContext"/>
    </Window.DataContext>
    ...
    <Button Content="{Binding Path=NextButtonText}" ... />
</Window>
// MainWindow.xaml.cs
private void Button_Click(object sender, RoutedEventArgs e)
{
    viewModelInDataContext.NextButtonText = "Updated Next button text";
}

Beachten Sie den Wert, den wir für Path angeben. Dieser Wert wird im Kontext der Eigenschaft DataContext des Fensters interpretiert, die in diesem Beispiel auf eine Instanz von HostViewModel festgelegt ist. Der Pfad verweist auf die HostViewModel.NextButtonText-Eigenschaft. Wir können Mode weglassen, da der {Binding}-Standardwert einer unidirektionalen Bindung in diesem Fall funktioniert.

Der Standardwert von DataContext für ein Benutzeroberflächenelement ist der von der übergeordneten Eigenschaft geerbte Wert. Sie können diesen Standardwert natürlich auch überschreiben, indem Sie DataContext explizit festlegen, und diese Eigenschaft wird wiederum standardmäßig von den untergeordneten Elementen geerbt. Das explizite Festlegen von DataContext für ein Element ist nützlich, wenn Sie mehrere Bindungen verwenden möchten, die dieselbe Quelle nutzen.

Ein Bindungsobjekt verfügt über eine Source-Eigenschaft, für die standardmäßig der DataContext des Benutzeroberflächenelements festgelegt ist, für das die Bindung deklariert ist. Sie können diese Standardeinstellung überschreiben, indem Sie Source, RelativeSource oder ElementName explizit für die Bindung festlegen. (Ausführliche Informationen finden Sie unter {Binding}.)

Innerhalb von DataTemplate wird der DataContext automatisch auf das Datenobjekt festgelegt, das als Vorlage verwendet werden soll. Das unten aufgeführte Beispiel kann als ItemTemplate eines Elementsteuerelements verwendet werden, das eine Sammlung eines beliebigen Typs gebunden ist, der über Zeichenfolgeneigenschaften mit dem Namen Title und Description verfügt.

<DataTemplate x:Key="SimpleItemTemplate">
    <StackPanel Orientation="Vertical" Height="50">
      <TextBlock Text="{Binding Title}"/>
      <TextBlock Text="{Binding Description"/>
    </StackPanel>
  </DataTemplate>

Hinweis

Standardmäßig werden Änderungen an TextBox.Text an eine bidirektionale Bindungsquelle gesendet, wenn das TextBox-Element den Fokus verliert. Damit Änderungen nach jedem Tastaturanschlag des Benutzers gesendet werden, legen Sie UpdateSourceTrigger auf PropertyChanged für die Bindung im Markup fest. Sie können auch vollständig steuern, wann Änderungen an die Quelle gesendet werden, indem Sie UpdateSourceTrigger auf Explicit festlegen. Sie behandeln dann Ereignisse für das Textfeld (in der Regel TextBox.TextChanged), rufen GetBindingExpression am Ziel auf, um ein BindingExpression-Objekt abzurufen, und rufen zum Schluss BindingExpression.UpdateSource auf, um die Datenquelle programmgesteuert zu aktualisieren.

Die Path-Eigenschaft unterstützt eine Vielzahl von Syntaxoptionen zum Binden geschachtelter Eigenschaften, angefügter Eigenschaften sowie von Ganzzahl- und Zeichenfolgenindexern. Weitere Informationen finden Sie unter Property-path-Syntax. Die Bindung an Zeichenfolgenindexer hat dieselbe Wirkung wie die Bindung an dynamische Eigenschaften, ohne ICustomPropertyProvider implementieren zu müssen. Die ElementName-Eigenschaft ist hilfreich für eine Element-an-Element-Bindung. Die RelativeSource-Eigenschaft hat mehrere Funktionen. Eine davon ist, dass sie eine leistungsfähigere Alternative zur Vorlagenbindung innerhalb von ControlTemplate ist. Informationen zu anderen Einstellungen finden Sie unter {Binding}-Markuperweiterung und der Binding-Klasse.

Was geschieht, wenn die Quelle und das Ziel nicht den gleichen Typ haben?

Wenn Sie die Sichtbarkeit eines Benutzeroberflächenelements basierend auf dem Wert einer booleschen Eigenschaft steuern möchten, oder wenn Sie ein Benutzeroberflächenelement mit einer Farbe rendern möchten, die eine Funktion des Bereichs oder Trends eines numerischen Werts ist, oder wenn Sie einen Datums- und/oder Uhrzeitwert in einer Benutzeroberflächen-Elementeigenschaft angezeigt möchten, die eine Zeichenfolge erwartet, müssen Sie Werte von einem Typ in einen anderen konvertieren. Es wird Fälle geben, bei denen die richtige Lösung ist, eine andere Eigenschaft des richtigen Typs über die Bindungsquellenklasse verfügbar zu machen und die Konvertierungslogik dort gekapselt und testfähig zu belassen. Dies ist jedoch weder eine flexible noch eine skalierbare Lösung, wenn Sie über eine große Anzahl bzw. umfangreiche Kombinationen von Quell- und Zieleigenschaften verfügen. In diesem Fall verfügen Sie über verschiedene Optionen:

  • Bei Verwendung von {x:Bind} kann direkt eine Bindung an eine Funktion vorgenommen werden, um die Konvertierung durchzuführen.
  • Sie können zudem einen Wertkonverter als Objekt zum Durchführen der Konvertierung angeben.

Wertkonverter

Her ist ein Wertkonverter, der sich für eine einmalige oder eine unidirektionale Bindung eignet und einen Wert vom Typ DateTime in einen Zeichenfolgenwert (string) konvertiert, der den Monat enthält. Die Klasse implementiert IValueConverter.

public class DateToStringConverter : IValueConverter
{
    // Define the Convert method to convert a DateTime value to 
    // a month string.
    public object Convert(object value, Type targetType, 
        object parameter, string language)
    {
        // value is the data from the source object.
        DateTime thisDate = (DateTime)value;
        int monthNum = thisDate.Month;
        string month;
        switch (monthNum)
        {
            case 1:
                month = "January";
                break;
            case 2:
                month = "February";
                break;
            default:
                month = "Month not found";
                break;
        }
        // Return the value to pass to the target.
        return month;
    }

    // ConvertBack is not implemented for a OneWay binding.
    public object ConvertBack(object value, Type targetType, 
        object parameter, string language)
    {
        throw new NotImplementedException();
    }
}

Hier sehen Sie, wie Sie diesen Wertkonverter im Bindungsobjektmarkup nutzen.

<UserControl.Resources>
  <local:DateToStringConverter x:Key="Converter1"/>
</UserControl.Resources>
...
<TextBlock Grid.Column="0" 
  Text="{x:Bind ViewModel.Month, Converter={StaticResource Converter1}}"/>
<TextBlock Grid.Column="0" 
  Text="{Binding Month, Converter={StaticResource Converter1}}"/>

Das Bindungsmodul ruft die Methoden Convert und ConvertBack auf, wenn der Converter-Parameter für die Bindung definiert ist. Wenn Daten aus der Quelle übergeben werden, ruft das Bindungsmodul Convert auf und übergibt die zurückgegebenen Daten an das Ziel. Wenn Daten aus dem Ziel übergeben werden (bei einer bidirektionalen Bindung), ruft das Bindungsmodul ConvertBack auf und übergibt die zurückgegebenen Daten an die Quelle.

Der Konverter verfügt auch über optionale Parameter: ConverterLanguage ermöglicht das Angeben der für die Konvertierung zu verwendenden Sprache und ConverterParameter ermöglicht das Übergeben eines Parameters für die Konvertierungslogik. Ein Beispiel, in dem ein Konverterparameter verwendet wird, finden Sie unter IValueConverter.

Hinweis

Wenn bei der Konvertierung ein Fehler auftritt, lösen Sie keine Ausnahme aus. Geben Sie stattdessen DependencyProperty.UnsetValue zurück, wodurch die Datenübertragung beendet wird.

Um immer dann einen Standardwert anzuzeigen, wenn die Bindungsquelle nicht aufgelöst werden kann, legen Sie die FallbackValue -Eigenschaft für das Bindungsobjekt im Markup fest. Dies ist hilfreich bei der Behandlung von Konvertierungs- und Formatierungsfehlern. Außerdem ist es nützlich zum Binden an Quelleigenschaften, die in einer gebundenen Auflistung von heterogenen Typen ggf. nicht für alle Objekte vorhanden sind.

Wenn Sie ein Textsteuerelement an einen Wert binden, bei dem es sich nicht um eine Zeichenfolge handelt, wird der Wert vom Datenbindungsmodul in eine Zeichenfolge konvertiert. Wenn der Wert ein Verweistyp ist, ruft das Datenbindungsmodul den Zeichenfolgenwert ab, indem ICustomPropertyProvider.GetStringRepresentation oder IStringable.ToString abgerufen wird (falls verfügbar). Andernfalls wird Object.ToString aufgerufen. Beachten Sie jedoch, dass das Bindungsmodul alle ToString-Implementierungen ignoriert, bei denen die Basisklassenimplementierung ausgeblendet wird. Stattdessen sollte bei Implementierungen von Unterklassen die ToString-Basisklassenmethode überschrieben werden. Auf ähnliche Weise werden in systemeigenen Sprachen analog dazu scheinbar ICustomPropertyProvider und IStringable implementiert. Alle Aufrufe von GetStringRepresentation und IStringable.ToString werden jedoch an Object.ToString oder eine Überschreibung dieser Methode weitergeleitet, und niemals an eine neue ToString-Implementierung, bei der die Basisklassenimplementierung ausgeblendet wird.

Hinweis

Das Windows-Community-Toolkit enthält einen Konverter vom Typ BoolToVisibilityConverter. Der Konverter verknüpft true mit dem Enumerationswert Visible und false mit dem Wert Collapsed, sodass Sie eine Eigenschaft vom Typ Visibility an einen booleschen Wert binden können, ohne einen Konverter zu erstellen. Um den Konverter verwenden zu können, muss Ihr Projekt das NuGet-Paket CommunityToolkit.WinUI.Converters hinzufügen.

Funktionsbindung in {x:Bind}

Dank {x:Bind} kann der letzte Schritt in einem Bindungspfad eine Funktion sein. Hiermit können Konvertierungen und Bindungen durchgeführt werden, die von mehreren Eigenschaften abhängen. Siehe Funktionen in x:Bind.

Element-an-Element-Bindung

Sie können die Eigenschaft eines XAML-Elements an die Eigenschaft eines anderen XAML-Elements binden. Dies ist ein Beispiel hierfür im Markup.

<TextBox x:Name="myTextBox" />
<TextBlock Text="{x:Bind myTextBox.Text, Mode=OneWay}" />

Ressourcenwörterbücher mit {x:Bind}

Die {x:Bind}-Markuperweiterung hängt von der Codegenerierung ab. Sie benötigt daher eine CodeBehind-Datei mit einem Konstruktor, der InitializeComponent aufruft (um den generierten Code zu initialisieren). Sie verwenden das Ressourcenwörterbuch erneut, indem Sie seinen Typ instanziieren (sodass InitializeComponent aufgerufen wird), anstatt auf den Dateinamen zu verweisen. Im folgenden Beispiel wird gezeigt, was zu tun ist, wenn Sie über ein vorhandenes Ressourcenwörterbuch verfügen und darin {x:Bind} verwenden möchten.

<!-- TemplatesResourceDictionary.xaml -->
<ResourceDictionary
    x:Class="ExampleNamespace.TemplatesResourceDictionary"
    .....
    xmlns:examplenamespace="using:ExampleNamespace">
    
    <DataTemplate x:Key="EmployeeTemplate" x:DataType="examplenamespace:IEmployee">
        <Grid>
            <TextBlock Text="{x:Bind Name}"/>
        </Grid>
    </DataTemplate>
</ResourceDictionary>
// TemplatesResourceDictionary.xaml.cs
using Microsoft.UI.Xaml.Data;
 
namespace ExampleNamespace
{
    public partial class TemplatesResourceDictionary
    {
        public TemplatesResourceDictionary()
        {
            InitializeComponent();
        }
    }
}
<!-- MainWindow.xaml -->
<Window x:Class="ExampleNamespace.MainWindow"
    ....
    xmlns:examplenamespace="using:ExampleNamespace">

    <Window.Resources>
        <ResourceDictionary>
            .... 
            <ResourceDictionary.MergedDictionaries>
                <examplenamespace:TemplatesResourceDictionary/>
            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>
    </Window.Resources>
</Window>

Ereignisbindung und ICommand

{b:Bind} unterstützt ein Feature namens „Ereignisbindung“. Mit diesem Feature können Sie den Handler für ein Ereignis mit einer Bindung angeben, wobei es sich zusätzlich zum Behandeln von Ereignissen mit einer Methode in der CodeBehind-Datei um eine weitere Option handelt. Angenommen, Sie verfügen über einen Handler des Ereignisses ListViewDoubleTapped für Ihre Klasse MainWindow.

public sealed partial class MainWindow : Window
{
    ...
    public void ListViewDoubleTapped()
    {
        // Handle double-tapped logic
    }
}

Anschließend können Sie das Ereignis DoubleTapped eines ListView-Elements wie folgt an eine Methode im Hauptfenster (MainWindow) binden.

<ListView DoubleTapped="{x:Bind ListViewDoubleTapped}" />

Überladene Methoden können nicht verwendet werden, um ein Ereignis mit diesem Verfahren zu behandeln. Wenn die Methode, die das Ereignis behandelt, darüber hinaus über Parameter verfügt, müssen diese alle von den Typen aller Ereignisparameter zugeordnet werden können. In diesem Fall ist ListViewDoubleTapped nicht überladen und verfügt über keine Parameter (wäre aber auch bei Verwendung zweier Parameter vom Typ object gültig).

Das Verfahren zu Ereignisbindung ist ähnlich der Implementierung und Nutzung von Befehlen (ein Befehl ist eine Eigenschaft, die ein Objekt zurückgibt, das die ICommand-Schnittstelle implementiert). Sowohl {x:Bind} als auch {Binding} kann mit Befehlen verwendet werden. Damit Sie das Befehlsmuster nicht mehrmals implementieren müssen, können Sie die Hilfsklasse DelegateCommand verwenden, die Sie im UWP-Beispiel QuizGame (im Ordner „Common“) finden.

Binden an eine Sammlung von Dateien oder Ordnern

Sie können die APIs im Namespace Windows.Storage verwenden, um Ordner- und Dateidaten in Ihren gepackten Windows App SDK-Apps abzurufen. Die verschiedenen GetFilesAsync-, GetFoldersAsync- und GetItemsAsync-Methoden geben jedoch keine Werte zurück, die zur Bindung an Listensteuerelemente geeignet sind. Stattdessen müssen Sie die Bindung an die Rückgabewerte der GetVirtualizedFilesVector-, GetVirtualizedFoldersVector-, und GetVirtualizedItemsVector-Methoden der FileInformationFactory-Klasse vornehmen. Das folgende Codebeispiel aus dem UWP-Beispiel für StorageDataSource und GetVirtualizedFilesVector veranschaulicht das typische Verwendungsmuster. Vergessen Sie nicht, die picturesLibrary-Funktion im App-Paketmanifest zu deklarieren, und bestätigen Sie, dass Bilder im Bildbibliotheksordner vorhanden sind.

protected override void OnNavigatedTo(NavigationEventArgs e)
{
    var library = Windows.Storage.KnownFolders.PicturesLibrary;
    var queryOptions = new Windows.Storage.Search.QueryOptions();
    queryOptions.FolderDepth = Windows.Storage.Search.FolderDepth.Deep;
    queryOptions.IndexerOption = Windows.Storage.Search.IndexerOption.UseIndexerWhenAvailable;

    var fileQuery = library.CreateFileQueryWithOptions(queryOptions);

    var fif = new Windows.Storage.BulkAccess.FileInformationFactory(
        fileQuery,
        Windows.Storage.FileProperties.ThumbnailMode.PicturesView,
        190,
        Windows.Storage.FileProperties.ThumbnailOptions.UseCurrentScale,
        false
        );

    var dataSource = fif.GetVirtualizedFilesVector();
    this.PicturesListView.ItemsSource = dataSource;
}

In der Regel verwenden Sie diese Vorgehensweise, um eine schreibgeschützte Ansicht der Datei- und Ordnerinformationen zu erstellen. Sie können Zwei-Wege-Bindungen an die Datei- und Ordnereigenschaften erstellen, zum Beispiel um Benutzern die Bewertung eines Titels in einer Musikansicht zu ermöglichen. Änderungen werden jedoch erst beibehalten, wenn Sie die entsprechende SavePropertiesAsync-Methode aufrufen (beispielsweise MusicProperties.SavePropertiesAsync). Sie müssen für Änderungen einen Commit ausführen, wenn das Element den Fokus verliert, da dadurch das Zurücksetzen der Auswahl ausgelöst wird.

Beachten Sie, dass eine bidirektionale Bindung mit dieser Technik nur für indizierte Speicherorte (beispielsweise Musik) funktioniert. Sie können durch Aufrufen der FolderInformation.GetIndexedStateAsync-Methode feststellen, ob ein Ort indiziert ist.

Darüber hinaus kann der Wert null von einem virtualisierten Vektor für einige Elemente vor dem Füllen des Werts zurückgegeben werden. Beispielsweise sollten Sie nach null suchen, bevor Sie den SelectedItem-Wert eines Listensteuerelements verwenden, das an einen virtualisierten Vektor gebunden ist. Sie können stattdessen jedoch auch SelectedIndex verwenden.

Binden an Daten, die durch einen Schlüssel gruppiert werden

Wenn Sie eine flache Sammlung von Elementen aufnehmen (beispielsweise Bücher, dargestellt durch eine BookSku-Klasse) und die Elemente mithilfe einer allgemeinen Eigenschaft als Schlüssel gruppieren (beispielsweise der Eigenschaft BookSku.AuthorName) werden als Ergebnis gruppierte Daten aufgerufen. Wenn Sie Daten gruppieren, handelt es sich nicht mehr um eine flache Sammlung. Bei gruppierten Daten handelt es sich um eine Sammlung von Gruppenobjekten, wobei jedes Gruppenobjekt:

  • (a) über einen Schlüssel und
  • (b) über eine Sammlung von Elementen verfügt, deren Eigenschaft dem Schlüssel entspricht.

Im Fall des Beispiels mit den Büchern resultiert die Gruppierung der Bücher nach Autorennamen in einer Sammlung von Autorennamengruppen, wobei jede Gruppe:

  • a) über einen Schlüssel verfügt, bei dem es sich um den Autorennamen handelt, und
  • über eine Sammlung der Objekte vom Typ BookSku, deren Eigenschaft AuthorName dem Schlüssel der Gruppe entspricht.

In der Regel binden Sie zum Anzeigen einer Sammlung die ItemsSource eines Elementsteuerelements (wie beispielsweise ListView oder GridView) direkt an eine Eigenschaft, die eine Sammlung zurückgibt. Wenn dies eine flache Sammlung von Elementen ist, müssen Sie nichts Besonderes tun. Wenn es sich jedoch um eine Sammlung von Gruppenobjekten handelt (wie bei der Bindung gruppierter Daten), benötigen Sie die Dienste eines Zwischenobjekts, das als CollectionViewSource bezeichnet wird und sich zwischen dem Elementsteuerelement und der Bindungsquelle befindet. Sie binden die CollectionViewSource an die Eigenschaft, die gruppierte Daten zurückgibt, und das Elementsteuerelement an die CollectionViewSource. Ein weiterer Vorteil einer CollectionViewSource besteht darin, dass sie das aktuelle Element verfolgt, sodass Sie mehr als ein Elementsteuerelement synchron beibehalten können, indem Sie alle Steuerelemente an die gleiche CollectionViewSource binden. Sie können über die ICollectionView.CurrentItem-Eigenschaft des Objekts, das von der CollectionViewSource.View-Eigenschaft zurückgegeben wird, auch programmgesteuert auf das aktuelle Element zugreifen.

Um die Gruppierungsfunktion einer CollectionViewSource zu aktivieren, legen Sie IsSourceGrouped auf true fest. Ob Sie auch die ItemsPath-Eigenschaft festlegen müssen, hängt davon ab, wie Sie die Gruppenobjekte erstellen. Es gibt zwei Möglichkeiten zum Erstellen eines Gruppenobjekts: das Muster „is-a-group“ und das Muster „has-a-group“. Im Muster „is-a-group“ wird das Gruppenobjekt von einem Sammlungstyp abgeleitet (beispielsweise List<T>), sodass das Gruppenobjekt selbst die Gruppe von Elementen darstellt. Bei diesem Muster müssen Sie ItemsPath nicht festlegen. Im Muster „has-a-group“ hat das Gruppenobjekt eine oder mehrere Eigenschaften eines Sammlungstyps (beispielsweise List<T>), sodass die Gruppe über eine Gruppe von Elementen in Form einer Eigenschaft verfügt (oder über mehrere Gruppen von Elementen in Form mehrerer Eigenschaften). Bei diesem Muster müssen Sie ItemsPath auf den Namen der Eigenschaft festlegen, die die Gruppe von Elementen enthält.

Das folgende Beispiel veranschaulicht das Muster „has-a-group“. Die Fensterklasse verfügt über eine Eigenschaft namens DataContext, die eine Instanz des Ansichtsmodells zurückgibt. Die CollectionViewSource bindet an die Authors-Eigenschaft des Ansichtsmodells (Authors ist die Sammlung von Gruppenobjekten) und gibt darüber hinaus an, dass die Author.BookSkus-Eigenschaft die gruppierten Elemente enthält. Zuletzt wird GridView an die CollectionViewSource gebunden und erhält eine Definition des Gruppenstils, damit die Ansicht die Elemente in den Gruppen rendern kann.

<Window.Resources>
    <CollectionViewSource
    x:Name="AuthorHasACollectionOfBookSku"
    Source="{x:Bind ViewModel.Authors}"
    IsSourceGrouped="true"
    ItemsPath="BookSkus"/>
</Window.Resources>
...
<GridView
ItemsSource="{x:Bind AuthorHasACollectionOfBookSku}" ...>
    <GridView.GroupStyle>
        <GroupStyle
            HeaderTemplate="{StaticResource AuthorGroupHeaderTemplateWide}" ... />
    </GridView.GroupStyle>
</GridView>

Sie haben zwei Möglichkeiten zum Implementieren des Musters „is-a-group“. Eine Möglichkeit besteht darin, eine eigene Gruppenklasse zu erstellen. Leiten Sie die Klasse von List<T> ab (wobei T die Art der Elemente ist). Beispiel: public class Author : List<BookSku>. Die zweite Möglichkeit besteht in der Verwendung eines LINQ-Ausdrucks zum dynamischen Erstellen von Gruppenobjekten (und einer Gruppenklasse) aus ähnlichen Eigenschaftswerten der BookSku-Elemente. Dieser Ansatz, bei dem nur eine flache Liste von Elementen beibehalten wird, die ad-hoc zusammen gruppiert werden, ist typisch für eine App, die über einen Clouddienst auf Daten zugreift. Sie können Bücher beispielsweise nach Autor oder Genre gruppieren, ohne dafür spezielle Gruppenklassen wie Author und Genre zu benötigen.

Das folgende Beispiel veranschaulicht das Muster „is-a-group“ unter Verwendung von LINQ. Dieses Mal werden die Bücher nach Genre gruppiert, wobei der Name des Genres in der Gruppenkopfzeile angezeigt wird. Dies wird durch den „Key“-Eigenschaftspfad als Verweis auf den Key-Wert der Gruppe angezeigt.

using System.Linq;
...
private IOrderedEnumerable<IGrouping<string, BookSku>> genres;

public IOrderedEnumerable<IGrouping<string, BookSku>> Genres
{
    get
    {
        if (genres == null)
        {
            genres = from book in bookSkus
                     group book by book.genre into grp
                     orderby grp.Key
                     select grp;
        }
        return genres;
    }
}

Vergessen Sie nicht, dass Sie bei der Verwendung von {x:Bind} mit Datenvorlagen den Typ angeben müssen, an den die Bindung erfolgt, indem Sie einen x:DataType-Wert festlegen. Wenn es sich um einen generischen Typ handelt, können wir dies nicht im Markup ausdrücken. Daher müssen wir stattdessen {Binding} in der Gruppenstil-Kopfzeilenvorlage verwenden.

    <Grid.Resources>
        <CollectionViewSource x:Name="GenreIsACollectionOfBookSku"
        Source="{x:Bind Genres}"
        IsSourceGrouped="true"/>
    </Grid.Resources>
    <GridView ItemsSource="{x:Bind GenreIsACollectionOfBookSku}">
        <GridView.ItemTemplate x:DataType="local:BookTemplate">
            <DataTemplate>
                <TextBlock Text="{x:Bind Title}"/>
            </DataTemplate>
        </GridView.ItemTemplate>
        <GridView.GroupStyle>
            <GroupStyle>
                <GroupStyle.HeaderTemplate>
                    <DataTemplate>
                        <TextBlock Text="{Binding Key}"/>
                    </DataTemplate>
                </GroupStyle.HeaderTemplate>
            </GroupStyle>
        </GridView.GroupStyle>
    </GridView>

Mit einem SemanticZoom-Steuerelement haben Benutzer eine sehr gute Möglichkeit, gruppierte Daten anzuzeigen und darin zu navigieren. Die UWP-Beispiel-App Bookstore2 veranschaulicht die Verwendung von SemanticZoom. In dieser App können Sie in der vergrößerten Ansicht eine Liste der Bücher gruppiert nach Autor anzeigen, oder Sie können die Ansicht verkleinern, um eine Sprungliste der Autoren anzuzeigen. Die Sprungliste ermöglicht eine wesentlich schnellere Navigation im Vergleich zum Blättern in der Bücherliste. Die vergrößerten und verkleinerten Ansichten sind eigentlich ListView- bzw. GridView-Steuerelemente, die an dieselbe CollectionViewSource gebunden sind.

Abbildung zur Veranschaulichung eines semantischen Zooms

Wenn Sie eine Bindung an hierarchische Daten vornehmen, wie z. B. Unterkategorien von Kategorien, können Sie die Hierarchieebenen in der Benutzeroberfläche mit einer Reihe von Elementsteuerelementen anzeigen. Eine Auswahl in einem Elementsteuerelement bestimmt den Inhalt der nachfolgenden Elementsteuerelemente. Die Listen können synchron gehalten werden, indem jede Liste jeweils an ihre eigene CollectionViewSource und die CollectionViewSource-Instanzen in einer Kette gebunden werden. Dies wird als „Master-/Detailansicht“ (oder „Listen-/Detailansicht“) bezeichnet. Weitere Informationen finden Sie unter Binden an hierarchische Daten und Erstellen einer Master-/Detailansicht.

Diagnose und Debuggen von Problemen bei der Datenbindung

Ihr Bindungsmarkup enthält die Namen der Eigenschaften (und für C# manchmal Felder und Methoden). Wenn Sie eine Eigenschaft umbenennen, müssen Sie daher außerdem alle Bindungen ändern, die darauf verweisen. Wenn Sie dies vergessen, führt dies zu einem typischen Beispiel eines Datenbindungsfehlers, und Ihre App kann nicht kompiliert oder ordnungsgemäß ausgeführt werden.

Die von {x:Bind} und {Binding} erstellten Bindungsobjekte sind von der Funktionsweise her größtenteils identisch. {x:Bind} verfügt jedoch über Typinformationen für die Bindungsquelle und generiert zum Zeitpunkt der Kompilierung Quellcode. Bei Verwendung von {x:Bind} erhalten Sie die gleiche Art von Problemerkennung wie bei dem restlichen Code. Dazu gehört die Überprüfung von Bindungsausdrücken während des Kompilierens sowie das Debuggen durch Setzen von Haltepunkten im Quellcode, der als Teilklasse für die Seite erstellt wird. Diese Klassen befinden sich in den Dateien in Ihrem obj-Ordner und weisen Namen wie <view name>.g.cs auf (in C#). Wenn bei einer Bindung ein Problem auftritt, aktivieren Sie im Microsoft Visual Studio-Debugger die Option Bei nicht behandelten Ausnahmen unterbrechen. Der Debugger unterbricht die Ausführung an dieser Stelle, und Sie können den Fehler dann debuggen. Der von {x:Bind} generierte Code folgt für jeden Teil des Bindungsquellenknoten-Graphs dem gleichen Muster. Anhand der Informationen im Fenster Aufrufliste können Sie die Sequenz der Aufrufe bis zum Auftreten des Problems ermitteln.

{Binding} verfügt nicht über Typinformation für die Bindungsquelle. Wenn Sie jedoch die App mit angefügtem Debugger ausführen, werden alle Bindungsfehler in Visual Studio in den Fenstern Ausgabe und XAML-Bindungsfehler angezeigt. Weitere Informationen zum Debuggen von Bindungsfehlern in Visual Studio finden Sie unter XAML-Datenbindungsdiagnose.

Erstellen von Bindungen im Code

Hinweis

Dieser Abschnitt gilt nur für {Binding}, da im Code keine Bindungen vom Typ {x:Bind} erstellt werden können. Allerdings können einige der Vorteile von {x:Bind} auch mit DependencyObject.RegisterPropertyChangedCallback erzielt werden, was die Registrierung von Änderungsbenachrichtigungen für Abhängigkeitseigenschaften ermöglicht.

Sie können darüber hinaus Benutzeroberflächenelemente mit Daten verbinden, indem Sie anstelle von XAML prozeduralen Code verwenden. Erstellen Sie hierzu ein neues Binding-Objekt, legen Sie die entsprechenden Eigenschaften fest, und rufen Sie anschließend FrameworkElement.SetBinding oder BindingOperations.SetBinding auf. Die programmgesteuerte Erstellung von Bindungen ist nützlich, wenn Sie die Bindungseigenschaftswerte zur Laufzeit auswählen oder eine einzelne Bindung für mehrere Steuerelemente gemeinsam verwenden möchten. Beachten Sie jedoch, dass Sie die Bindungseigenschaftswerte nach dem Aufrufen von SetBinding nicht mehr ändern können.

Im folgenden Beispiel wird gezeigt, wie Sie eine Bindung im Code implementieren.

<TextBox x:Name="MyTextBox" Text="Text"/>
// Create an instance of the MyColors class 
// that implements INotifyPropertyChanged.
var textcolor = new MyColors();

// Brush1 is set to be a SolidColorBrush with the value Red.
textcolor.Brush1 = new SolidColorBrush(Colors.Red);

// Set the DataContext of the TextBox MyTextBox.
MyTextBox.DataContext = textcolor;

// Create the binding and associate it with the text box.
var binding = new Binding { Path = new PropertyPath("Brush1") };
MyTextBox.SetBinding(TextBox.ForegroundProperty, binding);

Vergleich der Features von {x:Bind} und {Binding}

Feature {x:Bind} vs. {Binding} Hinweise
„Path“ ist die Standardeigenschaft. {x:Bind a.b.c}
-
{Binding a.b.c}
Path-Eigenschaft {x:Bind Path=a.b.c}
-
{Binding Path=a.b.c}
In x:Bind ist Path standardmäßig an das Fenster als Stamm gebunden, nicht an den Datenkontext (DataContext).
Indexerstellung {x:Bind Groups[2].Title}
-
{Binding Groups[2].Title}
Bindung an das in der Sammlung angegebene Element. Es werden nur ganzzahlbasierte Indizes unterstützt.
Angefügte Eigenschaften {x:Bind Button22.(Grid.Row)}
-
{Binding Button22.(Grid.Row)}
Angefügte Eigenschaften werden in Klammern angegeben. Wenn die Eigenschaft nicht in einem XAML-Namespace deklariert wird, stellen Sie ihr einen XML-Namespace als Präfix voran, der einem Codenamespace am Anfang des Dokuments zugeordnet werden sollte.
Umwandlung {x:Bind groups[0].(data:SampleDataGroup.Title)}
-
{Binding} wird nicht benötigt.
Umwandlungen werden in Klammern angegeben. Wenn die Eigenschaft nicht in einem XAML-Namespace deklariert wird, stellen Sie ihr einen XML-Namespace als Präfix voran, der einem Codenamespace am Anfang des Dokuments zugeordnet werden sollte.
Converter {x:Bind IsShown, Converter={StaticResource BoolToVisibility}}
-
{Binding IsShown, Converter={StaticResource BoolToVisibility}}
Konverter müssen am Stamm von „Window/Control/ResourceDictionary“ oder in App.xaml deklariert werden.
KonverterParameter, KonverterLanguage {x:Bind IsShown, Converter={StaticResource BoolToVisibility}, ConverterParameter=One, ConverterLanguage=fr-fr}
-
{Binding IsShown, Converter={StaticResource BoolToVisibility}, ConverterParameter=One, ConverterLanguage=fr-fr}
Konverter müssen am Stamm von „Window/Control/ResourceDictionary“ oder in App.xaml deklariert werden.
TargetNullValue {x:Bind Name, TargetNullValue=0}
-
{Binding Name, TargetNullValue=0}
Wird verwendet, wenn das Blatt des Bindungsausdrucks NULL ist. Verwenden Sie zum Angeben eines Zeichenfolgenwerts einfache Anführungszeichen.
FallbackValue {x:Bind Name, FallbackValue='empty'}
-
{Binding Name, FallbackValue='empty'}
Wird verwendet, wenn ein beliebiger Teil des Pfads für die Bindung (mit Ausnahme des Blatts) NULL ist.
ElementName {x:Bind slider1.Value}
-
{Binding Value, ElementName=slider1}
Mit {x:Bind} nehmen Sie eine Bindung an ein Feld vor. Path ist standardmäßig an das Fenster als Stamm gebunden, damit auf jedes benannte Element über das zugehörige Feld zugegriffen werden kann.
RelativeSource: Self (Selbst) <Rectangle x:Name="rect1" Width="200" Height="{x:Bind rect1.Width}" ... />
-
<Rectangle Width="200" Height="{Binding Width, RelativeSource={RelativeSource Self}}" ... />
Bei {x:Bind}: Benennen Sie das Element, und verwenden Sie den Namen in Path.
RelativeSource: TemplatedParent Für {x:Bind} nicht erforderlich.
-
{Binding <path>, RelativeSource={RelativeSource TemplatedParent}}
Bei {x:Bind}: TargetType in ControlTemplate gibt die Bindung an ein übergeordnetes Element der Vorlage an. Bei {Binding}: In den meisten Fällen kann eine reguläre Vorlagenbindung in Steuerelementvorlagen verwendet werden. Verwenden Sie jedoch TemplatedParent, wenn Sie einen Konverter verwenden müssen (oder eine bidirektionale Bindung).
`Source` Für {x:Bind} nicht erforderlich.
-
<ListView ItemsSource="{Binding Orders, Source={StaticResource MyData}}"/>
Bei {x:Bind} können Sie das benannte Element direkt oder aber eine Eigenschaft oder einen statischen Pfad verwenden.
Mode {x:Bind Name, Mode=OneWay}
-
{Binding Name, Mode=TwoWay}
Mode kann OneTime, OneWay oder TwoWay sein. {x:Bind} entspricht standardmäßig OneTime, und {Binding} entspricht standardmäßig OneWay.
UpdateSourceTrigger {x:Bind Name, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}
-
{Binding UpdateSourceTrigger=PropertyChanged}
UpdateSourceTrigger kann Default, LostFocus oder PropertyChanged sein. {x:Bind} unterstützt UpdateSourceTrigger=Explicit nicht. {x:Bind} verwendet immer das Verhalten PropertyChanged. Einzige Ausnahme ist TextBox.Text: Hier wird das Verhalten LostFocus verwendet.

Weitere Informationen