Condividi tramite


Creare data binding

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.

Screenshot della pagina principale dell'app PhotoLab che mostra una visualizzazione della raccolta foto con i metadati dell'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.

Screenshot della pagina dei dettagli dell'app PhotoLab che mostra una singola foto con opzioni di modifica.

Prerequisiti

Parte 0: Ottenere il codice di avvio da GitHub

Per questa esercitazione si inizia con una versione semplificata dell'esempio PhotoLab.

  1. Passare alla pagina GitHub per l'esempio: https://github.com/Microsoft/Windows-appsample-photo-lab.

  2. Successivamente, è necessario clonare o scaricare l'esempio. Selezionare il pulsante clona o scarica. Viene visualizzato un sottomenu. menu Clone or download (Clona o scarica) nella pagina GitHub dell'esempio PhotoLab

    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.

  3. Fare doppio clic Photolab.sln per 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

  1. Aprire la xaml-basics-starting-points\data-binding cartella e avviare il PhotoLab.sln file in Visual Studio.

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

    Esecuzione dell'app con immagini segnaposto e di testo

  3. Aprire MainPage.xaml e cercare un DataTemplate denominato ImageGridView_DefaultItemTemplate. Aggiornerai questo modello per usare le associazioni di dati.

    prima:

    <DataTemplate x:Key="ImageGridView_DefaultItemTemplate">
    

    Il x:Key valore viene utilizzato da ImageGridView per selezionare questo modello per la visualizzazione di oggetti dati.

  4. Aggiungere un x:DataType valore al modello.

    dopo:

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

    x:DataType indica a quale tipo è destinato questo modello. In questo caso, si tratta di un modello per la ImageFileInfo classe (dove local: indica lo spazio dei nomi locale, come definito in una dichiarazione xmlns nella parte superiore del file).

    È necessario x:DataType quando si usano x:Bind espressioni in un modello di dati, come descritto di seguito.

  5. Cerca nella DataTemplatel'elemento Image denominato ItemImage e sostituisci il suo valore Source come illustrato.

    prima:

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

    dopo:

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

    x:Name identifica 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 è la ImageFileInfo.ImageSource proprietà .

    Annotazioni

    Il x:Bind valore consente inoltre all'editor di conoscere il tipo di dati, quindi è possibile usare IntelliSense invece di digitare il nome della proprietà in un'espressione x:Bind . Prova il codice appena incollato: posiziona il cursore subito dopo x:Bind e premi la barra spaziatrice per visualizzare un elenco di proprietà a cui puoi collegarti.

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

Esecuzione dell'app con immagini e testo reali anziché segnaposto

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.

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

  1. In MainPage.xaml.cs trovare il metodo GetItemsAsync e rimuovere il codice che imposta ItemsSource.

    prima:

    ImageGridView.ItemsSource = Images;
    

    dopo:

    // Replaced with XAML binding:
    // ImageGridView.ItemsSource = Images;
    
  2. In MainPage.xaml, trova il GridView denominato ImageGridView e aggiungi un attributo ItemsSource. Per il valore, usare un'espressione x:Bind che fa riferimento alla Images proprietà implementata nel code-behind.

    prima:

    <GridView x:Name="ImageGridView"
    

    dopo:

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

    La Images proprietà è di tipo ObservableCollection<ImageFileInfo>, quindi i singoli elementi visualizzati in GridView sono di tipo ImageFileInfo. Questo tipo corrisponde al x:DataType valore 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

  1. In MainPage.xaml trovare il CommandBar denominato 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 Click valore 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 con x: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.

  2. In MainPage.xaml.cs aggiungere il DeleteSelectedImage metodo .

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

    Questo metodo elimina semplicemente l'immagine selezionata dalla Images raccolta.

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 DataTemplate denominato ImageGridView_DefaultItemTemplate e sostituisci i valori **Height** e Width del controllo Grid in 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.

App in esecuzione con slider di zoom che mostra

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

  1. In MainPage.xaml.cs modificare la firma della MainPage classe in modo che implementi l'interfaccia INotifyPropertyChanged .

    prima:

    public sealed partial class MainPage : Page
    

    dopo:

    public sealed partial class MainPage : Page, INotifyPropertyChanged
    

    In questo modo si informa il sistema di associazione che MainPage ha un evento PropertyChanged (aggiunto in seguito) a cui i binding possono ascoltare per aggiornare l'interfaccia utente.

  2. Aggiungere un PropertyChanged evento alla MainPage classe .

    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.

  3. Aggiungere una proprietà ItemSize e generare l'evento PropertyChanged nel 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à ItemSize espone il valore di un campo _itemSize privato. 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 necessario PropertyChanged .

    L'evento stesso viene generato dal metodo Invoke. Il punto interrogativo controlla se l'evento PropertyChanged è 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. Se PropertyChanged non è null, tuttavia, Invoke viene chiamato con un riferimento all'origine evento (la pagina stessa, rappresentata dalla this parola chiave ) e un oggetto event-args che indica il nome della proprietà. Con queste informazioni, qualsiasi binding unidirezionale o bidirezionale alla proprietà ItemSize verrà informato di eventuali modifiche in modo che possano aggiornare l'interfaccia utente associata.

  4. In MainPage.xaml trovare il DataTemplate denominato ImageGridView_DefaultItemTemplate e sostituire i valori Height e Width del controllo Grid nella parte superiore del modello. Se hai effettuato l'associazione da controllo a controllo nella parte precedente di questo tutorial, le uniche modifiche consistono nel sostituire Value con ItemSize e ZoomSlider con page. Assicurati di farlo sia per Height che per Width!

    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

  1. Aggiungere il DetermineItemSize metodo 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;
        }
    }
    
  2. In MainPage.xaml, vai alla parte superiore del file e aggiungi un'associazione di eventi SizeChanged all'elemento Page.

    prima:

    <Page x:Name="page"
    

    dopo:

    <Page x:Name="page"
          SizeChanged="{x:Bind DetermineItemSize}"
    
  3. Trova il Slider chiamato ZoomSlider (nella sezione Page.Resources) e aggiungi un'associazione di eventi ValueChanged.

    prima:

    <Slider x:Name="ZoomSlider"
    

    dopo:

    <Slider x:Name="ZoomSlider"
            ValueChanged="{x:Bind DetermineItemSize}"
    
  4. Trova il ToggleSwitch denominato FitScreenToggle e aggiungi un'associazione eventi Toggled.

    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.

L'app in esecuzione con l'adattamento allo schermo abilitato

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

  1. In MainPage.xaml trovare il GridView denominato ImageGridView. Per rendere gli elementi cliccabili, impostare IsItemClickEnabled su True e aggiungere un gestore eventi ItemClick.

    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.

  2. 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 ImageFileInfo oggetto utilizzato da DetailPage.OnNavigatedTo per inizializzare la pagina. Non è necessario implementare questo metodo in questa esercitazione, ma è possibile esaminare le operazioni che esegue.

  3. (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

  1. In DetailPage.xaml trovare il TextBlock denominato TitleTextBlock e il controllo RatingControl dopo di esso e aggiornare le espressioni x:Bind in 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}"
                            ... >
    
  2. 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.

dispositivi di scorrimento effetto con valori di etichetta predefiniti

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

  1. Trova il TextBlock dopo il dispositivo di scorrimento Exposure e sostituisci il valore Text con 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:DataType se si usa un modello di dati. In questo caso, il metodo è il metodo .NET ToString familiare a cui si accede tramite la proprietà item della pagina e quindi tramite la Exposure proprietà 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'espressione x:Bind chiami ToString con il nuovo valore e quindi aggiorni l'interfaccia utente con il risultato.

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

dispositivi di scorrimento per effetti con etichette funzionanti

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:Bind non 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:Bind non eredita automaticamente il DataContext di un elemento padre.
  • Two-way bindings: x:Bind supporta 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 implementare INotifyPropertyChanged negli 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: