Case study da Windows Phone Silverlight a UWP: Bookstore2

Questo case study, basato sulle informazioni fornite in Bookstore1, inizia con un'app di Windows Phone Silverlight che visualizza i dati raggruppati in un LongListSelector. Nel modello di visualizzazione, ogni istanza della classe Author rappresenta il gruppo dei libri scritti da tale autore e in LongListSelector è possibile visualizzare l'elenco di libri raggruppati per autore oppure eseguire lo zoom indietro per visualizzare un jump list di autori. La jump list offre una navigazione molto più rapida rispetto allo scorrimento nell'elenco dei libri. Verranno illustrati i passaggi per convertire l'app in un'app UWP (Universal Windows Platform) Windows 10.

Nota Quando si apre Bookstore2Universal_10 in Visual, se viene visualizzato il messaggio "Aggiornamento di Visual Studio richiesto", seguire la procedura per selezionare un controllo della versione della piattaforma di destinazione in TargetPlatformVersion.

Download

Scaricare l'app Windows Phone Silverlight bookstore2WPSL8.

Scaricare l'app Bookstore2Universal_10 Windows 10.

L'app Windows Phone Silverlight

La figura seguente mostra l'aspetto di Bookstore2WPSL8, ovvero l'app da convertire. Si tratta di un LongListSelector a scorrimento verticale di libri raggruppati per autore. È possibile ingrandire la jump list e da qui è possibile tornare a qualsiasi gruppo. Esistono due elementi principali per questa app: il modello visualizzazione che fornisce l'origine dati raggruppati e l'interfaccia utente associata a tale modello di visualizzazione. Come vedremo, entrambe queste parti porta facilmente dalla tecnologia Windows Phone Silverlight alla piattaforma UWP (Universal Windows Platform).

how bookstore2wpsl8 looks

Conversione in un progetto Windows 10

È un'attività molto rapida per creare un nuovo progetto in Visual Studio, copiarvi i file da Bookstore2WPSL8 e includere i file copiati nel nuovo progetto. Per iniziare, creare un nuovo progetto di applicazione vuota (Windows Universal). Denominarlo Bookstore2Universal_10. Questi sono i file da copiare da Bookstore2WPSL8 a Bookstore2Universal_10.

  • Copiare la cartella contenente i file PNG della copertina del libro (la cartella è \Assets\CoverImages). Dopo aver copiato la cartella, in Esplora soluzioni, verificare che l'opzione Mostra tutti i file sia attivata. Fai clic con il pulsante destro del mouse sulla cartella copiata, quindi fare clic su Includi nel progetto. Questo comando è ciò che si intende per "inclusione" di file o cartelle in un progetto. Ogni volta che si copia un file o una cartella, fare clic su Aggiorna in Esplora soluzioni e quindi includere il file o la cartella nel progetto. Non è necessario eseguire questa operazione per i file che si stanno sostituendo nella destinazione.
  • Copiare la cartella contenente il file di origine del modello di visualizzazione (la cartella è \ViewModel).
  • Copiare MainPage.xaml e sostituire il file nella destinazione.

È possibile mantenere app.xaml e App.xaml.cs generati da Visual Studio nel progetto Windows 10.

Modificare il codice sorgente e i file di markup appena copiati e modificare i riferimenti allo spazio dei nomi Bookstore2WPSL8 in Bookstore2Universal_10. Un modo rapido per eseguire questa operazione consiste nell'usare la funzionalità Sostituisci nei file. Nel codice imperativo nel file di origine del modello di visualizzazione sono necessarie queste modifiche di conversione.

  • Passare System.ComponentModel.DesignerProperties a DesignMode e quindi usare il comando Risolvi su di esso. Eliminare la proprietà IsInDesignTool e usare IntelliSense per aggiungere il nome della proprietà corretto: DesignModeEnabled.
  • Usare il comando Resolve in ImageSource.
  • Usare il comando Resolve in BitmapImage.
  • Eliminare using System.Windows.Media; e using System.Windows.Media.Imaging;.
  • Modificare il valore restituito dalla proprietà Bookstore2Universal_10.BookstoreViewModel.AppName da "BOOKSTORE2WPSL8" a "BOOKSTORE2UNIVERSAL".
  • Proprio come per Bookstore1, aggiornare l'implementazione della proprietà BookSku.CoverImage (vedere Binding di un'immagine a un modello di visualizzazione).

In MainPage.xaml sono necessarie queste modifiche iniziali alla conversione.

  • Passare phone:PhoneApplicationPage a Page (incluse le occorrenze nella sintassi dell'elemento proprietà).
  • Eliminare le dichiarazioni del prefisso dello spazio dei nomi phone e shell.
  • Modificare "clr-namespace" in "using" nella dichiarazione del prefisso dello spazio dei nomi rimanente.
  • Eliminare SupportedOrientations="Portrait"e Orientation="Portrait" e configurare Verticale nel manifesto del pacchetto dell'app nel nuovo progetto.
  • Eliminare shell:SystemTray.IsVisible="True".
  • I tipi di convertitori di elementi jump list (presenti nel markup come risorse) sono stati spostati nello spazio dei nomi Windows.UI.Xaml.Controls.Primitives. Aggiungere quindi la dichiarazione di prefisso dello spazio dei nomi Windows_UI_Xaml_Controls_Primitives ed eseguirne il mapping a Windows.UI.Xaml.Controls.Primitives. Nelle risorse del convertitore di elementi jump list modificare il prefisso da phone: a Windows_UI_Xaml_Controls_Primitives:.
  • Proprio come per Bookstore1, sostituire tutti i riferimenti allo stile PhoneTextExtraLargeStyleTextBlock con un riferimento a SubtitleTextBlockStyle, sostituire PhoneTextSubtleStyle con SubtitleTextBlockStyle, sostituire PhoneTextNormalStyle con CaptionTextBlockStyle e sostituire PhoneTextTitle1Style con HeaderTextBlockStyle.
  • Esiste un'eccezione in BookTemplate. Lo stile del secondo TextBlock deve fare riferimento a CaptionTextBlockStyle.
  • Rimuovere l'attributo FontFamily da TextBlock all'interno AuthorGroupHeaderTemplate e impostare lo sfondo del bordo in modo che faccia riferimento a PhoneAccentBrush anziché a SystemControlBackgroundAccentBrush
  • A causa delle modifiche correlate ai pixel di visualizzazione, passare attraverso il markup e moltiplicare qualsiasi dimensione a dimensione fissa (margini, larghezza, altezza e così via) per 0,8.

Sostituzione di LongListSelector

La sostituzione di LongListSelector con un SemanticZoom eseguirà diversi passaggi, quindi è possibile iniziare. Un LongListSelector viene associato direttamente all'origine dati raggruppata, ma un oggetto SemanticZoom contiene controlli ListView o GridView, che si associano indirettamente ai dati tramite un adattatore CollectionViewSource. CollectionViewSource deve essere presente nel markup come risorsa, quindi iniziamo aggiungendolo al markup in MainPage.xaml all'interno di <Page.Resources>.

    <CollectionViewSource
        x:Name="AuthorHasACollectionOfBookSku"
        Source="{Binding Authors}"
        IsSourceGrouped="true"/>

Si noti che l'associazione in LongListSelector.ItemsSource diventa il valore di CollectionViewSource.Source e LongListSelector.IsGroupingEnabled diventa CollectionViewSource.IsSourceGrouped. CollectionViewSource ha un nome (nota: non una chiave, come previsto) in modo che sia possibile associarlo.

Sostituire quindi con phone:LongListSelector questo markup, che ci darà un SemanticZoom preliminare da usare.

    <SemanticZoom>
        <SemanticZoom.ZoomedInView>
            <ListView
                ItemsSource="{Binding Source={StaticResource AuthorHasACollectionOfBookSku}}"
                ItemTemplate="{StaticResource BookTemplate}">
                <ListView.GroupStyle>
                    <GroupStyle
                        HeaderTemplate="{StaticResource AuthorGroupHeaderTemplate}"
                        HidesIfEmpty="True"/>
                </ListView.GroupStyle>
            </ListView>
        </SemanticZoom.ZoomedInView>
        <SemanticZoom.ZoomedOutView>
            <ListView
                ItemsSource="{Binding CollectionGroups, Source={StaticResource AuthorHasACollectionOfBookSku}}"
                ItemTemplate="{StaticResource ZoomedOutAuthorTemplate}"/>
        </SemanticZoom.ZoomedOutView>
    </SemanticZoom>

La nozione di LongListSelector delle modalità elenco semplice e jump list viene risposta rispettivamente nella nozione SemanticZoom di una visualizzazione ingrandita e rimpicciolita. La visualizzazione ingrandita è una proprietà e si imposta tale proprietà su un'istanza di un controllo ListView. In questo caso, anche la visualizzazione ingrandita viene impostata su un controllo ListView e entrambi i controlli ListView sono associati a CollectionViewSource. La visualizzazione ingrandita usa lo stesso modello di elemento, il modello di intestazione di gruppo e l'impostazione HideEmptyGroups (ora denominata HidesIfEmpty) come fa l'elenco flat di LongListSelector. E la visualizzazione ingrandita usa un modello di elemento molto simile a quello all'interno dello stile jump list di LongListSelector (AuthorNameJumpListStyle). Si noti inoltre che la visualizzazione ingrandita viene associata a una proprietà speciale di CollectionViewSource denominata CollectionGroups, ovvero un insieme contenente i gruppi anziché gli elementi.

Non abbiamo più bisogno di AuthorNameJumpListStyle, almeno non tutti. È necessario solo il modello di dati per i gruppi (che sono autori in questa app) nella visualizzazione rimpicciolita. Quindi, eliminiamo lo stile AuthorNameJumpListStyle e lo sostituiamo con questo modello di dati.

   <DataTemplate x:Key="ZoomedOutAuthorTemplate">
        <Border Margin="9.6,0.8" Background="{Binding Converter={StaticResource JumpListItemBackgroundConverter}}">
            <TextBlock Margin="9.6,0,9.6,4.8" Text="{Binding Group.Name}" Style="{StaticResource SubtitleTextBlockStyle}"
            Foreground="{Binding Converter={StaticResource JumpListItemForegroundConverter}}" VerticalAlignment="Bottom"/>
        </Border>
    </DataTemplate>

Si noti che, poiché il contesto dati di questo modello di dati è un gruppo anziché un elemento, viene associato a una proprietà speciale denominata Group.

È ora possibile compilare ed eseguire l'app. Ecco come appare nell'emulatore di dispositivi mobili.

the uwp app on mobile with initial source code changes

Il modello di visualizzazione e le visualizzazioni ingrandita e ingrandita funzionano correttamente, anche se un problema è che è necessario eseguire un po' più di stili e modelli. Ad esempio, gli stili e i pennelli corretti non vengono ancora usati, quindi il testo è invisibile nelle intestazioni di gruppo su cui è possibile fare clic per eseguire lo zoom indietro. Se si esegue l'app in un dispositivo desktop, si vedrà un secondo problema, che è che l'app non adatta ancora la sua interfaccia utente per offrire la migliore esperienza e l'uso dello spazio nei dispositivi più grandi in cui le finestre possono essere potenzialmente molto più grandi dello schermo di un dispositivo mobile. Quindi, nelle prossime sezioni (Applicazione iniziale di stili e modelli, interfaccia utente adattiva e stili finali), verranno risolti questi problemi.

Applicazione iniziale di stili e modelli

Per spaziare bene le intestazioni di gruppo, modificare AuthorGroupHeaderTemplate e impostare un margine di "0,0,0,9.6" sul bordo.

Per spaziare bene gli elementi del libro, modificare BookTemplate e impostare ilMargine a "9.6,0" su entrambi gli oggetti TextBlock.

Per disporre meglio il nome dell'app e il titolo della pagina, all'interno di TitlePanel, rimuovere il margine superiore nel secondo TextBlock impostando il valore su "7.2,0,0,0". E su TitlePanel stesso, impostare il margine su 0 (o qualsiasi valore sembra adatto)

Modificare LayoutRootlo sfondo in "{ThemeResource ApplicationPageBackgroundThemeBrush}".

Interfaccia utente adattiva

Poiché abbiamo iniziato con un'app per telefoni, non c'è da sorprendersi che il layout dell'interfaccia utente dell'app con porting abbia senso solo per i dispositivi di piccole dimensioni e le finestre strette in questa fase del processo. Tuttavia, vorremmo davvero che il layout dell'interfaccia utente si adatti e faccia un uso migliore dello spazio quando l'app è in esecuzione in una finestra ampia (che è possibile solo in un dispositivo con uno schermo grande) e per usarlo solo per usare l'interfaccia utente che abbiamo attualmente quando la finestra dell'app è stretta (che accade su un piccolo dispositivo e possono verificarsi anche in un dispositivo di grandi dimensioni).

Per ottenere questo risultato, è possibile usare la funzionalità Visual State Manager adattiva. Le proprietà sugli elementi visivi verranno impostate in modo che, per impostazione predefinita, l'interfaccia utente sia disposta nello stato stretto usando i modelli usati in questo momento. Si rileverà quindi quando la finestra dell'app è più ampia o uguale a una dimensione specifica (misurata in unità di pixel effettivi) e, in risposta, verranno modificate le proprietà degli elementi visivi in modo da ottenere un layout più grande e più ampio. Queste modifiche alle proprietà verranno inserite in uno stato di visualizzazione e verrà usato un trigger adattivo per monitorare continuamente e determinare se applicare lo stato di visualizzazione o meno, a seconda della larghezza della finestra in pixel effettivi. In questo caso si attiva la larghezza della finestra, ma è anche possibile attivare l'altezza della finestra.

Una larghezza minima della finestra di 548 epx è appropriata per questo caso d'uso perché è la dimensione del dispositivo più piccolo su cui si vuole mostrare il layout wide. Telefono sono in genere inferiori a 548 epx, quindi in un piccolo dispositivo come quello che resteremo nel layout stretto predefinito. In un PC, la finestra verrà avviata per impostazione predefinita abbastanza larga da attivare il passaggio allo stato wide, che visualizzerà 250x250 elementi di dimensioni. Da qui sarà possibile trascinare la finestra abbastanza stretta per visualizzare come minimo due colonne degli elementi 250x250. Eventuale più stretto di quello e il trigger verrà disattivato, lo stato di visualizzazione wide verrà rimosso e il layout stretto predefinito sarà effettivo.

Prima di affrontare la parte adattiva di Visual State Manager, è necessario progettare lo stato wide e questo significa aggiungere alcuni nuovi elementi visivi e modelli al markup. Questa procedura descrive come eseguire questa operazione. In base alle convenzioni di denominazione per elementi visivi e modelli, si includerà la parola "wide" nel nome di qualsiasi elemento o modello per lo stato wide. Se un elemento o un modello non contiene la parola "wide", è possibile presupporre che si tratti dello stato stretto, ovvero lo stato predefinito e i cui valori di proprietà sono impostati come valori locali negli elementi visivi della pagina. Solo i valori delle proprietà per lo stato wide vengono impostati tramite uno stato di visualizzazione effettivo nel markup.

  • Creare una copia del controllo SemanticZoom nel markup e impostare x:Name="narrowSeZo" sulla copia. Nell'originale impostare x:Name="wideSeZo" e impostare Visibility="Collapsed" anche in modo che la larghezza non sia visibile per impostazione predefinita.
  • In wideSeZo modificare ListView in GridView sia nella visualizzazione ingrandita che nella visualizzazione rimpicciolita.
  • Creare una copia di queste tre risorse AuthorGroupHeaderTemplate, ZoomedOutAuthorTemplate e BookTemplate aggiungere la parola Wide alle chiavi delle copie. Aggiornare wideSeZo anche in modo che faccia riferimento alle chiavi di queste nuove risorse.
  • Sostituire il contenuto di AuthorGroupHeaderTemplateWide con <TextBlock Style="{StaticResource SubheaderTextBlockStyle}" Text="{Binding Name}"/>.
  • Sostituire il contenuto di ZoomedOutAuthorTemplateWide con:
    <Grid HorizontalAlignment="Left" Width="250" Height="250" >
        <Border Background="{StaticResource ListViewItemPlaceholderBackgroundThemeBrush}"/>
        <StackPanel VerticalAlignment="Bottom" Background="{StaticResource ListViewItemOverlayBackgroundThemeBrush}">
          <TextBlock Foreground="{StaticResource ListViewItemOverlayForegroundThemeBrush}"
              Style="{StaticResource SubtitleTextBlockStyle}"
            Height="80" Margin="15,0" Text="{Binding Group.Name}"/>
        </StackPanel>
    </Grid>
  • Sostituire il contenuto di BookTemplateWide con:
    <Grid HorizontalAlignment="Left" Width="250" Height="250">
        <Border Background="{StaticResource ListViewItemPlaceholderBackgroundThemeBrush}"/>
        <Image Source="{Binding CoverImage}" Stretch="UniformToFill"/>
        <StackPanel VerticalAlignment="Bottom" Background="{StaticResource ListViewItemOverlayBackgroundThemeBrush}">
            <TextBlock Style="{StaticResource SubtitleTextBlockStyle}"
                Foreground="{StaticResource ListViewItemOverlaySecondaryForegroundThemeBrush}"
                TextWrapping="NoWrap" TextTrimming="CharacterEllipsis"
                Margin="12,0,24,0" Text="{Binding Title}"/>
            <TextBlock Style="{StaticResource CaptionTextBlockStyle}" Text="{Binding Author.Name}"
                Foreground="{StaticResource ListViewItemOverlaySecondaryForegroundThemeBrush}" TextWrapping="NoWrap"
                TextTrimming="CharacterEllipsis" Margin="12,0,12,12"/>
        </StackPanel>
    </Grid>
  • Per lo stato wide, i gruppi nella visualizzazione ingrandita avranno bisogno di più spazio di respirazione verticale intorno a essi. La creazione e il riferimento a un modello di pannello elementi consentirà di ottenere i risultati desiderati. Ecco l'aspetto del markup.
   <ItemsPanelTemplate x:Key="ZoomedInItemsPanelTemplate">
        <ItemsWrapGrid Orientation="Horizontal" GroupPadding="0,0,0,20"/>
    </ItemsPanelTemplate>
    ...

    <SemanticZoom x:Name="wideSeZo" ... >
        <SemanticZoom.ZoomedInView>
            <GridView
            ...
            ItemsPanel="{StaticResource ZoomedInItemsPanelTemplate}">
            ...
  • Aggiungere infine il markup di Visual State Manager appropriato come primo elemento figlio di LayoutRoot.
    <Grid x:Name="LayoutRoot" ... >
        <VisualStateManager.VisualStateGroups>
            <VisualStateGroup>
                <VisualState x:Name="WideState">
                    <VisualState.StateTriggers>
                        <AdaptiveTrigger MinWindowWidth="548"/>
                    </VisualState.StateTriggers>
                    <VisualState.Setters>
                        <Setter Target="wideSeZo.Visibility" Value="Visible"/>
                        <Setter Target="narrowSeZo.Visibility" Value="Collapsed"/>
                    </VisualState.Setters>
                </VisualState>
            </VisualStateGroup>
        </VisualStateManager.VisualStateGroups>

    ...

Stile finale

Tutto ciò che rimane sono alcune modifiche di stile finali.

  • In AuthorGroupHeaderTemplate impostare Foreground="White" su TextBlock in modo che sia corretto durante l'esecuzione nella famiglia di dispositivi mobili.
  • Aggiungere FontWeight="SemiBold" a TextBlock sia in che ZoomedOutAuthorTemplatein AuthorGroupHeaderTemplate.
  • In narrowSeZo, le intestazioni di gruppo e gli autori nella visualizzazione ingrandita vengono allineate a sinistra invece di essere estese, quindi è possibile procedere con tale operazione. Verrà creato un oggetto HeaderContainerStyle per la visualizzazione ingrandita con HorizontalContentAlignment impostato a Stretch. Verrà inoltre creato un ItemContainerStyle per la visualizzazione rimpicciolita contenente lo stesso Setter. Ecco qual è l'aspetto.
   <Style x:Key="AuthorGroupHeaderContainerStyle" TargetType="ListViewHeaderItem">
        <Setter Property="HorizontalContentAlignment" Value="Stretch"/>
    </Style>

    <Style x:Key="ZoomedOutAuthorItemContainerStyle" TargetType="ListViewItem">
        <Setter Property="HorizontalContentAlignment" Value="Stretch"/>
    </Style>

    ...

    <SemanticZoom x:Name="narrowSeZo" ... >
        <SemanticZoom.ZoomedInView>
            <ListView
            ...
                <ListView.GroupStyle>
                    <GroupStyle
                    ...
                    HeaderContainerStyle="{StaticResource AuthorGroupHeaderContainerStyle}"
                    ...
        <SemanticZoom.ZoomedOutView>
            <ListView
                ...
                ItemContainerStyle="{StaticResource ZoomedOutAuthorItemContainerStyle}"
                ...

L'ultima sequenza di operazioni di stile lascia l'app simile alla seguente.

the ported windows 10 app running on a desktop device, zoomed-in view, two sizes of window

App di Windows 10 con conversione in esecuzione su un dispositivo desktop, visualizzazione ingrandita, due dimensioni della finestra the ported windows 10 app running on a desktop device, zoomed-out view, two sizes of window

App di Windows 10 con conversione in esecuzione su un dispositivo desktop, visualizzazione rimpicciolita, due dimensioni della finestra

the ported windows 10 app running on a mobile device, zoomed-in view

App di Windows 10 con conversione in esecuzione su un dispositivo mobile, visualizzazione ingrandita

the ported windows 10 app running on a mobile device, zoomed-out view

App di Windows 10 con conversione in esecuzione su un dispositivo mobile, visualizzazione rimpicciolita

Rendere il modello di visualizzazione più flessibile

Questa sezione contiene un esempio di strutture che ci aprono in virtù della spostamento della nostra app per l'uso della piattaforma UWP. Qui vengono illustrati i passaggi facoltativi che è possibile seguire per rendere il modello di visualizzazione più flessibile quando si accede tramite CollectionViewSource. Il modello di visualizzazione (il file di origine si trova in ViewModel\BookstoreViewModel.cs) che è stato convertito dall'app Windows Telefono Silverlight Bookstore2WPSL8 contiene una classe denominata Author, che deriva da List<T>, dove T è BookSku. Ciò significa che la classe Author è un gruppo di BookSku.

Quando si associa CollectionViewSource.Source agli autori, l'unica cosa che comunichiamo è che ogni autore in autori è un gruppo di elementi. La proprietà CollectionViewSource viene lasciata per determinare che Author è, in questo caso, un gruppo di BookSku. Questo funziona: ma non è flessibile. E se vogliamo che Author sia sia un gruppo di BookSku che un gruppo di indirizzi in cui l'autore ha vissuto? L'autore non può essere entrambi i gruppi. Tuttavia, Author può avere un numero qualsiasi di gruppi. E questa è la soluzione: usare il modello has-a-group invece che, o oltre a, il modello is-a-group attualmente in uso. In tal caso, eseguire la procedura seguente:

  • Modificare Author in modo che non derivi più da List<T>.
  • Aggiungere questo campo a
  • Aggiungere questa proprietà a
  • E naturalmente è possibile ripetere i due passaggi precedenti per aggiungere tutti i gruppi a Author in base alle esigenze.
  • Modificare l'implementazione del metodo AddBookSku in this.BookSkus.Add(bookSku);.
  • Ora che Author ha almeno un gruppo, è necessario comunicare a CollectionViewSource quale di tali gruppi deve usare. A tale scopo, aggiungere questa proprietà a CollectionViewSource: ItemsPath="BookSkus"

Queste modifiche lasciano l'app in modo funzionale invariato, ma ora sai come estendere Author e CollectionViewSource, se necessario. Verrà apportata un'ultima modifica a Author in modo che, se viene usata senza specificare CollectionViewSource.ItemsPath, verrà usato un gruppo predefinito di propria scelta:

    public class Author : IEnumerable<BookSku>
    {
        ...

        public IEnumerator<BookSku> GetEnumerator()
        {
            return this.BookSkus.GetEnumerator();
        }
        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
        {
            return this.BookSkus.GetEnumerator();
        }
    }

E ora possiamo scegliere di rimuovere ItemsPath="BookSkus" se ci piace e l'app si comporta comunque allo stesso modo.

Conclusione

Questo case study ha coinvolto un'interfaccia utente più ambiziosa di quella precedente. Tutte le funzionalità e i concetti di Windows Telefono Silverlight LongListSelector, e altro ancora, sono stati trovati per essere disponibili per un'app UWP sotto forma di SemanticZoom, ListView, GridView e CollectionViewSource. Abbiamo illustrato come riutilizzare, o copiare e modificare, sia il codice imperativo che il markup in un'app UWP per ottenere funzionalità, interfaccia utente e interazioni personalizzate in base ai fattori di modulo dei dispositivi Windows più stretti e più ampi e a tutte le dimensioni tra loro.