Tutorial: Criar um visualizador de fotos simples direcionado a várias plataformas
Depois de criar um aplicativo WinUI 3 visualizador de fotos simples inicial, você vai querer alcançar mais usuários sem precisar reescrever o aplicativo. Este tutorial usa a Uno Platform para expandir o alcance do aplicativo WinUI 3 C# existente, permitindo a reutilização da lógica de negócios e da camada de interface do usuário em dispositivos nativos móveis, da Web e da área de trabalho. Com apenas alterações mínimas no aplicativo visualizador de fotos simples, podemos executar uma cópia perfeita de pixels do aplicativo portado para essas plataformas.
Pré-requisitos
Configurar seu computador de desenvolvimento (consulte Introdução ao WinUI)
Carga de trabalho de desenvolvimento para Web e ASP.NET (de desenvolvimento para WebAssembly)
Desenvolvimento do .NET Multi-Platform App UI instalado (para desenvolvimento iOS, Android e Mac Catalyst).
Desenvolvimento para área de trabalho do .NET instalado (de desenvolvimento para Gtk, Wpf e Linux Framebuffer)
Finalizar o ambiente
Abra um prompt de linha de comando, o Terminal do Windows se estiver instalado, o Prompt de Comando ou o Windows Powershell no menu Iniciar.
Instale ou atualize a ferramenta
uno-check
:Use o seguinte comando:
dotnet tool install -g uno.check
Para atualizar a ferramenta, se você já tiver instalado uma versão mais antiga:
dotnet tool update -g uno.check
Execute a ferramenta com o seguinte comando:
uno-check
Siga as instruções indicadas pela ferramenta. Como ele precisa modificar o sistema, podem ser solicitadas permissões elevadas.
Instalar os modelos de solução da Uno Platform
Inicie o Visual Studio e clique em Continue without code
. Clique em Extensions
–>Manage Extensions
na barra de menus.
No Gerenciador de Extensões, expanda o nó Online e procure Uno
, instale a extensão Uno Platform
ou baixe-a e instale-a no Visual Studio Marketplace e reinicie o Visual Studio.
Criar um aplicativo
Agora que estamos prontos para criar um aplicativo multiplataforma, a abordagem será criar um aplicativo da Uno Platform. Copiaremos o código do projeto SimplePhotos WinUI 3 do tutorial anterior para o projeto multiplataforma. Isso é possível porque a Uno Platform permite reutilizar a base de código existente. Ao longo do tempo, você pode colocar em funcionamento recursos que dependem de APIs do sistema operacional fornecidas pela plataforma. Essa abordagem é muito útil quando você tem um aplicativo existente que deseja portar para outras plataformas.
Em breve, você poderá colher os benefícios dessa abordagem, pois poderá direcionar um tipo XAML familiar e a base de código que você já tem a mais plataformas.
Abra o Visual Studio e crie um projeto por meio de File
>New
>Project
:
Pesquise Uno e selecione o modelo de projeto de aplicativo da Uno Platform:
Crie uma solução em C# usando o tipo aplicativo da Uno Platform na Página Inicial do Visual Studio. Para evitar conflitos com o código do tutorial anterior, daremos a essa solução um nome diferente: "UnoSimplePhotos". Especifique o nome do projeto, o nome da solução e o diretório. Neste exemplo, o projeto multiplataforma UnoSimplePhotos
pertence a uma solução UnoSimplePhotos
, que residirá em C:\Projects:
Agora você escolherá um modelo base para o aplicativo de galeria de fotos simples multiplataforma.
O modelo aplicativo da Uno Platform vem com duas opções predefinidas que permitem começar a usar rapidamente uma solução Em branco ou a configuração Padrão que inclui referências às bibliotecas Uno.Material e Uno.Toolkit. A configuração padrão também inclui Uno.Extensions, que é usado para injeção de dependência, configuração, navegação e registro em log. Além disso, ela usa MVUX em vez de MVVM e, portanto, é um ótimo ponto de partida para a criação rápida de aplicativos do mundo real.
Para simplificar, selecione a predefinição Em branco. Em seguida, clique no botão Criar. Aguarde até que os projetos sejam criados e as dependências sejam restauradas.
Um banner na parte superior do editor pode solicitar o recarregamento dos projetos, clique em Recarregar projetos:
Você verá a seguinte estrutura de arquivo padrão no Gerenciador de Soluções:
Adicionar ativos de imagem ao projeto
O aplicativo precisará de algumas imagens para exibição. Você pode usar as mesmas imagens do tutorial anterior.
No projeto UnoSimplePhotos
, crie uma pasta chamada Assets
e copie os arquivos de imagem JPG em uma subpasta Samples
. A estrutura de pasta Assets
ficará assim:
Para obter mais informações sobre como criar a pasta Assets
e adicionar imagens a ela, confira a documentação da Uno Platform sobre Ativos e exibição de imagem.
Preparar o aplicativo
Agora que você gerou o ponto de partida funcional do aplicativo WinUI multiplataforma, copie nele o código do projeto de área de trabalho.
Copiar a exibição
Como a Uno Platform permite que você use o tipo XAML com o qual já está familiarizado, copie o mesmo código sobre o que você criou no tutorial anterior.
Retorne ao projeto SimplePhotos do tutorial anterior. No Gerenciador de Soluções, localize o arquivo chamado MainWindow.xaml
e abra-o. Observe que o conteúdo da exibição é definido dentro de um elemento Window
, não em um Page
. Isso ocorre porque o projeto de área de trabalho é um aplicativo WinUI 3, que pode usar elementos Window
para definir o conteúdo da exibição:
<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>
A implementação multiplataforma da Uno Platform dos controles encontrados no elemento Window
, como GridView
, Image
e RatingControl
, garante que a exibição em funcionará em todas as plataformas com suporte exigindo poucas ações. Copie o conteúdo deste Window
e cole-o no elemento Page
do arquivo MainPage.xaml
no projeto UnoSimplePhotos da Uno Platform. A exibição XAML MainPage
é assim:
<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>
A solução da área de trabalho também tinha um arquivo MainWindow.xaml.cs
que continha um code-behind correspondente à exibição. No projeto da Uno Platform, o code-behind da exibição MainPage
de destino da cópia está contido no arquivo MainPage.xaml.cs
.
Para ativar esse code-behind multiplataforma, primeiro devemos colocar o seguinte no arquivo MainPage.xaml.cs
:
Propriedade
Images
: fornece oGridView
com uma coleção observável de arquivos de imagemConteúdo do construtor: chama
GetItemsAsync()
para preencher a coleçãoImages
com itens que representam arquivos de imagemRemover a modificação manual da propriedade
ItemsSource
do controleImageGridView
Método
ImageGridView_ContainerContentChanging
: usado em uma estratégia para carregar os itensGridView
progressivamente conforme eles entram na exibiçãoMétodo
ShowImage
: carrega os arquivos de imagem noGridView
Método
GetItemsAsync
: obtém os arquivos de ativo de imagem da pastaSamples
Método
LoadImageInfoAsync
: constrói um objetoImageFileInfo
por meio de umStorageFile
criado
Depois que tudo for colocado no MainPage.xaml.cs
, ele ficará assim:
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;
}
}
Observação
Os arquivos no projeto de aplicativo Uno devem usar UnoSimplePhotos
como o namespace.
Até agora, os arquivos da exibição principal com a qual estamos trabalhando contêm todas as funcionalidades da solução de área de trabalho. Depois de copiar o arquivo de modelo ImageFileInfo.cs
, aprenderemos a modificar os blocos de código direcionados à área de trabalho para garantir a compatibilidade multiplataforma.
Copie ImageFileInfo
do projeto de área de trabalho e cole-o no arquivo ImageFileInfo.cs
. Faça as seguintes alterações:
Renomeie o namespace como
UnoSimplePhotos
, em vez deSimplePhotos
:// Found towards the top of the file namespace UnoSimplePhotos;
Altere o tipo de parâmetro do método
OnPropertyChanged
para anulável:// string -> string? protected void OnPropertyChanged([CallerMemberName] string? propertyName = null) ...
Altere
PropertyChangedEventHandler
para anulável:// PropertyChangedEventHandler -> PropertyChangedEventHandler? public event PropertyChangedEventHandler? PropertyChanged;
Em conjunto, o arquivo deve ficar assim:
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));
}
Essa classe funcionará como um modelo para representar os arquivos de imagem no GridView
. Embora seja tecnicamente possível executar o aplicativo neste ponto, ele pode não renderizar as imagens ou não exibir as respectivas propriedades corretamente. Nas próximas seções, faremos um conjunto de alterações nesses arquivos copiados para garantir a compatibilidade multiplataforma.
Usar diretivas de pré-processador
No projeto de área de trabalho do tutorial anterior, o arquivo MainPage.xaml.cs
continha um método GetItemsAsync
que enumera itens de um StorageFolder
que representa o local do pacote instalado. Como esse local não está disponível em determinadas plataformas, como WebAssembly, precisaremos fazer alterações nesse método para que ele fique compatível com todas as plataformas. Faremos algumas alterações na classe ImageFileInfo
para garantir a compatibilidade.
Primeiro, faça as alterações necessárias no método GetItemsAsync
. Substitua o método GetItemsAsync
no arquivo MainPage.xaml.cs
pelo seguinte código:
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));
}
}
Esse método agora usa uma diretiva de pré-processador para determinar qual código executar com base na plataforma. No Windows, o método obtém o StorageFolder
que representa o local do pacote instalado e retorna a pasta Samples
dele. Em outras plataformas, o método conta até 20, obtendo os arquivos de imagem da pasta Samples
usando um Uri
para representar o arquivo de imagem.
Em seguida, ajuste o método LoadImageInfoAsync
para incluir as alterações feitas no método GetItemsAsync
. Substitua o método LoadImageInfoAsync
no arquivo MainPage.xaml.cs
pelo seguinte código:
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;
}
Como no método GetItemsAsync
, esse método agora usa uma diretiva de pré-processador para determinar qual código será executado com base na plataforma. No Windows, o método obtém o ImageProperties
do StorageFile
e o usa para criar um objeto ImageFileInfo
. Em outras plataformas, o método constrói um objeto ImageFileInfo
sem o parâmetro ImageProperties
. Mais tarde, serão feitas modificações na classe ImageFileInfo
para incluir essa alteração.
Controles como GridView
permitem o carregamento progressivo do conteúdo do contêiner de item atualizado à medida que ele chega no visor. Isso é feito usando o evento ContainerContentChanging
. No projeto de área de trabalho do tutorial anterior, o método ImageGridView_ContainerContentChanging
usa esse evento para carregar os arquivos de imagem no GridView
. Como determinados aspectos desse evento não são compatíveis com todas as plataformas, precisaremos fazer alterações nesse método para torná-lo compatível.
Por exemplo, no momento, a propriedade ContainerContentChangingEventArgs.Phase
não é compatível com plataformas que não sejam Windows. Precisaremos fazer alterações no método ImageGridView_ContainerContentChanging
para incluir essa alteração. Substitua o método ImageGridView_ContainerContentChanging
no arquivo MainPage.xaml.cs
pelo seguinte código:
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
}
O retorno de chamada especializado agora só será registrado usando ContainerContentChangingEventArgs.RegisterUpdateCallback()
se a plataforma for Windows. Caso contrário, o método ShowImage
será chamado diretamente. Também precisaremos fazer alterações no método ShowImage
para trabalhar junto com as alterações feitas no método ImageGridView_ContainerContentChanging
. Substitua o método ShowImage
no arquivo MainPage.xaml.cs
pelo seguinte código:
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
}
}
Novamente, as diretivas de pré-processador garantem que a propriedade ContainerContentChangingEventArgs.Phase
seja usada apenas em plataformas com as quais seja compatível. Usamos o método GetImageSourceAsync()
que não foi usado antes para carregar os arquivos de imagem em GridView
em plataformas que não sejam Windows. Neste ponto, incluiremos as alterações feitas acima editando a classe ImageFileInfo
.
Criar um caminho de código separado para outras plataformas
Atualize ImageFileInfo.cs
para incluir uma nova propriedade chamada ImageSource
que será usada para carregar o arquivo de imagem.
public BitmapImage? ImageSource { get; private set; }
Como plataformas como a Web não são compatíveis com propriedades avançadas de arquivo de imagem prontamente disponíveis no Windows, adicionaremos uma sobrecarga de construtor que não requer um parâmetro tipado ImageProperties
. Adicione a nova sobrecarga após a existente usando o seguinte código:
public ImageFileInfo(StorageFile imageFile,
string name,
string type)
{
ImageName = name;
ImageFileType = type;
ImageFile = imageFile;
}
Essa sobrecarga de construtor é usada para construir um objeto ImageFileInfo
em plataformas que não sejam Windows. Como isso foi feito, é melhor alterar a propriedade ImageProperties
para anulável. Atualize a propriedade ImageProperties
para anulável usando o seguinte código:
public ImageProperties? ImageProperties { get; }
Atualize o método GetImageSourceAsync
para usar a propriedade ImageSource
em vez de retornar apenas um objeto BitmapImage
. Substitua o método GetImageSourceAsync
no arquivo ImageFileInfo.cs
pelo seguinte código:
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;
}
Para evitar obter o valor de ImageProperties
quando ele é nulo, faça as seguintes alterações:
Modifique a propriedade
ImageDimensions
para usar o operador condicional nulo:public string ImageDimensions => $"{ImageProperties?.Width} x {ImageProperties?.Height}";
Altere a propriedade
ImageTitle
para usar o operador condicional nulo: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(); } } } }
Altere
ImageRating
para não depender deImageProperties
gerando uma classificação por estrelas aleatória para fins de demonstração: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(); } } } }
Atualize o construtor que gera um inteiro aleatório para não fazer mais isso:
public ImageFileInfo(ImageProperties properties, StorageFile imageFile, string name, string type) { ImageProperties = properties; ImageName = name; ImageFileType = type; ImageFile = imageFile; }
Com essas edições, a classe ImageFileInfo
deve conter o código a seguir. Agora há um novo caminho de código separado para plataformas que não sejam 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));
}
Essa classe ImageFileInfo
é usada para representar os arquivos de imagem no GridView
. Por fim, faremos alterações no arquivo MainPage.xaml
para incluir as alterações no modelo.
Usar a marcação XAML específica da plataforma
Há alguns itens na marcação de exibição que só devem ser avaliados no Windows. Adicione um novo namespace ao elemento Page
do arquivo MainPage.xaml
da seguinte maneira:
...
xmlns:win="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
Agora, em MainPage.xaml
, substitua o setter de propriedade ItemsPanel
no elemento GridView
pelo seguinte código:
win:ItemsPanel="{StaticResource ImageGridView_ItemsPanelTemplate}"
Quando win:
é acrescentado ao nome da propriedade, ela é definida apenas no Windows. Faça isso novamente no recurso ImageGridView_ItemTemplate
. Queremos carregar apenas elementos que usam a propriedade ImageDimensions
no Windows. Substitua o elemento TextBlock
que usa a propriedade ImageDimensions
pelo seguinte código:
<win:TextBlock Text="{x:Bind ImageDimensions}"
HorizontalAlignment="Center"
Style="{StaticResource CaptionTextBlockStyle}"
Margin="8,0,0,0" />
O arquivo MainPage.xaml
deve estar assim agora:
<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>
Executar o aplicativo
Inicie o destino UnoSimplePhotos.Windows
. Observe que esse aplicativo WinUI é muito semelhante ao do tutorial anterior.
Agora você pode criar e executar o aplicativo em qualquer plataforma com suporte. Para fazer isso, você pode usar a lista suspensa da barra de ferramentas de depuração para selecionar uma plataforma de destino de implantação:
Para executar o cabeçalho WebAssembly (Wasm):
- Clique com o botão direito do mouse no projeto
UnoSimplePhotos.Wasm
, selecione Definir como projeto de inicialização - Pressione o botão
UnoSimplePhotos.Wasm
para implantar o aplicativo - Se desejar, adicione e use o projeto
UnoSimplePhotos.Server
como alternativa
- Clique com o botão direito do mouse no projeto
Como fazer a depuração para iOS:
Clique com o botão direito do mouse no projeto
UnoSimplePhotos.Mobile
e selecione Definir como projeto de inicializaçãoNa lista suspensa da barra de ferramentas de depuração, selecione um dispositivo iOS ativo ou o simulador. Você precisará fazer o emparelhamento com um Mac para que isso funcione.
Como depurar para Mac Catalyst:
- Clique com o botão direito do mouse no projeto
UnoSimplePhotos.Mobile
e selecione Definir como projeto de inicialização - Na lista suspensa da barra da ferramentas de depuração, selecione um dispositivo macOS remoto. Você precisará estar emparelhado com um deles para que isso funcione.
- Clique com o botão direito do mouse no projeto
Como depurar a plataforma Android:
- Clique com o botão direito do mouse no projeto
UnoSimplePhotos.Mobile
e selecione Definir como projeto de inicialização - Na lista suspensa da barra de ferramentas de depuração, selecione um dispositivo Android ativo ou o emulador
- Selecione um dispositivo ativo no submenu "Dispositivo"
- Clique com o botão direito do mouse no projeto
Como depurar no Linux com Skia GTK:
- Clique com o botão direito do mouse no projeto
UnoSimplePhotos.Skia.Gtk
e selecione Definir como projeto de inicialização - Pressione o botão
UnoSimplePhotos.Skia.Gtk
para implantar o aplicativo
- Clique com o botão direito do mouse no projeto
Confira também
Windows developer