I\'m trying to create a dialog in which I define multiple waterfall steps. In the context of this dialog, I need sometimes to go back to the previous waterfall step accordin
I'm not sure, if it's the right and efficient way to do it, but you can experiment with the State
property of the context.ActiveDialog
within your Task<DialogTurnResult>
function.
context.ActiveDialog.State["stepIndex"] = (int)context.ActiveDialog.State["stepIndex"] -2;
Waterfall dialogs weren't designed with the idea of 'going backwards' to traverse them, though I can see the possible need to. The only solution I've found is to break your waterfall into smaller "mini" waterfalls, and nest them into one larger waterfall.
// define and add waterfall dialogs (main)
WaterfallStep[] welcomeDialogSteps = new WaterfallStep[]
{
MainDialogSteps.PresentMenuAsync,
MainDialogSteps.ProcessInputAsync,
MainDialogSteps.RepeatMenuAsync,
};
Then in MainDialogSteps.ProcessInputAsync:
public static async Task<DialogTurnResult> ProcessInputAsync(
WaterfallStepContext stepContext,
CancellationToken cancellationToken)
{
var choice = (FoundChoice)stepContext.Result;
var dialogId = Lists.WelcomeOptions[choice.Index].DialogName;
return await stepContext.BeginDialogAsync(dialogId, null, cancellationToken);
}
This allows the users to start new dialogs still within the main dialog stack. One of my options I offered was a prompt of a list of phone numbers:
WaterfallStep[] phoneChoiceDialogSteps = new WaterfallStep[]
{
PhoneChoicePromptSteps.PromptForPhoneAsync,
PhoneChoicePromptSteps.ConfirmPhoneAsync,
PhoneChoicePromptSteps.ProcessInputAsync,
};
Add(new WaterfallDialog(Dialogs.PhonePrompt, phoneChoiceDialogSteps));
And finally, in the PhoneChoicePromptSteps.ProcessInputAsync, I allowed for the selection of 'no' from the confirm to ReplaceDialogAsync and effectivly reset this smaller waterfall, without effecting the rest of the overall waterfall:
public static async Task<DialogTurnResult> ProcessInputAsync(
WaterfallStepContext stepContext,
CancellationToken cancellationToken)
{
if ((bool)stepContext.Result)
{
await stepContext.Context.SendActivityAsync(
$"Calling {stepContext.Values[Outputs.PhoneNumber]}",
cancellationToken: cancellationToken);
return await stepContext.EndDialogAsync(null, cancellationToken);
}
else
{
return await stepContext.ReplaceDialogAsync(Dialogs.PhonePrompt, null, cancellationToken);
}
}
You can use the option parameter in the method "ReplaceDialogAsync" and skip steps with the method "NextAsync".
For example in my waterfall steps (defined in the constructor):
AddDialog(new WaterfallDialog(nameof(WaterfallDialog), new WaterfallStep[]
{
IntroStepAsync,
ActStepAsync,
FinalStepAsync
}));
// The initial child Dialog to run.
InitialDialogId = nameof(WaterfallDialog);
If you want pass to second step (in my case ActStepAsync) from the final step (FinalStepAsync) , when I going to replace the dialog I use the created a label in the Dialog:
private const string FLAG = "MY_FLAG";
When I invoke the method from final step I do this :
return await stepContext.ReplaceDialogAsync(InitialDialogId, FLAG, cancellationToken);
So I only need check the option in the first step if the context has the flag :
// Use the text provided in FinalStepAsync or the default if it is the first time.
var messageText = stepContext.Options?.ToString() ?? "welcome-message";
if (messageText.Equals(FLAG_REPROMPT))
{
return await stepContext.NextAsync(null,cancellationToken);
}
Later this you are in the second step