Tutoriel : Créer une visionneuse de photos simple qui cible plusieurs plateformes
Une fois que vous aurez créé une application winUI 3 de visionneuse de photos simple de débutant, vous vous demanderez peut-être comment toucher davantage d’utilisateurs sans avoir à réécrire votre application. Ce tutoriel utilise Uno Platform pour élargir la portée de votre application C# WinUI 3 existante, ce qui permet une réutilisation de la logique métier et de la couche d’interface utilisateur sur les plateformes mobiles, web et bureau natives. En apportant un minimum de modifications à l’application de visionneuse de photos simple, nous pouvons porter une copie parfaite de l’application sur ces plateformes.
Prérequis
Configurez votre ordinateur de développement (voir Bien démarrer avec WinUI)
Charge de travail « Développement web et ASP.NET » (pour le développement avec WebAssembly)
Développement de l’interface utilisateur d’application multiplateforme .NET installé (pour le développement iOS, Android, Mac Catalyst)
Développement de bureau .NET installé (pour le développement Gtk, WPF et Linux Framebuffer)
Finaliser votre environnement
Ouvrez une invite de ligne de commande, Terminal Windows si vous l’avez installé, ou sinon Invite de commandes ou Windows PowerShell à partir du menu Démarrer.
Installez ou mettez à jour l’outil
uno-check
:Utilisez la commande suivante :
dotnet tool install -g uno.check
Pour mettre à jour l’outil, si vous avez déjà installé une version antérieure :
dotnet tool update -g uno.check
Exécutez l’outil avec la commande suivante :
uno-check
Suivez les instructions indiquées par l’outil. Sachant qu’il doit modifier votre système, il se peut que des autorisations élevées vous soient demandées.
Installer les modèles de solution Uno Platform
Lancez Visual Studio, puis cliquez sur Continue without code
. Cliquez sur Extensions
->Manage Extensions
dans la barre de menus.
Dans le Gestionnaire d’extensions, développez le nœud En ligne, recherchez Uno
, installez l’extension Uno Platform
ou téléchargez-la et installez-la sur Visual Studio Marketplace, puis redémarrez Visual Studio.
Créer une application
Maintenant que nous sommes prêts à créer une application multiplateforme, l’approche que nous allons adopter consiste à créer une application Uno Platform. Nous allons copier le code du projet WinUI 3 SimplePhotos du tutoriel précédent dans notre projet multiplateforme. Si cela est possible, c’est parce que Uno Platform vous permet de réutiliser le codebase existant. Pour les fonctionnalités qui dépendent des API de système d’exploitation fournies par chaque plateforme, vous pouvez facilement les faire fonctionner au fil du temps. Cette approche est particulièrement utile si vous souhaitez porter une application existante vers d’autres plateformes.
Assez vite, vous pourrez profiter des avantages de cette approche, car vous pourrez cibler davantage de plateformes avec une saveur XAML que vous connaissez et le codebase que vous possédez déjà.
Ouvrez Visual Studio et créez un projet via File
>New
>Project
:
Recherchez Uno et sélectionnez le modèle de projet Uno Platform App :
Créez une solution C# en utilisant le type Uno Platform App dans la page de démarrage de Visual Studio. Pour éviter tout conflit avec le code du tutoriel précédent, nous allons donner à cette solution un autre nom, « UnoSimplePhotos ». Spécifiez le nom du projet, le nom de la solution et le répertoire. Dans cet exemple, notre projet multiplateforme UnoSimplePhotos
appartient à une solution UnoSimplePhotos
, qui résidera dans C:\Projects :
Vous allez maintenant choisir un modèle de base pour rendre votre application de galerie Simple Photo multiplateforme.
Le modèle Uno Platform App propose deux options prédéfinies qui vous permettent de commencer rapidement avec une solution Blank (Vide) ou la configuration Default (Par défaut) qui comprend des références aux bibliothèques Uno.Material et Uno.Toolkit. La configuration Default comprend également Uno.Extensions qui est utile pour l’injection de dépendances, la configuration, la navigation et la journalisation. Par ailleurs, elle utilise MVUX à la place de MVVM, ce qui en fait un excellent point de départ pour la création rapide d’applications réelles.
Pour simplifier les choses, sélectionnez l’option prédéfinie Blank (Vide). Cliquez ensuite sur le bouton Create (Créer). Attendez que les projets soient créés et que leurs dépendances soient restaurées.
Une bannière en haut de l'éditeur peut demander de recharger les projets, cliquez sur Recharger les projets :
La structure de fichiers par défaut présentée ci-dessous doit alors s’afficher dans votre Explorateur de solutions :
Ajouter des ressources d’images au projet
Votre application va avoir besoin d’images à afficher. Vous pouvez utiliser celles du tutoriel précédent.
Dans le projet UnoSimplePhotos
, créez un dossier nommé Assets
et copiez les fichiers image JPG dans un sous-dossier Samples
. La structure de dossiers Assets
doit maintenant se présenter comme ceci :
Pour savoir comment créer le dossier Assets
et y ajouter des images, consultez la documentation Uno Platform traitant des ressources et de l’affichage d’images.
Préparation de votre application
Maintenant que vous avez généré le point de départ fonctionnel de votre application WinUI multiplateforme, vous pouvez y copier le code du projet bureau.
Copier la vue
Comme Uno Platform vous permet d’utiliser la saveur XAML que vous connaissez déjà, vous pouvez copier le code que vous avez créé dans le tutoriel précédent.
Retourner dans la projet SimplePhotos du tutoriel précédent. Dans l’Explorateur de solutions, recherchez le fichier nommé MainWindow.xaml
et ouvrez-le. Remarquez que le contenu de la vue est défini dans un élément Window
et non dans Page
. En effet, le projet bureau est une application WinUI 3, qui peut utiliser des éléments Window
pour définir le contenu de la vue :
<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>
Avec Uno Platform, l’implémentation multiplateforme des contrôles présents dans l’élément Window
, notamment GridView
, Image
et RatingControl
, est l’assurance que la vue elle-même fonctionnera sur toutes les plateformes prises en charge sans trop d’efforts. Copiez le contenu de cet élément Window
et collez-le dans l’élément Page
du fichier MainPage.xaml
dans le projet Uno Platform UnoSimplePhotos. Le fichier XAML de la vue MainPage
doit se présenter comme suit :
<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>
Vous vous souvenez peut-être que la solution bureau comportait également un fichier MainWindow.xaml.cs
qui contenait du code-behind qui correspond à la vue. Dans le projet Uno Platform, le code-behind de la vue MainPage
dans laquelle nous avons effectué la copie est contenu dans le fichier MainPage.xaml.cs
.
Pour rendre ce code-behind multiplateforme, nous devons d’abord déplacer les éléments suivants dans le fichier MainPage.xaml.cs
:
La propriété
Images
: fournit àGridView
une collection observable de fichiers imageLe contenu du constructeur : appelle
GetItemsAsync()
pour remplir la collectionImages
d’éléments représentant des fichiers imageSupprime la modification manuelle de la propriété
ItemsSource
du contrôleImageGridView
La méthode
ImageGridView_ContainerContentChanging
: utilisée dans le cadre d’une stratégie visant à charger progressivement les élémentsGridView
à mesure qu’ils défilent dans la vueLa méthode
ShowImage
: charge les fichiers image dansGridView
La méthode
GetItemsAsync
: obtient les fichiers de ressources image à partir du dossierSamples
La méthode
LoadImageInfoAsync
: construit un objetImageFileInfo
à partir d’unStorageFile
créé
Après avoir tout déplacé, MainPage.xaml.cs
doit maintenant se présenter comme suit :
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;
}
}
Note
Les fichiers de votre projet d’application Uno doivent être utilisés UnoSimplePhotos
comme espace de noms.
Jusqu’à présent, les fichiers de la vue principale sur laquelle nous travaillons contiennent toutes les fonctionnalités de la solution bureau. Après avoir copié sur le fichier modèle ImageFileInfo.cs
, nous allons apprendre à modifier les blocs de code orientés bureau pour une compatibilité multiplateforme.
Copiez ImageFileInfo
à partir du projet bureau et collez-le dans le fichier ImageFileInfo.cs
. Effectuez les modifications suivantes :
Renommez l’espace de noms
UnoSimplePhotos
au lieu deSimplePhotos
:// Found towards the top of the file namespace UnoSimplePhotos;
Changez le type de paramètre de la méthode
OnPropertyChanged
en Nullable :// string -> string? protected void OnPropertyChanged([CallerMemberName] string? propertyName = null) ...
Rendez
PropertyChangedEventHandler
nullable :// PropertyChangedEventHandler -> PropertyChangedEventHandler? public event PropertyChangedEventHandler? PropertyChanged;
Au final, le fichier doit se présenter comme suit :
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));
}
Cette classe servira de modèle pour représenter les fichiers image dans GridView
. Même si, à ce stade, il est techniquement possible d’exécuter l’application, il se peut qu’elle ne puisse pas assurer un rendu correct des images ou en afficher les propriétés. Dans les prochaines sections, nous allons apporter un ensemble de modifications à ces fichiers copiés pour les rendre compatibles dans un contexte multiplateforme.
Utilisation de directives de préprocesseur
Dans le projet bureau du tutoriel précédent, le fichier MainPage.xaml.cs
contient une méthode GetItemsAsync
qui énumère les éléments d’un StorageFolder
représentant l’emplacement du package installé. Cet emplacement n’étant pas disponible sur certaines plateformes comme WebAssembly, nous devons apporter des modifications à cette méthode pour la rendre compatible avec toutes les plateformes. Nous allons donc apporter des modifications à la classe ImageFileInfo
afin de garantir la compatibilité.
Tout d’abord, apportez les modifications nécessaires à la méthode GetItemsAsync
. Remplacez la méthode GetItemsAsync
dans le fichier MainPage.xaml.cs
par le code suivant :
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));
}
}
Cette méthode utilise désormais une directive de préprocesseur pour déterminer le code à exécuter en fonction de la plateforme. Sur Windows, la méthode obtient le StorageFolder
représentant l’emplacement du package installé et retourne le dossier Samples
à partir de celui-ci. Sur les autres plateformes, la méthode compte jusqu’à 20, obtenant les fichiers image du dossier Samples
en utilisant un Uri
pour représenter le fichier image.
Ensuite, ajustez la méthode LoadImageInfoAsync
pour tenir compte des modifications que nous avons apportées à la méthode GetItemsAsync
. Remplacez la méthode LoadImageInfoAsync
dans le fichier MainPage.xaml.cs
par le code suivant :
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;
}
À l’instar de la méthode GetItemsAsync
, cette méthode utilise à présent une directive de préprocesseur pour déterminer le code à exécuter en fonction de la plateforme. Sur Windows, la méthode obtient ImageProperties
à partir de StorageFile
et s’en sert pour créer un objet ImageFileInfo
. Sur les autres plateformes, la méthode construit un objet ImageFileInfo
sans le paramètre ImageProperties
. Par la suite, des modifications seront apportées à la classe ImageFileInfo
pour prendre en compte cette modification.
Certains contrôles comme GridView
autorisent le chargement progressif du contenu du conteneur d’éléments mis à jour à mesure qu’ils défilent dans la fenêtre d’affichage. Pour cela, l’événement ContainerContentChanging
est utilisé. Dans le projet bureau du tutoriel précédent, la méthode ImageGridView_ContainerContentChanging
se sert de cet événement pour charger les fichiers image dans GridView
. Sachant que certains aspects de cet événement ne sont pas pris en charge sur toutes les plateformes, nous devons apporter des modifications à cette méthode pour la rendre compatible avec lesdites plateformes.
Par exemple, la propriété ContainerContentChangingEventArgs.Phase
n’est actuellement pas prise en charge sur les plateformes autres que Windows. Nous devons apporter des modifications à la méthode ImageGridView_ContainerContentChanging
pour prendre en compte cette modification. Remplacez la méthode ImageGridView_ContainerContentChanging
dans le fichier MainPage.xaml.cs
par le code suivant :
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
}
Le rappel spécialisé est désormais inscrit uniquement à l’aide de ContainerContentChangingEventArgs.RegisterUpdateCallback()
si la plateforme est Windows. Sinon, la méthode ShowImage
est appelée directement. Nous devons également apporter des modifications à la méthode ShowImage
pour l’accommoder à celles qui ont été apportées à la méthode ImageGridView_ContainerContentChanging
. Remplacez la méthode ShowImage
dans le fichier MainPage.xaml.cs
par le code suivant :
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
}
}
Là encore, les directives de préprocesseur garantissent que la propriété ContainerContentChangingEventArgs.Phase
est utilisée uniquement sur les plateformes qui la prennent en charge. Nous utilisons la méthode GetImageSourceAsync()
non utilisée précédemment pour charger les fichiers image dans GridView
sur les plateformes autres que Windows. À ce stade, nous allons prendre en compte les modifications apportées ci-dessus en modifiant la classe ImageFileInfo
.
Création d’un chemin de code distinct pour les autres plateformes
Mettez à jour ImageFileInfo.cs
pour inclure une nouvelle propriété appelée ImageSource
qui servira à charger le fichier image.
public BitmapImage? ImageSource { get; private set; }
Sachant que les plateformes comme le web ne prennent pas en charge les propriétés avancées des fichiers image qui sont facilement accessibles sur Windows, nous allons ajouter une surcharge de constructeur qui ne nécessite pas de paramètre typé ImageProperties
. Ajoutez la nouvelle surcharge après celle qui existe déjà en utilisant le code suivant :
public ImageFileInfo(StorageFile imageFile,
string name,
string type)
{
ImageName = name;
ImageFileType = type;
ImageFile = imageFile;
}
Cette surcharge de constructeur sert à construire un objet ImageFileInfo
sur les plateformes autres que Windows. La suite logique est de rendre la propriété ImageProperties
nullable. Mettez à jour la propriété ImageProperties
pour la rendre nullable en utilisant le code suivant :
public ImageProperties? ImageProperties { get; }
Mettez à jour la méthode GetImageSourceAsync
pour utiliser la propriété ImageSource
au lieu de retourner un objet BitmapImage
. Remplacez la méthode GetImageSourceAsync
dans le fichier ImageFileInfo.cs
par le code suivant :
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;
}
Pour éviter d’obtenir la valeur de ImageProperties
quand elle est null, apportez les modifications suivantes :
Modifiez la propriété
ImageDimensions
pour utiliser l’opérateur conditionnel Null :public string ImageDimensions => $"{ImageProperties?.Width} x {ImageProperties?.Height}";
Rectifiez la propriété
ImageTitle
pour utiliser l’opérateur conditionnel Null :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(); } } } }
Rectifiez
ImageRating
pour ne pas utiliserImageProperties
en générant une évaluation (étoiles) aléatoire à des fins de démonstration :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(); } } } }
Mettez à jour le constructeur pour l’empêcher de générer un entier aléatoire :
public ImageFileInfo(ImageProperties properties, StorageFile imageFile, string name, string type) { ImageProperties = properties; ImageName = name; ImageFileType = type; ImageFile = imageFile; }
À la suite de ces modifications, la classe ImageFileInfo
devrait contenir le code suivant. Il dispose désormais d’un chemin de code distinct pour les plateformes autres que 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));
}
Cette classe ImageFileInfo
sert à représenter les fichiers image dans GridView
. Enfin, nous allons apporter des modifications au fichier MainPage.xaml
pour prendre en compte les modifications apportées au modèle.
Utilisation du balisage XAML propre à la plateforme
Le balisage de la vue contient quelques éléments qui ne doivent être évalués que sur Windows. Ajoutez un nouvel espace de noms dans l’élément Page
du MainPage.xaml
fichier comme ceci :
...
xmlns:win="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
À présent, dans MainPage.xaml
, remplacez la méthode setter de la propriété ItemsPanel
dans l’élément GridView
par le code suivant :
win:ItemsPanel="{StaticResource ImageGridView_ItemsPanelTemplate}"
Le fait de faire précéder le nom de propriété de win:
est l’assurance que la propriété n’est définie que sur Windows. Faites la même chose dans la ressource ImageGridView_ItemTemplate
. L’objectif est ici de charger uniquement les éléments qui utilisent la propriété ImageDimensions
sur Windows. Remplacez l’élément TextBlock
qui utilise la propriété ImageDimensions
par le code suivant :
<win:TextBlock Text="{x:Bind ImageDimensions}"
HorizontalAlignment="Center"
Style="{StaticResource CaptionTextBlockStyle}"
Margin="8,0,0,0" />
Le fichier MainPage.xaml
doit maintenant se présenter comme ceci :
<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>
Exécution de l’application
Lancez la cible UnoSimplePhotos.Windows
. Notez que cette application WinUI ressemble beaucoup à celle du tutoriel précédent.
Vous pouvez maintenant créer et exécuter votre application sur n’importe quelle plateforme prise en charge. Pour ce faire, vous pouvez utiliser la liste déroulante de la barre d’outils de débogage pour sélectionner la plateforme cible du déploiement :
Pour exécuter l’en-tête WebAssembly (Wasm) :
- Cliquez avec le bouton droit sur le projet
UnoSimplePhotos.Wasm
, sélectionnez Définir comme projet de démarrage - Appuyez sur le bouton
UnoSimplePhotos.Wasm
pour déployer l’application - Si vous le souhaitez, vous pouvez ajouter et utiliser le projet
UnoSimplePhotos.Server
en guise d’alternative
- Cliquez avec le bouton droit sur le projet
Pour déboguer pour iOS :
Cliquez avec le bouton droit sur le projet
UnoSimplePhotos.Mobile
, puis sélectionnez Définir comme projet de démarrageDans la liste déroulante de la barre d’outils de débogage, sélectionnez un appareil iOS actif ou le simulateur. Vous devez être associé à un Mac pour que cela fonctionne.
Pour déboguer pour Mac Catalyst :
- Cliquez avec le bouton droit sur le projet
UnoSimplePhotos.Mobile
, puis sélectionnez Définir comme projet de démarrage - Dans la liste déroulante de la barre d’outils de débogage, sélectionnez un appareil macOS distant. Vous devez être associé à un appareil de ce type pour que cela fonctionne.
- Cliquez avec le bouton droit sur le projet
Pour déboguer la plateforme Android :
- Cliquez avec le bouton droit sur le projet
UnoSimplePhotos.Mobile
, puis sélectionnez Définir comme projet de démarrage - Dans la liste déroulante de la barre d’outils de débogage, sélectionnez un appareil Android actif ou l’émulateur
- Sélectionnez un appareil actif dans le sous-menu « Appareil »
- Cliquez avec le bouton droit sur le projet
Pour déboguer sur Linux avec Skia GTK :
- Cliquez avec le bouton droit sur le projet
UnoSimplePhotos.Skia.Gtk
, puis sélectionnez Définir comme projet de démarrage - Appuyez sur le bouton
UnoSimplePhotos.Skia.Gtk
pour déployer l’application
- Cliquez avec le bouton droit sur le projet
Voir aussi
Windows developer