Major refactor of PersonView with new sample page (#133)

* Major refactor of PersonView with new sample page

* Added support for image decode pixel height/width in PersonView

* Fixing the way Graph calls are made so we don't silence exceptions anymore.
This commit is contained in:
Shane Weaver 2021-07-26 16:55:32 -07:00 коммит произвёл GitHub
Родитель 6ec457e729
Коммит f999c2bd40
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
12 изменённых файлов: 510 добавлений и 252 удалений

Просмотреть файл

@ -289,9 +289,14 @@ namespace CommunityToolkit.Graph.Uwp.Controls
var provider = ProviderManager.Instance.GlobalProvider;
if (provider != null)
{
// https://github.com/microsoftgraph/microsoft-graph-toolkit/blob/master/src/components/mgt-login/mgt-login.ts#L139
// TODO: Batch with photo request later? https://github.com/microsoftgraph/msgraph-sdk-dotnet-core/issues/29
UserDetails = await provider.GetClient().GetMeAsync();
try
{
UserDetails = await provider.GetClient().GetMeAsync();
}
catch
{
// TODO: Handle if UserDetails is null.
}
}
}

Просмотреть файл

@ -6,6 +6,7 @@ using System;
using System.Collections;
using System.Collections.ObjectModel;
using System.Linq;
using System.Threading.Tasks;
using CommunityToolkit.Authentication;
using CommunityToolkit.Graph.Extensions;
using Microsoft.Graph;
@ -68,49 +69,85 @@ namespace CommunityToolkit.Graph.Uwp.Controls
if (args.Reason == AutoSuggestionBoxTextChangeReason.UserInput)
{
var text = sender.Text;
var list = SuggestedItemsSource as IList;
if (list != null)
_typeTimer.Debounce(
async () =>
{
_typeTimer.Debounce(
async () =>
var text = sender.Text;
await UpdateResultsAsync(text);
// TODO: If we don't have Graph connection and just list of Person should we auto-filter here?
}, TimeSpan.FromSeconds(0.3));
}
}
private async Task UpdateResultsAsync(string text)
{
var list = SuggestedItemsSource as IList;
if (list == null)
{
return;
}
var graph = ProviderManager.Instance.GlobalProvider.GetClient();
if (graph == null)
{
return;
}
// If empty, will clear out
list.Clear();
if (string.IsNullOrWhiteSpace(text))
{
return;
}
IGraphServiceUsersCollectionPage usersCollection = null;
try
{
usersCollection = await graph.FindUserAsync(text);
}
catch
{
// No users found.
}
if (usersCollection != null)
{
foreach (var user in usersCollection.CurrentPage)
{
// Exclude people in suggested list that we already have picked
if (!Items.Any(person => (person as Person)?.Id == user.Id))
{
var graph = ProviderManager.Instance.GlobalProvider.GetClient();
if (graph != null)
{
// If empty, will clear out
list.Clear();
list.Add(user.ToPerson());
}
}
}
if (!string.IsNullOrWhiteSpace(text))
{
foreach (var user in (await graph.FindUserAsync(text)).CurrentPage)
{
// Exclude people in suggested list that we already have picked
if (!Items.Any(person => (person as Person)?.Id == user.Id))
{
list.Add(user.ToPerson());
}
}
IUserPeopleCollectionPage peopleCollection = null;
try
{
peopleCollection = await graph.FindPersonAsync(text);
}
catch
{
// No people found.
}
// Grab ids of current suggestions
var ids = list.Cast<object>().Select(person => (person as Person).Id);
if (peopleCollection != null)
{
// Grab ids of current suggestions
var ids = list.Cast<object>().Select(person => (person as Person).Id);
foreach (var contact in (await graph.FindPersonAsync(text)).CurrentPage)
{
// Exclude people in suggested list that we already have picked
// Or already suggested
if (!Items.Any(person => (person as Person)?.Id == contact.Id) &&
!ids.Any(id => id == contact.Id))
{
list.Add(contact);
}
}
}
}
// TODO: If we don't have Graph connection and just list of Person should we auto-filter here?
}, TimeSpan.FromSeconds(0.3));
foreach (var contact in peopleCollection.CurrentPage)
{
// Exclude people in suggested list that we already have picked
// Or already suggested
if (!Items.Any(person => (person as Person)?.Id == contact.Id) &&
!ids.Any(id => id == contact.Id))
{
list.Add(contact);
}
}
}
}

Просмотреть файл

@ -0,0 +1,22 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
namespace CommunityToolkit.Graph.Uwp.Controls
{
/// <summary>
/// The type of visual representation to use for the person.
/// </summary>
public enum PersonAvatarType
{
/// <summary>
/// Show a user's photo if available, otherwise show initials.
/// </summary>
Photo,
/// <summary>
/// Show the user's initials only.
/// </summary>
Initials,
}
}

Просмотреть файл

@ -135,5 +135,20 @@ namespace CommunityToolkit.Graph.Uwp.Controls
/// </summary>
public static readonly DependencyProperty PersonViewTypeProperty =
DependencyProperty.Register(nameof(PersonViewType), typeof(PersonViewType), typeof(PersonView), new PropertyMetadata(PersonViewType.TwoLines, PersonViewTypePropertiesChanged));
/// <summary>
/// Gets or sets the type of visual to display in the image part of the template.
/// </summary>
public PersonAvatarType PersonAvatarType
{
get => (PersonAvatarType)GetValue(PersonAvatarTypeProperty);
set => SetValue(PersonAvatarTypeProperty, value);
}
/// <summary>
/// Identifies the <see cref="PersonAvatarType"/> dependency property.
/// </summary>
public static readonly DependencyProperty PersonAvatarTypeProperty =
DependencyProperty.Register(nameof(PersonAvatarType), typeof(PersonAvatarType), typeof(PersonView), new PropertyMetadata(PersonAvatarType.Photo, PersonAvatarTypePropertiesChanged));
}
}

Просмотреть файл

@ -9,6 +9,7 @@ using System.Threading.Tasks;
using CommunityToolkit.Authentication;
using CommunityToolkit.Graph.Extensions;
using Microsoft.Graph;
using Windows.System;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Media.Imaging;
@ -20,48 +21,19 @@ namespace CommunityToolkit.Graph.Uwp.Controls
/// </summary>
public partial class PersonView : Control
{
private const string PersonViewDefaultImageSourceResourceName = "PersonViewDefaultImageSource";
/// <summary>
/// <see cref="PersonQuery"/> value used to retrieve the signed-in user's info.
/// </summary>
public const string PersonQueryMe = "me";
private string _photoId = null;
private const string PersonViewDefaultImageSourceResourceName = "PersonViewDefaultImageSource";
private const string PackageDefaultImageSource = "ms-appx:///CommunityToolkit.Graph.Uwp/Assets/person.png";
private string _defaultImageSource = "ms-appx:///Microsoft.Toolkit.Graph.Controls/Assets/person.png";
private BitmapImage _defaultImage;
private static async void PersonDetailsPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
private static void PersonDetailsPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is PersonView pv)
{
if (pv.PersonDetails != null)
{
if (pv?.PersonDetails?.GivenName?.Length > 0 && pv?.PersonDetails?.Surname?.Length > 0)
{
pv.Initials = string.Empty + pv.PersonDetails.GivenName[0] + pv.PersonDetails.Surname[0];
}
else if (pv?.PersonDetails?.DisplayName?.Length > 0)
{
// Grab first two initials in name
var initials = pv.PersonDetails.DisplayName.ToUpper().Split(' ').Select(i => i.First());
pv.Initials = string.Join(string.Empty, initials.Where(i => char.IsLetter(i)).Take(2));
}
if (pv?.UserPhoto?.UriSource?.AbsoluteUri == pv._defaultImageSource || pv?.PersonDetails?.Id != pv._photoId)
{
// Reload Image
pv.UserPhoto = pv._defaultImage;
await pv.LoadImageAsync(pv.PersonDetails);
}
else if (pv?.PersonDetails?.Id != pv._photoId)
{
pv.UserPhoto = pv._defaultImage;
pv._photoId = null;
}
}
pv.UpdateVisual();
}
}
@ -78,20 +50,42 @@ namespace CommunityToolkit.Graph.Uwp.Controls
{
if (d is PersonView pv)
{
pv.IsLargeImage = pv.PersonViewType == PersonViewType.Avatar;
pv.IsLargeImage = pv.PersonViewType is PersonViewType.TwoLines or PersonViewType.ThreeLines;
if (pv.IsLargeImage)
{
pv._imageDecodePixelHeight = 48;
pv._imageDecodePixelWidth = 48;
}
else
{
pv._imageDecodePixelHeight = 24;
pv._imageDecodePixelWidth = 24;
}
pv.UpdateImageSize();
}
}
private static void PersonAvatarTypePropertiesChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is PersonView pv)
{
pv.UpdateVisual();
}
}
private BitmapImage _defaultImage;
private int _imageDecodePixelHeight;
private int _imageDecodePixelWidth;
private string _photoId = null;
/// <summary>
/// Initializes a new instance of the <see cref="PersonView"/> class.
/// </summary>
public PersonView()
{
this.DefaultStyleKey = typeof(PersonView);
_defaultImage = new BitmapImage(new Uri(_defaultImageSource));
ProviderManager.Instance.ProviderStateChanged += (sender, args) => LoadData();
}
/// <inheritdoc/>
@ -99,13 +93,25 @@ namespace CommunityToolkit.Graph.Uwp.Controls
{
base.OnApplyTemplate();
if (Resources.TryGetValue(PersonViewDefaultImageSourceResourceName, out object value) && value is string uri)
if (Resources.TryGetValue(PersonViewDefaultImageSourceResourceName, out object value) && value is string uriString)
{
_defaultImageSource = uri;
_defaultImage = new BitmapImage(new Uri(_defaultImageSource)); // TODO: Couldn't load image from app package, only remote or in our assembly?
UserPhoto = _defaultImage;
_defaultImage = new BitmapImage(new Uri(uriString));
}
else
{
_defaultImage = new BitmapImage(new Uri(PackageDefaultImageSource));
}
ProviderManager.Instance.ProviderStateChanged -= OnProviderStateChanged;
ProviderManager.Instance.ProviderStateChanged += OnProviderStateChanged;
VisualStateManager.GoToState(this, Enum.GetName(typeof(ProviderState), ProviderManager.Instance.State), true);
LoadData();
}
private void OnProviderStateChanged(object sender, ProviderStateChangedEventArgs e)
{
VisualStateManager.GoToState(this, Enum.GetName(typeof(ProviderState), e.NewState), true);
LoadData();
}
@ -113,116 +119,225 @@ namespace CommunityToolkit.Graph.Uwp.Controls
{
var provider = ProviderManager.Instance.GlobalProvider;
if (provider == null || provider.State != ProviderState.SignedIn)
if (provider?.State == ProviderState.SignedIn)
{
// Set back to Default if not signed-in
if (provider != null)
{
UserPhoto = _defaultImage;
}
return;
await TryLoadPersonDetailsAsync();
UpdateVisual();
}
if (PersonDetails != null && UserPhoto == null)
else
{
await LoadImageAsync(PersonDetails);
LoadDefaultImage();
}
else if (!string.IsNullOrWhiteSpace(UserId) || PersonQuery?.ToLowerInvariant() == PersonQueryMe)
{
User user = null;
if (!string.IsNullOrWhiteSpace(UserId))
{
// TODO: Batch when API easier https://github.com/microsoftgraph/msgraph-sdk-dotnet-core/issues/29
try
{
user = await provider.GetClient().GetUserAsync(UserId);
}
catch
{
}
}
try
{
// TODO: Move to LoadImage based on previous call?
await DecodeStreamAsync(await provider.GetBetaClient().GetUserPhoto(UserId));
_photoId = UserId;
}
catch
{
}
private async void UpdateVisual()
{
if (PersonAvatarType is PersonAvatarType.Initials)
{
var initialsLoaded = TryLoadInitials();
if (initialsLoaded)
{
ClearUserPhoto();
}
else
{
try
{
user = await provider.GetClient().GetMeAsync();
}
catch
{
}
try
{
await DecodeStreamAsync(await provider.GetBetaClient().GetMyPhotoAsync());
_photoId = user.Id;
}
catch
{
}
}
if (user != null)
{
PersonDetails = user.ToPerson();
LoadDefaultImage();
}
}
else if (PersonDetails == null && !string.IsNullOrWhiteSpace(PersonQuery))
else if (PersonDetails != null)
{
var people = await provider.GetClient().FindPersonAsync(PersonQuery);
if (people != null && people.Count > 0)
if (PersonDetails.Id != _photoId)
{
var person = people.FirstOrDefault();
PersonDetails = person;
await LoadImageAsync(person);
LoadDefaultImage();
var photoLoaded = await TryLoadUserPhotoAsync();
if (photoLoaded)
{
UpdateImageSize();
}
else
{
var initialsLoaded = TryLoadInitials();
if (initialsLoaded)
{
ClearUserPhoto();
}
}
}
}
else
{
LoadDefaultImage();
}
}
private async Task LoadImageAsync(Person person)
private void LoadDefaultImage()
{
if (UserPhoto != _defaultImage)
{
UserPhoto = _defaultImage;
UpdateImageSize();
_photoId = null;
Initials = null;
}
}
private void UpdateImageSize()
{
if (UserPhoto != null)
{
UserPhoto.DecodePixelHeight = _imageDecodePixelHeight;
UserPhoto.DecodePixelWidth = _imageDecodePixelWidth;
}
}
private void ClearUserPhoto()
{
UserPhoto = null;
_photoId = null;
}
private async Task<bool> TryLoadPersonDetailsAsync()
{
if (PersonDetails != null)
{
return true;
}
var provider = ProviderManager.Instance.GlobalProvider;
if (provider?.State != ProviderState.SignedIn)
{
return false;
}
try
{
// TODO: Better guarding
var graph = ProviderManager.Instance.GlobalProvider.GetBetaClient();
if (!string.IsNullOrWhiteSpace(UserId))
{
var user = await provider.GetClient().GetUserAsync(UserId);
PersonDetails = user.ToPerson();
}
else if (PersonQuery?.ToLowerInvariant() == PersonQueryMe)
{
var user = await provider.GetClient().GetMeAsync();
PersonDetails = user.ToPerson();
}
else if (!string.IsNullOrWhiteSpace(PersonQuery))
{
var people = await provider.GetClient().FindPersonAsync(PersonQuery);
if (people != null && people.Count > 0)
{
var person = people.FirstOrDefault();
PersonDetails = person;
}
}
if (!string.IsNullOrWhiteSpace(person.UserPrincipalName))
{
await DecodeStreamAsync(await graph.GetUserPhoto(person.UserPrincipalName));
_photoId = person.Id; // TODO: Only set on success for photo?
}
else if (!string.IsNullOrWhiteSpace(person.ScoredEmailAddresses.First().Address))
{
// TODO https://github.com/microsoftgraph/microsoft-graph-toolkit/blob/master/src/components/mgt-person/mgt-person.ts#L174
}
return PersonDetails != null;
}
catch
{
// If we can't load a photo, that's ok.
// TODO: Log exception
}
return false;
}
private async Task DecodeStreamAsync(Stream photoStream)
private async Task<bool> TryLoadUserPhotoAsync()
{
var person = PersonDetails;
if (person == null)
{
return false;
}
if (PersonDetails.Id == _photoId && UserPhoto != null && UserPhoto != _defaultImage)
{
return true;
}
var provider = ProviderManager.Instance.GlobalProvider;
if (provider?.State != ProviderState.SignedIn)
{
return false;
}
Stream photoStream = null;
// TODO: Better guarding
try
{
var graph = ProviderManager.Instance.GlobalProvider?.GetBetaClient();
photoStream = await graph.GetUserPhoto(person.Id);
}
catch (Exception e)
{
}
if (photoStream != null)
{
var decodeResults = await TryDecodeStreamAsync(photoStream);
if (decodeResults.Success)
{
UserPhoto = decodeResults.Image;
_photoId = person.Id;
return true;
}
}
return false;
}
private bool TryLoadInitials()
{
if (!string.IsNullOrWhiteSpace(Initials))
{
return true;
}
if (PersonDetails == null)
{
Initials = null;
return false;
}
string initials = null;
if (PersonDetails?.GivenName?.Length > 0 && PersonDetails?.Surname?.Length > 0)
{
initials = string.Empty + PersonDetails.GivenName[0] + PersonDetails.Surname[0];
}
else if (PersonDetails?.DisplayName?.Length > 0)
{
// Grab first two initials in name
var nameParts = PersonDetails.DisplayName.ToUpper().Split(' ').Select(i => i.First());
initials = string.Join(string.Empty, nameParts.Where(i => char.IsLetter(i)).Take(2));
}
Initials = initials;
return Initials != null;
}
private async Task<(bool Success, BitmapImage Image)> TryDecodeStreamAsync(Stream photoStream)
{
if (photoStream != null)
{
using (var ras = photoStream.AsRandomAccessStream())
try
{
using var ras = photoStream.AsRandomAccessStream();
var bitmap = new BitmapImage();
await bitmap.SetSourceAsync(ras);
UserPhoto = bitmap;
return (true, bitmap);
}
catch
{
}
}
return (false, null);
}
}
}

Просмотреть файл

@ -64,30 +64,76 @@
</Grid>
</Grid>
<controls:SwitchPresenter
Grid.Column="1"
Value="{Binding PersonViewType, RelativeSource={RelativeSource TemplatedParent}, Converter={StaticResource ObjectToStringConverter}}"
VerticalAlignment="Center">
<!-- One line -->
<controls:Case Value="OneLine">
<TextBlock Margin="6,-2,2,0"
FontWeight="SemiBold"
Text="{Binding PersonDetails.DisplayName, RelativeSource={RelativeSource TemplatedParent}}" />
</controls:Case>
<Grid Grid.Column="1">
<Grid Name="LoadingContent" Visibility="Collapsed"></Grid>
<!-- Two lines -->
<controls:Case Value="TwoLines">
<StackPanel Margin="6,-2,2,0" Spacing="2">
<TextBlock
FontWeight="SemiBold"
Text="{Binding PersonDetails.DisplayName, RelativeSource={RelativeSource TemplatedParent}}"/>
<TextBlock
FontSize="12"
Text="{Binding PersonDetails.ScoredEmailAddresses[0].Address, RelativeSource={RelativeSource TemplatedParent}}" />
</StackPanel>
</controls:Case>
</controls:SwitchPresenter>
<Grid Name="SignedInContent" Visibility="Collapsed">
<controls:SwitchPresenter
Value="{Binding PersonViewType, RelativeSource={RelativeSource TemplatedParent}, Converter={StaticResource ObjectToStringConverter}}"
VerticalAlignment="Center">
<!-- Avatar -->
<controls:Case Value="Avatar" IsDefault="True" />
<!-- One line -->
<controls:Case Value="OneLine">
<TextBlock Margin="6,-2,2,0"
FontWeight="SemiBold"
Text="{Binding PersonDetails.DisplayName, RelativeSource={RelativeSource TemplatedParent}}" />
</controls:Case>
<!-- Two lines -->
<controls:Case Value="TwoLines">
<StackPanel Margin="6,-2,2,0" Spacing="2">
<TextBlock
FontWeight="SemiBold"
Text="{Binding PersonDetails.DisplayName, RelativeSource={RelativeSource TemplatedParent}}"/>
<TextBlock
FontSize="12"
Text="{Binding PersonDetails.ScoredEmailAddresses[0].Address, RelativeSource={RelativeSource TemplatedParent}}" />
</StackPanel>
</controls:Case>
<!-- Three lines-->
<controls:Case Value="ThreeLines">
<StackPanel Margin="6,-2,2,0" Spacing="2" VerticalAlignment="Center">
<TextBlock
FontWeight="SemiBold"
Text="{Binding PersonDetails.DisplayName, RelativeSource={RelativeSource TemplatedParent}}"/>
<TextBlock
FontSize="12"
Text="{Binding PersonDetails.ScoredEmailAddresses[0].Address, RelativeSource={RelativeSource TemplatedParent}}" />
<TextBlock
FontSize="12"
Text="{Binding PersonDetails.JobTitle, RelativeSource={RelativeSource TemplatedParent}}" />
</StackPanel>
</controls:Case>
</controls:SwitchPresenter>
</Grid>
<Grid Name="SignedOutContent" Visibility="Collapsed">
<TextBlock>foobar</TextBlock>
</Grid>
</Grid>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="ProviderStates">
<VisualState x:Name="Loading">
<VisualState.Setters>
<Setter Target="LoadingContent.Visibility" Value="Visible" />
</VisualState.Setters>
</VisualState>
<VisualState x:Name="SignedIn">
<VisualState.Setters>
<Setter Target="SignedInContent.Visibility" Value="Visible" />
</VisualState.Setters>
</VisualState>
<VisualState x:Name="SignedOut">
<VisualState.Setters>
<Setter Target="SignedOutContent.Visibility" Value="Visible" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
</Grid>
</ControlTemplate>
</Setter.Value>

Просмотреть файл

@ -9,19 +9,24 @@ namespace CommunityToolkit.Graph.Uwp.Controls
/// </summary>
public enum PersonViewType
{
/**
* Render only the avatar
*/
/// <summary>
/// Render only the avatar.
/// </summary>
Avatar = 0,
/**
* Render the avatar and one line of text
*/
/// <summary>
/// Render the avatar and one line of text.
/// </summary>
OneLine = 1,
/**
* Render the avatar and two lines of text
*/
/// <summary>
/// Render the avatar and two lines of text.
/// </summary>
TwoLines = 2,
/// <summary>
/// Render the avatar and three lines of text.
/// </summary>
ThreeLines = 3,
}
}

Просмотреть файл

@ -20,15 +20,7 @@ namespace CommunityToolkit.Graph.Extensions
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
public static async Task<User> GetMeAsync(this GraphServiceClient graph)
{
try
{
return await graph.Me.Request().GetAsync();
}
catch
{
}
return null;
return await graph.Me.Request().GetAsync();
}
/// <summary>
@ -39,15 +31,7 @@ namespace CommunityToolkit.Graph.Extensions
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
public static async Task<User> GetUserAsync(this GraphServiceClient graph, string userId)
{
try
{
return await graph.Users[userId].Request().GetAsync();
}
catch
{
}
return null;
return await graph.Users[userId].Request().GetAsync();
}
/// <summary>
@ -58,20 +42,12 @@ namespace CommunityToolkit.Graph.Extensions
/// <returns><see cref="IGraphServiceUsersCollectionPage"/> collection of <see cref="User"/>.</returns>
public static async Task<IGraphServiceUsersCollectionPage> FindUserAsync(this GraphServiceClient graph, string query)
{
try
{
return await graph
.Users
.Request()
.Filter($"startswith(displayName, '{query}') or startswith(givenName, '{query}') or startswith(surname, '{query}') or startswith(mail, '{query}') or startswith(userPrincipalName, '{query}')")
////.WithScopes(new string[] { "user.readbasic.all" })
.GetAsync();
}
catch
{
}
return new GraphServiceUsersCollectionPage();
return await graph
.Users
.Request()
.Filter($"startswith(displayName, '{query}') or startswith(givenName, '{query}') or startswith(surname, '{query}') or startswith(mail, '{query}') or startswith(userPrincipalName, '{query}')")
////.WithScopes(new string[] { "user.readbasic.all" })
.GetAsync();
}
/// <summary>
@ -82,21 +58,13 @@ namespace CommunityToolkit.Graph.Extensions
/// <returns>Stream with user photo or null.</returns>
public static async Task<Stream> GetUserPhoto(this GraphServiceClient graph, string userId)
{
try
{
return await graph
.Users[userId]
.Photo
.Content
.Request()
////.WithScopes(new string[] { "user.readbasic.all" })
.GetAsync();
}
catch
{
}
return null;
return await graph
.Users[userId]
.Photo
.Content
.Request()
////.WithScopes(new string[] { "user.readbasic.all" })
.GetAsync();
}
/// <summary>
@ -106,21 +74,13 @@ namespace CommunityToolkit.Graph.Extensions
/// <returns>Stream with user photo or null.</returns>
public static async Task<Stream> GetMyPhotoAsync(this GraphServiceClient graph)
{
try
{
return await graph
.Me
.Photo
.Content
.Request()
////.WithScopes(new string[] { "user.read" })
.GetAsync();
}
catch
{
}
return null;
return await graph
.Me
.Photo
.Content
.Request()
////.WithScopes(new string[] { "user.read" })
.GetAsync();
}
}
}

Просмотреть файл

@ -49,6 +49,9 @@
<Setter Property="Margin" Value="8,8,8,0" />
</Style>
</Pivot.ItemContainerStyle>
<PivotItem Header="PersonView">
<samples:PersonViewSample />
</PivotItem>
<PivotItem Header="RoamingSettings">
<samples:RoamingSettingsView />
</PivotItem>

Просмотреть файл

@ -123,6 +123,9 @@
<DependentUpon>MainPage.xaml</DependentUpon>
</Compile>
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Samples\PersonView\PersonViewSample.xaml.cs">
<DependentUpon>PersonViewSample.xaml</DependentUpon>
</Compile>
<Compile Include="Samples\RoamingSettings\RoamingSettingsView.xaml.cs">
<DependentUpon>RoamingSettingsView.xaml</DependentUpon>
</Compile>
@ -135,6 +138,7 @@
</ItemGroup>
<ItemGroup>
<Content Include="Assets\FileIcon.png" />
<None Include="Package.StoreAssociation.xml" />
<Content Include="Properties\Default.rd.xml" />
<Content Include="Assets\LockScreenLogo.scale-200.png" />
<Content Include="Assets\SplashScreen.scale-200.png" />
@ -153,6 +157,10 @@
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
<Page Include="Samples\PersonView\PersonViewSample.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Samples\RoamingSettings\RoamingSettingsView.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>

Просмотреть файл

@ -0,0 +1,26 @@
<Page
x:Class="SampleTest.Samples.PersonViewSample"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:SampleTest.Samples"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:controls="using:CommunityToolkit.Graph.Uwp.Controls"
mc:Ignorable="d"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<StackPanel>
<TextBlock Text="Avatar" />
<controls:PersonView PersonQuery="me" PersonViewType="Avatar" />
<TextBlock Text="OneLine" />
<controls:PersonView PersonQuery="me" PersonViewType="OneLine" />
<TextBlock Text="TwoLines" />
<controls:PersonView PersonQuery="me" PersonViewType="TwoLines" />
<TextBlock Text="ThreeLines" />
<controls:PersonView PersonQuery="me" PersonViewType="ThreeLines" />
<TextBlock Text="Avatar w/ Initials" />
<controls:PersonView PersonQuery="me" PersonViewType="Avatar" PersonAvatarType="Initials" />
<TextBlock Text="TwoLines w/ Initials" />
<controls:PersonView PersonQuery="me" PersonViewType="TwoLines" PersonAvatarType="Initials" />
</StackPanel>
</Page>

Просмотреть файл

@ -0,0 +1,16 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using Windows.UI.Xaml.Controls;
namespace SampleTest.Samples
{
public sealed partial class PersonViewSample : Page
{
public PersonViewSample()
{
this.InitializeComponent();
}
}
}