Compartilhar via


Tutorial: criar um visualizador de fotos simples com o WinUI 3

Observação

Para obter informações sobre os benefícios do WinUI 3, bem como outras opções de tipo de aplicativo, confira Visão geral das opções de desenvolvimento de aplicativos.

Neste tópico, vamos percorrer o processo de criação de um projeto WinUI 3 no Visual Studio e então criar um aplicativo simples para exibir fotos. Usaremos controles, painéis de layout e associação de dados. E escreveremos a marcação XAML (que é declarativa) e sua escolha de código C# ou C++ (que são, respectivamente, imperativo e processual). Use o seletor de linguagens de programação acima do título do tópico para escolher C# ou C++/WinRT.

Dica

O código-fonte neste tópico é fornecido em C# e C++/WinRT. Se você for um desenvolvedor do C++, para obter mais detalhes e conceitos explicando como o código que mostramos aqui funciona, consulte a documentação do C++/WinRT. Os tópicos relevantes incluem controles XAML; associar a uma propriedade C++/WinRT, controles de itens XAML; associar a uma coleção C++/WinRT e aplicativo de exemplo C++/WinRT do Editor de Fotos.

Etapa 1: Instalar ferramentas para o SDK do Aplicativo Windows

Para configurar o ambiente de desenvolvimento, confira Instalar ferramentas para o SDK do Aplicativo Windows. Você pode, opcionalmente, acompanhar Criar seu primeiro projeto WinUI 3.

Importante

Você encontrará tópicos de notas de versão com o tópico Canais de versão do SDK do Aplicativo Windows. Há notas de versão para cada canal. Verifique as limitações e os problemas conhecidos nas notas de versão, pois eles podem afetar os resultados de acompanhar este tutorial e/ou executar o aplicativo que vamos compilar.

Etapa 2: Criar um projeto

No Visual Studio, crie um projeto em C# ou C++, conforme sua escolha, com base no modelo de projeto Aplicativo em Branco, Empacotado (WinUI 3 na Área de Trabalho). Dê ao projeto o nome SimplePhotos e (para que sua estrutura de pastas corresponda à descrita neste tutorial) desmarque Colocar a solução e o projeto no mesmo diretório. Você pode ter como alvo a versão mais recente (não a versão prévia) do sistema operacional cliente.

Etapa 3: Copiar arquivos de ativo

O aplicativo que vamos criar carrega arquivos de imagem com ele na forma de arquivos de ativos, que são as fotos que ele exibe. Nesta seção, você vai adicionar a ferramenta Gerenciador de Segredos ao projeto. Porém, primeiro você precisará obter uma cópia dos arquivos.

  1. Portanto, clone (ou baixe como um .zip) o repositório de exemplos do SDK do Aplicativo Windows (confira WindowsAppSDK-Samples). Depois de fazer isso, você encontrará os arquivos de ativos que usaremos na pasta \WindowsAppSDK-Samples\Samples\PhotoEditor\cs-winui\Assets\Samples (use essa pasta para um projeto C# e C++/WinRT). Se você quiser conferir estes arquivos no repositório online, acesse WindowsAppSDK-Samples/Samples/PhotoEditor/cs-winui/Assets/Samples/.

  2. No Explorador de Arquivos, selecione a pasta Exemplos e copie-a para a área de transferência.

  1. Acesse Gerenciador de Soluções no Visual Studio. Clique com o botão direito do mouse na pasta Ativos (é um filho do nó do projeto) e clique em Abrir Pasta no Explorador de Arquivos. Isso abre a pasta Ativos no Explorador de Arquivos.

  2. Cole (na pasta Ativos) a pasta Exemplos que você acabou de copiar.

Etapa 4: Adicionar um controle de GridView

Nosso aplicativo precisa exibir linhas e colunas de fotos. Em outras palavras, uma grade de imagens. Para uma interface do usuário como essa, os controles principais a serem usados são Exibição de lista e exibição de grade.

  1. Abra o MainWindow.xaml. Atualmente, há um elemento Window e, dentro dele, um painel de layout do StackPanel. Dentro do StackPanel, há um controle de Botão, que é conectado a um método de manipulador de eventos.

    A janela principal de qualquer aplicativo representa a exibição que você vê primeiro quando executa o aplicativo. No aplicativo que vamos criar, o trabalho da janela principal é carregar as fotos da pasta Exemplos e mostrar uma exibição em bloco dessas imagens com várias informações sobre elas.

  2. Substitua a marcação StackPanel e Button pelo painel de layout de Grade e o controle GridView mostrado na listagem abaixo.

    <Window ...>
        <Grid>
            <GridView x:Name="ImageGridView"/>
        </Grid>
    </Window>
    

    Dica

    O x:Name identifica um elemento XAML para que você possa consultá-lo em outro lugar no XAML e no code-behind.

  3. C#. Abra MainWindow.xaml.cs e exclua o método myButton_Click.

  4. C++/WinRT. Abra MainWindow.xaml.h e MainWindow.xaml.cpp e exclua o método myButton_Click.

Você pode compilar e executar agora, mas a janela estará vazia nesta fase. Para que o controle GridView mostre tudo, você precisa atribuir a ele uma coleção de dados a ser exibida. Vamos começar com isso a seguir.

Para obter informações em segundo plano sobre alguns dos tipos que acabamos de mencionar, confira Painéis de layout e Controles para aplicativos do Windows.

Etapa 5: o modelo ImageFileInfo

Um modelo (no sentido de modelos, exibições e modelos de exibição) é uma classe que, até certo ponto, representa um objeto ou conceito do mundo real (como uma conta bancária). É uma abstração dessa coisa do mundo real. Nesta seção, vamos adicionar ao projeto uma nova classe chamada ImageFileInfo. ImageFileInfo será um modelo de um arquivo de imagem, como uma foto. Esta seção nos deixará um passo mais perto de exibir fotos na interface do usuário do aplicativo.

Dica

Em preparação para o exemplo de código abaixo, vamos apresentar o termo observável. Uma propriedade que pode ser associada dinamicamente a um controle XAML (de modo que a interface do usuário seja atualizada sempre que o valor da propriedade mudar) é conhecida como uma propriedade observável. Essa ideia baseia-se no padrão de design de software conhecido como o padrão do observador. No aplicativo que vamos criar neste tutorial, as propriedades do nosso modelo ImageFileInfo não serão alteradas. Porém, mesmo assim, vamos mostrar como tornar ImageFileInfo observável, fazendo com que implemente a interface INotifyPropertyChanged.

  1. Clique com o botão direito do mouse no nó do projeto (SimplePhotos) e clique em Adicionar>Novo Item.... Em Código de>Itens do C#, selecione Classe. Defina o nome como ImageFileInfo.cs e clique em Adicionar.

  2. Substitua o conteúdo de ImageFileInfo.cs pela listagem de códigos a seguir.

    using Microsoft.UI.Xaml.Media.Imaging;
    using System;
    using System.ComponentModel;
    using System.Runtime.CompilerServices;
    using System.Threading.Tasks;
    using Windows.Storage;
    using Windows.Storage.FileProperties;
    using Windows.Storage.Streams;
    
    namespace SimplePhotos
    {
        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));
        }
    }
    
  3. Salve e feche o arquivo ImageFileInfo.cs.

Etapa 6: Definir e preencher uma propriedade para uma coleção de imagens

Nesta seção, vamos adicionar uma propriedade à classe MainWindow. A propriedade (chamada Images) será uma classe de coleção contendo as imagens que queremos exibir.

  1. Defina a propriedade como essa em MainWindow.xaml.cs:

    ...
    using System.Collections.ObjectModel;
    ...
    namespace SimplePhotos
    {
        public sealed partial class MainWindow : Window
        {
            public ObservableCollection<ImageFileInfo> Images { get; } = 
                new ObservableCollection<ImageFileInfo>();
            ...
        }
    }
    
  2. O código para preencher a nova propriedade de coleção com imagens é mostrado nos métodos GetItemsAsync e LoadImageInfoAsync abaixo. Cole as diretivas using e as duas implementações de método também MainWindow.xaml.cs. Esses métodos são membros da classe MainWindow, então cole-os lá dentro, assim como você fez com a propriedade Imagens acima.

    ...
    using System.Threading.Tasks;
    using Windows.ApplicationModel;
    using Windows.Storage;
    using Windows.Storage.Search;
    ...
    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));
        }
    
        ImageGridView.ItemsSource = Images;
    }
    
    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;
    }
    
  3. A última coisa que precisamos fazer nesta seção é atualizar o construtor de MainWindow para chamar GetItemsAsync.

    public MainWindow()
    {
        ...
        GetItemsAsync();
    }
    

Você pode compilar e executar agora, se quiser (para confirmar que seguiu corretamente as etapas), mas não há muito o que ver na janela nesta fase. Isso porque o que fizemos até agora é pedir para a GridView renderizar uma coleção de objetos do tipo ImageFileInfo; e a GridView ainda não sabe como fazer isso.

Lembre-se de que a propriedade Images é uma coleção observável de objetos ImageFileInfo. E a última linha de GetItemsAsync informa à GridView (que se chama ImageGridView) que a origem de seus itens (ItemsSource) é a propriedade Images. O trabalho da GridView é exibir esses itens.

Contudo, ainda não contamos nada à GridView sobre a classe ImageFileInfo. Assim, o melhor que ela pode fazer por enquanto é exibir o valor ToString de cada objeto ImageFileInfo na coleção. E, por padrão, é apenas o nome do tipo. Na próxima seção, vamos criar um modelo de dados para definir como queremos que um objeto ImageFileInfo seja exibido.

Dica

Usei o termo coleção observável acima. No aplicativo que criamos neste tutorial, o número de imagens não muda (e, como dissemos, os valores das propriedades de cada imagem não mudam também). Porém, ainda é conveniente, e uma boa prática, usar a associação de dados para conectar inicialmente a interface do usuário aos dados. Então é isso que vamos fazer.

Etapa 7: Adicionar um modelo de dados

Para começar, vamos usar um modelo de dados de espaço reservado semelhante a um esboço. Isso servirá até terminarmos de explorar algumas opções de layout. Depois disso, podemos atualizar o modelo de dados para mostrar as fotos reais.

Dica

Isso é realmente uma coisa muito prática de se fazer. Descobriu-se que, se uma interface do usuário parece um esboço (em outras palavras, baixa fidelidade), as pessoas estão mais dispostas a sugerir e/ou testar ideias rápidas com ela, às vezes envolvendo mudanças bastante substanciais. Isso porque supusemos (corretamente) que teria um baixo custo tentar essas alterações.

Por outro lado, quanto mais finalizada for aparência de uma interface do usuário (maior for a sua fidelidade), mais suporemos (outra vez, corretamente) que foi dedicado muito trabalho à aparência atual. E isso nos torna menos propensos a sugerir, ou experimentar, novas ideias.

  1. Abra MainWindow.xaml e altere o conteúdo da Janela para que eles se pareçam com esta marcação:

    <Window ...>
        <Grid>
            <Grid.Resources>
                <DataTemplate x:Key="ImageGridView_ItemTemplate">
                    <Grid/>
                </DataTemplate>
            </Grid.Resources>
            <GridView x:Name="ImageGridView"
                    ItemTemplate="{StaticResource ImageGridView_ItemTemplate}">
            </GridView>
        </Grid>
    </Window>
    

    Para a raiz do layout, vamos adicionar um recurso DataTemplate simples e fornecer a ele uma chave de ImageGridView_ItemTemplate. E usamos essa mesma chave para definir o ItemTemplate da GridView. Controles de itens, como GridView, têm uma propriedade ItemTemplate (assim como eles têm a propriedade ItemsSource que vimos anteriormente). Um modelo de item é um modelo de dados; ele é usado para exibir cada item na coleção.

    Para obter mais informações, confira Contêineres e modelos de item.

  2. Agora, podemos fazer algumas passagens de edição sobre o modelo de dados, adicionando e editando os elementos dentro dele para torná-lo mais interessante e útil. Vamos atribuir à Grade raiz um altura e uma largura de 300 e uma margem de 8. Então vamos adicionar duas definições de linhas e definir a altura da segunda definição de linha como Automática.

    <DataTemplate x:Key="ImageGridView_ItemTemplate">
        <Grid Height="300"
              Width="300"
              Margin="8">
            <Grid.RowDefinitions>
                <RowDefinition />
                <RowDefinition Height="Auto" />
            </Grid.RowDefinitions>
        </Grid>
    </DataTemplate>
    

    Para obter mais informações, confira Alinhamento, margem e preenchimento.

  3. Queremos que o modelo de dados exiba a imagem, o nome, o tipo de arquivo, as dimensões e a classificação de cada foto. Portanto, vamos adicionar, respectivamente, um controle image, alguns controles TextBlock e um controle RatingControl. Vamos definir o texto dentro dos painéis de layout do StackPanel. A Imagem exibirá, inicialmente, o logotipo Microsoft Store do projeto como um espaço reservado.

  4. Depois de todas essas edições, veja a aparência do modelo de dados:

    <DataTemplate x:Key="ImageGridView_ItemTemplate">
        <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="ImageTitle"
                           HorizontalAlignment="Center"
                           Style="{StaticResource SubtitleTextBlockStyle}" />
                <StackPanel Orientation="Horizontal"
                            HorizontalAlignment="Center">
                    <TextBlock Text="ImageFileType"
                               HorizontalAlignment="Center"
                               Style="{StaticResource CaptionTextBlockStyle}" />
                    <TextBlock Text="ImageDimensions"
                               HorizontalAlignment="Center"
                               Style="{StaticResource CaptionTextBlockStyle}"
                               Margin="8,0,0,0" />
                </StackPanel>
    
                <RatingControl Value="3" IsReadOnly="True"/>
            </StackPanel>
        </Grid>
    </DataTemplate>
    

Compile o projeto agora e execute o aplicativo para ver o controle GridView com o modelo de item que você acabou de criar. Em seguida, vamos dar uma olhada em como os itens são dispostos. Vamos alterar alguns pincéis e adicionaremos espaço entre os itens.

The placeholder item template.

Etapa 8: Editar o estilo do contêiner de item

Outro conceito relacionado a controles de itens, como GridView, é contêiner de itens. Um contêiner de itens é um controle de conteúdo que exibe um item como o valor de sua propriedade Content. Um controle de itens cria quantos contêineres de item precisar para exibir os itens visíveis na tela a qualquer momento.

Sendo um controle, um contêiner de item tem um estilo e um modelo de controle. Seu modelo de estilo e controle determina a aparência do contêiner de item em seus vários estados (como seleção, ponteiro e foco). Como vimos, o modelo de item (que é um modelo de dados) determina a aparência do item em si.

Para GridView, o tipo de contêineres de item é GridViewItem.

Portanto, nesta seção, vamos nos concentrar em projetar o estilo do contêiner de item. Para isso, vamos criar um recurso Style para GridViewItem e, em seguida, defini-lo como o ItemContainerStyle da *GridView. No estilo, vamos definir as propriedades Background e Margin do contêiner de item para dar a ele uma tela de fundo cinza e um pouco de margem ao redor.

  1. Em MainWindow.xaml, adicione um recurso Style ao mesmo elemento XML Grid.Resources em que colocamos o modelo de dados.

    <Grid>
        <Grid.Resources>
        ...
            <Style x:Key="ImageGridView_ItemContainerStyle"
                TargetType="GridViewItem">
                <Setter Property="Background" Value="Gray"/>
                <Setter Property="Margin" Value="8"/>
            </Style>
        </Grid.Resources>
    
  2. Em seguida, usamos a chave ImageGridView_ItemContainerStyle para definir o ItemContainerStyle da GridView.

    <GridView x:Name="ImageGridView"
            ...
            ItemContainerStyle="{StaticResource ImageGridView_ItemContainerStyle}">
    </GridView>
    

Compile e execute o aplicativo e veja como ele está agora. À medida que você redimensiona a janela, o controle GridView cuida da reorganização dos itens para se ajustar melhor ao espaço. Em algumas larguras, há muito espaço no lado direito da janela do aplicativo. Ficaria melhor se a GridView e/ou seu conteúdo fossem centralizados. Portanto, vamos cuidar disso a seguir.

Dica

Se você quiser experimentar, tente definir os setters Background e Margin para valores diferentes para ver o efeito que isso tem.

Etapa 9: Experimentar com layout

Talvez você esteja se perguntando se é melhor centralizar a própria GridView ou centralizar o conteúdo. Vamos tentar centralizar a GridView primeiro.

  1. Para tornar mais fácil ver exatamente em que local a GridView está na Janela e o que acontece enquanto experimentamos o layout, vamos definir a propriedade Background como vermelha.

    <GridView x:Name="ImageGridView"
            ...
            Background="Red">
    </GridView>
    
  2. Agora vamos definir a propriedade HorizontalAlignment como Center.

    <GridView x:Name="ImageGridView"
            ...
            HorizontalAlignment="Center">
    </GridView>
    

    Confira também Alinhamento, margem, preenchimento.

Crie e execute agora e experimente ajustar a largura da janela. Você pode ver que há uma quantidade igual de espaço vazio em ambos os lados da tela de fundo vermelha da GridView. Portanto, alcançamos a meta de centralizar as imagens. Porém, agora está mais claro do que antes que a barra de rolagem pertence à GridView, não à janela. Portanto, precisamos alterar a GridView de volta para preencher a janela. Demonstramos que, em vez de centralizar a GridView na janela, precisamos centralizar as imagens na GridView.

  1. Agora, exclua o atributo HorizontalAlignment que você adicionou na etapa anterior.

Etapa 10: Editar o modelo de painel de itens

Os controles de itens dispõem seus contêineres de itens dentro do que é conhecido como um painel de itens. Podemos definir que tipo de painel é usado e definir propriedades nesse painel editando o modelo de painel de itens da GridView. Então é isso que vamos fazer nesta seção.

  1. Em MainWindow.xaml, adicione um recurso ItemsPanelTemplate a nosso dicionário de recursos. O painel de itens é do tipo ItemsWrapGrid e estamos definindo sua propriedade HorizontalAlignment como Central.

    <Grid>
        <Grid.Resources>
        ...
            <ItemsPanelTemplate x:Key="ImageGridView_ItemsPanelTemplate">
                <ItemsWrapGrid Orientation="Horizontal"
                               HorizontalAlignment="Center"/>
            </ItemsPanelTemplate>
        </Grid.Resources>
    
  2. Em seguida, usamos a chave ImageGridView_ItemsPanelTemplate para definir o ItemsPanel da GridView.

    <GridView x:Name="ImageGridView"
            ...
            ItemsPanel="{StaticResource ImageGridView_ItemsPanelTemplate}">
    </GridView>
    

Quando você cria e executa desta vez e experimenta o ajuste da largura da janela, há uma quantidade igual da tela de fundo vermelha da GridView em ambos os lados das imagens. E como a GridView preenche a janela, a barra de rolagem se alinha bem com a borda da janela, em que os usuários podem esperar que ela seja.

  1. Agora que terminamos de experimentar o layout, remova Background="Red" da GridView.

Etapa 11: Substituir a imagem do espaço reservado por uma foto

Agora é hora de mover nosso esboço para um nível mais alto de fidelidade; e isso significa substituir a imagem de espaço reservado pelas reais e substituir o texto de espaço reservado no estilo "lorem ipsum" por dados reais. Vamos cuidar das imagens primeiro.

Importante

A técnica que vamos usar para exibir as fotos na pasta Assets\Samples envolve a atualização progressiva dos itens da GridView. Especificamente, esse é o código nos métodos ImageGridView_ContainerContentChanging e ShowImage no exemplo de código abaixo, incluindo o uso das propriedades ContainerContentChangingEventArgs.InRecycleQueue e ContainerContentChangingEventArgs.Phase. Para saber mais, confira a otimização das interfaces do usuário ListView e GridView. Mas, em poucas palavras, a GridView nos informará (por meio de um evento) quando um de seus contêineres de itens estiver pronto para exibir seu item. Então vamos acompanhar em qual fase do ciclo de vida de atualização o contêiner de item está para que possamos determinar quando ele está pronto para exibir dados de fotos.

  1. Em MainWindow.xaml.cs, adicione um método à MainWindow chamado ImageGridView_ContainerContentChanging. Esse é um método de manipulação de eventos e o evento que ele vai manipular é ContainerContentChanging. Também precisamos fornecer a implementação do método ShowImage do qual ImageGridView_ContainerContentChanging depende. Cole as diretivas using e as duas implementações de método em MainWindow.xaml.cs:

    ...
    using Microsoft.UI.Xaml.Controls;
    ...
    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();
        }
    }
    
  1. Então, em MainWindow.xaml, registre o manipulador de eventos ImageGridView_ContainerContentChanging com o evento ContainerContentChanging da GridView.

    <GridView x:Name="ImageGridView"
            ...
            ContainerContentChanging="ImageGridView_ContainerContentChanging">
    </GridView>
    

Etapa 12: Substituir o texto do espaço reservado por dados reais

Nesta seção, vamos usar associações de dados únicos. Uma associação única é ótima para dados que não são alterados no tempo de execução. Isso significa que as associações pontuais são de alto desempenho e fáceis de criar.

  1. Em MainWindow.xaml, localize o recurso do modelo de dados ImageGridView_ItemTemplate. Vamos informar ao modelo de dados que seu trabalho será um modelo para a classe ImageFileInfo, que, como você lembrará, é do tipo de itens que nossa GridView está exibindo.

  2. Para fazer isso, adicione um valor x:DataType ao modelo, assim:

    <DataTemplate x:Key="ImageGridView_ItemTemplate"
                  x:DataType="local:ImageFileInfo">
        ...
    

    Se você não estiver familiarizado com a sintaxe local: mostrada acima (ou com a sintaxe xmlns:local já na marca Window de abertura), confira namespaces XAML e mapeamento de namespace.

    Agora que definimos um x:DataType, podemos usar expressões x:Bind de associação de dados no modelo de dados para fazer a associação com propriedade do tipo de dados que especificamos (ImageFileInfo, nesse caso).

  3. No modelo de dados, localize o primeiro elemento TextBlock (aquele com seu Texto atualmente definido como ImageTitle). Substitua seu valor de Texto, conforme mostrado abaixo.

    Dica

    Você pode copiar e colar a marcação abaixo ou pode usar IntelliSense no Visual Studio. Para fazer isso, selecione o valor atual que está dentro das aspas e digite {. O IntelliSense adiciona automaticamente a chave de fechamento e exibe uma lista de conclusão de código. Você pode rolar para baixo para x:Bind e clicar duas vezes nele. Porém, pode ser mais eficiente digitar x: (observe como x:Bind é filtrado e aparece no topo da lista de conclusão) e pressionar a tecla TAB. Agora pressione a tecla SPACE e digite ImageT (tanto do nome da propriedade ImageTitle quanto necessário para levá-la ao topo da lista de conclusão) e TAB.

    <TextBlock Text="{x:Bind ImageTitle}"
        ... />
    

    Uma x:Bind expressão vincula o valor de uma propriedade de interface do usuário com o valor de uma propriedade de objeto de dados. Claro, isso depende da primeira configuração x:DataType para o tipo desse objeto de dados para que as ferramentas e o runtime saibam a quais propriedades estão disponíveis para associar.

    Para obter mais informações, confira extensão de marcação {x:Bind} e vinculação de dados em profundidade.

  4. Da mesma forma, substitua os valores dos outros TextBlocks e do RatingControl. Eis o resultado:

    <TextBlock Text="{x:Bind ImageTitle}" ... />
    <StackPanel ... >
        <TextBlock Text="{x:Bind ImageFileType}" ... />
        <TextBlock Text="{x:Bind ImageDimensions}" ... />
    </StackPanel>
    <RatingControl Value="{x:Bind ImageRating}" ... />
    

Se você compilar e executar o aplicativo agora, em vez de espaços reservados, verá fotos reais e texto real (e outros dados). Visual e funcionalmente, esse aplicativo simples agora está concluído. Porém, como coda, vamos fazer uma última pequena vinculação de dados.

The finished app.

Etapa 13: Associar a GridView à coleção de Imagens (apenas C#)

Importante

Execute esta última etapa somente se você criou um projeto C#.

Dica

Você descobrirá que há algumas coisas (geralmente relacionadas à interface do usuário gerada dinamicamente) que você não pode fazer na marcação XAML. Porém, em geral, se você pode fazer algo na marcação, então isso é preferível. Essa opção oferece uma separação ligeiramente mais limpa entre a exibição que a marcação XAML representa e o modelo (ou modelo de exibição) que o código imperativo representa. Isso tende a melhorar o fluxo de trabalho nas ferramentas e entre os membros da equipe.

No momento, estamos usando o código imperativo para associar a propriedade ItemsSource da GridView à propriedade Images de MainWindow. Contudo, podemos fazer isso na marcação.

  1. Na classe MainWindow, exclua (ou comente) a última linha de GetItemsAsync, que define o ItemsSource de ImageGridView como o valor da propriedade Images.

  2. Então, em MainWindow.xaml, localize a GridView chamada ImageGridView e adicione um atributo ItemsSource como este. Você pode usar o IntelliSense para fazer essa alteração se desejar.

    <GridView x:Name="ImageGridView"
              ...
              ItemsSource="{x:Bind Images}"
    

O valor da propriedade Images não é alterado no tempo de execução para esse aplicativo específico. No entanto, como Images é do tipo ObservableCollection<T>, o conteúdo da coleção pode ser alterado (ou seja, os elementos podem ser adicionados ou excluídos) e a associação observará automaticamente as alterações e atualizará a interface do usuário.

Conclusão

Neste tutorial, percorremos o processo de usar o Visual Studio para criar um aplicativo WinUI 3 simples que exibe fotos. Espero que este tutorial tenha dado a você experiência em trabalhar em um aplicativo WinUI 3 com controles, painéis de layout, associação de dados e otimização da interface do usuário da GridView.

Confira também

Tutorial: Criar um visualizador de fotos simples direcionado a várias plataformas