Создание приложений Xamarin с помощью Microsoft Graph
В этом руководстве рассказывается о создании приложения Xamarin, которое использует API microsoft Graph для получения сведений о календаре для пользователя.
Совет
Если вы предпочитаете просто скачать завершенный учебник, вы можете скачать или клонировать GitHub репозиторий.
Предварительные требования
Перед началом этого руководства необходимо установить Visual Studio на компьютере с Windows 10 с включенным режимом разработчика. Если у вас нет Visual Studio, посетите предыдущую ссылку для параметров загрузки.
Кроме того, в рамках установки Visual Studio Xamarin. Инструкции по установке и настройке Xamarin см. в инструкции по установке Xamarin.
Кроме того, необходимо иметь доступ к Компьютеру Mac с Visual Studio для Mac установлен. Если у вас нет доступа, вы все равно можете завершить этот учебник, но не сможете завершить разделы, определенные для iOS.
Вы также должны иметь личную учетную запись Майкрософт с почтовым ящиком на Outlook.com или учетную запись Microsoft work или school. Если у вас нет учетной записи Майкрософт, существует несколько вариантов получения бесплатной учетной записи:
- Вы можете зарегистрироваться на новую личную учетную запись Майкрософт.
- Вы можете зарегистрироваться в программе Microsoft 365 разработчика, чтобы получить бесплатную Microsoft 365 подписку.
Примечание
Этот учебник был написан Visual Studio версии 16.10.3 и Visual Studio для Mac версии 8.5.1. На обеих машинах установлена платформа SDK Для Android 28. Действия в этом руководстве могут работать с другими версиями, но они не были проверены.
Отзывы
Обратите внимание на этот учебник в репозитории GitHub.
Создание приложения Xamarin
Откройте Visual Studio и нажмите Создать новый проект.
В диалоговом окте "Создание нового проекта" выберите мобильное приложение (Xamarin.Forms), а затем выберите Далее.
В диалоговом
GraphTutorial
октаге Настройка нового проекта введите имя Project и имя решения, а затем выберите Create.Важно!
Убедитесь, что вы вводите точно такое же имя для Visual Studio Project, указанного в этих лабораторных инструкциях. Имя проекта Visual Studio становится частью пространства имен в коде. Для кода в этих инструкциях важно, чтобы пространство имен совпадало с именем проекта Visual Studio, указанным в этих инструкциях. Если ввести другое имя проекта, код не будет компилироваться, пока вы не настроите все пространства имен в соответствии с именем, которое вы указали для проекта Visual Studio при его создании.
В диалоговом окланте New Cross Platform App выберите пустой шаблон и выберите платформы, которые необходимо создать в рамках Платформы. Выберите ОК для создания решения.
Установка пакетов
Прежде чем двигаться дальше, установите дополнительные NuGet пакеты, которые вы будете использовать позже.
- Microsoft.Identity.Client для обработки проверки подлинности Azure AD и управления маркерами.
- Microsoft. Graph для звонков в microsoft Graph.
- TimeZoneConverter для обработки часовых поясов поперек платформы.
Выберите Сервис > Диспетчер пакетов NuGet > Консоль диспетчера пакетов.
В консоли диспетчера пакетов введите следующие команды.
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
Проектирование приложения
Начните с обновления App
класса, чтобы добавить переменные для отслеживания состояния проверки подлинности и подписанного пользователя.
В Обозревателе решений расширь проект GraphTutorial , а затем разогнай файл App.xaml . Откройте файл App.xaml.cs
using
и добавьте следующие утверждения в верхнюю часть файла.using System.ComponentModel; using System.IO; using System.Reflection; using System.Threading.Tasks;
Добавьте интерфейс
INotifyPropertyChanged
в объявление класса.public partial class App : Application, INotifyPropertyChanged
Добавьте в класс следующие
App
свойства.// 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;
Добавьте в класс следующие
App
функции. ,SignIn
иSignOut
функцииGetUserInfo
являются только placeholders на данный момент.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"); }
Функция
GetUserPhoto
возвращает фотографию по умолчанию на данный момент. Вы можете либо предоставить собственный файл, либо скачать тот, который используется в примере из GitHub. Если вы используете собственный файл, переименуйте его в no-profile-pic.png.Скопируйте файл в каталог ./GraphTutorial/GraphTutorial .
Щелкните правой кнопкой мыши файл в обозревателе решений и выберите Свойства. В окне Свойства измените значение действия сборки на встроенный ресурс.
Навигация по приложениям
В этом разделе вы измените главную страницу приложения на FlyoutPage. Это обеспечит меню навигации для переключения между представлением в приложении.
Откройте файл MainPage.xaml в проекте GraphTutorial и замените его содержимое следующим.
<!-- 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> -->
Реализация меню
Щелкните правой кнопкой мыши проект GraphTutorial и выберите Добавить, а затем новую папку. Назови папку
Models
.Щелкните правой кнопкой мыши папку Модели и выберите Добавить, а затем класс .... Назови класс и
NavMenuItem
выберите Добавить.Откройте файл NavMenuItem.cs и замените его содержимое следующим.
namespace GraphTutorial.Models { public enum MenuItemType { Welcome, Calendar, NewEvent } public class NavMenuItem { public MenuItemType Id { get; set; } public string Title { get; set; } } }
Щелкните правой кнопкой мыши проект GraphTutorial и выберите Добавить, а затем New Item.... Выберите страницу контента и назови ее
MenuPage
. Нажмите Добавить.Откройте файл MenuPage.xaml и замените его содержимое следующим.
<!-- 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> -->
Расширь menuPage.xaml в обозревателе решений и откройте файл MenuPage.xaml.cs. Замените его содержимое следующим.
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"); } } } }
Примечание
Visual Studio будет сообщать об ошибках в MenuPage.xaml.cs. Эти ошибки будут устранены на более позднем этапе.
Реализация страницы приветствия
Щелкните правой кнопкой мыши проект GraphTutorial и выберите Добавить, а затем New Item.... Выберите страницу контента и назови ее
WelcomePage
. Нажмите Добавить. Откройте файл WelcomePage.xaml и замените его содержимое следующим.<!-- 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> -->
Расширь welcomePage.xaml в обозревателе решений и откройте файл WelcomePage.xaml.cs. Добавьте к классу
WelcomePage
следующую функцию:private void OnSignIn(object sender, EventArgs e) { (App.Current.MainPage as MasterDetailPage).IsPresented = true; }
Добавление страниц календаря и новых событий
Теперь добавьте страницу календаря и новую страницу событий. Они будут просто местообнажителей на данный момент.
Щелкните правой кнопкой мыши проект GraphTutorial и выберите Добавить, а затем New Item.... Выберите страницу контента и назови ее
CalendarPage
. Нажмите Добавить.Щелкните правой кнопкой мыши проект GraphTutorial и выберите Добавить, а затем New Item.... Выберите страницу контента и назови ее
NewEventPage
. Нажмите Добавить.
Обновление кода MainPage
Теперь, когда все страницы на месте, обнови код-за для MainPage.xaml.
Разместим MainPage.xaml в обозревателе решений и откройте файл MainPage.xaml.cs и замените все содержимое следующим образом.
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; } } } }
Сохраните все изменения. Щелкните правой кнопкой мыши проект, который необходимо запустить (Android, iOS или UWP) и выберите Set как startUp Project. Нажмите кнопку F5 или выберите отладку > начать отладку в Visual Studio.
Регистрация приложения на портале
В этом упражнении будет создаваться новое родной приложение Azure AD с Azure Active Directory центра администрирования.
Откройте браузер и перейдите в Центр администрирования Azure Active Directory и войдите с помощью личной учетной записи (т.е. учетной записи Майкрософт) или рабочей или учебной учетной записи.
Выберите Azure Active Directory на панели навигации слева, затем выберите Регистрация приложений в разделе Управление.
Выберите Новая регистрация. На странице Зарегистрировать приложение задайте необходимые значения следующим образом.
- Введите имя
Xamarin Graph Tutorial
. - Введите поддерживаемые типы учетных записей для учетных записей в любом каталоге организаций и личных учетных записей Microsoft.
- В статье Перенаправление URI (необязательно) измените отсев на общедоступный клиент (мобильный & рабочий стол) и установите значение
msauth://com.companyname.GraphTutorial
.
- Введите имя
Нажмите Зарегистрировать. На странице Учебник Graph Xamarin скопируйте значение ID приложения (клиента) и сохраните его, оно потребуется на следующем шаге.
Добавление проверки подлинности с помощью Azure AD
В этом упражнении вы расширит приложение от предыдущего упражнения для поддержки проверки подлинности с помощью Azure AD. Это необходимо для получения необходимого маркера доступа OAuth для вызова microsoft Graph. На этом этапе вы интегрируете библиотеку проверки подлинности Майкрософт для .NET (MSAL) в приложение.
В Обозревателе решений разойдите проект GraphTutorial и щелкните правой кнопкой мыши папку Models. Выберите Добавить > класс.... Назови класс
OAuthSettings
и выберите Добавить.Откройте файл OAuthSettings.cs и замените его содержимое следующим.
Замените
YOUR_APP_ID_HERE
с помощью ID приложения из регистрации приложения.Важно!
Если вы используете источник управления, например git, сейчас самое время исключить файл из источника управления, чтобы избежать случайной утечки вашего
OAuthSettings.cs
ID приложения.
Реализация входа в систему
Откройте файл App.xaml.cs в проекте GraphTutorial и добавьте в верхней части файла следующие
using
утверждения.using GraphTutorial.Models; using Microsoft.Identity.Client; using Microsoft.Graph; using System.Diagnostics; using System.Linq; using System.Net.Http.Headers; using TimeZoneConverter;
Измените строку объявления класса App, чтобы разрешить конфликт имен для приложения.
public partial class App : Xamarin.Forms.Application, INotifyPropertyChanged
Добавьте в класс следующие
App
свойства.// 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(' ');
Далее создайте новое
PublicClientApplication
в конструктореApp
класса.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(); }
SignIn
Обнови функцию, чтобы использоватьPublicClientApplication
маркер доступа. Добавьте следующий код надawait GetUserInfo();
строкой.// 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); }
Этот код сначала пытается получить маркер доступа молча. Если сведения пользователя уже есть в кэше приложения (например, если пользователь закрыл приложение ранее без подписки), это будет успешно, и нет причин подсказывать пользователю. Если в кэше нет сведений пользователя, функция
AcquireTokenSilent().ExecuteAsync()
бросаетMsalUiRequiredException
. В этом случае код вызывает интерактивную функцию для полученияAcquireTokenInteractive
маркера.SignOut
Обновите функцию, чтобы удалить сведения пользователя из кэша. Добавьте следующий код в началоSignOut
функции.// 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(); }
Обновление проекта Android, чтобы включить вход
При его реализации в android-проекте Xamarin библиотека проверки подлинности Майкрософт имеет несколько требований, определенных для Android.
В проекте GraphTutorial.Android разложите папку Свойства и откройте AndroidManifest.xml. Если вы используете Visual Studio для Mac, нажмите кнопку AndroidManifest.xml и выберите Open With, а затем редактор исходных кодов. Замените все содержимое следующим.
<?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>
Откройте MainActivity.cs и добавьте следующие утверждения
using
в верхнюю часть файла.using Android.Content; using Microsoft.Identity.Client;
Переопределять функцию, чтобы передать управление библиотеке
OnActivityResult
MSAL. Добавьте следующее вMainActivity
класс.protected override void OnActivityResult(int requestCode, Result resultCode, Intent data) { base.OnActivityResult(requestCode, resultCode, data); AuthenticationContinuationHelper .SetAuthenticationContinuationEventArgs(requestCode, resultCode, data); }
В
OnCreate
функции добавьте следующую строку послеLoadApplication(new App());
строки.App.AuthUIParent = this;
Обновление проекта iOS, чтобы включить вход
Важно!
Так как MSAL требует использования файла Entitlements.plist, необходимо настроить Visual Studio учетной записью разработчика Apple, чтобы включить подготовка. Если вы работаете с этим учебником в симуляторе iPhone, вам необходимо добавить Entitlements.plist в поле Custom Entitlements в параметрах проекта GraphTutorial.iOS, build->iOS Bundle Signing. Дополнительные сведения см. в пункте Подготовка устройств для Xamarin.iOS.
При его реализации в проекте Xamarin iOS библиотека проверки подлинности Майкрософт имеет несколько требований, определенных для iOS.
В Обозревателе решений раз откройте проект GraphTutorial.iOS, а затем откройте файл Entitlements.plist.
Найдите право Keychain и выберите Включить KeyChain.
В Keychain Groups добавьте запись с форматом
com.companyname.GraphTutorial
.Обновление кода в проекте GraphTutorial.iOS для обработки перенаправления во время проверки подлинности. Откройте файл AppDelegate.cs и добавьте следующее утверждение
using
в верхней части файла.using Microsoft.Identity.Client;
Добавьте следующую строку
FinishedLaunching
для работы передLoadApplication(new App());
строкой.// Specify the Keychain access group App.iOSKeychainSecurityGroup = NSBundle.MainBundle.BundleIdentifier;
Переопределять
OpenUrl
функцию, чтобы передать URL-адрес в библиотеку MSAL. Добавьте следующее вAppDelegate
класс.// 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; }
Хранение маркеров
Когда библиотека проверки подлинности Майкрософт используется в проекте Xamarin, она по умолчанию используется для кэширования маркеров на родном безопасном хранилище. Дополнительные сведения см. в дополнительных сведениях.
Тестовый вход
В этот момент при запуске приложения и нажатии кнопки Вход в кнопку, вам будет предложено войти. При успешном входе вы должны увидеть маркер доступа, напечатанный на выходе отладки.
Получение сведений о пользователе
Добавьте новую функцию в класс App, чтобы инициализировать
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}"); } }
SignIn
Обнови функцию в App.xaml.cs, чтобы вызвать эту функцию вместоGetUserInfo
. Удалите следующее изSignIn
функции.await GetUserInfo(); IsSignedIn = true;
Добавьте следующее в конец
SignIn
функции.await InitializeGraphClientAsync();
Обнови функцию, чтобы получить сведения пользователя
GetUserInfo
из microsoft Graph. Замените имеющуюся функциюGetUserInfo
указанным ниже кодом.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; } }
Сохраните изменения и запустите приложение. После регистрации пользовательский интерфейс обновляется с именем и адресом электронной почты пользователя.
Просмотр календаря
В этом упражнении вы будете включать Graph Microsoft в приложение. Для этого приложения вы будете использовать клиентскую библиотеку Microsoft Graph для .NET для звонков в Microsoft Graph.
Получение событий календаря из Outlook
Откройте CalendarPage.xaml в проекте GraphTutorial и замените его содержимое следующим.
<?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>
Откройте CalendarPage.xaml.cs и добавьте следующие утверждения в
using
верхней части файла.using Microsoft.Graph; using System.Collections.ObjectModel; using System.ComponentModel;
Добавьте в класс следующую функцию, чтобы получить начало текущей недели
CalendarPage
в часовом поясе пользователя.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); }
Добавьте в класс следующую функцию, чтобы получить события пользователя и
CalendarPage
отобразить ответ 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); }
Рассмотрим, что делает
OnAppearing
код.- Вызывается URL-адрес
/v1.0/me/calendarview
.- Параметры
startDateTime
endDateTime
и параметров определяют начало и конец представления календаря. - Заготвка приводит к возвращению событий в часовом поясе
Prefer: outlook.timezone
start
end
пользователя. - Функция
Select
ограничивает поля, возвращаемые для каждого события, только теми, которые приложение будет фактически использовать. - Функция
OrderBy
сортировать результаты к дате начала и времени. - Функция
Top
запрашивает не более 50 событий.
- Параметры
- Вызывается URL-адрес
Запустите приложение, войдите и нажмите элемент навигации Календарь в меню. Вы должны увидеть сброс JSON событий в календаре пользователя.
Отображение результатов
Теперь вы можете заменить свалку JSON чем-то, чтобы отобразить результаты в удобной для пользователя манере.
Начните с создания конвертера связывающего значения для преобразования значений dateTimeTimeZone, возвращенных Корпорацией Майкрософт Graph в форматы даты и времени, которые ожидает пользователь.
Щелкните правой кнопкой мыши папку Модели в проекте GraphTutorial и выберите Добавить, а затем класс .... Назови класс
GraphDateTimeTimeZoneConverter
и выберите Добавить.Замените все содержимое файла следующим.
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(); } } }
Замените все содержимое CalendarPage.xaml следующим.
<!-- 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> -->
Это заменяет на
Editor
ListView
. Используемый для отображения каждого элемента используется для преобразования иDataTemplate
GraphDateTimeTimeZoneConverter
свойств события вStart
End
строку.Откройте CalendarPage.xaml.cs и удалите следующие строки из
OnAppearing
функции.// Temporary JSONResponse.Text = JsonConvert.SerializeObject(events.CurrentPage, Formatting.Indented);
На их месте добавьте следующий код.
// Add the events to the list view CalendarList.ItemsSource = events.CurrentPage.ToList();
Запустите приложение, войдите и нажмите элемент навигации Календарь. Список событий с форматными значениями "Начните" и "Конец".
Создание нового события
В этом разделе вы добавим возможность создания событий в календаре пользователя.
Откройте NewEventPage.xaml и замените его содержимое следующим.
<!-- 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> -->
Откройте NewEventPage.xaml.cs и добавьте следующие утверждения в
using
верхнюю часть файла.using System.ComponentModel; using Microsoft.Graph;
Добавьте интерфейс INotifyPropertyChange в класс NewEventPage. Замените существующее объявление класса следующим.
[XamlCompilation(XamlCompilationOptions.Compile)] public partial class NewEventPage : ContentPage, INotifyPropertyChanged { public NewEventPage() { InitializeComponent(); BindingContext = this; } }
Добавьте следующие свойства в класс 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(); } }
Добавьте следующий код для создания события.
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; }
Сохраните изменения и запустите приложение. Во входе выберите элемент меню "Новое событие", заполните форму и выберите Создать, чтобы добавить событие в календарь пользователя.
Поздравляем!
Вы завершили руководство по microsoft Graph Xamarin. Теперь, когда у вас есть рабочее приложение, которое вызывает Microsoft Graph, вы можете экспериментировать и добавлять новые функции. В обзоре microsoft Graph, чтобы увидеть все данные, к ним можно получить доступ с помощью microsoft Graph.
Отзывы
Возникла проблема с этим разделом? Если это так, отправьте нам отзыв, чтобы мы исправили этот раздел.