7/25/2016 Webmaster

Introduction To FormFlow With The Microsoft Bot Framework


image

Using FormFlow with the Microsoft Bot Framework, allows you to quickly create a guided conversation, to gather information from a user, with the least amount of code. While it is less flexible than using Dialogs, it can be combined with Dialogs to increase its functionality.

 

A Walk-Thru

image

When a user begins a conversation with the Bot, it introduces itself, and asks the user’s name.

image

The Bot proceeds to ask questions and gather the responses.

image

A feature of FormFlow is that the user can type help at any time to obtain assistance with any question or the FormFlow itself.

As a developer, you get this functionality without the need to write any additional code.

image

The FormFlow continues until the form is complete.

Creating The Project

image

Download the Visual Studio 2015 Microsoft Bot Framework template (using this link)

Save the .zip file in the templates directory (located at: %USERPROFILE%\Documents\Visual Studio 2015\Templates\ProjectTemplates\Visual C# ).

This creates a template that you can use in Visual Studio to create Bot projects.

image

Open Visual Studio.

image

Create a new Project.

image

Select the Bot Application template and name the project HelloFormFlowBot.

image

The project will be created.

image

Right-click on the project, and select Add then New Item.

We will now create the class that will contain the logic for our FormFlow.

image

Add a new C# class called ProfileForm.cs.

Replace all the code with the following code:

 

using System;
using Microsoft.Bot.Builder.Dialogs;
using Microsoft.Bot.Builder.FormFlow;
namespace HelloFormFlowBot
{
    [Serializable]
    public class ProfileForm
    {
        // these are the fields that will hold the data
        // we will gather with the form
        [Prompt("What is your first name? {||}")]
        public string FirstName;
        [Prompt("What is your last name? {||}")]
        public string LastName;
        [Prompt("What is your gender? {||}")]
        public Gender Gender;
        // This method 'builds' the form 
        // This method will be called by code we will place
        // in the MakeRootDialog method of the MessagesControlller.cs file
        public static IForm<ProfileForm> BuildForm()
        {
            return new FormBuilder<ProfileForm>()
                    .Message("Welcome to the profile bot!")
                    .OnCompletion(async (context, profileForm) =>
                    {
                        // Tell the user that the form is complete
                        await context.PostAsync("Your profile is complete.");
                    })
                    .Build();
        }
    }
    // This enum provides the possible values for the 
    // Gender property in the ProfileForm class
    // Notice we start the options at 1 
    [Serializable]
    public enum Gender
    {
        Male = 1, Female = 2
    };
}

 

This code indicates the fields we will gather with our FormFlow.

Notice that the class, as well as the Gender enum that it consumes, is marked [Serializable].

The Microsoft Bot Framework requires that the classes must be serializable so the Bot can be stateless.

Save the file.

image

Open the MessagesController.cs file.

Add the following using statements at the top of the file (to support the FormFlow code):

 

using Microsoft.Bot.Builder.Dialogs;
using Microsoft.Bot.Builder.FormFlow;

 

Add the following MakeRootDialog method to the MessagesController class (to call the BuildForm method in the ProfileForm class we created earlier):

 

    internal static IDialog<ProfileForm> MakeRootDialog()
    {
        return Chain.From(() => FormDialog.FromForm(ProfileForm.BuildForm));
    }

 

Finally, alter the Post method in the MessagesController class to the following:

 

    public async Task<HttpResponseMessage> Post([FromBody]Activity activity)
    {
        // Detect if this is a Message activity
        if (activity.Type == ActivityTypes.Message)
        {
            // Call our FormFlow by calling MakeRootDialog
            await Conversation.SendAsync(activity, MakeRootDialog);
        }
        else
        {
            // This was not a Message activity
            HandleSystemMessage(activity);
        }
        // Return response
        var response = Request.CreateResponse(HttpStatusCode.OK);
        return response;            
    }


Save the file.

Test The Application

image

Hit F5 to run the application.

image

The web browser will open.

Note the port number and the web address.

image

Download, install, and run the Bot Framework Emulator (Windows) (also see: Mac and Linux support using command line emulator if you don’t have Windows).

When the emulator starts, connect to the Bot by setting the address to the the one indicted in the web browser (however, add /api/messages to the end).

image

Ensure that the Bot Url is connecting to the correct address.

image

Type a message, and click the send key (or press Enter).

image

You can now converse with the Bot and fill out the FormFlow.

Saving The Data

Currently the application does not save the responses from the user. In fact, after you fill the FormFlow out, it will simply ask you to fill it out again.

We can use the Bot State Service to save and retrieve the values.

Alter the BuildForm method in the ProfileForm.cs file to the following (to save the values entered into the form):

 

    public static IForm<ProfileForm> BuildForm()
    {
        return new FormBuilder<ProfileForm>()
                .Message("Welcome to the profile bot!")
                .OnCompletion(async (context, profileForm) =>
                {
                    // Set BotUserData
                    context.PrivateConversationData.SetValue<bool>(
                        "ProfileComplete", true);
                    context.PrivateConversationData.SetValue<string>(
                        "FirstName", profileForm.FirstName);
                    context.PrivateConversationData.SetValue<string>(
                        "LastName", profileForm.LastName);
                    context.PrivateConversationData.SetValue<string>(
                        "Gender", profileForm.Gender.ToString());
                    // Tell the user that the form is complete
                    await context.PostAsync("Your profile is complete.");
                })
                .Build();
    }

 

We are not only storing the values provided by the user, but we are also setting a flag (ProfileComplete), so that we don’t ask the user to fill out the form again.

Finally, alter the Post method in the MessagesController class to the following:

 

    public async Task<HttpResponseMessage> Post([FromBody]Activity activity)
    {
        // Detect if this is a Message activity
        if (activity.Type == ActivityTypes.Message)
        {
            // Get any saved values
            StateClient sc = activity.GetStateClient();
            BotData userData = sc.BotState.GetPrivateConversationData(
                activity.ChannelId, activity.Conversation.Id, activity.From.Id);
            var boolProfileComplete = userData.GetProperty<bool>("ProfileComplete");
            if (!boolProfileComplete)
            {
                // Call our FormFlow by calling MakeRootDialog
                await Conversation.SendAsync(activity, MakeRootDialog);
            }
            else
            {
                // Get the saved profile values
                var FirstName = userData.GetProperty<string>("FirstName");
                var LastName = userData.GetProperty<string>("LastName");
                var Gender = userData.GetProperty<string>("Gender");
                // Tell the user their profile is complete
                System.Text.StringBuilder sb = new System.Text.StringBuilder();
                sb.Append("Your profile is complete.\n\n");
                sb.Append(String.Format("FirstName = {0}\n\n", FirstName));
                sb.Append(String.Format("LastName = {0}\n\n", LastName));
                sb.Append(String.Format("Gender = {0}", Gender));
                // Create final reply
                ConnectorClient connector = new ConnectorClient(new Uri(activity.ServiceUrl));
                Activity replyMessage = activity.CreateReply(sb.ToString());
                await connector.Conversations.ReplyToActivityAsync(replyMessage);
            }
        }
        else
        {
            // This was not a Message activity
            HandleSystemMessage(activity);
        }
        // Send response
        var response = Request.CreateResponse(HttpStatusCode.OK);
        return response;
    }

 

image

 

Now the application will only ask the user to fill in the form one time, and display the values received using the Bot State Service.

The values will be persisted by the Microsoft Bot Framework keyed to that user in that conversation.

In our example we used PrivateConversationData, but the Bot State Service exposes the following methods, each with a different scope:

Method

Scoped

Description

SetUserData()

User

Remembering context object with a user

SetConversationData()

Conversation

Remembering context object with a conversation

SetPrivateConversationData()

User & Conversation

Remembering context object with a person in a conversation

 

Microsoft Links

Microsoft Bot Framework

FormFlow

Bot State Service

Bot Builder (GitHub)

Bot Framework Forum (stack overflow)

Visual Studio 2015 Bot Project Template

Bot Framework Emulator (Windows)

Download

You can download the code from the Download page

An unhandled error has occurred. Reload 🗙