Advanced features of FormFlow

APPLIES TO: SDK v3

Basic features of FormFlow describes a basic FormFlow implementation that delivers a fairly generic user experience. To deliver a more customized user experience using FormFlow, you can specify initial form state, add business logic to manage interdependencies between fields and process user input, and use attributes to customize prompts, override templates, designate optional fields, match user input, and validate user input.

Specify initial form state and entities

When you launch a FormDialog, you may optionally pass in an instance of your state. If you do pass in an instance of your state, then by default, FormFlow will skip steps for any fields that already contain values; the user will not be prompted for those fields. To force the form to prompt the user for all fields (including those fields that already contain values in the initial state), pass in FormOptions.PromptFieldsWithValues when you launch the FormDialog. If a field contains an initial value, the prompt will use that value as the default value.

You can also pass in LUIS entities to bind to the state. If the EntityRecommendation.Type is a path to a field in your C# class, the EntityRecommendation.Entity will be passed through the recognizer to bind to your field. FormFlow will skip steps for any fields that are bound to an entity; the user will not be prompted for those fields.

Add business logic

To handle interdependencies between form fields or apply specific logic during the process of getting or setting a field value, you can specify business logic within a validation function. A validation function lets you manipulate the state and return a ValidateResult object that can contain:

  • a feedback string that describes the reason that a value is invalid
  • a transformed value
  • a set of choices for clarifying a value

This code example shows a validation function for the Toppings field. If input for the field contains the ToppingOptions.Everything enumeration value, the function ensures that the Toppings field value contains the full list of toppings.

public static IForm<SandwichOrder> BuildForm()
{
    ...
    return new FormBuilder<SandwichOrder>()
        .Message("Welcome to the sandwich order bot!")
        .Field(nameof(Sandwich))
        .Field(nameof(Length))
        .Field(nameof(Bread))
        .Field(nameof(Cheese))
        .Field(nameof(Toppings),
            validate: async (state, value) =>
            {
                var values = ((List<object>)value).OfType<ToppingOptions>();
                var result = new ValidateResult { IsValid = true, Value = values };
                if (values != null && values.Contains(ToppingOptions.Everything))
                {
                    result.Value = (from ToppingOptions topping in Enum.GetValues(typeof(ToppingOptions))
                                    where topping != ToppingOptions.Everything && !values.Contains(topping)
                                    select topping).ToList();
                }
                return result;
            })
        .Message("For sandwich toppings you have selected {Toppings}.")
        ...
        .Build();
}

In addition to the validation function, you can add the Term attribute to match user expressions such as "everything" or "not".

public enum ToppingOptions
{
    // This starts at 1 because 0 is the "no value" value
    [Terms("except", "but", "not", "no", "all", "everything")]
    Everything = 1,
    ...
}

Using the validation function shown above, this snippet shows the interaction between bot and user when the user requests "everything but Jalapenos."

Please select one or more toppings (current choice: No Preference)
 1. Everything
 2. Avocado
 3. Banana Peppers
 4. Cucumbers
 5. Green Bell Peppers
 6. Jalapenos
 7. Lettuce
 8. Olives
 9. Pickles
 10. Red Onion
 11. Spinach
 12. Tomatoes
> everything but jalapenos
For sandwich toppings you have selected Avocado, Banana Peppers, Cucumbers, Green Bell Peppers, Lettuce, Olives, Pickles, Red Onion, Spinach, and Tomatoes.

FormFlow attributes

You can add these C# attributes to your class to customize behavior of a FormFlow dialog.

Attribute Purpose
Describe Alter how a field or a value is shown in a template or card
Numeric Restrict the accepted values of a numeric field
Optional Mark a field as optional
Pattern Define a regular expression to validate a string field
Prompt Define the prompt for a field
Template Define the template to use to generate prompts or values in prompts
Terms Define the input terms that match a field or value

Customize prompts using the Prompt attribute

Default prompts are automatically generated for each field in your form, but you can specify a custom prompt for any field by using the Prompt attribute. For example, if the default prompt for the SandwichOrder.Sandwich field is "Please select a sandwich", you can add the Prompt attribute to specify a custom prompt for that field.

[Prompt("What kind of {&} would you like? {||}")]
public SandwichOptions? Sandwich;

This example uses pattern language to dynamically populate the prompt with form data at runtime: {&} is replaced with the description of the field and {||} is replaced with the list of choices in the enumeration.

Note

By default, the description of a field is generated from the field's name. To specify a custom description for a field, add the Describe attribute.

This snippet shows the customized prompt that is specified by the example above.

What kind of sandwich would you like?
1. BLT
2. Black Forest Ham
3. Buffalo Chicken
4. Chicken And Bacon Ranch Melt
5. Cold Cut Combo
6. Meatball Marinara
7. Oven Roasted Chicken
8. Roast Beef
9. Rotisserie Style Chicken
10. Spicy Italian
11. Steak And Cheese
12. Sweet Onion Teriyaki
13. Tuna
14. Turkey Breast
15. Veggie
>

A Prompt attribute may also specify parameters that affect how the form displays the prompt. For example, the ChoiceFormat parameter determines how the form renders the list of choices.

[Prompt("What kind of {&} would you like? {||}", ChoiceFormat="{1}")]
public SandwichOptions? Sandwich;

In this example, the value of the ChoiceFormat parameter indicates that the choices should be displayed as a bulleted list (instead of a numbered list).

What kind of sandwich would you like?
- BLT
- Black Forest Ham
- Buffalo Chicken
- Chicken And Bacon Ranch Melt
- Cold Cut Combo
- Meatball Marinara
- Oven Roasted Chicken
- Roast Beef
- Rotisserie Style Chicken
- Spicy Italian
- Steak And Cheese
- Sweet Onion Teriyaki
- Tuna
- Turkey Breast
- Veggie
>

Customize prompts using the Template attribute

While the Prompt attribute enables you to customize the prompt for a single field, the Template attribute enables you to replace the default templates that FormFlow uses to automatically generate prompts. This code example uses the Template attribute to redefine how the form handles all enumeration fields. The attribute indicates that the user may select only one item, sets the prompt text by using pattern language, and specifies that the form should display only one item per line.

[Template(TemplateUsage.EnumSelectOne, "What kind of {&} would you like on your sandwich? {||}", ChoiceStyle = ChoiceStyleOptions.PerLine)]
public class SandwichOrder

This snippet shows the resulting prompts for the Bread field and Cheese field.

What kind of bread would you like on your sandwich?
 1. Nine Grain Wheat
 2. Nine Grain Honey Oat
 3. Italian
 4. Italian Herbs And Cheese
 5. Flatbread
> 

What kind of cheese would you like on your sandwich? 
 1. American
 2. Monterey Cheddar
 3. Pepperjack
> 

If you use the Template attribute to replace the default templates that FormFlow uses to generate prompts, you may want to interject some variation into the prompts and messages that the form generates. To do so, you can define multiple text strings using pattern language, and the form will randomly choose from the available options each time it needs to display a prompt or message.

This code example redefines the TemplateUsage.NotUnderstood template to specify two different variations of message. When the bot needs to communicate that it does not understand a user's input, it will determine message contents by randomly selecting one of the two text strings.

[Template(TemplateUsage.NotUnderstood, "I do not understand \"{0}\".", "Try again, I don't get \"{0}\".")]
[Template(TemplateUsage.EnumSelectOne, "What kind of {&} would you like on your sandwich? {||}")]
public class SandwichOrder

This snippet shows an example of the resulting the interaction between bot and user.

What size of sandwich do you want? (1. Six Inch, 2. Foot Long)
> two feet
I do not understand "two feet".
> two feet
Try again, I don't get "two feet"
> 

Designate a field as optional using the Optional attribute

To designate a field as optional, use the Optional attribute. This code example specifies that the Cheese field is optional.

[Optional]
public CheeseOptions? Cheese;

If a field is optional and no value has been specified, the current choice will be displayed as "No Preference".

What kind of cheese would you like on your sandwich? (current choice: No Preference)
  1. American
  2. Monterey Cheddar
  3. Pepperjack
 >

If a field is optional and the user has specified a value, "No Preference" will be displayed as the last choice in the list.

What kind of cheese would you like on your sandwich? (current choice: American)
 1. American
 2. Monterey Cheddar
 3. Pepperjack
 4. No Preference
>

Match user input using the Terms attribute

When a user sends a message to a bot that is built using FormFlow, the bot attempts to identify the meaning of the user's input by matching the input to a list of terms. By default, the list of terms is generated by applying these steps to the field or value:

  1. Break on case changes and underscore (_).
  2. Generate each n-gram up to a maximum length.
  3. Add "s?" to the end of each word (to support plurals).

For example, the value "AngusBeefAndGarlicPizza" would generate these terms:

  • 'angus?'
  • 'beefs?'
  • 'garlics?'
  • 'pizzas?'
  • 'angus? beefs?'
  • 'garlics? pizzas?'
  • 'angus beef and garlic pizza'

To override this default behavior and define the list of terms that are used to match user input to a field or a value in a field, use the Terms attribute. For example, you may use the Terms attribute (with a regular expression) to account for the fact that users are likely to misspell the word "rotisserie."

[Terms(@"rotis\w* style chicken", MaxPhrase = 3)]
RotisserieStyleChicken, SpicyItalian, SteakAndCheese, SweetOnionTeriyaki, Tuna,...

By using the Terms attribute, you increase the likelihood of being able to match user input with one of the valid choices. The Terms.MaxPhrase parameter in this example causes the Language.GenerateTerms to generate additional variations of terms.

This snippet shows the resulting interaction between bot and user when the user misspells "Rotisserie."

What kind of sandwich would you like?
 1. BLT
 2. Black Forest Ham
 3. Buffalo Chicken
 4. Chicken And Bacon Ranch Melt
 5. Cold Cut Combo
 6. Meatball Marinara
 7. Oven Roasted Chicken
 8. Roast Beef
 9. Rotisserie Style Chicken
 10. Spicy Italian
 11. Steak And Cheese
 12. Sweet Onion Teriyaki
 13. Tuna
 14. Turkey Breast
 15. Veggie
> rotissary checkin
For sandwich I understood Rotisserie Style Chicken. "checkin" is not an option.

Validate user input using the Numeric attribute or Pattern attribute

To restrict the range of allowed values for a numeric field, use the Numeric attribute. This code example uses the Numeric attribute to specify that input for the Rating field must be a number between 1 and 5.

[Numeric(1, 5)]
public double? Rating;

To specify the required format for the value of a particular field, use the Pattern attribute. This code example uses the Pattern attribute to specify the required format for the value of the PhoneNumber field.

[Pattern(@"(<Undefined control sequence>\d)?\s*\d{3}(-|\s*)\d{4}")]
public string PhoneNumber;

Summary

This article has described how to deliver a customized user experience with FormFlow by specifying initial form state, adding business logic to manage interdependencies between fields and process user input, and using attributes to customize prompts, override templates, designate optional fields, match user input, and validate user input. For information about additional ways to customize the user experience with FormFlow, see Customize a form using FormBuilder.

Sample code

For complete samples that show how to implement FormFlow using the Bot Framework SDK for .NET, see the Multi-Dialog Bot sample and the Contoso Flowers Bot sample in GitHub.

Additional resources