Freigeben über


Datenbindung im Detail

Wichtige APIs

Hinweis

In diesem Thema werden die Datenbindungsfeatures ausführlich beschrieben. Eine kurze, praktische Einführung finden Sie in der Übersicht über die Datenbindung.

In diesem Thema geht es um die Datenbindung für die APIs, die sich im Windows.UI.Xaml.Data-Namespace befinden.

Die Datenbindung ist eine Möglichkeit für die Benutzeroberfläche Ihrer App zum Anzeigen von Daten und optional zum Synchronisieren mit diesen Daten. Die Datenbindung ermöglicht es Ihnen, das Anliegen von Daten von der Benutzeroberfläche zu trennen, was zu einem einfacheren konzeptuellen Modell sowie einer besseren Lesbarkeit, Testbarkeit und Wartung ihrer App führt.

Sie können datenbindung verwenden, um einfach Werte aus einer Datenquelle anzuzeigen, wenn die Benutzeroberfläche zum ersten Mal angezeigt wird, aber nicht auf Änderungen dieser Werte zu reagieren. Dies ist ein Modus der Bindung, die als einmal bezeichnet wird, und es eignet sich gut für einen Wert, der sich während der Laufzeit nicht ändert. Alternativ können Sie die Werte "beobachten" und die Benutzeroberfläche aktualisieren, wenn sie sich ändern. Dieser Modus wird als unidirektionale Methode bezeichnet und eignet sich gut für schreibgeschützte Daten. Letztendlich können Sie sich entscheiden, sowohl zu beobachten als auch zu aktualisieren, sodass Änderungen, die der Benutzer an Werten in der Benutzeroberfläche vorgibt, automatisch in die Datenquelle zurückgesendet werden. Dieser Modus wird als bidirektionale Methode bezeichnet und eignet sich gut für Lese-/Schreibzugriffsdaten. Hier sind einige Beispiele.

  • Sie können den einmaligen Modus verwenden, um ein Bild an das Foto des aktuellen Benutzers zu binden.
  • Sie können den Unidirektionale Modus verwenden, um eine ListView an eine Sammlung von Nachrichtenartikeln in Echtzeit zu binden, die nach Zeitungsabschnitt gruppiert sind.
  • Sie können den bidirektionale Modus verwenden, um ein TextBox-Element an den Namen eines Kunden in einem Formular zu binden.

Unabhängig vom Modus gibt es zwei Arten von Bindung, und beide werden in der Regel im UI-Markup deklariert. Sie können entweder die {x:Bind}-Markuperweiterung oder die {Binding}-Markuperweiterung verwenden. Und Sie können sogar eine Mischung aus den beiden in derselben App verwenden – auch auf demselben UI-Element. {x:Bind} ist neu für Windows 10 und hat eine bessere Leistung. Alle in diesem Thema beschriebenen Details gelten für beide Arten von Bindung, es sei denn, wir sagen ausdrücklich anders.

Beispiel-Apps, die {x:Bind} veranschaulichen

Beispiel-Apps, die {Binding} veranschaulichen

Jede Bindung umfasst diese Teile.

  • Eine Bindungsquelle. Dies ist die Quelle der Daten für die Bindung, und es kann sich um eine Instanz jeder Klasse handeln, die Elemente enthält, deren Werte in der Benutzeroberfläche angezeigt werden sollen.
  • Ein Bindungsziel. Dies ist eine Abhängigkeitseigenschaft des FrameworkElements in Ihrer Benutzeroberfläche, die die Daten anzeigt.
  • Ein Bindungsobjekt. Dies ist das Element, das Datenwerte von der Quelle an das Ziel und optional vom Ziel zurück an die Quelle überträgt. Das Bindungsobjekt wird zur XAML-Ladezeit aus Ihrer {x:Bind} - oder {Binding} -Markuperweiterung erstellt.

In den folgenden Abschnitten werfen wir einen genaueren Blick auf die Bindungsquelle, das Bindungsziel und das Bindungsobjekt. Und wir verknüpfen die Abschnitte zusammen mit dem Beispiel zum Binden des Inhalts einer Schaltfläche an eine Zeichenfolgeneigenschaft namens NextButtonText, die zu einer Klasse namens HostViewModel gehört.

Bindungsquelle

Hier ist eine sehr rudimentäre Implementierung einer Klasse, die wir als Bindungsquelle verwenden können.

Wenn Sie C++/WinRT verwenden, fügen Sie dem Projekt neue Midl File (.idl) -Elemente hinzu, wie im folgenden C++/WinRT-Codebeispiel dargestellt. Ersetzen Sie den Inhalt dieser neuen Dateien durch den in der Auflistung angezeigten MIDL 3.0-Code , erstellen Sie das zu generierende HostViewModel.h Projekt und .cppfü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.

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

    public string NextButtonText { get; set; }
}
// HostViewModel.idl
namespace DataBindingInDepth
{
    runtimeclass HostViewModel
    {
        HostViewModel();
        String NextButtonText;
    }
}

// HostViewModel.h
// Implement the constructor like this, and add this field:
...
HostViewModel() : m_nextButtonText{ L"Next" } {}
...
private:
    std::wstring m_nextButtonText;
...

// HostViewModel.cpp
// Implement like this:
...
hstring HostViewModel::NextButtonText()
{
    return hstring{ m_nextButtonText };
}

void HostViewModel::NextButtonText(hstring const& value)
{
    m_nextButtonText = value;
}
...

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

Eine Möglichkeit besteht darin, die Klasse abzuleiten, die Ihre Bindungsquelle von DependencyObject darstellt, und einen Datenwert über eine DependencyProperty verfügbar zu machen. So wird ein FrameworkElement feststellbar. FrameworkElements sind gute Bindungsquellen direkt außerhalb des Felds.

Eine einfachere Möglichkeit, eine Klasse feststellbar zu machen – und eine erforderliche für Klassen, die bereits über eine Basisklasse verfügen – besteht darin, System.ComponentModel.INotifyPropertyChanged zu implementieren. Dies erfordert nur die Implementierung eines einzelnen Ereignisses namens PropertyChanged. Ein Beispiel für die Verwendung von HostViewModel finden Sie unten.

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

    public event PropertyChangedEventHandler PropertyChanged = delegate { };

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

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

    public void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        // Raise the PropertyChanged event, passing the name of the property whose value has changed.
        this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}
// HostViewModel.idl
namespace DataBindingInDepth
{
    runtimeclass HostViewModel : Windows.UI.Xaml.Data.INotifyPropertyChanged
    {
        HostViewModel();
        String NextButtonText;
    }
}

// HostViewModel.h
// Add this field:
...
    winrt::event_token PropertyChanged(Windows::UI::Xaml::Data::PropertyChangedEventHandler const& handler);
    void PropertyChanged(winrt::event_token const& token) noexcept;

private:
    winrt::event<Windows::UI::Xaml::Data::PropertyChangedEventHandler> m_propertyChanged;
...

// HostViewModel.cpp
// Implement like this:
...
void HostViewModel::NextButtonText(hstring const& value)
{
    if (m_nextButtonText != value)
    {
        m_nextButtonText = value;
        m_propertyChanged(*this, Windows::UI::Xaml::Data::PropertyChangedEventArgs{ L"NextButtonText" });
    }
}

winrt::event_token HostViewModel::PropertyChanged(Windows::UI::Xaml::Data::PropertyChangedEventHandler const& handler)
{
    return m_propertyChanged.add(handler);
}

void HostViewModel::PropertyChanged(winrt::event_token const& token) noexcept
{
    m_propertyChanged.remove(token);
}
...

Jetzt ist die NextButtonText-Eigenschaft feststellbar. Wenn Sie eine unidirektionale oder bidirektionale Bindung zu dieser Eigenschaft erstellen (wie später gezeigt wird), abonniert das resultierende Bindungsobjekt das PropertyChanged-Ereignis . Wenn dieses Ereignis ausgelöst wird, empfängt der Handler des Bindungsobjekts ein Argument mit dem Namen der Eigenschaft, die geändert wurde. So weiß das Bindungsobjekt, welcher Wert der Eigenschaft wieder gelesen werden soll.

Damit Sie das oben gezeigte Muster nicht mehrmals implementieren müssen, können Sie, wenn Sie C# verwenden, einfach von der BindableBase-Basisklasse abgeleitet werden, die Sie im QuizGame-Beispiel (im Ordner "Common") finden. Hier ist ein Beispiel dafür, wie das aussieht.

public class HostViewModel : BindableBase
{
    private string nextButtonText;

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

    public string NextButtonText
    {
        get { return this.nextButtonText; }
        set { this.SetProperty(ref this.nextButtonText, value); }
    }
}
// Your BindableBase base class should itself derive from Windows::UI::Xaml::DependencyObject. Then, in HostViewModel.idl, derive from BindableBase instead of implementing INotifyPropertyChanged.

Hinweis

Für C++/WinRT wird jede Laufzeitklasse, die Sie in Ihrer Anwendung deklarieren, die von einer Basisklasse abgeleitet wird, als kompositable Klasse bezeichnet. Und es gibt Einschränkungen bei kompositablen Klassen. Damit eine Anwendung die von Visual Studio und vom Microsoft Store verwendeten Tests des Zertifizierungskits für Windows-Apps bestehen kann, um Übermittlungen zu überprüfen (und damit die Anwendung erfolgreich in den Microsoft Store aufgenommen wird), muss eine komponierbare Klasse letztendlich von einer Windows-Basisklasse abgeleitet werden. Dies bedeutet, dass die Klasse am Stamm der Vererbungshierarchie ein Typ sein muss, der aus einem Windows.*-Namespace stammt. Wenn Sie eine Laufzeitklasse von einer Basisklasse ableiten müssen, z. B. um eine BindableBase-Klasse für alle Ansichtsmodelle zu implementieren, die abgeleitet werden sollen, können Sie von Windows.UI.Xaml.DependencyObject abgeleitet werden.

Das Auslösen des PropertyChanged-Ereignisses mit einem Argument von String.Empty oder NULL gibt an, dass alle Nicht-Indexereigenschaften 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 (wobei indexer der Indexwert ist) 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 Auflistung von Objekten. In C# und Visual Basic-Code können Sie eine einmalige Bindung an ein Objekt vornehmen, das List(Of T) implementiert, um eine Auflistung anzuzeigen, die sich zur Laufzeit nicht ändert. Bei einer observablen Auflistung (beobachten, wann Elemente der Auflistung hinzugefügt und daraus entfernt werden), binden Sie stattdessen unidirektionale Bindung an ObservableCollection(Of T). In C++/CX-Code können Sie für observable und nicht observable Auflistungen an Vector<T> binden, und C++/WinRT verfügt über eigene Typen. Um eine Bindung an Ihre eigenen Sammlungsklassen zu erstellen, verwenden Sie die Anleitungen in der folgenden Tabelle.

Scenario C# und VB (CLR) C++/WinRT C++/CX
Binden an ein Objekt. Dabei kann es sich um ein beliebiges Objekt handeln. Dabei kann es sich um ein beliebiges Objekt handeln. Objekt muss BindableAttribute besitzen oder ICustomPropertyProvider implementieren.
Dient zum Abrufen von Eigenschaftsänderungsbenachrichtigungen aus einem gebundenen Objekt. Das Objekt muss INotifyPropertyChanged implementieren. Das Objekt muss INotifyPropertyChanged implementieren. Das Objekt muss INotifyPropertyChanged implementieren.
Binden an eine Auflistung. List(Of T) IVector von IInspectable oder IBindableObservableVector. Siehe XAML-Elementsteuerelemente; Binden an eine C++/WinRT-Auflistung und Sammlungen mit C++/WinRT. Vektor<T>
Abrufen von Sammlungsänderungsbenachrichtigungen aus einer gebundenen Sammlung. ObservableCollection(Of T) IObservableVector von IInspectable. Beispiel: winrt::single_threaded_observable_vector<T>. IObservableVector<T>. Vektor<T> implementiert diese Schnittstelle.
Implementieren Sie eine Sammlung, die bindung unterstützt. Extend List(Of T) or implement IList, IList(Of Object), IEnumerable, or IEnumerable(Of Object). Die Bindung an generische IList(Of T) und IEnumerable(Of T) wird nicht unterstützt. Implementieren sie IVector von IInspectable. Siehe XAML-Elementsteuerelemente; Binden an eine C++/WinRT-Auflistung und Sammlungen mit C++/WinRT. Implementieren Von IBindableVector, IBindableIterable, IVector<Object^>, IIterable<Object^>, IVector<IInspectable*>, oder IIterable<IInspectable*>. Die Bindung an generische IVector<T> und IIterable<T> wird nicht unterstützt.
Implementieren Sie eine Sammlung, die Änderungsbenachrichtigungen für die Sammlung unterstützt. Extend ObservableCollection(Of T) or implement (non-generic) IList and INotifyCollectionChanged. Implementieren Sie IObservableVector von IInspectable oder IBindableObservableVector. Implementieren Sie IBindableVector und IBindableObservableVector.
Implementieren Sie eine Auflistung, die das inkrementelle Laden unterstützt. Extend ObservableCollection(Of T) or implement (non-generic) IList and INotifyCollectionChanged. Implementieren Sie darüber hinaus ISupportIncrementalLoading. Implementieren Sie IObservableVector von IInspectable oder IBindableObservableVector. Implementieren von ISupportIncrementalLoading Implementieren Sie IBindableVector, IBindableObservableVector und ISupportIncrementalLoading.

Sie können Listensteuerelemente an beliebig große Datenquellen binden und dennoch eine hohe Leistung erzielen, indem Sie das inkrementelle Laden verwenden. Sie können beispielsweise Listensteuerelemente an Bing-Bildabfrageergebnisse binden, ohne alle Ergebnisse gleichzeitig laden zu müssen. Stattdessen laden Sie nur einige Ergebnisse sofort und laden zusätzliche Ergebnisse nach Bedarf. Um das inkrementelle Laden zu unterstützen, müssen Sie ISupportIncrementalLoading für eine Datenquelle implementieren, die Sammlungsänderungsbenachrichtigungen unterstützt. Wenn das Datenbindungsmodul weitere Daten anfordert, muss Ihre Datenquelle die entsprechenden Anforderungen stellen, die Ergebnisse integrieren und dann die entsprechenden Benachrichtigungen senden, um die Benutzeroberfläche zu aktualisieren.

Bindungsziel

In den beiden folgenden Beispielen ist die Button.Content-Eigenschaft das Bindungsziel, und der Wert wird auf eine Markuperweiterung festgelegt, die das Bindungsobjekt deklariert. Zuerst wird {x:Bind} und dann {Binding} angezeigt. Das Deklarieren von Bindungen im Markup ist der gängige Fall (es ist praktisch, lesbar und toolierbar). Sie können markups jedoch vermeiden und stattdessen (programmgesteuert) eine Instanz der Binding-Klasse erstellen, wenn Sie dies benötigen.

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

Wenn Sie C++/WinRT- oder Visual C++-Komponentenerweiterungen (C++/CX) verwenden, müssen Sie das BindableAttribute-Attribut jeder Laufzeitklasse hinzufügen, mit der Sie die {Binding} -Markuperweiterung verwenden möchten.

Von Bedeutung

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

Binding-Objekt mit {x:Bind} deklariert

Es gibt einen Schritt, den wir ausführen müssen, bevor wir unser {x:Bind} -Markup erstellen. Wir müssen unsere Bindungsquellklasse aus der Klasse verfügbar machen, die unsere Markupseite darstellt. Dazu fügen wir der MainPage-Seitenklasse eine Eigenschaft (vom Typ HostViewModel in diesem Fall) hinzu.

namespace DataBindingInDepth
{
    public sealed partial class MainPage : Page
    {
        public MainPage()
        {
            this.InitializeComponent();
            this.ViewModel = new HostViewModel();
        }
    
        public HostViewModel ViewModel { get; set; }
    }
}
// MainPage.idl
import "HostViewModel.idl";

namespace DataBindingInDepth
{
    runtimeclass MainPage : Windows.UI.Xaml.Controls.Page
    {
        MainPage();
        HostViewModel ViewModel{ get; };
    }
}

// MainPage.h
// Include a header, and add this field:
...
#include "HostViewModel.h"
...
    DataBindingInDepth::HostViewModel ViewModel();

private:
    DataBindingInDepth::HostViewModel m_viewModel{ nullptr };
...

// MainPage.cpp
// Implement like this:
...
MainPage::MainPage()
{
    InitializeComponent();

}

DataBindingInDepth::HostViewModel MainPage::ViewModel()
{
    return m_viewModel;
}
...

Dies geschieht, wir können nun einen genaueren Blick auf das Markup werfen, das das Bindungsobjekt deklariert. Im folgenden Beispiel wird das gleiche Button.Content-Bindungsziel verwendet, das wir zuvor im Abschnitt "Bindungsziel" verwendet haben, und zeigt, dass es an die HostViewModel.NextButtonText-Eigenschaft gebunden ist.

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

Beachten Sie den Wert, den wir für Path angeben. Dieser Wert wird im Kontext der Seite selbst interpretiert, und in diesem Fall beginnt der Pfad mit dem Verweisen auf die ViewModel-Eigenschaft , die wir soeben zur MainPage-Seite hinzugefügt haben. Diese Eigenschaft gibt eine HostViewModel-Instanz zurück, sodass wir in dieses Objekt punktieren können, um auf die HostViewModel.NextButtonText-Eigenschaft zuzugreifen. Und wir geben den Modus an, um den { x:Bind} -Standardwert einmal außer Kraft zu setzen.

Die Path-Eigenschaft unterstützt eine Vielzahl von Syntaxoptionen für die Bindung an geschachtelte Eigenschaften, angefügte Eigenschaften und Ganzzahl- und Zeichenfolgenindexer. Weitere Informationen finden Sie unter Property-path-Syntax. Die Bindung an Zeichenfolgenindexer bewirkt die Bindung an dynamische Eigenschaften, ohne ICustomPropertyProvider implementieren zu müssen. Weitere Einstellungen finden Sie unter {x:Bind}-Markuperweiterung.

Um zu veranschaulichen, dass die HostViewModel.NextButtonText-Eigenschaft tatsächlich feststellbar ist, fügen Sie der Schaltfläche einen Click-Ereignishandler hinzu, und aktualisieren Sie den Wert von HostViewModel.NextButtonText. Erstellen, ausführen und klicken Sie auf die Schaltfläche, um den Wert der Inhaltsaktualisierung der Schaltfläche anzuzeigen.

// MainPage.xaml.cs
private void Button_Click(object sender, RoutedEventArgs e)
{
    this.ViewModel.NextButtonText = "Updated Next button text";
}
// MainPage.cpp
void MainPage::ClickHandler(IInspectable const&, RoutedEventArgs const&)
{
    ViewModel().NextButtonText(L"Updated Next button text");
}

Hinweis

Änderungen an TextBox.Text werden an eine bidirektionale gebundene Quelle gesendet, wenn das TextBox-Objekt den Fokus verliert und nicht nach jedem Tastenanschlag des Benutzers.

DataTemplate und x:DataType

Innerhalb einer DataTemplate (unabhängig davon, ob es sich um eine Elementvorlage, eine Inhaltsvorlage oder eine Kopfzeilenvorlage handelt), wird der Wert von "Path " nicht im Kontext der Seite interpretiert, sondern im Kontext des datenobjekts, das vorlagenweise erstellt wird. Wenn Sie {x:Bind} in einer Datenvorlage verwenden, damit ihre Bindungen zur Kompilierzeit überprüft (und effizienter Code generiert werden können), muss dataTemplate den Typ des Datenobjekts mithilfe von x:DataType deklarieren. Das unten angegebene Beispiel kann als ItemTemplate eines Elementsteuerelements verwendet werden, das an eine Auflistung 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 typierte Objekte in Ihrem Pfad

Angenommen, Sie haben einen Typ namens SampleDataGroup, der eine Zeichenfolgeneigenschaft namens Title implementiert. Außerdem verfügen Sie über eine Eigenschaft "MainPage.SampleDataGroupAsObject", die vom Typobjekt stammt, aber tatsächlich eine Instanz von SampleDataGroup zurückgibt. Die Bindung <TextBlock Text="{x:Bind SampleDataGroupAsObject.Title}"/> führt zu einem Kompilierungsfehler, da die Title-Eigenschaft für das Typobjekt nicht gefunden wird. Die Lösung hierfür ist das Hinzufügen einer Umwandlung zur Pfadsyntax wie folgt: <TextBlock Text="{x:Bind ((data:SampleDataGroup)SampleDataGroupAsObject).Title}"/>. Hier ist ein weiteres Beispiel, in dem Element als Objekt deklariert wird, aber tatsächlich ein TextBlock-Element ist: <TextBlock Text="{x:Bind Element.Text}"/>. Und eine Umwandlung behebt das Problem: <TextBlock Text="{x:Bind ((TextBlock)Element).Text}"/>.

Wenn Ihre Daten asynchron geladen werden

Code zur Unterstützung von {x:Bind} wird zur Kompilierungszeit in den Partialklassen für Ihre Seiten generiert. Diese Dateien finden Sie in Ihrem obj Ordner mit Namen wie (für C#). <view name>.g.cs Der generierte Code enthält einen Handler für das Loading-Ereignis Ihrer Seite, und dieser Handler ruft die Initialize-Methode für eine generierte Klasse auf, die die Bindungen Ihrer Seite darstellt. Initialisieren Siewiederum Aktualisierung , um mit dem Verschieben von Daten zwischen der Bindungsquelle und dem Ziel zu beginnen. Das Laden wird direkt vor dem ersten Messdurchlauf der Seite oder des Benutzersteuerelements ausgelöst. Wenn Ihre Daten also asynchron geladen werden, ist sie möglicherweise nach dem Aufruf der Initialisierung nicht bereit. Nachdem Sie Also Daten geladen haben, können Sie erzwingen, dass einmalige Bindungen durch Aufrufen this.Bindings.Update();initialisiert werden. Wenn Sie nur einmalige Bindungen für asynchron geladene Daten benötigen, ist es viel billiger, sie auf diese Weise zu initialisieren, als es ist, unidirektionale Bindungen zu haben und Änderungen zu überwachen. Wenn Ihre Daten keine feinkörnigen Änderungen durchlaufen, und wenn sie wahrscheinlich als Teil einer bestimmten Aktion aktualisiert werden, können Sie Ihre Bindungen einmalig vornehmen und ein manuelles Update jederzeit mit einem Aufruf von Update erzwingen.

Hinweis

{x:Bind} eignet sich nicht für spät gebundene Szenarien, z. B. das Navigieren in der Wörterbuchstruktur eines JSON-Objekts oder enten sie. "Ente typing" ist eine schwache Form der Eingabe basierend auf lexikalischen Übereinstimmungen auf Eigenschaftsnamen (wie in, "wenn es läuft, schwimmen und quacks wie eine Ente, dann ist es eine Ente"). Bei der Enteneingabe wäre eine Bindung an die Age-Eigenschaft ebenso zufrieden mit einer Person oder einem Wine -Objekt (vorausgesetzt, dass diese Typen jeweils eine Alterseigenschaft hatten). Verwenden Sie für diese Szenarien die {Binding} -Markuperweiterung.

Binding-Objekt, das mit {Binding} deklariert wurde

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

// HostViewModel.idl
// Add this attribute:
[Windows.UI.Xaml.Data.Bindable]
runtimeclass HostViewModel : Windows.UI.Xaml.Data.INotifyPropertyChanged
...

Von Bedeutung

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

{Binding} geht standardmäßig davon aus, dass Sie an den DataContext Ihrer Markupseite binden. Daher legen wir den DataContext unserer Seite auf eine Instanz unserer Bindungsquellklasse fest (in diesem Fall vom Typ HostViewModel ). Das folgende Beispiel zeigt das Markup, das das Bindungsobjekt deklariert. Wir verwenden das gleiche Button.Content-Bindungsziel , das wir zuvor im Abschnitt "Bindungsziel" verwendet haben, und wir binden an die HostViewModel.NextButtonText-Eigenschaft .

<Page xmlns:viewmodel="using:DataBindingInDepth" ... >
    <Page.DataContext>
        <viewmodel:HostViewModel x:Name="viewModelInDataContext"/>
    </Page.DataContext>
    ...
    <Button Content="{Binding Path=NextButtonText}" ... />
</Page>
// MainPage.xaml.cs
private void Button_Click(object sender, RoutedEventArgs e)
{
    this.viewModelInDataContext.NextButtonText = "Updated Next button text";
}
// MainPage.cpp
void MainPage::ClickHandler(IInspectable const&, RoutedEventArgs const&)
{
    viewModelInDataContext().NextButtonText(L"Updated Next button text");
}

Beachten Sie den Wert, den wir für Path angeben. Dieser Wert wird im Kontext des DataContexts der Seite interpretiert, der in diesem Beispiel auf eine Instanz von HostViewModel festgelegt ist. Der Pfad verweist auf die HostViewModel.NextButtonText-Eigenschaft . Der Modus kann weggelassen werden, da der Standardwert von {Binding} hier funktioniert.

Der Standardwert von DataContext für ein UI-Element ist der geerbte Wert des übergeordneten Elements. Sie können diese Standardeinstellung natürlich überschreiben, indem Sie DataContext explizit festlegen, was wiederum standardmäßig von untergeordneten Elementen geerbt wird. Das explizite Festlegen von DataContext für ein Element ist nützlich, wenn Sie mehrere Bindungen verwenden möchten, die dieselbe Quelle verwenden.

Ein Bindungsobjekt verfügt über eine Source-Eigenschaft , die standardmäßig den DataContext des UI-Elements angibt, für das die Bindung deklariert wird. Sie können diese Standardeinstellung überschreiben, indem Sie "Source", "RelativeSource" oder "ElementName " explizit für die Bindung festlegen (einzelheiten hierzu finden Sie unter {Binding} ).

Innerhalb einer DataTemplate wird der DataContext automatisch auf das Datenobjekt festgelegt, das vorlaged ist. Das unten angegebene Beispiel kann als ItemTemplate eines Elementsteuerelements verwendet werden, das an eine Auflistung eines beliebigen Typs gebunden ist, der Zeichenfolgeneigenschaften namens "Title" und "Description" enthält.

<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 gebundene Quelle gesendet, wenn das TextBox-Objekt den Fokus verliert. Wenn Änderungen nach jedem Tastaturanschlag des Benutzers gesendet werden sollen, 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 "Explizit" festlegen. Anschließend behandeln Sie Ereignisse im Textfeld (in der Regel TextBox.TextChanged), rufen GetBindingExpression für das Ziel auf, um ein BindingExpression-Objekt abzurufen, und rufen schließlich BindingExpression.UpdateSource auf, um die Datenquelle programmgesteuert zu aktualisieren.

Die Path-Eigenschaft unterstützt eine Vielzahl von Syntaxoptionen für die Bindung an geschachtelte Eigenschaften, angefügte Eigenschaften und Ganzzahl- und Zeichenfolgenindexer. Weitere Informationen finden Sie unter Property-path-Syntax. Die Bindung an Zeichenfolgenindexer bewirkt die Bindung an dynamische Eigenschaften, ohne ICustomPropertyProvider implementieren zu müssen. Die ElementName-Eigenschaft ist nützlich für die Element-zu-Element-Bindung. Die RelativeSource-Eigenschaft hat mehrere Verwendungsmöglichkeiten, einer davon ist eine leistungsfähigere Alternative zur Vorlagenbindung in einer ControlTemplate. Weitere Einstellungen finden Sie unter {Binding}-Markuperweiterung und der Binding-Klasse .

Was geschieht, wenn die Quelle und das Ziel nicht derselbe Typ sind?

Wenn Sie die Sichtbarkeit eines UI-Elements basierend auf dem Wert einer booleschen Eigenschaft steuern oder ein UI-Element mit einer Farbe rendern möchten, die eine Funktion des Bereichs oder Trend eines numerischen Werts ist, oder wenn Sie einen Datums- und/oder Uhrzeitwert in einer UI-Elementeigenschaft anzeigen möchten, die eine Zeichenfolge erwartet, Dann müssen Sie Werte von einem Typ in einen anderen konvertieren. Es gibt Fälle, in denen die richtige Lösung darin besteht, eine andere Eigenschaft des richtigen Typs aus der Bindungsquellklasse verfügbar zu machen, und die Konvertierungslogik dort gekapselt und testbar zu halten. Das ist jedoch nicht flexibel oder skalierbar, wenn Sie über große Zahlen oder große Kombinationen von Quell- und Zieleigenschaften verfügen. In diesem Fall haben Sie eine Reihe von Optionen:

  • Wenn Sie {x:Bind} verwenden, können Sie direkt an eine Funktion binden, um diese Konvertierung durchzuführen.
  • Oder Sie können einen Wertkonverter angeben, der ein Objekt ist, das für die Konvertierung entwickelt wurde.

Wertkonverter

Hier ist ein Wertkonverter, der für eine einmalige oder unidirektionale Bindung geeignet ist, die einen DateTime-Wert in einen Zeichenfolgenwert 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();
    }
}
// See the "Formatting or converting data values for display" section in the "Data binding overview" topic.

Und so verwenden Sie diesen Wertkonverter in Ihrem Bindungsobjektmarkup.

<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 Convert - und ConvertBack-Methoden 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 vom Ziel übergeben werden (für eine bidirektionale Bindung), ruft das Bindungsmodul ConvertBack auf und übergibt die zurückgegebenen Daten an die Quelle.

Der Konverter verfügt auch über optionale Parameter: ConverterLanguage, wodurch die in der Konvertierung zu verwendende Sprache angegeben werden kann, und ConverterParameter, die das Übergeben eines Parameters für die Konvertierungslogik ermöglicht. Ein Beispiel, das einen Konverterparameter verwendet, finden Sie unter "IValueConverter".

Hinweis

Wenn in 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 einen Standardwert anzuzeigen, der verwendet werden kann, wenn die Bindungsquelle nicht aufgelöst werden kann, legen Sie die FallbackValue-Eigenschaft für das Bindungsobjekt im Markup fest. Dies ist nützlich, um Konvertierungs- und Formatierungsfehler zu behandeln. Es ist auch hilfreich, eine Bindung an Quelleigenschaften zu erstellen, die möglicherweise nicht für alle Objekte in einer gebundenen Auflistung heterogener Typen vorhanden sind.

Wenn Sie ein Textsteuerelement an einen Wert binden, der keine Zeichenfolge ist, konvertiert das Datenbindungsmodul den Wert in eine Zeichenfolge. Wenn der Wert ein Verweistyp ist, ruft das Datenbindungsmodul den Zeichenfolgenwert ab, indem "ICustomPropertyProvider.GetStringRepresentation " oder "IStringable.ToString " aufgerufen wird, falls verfügbar, und andernfalls wird Object.ToString aufgerufen. Beachten Sie jedoch, dass das Bindungsmodul jede ToString-Implementierung ignoriert, die die Basisklassenimplementierung ausblendet. Unterklassenimplementierungen sollten stattdessen die ToString-Basisklasse außer Kraft setzen. In nativen Sprachen scheinen alle verwalteten Objekte ICustomPropertyProvider und IStringable zu implementieren. Alle Aufrufe von GetStringRepresentation und IStringable.ToString werden jedoch an Object.ToString oder eine Außerkraftsetzung dieser Methode und niemals an eine neue ToString-Implementierung weitergeleitet, die die Basisklassenimplementierung ausblendet.

Hinweis

Ab Windows 10, Version 1607, bietet das XAML-Framework einen integrierten booleschen zu Visibility-Konverter. Der Konverter ordnet den Wert der Visible-Aufzählung und "false " zu "Collapsed " zu, sodass Sie eine Visibility-Eigenschaft an einen booleschen Wert binden können, ohne einen Konverter zu erstellen. 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 Versionsadaptiven Code.

Funktionsbindung in {x:Bind}

{x:Bind} ermöglicht den letzten Schritt in einem Bindungspfad, eine Funktion zu sein. Dies kann zum Ausführen von Konvertierungen und zum Ausführen von Bindungen verwendet werden, die von mehreren Eigenschaften abhängig sind. Siehe Funktionen in x:Bind

Element-zu-Element-Bindung

Sie können die Eigenschaft eines XAML-Elements an die Eigenschaft eines anderen XAML-Elements binden. Hier ist ein Beispiel dafür, wie das im Markup aussieht.

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

Von Bedeutung

Informationen zum erforderlichen Workflow für die Element-zu-Element-Bindung mit C++/WinRT finden Sie unter Element-zu-Element-Bindung.

Ressourcenwörterbücher mit {x:Bind}

Die {x:Bind}-Markuperweiterung hängt von der Codegenerierung ab, daher benötigt sie eine CodeBehind-Datei mit einem Konstruktor, der InitializeComponent aufruft (um den generierten Code zu initialisieren). Sie verwenden das Ressourcenwörterbuch erneut, indem Sie den Typ instanziieren (sodass InitializeComponent aufgerufen wird), anstatt auf den Dateinamen zu verweisen. Nachfolgend finden Sie ein Beispiel dafür, was Sie tun müssen, wenn Sie über ein vorhandenes Ressourcenwörterbuch verfügen und {x:Bind} darin 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 Windows.UI.Xaml.Data;
 
namespace ExampleNamespace
{
    public partial class TemplatesResourceDictionary
    {
        public TemplatesResourceDictionary()
        {
            InitializeComponent();
        }
    }
}

MainPage.xaml

<Page x:Class="ExampleNamespace.MainPage"
    ....
    xmlns:examplenamespace="using:ExampleNamespace">

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

Mischen von {x:Bind} und {Binding} in einem wiederverwendbaren Stil

Während im vorherigen Beispiel die Verwendung von {x:Bind} in DataTemplates gezeigt wurde, können Sie auch wiederverwendbare Formatvorlagen erstellen, die sowohl {x:Bind} als auch {Binding}-Markuperweiterungen kombinieren. Dies ist nützlich, wenn Sie einige Eigenschaften binden möchten, um bekannte Werte mithilfe von {x:Bind} und anderen Eigenschaften zur Laufzeit von DataContext-Werten mithilfe von {Binding} zu kompilieren.

Hier ist ein Beispiel, das zeigt, wie Sie eine wiederverwendbare Schaltflächenformatvorlage erstellen, die beide Bindungsansätze verwendet:

TemplatesResourceDictionary.xaml

<ResourceDictionary
    x:Class="ExampleNamespace.TemplatesResourceDictionary"
    .....
    xmlns:examplenamespace="using:ExampleNamespace">
    
    <!-- DataTemplate using x:Bind -->
    <DataTemplate x:Key="EmployeeTemplate" x:DataType="examplenamespace:IEmployee">
        <Grid>
            <TextBlock Text="{x:Bind Name}"/>
        </Grid>
    </DataTemplate>
    
    <!-- Style that mixes x:Bind and Binding -->
    <Style x:Key="CustomButtonStyle" TargetType="Button">
        <Setter Property="Background" Value="{Binding ButtonBackgroundBrush}"/>
        <Setter Property="Foreground" Value="{Binding ButtonForegroundBrush}"/>
        <Setter Property="FontSize" Value="16"/>
        <Setter Property="Margin" Value="4"/>
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="Button">
                    <Border x:Name="RootBorder"
                            Background="{TemplateBinding Background}"
                            BorderBrush="{TemplateBinding BorderBrush}"
                            BorderThickness="{TemplateBinding BorderThickness}"
                            CornerRadius="4">
                        <StackPanel Orientation="Horizontal" 
                                    HorizontalAlignment="Center"
                                    VerticalAlignment="Center">
                            <!-- x:Bind to a static property or page-level property -->
                            <Ellipse Width="8" Height="8" 
                                     Fill="{x:Bind DefaultIndicatorBrush}" 
                                     Margin="0,0,8,0"/>
                            <!-- Binding to DataContext -->
                            <ContentPresenter x:Name="ContentPresenter"
                                              Content="{TemplateBinding Content}"
                                              Foreground="{TemplateBinding Foreground}"
                                              FontSize="{TemplateBinding FontSize}"/>
                        </StackPanel>
                        <VisualStateManager.VisualStateGroups>
                            <VisualStateGroup x:Name="CommonStates">
                                <VisualState x:Name="Normal"/>
                                <VisualState x:Name="PointerOver">
                                    <VisualState.Setters>
                                        <!-- Binding to DataContext for hover color -->
                                        <Setter Target="RootBorder.Background" 
                                                Value="{Binding ButtonHoverBrush}"/>
                                    </VisualState.Setters>
                                </VisualState>
                                <VisualState x:Name="Pressed">
                                    <VisualState.Setters>
                                        <!-- x:Bind to a compile-time known resource -->
                                        <Setter Target="RootBorder.Background" 
                                                Value="{x:Bind DefaultPressedBrush}"/>
                                    </VisualState.Setters>
                                </VisualState>
                            </VisualStateGroup>
                        </VisualStateManager.VisualStateGroups>
                    </Border>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</ResourceDictionary>

TemplatesResourceDictionary.xaml.cs

using Windows.UI;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Media;
 
namespace ExampleNamespace
{
    public partial class TemplatesResourceDictionary
    {
        public TemplatesResourceDictionary()
        {
            InitializeComponent();
        }
        
        // Properties for x:Bind - these are compile-time bound
        public SolidColorBrush DefaultIndicatorBrush { get; } = 
            new SolidColorBrush(Colors.Green);
            
        public SolidColorBrush DefaultPressedBrush { get; } = 
            new SolidColorBrush(Colors.DarkGray);
    }
}

Verwendung in "MainPage.xaml" mit einem ViewModel, das Laufzeitwerte bereitstellt:

<Page x:Class="ExampleNamespace.MainPage"
    ....
    xmlns:examplenamespace="using:ExampleNamespace">

    <Page.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <examplenamespace:TemplatesResourceDictionary/>
            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>
    </Page.Resources>
    
    <Page.DataContext>
        <examplenamespace:ButtonThemeViewModel/>
    </Page.DataContext>

    <StackPanel Margin="20">
        <!-- This button uses the mixed binding style -->
        <Button Content="Save" Style="{StaticResource CustomButtonStyle}"/>
        <Button Content="Cancel" Style="{StaticResource CustomButtonStyle}"/>
    </StackPanel>
</Page>

ButtonThemeViewModel.cs (der DataContext, der Laufzeitbindungswerte bereitstellt):

using System.ComponentModel;
using Windows.UI;
using Windows.UI.Xaml.Media;

namespace ExampleNamespace
{
    public class ButtonThemeViewModel : INotifyPropertyChanged
    {
        private SolidColorBrush _buttonBackgroundBrush = new SolidColorBrush(Colors.LightBlue);
        private SolidColorBrush _buttonForegroundBrush = new SolidColorBrush(Colors.DarkBlue);
        private SolidColorBrush _buttonHoverBrush = new SolidColorBrush(Colors.LightCyan);

        public SolidColorBrush ButtonBackgroundBrush
        {
            get => _buttonBackgroundBrush;
            set
            {
                _buttonBackgroundBrush = value;
                PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(ButtonBackgroundBrush)));
            }
        }

        public SolidColorBrush ButtonForegroundBrush
        {
            get => _buttonForegroundBrush;
            set
            {
                _buttonForegroundBrush = value;
                PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(ButtonForegroundBrush)));
            }
        }

        public SolidColorBrush ButtonHoverBrush
        {
            get => _buttonHoverBrush;
            set
            {
                _buttonHoverBrush = value;
                PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(ButtonHoverBrush)));
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;
    }
}

In diesem Beispiel:

  • {Binding} wird für Eigenschaften verwendet, die von DataContext (ButtonBackgroundBrush, ButtonForegroundBrush, ButtonHoverBrush) abhängen.
  • {x:Bind} wird für Eigenschaften verwendet, die zur Kompilierungszeit bekannt sind und zum ResourceDictionary selbst gehören (DefaultIndicatorBrush, DefaultPressedBrush)
  • Die Formatvorlage ist wiederverwendbar und kann auf eine beliebige Schaltfläche angewendet werden.
  • Laufzeitdesigns sind über DataContext möglich und profitieren dennoch von der Leistung von {x:Bind} für statische Elemente.

Ereignisbindung und ICommand

{x:Bind} unterstützt ein Feature namens Ereignisbindung. Mit diesem Feature können Sie den Handler für ein Ereignis mithilfe einer Bindung angeben, bei der es sich um eine zusätzliche Option über das Behandeln von Ereignissen mit einer Methode für die CodeBehind-Datei handelt. Angenommen, Sie haben eine RootFrame-Eigenschaft für Ihre MainPage-Klasse .

public sealed partial class MainPage : Page
{
    ...
    public Frame RootFrame { get { return Window.Current.Content as Frame; } }
}

Anschließend können Sie das Click-Ereignis einer Schaltfläche an eine Methode für das Frame-Objekt binden, das von der RootFrame-Eigenschaft wie folgt zurückgegeben wird. Beachten Sie, dass wir auch die IsEnabled-Eigenschaft der Schaltfläche an ein anderes Element desselben Frames binden.

<AppBarButton Icon="Forward" IsCompact="True"
IsEnabled="{x:Bind RootFrame.CanGoForward, Mode=OneWay}"
Click="{x:Bind RootFrame.GoForward}"/>

Überladene Methoden können nicht zum Behandeln eines Ereignisses mit dieser Technik verwendet werden. Wenn die Methode, die das Ereignis verarbeitet, Parameter aufweist, müssen sie alle aus den Typen aller Ereignisparameter zugewiesen werden können. In diesem Fall ist Frame.GoForward nicht überladen und hat keine Parameter (aber es wäre auch dann gültig, wenn es zwei Objektparameter verwendet hat). Frame.GoBack ist jedoch überladen, sodass wir diese Methode nicht mit dieser Technik verwenden können.

Die Ereignisbindungstechnik ähnelt der Implementierung und Verwendung von Befehlen (ein Befehl ist eine Eigenschaft, die ein Objekt zurückgibt, das die ICommand-Schnittstelle implementiert). Sowohl {x:Bind} als auch {Binding} funktionieren mit Befehlen. Damit Sie das Befehlsmuster nicht mehrmals implementieren müssen, können Sie die DelegateCommand-Hilfsklasse verwenden, die Sie im QuizGame-Beispiel (im Ordner "Common") finden.

Binden an eine Sammlung von Ordnern oder Dateien

Sie können die APIs im Windows.Storage-Namespace verwenden, um Ordner- und Dateidaten abzurufen. Die verschiedenen Methoden "GetFilesAsync", " GetFoldersAsync" und "GetItemsAsync " geben jedoch keine Werte zurück, die für die Bindung an Listensteuerelemente geeignet sind. Stattdessen müssen Sie an die Rückgabewerte der Methoden "GetVirtualizedFilesVector", "GetVirtualizedFoldersVector" und " GetVirtualizedItemsVector " der FileInformationFactory-Klasse binden. Das folgende Codebeispiel aus dem Beispiel "StorageDataSource" und "GetVirtualizedFilesVector " zeigt das typische Verwendungsmuster. Denken Sie daran, die PicturesLibrary-Funktion im App-Paketmanifest zu deklarieren, und vergewissern Sie sich, dass bilder im Ordner "Bilderbibliothek" 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 diesen Ansatz, um eine schreibgeschützte Ansicht von Datei- und Ordnerinformationen zu erstellen. Sie können bidirektionale Bindungen an die Datei- und Ordnereigenschaften erstellen, z. B. damit Benutzer einen Song in einer Musikansicht bewerten können. Alle Änderungen werden jedoch erst beibehalten, wenn Sie die entsprechende SavePropertiesAsync-Methode aufrufen (z. B. MusicProperties.SavePropertiesAsync). Sie sollten Änderungen übernehmen, wenn das Element den Fokus verliert, da dadurch eine Auswahlzurücksetzung ausgelöst wird.

Beachten Sie, dass die bidirektionale Bindung mit dieser Technik nur bei indizierten Speicherorten funktioniert, z. B. Musik. Sie können ermitteln, ob ein Speicherort indiziert wird, indem Sie die FolderInformation.GetIndexedStateAsync-Methode aufrufen.

Beachten Sie auch, dass ein virtualisierter Vektor null für einige Elemente zurückgeben kann, bevor er seinen Wert auffüllt. Sie sollten z. B. auf NULL überprüfen, bevor Sie den SelectedItem-Wert eines Listensteuerelements verwenden, das an einen virtualisierten Vektor gebunden ist, oder stattdessen SelectedIndex verwenden.

Binden an Daten gruppiert nach einem Schlüssel

Wenn Sie eine flache Sammlung von Elementen (z. B. Bücher, dargestellt durch eine BookSku-Klasse ) verwenden und die Elemente mithilfe einer gemeinsamen Eigenschaft als Schlüssel (z. B. die BookSku.AuthorName-Eigenschaft ) gruppieren, wird das Ergebnis als gruppierte Daten bezeichnet. Wenn Sie Daten gruppieren, handelt es sich nicht mehr um eine flache Sammlung. Gruppierte Daten sind eine Sammlung von Gruppenobjekten, wobei jedes Gruppenobjekt über

  • ein Schlüssel und
  • eine Auflistung von Elementen, deren Eigenschaft diesem Schlüssel entspricht.

Um das Buchbeispiel erneut zu verwenden, führt das Ergebnis der Gruppierung der Bücher nach Autorennamen zu einer Sammlung von Autorennamensgruppen, in denen jede Gruppe verfügt

  • ein Schlüssel, bei dem es sich um einen Autorennamen handelt, und
  • eine Auflistung der BookSku-Elemente, deren AuthorName-Eigenschaft dem Schlüssel der Gruppe entspricht.

Im Allgemeinen binden Sie zum Anzeigen einer Auflistung die ItemsSource eines Elementsteuerelements (z. B. ListView oder GridView) direkt an eine Eigenschaft, die eine Auflistung 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 es sich bei der Bindung an gruppierte Daten handelt), benötigen Sie die Dienste eines zwischengeschalteten Objekts namens CollectionViewSource , das sich zwischen dem Elementsteuerelement und der Bindungsquelle befindet. Sie binden die CollectionViewSource an die Eigenschaft, die gruppierte Daten zurückgibt, und binden das Elementsteuerelement an die CollectionViewSource. Ein zusätzlicher Mehrwert einer CollectionViewSource besteht darin, dass es das aktuelle Element nachverfolgt, sodass Sie mehrere Elementsteuerelemente synchronisieren können, indem Sie sie alle an dieselbe CollectionViewSource binden. Sie können auch programmgesteuert über die ICollectionView.CurrentItem-Eigenschaft des Objekts, das von der CollectionViewSource.View-Eigenschaft zurückgegeben wird, 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 Ihre 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 Auflistungstyp abgeleitet (z. B. List<T>), sodass das Gruppenobjekt selbst die Gruppe von Elementen ist. Bei diesem Muster müssen Sie ItemsPath nicht festlegen. Im Muster "has-a-group" verfügt das Gruppenobjekt über eine oder mehrere Eigenschaften eines Auflistungstyps (z. B. List<T>), sodass die Gruppe eine Gruppe von Elementen in Form einer Eigenschaft hat (oder 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 Seitenklasse hat eine Eigenschaft namens ViewModel, die eine Instanz unseres Ansichtsmodells zurückgibt. Die CollectionViewSource bindet an die Authors-Eigenschaft des Ansichtsmodells (Authors ist die Sammlung von Gruppenobjekten) und gibt außerdem an, dass es sich um die Author.BookSkus-Eigenschaft handelt, die die gruppierten Elemente enthält. Schließlich ist gridView an die CollectionViewSource gebunden und hat seine Gruppenformatvorlage definiert, sodass die Elemente in Gruppen gerendert werden können.

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

Sie können das Muster "is-a-group" auf eine von zwei Arten implementieren. Eine Möglichkeit besteht darin, Ihre eigene Gruppenklasse zu erstellen. Leiten Sie die Klasse von List<T> ab (wobei T der Typ der Elemente ist). Beispiel: public class Author : List<BookSku>. Die zweite Möglichkeit besteht darin, einen LINQ-Ausdruck zum dynamischen Erstellen von Gruppenobjekten (und einer Gruppenklasse) aus ähnlichen Eigenschaftswerten der BookSku-Elemente zu verwenden. Dieser Ansatz – nur eine flache Liste von Elementen zu verwalten und sie direkt zusammen zu gruppieren – ist typisch für eine App, die auf Daten von einem Clouddienst zugreift. Sie haben die Flexibilität, Bücher nach Autor oder Genre (z. B.) zu gruppieren, ohne spezielle Gruppenklassen wie Autor und Genre zu benötigen.

Das folgende Beispiel veranschaulicht das Muster "is-a-group" mithilfe von LINQ. Diesmal gruppieren wir Bücher nach Genre, die mit dem Genrenamen in den Gruppenkopfzeilen angezeigt werden. Dies wird durch den Schlüsseleigenschaftspfad im Verweis auf den Gruppenschlüsselwert angegeben.

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

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

Denken Sie daran, dass beim Verwenden von {x:Bind} mit Datenvorlagen der Typ angegeben werden muss, an den ein x:DataType-Wert festgelegt wird. Wenn der Typ generisch ist, können wir dies im Markup nicht ausdrücken, sodass wir { Binding} stattdessen in der Gruppenformatvorlagenkopfvorlage verwenden müssen.

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

Ein SemanticZoom-Steuerelement ist eine hervorragende Möglichkeit, damit Ihre Benutzer gruppierte Daten anzeigen und navigieren können. Die Bookstore2-Beispiel-App veranschaulicht die Verwendung des SemanticZoom. In dieser App können Sie eine Liste von Büchern nach Autor (der vergrößerten Ansicht) gruppieren oder verkleinert werden, um eine Sprungliste von Autoren (die verkleinerte Ansicht) anzuzeigen. Die Sprungliste ermöglicht eine wesentlich schnellere Navigation als das Scrollen durch die Liste der Bücher. Die verkleinerten und verkleinerten Ansichten sind tatsächlich ListView - oder GridView-Steuerelemente , die an dieselbe CollectionViewSource gebunden sind.

Abbildung eines SemanticZoom

Wenn Sie eine Bindung an hierarchische Daten (z. B. Unterkategorien innerhalb von Kategorien) festlegen, können Sie die hierarchischen Ebenen in Der Benutzeroberfläche mit einer Reihe von Elementsteuerelementen anzeigen. Eine Auswahl in einem Elementsteuerelement bestimmt den Inhalt der nachfolgenden Elementsteuerelemente. Sie können die Listen synchronisieren, indem Sie jede Liste an ihre eigene CollectionViewSource binden und die CollectionViewSource-Instanzen in einer Kette binden. Dies wird als Master-/Detailansicht (oder Listen-/Detailansicht) bezeichnet. Weitere Informationen finden Sie unter How to bind to hierarchical data and create a master/details view.

Diagnostizieren und Debuggen von Datenbindungsproblemen

Ihr Bindungsmarkup enthält die Namen von Eigenschaften (und für C#, manchmal Felder und Methoden). Wenn Sie also eine Eigenschaft umbenennen, müssen Sie auch jede Bindung ändern, die darauf verweist. Vergessen Sie, dies zu einem typischen Beispiel für einen Datenbindungsfehler zu führen, und Ihre App wird entweder nicht kompiliert oder nicht ordnungsgemäß ausgeführt.

Die von {x:Bind} und {Binding} erstellten Bindungsobjekte sind weitgehend funktional gleichwertig. {x:Bind} verfügt jedoch über Typinformationen für die Bindungsquelle und generiert quellcode zur Kompilierungszeit. Mit {x:Bind} erhalten Sie die gleiche Art von Problemerkennung, die Sie mit dem Rest Ihres Codes erhalten. Dazu gehören die Kompilierungszeitüberprüfung Ihrer Bindungsausdrücke und das Debuggen durch Festlegen von Haltepunkten im Quellcode, der als Teilklasse für Ihre Seite generiert wurde. Diese Klassen finden Sie in den Dateien in Ihrem obj Ordner mit Namen wie (für C#). <view name>.g.cs Wenn sie ein Problem mit einer Bindung haben, aktivieren Sie im Microsoft Visual Studio-Debugger unterbrechungslose Ausnahmen . Der Debugger unterbricht die Ausführung an diesem Punkt, und Sie können dann debuggen, was nicht geklappt hat. Der von {x:Bind} generierte Code folgt demselben Muster für jeden Teil des Diagramms der Bindungsquellknoten, und Sie können die Informationen im Fenster "Aufrufstapel " verwenden, um die Abfolge der Aufrufe zu bestimmen, die zu dem Problem geführt haben.

{Binding} enthält keine Typinformationen für die Bindungsquelle. Wenn Sie ihre App jedoch mit dem angefügten Debugger ausführen, werden alle Bindungsfehler im Ausgabefenster in Visual Studio angezeigt.

Erstellen von Bindungen im Code

Anmerkung Dieser Abschnitt gilt nur für {Binding}, da Sie keine {x:Bind} -Bindungen im Code erstellen können. Einige der gleichen Vorteile von {x:Bind} können jedoch mit DependencyObject.RegisterPropertyChangedCallback erreicht werden, wodurch Sie sich für Änderungsbenachrichtigungen für jede Abhängigkeitseigenschaft registrieren können.

Sie können UI-Elemente auch mit Daten verbinden, indem Sie prozeduralen Code anstelle von XAML verwenden. Erstellen Sie dazu ein neues Binding-Objekt , legen Sie die entsprechenden Eigenschaften fest, und rufen Sie dann "FrameworkElement.SetBinding " oder "BindingOperations.SetBinding" auf. Das programmgesteuerte Erstellen von Bindungen ist nützlich, wenn Sie die Bindungseigenschaftswerte zur Laufzeit auswählen oder eine einzelne Bindung zwischen mehreren Steuerelementen freigeben möchten. Beachten Sie jedoch, dass Sie die Bindungseigenschaftswerte nicht ändern können, nachdem Sie SetBinding aufgerufen haben.

Das folgende Beispiel zeigt, wie eine Bindung im Code implementiert wird.

<TextBox x:Name="MyTextBox" Text="Text"/>
// Create an instance of the MyColors class 
// that implements INotifyPropertyChanged.
MyColors 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.
Binding binding = new Binding() { Path = new PropertyPath("Brush1") };
MyTextBox.SetBinding(TextBox.ForegroundProperty, binding);
' Create an instance of the MyColors class 
' that implements INotifyPropertyChanged. 
Dim textcolor As 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.
Dim binding As New Binding() With {.Path = New PropertyPath("Brush1")}
MyTextBox.SetBinding(TextBox.ForegroundProperty, binding)

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

Merkmal {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 auf der Seite und nicht auf "DataContext" gewurzelt.
Indexer {x:Bind Groups[2].Title}
-
{Binding Groups[2].Title}
Bindet an das angegebene Element in der Auflistung. Es werden nur ganzzahlbasierte Indizes unterstützt.
Angefügte Eigenschaften {x:Bind Button22.(Grid.Row)}
-
{Binding Button22.(Grid.Row)}
Angefügte Eigenschaften werden mithilfe von Klammern angegeben. Wenn die Eigenschaft nicht in einem XAML-Namespace deklariert ist, stellen Sie sie einem XML-Namespace voran, der einem Codenamespace am Anfang des Dokuments zugeordnet werden soll.
Guss {x:Bind groups[0].(data:SampleDataGroup.Title)}
-
Für {Binding} nicht erforderlich.
Umwandlungen werden mithilfe von Klammern angegeben. Wenn die Eigenschaft nicht in einem XAML-Namespace deklariert ist, stellen Sie sie einem XML-Namespace voran, der einem Codenamespace am Anfang des Dokuments zugeordnet werden soll.
Konverter {x:Bind IsShown, Converter={StaticResource BoolToVisibility}}
-
{Binding IsShown, Converter={StaticResource BoolToVisibility}}
Konverter müssen im Stammverzeichnis von Page/ResourceDictionary oder in "App.xaml" deklariert werden.
ConverterParameter, ConverterLanguage {x:Bind IsShown, Converter={StaticResource BoolToVisibility}, ConverterParameter=One, ConverterLanguage=fr-fr}
-
{Binding IsShown, Converter={StaticResource BoolToVisibility}, ConverterParameter=One, ConverterLanguage=fr-fr}
Konverter müssen im Stammverzeichnis von Page/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 einfache Anführungszeichen für einen Zeichenfolgenwert.
FallbackValue {x:Bind Name, FallbackValue='empty'}
-
{Binding Name, FallbackValue='empty'}
Wird verwendet, wenn ein Teil des Pfads für die Bindung (mit Ausnahme des Blatts) null ist.
ElementName {x:Bind slider1.Value}
-
{Binding Value, ElementName=slider1}
Bei {x:Bind} binden Sie eine Bindung an ein Feld; Der Pfad ist standardmäßig auf der Seite verwurzelt, sodass auf jedes benannte Element über sein Feld zugegriffen werden kann.
RelativeSource: Selbst <Rectangle x:Name="rect1" Width="200" Height="{x:Bind rect1.Width}" ... />
-
<Rectangle Width="200" Height="{Binding Width, RelativeSource={RelativeSource Self}}" ... />
Benennen Sie mit {x:Bind} das Element, und verwenden Sie dessen Namen in Path.
RelativeSource: TemplatedParent Für {x:Bind} nicht erforderlich
-
{Binding <path>, RelativeSource={RelativeSource TemplatedParent}}
Mit {x:Bind} TargetType auf ControlTemplate gibt die Bindung an das übergeordnete Element der Vorlage an. Bei {Binding} Reguläre Vorlagenbindung kann für die meisten Anwendungen in Steuerelementvorlagen verwendet werden. Verwenden Sie jedoch TemplatedParent, wo Sie einen Konverter oder eine bidirektionale Bindung verwenden müssen.<
Quelle Für {x:Bind} nicht erforderlich
-
<ListView ItemsSource="{Binding Orders, Source={StaticResource MyData}}"/>
Für {x:Bind} können Sie das benannte Element direkt verwenden, eine Eigenschaft oder einen statischen Pfad verwenden.
Modus {x:Bind Name, Mode=OneWay}
-
{Binding Name, Mode=TwoWay}
Der Modus kann OneTime, OneWay oder TwoWay sein. {x:Bind} ist standardmäßig auf OneTime festgelegt; {Binding} ist standardmäßig auf OneWay festgelegt.
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 PropertyChanged-Verhalten für alle Fälle außer "TextBox.Text", wobei das LostFocus-Verhalten verwendet wird.