Parte 3: Aggiungere un controllo CalendarView di UWP tramite le isole XAML

Questa è la terza parte di un'esercitazione che illustra come modernizzare un'app desktop WPF di esempio denominata Contoso Expenses. Per una panoramica dell'esercitazione, dei prerequisiti e delle istruzioni per il download dell'app di esempio, vedere Esercitazione: Modernizzare un'app WPF. Questo articolo presuppone che tu abbia già completato la parte 2.

Nello scenario fittizio di questa esercitazione il team di sviluppo di Contoso vuole semplificare la scelta della data per una nota spese su un dispositivo abilitato per il tocco. In questa parte dell'esercitazione aggiungerai all'app un controllo UWP CalendarView. Si tratta dello stesso controllo usato nella funzionalità di data e ora di Windows sulla barra delle applicazioni.

CalendarViewControl image

Diversamente dal controllo InkCanvas aggiunto nella parte 2, Windows Community Toolkit non fornisce una versione con wrapper del controllo UWP CalendarView da usare nelle app WPF. In alternativa, ospiterai un controllo InkCanvas nel controllo generico WindowsXamlHost. Puoi usare questo controllo per ospitare qualsiasi controllo UWP del produttore fornito dalla libreria WinUI o da Windows SDK o qualsiasi controllo UWP personalizzato creato da terze parti. Il controllo WindowsXamlHost viene fornito dal pacchetto NuGet Microsoft.Toolkit.Wpf.UI.XamlHost. Questo pacchetto è incluso nel pacchetto NuGet Microsoft.Toolkit.Wpf.UI.Controls installato nella parte 2.

Nota

Questa esercitazione illustra solo come usare WindowsXamlHost per ospitare il controllo CalendarView del produttore fornito da Windows SDK. Per una procedura dettagliata su come ospitare un controllo personalizzato, vedi Ospitare un controllo UWP personalizzato in un'app WPF usando le isole XAML.

Per usare il controllo WindowsXamlHost, dovrai chiamare direttamente le API WinRT dal codice nell'app WPF. Il pacchetto NuGet Microsoft.Windows.SDK.Contracts contiene i riferimenti necessari per consentirti di chiamare le API WinRT dall'app. Questo pacchetto è incluso anche nel pacchetto NuGet Microsoft.Toolkit.Wpf.UI.Controls installato nella parte 2.

Aggiungere il controllo WindowsXamlHost

  1. In Esplora soluzioni espandi la cartella Viste del progetto ContosoExpenses.Core e fai doppio clic sul file AddNewExpense.xaml. Si tratta del modulo usato per aggiungere una nuova spesa alla nota spese. Ecco come viene visualizzato nella versione corrente dell'app.

    Add new expense

    Il controllo selezione data incluso in WPF è destinato ai computer tradizionali con mouse e tastiera. La scelta di una data con un touchscreen non è fattibile, a causa delle dimensioni ridotte del controllo e dello spazio limitato tra ogni giorno nel calendario.

  2. Nella parte superiore del file AddNewExpense.XAML aggiungi l'attributo seguente all'elemento Window.

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

    Dopo l'aggiunta di questo attributo, l'elemento Window dovrebbe essere simile al seguente.

    <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. Modifica l'attributo Height dell'elemento Window passando da 450 a 800. Questa operazione è necessaria perché il controllo UWP CalendarView occupa più spazio rispetto alla selezione data 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. Verso la fine del file individua l'elemento DatePicker e sostituiscilo con il codice XAML seguente.

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

    Il codice XAML aggiunge il controllo WindowsXamlHost. La proprietà InitialTypeName indica il nome completo del controllo UWP che vuoi ospitare, in questo caso Windows.UI.Xaml.Controls.CalendarView.

  5. Premi F5 per compilare ed eseguire l'app nel debugger. Scegli un dipendente dall'elenco e quindi fai clic sul pulsante Add new expense (Aggiungi nuova spesa). Verifica che la pagina seguente ospiti il nuovo controllo UWP CalendarView.

    CalendarView wrapper

  6. Chiudere l'app.

Interagire con il controllo WindowsXamlHost

A questo punto, aggiornerai l'app in modo che elabori la data selezionata, la visualizzi sullo schermo e popoli l'oggetto Expense da salvare nel database.

Il controllo UWP CalendarView contiene due membri pertinenti per questo scenario:

  • La proprietà SelectedDates contiene la data selezionata dall'utente.
  • L'evento SelectedDatesChanged viene generato quando l'utente seleziona una data.

Tuttavia, il controllo WindowsXamlHost è un controllo host generico per qualsiasi tipo di controllo UWP. Non espone pertanto una proprietà denominata SelectedDates o un evento denominato SelectedDatesChanged, perché entrambi sono specifici del controllo CalendarView. Per accedere a questi membri, devi scrivere codice per eseguire il cast di WindowsXamlHost nel tipo CalendarView. Il modo migliore per eseguire questa operazione è in risposta all'evento ChildChanged del controllo WindowsXamlHost, che viene generato dopo il rendering del controllo ospitato.

  1. Nel file AddNewExpense.xaml aggiungi un gestore eventi per l'evento ChildChanged del controllo WindowsXamlHost aggiunto in precedenza. Al termine, l'elemento WindowsXamlHost dovrebbe essere simile al seguente.

    <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. Nello stesso file individua l'elemento Grid.RowDefinitions dell'elemento Grid principale. Aggiungi un altro elemento RowDefinition con l'elemento Height impostato su Auto alla fine dell'elenco di elementi figlio. Al termine, l'elemento Grid.RowDefinitions dovrebbe essere simile al seguente (dovrebbero essere presenti nove elementi 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. Aggiungi il seguente codice XAML dopo l'elemento WindowsXamlHost e prima dell'elemento Button verso la fine del file.

    <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. Individua l'elemento Button verso la fine del file e modifica il valore della proprietà Grid.Row passando da 7 a 8. In questo modo il pulsante verrà spostato di una riga verso il basso nella griglia perché è stata aggiunta una nuova riga.

    <Button Content="Save" Grid.Row="8" Grid.Column="0" Command="{Binding Path=SaveExpenseCommand}" Margin="5, 12, 0, 0" HorizontalAlignment="Left" Width="180" />
    
  5. Apri il file di codice AddNewExpense.xaml.cs.

  6. Aggiungi la seguente istruzione all'inizio del file.

    using Microsoft.Toolkit.Wpf.UI.XamlHost;
    
  7. Aggiungi alla classe AddNewExpense il gestore eventi indicato di seguito. Questo codice implementa l'evento ChildChanged del controllo WindowsXamlHost dichiarato in precedenza nel file 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));
                }
            };
        }
    }
    

    Questo codice usa la proprietà Child del controllo WindowsXamlHost per accedere al controllo UWP CalendarView. Il codice sottoscrive quindi l'evento SelectedDatesChanged, che viene attivato quando l'utente seleziona una data dal calendario. Questo gestore eventi passa la data selezionata all'elemento ViewModel in cui il nuovo oggetto Expense viene creato e salvato nel database. A tale scopo, il codice usa l'infrastruttura di messaggistica fornita dal pacchetto NuGet MVVM Light. Il codice invia un messaggio denominato SelectedDateMessage a ViewModel, che lo riceverà e imposterà la proprietà Date sul valore selezionato. In questo scenario il controllo CalendarView è configurato per la modalità a selezione singola, quindi la raccolta conterrà un solo elemento.

    A questo punto, il progetto non verrà ancora compilato perché non dispone dell'implementazione per la classe SelectedDateMessage, che viene implementata con i passaggi seguenti.

  8. In Esplora soluzioni fare clic con il pulsante destro del mouse sulla cartella Messaggi e sceglere Aggiungi -> Classe. Assegna il nome SelectedDateMessage alla classe e fai clic su Aggiungi.

  9. Sostituisci il contento del file di codice SelectedDateMessage.cs con il codice seguente. Questo codice aggiunge un'istruzione using per lo spazio dei nomi GalaSoft.MvvmLight.Messaging (dal pacchetto NuGet Light MVVM), eredita dalla classe MessageBase e aggiunge la proprietà DateTime inizializzata tramite il costruttore pubblico. Al termine, il file dovrebbe essere simile al seguente.

    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. Aggiornerai quindi ViewModel in modo da ricevere questo messaggio e popolare la proprietà Date di ViewModel. In Esplora soluzioni espandi la cartella ViewModels e apri il file AddNewExpenseViewModel.cs.

  11. Individua il costruttore pubblico per la classe AddNewExpenseViewModel e aggiungi alla fine del costruttore il codice seguente. Questo codice esegue la registrazione per ricevere SelectedDateMessage. Estrai dal codice tramite la proprietà SelectedDate la data selezionata, che verrà usata per impostare la proprietà Date esposta da ViewModel. Poiché questa proprietà è associata al controllo TextBlock aggiunto in precedenza, puoi visualizzare la data selezionata dall'utente.

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

    Al termine, il costruttore AddNewExpenseViewModel avrà un aspetto simile al seguente.

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

    Nota

    Il metodo SaveExpenseCommand nello stesso file di codice salva le spese nel database. Questo metodo usa la proprietà Date per creare il nuovo oggetto Expense. Questa proprietà viene ora popolata dal controllo CalendarView tramite il messaggio SelectedDateMessage appena creato.

  12. Premi F5 per compilare ed eseguire l'app nel debugger. Scegli uno dei dipendenti disponibili e quindi fai clic sul pulsante Add new expense (Aggiungi nuova spesa). Completa tutti i campi nel modulo e scegli una data dal nuovo controllo CalendarView. Verifica che la data selezionata venga visualizzata come stringa sotto il controllo.

  13. Fai clic sul pulsante Salva. Il modulo verrà chiuso e la nuova spesa verrà aggiunta alla fine della nota spese. La prima colonna con la data della spesa deve corrispondere alla data selezionata nel controllo CalendarView.

Passaggi successivi

A questo punto dell'esercitazione hai sostituito un controllo WPF data/ora con il controllo UWP CalendarView, che supporta il tocco e le penne digitali, oltre all'input da mouse e tastiera. Sebbene Windows Community Toolkit non fornisca una versione con wrapper del controllo UWP CalendarView da usare direttamente in un'app WPF, hai potuto ospitare il controllo usando il controllo generico WindowsXamlHost.

Adesso è tutto pronto per Parte 4: Aggiungere attività utente e notifiche di Windows.