Databindning utförligt

Viktiga API:er

Anmärkning

I det här avsnittet beskrivs databindningsfunktioner i detalj. En kort, praktisk introduktion finns i Översikt över databindning.

Det här avsnittet handlar om databindning för API:er som finns i namnområdet Windows.UI.Xaml.Data.

Databindning är ett sätt för appens användargränssnitt att visa data och, om så önskas, förbli synkroniserad med dessa data. Med databindning kan du skilja data från användargränssnittets problem, vilket resulterar i en enklare konceptuell modell samt bättre läsbarhet, testbarhet och underhåll av din app.

Du kan använda databindning för att helt enkelt visa värden från en datakälla när användargränssnittet först visas, men inte svara på ändringar i dessa värden. Det här är ett bindningsläge som kallas en gång, och det fungerar bra för ett värde som inte ändras under körningen. Du kan också välja att "observera" värdena och uppdatera användargränssnittet när de ändras. Det här läget kallas enkelriktat och fungerar bra för skrivskyddade data. I slutändan kan du välja att både observera och uppdatera, så att ändringar som användaren gör i värden i användargränssnittet automatiskt skickas tillbaka till datakällan. Det här läget kallas dubbelriktat och fungerar bra för skrivskyddade data. Här följer några exempel.

  • Du kan använda engångsläget för att binda en bild till den aktuella användarens foto.
  • Du kan använda enkelriktat läge för att binda en ListView till en samling nyhetsartiklar i realtid grupperade efter tidningsavsnitt.
  • Du kan använda tvåvägsläget för att binda en textruta till en kunds namn i ett formulär.

Oberoende av läget finns det två typer av bindningar och båda deklareras vanligtvis i UI-markering. Du kan välja att använda markeringstillägget {x:Bind} eller {Binding}-markeringstillägget. Och du kan till och med använda en blandning av de två i samma app, även i samma användargränssnittselement. {x:Bind} är nytt för Windows 10 och har bättre prestanda. All information som beskrivs i det här avsnittet gäller för båda typerna av bindningar om vi inte uttryckligen säger något annat.

Exempelappar som demonstrerar {x:Bind}

Exempelappar som demonstrerar {Binding}

Varje bindning omfattar dessa delar

  • En bindningskälla. Det här är källan till data för bindningen, och det kan vara en instans av alla klasser som har medlemmar vars värden du vill visa i användargränssnittet.
  • Ett bindningsmål. Det här är en DependencyProperty för FrameworkElement i ditt användargränssnitt som visar data.
  • Ett bindningsobjekt. Det här är den del som överför datavärden från källan till målet och eventuellt från målet tillbaka till källan. Bindningsobjektet skapas vid XAML-inläsningstid från ditt {x:Bind} eller {Binding} -tillägg.

I följande avsnitt tar vi en närmare titt på bindningskällan, bindningsmålet och bindningsobjektet. Och vi länkar avsnitten tillsammans med exemplet med att binda en knapps innehåll till en strängegenskap med namnet NextButtonText, som tillhör en klass med namnet HostViewModel.

Bindningskälla

Här är en mycket rudimentär implementering av en klass som vi kan använda som bindningskälla.

Om du använder C++/WinRT lägger du till nya Midl File-objekt (.idl) i projektet, med namnet som visas i C++/WinRT-kodexemplet nedan. Ersätt innehållet i de nya filerna med MIDL 3.0-koden som visas i listan, skapa projektet för att generera HostViewModel.h och .cpp, och lägg sedan till kod i de genererade filerna för att matcha listan. Mer information om de genererade filerna och hur du kopierar dem till projektet finns i XAML-kontroller; bind till en C++/WinRT-egenskap.

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;
}
...

Implementeringen av HostViewModel och dess egenskap NextButtonText är endast lämplig för engångsbindning. Men enkelriktade och dubbelriktade bindningar är mycket vanliga, och i den typen av bindning uppdateras användargränssnittet automatiskt som svar på ändringar i datavärdena för bindningskällan. För att den typen av bindning ska fungera korrekt måste du göra bindningskällan "observerbar" till bindningsobjektet. Så i vårt exempel, om vi vill envägs- eller tvåvägsbindning till egenskapen NextButtonText , måste alla ändringar som sker vid körning till värdet för den egenskapen göras observerbara för bindningsobjektet.

Ett sätt att göra det är att härleda den klass som representerar din bindningskälla från DependencyObject och exponera ett datavärde via en DependencyProperty. Det är så ett FrameworkElement blir observerbart. FrameworkElements är bra bindningskällor direkt.

Ett enklare sätt att göra en klass observerbar – och ett nödvändigt för klasser som redan har en basklass – är att implementera System.ComponentModel.INotifyPropertyChanged. Detta innebär egentligen bara att implementera en enda händelse med namnet PropertyChanged. Ett exempel med HostViewModel finns nedan.

...
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 är egenskapen NextButtonText observerbar. När du skapar en enkelriktad eller dubbelriktad bindning till den egenskapen (vi visar hur senare) prenumererar det resulterande bindningsobjektet på händelsen PropertyChanged . När händelsen utlöses tar bindningsobjektets hanterare emot ett argument som innehåller namnet på den egenskap som har ändrats. Det är så bindningsobjektet vet vilken egenskaps värde som ska läsas och läsas igen.

Så att du inte behöver implementera mönstret som visas ovan flera gånger, om du använder C# kan du bara härleda från bindableBase-basklassen som du hittar i QuizGame-exemplet (i mappen "Common" ). Här är ett exempel på hur det ser ut.

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.

Anmärkning

För C++/WinRT kallas alla körningsklasser som du deklarerar i ditt program som härleds från en basklass en sammansättningsbar klass. Och det finns begränsningar kring sammansättningsbara klasser. För att ett program ska klara Windows App Certification Kit-testerna som används av Visual Studio och Av Microsoft Store för att validera inskickade filer (och därför för att programmet ska kunna matas in i Microsoft Store), måste en sammansättningsbar klass slutligen härledas från en Windows-basklass. Det innebär att klassen i själva roten i arvshierarkin måste vara en typ som kommer från ett Windows.* -namnområde. Om du behöver härleda en körningsklass från en basklass, till exempel för att implementera en BindableBase-klass för alla dina vymodeller att härleda från, kan du härleda från Windows.UI.Xaml.DependencyObject.

Att höja händelsen PropertyChanged med argumentet String.Empty eller null anger att alla egenskaper som inte är indexerare för objektet ska läsas på nytt. Du kan höja händelsen för att indikera att indexerarens egenskaper för objektet har ändrats med hjälp av argumentet "Item[indexer]" för specifika indexerare (där indexeraren är indexvärdet) eller ett värde av "Item[]" för alla indexerare.

En bindningskälla kan behandlas antingen som ett enskilt objekt vars egenskaper innehåller data eller som en samling objekt. I C# och Visual Basic-kod kan du en gång binda till ett objekt som implementerar List(Of T) för att visa en samling som inte ändras vid körning. För en observerbar samling (observerar när objekt läggs till och tas bort från samlingen) binder envägsbindning till ObservableCollection(Av T) i stället. I C++/CX-kod kan du binda till Vector<T> för både observerbara och icke-observerbara samlingar, och C++/WinRT har sina egna typer. Om du vill binda till dina egna samlingsklasser använder du vägledningen i följande tabell.

Scenario C# och VB (CLR) C++/WinRT C++/CX
Binda till ett objekt. Kan vara valfritt objekt. Kan vara valfritt objekt. Objektet måste ha BindableAttribute eller implementera ICustomPropertyProvider.
Hämta meddelanden om egenskapsändring från ett bundet objekt. Objektet måste implementera INotifyPropertyChanged. Objektet måste implementera INotifyPropertyChanged. Objektet måste implementera INotifyPropertyChanged.
Binda till en samling. List(av T) IVector för IInspectable eller IBindableObservableVector. Se XAML-objektkontroller; binda till en C++/WinRT-samling och samlingar med C++/WinRT. Vektor<T>
Hämta meddelanden om samlingsändring från en bunden samling. ObservableCollection(Av T) IObservableVector av IInspectable. Till exempel winrt::single_threaded_observable_vector<T>. IObservableVector<T>. Vektor<T> implementerar det här gränssnittet.
Implementera en samling som stöder bindning. Utöka lista(av T) eller implementera IList, IList(of Object), IEnumerable eller IEnumerable(Of Object). Bindning till allmän IList(Av T) och IEnumerable(Av T) stöds inte. Implementera IVector för IInspectable. Se XAML-objektkontroller; binda till en C++/WinRT-samling och samlingar med C++/WinRT. Implementera IBindableVector, IBindableIterable, IVector<Object^>, IIterable<Object^>, IVector<IInspectable*>eller IIterable<IInspectable*>. Bindning till allmän IVector<T> och IIterable<T> stöds inte.
Implementera en samling som stöder meddelanden om insamlingsändring. Utöka ObservableCollection(Av T) eller implementera (icke-generisk) IList och INotifyCollectionChanged. Implementera IObservableVector för IInspectable eller IBindableObservableVector. Implementera IBindableVector och IBindableObservableVector.
Implementera en samling som stöder inkrementell inläsning. Utöka ObservableCollection(Av T) eller implementera (icke-generisk) IList och INotifyCollectionChanged. Dessutom implementerar du ISupportIncrementalLoading. Implementera IObservableVector för IInspectable eller IBindableObservableVector. Dessutom implementerar du ISupportIncrementalLoading Implementera IBindableVector, IBindableObservableVector och ISupportIncrementalLoading.

Du kan binda listkontroller till godtyckligt stora datakällor och fortfarande uppnå höga prestanda med hjälp av inkrementell inläsning. Du kan till exempel binda listkontroller till Bing-bildfrågeresultat utan att behöva läsa in alla resultat samtidigt. I stället läser du bara in vissa resultat omedelbart och läser in ytterligare resultat efter behov. Om du vill ha stöd för inkrementell inläsning måste du implementera ISupportIncrementalLoading på en datakälla som stöder meddelanden om insamlingsändringar. När databindningsmotorn begär mer data måste datakällan göra lämpliga begäranden, integrera resultaten och sedan skicka lämpliga meddelanden för att uppdatera användargränssnittet.

Bindningsmål

I de två exemplen nedan är egenskapen Button.Content bindningsmålet och dess värde är inställt på ett markeringstillägg som deklarerar bindningsobjektet. Först visas {x:Bind} och sedan {Binding}. Att deklarera bindningar i markering är vanligt (det är praktiskt, läsbart och lätt att använda). Men du kan undvika markering och imperativt (programmatiskt) skapa en instans av klassen Bindning i stället om du behöver.

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

Om du använder C++/WinRT- eller Visual C++-komponenttillägg (C++/CX) måste du lägga till attributet BindableAttribute i alla körningsklasser som du vill använda {Binding} -markeringstillägget med.

Viktigt!

Om du använder C++/WinRT är attributetBindableAttribute tillgängligt om du har installerat Windows SDK version 10.0.17763.0 (Windows 10, version 1809) eller senare. Utan det attributet måste du implementera gränssnitten ICustomPropertyProvider och ICustomProperty för att kunna använda markeringstillägget {Binding} .

Bindningsobjekt som deklarerats med {x:Bind}

Det finns ett steg som vi måste göra innan vi skapar vår {x:Bind} -markering. Vi måste exponera vår bindningskällaklass från klassen som representerar vår sida med markering. Det gör vi genom att lägga till en egenskap (av typen HostViewModel i det här fallet) i vår MainPage-sidklass .

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;
}
...

Nu kan vi titta närmare på markering som deklarerar bindningsobjektet. Exemplet nedan använder samma Button.Content-bindningsmål som vi använde i avsnittet "Bindningsmål" tidigare och visar att det är bundet till egenskapen HostViewModel.NextButtonText .

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

Observera värdet som vi anger för Sökväg. Det här värdet tolkas i kontexten för själva sidan, och i det här fallet börjar sökvägen genom att referera till egenskapen ViewModel som vi just lade till på sidan MainPage . Den egenskapen returnerar en HostViewModel-instans , så att vi kan pricka in i objektet för att få åtkomst till egenskapen HostViewModel.NextButtonText . Och vi anger Läge för att åsidosätta standardvärdet {x:Bind} en gång.

Egenskapen Path stöder en mängd olika syntaxalternativ för bindning till kapslade egenskaper, anslutna egenskaper samt heltals- och strängindexerare. Mer information finns i Syntax för egenskapssökväg. Bindning till strängindexerare ger dig effekten av bindning till dynamiska egenskaper utan att behöva implementera ICustomPropertyProvider. Andra inställningar finns i {x:Bind} markeringstillägg.

För att illustrera att egenskapen HostViewModel.NextButtonText verkligen är observerbar lägger du till en Click-händelsehanterare på knappen och uppdaterar värdet för HostViewModel.NextButtonText. Skapa, kör och klicka på knappen för att se värdet för knappens innehållsuppdatering .

// 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");
}

Anmärkning

Ändringar i TextBox.Text skickas till en tvåvägsbunden källa när textrutan förlorar fokus och inte efter varje tangenttryckning.

DataTemplate och x:DataType

I ett DataTemplate (om det används som en objektmall, en innehållsmall eller en rubrikmall) tolkas inte värdet för Sökväg i kontexten för sidan, utan i kontexten för det dataobjekt som mallas. När du använder {x:Bind} i en datamall, så att dess bindningar kan verifieras (och effektiv kod genereras för dem) vid kompileringstiden, måste DataTemplate deklarera typen av dataobjekt med x:DataType. Exemplet nedan kan användas som ItemTemplate för en objektkontroll som är bunden till en samling SampleDataGroup-objekt .

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

Objekt med svag typ i sökvägen

Tänk till exempel på att du har en typ med namnet SampleDataGroup, som implementerar en strängegenskap med namnet Title. Och du har egenskapen MainPage.SampleDataGroupAsObject, som är av typen object, men som faktiskt returnerar en instans av SampleDataGroup. Bindningen <TextBlock Text="{x:Bind SampleDataGroupAsObject.Title}"/> resulterar i ett kompileringsfel eftersom egenskapen Title inte hittas på typobjektet. Botemedlet för detta är att lägga till en gjutning i sökvägssyntaxen så här: <TextBlock Text="{x:Bind ((data:SampleDataGroup)SampleDataGroupAsObject).Title}"/>. Här är ett annat exempel där Element deklareras som objekt men faktiskt är en TextBlock: <TextBlock Text="{x:Bind Element.Text}"/>. Och en rollbesättning åtgärdar problemet: <TextBlock Text="{x:Bind ((TextBlock)Element).Text}"/>.

Om dina data läses in asynkront

Kod som stöder {x:Bind} genereras vid kompileringstid i de partiella klasserna för dina sidor. Dessa filer finns i mappen obj med namn som (för C#) <view name>.g.cs. Den genererade koden innehåller en hanterare för sidans inläsningshändelse och den hanteraren anropar metoden Initialize på en genererad klass som representerar sidans bindningar. Initiera anropar i sin tur Update för att börja flytta data mellan bindningskällan och målet. Inläsningen utlöses strax före det första måttpasset för sidan eller användarkontrollen. Så om dina data läses in asynkront kanske de inte är klara när Initiera anropas. När du har läst in data kan du därför framtvinga att engångsbindningar initieras genom att anropa this.Bindings.Update();. Om du bara behöver engångsbindningar för asynkront inlästa data är det mycket billigare att initiera dem på det här sättet än att ha enkelriktade bindningar och lyssna efter ändringar. Om dina data inte genomgår detaljerade ändringar, och om de sannolikt kommer att uppdateras som en del av en specifik åtgärd, kan du göra dina bindningar en gång och framtvinga en manuell uppdatering när som helst med ett anrop till Uppdatera.

Anmärkning

{x:Bind} passar inte för sena scenarier, till exempel navigering i ordlistestrukturen för ett JSON-objekt eller ankskrivning. "Anka skriver" är en svag form av att skriva baserat på lexikala matchningar på egenskapsnamn (som i , "om det går, simmar och kvacksalvare som en anka, då är det en anka"). Med ankskrivning skulle en bindning till egenskapen Age vara lika nöjd med en person eller ett vinobjekt (förutsatt att dessa typer var och en hade en åldersegenskap ). I dessa scenarier använder du markeringstillägget {Binding} .

Bindningsobjekt som deklarerats med {Binding}

Om du använder C++/WinRT- eller Visual C++-komponenttillägg (C++/CX) måste du lägga till attributet BindableAttribute i valfri körningsklass som du vill binda till för att kunna använda tillägget {Binding}. Om du vill använda {x:Bind} behöver du inte det attributet.

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

Viktigt!

Om du använder C++/WinRT är attributetBindableAttribute tillgängligt om du har installerat Windows SDK version 10.0.17763.0 (Windows 10, version 1809) eller senare. Utan det attributet måste du implementera gränssnitten ICustomPropertyProvider och ICustomProperty för att kunna använda markeringstillägget {Binding} .

{Binding} förutsätter som standard att du binder till DataContext på din markeringssida. Därför ställer vi in DataContext på vår sida som en instans av vår bindningskällklass (av typen HostViewModel i det här fallet). Exemplet nedan visar markering som deklarerar bindningsobjektet. Vi använder samma Button.Content-bindningsmål som vi använde i avsnittet "Bindningsmål" tidigare, och vi binder till egenskapen 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");
}

Observera värdet som vi anger för Sökväg. Det här värdet tolkas i kontexten för sidans DataContext, som i det här exemplet är inställt på en instans av HostViewModel. Sökvägen refererar till egenskapen HostViewModel.NextButtonText . Vi kan utelämna läge eftersom standardvärdet {Binding} för enkelriktad fungerar här.

Standardvärdet för DataContext för ett användargränssnittselement är det ärvda värdet för dess överordnade element. Du kan naturligtvis åsidosätta den standardinställningen genom att uttryckligen ange DataContext , som i sin tur ärvs av underordnade som standard. Att uttryckligen ange DataContext för ett element är användbart när du vill ha flera bindningar som använder samma källa.

Ett bindningsobjekt har en källegenskap som standard är DataContext för UI-elementet där bindningen deklareras. Du kan åsidosätta den här standardinställningen genom att ange Source, RelativeSource eller ElementName explicit på bindningen (se {Binding} för mer information).

I en DataTemplate anges DataContext automatiskt till det dataobjekt som mallas. Exemplet nedan kan användas som ItemTemplate för en objektkontroll som är bunden till en samling av alla typer som har strängegenskaper med namnet Rubrik och Beskrivning.

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

Anmärkning

Som standard skickas ändringar i TextBox.Text till en tvåvägsbunden källa när textrutan förlorar fokus. Om du vill att ändringar ska skickas efter varje användarintryckning anger du UpdateSourceTrigger till PropertyChanged på bindningen i markering. Du kan också helt ta kontroll över när ändringar skickas till källan genom att ange UpdateSourceTrigger till Explicit. Sedan hanterar du händelser i textrutan (vanligtvis TextBox.TextChanged), anropar GetBindingExpression på målet för att hämta ett BindingExpression-objekt och anropar slutligen BindingExpression.UpdateSource för att programmatiskt uppdatera datakällan.

Egenskapen Path stöder en mängd olika syntaxalternativ för bindning till kapslade egenskaper, anslutna egenskaper samt heltals- och strängindexerare. Mer information finns i Syntax för egenskapssökväg. Bindning till strängindexerare ger dig effekten av bindning till dynamiska egenskaper utan att behöva implementera ICustomPropertyProvider. Egenskapen ElementName är användbar för element-till-element-bindning. Egenskapen RelativeSource har flera användningsområden, varav en är ett mer kraftfullt alternativ till mallbindning i ett ControlTemplate. Andra inställningar finns i {Binding}-markeringstillägget och klassen Bindning .

Vad händer om källan och målet inte är av samma typ?

Om du vill styra synligheten för ett användargränssnittselement baserat på värdet för en boolesk egenskap, eller om du vill återge ett gränssnittselement med en färg som är en funktion av ett numeriskt värdes intervall eller trend, eller om du vill visa ett datum- och/eller tidsvärde i en egenskap för användargränssnittselement som förväntar sig en sträng, måste du konvertera värden från en typ till en annan. Det finns fall där rätt lösning är att exponera en annan egenskap av rätt typ från din bindningskällklass och hålla konverteringslogik inkapslad och testbar där. Men det är inte flexibelt eller skalbart när du har stora antal, eller stora kombinationer, av käll- och målegenskaper. I så fall har du ett par alternativ:

  • Om du använder {x:Bind} kan du binda direkt till en funktion för att utföra konverteringen
  • Eller så kan du ange en värdekonverterare som är ett objekt som är utformat för att utföra konverteringen

Värdekonverterare

Här är en värdekonverterare, lämplig för en engångsbindning eller en enkelriktad bindning, som konverterar ett DateTime-värde till ett strängvärde som innehåller månaden. Klassen implementerar 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.

Och så här använder du värdekonverteraren i din bindningsobjektmarkering.

<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}}"/>

Bindningsmotorn anropar metoderna Convert och ConvertBack om parametern Converter har definierats för bindningen. När data skickas från källan anropar bindningsmotorn Konvertera och skickar returnerade data till målet. När data skickas från målet (för en dubbelriktad bindning) anropar bindningsmotorn ConvertBack och skickar returnerade data till källan.

Konverteraren har också valfria parametrar: ConverterLanguage, som gör det möjligt att ange vilket språk som ska användas i konverteringen, och ConverterParameter, vilket gör det möjligt att skicka en parameter för konverteringslogik. Ett exempel som använder en konverterarparameter finns i IValueConverter.

Anmärkning

Om det finns ett fel i konverteringen ska du inte utlösa ett undantag. Returnera i stället DependencyProperty.UnsetValue, vilket stoppar dataöverföringen.

Om du vill visa ett standardvärde som ska användas när bindningskällan inte kan matchas anger du egenskapen FallbackValue på bindningsobjektet i markering. Detta är användbart för att hantera konverterings- och formateringsfel. Det är också användbart att binda till källegenskaper som kanske inte finns på alla objekt i en bunden samling heterogena typer.

Om du binder en textkontroll till ett värde som inte är en sträng konverterar databindningsmotorn värdet till en sträng. Om värdet är en referenstyp hämtar databindningsmotorn strängvärdet genom att anropa ICustomPropertyProvider.GetStringRepresentation eller IStringable.ToString om det är tillgängligt och anropar annars Object.ToString. Observera dock att bindningsmotorn ignorerar alla ToString-implementeringar som döljer basklassimplementeringen. Underklassimplementeringar bör åsidosätta basklassens ToString-metod i stället. På samma sätt verkar alla hanterade objekt på interna språk implementera ICustomPropertyProvider och IStringable. Alla anrop till GetStringRepresentation och IStringable.ToString dirigeras dock till Object.ToString eller en åsidosättning av metoden och aldrig till en ny ToString-implementering som döljer basklassimplementeringen.

Anmärkning

Från och med Windows 10, version 1607, tillhandahåller XAML-ramverket en inbyggd boolesk till synlighetskonverterare. Konverteraren mappar sant till det synliga uppräkningsvärdet och falskt till Komprimerad så att du kan binda en synlighetsegenskap till ett booleskt värde utan att skapa en konverterare. Om du vill använda den inbyggda konverteraren måste appens lägsta SDK-målversion vara 14393 eller senare. Du kan inte använda den när din app riktar in sig på tidigare versioner av Windows 10. Mer information om målversioner finns i Versionsanpassad kod.

Funktionsbindning i {x:Bind}

{x:Bind} gör att det sista steget i en bindningssökväg kan vara en funktion. Detta kan användas för att utföra konverteringar och för att utföra bindningar som är beroende av mer än en egenskap. Se Funktioner i x:Bind

Bindning mellan element

Du kan binda egenskapen för ett XAML-element till egenskapen för ett annat XAML-element. Här är ett exempel på hur det ser ut i markering.

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

Viktigt!

Det nödvändiga arbetsflödet för element-till-element-bindning med C++/WinRT finns i Bindning mellan element.

Resursordlistor med {x:Bind}

Markeringstillägget {x:Bind} är beroende av kodgenerering, så den behöver en kod bakom fil som innehåller en konstruktor som anropar InitializeComponent (för att initiera den genererade koden). Du återanvänder resursordlistan genom att instansiera dess typ (så att InitializeComponent anropas) i stället för att referera till dess filnamn. Här är ett exempel på vad du kan göra om du har en befintlig resursordlista och vill använda {x:Bind} i den.

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>

Blanda {x:Bind} och {Binding} i ett återanvändbart format

I föregående exempel visas {x:Bind} i DataTemplates, men du kan också skapa återanvändbara format som kombinerar både {x:Bind} och {Binding} markeringstillägg. Det här är användbart när du vill binda vissa egenskaper till kompilering av kända värden med {x:Bind} och andra egenskaper för att köra DataContext-värden med hjälp av {Binding}.

Här är ett exempel som visar hur du skapar ett återanvändbart knappformat som använder båda bindningsmetoderna:

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);
    }
}

Användning i MainPage.xaml med en ViewModel som tillhandahåller körningsvärden:

<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 (DataContext som tillhandahåller körningsbindningsvärden):

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

I det här exemplet:

  • {Binding} används för egenskaper som är beroende av DataContext (ButtonBackgroundBrush, ButtonForegroundBrush, ButtonHoverBrush)
  • {x:Bind} används för egenskaper som är kända för kompileringstid och som tillhör själva ResourceDictionary (DefaultIndicatorBrush, DefaultPressedBrush)
  • Formatet kan återanvändas och kan tillämpas på valfri knapp
  • Körningstema är möjligt via DataContext samtidigt som prestanda för {x:Bind} fortfarande kan användas för statiska element

Händelsebindning och ICommand

{x:Bind} stöder en funktion som kallas händelsebindning. Med den här funktionen kan du ange hanteraren för en händelse med hjälp av en bindning, vilket är ytterligare ett alternativ utöver att hantera händelser med en metod i filen bakom koden. Anta att du har en RootFrame-egenskap i klassen MainPage .

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

Du kan sedan binda en knapps Klicka-händelse till en metod i ramobjektet som returneras av egenskapen RootFrame så här. Observera att vi också binder knappens isEnabled-egenskap till en annan medlem i samma ram.

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

Överlagrade metoder kan inte användas för att hantera en händelse med den här tekniken. Om metoden som hanterar händelsen har parametrar måste alla vara tilldelningsbara från typerna av alla händelsens parametrar. I det här fallet är Frame.GoForward inte överbelastad och har inga parametrar (men det skulle fortfarande vara giltigt även om det tog två objektparametrar). Frame.GoBack är dock överbelastat, så vi kan inte använda den metoden med den här tekniken.

Händelsebindningstekniken liknar implementering och användning av kommandon (ett kommando är en egenskap som returnerar ett objekt som implementerar ICommand-gränssnittet ). Både {x:Bind} och {Binding} fungerar med kommandon. Så att du inte behöver implementera kommandomönstret flera gånger kan du använda hjälpklassen DelegateCommand som du hittar i QuizGame-exemplet (i mappen Common).

Bindning till en samling mappar eller filer

Du kan använda API:erna i namnområdet Windows.Storage för att hämta mapp- och fildata. De olika metoderna GetFilesAsync, GetFoldersAsync och GetItemsAsync returnerar dock inte värden som är lämpliga för bindning till listkontroller. I stället måste du binda till returvärdena för metoderna GetVirtualizedFilesVector, GetVirtualizedFoldersVector och GetVirtualizedItemsVector i klassen FileInformationFactory . Följande kodexempel från Exemplet StorageDataSource och GetVirtualizedFilesVector visar det typiska användningsmönstret. Kom ihåg att deklarera funktionen picturesLibrary i apppaketmanifestet och bekräfta att det finns bilder i biblioteksmappen Bilder.

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

Du använder vanligtvis den här metoden för att skapa en skrivskyddad vy över fil- och mappinformation. Du kan skapa tvåvägsbindningar till fil- och mappegenskaperna, till exempel för att låta användare betygsätta en låt i en musikvy. Ändringar sparas dock inte förrän du anropar lämplig SavePropertiesAsync-metod (till exempel MusicProperties.SavePropertiesAsync). Du bör checka in ändringar när objektet förlorar fokus eftersom detta utlöser en markeringsåterställning.

Observera att dubbelriktad bindning med den här tekniken endast fungerar med indexerade platser, till exempel Musik. Du kan avgöra om en plats indexeras genom att anropa metoden FolderInformation.GetIndexedStateAsync .

Observera också att en virtualiserad vektor kan returnera null för vissa objekt innan den fyller i deras värde. Du bör till exempel söka efter null innan du använder värdet SelectedItem för en listkontroll som är bunden till en virtualiserad vektor, eller använd SelectedIndex i stället.

Bindning till data grupperade efter en nyckel

Om du tar en platt samling objekt (böcker, till exempel representeras av en BookSku-klass ) och du grupperar objekten med hjälp av en gemensam egenskap som en nyckel (till exempel egenskapen BookSku.AuthorName ) kallas resultatet grupperade data. När du grupperar data är det inte längre en platt samling. Grupperade data är en samling gruppobjekt, där varje gruppobjekt har

  • en nyckel, och
  • en samling objekt vars egenskap matchar den nyckeln.

Om du vill ta bokexemplet igen resulterar resultatet av att gruppera böckerna efter författarens namn i en samling med författarnamnsgrupper där varje grupp har

  • en nyckel, som är ett författarens namn, och
  • en samling av BookSkuvars AuthorName-egenskap matchar gruppens nyckel.

Om du vill visa en samling binder du objektkällan för en objektkontroll (till exempel ListView eller GridView) direkt till en egenskap som returnerar en samling. Om det är en platt samling objekt behöver du inte göra något speciellt. Men om det är en samling gruppobjekt (som det är när du binder till grupperade data) behöver du tjänsterna för ett mellanliggande objekt som kallas en CollectionViewSource som finns mellan objektkontrollen och bindningskällan. Du binder CollectionViewSource till egenskapen som returnerar grupperade data och du binder objektkontrollen till CollectionViewSource. Ett extra värdetillägg för en CollectionViewSource är att det håller reda på det aktuella objektet, så att du kan hålla fler än en objektkontroll synkroniserad genom att binda dem alla till samma CollectionViewSource. Du kan också komma åt det aktuella objektet programmatiskt via egenskapen ICollectionView.CurrentItem för objektet som returneras av egenskapen CollectionViewSource.View .

Om du vill aktivera grupperingsanläggningen för en CollectionViewSource anger du IsSourceGrouped till true. Om du också behöver ange egenskapen ItemsPath beror på exakt hur du skapar dina gruppobjekt. Det finns två sätt att skapa ett gruppobjekt: mönstret "is-a-group" och mönstret "has-a-group". I mönstret "is-a-group" härleds gruppobjektet från en samlingstyp (till exempel List<T>), så gruppobjektet är faktiskt själva objektgruppen. Med det här mönstret behöver du inte ange ItemsPath. I mönstret "has-a-group" har gruppobjektet en eller flera egenskaper av en samlingstyp (till exempel Lista<T>), så gruppen "har en" grupp med objekt i form av en egenskap (eller flera grupper av objekt i form av flera egenskaper). Med det här mönstret måste du ange ItemsPath till namnet på den egenskap som innehåller objektgruppen.

Exemplet nedan illustrerar mönstret "has-a-group". Sidklassen har en egenskap med namnet ViewModel, som returnerar en instans av vår vymodell. CollectionViewSource binder till egenskapen Författare för vymodellen (författare är samlingen med gruppobjekt) och anger även att det är egenskapen Author.BookSkus som innehåller de grupperade objekten. Slutligen är GridView bundet till CollectionViewSource och har dess gruppformat definierat så att det kan återge objekten i grupper.

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

Du kan implementera mönstret "is-a-group" på något av två sätt. Ett sätt är att skapa en egen gruppklass. Härled klassen från List<T> (där T är typen av objekt). Till exempel public class Author : List<BookSku>. Det andra sättet är att använda ett LINQ-uttryck för att dynamiskt skapa gruppobjekt (och en gruppklass) från liknande egenskapsvärden för BookSku-objekten . Den här metoden – att endast underhålla en platt lista över objekt och gruppera dem i farten – är typisk för en app som kommer åt data från en molntjänst. Du får flexibiliteten att gruppera böcker efter författare eller genre (till exempel) utan att behöva särskilda gruppklasser som Författare och Genre.

Exemplet nedan illustrerar "is-a-group"-mönstret med LINQ. Den här gången grupperar vi böcker efter genre, som visas med genrenamnet i grupprubrikerna. Detta indikeras av egenskapssökvägen "Nyckel" som referens till gruppnyckelvärdet .

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

Kom ihåg att när du använder {x:Bind} med datamallar måste vi ange vilken typ som ska bindas till genom att ange ett x:DataType-värde . Om typen är allmän kan vi inte uttrycka den i markering, så vi måste använda {Binding} i stället i mallen för gruppformatrubriken.

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

En SemanticZoom-kontroll är ett bra sätt för användarna att visa och navigera i grupperade data. Exempelappen Bookstore2 visar hur du använder SemanticZoom. I appen kan du visa en lista över böcker grupperade efter författare (den inzoomade vyn) eller så kan du zooma ut för att se en snabblista med författare (den utzoomade vyn). Hopplistan ger mycket snabbare navigering än att bläddra igenom listan över böcker. De inzoomade och utzoomade vyerna är faktiskt ListView - eller GridView-kontroller som är bundna till samma CollectionViewSource.

En bild av en SemanticZoom

När du binder till hierarkiska data, till exempel underkategorier inom kategorier, kan du välja att visa hierarkiska nivåer i användargränssnittet med en serie objektkontroller. En markering i en objektkontroll avgör innehållet i efterföljande objektkontroller. Du kan hålla listorna synkroniserade genom att binda varje lista till sin egen CollectionViewSource och binda samman CollectionViewSource-instanserna i en kedja. Detta kallas för en huvud-/informationsvy (eller lista/information). Mer information finns i Så här binder du till hierarkiska data och skapar en huvud-/informationsvy.

Diagnostisera och felsöka problem med databindning

Bindningsmarkeringen innehåller namnen på egenskaperna (och för C#, ibland fält och metoder). Så när du byter namn på en egenskap måste du också ändra alla bindningar som refererar till den. Om du glömmer att göra det leder det till ett typiskt exempel på en databindningsfel, och din app kompilerar inte eller kommer inte att köras korrekt.

Bindningsobjekten som skapats av {x:Bind} och {Binding} är till stor del funktionellt likvärdiga. Men {x:Bind} har typinformation för bindningskällan och genererar källkod vid kompileringstid. Med {x:Bind} får du samma typ av problemidentifiering som du får med resten av koden. Det inkluderar kompileringstidsverifiering av dina bindningsuttryck och felsökning genom att ange brytpunkter i källkoden som genereras som partiell klass för sidan. Dessa klasser finns i filerna i mappen obj med namn som (för C#) <view name>.g.cs). Om du har problem med en bindning aktiverar du Bryt vid ohanterade undantag i Microsoft Visual Studio-felsökningsprogrammet. Felsökningsprogrammet bryter körningen vid den tidpunkten och du kan sedan felsöka vad som har gått fel. Koden som genereras av {x:Bind} följer samma mönster för varje del av diagrammet med bindningskällnoder, och du kan använda informationen i fönstret Anropa stack för att fastställa sekvensen med anrop som ledde fram till problemet.

{Binding} har inte typinformation för bindningskällan. Men när du kör appen med felsökningsprogrammet kopplat visas eventuella bindningsfel i utdatafönstret i Visual Studio.

Skapa bindningar i kod

Not Det här avsnittet gäller endast för {Binding}, eftersom du inte kan skapa {x:Bind} -bindningar i kod. Vissa av fördelarna med {x:Bind} kan dock uppnås med DependencyObject.RegisterPropertyChangedCallback, vilket gör att du kan registrera dig för ändringsmeddelanden för alla beroendeegenskaper.

Du kan också ansluta gränssnittselement till data med hjälp av procedurkod i stället för XAML. Det gör du genom att skapa ett nytt bindningsobjekt , ange lämpliga egenskaper och sedan anropa FrameworkElement.SetBinding eller BindingOperations.SetBinding. Att skapa bindningar programmatiskt är användbart när du vill välja bindningsegenskapsvärdena vid körning eller dela en enda bindning mellan flera kontroller. Observera dock att du inte kan ändra bindningsegenskapsvärdena när du anropar SetBinding.

I följande exempel visas hur du implementerar en bindning i kod.

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

Funktionsjämförelse för {x:Bind} och {Binding}

Feature {x:Bind} jämfört med {Binding} Noteringar
Sökvägen är standardegenskapen {x:Bind a.b.c}
-
{Binding a.b.c}
Sökvägsegenskap {x:Bind Path=a.b.c}
-
{Binding Path=a.b.c}
I x:Bind är sökvägen rotad på sidan som standard, inte på DataContext.
Indexer {x:Bind Groups[2].Title}
-
{Binding Groups[2].Title}
Binder till det angivna objektet i samlingen. Endast heltalsbaserade index stöds.
Anslutna egenskaper {x:Bind Button22.(Grid.Row)}
-
{Binding Button22.(Grid.Row)}
Anslutna egenskaper anges med parenteser. Om egenskapen inte deklareras i ett XAML-namnområde prefixar du den med ett XML-namnområde, som ska mappas till ett kodnamnområde i dokumentets huvud.
Gjutning {x:Bind groups[0].(data:SampleDataGroup.Title)}
-
Behövs inte för {Binding}.
Casts anges med parenteser. Om egenskapen inte deklareras i ett XAML-namnområde prefixar du den med ett XML-namnområde, som ska mappas till ett kodnamnområde i dokumentets huvud.
Omvandlare {x:Bind IsShown, Converter={StaticResource BoolToVisibility}}
-
{Binding IsShown, Converter={StaticResource BoolToVisibility}}
Konverterare måste deklareras i roten på sidan/ResourceDictionary eller i 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}
Konverterare måste deklareras i roten på sidan/ResourceDictionary eller i App.xaml.
TargetNullValue {x:Bind Name, TargetNullValue=0}
-
{Binding Name, TargetNullValue=0}
Används när bladet i bindningsuttrycket är null. Använd enkla citattecken för ett strängvärde.
FallbackValue {x:Bind Name, FallbackValue='empty'}
-
{Binding Name, FallbackValue='empty'}
Används när någon del av sökvägen för bindningen (förutom bladet) är null.
ElementName {x:Bind slider1.Value}
-
{Binding Value, ElementName=slider1}
Med {x:Bind} binds du till ett fält. Sökvägen är rotad på sidan som standard, så alla namngivna element kan nås via dess fält.
RelativeSource: Self <Rectangle x:Name="rect1" Width="200" Height="{x:Bind rect1.Width}" ... />
-
<Rectangle Width="200" Height="{Binding Width, RelativeSource={RelativeSource Self}}" ... />
Med {x:Bind} namnger du elementet och använder dess namn i Sökväg.
RelativeSource: TemplatedParent Behövs inte för {x:Bind}
-
{Binding <path>, RelativeSource={RelativeSource TemplatedParent}}
Med {x:Bind} TargetType på ControlTemplate anger bindningen till mallens överordnade. För {Binding} kan vanlig mallbindning användas i kontrollmallar för de flesta användningsområden. Men använd TemplatedParent där du behöver använda en konverterare eller en dubbelriktad bindning.<
Källa Behövs inte för {x:Bind}
-
<ListView ItemsSource="{Binding Orders, Source={StaticResource MyData}}"/>
För {x:Bind} kan du direkt använda det namngivna elementet, använda en egenskap eller en statisk sökväg.
Mode {x:Bind Name, Mode=OneWay}
-
{Binding Name, Mode=TwoWay}
Läget kan vara OneTime, OneWay eller TwoWay. {x:Bind} är som standard OneTime; {Binding} är som standard OneWay.
UpdateSourceTrigger {x:Bind Name, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}
-
{Binding UpdateSourceTrigger=PropertyChanged}
UpdateSourceTrigger kan vara Standard, LostFocus eller PropertyChanged. {x:Bind} stöder inte UpdateSourceTrigger=Explicit. {x:Bind} använder PropertyChanged-beteende för alla fall utom TextBox.Text, där funktionen LostFocus används.