Crie aplicativos Xamarin com o Microsoft Graph
Este tutorial ensina como criar um aplicativo Xamarin que usa a API do Microsoft Graph para recuperar informações de calendário para um usuário.
Dica
Se você preferir apenas baixar o tutorial concluído, poderá baixar ou clonar o GitHub repositório.
Pré-requisitos
Antes de iniciar este tutorial, você deve ter Visual Studio instalado em um computador executando Windows 10 com o modo desenvolvedor ligado. Se você não tiver Visual Studio, visite o link anterior para opções de download.
Você também deve ter o Xamarin instalado como parte da instalação Visual Studio usuário. Consulte Installing Xamarin for instructions on installing and configuring Xamarin.
Opcionalmente, você também deve ter acesso a um Mac com Visual Studio para Mac instalado. Se você não tiver acesso, ainda poderá concluir esse tutorial, mas não poderá concluir as seções específicas do iOS.
Você também deve ter uma conta pessoal da Microsoft com uma caixa de correio em Outlook.com, ou uma conta de trabalho ou de estudante da Microsoft. Se você não tiver uma conta da Microsoft, há algumas opções para obter uma conta gratuita:
- Você pode se inscrever em uma nova conta pessoal da Microsoft.
- Você pode se inscrever no programa Microsoft 365 desenvolvedor para obter uma assinatura Microsoft 365 gratuita.
Observação
Este tutorial foi escrito com Visual Studio versão 16.10.3 de 2019 e Visual Studio para Mac versão 8.5.1. Ambos os dispositivos têm a Plataforma SDK do Android 28 instalada. As etapas neste guia podem funcionar com outras versões, mas que não foram testadas.
Comentários
Forneça qualquer comentário sobre este tutorial no repositório GitHub.
Crie um aplicativo Xamarin
Abra o Visual Studio e selecione Criar um novo projeto.
Na caixa de diálogo Criar um novo projeto , escolha Aplicativo Móvel (Xamarin.Forms) e selecione Próximo.
Na caixa de diálogo Configurar um novo projeto, digite
GraphTutorial
o nome Project nome da solução e selecione Criar.Importante
Certifique-se de inserir exatamente o mesmo nome para o Visual Studio Project especificado nessas instruções de laboratório. O nome do projeto Visual Studio torna-se parte do namespace no código. O código dentro dessas instruções depende do namespace correspondente ao nome do projeto Visual Studio especificado nessas instruções. Se você usar um nome de projeto diferente, o código não será compilado, a menos que você ajuste todos os namespaces para corresponder ao nome do projeto do Visual Studio inserido ao criar o projeto.
Na caixa de diálogo Novo Aplicativo de Plataforma Cruzada, selecione o modelo em branco e selecione as plataformas que você deseja criar em Plataformas. Selecione OK para criar a solução.
Instalar pacotes
Antes de continuar, instale alguns pacotes NuGet que você usará posteriormente.
- Microsoft.Identity.Client para manipular a autenticação e o gerenciamento de tokens do Azure AD.
- Microsoft. Graph para fazer chamadas para o microsoft Graph.
- TimeZoneConverter para manipular fusos horário entre plataformas.
Selecione Ferramentas> Gerenciador de pacotes NuGet> Console do gerenciador de pacotes.
No Console do Gerenciador de Pacotes, digite os seguintes comandos.
Install-Package Microsoft.Identity.Client -Version 4.35.1 -Project GraphTutorial Install-Package Microsoft.Identity.Client -Version 4.35.1 -Project GraphTutorial.Android Install-Package Microsoft.Identity.Client -Version 4.35.1 -Project GraphTutorial.iOS Install-Package Microsoft.Graph -Version 4.1.0 -Project GraphTutorial Install-Package TimeZoneConverter -Project GraphTutorial
Design do aplicativo
Comece atualizando a classe para App
adicionar variáveis para rastrear o estado de autenticação e o usuário in-locar.
No Explorador de Soluções, expanda o projeto GraphTutorial e expanda o arquivo App.xaml . Abra o arquivo App.xaml.cs e adicione as instruções
using
a seguir à parte superior do arquivo.using System.ComponentModel; using System.IO; using System.Reflection; using System.Threading.Tasks;
Adicione a
INotifyPropertyChanged
interface à declaração de classe.public partial class App : Application, INotifyPropertyChanged
Adicione as seguintes propriedades à
App
classe.// Is a user signed in? private bool isSignedIn; public bool IsSignedIn { get { return isSignedIn; } set { isSignedIn = value; OnPropertyChanged("IsSignedIn"); OnPropertyChanged("IsSignedOut"); } } public bool IsSignedOut { get { return !isSignedIn; } } // The user's display name private string userName; public string UserName { get { return userName; } set { userName = value; OnPropertyChanged("UserName"); } } // The user's email address private string userEmail; public string UserEmail { get { return userEmail; } set { userEmail = value; OnPropertyChanged("UserEmail"); } } // The user's profile photo private ImageSource userPhoto; public ImageSource UserPhoto { get { return userPhoto; } set { userPhoto = value; OnPropertyChanged("UserPhoto"); } } // The user's time zone public static TimeZoneInfo UserTimeZone;
Adicione as seguintes funções à
App
classe. AsSignIn
funções ,SignOut
eGetUserInfo
são apenas espaço reservados por enquanto.public async Task SignIn() { await GetUserInfo(); IsSignedIn = true; } public async Task SignOut() { UserPhoto = null; UserName = string.Empty; UserEmail = string.Empty; IsSignedIn = false; } private async Task GetUserInfo() { UserPhoto = ImageSource.FromStream(() => GetUserPhoto()); UserName = "Adele Vance"; UserEmail = "adelev@contoso.com"; } private Stream GetUserPhoto() { // Return the default photo return Assembly.GetExecutingAssembly().GetManifestResourceStream("GraphTutorial.no-profile-pic.png"); }
A
GetUserPhoto
função retorna uma foto padrão por enquanto. Você pode fornecer seu próprio arquivo ou baixar o usado no exemplo de GitHub. Se você usar seu próprio arquivo, renomeie-o parano-profile-pic.png.Copie o arquivo para o diretório ./GraphTutorial/GraphTutorial .
Clique com o botão direito do mouse no arquivo no Explorador de Soluções e selecione Propriedades. Na janela Propriedades , altere o valor da Ação de Com build para o recurso Embedded.
Navegação de aplicativo
Nesta seção, você alterará a página principal do aplicativo para um FlyoutPage. Isso fornecerá um menu de navegação para alternar entre o modo de exibição no aplicativo.
Abra o arquivo MainPage.xaml no projeto GraphTutorial e substitua seu conteúdo pelo seguinte.
<!-- Copyright (c) Microsoft Corporation. Licensed under the MIT License. --> <!-- <MainPageXamlSnippet> --> <?xml version="1.0" encoding="utf-8" ?> <MasterDetailPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:local="clr-namespace:GraphTutorial" x:Class="GraphTutorial.MainPage"> <MasterDetailPage.Master> <local:MenuPage/> </MasterDetailPage.Master> <MasterDetailPage.Detail> <NavigationPage> <x:Arguments> <local:WelcomePage/> </x:Arguments> </NavigationPage> </MasterDetailPage.Detail> </MasterDetailPage> <!-- </MainPageXamlSnippet> -->
Implementar o menu
Clique com o botão direito do mouse no projeto GraphTutorial e selecione Adicionar, em seguida, Nova Pasta. Nomeia a pasta
Models
.Clique com o botão direito do mouse na pasta Modelos e selecione Adicionar, em seguida, Classe.... Nomeia a classe
NavMenuItem
e selecione Adicionar.Abra o arquivo NavMenuItem.cs e substitua seu conteúdo pelo seguinte.
namespace GraphTutorial.Models { public enum MenuItemType { Welcome, Calendar, NewEvent } public class NavMenuItem { public MenuItemType Id { get; set; } public string Title { get; set; } } }
Clique com o botão direito do mouse no projeto GraphTutorial e selecione Adicionar, em seguida, Novo Item.... Escolha Página de Conteúdo e nomee a página
MenuPage
. Selecione Adicionar.Abra o arquivo MenuPage.xaml e substitua seu conteúdo pelo seguinte.
<!-- Copyright (c) Microsoft Corporation. Licensed under the MIT License. --> <!-- <MenuPageXamlSnippet> --> <?xml version="1.0" encoding="utf-8" ?> <ContentPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:ios="clr-namespace:Xamarin.Forms.PlatformConfiguration.iOSSpecific;assembly=Xamarin.Forms.Core" ios:Page.UseSafeArea="true" Title="Menu" x:Class="GraphTutorial.MenuPage"> <ContentPage.Padding> <OnPlatform x:TypeArguments="Thickness"> <On Platform="UWP" Value="10, 10, 10, 10" /> </OnPlatform> </ContentPage.Padding> <ContentPage.Content> <StackLayout VerticalOptions="Start" HorizontalOptions="Center"> <StackLayout x:Name="UserArea" /> <!-- Signed out UI --> <StackLayout IsVisible="{Binding Path=IsSignedOut, Source={x:Static Application.Current}}"> <Label Text="Sign in to get started" HorizontalOptions="Center" FontAttributes="Bold" FontSize="Medium" Margin="10,20,10,20" /> <Button Text="Sign in" Clicked="OnSignIn" HorizontalOptions="Center" /> </StackLayout> <!-- Signed in UI --> <StackLayout IsVisible="{Binding Path=IsSignedIn, Source={x:Static Application.Current}}"> <Image Source="{Binding Path=UserPhoto, Source={x:Static Application.Current}}" HorizontalOptions="Center" Margin="0,20,0,10" /> <Label Text="{Binding Path=UserName, Source={x:Static Application.Current}}" HorizontalOptions="Center" FontAttributes="Bold" FontSize="Small" /> <Label Text="{Binding Path=UserEmail, Source={x:Static Application.Current}}" HorizontalOptions="Center" FontAttributes="Italic" /> <Button Text="Sign out" Margin="0,20,0,20" Clicked="OnSignOut" HorizontalOptions="Center" /> <ListView x:Name="ListViewMenu" HasUnevenRows="True" HorizontalOptions="Start"> <ListView.ItemTemplate> <DataTemplate> <ViewCell> <Grid Padding="10"> <Label Text="{Binding Title}" FontSize="20"/> </Grid> </ViewCell> </DataTemplate> </ListView.ItemTemplate> </ListView> </StackLayout> </StackLayout> </ContentPage.Content> </ContentPage> <!-- </MenuPageXamlSnippet> -->
Expanda MenuPage.xaml no Explorador de Soluções e abra o arquivo MenuPage.xaml.cs. Substitua seu conteúdo pelo seguinte.
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using Xamarin.Forms; using Xamarin.Forms.Xaml; using GraphTutorial.Models; namespace GraphTutorial { [XamlCompilation(XamlCompilationOptions.Compile)] public partial class MenuPage : ContentPage { MainPage RootPage => Application.Current.MainPage as MainPage; List<NavMenuItem> menuItems; public MenuPage() { InitializeComponent(); // Add items to the menu menuItems = new List<NavMenuItem> { new NavMenuItem {Id = MenuItemType.Welcome, Title="Home" }, new NavMenuItem {Id = MenuItemType.Calendar, Title="Calendar" }, new NavMenuItem {Id = MenuItemType.NewEvent, Title="New event" } }; ListViewMenu.ItemsSource = menuItems; // Initialize the selected item ListViewMenu.SelectedItem = menuItems[0]; // Handle the ItemSelected event to navigate to the // selected page ListViewMenu.ItemSelected += async (sender, e) => { if (e.SelectedItem == null) return; var id = (int)((NavMenuItem)e.SelectedItem).Id; await RootPage.NavigateFromMenu(id); }; } private async void OnSignOut(object sender, EventArgs e) { var signout = await DisplayAlert("Sign out?", "Do you want to sign out?", "Yes", "No"); if (signout) { await (Application.Current as App).SignOut(); } } private async void OnSignIn(object sender, EventArgs e) { try { await (Application.Current as App).SignIn(); } catch (Exception ex) { await DisplayAlert("Authentication Error", ex.Message, "OK"); } } } }
Observação
Visual Studio relatará erros em MenuPage.xaml.cs. Esses erros serão resolvidos em uma etapa posterior.
Implementar a página de boas-vindas
Clique com o botão direito do mouse no projeto GraphTutorial e selecione Adicionar, em seguida, Novo Item.... Escolha Página de Conteúdo e nomee a página
WelcomePage
. Selecione Adicionar. Abra o arquivo WelcomePage.xaml e substitua seu conteúdo pelo seguinte.<!-- Copyright (c) Microsoft Corporation. Licensed under the MIT License. --> <!-- <WelcomePageXamlSnippet> --> <?xml version="1.0" encoding="utf-8" ?> <ContentPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" Title="Home" x:Class="GraphTutorial.WelcomePage"> <ContentPage.Padding> <OnPlatform x:TypeArguments="Thickness"> <On Platform="UWP" Value="10, 10, 10, 10" /> </OnPlatform> </ContentPage.Padding> <ContentPage.Content> <StackLayout> <Label Text="Graph Xamarin Tutorial App" HorizontalOptions="Center" FontAttributes="Bold" FontSize="Large" Margin="10,20,10,20" /> <!-- Signed out UI --> <StackLayout IsVisible="{Binding Path=IsSignedOut, Source={x:Static Application.Current}}"> <Label Text="Please sign in to get started" HorizontalOptions="Center" FontSize="Medium" Margin="10,0,10,20"/> <Button Text="Sign in" HorizontalOptions="Center" Clicked="OnSignIn" /> </StackLayout> <!-- Signed in UI --> <StackLayout IsVisible="{Binding Path=IsSignedIn, Source={x:Static Application.Current}}"> <Label Text="{Binding Path=UserName, Source={x:Static Application.Current}, StringFormat='Welcome \{0\}!'}" HorizontalOptions="Center" FontSize="Medium"/> </StackLayout> </StackLayout> </ContentPage.Content> </ContentPage> <!-- </WelcomePageXamlSnippet> -->
Expanda WelcomePage.xaml no Explorador de Soluções e abra o arquivo WelcomePage.xaml.cs. Adicione a função a seguir à classe
WelcomePage
.private void OnSignIn(object sender, EventArgs e) { (App.Current.MainPage as MasterDetailPage).IsPresented = true; }
Adicionar calendário e novas páginas de eventos
Agora adicione uma página de calendário e uma nova página de evento. Eles serão apenas espaço reservados por enquanto.
Clique com o botão direito do mouse no projeto GraphTutorial e selecione Adicionar, em seguida, Novo Item.... Escolha Página de Conteúdo e nomee a página
CalendarPage
. Selecione Adicionar.Clique com o botão direito do mouse no projeto GraphTutorial e selecione Adicionar, em seguida, Novo Item.... Escolha Página de Conteúdo e nomee a página
NewEventPage
. Selecione Adicionar.
Atualizar code-behind mainPage
Agora que todas as páginas estão no local, atualize o code-behind para MainPage.xaml.
Expanda MainPage.xaml no Solution Explorer e abra o arquivo MainPage.xaml.cs e substitua todo o conteúdo pelo seguinte.
using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Reflection; using System.Text; using System.Threading.Tasks; using Xamarin.Forms; using GraphTutorial.Models; namespace GraphTutorial { public partial class MainPage : MasterDetailPage { Dictionary<int, NavigationPage> MenuPages = new Dictionary<int, NavigationPage>(); public MainPage() { InitializeComponent(); MasterBehavior = MasterBehavior.Popover; // Load the welcome page at start MenuPages.Add((int)MenuItemType.Welcome, (NavigationPage)Detail); } // Navigate to the selected page public async Task NavigateFromMenu(int id) { if (!MenuPages.ContainsKey(id)) { switch (id) { case (int)MenuItemType.Welcome: MenuPages.Add(id, new NavigationPage(new WelcomePage())); break; case (int)MenuItemType.Calendar: MenuPages.Add(id, new NavigationPage(new CalendarPage())); break; case (int)MenuItemType.NewEvent: MenuPages.Add(id, new NavigationPage(new NewEventPage())); break; } } var newPage = MenuPages[id]; if (newPage != null && Detail != newPage) { Detail = newPage; if (Device.RuntimePlatform == Device.Android) await Task.Delay(100); IsPresented = false; } } } }
Salve todas as alterações. Clique com o botão direito do mouse no projeto que você deseja executar (Android, iOS ou UWP) e selecione Definir como Iniciar Project. Pressione F5 ou selecione Depurar > Iniciar Depuração em Visual Studio.
Registrar o aplicativo no portal
Neste exercício, você criará um novo aplicativo nativo do Azure AD usando o Azure Active Directory de administração.
Abra um navegador, navegue até o centro de administração do Azure Active Directory e faça logon usando uma conta pessoal (também conhecida como conta da Microsoft) ou Conta Corporativa ou de Estudante.
Selecione Azure Active Directory na navegação esquerda e selecione Registros de aplicativos em Gerenciar.
Selecione Novo registro. Na página Registrar um aplicativo, defina os valores da seguinte forma.
- Defina Nome para
Xamarin Graph Tutorial
. - Defina Tipos de conta com suporte para Contas em qualquer diretório organizacional e contas pessoais da Microsoft.
- Em URI de redirecionamento (opcional), altere o menu suspenso para Cliente Público (área de trabalho móvel &) e de definir o valor como
msauth://com.companyname.GraphTutorial
.
- Defina Nome para
Selecione Registrar. Na página Tutorial Graph Xamarin, copie o valor da ID do Aplicativo (cliente) e salve-a, você precisará dela na próxima etapa.
Adicionar autenticação do Azure AD
Neste exercício, você estenderá o aplicativo do exercício anterior para dar suporte à autenticação com o Azure AD. Isso é necessário para obter o token de acesso OAuth necessário para chamar o microsoft Graph. Nesta etapa, você integrará a Biblioteca de Autenticação da Microsoft para .NET (MSAL) ao aplicativo.
No Explorador de Soluções, expanda o projeto GraphTutorial e clique com o botão direito do mouse na pasta Modelos. Selecione Adicionar > Classe.... Nomeia a
OAuthSettings
classe e selecione Adicionar.Abra o arquivo OAuthSettings.cs e substitua seu conteúdo pelo seguinte.
Substitua
YOUR_APP_ID_HERE
pela ID do aplicativo do registro do aplicativo.Importante
Se você estiver usando o controle de origem, como git, agora seria um bom momento para excluir o arquivo do controle de origem para evitar o vazamento
OAuthSettings.cs
inadvertida da ID do aplicativo.
Implementar login
Abra o arquivo App.xaml.cs no projeto GraphTutorial e adicione as instruções a seguir à
using
parte superior do arquivo.using GraphTutorial.Models; using Microsoft.Identity.Client; using Microsoft.Graph; using System.Diagnostics; using System.Linq; using System.Net.Http.Headers; using TimeZoneConverter;
Altere a linha de declaração da classe App para resolver o conflito de nomes para Application.
public partial class App : Xamarin.Forms.Application, INotifyPropertyChanged
Adicione as seguintes propriedades à
App
classe.// UIParent used by Android version of the app public static object AuthUIParent = null; // Keychain security group used by iOS version of the app public static string iOSKeychainSecurityGroup = null; // Microsoft Authentication client for native/mobile apps public static IPublicClientApplication PCA; // Microsoft Graph client public static GraphServiceClient GraphClient; // Microsoft Graph permissions used by app private readonly string[] Scopes = OAuthSettings.Scopes.Split(' ');
Em seguida, crie
PublicClientApplication
um novo no construtor daApp
classe.public App() { InitializeComponent(); var builder = PublicClientApplicationBuilder .Create(OAuthSettings.ApplicationId) .WithRedirectUri(OAuthSettings.RedirectUri); if (!string.IsNullOrEmpty(iOSKeychainSecurityGroup)) { builder = builder.WithIosKeychainSecurityGroup(iOSKeychainSecurityGroup); } PCA = builder.Build(); MainPage = new MainPage(); }
Atualize
SignIn
a função para usar o para obter um token dePublicClientApplication
acesso. Adicione o seguinte código acima daawait GetUserInfo();
linha.// First, attempt silent sign in // If the user's information is already in the app's cache, // they won't have to sign in again. try { var accounts = await PCA.GetAccountsAsync(); var silentAuthResult = await PCA .AcquireTokenSilent(Scopes, accounts.FirstOrDefault()) .ExecuteAsync(); Debug.WriteLine("User already signed in."); Debug.WriteLine($"Successful silent authentication for: {silentAuthResult.Account.Username}"); Debug.WriteLine($"Access token: {silentAuthResult.AccessToken}"); } catch (MsalUiRequiredException msalEx) { // This exception is thrown when an interactive sign-in is required. Debug.WriteLine("Silent token request failed, user needs to sign-in: " + msalEx.Message); // Prompt the user to sign-in var interactiveRequest = PCA.AcquireTokenInteractive(Scopes); if (AuthUIParent != null) { interactiveRequest = interactiveRequest .WithParentActivityOrWindow(AuthUIParent); } var interactiveAuthResult = await interactiveRequest.ExecuteAsync(); Debug.WriteLine($"Successful interactive authentication for: {interactiveAuthResult.Account.Username}"); Debug.WriteLine($"Access token: {interactiveAuthResult.AccessToken}"); } catch (Exception ex) { Debug.WriteLine("Authentication failed. See exception messsage for more details: " + ex.Message); }
Este código tenta primeiro obter um token de acesso silenciosamente. Se as informações de um usuário já estão no cache do aplicativo (por exemplo, se o usuário fechou o aplicativo anteriormente sem sair), isso será bem-sucedido e não há motivo para solicitar ao usuário. Se não houver informações de um usuário no cache, a
AcquireTokenSilent().ExecuteAsync()
função lançará umMsalUiRequiredException
. Nesse caso, o código chama a função interativa para obter um token,AcquireTokenInteractive
.Atualize
SignOut
a função para remover as informações do usuário do cache. Adicione o código a seguir ao início daSignOut
função.// Get all cached accounts for the app // (Should only be one) var accounts = await PCA.GetAccountsAsync(); while (accounts.Any()) { // Remove the account info from the cache await PCA.RemoveAsync(accounts.First()); accounts = await PCA.GetAccountsAsync(); }
Atualizar o projeto android para habilitar a login
Quando usada em um projeto Xamarin Android, a Biblioteca de Autenticação da Microsoft tem alguns requisitos específicos para Android.
No projeto GraphTutorial.Android, expanda a pasta Propriedades e abra AndroidManifest.xml. Se você estiver usando Visual Studio para Mac, Clique em Controle AndroidManifest.xml e escolha Abrir com, em seguida, Editor de Código-Fonte. Substitua todo o conteúdo pelo seguinte.
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="1" android:versionName="1.0" package="com.companyname.GraphTutorial"> <uses-sdk android:minSdkVersion="21" android:targetSdkVersion="28" /> <application android:label="GraphTutorial.Android"> <activity android:name="microsoft.identity.client.BrowserTabActivity"> <intent-filter> <action android:name="android.intent.action.VIEW" /> <category android:name="android.intent.category.DEFAULT" /> <category android:name="android.intent.category.BROWSABLE" /> <data android:scheme="msauth" android:host="com.companyname.GraphTutorial" /> </intent-filter> </activity> </application> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <uses-permission android:name="android.permission.INTERNET" /> </manifest>
Abra MainActivity.cs e adicione as instruções
using
a seguir à parte superior do arquivo.using Android.Content; using Microsoft.Identity.Client;
Substitua a
OnActivityResult
função para passar o controle para a biblioteca MSAL. Adicione o seguinte àMainActivity
classe.protected override void OnActivityResult(int requestCode, Result resultCode, Intent data) { base.OnActivityResult(requestCode, resultCode, data); AuthenticationContinuationHelper .SetAuthenticationContinuationEventArgs(requestCode, resultCode, data); }
Na
OnCreate
função, adicione a seguinte linha após aLoadApplication(new App());
linha.App.AuthUIParent = this;
Atualizar o projeto do iOS para habilitar a login
Importante
Como o MSAL requer o uso de um arquivo Entitlements.plist, você deve configurar Visual Studio com sua conta de desenvolvedor da Apple para habilitar o provisionamento. Se você estiver executando este tutorial no simulador de iPhone, precisará adicionar Entitlements.plist no campo Direitos Personalizados nas configurações do projeto GraphTutorial.iOS, Build->iOS Bundle Signing. Para obter mais informações, consulte Provisionamento de dispositivo para Xamarin.iOS.
Quando usada em um projeto Xamarin iOS, a Biblioteca de Autenticação da Microsoft tem alguns requisitos específicos para iOS.
No Explorador de Soluções, expanda o projeto GraphTutorial.iOS e abra o arquivo Entitlements.plist.
Localize o direito de chaveiro e selecione Habilitar ChaveChain.
Em Grupos de Chave, adicione uma entrada com o formato
com.companyname.GraphTutorial
.Atualize o código no projeto GraphTutorial.iOS para manipular o redirecionamento durante a autenticação. Abra o arquivo AppDelegate.cs e adicione a seguinte
using
instrução na parte superior do arquivo.using Microsoft.Identity.Client;
Adicione a linha a seguir
FinishedLaunching
para funcionar logo antes daLoadApplication(new App());
linha.// Specify the Keychain access group App.iOSKeychainSecurityGroup = NSBundle.MainBundle.BundleIdentifier;
Substitua a
OpenUrl
função para passar a URL para a biblioteca MSAL. Adicione o seguinte àAppDelegate
classe.// Handling redirect URL // See: https://github.com/azuread/microsoft-authentication-library-for-dotnet/wiki/Xamarin-iOS-specifics public override bool OpenUrl(UIApplication app, NSUrl url, NSDictionary options) { AuthenticationContinuationHelper.SetAuthenticationContinuationEventArgs(url); return true; }
Armazenando os tokens
Quando a Biblioteca de Autenticação da Microsoft é usada em um projeto Xamarin, ela aproveita o armazenamento seguro nativo para armazenar tokens em cache por padrão. Consulte Serialização de cache de token para obter mais informações.
Test sign-in
Neste ponto, se você executar o aplicativo e tocar no botão Entrar, será solicitado a entrar. Ao entrar com êxito, você deve ver o token de acesso impresso na saída do depurador.
Obter detalhes do usuário
Adicione uma nova função à classe App para inicializar o
GraphServiceClient
.private async Task InitializeGraphClientAsync() { var currentAccounts = await PCA.GetAccountsAsync(); try { if (currentAccounts.Count() > 0) { // Initialize Graph client GraphClient = new GraphServiceClient(new DelegateAuthenticationProvider( async (requestMessage) => { var result = await PCA.AcquireTokenSilent(Scopes, currentAccounts.FirstOrDefault()) .ExecuteAsync(); requestMessage.Headers.Authorization = new AuthenticationHeaderValue("Bearer", result.AccessToken); })); await GetUserInfo(); IsSignedIn = true; } else { IsSignedIn = false; } } catch (Exception ex) { Debug.WriteLine("Failed to initialized graph client."); Debug.WriteLine($"Accounts in the msal cache: {currentAccounts.Count()}."); Debug.WriteLine($"See exception message for details: {ex.Message}"); } }
Atualize
SignIn
a função em App.xaml.cs para chamar essa função em vez deGetUserInfo
. Remova o seguinte daSignIn
função.await GetUserInfo(); IsSignedIn = true;
Adicione o seguinte ao final da
SignIn
função.await InitializeGraphClientAsync();
Atualize
GetUserInfo
a função para obter os detalhes do usuário do Microsoft Graph. Substitua a funçãoGetUserInfo
existente pela seguinte.private async Task GetUserInfo() { // Get the logged on user's profile (/me) var user = await GraphClient.Me.Request() .Select(u => new { u.DisplayName, u.Mail, u.MailboxSettings, u.UserPrincipalName }) .GetAsync(); UserPhoto = ImageSource.FromStream(() => GetUserPhoto()); UserName = user.DisplayName; UserEmail = string.IsNullOrEmpty(user.Mail) ? user.UserPrincipalName : user.Mail; try { UserTimeZone = TZConvert.GetTimeZoneInfo(user.MailboxSettings.TimeZone); } catch { // Default to local time zone UserTimeZone = TimeZoneInfo.Local; } }
Salve suas alterações e execute o aplicativo. Depois que a interface do usuário for atualizada com o nome de exibição e o endereço de email do usuário.
Obter uma exibição de calendário
Neste exercício, você incorporará o microsoft Graph no aplicativo. Para este aplicativo, você usará a Biblioteca de Clientes do Microsoft Graph para .NET para fazer chamadas para o Microsoft Graph.
Obtenha eventos de calendário do Outlook
Abra CalendarPage.xaml no projeto GraphTutorial e substitua seu conteúdo pelo seguinte.
<?xml version="1.0" encoding="utf-8" ?> <ContentPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" Title="Calendar" x:Class="GraphTutorial.CalendarPage"> <ContentPage.Content> <StackLayout> <Editor x:Name="JSONResponse" IsSpellCheckEnabled="False" HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand"/> </StackLayout> </ContentPage.Content> </ContentPage>
Abra CalendarPage.xaml.cs e adicione as instruções a seguir
using
na parte superior do arquivo.using Microsoft.Graph; using System.Collections.ObjectModel; using System.ComponentModel;
Adicione a seguinte função à classe para obter o início da semana atual no
CalendarPage
fuso horário do usuário.private static DateTime GetUtcStartOfWeekInTimeZone(DateTime today, TimeZoneInfo timeZone) { // Assumes Sunday as first day of week int diff = System.DayOfWeek.Sunday - today.DayOfWeek; // create date as unspecified kind var unspecifiedStart = DateTime.SpecifyKind(today.AddDays(diff), DateTimeKind.Unspecified); // convert to UTC return TimeZoneInfo.ConvertTimeToUtc(unspecifiedStart, timeZone); }
Adicione a seguinte função à
CalendarPage
classe para obter os eventos do usuário e exibir a resposta JSON.protected override async void OnAppearing() { base.OnAppearing(); // Get start and end of week in user's time zone var startOfWeek = GetUtcStartOfWeekInTimeZone(DateTime.Today, App.UserTimeZone); var endOfWeek = startOfWeek.AddDays(7); var queryOptions = new List<QueryOption> { new QueryOption("startDateTime", startOfWeek.ToString("o")), new QueryOption("endDateTime", endOfWeek.ToString("o")) }; var timeZoneString = Xamarin.Forms.Device.RuntimePlatform == Xamarin.Forms.Device.UWP ? App.UserTimeZone.StandardName : App.UserTimeZone.DisplayName; // Get the events var events = await App.GraphClient.Me.CalendarView.Request(queryOptions) .Header("Prefer", $"outlook.timezone=\"{timeZoneString}\"") .Select(e => new { e.Subject, e.Organizer, e.Start, e.End }) .OrderBy("start/DateTime") .Top(50) .GetAsync(); // Temporary JSONResponse.Text = App.GraphClient.HttpProvider.Serializer.SerializeObject(events.CurrentPage); }
Considere o que o código
OnAppearing
está fazendo.- O URL que será chamado é
/v1.0/me/calendarview
.- Os
startDateTime
endDateTime
parâmetros e definem o início e o fim do exibição de calendário. - O header faz com que os eventos sejam
Prefer: outlook.timezone
start
end
retornados no fuso horário do usuário. - A
Select
função limita os campos retornados para cada evento para apenas aqueles que o aplicativo realmente usará. - A
OrderBy
função classifica os resultados pela data e hora de início. - A
Top
função solicita no máximo 50 eventos.
- Os
- O URL que será chamado é
Execute o aplicativo, entre e clique no item de navegação Calendário no menu. Você deve ver um despejo JSON dos eventos no calendário do usuário.
Exibir os resultados
Agora você pode substituir o despejo JSON por algo para exibir os resultados de maneira amigável.
Comece criando um conversor de valor de associação para converter os valores dateTimeTimeZone retornados pela Microsoft Graph nos formatos de data e hora esperados pelo usuário.
Clique com o botão direito do mouse na pasta Modelos no projeto GraphTutorial e selecione Adicionar, em seguida, Classe.... Nomeia a
GraphDateTimeTimeZoneConverter
classe e selecione Adicionar.Substitua todo o conteúdo do arquivo pelo seguinte.
using Microsoft.Graph; using System; using System.Globalization; using Xamarin.Forms; namespace GraphTutorial.Models { class GraphDateTimeTimeZoneConverter : IValueConverter { public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { if (value is DateTimeTimeZone date) { var parsedDateAs = DateTimeOffset.Parse(date.DateTime); // Return the local date time string return $"{parsedDateAs.LocalDateTime.ToShortDateString()} {parsedDateAs.LocalDateTime.ToShortTimeString()}"; } return string.Empty; } public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { throw new NotImplementedException(); } } }
Substitua todo o conteúdo de CalendarPage.xaml pelo seguinte.
<!-- Copyright (c) Microsoft Corporation. Licensed under the MIT License. --> <!-- <CalendarPageXamlSnippet> --> <?xml version="1.0" encoding="utf-8" ?> <ContentPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:local="clr-namespace:GraphTutorial.Models" Title="Calendar" x:Class="GraphTutorial.CalendarPage"> <ContentPage.Resources> <local:GraphDateTimeTimeZoneConverter x:Key="DateConverter" /> </ContentPage.Resources> <ContentPage.Content> <StackLayout> <ListView x:Name="CalendarList" HasUnevenRows="true" Margin="10,10,10,10"> <ListView.ItemTemplate> <DataTemplate> <ViewCell> <StackLayout Margin="10,10,10,10"> <Label Text="{Binding Path=Subject}" FontAttributes="Bold" FontSize="Medium" /> <Label Text="{Binding Path=Organizer.EmailAddress.Name}" FontSize="Small" /> <StackLayout Orientation="Horizontal"> <Label Text="{Binding Path=Start, Converter={StaticResource DateConverter}}" FontSize="Micro" /> <Label Text="to" FontSize="Micro" /> <Label Text="{Binding Path=End, Converter={StaticResource DateConverter}}" FontSize="Micro" /> </StackLayout> </StackLayout> </ViewCell> </DataTemplate> </ListView.ItemTemplate> </ListView> </StackLayout> </ContentPage.Content> </ContentPage> <!-- </CalendarPageXamlSnippet> -->
Isso substitui o
Editor
porListView
um . ODataTemplate
usado para renderizar cada item usa oGraphDateTimeTimeZoneConverter
para converter as propriedades e do evento em uma cadeia deStart
End
caracteres.Abra CalendarPage.xaml.cs e remova as linhas a seguir da
OnAppearing
função.// Temporary JSONResponse.Text = JsonConvert.SerializeObject(events.CurrentPage, Formatting.Indented);
Em seu lugar, adicione o código a seguir.
// Add the events to the list view CalendarList.ItemsSource = events.CurrentPage.ToList();
Execute o aplicativo, entre e clique no item de navegação Calendário. Você deve ver a lista de eventos com os valores Start e End formatados.
Criar um novo evento
Nesta seção, você adicionará a capacidade de criar eventos no calendário do usuário.
Abra NewEventPage.xaml e substitua seu conteúdo pelo seguinte.
<!-- Copyright (c) Microsoft Corporation. Licensed under the MIT License. --> <!-- <NewEventPageXamlSnippet> --> <?xml version="1.0" encoding="utf-8" ?> <ContentPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="GraphTutorial.NewEventPage"> <ContentPage.Content> <AbsoluteLayout> <ScrollView AbsoluteLayout.LayoutBounds="0,0,1,1" AbsoluteLayout.LayoutFlags="All"> <StackLayout Spacing="10" Margin="10"> <Label Text="Subject" /> <Entry Text="{Binding Subject}" /> <Label Text="Attendees" /> <Entry Text="{Binding Attendees}" Placeholder="Add multiple email addresses separated by a semicolon (';')" /> <Grid> <Grid.ColumnDefinitions> <ColumnDefinition Width="auto" /> <ColumnDefinition Width="auto" /> </Grid.ColumnDefinitions> <Grid.RowDefinitions> <RowDefinition Height="auto" /> <RowDefinition Height="auto" /> <RowDefinition Height="auto" /> <RowDefinition Height="auto" /> </Grid.RowDefinitions> <Label Text="Start" /> <DatePicker Grid.Row="2" Date="{Binding StartDate, Mode=TwoWay}" /> <TimePicker Grid.Row="2" Grid.Column="2" Time="{Binding StartTime}" /> <Label Text="End" Grid.Row="3" /> <DatePicker Grid.Row="4" Date="{Binding EndDate, Mode=TwoWay}" /> <TimePicker Grid.Row="4" Grid.Column="2" Time="{Binding EndTime}" /> </Grid> <Label Text="Body" /> <Editor HeightRequest="200" Text="{Binding Body}" /> <Button Text="Create" Clicked="CreateEvent" IsEnabled="{Binding IsValid}" /> </StackLayout> </ScrollView> <StackLayout AbsoluteLayout.LayoutBounds="0,0,1,1" AbsoluteLayout.LayoutFlags="All" IsVisible="{Binding IsWorking}"> <ActivityIndicator HorizontalOptions="CenterAndExpand" VerticalOptions="CenterAndExpand" IsRunning="{Binding IsWorking}" /> </StackLayout> </AbsoluteLayout> </ContentPage.Content> </ContentPage> <!-- </NewEventPageXamlSnippet> -->
Abra NewEventPage.xaml.cs e adicione as instruções a seguir à
using
parte superior do arquivo.using System.ComponentModel; using Microsoft.Graph;
Adicione a interface INotifyPropertyChange à classe NewEventPage. Substitua a declaração de classe existente pelo seguinte.
[XamlCompilation(XamlCompilationOptions.Compile)] public partial class NewEventPage : ContentPage, INotifyPropertyChanged { public NewEventPage() { InitializeComponent(); BindingContext = this; } }
Adicione as seguintes propriedades à classe NewEventPage.
// Value of the Subject text box private string _subject = ""; public string Subject { get { return _subject; } set { _subject = value; OnPropertyChanged(); IsValid = true; } } // Value of the Attendees text box private string _attendees = ""; public string Attendees { get { return _attendees; } set { _attendees = value; OnPropertyChanged(); } } // Value of the Start date picker private DateTime _startDate = DateTime.Today; public DateTime StartDate { get { return _startDate; } set { _startDate = value; OnPropertyChanged(); IsValid = true; } } // Value of the Start time picker private TimeSpan _startTime = TimeSpan.Zero; public TimeSpan StartTime { get { return _startTime; } set { _startTime = value; OnPropertyChanged(); IsValid = true; } } // Value of the End date picker private DateTime _endDate = DateTime.Today; public DateTime EndDate { get { return _endDate; } set { _endDate = value; OnPropertyChanged(); IsValid = true; } } // Value of the End time picker private TimeSpan _endTime = TimeSpan.Zero; public TimeSpan EndTime { get { return _endTime; } set { _endTime = value; OnPropertyChanged(); IsValid = true; } } // Value of the Body text box private string _body = ""; public string Body { get { return _body; } set { _body = value; OnPropertyChanged(); } } // Combine the date from date picker with time from time picker private DateTimeOffset CombineDateAndTime(DateTime date, TimeSpan time) { // Use the year, month, and day from the supplied DateTimeOffset // to create a new DateTime at midnight var dt = new DateTime(date.Year, date.Month, date.Day); // Add the TimeSpan, and use the user's timezone offset return new DateTimeOffset(dt + time, App.UserTimeZone.BaseUtcOffset); } // Combined value of Start date and time pickers public DateTimeOffset Start { get { return CombineDateAndTime(StartDate, StartTime); } } // Combined value of End date and time pickers public DateTimeOffset End { get { return CombineDateAndTime(EndDate, EndTime); } } public bool IsValid { get { // Subject is required, Start must be before // End return !string.IsNullOrWhiteSpace(Subject) && DateTimeOffset.Compare(Start, End) < 0; } private set { // Only used to fire event, value // is always calculated OnPropertyChanged(); } } private bool _isWorking = false; public bool IsWorking { get { return _isWorking; } set { _isWorking = value; OnPropertyChanged(); } }
Adicione o código a seguir para criar o evento.
private async void CreateEvent(object sender, EventArgs e) { IsWorking = true; var timeZoneString = Xamarin.Forms.Device.RuntimePlatform == Xamarin.Forms.Device.UWP ? App.UserTimeZone.StandardName : App.UserTimeZone.DisplayName; // Initialize a new Event object with the required fields var newEvent = new Event { Subject = Subject, Start = new DateTimeTimeZone { DateTime = Start.ToString("yyyy'-'MM'-'dd'T'HH':'mm':'ss"), TimeZone = timeZoneString }, End = new DateTimeTimeZone { DateTime = End.ToString("yyyy'-'MM'-'dd'T'HH':'mm':'ss"), TimeZone = timeZoneString } }; // If there's a body, add it if (!string.IsNullOrEmpty(Body)) { newEvent.Body = new ItemBody { Content = Body, ContentType = BodyType.Text }; } if (!string.IsNullOrEmpty(Attendees)) { var attendeeList = new List<Attendee>(); // Treat any unrecognized text as a list of email addresses var emails = Attendees.Split(new[] { ';', ',', ' ' }, StringSplitOptions.RemoveEmptyEntries); foreach (var email in emails) { try { // Validate the email address var addr = new System.Net.Mail.MailAddress(email); if (addr.Address == email) { attendeeList.Add(new Attendee { Type = AttendeeType.Required, EmailAddress = new EmailAddress { Address = email } }); } } catch { /* Invalid, skip */ } } if (attendeeList.Count > 0) { newEvent.Attendees = attendeeList; } } await App.GraphClient.Me.Events.Request().AddAsync(newEvent); await DisplayAlert("Success", "Event created.", "OK"); IsWorking = false; }
Salve suas alterações e execute o aplicativo. Entre, selecione o item de menu Novo evento, preencha o formulário e selecione Criar para adicionar um evento ao calendário do usuário.
Parabéns!
Você concluiu o tutorial Xamarin Microsoft Graph. Agora que você tem um aplicativo de trabalho que chama a Microsoft Graph, você pode experimentar e adicionar novos recursos. Visite a visão geral do microsoft Graph para ver todos os dados que você pode acessar com o Microsoft Graph.
Comentários
Forneça qualquer comentário sobre este tutorial no repositório GitHub .
Tem algum problema com essa seção? Se tiver, envie seus comentários para que possamos melhorar esta seção.