Hinweis
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, sich anzumelden oder das Verzeichnis zu wechseln.
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, das Verzeichnis zu wechseln.
In diesem Artikel werden die WinUI-Datenbindungsfeatures mithilfe der APIs im Namespace "Microsoft.UI.Xaml.Data" beschrieben.
Hinweis
In diesem Thema werden die Datenbindungsfeatures ausführlich beschrieben. Eine kurze, praktische Einführung finden Sie in der Übersicht über die Datenbindung.
Wichtige APIs
Einleitung
Die Datenbindung ist eine Technik, mit der die Benutzeroberfläche Ihrer App Daten effizient anzeigen und synchronisieren kann. Durch die Trennung von Datenbedenken von Benutzeroberflächenbedenken wird das App-Design vereinfacht, die Lesbarkeit verbessert und die Wartungsfreundlichkeit verbessert.
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. Dieser Bindungsmodus wird als einmal bezeichnet und 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 unidirektionalen Modus verwenden, um eine ListView mit einer Sammlung von Echtzeit-Nachrichtenartikeln zu verknüpfen, die nach Zeitungsabschnitten 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 In der Regel deklarieren Sie beide im UI-Markup. Sie können entweder die {x:Bind}-Markuperweiterung oder die {Binding}-Markuperweiterung verwenden. Sie können sogar eine Mischung aus den beiden in derselben App verwenden – auch auf demselben UI-Element.
{x:Bind} war neu in UWP 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.
UWP-Beispiel-Apps, die {x:Bind} veranschaulichen
UWP-Beispiel-Apps, die {Binding} veranschaulichen
- Laden Sie die UWP Bookstore1-App herunter.
- Laden Sie die Bookstore2-App herunter.
Jede Bindung umfasst diese Teile.
- Eine Bindungsquelle. Diese Quelle stellt die Daten für die Bindung bereit. Dabei kann es sich um eine Instanz jeder Klasse handeln, die Elemente enthält, deren Werte in der Benutzeroberfläche angezeigt werden sollen.
- Ein Bindungsziel. Dieses Ziel ist eine DependencyProperty des FrameworkElements in Ihrer Benutzeroberfläche, die die Daten anzeigt.
- Ein Bindungsobjekt. Dieses Objekt überträgt Datenwerte von der Quelle an das Ziel und optional vom Ziel zurück an die Quelle. Das Bindungsobjekt wird zur XAML-Ladezeit aus Ihrer {x:Bind} - oder {Binding} -Markuperweiterung erstellt.
In den folgenden Abschnitten werfen Sie einen genaueren Blick auf die Bindungsquelle, das Bindungsziel und das Bindungsobjekt. Die Abschnitte werden anhand des Beispiels verknüpft, in dem der Inhalt einer Schaltfläche an eine Zeichenfolgeneigenschaft namens NextButtonText gebunden wird, die zu einer Klasse namens HostViewModel gehört.
Bindungsquelle
Hier ist eine grundlegende Implementierung einer Klasse, die Sie als Bindungsquelle verwenden können.
public class HostViewModel
{
public HostViewModel()
{
NextButtonText = "Next";
}
public string NextButtonText { get; set; }
}
Diese Implementierung von HostViewModel, und seiner Eigenschaft NextButtonText, funktioniert nur für einmalige Bindung. Unidirektionale und bidirektionale Bindungen sind jedoch sehr häufig. In diesen Bindungsarten aktualisiert die Benutzeroberfläche automatisch als Reaktion auf Änderungen der Datenwerte der Bindungsquelle. Damit diese Bindungsarten ordnungsgemäß funktionieren, müssen Sie die Bindungsquelle für das Bindungsobjekt feststellbar machen. Wenn Sie 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 sein.
Eine Möglichkeit dazu 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. A FrameworkElement ist eine gute Bindungsquelle direkt aus der Box.
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. Bei diesem Ansatz wird ein einzelnes Ereignis namens PropertyChangedimplementiert. Ein Beispiel für die Verwendung HostViewModel ist im folgenden Code dargestellt.
...
using System.ComponentModel;
using System.Runtime.CompilerServices;
...
public class HostViewModel : INotifyPropertyChanged
{
private string nextButtonText;
public event PropertyChangedEventHandler PropertyChanged = delegate { };
public HostViewModel()
{
NextButtonText = "Next";
}
public string NextButtonText
{
get { return nextButtonText; }
set
{
nextButtonText = value;
OnPropertyChanged();
}
}
public void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
// Raise the PropertyChanged event, passing the name of the property whose value has changed.
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
Jetzt ist die NextButtonText Eigenschaft feststellbar. Wenn Sie eine unidirektionale oder bidirektionale Bindung zu dieser Eigenschaft erstellen (wie später wird gezeigt), abonniert das resultierende Bindungsobjekt das PropertyChanged Ereignis. Wenn dieses Ereignis ausgelöst wird, empfängt der Handler des Bindungsobjekts ein Argument, das den Namen der geänderten Eigenschaft enthält. So weiß das Bindungsobjekt, welcher Eigenschaftswert erneut gelesen werden soll.
Damit Sie das zuvor gezeigte Muster nicht mehrmals implementieren müssen, können Sie, wenn Sie C# verwenden, 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()
{
NextButtonText = "Next";
}
public string NextButtonText
{
get { return nextButtonText; }
set { SetProperty(ref nextButtonText, value); }
}
}
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.
Sie können eine Bindungsquelle entweder als einzelnes Objekt behandeln, dessen Eigenschaften Daten enthalten, oder als Auflistung von Objekten. Im C#-Code können Sie eine einmalige Bindung an ein Objekt vornehmen, das List<T> implementiert, um eine Auflistung anzuzeigen, die sich zur Laufzeit nicht ändert. Bei einer beobachtbaren Auflistung (beobachten, wenn Elemente zur Auflistung hinzugefügt oder daraus entfernt werden), einseitig an ObservableCollection<T> binden statt. Um eine Bindung an Ihre eigenen Sammlungsklassen zu erstellen, verwenden Sie die Anleitungen in der folgenden Tabelle.
| Scenario | C# (CLR) | C++/WinRT |
|---|---|---|
| Binden an ein Objekt. | Dabei kann es sich um ein beliebiges Objekt handeln. | Dabei kann es sich um ein beliebiges Objekt handeln. |
| Dient zum Abrufen von Eigenschaftsänderungsbenachrichtigungen aus einem gebundenen Objekt. | Das Objekt muss INotifyPropertyChanged implementieren. | Das Objekt muss INotifyPropertyChanged implementieren. |
| Binden an eine Auflistung. | Liste<T> | IVector von IInspectable oder IBindableObservableVector. Siehe XAML-Elementsteuerelemente; Binden an eine C++/WinRT-Auflistung und Sammlungen mit C++/WinRT. |
| Abrufen von Sammlungsänderungsbenachrichtigungen aus einer gebundenen Sammlung. | ObservableCollection<T> | IObservableVector von IInspectable. Beispiel: winrt::single_threaded_observable_vector<T>. |
| Implementieren Sie eine Sammlung, die bindung unterstützt. | IList<T> und IEnumerable<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 Sie eine Sammlung, die Änderungsbenachrichtigungen für die Sammlung unterstützt. | Erweitern Sie ObservableCollection<T> oder implementieren Sie (nicht-generische) IList und INotifyCollectionChanged. | Implementieren Sie IObservableVector von IInspectable oder IBindableObservableVector. |
| Implementieren Sie eine Auflistung, die das inkrementelle Laden unterstützt. | Erweitern Sie ObservableCollection<T> oder implementieren Sie (nicht-generische) IList und INotifyCollectionChanged. Implementieren Sie darüber hinaus ISupportIncrementalLoading. | Implementieren Sie IObservableVector von IInspectable oder IBindableObservableVector. Implementieren Sie 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 folgenden beiden Beispielen ist die Button.Content Eigenschaft das Bindungsziel. Der Wert wird auf eine Markuperweiterung festgelegt, die das Bindungsobjekt deklariert. Das erste Beispiel zeigt {x:Bind}, und das zweite Beispiel zeigt {Binding}. Das Deklarieren von Bindungen im Markup ist der gängige Fall, da es praktisch, lesbar und toolierbar ist. Wenn Sie es jedoch benötigen, können Sie Markup vermeiden und stattdessen eine Instanz der Binding-Klasse imperativ (programmgesteuert) erstellen.
<Button Content="{x:Bind ...}" ... />
<Button Content="{Binding ...}" ... />
Wenn Sie C++/WinRT 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 mit dem Windows App SDK verfügbar. 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
Bevor Sie Ihr {x:Bind} -Markup erstellen, müssen Sie die Bindungsquellklasse aus der Klasse verfügbar machen, die Ihre Markupseite darstellt. Fügen Sie ihrer HostViewModel Fensterklasse eine Eigenschaft (in diesem Fall vom TypMainWindow) hinzu.
namespace DataBindingInDepth
{
public sealed partial class MainWindow : Window
{
public MainWindow()
{
this.InitializeComponent();
ViewModel = new HostViewModel();
}
public HostViewModel ViewModel { get; set; }
}
}
Nachdem Sie die Eigenschaft hinzugefügt haben, können Sie sich das Markup genauer ansehen, das das Bindungsobjekt deklariert. Im folgenden Beispiel wird das gleiche Button.Content Bindungsziel verwendet, das Sie zuvor im Abschnitt "Bindungsziel" gesehen haben. Es zeigt das Bindungsziel an, das an die HostViewModel.NextButtonText Eigenschaft gebunden wird.
<!-- MainWindow.xaml -->
<Window x:Class="DataBindingInDepth.MainWindow" ... >
<Button Content="{x:Bind Path=ViewModel.NextButtonText, Mode=OneWay}" ... />
</Window>
Beachten Sie den Wert, den Sie für Path angeben. Das Fenster interpretiert diesen Wert in seinem eigenen Kontext. In diesem Fall beginnt der Pfad mit dem Verweisen auf die ViewModel Eigenschaft, die Sie soeben der MainWindow Seite hinzugefügt haben. Diese Eigenschaft gibt eine HostViewModel Instanz zurück, sodass Sie in dieses Objekt punktieren können, um auf die HostViewModel.NextButtonText Eigenschaft zuzugreifen. Sie geben Mode an, um das einmalige Standardverhalten von {x:Bind} zu überschreiben.
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 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 Aktualisierung der Schaltfläche Content anzuzeigen.
// MainWindow.xaml.cs
private void Button_Click(object sender, RoutedEventArgs e)
{
ViewModel.NextButtonText = "Updated Next button text";
}
Hinweis
Änderungen an TextBox.Text werden an eine bidirektionale 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 Sie es als Elementvorlage, als Inhaltsvorlage oder als Kopfzeilenvorlage verwenden), wird der Wert des Path Objekts nicht im Kontext des Fensters interpretiert. Stattdessen arbeitet es im Kontext des Datenobjekts, das Sie beinflussen. Wenn Sie `{x:Bind}` in einer Datenvorlage verwenden, können Sie deren Bindungen zur Kompilierungszeit überprüfen und effizienten Code für sie generieren. Dazu muss der Typ des Datenobjekts von DataTemplate mithilfe von x:DataType deklariert werden. Das folgende Beispiel könnte als ItemTemplate eines Elementsteuerungselements 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 Titleimplementiert. Sie haben auch eine Eigenschaft MainWindow.SampleDataGroupAsObject, die vom Typ object ist, 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 den Typ objectnicht gefunden wird. Um diesen Fehler zu beheben, fügen Sie ihrer Path Syntax eine Umwandlung wie folgt hinzu: <TextBlock Text="{x:Bind ((data:SampleDataGroup)SampleDataGroupAsObject).Title}"/>. Hier ist ein weiteres Beispiel, in dem Element sie deklariert wird, object aber tatsächlich ein TextBlock: <TextBlock Text="{x:Bind Element.Text}"/>. Ein Cast behebt das Problem: <TextBlock Text="{x:Bind ((TextBlock)Element).Text}"/>.
Wenn Ihre Daten asynchron geladen werden
Die partiellen Klassen für Ihre Fenster generieren Code, der {x:Bind} während der Kompilierung unterstützt. Sie finden diese Dateien 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 des Fensters. Dieser Handler ruft die Initialize Methode für eine generierte Klasse auf, die die Bindungen des Fensters darstellt.
Initialize Aufrufe Update , um mit dem Verschieben von Daten zwischen der Bindungsquelle und dem Ziel zu beginnen.
Loading wird direkt vor dem ersten Messdurchlauf des Fenster- oder Benutzersteuerelements ausgelöst. Wenn Ihre Daten asynchron geladen werden, ist sie möglicherweise nicht zum Zeitpunkt Initialize des Aufrufs bereit. Nach dem Laden von Daten 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 unidirektionale Bindungen zu haben und Änderungen zu überwachen. Wenn Ihre Daten keine fein abgestimmten Änderungen durchlaufen und wahrscheinlich als Teil einer bestimmten Aktion aktualisiert werden, können Sie Ihre Bindungen einmalig vornehmen und ein manuelles Update jederzeit mit einem Aufruf Updateerzwingen.
Hinweis
{x:Bind} eignet sich nicht für spät gebundene Szenarien, wie das Navigieren in der Wörterbuchstruktur eines JSON-Objekts oder Duck-Typing. "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 mit einem Person oder einem Wine Objekt gleichermaßen zufrieden (vorausgesetzt, dass diese Typen jeweils eine Age Eigenschaft hatten). Verwenden Sie für diese Szenarien die {Binding} Markuperweiterung.
Mit {Binding} deklariertes Bindungsobjekt
Wenn Sie C++/WinRT verwenden, fügen Sie das BindableAttribute-Attribut einer beliebigen Laufzeitklasse hinzu, an die Sie binden möchten, wenn Sie die {Binding} -Markuperweiterung verwenden. Um {x:Bind} zu verwenden, benötigen Sie dieses Attribut nicht.
// HostViewModel.idl
// Add this attribute:
[Microsoft.UI.Xaml.Data.Bindable]
runtimeclass HostViewModel : Microsoft.UI.Xaml.Data.INotifyPropertyChanged
{
HostViewModel();
String NextButtonText;
}
Von Bedeutung
Wenn Sie C++/WinRT verwenden, ist das BindableAttribute-Attribut mit dem Windows App SDK verfügbar. Ohne dieses Attribut müssen Sie die ICustomPropertyProvider - und ICustomProperty-Schnittstellen implementieren, um die {Binding} -Markuperweiterung verwenden zu können.
Standardmäßig geht {Binding} davon aus, dass Sie eine Bindung an den DataContext Ihres Markupfensters ausführen. Legen Sie also das DataContext Fenster so fest, dass es sich um eine Instanz Ihrer Bindungsquellklasse (in diesem Fall vom Typ HostViewModel ) handelt. Das folgende Beispiel zeigt das Markup, das das Bindungsobjekt deklariert. Es verwendet das gleiche Button.Content Bindungsziel, das zuvor im Abschnitt "Bindungsziel" verwendet wird, und es wird an die HostViewModel.NextButtonText Eigenschaft gebunden.
<Window xmlns:viewmodel="using:DataBindingInDepth" ... >
<Window.DataContext>
<viewmodel:HostViewModel x:Name="viewModelInDataContext"/>
</Window.DataContext>
...
<Button Content="{Binding Path=NextButtonText}" ... />
</Window>
// MainWindow.xaml.cs
private void Button_Click(object sender, RoutedEventArgs e)
{
viewModelInDataContext.NextButtonText = "Updated Next button text";
}
Beachten Sie den angegebenen Wert für Path. Der DataContext-Wert des Fensters interpretiert diesen Wert, der in diesem Beispiel auf eine Instanz von HostViewModelfestgelegt ist. Der Pfad verweist auf die HostViewModel.NextButtonText Eigenschaft. Sie können das Mode auslassen, da die {Binding}-Standardeinstellung Einweg an dieser Stelle ausreicht.
Der Standardwert von DataContext für ein UI-Element ist der geerbte Wert des übergeordneten Elements. Sie können diese Standardeinstellung explizit DataContext setzen und außer Kraft setzen, was wiederum standardmäßig von untergeordneten Elementen geerbt wird. Die explizite Einstellung DataContext für ein Element ist nützlich, wenn Sie mehrere Bindungen haben 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 die Bindung festlegen Sourceoder RelativeSourceElementName explizit festlegen (einzelheiten hierzu finden Sie unter {Binding}).
Innerhalb einer DataTemplate wird der DataContext automatisch auf das Datenobjekt festgelegt, das vorlaged ist. Das folgende Beispiel könnte als ItemTemplate eines Elementsteuerungs verwendet werden, das an eine Auflistung eines beliebigen Typs gebunden ist, die Zeichenfolgeneigenschaften mit den Namen Title und Description hat.
<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. Damit Änderungen nach jedem Tastaturanschlag des Benutzers gesendet werden, legen Sie sie für die Bindung im Markup fest UpdateSourceTriggerPropertyChanged . Sie können auch die Kontrolle übernehmen, wann Änderungen an die Quelle gesendet werden, indem Sie diese einstellung festlegen UpdateSourceTriggerExplicit. 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, Anschließend 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. Diese Lösung ist jedoch nicht flexibel oder skalierbar, wenn Sie über große Zahlen oder große Kombinationen aus Quell- und Zieleigenschaften verfügen. In diesem Fall haben Sie eine Reihe von Optionen:
- Wenn Sie diese Konvertierung verwenden
{x:Bind}, können Sie eine direkte Bindung an eine Funktion vornehmen. - 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 und einen DateTime-Wert in einen string Wert 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();
}
}
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 Convert das Bindungsmodul die zurückgegebenen Daten auf und übergibt sie an das Ziel. Wenn Daten vom Ziel übergeben werden (für eine bidirektionale Bindung), ruft ConvertBack das Bindungsmodul die zurückgegebenen Daten an die Quelle ab und übergibt sie.
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 soll, 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 alle ToString Implementierungen ignoriert, die die Basisklassenimplementierung ausblenden. Stattdessen sollten Unterklassenimplementierungen die Basisklassenmethode ToString überschreiben. In nativen Sprachen scheinen alle verwalteten Objekte ICustomPropertyProvider und IStringable zu implementieren. Allerdings werden alle Aufrufe an GetStringRepresentation diese IStringable.ToString Methode oder eine Außerkraftsetzung dieser Methode und Object.ToString nie an eine neue ToString Implementierung weitergeleitet, die die Basisklassenimplementierung ausblendet.
Hinweis
Das Windows Community Toolkit stellt einen BoolToVisibilityConverter bereit. Der Konverter ordnet true den Visible Enumerationswert und false damit Collapsed eine Eigenschaft an einen booleschen Wert Visibility zu, ohne einen Konverter zu erstellen. Um den Konverter zu verwenden, muss Ihr Projekt das CommunityToolkit.WinUI.Converters NuGet-Paket hinzufügen.
Funktionsbindung in {x:Bind}
{x:Bind} aktiviert den letzten Schritt in einem Bindungspfad als Funktion. Verwenden Sie dieses Feature, um Konvertierungen durchzuführen oder Bindungen zu erstellen, die von mehr als einer Eigenschaft abhängen. Weitere Informationen finden Sie unter "Functions 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 diese Bindung im Markup aussieht.
<TextBox x:Name="myTextBox" />
<TextBlock Text="{x:Bind myTextBox.Text, Mode=OneWay}" />
Ressourcenwörterbücher mit {x:Bind}
Die {x:Bind}-Markuperweiterung hängt von der Codegenerierung ab. Daher ist eine CodeBehind-Datei erforderlich, die einen Konstruktor enthält, der aufruft InitializeComponent (um den generierten Code zu initialisieren). Um das Ressourcenwörterbuch wiederzuverwenden, instanziieren Sie den Typ (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 in ihr verwenden {x:Bind} möchten.
<!-- TemplatesResourceDictionary.xaml -->
<ResourceDictionary
x:Class="ExampleNamespace.TemplatesResourceDictionary"
.....
xmlns:examplenamespace="using:ExampleNamespace">
<DataTemplate x:Key="EmployeeTemplate" x:DataType="examplenamespace:IEmployee">
<Grid>
<TextBlock Text="{x:Bind Name}"/>
</Grid>
</DataTemplate>
</ResourceDictionary>
// TemplatesResourceDictionary.xaml.cs
using Microsoft.UI.Xaml.Data;
namespace ExampleNamespace
{
public partial class TemplatesResourceDictionary
{
public TemplatesResourceDictionary()
{
InitializeComponent();
}
}
}
<!-- MainWindow.xaml -->
<Window x:Class="ExampleNamespace.MainWindow"
....
xmlns:examplenamespace="using:ExampleNamespace">
<Window.Resources>
<ResourceDictionary>
....
<ResourceDictionary.MergedDictionaries>
<examplenamespace:TemplatesResourceDictionary/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Window.Resources>
</Window>
Mischen von {x:Bind} und {Binding} in einem wiederverwendbaren Stil
Das vorherige Beispiel zeigte, wie {x:Bind} in DataTemplates verwendet werden. Sie können auch wiederverwendbare Formatvorlagen erstellen, die sowohl {x:Bind} als auch {Binding} Markuperweiterungen kombinieren. Diese Kombination ist nützlich, wenn Sie einige Eigenschaften an zur Kompilierungszeit bekannte Werte binden möchten, indem Sie {x:Bind} verwenden, und andere Eigenschaften an Werte des DataContext zur Laufzeit, indem Sie {Binding} nutzen.
Das folgende Beispiel zeigt, wie Sie eine wiederverwendbare Schaltflächenformatvorlage erstellen, die beide Bindungsansätze verwendet:
TemplatesResourceDictionary.xaml
<!-- 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
// TemplatesResourceDictionary.xaml.cs
using Microsoft.UI;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Data;
using Microsoft.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 MainWindow.xaml mit einem ViewModel, das Laufzeitwerte bereitstellt:
<!-- MainWindow.xaml -->
<Window x:Class="ExampleNamespace.MainWindow"
....
xmlns:examplenamespace="using:ExampleNamespace">
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<examplenamespace:TemplatesResourceDictionary/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Window.Resources>
<Grid>
<Grid.DataContext>
<examplenamespace:ButtonThemeViewModel/>
</Grid.DataContext>
<StackPanel Margin="20">
<!-- These buttons use the mixed binding style -->
<Button Content="Save" Style="{StaticResource CustomButtonStyle}"/>
<Button Content="Cancel" Style="{StaticResource CustomButtonStyle}"/>
</StackPanel>
</Grid>
</Window>
ButtonThemeViewModel.cs (der DataContext, der Laufzeitbindungswerte bereitstellt):
using System.ComponentModel;
using Microsoft.UI;
using Microsoft.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 vom DataContext (ButtonBackgroundBrush, ButtonForegroundBrush, ButtonHoverBrush) abhängen. -
{x:Bind}wird für Eigenschaften verwendet, die kompilierungszeit bekannt sind und zum ResourceDictionary selbst gehören (DefaultIndicatorBrush, DefaultPressedBrush) - Die Formatvorlage ist wiederverwendbar, und Sie können sie auf eine beliebige Schaltfläche anwenden.
- Laufzeitdesigns sind über DataContext möglich und profitieren dennoch von der Leistung für
{x:Bind}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. Dieses Feature ist eine zusätzliche Option zum Behandeln von Ereignissen, zusätzlich zur Behandlung von Ereignissen mit einer Methode in der Code-Behind-Datei. Angenommen, Sie haben einen ListViewDoubleTapped Ereignishandler in Ihrer MainWindow Klasse.
public sealed partial class MainWindow : Window
{
...
public void ListViewDoubleTapped()
{
// Handle double-tapped logic
}
}
Sie können das DoubleTapped-Ereignis eines ListView an eine Methode in der MainWindow wie folgt binden.
<ListView DoubleTapped="{x:Bind ListViewDoubleTapped}" />
Sie können mit dieser Technik keine überladenen Methoden verwenden, um ein Ereignis zu behandeln. Wenn die Methode, die das Ereignis behandelt, Parameter hat, müssen alle Parameter den Typen der Ereignisparameter jeweils zugewiesen werden können. In diesem Fall ist ListViewDoubleTapped nicht überladen und hat keine Parameter (aber es wäre auch gültig, wenn es zwei object Parameter verwenden würde).
Die Ereignisbindungstechnik ähnelt der Implementierung und Nutzung von Befehlen. Ein Befehl ist eine Eigenschaft, die ein Objekt zurückgibt, das die ICommand-Schnittstelle implementiert. Sowohl {x:Bind} als auch {Binding} funktionieren mit Befehlen. Damit Sie das Befehlsmuster nicht mehrmals implementieren müssen, können Sie die DelegateCommand Hilfsklasse verwenden, die Sie im UWP-Beispiel " QuizGame " (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 in Ihren verpackten Windows App SDK-Apps abzurufen. Die verschiedenen GetFilesAsync, GetFoldersAsync und GetItemsAsync-Methoden geben jedoch keine Werte zurück, die sich zur Bindung an Listensteuerelemente eignen. 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 gespeichert, wenn Sie die entsprechende SavePropertiesAsync Methode aufrufen (z. B. MusicProperties.SavePropertiesAsync). Sie sollten Änderungen speichern, wenn das Element den Fokus verliert, da diese Aktion eine Zurücksetzung der Auswahl auslöst.
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 für einige Elemente zurückgeben null kann, bevor er seinen Wert auffüllt. Sie sollten beispielsweise prüfen null , ob 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 folgendes hat:
- 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 folgendes hat:
- ein Schlüssel, bei dem es sich um einen Autorennamen handelt, und
- eine Auflistung der
BookSkuObjekte, derenAuthorNameEigenschaft 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 Eigenschaft, die gruppierte Daten zurückgibt, und binden das Elementsteuerelement an die CollectionViewSource. Ein zusätzliches Wert-Add-In eines CollectionViewSource ist, dass es das aktuelle Element nachverfolgt, sodass Sie mehrere Elementsteuerelemente synchronisieren können, indem Sie sie alle an dasselbe CollectionViewSourcebinden. 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. 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 tatsächlich die Gruppe von Elementen ist. Mit diesem Muster müssen Sie nicht festlegen ItemsPath. Im Muster "has-a-group" verfügt das Gruppenobjekt über eine oder mehrere Eigenschaften eines Auflistungstyps (z List<T>. B. ), 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 auf den Namen der Eigenschaft festlegen ItemsPath , die die Gruppe von Elementen enthält.
Das folgende Beispiel veranschaulicht das Muster "hat eine Gruppe". Die Fensterklasse verfügt über eine Eigenschaft mit dem Namen DataContext, die eine Instanz unseres Ansichtsmodells zurückgibt. Die CollectionViewSource bindet an die Authors Eigenschaft des Ansichtsmodells (Authors ist die Auflistung 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 das CollectionViewSourceRasteransicht gebunden und hat seine Gruppenformatvorlage definiert, sodass die Elemente in Gruppen gerendert werden können.
<Window.Resources>
<CollectionViewSource
x:Name="AuthorHasACollectionOfBookSku"
Source="{x:Bind ViewModel.Authors}"
IsSourceGrouped="true"
ItemsPath="BookSkus"/>
</Window.Resources>
...
<GridView
ItemsSource="{x:Bind AuthorHasACollectionOfBookSku}" ...>
<GridView.GroupStyle>
<GroupStyle
HeaderTemplate="{StaticResource AuthorGroupHeaderTemplateWide}" ... />
</GridView.GroupStyle>
</GridView>
Sie 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 ab List<T> (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. Diese Gruppierung wird durch den Eigenschaftspfad "Key" im Verweis auf den Gruppenschlüsselwert angegeben.
using System.Linq;
...
private IOrderedEnumerable<IGrouping<string, BookSku>> genres;
public IOrderedEnumerable<IGrouping<string, BookSku>> Genres
{
get
{
if (genres == null)
{
genres = from book in bookSkus
group book by book.genre into grp
orderby grp.Key
select grp;
}
return genres;
}
}
Denken Sie daran, dass Sie beim Verwenden von {x:Bind} mit Datenvorlagen den Typ angeben müssen, an den der Typ gebunden wird, indem Sie einen x:DataType Wert festlegen. Wenn der Typ generisch ist, können Sie dies im Markup nicht ausdrücken, sodass Sie stattdessen {Binding} 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 UWP-Beispiel-App Bookstore2 veranschaulicht die Verwendung der 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 dasselbe CollectionViewSourcegebunden sind.
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. Diese Konfiguration 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. Wenn Sie dies vergessen, erstellen Sie einen Datenbindungsfehler, und Ihre App wird entweder nicht kompiliert oder nicht ordnungsgemäß ausgeführt.
Die Bindungsobjekte, die {x:Bind} und {Binding} erstellen, sind weitgehend funktional gleichwertig. Enthält jedoch {x:Bind} 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. Diese Erkennung umfasst die Kompilierungszeitüberprüfung Ihrer Bindungsausdrücke und das Debuggen, indem Haltepunkte im Quellcode festgelegt werden, der als Teilklasse für Ihre Seite generiert wird. Sie finden diese Klassen 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 "Unterbrechung bei nicht behandelten Ausnahmen" im Microsoft Visual Studio-Debugger. Der Debugger bricht die Ausführung an diesem Punkt auf, und Sie können dann debuggen, was nicht geklappt hat. Der von dem Code generierte {x:Bind} Code folgt demselben Muster für jeden Teil des Diagramms der Bindungsquellknoten, und Sie können die Informationen im Fenster "Aufrufliste " verwenden, um die Reihenfolge der Aufrufe zu ermitteln, 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 in den Fenstern "Ausgabe - und XAML-Bindungsfehler " in Visual Studio angezeigt. Weitere Informationen zum Debuggen von Bindungsfehlern in Visual Studio finden Sie unter XAML-Datenbindungsdiagnose.
Erstellen von Bindungen im Code
Hinweis
Dieser Abschnitt gilt nur für {Binding}, da Sie keine {x:Bind} -Bindungen im Code erstellen können. Sie können jedoch einige der vorteile von {x:Bind}DependencyObject.RegisterPropertyChangedCallback erreichen, mit der 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. Sie können die Bindungseigenschaftswerte nach dem Aufruf SetBindingjedoch nicht mehr ändern.
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.
var textcolor = new MyColors();
// Brush1 is set to be a SolidColorBrush with the value Red.
textcolor.Brush1 = new SolidColorBrush(Colors.Red);
// Set the DataContext of the TextBox MyTextBox.
MyTextBox.DataContext = textcolor;
// Create the binding and associate it with the text box.
var binding = new Binding { Path = new PropertyPath("Brush1") };
MyTextBox.SetBinding(TextBox.ForegroundProperty, binding);
Vergleich der Features {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, Path ist standardmäßig im Fenster verwurzelt, nicht der DataContext. |
| 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 ihr einen XML-Namespace voran, der einem Codenamespace am Anfang des Dokuments zugeordnet werden soll. |
| Guss | {x:Bind groups[0].(data:SampleDataGroup.Title)}- Nicht erforderlich für {Binding}. |
Umwandlungen werden mithilfe von Klammern angegeben. Wenn die Eigenschaft nicht in einem XAML-Namespace deklariert ist, stellen Sie ihr einen 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}} |
Deklarieren Sie Konverter im Stammverzeichnis von Window, Control, ResourceDictionary oder in "App.xaml". |
| ConverterParameter, ConverterLanguage | {x:Bind IsShown, Converter={StaticResource BoolToVisibility}, ConverterParameter=One, ConverterLanguage=fr-fr}- {Binding IsShown, Converter={StaticResource BoolToVisibility}, ConverterParameter=One, ConverterLanguage=fr-fr} |
Deklarieren Sie Konverter im Stammverzeichnis von Window, Control, ResourceDictionary oder in "App.xaml". |
| 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} |
Mit {x:Bind} binden Sie an ein Feld; Path ist standardmäßig im Fenster verwurzelt, sodass Sie über dessen Feld auf jedes benannte Element zugreifen können. |
| 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}, benennen Sie das Element, und verwenden Sie dessen Namen in Path. |
| RelativeSource: TemplatedParent (VorlageEltern) | Nicht erforderlich für {x:Bind}- {Binding <path>, RelativeSource={RelativeSource TemplatedParent}} |
Bei {x:Bind}, TargetType zeigt die ControlTemplate Bindung an das übergeordnete Element der Vorlage an. Für {Binding}die meisten Verwendungen kann die reguläre Vorlagenbindung in Steuerelementvorlagen verwendet werden. Verwenden Sie TemplatedParent jedoch die Stelle, an der Sie einen Konverter oder eine bidirektionale Bindung verwenden müssen. |
| Quelle | Nicht erforderlich für {x:Bind}- <ListView ItemsSource="{Binding Orders, Source={StaticResource MyData}}"/> |
Für {x:Bind} können Sie das benannte Element direkt verwenden, eine Eigenschaft nutzen oder einen statischen Pfad angeben. |
| Modus | {x:Bind Name, Mode=OneWay}- {Binding Name, Mode=TwoWay} |
Mode kann sein OneTime, , OneWayoder TwoWay.
{x:Bind} Standardwert ist OneTime; {Binding} Standardwert ist OneWay. |
| UpdateSourceTrigger | {x:Bind Name, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}- {Binding UpdateSourceTrigger=PropertyChanged} |
UpdateSourceTrigger kann sein Default, , LostFocusoder PropertyChanged.
{x:Bind} unterstützt nicht UpdateSourceTrigger=Explicit.
{x:Bind} verwendet PropertyChanged Verhalten für alle Fälle, außer TextBox.Textin fällen, in denen das Verhalten verwendet wird LostFocus . |
Siehe auch
Windows developer