diff --git a/Microsoft.Toolkit.Graph.Controls/Controls/GraphPresenter/GraphPresenter.cs b/Microsoft.Toolkit.Graph.Controls/Controls/GraphPresenter/GraphPresenter.cs
new file mode 100644
index 0000000..5fa1a3c
--- /dev/null
+++ b/Microsoft.Toolkit.Graph.Controls/Controls/GraphPresenter/GraphPresenter.cs
@@ -0,0 +1,113 @@
+// 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 System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading;
+using Microsoft.Graph;
+using Microsoft.Toolkit.Uwp.Helpers;
+using Newtonsoft.Json.Linq;
+using Windows.UI.Xaml;
+using Windows.UI.Xaml.Controls;
+
+namespace Microsoft.Toolkit.Graph.Controls
+{
+ ///
+ /// Specialized to fetch and display data from the Microsoft Graph.
+ ///
+ public class GraphPresenter : ContentPresenter
+ {
+ ///
+ /// Gets or sets a to be used to make a request to the graph. The results will be automatically populated to the property. Use a to change the presentation of the data.
+ ///
+ public IBaseRequestBuilder RequestBuilder
+ {
+ get { return (IBaseRequestBuilder)GetValue(RequestBuilderProperty); }
+ set { SetValue(RequestBuilderProperty, value); }
+ }
+
+ ///
+ /// Identifies the dependency property.
+ ///
+ ///
+ /// The identifier for the dependency property.
+ ///
+ public static readonly DependencyProperty RequestBuilderProperty =
+ DependencyProperty.Register(nameof(RequestBuilder), typeof(IBaseRequestBuilder), typeof(GraphPresenter), new PropertyMetadata(null));
+
+ ///
+ /// Gets or sets the of item returned by the .
+ /// Set to the base item type and use the property to indicate if a collection is expected back.
+ ///
+ public Type ResponseType { get; set; }
+
+ ///
+ /// Gets or sets a value indicating whether the returned data from the is a collection.
+ ///
+ public bool IsCollection { get; set; }
+
+ ///
+ /// Gets or sets list of representing values to pass into the request built by .
+ ///
+ public List QueryOptions { get; set; } = new List();
+
+ ///
+ /// Gets or sets a string to indicate a sorting order for the . This is a helper to add this specific request option to the .
+ ///
+ public string OrderBy { get; set; }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public GraphPresenter()
+ {
+ Loaded += GraphPresenter_Loaded;
+ }
+
+ private async void GraphPresenter_Loaded(object sender, RoutedEventArgs e)
+ {
+ // Note: some interfaces from the Graph SDK don't implement IBaseRequestBuilder properly, see https://github.com/microsoftgraph/msgraph-sdk-dotnet/issues/722
+ if (RequestBuilder != null)
+ {
+ var request = new BaseRequest(
+ RequestBuilder.RequestUrl,
+ RequestBuilder.Client); // TODO: Do we need separate Options here?
+ request.Method = "GET";
+ request.QueryOptions = QueryOptions?.Select(option => (Microsoft.Graph.QueryOption)option)?.ToList() ?? new List();
+
+ // Handle Special QueryOptions
+ if (!string.IsNullOrWhiteSpace(OrderBy))
+ {
+ request.QueryOptions.Add(new Microsoft.Graph.QueryOption("$orderby", OrderBy));
+ }
+
+ try
+ {
+ var response = await request.SendAsync(null, CancellationToken.None).ConfigureAwait(false) as JObject;
+
+ //// TODO: Deal with paging?
+
+ var values = response["value"];
+ object data = null;
+
+ if (IsCollection)
+ {
+ data = values.ToObject(Array.CreateInstance(ResponseType, 0).GetType());
+ }
+ else
+ {
+ data = values.ToObject(ResponseType);
+ }
+
+ _ = DispatcherHelper.ExecuteOnUIThreadAsync(() => Content = data);
+ }
+ catch
+ {
+ // TODO: We should figure out what we want to do for Loading/Error states here.
+ }
+ }
+ }
+ }
+}
diff --git a/Microsoft.Toolkit.Graph.Controls/Controls/GraphPresenter/QueryOption.cs b/Microsoft.Toolkit.Graph.Controls/Controls/GraphPresenter/QueryOption.cs
new file mode 100644
index 0000000..8af7159
--- /dev/null
+++ b/Microsoft.Toolkit.Graph.Controls/Controls/GraphPresenter/QueryOption.cs
@@ -0,0 +1,34 @@
+// 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 System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Windows.UI.Xaml;
+
+namespace Microsoft.Toolkit.Graph.Controls
+{
+ ///
+ /// XAML Proxy for .
+ ///
+ public sealed class QueryOption
+ {
+ ///
+ public string Name { get; set; }
+
+ ///
+ public string Value { get; set; }
+
+ ///
+ /// Implicit conversion for to .
+ ///
+ /// query option to convert.
+ public static implicit operator Microsoft.Graph.QueryOption(QueryOption option)
+ {
+ return new Microsoft.Graph.QueryOption(option.Name, option.Value);
+ }
+ }
+}
diff --git a/Microsoft.Toolkit.Graph.Controls/Microsoft.Toolkit.Graph.Controls.csproj b/Microsoft.Toolkit.Graph.Controls/Microsoft.Toolkit.Graph.Controls.csproj
index 6cd7fde..f44f769 100644
--- a/Microsoft.Toolkit.Graph.Controls/Microsoft.Toolkit.Graph.Controls.csproj
+++ b/Microsoft.Toolkit.Graph.Controls/Microsoft.Toolkit.Graph.Controls.csproj
@@ -15,6 +15,7 @@
UWP Toolkit Windows Controls MSAL Microsoft Graph AadLogin ProfileCard Person PeoplePicker Login
false
true
+ 8.0
Debug;Release;CI
AnyCPU;ARM;ARM64;x64;x86
@@ -34,8 +35,8 @@
-
-
+
+
diff --git a/Microsoft.Toolkit.Graph/Extensions/GraphExtensions.cs b/Microsoft.Toolkit.Graph/Extensions/GraphExtensions.cs
index 8e8a433..9d7668a 100644
--- a/Microsoft.Toolkit.Graph/Extensions/GraphExtensions.cs
+++ b/Microsoft.Toolkit.Graph/Extensions/GraphExtensions.cs
@@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
+using System;
using System.IO;
using System.Threading.Tasks;
using Microsoft.Graph;
diff --git a/Microsoft.Toolkit.Graph/Microsoft.Toolkit.Graph.csproj b/Microsoft.Toolkit.Graph/Microsoft.Toolkit.Graph.csproj
index 8e49be7..5164e15 100644
--- a/Microsoft.Toolkit.Graph/Microsoft.Toolkit.Graph.csproj
+++ b/Microsoft.Toolkit.Graph/Microsoft.Toolkit.Graph.csproj
@@ -21,8 +21,8 @@
-
-
+
+
diff --git a/Microsoft.Toolkit.Graph/Providers/MockProvider.cs b/Microsoft.Toolkit.Graph/Providers/MockProvider.cs
index 863c3dd..a0d54ec 100644
--- a/Microsoft.Toolkit.Graph/Providers/MockProvider.cs
+++ b/Microsoft.Toolkit.Graph/Providers/MockProvider.cs
@@ -21,6 +21,8 @@ namespace Microsoft.Toolkit.Graph.Providers
///
public class MockProvider : IProvider
{
+ private const string GRAPH_PROXY_URL = "https://proxy.apisandbox.msdn.microsoft.com/svc?url=";
+
private ProviderState _state = ProviderState.Loading;
///
@@ -42,22 +44,15 @@ private set
///
public GraphServiceClient Graph => new GraphServiceClient(
- "https://proxy.apisandbox.msdn.microsoft.com/svc?url=" + HttpUtility.HtmlEncode("https://graph.microsoft.com/beta/"),
- new DelegateAuthenticationProvider((requestMessage) =>
- {
- //// Temporary Workaround for https://github.com/microsoftgraph/msgraph-sdk-dotnet-core/issues/59
- //// ------------------------
- var requestUri = requestMessage.RequestUri.ToString();
- var index = requestUri.IndexOf("&");
- if (index >= 0)
- {
- requestMessage.RequestUri = new Uri(requestUri.Remove(index, 1).Insert(index, "?"));
- }
-
- //// End Workaround
-
- return this.AuthenticateRequestAsync(requestMessage);
- }));
+ new DelegateAuthenticationProvider((requestMessage) =>
+ {
+ var requestUri = requestMessage.RequestUri.ToString();
+
+ // Prepend Proxy Service URI to our request
+ requestMessage.RequestUri = new Uri(GRAPH_PROXY_URL + Uri.EscapeDataString(requestUri));
+
+ return this.AuthenticateRequestAsync(requestMessage);
+ }));
///
public event EventHandler StateChanged;
diff --git a/Microsoft.Toolkit.Graph/Providers/ProviderManager.cs b/Microsoft.Toolkit.Graph/Providers/ProviderManager.cs
index ba03df9..f585ef5 100644
--- a/Microsoft.Toolkit.Graph/Providers/ProviderManager.cs
+++ b/Microsoft.Toolkit.Graph/Providers/ProviderManager.cs
@@ -3,6 +3,10 @@
// See the LICENSE file in the project root for more information.
using System;
+using System.ComponentModel;
+using Microsoft.Graph;
+using Microsoft.Identity.Client;
+using Microsoft.Toolkit.Helpers;
namespace Microsoft.Toolkit.Graph.Providers
{
@@ -14,7 +18,7 @@ namespace Microsoft.Toolkit.Graph.Providers
/// ProviderManager.Instance.GlobalProvider = await MsalProvider.CreateAsync(...);
///
///
- public class ProviderManager
+ public class ProviderManager : INotifyPropertyChanged
{
///
/// Gets the name of the toolkit client to identify self in Graph calls.
@@ -31,6 +35,9 @@ public class ProviderManager
///
public event EventHandler ProviderUpdated;
+ ///
+ public event PropertyChangedEventHandler PropertyChanged;
+
private IProvider _provider;
///
@@ -58,6 +65,8 @@ public IProvider GlobalProvider
}
ProviderUpdated?.Invoke(this, new ProviderUpdatedEventArgs(ProviderManagerChangedState.ProviderChanged));
+
+ PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(GlobalProvider)));
}
}
diff --git a/SampleTest/Assets/FileIcon.png b/SampleTest/Assets/FileIcon.png
new file mode 100644
index 0000000..0435822
Binary files /dev/null and b/SampleTest/Assets/FileIcon.png differ
diff --git a/SampleTest/MainPage.xaml b/SampleTest/MainPage.xaml
index 986f831..54dc422 100644
--- a/SampleTest/MainPage.xaml
+++ b/SampleTest/MainPage.xaml
@@ -2,16 +2,19 @@
x:Class="SampleTest.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
- xmlns:local="using:SampleTest"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
+ xmlns:local="using:SampleTest"
xmlns:wgt="using:Microsoft.Toolkit.Graph.Controls"
xmlns:graph="using:Microsoft.Graph"
+ xmlns:global="using:System.Globalization"
xmlns:Interactivity="using:Microsoft.Xaml.Interactivity"
xmlns:providers="using:Microsoft.Toolkit.Graph.Providers"
+ xmlns:controls="using:Microsoft.Toolkit.Uwp.UI.Controls"
+ xmlns:ex="using:Microsoft.Toolkit.Extensions"
mc:Ignorable="d"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"
- xmlns:controls="using:Microsoft.Toolkit.Uwp.UI.Controls">
+ xmlns:controlsKeepThis="using:Microsoft.Toolkit.Uwp.UI.Controls">
-
+
-
-
-
-
- Picked People:
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+ The `LoginButton` above allows your user and application to easily connect to the Microsoft Graph. They can then also easily logout from the drop-down menu.
+
+
+
+
+
+
+ The `PeoplePicker` lets a logged in user easily search for familiar people they interact with or contacts. Great for emails or messages.
+
+
+ Picked People:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ The `GraphPresenter` is a unique control in the library which makes it easier for a developer to make any graph call and configure a nice display template in XAML. This opens up a world of possibilities for many uses outside of any other control available within this library. You can see a few examples of what's possible below.
+
+
+
+
+
+
+
+
+ The following example shows the `Me.CalendarView` API.
+ My Upcoming Calendar Events:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ -
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ The following example shows the `Me.Messages` API for getting a user's inbox mail messages.
+ My Messages:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ The following example shows the `Me.Planner.Tasks` API for getting a user's tasks.
+ My Tasks:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Due
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ The following example shows the beta `Teams/id/Channels/id/messages` API for getting a list of messages (without replies) from a Channel in Teams.
+ My Chat Messages:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/SampleTest/MainPage.xaml.cs b/SampleTest/MainPage.xaml.cs
index 2689205..428741f 100644
--- a/SampleTest/MainPage.xaml.cs
+++ b/SampleTest/MainPage.xaml.cs
@@ -2,6 +2,11 @@
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
+using Microsoft.Graph;
+using Microsoft.Graph.Extensions;
+using Microsoft.Toolkit.Graph.Providers;
+using System;
+using System.Text.RegularExpressions;
using Windows.UI.Xaml.Controls;
namespace SampleTest
@@ -11,9 +16,44 @@ namespace SampleTest
///
public sealed partial class MainPage : Page
{
+ // Workaround for https://github.com/microsoft/microsoft-ui-xaml/issues/2407
+ public DateTime Today => DateTimeOffset.Now.Date.ToUniversalTime();
+
+ // Workaround for https://github.com/microsoft/microsoft-ui-xaml/issues/2407
+ public DateTime ThreeDaysFromNow => Today.AddDays(3);
+
public MainPage()
{
this.InitializeComponent();
}
+
+ public static string ToLocalTime(DateTimeTimeZone value)
+ {
+ // Workaround for https://github.com/microsoft/microsoft-ui-xaml/issues/2407
+ return value.ToDateTimeOffset().LocalDateTime.ToString("g");
+ }
+
+ public static string ToLocalTime(DateTimeOffset? value)
+ {
+ // Workaround for https://github.com/microsoft/microsoft-ui-xaml/issues/2654
+ return value?.LocalDateTime.ToString("g");
+ }
+
+ public static string RemoveWhitespace(string value)
+ {
+ // Workaround for https://github.com/microsoft/microsoft-ui-xaml/issues/2654
+ return Regex.Replace(value, @"\t|\r|\n", " ");
+ }
+
+ public static bool IsTaskCompleted(int? percentCompleted)
+ {
+ return percentCompleted == 100;
+ }
+
+ public static IBaseRequestBuilder GetTeamsChannelMessagesBuilder(string team, string channel)
+ {
+ // Workaround for https://github.com/microsoft/microsoft-ui-xaml/issues/3064
+ return ProviderManager.Instance.GlobalProvider.Graph.Teams[team].Channels[channel].Messages;
+ }
}
}
diff --git a/SampleTest/SampleTest.csproj b/SampleTest/SampleTest.csproj
index e98c1d1..4a1c7d9 100644
--- a/SampleTest/SampleTest.csproj
+++ b/SampleTest/SampleTest.csproj
@@ -130,6 +130,7 @@
+