Nuta
Dostęp do tej strony wymaga autoryzacji. Możesz spróbować się zalogować lub zmienić katalog.
Dostęp do tej strony wymaga autoryzacji. Możesz spróbować zmienić katalogi.
Jest to czwarta 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 tym artykule założono, że ukończono już część 3.
W poprzednich częściach tego samouczka dodałeś kontrolki UWP XAML do aplikacji, korzystając z wysp XAML. Jako efekt uboczny, umożliwiłeś aplikacji wywoływanie dowolnego interfejsu API WinRT. Dzięki temu aplikacja może korzystać z wielu innych funkcji oferowanych przez system Windows, a nie tylko kontrolek XAML platformy UWP.
W fikcyjnym scenariuszu tego samouczka zespół deweloperów firmy Contoso zdecydował się dodać do aplikacji dwie nowe funkcje: działania i powiadomienia. W tej części samouczka pokazano, jak zaimplementować te funkcje.
Dodawanie działania użytkownika
Uwaga / Notatka
Funkcja osi czasu została przerwana od czasu systemu Windows 11
W systemie Windows 10 aplikacje mogą śledzić działania wykonywane przez użytkownika, takie jak otwieranie pliku lub wyświetlanie określonej strony. Te działania są następnie udostępniane za pośrednictwem osi czasu, funkcji wprowadzonej w systemie Windows 10 w wersji 1803, która pozwala użytkownikowi szybko wrócić do przeszłości i wznowić działanie, które zostały uruchomione wcześniej.
Działania użytkowników są śledzone przy użyciu Microsoft Graph. Jednak podczas tworzenia aplikacji systemu Windows 10 nie trzeba korzystać bezpośrednio z punktów końcowych REST udostępnianych przez program Microsoft Graph. Zamiast tego możesz użyć wygodnego zestawu interfejsów API WinRT. Użyjemy tych interfejsów API WinRT w aplikacji Contoso Expenses do śledzenia za każdym razem, gdy użytkownik otworzy wydatek w aplikacji, i zastosujemy karty adaptacyjne, aby umożliwić użytkownikom tworzenie aktywności.
Wprowadzenie do kart adaptacyjnych
Ta sekcja zawiera krótkie omówienie kart adaptacyjnych. Jeśli nie potrzebujesz tych informacji, możesz pominąć tę informację i przejść bezpośrednio do instrukcji dotyczących dodania karty adaptacyjnej.
Karty adaptacyjne umożliwiają deweloperom wymianę zawartości karty w wspólny i spójny sposób. Karta adaptacyjna jest opisywana przez ładunek JSON, który definiuje jego zawartość, która może zawierać tekst, obrazy, akcje i nie tylko.
Karta adaptacyjna definiuje tylko zawartość, a nie wygląd wizualizacji zawartości. Platforma, na której jest odbierana karta adaptacyjna, może renderować zawartość przy użyciu najbardziej odpowiedniego stylu. Sposób projektowania kart adaptacyjnych odbywa się za pomocą modułu renderowania, który jest w stanie pobrać ładunek JSON i przekonwertować go na natywny interfejs użytkownika. Na przykład interfejs użytkownika może być XAML dla aplikacji WPF lub UWP, AXML dla aplikacji systemu Android lub HTML dla witryny internetowej lub czatu bota.
Oto przykład prostej zawartości karty adaptacyjnej.
{
"type": "AdaptiveCard",
"body": [
{
"type": "Container",
"items": [
{
"type": "TextBlock",
"size": "Medium",
"weight": "Bolder",
"text": "Publish Adaptive Card schema"
},
{
"type": "ColumnSet",
"columns": [
{
"type": "Column",
"items": [
{
"type": "Image",
"style": "Person",
"url": "https://pbs.twimg.com/profile_images/3647943215/d7f12830b3c17a5a9e4afcc370e3a37e_400x400.jpeg",
"size": "Small"
}
],
"width": "auto"
},
{
"type": "Column",
"items": [
{
"type": "TextBlock",
"weight": "Bolder",
"text": "Matt Hidinger",
"wrap": true
},
{
"type": "TextBlock",
"spacing": "None",
"text": "Created {{DATE(2017-02-14T06:08:39Z,SHORT)}}",
"isSubtle": true,
"wrap": true
}
],
"width": "stretch"
}
]
}
]
}
],
"actions": [
{
"type": "Action.ShowCard",
"title": "Set due date",
"card": {
"type": "AdaptiveCard",
"style": "emphasis",
"body": [
{
"type": "Input.Date",
"id": "dueDate"
},
{
"type": "Input.Text",
"id": "comment",
"placeholder": "Add a comment",
"isMultiline": true
}
],
"actions": [
{
"type": "Action.OpenUrl",
"title": "OK",
"url": "http://adaptivecards.io"
}
],
"$schema": "http://adaptivecards.io/schemas/adaptive-card.json"
}
},
{
"type": "Action.OpenUrl",
"title": "View",
"url": "http://adaptivecards.io"
}
],
"$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
"version": "1.0"
}
Na poniższym obrazie pokazano, jak ten kod JSON jest renderowany na różne sposoby przez kanał usługi Teams, Cortanę i powiadomienie systemu Windows.
Karty adaptacyjne odgrywają ważną rolę na osi czasu, ponieważ jest to sposób renderowania działań w systemie Windows. Każda miniatura wyświetlana wewnątrz „osi czasu” jest w rzeczywistości „kartą adaptacyjną”. W związku z tym, gdy zamierzasz utworzyć aktywność użytkownika w aplikacji, zostaniesz poproszony o podanie karty adaptacyjnej do jej wyświetlenia.
Uwaga / Notatka
Doskonałym sposobem na zastanowienie się nad projektem karty adaptacyjnej jest użycie projektanta online. Będziesz mieć możliwość zaprojektowania karty z blokami konstrukcyjnymi (obrazami, tekstami, kolumnami itp.) i uzyskaniem odpowiedniego kodu JSON. Po zapoznaniu się z ostatecznym projektem możesz użyć biblioteki o nazwie Karty adaptacyjne , aby ułatwić kompilowanie karty adaptacyjnej przy użyciu klas języka C# zamiast zwykłego kodu JSON, co może być trudne do debugowania i kompilowania.
Dodawanie kart adaptacyjnych
Kliknij prawym przyciskiem myszy projekt ContosoExpenses.Core w Eksploratorze rozwiązań i wybierz Zarządzanie pakietami NuGet.
W oknie menedżera pakietów NuGet
kliknij pozycję Przeglądaj .Newtonsoft.JsonWyszukaj pakiet i zainstaluj najnowszą dostępną wersję. Jest to popularna biblioteka manipulowania kodami JSON, która ułatwia manipulowanie ciągami JSON wymaganymi przez karty adaptacyjne.pakiet NuGet

Uwaga / Notatka
Jeśli nie zainstalujesz oddzielnie pakietu
Newtonsoft.Json, biblioteka kart adaptacyjnych będzie odwoływać się do wersji starszego pakietuNewtonsoft.Json, która nie obsługuje platformy .NET Core 3.0.W oknie menedżera pakietów NuGet
kliknij pozycję Przeglądaj .AdaptiveCardsWyszukaj pakiet i zainstaluj najnowszą dostępną wersję.
W Eksploratorze rozwiązań kliknij prawym przyciskiem myszy na projekcie ContosoExpenses.Core, wybierz Dodaj –> Klasa. Nadaj klasie nazwę TimelineService.cs i kliknij przycisk OK.
W pliku TimelineService.cs dodaj następujące instrukcje na początku pliku.
using AdaptiveCards; using ContosoExpenses.Data.Models;Zmień przestrzeń nazw zadeklarowaną w pliku z
ContosoExpenses.CorenaContosoExpenses.Dodaj następującą metodę do
TimelineServiceklasy .private string BuildAdaptiveCard(Expense expense) { AdaptiveCard card = new AdaptiveCard("1.0"); AdaptiveTextBlock title = new AdaptiveTextBlock { Text = expense.Description, Size = AdaptiveTextSize.Medium, Wrap = true }; AdaptiveColumnSet columnSet = new AdaptiveColumnSet(); AdaptiveColumn photoColumn = new AdaptiveColumn { Width = "auto" }; AdaptiveImage image = new AdaptiveImage { Url = new Uri("https://appmodernizationworkshop.blob.core.windows.net/contosoexpenses/Contoso192x192.png"), Size = AdaptiveImageSize.Small, Style = AdaptiveImageStyle.Default }; photoColumn.Items.Add(image); AdaptiveTextBlock amount = new AdaptiveTextBlock { Text = expense.Cost.ToString(), Weight = AdaptiveTextWeight.Bolder, Wrap = true }; AdaptiveTextBlock date = new AdaptiveTextBlock { Text = expense.Date.Date.ToShortDateString(), IsSubtle = true, Spacing = AdaptiveSpacing.None, Wrap = true }; AdaptiveColumn expenseColumn = new AdaptiveColumn { Width = "stretch" }; expenseColumn.Items.Add(amount); expenseColumn.Items.Add(date); columnSet.Columns.Add(photoColumn); columnSet.Columns.Add(expenseColumn); card.Body.Add(title); card.Body.Add(columnSet); string json = card.ToJson(); return json; }
Informacje o kodzie
Ta metoda otrzymuje obiekt Expense zawierający wszystkie informacje o wydatkach potrzebne do renderowania i tworzy nowy obiekt AdaptiveCard. Metoda dodaje następujące elementy do karty:
- Tytuł, który używa opisu wydatków.
- Obraz przedstawiający logo firmy Contoso.
- Kwota wydatków.
- Data wydatków.
Ostatnie 3 elementy są podzielone na dwie różne kolumny, dzięki czemu logo firmy Contoso i szczegóły dotyczące wydatków można umieścić obok siebie. Po skompilowania obiektu metoda zwraca odpowiedni ciąg JSON za pomocą metody ToJson .
Definiowanie działania użytkownika
Po zdefiniowaniu karty adaptacyjnej możesz utworzyć na jej podstawie działanie użytkownika.
Dodaj następujące instrukcje na początku pliku TimelineService.cs:
using Windows.ApplicationModel.UserActivities; using System.Threading.Tasks; using Windows.UI.Shell;Uwaga / Notatka
Są to przestrzenie nazw UWP. Te problemy są rozwiązywane, ponieważ pakiet NuGet
Microsoft.Toolkit.Wpf.UI.Controlszainstalowany w kroku 2 zawiera odwołanie do pakietuMicrosoft.Windows.SDK.Contracts, co umożliwia projektowi ContosoExpenses.Core korzystanie z interfejsów API WinRT, mimo że jest projektem .NET Core 3.Dodaj następujące deklaracje pól do
TimelineServiceklasy .private UserActivityChannel _userActivityChannel; private UserActivity _userActivity; private UserActivitySession _userActivitySession;Dodaj następującą metodę do
TimelineServiceklasy .public async Task AddToTimeline(Expense expense) { _userActivityChannel = UserActivityChannel.GetDefault(); _userActivity = await _userActivityChannel.GetOrCreateUserActivityAsync($"Expense-{expense.ExpenseId}"); _userActivity.ActivationUri = new Uri($"contosoexpenses://expense/{expense.ExpenseId}"); _userActivity.VisualElements.DisplayText = "Contoso Expenses"; string json = BuildAdaptiveCard(expense); _userActivity.VisualElements.Content = AdaptiveCardBuilder.CreateAdaptiveCardFromJson(json); await _userActivity.SaveAsync(); _userActivitySession?.Dispose(); _userActivitySession = _userActivity.CreateSession(); }Zapisz zmiany w TimelineService.cs.
Informacje o kodzie
Metoda AddToTimeline najpierw pobiera obiekt UserActivityChannel , który jest wymagany do przechowywania działań użytkownika. Następnie tworzy nowe działanie użytkownika przy użyciu metody GetOrCreateUserActivityAsync , która wymaga unikatowego identyfikatora. W ten sposób, jeśli działanie już istnieje, aplikacja może ją zaktualizować; w przeciwnym razie zostanie utworzony nowy. Identyfikator do przekazania zależy od rodzaju aplikacji, którą tworzysz.
- Jeśli chcesz zawsze aktualizować tę samą czynność, aby oś czasu pokazywała tylko najnowszą, możesz użyć stałego identyfikatora, np. Wydatki.
- Jeśli chcesz śledzić każde działanie jako osobne, aby Oś Czasu wyświetlała wszystkie z nich, możesz skorzystać z dynamicznego identyfikatora.
W tym scenariuszu aplikacja będzie śledzić każdy otwarty wydatek jako inną aktywność użytkownika, więc kod tworzy każdy identyfikator przy użyciu słowa kluczowego Expense-, a następnie unikatowym identyfikatorem wydatku.
Po utworzeniu przez metodę obiektu UserActivity wypełnia się go następującymi informacjami:
- ActivationUri wywoływany, gdy użytkownik kliknie aktywność w Osi czasu. Kod używa niestandardowego protokołu o nazwie contosoexpenses , który aplikacja będzie obsługiwać później.
- Obiekt VisualElements , który zawiera zestaw właściwości definiujących wygląd wizualizacji działania. Ten kod ustawia DisplayText (który jest tytułem wyświetlanym u góry wpisu na osi czasu) oraz zawartość .
W tym miejscu zdefiniowana wcześniej karta adaptacyjna odgrywa rolę. Aplikacja przekazuje wcześniej zaprojektowaną Kartę adaptacyjną jako zawartość dla metody. Jednak system Windows 10 używa innego obiektu do reprezentowania karty w porównaniu z tym, który jest używany przez AdaptiveCards pakiet NuGet. W związku z tym metoda odtwarza kartę, korzystając z metody CreateAdaptiveCardFromJson, udostępnionej przez klasę AdaptiveCardBuilder. Po utworzeniu działania użytkownika metoda zapisuje działanie i tworzy nową sesję.
Gdy użytkownik kliknie na działanie w osi czasu, protokół contosoexpenses:// zostanie uruchomiony, a adres URL będzie zawierać informacje, które aplikacja potrzebuje, aby pobrać wybrany wydatek. Jako opcjonalne zadanie można zaimplementować aktywację protokołu, aby aplikacja reagowała prawidłowo, gdy użytkownik używa osi czasu.
Integrowanie aplikacji z osią czasu
Teraz, gdy utworzono klasę, która wchodzi w interakcję z osią czasu, możemy zacząć jej używać, aby ulepszyć środowisko aplikacji. Najlepszym miejscem do użycia metody AddToTimeline uwidocznionej przez klasę TimelineService jest otwarcie strony szczegółów wydatków przez użytkownika.
W projekcie ContosoExpenses.Core rozwiń folder ViewModels i otwórz plik ExpenseDetailViewModel.cs . Jest to ViewModel obsługujący okno szczegółów wydatków.
Znajdź publiczny konstruktor klasy ExpenseDetailViewModel i dodaj następujący kod na końcu konstruktora. Za każdym razem, gdy zostanie otwarte okno wydatków, metoda wywołuje metodę AddToTimeline i przekazuje bieżące wydatki. Klasa TimelineService używa tych informacji do utworzenia działania użytkownika przy użyciu informacji o wydatkach.
TimelineService timeline = new TimelineService(); timeline.AddToTimeline(expense);Po zakończeniu konstruktor powinien wyglądać następująco.
public ExpensesDetailViewModel(IDatabaseService databaseService, IStorageService storageService) { var expense = databaseService.GetExpense(storageService.SelectedExpense); ExpenseType = expense.Type; Description = expense.Description; Location = expense.Address; Amount = expense.Cost; TimelineService timeline = new TimelineService(); timeline.AddToTimeline(expense); }Naciśnij F5, aby skompilować i uruchomić aplikację w debugerze. Wybierz pracownika z listy, a następnie wybierz wydatek. Na stronie szczegółów zanotuj opis wydatków, datę i kwotę.
Naciśnij przycisk Start + TAB , aby otworzyć oś czasu.
Przewiń listę aktualnie otwartych aplikacji, aż zobaczysz sekcję zatytułowaną Wcześniejsza. W tej sekcji przedstawiono niektóre z najnowszych działań użytkownika. Kliknij link Zobacz wszystkie działania obok nagłówka Wcześniejsza.
Upewnij się, że zostanie wyświetlona nowa karta z informacjami o wydatkach wybranych właśnie w aplikacji.
Jeśli teraz otworzysz inne wydatki, zobaczysz nowe karty dodawane jako działania użytkownika. Pamiętaj, że kod używa innego identyfikatora dla każdej aktywności, więc tworzy kartę dla każdego wydatku, który otworzysz w aplikacji.
Zamknij aplikację.
Dodawanie powiadomienia
Drugą funkcją, którą chce dodać zespół deweloperów firmy Contoso, jest powiadomienie wyświetlane użytkownikowi za każdym razem, gdy nowy wydatek zostanie zapisany w bazie danych. W tym celu można użyć wbudowanego systemu powiadomień w systemie Windows 10, który jest udostępniany deweloperom za pośrednictwem interfejsów API WinRT. Ten system powiadomień ma wiele zalet:
- Powiadomienia są zgodne z resztą systemu operacyjnego.
- Są wykonalne.
- Są one przechowywane w Centrum akcji, aby można je było przejrzeć później.
Aby dodać powiadomienie do aplikacji:
W Eksploratorze rozwiązań kliknij prawym przyciskiem myszy na projekcie ContosoExpenses.Core, wybierz Dodaj –> Klasa. Nadaj klasie nazwę NotificationService.cs i kliknij przycisk OK.
W pliku NotificationService.cs dodaj następujące instrukcje na początku pliku.
using Windows.Data.Xml.Dom; using Windows.UI.Notifications;Zmień przestrzeń nazw zadeklarowaną w pliku z
ContosoExpenses.CorenaContosoExpenses.Dodaj następującą metodę do
NotificationServiceklasy .public void ShowNotification(string description, double amount) { string xml = $@"<toast> <visual> <binding template='ToastGeneric'> <text>Expense added</text> <text>Description: {description} - Amount: {amount} </text> </binding> </visual> </toast>"; XmlDocument doc = new XmlDocument(); doc.LoadXml(xml); ToastNotification toast = new ToastNotification(doc); ToastNotificationManager.CreateToastNotifier().Show(toast); }Powiadomienia toast są reprezentowane przez dokument XML, który może zawierać tekst, obrazy, akcje i inne elementy. Wszystkie obsługiwane elementy można znaleźć tutaj. Ten kod używa bardzo prostego schematu z dwoma wierszami tekstu: tytułem i treścią. Gdy kod definiuje ładunek XML i ładuje go w obiekcie XmlDocument , opakowuje kod XML w obiekcie ToastNotification i wyświetla go przy użyciu klasy ToastNotificationManager .
W projekcie ContosoExpenses.Core rozwiń folder ViewModels i otwórz plik AddNewExpenseViewModel.cs .
Znajdź metodę
SaveExpenseCommand, która jest wyzwalana, gdy użytkownik naciśnie przycisk, aby zapisać nowe wydatki. Dodaj następujący kod do tej metody tuż po wywołaniuSaveExpensemetody .NotificationService notificationService = new NotificationService(); notificationService.ShowNotification(expense.Description, expense.Cost);Kiedy skończysz, metoda
SaveExpenseCommandpowinna wyglądać następująco.private RelayCommand _saveExpenseCommand; public RelayCommand SaveExpenseCommand { get { if (_saveExpenseCommand == null) { _saveExpenseCommand = new RelayCommand(() => { Expense expense = new Expense { Address = Address, City = City, Cost = Cost, Date = Date, Description = Description, EmployeeId = storageService.SelectedEmployeeId, Type = ExpenseType }; databaseService.SaveExpense(expense); NotificationService notificationService = new NotificationService(); notificationService.ShowNotification(expense.Description, expense.Cost); Messenger.Default.Send<UpdateExpensesListMessage>(new UpdateExpensesListMessage()); Messenger.Default.Send<CloseWindowMessage>(new CloseWindowMessage()); }, () => IsFormFilled ); } return _saveExpenseCommand; } }Naciśnij F5, aby skompilować i uruchomić aplikację w debugerze. Wybierz pracownika z listy, a następnie kliknij przycisk Dodaj nowy wydatek . Wypełnij wszystkie pola w formularzu i naciśnij Zapisz.
Otrzymasz następujący wyjątek.
Ten wyjątek jest spowodowany faktem, że aplikacja Contoso Expenses nie ma jeszcze tożsamości pakietu. Niektóre interfejsy API winRT, w tym interfejs API powiadomień, wymagają tożsamości pakietu, zanim będą mogły być używane w aplikacji. Aplikacje uniwersalne systemu Windows mają domyślnie nadaną tożsamość pakietu, ponieważ mogą być dystrybuowane tylko za pośrednictwem pakietów MSIX. Inne typy aplikacji systemu Windows, w tym aplikacje WPF, można również wdrażać za pośrednictwem pakietów MSIX w celu uzyskania tożsamości pakietu. W następnej części tego samouczka dowiesz się, jak to zrobić.
Dalsze kroki
Na tym etapie samouczka pomyślnie dodano aktywność użytkownika do aplikacji, która integruje się z osią czasu systemu Windows, i dodano również powiadomienie do aplikacji, które jest wyzwalane, gdy użytkownicy tworzą nowy wydatek. Jednak powiadomienie nie działa jeszcze, ponieważ aplikacja wymaga tożsamości pakietu do korzystania z interfejsu API powiadomień. Aby dowiedzieć się, jak utworzyć pakiet MSIX dla aplikacji, aby uzyskać tożsamość pakietu i uzyskać inne korzyści z wdrożenia, zobacz Część 5: Pakowanie i wdrażanie za pomocą pliku MSIX.
Windows developer