Anteckning
Åtkomst till den här sidan kräver auktorisering. Du kan prova att logga in eller ändra kataloger.
Åtkomst till den här sidan kräver auktorisering. Du kan prova att ändra kataloger.
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.
Detaljsidan visar ett enda foto efter att det har valts. Med en utfälld redigeringsmeny kan fotot ändras, byta namn och sparas.
Förutsättningar
- Visual Studio 2019 eller senare: Ladda ned Visual Studio (Community-utgåvan är kostnadsfri.)
- Windows SDK (10.0.17763.0 eller senare): Ladda ned den senaste Windows SDK(kostnadsfri)
- Windows 10, version 1809 eller senare
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.
Gå till GitHub-sidan för exemplet: https://github.com/Microsoft/Windows-appsample-photo-lab.
Därefter måste du klona eller ladda ned exemplet. Välj knappen Klona eller ladda ned. En undermeny visas.
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.Dubbelklicka på
Photolab.slnfö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
Öppna mappen
xaml-basics-starting-points\data-bindingoch starta filenPhotoLab.slni Visual Studio.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.
Öppna MainPage.xaml och sök efter en
DataTemplatemed 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:Keyanvänds avImageGridViewför att välja den här mallen för att visa dataobjekt.Lägg till ett
x:DataTypevärde i mallen.Efter:
<DataTemplate x:Key="ImageGridView_DefaultItemTemplate" x:DataType="local:ImageFileInfo">x:DataTypeanger vilken typ detta är en mall för. I det här fallet är det en mall för klassenImageFileInfo(därlocal:anger det lokala namnområdet enligt definitionen i en xmlns-deklaration längst upp i filen).x:DataTypekrävs när du använderx:Binduttryck i en datamall enligt beskrivningen nedan.I
DataTemplateletar du reda påImage-elementet med namnetItemImageoch ersätter dessSource-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:Nameidentifierar ett XAML-element så att du kan referera till det någon annanstans i XAML och i koden bakom.x:Binduttryck 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 somx:DataTypehar ställts in på. Så i det här fallet är datakällan egenskapenImageFileInfo.ImageSource.Anmärkning
Värdet
x:Bindlåter också redigeraren veta om datatypen, så att du kan använda IntelliSense i stället för att skriva in egenskapsnamnet i ettx:Binduttryck. Prova det på koden du just klistrade in: placera markören precis efterx:Bindoch tryck på mellanslagstangenten för att se en lista över egenskaper du kan binda till.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.
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.
Del 2: Använd bindning för att ansluta galleriets användargränssnitt till bilderna
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
I MainPage.xaml.cs letar du upp metoden
GetItemsAsyncoch tar bort koden som angerItemsSource.Före:
ImageGridView.ItemsSource = Images;Efter:
// Replaced with XAML binding: // ImageGridView.ItemsSource = Images;I MainPage.xaml hittar du
GridViewmed namnetImageGridViewoch lägger till ettItemsSource-attribut. För värdet använder du ettx:Binduttryck som refererar till egenskapenImagesimplementerad i code-behind.Före:
<GridView x:Name="ImageGridView"Efter:
<GridView x:Name="ImageGridView" ItemsSource="{x:Bind Images}"Egenskapen
Imagesär av typenObservableCollection<ImageFileInfo>, så de enskilda objekt som visas iGridViewär av typenImageFileInfo. Detta matchar detx:DataTypevä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
I MainPage.xaml hittar du
CommandBarmed 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
Clickvä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 medx: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.Lägg till metoden
DeleteSelectedImagei 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
DataTemplatemed namnetImageGridView_DefaultItemTemplateoch ersätt**Height**- ochWidth-värdena förGrid-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.
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
I MainPage.xaml.cs ändrar du signaturen för klassen
MainPageså att den implementerarINotifyPropertyChanged-gränssnittet.Före:
public sealed partial class MainPage : PageEfter:
public sealed partial class MainPage : Page, INotifyPropertyChangedDetta informerar bindningssystemet om att
MainPagehar enPropertyChangedhändelse (läggs till nästa) som bindningar kan lyssna efter för att uppdatera användargränssnittet.Lägg till en
PropertyChanged-händelse tillMainPage-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.Lägg till en
ItemSize-egenskap och utlös händelsenPropertyChangedi 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
ItemSizeexponerar 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ödigPropertyChangedhändelse.Själva händelsen utlöses av metoden
Invoke. Frågetecknet kontrollerar omPropertyChanged-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. OmPropertyChangedinte är null anropas dockInvokemed en referens till händelsekällan (själva sidan, som representeras av nyckelordetthis) och ett event-args--objekt som anger namnet på egenskapen. Med den här informationen informeras alla enkelriktade eller dubbelriktade bindningar till egenskapenItemSizeom eventuella ändringar så att de kan uppdatera det bundna användargränssnittet.I MainPage.xaml hittar du
DataTemplatemed namnetImageGridView_DefaultItemTemplateoch ersätterHeight- ochWidth-värdena förGrid-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ättaValuemedItemSizeochZoomSlidermedpage. Se till att göra detta för bådeHeightochWidth!)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
Lägg till metoden
DetermineItemSizei 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; } }I MainPage.xaml navigerar du längst upp i filen och lägger till en
SizeChangedhändelsebindning tillPage-elementet.Före:
<Page x:Name="page"Efter:
<Page x:Name="page" SizeChanged="{x:Bind DetermineItemSize}"Leta upp
Slidermed namnetZoomSlider(i avsnittetPage.Resources) och lägg till enValueChangedhändelsebindning.Före:
<Slider x:Name="ZoomSlider"Efter:
<Slider x:Name="ZoomSlider" ValueChanged="{x:Bind DetermineItemSize}"Leta reda på
ToggleSwitchsom heterFitScreenToggleoch lägg till en händelsebindning förToggled.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.
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
I MainPage.xaml hittar du
GridViewmed namnetImageGridView. Om du vill göra objekten klickbara anger duIsItemClickEnabledtillTrueoch lägger till enItemClickhä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.
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
ImageFileInfoobjekt 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.(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
I DetailPage.xaml, hitta
TextBlockmed namnet TitleTextBlock och därefter kontrollen RatingControl, och uppdatera derasx:Binduttryck 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}" ... >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.
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
Leta upp
TextBlockefter skjutreglagetExposureoch ersättText-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:DataTypeom 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 objektetsExposure-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
PropertyChangedhändelser. I så fall skulle eventuella ändringar av egenskapsvärdet göra attx:Bind-uttrycket anroparToStringmed det nya värdet och sedan uppdaterar användargränssnittet med resultatet.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.
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: