Nota
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare ad accedere o modificare le directory.
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare a modificare le directory.
Se è stata progettata un'interfaccia utente con immagini segnaposto e testo boilerplate, questa esercitazione illustra come connetterla ai dati reali usando i data binding. Informazioni su come formattare i dati, mantenere sincronizzati l'interfaccia utente e i dati e migliorare la gestibilità del codice.
In questa esercitazione si apprenderà come sostituire il boilerplate con i data binding e creare altri collegamenti diretti tra l'interfaccia utente e i dati. Si apprenderà anche come formattare o convertire i dati per la visualizzazione e mantenere sincronizzati l'interfaccia utente e i dati. Al termine di questa esercitazione, è possibile migliorare la semplicità e l'organizzazione del codice XAML e C#, semplificando la gestione e l'estensione.
Si inizia con una versione semplificata dell'esempio PhotoLab. Questa versione iniziale include il livello di dati completo e i layout di pagina XAML di base, omettendo molte funzionalità per rendere il codice più facile da esplorare. Questa esercitazione non sviluppa l'app completa, quindi ti consigliamo di controllare la versione finale per scoprire funzionalità come animazioni personalizzate e layout adattivi. Puoi trovare la versione finale nella cartella radice del repository Windows-appsample-photo-lab .
L'app di esempio PhotoLab ha due pagine. La pagina principale mostra una galleria di foto, insieme ad alcune informazioni su ogni file immagine.
La pagina dei dettagli visualizza una singola foto dopo averla selezionata. Un menu di modifica a scomparsa consente di modificare, rinominare e salvare l'immagine.
Prerequisiti
- Visual Studio 2019 o versione successiva: scaricare Visual Studio (l'edizione Community è gratuita).
- Windows SDK (10.0.17763.0 o versione successiva): scaricare la versione più recente di Windows SDK (gratuito)
- Windows 10, versione 1809 o successiva
Parte 0: Ottenere il codice di avvio da GitHub
Per questa esercitazione si inizia con una versione semplificata dell'esempio PhotoLab.
Passare alla pagina GitHub per l'esempio: https://github.com/Microsoft/Windows-appsample-photo-lab.
Successivamente, è necessario clonare o scaricare l'esempio. Selezionare il pulsante clona o scarica. Viene visualizzato un sottomenu.
Se non si ha familiarità con GitHub:
a) Selezionare Scarica ZIP e salvare il file in locale. Questa azione scarica un file .zip che contiene tutti i file di progetto necessari.
b. Estrarre il file. Usare Esplora file per passare al file .zip appena scaricato, fare clic con il pulsante destro del mouse e selezionare Estrai tutto....
c. Passare alla copia locale dell'esempio ed entrare nella directory
Windows-appsample-photo-lab-master\xaml-basics-starting-points\data-binding.Se si ha familiarità con GitHub:
a) Clonare il ramo principale del repository localmente.
b. Navigare nella directory
Windows-appsample-photo-lab\xaml-basics-starting-points\data-binding.Fare doppio clic
Photolab.slnper aprire la soluzione in Visual Studio.
Parte 1: Sostituire i segnaposto
In questa sezione vengono creati binding monouso nel codice XAML del modello di dati per visualizzare immagini reali e metadati dell'immagine anziché il contenuto segnaposto.
Le associazioni una tantum sono destinate a dati di sola lettura e non modificabili. Hanno prestazioni elevate e sono facili da creare, in modo da poter visualizzare set di dati di grandi dimensioni nei controlli GridView e ListView.
Sostituire i segnaposto con associazioni monouso
Aprire la
xaml-basics-starting-points\data-bindingcartella e avviare ilPhotoLab.slnfile in Visual Studio.Assicurarsi che il
Solution Platform sia impostato su x86 o x64, non su Arm e quindi eseguire l'app. Questo passaggio mostra lo stato dell'app con segnaposto UI, prima dell'aggiunta dei collegamenti.
di testoAprire MainPage.xaml e cercare un
DataTemplatedenominato ImageGridView_DefaultItemTemplate. Aggiornerai questo modello per usare le associazioni di dati.prima:
<DataTemplate x:Key="ImageGridView_DefaultItemTemplate">Il
x:Keyvalore viene utilizzato daImageGridViewper selezionare questo modello per la visualizzazione di oggetti dati.Aggiungere un
x:DataTypevalore al modello.dopo:
<DataTemplate x:Key="ImageGridView_DefaultItemTemplate" x:DataType="local:ImageFileInfo">x:DataTypeindica a quale tipo è destinato questo modello. In questo caso, si tratta di un modello per laImageFileInfoclasse (dovelocal:indica lo spazio dei nomi locale, come definito in una dichiarazione xmlns nella parte superiore del file).È necessario
x:DataTypequando si usanox:Bindespressioni in un modello di dati, come descritto di seguito.Cerca nella
DataTemplatel'elementoImagedenominatoItemImagee sostituisci il suo valoreSourcecome illustrato.prima:
<Image x:Name="ItemImage" Source="/Assets/StoreLogo.png" Stretch="Uniform" />dopo:
<Image x:Name="ItemImage" Source="{x:Bind ImageSource}" Stretch="Uniform" />x:Nameidentifica un elemento XAML in modo da poterti riferire ad esso altrove nello XAML e nel code-behind.le espressioni forniscono un valore a una proprietà dell'interfaccia utente ottenendo il valore da una proprietà dell'oggetto dati . Nei modelli, la proprietà indicata è una proprietà dell'elemento a cui è impostato x:DataType. In questo caso, quindi, l'origine dati è laImageFileInfo.ImageSourceproprietà .Annotazioni
Il
x:Bindvalore consente inoltre all'editor di conoscere il tipo di dati, quindi è possibile usare IntelliSense invece di digitare il nome della proprietà in un'espressionex:Bind. Prova il codice appena incollato: posiziona il cursore subito dopox:Binde premi la barra spaziatrice per visualizzare un elenco di proprietà a cui puoi collegarti.Sostituire i valori degli altri controlli dell'interfaccia utente nello stesso modo. Provare a eseguire questa operazione con IntelliSense invece di copiare/incollare!)
prima:
<TextBlock Text="Placeholder" ... /> <StackPanel ... > <TextBlock Text="PNG file" ... /> <TextBlock Text="50 x 50" ... /> </StackPanel> <muxc:RatingControl Value="3" ... />dopo:
<TextBlock Text="{x:Bind ImageTitle}" ... /> <StackPanel ... > <TextBlock Text="{x:Bind ImageFileType}" ... /> <TextBlock Text="{x:Bind ImageDimensions}" ... /> </StackPanel> <muxc:RatingControl Value="{x:Bind ImageRating}" ... />
Lancia l'app per vedere com'è finora. Niente più segnaposti! Sei partito bene.
Annotazioni
Se si vuole sperimentare ulteriormente, provare ad aggiungere un nuovo textBlock al modello di dati e usare il trucco x:Bind IntelliSense per trovare una proprietà da visualizzare.
Parte 2: Usare il binding per connettere l'interfaccia utente della galleria alle immagini
In questa sezione, vengono creati binding a una sola istanza nel codice XAML della pagina per connettere la visualizzazione galleria alla raccolta di immagini. Queste associazioni sostituiscono il codice procedurale esistente nel code-behind. Si crea anche un pulsante Elimina per vedere come cambia la visualizzazione della raccolta quando si rimuovono immagini dalla raccolta. Allo stesso tempo, si apprenderà come associare eventi ai gestori eventi per una maggiore flessibilità rispetto ai gestori eventi tradizionali.
Tutte le associazioni descritte finora si trovano all'interno di modelli di dati e fanno riferimento alle proprietà della classe indicata dal x:DataType valore . Che ne dici del resto del codice XAML nella tua pagina?
x:Bind le espressioni esterne ai modelli di dati si associano sempre alla pagina stessa. Questo significa che puoi fare riferimento a qualsiasi elemento inserito nel code-behind o dichiarato in XAML, incluse le proprietà personalizzate e le proprietà di altri controlli dell'interfaccia utente nella pagina (purché abbiano il valore x:Name).
Nell'esempio PhotoLab si usa un'associazione come questa per connettere il controllo principale GridView direttamente alla raccolta di immagini, anziché eseguirla nel code-behind. Più avanti vengono visualizzati altri esempi.
Associare il controllo GridView principale all'insieme Immagini
In MainPage.xaml.cs trovare il metodo
GetItemsAsynce rimuovere il codice che impostaItemsSource.prima:
ImageGridView.ItemsSource = Images;dopo:
// Replaced with XAML binding: // ImageGridView.ItemsSource = Images;In MainPage.xaml, trova il
GridViewdenominatoImageGridViewe aggiungi un attributoItemsSource. Per il valore, usare un'espressionex:Bindche fa riferimento allaImagesproprietà implementata nel code-behind.prima:
<GridView x:Name="ImageGridView"dopo:
<GridView x:Name="ImageGridView" ItemsSource="{x:Bind Images}"La
Imagesproprietà è di tipoObservableCollection<ImageFileInfo>, quindi i singoli elementi visualizzati inGridViewsono di tipoImageFileInfo. Questo tipo corrisponde alx:DataTypevalore descritto nella parte 1.
Tutte le associazioni visualizzate in precedenza sono associazioni monouso di sola lettura, ovvero il comportamento predefinito per le espressioni non crittografate x:Bind . I dati vengono caricati solo all'inizializzazione, che rende ideali per associazioni ad alte prestazioni, ideali per supportare più viste complesse di set di dati di grandi dimensioni.
Anche l'associazione ItemsSource appena aggiunta è un binding monouso di sola lettura a un valore di proprietà non modificabile, ma esiste una distinzione importante da fare qui. Il valore non modificabile della Images proprietà è una singola istanza specifica di una raccolta, inizializzata una volta come illustrato di seguito.
private ObservableCollection<ImageFileInfo> Images { get; }
= new ObservableCollection<ImageFileInfo>();
Il Images valore della proprietà non cambia mai, ma poiché la proprietà è di tipo ObservableCollection<T>, il contenuto della raccolta può cambiare e l'associazione rileva automaticamente le modifiche e aggiorna l'interfaccia utente.
Per testare questo comportamento, aggiungere temporaneamente un pulsante che elimina l'immagine attualmente selezionata. Questo pulsante non è nella versione finale perché la selezione di un'immagine consente di passare a una pagina di dettaglio. Tuttavia, il comportamento di ObservableCollection<T> è ancora importante nell'esempio PhotoLab finale perché il codice XAML si inizializza nel costruttore della pagina (tramite la chiamata del metodo InitializeComponent), ma la raccolta Images viene popolata successivamente nel metodo GetItemsAsync.
Aggiungere un pulsante di eliminazione
In MainPage.xaml trovare il
CommandBardenominato MainCommandBar e aggiungere un nuovo pulsante prima del pulsante di zoom. I controlli zoom non funzionano ancora. Questi elementi verranno agganciati nella parte successiva dell'esercitazione.<AppBarButton Icon="Delete" Label="Delete selected image" Click="{x:Bind DeleteSelectedImage}" />Se hai già familiarità con XAML, questo
Clickvalore potrebbe sembrare insolito. Nelle versioni precedenti di XAML dovevi impostarlo su un metodo con una firma del gestore eventi specifica, in genere includendo parametri per il mittente dell'evento e un oggetto di argomenti specifici dell'evento. È comunque possibile usare questa tecnica quando sono necessari gli argomenti dell'evento, ma anche conx:Bind, è possibile connettersi ad altri metodi. Ad esempio, se non sono necessari i dati dell'evento, è possibile connettersi ai metodi senza parametri, come illustrato qui.In MainPage.xaml.cs aggiungere il
DeleteSelectedImagemetodo .private void DeleteSelectedImage() => Images.Remove(ImageGridView.SelectedItem as ImageFileInfo);Questo metodo elimina semplicemente l'immagine selezionata dalla
Imagesraccolta.
Eseguire ora l'app e usare il pulsante per eliminare alcune immagini. Come si può notare, l'interfaccia utente viene aggiornata automaticamente grazie al "data binding" e al tipo "ObservableCollection<T>".
Annotazioni
Questo codice elimina solo l'istanza di ImageFileInfo dalla raccolta Images nell'app in esecuzione. Non elimina il file di immagine dal computer.
Parte 3: Configurare la barra di scorrimento dello zoom
In questa parte, creerai associazioni unidirezionali da un controllo nel modello dati allo slider dello zoom, che si trova all'esterno del modello. Si apprenderà anche che è possibile usare il data binding con molte proprietà del controllo, non solo quelle più ovvie come TextBlock.Text e Image.Source.
Associare il modello di dati immagine al slider dello zoom
Trova il
DataTemplatedenominatoImageGridView_DefaultItemTemplatee sostituisci i valori**Height**eWidthdel controlloGridin cima al modello.Prima
<DataTemplate x:Key="ImageGridView_DefaultItemTemplate" x:DataType="local:ImageFileInfo"> <Grid Height="200" Width="200" Margin="{StaticResource LargeItemMargin}">Dopo
<DataTemplate x:Key="ImageGridView_DefaultItemTemplate" x:DataType="local:ImageFileInfo"> <Grid Height="{Binding Value, ElementName=ZoomSlider}" Width="{Binding Value, ElementName=ZoomSlider}" Margin="{StaticResource LargeItemMargin}">
Hai notato che si tratta di espressioni Binding e non di espressioni x:Bind? Si tratta del vecchio modo di eseguire i data binding ed è per lo più obsoleto.
x:Bind fa quasi tutto ciò che Binding fa, e altro ancora. Tuttavia, quando si usa x:Bind in un modello di dati, viene associato al tipo dichiarato nel x:DataType valore . In che modo associare qualcosa nel modello a qualcosa nel codice XAML della pagina o nel code-behind? È necessario usare un'espressione Binding in vecchio stile.
Le espressioni Binding non riconoscono il valore x:DataType, ma le espressioni Binding hanno valori ElementName che funzionano in modo quasi identico. Questi comunicano al motore di associazione che Binding Value è un'associazione con la proprietà Value dell'elemento specificato nella pagina (cioè, l'elemento con il valore x:Name). Se desideri associare una proprietà nel code-behind, appare qualcosa come {Binding MyCodeBehindProperty, ElementName=page} dove page si riferisce al valore x:Name impostato nell'elemento Page in XAML.
Annotazioni
Per impostazione predefinita, Binding le espressioni sono unidirezionali, ovvero aggiornano automaticamente l'interfaccia utente quando il valore della proprietà associata cambia.
Al contrario, l'impostazione predefinita per x:Bind è unicavolta, il che significa che eventuali modifiche alla proprietà associata vengono ignorate. Si tratta dell'impostazione predefinita perché è l'opzione a prestazioni più elevate e la maggior parte dei binding è per i dati statici di sola lettura.
La lezione qui è che se si usa x:Bind con proprietà che possono modificare i relativi valori, assicurarsi di aggiungere Mode=OneWay o Mode=TwoWay. Nella sezione successiva verranno visualizzati esempi di questo tipo.
Avvia l'applicazione e usa il cursore per modificare le dimensioni del modello immagine. Come si può notare, l'effetto è piuttosto potente senza bisogno di molto codice.
Annotazioni
Per una sfida, provate a legare altre proprietà dell'interfaccia utente alla proprietà dello slider di zoom Value, o ad altri slider che aggiungete dopo lo slider di zoom. Ad esempio, è possibile collegare la proprietà FontSize del TitleTextBlock a un nuovo dispositivo di scorrimento, con un valore predefinito di 24. Assicurarsi di impostare valori minimi e massimi ragionevoli.
Parte 4: Migliorare l'esperienza di zoom
In questa parte si aggiungerà una proprietà ItemSize personalizzata nel code-behind e si creeranno associazioni unidirezionali dal modello di immagine alla nuova proprietà. Il valore ItemSize sarà aggiornato tramite la barra di regolazione dello zoom e da altri fattori, come l'interruttore Adatta allo schermo e le dimensioni della finestra, per offrire un'esperienza più raffinata.
A differenza delle proprietà predefinite del controllo, le proprietà personalizzate non aggiornano automaticamente l'interfaccia utente, anche con associazioni unidirezionale e bidirezionale. Funzionano correttamente con untempo binding, ma se vuoi che le modifiche alle proprietà vengano effettivamente visualizzate nell'interfaccia utente, devi eseguire alcune operazioni.
Creare la proprietà ItemSize in modo che aggiorni l'interfaccia utente
In MainPage.xaml.cs modificare la firma della
MainPageclasse in modo che implementi l'interfacciaINotifyPropertyChanged.prima:
public sealed partial class MainPage : Pagedopo:
public sealed partial class MainPage : Page, INotifyPropertyChangedIn questo modo si informa il sistema di associazione che
MainPageha un eventoPropertyChanged(aggiunto in seguito) a cui i binding possono ascoltare per aggiornare l'interfaccia utente.Aggiungere un
PropertyChangedevento allaMainPageclasse .public event PropertyChangedEventHandler PropertyChanged;Questo evento fornisce l'implementazione completa richiesta dall'interfaccia
INotifyPropertyChanged. Tuttavia, affinché abbia alcun effetto, è necessario generare in modo esplicito l'evento nelle proprietà personalizzate.Aggiungere una proprietà
ItemSizee generare l'eventoPropertyChangednel relativo setter.public double ItemSize { get => _itemSize; set { if (_itemSize != value) { _itemSize = value; PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(ItemSize))); } } } private double _itemSize;La proprietà
ItemSizeespone il valore di un campo_itemSizeprivato. L'uso di un campo sottostante simile a questo consente alla proprietà di verificare se un nuovo valore è uguale al valore precedente prima che generi un evento potenzialmente non necessarioPropertyChanged.L'evento stesso viene generato dal metodo
Invoke. Il punto interrogativo controlla se l'eventoPropertyChangedè Null, ovvero se sono stati aggiunti gestori eventi. Ogni binding unidirezionale o bidirezionale aggiunge un gestore eventi dietro le quinte, ma se nessuno è in ascolto, non succederebbe altro qui. SePropertyChangednon è null, tuttavia,Invokeviene chiamato con un riferimento all'origine evento (la pagina stessa, rappresentata dallathisparola chiave ) e un oggetto event-args che indica il nome della proprietà. Con queste informazioni, qualsiasi binding unidirezionale o bidirezionale alla proprietàItemSizeverrà informato di eventuali modifiche in modo che possano aggiornare l'interfaccia utente associata.In MainPage.xaml trovare il
DataTemplatedenominatoImageGridView_DefaultItemTemplatee sostituire i valoriHeighteWidthdel controlloGridnella parte superiore del modello. Se hai effettuato l'associazione da controllo a controllo nella parte precedente di questo tutorial, le uniche modifiche consistono nel sostituireValueconItemSizeeZoomSliderconpage. Assicurati di farlo sia perHeightche perWidth!Prima
<DataTemplate x:Key="ImageGridView_DefaultItemTemplate" x:DataType="local:ImageFileInfo"> <Grid Height="{Binding Value, ElementName=ZoomSlider}" Width="{Binding Value, ElementName=ZoomSlider}" Margin="{StaticResource LargeItemMargin}">Dopo
<DataTemplate x:Key="ImageGridView_DefaultItemTemplate" x:DataType="local:ImageFileInfo"> <Grid Height="{Binding ItemSize, ElementName=page}" Width="{Binding ItemSize, ElementName=page}" Margin="{StaticResource LargeItemMargin}">
Ora che l'interfaccia utente può rispondere alle ItemSize modifiche, è necessario apportare effettivamente alcune modifiche. Come accennato in precedenza, il ItemSize valore viene calcolato dallo stato corrente di vari controlli dell'interfaccia utente, ma il calcolo deve essere eseguito ogni volta che questi controlli modificano lo stato. A tale scopo, si userà l'associazione di eventi in modo che determinate modifiche all'interfaccia utente chiameranno un metodo helper che aggiorna ItemSize.
Aggiornare il valore della proprietà ItemSize
Aggiungere il
DetermineItemSizemetodo a 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; } }In MainPage.xaml, vai alla parte superiore del file e aggiungi un'associazione di eventi
SizeChangedall'elementoPage.prima:
<Page x:Name="page"dopo:
<Page x:Name="page" SizeChanged="{x:Bind DetermineItemSize}"Trova il
SliderchiamatoZoomSlider(nella sezionePage.Resources) e aggiungi un'associazione di eventiValueChanged.prima:
<Slider x:Name="ZoomSlider"dopo:
<Slider x:Name="ZoomSlider" ValueChanged="{x:Bind DetermineItemSize}"Trova il
ToggleSwitchdenominatoFitScreenTogglee aggiungi un'associazione eventiToggled.prima:
<ToggleSwitch x:Name="FitScreenToggle"dopo:
<ToggleSwitch x:Name="FitScreenToggle" Toggled="{x:Bind DetermineItemSize}"
Eseguire l'app e utilizzare il dispositivo di scorrimento dello zoom e l'interruttore Adatta allo schermo per modificare le dimensioni del modello di immagine, attivando o disattivando quest'ultimo in base alle necessità. Come si può notare, le modifiche più recenti consentono un'esperienza di zoom/ridimensionamento più raffinata mantenendo il codice ben organizzato.
Annotazioni
Per una sfida, prova ad aggiungere un TextBlock dopo il ZoomSlider e a collegare la proprietà Text alla proprietà ItemSize. Poiché non si trova in un modello di dati, è possibile usare x:Bind anziché Binding come nelle associazioni precedenti ItemSize .
Parte 5: Abilitare le modifiche degli utenti
In questo caso si creeranno associazioni bidirezionali per consentire agli utenti di aggiornare i valori, inclusi il titolo dell'immagine, la classificazione e vari effetti visivi.
A tale scopo, si aggiornerà l'oggetto esistente DetailPage, che fornisce un visualizzatore a immagine singola, un controllo zoom e un'interfaccia utente di modifica.
Prima di tutto, tuttavia, è necessario allegare il DetailPage in modo che l'applicazione si navighi verso di esso quando l'utente fa clic su un'immagine nella visualizzazione galleria.
Allegare la Pagina Dettagli
In MainPage.xaml trovare il
GridViewdenominatoImageGridView. Per rendere gli elementi cliccabili, impostareIsItemClickEnabledsuTruee aggiungere un gestore eventiItemClick.Suggerimento
Se digiti la modifica seguente invece di copiare/incollare, verrà visualizzato un popup IntelliSense che indica "<Nuovo gestore eventi>". Se si preme il tasto Tab, il valore verrà riempito con un nome predefinito del gestore del metodo e creerà automaticamente una bozza del metodo illustrato nel passaggio successivo. È quindi possibile premere F12 per passare al metodo nel code-behind.
prima:
<GridView x:Name="ImageGridView">dopo:
<GridView x:Name="ImageGridView" IsItemClickEnabled="True" ItemClick="ImageGridView_ItemClick">Annotazioni
In questo caso viene usato un gestore eventi convenzionale anziché un'espressione x:Bind. Questo perché è necessario visualizzare i dati dell'evento, come illustrato di seguito.
In MainPage.xaml.cs aggiungi il gestore eventi (o completalo, se è stato usato il suggerimento nell'ultimo passaggio).
private void ImageGridView_ItemClick(object sender, ItemClickEventArgs e) { this.Frame.Navigate(typeof(DetailPage), e.ClickedItem); }Questo metodo passa semplicemente alla pagina dei dettagli, passando l'elemento selezionato, che è un
ImageFileInfooggetto utilizzato da DetailPage.OnNavigatedTo per inizializzare la pagina. Non è necessario implementare questo metodo in questa esercitazione, ma è possibile esaminare le operazioni che esegue.(Facoltativo) Eliminare o impostare come commento tutti i controlli aggiunti nei punti di riproduzione precedenti che funzionano con l'immagine attualmente selezionata. Tenerli in giro non farà male, ma ora è molto più difficile selezionare un'immagine senza passare alla pagina dei dettagli.
Dopo aver connesso le due pagine, avvia l'app e dai un'occhiata in giro. Tutto funziona tranne i controlli nel riquadro di modifica, che non rispondono quando si tenta di modificare i valori.
Come si può notare, la casella di testo del titolo visualizza il titolo e consente di digitare le modifiche. È necessario spostare lo stato attivo su un altro controllo per confermare le modifiche, ma il titolo nell'angolo superiore sinistro dello schermo non viene ancora aggiornato.
Tutti i controlli sono già associati usando le espressioni semplici x:Bind illustrate nella parte 1. Se si ricorda, ciò significa che sono tutte legature monouso, il che spiega perché le modifiche ai valori non vengono registrate. Per risolvere questo problema, è necessario trasformarli in associazioni bidirezionali.
Rendere interattivi i controlli di modifica
In DetailPage.xaml trovare il
TextBlockdenominato TitleTextBlock e il controllo RatingControl dopo di esso e aggiornare le espressionix:Bindin modo da includere Mode=TwoWay.prima:
<TextBlock x:Name="TitleTextBlock" Text="{x:Bind item.ImageTitle}" ... > <muxc:RatingControl Value="{x:Bind item.ImageRating}" ... >dopo:
<TextBlock x:Name="TitleTextBlock" Text="{x:Bind item.ImageTitle, Mode=TwoWay}" ... > <muxc:RatingControl Value="{x:Bind item.ImageRating, Mode=TwoWay}" ... >Fai la stessa cosa per tutte le barre degli effetti che si trovano dopo il controllo di classificazione.
<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}" ...
La modalità bidirezionale, come si potrebbe prevedere, indica che i dati vengono spostati in entrambe le direzioni ogni volta che sono presenti modifiche su entrambi i lati.
Analogamente alle associazioni unidirezionale descritte in precedenza, queste associazioni bidirezionali aggiorneranno ora l'interfaccia utente ogni volta che le proprietà associate cambiano, grazie all'implementazione INotifyPropertyChanged nella ImageFileInfo classe . Con l'associazione bidirezionale, tuttavia, i valori passeranno anche dall'interfaccia utente alle proprietà associate ogni volta che l'utente interagisce con il controllo. Non è necessario nient'altro sul lato XAML.
Eseguire l'app e provare i controlli di modifica. Come si può notare, quando si apporta una modifica, ora influisce sui valori dell'immagine e tali modifiche vengono mantenute quando si torna alla pagina principale.
Parte 6: Formattare i valori tramite l'associazione di funzioni
Resta un ultimo problema. Quando si spostano i cursori degli effetti, le etichette accanto non cambiano ancora.
La parte finale di questa esercitazione consiste nell'aggiungere associazioni che formattano i valori del dispositivo di scorrimento per la visualizzazione.
Collegare le etichette dello slider degli effetti e formattare i valori per la visualizzazione
Trova il
TextBlockdopo il dispositivo di scorrimentoExposuree sostituisci il valoreTextcon l'espressione di associazione qui mostrata.prima:
<Slider Header="Exposure" ... /> <TextBlock ... Text="0.00" />dopo:
<Slider Header="Exposure" ... /> <TextBlock ... Text="{x:Bind item.Exposure.ToString('N', culture), Mode=OneWay}" />Si tratta di un'associazione di funzioni perché si esegue l'associazione al valore restituito di un metodo. Il metodo deve essere accessibile tramite code-behind della pagina o il tipo
x:DataTypese si usa un modello di dati. In questo caso, il metodo è il metodo .NETToStringfamiliare a cui si accede tramite la proprietà item della pagina e quindi tramite laExposureproprietà dell'elemento. Questo illustra come è possibile eseguire il binding a metodi e proprietà annidati in modo profondo in una catena di connessioni.L'associazione di funzioni è un metodo ideale per formattare i valori per la visualizzazione, poiché consente di passare altre origini di associazione come argomenti del metodo, e l'espressione di associazione reagisce ai cambiamenti di tali valori, come previsto con la modalità unidirezionale. In questo esempio, l'argomento cultura è un riferimento a un campo non modificabile implementato nel code-behind, ma potrebbe essere altrettanto facilmente una proprietà che solleva eventi
PropertyChanged. In tal caso, le modifiche apportate al valore della proprietà farebbero sì che l'espressionex:BindchiamiToStringcon il nuovo valore e quindi aggiorni l'interfaccia utente con il risultato.Eseguire la stessa operazione per i
TextBlockche etichettano gli altri dispositivi di scorrimento degli effetti.<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}" />
Ora, quando avvii l'app, tutto funziona, incluse le etichette del dispositivo di scorrimento.
Differenze tra binding e x:Bind
Quando crei data binding in XAML nelle tue app UWP, puoi scegliere tra Binding e x:Bind. Ecco le differenze principali:
-
x:Bind: fornisce la convalida in fase di compilazione, prestazioni migliori ed è fortemente tipizzata. È più adatto per gli scenari in cui la struttura dei dati è nota in fase di compilazione. -
Binding: offre una valutazione in fase di esecuzione e una maggiore flessibilità per scenari dinamici, ad esempio quando la struttura dei dati viene determinata in fase di esecuzione.
Scenari non supportati da x:Bind
Sebbene x:Bind sia estremamente efficiente, presenta limitazioni in determinati scenari:
-
Strutture di dati dinamiche:
x:Bindnon possono essere usate quando la struttura dei dati viene determinata in fase di esecuzione. -
Associazione da elemento a elemento: l'associazione diretta tra due elementi dell'interfaccia utente non è supportata da
x:Bind. -
Ereditarietà di DataContext: A differenza di
Binding,x:Bindnon eredita automaticamente ilDataContextdi un elemento padre. -
Two-way bindings:
x:Bindsupporta binding bidirezionali, consentendo il flusso delle modifiche dall'interfaccia utente alla proprietà di origine. Affinché l'interfaccia utente venga aggiornata quando la proprietà di origine cambia (in binding unidirezionale o bidirezionale), è necessario implementareINotifyPropertyChangednegli oggetti dati.
Per altri dettagli ed esempi, vedere le risorse seguenti:
Conclusione
Questa esercitazione ha fornito un'idea del data binding e ha mostrato alcune delle funzionalità disponibili. Una parola di cautela prima di concludere: non tutto è associabile e a volte i valori a cui si tenta di connettersi sono incompatibili con le proprietà che si sta tentando di associare. C'è molta flessibilità nel legame, ma non funzionerà in ogni situazione.
Un esempio di problema non risolto dall'associazione consiste nel caso in cui un controllo non abbia proprietà adatte a cui eseguire l'associazione, come con la funzionalità di zoom della pagina dei dettagli. Questo dispositivo di scorrimento dello zoom deve interagire con il ScrollViewer che visualizza l'immagine, ma ScrollViewer può essere aggiornato solo tramite il metodo ChangeView. In questo caso, usiamo i gestori di eventi convenzionali per mantenere sincronizzati il ScrollViewer e lo slider dello zoom; per informazioni dettagliate, vedere i metodi ZoomSlider_ValueChanged e MainImageScroll_ViewChanged in DetailPage.
Tuttavia, il binding è un modo potente e flessibile per semplificare il codice e mantenere la logica dell'interfaccia utente separata dalla logica dei dati. In questo modo sarà molto più facile modificare entrambi i lati di questa divisione riducendo il rischio di introdurre bug dall'altro lato.
Un esempio di interfaccia utente e separazione dei dati è la ImageFileInfo.ImageTitle proprietà . Questa proprietà (e la ImageRating proprietà) è leggermente diversa dalla ItemSize proprietà creata nella parte 4 perché il valore viene archiviato nei metadati del file (esposti tramite il ImageProperties tipo) anziché in un campo. Restituisce anche ImageTitle il valore ImageName (impostato sul nome file) se non è presente alcun titolo nei metadati del file.
public string ImageTitle
{
get => String.IsNullOrEmpty(ImageProperties.Title) ? ImageName : ImageProperties.Title;
set
{
if (ImageProperties.Title != value)
{
ImageProperties.Title = value;
var ignoreResult = ImageProperties.SavePropertiesAsync();
OnPropertyChanged();
}
}
}
Come si può notare, il setter aggiorna la ImageProperties.Title proprietà e quindi chiama SavePropertiesAsync per scrivere il nuovo valore nel file. Questo è un metodo asincrono, ma non possiamo usare la parola chiave await in una proprietà, e non lo si vorrebbe fare perché i getter e i setter delle proprietà devono completarsi immediatamente. Quindi, invece, si chiamerebbe il metodo e si ignorerebbe l'oggetto Task che restituisce.
Andare oltre
Dopo aver completato questo laboratorio, disponi di conoscenze in materia di binding sufficienti per affrontare autonomamente un problema.
Come avrai notato, se modifichi il livello di zoom nella pagina dei dettagli, viene reimpostato automaticamente quando torni indietro e selezioni di nuovo la stessa immagine. È possibile capire come mantenere e ripristinare il livello di zoom per ogni immagine singolarmente? Buona Fortuna!
Dovresti avere tutte le informazioni di cui hai bisogno in questo tutorial, ma se hai bisogno di ulteriori indicazioni, la documentazione sul data binding è solo a un clic di distanza. Inizia qui:
- dell'estensione di markup {x:Bind}
- Associazione dati in profondità