Microsoft 365 Microsoft Teams Windows

Building a Windows Presentation Foundation (WPF) Microsoft Teams App

Author: Alessandro Avila, a Microsoft Apps Consultant developer

Today’s guest blog post comes from Alessandro Avila, a Microsoft Apps Consultant developer, and his experiences with app development, cloud migration, and overall digital transformation at ABB – a global leader in robotics, power, heavy electrical equipment, and automation technology based in Europe.


Teams is the hub for teamwork in Microsoft 365 – bringing together people, chat, content, calls and apps in a central space for collaboration and productivity. Enterprises across the globe rely on the rich features provided by the platform to build powerful workspaces and provide valuable feedback to the Teams product group to further enhance those experiences. This post covers my recent experience with an enterprise customer, ABB – a global digital transformation leader, helping their marketing organization through their Teams adoption, as well as custom app solution development to address some of their process automation needs.

ABB’s marketing organization was an early adopter of Teams – and heavily used the Microsoft Planner app integration in Teams to organize activities and tasks in a systematic and structured fashion and assign them to staff across different divisions and geos. For those unfamiliar with the app, Microsoft Planner is a project management application that allows teams to create, assign and organize work visually to better facilitate teamwork and monitor progress. With the high usage of Teams and the Planner integration, our customer needed a solution that would be able to automate the capability of copying Planner plans (including tasks/activities/content) in bulk across different Teams teams – eliminating the time-consuming manual copy process that it was performing currently.

In this post we’ll walk you through our custom app solution demo we built for ABB to address their need and requirements. We’ll cover knowledge areas spanning across Teams app development, Graph APIs, C#, and Azure.

Planner app integration in Teams

Teams extends content and web services through ‘apps’ that enable one or more capabilities teams can use in their daily work for collaboration and productivity. You can take advantage of the many Microsoft and 3rd-party app integrations available in the Teams store or you can even build your own custom app for your organization or widely-available to the public.

Screenshot of different apps available in Teams for addition as a tab in a Teams channel

With the Microsoft Planner app integration in Teams, teams can create custom plans, create tasks, and assign work item all within a Teams channel. Tasks are organized in Buckets (Kanban) by default and enhanced with contextual details (Task progress, Start Date, Due Date, Checklist) that fosters teamwork, collaboration, and productivity.

Screenshot of Planner plan’s boards view Screenshot of Planner’s task details pane


The Planner app integration in Teams, provides the ability to copy an existing Planner plan as a tab. However, there are a couple limitations which impact the user experience for these migration scenarios:

  1. Provides only an ‘intra-team’ copy (i.e., you can create a copy of an existing plan only within the same Team where the source plan resides);
  2. By selecting an existing plan and adding it as a tab, the copied plan is actually a pointer to the previous one: any changes made to the newly created plan would affect the source plan; in other words, it’s a shallow copy, not a deep copy.

Screenshot of copy plan option

To address these limitations that was impacting our client’s experience, we built a custom app solution (“TeamsApp”) which enables an inter-Teams copy of Planner Plans, with full copy of underlying Buckets and Tasks and gives IT Admins a tool that can be used to set up tasks within a new team.


Before we dive into the demo, we’ll walk through some of the prerequisites you’ll need to complete.

We’ll be leveraging Microsoft Graph APIs, so a Microsoft 365 tenant is needed. We recommend using a developer tenancy for this and you can get one if you register for the Microsoft 365 Developer Program here.  An Azure AD App needs to be registered in the Azure Portal, as well.

Having these set up will enable communication between ‘TeamsApp’ and the Microsoft Identity Platform of the specific tenant and establishes the information that it uses to get OAuth Access Tokens. Through the registered Azure AD App, the ‘TeamsApp’ can access Microsoft 365 organization resources.

Azure AD App Registration

(For a detailed description, follow the steps explained in the Microsoft Docs).

  1. Sign into the Azure Portal with a Work or School / Personal Account.
  2. If the specified account gives access to more than one tenant, select the desired tenant.
  3. From the left-menu: Azure Active Directory -> App registrations -> New registration.

Azure Registration Window

4. Fill in the mandatory fields:

a. Name: the app name (e.g.: “TeamsApp”).

b. Supported account types: it depends on which accounts will access TeamsApp and Graph APIs through the AD v2.0 application. You can choose between:

i. (default) Accounts in this organizational directory <yourOrgName>: this option maps to Azure AD only single tenant; select this option if you’re building a line-of-business application;

ii. Accounts in any organizational directory: this option maps to Azure AD only multi-tenant;

iii. Accounts in any organizational directory and personal Microsoft accounts (e.g. Skype, Xbox,; this option maps to Azure AD multi-tenant and personal Microsoft accounts; this option targets the widest set of customers. Note: This was the option we used.

c. Redirect URI: it’s URI AD will return the authentication response to, after successfully authenticating the user.

i. Select “Public client (mobile & desktop)” from the first dropdown;

ii. Set the value to “urn:ietf:wg:oauth:2.0:oob” in the textbox.

5. Click on Register.

App registration window

6. On the application page, take note of the Application (client) ID field, we’ll need it when it comes to properly setting the authentication properties in our app;

7. Since we are using NET as .NET authentication library (v4.9.0) to get tokens from the AD identity platform, we need to get back to our application and change the Redirect URI field.

8. From the Suggested Redirect URIs section, select the URI in the form:

msal<appclientID>://auth (MSAL only)

9. Take note of the new Redirect URI as well.

App Permissions

Since ‘TeamsApp’ integrates with Microsoft Identity Platform, it follows an authorization model that gives users control over how data can be accessed; see Microsoft Docs for more information about permissions and consent.

Specifying the app’s permissions (or scopes), we have fine-grained control over data our APIs can access to perform their functions.

For our purposes, we need to setup two permissions:

  1. Read.All: Sign in and read user profile
  2. ReadWrite.All: Read and write all groups

Here are the permissions already granted for the demo AD App:

Permissions screen

Let’s add a new permission:

  1. Click on “Add a permission” button;
  2. Select “Microsoft Graph” as Microsoft APIs, then Delegated permissions as type of permissions, since ‘TeamsApp’ needs to access the API as the signed-in user.
  3. Look for “Group.ReadWrite.All”, check it and click on Add permissions button.

Permissions screen

4. Since Admin consent is required for this permission, click on “Grant Admin consent for Contoso”.

Consent screen

The ‘TeamsApp’ Solution

Solution Architecture

We developed ‘TeamsApp’ with Visual Studio, and it contains the following two projects:

  • TeamsApp.Lib: Class library serves as the “core” solution and provides the building blocks of the architecture: access to APIs, Authentication Layer, Factory Pattern, Helpers, Extension Methods, Models and Settings

Project structure

  • TeamsApp.WPF: The main windows application built with the WPF (Windows Presentation Foundation) framework; one main window (MainWindow) from where all operations will be performed

Project structure

Either two projects are built using .NET Framework 4.7.2 (even though some C# 8.0 Preview 2 features are used – the inline using, to mention the most recurrent).

Authentication and access

To interact with Microsoft Graph REST APIs, a JSON-like access token is required and is provided by the Microsoft Identity Platform (Azure AD v2.0). It’s needed to validate the caller (the Azure AD App) and check that it has the proper permissions to access Graph resources (each API endpoint will expose different resources, each of them requires different permissions, as you can discover in more details through this link).

‘TeamsApp’ uses MSAL.NET (Microsoft Authentication Library for .NET) to easily get tokens and allow users to sign in with their work or school accounts (it depends on how you configured the Azure AD App in Azure AD App Registration (4.b).

The class responsible for authentication is AuthenticationHelper (Helpers\AuthenticationHelper.cs in TeamsApp.Lib). This class implements the Singleton design pattern to expose a single instance of an authentication provider that’d be used for the entire application’execution; ClientID and ReturnUrl must be set in the App.xaml file (TeamsApp.WPF) before running the application, with the values provided during the App registration in the Azure Portal (see Azure AD App Registration for details); replace placeholders in the tags ida:ClientID and ida:ReturnUrl:

<system:String x:Key=”ida:ClientID”>[put your ClientID here]</system:String>
<system:String x:Key=”ida:ReturnUrl”>[put your ReplaceUrl here]</system:String>

Graph APIs

Microsoft Graph exposes several endpoints to access Microsoft Cloud service resources. After getting the authentication token (see previous section), you can access resources through HTTP calls that look like the following:

{HTTP method}{version}/{resource}?{query-parameters}

The components of a requests are:

  • {HTTP method}: the method used to perform the request to Microsoft Graph; the API supports the following methods: GET, POST, PATCH, PUT, DELETE;
  • {version}: the version of the Microsoft Graph API your application is using; TeamsApp lets you to choose between the following two versions (see O365Settings static class):
    • 0: includes GA (Generally Available APIs);
    • beta: preview APIs, this is the default in TeamsApp (feel free to change it).
  • {resource}: the resource we are performing a request to;
  • {query-parameters}: optional REST parameters.

In the TeamsApp.Lib\Api folder the API abstract class represents the root type for managing Graph API calls. The HttpClientApi class inherits from API since it’s a specialization of it. Each HttClientApi sub-type (BucketApi, PlannerApi, TaskApi, etc.) exposes properties and helper methods (GET/POST, mainly) to access resources of that specific sub-type. Each sub-type inherits from HttpClientApi class.

‘TeamsApp’ provides a caching abstraction layer (Factory\TeamsFactory) to quickly get the correct API type from the list of more frequently accessed APIs; the solution leverages on the CacheManager.Core NuGet package.


The logging layer in ‘TeamsApp’ is based on a telemetry library called Common.Diagnostics, a brand-new .NET solution built upon System.Diagnostics for categorized and multi-layered tracing by means of listeners – supported by .NET framework 4.6.2+ and .NET Core 3.0+. Under the ‘Logs’ folder, you’ll find an early version of its core class (TraceManager.cs). For more information about how to use the whole library, have access to the documentation and .nuget packages, please visit the official repo at


Now we’ll show how to use the ‘TeamsApp application to perform a copy of a Planner from a Team to another.

Source Team: Contoso

Destination Team: Sales and Marketing

Let’s start by creating a new Planner tab called Planner.Source, in the General channel:

 Planner.Source tab

We’ll be providing the Planner with the following content:

  • Two buckets: “To Do” and “Another bucket”
  • Two tasks for each bucket (named progressively, as you can see from the picture above)

An empty destination Planner (Planner.Dest) has been created in the Sales and Marketing Team:

Planner.Dest tab

Step 1: Click Connect button and provide user credentials:

Main page of ‘TeamsApp’ app

Step 2: Click Load Teams and select either Source Team or Destination Team from related dropdowns. The user will get all available Planners from selected teams.

 Load teams button within the ‘TeamsApp’

Step 3: Select Planner.Source as source planner and Planner.Dest as destination planner.

Source planner and destination planner inputs

Step 4: Click on COPY button and wait for the planner copy to complete.

Completion of the copy

As you can see here, the Planner.Dest’s tab has all of the content migrated into the relevant buckets after copy.

View of final content migration


Here’s the link to the official ‘TeamsApp’ GitHub repo:


My heartfelt thanks to Dario Airoldi for supporting me during the development of this application. His ‘never give up’ approach and determination helped exceed our customer’s expectations and provided useful insights via his Common.Diagnostics telemetry library (see Logs section). That greatly helped me in troubleshooting the first application runs with the customer.