Sdílet prostřednictvím


Část 4: Přidání uživatelských aktivit a oznámení systému Windows

Toto je čtvrtá část kurzu, která ukazuje, jak modernizovat ukázkovou desktopovou aplikaci WPF s názvem Contoso Expenses. Přehled kurzu, předpokladů a pokynů ke stažení ukázkové aplikace najdete v tématu Kurz: Modernizace aplikace WPF. Tento článek předpokládá, že jste už dokončili část 3.

V předchozích částech tohoto kurzu jste do aplikace přidali ovládací prvky UPW XAML pomocí ostrůvků XAML. Jako vedlejší produkt jste také povolili aplikaci, aby volala jakékoli rozhraní API WinRT. Tím se otevře příležitost, aby aplikace používala mnoho dalších funkcí, které nabízí Windows, nejen ovládací prvky XAML pro UPW.

Ve fiktivním scénáři tohoto kurzu se vývojový tým Společnosti Contoso rozhodl přidat do aplikace dvě nové funkce: aktivity a oznámení. Tato část kurzu ukazuje, jak tyto funkce implementovat.

Přidání aktivity uživatele

Poznámka:

Funkce časové osy byla od Windows 11 ukončena.

Ve Windows 10 můžou aplikace sledovat aktivity prováděné uživatelem, jako je otevření souboru nebo zobrazení konkrétní stránky. Tyto aktivity jsou pak zpřístupněny prostřednictvím časové osy, což je funkce zavedená ve Windows 10 verze 1803, která uživateli umožňuje rychle se vrátit k minulosti a obnovit aktivitu, kterou spustil dříve.

obrázek časové osy Windows

Aktivity uživatelů se sledují pomocí Microsoft Graphu. Při vytváření aplikace pro Windows 10 ale nemusíte interagovat přímo s koncovými body REST, které poskytuje Microsoft Graph. Místo toho můžete použít pohodlnou sadu rozhraní API WinRT. Tato rozhraní API WinRT použijeme v aplikaci Contoso Expenses ke sledování pokaždé, když uživatel otevře výdaje v aplikaci, a pomocí adaptivních karet umožníme uživatelům vytvářet aktivitu.

Úvod do adaptivních karet

Tato část obsahuje stručný přehled adaptivních karet. Pokud tyto informace nepotřebujete, můžete tuto část přeskočit a přejít rovnou na pokyny pro přidání adaptivní karty.

Adaptivní karty umožňují vývojářům vyměňovat obsah karet běžným a konzistentním způsobem. Adaptivní karta je popsaná datovou částí JSON, která definuje jeho obsah, který může zahrnovat text, obrázky, akce a další.

Adaptivní karta definuje jenom obsah a ne vizuální vzhled obsahu. Platforma, kde se přijímá adaptivní karta, může vykreslit obsah pomocí nejvhodnějšího stylu. Způsob, jakým jsou adaptivní karty navrženy, je prostřednictvím vykreslovacího, který dokáže převzít datovou část JSON a převést ji na nativní uživatelské rozhraní. Uživatelské rozhraní může být například XAML pro aplikaci WPF nebo UPW, AXML pro aplikaci pro Android nebo HTML pro web nebo chat robota.

Tady je příklad jednoduché datové části adaptivní karty.

{
    "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"
}

Následující obrázek ukazuje, jak se tento JSON vykresluje různými způsoby kanálem Teams, Cortanou a oznámením windows.

vykreslování adaptivní karty obrázku

Adaptivní karty hrají důležitou roli na časové ose, protože se jedná o způsob, jakým Windows vykresluje aktivity. Každá miniatura zobrazená uvnitř časové osy je ve skutečnosti adaptivní kartou. Když tedy v aplikaci vytvoříte aktivitu uživatele, zobrazí se výzva k poskytnutí adaptivní karty, která ji vykreslí.

Poznámka:

Skvělý způsob, jak promyslet návrh adaptivní karty, je používat online návrhář. Budete mít možnost navrhnout kartu stavebními bloky (obrázky, texty, sloupce atd.) a získat odpovídající JSON. Jakmile budete mít představu o konečném návrhu, můžete použít knihovnu s názvem Adaptivní karty , která usnadňuje sestavení adaptivní karty pomocí tříd jazyka C# místo prostého formátu JSON, což může být obtížné ladit a sestavovat.

Přidání adaptivní karty

  1. V Průzkumníku řešení klikněte pravým tlačítkem na projekt ContosoExpenses.Core a zvolte Spravovat balíčky NuGet.

  2. V okně Správce balíčků NuGet klikněte na Procházet. Vyhledejte Newtonsoft.Json balíček a nainstalujte nejnovější dostupnou verzi. Jedná se o oblíbenou knihovnu pro manipulaci s JSON, kterou použijete k manipulaci s řetězci JSON vyžadovanými adaptivními kartami.

    balíček NuGet NewtonSoft.Json

    Poznámka:

    Pokud balíček nenainstalujete Newtonsoft.Json samostatně, bude knihovna Adaptivní karty odkazovat na starší verzi Newtonsoft.Json balíčku, která nepodporuje .NET Core 3.0.

  3. V okně Správce balíčků NuGet klikněte na Procházet. Vyhledejte AdaptiveCards balíček a nainstalujte nejnovější dostupnou verzi.

    balíčku NuGet adaptivních karet

  4. V Průzkumníku řešeníklikněte pravým tlačítkem na projekt ContosoExpenses.Core, zvolte Přidat –> třída. Pojmenujte třídu TimelineService.cs a klikněte na OK.

  5. Do souboru TimelineService.cs přidejte následující příkazy na začátek souboru.

    using AdaptiveCards;
    using ContosoExpenses.Data.Models;
    
  6. Změňte obor názvů deklarovaný v souboru z ContosoExpenses.Core na ContosoExpenses.

  7. Do třídy přidejte následující metodu TimelineService .

     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;
     }
    

O kódu

Tato metoda obdrží objekt Expense, obsahující všechny informace o výdajích, jež mají být vykresleny, a vytvoří nový objekt AdaptiveCard. Metoda přidá na kartu následující položky:

  • Název, který používá popis výdajů.
  • Obrázek, který je logem Contoso.
  • Částka výdajů.
  • Datum výdajů.

Poslední tři prvky jsou rozdělené do dvou různých sloupců, aby logo Contoso a podrobnosti o výdajích mohly být umístěny vedle sebe. Po sestavení objektu vrátí metoda odpovídající řetězec JSON pomocí ToJson metody.

Definování aktivity uživatele

Teď, když jste definovali adaptivní kartu, můžete na základě ní vytvořit aktivitu uživatele.

  1. Na začátek souboru TimelineService.cs přidejte následující příkazy:

    using Windows.ApplicationModel.UserActivities;
    using System.Threading.Tasks;
    using Windows.UI.Shell;
    

    Poznámka:

    Jedná se o obory názvů UWP. Tyto problémy se vyřeší, protože Microsoft.Toolkit.Wpf.UI.Controls balíček NuGet, který jste nainstalovali v kroku 2, obsahuje odkaz na Microsoft.Windows.SDK.Contracts balíček, který umožňuje projektu ContosoExpenses.Core odkazovat na rozhraní API WinRT, i když se jedná o projekt .NET Core 3.

  2. Do třídy přidejte následující deklarace TimelineService polí.

    private UserActivityChannel _userActivityChannel;
    private UserActivity _userActivity;
    private UserActivitySession _userActivitySession;
    
  3. Do třídy přidejte následující metodu TimelineService .

    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();
    }
    
  4. Uložte změny do TimelineService.cs.

O kódu

Metoda AddToTimeline nejprve získá UserActivityChannel objekt, který je nutný k ukládání aktivit uživatele. Pak vytvoří novou aktivitu uživatele pomocí metody GetOrCreateUserActivityAsync , která vyžaduje jedinečný identifikátor. Tímto způsobem, pokud aktivita již existuje, může ji aplikace aktualizovat; jinak vytvoří nový. Identifikátor, který se má předat, závisí na typu aplikace, kterou vytváříte:

  • Pokud chcete vždy aktualizovat stejnou aktivitu tak, aby časová osa zobrazovala jenom nejnovější aktivitu, můžete použít pevný identifikátor (například Výdaje).
  • Pokud chcete sledovat každou aktivitu jako jinou, aby se všechny z nich zobrazovaly, můžete použít dynamický identifikátor.

V tomto scénáři bude aplikace sledovat jednotlivé otevřené výdaje jako jinou aktivitu uživatele, takže kód vytvoří každý identifikátor pomocí klíčového slova Expense- následované jedinečným ID výdajů.

Jakmile metoda vytvoří objekt UserActivity , naplní objekt následujícími informacemi:

  • Aktivační identifikátor , který se vyvolá, když uživatel klikne na aktivitu na časové ose. Kód používá vlastní protokol s názvem contosoexpenses, který aplikace bude zpracovávat později.
  • VisualElements objekt, který obsahuje sadu vlastností, které definují vizuální vzhled aktivity. Tento kód nastaví DisplayText (což je název zobrazený v horní části položky na časové ose) a Obsah.

Tady hraje roli adaptivní karta, kterou jste definovali dříve. Aplikace předá adaptivní kartu, kterou jste navrhli dříve, jako obsah do metody. Windows 10 však používá jiný objekt k reprezentaci karty v porovnání s objektem používaným balíčkem AdaptiveCards NuGet. Proto metoda vytvoří kartu pomocí metody CreateAdaptiveCardFromJson, vystavené třídou AdaptiveCardBuilder. Jakmile metoda vytvoří aktivitu uživatele, uloží aktivitu a vytvoří novou relaci.

Když uživatel klikne na aktivitu na časové ose, aktivuje se protokol contosoexpenses:// a adresa URL bude obsahovat informace, které aplikace potřebuje k načtení vybraných výdajů. Jako volitelný úkol můžete implementovat aktivaci protokolu, aby aplikace správně reagovala, když uživatel používá časovou osu.

Integrace aplikace s časovou osou

Teď, když jste vytvořili třídu, která komunikuje s časovou osou, ji můžeme začít používat k vylepšení prostředí aplikace. Nejlepším místem pro použití metody AddToTimeline, kterou vystavuje třída TimelineService, je, když uživatel otevře stránku s podrobnostmi o výdaji.

  1. V projektu ContosoExpenses.Core rozbalte složku ViewModels a otevřete soubor ExpenseDetailViewModel.cs . Toto je model ViewModel, který podporuje okno podrobností o výdajích.

  2. Vyhledejte veřejný konstruktor ExpenseDetailViewModel třídy a na konec konstruktoru přidejte následující kód. Při každém otevření okna nákladů volá metoda AddToTimeline a předá aktuální náklady. Třída TimelineService používá tyto informace k vytvoření aktivity uživatele pomocí informací o výdajích.

    TimelineService timeline = new TimelineService();
    timeline.AddToTimeline(expense);
    

    Až budete hotovi, měl by konstruktor vypadat takto.

    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);
    }
    
  3. Stisknutím klávesy F5 sestavte a spusťte aplikaci v debuggeru. Vyberte zaměstnance ze seznamu a pak zvolte výdaje. Na stránce podrobností si poznamenejte popis výdajů, data a částky.

  4. Stisknutím klávesy Start + TAB otevřete časovou osu.

  5. Posuňte se dolů po seznamu aktuálně otevřených aplikací, dokud neuvidíte oddíl nazvaný Dříve dnes. Tato část ukazuje některé z vašich nejnovějších aktivit uživatelů. Klikněte na odkaz Zobrazit všechny aktivity vedle nadpisu Dnes dříve.

  6. Potvrďte, že se zobrazí nová karta s informacemi o výdajích, které jste právě vybrali v aplikaci.

    Časová osa výdajů Contoso

  7. Pokud teď otevřete další výdaje, uvidíte, že se jako uživatelské aktivity přidávají nové karty. Nezapomeňte, že kód používá pro každou aktivitu jiný identifikátor, takže vytvoří kartu pro všechny výdaje, které otevřete v aplikaci.

  8. Zavřete aplikaci.

Přidání oznámení

Druhou funkcí, kterou chce vývojový tým Společnosti Contoso přidat, je oznámení, které se uživateli zobrazí při každém uložení nových výdajů do databáze. K tomu můžete využít integrovaný systém oznámení ve Windows 10, který je přístupný vývojářům prostřednictvím rozhraní API WinRT. Tento systém oznámení má mnoho výhod:

  • Oznámení jsou konzistentní se zbytkem operačního systému.
  • Jsou realizovatelné.
  • Ukládají se v Centru akcí, aby je bylo možné později zkontrolovat.

Přidání oznámení do aplikace:

  1. V Průzkumníku řešeníklikněte pravým tlačítkem na projekt ContosoExpenses.Core, zvolte Přidat –> třída. Pojmenujte třídu NotificationService.cs a klikněte na OK.

  2. Do souboru NotificationService.cs přidejte následující příkazy na začátek souboru.

    using Windows.Data.Xml.Dom;
    using Windows.UI.Notifications;
    
  3. Změňte obor názvů deklarovaný v souboru z ContosoExpenses.Core na ContosoExpenses.

  4. Do třídy přidejte následující metodu NotificationService .

    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);
    }
    

    Informační zprávy jsou reprezentovány datovou částí XML, která může obsahovat text, obrázky, akce a další. Všechny podporované prvky najdete tady. Tento kód používá velmi jednoduché schéma se dvěma řádky textu: nadpisem a textem. Jakmile kód definuje datovou část XML a načte ji do objektu XmlDocument , zabalí XML do objektu ToastNotification a zobrazí ji pomocí ToastNotificationManager třídy.

  5. V projektu ContosoExpenses.Core rozbalte složku ViewModels a otevřete soubor AddNewExpenseViewModel.cs .

  6. Vyhledejte metodu SaveExpenseCommand , která se aktivuje, když uživatel stiskne tlačítko, aby se ušetřily nové výdaje. Do této metody přidejte následující kód hned za volání metody SaveExpense .

    NotificationService notificationService = new NotificationService();
    notificationService.ShowNotification(expense.Description, expense.Cost);
    

    Až budete hotovi, metoda SaveExpenseCommand by měla vypadat takto.

    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;
        }
    }
    
  7. Stisknutím klávesy F5 sestavte a spusťte aplikaci v debuggeru. Ze seznamu vyberte zaměstnance a klikněte na tlačítko Přidat nový výdaj . Vyplňte všechna pole ve formuláři a stiskněte Uložit.

  8. Zobrazí se následující výjimka.

    Chyba vyskakovacího oznámení

Tato výjimka je způsobená skutečností, že aplikace Contoso Expenses ještě nemá identitu balíčku. Některá rozhraní API WinRT, včetně rozhraní API pro oznámení, vyžadují identitu balíčku, než se dají použít v aplikaci. Aplikace pro UPW ve výchozím nastavení přijímají identitu balíčku, protože je možné je distribuovat pouze prostřednictvím balíčků MSIX. Další typy aplikací pro Windows, včetně aplikací WPF, je možné nasadit také prostřednictvím balíčků MSIX k získání identity balíčku. V další části tohoto kurzu se dozvíte, jak to udělat.

Další kroky

V tomto okamžiku kurzu jste úspěšně přidali aktivitu uživatele do aplikace, která se integruje s časovou osou Windows, a také jste do aplikace, která se aktivuje při vytváření nových výdajů, přidali oznámení. Oznámení ale ještě nefunguje, protože aplikace k používání rozhraní API pro oznámení vyžaduje identitu balíčku. Informace o vytvoření balíčku MSIX pro aplikaci za účelem získání identity balíčku a získání dalších výhod nasazení najdete v části 5: Zabalení a nasazení pomocí MSIX.