Udostępnij przez


Część 3: Dodaj kontrolkę UWP CalendarView za pomocą wysp XAML

Jest to trzecia część samouczka, który pokazuje, jak zmodernizować przykładową aplikację klasyczną WPF o nazwie Contoso Expenses. Aby zapoznać się z omówieniem samouczka, wymagań wstępnych i instrukcji dotyczących pobierania przykładowej aplikacji, zobacz Tutorial: Modernize a WPF app. W artykule przyjęto, że ukończyłeś już część 2 .

W fikcyjnym scenariuszu tego samouczka zespół deweloperów firmy Contoso chce ułatwić wybór daty raportu wydatków na urządzeniu z obsługą dotykową. W tej części samouczka dodasz do aplikacji kontrolkę CalendarView platformy UWP. Jest to ta sama kontrolka, która jest używana w funkcji daty i godziny systemu Windows na pasku zadań.

obraz CalendarViewControl

W przeciwieństwie do kontrolki InkCanvas dodanej w części 2, zestaw narzędzi Windows Community Toolkit nie udostępnia opakowanej wersji CalendarView platformy UWP, której można używać w aplikacjach WPF. Alternatywnie będziesz hostować InkCanvas w kontrolce ogólnej WindowsXamlHost. Za pomocą tej kontrolki można hostować dowolną kontrolkę platformy UWP innej firmy zapewnianą przez zestaw Windows SDK lub bibliotekę WinUI lub dowolną niestandardową kontrolkę platformy UWP utworzoną przez inną firmę. Kontrolka WindowsXamlHost jest dostarczana przez pakiet NuGet Microsoft.Toolkit.Wpf.UI.XamlHost. Ten pakiet jest dołączony do pakietu NuGet , który zainstalowałeś w części 2.

Uwaga / Notatka

W tym samouczku pokazano tylko, jak używać WindowsXamlHost do hostowania kontrolki CalendarView dostępnej w zestawie Windows SDK. Aby zapoznać się z przewodnikiem pokazującym, jak hostować niestandardową kontrolkę UWP w aplikacji WPF, zobacz Hostowanie kontrolki niestandardowej UWP wykorzystując wyspy XAML.

Aby użyć kontrolki WindowsXamlHost , należy bezpośrednio wywołać interfejsy API WinRT z kodu w aplikacji WPF. Microsoft.Windows.SDK.Contracts Pakiet NuGet zawiera odwołania niezbędne do umożliwienia wywoływania interfejsów API WinRT z aplikacji. Ten pakiet znajduje się również w pakiecie NuGet Microsoft.Toolkit.Wpf.UI.Controls zainstalowanym w części 2.

Dodawanie kontrolki WindowsXamlHost

  1. W Eksploratorze rozwiązań rozwiń folder Views w projekcie ContosoExpenses.Core i kliknij dwukrotnie plik AddNewExpense.xaml. Jest to formularz używany do dodawania nowych wydatków do listy. Poniżej przedstawiono sposób wyświetlania jej w bieżącej wersji aplikacji.

    Dodaj nowy wydatek

    Kontrolka selektora dat zawarta w WPF jest przeznaczona dla tradycyjnych komputerów z myszą i klawiaturą. Wybranie daty z ekranem dotykowym nie jest naprawdę możliwe, ze względu na niewielki rozmiar kontrolki i ograniczoną przestrzeń między każdym dniem w kalendarzu.

  2. W górnej części pliku AddNewExpense.xaml dodaj następujący atrybut do elementu Window.

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

    Po dodaniu tego atrybutu element Window powinien teraz wyglądać następująco.

    <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. Zmień atrybut Height elementu Okna z 450 na 800. Jest to konieczne, ponieważ kontrolka CalendarView platformy UWP zajmuje więcej miejsca niż selektor dat 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. DatePicker Znajdź element w dolnej części pliku i zastąp ten element następującym kodem XAML.

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

    Ten kod XAML dodaje kontrolkę WindowsXamlHost . Właściwość InitialTypeName wskazuje pełną nazwę kontrolki uwP, którą chcesz hostować (w tym przypadku Windows.UI.Xaml.Controls.CalendarView).

  5. Naciśnij F5, aby skompilować i uruchomić aplikację w debugerze. Wybierz pracownika z listy, a następnie naciśnij przycisk Dodaj nowy wydatek . Upewnij się, że poniższa strona hostuje nową kontrolkę CalendarView platformy UWP.

    opakowanie CalendarView

  6. Zamknij aplikację.

Interakcja z kontrolką WindowsXamlHost

Następnie zaktualizujesz aplikację, aby przetworzyć wybraną datę, wyświetlić ją na ekranie i wypełnić obiekt Wydatki , aby zapisać w bazie danych.

UWP CalendarView zawiera dwóch członków istotnych dla tego scenariusza:

  • Właściwość SelectedDates zawiera datę wybraną przez użytkownika.
  • Zdarzenie SelectedDatesChanged jest zgłaszane, gdy użytkownik wybierze datę.

Jednak kontrolka WindowsXamlHost jest ogólną kontrolką hosta dla dowolnego rodzaju kontrolki platformy UWP. W związku z tym nie uwidacznia właściwości o nazwie SelectedDates ani zdarzenia o nazwie SelectedDatesChanged, ponieważ są one specyficzne dla kontrolki CalendarView . Aby uzyskać dostęp do tych elementów członkowskich, należy napisać kod, który przekonwertuje WindowsXamlHost na typ CalendarView. Najlepszym miejscem, aby to zrobić, to w odpowiedzi na zdarzenie ChildChanged kontrolki WindowsXamlHost, które jest wywoływane, gdy kontrolka jest hostowana i wyrenderowana.

  1. W pliku AddNewExpense.xaml dodaj procedurę obsługi zdarzenia ChildChanged dla kontrolki WindowsXamlHost, którą dodałeś wcześniej. Po zakończeniu element WindowsXamlHost powinien wyglądać następująco.

    <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. W tym samym pliku znajdź element Grid.RowDefinitions głównej Grid. Dodaj jeszcze jeden element RowDefinition z Height równy Auto na końcu listy elementów podrzędnych. Kiedy skończysz, element Grid.RowDefinitions powinien wyglądać następująco (teraz powinno być 9 elementów 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. Dodaj następujący kod XAML po elemecie WindowsXamlHost i przed elementem Button na końcu pliku.

    <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. Znajdź element przycisk blisko końca pliku i zmień właściwość Grid.Row z 7 na 8. Spowoduje to przesunięcie przycisku w dół o jeden wiersz w siatce, ponieważ dodano nowy wiersz.

    <Button Content="Save" Grid.Row="8" Grid.Column="0" Command="{Binding Path=SaveExpenseCommand}" Margin="5, 12, 0, 0" HorizontalAlignment="Left" Width="180" />
    
  5. Otwórz plik kodu AddNewExpense.xaml.cs .

  6. Dodaj następującą instrukcję na początku pliku.

    using Microsoft.Toolkit.Wpf.UI.XamlHost;
    
  7. Dodaj następującą obsługę zdarzeń do klasy AddNewExpense. Ten kod implementuje zdarzenie ChildChanged kontrolki WindowsXamlHost, które zadeklarowałeś wcześniej w pliku 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));
                }
            };
        }
    }
    

    Ten kod używa właściwości Child kontrolki WindowsXamlHost do uzyskania dostępu do kontrolki CalendarView platformy UWP . Następnie kod subskrybuje zdarzenie SelectedDatesChanged, które jest wyzwalane, gdy użytkownik wybierze datę z kalendarza. Ta procedura obsługi zdarzeń przekazuje wybraną datę do modelu ViewModel, w którym jest tworzony nowy obiekt Expense i zapisywany w bazie danych. W tym celu kod używa infrastruktury obsługi komunikatów dostarczonej przez pakiet NuGet MVVM Light. Kod wysyła komunikat o nazwie SelectedDateMessage do modelu ViewModel, który otrzyma go i ustawi właściwość Date z wybraną wartością. W tym scenariuszu kontrolka CalendarView jest skonfigurowana dla trybu wyboru pojedynczego, więc kolekcja będzie zawierać tylko jeden element.

    W tym momencie projekt nadal nie zostanie skompilowany, ponieważ brakuje implementacji dla klasy SelectedDateMessage . Poniższe kroki implementują tę klasę.

  8. W Eksploratorze rozwiązań kliknij prawym przyciskiem folder Messages i wybierz Dodaj -> Klasa. Nadaj nowej klasie nazwę SelectedDateMessage i kliknij przycisk Dodaj.

  9. Zastąp zawartość pliku kodu SelectedDateMessage.cs następującym kodem. Ten kod dodaje instrukcję using dla przestrzeni nazw GalaSoft.MvvmLight.Messaging (z pakietu NuGet MVVM Light), dziedziczy z klasy MessageBase i dodaje właściwość DateTime zainicjowaną za pomocą konstruktora publicznego. Po zakończeniu plik powinien wyglądać następująco.

    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. Następnie zaktualizujesz ViewModel, by mógł odbierać ten komunikat i wypełnisz pole Date modelu ViewModel. W Eksploratorze rozwiązań rozwiń folder ViewModels i otwórz plik AddNewExpenseViewModel.cs .

  11. Znajdź publiczny konstruktor klasy AddNewExpenseViewModel i dodaj następujący kod na końcu konstruktora. Ten kod rejestruje się do otrzymywania SelectedDateMessage, wyodrębnia wybraną datę za pomocą właściwości SelectedDate i używa jej do ustawienia właściwości Date, udostępnionej przez model ViewModel. Ponieważ ta właściwość jest powiązana z dodaną wcześniej kontrolką TextBlock , możesz zobaczyć datę wybraną przez użytkownika.

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

    Gdy skończysz, konstruktor AddNewExpenseViewModel powinien wyglądać następująco.

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

    Uwaga / Notatka

    Metoda SaveExpenseCommand w tym samym pliku kodu wykonuje pracę nad zapisaniem wydatków w bazie danych. Ta metoda używa właściwości Date do utworzenia nowego obiektu Expense . Ta właściwość jest teraz wypełniana przez kontrolkę CalendarView za pomocą komunikatu SelectedDateMessage, który właśnie utworzyłeś.

  12. Naciśnij F5, aby skompilować i uruchomić aplikację w debugerze. Wybierz jednego z dostępnych pracowników, a następnie kliknij przycisk Dodaj nowy wydatek . Wypełnij wszystkie pola w formularzu i wybierz datę z nowej kontrolki CalendarView . Upewnij się, że wybrana data jest wyświetlana jako ciąg poniżej kontrolki.

  13. Naciśnij przycisk Zapisz. Formularz zostanie zamknięty, a nowy wydatek zostanie dodany na końcu listy wydatków. Pierwsza kolumna z datą wydatków powinna być datą wybraną w kontrolce CalendarView .

Dalsze kroki

W tym momencie w samouczku pomyślnie zastąpiono kontrolkę daty i czasu WPF kontrolką CalendarView platformy UWP, która obsługuje dotyk oraz pióra cyfrowe, oprócz obsługi myszy i klawiatury. Mimo że zestaw narzędzi Windows Community Toolkit nie udostępnia opakowanej wersji kontrolki CalendarView platformy uniwersalnej systemu Windows, której można używać bezpośrednio w aplikacji WPF, można było hostować kontrolkę przy użyciu ogólnego WindowsXamlHost kontrolki.

Teraz jesteś gotowy na część 4: Dodawanie działań i powiadomień użytkownika systemu Windows.