With component dialogs, you can create independent dialogs to handle specific scenarios, breaking a large dialog set into more manageable pieces. Each of these pieces has its own dialog set, and avoids any name collisions with the dialog sets outside of it. Component dialogs are reusable in that they can be:
Added to another ComponentDialog or DialogSet in your bot.
Exported as a part of a package.
Used within other bots.
Note
The Bot Framework JavaScript, C#, and Python SDKs will continue to be supported, however, the Java SDK is being retired with final long-term support ending in November 2023.
Existing bots built with the Java SDK will continue to function.
In the multi-turn prompt sample, we use a waterfall dialog, a few prompts, and a component dialog to create an interaction that asks the user a series of questions. The code uses a dialog to cycle through these steps:
Steps
Prompt type
Ask the user for their mode of transportation
Choice prompt
Ask the user for their name
Text prompt
Ask the user if they want to provide their age
Confirm prompt
If they answered yes, ask for their age
Number prompt with validation to only accept ages greater than 0 and less than 150.
Ask if the collected information is "ok"
Reuse Confirm prompt
Finally, if they answered yes, display the collected information; otherwise, tell the user that their information won't be kept.
Implement your component dialog
In the multi-turn prompt sample, we use a waterfall dialog, a few prompts, and a component dialog to create an interaction that asks the user a series of questions.
A component dialog encapsulates one or more dialogs. The component dialog has an inner dialog set, and the dialogs and prompts that you add to this inner dialog set have their own IDs, visible only from within the component dialog.
To use dialogs, install the Microsoft.Bot.Builder.Dialogs NuGet package.
Dialogs\UserProfileDialog.cs
Here the UserProfileDialog class derives from the ComponentDialog class.
public class UserProfileDialog : ComponentDialog
Within the constructor, the AddDialog method adds dialogs and prompts to the component dialog. The first item you add with this method is set as the initial dialog. You can change the initial dialog by explicitly setting the InitialDialogId property. When you start a component dialog, it will start its initial dialog.
public UserProfileDialog(UserState userState)
: base(nameof(UserProfileDialog))
{
_userProfileAccessor = userState.CreateProperty<UserProfile>("UserProfile");
// This array defines how the Waterfall will execute.
var waterfallSteps = new WaterfallStep[]
{
TransportStepAsync,
NameStepAsync,
NameConfirmStepAsync,
AgeStepAsync,
PictureStepAsync,
SummaryStepAsync,
ConfirmStepAsync,
};
// Add named dialogs to the DialogSet. These names are saved in the dialog state.
AddDialog(new WaterfallDialog(nameof(WaterfallDialog), waterfallSteps));
AddDialog(new TextPrompt(nameof(TextPrompt)));
AddDialog(new NumberPrompt<int>(nameof(NumberPrompt<int>), AgePromptValidatorAsync));
AddDialog(new ChoicePrompt(nameof(ChoicePrompt)));
AddDialog(new ConfirmPrompt(nameof(ConfirmPrompt)));
AddDialog(new AttachmentPrompt(nameof(AttachmentPrompt), PicturePromptValidatorAsync));
// The initial child Dialog to run.
InitialDialogId = nameof(WaterfallDialog);
}
The following code represents the first step of the waterfall dialog.
private static async Task<DialogTurnResult> NameStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
{
stepContext.Values["transport"] = ((FoundChoice)stepContext.Result).Value;
return await stepContext.PromptAsync(nameof(TextPrompt), new PromptOptions { Prompt = MessageFactory.Text("Please enter your name.") }, cancellationToken);
}
To use dialogs, your project needs to install the botbuilder-dialogs npm package.
dialogs/userProfileDialog.js
Here the UserProfileDialog class extends ComponentDialog.
class UserProfileDialog extends ComponentDialog {
Within the constructor, the AddDialog method adds dialogs and prompts to the component dialog. The first item you add with this method is set as the initial dialog. You can change the initial dialog by explicitly setting the InitialDialogId property. When you start a component dialog, it will start its initial dialog.
The following code represents the first step of the waterfall dialog.
async transportStep(step) {
// WaterfallStep always finishes with the end of the Waterfall or with another dialog; here it is a Prompt Dialog.
// Running a prompt here means the next WaterfallStep will be run when the user's response is received.
return await step.prompt(CHOICE_PROMPT, {
prompt: 'Please enter your mode of transport.',
choices: ChoiceFactory.toChoices(['Car', 'Bus', 'Bicycle'])
});
}
Here the UserProfileDialog class derives from the ComponentDialog class.
public class UserProfileDialog extends ComponentDialog {
Within the constructor, the addDialog method adds dialogs and prompts to the component dialog. The first item you add with this method is set as the initial dialog. You can change the initial dialog by calling the setInitialDialogId method and provide the name of the initial dialog. When you start a component dialog, it will start its initial dialog.
public UserProfileDialog(UserState withUserState) {
super("UserProfileDialog");
userProfileAccessor = withUserState.createProperty("UserProfile");
WaterfallStep[] waterfallSteps = {
UserProfileDialog::transportStep,
UserProfileDialog::nameStep,
this::nameConfirmStep,
this::ageStep,
UserProfileDialog::pictureStep,
this::confirmStep,
this::summaryStep
};
// Add named dialogs to the DialogSet. These names are saved in the dialog state.
addDialog(new WaterfallDialog("WaterfallDialog", Arrays.asList(waterfallSteps)));
addDialog(new TextPrompt("TextPrompt"));
addDialog(new NumberPrompt<Integer>("NumberPrompt", UserProfileDialog::agePromptValidator, Integer.class));
addDialog(new ChoicePrompt("ChoicePrompt"));
addDialog(new ConfirmPrompt("ConfirmPrompt"));
addDialog(new AttachmentPrompt("AttachmentPrompt", UserProfileDialog::picturePromptValidator));
// The initial child Dialog to run.
setInitialDialogId("WaterfallDialog");
}
The following code represents the first step of the waterfall dialog.
private static CompletableFuture<DialogTurnResult> nameStep(WaterfallStepContext stepContext) {
stepContext.getValues().put("transport", ((FoundChoice) stepContext.getResult()).getValue());
PromptOptions promptOptions = new PromptOptions();
promptOptions.setPrompt(MessageFactory.text("Please enter your name."));
return stepContext.prompt("TextPrompt", promptOptions);
}
To use dialogs, install the botbuilder-dialogs and botbuilder-ai PyPI packages by running pip install botbuilder-dialogs and pip install botbuilder-ai from a terminal.
dialogs/user_profile_dialog.py
Here the UserProfileDialog class extends ComponentDialog.
class UserProfileDialog(ComponentDialog):
Within the constructor, the add_dialog method adds dialogs and prompts to the component dialog. The first item you add with this method is set as the initial dialog. You can change the initial dialog by explicitly setting the initial_dialog_id property. When you start a component dialog, it will start its initial dialog.
The following code represents the first step of the waterfall dialog.
async def transport_step(
self, step_context: WaterfallStepContext
) -> DialogTurnResult:
# WaterfallStep always finishes with the end of the Waterfall or with another dialog;
# here it is a Prompt Dialog. Running a prompt here means the next WaterfallStep will
# be run when the users response is received.
return await step_context.prompt(
ChoicePrompt.__name__,
PromptOptions(
prompt=MessageFactory.text("Please enter your mode of transport."),
choices=[Choice("Car"), Choice("Bus"), Choice("Bicycle")],
),
)
At run-time, the component dialog maintains its own dialog stack. When the component dialog is started:
An instance is created and added to the outer dialog stack
It creates an inner dialog stack that it adds to its state
It starts its initial dialog and adds that to the inner dialog stack.
The parent context sees the component as the active dialog. However, to the context inside the component, it looks like the initial dialog is the active dialog.
Call the dialog from your bot
In the outer dialog set, the one to which you added the component dialog, the component dialog has the ID that you created it with. In the outer set, the component looks like a single dialog, much like prompts do.
To use a component dialog, add an instance of it to the bot's dialog set.
In the sample, this is done using the RunAsync method that is called from the bot's OnMessageActivityAsync method.
protected override async Task OnMessageActivityAsync(ITurnContext<IMessageActivity> turnContext, CancellationToken cancellationToken)
{
Logger.LogInformation("Running dialog with Message Activity.");
// Run the Dialog with the new message Activity.
await Dialog.RunAsync(turnContext, ConversationState.CreateProperty<DialogState>(nameof(DialogState)), cancellationToken);
}
dialogs/userProfileDialog.js
In the sample, we've added a run method to the user profile dialog.
The run method is called from the bot's onMessage method.
this.onMessage(async (context, next) => {
console.log('Running dialog with Message Activity.');
// Run the Dialog with the new message Activity.
await this.dialog.run(context, this.dialogState);
await next();
});
DialogBot.java
In the sample, this is done using the run method that is called from the bot's onMessageActivity method.
@Override
protected CompletableFuture<Void> onMessageActivity(
TurnContext turnContext
) {
LoggerFactory.getLogger(DialogBot.class).info("Running dialog with Message Activity.");
// Run the Dialog with the new message Activity.
return Dialog.run(dialog, turnContext, conversationState.createProperty("DialogState"));
}
helpers/dialog_helper.py
In the sample, we've added a run_dialog method to the user profile dialog.
Start the Emulator, connect to your bot, and send messages as shown below.
Additional information
How cancellation works for component dialogs
If you call cancel all dialogs from the component dialog's context, the component dialog will cancel all of the dialogs on its inner stack and then end, returning control to the next dialog on the outer stack.
If you call cancel all dialogs from the outer context, the component is canceled, along with the rest of the dialogs on the outer context.
Next steps
Learn how to create complex conversations that branch and loop.