Uso dei controlli

Uso dei controlli in Hilo (app per Windows Store scritte in C++ e XAML)

[ Questo articolo è rivolto agli sviluppatori per Windows 8.x e Windows Phone 8.x che realizzano app di Windows Runtime. Gli sviluppatori che usano Windows 10 possono vedere Documentazione aggiornata ]

Da : Sviluppo di un'app di Windows Store end-to-end con C++ e XAML: Hilo

Logo Patterns & Practices

Pagina precedente | Pagina successiva

I controlli sono il cuore di Windows 8 e del linguaggio XAML. In questo argomento imparerai a conoscere alcuni dei controlli, tra cui Image, Grid e GridView, ProgressRing, Button, TextBlock, AppBar, StackPanel, ListView, SemanticZoom, Canvas e ContentControl e Popup e ti spiegheremo come abbiamo usato l'associazione e gli stili per implementare l'interfaccia utente di Hilo in C++.

Download

Scarica l'esempio di Hilo
Download guida (PDF)

Dopo aver scaricato il codice, per le istruzioni vedi Introduzione a Hilo.

Contenuti

  • Uso dell'associazione per collegare l'interfaccia utente al code-behind.
  • Applicazione dei modelli di dati.
  • Scrittura dei controlli personalizzati
  • Gestione delle immagini non valide quando si usa l'associazione.
  • Conversione dei dati del modello per la visualizzazione corretta.

Si applica a

  • Windows Runtime per Windows 8
  • Estensioni del componente Visual C++ (C++/CX)
  • XAML

Forse avrai notato che il linguaggio XAML ha una natura dichiarativa, più che imperativa. Il linguaggio XAML crea un albero di oggetti, paragonabili a un DOM nel linguaggio HTML. Se non hai nessuna esperienza, vedi Guida introduttiva: Creazione di un'interfaccia utente con XAML e quindi Guida introduttiva: Aggiunta di controlli e gestione di eventi.

Nota  L'argomento Creazione di pagine e navigazione tra le pagine illustra come abbiamo usato Blend per Visual Studio e la finestra di progettazione XAML di Visual Studio per intervenire su pagine e controlli XAML. Anche se si tratta di strumenti piuttosto facili da usare, non applicare troppi stili ai controlli. Ad esempio, non tutti i pulsanti devono avere uno sfondo sfumato o spigoli arrotondati. Per le linee guida consigliate, vedi l'indice delle linee guida relative all'esperienza utente per le app di Windows Store
 

Ecco qualche concetto importante del linguaggio XAML forse poco conosciuto dagli sviluppatori C++:

  • Code-behind collega il markup XAML al codice compilato che implementa la logica dell'app. Ad esempio, nel caso di un controllo Button, si implementa l'handler dell'evento Click nel codice C++. Questo codice C++ è un esempio di code-behind.

    Anche se si può aggiungere la logica direttamente nel code behind, in Hilo il code behind si collega al codice XAML per visualizzare i modelli che ne definiscono la logica.

  • Un modello o modello di dati definisce la struttura, il layout e le animazioni associate a un controllo, oltre ai relativi stili. I modelli influiscono sull'aspetto esteriore dei controlli, ma non sul loro comportamento. Ad esempio, le caselle di riepilogo e gran parte degli altri controlli ricorrono alle stringhe per rappresentare i propri elementi dati. I modelli consentono di rappresentare i dati utilizzando la grafica e altre funzionalità visive. La classe DataTemplate si usa per definire la rappresentazione visiva dei dati provenienti da XAML.
  • L'associazione fornisce un valore di proprietà associato ai dati tale per cui il valore viene posticipato fino alla fase di esecuzione. La classe Binding si usa per connettere l'interfaccia utente ai dati.
  • I controlli personalizzati aggregano le funzionalità di altri controlli.
  • I convertitori di dati trasformano un valore in un altro valore. Usa i convertitori di dati insieme all'associazione per trasformare valore o formato dei dati associati in contenuti che verranno visualizzati dall'app.
  • Il termine risorsa è riferito a una funzionalità riutilizzabile. Ad esempio, gli stili, i pennelli e il testo visualizzati su una pagina sono tutti esempi di risorse.

Le tecniche di programmazione delle app di Windows Store con il linguaggio XAML sono molto simili a quelle utilizzate per Windows Presentation Foundation (WPF) e Silverlight. La differenza principale è che le app di Windows Store sono incentrate principalmente sull'uso del tocco e offrono molti controlli nuovi. Per conoscere la differenza tra Silverlight o WPF e le app di Windows Store in XAML, leggi Porting di codice/XAML Silverlight o WPF in un'app di Windows Store. Gli argomenti Elenco dei controlli e Controlli per funzione contengono l'elenco di tutti i controlli disponibili, inclusi AppBar, GridView, SemanticZoom e altri controlli introdotti con Windows 8.

Nota  Le app per Windows Store scritte in C++ e XAML sono eseguite completamente come codice nativo. I controlli XAML sono inoltre accelerati dall'hardware grafico. La sintassi di C++/CX facilita l'orientamento verso Windows Runtime, e non utilizza .NET Framework. L'app in C++ è orientata verso .NET solo quando utilizza un componente di Windows Runtime che a sua volta utilizza .NET. Per altre info su C++/CX, vedi Scrittura di codice C++ moderno .
 

[In alto]

Associazione dati

L'associazione fornisce un valore di proprietà associato ai dati tale per cui il valore viene posticipato fino alla fase di esecuzione. L'associazione è essenziale per un uso efficace del linguaggio XAML, perché ti consente di collegare l'interfaccia utente ai dati in modo dichiarativo. Inoltre, l'associazione riduce al minimo la necessità del code behind. Poiché i dati vengono risolti in fase di esecuzione, durante la progettazione dell'app puoi separare in modo chiaro l'interfaccia utente dai dati. Per quanto riguarda Hilo, l'associazione dei dati in fase di esecuzione è essenziale, perché al momento della progettazione non sappiamo nulla della raccolta immagini dell'utente. Per altre info sull'associazione, vedi Panoramica dell'associazione dati e Estensione di markup Binding.

L'associazione supporta inoltre il modello MVVM. Per altre info sull'uso dell'associazione in Hilo, vedi Uso del modello MVVM. Sezioni successive di questa pagina descrivono inoltre come abbiamo utilizzato l'associazione per collegare contenuti statici e dinamici.

[In alto]

Convertitori di dati

I convertitori di dati trasformano un valore in un altro valore. Un convertitore può prendere un valore di un tipo e restituire un valore di un altro tipo oppure semplicemente modificare un valore per produrne uno dello stesso tipo.

Per quanto riguarda Hilo, usiamo i convertitori di dati per modificare il valore o il formato dei dati associati quando ci sono tipi di dati che richiedono una formattazione particolare non offerta dal modello di visualizzazione. Questi sono i convertitori di dati che abbiamo definito per Hilo.

Tipo convertitoreConverte daConverte inDescrizione
BooleanToBrushConverterbool Platform::String Nella visualizzazione calendario, utilizzato per visualizzare in arancione i mesi in cui ci sono foto e in grigio quelli in cui non ce ne sono. Il valore true esegue il mapping sull'arancione (#F19720), mentre false esegue il mapping sul grigio (#E2E2E2). Questo è possibile perché nella classe Brush è presente un convertitore per convertire il valore stringa in Brush.
FileSizeConverterunsigned long long Platform::String Utilizzato per convertire in kilobyte o in megabyte le dimensioni file espresse in byte e renderle più leggibili.

 

I modelli di progetto di Visual Studio includono inoltre le classi BooleanNegationConverter e BooleanToVisibilityConverter che negano i valori bool e convertono i valori bool in valori di enumerazione Visibility, rispettivamente. Trovi queste classi nella cartella Common della soluzione.

Per creare un convertitore di dati, devi creare un tipo derivante da IValueConverter, quindi devi implementare i metodi Convert, ConvertBack e qualsiasi altro metodo eventualmente necessario. Ecco la dichiarazione per la classe FileSizeConverter.

FileSizeConverter.h


[Windows::Foundation::Metadata::WebHostHidden]
public ref class FileSizeConverter sealed : public Windows::UI::Xaml::Data::IValueConverter
{
public:
    FileSizeConverter();
    FileSizeConverter(IResourceLoader^ loader);

    virtual Object^ Convert(Object^ value, Windows::UI::Xaml::Interop::TypeName targetType, Object^ parameter, Platform::String^ language);
    virtual Object^ ConvertBack(Object^ value, Windows::UI::Xaml::Interop::TypeName targetType, Object^ parameter, Platform::String^ language);

private:
    float64 ToTwoDecimalPlaces(float64 value);
    IResourceLoader^ m_loader;
};


E questa è la relativa implementazione.

FileSizeConverter.cpp


Object^ FileSizeConverter::Convert(Object^ value, TypeName targetType, Object^ parameter, String^)
{
    float64 size = static_cast<float64>(safe_cast<uint64>(value));
    std::array<String^, 3> units = 
    { 
        m_loader->GetString("BytesUnit"), 
        m_loader->GetString("KilobytesUnit"), 
        m_loader->GetString("MegabytesUnit") 
    };
    unsigned int index = 0;

    while (size >= 1024)
    {
        size /= 1024;
        index++;
    }

    return ToTwoDecimalPlaces(size) + " " + units[index];
}

float64 FileSizeConverter::ToTwoDecimalPlaces(float64 value)
{
    float64 f;
    float64 intpart;
    float64 fractpart;
    fractpart = modf(value, &intpart);
    f = floor(fractpart * 100 + 0.5) / 100.0;
    return intpart + f;
}

Object^ FileSizeConverter::ConvertBack(Object^ value, TypeName targetType, Object^ parameter, String^)
{
    throw ref new NotImplementedException();
}


Nel metodo FileSizeConverter::Convert usiamo safe_cast per effettuare la conversione da Object^ al tipo di valore uint64; se la conversione non riesce safe_cast genera Platform::InvalidCastException. Eseguiamo il cast in uint64 perché associamo il convertitore alla proprietà IPhoto::FileSize, che restituisce uint64. Quindi convertiamo il valore uint64 in float64 perché per il calcolo di utilizzano operazioni aritmetiche in virgola mobile.

Dobbiamo implementare il metodo ConvertBack perché fa parte dell'interfaccia IValueConverter. Ma generiamo NotImplementedException per indicare che non abbiamo implementato questa funzionalità. Quando non prevedi che venga chiamato questo metodo, genera questa eccezione invece di restituire un valore predefinito. In questo modo, durante la fase di sviluppo potrai verificare che il metodo non venga mai chiamato. Se restituisci un valore predefinito, l'app potrebbe assumere comportamenti imprevisti. Per quanto riguarda Hilo, non ci aspettiamo che venga chiamato il metodo ConvertBack perché non stiamo eseguendo un'associazione bidirezionale. In alcune applicazioni un campo associato a una data potrebbe consentire all'utente di inserire un nuovo valore per la data. Per funzionare correttamente, questa associazione bidirezionale richiederebbe sia il metodo Convert che il metodo ConvertBack.

Per utilizzare il convertitore dall'interno del codice XAML, prima di tutto aggiungilo al dizionario risorse. Ecco come abbiamo proceduto noi per la visualizzazione delle immagini.

ImageView.xaml


<Page.Resources>
    <local:FileSizeConverter x:Key="FileSizeConverter" />
    <Style x:Key="FilmStripGridViewItemStyle" 
           BasedOn="{StaticResource HiloGridViewItemStyle}" 
           TargetType="GridViewItem">
        <Setter Property="Margin" Value="0,0,2,4" />
    </Style>
</Page.Resources>


Nel code-behind della pagina devi includere anche il file di intestazione del convertitore.

ImageView.xaml.h


#include "FileSizeConverter.h" // Required by generated header
#include "ImageView.g.h"


Per convertire un valore associato, usa la proprietà Binding::Converter del linguaggio XAML. In questo esempio viene usato FileSizeConverter per rendere più leggibili le dimensioni dell'immagine su disco visualizzate nel popup contenente altre info sul file. Per altre info sul popup, vedi Pressione prolungata per imparare in questa guida.

ImageView.xaml


<TextBlock Foreground="{StaticResource ApplicationForegroundThemeBrush}" 
           Text="{Binding Path=CurrentPhotoImage.FileSize, Converter={StaticResource FileSizeConverter}}" />


Per altre info sui convertitori, vedi Panoramica dell'associazione dati.

[In alto]

Controlli comuni utilizzati in Hilo

Ecco alcuni dei controlli comuni che abbiamo utilizzato in Hilo. Puoi fare riferimento anche ai file .xaml contenuti nella cartella Views del progetto di Visual Studio.

Suggerimento  Per visualizzare tutti i controlli disponibili, vedi gli argomenti Elenco dei controlli e Controlli per funzione. Imposta un segnalibro per queste pagine, così potrai tornarci ogni volta che vorrai aggiungere un'altra funzionalità alla tua app. Inoltre, usa la Casella degli strumenti in Visual Studio e la scheda Risorse in Blend per Visual Studio per sfogliare e aggiungere controlli alle pagine.
 

Image

Per visualizzare immagini nell'app si usa il controllo Image. Ci è sembrato relativamente semplice utilizzare le proprietà standard per impostare dimensioni, allineamento e altre proprietà di visualizzazione. Talvolta, abbiamo scelto di non specificare proprietà quali le dimensioni per fare in modo che il framework adattasse l'immagine alle dimensioni disponibili. Ad esempio, nel caso di un controllo Grid, se le dimensioni di un elemento figlio non sono impostate in modo esplicito, si estendono fino a riempire tutto lo spazio disponibile nella cella della griglia. Per altre info sulle caratteristiche di layout dei controlli comuni, vedi Guida introduttiva: Aggiunta di controlli layout.

Un controllo immagine

Ecco il codice XAML aggiornato per Image.

CropImageView.xaml


<Image x:Name="Photo" 
       AutomationProperties.AutomationId="ImageControl"
       HorizontalAlignment="Center" 
       VerticalAlignment="Center" 
       SizeChanged="OnSizeChanged"
       Source="{Binding Image}"/>


Poiché le immagini sono per la maggior parte foto degli utenti, l'associazione ci è sembrata un modo molto utile di specificare cosa caricare in fase di esecuzione senza la necessità di scrivere code-behind. Inoltre, avevamo la necessità di gestire i file di immagine che non si riescono a caricare, ad esempio quelli danneggiati. La classe Photo di Hilo, che è associata al modello di visualizzazione e fornisce i dati dell'immagine, sottoscrive l'evento Image::ImageFailed, che viene chiamato quando si verifica un errore durante il recupero di un'immagine.

PhotoImage.cpp


m_image = ref new BitmapImage();
m_imageFailedEventToken = m_image->ImageFailed::add(ref new ExceptionRoutedEventHandler(this, &PhotoImage::OnImageFailedToOpen));


Il gestore dell'evento Image::ImageFailed, PhotoImage::OnImageFailedToOpen, usa la sintassi ms-appx:// per caricare un'immagine predefinita quando si verifica un errore durante il recupero di un'immagine.

PhotoImage.cpp


void PhotoImage::OnImageFailedToOpen(Object^ sender, ExceptionRoutedEventArgs^ e)
{
    assert(IsMainThread());

    // Load a default image.
    ClearImageData();
    m_image = ref new BitmapImage(ref new Uri("ms-appx:///Assets/HiloLogo.png"));  
    OnPropertyChanged("Image");
}


Per altre info su ms-appx:// e altri schemi URI, vedi Come caricare risorse di tipo file.

La prossima sezione descrive il modo in cui abbiamo utilizzato l'associazione per visualizzare la filmstrip che compare sulla pagina delle immagini.

Per altre info sulle immagini, vedi Guida introduttiva: Immagini e ImageBrush, l'esempio di immagini XAML e l'esempio di controlli XAML essenziali.

Grid e GridView

Le griglie sono una parte essenziale di tutte le pagine dell'app di Hilo, perché ci hanno consentito di posizionare i controlli figlio esattamente dove volevamo. Un controllo Grid è un pannello di layout che supporta la disposizione di elementi figlio in righe e colonne. Un controllo GridView presenta una raccolta di elementi in righe e colonne che è possibile scorrere orizzontalmente. Inoltre, GridView consente di visualizzare raccolte di dimensioni variabili.

Abbiamo utilizzato Grid per visualizzare numeri fissi di elementi che possono essere contenuti per intero nella schermata. Abbiamo utilizzato GridView per visualizzare raccolte di dimensioni variabili. Ad esempio, GridView è stato utilizzato nella pagina del visualizzatore immagini per raggruppare le foto in base al mese. Inoltre, abbiamo utilizzato GridView per visualizzare la filmstrip che compare nella barra di spostamento nella pagina delle immagini.

Filmstrip che compare nella barra superiore dell'app

Ecco il codice XAML relativo al controllo GridView che rappresenta la filmstrip.

ImageView.xaml


<GridView x:Name="PhotosFilmStripGridView"
          Grid.Column="1"
          AutomationProperties.AutomationId="PhotosFilmStripGridView"
          IsItemClickEnabled="False"
          ItemContainerStyle="{StaticResource FilmStripGridViewItemStyle}"
          ItemsSource="{Binding Photos}"
          SelectedItem="{Binding Path=SelectedItem, Mode=TwoWay}"
          SelectionMode="Single"
          VerticalAlignment="Center">
    <GridView.ItemsPanel>
        <ItemsPanelTemplate>
            <VirtualizingStackPanel Height="138" Orientation="Horizontal" />
        </ItemsPanelTemplate>
    </GridView.ItemsPanel>
    <GridView.ItemTemplate>
        <DataTemplate>
            <Border>
                <Image Source="{Binding Path=Thumbnail}" 
                       Height="138" 
                       Width="200" 
                       Stretch="UniformToFill" />
            </Border>
        </DataTemplate>
    </GridView.ItemTemplate>
</GridView>


Nota  L'attributo x:Name specifica il nome dell'istanza del controllo nel codice. In questo esempio il compilatore genera, per la classe ImageView, una variabile membro denominata PhotosFilmStripGridView che compare nel file ImageView.g.h generato dal compilatore.
 

Questo controllo GridView usa lo stile FilmStripGridViewItemStyle, definito in ApplicationStyles.xaml, per personalizzare l'aspetto degli elementi GridViewItem visualizzati in GridView. Ogni GridViewItem è costituito da un controllo Image posizionato in un controllo Border e definito nel DataTemplate di GridView. Per supportare la virtualizzazione dell'interfaccia utente, abbiamo usato un VirtualizingStackPanel definito nel ItemsPanelTemplate di GridView per visualizzare gli elementi GridViewItem. Per altre informazioni sulla virtualizzazione dell'interfaccia utente, vedi l'argomento su virtualizzazione dell'interfaccia utente per usare grandi set di dati in questa guida.

Il prossimo esempio mostra il modo in cui il metodo ImageView::OnFilmStripLoaded scorre gli elementi selezionati nella visualizzazione.

ImageView.xaml.cpp


// Scrolls the selected item into view after the collection is likely to have loaded.
void ImageView::OnFilmStripLoaded(Object^ sender, RoutedEventArgs^ e)
{
    auto vm = dynamic_cast<ImageViewModel^>(DataContext);
    if (vm != nullptr)
    {
        PhotosFilmStripGridView->ScrollIntoView(vm->SelectedItem);
    }

    PhotosFilmStripGridView->Loaded::remove(m_filmStripLoadedToken);
}


Un aspetto interessante di GridView è il modo in cui esegue l'associazione ai dati. Per abilitare il modello MVVM nella pagina di visualizzazione delle immagini abbiamo impostato l'elemento DataContext delle pagine sulla proprietà ImageVM della classe ViewModelLocator. Per altre info sul modello MVVM, vedi Uso del modello MVVM.

ImageView.xaml


<local:HiloPage
    x:Name="pageRoot"
    x:Uid="Page"
    x:Class="Hilo.ImageView"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:Hilo"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d"
    DataContext="{Binding Source={StaticResource ViewModelLocator}, Path=ImageVM}">


Di conseguenza, i controlli figlio presenti sulla pagina sono associati alla classe ImageViewModel; la proprietà ViewModelLocator::ImageVM restituisce un oggetto ImageViewModel. Per quanto riguarda la filmstrip, abbiamo eseguito l'associazione alla proprietà ImageViewModel::Photos per visualizzare le foto del mese corrente.

ImageView.xaml


<GridView x:Name="PhotosFilmStripGridView"
          Grid.Column="1"
          AutomationProperties.AutomationId="PhotosFilmStripGridView"
          IsItemClickEnabled="False"
          ItemContainerStyle="{StaticResource FilmStripGridViewItemStyle}"
          ItemsSource="{Binding Photos}"
          SelectedItem="{Binding Path=SelectedItem, Mode=TwoWay}"
          SelectionMode="Single"
          VerticalAlignment="Center">


La proprietà ImageViewModel::Photos è costituita da una raccolta di oggetti IPhoto. Per visualizzare una foto singola utilizziamo un elemento Image associato alla proprietà IPhoto::Thumbnail.

ImageView.xaml


<GridView.ItemTemplate>
    <DataTemplate>
        <Border>
            <Image Source="{Binding Path=Thumbnail}" 
                   Height="138" 
                   Width="200" 
                   Stretch="UniformToFill" />
        </Border>
    </DataTemplate>
</GridView.ItemTemplate>


In fase di esecuzione, l'associazione tra visualizzazione e modello di visualizzazione si verifica al momento della creazione della pagina. Quando un utente passa a quella pagina, il metodo ImageViewModel::OnNavigatedTo imposta la query del file system relativa alle foto del mese corrente Quando GridView visualizza per la prima volta le foto del mese, la proprietà ImageViewModel::Photos invoca il metodo ImageViewModel::OnDataChanged, che a sua volta invoca il metodo ImageViewModel::QueryPhotosAsync. Il metodo ImageViewModel::QueryPhotosAsync invoca GetPhotosForDateRangeQueryAsync sull'istanza puntatore condivisa della classe FileSystemRepository, che era passata nel costruttore ImageViewModel, e passa la variabile membro m_query contenente la query del file system relativa alle foto del mese corrente.

Per altre info su GridView, vedi gli argomenti su aggiunta dei controlli ListView e GridView e modelli di elemento per layout griglia.

ProgressRing

Per indicare che un'operazione è in corso usiamo il controllo ProgressRing. Le operazioni che richiedono una lunga esecuzione vengono eseguite in background, per consentire all'utente di interagire con l'app, e ricorriamo agli anelli di stato per indicare che una data operazione è in corso e presto si concluderà. Usa i controlli di stato per le operazioni la cui esecuzione richiede 2 o più secondi.

Ecco il codice XAML relativo alla pagina del visualizzatore immagini. L'anello di stato viene visualizzato quando il sistema operativo carica le informazioni relative all'anteprima.

ImageBrowserView.xaml


<ProgressRing x:Name="ProgressRing" 
              IsActive="{Binding InProgress}" 
              Height="60"
              Width="60"
              Foreground="{StaticResource HiloHighlightBrush}"/>


La proprietà IsActive è associata alla proprietà ImageBrowserViewModel::InProgress, un valore booleano. La proprietà ImageBrowserViewModel::InProgress restituisce true quando il modello di visualizzazione richiede le immagini al sistema operativo. Al termine della query, la proprietà ImageBrowserViewModel::InProgress restituisce false e l'oggetto modello di visualizzazione genera l'evento PropertyChanged per indicare al runtime di XAML di nascondere l'anello di stato.

Per indicare che un'operazione è in corso usiamo il controllo ProgressRing invece del controllo ProgressBar, perché non si sa quanto tempo sarà necessario per portare a termine l'operazione. Usa ProgressBar per fornire info più dettagliate sul completamento dell'operazione. Barre di stato e anelli di stato non devono impedire all'utente di annullare l'operazione o di passare a un'altra pagina. Per altre info sull'esecuzione e l'annullamento delle operazioni in background, vedi l'argomento sui modelli di programmazione asincrona in C++.

Nota  Imposta sempre le proprietà ProgressRing::IsActive e ProgressBar::IsIndeterminate su false quando non devi più visualizzare le info sullo stato. Anche se i controlli non sono visibili, le animazioni possono comunque consumare cicli di CPU, causando problemi di prestazioni o riducendo la durata della batteria. Inoltre, quando il valore di ProgressRing::IsActive è false, anche se il controllo ProgressRing non è visualizzato, nel layout dell'interfaccia utente viene riservato lo spazio corrispondente. Per fare in modo che non venga riservato lo spazio per ProgressRing, imposta la relativa proprietà Visibility su Collapsed.
 

Per altre linee guida sui controlli di stato, vedi Linee guida ed elenco di controllo per i controlli di stato.

Button

I controlli Button rispondono all'input dell'utente e generano un evento Click. In Hilo, usiamo i pulsanti per:

  • Definire un pulsante Indietro per ogni pagina.
  • Consentire agli utenti di passare alla pagina di visualizzazione immagini dalla pagina Hub.
  • Consentire agli utenti di esplorare tutte le foto di un dato mese.
  • Aggiungere comandi alle barre dell'app.

Ecco il codice XAML relativo al controllo Button che consente agli utenti di esplorare tutte le foto di un dato mese.

ImageBrowserView.xaml


<Button AutomationProperties.AutomationId="MonthGroupTitle"
        Content="{Binding Title}"
        Command="{Binding ElementName=MonthPhotosGridView, Path=DataContext.GroupCommand}"
        CommandParameter="{Binding}"
        Style="{StaticResource HiloTextButtonStyle}" />


Parte importante di questa implementazione è costituita dal fatto che la proprietà Command del pulsante si associa alla proprietà GroupCommand del modello di visualizzazione. Inoltre, la proprietà Content del pulsante è associata a una stringa di testo. Tuttavia, puoi associare il contenuto del pulsante a qualsiasi classe derivante da Panel. Al controllo Button viene applicato lo stile HiloTextButtonStyle, che a sua volta utilizza lo stile SubheaderTextStyle specificato StandardStyles.xaml, senza altre aree di controllo.

Il costruttore ImageBrowserViewModel imposta m_groupCommand, ovvero il valore ICommand della proprietà GroupCommand, in modo che punti al metodo NavigateToGroup.

ImageBrowserViewModel.cpp


m_groupCommand = ref new DelegateCommand(ref new ExecuteDelegate(this, &ImageBrowserViewModel::NavigateToGroup), nullptr);


Il metodo NavigateToGroup passa alla pagina del visualizzatore immagini del mese corrente.

ImageBrowserViewModel.cpp


void ImageBrowserViewModel::NavigateToGroup(Object^ parameter)
{
    auto group = dynamic_cast<IPhotoGroup^>(parameter);
    assert(group != nullptr);
    if (group->Items->Size > 0)
    {
        auto photo = dynamic_cast<IPhoto^>(group->Items->GetAt(0));
        assert(photo != nullptr);
        
        create_task(photo->GetDateTakenAsync()).then([this, photo](DateTime dateTaken)
    {
        ImageNavigationData data(photo->Path, dateTaken);
        ViewModelBase::GoToPage(PageType::Image, data.SerializeToString());
    }).then(ObserveException<void>(m_exceptionPolicy));

    }
}


Per altre info sull'associazione di comandi al modello di visualizzazione, vedi l'argomento sull'esecuzione di comandi in un modello di visualizzazione in questa guida.

Per altre info sui pulsanti, vedi l'argomento sull'aggiunta di pulsanti e l'esempio di controlli XAML essenziali.

TextBlock

Il controllo TextBlock visualizza il testo. I blocchi di testo si usano per visualizzare il titolo su ogni pagina e per riempire il controllo Popup che visualizza altre info sull'immagine quando l'utente prolunga la pressione sull'immagine corrente.

Pressione prolungata per visualizzare le proprietà della foto

L'associazione e l'uso delle risorse sono due aspetti importanti per quanto riguarda la visualizzazione di testo. L'associazione consente di rimandare il valore di un blocco di testo fino alla fase di esecuzione, ad esempio per visualizzare le info relative a un'immagine. Le risorse sono importanti perché facilitano la localizzazione dell'app. Per altre info sulla localizzazione, vedi Creazione di un'app per il mercato internazionale in questa guida.

Per altre info blocchi di testo, vedi Guida introduttiva: Visualizzazione di testo e l'esempio di visualizzazione di testo XAML.

AppBar

Il controllo AppBar è una barra degli strumenti per la visualizzazione dei comandi specifici dell'app. Hilo visualizza una barra dell'app nella parte inferiore di ogni pagina e una barra di spostamento nella pagina di visualizzazione delle immagini contenente una filmstrip di tutte le foto del mese corrente. Per definire la barra dell'app inferiore si può usare la proprietà Page::BottomAppBar, mentre per definire la barra di spostamento si può utilizzare la proprietà Page::TopAppBar.

Ecco come appaiono i pulsanti presenti sulla barra dell'app situata nella parte inferiore della pagina di visualizzazione delle immagini.

Pulsanti della barra dell'app inferiore

Puoi includere in una pagina i pulsanti che abilitano la navigazione o importanti funzioni dell'app e posizionare su una barra dell'app i pulsanti non essenziali per la navigazione e l'utilizzo dell'app. Ad esempio, nella pagina del visualizzatore immagini abbiamo fatto in modo che l'utente possa fare clic sul testo di un dato mese per passare immediatamente a tutte le foto relative a quel mese perché ci sembrava essenziale per la navigazione. Abbiamo invece incluso i comandi di rotazione, ritaglio e applicazione dell'effetto cartone animato nella barra dell'app perché si tratta di comandi secondari e non volevamo che diventassero elementi di distrazione per gli utenti.

Ecco il codice XAML della barra dell'app inferiore che compare nella pagina di visualizzazione delle immagini. Questa barra dell'app contiene elementi Button che consentono all'utente di passare alle modalità di rotazione, ritaglio e applicazione dell'effetto cartone animato.

ImageView.xaml


<local:HiloPage.BottomAppBar>
    <AppBar x:Name="ImageViewBottomAppBar"
            x:Uid="AppBar"
            AutomationProperties.AutomationId="ImageViewBottomAppBar"
            Padding="10,0,10,0">
        <Grid>
            <StackPanel HorizontalAlignment="Left" 
                        Orientation="Horizontal">
                <Button x:Name="RotateButton"
                        x:Uid="RotateAppBarButton"
                        Command="{Binding RotateImageCommand}" 
                        Style="{StaticResource RotateAppBarButtonStyle}" 
                        Tag="Rotate" />
                <Button x:Name="CropButton"
                        x:Uid="CropAppBarButton"
                        Command="{Binding CropImageCommand}"
                        Style="{StaticResource CropAppBarButtonStyle}"
                        Tag="Crop" />
                <Button x:Name="CartoonizeButton"
                        x:Uid="CartoonizeAppBarButton"
                        Command="{Binding CartoonizeImageCommand}"
                        Style="{StaticResource CartoonEffectAppBarButtonStyle}"
                        Tag="Cartoon effect" />
                <Button x:Name="RotateButtonNoLabel"
                        Command="{Binding RotateImageCommand}" 
                        Style="{StaticResource RotateAppBarButtonNoLabelStyle}" 
                        Tag="Rotate"
                        Visibility="Collapsed">
                    <ToolTipService.ToolTip>
                        <ToolTip x:Uid="RotateAppBarButtonToolTip" />
                    </ToolTipService.ToolTip>
                </Button>
                <Button x:Name="CropButtonNoLabel"
                        Command="{Binding CropImageCommand}"
                        Style="{StaticResource CropAppBarButtonNoLabelStyle}"
                        Tag="Crop"
                        Visibility="Collapsed">
                    <ToolTipService.ToolTip>
                        <ToolTip x:Uid="CropAppBarButtonToolTip" />
                    </ToolTipService.ToolTip>
                </Button>
                <Button x:Name="CartoonizeButtonNoLabel"
                        Command="{Binding CartoonizeImageCommand}"
                        Style="{StaticResource CartoonEffectAppBarButtonNoLabelStyle}"
                        Tag="Cartoon effect"
                        Visibility="Collapsed">
                    <ToolTipService.ToolTip>
                        <ToolTip x:Uid="CartoonizeAppBarButtonToolTip" />
                    </ToolTipService.ToolTip>
                </Button>
            </StackPanel>
        </Grid>
    </AppBar>
</local:HiloPage.BottomAppBar>


Definiamo due serie di pulsanti: uno per la modalità orizzontale e l'altro per quella verticale. In conformità alle linee guida dell'interfaccia utente sulle barre dell'app, in modalità orizzontale le etichette dei pulsanti sono visualizzate, mentre in modalità verticale sono nascoste. Ecco il codice XAML che definisce la visibilità dei pulsanti in modalità verticale.

ImageView.xaml


<VisualState x:Name="FullScreenPortrait">
    <Storyboard>
        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="BackButton" 
                                       Storyboard.TargetProperty="Style">
            <DiscreteObjectKeyFrame KeyTime="0" 
                                    Value="{StaticResource PortraitBackButtonStyle}"/>
        </ObjectAnimationUsingKeyFrames>

        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="ItemGridView"
                                       Storyboard.TargetProperty="ItemsPanel">
            <DiscreteObjectKeyFrame KeyTime="0"
                                    Value="{StaticResource HubVerticalVirtualizingStackPanelTemplate}" />
        </ObjectAnimationUsingKeyFrames>

        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="ItemGridView" 
                                       Storyboard.TargetProperty="Padding">
            <DiscreteObjectKeyFrame KeyTime="0" 
                                    Value="96,0,10,56"/>
        </ObjectAnimationUsingKeyFrames>
        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="RotateButton"
                                       Storyboard.TargetProperty="Visibility">
            <DiscreteObjectKeyFrame KeyTime="0"
                                    Value="Collapsed"/>
        </ObjectAnimationUsingKeyFrames>
        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="CropButton"
                                       Storyboard.TargetProperty="Visibility">
            <DiscreteObjectKeyFrame KeyTime="0"
                                    Value="Collapsed"/>
        </ObjectAnimationUsingKeyFrames>
        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="CartoonizeButton"
                                       Storyboard.TargetProperty="Visibility">
            <DiscreteObjectKeyFrame KeyTime="0"
                                    Value="Collapsed"/>
        </ObjectAnimationUsingKeyFrames>
        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="RotateButtonNoLabel"
                                       Storyboard.TargetProperty="Visibility">
            <DiscreteObjectKeyFrame KeyTime="0"
                                    Value="Visible"/>
        </ObjectAnimationUsingKeyFrames>
        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="CropButtonNoLabel"
                                       Storyboard.TargetProperty="Visibility">
            <DiscreteObjectKeyFrame KeyTime="0"
                                    Value="Visible"/>
        </ObjectAnimationUsingKeyFrames>
        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="CartoonizeButtonNoLabel"
                                       Storyboard.TargetProperty="Visibility">
            <DiscreteObjectKeyFrame KeyTime="0"
                                    Value="Visible"/>
        </ObjectAnimationUsingKeyFrames>
    </Storyboard>
</VisualState>


Suggerimento  L'authoring di questo codice XAML è stato fatto manualmente, tuttavia molti sviluppatori preferiscono usare Blend per Visual Studio per definire gli stati di visualizzazione.
 

Per altre info su come abbiamo utilizzato le barre dell'app in Hilo, vedi Scorrimento rapido da un lato dello schermo per accedere ai comandi dell'app in questa guida.

StackPanel

Il controllo StackPanel dispone i propri elementi figlio in una singola riga con orientamento orizzontale o verticale. Abbiamo usato StackPanel per:

  • Disporre i pulsanti nella barra dell'app inferiore.
  • Visualizzare un anello di stato accanto a un altro controllo, ad esempio Button.
  • Visualizzare elementi in un controllo Popup per implementare la pressione prolungata. Per altre info, vedi Pressione prolungata per imparare in questa guida.

Ecco un esempio di come abbiamo utilizzato StackPanel per disporre i pulsanti sulla barra dell'app inferiore della pagina hub.

MainHubView.xaml.


<local:HiloPage.BottomAppBar>
    <AppBar x:Name="MainHubViewBottomAppBar"
            x:Uid="AppBar"
            AutomationProperties.AutomationId="MainHubViewBottomAppBar"
            IsOpen="{Binding Path=IsAppBarOpen, Mode=TwoWay}"
            IsSticky="{Binding Path=IsAppBarSticky, Mode=TwoWay}"
            Padding="10,0,10,0"
            Visibility="{Binding Path=IsAppBarEnabled, Mode=TwoWay, Converter={StaticResource BoolToVisConverter}}">
        <Grid>
            <StackPanel HorizontalAlignment="Left" 
                        Orientation="Horizontal">
                <Button x:Name="RotateButton"
                        x:Uid="RotateAppBarButton"
                        Command="{Binding RotateImageCommand}" 
                        Style="{StaticResource RotateAppBarButtonStyle}" 
                        Tag="Rotate" />
                <Button x:Name="CropButton"
                        x:Uid="CropAppBarButton"
                        Command="{Binding CropImageCommand}"
                        Style="{StaticResource CropAppBarButtonStyle}"
                        Tag="Crop" />
                <Button x:Name="CartoonizeButton"
                        x:Uid="CartoonizeAppBarButton"
                        Command="{Binding CartoonizeImageCommand}"
                        Style="{StaticResource CartoonEffectAppBarButtonStyle}"
                        Tag="Cartoon effect" />
                <Button x:Name="RotateButtonNoLabel"
                        Command="{Binding RotateImageCommand}" 
                        Style="{StaticResource RotateAppBarButtonNoLabelStyle}" 
                        Tag="Rotate"
                        Visibility="Collapsed">
                    <ToolTipService.ToolTip>
                        <ToolTip x:Uid="RotateAppBarButtonToolTip" />
                    </ToolTipService.ToolTip>
                </Button>
                <Button x:Name="CropButtonNoLabel"
                        Command="{Binding CropImageCommand}"
                        Style="{StaticResource CropAppBarButtonNoLabelStyle}"
                        Tag="Crop"
                        Visibility="Collapsed">
                    <ToolTipService.ToolTip>
                        <ToolTip x:Uid="CropAppBarButtonToolTip" />
                    </ToolTipService.ToolTip>
                </Button>
                <Button x:Name="CartoonizeButtonNoLabel"
                        Command="{Binding CartoonizeImageCommand}"
                        Style="{StaticResource CartoonEffectAppBarButtonNoLabelStyle}"
                        Tag="Cartoon effect"
                        Visibility="Collapsed">
                    <ToolTipService.ToolTip>
                        <ToolTip x:Uid="CartoonizeAppBarButtonToolTip" />
                    </ToolTipService.ToolTip>
                </Button>
            </StackPanel>
        </Grid>
    </AppBar>
</local:HiloPage.BottomAppBar>


Abbiamo impostato la proprietà Orientation su Horizontal per disporre da sinistra a destra gli elementi figlio di StackPanel. Imposta Orientation su Vertical per disporre gli elementi figlio dall'alto verso il basso.

ListView

Il controllo ListView visualizza i dati in senso verticale. Abbiamo utilizzato questo controllo, tra l'altro, per visualizzare verticalmente le foto presenti sulla pagina di visualizzazione delle immagini quando l'app è nella visualizzazione ancorata.

Uso di ListView per visualizzare i dati in senso verticale

Ecco il codice XAML aggiornato per ListView.

ImageView.xaml


<ListView x:Name="SnappedPhotosFilmStripListView"
          Grid.Row="1"
          AutomationProperties.AutomationId="SnappedPhotosFilmStripListView"
          Margin="0,-10,0,0"
          Padding="10,0,0,60"
          ItemsSource="{Binding Photos}" 
          IsItemClickEnabled="False"
          ItemContainerStyle="{StaticResource HiloListViewItemStyle}"
          SelectedItem="{Binding SelectedItem, Mode=TwoWay}"
          SelectionMode="Single"
          Visibility="Collapsed">
    <ListView.ItemsPanel>
        <ItemsPanelTemplate>
            <VirtualizingStackPanel Orientation="Vertical" />
        </ItemsPanelTemplate>
    </ListView.ItemsPanel>
    <ListView.ItemTemplate>
        <DataTemplate>
            <Border>
                <Image Source="{Binding Path=Thumbnail}" 
                       Stretch="UniformToFill" />
            </Border>
        </DataTemplate>
    </ListView.ItemTemplate>
</ListView>


ListView usa lo stile HiloListViewItemStyle, definito in ApplicationStyles.xaml, per personalizzare l'aspetto delle istanze di ListViewItem visualizzate in ListView. Ogni ListViewItem è costituito da un controllo Image posizionato in un controllo Border e definito nel DataTemplate di ListView. Per supportare la virtualizzazione dell'interfaccia utente, abbiamo usato un VirtualizingStackPanel definito nel ItemsPanelTemplate di ListView. per visualizzare le istanze di ListViewItem. Per altre info sulla virtualizzazione dell'interfaccia utente, vedi l'argomento su virtualizzazione dell'interfaccia utente per usare grandi set di dati in questa guida.

Quando l'app passa alla visualizzazione ancorata, nascondiamo la griglia delle foto contenente il controllo Image e visualizziamo le foto come una filmstrip verticale.

ImageView.xaml


<ObjectAnimationUsingKeyFrames Storyboard.TargetName="PhotoGrid" 
                               Storyboard.TargetProperty="Visibility">
    <DiscreteObjectKeyFrame KeyTime="0" 
                            Value="Collapsed"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="SnappedPhotosFilmStripListView" 
                               Storyboard.TargetProperty="Visibility">
    <DiscreteObjectKeyFrame KeyTime="0" 
                            Value="Visible"/>
</ObjectAnimationUsingKeyFrames>


A nostro avviso il layout verticale offerto da ListView rappresenta un modo del tutto naturale di visualizzare gruppi di foto nella visualizzazione ancorata. ListView è simile a GridView— in quanto entrambi ereditano ListViewBase. La differenza principale consiste nel modo in cui definiscono i rispettivi layout.

Per altre info su ListView, vedi l'argomento sull'aggiunta dei controlli ListView e GridView, l'esempio di aspetti fondamentali dei controlli XAML ListView e GridView e l'esempio di personalizzazione dell'interattività dei controlli XAML ListView e GridView.

SemanticZoom

Il controllo SemanticZoom consente all'utente di eseguire lo zoom tra due visualizzazioni di una raccolta di elementi. Per altre info su come abbiamo usato il controllo per consentire agli utenti di esplorare grandi insiemi di immagini, vedi l'argomento su avvicinamento e allontanamento delle dita per le operazioni di zoom in questa guida.

Canvas e ContentControl

Il controllo Canvas supporta il posizionamento assoluto di elementi figlio rispetto all'angolo superiore sinistro del canvas. Abbiamo usato il controllo Canvas per controllare la posizione del rettangolo che viene regolato quando si ritaglia una foto.

Suggerimento   Quando non devi arrivare a un posizionamento assoluto, usa un controllo di layout dinamico come Grid. Un layout dinamico si adatta automaticamente a varie risoluzioni e orientamenti dello schermo. I controlli di layout fisso, come Canvas, consentono un controllo maggiore, ma quando l'orientamento o il layout della pagina vengono modificati bisogna aggiornare manualmente il layout.
 

Ecco il codice XAML aggiornato per Canvas.

CropImageView.xaml


<Canvas x:Name="CropCanvas" HorizontalAlignment="Left" VerticalAlignment="Top">
    <ContentControl x:Name="CropOverlay"
                    Canvas.Left="{Binding CropOverlayLeft}"
                    Canvas.Top="{Binding CropOverlayTop}"
                    Height="{Binding CropOverlayHeight}"
                    MinHeight="100"
                    MinWidth="100"
                    Template="{StaticResource HiloCroppingOverlayTemplate}"
                    Visibility="{Binding Path=IsCropOverlayVisible, Converter={StaticResource BoolToVisConverter}}"
                    Width="{Binding CropOverlayWidth}">
        <Grid Background="Transparent"
              HorizontalAlignment="Stretch"
              VerticalAlignment="Stretch"
              Tapped="OnCropRectangleTapped">
        </Grid>
    </ContentControl>
</Canvas>


Ecco come appare l'esperienza utente di ritaglio di Hilo:

Modalità ritaglio in Hilo

Il controllo Canvas contiene un elemento figlio, —un ContentControl che implementa i punti trascinabili mediante i quali l'utente posiziona i contorni del rettangolo di ritaglio. Un ContentControl rappresenta un controllo con un solo elemento di contenuto. Button e altri controlli standard ereditano da ContentControl. Usiamo ContentControl per i contenuti personalizzati. Il modello ControlTemplate, HiloCroppingOverlayTemplate, è definito nella sezione Resources di CropImageView.xaml. ControlTemplate definisce un elemento Thumb per ciascuno degli otto punti trascinabili. HiloCroppingOverlayThumbStyle definisce per ogni punto colore, forma e altri elementi stilistici e viene definito in ApplicationStyles.xaml.

CropImageView.xaml


<ControlTemplate x:Key="HiloCroppingOverlayTemplate" TargetType="ContentControl">
    <Grid>
        <Thumb Canvas.ZIndex="1"
               DragDelta="OnThumbDragDelta"
               HorizontalAlignment="Stretch" 
               Style="{StaticResource HiloCroppingOverlayThumbStyle}"
               VerticalAlignment="Top" />
        <Thumb Canvas.ZIndex="1"
               DragDelta="OnThumbDragDelta" 
               HorizontalAlignment="Left"
               Style="{StaticResource HiloCroppingOverlayThumbStyle}"
               VerticalAlignment="Stretch" />
        <Thumb Canvas.ZIndex="1"
               DragDelta="OnThumbDragDelta"
               HorizontalAlignment="Right"
               Style="{StaticResource HiloCroppingOverlayThumbStyle}"
               VerticalAlignment="Stretch" />
        <Thumb Canvas.ZIndex="1"
               DragDelta="OnThumbDragDelta"
               HorizontalAlignment="Stretch"
               Style="{StaticResource HiloCroppingOverlayThumbStyle}"
               VerticalAlignment="Bottom" />
        <Thumb Canvas.ZIndex="1"
               DragDelta="OnThumbDragDelta"  
               HorizontalAlignment="Left"
               Style="{StaticResource HiloCroppingOverlayThumbStyle}"
               VerticalAlignment="Top" />
        <Thumb Canvas.ZIndex="1"
               DragDelta="OnThumbDragDelta" 
               HorizontalAlignment="Right"
               Style="{StaticResource HiloCroppingOverlayThumbStyle}"
               VerticalAlignment="Top" />
        <Thumb Canvas.ZIndex="1"
               DragDelta="OnThumbDragDelta" 
               HorizontalAlignment="Left"
               Style="{StaticResource HiloCroppingOverlayThumbStyle}"
               VerticalAlignment="Bottom" />
        <Thumb Canvas.ZIndex="1"
               DragDelta="OnThumbDragDelta" 
               HorizontalAlignment="Right"
               Style="{StaticResource HiloCroppingOverlayThumbStyle}"
               VerticalAlignment="Bottom" />
        <ContentPresenter Content="{TemplateBinding ContentControl.Content}" />
    </Grid>
</ControlTemplate>


Per altre info sull'esperienza utente di ritaglio, vedi Uso del tocco in questa guida.

Popup

Abbiamo usato il controllo Popup per visualizzare altre info su un'immagine quando l'utente tiene premuta l'immagine corrente. Per altre info su come abbiamo utilizzato questo controllo, vedi Pressione prolungata per imparare in questa guida.

[In alto]

Applicazione di stili ai controlli

Per personalizzare l'aspetto di Hilo abbiamo applicato stili e modelli ai controlli utilizzati nell'app.

Con gli stili puoi impostare le proprietà dei controlli e riusarle per creare controlli dall'aspetto uniforme. Si possono definire stili incorporati nel codice XAML per i controlli oppure come risorsa riutilizzabile. Le risorse si possono definire nel file XAML di una pagina, nel file App.xaml oppure in un dizionario risorse a parte. Un dizionario risorse può essere condiviso tra app e più dizionari risorse possono essere uniti in un'unica app. Per altre info, vedi Guida introduttiva: Applicazione di stili ai controlli.

Si possono personalizzare struttura e aspetto di un controllo definendo un nuovo modello ControlTemplate del controllo. L'assegnazione di un modello a un controllo spesso consente di evitare di scrivere controlli personalizzati. Per altre info, vedi Guida introduttiva: Modelli di controllo.

[In alto]

Virtualizzazione dell'interfaccia utente per usare grandi set di dati

Dato che Hilo è un'app per la gestione di foto, dovevamo pensare al modo più efficace per caricare e visualizzare le immagini degli utenti. Era sottinteso che per ogni mese gli utenti potevano avere centinaia di foto o solo alcune, e perciò volevamo creare un'esperienza utente che fosse efficace in entrambi i casi.

Inoltre, volevamo ridurre al minimo il consumo di memoria da parte dell'app. Infatti, le probabilità di chiusura di un'app sospesa o inattiva sono maggiori se il consumo di memoria è elevato.

Dato che viene visualizzato in contemporanea solo un sottoinsieme di foto, abbiamo fatto ricorso alla virtualizzazione dell'interfaccia utente. La virtualizzazione dell'interfaccia utente consente ai controlli derivanti da ItemsControl, vale a dire quelli che si possono usare per presentare una raccolta di elementi, di caricare in memoria solo gli elementi dell'interfaccia utente vicini al riquadro di visualizzazione, ossia l'area del controllo visibile. Mentre l'utente scorre l'elenco, gli elementi che in precedenza erano vicini al riquadro di visualizzazione vengono scaricati dalla memoria e ne vengono caricati di nuovi.

I controlli derivanti da ItemsControl, come ListView e GridView, eseguono la virtualizzazione dell'interfaccia utente per impostazione predefinita. Il codice XAML genera l'interfaccia utente dell'elemento e la trattiene in memoria quando l'elemento sta per essere visualizzato sullo schermo. Quando l'elemento non è più visualizzato, il controllo riutilizza quella memoria per un altro elemento prossimo alla visualizzazione.

Se modifichi lo stile di ItemsControl in modo che utilizzi un pannello diverso da quello predefinito, il controllo continua a supportare la virtualizzazione dell'interfaccia utente a condizione che utilizzi un pannello di virtualizzazione. I pannelli di virtualizzazione standard includono WrapGrid e VirtualizingStackPanel. L'uso di pannelli standard che non eseguono la virtualizzazione, quali VariableSizedWrapGrid e StackPanel, disattiverà la virtualizzazione dell'interfaccia utente per quel controllo.

Nota  La virtualizzazione dell'interfaccia utente non è supportata per i dati raggruppati. Perciò, in Hilo abbiamo limitato le dimensioni dei gruppi per ridurre il footprint di memoria dell'app.
 
Suggerimento  Inoltre, per lavorare in modo più efficiente con grandi set di dati, puoi utilizzare lo zoom semantico. Per altre info sull'uso dello zoom semantico in Hilo, vedi l'argomento su avvicinamento e allontanamento delle dita per le operazioni di zoom.
 

Per altre info sull'uso di grandi set di dati, vedi Utilizzo della virtualizzazione con un elenco o una griglia e Caricare, archiviare e visualizzare in modo efficiente set di dati di grandi dimensioni. Leggi l'argomento sull'ottimizzazione delle prestazioni per conoscere le altre considerazioni sulle prestazioni fatte in Hilo.

[In alto]

Override dei controlli incorporati

Nella pagina hub page volevamo che la prima immagine avesse le dimensioni di quattro miniature, mentre per le altre larghezza e altezza dovevano restare normali.

Uno stile di layout personalizzato

Non è semplicissimo definire un simile layout solo con gli stili, vale a dire rilevare il primo elemento di una raccolta e disporlo correttamente. Allora, abbiamo definito la classe VariableGridView, derivante da GridView.

VariableGridView.h


[Windows::Foundation::Metadata::WebHostHidden]
public ref class VariableGridView sealed : public Windows::UI::Xaml::Controls::GridView
{
protected:
    virtual void PrepareContainerForItemOverride(Windows::UI::Xaml::DependencyObject^ element, Platform::Object^ item) override;
};


Abbiamo eseguito l'override del metodo PrepareContainerForItemOverride per consentire alla prima immagine di estendersi su più righe e più colonne.

VariableGridView.cpp


void VariableGridView::PrepareContainerForItemOverride(DependencyObject^ element, Object^ item)
{
    auto model = dynamic_cast<IResizable^>(item);

    if (model != nullptr)
    {
        element->SetValue(VariableSizedWrapGrid::ColumnSpanProperty, model->ColumnSpan);
        element->SetValue(VariableSizedWrapGrid::RowSpanProperty, model->RowSpan);
    }

    GridView::PrepareContainerForItemOverride(element, item);
}


Abbiamo definito l'interfaccia IResizable per consentire agli implementatori di definire l'estensione su righe e colonne nel contenitore padre.

IResizable.h


public interface class IResizable
{
    property int ColumnSpan 
    {
        int get();
        void set(int value);
    }

    property int RowSpan
    {
        int get();
        void set(int value);
    }
};


La classe Photo implementa sia IPhoto che IResizable. Il costruttore imposta il valore predefinito dell'estensione su righe e colonne a 1.

Photo.h


[Windows::UI::Xaml::Data::Bindable]
[Windows::Foundation::Metadata::WebHostHidden]
public ref class Photo sealed : public IResizable, public IPhoto, public Windows::UI::Xaml::Data::INotifyPropertyChanged


Il metodo HubPhotoGroup::QueryPhotosAsync, che carica le foto della pagina hub, imposta il valore dell'estensione su righe e colonne relativo alla prima foto della raccolta su 2.

HubPhotoGroup.cpp


bool firstPhoto = true;
for (auto photo : photos)
{
    if (firstPhoto)
    {
        IResizable^ resizable = dynamic_cast<IResizable^>(photo);
        if (nullptr != resizable)
        {
            resizable->ColumnSpan = 2;
            resizable->RowSpan = 2;
        }
        firstPhoto = false;
    }
    m_photos->Append(photo);
}


Ora la prima foto visualizzata nell'hub occupa due righe e due colonne.

Nota  In questa soluzione il modello che abbiamo scelto ha il controllo della visualizzazione e relativo layout. Anche se si viola il modello MVVM, per altre info, vedi Uso del modello MVVM, abbiamo cercato di astrarre la soluzione in modo che restasse flessibile. Dato che la soluzione è destinata a un problema piuttosto limitato, abbiamo ritenuto accettabile il compromesso tra funzionamento dell'app desiderato e introduzione di una qualche associazione tra vista e modello.
 

[In alto]

Tocco e gesti

Il supporto del tocco è incorporato nel runtime di XAML. Dato che il runtime di XAML usa un unico sistema di gestione degli eventi per diverse interazioni dell'utente, il supporto per le interazioni mouse, penna e altre interazioni basate su puntatore è automatico.

Suggerimento  Se progetti l'app per l'esperienza tocco, il supporto per mouse e penna è compreso.
 

Per altre info su come abbiamo usato il tocco in Hilo, vedi Uso del tocco.

[In alto]

Test dei controlli

Quando esegui i test sull'app, assicurati che tutti i controlli si comportino come previsto in configurazioni e orientamenti diversi. Quando abbiamo usato un controllo per aggiungere all'app una nuova funzionalità, ci siamo assicurati che il controllo si comportasse correttamente nelle visualizzazioni ancorata e di riempimento come pure negli orientamenti orizzontale e verticale.

Se il tuo monitor non è abilitato per il tocco, puoi ricorrere al simulatore per emulare avvicinamento delle dita, zoom, rotazione e altri gesti. Inoltre, puoi simulare anche la georilevazione e lavorare con diverse risoluzioni dello schermo. Per ulteriori informazioni, vedi Esecuzione di app di Windows Store nel simulatore.

Puoi testare l'app su un computer in cui non è installato Visual Studio, ma che dispone dell'hardware da verificare. Per ulteriori informazioni, vedi Esecuzione di app di Windows Store in un computer remoto.

Per altre info su come è stato eseguito il test di Hilo, vedi l'argomento sui test delle app.

[In alto]

 

 

Mostra:
© 2017 Microsoft