Esercitazione: Creare un visualizzatore di foto semplice destinato a più piattaforme
Dopo aver creato un visualizzatore di foto semplice iniziale WinUI 3, potresti chiederti come raggiungere più utenti senza dover riscrivere l'app. Questa esercitazione usa Uno Platform per espandere la portata dell'applicazione WinUI 3 C# esistente, consentendo il riutilizzo della logica di business e del livello dell'interfaccia utente in dispositivi mobili, Web e desktop nativi. Eseguendo solo modifiche minime all'app visualizzatore di foto semplice, è possibile eseguire una copia ad alta risoluzione dell'app convertita in queste piattaforme.
Prerequisiti
Configurare il computer di sviluppo (vedere Introduzione a WinUI)
carico di lavoro ASP.NET e sviluppo Web (per lo sviluppo WebAssembly)
Sviluppo di app multipiattaforma .NET installato (per lo sviluppo di app per iOS, Android, Mac Catalyst)
Sviluppo di desktop .NET installato (per lo sviluppo Gtk, Wpf e Linux Framebuffer)
Finalizzare l'ambiente
Aprire un prompt della riga di comando, Terminale Windows se è installato oppure un prompt dei comandi o di Windows PowerShell dal menu Start.
Installare o aggiornare lo strumento
uno-check
:Utilizza il seguente comando:
dotnet tool install -g uno.check
Per aggiornare lo strumento, se è già stata installata una versione precedente:
dotnet tool update -g uno.check
Eseguire lo strumento con il comando seguente:
uno-check
Seguire le istruzioni indicate dallo strumento. Poiché deve modificare il sistema, potrebbe essere richiesto di disporre di autorizzazioni elevate.
Installare i modelli di soluzione Uno Platform
Avviare Visual Studio, quindi fare clic su Continue without code
. Fare clic su Extensions
->Manage Extensions
dalla barra dei menu.
In Gestione estensioni espandere il nodo Online e cercare Uno
, installare l'estensione Uno Platform
o scaricarla e installarla da Visual Studio Marketplace, quindi riavviare Visual Studio.
Creare un'applicazione
Ora che siamo pronti per creare un'applicazione multipiattaforma, l'approccio che verrà adottato consiste nel creare una nuova applicazione Uno Platform. Il codice verrà copiato dal progetto SimplePhotos WinUI 3 dell'esercitazione precedente al progetto multipiattaforma. Ciò è possibile perché Uno Platform consente di riutilizzare il codebase esistente. Per le funzionalità dipendenti dalle API del sistema operativo fornite da ogni piattaforma, è possibile renderle facilmente funzionanti nel tempo. Questo approccio è particolarmente utile se hai un'applicazione esistente che vuoi convertire in altre piattaforme.
A breve, potrai sfruttare i vantaggi di questo approccio, in quanto puoi usare più piattaforme con una versione XAML familiare e il codebase già disponibile.
Aprire Visual Studio e creare un nuovo progetto tramite File
>New
>Project
:
Cercare Uno e selezionare il modello di progetto per l’app Uno Platform:
Creare una nuova soluzione C# usando il tipo di app Uno Platform dalla pagina iniziale di Visual Studio. Per evitare conflitti con il codice dell'esercitazione precedente, a questa soluzione verrà assegnato un nome diverso, "UnoSimplePhotos". Specificare il nome del progetto, il nome della soluzione e la directory. In questo esempio il progetto multipiattaforma UnoSimplePhotos
appartiene a una soluzione UnoSimplePhotos
, che sarà disponibile in C:\Projects:
A questo punto devi scegliere un modello di base per rendere la raccolta dell’app semplice per foto multipiattaforma.
Il modello di app Uno Platform include due opzioni predefinite che consentono di iniziare rapidamente a usare una soluzione vuota o la configurazione predefinita che include riferimenti alle librerie Uno.Material e Uno.Toolkit. La configurazione predefinita include anche Uno.Extensions, usato per l'inserimento delle dipendenze, la configurazione, la navigazione e la registrazione. Inoltre, usa MVUX al posto di MVVM, rendendolo un ottimo punto di partenza per la creazione rapida di applicazioni reali.
Per semplificare le operazioni, selezionare il set di impostazioni Vuoto. Fare quindi clic sul pulsante Crea. Attendere che i progetti vengano creati e che le relative dipendenze vengano ripristinate.
Un banner nella parte superiore dell'editor potrebbe richiedere di ricaricare i progetti, fare clic su Ricarica progetti:
In Esplora soluzioni dovrebbe essere visualizzata la struttura di file predefinita seguente:
Aggiungere risorse immagini al progetto
L'app dovrà visualizzare alcune immagini. È possibile usare le stesse immagini dell'esercitazione precedente.
Nel progetto UnoSimplePhotos
creare una nuova cartella denominata Assets
e copiare i file di immagine JPG in una sottocartella Samples
. La struttura della cartella di Assets
dovrebbe risultare come segue:
Per ulteriori informazioni sulla creazione della cartella Assets
e sull'aggiunta di immagini, consultare la documentazione di Uno Platform relativa alle Risorse e alla visualizzazione delle immagini.
Preparazione dell’app
Dopo aver generato il punto di partenza funzionale dell'applicazione WinUI multipiattaforma, è possibile copiare il codice dal progetto desktop.
Copiare la visualizzazione
Poiché Uno Platform consente di usare la versione XAML già nota, è possibile copiare lo stesso codice creato nell'esercitazione precedente.
Tornare al progetto SimplePhotos dell'esercitazione precedente. InEsplora soluzioni trovare il file denominato MainWindow.xaml
e aprirlo. Tieni presente che i contenuti della visualizzazione sono definito all'interno di un elemento Window
anziché di un oggetto Page
. Questo perché il progetto desktop è un'applicazione WinUI 3, che può usare elementi Window
per definire il contenuto della visualizzazione:
<Window x:Class="SimplePhotos.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:SimplePhotos"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Grid>
<Grid.Resources>
<DataTemplate x:Key="ImageGridView_ItemTemplate"
x:DataType="local:ImageFileInfo">
<Grid Height="300"
Width="300"
Margin="8">
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Image x:Name="ItemImage"
Source="Assets/StoreLogo.png"
Stretch="Uniform" />
<StackPanel Orientation="Vertical"
Grid.Row="1">
<TextBlock Text="{x:Bind ImageTitle}"
HorizontalAlignment="Center"
Style="{StaticResource SubtitleTextBlockStyle}" />
<StackPanel Orientation="Horizontal"
HorizontalAlignment="Center">
<TextBlock Text="{x:Bind ImageFileType}"
HorizontalAlignment="Center"
Style="{StaticResource CaptionTextBlockStyle}" />
<TextBlock Text="{x:Bind ImageDimensions}"
HorizontalAlignment="Center"
Style="{StaticResource CaptionTextBlockStyle}"
Margin="8,0,0,0" />
</StackPanel>
<RatingControl Value="{x:Bind ImageRating}"
IsReadOnly="True"/>
</StackPanel>
</Grid>
</DataTemplate>
<Style x:Key="ImageGridView_ItemContainerStyle"
TargetType="GridViewItem">
<Setter Property="Background"
Value="Gray"/>
<Setter Property="Margin"
Value="8"/>
</Style>
<ItemsPanelTemplate x:Key="ImageGridView_ItemsPanelTemplate">
<ItemsWrapGrid Orientation="Horizontal"
HorizontalAlignment="Center"/>
</ItemsPanelTemplate>
</Grid.Resources>
<GridView x:Name="ImageGridView"
ItemsSource="{x:Bind Images}"
ItemTemplate="{StaticResource ImageGridView_ItemTemplate}"
ItemContainerStyle="{StaticResource ImageGridView_ItemContainerStyle}"
ItemsPanel="{StaticResource ImageGridView_ItemsPanelTemplate}"
ContainerContentChanging="ImageGridView_ContainerContentChanging" />
</Grid>
</Window>
L'implementazione multipiattaforma di Uno Platform dei controlli trovati nell'elemento Window
, come GridView
, Image
e RatingControl
, garantisce che la visualizzazione stessa funzioni su tutte le piattaforme supportate con una semplice quantità di lavoro. Copiare il contenuto di Window
e incollarlo nell'elemento Page
del file MainPage.xaml
nel progetto UnoSimplePhotos Uno Platform. La visualizzazione XAML MainPage
dovrebbe risultare come segue:
<Page x:Class="UnoSimplePhotos.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:UnoSimplePhotos"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Grid>
<Grid.Resources>
<DataTemplate x:Key="ImageGridView_ItemTemplate"
x:DataType="local:ImageFileInfo">
<Grid Height="300"
Width="300"
Margin="8">
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Image x:Name="ItemImage"
Source="Assets/StoreLogo.png"
Stretch="Uniform" />
<StackPanel Orientation="Vertical"
Grid.Row="1">
<TextBlock Text="{x:Bind ImageTitle}"
HorizontalAlignment="Center"
Style="{StaticResource SubtitleTextBlockStyle}" />
<StackPanel Orientation="Horizontal"
HorizontalAlignment="Center">
<TextBlock Text="{x:Bind ImageFileType}"
HorizontalAlignment="Center"
Style="{StaticResource CaptionTextBlockStyle}" />
<TextBlock Text="{x:Bind ImageDimensions}"
HorizontalAlignment="Center"
Style="{StaticResource CaptionTextBlockStyle}"
Margin="8,0,0,0" />
</StackPanel>
<RatingControl Value="{x:Bind ImageRating}"
IsReadOnly="True"/>
</StackPanel>
</Grid>
</DataTemplate>
<Style x:Key="ImageGridView_ItemContainerStyle"
TargetType="GridViewItem">
<Setter Property="Background"
Value="Gray"/>
<Setter Property="Margin"
Value="8"/>
</Style>
<ItemsPanelTemplate x:Key="ImageGridView_ItemsPanelTemplate">
<ItemsWrapGrid Orientation="Horizontal"
HorizontalAlignment="Center"/>
</ItemsPanelTemplate>
</Grid.Resources>
<GridView x:Name="ImageGridView"
ItemsSource="{x:Bind Images}"
ItemTemplate="{StaticResource ImageGridView_ItemTemplate}"
ItemContainerStyle="{StaticResource ImageGridView_ItemContainerStyle}"
ItemsPanel="{StaticResource ImageGridView_ItemsPanelTemplate}"
ContainerContentChanging="ImageGridView_ContainerContentChanging">
</GridView>
</Grid>
</Page>
Forse ricorderai che la soluzione desktop aveva anche un file MainWindow.xaml.cs
che conteneva code-behind che corrisponde alla visualizzazione. Nel progetto Uno Platform il code-behind per la visualizzazione MainPage
copiata è contenuto nel file MainPage.xaml.cs
.
Per rendere questo code-behind multipiattaforma, è necessario prima spostare il codice seguente nel file MainPage.xaml.cs
:
Proprietà
Images
: fornisceGridView
con una raccolta osservabile di file di immagineContenuto del costruttore: chiama
GetItemsAsync()
per popolare la raccoltaImages
con elementi che rappresentano i file di immagineRimuovere la modifica manuale della proprietà
ImageGridView
del controlloItemsSource
Metodo
ImageGridView_ContainerContentChanging
: usato come parte di una strategia per caricare progressivamente elementiGridView
mentre vengono fatti scorrere nella visualizzazioneMetodo
ShowImage
: carica i file di immagine nell'oggettoGridView
Metodo
GetItemsAsync
: ottiene i file della risorsa immagine dalla cartellaSamples
Metodo
LoadImageInfoAsync
: costruisce un oggettoImageFileInfo
da unStorageFile
creato
Dopo aver spostato tutto, MainPage.xaml.cs
dovrebbe risultare come segue:
using Microsoft.UI.Xaml.Controls;
using System.Collections.ObjectModel;
using Windows.Storage;
using Windows.Storage.Search;
namespace UnoSimplePhotos;
public sealed partial class MainPage : Page
{
public ObservableCollection<ImageFileInfo> Images { get; }
= new ObservableCollection<ImageFileInfo>();
public MainPage()
{
this.InitializeComponent();
GetItemsAsync();
}
private void ImageGridView_ContainerContentChanging(ListViewBase sender,
ContainerContentChangingEventArgs args)
{
if (args.InRecycleQueue)
{
var templateRoot = args.ItemContainer.ContentTemplateRoot as Grid;
var image = templateRoot.FindName("ItemImage") as Image;
image.Source = null;
}
if (args.Phase == 0)
{
args.RegisterUpdateCallback(ShowImage);
args.Handled = true;
}
}
private async void ShowImage(ListViewBase sender, ContainerContentChangingEventArgs args)
{
if (args.Phase == 1)
{
// It's phase 1, so show this item's image.
var templateRoot = args.ItemContainer.ContentTemplateRoot as Grid;
var image = templateRoot.FindName("ItemImage") as Image;
var item = args.Item as ImageFileInfo;
image.Source = await item.GetImageThumbnailAsync();
}
}
private async Task GetItemsAsync()
{
StorageFolder appInstalledFolder = Package.Current.InstalledLocation;
StorageFolder picturesFolder = await appInstalledFolder.GetFolderAsync("Assets\\Samples");
var result = picturesFolder.CreateFileQueryWithOptions(new QueryOptions());
IReadOnlyList<StorageFile> imageFiles = await result.GetFilesAsync();
foreach (StorageFile file in imageFiles)
{
Images.Add(await LoadImageInfoAsync(file));
}
}
public async static Task<ImageFileInfo> LoadImageInfoAsync(StorageFile file)
{
var properties = await file.Properties.GetImagePropertiesAsync();
ImageFileInfo info = new(properties,
file, file.DisplayName, file.DisplayType);
return info;
}
}
Nota
I file nel progetto dell’app Uno devono essere usare UnoSimplePhotos
come spazio dei nomi.
Finora, i file per la visualizzazione principale con cui stiamo lavorando contengono tutte le funzionalità della soluzione desktop. Dopo aver copiato il file del modello ImageFileInfo.cs
, capiremo come modificare i blocchi orientati al desktop del codice per la compatibilità multipiattaforma.
Copiare ImageFileInfo
dal progetto desktop e incollarlo nel file ImageFileInfo.cs
. Apportare le modifiche seguenti:
Rinominare lo spazio dei nomi in modo che sia
UnoSimplePhotos
invece diSimplePhotos
:// Found towards the top of the file namespace UnoSimplePhotos;
Modificare il tipo di parametro del metodo
OnPropertyChanged
in modo che sia annullabile:// string -> string? protected void OnPropertyChanged([CallerMemberName] string? propertyName = null) ...
Rendere annullabile
PropertyChangedEventHandler
:// PropertyChangedEventHandler -> PropertyChangedEventHandler? public event PropertyChangedEventHandler? PropertyChanged;
Mettere insieme, ora il file dovrebbe risultare come segue:
using Microsoft.UI.Xaml.Media.Imaging;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using Windows.Storage;
using Windows.Storage.FileProperties;
using Windows.Storage.Streams;
using ThumbnailMode = Windows.Storage.FileProperties.ThumbnailMode;
namespace UnoSimplePhotos;
public class ImageFileInfo : INotifyPropertyChanged
{
public ImageFileInfo(ImageProperties properties,
StorageFile imageFile,
string name,
string type)
{
ImageProperties = properties;
ImageName = name;
ImageFileType = type;
ImageFile = imageFile;
var rating = (int)properties.Rating;
var random = new Random();
ImageRating = rating == 0 ? random.Next(1, 5) : rating;
}
public StorageFile ImageFile { get; }
public ImageProperties ImageProperties { get; }
public async Task<BitmapImage> GetImageSourceAsync()
{
using IRandomAccessStream fileStream = await ImageFile.OpenReadAsync();
// Create a bitmap to be the image source.
BitmapImage bitmapImage = new();
bitmapImage.SetSource(fileStream);
return bitmapImage;
}
public async Task<BitmapImage> GetImageThumbnailAsync()
{
StorageItemThumbnail thumbnail =
await ImageFile.GetThumbnailAsync(ThumbnailMode.PicturesView);
// Create a bitmap to be the image source.
var bitmapImage = new BitmapImage();
bitmapImage.SetSource(thumbnail);
thumbnail.Dispose();
return bitmapImage;
}
public string ImageName { get; }
public string ImageFileType { get; }
public string ImageDimensions => $"{ImageProperties.Width} x {ImageProperties.Height}";
public string ImageTitle
{
get => string.IsNullOrEmpty(ImageProperties.Title) ? ImageName : ImageProperties.Title;
set
{
if (ImageProperties.Title != value)
{
ImageProperties.Title = value;
_ = ImageProperties.SavePropertiesAsync();
OnPropertyChanged();
}
}
}
public int ImageRating
{
get => (int)ImageProperties.Rating;
set
{
if (ImageProperties.Rating != value)
{
ImageProperties.Rating = (uint)value;
_ = ImageProperties.SavePropertiesAsync();
OnPropertyChanged();
}
}
}
public event PropertyChangedEventHandler? PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] string? propertyName = null) =>
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
Questa classe fungerà da modello per rappresentare i file di immagine in GridView
. Sebbene sia tecnicamente possibile eseguire l'app a questo punto, potrebbe non eseguire correttamente il rendering delle immagini o visualizzare le relative proprietà. Nelle sezioni successive apporteremo una serie di modifiche a questi file copiati per renderli compatibili in un contesto multipiattaforma.
Usare le direttive per il preprocessore
Nel progetto desktop dell'esercitazione precedente il file MainPage.xaml.cs
contiene un metodo GetItemsAsync
che elenca gli elementi da StorageFolder
che rappresenta il percorso del pacchetto installato. Poiché tale percorso non è disponibile su determinate piattaforme, come WebAssembly, è necessario apportare modifiche a questo metodo per renderlo compatibile con tutte le piattaforme. Di conseguenza verranno apportate alcune modifiche alla classe ImageFileInfo
per garantire la compatibilità.
Prima di tutto, apportare le modifiche necessarie al metodo GetItemsAsync
. Sostituire il metodo GetItemsAsync
nel file MainPage.xaml.cs
con il seguente codice:
private async Task GetItemsAsync()
{
#if WINDOWS
StorageFolder appInstalledFolder = Package.Current.InstalledLocation;
StorageFolder picturesFolder = await appInstalledFolder.GetFolderAsync("UnoSimplePhotos\\Assets\\Samples");
var result = picturesFolder.CreateFileQueryWithOptions(new QueryOptions());
IReadOnlyList<StorageFile> imageFiles = await result.GetFilesAsync();
#else
var imageFileNames = Enumerable.Range(1, 20).Select(i => new Uri($"ms-appx:///UnoSimplePhotos/Assets/Samples/{i}.jpg"));
var imageFiles = new List<StorageFile>();
foreach (var file in imageFileNames)
{
imageFiles.Add(await StorageFile.GetFileFromApplicationUriAsync(file));
}
#endif
foreach (StorageFile file in imageFiles)
{
Images.Add(await LoadImageInfoAsync(file));
}
}
Questo metodo usa ora una direttiva per il preprocessore per determinare il codice da eseguire in base alla piattaforma. In Windows, il metodo ottiene l'oggetto StorageFolder
che rappresenta il percorso del pacchetto installato e ne restituisce la cartella Samples
. In altre piattaforme, il metodo conta fino a 20, recuperando i file di immagine dalla cartella Samples
usando un Uri
per rappresentare il file di immagine.
Modificare quindi il metodo LoadImageInfoAsync
per adattare le modifiche apportate al metodo GetItemsAsync
. Sostituire il metodo LoadImageInfoAsync
nel file MainPage.xaml.cs
con il seguente codice:
public async static Task<ImageFileInfo> LoadImageInfoAsync(StorageFile file)
{
#if WINDOWS
var properties = await file.Properties.GetImagePropertiesAsync();
ImageFileInfo info = new(properties,
file, file.DisplayName, $"{file.FileType} file");
#else
ImageFileInfo info = new(file, file.DisplayName, $"{file.FileType} file");
#endif
return info;
}
Così come il metodo GetItemsAsync
, anche questo metodo ora usa una direttiva per il preprocessore per determinare il codice da eseguire in base alla piattaforma. In Windows il metodo ottiene l'oggetto ImageProperties
da StorageFile
e lo usa per creare un oggetto ImageFileInfo
. In altre piattaforme il metodo costruisce un oggetto ImageFileInfo
senza il parametro ImageProperties
. Successivamente, verranno apportate modifiche alla classe ImageFileInfo
per supportare questa modifica.
Controlli come GridView
consentono il caricamento progressivo del contenuto del contenitore di elementi aggiornato mentre vengono fatte scorrere nel riquadro di visualizzazione. A tale scopo, viene usato l'evento ContainerContentChanging
. Nel progetto desktop dell'esercitazione precedente il metodo ImageGridView_ContainerContentChanging
usa questo evento per caricare i file di immagine in GridView
. Poiché alcuni aspetti di questo evento non sono supportati in tutte le piattaforme, è necessario apportare modifiche a questo metodo per renderlo compatibile.
Ad esempio, la proprietà ContainerContentChangingEventArgs.Phase
attualmente non è supportata su piattaforme diverse da Windows. Sarà necessario apportare modifiche al metodo ImageGridView_ContainerContentChanging
per soddisfare questa modifica. Sostituire il metodo ImageGridView_ContainerContentChanging
nel file MainPage.xaml.cs
con il seguente codice:
private void ImageGridView_ContainerContentChanging(
ListViewBase sender,
ContainerContentChangingEventArgs args)
{
if (args.InRecycleQueue)
{
var templateRoot = args.ItemContainer.ContentTemplateRoot as Grid;
var image = templateRoot?.FindName("ItemImage") as Image;
if (image is not null)
{
image.Source = null;
}
}
#if WINDOWS
if (args.Phase == 0)
{
args.RegisterUpdateCallback(ShowImage);
args.Handled = true;
}
#else
ShowImage(sender, args);
#endif
}
Il callback specializzato è ora registrato solo usando ContainerContentChangingEventArgs.RegisterUpdateCallback()
se la piattaforma è Windows. In caso contrario, il metodo ShowImage
viene chiamato direttamente. Sarà anche necessario apportare modifiche al metodo ShowImage
per lavorare insieme alle modifiche apportate al metodo ImageGridView_ContainerContentChanging
. Sostituire il metodo ShowImage
nel file MainPage.xaml.cs
con il seguente codice:
private async void ShowImage(ListViewBase sender, ContainerContentChangingEventArgs args)
{
if (
#if WINDOWS
args.Phase == 1
#else
true
#endif
)
{
// It's phase 1, so show this item's image.
var templateRoot = args.ItemContainer.ContentTemplateRoot as Grid;
var image = templateRoot?.FindName("ItemImage") as Image;
var item = args.Item as ImageFileInfo;
#if WINDOWS
if (image is not null && item is not null)
{
image.Source = await item.GetImageThumbnailAsync();
}
#else
if (item is not null)
{
await item.GetImageSourceAsync();
}
#endif
}
}
Anche in questo caso, le direttive per il preprocessore assicurano che la proprietà ContainerContentChangingEventArgs.Phase
venga usata solo nelle piattaforme in cui è supportata. Usiamo il metodo precedentemente inutilizzato GetImageSourceAsync()
per caricare i file di immagine in GridView
nelle piattaforme diverse da Windows. A questo punto, le modifiche apportate in precedenza verranno modificate modificando la classe ImageFileInfo
.
Creazione di un percorso di codice separato per altre piattaforme
Aggiornare ImageFileInfo.cs
per includere una nuova proprietà denominata ImageSource
che verrà usata per caricare il file di immagine.
public BitmapImage? ImageSource { get; private set; }
Poiché le piattaforme come quelle Web non supportano proprietà avanzate dei file di immagine che sono facilmente disponibili in Windows, verrà aggiunto un overload del costruttore che non richiede un parametro ImageProperties
tipizzato. Aggiungere il nuovo overload dopo quello esistente usando il codice seguente:
public ImageFileInfo(StorageFile imageFile,
string name,
string type)
{
ImageName = name;
ImageFileType = type;
ImageFile = imageFile;
}
Questo overload del costruttore viene usato per costruire un oggetto ImageFileInfo
su piattaforme diverse da Windows. Dal momento che abbiamo effettuato questa operazione, è opportuno rendere annullabile la proprietà ImageProperties
. Aggiornare la proprietà ImageProperties
in modo che sia annullabile usando il codice seguente:
public ImageProperties? ImageProperties { get; }
Aggiornare il metodo GetImageSourceAsync
per utilizzare la proprietà ImageSource
anziché restituire solo un oggetto BitmapImage
. Sostituire il metodo GetImageSourceAsync
nel file ImageFileInfo.cs
con il seguente codice:
public async Task<BitmapImage> GetImageSourceAsync()
{
using IRandomAccessStream fileStream = await ImageFile.OpenReadAsync();
// Create a bitmap to be the image source.
BitmapImage bitmapImage = new();
bitmapImage.SetSource(fileStream);
ImageSource = bitmapImage;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(ImageSource)));
return bitmapImage;
}
Per evitare di ottenere il valore di ImageProperties
quando è annullabile, apportare le modifiche seguenti:
Modificare la proprietà
ImageDimensions
per usare l'operatore condizionale nullo:public string ImageDimensions => $"{ImageProperties?.Width} x {ImageProperties?.Height}";
Modificare la proprietà
ImageTitle
in modo da usare l'operatore condizionale nullo:public string ImageTitle { get => string.IsNullOrEmpty(ImageProperties?.Title) ? ImageName : ImageProperties?.Title; set { if (ImageProperties is not null) { if (ImageProperties.Title != value) { ImageProperties.Title = value; _ = ImageProperties.SavePropertiesAsync(); OnPropertyChanged(); } } } }
Modificare
ImageRating
in modo da non dover dipendere daImageProperties
generando una classificazione a stella casuale a scopo dimostrativo:public int ImageRating { get => (int)((ImageProperties?.Rating == null || ImageProperties.Rating == 0) ? (uint)Random.Shared.Next(1, 5) : ImageProperties.Rating); set { if (ImageProperties is not null) { if (ImageProperties.Rating != value) { ImageProperties.Rating = (uint)value; _ = ImageProperties.SavePropertiesAsync(); OnPropertyChanged(); } } } }
Aggiornare il costruttore che genera un numero intero casuale per non eseguire più questa operazione:
public ImageFileInfo(ImageProperties properties, StorageFile imageFile, string name, string type) { ImageProperties = properties; ImageName = name; ImageFileType = type; ImageFile = imageFile; }
Con queste modifiche, la classe ImageFileInfo
deve contenere il codice seguente. Ora ha un percorso di codice separato per le piattaforme diverse da Windows:
using Microsoft.UI.Xaml.Media.Imaging;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using Windows.Storage;
using Windows.Storage.FileProperties;
using Windows.Storage.Streams;
using ThumbnailMode = Windows.Storage.FileProperties.ThumbnailMode;
namespace UnoSimplePhotos;
public class ImageFileInfo : INotifyPropertyChanged
{
public BitmapImage? ImageSource { get; private set; }
public ImageFileInfo(ImageProperties properties,
StorageFile imageFile,
string name,
string type)
{
ImageProperties = properties;
ImageName = name;
ImageFileType = type;
ImageFile = imageFile;
}
public ImageFileInfo(StorageFile imageFile,
string name,
string type)
{
ImageName = name;
ImageFileType = type;
ImageFile = imageFile;
}
public StorageFile ImageFile { get; }
public ImageProperties? ImageProperties { get; }
public async Task<BitmapImage> GetImageSourceAsync()
{
using IRandomAccessStream fileStream = await ImageFile.OpenReadAsync();
// Create a bitmap to be the image source.
BitmapImage bitmapImage = new();
bitmapImage.SetSource(fileStream);
ImageSource = bitmapImage;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(ImageSource)));
return bitmapImage;
}
public async Task<BitmapImage> GetImageThumbnailAsync()
{
StorageItemThumbnail thumbnail =
await ImageFile.GetThumbnailAsync(ThumbnailMode.PicturesView);
// Create a bitmap to be the image source.
var bitmapImage = new BitmapImage();
bitmapImage.SetSource(thumbnail);
thumbnail.Dispose();
return bitmapImage;
}
public string ImageName { get; }
public string ImageFileType { get; }
public string ImageDimensions => $"{ImageProperties?.Width} x {ImageProperties?.Height}";
public string ImageTitle
{
get => string.IsNullOrEmpty(ImageProperties?.Title) ? ImageName : ImageProperties.Title;
set
{
if (ImageProperties is not null)
{
if (ImageProperties.Title != value)
{
ImageProperties.Title = value;
_ = ImageProperties.SavePropertiesAsync();
OnPropertyChanged();
}
}
}
}
public int ImageRating
{
get => (int)((ImageProperties?.Rating == null || ImageProperties.Rating == 0) ? (uint)Random.Shared.Next(1, 5) : ImageProperties.Rating);
set
{
if (ImageProperties is not null)
{
if (ImageProperties.Rating != value)
{
ImageProperties.Rating = (uint)value;
_ = ImageProperties.SavePropertiesAsync();
OnPropertyChanged();
}
}
}
}
public event PropertyChangedEventHandler? PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] string? propertyName = null) =>
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
La classe ImageFileInfo
viene usata per rappresentare i file di immagine in GridView
. Infine, verranno apportate modifiche al file MainPage.xaml
per adattare le modifiche apportate al modello.
Uso del markup XAML specifico della piattaforma
Ci sono un paio di elementi nel markup di visualizzazione che devono essere valutati solo su Windows. Aggiungere un nuovo spazio dei nomi sull'elemento Page
del file MainPage.xaml
come segue:
...
xmlns:win="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
Ora in MainPage.xaml
, sostituire il setter della proprietà ItemsPanel
sull'elemento GridView
con il seguente codice:
win:ItemsPanel="{StaticResource ImageGridView_ItemsPanelTemplate}"
Anteponendo il nome della proprietà con win:
assicura che la proprietà sia impostata solo su Windows. Eseguire di nuovo questa operazione all'interno della risorsa ImageGridView_ItemTemplate
. Vogliamo caricare solo gli elementi che usano la proprietà ImageDimensions
su Windows. Sostituire l'elemento TextBlock
che usa la proprietà ImageDimensions
con il seguente codice:
<win:TextBlock Text="{x:Bind ImageDimensions}"
HorizontalAlignment="Center"
Style="{StaticResource CaptionTextBlockStyle}"
Margin="8,0,0,0" />
Il file MainPage.xaml
dovrebbe risultare come segue:
<Page x:Class="UnoSimplePhotos.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:UnoSimplePhotos"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:win="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
mc:Ignorable="d"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Grid>
<Grid.Resources>
<DataTemplate x:Key="ImageGridView_ItemTemplate"
x:DataType="local:ImageFileInfo">
<Grid Height="300"
Width="300"
Margin="8">
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Image x:Name="ItemImage"
Source="{x:Bind ImageSource}"
Stretch="Uniform" />
<StackPanel Orientation="Vertical"
Grid.Row="1">
<TextBlock Text="{x:Bind ImageTitle}"
HorizontalAlignment="Center"
Style="{StaticResource SubtitleTextBlockStyle}" />
<StackPanel Orientation="Horizontal"
HorizontalAlignment="Center">
<TextBlock Text="{x:Bind ImageFileType}"
HorizontalAlignment="Center"
Style="{StaticResource CaptionTextBlockStyle}" />
<win:TextBlock Text="{x:Bind ImageDimensions}"
HorizontalAlignment="Center"
Style="{StaticResource CaptionTextBlockStyle}"
Margin="8,0,0,0" />
</StackPanel>
<RatingControl Value="{x:Bind ImageRating}"
IsReadOnly="True" />
</StackPanel>
</Grid>
</DataTemplate>
<Style x:Key="ImageGridView_ItemContainerStyle"
TargetType="GridViewItem">
<Setter Property="Background"
Value="Gray" />
<Setter Property="Margin"
Value="8"/>
</Style>
<ItemsPanelTemplate x:Key="ImageGridView_ItemsPanelTemplate">
<ItemsWrapGrid Orientation="Horizontal"
HorizontalAlignment="Center"/>
</ItemsPanelTemplate>
</Grid.Resources>
<GridView x:Name="ImageGridView"
ItemsSource="{x:Bind Images, Mode=OneWay}"
win:ItemsPanel="{StaticResource ImageGridView_ItemsPanelTemplate}"
ContainerContentChanging="ImageGridView_ContainerContentChanging"
ItemContainerStyle="{StaticResource ImageGridView_ItemContainerStyle}"
ItemTemplate="{StaticResource ImageGridView_ItemTemplate}" />
</Grid>
</Page>
Esecuzione dell'app
Avviare la destinazione UnoSimplePhotos.Windows
. Bisogna tenere presente che questa app WinUI è molto simile all'esercitazione precedente.
Ora è possibile creare ed eseguire l'app in una qualsiasi delle piattaforme supportate. A tale scopo, è possibile usare l'elenco a discesa della barra degli strumenti di debug per selezionare una piattaforma di destinazione da distribuire:
Esecuzione dell'intestazione WebAssembly (Wasm):
- Fare clic con il pulsante destro del mouse sul progetto
UnoSimplePhotos.Wasm
, scegliere Imposta come progetto di avvio - Premere il pulsante
UnoSimplePhotos.Wasm
per distribuire l'app - Eventualmente è possibile aggiungere e usare il progetto
UnoSimplePhotos.Server
come alternativa
- Fare clic con il pulsante destro del mouse sul progetto
Esecuzione del debug per iOS:
Fare clic con il pulsante destro del mouse sul progetto
UnoSimplePhotos.Mobile
e scegliere Imposta come progetto di avvioNell'elenco a discesa della barra degli strumenti di debug selezionare un dispositivo iOS attivo o il simulatore. Dovrà essere associato a un Mac per consentire il funzionamento.
Esecuzione del debug per Mac Catalyst:
- Fare clic con il pulsante destro del mouse sul progetto
UnoSimplePhotos.Mobile
e scegliere Imposta come progetto di avvio - Nell'elenco a discesa della barra degli strumenti di debug selezionare un dispositivo macOS remoto. Sarà necessario associarlo a uno per consentire il funzionamento.
- Fare clic con il pulsante destro del mouse sul progetto
Esecuzione del debug della piattaforma Android:
- Fare clic con il pulsante destro del mouse sul progetto
UnoSimplePhotos.Mobile
e scegliere Imposta come progetto di avvio - Nell'elenco a discesa della barra degli strumenti di debug selezionare un dispositivo Android attivo o l'emulatore
- Selezionare un dispositivo attivo nel sottomenu "Dispositivo"
- Fare clic con il pulsante destro del mouse sul progetto
Esecuzione del debug su Linux con Skia GTK:
- Fare clic con il pulsante destro del mouse sul progetto
UnoSimplePhotos.Skia.Gtk
e scegliere Imposta come progetto di avvio - Premere il pulsante
UnoSimplePhotos.Skia.Gtk
per distribuire l'app
- Fare clic con il pulsante destro del mouse sul progetto