Notitie
Voor toegang tot deze pagina is autorisatie vereist. U kunt proberen u aan te melden of de directory te wijzigen.
Voor toegang tot deze pagina is autorisatie vereist. U kunt proberen de mappen te wijzigen.
Belangrijke API's
Opmerking
In dit onderwerp worden gedetailleerde functies voor gegevensbinding beschreven. Zie Het overzicht van gegevensbinding voor een korte, praktische inleiding.
Dit onderwerp gaat over gegevensbinding voor de API's die zich in de naamruimte Windows.UI.Xaml.Data bevinden.
Gegevensbinding is een manier voor de gebruikersinterface van uw app om gegevens weer te geven en optioneel om gesynchroniseerd te blijven met die gegevens. Met gegevensbinding kunt u het probleem van gegevens scheiden van het probleem van de gebruikersinterface en dat resulteert in een eenvoudiger conceptueel model, evenals een betere leesbaarheid, testbaarheid en onderhoudbaarheid van uw app.
U kunt gegevensbinding gebruiken om alleen waarden uit een gegevensbron weer te geven wanneer de gebruikersinterface voor het eerst wordt weergegeven, maar niet om te reageren op wijzigingen in deze waarden. Dit is een bindingsmodus die eenmalig wordt genoemd en het werkt goed voor een waarde die niet wordt gewijzigd tijdens runtime. U kunt er ook voor kiezen om de waarden te 'observeren' en de gebruikersinterface bij te werken wanneer deze worden gewijzigd. Deze modus wordt eenrichtingsmodus genoemd en werkt goed voor alleen-lezen gegevens. Uiteindelijk kunt u ervoor kiezen om zowel te observeren als bij te werken, zodat wijzigingen die de gebruiker aanbrengt in waarden in de gebruikersinterface automatisch naar de gegevensbron worden gepusht. Deze modus wordt in twee richtingen genoemd en werkt goed voor lees-/schrijfgegevens. Hieronder vindt u enkele voorbeelden.
- U kunt de eenmalige modus gebruiken om een afbeelding te binden aan de foto van de huidige gebruiker.
- U kunt de eenrichtingsmodus gebruiken om een ListView te binden aan een verzameling realtime nieuwsartikelen gegroepeerd op krantensectie.
- U kunt de tweerichtingsmodus gebruiken om een tekstvak te binden aan de naam van een klant in een formulier.
Onafhankelijk van de modus zijn er twee soorten bindingen en ze worden meestal gedeclareerd in ui-markeringen. U kunt ervoor kiezen om de markeringsextensie {x:Bind} of de markeringsextensie {Binding} te gebruiken. En u kunt zelfs een combinatie van de twee in dezelfde app gebruiken, zelfs in hetzelfde UI-element. {x:Bind} is nieuw voor Windows 10 en heeft betere prestaties. Alle details die in dit onderwerp worden beschreven, zijn van toepassing op beide soorten bindingen, tenzij we expliciet anders zeggen.
Voorbeeld-apps die {x:Bind} demonstreren
Voorbeeld-apps die {Binding} demonstreren
- Download de App Bookstore1 .
- Download de App Bookstore2 .
Elke binding omvat deze stukken
- Een bindingsbron. Dit is de bron van de gegevens voor de binding en kan een exemplaar zijn van elke klasse met leden waarvan u de waarden wilt weergeven in uw gebruikersinterface.
- Een bindingsdoel. Dit is een DependencyProperty van het FrameworkElement in uw gebruikersinterface waarin de gegevens worden weergegeven.
- Een bindingsobject. Dit is het stuk dat gegevenswaarden van de bron naar het doel overdraagt en eventueel van het doel terug naar de bron. Het bindingsobject wordt gemaakt op de laadtijd van XAML vanuit de markeringsextensie {x:Bind} of {Binding} .
In de volgende secties bekijken we de bindingsbron, het bindingsdoel en het bindingsobject. En we koppelen de secties samen met het voorbeeld van het koppelen van de inhoud van een knop aan een tekenreekseigenschap met de naam NextButtonText, die deel uitmaakt van een klasse met de naam HostViewModel.
Bindingsbron
Hier volgt een zeer elementaire implementatie van een klasse die we als bindingsbron kunnen gebruiken.
Als u C++/WinRT gebruikt, voegt u nieuwe Midl File-items (.idl) toe aan het project, zoals wordt weergegeven in de onderstaande lijst met C++/WinRT-codevoorbeelden. Vervang de inhoud van deze nieuwe bestanden door de MIDL 3.0-code die wordt weergegeven in de vermelding, bouw het project om te genereren HostViewModel.h en .cppen voeg vervolgens code toe aan de gegenereerde bestanden die overeenkomen met de vermelding. Zie XAML-besturingselementen voor meer informatie over deze gegenereerde bestanden en hoe u ze naar uw project kopieert. Bind deze met een C++/WinRT-eigenschap.
public class HostViewModel
{
public HostViewModel()
{
this.NextButtonText = "Next";
}
public string NextButtonText { get; set; }
}
// HostViewModel.idl
namespace DataBindingInDepth
{
runtimeclass HostViewModel
{
HostViewModel();
String NextButtonText;
}
}
// HostViewModel.h
// Implement the constructor like this, and add this field:
...
HostViewModel() : m_nextButtonText{ L"Next" } {}
...
private:
std::wstring m_nextButtonText;
...
// HostViewModel.cpp
// Implement like this:
...
hstring HostViewModel::NextButtonText()
{
return hstring{ m_nextButtonText };
}
void HostViewModel::NextButtonText(hstring const& value)
{
m_nextButtonText = value;
}
...
Deze implementatie van HostViewModel en de eigenschap NextButtonText zijn alleen geschikt voor eenmalige binding. Maar bindingen in één richting en in twee richtingen zijn zeer gebruikelijk, en in dergelijke bindingen wordt de gebruikersinterface automatisch bijgewerkt als reactie op wijzigingen in de gegevenswaarden van de bindingsbron. Als u wilt dat deze soorten bindingen correct werken, moet u de bindingsbron 'waarneembaar' maken voor het bindingsobject. Dus als we in ons voorbeeld eenrichtings- of tweerichtingsbinding willen maken met de eigenschap NextButtonText , moeten wijzigingen die zich tijdens de runtime voordoen, worden aangebracht in de waarde van die eigenschap die waarneembaar is voor het bindingsobject.
Een manier om dit te doen, is door de klasse af te leiden die de bindingsbron van DependencyObject vertegenwoordigt en een gegevenswaarde beschikbaar te maken via een DependencyProperty. Zo wordt een FrameworkElement waarneembaar. FrameworkElements zijn goede bindingsbronnen direct in de doos.
Een lichtgewicht manier om een klasse waarneembaar te maken, en een vereiste voor klassen die al een basisklasse hebben, is het implementeren van System.ComponentModel.INotifyPropertyChanged. Dit omvat alleen het implementeren van één gebeurtenis met de naam PropertyChanged. Hieronder ziet u een voorbeeld van het gebruik van HostViewModel .
...
using System.ComponentModel;
using System.Runtime.CompilerServices;
...
public class HostViewModel : INotifyPropertyChanged
{
private string nextButtonText;
public event PropertyChangedEventHandler PropertyChanged = delegate { };
public HostViewModel()
{
this.NextButtonText = "Next";
}
public string NextButtonText
{
get { return this.nextButtonText; }
set
{
this.nextButtonText = value;
this.OnPropertyChanged();
}
}
public void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
// Raise the PropertyChanged event, passing the name of the property whose value has changed.
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
// HostViewModel.idl
namespace DataBindingInDepth
{
runtimeclass HostViewModel : Windows.UI.Xaml.Data.INotifyPropertyChanged
{
HostViewModel();
String NextButtonText;
}
}
// HostViewModel.h
// Add this field:
...
winrt::event_token PropertyChanged(Windows::UI::Xaml::Data::PropertyChangedEventHandler const& handler);
void PropertyChanged(winrt::event_token const& token) noexcept;
private:
winrt::event<Windows::UI::Xaml::Data::PropertyChangedEventHandler> m_propertyChanged;
...
// HostViewModel.cpp
// Implement like this:
...
void HostViewModel::NextButtonText(hstring const& value)
{
if (m_nextButtonText != value)
{
m_nextButtonText = value;
m_propertyChanged(*this, Windows::UI::Xaml::Data::PropertyChangedEventArgs{ L"NextButtonText" });
}
}
winrt::event_token HostViewModel::PropertyChanged(Windows::UI::Xaml::Data::PropertyChangedEventHandler const& handler)
{
return m_propertyChanged.add(handler);
}
void HostViewModel::PropertyChanged(winrt::event_token const& token) noexcept
{
m_propertyChanged.remove(token);
}
...
Nu is de eigenschap NextButtonText waarneembaar. Wanneer u een eenrichtings- of tweerichtingsbinding met die eigenschap maakt (we laten zien hoe later), wordt het resulterende bindingsobject geabonneerd op de eigenschap PropertyChanged . Wanneer deze gebeurtenis wordt gegenereerd, ontvangt de handler van het bindingsobject een argument met de naam van de eigenschap die is gewijzigd. Zo weet het bindingsobject welke waarde van de eigenschap moet worden gebruikt en opnieuw moet worden gelezen.
Zodat u het bovenstaande patroon niet meerdere keren hoeft te implementeren, kunt u, als u C# gebruikt, gewoon afleiden van de BindableBase-basisklasse die u vindt in het QuizGame-voorbeeld (in de map 'Common'). Hier volgt een voorbeeld van hoe dat eruitziet.
public class HostViewModel : BindableBase
{
private string nextButtonText;
public HostViewModel()
{
this.NextButtonText = "Next";
}
public string NextButtonText
{
get { return this.nextButtonText; }
set { this.SetProperty(ref this.nextButtonText, value); }
}
}
// Your BindableBase base class should itself derive from Windows::UI::Xaml::DependencyObject. Then, in HostViewModel.idl, derive from BindableBase instead of implementing INotifyPropertyChanged.
Opmerking
Voor C++/WinRT wordt elke runtimeklasse die u declareert in uw toepassing die is afgeleid van een basisklasse, een composable-klasse genoemd. En er zijn beperkingen rond composable klassen. Een toepassing kan alleen de Windows App Certification Kit-tests doorgeven die door Visual Studio en de Microsoft Store worden gebruikt om inzendingen te valideren (en daarom moet de toepassing worden opgenomen in de Microsoft Store), een samenstelbare klasse uiteindelijk afgeleid van een Windows-basisklasse. Dit betekent dat de klasse in de hoofdmap van de overnamehiërarchie een type moet zijn dat afkomstig is van een Windows.*-naamruimte. Als u een runtimeklasse moet afleiden van een basisklasse, bijvoorbeeld om een BindableBase-klasse te implementeren voor al uw weergavemodellen waaruit moet worden afgeleid, kunt u afleiden van Windows.UI.Xaml.DependencyObject.
Het verhogen van de eigenschapswijzigingsgebeurtenis met een argument String.Empty of null geeft aan dat alle eigenschappen van niet-indexeerfuncties in het object opnieuw moeten worden gelezen. U kunt de gebeurtenis verhogen om aan te geven dat de eigenschappen van de indexeerfunctie voor het object zijn gewijzigd met behulp van een argument 'Item[indexer]' voor specifieke indexeerfuncties (waarbij indexeerfunctie de indexwaarde is) of een waarde van 'Item[]' voor alle indexeerfuncties.
Een bindingsbron kan worden behandeld als één object waarvan de eigenschappen gegevens bevatten of als een verzameling objecten. In C# en Visual Basic-code kunt u eenmalig een binding maken met een object waarmee List(Of T) wordt geïmplementeerd om een verzameling weer te geven die niet tijdens runtime verandert. Voor een waarneembare verzameling (waarneembaar wanneer items worden toegevoegd aan en verwijderd uit de verzameling), bindt u in plaats daarvan in één richting aan ObservableCollection(Of T). In C++/CX-code kunt u verbinden met Vector<T> voor zowel waarneembare als niet-waarneembare verzamelingen, en C++/WinRT heeft zijn eigen typen. Als u verbinding wilt maken met uw eigen verzamelingsklassen, gebruikt u de richtlijnen in de volgende tabel.
| Scenario | C# en VB (CLR) | C++/WinRT | C++/CX |
|---|---|---|---|
| Binden aan een object. | Kan elk object zijn. | Kan elk object zijn. | Het object moet BindableAttribute hebben of ICustomPropertyProvider implementeren. |
| Meldingen over het wijzigen van eigenschappen van een afhankelijk object ophalen. | Object moet INotifyPropertyChanged implementeren. | Object moet INotifyPropertyChanged implementeren. | Object moet INotifyPropertyChanged implementeren. |
| Binden aan een verzameling. | List(Of T) | IVector van IInspectable of IBindableObservableVector. Zie besturingselementen voor XAML-items; bind aan een C++/WinRT-verzameling en -verzamelingen met C++/WinRT. | Vector<T> |
| Meldingen over verzamelingswijziging ontvangen van een afhankelijke verzameling. | ObservableCollection(van T) | IObservableVector van IInspectable. Bijvoorbeeld winrt::single_threaded_observable_vector<T>. | IObservableVector<T>. Vector<T> implementeert deze interface. |
| Implementeer een verzameling die ondersteuning biedt voor binding. | Breid list(of T) uit of implementeer IList, IList(of Object), IEnumerable of IEnumerable(of Object). Binding met algemene IList(of T) en IEnumerable(of T) wordt niet ondersteund. | IVector van IInspectable implementeren. Zie besturingselementen voor XAML-items; bind aan een C++/WinRT-verzameling en -verzamelingen met C++/WinRT. | Implementeer IBindableVector, IBindableIterable, IVector<Object^>, IIterable<Object^>, IVector<IInspectable*>, of IIterable<IInspectable*>. Binding met algemene IVector<T> en IIterable<T> wordt niet ondersteund. |
| Implementeer een verzameling die ondersteuning biedt voor meldingen voor het wijzigen van verzamelingen. | Breid ObservableCollection(van T) uit of implementeer (niet-algemeen) IList en INotifyCollectionChanged. | Implementeer IObservableVector van IInspectable of IBindableObservableVector. | Implementeer IBindableVector en IBindableObservableVector. |
| Implementeer een verzameling die ondersteuning biedt voor incrementeel laden. | Breid ObservableCollection(van T) uit of implementeer (niet-algemeen) IList en INotifyCollectionChanged. Implementeer daarnaast ISupportIncrementalLoading. | Implementeer IObservableVector van IInspectable of IBindableObservableVector. Daarnaast implementeert u ISupportIncrementalLoading | Implementeer IBindableVector, IBindableObservableVector en ISupportIncrementalLoading. |
U kunt lijstbesturingselementen binden aan willekeurige grote gegevensbronnen en toch hoge prestaties bereiken door incrementeel laden te gebruiken. U kunt bijvoorbeeld lijstbesturingselementen koppelen aan queryresultaten van Bing-afbeeldingen zonder dat u alle resultaten tegelijk hoeft te laden. In plaats daarvan laadt u slechts enkele resultaten onmiddellijk en laadt u indien nodig extra resultaten. Als u incrementeel laden wilt ondersteunen, moet u ISupportIncrementalLoading implementeren op een gegevensbron die ondersteuning biedt voor wijzigingsmeldingen voor verzamelingen. Wanneer de gegevensbindingsengine meer gegevens aanvraagt, moet uw gegevensbron de juiste aanvragen indienen, de resultaten integreren en vervolgens de juiste meldingen verzenden om de gebruikersinterface bij te werken.
Bindingsdoel
In de twee onderstaande voorbeelden is de eigenschap Button.Content het bindingsdoel en de waarde ervan is ingesteld op een markeringsextensie die het bindingsobject declareert. Eerst {x:Bind} wordt weergegeven en vervolgens {Binding}. Het declareren van bindingen in markeringen is het meest voorkomende geval (het is handig, leesbaar en toolable). Maar u kunt markeringen voorkomen en imperatief (programmatisch) een exemplaar van de bindingsklasse maken als dat nodig is.
<Button Content="{x:Bind ...}" ... />
<Button Content="{Binding ...}" ... />
Als u C++/WinRT- of Visual C++-onderdeelextensies (C++/CX) gebruikt, moet u het kenmerk BindableAttribute toevoegen aan elke runtimeklasse waarmee u de markeringsextensie {Binding} wilt gebruiken.
Belangrijk
Als u C++/WinRT gebruikt, is het kenmerk BindableAttribute beschikbaar als u de Windows SDK-versie 10.0.17763.0 (Windows 10, versie 1809) of hoger hebt geïnstalleerd. Zonder dat kenmerk moet u de interfaces ICustomPropertyProvider en ICustomProperty implementeren om de markeringsextensie {Binding} te kunnen gebruiken.
Bindingsobject gedeclareerd met {x:Bind}
Er is één stap die we moeten uitvoeren voordat we onze {x:Bind} -opmaak maken. We moeten onze bindingsbronklasse beschikbaar maken vanuit de klasse die onze pagina met markeringen vertegenwoordigt. We doen dit door een eigenschap (van het type HostViewModel in dit geval) toe te voegen aan onze MainPage-paginaklasse .
namespace DataBindingInDepth
{
public sealed partial class MainPage : Page
{
public MainPage()
{
this.InitializeComponent();
this.ViewModel = new HostViewModel();
}
public HostViewModel ViewModel { get; set; }
}
}
// MainPage.idl
import "HostViewModel.idl";
namespace DataBindingInDepth
{
runtimeclass MainPage : Windows.UI.Xaml.Controls.Page
{
MainPage();
HostViewModel ViewModel{ get; };
}
}
// MainPage.h
// Include a header, and add this field:
...
#include "HostViewModel.h"
...
DataBindingInDepth::HostViewModel ViewModel();
private:
DataBindingInDepth::HostViewModel m_viewModel{ nullptr };
...
// MainPage.cpp
// Implement like this:
...
MainPage::MainPage()
{
InitializeComponent();
}
DataBindingInDepth::HostViewModel MainPage::ViewModel()
{
return m_viewModel;
}
...
Dat is gebeurd, we kunnen nu de markeringen bekijken die het bindingsobject declareren. In het onderstaande voorbeeld wordt hetzelfde bindingsdoel button.content gebruikt dat we eerder hebben gebruikt in de sectie Bindingsdoel en wordt het gekoppeld aan de eigenschap HostViewModel.NextButtonText .
<!-- MainPage.xaml -->
<Page x:Class="DataBindingInDepth.Mainpage" ... >
<Button Content="{x:Bind Path=ViewModel.NextButtonText, Mode=OneWay}" ... />
</Page>
Let op de waarde die we voor Pad opgeven. Deze waarde wordt geïnterpreteerd in de context van de pagina zelf en in dit geval begint het pad door te verwijzen naar de eigenschap ViewModel die we zojuist hebben toegevoegd aan de MainPage-pagina . Deze eigenschap retourneert een HostViewModel-exemplaar , zodat we in dat object toegang kunnen krijgen tot de eigenschap HostViewModel.NextButtonText . En we geven de modus op om de standaardwaarde {x:Bind} van eenmalig te overschrijven.
De eigenschap Path ondersteunt diverse syntaxisopties voor binding met geneste eigenschappen, gekoppelde eigenschappen en indexeerfuncties voor gehele getallen en tekenreeksen. Zie de syntaxis van het eigenschapspad voor meer informatie. Binding met tekenreeksindexeerfuncties biedt u het effect van binding met dynamische eigenschappen zonder dat u ICustomPropertyProvider hoeft te implementeren. Zie de markeringsextensie {x:Bind} voor andere instellingen.
Ter illustratie dat de eigenschap HostViewModel.NextButtonText inderdaad waarneembaar is, voegt u een Click-gebeurtenishandler toe aan de knop en werkt u de waarde van HostViewModel.NextButtonText bij. Bouw, voer deze uit en klik op de knop om de waarde van de inhoudsupdate van de knop weer te geven.
// MainPage.xaml.cs
private void Button_Click(object sender, RoutedEventArgs e)
{
this.ViewModel.NextButtonText = "Updated Next button text";
}
// MainPage.cpp
void MainPage::ClickHandler(IInspectable const&, RoutedEventArgs const&)
{
ViewModel().NextButtonText(L"Updated Next button text");
}
Opmerking
Wijzigingen in Tekstvak.Text worden verzonden naar een afhankelijke bron in twee richtingen wanneer het tekstvak de focus verliest en niet na elke toetsaanslag van de gebruiker.
DataTemplate en x:DataType
In een DataTemplate (of deze nu wordt gebruikt als itemsjabloon, een inhoudssjabloon of een headersjabloon), wordt de waarde van Path niet geïnterpreteerd in de context van de pagina, maar in de context van het gegevensobject dat wordt gesjabloond. Wanneer u {x:Bind} gebruikt in een gegevenssjabloon, zodat de bindingen kunnen worden gevalideerd (en efficiënte code kan worden gegenereerd) tijdens het compileren, moet dataTemplate het type van het gegevensobject declareren met behulp van x:DataType. Het onderstaande voorbeeld kan worden gebruikt als itemtemplate van een item besturingselement dat is gebonden aan een verzameling SampleDataGroup-objecten .
<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>
Zwak getypte objecten in uw pad
Denk bijvoorbeeld aan een type met de naam SampleDataGroup, waarmee een tekenreekseigenschap met de naam Title wordt geïmplementeerd. En u hebt een eigenschap MainPage.SampleDataGroupAsObject, dat van het type object is, maar die daadwerkelijk een exemplaar van SampleDataGroup retourneert. De binding <TextBlock Text="{x:Bind SampleDataGroupAsObject.Title}"/> resulteert in een compileerfout omdat de eigenschap Titel niet is gevonden in het typeobject. De oplossing hiervoor is om een cast toe te voegen aan uw padsyntaxis als volgt: <TextBlock Text="{x:Bind ((data:SampleDataGroup)SampleDataGroupAsObject).Title}"/>. Hier volgt nog een voorbeeld waarin element wordt gedeclareerd als object, maar eigenlijk een TextBlock is: <TextBlock Text="{x:Bind Element.Text}"/>. En een cast verhelpt het probleem: <TextBlock Text="{x:Bind ((TextBlock)Element).Text}"/>.
Als uw gegevens asynchroon worden geladen
Code ter ondersteuning van {x:Bind} wordt gegenereerd tijdens het compileren in de gedeeltelijke klassen voor uw pagina's. Deze bestanden zijn te vinden in uw obj map, met namen zoals (voor C#). <view name>.g.cs De gegenereerde code bevat een handler voor de gebeurtenis Laden van uw pagina en die handler roept de methode Initialize aan op een gegenereerde klasse die de bindingen van uw pagina vertegenwoordigt.
Initialiseer op zijn beurt update om te beginnen met het verplaatsen van gegevens tussen de bindingsbron en het doel.
Laden wordt vlak voor de eerste meting van de pagina of het gebruikersbeheer gegenereerd. Dus als uw gegevens asynchroon worden geladen, zijn deze mogelijk niet gereed voor de tijd dat initialiseren wordt aangeroepen. Nadat u gegevens hebt geladen, kunt u dus afdwingen dat eenmalige bindingen worden geïnitialiseerd door aan te roepen this.Bindings.Update();. Als u slechts eenmalig bindingen nodig hebt voor asynchroon geladen gegevens, is het veel goedkoper om ze op deze manier te initialiseren dan dat het gaat om eenrichtingsbindingen en om te luisteren naar wijzigingen. Als uw gegevens geen fijnmazige wijzigingen ondergaan en als deze waarschijnlijk worden bijgewerkt als onderdeel van een specifieke actie, kunt u uw bindingen eenmalig aanbrengen en een handmatige update afdwingen met een aanroep naar Bijwerken.
Opmerking
{x:Bind} is niet geschikt voor scenario's die te laat zijn gebonden, zoals het navigeren in de woordenlijststructuur van een JSON-object of het typen van eenden. "Eenden typen" is een zwakke vorm van typen op basis van lexicale overeenkomsten op eigenschapsnamen (zoals in" als het loopt, zwemt en kwakken zoals een eend, dan is het eenden). Bij het typen van eenden zou een binding met de eigenschap Leeftijd even tevreden zijn met een Persoon of een Wijnobject (ervan uitgaande dat deze typen elk een eigenschap Leeftijd hadden). Gebruik voor deze scenario's de markeringsextensie {Binding} .
Bindingsobject dat is gedeclareerd met {Binding}
Als u C++/WinRT- of Visual C++-onderdeelextensies (C++/CX) gebruikt, moet u het kenmerk BindableAttribute toevoegen aan een runtimeklasse waaraan u een binding wilt maken. Als u {x:Bind} wilt gebruiken, hebt u dat kenmerk niet nodig.
// HostViewModel.idl
// Add this attribute:
[Windows.UI.Xaml.Data.Bindable]
runtimeclass HostViewModel : Windows.UI.Xaml.Data.INotifyPropertyChanged
...
Belangrijk
Als u C++/WinRT gebruikt, is het kenmerk BindableAttribute beschikbaar als u de Windows SDK-versie 10.0.17763.0 (Windows 10, versie 1809) of hoger hebt geïnstalleerd. Zonder dat kenmerk moet u de interfaces ICustomPropertyProvider en ICustomProperty implementeren om de markeringsextensie {Binding} te kunnen gebruiken.
In {Binding} wordt ervan uitgegaan dat u standaard verbinding maakt met de DataContext van uw opmaakpagina. Daarom stellen we de DataContext van onze pagina in op een exemplaar van onze bindingsbronklasse (van het type HostViewModel in dit geval). In het onderstaande voorbeeld ziet u de markeringen waarmee het bindingsobject wordt gede declareren. We gebruiken hetzelfde bindingsdoel button.content dat we eerder hebben gebruikt in de sectie Bindingsdoel en verbinden we met de eigenschap HostViewModel.NextButtonText .
<Page xmlns:viewmodel="using:DataBindingInDepth" ... >
<Page.DataContext>
<viewmodel:HostViewModel x:Name="viewModelInDataContext"/>
</Page.DataContext>
...
<Button Content="{Binding Path=NextButtonText}" ... />
</Page>
// MainPage.xaml.cs
private void Button_Click(object sender, RoutedEventArgs e)
{
this.viewModelInDataContext.NextButtonText = "Updated Next button text";
}
// MainPage.cpp
void MainPage::ClickHandler(IInspectable const&, RoutedEventArgs const&)
{
viewModelInDataContext().NextButtonText(L"Updated Next button text");
}
Let op de waarde die we voor Pad opgeven. Deze waarde wordt geïnterpreteerd in de context van de DataContext van de pagina, die in dit voorbeeld is ingesteld op een exemplaar van HostViewModel. Het pad verwijst naar de eigenschap HostViewModel.NextButtonText . We kunnen de modus weglaten, omdat de standaardinstelling voor {Binding} hier werkt.
De standaardwaarde van DataContext voor een UI-element is de overgenomen waarde van het bovenliggende element. U kunt deze standaardinstelling natuurlijk overschrijven door DataContext expliciet in te stellen. Deze wordt standaard overgenomen door onderliggende elementen. Het expliciet instellen van DataContext op een element is handig als u meerdere bindingen wilt hebben die dezelfde bron gebruiken.
Een bindingsobject heeft een broneigenschap , die standaard wordt ingesteld op de DataContext van het UI-element waarop de binding wordt gedeclareerd. U kunt deze standaardinstelling overschrijven door Bron, RelativeSource of ElementName expliciet in te stellen op de binding (zie {Binding} voor meer informatie).
In een DataTemplate wordt DataContext automatisch ingesteld op het gegevensobject dat wordt gesjabloond. Het onderstaande voorbeeld kan worden gebruikt als itemTemplate van een item besturingselement dat is gebonden aan een verzameling van elk type met tekenreekseigenschappen met de naam Titel en Beschrijving.
<DataTemplate x:Key="SimpleItemTemplate">
<StackPanel Orientation="Vertical" Height="50">
<TextBlock Text="{Binding Title}"/>
<TextBlock Text="{Binding Description"/>
</StackPanel>
</DataTemplate>
Opmerking
Standaard worden wijzigingen in TextBox.Text verzonden naar een afhankelijke bron in twee richtingen wanneer het tekstvak de focus verliest. Als u wilt dat wijzigingen na elke toetsaanslag van de gebruiker worden verzonden, stelt u UpdateSourceTrigger in op PropertyChanged op de binding in markeringen. U kunt ook de controle over wanneer wijzigingen naar de bron worden verzonden, volledig overnemen door UpdateSourceTrigger in te stellen op Expliciet. Vervolgens verwerkt u gebeurtenissen in het tekstvak (meestal TextBox.TextChanged), roept u GetBindingExpression aan op het doel om een BindingExpression-object op te halen en roept u tot slot BindingExpression.UpdateSource aan om de gegevensbron programmatisch bij te werken.
De eigenschap Path ondersteunt diverse syntaxisopties voor binding met geneste eigenschappen, gekoppelde eigenschappen en indexeerfuncties voor gehele getallen en tekenreeksen. Zie de syntaxis van het eigenschapspad voor meer informatie. Binding met tekenreeksindexeerfuncties biedt u het effect van binding met dynamische eigenschappen zonder dat u ICustomPropertyProvider hoeft te implementeren. De eigenschap ElementName is handig voor element-naar-elementbinding. De eigenschap RelativeSource heeft verschillende toepassingen, waarvan een een krachtiger alternatief is voor sjabloonbinding in een ControlTemplate. Zie de markeringsextensie {Binding} en de bindingsklasse voor andere instellingen.
Wat gebeurt er als de bron en het doel niet hetzelfde type zijn?
Als u de zichtbaarheid van een UI-element wilt beheren op basis van de waarde van een booleaanse eigenschap of als u een UI-element wilt weergeven met een kleur die een functie is van het bereik of de trend van een numerieke waarde, of als u een datum- en/of tijdwaarde wilt weergeven in een ui-elementeigenschap die een tekenreeks verwacht, vervolgens moet u waarden van het ene type naar het andere converteren. Er zijn gevallen waarin de juiste oplossing een andere eigenschap van het juiste type beschikbaar maakt vanuit uw bindingsbronklasse en de conversielogica daar ingekapseld en testbaar houdt. Maar dat is niet flexibel of schaalbaar wanneer u grote getallen of grote combinaties van bron- en doeleigenschappen hebt. In dat geval hebt u een aantal opties:
- Als u {x:Bind} gebruikt, kunt u rechtstreeks verbinding maken met een functie om die conversie uit te voeren
- U kunt ook een waardeconversieprogramma opgeven dat is ontworpen om de conversie uit te voeren
Waardeconversieprogramma's
Hier volgt een waardeconversieprogramma dat geschikt is voor een eenmalige binding of een eenrichtingsbinding waarmee een Datum/tijd-waarde wordt geconverteerd naar een tekenreekswaarde die de maand bevat. De klasse implementeert IValueConverter.
public class DateToStringConverter : IValueConverter
{
// Define the Convert method to convert a DateTime value to
// a month string.
public object Convert(object value, Type targetType,
object parameter, string language)
{
// value is the data from the source object.
DateTime thisdate = (DateTime)value;
int monthnum = thisdate.Month;
string month;
switch (monthnum)
{
case 1:
month = "January";
break;
case 2:
month = "February";
break;
default:
month = "Month not found";
break;
}
// Return the value to pass to the target.
return month;
}
// ConvertBack is not implemented for a OneWay binding.
public object ConvertBack(object value, Type targetType,
object parameter, string language)
{
throw new NotImplementedException();
}
}
// See the "Formatting or converting data values for display" section in the "Data binding overview" topic.
En hier ziet u hoe u dat waardeconversieprogramma gebruikt in de markeringen voor bindingsobjecten.
<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}}"/>
De bindingsengine roept de methoden Convert en ConvertBack aan als de parameter Converter is gedefinieerd voor de binding. Wanneer gegevens uit de bron worden doorgegeven, roept de bindingsengine Converteren aan en geeft de geretourneerde gegevens door aan het doel. Wanneer gegevens worden doorgegeven vanuit het doel (voor een binding in twee richtingen), roept de bindingsengine ConvertBack aan en worden de geretourneerde gegevens doorgegeven aan de bron.
Het conversieprogramma heeft ook optionele parameters: ConverterLanguage, waarmee u de taal kunt opgeven die moet worden gebruikt in de conversie en ConverterParameter, waarmee een parameter voor de conversielogica kan worden doorgegeven. Zie IValue Converter voor een voorbeeld dat gebruikmaakt van een conversieparameter.
Opmerking
Als er een fout optreedt in de conversie, kunt u geen uitzondering genereren. Retourneer in plaats daarvan DependencyProperty.UnsetValue, waardoor de gegevensoverdracht wordt gestopt.
Als u een standaardwaarde wilt weergeven die moet worden gebruikt wanneer de bindingsbron niet kan worden omgezet, stelt u de eigenschap FallbackValue in op het bindingsobject in markeringen. Dit is handig voor het afhandelen van conversie- en opmaakfouten. Het is ook handig om verbinding te maken met broneigenschappen die mogelijk niet bestaan op alle objecten in een gebonden verzameling heterogene typen.
Als u een tekst besturingselement koppelt aan een waarde die geen tekenreeks is, converteert de gegevensbindingsengine de waarde naar een tekenreeks. Als de waarde een verwijzingstype is, haalt de engine voor gegevensbinding de tekenreekswaarde op door ICustomPropertyProvider.GetStringRepresentation of IStringable.ToString aan te roepen, indien beschikbaar, en wordt object.ToString anders aangeroepen. Houd er echter rekening mee dat de bindingsengine elke ToString-implementatie negeert die de basisklasse-implementatie verbergt. Implementaties van subklassen moeten in plaats daarvan de ToString-methode van de basisklasse overschrijven. Op dezelfde manier lijken alle beheerde objecten in systeemeigen talen ICustomPropertyProvider en IStringable te implementeren. Alle aanroepen naar GetStringRepresentation en IStringable.ToString worden echter gerouteerd naar Object.ToString of een onderdrukking van die methode, en nooit naar een nieuwe ToString-implementatie die de basisklasse-implementatie verbergt.
Opmerking
Vanaf Windows 10 versie 1607 biedt het XAML-framework een ingebouwde booleaanse conversieprogramma voor zichtbaarheid. Het conversieprogramma wordt toegewezen aan de Visible-opsommingswaarde en onwaar voor Samengevouwen , zodat u een eigenschap Zichtbaarheid kunt binden aan een Booleaanse waarde zonder een conversieprogramma te maken. Als u het ingebouwde conversieprogramma wilt gebruiken, moet de minimale doel-SDK-versie van uw app 14393 of hoger zijn. U kunt deze niet gebruiken wanneer uw app is gericht op eerdere versies van Windows 10. Zie Adaptieve versiecode voor meer informatie over doelversies.
Functiebinding in {x:Bind}
Met {x:Bind} kan de laatste stap in een bindingspad een functie zijn. Dit kan worden gebruikt om conversies uit te voeren en bindingen uit te voeren die afhankelijk zijn van meer dan één eigenschap. Zie Functies in x:Bind
Element-naar-element-binding
U kunt de eigenschap van één XAML-element binden aan de eigenschap van een ander XAML-element. Hier volgt een voorbeeld van hoe dit eruitziet in markeringen.
<TextBox x:Name="myTextBox" />
<TextBlock Text="{x:Bind myTextBox.Text, Mode=OneWay}" />
Belangrijk
Zie Element-naar-element-binding voor de benodigde werkstroom voor element-naar-elementbinding met behulp van C++/WinRT.
Resourcewoordenlijsten met {x:Bind}
De markeringsextensie {x:Bind} is afhankelijk van het genereren van code, dus er moet een code-behind-bestand met een constructor worden aanroepen die InitializeComponent aanroept (om de gegenereerde code te initialiseren). U gebruikt de resourcewoordenlijst opnieuw door het type te instantiëren (zodat InitializeComponent wordt aangeroepen) in plaats van te verwijzen naar de bestandsnaam. Hier volgt een voorbeeld van wat u moet doen als u een bestaande resourcewoordenlijst hebt en u er {x:Bind} in wilt gebruiken.
TemplatesResourceDictionary.xaml
<ResourceDictionary
x:Class="ExampleNamespace.TemplatesResourceDictionary"
.....
xmlns:examplenamespace="using:ExampleNamespace">
<DataTemplate x:Key="EmployeeTemplate" x:DataType="examplenamespace:IEmployee">
<Grid>
<TextBlock Text="{x:Bind Name}"/>
</Grid>
</DataTemplate>
</ResourceDictionary>
TemplatesResourceDictionary.xaml.cs
using Windows.UI.Xaml.Data;
namespace ExampleNamespace
{
public partial class TemplatesResourceDictionary
{
public TemplatesResourceDictionary()
{
InitializeComponent();
}
}
}
MainPage.xaml
<Page x:Class="ExampleNamespace.MainPage"
....
xmlns:examplenamespace="using:ExampleNamespace">
<Page.Resources>
<ResourceDictionary>
....
<ResourceDictionary.MergedDictionaries>
<examplenamespace:TemplatesResourceDictionary/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Page.Resources>
</Page>
{x:Bind} en {Binding} combineren in een herbruikbare stijl
Terwijl in het vorige voorbeeld {x:Bind} in DataTemplates werd getoond, kunt u ook herbruikbare stijlen maken die zowel {x:Bind} als {Binding}-opmaakextensies combineren. Dit is handig als u bepaalde eigenschappen wilt binden om bekende waarden te compileren met behulp van {x:Bind} en andere eigenschappen voor runtime DataContext-waarden met behulp van {Binding}.
Hier volgt een voorbeeld waarin wordt getoond hoe u een herbruikbare knopstijl maakt die gebruikmaakt van beide bindingsmethoden:
TemplatesResourceDictionary.xaml
<ResourceDictionary
x:Class="ExampleNamespace.TemplatesResourceDictionary"
.....
xmlns:examplenamespace="using:ExampleNamespace">
<!-- DataTemplate using x:Bind -->
<DataTemplate x:Key="EmployeeTemplate" x:DataType="examplenamespace:IEmployee">
<Grid>
<TextBlock Text="{x:Bind Name}"/>
</Grid>
</DataTemplate>
<!-- Style that mixes x:Bind and Binding -->
<Style x:Key="CustomButtonStyle" TargetType="Button">
<Setter Property="Background" Value="{Binding ButtonBackgroundBrush}"/>
<Setter Property="Foreground" Value="{Binding ButtonForegroundBrush}"/>
<Setter Property="FontSize" Value="16"/>
<Setter Property="Margin" Value="4"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Border x:Name="RootBorder"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
CornerRadius="4">
<StackPanel Orientation="Horizontal"
HorizontalAlignment="Center"
VerticalAlignment="Center">
<!-- x:Bind to a static property or page-level property -->
<Ellipse Width="8" Height="8"
Fill="{x:Bind DefaultIndicatorBrush}"
Margin="0,0,8,0"/>
<!-- Binding to DataContext -->
<ContentPresenter x:Name="ContentPresenter"
Content="{TemplateBinding Content}"
Foreground="{TemplateBinding Foreground}"
FontSize="{TemplateBinding FontSize}"/>
</StackPanel>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal"/>
<VisualState x:Name="PointerOver">
<VisualState.Setters>
<!-- Binding to DataContext for hover color -->
<Setter Target="RootBorder.Background"
Value="{Binding ButtonHoverBrush}"/>
</VisualState.Setters>
</VisualState>
<VisualState x:Name="Pressed">
<VisualState.Setters>
<!-- x:Bind to a compile-time known resource -->
<Setter Target="RootBorder.Background"
Value="{x:Bind DefaultPressedBrush}"/>
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
TemplatesResourceDictionary.xaml.cs
using Windows.UI;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Media;
namespace ExampleNamespace
{
public partial class TemplatesResourceDictionary
{
public TemplatesResourceDictionary()
{
InitializeComponent();
}
// Properties for x:Bind - these are compile-time bound
public SolidColorBrush DefaultIndicatorBrush { get; } =
new SolidColorBrush(Colors.Green);
public SolidColorBrush DefaultPressedBrush { get; } =
new SolidColorBrush(Colors.DarkGray);
}
}
Gebruik in MainPage.xaml met een ViewModel dat runtime-waarden biedt:
<Page x:Class="ExampleNamespace.MainPage"
....
xmlns:examplenamespace="using:ExampleNamespace">
<Page.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<examplenamespace:TemplatesResourceDictionary/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Page.Resources>
<Page.DataContext>
<examplenamespace:ButtonThemeViewModel/>
</Page.DataContext>
<StackPanel Margin="20">
<!-- This button uses the mixed binding style -->
<Button Content="Save" Style="{StaticResource CustomButtonStyle}"/>
<Button Content="Cancel" Style="{StaticResource CustomButtonStyle}"/>
</StackPanel>
</Page>
ButtonThemeViewModel.cs (de DataContext die runtimebindingswaarden biedt):
using System.ComponentModel;
using Windows.UI;
using Windows.UI.Xaml.Media;
namespace ExampleNamespace
{
public class ButtonThemeViewModel : INotifyPropertyChanged
{
private SolidColorBrush _buttonBackgroundBrush = new SolidColorBrush(Colors.LightBlue);
private SolidColorBrush _buttonForegroundBrush = new SolidColorBrush(Colors.DarkBlue);
private SolidColorBrush _buttonHoverBrush = new SolidColorBrush(Colors.LightCyan);
public SolidColorBrush ButtonBackgroundBrush
{
get => _buttonBackgroundBrush;
set
{
_buttonBackgroundBrush = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(ButtonBackgroundBrush)));
}
}
public SolidColorBrush ButtonForegroundBrush
{
get => _buttonForegroundBrush;
set
{
_buttonForegroundBrush = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(ButtonForegroundBrush)));
}
}
public SolidColorBrush ButtonHoverBrush
{
get => _buttonHoverBrush;
set
{
_buttonHoverBrush = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(ButtonHoverBrush)));
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
}
In dit voorbeeld:
- {Binding} wordt gebruikt voor eigenschappen die afhankelijk zijn van de DataContext (ButtonBackgroundBrush, ButtonForegroundBrush, ButtonHoverBrush)
- {x:Bind} wordt gebruikt voor eigenschappen die bekend zijn over de gecompileerde tijd en behoren tot de ResourceDictionary zelf (DefaultIndicatorBrush, DefaultPressedBrush)
- De stijl is herbruikbaar en kan worden toegepast op elke knop
- Runtimethema's zijn mogelijk via DataContext terwijl ze nog steeds profiteren van de prestaties van {x:Bind} voor statische elementen
Gebeurtenisbinding en ICommand
{x:Bind} ondersteunt een functie met de naam gebeurtenisbinding. Met deze functie kunt u de handler voor een gebeurtenis opgeven met behulp van een binding. Dit is een extra optie voor het afhandelen van gebeurtenissen met een methode in het bestand code-behind. Stel dat u een RootFrame-eigenschap hebt in uw MainPage-klasse .
public sealed partial class MainPage : Page
{
...
public Frame RootFrame { get { return Window.Current.Content as Frame; } }
}
Vervolgens kunt u de klikgebeurtenis van een knop binden aan een methode in het Frame-object dat wordt geretourneerd door de eigenschap RootFrame, zoals deze. Houd er rekening mee dat we ook de eigenschap IsEnabled van de knop binden aan een ander lid van hetzelfde frame.
<AppBarButton Icon="Forward" IsCompact="True"
IsEnabled="{x:Bind RootFrame.CanGoForward, Mode=OneWay}"
Click="{x:Bind RootFrame.GoForward}"/>
Overbelaste methoden kunnen niet worden gebruikt voor het afhandelen van een gebeurtenis met deze techniek. Als de methode voor de verwerking van de gebeurtenis parameters heeft, moeten ze allemaal worden toegewezen op basis van de typen parameters van de gebeurtenis. In dit geval is Frame.GoForward niet overbelast en heeft het geen parameters (maar het zou nog steeds geldig zijn, zelfs als er twee objectparameters nodig waren). Frame.GoBack is echter overbelast, dus we kunnen die methode niet gebruiken met deze techniek.
De gebeurtenisbindingstechniek is vergelijkbaar met het implementeren en gebruiken van opdrachten (een opdracht is een eigenschap die een object retourneert dat de ICommand-interface implementeert). Zowel {x:Bind} als {Binding} werken met opdrachten. Zodat u het opdrachtpatroon niet meerdere keren hoeft te implementeren, kunt u de helperklasse DelegateCommand gebruiken die u vindt in het quizgame-voorbeeld (in de map 'Algemeen').
Binden aan een verzameling mappen of bestanden
U kunt de API's in de Windows.Storage-naamruimte gebruiken om map- en bestandsgegevens op te halen. De verschillende methoden GetFilesAsync, GetFoldersAsync en GetItemsAsync retourneren echter geen waarden die geschikt zijn voor binding met lijstbesturingselementen. In plaats daarvan moet u verbinden met de retourwaarden van de methoden GetVirtualizedFilesVector, GetVirtualizedFoldersVector en GetVirtualizedItemsVector van de klasse FileInformationFactory . In het volgende codevoorbeeld uit storageDataSource en GetVirtualizedFilesVector ziet u het typische gebruikspatroon. Vergeet niet om de mogelijkheid picturesLibrary in uw app-pakketmanifest te declareren en te bevestigen dat er afbeeldingen in de map Afbeeldingenbibliotheek staan.
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;
}
Doorgaans gebruikt u deze methode om een alleen-lezenweergave van bestands- en mapgegevens te maken. U kunt tweerichtingsbindingen maken met de eigenschappen van het bestand en de map, bijvoorbeeld om gebruikers een nummer te laten beoordelen in een muziekweergave. Wijzigingen worden echter pas behouden als u de juiste SavePropertiesAsync-methode aanroept (bijvoorbeeld MusicProperties.SavePropertiesAsync). U moet wijzigingen doorvoeren wanneer het item de focus verliest, omdat hierdoor een selectie opnieuw wordt ingesteld.
Houd er rekening mee dat binding in twee richtingen met deze techniek alleen werkt met geïndexeerde locaties, zoals Muziek. U kunt bepalen of een locatie wordt geïndexeerd door de methode FolderInformation.GetIndexedStateAsync aan te roepen.
Houd er ook rekening mee dat een gevirtualiseerde vector null kan retourneren voor sommige items voordat deze de waarde vult. U moet bijvoorbeeld controleren op null voordat u de selectedItem-waarde van een lijst besturingselement gebruikt dat is gebonden aan een gevirtualiseerde vector, of in plaats daarvan SelectedIndex gebruiken.
Binding met gegevens gegroepeerd op een sleutel
Als u een platte verzameling items maakt (bijvoorbeeld boeken, vertegenwoordigd door een BookSku-klasse ) en u de items groepeert met behulp van een gemeenschappelijke eigenschap als een sleutel (bijvoorbeeld de eigenschap BookSku.AuthorName ), wordt het resultaat gegroepeerde gegevens genoemd. Wanneer u gegevens groepeert, is het geen platte verzameling meer. Gegroepeerde gegevens zijn een verzameling groepsobjecten, waarbij elk groepsobject elk groepsobject heeft
- een sleutel, en
- een verzameling items waarvan de eigenschap overeenkomt met die sleutel.
Als u het voorbeeld van de boeken opnieuw wilt gebruiken, resulteert het resultaat van het groeperen van de boeken op auteurnaam in een verzameling auteursnaamgroepen met elke groep
- een sleutel, een naam van de auteur en
- een verzameling van de BookSku'swaarvan de eigenschap AuthorName overeenkomt met de sleutel van de groep.
Over het algemeen koppelt u de itemsbron van een itembesturingselement (zoals ListView of GridView) rechtstreeks aan een eigenschap die een verzameling retourneert. Als dat een platte verzameling items is, hoeft u niets speciaals te doen. Maar als het een verzameling groepsobjecten is (net als bij het binden aan gegroepeerde gegevens), hebt u de services van een tussenliggend object, een CollectionViewSource genaamd, nodig die zich tussen het itembesturingselement en de bindingsbron bevindt. U verbindt de CollectionViewSource aan de eigenschap die gegroepeerde gegevens retourneert en u verbindt het itembesturingselement met de CollectionViewSource. Een extra toegevoegde waarde van een CollectionViewSource is dat het huidige item wordt bijgehouden, zodat u meer dan één item gesynchroniseerd kunt houden door ze allemaal aan dezelfde CollectionViewSource te koppelen. U kunt het huidige item ook programmatisch openen via de eigenschap ICollectionView.CurrentItem van het object dat wordt geretourneerd door de eigenschap CollectionViewSource.View .
Als u de groeperingsfaciliteit van een CollectionViewSource wilt activeren, stelt u IsSourceGrouped in op true. Of u ook de eigenschap ItemsPath moet instellen, is afhankelijk van de manier waarop u uw groepsobjecten ontwerpt. Er zijn twee manieren om een groepsobject te ontwerpen: het patroon 'is-a-group' en het patroon 'has-a-group'. In het patroon 'is-a-group' is het groepsobject afgeleid van een verzamelingstype (bijvoorbeeld Lijst<T>), zodat het groepsobject eigenlijk de groep items is. Met dit patroon hoeft u ItemsPath niet in te stellen. In het patroon 'has-a-group' heeft het groepsobject een of meer eigenschappen van een verzamelingstype (zoals Lijst<T>), zodat de groep 'heeft een' groep items in de vorm van een eigenschap (of meerdere groepen items in de vorm van verschillende eigenschappen). Met dit patroon moet u ItemsPath instellen op de naam van de eigenschap die de groep items bevat.
In het onderstaande voorbeeld ziet u het patroon 'has-a-group'. De paginaklasse heeft een eigenschap met de naam ViewModel, die een exemplaar van ons weergavemodel retourneert. CollectionViewSource wordt gekoppeld aan de eigenschap Auteurs van het weergavemodel (Auteurs is de verzameling groepsobjecten) en geeft ook aan dat het de eigenschap Author.BookSkus is die de gegroepeerde items bevat. Ten slotte is de GridView gebonden aan de CollectionViewSource en is de groepsstijl gedefinieerd, zodat de items in groepen kunnen worden weergegeven.
<Page.Resources>
<CollectionViewSource
x:Name="AuthorHasACollectionOfBookSku"
Source="{x:Bind ViewModel.Authors}"
IsSourceGrouped="true"
ItemsPath="BookSkus"/>
</Page.Resources>
...
<GridView
ItemsSource="{x:Bind AuthorHasACollectionOfBookSku}" ...>
<GridView.GroupStyle>
<GroupStyle
HeaderTemplate="{StaticResource AuthorGroupHeaderTemplateWide}" ... />
</GridView.GroupStyle>
</GridView>
U kunt het patroon 'is-a-group' op twee manieren implementeren. Een manier is om uw eigen groepsklasse te maken. De klasse afleiden van Lijst<T> (waarbij T het type van de items is). Bijvoorbeeld: public class Author : List<BookSku>. De tweede manier is om een LINQ-expressie te gebruiken om dynamisch groepsobjecten (en een groepsklasse) te maken op basis van eigenschappenwaarden van de BookSku-items . Deze aanpak, die alleen een platte lijst met items onderhoudt en ze tegelijk groepeert, is typisch een app die toegang heeft tot gegevens van een cloudservice. U krijgt de flexibiliteit om boeken te groeperen op auteur of genre (bijvoorbeeld) zonder speciale groepsklassen zoals Auteur en Genre nodig te hebben.
In het onderstaande voorbeeld ziet u het patroon is-a-group met behulp van LINQ. Deze keer groeperen we boeken op genre, weergegeven met de genrenaam in de groepskoppen. Dit wordt aangegeven door het eigenschapspad 'Sleutel' in verwijzing naar de waarde van de groepssleutel .
using System.Linq;
...
private IOrderedEnumerable<IGrouping<string, BookSku>> genres;
public IOrderedEnumerable<IGrouping<string, BookSku>> Genres
{
get
{
if (this.genres == null)
{
this.genres = from book in this.bookSkus
group book by book.genre into grp
orderby grp.Key
select grp;
}
return this.genres;
}
}
Houd er rekening mee dat wanneer u {x:Bind} met gegevenssjablonen gebruikt, het type moet worden aangegeven door een x:DataType-waarde in te stellen. Als het type algemeen is, kunnen we dat niet in markeringen uitdrukken, dus moeten we in plaats daarvan {Binding} gebruiken in de koptekstsjabloon voor groepsstijlen.
<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>
Een SemanticZoom-besturingselement is een uitstekende manier voor uw gebruikers om gegroepeerde gegevens weer te geven en te navigeren. In de voorbeeld-app Bookstore2 ziet u hoe u de SemanticZoom gebruikt. In die app kunt u een lijst met boeken weergeven die zijn gegroepeerd op auteur (de ingezoomde weergave) of kunt u uitzoomen om een jumplist met auteurs te zien (de uitgezoomde weergave). De jumplist biedt veel snellere navigatie dan door de lijst met boeken bladeren. De ingezoomde en uitgezoomde weergaven zijn eigenlijk ListView - of GridView-besturingselementen die afhankelijk zijn van dezelfde CollectionViewSource.
Wanneer u verbinding wilt maken met hiërarchische gegevens, zoals subcategorieën binnen categorieën, kunt u ervoor kiezen om de hiërarchische niveaus in uw gebruikersinterface weer te geven met een reeks itemsbesturingselementen. Een selectie in een itembesturingselement bepaalt de inhoud van de volgende items. U kunt de lijsten gesynchroniseerd houden door elke lijst te binden aan een eigen CollectionViewSource en de CollectionViewSource-exemplaren in een keten te binden. Dit wordt een hoofd-/detailweergave (of lijst/details) genoemd. Zie How to bind to hierarchical data and create a master/details view voor meer informatie.
Problemen met gegevensbinding vaststellen en fouten opsporen
De bindingsmarkeringen bevatten de namen van eigenschappen (en, voor C#, soms velden en methoden). Dus wanneer u de naam van een eigenschap wijzigt, moet u ook elke binding wijzigen die ernaar verwijst. Vergeet dit te doen, leidt tot een typisch voorbeeld van een gegevensbindingsfout en uw app compileert niet of wordt niet correct uitgevoerd.
De bindingsobjecten die zijn gemaakt door {x:Bind} en {Binding} zijn grotendeels functioneel equivalent. Maar {x:Bind} heeft typegegevens voor de bindingsbron en genereert broncode tijdens het compileren. Met {x:Bind} krijgt u dezelfde soort probleemdetectie die u krijgt met de rest van uw code. Dit omvat compileertijdvalidatie van uw bindingsexpressies en foutopsporing door onderbrekingspunten in te stellen in de broncode die is gegenereerd als de gedeeltelijke klasse voor uw pagina. Deze klassen zijn te vinden in de bestanden in uw obj map, met namen zoals (voor C#) <view name>.g.cs). Als u een probleem hebt met een binding, schakelt u Break Onhandled Exceptions in het foutopsporingsprogramma van Microsoft Visual Studio in. Het foutopsporingsprogramma onderbreekt de uitvoering op dat moment en u kunt vervolgens fouten opsporen in wat er mis is gegaan. De code die door {x:Bind} wordt gegenereerd, volgt hetzelfde patroon voor elk deel van de grafiek van bindingsbronknooppunten en u kunt de informatie in het venster Oproepstack gebruiken om de volgorde van aanroepen te bepalen die tot het probleem hebben geleid.
{Binding} heeft geen typegegevens voor de bindingsbron. Maar wanneer u uw app uitvoert waaraan het foutopsporingsprogramma is gekoppeld, worden eventuele bindingsfouten weergegeven in het uitvoervenster in Visual Studio.
Bindingen maken in code
Notitie Deze sectie is alleen van toepassing op {Binding}, omdat u geen {x:Bind} -bindingen in code kunt maken. Sommige van dezelfde voordelen van {x:Bind} kunnen echter worden bereikt met DependencyObject.RegisterPropertyChangedCallback, waarmee u zich kunt registreren voor wijzigingsmeldingen voor elke afhankelijkheidseigenschap.
U kunt ui-elementen ook verbinden met gegevens met behulp van procedurele code in plaats van XAML. Hiervoor maakt u een nieuw bindingsobject , stelt u de juiste eigenschappen in en roept u FrameworkElement.SetBinding of BindingOperations.SetBinding aan. Het programmatisch maken van bindingen is handig als u de waarden van de bindingseigenschap tijdens runtime wilt kiezen of één binding wilt delen tussen meerdere besturingselementen. Houd er echter rekening mee dat u de bindingeigenschapswaarden niet kunt wijzigen nadat u SetBinding hebt aangeroepen.
In het volgende voorbeeld ziet u hoe u een binding in code implementeert.
<TextBox x:Name="MyTextBox" Text="Text"/>
// Create an instance of the MyColors class
// that implements INotifyPropertyChanged.
MyColors textcolor = new MyColors();
// Brush1 is set to be a SolidColorBrush with the value Red.
textcolor.Brush1 = new SolidColorBrush(Colors.Red);
// Set the DataContext of the TextBox MyTextBox.
MyTextBox.DataContext = textcolor;
// Create the binding and associate it with the text box.
Binding binding = new Binding() { Path = new PropertyPath("Brush1") };
MyTextBox.SetBinding(TextBox.ForegroundProperty, binding);
' Create an instance of the MyColors class
' that implements INotifyPropertyChanged.
Dim textcolor As New MyColors()
' Brush1 is set to be a SolidColorBrush with the value Red.
textcolor.Brush1 = New SolidColorBrush(Colors.Red)
' Set the DataContext of the TextBox MyTextBox.
MyTextBox.DataContext = textcolor
' Create the binding and associate it with the text box.
Dim binding As New Binding() With {.Path = New PropertyPath("Brush1")}
MyTextBox.SetBinding(TextBox.ForegroundProperty, binding)
{x:Bind} en {Binding} functievergelijking
| Eigenschap | {x:Bind} versus {Binding} | Opmerkingen |
|---|---|---|
| Pad is de standaardeigenschap | {x:Bind a.b.c}- {Binding a.b.c} |
|
| Padeigenschap | {x:Bind Path=a.b.c}- {Binding Path=a.b.c} |
In x:Bind is het pad standaard geroot op de pagina, niet de DataContext. |
| Indexer | {x:Bind Groups[2].Title}- {Binding Groups[2].Title} |
Bindt aan het opgegeven item in de verzameling. Alleen indexen op basis van gehele getallen worden ondersteund. |
| Gekoppelde eigenschappen | {x:Bind Button22.(Grid.Row)}- {Binding Button22.(Grid.Row)} |
Gekoppelde eigenschappen worden opgegeven met haakjes. Als de eigenschap niet is gedeclareerd in een XAML-naamruimte, moet u deze vooraf laten gaan door een XML-naamruimte, die moet worden toegewezen aan een codenaamruimte aan het hoofd van het document. |
| Gietsel | {x:Bind groups[0].(data:SampleDataGroup.Title)}- Niet nodig voor {Binding}. |
Casts worden opgegeven met haakjes. Als de eigenschap niet is gedeclareerd in een XAML-naamruimte, moet u deze vooraf laten gaan door een XML-naamruimte, die moet worden toegewezen aan een codenaamruimte aan het hoofd van het document. |
| Conversieprogramma | {x:Bind IsShown, Converter={StaticResource BoolToVisibility}}- {Binding IsShown, Converter={StaticResource BoolToVisibility}} |
Conversieprogramma's moeten worden gedeclareerd in de hoofdmap van de Pagina/ResourceDictionary of 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} |
Conversieprogramma's moeten worden gedeclareerd in de hoofdmap van de Pagina/ResourceDictionary of in App.xaml. |
| TargetNullValue | {x:Bind Name, TargetNullValue=0}- {Binding Name, TargetNullValue=0} |
Wordt gebruikt wanneer het blad van de bindingsexpressie null is. Gebruik enkele aanhalingstekens voor een tekenreekswaarde. |
| FallbackValue | {x:Bind Name, FallbackValue='empty'}- {Binding Name, FallbackValue='empty'} |
Wordt gebruikt wanneer een deel van het pad voor de binding (met uitzondering van het blad) null is. |
| ElementName | {x:Bind slider1.Value}- {Binding Value, ElementName=slider1} |
Met {x:Bind} verbindt u een veld; Het pad is standaard geroot op de pagina, zodat elk benoemd element kan worden geopend via het bijbehorende veld. |
| Relatieve bron: zelf | <Rectangle x:Name="rect1" Width="200" Height="{x:Bind rect1.Width}" ... />- <Rectangle Width="200" Height="{Binding Width, RelativeSource={RelativeSource Self}}" ... /> |
Geef bij {x:Bind} de naam van het element en gebruik de naam ervan in Path. |
| RelativeSource: TemplatedParent | Niet nodig voor {x:Bind} - {Binding <path>, RelativeSource={RelativeSource TemplatedParent}} |
Met {x:Bind} TargetType op ControlTemplate wordt de binding aan de bovenliggende sjabloon aangegeven. Voor {Binding} kan reguliere sjabloonbinding worden gebruikt in besturingssjablonen voor de meeste gebruik. Maar gebruik TemplatedParent waar u een conversieprogramma of een binding in twee richtingen moet gebruiken.< |
| Bron | Niet nodig voor {x:Bind} - <ListView ItemsSource="{Binding Orders, Source={StaticResource MyData}}"/> |
Voor {x:Bind} kunt u het benoemde element rechtstreeks gebruiken, een eigenschap of een statisch pad gebruiken. |
| Wijze | {x:Bind Name, Mode=OneWay}- {Binding Name, Mode=TwoWay} |
De modus kan OneTime, OneWay of TwoWay zijn. {x:Bind} wordt standaard ingesteld op OneTime; {Binding} wordt standaard ingesteld op OneWay. |
| UpdateSourceTrigger | {x:Bind Name, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}- {Binding UpdateSourceTrigger=PropertyChanged} |
UpdateSourceTrigger kan Standaard, LostFocus of PropertyChanged zijn. {x:Bind} biedt geen ondersteuning voor UpdateSourceTrigger=Explicit. {x:Bind} maakt gebruik van PropertyChanged-gedrag voor alle gevallen behalve TextBox.Text, waarbij het lostFocus-gedrag gebruikt. |