Creating Dialogs
If you're not familiar with how to build Adaptive Cards, check out the cards guide. Understanding their basics is a prerequisite for this guide.
Entry Point
To open a dialog, you need to supply a special type of action to the Adaptive Card. The TaskFetchAction
is specifically designed for this purpose - it automatically sets up the proper Teams data structure to trigger a dialog. Once this button is clicked, the dialog will open and ask the application what to show.
[Message]
public async Task OnMessage([Context] MessageActivity activity, [Context] IContext.Client client, [Context] ILogger log)
{
// Create the launcher adaptive card
var card = CreateDialogLauncherCard();
await client.Send(card);
}
private static AdaptiveCard CreateDialogLauncherCard()
{
var card = new AdaptiveCard
{
Body = new List<CardElement>
{
new TextBlock("Select the examples you want to see!")
{
Size = TextSize.Large,
Weight = TextWeight.Bolder
}
},
Actions = new List<Action>
{
new TaskFetchAction(new { opendialogtype = "simple_form" })
{
Title = "Simple form test"
},
new TaskFetchAction(new { opendialogtype = "webpage_dialog" })
{
Title = "Webpage Dialog"
},
new TaskFetchAction(new { opendialogtype = "multi_step_form" })
{
Title = "Multi-step Form"
}
}
};
return card;
}
Handling Dialog Open Events
Once an action is executed to open a dialog, the Teams client will send an event to the agent to request what the content of the dialog should be. When using TaskFetchAction
, the data is nested inside an MsTeams
property structure.
[TaskFetch]
public Microsoft.Teams.Api.TaskModules.Response OnTaskFetch([Context] Tasks.FetchActivity activity, [Context] IContext.Client client, [Context] ILogger log)
{
var data = activity.Value?.Data as JsonElement?;
if (data == null)
{
log.Info("[TASK_FETCH] No data found in the activity value");
return new Microsoft.Teams.Api.TaskModules.Response(new Microsoft.Teams.Api.TaskModules.MessageTask("No data found in the activity value"));
}
var dialogType = data.Value.TryGetProperty("opendialogtype", out var dialogTypeElement) && dialogTypeElement.ValueKind == JsonValueKind.String
? dialogTypeElement.GetString()
: null;
log.Info($"[TASK_FETCH] Dialog type: {dialogType}");
return dialogType switch
{
"simple_form" => CreateSimpleFormDialog(),
"webpage_dialog" => CreateWebpageDialog(_configuration, log),
"multi_step_form" => CreateMultiStepFormDialog(),
"mixed_example" => CreateMixedExampleDialog(),
_ => new Microsoft.Teams.Api.TaskModules.Response(new Microsoft.Teams.Api.TaskModules.MessageTask("Unknown dialog type"))
};
}
Rendering A Card
You can render an Adaptive Card in a dialog by returning a card response.
private static Microsoft.Teams.Api.TaskModules.Response CreateSimpleFormDialog()
{
var cardJson = """
{
"type": "AdaptiveCard",
"version": "1.4",
"body": [
{
"type": "TextBlock",
"text": "This is a simple form",
"size": "Large",
"weight": "Bolder"
},
{
"type": "Input.Text",
"id": "name",
"label": "Name",
"placeholder": "Enter your name",
"isRequired": true
}
],
"actions": [
{"type": "Action.Submit", "title": "Submit", "data": {"submissiondialogtype": "simple_form"}}
]
}
""";
var dialogCard = JsonSerializer.Deserialize<AdaptiveCard>(cardJson)
?? throw new InvalidOperationException("Failed to deserialize simple form card");
var taskInfo = new TaskInfo
{
Title = "Simple Form Dialog",
Card = new Attachment
{
ContentType = new ContentType("application/vnd.microsoft.card.adaptive"),
Content = dialogCard
}
};
return new Response(new ContinueTask(taskInfo));
}
The action type for submitting a dialog must be Action.Submit
. This is a requirement of the Teams client. If you use a different action type, the dialog will not be submitted and the agent will not receive the submission event.
Rendering A Webpage
You can render a webpage in a dialog as well. There are some security requirements to be aware of:
- The webpage must be hosted on a domain that is allow-listed as
validDomains
in the Teams app manifest for the agent - The webpage must also host the teams-js client library. The reason for this is that for security purposes, the Teams client will not render arbitrary webpages. As such, the webpage must explicitly opt-in to being rendered in the Teams client. Setting up the teams-js client library handles this for you.
private static Microsoft.Teams.Api.TaskModules.Response CreateWebpageDialog(IConfiguration configuration, ILogger log)
{
var botEndpoint = configuration["BotEndpoint"];
if (string.IsNullOrEmpty(botEndpoint))
{
log.Warn("No remote endpoint detected. Using webpages for dialog will not work as expected");
botEndpoint = "http://localhost:3978"; // Fallback for local development
}
else
{
log.Info($"Using BotEndpoint: {botEndpoint}/tabs/dialog-form");
}
var taskInfo = new TaskInfo
{
Title = "Webpage Dialog",
Width = new Union<int, Size>(1000),
Height = new Union<int, Size>(800),
// Here we are using a webpage that is hosted in the same
// server as the agent. This server needs to be publicly accessible,
// needs to set up teams.js client library (https://www.npmjs.com/package/@microsoft/teams-js)
// and needs to be registered in the manifest.
Url = $"{botEndpoint}/tabs/dialog-form"
};
return new Response(new ContinueTask(taskInfo));
}
Setting up Embedded Web Content
To serve web content for dialogs, you can use the AddTab
functionality to embed HTML files as resources:
// In Program.cs when building your app
app.UseTeams();
app.AddTab("dialog-form", "Web/dialog-form");
// Configure project file to embed web resources
// In .csproj:
// <GenerateEmbeddedFilesManifest>true</GenerateEmbeddedFilesManifest>
// <EmbeddedResource Include="Web/**" />
// <Content Remove="Web/**" />