Partilhar via


Parte 3: Adicionar um controlo CalendarView da UWP usando XAML Islands

Esta é a terceira parte de um tutorial que demonstra como modernizar um aplicativo de desktop WPF de exemplo chamado Contoso Expenses. Para obter uma visão geral do tutorial, pré-requisitos e instruções para baixar o aplicativo de exemplo, consulte Tutorial: Modernizar um aplicativo WPF. Este artigo pressupõe que você já tenha concluído parte 2.

No cenário fictício deste tutorial, a equipe de desenvolvimento da Contoso deseja facilitar a escolha da data para um relatório de despesas em um dispositivo habilitado para toque. Nesta parte do tutorial, irá adicionar um controlo UWP CalendarView à aplicação. Este é o mesmo controle que é usado na funcionalidade de data e hora do Windows na barra de tarefas.

imagem de CalendarViewControl

Ao contrário do controle de InkCanvas que você adicionou noparte 2, o Kit de Ferramentas da Comunidade do Windows não fornece uma versão encapsulada do UWP CalendarView que pode ser usado em aplicativos WPF. Como alternativa, irá hospedar um InkCanvas no controle genérico WindowsXamlHost. Você pode usar esse controle para hospedar qualquer controle UWP primário fornecido pelo SDK do Windows ou biblioteca WinUI ou qualquer controle UWP personalizado criado por terceiros. O controlo WindowsXamlHost é fornecido pelo pacote NuGet Microsoft.Toolkit.Wpf.UI.XamlHost. Este pacote está incluído no pacote NuGet Microsoft.Toolkit.Wpf.UI.Controls que você instalou na parte 2 .

Observação

Este tutorial demonstra apenas como usar WindowsXamlHost para hospedar o controle CalendarView de primeira parte fornecido pelo SDK do Windows. Para obter uma explicação passo a passo que demonstra como hospedar um controle personalizado, consulte hospedar um controle UWP personalizado em um aplicativo WPF usando Ilhas XAML.

Para usar o controle WindowsXamlHost, você precisará chamar diretamente as APIs do WinRT a partir do código no aplicativo WPF. O pacote NuGet Microsoft.Windows.SDK.Contracts contém as referências necessárias para permitir que você chame APIs do WinRT a partir do aplicativo. Este pacote também está incluído no pacote NuGet Microsoft.Toolkit.Wpf.UI.Controls que você instalou na parte 2 .

Adicionar o controle WindowsXamlHost

  1. No Gerenciador de Soluções , expanda a pasta Modos de Exibição no projeto ContosoExpenses.Core e clique duas vezes no arquivo AddNewExpense.xaml. Este é o formulário usado para adicionar uma nova despesa à lista. Aqui está como ele aparece na versão atual do aplicativo.

    Adicionar nova despesa

    O controle seletor de data incluído no WPF destina-se a computadores tradicionais com mouse e teclado. Escolher uma data com um ecrã táctil não é realmente viável, devido ao pequeno tamanho do controlo e ao espaço limitado entre cada dia no calendário.

  2. No início do ficheiro AddNewExpense.xaml, adicione o seguinte atributo ao elemento Window.

    xmlns:xamlhost="clr-namespace:Microsoft.Toolkit.Wpf.UI.XamlHost;assembly=Microsoft.Toolkit.Wpf.UI.XamlHost"
    

    Depois de adicionar esse atributo, o elemento Window agora deve ter esta aparência.

    <Window x:Class="ContosoExpenses.Views.AddNewExpense"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
            xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
            xmlns:xamlhost="clr-namespace:Microsoft.Toolkit.Wpf.UI.XamlHost;assembly=Microsoft.Toolkit.Wpf.UI.XamlHost"
            DataContext="{Binding Source={StaticResource ViewModelLocator},Path=AddNewExpenseViewModel}"
            xmlns:local="clr-namespace:ContosoExpenses"
            mc:Ignorable="d"
            Title="Add new expense" Height="450" Width="800"
            Background="{StaticResource AddNewExpenseBackground}">
    
  3. Altere o atributo Height do elemento Window de 450 para 800. Isto é necessário porque o controle UWP CalendarView ocupa mais espaço do que o seletor de data do WPF.

    <Window x:Class="ContosoExpenses.Views.AddNewExpense"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
            xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
            xmlns:xamlhost="clr-namespace:Microsoft.Toolkit.Wpf.UI.XamlHost;assembly=Microsoft.Toolkit.Wpf.UI.XamlHost"
            DataContext="{Binding Source={StaticResource ViewModelLocator},Path=AddNewExpenseViewModel}"
            xmlns:local="clr-namespace:ContosoExpenses"
            mc:Ignorable="d"
            Title="Add new expense" Height="800" Width="800"
            Background="{StaticResource AddNewExpenseBackground}">
    
  4. Localize o elemento DatePicker perto da parte inferior do arquivo e substitua esse elemento pelo seguinte XAML.

    <xamlhost:WindowsXamlHost InitialTypeName="Windows.UI.Xaml.Controls.CalendarView" Grid.Column="1" Grid.Row="6" Margin="5, 0, 0, 0" x:Name="CalendarUwp"  />
    

    Este XAML adiciona o controlo WindowsXamlHost. A propriedade InitialTypeName indica o nome completo do controle UWP que você deseja hospedar (neste caso, Windows.UI.Xaml.Controls.CalendarView).

  5. Pressione F5 para criar e executar o aplicativo no depurador. Escolha um funcionário na lista e pressione o botão Adicionar nova despesa. Confirme se a página a seguir contém o novo controlo UWP CalendarView.

    Invólucro CalendarView

  6. Feche o aplicativo.

Interagir com o controlo WindowsXamlHost

Em seguida, você atualizará o aplicativo para processar a data selecionada, exibi-la na tela e preencher o objeto Expense salvar no banco de dados.

O UWP CalendarView contém dois membros que são relevantes para este cenário:

  • A propriedade SelectedDates contém a data selecionada pelo usuário.
  • O evento SelectedDatesChanged é gerado quando o usuário seleciona uma data.

No entanto, o controlador WindowsXamlHost é um controlador de host genérico para qualquer tipo de controlador UWP. Como tal, ele não expõe uma propriedade chamada SelectedDates ou um evento chamado SelectedDatesChanged, porque eles são específicos do controle CalendarView . Para acessar esses membros, você deve escrever um código que transmita o WindowsXamlHost para o tipo de CalendarView. O melhor lugar para fazer isso é em resposta ao evento ChildChanged do controlo WindowsXamlHost, que é gerado quando o controlo hospedado foi renderizado.

  1. No arquivo AddNewExpense.xaml, adicione um event handler para o evento ChildChanged do controle WindowsXamlHost que você adicionou anteriormente. Quando terminar, o elemento WindowsXamlHost deverá ter esta aparência.

    <xamlhost:WindowsXamlHost InitialTypeName="Windows.UI.Xaml.Controls.CalendarView" Grid.Column="1" Grid.Row="6" Margin="5, 0, 0, 0" x:Name="CalendarUwp"  ChildChanged="CalendarUwp_ChildChanged" />
    
  2. No mesmo arquivo, localize o elemento Grid.RowDefinitions do Grid principal . Adicione mais um elemento RowDefinition com a Height igual a Auto no final da lista de elementos filhos. Quando terminar, o elemento Grid.RowDefinitions deve ter este aspecto (deve haver agora 9 elementos RowDefinition).

    <Grid.RowDefinitions>
        <RowDefinition Height="Auto"/>
        <RowDefinition Height="Auto"/>
        <RowDefinition Height="Auto"/>
        <RowDefinition Height="Auto"/>
        <RowDefinition Height="Auto"/>
        <RowDefinition Height="Auto"/>
        <RowDefinition Height="Auto"/>
        <RowDefinition Height="Auto"/>
        <RowDefinition Height="Auto"/>
    </Grid.RowDefinitions>
    
  3. Adicione o seguinte XAML após o elemento WindowsXamlHost e antes do elemento Button perto do final do arquivo.

    <TextBlock Text="Selected date:" FontSize="16" FontWeight="Bold" Grid.Row="7" Grid.Column="0" />
    <TextBlock Text="{Binding Path=Date}" FontSize="16" Grid.Row="7" Grid.Column="1" />
    
  4. Localize o elemento Button perto do final do ficheiro e altere a propriedade Grid.Row de 7 para 8. Isso desloca o botão para baixo uma linha na grade porque você adicionou uma nova linha.

    <Button Content="Save" Grid.Row="8" Grid.Column="0" Command="{Binding Path=SaveExpenseCommand}" Margin="5, 12, 0, 0" HorizontalAlignment="Left" Width="180" />
    
  5. Abra o arquivo de código AddNewExpense.xaml.cs.

  6. Adicione a seguinte instrução à parte superior do arquivo.

    using Microsoft.Toolkit.Wpf.UI.XamlHost;
    
  7. Adicione o seguinte manipulador de eventos à classe AddNewExpense. Este código implementa o evento ChildChanged do controlo WindowsXamlHost que declarou anteriormente no arquivo XAML.

    private void CalendarUwp_ChildChanged(object sender, System.EventArgs e)
    {
        WindowsXamlHost windowsXamlHost = (WindowsXamlHost)sender;
    
        Windows.UI.Xaml.Controls.CalendarView calendarView =
            (Windows.UI.Xaml.Controls.CalendarView)windowsXamlHost.Child;
    
        if (calendarView != null)
        {
            calendarView.SelectedDatesChanged += (obj, args) =>
            {
                if (args.AddedDates.Count > 0)
                {
                    Messenger.Default.Send<SelectedDateMessage>(new SelectedDateMessage(args.AddedDates[0].DateTime));
                }
            };
        }
    }
    

    Esse código usa a propriedade Child do controlo WindowsXamlHost para aceder ao controlo CalendarView da UWP. Em seguida, o código se inscreve no evento SelectedDatesChanged, que é acionado quando o usuário seleciona uma data do calendário. Este manipulador de eventos passa a data selecionada para o ViewModel, onde o novo objeto de Despesas é criado e salvo no banco de dados. Para fazer isso, o código usa a infraestrutura de mensagens fornecida pelo pacote MVVM Light NuGet. O código envia uma mensagem chamada SelectedDateMessage para o ViewModel, que a receberá e definirá a propriedade Date com o valor selecionado. Nesse cenário, o controle CalendarView é configurado para o modo de seleção único, portanto, a coleção conterá apenas um elemento.

    Neste ponto, o projeto ainda não consegue ser compilado porque está faltando a implementação da classe SelectedDateMessage. As etapas a seguir implementam essa classe.

  8. No Gerenciador de Soluções, clique com o botão direito do rato na pasta Mensagens e escolha Adicionar -> Classe. Nomeie a nova classe SelectedDateMessage e clique em Adicionar.

  9. Substitua o conteúdo do arquivo de código SelectedDateMessage.cs pelo código a seguir. Esse código adiciona uma instrução using para o namespace GalaSoft.MvvmLight.Messaging (do pacote NuGet MVVM Light), herda da classe MessageBase e adiciona a propriedade DateTime que é inicializada através do construtor público. Quando terminar, o ficheiro deverá ter o seguinte aspeto.

    using GalaSoft.MvvmLight.Messaging;
    using System;
    
    namespace ContosoExpenses.Messages
    {
        public class SelectedDateMessage: MessageBase
        {
            public DateTime SelectedDate { get; set; }
    
            public SelectedDateMessage(DateTime selectedDate)
            {
                this.SelectedDate = selectedDate;
            }
        }
    }
    
  10. Em seguida, você atualizará o ViewModel para receber essa mensagem e preencherá a propriedade Date do ViewModel. No Explorador de Soluções, expanda a pasta ViewModels e abra o arquivo AddNewExpenseViewModel.cs.

  11. Localize o construtor público para a classe AddNewExpenseViewModel e adicione o seguinte código ao final do construtor. Esse código se registra para receber o SelectedDateMessage, extrai a data selecionada dele por meio da propriedade SelectedDate e o usamos para definir a propriedade Date exposta pelo ViewModel. Como essa propriedade está vinculada ao controle de TextBlock que você adicionou anteriormente, você pode ver a data selecionada pelo usuário.

    Messenger.Default.Register<SelectedDateMessage>(this, message =>
    {
        Date = message.SelectedDate;
    });
    

    Quando terminar, o construtor AddNewExpenseViewModel deve ter esta aparência.

    public AddNewExpenseViewModel(IDatabaseService databaseService, IStorageService storageService)
    {
        this.databaseService = databaseService;
        this.storageService = storageService;
    
        Date = DateTime.Today;
    
        Messenger.Default.Register<SelectedDateMessage>(this, message =>
        {
            Date = message.SelectedDate;
        });
    }
    

    Observação

    O método SaveExpenseCommand no mesmo arquivo de código faz o trabalho de salvar as despesas para o banco de dados. Esse método usa a propriedade Date para criar o novo objeto Expense. Essa propriedade agora está sendo preenchida pelo controle CalendarView por meio da mensagem SelectedDateMessage que você acabou de criar.

  12. Pressione F5 para criar e executar o aplicativo no depurador. Escolha um dos funcionários disponíveis e clique no botão Adicionar nova despesa. Preencha todos os campos no formulário e escolha uma data através do novo controlo CalendarView. Confirme se a data selecionada é exibida como uma cadeia de caracteres abaixo do controle.

  13. Pressione o botão Salvar. O formulário será fechado e a nova despesa será adicionada ao final da lista de despesas. A primeira coluna com a data da despesa deve ser a data selecionada no controle CalendarView.

Próximos passos

Neste ponto do tutorial, você substituiu com êxito um controle de data e hora do WPF pelo controle de UWP CalendarView, que suporta canetas digitais e de toque, além da entrada de mouse e teclado. Embora o Toolkit da Comunidade do Windows não forneça uma versão empacotada do controlo CalendarView do UWP que possa ser utilizado diretamente numa aplicação WPF, foi possível hospedar o controlo utilizando o controlo genérico WindowsXamlHost.

Agora você está pronto para Parte 4: Adicionar atividades de usuário do Windows e notificações.