您现在访问的是微软AZURE全球版技术文档网站,若需要访问由世纪互联运营的MICROSOFT AZURE中国区技术文档网站,请访问 https://docs.azure.cn.

使用 FormBuilder 自定义表单Customize a form using FormBuilder

备注

本主题适用于 SDK v3 版本。This topic applies to SDK v3 release. 可以在此处找到最新版 SDK (v4) 的文档。You can find the documentation for the latest version of the SDK v4 here.

FormFlow 的基本功能介绍了能提供比较通用的用户体验的基本 FormFlow 实现,FormFlow 的高级功能则介绍了如何使用业务逻辑和属性自定义用户体验。Basic features of FormFlow describes a basic FormFlow implementation that delivers a fairly generic user experience, and Advanced features of FormFlow describes how you can customize user experience by using business logic and attributes. 本文介绍如何通过指定表单执行步骤的序列并动态定义字段值、确认和消息,使用 FormBuilder 更进一步地自定义用户体验。This article describes how you can use FormBuilder to customize user experience even further, by specifying the sequence in which the form executes steps and dynamically defining field values, confirmations, and messages.

动态定义字段值、确认和消息Dynamically define field values, confirmations, and messages

你可使用 FormBuilder 动态定义字段值、确认和消息。Using FormBuilder, you can dynamically define field values, confirmations, and messages.

动态定义字段值Dynamically define field values

三明治机器人旨在向要求一英寸长三明治的所有订单添加一份免费的饮品或饼干,它使用 Sandwich.Specials 字段来存储免费物品的相关数据。A sandwich bot that is designed to add a free drink or cookie to any order that specifies a foot-long sandwich uses the Sandwich.Specials field to store data about free items. 这样的话,必须根据订单中是否要求一英尺长的三明治,为每个订单动态设置 Sandwich.Specials 字段的值。In this case, the value of the Sandwich.Specials field must be dynamically set for each order according to whether or not the order contains a foot-long sandwich.

Specials 字段已指定为可选,“None”则指定为表示无偏好的选项的文本。The Specials field is specified as optional and "None" is designated as text for the choice that indicates no preference.

[Optional]
[Template(TemplateUsage.NoPreference, "None")]
public string Specials;

以下代码示例展示了如何动态设置 Specials 字段的值。This code example shows how to dynamically set the value of the Specials field.

.Field(new FieldReflector<SandwichOrder>(nameof(Specials))
    .SetType(null)
    .SetActive((state) => state.Length == LengthOptions.FootLong)
    .SetDefine(async (state, field) =>
    {
        field
            .AddDescription("cookie", "Free cookie")
            .AddTerms("cookie", "cookie", "free cookie")
            .AddDescription("drink", "Free large drink")
            .AddTerms("drink", "drink", "free drink");
        return true;
    }))

在本例中,Advanced.Field.SetType 方法指定字段类型(null 表示枚举字段)。In this example, the Advanced.Field.SetType method specifies the field type (null represents an enumeration field). Advanced.Field.SetActive 方法指定仅在三明治的长度为 Length.FootLong 时才启用该字段。The Advanced.Field.SetActive method specifies that the field should only be enabled if the length of the sandwich is Length.FootLong. 最后,Advanced.Field.SetDefine 方法指定一个用于定义字段的异步委托。Finally, the Advanced.Field.SetDefine method specifies an async delegate that defines the field. 会向委托传递当前状态对象以及要动态定义的 Advanced.FieldThe delegate is passed the current state object and the Advanced.Field that is being dynamically defined. 委托使用字段的 fluent 方法来动态定义值。The delegate uses the field's fluent methods to dynamically define values. 在此示例中,值是字符串,AddDescriptionAddTerms 方法指定每个值的说明和对应物品。In this example, the values are strings and the AddDescription and AddTerms methods specify the descriptions and terms for each value.

备注

若要动态定义字段值,可自行实现 Advanced.IField,也可使用 Advanced.FieldReflector 类简化此过程,如上例所示。To dynamically define a field value, you can implement Advanced.IField yourself, or streamline the process by using the Advanced.FieldReflector class as shown in the example above.

动态定义消息和确认Dynamically define messages and confirmations

你还可使用 FormBuilder 动态定义消息和确认。Using FormBuilder, you can also dynamically define messages and confirmations. 只有在表单中之前的步骤处于非活动状态或已完成时,每个消息和确认才会运行。Each message and confirmation runs only when prior steps in the form are inactive or completed.

以下代码示例展示了一个自动生成的计算三明治成本的确认消息。This code example shows a dynamically generated confirmation that computes the cost of the sandwich.

.Confirm(async (state) =>
{
    var cost = 0.0;
    switch (state.Length)
    {
        case LengthOptions.SixInch: cost = 5.0; break;
        case LengthOptions.FootLong: cost = 6.50; break;
    }
    return new PromptAttribute($"Total for your sandwich is {cost:C2} is that ok?");
})

使用 FormBuilder 自定义表单Customize a form using FormBuilder

以下代码示例使用 FormBuilder 定义表单的步骤、验证所选内容以及动态定义字段值和确认This code example uses FormBuilder to define the steps of the form, validate selections, and dynamically define a field value and confirmation. 默认情况下,表单中的步骤将按列出的序列执行。By default, steps in the form will be executed in the sequence in which they are listed. 但是,对于已包含值的字段或在已指定显式导航的情况下,可能会跳过步骤。However, steps might be skipped for fields that already contain values or if explicit navigation is specified.

public static IForm<SandwichOrder> BuildForm()
{
    OnCompletionAsyncDelegate<SandwichOrder> processOrder = async (context, state) =>
    {
        await context.PostAsync("We are currently processing your sandwich. We will message you the status.");
    };

    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}.")
        .Field(nameof(SandwichOrder.Sauces))
        .Field(new FieldReflector<SandwichOrder>(nameof(Specials))
            .SetType(null)
            .SetActive((state) => state.Length == LengthOptions.FootLong)
            .SetDefine(async (state, field) =>
            {
                field
                    .AddDescription("cookie", "Free cookie")
                    .AddTerms("cookie", "cookie", "free cookie")
                    .AddDescription("drink", "Free large drink")
                    .AddTerms("drink", "drink", "free drink");
                return true;
            }))
        .Confirm(async (state) =>
        {
            var cost = 0.0;
            switch (state.Length)
            {
                case LengthOptions.SixInch: cost = 5.0; break;
                case LengthOptions.FootLong: cost = 6.50; break;
            }
            return new PromptAttribute($"Total for your sandwich is {cost:C2} is that ok?");
        })
        .Field(nameof(SandwichOrder.DeliveryAddress),
            validate: async (state, response) =>
            {
                var result = new ValidateResult { IsValid = true, Value = response };
                var address = (response as string).Trim();
                if (address.Length > 0 && (address[0] < '0' || address[0] > '9'))
                {
                    result.Feedback = "Address must start with a number.";
                    result.IsValid = false;
                }
                return result;
            })
        .Field(nameof(SandwichOrder.DeliveryTime), "What time do you want your sandwich delivered? {||}")
        .Confirm("Do you want to order your {Length} {Sandwich} on {Bread} {&Bread} with {[{Cheese} {Toppings} {Sauces}]} to be sent to {DeliveryAddress} {?at {DeliveryTime:t}}?")
        .AddRemainingFields()
        .Message("Thanks for ordering a sandwich!")
        .OnCompletion(processOrder)
        .Build();
}

在本例中,表单执行以下步骤:In this example, the form executes these steps:

  • 显示一条欢迎消息。Shows a welcome message.
  • 填写 SandwichOrder.SandwichFills in SandwichOrder.Sandwich.
  • 填写 SandwichOrder.LengthFills in SandwichOrder.Length.
  • 填写 SandwichOrder.BreadFills in SandwichOrder.Bread.
  • 填写 SandwichOrder.CheeseFills in SandwichOrder.Cheese.
  • 填写 SandwichOrder.Toppings 并添加缺失值(如果用户已选择 ToppingOptions.Everything)。Fills in SandwichOrder.Toppings and adds missing values if the user selected ToppingOptions.Everything. -.-. 显示确认所选浇汁的消息。Shows a message that confirms the selected toppings.
  • 填写 SandwichOrder.SaucesFills in SandwichOrder.Sauces.
  • 动态定义SandwichOrder.Specials 的字段值。Dynamically defines the field value for SandwichOrder.Specials.
  • 动态定义三明治成本的确认。Dynamically defines the confirmation for cost of the sandwich.
  • 填写 SandwichOrder.DeliveryAddress验证所生成的字符串。Fills in SandwichOrder.DeliveryAddress and verifies the resulting string. 如果该地址不以数字开头,则表单返回一条消息。If the address does not start with a number, the form returns a message.
  • 使用自定义提示符填写 SandwichOrder.DeliveryTimeFills in SandwichOrder.DeliveryTime with a custom prompt.
  • 确认订单。Confirms the order.
  • 添加已在类中定义但 Field 未显式引用的任何剩余字段。Adds any remaining fields that were defined in the class but not explicitly referenced by Field. (如果示例未调用 AddRemainingFields 方法,则表单不会包括未显式引用的任何字段。)(If the example did not call the AddRemainingFields method, the form would not include any fields that were not explicity referenced.)
  • 显示感谢消息。Shows a thank you message.
  • 定义 OnCompletionAsync 处理程序以处理订单。Defines an OnCompletionAsync handler to process the order.

示例代码Sample code

有关演示如何使用 Bot Framework SDK for .NET 实现 FormFlow 的完整示例,请参阅 GitHub 中的多对话机器人示例Contoso Flowers 机器人示例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