Dela via


Självstudie: Skapa databindningar

Anta att du har utformat och implementerat ett snyggt användargränssnitt fyllt med platshållarbilder, "lorem ipsum" pannplåtstext och kontroller som inte gör något ännu. Därefter vill du ansluta dem till verkliga data och omvandla dem från en designprototyp till en levande app.

I den här handledningen kommer du att lära dig hur du ersätter din standardkod med databindningar och skapar andra direkta länkar mellan ditt användargränssnitt och dina data. Du får också lära dig hur du formaterar eller konverterar dina data för visning och håller användargränssnittet och data synkroniserade. När du slutför den här självstudien kan du förbättra enkelheten och organisationen av XAML- och C#-koden, vilket gör det enklare att underhålla och utöka.

Du börjar med en förenklad version av PhotoLab-exemplet. Den här startversionen innehåller det fullständiga dataskiktet plus de grundläggande XAML-sidlayouterna och utelämnar många funktioner för att göra koden enklare att bläddra runt i. Den här självstudien sträcker sig inte till en komplett app, så se till att kolla in den slutliga versionen för att se funktioner som anpassade animeringar och responsiva layouter. Du hittar den slutförda versionen i rotmappen i Windows-appsample-photo-lab lagringsplats.

PhotoLab-exempelappen har två sidor. Den huvudsidan visar en bildgallerivy, tillsammans med viss information om varje bildfil.

Skärmbild av huvudsidan för fotolabbet.

Detaljsidan visar ett enda foto efter att det har valts. Med en utfälld redigeringsmeny kan fotot ändras, byta namn och sparas.

Skärmbild av informationssidan för fotolabb.

Förutsättningar

Del 0: Hämta startkoden från GitHub

I den här självstudien börjar du med en förenklad version av PhotoLab-exemplet.

  1. Gå till GitHub-sidan för exemplet: https://github.com/Microsoft/Windows-appsample-photo-lab.

  2. Därefter måste du klona eller ladda ned exemplet. Välj knappen Klona eller ladda ned. En undermeny visas. Klona eller ladda ned-menyn på GitHub-exempelsidan för PhotoLab

    Om du inte är bekant med GitHub:

    a. Välj Ladda ned ZIP- och spara filen lokalt. Detta laddar ned en .zip fil som innehåller alla projektfiler som du behöver.

    b) Extrahera filen. Använd Utforskaren för att bläddra till den .zip fil som du nyss laddade ned, högerklicka på den och välj Extrahera alla....

    Punkt c Bläddra till din lokala kopia av exemplet och gå till katalogen Windows-appsample-photo-lab-master\xaml-basics-starting-points\data-binding.

    Om du är bekant med GitHub:

    a. Klona huvudgrenen av repo:t lokalt.

    b) Bläddra till katalogen Windows-appsample-photo-lab\xaml-basics-starting-points\data-binding.

  3. Dubbelklicka på Photolab.sln för att öppna lösningen i Visual Studio.

Del 1: Ersätt platshållarna

Här skapar du engångsbindningar i XAML för datamall för att visa verkliga bilder och bildmetadata i stället för platshållarinnehåll.

Engångsbindningar är för endast läsbar, oföränderlig data, vilket innebär att de är högpresterande och enkla att skapa, så att du kan visa stora datamängder i GridView- och ListView-kontroll.

Ersätt platshållarna med engångsbindningar

  1. Öppna mappen xaml-basics-starting-points\data-binding och starta filen PhotoLab.sln i Visual Studio.

  2. Kontrollera att din Solution Platform- är inställd på x86 eller x64, inte Arm, och kör sedan appen. Detta visar appens tillstånd med platshållare för användargränssnittet innan bindningar har lagts till.

    Appen körs med platshållarbilder och text

  3. Öppna MainPage.xaml och sök efter en DataTemplate med namnet ImageGridView_DefaultItemTemplate. Du uppdaterar den här mallen så att den använder databindningar.

    Före:

    <DataTemplate x:Key="ImageGridView_DefaultItemTemplate">
    

    Värdet x:Key används av ImageGridView för att välja den här mallen för att visa dataobjekt.

  4. Lägg till ett x:DataType värde i mallen.

    Efter:

    <DataTemplate x:Key="ImageGridView_DefaultItemTemplate"
                  x:DataType="local:ImageFileInfo">
    

    x:DataType anger vilken typ detta är en mall för. I det här fallet är det en mall för klassen ImageFileInfo (där local: anger det lokala namnområdet enligt definitionen i en xmlns-deklaration längst upp i filen).

    x:DataType krävs när du använder x:Bind uttryck i en datamall enligt beskrivningen nedan.

  5. I DataTemplateletar du reda på Image-elementet med namnet ItemImage och ersätter dess Source-värde som det visas.

    Före:

    <Image x:Name="ItemImage"
           Source="/Assets/StoreLogo.png"
           Stretch="Uniform" />
    

    Efter:

    <Image x:Name="ItemImage"
           Source="{x:Bind ImageSource}"
           Stretch="Uniform" />
    

    x:Name identifierar ett XAML-element så att du kan referera till det någon annanstans i XAML och i koden bakom.

    x:Bind uttryck tillhandahåller ett värde till en UI-egenskap genom att hämta värdet från en egenskap för dataobjekt. I mallar är den angivna egenskapen en egenskap hos det som x:DataType har ställts in på. Så i det här fallet är datakällan egenskapen ImageFileInfo.ImageSource.

    Anmärkning

    Värdet x:Bind låter också redigeraren veta om datatypen, så att du kan använda IntelliSense i stället för att skriva in egenskapsnamnet i ett x:Bind uttryck. Prova det på koden du just klistrade in: placera markören precis efter x:Bind och tryck på mellanslagstangenten för att se en lista över egenskaper du kan binda till.

  6. Ersätt värdena för de andra UI-kontrollerna på samma sätt. (Prova att göra detta med IntelliSense i stället för att kopiera/klistra in!)

    Före:

    <TextBlock Text="Placeholder" ... />
    <StackPanel ... >
        <TextBlock Text="PNG file" ... />
        <TextBlock Text="50 x 50" ... />
    </StackPanel>
    <muxc:RatingControl Value="3" ... />
    

    Efter:

    <TextBlock Text="{x:Bind ImageTitle}" ... />
    <StackPanel ... >
        <TextBlock Text="{x:Bind ImageFileType}" ... />
        <TextBlock Text="{x:Bind ImageDimensions}" ... />
    </StackPanel>
    <muxc:RatingControl Value="{x:Bind ImageRating}" ... />
    

Kör appen för att se hur den ser ut hittills. Inga fler platshållare! Vi har en bra start.

Att köra appen med riktiga bilder och text i stället för platshållare

Anmärkning

Om du vill experimentera ytterligare kan du prova att lägga till en ny TextBlock i datamallen och använda tricket x:Bind IntelliSense för att hitta en egenskap som ska visas.

Här skapar du engångsbindningar på sidan XAML för att ansluta gallerivyn till bildsamlingen och ersätta den befintliga procedurkoden som gör detta i kod bakom. Du kommer också att skapa en Ta bort-knapp för att se hur gallerivyn ändras när bilder tas bort från samlingen. Samtidigt får du lära dig hur du binder händelser till händelsehanterare för mer flexibilitet än vad som tillhandahålls av traditionella händelsehanterare.

Alla bindningar som omfattas hittills finns i datamallar och refererar till egenskaperna för klassen som anges av x:DataType-värdet. Hur är det med resten av XAML på din sida?

x:Bind uttryck utanför datamallar är alltid bundna till själva sidan. Det innebär att du kan referera till allt du lägger i kod bakom eller deklarerar i XAML, inklusive anpassade egenskaper och egenskaper för andra användargränssnittskontroller på sidan (så länge de har ett x:Name värde).

I PhotoLab-exemplet kan en bindning som denna användas för att ansluta huvudkontrollen GridView direkt till samlingen av bilder, i stället för att göra det i bakomliggande kod. Senare visas andra exempel.

Binda den huvudsakliga GridView-kontrollen till samlingen Bilder

  1. I MainPage.xaml.cs letar du upp metoden GetItemsAsync och tar bort koden som anger ItemsSource.

    Före:

    ImageGridView.ItemsSource = Images;
    

    Efter:

    // Replaced with XAML binding:
    // ImageGridView.ItemsSource = Images;
    
  2. I MainPage.xaml hittar du GridView med namnet ImageGridView och lägger till ett ItemsSource-attribut. För värdet använder du ett x:Bind uttryck som refererar till egenskapen Images implementerad i code-behind.

    Före:

    <GridView x:Name="ImageGridView"
    

    Efter:

    <GridView x:Name="ImageGridView"
              ItemsSource="{x:Bind Images}"
    

    Egenskapen Images är av typen ObservableCollection<ImageFileInfo>, så de enskilda objekt som visas i GridView är av typen ImageFileInfo. Detta matchar det x:DataType värde som beskrivs i del 1.

Alla bindningar som vi har tittat på hittills är engångs- och skrivskyddade bindningar, vilket är standardbeteendet för vanliga x:Bind-uttryck. Data läses bara in vid initiering, vilket ger högpresterande bindningar – perfekt för stöd för flera komplexa vyer av stora datamängder.

Även bindningen ItemsSource som du just lagt till är en engångs-, skrivskyddad bindning till ett oföränderligt egenskapsvärde, men det finns en viktig distinktion att göra här. Det oföränderliga värdet för egenskapen Images är en enda specifik instans av en samling, initialiserad en gång som visas här.

private ObservableCollection<ImageFileInfo> Images { get; }
    = new ObservableCollection<ImageFileInfo>();

Egenskapsvärdet Images ändras aldrig, men eftersom egenskapen är av typen ObservableCollection<T>kan innehållet i samlingen ändras, och bindningen upptäcker automatiskt ändringarna och uppdaterar användargränssnittet.

För att testa detta ska vi tillfälligt lägga till en knapp som tar bort den bild som för närvarande är vald. Den här knappen är inte i den slutliga versionen eftersom du kommer till en detaljsida om du väljer en bild. Beteendet för ObservableCollection<T> är dock fortfarande viktigt i det slutliga PhotoLab-exemplet eftersom XAML initieras i sidkonstruktorn (via InitializeComponent-metodanropet), men Images-samlingen fylls i senare i GetItemsAsync-metoden.

Lägg till en borttagningsknapp

  1. I MainPage.xaml hittar du CommandBar med namnet MainCommandBar och lägger till en ny knapp före zoomknappen. (Zoomkontrollerna fungerar inte än. Du ansluter dem i nästa del av självstudien.)

    <AppBarButton Icon="Delete"
                  Label="Delete selected image"
                  Click="{x:Bind DeleteSelectedImage}" />
    

    Om du redan är bekant med XAML kan det här Click värdet se ovanligt ut. I tidigare versioner av XAML var du tvungen att ange detta till en metod med en specifik händelsehanterarsignatur, vanligtvis inklusive parametrar för händelsesändaren och ett händelsespecifikt argumentobjekt. Du kan fortfarande använda den här tekniken när du behöver händelseargumenten, men med x:Bindkan du också ansluta till andra metoder. Om du till exempel inte behöver händelsedata kan du ansluta till metoder som inte har några parametrar, som vi gör här.

  2. Lägg till metoden DeleteSelectedImage i MainPage.xaml.cs.

    private void DeleteSelectedImage() =>
        Images.Remove(ImageGridView.SelectedItem as ImageFileInfo);
    

    Den här metoden tar helt enkelt bort den markerade bilden från samlingen Images.

Kör appen och använd knappen för att ta bort några bilder. Som du ser uppdateras användargränssnittet automatiskt tack vare databindning och ObservableCollection<T> typ.

Anmärkning

Den här koden tar bara bort ImageFileInfo-instansen från Images-samlingen i appen som körs. Avbildningsfilen tas inte bort från datorn.

Del 3: Konfigurera zoomreglaget

I den här delen skapar du enkelriktade bindningar från en kontroll i datamallen till zoomreglaget, som ligger utanför mallen. Du får också lära dig att du kan använda databindning med många kontrollegenskaper, inte bara de mest uppenbara som TextBlock.Text och Image.Source.

Binda bilddatamallen till zoomreglaget

  • Leta upp DataTemplate med namnet ImageGridView_DefaultItemTemplate och ersätt **Height**- och Width-värdena för Grid-kontrollen överst i mallen.

    före

    <DataTemplate x:Key="ImageGridView_DefaultItemTemplate"
                  x:DataType="local:ImageFileInfo">
        <Grid Height="200"
              Width="200"
              Margin="{StaticResource LargeItemMargin}">
    

    efter

    <DataTemplate x:Key="ImageGridView_DefaultItemTemplate"
                  x:DataType="local:ImageFileInfo">
        <Grid Height="{Binding Value, ElementName=ZoomSlider}"
              Width="{Binding Value, ElementName=ZoomSlider}"
              Margin="{StaticResource LargeItemMargin}">
    

Märkte du att det här är Binding uttryck och inte x:Bind uttryck? Det här är det gamla sättet att göra databindningar, och det är mestadels föråldrat. x:Bind gör nästan allt som Binding gör med mera. Men när du använder x:Bind i en datamall binder den till den typ som deklareras i x:DataType-värdet. Så hur binder du något i mallen till något på sidan XAML eller i kod bakom? Du måste använda ett uttryck i äldre stil Binding.

Binding uttryck känner inte igen värdet för x:DataType, men dessa Binding uttryck har ElementName värden som fungerar nästan på samma sätt. Dessa talar om för bindningsmotorn att bindningsvärde är en bindning till egenskapen Value för det angivna elementet på sidan (det vill säga elementet med det x:Name värdet). Om du vill binda till en egenskap i code-behind ser det ut ungefär som {Binding MyCodeBehindProperty, ElementName=page} där page refererar till det x:Name värde som anges i Page-elementet i XAML.

Anmärkning

Som standard är Binding uttryck ettsätt, vilket innebär att användargränssnittet uppdateras automatiskt när värdet för den bundna egenskapen ändras.

Standardvärdet för x:Bind är däremot entid, vilket innebär att alla ändringar i den bundna egenskapen ignoreras. Detta är standard eftersom det är det mest högpresterande alternativet, och de flesta bindningar är statiska, skrivskyddade data.

Lektionen här är att om du använder x:Bind med egenskaper som kan ändra deras värden måste du lägga till Mode=OneWay eller Mode=TwoWay. Du ser exempel på detta i nästa avsnitt.

Kör appen och använd skjutreglaget för att ändra dimensionerna för avbildningsmallar. Som du ser är effekten ganska kraftfull utan att behöva mycket kod.

Kör app med zoomreglage som visar

Anmärkning

Prova att binda andra UI-egenskaper till zoomreglagets Value-egenskap eller till andra reglage som du lägger till efter zoomreglaget. Du kan till exempel binda egenskapen FontSize för TitleTextBlock till ett nytt skjutreglage med standardvärdet 24. Se till att ange rimliga minimi- och maxvärden.

Del 4: Förbättra zoomupplevelsen

I den här delen lägger du till en anpassad ItemSize-egenskap i code-behind och skapar enkelriktade bindningar från bildmallen till den nya egenskapen. ItemSize-värdet uppdateras av zoomreglaget och andra faktorer som Anpassa till skärm-brytaren och fönsterstorleken, vilket skapar en mer förfinad upplevelse.

Till skillnad från inbyggda kontrollegenskaper uppdaterar inte dina anpassade egenskaper användargränssnittet automatiskt, även med enkelriktade och dubbelriktade bindningar. De fungerar bra med entid bindningar, men om du vill att dina egenskapsändringar ska visas i användargränssnittet måste du utföra lite arbete.

Skapa egenskapen ItemSize så att den uppdaterar användargränssnittet

  1. I MainPage.xaml.cs ändrar du signaturen för klassen MainPage så att den implementerar INotifyPropertyChanged-gränssnittet.

    Före:

    public sealed partial class MainPage : Page
    

    Efter:

    public sealed partial class MainPage : Page, INotifyPropertyChanged
    

    Detta informerar bindningssystemet om att MainPage har en PropertyChanged händelse (läggs till nästa) som bindningar kan lyssna efter för att uppdatera användargränssnittet.

  2. Lägg till en PropertyChanged-händelse till MainPage-klassen.

    public event PropertyChangedEventHandler PropertyChanged;
    

    Den här händelsen tillhandahåller den fullständiga implementering som krävs av INotifyPropertyChanged-gränssnittet. Men för att den ska ha någon effekt måste du uttryckligen utlösa händelsen i dina anpassade egenskaper.

  3. Lägg till en ItemSize-egenskap och utlös händelsen PropertyChanged i dess sättare.

    public double ItemSize
    {
        get => _itemSize;
        set
        {
            if (_itemSize != value)
            {
                _itemSize = value;
                PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(ItemSize)));
            }
        }
    }
    private double _itemSize;
    

    Egenskapen ItemSize exponerar värdet för ett privat _itemSize-fält. Om du använder ett bakgrundsfält som det här kan egenskapen kontrollera om ett nytt värde är detsamma som det gamla värdet innan det skapar en potentiellt onödig PropertyChanged händelse.

    Själva händelsen utlöses av metoden Invoke. Frågetecknet kontrollerar om PropertyChanged-händelsen är null, det vill säga om några händelsehanterare har lagts till än. Varje enkelriktad eller dubbelriktad bindning lägger till en händelsehanterare i bakgrunden, men om ingen lyssnar kommer inget mer att hända här. Om PropertyChanged inte är null anropas dock Invoke med en referens till händelsekällan (själva sidan, som representeras av nyckelordet this) och ett event-args--objekt som anger namnet på egenskapen. Med den här informationen informeras alla enkelriktade eller dubbelriktade bindningar till egenskapen ItemSize om eventuella ändringar så att de kan uppdatera det bundna användargränssnittet.

  4. I MainPage.xaml hittar du DataTemplate med namnet ImageGridView_DefaultItemTemplate och ersätter Height- och Width-värdena för Grid-kontrollen överst i mallen. (Om du gjorde kontroll-till-kontroll-bindningen i föregående del av den här självstudien är de enda ändringarna att ersätta Value med ItemSize och ZoomSlider med page. Se till att göra detta för både Height och Width!)

    före

    <DataTemplate x:Key="ImageGridView_DefaultItemTemplate"
                  x:DataType="local:ImageFileInfo">
        <Grid Height="{Binding Value, ElementName=ZoomSlider}"
            Width="{Binding Value, ElementName=ZoomSlider}"
            Margin="{StaticResource LargeItemMargin}">
    

    efter

    <DataTemplate x:Key="ImageGridView_DefaultItemTemplate"
                  x:DataType="local:ImageFileInfo">
        <Grid Height="{Binding ItemSize, ElementName=page}"
              Width="{Binding ItemSize, ElementName=page}"
              Margin="{StaticResource LargeItemMargin}">
    

Nu när användargränssnittet kan svara på ItemSize ändringar måste du faktiskt göra vissa ändringar. Som tidigare nämnts beräknas värdet ItemSize från det aktuella tillståndet för olika användargränssnittskontroller, men beräkningen måste utföras när dessa kontroller ändrar tillstånd. För att göra detta använder du händelsebindning så att vissa ändringar i användargränssnittet anropar en hjälpmetod som uppdaterar ItemSize.

Uppdatera värdet på egenskapen ItemSize

  1. Lägg till metoden DetermineItemSize i MainPage.xaml.cs.

    private void DetermineItemSize()
    {
        if (FitScreenToggle != null
            && FitScreenToggle.IsOn == true
            && ImageGridView != null
            && ZoomSlider != null)
        {
            // The 'margins' value represents the total of the margins around the
            // image in the grid item. 8 from the ItemTemplate root grid + 8 from
            // the ItemContainerStyle * (Right + Left). If those values change,
            // this value needs to be updated to match.
            int margins = (int)this.Resources["LargeItemMarginValue"] * 4;
            double gridWidth = ImageGridView.ActualWidth -
                (int)this.Resources["DefaultWindowSidePaddingValue"];
            double ItemWidth = ZoomSlider.Value + margins;
            // We need at least 1 column.
            int columns = (int)Math.Max(gridWidth / ItemWidth, 1);
    
            // Adjust the available grid width to account for margins around each item.
            double adjustedGridWidth = gridWidth - (columns * margins);
    
            ItemSize = (adjustedGridWidth / columns);
        }
        else
        {
            ItemSize = ZoomSlider.Value;
        }
    }
    
  2. I MainPage.xaml navigerar du längst upp i filen och lägger till en SizeChanged händelsebindning till Page-elementet.

    Före:

    <Page x:Name="page"
    

    Efter:

    <Page x:Name="page"
          SizeChanged="{x:Bind DetermineItemSize}"
    
  3. Leta upp Slider med namnet ZoomSlider (i avsnittet Page.Resources) och lägg till en ValueChanged händelsebindning.

    Före:

    <Slider x:Name="ZoomSlider"
    

    Efter:

    <Slider x:Name="ZoomSlider"
            ValueChanged="{x:Bind DetermineItemSize}"
    
  4. Leta reda på ToggleSwitch som heter FitScreenToggle och lägg till en händelsebindning för Toggled.

    Före:

    <ToggleSwitch x:Name="FitScreenToggle"
    

    Efter:

    <ToggleSwitch x:Name="FitScreenToggle"
                  Toggled="{x:Bind DetermineItemSize}"
    

Kör appen och använd zoomreglaget samt Anpassa till skärm växlingsknappen för att ändra måtten för bildmallen. Som du ser möjliggör de senaste ändringarna en mer förfinad zoom-/storleksändringsupplevelse samtidigt som koden hålls väl organiserad.

Appen körs med skärmpassning aktiverat

Anmärkning

Prova att lägga till en TextBlock efter ZoomSlider och binda egenskapen Text till egenskapen ItemSize. Eftersom den inte finns i en datamall kan du använda x:Bind i stället för Binding som i föregående ItemSize bindningar.

Del 5: Aktivera användarredigeringar

Här skapar du tvåvägsbindningar så att användarna kan uppdatera värden, inklusive bildrubrik, klassificering och olika visuella effekter.

För att uppnå detta uppdaterar du den befintliga DetailPage, som ger envisning av en enda bild, zoomkontroll och redigeringsgränssnitt.

Först måste du dock bifoga DetailPage så att appen navigerar till den när användaren klickar på en bild i gallerivyn.

Bifoga detaljsida

  1. I MainPage.xaml hittar du GridView med namnet ImageGridView. Om du vill göra objekten klickbara anger du IsItemClickEnabled till True och lägger till en ItemClick händelsehanterare.

    Tips/Råd

    Om du skriver in ändringen nedan i stället för att kopiera/klistra in visas ett IntelliSense-popup-fönster med texten "<New Event Handler>". Om du trycker på tabbtangenten, fylls värdet automatiskt i med ett standardnamn för metodhanteraren, och metoden som visas i nästa steg skapas automatiskt som en stub. Du kan sedan trycka på F12 för att navigera till metoden i koden bakom.

    Före:

    <GridView x:Name="ImageGridView">
    

    Efter:

    <GridView x:Name="ImageGridView"
              IsItemClickEnabled="True"
              ItemClick="ImageGridView_ItemClick">
    

    Anmärkning

    Vi använder en konventionell händelsehanterare här i stället för ett x:Bind-uttryck. Det beror på att vi måste se händelsedata, som du ser härnäst.

  2. I MainPage.xaml.cs lägger du till händelsehanteraren (eller fyller i den om du använde tipset i det senaste steget).

    private void ImageGridView_ItemClick(object sender, ItemClickEventArgs e)
    {
        this.Frame.Navigate(typeof(DetailPage), e.ClickedItem);
    }
    

    Den här metoden navigerar helt enkelt till informationssidan och skickar det klickade objektet, vilket är ett ImageFileInfo objekt som används av DetailPage.OnNavigatedTo för att initiera sidan. Du behöver inte implementera den metoden i den här självstudien, men du kan ta en titt för att se vad den gör.

  3. (Valfritt) Ta bort eller kommentera bort eventuella kontroller som du har lagt till i tidigare uppspelningspunkter som fungerar med den markerade bilden. Att behålla dem kommer inte att skada något, men det är nu mycket svårare att välja en bild utan att gå till detaljsidan.

Nu när du har anslutit de två sidorna kör du appen och tar en titt. Allt fungerar förutom kontrollerna i redigeringsfönstret, som inte svarar när du försöker ändra värdena.

Som du ser visar textrutan rubrik och låter dig skriva in ändringar. Du måste ändra fokus till en annan kontroll för att genomföra ändringarna, men rubriken i det övre vänstra hörnet på skärmen uppdateras inte ännu.

Alla kontroller är redan bundna med de enkla x:Bind uttryck som vi beskrev i del 1. Om du kommer ihåg innebär det att de alla är engångsbindningar, vilket förklarar varför ändringar i värdena inte registreras. För att åtgärda detta är det enda vi behöver göra att göra dem till dubbelriktade bindningar.

Gör redigeringskontrollerna interaktiva

  1. I DetailPage.xaml, hitta TextBlock med namnet TitleTextBlock och därefter kontrollen RatingControl, och uppdatera deras x:Bind uttryck så att de inkluderar Mode=TwoWay.

    Före:

    <TextBlock x:Name="TitleTextBlock"
               Text="{x:Bind item.ImageTitle}"
               ... >
    <muxc:RatingControl Value="{x:Bind item.ImageRating}"
                            ... >
    

    Efter:

    <TextBlock x:Name="TitleTextBlock"
               Text="{x:Bind item.ImageTitle, Mode=TwoWay}"
               ... >
    <muxc:RatingControl Value="{x:Bind item.ImageRating, Mode=TwoWay}"
                            ... >
    
  2. Gör samma sak för alla effektreglage som kommer efter betygskontrollen.

    <Slider Header="Exposure"    ... Value="{x:Bind item.Exposure, Mode=TwoWay}" ...
    <Slider Header="Temperature" ... Value="{x:Bind item.Temperature, Mode=TwoWay}" ...
    <Slider Header="Tint"        ... Value="{x:Bind item.Tint, Mode=TwoWay}" ...
    <Slider Header="Contrast"    ... Value="{x:Bind item.Contrast, Mode=TwoWay}" ...
    <Slider Header="Saturation"  ... Value="{x:Bind item.Saturation, Mode=TwoWay}" ...
    <Slider Header="Blur"        ... Value="{x:Bind item.Blur, Mode=TwoWay}" ...
    

Dubbelriktade läge, som man kan förvänta sig, innebär att data flyttas i båda riktningarna när det finns ändringar på någon av sidorna.

Precis som de envägsbindningar som beskrevs tidigare uppdaterar dessa tvåvägsbindningar nu användargränssnittet när de bundna egenskaperna ändras, tack vare INotifyPropertyChanged implementeringen i klassen ImageFileInfo. Med tvåvägsbindning flyttas dock värdena också från användargränssnittet till de bundna egenskaperna när användaren interagerar med kontrollen. Inget mer behövs på XAML-sidan.

Kör appen och prova redigeringskontrollerna. Som du ser påverkar det nu bildvärdena när du gör en ändring, och ändringarna sparas när du går tillbaka till huvudsidan.

Del 6: Formatera värden via funktionsbindning

Ett sista problem kvarstår. När du flyttar effektreglagen ändras inte etiketterna bredvid dem.

Effektreglage med standardetikettvärden

Den sista delen i den här självstudien är att lägga till bindningar som formaterar skjutreglagets värden för visning.

Anslut etiketterna till effektreglaget och formatera värdena för visning

  1. Leta upp TextBlock efter skjutreglaget Exposure och ersätt Text-värdet med det bindningsuttryck som visas här.

    Före:

    <Slider Header="Exposure" ... />
    <TextBlock ... Text="0.00" />
    

    Efter:

    <Slider Header="Exposure" ... />
    <TextBlock ... Text="{x:Bind item.Exposure.ToString('N', culture), Mode=OneWay}" />
    

    Detta kallas för en funktionsbindning eftersom du binder till returvärdet av en metod. Metoden måste vara tillgänglig via sidans bakomliggande kod eller typen x:DataType om du är i en datamall. I det här fallet är metoden den välbekanta .NET-ToString-metoden, som nås via objektegenskapen på sidan och sedan genom objektets Exposure-egenskap. (Detta illustrerar hur du kan binda till metoder och egenskaper som är djupt kapslade i en anslutningskedja.)

    Funktionsbindning är ett idealiskt sätt att formatera värden för visning eftersom du kan skicka in andra bindningskällor som metodargument, och bindningsuttrycket lyssnar efter ändringar i dessa värden som förväntat med enkelriktade lägen. I det här exemplet är argumentet kultur en referens till ett oföränderligt fält som implementeras i bakomliggande kod, men det kan lika gärna ha varit en egenskap som utlöser PropertyChanged händelser. I så fall skulle eventuella ändringar av egenskapsvärdet göra att x:Bind-uttrycket anropar ToString med det nya värdet och sedan uppdaterar användargränssnittet med resultatet.

  2. Gör samma sak för de TextBlocksom märker de andra effektreglagen.

    <Slider Header="Temperature" ... />
    <TextBlock ... Text="{x:Bind item.Temperature.ToString('N', culture), Mode=OneWay}" />
    
    <Slider Header="Tint" ... />
    <TextBlock ... Text="{x:Bind item.Tint.ToString('N', culture), Mode=OneWay}" />
    
    <Slider Header="Contrast" ... />
    <TextBlock ... Text="{x:Bind item.Contrast.ToString('N', culture), Mode=OneWay}" />
    
    <Slider Header="Saturation" ... />
    <TextBlock ... Text="{x:Bind item.Saturation.ToString('N', culture), Mode=OneWay}" />
    
    <Slider Header="Blur" ... />
    <TextBlock ... Text="{x:Bind item.Blur.ToString('N', culture), Mode=OneWay}" />
    

Nu när du kör appen fungerar allt, inklusive skjutreglageetiketterna.

Effektreglage med arbetsetiketter

Slutsats

Den här självstudien har gett dig en inblick i databindning och visat några av de tillgängliga funktionerna. Ett varningens ord innan vi avslutar: allt kan inte bindas och ibland är de värden som du försöker ansluta till inte kompatibla med de egenskaper som du försöker binda. Det finns mycket flexibilitet i bindning, men det fungerar inte i alla situationer.

Ett exempel på ett problem som inte åtgärdas med bindning är när en kontroll inte har några lämpliga egenskaper att binda till, som med detaljsidans zoomfunktion. Det här zoomreglaget måste interagera med ScrollViewer som visar bilden, men ScrollViewer kan bara uppdateras via dess ChangeView-metod. I det här fallet använder vi konventionella händelsehanterare för att hålla ScrollViewer och zoomreglaget synkroniserat. Mer information finns i metoderna ZoomSlider_ValueChanged och MainImageScroll_ViewChanged i DetailPage.

Bindning är dock ett kraftfullt och flexibelt sätt att förenkla koden och hålla användargränssnittslogik åtskild från datalogik. Detta gör det mycket enklare för dig att justera båda sidor av den här klyftan samtidigt som du minskar risken för att införa buggar på andra sidan.

Ett exempel på gränssnitt och dataavgränsning är med egenskapen ImageFileInfo.ImageTitle. Den här egenskapen (och egenskapen ImageRating) skiljer sig något från den ItemSize egenskap som du skapade i del 4 eftersom värdet lagras i filmetadata (exponeras via ImageProperties typ) i stället för i ett fält. Dessutom returnerar ImageTitle värdet ImageName (inställt på filnamnet) om det inte finns någon rubrik i filmetadata.

public string ImageTitle
{
    get => String.IsNullOrEmpty(ImageProperties.Title) ? ImageName : ImageProperties.Title;
    set
    {
        if (ImageProperties.Title != value)
        {
            ImageProperties.Title = value;
            var ignoreResult = ImageProperties.SavePropertiesAsync();
            OnPropertyChanged();
        }
    }
}

Som du ser uppdaterar settern egenskapen ImageProperties.Title och anropar sedan SavePropertiesAsync för att skriva det nya värdet till filen. (Det här är en asynkron metod, men vi kan inte använda nyckelordet await i en egenskap – och det bör du inte heller eftersom getters och setters bör slutföras omedelbart. Istället anropar du metoden och ignorerar det Task-objekt som den returnerar.)

Gå vidare

Nu när du har slutfört det här labbet har du tillräckligt med bindningskunskap för att lösa ett problem på egen hand.

Som du kanske har märkt återställs zoomnivån på informationssidan automatiskt när du navigerar bakåt och väljer sedan samma bild igen. Kan du ta reda på hur du bevarar och återställer zoomnivån för varje bild individuellt? Lycka till!

Du bör ha all information du behöver i den här självstudien, men om du behöver mer vägledning är databindningsdokumenten bara ett klick bort. Börja här: