Manage dialog complexity

APPLIES TO: SDK v4

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. Only critical security and bug fixes within this repository will be undertaken.

Existing bots built with the Java SDK will continue to function.

For new bot building, consider using Power Virtual Agents and read about choosing the right chatbot solution.

For more information, see The future of bot building.

Prerequisites

About the sample

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,
        ConfirmStepAsync,
        SummaryStepAsync,
    };

    // 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);
}

For more information on implementing waterfall dialogs, see how to implement sequential conversation flow.

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.

Bots\DialogBot.cs

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);
}

Test your bot

  1. If you haven't done so already, install the Bot Framework Emulator.
  2. Run the sample locally on your machine.
  3. Start the Emulator, connect to your bot, and send messages as shown below.

Sample transcript from the multi-turn prompt dialog.

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.