diff --git a/CommunityToolkit.Graph.Uwp/Controls/LoginButton/LoginButton.cs b/CommunityToolkit.Graph.Uwp/Controls/LoginButton/LoginButton.cs index 991d6fa..55784ee 100644 --- a/CommunityToolkit.Graph.Uwp/Controls/LoginButton/LoginButton.cs +++ b/CommunityToolkit.Graph.Uwp/Controls/LoginButton/LoginButton.cs @@ -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. + } } } diff --git a/CommunityToolkit.Graph.Uwp/Controls/PeoplePicker/PeoplePicker.cs b/CommunityToolkit.Graph.Uwp/Controls/PeoplePicker/PeoplePicker.cs index 3203c87..337605e 100644 --- a/CommunityToolkit.Graph.Uwp/Controls/PeoplePicker/PeoplePicker.cs +++ b/CommunityToolkit.Graph.Uwp/Controls/PeoplePicker/PeoplePicker.cs @@ -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().Select(person => (person as Person).Id); + if (peopleCollection != null) + { + // Grab ids of current suggestions + var ids = list.Cast().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); + } } } } diff --git a/CommunityToolkit.Graph.Uwp/Controls/PersonView/PersonAvatarType.cs b/CommunityToolkit.Graph.Uwp/Controls/PersonView/PersonAvatarType.cs new file mode 100644 index 0000000..b75946a --- /dev/null +++ b/CommunityToolkit.Graph.Uwp/Controls/PersonView/PersonAvatarType.cs @@ -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 +{ + /// + /// The type of visual representation to use for the person. + /// + public enum PersonAvatarType + { + /// + /// Show a user's photo if available, otherwise show initials. + /// + Photo, + + /// + /// Show the user's initials only. + /// + Initials, + } +} diff --git a/CommunityToolkit.Graph.Uwp/Controls/PersonView/PersonView.Properties.cs b/CommunityToolkit.Graph.Uwp/Controls/PersonView/PersonView.Properties.cs index 1b47d42..26db389 100644 --- a/CommunityToolkit.Graph.Uwp/Controls/PersonView/PersonView.Properties.cs +++ b/CommunityToolkit.Graph.Uwp/Controls/PersonView/PersonView.Properties.cs @@ -135,5 +135,20 @@ namespace CommunityToolkit.Graph.Uwp.Controls /// public static readonly DependencyProperty PersonViewTypeProperty = DependencyProperty.Register(nameof(PersonViewType), typeof(PersonViewType), typeof(PersonView), new PropertyMetadata(PersonViewType.TwoLines, PersonViewTypePropertiesChanged)); + + /// + /// Gets or sets the type of visual to display in the image part of the template. + /// + public PersonAvatarType PersonAvatarType + { + get => (PersonAvatarType)GetValue(PersonAvatarTypeProperty); + set => SetValue(PersonAvatarTypeProperty, value); + } + + /// + /// Identifies the dependency property. + /// + public static readonly DependencyProperty PersonAvatarTypeProperty = + DependencyProperty.Register(nameof(PersonAvatarType), typeof(PersonAvatarType), typeof(PersonView), new PropertyMetadata(PersonAvatarType.Photo, PersonAvatarTypePropertiesChanged)); } } diff --git a/CommunityToolkit.Graph.Uwp/Controls/PersonView/PersonView.cs b/CommunityToolkit.Graph.Uwp/Controls/PersonView/PersonView.cs index 24ec786..859dea1 100644 --- a/CommunityToolkit.Graph.Uwp/Controls/PersonView/PersonView.cs +++ b/CommunityToolkit.Graph.Uwp/Controls/PersonView/PersonView.cs @@ -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 /// public partial class PersonView : Control { - private const string PersonViewDefaultImageSourceResourceName = "PersonViewDefaultImageSource"; - /// /// value used to retrieve the signed-in user's info. /// 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; + /// /// Initializes a new instance of the class. /// public PersonView() { this.DefaultStyleKey = typeof(PersonView); - - _defaultImage = new BitmapImage(new Uri(_defaultImageSource)); - - ProviderManager.Instance.ProviderStateChanged += (sender, args) => LoadData(); } /// @@ -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 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 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); } } } diff --git a/CommunityToolkit.Graph.Uwp/Controls/PersonView/PersonView.xaml b/CommunityToolkit.Graph.Uwp/Controls/PersonView/PersonView.xaml index b9988f3..ad2929d 100644 --- a/CommunityToolkit.Graph.Uwp/Controls/PersonView/PersonView.xaml +++ b/CommunityToolkit.Graph.Uwp/Controls/PersonView/PersonView.xaml @@ -64,30 +64,76 @@ - - - - - + + - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + foobar + + + + + + + + + + + + + + + + + + + + + + diff --git a/CommunityToolkit.Graph.Uwp/Controls/PersonView/PersonViewType.cs b/CommunityToolkit.Graph.Uwp/Controls/PersonView/PersonViewType.cs index d80c36b..1b86057 100644 --- a/CommunityToolkit.Graph.Uwp/Controls/PersonView/PersonViewType.cs +++ b/CommunityToolkit.Graph.Uwp/Controls/PersonView/PersonViewType.cs @@ -9,19 +9,24 @@ namespace CommunityToolkit.Graph.Uwp.Controls /// public enum PersonViewType { - /** - * Render only the avatar - */ + /// + /// Render only the avatar. + /// Avatar = 0, - /** - * Render the avatar and one line of text - */ + /// + /// Render the avatar and one line of text. + /// OneLine = 1, - /** - * Render the avatar and two lines of text - */ + /// + /// Render the avatar and two lines of text. + /// TwoLines = 2, + + /// + /// Render the avatar and three lines of text. + /// + ThreeLines = 3, } } diff --git a/CommunityToolkit.Graph/Extensions/GraphExtensions.Users.cs b/CommunityToolkit.Graph/Extensions/GraphExtensions.Users.cs index 7f0d381..6a641ca 100644 --- a/CommunityToolkit.Graph/Extensions/GraphExtensions.Users.cs +++ b/CommunityToolkit.Graph/Extensions/GraphExtensions.Users.cs @@ -20,15 +20,7 @@ namespace CommunityToolkit.Graph.Extensions /// A representing the asynchronous operation. public static async Task GetMeAsync(this GraphServiceClient graph) { - try - { - return await graph.Me.Request().GetAsync(); - } - catch - { - } - - return null; + return await graph.Me.Request().GetAsync(); } /// @@ -39,15 +31,7 @@ namespace CommunityToolkit.Graph.Extensions /// A representing the asynchronous operation. public static async Task GetUserAsync(this GraphServiceClient graph, string userId) { - try - { - return await graph.Users[userId].Request().GetAsync(); - } - catch - { - } - - return null; + return await graph.Users[userId].Request().GetAsync(); } /// @@ -58,20 +42,12 @@ namespace CommunityToolkit.Graph.Extensions /// collection of . public static async Task 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(); } /// @@ -82,21 +58,13 @@ namespace CommunityToolkit.Graph.Extensions /// Stream with user photo or null. public static async Task 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(); } /// @@ -106,21 +74,13 @@ namespace CommunityToolkit.Graph.Extensions /// Stream with user photo or null. public static async Task 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(); } } } diff --git a/SampleTest/MainPage.xaml b/SampleTest/MainPage.xaml index a762b91..edd557b 100644 --- a/SampleTest/MainPage.xaml +++ b/SampleTest/MainPage.xaml @@ -49,6 +49,9 @@ + + + diff --git a/SampleTest/SampleTest.csproj b/SampleTest/SampleTest.csproj index eb8508b..75520a4 100644 --- a/SampleTest/SampleTest.csproj +++ b/SampleTest/SampleTest.csproj @@ -123,6 +123,9 @@ MainPage.xaml + + PersonViewSample.xaml + RoamingSettingsView.xaml @@ -135,6 +138,7 @@ + @@ -153,6 +157,10 @@ MSBuild:Compile Designer + + Designer + MSBuild:Compile + MSBuild:Compile Designer diff --git a/SampleTest/Samples/PersonView/PersonViewSample.xaml b/SampleTest/Samples/PersonView/PersonViewSample.xaml new file mode 100644 index 0000000..6f76452 --- /dev/null +++ b/SampleTest/Samples/PersonView/PersonViewSample.xaml @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + diff --git a/SampleTest/Samples/PersonView/PersonViewSample.xaml.cs b/SampleTest/Samples/PersonView/PersonViewSample.xaml.cs new file mode 100644 index 0000000..b9e062c --- /dev/null +++ b/SampleTest/Samples/PersonView/PersonViewSample.xaml.cs @@ -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(); + } + } +}