Übersicht über Datenbindung

Die Windows Presentation Foundation (WPF)-Datenbindung bietet für Anwendungen eine einfache und konsistente Möglichkeit, Daten darzustellen und mit ihnen zu interagieren. Elemente können an Daten aus einer Vielzahl von Datenquellen in Form von common language runtime (CLR)-Objekten und XML gebunden werden. ContentControl wie Button und ItemsControl wie ListBox und ListView verfügen über integrierte Funktionen, die eine flexible Formatierung von einzelnen Datenelementen oder Auflistungen von Datenelementen ermöglichen. Sortier-, Filter- und Gruppenansichten können übergreifend für die Daten generiert werden.

Die Datenbindungsfunktionen in WPF bieten gegenüber herkömmlichen Modellen einige Vorteile. Dazu zählen eine Reihe von Eigenschaften, die Datenbindung grundsätzlich unterstützen, eine flexible Darstellung von Daten auf einer UI sowie die klare Trennung zwischen Geschäftslogik und UI.

In diesem Thema werden zunächst die grundlegenden Konzepte der WPF-Datenbindung erläutert, und anschließend wird auf die Verwendung der Binding-Klasse und anderer Datenbindungsfunktionen eingegangen.

Dieses Thema enthält folgende Abschnitte.

  • Was ist Datenbindung?
  • Grundlegende Konzepte zur Datenbindung
  • Erstellen einer Bindung
  • Datenkonvertierung
  • Binden an Auflistungen
  • Datenvorlagen
  • Datenvalidierung
  • Debugverfahren
  • Verwandte Abschnitte

Was ist Datenbindung?

Durch Datenbindung wird eine Verbindung zwischen der UI der Anwendung und der Geschäftslogik hergestellt. Wenn die Bindung über die korrekten Einstellungen verfügt und die Daten die richtigen Benachrichtigungen bereitstellen, ändern sich die an die Daten gebundenen Elemente automatisch bei jeder Änderung des Werts der Daten, sodass die Änderungen entsprechend wiedergegeben werden. Datenbindung kann auch bedeuten, dass bei einer Änderung der äußeren Darstellung von Daten in einem Element die zugrunde liegenden Daten automatisch aktualisiert werden können, um die Änderung wiederzugeben. Wenn der Benutzer beispielsweise den Wert in einem TextBox-Element bearbeitet, wird der zugrunde liegende Datenwert automatisch aktualisiert, um diese Änderung anzuzeigen.

Eine typische Verwendung der Datenbindung wäre, die auf einem Server oder lokal gespeicherten Konfigurationsdaten in Formulare oder andere Steuerelemente der UI einzufügen. In WPF wird dieses Konzept dahingehend erweitert, dass auch eine Vielzahl von Eigenschaften an die unterschiedlichsten Datenquellen gebunden werden können. In WPF können Abhängigkeitseigenschaften von Elementen an CLR-Objekte (einschließlich ADO.NET-Objekte oder Objekte, die Webdiensten und Webeigenschaften zugeordnet sind) und XML-Daten gebunden werden.

Ein Beispiel für eine Datenbindung finden Sie in der folgenden Anwendung UI in der Demo für die Datenbindung:

Bildschirmabbildung für Datenbindungsbeispiel

Oben sehen Sie die UI einer Anwendung, die eine Liste von Auktionselementen anzeigt. Die Anwendung veranschaulicht die folgenden Datenbindungsfunktionen:

  • Der Inhalt von ListBox ist an eine Auflistung von AuctionItem-Objekten gebunden. Ein AuctionItem-Objekt verfügt über Eigenschaften wie Description, StartPrice, StartDate, Category, SpecialFeatures usw.

  • Die Daten (AuctionItem-Objekte), die in ListBox angezeigt werden, sind vorlagenbasiert, sodass für jedes Element Beschreibung und aktueller Preis zu sehen sind. Zu diesem Zweck wird DataTemplate verwendet. Darüber hinaus hängt die Darstellung jedes Elements vom SpecialFeatures-Wert des angezeigten AuctionItem ab. Wenn der SpecialFeatures-Wert von AuctionItem Color ist, hat das Element einen blauen Rahmen. Wenn der Wert Highlight ist, hat das Element einen orangefarbenen Rahmen und einen Stern. Im Abschnitt Datenvorlagen erhalten Sie weitere Informationen über Datenvorlagen.

  • Der Benutzer kann die Daten mithilfe der bereitgestellten CheckBox gruppieren, filtern oder sortieren. Im oben gezeigten Bild sind die beiden Kontrollkästchen (CheckBox) "Group by category" und "Sort by category and date" aktiviert. Möglicherweise haben Sie bereits bemerkt, dass die Daten nach der Kategorie des Produkts gruppiert sind und der Name der Kategorie in alphabetischer Reihenfolge aufgeführt ist. Im Bild ist nicht so gut zu erkennen, dass auch die Elemente innerhalb der einzelnen Kategorien nach Startdatum sortiert sind. Dazu wird eine Auflistungsansicht verwendet. Im Abschnitt Binden an Auflistungen werden Auflistungsansichten erläutert.

  • Wenn der Benutzer ein Element auswählt, werden in ContentControl die Einzelheiten zum ausgewählten Element angezeigt. Dies wird als Master/Detail-Szenario bezeichnet. Im Abschnitt Master/Detail-Szenario erhalten Sie Informationen über diesen Bindungsszenariotyp.

  • Der Typ der StartDate-Eigenschaft ist DateTime, wodurch ein Datum mit der Zeit bis auf die Millisekunde genau zurückgegeben wird. In dieser Anwendung wurde ein benutzerdefinierter Konverter verwendet, damit eine kürzere Datumszeichenfolge angezeigt wird. Weitere Informationen zu Konvertern finden Sie im Abschnitt Datenkonvertierung.

Wenn der Benutzer auf die Schaltfläche Add Product klickt, wird das folgende Formular aufgerufen:

Seite Produktliste hinzufügen

Der Benutzer kann die Felder im Formular bearbeiten, die Produktauflistung in der Kurzvorschau und in den detaillierteren Vorschaufenstern anzeigen und dann auf submit klicken, um die neue Produktauflistung hinzuzufügen. Alle vorhandenen Gruppierungs-, Filter- und Sortierfunktionen werden auf den neuen Eintrag angewendet. In diesem speziellen Fall wird das im obigen Bild eingegebene Element in der Kategorie Computer an zweiter Stelle angezeigt.

Was in diesem Bild nicht angezeigt wird, ist die in der TextBox Start Date bereitgestellte Validierungslogik. Wenn der Benutzer ein ungültiges Datum eingibt (mit ungültiger Formatierung oder einem vergangenen Datum), wird er durch einen ToolTip und ein rotes Ausrufezeichen neben TextBox benachrichtigt. Im Abschnitt Datenvalidierung wird näher auf das Erstellen von Validierungslogik eingegangen.

Bevor die oben genannten unterschiedlichen Datenbindungsfunktionen näher erläutert werden, werden im nächsten Abschnitt die grundlegenden Konzepte erläutert, die für ein Verständnis der WPF-Datenbindung unerlässlich sind.

Grundlegende Konzepte zur Datenbindung

Dieser Abschnitt enthält folgende Unterabschnitte.

  • Richtung des Datenflusses
  • Wodurch werden Quellaktualisierungen ausgelöst?

Unabhängig davon, was für ein Element Sie binden und welcher Art die Datenquelle ist, geschieht die Bindung immer nach dem in der folgenden Abbildung gezeigten Modell:

Grundlegendes Datenbindungsdiagramm

Wie in der Abbildung dargestellt, ist Datenbindung die Brücke zwischen dem Bindungsziel und der Bindungsquelle. Die Abbildung veranschaulicht die folgenden grundlegenden Konzepte der WPF-Datenbindung:

  • Eine Bindung besteht in der Regel aus diesen vier Komponenten: einem Bindungszielobjekt, einer Zieleigenschaft, einer Bindungsquelle sowie einem Pfad zum Wert in der Bindungsquelle, die verwendet werden soll. Angenommen, Sie möchten den Inhalt von TextBox an die Name-Eigenschaft eines Employee-Objekts binden, dann ist Ihr Zielobjekt TextBox, die Zieleigenschaft die Text-Eigenschaft, der zu verwendende Wert Name und das Quellobjekt das Employee-Objekt.

  • Die Zieleigenschaft muss eine Abhängigkeitseigenschaft sein. Die meisten UIElement-Eigenschaften sind Abhängigkeitseigenschaften, und die meisten Abhängigkeitseigenschaften, mit Ausnahme der schreibgeschützten, unterstützen die Datenbindung standardmäßig. (Lediglich DependencyObject-Typen können Abhängigkeitseigenschaften definieren, und jedes UIElement wird von DependencyObject abgeleitet.)

  • Obwohl nicht in der Abbildung angegeben, sollte beachtet werden, dass das Bindungsquellen-Objekt nicht darauf beschränkt ist, als benutzerdefiniertes CLR-Objekt zu fungieren. Die WPF-Datenbindung unterstützt Daten in Form von CLR-Objekten und XML. So könnte es sich bei Ihrer Bindungsquelle um UIElement handeln, ein beliebiges Listenobjekt, ein CLR-Objekt, das ADO.NET-Daten oder -Webdiensten zugeordnet ist, oder um einen XmlNode, der Ihre XML-Daten enthält. Weitere Informationen finden Sie unter Übersicht über Bindungsquellen.

Beim Lesen anderer software development kit (SDK)-Themen sollten Sie daran denken, dass Sie beim Einrichten einer Bindung ein Bindungsziel an eine Bindungsquelle binden. Wenn Sie z. B. zugrunde liegende XML-Daten mithilfe der Datenbindung in ListBox anzeigen, binden Sie ListBox an die XML-Daten.

Zum Einrichten einer Bindung verwenden Sie das Binding-Objekt. Im weiteren Verlauf dieses Themas werden zahlreiche Konzepte zum Binding-Objekt, einige Eigenschaften und dessen Verwendung erläutert.

Richtung des Datenflusses

Wie bereits erwähnt und durch den Pfeil in der Abbildung oben gezeigt, kann der Datenfluss einer Bindung vom Bindungsziel zur Bindungsquelle verlaufen (z. B. ändert sich der Quellwert, wenn ein Benutzer den Wert von TextBox bearbeitet) und/oder von der Bindungsquelle zum Bindungsziel (z. B. wenn der TextBox-Inhalt durch Änderungen in der Bindungsquelle aktualisiert wird), wenn die Bindungsquelle die entsprechenden Benachrichtigungen bereitstellt.

Sie können die Anwendung so einrichten, dass Benutzer die Daten ändern und zurück an das Quellobjekt übertragen können. Sie können auch das Aktualisieren von Quelldaten durch Benutzer unterbinden. Legen Sie dazu die Mode-Eigenschaft des Binding-Objekts fest. In der folgenden Abbildung werden die unterschiedlichen Datenflusstypen veranschaulicht:

Datenbindungsdatenfluss

  • OneWay-Bindung bewirkt, dass bei Änderungen an der Quelleigenschaft die Zieleigenschaft automatisch aktualisiert wird, ohne dass Änderungen an der Zieleigenschaft an die Quelleigenschaft zurückübertragen werden. Dieser Typ von Bindung empfiehlt sich, wenn das gebundene Steuerelement implizit als schreibgeschützt festgelegt wurde. Sie können beispielsweise eine Bindung an eine Quelle wie einen Börsenticker erstellen, oder möglicherweise ist für die Zieleigenschaft keine Steuerungsoberfläche verfügbar, um Änderungen vorzunehmen, z. B. an der datengebundenen Hintergrundfarbe einer Tabelle. Wenn die Änderungen der Zieleigenschaft nicht überwacht werden müssen, vermeiden Sie mit dem OneWay-Bindungsmodus den zusätzlichen Aufwand durch den TwoWay-Bindungsmodus.

  • TwoWay-Bindung bewirkt, dass bei Änderungen an der Quelleigenschaft bzw. der Zieleigenschaft die jeweils andere automatisch aktualisiert wird. Dieser Typ von Bindung ist für bearbeitbare Formulare und sonstige vollständig interaktive UInszenarien geeignet. Die meisten Eigenschaften sind standardmäßig auf OneWay-Bindung festgelegt, aber einige Abhängigkeitseigenschaften (meistens Eigenschaften von Steuerelementen, die vom Benutzer bearbeitet werden können, z. B. die Text-Eigenschaft von TextBox und die IsChecked-Eigenschaft von CheckBox) sind standardmäßig auf TwoWay-Bindung festgelegt. Eine programmgesteuerte Möglichkeit zu bestimmen, ob eine Abhängigkeitseigenschaft über eine unidirektionale oder bidirektionale Bindung verfügt, besteht darin, die Eigenschaftenmetadaten der Eigenschaft mithilfe von GetMetadata abzurufen und anschließend den booleschen Wert der BindsTwoWayByDefault-Eigenschaft zu überprüfen.

  • OneWayToSource stellt die Umkehrung der OneWay-Bindung dar: die Quelleigenschaft wird aktualisiert, wenn sich die Zieleigenschaft ändert. Ein Beispielszenario besteht darin, einfach den Quellwert von der UI neu zu bewerten.

  • In der Abbildung nicht gezeigt wird die OneTime-Bindung, die bewirkt, dass die Quelleigenschaft die Zieleigenschaft initialisiert, ohne dass nachfolgende Änderungen weitergegeben werden. Wenn sich also der Datenkontext oder das Objekt im Datenkontext ändert, wird die Änderung in der Zieleigenschaft nicht angezeigt. Dieser Typ von Bindung empfiehlt sich, wenn Sie Daten verwenden, bei denen eine Momentaufnahme des aktuellen Zustands verwendet werden kann oder die Daten tatsächlich statisch sind. Dieser Bindungstyp ist auch hilfreich, wenn die Zieleigenschaft mit einem bestimmten Wert der Quelleigenschaft initialisiert werden soll und der Datenkontext vorab nicht bekannt ist. Dies ist eine wesentlich einfachere Form der OneWay-Bindung, die eine bessere Leistung in Situationen bietet, in denen der Quellwert unverändert bleibt.

Beachten Sie, dass zum Erkennen von Quelländerungen (das gilt für die OneWay-Bindung und die TwoWay-Bindung) die Quelle einen geeigneten Mechanismus für Benachrichtigungen bei Eigenschaftenänderungen implementieren muss, z. B. INotifyPropertyChanged. Unter Gewusst wie: Implementieren von Benachrichtigungen bei Eigenschaftenänderungen finden Sie ein Beispiel für eine INotifyPropertyChanged-Implementierung.

Die Mode-Eigenschaftenseite bietet weitere Informationen zu Bindungsmodi und ein Beispiel zum Angeben der Bindungsrichtung.

Wodurch werden Quellaktualisierungen ausgelöst?

Bindungen vom Typ TwoWay oder OneWayToSource überwachen die Zieleigenschaft auf Änderungen und übertragen sie zurück an die Quelle. Dies wird als Aktualisieren der Quelle bezeichnet. Sie könnten beispielsweise den Text eines TextBox-Elements bearbeiten, um den zugrunde liegenden Quellwert zu ändern. Wie im letzten Abschnitt beschrieben, wird die Richtung des Datenflusses durch den Wert der Mode-Eigenschaft der Bindung bestimmt.

Wird der Quellwert aktualisiert, während Sie den Text bearbeiten oder nachdem Sie die Bearbeitung abgeschlossen haben und den Mauszeiger aus dem TextBox-Steuerelement bewegen? Durch die UpdateSourceTrigger-Eigenschaft der Bindung ist festgelegt, wodurch die Aktualisierung der Quelle ausgelöst wird. Die Punkte der nach rechts weisen den Pfeile in der folgenden Abbildung veranschaulichen die Rolle der UpdateSourceTrigger-Eigenschaft:

UpdateSourceTrigger-Diagramm

Ist der UpdateSourceTrigger-Wert auf PropertyChanged festgelegt, wird der Wert, auf den mit dem nach rechts weisenden Pfeil der TwoWay-Bindung oder der OneWayToSource-Bindungen gezeigt wird, aktualisiert, sobald sich die Zieleigenschaft ändert. Ist der UpdateSourceTrigger-Wert hingegen auf LostFocus festgelegt, wird dieser Wert nur dann durch den neuen Wert aktualisiert, wenn die Zieleigenschaft den Fokus verliert.

Wie bei der Mode-Eigenschaft haben verschiedene Abhängigkeitseigenschaften unterschiedliche UpdateSourceTrigger-Standardwerte. Der Standardwert für die meisten Abhängigkeitseigenschaften lautet PropertyChanged, während die Text-Eigenschaft den Standardwert LostFocus aufweist. Dies bedeutet, dass Quellaktualisierungen automatisch bei jeder Änderung der Zieleigenschaft erfolgen, was bei CheckBox und anderen einfachen Steuerelementen unproblematisch ist. Bei Textfeldern kann eine Aktualisierung nach jeder Tastatureingabe die Leistung mindern und führt außerdem dazu, dass der Benutzer nicht wie gewohnt durch Drücken der Rücktaste Tippfehler beheben kann, bevor der neue Wert übergeben wird. Aus diesem Grund hat die Text-Eigenschaft den Standardwert LostFocus und nicht PropertyChanged.

Auf der UpdateSourceTrigger-Eigenschaftenseite erhalten Sie weitere Informationen zur Suche nach dem UpdateSourceTrigger-Standardwert einer Abhängigkeitseigenschaft.

In der folgenden Tabelle sehen Sie ein Beispielszenario für jeden UpdateSourceTrigger-Wert, wobei TextBox als Beispiel verwendet wird:

UpdateSourceTrigger-Wert

Wenn der Quellwert aktualisiert wird

Beispielszenario für TextBox

LostFocus (Standard für TextBox.Text)

Wenn das TextBox-Steuerelement den Fokus verliert

Eine TextBox, der Validierungslogik zugeordnet ist (siehe Abschnitt zur Datenvalidierung).

PropertyChanged

Während der Eingabe in TextBox

TextBox-Steuerelemente in einem Chatroom-Fenster

Explicit

Wenn die Anwendung UpdateSource aufruft

TextBox-Steuerelemente in einem bearbeitbaren Formular (aktualisiert die Quellwerte nur dann, wenn der Benutzer auf die Schaltfläche zum Senden klickt)

Ein Beispiel finden Sie unter Gewusst wie: Steuern, wann der TextBox-Text die Quelle aktualisiert.

Erstellen einer Bindung

Dieser Abschnitt enthält folgende Unterabschnitte.

  • Angeben der Bindungsquelle
  • Angeben des Pfads zum Wert
  • Bindung und BindingExpression

Um nochmals auf die in den vorigen Abschnitten vorgestellten Konzepte einzugehen, richten Sie mithilfe des Binding-Objekts eine Bindung ein. Bindungen bestehen in der Regel aus vier Komponenten: Bindungsziel, Zieleigenschaft, Bindungsquelle sowie ein Pfad zum Quellwert, der verwendet werden soll. In diesem Abschnitt wird das Einrichten einer Bindung erläutert.

Im folgenden Beispiel ist das Bindungsquellobjekt eine Klasse namens MyData, die im SDKSample-Namespace definiert ist. Zu Demonstrationszwecken hat die MyData-Klasse eine Zeichenfolgeneigenschaft namens ColorName, deren Wert auf "Red" festgelegt ist. In diesem Beispiel wird also eine Schaltfläche mit einem roten Hintergrund erstellt.

<DockPanel
  xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
  xmlns:c="clr-namespace:SDKSample">
  <DockPanel.Resources>
    <c:MyData x:Key="myDataSource"/>
  </DockPanel.Resources>
  <DockPanel.DataContext>
    <Binding Source="{StaticResource myDataSource}"/>
  </DockPanel.DataContext>
  <Button Background="{Binding Path=ColorName}"
          Width="150" Height="30">I am bound to be RED!</Button>
</DockPanel>

Weitere Informationen zur Syntax der Bindungsdeklaration und Beispiele zum Einrichten einer Bindung im Code finden Sie unter Übersicht über Bindungsdeklarationen.

Wenn dieses Beispiel auf das einfache Diagramm angewendet wird, wird eine Abbildung wie die folgende angezeigt. Dies ist eine OneWay-Bindung, da die Background-Eigenschaft standardmäßig OneWay-Bindungen unterstützt.

Datenbindungsdiagramm

Sie fragen sich vielleicht, warum das funktioniert, obwohl die ColorName-Eigenschaft ein Zeichenfolgentyp ist, während die Background-Eigenschaft vom Typ Brush ist. Der Grund dafür ist die Standardtypkonvertierung, die im Abschnitt Datenkonvertierung näher erläutert wird.

Angeben der Bindungsquelle

Beachten Sie, dass im vorigen Beispiel die Bindungsquelle durch Festlegen der DataContext-Eigenschaft für das DockPanel-Element angegeben wurde. Button erbt dann den DataContext-Wert von DockPanel, wobei es sich um das übergeordnete Element handelt. Das Bindungsquellobjekt stellt, wie bereits erwähnt, eine der vier erforderlichen Komponenten einer Bindung dar. Wäre das Bindungsquellobjekt nicht angegeben, hätte die Bindung keine Auswirkungen.

Zum Angeben des Bindungsquellobjekts gibt es mehrere Möglichkeiten. Die Verwendung der DataContext-Eigenschaft für ein übergeordnetes Element ist hilfreich, wenn Sie mehrere Eigenschaften an dieselbe Quelle binden. Es kann aber auch zweckmäßiger sein, die Bindungsquelle in einzelnen Bindungsdeklarationen anzugeben. Im vorigen Beispiel können Sie auch, anstatt die DataContext-Eigenschaft zu verwenden, die Bindungsquelle angeben, indem Sie die Source-Eigenschaft direkt in der Bindungsdeklaration der Schaltfläche festlegen, wie im folgenden Beispiel gezeigt:

<DockPanel.Resources>
  <c:MyData x:Key="myDataSource"/>
</DockPanel.Resources>
<Button Width="150" Height="30"
        Background="{Binding Source={StaticResource myDataSource},
                             Path=ColorName}">I am bound to be RED!</Button>

Statt die DataContext-Eigenschaft direkt für ein Element festzulegen, den DataContext-Wert von einem übergeordneten Element zu erben (z. B. die Schaltfläche im ersten Beispiel) und die Bindungsquelle explizit durch Festlegen der Source-Eigenschaft für Binding anzugeben (z. B. die Schaltfläche im letzten Beispiel), können Sie zum Angeben der Bindungsquelle auch die ElementName-Eigenschaft oder die RelativeSource-Eigenschaft verwenden. Die ElementName-Eigenschaft ist hilfreich zum Binden an andere Elemente in der Anwendung, beispielsweise wenn Sie die Breite einer Schaltfläche mit einem Schieberegler anpassen. Die RelativeSource-Eigenschaft bietet sich an, wenn die Bindung in ControlTemplate oder Style angegeben ist. Weitere Informationen finden Sie unter Gewusst wie: Angeben der Bindungsquelle.

Angeben des Pfads zum Wert

Wenn die Bindungsquelle ein Objekt ist, verwenden Sie die Path-Eigenschaft, um den für die Bindung zu verwendenden Wert anzugeben. Wenn Sie eine Bindung an XML-Daten vornehmen, geben Sie den Wert mithilfe der XPath-Eigenschaft an. In einigen Fällen können Sie die Path-Eigenschaft auch dann verwenden, wenn es sich bei den Daten um XML handelt. Angenommen, Sie möchten auf die Name-Eigenschaft eines zurückgegebenen XmlNode zugreifen (als Ergebnis einer XPath-Abfrage), dann sollten Sie die Path-Eigenschaft zusätzlich zur XPath-Eigenschaft verwenden.

Informationen zur Syntax und Beispiele finden Sie auf den Eigenschaftenseiten zu Path und XPath.

Obwohl darauf hingewiesen wurde, dass der Path zum Wert, der verwendet werden soll, einer der vier erforderlichen Komponenten einer Bindung entspricht, so ist der in den Szenarien zum Binden an ein vollständiges Objekt zu verwendende Wert identisch mit dem Bindungsquellobjekt. In einem solchen Fall muss kein Path angegeben werden. Betrachten Sie das folgende Beispiel:

<ListBox ItemsSource="{Binding}"
         IsSynchronizedWithCurrentItem="true"/>

Im obigen Beispiel wird die leere Bindungssyntax verwendet: {Binding}. In diesem Fall erbt ListBox den DataContext von einem übergeordneten DockPanel-Element (wird in diesem Beispiel nicht gezeigt). Wenn der Pfad nicht angegeben wurde, erfolgt die Bindung standardmäßig an das gesamte Objekt. Mit anderen Worten, der Pfad wird in diesem Beispiel ausgelassen, da die ItemsSource-Eigenschaft an das gesamte Objekt gebunden wird. (Im Abschnitt Binden an Auflistungen wird ausführlich darauf eingegangen.)

Dieses Szenario ist nicht nur zum Binden an eine Auflistung hilfreich, sondern auch zum Binden an ein vollständiges Objekt statt nur an eine einzelne Eigenschaft eines Objekts. Wenn es sich beim Quellobjekt beispielsweise um einen Zeichenfolgentyp handelt und Sie lediglich an die Zeichenfolge binden möchten. Ein anderes allgemeines Szenario besteht darin, ein Element an ein Objekt mit mehreren Eigenschaften zu binden.

Beachten Sie, dass Sie ggf. benutzerdefinierte Logik anwenden müssen, damit die Daten für die gebundene Zieleigenschaft sinnvoll sind. Bei der benutzerdefinierten Logik kann es sich z. B. um einen benutzerdefinierten Konverter handeln (falls keine Standardtypkonvertierung vorhanden ist). Weitere Informationen über Konverter finden Sie unter Datenkonvertierung.

Bindung und BindingExpression

Bevor auf andere Features und Verwendungsmöglichkeiten der Datenbindung eingegangen wird, ist es sinnvoll, die BindingExpression-Klasse einzuführen. Wie die vorherigen Abschnitte gezeigt haben, handelt es sich bei der Binding-Klasse um die Klasse höherer Ebene für die Deklaration einer Bindung. Die Binding-Klasse bietet zahlreiche Eigenschaften, mit denen Sie die Merkmale einer Bindung angeben können. Die verwandte BindingExpression-Klasse ist das zugrunde liegende Objekt, das die Verbindung zwischen Quelle und Ziel aufrechterhält. Ein Bindung enthält sämtliche Informationen, die von mehreren Bindungsausdrücken gemeinsam genutzt werden können. BindingExpression ist ein Instanzenausdruck, der nicht gemeinsam genutzt werden kann und sämtliche Instanzeninformationen von Binding enthält.

Beachten Sie folgendes Beispiel: myDataObject ist eine Instanz der MyData-Klasse, myBinding das Quellobjekt von Binding, und die MyData-Klasse ist eine definierte Klasse, die die Zeichenfolgeneigenschaft MyDataProperty enthält. In diesem Beispiel wird der Textinhalt von mytext, einer Instanz von TextBlock, an MyDataProperty gebunden.

Dim data1 As New MyData(DateTime.Now)
Dim binding1 As New Binding("MyDataProperty")
binding1.Source = data1
Me.myText.SetBinding(TextBlock.TextProperty, binding1)
//make a new source
  MyData myDataObject = new MyData(DateTime.Now);      
  Binding myBinding = new Binding("MyDataProperty");
  myBinding.Source = myDataObject;
  myText.SetBinding(TextBlock.TextProperty, myBinding);

Mit demselben myBinding-Objekt können Sie auch andere Bindungen erstellen. Sie können beispielsweise mit dem myBinding-Objekt eine Bindung des Textinhalts eines Kontrollkästchens an MyDataProperty herstellen. In diesem Szenario werden zwei Instanzen von BindingExpression verwendet, die das myBinding-Objekt gemeinsam nutzen.

Ein BindingExpression-Objekt kann durch den Wert abgerufen werden, der nach dem Aufrufen von GetBindingExpression für ein datengebundenes Objekt zurückgegeben wird. In den folgenden Themen werden einige Verwendungsmöglichkeiten der BindingExpression-Klasse veranschaulicht:

Datenkonvertierung

Im vorherigen Beispiel ist die Schaltfläche rot, da die Background-Eigenschaft an eine Zeichenfolgeneigenschaft mit dem Wert "Red" gebunden ist. Dies funktioniert, weil ein Typkonverter für den Brush-Typ vorhanden ist, mit dem der Zeichenfolgenwert in Brush konvertiert wird.

Wenn diese Informationen der Abbildung im Abschnitt Erstellen einer Bindung hinzugefügt werden, sieht das Diagramm folgendermaßen aus:

Datenbindungsdiagramm

Was geschieht jedoch, wenn keine Eigenschaft mit einem Zeichenfolgentyp vorhanden ist, sondern das Bindungsquellobjekt eine Color-Eigenschaft mit dem Typ Color aufweist? Damit in diesem Fall die Bindung funktioniert, müssten Sie den Color-Eigenschaftswert zuerst in einen Wert ändern, der von der Background-Eigenschaft akzeptiert wird. Dazu müssten Sie einen benutzerdefinierten Konverter erstellen, indem Sie die IValueConverter-Schnittstelle implementieren, wie im folgenden Beispiel gezeigt:

    <ValueConversion(GetType(Color), GetType(SolidColorBrush))>
    Public Class ColorBrushConverter
        Implements IValueConverter
        Public Function Convert(ByVal value As Object, ByVal targetType As Type, ByVal parameter As Object, ByVal culture As System.Globalization.CultureInfo) As Object Implements IValueConverter.Convert
            Dim color As Color = CType(value, Color)
            Return New SolidColorBrush(color)
        End Function

        Public Function ConvertBack(ByVal value As Object, ByVal targetType As Type, ByVal parameter As Object, ByVal culture As System.Globalization.CultureInfo) As Object Implements IValueConverter.ConvertBack
            Return Nothing
        End Function
    End Class
[ValueConversion(typeof(Color), typeof(SolidColorBrush))]
public class ColorBrushConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        Color color = (Color)value;
        return new SolidColorBrush(color);
    }

    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        return null;
    }
}

Auf der IValueConverter-Referenzseite erhalten Sie weitere Informationen.

Jetzt wird anstelle der Standardkonvertierung der benutzerdefinierte Konverter verwendet, und unser Diagramm sieht wie folgt aus:

Datenbindungsdiagramm

Wie bereits ausgeführt, können aufgrund von Typkonvertern, die im Typ vorhanden sind, an den gebunden wird, Standardkonvertierungen verfügbar sein. Dieses Verhalten hängt von den im Ziel vorhandenen Typkonvertern ab. Erstellen Sie im Zweifelsfall einen eigenen Konverter.

Es folgen einige typische Szenarien, in denen die Implementierung eines Datenkonverters sinnvoll ist:

  • Die Daten sollen, abhängig von der Kultur, unterschiedlich angezeigt werden. Sie können z. B. einen Währungskonverter oder einen Datum-/Uhrzeitkonverter implementieren, der auf den Werten oder Standards basiert, die in einer bestimmten Kultur verwendet werden.

  • Die verwendeten Daten müssen nicht unbedingt dazu dienen, den Textwert einer Eigenschaft zu ändern. Sie können auch den Zweck haben, andere Werte zu ändern, beispielsweise die Quelle für ein Bild oder die Farbe bzw. das Format des Anzeigetexts. Konverter können in dieser Instanz verwendet werden, indem die Bindung einer Eigenschaft konvertiert wird, die ungeeignet zu sein scheint, z. B. wenn ein Textfeld an die Background-Eigenschaft einer Tabellenzelle gebunden wird.

  • Mehrere Steuerelemente oder mehrere Steuerelementeigenschaften sind an dieselben Daten gebunden. In diesem Fall wird durch die primäre Bindung möglicherweise nur der Text angezeigt, wohingegen andere Bindungen bestimmte Anzeigeprobleme behandeln, aber dieselben Bindungen als Quellinformationen verwenden.

  • Bisher wurde noch nicht auf MultiBinding eingegangen, wobei eine Zieleigenschaft über einer Auflistung von Bindungen verfügt. Bei MultiBinding verwenden Sie einen benutzerdefinierten IMultiValueConverter, um einen endgültigen Wert aus den Werten der Bindungen zu generieren. So kann die Farbe beispielsweise aus roten, blauen und grünen Werten berechnet werden, die aus denselben oder anderen Bindungsquellobjekten stammen können. Auf der Seite zur MultiBinding-Klasse finden Sie Beispiele und weitere Informationen.

Binden an Auflistungen

Dieser Abschnitt enthält folgende Unterabschnitte.

  • Implementieren von Auflistungen
  • Auflistungsansichten

Ein Bindungsquellobjekt kann als einzelnes Objekt behandelt werden, dessen Eigenschaften Daten enthalten, oder als eine Datenauflistung von polymorphen Objekten, die häufig zusammen gruppiert werden (beispielsweise als Ergebnis einer Datenbankabfrage). Bisher wurde nur das Binden an einzelne Objekte behandelt, aber auch das Binden an eine Datenauflistung ist ein gängiges Szenario. So besteht ein häufiges Szenario darin, ein ItemsControl wie ListBox, ListView oder TreeView zu verwenden, um eine Datenauflistung anzuzeigen. Ein Beispiel finden Sie in der Anwendung, die im Abschnitt Was ist Datenbindung? verwendet wird.

Das bisherige einfache Diagramm ist praktischerweise immer noch gültig. Wenn Sie ItemsControl an eine Auflistung binden, sieht das Diagramm wie folgt aus:

Datenbindungs-ItemsControl-Diagramm

Wie in diesem Diagramm gezeigt, muss zum Binden von ItemsControl an ein Auflistungsobjekt die ItemsSource-Eigenschaft verwendet werden. Sie können sich die ItemsSource-Eigenschaft als Inhalt von ItemsControl vorstellen. Beachten Sie, dass die Bindung OneWay entspricht, da die ItemsSource-Eigenschaft standardmäßig OneWay-Bindungen unterstützt.

Implementieren von Auflistungen

Sie können jede Auflistung auflisten, die die IEnumerable-Schnittstelle implementiert. Um dynamische Bindungen einzurichten, bei denen die UI automatisch nach Einfügungen oder Löschungen in der Auflistung aktualisiert wird, muss die Auflistung die INotifyCollectionChanged-Schnittstelle implementieren. Diese Schnittstelle macht ein Ereignis verfügbar, das bei jeder Änderung der zugrunde liegenden Auflistung ausgelöst werden sollte.

WPF stellt die ObservableCollection<T>-Klasse bereit, bei der es sich um die integrierte Implementierung einer Datenauflistung handelt, die die INotifyCollectionChanged-Schnittstelle verfügbar macht. Um eine vollständige Unterstützung für die Übertragung von Datenwerten von Quellobjekten zu Zielen zu gewährleisten, muss jedes Objekt in der Auflistung, die bindbare Eigenschaften unterstützt, auch die INotifyPropertyChanged-Schnittstelle implementieren. Weitere Informationen finden Sie unter Übersicht über Bindungsquellen.

Statt eigene Auflistungen zu implementieren, können Sie auch ObservableCollection<T> bzw. eine vorhandene Auflistungsklasse wie List<T>, Collection<T> oder BindingList<T> verwenden. Falls Sie für ein erweitertes Szenario Ihre eigene Auflistung implementieren möchten, können Sie IList und somit eine nicht generische Auflistung von Objekten verwenden, auf die einzeln über den Index zugegriffen werden kann. So lässt sich eine optimale Leistung erzielen.

Auflistungsansichten

Nachdem ItemsControl an eine Datenauflistung gebunden wurde, können Sie die Daten sortieren, filtern oder gruppieren. Hierzu verwenden Sie Auflistungsansichten, bei denen es sich um Klassen handelt, die die ICollectionView-Schnittstelle implementieren.

Dieser Abschnitt enthält folgende Unterabschnitte.

  • Was sind Auflistungsansichten?
  • So erstellen Sie eine Ansicht
  • Sortieren
  • Filtern
  • Gruppierung
  • Zeiger auf aktuelle Elemente
  • Szenario für Master-Detail-Bindung

Was sind Auflistungsansichten?

Eine Auflistungsansicht fungiert als Ebene über der Bindungsquellauflistung, in der Sie mit Sortier-, Filter- und Gruppierungsabfragen navigieren und die jeweilige Quellauflistung anzeigen können, ohne die zugrunde liegende Quellauflistung selbst ändern zu müssen. Eine Auflistungsansicht stellt außerdem einen Zeiger auf das aktuelle Element in der Auflistung zur Verfügung. Wenn die Quellauflistung die INotifyCollectionChanged-Schnittstelle implementiert, werden die vom CollectionChanged-Ereignis ausgelösten Änderungen an die Ansichten weitergegeben.

Da in Ansichten die zugrunde liegenden Quellauflistungen nicht geändert werden, können einer Quellauflistung mehrere Ansichten zugeordnet sein. Angenommen, Sie verfügen über eine Auflistung von Task-Objekten. Mithilfe von Ansichten können Sie dieselben Daten auf verschiedene Weise anzeigen. Beispielsweise können Sie links auf der Seite Aufgaben nach Priorität und rechts nach Bereich sortieren.

So erstellen Sie eine Ansicht

Eine Möglichkeit, eine Ansicht zu erstellen und zu verwenden, besteht darin, das Ansichtsobjekt direkt zu instanziieren und dann als Bindungsquelle zu verwenden. Betrachten Sie z. B. die Anwendung Demo für die Datenbindung, die im Abschnitt Was ist Datenbindung? dargestellt wird. Die Anwendung wurde so implementiert, dass ListBox an eine Ansicht über die Datenauflistung gebunden wird, anstatt direkt an die Datenauflistung. Das folgende Beispiel wird aus der Demo für die Datenbindung-Anwendung extrahiert. Die CollectionViewSource-Klasse ist eine Extensible Application Markup Language (XAML)-Proxyklasse, die von CollectionView erbt. In diesem Beispiel ist die Source der Ansicht an die AuctionItems-Auflistung (vom Typ ObservableCollection<T>) des aktuellen Anwendungsobjekts gebunden.

<Window.Resources>


...


<CollectionViewSource 
      Source="{Binding Source={x:Static Application.Current}, Path=AuctionItems}"   
      x:Key="listingDataView" />


...


</Window.Resources>

Die Ressource listingDataView dient dann als Bindungsquelle für Elemente in der Anwendung, z. B. ListBox:

<ListBox Name="Master" Grid.Row="2" Grid.ColumnSpan="3" Margin="8"
    ItemsSource="{Binding Source={StaticResource listingDataView}}">


...


</ListBox>

Wenn Sie eine weitere Ansicht für dieselbe Auflistung erstellen möchten, können Sie eine weitere CollectionViewSource-Instanz erstellen und ihr einen anderen x:Key-Namen geben.

Die folgende Tabelle zeigt, welche Ansichtsdatentypen als Standardauflistungsansicht bzw. mit CollectionViewSource basierend auf dem Quellauflistungstyp erstellt werden.

Quellauflistungstyp

Auflistungsansichtstyp

Hinweise

IEnumerable

Ein interner Typ auf Basis von CollectionView

Kann Elemente nicht gruppieren.

IList

ListCollectionView

Am schnellsten.

IBindingList

BindingListCollectionView

Verwenden einer Standardansicht

Die Angabe einer Auflistungsansicht als Bindungsquelle ist eine Möglichkeit, eine Auflistungsansicht zu erstellen und zu verwenden. WPF erstellt außerdem eine Standardauflistungsansicht für jede als Bindungsquelle verwendete Auflistung. Bei der direkten Bindung an eine Auflistung bindet WPF an die Standardansicht der Auflistung. Diese Standardansicht wird von allen Bindungen an diese Auflistung gemeinsam verwendet, sodass eine Änderung an der Standardansicht durch ein gebundenes Steuerelement oder Code, z. B. Sortierung oder eine Änderung des aktuellen Elementzeigers (dies wird zu einem späteren Zeitpunkt erläutert), sich auf alle anderen Bindungen an dieselbe Auflistung auswirkt.

Zum Abrufen der Standardansicht verwenden Sie die GetDefaultView-Methode. Ein Beispiel finden Sie unter Gewusst wie: Abrufen der Standardansicht einer Datenauflistung.

Auflistungsansichten mit ADO.NET-Datentabellen

Zur Leistungsoptimierung wird bei Auflistungsansichten für ADO.NET DataTable-Objekte oder DataView-Objekte die Sortierung und Filterung an die DataView delegiert. So wird die Sortierung und Filterung von allen Auflistungsansichten der Datenquelle gemeinsam verwendet. Um für die einzelnen Auflistungsansichten eine eigene Sortierung und Filterung zu ermöglichen, müssen Sie jede Auflistungsansicht mit einem eigenen DataView-Objekt initialisieren.

Sortieren

Wie bereits erwähnt, können mit Ansichten Sortierreihenfolgen auf Auflistungen angewendet werden. Da sie in der zugrunde liegenden Auflistung vorhanden sind, haben die Daten möglicherweise eine relevante inhärente Reihenfolge oder nicht. Die Ansicht der Auflistung ermöglicht Ihnen das Festlegen einer Reihenfolge oder das Ändern der Standardreihenfolge, je nachdem, welche Vergleichskriterien Sie angeben. Da es sich um eine clientbasierte Ansicht der Daten handelt, besteht ein übliches Szenario darin, dass der Benutzer Spalten mit Tabellendaten nach dem Wert sortiert, dem die Spalte entspricht. Mithilfe von Ansichten kann diese benutzerdefinierte Sortierung angewendet werden, ohne die zugrunde liegende Auflistung ändern oder den Auflistungsinhalt erneut abfragen zu müssen. Ein Beispiel finden Sie unter Gewusst wie: Sortieren einer GridView-Spalte beim Klicken auf einen Header.

Das folgende Beispiel zeigt die Sortierlogik des Kontrollkästchens (CheckBox) "Sort by category and date", das in der UI der Anwendung im Abschnitt Was ist Datenbindung? verwendet wird.

private void AddSorting(object sender, RoutedEventArgs args)
{
    // This sorts the items first by Category and within each Category,
    // by StartDate. Notice that because Category is an enumeration,
    // the order of the items is the same as in the enumeration declaration
    listingDataView.SortDescriptions.Add(
        new SortDescription("Category", ListSortDirection.Ascending));
    listingDataView.SortDescriptions.Add(
        new SortDescription("StartDate", ListSortDirection.Ascending));
}

Filtern

Ansichten können auch einen Filter auf eine Auflistung anwenden. Dies bedeutet, dass in der Auflistung zwar ein Element vorhanden sein kann, in dieser bestimmten Ansicht jedoch nur eine bestimmte Teilmenge der gesamten Auflistung angezeigt werden soll. Sie können die Daten nach einer Bedingung filtern. In der Anwendung im Abschnitt Was ist Datenbindung? enthält das Kontrollkästchen (CheckBox) z. B. eine Logik, mit der Elemente herausgefiltert werden, die mehr als 25 Dollar kosten. Der folgende Code wird ausgeführt, um ShowOnlyBargainsFilter als den Filter-Ereignishandler festzulegen, wenn CheckBox ausgewählt wird:

listingDataView.Filter += new FilterEventHandler(ShowOnlyBargainsFilter);

Der ShowOnlyBargainsFilter-Ereignishandler hat folgende Implementierung:

private void ShowOnlyBargainsFilter(object sender, FilterEventArgs e)
{
    AuctionItem product = e.Item as AuctionItem;
    if (product != null)
    {
        // Filter out products with price 25 or above
        if (product.CurrentPrice < 25)
        {
            e.Accepted = true;
        }
        else
        {
            e.Accepted = false;
        }
    }
}

Wenn Sie direkt eine der CollectionView-Klassen anstelle von CollectionViewSource verwenden, verwenden Sie die Filter-Eigenschaft zum Angeben eines Rückrufs. Ein Beispiel finden Sie unter Gewusst wie: Filtern von Daten in einer Ansicht.

Gruppierung

Mit Ausnahme der internen Klasse zur Ansicht einer IEnumerable-Auflistung unterstützen alle Auflistungsansichten Gruppierungsfunktionen, wodurch der Benutzer die Auflistung in der Auflistungsansicht in logische Gruppen unterteilen kann. Die Gruppen können explizit sein, wobei Benutzer eine Liste von Gruppen angeben. Sie können auch implizit sein, wobei die Gruppen dynamisch in Abhängigkeit von den Daten generiert werden.

Das folgende Beispiel zeigt die Logik des Kontrollkästchens (CheckBox) "Group by category":

// This groups the items in the view by the property "Category"
PropertyGroupDescription groupDescription = new PropertyGroupDescription();
groupDescription.PropertyName = "Category";
listingDataView.GroupDescriptions.Add(groupDescription);

Ein weiteres Beispiel zu Gruppierungen finden Sie unter Gewusst wie: Gruppieren von Elementen in einem ListView, in dem ein GridView implementiert ist.

Zeiger auf aktuelle Elemente

Ansichten unterstützen ebenfalls das Konzept eines aktuellen Elements. Sie können durch die Objekte in einer Auflistungsansicht navigieren. Beim Navigieren verschieben Sie einen Elementzeiger, mit dem Sie das Objekt abrufen können, das sich an einer bestimmten Position in der Auflistung befindet. Ein Beispiel finden Sie unter Gewusst wie: Navigieren durch die Objekte in einer Datenauflistungsansicht.

Da WPF Bindungen an Auflistungen immer über Ansichten herstellt (entweder über eine von Ihnen erstellte Ansicht oder über die Standardansicht der jeweiligen Auflistung), verfügen alle Bindungen an Auflistungen über einen Zeiger auf das aktuelle Element. Bei der Bindung an eine Ansicht gibt der Schrägstrich ("/") im Path-Wert das aktuelle Element der Ansicht an. Der Datenkontext im folgenden Beispiel ist eine Auflistungsansicht. Die erste Zeile wird an die Auflistung gebunden. Die zweite Zeile wird an das aktuelle Element in der Auflistung gebunden. Die dritte Zeile wird an die Description-Eigenschaft des aktuellen Elements in der Auflistung gebunden.

<Button Content="{Binding }" />
<Button Content="{Binding Path=/}" />
<Button Content="{Binding Path=/Description}" /> 

Der Schrägstrich und die Eigenschaftensyntax können gestapelt werden, um eine Hierarchie von Auflistungen zu durchlaufen. Im folgenden Beispiel erfolgt die Bindung an das aktuelle Element einer Auflistung mit dem Namen Offices, wobei es sich um eine Eigenschaft des aktuellen Elements der Quellauflistung handelt.

<Button Content="{Binding /Offices/}" />

Sortier- und Filtervorgänge, die für die Auflistung erfolgen, können Auswirkungen auf den Zeiger auf das aktuelle Element haben. Beim Sortieren verbleibt der Zeiger für das aktuelle Element auf dem zuletzt ausgewählten Element, die Auflistungsansicht wird jedoch nun um den Zeiger herum neu angeordnet. (Möglicherweise befand sich das ausgewählte Element zuvor am Anfang der Liste, jetzt aber in der Mitte.) Beim Filtern wird das ausgewählte Element beibehalten, wenn diese Auswahl nach dem Filtern in der Ansicht verbleibt. Andernfalls wird der Zeiger für das aktuelle Element auf das erste Element der gefilterten Auflistungsansicht festgelegt.

Szenario für Master-Detail-Bindung

Das Konzept eines aktuellen Elements ist nicht nur hilfreich, um durch Elemente in einer Auflistung zu navigieren, sondern auch für das Szenario einer Master-Detail-Bindung. Betrachten Sie wieder die UI der Anwendung, die im Abschnitt Was ist Datenbindung? verwendet wird. In dieser Anwendung wird durch die Auswahl in ListBox der in ContentControl angezeigte Inhalt bestimmt. Anders ausgedrückt, wenn ein ListBox-Element ausgewählt wird, werden in ContentControl die Einzelheiten zum ausgewählten Element angezeigt.

Sie können das Master-Detail-Szenario auch einfach dadurch implementieren, dass mindestens zwei Steuerelemente an dieselbe Ansicht gebunden sind. Im folgenden Beispiel aus der Demo für die Datenbindung ist das Markup des ListBox und des ContentControl dargestellt, das in der UI der Anwendung im Abschnitt Was ist Datenbindung? verwendet wird.

<ListBox Name="Master" Grid.Row="2" Grid.ColumnSpan="3" Margin="8"
    ItemsSource="{Binding Source={StaticResource listingDataView}}">


...


</ListBox>


...


<ContentControl Name="Detail" Grid.Row="3" Grid.ColumnSpan="3" 
        Content="{Binding Source={StaticResource listingDataView}}" 
        ContentTemplate="{StaticResource detailsProductListingTemplate}" 
        Margin="9,0,0,0"/>

Beachten Sie, dass beide Steuerelemente an dieselbe Quelle gebunden sind, und zwar die statische listingDataView-Ressource (siehe die Definition dieser Ressource im Abschnitt So erstellen Sie eine Ansicht). Dies funktioniert deshalb, weil ein Singleton-Objekt (in diesem Fall ContentControl) beim Binden an eine Auflistungsansicht automatisch an CurrentItem in der Ansicht gebunden wird. Beachten Sie, dass CollectionViewSource-Objekte Währung und Auswahl automatisch synchronisieren. Ist Ihr Listensteuerelement, anders als in diesem Beispiel, nicht an ein CollectionViewSource-Objekt gebunden, müssten Sie dessen IsSynchronizedWithCurrentItem-Eigenschaft auf true festlegen.

Weitere Beispiele finden Sie unter Gewusst wie: Binden an eine Auflistung und Anzeigen von Informationen auf Grundlage der Auswahl und Gewusst wie: Verwenden des Master/Detail-Musters mit hierarchischen Daten.

Ihnen ist vielleicht aufgefallen, dass im oben gezeigten Beispiel eine Vorlage verwendet wird. Die Daten würden nicht wunschgemäß angezeigt, wenn keine Vorlagen verwendet würden (eine wird explizit von ContentControl verwendet und die andere implizit von ListBox). Im nächsten Abschnitt geht es um Datenvorlagen.

Datenvorlagen

Ohne Datenvorlagen würde die UI der Anwendung im Abschnitt Was ist Datenbindung? wie folgt aussehen:

Demobeispiel für Datenbindung ohne Datenvorlagen

Wie im Beispiel im vorherigen Abschnitt gezeigt, werden sowohl das ListBox-Steuerelement als auch das ContentControl an das gesamte (oder genauer gesagt, die Ansicht des Auflistungsobjekts) von AuctionItem gebunden. Ohne spezielle Anzeigeanweisungen für die Datensammlung, wird das ListBox als Zeichenfolgendarstellung der Objekte in der zugrunde liegenden Auflistung angezeigt, und das ContentControl wird als Zeichenfolgendarstellung des Objekts angezeigt, an das es gebunden ist.

Um dieses Problem zu beheben, definiert die Anwendung DataTemplaten. Wie im Beispiel im vorherigen Abschnitt gezeigt, verwendet das ContentControl explizit die detailsProductListingTemplate DataTemplate. Das ListBox-Steuerelement verwendet implizit folgende DataTemplate, wenn die AuctionItem-Objekte in der Auflistung angezeigt werden:

<DataTemplate DataType="{x:Type src:AuctionItem}">
    <Border BorderThickness="1" BorderBrush="Gray"
            Padding="7" Name="border" Margin="3" Width="500">
        <Grid>
          <Grid.RowDefinitions>
            <RowDefinition/>
            <RowDefinition/>
            <RowDefinition/>
            <RowDefinition/>
          </Grid.RowDefinitions>
          <Grid.ColumnDefinitions>
            <ColumnDefinition Width="20"/>
            <ColumnDefinition Width="86"/>
            <ColumnDefinition Width="*"/>
          </Grid.ColumnDefinitions>

            <Polygon Grid.Row="0" Grid.Column="0" Grid.RowSpan="4"
                     Fill="Yellow" Stroke="Black" StrokeThickness="1"
                     StrokeLineJoin="Round" Width="20" Height="20"
                     Stretch="Fill"
                     Points="9,2 11,7 17,7 12,10 14,15 9,12 4,15 6,10 1,7 7,7"
                     Visibility="Hidden" Name="star"/>

            <TextBlock Grid.Row="0" Grid.Column="1" Margin="0,0,8,0"
                       Name="descriptionTitle"
                       Style="{StaticResource smallTitleStyle}">Description:</TextBlock>
            <TextBlock Name="DescriptionDTDataType" Grid.Row="0" Grid.Column="2" 
                Text="{Binding Path=Description}" 
                Style="{StaticResource textStyleTextBlock}"/>

            <TextBlock Grid.Row="1" Grid.Column="1" Margin="0,0,8,0"
                       Name="currentPriceTitle"
                       Style="{StaticResource smallTitleStyle}">Current Price:</TextBlock>
            <StackPanel Grid.Row="1" Grid.Column="2" Orientation="Horizontal">
                <TextBlock Text="$" Style="{StaticResource textStyleTextBlock}"/>
                <TextBlock Name="CurrentPriceDTDataType" 
                    Text="{Binding Path=CurrentPrice}" 
                    Style="{StaticResource textStyleTextBlock}"/>
            </StackPanel>
        </Grid>
    </Border>
    <DataTemplate.Triggers>
        <DataTrigger Binding="{Binding Path=SpecialFeatures}">
            <DataTrigger.Value>
                <src:SpecialFeatures>Color</src:SpecialFeatures>
            </DataTrigger.Value>
          <DataTrigger.Setters>
            <Setter Property="BorderBrush" Value="DodgerBlue" TargetName="border" />
            <Setter Property="Foreground" Value="Navy" TargetName="descriptionTitle" />
            <Setter Property="Foreground" Value="Navy" TargetName="currentPriceTitle" />
            <Setter Property="BorderThickness" Value="3" TargetName="border" />
            <Setter Property="Padding" Value="5" TargetName="border" />
          </DataTrigger.Setters>
        </DataTrigger>
        <DataTrigger Binding="{Binding Path=SpecialFeatures}">
            <DataTrigger.Value>
                <src:SpecialFeatures>Highlight</src:SpecialFeatures>
            </DataTrigger.Value>
            <Setter Property="BorderBrush" Value="Orange" TargetName="border" />
            <Setter Property="Foreground" Value="Navy" TargetName="descriptionTitle" />
            <Setter Property="Foreground" Value="Navy" TargetName="currentPriceTitle" />
            <Setter Property="Visibility" Value="Visible" TargetName="star" />
            <Setter Property="BorderThickness" Value="3" TargetName="border" />
            <Setter Property="Padding" Value="5" TargetName="border" />
        </DataTrigger>
    </DataTemplate.Triggers>
</DataTemplate>

Bei Verwendung dieser beiden DataTemplates entspricht die resultierende Benutzeroberfläche der im Abschnitt Was ist Datenbindung? dargestellten Benutzeroberfläche. Wie Sie in dieser Bildschirmabbildung erkennen können, ermöglichen die DataTemplates nicht nur das Einfügen von Daten in die Steuerelemente, sondern auch das Definieren überzeugender Grafiken für Ihre Daten. Beispielsweise werden DataTrigger in der obigen DataTemplate verwendet, sodass AuctionItem-Objekte mit einem SpecialFeatures-Wert von HighLight mit einem orangefarbenen Rahmen und einem Stern angezeigt würden.

Weitere Informationen zu Datenvorlagen finden Sie unter Übersicht über Datenvorlagen.

Datenvalidierung

Dieser Abschnitt enthält folgende Unterabschnitte.

  • Zuordnen von Validierungsregeln zu einer Bindung
  • Bereitstellen von visuellem Feedback
  • Validierungsprozess

Die meisten Anwendungen, bei denen Benutzereingaben erfolgen, benötigen Validierungslogik, um sicherzustellen, dass der Benutzer die erwarteten Informationen eingegeben hat. Die Validierungsüberprüfungen können auf Typ, Bereich, Format oder anderen anwendungsspezifischen Anforderungen basieren. In diesem Abschnitt wird erklärt, wie Datenvalidierung in WPF funktioniert.

Zuordnen von Validierungsregeln zu einer Bindung

Das WPF-Datenbindungsmodell ermöglicht Ihnen die Zuordnung von ValidationRules zum Binding-Objekt. Im folgenden Beispiel wird z. B. ein TextBox-Element an eine Eigenschaft mit der Bezeichnung StartPrice gebunden, und ein ExceptionValidationRule-Objekt wird der Binding.ValidationRules-Eigenschaft hinzugefügt.

<TextBox Name="StartPriceEntryForm" Grid.Row="2" Grid.Column="1"
    Style="{StaticResource textStyleTextBox}" Margin="8,5,0,5">
  <TextBox.Text>
    <Binding Path="StartPrice" UpdateSourceTrigger="PropertyChanged">
      <Binding.ValidationRules>
        <ExceptionValidationRule />
      </Binding.ValidationRules>
    </Binding>
  </TextBox.Text>
</TextBox>

Ein ValidationRule-Objekt überprüft, ob der Wert einer Eigenschaft gültig ist. WPF verfügt über die folgenden zwei Typen integrierter ValidationRule-Objekte:

Sie können auch eigene Validierungsregeln erstellen, indem Sie von der ValidationRule-Klasse ableiten und die Validate-Methode implementieren. Das folgende Beispiel zeigt die Regel, die vom TextBox "Start Date" von Add Product Listing im Abschnitt Was ist Datenbindung? verwendet wird:

class FutureDateRule : ValidationRule
{
    public override ValidationResult Validate(object value, CultureInfo cultureInfo)
    {
        DateTime date;
        try
        {
            date = DateTime.Parse(value.ToString());
        }
        catch (FormatException)
        {
            return new ValidationResult(false, "Value is not a valid date.");
        }
        if (DateTime.Now.Date > date)
        {
            return new ValidationResult(false, "Please enter a date in the future.");
        }
        else
        {
            return ValidationResult.ValidResult;
        }
    }
}

Die StartDateEntryForm-TextBox verwendet diese FutureDateRule, wie im folgenden Beispiel gezeigt:

<TextBox Name="StartDateEntryForm" Grid.Row="3" Grid.Column="1" 
    Validation.ErrorTemplate="{StaticResource validationTemplate}" 
    Style="{StaticResource textStyleTextBox}" Margin="8,5,0,5">
    <TextBox.Text>
        <Binding Path="StartDate" UpdateSourceTrigger="PropertyChanged" 
            Converter="{StaticResource dateConverter}" >
            <Binding.ValidationRules>
                <src:FutureDateRule />
            </Binding.ValidationRules>
        </Binding>
    </TextBox.Text>
</TextBox>

Da der UpdateSourceTrigger-Wert auf PropertyChanged festgelegt ist, aktualisiert das Bindungsmodul den Quellwert bei jeder Tastatureingabe und überprüft daher auch alle Regeln in der ValidationRules-Auflistung bei jeder Tastatureingabe. Dies wird im Abschnitt zum Validierungsprozess näher erläutert.

Bereitstellen von visuellem Feedback

Wenn der Benutzer einen ungültigen Wert eingibt, könnte es sinnvoll sein, Feedback zum Fehler in der UI der Anwendung zu geben. Eine Möglichkeit zum Bereitstellen von Feedback besteht darin, die angefügte Eigenschaft Validation.ErrorTemplate auf eine benutzerdefinierte ControlTemplate festzulegen. Wie im vorherigen Unterabschnitt gezeigt, verwendet die StartDateEntryForm-TextBox eine ErrorTemplate namens validationTemplate. Im folgenden Beispiel sehen Sie die Definition von validationTemplate:

<ControlTemplate x:Key="validationTemplate">
  <DockPanel>
    <TextBlock Foreground="Red" FontSize="20">!</TextBlock>
    <AdornedElementPlaceholder/>
  </DockPanel>
</ControlTemplate>

Das AdornedElementPlaceholder-Element gibt an, wo das erweiterte Steuerelement platziert werden soll.

Darüber hinaus können Sie ToolTip verwenden, um die Fehlermeldung anzuzeigen. Sowohl die StartDateEntryForm- als auch die StartPriceEntryForm-TextBox verwenden das Format textStyleTextBox, das einen ToolTip erstellt, um die Fehlermeldung anzuzeigen. Im folgenden Beispiel wird die Definition von textStyleTextBox dargestellt. Die angefügte Eigenschaft Validation.HasError ist true, wenn mindestens eine der Bindungen an die Eigenschaften des gebundenen Elements fehlerhaft ist.

<Style x:Key="textStyleTextBox" TargetType="TextBox">
  <Setter Property="Foreground" Value="#333333" />
  <Setter Property="MaxLength" Value="40" />
  <Setter Property="Width" Value="392" />
  <Style.Triggers>
    <Trigger Property="Validation.HasError" Value="true">
      <Setter Property="ToolTip"
        Value="{Binding RelativeSource={RelativeSource Self},
                        Path=(Validation.Errors)[0].ErrorContent}"/>
    </Trigger>
  </Style.Triggers>
</Style>

Mit der benutzerdefinierten ErrorTemplate und ToolTip sieht die StartDateEntryForm-TextBox bei einem Validierungsfehler wie folgt aus:

Datenbindungs-Validierungsfehler

Verfügt Binding über zugeordnete Validierungsregeln, Sie aber keine ErrorTemplate für das gebundene Steuerelement angeben, wird eine standardmäßige ErrorTemplate verwendet, um Benutzer bei einem Validierungsfehler zu benachrichtigen. Die standardmäßige ErrorTemplate ist eine Steuerelementvorlage, die einen roten Rahmen auf der Adornerebene definiert. Mit der standardmäßigen ErrorTemplate und ToolTip sieht die UI der StartPriceEntryForm-TextBox bei einem Validierungsfehler wie folgt aus:

Datenbindungs-Validierungsfehler

Ein Beispiel zum Bereitstellen von Logik zum Validieren aller Steuerelemente in einem Dialogfeld finden Sie im Abschnitt zu benutzerdefinierten Dialogfeldern in der Übersicht über Dialogfelder.

Validierungsprozess

Eine Validierung erfolgt normalerweise, wenn der Wert eines Ziels an die Bindungsquelleigenschaft übergeben wird. Dies tritt bei TwoWay-Bindungen und OneWayToSource-Bindungen auf. Wodurch die Aktualisierung einer Quelle verursacht wird, hängt also vom Wert der UpdateSourceTrigger-Eigenschaft ab, wie im Abschnitt Wodurch werden Quellaktualisierungen ausgelöst? beschrieben.

In der folgenden Tabelle wird der Prozess der Validierung beschrieben. Beachten Sie, dass der Prozess beim Auftreten eines Validierungs- oder anderen Fehlers während dieses Prozesses angehalten wird.

  1. Das Bindungsmodul überprüft, ob benutzerdefinierte ValidationRule-Objekte definiert sind, dessen ValidationStep auf RawProposedValue für die Binding festgelegt ist. In diesem Fall wird die Validate-Methode für jede ValidationRule aufgerufen, bis eine der Regeln einen Fehler zurückgibt oder alle Regeln erfolgreich überprüft wurden

  2. Anschließend ruft das Bindungsmodul den Konverter auf, sofern vorhanden.

  3. Bei erfolgreicher Ausführung des Konverters überprüft das Bindungsmodul, ob benutzerdefinierte ValidationRule-Objekte definiert sind, dessen ValidationStep auf ConvertedProposedValue für die Binding festgelegt ist. In diesem Fall wird die Validate-Methode für jede ValidationRule aufgerufen, für die ValidationStep auf ConvertedProposedValue festgelegt ist, bis eine der Regeln einen Fehler zurückgibt oder alle Regeln erfolgreich überprüft wurden.

  4. Das Bindungsmodul legt die Quelleigenschaft fest.

  5. Das Bindungsmodul überprüft, ob benutzerdefinierte ValidationRule-Objekte definiert sind, dessen ValidationStep auf UpdatedValue für die Binding festgelegt ist. In diesem Fall wird die Validate-Methode für jede ValidationRule aufgerufen, für die ValidationStep auf UpdatedValue festgelegt ist, bis eine der Regeln einen Fehler zurückgibt oder alle Regeln erfolgreich überprüft wurden. Wird ein DataErrorValidationRule-Element einer Bindung zugeordnet und wird das ValidationStep-Element des Objekts auf den Standardwert (UpdatedValue) festgelegt, wird an dieser Stelle das DataErrorValidationRule-Element geprüft. An dieser Stelle werden auch Bindungen geprüft, deren ValidatesOnDataErrors-Element auf true festgelegt ist.

  6. Das Bindungsmodul überprüft, ob benutzerdefinierte ValidationRule-Objekte definiert sind, deren ValidationStep-Elemente auf CommittedValue für das Binding-Element festgelegt ist. In diesem Fall wird die Validate-Methode für jedes ValidationRule-Element aufgerufen, für das das ValidationStep-Element auf CommittedValue festgelegt ist, bis eine der Regeln einen Fehler zurückgibt oder alle Regeln erfolgreich überprüft wurden.

Wenn während dieses Prozesses eine ValidationRule nicht erfolgreich überprüft wird, erstellt das Bindungsmodul ein ValidationError-Objekt und fügt es der Validation.Errors-Auflistung des gebundenen Elements hinzu. Bevor das Bindungsmodul die ValidationRule-Objekte im Rahmen eines Schritts ausführt, werden alle ValidationError entfernt, die der Validation.Errors angefügten Eigenschaft des gebundenen Elements während dieses Schritts hinzugefügt wurden. Wenn beispielsweise eine ValidationRule, dessen ValidationStep auf UpdatedValue festgelegt ist, fehlgeschlagen ist, entfernt das Bindungsmodul bei der nächsten Ausführung des Validierungsprozesses diesen ValidationError unmittelbar vor dem Aufruf einer ValidationRule, für die ValidationStep auf UpdatedValue festgelegt ist.

Wenn Validation.Errors nicht leer ist, wird die angefügte Eigenschaft Validation.HasError des Elements auf true festgelegt. Wenn außerdem die NotifyOnValidationError-Eigenschaft der Binding auf true festgelegt ist, löst das Bindungsmodul das angefügte Ereignis Validation.Error des Elements aus.

Beachten Sie außerdem, dass durch eine gültige Wertübertragung (Ziel zu Quelle oder umgekehrt) die Validation.Errors angefügte Eigenschaft gelöscht wird.

Wenn der Bindung entweder ein ExceptionValidationRule-Element zugeordnet ist oder die ValidatesOnExceptions-Eigenschaft der Bindung auf true festgelegt wurde und beim Festlegen der Quelle durch das Bindungsmodul eine Ausnahme ausgelöst wird, überprüft das Bindungsmodul, ob ein UpdateSourceExceptionFilter-Element vorhanden ist. Sie können den UpdateSourceExceptionFilter-Rückruf verwenden, um einen benutzerdefinierten Handler zum Behandeln von Ausnahmen bereitzustellen. Wenn kein UpdateSourceExceptionFilter für die Binding angegeben ist, erstellt das Bindungsmodul einen ValidationError mit der Ausnahme und fügt ihn der Validation.Errors-Auflistung des gebundenen Elements hinzu.

Debugverfahren

Sie können die angefügte Eigenschaft PresentationTraceSources.TraceLevel auf ein für die Datenbindung relevantes Objekt festlegen, um Informationen über den Status einer bestimmten Bindung zu erhalten.

Siehe auch

Aufgaben

Gewusst wie: Binden an die Ergebnisse einer LINQ-Abfrage

Gewusst wie: Binden an eine ADO.NET-Datenquelle

Referenz

DataErrorValidationRule

Konzepte

Neues in WPF Version 4

Optimieren der Leistung: Datenbindung

Weitere Ressourcen

Demo für die Datenbindung

Gewusst-wie-Themen zur Datenbindung