Technology Experience
.NET World

UWP e la Adaptive UI

Dunque, partiamo da qualche presupposto tecnologico.

Con Windows 8 e Windows 8.1 TUTTE le Windows Store App venivano disegnate per essere visualizzate “solo” in full-screen. Il “solo” l’ho messo tra virgolette perchè c’era un altra casistica: quando le app venivano snappate sul lato sinistro o destro dello schermo. E siccome stiamo parlando di app per Windows bisognava pensarle per tutte le risoluzioni possibili ed immaginabili. E bisognava pensarle sia in landscape che portrait. In ogni caso, l’utente non aveva la possibilità di ridimensionare l’app, perchè di fatto non c’era alcuna finestra con nessun bordo.

Con Windows 10 le cose sono un po’ diverse. Innanzitutto esiste la Modalità Tablet, che se attivata fa agire le nuove Universal Windows App (UWP) esattamente come sotto Windows 8/8.1. Partono in full-screen, possiamo snapparle, etc. etc. Quando la Modalità Tablet è spenta, le UWP vengono inserite in una finestra, al punto che l’utente visivamente non le distingue più dalle classiche applicazioni desktop. L’utente può ridurre ad icona l’app, può spostare la finestra e soprattutto può ridimensionarla.

E qui arriva il tema della Adaptive UI offerta dall’SDK di Windows 10. Non è scopo di questo post raccontare tutti gli aspetti della Adaptive UI, anche perchè io stesso ho ancora un po’ di confusione in testa. Lo scopo di per sè è piuttosto semplice:

Permettere ad una Universal Windows App di adattare il proprio contenuto in base a risoluzione & dimensione.

Ricordo, punto fondamentale, che un’app UWP gira ovunque sia presente Windows 10: desktop, portatile, mega-schermo, in futuro smartphone, XBOX, e via dicendo. Quindi, sostanzialmente, significa adattare il contenuto in base alla risoluzione/dimensione e fare in modo che il tutto sia sempre fruibile nel migliore dei modi. E quindi parliamo di schermi che vanno dal 10” di un ultrabook al 22” di un desktop, al 5” di uno smartphone (e la cosa divertente è che magari la risoluzione è sempre Full-HD).

Ecco una serie di articoli che trattano meglio questo argomento:

A Developer’s Guide to Windows 10: (07) Adaptive UI

Windows 10 Universal Apps – Adaptive Triggers

Template 10

Parlando di Template 10 mi sono guardato e studiato questa MainPage.xaml inserita nel progetto Sample. E’ un ottimo esempio di come la view possa reagire in base alla dimensione della finestra.

snip

Analizzando questa view, da cui sono partito, vi propongo una mia soluzione, che illustro in questo post. Esso utilizza ovviamente l’SDK di UWP, AdaptiveTrigger, Model-View-ViewModel con MvvmLight, e qualche altra nozione indispensabile.

Introduzione
Supponiamo di avere un’app UWP che mostra un elenco di automobili.

Ecco la nostra “bellissima” UI:

image

Qui abbiamo un MainViewModel che governa l’intera interfaccia. Questo viewmodel espone una proprietà Cars di tipo ObservableCollection<Car>. I dati sono stati inseriti manualmente nel costruttore. La UI è composta da un Hub con 3 HubSection al suo interno, uguali uno all’altro, sia nell’aspetto che nei dati visualizzati:

<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"
    DataContext="{StaticResource vm}">
    <Hub Grid.Row="1" x:Name="HorizontalView">
        <HubSection x:Name="CarsList1" Header="car list 1" Width="512"
                    HorizontalContentAlignment="Stretch"
                ContentTemplate="{StaticResource CarsListTemplateWide}" />
        <HubSection x:Name="CarsList2" Header="car list 2" Width="512"
                HorizontalContentAlignment="Stretch"
                ContentTemplate="{StaticResource CarsListTemplateWide}" />
        <HubSection x:Name="CarsList3" Header="car list 3" Width="512"
                HorizontalContentAlignment="Stretch"
                ContentTemplate="{StaticResource CarsListTemplateWide}" />
    </Hub>
</Grid>

Il contenuto di ciascun HubSection è prelevato dal DataTemplate CarsListTemplateWide.

In questo momento la nostra UI non è assolutamente adattiva. Se lanciamo l’app e ridimensioniamo la finestra, la UI non reagisce in alcun modo: al massimo ottenete ScrollBar verticali o orizzontali per permettere all’utente di scorrere e vedere tutto il contenuto.

Ciò che ci permettono di fare gli Adaptive Trigger di UWP è quello di intercettare i cambiamenti delle dimensioni e reagire in modo opportuno. Il tutto in modo dichiarativo usando lo XAML, senza scrivere alcuna linea di codice C#.

Concettualmente, quindi, possiamo andare a cambiare il ContentTemplate di ciascuno di quei 3 HubSection, switchando da CarsListTemplateWide a, per esempio, CarsListTemplateSmall. E supponiamo di adattare la UI dicendo: se la nostra finestra si stringe fino ad un certo limite, allora facciamo in modo che l’elenco delle automobili si adatti, rimuovendo alcune informazioni che decidiamo essere superflue.

Questa è la seconda visualizzazione che implementa questa logica. Stringendo la finestra, due colonne ad un certo punto scompaiono (quelle relative al modello e all’anno di immatricolazione).

image

Questo grazie al seguente blocco di XAML:

<VisualStateManager.VisualStateGroups>
	<VisualStateGroup x:Name="VisualStateGroup">
		<VisualState x:Name="VisualStateWide">
			<VisualState.StateTriggers>
				<AdaptiveTrigger MinWindowWidth="600" />
			</VisualState.StateTriggers>
			<VisualState.Setters>
				<Setter Target="CarsList1.ContentTemplate"
					Value="{StaticResource CarsListTemplateSmall}" />
				<Setter Target="CarsList2.ContentTemplate"
					Value="{StaticResource CarsListTemplateSmall}" />
				<Setter Target="CarsList3.ContentTemplate"
					Value="{StaticResource CarsListTemplateSmall}" />
				<Setter Target="CarsList1.Width" Value="300" />
				<Setter Target="CarsList2.Width" Value="300" />
				<Setter Target="CarsList3.Width" Value="300" />
			</VisualState.Setters>
		</VisualState>
		<VisualState x:Name="VisualStateVeryWide">
			<VisualState.StateTriggers>
				<AdaptiveTrigger MinWindowWidth="1024" />
			</VisualState.StateTriggers>
			<VisualState.Setters>
				<Setter Target="CarsList1.ContentTemplate"
					Value="{StaticResource CarsListTemplateWide}" />
				<Setter Target="CarsList2.ContentTemplate"
					Value="{StaticResource CarsListTemplateWide}" />
				<Setter Target="CarsList3.ContentTemplate"
					Value="{StaticResource CarsListTemplateWide}" />
				<Setter Target="CarsList1.Width" Value="512" />
				<Setter Target="CarsList2.Width" Value="512" />
				<Setter Target="CarsList3.Width" Value="512" />
			</VisualState.Setters>
		</VisualState>
	</VisualStateGroup>
</VisualStateManager.VisualStateGroups>

Vengono definiti 2 VisualState: uno è VisualStateWide, l’altro è VisualStateVeryWide. Il primo è valido dai 600 pixel in su, il secondo dai 1024 pixel in su. Giocando con i Setter del Visual State Manager vado a cambiare alcune proprietà degli oggetti sulla UI, principalmente due:

  • la larghezza degli HubSection (300 pixel oppure 512)
  • Il ContentTemplate degli HubSection (CarsListTemplateSmall oppure CarsListTemplateWide)

Il Setter agisce in modo immediato, e la UI si aggiorna come stabilito.

Conclusioni

Questa soluzione è molto carina da vedere in esecuzione. In forma statica, qui sul blog, rende molto poco l’idea. E soprattutto ha molto più senso in un’applicazione reale complessa, con molte informazioni da mostrare: in un esempio semplice come questo non risaltano bene le potenzialità. Pian piano, forse, l’Adaptive UI mi entra in testa!

Send to Kindle

Igor Damiani

La sua passione per l'informatica nasce nella prima metà degli anni '80, quando suo padre acquistò un Texas Instruments TI-99. Da allora ha continuato a seguire l'evoluzione sia hardware che software avvenuta nel corso degli anni. E' un utente, un videogiocatore ed uno sviluppatore software a tempo pieno. Igor ha lavorato e lavora anche oggi con le più moderne tecnologie Microsoft per lo sviluppo di applicazioni: .NET Framework, XAML, Universal Windows Platform, su diverse piattaforme, tra cui spiccano Windows 10 piattaforme mobile. Numerose sono le app che Igor ha creato e pubblicato sul marketplace sotto il nome VivendoByte, suo personale marchio di fabbrica. Adora mantenere i contatti attraverso Twitter e soprattutto attraverso gli eventi delle community .NET.

Lascia un commento

Il tuo indirizzo email non sarà pubblicato. I campi obbligatori sono contrassegnati *

Questo sito usa Akismet per ridurre lo spam. Scopri come i tuoi dati vengono elaborati.