Nota
O acesso a esta página requer autorização. Podes tentar iniciar sessão ou mudar de diretório.
O acesso a esta página requer autorização. Podes tentar mudar de diretório.
Esta é a quarta 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 3.
Nas partes anteriores deste tutorial, você adicionou controles UWP XAML ao aplicativo usando Ilhas XAML. Como um subproduto disso, você também habilitou o aplicativo para chamar qualquer API do WinRT. Isso abre a oportunidade para o aplicativo usar muitos outros recursos oferecidos pelo Windows, não apenas controles UWP XAML.
No cenário fictício deste tutorial, a equipe de desenvolvimento da Contoso decidiu adicionar dois novos recursos ao aplicativo: atividades e notificações. Esta parte do tutorial mostra como implementar esses recursos.
Adicionar uma atividade de utilizador
Observação
O recurso de linha do tempo foi descontinuado desde o Windows 11
No Windows 10, os aplicativos podem rastrear atividades realizadas pelo usuário, como abrir um arquivo ou exibir uma página específica. Essas atividades são então disponibilizadas por meio da Linha do Tempo, um recurso introduzido no Windows 10 versão 1803, que permite ao usuário voltar rapidamente ao passado e retomar uma atividade iniciada anteriormente.
As atividades do usuário são rastreadas usando Microsoft Graph. No entanto, ao criar um aplicativo do Windows 10, você não precisa interagir diretamente com os pontos de extremidade REST fornecidos pelo Microsoft Graph. Em vez disso, você pode usar um conjunto conveniente de APIs do WinRT. Vamos usar essas APIs do WinRT no aplicativo Contoso Expenses para acompanhar toda vez que o usuário abrir uma despesa no aplicativo e usar Cartões Adaptáveis para permitir que os usuários criem a atividade.
Introdução aos Cartões Adaptáveis
Esta secção fornece uma breve visão geral do Adaptive Cards. Se você não precisar dessas informações, pode ignorá-las e ir direto para a adicionar um cartão adaptável instruções.
Os Adaptive Cards permitem que os desenvolvedores troquem o conteúdo do cartão de forma comum e consistente. Um cartão adaptável é descrito por uma carga JSON que define seu conteúdo, que pode incluir texto, imagens, ações e muito mais.
Um cartão adaptável define apenas o conteúdo e não a aparência visual do conteúdo. A plataforma onde o Adaptive Card é recebido pode renderizar o conteúdo usando o estilo mais apropriado. A forma como os cartões adaptáveis são desenhados é através um renderizador, que é capaz de pegar a carga JSON e convertê-la em interface de utilizador nativa. Por exemplo, a interface do usuário pode ser XAML para um aplicativo WPF ou UWP, AXML para um aplicativo Android ou HTML para um site ou um bate-papo de bot.
Aqui está um exemplo de uma carga útil simples do Adaptive Card.
{
"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"
}
A imagem abaixo mostra como esse JSON é renderizado de diferentes maneiras pelo canal ta Teams, Cortana e uma notificação do Windows.
Os cartões adaptáveis desempenham um papel importante na Linha do tempo porque é a forma como o Windows renderiza as atividades. Cada miniatura exibida dentro da Linha do tempo é, na verdade, um cartão adaptável. Como tal, quando for criar uma atividade de utilizador dentro da sua aplicação, ser-lhe-á pedido que forneça um Cartão Adaptável para a renderizar.
Observação
Uma ótima maneira de conceber o design de um Adaptive Card é usar o designer online. Você terá a chance de projetar o cartão com blocos de construção (imagens, textos, colunas, etc) e obter o JSON correspondente. Depois de ter uma ideia do design final, você pode usar uma biblioteca chamada Adaptive Cards para facilitar a criação do seu Adaptive Card usando classes C# em vez de JSON simples, o que pode ser difícil de depurar e compilar.
Adicionar um cartão adaptável
Clique com o botão direito do mouse no projeto
ContosoExpenses.Core no Gerenciador de Soluções e escolha Gerenciar pacotes NuGet .Na janela Gestor de Pacotes NuGet, clique em Procurar. Procure o pacote
Newtonsoft.Jsone instale a versão mais recente disponível. Esta é uma biblioteca de manipulação JSON popular que você usará para ajudar a manipular as cadeias de caracteres JSON exigidas pelos Adaptive Cards.
Observação
Se você não instalar o pacote
Newtonsoft.Jsonseparadamente, a biblioteca Adaptive Cards fará referência a uma versão mais antiga do pacoteNewtonsoft.Jsonque não suporta o .NET Core 3.0.Na janela Gestor de Pacotes NuGet, clique em Procurar. Procure o pacote
AdaptiveCardse instale a versão mais recente disponível.
No Explorador de Soluções , clique com o botão direito do rato no projeto ContosoExpenses.Core, escolha Adicionar -> Classe. Nomeie a classe TimelineService.cs e clique em OK.
No arquivo TimelineService.cs, adicione as seguintes instruções à parte superior do arquivo.
using AdaptiveCards; using ContosoExpenses.Data.Models;Altere o namespace declarado no arquivo de
ContosoExpenses.CoreparaContosoExpenses.Adicione o seguinte método à classe
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; }
Sobre o código
Esse método recebe um objeto Expense com todas as informações sobre a despesa a ser renderizada e cria um novo objeto AdaptiveCard. O método adiciona o seguinte ao cartão:
- Um título, que usa a descrição da despesa.
- Uma imagem, que é o logotipo da Contoso.
- O valor da despesa.
- A data da despesa.
Os últimos 3 elementos são divididos em duas colunas diferentes, para que o logotipo da Contoso e os detalhes sobre a despesa possam ser colocados lado a lado. Depois que o objeto é criado, o método retorna a cadeia de caracteres JSON correspondente com a ajuda do ToJson método.
Definir a atividade do usuário
Agora que você definiu o Adaptive Card, pode criar uma atividade de usuário com base nele.
Adicione as seguintes declarações à parte superior do arquivo TimelineService.cs:
using Windows.ApplicationModel.UserActivities; using System.Threading.Tasks; using Windows.UI.Shell;Observação
Estes são namespaces UWP. Isso é resolvido porque o pacote NuGet
Microsoft.Toolkit.Wpf.UI.Controlsque você instalou na etapa 2 inclui uma referência ao pacoteMicrosoft.Windows.SDK.Contracts, que permite que o projeto ContosoExpenses.Core faça referência a APIs do WinRT mesmo que seja um projeto .NET Core 3.Adicione as seguintes declarações de campo à classe
TimelineService.private UserActivityChannel _userActivityChannel; private UserActivity _userActivity; private UserActivitySession _userActivitySession;Adicione o seguinte método à classe
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(); }Salve as alterações em TimelineService.cs.
Sobre o código
O método AddToTimeline primeiro obtém um objeto UserActivityChannel que é necessário para armazenar atividades do usuário. Em seguida, ele cria uma nova atividade do usuário usando o método GetOrCreateUserActivityAsync, que requer um identificador exclusivo. Desta forma, se já existir uma atividade, a aplicação pode atualizá-la; caso contrário, criará um novo. O identificador a ser transmitido depende do tipo de aplicativo que você está criando:
- Se quiser atualizar sempre a mesma atividade para que a Linha do tempo mostre apenas a mais recente, você pode usar um identificador fixo (como Despesas).
- Se você quiser acompanhar cada atividade como uma diferente, para que a Linha do tempo exiba todas elas, você pode usar um identificador dinâmico.
Nesse cenário, o aplicativo rastreará cada despesa aberta como uma atividade de usuário diferente, de modo que o código cria cada identificador usando a palavra-chave Expense- seguida pela ID de despesa exclusiva.
Depois que o método cria um objeto UserActivity, ele preenche o objeto com as seguintes informações:
- Um ActivationUri que é invocado quando o utilizador clica na atividade na Linha do Tempo. O código usa um protocolo personalizado chamado contosoexpenses que o aplicativo manipulará mais tarde.
- O VisualElements objeto, que contém um conjunto de propriedades que definem a aparência visual da atividade. Este código define o DisplayText (que é o título exibido no topo da entrada na Linha do Tempo) e o Conteúdo.
É aqui que o Adaptive Card definido anteriormente desempenha um papel. A aplicação passa o Adaptive Card que desenhaste anteriormente como conteúdo para o método. No entanto, o Windows 10 usa um objeto diferente para representar um cartão em comparação com o usado pelo pacote NuGet AdaptiveCards. Portanto, o método recria o cartão usando o CreateAdaptiveCardFromJson método exposto pelo AdaptiveCardBuilder classe. Depois que o método cria a atividade do usuário, ele salva a atividade e cria uma nova sessão.
Quando um usuário clica em uma atividade na Linha do tempo, o protocolo contosoexpenses:// será ativado e a URL incluirá as informações de que o aplicativo precisa para recuperar a despesa selecionada. Como uma tarefa opcional, você pode implementar a ativação do protocolo para que o aplicativo reaja corretamente quando o usuário usa a Linha do tempo.
Integre a aplicação com a Linha Temporal
Agora que você criou uma classe que interage com a Linha do tempo, podemos começar a usá-la para aprimorar a experiência do aplicativo. O melhor lugar para usar o método
No projeto ContosoExpenses.Core, expanda a pasta ViewModels e abra o arquivo ExpenseDetailViewModel.cs. Este é o ViewModel que suporta a janela de detalhes da despesa.
Localize o construtor público da classe ExpenseDetailViewModel e adicione o seguinte código no final do construtor. Sempre que a janela de despesas é aberta, o método chama o AddToTimeline método e passa a despesa atual. A classe
TimelineService usa essas informações para criar uma atividade do usuário usando as informações de despesa. TimelineService timeline = new TimelineService(); timeline.AddToTimeline(expense);Quando terminar, o construtor deve ter esta aparência.
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); }Pressione F5 para criar e executar o aplicativo no depurador. Escolha um funcionário da lista e, em seguida, escolha uma despesa. Na página de detalhes, anote a descrição da despesa, a data e o valor.
Pressione Iniciar + TAB para abrir a Linha do tempo.
Role para baixo a lista de aplicações atualmente abertas até ver a seção intitulada Hoje, mais cedo. Esta seção mostra algumas de suas atividades de usuário mais recentes. Clique no link Ver todas as atividades ao lado do cabeçalho Mais cedo hoje.
Confirme que vê um novo cartão com a informação sobre a despesa que acabou de selecionar na aplicação.
Se você abrir outras despesas, verá novos cartões sendo adicionados como atividades do usuário. Lembre-se de que o código usa um identificador diferente para cada atividade, por isso cria um cartão para cada despesa que você abre no aplicativo.
Feche o aplicativo.
Adicionar uma notificação
O segundo recurso que a equipe de desenvolvimento da Contoso deseja adicionar é uma notificação que é mostrada ao usuário sempre que uma nova despesa é salva no banco de dados. Para fazer isso, você pode aproveitar o sistema de notificações interno no Windows 10, que é exposto aos desenvolvedores por meio de APIs do WinRT. Este sistema de notificação tem muitas vantagens:
- As notificações são consistentes com o resto do SO.
- Eles são acionáveis.
- Eles são armazenados na Central de Ações para que possam ser revisados posteriormente.
Para adicionar uma notificação ao aplicativo:
No Explorador de Soluções , clique com o botão direito do rato no projeto ContosoExpenses.Core, escolha Adicionar -> Classe. Nomeie a classe NotificationService.cs e clique em OK.
No arquivo NotificationService.cs, adicione as seguintes instruções à parte superior do arquivo.
using Windows.Data.Xml.Dom; using Windows.UI.Notifications;Altere o namespace declarado no arquivo de
ContosoExpenses.CoreparaContosoExpenses.Adicione o seguinte método à classe
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); }As notificações Toast são representadas por um conteúdo XML, que pode incluir texto, imagens, ações e muito mais. Você pode encontrar todos os elementos suportados aqui. Este código usa um esquema muito simples com duas linhas de texto: o título e o corpo. Depois que o código define a carga XML e a carrega em um objeto XmlDocument , ele encapsula o XML em um objeto ToastNotification e o mostra usando a classe ToastNotificationManager .
No projeto
ContosoExpenses.Core, expanda a pasta ViewModels e abra o arquivoAddNewExpenseViewModel.cs .Localize o método
SaveExpenseCommand, que é acionado quando o usuário pressiona o botão para salvar uma nova despesa. Adicione o seguinte código a esse método, logo após a chamada para o métodoSaveExpense.NotificationService notificationService = new NotificationService(); notificationService.ShowNotification(expense.Description, expense.Cost);Quando terminar, o método
SaveExpenseCommanddeverá ter esta aparência.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; } }Pressione F5 para criar e executar o aplicativo no depurador. Escolha um funcionário na lista e clique no botão Adicionar nova despesa. Preencha todos os campos do formulário e pressione Salvar.
Você receberá a seguinte exceção.
Essa exceção é causada pelo fato de que o aplicativo Contoso Expenses ainda não tem identidade de pacote. Algumas APIs do WinRT, incluindo a API de notificações, exigem a identidade do pacote antes de poderem ser usadas em um aplicativo. Os aplicativos UWP recebem identidade de pacote por padrão porque só podem ser distribuídos por meio de pacotes MSIX. Outros tipos de aplicativos do Windows, incluindo aplicativos WPF, também podem ser implantados por meio de pacotes MSIX para obter a identidade do pacote. A próxima parte deste tutorial explorará como fazer isso.
Próximos passos
Neste ponto do tutorial, você adicionou com êxito uma atividade de usuário ao aplicativo que se integra à Linha do Tempo do Windows e também adicionou uma notificação ao aplicativo que é acionada quando os usuários criam uma nova despesa. No entanto, a notificação ainda não funciona porque o aplicativo requer a identidade do pacote para usar a API de notificações. Para saber como criar um pacote MSIX para a aplicação a fim de obter identidade de pacote e outros benefícios de implementação, consulte Parte 5: Empacotar e implantar com o MSIX.
Windows developer